1use std::any::{Any, TypeId};
2use std::collections::HashMap;
3use std::sync::Arc;
4
5use crate::models::error::Error;
6use crate::models::file::File;
7use crate::models::nlcst::Node;
8use crate::models::position::Position;
9use crate::models::smartypants::Typography;
10use crate::models::tree::{Tree, TreeKind};
11use crate::{Allocator, ArenaVec};
12
13pub trait Plugin: Send + Sync {
15 fn attach(&self, context: &mut Attach) -> Result<(), Error>;
16}
17
18pub trait Parser: Send + Sync {
20 fn output(&self) -> TreeKind;
21
22 fn source<'a>(&self, input: &'a str) -> &'a str {
24 input
25 }
26
27 fn parse<'a>(
28 &self,
29 input: &'a str,
30 file: &mut File,
31 alloc: &'a Allocator,
32 ) -> Result<Tree<'a>, Error>;
33}
34
35pub trait Transform: Send + Sync {
37 fn input(&self) -> TreeKind;
38 fn output(&self) -> TreeKind;
39 fn run<'a>(
40 &self,
41 tree: Tree<'a>,
42 file: &mut File,
43 alloc: &'a Allocator,
44 ) -> Result<Tree<'a>, Error>;
45
46 fn start<'a>(
48 &self,
49 tree: Tree<'a>,
50 file: &mut File,
51 alloc: &'a Allocator,
52 ) -> Result<Step<'a>, Error> {
53 self.run(tree, file, alloc).map(Step::Done)
54 }
55
56 fn host(&self) -> Option<u32> {
59 None
60 }
61
62 fn html_writer(&self, _target: HtmlTarget) -> Option<Box<dyn HtmlWriter>> {
65 None
66 }
67}
68
69#[derive(Clone, Copy, Debug, PartialEq)]
71pub struct HtmlTarget {
72 pub allow_dangerous_html: bool,
74}
75
76pub trait HtmlWriter: Send + Sync {
78 fn write(&self, tree: &Tree<'_>) -> Option<String>;
79}
80
81pub type Job<'a> = Box<dyn FnOnce(&mut File) -> Result<Tree<'a>, Error> + 'a>;
83
84pub enum Step<'a> {
86 Done(Tree<'a>),
87 Job(Job<'a>),
88}
89
90pub trait Compiler: Send + Sync {
92 fn input(&self) -> TreeKind;
93 fn extension(&self) -> &'static str;
94 fn compile(&self, tree: &Tree<'_>, file: &mut File) -> Result<String, Error>;
95
96 fn html_target(&self) -> Option<HtmlTarget> {
98 None
99 }
100}
101
102pub trait NaturalLanguageParser: Send + Sync {
105 fn tokenize<'a>(
108 &self,
109 value: &'a str,
110 offset: usize,
111 alloc: &'a Allocator,
112 ) -> ArenaVec<'a, Node<'a>>;
113
114 fn tokenize_paragraph<'a>(
118 &self,
119 content: ArenaVec<'a, Node<'a>>,
120 position: Option<Position<'a>>,
121 alloc: &'a Allocator,
122 ) -> Node<'a>;
123
124 fn tokenize_root<'a>(
127 &self,
128 paragraph: Node<'a>,
129 position: Option<Position<'a>>,
130 alloc: &'a Allocator,
131 ) -> Node<'a>;
132}
133
134#[derive(Clone, Default)]
137pub struct NaturalLanguageParserSlot {
138 pub parser: Option<Arc<dyn NaturalLanguageParser>>,
139}
140
141pub trait Typographer: Send + Sync {
144 fn smarten(&self, text: &str, typography: &Typography) -> String;
146}
147
148#[derive(Clone, Default)]
151pub struct TypographerSlot {
152 pub typographer: Option<Arc<dyn Typographer>>,
153}
154
155pub trait ScriptParser: Send + Sync {
158 fn parse_expression(&self, source: &str) -> Result<ScriptExpression, ScriptError>;
161
162 fn parse_module(&self, source: &str) -> Result<Vec<ScriptStatement>, ScriptError>;
164}
165
166#[derive(Clone, Debug, PartialEq, Eq)]
170pub struct ScriptExpression {
171 pub end: usize,
172 pub object: Option<Vec<(String, usize)>>,
173}
174
175#[derive(Clone, Debug, PartialEq, Eq)]
178pub struct ScriptStatement {
179 pub kind: String,
180 pub offset: usize,
181 pub imports: Vec<String>,
182}
183
184#[derive(Clone, Debug, PartialEq, Eq)]
187pub struct ScriptError {
188 pub start: usize,
189 pub end: usize,
190 pub reason: String,
191}
192
193#[derive(Clone, Default)]
197pub struct ScriptParserSlot {
198 pub parser: Option<Arc<dyn ScriptParser>>,
199}
200
201type ParserFactory = Box<dyn FnOnce(&Slots) -> Result<Box<dyn Parser>, Error>>;
202type CompilerFactory = Box<dyn FnOnce(&Slots) -> Result<Box<dyn Compiler>, Error>>;
203
204#[derive(Default)]
206pub struct Attach {
207 parser: Option<ParserFactory>,
208 compiler: Option<CompilerFactory>,
209 transforms: Vec<Box<dyn Transform>>,
210 slots: Slots,
211}
212
213#[derive(Default)]
215pub struct Slots {
216 values: HashMap<TypeId, Box<dyn Any>>,
217}
218
219impl Attach {
220 pub fn set_parser(
222 &mut self,
223 factory: impl FnOnce(&Slots) -> Result<Box<dyn Parser>, Error> + 'static,
224 ) {
225 self.parser = Some(Box::new(factory));
226 }
227
228 pub fn set_compiler(
230 &mut self,
231 factory: impl FnOnce(&Slots) -> Result<Box<dyn Compiler>, Error> + 'static,
232 ) {
233 self.compiler = Some(Box::new(factory));
234 }
235
236 pub fn add_transform(&mut self, transform: Box<dyn Transform>) {
237 self.transforms.push(transform);
238 }
239
240 pub fn slots_mut(&mut self) -> &mut Slots {
241 &mut self.slots
242 }
243
244 #[allow(clippy::type_complexity)]
246 pub fn finish(
247 self,
248 ) -> Result<
249 (
250 Option<Box<dyn Parser>>,
251 Vec<Box<dyn Transform>>,
252 Option<Box<dyn Compiler>>,
253 ),
254 Error,
255 > {
256 let parser = self
257 .parser
258 .map(|factory| factory(&self.slots))
259 .transpose()?;
260 let compiler = self
261 .compiler
262 .map(|factory| factory(&self.slots))
263 .transpose()?;
264 Ok((parser, self.transforms, compiler))
265 }
266}
267
268impl Slots {
269 pub fn get<T: 'static>(&self) -> Option<&T> {
270 self.values
271 .get(&TypeId::of::<T>())
272 .and_then(|value| value.downcast_ref())
273 }
274
275 pub fn get_or_default<T: Default + 'static>(&mut self) -> &mut T {
276 let value = self
277 .values
278 .entry(TypeId::of::<T>())
279 .or_insert_with(|| Box::new(T::default()));
280 match value.downcast_mut() {
281 Some(value) => value,
282 None => unreachable!("a slot holds the type it is keyed by"),
283 }
284 }
285}