use std::any::TypeId; use std::fmt::{self, Debug, Formatter}; use std::hash::{Hash, Hasher}; use ecow::EcoString; use once_cell::sync::Lazy; use super::{Content, Selector, Styles}; use crate::diag::SourceResult; use crate::eval::{ cast_from_value, cast_to_value, Args, Dict, Func, FuncInfo, Value, Vm, }; /// A document element. pub trait Element: Construct + Set + Sized + 'static { /// Pack the element into type-erased content. fn pack(self) -> Content; /// Extract this element from type-erased content. fn unpack(content: &Content) -> Option<&Self>; /// The element's function. fn func() -> ElemFunc; } /// An element's constructor function. pub trait Construct { /// Construct an element from the arguments. /// /// This is passed only the arguments that remain after execution of the /// element's set rule. fn construct(vm: &mut Vm, args: &mut Args) -> SourceResult; } /// An element's set rule. pub trait Set { /// Parse relevant arguments into style properties for this element. fn set(args: &mut Args) -> SourceResult; } /// An element's function. #[derive(Copy, Clone)] pub struct ElemFunc(pub(super) &'static NativeElemFunc); impl ElemFunc { /// The function's name. pub fn name(self) -> &'static str { self.0.name } /// Apply the given arguments to the function. pub fn with(self, args: Args) -> Func { Func::from(self).with(args) } /// Extract details about the function. pub fn info(&self) -> &'static FuncInfo { &self.0.info } /// Construct an element. pub fn construct(self, vm: &mut Vm, args: &mut Args) -> SourceResult { (self.0.construct)(vm, args) } /// Create a selector for elements of this function. pub fn select(self) -> Selector { Selector::Elem(self, None) } /// Create a selector for elements of this function, filtering for those /// whose [fields](super::Content::field) match the given arguments. pub fn where_(self, fields: Dict) -> Selector { Selector::Elem(self, Some(fields)) } /// Execute the set rule for the element and return the resulting style map. pub fn set(self, mut args: Args) -> SourceResult { let styles = (self.0.set)(&mut args)?; args.finish()?; Ok(styles) } } impl Debug for ElemFunc { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.pad(self.name()) } } impl Eq for ElemFunc {} impl PartialEq for ElemFunc { fn eq(&self, other: &Self) -> bool { std::ptr::eq(self.0, other.0) } } impl Hash for ElemFunc { fn hash(&self, state: &mut H) { state.write_usize(self.0 as *const _ as usize); } } cast_from_value! { ElemFunc, v: Func => v.element().ok_or("expected element function")?, } cast_to_value! { v: ElemFunc => Value::Func(v.into()) } impl From<&'static NativeElemFunc> for ElemFunc { fn from(native: &'static NativeElemFunc) -> Self { Self(native) } } /// An element function backed by a Rust type. pub struct NativeElemFunc { /// The element's name. pub name: &'static str, /// The element's vtable for capability dispatch. pub vtable: fn(of: TypeId) -> Option<*const ()>, /// The element's constructor. pub construct: fn(&mut Vm, &mut Args) -> SourceResult, /// The element's set rule. pub set: fn(&mut Args) -> SourceResult, /// Details about the function. pub info: Lazy, } /// A label for an element. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct Label(pub EcoString); impl Debug for Label { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "<{}>", self.0) } } /// Indicates that an element cannot be labelled. pub trait Unlabellable {}