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 default: Option<RString>,
386}
387
388impl<D> From<(String, PropertyFlags, D, DocComments)> for Property {
389 fn from(value: (String, PropertyFlags, D, DocComments)) -> Self {
390 let (name, flags, _default, docs) = value;
391 let static_ = flags.contains(PropertyFlags::Static);
392 let vis = Visibility::from(flags);
393 let ty = Option::None;
395 let default = Option::<RString>::None;
397 let nullable = false;
399 let docs = docs.into();
400
401 Self {
402 name: name.into(),
403 docs,
404 ty,
405 vis,
406 static_,
407 nullable,
408 default,
409 }
410 }
411}
412
413#[repr(C)]
415#[derive(Debug, PartialEq)]
416pub struct Method {
417 pub name: RString,
419 pub docs: DocBlock,
421 pub ty: MethodType,
423 pub params: Vec<Parameter>,
425 pub retval: Option<Retval>,
427 pub r#static: bool,
429 pub visibility: Visibility,
431 pub r#abstract: bool,
433}
434
435impl From<(FunctionBuilder<'_>, MethodFlags)> for Method {
436 fn from(val: (FunctionBuilder<'_>, MethodFlags)) -> Self {
437 let (builder, flags) = val;
438 let ret_allow_null = builder.ret_as_null;
439 Method {
440 name: builder.name.into(),
441 docs: DocBlock(
442 builder
443 .docs
444 .iter()
445 .map(|d| (*d).into())
446 .collect::<StdVec<_>>()
447 .into(),
448 ),
449 retval: builder
450 .retval
451 .map(|r| Retval {
452 ty: r,
453 nullable: r != DataType::Mixed && ret_allow_null,
454 })
455 .into(),
456 params: builder
457 .args
458 .into_iter()
459 .map(Into::into)
460 .collect::<StdVec<_>>()
461 .into(),
462 ty: flags.into(),
463 r#static: flags.contains(MethodFlags::Static),
464 visibility: flags.into(),
465 r#abstract: flags.contains(MethodFlags::Abstract),
466 }
467 }
468}
469
470#[repr(C)]
472#[derive(Debug, PartialEq)]
473pub struct Retval {
474 pub ty: DataType,
476 pub nullable: bool,
478}
479
480#[repr(C)]
482#[derive(Clone, Copy, Debug, PartialEq)]
483pub enum MethodType {
484 Member,
486 Static,
488 Constructor,
490}
491
492impl From<MethodFlags> for MethodType {
493 fn from(value: MethodFlags) -> Self {
494 if value.contains(MethodFlags::IsConstructor) {
495 return Self::Constructor;
496 }
497 if value.contains(MethodFlags::Static) {
498 return Self::Static;
499 }
500
501 Self::Member
502 }
503}
504
505#[repr(C)]
508#[derive(Clone, Copy, Debug, PartialEq)]
509pub enum Visibility {
510 Private,
512 Protected,
514 Public,
516}
517
518impl From<PropertyFlags> for Visibility {
519 fn from(value: PropertyFlags) -> Self {
520 if value.contains(PropertyFlags::Protected) {
521 return Self::Protected;
522 }
523 if value.contains(PropertyFlags::Private) {
524 return Self::Private;
525 }
526
527 Self::Public
528 }
529}
530
531impl From<MethodFlags> for Visibility {
532 fn from(value: MethodFlags) -> Self {
533 if value.contains(MethodFlags::Protected) {
534 return Self::Protected;
535 }
536
537 if value.contains(MethodFlags::Private) {
538 return Self::Private;
539 }
540
541 Self::Public
542 }
543}
544
545#[repr(C)]
547pub struct Constant {
548 pub name: RString,
550 pub docs: DocBlock,
552 pub value: Option<RString>,
554}
555
556impl From<(String, DocComments)> for Constant {
557 fn from(val: (String, DocComments)) -> Self {
558 let (name, docs) = val;
559 Constant {
560 name: name.into(),
561 value: Option::None,
562 docs: docs.into(),
563 }
564 }
565}
566
567impl From<(String, Box<dyn IntoConst + Send>, DocComments)> for Constant {
568 fn from(val: (String, Box<dyn IntoConst + Send + 'static>, DocComments)) -> Self {
569 let (name, value, docs) = val;
570 Constant {
571 name: name.into(),
572 value: Option::Some(value.stub_value().into()),
573 docs: docs.into(),
574 }
575 }
576}
577
578#[cfg(test)]
579mod tests {
580 #![cfg_attr(windows, feature(abi_vectorcall))]
581 use cfg_if::cfg_if;
582
583 use super::*;
584
585 use crate::{args::Arg, test::test_function};
586
587 #[test]
588 fn test_new_description() {
589 let module = Module {
590 name: "test".into(),
591 functions: vec![].into(),
592 classes: vec![].into(),
593 constants: vec![].into(),
594 #[cfg(feature = "enum")]
595 enums: vec![].into(),
596 };
597
598 let description = Description::new(module);
599 assert_eq!(description.version, crate::VERSION);
600 assert_eq!(description.module.name, "test".into());
601 }
602
603 #[test]
604 fn test_doc_block_from() {
605 let docs: &'static [&'static str] = &["doc1", "doc2"];
606 let docs: DocBlock = docs.into();
607 assert_eq!(docs.0.len(), 2);
608 assert_eq!(docs.0[0], "doc1".into());
609 assert_eq!(docs.0[1], "doc2".into());
610 }
611
612 #[test]
613 fn test_module_from() {
614 let builder = ModuleBuilder::new("test", "test_version")
615 .function(FunctionBuilder::new("test_function", test_function));
616 let module: Module = builder.into();
617 assert_eq!(module.name, "test".into());
618 assert_eq!(module.functions.len(), 1);
619 cfg_if! {
620 if #[cfg(feature = "closure")] {
621 assert_eq!(module.classes.len(), 1);
622 } else {
623 assert_eq!(module.classes.len(), 0);
624 }
625 }
626 assert_eq!(module.constants.len(), 0);
627 }
628
629 #[test]
630 fn test_function_from() {
631 let builder = FunctionBuilder::new("test_function", test_function)
632 .docs(&["doc1", "doc2"])
633 .arg(Arg::new("foo", DataType::Long))
634 .returns(DataType::Bool, true, true);
635 let function: Function = builder.into();
636 assert_eq!(function.name, "test_function".into());
637 assert_eq!(function.docs.0.len(), 2);
638 assert_eq!(
639 function.params,
640 vec![Parameter {
641 name: "foo".into(),
642 ty: Option::Some(DataType::Long),
643 nullable: false,
644 variadic: false,
645 default: Option::None,
646 }]
647 .into()
648 );
649 assert_eq!(
650 function.ret,
651 Option::Some(Retval {
652 ty: DataType::Bool,
653 nullable: true,
654 })
655 );
656 }
657
658 #[test]
659 fn test_class_from() {
660 let builder = ClassBuilder::new("TestClass")
661 .docs(&["doc1", "doc2"])
662 .extends((|| todo!(), "BaseClass"))
663 .implements((|| todo!(), "Interface1"))
664 .implements((|| todo!(), "Interface2"))
665 .property("prop1", PropertyFlags::Public, None, &["doc1"])
666 .method(
667 FunctionBuilder::new("test_function", test_function),
668 MethodFlags::Protected,
669 );
670 let class: Class = builder.into();
671
672 assert_eq!(class.name, "TestClass".into());
673 assert_eq!(class.docs.0.len(), 2);
674 assert_eq!(class.extends, Option::Some("BaseClass".into()));
675 assert_eq!(
676 class.implements,
677 vec!["Interface1".into(), "Interface2".into()].into()
678 );
679 assert_eq!(class.properties.len(), 1);
680 assert_eq!(
681 class.properties[0],
682 Property {
683 name: "prop1".into(),
684 docs: DocBlock(vec!["doc1".into()].into()),
685 ty: Option::None,
686 vis: Visibility::Public,
687 static_: false,
688 nullable: false,
689 default: Option::None,
690 }
691 );
692 assert_eq!(class.methods.len(), 1);
693 assert_eq!(
694 class.methods[0],
695 Method {
696 name: "test_function".into(),
697 docs: DocBlock(vec![].into()),
698 ty: MethodType::Member,
699 params: vec![].into(),
700 retval: Option::None,
701 r#static: false,
702 visibility: Visibility::Protected,
703 r#abstract: false
704 }
705 );
706 }
707
708 #[test]
709 fn test_property_from() {
710 let docs: &'static [&'static str] = &["doc1", "doc2"];
711 let property: Property = (
712 "test_property".to_string(),
713 PropertyFlags::Protected,
714 (),
715 docs,
716 )
717 .into();
718 assert_eq!(property.name, "test_property".into());
719 assert_eq!(property.docs.0.len(), 2);
720 assert_eq!(property.vis, Visibility::Protected);
721 assert!(!property.static_);
722 assert!(!property.nullable);
723 }
724
725 #[test]
726 fn test_method_from() {
727 let builder = FunctionBuilder::new("test_method", test_function)
728 .docs(&["doc1", "doc2"])
729 .arg(Arg::new("foo", DataType::Long))
730 .returns(DataType::Bool, true, true);
731 let method: Method = (builder, MethodFlags::Static | MethodFlags::Protected).into();
732 assert_eq!(method.name, "test_method".into());
733 assert_eq!(method.docs.0.len(), 2);
734 assert_eq!(
735 method.params,
736 vec![Parameter {
737 name: "foo".into(),
738 ty: Option::Some(DataType::Long),
739 nullable: false,
740 variadic: false,
741 default: Option::None,
742 }]
743 .into()
744 );
745 assert_eq!(
746 method.retval,
747 Option::Some(Retval {
748 ty: DataType::Bool,
749 nullable: true,
750 })
751 );
752 assert!(method.r#static);
753 assert_eq!(method.visibility, Visibility::Protected);
754 assert_eq!(method.ty, MethodType::Static);
755 }
756
757 #[test]
758 fn test_ty_from() {
759 let r#static: MethodType = MethodFlags::Static.into();
760 assert_eq!(r#static, MethodType::Static);
761
762 let constructor: MethodType = MethodFlags::IsConstructor.into();
763 assert_eq!(constructor, MethodType::Constructor);
764
765 let member: MethodType = MethodFlags::Public.into();
766 assert_eq!(member, MethodType::Member);
767
768 let mixed: MethodType = (MethodFlags::Protected | MethodFlags::Static).into();
769 assert_eq!(mixed, MethodType::Static);
770
771 let both: MethodType = (MethodFlags::Static | MethodFlags::IsConstructor).into();
772 assert_eq!(both, MethodType::Constructor);
773
774 let empty: MethodType = MethodFlags::empty().into();
775 assert_eq!(empty, MethodType::Member);
776 }
777
778 #[test]
779 fn test_prop_visibility_from() {
780 let private: Visibility = PropertyFlags::Private.into();
781 assert_eq!(private, Visibility::Private);
782
783 let protected: Visibility = PropertyFlags::Protected.into();
784 assert_eq!(protected, Visibility::Protected);
785
786 let public: Visibility = PropertyFlags::Public.into();
787 assert_eq!(public, Visibility::Public);
788
789 let mixed: Visibility = (PropertyFlags::Protected | PropertyFlags::Static).into();
790 assert_eq!(mixed, Visibility::Protected);
791
792 let empty: Visibility = PropertyFlags::empty().into();
793 assert_eq!(empty, Visibility::Public);
794 }
795
796 #[test]
797 fn test_method_visibility_from() {
798 let private: Visibility = MethodFlags::Private.into();
799 assert_eq!(private, Visibility::Private);
800
801 let protected: Visibility = MethodFlags::Protected.into();
802 assert_eq!(protected, Visibility::Protected);
803
804 let public: Visibility = MethodFlags::Public.into();
805 assert_eq!(public, Visibility::Public);
806
807 let mixed: Visibility = (MethodFlags::Protected | MethodFlags::Static).into();
808 assert_eq!(mixed, Visibility::Protected);
809
810 let empty: Visibility = MethodFlags::empty().into();
811 assert_eq!(empty, Visibility::Public);
812 }
813}