1use std::rc::Rc;
2
3use crate::*;
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash)]
6pub struct Ref {
7 pub lifetime: Option<Lifetime>,
8 pub kind: RefKind,
9}
10
11impl ToTokens for Ref {
12 fn to_tokens(&self, tokens: &mut TokenStream2) {
13 let lifetime = &self.lifetime;
14 let output = match &self.kind {
15 RefKind::Ref => quote! { & #lifetime },
16 RefKind::MutRef => quote! { & #lifetime mut },
17 };
18
19 tokens.extend(output);
20 }
21}
22
23#[derive(Debug, Clone, PartialEq, Eq, Hash)]
24pub struct TypeInfo {
25 pub reference: Option<Ref>,
26 pub type_: Rc<RustType>,
27}
28
29#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
30pub enum RefKind {
31 Ref,
32 MutRef,
33}
34
35#[derive(Debug, Clone, PartialEq, Eq, Hash)]
36pub struct Array {
37 pub len: Expr,
38 pub inner: Rc<TypeInfo>,
39}
40
41#[derive(Debug, Clone, PartialEq, Eq, Hash)]
42pub enum RustType {
43 Slice(Rc<TypeInfo>),
44 Array(Rc<Array>),
45 Tuple(Rc<[TypeInfo]>),
46 Option(Rc<TypeInfo>),
47 Box(Rc<TypeInfo>),
48 Vec(Rc<TypeInfo>),
49 HashMap((Rc<TypeInfo>, Rc<TypeInfo>)),
50 Other(Rc<TypePath>),
51}
52
53impl RustType {
54 #[must_use]
58 pub fn is_slice(&self) -> bool {
59 matches!(self, Self::Slice(..))
60 }
61
62 #[must_use]
66 pub fn is_array(&self) -> bool {
67 matches!(self, Self::Array { .. })
68 }
69
70 #[must_use]
74 pub fn is_tuple(&self) -> bool {
75 matches!(self, Self::Tuple(..))
76 }
77
78 #[must_use]
82 pub fn is_option(&self) -> bool {
83 matches!(self, Self::Option(..))
84 }
85
86 #[must_use]
90 pub fn is_box(&self) -> bool {
91 matches!(self, Self::Box(..))
92 }
93
94 #[must_use]
98 pub fn is_vec(&self) -> bool {
99 matches!(self, Self::Vec(..))
100 }
101
102 #[must_use]
106 pub fn is_hash_map(&self) -> bool {
107 matches!(self, Self::HashMap(..))
108 }
109
110 #[must_use]
114 pub fn is_other(&self) -> bool {
115 matches!(self, Self::Other(..))
116 }
117
118 pub fn as_option(&self) -> Option<&TypeInfo> {
119 if let Self::Option(v) = self {
120 Some(v)
121 } else {
122 None
123 }
124 }
125
126 pub fn as_slice(&self) -> Option<&TypeInfo> {
127 if let Self::Slice(v) = self {
128 Some(v)
129 } else {
130 None
131 }
132 }
133
134 pub fn as_tuple(&self) -> Option<&[TypeInfo]> {
135 if let Self::Tuple(v) = self {
136 Some(v.as_ref())
137 } else {
138 None
139 }
140 }
141
142 pub fn as_box(&self) -> Option<&TypeInfo> {
143 if let Self::Box(v) = self {
144 Some(v)
145 } else {
146 None
147 }
148 }
149
150 pub fn as_vec(&self) -> Option<&TypeInfo> {
151 if let Self::Vec(v) = self {
152 Some(v)
153 } else {
154 None
155 }
156 }
157
158 pub fn as_hash_map(&self) -> Option<&(Rc<TypeInfo>, Rc<TypeInfo>)> {
159 if let Self::HashMap(v) = self {
160 Some(v)
161 } else {
162 None
163 }
164 }
165
166 pub fn as_other(&self) -> Option<&TypePath> {
167 if let Self::Other(v) = self {
168 Some(v)
169 } else {
170 None
171 }
172 }
173
174 pub fn as_array(&self) -> Option<&Array> {
175 if let Self::Array(v) = self {
176 Some(v)
177 } else {
178 None
179 }
180 }
181}
182
183impl ToTokens for TypeInfo {
184 fn to_tokens(&self, tokens: &mut TokenStream2) {
185 let ref_tokens = &self.reference;
186 let type_tokens = &self.type_;
187
188 tokens.extend(quote! {
189 #ref_tokens #type_tokens
190 });
191 }
192}
193
194impl ToTokens for RustType {
195 fn to_tokens(&self, tokens: &mut TokenStream2) {
196 let output = match self {
197 RustType::Slice(ty) => quote! { [#ty] },
198 RustType::Array(array) => {
199 let Array { len, inner } = array.as_ref();
200 quote! { [#inner; #len] }
201 }
202 RustType::Tuple(types) => quote! { (#(#types),*) },
203 RustType::Option(ty) => quote! { ::core::option::Option<#ty> },
204 RustType::Box(ty) => quote! { Box<#ty> },
205 RustType::Vec(ty) => quote! { Vec<#ty> },
206 RustType::HashMap((k, v)) => quote! { HashMap<#k, #v> },
207 RustType::Other(path) => quote! { #path },
208 };
209
210 tokens.extend(output);
211 }
212}
213
214impl From<TypeInfo> for Type {
215 fn from(value: TypeInfo) -> Self {
216 parse_quote!(#value)
217 }
218}
219
220impl TypeInfo {
221 pub fn as_type(&self) -> Type {
222 parse_quote!(#self)
223 }
224
225 pub fn is_mut_ref(&self) -> bool {
226 self
227 .reference
228 .as_ref()
229 .is_some_and(|r| matches!(r.kind, RefKind::MutRef))
230 }
231
232 pub fn is_ref(&self) -> bool {
233 self
234 .reference
235 .as_ref()
236 .is_some_and(|r| matches!(r.kind, RefKind::Ref))
237 }
238
239 pub fn is_owned(&self) -> bool {
240 self.reference.is_none()
241 }
242
243 pub fn from_type(typ: &Type) -> syn::Result<Self> {
244 if let Type::Reference(reference) = typ {
245 let ownership = if reference.mutability.is_some() {
246 RefKind::MutRef
247 } else {
248 RefKind::Ref
249 };
250
251 if let Type::Slice(slice) = &*reference.elem {
252 return Ok(Self {
253 reference: Some(Ref {
254 lifetime: reference.lifetime.clone(),
255 kind: ownership,
256 }),
257 type_: RustType::Slice(Self::from_type(&slice.elem)?.into()).into(),
258 });
259 } else {
260 let mut ref_type = Self::from_type(&reference.elem)?;
261 ref_type.reference = Some(Ref {
262 lifetime: reference.lifetime.clone(),
263 kind: ownership,
264 });
265
266 return Ok(ref_type);
267 }
268 }
269
270 let output = match typ {
271 Type::Slice(slice) => {
272 let inner = Self::from_type(&slice.elem)?;
273
274 Self {
275 reference: None,
276 type_: RustType::Slice(inner.into()).into(),
277 }
278 }
279 Type::Array(TypeArray { elem, len, .. }) => {
280 let inner = Self::from_type(elem)?;
281
282 Self {
283 reference: None,
284 type_: RustType::Array(
285 Array {
286 len: len.clone(),
287 inner: inner.into(),
288 }
289 .into(),
290 )
291 .into(),
292 }
293 }
294 Type::Path(path) => {
295 let last_segment = path.path.last_segment();
296
297 let last_segment_ident = last_segment.ident.to_string();
298
299 match last_segment_ident.as_str() {
300 "HashMap" => {
301 let (k, v) = last_segment.first_two_generics().unwrap();
302
303 Self {
304 reference: None,
305 type_: RustType::HashMap((
306 Self::from_type(k.as_type()?)?.into(),
307 Self::from_type(v.as_type()?)?.into(),
308 ))
309 .into(),
310 }
311 }
312 "Box" => {
313 let inner = last_segment.first_generic().unwrap();
314
315 Self {
316 reference: None,
317
318 type_: RustType::Box(Self::from_type(inner.as_type()?)?.into()).into(),
319 }
320 }
321 "Vec" => {
322 let inner = last_segment.first_generic().unwrap();
323
324 Self {
325 reference: None,
326
327 type_: RustType::Vec(Self::from_type(inner.as_type()?)?.into()).into(),
328 }
329 }
330 "Option" => {
331 let inner = last_segment.first_generic().unwrap();
332
333 Self {
334 reference: None,
335
336 type_: RustType::Option(Self::from_type(inner.as_type()?)?.into()).into(),
337 }
338 }
339 _ => Self {
340 reference: None,
341
342 type_: RustType::Other(path.clone().into()).into(),
343 },
344 }
345 }
346 Type::Tuple(tuple) => {
347 let types: Vec<Self> = tuple
348 .elems
349 .iter()
350 .map(Self::from_type)
351 .collect::<syn::Result<Vec<Self>>>()?;
352
353 let type_enum = RustType::Tuple(types.into());
354
355 Self {
356 reference: None,
357 type_: type_enum.into(),
358 }
359 }
360
361 _ => bail!(
362 typ,
363 "Unsupported type {}",
364 typ.to_token_stream().to_string()
365 ),
366 };
367
368 Ok(output)
369 }
370
371 #[must_use]
375 pub fn is_slice(&self) -> bool {
376 matches!(*self.type_, RustType::Slice(..))
377 }
378
379 #[must_use]
383 pub fn is_array(&self) -> bool {
384 matches!(*self.type_, RustType::Array { .. })
385 }
386
387 #[must_use]
391 pub fn is_tuple(&self) -> bool {
392 matches!(*self.type_, RustType::Tuple(..))
393 }
394
395 #[must_use]
399 pub fn is_option(&self) -> bool {
400 matches!(*self.type_, RustType::Option(..))
401 }
402
403 #[must_use]
407 pub fn is_box(&self) -> bool {
408 matches!(*self.type_, RustType::Box(..))
409 }
410
411 #[must_use]
415 pub fn is_vec(&self) -> bool {
416 matches!(*self.type_, RustType::Vec(..))
417 }
418
419 #[must_use]
423 pub fn is_hash_map(&self) -> bool {
424 matches!(*self.type_, RustType::HashMap(..))
425 }
426
427 #[must_use]
431 pub fn is_other(&self) -> bool {
432 matches!(*self.type_, RustType::Other(..))
433 }
434}
435
436#[cfg(test)]
437mod tests {
438 use quote::ToTokens;
439 use syn::Type;
440
441 use super::*;
442
443 fn assert_round_trip(input_str: &str) {
444 let original_type: Type = syn::parse_str(input_str).expect("Invalid Rust syntax in test");
445
446 let info = TypeInfo::from_type(&original_type).unwrap();
447
448 let tokens = info.to_token_stream();
449
450 let output_str = tokens.to_string();
451
452 let normalized_input = quote!(#original_type).to_string();
453
454 assert_eq!(
455 output_str, normalized_input,
456 "Round trip failed for: {}",
457 input_str
458 );
459 }
460
461 fn assert_is_option(ty: &str) {
462 let original: Type = syn::parse_str(ty).unwrap();
463 let info = TypeInfo::from_type(&original).unwrap();
464
465 if let RustType::Option(_) = *info.type_ {
466 } else {
468 panic!("Expected Option, got {:?}", info.type_);
469 }
470 }
471
472 #[test]
473 fn test_primitives() {
474 assert_round_trip("i32");
475 assert_round_trip("String");
476 assert_round_trip("bool");
477 }
478
479 #[test]
480 fn test_references() {
481 assert_round_trip("&i32");
482 assert_round_trip("&mut String");
483 assert_round_trip("&'a [u8]");
484 assert_round_trip("&'static mut Vec<i32>");
485 }
486
487 #[test]
488 fn test_wrappers() {
489 assert_round_trip("::core::option::Option<i32>");
490 assert_round_trip("Vec<String>");
491 assert_round_trip("Box<MyStruct>");
492 assert_round_trip("HashMap<String, i32>");
493
494 assert_is_option("::core::option::Option<i32>");
495 }
496
497 #[test]
498 fn test_nested_complex() {
499 assert_round_trip("::core::option::Option<Vec<Box<i32>>>");
500 assert_round_trip("&mut HashMap<String, ::core::option::Option<Vec<u8>>>");
501 }
502
503 #[test]
504 fn test_arrays_and_tuples() {
505 assert_round_trip("[u8; 4]");
506 assert_round_trip("(&str, i32, ::core::option::Option<bool>)");
507 }
508
509 #[test]
510 fn test_slices() {
511 assert_round_trip("[u8]");
512 assert_round_trip("&[i32]");
513 }
514
515 fn get_info(s: &str) -> TypeInfo {
516 let ty: Type =
517 syn::parse_str(s).unwrap_or_else(|_| panic!("Failed to parse rust syntax: {}", s));
518 TypeInfo::from_type(&ty).unwrap()
519 }
520
521 fn assert_inner_eq(info: &TypeInfo, expected: &str) {
522 let tokens = info.to_token_stream().to_string();
523 assert_eq!(tokens, expected, "Inner type mismatch");
524 }
525
526 #[test]
527 fn test_wrappers_option() {
528 let info = get_info("Option<i32>");
529
530 if let RustType::Option(inner) = &*info.type_ {
531 assert_inner_eq(inner, "i32");
532 } else {
533 panic!("Failed to parse Option. Got different variant.");
534 }
535 }
536
537 #[test]
538 fn test_wrappers_vec() {
539 let info = get_info("Vec<String>");
540
541 if let RustType::Vec(inner) = &*info.type_ {
542 assert_inner_eq(inner, "String");
543 } else {
544 panic!("Failed to parse Vec.");
545 }
546 }
547
548 #[test]
549 fn test_wrappers_box() {
550 let info = get_info("Box<MyStruct>");
551
552 if let RustType::Box(inner) = &*info.type_ {
553 assert_inner_eq(inner, "MyStruct");
554 } else {
555 panic!("Failed to parse Box.");
556 }
557 }
558
559 #[test]
560 fn test_hashmap() {
561 let info = get_info("HashMap<String, i32>");
562
563 if let RustType::HashMap((k, v)) = &*info.type_ {
564 assert_inner_eq(k, "String");
565 assert_inner_eq(v, "i32");
566 } else {
567 panic!("Failed to parse HashMap. Got type {:#?}", info.type_);
568 }
569 }
570
571 #[test]
572 fn test_slice() {
573 let info = get_info("[u8]");
574
575 if let RustType::Slice(inner) = &*info.type_ {
576 assert_inner_eq(inner, "u8");
577 } else {
578 panic!("Failed to parse Slice.");
579 }
580 }
581
582 #[test]
583 fn test_array() {
584 let info = get_info("[u8; 4]");
585
586 if let RustType::Array(array) = &*info.type_ {
587 let Array { len, inner } = array.as_ref();
588 assert_inner_eq(inner, "u8");
589 let len_str = quote!(#len).to_string();
590 assert_eq!(len_str, "4");
591 } else {
592 panic!("Failed to parse Array.");
593 }
594 }
595
596 #[test]
597 fn test_tuple() {
598 let info = get_info("(i32, bool, String)");
599
600 if let RustType::Tuple(items) = &*info.type_ {
601 assert_eq!(items.len(), 3);
602 assert_inner_eq(&items[0], "i32");
603 assert_inner_eq(&items[1], "bool");
604 assert_inner_eq(&items[2], "String");
605 } else {
606 panic!("Failed to parse Tuple.");
607 }
608 }
609
610 #[test]
611 fn test_nested_wrappers() {
612 let info = get_info("Option<Vec<Box<i32>>>");
613
614 if let RustType::Option(l1) = &*info.type_
615 && let RustType::Vec(l2) = &*l1.type_
616 && let RustType::Box(l3) = &*l2.type_ {
617 assert_inner_eq(l3, "i32");
618 return;
619 }
620 panic!("Failed to parse deeply nested wrappers");
621 }
622
623 #[test]
624 fn test_references_mut() {
625 let info = get_info("&mut String");
626
627 assert!(info.reference.is_some(), "Should be a reference");
628 let ref_data = info.reference.unwrap();
629
630 match ref_data.kind {
631 RefKind::MutRef => {}
632 _ => panic!("Expected MutRef"),
633 }
634
635 if let RustType::Other(path) = &*info.type_ {
636 let path_str = quote!(#path).to_string();
637 assert_eq!(path_str, "String");
638 } else {
639 panic!("Inner type should be Other(String)");
640 }
641 }
642
643 #[test]
644 fn test_references_const() {
645 let info = get_info("&i32");
646
647 let ref_data = info.reference.expect("Should be a reference");
648 match ref_data.kind {
649 RefKind::Ref => {}
650 _ => panic!("Expected Ref (const)"),
651 }
652 }
653
654 #[test]
655 fn test_other_path() {
656 let info = get_info("my_crate::MyType");
657
658 if let RustType::Other(path) = &*info.type_ {
659 let s = quote::quote!(#path).to_string().replace(" ", "");
660 assert_eq!(s, "my_crate::MyType");
661 } else {
662 panic!("Should have parsed as Other/Path");
663 }
664 }
665}