plist/
integer.rs

1use std::{fmt, num::ParseIntError};
2
3/// An integer that can be represented by either an `i64` or a `u64`.
4#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
5pub struct Integer {
6    value: i128,
7}
8
9impl Integer {
10    /// Returns the value as an `i64` if it can be represented by that type.
11    pub fn as_signed(self) -> Option<i64> {
12        i64::try_from(self.value).ok()
13    }
14
15    /// Returns the value as a `u64` if it can be represented by that type.
16    pub fn as_unsigned(self) -> Option<u64> {
17        u64::try_from(self.value).ok()
18    }
19
20    pub(crate) fn from_str(s: &str) -> Result<Self, ParseIntError> {
21        if s.starts_with("0x") {
22            // NetBSD dialect adds the `0x` numeric objects,
23            // which are always unsigned.
24            // See the `PROP_NUMBER(3)` man page
25            let s = s.trim_start_matches("0x");
26            u64::from_str_radix(s, 16).map(Into::into)
27        } else {
28            // Match Apple's implementation in CFPropertyList.h - always try to parse as an i64 first.
29            // TODO: Use IntErrorKind once stable and retry parsing on overflow only.
30            Ok(match s.parse::<i64>() {
31                Ok(v) => v.into(),
32                Err(_) => s.parse::<u64>()?.into(),
33            })
34        }
35    }
36}
37
38impl fmt::Debug for Integer {
39    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
40        self.value.fmt(f)
41    }
42}
43
44impl fmt::Display for Integer {
45    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
46        self.value.fmt(f)
47    }
48}
49
50impl From<i64> for Integer {
51    fn from(value: i64) -> Integer {
52        Integer {
53            value: value.into(),
54        }
55    }
56}
57
58impl From<i32> for Integer {
59    fn from(value: i32) -> Integer {
60        Integer {
61            value: value.into(),
62        }
63    }
64}
65
66impl From<i16> for Integer {
67    fn from(value: i16) -> Integer {
68        Integer {
69            value: value.into(),
70        }
71    }
72}
73
74impl From<i8> for Integer {
75    fn from(value: i8) -> Integer {
76        Integer {
77            value: value.into(),
78        }
79    }
80}
81
82impl From<u64> for Integer {
83    fn from(value: u64) -> Integer {
84        Integer {
85            value: value.into(),
86        }
87    }
88}
89
90impl From<u32> for Integer {
91    fn from(value: u32) -> Integer {
92        Integer {
93            value: value.into(),
94        }
95    }
96}
97
98impl From<u16> for Integer {
99    fn from(value: u16) -> Integer {
100        Integer {
101            value: value.into(),
102        }
103    }
104}
105
106impl From<u8> for Integer {
107    fn from(value: u8) -> Integer {
108        Integer {
109            value: value.into(),
110        }
111    }
112}
113
114#[cfg(feature = "serde")]
115pub mod serde_impls {
116    use serde::{
117        de::{Deserialize, Deserializer, Error, Visitor},
118        ser::{Serialize, Serializer},
119    };
120    use std::fmt;
121
122    use crate::Integer;
123
124    impl Serialize for Integer {
125        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
126        where
127            S: Serializer,
128        {
129            if let Some(v) = self.as_unsigned() {
130                serializer.serialize_u64(v)
131            } else if let Some(v) = self.as_signed() {
132                serializer.serialize_i64(v)
133            } else {
134                unreachable!();
135            }
136        }
137    }
138
139    struct IntegerVisitor;
140
141    impl Visitor<'_> for IntegerVisitor {
142        type Value = Integer;
143
144        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
145            formatter.write_str("a plist integer")
146        }
147
148        fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
149        where
150            E: Error,
151        {
152            Ok(Integer::from(v))
153        }
154
155        fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
156        where
157            E: Error,
158        {
159            Ok(Integer::from(v))
160        }
161    }
162
163    impl<'de> Deserialize<'de> for Integer {
164        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
165        where
166            D: Deserializer<'de>,
167        {
168            deserializer.deserialize_any(IntegerVisitor)
169        }
170    }
171}
172
173#[cfg(test)]
174mod tests {
175    use super::Integer;
176
177    #[test]
178    fn from_str_limits() {
179        assert_eq!(Integer::from_str("-1"), Ok((-1).into()));
180        assert_eq!(Integer::from_str("0"), Ok(0.into()));
181        assert_eq!(Integer::from_str("1"), Ok(1.into()));
182        assert_eq!(
183            Integer::from_str("-9223372036854775808"),
184            Ok((-9223372036854775808i64).into())
185        );
186        assert!(Integer::from_str("-9223372036854775809").is_err());
187        assert_eq!(
188            Integer::from_str("18446744073709551615"),
189            Ok(18446744073709551615u64.into())
190        );
191        assert!(Integer::from_str("18446744073709551616").is_err());
192    }
193}