-
Notifications
You must be signed in to change notification settings - Fork 1
⚡ Optimize MDX file formatting with concurrent async I/O #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| use regex::Regex; | ||
| use std::fs; | ||
| use std::path::Path; | ||
| use tokio::fs; | ||
| use walkdir::WalkDir; | ||
|
|
||
| /// Format a single MDX file with all transformations | ||
|
|
@@ -378,22 +378,35 @@ fn wrap_accordions_in_container(content: &str) -> String { | |
| } | ||
|
|
||
| /// Format all MDX files in a directory recursively | ||
| pub fn format_all_mdx_files(docs_dir: &Path) -> crate::error::Result<usize> { | ||
| let mut modified_count = 0; | ||
| pub async fn format_all_mdx_files(docs_dir: &Path) -> crate::error::Result<usize> { | ||
| let mut tasks = Vec::new(); | ||
|
|
||
| for entry in WalkDir::new(docs_dir) | ||
| .into_iter() | ||
| .filter_map(|e| e.ok()) | ||
| .filter(|e| e.path().extension().is_some_and(|ext| ext == "mdx")) | ||
| { | ||
| let path = entry.path(); | ||
| let original = fs::read_to_string(path)?; | ||
| let formatted = format_mdx_file(&original); | ||
| let path = entry.path().to_path_buf(); | ||
|
|
||
| if formatted != original { | ||
| fs::write(path, formatted)?; | ||
| modified_count += 1; | ||
| } | ||
| let task = tokio::spawn(async move { | ||
| let original = fs::read_to_string(&path).await?; | ||
| let formatted = format_mdx_file(&original); | ||
|
|
||
| if formatted != original { | ||
| fs::write(&path, formatted).await?; | ||
| Ok::<usize, std::io::Error>(1) | ||
| } else { | ||
| Ok::<usize, std::io::Error>(0) | ||
| } | ||
| }); | ||
|
|
||
| tasks.push(task); | ||
| } | ||
|
|
||
| let mut modified_count = 0; | ||
| for task in tasks { | ||
| let res = task.await.unwrap()?; // unwrap JoinError, then ? for io::Error | ||
| modified_count += res; | ||
| } | ||
|
|
||
| Ok(modified_count) | ||
|
|
@@ -739,4 +752,24 @@ Final $a$ inline."#; | |
| assert!(output.contains("x = $5")); | ||
| assert!(output.contains(r#"let formula = "$$E=mc^2$$";"#)); | ||
| } | ||
|
|
||
| #[tokio::test] | ||
| async fn test_format_mdx_file_integration_async() { | ||
| let input = r#"<!-- comment --> | ||
| # Title | ||
|  | ||
| <br> | ||
| <div style="text-align:center;">Content</div> | ||
| Math: $x = {1}$ | ||
| {{% details title="Test" %}}Answer{{% /details %}}"#; | ||
|
|
||
| let output = format_mdx_file(input); | ||
|
|
||
| // Check all transformations applied | ||
| assert!(!output.contains("<!--")); | ||
| assert!(!output.contains("shields.io")); | ||
| assert!(output.contains("<br />")); | ||
| assert!(output.contains("textAlign")); | ||
| assert!(output.contains("<Accordion")); | ||
| } | ||
|
Comment on lines
+756
to
+774
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Redundant async test duplicates existing sync test
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JoinErrorsilently panics instead of propagatingtask.awaitreturnsResult<Result<usize, std::io::Error>, JoinError>. The.unwrap()on the outerJoinErrorwill panic the entire process if any spawned task panics or is cancelled — instead of returning a recoverableErrthroughcrate::error::Result. SinceJoinErrordoesn't implementFrom<FumaError>, the automatic?conversion isn't available, but a manual map is straightforward:This converts the
JoinErrorinto astd::io::Error, which already has aFromimpl inFumaError::Io, so the second?then propagates it cleanly throughcrate::error::Result.