1use std::collections::BTreeSet;
2use std::fs;
3use std::path::{Path, PathBuf};
4
5use crate::diagnostics::{DiagCtx, RunningCheck};
6use crate::features::{CollectedFeatures, Features, Status};
7
8pub const PATH_STR: &str = "doc/unstable-book";
9
10pub const ENV_VARS_DIR: &str = "src/compiler-environment-variables";
11
12pub const COMPILER_FLAGS_DIR: &str = "src/compiler-flags";
13
14pub const LANG_FEATURES_DIR: &str = "src/language-features";
15
16pub const LIB_FEATURES_DIR: &str = "src/library-features";
17
18pub fn unstable_book_path(base_src_path: &Path) -> PathBuf {
20 base_src_path.join(PATH_STR)
21}
22
23pub fn unstable_book_lang_features_path(base_src_path: &Path) -> PathBuf {
26 unstable_book_path(base_src_path).join(LANG_FEATURES_DIR)
27}
28
29pub fn unstable_book_lib_features_path(base_src_path: &Path) -> PathBuf {
32 unstable_book_path(base_src_path).join(LIB_FEATURES_DIR)
33}
34
35fn dir_entry_is_file(dir_entry: &fs::DirEntry) -> bool {
37 dir_entry.file_type().expect("could not determine file type of directory entry").is_file()
38}
39
40pub fn collect_unstable_feature_names(features: &Features) -> BTreeSet<String> {
42 features
43 .iter()
44 .filter(|&(_, f)| f.level == Status::Unstable)
45 .map(|(name, _)| name.replace('_', "-"))
46 .collect()
47}
48
49pub fn collect_unstable_book_section_file_names(dir: &Path) -> BTreeSet<String> {
50 fs::read_dir(dir)
51 .expect("could not read directory")
52 .map(|entry| entry.expect("could not read directory entry"))
53 .filter(dir_entry_is_file)
54 .map(|entry| entry.path())
55 .filter(|path| path.extension().map(|e| e.to_str().unwrap()) == Some("md"))
56 .map(|path| path.file_stem().unwrap().to_str().unwrap().into())
57 .collect()
58}
59
60fn collect_unstable_book_lang_features_section_file_names(
65 base_src_path: &Path,
66) -> BTreeSet<String> {
67 collect_unstable_book_section_file_names(&unstable_book_lang_features_path(base_src_path))
68}
69
70fn collect_unstable_book_lib_features_section_file_names(base_src_path: &Path) -> BTreeSet<String> {
75 collect_unstable_book_section_file_names(&unstable_book_lib_features_path(base_src_path))
76}
77
78fn maybe_suggest_dashes(names: &BTreeSet<String>, feature_name: &str, check: &mut RunningCheck) {
80 let with_dashes = feature_name.replace('_', "-");
81 if names.contains(&with_dashes) {
82 check.error(format!(
83 "the file `{feature_name}.md` contains underscores; use dashes instead: `{with_dashes}.md`",
84 ));
85 }
86}
87
88pub fn check(path: &Path, features: CollectedFeatures, diag_ctx: DiagCtx) {
89 let mut check = diag_ctx.start_check("unstable_book");
90
91 let lang_features = features.lang;
92 let lib_features = features
93 .lib
94 .into_iter()
95 .filter(|(name, _)| !lang_features.contains_key(name))
96 .collect::<Features>();
97
98 let unstable_lib_feature_names = collect_unstable_feature_names(&lib_features);
100 let unstable_book_lib_features_section_file_names =
101 collect_unstable_book_lib_features_section_file_names(path);
102
103 let unstable_lang_feature_names = collect_unstable_feature_names(&lang_features);
105 let unstable_book_lang_features_section_file_names =
106 collect_unstable_book_lang_features_section_file_names(path);
107
108 for feature_name in &unstable_book_lib_features_section_file_names - &unstable_lib_feature_names
110 {
111 check.error(format!(
112 "The Unstable Book has a 'library feature' section '{feature_name}' which doesn't \
113 correspond to an unstable library feature"
114 ));
115 maybe_suggest_dashes(&unstable_lib_feature_names, &feature_name, &mut check);
116 }
117
118 for feature_name in
120 &unstable_book_lang_features_section_file_names - &unstable_lang_feature_names
121 {
122 check.error(format!(
123 "The Unstable Book has a 'language feature' section '{feature_name}' which doesn't \
124 correspond to an unstable language feature"
125 ));
126 maybe_suggest_dashes(&unstable_lang_feature_names, &feature_name, &mut check);
127 }
128
129 }