1use std::fmt;
2
3use crate::Ty;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
7pub enum AccessMode {
8 View,
10 MutBorrow,
12 Owned,
14}
15
16#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub enum BaseTy {
19 Unit,
21 U8,
23 U16,
25 U32,
27 U64,
29 I8,
31 I16,
33 I32,
35 I64,
37 F32,
39 F64,
41 String,
43 Bool,
45 Pointer(Box<BaseTy>),
47 Option(Box<BaseTy>),
49 StaticArray {
51 item: Box<BaseTy>,
53 len: usize,
55 },
56 DynamicArray(Box<BaseTy>),
58 Slice(Box<BaseTy>),
60 StaticSlice {
62 item: Box<BaseTy>,
64 len: usize,
66 },
67 Tuple(Vec<BaseTy>),
69 Struct {
71 name: String,
73 args: Vec<BaseTy>,
75 },
76 Enum {
78 name: String,
80 args: Vec<BaseTy>,
82 },
83}
84
85#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
87pub struct NormalizedTy {
88 pub base: BaseTy,
90 pub access: AccessMode,
92}
93
94#[derive(Debug, Clone, PartialEq, Eq)]
96pub enum NormalizeError {
97 NestedReference,
99}
100
101impl fmt::Display for NormalizeError {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 match self {
104 Self::NestedReference => write!(f, "nested RefRo/RefMut is unsupported"),
105 }
106 }
107}
108
109impl std::error::Error for NormalizeError {}
110
111impl BaseTy {
112 pub fn into_ty(self) -> Ty {
114 match self {
115 Self::Unit => Ty::Unit,
116 Self::U8 => Ty::U8,
117 Self::U16 => Ty::U16,
118 Self::U32 => Ty::U32,
119 Self::U64 => Ty::U64,
120 Self::I8 => Ty::I8,
121 Self::I16 => Ty::I16,
122 Self::I32 => Ty::I32,
123 Self::I64 => Ty::I64,
124 Self::F32 => Ty::F32,
125 Self::F64 => Ty::F64,
126 Self::String => Ty::String,
127 Self::Bool => Ty::Bool,
128 Self::Pointer(item) => Ty::Pointer(Box::new(item.into_ty())),
129 Self::Option(item) => Ty::Option(Box::new(item.into_ty())),
130 Self::StaticArray { item, len } => Ty::Array {
131 item: Box::new(item.into_ty()),
132 len,
133 },
134 Self::DynamicArray(item) => Ty::Slice(Box::new(item.into_ty())),
135 Self::Slice(item) => Ty::Slice(Box::new(item.into_ty())),
136 Self::StaticSlice { item, len } => Ty::Array {
137 item: Box::new(item.into_ty()),
138 len,
139 },
140 Self::Tuple(items) => {
141 Ty::Tuple(items.into_iter().map(BaseTy::into_ty).collect::<Vec<_>>())
142 }
143 Self::Struct { name, args } => Ty::Struct {
144 name,
145 args: args.into_iter().map(BaseTy::into_ty).collect::<Vec<_>>(),
146 },
147 Self::Enum { name, args } => Ty::Enum {
148 name,
149 args: args.into_iter().map(BaseTy::into_ty).collect::<Vec<_>>(),
150 },
151 }
152 }
153}
154
155impl NormalizedTy {
156 pub fn into_ty(self) -> Ty {
158 let base_ty = self.base.into_ty();
159 match self.access {
160 AccessMode::View => Ty::RefRo(Box::new(base_ty)),
161 AccessMode::MutBorrow => Ty::RefMut(Box::new(base_ty)),
162 AccessMode::Owned => base_ty,
163 }
164 }
165}
166
167pub fn normalize_ty(ty: &Ty) -> Result<NormalizedTy, NormalizeError> {
169 match ty {
170 Ty::RefRo(inner) => normalize_ty_with_access(inner, AccessMode::View),
171 Ty::RefMut(inner) => normalize_ty_with_access(inner, AccessMode::MutBorrow),
172 _ => normalize_ty_with_access(ty, AccessMode::Owned),
173 }
174}
175
176pub fn normalize_ty_with_access(
178 ty: &Ty,
179 access: AccessMode,
180) -> Result<NormalizedTy, NormalizeError> {
181 let base = normalize_base_ty(ty)?;
182 Ok(NormalizedTy {
183 base: boundary_base_for_access(base, access),
184 access,
185 })
186}
187
188fn normalize_base_ty(ty: &Ty) -> Result<BaseTy, NormalizeError> {
190 match ty {
191 Ty::Unit => Ok(BaseTy::Unit),
192 Ty::U8 => Ok(BaseTy::U8),
193 Ty::U16 => Ok(BaseTy::U16),
194 Ty::U32 => Ok(BaseTy::U32),
195 Ty::U64 => Ok(BaseTy::U64),
196 Ty::I8 => Ok(BaseTy::I8),
197 Ty::I16 => Ok(BaseTy::I16),
198 Ty::I32 => Ok(BaseTy::I32),
199 Ty::I64 => Ok(BaseTy::I64),
200 Ty::F32 => Ok(BaseTy::F32),
201 Ty::F64 => Ok(BaseTy::F64),
202 Ty::String => Ok(BaseTy::String),
203 Ty::Bool => Ok(BaseTy::Bool),
204 Ty::Pointer(item) => Ok(BaseTy::Pointer(Box::new(normalize_base_ty(item)?))),
205 Ty::Option(item) => Ok(BaseTy::Option(Box::new(normalize_base_ty(item)?))),
206 Ty::Array { item, len } => Ok(BaseTy::StaticArray {
207 item: Box::new(normalize_base_ty(item)?),
208 len: *len,
209 }),
210 Ty::Slice(item) => Ok(BaseTy::DynamicArray(Box::new(normalize_base_ty(item)?))),
211 Ty::Tuple(items) => Ok(BaseTy::Tuple(
212 items
213 .iter()
214 .map(normalize_base_ty)
215 .collect::<Result<Vec<_>, _>>()?,
216 )),
217 Ty::Struct { name, args } => Ok(BaseTy::Struct {
218 name: name.clone(),
219 args: args
220 .iter()
221 .map(normalize_base_ty)
222 .collect::<Result<Vec<_>, _>>()?,
223 }),
224 Ty::Enum { name, args } => Ok(BaseTy::Enum {
225 name: name.clone(),
226 args: args
227 .iter()
228 .map(normalize_base_ty)
229 .collect::<Result<Vec<_>, _>>()?,
230 }),
231 Ty::RefRo(_) | Ty::RefMut(_) => Err(NormalizeError::NestedReference),
232 }
233}
234
235fn boundary_base_for_access(base: BaseTy, access: AccessMode) -> BaseTy {
237 match (base, access) {
238 (BaseTy::StaticArray { item, len }, AccessMode::View | AccessMode::MutBorrow) => {
239 BaseTy::StaticSlice { item, len }
240 }
241 (BaseTy::DynamicArray(item), AccessMode::View | AccessMode::MutBorrow) => {
242 BaseTy::Slice(item)
243 }
244 (base, _) => base,
245 }
246}
247
248impl fmt::Display for BaseTy {
249 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250 match self {
251 BaseTy::Unit => write!(f, "Unit"),
252 BaseTy::U8 => write!(f, "u8"),
253 BaseTy::U16 => write!(f, "u16"),
254 BaseTy::U32 => write!(f, "u32"),
255 BaseTy::U64 => write!(f, "u64"),
256 BaseTy::I8 => write!(f, "i8"),
257 BaseTy::I16 => write!(f, "i16"),
258 BaseTy::I32 => write!(f, "i32"),
259 BaseTy::I64 => write!(f, "i64"),
260 BaseTy::F32 => write!(f, "f32"),
261 BaseTy::F64 => write!(f, "f64"),
262 BaseTy::String => write!(f, "String"),
263 BaseTy::Bool => write!(f, "Bool"),
264 BaseTy::Pointer(item) => write!(f, "*{item}"),
265 BaseTy::Option(item) => write!(f, "Option[{item}]"),
266 BaseTy::StaticArray { item, len } => write!(f, "StaticArray[{item}; {len}]"),
267 BaseTy::DynamicArray(item) => write!(f, "DynamicArray[{item}]"),
268 BaseTy::Slice(item) => write!(f, "Slice[{item}]"),
269 BaseTy::StaticSlice { item, len } => write!(f, "StaticSlice[{item}; {len}]"),
270 BaseTy::Tuple(items) => {
271 write!(f, "(")?;
272 for (index, item) in items.iter().enumerate() {
273 if index > 0 {
274 write!(f, ", ")?;
275 }
276 write!(f, "{item}")?;
277 }
278 if items.len() == 1 {
279 write!(f, ",")?;
280 }
281 write!(f, ")")
282 }
283 BaseTy::Struct { name, args } | BaseTy::Enum { name, args } => {
284 if args.is_empty() {
285 write!(f, "{name}")
286 } else {
287 let rendered = args
288 .iter()
289 .map(ToString::to_string)
290 .collect::<Vec<_>>()
291 .join(", ");
292 write!(f, "{name}[{rendered}]")
293 }
294 }
295 }
296 }
297}
298
299impl fmt::Display for NormalizedTy {
300 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301 match self.access {
302 AccessMode::View => write!(f, "view {base}", base = self.base),
303 AccessMode::MutBorrow => write!(f, "mut-borrow {base}", base = self.base),
304 AccessMode::Owned => write!(f, "owned {base}", base = self.base),
305 }
306 }
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312
313 #[test]
314 fn normalize_owned_scalar() {
315 let normalized = normalize_ty(&Ty::I64).expect("owned scalar should normalize");
316 assert_eq!(normalized.access, AccessMode::Owned);
317 assert_eq!(normalized.base, BaseTy::I64);
318 }
319
320 #[test]
321 fn normalize_view_reference() {
322 let normalized = normalize_ty(&Ty::RefRo(Box::new(Ty::Slice(Box::new(Ty::I64)))))
323 .expect("view ref should normalize");
324 assert_eq!(normalized.access, AccessMode::View);
325 assert_eq!(normalized.base, BaseTy::Slice(Box::new(BaseTy::I64)));
326 }
327
328 #[test]
329 fn normalize_mut_borrow_reference() {
330 let normalized = normalize_ty(&Ty::RefMut(Box::new(Ty::Array {
331 item: Box::new(Ty::Bool),
332 len: 4,
333 })))
334 .expect("mutable ref should normalize");
335 assert_eq!(normalized.access, AccessMode::MutBorrow);
336 assert_eq!(
337 normalized.base,
338 BaseTy::StaticSlice {
339 item: Box::new(BaseTy::Bool),
340 len: 4,
341 }
342 );
343 }
344
345 #[test]
346 fn reject_nested_reference() {
347 let err = normalize_ty(&Ty::RefRo(Box::new(Ty::RefMut(Box::new(Ty::I64)))))
348 .expect_err("nested refs should be rejected");
349 assert_eq!(err, NormalizeError::NestedReference);
350 }
351
352 #[test]
353 fn round_trip_ty_conversion() {
354 let source = Ty::RefMut(Box::new(Ty::Tuple(vec![Ty::I64, Ty::Bool])));
355 let normalized = normalize_ty(&source).expect("source type should normalize");
356 let restored = normalized.into_ty();
357 assert_eq!(restored, source);
358 }
359}