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};
12use typst_utils::ArcExt;
13
14use crate::diag::{DeprecationSink, HintedStrResult, HintedString, StrResult};
15use crate::foundations::{
16 Args, Array, AutoValue, Bytes, CastInfo, Content, Datetime, Decimal, Dict, Duration,
17 Fold, FromValue, Func, IntoValue, Label, Module, NativeElement, NativeType,
18 NoneValue, Reflect, Repr, Resolve, Scope, Str, Styles, Symbol, SymbolElem, Type,
19 Version, fields, ops, repr,
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) => {
161 symbol.clone().modified(sink, field).map(Self::Symbol)
162 }
163 Self::Version(version) => version.component(field).map(Self::Int),
164 Self::Dict(dict) => dict.get(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)]
497#[allow(clippy::derived_hash_with_manual_eq)]
498pub struct Dynamic(Arc<dyn Bounds>);
499
500impl Dynamic {
501 pub fn new<T>(any: T) -> Self
503 where
504 T: Debug + Repr + NativeType + PartialEq + Hash + Sync + Send + 'static,
505 {
506 Self(Arc::new(any))
507 }
508
509 pub fn is<T: 'static>(&self) -> bool {
511 (*self.0).as_any().is::<T>()
512 }
513
514 pub fn downcast<T: 'static>(&self) -> Option<&T> {
516 (*self.0).as_any().downcast_ref()
517 }
518
519 pub fn ty(&self) -> Type {
521 self.0.dyn_ty()
522 }
523}
524
525impl Debug for Dynamic {
526 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
527 self.0.fmt(f)
528 }
529}
530
531impl Repr for Dynamic {
532 fn repr(&self) -> EcoString {
533 self.0.repr()
534 }
535}
536
537impl PartialEq for Dynamic {
538 fn eq(&self, other: &Self) -> bool {
539 self.0.dyn_eq(other)
540 }
541}
542
543trait Bounds: Debug + Repr + Sync + Send + 'static {
544 fn as_any(&self) -> &dyn Any;
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 as_any(&self) -> &dyn Any {
555 self
556 }
557
558 fn dyn_eq(&self, other: &Dynamic) -> bool {
559 let Some(other) = other.downcast::<Self>() else { return false };
560 self == other
561 }
562
563 fn dyn_ty(&self) -> Type {
564 Type::of::<T>()
565 }
566
567 fn dyn_hash(&self, mut state: &mut dyn Hasher) {
568 TypeId::of::<Self>().hash(&mut state);
571 self.hash(&mut state);
572 }
573}
574
575impl Hash for dyn Bounds {
576 fn hash<H: Hasher>(&self, state: &mut H) {
577 self.dyn_hash(state);
578 }
579}
580
581macro_rules! primitive {
583 (
584 $ty:ty: $name:literal, $variant:ident
585 $(, $other:ident$(($binding:ident))? => $out:expr)*
586 ) => {
587 impl Reflect for $ty {
588 fn input() -> CastInfo {
589 CastInfo::Type(Type::of::<Self>())
590 }
591
592 fn output() -> CastInfo {
593 CastInfo::Type(Type::of::<Self>())
594 }
595
596 fn castable(value: &Value) -> bool {
597 matches!(value, Value::$variant(_)
598 $(| primitive!(@$other $(($binding))?))*)
599 }
600 }
601
602 impl IntoValue for $ty {
603 fn into_value(self) -> Value {
604 Value::$variant(self)
605 }
606 }
607
608 impl FromValue for $ty {
609 fn from_value(value: Value) -> HintedStrResult<Self> {
610 match value {
611 Value::$variant(v) => Ok(v),
612 $(Value::$other$(($binding))? => Ok($out),)*
613 v => Err(<Self as Reflect>::error(&v)),
614 }
615 }
616 }
617 };
618
619 (@$other:ident($binding:ident)) => { Value::$other(_) };
620 (@$other:ident) => { Value::$other };
621}
622
623primitive! { bool: "boolean", Bool }
624primitive! { i64: "integer", Int }
625primitive! { f64: "float", Float, Int(v) => v as f64 }
626primitive! { Length: "length", Length }
627primitive! { Angle: "angle", Angle }
628primitive! { Ratio: "ratio", Ratio }
629primitive! { Rel<Length>: "relative length",
630 Relative,
631 Length(v) => v.into(),
632 Ratio(v) => v.into()
633}
634primitive! { Fr: "fraction", Fraction }
635primitive! { Color: "color", Color }
636primitive! { Gradient: "gradient", Gradient }
637primitive! { Tiling: "tiling", Tiling }
638primitive! { Symbol: "symbol", Symbol }
639primitive! { Version: "version", Version }
640primitive! {
641 Str: "string",
642 Str,
643 Symbol(symbol) => symbol.get().into()
644}
645primitive! { Bytes: "bytes", Bytes }
646primitive! { Label: "label", Label }
647primitive! { Datetime: "datetime", Datetime }
648primitive! { Decimal: "decimal", Decimal }
649primitive! { Duration: "duration", Duration }
650primitive! { Content: "content",
651 Content,
652 None => Content::empty(),
653 Symbol(v) => SymbolElem::packed(v.get()),
654 Str(v) => TextElem::packed(v)
655}
656primitive! { Styles: "styles", Styles }
657primitive! { Array: "array", Array }
658primitive! { Dict: "dictionary", Dict }
659primitive! {
660 Func: "function",
661 Func,
662 Type(ty) => ty.constructor()?.clone(),
663 Symbol(symbol) => symbol.func()?
664}
665primitive! { Args: "arguments", Args }
666primitive! { Type: "type", Type }
667primitive! { Module: "module", Module }
668
669impl<T: Reflect> Reflect for Arc<T> {
670 fn input() -> CastInfo {
671 T::input()
672 }
673
674 fn output() -> CastInfo {
675 T::output()
676 }
677
678 fn castable(value: &Value) -> bool {
679 T::castable(value)
680 }
681
682 fn error(found: &Value) -> HintedString {
683 T::error(found)
684 }
685}
686
687impl<T: Clone + IntoValue> IntoValue for Arc<T> {
688 fn into_value(self) -> Value {
689 Arc::take(self).into_value()
690 }
691}
692
693impl<T: FromValue> FromValue for Arc<T> {
694 fn from_value(value: Value) -> HintedStrResult<Self> {
695 match value {
696 v if T::castable(&v) => Ok(Arc::new(T::from_value(v)?)),
697 _ => Err(Self::error(&value)),
698 }
699 }
700}
701
702impl<T: Clone + Resolve> Resolve for Arc<T> {
703 type Output = Arc<T::Output>;
704
705 fn resolve(self, styles: super::StyleChain) -> Self::Output {
706 Arc::new(Arc::take(self).resolve(styles))
707 }
708}
709
710impl<T: Clone + Fold> Fold for Arc<T> {
711 fn fold(self, outer: Self) -> Self {
712 Arc::new(Arc::take(self).fold(Arc::take(outer)))
713 }
714}
715
716#[cfg(test)]
717mod tests {
718 use super::*;
719 use crate::foundations::{array, dict};
720
721 #[track_caller]
722 fn test(value: impl IntoValue, exp: &str) {
723 assert_eq!(value.into_value().repr(), exp);
724 }
725
726 #[test]
727 fn test_value_size() {
728 assert!(std::mem::size_of::<Value>() <= 32);
729 }
730
731 #[test]
732 fn test_value_debug() {
733 test(Value::None, "none");
735 test(Value::Auto, "auto");
736 test(Value::None.ty(), "type(none)");
737 test(Value::Auto.ty(), "type(auto)");
738 test(false, "false");
739 test(12i64, "12");
740 test(3.24, "3.24");
741 test(Abs::pt(5.5), "5.5pt");
742 test(Angle::deg(90.0), "90deg");
743 test(Ratio::one() / 2.0, "50%");
744 test(Ratio::new(0.3) + Length::from(Abs::cm(2.0)), "30% + 56.69pt");
745 test(Fr::one() * 7.55, "7.55fr");
746
747 test("hello", r#""hello""#);
749 test("\n", r#""\n""#);
750 test("\\", r#""\\""#);
751 test("\"", r#""\"""#);
752 test(array![], "()");
753 test(array![Value::None], "(none,)");
754 test(array![1, 2], "(1, 2)");
755 test(dict![], "(:)");
756 test(dict!["one" => 1], "(one: 1)");
757 test(dict!["two" => false, "one" => 1], "(two: false, one: 1)");
758 }
759}