use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::sync::Arc;
use crate::models::error::Error;
use crate::models::file::File;
use crate::models::nlcst::Node;
use crate::models::position::Position;
use crate::models::smartypants::Typography;
use crate::models::tree::{Tree, TreeKind};
use crate::{Allocator, ArenaVec};
pub trait Plugin: Send + Sync {
fn attach(&self, context: &mut Attach) -> Result<(), Error>;
}
pub trait Parser: Send + Sync {
fn output(&self) -> TreeKind;
fn source<'a>(&self, input: &'a str) -> &'a str {
input
}
fn parse<'a>(
&self,
input: &'a str,
file: &mut File,
alloc: &'a Allocator,
) -> Result<Tree<'a>, Error>;
}
pub trait Transform: Send + Sync {
fn input(&self) -> TreeKind;
fn output(&self) -> TreeKind;
fn run<'a>(
&self,
tree: Tree<'a>,
file: &mut File,
alloc: &'a Allocator,
) -> Result<Tree<'a>, Error>;
fn start<'a>(
&self,
tree: Tree<'a>,
file: &mut File,
alloc: &'a Allocator,
) -> Result<Step<'a>, Error> {
self.run(tree, file, alloc).map(Step::Done)
}
fn host(&self) -> Option<u32> {
None
}
fn html_writer(&self, _target: HtmlTarget) -> Option<Box<dyn HtmlWriter>> {
None
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct HtmlTarget {
pub allow_dangerous_html: bool,
}
pub trait HtmlWriter: Send + Sync {
fn write(&self, tree: &Tree<'_>) -> Option<String>;
}
pub type Job<'a> = Box<dyn FnOnce(&mut File) -> Result<Tree<'a>, Error> + 'a>;
pub enum Step<'a> {
Done(Tree<'a>),
Job(Job<'a>),
}
pub trait Compiler: Send + Sync {
fn input(&self) -> TreeKind;
fn extension(&self) -> &'static str;
fn compile(&self, tree: &Tree<'_>, file: &mut File) -> Result<String, Error>;
fn html_target(&self) -> Option<HtmlTarget> {
None
}
}
pub trait NaturalLanguageParser: Send + Sync {
fn tokenize<'a>(
&self,
value: &'a str,
offset: usize,
alloc: &'a Allocator,
) -> ArenaVec<'a, Node<'a>>;
fn tokenize_paragraph<'a>(
&self,
content: ArenaVec<'a, Node<'a>>,
position: Option<Position<'a>>,
alloc: &'a Allocator,
) -> Node<'a>;
fn tokenize_root<'a>(
&self,
paragraph: Node<'a>,
position: Option<Position<'a>>,
alloc: &'a Allocator,
) -> Node<'a>;
}
#[derive(Clone, Default)]
pub struct NaturalLanguageParserSlot {
pub parser: Option<Arc<dyn NaturalLanguageParser>>,
}
pub trait Typographer: Send + Sync {
fn smarten(&self, text: &str, typography: &Typography) -> String;
}
#[derive(Clone, Default)]
pub struct TypographerSlot {
pub typographer: Option<Arc<dyn Typographer>>,
}
pub trait ScriptParser: Send + Sync {
fn parse_expression(&self, source: &str) -> Result<ScriptExpression, ScriptError>;
fn parse_module(&self, source: &str) -> Result<Vec<ScriptStatement>, ScriptError>;
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ScriptExpression {
pub end: usize,
pub object: Option<Vec<(String, usize)>>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ScriptStatement {
pub kind: String,
pub offset: usize,
pub imports: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ScriptError {
pub start: usize,
pub end: usize,
pub reason: String,
}
#[derive(Clone, Default)]
pub struct ScriptParserSlot {
pub parser: Option<Arc<dyn ScriptParser>>,
}
type ParserFactory = Box<dyn FnOnce(&Slots) -> Result<Box<dyn Parser>, Error>>;
type CompilerFactory = Box<dyn FnOnce(&Slots) -> Result<Box<dyn Compiler>, Error>>;
#[derive(Default)]
pub struct Attach {
parser: Option<ParserFactory>,
compiler: Option<CompilerFactory>,
transforms: Vec<Box<dyn Transform>>,
slots: Slots,
}
#[derive(Default)]
pub struct Slots {
values: HashMap<TypeId, Box<dyn Any>>,
}
impl Attach {
pub fn set_parser(
&mut self,
factory: impl FnOnce(&Slots) -> Result<Box<dyn Parser>, Error> + 'static,
) {
self.parser = Some(Box::new(factory));
}
pub fn set_compiler(
&mut self,
factory: impl FnOnce(&Slots) -> Result<Box<dyn Compiler>, Error> + 'static,
) {
self.compiler = Some(Box::new(factory));
}
pub fn add_transform(&mut self, transform: Box<dyn Transform>) {
self.transforms.push(transform);
}
pub fn slots_mut(&mut self) -> &mut Slots {
&mut self.slots
}
#[allow(clippy::type_complexity)]
pub fn finish(
self,
) -> Result<
(
Option<Box<dyn Parser>>,
Vec<Box<dyn Transform>>,
Option<Box<dyn Compiler>>,
),
Error,
> {
let parser = self
.parser
.map(|factory| factory(&self.slots))
.transpose()?;
let compiler = self
.compiler
.map(|factory| factory(&self.slots))
.transpose()?;
Ok((parser, self.transforms, compiler))
}
}
impl Slots {
pub fn get<T: 'static>(&self) -> Option<&T> {
self.values
.get(&TypeId::of::<T>())
.and_then(|value| value.downcast_ref())
}
pub fn get_or_default<T: Default + 'static>(&mut self) -> &mut T {
let value = self
.values
.entry(TypeId::of::<T>())
.or_insert_with(|| Box::new(T::default()));
match value.downcast_mut() {
Some(value) => value,
None => unreachable!("a slot holds the type it is keyed by"),
}
}
}