1use crate::{
5 common::PRNumber,
6 config::PRDocConfig,
7 doc_filename::DocFileName,
8 docfile::DocFile,
9 error::{self, PRdocLibError},
10 prdoc_source::PRDocSource,
11 schema::Schema,
12 utils::{get_numbers_from_file, get_project_root},
13};
14use std::{
15 collections::HashSet,
16 path::{Path, PathBuf},
17};
18
19pub struct CheckCmd {
21 pub(crate) schema: Schema,
22}
23
24pub type CheckResult = (PRDocSource, bool);
28
29impl CheckCmd {
30 pub fn new(schema: Schema) -> Self {
32 Self { schema }
33 }
34
35 pub(crate) fn check_numbers(
36 &self,
37 numbers: Vec<PRNumber>,
38 dir: &PathBuf,
39 ) -> error::Result<HashSet<CheckResult>> {
40 log::debug!("Checking PRs: {:?}", numbers);
41
42 let res = numbers
43 .iter()
44 .map(|&number| {
45 log::debug!("Checking PR #{}", number);
46
47 let file_maybe = DocFileName::find(number, None, dir);
48
49 match file_maybe {
50 Ok(file) => {
51 log::debug!("Attempting to load file: {}", file.display());
52 let yaml = self.schema.load(&file);
53
54 match yaml {
55 Ok(_value) => {
56 log::debug!("Loading was OK");
57 (number.into(), true)
58 },
59 Err(PRdocLibError::ValidationErrors(validation)) => {
60 log::info!("errors: {:#?}", validation.errors);
61 log::info!("missing: {:#?}", validation.missing);
62 (number.into(), false)
63 },
64 Err(e) => {
65 log::error!("Loading the schema failed:");
66 log::error!("{}", e.to_string());
67 (number.into(), false)
68 },
69 }
70 },
71 Err(e) => {
72 log::error!("{}", e.to_string());
73 (number.into(), false)
74 },
75 }
76 })
77 .collect();
78
79 Ok(res)
80 }
81
82 pub(crate) fn _check_number(
84 &self,
85 number: PRNumber,
86 dir: &PathBuf,
87 ) -> error::Result<CheckResult> {
88 let file = DocFileName::find(number, None, dir)?;
89 Ok((file.clone().into(), self.check_file(&file).1))
90 }
91
92 pub(crate) fn check_file(&self, file: &PathBuf) -> CheckResult {
95 log::debug!("Checking file {}", file.display());
96
97 let value = self.schema.load(&file);
98 let filename_maybe = DocFileName::try_from(file);
99 if let Ok(_value) = value {
100 if let Ok(filename) = filename_maybe {
101 (filename.into(), true)
102 } else {
103 (file.into(), false)
104 }
105 } else if let Ok(f) = filename_maybe {
106 (f.into(), false)
107 } else {
108 (file.into(), false)
109 }
110 }
111
112 pub(crate) fn check_files_in_folder(
116 self,
117 dir: &PathBuf,
118 valid_only: bool,
119 ) -> error::Result<HashSet<CheckResult>> {
120 log::debug!("Checking all files in folder {}", dir.display());
121
122 let schema = self.schema.clone();
123 let files = DocFile::find(schema, dir, valid_only)?
124 .filter(|f| !f.file_name().unwrap_or_default().to_string_lossy().starts_with('.'));
125 let hs: HashSet<CheckResult> = files.map(|f| self.check_file(&f)).collect();
126 Ok(hs)
127 }
128
129 pub(crate) fn check_list(
133 &self,
134 file: &PathBuf,
135 dir: &PathBuf,
136 ) -> error::Result<HashSet<CheckResult>> {
137 let extract_numbers = get_numbers_from_file(file)?;
138
139 let numbers: Vec<PRNumber> =
140 extract_numbers.iter().filter_map(|(_, _, n)| n.to_owned()).collect();
141
142 self.check_numbers(numbers, dir)
143 }
144
145 pub fn global_result(hs: HashSet<CheckResult>) -> bool {
147 for item in hs.iter() {
148 if !item.1 {
149 return false;
150 }
151 }
152
153 true
154 }
155
156 pub fn run(
161 config: &PRDocConfig,
162 schema: Option<PathBuf>,
163 dir: &PathBuf,
164 file: Option<PathBuf>,
165 numbers: Option<Vec<PRNumber>>,
166 list: Option<PathBuf>,
167 ) -> crate::error::Result<HashSet<CheckResult>> {
168 log::info!("Checking directory {}", dir.display());
169 log::debug!("From dir: {}", dir.canonicalize().unwrap().display());
170
171 let repo_root = get_project_root()?;
172 log::debug!("From repo root: {}", repo_root.canonicalize().unwrap().display());
173
174 let schema_path = if let Some(schema_path) = schema {
175 schema_path
176 } else if config.schema_path().is_absolute() {
177 config.schema_path()
178 } else {
179 repo_root.join(config.schema_path())
180 };
181
182 log::info!("Using schema: {}", schema_path.canonicalize().unwrap().display());
183 let schema = Schema::new(schema_path);
184
185 let check_cmd = CheckCmd::new(schema);
186
187 match (file, numbers, list) {
188 (Some(file), None, None) => {
189 let file =
190 if file.is_relative() { Path::new(&dir).join(&file) } else { file.clone() };
191
192 let mut hs = HashSet::new();
193 let _ = hs.insert(check_cmd.check_file(&file));
194 Ok(hs)
195 },
196
197 (None, Some(numbers), None) => check_cmd.check_numbers(numbers, dir),
198 (None, None, Some(list)) => check_cmd.check_list(&list, dir),
199 (None, None, None) => check_cmd.check_files_in_folder(dir, false),
200
201 _ => unreachable!(),
202 }
203 }
204}