diff options
Diffstat (limited to 'xtask/src/codegen/gen_feature_docs.rs')
-rw-r--r-- | xtask/src/codegen/gen_feature_docs.rs | 75 |
1 files changed, 75 insertions, 0 deletions
diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs new file mode 100644 index 000000000..31bc3839d --- /dev/null +++ b/xtask/src/codegen/gen_feature_docs.rs | |||
@@ -0,0 +1,75 @@ | |||
1 | //! Generates `assists.md` documentation. | ||
2 | |||
3 | use std::{fmt, fs, path::PathBuf}; | ||
4 | |||
5 | use crate::{ | ||
6 | codegen::{self, extract_comment_blocks_with_empty_lines, Location, Mode}, | ||
7 | project_root, rust_files, Result, | ||
8 | }; | ||
9 | |||
10 | pub fn generate_feature_docs(mode: Mode) -> Result<()> { | ||
11 | let features = Feature::collect()?; | ||
12 | let contents = features.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); | ||
13 | let contents = contents.trim().to_string() + "\n"; | ||
14 | let dst = project_root().join("docs/user/generated_features.adoc"); | ||
15 | codegen::update(&dst, &contents, mode)?; | ||
16 | Ok(()) | ||
17 | } | ||
18 | |||
19 | #[derive(Debug)] | ||
20 | struct Feature { | ||
21 | id: String, | ||
22 | location: Location, | ||
23 | doc: String, | ||
24 | } | ||
25 | |||
26 | impl Feature { | ||
27 | fn collect() -> Result<Vec<Feature>> { | ||
28 | let mut res = Vec::new(); | ||
29 | for path in rust_files(&project_root()) { | ||
30 | collect_file(&mut res, path)?; | ||
31 | } | ||
32 | res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id)); | ||
33 | return Ok(res); | ||
34 | |||
35 | fn collect_file(acc: &mut Vec<Feature>, path: PathBuf) -> Result<()> { | ||
36 | let text = fs::read_to_string(&path)?; | ||
37 | let comment_blocks = extract_comment_blocks_with_empty_lines("Feature", &text); | ||
38 | |||
39 | for block in comment_blocks { | ||
40 | let id = block.id; | ||
41 | assert!(is_valid_feature_name(&id), "invalid feature name: {:?}", id); | ||
42 | let doc = block.contents.join("\n"); | ||
43 | let location = Location::new(path.clone(), block.line); | ||
44 | acc.push(Feature { id, location, doc }) | ||
45 | } | ||
46 | |||
47 | Ok(()) | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | |||
52 | fn is_valid_feature_name(feature: &str) -> bool { | ||
53 | 'word: for word in feature.split_whitespace() { | ||
54 | for &short in ["to", "and"].iter() { | ||
55 | if word == short { | ||
56 | continue 'word; | ||
57 | } | ||
58 | } | ||
59 | for &short in ["To", "And"].iter() { | ||
60 | if word == short { | ||
61 | return false; | ||
62 | } | ||
63 | } | ||
64 | if !word.starts_with(char::is_uppercase) { | ||
65 | return false; | ||
66 | } | ||
67 | } | ||
68 | true | ||
69 | } | ||
70 | |||
71 | impl fmt::Display for Feature { | ||
72 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
73 | writeln!(f, "=== {}\n**Source:** {}\n{}", self.id, self.location, self.doc) | ||
74 | } | ||
75 | } | ||