mod builder;
mod extensions;
mod io;
mod provenance;
mod security;
mod state;
mod verification;
#[cfg(test)]
mod tests;
pub use builder::DocumentBuilder;
pub use verification::{ExtensionValidationReport, VerificationReport};
use chrono::Utc;
use crate::content::Content;
use crate::metadata::DublinCore;
use crate::{DocumentId, DocumentState, HashAlgorithm, Manifest, Result};
#[cfg(feature = "signatures")]
use crate::security::SignatureFile;
#[cfg(feature = "encryption")]
use crate::security::EncryptionMetadata;
use crate::extensions::academic::NumberingConfig;
use crate::extensions::{Bibliography, CommentThread, FormData, JsonLdMetadata, PhantomClusters};
trait MutableResource {
fn state(&self) -> DocumentState;
fn require_mutable(&self, action: &str) -> Result<()> {
if self.state().is_immutable() {
return Err(crate::Error::ImmutableDocument {
action: action.to_string(),
state: self.state(),
});
}
Ok(())
}
}
macro_rules! define_extension_accessors {
($field:ident, $field_mut:ident, $has:ident, $set:ident, $clear:ident, $type:ty, $label:expr) => {
#[doc = concat!("Get the ", $label, ", if present.")]
#[must_use]
pub fn $field(&self) -> Option<&$type> {
self.$field.as_ref()
}
#[doc = concat!("Get a mutable reference to the ", $label, ".\n\n# Errors\n\nReturns an error if the document is in an immutable state.")]
pub fn $field_mut(&mut self) -> Result<Option<&mut $type>> {
self.require_mutable(concat!("modify ", $label))?;
self.manifest.modified = chrono::Utc::now();
Ok(self.$field.as_mut())
}
#[doc = concat!("Check if the document has ", $label, ".")]
#[must_use]
pub fn $has(&self) -> bool {
self.$field.is_some()
}
#[doc = concat!("Set the ", $label, ".\n\n# Errors\n\nReturns an error if the document is in an immutable state.")]
pub fn $set(&mut self, value: $type) -> Result<()> {
self.require_mutable(concat!("set ", $label))?;
self.$field = Some(value);
self.manifest.modified = chrono::Utc::now();
Ok(())
}
#[doc = concat!("Remove the ", $label, ".\n\n# Errors\n\nReturns an error if the document is in an immutable state.")]
pub fn $clear(&mut self) -> Result<()> {
self.require_mutable(concat!("remove ", $label))?;
self.$field = None;
self.manifest.modified = chrono::Utc::now();
Ok(())
}
};
}
pub(crate) use define_extension_accessors;
impl MutableResource for Document {
fn state(&self) -> DocumentState {
self.manifest.state
}
}
#[derive(Debug, Clone)]
pub struct Document {
manifest: Manifest,
content: Content,
dublin_core: DublinCore,
#[cfg(feature = "signatures")]
signature_file: Option<SignatureFile>,
#[cfg(feature = "encryption")]
encryption_metadata: Option<EncryptionMetadata>,
academic_numbering: Option<NumberingConfig>,
comments: Option<CommentThread>,
phantom_clusters: Option<PhantomClusters>,
form_data: Option<FormData>,
bibliography: Option<Bibliography>,
jsonld_metadata: Option<JsonLdMetadata>,
}
impl Document {
#[must_use]
pub fn builder() -> DocumentBuilder {
DocumentBuilder::new()
}
#[must_use]
pub fn manifest(&self) -> &Manifest {
&self.manifest
}
#[must_use]
pub fn content(&self) -> &Content {
&self.content
}
pub fn content_mut(&mut self) -> Result<&mut Content> {
self.require_mutable("modify content")?;
self.manifest.modified = Utc::now();
if !self.manifest.id.is_pending() {
self.manifest.id = DocumentId::pending();
}
Ok(&mut self.content)
}
#[must_use]
pub fn dublin_core(&self) -> &DublinCore {
&self.dublin_core
}
pub fn dublin_core_mut(&mut self) -> Result<&mut DublinCore> {
self.require_mutable("modify Dublin Core metadata")?;
self.manifest.modified = Utc::now();
if !self.manifest.id.is_pending() {
self.manifest.id = DocumentId::pending();
}
Ok(&mut self.dublin_core)
}
#[must_use]
pub fn title(&self) -> &str {
self.dublin_core.title()
}
#[must_use]
pub fn creators(&self) -> Vec<&str> {
self.dublin_core.creators()
}
#[must_use]
pub fn state(&self) -> DocumentState {
self.manifest.state
}
#[must_use]
pub fn id(&self) -> &DocumentId {
&self.manifest.id
}
#[must_use]
pub fn hash_algorithm(&self) -> HashAlgorithm {
self.manifest.hash_algorithm
}
#[must_use]
pub fn manifest_mut(&mut self) -> &mut Manifest {
&mut self.manifest
}
}