summaryrefslogtreecommitdiff
path: root/src/library/structure
diff options
context:
space:
mode:
Diffstat (limited to 'src/library/structure')
-rw-r--r--src/library/structure/doc.rs29
-rw-r--r--src/library/structure/heading.rs176
-rw-r--r--src/library/structure/list.rs371
-rw-r--r--src/library/structure/mod.rs13
-rw-r--r--src/library/structure/reference.rs29
-rw-r--r--src/library/structure/table.rs160
6 files changed, 0 insertions, 778 deletions
diff --git a/src/library/structure/doc.rs b/src/library/structure/doc.rs
deleted file mode 100644
index c3af3f1c..00000000
--- a/src/library/structure/doc.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-use crate::library::layout::PageNode;
-use crate::library::prelude::*;
-
-/// A sequence of page runs.
-#[derive(Hash)]
-pub struct DocNode(pub StyleVec<PageNode>);
-
-impl DocNode {
- /// Layout the document into a sequence of frames, one per page.
- pub fn layout(
- &self,
- world: Tracked<dyn World>,
- styles: StyleChain,
- ) -> SourceResult<Vec<Frame>> {
- let mut frames = vec![];
- for (page, map) in self.0.iter() {
- let number = 1 + frames.len();
- frames.extend(page.layout(world, number, map.chain(&styles))?);
- }
- Ok(frames)
- }
-}
-
-impl Debug for DocNode {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.write_str("Doc ")?;
- self.0.fmt(f)
- }
-}
diff --git a/src/library/structure/heading.rs b/src/library/structure/heading.rs
deleted file mode 100644
index 5b056c30..00000000
--- a/src/library/structure/heading.rs
+++ /dev/null
@@ -1,176 +0,0 @@
-use crate::library::layout::{BlockNode, BlockSpacing};
-use crate::library::prelude::*;
-use crate::library::text::{FontFamily, TextNode, TextSize};
-
-/// A section heading.
-#[derive(Debug, Hash)]
-pub struct HeadingNode {
- /// The logical nesting depth of the section, starting from one. In the
- /// default style, this controls the text size of the heading.
- pub level: NonZeroUsize,
- /// The heading's contents.
- pub body: Content,
-}
-
-#[node(Show)]
-impl HeadingNode {
- /// The heading's font family. Just the normal text family if `auto`.
- #[property(referenced)]
- pub const FAMILY: Leveled<Smart<FontFamily>> = Leveled::Value(Smart::Auto);
- /// The color of text in the heading. Just the normal text color if `auto`.
- #[property(referenced)]
- pub const FILL: Leveled<Smart<Paint>> = Leveled::Value(Smart::Auto);
- /// The size of text in the heading.
- #[property(referenced)]
- pub const SIZE: Leveled<TextSize> = Leveled::Mapping(|level| {
- let size = match level.get() {
- 1 => 1.4,
- 2 => 1.2,
- _ => 1.0,
- };
- TextSize(Em::new(size).into())
- });
-
- /// Whether text in the heading is strengthend.
- #[property(referenced)]
- pub const STRONG: Leveled<bool> = Leveled::Value(true);
- /// Whether text in the heading is emphasized.
- #[property(referenced)]
- pub const EMPH: Leveled<bool> = Leveled::Value(false);
- /// Whether the heading is underlined.
- #[property(referenced)]
- pub const UNDERLINE: Leveled<bool> = Leveled::Value(false);
-
- /// The spacing above the heading.
- #[property(referenced, shorthand(around))]
- pub const ABOVE: Leveled<Option<BlockSpacing>> = Leveled::Mapping(|level| {
- let ratio = match level.get() {
- 1 => 1.5,
- _ => 1.2,
- };
- Some(Ratio::new(ratio).into())
- });
- /// The spacing below the heading.
- #[property(referenced, shorthand(around))]
- pub const BELOW: Leveled<Option<BlockSpacing>> =
- Leveled::Value(Some(Ratio::new(0.55).into()));
-
- /// Whether the heading appears in the outline.
- pub const OUTLINED: bool = true;
- /// Whether the heading is numbered.
- pub const NUMBERED: bool = true;
-
- fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self {
- body: args.expect("body")?,
- level: args.named("level")?.unwrap_or(NonZeroUsize::new(1).unwrap()),
- }
- .pack())
- }
-}
-
-impl Show for HeadingNode {
- fn unguard_parts(&self, sel: Selector) -> Content {
- Self { body: self.body.unguard(sel), ..*self }.pack()
- }
-
- fn field(&self, name: &str) -> Option<Value> {
- match name {
- "level" => Some(Value::Int(self.level.get() as i64)),
- "body" => Some(Value::Content(self.body.clone())),
- _ => None,
- }
- }
-
- fn realize(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
- Ok(BlockNode(self.body.clone()).pack())
- }
-
- fn finalize(
- &self,
- world: Tracked<dyn World>,
- styles: StyleChain,
- mut realized: Content,
- ) -> SourceResult<Content> {
- macro_rules! resolve {
- ($key:expr) => {
- styles.get($key).resolve(world, self.level)?
- };
- }
-
- let mut map = StyleMap::new();
- map.set(TextNode::SIZE, resolve!(Self::SIZE));
-
- if let Smart::Custom(family) = resolve!(Self::FAMILY) {
- map.set_family(family, styles);
- }
-
- if let Smart::Custom(fill) = resolve!(Self::FILL) {
- map.set(TextNode::FILL, fill);
- }
-
- if resolve!(Self::STRONG) {
- realized = realized.strong();
- }
-
- if resolve!(Self::EMPH) {
- realized = realized.emph();
- }
-
- if resolve!(Self::UNDERLINE) {
- realized = realized.underlined();
- }
-
- realized = realized.styled_with_map(map);
- realized = realized.spaced(
- resolve!(Self::ABOVE).resolve(styles),
- resolve!(Self::BELOW).resolve(styles),
- );
-
- Ok(realized)
- }
-}
-
-/// Either the value or a closure mapping to the value.
-#[derive(Debug, Clone, PartialEq, Hash)]
-pub enum Leveled<T> {
- /// A bare value.
- Value(T),
- /// A simple mapping from a heading level to a value.
- Mapping(fn(NonZeroUsize) -> T),
- /// A closure mapping from a heading level to a value.
- Func(Func, Span),
-}
-
-impl<T: Cast + Clone> Leveled<T> {
- /// Resolve the value based on the level.
- pub fn resolve(
- &self,
- world: Tracked<dyn World>,
- level: NonZeroUsize,
- ) -> SourceResult<T> {
- Ok(match self {
- Self::Value(value) => value.clone(),
- Self::Mapping(mapping) => mapping(level),
- Self::Func(func, span) => {
- let args = Args::new(*span, [Value::Int(level.get() as i64)]);
- func.call_detached(world, args)?.cast().at(*span)?
- }
- })
- }
-}
-
-impl<T: Cast> Cast<Spanned<Value>> for Leveled<T> {
- fn is(value: &Spanned<Value>) -> bool {
- matches!(&value.v, Value::Func(_)) || T::is(&value.v)
- }
-
- fn cast(value: Spanned<Value>) -> StrResult<Self> {
- match value.v {
- Value::Func(v) => Ok(Self::Func(v, value.span)),
- v => T::cast(v)
- .map(Self::Value)
- .map_err(|msg| with_alternative(msg, "function")),
- }
- }
-}
diff --git a/src/library/structure/list.rs b/src/library/structure/list.rs
deleted file mode 100644
index f061c5f8..00000000
--- a/src/library/structure/list.rs
+++ /dev/null
@@ -1,371 +0,0 @@
-use unscanny::Scanner;
-
-use crate::library::layout::{BlockSpacing, GridNode, HNode, TrackSizing};
-use crate::library::prelude::*;
-use crate::library::text::{ParNode, SpaceNode};
-use crate::library::utility::Numbering;
-
-/// An unordered (bulleted) or ordered (numbered) list.
-#[derive(Debug, Hash)]
-pub struct ListNode<const L: ListKind = LIST> {
- /// If true, the items are separated by leading instead of list spacing.
- pub tight: bool,
- /// If true, the spacing above the list is leading instead of above spacing.
- pub attached: bool,
- /// The individual bulleted or numbered items.
- pub items: StyleVec<ListItem>,
-}
-
-/// An ordered list.
-pub type EnumNode = ListNode<ENUM>;
-
-/// A description list.
-pub type DescNode = ListNode<DESC>;
-
-#[node(Show)]
-impl<const L: ListKind> ListNode<L> {
- /// How the list is labelled.
- #[property(referenced)]
- pub const LABEL: Label = Label::Default;
- /// The indentation of each item's label.
- #[property(resolve)]
- pub const INDENT: Length = Length::zero();
- /// The space between the label and the body of each item.
- #[property(resolve)]
- pub const BODY_INDENT: Length = Em::new(match L {
- LIST | ENUM => 0.5,
- DESC | _ => 1.0,
- })
- .into();
-
- /// The spacing above the list.
- #[property(resolve, shorthand(around))]
- pub const ABOVE: Option<BlockSpacing> = Some(Ratio::one().into());
- /// The spacing below the list.
- #[property(resolve, shorthand(around))]
- pub const BELOW: Option<BlockSpacing> = Some(Ratio::one().into());
- /// The spacing between the items of a wide (non-tight) list.
- #[property(resolve)]
- pub const SPACING: BlockSpacing = Ratio::one().into();
-
- fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
- let items = match L {
- LIST => args
- .all()?
- .into_iter()
- .map(|body| ListItem::List(Box::new(body)))
- .collect(),
- ENUM => {
- let mut number: usize = args.named("start")?.unwrap_or(1);
- args.all()?
- .into_iter()
- .map(|body| {
- let item = ListItem::Enum(Some(number), Box::new(body));
- number += 1;
- item
- })
- .collect()
- }
- DESC | _ => args
- .all()?
- .into_iter()
- .map(|item| ListItem::Desc(Box::new(item)))
- .collect(),
- };
-
- Ok(Self {
- tight: args.named("tight")?.unwrap_or(true),
- attached: args.named("attached")?.unwrap_or(false),
- items,
- }
- .pack())
- }
-}
-
-impl<const L: ListKind> Show for ListNode<L> {
- fn unguard_parts(&self, sel: Selector) -> Content {
- Self {
- items: self.items.map(|item| item.unguard(sel)),
- ..*self
- }
- .pack()
- }
-
- fn field(&self, name: &str) -> Option<Value> {
- match name {
- "tight" => Some(Value::Bool(self.tight)),
- "attached" => Some(Value::Bool(self.attached)),
- "items" => Some(Value::Array(
- self.items.items().map(|item| item.encode()).collect(),
- )),
- _ => None,
- }
- }
-
- fn realize(
- &self,
- world: Tracked<dyn World>,
- styles: StyleChain,
- ) -> SourceResult<Content> {
- let mut cells = vec![];
- let mut number = 1;
-
- let label = styles.get(Self::LABEL);
- let indent = styles.get(Self::INDENT);
- let body_indent = styles.get(Self::BODY_INDENT);
- let gutter = if self.tight {
- styles.get(ParNode::LEADING)
- } else {
- styles.get(Self::SPACING)
- };
-
- for (item, map) in self.items.iter() {
- if let &ListItem::Enum(Some(n), _) = item {
- number = n;
- }
-
- cells.push(Content::empty());
-
- let label = if L == LIST || L == ENUM {
- label.resolve(world, L, number)?.styled_with_map(map.clone())
- } else {
- Content::empty()
- };
-
- cells.push(label);
- cells.push(Content::empty());
-
- let body = match &item {
- ListItem::List(body) => body.as_ref().clone(),
- ListItem::Enum(_, body) => body.as_ref().clone(),
- ListItem::Desc(item) => Content::sequence(vec![
- HNode {
- amount: (-body_indent).into(),
- weak: false,
- }
- .pack(),
- (item.term.clone() + TextNode(':'.into()).pack()).strong(),
- SpaceNode.pack(),
- item.body.clone(),
- ]),
- };
-
- cells.push(body.styled_with_map(map.clone()));
- number += 1;
- }
-
- Ok(GridNode {
- tracks: Axes::with_x(vec![
- TrackSizing::Relative(indent.into()),
- TrackSizing::Auto,
- TrackSizing::Relative(body_indent.into()),
- TrackSizing::Auto,
- ]),
- gutter: Axes::with_y(vec![TrackSizing::Relative(gutter.into())]),
- cells,
- }
- .pack())
- }
-
- fn finalize(
- &self,
- _: Tracked<dyn World>,
- styles: StyleChain,
- realized: Content,
- ) -> SourceResult<Content> {
- let mut above = styles.get(Self::ABOVE);
- let mut below = styles.get(Self::BELOW);
-
- if self.attached {
- if above.is_some() {
- above = Some(styles.get(ParNode::LEADING));
- }
- if below.is_some() {
- below = Some(styles.get(ParNode::SPACING));
- }
- }
-
- Ok(realized.spaced(above, below))
- }
-}
-
-/// An item in a list.
-#[derive(Clone, PartialEq, Hash)]
-pub enum ListItem {
- /// An item of an unordered list.
- List(Box<Content>),
- /// An item of an ordered list.
- Enum(Option<usize>, Box<Content>),
- /// An item of a description list.
- Desc(Box<DescItem>),
-}
-
-impl ListItem {
- /// What kind of item this is.
- pub fn kind(&self) -> ListKind {
- match self {
- Self::List(_) => LIST,
- Self::Enum { .. } => ENUM,
- Self::Desc { .. } => DESC,
- }
- }
-
- fn unguard(&self, sel: Selector) -> Self {
- match self {
- Self::List(body) => Self::List(Box::new(body.unguard(sel))),
- Self::Enum(number, body) => Self::Enum(*number, Box::new(body.unguard(sel))),
- Self::Desc(item) => Self::Desc(Box::new(DescItem {
- term: item.term.unguard(sel),
- body: item.body.unguard(sel),
- })),
- }
- }
-
- /// Encode the item into a value.
- fn encode(&self) -> Value {
- match self {
- Self::List(body) => Value::Content(body.as_ref().clone()),
- Self::Enum(number, body) => Value::Dict(dict! {
- "number" => match *number {
- Some(n) => Value::Int(n as i64),
- None => Value::None,
- },
- "body" => Value::Content(body.as_ref().clone()),
- }),
- Self::Desc(item) => Value::Dict(dict! {
- "term" => Value::Content(item.term.clone()),
- "body" => Value::Content(item.body.clone()),
- }),
- }
- }
-}
-
-impl Debug for ListItem {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- match self {
- Self::List(body) => write!(f, "- {body:?}"),
- Self::Enum(number, body) => match number {
- Some(n) => write!(f, "{n}. {body:?}"),
- None => write!(f, "+ {body:?}"),
- },
- Self::Desc(item) => item.fmt(f),
- }
- }
-}
-
-#[node]
-impl ListItem {}
-
-/// A description list item.
-#[derive(Clone, PartialEq, Hash)]
-pub struct DescItem {
- /// The term described by the list item.
- pub term: Content,
- /// The description of the term.
- pub body: Content,
-}
-
-castable! {
- DescItem,
- Expected: "dictionary with `term` and `body` keys",
- Value::Dict(dict) => {
- let term: Content = dict.get("term")?.clone().cast()?;
- let body: Content = dict.get("body")?.clone().cast()?;
- Self { term, body }
- },
-}
-
-impl Debug for DescItem {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "/ {:?}: {:?}", self.term, self.body)
- }
-}
-
-/// How to label a list.
-pub type ListKind = usize;
-
-/// An unordered list.
-pub const LIST: ListKind = 0;
-
-/// An ordered list.
-pub const ENUM: ListKind = 1;
-
-/// A description list.
-pub const DESC: ListKind = 2;
-
-/// How to label a list or enumeration.
-#[derive(Debug, Clone, PartialEq, Hash)]
-pub enum Label {
- /// The default labelling.
- Default,
- /// A pattern with prefix, numbering, lower / upper case and suffix.
- Pattern(EcoString, Numbering, bool, EcoString),
- /// Bare content.
- Content(Content),
- /// A closure mapping from an item number to a value.
- Func(Func, Span),
-}
-
-impl Label {
- /// Resolve the value based on the level.
- pub fn resolve(
- &self,
- world: Tracked<dyn World>,
- kind: ListKind,
- number: usize,
- ) -> SourceResult<Content> {
- Ok(match self {
- Self::Default => match kind {
- LIST => TextNode('•'.into()).pack(),
- ENUM => TextNode(format_eco!("{}.", number)).pack(),
- DESC | _ => panic!("description lists don't have a label"),
- },
- Self::Pattern(prefix, numbering, upper, suffix) => {
- let fmt = numbering.apply(number);
- let mid = if *upper { fmt.to_uppercase() } else { fmt.to_lowercase() };
- TextNode(format_eco!("{}{}{}", prefix, mid, suffix)).pack()
- }
- Self::Content(content) => content.clone(),
- Self::Func(func, span) => {
- let args = Args::new(*span, [Value::Int(number as i64)]);
- func.call_detached(world, args)?.display(world)
- }
- })
- }
-}
-
-impl Cast<Spanned<Value>> for Label {
- fn is(value: &Spanned<Value>) -> bool {
- matches!(&value.v, Value::Content(_) | Value::Func(_))
- }
-
- fn cast(value: Spanned<Value>) -> StrResult<Self> {
- match value.v {
- Value::None => Ok(Self::Content(Content::empty())),
- Value::Str(pattern) => {
- let mut s = Scanner::new(&pattern);
- let mut prefix;
- let numbering = loop {
- prefix = s.before();
- match s.eat().map(|c| c.to_ascii_lowercase()) {
- Some('1') => break Numbering::Arabic,
- Some('a') => break Numbering::Letter,
- Some('i') => break Numbering::Roman,
- Some('*') => break Numbering::Symbol,
- Some(_) => {}
- None => Err("invalid pattern")?,
- }
- };
- let upper = s.scout(-1).map_or(false, char::is_uppercase);
- let suffix = s.after().into();
- Ok(Self::Pattern(prefix.into(), numbering, upper, suffix))
- }
- Value::Content(v) => Ok(Self::Content(v)),
- Value::Func(v) => Ok(Self::Func(v, value.span)),
- v => Err(format!(
- "expected string, content or function, found {}",
- v.type_name(),
- )),
- }
- }
-}
diff --git a/src/library/structure/mod.rs b/src/library/structure/mod.rs
deleted file mode 100644
index 088d1e6c..00000000
--- a/src/library/structure/mod.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-//! Document structuring.
-
-mod doc;
-mod heading;
-mod list;
-mod reference;
-mod table;
-
-pub use doc::*;
-pub use heading::*;
-pub use list::*;
-pub use reference::*;
-pub use table::*;
diff --git a/src/library/structure/reference.rs b/src/library/structure/reference.rs
deleted file mode 100644
index b4e8b047..00000000
--- a/src/library/structure/reference.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-use crate::library::prelude::*;
-
-/// A reference to a label.
-#[derive(Debug, Hash)]
-pub struct RefNode(pub EcoString);
-
-#[node(Show)]
-impl RefNode {
- fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.expect("label")?).pack())
- }
-}
-
-impl Show for RefNode {
- fn unguard_parts(&self, _: Selector) -> Content {
- Self(self.0.clone()).pack()
- }
-
- fn field(&self, name: &str) -> Option<Value> {
- match name {
- "label" => Some(Value::Str(self.0.clone().into())),
- _ => None,
- }
- }
-
- fn realize(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
- Ok(TextNode(format_eco!("@{}", self.0)).pack())
- }
-}
diff --git a/src/library/structure/table.rs b/src/library/structure/table.rs
deleted file mode 100644
index 8a4eb302..00000000
--- a/src/library/structure/table.rs
+++ /dev/null
@@ -1,160 +0,0 @@
-use crate::library::layout::{BlockSpacing, GridNode, TrackSizing};
-use crate::library::prelude::*;
-
-/// A table of items.
-#[derive(Debug, Hash)]
-pub struct TableNode {
- /// Defines sizing for content rows and columns.
- pub tracks: Axes<Vec<TrackSizing>>,
- /// Defines sizing of gutter rows and columns between content.
- pub gutter: Axes<Vec<TrackSizing>>,
- /// The content to be arranged in the table.
- pub cells: Vec<Content>,
-}
-
-#[node(Show)]
-impl TableNode {
- /// How to fill the cells.
- #[property(referenced)]
- pub const FILL: Celled<Option<Paint>> = Celled::Value(None);
- /// How to stroke the cells.
- #[property(resolve, fold)]
- pub const STROKE: Option<RawStroke> = Some(RawStroke::default());
- /// How much to pad the cells's content.
- pub const PADDING: Rel<Length> = Abs::pt(5.0).into();
-
- /// The spacing above the table.
- #[property(resolve, shorthand(around))]
- pub const ABOVE: Option<BlockSpacing> = Some(Ratio::one().into());
- /// The spacing below the table.
- #[property(resolve, shorthand(around))]
- pub const BELOW: Option<BlockSpacing> = Some(Ratio::one().into());
-
- fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
- let columns = args.named("columns")?.unwrap_or_default();
- let rows = args.named("rows")?.unwrap_or_default();
- let base_gutter: Vec<TrackSizing> = args.named("gutter")?.unwrap_or_default();
- let column_gutter = args.named("column-gutter")?;
- let row_gutter = args.named("row-gutter")?;
- Ok(Self {
- tracks: Axes::new(columns, rows),
- gutter: Axes::new(
- column_gutter.unwrap_or_else(|| base_gutter.clone()),
- row_gutter.unwrap_or(base_gutter),
- ),
- cells: args.all()?,
- }
- .pack())
- }
-}
-
-impl Show for TableNode {
- fn unguard_parts(&self, sel: Selector) -> Content {
- Self {
- tracks: self.tracks.clone(),
- gutter: self.gutter.clone(),
- cells: self.cells.iter().map(|cell| cell.unguard(sel)).collect(),
- }
- .pack()
- }
-
- fn field(&self, name: &str) -> Option<Value> {
- match name {
- "cells" => Some(Value::Array(
- self.cells.iter().cloned().map(Value::Content).collect(),
- )),
- _ => None,
- }
- }
-
- fn realize(
- &self,
- world: Tracked<dyn World>,
- styles: StyleChain,
- ) -> SourceResult<Content> {
- let fill = styles.get(Self::FILL);
- let stroke = styles.get(Self::STROKE).map(RawStroke::unwrap_or_default);
- let padding = styles.get(Self::PADDING);
-
- let cols = self.tracks.x.len().max(1);
- let cells = self
- .cells
- .iter()
- .cloned()
- .enumerate()
- .map(|(i, child)| {
- let mut child = child.padded(Sides::splat(padding));
-
- if let Some(stroke) = stroke {
- child = child.stroked(stroke);
- }
-
- let x = i % cols;
- let y = i / cols;
- if let Some(fill) = fill.resolve(world, x, y)? {
- child = child.filled(fill);
- }
-
- Ok(child)
- })
- .collect::<SourceResult<_>>()?;
-
- Ok(GridNode {
- tracks: self.tracks.clone(),
- gutter: self.gutter.clone(),
- cells,
- }
- .pack())
- }
-
- fn finalize(
- &self,
- _: Tracked<dyn World>,
- styles: StyleChain,
- realized: Content,
- ) -> SourceResult<Content> {
- Ok(realized.spaced(styles.get(Self::ABOVE), styles.get(Self::BELOW)))
- }
-}
-
-/// A value that can be configured per cell.
-#[derive(Debug, Clone, PartialEq, Hash)]
-pub enum Celled<T> {
- /// A bare value, the same for all cells.
- Value(T),
- /// A closure mapping from cell coordinates to a value.
- Func(Func, Span),
-}
-
-impl<T: Cast + Clone> Celled<T> {
- /// Resolve the value based on the cell position.
- pub fn resolve(
- &self,
- world: Tracked<dyn World>,
- x: usize,
- y: usize,
- ) -> SourceResult<T> {
- Ok(match self {
- Self::Value(value) => value.clone(),
- Self::Func(func, span) => {
- let args = Args::new(*span, [Value::Int(x as i64), Value::Int(y as i64)]);
- func.call_detached(world, args)?.cast().at(*span)?
- }
- })
- }
-}
-
-impl<T: Cast> Cast<Spanned<Value>> for Celled<T> {
- fn is(value: &Spanned<Value>) -> bool {
- matches!(&value.v, Value::Func(_)) || T::is(&value.v)
- }
-
- fn cast(value: Spanned<Value>) -> StrResult<Self> {
- match value.v {
- Value::Func(v) => Ok(Self::Func(v, value.span)),
- v => T::cast(v)
- .map(Self::Value)
- .map_err(|msg| with_alternative(msg, "function")),
- }
- }
-}