diff --git a/crates/typst-docs/src/lib.rs b/crates/typst-docs/src/lib.rs index 921695c72..d58734c6f 100644 --- a/crates/typst-docs/src/lib.rs +++ b/crates/typst-docs/src/lib.rs @@ -21,9 +21,11 @@ use serde::Deserialize; use serde_yaml as yaml; use typst::diag::{bail, StrResult}; use typst::doc::Frame; -use typst::eval::{CastInfo, Func, Library, Module, ParamInfo, Repr, Scope, Type, Value}; +use typst::eval::{ + CastInfo, Func, Library, Module, ParamInfo, Repr, Scope, Smart, Type, Value, +}; use typst::font::{Font, FontBook}; -use typst::geom::{Abs, Smart}; +use typst::geom::Abs; use typst_library::layout::{Margin, PageElem}; static DOCS_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/../../docs"); diff --git a/crates/typst-ide/src/complete.rs b/crates/typst-ide/src/complete.rs index 325055859..c9a625d1c 100644 --- a/crates/typst-ide/src/complete.rs +++ b/crates/typst-ide/src/complete.rs @@ -5,6 +5,7 @@ use ecow::{eco_format, EcoString}; use if_chain::if_chain; use serde::{Deserialize, Serialize}; use typst::doc::Frame; +use typst::eval::repr::separated_list; use typst::eval::{ format_str, AutoValue, CastInfo, Func, Library, NoneValue, Repr, Scope, Type, Value, }; @@ -13,7 +14,6 @@ use typst::model::Label; use typst::syntax::{ ast, is_id_continue, is_id_start, is_ident, LinkedNode, Source, SyntaxKind, }; -use typst::util::separated_list; use typst::World; use unscanny::Scanner; diff --git a/crates/typst-ide/src/tooltip.rs b/crates/typst-ide/src/tooltip.rs index f90f2aea6..09433cde5 100644 --- a/crates/typst-ide/src/tooltip.rs +++ b/crates/typst-ide/src/tooltip.rs @@ -3,11 +3,11 @@ use std::fmt::Write; use ecow::{eco_format, EcoString}; use if_chain::if_chain; use typst::doc::Frame; +use typst::eval::repr::{pretty_comma_list, separated_list}; use typst::eval::{CapturesVisitor, CastInfo, Repr, Tracer, Value}; use typst::geom::{round_2, Length, Numeric}; use typst::syntax::ast; use typst::syntax::{LinkedNode, Source, SyntaxKind}; -use typst::util::{pretty_comma_list, separated_list}; use typst::World; use super::analyze::analyze_labels; diff --git a/crates/typst-library/src/lib.rs b/crates/typst-library/src/lib.rs index da31409aa..554e7bcbc 100644 --- a/crates/typst-library/src/lib.rs +++ b/crates/typst-library/src/lib.rs @@ -14,8 +14,8 @@ pub mod symbols; pub mod text; pub mod visualize; -use typst::eval::{Array, LangItems, Library, Module, Scope}; -use typst::geom::{Align, Color, Dir, Smart}; +use typst::eval::{Array, LangItems, Library, Module, Scope, Smart}; +use typst::geom::{Align, Color, Dir}; use typst::model::{NativeElement, Styles}; use self::layout::LayoutRoot; diff --git a/crates/typst-library/src/prelude.rs b/crates/typst-library/src/prelude.rs index e3163e0fc..007005400 100644 --- a/crates/typst-library/src/prelude.rs +++ b/crates/typst-library/src/prelude.rs @@ -18,7 +18,7 @@ pub use typst::doc::*; #[doc(no_inline)] pub use typst::eval::{ array, cast, dict, format_str, func, scope, ty, Args, Array, Bytes, Cast, Dict, - FromValue, Func, IntoValue, Repr, Scope, Str, Symbol, Type, Value, Vm, + FromValue, Func, IntoValue, Repr, Scope, Smart, Str, Symbol, Type, Value, Vm, }; #[doc(no_inline)] pub use typst::geom::*; diff --git a/crates/typst-library/src/visualize/image.rs b/crates/typst-library/src/visualize/image.rs index 6e166bc08..7f0b82891 100644 --- a/crates/typst-library/src/visualize/image.rs +++ b/crates/typst-library/src/visualize/image.rs @@ -1,7 +1,5 @@ use std::ffi::OsStr; -use std::path::Path; -use typst::geom::{self, Smart}; use typst::image::{Image, ImageFormat, RasterFormat, VectorFormat}; use typst::util::option_eq; @@ -135,7 +133,7 @@ impl Layout for ImageElem { let format = match self.format(styles) { Smart::Custom(v) => v, Smart::Auto => { - let ext = Path::new(self.path().as_str()) + let ext = std::path::Path::new(self.path().as_str()) .extension() .and_then(OsStr::to_str) .unwrap_or_default() @@ -213,7 +211,7 @@ impl Layout for ImageElem { // Create a clipping group if only part of the image should be visible. if fit == ImageFit::Cover && !target.fits(fitted) { - frame.clip(geom::Path::rect(frame.size())); + frame.clip(Path::rect(frame.size())); } // Apply metadata. diff --git a/crates/typst-macros/src/elem.rs b/crates/typst-macros/src/elem.rs index a5e14b5a2..512cbc844 100644 --- a/crates/typst-macros/src/elem.rs +++ b/crates/typst-macros/src/elem.rs @@ -1087,7 +1087,7 @@ fn create_repr_impl(element: &Elem) -> TokenStream { let fields = self.fields().into_iter() .map(|(name, value)| eco_format!("{}: {}", name, value.repr())) .collect::>(); - ::ecow::eco_format!(#repr_format, ::typst::util::pretty_array_like(&fields, false)) + ::ecow::eco_format!(#repr_format, ::typst::eval::repr::pretty_array_like(&fields, false)) } } } @@ -1108,7 +1108,7 @@ fn create_vtable_func(element: &Elem) -> TokenStream { if id == ::std::any::TypeId::of::() { let vtable = unsafe { let dangling = ::std::ptr::NonNull::<#ident>::dangling().as_ptr() as *const dyn #capability; - ::typst::util::fat::vtable(dangling) + ::typst::model::fat::vtable(dangling) }; return Some(vtable); } diff --git a/crates/typst-pdf/src/outline.rs b/crates/typst-pdf/src/outline.rs index d17f89489..4fd072b66 100644 --- a/crates/typst-pdf/src/outline.rs +++ b/crates/typst-pdf/src/outline.rs @@ -1,8 +1,8 @@ use std::num::NonZeroUsize; use pdf_writer::{Finish, Ref, TextStr}; -use typst::eval::item; -use typst::geom::{Abs, Smart}; +use typst::eval::{item, Smart}; +use typst::geom::Abs; use typst::model::Content; use crate::{AbsExt, PdfContext}; diff --git a/crates/typst/src/doc.rs b/crates/typst/src/doc.rs index 48f201dc9..a53836ac1 100644 --- a/crates/typst/src/doc.rs +++ b/crates/typst/src/doc.rs @@ -8,12 +8,11 @@ use std::sync::Arc; use ecow::{eco_format, EcoString}; -use crate::eval::{cast, dict, ty, Datetime, Dict, Repr, Value}; +use crate::eval::{cast, dict, ty, Datetime, Dict, Repr, Smart, Value}; use crate::font::Font; use crate::geom::{ self, styled_rect, Abs, Axes, Color, Corners, Dir, Em, FixedAlign, FixedStroke, - Geometry, Length, Numeric, Paint, Path, Point, Rel, Shape, Sides, Size, Smart, - Transform, + Geometry, Length, Numeric, Paint, Path, Point, Rel, Shape, Sides, Size, Transform, }; use crate::image::Image; use crate::model::{Content, Location, MetaElem, StyleChain}; diff --git a/crates/typst/src/eval/args.rs b/crates/typst/src/eval/args.rs index c347f5277..543d19135 100644 --- a/crates/typst/src/eval/args.rs +++ b/crates/typst/src/eval/args.rs @@ -2,10 +2,10 @@ use std::fmt::{self, Debug, Formatter}; use ecow::{eco_format, eco_vec, EcoString, EcoVec}; +use super::repr::pretty_array_like; use super::{func, scope, ty, Array, Dict, FromValue, IntoValue, Repr, Str, Value}; use crate::diag::{bail, At, SourceDiagnostic, SourceResult}; use crate::syntax::{Span, Spanned}; -use crate::util::pretty_array_like; /// Captured arguments to a function. /// diff --git a/crates/typst/src/eval/array.rs b/crates/typst/src/eval/array.rs index 772db535a..497f91b5f 100644 --- a/crates/typst/src/eval/array.rs +++ b/crates/typst/src/eval/array.rs @@ -7,6 +7,7 @@ use ecow::{eco_format, EcoString, EcoVec}; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; +use super::repr::pretty_array_like; use super::{ cast, func, ops, scope, ty, Args, Bytes, CastInfo, FromValue, Func, IntoValue, Reflect, Repr, Value, Version, Vm, @@ -14,7 +15,6 @@ use super::{ use crate::diag::{At, SourceResult, StrResult}; use crate::eval::ops::{add, mul}; use crate::syntax::Span; -use crate::util::pretty_array_like; /// Create a new [`Array`] from values. #[macro_export] diff --git a/crates/typst/src/eval/auto.rs b/crates/typst/src/eval/auto.rs index 24254a0fc..b14b21b12 100644 --- a/crates/typst/src/eval/auto.rs +++ b/crates/typst/src/eval/auto.rs @@ -3,6 +3,7 @@ use std::fmt::Debug; use super::{ty, CastInfo, FromValue, IntoValue, Reflect, Repr, Type, Value}; use crate::diag::StrResult; +use crate::model::{Fold, Resolve, StyleChain}; /// A value that indicates a smart default. /// @@ -50,3 +51,182 @@ impl Repr for AutoValue { "auto".into() } } + +/// A value that can be automatically determined. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum Smart { + /// The value should be determined smartly based on the circumstances. + Auto, + /// A specific value. + Custom(T), +} + +impl Smart { + /// Whether the value is `Auto`. + pub fn is_auto(&self) -> bool { + matches!(self, Self::Auto) + } + + /// Whether this holds a custom value. + pub fn is_custom(&self) -> bool { + matches!(self, Self::Custom(_)) + } + + /// Returns a `Smart<&T>` borrowing the inner `T`. + pub fn as_ref(&self) -> Smart<&T> { + match self { + Smart::Auto => Smart::Auto, + Smart::Custom(v) => Smart::Custom(v), + } + } + + /// Returns a reference the contained custom value. + /// If the value is [`Smart::Auto`], `None` is returned. + pub fn as_custom(self) -> Option { + match self { + Self::Auto => None, + Self::Custom(x) => Some(x), + } + } + + /// Map the contained custom value with `f`. + pub fn map(self, f: F) -> Smart + where + F: FnOnce(T) -> U, + { + match self { + Self::Auto => Smart::Auto, + Self::Custom(x) => Smart::Custom(f(x)), + } + } + + /// Map the contained custom value with `f` if it contains a custom value, + /// otherwise returns `default`. + pub fn map_or(self, default: U, f: F) -> U + where + F: FnOnce(T) -> U, + { + match self { + Self::Auto => default, + Self::Custom(x) => f(x), + } + } + + /// Keeps `self` if it contains a custom value, otherwise returns `other`. + pub fn or(self, other: Smart) -> Self { + match self { + Self::Custom(x) => Self::Custom(x), + Self::Auto => other, + } + } + + /// Retusn `Auto` if `self` is `Auto`, otherwise calls the provided function onthe contained + /// value and returns the result. + pub fn and_then(self, f: F) -> Smart + where + F: FnOnce(T) -> Smart, + { + match self { + Smart::Auto => Smart::Auto, + Smart::Custom(x) => f(x), + } + } + + /// Returns the contained custom value or a provided default value. + pub fn unwrap_or(self, default: T) -> T { + match self { + Self::Auto => default, + Self::Custom(x) => x, + } + } + + /// Returns the contained custom value or computes a default value. + pub fn unwrap_or_else(self, f: F) -> T + where + F: FnOnce() -> T, + { + match self { + Self::Auto => f(), + Self::Custom(x) => x, + } + } + + /// Returns the contained custom value or the default value. + pub fn unwrap_or_default(self) -> T + where + T: Default, + { + // we want to do this; the Clippy lint is not type-aware + #[allow(clippy::unwrap_or_default)] + self.unwrap_or_else(T::default) + } +} + +impl Smart> { + /// Removes a single level of nesting, returns `Auto` if the inner or outer value is `Auto`. + pub fn flatten(self) -> Smart { + match self { + Smart::Custom(Smart::Auto) | Smart::Auto => Smart::Auto, + Smart::Custom(Smart::Custom(v)) => Smart::Custom(v), + } + } +} + +impl Default for Smart { + fn default() -> Self { + Self::Auto + } +} + +impl Reflect for Smart { + fn input() -> CastInfo { + T::input() + AutoValue::input() + } + + fn output() -> CastInfo { + T::output() + AutoValue::output() + } + + fn castable(value: &Value) -> bool { + AutoValue::castable(value) || T::castable(value) + } +} + +impl IntoValue for Smart { + fn into_value(self) -> Value { + match self { + Smart::Custom(v) => v.into_value(), + Smart::Auto => Value::Auto, + } + } +} + +impl FromValue for Smart { + fn from_value(value: Value) -> StrResult { + match value { + Value::Auto => Ok(Self::Auto), + v if T::castable(&v) => Ok(Self::Custom(T::from_value(v)?)), + _ => Err(Self::error(&value)), + } + } +} + +impl Resolve for Smart { + type Output = Smart; + + fn resolve(self, styles: StyleChain) -> Self::Output { + self.map(|v| v.resolve(styles)) + } +} + +impl Fold for Smart +where + T: Fold, + T::Output: Default, +{ + type Output = Smart; + + fn fold(self, outer: Self::Output) -> Self::Output { + self.map(|inner| inner.fold(outer.unwrap_or_default())) + } +} diff --git a/crates/typst/src/eval/cast.rs b/crates/typst/src/eval/cast.rs index eeb632f9f..4bb5b6af1 100644 --- a/crates/typst/src/eval/cast.rs +++ b/crates/typst/src/eval/cast.rs @@ -10,10 +10,10 @@ use ecow::{eco_format, EcoString}; use smallvec::SmallVec; use unicode_math_class::MathClass; +use super::repr::separated_list; use super::{Repr, Type, Value}; use crate::diag::{At, SourceResult, StrResult}; use crate::syntax::{Span, Spanned}; -use crate::util::separated_list; /// Determine details of a type. /// diff --git a/crates/typst/src/eval/datetime.rs b/crates/typst/src/eval/datetime.rs index a158ad31b..129a05cab 100644 --- a/crates/typst/src/eval/datetime.rs +++ b/crates/typst/src/eval/datetime.rs @@ -9,10 +9,9 @@ use time::error::{Format, InvalidFormatDescription}; use time::macros::format_description; use time::{format_description, Month, PrimitiveDateTime}; -use super::{cast, func, scope, ty, Dict, Duration, Repr, Str, Value, Vm}; +use super::repr::pretty_array_like; +use super::{cast, func, scope, ty, Dict, Duration, Repr, Smart, Str, Value, Vm}; use crate::diag::{bail, StrResult}; -use crate::geom::Smart; -use crate::util::pretty_array_like; use crate::World; /// Represents a date, a time, or a combination of both. diff --git a/crates/typst/src/eval/dict.rs b/crates/typst/src/eval/dict.rs index 1cc675c6b..568c6e124 100644 --- a/crates/typst/src/eval/dict.rs +++ b/crates/typst/src/eval/dict.rs @@ -7,10 +7,11 @@ use ecow::{eco_format, EcoString}; use indexmap::IndexMap; use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use super::repr::{pretty_array_like, separated_list}; use super::{array, func, scope, ty, Array, Repr, Str, Value}; use crate::diag::StrResult; use crate::syntax::is_ident; -use crate::util::{pretty_array_like, separated_list, ArcExt}; +use crate::util::ArcExt; /// Create a new [`Dict`] from key-value pairs. #[macro_export] diff --git a/crates/typst/src/eval/duration.rs b/crates/typst/src/eval/duration.rs index e1e6ee577..8aa44aba9 100644 --- a/crates/typst/src/eval/duration.rs +++ b/crates/typst/src/eval/duration.rs @@ -4,8 +4,8 @@ use std::fmt::Debug; use std::ops::{Add, Div, Mul, Neg, Sub}; use time::ext::NumericalDuration; +use super::repr::pretty_array_like; use super::{func, scope, ty, Repr}; -use crate::util::pretty_array_like; /// Represents a positive or negative span of time. #[ty(scope)] diff --git a/crates/typst/src/eval/float.rs b/crates/typst/src/eval/float.rs index 55becee23..f9340d69b 100644 --- a/crates/typst/src/eval/float.rs +++ b/crates/typst/src/eval/float.rs @@ -2,9 +2,9 @@ use std::num::ParseFloatError; use ecow::{eco_format, EcoString}; +use super::repr::{format_float, MINUS_SIGN}; use super::{cast, func, scope, ty, Repr, Str}; use crate::geom::Ratio; -use crate::util::fmt::{format_float, MINUS_SIGN}; /// A floating-point number. /// diff --git a/crates/typst/src/eval/int.rs b/crates/typst/src/eval/int.rs index 077a1ec03..2cd196084 100644 --- a/crates/typst/src/eval/int.rs +++ b/crates/typst/src/eval/int.rs @@ -1,8 +1,8 @@ use std::num::{NonZeroI64, NonZeroIsize, NonZeroU64, NonZeroUsize, ParseIntError}; -use crate::util::fmt::{format_int_with_base, MINUS_SIGN}; use ecow::{eco_format, EcoString}; +use super::repr::{format_int_with_base, MINUS_SIGN}; use super::{cast, func, scope, ty, Repr, Str, Value}; /// A whole number. diff --git a/crates/typst/src/eval/mod.rs b/crates/typst/src/eval/mod.rs index 26de43998..ce055f1e6 100644 --- a/crates/typst/src/eval/mod.rs +++ b/crates/typst/src/eval/mod.rs @@ -27,6 +27,7 @@ mod module; mod none; pub mod ops; mod plugin; +pub mod repr; mod scope; mod symbol; mod tracer; @@ -43,7 +44,7 @@ pub use { pub use self::args::{Arg, Args}; pub use self::array::{array, Array}; -pub use self::auto::AutoValue; +pub use self::auto::{AutoValue, Smart}; pub use self::bytes::Bytes; pub use self::cast::{ cast, Cast, CastInfo, Container, FromValue, IntoResult, IntoValue, Never, Reflect, @@ -60,12 +61,13 @@ pub use self::methods::mutable_methods_on; pub use self::module::Module; pub use self::none::NoneValue; pub use self::plugin::Plugin; +pub use self::repr::Repr; pub use self::scope::{NativeScope, Scope, Scopes}; pub use self::str::{format_str, Regex, Str}; 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, Repr, Value}; +pub use self::value::{Dynamic, Value}; pub use self::version::Version; use std::collections::HashSet; diff --git a/crates/typst/src/eval/ops.rs b/crates/typst/src/eval/ops.rs index e0c3b984d..cf5d27a65 100644 --- a/crates/typst/src/eval/ops.rs +++ b/crates/typst/src/eval/ops.rs @@ -4,10 +4,9 @@ use std::cmp::Ordering; use ecow::eco_format; -use super::{format_str, IntoValue, Regex, Repr, Value}; +use super::{format_str, item, IntoValue, Regex, Repr, Smart, Value}; use crate::diag::{bail, StrResult}; -use crate::eval::item; -use crate::geom::{Align, Length, Numeric, Rel, Smart, Stroke}; +use crate::geom::{Align, Length, Numeric, Rel, Stroke}; use Value::*; /// Bail with a type mismatch error. diff --git a/crates/typst/src/util/fmt.rs b/crates/typst/src/eval/repr.rs similarity index 95% rename from crates/typst/src/util/fmt.rs rename to crates/typst/src/eval/repr.rs index 50e590958..96cca2347 100644 --- a/crates/typst/src/util/fmt.rs +++ b/crates/typst/src/eval/repr.rs @@ -2,6 +2,12 @@ use ecow::{eco_format, EcoString}; pub const MINUS_SIGN: &str = "\u{2212}"; +/// A trait that defines the `repr` of a Typst value. +pub trait Repr { + /// Return the debug representation of the value. + fn repr(&self) -> EcoString; +} + /// Format an integer in a base. pub fn format_int_with_base(mut n: i64, base: i64) -> EcoString { if n == 0 { diff --git a/crates/typst/src/eval/str.rs b/crates/typst/src/eval/str.rs index 97d158704..def42059a 100644 --- a/crates/typst/src/eval/str.rs +++ b/crates/typst/src/eval/str.rs @@ -7,6 +7,7 @@ use ecow::EcoString; use serde::{Deserialize, Serialize}; use unicode_segmentation::UnicodeSegmentation; +use super::repr::{format_float, format_int_with_base}; use super::{ cast, dict, func, scope, ty, Args, Array, Bytes, Dict, Func, IntoValue, Repr, Type, Value, Version, Vm, @@ -15,7 +16,6 @@ use crate::diag::{bail, At, SourceResult, StrResult}; use crate::geom::Align; use crate::model::Label; use crate::syntax::{Span, Spanned}; -use crate::util::fmt::{format_float, format_int_with_base}; /// Create a new [`Str`] from a format string. #[macro_export] diff --git a/crates/typst/src/eval/value.rs b/crates/typst/src/eval/value.rs index 1d866418c..a0b2645c9 100644 --- a/crates/typst/src/eval/value.rs +++ b/crates/typst/src/eval/value.rs @@ -11,17 +11,17 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use siphasher::sip128::{Hasher128, SipHasher13}; use typst::eval::Duration; +use super::repr::{format_float, format_int_with_base}; use super::{ fields, ops, Args, Array, AutoValue, Bytes, CastInfo, Content, Dict, FromValue, Func, - IntoValue, Module, NativeType, NoneValue, Plugin, Reflect, Scope, Str, Symbol, Type, - Version, + IntoValue, Module, NativeType, NoneValue, Plugin, Reflect, Repr, Scope, Str, Symbol, + Type, Version, }; use crate::diag::StrResult; use crate::eval::{item, Datetime}; use crate::geom::{Abs, Angle, Color, Em, Fr, Gradient, Length, Ratio, Rel}; use crate::model::{Label, Styles}; use crate::syntax::{ast, Span}; -use crate::util::fmt::{format_float, format_int_with_base}; /// A computational value. #[derive(Debug, Default, Clone)] @@ -489,12 +489,6 @@ impl PartialEq for Dynamic { } } -/// A trait that defines the `repr` of a Typst value. -pub trait Repr { - /// Return the debug representation of the value. - fn repr(&self) -> EcoString; -} - trait Bounds: Debug + Repr + Sync + Send + 'static { fn as_any(&self) -> &dyn Any; fn dyn_eq(&self, other: &Dynamic) -> bool; diff --git a/crates/typst/src/eval/version.rs b/crates/typst/src/eval/version.rs index bf4887920..cf60fd681 100644 --- a/crates/typst/src/eval/version.rs +++ b/crates/typst/src/eval/version.rs @@ -5,9 +5,9 @@ use std::iter::repeat; use ecow::{eco_format, EcoString, EcoVec}; +use super::repr::pretty_array_like; use super::{cast, func, scope, ty, Repr}; use crate::diag::{bail, error, StrResult}; -use crate::util::pretty_array_like; /// A version with an arbitrary number of components. /// diff --git a/crates/typst/src/geom/mod.rs b/crates/typst/src/geom/mod.rs index 1b2a79bce..fb38bd0a4 100644 --- a/crates/typst/src/geom/mod.rs +++ b/crates/typst/src/geom/mod.rs @@ -24,7 +24,6 @@ mod scalar; mod shape; mod sides; mod size; -mod smart; mod stroke; mod transform; @@ -52,7 +51,6 @@ pub use self::scalar::Scalar; pub use self::shape::{Geometry, Shape}; pub use self::sides::{Side, Sides}; pub use self::size::Size; -pub use self::smart::Smart; pub use self::stroke::{DashLength, DashPattern, FixedStroke, LineCap, LineJoin, Stroke}; pub use self::transform::Transform; @@ -66,9 +64,9 @@ use std::ops::*; use ecow::{eco_format, EcoString}; use crate::diag::{bail, StrResult}; -use crate::eval::{array, cast, func, scope, ty, Array, Dict, Repr, Value}; +use crate::eval::repr::format_float; +use crate::eval::{array, cast, func, scope, ty, Array, Dict, Repr, Smart, Value}; use crate::model::{Fold, Resolve, StyleChain}; -use crate::util::fmt::format_float; /// Generic access to a structure's components. pub trait Get { diff --git a/crates/typst/src/geom/smart.rs b/crates/typst/src/geom/smart.rs deleted file mode 100644 index bbd9b52a8..000000000 --- a/crates/typst/src/geom/smart.rs +++ /dev/null @@ -1,181 +0,0 @@ -use super::*; -use crate::eval::{AutoValue, CastInfo, FromValue, IntoValue, Reflect}; - -/// A value that can be automatically determined. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub enum Smart { - /// The value should be determined smartly based on the circumstances. - Auto, - /// A specific value. - Custom(T), -} - -impl Smart { - /// Whether the value is `Auto`. - pub fn is_auto(&self) -> bool { - matches!(self, Self::Auto) - } - - /// Whether this holds a custom value. - pub fn is_custom(&self) -> bool { - matches!(self, Self::Custom(_)) - } - - /// Returns a `Smart<&T>` borrowing the inner `T`. - pub fn as_ref(&self) -> Smart<&T> { - match self { - Smart::Auto => Smart::Auto, - Smart::Custom(v) => Smart::Custom(v), - } - } - - /// Returns a reference the contained custom value. - /// If the value is [`Smart::Auto`], `None` is returned. - pub fn as_custom(self) -> Option { - match self { - Self::Auto => None, - Self::Custom(x) => Some(x), - } - } - - /// Map the contained custom value with `f`. - pub fn map(self, f: F) -> Smart - where - F: FnOnce(T) -> U, - { - match self { - Self::Auto => Smart::Auto, - Self::Custom(x) => Smart::Custom(f(x)), - } - } - - /// Map the contained custom value with `f` if it contains a custom value, - /// otherwise returns `default`. - pub fn map_or(self, default: U, f: F) -> U - where - F: FnOnce(T) -> U, - { - match self { - Self::Auto => default, - Self::Custom(x) => f(x), - } - } - - /// Keeps `self` if it contains a custom value, otherwise returns `other`. - pub fn or(self, other: Smart) -> Self { - match self { - Self::Custom(x) => Self::Custom(x), - Self::Auto => other, - } - } - - /// Retusn `Auto` if `self` is `Auto`, otherwise calls the provided function onthe contained - /// value and returns the result. - pub fn and_then(self, f: F) -> Smart - where - F: FnOnce(T) -> Smart, - { - match self { - Smart::Auto => Smart::Auto, - Smart::Custom(x) => f(x), - } - } - - /// Returns the contained custom value or a provided default value. - pub fn unwrap_or(self, default: T) -> T { - match self { - Self::Auto => default, - Self::Custom(x) => x, - } - } - - /// Returns the contained custom value or computes a default value. - pub fn unwrap_or_else(self, f: F) -> T - where - F: FnOnce() -> T, - { - match self { - Self::Auto => f(), - Self::Custom(x) => x, - } - } - - /// Returns the contained custom value or the default value. - pub fn unwrap_or_default(self) -> T - where - T: Default, - { - // we want to do this; the Clippy lint is not type-aware - #[allow(clippy::unwrap_or_default)] - self.unwrap_or_else(T::default) - } -} - -impl Smart> { - /// Removes a single level of nesting, returns `Auto` if the inner or outer value is `Auto`. - pub fn flatten(self) -> Smart { - match self { - Smart::Custom(Smart::Auto) | Smart::Auto => Smart::Auto, - Smart::Custom(Smart::Custom(v)) => Smart::Custom(v), - } - } -} - -impl Default for Smart { - fn default() -> Self { - Self::Auto - } -} - -impl Reflect for Smart { - fn input() -> CastInfo { - T::input() + AutoValue::input() - } - - fn output() -> CastInfo { - T::output() + AutoValue::output() - } - - fn castable(value: &Value) -> bool { - AutoValue::castable(value) || T::castable(value) - } -} - -impl IntoValue for Smart { - fn into_value(self) -> Value { - match self { - Smart::Custom(v) => v.into_value(), - Smart::Auto => Value::Auto, - } - } -} - -impl FromValue for Smart { - fn from_value(value: Value) -> StrResult { - match value { - Value::Auto => Ok(Self::Auto), - v if T::castable(&v) => Ok(Self::Custom(T::from_value(v)?)), - _ => Err(Self::error(&value)), - } - } -} - -impl Resolve for Smart { - type Output = Smart; - - fn resolve(self, styles: StyleChain) -> Self::Output { - self.map(|v| v.resolve(styles)) - } -} - -impl Fold for Smart -where - T: Fold, - T::Output: Default, -{ - type Output = Smart; - - fn fold(self, outer: Self::Output) -> Self::Output { - self.map(|inner| inner.fold(outer.unwrap_or_default())) - } -} diff --git a/crates/typst/src/model/content.rs b/crates/typst/src/model/content.rs index f853fc51d..1d7a1978b 100644 --- a/crates/typst/src/model/content.rs +++ b/crates/typst/src/model/content.rs @@ -16,9 +16,9 @@ use super::{ }; use crate::diag::{SourceResult, StrResult}; use crate::doc::Meta; +use crate::eval::repr::pretty_array_like; use crate::eval::{func, scope, ty, Dict, FromValue, IntoValue, Repr, Str, Value, Vm}; use crate::syntax::Span; -use crate::util::pretty_array_like; /// A piece of document content. /// @@ -256,7 +256,7 @@ impl Content { { let vtable = self.elem().vtable()(TypeId::of::())?; let data = Arc::as_ptr(&self.0) as *const (); - Some(unsafe { &*crate::util::fat::from_raw_parts(data, vtable) }) + Some(unsafe { &*fat::from_raw_parts(data, vtable) }) } /// Cast to a mutable trait object if the contained element has the given @@ -268,7 +268,7 @@ impl Content { // Safety: We ensure the element is not shared. let vtable = self.elem().vtable()(TypeId::of::())?; let data = self.make_mut() as *mut dyn NativeElement as *mut (); - Some(unsafe { &mut *crate::util::fat::from_raw_parts_mut(data, vtable) }) + Some(unsafe { &mut *fat::from_raw_parts_mut(data, vtable) }) } /// Whether the content is a sequence. @@ -749,3 +749,66 @@ fn missing_field_no_default(field: &str) -> EcoString { field.repr() ) } + +/// Fat pointer handling. +/// +/// This assumes the memory representation of fat pointers. Although it is not +/// guaranteed by Rust, it's improbable that it will change. Still, when the +/// pointer metadata APIs are stable, we should definitely move to them: +/// +pub mod fat { + use std::alloc::Layout; + use std::mem; + + /// Create a fat pointer from a data address and a vtable address. + /// + /// # Safety + /// Must only be called when `T` is a `dyn Trait`. The data address must point + /// to a value whose type implements the trait of `T` and the `vtable` must have + /// been extracted with [`vtable`]. + #[track_caller] + pub unsafe fn from_raw_parts( + data: *const (), + vtable: *const (), + ) -> *const T { + let fat = FatPointer { data, vtable }; + debug_assert_eq!(Layout::new::<*const T>(), Layout::new::()); + mem::transmute_copy::(&fat) + } + + /// Create a mutable fat pointer from a data address and a vtable address. + /// + /// # Safety + /// Must only be called when `T` is a `dyn Trait`. The data address must point + /// to a value whose type implements the trait of `T` and the `vtable` must have + /// been extracted with [`vtable`]. + #[track_caller] + pub unsafe fn from_raw_parts_mut( + data: *mut (), + vtable: *const (), + ) -> *mut T { + let fat = FatPointer { data, vtable }; + debug_assert_eq!(Layout::new::<*mut T>(), Layout::new::()); + mem::transmute_copy::(&fat) + } + + /// Extract the address to a trait object's vtable. + /// + /// # Safety + /// Must only be called when `T` is a `dyn Trait`. + #[track_caller] + pub unsafe fn vtable(ptr: *const T) -> *const () { + debug_assert_eq!(Layout::new::<*const T>(), Layout::new::()); + mem::transmute_copy::<*const T, FatPointer>(&ptr).vtable + } + + /// The memory representation of a trait object pointer. + /// + /// Although this is not guaranteed by Rust, it's improbable that it will + /// change. + #[repr(C)] + struct FatPointer { + data: *const (), + vtable: *const (), + } +} diff --git a/crates/typst/src/model/mod.rs b/crates/typst/src/model/mod.rs index 0c91f3867..de50f24ab 100644 --- a/crates/typst/src/model/mod.rs +++ b/crates/typst/src/model/mod.rs @@ -14,7 +14,7 @@ use ecow::EcoVec; pub use typst_macros::elem; pub use self::block::{Block, Blockable}; -pub use self::content::{Content, MetaElem, PlainText}; +pub use self::content::{fat, Content, MetaElem, PlainText}; pub use self::element::{ Construct, Element, ElementFields, LocalName, NativeElement, NativeElementData, Set, }; diff --git a/crates/typst/src/model/selector.rs b/crates/typst/src/model/selector.rs index 89c518bcb..c71569e6c 100644 --- a/crates/typst/src/model/selector.rs +++ b/crates/typst/src/model/selector.rs @@ -7,11 +7,11 @@ use smallvec::SmallVec; use super::{Content, Element, Label, Locatable, Location}; use crate::diag::{bail, StrResult}; +use crate::eval::repr::pretty_array_like; use crate::eval::{ cast, func, item, scope, ty, CastInfo, Dict, FromValue, Func, Reflect, Regex, Repr, Str, Symbol, Type, Value, }; -use crate::util::pretty_array_like; /// A helper macro to create a field selector used in [`Selector::Elem`] /// diff --git a/crates/typst/src/util/fat.rs b/crates/typst/src/util/fat.rs deleted file mode 100644 index d3c9bb200..000000000 --- a/crates/typst/src/util/fat.rs +++ /dev/null @@ -1,55 +0,0 @@ -//! Fat pointer handling. -//! -//! This assumes the memory representation of fat pointers. Although it is not -//! guaranteed by Rust, it's improbable that it will change. Still, when the -//! pointer metadata APIs are stable, we should definitely move to them: -//! - -use std::alloc::Layout; -use std::mem; - -/// Create a fat pointer from a data address and a vtable address. -/// -/// # Safety -/// Must only be called when `T` is a `dyn Trait`. The data address must point -/// to a value whose type implements the trait of `T` and the `vtable` must have -/// been extracted with [`vtable`]. -#[track_caller] -pub unsafe fn from_raw_parts(data: *const (), vtable: *const ()) -> *const T { - let fat = FatPointer { data, vtable }; - debug_assert_eq!(Layout::new::<*const T>(), Layout::new::()); - mem::transmute_copy::(&fat) -} - -/// Create a mutable fat pointer from a data address and a vtable address. -/// -/// # Safety -/// Must only be called when `T` is a `dyn Trait`. The data address must point -/// to a value whose type implements the trait of `T` and the `vtable` must have -/// been extracted with [`vtable`]. -#[track_caller] -pub unsafe fn from_raw_parts_mut(data: *mut (), vtable: *const ()) -> *mut T { - let fat = FatPointer { data, vtable }; - debug_assert_eq!(Layout::new::<*mut T>(), Layout::new::()); - mem::transmute_copy::(&fat) -} - -/// Extract the address to a trait object's vtable. -/// -/// # Safety -/// Must only be called when `T` is a `dyn Trait`. -#[track_caller] -pub unsafe fn vtable(ptr: *const T) -> *const () { - debug_assert_eq!(Layout::new::<*const T>(), Layout::new::()); - mem::transmute_copy::<*const T, FatPointer>(&ptr).vtable -} - -/// The memory representation of a trait object pointer. -/// -/// Although this is not guaranteed by Rust, it's improbable that it will -/// change. -#[repr(C)] -struct FatPointer { - data: *const (), - vtable: *const (), -} diff --git a/crates/typst/src/util/mod.rs b/crates/typst/src/util/mod.rs index 573d6b1c4..535344909 100644 --- a/crates/typst/src/util/mod.rs +++ b/crates/typst/src/util/mod.rs @@ -1,11 +1,8 @@ //! Utilities. -pub mod fat; -pub mod fmt; -mod str; +mod pico; -pub use self::fmt::{pretty_array_like, pretty_comma_list, separated_list}; -pub use self::str::PicoStr; +pub use self::pico::PicoStr; use std::fmt::{Debug, Formatter}; use std::hash::Hash; diff --git a/crates/typst/src/util/str.rs b/crates/typst/src/util/pico.rs similarity index 100% rename from crates/typst/src/util/str.rs rename to crates/typst/src/util/pico.rs diff --git a/tests/src/tests.rs b/tests/src/tests.rs index 0271839c5..3b33a729b 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -23,10 +23,10 @@ use walkdir::WalkDir; use typst::diag::{bail, FileError, FileResult, Severity, StrResult}; use typst::doc::{Document, Frame, FrameItem, Meta}; use typst::eval::{ - eco_format, func, Bytes, Datetime, Library, NoneValue, Repr, Tracer, Value, + eco_format, func, Bytes, Datetime, Library, NoneValue, Repr, Smart, Tracer, Value, }; use typst::font::{Font, FontBook}; -use typst::geom::{Abs, Color, Smart, Transform}; +use typst::geom::{Abs, Color, Transform}; use typst::syntax::{FileId, PackageVersion, Source, SyntaxNode, VirtualPath}; use typst::{World, WorldExt}; use typst_library::layout::{Margin, PageElem};