diff --git a/crates/typst-cli/src/main.rs b/crates/typst-cli/src/main.rs index c5cfd5145..bab1a07b9 100644 --- a/crates/typst-cli/src/main.rs +++ b/crates/typst-cli/src/main.rs @@ -11,7 +11,6 @@ mod watch; mod world; use std::cell::Cell; -use std::env; use std::io::{self, IsTerminal, Write}; use std::process::ExitCode; diff --git a/crates/typst-library/src/compute/foundations.rs b/crates/typst-library/src/compute/foundations.rs index dad05717d..250a9f577 100644 --- a/crates/typst-library/src/compute/foundations.rs +++ b/crates/typst-library/src/compute/foundations.rs @@ -1,5 +1,5 @@ use typst::eval::{ - Datetime, Duration, EvalMode, Module, Never, NoneValue, Plugin, Regex, + Datetime, Duration, EvalMode, Module, Never, NoneValue, Plugin, Regex, Version, }; use crate::prelude::*; @@ -22,6 +22,7 @@ pub(super) fn define(global: &mut Scope) { global.define_type::(); global.define_type::(); global.define_type::(); + global.define_type::(); global.define_type::(); global.define_func::(); global.define_func::(); diff --git a/crates/typst-library/src/compute/mod.rs b/crates/typst-library/src/compute/mod.rs index 9e8976532..f1af24c55 100644 --- a/crates/typst-library/src/compute/mod.rs +++ b/crates/typst-library/src/compute/mod.rs @@ -1,6 +1,7 @@ //! Computational functions. pub mod calc; +pub mod sys; mod data; mod foundations; @@ -15,4 +16,5 @@ pub(super) fn define(global: &mut Scope) { self::foundations::define(global); self::data::define(global); self::calc::define(global); + self::sys::define(global); } diff --git a/crates/typst-library/src/compute/sys.rs b/crates/typst-library/src/compute/sys.rs new file mode 100644 index 000000000..6404e6252 --- /dev/null +++ b/crates/typst-library/src/compute/sys.rs @@ -0,0 +1,24 @@ +//! System-related things. + +use typst::eval::{Module, Scope, Version}; + +/// Hook up all calculation definitions. +pub(super) fn define(global: &mut Scope) { + global.category("sys"); + global.define_module(module()); +} + +/// A module with system-related things. +fn module() -> Module { + let mut scope = Scope::deduplicating(); + scope.category("sys"); + scope.define( + "version", + Version::from_iter([ + env!("CARGO_PKG_VERSION_MAJOR").parse::().unwrap(), + env!("CARGO_PKG_VERSION_MINOR").parse::().unwrap(), + env!("CARGO_PKG_VERSION_PATCH").parse::().unwrap(), + ]), + ); + Module::new("sys", scope) +} diff --git a/crates/typst/src/eval/array.rs b/crates/typst/src/eval/array.rs index 8e9e5f27f..3da7c0b01 100644 --- a/crates/typst/src/eval/array.rs +++ b/crates/typst/src/eval/array.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use super::{ cast, func, ops, scope, ty, Args, Bytes, CastInfo, FromValue, Func, IntoValue, - Reflect, Value, Vm, + Reflect, Value, Version, Vm, }; use crate::diag::{At, SourceResult, StrResult}; use crate::eval::ops::{add, mul}; @@ -804,6 +804,7 @@ cast! { ToArray, v: Bytes => Self(v.iter().map(|&b| Value::Int(b.into())).collect()), v: Array => Self(v), + v: Version => Self(v.values().iter().map(|&v| Value::Int(v as i64)).collect()) } impl Debug for Array { diff --git a/crates/typst/src/eval/fields.rs b/crates/typst/src/eval/fields.rs index 7d1644975..37e490e66 100644 --- a/crates/typst/src/eval/fields.rs +++ b/crates/typst/src/eval/fields.rs @@ -1,6 +1,7 @@ use ecow::{eco_format, EcoString}; use crate::diag::StrResult; +use crate::eval::Version; use crate::geom::{Align, Length, Rel, Stroke}; use super::{IntoValue, Type, Value}; @@ -16,6 +17,10 @@ pub(crate) fn field(value: &Value, field: &str) -> StrResult { // Special cases, such as module and dict, are handled by Value itself let result = match value { + Value::Version(version) => match version.component(field) { + Ok(i) => i.into_value(), + Err(_) => return missing(), + }, Value::Length(length) => match field { "em" => length.em.get().into_value(), "abs" => length.abs.into_value(), @@ -69,7 +74,9 @@ fn missing_field(ty: Type, field: &str) -> EcoString { /// List the available fields for a type. pub fn fields_on(ty: Type) -> &'static [&'static str] { - if ty == Type::of::() { + if ty == Type::of::() { + &Version::COMPONENTS + } else if ty == Type::of::() { &["em", "abs"] } else if ty == Type::of::() { &["ratio", "length"] diff --git a/crates/typst/src/eval/mod.rs b/crates/typst/src/eval/mod.rs index 424e15ee1..f0d1be20e 100644 --- a/crates/typst/src/eval/mod.rs +++ b/crates/typst/src/eval/mod.rs @@ -31,6 +31,7 @@ mod scope; mod symbol; mod tracer; mod ty; +mod version; #[doc(hidden)] pub use { @@ -65,6 +66,7 @@ pub use self::symbol::{symbols, Symbol}; pub use self::tracer::Tracer; pub use self::ty::{scope, ty, NativeType, NativeTypeData, Type}; pub use self::value::{Dynamic, Value}; +pub use self::version::Version; use std::collections::HashSet; use std::mem; diff --git a/crates/typst/src/eval/ops.rs b/crates/typst/src/eval/ops.rs index 23a324167..9280b6c63 100644 --- a/crates/typst/src/eval/ops.rs +++ b/crates/typst/src/eval/ops.rs @@ -366,6 +366,7 @@ pub fn equal(lhs: &Value, rhs: &Value) -> bool { (Fraction(a), Fraction(b)) => a == b, (Color(a), Color(b)) => a == b, (Symbol(a), Symbol(b)) => a == b, + (Version(a), Version(b)) => a == b, (Str(a), Str(b)) => a == b, (Bytes(a), Bytes(b)) => a == b, (Label(a), Label(b)) => a == b, @@ -408,6 +409,7 @@ pub fn compare(lhs: &Value, rhs: &Value) -> StrResult { (Ratio(a), Ratio(b)) => a.cmp(b), (Relative(a), Relative(b)) => try_cmp_values(a, b)?, (Fraction(a), Fraction(b)) => a.cmp(b), + (Version(a), Version(b)) => a.cmp(b), (Str(a), Str(b)) => a.cmp(b), // Some technically different things should be comparable. diff --git a/crates/typst/src/eval/str.rs b/crates/typst/src/eval/str.rs index 6684389b1..3a8d47302 100644 --- a/crates/typst/src/eval/str.rs +++ b/crates/typst/src/eval/str.rs @@ -9,7 +9,7 @@ use unicode_segmentation::UnicodeSegmentation; use super::{ cast, dict, func, scope, ty, Args, Array, Bytes, Dict, Func, IntoValue, Type, Value, - Vm, + Version, Vm, }; use crate::diag::{bail, At, SourceResult, StrResult}; use crate::geom::Align; @@ -605,6 +605,7 @@ cast! { ToStr, v: i64 => Self::Int(v), v: f64 => Self::Str(format_str!("{}", v)), + v: Version => Self::Str(format_str!("{}", v)), v: Bytes => Self::Str( std::str::from_utf8(&v) .map_err(|_| "bytes are not valid utf-8")? diff --git a/crates/typst/src/eval/value.rs b/crates/typst/src/eval/value.rs index 69edcdec1..c53f2df4d 100644 --- a/crates/typst/src/eval/value.rs +++ b/crates/typst/src/eval/value.rs @@ -14,7 +14,7 @@ use typst::eval::Duration; use super::{ fields, format_str, ops, Args, Array, AutoValue, Bytes, CastInfo, Content, Dict, FromValue, Func, IntoValue, Module, NativeType, NoneValue, Plugin, Reflect, Scope, - Str, Symbol, Type, + Str, Symbol, Type, Version, }; use crate::diag::StrResult; use crate::eval::Datetime; @@ -50,6 +50,8 @@ pub enum Value { Color(Color), /// A symbol: `arrow.l`. Symbol(Symbol), + /// A version. + Version(Version), /// A string: `"string"`. Str(Str), /// Raw bytes. @@ -122,6 +124,7 @@ impl Value { Self::Fraction(_) => Type::of::(), Self::Color(_) => Type::of::(), Self::Symbol(_) => Type::of::(), + Self::Version(_) => Type::of::(), Self::Str(_) => Type::of::(), Self::Bytes(_) => Type::of::(), Self::Label(_) => Type::of::