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