1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use crate::features::{CollectedFeatures, Features, Status};
use std::collections::BTreeSet;
use std::fs;
use std::path::{Path, PathBuf};

pub const PATH_STR: &str = "doc/unstable-book";

pub const COMPILER_FLAGS_DIR: &str = "src/compiler-flags";

pub const LANG_FEATURES_DIR: &str = "src/language-features";

pub const LIB_FEATURES_DIR: &str = "src/library-features";

/// Builds the path to the Unstable Book source directory from the Rust 'src' directory.
pub fn unstable_book_path(base_src_path: &Path) -> PathBuf {
    base_src_path.join(PATH_STR)
}

/// Builds the path to the directory where the features are documented within the Unstable Book
/// source directory.
pub fn unstable_book_lang_features_path(base_src_path: &Path) -> PathBuf {
    unstable_book_path(base_src_path).join(LANG_FEATURES_DIR)
}

/// Builds the path to the directory where the features are documented within the Unstable Book
/// source directory.
pub fn unstable_book_lib_features_path(base_src_path: &Path) -> PathBuf {
    unstable_book_path(base_src_path).join(LIB_FEATURES_DIR)
}

/// Tests whether `DirEntry` is a file.
fn dir_entry_is_file(dir_entry: &fs::DirEntry) -> bool {
    dir_entry.file_type().expect("could not determine file type of directory entry").is_file()
}

/// Retrieves names of all unstable features.
pub fn collect_unstable_feature_names(features: &Features) -> BTreeSet<String> {
    features
        .iter()
        .filter(|&(_, ref f)| f.level == Status::Unstable)
        .map(|(name, _)| name.replace('_', "-"))
        .collect()
}

pub fn collect_unstable_book_section_file_names(dir: &Path) -> BTreeSet<String> {
    fs::read_dir(dir)
        .expect("could not read directory")
        .map(|entry| entry.expect("could not read directory entry"))
        .filter(dir_entry_is_file)
        .map(|entry| entry.path())
        .filter(|path| path.extension().map(|e| e.to_str().unwrap()) == Some("md"))
        .map(|path| path.file_stem().unwrap().to_str().unwrap().into())
        .collect()
}

/// Retrieves file names of all library feature sections in the Unstable Book with:
///
/// * hyphens replaced by underscores,
/// * the markdown suffix ('.md') removed.
fn collect_unstable_book_lang_features_section_file_names(
    base_src_path: &Path,
) -> BTreeSet<String> {
    collect_unstable_book_section_file_names(&unstable_book_lang_features_path(base_src_path))
}

/// Retrieves file names of all language feature sections in the Unstable Book with:
///
/// * hyphens replaced by underscores,
/// * the markdown suffix ('.md') removed.
fn collect_unstable_book_lib_features_section_file_names(base_src_path: &Path) -> BTreeSet<String> {
    collect_unstable_book_section_file_names(&unstable_book_lib_features_path(base_src_path))
}

pub fn check(path: &Path, features: CollectedFeatures, bad: &mut bool) {
    let lang_features = features.lang;
    let lib_features = features
        .lib
        .into_iter()
        .filter(|&(ref name, _)| !lang_features.contains_key(name))
        .collect::<Features>();

    // Library features
    let unstable_lib_feature_names = collect_unstable_feature_names(&lib_features);
    let unstable_book_lib_features_section_file_names =
        collect_unstable_book_lib_features_section_file_names(path);

    // Language features
    let unstable_lang_feature_names = collect_unstable_feature_names(&lang_features);
    let unstable_book_lang_features_section_file_names =
        collect_unstable_book_lang_features_section_file_names(path);

    // Check for Unstable Book sections that don't have a corresponding unstable feature
    for feature_name in &unstable_book_lib_features_section_file_names - &unstable_lib_feature_names
    {
        if !unstable_lang_feature_names.contains(&feature_name) {
            tidy_error!(
                bad,
                "The Unstable Book has a 'library feature' section '{}' which doesn't \
                         correspond to an unstable library feature",
                feature_name
            );
        }
    }

    // Check for Unstable Book sections that don't have a corresponding unstable feature.
    for feature_name in
        &unstable_book_lang_features_section_file_names - &unstable_lang_feature_names
    {
        tidy_error!(
            bad,
            "The Unstable Book has a 'language feature' section '{}' which doesn't \
                     correspond to an unstable language feature",
            feature_name
        )
    }

    // List unstable features that don't have Unstable Book sections.
    // Remove the comment marker if you want the list printed.
    /*
    println!("Lib features without unstable book sections:");
    for feature_name in &unstable_lang_feature_names -
                        &unstable_book_lang_features_section_file_names {
        println!("    * {} {:?}", feature_name, lib_features[&feature_name].tracking_issue);
    }

    println!("Lang features without unstable book sections:");
    for feature_name in &unstable_lib_feature_names-
                        &unstable_book_lib_features_section_file_names {
        println!("    * {} {:?}", feature_name, lang_features[&feature_name].tracking_issue);
    }
    // */
}