1#![cfg_attr(not(feature = "no_tagging_doctest"), doc = "```rust")]
29#![cfg_attr(feature = "no_tagging_doctest", doc = "```ignore")]
30#![cfg_attr(not(feature = "no_tagging_doctest"), doc = "```rust")]
51#![cfg_attr(feature = "no_tagging_doctest", doc = "```ignore")]
52#![cfg_attr(not(feature = "no_tagging_doctest"), doc = "```rust")]
112#![cfg_attr(feature = "no_tagging_doctest", doc = "```ignore")]
113#![cfg_attr(not(feature = "no_tagging_doctest"), doc = "```rust")]
178#![cfg_attr(feature = "no_tagging_doctest", doc = "```ignore")]
179use 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#[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#[repr(u8)]
239#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, StableAbi)]
240#[sabi(unsafe_sabi_opaque_fields)]
241pub enum TagVariant {
242 Primitive(Primitive),
244 Ignored(&'static Tag),
246 Array(RSlice<'static, Tag>),
248 Set(RSlice<'static, Tag>),
250 Map(RSlice<'static, KeyValue<Tag>>),
252}
253
254#[repr(u8)]
256#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, StableAbi)]
257#[sabi(unsafe_sabi_opaque_fields)]
258pub enum Primitive {
259 Null,
261 Bool(bool),
263 Int(i64),
265 UInt(u64),
267 String_(RStr<'static>),
269}
270
271#[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#[repr(u8)]
281#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, StableAbi)]
282#[sabi(unsafe_sabi_opaque_fields)]
283pub enum CTVariant {
284 Primitive(Primitive),
286 Ignored(RBox<CheckableTag>),
288 Array(RVec<CheckableTag>),
290 Set(RVec<KeyValue<CheckableTag>>),
292 Map(RVec<KeyValue<CheckableTag>>),
294}
295
296#[repr(C)]
298#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, StableAbi)]
299pub struct KeyValue<T> {
300 pub key: T,
302 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 pub const NULL: &'static Tag = &Tag::null();
360
361 pub const fn null() -> Self {
363 Self::new(TagVariant::Primitive(Primitive::Null))
364 }
365 pub const fn bool_(b: bool) -> Self {
367 Self::new(TagVariant::Primitive(Primitive::Bool(b)))
368 }
369
370 pub const fn int(n: i64) -> Self {
372 Self::new(TagVariant::Primitive(Primitive::Int(n)))
373 }
374
375 pub const fn uint(n: u64) -> Self {
377 Self::new(TagVariant::Primitive(Primitive::UInt(n)))
378 }
379
380 pub const fn str(s: &'static str) -> Self {
382 Self::new(TagVariant::Primitive(Primitive::String_(RStr::from_str(s))))
383 }
384
385 pub const fn rstr(s: RStr<'static>) -> Self {
387 Self::new(TagVariant::Primitive(Primitive::String_(s)))
388 }
389
390 pub const fn ignored(ignored: &'static Tag) -> Self {
392 Self::new(TagVariant::Ignored(ignored))
393 }
394
395 pub const fn arr(s: RSlice<'static, Tag>) -> Self {
397 Self::new(TagVariant::Array(s))
398 }
399
400 pub const fn set(s: RSlice<'static, Tag>) -> Self {
402 Self::new(TagVariant::Set(s))
403 }
404
405 pub const fn kv(key: Tag, value: Tag) -> KeyValue<Tag> {
407 KeyValue { key, value }
408 }
409
410 pub const fn map(s: RSlice<'static, KeyValue<Tag>>) -> Self {
412 Self::new(TagVariant::Map(s))
413 }
414}
415
416impl Tag {
417 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 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#[allow(clippy::missing_const_for_fn)]
601impl<T> KeyValue<T> {
602 pub const fn new(key: T, value: T) -> Self {
604 Self { key, value }
605 }
606 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 pub fn into_pair(self) -> (T, T) {
620 (self.key, self.value)
621 }
622
623 pub const fn as_pair(&self) -> (&T, &T) {
625 (&self.key, &self.value)
626 }
627
628 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
647pub struct FromLiteral<T>(pub T);
651
652#[allow(clippy::wrong_self_convention)]
653impl FromLiteral<bool> {
654 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 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 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 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 pub const fn to_tag(self) -> Tag {
688 self.0
689 }
690}
691
692fn 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#[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
827unsafe 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#[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#[cfg(all(
932 test,
933 not(feature = "only_new_tests"),
934 not(feature = "no_fn_promotion")
935))]
936mod test;