asciidoc_parser/document/
document.rs

1//! Describes the top-level document structure.
2
3use std::{marker::PhantomData, slice::Iter};
4
5use self_cell::self_cell;
6
7use crate::{
8    Parser, Span,
9    attributes::Attrlist,
10    blocks::{Block, ContentModel, IsBlock, parse_utils::parse_blocks_until},
11    document::Header,
12    parser::SourceMap,
13    strings::CowStr,
14    warnings::Warning,
15};
16
17/// A document represents the top-level block element in AsciiDoc. It consists
18/// of an optional document header and either a) one or more sections preceded
19/// by an optional preamble or b) a sequence of top-level blocks only.
20///
21/// The document can be configured using a document header. The header is not a
22/// block itself, but contributes metadata to the document, such as the document
23/// title and document attributes.
24///
25/// The `Document` structure is a self-contained package of the original content
26/// that was parsed and the data structures that describe that parsed content.
27/// The API functions on this struct can be used to understand the parse
28/// results.
29#[derive(Eq, PartialEq)]
30pub struct Document<'src> {
31    internal: Internal,
32    _phantom: PhantomData<&'src ()>,
33}
34
35/// Internal dependent struct containing the actual data members that reference
36/// the owned source.
37#[derive(Debug, Eq, PartialEq)]
38struct InternalDependent<'src> {
39    header: Header<'src>,
40    blocks: Vec<Block<'src>>,
41    source: Span<'src>,
42    warnings: Vec<Warning<'src>>,
43    source_map: SourceMap,
44}
45
46self_cell! {
47    /// Internal implementation struct containing the actual data members.
48    struct Internal {
49        owner: String,
50        #[covariant]
51        dependent: InternalDependent,
52    }
53    impl {Debug, Eq, PartialEq}
54}
55
56impl<'src> Document<'src> {
57    pub(crate) fn parse(source: &str, source_map: SourceMap, parser: &mut Parser) -> Self {
58        let owned_source = source.to_string();
59
60        let internal = Internal::new(owned_source, |owned_src| {
61            let source = Span::new(owned_src);
62
63            let mi = Header::parse(source, parser);
64            let next = mi.item.after;
65
66            let header = mi.item.item;
67            let mut warnings = mi.warnings;
68
69            let mut maw_blocks = parse_blocks_until(next, |_| false, parser);
70
71            if !maw_blocks.warnings.is_empty() {
72                warnings.append(&mut maw_blocks.warnings);
73            }
74
75            InternalDependent {
76                header,
77                blocks: maw_blocks.item.item,
78                source: source.trim_trailing_whitespace(),
79                warnings,
80                source_map,
81            }
82        });
83
84        Self {
85            internal,
86            _phantom: PhantomData,
87        }
88    }
89
90    /// Return the document header.
91    pub fn header(&self) -> &Header<'_> {
92        &self.internal.borrow_dependent().header
93    }
94
95    /// Return an iterator over any warnings found during parsing.
96    pub fn warnings(&self) -> Iter<'_, Warning<'_>> {
97        self.internal.borrow_dependent().warnings.iter()
98    }
99
100    /// Return a [`Span`] describing the entire document source.
101    pub fn span(&self) -> Span<'_> {
102        self.internal.borrow_dependent().source
103    }
104
105    /// Return the source map that tracks original file locations.
106    pub fn source_map(&self) -> &SourceMap {
107        &self.internal.borrow_dependent().source_map
108    }
109}
110
111impl<'src> IsBlock<'src> for Document<'src> {
112    fn content_model(&self) -> ContentModel {
113        ContentModel::Compound
114    }
115
116    fn raw_context(&self) -> CowStr<'src> {
117        "document".into()
118    }
119
120    fn nested_blocks(&'src self) -> Iter<'src, Block<'src>> {
121        self.internal.borrow_dependent().blocks.iter()
122    }
123
124    fn title_source(&'src self) -> Option<Span<'src>> {
125        // Document title is reflected in the Header.
126        None
127    }
128
129    fn title(&self) -> Option<&str> {
130        // Document title is reflected in the Header.
131        None
132    }
133
134    fn anchor(&'src self) -> Option<Span<'src>> {
135        None
136    }
137
138    fn attrlist(&'src self) -> Option<&'src Attrlist<'src>> {
139        // Document attributes are reflected in the Header.
140        None
141    }
142}
143
144impl std::fmt::Debug for Document<'_> {
145    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146        let dependent = self.internal.borrow_dependent();
147        f.debug_struct("Document")
148            .field("header", &dependent.header)
149            .field("blocks", &dependent.blocks)
150            .field("source", &dependent.source)
151            .field("warnings", &dependent.warnings)
152            .field("source_map", &dependent.source_map)
153            .finish()
154    }
155}