diff options
| author | 魏曹先生 <1992414357@qq.com> | 2025-11-06 22:11:10 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2025-11-06 22:11:10 +0800 |
| commit | 986a896062939c41f769b30c90d8d955b959f788 (patch) | |
| tree | e170286efe8120d8995240066ed188141862d2b7 /crates/vcs_docs | |
| parent | d2713f597bb860996f75749d2753e52ebe5297e0 (diff) | |
feat: Add documentation management module (WIP)
- Create vcs_docs crate for documentation handling
- Add build script and Cargo.toml
- NOTE: Core implementation pending
Diffstat (limited to 'crates/vcs_docs')
| -rw-r--r-- | crates/vcs_docs/Cargo.toml | 6 | ||||
| -rw-r--r-- | crates/vcs_docs/build.rs | 182 | ||||
| -rw-r--r-- | crates/vcs_docs/src/docs.rs.template | 26 | ||||
| -rw-r--r-- | crates/vcs_docs/src/lib.rs | 1 |
4 files changed, 215 insertions, 0 deletions
diff --git a/crates/vcs_docs/Cargo.toml b/crates/vcs_docs/Cargo.toml new file mode 100644 index 0000000..285b83d --- /dev/null +++ b/crates/vcs_docs/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "vcs_docs" +edition = "2024" +version.workspace = true + +[dependencies] diff --git a/crates/vcs_docs/build.rs b/crates/vcs_docs/build.rs new file mode 100644 index 0000000..79d1b32 --- /dev/null +++ b/crates/vcs_docs/build.rs @@ -0,0 +1,182 @@ +use std::env; +use std::fs; +use std::io::{self, Write}; +use std::path::Path; + +// Template markers for code generation +const TEMPLATE_DOCUMENT_BEGIN: &str = "--- TEMPLATE DOCUMENT BEGIN ---"; +const TEMPLATE_DOCUMENT_END: &str = "--- TEMPLATE DOCUMENT END ---"; +const TEMPLATE_FUNC_BEGIN: &str = "--- TEMPLATE FUNC BEGIN ---"; +const TEMPLATE_FUNC_END: &str = "--- TEMPLATE FUNC END ---"; +const TEMPLATE_LIST_BEGIN: &str = "--- TEMPLATE LIST BEGIN ---"; +const TEMPLATE_LIST_END: &str = "--- TEMPLATE LIST END ---"; + +// Template parameter patterns for substitution +const PARAM_DOCUMENT_PATH: &str = "{{DOCUMENT_PATH}}"; +const PARAM_DOCUMENT_CONSTANT_NAME: &str = "{{DOCUMENT_CONSTANT_NAME}}"; +const PARAM_DOCUMENT_CONTENT: &str = "{{DOCUMENT_CONTENT}}"; +const PARAM_DOCUMENT_PATH_SNAKE_CASE: &str = "{{DOCUMENT_PATH_SNAKE_CASE}}"; + +fn main() -> io::Result<()> { + println!("cargo:rerun-if-changed=src/docs.rs.template"); + println!("cargo:rerun-if-changed=../../docs/Documents"); + + let out_dir = env::var("OUT_DIR").unwrap(); + let dest_path = Path::new(&out_dir).join("docs.rs"); + + // Read all markdown files from docs directory recursively + let docs_dir = Path::new("../../docs/Documents"); + let mut documents = Vec::new(); + + if docs_dir.exists() { + collect_markdown_files(&docs_dir, &mut documents)?; + } + + // Read template file + let template_path = Path::new("src/docs.rs.template"); + let template_content = fs::read_to_string(template_path)?; + + // Extract template sections preserving original indentation + let document_template = template_content + .split(TEMPLATE_DOCUMENT_BEGIN) + .nth(1) + .and_then(|s| s.split(TEMPLATE_DOCUMENT_END).next()) + .unwrap_or("") + .trim_start_matches('\n') + .trim_end_matches('\n'); + + let match_arm_template = template_content + .split(TEMPLATE_FUNC_BEGIN) + .nth(1) + .and_then(|s| s.split(TEMPLATE_FUNC_END).next()) + .unwrap_or("") + .trim_start_matches('\n') + .trim_end_matches('\n'); + + // Generate document blocks and match arms + let mut document_blocks = String::new(); + let mut match_arms = String::new(); + let mut list_items = String::new(); + + for (relative_path, content) in &documents { + // Calculate parameters for template substitution + let document_path = format!("./docs/Documents/{}", relative_path); + + // Generate constant name from relative path + let document_constant_name = relative_path + .replace('/', "_") + .replace('-', "_") + .replace(".md", "") + .replace(".txt", "") + .to_uppercase(); + + // Generate snake_case name for function matching + let document_path_snake_case = relative_path + .replace('/', "_") + .replace('-', "_") + .replace(".md", "") + .replace(".txt", "") + .to_lowercase(); + + // Replace template parameters in document block preserving indentation + let document_block = document_template + .replace(PARAM_DOCUMENT_PATH, &document_path) + .replace(PARAM_DOCUMENT_CONSTANT_NAME, &document_constant_name) + .replace(PARAM_DOCUMENT_CONTENT, content.trim()) + .replace("r#\"\"#", &format!("r#\"{}\"#", content.trim())); + + document_blocks.push_str(&document_block); + document_blocks.push_str("\n\n"); + + // Replace template parameters in match arm preserving indentation + let match_arm = match_arm_template + .replace(PARAM_DOCUMENT_PATH_SNAKE_CASE, &document_path_snake_case) + .replace(PARAM_DOCUMENT_CONSTANT_NAME, &document_constant_name); + + match_arms.push_str(&match_arm); + match_arms.push_str("\n"); + + // Generate list item for documents() function + let list_item = format!(" \"{}\".to_string(),", document_path_snake_case); + list_items.push_str(&list_item); + list_items.push_str("\n"); + } + + // Remove trailing newline from the last list item + if !list_items.is_empty() { + list_items.pop(); + } + + // Build final output by replacing template sections + let mut output = String::new(); + + // Add header before document blocks + if let Some(header) = template_content.split(TEMPLATE_DOCUMENT_BEGIN).next() { + output.push_str(header.trim()); + output.push_str("\n\n"); + } + + // Add document blocks + output.push_str(&document_blocks); + + // Add function section + if let Some(func_section) = template_content.split(TEMPLATE_FUNC_BEGIN).next() { + if let Some(rest) = func_section.split(TEMPLATE_DOCUMENT_END).nth(1) { + output.push_str(rest.trim()); + output.push_str("\n"); + } + } + + // Add match arms + output.push_str(&match_arms); + + // Add list items for documents() function + if let Some(list_section) = template_content.split(TEMPLATE_LIST_BEGIN).next() { + if let Some(rest) = list_section.split(TEMPLATE_FUNC_END).nth(1) { + output.push_str(rest.trim()); + output.push_str("\n"); + } + } + output.push_str(&list_items); + + // Add footer + if let Some(footer) = template_content.split(TEMPLATE_LIST_END).nth(1) { + // Preserve original indentation in footer + output.push_str(footer); + } + + // Write generated file + let mut file = fs::File::create(&dest_path)?; + file.write_all(output.as_bytes())?; + + // Copy to src directory for development + let src_dest_path = Path::new("src/docs.rs"); + fs::write(src_dest_path, output)?; + + Ok(()) +} + +fn collect_markdown_files(dir: &Path, documents: &mut Vec<(String, String)>) -> io::Result<()> { + for entry in fs::read_dir(dir)? { + let entry = entry?; + let path = entry.path(); + + if path.is_dir() { + collect_markdown_files(&path, documents)?; + } else if path + .extension() + .map_or(false, |ext| ext == "md" || ext == "txt") + { + if let Ok(relative_path) = path.strip_prefix("../../docs/Documents") { + if let Some(relative_path_str) = relative_path.to_str() { + let content = fs::read_to_string(&path)?; + documents.push(( + relative_path_str.trim_start_matches('/').to_string(), + content, + )); + } + } + } + } + Ok(()) +} diff --git a/crates/vcs_docs/src/docs.rs.template b/crates/vcs_docs/src/docs.rs.template new file mode 100644 index 0000000..c6787d9 --- /dev/null +++ b/crates/vcs_docs/src/docs.rs.template @@ -0,0 +1,26 @@ +// Auto-generated code. + +--- TEMPLATE DOCUMENT BEGIN --- +/// From {{DOCUMENT_PATH}} +pub const {{DOCUMENT_CONSTANT_NAME}}: &str = "{{DOCUMENT_CONTENT}}"; + +--- TEMPLATE DOCUMENT END --- + +// Get document content by name +pub fn document(name: impl AsRef<str>) -> Option<String> { + match name.as_ref() { +--- TEMPLATE FUNC BEGIN --- + "{{DOCUMENT_PATH_SNAKE_CASE}}" => Some({{DOCUMENT_CONSTANT_NAME}}.to_string()), +--- TEMPLATE FUNC END --- + _ => None, + } +} + +// Get list of all available document names +pub fn documents() -> Vec<String> { + vec![ +--- TEMPLATE LIST BEGIN --- + "{{DOCUMENT_PATH_SNAKE_CASE}}".to_string(), +--- TEMPLATE LIST END --- + ] +} diff --git a/crates/vcs_docs/src/lib.rs b/crates/vcs_docs/src/lib.rs new file mode 100644 index 0000000..ca422a9 --- /dev/null +++ b/crates/vcs_docs/src/lib.rs @@ -0,0 +1 @@ +pub mod docs; |
