Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'xtask/src/codegen/feature_docs.rs')
| -rw-r--r-- | xtask/src/codegen/feature_docs.rs | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/xtask/src/codegen/feature_docs.rs b/xtask/src/codegen/feature_docs.rs new file mode 100644 index 0000000000..c6451d888b --- /dev/null +++ b/xtask/src/codegen/feature_docs.rs @@ -0,0 +1,85 @@ +//! Generates `assists.md` documentation. + +use std::{fmt, fs, io, path::PathBuf}; + +use crate::{ + codegen::{CommentBlock, Location}, + project_root, + util::list_rust_files, +}; + +pub(crate) fn generate(_check: bool) { + let features = Feature::collect().unwrap(); + let contents = features.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); + let contents = format!( + " +// Generated file, do not edit by hand, see `sourcegen_feature_docs`. +{} +", + contents.trim() + ); + let dst = project_root().join("docs/user/generated_features.adoc"); + fs::write(dst, contents).unwrap(); +} + +#[derive(Debug)] +struct Feature { + id: String, + location: Location, + doc: String, +} + +impl Feature { + fn collect() -> io::Result<Vec<Feature>> { + let crates_dir = project_root().join("crates"); + + let mut res = Vec::new(); + for path in list_rust_files(&crates_dir) { + collect_file(&mut res, path)?; + } + res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id)); + return Ok(res); + + fn collect_file(acc: &mut Vec<Feature>, path: PathBuf) -> io::Result<()> { + let text = std::fs::read_to_string(&path)?; + let comment_blocks = CommentBlock::extract("Feature", &text); + + for block in comment_blocks { + let id = block.id; + if let Err(msg) = is_valid_feature_name(&id) { + panic!("invalid feature name: {id:?}:\n {msg}") + } + let doc = block.contents.join("\n"); + let location = Location { file: path.clone(), line: block.line }; + acc.push(Feature { id, location, doc }) + } + + Ok(()) + } + } +} + +fn is_valid_feature_name(feature: &str) -> Result<(), String> { + 'word: for word in feature.split_whitespace() { + for short in ["to", "and"] { + if word == short { + continue 'word; + } + } + for short in ["To", "And"] { + if word == short { + return Err(format!("Don't capitalize {word:?}")); + } + } + if !word.starts_with(char::is_uppercase) { + return Err(format!("Capitalize {word:?}")); + } + } + Ok(()) +} + +impl fmt::Display for Feature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "=== {}\n**Source:** {}\n{}", self.id, self.location, self.doc) + } +} |