Skip to main content

sim_codec_doc/
asciidoc.rs

1//! AsciiDoc backend implementation over `asciidork-parser`.
2
3use std::collections::BTreeMap;
4
5use asciidork_parser::prelude::{Bump, Parser, SourceFile};
6
7use crate::MarkupFidelity;
8use crate::backend::{MarkupBackend, MarkupDecodeOptions, MarkupEncodeOptions, MarkupError};
9use crate::markup::{BackendId, MarkupDoc, SourceDoc};
10
11#[path = "asciidoc_decoder.rs"]
12mod asciidoc_decoder;
13#[path = "asciidoc_support.rs"]
14mod asciidoc_support;
15#[path = "asciidoc_writer.rs"]
16mod asciidoc_writer;
17
18use asciidoc_decoder::AsciiDocDecoder;
19use asciidoc_support::{asciidoc_id, inline_plain_text, mask_directives, raw_directives};
20use asciidoc_writer::AsciiDocEncoder;
21
22/// Safe AsciiDoc markup backend.
23#[derive(Clone, Debug, Default)]
24pub struct AsciiDocBackend;
25
26impl MarkupBackend for AsciiDocBackend {
27    fn id(&self) -> BackendId {
28        asciidoc_id()
29    }
30
31    fn decode(
32        &self,
33        input: &str,
34        opts: &MarkupDecodeOptions,
35    ) -> Result<(MarkupDoc, MarkupFidelity), MarkupError> {
36        let directives = raw_directives(input);
37        let masked = mask_directives(input, &directives);
38        let parse_input = masked.as_ref();
39        let bump = Bump::new();
40        let parser = Parser::from_str(parse_input, SourceFile::Tmp, &bump);
41        let result = parser.parse().map_err(|diagnostics| {
42            MarkupError::Decode(
43                diagnostics
44                    .into_iter()
45                    .map(|diagnostic| diagnostic.message)
46                    .collect::<Vec<_>>()
47                    .join("; "),
48            )
49        })?;
50
51        let mut decoder = AsciiDocDecoder::new(input, opts);
52        decoder.fidelity.warnings.extend(
53            result
54                .warnings
55                .into_iter()
56                .map(|diagnostic| diagnostic.message),
57        );
58        decoder.record_directives(&directives);
59        let title = result
60            .document
61            .title()
62            .map(|title| inline_plain_text(&decoder.inlines(&title.main)));
63        let parsed_blocks = decoder.blocks_from_content(&result.document.content);
64        let blocks = decoder.merge_directives(parsed_blocks);
65        let source = opts.preserve_source.then(|| SourceDoc {
66            backend: asciidoc_id(),
67            text: input.to_owned(),
68        });
69
70        Ok((
71            MarkupDoc {
72                title,
73                blocks,
74                attrs: BTreeMap::new(),
75                source,
76            },
77            decoder.fidelity,
78        ))
79    }
80
81    fn encode(
82        &self,
83        doc: &MarkupDoc,
84        opts: &MarkupEncodeOptions,
85    ) -> Result<(String, MarkupFidelity), MarkupError> {
86        let mut encoder = AsciiDocEncoder::new(opts);
87        let source = encoder.write_doc(doc);
88        if opts.fail_on_loss && !encoder.fidelity().dropped.is_empty() {
89            return Err(MarkupError::Encode(format!(
90                "asciidoc encode dropped {} unsupported fragment(s)",
91                encoder.fidelity().dropped.len()
92            )));
93        }
94        Ok((source, encoder.into_fidelity()))
95    }
96}