objc2_encode/
encoding_box.rs

1use alloc::boxed::Box;
2use alloc::string::String;
3use alloc::vec::Vec;
4use core::fmt;
5use core::str::FromStr;
6
7use crate::helper::{compare_encodings, Helper, NestingLevel};
8use crate::parse::{ErrorKind, ParseError, Parser};
9use crate::Encoding;
10
11/// The boxed version of [`Encoding`].
12///
13/// This has exactly the same items as `Encoding`, the only difference is in
14/// where the contents of the more complex encodings like [`Struct`] are
15/// stored.
16///
17/// In `Encoding`, the data is stored in static memory, while in `EncodingBox`
18/// it is stored on the heap. The former allows storing in constants (which is
19/// required by the `objc2::encode::Encode` and `objc2::encode::RefEncode`
20/// traits), while the latter allows dynamic creation, such as in the case of
21/// parsing encodings.
22///
23/// **This should be considered a _temporary_ restriction**. `Encoding` and
24/// `EncodingBox` will become equivalent once heap allocation in constants
25/// is possible.
26///
27/// [`Struct`]: Self::Struct
28#[derive(Clone, Debug, PartialEq, Eq, Hash)]
29#[non_exhaustive] // Maybe we're missing some encodings?
30pub enum EncodingBox {
31    /// Same as [`Encoding::Char`].
32    Char,
33    /// Same as [`Encoding::Short`].
34    Short,
35    /// Same as [`Encoding::Int`].
36    Int,
37    /// Same as [`Encoding::Long`].
38    Long,
39    /// Same as [`Encoding::LongLong`].
40    LongLong,
41    /// Same as [`Encoding::UChar`].
42    UChar,
43    /// Same as [`Encoding::UShort`].
44    UShort,
45    /// Same as [`Encoding::UInt`].
46    UInt,
47    /// Same as [`Encoding::ULong`].
48    ULong,
49    /// Same as [`Encoding::ULongLong`].
50    ULongLong,
51    /// Same as [`Encoding::Float`].
52    Float,
53    /// Same as [`Encoding::Double`].
54    Double,
55    /// Same as [`Encoding::LongDouble`].
56    LongDouble,
57    /// Same as [`Encoding::FloatComplex`].
58    FloatComplex,
59    /// Same as [`Encoding::DoubleComplex`].
60    DoubleComplex,
61    /// Same as [`Encoding::LongDoubleComplex`].
62    LongDoubleComplex,
63    /// Same as [`Encoding::Bool`].
64    Bool,
65    /// Same as [`Encoding::Void`].
66    Void,
67    /// Same as [`Encoding::String`].
68    String,
69    /// Same as [`Encoding::Object`].
70    Object,
71    /// Same as [`Encoding::Block`].
72    Block,
73    /// Same as [`Encoding::Class`].
74    Class,
75    /// Same as [`Encoding::Sel`].
76    Sel,
77    /// Same as [`Encoding::Unknown`].
78    Unknown,
79    /// Same as [`Encoding::BitField`].
80    BitField(u8, Option<Box<(u64, Self)>>),
81    /// Same as [`Encoding::Pointer`].
82    Pointer(Box<Self>),
83    /// Same as [`Encoding::Atomic`].
84    Atomic(Box<Self>),
85    /// Same as [`Encoding::Array`].
86    Array(u64, Box<Self>),
87    /// Same as [`Encoding::Struct`].
88    Struct(String, Vec<Self>),
89    /// Same as [`Encoding::Union`].
90    Union(String, Vec<Self>),
91    /// Same as [`Encoding::None`].
92    None,
93}
94
95impl EncodingBox {
96    /// Same as [`Encoding::C_LONG`].
97    pub const C_LONG: Self = match Encoding::C_LONG {
98        Encoding::Long => Self::Long,
99        Encoding::LongLong => Self::LongLong,
100        _ => unreachable!(),
101    };
102
103    /// Same as [`Encoding::C_ULONG`].
104    pub const C_ULONG: Self = match Encoding::C_ULONG {
105        Encoding::ULong => Self::ULong,
106        Encoding::ULongLong => Self::ULongLong,
107        _ => unreachable!(),
108    };
109
110    /// Parse and consume an encoding from the start of a string.
111    ///
112    /// This is can be used to parse concatenated encodings, such as those
113    /// returned by `method_getTypeEncoding`.
114    ///
115    /// [`from_str`][Self::from_str] is simpler, use that instead if you can.
116    ///
117    ///
118    /// # Errors
119    ///
120    /// Returns an error if the string was an ill-formatted encoding string.
121    pub fn from_start_of_str(s: &mut &str) -> Result<Self, ParseError> {
122        let mut parser = Parser::new(s);
123        parser.strip_leading_qualifiers();
124
125        match parser.parse_encoding_or_none() {
126            Err(ErrorKind::Unknown(b'0'..=b'9')) => {
127                let remaining = parser.remaining();
128                *s = remaining;
129
130                Ok(EncodingBox::None)
131            }
132            Err(err) => Err(ParseError::new(parser, err)),
133            Ok(encoding) => {
134                let remaining = parser.remaining();
135                *s = remaining;
136
137                Ok(encoding)
138            }
139        }
140    }
141}
142
143/// Same formatting as [`Encoding`]'s `Display` implementation.
144impl fmt::Display for EncodingBox {
145    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146        Helper::from_box(self).fmt(f, NestingLevel::new())
147    }
148}
149
150impl PartialEq<Encoding> for EncodingBox {
151    fn eq(&self, other: &Encoding) -> bool {
152        compare_encodings(self, other, NestingLevel::new(), true)
153    }
154}
155
156impl PartialEq<EncodingBox> for Encoding {
157    fn eq(&self, other: &EncodingBox) -> bool {
158        other.eq(self)
159    }
160}
161
162impl FromStr for EncodingBox {
163    type Err = ParseError;
164
165    fn from_str(s: &str) -> Result<Self, Self::Err> {
166        let mut parser = Parser::new(s);
167        parser.strip_leading_qualifiers();
168
169        parser
170            .parse_encoding_or_none()
171            .and_then(|enc| parser.expect_empty().map(|()| enc))
172            .map_err(|err| ParseError::new(parser, err))
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179    use alloc::string::ToString;
180    use alloc::vec;
181
182    #[test]
183    fn eq_encodings() {
184        let enc1 = Encoding::Char;
185        let enc2 = EncodingBox::Char;
186        let enc3 = EncodingBox::String;
187        assert_eq!(enc1, enc2);
188        assert_ne!(enc1, enc3);
189    }
190
191    #[test]
192    fn eq_complex_encodings() {
193        let enc1 = Encoding::Atomic(&Encoding::Struct(
194            "test",
195            &[Encoding::Array(2, &Encoding::Int)],
196        ));
197        let enc2 = EncodingBox::Atomic(Box::new(EncodingBox::Struct(
198            "test".to_string(),
199            vec![EncodingBox::Array(2, Box::new(EncodingBox::Int))],
200        )));
201        let enc3 = EncodingBox::Atomic(Box::new(EncodingBox::Struct(
202            "test".to_string(),
203            vec![EncodingBox::Array(2, Box::new(EncodingBox::Char))],
204        )));
205        assert_eq!(enc1, enc2);
206        assert_ne!(enc1, enc3);
207    }
208
209    #[test]
210    fn struct_nested_in_pointer() {
211        let enc1 = EncodingBox::Struct("test".to_string(), vec![EncodingBox::Char]);
212        let enc2 = EncodingBox::Struct("test".to_string(), vec![EncodingBox::Int]);
213        const ENC3A: Encoding = Encoding::Struct("test", &[Encoding::Char]);
214        assert_ne!(enc1, enc2);
215        assert!(ENC3A.equivalent_to_box(&enc1));
216        assert!(!ENC3A.equivalent_to_box(&enc2));
217
218        let enc1 = EncodingBox::Pointer(Box::new(enc1));
219        let enc2 = EncodingBox::Pointer(Box::new(enc2));
220        const ENC3B: Encoding = Encoding::Pointer(&ENC3A);
221        assert_ne!(enc1, enc2);
222        assert!(ENC3B.equivalent_to_box(&enc1));
223        assert!(!ENC3B.equivalent_to_box(&enc2));
224
225        let enc1 = EncodingBox::Pointer(Box::new(enc1));
226        let enc2 = EncodingBox::Pointer(Box::new(enc2));
227        const ENC3C: Encoding = Encoding::Pointer(&ENC3B);
228        assert_ne!(enc1, enc2);
229        assert!(ENC3C.equivalent_to_box(&enc1));
230        assert!(ENC3C.equivalent_to_box(&enc2), "now they're equivalent");
231    }
232
233    #[test]
234    fn parse_atomic_struct() {
235        let expected = EncodingBox::Atomic(Box::new(EncodingBox::Atomic(Box::new(
236            EncodingBox::Struct("a".into(), vec![]),
237        ))));
238        let actual = EncodingBox::from_str("AA{a=}").unwrap();
239        assert_eq!(expected, actual);
240        assert_eq!(expected.to_string(), "AA{a}");
241
242        let actual = EncodingBox::from_str("AA{a}").unwrap();
243        assert_eq!(expected, actual);
244        assert_eq!(expected.to_string(), "AA{a}");
245    }
246
247    #[test]
248    fn parse_part_of_string() {
249        let mut s = "{a}cb0i16";
250
251        let expected = EncodingBox::Struct("a".into(), vec![]);
252        let actual = EncodingBox::from_start_of_str(&mut s).unwrap();
253        assert_eq!(expected, actual);
254
255        let expected = EncodingBox::Char;
256        let actual = EncodingBox::from_start_of_str(&mut s).unwrap();
257        assert_eq!(expected, actual);
258
259        let expected = EncodingBox::BitField(16, Some(Box::new((0, EncodingBox::Int))));
260        let actual = EncodingBox::from_start_of_str(&mut s).unwrap();
261        assert_eq!(expected, actual);
262
263        assert_eq!(s, "");
264    }
265}