use std::any::TypeId; use std::fmt::{self, Debug, Formatter, Write}; use std::iter::Sum; use std::ops::{Add, AddAssign}; use comemo::{Prehashed, Tracked}; use ecow::{eco_format, EcoString, EcoVec}; use super::{ element, Behave, Behaviour, ElemFunc, Element, Fold, Guard, Introspector, Label, Locatable, Location, Recipe, Selector, Style, Styles, Synthesize, }; use crate::diag::{SourceResult, StrResult}; use crate::doc::Meta; use crate::eval::{Cast, Str, Value, Vm}; use crate::syntax::Span; use crate::util::pretty_array_like; /// Composable representation of styled content. #[allow(clippy::derived_hash_with_manual_eq)] #[derive(Clone, Hash)] pub struct Content { func: ElemFunc, attrs: EcoVec, } /// Attributes that can be attached to content. #[derive(Debug, Clone, PartialEq, Hash)] enum Attr { Span(Span), Field(EcoString), Value(Prehashed), Child(Prehashed), Styles(Styles), Prepared, Guard(Guard), Location(Location), } impl Content { /// Create an empty element. pub fn new(func: ElemFunc) -> Self { Self { func, attrs: EcoVec::new() } } /// Create empty content. pub fn empty() -> Self { Self::new(SequenceElem::func()) } /// Create a new sequence element from multiples elements. pub fn sequence(iter: impl IntoIterator) -> Self { let mut iter = iter.into_iter(); let Some(first) = iter.next() else { return Self::empty() }; let Some(second) = iter.next() else { return first }; let mut content = Content::empty(); content.attrs.push(Attr::Child(Prehashed::new(first))); content.attrs.push(Attr::Child(Prehashed::new(second))); content .attrs .extend(iter.map(|child| Attr::Child(Prehashed::new(child)))); content } /// The element function of the contained content. pub fn func(&self) -> ElemFunc { self.func } /// Whether the content is an empty sequence. pub fn is_empty(&self) -> bool { self.is::() && self.attrs.is_empty() } /// Whether the contained element is of type `T`. pub fn is(&self) -> bool { self.func == T::func() } /// Cast to `T` if the contained element is of type `T`. pub fn to(&self) -> Option<&T> { T::unpack(self) } /// Access the children if this is a sequence. pub fn to_sequence(&self) -> Option> { if !self.is::() { return None; } Some(self.attrs.iter().filter_map(Attr::child)) } /// Access the child and styles. pub fn to_styled(&self) -> Option<(&Content, &Styles)> { if !self.is::() { return None; } let child = self.attrs.iter().find_map(Attr::child)?; let styles = self.attrs.iter().find_map(Attr::styles)?; Some((child, styles)) } /// Whether the contained element has the given capability. pub fn can(&self) -> bool where C: ?Sized + 'static, { (self.func.0.vtable)(TypeId::of::()).is_some() } /// Whether the contained element has the given capability. /// Where the capability is given by a `TypeId`. pub fn can_type_id(&self, type_id: TypeId) -> bool { (self.func.0.vtable)(type_id).is_some() } /// Cast to a trait object if the contained element has the given /// capability. pub fn with(&self) -> Option<&C> where C: ?Sized + 'static, { let vtable = (self.func.0.vtable)(TypeId::of::())?; let data = self as *const Self as *const (); Some(unsafe { &*crate::util::fat::from_raw_parts(data, vtable) }) } /// Cast to a mutable trait object if the contained element has the given /// capability. pub fn with_mut(&mut self) -> Option<&mut C> where C: ?Sized + 'static, { let vtable = (self.func.0.vtable)(TypeId::of::())?; let data = self as *mut Self as *mut (); Some(unsafe { &mut *crate::util::fat::from_raw_parts_mut(data, vtable) }) } /// The content's span. pub fn span(&self) -> Span { self.attrs.iter().find_map(Attr::span).unwrap_or(Span::detached()) } /// Attach a span to the content if it doesn't already have one. pub fn spanned(mut self, span: Span) -> Self { if self.span().is_detached() { self.attrs.push(Attr::Span(span)); } self } /// 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.attrs.iter().position(|attr| match attr { Attr::Field(field) => *field == name, _ => false, }) { self.attrs.make_mut()[i + 1] = Attr::Value(Prehashed::new(value.into())); } else { self.attrs.push(Attr::Field(name)); self.attrs.push(Attr::Value(Prehashed::new(value.into()))); } } /// Access a field on the content. pub fn field(&self, name: &str) -> Option { if let (Some(iter), "children") = (self.to_sequence(), name) { Some(Value::Array(iter.cloned().map(Value::Content).collect())) } else if let (Some((child, _)), "child") = (self.to_styled(), "child") { Some(Value::Content(child.clone())) } else { self.field_ref(name).cloned() } } /// Access a field on the content by reference. /// /// Does not include synthesized fields for sequence and styled elements. pub fn field_ref(&self, name: &str) -> Option<&Value> { self.fields_ref() .find(|&(field, _)| field == name) .map(|(_, value)| value) } /// Iter over all fields on the content. /// /// Does not include synthesized fields for sequence and styled elements. pub fn fields(&self) -> impl Iterator { static CHILD: EcoString = EcoString::inline("child"); static CHILDREN: EcoString = EcoString::inline("children"); let option = if let Some(iter) = self.to_sequence() { Some((&CHILDREN, Value::Array(iter.cloned().map(Value::Content).collect()))) } else if let Some((child, _)) = self.to_styled() { Some((&CHILD, Value::Content(child.clone()))) } else { None }; self.fields_ref() .map(|(name, value)| (name, value.clone())) .chain(option) } /// Iter over all fields on the content. /// /// Does not include synthesized fields for sequence and styled elements. pub fn fields_ref(&self) -> impl Iterator { let mut iter = self.attrs.iter(); std::iter::from_fn(move || { let field = iter.find_map(Attr::field)?; let value = iter.next()?.value()?; Some((field, value)) }) } /// Try to access a field on the content as a specified type. pub fn cast_field(&self, name: &str) -> Option { match self.field(name) { Some(value) => value.cast().ok(), 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.field(name).unwrap().cast().unwrap() } /// 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 { self.field(field).ok_or_else(|| missing_field(field)) } /// The content's label. pub fn label(&self) -> Option<&Label> { match self.field_ref("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(mut self, style: impl Into