prdoclib/
docfile.rs

1//! The content of a `prdoc` file
2
3use log::*;
4use serde_yaml::Value;
5use std::{fs, path::PathBuf};
6
7use crate::{
8	common::PRNumber, doc_filename::DocFileName, error, schema::Schema, utils::get_project_root,
9};
10
11/// Wrapper around filename and content of a `prdoc` file
12#[derive(Debug)]
13pub struct DocFile {
14	/// The file path
15	pub file: PathBuf,
16
17	/// The content of the PRDoc
18	pub content: Value,
19
20	/// Schema
21	pub schema: Schema,
22}
23
24// impl From<PathBuf> for DocFile {
25// 	fn from(file: PathBuf) -> Self {
26// 		let content = Self::load(schema, &file).unwrap();
27// 		Self { file, content, schema }
28// 	}
29// }
30
31impl DocFile {
32	/// Create a new instance of a `prdoc` file
33	pub fn new(schema: Schema, file: PathBuf) -> Self {
34		let content = Self::load(schema.clone(), &file).unwrap();
35		Self { file, content, schema }
36	}
37
38	/// Load a `prdoc` file given its PR number
39	pub fn load_from_number(schema: Schema, n: PRNumber) -> Self {
40		let filename = DocFileName::from(n);
41		let file = PathBuf::from(filename);
42		let content = Self::load(schema.clone(), &file).unwrap();
43		Self { file, content, schema }
44	}
45
46	/// Attempt to load a `prdoc` file given its filename and schema
47	pub fn load(schema: Schema, file: &PathBuf) -> crate::error::Result<Value> {
48		schema.load(file)
49	}
50
51	/// Generate a new PRDoc
52	pub fn generate(file: PathBuf) -> error::Result<String> {
53		let template_file = if file.is_absolute() {
54			file
55		} else {
56			let repo_root = get_project_root().expect("We need to work in a repo");
57			repo_root.join(file)
58		};
59
60		match fs::read_to_string(&template_file) {
61			Ok(res) => Ok(res),
62			Err(ref e) if e.kind() == std::io::ErrorKind::NotFound =>
63				Err(error::PRdocLibError::MissingTemplateFile(template_file)),
64			Err(e) => Err(error::PRdocLibError::IO(e)),
65		}
66	}
67
68	/// Returns an iterator if the `dir` was a valid directory or an error otherwise.
69	pub fn find(
70		schema: Schema,
71		dir: &PathBuf,
72		valid_only: bool,
73	) -> crate::error::Result<impl Iterator<Item = PathBuf>> {
74		trace!("valid_only: {valid_only}");
75
76		let res = std::fs::read_dir(dir)?
77			.filter_map(|res| res.ok())
78			// Map the directory entries to paths
79			.map(|dir_entry| dir_entry.path())
80			// Filter out all paths with extensions other than what we want
81			.filter_map(|path| {
82				if path.extension().map_or(false, |ext| ext == "prdoc") {
83					Some(path)
84				} else {
85					None
86				}
87			})
88			.filter_map(move |path| {
89				if valid_only {
90					let is_valid = DocFileName::is_valid(&path);
91					trace!(
92						"{}: filename {}",
93						path.display(),
94						if is_valid { " VALID " } else { "INVALID" }
95					);
96					if is_valid {
97						Some(path)
98					} else {
99						None
100					}
101				} else {
102					Some(path)
103				}
104			})
105			.filter_map(move |path| {
106				let schema_valid = schema.check_file(&path);
107				trace!(
108					"{}: schema {}",
109					path.display(),
110					if schema_valid { " VALID " } else { "INVALID" }
111				);
112
113				if valid_only {
114					if schema_valid {
115						Some(path)
116					} else {
117						None
118					}
119				} else {
120					Some(path)
121				}
122			});
123		Ok(res)
124	}
125}