summaryrefslogtreecommitdiff
path: root/docs/build.rs
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-01-12 04:28:28 +0800
committer魏曹先生 <1992414357@qq.com>2026-01-12 04:51:34 +0800
commitc5fb22694e95f12c24b8d8af76999be7aea3fcec (patch)
tree399d8a24ce491fb635f3d09f2123290fe784059e /docs/build.rs
parent444754489aca0454eb54e15a49fb8a6db0b68a07 (diff)
Reorganize crate structure and move documentation files
Diffstat (limited to 'docs/build.rs')
-rw-r--r--docs/build.rs196
1 files changed, 196 insertions, 0 deletions
diff --git a/docs/build.rs b/docs/build.rs
new file mode 100644
index 0000000..53679db
--- /dev/null
+++ b/docs/build.rs
@@ -0,0 +1,196 @@
+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=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("./Documents");
+ let mut documents = Vec::new();
+
+ if docs_dir.exists() {
+ collect_text_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(".md", "")
+ .replace(".txt", "")
+ .replace(".toml", "")
+ .replace(".yaml", "")
+ .replace(".yml", "")
+ .replace(".json", "")
+ .replace(".rs", "")
+ .to_uppercase();
+
+ // Generate snake_case name for function matching
+ let document_path_snake_case = relative_path
+ .replace(['/', '\\', '-'], "_")
+ .replace(".md", "")
+ .replace(".txt", "")
+ .replace(".toml", "")
+ .replace(".yaml", "")
+ .replace(".yml", "")
+ .replace(".json", "")
+ .replace(".rs", "")
+ .to_lowercase();
+
+ // Escape double quotes in content
+ let escaped_content = content.trim().replace('\"', "\\\"");
+
+ // 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, &escaped_content)
+ .replace("r#\"\"#", &format!("r#\"{}\"#", escaped_content));
+
+ 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('\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('\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()
+ && let Some(rest) = func_section.split(TEMPLATE_DOCUMENT_END).nth(1)
+ {
+ output.push_str(rest.trim());
+ output.push('\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()
+ && let Some(rest) = list_section.split(TEMPLATE_FUNC_END).nth(1)
+ {
+ output.push_str(rest.trim());
+ output.push('\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_text_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_text_files(&path, documents)?;
+ } else if path.extension().is_some_and(|ext| {
+ ext == "md"
+ || ext == "txt"
+ || ext == "toml"
+ || ext == "yaml"
+ || ext == "yml"
+ || ext == "json"
+ || ext == "rs"
+ }) && let Ok(relative_path) = path.strip_prefix("./Documents")
+ && 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(())
+}