1use std::any::{Any, TypeId};
2use std::cmp::Ordering;
3use std::fmt::{self, Debug, Formatter};
4use std::hash::{Hash, Hasher};
5use std::sync::Arc;
6
7use ecow::{eco_format, EcoString};
8use serde::de::value::{MapAccessDeserializer, SeqAccessDeserializer};
9use serde::de::{Error, MapAccess, SeqAccess, Visitor};
10use serde::{Deserialize, Deserializer, Serialize, Serializer};
11use typst_syntax::{ast, Span};
12use typst_utils::ArcExt;
13
14use crate::diag::{DeprecationSink, HintedStrResult, HintedString, StrResult};
15use crate::foundations::{
16 fields, ops, repr, Args, Array, AutoValue, Bytes, CastInfo, Content, Datetime,
17 Decimal, Dict, Duration, Fold, FromValue, Func, IntoValue, Label, Module,
18 NativeElement, NativeType, NoneValue, Reflect, Repr, Resolve, Scope, Str, Styles,
19 Symbol, SymbolElem, Type, Version,
20};
21use crate::layout::{Abs, Angle, Em, Fr, Length, Ratio, Rel};
22use crate::text::{RawContent, RawElem, TextElem};
23use crate::visualize::{Color, Gradient, Tiling};
24
25#[derive(Default, Clone)]
27pub enum Value {
28 #[default]
30 None,
31 Auto,
33 Bool(bool),
35 Int(i64),
37 Float(f64),
39 Length(Length),
41 Angle(Angle),
43 Ratio(Ratio),
45 Relative(Rel<Length>),
47 Fraction(Fr),
49 Color(Color),
51 Gradient(Gradient),
53 Tiling(Tiling),
55 Symbol(Symbol),
57 Version(Version),
59 Str(Str),
61 Bytes(Bytes),
63 Label(Label),
65 Datetime(Datetime),
67 Decimal(Decimal),
69 Duration(Duration),
71 Content(Content),
73 Styles(Styles),
75 Array(Array),
77 Dict(Dict),
79 Func(Func),
81 Args(Args),
83 Type(Type),
85 Module(Module),
87 Dyn(Dynamic),
89}
90
91impl Value {
92 pub fn dynamic<T>(any: T) -> Self
94 where
95 T: Debug + Repr + NativeType + PartialEq + Hash + Sync + Send + 'static,
96 {
97 Self::Dyn(Dynamic::new(any))
98 }
99
100 pub fn numeric(pair: (f64, ast::Unit)) -> Self {
102 let (v, unit) = pair;
103 match unit {
104 ast::Unit::Pt => Abs::pt(v).into_value(),
105 ast::Unit::Mm => Abs::mm(v).into_value(),
106 ast::Unit::Cm => Abs::cm(v).into_value(),
107 ast::Unit::In => Abs::inches(v).into_value(),
108 ast::Unit::Rad => Angle::rad(v).into_value(),
109 ast::Unit::Deg => Angle::deg(v).into_value(),
110 ast::Unit::Em => Em::new(v).into_value(),
111 ast::Unit::Fr => Fr::new(v).into_value(),
112 ast::Unit::Percent => Ratio::new(v / 100.0).into_value(),
113 }
114 }
115
116 pub fn ty(&self) -> Type {
118 match self {
119 Self::None => Type::of::<NoneValue>(),
120 Self::Auto => Type::of::<AutoValue>(),
121 Self::Bool(_) => Type::of::<bool>(),
122 Self::Int(_) => Type::of::<i64>(),
123 Self::Float(_) => Type::of::<f64>(),
124 Self::Length(_) => Type::of::<Length>(),
125 Self::Angle(_) => Type::of::<Angle>(),
126 Self::Ratio(_) => Type::of::<Ratio>(),
127 Self::Relative(_) => Type::of::<Rel<Length>>(),
128 Self::Fraction(_) => Type::of::<Fr>(),
129 Self::Color(_) => Type::of::<Color>(),
130 Self::Gradient(_) => Type::of::<Gradient>(),
131 Self::Tiling(_) => Type::of::<Tiling>(),
132 Self::Symbol(_) => Type::of::<Symbol>(),
133 Self::Version(_) => Type::of::<Version>(),
134 Self::Str(_) => Type::of::<Str>(),
135 Self::Bytes(_) => Type::of::<Bytes>(),
136 Self::Label(_) => Type::of::<Label>(),
137 Self::Datetime(_) => Type::of::<Datetime>(),
138 Self::Decimal(_) => Type::of::<Decimal>(),
139 Self::Duration(_) => Type::of::<Duration>(),
140 Self::Content(_) => Type::of::<Content>(),
141 Self::Styles(_) => Type::of::<Styles>(),
142 Self::Array(_) => Type::of::<Array>(),
143 Self::Dict(_) => Type::of::<Dict>(),
144 Self::Func(_) => Type::of::<Func>(),
145 Self::Args(_) => Type::of::<Args>(),
146 Self::Type(_) => Type::of::<Type>(),
147 Self::Module(_) => Type::of::<Module>(),
148 Self::Dyn(v) => v.ty(),
149 }
150 }
151
152 pub fn cast<T: FromValue>(self) -> HintedStrResult<T> {
154 T::from_value(self)
155 }
156
157 pub fn field(&self, field: &str, sink: impl DeprecationSink) -> StrResult<Value> {
159 match self {
160 Self::Symbol(symbol) => symbol.clone().modified(field).map(Self::Symbol),
161 Self::Version(version) => version.component(field).map(Self::Int),
162 Self::Dict(dict) => dict.get(field).cloned(),
163 Self::Content(content) => content.field_by_name(field),
164 Self::Type(ty) => ty.field(field, sink).cloned(),
165 Self::Func(func) => func.field(field, sink).cloned(),
166 Self::Module(module) => module.field(field, sink).cloned(),
167 _ => fields::field(self, field),
168 }
169 }
170
171 pub fn scope(&self) -> Option<&Scope> {
173 match self {
174 Self::Func(func) => func.scope(),
175 Self::Type(ty) => Some(ty.scope()),
176 Self::Module(module) => Some(module.scope()),
177 _ => None,
178 }
179 }
180
181 pub fn docs(&self) -> Option<&'static str> {
183 match self {
184 Self::Func(func) => func.docs(),
185 Self::Type(ty) => Some(ty.docs()),
186 _ => None,
187 }
188 }
189
190 pub fn display(self) -> Content {
192 match self {
193 Self::None => Content::empty(),
194 Self::Int(v) => TextElem::packed(repr::format_int_with_base(v, 10)),
195 Self::Float(v) => TextElem::packed(repr::display_float(v)),
196 Self::Decimal(v) => TextElem::packed(eco_format!("{v}")),
197 Self::Str(v) => TextElem::packed(v),
198 Self::Version(v) => TextElem::packed(eco_format!("{v}")),
199 Self::Symbol(v) => SymbolElem::packed(v.get()),
200 Self::Content(v) => v,
201 Self::Module(module) => module.content(),
202 _ => RawElem::new(RawContent::Text(self.repr()))
203 .with_lang(Some("typc".into()))
204 .with_block(false)
205 .pack(),
206 }
207 }
208
209 pub fn spanned(self, span: Span) -> Self {
211 match self {
212 Value::Content(v) => Value::Content(v.spanned(span)),
213 Value::Func(v) => Value::Func(v.spanned(span)),
214 v => v,
215 }
216 }
217}
218
219impl Debug for Value {
220 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
221 match self {
222 Self::None => Debug::fmt(&NoneValue, f),
223 Self::Auto => Debug::fmt(&AutoValue, f),
224 Self::Bool(v) => Debug::fmt(v, f),
225 Self::Int(v) => Debug::fmt(v, f),
226 Self::Float(v) => Debug::fmt(v, f),
227 Self::Length(v) => Debug::fmt(v, f),
228 Self::Angle(v) => Debug::fmt(v, f),
229 Self::Ratio(v) => Debug::fmt(v, f),
230 Self::Relative(v) => Debug::fmt(v, f),
231 Self::Fraction(v) => Debug::fmt(v, f),
232 Self::Color(v) => Debug::fmt(v, f),
233 Self::Gradient(v) => Debug::fmt(v, f),
234 Self::Tiling(v) => Debug::fmt(v, f),
235 Self::Symbol(v) => Debug::fmt(v, f),
236 Self::Version(v) => Debug::fmt(v, f),
237 Self::Str(v) => Debug::fmt(v, f),
238 Self::Bytes(v) => Debug::fmt(v, f),
239 Self::Label(v) => Debug::fmt(v, f),
240 Self::Datetime(v) => Debug::fmt(v, f),
241 Self::Decimal(v) => Debug::fmt(v, f),
242 Self::Duration(v) => Debug::fmt(v, f),
243 Self::Content(v) => Debug::fmt(v, f),
244 Self::Styles(v) => Debug::fmt(v, f),
245 Self::Array(v) => Debug::fmt(v, f),
246 Self::Dict(v) => Debug::fmt(v, f),
247 Self::Func(v) => Debug::fmt(v, f),
248 Self::Args(v) => Debug::fmt(v, f),
249 Self::Type(v) => Debug::fmt(v, f),
250 Self::Module(v) => Debug::fmt(v, f),
251 Self::Dyn(v) => Debug::fmt(v, f),
252 }
253 }
254}
255
256impl Repr for Value {
257 fn repr(&self) -> EcoString {
258 match self {
259 Self::None => NoneValue.repr(),
260 Self::Auto => AutoValue.repr(),
261 Self::Bool(v) => v.repr(),
262 Self::Int(v) => v.repr(),
263 Self::Float(v) => v.repr(),
264 Self::Length(v) => v.repr(),
265 Self::Angle(v) => v.repr(),
266 Self::Ratio(v) => v.repr(),
267 Self::Relative(v) => v.repr(),
268 Self::Fraction(v) => v.repr(),
269 Self::Color(v) => v.repr(),
270 Self::Gradient(v) => v.repr(),
271 Self::Tiling(v) => v.repr(),
272 Self::Symbol(v) => v.repr(),
273 Self::Version(v) => v.repr(),
274 Self::Str(v) => v.repr(),
275 Self::Bytes(v) => v.repr(),
276 Self::Label(v) => v.repr(),
277 Self::Datetime(v) => v.repr(),
278 Self::Decimal(v) => v.repr(),
279 Self::Duration(v) => v.repr(),
280 Self::Content(v) => v.repr(),
281 Self::Styles(v) => v.repr(),
282 Self::Array(v) => v.repr(),
283 Self::Dict(v) => v.repr(),
284 Self::Func(v) => v.repr(),
285 Self::Args(v) => v.repr(),
286 Self::Type(v) => v.repr(),
287 Self::Module(v) => v.repr(),
288 Self::Dyn(v) => v.repr(),
289 }
290 }
291}
292
293impl PartialEq for Value {
294 fn eq(&self, other: &Self) -> bool {
295 ops::equal(self, other, &mut ())
297 }
298}
299
300impl PartialOrd for Value {
301 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
302 ops::compare(self, other).ok()
303 }
304}
305
306impl Hash for Value {
307 fn hash<H: Hasher>(&self, state: &mut H) {
308 std::mem::discriminant(self).hash(state);
309 match self {
310 Self::None => {}
311 Self::Auto => {}
312 Self::Bool(v) => v.hash(state),
313 Self::Int(v) => v.hash(state),
314 Self::Float(v) => v.to_bits().hash(state),
315 Self::Length(v) => v.hash(state),
316 Self::Angle(v) => v.hash(state),
317 Self::Ratio(v) => v.hash(state),
318 Self::Relative(v) => v.hash(state),
319 Self::Fraction(v) => v.hash(state),
320 Self::Color(v) => v.hash(state),
321 Self::Gradient(v) => v.hash(state),
322 Self::Tiling(v) => v.hash(state),
323 Self::Symbol(v) => v.hash(state),
324 Self::Version(v) => v.hash(state),
325 Self::Str(v) => v.hash(state),
326 Self::Bytes(v) => v.hash(state),
327 Self::Label(v) => v.hash(state),
328 Self::Content(v) => v.hash(state),
329 Self::Styles(v) => v.hash(state),
330 Self::Datetime(v) => v.hash(state),
331 Self::Decimal(v) => v.hash(state),
332 Self::Duration(v) => v.hash(state),
333 Self::Array(v) => v.hash(state),
334 Self::Dict(v) => v.hash(state),
335 Self::Func(v) => v.hash(state),
336 Self::Args(v) => v.hash(state),
337 Self::Type(v) => v.hash(state),
338 Self::Module(v) => v.hash(state),
339 Self::Dyn(v) => v.hash(state),
340 }
341 }
342}
343
344impl Serialize for Value {
345 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
346 where
347 S: Serializer,
348 {
349 match self {
350 Self::None => NoneValue.serialize(serializer),
351 Self::Bool(v) => v.serialize(serializer),
352 Self::Int(v) => v.serialize(serializer),
353 Self::Float(v) => v.serialize(serializer),
354 Self::Str(v) => v.serialize(serializer),
355 Self::Bytes(v) => v.serialize(serializer),
356 Self::Symbol(v) => v.serialize(serializer),
357 Self::Content(v) => v.serialize(serializer),
358 Self::Array(v) => v.serialize(serializer),
359 Self::Dict(v) => v.serialize(serializer),
360
361 other => serializer.serialize_str(&other.repr()),
363 }
364 }
365}
366
367impl<'de> Deserialize<'de> for Value {
368 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
369 where
370 D: Deserializer<'de>,
371 {
372 deserializer.deserialize_any(ValueVisitor)
373 }
374}
375
376struct ValueVisitor;
378
379impl<'de> Visitor<'de> for ValueVisitor {
380 type Value = Value;
381
382 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
383 formatter.write_str("a typst value")
384 }
385
386 fn visit_bool<E: Error>(self, v: bool) -> Result<Self::Value, E> {
387 Ok(v.into_value())
388 }
389
390 fn visit_i8<E: Error>(self, v: i8) -> Result<Self::Value, E> {
391 Ok(v.into_value())
392 }
393
394 fn visit_i16<E: Error>(self, v: i16) -> Result<Self::Value, E> {
395 Ok(v.into_value())
396 }
397
398 fn visit_i32<E: Error>(self, v: i32) -> Result<Self::Value, E> {
399 Ok(v.into_value())
400 }
401
402 fn visit_i64<E: Error>(self, v: i64) -> Result<Self::Value, E> {
403 Ok(v.into_value())
404 }
405
406 fn visit_u8<E: Error>(self, v: u8) -> Result<Self::Value, E> {
407 Ok(v.into_value())
408 }
409
410 fn visit_u16<E: Error>(self, v: u16) -> Result<Self::Value, E> {
411 Ok(v.into_value())
412 }
413
414 fn visit_u32<E: Error>(self, v: u32) -> Result<Self::Value, E> {
415 Ok(v.into_value())
416 }
417
418 fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> {
419 Ok(v.into_value())
420 }
421
422 fn visit_f32<E: Error>(self, v: f32) -> Result<Self::Value, E> {
423 Ok((v as f64).into_value())
424 }
425
426 fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> {
427 Ok(v.into_value())
428 }
429
430 fn visit_char<E: Error>(self, v: char) -> Result<Self::Value, E> {
431 Ok(v.into_value())
432 }
433
434 fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
435 Ok(v.into_value())
436 }
437
438 fn visit_borrowed_str<E: Error>(self, v: &'de str) -> Result<Self::Value, E> {
439 Ok(v.into_value())
440 }
441
442 fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
443 Ok(v.into_value())
444 }
445
446 fn visit_bytes<E: Error>(self, v: &[u8]) -> Result<Self::Value, E> {
447 Ok(Bytes::new(v.to_vec()).into_value())
448 }
449
450 fn visit_borrowed_bytes<E: Error>(self, v: &'de [u8]) -> Result<Self::Value, E> {
451 Ok(Bytes::new(v.to_vec()).into_value())
452 }
453
454 fn visit_byte_buf<E: Error>(self, v: Vec<u8>) -> Result<Self::Value, E> {
455 Ok(Bytes::new(v).into_value())
456 }
457
458 fn visit_none<E: Error>(self) -> Result<Self::Value, E> {
459 Ok(Value::None)
460 }
461
462 fn visit_some<D: Deserializer<'de>>(
463 self,
464 deserializer: D,
465 ) -> Result<Self::Value, D::Error> {
466 Value::deserialize(deserializer)
467 }
468
469 fn visit_unit<E: Error>(self) -> Result<Self::Value, E> {
470 Ok(Value::None)
471 }
472
473 fn visit_seq<A: SeqAccess<'de>>(self, seq: A) -> Result<Self::Value, A::Error> {
474 Ok(Array::deserialize(SeqAccessDeserializer::new(seq))?.into_value())
475 }
476
477 fn visit_map<A: MapAccess<'de>>(self, map: A) -> Result<Self::Value, A::Error> {
478 let dict = Dict::deserialize(MapAccessDeserializer::new(map))?;
479 Ok(match Datetime::from_toml_dict(&dict) {
480 None => dict.into_value(),
481 Some(datetime) => datetime.into_value(),
482 })
483 }
484}
485
486#[derive(Clone, Hash)]
488#[allow(clippy::derived_hash_with_manual_eq)]
489pub struct Dynamic(Arc<dyn Bounds>);
490
491impl Dynamic {
492 pub fn new<T>(any: T) -> Self
494 where
495 T: Debug + Repr + NativeType + PartialEq + Hash + Sync + Send + 'static,
496 {
497 Self(Arc::new(any))
498 }
499
500 pub fn is<T: 'static>(&self) -> bool {
502 (*self.0).as_any().is::<T>()
503 }
504
505 pub fn downcast<T: 'static>(&self) -> Option<&T> {
507 (*self.0).as_any().downcast_ref()
508 }
509
510 pub fn ty(&self) -> Type {
512 self.0.dyn_ty()
513 }
514}
515
516impl Debug for Dynamic {
517 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
518 self.0.fmt(f)
519 }
520}
521
522impl Repr for Dynamic {
523 fn repr(&self) -> EcoString {
524 self.0.repr()
525 }
526}
527
528impl PartialEq for Dynamic {
529 fn eq(&self, other: &Self) -> bool {
530 self.0.dyn_eq(other)
531 }
532}
533
534trait Bounds: Debug + Repr + Sync + Send + 'static {
535 fn as_any(&self) -> &dyn Any;
536 fn dyn_eq(&self, other: &Dynamic) -> bool;
537 fn dyn_ty(&self) -> Type;
538 fn dyn_hash(&self, state: &mut dyn Hasher);
539}
540
541impl<T> Bounds for T
542where
543 T: Debug + Repr + NativeType + PartialEq + Hash + Sync + Send + 'static,
544{
545 fn as_any(&self) -> &dyn Any {
546 self
547 }
548
549 fn dyn_eq(&self, other: &Dynamic) -> bool {
550 let Some(other) = other.downcast::<Self>() else { return false };
551 self == other
552 }
553
554 fn dyn_ty(&self) -> Type {
555 Type::of::<T>()
556 }
557
558 fn dyn_hash(&self, mut state: &mut dyn Hasher) {
559 TypeId::of::<Self>().hash(&mut state);
562 self.hash(&mut state);
563 }
564}
565
566impl Hash for dyn Bounds {
567 fn hash<H: Hasher>(&self, state: &mut H) {
568 self.dyn_hash(state);
569 }
570}
571
572macro_rules! primitive {
574 (
575 $ty:ty: $name:literal, $variant:ident
576 $(, $other:ident$(($binding:ident))? => $out:expr)*
577 ) => {
578 impl Reflect for $ty {
579 fn input() -> CastInfo {
580 CastInfo::Type(Type::of::<Self>())
581 }
582
583 fn output() -> CastInfo {
584 CastInfo::Type(Type::of::<Self>())
585 }
586
587 fn castable(value: &Value) -> bool {
588 matches!(value, Value::$variant(_)
589 $(| primitive!(@$other $(($binding))?))*)
590 }
591 }
592
593 impl IntoValue for $ty {
594 fn into_value(self) -> Value {
595 Value::$variant(self)
596 }
597 }
598
599 impl FromValue for $ty {
600 fn from_value(value: Value) -> HintedStrResult<Self> {
601 match value {
602 Value::$variant(v) => Ok(v),
603 $(Value::$other$(($binding))? => Ok($out),)*
604 v => Err(<Self as Reflect>::error(&v)),
605 }
606 }
607 }
608 };
609
610 (@$other:ident($binding:ident)) => { Value::$other(_) };
611 (@$other:ident) => { Value::$other };
612}
613
614primitive! { bool: "boolean", Bool }
615primitive! { i64: "integer", Int }
616primitive! { f64: "float", Float, Int(v) => v as f64 }
617primitive! { Length: "length", Length }
618primitive! { Angle: "angle", Angle }
619primitive! { Ratio: "ratio", Ratio }
620primitive! { Rel<Length>: "relative length",
621 Relative,
622 Length(v) => v.into(),
623 Ratio(v) => v.into()
624}
625primitive! { Fr: "fraction", Fraction }
626primitive! { Color: "color", Color }
627primitive! { Gradient: "gradient", Gradient }
628primitive! { Tiling: "tiling", Tiling }
629primitive! { Symbol: "symbol", Symbol }
630primitive! { Version: "version", Version }
631primitive! {
632 Str: "string",
633 Str,
634 Symbol(symbol) => symbol.get().into()
635}
636primitive! { Bytes: "bytes", Bytes }
637primitive! { Label: "label", Label }
638primitive! { Datetime: "datetime", Datetime }
639primitive! { Decimal: "decimal", Decimal }
640primitive! { Duration: "duration", Duration }
641primitive! { Content: "content",
642 Content,
643 None => Content::empty(),
644 Symbol(v) => SymbolElem::packed(v.get()),
645 Str(v) => TextElem::packed(v)
646}
647primitive! { Styles: "styles", Styles }
648primitive! { Array: "array", Array }
649primitive! { Dict: "dictionary", Dict }
650primitive! {
651 Func: "function",
652 Func,
653 Type(ty) => ty.constructor()?.clone(),
654 Symbol(symbol) => symbol.func()?
655}
656primitive! { Args: "arguments", Args }
657primitive! { Type: "type", Type }
658primitive! { Module: "module", Module }
659
660impl<T: Reflect> Reflect for Arc<T> {
661 fn input() -> CastInfo {
662 T::input()
663 }
664
665 fn output() -> CastInfo {
666 T::output()
667 }
668
669 fn castable(value: &Value) -> bool {
670 T::castable(value)
671 }
672
673 fn error(found: &Value) -> HintedString {
674 T::error(found)
675 }
676}
677
678impl<T: Clone + IntoValue> IntoValue for Arc<T> {
679 fn into_value(self) -> Value {
680 Arc::take(self).into_value()
681 }
682}
683
684impl<T: FromValue> FromValue for Arc<T> {
685 fn from_value(value: Value) -> HintedStrResult<Self> {
686 match value {
687 v if T::castable(&v) => Ok(Arc::new(T::from_value(v)?)),
688 _ => Err(Self::error(&value)),
689 }
690 }
691}
692
693impl<T: Clone + Resolve> Resolve for Arc<T> {
694 type Output = Arc<T::Output>;
695
696 fn resolve(self, styles: super::StyleChain) -> Self::Output {
697 Arc::new(Arc::take(self).resolve(styles))
698 }
699}
700
701impl<T: Clone + Fold> Fold for Arc<T> {
702 fn fold(self, outer: Self) -> Self {
703 Arc::new(Arc::take(self).fold(Arc::take(outer)))
704 }
705}
706
707#[cfg(test)]
708mod tests {
709 use super::*;
710 use crate::foundations::{array, dict};
711
712 #[track_caller]
713 fn test(value: impl IntoValue, exp: &str) {
714 assert_eq!(value.into_value().repr(), exp);
715 }
716
717 #[test]
718 fn test_value_size() {
719 assert!(std::mem::size_of::<Value>() <= 32);
720 }
721
722 #[test]
723 fn test_value_debug() {
724 test(Value::None, "none");
726 test(Value::Auto, "auto");
727 test(Value::None.ty(), "type(none)");
728 test(Value::Auto.ty(), "type(auto)");
729 test(false, "false");
730 test(12i64, "12");
731 test(3.24, "3.24");
732 test(Abs::pt(5.5), "5.5pt");
733 test(Angle::deg(90.0), "90deg");
734 test(Ratio::one() / 2.0, "50%");
735 test(Ratio::new(0.3) + Length::from(Abs::cm(2.0)), "30% + 56.69pt");
736 test(Fr::one() * 7.55, "7.55fr");
737
738 test("hello", r#""hello""#);
740 test("\n", r#""\n""#);
741 test("\\", r#""\\""#);
742 test("\"", r#""\"""#);
743 test(array![], "()");
744 test(array![Value::None], "(none,)");
745 test(array![1, 2], "(1, 2)");
746 test(dict![], "(:)");
747 test(dict!["one" => 1], "(one: 1)");
748 test(dict!["two" => false, "one" => 1], "(two: false, one: 1)");
749 }
750}