summaryrefslogtreecommitdiff
path: root/library/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-06-06 21:13:59 +0200
committerLaurenz <laurmaedje@gmail.com>2023-06-06 22:06:16 +0200
commitfd417da04f7ca4b995de7f6510abafd3e9c31307 (patch)
tree3675529c75ca7363701ac8ea306de2cc1d3cbcb3 /library/src
parent168bdf35bd773e67343c965cb473492cc5cae9e7 (diff)
Improve value casting infrastructure
Diffstat (limited to 'library/src')
-rw-r--r--library/src/compute/calc.rs363
-rw-r--r--library/src/compute/construct.rs226
-rw-r--r--library/src/compute/data.rs142
-rw-r--r--library/src/compute/foundations.rs64
-rw-r--r--library/src/compute/mod.rs44
-rw-r--r--library/src/layout/container.rs19
-rw-r--r--library/src/layout/enum.rs9
-rw-r--r--library/src/layout/grid.rs7
-rw-r--r--library/src/layout/list.rs32
-rw-r--r--library/src/layout/measure.rs7
-rw-r--r--library/src/layout/mod.rs14
-rw-r--r--library/src/layout/page.rs24
-rw-r--r--library/src/layout/spacing.rs25
-rw-r--r--library/src/layout/stack.rs13
-rw-r--r--library/src/layout/table.rs51
-rw-r--r--library/src/layout/terms.rs2
-rw-r--r--library/src/lib.rs37
-rw-r--r--library/src/math/accent.rs7
-rw-r--r--library/src/math/delimited.rs18
-rw-r--r--library/src/math/mod.rs44
-rw-r--r--library/src/math/root.rs5
-rw-r--r--library/src/math/row.rs19
-rw-r--r--library/src/math/style.rs79
-rw-r--r--library/src/meta/bibliography.rs9
-rw-r--r--library/src/meta/context.rs28
-rw-r--r--library/src/meta/counter.rs48
-rw-r--r--library/src/meta/document.rs7
-rw-r--r--library/src/meta/figure.rs19
-rw-r--r--library/src/meta/footnote.rs2
-rw-r--r--library/src/meta/heading.rs6
-rw-r--r--library/src/meta/link.rs13
-rw-r--r--library/src/meta/mod.rs16
-rw-r--r--library/src/meta/numbering.rs56
-rw-r--r--library/src/meta/outline.rs30
-rw-r--r--library/src/meta/query.rs19
-rw-r--r--library/src/meta/reference.rs19
-rw-r--r--library/src/meta/state.rs22
-rw-r--r--library/src/prelude.rs4
-rw-r--r--library/src/text/deco.rs4
-rw-r--r--library/src/text/misc.rs52
-rw-r--r--library/src/text/mod.rs96
-rw-r--r--library/src/text/shaping.rs5
-rw-r--r--library/src/visualize/mod.rs18
-rw-r--r--library/src/visualize/path.rs32
44 files changed, 761 insertions, 995 deletions
diff --git a/library/src/compute/calc.rs b/library/src/compute/calc.rs
index f485f817..0fc45891 100644
--- a/library/src/compute/calc.rs
+++ b/library/src/compute/calc.rs
@@ -11,38 +11,38 @@ use crate::prelude::*;
/// A module with computational functions.
pub fn module() -> Module {
let mut scope = Scope::new();
- scope.define("abs", abs);
- scope.define("pow", pow);
- scope.define("sqrt", sqrt);
- scope.define("sin", sin);
- scope.define("cos", cos);
- scope.define("tan", tan);
- scope.define("asin", asin);
- scope.define("acos", acos);
- scope.define("atan", atan);
- scope.define("atan2", atan2);
- scope.define("sinh", sinh);
- scope.define("cosh", cosh);
- scope.define("tanh", tanh);
- scope.define("log", log);
- scope.define("fact", fact);
- scope.define("perm", perm);
- scope.define("binom", binom);
- scope.define("gcd", gcd);
- scope.define("lcm", lcm);
- scope.define("floor", floor);
- scope.define("ceil", ceil);
- scope.define("trunc", trunc);
- scope.define("fract", fract);
- scope.define("round", round);
- scope.define("clamp", clamp);
- scope.define("min", min);
- scope.define("max", max);
- scope.define("even", even);
- scope.define("odd", odd);
- scope.define("rem", rem);
- scope.define("mod", mod_);
- scope.define("quo", quo);
+ scope.define("abs", abs_func());
+ scope.define("pow", pow_func());
+ scope.define("sqrt", sqrt_func());
+ scope.define("sin", sin_func());
+ scope.define("cos", cos_func());
+ scope.define("tan", tan_func());
+ scope.define("asin", asin_func());
+ scope.define("acos", acos_func());
+ scope.define("atan", atan_func());
+ scope.define("atan2", atan2_func());
+ scope.define("sinh", sinh_func());
+ scope.define("cosh", cosh_func());
+ scope.define("tanh", tanh_func());
+ scope.define("log", log_func());
+ scope.define("fact", fact_func());
+ scope.define("perm", perm_func());
+ scope.define("binom", binom_func());
+ scope.define("gcd", gcd_func());
+ scope.define("lcm", lcm_func());
+ scope.define("floor", floor_func());
+ scope.define("ceil", ceil_func());
+ scope.define("trunc", trunc_func());
+ scope.define("fract", fract_func());
+ scope.define("round", round_func());
+ scope.define("clamp", clamp_func());
+ scope.define("min", min_func());
+ scope.define("max", max_func());
+ scope.define("even", even_func());
+ scope.define("odd", odd_func());
+ scope.define("rem", rem_func());
+ scope.define("mod", mod_func());
+ scope.define("quo", quo_func());
scope.define("inf", Value::Float(f64::INFINITY));
scope.define("nan", Value::Float(f64::NAN));
scope.define("pi", Value::Float(std::f64::consts::PI));
@@ -61,7 +61,6 @@ pub fn module() -> Module {
///
/// Display: Absolute
/// Category: calculate
-/// Returns: any
#[func]
pub fn abs(
/// The value whose absolute value to calculate.
@@ -71,12 +70,12 @@ pub fn abs(
}
/// A value of which the absolute value can be taken.
-struct ToAbs(Value);
+pub struct ToAbs(Value);
-cast_from_value! {
+cast! {
ToAbs,
- v: i64 => Self(Value::Int(v.abs())),
- v: f64 => Self(Value::Float(v.abs())),
+ v: i64 => Self(v.abs().into_value()),
+ v: f64 => Self(v.abs().into_value()),
v: Length => Self(Value::Length(v.try_abs()
.ok_or("cannot take absolute value of this length")?)),
v: Angle => Self(Value::Angle(v.abs())),
@@ -93,17 +92,18 @@ cast_from_value! {
///
/// Display: Power
/// Category: calculate
-/// Returns: integer or float
#[func]
pub fn pow(
/// The base of the power.
base: Num,
/// The exponent of the power. Must be non-negative.
exponent: Spanned<Num>,
-) -> Value {
+ /// The callsite span.
+ span: Span,
+) -> SourceResult<Num> {
match exponent.v {
_ if exponent.v.float() == 0.0 && base.float() == 0.0 => {
- bail!(args.span, "zero to the power of zero is undefined")
+ bail!(span, "zero to the power of zero is undefined")
}
Num::Int(i) if i32::try_from(i).is_err() => {
bail!(exponent.span, "exponent is too large")
@@ -119,16 +119,16 @@ pub fn pow(
.checked_pow(b as u32)
.map(Num::Int)
.ok_or("the result is too large")
- .at(args.span)?,
+ .at(span)?,
(a, Num::Int(b)) => Num::Float(a.float().powi(b as i32)),
(a, b) => Num::Float(a.float().powf(b.float())),
};
if result.float().is_nan() {
- bail!(args.span, "the result is not a real number")
+ bail!(span, "the result is not a real number")
}
- result.value()
+ Ok(result)
}
/// Calculate the square root of a number.
@@ -141,16 +141,15 @@ pub fn pow(
///
/// Display: Square Root
/// Category: calculate
-/// Returns: float
#[func]
pub fn sqrt(
/// The number whose square root to calculate. Must be non-negative.
value: Spanned<Num>,
-) -> Value {
+) -> SourceResult<f64> {
if value.v.float() < 0.0 {
bail!(value.span, "cannot take square root of negative number");
}
- Value::Float(value.v.float().sqrt())
+ Ok(value.v.float().sqrt())
}
/// Calculate the sine of an angle.
@@ -167,17 +166,16 @@ pub fn sqrt(
///
/// Display: Sine
/// Category: calculate
-/// Returns: float
#[func]
pub fn sin(
/// The angle whose sine to calculate.
angle: AngleLike,
-) -> Value {
- Value::Float(match angle {
+) -> f64 {
+ match angle {
AngleLike::Angle(a) => a.sin(),
AngleLike::Int(n) => (n as f64).sin(),
AngleLike::Float(n) => n.sin(),
- })
+ }
}
/// Calculate the cosine of an angle.
@@ -194,17 +192,16 @@ pub fn sin(
///
/// Display: Cosine
/// Category: calculate
-/// Returns: float
#[func]
pub fn cos(
/// The angle whose cosine to calculate.
angle: AngleLike,
-) -> Value {
- Value::Float(match angle {
+) -> f64 {
+ match angle {
AngleLike::Angle(a) => a.cos(),
AngleLike::Int(n) => (n as f64).cos(),
AngleLike::Float(n) => n.cos(),
- })
+ }
}
/// Calculate the tangent of an angle.
@@ -220,17 +217,16 @@ pub fn cos(
///
/// Display: Tangent
/// Category: calculate
-/// Returns: float
#[func]
pub fn tan(
/// The angle whose tangent to calculate.
angle: AngleLike,
-) -> Value {
- Value::Float(match angle {
+) -> f64 {
+ match angle {
AngleLike::Angle(a) => a.tan(),
AngleLike::Int(n) => (n as f64).tan(),
AngleLike::Float(n) => n.tan(),
- })
+ }
}
/// Calculate the arcsine of a number.
@@ -243,17 +239,16 @@ pub fn tan(
///
/// Display: Arcsine
/// Category: calculate
-/// Returns: angle
#[func]
pub fn asin(
/// The number whose arcsine to calculate. Must be between -1 and 1.
value: Spanned<Num>,
-) -> Value {
+) -> SourceResult<Angle> {
let val = value.v.float();
if val < -1.0 || val > 1.0 {
bail!(value.span, "value must be between -1 and 1");
}
- Value::Angle(Angle::rad(val.asin()))
+ Ok(Angle::rad(val.asin()))
}
/// Calculate the arccosine of a number.
@@ -266,17 +261,16 @@ pub fn asin(
///
/// Display: Arccosine
/// Category: calculate
-/// Returns: angle
#[func]
pub fn acos(
/// The number whose arcsine to calculate. Must be between -1 and 1.
value: Spanned<Num>,
-) -> Value {
+) -> SourceResult<Angle> {
let val = value.v.float();
if val < -1.0 || val > 1.0 {
bail!(value.span, "value must be between -1 and 1");
}
- Value::Angle(Angle::rad(val.acos()))
+ Ok(Angle::rad(val.acos()))
}
/// Calculate the arctangent of a number.
@@ -289,13 +283,12 @@ pub fn acos(
///
/// Display: Arctangent
/// Category: calculate
-/// Returns: angle
#[func]
pub fn atan(
/// The number whose arctangent to calculate.
value: Num,
-) -> Value {
- Value::Angle(Angle::rad(value.float().atan()))
+) -> Angle {
+ Angle::rad(value.float().atan())
}
/// Calculate the four-quadrant arctangent of a coordinate.
@@ -310,15 +303,14 @@ pub fn atan(
///
/// Display: Four-quadrant Arctangent
/// Category: calculate
-/// Returns: angle
#[func]
pub fn atan2(
/// The X coordinate.
x: Num,
/// The Y coordinate.
y: Num,
-) -> Value {
- Value::Angle(Angle::rad(f64::atan2(y.float(), x.float())))
+) -> Angle {
+ Angle::rad(f64::atan2(y.float(), x.float()))
}
/// Calculate the hyperbolic sine of an angle.
@@ -333,17 +325,16 @@ pub fn atan2(
///
/// Display: Hyperbolic sine
/// Category: calculate
-/// Returns: float
#[func]
pub fn sinh(
/// The angle whose hyperbolic sine to calculate.
angle: AngleLike,
-) -> Value {
- Value::Float(match angle {
+) -> f64 {
+ match angle {
AngleLike::Angle(a) => a.to_rad().sinh(),
AngleLike::Int(n) => (n as f64).sinh(),
AngleLike::Float(n) => n.sinh(),
- })
+ }
}
/// Calculate the hyperbolic cosine of an angle.
@@ -358,17 +349,16 @@ pub fn sinh(
///
/// Display: Hyperbolic cosine
/// Category: calculate
-/// Returns: float
#[func]
pub fn cosh(
/// The angle whose hyperbolic cosine to calculate.
angle: AngleLike,
-) -> Value {
- Value::Float(match angle {
+) -> f64 {
+ match angle {
AngleLike::Angle(a) => a.to_rad().cosh(),
AngleLike::Int(n) => (n as f64).cosh(),
AngleLike::Float(n) => n.cosh(),
- })
+ }
}
/// Calculate the hyperbolic tangent of an angle.
@@ -383,17 +373,16 @@ pub fn cosh(
///
/// Display: Hyperbolic tangent
/// Category: calculate
-/// Returns: float
#[func]
pub fn tanh(
/// The angle whose hyperbolic tangent to calculate.
angle: AngleLike,
-) -> Value {
- Value::Float(match angle {
+) -> f64 {
+ match angle {
AngleLike::Angle(a) => a.to_rad().tanh(),
AngleLike::Int(n) => (n as f64).tanh(),
AngleLike::Float(n) => n.tanh(),
- })
+ }
}
/// Calculate the logarithm of a number.
@@ -407,7 +396,6 @@ pub fn tanh(
///
/// Display: Logarithm
/// Category: calculate
-/// Returns: float
#[func]
pub fn log(
/// The number whose logarithm to calculate. Must be strictly positive.
@@ -416,7 +404,9 @@ pub fn log(
#[named]
#[default(Spanned::new(10.0, Span::detached()))]
base: Spanned<f64>,
-) -> Value {
+ /// The callsite span.
+ span: Span,
+) -> SourceResult<f64> {
let number = value.v.float();
if number <= 0.0 {
bail!(value.span, "value must be strictly positive")
@@ -435,10 +425,10 @@ pub fn log(
};
if result.is_infinite() || result.is_nan() {
- bail!(args.span, "the result is not a real number")
+ bail!(span, "the result is not a real number")
}
- Value::Float(result)
+ Ok(result)
}
/// Calculate the factorial of a number.
@@ -450,33 +440,12 @@ pub fn log(
///
/// Display: Factorial
/// Category: calculate
-/// Returns: integer
#[func]
pub fn fact(
/// The number whose factorial to calculate. Must be non-negative.
number: u64,
-) -> Value {
- factorial_range(1, number)
- .map(Value::Int)
- .ok_or("the result is too large")
- .at(args.span)?
-}
-
-/// Calculates the product of a range of numbers. Used to calculate
-/// permutations. Returns None if the result is larger than `i64::MAX`
-fn factorial_range(start: u64, end: u64) -> Option<i64> {
- // By convention
- if end + 1 < start {
- return Some(0);
- }
-
- let real_start: u64 = cmp::max(1, start);
- let mut count: u64 = 1;
- for i in real_start..=end {
- count = count.checked_mul(i)?;
- }
-
- i64::try_from(count).ok()
+) -> StrResult<i64> {
+ Ok(fact_impl(1, number).ok_or("the result is too large")?)
}
/// Calculate a permutation.
@@ -488,23 +457,36 @@ fn factorial_range(start: u64, end: u64) -> Option<i64> {
///
/// Display: Permutation
/// Category: calculate
-/// Returns: integer
#[func]
pub fn perm(
/// The base number. Must be non-negative.
base: u64,
/// The number of permutations. Must be non-negative.
numbers: u64,
-) -> Value {
+) -> StrResult<i64> {
// By convention.
if base < numbers {
- return Ok(Value::Int(0));
+ return Ok(0);
}
- factorial_range(base - numbers + 1, base)
- .map(Value::Int)
- .ok_or("the result is too large")
- .at(args.span)?
+ Ok(fact_impl(base - numbers + 1, base).ok_or("the result is too large")?)
+}
+
+/// Calculates the product of a range of numbers. Used to calculate
+/// permutations. Returns None if the result is larger than `i64::MAX`
+fn fact_impl(start: u64, end: u64) -> Option<i64> {
+ // By convention
+ if end + 1 < start {
+ return Some(0);
+ }
+
+ let real_start: u64 = cmp::max(1, start);
+ let mut count: u64 = 1;
+ for i in real_start..=end {
+ count = count.checked_mul(i)?;
+ }
+
+ count.try_into().ok()
}
/// Calculate a binomial coefficient.
@@ -516,24 +498,20 @@ pub fn perm(
///
/// Display: Binomial
/// Category: calculate
-/// Returns: integer
#[func]
pub fn binom(
/// The upper coefficient. Must be non-negative.
n: u64,
/// The lower coefficient. Must be non-negative.
k: u64,
-) -> Value {
- binomial(n, k)
- .map(Value::Int)
- .ok_or("the result is too large")
- .at(args.span)?
+) -> StrResult<i64> {
+ Ok(binom_impl(n, k).ok_or("the result is too large")?)
}
/// Calculates a binomial coefficient, with `n` the upper coefficient and `k`
/// the lower coefficient. Returns `None` if the result is larger than
/// `i64::MAX`
-fn binomial(n: u64, k: u64) -> Option<i64> {
+fn binom_impl(n: u64, k: u64) -> Option<i64> {
if k > n {
return Some(0);
}
@@ -549,7 +527,7 @@ fn binomial(n: u64, k: u64) -> Option<i64> {
result = result.checked_mul(n - i)?.checked_div(i + 1)?;
}
- i64::try_from(result).ok()
+ result.try_into().ok()
}
/// Calculate the greatest common divisor of two integers.
@@ -561,20 +539,14 @@ fn binomial(n: u64, k: u64) -> Option<i64> {
///
/// Display: Greatest Common Divisor
/// Category: calculate
-/// Returns: integer
#[func]
pub fn gcd(
/// The first integer.
a: i64,
/// The second integer.
b: i64,
-) -> Value {
- Value::Int(calculate_gcd(a, b))
-}
-
-/// Calculates the greatest common divisor of two integers
-/// It is always non-negative.
-fn calculate_gcd(mut a: i64, mut b: i64) -> i64 {
+) -> i64 {
+ let (mut a, mut b) = (a, b);
while b != 0 {
let temp = b;
b = a % b;
@@ -593,29 +565,21 @@ fn calculate_gcd(mut a: i64, mut b: i64) -> i64 {
///
/// Display: Least Common Multiple
/// Category: calculate
-/// Returns: integer
#[func]
pub fn lcm(
/// The first integer.
a: i64,
/// The second integer.
b: i64,
-) -> Value {
- calculate_lcm(a, b)
- .map(Value::Int)
- .ok_or("the return value is too large")
- .at(args.span)?
-}
-
-/// Calculates the least common multiple between two non-zero integers
-/// Returns None if the value cannot be computed.
-/// It is always non-negative.
-fn calculate_lcm(a: i64, b: i64) -> Option<i64> {
+) -> StrResult<i64> {
if a == b {
- return Some(a.abs());
+ return Ok(a.abs());
}
- a.checked_div(calculate_gcd(a, b))?.checked_mul(b).map(|v| v.abs())
+ Ok(a.checked_div(gcd(a, b))
+ .and_then(|gcd| gcd.checked_mul(b))
+ .map(|v| v.abs())
+ .ok_or("the return value is too large")?)
}
/// Round a number down to the nearest integer.
@@ -631,15 +595,14 @@ fn calculate_lcm(a: i64, b: i64) -> Option<i64> {
///
/// Display: Round down
/// Category: calculate
-/// Returns: integer
#[func]
pub fn floor(
/// The number to round down.
value: Num,
-) -> Value {
+) -> i64 {
match value {
- Num::Int(n) => Value::Int(n),
- Num::Float(n) => Value::Int(n.floor() as i64),
+ Num::Int(n) => n,
+ Num::Float(n) => n.floor() as i64,
}
}
@@ -656,15 +619,14 @@ pub fn floor(
///
/// Display: Round up
/// Category: calculate
-/// Returns: integer
#[func]
pub fn ceil(
/// The number to round up.
value: Num,
-) -> Value {
+) -> i64 {
match value {
- Num::Int(n) => Value::Int(n),
- Num::Float(n) => Value::Int(n.ceil() as i64),
+ Num::Int(n) => n,
+ Num::Float(n) => n.ceil() as i64,
}
}
@@ -681,16 +643,15 @@ pub fn ceil(
///
/// Display: Truncate
/// Category: calculate
-/// Returns: integer
#[func]
pub fn trunc(
/// The number to truncate.
value: Num,
-) -> Value {
- Value::Int(match value {
+) -> i64 {
+ match value {
Num::Int(n) => n,
Num::Float(n) => n.trunc() as i64,
- })
+ }
}
/// Returns the fractional part of a number.
@@ -705,15 +666,14 @@ pub fn trunc(
///
/// Display: Fractional
/// Category: calculate
-/// Returns: integer or float
#[func]
pub fn fract(
/// The number to truncate.
value: Num,
-) -> Value {
+) -> Num {
match value {
- Num::Int(_) => Value::Int(0),
- Num::Float(n) => Value::Float(n.fract()),
+ Num::Int(_) => Num::Int(0),
+ Num::Float(n) => Num::Float(n.fract()),
}
}
@@ -730,7 +690,6 @@ pub fn fract(
///
/// Display: Round
/// Category: calculate
-/// Returns: integer or float
#[func]
pub fn round(
/// The number to round.
@@ -739,13 +698,13 @@ pub fn round(
#[named]
#[default(0)]
digits: i64,
-) -> Value {
+) -> Num {
match value {
- Num::Int(n) if digits == 0 => Value::Int(n),
+ Num::Int(n) if digits == 0 => Num::Int(n),
_ => {
let n = value.float();
let factor = 10.0_f64.powi(digits as i32);
- Value::Float((n * factor).round() / factor)
+ Num::Float((n * factor).round() / factor)
}
}
}
@@ -761,7 +720,6 @@ pub fn round(
///
/// Display: Clamp
/// Category: calculate
-/// Returns: integer or float
#[func]
pub fn clamp(
/// The number to clamp.
@@ -770,11 +728,11 @@ pub fn clamp(
min: Num,
/// The inclusive maximum value.
max: Spanned<Num>,
-) -> Value {
+) -> SourceResult<Num> {
if max.v.float() < min.float() {
bail!(max.span, "max must be greater than or equal to min")
}
- value.apply3(min, max.v, i64::clamp, f64::clamp).value()
+ Ok(value.apply3(min, max.v, i64::clamp, f64::clamp))
}
/// Determine the minimum of a sequence of values.
@@ -787,15 +745,16 @@ pub fn clamp(
///
/// Display: Minimum
/// Category: calculate
-/// Returns: any
#[func]
pub fn min(
/// The sequence of values from which to extract the minimum.
/// Must not be empty.
#[variadic]
values: Vec<Spanned<Value>>,
-) -> Value {
- minmax(args.span, values, Ordering::Less)?
+ /// The callsite span.
+ span: Span,
+) -> SourceResult<Value> {
+ minmax(span, values, Ordering::Less)
}
/// Determine the maximum of a sequence of values.
@@ -808,15 +767,16 @@ pub fn min(
///
/// Display: Maximum
/// Category: calculate
-/// Returns: any
#[func]
pub fn max(
/// The sequence of values from which to extract the maximum.
/// Must not be empty.
#[variadic]
values: Vec<Spanned<Value>>,
-) -> Value {
- minmax(args.span, values, Ordering::Greater)?
+ /// The callsite span.
+ span: Span,
+) -> SourceResult<Value> {
+ minmax(span, values, Ordering::Greater)
}
/// Find the minimum or maximum of a sequence of values.
@@ -851,13 +811,12 @@ fn minmax(
///
/// Display: Even
/// Category: calculate
-/// Returns: boolean
#[func]
pub fn even(
/// The number to check for evenness.
value: i64,
-) -> Value {
- Value::Bool(value % 2 == 0)
+) -> bool {
+ value % 2 == 0
}
/// Determine whether an integer is odd.
@@ -871,13 +830,12 @@ pub fn even(
///
/// Display: Odd
/// Category: calculate
-/// Returns: boolean
#[func]
pub fn odd(
/// The number to check for oddness.
value: i64,
-) -> Value {
- Value::Bool(value % 2 != 0)
+) -> bool {
+ value % 2 != 0
}
/// Calculate the remainder of two numbers.
@@ -890,18 +848,17 @@ pub fn odd(
///
/// Display: Remainder
/// Category: calculate
-/// Returns: integer or float
#[func]
pub fn rem(
/// The dividend of the remainder.
dividend: Num,
/// The divisor of the remainder.
divisor: Spanned<Num>,
-) -> Value {
+) -> SourceResult<Num> {
if divisor.v.float() == 0.0 {
bail!(divisor.span, "divisor must not be zero");
}
- dividend.apply2(divisor.v, Rem::rem, Rem::rem).value()
+ Ok(dividend.apply2(divisor.v, Rem::rem, Rem::rem))
}
/// Calculate the modulus of two numbers. (Deprecated)
@@ -911,18 +868,17 @@ pub fn rem(
///
/// Display: Modulus
/// Category: calculate
-/// Returns: integer or float
#[func]
pub fn mod_(
/// The dividend of the remainder.
dividend: Num,
/// The divisor of the remainder.
divisor: Spanned<Num>,
-) -> Value {
+) -> SourceResult<Num> {
if divisor.v.float() == 0.0 {
bail!(divisor.span, "divisor must not be zero");
}
- dividend.apply2(divisor.v, Rem::rem, Rem::rem).value()
+ Ok(dividend.apply2(divisor.v, Rem::rem, Rem::rem))
}
/// Calculate the quotient of two numbers.
@@ -935,33 +891,29 @@ pub fn mod_(
///
/// Display: Quotient
/// Category: calculate
-/// Returns: integer or float
#[func]
pub fn quo(
/// The dividend of the quotient.
dividend: Num,
/// The divisor of the quotient.
divisor: Spanned<Num>,
-) -> Value {
+) -> SourceResult<i64> {
if divisor.v.float() == 0.0 {
bail!(divisor.span, "divisor must not be zero");
}
- Value::Int(match dividend.apply2(divisor.v, Div::div, Div::div) {
- Num::Int(i) => i,
- Num::Float(f) => f.floor() as i64, // Note: the result should be an integer but floats doesn't have the same precision as i64.
- })
+ Ok(floor(dividend.apply2(divisor.v, Div::div, Div::div)))
}
/// A value which can be passed to functions that work with integers and floats.
#[derive(Debug, Copy, Clone)]
-enum Num {
+pub enum Num {
Int(i64),
Float(f64),
}
impl Num {
- fn apply2(
+ pub fn apply2(
self,
other: Self,
int: impl FnOnce(i64, i64) -> i64,
@@ -973,7 +925,7 @@ impl Num {
}
}
- fn apply3(
+ pub fn apply3(
self,
other: Self,
third: Self,
@@ -986,35 +938,32 @@ impl Num {
}
}
- fn float(self) -> f64 {
+ pub fn float(self) -> f64 {
match self {
Self::Int(v) => v as f64,
Self::Float(v) => v,
}
}
-
- fn value(self) -> Value {
- match self {
- Self::Int(v) => Value::Int(v),
- Self::Float(v) => Value::Float(v),
- }
- }
}
-cast_from_value! {
+cast! {
Num,
+ self => match self {
+ Self::Int(v) => v.into_value(),
+ Self::Float(v) => v.into_value(),
+ },
v: i64 => Self::Int(v),
v: f64 => Self::Float(v),
}
/// A value that can be passed to a trigonometric function.
-enum AngleLike {
+pub enum AngleLike {
Int(i64),
Float(f64),
Angle(Angle),
}
-cast_from_value! {
+cast! {
AngleLike,
v: i64 => Self::Int(v),
v: f64 => Self::Float(v),
diff --git a/library/src/compute/construct.rs b/library/src/compute/construct.rs
index d11eb398..5d2d35ae 100644
--- a/library/src/compute/construct.rs
+++ b/library/src/compute/construct.rs
@@ -3,7 +3,7 @@ use std::str::FromStr;
use time::{Month, PrimitiveDateTime};
-use typst::eval::{Datetime, Dynamic, Regex};
+use typst::eval::{Datetime, Regex};
use crate::prelude::*;
@@ -23,19 +23,18 @@ use crate::prelude::*;
///
/// Display: Integer
/// Category: construct
-/// Returns: integer
#[func]
pub fn int(
/// The value that should be converted to an integer.
value: ToInt,
-) -> Value {
- Value::Int(value.0)
+) -> i64 {
+ value.0
}
/// A value that can be cast to an integer.
-struct ToInt(i64);
+pub struct ToInt(i64);
-cast_from_value! {
+cast! {
ToInt,
v: bool => Self(v as i64),
v: i64 => Self(v),
@@ -63,19 +62,18 @@ cast_from_value! {
///
/// Display: Float
/// Category: construct
-/// Returns: float
#[func]
pub fn float(
/// The value that should be converted to a float.
value: ToFloat,
-) -> Value {
- Value::Float(value.0)
+) -> f64 {
+ value.0
}
/// A value that can be cast to a float.
-struct ToFloat(f64);
+pub struct ToFloat(f64);
-cast_from_value! {
+cast! {
ToFloat,
v: bool => Self(v as i64 as f64),
v: i64 => Self(v as f64),
@@ -95,13 +93,12 @@ cast_from_value! {
///
/// Display: Luma
/// Category: construct
-/// Returns: color
#[func]
pub fn luma(
/// The gray component.
gray: Component,
-) -> Value {
- Value::Color(LumaColor::new(gray.0).into())
+) -> Color {
+ LumaColor::new(gray.0).into()
}
/// Create an RGB(A) color.
@@ -121,7 +118,6 @@ pub fn luma(
///
/// Display: RGB
/// Category: construct
-/// Returns: color
#[func]
pub fn rgb(
/// The color in hexadecimal notation.
@@ -150,11 +146,14 @@ pub fn rgb(
/// The alpha component.
#[external]
alpha: Component,
-) -> Value {
- Value::Color(if let Some(string) = args.find::<Spanned<EcoString>>()? {
+ /// The arguments.
+ args: Args,
+) -> SourceResult<Color> {
+ let mut args = args;
+ Ok(if let Some(string) = args.find::<Spanned<EcoString>>()? {
match RgbaColor::from_str(&string.v) {
Ok(color) => color.into(),
- Err(msg) => bail!(string.span, msg),
+ Err(msg) => bail!(string.span, "{msg}"),
}
} else {
let Component(r) = args.expect("red component")?;
@@ -166,9 +165,9 @@ pub fn rgb(
}
/// An integer or ratio component.
-struct Component(u8);
+pub struct Component(u8);
-cast_from_value! {
+cast! {
Component,
v: i64 => match v {
0 ..= 255 => Self(v as u8),
@@ -208,10 +207,9 @@ cast_from_value! {
///
/// Display: Datetime
/// Category: construct
-/// Returns: datetime
#[func]
#[scope(
- scope.define("today", datetime_today);
+ scope.define("today", datetime_today_func());
scope
)]
pub fn datetime(
@@ -233,95 +231,77 @@ pub fn datetime(
/// The second of the datetime.
#[named]
second: Option<SecondComponent>,
-) -> Value {
+) -> StrResult<Datetime> {
let time = match (hour, minute, second) {
(Some(hour), Some(minute), Some(second)) => {
match time::Time::from_hms(hour.0, minute.0, second.0) {
Ok(time) => Some(time),
- Err(_) => bail!(args.span, "time is invalid"),
+ Err(_) => bail!("time is invalid"),
}
}
(None, None, None) => None,
- _ => bail!(args.span, "time is incomplete"),
+ _ => bail!("time is incomplete"),
};
let date = match (year, month, day) {
(Some(year), Some(month), Some(day)) => {
match time::Date::from_calendar_date(year.0, month.0, day.0) {
Ok(date) => Some(date),
- Err(_) => bail!(args.span, "date is invalid"),
+ Err(_) => bail!("date is invalid"),
}
}
(None, None, None) => None,
- _ => bail!(args.span, "date is incomplete"),
+ _ => bail!("date is incomplete"),
};
- match (date, time) {
- (Some(date), Some(time)) => Value::Dyn(Dynamic::new(Datetime::Datetime(
- PrimitiveDateTime::new(date, time),
- ))),
- (Some(date), None) => Value::Dyn(Dynamic::new(Datetime::Date(date))),
- (None, Some(time)) => Value::Dyn(Dynamic::new(Datetime::Time(time))),
+ Ok(match (date, time) {
+ (Some(date), Some(time)) => {
+ Datetime::Datetime(PrimitiveDateTime::new(date, time))
+ }
+ (Some(date), None) => Datetime::Date(date),
+ (None, Some(time)) => Datetime::Time(time),
(None, None) => {
- bail!(args.span, "at least one of date or time must be fully specified")
+ bail!("at least one of date or time must be fully specified")
}
- }
+ })
}
-struct YearComponent(i32);
-struct MonthComponent(Month);
-struct DayComponent(u8);
-struct HourComponent(u8);
-struct MinuteComponent(u8);
-struct SecondComponent(u8);
+pub struct YearComponent(i32);
+pub struct MonthComponent(Month);
+pub struct DayComponent(u8);
+pub struct HourComponent(u8);
+pub struct MinuteComponent(u8);
+pub struct SecondComponent(u8);
-cast_from_value!(
+cast! {
YearComponent,
- v: i64 => match i32::try_from(v) {
- Ok(n) => Self(n),
- _ => Err("year is invalid")?
- }
-);
+ v: i32 => Self(v),
+}
-cast_from_value!(
+cast! {
MonthComponent,
- v: i64 => match u8::try_from(v).ok().and_then(|n1| Month::try_from(n1).ok()).map(Self) {
- Some(m) => m,
- _ => Err("month is invalid")?
- }
-);
+ v: u8 => Self(Month::try_from(v).map_err(|_| "month is invalid")?)
+}
-cast_from_value!(
+cast! {
DayComponent,
- v: i64 => match u8::try_from(v) {
- Ok(n) => Self(n),
- _ => Err("day is invalid")?
- }
-);
+ v: u8 => Self(v),
+}
-cast_from_value!(
+cast! {
HourComponent,
- v: i64 => match u8::try_from(v) {
- Ok(n) => Self(n),
- _ => Err("hour is invalid")?
- }
-);
+ v: u8 => Self(v),
+}
-cast_from_value!(
+cast! {
MinuteComponent,
- v: i64 => match u8::try_from(v) {
- Ok(n) => Self(n),
- _ => Err("minute is invalid")?
- }
-);
+ v: u8 => Self(v),
+}
-cast_from_value!(
+cast! {
SecondComponent,
- v: i64 => match u8::try_from(v) {
- Ok(n) => Self(n),
- _ => Err("second is invalid")?
- }
-);
+ v: u8 => Self(v),
+}
/// Returns the current date.
///
@@ -333,7 +313,6 @@ cast_from_value!(
///
/// Display: Today
/// Category: construct
-/// Returns: datetime
#[func]
pub fn datetime_today(
/// An offset to apply to the current UTC date. If set to `{auto}`, the
@@ -341,13 +320,13 @@ pub fn datetime_today(
#[named]
#[default]
offset: Smart<i64>,
-) -> Value {
- let current_date = match vm.vt.world.today(offset.as_custom()) {
- Some(d) => d,
- None => bail!(args.span, "unable to get the current date"),
- };
-
- Value::Dyn(Dynamic::new(current_date))
+ /// The virtual machine.
+ vt: &mut Vt,
+) -> StrResult<Datetime> {
+ Ok(vt
+ .world
+ .today(offset.as_custom())
+ .ok_or("unable to get the current date")?)
}
/// Create a CMYK color.
@@ -365,7 +344,6 @@ pub fn datetime_today(
///
/// Display: CMYK
/// Category: construct
-/// Returns: color
#[func]
pub fn cmyk(
/// The cyan component.
@@ -376,14 +354,14 @@ pub fn cmyk(
yellow: RatioComponent,
/// The key component.
key: RatioComponent,
-) -> Value {
- Value::Color(CmykColor::new(cyan.0, magenta.0, yellow.0, key.0).into())
+) -> Color {
+ CmykColor::new(cyan.0, magenta.0, yellow.0, key.0).into()
}
/// A component that must be a ratio.
-struct RatioComponent(u8);
+pub struct RatioComponent(u8);
-cast_from_value! {
+cast! {
RatioComponent,
v: Ratio => if (0.0 ..= 1.0).contains(&v.get()) {
Self((v.get() * 255.0).round() as u8)
@@ -413,7 +391,6 @@ cast_from_value! {
///
/// Display: Symbol
/// Category: construct
-/// Returns: symbol
#[func]
pub fn symbol(
/// The variants of the symbol.
@@ -425,10 +402,12 @@ pub fn symbol(
/// all attached modifiers and the minimum number of other modifiers.
#[variadic]
variants: Vec<Spanned<Variant>>,
-) -> Value {
+ /// The callsite span.
+ span: Span,
+) -> SourceResult<Symbol> {
let mut list = Vec::new();
if variants.is_empty() {
- bail!(args.span, "expected at least one variant");
+ bail!(span, "expected at least one variant");
}
for Spanned { v, span } in variants {
if list.iter().any(|(prev, _)| &v.0 == prev) {
@@ -436,13 +415,13 @@ pub fn symbol(
}
list.push((v.0, v.1));
}
- Value::Symbol(Symbol::runtime(list.into_boxed_slice()))
+ Ok(Symbol::runtime(list.into_boxed_slice()))
}
/// A value that can be cast to a symbol.
-struct Variant(EcoString, char);
+pub struct Variant(EcoString, char);
-cast_from_value! {
+cast! {
Variant,
c: char => Self(EcoString::new(), c),
array: Array => {
@@ -476,11 +455,10 @@ cast_from_value! {
///
/// Display: String
/// Category: construct
-/// Returns: string
#[func]
#[scope(
- scope.define("to-unicode", to_unicode);
- scope.define("from-unicode", from_unicode);
+ scope.define("to-unicode", str_to_unicode_func());
+ scope.define("from-unicode", str_from_unicode_func());
scope
)]
pub fn str(
@@ -490,13 +468,13 @@ pub fn str(
#[named]
#[default(Spanned::new(10, Span::detached()))]
base: Spanned<i64>,
-) -> Value {
- match value {
+) -> SourceResult<Str> {
+ Ok(match value {
ToStr::Str(s) => {
if base.v != 10 {
bail!(base.span, "base is only supported for integers");
}
- Value::Str(s)
+ s
}
ToStr::Int(n) => {
if base.v < 2 || base.v > 36 {
@@ -504,18 +482,18 @@ pub fn str(
}
int_to_base(n, base.v).into()
}
- }
+ })
}
/// A value that can be cast to a string.
-enum ToStr {
+pub enum ToStr {
/// A string value ready to be used as-is.
Str(Str),
/// An integer about to be formatted in a given base.
Int(i64),
}
-cast_from_value! {
+cast! {
ToStr,
v: i64 => Self::Int(v),
v: f64 => Self::Str(format_str!("{}", v)),
@@ -572,16 +550,14 @@ fn int_to_base(mut n: i64, base: i64) -> EcoString {
///
/// Display: String To Unicode
/// Category: construct
-/// Returns: int
#[func]
-pub fn to_unicode(
+pub fn str_to_unicode(
/// The character that should be converted.
value: char,
-) -> Value {
- Value::Int(From::<u32>::from(value.into()))
+) -> u32 {
+ value.into()
}
-#[func]
/// Converts a unicode code point into its corresponding string.
///
/// ```example
@@ -590,18 +566,18 @@ pub fn to_unicode(
///
/// Display: Sting From Unicode
/// Category: construct
-/// Returns: string
-pub fn from_unicode(
+#[func]
+pub fn str_from_unicode(
/// The code point that should be converted.
value: CodePoint,
-) -> Value {
- Value::Str(format_str!("{}", value.0))
+) -> Str {
+ format_str!("{}", value.0)
}
/// The numeric representation of a single unicode code point.
-struct CodePoint(char);
+pub struct CodePoint(char);
-cast_from_value! {
+cast! {
CodePoint,
v: i64 => {
Self(v.try_into().ok().and_then(|v: u32| v.try_into().ok()).ok_or_else(
@@ -631,13 +607,12 @@ cast_from_value! {
///
/// Display: Label
/// Category: construct
-/// Returns: label
#[func]
pub fn label(
/// The name of the label.
name: EcoString,
-) -> Value {
- Value::Label(Label(name))
+) -> Label {
+ Label(name)
}
/// Create a regular expression from a string.
@@ -663,7 +638,6 @@ pub fn label(
///
/// Display: Regex
/// Category: construct
-/// Returns: regex
#[func]
pub fn regex(
/// The regular expression as a string.
@@ -677,8 +651,8 @@ pub fn regex(
/// and extract its text to use it for your regular expressions:
/// ```{regex(`\d+\.\d+\.\d+`.text)}```.
regex: Spanned<EcoString>,
-) -> Value {
- Regex::new(&regex.v).at(regex.span)?.into()
+) -> SourceResult<Regex> {
+ Regex::new(&regex.v).at(regex.span)
}
/// Create an array consisting of a sequence of numbers.
@@ -698,7 +672,6 @@ pub fn regex(
///
/// Display: Range
/// Category: construct
-/// Returns: array
#[func]
pub fn range(
/// The start of the range (inclusive).
@@ -712,7 +685,10 @@ pub fn range(
#[named]
#[default(NonZeroI64::new(1).unwrap())]
step: NonZeroI64,
-) -> Value {
+ /// The arguments.
+ args: Args,
+) -> SourceResult<Array> {
+ let mut args = args;
let first = args.expect::<i64>("end")?;
let (start, end) = match args.eat::<i64>()? {
Some(second) => (first, second),
@@ -729,7 +705,7 @@ pub fn range(
x += step;
}
- Value::Array(array)
+ Ok(array)
}
#[cfg(test)]
diff --git a/library/src/compute/data.rs b/library/src/compute/data.rs
index e3918458..b236536d 100644
--- a/library/src/compute/data.rs
+++ b/library/src/compute/data.rs
@@ -14,21 +14,22 @@ use crate::prelude::*;
/// #raw(text, lang: "html")
/// ```
///
-/// Display: Plain text
+/// Display: Read
/// Category: data-loading
-/// Returns: string
#[func]
pub fn read(
/// Path to a file.
path: Spanned<EcoString>,
-) -> Value {
+ /// The virtual machine.
+ vm: &mut Vm,
+) -> SourceResult<Str> {
let Spanned { v: path, span } = path;
let path = vm.locate(&path).at(span)?;
let data = vm.world().file(&path).at(span)?;
let text = std::str::from_utf8(&data)
.map_err(|_| "file is not valid utf-8")
.at(span)?;
- Value::Str(text.into())
+ Ok(text.into())
}
/// Read structured data from a CSV file.
@@ -51,7 +52,6 @@ pub fn read(
///
/// Display: CSV
/// Category: data-loading
-/// Returns: array
#[func]
pub fn csv(
/// Path to a CSV file.
@@ -61,7 +61,9 @@ pub fn csv(
#[named]
#[default]
delimiter: Delimiter,
-) -> Value {
+ /// The virtual machine.
+ vm: &mut Vm,
+) -> SourceResult<Array> {
let Spanned { v: path, span } = path;
let path = vm.locate(&path).at(span)?;
let data = vm.world().file(&path).at(span)?;
@@ -74,22 +76,30 @@ pub fn csv(
let mut array = Array::new();
for (line, result) in reader.records().enumerate() {
- // Original solution use line from error, but that is incorrect with has_headers set to false
- // See issue: https://github.com/BurntSushi/rust-csv/issues/184
+ // Original solution use line from error, but that is incorrect with
+ // `has_headers` set to `false`. See issue:
+ // https://github.com/BurntSushi/rust-csv/issues/184
let line = line + 1; // Counting lines from 1
let row = result.map_err(|err| format_csv_error(err, line)).at(span)?;
- let sub = row.into_iter().map(|field| Value::Str(field.into())).collect();
+ let sub = row.into_iter().map(|field| field.into_value()).collect();
array.push(Value::Array(sub))
}
- Value::Array(array)
+ Ok(array)
}
/// The delimiter to use when parsing CSV files.
-struct Delimiter(char);
+pub struct Delimiter(char);
-cast_from_value! {
+impl Default for Delimiter {
+ fn default() -> Self {
+ Self(',')
+ }
+}
+
+cast! {
Delimiter,
+ self => self.0.into_value(),
v: EcoString => {
let mut chars = v.chars();
let first = chars.next().ok_or("delimiter must not be empty")?;
@@ -105,16 +115,6 @@ cast_from_value! {
},
}
-cast_to_value! {
- v: Delimiter => v.0.into()
-}
-
-impl Default for Delimiter {
- fn default() -> Self {
- Self(',')
- }
-}
-
/// Format the user-facing CSV error message.
fn format_csv_error(error: csv::Error, line: usize) -> EcoString {
match error.kind() {
@@ -168,38 +168,39 @@ fn format_csv_error(error: csv::Error, line: usize) -> EcoString {
///
/// Display: JSON
/// Category: data-loading
-/// Returns: array or dictionary
#[func]
pub fn json(
/// Path to a JSON file.
path: Spanned<EcoString>,
-) -> Value {
+ /// The virtual machine.
+ vm: &mut Vm,
+) -> SourceResult<Value> {
let Spanned { v: path, span } = path;
let path = vm.locate(&path).at(span)?;
let data = vm.world().file(&path).at(span)?;
let value: serde_json::Value =
serde_json::from_slice(&data).map_err(format_json_error).at(span)?;
- convert_json(value)
+ Ok(convert_json(value))
}
/// Convert a JSON value to a Typst value.
fn convert_json(value: serde_json::Value) -> Value {
match value {
serde_json::Value::Null => Value::None,
- serde_json::Value::Bool(v) => Value::Bool(v),
+ serde_json::Value::Bool(v) => v.into_value(),
serde_json::Value::Number(v) => match v.as_i64() {
- Some(int) => Value::Int(int),
- None => Value::Float(v.as_f64().unwrap_or(f64::NAN)),
+ Some(int) => int.into_value(),
+ None => v.as_f64().unwrap_or(f64::NAN).into_value(),
},
- serde_json::Value::String(v) => Value::Str(v.into()),
+ serde_json::Value::String(v) => v.into_value(),
serde_json::Value::Array(v) => {
- Value::Array(v.into_iter().map(convert_json).collect())
+ v.into_iter().map(convert_json).collect::<Array>().into_value()
}
- serde_json::Value::Object(v) => Value::Dict(
- v.into_iter()
- .map(|(key, value)| (key.into(), convert_json(value)))
- .collect(),
- ),
+ serde_json::Value::Object(v) => v
+ .into_iter()
+ .map(|(key, value)| (key.into(), convert_json(value)))
+ .collect::<Dict>()
+ .into_value(),
}
}
@@ -233,12 +234,13 @@ fn format_json_error(error: serde_json::Error) -> EcoString {
///
/// Display: TOML
/// Category: data-loading
-/// Returns: dictionary
#[func]
pub fn toml(
/// Path to a TOML file.
path: Spanned<EcoString>,
-) -> Value {
+ /// The virtual machine.
+ vm: &mut Vm,
+) -> SourceResult<Value> {
let Spanned { v: path, span } = path;
let path = vm.locate(&path).at(span)?;
let data = vm.world().file(&path).at(span)?;
@@ -248,24 +250,26 @@ pub fn toml(
.at(span)?;
let value: toml::Value = toml::from_str(raw).map_err(format_toml_error).at(span)?;
- convert_toml(value)
+ Ok(convert_toml(value))
}
/// Convert a TOML value to a Typst value.
fn convert_toml(value: toml::Value) -> Value {
match value {
- toml::Value::String(v) => Value::Str(v.into()),
- toml::Value::Integer(v) => Value::Int(v),
- toml::Value::Float(v) => Value::Float(v),
- toml::Value::Boolean(v) => Value::Bool(v),
- toml::Value::Array(v) => Value::Array(v.into_iter().map(convert_toml).collect()),
- toml::Value::Table(v) => Value::Dict(
- v.into_iter()
- .map(|(key, value)| (key.into(), convert_toml(value)))
- .collect(),
- ),
+ toml::Value::String(v) => v.into_value(),
+ toml::Value::Integer(v) => v.into_value(),
+ toml::Value::Float(v) => v.into_value(),
+ toml::Value::Boolean(v) => v.into_value(),
+ toml::Value::Array(v) => {
+ v.into_iter().map(convert_toml).collect::<Array>().into_value()
+ }
+ toml::Value::Table(v) => v
+ .into_iter()
+ .map(|(key, value)| (key.into(), convert_toml(value)))
+ .collect::<Dict>()
+ .into_value(),
// TODO: Make it use native date/time object(s) once it is implemented.
- toml::Value::Datetime(v) => Value::Str(v.to_string().into()),
+ toml::Value::Datetime(v) => v.to_string().into_value(),
}
}
@@ -323,39 +327,40 @@ fn format_toml_error(error: toml::de::Error) -> EcoString {
///
/// Display: YAML
/// Category: data-loading
-/// Returns: array or dictionary or none or boolean or integer or float or string
#[func]
pub fn yaml(
/// Path to a YAML file.
path: Spanned<EcoString>,
-) -> Value {
+ /// The virtual machine.
+ vm: &mut Vm,
+) -> SourceResult<Value> {
let Spanned { v: path, span } = path;
let path = vm.locate(&path).at(span)?;
let data = vm.world().file(&path).at(span)?;
let value: serde_yaml::Value =
serde_yaml::from_slice(&data).map_err(format_yaml_error).at(span)?;
- convert_yaml(value)
+ Ok(convert_yaml(value))
}
/// Convert a YAML value to a Typst value.
fn convert_yaml(value: serde_yaml::Value) -> Value {
match value {
serde_yaml::Value::Null => Value::None,
- serde_yaml::Value::Bool(v) => Value::Bool(v),
+ serde_yaml::Value::Bool(v) => v.into_value(),
serde_yaml::Value::Number(v) => match v.as_i64() {
- Some(int) => Value::Int(int),
- None => Value::Float(v.as_f64().unwrap_or(f64::NAN)),
+ Some(int) => int.into_value(),
+ None => v.as_f64().unwrap_or(f64::NAN).into_value(),
},
- serde_yaml::Value::String(v) => Value::Str(v.into()),
+ serde_yaml::Value::String(v) => v.into_value(),
serde_yaml::Value::Sequence(v) => {
- Value::Array(v.into_iter().map(convert_yaml).collect())
+ v.into_iter().map(convert_yaml).collect::<Array>().into_value()
}
- serde_yaml::Value::Mapping(v) => Value::Dict(
- v.into_iter()
- .map(|(key, value)| (convert_yaml_key(key), convert_yaml(value)))
- .filter_map(|(key, value)| key.map(|key| (key, value)))
- .collect(),
- ),
+ serde_yaml::Value::Mapping(v) => v
+ .into_iter()
+ .map(|(key, value)| (convert_yaml_key(key), convert_yaml(value)))
+ .filter_map(|(key, value)| key.map(|key| (key, value)))
+ .collect::<Dict>()
+ .into_value(),
}
}
@@ -425,24 +430,25 @@ fn format_yaml_error(error: serde_yaml::Error) -> EcoString {
///
/// Display: XML
/// Category: data-loading
-/// Returns: array
#[func]
pub fn xml(
/// Path to an XML file.
path: Spanned<EcoString>,
-) -> Value {
+ /// The virtual machine.
+ vm: &mut Vm,
+) -> SourceResult<Value> {
let Spanned { v: path, span } = path;
let path = vm.locate(&path).at(span)?;
let data = vm.world().file(&path).at(span)?;
let text = std::str::from_utf8(&data).map_err(FileError::from).at(span)?;
let document = roxmltree::Document::parse(text).map_err(format_xml_error).at(span)?;
- convert_xml(document.root())
+ Ok(convert_xml(document.root()))
}
/// Convert an XML node to a Typst value.
fn convert_xml(node: roxmltree::Node) -> Value {
if node.is_text() {
- return Value::Str(node.text().unwrap_or_default().into());
+ return node.text().unwrap_or_default().into_value();
}
let children: Array = node.children().map(convert_xml).collect();
@@ -453,7 +459,7 @@ fn convert_xml(node: roxmltree::Node) -> Value {
let tag: Str = node.tag_name().name().into();
let attrs: Dict = node
.attributes()
- .map(|attr| (attr.name().into(), attr.value().into()))
+ .map(|attr| (attr.name().into(), attr.value().into_value()))
.collect();
Value::Dict(dict! {
diff --git a/library/src/compute/foundations.rs b/library/src/compute/foundations.rs
index 6a92c2a8..7964ac45 100644
--- a/library/src/compute/foundations.rs
+++ b/library/src/compute/foundations.rs
@@ -16,12 +16,11 @@ use crate::prelude::*;
///
/// Display: Type
/// Category: foundations
-/// Returns: string
#[func]
pub fn type_(
/// The value whose type's to determine.
value: Value,
-) -> Value {
+) -> Str {
value.type_name().into()
}
@@ -44,13 +43,12 @@ pub fn type_(
///
/// Display: Representation
/// Category: foundations
-/// Returns: string
#[func]
pub fn repr(
/// The value whose string representation to produce.
value: Value,
-) -> Value {
- value.repr().into()
+) -> Str {
+ value.repr()
}
/// Fail with an error.
@@ -63,13 +61,12 @@ pub fn repr(
///
/// Display: Panic
/// Category: foundations
-/// Returns:
#[func]
pub fn panic(
/// The values to panic with.
#[variadic]
values: Vec<Value>,
-) -> Value {
+) -> StrResult<Never> {
let mut msg = EcoString::from("panicked");
if !values.is_empty() {
msg.push_str(" with: ");
@@ -80,7 +77,7 @@ pub fn panic(
msg.push_str(&value.repr());
}
}
- bail!(args.span, msg);
+ Err(msg)
}
/// Ensure that a condition is fulfilled.
@@ -98,11 +95,10 @@ pub fn panic(
///
/// Display: Assert
/// Category: foundations
-/// Returns:
#[func]
#[scope(
- scope.define("eq", assert_eq);
- scope.define("ne", assert_ne);
+ scope.define("eq", assert_eq_func());
+ scope.define("ne", assert_ne_func());
scope
)]
pub fn assert(
@@ -110,17 +106,16 @@ pub fn assert(
condition: bool,
/// The error message when the assertion fails.
#[named]
- #[default]
message: Option<EcoString>,
-) -> Value {
+) -> StrResult<NoneValue> {
if !condition {
if let Some(message) = message {
- bail!(args.span, "assertion failed: {}", message);
+ bail!("assertion failed: {message}");
} else {
- bail!(args.span, "assertion failed");
+ bail!("assertion failed");
}
}
- Value::None
+ Ok(NoneValue)
}
/// Ensure that two values are equal.
@@ -135,7 +130,6 @@ pub fn assert(
///
/// Display: Assert Equals
/// Category: foundations
-/// Returns:
#[func]
pub fn assert_eq(
/// The first value to compare.
@@ -147,22 +141,16 @@ pub fn assert_eq(
/// An optional message to display on error instead of the representations
/// of the compared values.
#[named]
- #[default]
message: Option<EcoString>,
-) -> Value {
+) -> StrResult<NoneValue> {
if left != right {
if let Some(message) = message {
- bail!(args.span, "equality assertion failed: {}", message);
+ bail!("equality assertion failed: {message}");
} else {
- bail!(
- args.span,
- "equality assertion failed: value {:?} was not equal to {:?}",
- left,
- right
- );
+ bail!("equality assertion failed: value {left:?} was not equal to {right:?}");
}
}
- Value::None
+ Ok(NoneValue)
}
/// Ensure that two values are not equal.
@@ -177,7 +165,6 @@ pub fn assert_eq(
///
/// Display: Assert Not Equals
/// Category: foundations
-/// Returns:
#[func]
pub fn assert_ne(
/// The first value to compare.
@@ -189,22 +176,16 @@ pub fn assert_ne(
/// An optional message to display on error instead of the representations
/// of the compared values.
#[named]
- #[default]
message: Option<EcoString>,
-) -> Value {
+) -> StrResult<NoneValue> {
if left == right {
if let Some(message) = message {
- bail!(args.span, "inequality assertion failed: {}", message);
+ bail!("inequality assertion failed: {message}");
} else {
- bail!(
- args.span,
- "inequality assertion failed: value {:?} was equal to {:?}",
- left,
- right
- );
+ bail!("inequality assertion failed: value {left:?} was equal to {right:?}");
}
}
- Value::None
+ Ok(NoneValue)
}
/// Evaluate a string as Typst code.
@@ -220,14 +201,15 @@ pub fn assert_ne(
///
/// Display: Evaluate
/// Category: foundations
-/// Returns: any
#[func]
pub fn eval(
/// A string of Typst code to evaluate.
///
/// The code in the string cannot interact with the file system.
source: Spanned<String>,
-) -> Value {
+ /// The virtual machine.
+ vm: &mut Vm,
+) -> SourceResult<Value> {
let Spanned { v: text, span } = source;
- typst::eval::eval_string(vm.world(), &text, span)?
+ typst::eval::eval_string(vm.world(), &text, span)
}
diff --git a/library/src/compute/mod.rs b/library/src/compute/mod.rs
index 90730e02..e9e4870c 100644
--- a/library/src/compute/mod.rs
+++ b/library/src/compute/mod.rs
@@ -13,27 +13,27 @@ use crate::prelude::*;
/// Hook up all compute definitions.
pub(super) fn define(global: &mut Scope) {
- global.define("type", type_);
- global.define("repr", repr);
- global.define("panic", panic);
- global.define("assert", assert);
- global.define("eval", eval);
- global.define("int", int);
- global.define("float", float);
- global.define("luma", luma);
- global.define("rgb", rgb);
- global.define("cmyk", cmyk);
- global.define("datetime", datetime);
- global.define("symbol", symbol);
- global.define("str", str);
- global.define("label", label);
- global.define("regex", regex);
- global.define("range", range);
- global.define("read", read);
- global.define("csv", csv);
- global.define("json", json);
- global.define("toml", toml);
- global.define("yaml", yaml);
- global.define("xml", xml);
+ global.define("type", type_func());
+ global.define("repr", repr_func());
+ global.define("panic", panic_func());
+ global.define("assert", assert_func());
+ global.define("eval", eval_func());
+ global.define("int", int_func());
+ global.define("float", float_func());
+ global.define("luma", luma_func());
+ global.define("rgb", rgb_func());
+ global.define("cmyk", cmyk_func());
+ global.define("datetime", datetime_func());
+ global.define("symbol", symbol_func());
+ global.define("str", str_func());
+ global.define("label", label_func());
+ global.define("regex", regex_func());
+ global.define("range", range_func());
+ global.define("read", read_func());
+ global.define("csv", csv_func());
+ global.define("json", json_func());
+ global.define("toml", toml_func());
+ global.define("yaml", yaml_func());
+ global.define("xml", xml_func());
global.define("calc", calc::module());
}
diff --git a/library/src/layout/container.rs b/library/src/layout/container.rs
index b7d0ba2f..d28c8b5e 100644
--- a/library/src/layout/container.rs
+++ b/library/src/layout/container.rs
@@ -1,3 +1,5 @@
+use typst::eval::AutoValue;
+
use super::VElem;
use crate::layout::Spacing;
use crate::prelude::*;
@@ -482,17 +484,14 @@ impl<T: Into<Spacing>> From<T> for Sizing {
}
}
-cast_from_value! {
+cast! {
Sizing,
- _: Smart<Never> => Self::Auto,
+ self => match self {
+ Self::Auto => Value::Auto,
+ Self::Rel(rel) => rel.into_value(),
+ Self::Fr(fr) => fr.into_value(),
+ },
+ _: AutoValue => Self::Auto,
v: Rel<Length> => Self::Rel(v),
v: Fr => Self::Fr(v),
}
-
-cast_to_value! {
- v: Sizing => match v {
- Sizing::Auto => Value::Auto,
- Sizing::Rel(rel) => Value::Relative(rel),
- Sizing::Fr(fr) => Value::Fraction(fr),
- }
-}
diff --git a/library/src/layout/enum.rs b/library/src/layout/enum.rs
index 0fd0ebb2..62f4c351 100644
--- a/library/src/layout/enum.rs
+++ b/library/src/layout/enum.rs
@@ -283,7 +283,7 @@ pub struct EnumItem {
pub body: Content,
}
-cast_from_value! {
+cast! {
EnumItem,
array: Array => {
let mut iter = array.into_iter();
@@ -298,15 +298,12 @@ cast_from_value! {
struct Parent(usize);
-cast_from_value! {
+cast! {
Parent,
+ self => self.0.into_value(),
v: usize => Self(v),
}
-cast_to_value! {
- v: Parent => v.0.into()
-}
-
impl Fold for Parent {
type Output = Vec<usize>;
diff --git a/library/src/layout/grid.rs b/library/src/layout/grid.rs
index 3c299eb0..ea155acd 100644
--- a/library/src/layout/grid.rs
+++ b/library/src/layout/grid.rs
@@ -129,17 +129,14 @@ impl Layout for GridElem {
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
pub struct TrackSizings(pub Vec<Sizing>);
-cast_from_value! {
+cast! {
TrackSizings,
+ self => self.0.into_value(),
sizing: Sizing => Self(vec![sizing]),
count: NonZeroUsize => Self(vec![Sizing::Auto; count.get()]),
values: Array => Self(values.into_iter().map(Value::cast).collect::<StrResult<_>>()?),
}
-cast_to_value! {
- v: TrackSizings => v.0.into()
-}
-
/// Performs grid layout.
pub struct GridLayouter<'a, 'v> {
/// The core context.
diff --git a/library/src/layout/list.rs b/library/src/layout/list.rs
index 1e42d51b..b308c2ce 100644
--- a/library/src/layout/list.rs
+++ b/library/src/layout/list.rs
@@ -174,7 +174,7 @@ pub struct ListItem {
pub body: Content,
}
-cast_from_value! {
+cast! {
ListItem,
v: Content => v.to::<Self>().cloned().unwrap_or_else(|| Self::new(v.clone())),
}
@@ -193,13 +193,21 @@ impl ListMarker {
Self::Content(list) => {
list.get(depth).or(list.last()).cloned().unwrap_or_default()
}
- Self::Func(func) => func.call_vt(vt, [Value::Int(depth as i64)])?.display(),
+ Self::Func(func) => func.call_vt(vt, [depth])?.display(),
})
}
}
-cast_from_value! {
+cast! {
ListMarker,
+ self => match self {
+ Self::Content(vec) => if vec.len() == 1 {
+ vec.into_iter().next().unwrap().into_value()
+ } else {
+ vec.into_value()
+ },
+ Self::Func(func) => func.into_value(),
+ },
v: Content => Self::Content(vec![v]),
array: Array => {
if array.is_empty() {
@@ -210,28 +218,14 @@ cast_from_value! {
v: Func => Self::Func(v),
}
-cast_to_value! {
- v: ListMarker => match v {
- ListMarker::Content(vec) => if vec.len() == 1 {
- vec.into_iter().next().unwrap().into()
- } else {
- vec.into()
- },
- ListMarker::Func(func) => func.into(),
- }
-}
-
struct Depth;
-cast_from_value! {
+cast! {
Depth,
+ self => Value::None,
_: Value => Self,
}
-cast_to_value! {
- _: Depth => Value::None
-}
-
impl Fold for Depth {
type Output = usize;
diff --git a/library/src/layout/measure.rs b/library/src/layout/measure.rs
index c7a19243..4cbd1698 100644
--- a/library/src/layout/measure.rs
+++ b/library/src/layout/measure.rs
@@ -39,17 +39,18 @@ use crate::prelude::*;
///
/// Display: Measure
/// Category: layout
-/// Returns: dictionary
#[func]
pub fn measure(
/// The content whose size to measure.
content: Content,
/// The styles with which to layout the content.
styles: Styles,
-) -> Value {
+ /// The virtual machine.
+ vm: &mut Vm,
+) -> SourceResult<Dict> {
let pod = Regions::one(Axes::splat(Abs::inf()), Axes::splat(false));
let styles = StyleChain::new(&styles);
let frame = content.measure(&mut vm.vt, styles, pod)?.into_frame();
let Size { x, y } = frame.size();
- dict! { "width" => x, "height" => y }.into()
+ Ok(dict! { "width" => x, "height" => y })
}
diff --git a/library/src/layout/mod.rs b/library/src/layout/mod.rs
index 2d5b18c8..9b6ba204 100644
--- a/library/src/layout/mod.rs
+++ b/library/src/layout/mod.rs
@@ -87,7 +87,19 @@ pub(super) fn define(global: &mut Scope) {
global.define("scale", ScaleElem::func());
global.define("rotate", RotateElem::func());
global.define("hide", HideElem::func());
- global.define("measure", measure);
+ global.define("measure", measure_func());
+ global.define("ltr", Dir::LTR);
+ global.define("rtl", Dir::RTL);
+ global.define("ttb", Dir::TTB);
+ global.define("btt", Dir::BTT);
+ global.define("start", GenAlign::Start);
+ global.define("end", GenAlign::End);
+ global.define("left", GenAlign::Specific(Align::Left));
+ global.define("center", GenAlign::Specific(Align::Center));
+ global.define("right", GenAlign::Specific(Align::Right));
+ global.define("top", GenAlign::Specific(Align::Top));
+ global.define("horizon", GenAlign::Specific(Align::Horizon));
+ global.define("bottom", GenAlign::Specific(Align::Bottom));
}
/// Root-level layout.
diff --git a/library/src/layout/page.rs b/library/src/layout/page.rs
index 6510bd58..26ebbc53 100644
--- a/library/src/layout/page.rs
+++ b/library/src/layout/page.rs
@@ -341,7 +341,7 @@ impl PageElem {
let footer_descent = self.footer_descent(styles);
let numbering_meta = FrameItem::Meta(
- Meta::PageNumbering(self.numbering(styles).into()),
+ Meta::PageNumbering(self.numbering(styles).into_value()),
Size::zero(),
);
@@ -451,24 +451,21 @@ impl Marginal {
pub fn resolve(&self, vt: &mut Vt, page: usize) -> SourceResult<Content> {
Ok(match self {
Self::Content(content) => content.clone(),
- Self::Func(func) => func.call_vt(vt, [Value::Int(page as i64)])?.display(),
+ Self::Func(func) => func.call_vt(vt, [page])?.display(),
})
}
}
-cast_from_value! {
+cast! {
Marginal,
+ self => match self {
+ Self::Content(v) => v.into_value(),
+ Self::Func(v) => v.into_value(),
+ },
v: Content => Self::Content(v),
v: Func => Self::Func(v),
}
-cast_to_value! {
- v: Marginal => match v {
- Marginal::Content(v) => v.into(),
- Marginal::Func(v) => v.into(),
- }
-}
-
/// Specification of a paper.
#[derive(Debug, Copy, Clone, Hash)]
pub struct Paper {
@@ -517,17 +514,14 @@ macro_rules! papers {
}
}
- cast_from_value! {
+ cast! {
Paper,
+ self => self.name.into_value(),
$(
/// Produces a paper of the respective size.
$name => Self::$var,
)*
}
-
- cast_to_value! {
- v: Paper => v.name.into()
- }
};
}
diff --git a/library/src/layout/spacing.rs b/library/src/layout/spacing.rs
index 588f9f29..0cbf6641 100644
--- a/library/src/layout/spacing.rs
+++ b/library/src/layout/spacing.rs
@@ -157,7 +157,7 @@ impl Behave for VElem {
}
}
-cast_from_value! {
+cast! {
VElem,
v: Content => v.to::<Self>().cloned().ok_or("expected `v` element")?,
}
@@ -221,23 +221,20 @@ impl PartialOrd for Spacing {
}
}
-cast_from_value! {
+cast! {
Spacing,
- v: Rel<Length> => Self::Rel(v),
- v: Fr => Self::Fr(v),
-}
-
-cast_to_value! {
- v: Spacing => match v {
- Spacing::Rel(rel) => {
+ self => match self {
+ Self::Rel(rel) => {
if rel.rel.is_zero() {
- Value::Length(rel.abs)
+ rel.abs.into_value()
} else if rel.abs.is_zero() {
- Value::Ratio(rel.rel)
+ rel.rel.into_value()
} else {
- Value::Relative(rel)
+ rel.into_value()
}
}
- Spacing::Fr(fr) => Value::Fraction(fr),
- }
+ Self::Fr(fr) => fr.into_value(),
+ },
+ v: Rel<Length> => Self::Rel(v),
+ v: Fr => Self::Fr(v),
}
diff --git a/library/src/layout/stack.rs b/library/src/layout/stack.rs
index 2ce2cc28..f8670026 100644
--- a/library/src/layout/stack.rs
+++ b/library/src/layout/stack.rs
@@ -90,19 +90,16 @@ impl Debug for StackChild {
}
}
-cast_from_value! {
+cast! {
StackChild,
+ self => match self {
+ Self::Spacing(spacing) => spacing.into_value(),
+ Self::Block(content) => content.into_value(),
+ },
v: Spacing => Self::Spacing(v),
v: Content => Self::Block(v),
}
-cast_to_value! {
- v: StackChild => match v {
- StackChild::Spacing(spacing) => spacing.into(),
- StackChild::Block(content) => content.into(),
- }
-}
-
/// Performs stack layout.
struct StackLayouter<'a> {
/// The stacking direction.
diff --git a/library/src/layout/table.rs b/library/src/layout/table.rs
index 9d5a83d2..74370cb2 100644
--- a/library/src/layout/table.rs
+++ b/library/src/layout/table.rs
@@ -1,3 +1,5 @@
+use typst::eval::{CastInfo, Reflect};
+
use crate::layout::{AlignElem, GridLayouter, TrackSizings};
use crate::meta::{Figurable, LocalName};
use crate::prelude::*;
@@ -243,15 +245,12 @@ pub enum Celled<T> {
Array(Vec<T>),
}
-impl<T: Cast + Clone + Default> Celled<T> {
+impl<T: Default + Clone + FromValue> Celled<T> {
/// Resolve the value based on the cell position.
pub fn resolve(&self, vt: &mut Vt, x: usize, y: usize) -> SourceResult<T> {
Ok(match self {
Self::Value(value) => value.clone(),
- Self::Func(func) => func
- .call_vt(vt, [Value::Int(x as i64), Value::Int(y as i64)])?
- .cast()
- .at(func.span())?,
+ Self::Func(func) => func.call_vt(vt, [x, y])?.cast().at(func.span())?,
Self::Array(array) => x
.checked_rem(array.len())
.and_then(|i| array.get(i))
@@ -267,33 +266,35 @@ impl<T: Default> Default for Celled<T> {
}
}
-impl<T: Cast> Cast for Celled<T> {
- fn is(value: &Value) -> bool {
- matches!(value, Value::Array(_) | Value::Func(_)) || T::is(value)
+impl<T: Reflect> Reflect for Celled<T> {
+ fn describe() -> CastInfo {
+ T::describe() + Array::describe() + Func::describe()
}
- fn cast(value: Value) -> StrResult<Self> {
- match value {
- Value::Func(v) => Ok(Self::Func(v)),
- Value::Array(array) => {
- Ok(Self::Array(array.into_iter().map(T::cast).collect::<StrResult<_>>()?))
- }
- v if T::is(&v) => Ok(Self::Value(T::cast(v)?)),
- v => <Self as Cast>::error(v),
- }
+ fn castable(value: &Value) -> bool {
+ Array::castable(value) || Func::castable(value) || T::castable(value)
}
+}
- fn describe() -> CastInfo {
- T::describe() + CastInfo::Type("array") + CastInfo::Type("function")
+impl<T: IntoValue> IntoValue for Celled<T> {
+ fn into_value(self) -> Value {
+ match self {
+ Self::Value(value) => value.into_value(),
+ Self::Func(func) => func.into_value(),
+ Self::Array(arr) => arr.into_value(),
+ }
}
}
-impl<T: Into<Value>> From<Celled<T>> for Value {
- fn from(celled: Celled<T>) -> Self {
- match celled {
- Celled::Value(value) => value.into(),
- Celled::Func(func) => func.into(),
- Celled::Array(arr) => arr.into(),
+impl<T: FromValue> FromValue for Celled<T> {
+ fn from_value(value: Value) -> StrResult<Self> {
+ match value {
+ Value::Func(v) => Ok(Self::Func(v)),
+ Value::Array(array) => Ok(Self::Array(
+ array.into_iter().map(T::from_value).collect::<StrResult<_>>()?,
+ )),
+ v if T::castable(&v) => Ok(Self::Value(T::from_value(v)?)),
+ v => Err(Self::error(&v)),
}
}
}
diff --git a/library/src/layout/terms.rs b/library/src/layout/terms.rs
index 43127d97..0cfe98d9 100644
--- a/library/src/layout/terms.rs
+++ b/library/src/layout/terms.rs
@@ -147,7 +147,7 @@ pub struct TermItem {
pub description: Content,
}
-cast_from_value! {
+cast! {
TermItem,
array: Array => {
let mut iter = array.into_iter();
diff --git a/library/src/lib.rs b/library/src/lib.rs
index 0f8346f0..0bd88501 100644
--- a/library/src/lib.rs
+++ b/library/src/lib.rs
@@ -1,6 +1,7 @@
//! Typst's standard library.
#![allow(clippy::wildcard_in_or_patterns)]
+#![allow(clippy::manual_range_contains)]
#![allow(clippy::comparison_chain)]
pub mod compute;
@@ -15,7 +16,7 @@ pub mod visualize;
use typst::diag::At;
use typst::eval::{LangItems, Library, Module, Scope};
-use typst::geom::{Align, Color, Dir, GenAlign, Smart};
+use typst::geom::Smart;
use typst::model::{Element, Styles};
use self::layout::LayoutRoot;
@@ -41,40 +42,6 @@ fn global(math: Module) -> Module {
symbols::define(&mut global);
global.define("math", math);
- // Colors.
- global.define("black", Color::BLACK);
- global.define("gray", Color::GRAY);
- global.define("silver", Color::SILVER);
- global.define("white", Color::WHITE);
- global.define("navy", Color::NAVY);
- global.define("blue", Color::BLUE);
- global.define("aqua", Color::AQUA);
- global.define("teal", Color::TEAL);
- global.define("eastern", Color::EASTERN);
- global.define("purple", Color::PURPLE);
- global.define("fuchsia", Color::FUCHSIA);
- global.define("maroon", Color::MAROON);
- global.define("red", Color::RED);
- global.define("orange", Color::ORANGE);
- global.define("yellow", Color::YELLOW);
- global.define("olive", Color::OLIVE);
- global.define("green", Color::GREEN);
- global.define("lime", Color::LIME);
-
- // Other constants.
- global.define("ltr", Dir::LTR);
- global.define("rtl", Dir::RTL);
- global.define("ttb", Dir::TTB);
- global.define("btt", Dir::BTT);
- global.define("start", GenAlign::Start);
- global.define("end", GenAlign::End);
- global.define("left", GenAlign::Specific(Align::Left));
- global.define("center", GenAlign::Specific(Align::Center));
- global.define("right", GenAlign::Specific(Align::Right));
- global.define("top", GenAlign::Specific(Align::Top));
- global.define("horizon", GenAlign::Specific(Align::Horizon));
- global.define("bottom", GenAlign::Specific(Align::Bottom));
-
Module::new("global").with_scope(global)
}
diff --git a/library/src/math/accent.rs b/library/src/math/accent.rs
index 7878782b..4a12f5c5 100644
--- a/library/src/math/accent.rs
+++ b/library/src/math/accent.rs
@@ -128,15 +128,12 @@ impl Accent {
}
}
-cast_from_value! {
+cast! {
Accent,
+ self => self.0.into_value(),
v: char => Self::new(v),
v: Content => match v.to::<TextElem>() {
Some(elem) => Value::Str(elem.text().into()).cast()?,
None => Err("expected text")?,
},
}
-
-cast_to_value! {
- v: Accent => v.0.into()
-}
diff --git a/library/src/math/delimited.rs b/library/src/math/delimited.rs
index 403f7922..08d18a5e 100644
--- a/library/src/math/delimited.rs
+++ b/library/src/math/delimited.rs
@@ -113,12 +113,11 @@ fn scale(
///
/// Display: Floor
/// Category: math
-/// Returns: content
#[func]
pub fn floor(
/// The expression to floor.
body: Content,
-) -> Value {
+) -> Content {
delimited(body, '⌊', '⌋')
}
@@ -131,12 +130,11 @@ pub fn floor(
///
/// Display: Ceil
/// Category: math
-/// Returns: content
#[func]
pub fn ceil(
/// The expression to ceil.
body: Content,
-) -> Value {
+) -> Content {
delimited(body, '⌈', '⌉')
}
@@ -149,12 +147,11 @@ pub fn ceil(
///
/// Display: Round
/// Category: math
-/// Returns: content
#[func]
pub fn round(
/// The expression to round.
body: Content,
-) -> Value {
+) -> Content {
delimited(body, '⌊', '⌉')
}
@@ -168,12 +165,11 @@ pub fn round(
///
/// Display: Abs
/// Category: math
-/// Returns: content
#[func]
pub fn abs(
/// The expression to take the absolute value of.
body: Content,
-) -> Value {
+) -> Content {
delimited(body, '|', '|')
}
@@ -186,21 +182,19 @@ pub fn abs(
///
/// Display: Norm
/// Category: math
-/// Returns: content
#[func]
pub fn norm(
/// The expression to take the norm of.
body: Content,
-) -> Value {
+) -> Content {
delimited(body, '‖', '‖')
}
-fn delimited(body: Content, left: char, right: char) -> Value {
+fn delimited(body: Content, left: char, right: char) -> Content {
LrElem::new(Content::sequence([
TextElem::packed(left),
body,
TextElem::packed(right),
]))
.pack()
- .into()
}
diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs
index 3ea96cfa..4ab23f23 100644
--- a/library/src/math/mod.rs
+++ b/library/src/math/mod.rs
@@ -59,11 +59,11 @@ pub fn module() -> Module {
// Grouping.
math.define("lr", LrElem::func());
- math.define("abs", abs);
- math.define("norm", norm);
- math.define("floor", floor);
- math.define("ceil", ceil);
- math.define("round", round);
+ math.define("abs", abs_func());
+ math.define("norm", norm_func());
+ math.define("floor", floor_func());
+ math.define("ceil", ceil_func());
+ math.define("round", round_func());
// Attachments and accents.
math.define("attach", AttachElem::func());
@@ -86,24 +86,24 @@ pub fn module() -> Module {
math.define("cases", CasesElem::func());
// Roots.
- math.define("sqrt", sqrt);
+ math.define("sqrt", sqrt_func());
math.define("root", RootElem::func());
// Styles.
- math.define("upright", upright);
- math.define("bold", bold);
- math.define("italic", italic);
- math.define("serif", serif);
- math.define("sans", sans);
- math.define("cal", cal);
- math.define("frak", frak);
- math.define("mono", mono);
- math.define("bb", bb);
-
- math.define("display", display);
- math.define("inline", inline);
- math.define("script", script);
- math.define("sscript", sscript);
+ math.define("upright", upright_func());
+ math.define("bold", bold_func());
+ math.define("italic", italic_func());
+ math.define("serif", serif_func());
+ math.define("sans", sans_func());
+ math.define("cal", cal_func());
+ math.define("frak", frak_func());
+ math.define("mono", mono_func());
+ math.define("bb", bb_func());
+
+ math.define("display", display_func());
+ math.define("inline", inline_func());
+ math.define("script", script_func());
+ math.define("sscript", sscript_func());
// Text operators.
math.define("op", OpElem::func());
@@ -197,9 +197,7 @@ impl Synthesize for EquationElem {
let supplement = match self.supplement(styles) {
Smart::Auto => TextElem::packed(self.local_name_in(styles)),
Smart::Custom(None) => Content::empty(),
- Smart::Custom(Some(supplement)) => {
- supplement.resolve(vt, [self.clone().into()])?
- }
+ Smart::Custom(Some(supplement)) => supplement.resolve(vt, [self.clone()])?,
};
self.push_block(self.block(styles));
diff --git a/library/src/math/root.rs b/library/src/math/root.rs
index 05f3a2a7..d1c5f46a 100644
--- a/library/src/math/root.rs
+++ b/library/src/math/root.rs
@@ -9,13 +9,12 @@ use super::*;
///
/// Display: Square Root
/// Category: math
-/// Returns: content
#[func]
pub fn sqrt(
/// The expression to take the square root of.
radicand: Content,
-) -> Value {
- RootElem::new(radicand).pack().into()
+) -> Content {
+ RootElem::new(radicand).pack()
}
/// A general root.
diff --git a/library/src/math/row.rs b/library/src/math/row.rs
index 9b693bc1..6e666e89 100644
--- a/library/src/math/row.rs
+++ b/library/src/math/row.rs
@@ -233,3 +233,22 @@ impl<T: Into<MathFragment>> From<T> for MathRow {
Self(vec![fragment.into()])
}
}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+enum LeftRightAlternator {
+ Left,
+ Right,
+}
+
+impl Iterator for LeftRightAlternator {
+ type Item = LeftRightAlternator;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let r = Some(*self);
+ match self {
+ Self::Left => *self = Self::Right,
+ Self::Right => *self = Self::Left,
+ }
+ r
+ }
+}
diff --git a/library/src/math/style.rs b/library/src/math/style.rs
index e0e1ccad..05ff64dc 100644
--- a/library/src/math/style.rs
+++ b/library/src/math/style.rs
@@ -9,13 +9,12 @@ use super::*;
///
/// Display: Bold
/// Category: math
-/// Returns: content
#[func]
pub fn bold(
/// The content to style.
body: Content,
-) -> Value {
- MathStyleElem::new(body).with_bold(Some(true)).pack().into()
+) -> Content {
+ MathStyleElem::new(body).with_bold(Some(true)).pack()
}
/// Upright (non-italic) font style in math.
@@ -27,13 +26,12 @@ pub fn bold(
///
/// Display: Upright
/// Category: math
-/// Returns: content
#[func]
pub fn upright(
/// The content to style.
body: Content,
-) -> Value {
- MathStyleElem::new(body).with_italic(Some(false)).pack().into()
+) -> Content {
+ MathStyleElem::new(body).with_italic(Some(false)).pack()
}
/// Italic font style in math.
@@ -42,13 +40,12 @@ pub fn upright(
///
/// Display: Italic
/// Category: math
-/// Returns: content
#[func]
pub fn italic(
/// The content to style.
body: Content,
-) -> Value {
- MathStyleElem::new(body).with_italic(Some(true)).pack().into()
+) -> Content {
+ MathStyleElem::new(body).with_italic(Some(true)).pack()
}
/// Serif (roman) font style in math.
///
@@ -56,16 +53,12 @@ pub fn italic(
///
/// Display: Serif
/// Category: math
-/// Returns: content
#[func]
pub fn serif(
/// The content to style.
body: Content,
-) -> Value {
- MathStyleElem::new(body)
- .with_variant(Some(MathVariant::Serif))
- .pack()
- .into()
+) -> Content {
+ MathStyleElem::new(body).with_variant(Some(MathVariant::Serif)).pack()
}
/// Sans-serif font style in math.
@@ -77,16 +70,12 @@ pub fn serif(
///
/// Display: Sans-serif
/// Category: math
-/// Returns: content
#[func]
pub fn sans(
/// The content to style.
body: Content,
-) -> Value {
- MathStyleElem::new(body)
- .with_variant(Some(MathVariant::Sans))
- .pack()
- .into()
+) -> Content {
+ MathStyleElem::new(body).with_variant(Some(MathVariant::Sans)).pack()
}
/// Calligraphic font style in math.
@@ -98,16 +87,12 @@ pub fn sans(
///
/// Display: Calligraphic
/// Category: math
-/// Returns: content
#[func]
pub fn cal(
/// The content to style.
body: Content,
-) -> Value {
- MathStyleElem::new(body)
- .with_variant(Some(MathVariant::Cal))
- .pack()
- .into()
+) -> Content {
+ MathStyleElem::new(body).with_variant(Some(MathVariant::Cal)).pack()
}
/// Fraktur font style in math.
@@ -119,16 +104,12 @@ pub fn cal(
///
/// Display: Fraktur
/// Category: math
-/// Returns: content
#[func]
pub fn frak(
/// The content to style.
body: Content,
-) -> Value {
- MathStyleElem::new(body)
- .with_variant(Some(MathVariant::Frak))
- .pack()
- .into()
+) -> Content {
+ MathStyleElem::new(body).with_variant(Some(MathVariant::Frak)).pack()
}
/// Monospace font style in math.
@@ -140,16 +121,12 @@ pub fn frak(
///
/// Display: Monospace
/// Category: math
-/// Returns: content
#[func]
pub fn mono(
/// The content to style.
body: Content,
-) -> Value {
- MathStyleElem::new(body)
- .with_variant(Some(MathVariant::Mono))
- .pack()
- .into()
+) -> Content {
+ MathStyleElem::new(body).with_variant(Some(MathVariant::Mono)).pack()
}
/// Blackboard bold (double-struck) font style in math.
@@ -166,16 +143,12 @@ pub fn mono(
///
/// Display: Blackboard Bold
/// Category: math
-/// Returns: content
#[func]
pub fn bb(
/// The content to style.
body: Content,
-) -> Value {
- MathStyleElem::new(body)
- .with_variant(Some(MathVariant::Bb))
- .pack()
- .into()
+) -> Content {
+ MathStyleElem::new(body).with_variant(Some(MathVariant::Bb)).pack()
}
/// Forced display style in math.
@@ -189,7 +162,6 @@ pub fn bb(
///
/// Display: Display Size
/// Category: math
-/// Returns: content
#[func]
pub fn display(
/// The content to size.
@@ -199,12 +171,11 @@ pub fn display(
#[named]
#[default(false)]
cramp: bool,
-) -> Value {
+) -> Content {
MathStyleElem::new(body)
.with_size(Some(MathSize::Display))
.with_cramp(Some(cramp))
.pack()
- .into()
}
/// Forced inline (text) style in math.
@@ -219,7 +190,6 @@ pub fn display(
///
/// Display: Inline Size
/// Category: math
-/// Returns: content
#[func]
pub fn inline(
/// The content to size.
@@ -229,12 +199,11 @@ pub fn inline(
#[named]
#[default(false)]
cramp: bool,
-) -> Value {
+) -> Content {
MathStyleElem::new(body)
.with_size(Some(MathSize::Text))
.with_cramp(Some(cramp))
.pack()
- .into()
}
/// Forced script style in math.
@@ -248,7 +217,6 @@ pub fn inline(
///
/// Display: Script Size
/// Category: math
-/// Returns: content
#[func]
pub fn script(
/// The content to size.
@@ -258,12 +226,11 @@ pub fn script(
#[named]
#[default(true)]
cramp: bool,
-) -> Value {
+) -> Content {
MathStyleElem::new(body)
.with_size(Some(MathSize::Script))
.with_cramp(Some(cramp))
.pack()
- .into()
}
/// Forced second script style in math.
@@ -278,7 +245,6 @@ pub fn script(
///
/// Display: Script-Script Size
/// Category: math
-/// Returns: content
#[func]
pub fn sscript(
/// The content to size.
@@ -288,12 +254,11 @@ pub fn sscript(
#[named]
#[default(true)]
cramp: bool,
-) -> Value {
+) -> Content {
MathStyleElem::new(body)
.with_size(Some(MathSize::ScriptScript))
.with_cramp(Some(cramp))
.pack()
- .into()
}
/// A font variant in math.
diff --git a/library/src/meta/bibliography.rs b/library/src/meta/bibliography.rs
index a2491bb5..5e5145b1 100644
--- a/library/src/meta/bibliography.rs
+++ b/library/src/meta/bibliography.rs
@@ -84,16 +84,13 @@ pub struct BibliographyElem {
#[derive(Debug, Default, Clone, Hash)]
pub struct BibPaths(Vec<EcoString>);
-cast_from_value! {
+cast! {
BibPaths,
+ self => self.0.into_value(),
v: EcoString => Self(vec![v]),
v: Array => Self(v.into_iter().map(Value::cast).collect::<StrResult<_>>()?),
}
-cast_to_value! {
- v: BibPaths => v.0.into()
-}
-
impl BibliographyElem {
/// Find the document's bibliography.
pub fn find(introspector: Tracked<Introspector>) -> StrResult<Self> {
@@ -374,7 +371,7 @@ impl Show for CiteElem {
}
}
-cast_from_value! {
+cast! {
CiteElem,
v: Content => v.to::<Self>().cloned().ok_or("expected citation")?,
}
diff --git a/library/src/meta/context.rs b/library/src/meta/context.rs
index ad466305..d599c63e 100644
--- a/library/src/meta/context.rs
+++ b/library/src/meta/context.rs
@@ -46,7 +46,6 @@ use crate::prelude::*;
///
/// Display: Locate
/// Category: meta
-/// Returns: content
#[func]
pub fn locate(
/// A function that receives a `location`. Its return value is displayed
@@ -56,8 +55,8 @@ pub fn locate(
/// `locate` appears in the document. That makes it possible to generate
/// content that depends on its own location in the document.
func: Func,
-) -> Value {
- LocateElem::new(func).pack().into()
+) -> Content {
+ LocateElem::new(func).pack()
}
/// Executes a `locate` call.
@@ -79,7 +78,7 @@ impl Show for LocateElem {
}
let location = self.0.location().unwrap();
- Ok(self.func().call_vt(vt, [location.into()])?.display())
+ Ok(self.func().call_vt(vt, [location])?.display())
}
}
@@ -102,7 +101,6 @@ impl Show for LocateElem {
///
/// Display: Style
/// Category: meta
-/// Returns: content
#[func]
pub fn style(
/// A function to call with the styles. Its return value is displayed
@@ -112,8 +110,8 @@ pub fn style(
/// `style` appears in the document. That makes it possible to generate
/// content that depends on the style context it appears in.
func: Func,
-) -> Value {
- StyleElem::new(func).pack().into()
+) -> Content {
+ StyleElem::new(func).pack()
}
/// Executes a style access.
@@ -130,7 +128,7 @@ struct StyleElem {
impl Show for StyleElem {
#[tracing::instrument(name = "StyleElem::show", skip_all)]
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
- Ok(self.func().call_vt(vt, [styles.to_map().into()])?.display())
+ Ok(self.func().call_vt(vt, [styles.to_map()])?.display())
}
}
@@ -177,7 +175,6 @@ impl Show for StyleElem {
///
/// Display: Layout
/// Category: meta
-/// Returns: content
#[func]
pub fn layout(
/// A function to call with the outer container's size. Its return value is
@@ -190,8 +187,8 @@ pub fn layout(
/// `layout` appears in the document. That makes it possible to generate
/// content that depends on the size of the container it is inside of.
func: Func,
-) -> Value {
- LayoutElem::new(func).pack().into()
+) -> Content {
+ LayoutElem::new(func).pack()
}
/// Executes a `layout` call.
@@ -213,16 +210,13 @@ impl Layout for LayoutElem {
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
- // Gets the current region's base size, which will be the size of the outer container,
- // or of the page if there is no such container.
+ // Gets the current region's base size, which will be the size of the
+ // outer container, or of the page if there is no such container.
let Size { x, y } = regions.base();
- let size_dict = dict! { "width" => x, "height" => y }.into();
-
let result = self
.func()
- .call_vt(vt, [size_dict])? // calls func(size)
+ .call_vt(vt, [dict! { "width" => x, "height" => y }])?
.display();
-
result.layout(vt, styles, regions)
}
}
diff --git a/library/src/meta/counter.rs b/library/src/meta/counter.rs
index 46992d38..ef4646ab 100644
--- a/library/src/meta/counter.rs
+++ b/library/src/meta/counter.rs
@@ -277,7 +277,6 @@ use crate::prelude::*;
///
/// Display: Counter
/// Category: meta
-/// Returns: counter
#[func]
pub fn counter(
/// The key that identifies this counter.
@@ -288,8 +287,8 @@ pub fn counter(
/// - If this is an element function or selector, counts through its elements,
/// - If this is the [`page`]($func/page) function, counts through pages.
key: CounterKey,
-) -> Value {
- Value::dynamic(Counter::new(key))
+) -> Counter {
+ Counter::new(key)
}
/// Counts through pages, elements, and more.
@@ -317,17 +316,17 @@ impl Counter {
span: Span,
) -> SourceResult<Value> {
let value = match method {
- "display" => {
- self.display(args.eat()?, args.named("both")?.unwrap_or(false)).into()
- }
+ "display" => self
+ .display(args.eat()?, args.named("both")?.unwrap_or(false))
+ .into_value(),
"step" => self
.update(CounterUpdate::Step(
args.named("level")?.unwrap_or(NonZeroUsize::ONE),
))
- .into(),
- "update" => self.update(args.expect("value or function")?).into(),
- "at" => self.at(&mut vm.vt, args.expect("location")?)?.into(),
- "final" => self.final_(&mut vm.vt, args.expect("location")?)?.into(),
+ .into_value(),
+ "update" => self.update(args.expect("value or function")?).into_value(),
+ "at" => self.at(&mut vm.vt, args.expect("location")?)?.into_value(),
+ "final" => self.final_(&mut vm.vt, args.expect("location")?)?.into_value(),
_ => bail!(span, "type counter has no method `{}`", method),
};
args.finish()?;
@@ -342,10 +341,7 @@ impl Counter {
/// Get the value of the state at the given location.
pub fn at(&self, vt: &mut Vt, location: Location) -> SourceResult<CounterState> {
let sequence = self.sequence(vt)?;
- let offset = vt
- .introspector
- .query(&Selector::before(self.selector(), location, true))
- .len();
+ let offset = vt.introspector.query(&self.selector().before(location, true)).len();
let (mut state, page) = sequence[offset].clone();
if self.is_page() {
let delta = vt.introspector.page(location).get().saturating_sub(page.get());
@@ -479,8 +475,8 @@ impl Debug for Counter {
}
}
-cast_from_value! {
- Counter: "counter",
+cast! {
+ type Counter: "counter",
}
/// Identifies a counter.
@@ -495,7 +491,7 @@ pub enum CounterKey {
Str(Str),
}
-cast_from_value! {
+cast! {
CounterKey,
v: Str => Self::Str(v),
label: Label => Self::Selector(Selector::Label(label)),
@@ -503,7 +499,7 @@ cast_from_value! {
if v == PageElem::func() {
Self::Page
} else {
- Self::Selector(LocatableSelector::cast(Value::from(v))?.0)
+ Self::Selector(LocatableSelector::from_value(v.into_value())?.0)
}
},
selector: LocatableSelector => Self::Selector(selector.0),
@@ -536,8 +532,8 @@ impl Debug for CounterUpdate {
}
}
-cast_from_value! {
- CounterUpdate: "counter update",
+cast! {
+ type CounterUpdate: "counter update",
v: CounterState => Self::Set(v),
v: Func => Self::Func(v),
}
@@ -559,10 +555,7 @@ impl CounterState {
CounterUpdate::Set(state) => *self = state,
CounterUpdate::Step(level) => self.step(level, 1),
CounterUpdate::Func(func) => {
- *self = func
- .call_vt(vt, self.0.iter().copied().map(Into::into))?
- .cast()
- .at(func.span())?
+ *self = func.call_vt(vt, self.0.iter().copied())?.cast().at(func.span())?
}
}
Ok(())
@@ -593,8 +586,9 @@ impl CounterState {
}
}
-cast_from_value! {
+cast! {
CounterState,
+ self => Value::Array(self.0.into_iter().map(IntoValue::into_value).collect()),
num: usize => Self(smallvec![num]),
array: Array => Self(array
.into_iter()
@@ -602,10 +596,6 @@ cast_from_value! {
.collect::<StrResult<_>>()?),
}
-cast_to_value! {
- v: CounterState => Value::Array(v.0.into_iter().map(Into::into).collect())
-}
-
/// Executes a display of a state.
///
/// Display: State
diff --git a/library/src/meta/document.rs b/library/src/meta/document.rs
index 1ce900ed..db036e0a 100644
--- a/library/src/meta/document.rs
+++ b/library/src/meta/document.rs
@@ -78,12 +78,9 @@ impl LayoutRoot for DocumentElem {
#[derive(Debug, Default, Clone, Hash)]
pub struct Author(Vec<EcoString>);
-cast_from_value! {
+cast! {
Author,
+ self => self.0.into_value(),
v: EcoString => Self(vec![v]),
v: Array => Self(v.into_iter().map(Value::cast).collect::<StrResult<_>>()?),
}
-
-cast_to_value! {
- v: Author => v.0.into()
-}
diff --git a/library/src/meta/figure.rs b/library/src/meta/figure.rs
index 67087832..0d218770 100644
--- a/library/src/meta/figure.rs
+++ b/library/src/meta/figure.rs
@@ -209,7 +209,7 @@ impl Synthesize for FigureElem {
};
let target = descendant.unwrap_or_else(|| self.body());
- supplement.resolve(vt, [target.into()])?
+ supplement.resolve(vt, [target])?
}
};
@@ -312,8 +312,8 @@ impl FigureElem {
self.counter(),
self.numbering(StyleChain::default()),
) {
- let numbers =
- counter.at(vt, self.0.location().unwrap())?.display(vt, &numbering)?;
+ let loc = self.0.location().unwrap();
+ let numbers = counter.at(vt, loc)?.display(vt, &numbering)?;
if !supplement.is_empty() {
supplement += TextElem::packed("\u{a0}");
@@ -335,19 +335,16 @@ pub enum FigureKind {
Name(EcoString),
}
-cast_from_value! {
+cast! {
FigureKind,
+ self => match self {
+ Self::Elem(v) => v.into_value(),
+ Self::Name(v) => v.into_value(),
+ },
v: ElemFunc => Self::Elem(v),
v: EcoString => Self::Name(v),
}
-cast_to_value! {
- v: FigureKind => match v {
- FigureKind::Elem(v) => v.into(),
- FigureKind::Name(v) => v.into(),
- }
-}
-
/// An element that can be auto-detected in a figure.
///
/// This trait is used to determine the type of a figure.
diff --git a/library/src/meta/footnote.rs b/library/src/meta/footnote.rs
index 22de91c3..1c95716c 100644
--- a/library/src/meta/footnote.rs
+++ b/library/src/meta/footnote.rs
@@ -211,7 +211,7 @@ impl Finalize for FootnoteEntry {
}
}
-cast_from_value! {
+cast! {
FootnoteElem,
v: Content => v.to::<Self>().cloned().unwrap_or_else(|| Self::new(v.clone())),
}
diff --git a/library/src/meta/heading.rs b/library/src/meta/heading.rs
index d6ad7044..7a333e36 100644
--- a/library/src/meta/heading.rs
+++ b/library/src/meta/heading.rs
@@ -104,9 +104,7 @@ impl Synthesize for HeadingElem {
let supplement = match self.supplement(styles) {
Smart::Auto => TextElem::packed(self.local_name_in(styles)),
Smart::Custom(None) => Content::empty(),
- Smart::Custom(Some(supplement)) => {
- supplement.resolve(vt, [self.clone().into()])?
- }
+ Smart::Custom(Some(supplement)) => supplement.resolve(vt, [self.clone()])?,
};
self.push_level(self.level(styles));
@@ -164,7 +162,7 @@ impl Count for HeadingElem {
}
}
-cast_from_value! {
+cast! {
HeadingElem,
v: Content => v.to::<Self>().ok_or("expected heading")?.clone(),
}
diff --git a/library/src/meta/link.rs b/library/src/meta/link.rs
index 93b9999d..43f6a34d 100644
--- a/library/src/meta/link.rs
+++ b/library/src/meta/link.rs
@@ -125,19 +125,16 @@ pub enum LinkTarget {
Label(Label),
}
-cast_from_value! {
+cast! {
LinkTarget,
+ self => match self {
+ Self::Dest(v) => v.into_value(),
+ Self::Label(v) => v.into_value(),
+ },
v: Destination => Self::Dest(v),
v: Label => Self::Label(v),
}
-cast_to_value! {
- v: LinkTarget => match v {
- LinkTarget::Dest(v) => v.into(),
- LinkTarget::Label(v) => v.into(),
- }
-}
-
impl From<Destination> for LinkTarget {
fn from(dest: Destination) -> Self {
Self::Dest(dest)
diff --git a/library/src/meta/mod.rs b/library/src/meta/mod.rs
index 724e5d20..dcac6379 100644
--- a/library/src/meta/mod.rs
+++ b/library/src/meta/mod.rs
@@ -42,14 +42,14 @@ pub(super) fn define(global: &mut Scope) {
global.define("footnote", FootnoteElem::func());
global.define("cite", CiteElem::func());
global.define("bibliography", BibliographyElem::func());
- global.define("locate", locate);
- global.define("style", style);
- global.define("layout", layout);
- global.define("counter", counter);
- global.define("numbering", numbering);
- global.define("state", state);
- global.define("query", query);
- global.define("selector", selector);
+ global.define("locate", locate_func());
+ global.define("style", style_func());
+ global.define("layout", layout_func());
+ global.define("counter", counter_func());
+ global.define("numbering", numbering_func());
+ global.define("state", state_func());
+ global.define("query", query_func());
+ global.define("selector", selector_func());
}
/// The named with which an element is referenced.
diff --git a/library/src/meta/numbering.rs b/library/src/meta/numbering.rs
index fbe7306c..ef0d0f70 100644
--- a/library/src/meta/numbering.rs
+++ b/library/src/meta/numbering.rs
@@ -32,14 +32,13 @@ use crate::text::Case;
///
/// Display: Numbering
/// Category: meta
-/// Returns: any
#[func]
pub fn numbering(
/// Defines how the numbering works.
///
- /// **Counting symbols** are `1`, `a`, `A`, `i`, `I`, `い`, `イ`,
- /// `א`, `가`, `ㄱ`, and `*`. They are replaced by the number in the sequence,
- /// in the given case.
+ /// **Counting symbols** are `1`, `a`, `A`, `i`, `I`, `い`, `イ`, `א`, `가`,
+ /// `ㄱ`, and `*`. They are replaced by the number in the sequence, in the
+ /// given case.
///
/// The `*` character means that symbols should be used to count, in the
/// order of `*`, `†`, `‡`, `§`, `¶`, and `‖`. If there are more than six
@@ -52,9 +51,9 @@ pub fn numbering(
/// suffixes. They are repeated as-is at in front of their rendered
/// equivalent of their counting symbol.
///
- /// This parameter can also be an arbitrary function that gets each number as
- /// an individual argument. When given a function, the `numbering` function
- /// just forwards the arguments to that function. While this is not
+ /// This parameter can also be an arbitrary function that gets each number
+ /// as an individual argument. When given a function, the `numbering`
+ /// function just forwards the arguments to that function. While this is not
/// particularly useful in itself, it means that you can just give arbitrary
/// numberings to the `numbering` function without caring whether they are
/// defined as a pattern or function.
@@ -65,8 +64,10 @@ pub fn numbering(
/// given, the last counting symbol with its prefix is repeated.
#[variadic]
numbers: Vec<usize>,
-) -> Value {
- numbering.apply_vm(vm, &numbers)?
+ /// The virtual machine.
+ vm: &mut Vm,
+) -> SourceResult<Value> {
+ numbering.apply_vm(vm, &numbers)
}
/// How to number a sequence of things.
@@ -84,8 +85,7 @@ impl Numbering {
Ok(match self {
Self::Pattern(pattern) => Value::Str(pattern.apply(numbers).into()),
Self::Func(func) => {
- let args =
- Args::new(func.span(), numbers.iter().map(|&n| Value::Int(n as i64)));
+ let args = Args::new(func.span(), numbers.iter().copied());
func.call_vm(vm, args)?
}
})
@@ -95,9 +95,7 @@ impl Numbering {
pub fn apply_vt(&self, vt: &mut Vt, numbers: &[usize]) -> SourceResult<Value> {
Ok(match self {
Self::Pattern(pattern) => Value::Str(pattern.apply(numbers).into()),
- Self::Func(func) => {
- func.call_vt(vt, numbers.iter().map(|&n| Value::Int(n as i64)))?
- }
+ Self::Func(func) => func.call_vt(vt, numbers.iter().copied())?,
})
}
@@ -116,19 +114,16 @@ impl From<NumberingPattern> for Numbering {
}
}
-cast_from_value! {
+cast! {
Numbering,
+ self => match self {
+ Self::Pattern(pattern) => pattern.into_value(),
+ Self::Func(func) => func.into_value(),
+ },
v: NumberingPattern => Self::Pattern(v),
v: Func => Self::Func(v),
}
-cast_to_value! {
- v: Numbering => match v {
- Numbering::Pattern(pattern) => pattern.into(),
- Numbering::Func(func) => func.into(),
- }
-}
-
/// How to turn a number into text.
///
/// A pattern consists of a prefix, followed by one of `1`, `a`, `A`, `i`,
@@ -230,15 +225,11 @@ impl FromStr for NumberingPattern {
}
}
-cast_from_value! {
+cast! {
NumberingPattern,
- v: Str => v.parse()?,
-}
-
-cast_to_value! {
- v: NumberingPattern => {
+ self => {
let mut pat = EcoString::new();
- for (prefix, kind, case) in &v.pieces {
+ for (prefix, kind, case) in &self.pieces {
pat.push_str(prefix);
let mut c = kind.to_char();
if *case == Case::Upper {
@@ -246,9 +237,10 @@ cast_to_value! {
}
pat.push(c);
}
- pat.push_str(&v.suffix);
- pat.into()
- }
+ pat.push_str(&self.suffix);
+ pat.into_value()
+ },
+ v: Str => v.parse()?,
}
/// Different kinds of numberings.
diff --git a/library/src/meta/outline.rs b/library/src/meta/outline.rs
index 089eceb2..894c3b8b 100644
--- a/library/src/meta/outline.rs
+++ b/library/src/meta/outline.rs
@@ -367,19 +367,14 @@ impl OutlineIndent {
// Length => indent with some fixed spacing per level
Some(Smart::Custom(OutlineIndent::Length(length))) => {
- let Ok(depth): Result<i64, _> = ancestors.len().try_into() else {
- bail!(span, "outline element depth too large");
- };
-
- let hspace = HElem::new(*length).pack().repeat(depth).unwrap();
- seq.push(hspace);
+ seq.push(HElem::new(*length).pack().repeat(ancestors.len()));
}
// Function => call function with the current depth and take
// the returned content
Some(Smart::Custom(OutlineIndent::Function(func))) => {
let depth = ancestors.len();
- let returned = func.call_vt(vt, [depth.into()])?;
+ let returned = func.call_vt(vt, [depth])?;
let Ok(returned) = returned.cast::<Content>() else {
bail!(
span,
@@ -396,17 +391,14 @@ impl OutlineIndent {
}
}
-cast_from_value! {
+cast! {
OutlineIndent,
- b: bool => OutlineIndent::Bool(b),
- s: Spacing => OutlineIndent::Length(s),
- f: Func => OutlineIndent::Function(f),
-}
-
-cast_to_value! {
- v: OutlineIndent => match v {
- OutlineIndent::Bool(b) => b.into(),
- OutlineIndent::Length(s) => s.into(),
- OutlineIndent::Function(f) => f.into()
- }
+ self => match self {
+ Self::Bool(v) => v.into_value(),
+ Self::Length(v) => v.into_value(),
+ Self::Function(v) => v.into_value()
+ },
+ v: bool => OutlineIndent::Bool(v),
+ v: Spacing => OutlineIndent::Length(v),
+ v: Func => OutlineIndent::Function(v),
}
diff --git a/library/src/meta/query.rs b/library/src/meta/query.rs
index ad7274f1..7f839f97 100644
--- a/library/src/meta/query.rs
+++ b/library/src/meta/query.rs
@@ -98,7 +98,6 @@ use crate::prelude::*;
///
/// Display: Query
/// Category: meta
-/// Returns: array
#[func]
pub fn query(
/// Can be an element function like a `heading` or `figure`, a `{<label>}`
@@ -111,7 +110,6 @@ pub fn query(
/// have an explicit label attached to them. This limitation will be
/// resolved in the future.
target: LocatableSelector,
-
/// Can be any location. Why is it required then? As noted before, Typst has
/// to evaluate parts of your code multiple times to determine the values of
/// all state. By only allowing this function within
@@ -120,14 +118,14 @@ pub fn query(
/// level of a module, the evaluation of the whole module and its exports
/// could depend on the query's result.
location: Location,
-) -> Value {
+ /// The virtual machine.
+ vm: &mut Vm,
+) -> Array {
let _ = location;
let vec = vm.vt.introspector.query(&target.0);
- Value::Array(
- vec.into_iter()
- .map(|elem| Value::Content(elem.into_inner()))
- .collect(),
- )
+ vec.into_iter()
+ .map(|elem| Value::Content(elem.into_inner()))
+ .collect()
}
/// Turns a value into a selector. The following values are accepted:
@@ -137,12 +135,11 @@ pub fn query(
///
/// Display: Selector
/// Category: meta
-/// Returns: content
#[func]
pub fn selector(
/// Can be an element function like a `heading` or `figure`, a `{<label>}`
/// or a more complex selector like `{heading.where(level: 1)}`.
target: Selector,
-) -> Value {
- target.into()
+) -> Selector {
+ target
}
diff --git a/library/src/meta/reference.rs b/library/src/meta/reference.rs
index c538b696..42450b97 100644
--- a/library/src/meta/reference.rs
+++ b/library/src/meta/reference.rs
@@ -193,7 +193,7 @@ impl Show for RefElem {
Smart::Auto => refable.supplement(),
Smart::Custom(None) => Content::empty(),
Smart::Custom(Some(supplement)) => {
- supplement.resolve(vt, [(*elem).clone().into()])?
+ supplement.resolve(vt, [(*elem).clone()])?
}
};
@@ -229,10 +229,10 @@ pub enum Supplement {
impl Supplement {
/// Tries to resolve the supplement into its content.
- pub fn resolve(
+ pub fn resolve<T: IntoValue>(
&self,
vt: &mut Vt,
- args: impl IntoIterator<Item = Value>,
+ args: impl IntoIterator<Item = T>,
) -> SourceResult<Content> {
Ok(match self {
Supplement::Content(content) => content.clone(),
@@ -241,19 +241,16 @@ impl Supplement {
}
}
-cast_from_value! {
+cast! {
Supplement,
+ self => match self {
+ Self::Content(v) => v.into_value(),
+ Self::Func(v) => v.into_value(),
+ },
v: Content => Self::Content(v),
v: Func => Self::Func(v),
}
-cast_to_value! {
- v: Supplement => match v {
- Supplement::Content(v) => v.into(),
- Supplement::Func(v) => v.into(),
- }
-}
-
/// Marks an element as being able to be referenced. This is used to implement
/// the `@ref` element.
pub trait Refable {
diff --git a/library/src/meta/state.rs b/library/src/meta/state.rs
index 284daf54..231852e3 100644
--- a/library/src/meta/state.rs
+++ b/library/src/meta/state.rs
@@ -233,7 +233,6 @@ use crate::prelude::*;
///
/// Display: State
/// Category: meta
-/// Returns: state
#[func]
pub fn state(
/// The key that identifies this state.
@@ -241,8 +240,8 @@ pub fn state(
/// The initial value of the state.
#[default]
init: Value,
-) -> Value {
- Value::dynamic(State { key, init })
+) -> State {
+ State { key, init }
}
/// A state.
@@ -265,10 +264,10 @@ impl State {
span: Span,
) -> SourceResult<Value> {
let value = match method {
- "display" => self.display(args.eat()?).into(),
+ "display" => self.display(args.eat()?).into_value(),
"at" => self.at(&mut vm.vt, args.expect("location")?)?,
"final" => self.final_(&mut vm.vt, args.expect("location")?)?,
- "update" => self.update(args.expect("value or function")?).into(),
+ "update" => self.update(args.expect("value or function")?).into_value(),
_ => bail!(span, "type state has no method `{}`", method),
};
args.finish()?;
@@ -284,10 +283,7 @@ impl State {
#[tracing::instrument(skip(self, vt))]
pub fn at(self, vt: &mut Vt, location: Location) -> SourceResult<Value> {
let sequence = self.sequence(vt)?;
- let offset = vt
- .introspector
- .query(&Selector::before(self.selector(), location, true))
- .len();
+ let offset = vt.introspector.query(&self.selector().before(location, true)).len();
Ok(sequence[offset].clone())
}
@@ -358,8 +354,8 @@ impl Debug for State {
}
}
-cast_from_value! {
- State: "state",
+cast! {
+ type State: "state",
}
/// An update to perform on a state.
@@ -377,8 +373,8 @@ impl Debug for StateUpdate {
}
}
-cast_from_value! {
- StateUpdate: "state update",
+cast! {
+ type StateUpdate: "state update",
v: Func => Self::Func(v),
v: Value => Self::Set(v),
}
diff --git a/library/src/prelude.rs b/library/src/prelude.rs
index 59b8a0a0..f0dda5c8 100644
--- a/library/src/prelude.rs
+++ b/library/src/prelude.rs
@@ -15,8 +15,8 @@ pub use typst::diag::{bail, error, At, SourceResult, StrResult};
pub use typst::doc::*;
#[doc(no_inline)]
pub use typst::eval::{
- array, cast_from_value, cast_to_value, dict, format_str, func, Args, Array, Cast,
- CastInfo, Dict, Func, Never, Scope, Str, Symbol, Value, Vm,
+ array, cast, dict, format_str, func, Args, Array, AutoValue, Cast, Dict, FromValue,
+ Func, IntoValue, Never, NoneValue, Scope, Str, Symbol, Type, Value, Vm,
};
#[doc(no_inline)]
pub use typst::geom::*;
diff --git a/library/src/text/deco.rs b/library/src/text/deco.rs
index cfb06956..329d3f85 100644
--- a/library/src/text/deco.rs
+++ b/library/src/text/deco.rs
@@ -251,8 +251,8 @@ impl Fold for Decoration {
}
}
-cast_from_value! {
- Decoration: "decoration",
+cast! {
+ type Decoration: "decoration",
}
/// A kind of decorative line.
diff --git a/library/src/text/misc.rs b/library/src/text/misc.rs
index 5dafe4ac..2af7a212 100644
--- a/library/src/text/misc.rs
+++ b/library/src/text/misc.rs
@@ -115,15 +115,12 @@ impl Show for StrongElem {
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Delta(pub i64);
-cast_from_value! {
+cast! {
Delta,
+ self => self.0.into_value(),
v: i64 => Self(v),
}
-cast_to_value! {
- v: Delta => v.0.into()
-}
-
impl Fold for Delta {
type Output = i64;
@@ -176,15 +173,12 @@ impl Show for EmphElem {
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Toggle;
-cast_from_value! {
+cast! {
Toggle,
+ self => Value::None,
_: Value => Self,
}
-cast_to_value! {
- _: Toggle => Value::None
-}
-
impl Fold for Toggle {
type Output = bool;
@@ -204,12 +198,11 @@ impl Fold for Toggle {
///
/// Display: Lowercase
/// Category: text
-/// Returns: string or content
#[func]
pub fn lower(
/// The text to convert to lowercase.
- text: ToCase,
-) -> Value {
+ text: Caseable,
+) -> Caseable {
case(text, Case::Lower)
}
@@ -224,31 +217,36 @@ pub fn lower(
///
/// Display: Uppercase
/// Category: text
-/// Returns: string or content
#[func]
pub fn upper(
/// The text to convert to uppercase.
- text: ToCase,
-) -> Value {
+ text: Caseable,
+) -> Caseable {
case(text, Case::Upper)
}
/// Change the case of text.
-fn case(text: ToCase, case: Case) -> Value {
+fn case(text: Caseable, case: Case) -> Caseable {
match text {
- ToCase::Str(v) => Value::Str(case.apply(&v).into()),
- ToCase::Content(v) => Value::Content(v.styled(TextElem::set_case(Some(case)))),
+ Caseable::Str(v) => Caseable::Str(case.apply(&v).into()),
+ Caseable::Content(v) => {
+ Caseable::Content(v.styled(TextElem::set_case(Some(case))))
+ }
}
}
/// A value whose case can be changed.
-enum ToCase {
+pub enum Caseable {
Str(Str),
Content(Content),
}
-cast_from_value! {
- ToCase,
+cast! {
+ Caseable,
+ self => match self {
+ Self::Str(v) => v.into_value(),
+ Self::Content(v) => v.into_value(),
+ },
v: Str => Self::Str(v),
v: Content => Self::Content(v),
}
@@ -297,13 +295,12 @@ impl Case {
///
/// Display: Small Capitals
/// Category: text
-/// Returns: content
#[func]
pub fn smallcaps(
/// The text to display to small capitals.
body: Content,
-) -> Value {
- Value::Content(body.styled(TextElem::set_smallcaps(true)))
+) -> Content {
+ body.styled(TextElem::set_smallcaps(true))
}
/// Create blind text.
@@ -324,11 +321,10 @@ pub fn smallcaps(
///
/// Display: Blind Text
/// Category: text
-/// Returns: string
#[func]
pub fn lorem(
/// The length of the blind text in words.
words: usize,
-) -> Value {
- Value::Str(lipsum::lipsum(words).replace("--", "–").into())
+) -> Str {
+ lipsum::lipsum(words).replace("--", "–").into()
}
diff --git a/library/src/text/mod.rs b/library/src/text/mod.rs
index e86bc168..6090094a 100644
--- a/library/src/text/mod.rs
+++ b/library/src/text/mod.rs
@@ -14,8 +14,6 @@ pub use self::raw::*;
pub use self::shaping::*;
pub use self::shift::*;
-use std::borrow::Cow;
-
use rustybuzz::Tag;
use typst::font::{FontMetrics, FontStretch, FontStyle, FontWeight, VerticalFontMetric};
@@ -29,16 +27,16 @@ pub(super) fn define(global: &mut Scope) {
global.define("smartquote", SmartQuoteElem::func());
global.define("strong", StrongElem::func());
global.define("emph", EmphElem::func());
- global.define("lower", lower);
- global.define("upper", upper);
- global.define("smallcaps", smallcaps);
+ global.define("lower", lower_func());
+ global.define("upper", upper_func());
+ global.define("smallcaps", smallcaps_func());
global.define("sub", SubElem::func());
global.define("super", SuperElem::func());
global.define("underline", UnderlineElem::func());
global.define("strike", StrikeElem::func());
global.define("overline", OverlineElem::func());
global.define("raw", RawElem::func());
- global.define("lorem", lorem);
+ global.define("lorem", lorem_func());
}
/// Customize the look and layout of text in a variety of ways.
@@ -560,15 +558,12 @@ impl Debug for FontFamily {
}
}
-cast_from_value! {
+cast! {
FontFamily,
+ self => self.0.into_value(),
string: EcoString => Self::new(&string),
}
-cast_to_value! {
- v: FontFamily => v.0.into()
-}
-
/// Font family fallback list.
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
pub struct FontList(pub Vec<FontFamily>);
@@ -582,20 +577,17 @@ impl IntoIterator for FontList {
}
}
-cast_from_value! {
+cast! {
FontList,
+ self => if self.0.len() == 1 {
+ self.0.into_iter().next().unwrap().0.into_value()
+ } else {
+ self.0.into_value()
+ },
family: FontFamily => Self(vec![family]),
values: Array => Self(values.into_iter().map(|v| v.cast()).collect::<StrResult<_>>()?),
}
-cast_to_value! {
- v: FontList => if v.0.len() == 1 {
- v.0.into_iter().next().unwrap().0.into()
- } else {
- v.0.into()
- }
-}
-
/// The size of text.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct TextSize(pub Length);
@@ -608,15 +600,12 @@ impl Fold for TextSize {
}
}
-cast_from_value! {
+cast! {
TextSize,
+ self => self.0.into_value(),
v: Length => Self(v),
}
-cast_to_value! {
- v: TextSize => v.0.into()
-}
-
/// Specifies the bottom or top edge of text.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum TextEdge {
@@ -636,25 +625,23 @@ impl TextEdge {
}
}
-cast_from_value! {
+cast! {
TextEdge,
+ self => match self {
+ Self::Metric(metric) => metric.into_value(),
+ Self::Length(length) => length.into_value(),
+ },
v: VerticalFontMetric => Self::Metric(v),
v: Length => Self::Length(v),
}
-cast_to_value! {
- v: TextEdge => match v {
- TextEdge::Metric(metric) => metric.into(),
- TextEdge::Length(length) => length.into(),
- }
-}
-
/// The direction of text and inline objects in their line.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct TextDir(pub Smart<Dir>);
-cast_from_value! {
+cast! {
TextDir,
+ self => self.0.into_value(),
v: Smart<Dir> => {
if v.map_or(false, |dir| dir.axis() == Axis::Y) {
Err("text direction must be horizontal")?;
@@ -663,10 +650,6 @@ cast_from_value! {
},
}
-cast_to_value! {
- v: TextDir => v.0.into()
-}
-
impl Resolve for TextDir {
type Output = Dir;
@@ -682,15 +665,12 @@ impl Resolve for TextDir {
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Hyphenate(pub Smart<bool>);
-cast_from_value! {
+cast! {
Hyphenate,
+ self => self.0.into_value(),
v: Smart<bool> => Self(v),
}
-cast_to_value! {
- v: Hyphenate => v.0.into()
-}
-
impl Resolve for Hyphenate {
type Output = bool;
@@ -718,18 +698,15 @@ impl StylisticSet {
}
}
-cast_from_value! {
+cast! {
StylisticSet,
+ self => self.0.into_value(),
v: i64 => match v {
1 ..= 20 => Self::new(v as u8),
_ => Err("stylistic set must be between 1 and 20")?,
},
}
-cast_to_value! {
- v: StylisticSet => v.0.into()
-}
-
/// Which kind of numbers / figures to select.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
pub enum NumberType {
@@ -754,8 +731,17 @@ pub enum NumberWidth {
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
pub struct FontFeatures(pub Vec<(Tag, u32)>);
-cast_from_value! {
+cast! {
FontFeatures,
+ self => self.0
+ .into_iter()
+ .map(|(tag, num)| {
+ let bytes = tag.to_bytes();
+ let key = std::str::from_utf8(&bytes).unwrap_or_default();
+ (key.into(), num.into_value())
+ })
+ .collect::<Dict>()
+ .into_value(),
values: Array => Self(values
.into_iter()
.map(|v| {
@@ -773,18 +759,6 @@ cast_from_value! {
.collect::<StrResult<_>>()?),
}
-cast_to_value! {
- v: FontFeatures => Value::Dict(
- v.0.into_iter()
- .map(|(tag, num)| {
- let bytes = tag.to_bytes();
- let key = std::str::from_utf8(&bytes).unwrap_or_default();
- (key.into(), num.into())
- })
- .collect(),
- )
-}
-
impl Fold for FontFeatures {
type Output = Self;
diff --git a/library/src/text/shaping.rs b/library/src/text/shaping.rs
index 3610e0c0..5a1724ab 100644
--- a/library/src/text/shaping.rs
+++ b/library/src/text/shaping.rs
@@ -1,13 +1,14 @@
+use std::borrow::Cow;
use std::ops::Range;
use std::str::FromStr;
use az::SaturatingAs;
use rustybuzz::{Feature, Tag, UnicodeBuffer};
-use typst::font::{Font, FontVariant};
+use typst::font::{Font, FontStyle, FontVariant};
use typst::util::SliceExt;
use unicode_script::{Script, UnicodeScript};
-use super::*;
+use super::{decorate, FontFamily, NumberType, NumberWidth, TextElem};
use crate::layout::SpanMapper;
use crate::prelude::*;
diff --git a/library/src/visualize/mod.rs b/library/src/visualize/mod.rs
index 3adf4e77..ea873f44 100644
--- a/library/src/visualize/mod.rs
+++ b/library/src/visualize/mod.rs
@@ -24,4 +24,22 @@ pub(super) fn define(global: &mut Scope) {
global.define("circle", CircleElem::func());
global.define("polygon", PolygonElem::func());
global.define("path", PathElem::func());
+ global.define("black", Color::BLACK);
+ global.define("gray", Color::GRAY);
+ global.define("silver", Color::SILVER);
+ global.define("white", Color::WHITE);
+ global.define("navy", Color::NAVY);
+ global.define("blue", Color::BLUE);
+ global.define("aqua", Color::AQUA);
+ global.define("teal", Color::TEAL);
+ global.define("eastern", Color::EASTERN);
+ global.define("purple", Color::PURPLE);
+ global.define("fuchsia", Color::FUCHSIA);
+ global.define("maroon", Color::MAROON);
+ global.define("red", Color::RED);
+ global.define("orange", Color::ORANGE);
+ global.define("yellow", Color::YELLOW);
+ global.define("olive", Color::OLIVE);
+ global.define("green", Color::GREEN);
+ global.define("lime", Color::LIME);
}
diff --git a/library/src/visualize/path.rs b/library/src/visualize/path.rs
index c9b596c7..641095c5 100644
--- a/library/src/visualize/path.rs
+++ b/library/src/visualize/path.rs
@@ -1,6 +1,9 @@
-use self::PathVertex::{AllControlPoints, MirroredControlPoint, Vertex};
-use crate::prelude::*;
use kurbo::{CubicBez, ParamCurveExtrema};
+use typst::eval::Reflect;
+
+use crate::prelude::*;
+
+use PathVertex::{AllControlPoints, MirroredControlPoint, Vertex};
/// A path through a list of points, connected by Bezier curves.
///
@@ -179,8 +182,13 @@ impl PathVertex {
}
}
-cast_from_value! {
+cast! {
PathVertex,
+ self => match self {
+ Vertex(x) => x.into_value(),
+ MirroredControlPoint(x, c) => array![x, c].into_value(),
+ AllControlPoints(x, c1, c2) => array![x, c1, c2].into_value(),
+ },
array: Array => {
let mut iter = array.into_iter();
match (iter.next(), iter.next(), iter.next(), iter.next()) {
@@ -188,7 +196,7 @@ cast_from_value! {
Vertex(a.cast()?)
},
(Some(a), Some(b), None, None) => {
- if Axes::<Rel<Length>>::is(&a) {
+ if Axes::<Rel<Length>>::castable(&a) {
MirroredControlPoint(a.cast()?, b.cast()?)
} else {
Vertex(Axes::new(a.cast()?, b.cast()?))
@@ -201,19 +209,3 @@ cast_from_value! {
}
},
}
-
-cast_to_value! {
- v: PathVertex => {
- match v {
- PathVertex::Vertex(x) => {
- Value::from(x)
- },
- PathVertex::MirroredControlPoint(x, c) => {
- Value::Array(array![x, c])
- },
- PathVertex::AllControlPoints(x, c1, c2) => {
- Value::Array(array![x, c1, c2])
- },
- }
- }
-}