Pārlūkot izejas kodu

Merge branch 'master' into maybe-sync-signal

Evan Almloff 1 gadu atpakaļ
vecāks
revīzija
f646f68b60

+ 1 - 0
packages/cli/Cargo.toml

@@ -83,6 +83,7 @@ dioxus-html = { workspace = true, features = ["hot-reload-context"] }
 dioxus-core = { workspace = true, features = ["serialize"] }
 dioxus-hot-reload = { workspace = true }
 interprocess-docfix = { version = "1.2.2" }
+gitignore = "1.0.8"
 
 [features]
 default = []

+ 101 - 80
packages/cli/src/cli/autoformat.rs

@@ -27,15 +27,17 @@ pub struct Autoformat {
 impl Autoformat {
     // Todo: autoformat the entire crate
     pub async fn autoformat(self) -> Result<()> {
+        let Autoformat { check, raw, file } = self;
+
         // Default to formatting the project
-        if self.raw.is_none() && self.file.is_none() {
-            if let Err(e) = autoformat_project(self.check).await {
+        if raw.is_none() && file.is_none() {
+            if let Err(e) = autoformat_project(check).await {
                 eprintln!("error formatting project: {}", e);
                 exit(1);
             }
         }
 
-        if let Some(raw) = self.raw {
+        if let Some(raw) = raw {
             let indent = indentation_for(".")?;
             if let Some(inner) = dioxus_autofmt::fmt_block(&raw, 0, indent) {
                 println!("{}", inner);
@@ -47,47 +49,90 @@ impl Autoformat {
         }
 
         // Format single file
-        if let Some(file) = self.file {
-            let file_content;
-            let indent;
-            if file == "-" {
-                indent = indentation_for(".")?;
-                let mut contents = String::new();
-                std::io::stdin().read_to_string(&mut contents)?;
-                file_content = Ok(contents);
-            } else {
-                indent = indentation_for(".")?;
-                file_content = fs::read_to_string(&file);
-            };
-
-            match file_content {
-                Ok(s) => {
-                    let edits = dioxus_autofmt::fmt_file(&s, indent);
-                    let out = dioxus_autofmt::apply_formats(&s, edits);
-                    if file == "-" {
-                        print!("{}", out);
-                    } else {
-                        match fs::write(&file, out) {
-                            Ok(_) => {
-                                println!("formatted {}", file);
-                            }
-                            Err(e) => {
-                                eprintln!("failed to write formatted content to file: {}", e);
-                            }
-                        }
-                    }
-                }
-                Err(e) => {
-                    eprintln!("failed to open file: {}", e);
-                    exit(1);
-                }
-            }
+        if let Some(file) = file {
+            refactor_file(file)?;
         }
 
         Ok(())
     }
 }
 
+fn refactor_file(file: String) -> Result<(), Error> {
+    let indent = indentation_for(".")?;
+    let file_content = if file == "-" {
+        let mut contents = String::new();
+        std::io::stdin().read_to_string(&mut contents)?;
+        Ok(contents)
+    } else {
+        fs::read_to_string(&file)
+    };
+    let Ok(s) = file_content else {
+        eprintln!("failed to open file: {}", file_content.unwrap_err());
+        exit(1);
+    };
+    let edits = dioxus_autofmt::fmt_file(&s, indent);
+    let out = dioxus_autofmt::apply_formats(&s, edits);
+
+    if file == "-" {
+        print!("{}", out);
+    } else if let Err(e) = fs::write(&file, out) {
+        eprintln!("failed to write formatted content to file: {e}",);
+    } else {
+        println!("formatted {}", file);
+    }
+
+    Ok(())
+}
+
+fn get_project_files(config: &CrateConfig) -> Vec<PathBuf> {
+    let mut files = vec![];
+
+    let gitignore_path = config.crate_dir.join(".gitignore");
+    if gitignore_path.is_file() {
+        let gitigno = gitignore::File::new(gitignore_path.as_path()).unwrap();
+        if let Ok(git_files) = gitigno.included_files() {
+            let git_files = git_files
+                .into_iter()
+                .filter(|f| f.ends_with(".rs") && !is_target_dir(f));
+            files.extend(git_files)
+        };
+    } else {
+        collect_rs_files(&config.crate_dir, &mut files);
+    }
+
+    files
+}
+
+fn is_target_dir(file: &Path) -> bool {
+    let stripped = if let Ok(cwd) = std::env::current_dir() {
+        file.strip_prefix(cwd).unwrap_or(file)
+    } else {
+        file
+    };
+    if let Some(first) = stripped.components().next() {
+        first.as_os_str() == "target"
+    } else {
+        false
+    }
+}
+
+async fn format_file(
+    path: impl AsRef<Path>,
+    indent: IndentOptions,
+) -> Result<usize, tokio::io::Error> {
+    let contents = tokio::fs::read_to_string(&path).await?;
+
+    let edits = dioxus_autofmt::fmt_file(&contents, indent);
+    let len = edits.len();
+
+    if !edits.is_empty() {
+        let out = dioxus_autofmt::apply_formats(&contents, edits);
+        tokio::fs::write(path, out).await?;
+    }
+
+    Ok(len)
+}
+
 /// Read every .rs file accessible when considering the .gitignore and try to format it
 ///
 /// Runs using Tokio for multithreading, so it should be really really fast
@@ -96,8 +141,13 @@ impl Autoformat {
 async fn autoformat_project(check: bool) -> Result<()> {
     let crate_config = crate::CrateConfig::new(None)?;
 
-    let mut files_to_format = vec![];
-    collect_rs_files(&crate_config.crate_dir, &mut files_to_format);
+    let files_to_format = get_project_files(&crate_config);
+
+    if files_to_format.is_empty() {
+        return Ok(());
+    }
+
+    let indent = indentation_for(&files_to_format[0])?;
 
     if files_to_format.is_empty() {
         return Ok(());
@@ -107,38 +157,17 @@ async fn autoformat_project(check: bool) -> Result<()> {
 
     let counts = files_to_format
         .into_iter()
-        .filter(|file| {
-            if file.components().any(|f| f.as_os_str() == "target") {
-                return false;
-            }
-
-            true
-        })
         .map(|path| async {
-            let _path = path.clone();
-            let _indent = indent.clone();
-            let res = tokio::spawn(async move {
-                let contents = tokio::fs::read_to_string(&path).await?;
-
-                let edits = dioxus_autofmt::fmt_file(&contents, _indent.clone());
-                let len = edits.len();
-
-                if !edits.is_empty() {
-                    let out = dioxus_autofmt::apply_formats(&contents, edits);
-                    tokio::fs::write(&path, out).await?;
-                }
-
-                Ok(len) as Result<usize, tokio::io::Error>
-            })
-            .await;
+            let path_clone = path.clone();
+            let res = tokio::spawn(format_file(path, indent.clone())).await;
 
             match res {
                 Err(err) => {
-                    eprintln!("error formatting file: {}\n{err}", _path.display());
+                    eprintln!("error formatting file: {}\n{err}", path_clone.display());
                     None
                 }
                 Ok(Err(err)) => {
-                    eprintln!("error formatting file: {}\n{err}", _path.display());
+                    eprintln!("error formatting file: {}\n{err}", path_clone.display());
                     None
                 }
                 Ok(Ok(res)) => Some(res),
@@ -148,13 +177,7 @@ async fn autoformat_project(check: bool) -> Result<()> {
         .collect::<Vec<_>>()
         .await;
 
-    let files_formatted: usize = counts
-        .into_iter()
-        .map(|f| match f {
-            Some(res) => res,
-            _ => 0,
-        })
-        .sum();
+    let files_formatted: usize = counts.into_iter().flatten().sum();
 
     if files_formatted > 0 && check {
         eprintln!("{} files needed formatting", files_formatted);
@@ -207,26 +230,24 @@ fn indentation_for(file_or_dir: impl AsRef<Path>) -> Result<IndentOptions> {
     ))
 }
 
-fn collect_rs_files(folder: &Path, files: &mut Vec<PathBuf>) {
-    let Ok(folder) = folder.read_dir() else {
+fn collect_rs_files(folder: &impl AsRef<Path>, files: &mut Vec<PathBuf>) {
+    if is_target_dir(folder.as_ref()) {
+        return;
+    }
+    let Ok(folder) = folder.as_ref().read_dir() else {
         return;
     };
-
     // load the gitignore
-
     for entry in folder {
         let Ok(entry) = entry else {
             continue;
         };
-
         let path = entry.path();
-
         if path.is_dir() {
             collect_rs_files(&path, files);
         }
-
         if let Some(ext) = path.extension() {
-            if ext == "rs" {
+            if ext == "rs" && !is_target_dir(&path) {
                 files.push(path);
             }
         }

+ 303 - 0
packages/generational-box/src/lib.rs

@@ -485,6 +485,309 @@ impl<S> MemoryLocation<S> {
             _marker: PhantomData,
         }
     }
+
+    #[track_caller]
+    fn try_borrow<T: Any>(
+        &self,
+        #[cfg(any(debug_assertions, feature = "debug_ownership"))]
+        created_at: &'static std::panic::Location<'static>,
+    ) -> Result<GenerationalRef<T>, BorrowError> {
+        #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+        self.0
+            .borrowed_at
+            .borrow_mut()
+            .push(std::panic::Location::caller());
+        match self.0.data.try_borrow() {
+            Ok(borrow) => match Ref::filter_map(borrow, |any| any.as_ref()?.downcast_ref::<T>()) {
+                Ok(reference) => Ok(GenerationalRef {
+                    inner: reference,
+                    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+                    borrow: GenerationalRefBorrowInfo {
+                        borrowed_at: std::panic::Location::caller(),
+                        borrowed_from: self.0,
+                    },
+                }),
+                Err(_) => Err(BorrowError::Dropped(ValueDroppedError {
+                    #[cfg(any(debug_assertions, feature = "debug_ownership"))]
+                    created_at,
+                })),
+            },
+            Err(_) => Err(BorrowError::AlreadyBorrowedMut(AlreadyBorrowedMutError {
+                #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+                borrowed_mut_at: self.0.borrowed_mut_at.get().unwrap(),
+            })),
+        }
+    }
+
+    #[track_caller]
+    fn try_borrow_mut<T: Any>(
+        &self,
+        #[cfg(any(debug_assertions, feature = "debug_ownership"))]
+        created_at: &'static std::panic::Location<'static>,
+    ) -> Result<GenerationalRefMut<T>, BorrowMutError> {
+        #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+        {
+            self.0
+                .borrowed_mut_at
+                .set(Some(std::panic::Location::caller()));
+        }
+        match self.0.data.try_borrow_mut() {
+            Ok(borrow_mut) => {
+                match RefMut::filter_map(borrow_mut, |any| any.as_mut()?.downcast_mut::<T>()) {
+                    Ok(reference) => Ok(GenerationalRefMut {
+                        inner: reference,
+                        #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+                        borrow: GenerationalRefMutBorrowInfo {
+                            borrowed_from: self.0,
+                        },
+                    }),
+                    Err(_) => Err(BorrowMutError::Dropped(ValueDroppedError {
+                        #[cfg(any(debug_assertions, feature = "debug_ownership"))]
+                        created_at,
+                    })),
+                }
+            }
+            Err(_) => Err(BorrowMutError::AlreadyBorrowed(AlreadyBorrowedError {
+                #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+                borrowed_at: self.0.borrowed_at.borrow().clone(),
+            })),
+        }
+    }
+}
+
+#[derive(Debug, Clone)]
+/// An error that can occur when trying to borrow a value.
+pub enum BorrowError {
+    /// The value was dropped.
+    Dropped(ValueDroppedError),
+    /// The value was already borrowed mutably.
+    AlreadyBorrowedMut(AlreadyBorrowedMutError),
+}
+
+impl Display for BorrowError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            BorrowError::Dropped(error) => Display::fmt(error, f),
+            BorrowError::AlreadyBorrowedMut(error) => Display::fmt(error, f),
+        }
+    }
+}
+
+impl Error for BorrowError {}
+
+#[derive(Debug, Clone)]
+/// An error that can occur when trying to borrow a value mutably.
+pub enum BorrowMutError {
+    /// The value was dropped.
+    Dropped(ValueDroppedError),
+    /// The value was already borrowed.
+    AlreadyBorrowed(AlreadyBorrowedError),
+    /// The value was already borrowed mutably.
+    AlreadyBorrowedMut(AlreadyBorrowedMutError),
+}
+
+impl Display for BorrowMutError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            BorrowMutError::Dropped(error) => Display::fmt(error, f),
+            BorrowMutError::AlreadyBorrowedMut(error) => Display::fmt(error, f),
+            BorrowMutError::AlreadyBorrowed(error) => Display::fmt(error, f),
+        }
+    }
+}
+
+impl Error for BorrowMutError {}
+
+/// An error that can occur when trying to use a value that has been dropped.
+#[derive(Debug, Copy, Clone)]
+pub struct ValueDroppedError {
+    #[cfg(any(debug_assertions, feature = "debug_ownership"))]
+    created_at: &'static std::panic::Location<'static>,
+}
+
+impl Display for ValueDroppedError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.write_str("Failed to borrow because the value was dropped.")?;
+        #[cfg(any(debug_assertions, feature = "debug_ownership"))]
+        f.write_fmt(format_args!("created_at: {}", self.created_at))?;
+        Ok(())
+    }
+}
+
+impl std::error::Error for ValueDroppedError {}
+
+/// An error that can occur when trying to borrow a value that has already been borrowed mutably.
+#[derive(Debug, Copy, Clone)]
+pub struct AlreadyBorrowedMutError {
+    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+    borrowed_mut_at: &'static std::panic::Location<'static>,
+}
+
+impl Display for AlreadyBorrowedMutError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.write_str("Failed to borrow because the value was already borrowed mutably.")?;
+        #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+        f.write_fmt(format_args!("borrowed_mut_at: {}", self.borrowed_mut_at))?;
+        Ok(())
+    }
+}
+
+impl std::error::Error for AlreadyBorrowedMutError {}
+
+/// An error that can occur when trying to borrow a value mutably that has already been borrowed immutably.
+#[derive(Debug, Clone)]
+pub struct AlreadyBorrowedError {
+    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+    borrowed_at: Vec<&'static std::panic::Location<'static>>,
+}
+
+impl Display for AlreadyBorrowedError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.write_str("Failed to borrow mutably because the value was already borrowed immutably.")?;
+        #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+        f.write_str("borrowed_at:")?;
+        #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+        for location in self.borrowed_at.iter() {
+            f.write_fmt(format_args!("\t{}", location))?;
+        }
+        Ok(())
+    }
+}
+
+impl std::error::Error for AlreadyBorrowedError {}
+
+/// A reference to a value in a generational box.
+pub struct GenerationalRef<T: 'static> {
+    inner: Ref<'static, T>,
+    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+    borrow: GenerationalRefBorrowInfo,
+}
+
+impl<T: 'static> GenerationalRef<T> {
+    /// Map one ref type to another.
+    pub fn map<U, F>(orig: GenerationalRef<T>, f: F) -> GenerationalRef<U>
+    where
+        F: FnOnce(&T) -> &U,
+    {
+        GenerationalRef {
+            inner: Ref::map(orig.inner, f),
+            #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+            borrow: GenerationalRefBorrowInfo {
+                borrowed_at: orig.borrow.borrowed_at,
+                borrowed_from: orig.borrow.borrowed_from,
+            },
+        }
+    }
+
+    /// Filter one ref type to another.
+    pub fn filter_map<U, F>(orig: GenerationalRef<T>, f: F) -> Option<GenerationalRef<U>>
+    where
+        F: FnOnce(&T) -> Option<&U>,
+    {
+        let Self {
+            inner,
+            #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+            borrow,
+        } = orig;
+        Ref::filter_map(inner, f).ok().map(|inner| GenerationalRef {
+            inner,
+            #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+            borrow: GenerationalRefBorrowInfo {
+                borrowed_at: borrow.borrowed_at,
+                borrowed_from: borrow.borrowed_from,
+            },
+        })
+    }
+}
+
+impl<T: 'static> Deref for GenerationalRef<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        self.inner.deref()
+    }
+}
+
+#[cfg(any(debug_assertions, feature = "debug_borrows"))]
+struct GenerationalRefBorrowInfo {
+    borrowed_at: &'static std::panic::Location<'static>,
+    borrowed_from: &'static MemoryLocationInner,
+}
+
+#[cfg(any(debug_assertions, feature = "debug_borrows"))]
+impl Drop for GenerationalRefBorrowInfo {
+    fn drop(&mut self) {
+        self.borrowed_from
+            .borrowed_at
+            .borrow_mut()
+            .retain(|location| std::ptr::eq(*location, self.borrowed_at as *const _));
+    }
+}
+
+/// A mutable reference to a value in a generational box.
+pub struct GenerationalRefMut<T: 'static> {
+    inner: RefMut<'static, T>,
+    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+    borrow: GenerationalRefMutBorrowInfo,
+}
+
+impl<T: 'static> GenerationalRefMut<T> {
+    /// Map one ref type to another.
+    pub fn map<U, F>(orig: GenerationalRefMut<T>, f: F) -> GenerationalRefMut<U>
+    where
+        F: FnOnce(&mut T) -> &mut U,
+    {
+        GenerationalRefMut {
+            inner: RefMut::map(orig.inner, f),
+            #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+            borrow: orig.borrow,
+        }
+    }
+
+    /// Filter one ref type to another.
+    pub fn filter_map<U, F>(orig: GenerationalRefMut<T>, f: F) -> Option<GenerationalRefMut<U>>
+    where
+        F: FnOnce(&mut T) -> Option<&mut U>,
+    {
+        let Self {
+            inner,
+            #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+            borrow,
+        } = orig;
+        RefMut::filter_map(inner, f)
+            .ok()
+            .map(|inner| GenerationalRefMut {
+                inner,
+                #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+                borrow,
+            })
+    }
+}
+
+impl<T: 'static> Deref for GenerationalRefMut<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        self.inner.deref()
+    }
+}
+
+impl<T: 'static> DerefMut for GenerationalRefMut<T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        self.inner.deref_mut()
+    }
+}
+
+#[cfg(any(debug_assertions, feature = "debug_borrows"))]
+struct GenerationalRefMutBorrowInfo {
+    borrowed_from: &'static MemoryLocationInner,
+}
+
+#[cfg(any(debug_assertions, feature = "debug_borrows"))]
+impl Drop for GenerationalRefMutBorrowInfo {
+    fn drop(&mut self) {
+        self.borrowed_from.borrowed_mut_at.take();
+    }
 }
 
 /// Owner: Handles dropping generational boxes. The owner acts like a runtime lifetime guard. Any states that you create with an owner will be dropped when that owner is dropped.