Skip to main content

ephemeris_documents/markdown/
spec.rs

1//! The payload of a Markdown document.
2
3use facet::Facet;
4
5use super::ast::{MarkdownBlock, MarkdownSelector};
6use crate::error::DocumentError;
7use crate::header::GENERATED;
8use crate::line_ending::LineEndingSpec;
9use crate::source::Source;
10use crate::spec::Payload;
11use crate::text::CommentSyntax;
12
13/// A Markdown document.
14#[derive(Debug, Clone, Facet)]
15#[facet(deny_unknown_fields)]
16pub struct MarkdownSpec {
17    /// Whether Ephemeris owns the whole document or selected regions.
18    pub ownership: MarkdownOwnership,
19    /// The line ending of the written document.
20    #[facet(default)]
21    pub line_ending: LineEndingSpec,
22}
23
24/// Ownership mode for a Markdown document.
25#[derive(Debug, Clone, Facet)]
26#[facet(tag = "type", deny_unknown_fields)]
27#[repr(u8)]
28pub enum MarkdownOwnership {
29    /// Manage every block in the document.
30    #[facet(rename = "whole")]
31    Whole {
32        /// All Markdown blocks in the fully managed document.
33        blocks: Vec<MarkdownBlock>,
34    },
35    /// Manage only the selected regions, preserving the rest of the source.
36    #[facet(rename = "selected")]
37    Selected {
38        /// The selected regions to manage.
39        selectors: Vec<MarkdownSelector>,
40    },
41}
42
43impl MarkdownOwnership {
44    /// Whether this ownership leaves any part of the document user-owned.
45    const fn is_partial(&self) -> bool {
46        match self {
47            Self::Whole { blocks: _ } => false,
48            Self::Selected { selectors: _ } => true,
49        }
50    }
51}
52
53impl MarkdownSpec {
54    /// Whether the document leaves any part of its source user-owned.
55    #[must_use]
56    pub const fn is_partial(&self) -> bool {
57        self.ownership.is_partial()
58    }
59}
60
61impl Payload for MarkdownSpec {
62    fn line_ending(&self) -> LineEndingSpec {
63        self.line_ending
64    }
65
66    fn reconcile(&self, source: &Source<'_>) -> Result<Vec<u8>, DocumentError> {
67        match &self.ownership {
68            MarkdownOwnership::Whole { blocks } => {
69                let header = CommentSyntax::Xml.comment(GENERATED);
70                let body = MarkdownBlock::render_all(blocks)?;
71                Ok(source.write(&format_args!("{header}\n{body}")))
72            }
73            MarkdownOwnership::Selected { selectors } => {
74                MarkdownSelector::reconcile_all(source, selectors)
75            }
76        }
77    }
78
79    fn validate(&self) -> Result<(), DocumentError> {
80        match &self.ownership {
81            MarkdownOwnership::Whole { blocks: _ } => Ok(()),
82            MarkdownOwnership::Selected { selectors } => {
83                Ok(MarkdownSelector::validate_all(selectors)?)
84            }
85        }
86    }
87}