1use std::fmt::Display;
2
3use quote::quote;
4use syn::{punctuated::Punctuated, token::Comma};
5#[derive(Debug, Clone)]
9pub struct GenericParamAnnotations {
10 pub cffi_type: Option<String>,
11}
12
13impl Into<proc_macro2::TokenStream> for GenericParamAnnotations {
14 fn into(self) -> proc_macro2::TokenStream {
16 let cffi_type = self
17 .cffi_type
18 .as_ref()
19 .map(|s| proc_macro2::Literal::string(s))
20 .map(|lit| quote! { Some(::std::string::String::from(#lit)) })
21 .unwrap_or(quote! { None });
22 quote! {
23 rs_schema::GenericParamAnnotations {
24 cffi_type: #cffi_type,
25 }
26 }
27 }
28}
29
30impl GenericParamAnnotations {
31 pub fn new() -> Self {
33 GenericParamAnnotations { cffi_type: None }
34 }
35}
36
37#[derive(Debug, Clone)]
38pub struct GenericParamSchema {
39 pub name: String,
40 pub annotations: Option<GenericParamAnnotations>,
41}
42
43impl Into<proc_macro2::TokenStream> for GenericParamSchema {
44 fn into(self) -> proc_macro2::TokenStream {
46 let name_lit = proc_macro2::Literal::string(&self.name);
47 let annotations_tokens = if let Some(annotations) = self.annotations {
48 let annotations_ts: proc_macro2::TokenStream = annotations.into();
49 quote! { Some(#annotations_ts) }
50 } else {
51 quote! { None }
52 };
53
54 quote! {
55 rs_schema::GenericParamSchema {
56 name: ::std::string::String::from(#name_lit),
57 annotations: #annotations_tokens,
58 }
59 }
60 }
61}
62
63#[derive(Debug, Clone)]
64pub struct TraitSchema {
65 pub name: String,
66 pub functions: Vec<FunctionSchema>,
67 pub generics: Vec<GenericParamSchema>,
68 pub supertraits: Vec<TypeSchema>,
69}
70
71impl Into<proc_macro2::TokenStream> for TraitSchema {
72 fn into(self) -> proc_macro2::TokenStream {
74 let name_lit = proc_macro2::Literal::string(&self.name);
75 let field_tokens: Punctuated<proc_macro2::TokenStream, Comma> = self
76 .functions
77 .into_iter()
78 .map(|f| Into::<proc_macro2::TokenStream>::into(f))
79 .collect::<Punctuated<_, Comma>>();
80
81 let generics_tokens: Punctuated<proc_macro2::TokenStream, Comma> = self
82 .generics
83 .into_iter()
84 .map(|g| Into::<proc_macro2::TokenStream>::into(g))
85 .collect::<Punctuated<_, Comma>>();
86
87 let supertraits_tokens: Punctuated<proc_macro2::TokenStream, Comma> = self
88 .supertraits
89 .into_iter()
90 .map(|s| Into::<proc_macro2::TokenStream>::into(s))
91 .collect::<Punctuated<_, Comma>>();
92
93 quote! {
94 {
95 let functions = ::std::vec![
96 #field_tokens
97 ];
98 let generics = ::std::vec![
99 #generics_tokens
100 ];
101 let supertraits = ::std::vec![
102 #supertraits_tokens
103 ];
104 rs_schema::TraitSchema {
105 name: ::std::string::String::from(#name_lit),
106 functions: functions,
107 generics: generics,
108 supertraits: supertraits,
109 }
110 }
111 }
112 }
113}
114
115#[derive(Debug, Clone)]
116pub struct FunctionSchema {
117 pub name: String,
118 pub args: Vec<FunctionArgSchema>,
119 pub return_type: TypeSchema,
120 pub body: Option<String>,
121 pub extern_layout: Option<String>,
122 pub annotations: Option<FunctionAnnotations>,
123}
124
125impl Into<proc_macro2::TokenStream> for FunctionSchema {
126 fn into(self) -> proc_macro2::TokenStream {
128 let name_lit = proc_macro2::Literal::string(&self.name);
129 let args_tokens: Punctuated<proc_macro2::TokenStream, Comma> = self
130 .args
131 .into_iter()
132 .map(|arg| Into::<proc_macro2::TokenStream>::into(arg))
133 .collect::<Punctuated<_, Comma>>();
134 let return_type_tokens: proc_macro2::TokenStream = self.return_type.into();
135
136 let body = if let Some(body) = self.body {
137 let body_lit = proc_macro2::Literal::string(&body);
138 quote! {
139 Some(::std::string::String::from(#body_lit))
140 }
141 } else {
142 quote! {
143 None
144 }
145 };
146
147 let extern_layout = if let Some(extern_layout) = self.extern_layout {
148 let extern_layout = proc_macro2::Literal::string(&extern_layout);
149 quote! {
150 Some(::std::string::String::from(#extern_layout))
151 }
152 } else {
153 quote! {
154 None
155 }
156 };
157
158 let annotations_tokens = if let Some(annotations) = self.annotations {
159 let annotations_ts: proc_macro2::TokenStream = annotations.into();
160 quote! { Some(#annotations_ts) }
161 } else {
162 quote! { None }
163 };
164
165 quote! {
166 rs_schema::FunctionSchema {
167 name: ::std::string::String::from(#name_lit),
168 args: ::std::vec![
169 #args_tokens
170 ],
171 return_type: #return_type_tokens,
172 body: #body,
173 extern_layout: #extern_layout,
174 annotations: #annotations_tokens,
175 }
176 }
177 }
178}
179
180impl Display for FunctionSchema {
181 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183 if let Some(extern_layout) = &self.extern_layout {
184 write!(f, "extern \"{}\" ", extern_layout)?;
185 }
186 write!(f, "fn {}(", self.name)?;
187 for (i, arg) in self.args.iter().enumerate() {
188 if i > 0 {
189 write!(f, ", ")?;
190 }
191 if let Some(ty) = &arg.ty {
192 write!(f, "{}: {}", arg.name, ty)?;
193 } else {
194 write!(f, "&{}", arg.name)?;
197 }
198 }
199 write!(f, ")")?;
200 write!(f, " -> {}", self.return_type)?;
201 if let Some(body) = &self.body {
202 write!(f, " {{\n{}\n}}", body)?;
203 } else {
204 write!(f, ";")?;
205 }
206 Ok(())
207 }
208}
209
210#[derive(Debug, Clone)]
211pub struct FunctionAnnotations {
212 pub cffi_impl_no_op: bool,
213}
214
215impl Into<proc_macro2::TokenStream> for FunctionAnnotations {
216 fn into(self) -> proc_macro2::TokenStream {
218 let cffi_impl_no_op = self.cffi_impl_no_op;
219 quote! {
220 rs_schema::FunctionAnnotations {
221 cffi_impl_no_op: #cffi_impl_no_op,
222 }
223 }
224 }
225}
226
227impl FunctionAnnotations {
228 pub fn new() -> Self {
230 FunctionAnnotations {
231 cffi_impl_no_op: false,
232 }
233 }
234}
235
236#[derive(Debug, Clone)]
238pub struct TypeSchema {
239 pub ty: String,
240 pub generic_ty_args: Vec<TypeSchema>,
241}
242
243impl TypeSchema {
244 pub fn new_simple(ty: String) -> Self {
245 TypeSchema {
246 ty,
247 generic_ty_args: vec![],
248 }
249 }
250}
251
252impl Into<proc_macro2::TokenStream> for TypeSchema {
253 fn into(self) -> proc_macro2::TokenStream {
255 let ty_lit = proc_macro2::Literal::string(&self.ty);
256 let generic_ty_args_tokens: Punctuated<proc_macro2::TokenStream, Comma> = self
257 .generic_ty_args
258 .into_iter()
259 .map(|arg| Into::<proc_macro2::TokenStream>::into(arg))
260 .collect::<Punctuated<_, Comma>>();
261
262 quote! {
263 rs_schema::TypeSchema {
264 ty: ::std::string::String::from(#ty_lit),
265 generic_ty_args: ::std::vec![
266 #generic_ty_args_tokens
267 ],
268 }
269 }
270 }
271}
272
273impl Display for TypeSchema {
274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
276 write!(f, "{}", self.ty)?;
277 if !self.generic_ty_args.is_empty() {
278 write!(f, "<")?;
279 for (i, arg) in self.generic_ty_args.iter().enumerate() {
280 if i > 0 {
281 write!(f, ", ")?;
282 }
283 write!(f, "{}", arg)?;
284 }
285 write!(f, ">")?;
286 }
287 Ok(())
288 }
289}
290
291#[derive(Debug, Clone)]
292pub struct FunctionArgSchema {
293 pub name: String,
294 pub ty: Option<TypeSchema>,
295 pub annotations: Option<FnArgAnnotations>,
296}
297
298impl Into<proc_macro2::TokenStream> for FunctionArgSchema {
299 fn into(self) -> proc_macro2::TokenStream {
301 let name_lit = proc_macro2::Literal::string(&self.name);
302 let ty_tokens = self.ty.map(|ty| Into::<proc_macro2::TokenStream>::into(ty));
303 let annotations_tokens = if let Some(annotations) = self.annotations {
304 let annotations_ts: proc_macro2::TokenStream = annotations.into();
305 quote! { Some(#annotations_ts) }
306 } else {
307 quote! { None }
308 };
309
310 let ty_tokens: proc_macro2::TokenStream = if let Some(ty_tokens) = ty_tokens {
311 quote! { ty: ::std::option::Option::Some(#ty_tokens), }
312 } else {
313 quote! { ty: ::std::option::Option::None, }
314 };
315
316 quote! {
317 rs_schema::FunctionArgSchema {
318 name: ::std::string::String::from(#name_lit),
319 #ty_tokens
320 annotations: #annotations_tokens,
321 }
322 }
323 }
324}
325
326#[derive(Debug, Clone)]
327pub struct FnArgAnnotations {
328 pub collection_as_item: bool,
331 pub assert_len: Option<usize>, pub cffi_type: Option<String>, }
334
335impl Into<proc_macro2::TokenStream> for FnArgAnnotations {
336 fn into(self) -> proc_macro2::TokenStream {
338 let collection_as_item = self.collection_as_item;
339 let cffi_type = self
340 .cffi_type
341 .as_ref()
342 .map(|s| proc_macro2::Literal::string(s))
343 .map(|lit| quote! { Some(::std::string::String::from(#lit)) })
344 .unwrap_or(quote! { None });
345 let assert_len = match self.assert_len {
346 Some(len) => quote! { Some(#len) },
347 None => quote! { None },
348 };
349 quote! {
350 rs_schema::FnArgAnnotations {
351 collection_as_item: #collection_as_item,
352 assert_len: #assert_len,
353 cffi_type: #cffi_type,
354 }
355 }
356 }
357}
358
359impl FnArgAnnotations {
360 pub fn new() -> Self {
362 FnArgAnnotations {
363 collection_as_item: false,
364 assert_len: None,
365 cffi_type: None,
366 }
367 }
368}
369
370#[cfg(test)]
371mod tests {
372 use super::*;
373
374 #[test]
375 fn test_fn_arg_annotations_new() {
376 let annotations = FnArgAnnotations::new();
377 assert!(!annotations.collection_as_item);
378 assert!(annotations.assert_len.is_none());
379 }
380
381 #[test]
382 fn test_fn_arg_annotations_creation_with_values() {
383 let annotations = FnArgAnnotations {
384 collection_as_item: true,
385 assert_len: Some(42),
386 cffi_type: Some("ptr<i32>".to_string()),
387 };
388 assert!(annotations.collection_as_item);
389 assert_eq!(annotations.assert_len, Some(42));
390 assert_eq!(annotations.cffi_type, Some("ptr<i32>".to_string()));
391 }
392
393 #[test]
394 fn test_function_arg_schema_creation() {
395 let arg = FunctionArgSchema {
396 name: "test_arg".to_string(),
397 ty: Some(TypeSchema {
398 ty: "String".to_string(),
399 generic_ty_args: vec![],
400 }),
401 annotations: Some(FnArgAnnotations {
402 collection_as_item: false,
403 assert_len: Some(10),
404 cffi_type: None,
405 }),
406 };
407
408 assert_eq!(arg.name, "test_arg");
409 assert_eq!(arg.ty.as_ref().unwrap().ty, "String");
410 assert!(arg.annotations.is_some());
411 if let Some(ann) = arg.annotations {
412 assert_eq!(ann.assert_len, Some(10));
413 }
414 }
415
416 #[test]
417 fn test_function_arg_schema_without_annotations() {
418 let arg = FunctionArgSchema {
419 name: "simple_arg".to_string(),
420 ty: Some(TypeSchema {
421 ty: "i32".to_string(),
422 generic_ty_args: vec![],
423 }),
424 annotations: None,
425 };
426
427 assert_eq!(arg.name, "simple_arg");
428 assert_eq!(arg.ty.as_ref().unwrap().ty, "i32");
429 assert!(arg.annotations.is_none());
430 }
431
432 #[test]
433 fn test_function_schema_creation() {
434 let args = vec![
435 FunctionArgSchema {
436 name: "arg1".to_string(),
437 ty: Some(TypeSchema {
438 ty: "String".to_string(),
439 generic_ty_args: vec![],
440 }),
441 annotations: None,
442 },
443 FunctionArgSchema {
444 name: "arg2".to_string(),
445 ty: Some(TypeSchema {
446 ty: "i32".to_string(),
447 generic_ty_args: vec![],
448 }),
449 annotations: None,
450 },
451 ];
452
453 let func = FunctionSchema {
454 name: "test_fn".to_string(),
455 args,
456 return_type: TypeSchema {
457 ty: "bool".to_string(),
458 generic_ty_args: vec![],
459 },
460 body: None,
461 extern_layout: None,
462 annotations: None,
463 };
464
465 assert_eq!(func.name, "test_fn");
466 assert_eq!(func.args.len(), 2);
467 assert_eq!(func.return_type.ty, "bool");
468 assert!(func.body.is_none());
469 }
470
471 #[test]
472 fn test_function_schema_display() {
473 let args = vec![
474 FunctionArgSchema {
475 name: "x".to_string(),
476 ty: Some(TypeSchema {
477 ty: "i32".to_string(),
478 generic_ty_args: vec![],
479 }),
480 annotations: None,
481 },
482 FunctionArgSchema {
483 name: "y".to_string(),
484 ty: Some(TypeSchema {
485 ty: "String".to_string(),
486 generic_ty_args: vec![],
487 }),
488 annotations: None,
489 },
490 ];
491
492 let func = FunctionSchema {
493 name: "my_function".to_string(),
494 args,
495 return_type: TypeSchema {
496 ty: "bool".to_string(),
497 generic_ty_args: vec![],
498 },
499 body: None,
500 extern_layout: None,
501 annotations: None,
502 };
503
504 let display_str = format!("{}", func);
505 assert!(display_str.contains("my_function"));
506 assert!(display_str.contains("x: i32"));
507 assert!(display_str.contains("y: String"));
508 assert!(display_str.contains("-> bool"));
509 assert!(display_str.contains(";"));
510 }
511
512 #[test]
513 fn test_function_schema_display_with_body() {
514 let args = vec![];
515 let func = FunctionSchema {
516 name: "with_body".to_string(),
517 args,
518 return_type: TypeSchema {
519 ty: "String".to_string(),
520 generic_ty_args: vec![],
521 },
522 body: Some("return \"hello\".to_string();".to_string()),
523 extern_layout: None,
524 annotations: None,
525 };
526
527 let display_str = format!("{}", func);
528 assert!(display_str.contains("with_body"));
529 assert!(display_str.contains("hello"));
530 assert!(display_str.contains("{"));
531 assert!(display_str.contains("}"));
532 }
533
534 #[test]
535 fn test_trait_schema_creation() {
536 let functions = vec![
537 FunctionSchema {
538 name: "method1".to_string(),
539 args: vec![],
540 return_type: TypeSchema {
541 ty: "()".to_string(),
542 generic_ty_args: vec![],
543 },
544 body: None,
545 extern_layout: None,
546 annotations: None,
547 },
548 FunctionSchema {
549 name: "method2".to_string(),
550 args: vec![FunctionArgSchema {
551 name: "arg".to_string(),
552 ty: Some(TypeSchema {
553 ty: "String".to_string(),
554 generic_ty_args: vec![],
555 }),
556 annotations: None,
557 }],
558 return_type: TypeSchema {
559 ty: "String".to_string(),
560 generic_ty_args: vec![],
561 },
562 body: None,
563 extern_layout: None,
564 annotations: None,
565 },
566 ];
567
568 let schema = TraitSchema {
569 name: "MyTrait".to_string(),
570 functions,
571 generics: vec![],
572 supertraits: vec![],
573 };
574
575 assert_eq!(schema.name, "MyTrait");
576 assert_eq!(schema.functions.len(), 2);
577 assert_eq!(schema.functions[0].name, "method1");
578 assert_eq!(schema.functions[1].name, "method2");
579 assert_eq!(schema.generics.len(), 0);
580 assert_eq!(schema.supertraits.len(), 0);
581 }
582
583 #[test]
584 fn test_function_schema_no_args() {
585 let func = FunctionSchema {
586 name: "no_args".to_string(),
587 args: vec![],
588 return_type: TypeSchema {
589 ty: "()".to_string(),
590 generic_ty_args: vec![],
591 },
592 body: None,
593 extern_layout: None,
594 annotations: None,
595 };
596
597 assert_eq!(func.args.len(), 0);
598 let display_str = format!("{}", func);
599 assert!(display_str.contains("no_args()"));
600 }
601
602 #[test]
603 fn test_function_arg_schema_no_type() {
604 let arg = FunctionArgSchema {
605 name: "self".to_string(),
606 ty: None,
607 annotations: None,
608 };
609
610 assert_eq!(arg.name, "self");
611 assert!(arg.ty.is_none());
612 }
613
614 #[test]
615 fn test_generic_param_annotations_new() {
616 let annotations = GenericParamAnnotations::new();
617 assert!(annotations.cffi_type.is_none());
618 }
619
620 #[test]
621 fn test_generic_param_annotations_with_cffi_type() {
622 let annotations = GenericParamAnnotations {
623 cffi_type: Some("ptr<void>".to_string()),
624 };
625 assert_eq!(annotations.cffi_type, Some("ptr<void>".to_string()));
626 }
627
628 #[test]
629 fn test_generic_param_schema_creation() {
630 let param = GenericParamSchema {
631 name: "T".to_string(),
632 annotations: Some(GenericParamAnnotations {
633 cffi_type: Some("ptr<i32>".to_string()),
634 }),
635 };
636
637 assert_eq!(param.name, "T");
638 assert!(param.annotations.is_some());
639 if let Some(ann) = param.annotations {
640 assert_eq!(ann.cffi_type, Some("ptr<i32>".to_string()));
641 }
642 }
643
644 #[test]
645 fn test_generic_param_schema_without_annotations() {
646 let param = GenericParamSchema {
647 name: "U".to_string(),
648 annotations: None,
649 };
650
651 assert_eq!(param.name, "U");
652 assert!(param.annotations.is_none());
653 }
654
655 #[test]
656 fn test_trait_schema_with_generics() {
657 let generics = vec![GenericParamSchema {
658 name: "T".to_string(),
659 annotations: Some(GenericParamAnnotations {
660 cffi_type: Some("ptr<void>".to_string()),
661 }),
662 }];
663
664 let schema = TraitSchema {
665 name: "SpecializedTrait".to_string(),
666 functions: vec![],
667 generics,
668 supertraits: vec![],
669 };
670
671 assert_eq!(schema.name, "SpecializedTrait");
672 assert_eq!(schema.generics.len(), 1);
673 assert_eq!(schema.generics[0].name, "T");
674 assert_eq!(schema.supertraits.len(), 0);
675 }
676
677 #[test]
678 fn test_trait_schema_with_supertraits() {
679 let schema = TraitSchema {
680 name: "MyTraitWithBase".to_string(),
681 functions: vec![],
682 generics: vec![],
683 supertraits: vec![
684 TypeSchema {
685 ty: "BaseTrait".to_string(),
686 generic_ty_args: vec![],
687 },
688 TypeSchema {
689 ty: "OtherTrait".to_string(),
690 generic_ty_args: vec![],
691 },
692 ],
693 };
694
695 assert_eq!(schema.name, "MyTraitWithBase");
696 assert_eq!(schema.supertraits.len(), 2);
697 assert_eq!(schema.supertraits[0].ty, "BaseTrait");
698 assert_eq!(schema.supertraits[1].ty, "OtherTrait");
699 }
700}