From ec9edc294fd5e7e29977fc7b0e6fb953422bc0e2 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Wed, 19 Aug 2026 05:54:00 +0800 Subject: chore: reorganize dev tools into dev/ directory and consolidate configs Move CI, dev tools, and configs from root-level scattered locations into a unified `dev/` directory structure. Update all references across build scripts, documentation, and editor configurations. Also consolidate editor config generation into build.rs for automated synchronization of rust-analyzer settings across VS Code and Zed. --- mingling_ci/src/tools/example_refresh.rs | 279 ------------------------------- 1 file changed, 279 deletions(-) delete mode 100644 mingling_ci/src/tools/example_refresh.rs (limited to 'mingling_ci/src/tools/example_refresh.rs') diff --git a/mingling_ci/src/tools/example_refresh.rs b/mingling_ci/src/tools/example_refresh.rs deleted file mode 100644 index ca8443c..0000000 --- a/mingling_ci/src/tools/example_refresh.rs +++ /dev/null @@ -1,279 +0,0 @@ -//! Regenerates the example documentation module and the examples index. - -use std::collections::HashMap; -use std::fs; -use std::path::Path; - -use just_fmt::snake_case; -use just_template::Template; -use mingling::{ - Grouped, RenderResult, Routable, - macros::{buffer, command, r_println, renderer}, -}; -use serde::Serialize; - -use crate::Next; -use crate::res::{CargoError, MessagePrinter}; - -const EXAMPLE_ROOT: &str = "./examples"; -const EXAMPLE_DOCS_OUTPUT: &str = "./mingling/src/example_docs.rs"; -const EXAMPLE_DOCS_TEMPLATE: &str = include_str!("../../../mingling/src/example_docs.rs.tmpl"); -const EXAMPLES_JSON_OUTPUT: &str = "./docs/example-pages/examples.json"; - -#[command(node = "example-refresh")] -pub fn example_refresh() -> Next { - match refresh_all() { - Ok(written) => ResultExampleRefresh { written }.to_chain(), - Err(e) => ErrorExampleRefresh(e).to_chain(), - } -} - -fn refresh_all() -> Result, String> { - let mut written = Vec::new(); - written.extend(refresh_example_docs()?); - written.extend(sync_examples()?); - Ok(written) -} - -/// Part 1: regenerate `mingling/src/example_docs.rs` from the examples' -/// `src/main.rs` (header `//!` + code) and `Cargo.toml`. -fn refresh_example_docs() -> Result, String> { - let mut template = Template::from(EXAMPLE_DOCS_TEMPLATE); - - let mut examples = Vec::new(); - let entries = - fs::read_dir(EXAMPLE_ROOT).map_err(|e| format!("failed to read {EXAMPLE_ROOT}: {e}"))?; - for entry in entries.flatten() { - let path = entry.path(); - if !path.is_dir() { - continue; - } - let name = entry.file_name().to_string_lossy().into_owned(); - if !name.starts_with("example-") { - continue; - } - examples.push(ExampleContent::read(&name)); - } - examples.sort_by(|a, b| a.name.cmp(&b.name)); - - let mut written = Vec::new(); - for example in examples { - template - .add_impl("examples".to_string()) - .push(HashMap::from([ - ("example_header".to_string(), example.header), - ("example_import".to_string(), example.cargo_toml), - ("example_code".to_string(), example.code), - ("example_name".to_string(), snake_case!(&example.name)), - ])); - written.push(format!("example_docs: {}", example.name)); - } - - let template_str = template.to_string(); - let template_str = template_str - .lines() - .map(str::trim_end) - .collect::>() - .join("\n") - + "\n"; - fs::write(EXAMPLE_DOCS_OUTPUT, template_str) - .map_err(|e| format!("failed to write {EXAMPLE_DOCS_OUTPUT}: {e}"))?; - written.push(format!("written: {EXAMPLE_DOCS_OUTPUT}")); - Ok(written) -} - -struct ExampleContent { - name: String, - header: String, - code: String, - cargo_toml: String, -} - -impl ExampleContent { - fn read(name: &str) -> Self { - let prefix = |s: &str| { - s.lines() - .map(|line| format!("/// {line}")) - .collect::>() - .join("\n") - }; - - let (header, code) = read_header_and_code(name); - Self { - name: name.to_string(), - header: prefix(&header), - code: prefix(&code), - cargo_toml: prefix(&read_cargo_toml(name)), - } - } -} - -/// Reads an example's `src/main.rs`, splitting `//!` doc header from code. -fn read_header_and_code(name: &str) -> (String, String) { - let content = fs::read_to_string(Path::new(EXAMPLE_ROOT).join(name).join("src/main.rs")) - .unwrap_or_default(); - let mut lines = content.lines(); - let mut header = String::new(); - let mut code = String::new(); - - for line in lines.by_ref() { - if line.trim_start().starts_with("//!") { - header.push_str(line.trim_start_matches("//!")); - header.push('\n'); - } else { - code.push_str(line); - code.push('\n'); - break; - } - } - for line in lines { - code.push_str(line); - code.push('\n'); - } - - (header.trim().to_string(), code.trim().to_string()) -} - -fn read_cargo_toml(name: &str) -> String { - fs::read_to_string(Path::new(EXAMPLE_ROOT).join(name).join("Cargo.toml")).unwrap_or_default() -} - -/// Part 2: regenerate `docs/example-pages/examples.json` from each example's -/// `page.toml`. -fn sync_examples() -> Result, String> { - fs::create_dir_all("docs/example-pages") - .map_err(|e| format!("failed to create docs/example-pages: {e}"))?; - - let mut examples = Vec::new(); - let entries = - fs::read_dir(EXAMPLE_ROOT).map_err(|e| format!("failed to read {EXAMPLE_ROOT}: {e}"))?; - for entry in entries.flatten() { - let path = entry.path(); - if !path.is_dir() { - continue; - } - let dir_name = entry.file_name().to_string_lossy().into_owned(); - let page_toml = path.join("page.toml"); - if !page_toml.is_file() { - continue; - } - let Ok(content) = fs::read_to_string(&page_toml) else { - continue; - }; - let Ok(table) = content.parse::() else { - eprintln!("Warning: failed to parse {}", page_toml.display()); - continue; - }; - let Some(example) = table.get("example") else { - continue; - }; - - let get = |key: &str| { - example - .get(key) - .and_then(|v| v.as_str()) - .unwrap_or_default() - }; - let str_vec = |key: &str| { - example - .get(key) - .and_then(|v| v.as_array()) - .map(|a| { - a.iter() - .filter_map(|v| v.as_str().map(str::to_string)) - .collect() - }) - .unwrap_or_default() - }; - - let id = get("id"); - examples.push(ExampleMeta { - id: if id.is_empty() { - dir_name.clone() - } else { - id.to_string() - }, - name: { - let name = get("name"); - if name.is_empty() { - dir_name.clone() - } else { - name.to_string() - } - }, - icon: { - let icon = get("icon"); - if icon.is_empty() { - "📦".to_string() - } else { - icon.to_string() - } - }, - category: get("category").to_string(), - desc: get("desc").to_string(), - tags: str_vec("tags"), - files: { - let files = str_vec("files"); - if files.is_empty() { - vec!["Cargo.toml".to_string(), "src/main.rs".to_string()] - } else { - files - } - }, - }); - } - - // Basic first, then alphabetical. - examples.sort_by( - |a, b| match (a.id == "example-basic", b.id == "example-basic") { - (true, false) => std::cmp::Ordering::Less, - (false, true) => std::cmp::Ordering::Greater, - _ => a.id.cmp(&b.id), - }, - ); - - let json = serde_json::to_string_pretty(&examples) - .map_err(|e| format!("failed to serialize examples: {e}"))?; - fs::write(EXAMPLES_JSON_OUTPUT, json) - .map_err(|e| format!("failed to write {EXAMPLES_JSON_OUTPUT}: {e}"))?; - - Ok(vec![format!( - "synced: {} examples -> {EXAMPLES_JSON_OUTPUT}", - examples.len() - )]) -} - -/// One entry of `docs/example-pages/examples.json`. -#[derive(Serialize)] -struct ExampleMeta { - id: String, - name: String, - icon: String, - category: String, - desc: String, - tags: Vec, - files: Vec, -} - -/// Files written by `example-refresh`. -#[derive(Grouped)] -pub struct ResultExampleRefresh { - pub written: Vec, -} - -#[derive(Grouped, Default)] -pub struct ErrorExampleRefresh(pub String); - -#[renderer(buffer)] -pub fn render_example_refresh(r: ResultExampleRefresh) { - for item in r.written { - r_println!("{item}"); - } -} - -#[renderer] -pub fn render_error_example_refresh(e: ErrorExampleRefresh, error: &CargoError) -> RenderResult { - let render_result = RenderResult::new(); - error.println(vec![e.0]); - render_result -} -- cgit