mod.rs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. use futures::{stream::FuturesUnordered, StreamExt};
  2. use std::{fs, process::exit};
  3. use super::*;
  4. // For reference, the rustfmt main.rs file
  5. // https://github.com/rust-lang/rustfmt/blob/master/src/bin/main.rs
  6. /// Build the Rust WASM app and all of its assets.
  7. #[derive(Clone, Debug, Parser)]
  8. pub struct Autoformat {
  9. /// Run in 'check' mode. Exits with 0 if input is formatted correctly. Exits
  10. /// with 1 and prints a diff if formatting is required.
  11. #[clap(short, long)]
  12. pub check: bool,
  13. /// Input rsx (selection)
  14. #[clap(short, long)]
  15. pub raw: Option<String>,
  16. /// Input file
  17. #[clap(short, long)]
  18. pub file: Option<String>,
  19. }
  20. impl Autoformat {
  21. // Todo: autoformat the entire crate
  22. pub async fn autoformat(self) -> Result<()> {
  23. // Default to formatting the project
  24. if self.raw.is_none() && self.file.is_none() {
  25. if let Err(e) = autoformat_project(self.check).await {
  26. eprintln!("error formatting project: {}", e);
  27. exit(1);
  28. }
  29. }
  30. if let Some(raw) = self.raw {
  31. if let Some(inner) = dioxus_autofmt::fmt_block(&raw, 0) {
  32. println!("{}", inner);
  33. } else {
  34. // exit process with error
  35. eprintln!("error formatting codeblock");
  36. exit(1);
  37. }
  38. }
  39. // Format single file
  40. if let Some(file) = self.file {
  41. let file_content = fs::read_to_string(&file);
  42. match file_content {
  43. Ok(s) => {
  44. let edits = dioxus_autofmt::fmt_file(&s);
  45. let out = dioxus_autofmt::apply_formats(&s, edits);
  46. match fs::write(&file, out) {
  47. Ok(_) => {
  48. println!("formatted {}", file);
  49. }
  50. Err(e) => {
  51. eprintln!("failed to write formatted content to file: {}", e);
  52. }
  53. }
  54. }
  55. Err(e) => {
  56. eprintln!("failed to open file: {}", e);
  57. exit(1);
  58. }
  59. }
  60. }
  61. Ok(())
  62. }
  63. }
  64. /// Read every .rs file accessible when considering the .gitignore and try to format it
  65. ///
  66. /// Runs using Tokio for multithreading, so it should be really really fast
  67. ///
  68. /// Doesn't do mod-descending, so it will still try to format unreachable files. TODO.
  69. async fn autoformat_project(check: bool) -> Result<()> {
  70. let crate_config = crate::CrateConfig::new()?;
  71. let mut files_to_format = vec![];
  72. collect_rs_files(&crate_config.crate_dir, &mut files_to_format);
  73. let counts = files_to_format
  74. .into_iter()
  75. .filter(|file| {
  76. if file.components().any(|f| f.as_os_str() == "target") {
  77. return false;
  78. }
  79. true
  80. })
  81. .map(|path| async {
  82. let _path = path.clone();
  83. let res = tokio::spawn(async move {
  84. let contents = tokio::fs::read_to_string(&path).await?;
  85. let edits = dioxus_autofmt::fmt_file(&contents);
  86. let len = edits.len();
  87. if !edits.is_empty() {
  88. let out = dioxus_autofmt::apply_formats(&contents, edits);
  89. tokio::fs::write(&path, out).await?;
  90. }
  91. Ok(len) as Result<usize, tokio::io::Error>
  92. })
  93. .await;
  94. if res.is_err() {
  95. eprintln!("error formatting file: {}", _path.display());
  96. }
  97. res
  98. })
  99. .collect::<FuturesUnordered<_>>()
  100. .collect::<Vec<_>>()
  101. .await;
  102. let files_formatted: usize = counts
  103. .into_iter()
  104. .map(|f| match f {
  105. Ok(Ok(res)) => res,
  106. _ => 0,
  107. })
  108. .sum();
  109. if files_formatted > 0 && check {
  110. eprintln!("{} files needed formatting", files_formatted);
  111. exit(1);
  112. }
  113. Ok(())
  114. }
  115. fn collect_rs_files(folder: &PathBuf, files: &mut Vec<PathBuf>) {
  116. let Ok(folder) = folder.read_dir() else { return };
  117. // load the gitignore
  118. for entry in folder {
  119. let Ok(entry) = entry else { continue; };
  120. let path = entry.path();
  121. if path.is_dir() {
  122. collect_rs_files(&path, files);
  123. }
  124. if let Some(ext) = path.extension() {
  125. if ext == "rs" {
  126. files.push(path);
  127. }
  128. }
  129. }
  130. }
  131. #[test]
  132. fn spawn_properly() {
  133. let out = Command::new("dioxus")
  134. .args([
  135. "fmt",
  136. "-f",
  137. r#"
  138. //
  139. rsx! {
  140. div {}
  141. }
  142. //
  143. //
  144. //
  145. "#,
  146. ])
  147. .output()
  148. .expect("failed to execute process");
  149. dbg!(out);
  150. }