objc2_encode/
encoding.rs

1use core::fmt;
2
3use crate::helper::{compare_encodings, Helper, NestingLevel};
4use crate::parse::Parser;
5use crate::EncodingBox;
6
7/// An Objective-C type-encoding.
8///
9/// Can be retrieved in Objective-C for a type `T` using the `@encode(T)`
10/// directive.
11/// ```objc
12/// NSLog(@"Encoding of NSException: %s", @encode(NSException));
13/// ```
14///
15/// The [`Display`][`fmt::Display`] implementation converts the [`Encoding`]
16/// into its string representation, that the the `@encode` directive would
17/// return. This can be used conveniently through the `to_string` method:
18///
19/// ```
20/// use objc2_encode::Encoding;
21/// assert_eq!(Encoding::Int.to_string(), "i");
22/// ```
23///
24/// For more information on the string value of an encoding, see [Apple's
25/// documentation][ocrtTypeEncodings].
26///
27/// [ocrtTypeEncodings]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
28///
29/// # Examples
30///
31/// Comparing an encoding to a string from the Objective-C runtime:
32///
33/// ```
34/// use objc2_encode::Encoding;
35/// assert!(Encoding::Array(10, &Encoding::FloatComplex).equivalent_to_str("[10jf]"));
36/// ```
37// Not `Copy`, since this may one day be merged with `EncodingBox`
38#[allow(missing_copy_implementations)]
39#[derive(Clone, Debug, PartialEq, Eq, Hash)]
40// See <https://en.cppreference.com/w/c/language/type>
41#[non_exhaustive] // Maybe we're missing some encodings?
42pub enum Encoding {
43    /// A C `char`. Corresponds to the `"c"` code.
44    Char,
45    /// A C `short`. Corresponds to the `"s"` code.
46    Short,
47    /// A C `int`. Corresponds to the `"i"` code.
48    Int,
49    /// A C `long`. Corresponds to the `"l"` code.
50    ///
51    /// This is treated as a 32-bit quantity in 64-bit programs, see
52    /// [`Encoding::C_LONG`].
53    Long,
54    /// A C `long long`. Corresponds to the `"q"` code.
55    LongLong,
56    /// A C `unsigned char`. Corresponds to the `"C"` code.
57    UChar,
58    /// A C `unsigned short`. Corresponds to the `"S"` code.
59    UShort,
60    /// A C `unsigned int`. Corresponds to the `"I"` code.
61    UInt,
62    /// A C `unsigned long`. Corresponds to the `"L"` code.
63    ///
64    /// See [`Encoding::C_ULONG`].
65    ULong,
66    /// A C `unsigned long long`. Corresponds to the `"Q"` code.
67    ULongLong,
68    /// A C `float`. Corresponds to the `"f"` code.
69    Float,
70    /// A C `double`. Corresponds to the `"d"` code.
71    Double,
72    /// A C `long double`. Corresponds to the `"D"` code.
73    LongDouble,
74    /// A C `float _Complex`. Corresponds to the `"j" "f"` code.
75    FloatComplex,
76    /// A C `_Complex` or `double _Complex`. Corresponds to the `"j" "d"` code.
77    DoubleComplex,
78    /// A C `long double _Complex`. Corresponds to the `"j" "D"` code.
79    LongDoubleComplex,
80    /// A C++ `bool` / C99 `_Bool`. Corresponds to the `"B"` code.
81    Bool,
82    /// A C `void`. Corresponds to the `"v"` code.
83    Void,
84    /// A C `char *`. Corresponds to the `"*"` code.
85    String,
86    /// An Objective-C object (`id`). Corresponds to the `"@"` code.
87    ///
88    /// Some compilers may choose to store the name of the class in instance
89    /// variables and properties as `"@" class_name`, see [Extended Type Info
90    /// in Objective-C][ext] (note that this does not include generics).
91    ///
92    /// Such class names are currently ignored by `objc2-encode`.
93    ///
94    /// [ext]: https://bou.io/ExtendedTypeInfoInObjC.html
95    Object,
96    /// An Objective-C block. Corresponds to the `"@" "?"` code.
97    Block,
98    /// An Objective-C class (`Class`). Corresponds to the `"#"` code.
99    Class,
100    /// An Objective-C selector (`SEL`). Corresponds to the `":"` code.
101    Sel,
102    /// An unknown type. Corresponds to the `"?"` code.
103    ///
104    /// This is usually used to encode functions.
105    Unknown,
106    /// A bitfield with the given number of bits, and the given type.
107    ///
108    /// Corresponds to the `"b" size` code.
109    ///
110    /// On GNUStep, this uses the `"b" offset type size` code, so this
111    /// contains an `Option` that should be set for that. Only integral types
112    /// are possible for the type.
113    ///
114    /// A `BitField(_, Some(_))` and a `BitField(_, None)` do _not_ compare
115    /// equal; instead, you should set the bitfield correctly depending on the
116    /// target platform.
117    BitField(u8, Option<&'static (u64, Encoding)>),
118    /// A pointer to the given type.
119    ///
120    /// Corresponds to the `"^" type` code.
121    Pointer(&'static Encoding),
122    /// A C11 [`_Atomic`] type.
123    ///
124    /// Corresponds to the `"A" type` code. Not all encodings are possible in
125    /// this.
126    ///
127    /// [`_Atomic`]: https://en.cppreference.com/w/c/language/atomic
128    Atomic(&'static Encoding),
129    /// An array with the given length and type.
130    ///
131    /// Corresponds to the `"[" length type "]"` code.
132    Array(u64, &'static Encoding),
133    /// A struct with the given name and fields.
134    ///
135    /// The order of the fields must match the order of the order in this.
136    ///
137    /// It is not uncommon for the name to be `"?"`.
138    ///
139    /// Corresponds to the `"{" name "=" fields... "}"` code.
140    ///
141    /// Note that the `=` may be omitted in some situations; this is
142    /// considered equal to the case where there are no fields.
143    Struct(&'static str, &'static [Encoding]),
144    /// A union with the given name and members.
145    ///
146    /// The order of the members must match the order of the order in this.
147    ///
148    /// Corresponds to the `"(" name "=" members... ")"` code.
149    ///
150    /// Note that the `=` may be omitted in some situations; this is
151    /// considered equal to the case where there are no members.
152    Union(&'static str, &'static [Encoding]),
153    /// The type does not have an Objective-C encoding.
154    ///
155    /// This is usually only used on types where Clang fails to generate the
156    /// Objective-C encoding, like SIMD types marked with
157    /// `__attribute__((__ext_vector_type__(1)))`.
158    None,
159    // TODO: "Vector" types have the '!' encoding, but are not implemented in
160    // clang
161
162    // TODO: `t` and `T` codes for i128 and u128?
163}
164
165impl Encoding {
166    /// The encoding of [`c_long`](`std::os::raw::c_long`) on the current
167    /// target.
168    ///
169    /// Ideally the encoding of `long` would be the same as `int` when it's 32
170    /// bits wide and the same as `long long` when it is 64 bits wide; then
171    /// `c_long::ENCODING` would just work.
172    ///
173    /// Unfortunately, `long` have a different encoding than `int` when it is
174    /// 32 bits wide; the [`l`][`Encoding::Long`] encoding.
175    pub const C_LONG: Self = {
176        // TODO once `core::ffi::c_long` is in MSRV
177        // `mem::size_of::<c_long>() == 4`
178        //
179        // That would exactly match what `clang` does:
180        // https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/AST/ASTContext.cpp#L7245
181        if cfg!(any(target_pointer_width = "32", windows)) {
182            // @encode(long) = 'l'
183            Self::Long
184        } else {
185            // @encode(long) = 'q'
186            Self::LongLong
187        }
188    };
189
190    /// The encoding of [`c_ulong`](`std::os::raw::c_ulong`) on the current
191    /// target.
192    ///
193    /// See [`Encoding::C_LONG`] for explanation.
194    pub const C_ULONG: Self = {
195        if cfg!(any(target_pointer_width = "32", windows)) {
196            // @encode(unsigned long) = 'L'
197            Self::ULong
198        } else {
199            // @encode(unsigned long) = 'Q'
200            Self::ULongLong
201        }
202    };
203
204    /// Check if one encoding is equivalent to another.
205    ///
206    /// Currently, equivalence testing mostly requires that the encodings are
207    /// equal, except for:
208    /// - Any leading qualifiers that the encoding may have.
209    /// - Structs or unions behind multiple pointers are considered
210    ///   equivalent, since Objective-C compilers strip this information to
211    ///   avoid unnecessary nesting.
212    /// - Structs or unions with no fields/members are considered to represent
213    ///   "opqaue" types, and will therefore be equivalent to all other
214    ///   structs / unions.
215    /// - [`Object`], [`Block`] and [`Class`] compare as equivalent.
216    ///
217    /// The comparison may be changed in the future to e.g. ignore struct
218    /// names or similar changes that may be required because of limitations
219    /// in Objective-C compiler implementations.
220    ///
221    /// For example, you should not rely on two equivalent encodings to have
222    /// the same size or ABI - that is provided on a best-effort basis.
223    ///
224    /// [`Object`]: Self::Object
225    /// [`Block`]: Self::Block
226    /// [`Class`]: Self::Class
227    pub fn equivalent_to(&self, other: &Self) -> bool {
228        compare_encodings(self, other, NestingLevel::new(), false)
229    }
230
231    /// Check if an encoding is equivalent to the given string representation.
232    ///
233    /// See [`Encoding::equivalent_to`] for details about the meaning of
234    /// "equivalence".
235    pub fn equivalent_to_str(&self, s: &str) -> bool {
236        let mut parser = Parser::new(s);
237
238        parser.strip_leading_qualifiers();
239
240        if let Some(()) = parser.expect_encoding(self, NestingLevel::new()) {
241            // if the given encoding can be successfully removed from the
242            // start and an empty string remains, they were fully equivalent!
243            parser.is_empty()
244        } else {
245            false
246        }
247    }
248
249    /// Check if an encoding is equivalent to a boxed encoding.
250    ///
251    /// See [`Encoding::equivalent_to`] for details about the meaning of
252    /// "equivalence".
253    pub fn equivalent_to_box(&self, other: &EncodingBox) -> bool {
254        compare_encodings(self, other, NestingLevel::new(), false)
255    }
256
257    /// Computes the theoretical size in bytes of the represented value type.
258    ///
259    /// The size is only valid for the current target.
260    ///
261    /// This does not currently consider alignment, i.e. everything is
262    /// considered packed, but that may change in the future.
263    pub fn size(&self) -> Option<usize> {
264        Helper::new(self).size(NestingLevel::new())
265    }
266}
267
268/// Formats this [`Encoding`] in a similar way that the `@encode` directive
269/// would ordinarily do.
270///
271/// You should not rely on the output of this to be stable across versions. It
272/// may change if found to be required to be compatible with existing
273/// Objective-C compilers.
274impl fmt::Display for Encoding {
275    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
276        Helper::new(self).fmt(f, NestingLevel::new())
277    }
278}
279
280#[cfg(test)]
281mod tests {
282    use super::*;
283    use crate::static_str::{static_encoding_str_array, static_encoding_str_len};
284    use alloc::boxed::Box;
285    use alloc::string::ToString;
286    use alloc::vec;
287    use core::str::FromStr;
288
289    fn send_sync<T: Send + Sync>() {}
290
291    #[test]
292    fn test_send_sync() {
293        send_sync::<Encoding>();
294    }
295
296    #[test]
297    fn smoke() {
298        assert!(Encoding::Short.equivalent_to_str("s"));
299    }
300
301    #[test]
302    fn qualifiers() {
303        assert!(Encoding::Void.equivalent_to_str("v"));
304        assert!(Encoding::Void.equivalent_to_str("Vv"));
305        assert!(Encoding::String.equivalent_to_str("*"));
306        assert!(Encoding::String.equivalent_to_str("r*"));
307    }
308
309    macro_rules! assert_enc {
310        ($(
311            fn $name:ident() {
312                $encoding:expr;
313                $(
314                    ~$equivalent_encoding:expr;
315                )*
316                $(
317                    !$not_encoding:expr;
318                )*
319                $string:literal;
320                $(
321                    ~$equivalent_string:expr;
322                )*
323                $(
324                    !$not_string:literal;
325                )*
326            }
327        )+) => {$(
328            #[test]
329            fn $name() {
330                const E: Encoding = $encoding;
331
332                // Check PartialEq
333                assert_eq!(E, E, "equal");
334
335                // Check Display
336                assert_eq!(E.to_string(), $string, "equal to string");
337
338                // Check encoding box
339                let boxed = EncodingBox::from_str($string).expect("parse");
340                assert_eq!(boxed.to_string(), $string, "parsed");
341
342                // Check equivalence comparisons
343                assert!(E.equivalent_to(&E), "equivalent self");
344                assert!(E.equivalent_to_str($string), "equivalent self string {}", $string);
345                assert!(E.equivalent_to_box(&boxed), "equivalent self boxed");
346                $(
347                    assert!(E.equivalent_to(&$equivalent_encoding), "equivalent encoding {}", $equivalent_encoding);
348                    assert!(E.equivalent_to_str(&$equivalent_encoding.to_string()), "equivalent encoding string");
349                    let boxed = EncodingBox::from_str(&$equivalent_encoding.to_string()).expect("parse equivalent encoding");
350                    assert!(E.equivalent_to_box(&boxed), "equivalent encoding boxed");
351                )*
352                $(
353                    assert!(E.equivalent_to_str($equivalent_string), "equivalent string {}", $equivalent_string);
354                    let boxed = EncodingBox::from_str($equivalent_string).expect("parse equivalent string");
355                    assert!(E.equivalent_to_box(&boxed), "equivalent string boxed");
356                )*
357
358                // Negative checks
359                $(
360                    assert_ne!(E, $not_encoding, "not equal");
361                    assert!(!E.equivalent_to(&$not_encoding), "not equivalent encoding");
362                    assert!(!E.equivalent_to_str(&$not_encoding.to_string()), "not equivalent encoding string");
363                    let boxed = EncodingBox::from_str(&$not_encoding.to_string()).expect("parse not equivalent encoding");
364                    assert!(!E.equivalent_to_box(&boxed), "not equivalent boxed");
365                )*
366                $(
367                    assert!(!E.equivalent_to_str(&$not_string), "not equivalent string");
368                )*
369
370                // Check static str
371                const STATIC_ENCODING_DATA: [u8; static_encoding_str_len(&E, NestingLevel::new())] = static_encoding_str_array(&E, NestingLevel::new());
372                const STATIC_ENCODING_STR: &str = unsafe { core::str::from_utf8_unchecked(&STATIC_ENCODING_DATA) };
373                assert_eq!(STATIC_ENCODING_STR, $string, "static");
374            }
375        )+};
376    }
377
378    assert_enc! {
379        fn int() {
380            Encoding::Int;
381            !Encoding::Char;
382            "i";
383        }
384
385        fn char() {
386            Encoding::Char;
387            !Encoding::Int;
388            "c";
389            // Qualifiers
390            ~"rc";
391            ~"nc";
392            ~"Nc";
393            ~"oc";
394            ~"Oc";
395            ~"Rc";
396            ~"Vc";
397
398            !"ri";
399        }
400
401        fn block() {
402            Encoding::Block;
403            ~Encoding::Class;
404            ~Encoding::Object;
405            !Encoding::Unknown;
406            "@?";
407        }
408
409        fn object() {
410            Encoding::Object;
411            ~Encoding::Block;
412            ~Encoding::Class;
413            !Encoding::Sel;
414            "@";
415            ~"@\"AnyClassName\"";
416            ~"@\"\""; // Empty class name
417            ~"@?";
418            ~"#";
419            !"@\"MyClassName";
420            !"@MyClassName\"";
421        }
422
423        fn unknown() {
424            Encoding::Unknown;
425            !Encoding::Block;
426            "?";
427        }
428
429        fn double() {
430            Encoding::Double;
431            "d";
432        }
433
434        fn bitfield() {
435            Encoding::BitField(4, None);
436            !Encoding::Int;
437            !Encoding::BitField(5, None);
438            !Encoding::BitField(4, Some(&(0, Encoding::Bool)));
439            "b4";
440            !"b4a";
441            !"b4c";
442            !"b4B";
443            !"b";
444            !"b-4";
445            !"b0B4";
446        }
447
448        fn bitfield_gnustep() {
449            Encoding::BitField(4, Some(&(16, Encoding::Bool)));
450            !Encoding::Int;
451            !Encoding::BitField(4, None);
452            !Encoding::BitField(5, Some(&(16, Encoding::Bool)));
453            !Encoding::BitField(4, Some(&(20, Encoding::Bool)));
454            !Encoding::BitField(4, Some(&(16, Encoding::Char)));
455            "b16B4";
456            !"b4";
457            !"b16B";
458            !"b20B4";
459            !"b16B5";
460            !"b16c4";
461            !"b4a";
462            !"b";
463            !"b-4";
464        }
465
466        fn atomic() {
467            Encoding::Atomic(&Encoding::Int);
468            !Encoding::Pointer(&Encoding::Int);
469            !Encoding::Atomic(&Encoding::Char);
470            !Encoding::Atomic(&Encoding::Atomic(&Encoding::Int));
471            "Ai";
472        }
473
474        fn atomic_string() {
475            Encoding::Atomic(&Encoding::String);
476            "A*";
477        }
478
479        fn pointer() {
480            Encoding::Pointer(&Encoding::Int);
481            !Encoding::Atomic(&Encoding::Int);
482            !Encoding::Pointer(&Encoding::Char);
483            !Encoding::Pointer(&Encoding::Pointer(&Encoding::Int));
484            "^i";
485        }
486
487        fn array() {
488            Encoding::Array(12, &Encoding::Int);
489            !Encoding::Int;
490            !Encoding::Array(11, &Encoding::Int);
491            !Encoding::Array(12, &Encoding::Char);
492            "[12i]";
493            !"[12i";
494        }
495
496        fn struct_() {
497            Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int]);
498            ~Encoding::Struct("SomeStruct", &[]);
499            !Encoding::Union("SomeStruct", &[Encoding::Char, Encoding::Int]);
500            !Encoding::Int;
501            !Encoding::Struct("SomeStruct", &[Encoding::Int]);
502            !Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int, Encoding::Int]);
503            !Encoding::Struct("SomeStruct", &[Encoding::Int, Encoding::Char]);
504            !Encoding::Struct("AnotherName", &[Encoding::Char, Encoding::Int]);
505            "{SomeStruct=ci}";
506            ~"{SomeStruct=}";
507            !"{SomeStruct}";
508            !"{SomeStruct=ic}";
509            !"{SomeStruct=malformed";
510        }
511
512        fn pointer_struct() {
513            Encoding::Pointer(&Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int]));
514            ~Encoding::Pointer(&Encoding::Struct("SomeStruct", &[]));
515            !Encoding::Pointer(&Encoding::Struct("SomeStruct", &[Encoding::Int, Encoding::Char]));
516            !Encoding::Pointer(&Encoding::Struct("AnotherName", &[Encoding::Char, Encoding::Int]));
517            "^{SomeStruct=ci}";
518            ~"^{SomeStruct=}";
519            !"^{SomeStruct}";
520            !"^{SomeStruct=ic}";
521            !"^{SomeStruct=malformed";
522        }
523
524        fn pointer_pointer_struct() {
525            Encoding::Pointer(&Encoding::Pointer(&Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int])));
526            ~Encoding::Pointer(&Encoding::Pointer(&Encoding::Struct("SomeStruct", &[])));
527            ~Encoding::Pointer(&Encoding::Pointer(&Encoding::Struct("SomeStruct", &[Encoding::Int, Encoding::Char])));
528            !Encoding::Pointer(&Encoding::Pointer(&Encoding::Struct("AnotherName", &[Encoding::Char, Encoding::Int])));
529            "^^{SomeStruct}";
530            !"^^{SomeStruct=}";
531            !"^^{SomeStruct=ci}";
532            !"^^{SomeStruct=ic}";
533            !"^^{AnotherName=ic}";
534            !"^^{SomeStruct=malformed";
535        }
536
537        fn atomic_struct() {
538            Encoding::Atomic(&Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int]));
539            ~Encoding::Atomic(&Encoding::Struct("SomeStruct", &[]));
540            ~Encoding::Atomic(&Encoding::Struct("SomeStruct", &[Encoding::Int, Encoding::Char]));
541            !Encoding::Atomic(&Encoding::Struct("AnotherName", &[Encoding::Char, Encoding::Int]));
542            "A{SomeStruct}";
543            !"A{SomeStruct=}";
544            !"A{SomeStruct=ci}";
545            !"A{SomeStruct=ic}";
546            !"A{SomeStruct=malformed";
547        }
548
549        fn empty_struct() {
550            Encoding::Struct("SomeStruct", &[]);
551            "{SomeStruct=}";
552            ~"{SomeStruct=ci}";
553            !"{SomeStruct}";
554        }
555
556        fn union_() {
557            Encoding::Union("Onion", &[Encoding::Char, Encoding::Int]);
558            !Encoding::Struct("Onion", &[Encoding::Char, Encoding::Int]);
559            !Encoding::Int;
560            !Encoding::Union("Onion", &[Encoding::Int, Encoding::Char]);
561            !Encoding::Union("AnotherUnion", &[Encoding::Char, Encoding::Int]);
562            "(Onion=ci)";
563            !"(Onion=ci";
564        }
565
566        fn nested() {
567            Encoding::Struct(
568                "A",
569                &[
570                    Encoding::Struct("B", &[Encoding::Int]),
571                    Encoding::Pointer(&Encoding::Struct("C", &[Encoding::Double])),
572                    Encoding::Char,
573                ],
574            );
575            ~Encoding::Struct(
576                "A",
577                &[
578                    Encoding::Struct("B", &[Encoding::Int]),
579                    Encoding::Pointer(&Encoding::Struct("C", &[])),
580                    Encoding::Char,
581                ],
582            );
583            "{A={B=i}^{C}c}";
584            !"{A={B=i}^{C=d}c}";
585            !"{A={B=i}^{C=i}c}";
586            !"{A={B=i}^{C=d}c";
587        }
588
589        fn nested_pointer() {
590            Encoding::Pointer(&Encoding::Struct(
591                "A",
592                &[
593                    Encoding::Struct("B", &[Encoding::Int]),
594                    Encoding::Pointer(&Encoding::Struct("C", &[Encoding::Double])),
595                ],
596            ));
597            "^{A={B=i}^{C}}";
598            !"^{A={B}^{C}}";
599            !"^{A={B=i}^{C=d}}";
600        }
601
602        fn various() {
603            Encoding::Struct(
604                "abc",
605                &[
606                    Encoding::Pointer(&Encoding::Array(8, &Encoding::Bool)),
607                    Encoding::Union("def", &[Encoding::Block]),
608                    Encoding::Pointer(&Encoding::Pointer(&Encoding::BitField(255, None))),
609                    Encoding::Char,
610                    Encoding::Unknown,
611                ]
612            );
613            "{abc=^[8B](def=@?)^^b255c?}";
614            ~"{abc=}";
615            !"{abc}";
616        }
617
618        fn identifier() {
619            Encoding::Struct("_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", &[]);
620            "{_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=}";
621        }
622
623        // Regression test. The encoding of the `CGLContextObj` object changed
624        // between versions of macOS. As such, this is something that we must
625        // be prepared to handle.
626        fn cgl_context_obj() {
627            Encoding::Pointer(&Encoding::Struct("_CGLContextObject", &[]));
628            "^{_CGLContextObject=}";
629            ~"^{_CGLContextObject=^{__GLIContextRec}{__GLIFunctionDispatchRec=^?^?^?^?^?}^{_CGLPrivateObject}^v}";
630            !"^{_CGLContextObject}";
631            !"^{SomeOtherStruct=}";
632        }
633
634        fn none() {
635            Encoding::None;
636            "";
637            !"?";
638        }
639
640        fn none_in_array() {
641            Encoding::Array(42, &Encoding::None);
642            !Encoding::Array(42, &Encoding::Unknown);
643            "[42]";
644            !"[42i]";
645        }
646
647        fn none_in_pointer() {
648            Encoding::Pointer(&Encoding::None);
649            !Encoding::Pointer(&Encoding::Unknown);
650            "^";
651            !"";
652            !"^i";
653        }
654
655        fn none_in_pointer_in_array() {
656            Encoding::Array(42, &Encoding::Pointer(&Encoding::None));
657            "[42^]";
658        }
659
660        fn class() {
661            Encoding::Class;
662            ~Encoding::Object;
663            ~Encoding::Block;
664            !Encoding::Sel;
665            "#";
666            ~"@?";
667            ~"@";
668            !"a";
669        }
670    }
671
672    #[test]
673    #[should_panic = "Struct name was not a valid identifier"]
674    fn struct_empty() {
675        let _ = Encoding::Struct("", &[]).to_string();
676    }
677
678    #[test]
679    #[should_panic = "Struct name was not a valid identifier"]
680    fn struct_unicode() {
681        let _ = Encoding::Struct("☃", &[Encoding::Char]).to_string();
682    }
683
684    #[test]
685    #[should_panic = "Union name was not a valid identifier"]
686    fn union_invalid_identifier() {
687        let _ = Encoding::Union("a-b", &[Encoding::Char]).equivalent_to_str("(☃=c)");
688    }
689
690    // Note: A raw `?` cannot happen in practice, since functions can only
691    // be accessed through pointers, and that will yield `^?`
692    #[test]
693    fn object_unknown_in_struct() {
694        let enc = Encoding::Struct("S", &[Encoding::Block, Encoding::Object, Encoding::Unknown]);
695        let s = "{S=@?@?}";
696
697        assert_eq!(&enc.to_string(), s);
698
699        let parsed = EncodingBox::from_str(s).unwrap();
700        let expected = EncodingBox::Struct(
701            "S".to_string(),
702            vec![EncodingBox::Block, EncodingBox::Block],
703        );
704        assert_eq!(parsed, expected);
705
706        assert!(!enc.equivalent_to_box(&expected));
707    }
708
709    // Similar to `?`, `` cannot be accurately represented inside pointers
710    // inside structs, and may be parsed incorrectly.
711    #[test]
712    fn none_in_struct() {
713        let enc = Encoding::Struct("?", &[Encoding::Pointer(&Encoding::None), Encoding::Int]);
714        let s = "{?=^i}";
715        assert_eq!(&enc.to_string(), s);
716
717        let parsed = EncodingBox::from_str(s).unwrap();
718        let expected = EncodingBox::Struct(
719            "?".to_string(),
720            vec![EncodingBox::Pointer(Box::new(EncodingBox::Int))],
721        );
722        assert_eq!(parsed, expected);
723
724        assert!(!enc.equivalent_to_box(&expected));
725    }
726}