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, stub)| Constant {
274 name: name.into(),
275 value: Option::Some(stub.into()),
276 docs: docs.into(),
277 })
278 .collect::<StdVec<_>>()
279 .into(),
280 flags,
281 }
282 }
283}
284
285#[cfg(feature = "enum")]
286#[repr(C)]
288#[derive(Debug, PartialEq)]
289pub struct Enum {
290 pub name: RString,
292 pub docs: DocBlock,
294 pub cases: Vec<EnumCase>,
296 pub backing_type: Option<RString>,
298}
299
300#[cfg(feature = "enum")]
301impl From<EnumBuilder> for Enum {
302 fn from(val: EnumBuilder) -> Self {
303 Self {
304 name: val.name.into(),
305 docs: DocBlock(
306 val.docs
307 .iter()
308 .map(|d| (*d).into())
309 .collect::<StdVec<_>>()
310 .into(),
311 ),
312 cases: val
313 .cases
314 .into_iter()
315 .map(EnumCase::from)
316 .collect::<StdVec<_>>()
317 .into(),
318 backing_type: match val.datatype {
319 DataType::Long => Some("int".into()),
320 DataType::String => Some("string".into()),
321 _ => None,
322 }
323 .into(),
324 }
325 }
326}
327
328#[cfg(feature = "enum")]
329#[repr(C)]
331#[derive(Debug, PartialEq)]
332pub struct EnumCase {
333 pub name: RString,
335 pub docs: DocBlock,
337 pub value: Option<RString>,
339}
340
341#[cfg(feature = "enum")]
342impl From<&'static crate::enum_::EnumCase> for EnumCase {
343 fn from(val: &'static crate::enum_::EnumCase) -> Self {
344 Self {
345 name: val.name.into(),
346 docs: DocBlock(
347 val.docs
348 .iter()
349 .map(|d| (*d).into())
350 .collect::<StdVec<_>>()
351 .into(),
352 ),
353 value: val
354 .discriminant
355 .as_ref()
356 .map(|v| match v {
357 crate::enum_::Discriminant::Int(i) => i.to_string().into(),
358 crate::enum_::Discriminant::String(s) => format!("'{s}'").into(),
359 })
360 .into(),
361 }
362 }
363}
364
365#[repr(C)]
367#[derive(Debug, PartialEq)]
368pub struct Property {
369 pub name: RString,
371 pub docs: DocBlock,
373 pub ty: Option<DataType>,
375 pub vis: Visibility,
377 pub static_: bool,
379 pub nullable: bool,
381 pub default: Option<RString>,
383}
384
385impl<D> From<(String, PropertyFlags, D, DocComments)> for Property {
386 fn from(value: (String, PropertyFlags, D, DocComments)) -> Self {
387 let (name, flags, _default, docs) = value;
388 let static_ = flags.contains(PropertyFlags::Static);
389 let vis = Visibility::from(flags);
390 let ty = Option::None;
392 let default = Option::<RString>::None;
394 let nullable = false;
396 let docs = docs.into();
397
398 Self {
399 name: name.into(),
400 docs,
401 ty,
402 vis,
403 static_,
404 nullable,
405 default,
406 }
407 }
408}
409
410#[repr(C)]
412#[derive(Debug, PartialEq)]
413pub struct Method {
414 pub name: RString,
416 pub docs: DocBlock,
418 pub ty: MethodType,
420 pub params: Vec<Parameter>,
422 pub retval: Option<Retval>,
424 pub r#static: bool,
426 pub visibility: Visibility,
428 pub r#abstract: bool,
430}
431
432impl From<(FunctionBuilder<'_>, MethodFlags)> for Method {
433 fn from(val: (FunctionBuilder<'_>, MethodFlags)) -> Self {
434 let (builder, flags) = val;
435 let ret_allow_null = builder.ret_as_null;
436 Method {
437 name: builder.name.into(),
438 docs: DocBlock(
439 builder
440 .docs
441 .iter()
442 .map(|d| (*d).into())
443 .collect::<StdVec<_>>()
444 .into(),
445 ),
446 retval: builder
447 .retval
448 .map(|r| Retval {
449 ty: r,
450 nullable: r != DataType::Mixed && ret_allow_null,
451 })
452 .into(),
453 params: builder
454 .args
455 .into_iter()
456 .map(Into::into)
457 .collect::<StdVec<_>>()
458 .into(),
459 ty: flags.into(),
460 r#static: flags.contains(MethodFlags::Static),
461 visibility: flags.into(),
462 r#abstract: flags.contains(MethodFlags::Abstract),
463 }
464 }
465}
466
467#[repr(C)]
469#[derive(Debug, PartialEq)]
470pub struct Retval {
471 pub ty: DataType,
473 pub nullable: bool,
475}
476
477#[repr(C)]
479#[derive(Clone, Copy, Debug, PartialEq)]
480pub enum MethodType {
481 Member,
483 Static,
485 Constructor,
487}
488
489impl From<MethodFlags> for MethodType {
490 fn from(value: MethodFlags) -> Self {
491 if value.contains(MethodFlags::IsConstructor) {
492 return Self::Constructor;
493 }
494 if value.contains(MethodFlags::Static) {
495 return Self::Static;
496 }
497
498 Self::Member
499 }
500}
501
502#[repr(C)]
505#[derive(Clone, Copy, Debug, PartialEq)]
506pub enum Visibility {
507 Private,
509 Protected,
511 Public,
513}
514
515impl From<PropertyFlags> for Visibility {
516 fn from(value: PropertyFlags) -> Self {
517 if value.contains(PropertyFlags::Protected) {
518 return Self::Protected;
519 }
520 if value.contains(PropertyFlags::Private) {
521 return Self::Private;
522 }
523
524 Self::Public
525 }
526}
527
528impl From<MethodFlags> for Visibility {
529 fn from(value: MethodFlags) -> Self {
530 if value.contains(MethodFlags::Protected) {
531 return Self::Protected;
532 }
533
534 if value.contains(MethodFlags::Private) {
535 return Self::Private;
536 }
537
538 Self::Public
539 }
540}
541
542#[repr(C)]
544pub struct Constant {
545 pub name: RString,
547 pub docs: DocBlock,
549 pub value: Option<RString>,
551}
552
553impl From<(String, DocComments)> for Constant {
554 fn from(val: (String, DocComments)) -> Self {
555 let (name, docs) = val;
556 Constant {
557 name: name.into(),
558 value: Option::None,
559 docs: docs.into(),
560 }
561 }
562}
563
564impl From<(String, Box<dyn IntoConst + Send>, DocComments)> for Constant {
565 fn from(val: (String, Box<dyn IntoConst + Send + 'static>, DocComments)) -> Self {
566 let (name, value, docs) = val;
567 Constant {
568 name: name.into(),
569 value: Option::Some(value.stub_value().into()),
570 docs: docs.into(),
571 }
572 }
573}
574
575#[cfg(test)]
576mod tests {
577 #![cfg_attr(windows, feature(abi_vectorcall))]
578 use cfg_if::cfg_if;
579
580 use super::*;
581
582 use crate::{args::Arg, test::test_function};
583
584 #[test]
585 fn test_new_description() {
586 let module = Module {
587 name: "test".into(),
588 functions: vec![].into(),
589 classes: vec![].into(),
590 constants: vec![].into(),
591 #[cfg(feature = "enum")]
592 enums: vec![].into(),
593 };
594
595 let description = Description::new(module);
596 assert_eq!(description.version, crate::VERSION);
597 assert_eq!(description.module.name, "test".into());
598 }
599
600 #[test]
601 fn test_doc_block_from() {
602 let docs: &'static [&'static str] = &["doc1", "doc2"];
603 let docs: DocBlock = docs.into();
604 assert_eq!(docs.0.len(), 2);
605 assert_eq!(docs.0[0], "doc1".into());
606 assert_eq!(docs.0[1], "doc2".into());
607 }
608
609 #[test]
610 fn test_module_from() {
611 let builder = ModuleBuilder::new("test", "test_version")
612 .function(FunctionBuilder::new("test_function", test_function));
613 let module: Module = builder.into();
614 assert_eq!(module.name, "test".into());
615 assert_eq!(module.functions.len(), 1);
616 cfg_if! {
617 if #[cfg(feature = "closure")] {
618 assert_eq!(module.classes.len(), 1);
619 } else {
620 assert_eq!(module.classes.len(), 0);
621 }
622 }
623 assert_eq!(module.constants.len(), 0);
624 }
625
626 #[test]
627 fn test_function_from() {
628 let builder = FunctionBuilder::new("test_function", test_function)
629 .docs(&["doc1", "doc2"])
630 .arg(Arg::new("foo", DataType::Long))
631 .returns(DataType::Bool, true, true);
632 let function: Function = builder.into();
633 assert_eq!(function.name, "test_function".into());
634 assert_eq!(function.docs.0.len(), 2);
635 assert_eq!(
636 function.params,
637 vec![Parameter {
638 name: "foo".into(),
639 ty: Option::Some(DataType::Long),
640 nullable: false,
641 variadic: false,
642 default: Option::None,
643 }]
644 .into()
645 );
646 assert_eq!(
647 function.ret,
648 Option::Some(Retval {
649 ty: DataType::Bool,
650 nullable: true,
651 })
652 );
653 }
654
655 #[test]
656 fn test_class_from() {
657 let builder = ClassBuilder::new("TestClass")
658 .docs(&["doc1", "doc2"])
659 .extends((|| todo!(), "BaseClass"))
660 .implements((|| todo!(), "Interface1"))
661 .implements((|| todo!(), "Interface2"))
662 .property("prop1", PropertyFlags::Public, None, &["doc1"])
663 .method(
664 FunctionBuilder::new("test_function", test_function),
665 MethodFlags::Protected,
666 );
667 let class: Class = builder.into();
668
669 assert_eq!(class.name, "TestClass".into());
670 assert_eq!(class.docs.0.len(), 2);
671 assert_eq!(class.extends, Option::Some("BaseClass".into()));
672 assert_eq!(
673 class.implements,
674 vec!["Interface1".into(), "Interface2".into()].into()
675 );
676 assert_eq!(class.properties.len(), 1);
677 assert_eq!(
678 class.properties[0],
679 Property {
680 name: "prop1".into(),
681 docs: DocBlock(vec!["doc1".into()].into()),
682 ty: Option::None,
683 vis: Visibility::Public,
684 static_: false,
685 nullable: false,
686 default: Option::None,
687 }
688 );
689 assert_eq!(class.methods.len(), 1);
690 assert_eq!(
691 class.methods[0],
692 Method {
693 name: "test_function".into(),
694 docs: DocBlock(vec![].into()),
695 ty: MethodType::Member,
696 params: vec![].into(),
697 retval: Option::None,
698 r#static: false,
699 visibility: Visibility::Protected,
700 r#abstract: false
701 }
702 );
703 }
704
705 #[test]
706 fn test_property_from() {
707 let docs: &'static [&'static str] = &["doc1", "doc2"];
708 let property: Property = (
709 "test_property".to_string(),
710 PropertyFlags::Protected,
711 (),
712 docs,
713 )
714 .into();
715 assert_eq!(property.name, "test_property".into());
716 assert_eq!(property.docs.0.len(), 2);
717 assert_eq!(property.vis, Visibility::Protected);
718 assert!(!property.static_);
719 assert!(!property.nullable);
720 }
721
722 #[test]
723 fn test_method_from() {
724 let builder = FunctionBuilder::new("test_method", test_function)
725 .docs(&["doc1", "doc2"])
726 .arg(Arg::new("foo", DataType::Long))
727 .returns(DataType::Bool, true, true);
728 let method: Method = (builder, MethodFlags::Static | MethodFlags::Protected).into();
729 assert_eq!(method.name, "test_method".into());
730 assert_eq!(method.docs.0.len(), 2);
731 assert_eq!(
732 method.params,
733 vec![Parameter {
734 name: "foo".into(),
735 ty: Option::Some(DataType::Long),
736 nullable: false,
737 variadic: false,
738 default: Option::None,
739 }]
740 .into()
741 );
742 assert_eq!(
743 method.retval,
744 Option::Some(Retval {
745 ty: DataType::Bool,
746 nullable: true,
747 })
748 );
749 assert!(method.r#static);
750 assert_eq!(method.visibility, Visibility::Protected);
751 assert_eq!(method.ty, MethodType::Static);
752 }
753
754 #[test]
755 fn test_ty_from() {
756 let r#static: MethodType = MethodFlags::Static.into();
757 assert_eq!(r#static, MethodType::Static);
758
759 let constructor: MethodType = MethodFlags::IsConstructor.into();
760 assert_eq!(constructor, MethodType::Constructor);
761
762 let member: MethodType = MethodFlags::Public.into();
763 assert_eq!(member, MethodType::Member);
764
765 let mixed: MethodType = (MethodFlags::Protected | MethodFlags::Static).into();
766 assert_eq!(mixed, MethodType::Static);
767
768 let both: MethodType = (MethodFlags::Static | MethodFlags::IsConstructor).into();
769 assert_eq!(both, MethodType::Constructor);
770
771 let empty: MethodType = MethodFlags::empty().into();
772 assert_eq!(empty, MethodType::Member);
773 }
774
775 #[test]
776 fn test_prop_visibility_from() {
777 let private: Visibility = PropertyFlags::Private.into();
778 assert_eq!(private, Visibility::Private);
779
780 let protected: Visibility = PropertyFlags::Protected.into();
781 assert_eq!(protected, Visibility::Protected);
782
783 let public: Visibility = PropertyFlags::Public.into();
784 assert_eq!(public, Visibility::Public);
785
786 let mixed: Visibility = (PropertyFlags::Protected | PropertyFlags::Static).into();
787 assert_eq!(mixed, Visibility::Protected);
788
789 let empty: Visibility = PropertyFlags::empty().into();
790 assert_eq!(empty, Visibility::Public);
791 }
792
793 #[test]
794 fn test_method_visibility_from() {
795 let private: Visibility = MethodFlags::Private.into();
796 assert_eq!(private, Visibility::Private);
797
798 let protected: Visibility = MethodFlags::Protected.into();
799 assert_eq!(protected, Visibility::Protected);
800
801 let public: Visibility = MethodFlags::Public.into();
802 assert_eq!(public, Visibility::Public);
803
804 let mixed: Visibility = (MethodFlags::Protected | MethodFlags::Static).into();
805 assert_eq!(mixed, Visibility::Protected);
806
807 let empty: Visibility = MethodFlags::empty().into();
808 assert_eq!(empty, Visibility::Public);
809 }
810}