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}