prdoclib/commands/
load.rs

1//! Implementation of the load command. This command loads a PRDoc file and outputs its content as
2//! YAML or JSON. Load can also work on several files and aggregates its output as array in that
3//! case.
4
5use crate::{
6	common::PRNumber,
7	config::PRDocConfig,
8	doc_filename::DocFileName,
9	docfile::DocFile,
10	docfile_wrapper::DocFileWrapper,
11	error::{self, Result},
12	schema::Schema,
13	utils::{get_numbers_from_file, get_project_root},
14};
15use std::{
16	collections::HashSet,
17	path::{Path, PathBuf},
18};
19
20/// Wrapper for the load command
21pub struct LoadCmd {
22	pub(crate) schema: Schema,
23}
24
25/// Type alias for the load command result
26pub type LoadResult = (bool, HashSet<DocFileWrapper>);
27
28impl LoadCmd {
29	/// Create a new instance of the load command
30	pub fn new(schema: Schema) -> Self {
31		Self { schema }
32	}
33
34	/// Load PRDoc from one or more numbers
35	pub(crate) fn load_numbers(
36		&self,
37		numbers: Vec<PRNumber>,
38		dir: &PathBuf,
39	) -> error::Result<LoadResult> {
40		let mut global_result = true;
41
42		let res = numbers
43			.iter()
44			.filter_map(|&number| {
45				log::debug!("Loading PR #{}", number);
46				let file_maybe = DocFileName::find(number, None, dir);
47
48				match file_maybe {
49					Ok(file) => {
50						let filename = DocFileName::try_from(&file)
51							.expect("If we found a file, it should be valid");
52
53						let yaml = self.schema.load(&file);
54
55						if let Ok(value) = yaml {
56							Some(DocFileWrapper::new(file, filename, Some(value)))
57						} else {
58							global_result &= false;
59							None
60						}
61					},
62					Err(e) => {
63						global_result &= false;
64						log::warn!("{e:?}");
65						None
66					},
67				}
68			})
69			.collect();
70
71		Ok((global_result, res))
72	}
73
74	/// Load one file and returns a wrapper
75	pub fn load_file(&self, file: &PathBuf) -> Result<DocFileWrapper> {
76		let filename = DocFileName::try_from(file)?;
77		let value = self.schema.load(&file).ok();
78		let wrapper = DocFileWrapper::new(file.clone(), filename, value);
79		Ok(wrapper)
80	}
81
82	pub(crate) fn load_list(&self, file: &PathBuf, dir: &PathBuf) -> Result<LoadResult> {
83		let extract_numbers = get_numbers_from_file(file)?;
84		let numbers: Vec<PRNumber> =
85			extract_numbers.iter().filter_map(|(_, _, n)| n.to_owned()).collect();
86
87		let mut global_result = extract_numbers.iter().map(|(_, status, _)| status).all(|&x| x);
88
89		let (r, wrapper) = self.load_numbers(numbers, dir).unwrap();
90		global_result &= r;
91		Ok((global_result, wrapper))
92	}
93
94	pub(crate) fn load_from_folder(&self, dir: &PathBuf) -> Result<LoadResult> {
95		let res = DocFile::find(self.schema.clone(), dir, false);
96		let mut global_result = res.is_ok();
97
98		let wrapper = res
99			.unwrap()
100			.filter_map(|file| {
101				let filename_maybe = DocFileName::try_from(&file);
102
103				if let Ok(filename) = filename_maybe {
104					let yaml = self.schema.load(&file);
105					if let Ok(value) = yaml {
106						let wrapper = DocFileWrapper::new(file.clone(), filename, Some(value));
107
108						global_result &= true;
109						log::debug!("OK  {}", file.display());
110						Some(wrapper)
111					} else {
112						global_result &= false;
113						log::warn!("ERR {}", file.display());
114						None
115					}
116				} else {
117					log::warn!("Invalid file {:?}", file.display());
118					None
119				}
120			})
121			.collect();
122
123		Ok((global_result, wrapper))
124	}
125
126	/// Run of the load command
127	pub fn run(
128		config: &PRDocConfig,
129		schema: Option<PathBuf>,
130		dir: &PathBuf,
131		file: Option<PathBuf>,
132		numbers: Option<Vec<PRNumber>>,
133		list: Option<PathBuf>,
134	) -> Result<LoadResult> {
135		log::debug!("Loading from directory {}", dir.display());
136
137		let repo_root = get_project_root()?;
138		log::debug!("From repo root: {}", repo_root.canonicalize().unwrap().display());
139
140		let schema_path = if let Some(schema_path) = schema {
141			schema_path
142		} else if config.schema_path().is_absolute() {
143			config.schema_path()
144		} else {
145			repo_root.join(config.schema_path())
146		};
147		log::info!("Using schema: {}", schema_path.canonicalize().unwrap().display());
148
149		let schema = Schema::new(schema_path);
150		let load_cmd = LoadCmd::new(schema);
151
152		Ok(match (file, numbers, list) {
153			(Some(f), None, None) => {
154				let file_abs = if f.is_relative() { Path::new(&dir).join(&f) } else { f.clone() };
155				let mut wrapper = HashSet::new();
156				wrapper.insert(load_cmd.load_file(&file_abs)?);
157
158				(true, wrapper)
159			},
160
161			(None, Some(numbers), None) => {
162				log::debug!("Loading numbers {:?}", numbers);
163				let (global_result, wrapper) = load_cmd.load_numbers(numbers, dir)?;
164				(global_result, wrapper)
165			},
166
167			(None, None, Some(list)) => {
168				log::debug!("Loading list from {:?}", list);
169				let (global_result, wrapper) = load_cmd.load_list(&list, dir)?;
170				(global_result, wrapper)
171			},
172
173			(None, None, None) => {
174				log::debug!("Loading all files in folder {}", dir.display());
175				let (global_result, wrapper) = load_cmd.load_from_folder(dir)?;
176				(global_result, wrapper)
177			},
178
179			_ => unreachable!(),
180		})
181	}
182}