typst_library/foundations/
dict.rs1use std::fmt::{Debug, Formatter};
2use std::hash::{Hash, Hasher};
3use std::ops::{Add, AddAssign};
4use std::sync::Arc;
5
6use ecow::{eco_format, EcoString};
7use indexmap::IndexMap;
8use serde::{Deserialize, Deserializer, Serialize, Serializer};
9use typst_syntax::is_ident;
10use typst_utils::ArcExt;
11
12use crate::diag::{Hint, HintedStrResult, StrResult};
13use crate::foundations::{
14 array, cast, func, repr, scope, ty, Array, Module, Repr, Str, Value,
15};
16
17#[macro_export]
19#[doc(hidden)]
20macro_rules! __dict {
21 ($($key:expr => $value:expr),* $(,)?) => {{
22 #[allow(unused_mut)]
23 let mut map = $crate::foundations::IndexMap::new();
24 $(map.insert($key.into(), $crate::foundations::IntoValue::into_value($value));)*
25 $crate::foundations::Dict::from(map)
26 }};
27}
28
29#[doc(inline)]
30pub use crate::__dict as dict;
31
32#[ty(scope, cast, name = "dictionary")]
68#[derive(Default, Clone, PartialEq)]
69pub struct Dict(Arc<IndexMap<Str, Value>>);
70
71impl Dict {
72 pub fn new() -> Self {
74 Self::default()
75 }
76
77 pub fn is_empty(&self) -> bool {
79 self.0.is_empty()
80 }
81
82 pub fn get(&self, key: &str) -> StrResult<&Value> {
84 self.0.get(key).ok_or_else(|| missing_key(key))
85 }
86
87 pub fn at_mut(&mut self, key: &str) -> HintedStrResult<&mut Value> {
89 Arc::make_mut(&mut self.0)
90 .get_mut(key)
91 .ok_or_else(|| missing_key(key))
92 .hint("use `insert` to add or update values")
93 }
94
95 pub fn take(&mut self, key: &str) -> StrResult<Value> {
97 Arc::make_mut(&mut self.0)
98 .shift_remove(key)
99 .ok_or_else(|| missing_key(key))
100 }
101
102 pub fn contains(&self, key: &str) -> bool {
104 self.0.contains_key(key)
105 }
106
107 pub fn clear(&mut self) {
109 if Arc::strong_count(&self.0) == 1 {
110 Arc::make_mut(&mut self.0).clear();
111 } else {
112 *self = Self::new();
113 }
114 }
115
116 pub fn iter(&self) -> indexmap::map::Iter<Str, Value> {
118 self.0.iter()
119 }
120
121 pub fn finish(&self, expected: &[&str]) -> StrResult<()> {
124 let mut iter = self.iter().peekable();
125 if iter.peek().is_none() {
126 return Ok(());
127 }
128 let unexpected: Vec<&str> = iter.map(|kv| kv.0.as_str()).collect();
129
130 Err(Self::unexpected_keys(unexpected, Some(expected)))
131 }
132
133 pub fn unexpected_keys(
135 unexpected: Vec<&str>,
136 hint_expected: Option<&[&str]>,
137 ) -> EcoString {
138 let format_as_list = |arr: &[&str]| {
139 repr::separated_list(
140 &arr.iter().map(|s| eco_format!("\"{s}\"")).collect::<Vec<_>>(),
141 "and",
142 )
143 };
144
145 let mut msg = String::from(match unexpected.len() {
146 1 => "unexpected key ",
147 _ => "unexpected keys ",
148 });
149
150 msg.push_str(&format_as_list(&unexpected[..]));
151
152 if let Some(expected) = hint_expected {
153 msg.push_str(", valid keys are ");
154 msg.push_str(&format_as_list(expected));
155 }
156
157 msg.into()
158 }
159}
160
161#[scope]
162impl Dict {
163 #[func(constructor)]
173 pub fn construct(
174 value: ToDict,
176 ) -> Dict {
177 value.0
178 }
179
180 #[func(title = "Length")]
182 pub fn len(&self) -> usize {
183 self.0.len()
184 }
185
186 #[func]
192 pub fn at(
193 &self,
194 key: Str,
196 #[named]
198 default: Option<Value>,
199 ) -> StrResult<Value> {
200 self.0
201 .get(&key)
202 .cloned()
203 .or(default)
204 .ok_or_else(|| missing_key_no_default(&key))
205 }
206
207 #[func]
210 pub fn insert(
211 &mut self,
212 key: Str,
214 value: Value,
216 ) {
217 Arc::make_mut(&mut self.0).insert(key, value);
218 }
219
220 #[func]
222 pub fn remove(
223 &mut self,
224 key: Str,
226 #[named]
228 default: Option<Value>,
229 ) -> StrResult<Value> {
230 Arc::make_mut(&mut self.0)
231 .shift_remove(&key)
232 .or(default)
233 .ok_or_else(|| missing_key(&key))
234 }
235
236 #[func]
238 pub fn keys(&self) -> Array {
239 self.0.keys().cloned().map(Value::Str).collect()
240 }
241
242 #[func]
244 pub fn values(&self) -> Array {
245 self.0.values().cloned().collect()
246 }
247
248 #[func]
251 pub fn pairs(&self) -> Array {
252 self.0
253 .iter()
254 .map(|(k, v)| Value::Array(array![k.clone(), v.clone()]))
255 .collect()
256 }
257}
258
259pub struct ToDict(Dict);
261
262cast! {
263 ToDict,
264 v: Module => Self(v
265 .scope()
266 .iter()
267 .map(|(k, b)| (Str::from(k.clone()), b.read().clone()))
268 .collect()
269 ),
270}
271
272impl Debug for Dict {
273 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
274 f.debug_map().entries(self.0.iter()).finish()
275 }
276}
277
278impl Repr for Dict {
279 fn repr(&self) -> EcoString {
280 if self.is_empty() {
281 return "(:)".into();
282 }
283
284 let max = 40;
285 let mut pieces: Vec<_> = self
286 .iter()
287 .take(max)
288 .map(|(key, value)| {
289 if is_ident(key) {
290 eco_format!("{key}: {}", value.repr())
291 } else {
292 eco_format!("{}: {}", key.repr(), value.repr())
293 }
294 })
295 .collect();
296
297 if self.len() > max {
298 pieces.push(eco_format!(".. ({} pairs omitted)", self.len() - max));
299 }
300
301 repr::pretty_array_like(&pieces, false).into()
302 }
303}
304
305impl Add for Dict {
306 type Output = Self;
307
308 fn add(mut self, rhs: Dict) -> Self::Output {
309 self += rhs;
310 self
311 }
312}
313
314impl AddAssign for Dict {
315 fn add_assign(&mut self, rhs: Dict) {
316 match Arc::try_unwrap(rhs.0) {
317 Ok(map) => self.extend(map),
318 Err(rc) => self.extend(rc.iter().map(|(k, v)| (k.clone(), v.clone()))),
319 }
320 }
321}
322
323impl Hash for Dict {
324 fn hash<H: Hasher>(&self, state: &mut H) {
325 state.write_usize(self.0.len());
326 for item in self {
327 item.hash(state);
328 }
329 }
330}
331
332impl Serialize for Dict {
333 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
334 where
335 S: Serializer,
336 {
337 self.0.serialize(serializer)
338 }
339}
340
341impl<'de> Deserialize<'de> for Dict {
342 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
343 where
344 D: Deserializer<'de>,
345 {
346 Ok(IndexMap::<Str, Value>::deserialize(deserializer)?.into())
347 }
348}
349
350impl Extend<(Str, Value)> for Dict {
351 fn extend<T: IntoIterator<Item = (Str, Value)>>(&mut self, iter: T) {
352 Arc::make_mut(&mut self.0).extend(iter);
353 }
354}
355
356impl FromIterator<(Str, Value)> for Dict {
357 fn from_iter<T: IntoIterator<Item = (Str, Value)>>(iter: T) -> Self {
358 Self(Arc::new(iter.into_iter().collect()))
359 }
360}
361
362impl IntoIterator for Dict {
363 type Item = (Str, Value);
364 type IntoIter = indexmap::map::IntoIter<Str, Value>;
365
366 fn into_iter(self) -> Self::IntoIter {
367 Arc::take(self.0).into_iter()
368 }
369}
370
371impl<'a> IntoIterator for &'a Dict {
372 type Item = (&'a Str, &'a Value);
373 type IntoIter = indexmap::map::Iter<'a, Str, Value>;
374
375 fn into_iter(self) -> Self::IntoIter {
376 self.iter()
377 }
378}
379
380impl From<IndexMap<Str, Value>> for Dict {
381 fn from(map: IndexMap<Str, Value>) -> Self {
382 Self(Arc::new(map))
383 }
384}
385
386#[cold]
388fn missing_key(key: &str) -> EcoString {
389 eco_format!("dictionary does not contain key {}", key.repr())
390}
391
392#[cold]
394fn missing_key_no_default(key: &str) -> EcoString {
395 eco_format!(
396 "dictionary does not contain key {} \
397 and no default value was specified",
398 key.repr()
399 )
400}