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)]
88 let mut classes = builder
89 .interfaces
90 .into_iter()
91 .chain(builder.classes)
92 .map(|c| c().into())
93 .collect::<StdVec<_>>();
94
95 #[cfg(feature = "closure")]
96 classes.push(Class::closure());
97
98 Self {
99 name: builder.name.into(),
100 functions: functions
101 .into_iter()
102 .map(Function::from)
103 .collect::<StdVec<_>>()
104 .into(),
105 classes: classes.into(),
106 constants: builder
107 .constants
108 .into_iter()
109 .map(Constant::from)
110 .collect::<StdVec<_>>()
111 .into(),
112 #[cfg(feature = "enum")]
113 enums: builder
114 .enums
115 .into_iter()
116 .map(|e| e().into())
117 .collect::<StdVec<_>>()
118 .into(),
119 }
120 }
121}
122
123#[repr(C)]
125pub struct Function {
126 pub name: RString,
128 pub docs: DocBlock,
130 pub ret: Option<Retval>,
132 pub params: Vec<Parameter>,
134}
135
136impl From<FunctionBuilder<'_>> for Function {
137 fn from(val: FunctionBuilder<'_>) -> Self {
138 let ret_allow_null = val.ret_as_null;
139 Function {
140 name: val.name.into(),
141 docs: DocBlock(
142 val.docs
143 .iter()
144 .map(|d| (*d).into())
145 .collect::<StdVec<_>>()
146 .into(),
147 ),
148 ret: val
149 .retval
150 .map(|r| Retval {
151 ty: r,
152 nullable: r != DataType::Mixed && ret_allow_null,
153 })
154 .into(),
155 params: val
156 .args
157 .into_iter()
158 .map(Parameter::from)
159 .collect::<StdVec<_>>()
160 .into(),
161 }
162 }
163}
164
165#[repr(C)]
167#[derive(Debug, PartialEq)]
168pub struct Parameter {
169 pub name: RString,
171 pub ty: Option<DataType>,
173 pub nullable: bool,
175 pub variadic: bool,
177 pub default: Option<RString>,
179}
180
181#[repr(C)]
183pub struct Class {
184 pub name: RString,
186 pub docs: DocBlock,
188 pub extends: Option<RString>,
190 pub implements: Vec<RString>,
193 pub properties: Vec<Property>,
195 pub methods: Vec<Method>,
197 pub constants: Vec<Constant>,
199 pub flags: u32,
201}
202
203#[cfg(feature = "closure")]
204impl Class {
205 #[must_use]
208 pub fn closure() -> Self {
209 Self {
210 name: "RustClosure".into(),
211 docs: DocBlock(StdVec::new().into()),
212 extends: Option::None,
213 implements: StdVec::new().into(),
214 properties: StdVec::new().into(),
215 methods: vec![Method {
216 name: "__invoke".into(),
217 docs: DocBlock(StdVec::new().into()),
218 ty: MethodType::Member,
219 params: vec![Parameter {
220 name: "args".into(),
221 ty: Option::Some(DataType::Mixed),
222 nullable: false,
223 variadic: true,
224 default: Option::None,
225 }]
226 .into(),
227 retval: Option::Some(Retval {
228 ty: DataType::Mixed,
229 nullable: false,
230 }),
231 r#static: false,
232 visibility: Visibility::Public,
233 r#abstract: false,
234 }]
235 .into(),
236 constants: StdVec::new().into(),
237 flags: 0,
238 }
239 }
240}
241
242impl From<ClassBuilder> for Class {
243 fn from(val: ClassBuilder) -> Self {
244 let flags = val.get_flags();
245 Self {
246 name: val.name.into(),
247 docs: DocBlock(
248 val.docs
249 .iter()
250 .map(|doc| (*doc).into())
251 .collect::<StdVec<_>>()
252 .into(),
253 ),
254 extends: val.extends.map(|(_, stub)| stub.into()).into(),
255 implements: val
256 .interfaces
257 .into_iter()
258 .map(|(_, stub)| stub.into())
259 .collect::<StdVec<_>>()
260 .into(),
261 properties: val
262 .properties
263 .into_iter()
264 .map(Property::from)
265 .collect::<StdVec<_>>()
266 .into(),
267 methods: val
268 .methods
269 .into_iter()
270 .map(Method::from)
271 .collect::<StdVec<_>>()
272 .into(),
273 constants: val
274 .constants
275 .into_iter()
276 .map(|(name, _, docs, stub)| Constant {
277 name: name.into(),
278 value: Option::Some(stub.into()),
279 docs: docs.into(),
280 })
281 .collect::<StdVec<_>>()
282 .into(),
283 flags,
284 }
285 }
286}
287
288#[cfg(feature = "enum")]
289#[repr(C)]
291#[derive(Debug, PartialEq)]
292pub struct Enum {
293 pub name: RString,
295 pub docs: DocBlock,
297 pub cases: Vec<EnumCase>,
299 pub backing_type: Option<RString>,
301}
302
303#[cfg(feature = "enum")]
304impl From<EnumBuilder> for Enum {
305 fn from(val: EnumBuilder) -> Self {
306 Self {
307 name: val.name.into(),
308 docs: DocBlock(
309 val.docs
310 .iter()
311 .map(|d| (*d).into())
312 .collect::<StdVec<_>>()
313 .into(),
314 ),
315 cases: val
316 .cases
317 .into_iter()
318 .map(EnumCase::from)
319 .collect::<StdVec<_>>()
320 .into(),
321 backing_type: match val.datatype {
322 DataType::Long => Some("int".into()),
323 DataType::String => Some("string".into()),
324 _ => None,
325 }
326 .into(),
327 }
328 }
329}
330
331#[cfg(feature = "enum")]
332#[repr(C)]
334#[derive(Debug, PartialEq)]
335pub struct EnumCase {
336 pub name: RString,
338 pub docs: DocBlock,
340 pub value: Option<RString>,
342}
343
344#[cfg(feature = "enum")]
345impl From<&'static crate::enum_::EnumCase> for EnumCase {
346 fn from(val: &'static crate::enum_::EnumCase) -> Self {
347 Self {
348 name: val.name.into(),
349 docs: DocBlock(
350 val.docs
351 .iter()
352 .map(|d| (*d).into())
353 .collect::<StdVec<_>>()
354 .into(),
355 ),
356 value: val
357 .discriminant
358 .as_ref()
359 .map(|v| match v {
360 crate::enum_::Discriminant::Int(i) => i.to_string().into(),
361 crate::enum_::Discriminant::String(s) => format!("'{s}'").into(),
362 })
363 .into(),
364 }
365 }
366}
367
368#[repr(C)]
370#[derive(Debug, PartialEq)]
371pub struct Property {
372 pub name: RString,
374 pub docs: DocBlock,
376 pub ty: Option<DataType>,
378 pub vis: Visibility,
380 pub static_: bool,
382 pub nullable: bool,
384 pub readonly: bool,
386 pub default: Option<RString>,
388}
389
390impl From<crate::builders::ClassProperty> for Property {
391 fn from(val: crate::builders::ClassProperty) -> Self {
392 let static_ = val.flags.contains(PropertyFlags::Static);
393 let vis = Visibility::from(val.flags);
394 let docs = val.docs.into();
395
396 Self {
397 name: val.name.into(),
398 docs,
399 ty: val.ty.into(),
400 vis,
401 static_,
402 nullable: val.nullable,
403 readonly: val.readonly,
404 default: val.default_stub.map(RString::from).into(),
405 }
406 }
407}
408
409#[repr(C)]
411#[derive(Debug, PartialEq)]
412pub struct Method {
413 pub name: RString,
415 pub docs: DocBlock,
417 pub ty: MethodType,
419 pub params: Vec<Parameter>,
421 pub retval: Option<Retval>,
423 pub r#static: bool,
425 pub visibility: Visibility,
427 pub r#abstract: bool,
429}
430
431impl From<(FunctionBuilder<'_>, MethodFlags)> for Method {
432 fn from(val: (FunctionBuilder<'_>, MethodFlags)) -> Self {
433 let (builder, flags) = val;
434 let ret_allow_null = builder.ret_as_null;
435 Method {
436 name: builder.name.into(),
437 docs: DocBlock(
438 builder
439 .docs
440 .iter()
441 .map(|d| (*d).into())
442 .collect::<StdVec<_>>()
443 .into(),
444 ),
445 retval: builder
446 .retval
447 .map(|r| Retval {
448 ty: r,
449 nullable: r != DataType::Mixed && ret_allow_null,
450 })
451 .into(),
452 params: builder
453 .args
454 .into_iter()
455 .map(Into::into)
456 .collect::<StdVec<_>>()
457 .into(),
458 ty: flags.into(),
459 r#static: flags.contains(MethodFlags::Static),
460 visibility: flags.into(),
461 r#abstract: flags.contains(MethodFlags::Abstract),
462 }
463 }
464}
465
466#[repr(C)]
468#[derive(Debug, PartialEq)]
469pub struct Retval {
470 pub ty: DataType,
472 pub nullable: bool,
474}
475
476#[repr(C)]
478#[derive(Clone, Copy, Debug, PartialEq)]
479pub enum MethodType {
480 Member,
482 Static,
484 Constructor,
486}
487
488impl From<MethodFlags> for MethodType {
489 fn from(value: MethodFlags) -> Self {
490 if value.contains(MethodFlags::IsConstructor) {
491 return Self::Constructor;
492 }
493 if value.contains(MethodFlags::Static) {
494 return Self::Static;
495 }
496
497 Self::Member
498 }
499}
500
501#[repr(C)]
504#[derive(Clone, Copy, Debug, PartialEq)]
505pub enum Visibility {
506 Private,
508 Protected,
510 Public,
512}
513
514impl From<PropertyFlags> for Visibility {
515 fn from(value: PropertyFlags) -> Self {
516 if value.contains(PropertyFlags::Protected) {
517 return Self::Protected;
518 }
519 if value.contains(PropertyFlags::Private) {
520 return Self::Private;
521 }
522
523 Self::Public
524 }
525}
526
527impl From<MethodFlags> for Visibility {
528 fn from(value: MethodFlags) -> Self {
529 if value.contains(MethodFlags::Protected) {
530 return Self::Protected;
531 }
532
533 if value.contains(MethodFlags::Private) {
534 return Self::Private;
535 }
536
537 Self::Public
538 }
539}
540
541#[repr(C)]
543pub struct Constant {
544 pub name: RString,
546 pub docs: DocBlock,
548 pub value: Option<RString>,
550}
551
552impl From<(String, DocComments)> for Constant {
553 fn from(val: (String, DocComments)) -> Self {
554 let (name, docs) = val;
555 Constant {
556 name: name.into(),
557 value: Option::None,
558 docs: docs.into(),
559 }
560 }
561}
562
563impl From<(String, Box<dyn IntoConst + Send>, DocComments)> for Constant {
564 fn from(val: (String, Box<dyn IntoConst + Send + 'static>, DocComments)) -> Self {
565 let (name, value, docs) = val;
566 Constant {
567 name: name.into(),
568 value: Option::Some(value.stub_value().into()),
569 docs: docs.into(),
570 }
571 }
572}
573
574#[cfg(test)]
575mod tests {
576 #![cfg_attr(windows, feature(abi_vectorcall))]
577 use cfg_if::cfg_if;
578
579 use super::*;
580
581 use crate::{args::Arg, test::test_function};
582
583 #[test]
584 fn test_new_description() {
585 let module = Module {
586 name: "test".into(),
587 functions: vec![].into(),
588 classes: vec![].into(),
589 constants: vec![].into(),
590 #[cfg(feature = "enum")]
591 enums: vec![].into(),
592 };
593
594 let description = Description::new(module);
595 assert_eq!(description.version, crate::VERSION);
596 assert_eq!(description.module.name, "test".into());
597 }
598
599 #[test]
600 fn test_doc_block_from() {
601 let docs: &'static [&'static str] = &["doc1", "doc2"];
602 let docs: DocBlock = docs.into();
603 assert_eq!(docs.0.len(), 2);
604 assert_eq!(docs.0[0], "doc1".into());
605 assert_eq!(docs.0[1], "doc2".into());
606 }
607
608 #[test]
609 fn test_module_from() {
610 let builder = ModuleBuilder::new("test", "test_version")
611 .function(FunctionBuilder::new("test_function", test_function));
612 let module: Module = builder.into();
613 assert_eq!(module.name, "test".into());
614 assert_eq!(module.functions.len(), 1);
615 cfg_if! {
616 if #[cfg(feature = "closure")] {
617 assert_eq!(module.classes.len(), 1);
618 } else {
619 assert_eq!(module.classes.len(), 0);
620 }
621 }
622 assert_eq!(module.constants.len(), 0);
623 }
624
625 #[test]
626 fn test_function_from() {
627 let builder = FunctionBuilder::new("test_function", test_function)
628 .docs(&["doc1", "doc2"])
629 .arg(Arg::new("foo", DataType::Long))
630 .returns(DataType::Bool, true, true);
631 let function: Function = builder.into();
632 assert_eq!(function.name, "test_function".into());
633 assert_eq!(function.docs.0.len(), 2);
634 assert_eq!(
635 function.params,
636 vec![Parameter {
637 name: "foo".into(),
638 ty: Option::Some(DataType::Long),
639 nullable: false,
640 variadic: false,
641 default: Option::None,
642 }]
643 .into()
644 );
645 assert_eq!(
646 function.ret,
647 Option::Some(Retval {
648 ty: DataType::Bool,
649 nullable: true,
650 })
651 );
652 }
653
654 #[test]
655 fn test_class_from() {
656 let builder = ClassBuilder::new("TestClass")
657 .docs(&["doc1", "doc2"])
658 .extends((|| todo!(), "BaseClass"))
659 .implements((|| todo!(), "Interface1"))
660 .implements((|| todo!(), "Interface2"))
661 .property(crate::builders::ClassProperty {
662 name: "prop1".into(),
663 flags: PropertyFlags::Public,
664 default: None,
665 docs: &["doc1"],
666 ty: None,
667 nullable: false,
668 readonly: false,
669 default_stub: None,
670 })
671 .method(
672 FunctionBuilder::new("test_function", test_function),
673 MethodFlags::Protected,
674 );
675 let class: Class = builder.into();
676
677 assert_eq!(class.name, "TestClass".into());
678 assert_eq!(class.docs.0.len(), 2);
679 assert_eq!(class.extends, Option::Some("BaseClass".into()));
680 assert_eq!(
681 class.implements,
682 vec!["Interface1".into(), "Interface2".into()].into()
683 );
684 assert_eq!(class.properties.len(), 1);
685 assert_eq!(
686 class.properties[0],
687 Property {
688 name: "prop1".into(),
689 docs: DocBlock(vec!["doc1".into()].into()),
690 ty: Option::None,
691 vis: Visibility::Public,
692 static_: false,
693 nullable: false,
694 readonly: false,
695 default: Option::None,
696 }
697 );
698 assert_eq!(class.methods.len(), 1);
699 assert_eq!(
700 class.methods[0],
701 Method {
702 name: "test_function".into(),
703 docs: DocBlock(vec![].into()),
704 ty: MethodType::Member,
705 params: vec![].into(),
706 retval: Option::None,
707 r#static: false,
708 visibility: Visibility::Protected,
709 r#abstract: false
710 }
711 );
712 }
713
714 #[test]
715 fn test_property_from() {
716 let property: Property = crate::builders::ClassProperty {
717 name: "test_property".into(),
718 flags: PropertyFlags::Protected,
719 default: None,
720 docs: &["doc1", "doc2"],
721 ty: Some(DataType::String),
722 nullable: true,
723 readonly: false,
724 default_stub: Some("null".into()),
725 }
726 .into();
727 assert_eq!(property.name, "test_property".into());
728 assert_eq!(property.docs.0.len(), 2);
729 assert_eq!(property.vis, Visibility::Protected);
730 assert!(!property.static_);
731 assert!(property.nullable);
732 assert_eq!(property.default, Option::Some("null".into()));
733 assert_eq!(property.ty, Option::Some(DataType::String));
734 }
735
736 #[test]
737 fn test_method_from() {
738 let builder = FunctionBuilder::new("test_method", test_function)
739 .docs(&["doc1", "doc2"])
740 .arg(Arg::new("foo", DataType::Long))
741 .returns(DataType::Bool, true, true);
742 let method: Method = (builder, MethodFlags::Static | MethodFlags::Protected).into();
743 assert_eq!(method.name, "test_method".into());
744 assert_eq!(method.docs.0.len(), 2);
745 assert_eq!(
746 method.params,
747 vec![Parameter {
748 name: "foo".into(),
749 ty: Option::Some(DataType::Long),
750 nullable: false,
751 variadic: false,
752 default: Option::None,
753 }]
754 .into()
755 );
756 assert_eq!(
757 method.retval,
758 Option::Some(Retval {
759 ty: DataType::Bool,
760 nullable: true,
761 })
762 );
763 assert!(method.r#static);
764 assert_eq!(method.visibility, Visibility::Protected);
765 assert_eq!(method.ty, MethodType::Static);
766 }
767
768 #[test]
769 fn test_ty_from() {
770 let r#static: MethodType = MethodFlags::Static.into();
771 assert_eq!(r#static, MethodType::Static);
772
773 let constructor: MethodType = MethodFlags::IsConstructor.into();
774 assert_eq!(constructor, MethodType::Constructor);
775
776 let member: MethodType = MethodFlags::Public.into();
777 assert_eq!(member, MethodType::Member);
778
779 let mixed: MethodType = (MethodFlags::Protected | MethodFlags::Static).into();
780 assert_eq!(mixed, MethodType::Static);
781
782 let both: MethodType = (MethodFlags::Static | MethodFlags::IsConstructor).into();
783 assert_eq!(both, MethodType::Constructor);
784
785 let empty: MethodType = MethodFlags::empty().into();
786 assert_eq!(empty, MethodType::Member);
787 }
788
789 #[test]
790 fn test_prop_visibility_from() {
791 let private: Visibility = PropertyFlags::Private.into();
792 assert_eq!(private, Visibility::Private);
793
794 let protected: Visibility = PropertyFlags::Protected.into();
795 assert_eq!(protected, Visibility::Protected);
796
797 let public: Visibility = PropertyFlags::Public.into();
798 assert_eq!(public, Visibility::Public);
799
800 let mixed: Visibility = (PropertyFlags::Protected | PropertyFlags::Static).into();
801 assert_eq!(mixed, Visibility::Protected);
802
803 let empty: Visibility = PropertyFlags::empty().into();
804 assert_eq!(empty, Visibility::Public);
805 }
806
807 #[test]
808 fn test_method_visibility_from() {
809 let private: Visibility = MethodFlags::Private.into();
810 assert_eq!(private, Visibility::Private);
811
812 let protected: Visibility = MethodFlags::Protected.into();
813 assert_eq!(protected, Visibility::Protected);
814
815 let public: Visibility = MethodFlags::Public.into();
816 assert_eq!(public, Visibility::Public);
817
818 let mixed: Visibility = (MethodFlags::Protected | MethodFlags::Static).into();
819 assert_eq!(mixed, Visibility::Protected);
820
821 let empty: Visibility = MethodFlags::empty().into();
822 assert_eq!(empty, Visibility::Public);
823 }
824}