1use std::collections::BTreeMap;
4use std::error::Error as StdError;
5use std::fmt;
6use std::sync::Arc;
7
8use sim_kernel::CodecId;
9
10use crate::asciidoc::AsciiDocBackend;
11use crate::latex::LatexBackend;
12use crate::markdown::MarkdownBackend;
13use crate::markup::{BackendId, MarkupDoc};
14use crate::typst_backend::TypstBackend;
15
16#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct MarkupDecodeOptions {
19 pub preserve_source: bool,
21 pub preserve_raw: bool,
23}
24
25impl Default for MarkupDecodeOptions {
26 fn default() -> Self {
27 Self {
28 preserve_source: true,
29 preserve_raw: true,
30 }
31 }
32}
33
34#[derive(Clone, Debug, PartialEq, Eq)]
36pub struct MarkupEncodeOptions {
37 pub fail_on_loss: bool,
39 pub preserve_raw: bool,
41}
42
43impl Default for MarkupEncodeOptions {
44 fn default() -> Self {
45 Self {
46 fail_on_loss: true,
47 preserve_raw: true,
48 }
49 }
50}
51
52#[derive(Clone, Debug, PartialEq, Eq)]
54pub struct MarkupLoss {
55 pub path: String,
57 pub reason: String,
59}
60
61#[derive(Clone, Debug, PartialEq, Eq)]
63pub struct MarkupFidelity {
64 pub backend: BackendId,
66 pub preserved_raw: Vec<String>,
68 pub dropped: Vec<MarkupLoss>,
70 pub warnings: Vec<String>,
72}
73
74impl MarkupFidelity {
75 pub fn exact(backend: BackendId) -> Self {
77 Self {
78 backend,
79 preserved_raw: Vec::new(),
80 dropped: Vec::new(),
81 warnings: Vec::new(),
82 }
83 }
84}
85
86#[derive(Clone, Debug, PartialEq, Eq)]
88pub enum MarkupError {
89 UnknownBackend(BackendId),
91 Decode(String),
93 Encode(String),
95 InvalidDocument(String),
97}
98
99impl MarkupError {
100 pub(crate) fn into_kernel_error(self, codec: CodecId) -> sim_kernel::Error {
101 sim_kernel::Error::CodecError {
102 codec,
103 message: self.to_string(),
104 }
105 }
106}
107
108impl fmt::Display for MarkupError {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 match self {
111 Self::UnknownBackend(id) => write!(f, "unknown markup backend {id}"),
112 Self::Decode(message) => write!(f, "markup decode failed: {message}"),
113 Self::Encode(message) => write!(f, "markup encode failed: {message}"),
114 Self::InvalidDocument(message) => write!(f, "invalid markup document: {message}"),
115 }
116 }
117}
118
119impl StdError for MarkupError {}
120
121pub trait MarkupBackend: Send + Sync {
123 fn id(&self) -> BackendId;
125
126 fn decode(
128 &self,
129 input: &str,
130 opts: &MarkupDecodeOptions,
131 ) -> Result<(MarkupDoc, MarkupFidelity), MarkupError>;
132
133 fn encode(
135 &self,
136 doc: &MarkupDoc,
137 opts: &MarkupEncodeOptions,
138 ) -> Result<(String, MarkupFidelity), MarkupError>;
139}
140
141#[derive(Clone, Default)]
143pub struct BackendRegistry {
144 backends: BTreeMap<BackendId, Arc<dyn MarkupBackend>>,
145}
146
147impl BackendRegistry {
148 pub fn new() -> Self {
150 Self {
151 backends: BTreeMap::new(),
152 }
153 }
154
155 pub fn register<B: MarkupBackend + 'static>(
158 &mut self,
159 backend: B,
160 ) -> Option<Arc<dyn MarkupBackend>> {
161 self.register_arc(Arc::new(backend))
162 }
163
164 pub fn register_arc(
166 &mut self,
167 backend: Arc<dyn MarkupBackend>,
168 ) -> Option<Arc<dyn MarkupBackend>> {
169 self.backends.insert(backend.id(), backend)
170 }
171
172 pub fn backend(&self, id: &BackendId) -> Result<Arc<dyn MarkupBackend>, MarkupError> {
174 self.backends
175 .get(id)
176 .cloned()
177 .ok_or_else(|| MarkupError::UnknownBackend(id.clone()))
178 }
179
180 pub fn ids(&self) -> Vec<BackendId> {
182 self.backends.keys().cloned().collect()
183 }
184
185 pub fn iter(&self) -> impl Iterator<Item = (&BackendId, &Arc<dyn MarkupBackend>)> {
187 self.backends.iter()
188 }
189
190 pub fn is_empty(&self) -> bool {
192 self.backends.is_empty()
193 }
194}
195
196#[derive(Clone, Debug, Default)]
198pub struct BasicMarkdownBackend;
199
200impl MarkupBackend for BasicMarkdownBackend {
201 fn id(&self) -> BackendId {
202 BackendId::new("markdown")
203 }
204
205 fn decode(
206 &self,
207 input: &str,
208 opts: &MarkupDecodeOptions,
209 ) -> Result<(MarkupDoc, MarkupFidelity), MarkupError> {
210 MarkdownBackend.decode(input, opts)
211 }
212
213 fn encode(
214 &self,
215 doc: &MarkupDoc,
216 opts: &MarkupEncodeOptions,
217 ) -> Result<(String, MarkupFidelity), MarkupError> {
218 MarkdownBackend.encode(doc, opts)
219 }
220}
221
222pub fn default_backend_registry() -> BackendRegistry {
227 let mut registry = BackendRegistry::new();
228 registry.register(AsciiDocBackend);
229 registry.register(LatexBackend);
230 registry.register(MarkdownBackend);
231 registry.register(TypstBackend);
232 registry
233}