mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Add capability to get current compiler version (#2016)
This commit is contained in:
parent
34ebbaeb10
commit
cf9bde3245
@ -11,7 +11,6 @@ mod watch;
|
|||||||
mod world;
|
mod world;
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::env;
|
|
||||||
use std::io::{self, IsTerminal, Write};
|
use std::io::{self, IsTerminal, Write};
|
||||||
use std::process::ExitCode;
|
use std::process::ExitCode;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use typst::eval::{
|
use typst::eval::{
|
||||||
Datetime, Duration, EvalMode, Module, Never, NoneValue, Plugin, Regex,
|
Datetime, Duration, EvalMode, Module, Never, NoneValue, Plugin, Regex, Version,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
@ -22,6 +22,7 @@ pub(super) fn define(global: &mut Scope) {
|
|||||||
global.define_type::<Regex>();
|
global.define_type::<Regex>();
|
||||||
global.define_type::<Datetime>();
|
global.define_type::<Datetime>();
|
||||||
global.define_type::<Duration>();
|
global.define_type::<Duration>();
|
||||||
|
global.define_type::<Version>();
|
||||||
global.define_type::<Plugin>();
|
global.define_type::<Plugin>();
|
||||||
global.define_func::<repr>();
|
global.define_func::<repr>();
|
||||||
global.define_func::<panic>();
|
global.define_func::<panic>();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
//! Computational functions.
|
//! Computational functions.
|
||||||
|
|
||||||
pub mod calc;
|
pub mod calc;
|
||||||
|
pub mod sys;
|
||||||
|
|
||||||
mod data;
|
mod data;
|
||||||
mod foundations;
|
mod foundations;
|
||||||
@ -15,4 +16,5 @@ pub(super) fn define(global: &mut Scope) {
|
|||||||
self::foundations::define(global);
|
self::foundations::define(global);
|
||||||
self::data::define(global);
|
self::data::define(global);
|
||||||
self::calc::define(global);
|
self::calc::define(global);
|
||||||
|
self::sys::define(global);
|
||||||
}
|
}
|
||||||
|
24
crates/typst-library/src/compute/sys.rs
Normal file
24
crates/typst-library/src/compute/sys.rs
Normal file
@ -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)
|
||||||
|
}
|
@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
cast, func, ops, scope, ty, Args, Bytes, CastInfo, FromValue, Func, IntoValue,
|
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::diag::{At, SourceResult, StrResult};
|
||||||
use crate::eval::ops::{add, mul};
|
use crate::eval::ops::{add, mul};
|
||||||
@ -804,6 +804,7 @@ cast! {
|
|||||||
ToArray,
|
ToArray,
|
||||||
v: Bytes => Self(v.iter().map(|&b| Value::Int(b.into())).collect()),
|
v: Bytes => Self(v.iter().map(|&b| Value::Int(b.into())).collect()),
|
||||||
v: Array => Self(v),
|
v: Array => Self(v),
|
||||||
|
v: Version => Self(v.values().iter().map(|&v| Value::Int(v as i64)).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Array {
|
impl Debug for Array {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use ecow::{eco_format, EcoString};
|
use ecow::{eco_format, EcoString};
|
||||||
|
|
||||||
use crate::diag::StrResult;
|
use crate::diag::StrResult;
|
||||||
|
use crate::eval::Version;
|
||||||
use crate::geom::{Align, Length, Rel, Stroke};
|
use crate::geom::{Align, Length, Rel, Stroke};
|
||||||
|
|
||||||
use super::{IntoValue, Type, Value};
|
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
|
// Special cases, such as module and dict, are handled by Value itself
|
||||||
let result = match value {
|
let result = match value {
|
||||||
|
Value::Version(version) => match version.component(field) {
|
||||||
|
Ok(i) => i.into_value(),
|
||||||
|
Err(_) => return missing(),
|
||||||
|
},
|
||||||
Value::Length(length) => match field {
|
Value::Length(length) => match field {
|
||||||
"em" => length.em.get().into_value(),
|
"em" => length.em.get().into_value(),
|
||||||
"abs" => length.abs.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.
|
/// List the available fields for a type.
|
||||||
pub fn fields_on(ty: Type) -> &'static [&'static str] {
|
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"]
|
&["em", "abs"]
|
||||||
} else if ty == Type::of::<Rel>() {
|
} else if ty == Type::of::<Rel>() {
|
||||||
&["ratio", "length"]
|
&["ratio", "length"]
|
||||||
|
@ -31,6 +31,7 @@ mod scope;
|
|||||||
mod symbol;
|
mod symbol;
|
||||||
mod tracer;
|
mod tracer;
|
||||||
mod ty;
|
mod ty;
|
||||||
|
mod version;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub use {
|
pub use {
|
||||||
@ -65,6 +66,7 @@ pub use self::symbol::{symbols, Symbol};
|
|||||||
pub use self::tracer::Tracer;
|
pub use self::tracer::Tracer;
|
||||||
pub use self::ty::{scope, ty, NativeType, NativeTypeData, Type};
|
pub use self::ty::{scope, ty, NativeType, NativeTypeData, Type};
|
||||||
pub use self::value::{Dynamic, Value};
|
pub use self::value::{Dynamic, Value};
|
||||||
|
pub use self::version::Version;
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -366,6 +366,7 @@ pub fn equal(lhs: &Value, rhs: &Value) -> bool {
|
|||||||
(Fraction(a), Fraction(b)) => a == b,
|
(Fraction(a), Fraction(b)) => a == b,
|
||||||
(Color(a), Color(b)) => a == b,
|
(Color(a), Color(b)) => a == b,
|
||||||
(Symbol(a), Symbol(b)) => a == b,
|
(Symbol(a), Symbol(b)) => a == b,
|
||||||
|
(Version(a), Version(b)) => a == b,
|
||||||
(Str(a), Str(b)) => a == b,
|
(Str(a), Str(b)) => a == b,
|
||||||
(Bytes(a), Bytes(b)) => a == b,
|
(Bytes(a), Bytes(b)) => a == b,
|
||||||
(Label(a), Label(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),
|
(Ratio(a), Ratio(b)) => a.cmp(b),
|
||||||
(Relative(a), Relative(b)) => try_cmp_values(a, b)?,
|
(Relative(a), Relative(b)) => try_cmp_values(a, b)?,
|
||||||
(Fraction(a), Fraction(b)) => a.cmp(b),
|
(Fraction(a), Fraction(b)) => a.cmp(b),
|
||||||
|
(Version(a), Version(b)) => a.cmp(b),
|
||||||
(Str(a), Str(b)) => a.cmp(b),
|
(Str(a), Str(b)) => a.cmp(b),
|
||||||
|
|
||||||
// Some technically different things should be comparable.
|
// Some technically different things should be comparable.
|
||||||
|
@ -9,7 +9,7 @@ use unicode_segmentation::UnicodeSegmentation;
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
cast, dict, func, scope, ty, Args, Array, Bytes, Dict, Func, IntoValue, Type, Value,
|
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::diag::{bail, At, SourceResult, StrResult};
|
||||||
use crate::geom::Align;
|
use crate::geom::Align;
|
||||||
@ -605,6 +605,7 @@ cast! {
|
|||||||
ToStr,
|
ToStr,
|
||||||
v: i64 => Self::Int(v),
|
v: i64 => Self::Int(v),
|
||||||
v: f64 => Self::Str(format_str!("{}", v)),
|
v: f64 => Self::Str(format_str!("{}", v)),
|
||||||
|
v: Version => Self::Str(format_str!("{}", v)),
|
||||||
v: Bytes => Self::Str(
|
v: Bytes => Self::Str(
|
||||||
std::str::from_utf8(&v)
|
std::str::from_utf8(&v)
|
||||||
.map_err(|_| "bytes are not valid utf-8")?
|
.map_err(|_| "bytes are not valid utf-8")?
|
||||||
|
@ -14,7 +14,7 @@ use typst::eval::Duration;
|
|||||||
use super::{
|
use super::{
|
||||||
fields, format_str, ops, Args, Array, AutoValue, Bytes, CastInfo, Content, Dict,
|
fields, format_str, ops, Args, Array, AutoValue, Bytes, CastInfo, Content, Dict,
|
||||||
FromValue, Func, IntoValue, Module, NativeType, NoneValue, Plugin, Reflect, Scope,
|
FromValue, Func, IntoValue, Module, NativeType, NoneValue, Plugin, Reflect, Scope,
|
||||||
Str, Symbol, Type,
|
Str, Symbol, Type, Version,
|
||||||
};
|
};
|
||||||
use crate::diag::StrResult;
|
use crate::diag::StrResult;
|
||||||
use crate::eval::Datetime;
|
use crate::eval::Datetime;
|
||||||
@ -50,6 +50,8 @@ pub enum Value {
|
|||||||
Color(Color),
|
Color(Color),
|
||||||
/// A symbol: `arrow.l`.
|
/// A symbol: `arrow.l`.
|
||||||
Symbol(Symbol),
|
Symbol(Symbol),
|
||||||
|
/// A version.
|
||||||
|
Version(Version),
|
||||||
/// A string: `"string"`.
|
/// A string: `"string"`.
|
||||||
Str(Str),
|
Str(Str),
|
||||||
/// Raw bytes.
|
/// Raw bytes.
|
||||||
@ -122,6 +124,7 @@ impl Value {
|
|||||||
Self::Fraction(_) => Type::of::<Fr>(),
|
Self::Fraction(_) => Type::of::<Fr>(),
|
||||||
Self::Color(_) => Type::of::<Color>(),
|
Self::Color(_) => Type::of::<Color>(),
|
||||||
Self::Symbol(_) => Type::of::<Symbol>(),
|
Self::Symbol(_) => Type::of::<Symbol>(),
|
||||||
|
Self::Version(_) => Type::of::<Version>(),
|
||||||
Self::Str(_) => Type::of::<Str>(),
|
Self::Str(_) => Type::of::<Str>(),
|
||||||
Self::Bytes(_) => Type::of::<Bytes>(),
|
Self::Bytes(_) => Type::of::<Bytes>(),
|
||||||
Self::Label(_) => Type::of::<Label>(),
|
Self::Label(_) => Type::of::<Label>(),
|
||||||
@ -149,6 +152,7 @@ impl Value {
|
|||||||
pub fn field(&self, field: &str) -> StrResult<Value> {
|
pub fn field(&self, field: &str) -> StrResult<Value> {
|
||||||
match self {
|
match self {
|
||||||
Self::Symbol(symbol) => symbol.clone().modified(field).map(Self::Symbol),
|
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::Dict(dict) => dict.get(field).cloned(),
|
||||||
Self::Content(content) => content.get(field),
|
Self::Content(content) => content.get(field),
|
||||||
Self::Type(ty) => ty.field(field).cloned(),
|
Self::Type(ty) => ty.field(field).cloned(),
|
||||||
@ -199,6 +203,7 @@ impl Value {
|
|||||||
Self::Int(v) => item!(text)(eco_format!("{v}")),
|
Self::Int(v) => item!(text)(eco_format!("{v}")),
|
||||||
Self::Float(v) => item!(text)(eco_format!("{v}")),
|
Self::Float(v) => item!(text)(eco_format!("{v}")),
|
||||||
Self::Str(v) => item!(text)(v.into()),
|
Self::Str(v) => item!(text)(v.into()),
|
||||||
|
Self::Version(v) => item!(text)(eco_format!("{v}")),
|
||||||
Self::Symbol(v) => item!(text)(v.get().into()),
|
Self::Symbol(v) => item!(text)(v.get().into()),
|
||||||
Self::Content(v) => v,
|
Self::Content(v) => v,
|
||||||
Self::Module(module) => module.content(),
|
Self::Module(module) => module.content(),
|
||||||
@ -231,6 +236,7 @@ impl Debug for Value {
|
|||||||
Self::Fraction(v) => Debug::fmt(v, f),
|
Self::Fraction(v) => Debug::fmt(v, f),
|
||||||
Self::Color(v) => Debug::fmt(v, f),
|
Self::Color(v) => Debug::fmt(v, f),
|
||||||
Self::Symbol(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::Str(v) => Debug::fmt(v, f),
|
||||||
Self::Bytes(v) => Debug::fmt(v, f),
|
Self::Bytes(v) => Debug::fmt(v, f),
|
||||||
Self::Label(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::Fraction(v) => v.hash(state),
|
||||||
Self::Color(v) => v.hash(state),
|
Self::Color(v) => v.hash(state),
|
||||||
Self::Symbol(v) => v.hash(state),
|
Self::Symbol(v) => v.hash(state),
|
||||||
|
Self::Version(v) => v.hash(state),
|
||||||
Self::Str(v) => v.hash(state),
|
Self::Str(v) => v.hash(state),
|
||||||
Self::Bytes(v) => v.hash(state),
|
Self::Bytes(v) => v.hash(state),
|
||||||
Self::Label(v) => v.hash(state),
|
Self::Label(v) => v.hash(state),
|
||||||
@ -582,6 +589,7 @@ primitive! { Rel<Length>: "relative length",
|
|||||||
primitive! { Fr: "fraction", Fraction }
|
primitive! { Fr: "fraction", Fraction }
|
||||||
primitive! { Color: "color", Color }
|
primitive! { Color: "color", Color }
|
||||||
primitive! { Symbol: "symbol", Symbol }
|
primitive! { Symbol: "symbol", Symbol }
|
||||||
|
primitive! { Version: "version", Version }
|
||||||
primitive! {
|
primitive! {
|
||||||
Str: "string",
|
Str: "string",
|
||||||
Str,
|
Str,
|
||||||
|
199
crates/typst/src/eval/version.rs
Normal file
199
crates/typst/src/eval/version.rs
Normal file
@ -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)
|
||||||
|
}
|
@ -28,5 +28,5 @@
|
|||||||
#bytes((a: 1))
|
#bytes((a: 1))
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 8-15 expected bytes or array, found string
|
// Error: 8-15 expected bytes, array, or version, found string
|
||||||
#array("hello")
|
#array("hello")
|
||||||
|
@ -168,7 +168,7 @@
|
|||||||
#test(str(10 / 3).len() > 10, true)
|
#test(str(10 / 3).len() > 10, true)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 6-8 expected integer, float, bytes, label, type, or string, found content
|
// Error: 6-8 expected integer, float, version, bytes, label, type, or string, found content
|
||||||
#str([])
|
#str([])
|
||||||
|
|
||||||
---
|
---
|
||||||
|
47
tests/typ/compute/version.typ
Normal file
47
tests/typ/compute/version.typ
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Test versions.
|
||||||
|
// Ref: false
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test version constructor.
|
||||||
|
|
||||||
|
// Empty.
|
||||||
|
#version()
|
||||||
|
|
||||||
|
// Plain.
|
||||||
|
#version(1, 2)
|
||||||
|
|
||||||
|
// Single Array argument.
|
||||||
|
#version((1, 2))
|
||||||
|
|
||||||
|
// Mixed arguments.
|
||||||
|
#version(1, (2, 3), 4, (5, 6), 7)
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test equality of different-length versions
|
||||||
|
#test(version(), version(0))
|
||||||
|
#test(version(0), version(0, 0))
|
||||||
|
#test(version(1, 2), version(1, 2, 0, 0, 0, 0))
|
||||||
|
---
|
||||||
|
// Test `version.at`.
|
||||||
|
|
||||||
|
// Non-negative index in bounds
|
||||||
|
#test(version(1, 2).at(1), 2)
|
||||||
|
|
||||||
|
// Non-negative index out of bounds
|
||||||
|
#test(version(1, 2).at(4), 0)
|
||||||
|
|
||||||
|
// Negative index in bounds
|
||||||
|
#test(version(1, 2).at(-2), 1)
|
||||||
|
|
||||||
|
// Error: 2-22 component index out of bounds (index: -3, len: 2)
|
||||||
|
#version(1, 2).at(-3)
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test version fields.
|
||||||
|
#test(version(1, 2, 3).major, 1)
|
||||||
|
#test(version(1, 2, 3).minor, 2)
|
||||||
|
#test(version(1, 2, 3).patch, 3)
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test the type of `sys.version`
|
||||||
|
#test(type(sys.version), version)
|
Loading…
x
Reference in New Issue
Block a user