nmd_core/dossier/
dossier_configuration.rs

1pub 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}