nmd_core/dossier/
document.rs1pub mod chapter;
2
3
4use std::path::PathBuf;
5use std::time::Instant;
6
7pub use chapter::Chapter;
8use getset::{Getters, MutGetters, Setters};
9use rayon::slice::ParallelSliceMut;
10use serde::Serialize;
11use thiserror::Error;
12use crate::codex::Codex;
13use crate::compilation::compilation_configuration::compilation_configuration_overlay::CompilationConfigurationOverLay;
14use crate::compilation::compilation_configuration::CompilationConfiguration;
15use crate::compilation::compilation_error::CompilationError;
16use crate::compilation::compilable::Compilable;
17use crate::compilation::compilation_outcome::CompilationOutcome;
18use crate::content_bundle::ContentBundle;
19use crate::load::{LoadConfiguration, LoadConfigurationOverLay, LoadError};
20use crate::load_block::LoadBlock;
21use crate::output_format::OutputFormat;
22use crate::resource::disk_resource::DiskResource;
23use crate::resource::{Resource, ResourceError};
24use self::chapter::paragraph::ParagraphError;
25
26
27#[derive(Error, Debug)]
28pub enum DocumentError {
29 #[error(transparent)]
30 Load(#[from] ResourceError),
31
32 #[error(transparent)]
33 Compilation(#[from] CompilationError),
34
35 #[error(transparent)]
36 ParagraphError(#[from] ParagraphError),
37}
38
39#[derive(Debug, Getters, MutGetters, Setters, Serialize)]
40pub struct Document {
41
42 #[getset(get = "pub", set = "pub")]
43 name: String,
44
45 #[getset(get = "pub", get_mut = "pub", set = "pub")]
46 content: ContentBundle
47}
48
49
50impl Document {
51
52 pub fn new(name: String, content: ContentBundle) -> Self {
53
54 Self {
55 name,
56 content,
57 }
58 }
59
60 pub fn load_document_from_str(document_name: &str, content: &str, codex: &Codex, configuration: &LoadConfiguration, mut configuration_overlay: LoadConfigurationOverLay) -> Result<Document, LoadError> {
61
62 let now = Instant::now();
63
64 log::info!("loading document '{}' from its content...", document_name);
65
66 configuration_overlay.set_document_name(Some(document_name.to_string()));
67
68 let mut blocks: Vec<LoadBlock> = LoadBlock::load_from_str(content, codex, configuration, configuration_overlay.clone())?;
69
70 blocks.par_sort_by(|a, b| a.start().cmp(&b.start()));
71
72 let document = Self::create_document_by_blocks(document_name, blocks)?;
73
74 log::info!("document '{}' loaded in {} ms (preamble: {}, chapters: {})", document_name, now.elapsed().as_millis(), document.content().preamble().is_empty(), document.content().chapters().len());
75
76 Ok(document)
77 }
78
79 pub fn load_document_from_path(path_buf: &PathBuf, codex: &Codex, configuration: &LoadConfiguration, configuration_overlay: LoadConfigurationOverLay) -> Result<Document, LoadError> {
81
82 if !path_buf.exists() {
83 return Err(LoadError::ResourceError(ResourceError::InvalidResourceVerbose(format!("{} not exists", path_buf.to_string_lossy()))))
84 }
85
86 let now = Instant::now();
87
88 let resource = DiskResource::try_from(path_buf.clone())?;
89
90 log::info!("document in {:?} read in {} ms", path_buf, now.elapsed().as_millis());
91
92 let content = resource.content()?;
93
94 let document_name = resource.name();
95
96 match Self::load_document_from_str(document_name, &content, codex, configuration, configuration_overlay.clone()) {
97 Ok(document) => {
98 return Ok(document)
99 },
100 Err(err) => return Err(LoadError::ElaborationError(err.to_string()))
101 }
102 }
103
104 fn create_document_by_blocks(document_name: &str, blocks: Vec<LoadBlock>) -> Result<Document, LoadError> {
105
106 log::debug!("create document '{}' using blocks: {:#?}", document_name, blocks);
107
108 let content = ContentBundle::from(blocks);
109
110 let document = Document::new(document_name.to_string(), content);
111
112 log::debug!("document '{}' created: {:#?}", document_name, document);
113
114 Ok(document)
115 }
116}
117
118
119impl Compilable for Document {
120 fn standard_compile(&mut self, format: &OutputFormat, codex: &Codex, compilation_configuration: &CompilationConfiguration, mut compilation_configuration_overlay: CompilationConfigurationOverLay) -> Result<CompilationOutcome, CompilationError> {
121
122 compilation_configuration_overlay.set_document_name(Some(self.name().clone()));
123
124 self.content.compile(format, codex, compilation_configuration, compilation_configuration_overlay.clone())
125 }
126}
127
128
129
130#[cfg(test)]
131mod test {
132 use crate::{codex::Codex, dossier::document::Document, load::{LoadConfiguration, LoadConfigurationOverLay}};
133
134 #[test]
135 fn chapters_from_str() {
136
137 let codex = Codex::of_html();
138
139 let content: String =
140r#"
141preamble
142
143# title 1a
144
145paragraph 1a
146
147## title 2a
148
149paragraph 2a
150
151# title 1b
152
153paragraph 1b
154"#.trim().to_string();
155
156 let document = Document::load_document_from_str("test", &content, &codex, &LoadConfiguration::default(), LoadConfigurationOverLay::default()).unwrap();
157
158 assert_eq!(document.content().preamble().len(), 1);
159
160 assert_eq!(document.content().chapters().len(), 3);
161
162 }
163}