typst_library/foundations/
mod.rs

1//! Foundational types and functions.
2
3pub mod calc;
4pub mod ops;
5pub mod repr;
6pub mod sys;
7
8mod args;
9mod array;
10mod auto;
11mod bool;
12mod bytes;
13mod cast;
14mod content;
15mod context;
16mod datetime;
17mod decimal;
18mod dict;
19mod duration;
20mod element;
21mod fields;
22mod float;
23mod func;
24mod int;
25mod label;
26mod module;
27mod none;
28#[path = "plugin.rs"]
29mod plugin_;
30mod scope;
31mod selector;
32mod str;
33mod styles;
34mod symbol;
35#[path = "target.rs"]
36mod target_;
37mod ty;
38mod value;
39mod version;
40
41pub use self::args::*;
42pub use self::array::*;
43pub use self::auto::*;
44pub use self::bytes::*;
45pub use self::cast::*;
46pub use self::content::*;
47pub use self::context::*;
48pub use self::datetime::*;
49pub use self::decimal::*;
50pub use self::dict::*;
51pub use self::duration::*;
52pub use self::element::*;
53pub use self::fields::*;
54pub use self::float::*;
55pub use self::func::*;
56pub use self::int::*;
57pub use self::label::*;
58pub use self::module::*;
59pub use self::none::*;
60pub use self::plugin_::*;
61pub use self::repr::Repr;
62pub use self::scope::*;
63pub use self::selector::*;
64pub use self::str::*;
65pub use self::styles::*;
66pub use self::symbol::*;
67pub use self::target_::*;
68pub use self::ty::*;
69pub use self::value::*;
70pub use self::version::*;
71pub use typst_macros::{scope, ty};
72
73#[rustfmt::skip]
74#[doc(hidden)]
75pub use {
76    ecow::{eco_format, eco_vec},
77    indexmap::IndexMap,
78};
79
80use ecow::EcoString;
81use typst_syntax::Spanned;
82
83use crate::diag::{bail, SourceResult, StrResult};
84use crate::engine::Engine;
85use crate::routines::EvalMode;
86use crate::{Feature, Features};
87
88/// Hook up all `foundations` definitions.
89pub(super) fn define(global: &mut Scope, inputs: Dict, features: &Features) {
90    global.start_category(crate::Category::Foundations);
91    global.define_type::<bool>();
92    global.define_type::<i64>();
93    global.define_type::<f64>();
94    global.define_type::<Str>();
95    global.define_type::<Label>();
96    global.define_type::<Bytes>();
97    global.define_type::<Content>();
98    global.define_type::<Array>();
99    global.define_type::<Dict>();
100    global.define_type::<Func>();
101    global.define_type::<Args>();
102    global.define_type::<Type>();
103    global.define_type::<Module>();
104    global.define_type::<Regex>();
105    global.define_type::<Selector>();
106    global.define_type::<Datetime>();
107    global.define_type::<Decimal>();
108    global.define_type::<Symbol>();
109    global.define_type::<Duration>();
110    global.define_type::<Version>();
111    global.define_func::<repr::repr>();
112    global.define_func::<panic>();
113    global.define_func::<assert>();
114    global.define_func::<eval>();
115    global.define_func::<plugin>();
116    if features.is_enabled(Feature::Html) {
117        global.define_func::<target>();
118    }
119    global.define("calc", calc::module());
120    global.define("sys", sys::module(inputs));
121    global.reset_category();
122}
123
124/// Fails with an error.
125///
126/// Arguments are displayed to the user (not rendered in the document) as
127/// strings, converting with `repr` if necessary.
128///
129/// # Example
130/// The code below produces the error `panicked with: "this is wrong"`.
131/// ```typ
132/// #panic("this is wrong")
133/// ```
134#[func(keywords = ["error"])]
135pub fn panic(
136    /// The values to panic with and display to the user.
137    #[variadic]
138    values: Vec<Value>,
139) -> StrResult<Never> {
140    let mut msg = EcoString::from("panicked");
141    if !values.is_empty() {
142        msg.push_str(" with: ");
143        for (i, value) in values.iter().enumerate() {
144            if i > 0 {
145                msg.push_str(", ");
146            }
147            msg.push_str(&value.repr());
148        }
149    }
150    Err(msg)
151}
152
153/// Ensures that a condition is fulfilled.
154///
155/// Fails with an error if the condition is not fulfilled. Does not
156/// produce any output in the document.
157///
158/// If you wish to test equality between two values, see
159/// [`assert.eq`]($assert.eq) and [`assert.ne`]($assert.ne).
160///
161/// # Example
162/// ```typ
163/// #assert(1 < 2, message: "math broke")
164/// ```
165#[func(scope)]
166pub fn assert(
167    /// The condition that must be true for the assertion to pass.
168    condition: bool,
169    /// The error message when the assertion fails.
170    #[named]
171    message: Option<EcoString>,
172) -> StrResult<NoneValue> {
173    if !condition {
174        if let Some(message) = message {
175            bail!("assertion failed: {message}");
176        } else {
177            bail!("assertion failed");
178        }
179    }
180    Ok(NoneValue)
181}
182
183#[scope]
184impl assert {
185    /// Ensures that two values are equal.
186    ///
187    /// Fails with an error if the first value is not equal to the second. Does not
188    /// produce any output in the document.
189    ///
190    /// ```typ
191    /// #assert.eq(10, 10)
192    /// ```
193    #[func(title = "Assert Equal")]
194    pub fn eq(
195        /// The first value to compare.
196        left: Value,
197        /// The second value to compare.
198        right: Value,
199        /// An optional message to display on error instead of the representations
200        /// of the compared values.
201        #[named]
202        message: Option<EcoString>,
203    ) -> StrResult<NoneValue> {
204        if left != right {
205            if let Some(message) = message {
206                bail!("equality assertion failed: {message}");
207            } else {
208                bail!(
209                    "equality assertion failed: value {} was not equal to {}",
210                    left.repr(),
211                    right.repr()
212                );
213            }
214        }
215        Ok(NoneValue)
216    }
217
218    /// Ensures that two values are not equal.
219    ///
220    /// Fails with an error if the first value is equal to the second. Does not
221    /// produce any output in the document.
222    ///
223    /// ```typ
224    /// #assert.ne(3, 4)
225    /// ```
226    #[func(title = "Assert Not Equal")]
227    pub fn ne(
228        /// The first value to compare.
229        left: Value,
230        /// The second value to compare.
231        right: Value,
232        /// An optional message to display on error instead of the representations
233        /// of the compared values.
234        #[named]
235        message: Option<EcoString>,
236    ) -> StrResult<NoneValue> {
237        if left == right {
238            if let Some(message) = message {
239                bail!("inequality assertion failed: {message}");
240            } else {
241                bail!(
242                    "inequality assertion failed: value {} was equal to {}",
243                    left.repr(),
244                    right.repr()
245                );
246            }
247        }
248        Ok(NoneValue)
249    }
250}
251
252/// Evaluates a string as Typst code.
253///
254/// This function should only be used as a last resort.
255///
256/// # Example
257/// ```example
258/// #eval("1 + 1") \
259/// #eval("(1, 2, 3, 4)").len() \
260/// #eval("*Markup!*", mode: "markup") \
261/// ```
262#[func(title = "Evaluate")]
263pub fn eval(
264    engine: &mut Engine,
265    /// A string of Typst code to evaluate.
266    source: Spanned<String>,
267    /// The [syntactical mode]($reference/syntax/#modes) in which the string is
268    /// parsed.
269    ///
270    /// ```example
271    /// #eval("= Heading", mode: "markup")
272    /// #eval("1_2^3", mode: "math")
273    /// ```
274    #[named]
275    #[default(EvalMode::Code)]
276    mode: EvalMode,
277    /// A scope of definitions that are made available.
278    ///
279    /// ```example
280    /// #eval("x + 1", scope: (x: 2)) \
281    /// #eval(
282    ///   "abc/xyz",
283    ///   mode: "math",
284    ///   scope: (
285    ///     abc: $a + b + c$,
286    ///     xyz: $x + y + z$,
287    ///   ),
288    /// )
289    /// ```
290    #[named]
291    #[default]
292    scope: Dict,
293) -> SourceResult<Value> {
294    let Spanned { v: text, span } = source;
295    let dict = scope;
296    let mut scope = Scope::new();
297    for (key, value) in dict {
298        scope.bind(key.into(), Binding::new(value, span));
299    }
300    (engine.routines.eval_string)(engine.routines, engine.world, &text, span, mode, scope)
301}