1use std::any;
25use std::collections::BTreeSet;
26use std::fmt::{Debug, Display};
27use std::marker::PhantomData;
28
29use crate::{LibName, TypeName, VariantName};
30
31pub fn type_name<T>() -> String {
32 fn get_ident(path: &str) -> &str {
33 path.rsplit_once("::").map(|(_, n)| n.trim()).unwrap_or(path)
34 }
35
36 let name = any::type_name::<T>().replace('&', "");
37 let mut ident = vec![];
38 for mut arg in name.split([',', '<', '>', '(', ')']) {
39 arg = arg.trim();
40 if arg.is_empty() {
41 continue;
42 }
43 ident.push(get_ident(arg));
44 }
45 ident.join("")
46}
47
48#[derive(Clone, Eq, PartialEq, Debug, Display, Error)]
49#[display("unexpected variant {1} for enum or union {0:?}")]
50pub struct VariantError<V: Debug + Display>(pub Option<String>, pub V);
51
52impl<V: Debug + Display> VariantError<V> {
53 pub fn with<T>(val: V) -> Self { VariantError(Some(type_name::<T>()), val) }
54 pub fn typed(name: impl Into<String>, val: V) -> Self { VariantError(Some(name.into()), val) }
55 pub fn untyped(val: V) -> Self { VariantError(None, val) }
56}
57
58pub trait StrictDumb: Sized {
59 fn strict_dumb() -> Self;
60}
61
62impl<T> StrictDumb for T
63where T: StrictType + Default
64{
65 fn strict_dumb() -> T { T::default() }
66}
67
68pub trait StrictType: Sized {
69 const STRICT_LIB_NAME: &'static str;
70 fn strict_name() -> Option<TypeName> { Some(tn!(type_name::<Self>())) }
71}
72
73impl<T: StrictType> StrictType for &T {
74 const STRICT_LIB_NAME: &'static str = T::STRICT_LIB_NAME;
75}
76
77impl<T> StrictType for PhantomData<T> {
78 const STRICT_LIB_NAME: &'static str = "";
79}
80
81pub trait StrictProduct: StrictType + StrictDumb {}
82
83pub trait StrictTuple: StrictProduct {
84 const FIELD_COUNT: u8;
85 fn strict_check_fields() {
86 let name = Self::strict_name().unwrap_or_else(|| tn!("__unnamed"));
87 assert_ne!(
88 Self::FIELD_COUNT,
89 0,
90 "tuple type {} does not contain a single field defined",
91 name
92 );
93 }
94
95 fn strict_type_info() -> TypeInfo<Self> {
96 Self::strict_check_fields();
97 TypeInfo {
98 lib: libname!(Self::STRICT_LIB_NAME),
99 name: Self::strict_name().map(|name| tn!(name)),
100 cls: TypeClass::Tuple(Self::FIELD_COUNT),
101 dumb: Self::strict_dumb(),
102 }
103 }
104}
105
106pub trait StrictStruct: StrictProduct {
107 const ALL_FIELDS: &'static [&'static str];
108
109 fn strict_check_fields() {
110 let name = Self::strict_name().unwrap_or_else(|| tn!("__unnamed"));
111 assert!(
112 !Self::ALL_FIELDS.is_empty(),
113 "struct type {} does not contain a single field defined",
114 name
115 );
116 let names: BTreeSet<_> = Self::ALL_FIELDS.iter().copied().collect();
117 assert_eq!(
118 names.len(),
119 Self::ALL_FIELDS.len(),
120 "struct type {} contains repeated field names",
121 name
122 );
123 }
124
125 fn strict_type_info() -> TypeInfo<Self> {
126 Self::strict_check_fields();
127 TypeInfo {
128 lib: libname!(Self::STRICT_LIB_NAME),
129 name: Self::strict_name().map(|name| tn!(name)),
130 cls: TypeClass::Struct(Self::ALL_FIELDS),
131 dumb: Self::strict_dumb(),
132 }
133 }
134}
135
136pub trait StrictSum: StrictType {
137 const ALL_VARIANTS: &'static [(u8, &'static str)];
138
139 fn strict_check_variants() {
140 let name = Self::strict_name().unwrap_or_else(|| tn!("__unnamed"));
141 assert!(
142 !Self::ALL_VARIANTS.is_empty(),
143 "type {} does not contain a single variant defined",
144 name
145 );
146 let (ords, names): (BTreeSet<_>, BTreeSet<_>) = Self::ALL_VARIANTS.iter().copied().unzip();
147 assert_eq!(
148 ords.len(),
149 Self::ALL_VARIANTS.len(),
150 "type {} contains repeated variant ids",
151 name
152 );
153 assert_eq!(
154 names.len(),
155 Self::ALL_VARIANTS.len(),
156 "type {} contains repeated variant names",
157 name
158 );
159 }
160
161 fn variant_name_by_tag(tag: u8) -> Option<VariantName> {
162 Self::ALL_VARIANTS
163 .iter()
164 .find(|(n, _)| *n == tag)
165 .map(|(_, variant_name)| vname!(*variant_name))
166 }
167
168 fn variant_ord(&self) -> u8 {
169 let variant = self.variant_name();
170 for (tag, name) in Self::ALL_VARIANTS {
171 if *name == variant {
172 return *tag;
173 }
174 }
175 unreachable!(
176 "not all variants are enumerated for {} enum in StrictUnion::all_variants \
177 implementation",
178 type_name::<Self>()
179 )
180 }
181 fn variant_name(&self) -> &'static str;
182}
183
184pub trait StrictUnion: StrictSum + StrictDumb {
185 fn strict_type_info() -> TypeInfo<Self> {
186 Self::strict_check_variants();
187 TypeInfo {
188 lib: libname!(Self::STRICT_LIB_NAME),
189 name: Self::strict_name().map(|name| tn!(name)),
190 cls: TypeClass::Union(Self::ALL_VARIANTS),
191 dumb: Self::strict_dumb(),
192 }
193 }
194}
195
196pub trait StrictEnum
197where
198 Self: StrictSum + Copy + TryFrom<u8, Error = VariantError<u8>>,
199 u8: From<Self>,
200{
201 fn from_variant_name(name: &VariantName) -> Result<Self, VariantError<&VariantName>> {
202 for (tag, n) in Self::ALL_VARIANTS {
203 if *n == name.as_str() {
204 return Self::try_from(*tag).map_err(|_| VariantError::with::<Self>(name));
205 }
206 }
207 Err(VariantError::with::<Self>(name))
208 }
209
210 fn strict_type_info() -> TypeInfo<Self> {
211 Self::strict_check_variants();
212 TypeInfo {
213 lib: libname!(Self::STRICT_LIB_NAME),
214 name: Self::strict_name().map(|name| tn!(name)),
215 cls: TypeClass::Enum(Self::ALL_VARIANTS),
216 dumb: Self::try_from(Self::ALL_VARIANTS[0].0)
217 .expect("first variant contains invalid value"),
218 }
219 }
220}
221
222pub enum TypeClass {
223 Embedded,
224 Enum(&'static [(u8, &'static str)]),
225 Union(&'static [(u8, &'static str)]),
226 Tuple(u8),
227 Struct(&'static [&'static str]),
228}
229
230pub struct TypeInfo<T: StrictType> {
231 pub lib: LibName,
232 pub name: Option<TypeName>,
233 pub cls: TypeClass,
234 pub dumb: T,
235}
236
237#[cfg(test)]
238mod test {
239 use amplify::confinement::TinyVec;
240
241 use super::*;
242
243 #[test]
244 fn name_derivation() { assert_eq!(Option::<TinyVec<u8>>::strict_name(), None) }
245}