1use crate::{BitWidth, EncodingError, Signedness};
2use parse_display::Display;
3
4trait Sealed {}
5
6pub(crate) enum Signed {}
7pub(crate) enum Unsigned {}
8
9#[allow(private_bounds)]
10pub(crate) trait Sign: Sealed {
11 type Sign;
12}
13
14macro_rules! sign_impl {
15 ($($ty:ty => $ret:ident);* $(;)?) => {
16 $(
17 impl Sealed for $ty {}
18 impl Sign for $ty {
19 type Sign = $ret;
20 }
21 )*
22 };
23}
24
25sign_impl! {
26 u8 => Unsigned;
27 u16 => Unsigned;
28 u32 => Unsigned;
29 u64 => Unsigned;
30 u128 => Unsigned;
31 usize => Unsigned;
32 i8 => Signed;
33 i16 => Signed;
34 i32 => Signed;
35 i64 => Signed;
36 i128 => Signed;
37 isize => Signed;
38}
39
40#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display)]
41#[non_exhaustive]
42enum OpaqueInner {
43 #[display("U-{0}")]
44 Unsigned(u128),
45 #[display("I-{0}")]
46 Signed(i128),
47}
48
49#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display)]
67#[repr(transparent)]
68pub struct Opaque(OpaqueInner);
69
70impl Opaque {
71 #[allow(private_bounds)]
73 #[inline]
74 pub fn signed<V>(value: V) -> Self
75 where V: Sign<Sign = Signed>,
76 Self: From<V>
77 {
78 Self::from(value)
79 }
80
81 #[allow(private_bounds)]
83 #[inline]
84 pub fn unsigned<V>(value: V) -> Self
85 where V: Sign<Sign = Unsigned>,
86 Self: From<V>
87 {
88 Self::from(value)
89 }
90
91 #[inline]
93 pub fn sign(&self) -> Signedness {
94 match self.0 {
95 OpaqueInner::Unsigned(_) => Signedness::Unsigned,
96 OpaqueInner::Signed(_) => Signedness::Signed,
97 }
98 }
99}
100
101macro_rules! from_impl {
104 ($($ty:ty => $var:ident);* $(;)?) => {
105 $(
106 impl From<$ty> for Opaque {
107 #[inline]
108 fn from(value: $ty) -> Self {
109 Self(OpaqueInner::$var(value as _))
110 }
111 }
112 )*
113 };
114}
115
116from_impl! {
117 u8 => Unsigned;
118 u16 => Unsigned;
119 u32 => Unsigned;
120 u64 => Unsigned;
121 u128 => Unsigned;
122 usize => Unsigned;
123 i8 => Signed;
124 i16 => Signed;
125 i32 => Signed;
126 i64 => Signed;
127 i128 => Signed;
128 isize => Signed;
129}
130
131macro_rules! into_impl {
134 ($($($width:tt)::* => $ty:ty => $var:ident != $isnt:ident);* $(;)?) => {
135 $(
136 impl TryInto<$ty> for Opaque {
137 type Error = EncodingError;
138 #[inline]
139 fn try_into(self) -> Result<$ty, Self::Error> {
140 match self.0 {
141 OpaqueInner::$var(x) => {
142 x.try_into().map_err(|_| EncodingError::TooLarge {
143 value: self,
144 requested_width: BitWidth::$($width)*,
145 })
146 },
147 OpaqueInner::$isnt(_) => Err(EncodingError::SignMismatch {
148 expected: Signedness::$var,
149 got: Signedness::$isnt,
150 }),
151 }
152 }
153 }
154 )*
155 };
156}
157
158into_impl! {
159 Bit8 => u8 => Unsigned != Signed;
160 Bit16 => u16 => Unsigned != Signed;
161 Bit32 => u32 => Unsigned != Signed;
162 Bit64 => u64 => Unsigned != Signed;
163 Bit128 => u128 => Unsigned != Signed;
164 native::() => usize => Unsigned != Signed;
165 Bit8 => i8 => Signed != Unsigned;
166 Bit16 => i16 => Signed != Unsigned;
167 Bit32 => i32 => Signed != Unsigned;
168 Bit64 => i64 => Signed != Unsigned;
169 Bit128 => i128 => Signed != Unsigned;
170 native::() => isize => Signed != Unsigned;
171}