1use core::any::type_name;
2use core::cmp::Ordering;
3use core::fmt;
4use core::hash::{Hash, Hasher};
5use core::marker::PhantomData;
6use core::ops::{BitAnd, BitOr, BitXor, Not};
7use core::str::FromStr;
8
9use crate::{BitFlags, BitFlagsSpec, NameMatcher, util};
10
11#[repr(transparent)]
74pub struct BitFlag<T, U>(u8, PhantomData<fn() -> T>, PhantomData<fn() -> *const U>)
75where
76 U: ?Sized;
77
78#[cfg(feature = "serde")]
80struct BitFlagVisitor<T, U>(PhantomData<T>, PhantomData<fn() -> *const U>)
81where
82 U: ?Sized;
83
84impl<T, U> BitFlag<T, U>
85where
86 U: ?Sized,
87{
88 #[inline]
98 #[must_use]
99 pub const fn from_bit_retain(i: u8) -> Self {
100 Self(i, PhantomData, PhantomData)
101 }
102
103 #[inline]
105 #[must_use]
106 pub const fn bit(&self) -> u8 {
107 self.0
108 }
109
110 #[inline]
116 #[must_use]
117 pub const fn name(&self) -> Option<&'static str>
118 where
119 T: 'static,
120 U: BitFlagsSpec<T>,
121 {
122 let names_and_aliases = self.name_and_aliases();
123 if names_and_aliases.is_empty() {
124 None
125 } else {
126 Some(names_and_aliases[0])
127 }
128 }
129
130 #[inline]
136 #[must_use]
137 pub const fn name_and_aliases(&self) -> &'static [&'static str]
138 where
139 T: 'static,
140 U: BitFlagsSpec<T>,
141 {
142 #[expect(clippy::as_conversions, reason = "supported in const functions")]
143 let bit = self.0 as usize;
144 if bit < U::FLAG_NAMES.len() {
145 U::FLAG_NAMES[bit]
146 } else {
147 &[]
148 }
149 }
150
151 #[inline]
163 pub fn try_from_str(input: &str, name_matcher: NameMatcher) -> Result<Self, BitFlagParseError>
164 where
165 T: 'static + util::IntOpsExt,
166 U: BitFlagsSpec<T>,
167 {
168 if let Some(flag) = Self::from_str_opt(input, name_matcher) {
169 return Ok(flag);
170 }
171
172 #[cfg(feature = "alloc")]
173 let given = alloc::string::ToString::to_string(input);
174 #[cfg(not(feature = "alloc"))]
175 let given = ();
176 let expected = U::FLAG_NAMES;
177
178 Err(BitFlagParseError { given, expected })
179 }
180
181 #[inline]
189 #[must_use]
190 pub fn from_str_opt(input: &str, name_matcher: NameMatcher) -> Option<Self>
191 where
192 T: 'static + util::IntOpsExt,
193 U: BitFlagsSpec<T>,
194 {
195 if let Some(flag) = Self::from_name_opt(input, name_matcher) {
196 return Some(flag);
197 }
198 if let Some(flag) = Self::from_hex_opt(input) {
199 return Some(flag);
200 }
201
202 None
203 }
204
205 #[inline]
216 pub fn try_from_name(
217 input: &str,
218 name_matcher: NameMatcher,
219 ) -> Result<Self, BitFlagParseNameError>
220 where
221 T: 'static + util::IntOpsExt,
222 U: BitFlagsSpec<T>,
223 {
224 if let Some(flag) = Self::from_name_opt(input, name_matcher) {
225 return Ok(flag);
226 }
227
228 #[cfg(feature = "alloc")]
229 let given = alloc::string::ToString::to_string(input);
230 #[cfg(not(feature = "alloc"))]
231 let given = ();
232 let expected = U::FLAG_NAMES;
233
234 Err(BitFlagParseNameError { given, expected })
235 }
236
237 #[inline]
243 #[must_use]
244 pub fn from_name_opt(input: &str, name_matcher: NameMatcher) -> Option<Self>
245 where
246 T: 'static + util::IntOpsExt,
247 U: BitFlagsSpec<T>,
248 {
249 for (i, variants) in U::FLAG_NAMES.iter().enumerate().take(usize::from(T::BITS)) {
250 let Ok(bit) = u8::try_from(i) else {
251 #[cfg(debug_assertions)]
252 unreachable!("`i < T::BITS <= u8::MAX`, so this is unreachable");
253 #[cfg(not(debug_assertions))]
254 break;
255 };
256 let bit_flag = Self::from_bit_retain(bit);
257 for &expected in *variants {
258 let is_matched = name_matcher.matches(input, expected);
259 if is_matched {
260 return Some(bit_flag);
261 }
262 }
263 }
264
265 None
266 }
267
268 #[inline]
278 pub fn try_from_hex(input: &str) -> Result<Self, BitFlagParseHexError>
279 where
280 T: util::IntOpsExt,
281 {
282 if let Some(flag) = Self::from_hex_opt(input) {
283 return Ok(flag);
284 }
285
286 #[cfg(feature = "alloc")]
287 let given = alloc::string::ToString::to_string(input);
288 #[cfg(not(feature = "alloc"))]
289 let given = ();
290
291 Err(BitFlagParseHexError { given })
292 }
293
294 #[inline]
301 #[must_use]
302 pub fn from_hex_opt(input: &str) -> Option<Self>
303 where
304 T: util::IntOpsExt,
305 {
306 let input = input.strip_prefix("0x")?;
307 if input.starts_with('+') {
308 return None;
309 }
310 let bits = T::from_str_radix(input, 16).ok()?;
311
312 if bits.count_ones() != 1 {
313 return None;
314 }
315 let bit = bits.lowest_one()?;
316 let bit = u8::try_from(bit).ok()?;
317 (bit < T::BITS).then(|| Self::from_bit_retain(bit))
318 }
319}
320
321macro_rules! impl_bit_flag {
325 ($ty:ty) => {
326 impl<U> BitFlag<$ty, U>
327 where
328 U: ?Sized,
329 {
330 #[inline]
341 #[must_use]
342 pub const fn try_from_flags(flags: BitFlags<$ty, U>) -> Option<Self>
343 where
344 U: BitFlagsSpec<$ty>,
345 {
346 Self::try_from_bits(flags.bits())
347 }
348
349 #[inline]
357 #[must_use]
358 pub const fn try_from_bit(i: u8) -> Option<Self>
359 where
360 U: BitFlagsSpec<$ty>,
361 {
362 const MAX_BITS: usize = size_of::<$ty>() * 8;
363
364 #[expect(clippy::as_conversions, reason = "supported in const functions")]
365 if (i as usize) < MAX_BITS && ((1 << (i as u32)) & !U::VALID_BITS_MASK) == 0 {
366 Some(Self(i, PhantomData, PhantomData))
367 } else {
368 None
369 }
370 }
371
372 #[inline]
379 #[must_use]
380 pub const fn try_from_bits(bits: $ty) -> Option<Self>
381 where
382 U: BitFlagsSpec<$ty>,
383 {
384 if bits.count_ones() == 1
385 && let Some(bit) = bits.lowest_one()
386 {
387 #[expect(
388 clippy::as_conversions,
389 clippy::cast_possible_truncation,
390 reason = "supported in const functions"
391 )]
392 Self::try_from_bit(bit as u8)
393 } else {
394 None
395 }
396 }
397
398 #[inline]
405 #[must_use]
406 pub const fn try_from_bits_retain(bits: $ty) -> Option<Self> {
407 if bits.count_ones() == 1
408 && let Some(bit) = bits.lowest_one()
409 {
410 #[expect(
411 clippy::as_conversions,
412 clippy::cast_possible_truncation,
413 reason = "supported in const functions"
414 )]
415 Some(Self::from_bit_retain(bit as u8))
416 } else {
417 None
418 }
419 }
420
421 #[inline]
429 #[must_use]
430 pub const fn is_valid(&self) -> bool
431 where
432 U: BitFlagsSpec<$ty>,
433 {
434 #[expect(clippy::as_conversions, reason = "supported in const functions")]
435 ((self.0 as u32) < <$ty>::BITS && BitFlags::<$ty, U>::from_flag(*self).is_valid())
436 }
437
438 #[inline]
445 #[must_use]
446 pub const fn mask(&self) -> Option<$ty> {
447 #[expect(clippy::as_conversions, reason = "supported in const functions")]
448 let bit = self.0 as u32;
449 if bit < <$ty>::BITS {
450 Some(1 << bit)
451 } else {
452 None
453 }
454 }
455
456 #[inline]
461 #[must_use]
462 pub const fn mask_or_default(&self) -> $ty {
463 if let Some(mask) = self.mask() {
464 mask
465 } else {
466 0
467 }
468 }
469
470 #[inline]
476 #[must_use]
477 pub const fn intersection(self, other: Self) -> BitFlags<$ty, U> {
478 BitFlags::<$ty, U>::from_flag(self)
479 .intersection(BitFlags::<$ty, U>::from_flag(other))
480 }
481
482 #[inline]
487 #[must_use]
488 pub const fn union(self, other: Self) -> BitFlags<$ty, U> {
489 BitFlags::<$ty, U>::from_flag(self).union(BitFlags::<$ty, U>::from_flag(other))
490 }
491
492 #[inline]
500 #[must_use]
501 pub const fn difference(self, other: Self) -> BitFlags<$ty, U> {
502 BitFlags::<$ty, U>::from_flag(self).difference(BitFlags::<$ty, U>::from_flag(other))
503 }
504
505 #[inline]
512 #[must_use]
513 pub const fn symmetric_difference(self, other: Self) -> BitFlags<$ty, U> {
514 BitFlags::<$ty, U>::from_flag(self)
515 .symmetric_difference(BitFlags::<$ty, U>::from_flag(other))
516 }
517
518 #[inline]
524 #[must_use]
525 pub const fn complement(self) -> BitFlags<$ty, U>
526 where
527 U: BitFlagsSpec<$ty>,
528 {
529 BitFlags::<$ty, U>::from_flag(self).complement()
530 }
531 }
532 };
533}
534
535impl_bit_flag!(u8);
536impl_bit_flag!(u16);
537impl_bit_flag!(u32);
538impl_bit_flag!(u64);
539impl_bit_flag!(u128);
540impl_bit_flag!(usize);
541
542impl_bit_flag!(i8);
543impl_bit_flag!(i16);
544impl_bit_flag!(i32);
545impl_bit_flag!(i64);
546impl_bit_flag!(i128);
547impl_bit_flag!(isize);
548
549impl<T, U> Copy for BitFlag<T, U> where U: ?Sized {}
550
551impl<T, U> Clone for BitFlag<T, U>
552where
553 U: ?Sized,
554{
555 #[inline]
556 fn clone(&self) -> Self {
557 *self
558 }
559}
560
561impl<T, U> Eq for BitFlag<T, U> where U: ?Sized {}
562
563impl<T, U> PartialEq for BitFlag<T, U>
564where
565 U: ?Sized,
566{
567 #[inline]
568 fn eq(&self, other: &Self) -> bool {
569 self.0 == other.0
570 }
571}
572
573impl<T, U> Ord for BitFlag<T, U>
574where
575 U: ?Sized,
576{
577 #[inline]
578 fn cmp(&self, other: &Self) -> Ordering {
579 self.0.cmp(&other.0)
580 }
581}
582
583impl<T, U> PartialOrd for BitFlag<T, U>
584where
585 U: ?Sized,
586{
587 #[inline]
588 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
589 Some(self.cmp(other))
590 }
591}
592
593impl<T, U> Hash for BitFlag<T, U>
594where
595 U: ?Sized,
596{
597 #[inline]
598 fn hash<H: Hasher>(&self, state: &mut H) {
599 self.0.hash(state);
600 }
601}
602
603impl<T, U> TryFrom<BitFlags<T, U>> for BitFlag<T, U>
608where
609 T: 'static + Copy + PartialEq + Not<Output = T> + BitAnd<T, Output = T> + util::IntOpsExt,
610 U: BitFlagsSpec<T>,
611{
612 type Error = BitFlagFromFlagsError;
613
614 #[inline]
615 fn try_from(flags: BitFlags<T, U>) -> Result<Self, Self::Error> {
616 let bits = flags.bits();
617 if bits.count_ones() == 1
618 && let Some(bit) = bits.lowest_one()
619 && let Ok(bit) = u8::try_from(bit)
620 && let Some(mask) = T::ONE.checked_shl(u32::from(bit))
621 && (mask & !U::VALID_BITS_MASK) == T::ZERO
622 {
623 Ok(Self::from_bit_retain(bit))
624 } else {
625 Err(BitFlagFromFlagsError)
626 }
627 }
628}
629
630impl<T, U> BitAnd<Self> for BitFlag<T, U>
631where
632 T: BitAnd<T, Output = T> + util::IntOpsExt,
633 U: ?Sized,
634{
635 type Output = BitFlags<T, U>;
636
637 #[inline]
638 fn bitand(self, rhs: Self) -> Self::Output {
639 BitFlags::from(self) & BitFlags::from(rhs)
640 }
641}
642
643impl<T, U> BitOr<Self> for BitFlag<T, U>
644where
645 T: BitOr<T, Output = T> + util::IntOpsExt,
646 U: ?Sized,
647{
648 type Output = BitFlags<T, U>;
649
650 #[inline]
651 fn bitor(self, rhs: Self) -> Self::Output {
652 BitFlags::from(self) | BitFlags::from(rhs)
653 }
654}
655
656impl<T, U> BitXor<Self> for BitFlag<T, U>
657where
658 T: BitXor<T, Output = T> + util::IntOpsExt,
659 U: ?Sized,
660{
661 type Output = BitFlags<T, U>;
662
663 #[inline]
664 fn bitxor(self, rhs: Self) -> Self::Output {
665 BitFlags::from(self) ^ BitFlags::from(rhs)
666 }
667}
668
669impl<T, U> Not for BitFlag<T, U>
670where
671 T: 'static + Not<Output = T> + BitAnd<T, Output = T> + util::IntOpsExt,
672 U: ?Sized + BitFlagsSpec<T>,
673{
674 type Output = BitFlags<T, U>;
675
676 #[inline]
677 fn not(self) -> Self::Output {
678 !BitFlags::from(self)
679 }
680}
681
682impl<T, U> fmt::Display for BitFlag<T, U>
688where
689 T: 'static,
690 U: ?Sized + BitFlagsSpec<T>,
691{
692 #[expect(clippy::missing_inline_in_public_items, reason = "bloats binary size")]
693 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
694 let bit = usize::from(self.0);
695 if let Some(names) = U::FLAG_NAMES.get(bit)
696 && let Some(name) = names.first()
697 {
698 write!(f, "{name}")
699 } else {
700 let leading = 1 << (bit % 4);
701 let num_zeroes = bit / 4;
702 write!(f, "0x{leading}{:0<num_zeroes$}", "")
703 }
704 }
705}
706
707impl<T, U> fmt::Debug for BitFlag<T, U>
711where
712 U: ?Sized,
713{
714 #[expect(clippy::missing_inline_in_public_items, reason = "bloats binary size")]
715 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
716 write!(
717 f,
718 "BitFlag(1 << {}, PhantomData<({}, {})>)",
719 self.0,
720 type_name::<T>(),
721 type_name::<U>(),
722 )
723 }
724}
725
726impl<T, U> fmt::Binary for BitFlag<T, U>
728where
729 U: ?Sized,
730{
731 #[inline]
732 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
733 if f.alternate() {
734 write!(f, "0b")?;
735 }
736 let bit = usize::from(self.0);
737 write!(f, "1{:0<bit$}", "")
738 }
739}
740
741impl<T, U> fmt::Octal for BitFlag<T, U>
743where
744 U: ?Sized,
745{
746 #[inline]
747 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
748 if f.alternate() {
749 write!(f, "0o")?;
750 }
751 let bit = usize::from(self.0);
752 let leading = 1 << (bit % 3);
753 let num_zeroes = bit / 3;
754 write!(f, "{leading}{:0<num_zeroes$}", "")
755 }
756}
757
758impl<T, U> fmt::LowerHex for BitFlag<T, U>
760where
761 U: ?Sized,
762{
763 #[inline]
764 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
765 if f.alternate() {
766 write!(f, "0x")?;
767 }
768 let bit = usize::from(self.0);
769 let leading = 1 << (bit % 4);
770 let num_zeroes = bit / 4;
771 write!(f, "{leading}{:0<num_zeroes$}", "")
772 }
773}
774
775impl<T, U> fmt::UpperHex for BitFlag<T, U>
777where
778 U: ?Sized,
779{
780 #[inline]
781 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
782 if f.alternate() {
783 write!(f, "0x")?;
784 }
785 let bit = usize::from(self.0);
786 let leading = 1 << (bit % 4);
787 let num_zeroes = bit / 4;
788 write!(f, "{leading}{:0<num_zeroes$}", "")
789 }
790}
791
792impl<T, U> FromStr for BitFlag<T, U>
793where
794 T: 'static + util::IntOpsExt,
795 U: ?Sized + BitFlagsSpec<T>,
796{
797 type Err = BitFlagParseError;
798
799 #[inline]
800 fn from_str(input: &str) -> Result<Self, Self::Err> {
801 Self::try_from_str(input, U::DEFAULT_NAME_MATCHER)
802 }
803}
804
805#[cfg(feature = "serde")]
806#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
807impl<T, U> serde::Serialize for BitFlag<T, U>
808where
809 T: 'static,
810 U: ?Sized + BitFlagsSpec<T>,
811{
812 #[inline]
813 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
814 where
815 S: serde::Serializer,
816 {
817 serializer.collect_str(&self)
819 }
820}
821
822#[cfg(feature = "arbitrary")]
823#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
824impl<'a, T, U> arbitrary::Arbitrary<'a> for BitFlag<T, U>
825where
826 U: ?Sized,
827{
828 #[inline]
829 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
830 u8::arbitrary(u).map(|u| Self(u, PhantomData, PhantomData))
831 }
832
833 #[inline]
834 fn size_hint(depth: usize) -> (usize, Option<usize>) {
835 u8::size_hint(depth)
836 }
837}
838
839#[cfg(feature = "bytemuck")]
840#[cfg_attr(docsrs, doc(cfg(feature = "bytemuck")))]
841unsafe impl<T, U> bytemuck::Zeroable for BitFlag<T, U> where U: ?Sized {}
843
844#[cfg(feature = "bytemuck")]
845#[cfg_attr(docsrs, doc(cfg(feature = "bytemuck")))]
846unsafe impl<T, U> bytemuck::Pod for BitFlag<T, U>
849where
850 T: 'static,
851 U: 'static + ?Sized,
852{
853}
854
855#[cfg(feature = "serde")]
856#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
857impl<'de, T, U> serde::Deserialize<'de> for BitFlag<T, U>
858where
859 T: 'static + util::IntOpsExt,
860 U: ?Sized + BitFlagsSpec<T>,
861{
862 #[inline]
863 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
864 where
865 D: serde::Deserializer<'de>,
866 {
867 deserializer.deserialize_str(BitFlagVisitor::<T, U>(PhantomData, PhantomData))
868 }
869}
870
871#[cfg(feature = "serde")]
872#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
873impl<T, U> serde::de::Visitor<'_> for BitFlagVisitor<T, U>
874where
875 T: 'static + util::IntOpsExt,
876 U: ?Sized + BitFlagsSpec<T>,
877{
878 type Value = BitFlag<T, U>;
879
880 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
881 fmt_expected(f, U::FLAG_NAMES)
882 }
883
884 #[inline]
885 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
886 where
887 E: serde::de::Error,
888 {
889 Self::Value::from_str(value).map_err(E::custom)
890 }
891}
892
893#[cfg_attr(
895 not(feature = "alloc"),
896 expect(
897 missing_copy_implementations,
898 reason = "the payload is feature-dependent"
899 )
900)]
901#[derive(Clone, Debug)]
902pub struct BitFlagParseError {
903 #[cfg(not(feature = "alloc"))]
904 given: (),
905 #[cfg(feature = "alloc")]
906 given: alloc::string::String,
907
908 expected: &'static [&'static [&'static str]],
909}
910
911impl fmt::Display for BitFlagParseError {
912 #[expect(clippy::missing_inline_in_public_items, reason = "bloats binary size")]
913 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
914 let named_flags = self
915 .expected
916 .iter()
917 .filter(|variants| !variants.is_empty())
918 .count();
919
920 if named_flags == 0 {
921 write!(f, "unrecognized hex flag, ")?;
922 } else {
923 write!(f, "unrecognized named flag or hex flag, ")?;
924 }
925 #[cfg(feature = "alloc")]
926 write!(f, "given: \"{}\", expected ", self.given)?;
927 #[cfg(not(feature = "alloc"))]
928 write!(f, "expected ")?;
929 fmt_expected(f, self.expected)?;
930
931 #[cfg(not(feature = "alloc"))]
932 let () = self.given;
933
934 Ok(())
935 }
936}
937
938impl core::error::Error for BitFlagParseError {}
939
940#[inline]
943fn fmt_expected(
944 f: &mut fmt::Formatter<'_>,
945 expected: &'static [&'static [&'static str]],
946) -> fmt::Result {
947 let named_flags = expected
948 .iter()
949 .filter(|variants| !variants.is_empty())
950 .count();
951
952 if named_flags == 0 {
953 write!(f, "a valid one-bit hex flag with \"0x\" prefix")?;
954 } else {
955 fmt_expected_name(f, expected)?;
956 write!(f, ", or a valid one-bit hex flag with \"0x\" prefix")?;
957 }
958 Ok(())
959}
960
961#[cfg_attr(
963 not(feature = "alloc"),
964 expect(
965 missing_copy_implementations,
966 reason = "the payload is feature-dependent"
967 )
968)]
969#[derive(Clone, Debug)]
970pub struct BitFlagParseNameError {
971 #[cfg(not(feature = "alloc"))]
972 given: (),
973 #[cfg(feature = "alloc")]
974 given: alloc::string::String,
975
976 expected: &'static [&'static [&'static str]],
977}
978
979impl fmt::Display for BitFlagParseNameError {
980 #[expect(clippy::missing_inline_in_public_items, reason = "bloats binary size")]
981 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
982 let named_flags = self
983 .expected
984 .iter()
985 .filter(|variants| !variants.is_empty())
986 .count();
987
988 if named_flags == 0 {
989 write!(f, "does not support named flags")?;
990 } else {
991 write!(f, "unrecognized named flag, ")?;
992 #[cfg(feature = "alloc")]
993 write!(f, "given: \"{}\", expected ", self.given)?;
994 #[cfg(not(feature = "alloc"))]
995 write!(f, "expected ")?;
996 fmt_expected(f, self.expected)?;
997 }
998
999 #[cfg(not(feature = "alloc"))]
1000 let () = self.given;
1001
1002 Ok(())
1003 }
1004}
1005
1006impl core::error::Error for BitFlagParseNameError {}
1007
1008#[inline]
1010fn fmt_expected_name(
1011 f: &mut fmt::Formatter<'_>,
1012 expected: &'static [&'static [&'static str]],
1013) -> fmt::Result {
1014 let mut separator = " ";
1015
1016 write!(f, "one of")?;
1017 for names in expected {
1018 if let Some(name) = names.first() {
1019 write!(f, "{separator}\"{name}\"")?;
1020 separator = ", ";
1021 }
1022 }
1023 Ok(())
1024}
1025
1026#[cfg_attr(
1028 not(feature = "alloc"),
1029 expect(
1030 missing_copy_implementations,
1031 reason = "the payload is feature-dependent"
1032 )
1033)]
1034#[derive(Clone, Debug)]
1035pub struct BitFlagParseHexError {
1036 #[cfg(not(feature = "alloc"))]
1037 given: (),
1038 #[cfg(feature = "alloc")]
1039 given: alloc::string::String,
1040}
1041
1042impl fmt::Display for BitFlagParseHexError {
1043 #[expect(clippy::missing_inline_in_public_items, reason = "bloats binary size")]
1044 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1045 write!(f, "unrecognized hex flag")?;
1046
1047 #[cfg(feature = "alloc")]
1048 write!(f, ", given: \"{}\"", self.given)?;
1049 write!(f, ", expected a valid one-bit hex flag with \"0x\" prefix")?;
1050
1051 #[cfg(not(feature = "alloc"))]
1052 let () = self.given;
1053
1054 Ok(())
1055 }
1056}
1057
1058impl core::error::Error for BitFlagParseHexError {}
1059
1060#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default, Debug)]
1065pub struct BitFlagFromFlagsError;
1066
1067impl fmt::Display for BitFlagFromFlagsError {
1068 #[expect(clippy::missing_inline_in_public_items, reason = "bloats binary size")]
1069 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1070 write!(f, "flags do not contain exactly one valid bit")
1071 }
1072}
1073
1074impl core::error::Error for BitFlagFromFlagsError {}