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