1mod integer;
4mod real;
5
6#[cfg(feature = "serde")]
7pub(crate) mod de;
8#[cfg(feature = "serde")]
9pub(crate) mod ser;
10
11use indexmap::IndexMap;
12
13pub use self::integer::Integer;
14pub use self::real::Real;
15use crate::date::Date;
16#[cfg(any(test, feature = "xml", feature = "openstep"))]
17use crate::scalar;
18use crate::uid::Uid;
19
20pub type Dictionary = IndexMap<String, Value>;
26
27#[derive(Clone, Debug, PartialEq)]
49#[non_exhaustive]
50pub enum Value {
51 Dictionary(Dictionary),
53 Array(Vec<Self>),
55 String(String),
57 Integer(Integer),
59 Real(Real),
61 Boolean(bool),
63 Uid(Uid),
65 Data(Vec<u8>),
67 Date(Date),
69}
70
71impl Value {
72 #[cfg(any(test, feature = "serde"))]
75 pub(crate) const fn type_name(&self) -> &'static str {
76 match self {
77 Self::Dictionary(_) => "dictionary",
78 Self::Array(_) => "array",
79 Self::String(_) => "string",
80 Self::Integer(_) => "integer",
81 Self::Real(_) => "real",
82 Self::Boolean(_) => "boolean",
83 Self::Uid(_) => "UID",
84 Self::Data(_) => "data",
85 Self::Date(_) => "date",
86 }
87 }
88
89 #[must_use]
101 pub const fn as_dictionary(&self) -> Option<&Dictionary> {
102 match self {
103 Self::Dictionary(dict) => Some(dict),
104 _ => None,
105 }
106 }
107
108 #[must_use]
122 pub const fn as_dictionary_mut(&mut self) -> Option<&mut Dictionary> {
123 match self {
124 Self::Dictionary(dict) => Some(dict),
125 _ => None,
126 }
127 }
128
129 #[must_use]
141 pub const fn as_array(&self) -> Option<&Vec<Self>> {
142 match self {
143 Self::Array(values) => Some(values),
144 _ => None,
145 }
146 }
147
148 #[must_use]
162 pub const fn as_array_mut(&mut self) -> Option<&mut Vec<Self>> {
163 match self {
164 Self::Array(values) => Some(values),
165 _ => None,
166 }
167 }
168
169 #[must_use]
180 pub const fn as_str(&self) -> Option<&str> {
181 match self {
182 Self::String(s) => Some(s.as_str()),
183 _ => None,
184 }
185 }
186
187 #[must_use]
198 pub const fn as_integer(&self) -> Option<Integer> {
199 match self {
200 Self::Integer(integer) => Some(*integer),
201 _ => None,
202 }
203 }
204
205 #[must_use]
216 pub const fn as_real(&self) -> Option<f64> {
217 match self {
218 Self::Real(real) => Some(real.value()),
219 _ => None,
220 }
221 }
222
223 #[must_use]
234 pub const fn as_boolean(&self) -> Option<bool> {
235 match self {
236 Self::Boolean(b) => Some(*b),
237 _ => None,
238 }
239 }
240
241 #[must_use]
252 pub const fn as_data(&self) -> Option<&[u8]> {
253 match self {
254 Self::Data(data) => Some(data.as_slice()),
255 _ => None,
256 }
257 }
258
259 #[must_use]
273 pub const fn as_date(&self) -> Option<Date> {
274 match self {
275 Self::Date(date) => Some(*date),
276 _ => None,
277 }
278 }
279
280 #[must_use]
291 pub const fn as_uid(&self) -> Option<Uid> {
292 match self {
293 Self::Uid(uid) => Some(*uid),
294 _ => None,
295 }
296 }
297
298 #[must_use]
313 pub fn into_dictionary(self) -> Option<Dictionary> {
314 match self {
315 Self::Dictionary(dict) => Some(dict),
316 _ => None,
317 }
318 }
319
320 #[must_use]
334 pub fn into_array(self) -> Option<Vec<Self>> {
335 match self {
336 Self::Array(values) => Some(values),
337 _ => None,
338 }
339 }
340
341 #[must_use]
352 pub fn into_string(self) -> Option<String> {
353 match self {
354 Self::String(s) => Some(s),
355 _ => None,
356 }
357 }
358
359 #[must_use]
370 pub fn into_data(self) -> Option<Vec<u8>> {
371 match self {
372 Self::Data(data) => Some(data),
373 _ => None,
374 }
375 }
376}
377
378#[cfg(any(test, feature = "xml", feature = "openstep"))]
387pub(crate) fn maybe_uid(entries: Vec<(String, Value)>, lax: bool) -> Value {
388 if let [(key, value)] = entries.as_slice()
389 && key == "CF$UID"
390 {
391 match value {
392 Value::Integer(integer) => return Value::Uid(Uid::from(integer.to_raw_parts().1)),
393 Value::String(s) if lax => {
394 if let Ok(uid) = scalar::parse_u64(s, 10) {
395 return Value::Uid(Uid::from(uid));
396 }
397 }
398 _ => {}
399 }
400 }
401 Value::Dictionary(entries.into_iter().collect())
402}
403
404impl From<&str> for Value {
405 fn from(value: &str) -> Self {
406 Self::String(value.to_owned())
407 }
408}
409
410impl From<String> for Value {
411 fn from(value: String) -> Self {
412 Self::String(value)
413 }
414}
415
416impl From<bool> for Value {
417 fn from(value: bool) -> Self {
418 Self::Boolean(value)
419 }
420}
421
422impl From<f64> for Value {
423 fn from(value: f64) -> Self {
424 Self::Real(Real::from(value))
425 }
426}
427
428impl From<Real> for Value {
429 fn from(value: Real) -> Self {
430 Self::Real(value)
431 }
432}
433
434impl From<Integer> for Value {
435 fn from(value: Integer) -> Self {
436 Self::Integer(value)
437 }
438}
439
440impl From<Uid> for Value {
441 fn from(value: Uid) -> Self {
442 Self::Uid(value)
443 }
444}
445
446impl From<Date> for Value {
447 fn from(value: Date) -> Self {
448 Self::Date(value)
449 }
450}
451
452impl From<Vec<Self>> for Value {
453 fn from(value: Vec<Self>) -> Self {
454 Self::Array(value)
455 }
456}
457
458impl From<Dictionary> for Value {
459 fn from(value: Dictionary) -> Self {
460 Self::Dictionary(value)
461 }
462}
463
464impl From<Vec<u8>> for Value {
467 fn from(value: Vec<u8>) -> Self {
468 Self::Data(value)
469 }
470}
471
472macro_rules! impl_from_int {
473 ($($ty:ty),+) => {$(
474 impl From<$ty> for Value {
475 fn from(value: $ty) -> Self {
476 Self::Integer(Integer::from(value))
477 }
478 }
479 )+};
480}
481
482impl_from_int!(i8, i16, i32, i64, u8, u16, u32, u64);
483
484impl FromIterator<Self> for Value {
485 fn from_iter<T: IntoIterator<Item = Self>>(iter: T) -> Self {
486 Self::Array(iter.into_iter().collect())
487 }
488}
489
490impl FromIterator<(String, Self)> for Value {
493 fn from_iter<T: IntoIterator<Item = (String, Self)>>(iter: T) -> Self {
494 Self::Dictionary(iter.into_iter().collect())
495 }
496}
497
498#[cfg(test)]
499mod tests {
500 use super::*;
501
502 fn entry(key: &str, value: Value) -> (String, Value) {
503 (key.to_owned(), value)
504 }
505
506 #[test]
507 fn type_names_are_verbatim() {
508 let cases = [
509 (Value::Dictionary(Dictionary::new()), "dictionary"),
510 (Value::Array(Vec::new()), "array"),
511 (Value::from(""), "string"),
512 (Value::from(0u8), "integer"),
513 (Value::from(0.0), "real"),
514 (Value::from(false), "boolean"),
515 (Value::from(Uid::from(0)), "UID"),
516 (Value::from(Vec::<u8>::new()), "data"),
517 (
518 Value::from(Date::from(std::time::SystemTime::UNIX_EPOCH)),
519 "date",
520 ),
521 ];
522 for (value, want) in cases {
523 assert_eq!(value.type_name(), want);
524 }
525 }
526
527 #[test]
528 fn accessors_are_some_only_on_the_matching_variant() {
529 let string = Value::from("s");
530 assert_eq!(string.as_str(), Some("s"));
531 assert!(string.as_integer().is_none());
532 assert!(string.as_real().is_none());
533 assert!(string.as_boolean().is_none());
534 assert!(string.as_data().is_none());
535 assert!(string.as_date().is_none());
536 assert!(string.as_uid().is_none());
537 assert!(string.as_dictionary().is_none());
538 assert!(string.as_array().is_none());
539
540 assert!(Value::from(5i64).as_real().is_none());
542 assert!(Value::from(vec![0x68u8]).as_str().is_none());
543 }
544
545 #[test]
546 fn mutable_and_consuming_accessors_work() {
547 let mut value = Value::from(Dictionary::new());
548 if let Some(dict) = value.as_dictionary_mut() {
549 drop(dict.insert("k".to_owned(), Value::from(1u8)));
550 }
551 assert_eq!(value.clone().into_dictionary().map(|d| d.len()), Some(1));
552 assert!(value.into_array().is_none());
553
554 let mut array = Value::from(vec![Value::from(1u8)]);
555 if let Some(values) = array.as_array_mut() {
556 values.push(Value::from(2u8));
557 }
558 assert_eq!(array.into_array().map(|v| v.len()), Some(2));
559
560 assert_eq!(Value::from("hi").into_string(), Some("hi".to_owned()));
561 assert_eq!(Value::from(vec![1u8]).into_data(), Some(vec![1u8]));
562 assert!(Value::from(1u8).into_string().is_none());
563 assert!(Value::from("x").into_data().is_none());
564 }
565
566 #[test]
567 fn byte_vectors_become_data_not_arrays() {
568 assert_eq!(Value::from(vec![104u8, 105]).type_name(), "data");
569 let array: Value = [Value::from(104u8), Value::from(105u8)]
570 .into_iter()
571 .collect();
572 assert_eq!(array.type_name(), "array");
573 }
574
575 #[test]
576 fn from_f64_is_wide_and_from_real_preserves_width() {
577 assert_eq!(Value::from(1.5).as_real(), Some(1.5));
578 let narrow = Value::from(Real::from(1.5f32));
579 assert_eq!(narrow.as_real(), Some(1.5));
580 }
581
582 #[test]
583 fn dictionary_from_iterator_keeps_the_last_duplicate_key() {
584 let value: Value = [
585 entry("dup", Value::from(1u8)),
586 entry("other", Value::from(2u8)),
587 entry("dup", Value::from(3u8)),
588 ]
589 .into_iter()
590 .collect();
591 let dict = value.as_dictionary().cloned().unwrap_or_default();
592 assert_eq!(dict.get("dup"), Some(&Value::from(3u8)));
593 assert_eq!(dict.len(), 2);
594 }
595
596 #[test]
597 fn dictionary_iterates_in_insertion_order() {
598 let value: Value = [
599 entry("zebra", Value::from(1u8)),
600 entry("Alpha", Value::from(2u8)),
601 entry("alpha", Value::from(3u8)),
602 ]
603 .into_iter()
604 .collect();
605 let keys: Vec<&str> = value
606 .as_dictionary()
607 .into_iter()
608 .flatten()
609 .map(|(k, _)| k.as_str())
610 .collect();
611 assert_eq!(keys, ["zebra", "Alpha", "alpha"]);
612 }
613
614 #[test]
615 fn maybe_uid_collapses_the_single_integer_pair() {
616 let converted = maybe_uid(vec![entry("CF$UID", Value::from(255u8))], false);
617 assert_eq!(converted, Value::Uid(Uid::from(255)));
618 }
619
620 #[test]
621 fn maybe_uid_ignores_signedness() {
622 let converted = maybe_uid(vec![entry("CF$UID", Value::from(-1i64))], false);
623 assert_eq!(converted, Value::Uid(Uid::from(u64::MAX)));
624 }
625
626 #[test]
627 fn maybe_uid_accepts_numeric_strings_only_when_lax() {
628 let lax = maybe_uid(vec![entry("CF$UID", Value::from("12"))], true);
629 assert_eq!(lax.as_uid(), Some(Uid::from(12)));
630
631 let strict = maybe_uid(vec![entry("CF$UID", Value::from("12"))], false);
632 assert!(strict.as_dictionary().is_some());
633 }
634
635 #[test]
636 fn maybe_uid_lax_strings_parse_as_u64() {
637 for bad in ["+5", "-1", "0x10", "5_0", "", "18446744073709551616", " 5"] {
638 let value = maybe_uid(vec![entry("CF$UID", Value::from(bad))], true);
639 assert!(value.as_dictionary().is_some(), "{bad}");
640 }
641 let max = maybe_uid(
642 vec![entry("CF$UID", Value::from("18446744073709551615"))],
643 true,
644 );
645 assert_eq!(max.as_uid(), Some(Uid::from(u64::MAX)));
646 }
647
648 #[test]
649 fn maybe_uid_leaves_other_shapes_as_dictionaries() {
650 let duplicate = maybe_uid(
652 vec![
653 entry("CF$UID", Value::from(1u8)),
654 entry("CF$UID", Value::from(2u8)),
655 ],
656 true,
657 );
658 let dict = duplicate.as_dictionary().cloned().unwrap_or_default();
659 assert_eq!(dict.len(), 1);
660 assert_eq!(dict.get("CF$UID"), Some(&Value::from(2u8)));
661
662 for value in [
663 maybe_uid(vec![entry("cf$uid", Value::from(1u8))], true),
664 maybe_uid(vec![entry("CF$UID ", Value::from(1u8))], true),
665 maybe_uid(vec![entry("CF$UID", Value::from(1.0))], true),
666 maybe_uid(vec![entry("CF$UID", Value::from(true))], true),
667 maybe_uid(Vec::new(), true),
668 maybe_uid(
669 vec![
670 entry("CF$UID", Value::from(1u8)),
671 entry("extra", Value::from(2u8)),
672 ],
673 true,
674 ),
675 ] {
676 assert!(value.as_dictionary().is_some());
677 }
678 }
679
680 #[test]
681 fn value_equality_follows_payload_semantics() {
682 assert_eq!(Value::from(5i64), Value::from(5u64));
683 assert_ne!(Value::from(f64::NAN), Value::from(f64::NAN));
684 assert_eq!(Value::from(Real::from(2.0f32)), Value::from(2.0f64));
685 }
686}