1use std::vec::Vec as StdVec;
4
5#[cfg(feature = "enum")]
6use crate::builders::EnumBuilder;
7use crate::{
8 builders::{ClassBuilder, FunctionBuilder},
9 constant::IntoConst,
10 flags::{DataType, MethodFlags, PropertyFlags},
11 prelude::ModuleBuilder,
12};
13use abi::{Option, RString, Str, Vec};
14
15pub mod abi;
16mod stub;
17
18pub use stub::ToStub;
19
20pub type DocComments = &'static [&'static str];
22
23#[repr(C)]
25pub struct Description {
26 pub module: Module,
28 pub version: &'static str,
30}
31
32impl Description {
33 #[must_use]
39 pub fn new(module: Module) -> Self {
40 Self {
41 module,
42 version: crate::VERSION,
43 }
44 }
45}
46
47#[repr(C)]
49#[derive(Debug, PartialEq)]
50pub struct DocBlock(pub Vec<Str>);
51
52impl From<&'static [&'static str]> for DocBlock {
53 fn from(val: &'static [&'static str]) -> Self {
54 Self(
55 val.iter()
56 .map(|s| (*s).into())
57 .collect::<StdVec<_>>()
58 .into(),
59 )
60 }
61}
62
63#[repr(C)]
65pub struct Module {
66 pub name: RString,
68 pub functions: Vec<Function>,
70 pub classes: Vec<Class>,
72 #[cfg(feature = "enum")]
73 pub enums: Vec<Enum>,
75 pub constants: Vec<Constant>,
77}
78
79impl From<ModuleBuilder<'_>> for Module {
82 fn from(builder: ModuleBuilder) -> Self {
83 let functions = builder.functions;
84
85 #[allow(unused_mut)]
86 let mut classes = builder
87 .classes
88 .into_iter()
89 .map(|c| c().into())
90 .collect::<StdVec<_>>();
91
92 #[cfg(feature = "closure")]
93 classes.push(Class::closure());
94
95 Self {
96 name: builder.name.into(),
97 functions: functions
98 .into_iter()
99 .map(Function::from)
100 .collect::<StdVec<_>>()
101 .into(),
102 classes: classes.into(),
103 constants: builder
104 .constants
105 .into_iter()
106 .map(Constant::from)
107 .collect::<StdVec<_>>()
108 .into(),
109 #[cfg(feature = "enum")]
110 enums: builder
111 .enums
112 .into_iter()
113 .map(|e| e().into())
114 .collect::<StdVec<_>>()
115 .into(),
116 }
117 }
118}
119
120#[repr(C)]
122pub struct Function {
123 pub name: RString,
125 pub docs: DocBlock,
127 pub ret: Option<Retval>,
129 pub params: Vec<Parameter>,
131}
132
133impl From<FunctionBuilder<'_>> for Function {
134 fn from(val: FunctionBuilder<'_>) -> Self {
135 let ret_allow_null = val.ret_as_null;
136 Function {
137 name: val.name.into(),
138 docs: DocBlock(
139 val.docs
140 .iter()
141 .map(|d| (*d).into())
142 .collect::<StdVec<_>>()
143 .into(),
144 ),
145 ret: val
146 .retval
147 .map(|r| Retval {
148 ty: r,
149 nullable: r != DataType::Mixed && ret_allow_null,
150 })
151 .into(),
152 params: val
153 .args
154 .into_iter()
155 .map(Parameter::from)
156 .collect::<StdVec<_>>()
157 .into(),
158 }
159 }
160}
161
162#[repr(C)]
164#[derive(Debug, PartialEq)]
165pub struct Parameter {
166 pub name: RString,
168 pub ty: Option<DataType>,
170 pub nullable: bool,
172 pub variadic: bool,
174 pub default: Option<RString>,
176}
177
178#[repr(C)]
180pub struct Class {
181 pub name: RString,
183 pub docs: DocBlock,
185 pub extends: Option<RString>,
187 pub implements: Vec<RString>,
190 pub properties: Vec<Property>,
192 pub methods: Vec<Method>,
194 pub constants: Vec<Constant>,
196}
197
198#[cfg(feature = "closure")]
199impl Class {
200 #[must_use]
203 pub fn closure() -> Self {
204 Self {
205 name: "RustClosure".into(),
206 docs: DocBlock(StdVec::new().into()),
207 extends: Option::None,
208 implements: StdVec::new().into(),
209 properties: StdVec::new().into(),
210 methods: vec![Method {
211 name: "__invoke".into(),
212 docs: DocBlock(StdVec::new().into()),
213 ty: MethodType::Member,
214 params: vec![Parameter {
215 name: "args".into(),
216 ty: Option::Some(DataType::Mixed),
217 nullable: false,
218 variadic: true,
219 default: Option::None,
220 }]
221 .into(),
222 retval: Option::Some(Retval {
223 ty: DataType::Mixed,
224 nullable: false,
225 }),
226 r#static: false,
227 visibility: Visibility::Public,
228 }]
229 .into(),
230 constants: StdVec::new().into(),
231 }
232 }
233}
234
235impl From<ClassBuilder> for Class {
236 fn from(val: ClassBuilder) -> Self {
237 Self {
238 name: val.name.into(),
239 docs: DocBlock(
240 val.docs
241 .iter()
242 .map(|doc| (*doc).into())
243 .collect::<StdVec<_>>()
244 .into(),
245 ),
246 extends: val.extends.map(|(_, stub)| stub.into()).into(),
247 implements: val
248 .interfaces
249 .into_iter()
250 .map(|(_, stub)| stub.into())
251 .collect::<StdVec<_>>()
252 .into(),
253 properties: val
254 .properties
255 .into_iter()
256 .map(Property::from)
257 .collect::<StdVec<_>>()
258 .into(),
259 methods: val
260 .methods
261 .into_iter()
262 .map(Method::from)
263 .collect::<StdVec<_>>()
264 .into(),
265 constants: val
266 .constants
267 .into_iter()
268 .map(|(name, _, docs)| (name, docs))
269 .map(Constant::from)
270 .collect::<StdVec<_>>()
271 .into(),
272 }
273 }
274}
275
276#[cfg(feature = "enum")]
277#[repr(C)]
279#[derive(Debug, PartialEq)]
280pub struct Enum {
281 pub name: RString,
283 pub docs: DocBlock,
285 pub cases: Vec<EnumCase>,
287 pub backing_type: Option<RString>,
289}
290
291#[cfg(feature = "enum")]
292impl From<EnumBuilder> for Enum {
293 fn from(val: EnumBuilder) -> Self {
294 Self {
295 name: val.name.into(),
296 docs: DocBlock(
297 val.docs
298 .iter()
299 .map(|d| (*d).into())
300 .collect::<StdVec<_>>()
301 .into(),
302 ),
303 cases: val
304 .cases
305 .into_iter()
306 .map(EnumCase::from)
307 .collect::<StdVec<_>>()
308 .into(),
309 backing_type: match val.datatype {
310 DataType::Long => Some("int".into()),
311 DataType::String => Some("string".into()),
312 _ => None,
313 }
314 .into(),
315 }
316 }
317}
318
319#[cfg(feature = "enum")]
320#[repr(C)]
322#[derive(Debug, PartialEq)]
323pub struct EnumCase {
324 pub name: RString,
326 pub docs: DocBlock,
328 pub value: Option<RString>,
330}
331
332#[cfg(feature = "enum")]
333impl From<&'static crate::enum_::EnumCase> for EnumCase {
334 fn from(val: &'static crate::enum_::EnumCase) -> Self {
335 Self {
336 name: val.name.into(),
337 docs: DocBlock(
338 val.docs
339 .iter()
340 .map(|d| (*d).into())
341 .collect::<StdVec<_>>()
342 .into(),
343 ),
344 value: val
345 .discriminant
346 .as_ref()
347 .map(|v| match v {
348 crate::enum_::Discriminant::Int(i) => i.to_string().into(),
349 crate::enum_::Discriminant::String(s) => format!("'{s}'").into(),
350 })
351 .into(),
352 }
353 }
354}
355
356#[repr(C)]
358#[derive(Debug, PartialEq)]
359pub struct Property {
360 pub name: RString,
362 pub docs: DocBlock,
364 pub ty: Option<DataType>,
366 pub vis: Visibility,
368 pub static_: bool,
370 pub nullable: bool,
372 pub default: Option<RString>,
374}
375
376impl From<(String, PropertyFlags, DocComments)> for Property {
377 fn from(value: (String, PropertyFlags, DocComments)) -> Self {
378 let (name, flags, docs) = value;
379 let static_ = flags.contains(PropertyFlags::Static);
380 let vis = Visibility::from(flags);
381 let ty = abi::Option::None;
383 let default = abi::Option::<abi::RString>::None;
385 let nullable = false;
387 let docs = docs.into();
388
389 Self {
390 name: name.into(),
391 docs,
392 ty,
393 vis,
394 static_,
395 nullable,
396 default,
397 }
398 }
399}
400
401#[repr(C)]
403#[derive(Debug, PartialEq)]
404pub struct Method {
405 pub name: RString,
407 pub docs: DocBlock,
409 pub ty: MethodType,
411 pub params: Vec<Parameter>,
413 pub retval: Option<Retval>,
415 pub r#static: bool,
417 pub visibility: Visibility,
419}
420
421impl From<(FunctionBuilder<'_>, MethodFlags)> for Method {
422 fn from(val: (FunctionBuilder<'_>, MethodFlags)) -> Self {
423 let (builder, flags) = val;
424 let ret_allow_null = builder.ret_as_null;
425 Method {
426 name: builder.name.into(),
427 docs: DocBlock(
428 builder
429 .docs
430 .iter()
431 .map(|d| (*d).into())
432 .collect::<StdVec<_>>()
433 .into(),
434 ),
435 retval: builder
436 .retval
437 .map(|r| Retval {
438 ty: r,
439 nullable: r != DataType::Mixed && ret_allow_null,
440 })
441 .into(),
442 params: builder
443 .args
444 .into_iter()
445 .map(Into::into)
446 .collect::<StdVec<_>>()
447 .into(),
448 ty: flags.into(),
449 r#static: flags.contains(MethodFlags::Static),
450 visibility: flags.into(),
451 }
452 }
453}
454
455#[repr(C)]
457#[derive(Debug, PartialEq)]
458pub struct Retval {
459 pub ty: DataType,
461 pub nullable: bool,
463}
464
465#[repr(C)]
467#[derive(Clone, Copy, Debug, PartialEq)]
468pub enum MethodType {
469 Member,
471 Static,
473 Constructor,
475}
476
477impl From<MethodFlags> for MethodType {
478 fn from(value: MethodFlags) -> Self {
479 if value.contains(MethodFlags::IsConstructor) {
480 return Self::Constructor;
481 }
482 if value.contains(MethodFlags::Static) {
483 return Self::Static;
484 }
485
486 Self::Member
487 }
488}
489
490#[repr(C)]
493#[derive(Clone, Copy, Debug, PartialEq)]
494pub enum Visibility {
495 Private,
497 Protected,
499 Public,
501}
502
503impl From<PropertyFlags> for Visibility {
504 fn from(value: PropertyFlags) -> Self {
505 if value.contains(PropertyFlags::Protected) {
506 return Self::Protected;
507 }
508 if value.contains(PropertyFlags::Private) {
509 return Self::Private;
510 }
511
512 Self::Public
513 }
514}
515
516impl From<MethodFlags> for Visibility {
517 fn from(value: MethodFlags) -> Self {
518 if value.contains(MethodFlags::Protected) {
519 return Self::Protected;
520 }
521
522 if value.contains(MethodFlags::Private) {
523 return Self::Private;
524 }
525
526 Self::Public
527 }
528}
529
530#[repr(C)]
532pub struct Constant {
533 pub name: RString,
535 pub docs: DocBlock,
537 pub value: Option<RString>,
539}
540
541impl From<(String, DocComments)> for Constant {
542 fn from(val: (String, DocComments)) -> Self {
543 let (name, docs) = val;
544 Constant {
545 name: name.into(),
546 value: abi::Option::None,
547 docs: docs.into(),
548 }
549 }
550}
551
552impl From<(String, Box<dyn IntoConst + Send>, DocComments)> for Constant {
553 fn from(val: (String, Box<dyn IntoConst + Send + 'static>, DocComments)) -> Self {
554 let (name, _, docs) = val;
555 Constant {
556 name: name.into(),
557 value: abi::Option::None,
558 docs: docs.into(),
559 }
560 }
561}
562
563#[cfg(test)]
564mod tests {
565 #![cfg_attr(windows, feature(abi_vectorcall))]
566 use cfg_if::cfg_if;
567
568 use super::*;
569
570 use crate::{args::Arg, test::test_function};
571
572 #[test]
573 fn test_new_description() {
574 let module = Module {
575 name: "test".into(),
576 functions: vec![].into(),
577 classes: vec![].into(),
578 constants: vec![].into(),
579 #[cfg(feature = "enum")]
580 enums: vec![].into(),
581 };
582
583 let description = Description::new(module);
584 assert_eq!(description.version, crate::VERSION);
585 assert_eq!(description.module.name, "test".into());
586 }
587
588 #[test]
589 fn test_doc_block_from() {
590 let docs: &'static [&'static str] = &["doc1", "doc2"];
591 let docs: DocBlock = docs.into();
592 assert_eq!(docs.0.len(), 2);
593 assert_eq!(docs.0[0], "doc1".into());
594 assert_eq!(docs.0[1], "doc2".into());
595 }
596
597 #[test]
598 fn test_module_from() {
599 let builder = ModuleBuilder::new("test", "test_version")
600 .function(FunctionBuilder::new("test_function", test_function));
601 let module: Module = builder.into();
602 assert_eq!(module.name, "test".into());
603 assert_eq!(module.functions.len(), 1);
604 cfg_if! {
605 if #[cfg(feature = "closure")] {
606 assert_eq!(module.classes.len(), 1);
607 } else {
608 assert_eq!(module.classes.len(), 0);
609 }
610 }
611 assert_eq!(module.constants.len(), 0);
612 }
613
614 #[test]
615 fn test_function_from() {
616 let builder = FunctionBuilder::new("test_function", test_function)
617 .docs(&["doc1", "doc2"])
618 .arg(Arg::new("foo", DataType::Long))
619 .returns(DataType::Bool, true, true);
620 let function: Function = builder.into();
621 assert_eq!(function.name, "test_function".into());
622 assert_eq!(function.docs.0.len(), 2);
623 assert_eq!(
624 function.params,
625 vec![Parameter {
626 name: "foo".into(),
627 ty: Option::Some(DataType::Long),
628 nullable: false,
629 variadic: false,
630 default: Option::None,
631 }]
632 .into()
633 );
634 assert_eq!(
635 function.ret,
636 Option::Some(Retval {
637 ty: DataType::Bool,
638 nullable: true,
639 })
640 );
641 }
642
643 #[test]
644 fn test_class_from() {
645 let builder = ClassBuilder::new("TestClass")
646 .docs(&["doc1", "doc2"])
647 .extends((|| todo!(), "BaseClass"))
648 .implements((|| todo!(), "Interface1"))
649 .implements((|| todo!(), "Interface2"))
650 .property("prop1", PropertyFlags::Public, &["doc1"])
651 .method(
652 FunctionBuilder::new("test_function", test_function),
653 MethodFlags::Protected,
654 );
655 let class: Class = builder.into();
656
657 assert_eq!(class.name, "TestClass".into());
658 assert_eq!(class.docs.0.len(), 2);
659 assert_eq!(class.extends, Option::Some("BaseClass".into()));
660 assert_eq!(
661 class.implements,
662 vec!["Interface1".into(), "Interface2".into()].into()
663 );
664 assert_eq!(class.properties.len(), 1);
665 assert_eq!(
666 class.properties[0],
667 Property {
668 name: "prop1".into(),
669 docs: DocBlock(vec!["doc1".into()].into()),
670 ty: Option::None,
671 vis: Visibility::Public,
672 static_: false,
673 nullable: false,
674 default: Option::None,
675 }
676 );
677 assert_eq!(class.methods.len(), 1);
678 assert_eq!(
679 class.methods[0],
680 Method {
681 name: "test_function".into(),
682 docs: DocBlock(vec![].into()),
683 ty: MethodType::Member,
684 params: vec![].into(),
685 retval: Option::None,
686 r#static: false,
687 visibility: Visibility::Protected,
688 }
689 );
690 }
691
692 #[test]
693 fn test_property_from() {
694 let docs: &'static [&'static str] = &["doc1", "doc2"];
695 let property: Property =
696 ("test_property".to_string(), PropertyFlags::Protected, docs).into();
697 assert_eq!(property.name, "test_property".into());
698 assert_eq!(property.docs.0.len(), 2);
699 assert_eq!(property.vis, Visibility::Protected);
700 assert!(!property.static_);
701 assert!(!property.nullable);
702 }
703
704 #[test]
705 fn test_method_from() {
706 let builder = FunctionBuilder::new("test_method", test_function)
707 .docs(&["doc1", "doc2"])
708 .arg(Arg::new("foo", DataType::Long))
709 .returns(DataType::Bool, true, true);
710 let method: Method = (builder, MethodFlags::Static | MethodFlags::Protected).into();
711 assert_eq!(method.name, "test_method".into());
712 assert_eq!(method.docs.0.len(), 2);
713 assert_eq!(
714 method.params,
715 vec![Parameter {
716 name: "foo".into(),
717 ty: Option::Some(DataType::Long),
718 nullable: false,
719 variadic: false,
720 default: Option::None,
721 }]
722 .into()
723 );
724 assert_eq!(
725 method.retval,
726 Option::Some(Retval {
727 ty: DataType::Bool,
728 nullable: true,
729 })
730 );
731 assert!(method.r#static);
732 assert_eq!(method.visibility, Visibility::Protected);
733 assert_eq!(method.ty, MethodType::Static);
734 }
735
736 #[test]
737 fn test_ty_from() {
738 let r#static: MethodType = MethodFlags::Static.into();
739 assert_eq!(r#static, MethodType::Static);
740
741 let constructor: MethodType = MethodFlags::IsConstructor.into();
742 assert_eq!(constructor, MethodType::Constructor);
743
744 let member: MethodType = MethodFlags::Public.into();
745 assert_eq!(member, MethodType::Member);
746
747 let mixed: MethodType = (MethodFlags::Protected | MethodFlags::Static).into();
748 assert_eq!(mixed, MethodType::Static);
749
750 let both: MethodType = (MethodFlags::Static | MethodFlags::IsConstructor).into();
751 assert_eq!(both, MethodType::Constructor);
752
753 let empty: MethodType = MethodFlags::empty().into();
754 assert_eq!(empty, MethodType::Member);
755 }
756
757 #[test]
758 fn test_prop_visibility_from() {
759 let private: Visibility = PropertyFlags::Private.into();
760 assert_eq!(private, Visibility::Private);
761
762 let protected: Visibility = PropertyFlags::Protected.into();
763 assert_eq!(protected, Visibility::Protected);
764
765 let public: Visibility = PropertyFlags::Public.into();
766 assert_eq!(public, Visibility::Public);
767
768 let mixed: Visibility = (PropertyFlags::Protected | PropertyFlags::Static).into();
769 assert_eq!(mixed, Visibility::Protected);
770
771 let empty: Visibility = PropertyFlags::empty().into();
772 assert_eq!(empty, Visibility::Public);
773 }
774
775 #[test]
776 fn test_method_visibility_from() {
777 let private: Visibility = MethodFlags::Private.into();
778 assert_eq!(private, Visibility::Private);
779
780 let protected: Visibility = MethodFlags::Protected.into();
781 assert_eq!(protected, Visibility::Protected);
782
783 let public: Visibility = MethodFlags::Public.into();
784 assert_eq!(public, Visibility::Public);
785
786 let mixed: Visibility = (MethodFlags::Protected | MethodFlags::Static).into();
787 assert_eq!(mixed, Visibility::Protected);
788
789 let empty: Visibility = MethodFlags::empty().into();
790 assert_eq!(empty, Visibility::Public);
791 }
792}