1use std::vec::Vec as StdVec;
4
5use crate::{
6 builders::{ClassBuilder, FunctionBuilder},
7 constant::IntoConst,
8 flags::{DataType, MethodFlags, PropertyFlags},
9 prelude::ModuleBuilder,
10};
11use abi::{Option, RString, Str, Vec};
12
13pub mod abi;
14mod stub;
15
16pub use stub::ToStub;
17
18pub type DocComments = &'static [&'static str];
20
21#[repr(C)]
23pub struct Description {
24 pub module: Module,
26 pub version: &'static str,
28}
29
30impl Description {
31 #[must_use]
37 pub fn new(module: Module) -> Self {
38 Self {
39 module,
40 version: crate::VERSION,
41 }
42 }
43}
44
45#[repr(C)]
47#[derive(Debug, PartialEq)]
48pub struct DocBlock(pub Vec<Str>);
49
50impl From<&'static [&'static str]> for DocBlock {
51 fn from(val: &'static [&'static str]) -> Self {
52 Self(
53 val.iter()
54 .map(|s| (*s).into())
55 .collect::<StdVec<_>>()
56 .into(),
57 )
58 }
59}
60
61#[repr(C)]
63pub struct Module {
64 pub name: RString,
66 pub functions: Vec<Function>,
68 pub classes: Vec<Class>,
70 pub constants: Vec<Constant>,
72}
73
74impl From<ModuleBuilder<'_>> for Module {
77 fn from(builder: ModuleBuilder) -> Self {
78 let functions = builder.functions;
79 Self {
80 name: builder.name.into(),
81 functions: functions
82 .into_iter()
83 .map(Function::from)
84 .collect::<StdVec<_>>()
85 .into(),
86 classes: builder
87 .classes
88 .into_iter()
89 .map(|c| c().into())
90 .collect::<StdVec<_>>()
91 .into(),
92 constants: builder
93 .constants
94 .into_iter()
95 .map(Constant::from)
96 .collect::<StdVec<_>>()
97 .into(),
98 }
99 }
100}
101
102#[repr(C)]
104pub struct Function {
105 pub name: RString,
107 pub docs: DocBlock,
109 pub ret: Option<Retval>,
111 pub params: Vec<Parameter>,
113}
114
115impl From<FunctionBuilder<'_>> for Function {
116 fn from(val: FunctionBuilder<'_>) -> Self {
117 let ret_allow_null = val.ret_as_null;
118 Function {
119 name: val.name.into(),
120 docs: DocBlock(
121 val.docs
122 .iter()
123 .map(|d| (*d).into())
124 .collect::<StdVec<_>>()
125 .into(),
126 ),
127 ret: val
128 .retval
129 .map(|r| Retval {
130 ty: r,
131 nullable: r != DataType::Mixed && ret_allow_null,
132 })
133 .into(),
134 params: val
135 .args
136 .into_iter()
137 .map(Parameter::from)
138 .collect::<StdVec<_>>()
139 .into(),
140 }
141 }
142}
143
144#[repr(C)]
146#[derive(Debug, PartialEq)]
147pub struct Parameter {
148 pub name: RString,
150 pub ty: Option<DataType>,
152 pub nullable: bool,
154 pub default: Option<RString>,
156}
157
158#[repr(C)]
160pub struct Class {
161 pub name: RString,
163 pub docs: DocBlock,
165 pub extends: Option<RString>,
167 pub implements: Vec<RString>,
170 pub properties: Vec<Property>,
172 pub methods: Vec<Method>,
174 pub constants: Vec<Constant>,
176}
177
178impl From<ClassBuilder> for Class {
179 fn from(val: ClassBuilder) -> Self {
180 Self {
181 name: val.name.into(),
182 docs: DocBlock(
183 val.docs
184 .iter()
185 .map(|doc| (*doc).into())
186 .collect::<StdVec<_>>()
187 .into(),
188 ),
189 extends: val.extends.map(|(_, stub)| stub.into()).into(),
190 implements: val
191 .interfaces
192 .into_iter()
193 .map(|(_, stub)| stub.into())
194 .collect::<StdVec<_>>()
195 .into(),
196 properties: val
197 .properties
198 .into_iter()
199 .map(Property::from)
200 .collect::<StdVec<_>>()
201 .into(),
202 methods: val
203 .methods
204 .into_iter()
205 .map(Method::from)
206 .collect::<StdVec<_>>()
207 .into(),
208 constants: val
209 .constants
210 .into_iter()
211 .map(|(name, _, docs)| (name, docs))
212 .map(Constant::from)
213 .collect::<StdVec<_>>()
214 .into(),
215 }
216 }
217}
218
219#[repr(C)]
221#[derive(Debug, PartialEq)]
222pub struct Property {
223 pub name: RString,
225 pub docs: DocBlock,
227 pub ty: Option<DataType>,
229 pub vis: Visibility,
231 pub static_: bool,
233 pub nullable: bool,
235 pub default: Option<RString>,
237}
238
239impl From<(String, PropertyFlags, DocComments)> for Property {
240 fn from(value: (String, PropertyFlags, DocComments)) -> Self {
241 let (name, flags, docs) = value;
242 let static_ = flags.contains(PropertyFlags::Static);
243 let vis = Visibility::from(flags);
244 let ty = abi::Option::None;
246 let default = abi::Option::<abi::RString>::None;
248 let nullable = false;
250 let docs = docs.into();
251
252 Self {
253 name: name.into(),
254 docs,
255 ty,
256 vis,
257 static_,
258 nullable,
259 default,
260 }
261 }
262}
263
264#[repr(C)]
266#[derive(Debug, PartialEq)]
267pub struct Method {
268 pub name: RString,
270 pub docs: DocBlock,
272 pub ty: MethodType,
274 pub params: Vec<Parameter>,
276 pub retval: Option<Retval>,
278 pub r#static: bool,
280 pub visibility: Visibility,
282}
283
284impl From<(FunctionBuilder<'_>, MethodFlags)> for Method {
285 fn from(val: (FunctionBuilder<'_>, MethodFlags)) -> Self {
286 let (builder, flags) = val;
287 let ret_allow_null = builder.ret_as_null;
288 Method {
289 name: builder.name.into(),
290 docs: DocBlock(
291 builder
292 .docs
293 .iter()
294 .map(|d| (*d).into())
295 .collect::<StdVec<_>>()
296 .into(),
297 ),
298 retval: builder
299 .retval
300 .map(|r| Retval {
301 ty: r,
302 nullable: r != DataType::Mixed && ret_allow_null,
303 })
304 .into(),
305 params: builder
306 .args
307 .into_iter()
308 .map(Into::into)
309 .collect::<StdVec<_>>()
310 .into(),
311 ty: flags.into(),
312 r#static: flags.contains(MethodFlags::Static),
313 visibility: flags.into(),
314 }
315 }
316}
317
318#[repr(C)]
320#[derive(Debug, PartialEq)]
321pub struct Retval {
322 pub ty: DataType,
324 pub nullable: bool,
326}
327
328#[repr(C)]
330#[derive(Clone, Copy, Debug, PartialEq)]
331pub enum MethodType {
332 Member,
334 Static,
336 Constructor,
338}
339
340impl From<MethodFlags> for MethodType {
341 fn from(value: MethodFlags) -> Self {
342 if value.contains(MethodFlags::IsConstructor) {
343 return Self::Constructor;
344 }
345 if value.contains(MethodFlags::Static) {
346 return Self::Static;
347 }
348
349 Self::Member
350 }
351}
352
353#[repr(C)]
356#[derive(Clone, Copy, Debug, PartialEq)]
357pub enum Visibility {
358 Private,
360 Protected,
362 Public,
364}
365
366impl From<PropertyFlags> for Visibility {
367 fn from(value: PropertyFlags) -> Self {
368 if value.contains(PropertyFlags::Protected) {
369 return Self::Protected;
370 }
371 if value.contains(PropertyFlags::Private) {
372 return Self::Private;
373 }
374
375 Self::Public
376 }
377}
378
379impl From<MethodFlags> for Visibility {
380 fn from(value: MethodFlags) -> Self {
381 if value.contains(MethodFlags::Protected) {
382 return Self::Protected;
383 }
384
385 if value.contains(MethodFlags::Private) {
386 return Self::Private;
387 }
388
389 Self::Public
390 }
391}
392
393#[repr(C)]
395pub struct Constant {
396 pub name: RString,
398 pub docs: DocBlock,
400 pub value: Option<RString>,
402}
403
404impl From<(String, DocComments)> for Constant {
405 fn from(val: (String, DocComments)) -> Self {
406 let (name, docs) = val;
407 Constant {
408 name: name.into(),
409 value: abi::Option::None,
410 docs: docs.into(),
411 }
412 }
413}
414
415impl From<(String, Box<dyn IntoConst + Send>, DocComments)> for Constant {
416 fn from(val: (String, Box<dyn IntoConst + Send + 'static>, DocComments)) -> Self {
417 let (name, _, docs) = val;
418 Constant {
419 name: name.into(),
420 value: abi::Option::None,
421 docs: docs.into(),
422 }
423 }
424}
425
426#[cfg(test)]
427mod tests {
428 #![cfg_attr(windows, feature(abi_vectorcall))]
429 use super::*;
430
431 use crate::{args::Arg, test::test_function};
432
433 #[test]
434 fn test_new_description() {
435 let module = Module {
436 name: "test".into(),
437 functions: vec![].into(),
438 classes: vec![].into(),
439 constants: vec![].into(),
440 };
441
442 let description = Description::new(module);
443 assert_eq!(description.version, crate::VERSION);
444 assert_eq!(description.module.name, "test".into());
445 }
446
447 #[test]
448 fn test_doc_block_from() {
449 let docs: &'static [&'static str] = &["doc1", "doc2"];
450 let docs: DocBlock = docs.into();
451 assert_eq!(docs.0.len(), 2);
452 assert_eq!(docs.0[0], "doc1".into());
453 assert_eq!(docs.0[1], "doc2".into());
454 }
455
456 #[test]
457 fn test_module_from() {
458 let builder = ModuleBuilder::new("test", "test_version")
459 .function(FunctionBuilder::new("test_function", test_function));
460 let module: Module = builder.into();
461 assert_eq!(module.name, "test".into());
462 assert_eq!(module.functions.len(), 1);
463 assert_eq!(module.classes.len(), 0);
464 assert_eq!(module.constants.len(), 0);
465 }
466
467 #[test]
468 fn test_function_from() {
469 let builder = FunctionBuilder::new("test_function", test_function)
470 .docs(&["doc1", "doc2"])
471 .arg(Arg::new("foo", DataType::Long))
472 .returns(DataType::Bool, true, true);
473 let function: Function = builder.into();
474 assert_eq!(function.name, "test_function".into());
475 assert_eq!(function.docs.0.len(), 2);
476 assert_eq!(
477 function.params,
478 vec![Parameter {
479 name: "foo".into(),
480 ty: Option::Some(DataType::Long),
481 nullable: false,
482 default: Option::None,
483 }]
484 .into()
485 );
486 assert_eq!(
487 function.ret,
488 Option::Some(Retval {
489 ty: DataType::Bool,
490 nullable: true,
491 })
492 );
493 }
494
495 #[test]
496 fn test_class_from() {
497 let builder = ClassBuilder::new("TestClass")
498 .docs(&["doc1", "doc2"])
499 .extends((|| todo!(), "BaseClass"))
500 .implements((|| todo!(), "Interface1"))
501 .implements((|| todo!(), "Interface2"))
502 .property("prop1", PropertyFlags::Public, &["doc1"])
503 .method(
504 FunctionBuilder::new("test_function", test_function),
505 MethodFlags::Protected,
506 );
507 let class: Class = builder.into();
508
509 assert_eq!(class.name, "TestClass".into());
510 assert_eq!(class.docs.0.len(), 2);
511 assert_eq!(class.extends, Option::Some("BaseClass".into()));
512 assert_eq!(
513 class.implements,
514 vec!["Interface1".into(), "Interface2".into()].into()
515 );
516 assert_eq!(class.properties.len(), 1);
517 assert_eq!(
518 class.properties[0],
519 Property {
520 name: "prop1".into(),
521 docs: DocBlock(vec!["doc1".into()].into()),
522 ty: Option::None,
523 vis: Visibility::Public,
524 static_: false,
525 nullable: false,
526 default: Option::None,
527 }
528 );
529 assert_eq!(class.methods.len(), 1);
530 assert_eq!(
531 class.methods[0],
532 Method {
533 name: "test_function".into(),
534 docs: DocBlock(vec![].into()),
535 ty: MethodType::Member,
536 params: vec![].into(),
537 retval: Option::None,
538 r#static: false,
539 visibility: Visibility::Protected,
540 }
541 );
542 }
543
544 #[test]
545 fn test_property_from() {
546 let docs: &'static [&'static str] = &["doc1", "doc2"];
547 let property: Property =
548 ("test_property".to_string(), PropertyFlags::Protected, docs).into();
549 assert_eq!(property.name, "test_property".into());
550 assert_eq!(property.docs.0.len(), 2);
551 assert_eq!(property.vis, Visibility::Protected);
552 assert!(!property.static_);
553 assert!(!property.nullable);
554 }
555
556 #[test]
557 fn test_method_from() {
558 let builder = FunctionBuilder::new("test_method", test_function)
559 .docs(&["doc1", "doc2"])
560 .arg(Arg::new("foo", DataType::Long))
561 .returns(DataType::Bool, true, true);
562 let method: Method = (builder, MethodFlags::Static | MethodFlags::Protected).into();
563 assert_eq!(method.name, "test_method".into());
564 assert_eq!(method.docs.0.len(), 2);
565 assert_eq!(
566 method.params,
567 vec![Parameter {
568 name: "foo".into(),
569 ty: Option::Some(DataType::Long),
570 nullable: false,
571 default: Option::None,
572 }]
573 .into()
574 );
575 assert_eq!(
576 method.retval,
577 Option::Some(Retval {
578 ty: DataType::Bool,
579 nullable: true,
580 })
581 );
582 assert!(method.r#static);
583 assert_eq!(method.visibility, Visibility::Protected);
584 assert_eq!(method.ty, MethodType::Static);
585 }
586
587 #[test]
588 fn test_ty_from() {
589 let r#static: MethodType = MethodFlags::Static.into();
590 assert_eq!(r#static, MethodType::Static);
591
592 let constructor: MethodType = MethodFlags::IsConstructor.into();
593 assert_eq!(constructor, MethodType::Constructor);
594
595 let member: MethodType = MethodFlags::Public.into();
596 assert_eq!(member, MethodType::Member);
597
598 let mixed: MethodType = (MethodFlags::Protected | MethodFlags::Static).into();
599 assert_eq!(mixed, MethodType::Static);
600
601 let both: MethodType = (MethodFlags::Static | MethodFlags::IsConstructor).into();
602 assert_eq!(both, MethodType::Constructor);
603
604 let empty: MethodType = MethodFlags::empty().into();
605 assert_eq!(empty, MethodType::Member);
606 }
607
608 #[test]
609 fn test_prop_visibility_from() {
610 let private: Visibility = PropertyFlags::Private.into();
611 assert_eq!(private, Visibility::Private);
612
613 let protected: Visibility = PropertyFlags::Protected.into();
614 assert_eq!(protected, Visibility::Protected);
615
616 let public: Visibility = PropertyFlags::Public.into();
617 assert_eq!(public, Visibility::Public);
618
619 let mixed: Visibility = (PropertyFlags::Protected | PropertyFlags::Static).into();
620 assert_eq!(mixed, Visibility::Protected);
621
622 let empty: Visibility = PropertyFlags::empty().into();
623 assert_eq!(empty, Visibility::Public);
624 }
625
626 #[test]
627 fn test_method_visibility_from() {
628 let private: Visibility = MethodFlags::Private.into();
629 assert_eq!(private, Visibility::Private);
630
631 let protected: Visibility = MethodFlags::Protected.into();
632 assert_eq!(protected, Visibility::Protected);
633
634 let public: Visibility = MethodFlags::Public.into();
635 assert_eq!(public, Visibility::Public);
636
637 let mixed: Visibility = (MethodFlags::Protected | MethodFlags::Static).into();
638 assert_eq!(mixed, Visibility::Protected);
639
640 let empty: Visibility = MethodFlags::empty().into();
641 assert_eq!(empty, Visibility::Public);
642 }
643}