use std::any::TypeId; use std::fmt::{self, Debug, Formatter, Write}; use std::hash::{Hash, Hasher}; use std::iter::{self, Sum}; use std::ops::{Add, AddAssign, Deref}; use comemo::Tracked; use ecow::{eco_format, EcoString, EcoVec}; use once_cell::sync::Lazy; use super::{node, Guard, Recipe, Style, StyleMap}; use crate::diag::{SourceResult, StrResult}; use crate::eval::{ cast_from_value, cast_to_value, Args, Cast, Func, FuncInfo, Str, Value, Vm, }; use crate::syntax::Span; use crate::util::pretty_array_like; use crate::World; /// Composable representation of styled content. #[derive(Clone, Hash)] pub struct Content { id: NodeId, span: Span, fields: EcoVec<(EcoString, Value)>, modifiers: EcoVec, } /// Modifiers that can be attached to content. #[derive(Debug, Clone, PartialEq, Hash)] enum Modifier { Synthesized, Guard(Guard), } impl Content { pub fn new() -> Self { Self { id: T::id(), span: Span::detached(), fields: EcoVec::new(), modifiers: EcoVec::new(), } } /// Create empty content. pub fn empty() -> Self { SequenceNode::new(vec![]).pack() } /// Create a new sequence node from multiples nodes. pub fn sequence(seq: Vec) -> Self { match seq.as_slice() { [_] => seq.into_iter().next().unwrap(), _ => SequenceNode::new(seq).pack(), } } /// The id of the contained node. pub fn id(&self) -> NodeId { self.id } /// Whether the content is empty. pub fn is_empty(&self) -> bool { self.to::() .map_or(false, |seq| seq.children().is_empty()) } /// Whether the contained node is of type `T`. pub fn is(&self) -> bool where T: Node + 'static, { self.id == NodeId::of::() } /// Cast to `T` if the contained node is of type `T`. pub fn to(&self) -> Option<&T> where T: Node + 'static, { self.is::().then(|| unsafe { std::mem::transmute(self) }) } /// Whether this content has the given capability. pub fn can(&self) -> bool where C: ?Sized + 'static, { (self.id.0.vtable)(TypeId::of::()).is_some() } /// Cast to a trait object if this content has the given capability. pub fn with(&self) -> Option<&C> where C: ?Sized + 'static, { let vtable = (self.id.0.vtable)(TypeId::of::())?; let data = self as *const Self as *const (); Some(unsafe { &*crate::util::fat::from_raw_parts(data, vtable) }) } /// The node's span. pub fn span(&self) -> Span { self.span } /// Attach a span to the content. pub fn spanned(mut self, span: Span) -> Self { self.span = span; self } /// Access a field on the content. pub fn field(&self, name: &str) -> Option<&Value> { self.fields .iter() .find(|(field, _)| field == name) .map(|(_, value)| value) } /// Access a field on the content as a specified type. #[track_caller] pub fn cast_field(&self, name: &str) -> Option { match self.field(name) { Some(value) => Some(value.clone().cast().unwrap()), None => None, } } /// Expect a field on the content to exist as a specified type. #[track_caller] pub fn expect_field(&self, name: &str) -> T { self.cast_field(name).unwrap() } /// List all fields on the content. pub fn fields(&self) -> &[(EcoString, Value)] { &self.fields } /// Attach a field to the content. pub fn with_field( mut self, name: impl Into, value: impl Into, ) -> Self { self.push_field(name, value); self } /// Attach a field to the content. pub fn push_field(&mut self, name: impl Into, value: impl Into) { let name = name.into(); if let Some(i) = self.fields.iter().position(|(field, _)| *field == name) { self.fields.make_mut()[i] = (name, value.into()); } else { self.fields.push((name, value.into())); } } /// Whether the content has the specified field. pub fn has(&self, field: &str) -> bool { self.field(field).is_some() } /// Borrow the value of the given field. pub fn at(&self, field: &str) -> StrResult<&Value> { self.field(field).ok_or_else(|| missing_field(field)) } /// The content's label. pub fn label(&self) -> Option<&Label> { match self.field("label")? { Value::Label(label) => Some(label), _ => None, } } /// Attach a label to the content. pub fn labelled(self, label: Label) -> Self { self.with_field("label", label) } /// Style this content with a style entry. pub fn styled(self, style: impl Into