1use std::vec::Vec as StdVec;
8
9#[cfg(feature = "enum")]
10use crate::builders::EnumBuilder;
11use crate::{
12 builders::{ClassBuilder, FunctionBuilder},
13 constant::IntoConst,
14 flags::{ClassFlags, ConstantFlags, MethodFlags, PropertyFlags},
15 prelude::ModuleBuilder,
16};
17
18use ext_php_rs_introspection::abi::{Option, RString};
19pub use ext_php_rs_introspection::*;
20
21impl From<ModuleBuilder<'_>> for Module {
24 fn from(builder: ModuleBuilder) -> Self {
25 let functions = builder.functions;
26
27 #[allow(unused_mut)]
30 let mut classes = builder
31 .interfaces
32 .into_iter()
33 .chain(builder.classes)
34 .map(|c| c().into())
35 .collect::<StdVec<_>>();
36
37 #[cfg(feature = "closure")]
38 classes.push(Class::closure());
39
40 #[cfg(feature = "enum")]
41 let enums = builder
42 .enums
43 .into_iter()
44 .map(|e| e().into())
45 .collect::<StdVec<_>>();
46 #[cfg(not(feature = "enum"))]
47 let enums = StdVec::new();
48
49 Self {
50 name: builder.name.into(),
51 functions: functions
52 .into_iter()
53 .map(Function::from)
54 .collect::<StdVec<_>>()
55 .into(),
56 classes: classes.into(),
57 enums: enums.into(),
58 constants: builder
59 .constants
60 .into_iter()
61 .map(|(name, value, docs)| constant_from(name, &*value, docs))
62 .collect::<StdVec<_>>()
63 .into(),
64 }
65 }
66}
67
68impl From<FunctionBuilder<'_>> for Function {
69 fn from(val: FunctionBuilder<'_>) -> Self {
70 let ret_allow_null = val.ret_as_null;
71 Function {
72 name: val.name.into(),
73 docs: DocBlock(
74 val.docs
75 .iter()
76 .map(|d| (*d).into())
77 .collect::<StdVec<_>>()
78 .into(),
79 ),
80 ret: val
81 .retval
82 .map(|r| Retval {
83 ty: r,
84 nullable: r != DataType::Mixed && ret_allow_null,
85 })
86 .into(),
87 params: val
88 .args
89 .into_iter()
90 .map(Parameter::from)
91 .collect::<StdVec<_>>()
92 .into(),
93 }
94 }
95}
96
97impl From<ClassBuilder> for Class {
98 fn from(val: ClassBuilder) -> Self {
99 let is_interface =
100 ClassFlags::from_bits_retain(val.get_flags()).contains(ClassFlags::Interface);
101 Self {
102 name: val.name.into(),
103 docs: DocBlock(
104 val.docs
105 .iter()
106 .map(|doc| (*doc).into())
107 .collect::<StdVec<_>>()
108 .into(),
109 ),
110 extends: val.extends.map(|(_, stub)| stub.into()).into(),
111 implements: val
112 .interfaces
113 .into_iter()
114 .map(|(_, stub)| stub.into())
115 .collect::<StdVec<_>>()
116 .into(),
117 properties: val
118 .properties
119 .into_iter()
120 .map(Property::from)
121 .collect::<StdVec<_>>()
122 .into(),
123 methods: val
124 .methods
125 .into_iter()
126 .map(|(builder, flags)| method_from(builder, flags))
127 .collect::<StdVec<_>>()
128 .into(),
129 constants: val
130 .constants
131 .into_iter()
132 .map(|(name, _, docs, stub, flags)| Constant {
133 name: name.into(),
134 value: Option::Some(stub.into()),
135 docs: docs.into(),
136 visibility: flags.into(),
137 })
138 .collect::<StdVec<_>>()
139 .into(),
140 is_interface,
141 }
142 }
143}
144
145#[cfg(feature = "enum")]
146impl From<EnumBuilder> for Enum {
147 fn from(val: EnumBuilder) -> Self {
148 Self {
149 name: val.name.into(),
150 docs: DocBlock(
151 val.docs
152 .iter()
153 .map(|d| (*d).into())
154 .collect::<StdVec<_>>()
155 .into(),
156 ),
157 cases: val
158 .cases
159 .into_iter()
160 .map(EnumCase::from)
161 .collect::<StdVec<_>>()
162 .into(),
163 backing_type: match val.datatype {
164 DataType::Long => Some("int".into()),
165 DataType::String => Some("string".into()),
166 _ => None,
167 }
168 .into(),
169 }
170 }
171}
172
173#[cfg(feature = "enum")]
174impl From<&'static crate::enum_::EnumCase> for EnumCase {
175 fn from(val: &'static crate::enum_::EnumCase) -> Self {
176 Self {
177 name: val.name.into(),
178 docs: DocBlock(
179 val.docs
180 .iter()
181 .map(|d| (*d).into())
182 .collect::<StdVec<_>>()
183 .into(),
184 ),
185 value: val
186 .discriminant
187 .as_ref()
188 .map(|v| match v {
189 crate::enum_::Discriminant::Int(i) => i.to_string().into(),
190 crate::enum_::Discriminant::String(s) => format!("'{s}'").into(),
191 })
192 .into(),
193 }
194 }
195}
196
197impl From<crate::builders::ClassProperty> for Property {
198 fn from(val: crate::builders::ClassProperty) -> Self {
199 let static_ = val.flags.contains(PropertyFlags::Static);
200 let vis = Visibility::from(val.flags);
201 let docs = val.docs.into();
202
203 Self {
204 name: val.name.into(),
205 docs,
206 ty: val.ty.into(),
207 vis,
208 static_,
209 nullable: val.nullable,
210 readonly: val.readonly,
211 default: val.default_stub.map(RString::from).into(),
212 }
213 }
214}
215
216fn method_from(builder: FunctionBuilder<'_>, flags: MethodFlags) -> Method {
217 let ret_allow_null = builder.ret_as_null;
218 Method {
219 name: builder.name.into(),
220 docs: DocBlock(
221 builder
222 .docs
223 .iter()
224 .map(|d| (*d).into())
225 .collect::<StdVec<_>>()
226 .into(),
227 ),
228 retval: builder
229 .retval
230 .map(|r| Retval {
231 ty: r,
232 nullable: r != DataType::Mixed && ret_allow_null,
233 })
234 .into(),
235 params: builder
236 .args
237 .into_iter()
238 .map(Into::into)
239 .collect::<StdVec<_>>()
240 .into(),
241 ty: flags.into(),
242 r#static: flags.contains(MethodFlags::Static),
243 visibility: flags.into(),
244 r#abstract: flags.contains(MethodFlags::Abstract),
245 }
246}
247
248impl From<MethodFlags> for MethodType {
249 fn from(value: MethodFlags) -> Self {
250 if value.contains(MethodFlags::IsConstructor) {
251 return Self::Constructor;
252 }
253 if value.contains(MethodFlags::Static) {
254 return Self::Static;
255 }
256
257 Self::Member
258 }
259}
260
261impl From<PropertyFlags> for Visibility {
262 fn from(value: PropertyFlags) -> Self {
263 if value.contains(PropertyFlags::Protected) {
264 return Self::Protected;
265 }
266 if value.contains(PropertyFlags::Private) {
267 return Self::Private;
268 }
269
270 Self::Public
271 }
272}
273
274impl From<MethodFlags> for Visibility {
275 fn from(value: MethodFlags) -> Self {
276 if value.contains(MethodFlags::Protected) {
277 return Self::Protected;
278 }
279
280 if value.contains(MethodFlags::Private) {
281 return Self::Private;
282 }
283
284 Self::Public
285 }
286}
287
288impl From<ConstantFlags> for Visibility {
289 fn from(value: ConstantFlags) -> Self {
290 if value.contains(ConstantFlags::Protected) {
291 return Self::Protected;
292 }
293 if value.contains(ConstantFlags::Private) {
294 return Self::Private;
295 }
296
297 Self::Public
298 }
299}
300
301fn constant_from(name: String, value: &dyn IntoConst, docs: DocComments) -> Constant {
302 Constant {
303 name: name.into(),
304 value: Option::Some(value.stub_value().into()),
305 docs: docs.into(),
306 visibility: Visibility::Public,
307 }
308}
309
310#[cfg(test)]
311mod tests {
312 #![cfg_attr(windows, feature(abi_vectorcall))]
313 use cfg_if::cfg_if;
314
315 use super::*;
316
317 use crate::{args::Arg, test::test_function};
318
319 #[test]
320 fn test_module_from() {
321 let builder = ModuleBuilder::new("test", "test_version")
322 .function(FunctionBuilder::new("test_function", test_function));
323 let module: Module = builder.into();
324 assert_eq!(module.name, "test".into());
325 assert_eq!(module.functions.len(), 1);
326 cfg_if! {
327 if #[cfg(feature = "closure")] {
328 assert_eq!(module.classes.len(), 1);
329 } else {
330 assert_eq!(module.classes.len(), 0);
331 }
332 }
333 assert_eq!(module.enums.len(), 0);
334 assert_eq!(module.constants.len(), 0);
335 }
336
337 #[test]
338 fn test_function_from() {
339 let builder = FunctionBuilder::new("test_function", test_function)
340 .docs(&["doc1", "doc2"])
341 .arg(Arg::new("foo", DataType::Long))
342 .returns(DataType::Bool, true, true);
343 let function: Function = builder.into();
344 assert_eq!(function.name, "test_function".into());
345 assert_eq!(function.docs.0.len(), 2);
346 assert_eq!(
347 function.params,
348 vec![Parameter {
349 name: "foo".into(),
350 ty: Option::Some(DataType::Long),
351 nullable: false,
352 variadic: false,
353 default: Option::None,
354 }]
355 .into()
356 );
357 assert_eq!(
358 function.ret,
359 Option::Some(Retval {
360 ty: DataType::Bool,
361 nullable: true,
362 })
363 );
364 }
365
366 #[test]
367 fn test_class_from() {
368 let builder = ClassBuilder::new("TestClass")
369 .docs(&["doc1", "doc2"])
370 .extends((|| todo!(), "BaseClass"))
371 .implements((|| todo!(), "Interface1"))
372 .implements((|| todo!(), "Interface2"))
373 .property(crate::builders::ClassProperty {
374 name: "prop1".into(),
375 flags: PropertyFlags::Public,
376 default: None,
377 docs: &["doc1"],
378 ty: None,
379 nullable: false,
380 readonly: false,
381 default_stub: None,
382 })
383 .method(
384 FunctionBuilder::new("test_function", test_function),
385 MethodFlags::Protected,
386 );
387 let class: Class = builder.into();
388
389 assert_eq!(class.name, "TestClass".into());
390 assert!(!class.is_interface);
391 assert_eq!(class.docs.0.len(), 2);
392 assert_eq!(class.extends, Option::Some("BaseClass".into()));
393 assert_eq!(
394 class.implements,
395 vec!["Interface1".into(), "Interface2".into()].into()
396 );
397 assert_eq!(class.properties.len(), 1);
398 assert_eq!(
399 class.properties[0],
400 Property {
401 name: "prop1".into(),
402 docs: DocBlock(vec!["doc1".into()].into()),
403 ty: Option::None,
404 vis: Visibility::Public,
405 static_: false,
406 nullable: false,
407 readonly: false,
408 default: Option::None,
409 }
410 );
411 assert_eq!(class.methods.len(), 1);
412 assert_eq!(
413 class.methods[0],
414 Method {
415 name: "test_function".into(),
416 docs: DocBlock(vec![].into()),
417 ty: MethodType::Member,
418 params: vec![].into(),
419 retval: Option::None,
420 r#static: false,
421 visibility: Visibility::Protected,
422 r#abstract: false
423 }
424 );
425 }
426
427 #[test]
428 fn test_interface_from() {
429 let class: Class = ClassBuilder::new("TestInterface")
430 .flags(ClassFlags::Interface)
431 .into();
432 assert!(class.is_interface);
433 }
434
435 #[test]
436 fn test_property_from() {
437 let property: Property = crate::builders::ClassProperty {
438 name: "test_property".into(),
439 flags: PropertyFlags::Protected,
440 default: None,
441 docs: &["doc1", "doc2"],
442 ty: Some(DataType::String),
443 nullable: true,
444 readonly: false,
445 default_stub: Some("null".into()),
446 }
447 .into();
448 assert_eq!(property.name, "test_property".into());
449 assert_eq!(property.docs.0.len(), 2);
450 assert_eq!(property.vis, Visibility::Protected);
451 assert!(!property.static_);
452 assert!(property.nullable);
453 assert_eq!(property.default, Option::Some("null".into()));
454 assert_eq!(property.ty, Option::Some(DataType::String));
455 }
456
457 #[test]
458 fn test_method_from() {
459 let builder = FunctionBuilder::new("test_method", test_function)
460 .docs(&["doc1", "doc2"])
461 .arg(Arg::new("foo", DataType::Long))
462 .returns(DataType::Bool, true, true);
463 let method = method_from(builder, MethodFlags::Static | MethodFlags::Protected);
464 assert_eq!(method.name, "test_method".into());
465 assert_eq!(method.docs.0.len(), 2);
466 assert_eq!(
467 method.params,
468 vec![Parameter {
469 name: "foo".into(),
470 ty: Option::Some(DataType::Long),
471 nullable: false,
472 variadic: false,
473 default: Option::None,
474 }]
475 .into()
476 );
477 assert_eq!(
478 method.retval,
479 Option::Some(Retval {
480 ty: DataType::Bool,
481 nullable: true,
482 })
483 );
484 assert!(method.r#static);
485 assert_eq!(method.visibility, Visibility::Protected);
486 assert_eq!(method.ty, MethodType::Static);
487 }
488
489 #[test]
490 fn test_ty_from() {
491 let r#static: MethodType = MethodFlags::Static.into();
492 assert_eq!(r#static, MethodType::Static);
493
494 let constructor: MethodType = MethodFlags::IsConstructor.into();
495 assert_eq!(constructor, MethodType::Constructor);
496
497 let member: MethodType = MethodFlags::Public.into();
498 assert_eq!(member, MethodType::Member);
499
500 let mixed: MethodType = (MethodFlags::Protected | MethodFlags::Static).into();
501 assert_eq!(mixed, MethodType::Static);
502
503 let both: MethodType = (MethodFlags::Static | MethodFlags::IsConstructor).into();
504 assert_eq!(both, MethodType::Constructor);
505
506 let empty: MethodType = MethodFlags::empty().into();
507 assert_eq!(empty, MethodType::Member);
508 }
509
510 #[test]
511 fn test_prop_visibility_from() {
512 let private: Visibility = PropertyFlags::Private.into();
513 assert_eq!(private, Visibility::Private);
514
515 let protected: Visibility = PropertyFlags::Protected.into();
516 assert_eq!(protected, Visibility::Protected);
517
518 let public: Visibility = PropertyFlags::Public.into();
519 assert_eq!(public, Visibility::Public);
520
521 let mixed: Visibility = (PropertyFlags::Protected | PropertyFlags::Static).into();
522 assert_eq!(mixed, Visibility::Protected);
523
524 let empty: Visibility = PropertyFlags::empty().into();
525 assert_eq!(empty, Visibility::Public);
526 }
527
528 #[test]
529 fn test_method_visibility_from() {
530 let private: Visibility = MethodFlags::Private.into();
531 assert_eq!(private, Visibility::Private);
532
533 let protected: Visibility = MethodFlags::Protected.into();
534 assert_eq!(protected, Visibility::Protected);
535
536 let public: Visibility = MethodFlags::Public.into();
537 assert_eq!(public, Visibility::Public);
538
539 let mixed: Visibility = (MethodFlags::Protected | MethodFlags::Static).into();
540 assert_eq!(mixed, Visibility::Protected);
541
542 let empty: Visibility = MethodFlags::empty().into();
543 assert_eq!(empty, Visibility::Public);
544 }
545}