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)]
72pub struct BitFlag<T, U>(u8, PhantomData<fn() -> T>, PhantomData<fn() -> *const U>)
73where
74 U: ?Sized;
75
76#[cfg(feature = "serde")]
78struct BitFlagVisitor<T, U>(PhantomData<T>, PhantomData<fn() -> *const U>)
79where
80 U: ?Sized;
81
82impl<T, U> BitFlag<T, U>
83where
84 U: ?Sized,
85{
86 #[inline]
96 #[must_use]
97 pub const fn from_bit_retain(i: u8) -> Self {
98 Self(i, PhantomData, PhantomData)
99 }
100
101 #[inline]
103 #[must_use]
104 pub const fn bit(&self) -> u8 {
105 self.0
106 }
107
108 #[inline]
114 #[must_use]
115 pub const fn name(&self) -> Option<&'static str>
116 where
117 U: BitFlagsSpec<T>,
118 {
119 let names_and_aliases = self.name_and_aliases();
120 if names_and_aliases.is_empty() {
121 None
122 } else {
123 Some(names_and_aliases[0])
124 }
125 }
126
127 #[inline]
133 #[must_use]
134 pub const fn name_and_aliases(&self) -> &'static [&'static str]
135 where
136 U: BitFlagsSpec<T>,
137 {
138 #[expect(clippy::as_conversions, reason = "supported in const functions")]
139 let bit = self.0 as usize;
140 if bit < U::FLAG_NAMES.len() {
141 U::FLAG_NAMES[bit]
142 } else {
143 &[]
144 }
145 }
146
147 #[inline]
159 pub fn try_from_str(input: &str, name_matcher: NameMatcher) -> Result<Self, BitFlagParseError>
160 where
161 T: util::IntOpsExt,
162 U: BitFlagsSpec<T>,
163 {
164 if let Some(flag) = Self::from_str_opt(input, name_matcher) {
165 return Ok(flag);
166 }
167
168 #[cfg(feature = "alloc")]
169 let given = alloc::string::ToString::to_string(input);
170 #[cfg(not(feature = "alloc"))]
171 let given = ();
172 let expected = U::FLAG_NAMES;
173
174 Err(BitFlagParseError { given, expected })
175 }
176
177 #[inline]
185 #[must_use]
186 pub fn from_str_opt(input: &str, name_matcher: NameMatcher) -> Option<Self>
187 where
188 T: util::IntOpsExt,
189 U: BitFlagsSpec<T>,
190 {
191 if let Some(flag) = Self::from_name_opt(input, name_matcher) {
192 return Some(flag);
193 }
194 if let Some(flag) = Self::from_hex_opt(input) {
195 return Some(flag);
196 }
197
198 None
199 }
200
201 #[inline]
212 pub fn try_from_name(
213 input: &str,
214 name_matcher: NameMatcher,
215 ) -> Result<Self, BitFlagParseNameError>
216 where
217 T: util::IntOpsExt,
218 U: BitFlagsSpec<T>,
219 {
220 if let Some(flag) = Self::from_name_opt(input, name_matcher) {
221 return Ok(flag);
222 }
223
224 #[cfg(feature = "alloc")]
225 let given = alloc::string::ToString::to_string(input);
226 #[cfg(not(feature = "alloc"))]
227 let given = ();
228 let expected = U::FLAG_NAMES;
229
230 Err(BitFlagParseNameError { given, expected })
231 }
232
233 #[inline]
239 #[must_use]
240 pub fn from_name_opt(input: &str, name_matcher: NameMatcher) -> Option<Self>
241 where
242 T: util::IntOpsExt,
243 U: BitFlagsSpec<T>,
244 {
245 for (i, variants) in U::FLAG_NAMES.iter().enumerate().take(usize::from(T::BITS)) {
246 let Ok(bit) = u8::try_from(i) else {
247 #[cfg(debug_assertions)]
248 unreachable!("`i < T::BITS <= u8::MAX`, so this is unreachable");
249 #[cfg(not(debug_assertions))]
250 break;
251 };
252 let bit_flag = Self::from_bit_retain(bit);
253 for &expected in *variants {
254 let is_matched = name_matcher.matches(input, expected);
255 if is_matched {
256 return Some(bit_flag);
257 }
258 }
259 }
260
261 None
262 }
263
264 #[inline]
274 pub fn try_from_hex(input: &str) -> Result<Self, BitFlagParseHexError>
275 where
276 T: util::IntOpsExt,
277 {
278 if let Some(flag) = Self::from_hex_opt(input) {
279 return Ok(flag);
280 }
281
282 #[cfg(feature = "alloc")]
283 let given = alloc::string::ToString::to_string(input);
284 #[cfg(not(feature = "alloc"))]
285 let given = ();
286
287 Err(BitFlagParseHexError { given })
288 }
289
290 #[inline]
297 #[must_use]
298 pub fn from_hex_opt(input: &str) -> Option<Self>
299 where
300 T: util::IntOpsExt,
301 {
302 let input = input.strip_prefix("0x")?;
303 if input.starts_with('+') {
304 return None;
305 }
306 let bits = T::from_str_radix(input, 16).ok()?;
307
308 if bits.count_ones() != 1 {
309 return None;
310 }
311 let bit = bits.lowest_one()?;
312 let bit = u8::try_from(bit).ok()?;
313 (bit < T::BITS).then(|| Self::from_bit_retain(bit))
314 }
315}
316
317macro_rules! impl_bit_flag {
321 ($ty:ty) => {
322 impl<U> BitFlag<$ty, U>
323 where
324 U: ?Sized,
325 {
326 #[inline]
337 #[must_use]
338 pub const fn try_from_flags(flags: BitFlags<$ty, U>) -> Option<Self>
339 where
340 U: BitFlagsSpec<$ty>,
341 {
342 Self::try_from_bits(flags.bits())
343 }
344
345 #[inline]
353 #[must_use]
354 pub const fn try_from_bit(i: u8) -> Option<Self>
355 where
356 U: BitFlagsSpec<$ty>,
357 {
358 const MAX_BITS: usize = size_of::<$ty>() * 8;
359
360 #[expect(clippy::as_conversions, reason = "supported in const functions")]
361 if (i as usize) < MAX_BITS && ((1 << (i as u32)) & !U::VALID_BITS_MASK) == 0 {
362 Some(Self(i, PhantomData, PhantomData))
363 } else {
364 None
365 }
366 }
367
368 #[inline]
375 #[must_use]
376 pub const fn try_from_bits(bits: $ty) -> Option<Self>
377 where
378 U: BitFlagsSpec<$ty>,
379 {
380 if bits.count_ones() == 1
381 && let Some(bit) = bits.lowest_one()
382 {
383 #[expect(
384 clippy::as_conversions,
385 clippy::cast_possible_truncation,
386 reason = "supported in const functions"
387 )]
388 Self::try_from_bit(bit as u8)
389 } else {
390 None
391 }
392 }
393
394 #[inline]
401 #[must_use]
402 pub const fn try_from_bits_retain(bits: $ty) -> Option<Self> {
403 if bits.count_ones() == 1
404 && let Some(bit) = bits.lowest_one()
405 {
406 #[expect(
407 clippy::as_conversions,
408 clippy::cast_possible_truncation,
409 reason = "supported in const functions"
410 )]
411 Some(Self::from_bit_retain(bit as u8))
412 } else {
413 None
414 }
415 }
416
417 #[inline]
425 #[must_use]
426 pub const fn is_valid(&self) -> bool
427 where
428 U: BitFlagsSpec<$ty>,
429 {
430 #[expect(clippy::as_conversions, reason = "supported in const functions")]
431 ((self.0 as u32) < <$ty>::BITS && BitFlags::<$ty, U>::from_flag(*self).is_valid())
432 }
433
434 #[inline]
441 #[must_use]
442 pub const fn mask(&self) -> Option<$ty> {
443 #[expect(clippy::as_conversions, reason = "supported in const functions")]
444 let bit = self.0 as u32;
445 if bit < <$ty>::BITS {
446 Some(1 << bit)
447 } else {
448 None
449 }
450 }
451
452 #[inline]
458 #[must_use]
459 pub const fn intersection(self, other: Self) -> BitFlags<$ty, U> {
460 BitFlags::<$ty, U>::from_flag(self)
461 .intersection(BitFlags::<$ty, U>::from_flag(other))
462 }
463
464 #[inline]
469 #[must_use]
470 pub const fn union(self, other: Self) -> BitFlags<$ty, U> {
471 BitFlags::<$ty, U>::from_flag(self).union(BitFlags::<$ty, U>::from_flag(other))
472 }
473
474 #[inline]
482 #[must_use]
483 pub const fn difference(self, other: Self) -> BitFlags<$ty, U> {
484 BitFlags::<$ty, U>::from_flag(self).difference(BitFlags::<$ty, U>::from_flag(other))
485 }
486
487 #[inline]
494 #[must_use]
495 pub const fn symmetric_difference(self, other: Self) -> BitFlags<$ty, U> {
496 BitFlags::<$ty, U>::from_flag(self)
497 .symmetric_difference(BitFlags::<$ty, U>::from_flag(other))
498 }
499
500 #[inline]
506 #[must_use]
507 pub const fn complement(self) -> BitFlags<$ty, U>
508 where
509 U: BitFlagsSpec<$ty>,
510 {
511 BitFlags::<$ty, U>::from_flag(self).complement()
512 }
513 }
514 };
515}
516
517impl_bit_flag!(u8);
518impl_bit_flag!(u16);
519impl_bit_flag!(u32);
520impl_bit_flag!(u64);
521impl_bit_flag!(u128);
522impl_bit_flag!(usize);
523
524impl_bit_flag!(i8);
525impl_bit_flag!(i16);
526impl_bit_flag!(i32);
527impl_bit_flag!(i64);
528impl_bit_flag!(i128);
529impl_bit_flag!(isize);
530
531impl<T, U> Copy for BitFlag<T, U> where U: ?Sized {}
532
533impl<T, U> Clone for BitFlag<T, U>
534where
535 U: ?Sized,
536{
537 #[inline]
538 fn clone(&self) -> Self {
539 *self
540 }
541}
542
543impl<T, U> Eq for BitFlag<T, U> where U: ?Sized {}
544
545impl<T, U> PartialEq for BitFlag<T, U>
546where
547 U: ?Sized,
548{
549 #[inline]
550 fn eq(&self, other: &Self) -> bool {
551 self.0 == other.0
552 }
553}
554
555impl<T, U> Ord for BitFlag<T, U>
556where
557 U: ?Sized,
558{
559 #[inline]
560 fn cmp(&self, other: &Self) -> Ordering {
561 self.0.cmp(&other.0)
562 }
563}
564
565impl<T, U> PartialOrd for BitFlag<T, U>
566where
567 U: ?Sized,
568{
569 #[inline]
570 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
571 Some(self.cmp(other))
572 }
573}
574
575impl<T, U> Hash for BitFlag<T, U>
576where
577 U: ?Sized,
578{
579 #[inline]
580 fn hash<H: Hasher>(&self, state: &mut H) {
581 self.0.hash(state);
582 }
583}
584
585impl<T, U> TryFrom<BitFlags<T, U>> for BitFlag<T, U>
590where
591 T: Copy + PartialEq + Not<Output = T> + BitAnd<T, Output = T> + util::IntOpsExt,
592 U: BitFlagsSpec<T>,
593{
594 type Error = BitFlagFromFlagsError;
595
596 #[inline]
597 fn try_from(flags: BitFlags<T, U>) -> Result<Self, Self::Error> {
598 let bits = flags.bits();
599 if bits.count_ones() == 1
600 && let Some(bit) = bits.lowest_one()
601 && let Ok(bit) = u8::try_from(bit)
602 && let Some(mask) = T::ONE.checked_shl(u32::from(bit))
603 && (mask & !U::VALID_BITS_MASK) == T::ZERO
604 {
605 Ok(Self::from_bit_retain(bit))
606 } else {
607 Err(BitFlagFromFlagsError)
608 }
609 }
610}
611
612impl<T, U> BitAnd<Self> for BitFlag<T, U>
613where
614 T: BitAnd<T, Output = T> + util::IntOpsExt,
615 U: ?Sized,
616{
617 type Output = BitFlags<T, U>;
618
619 #[inline]
620 fn bitand(self, rhs: Self) -> Self::Output {
621 BitFlags::from(self) & BitFlags::from(rhs)
622 }
623}
624
625impl<T, U> BitOr<Self> for BitFlag<T, U>
626where
627 T: BitOr<T, Output = T> + util::IntOpsExt,
628 U: ?Sized,
629{
630 type Output = BitFlags<T, U>;
631
632 #[inline]
633 fn bitor(self, rhs: Self) -> Self::Output {
634 BitFlags::from(self) | BitFlags::from(rhs)
635 }
636}
637
638impl<T, U> BitXor<Self> for BitFlag<T, U>
639where
640 T: BitXor<T, Output = T> + util::IntOpsExt,
641 U: ?Sized,
642{
643 type Output = BitFlags<T, U>;
644
645 #[inline]
646 fn bitxor(self, rhs: Self) -> Self::Output {
647 BitFlags::from(self) ^ BitFlags::from(rhs)
648 }
649}
650
651impl<T, U> Not for BitFlag<T, U>
652where
653 T: Not<Output = T> + BitAnd<T, Output = T> + util::IntOpsExt,
654 U: ?Sized + BitFlagsSpec<T>,
655{
656 type Output = BitFlags<T, U>;
657
658 #[inline]
659 fn not(self) -> Self::Output {
660 !BitFlags::from(self)
661 }
662}
663
664impl<T, U> fmt::Display for BitFlag<T, U>
670where
671 U: ?Sized + BitFlagsSpec<T>,
672{
673 #[expect(clippy::missing_inline_in_public_items, reason = "bloats binary size")]
674 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
675 let bit = usize::from(self.0);
676 if let Some(names) = U::FLAG_NAMES.get(bit)
677 && let Some(name) = names.first()
678 {
679 write!(f, "{name}")
680 } else {
681 let leading = 1 << (bit % 4);
682 let num_zeroes = bit / 4;
683 write!(f, "0x{leading}{:0<num_zeroes$}", "")
684 }
685 }
686}
687
688impl<T, U> fmt::Debug for BitFlag<T, U>
692where
693 U: ?Sized,
694{
695 #[expect(clippy::missing_inline_in_public_items, reason = "bloats binary size")]
696 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
697 write!(
698 f,
699 "BitFlag(1 << {}, PhantomData<({}, {})>)",
700 self.0,
701 type_name::<T>(),
702 type_name::<U>(),
703 )
704 }
705}
706
707impl<T, U> fmt::Binary for BitFlag<T, U>
709where
710 U: ?Sized,
711{
712 #[inline]
713 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
714 if f.alternate() {
715 write!(f, "0b")?;
716 }
717 let bit = usize::from(self.0);
718 write!(f, "1{:0<bit$}", "")
719 }
720}
721
722impl<T, U> fmt::Octal for BitFlag<T, U>
724where
725 U: ?Sized,
726{
727 #[inline]
728 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
729 if f.alternate() {
730 write!(f, "0o")?;
731 }
732 let bit = usize::from(self.0);
733 let leading = 1 << (bit % 3);
734 let num_zeroes = bit / 3;
735 write!(f, "{leading}{:0<num_zeroes$}", "")
736 }
737}
738
739impl<T, U> fmt::LowerHex for BitFlag<T, U>
741where
742 U: ?Sized,
743{
744 #[inline]
745 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
746 if f.alternate() {
747 write!(f, "0x")?;
748 }
749 let bit = usize::from(self.0);
750 let leading = 1 << (bit % 4);
751 let num_zeroes = bit / 4;
752 write!(f, "{leading}{:0<num_zeroes$}", "")
753 }
754}
755
756impl<T, U> fmt::UpperHex for BitFlag<T, U>
758where
759 U: ?Sized,
760{
761 #[inline]
762 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
763 if f.alternate() {
764 write!(f, "0x")?;
765 }
766 let bit = usize::from(self.0);
767 let leading = 1 << (bit % 4);
768 let num_zeroes = bit / 4;
769 write!(f, "{leading}{:0<num_zeroes$}", "")
770 }
771}
772
773impl<T, U> FromStr for BitFlag<T, U>
774where
775 T: util::IntOpsExt,
776 U: ?Sized + BitFlagsSpec<T>,
777{
778 type Err = BitFlagParseError;
779
780 #[inline]
781 fn from_str(input: &str) -> Result<Self, Self::Err> {
782 Self::try_from_str(input, U::DEFAULT_NAME_MATCHER)
783 }
784}
785
786#[cfg(feature = "serde")]
787#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
788impl<T, U> serde::Serialize for BitFlag<T, U>
789where
790 U: ?Sized + BitFlagsSpec<T>,
791{
792 #[inline]
793 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
794 where
795 S: serde::Serializer,
796 {
797 serializer.collect_str(&self)
799 }
800}
801
802#[cfg(feature = "arbitrary")]
803#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
804impl<'a, T, U> arbitrary::Arbitrary<'a> for BitFlag<T, U>
805where
806 U: ?Sized,
807{
808 #[inline]
809 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
810 u8::arbitrary(u).map(|u| Self(u, PhantomData, PhantomData))
811 }
812
813 #[inline]
814 fn size_hint(depth: usize) -> (usize, Option<usize>) {
815 u8::size_hint(depth)
816 }
817}
818
819#[cfg(feature = "bytemuck")]
820#[cfg_attr(docsrs, doc(cfg(feature = "bytemuck")))]
821unsafe impl<T, U> bytemuck::Zeroable for BitFlag<T, U> where U: ?Sized {}
823
824#[cfg(feature = "bytemuck")]
825#[cfg_attr(docsrs, doc(cfg(feature = "bytemuck")))]
826unsafe impl<T, U> bytemuck::Pod for BitFlag<T, U>
829where
830 T: 'static,
831 U: 'static + ?Sized,
832{
833}
834
835#[cfg(feature = "serde")]
836#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
837impl<'de, T, U> serde::Deserialize<'de> for BitFlag<T, U>
838where
839 T: util::IntOpsExt,
840 U: ?Sized + BitFlagsSpec<T>,
841{
842 #[inline]
843 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
844 where
845 D: serde::Deserializer<'de>,
846 {
847 deserializer.deserialize_str(BitFlagVisitor::<T, U>(PhantomData, PhantomData))
848 }
849}
850
851#[cfg(feature = "serde")]
852#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
853impl<T, U> serde::de::Visitor<'_> for BitFlagVisitor<T, U>
854where
855 T: util::IntOpsExt,
856 U: ?Sized + BitFlagsSpec<T>,
857{
858 type Value = BitFlag<T, U>;
859
860 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
861 fmt_expected(f, U::FLAG_NAMES)
862 }
863
864 #[inline]
865 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
866 where
867 E: serde::de::Error,
868 {
869 Self::Value::from_str(value).map_err(E::custom)
870 }
871}
872
873#[cfg_attr(
875 not(feature = "alloc"),
876 expect(
877 missing_copy_implementations,
878 reason = "the payload is feature-dependent"
879 )
880)]
881#[derive(Clone, Debug)]
882pub struct BitFlagParseError {
883 #[cfg(not(feature = "alloc"))]
884 given: (),
885 #[cfg(feature = "alloc")]
886 given: alloc::string::String,
887
888 expected: &'static [&'static [&'static str]],
889}
890
891impl fmt::Display for BitFlagParseError {
892 #[expect(clippy::missing_inline_in_public_items, reason = "bloats binary size")]
893 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
894 let named_flags = self
895 .expected
896 .iter()
897 .filter(|variants| !variants.is_empty())
898 .count();
899
900 if named_flags == 0 {
901 write!(f, "unrecognized hex flag, ")?;
902 } else {
903 write!(f, "unrecognized named flag or hex flag, ")?;
904 }
905 #[cfg(feature = "alloc")]
906 write!(f, "given: \"{}\", expected ", self.given)?;
907 #[cfg(not(feature = "alloc"))]
908 write!(f, "expected ")?;
909 fmt_expected(f, self.expected)?;
910
911 #[cfg(not(feature = "alloc"))]
912 let () = self.given;
913
914 Ok(())
915 }
916}
917
918impl core::error::Error for BitFlagParseError {}
919
920#[inline]
923fn fmt_expected(
924 f: &mut fmt::Formatter<'_>,
925 expected: &'static [&'static [&'static str]],
926) -> fmt::Result {
927 let named_flags = expected
928 .iter()
929 .filter(|variants| !variants.is_empty())
930 .count();
931
932 if named_flags == 0 {
933 write!(f, "a valid one-bit hex flag with \"0x\" prefix")?;
934 } else {
935 fmt_expected_name(f, expected)?;
936 write!(f, ", or a valid one-bit hex flag with \"0x\" prefix")?;
937 }
938 Ok(())
939}
940
941#[cfg_attr(
943 not(feature = "alloc"),
944 expect(
945 missing_copy_implementations,
946 reason = "the payload is feature-dependent"
947 )
948)]
949#[derive(Clone, Debug)]
950pub struct BitFlagParseNameError {
951 #[cfg(not(feature = "alloc"))]
952 given: (),
953 #[cfg(feature = "alloc")]
954 given: alloc::string::String,
955
956 expected: &'static [&'static [&'static str]],
957}
958
959impl fmt::Display for BitFlagParseNameError {
960 #[expect(clippy::missing_inline_in_public_items, reason = "bloats binary size")]
961 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
962 let named_flags = self
963 .expected
964 .iter()
965 .filter(|variants| !variants.is_empty())
966 .count();
967
968 if named_flags == 0 {
969 write!(f, "does not support named flags")?;
970 } else {
971 write!(f, "unrecognized named flag, ")?;
972 #[cfg(feature = "alloc")]
973 write!(f, "given: \"{}\", expected ", self.given)?;
974 #[cfg(not(feature = "alloc"))]
975 write!(f, "expected ")?;
976 fmt_expected(f, self.expected)?;
977 }
978
979 #[cfg(not(feature = "alloc"))]
980 let () = self.given;
981
982 Ok(())
983 }
984}
985
986impl core::error::Error for BitFlagParseNameError {}
987
988#[inline]
990fn fmt_expected_name(
991 f: &mut fmt::Formatter<'_>,
992 expected: &'static [&'static [&'static str]],
993) -> fmt::Result {
994 let mut separator = " ";
995
996 write!(f, "one of")?;
997 for names in expected {
998 if let Some(name) = names.first() {
999 write!(f, "{separator}\"{name}\"")?;
1000 separator = ", ";
1001 }
1002 }
1003 Ok(())
1004}
1005
1006#[cfg_attr(
1008 not(feature = "alloc"),
1009 expect(
1010 missing_copy_implementations,
1011 reason = "the payload is feature-dependent"
1012 )
1013)]
1014#[derive(Clone, Debug)]
1015pub struct BitFlagParseHexError {
1016 #[cfg(not(feature = "alloc"))]
1017 given: (),
1018 #[cfg(feature = "alloc")]
1019 given: alloc::string::String,
1020}
1021
1022impl fmt::Display for BitFlagParseHexError {
1023 #[expect(clippy::missing_inline_in_public_items, reason = "bloats binary size")]
1024 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1025 write!(f, "unrecognized hex flag")?;
1026
1027 #[cfg(feature = "alloc")]
1028 write!(f, ", given: \"{}\"", self.given)?;
1029 write!(f, ", expected a valid one-bit hex flag with \"0x\" prefix")?;
1030
1031 #[cfg(not(feature = "alloc"))]
1032 let () = self.given;
1033
1034 Ok(())
1035 }
1036}
1037
1038impl core::error::Error for BitFlagParseHexError {}
1039
1040#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default, Debug)]
1045pub struct BitFlagFromFlagsError;
1046
1047impl fmt::Display for BitFlagFromFlagsError {
1048 #[expect(clippy::missing_inline_in_public_items, reason = "bloats binary size")]
1049 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1050 write!(f, "flags do not contain exactly one valid bit")
1051 }
1052}
1053
1054impl core::error::Error for BitFlagFromFlagsError {}