use std::any::TypeId;
use std::fmt::{self, Debug, Formatter};
use std::ops::Deref;
use std::ptr::NonNull;
use ecow::EcoString;
use super::raw::RawContent;
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{
Args, CastInfo, Construct, Content, LazyElementStore, NativeElement, NativeScope,
Packed, Repr, Scope, Set, StyleChain, Styles, Value,
};
use crate::text::{Lang, LocalName, Region};
pub(super) struct Handle<T, V: 'static>(T, &'static V);
impl<T, V> Handle<T, V> {
pub(super) unsafe fn new(content: T, vtable: &'static V) -> Self {
Self(content, vtable)
}
}
impl<T, V> Deref for Handle<T, V> {
type Target = V;
fn deref(&self) -> &Self::Target {
self.1
}
}
pub(super) type ContentHandle<T> = Handle<T, ContentVtable>;
pub(super) type FieldHandle<T> = Handle<T, FieldVtable>;
#[repr(C)]
pub struct ContentVtable<T: 'static = RawContent> {
pub(super) name: &'static str,
pub(super) title: &'static str,
pub(super) docs: &'static str,
pub(super) keywords: &'static [&'static str],
pub(super) fields: &'static [FieldVtable<T>],
pub(super) field_id: fn(name: &str) -> Option<u8>,
pub(super) construct: fn(&mut Engine, &mut Args) -> SourceResult<Content>,
pub(super) set: fn(&mut Engine, &mut Args) -> SourceResult<Styles>,
pub(super) local_name: Option<fn(Lang, Option<Region>) -> &'static str>,
pub(super) scope: fn() -> Scope,
pub(super) capability: fn(capability: TypeId) -> Option<NonNull<()>>,
pub(super) drop: unsafe fn(&mut RawContent),
pub(super) clone: unsafe fn(&T) -> RawContent,
pub(super) hash: unsafe fn(&T) -> u128,
pub(super) debug: unsafe fn(&T, &mut Formatter) -> fmt::Result,
pub(super) eq: Option<unsafe fn(&T, &T) -> bool>,
pub(super) repr: Option<unsafe fn(&T) -> EcoString>,
pub(super) store: fn() -> &'static LazyElementStore,
}
impl ContentVtable {
pub const fn new<E: NativeElement>(
name: &'static str,
title: &'static str,
docs: &'static str,
fields: &'static [FieldVtable<Packed<E>>],
field_id: fn(name: &str) -> Option<u8>,
capability: fn(TypeId) -> Option<NonNull<()>>,
store: fn() -> &'static LazyElementStore,
) -> ContentVtable<Packed<E>> {
ContentVtable {
name,
title,
docs,
keywords: &[],
fields,
field_id,
construct: <E as Construct>::construct,
set: <E as Set>::set,
local_name: None,
scope: || Scope::new(),
capability,
drop: RawContent::drop_impl::<E>,
clone: RawContent::clone_impl::<E>,
hash: |elem| typst_utils::hash128(elem.as_ref()),
debug: |elem, f| Debug::fmt(elem.as_ref(), f),
eq: None,
repr: None,
store,
}
}
pub fn field(&self, id: u8) -> Option<&'static FieldVtable> {
self.fields.get(usize::from(id))
}
}
impl<E: NativeElement> ContentVtable<Packed<E>> {
pub const fn with_keywords(mut self, keywords: &'static [&'static str]) -> Self {
self.keywords = keywords;
self
}
pub const fn with_repr(mut self) -> Self
where
E: Repr,
{
self.repr = Some(|e| E::repr(&**e));
self
}
pub const fn with_partial_eq(mut self) -> Self
where
E: PartialEq,
{
self.eq = Some(|a, b| E::eq(&**a, &**b));
self
}
pub const fn with_local_name(mut self) -> Self
where
Packed<E>: LocalName,
{
self.local_name = Some(<Packed<E> as LocalName>::local_name);
self
}
pub const fn with_scope(mut self) -> Self
where
E: NativeScope,
{
self.scope = || E::scope();
self
}
pub const fn erase(self) -> ContentVtable {
unsafe {
std::mem::transmute::<ContentVtable<Packed<E>>, ContentVtable<RawContent>>(
self,
)
}
}
}
impl<T> ContentHandle<T> {
pub(super) fn field(self, id: u8) -> Option<FieldHandle<T>> {
self.fields.get(usize::from(id)).map(|vtable| {
unsafe { Handle::new(self.0, vtable) }
})
}
pub(super) fn fields(self) -> impl Iterator<Item = FieldHandle<T>>
where
T: Copy,
{
self.fields.iter().map(move |vtable| {
unsafe { Handle::new(self.0, vtable) }
})
}
}
impl ContentHandle<&RawContent> {
pub fn debug(&self, f: &mut Formatter) -> fmt::Result {
unsafe { (self.1.debug)(self.0, f) }
}
pub fn repr(&self) -> Option<EcoString> {
unsafe { self.1.repr.map(|f| f(self.0)) }
}
pub fn clone(&self) -> RawContent {
unsafe { (self.1.clone)(self.0) }
}
pub fn hash(&self) -> u128 {
unsafe { (self.1.hash)(self.0) }
}
}
impl ContentHandle<&mut RawContent> {
pub unsafe fn drop(&mut self) {
unsafe { (self.1.drop)(self.0) }
}
}
impl ContentHandle<(&RawContent, &RawContent)> {
pub fn eq(&self) -> Option<bool> {
let (a, b) = self.0;
unsafe { self.1.eq.map(|f| f(a, b)) }
}
}
#[repr(C)]
pub struct FieldVtable<T: 'static = RawContent> {
pub(super) name: &'static str,
pub(super) docs: &'static str,
pub(super) positional: bool,
pub(super) variadic: bool,
pub(super) required: bool,
pub(super) settable: bool,
pub(super) synthesized: bool,
pub(super) input: fn() -> CastInfo,
pub(super) default: Option<fn() -> Value>,
pub(super) has: unsafe fn(elem: &T) -> bool,
pub(super) get: unsafe fn(elem: &T) -> Option<Value>,
pub(super) get_with_styles: unsafe fn(elem: &T, StyleChain) -> Option<Value>,
pub(super) get_from_styles: fn(StyleChain) -> Option<Value>,
pub(super) materialize: unsafe fn(elem: &mut T, styles: StyleChain),
pub(super) eq: unsafe fn(a: &T, b: &T) -> bool,
}
impl FieldHandle<&RawContent> {
pub fn has(&self) -> bool {
unsafe { (self.1.has)(self.0) }
}
pub fn get(&self) -> Option<Value> {
unsafe { (self.1.get)(self.0) }
}
pub fn get_with_styles(&self, styles: StyleChain) -> Option<Value> {
unsafe { (self.1.get_with_styles)(self.0, styles) }
}
}
impl FieldHandle<&mut RawContent> {
pub fn materialize(&mut self, styles: StyleChain) {
unsafe { (self.1.materialize)(self.0, styles) }
}
}
impl FieldHandle<(&RawContent, &RawContent)> {
pub fn eq(&self) -> bool {
let (a, b) = self.0;
unsafe { (self.1.eq)(a, b) }
}
}