diff --git a/crates/typst/src/foundations/dict.rs b/crates/typst/src/foundations/dict.rs index 06c8f0e48..e77d33ff1 100644 --- a/crates/typst/src/foundations/dict.rs +++ b/crates/typst/src/foundations/dict.rs @@ -8,7 +8,9 @@ use indexmap::IndexMap; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::diag::{Hint, HintedStrResult, StrResult}; -use crate::foundations::{array, func, repr, scope, ty, Array, Repr, Str, Value}; +use crate::foundations::{ + array, cast, func, repr, scope, ty, Array, Module, Repr, Str, Value, +}; use crate::syntax::is_ident; use crate::util::ArcExt; @@ -158,6 +160,23 @@ impl Dict { #[scope] impl Dict { + /// Converts a value into a dictionary. + /// + /// Note that this function is only intended for conversion of a + /// dictionary-like value to a dictionary, not for creation of a dictionary + /// from individual pairs. Use the dictionary syntax `(key: value)` instead. + /// + /// ```example + /// #dictionary(sys).at("version") + /// ``` + #[func(constructor)] + pub fn construct( + /// The value that should be converted to a dictionary. + value: ToDict, + ) -> Dict { + value.0 + } + /// The number of pairs in the dictionary. #[func(title = "Length")] pub fn len(&self) -> usize { @@ -237,6 +256,14 @@ impl Dict { } } +/// A value that can be cast to dictionary. +pub struct ToDict(Dict); + +cast! { + ToDict, + v: Module => Self(v.scope().iter().map(|(k, v)| (Str::from(k.clone()), v.clone())).collect()), +} + impl Debug for Dict { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_map().entries(self.0.iter()).finish() diff --git a/tests/typ/compiler/dict.typ b/tests/typ/compiler/dict.typ index eca1c4582..8a5be5cd0 100644 --- a/tests/typ/compiler/dict.typ +++ b/tests/typ/compiler/dict.typ @@ -83,6 +83,11 @@ #test("c" in dict, false) #test(dict, (a: 3, b: 1)) +--- +// Test dictionary constructor +#dictionary(sys).at("version") +#dictionary(sys).at("no_crash", default: none) + --- // Test that removal keeps order. #let dict = (a: 1, b: 2, c: 3, d: 4)