nmd_core/dossier/
dossier_configuration.rs1pub mod dossier_configuration_style;
2pub mod dossier_configuration_compilation;
3pub mod dossier_configuration_path_reference;
4pub mod dossier_configuration_path_reference_manager;
5pub mod dossier_configuration_table_of_contents;
6pub mod dossier_configuration_bibliography;
7
8use std::collections::HashMap;
9use std::io;
10use std::path::PathBuf;
11
12use dossier_configuration_bibliography::DossierConfigurationBibliography;
13use dossier_configuration_table_of_contents::DossierConfigurationTableOfContents;
14use getset::{Getters, Setters};
15use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
16use serde::{Deserialize, Serialize};
17use log;
18
19use crate::constants::{DOSSIER_CONFIGURATION_JSON_FILE_NAME, DOSSIER_CONFIGURATION_YAML_FILE_NAME, NMD_EXTENSION};
20use crate::resource::text_reference::TextReferenceMap;
21use crate::resource::Resource;
22use crate::resource::{disk_resource::DiskResource, ResourceError};
23use crate::utility::file_utility;
24
25use self::dossier_configuration_path_reference::{DossierConfigurationPathReference, DossierConfigurationRawPathReference};
26use self::dossier_configuration_path_reference_manager::DOSSIER_CONFIGURATION_RAW_REFERENCE_MANAGER;
27use self::{dossier_configuration_compilation::DossierConfigurationCompilation, dossier_configuration_style::DossierConfigurationStyle};
28
29
30
31#[derive(Debug, Clone, Deserialize, Serialize, Getters, Setters)]
32pub struct DossierConfiguration {
33 #[serde(default = "default_name")]
34 #[getset(get = "pub", set = "pub")]
35 name: String,
36
37 #[serde(rename(serialize = "toc", deserialize = "toc"), default = "default_toc")]
38 table_of_contents_configuration: DossierConfigurationTableOfContents,
39
40 #[serde(rename = "documents")]
41 raw_documents_paths: Vec<DossierConfigurationRawPathReference>,
42
43 #[serde(default = "default_style")]
44 style: DossierConfigurationStyle,
45
46 #[serde(default = "default_references")]
47 references: TextReferenceMap,
48
49 #[serde(default = "default_bibliography")]
50 bibliography: DossierConfigurationBibliography,
51
52 #[serde(default = "default_compilation")]
53 compilation: DossierConfigurationCompilation,
54}
55
56fn default_name() -> String {
57 "new-dossier".to_string()
58}
59
60fn default_style() -> DossierConfigurationStyle {
61 DossierConfigurationStyle::default()
62}
63
64fn default_references() -> TextReferenceMap {
65 HashMap::new()
66}
67
68fn default_compilation() -> DossierConfigurationCompilation {
69 DossierConfigurationCompilation::default()
70}
71
72fn default_toc() -> DossierConfigurationTableOfContents {
73 DossierConfigurationTableOfContents::default()
74}
75
76fn default_bibliography() -> DossierConfigurationBibliography {
77 DossierConfigurationBibliography::default()
78}
79
80
81#[allow(dead_code)]
82impl DossierConfiguration {
83
84 pub fn new(root_path: PathBuf, name: String, toc: DossierConfigurationTableOfContents, raw_documents_paths: Vec<String>, style: DossierConfigurationStyle, references: TextReferenceMap, compilation: DossierConfigurationCompilation, bibliography: DossierConfigurationBibliography) -> Self {
85
86 DOSSIER_CONFIGURATION_RAW_REFERENCE_MANAGER.lock().unwrap().set_root_path(root_path);
87
88 Self {
89 name,
90 table_of_contents_configuration: toc,
91 raw_documents_paths,
92 style,
93 references,
94 compilation,
95 bibliography
96 }
97 }
98
99 pub fn raw_documents_paths(&self) -> &Vec<String> {
100 &self.raw_documents_paths
101 }
102
103 pub fn documents_paths(&self) -> Vec<DossierConfigurationPathReference> {
104
105 let dcrfm = DOSSIER_CONFIGURATION_RAW_REFERENCE_MANAGER.lock().unwrap();
106
107 if self.compilation.parallelization() {
108
109 return self.raw_documents_paths.par_iter().map(|raw_reference| {
110 dcrfm.parse_raw_reference(raw_reference, None)
111 }).collect()
112
113 } else {
114
115 return self.raw_documents_paths.iter().map(|raw_reference| {
116 dcrfm.parse_raw_reference(raw_reference, None)
117 }).collect()
118 }
119 }
120
121 pub fn set_raw_documents_paths(&mut self, documents: Vec<String>) -> () {
122 self.raw_documents_paths = documents
123 }
124
125 pub fn append_raw_document_path(&mut self, raw_document_path: String) -> () {
126 self.raw_documents_paths.push(raw_document_path)
127 }
128
129 pub fn style(&self) -> &DossierConfigurationStyle {
130 &self.style
131 }
132
133 pub fn compilation(&self) -> &DossierConfigurationCompilation {
134 &self.compilation
135 }
136
137 pub fn references(&self) -> &TextReferenceMap {
138 &self.references
139 }
140
141 pub fn bibliography(&self) -> &DossierConfigurationBibliography {
142 &self.bibliography
143 }
144
145 pub fn table_of_contents_configuration(&self) -> &DossierConfigurationTableOfContents {
146 &self.table_of_contents_configuration
147 }
148
149 pub fn set_root_path(&mut self, root_path: PathBuf) {
150 DOSSIER_CONFIGURATION_RAW_REFERENCE_MANAGER.lock().unwrap().set_root_path(root_path);
151 }
152
153 pub fn dump_as_yaml(&self, complete_output_path: PathBuf) -> Result<(), ResourceError> {
154 let yaml_string = serde_yaml::to_string(&self).unwrap();
155
156 let mut disk_resource = DiskResource::try_from(complete_output_path)?;
157
158 disk_resource.erase()?;
159
160 log::debug!("dump dossier configuration:\n{:#?}\n", self);
161 disk_resource.write(&yaml_string)?;
162
163 Ok(())
164 }
165
166 pub fn with_files_in_dir(mut self, dir_path: &PathBuf) -> Result<Self, io::Error> {
167 let files = file_utility::all_files_in_dir(dir_path, &vec![NMD_EXTENSION.to_string()])?;
168
169 self.raw_documents_paths = files.iter().map(|f| f.to_string_lossy().to_string()).collect();
170
171 Ok(self)
172 }
173}
174
175impl Default for DossierConfiguration {
176 fn default() -> Self {
177 Self {
178 name: String::from("New Dossier"),
179 raw_documents_paths: vec![],
180 style: DossierConfigurationStyle::default(),
181 references: HashMap::new(),
182 compilation: DossierConfigurationCompilation::default(),
183 table_of_contents_configuration: DossierConfigurationTableOfContents::default(),
184 bibliography: DossierConfigurationBibliography::default()
185 }
186 }
187}
188
189
190impl DossierConfiguration {
191 fn try_from_as_yaml(content: String) -> Result<Self, ResourceError> {
192
193 log::info!("try to load dossier configuration from yaml content...");
194
195 match serde_yaml::from_str(&content) {
196 Ok(config) => {
197 log::info!("dossier configuration loaded from yaml");
198 return Ok(config)
199 },
200 Err(e) => return Err(ResourceError::InvalidResourceVerbose(e.to_string()))
201 }
202 }
203
204 fn try_from_as_json(content: String) -> Result<Self, ResourceError> {
205
206 log::info!("try to load dossier configuration from json content...");
207
208 match serde_json::from_str(&content) {
209 Ok(config) => {
210 log::info!("dossier configuration loaded from json");
211 return Ok(config)
212 },
213 Err(e) => return Err(ResourceError::InvalidResourceVerbose(e.to_string()))
214 }
215 }
216
217 pub fn load(path_buf: &PathBuf) -> Result<Self, ResourceError> {
218 Self::try_from(path_buf)
219 }
220}
221
222impl TryFrom<&PathBuf> for DossierConfiguration {
223 type Error = ResourceError;
224
225 fn try_from(path_buf: &PathBuf) -> Result<Self, Self::Error> {
226
227 if path_buf.is_file() {
228 if let Some(file_name) = path_buf.file_name() {
229
230 let file_content = file_utility::read_file_content(path_buf)?;
231
232 if file_name.to_string_lossy().eq(DOSSIER_CONFIGURATION_YAML_FILE_NAME) {
233
234 log::info!("{} found", DOSSIER_CONFIGURATION_YAML_FILE_NAME);
235
236 let mut config = Self::try_from_as_yaml(file_content)?;
237
238 config.set_root_path(path_buf.clone());
239
240 return Ok(config)
241 }
242
243 if file_name.to_string_lossy().eq(DOSSIER_CONFIGURATION_JSON_FILE_NAME) {
244
245 log::info!("{} found", DOSSIER_CONFIGURATION_JSON_FILE_NAME);
246
247 let mut config = Self::try_from_as_json(file_content)?;
248
249 config.set_root_path(path_buf.clone());
250
251 return Ok(config)
252 }
253 }
254 }
255
256 if path_buf.is_dir() {
257
258 let yaml_path_buf = path_buf.join(DOSSIER_CONFIGURATION_YAML_FILE_NAME);
259
260 if yaml_path_buf.exists() {
261
262 let file_content = file_utility::read_file_content(&yaml_path_buf)?;
263
264 let mut config = Self::try_from_as_yaml(file_content)?;
265
266 config.set_root_path(path_buf.clone());
267
268 return Ok(config)
269 }
270
271 let json_path_buf = path_buf.join(DOSSIER_CONFIGURATION_JSON_FILE_NAME);
272
273 if json_path_buf.exists() {
274
275 let file_content = file_utility::read_file_content(&json_path_buf)?;
276
277 let mut config = Self::try_from_as_json(file_content)?;
278
279 config.set_root_path(path_buf.clone());
280
281 return Ok(config)
282 }
283 }
284
285 Err(ResourceError::ResourceNotFound("dossier configuration".to_string()))
286 }
287}
288
289
290#[cfg(test)]
291mod test {
292 use std::path::PathBuf;
293 use super::*;
294
295
296 #[test]
297 fn apply_root_path() {
298 let project_directory = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
299 let dossier_dir = "nmd-test-dossier-1";
300 let nmd_dossier_path = project_directory.join("test-resources").join(dossier_dir);
301
302 let configuration = DossierConfiguration::try_from(&nmd_dossier_path).unwrap();
303
304 assert_eq!(configuration.documents_paths()[0], nmd_dossier_path.join("d1.nmd").to_string_lossy().to_string())
305 }
306}