abi_stable/type_layout/
tagging.rs

1//! Tag is a dynamically typed data structure used to encode extra properties
2//! about a type in its layout constant.
3//!
4//! # Comparison semantics
5//!
6//! Tags don't use strict equality when doing layout checking ,
7//! here is an exhaustive list on what is considered compatible
8//! for each variant **in the interface**:
9//!
10//! - Null:
11//!     A Tag which is compatible with any other one.
12//!     Note that Nulls are stripped from arrays,set,and map keys.
13//!
14//! - Integers/bools/strings:
15//!     They must be strictly equal.
16//!
17//! - Arrays:
18//!     They must have the same length, and have elements that compare equal.
19//!
20//! - Sets/Maps:
21//!     The set/map in the interface must be a subset of the implementation,
22//!
23//! # Examples
24//!
25//!
26//! ###  Declaring a unit type with a tag.
27//!
28#![cfg_attr(not(feature = "no_tagging_doctest"), doc = "```rust")]
29#![cfg_attr(feature = "no_tagging_doctest", doc = "```ignore")]
30//!
31//! use abi_stable::{tag,StableAbi};
32//!
33//! #[repr(C)]
34//! #[derive(StableAbi)]
35//! #[sabi( tag = tag!("WAT"))]
36//! struct UnitType;
37//!
38//!
39//! # fn main(){}
40//!
41//!
42//! ```
43//!
44//! ###  Emulating const generics for strings
45//!
46//! This emulates a `const NAME:&'static str` parameter,
47//! which is checked as being the same between the interface and implementation.
48//!
49//!
50#![cfg_attr(not(feature = "no_tagging_doctest"), doc = "```rust")]
51#![cfg_attr(feature = "no_tagging_doctest", doc = "```ignore")]
52//! use abi_stable::{tag,StableAbi,marker_type::UnsafeIgnoredType};
53//!
54//!
55//! trait Name{
56//!     const NAME:&'static str;
57//! }
58//!
59//! ///
60//! /// The layout of `StringParameterized<S>` is determined by `<S as Name>::NAME`,
61//! /// allowing the interface crate to have a different `S`
62//! /// type parameter than the implementation crate,
63//! /// so long as they have the same associated `&'static str`.
64//! ///
65//! /// StringParameterized<Foo> has the "same" layout as StringParameterized<Bar>.
66//! ///
67//! /// StringParameterized<Foo> has a "different" layout to StringParameterized<Boor>.
68//! ///
69//! #[repr(C)]
70//! #[derive(StableAbi)]
71//! #[sabi(
72//!     bound(S:Name),
73//!     tag = tag!( S::NAME ),
74//! )]
75//! struct StringParameterized<S>{
76//!     _marker:UnsafeIgnoredType<S>
77//! }
78//!
79//! #[repr(C)]
80//! #[derive(StableAbi)]
81//! struct Foo;
82//!
83//! impl Name for Foo{
84//!     const NAME:&'static str="Hello, World!";
85//! }
86//!
87//!
88//! #[repr(C)]
89//! #[derive(StableAbi)]
90//! struct Bar;
91//!
92//! impl Name for Bar{
93//!     const NAME:&'static str="Hello, Helloooooo!";
94//! }
95//!
96//!
97//! #[repr(C)]
98//! #[derive(StableAbi)]
99//! struct Boor;
100//!
101//! impl Name for Boor{
102//!     const NAME:&'static str="This is a different string!";
103//! }
104//!
105//! # fn main(){}
106//!
107//! ```
108//!
109//! ###  Declaring each variant.
110//!
111#![cfg_attr(not(feature = "no_tagging_doctest"), doc = "```rust")]
112#![cfg_attr(feature = "no_tagging_doctest", doc = "```ignore")]
113//! use abi_stable::{
114//!     rslice,tag,
115//!     type_layout::Tag,
116//! };
117//!
118//! const NULL:Tag=Tag::null();
119//!
120//!
121//! const BOOL_MACRO:Tag=tag!( false );
122//! const BOOL_FN   :Tag=Tag::bool_(false);
123//!
124//!
125//! const INT_MACRO_0:Tag=tag!(  100 );
126//! const INT_FN_0   :Tag=Tag::int(100);
127//!
128//! const INT_MACRO_1:Tag=tag!( -100 );
129//! const INT_FN_1   :Tag=Tag::int(-100);
130//!
131//!
132//! // This can only be declared using the function for now.
133//! const UINT:Tag=Tag::uint(100);
134//!
135//!
136//! const STR_0_MACRO:Tag=tag!("Hello,World!");
137//! const STR_0_FN:Tag=Tag::str("Hello,World!");
138//!
139//! const ARR_0_MACRO:Tag=tag![[ 0,1,2,3 ]];
140//! const ARR_0_FN:Tag=Tag::arr(rslice![
141//!     Tag::int(0),
142//!     Tag::int(1),
143//!     Tag::int(2),
144//!     Tag::int(3),
145//! ]);
146//!
147//!
148//! const SET_0_MACRO:Tag=tag!{{ 0,1,2,3 }};
149//! const SET_0_FN:Tag=Tag::set(rslice![
150//!     Tag::int(0),
151//!     Tag::int(1),
152//!     Tag::int(2),
153//!     Tag::int(3),
154//! ]);
155//!
156//!
157//! const MAP_0_MACRO:Tag=tag!{{
158//!     0=>"a",
159//!     1=>"b",
160//!     2=>false,
161//!     3=>100,
162//! }};
163//! const MAP_0_FN:Tag=Tag::map(rslice![
164//!     Tag::kv( Tag::int(0), Tag::str("a")),
165//!     Tag::kv( Tag::int(1), Tag::str("b")),
166//!     Tag::kv( Tag::int(2), Tag::bool_(false)),
167//!     Tag::kv( Tag::int(3), Tag::int(100)),
168//! ]);
169//!
170//! # fn main(){}
171//!
172//! ```
173//!
174//! ###  Creating a complex data structure.
175//!
176//!
177#![cfg_attr(not(feature = "no_tagging_doctest"), doc = "```rust")]
178#![cfg_attr(feature = "no_tagging_doctest", doc = "```ignore")]
179//! use abi_stable::{
180//!     tag,
181//!     type_layout::Tag,
182//! };
183//!
184//! const TAG:Tag=tag!{{
185//!     // This must match exactly,
186//!     // adding required traits on the interface or the implementation
187//!     // would be a breaking change.
188//!     "required"=>tag![[
189//!         "Copy",
190//!     ]],
191//!     
192//!     "requires at least"=>tag!{{
193//!         "Debug",
194//!         "Display",
195//!     }},
196//!
197//!
198//!     "maps"=>tag!{{
199//!         0=>"Zero",
200//!         1=>"One",
201//!     }}
202//! }};
203//!
204//!
205//! ```
206
207use std::{
208    collections::BTreeMap,
209    fmt::{self, Display},
210    mem,
211};
212
213use core_extensions::{matches, SelfOps};
214
215use crate::{
216    abi_stability::extra_checks::{
217        ExtraChecks, ExtraChecksError, ForExtraChecksImplementor, TypeCheckerMut,
218    },
219    std_types::{RBox, RCowSlice, RNone, ROption, RResult, RSlice, RSome, RStr, RVec},
220    traits::IntoReprC,
221    type_layout::TypeLayout,
222    utils::FmtPadding,
223    StableAbi,
224};
225
226/// Tag is a dynamically typed data structure used to encode extra properties
227/// about a type in its layout constant.
228///
229/// For more information [look at the module-level documentation](./index.html)
230#[repr(C)]
231#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, StableAbi)]
232#[sabi(unsafe_sabi_opaque_fields)]
233pub struct Tag {
234    variant: TagVariant,
235}
236
237/// All the Tag variants.
238#[repr(u8)]
239#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, StableAbi)]
240#[sabi(unsafe_sabi_opaque_fields)]
241pub enum TagVariant {
242    ///
243    Primitive(Primitive),
244    /// A Tag that's considered compatible with any other
245    Ignored(&'static Tag),
246    ///
247    Array(RSlice<'static, Tag>),
248    ///
249    Set(RSlice<'static, Tag>),
250    ///
251    Map(RSlice<'static, KeyValue<Tag>>),
252}
253
254/// The primitive types of a variant,which do not contain other nested tags.
255#[repr(u8)]
256#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, StableAbi)]
257#[sabi(unsafe_sabi_opaque_fields)]
258pub enum Primitive {
259    ///
260    Null,
261    ///
262    Bool(bool),
263    ///
264    Int(i64),
265    ///
266    UInt(u64),
267    ///
268    String_(RStr<'static>),
269}
270
271/// A tag that can be checked for compatibility with another tag.
272#[repr(C)]
273#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, StableAbi)]
274#[sabi(unsafe_sabi_opaque_fields)]
275pub struct CheckableTag {
276    variant: CTVariant,
277}
278
279/// The possible variants of CheckableTag.
280#[repr(u8)]
281#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, StableAbi)]
282#[sabi(unsafe_sabi_opaque_fields)]
283pub enum CTVariant {
284    ///
285    Primitive(Primitive),
286    /// A Tag that's considered compatible with any other
287    Ignored(RBox<CheckableTag>),
288    ///
289    Array(RVec<CheckableTag>),
290    ///
291    Set(RVec<KeyValue<CheckableTag>>),
292    ///
293    Map(RVec<KeyValue<CheckableTag>>),
294}
295
296/// A key-value pair,used when constructing a map.
297#[repr(C)]
298#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, StableAbi)]
299pub struct KeyValue<T> {
300    ///
301    pub key: T,
302    ///
303    pub value: T,
304}
305
306#[doc(hidden)]
307pub trait TagTrait {
308    fn is_null(&self) -> bool;
309}
310
311impl TagTrait for Tag {
312    fn is_null(&self) -> bool {
313        self.variant == TagVariant::Primitive(Primitive::Null)
314    }
315}
316
317impl<'a> TagTrait for &'a Tag {
318    fn is_null(&self) -> bool {
319        self.variant == TagVariant::Primitive(Primitive::Null)
320    }
321}
322
323impl TagTrait for CheckableTag {
324    fn is_null(&self) -> bool {
325        self.variant == CTVariant::Primitive(Primitive::Null)
326    }
327}
328
329impl<KV> TagTrait for KeyValue<KV>
330where
331    KV: TagTrait,
332{
333    fn is_null(&self) -> bool {
334        self.key.is_null()
335    }
336}
337
338impl<'a, KV> TagTrait for &'a KeyValue<KV>
339where
340    KV: TagTrait,
341{
342    fn is_null(&self) -> bool {
343        self.key.is_null()
344    }
345}
346
347impl<'a> TagTrait for &'a CheckableTag {
348    fn is_null(&self) -> bool {
349        *self == &Tag::null().to_checkable()
350    }
351}
352
353impl Tag {
354    const fn new(variant: TagVariant) -> Self {
355        Self { variant }
356    }
357
358    /// Constructs the Null variant.
359    pub const NULL: &'static Tag = &Tag::null();
360
361    /// Constructs the Null variant.
362    pub const fn null() -> Self {
363        Self::new(TagVariant::Primitive(Primitive::Null))
364    }
365    /// Constructs the Bool variant.
366    pub const fn bool_(b: bool) -> Self {
367        Self::new(TagVariant::Primitive(Primitive::Bool(b)))
368    }
369
370    /// Constructs the Int variant.
371    pub const fn int(n: i64) -> Self {
372        Self::new(TagVariant::Primitive(Primitive::Int(n)))
373    }
374
375    /// Constructs the UInt variant.
376    pub const fn uint(n: u64) -> Self {
377        Self::new(TagVariant::Primitive(Primitive::UInt(n)))
378    }
379
380    /// Constructs the String_ variant.
381    pub const fn str(s: &'static str) -> Self {
382        Self::new(TagVariant::Primitive(Primitive::String_(RStr::from_str(s))))
383    }
384
385    /// Constructs the String_ variant.
386    pub const fn rstr(s: RStr<'static>) -> Self {
387        Self::new(TagVariant::Primitive(Primitive::String_(s)))
388    }
389
390    /// Constructs the Ignored variant.
391    pub const fn ignored(ignored: &'static Tag) -> Self {
392        Self::new(TagVariant::Ignored(ignored))
393    }
394
395    /// Constructs the Array variant.
396    pub const fn arr(s: RSlice<'static, Tag>) -> Self {
397        Self::new(TagVariant::Array(s))
398    }
399
400    /// Constructs the Set variant.
401    pub const fn set(s: RSlice<'static, Tag>) -> Self {
402        Self::new(TagVariant::Set(s))
403    }
404
405    /// Constructs a KeyValue.
406    pub const fn kv(key: Tag, value: Tag) -> KeyValue<Tag> {
407        KeyValue { key, value }
408    }
409
410    /// Constructs the Map variant.
411    pub const fn map(s: RSlice<'static, KeyValue<Tag>>) -> Self {
412        Self::new(TagVariant::Map(s))
413    }
414}
415
416impl Tag {
417    /// Converts the `Tag` into a `CheckableTag`,
418    /// so as to check `Tag`s for compatibility.
419    pub fn to_checkable(self) -> CheckableTag {
420        let variant = match self.variant {
421            TagVariant::Primitive(prim) => CTVariant::Primitive(prim),
422            TagVariant::Ignored(ignored) => (*ignored)
423                .to_checkable()
424                .piped(RBox::new)
425                .piped(CTVariant::Ignored),
426            TagVariant::Array(arr) => arr
427                .iter()
428                .cloned()
429                .filter(|x| *x != Tag::null())
430                .map(Self::to_checkable)
431                .collect::<RVec<CheckableTag>>()
432                .piped(CTVariant::Array),
433            TagVariant::Set(arr) => arr
434                .iter()
435                .cloned()
436                .filter(|x| !x.is_null())
437                .map(|x| (x.to_checkable(), Tag::null().to_checkable()))
438                .piped(sorted_ct_vec_from_iter)
439                .piped(CTVariant::Set),
440            TagVariant::Map(arr) => arr
441                .iter()
442                .cloned()
443                .filter(|kv| !kv.key.is_null())
444                .map(|x| x.map(|y| y.to_checkable()).into_pair())
445                .piped(sorted_ct_vec_from_iter)
446                .piped(CTVariant::Map),
447        };
448
449        CheckableTag { variant }
450    }
451}
452
453fn sorted_ct_vec_from_iter<I>(iter: I) -> RVec<KeyValue<CheckableTag>>
454where
455    I: IntoIterator<Item = (CheckableTag, CheckableTag)>,
456{
457    iter.into_iter()
458        .collect::<BTreeMap<CheckableTag, CheckableTag>>()
459        .into_iter()
460        .map(KeyValue::from_pair)
461        .collect::<RVec<KeyValue<CheckableTag>>>()
462}
463
464impl CheckableTag {
465    /// Checks that this `CheckableTag` is compatible with another one,
466    /// returning `Ok` if it is compatible, `Err` if it was not.
467    pub fn check_compatible(&self, other: &Self) -> Result<(), TagErrors> {
468        use self::CTVariant as CTV;
469
470        let err_with_variant = |vari: TagErrorVariant| TagErrors {
471            expected: self.clone(),
472            found: other.clone(),
473            backtrace: vec![].into(),
474            errors: vec![vari].into(),
475        };
476
477        let mismatched_val_err = |cond: bool| {
478            if cond {
479                Ok(())
480            } else {
481                Err(err_with_variant(TagErrorVariant::MismatchedValue))
482            }
483        };
484
485        let same_variant = match (&self.variant, &other.variant) {
486            (CTV::Primitive(Primitive::Null), _) => return Ok(()),
487            (CTV::Primitive(l), CTV::Primitive(r)) => mem::discriminant(l) == mem::discriminant(r),
488            (l, r) => mem::discriminant(l) == mem::discriminant(r),
489        };
490
491        if !same_variant {
492            return Err(err_with_variant(TagErrorVariant::MismatchedDiscriminant));
493        }
494
495        let is_map = matches!(self.variant, CTV::Map { .. });
496
497        match (&self.variant, &other.variant) {
498            (CTV::Primitive(l), CTV::Primitive(r)) => match (l, r) {
499                (Primitive::Null, Primitive::Null) => (),
500                (Primitive::Null, _) => (),
501
502                (Primitive::Bool(l_cond), Primitive::Bool(r_cond)) => {
503                    mismatched_val_err(l_cond == r_cond)?
504                }
505                (Primitive::Bool(_), _) => {}
506
507                (Primitive::Int(l_num), Primitive::Int(r_num)) => {
508                    mismatched_val_err(l_num == r_num)?
509                }
510                (Primitive::Int(_), _) => {}
511
512                (Primitive::UInt(l_num), Primitive::UInt(r_num)) => {
513                    mismatched_val_err(l_num == r_num)?
514                }
515                (Primitive::UInt(_), _) => {}
516
517                (Primitive::String_(l_str), Primitive::String_(r_str)) => {
518                    mismatched_val_err(l_str.as_str() == r_str.as_str())?
519                }
520                (Primitive::String_(_), _) => {}
521            },
522            (CTV::Primitive(_), _) => {}
523
524            (CTV::Ignored(_), _) => {}
525
526            (CTV::Array(l_arr), CTV::Array(r_arr)) => {
527                let l_arr = l_arr.as_slice();
528                let r_arr = r_arr.as_slice();
529
530                if l_arr.len() != r_arr.len() {
531                    let e = TagErrorVariant::MismatchedArrayLength {
532                        expected: l_arr.len(),
533                        found: r_arr.len(),
534                    };
535                    return Err(err_with_variant(e));
536                }
537
538                for (l_elem, r_elem) in l_arr.iter().zip(r_arr.iter()) {
539                    l_elem
540                        .check_compatible(r_elem)
541                        .map_err(|errs| errs.context(l_elem.clone()))?;
542                }
543            }
544            (CTV::Array(_), _) => {}
545
546            (CTV::Set(l_map), CTV::Set(r_map)) | (CTV::Map(l_map), CTV::Map(r_map)) => {
547                if l_map.len() > r_map.len() {
548                    let e = TagErrorVariant::MismatchedAssocLength {
549                        expected: l_map.len(),
550                        found: r_map.len(),
551                    };
552                    return Err(err_with_variant(e));
553                }
554
555                let mut r_iter = r_map.iter().map(KeyValue::as_pair);
556
557                'outer: for (l_key, l_elem) in l_map.iter().map(KeyValue::as_pair) {
558                    let mut first_err = None::<KeyValue<&CheckableTag>>;
559
560                    'inner: loop {
561                        let (r_key, r_elem) = match r_iter.next() {
562                            Some(x) => x,
563                            None => break 'inner,
564                        };
565
566                        match l_key
567                            .check_compatible(r_key)
568                            .and_then(|_| l_elem.check_compatible(r_elem))
569                        {
570                            Ok(_) => continue 'outer,
571                            Err(_) => {
572                                first_err.get_or_insert(KeyValue::new(r_key, r_elem));
573                            }
574                        }
575                    }
576
577                    let e = if is_map {
578                        TagErrorVariant::MismatchedMapEntry {
579                            expected: KeyValue::new(l_key.clone(), l_elem.clone()),
580                            found: first_err.map(|x| x.map(Clone::clone)).into_c(),
581                        }
582                    } else {
583                        TagErrorVariant::MissingSetValue {
584                            expected: l_key.clone(),
585                            found: first_err.map(|x| x.key).cloned().into_c(),
586                        }
587                    };
588                    return Err(err_with_variant(e));
589                }
590            }
591            (CTV::Set(_), _) => {}
592            (CTV::Map(_), _) => {}
593        }
594        Ok(())
595    }
596}
597
598/////////////////////////////////////////////////////////////////
599
600#[allow(clippy::missing_const_for_fn)]
601impl<T> KeyValue<T> {
602    /// Constructs a KeyValue with `key`,`value`
603    pub const fn new(key: T, value: T) -> Self {
604        Self { key, value }
605    }
606    /// Transforms the `KeyValue<T>` to `KeyValue<U>`,
607    /// using `f` to convert `T` to `U`.
608    pub fn map<F, U>(self, mut f: F) -> KeyValue<U>
609    where
610        F: FnMut(T) -> U,
611    {
612        KeyValue {
613            key: f(self.key),
614            value: f(self.value),
615        }
616    }
617
618    /// Converts the KeyValue into a `(key, value)` pair.
619    pub fn into_pair(self) -> (T, T) {
620        (self.key, self.value)
621    }
622
623    /// Casts a &KeyValue into a `(key, value)` pair of references.
624    pub const fn as_pair(&self) -> (&T, &T) {
625        (&self.key, &self.value)
626    }
627
628    /// Converts a `(key, value)` pair into a KeyValue.
629    pub fn from_pair((key, value): (T, T)) -> Self {
630        Self { key, value }
631    }
632}
633
634impl<T> Display for KeyValue<T>
635where
636    T: Display + TagTrait,
637{
638    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
639        write!(f, "{}", self.key)?;
640        if !self.value.is_null() {
641            write!(f, "=>{}", self.value)?;
642        }
643        Ok(())
644    }
645}
646
647/////////////////////////////////////////////////////////////////
648
649/// Used to convert many types to `Tag`.
650pub struct FromLiteral<T>(pub T);
651
652#[allow(clippy::wrong_self_convention)]
653impl FromLiteral<bool> {
654    /// Converts the wrapped `bool` into a Tag.
655    pub const fn to_tag(self) -> Tag {
656        Tag::bool_(self.0)
657    }
658}
659
660#[allow(clippy::wrong_self_convention)]
661impl FromLiteral<&'static str> {
662    /// Converts the wrapped `&'static str` into a Tag.
663    pub const fn to_tag(self) -> Tag {
664        Tag::str(self.0)
665    }
666}
667
668#[allow(clippy::wrong_self_convention)]
669impl FromLiteral<RStr<'static>> {
670    /// Converts the wrapped `RStr<'static>` into a Tag.
671    pub const fn to_tag(self) -> Tag {
672        Tag::rstr(self.0)
673    }
674}
675
676#[allow(clippy::wrong_self_convention)]
677impl FromLiteral<i64> {
678    /// Converts the wrapped `i64` into a Tag.
679    pub const fn to_tag(self) -> Tag {
680        Tag::int(self.0)
681    }
682}
683
684#[allow(clippy::wrong_self_convention)]
685impl FromLiteral<Tag> {
686    /// Converts the wrapped `Tag` into a Tag.
687    pub const fn to_tag(self) -> Tag {
688        self.0
689    }
690}
691
692/////////////////////////////////////////////////////////////////
693
694fn display_iter<I>(iter: I, f: &mut fmt::Formatter<'_>, indent: usize) -> fmt::Result
695where
696    I: IntoIterator,
697    I::Item: Display + TagTrait,
698{
699    let mut buffer = String::new();
700    for elem in iter.into_iter().filter(|x| !x.is_null()) {
701        Display::fmt(&buffer.display_pad(indent, &elem)?, f)?;
702        writeln!(f, ",")?;
703    }
704    Ok(())
705}
706
707impl Display for Primitive {
708    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
709        match *self {
710            Primitive::Null => {
711                write!(f, "null")?;
712            }
713            Primitive::Bool(cond) => {
714                write!(f, "{}", cond)?;
715            }
716            Primitive::Int(num) => {
717                write!(f, "{}", num)?;
718            }
719            Primitive::UInt(num) => {
720                write!(f, "{}", num)?;
721            }
722            Primitive::String_(s) => {
723                write!(f, "'{}'", s)?;
724            }
725        }
726        Ok(())
727    }
728}
729
730impl Display for Tag {
731    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
732        match &self.variant {
733            TagVariant::Primitive(prim) => {
734                Display::fmt(prim, f)?;
735            }
736            TagVariant::Ignored(ignored) => {
737                Display::fmt(ignored, f)?;
738            }
739            TagVariant::Array(arr) => {
740                writeln!(f, "[")?;
741                display_iter(&**arr, f, 4)?;
742                write!(f, "]")?;
743            }
744            TagVariant::Set(map) => {
745                writeln!(f, "{{")?;
746                display_iter(map.iter(), f, 4)?;
747                write!(f, "}}")?;
748            }
749            TagVariant::Map(map) => {
750                writeln!(f, "{{")?;
751                display_iter(map.iter(), f, 4)?;
752                write!(f, "}}")?;
753            }
754        }
755        Ok(())
756    }
757}
758
759impl Display for CheckableTag {
760    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
761        match &self.variant {
762            CTVariant::Primitive(prim) => {
763                Display::fmt(prim, f)?;
764            }
765            CTVariant::Ignored(ignored) => {
766                Display::fmt(ignored, f)?;
767            }
768            CTVariant::Array(arr) => {
769                writeln!(f, "[")?;
770                display_iter(arr, f, 4)?;
771                write!(f, "]")?;
772            }
773            CTVariant::Set(map) | CTVariant::Map(map) => {
774                writeln!(f, "{{")?;
775                display_iter(map.iter(), f, 4)?;
776                write!(f, "}}")?;
777            }
778        }
779        Ok(())
780    }
781}
782
783/////////////////////////////////////////////////////////////////
784
785/////////////////////////////////////////////////////////////////
786
787/// The error produced when checking `CheckableTag`s.
788#[derive(Debug, Clone, PartialEq)]
789pub struct TagErrors {
790    expected: CheckableTag,
791    found: CheckableTag,
792    backtrace: RVec<CheckableTag>,
793    errors: RVec<TagErrorVariant>,
794}
795
796impl TagErrors {
797    fn context(mut self, current: CheckableTag) -> Self {
798        self.backtrace.push(current);
799        self
800    }
801}
802
803impl Display for TagErrors {
804    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
805        let mut buffer = String::new();
806
807        writeln!(f, "Stacktrace:")?;
808        if self.backtrace.is_empty() {
809            writeln!(f, "    Empty.")?;
810        } else {
811            for stack in self.backtrace.iter().rev() {
812                writeln!(f, "    Inside:\n{},", buffer.display_pad(8, stack)?)?;
813            }
814        }
815        writeln!(f, "Expected:\n{}", buffer.display_pad(4, &self.expected)?)?;
816        writeln!(f, "Found:\n{}", buffer.display_pad(4, &self.found)?)?;
817        writeln!(f, "Errors:\n")?;
818        for err in self.errors.iter().rev() {
819            writeln!(f, "\n{},", buffer.display_pad(4, err)?)?;
820        }
821        Ok(())
822    }
823}
824
825impl std::error::Error for TagErrors {}
826
827/////////////////////////////////////////////////////////////////
828
829unsafe impl ExtraChecks for Tag {
830    fn type_layout(&self) -> &'static TypeLayout {
831        Self::LAYOUT
832    }
833
834    fn check_compatibility(
835        &self,
836        _layout_containing_self: &'static TypeLayout,
837        layout_containing_other: &'static TypeLayout,
838        checker: TypeCheckerMut<'_>,
839    ) -> RResult<(), ExtraChecksError> {
840        Self::downcast_with_layout(layout_containing_other, checker, |other, _| {
841            let t_tag = self.to_checkable();
842            let o_tag = other.to_checkable();
843            t_tag.check_compatible(&o_tag)
844        })
845    }
846
847    fn nested_type_layouts(&self) -> RCowSlice<'_, &'static TypeLayout> {
848        RCowSlice::from_slice(&[])
849    }
850}
851
852/////////////////////////////////////////////////////////////////
853
854#[repr(u8)]
855#[derive(Debug, Clone, PartialEq, StableAbi)]
856pub(crate) enum TagErrorVariant {
857    MismatchedDiscriminant,
858    MismatchedValue,
859    MismatchedArrayLength {
860        expected: usize,
861        found: usize,
862    },
863    MismatchedAssocLength {
864        expected: usize,
865        found: usize,
866    },
867    MissingSetValue {
868        expected: CheckableTag,
869        found: ROption<CheckableTag>,
870    },
871    MismatchedMapEntry {
872        expected: KeyValue<CheckableTag>,
873        found: ROption<KeyValue<CheckableTag>>,
874    },
875}
876
877impl Display for TagErrorVariant {
878    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
879        match self {
880            TagErrorVariant::MismatchedDiscriminant => {
881                writeln!(f, "Mismatched Tag variant.")?;
882            }
883            TagErrorVariant::MismatchedValue => {
884                writeln!(f, "Mitmatched Value.")?;
885            }
886            TagErrorVariant::MismatchedArrayLength { expected, found } => {
887                writeln!(
888                    f,
889                    "Mismatched length  expected:{}  found:{}",
890                    expected, found
891                )?;
892            }
893            TagErrorVariant::MismatchedAssocLength { expected, found } => {
894                writeln!(
895                    f,
896                    "Mismatched length  expected at least:{}  found:{}",
897                    expected, found,
898                )?;
899            }
900            TagErrorVariant::MissingSetValue { expected, found } => {
901                let mut buffer = String::new();
902                writeln!(
903                    f,
904                    "Mismatched value in set\nExpected:\n{}",
905                    buffer.display_pad(4, &expected)?
906                )?;
907                match found {
908                    RSome(found) => writeln!(f, "Found:\n{}", buffer.display_pad(4, &found)?),
909                    RNone => writeln!(f, "Found:\n    Nothing",),
910                }?;
911            }
912            TagErrorVariant::MismatchedMapEntry { expected, found } => {
913                let mut buffer = String::new();
914                writeln!(
915                    f,
916                    "Mismatched entry in map\nExpected:\n{}",
917                    buffer.display_pad(4, &expected)?
918                )?;
919                match found {
920                    RSome(found) => writeln!(f, "Found:\n{}", buffer.display_pad(4, &found)?),
921                    RNone => writeln!(f, "Found:\n    Nothing",),
922                }?;
923            }
924        }
925        Ok(())
926    }
927}
928
929////////////////////////////////////////////////////////////////////////////////
930
931#[cfg(all(
932    test,
933    not(feature = "only_new_tests"),
934    not(feature = "no_fn_promotion")
935))]
936mod test;