1mod builder;
31mod extensions;
32mod io;
33mod provenance;
34mod security;
35mod state;
36mod verification;
37
38#[cfg(test)]
39mod tests;
40
41pub use builder::DocumentBuilder;
42pub use verification::{ExtensionValidationReport, VerificationReport};
43
44use chrono::Utc;
45
46use crate::content::Content;
47use crate::metadata::DublinCore;
48use crate::{DocumentId, DocumentState, HashAlgorithm, Manifest, Result};
49
50#[cfg(feature = "signatures")]
51use crate::security::SignatureFile;
52
53#[cfg(feature = "encryption")]
54use crate::security::EncryptionMetadata;
55
56use crate::extensions::academic::NumberingConfig;
57use crate::extensions::{Bibliography, CommentThread, FormData, JsonLdMetadata, PhantomClusters};
58
59trait MutableResource {
64 fn state(&self) -> DocumentState;
66
67 fn require_mutable(&self, action: &str) -> Result<()> {
73 if self.state().is_immutable() {
74 return Err(crate::Error::ImmutableDocument {
75 action: action.to_string(),
76 state: self.state(),
77 });
78 }
79 Ok(())
80 }
81}
82
83macro_rules! define_extension_accessors {
90 ($field:ident, $field_mut:ident, $has:ident, $set:ident, $clear:ident, $type:ty, $label:expr) => {
91 #[doc = concat!("Get the ", $label, ", if present.")]
92 #[must_use]
93 pub fn $field(&self) -> Option<&$type> {
94 self.$field.as_ref()
95 }
96
97 #[doc = concat!("Get a mutable reference to the ", $label, ".\n\n# Errors\n\nReturns an error if the document is in an immutable state.")]
98 pub fn $field_mut(&mut self) -> Result<Option<&mut $type>> {
99 self.require_mutable(concat!("modify ", $label))?;
100 self.manifest.modified = chrono::Utc::now();
101 Ok(self.$field.as_mut())
102 }
103
104 #[doc = concat!("Check if the document has ", $label, ".")]
105 #[must_use]
106 pub fn $has(&self) -> bool {
107 self.$field.is_some()
108 }
109
110 #[doc = concat!("Set the ", $label, ".\n\n# Errors\n\nReturns an error if the document is in an immutable state.")]
111 pub fn $set(&mut self, value: $type) -> Result<()> {
112 self.require_mutable(concat!("set ", $label))?;
113 self.$field = Some(value);
114 self.manifest.modified = chrono::Utc::now();
115 Ok(())
116 }
117
118 #[doc = concat!("Remove the ", $label, ".\n\n# Errors\n\nReturns an error if the document is in an immutable state.")]
119 pub fn $clear(&mut self) -> Result<()> {
120 self.require_mutable(concat!("remove ", $label))?;
121 self.$field = None;
122 self.manifest.modified = chrono::Utc::now();
123 Ok(())
124 }
125 };
126}
127
128pub(crate) use define_extension_accessors;
130
131impl MutableResource for Document {
132 fn state(&self) -> DocumentState {
133 self.manifest.state
134 }
135}
136
137#[derive(Debug, Clone)]
142pub struct Document {
143 manifest: Manifest,
144 content: Content,
145 dublin_core: DublinCore,
146 #[cfg(feature = "signatures")]
147 signature_file: Option<SignatureFile>,
148 #[cfg(feature = "encryption")]
149 encryption_metadata: Option<EncryptionMetadata>,
150 academic_numbering: Option<NumberingConfig>,
152 comments: Option<CommentThread>,
154 phantom_clusters: Option<PhantomClusters>,
156 form_data: Option<FormData>,
158 bibliography: Option<Bibliography>,
160 jsonld_metadata: Option<JsonLdMetadata>,
162}
163
164impl Document {
165 #[must_use]
167 pub fn builder() -> DocumentBuilder {
168 DocumentBuilder::new()
169 }
170
171 #[must_use]
173 pub fn manifest(&self) -> &Manifest {
174 &self.manifest
175 }
176
177 #[must_use]
179 pub fn content(&self) -> &Content {
180 &self.content
181 }
182
183 pub fn content_mut(&mut self) -> Result<&mut Content> {
189 self.require_mutable("modify content")?;
190 self.manifest.modified = Utc::now();
191 if !self.manifest.id.is_pending() {
193 self.manifest.id = DocumentId::pending();
194 }
195 Ok(&mut self.content)
196 }
197
198 #[must_use]
200 pub fn dublin_core(&self) -> &DublinCore {
201 &self.dublin_core
202 }
203
204 pub fn dublin_core_mut(&mut self) -> Result<&mut DublinCore> {
210 self.require_mutable("modify Dublin Core metadata")?;
211 self.manifest.modified = Utc::now();
212 if !self.manifest.id.is_pending() {
214 self.manifest.id = DocumentId::pending();
215 }
216 Ok(&mut self.dublin_core)
217 }
218
219 #[must_use]
221 pub fn title(&self) -> &str {
222 self.dublin_core.title()
223 }
224
225 #[must_use]
227 pub fn creators(&self) -> Vec<&str> {
228 self.dublin_core.creators()
229 }
230
231 #[must_use]
233 pub fn state(&self) -> DocumentState {
234 self.manifest.state
235 }
236
237 #[must_use]
239 pub fn id(&self) -> &DocumentId {
240 &self.manifest.id
241 }
242
243 #[must_use]
245 pub fn hash_algorithm(&self) -> HashAlgorithm {
246 self.manifest.hash_algorithm
247 }
248
249 #[must_use]
253 pub fn manifest_mut(&mut self) -> &mut Manifest {
254 &mut self.manifest
255 }
256}