summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorT0mstone <39707032+T0mstone@users.noreply.github.com>2023-10-02 20:28:19 +0200
committerGitHub <noreply@github.com>2023-10-02 20:28:19 +0200
commitcf9bde3245817e068c8253070e7a94dcc05369fb (patch)
treeec6bcd27a9e38d307015f9c3d5a94acc4379a468 /crates
parent34ebbaeb1082b82b4f38d0b8da9a07675127ed79 (diff)
Add capability to get current compiler version (#2016)
Diffstat (limited to 'crates')
-rw-r--r--crates/typst-cli/src/main.rs1
-rw-r--r--crates/typst-library/src/compute/foundations.rs3
-rw-r--r--crates/typst-library/src/compute/mod.rs2
-rw-r--r--crates/typst-library/src/compute/sys.rs24
-rw-r--r--crates/typst/src/eval/array.rs3
-rw-r--r--crates/typst/src/eval/fields.rs9
-rw-r--r--crates/typst/src/eval/mod.rs2
-rw-r--r--crates/typst/src/eval/ops.rs2
-rw-r--r--crates/typst/src/eval/str.rs3
-rw-r--r--crates/typst/src/eval/value.rs10
-rw-r--r--crates/typst/src/eval/version.rs199
11 files changed, 252 insertions, 6 deletions
diff --git a/crates/typst-cli/src/main.rs b/crates/typst-cli/src/main.rs
index c5cfd514..bab1a07b 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 dad05717..250a9f57 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::<Regex>();
global.define_type::<Datetime>();
global.define_type::<Duration>();
+ global.define_type::<Version>();
global.define_type::<Plugin>();
global.define_func::<repr>();
global.define_func::<panic>();
diff --git a/crates/typst-library/src/compute/mod.rs b/crates/typst-library/src/compute/mod.rs
index 9e897653..f1af24c5 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 00000000..6404e625
--- /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::<u32>().unwrap(),
+ env!("CARGO_PKG_VERSION_MINOR").parse::<u32>().unwrap(),
+ env!("CARGO_PKG_VERSION_PATCH").parse::<u32>().unwrap(),
+ ]),
+ );
+ Module::new("sys", scope)
+}
diff --git a/crates/typst/src/eval/array.rs b/crates/typst/src/eval/array.rs
index 8e9e5f27..3da7c0b0 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 7d164497..37e490e6 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<Value> {
// 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::<Length>() {
+ if ty == Type::of::<Version>() {
+ &Version::COMPONENTS
+ } else if ty == Type::of::<Length>() {
&["em", "abs"]
} else if ty == Type::of::<Rel>() {
&["ratio", "length"]
diff --git a/crates/typst/src/eval/mod.rs b/crates/typst/src/eval/mod.rs
index 424e15ee..f0d1be20 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 23a32416..9280b6c6 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<Ordering> {
(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 6684389b..3a8d4730 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 69edcdec..c53f2df4 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::<Fr>(),
Self::Color(_) => Type::of::<Color>(),
Self::Symbol(_) => Type::of::<Symbol>(),
+ Self::Version(_) => Type::of::<Version>(),
Self::Str(_) => Type::of::<Str>(),
Self::Bytes(_) => Type::of::<Bytes>(),
Self::Label(_) => Type::of::<Label>(),
@@ -149,6 +152,7 @@ impl Value {
pub fn field(&self, field: &str) -> StrResult<Value> {
match self {
Self::Symbol(symbol) => symbol.clone().modified(field).map(Self::Symbol),
+ Self::Version(version) => version.component(field).map(Self::Int),
Self::Dict(dict) => dict.get(field).cloned(),
Self::Content(content) => content.get(field),
Self::Type(ty) => ty.field(field).cloned(),
@@ -199,6 +203,7 @@ impl Value {
Self::Int(v) => item!(text)(eco_format!("{v}")),
Self::Float(v) => item!(text)(eco_format!("{v}")),
Self::Str(v) => item!(text)(v.into()),
+ Self::Version(v) => item!(text)(eco_format!("{v}")),
Self::Symbol(v) => item!(text)(v.get().into()),
Self::Content(v) => v,
Self::Module(module) => module.content(),
@@ -231,6 +236,7 @@ impl Debug for Value {
Self::Fraction(v) => Debug::fmt(v, f),
Self::Color(v) => Debug::fmt(v, f),
Self::Symbol(v) => Debug::fmt(v, f),
+ Self::Version(v) => Debug::fmt(v, f),
Self::Str(v) => Debug::fmt(v, f),
Self::Bytes(v) => Debug::fmt(v, f),
Self::Label(v) => Debug::fmt(v, f),
@@ -278,6 +284,7 @@ impl Hash for Value {
Self::Fraction(v) => v.hash(state),
Self::Color(v) => v.hash(state),
Self::Symbol(v) => v.hash(state),
+ Self::Version(v) => v.hash(state),
Self::Str(v) => v.hash(state),
Self::Bytes(v) => v.hash(state),
Self::Label(v) => v.hash(state),
@@ -582,6 +589,7 @@ primitive! { Rel<Length>: "relative length",
primitive! { Fr: "fraction", Fraction }
primitive! { Color: "color", Color }
primitive! { Symbol: "symbol", Symbol }
+primitive! { Version: "version", Version }
primitive! {
Str: "string",
Str,
diff --git a/crates/typst/src/eval/version.rs b/crates/typst/src/eval/version.rs
new file mode 100644
index 00000000..6ba0dff4
--- /dev/null
+++ b/crates/typst/src/eval/version.rs
@@ -0,0 +1,199 @@
+use std::cmp::Ordering;
+use std::fmt::{self, Debug, Display, Formatter, Write};
+use std::hash::Hash;
+use std::iter::repeat;
+
+use ecow::{eco_format, EcoVec};
+
+use super::{cast, func, scope, ty};
+use crate::diag::{bail, error, StrResult};
+use crate::util::pretty_array_like;
+
+/// A version, with any number of components.
+///
+/// The list of components is semantically extended by an infinite list of
+/// zeros. This means that, for example, `0.8` is the same as `0.8.0`. As a
+/// special case, the empty version (that has no components at all) is the same
+/// as `0`, `0.0`, `0.0.0`, and so on.
+///
+/// The first three components have names: `major`, `minor`, `patch`. All
+/// components after that do not have names.
+#[ty(scope)]
+#[derive(Default, Clone, Hash)]
+#[allow(clippy::derived_hash_with_manual_eq)]
+pub struct Version(EcoVec<u32>);
+
+impl Version {
+ /// The names for the first components of a version.
+ pub const COMPONENTS: [&'static str; 3] = ["major", "minor", "patch"];
+
+ /// Create a new (empty) version.
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Get a named component of a version.
+ ///
+ /// Always non-negative. Returns `0` if the version isn't specified to the
+ /// necessary length.
+ pub fn component(&self, name: &str) -> StrResult<i64> {
+ self.0
+ .iter()
+ .zip(Self::COMPONENTS)
+ .find_map(|(&i, s)| (s == name).then_some(i as i64))
+ .ok_or_else(|| error!("unknown version component"))
+ }
+
+ /// Push a component to the end of this version.
+ pub fn push(&mut self, component: u32) {
+ self.0.push(component);
+ }
+
+ /// The values of the version
+ pub fn values(&self) -> &[u32] {
+ &self.0
+ }
+}
+
+#[scope]
+impl Version {
+ /// Creates a new version.
+ ///
+ /// It can have any number of components (even zero).
+ ///
+ /// ```example
+ /// #version() \
+ /// #version(1) \
+ /// #version(1, 2, 3, 4) \
+ /// #version((1, 2, 3, 4)) \
+ /// #version((1, 2), 3)
+ /// ```
+ #[func(constructor)]
+ pub fn construct(
+ /// The components of the version (array arguments are flattened)
+ #[variadic]
+ components: Vec<VersionComponents>,
+ ) -> Version {
+ let mut version = Version::new();
+ for c in components {
+ match c {
+ VersionComponents::Single(v) => version.push(v),
+ VersionComponents::Multiple(values) => {
+ for v in values {
+ version.push(v);
+ }
+ }
+ }
+ }
+ version
+ }
+
+ /// Get a component of a version.
+ ///
+ /// Always non-negative. Returns `0` if the version isn't specified to the
+ /// necessary length.
+ #[func]
+ pub fn at(
+ &self,
+ /// The index at which to retrieve the component. If negative, indexes
+ /// from the back of the explicitly given components.
+ index: i64,
+ ) -> StrResult<i64> {
+ let mut index = index;
+ if index < 0 {
+ match (self.0.len() as i64).checked_add(index) {
+ Some(pos_index) if pos_index >= 0 => index = pos_index,
+ _ => bail!(
+ "component index out of bounds (index: {index}, len: {})",
+ self.0.len()
+ ),
+ }
+ }
+ Ok(usize::try_from(index)
+ .ok()
+ .and_then(|i| self.0.get(i).copied())
+ .unwrap_or_default() as i64)
+ }
+}
+
+impl FromIterator<u32> for Version {
+ fn from_iter<T: IntoIterator<Item = u32>>(iter: T) -> Self {
+ Self(EcoVec::from_iter(iter))
+ }
+}
+
+impl IntoIterator for Version {
+ type Item = u32;
+ type IntoIter = ecow::vec::IntoIter<u32>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.0.into_iter()
+ }
+}
+
+impl Ord for Version {
+ fn cmp(&self, other: &Self) -> Ordering {
+ let max_len = self.0.len().max(other.0.len());
+ let tail = repeat(&0);
+
+ let self_iter = self.0.iter().chain(tail.clone());
+ let other_iter = other.0.iter().chain(tail);
+
+ for (l, r) in self_iter.zip(other_iter).take(max_len) {
+ match l.cmp(r) {
+ Ordering::Equal => (),
+ ord => return ord,
+ }
+ }
+
+ Ordering::Equal
+ }
+}
+
+impl PartialOrd for Version {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Eq for Version {}
+
+impl PartialEq for Version {
+ fn eq(&self, other: &Self) -> bool {
+ matches!(self.cmp(other), Ordering::Equal)
+ }
+}
+
+impl Display for Version {
+ fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
+ let mut first = true;
+ for &v in &self.0 {
+ if !first {
+ f.write_char('.')?;
+ }
+ write!(f, "{v}")?;
+ first = false;
+ }
+ Ok(())
+ }
+}
+
+impl Debug for Version {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ f.write_str("version")?;
+ let parts: Vec<_> = self.0.iter().map(|v| eco_format!("{v}")).collect();
+ f.write_str(&pretty_array_like(&parts, false))
+ }
+}
+
+/// One or multiple version components.
+pub enum VersionComponents {
+ Single(u32),
+ Multiple(Vec<u32>),
+}
+
+cast! {
+ VersionComponents,
+ v: u32 => Self::Single(v),
+ v: Vec<u32> => Self::Multiple(v)
+}