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::html::HtmlBackend;
12use crate::latex::LatexBackend;
13use crate::markdown::MarkdownBackend;
14use crate::markup::{BackendId, MarkupDoc};
15use crate::typst_backend::TypstBackend;
16
17#[derive(Clone, Debug, PartialEq, Eq)]
19pub struct MarkupDecodeOptions {
20 pub preserve_source: bool,
22 pub preserve_raw: bool,
24}
25
26impl Default for MarkupDecodeOptions {
27 fn default() -> Self {
28 Self {
29 preserve_source: true,
30 preserve_raw: true,
31 }
32 }
33}
34
35#[derive(Clone, Debug, PartialEq, Eq)]
37pub struct MarkupEncodeOptions {
38 pub fail_on_loss: bool,
40 pub preserve_raw: bool,
42}
43
44impl Default for MarkupEncodeOptions {
45 fn default() -> Self {
46 Self {
47 fail_on_loss: true,
48 preserve_raw: true,
49 }
50 }
51}
52
53#[derive(Clone, Debug, PartialEq, Eq)]
55pub struct MarkupLoss {
56 pub path: String,
58 pub reason: String,
60}
61
62#[derive(Clone, Debug, PartialEq, Eq)]
64pub struct MarkupFidelity {
65 pub backend: BackendId,
67 pub preserved_raw: Vec<String>,
69 pub dropped: Vec<MarkupLoss>,
71 pub warnings: Vec<String>,
73}
74
75impl MarkupFidelity {
76 pub fn exact(backend: BackendId) -> Self {
78 Self {
79 backend,
80 preserved_raw: Vec::new(),
81 dropped: Vec::new(),
82 warnings: Vec::new(),
83 }
84 }
85}
86
87#[derive(Clone, Debug, PartialEq, Eq)]
89pub enum MarkupError {
90 UnknownBackend(BackendId),
92 Decode(String),
94 Encode(String),
96 InvalidDocument(String),
98}
99
100impl MarkupError {
101 pub(crate) fn into_kernel_error(self, codec: CodecId) -> sim_kernel::Error {
102 sim_kernel::Error::CodecError {
103 codec,
104 message: self.to_string(),
105 }
106 }
107}
108
109impl fmt::Display for MarkupError {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 match self {
112 Self::UnknownBackend(id) => write!(f, "unknown markup backend {id}"),
113 Self::Decode(message) => write!(f, "markup decode failed: {message}"),
114 Self::Encode(message) => write!(f, "markup encode failed: {message}"),
115 Self::InvalidDocument(message) => write!(f, "invalid markup document: {message}"),
116 }
117 }
118}
119
120impl StdError for MarkupError {}
121
122pub trait MarkupBackend: Send + Sync {
124 fn id(&self) -> BackendId;
126
127 fn decode(
129 &self,
130 input: &str,
131 opts: &MarkupDecodeOptions,
132 ) -> Result<(MarkupDoc, MarkupFidelity), MarkupError>;
133
134 fn encode(
136 &self,
137 doc: &MarkupDoc,
138 opts: &MarkupEncodeOptions,
139 ) -> Result<(String, MarkupFidelity), MarkupError>;
140}
141
142#[derive(Clone, Default)]
144pub struct BackendRegistry {
145 backends: BTreeMap<BackendId, Arc<dyn MarkupBackend>>,
146}
147
148impl BackendRegistry {
149 pub fn new() -> Self {
151 Self {
152 backends: BTreeMap::new(),
153 }
154 }
155
156 pub fn register<B: MarkupBackend + 'static>(
159 &mut self,
160 backend: B,
161 ) -> Option<Arc<dyn MarkupBackend>> {
162 self.register_arc(Arc::new(backend))
163 }
164
165 pub fn register_arc(
167 &mut self,
168 backend: Arc<dyn MarkupBackend>,
169 ) -> Option<Arc<dyn MarkupBackend>> {
170 self.backends.insert(backend.id(), backend)
171 }
172
173 pub fn backend(&self, id: &BackendId) -> Result<Arc<dyn MarkupBackend>, MarkupError> {
175 self.backends
176 .get(id)
177 .cloned()
178 .ok_or_else(|| MarkupError::UnknownBackend(id.clone()))
179 }
180
181 pub fn ids(&self) -> Vec<BackendId> {
183 self.backends.keys().cloned().collect()
184 }
185
186 pub fn iter(&self) -> impl Iterator<Item = (&BackendId, &Arc<dyn MarkupBackend>)> {
188 self.backends.iter()
189 }
190
191 pub fn is_empty(&self) -> bool {
193 self.backends.is_empty()
194 }
195}
196
197#[derive(Clone, Debug, Default)]
199pub struct BasicMarkdownBackend;
200
201impl MarkupBackend for BasicMarkdownBackend {
202 fn id(&self) -> BackendId {
203 BackendId::new("markdown")
204 }
205
206 fn decode(
207 &self,
208 input: &str,
209 opts: &MarkupDecodeOptions,
210 ) -> Result<(MarkupDoc, MarkupFidelity), MarkupError> {
211 MarkdownBackend.decode(input, opts)
212 }
213
214 fn encode(
215 &self,
216 doc: &MarkupDoc,
217 opts: &MarkupEncodeOptions,
218 ) -> Result<(String, MarkupFidelity), MarkupError> {
219 MarkdownBackend.encode(doc, opts)
220 }
221}
222
223pub fn default_backend_registry() -> BackendRegistry {
228 let mut registry = BackendRegistry::new();
229 registry.register(AsciiDocBackend);
230 registry.register(LatexBackend);
231 registry.register(HtmlBackend);
232 registry.register(MarkdownBackend);
233 registry.register(TypstBackend);
234 registry
235}