1use std::{ffi::CString, ptr};
4
5use crate::{
6 convert::{FromZvalMut, IntoZvalDyn},
7 describe::{Parameter, abi},
8 error::{Error, Result},
9 ffi::{zend_internal_arg_info, zend_wrong_parameters_count_error},
10 flags::DataType,
11 types::Zval,
12 zend::ZendType,
13};
14
15pub type ArgInfoTables = Box<[Box<[ArgInfo]>]>;
20
21#[must_use]
23#[derive(Debug)]
24pub struct Arg<'a> {
25 name: String,
26 r#type: DataType,
27 as_ref: bool,
28 allow_null: bool,
29 pub(crate) variadic: bool,
30 default_value: Option<String>,
31 zval: Option<&'a mut Zval>,
32 variadic_zvals: Vec<Option<&'a mut Zval>>,
33}
34
35impl<'a> Arg<'a> {
36 pub fn new<T: Into<String>>(name: T, r#type: DataType) -> Self {
43 Arg {
44 name: name.into(),
45 r#type,
46 as_ref: false,
47 allow_null: false,
48 variadic: false,
49 default_value: None,
50 zval: None,
51 variadic_zvals: vec![],
52 }
53 }
54
55 pub fn of<T: FromZvalMut<'a>>(name: impl Into<String>) -> Self {
62 Arg {
63 name: name.into(),
64 r#type: T::TYPE,
65 as_ref: T::BY_REF,
66 allow_null: T::NULLABLE,
67 variadic: false,
68 default_value: None,
69 zval: None,
70 variadic_zvals: vec![],
71 }
72 }
73
74 #[allow(clippy::wrong_self_convention)]
76 pub fn as_ref(mut self) -> Self {
77 self.as_ref = true;
78 self
79 }
80
81 pub fn is_variadic(mut self) -> Self {
83 self.variadic = true;
84 self
85 }
86
87 pub fn allow_null(mut self) -> Self {
89 self.allow_null = true;
90 self
91 }
92
93 pub fn default<T: Into<String>>(mut self, default: T) -> Self {
95 self.default_value = Some(default.into());
96 self
97 }
98
99 pub fn consume<T>(mut self) -> Result<T, Self>
110 where
111 for<'b> T: FromZvalMut<'b>,
112 {
113 self.zval
114 .as_mut()
115 .and_then(|zv| T::from_zval_mut(zv.dereference_mut()))
116 .ok_or(self)
117 }
118
119 pub fn val<T>(&'a mut self) -> Option<T>
123 where
124 T: FromZvalMut<'a>,
125 {
126 self.zval
127 .as_mut()
128 .and_then(|zv| T::from_zval_mut(zv.dereference_mut()))
129 }
130
131 pub fn variadic_vals<T>(&'a mut self) -> Vec<T>
135 where
136 T: FromZvalMut<'a>,
137 {
138 self.variadic_zvals
139 .iter_mut()
140 .filter_map(|zv| zv.as_mut())
141 .filter_map(|zv| T::from_zval_mut(zv.dereference_mut()))
142 .collect()
143 }
144
145 pub fn try_variadic_vals<T>(&'a mut self) -> Option<Vec<T>>
148 where
149 T: FromZvalMut<'a>,
150 {
151 self.variadic_zvals
152 .iter_mut()
153 .filter_map(|zv| zv.as_mut())
154 .map(|zv| T::from_zval_mut(zv.dereference_mut()))
155 .collect()
156 }
157
158 #[allow(clippy::mut_mut)]
166 pub fn zval(&mut self) -> Option<&mut &'a mut Zval> {
167 self.zval.as_mut()
168 }
169
170 #[allow(clippy::inline_always)]
188 #[inline(always)]
189 pub fn try_call(&self, params: Vec<&dyn IntoZvalDyn>) -> Result<Zval> {
190 self.zval.as_ref().ok_or(Error::Callable)?.try_call(params)
191 }
192
193 pub(crate) fn as_arg_info(&self, optional: bool) -> Result<ArgInfo> {
198 let default_value = match &self.default_value {
199 Some(val) => Some(val.as_str()),
200 None if optional && self.allow_null && !self.variadic => Some("null"),
201 None => None,
202 };
203 Ok(ArgInfo {
204 name: CString::new(self.name.as_str())?.into_raw(),
205 type_: ZendType::empty_from_type(
206 self.r#type,
207 self.as_ref,
208 self.variadic,
209 self.allow_null,
210 )
211 .ok_or(Error::ZvalConversion(self.r#type))?,
212 default_value: match default_value {
213 Some(val) => CString::new(val)?.into_raw(),
214 None => ptr::null(),
215 },
216 })
217 }
218}
219
220impl From<Arg<'_>> for Parameter {
221 fn from(val: Arg<'_>) -> Self {
222 Parameter {
223 name: val.name.into(),
224 ty: Some(val.r#type).into(),
225 nullable: val.allow_null,
226 variadic: val.variadic,
227 default: val.default_value.map(abi::RString::from).into(),
228 }
229 }
230}
231
232pub type ArgInfo = zend_internal_arg_info;
234
235#[must_use]
241pub const fn required_count(omittable: &[bool]) -> usize {
242 let mut required = omittable.len();
243 while required > 0 && omittable[required - 1] {
244 required -= 1;
245 }
246 required
247}
248
249#[must_use]
251pub struct ArgParser<'a, 'b> {
252 args: Vec<&'b mut Arg<'a>>,
253 min_num_args: Option<usize>,
254 arg_zvals: Vec<Option<&'a mut Zval>>,
255}
256
257impl<'a, 'b> ArgParser<'a, 'b> {
258 pub fn new(arg_zvals: Vec<Option<&'a mut Zval>>) -> Self {
260 ArgParser {
261 args: vec![],
262 min_num_args: None,
263 arg_zvals,
264 }
265 }
266
267 pub fn arg(mut self, arg: &'b mut Arg<'a>) -> Self {
273 self.args.push(arg);
274 self
275 }
276
277 pub fn not_required(mut self) -> Self {
279 self.min_num_args = Some(self.args.len());
280 self
281 }
282
283 pub fn required_args(mut self, count: usize) -> Self {
285 self.min_num_args = Some(count);
286 self
287 }
288
289 pub fn parse(mut self) -> Result<()> {
309 let max_num_args = self.args.len();
310 let mut min_num_args = self.min_num_args.unwrap_or(max_num_args);
311 let num_args = self.arg_zvals.len();
312 let has_variadic = self.args.last().is_some_and(|arg| arg.variadic);
313 if has_variadic {
314 min_num_args = min_num_args.saturating_sub(1);
315 }
316
317 if num_args < min_num_args || (!has_variadic && num_args > max_num_args) {
318 unsafe {
321 zend_wrong_parameters_count_error(
322 min_num_args.try_into()?,
323 max_num_args.try_into()?,
324 );
325 };
326 return Err(Error::IncorrectArguments(num_args, min_num_args));
327 }
328
329 for (i, arg_zval) in self.arg_zvals.into_iter().enumerate() {
330 let arg = match self.args.get_mut(i) {
331 Some(arg) => Some(arg),
332 None => self.args.last_mut().filter(|arg| arg.variadic),
334 };
335 if let Some(arg) = arg {
336 if arg.variadic {
337 arg.variadic_zvals.push(arg_zval);
338 } else {
339 arg.zval = arg_zval;
340 }
341 }
342 }
343
344 Ok(())
345 }
346}
347
348#[cfg(test)]
349mod tests {
350 #![allow(clippy::unwrap_used)]
351 #[cfg(feature = "embed")]
352 use crate::embed::Embed;
353
354 use super::*;
355
356 #[test]
357 fn required_count_keeps_omittable_args_before_a_required_one() {
358 assert_eq!(required_count(&[]), 0);
359 assert_eq!(required_count(&[false, false]), 2);
360 assert_eq!(required_count(&[false, true]), 1);
361 assert_eq!(required_count(&[true, false, true, true]), 2);
362 assert_eq!(required_count(&[true, true]), 0);
363 }
364
365 #[test]
366 fn of_reads_metadata_from_the_type() {
367 let arg = Arg::of::<Option<i64>>("maybe");
368 assert_eq!(arg.r#type, DataType::Long);
369 assert!(arg.allow_null);
370 assert!(!arg.as_ref);
371 let arg = Arg::of::<&mut Zval>("target");
372 assert_eq!(arg.r#type, DataType::Mixed);
373 assert!(!arg.allow_null);
374 assert!(arg.as_ref);
375 }
376
377 fn default_of(arg: &Arg<'_>, optional: bool) -> Option<String> {
378 let info = arg.as_arg_info(optional).unwrap();
379 unsafe { drop(CString::from_raw(info.name.cast_mut())) };
380 if info.default_value.is_null() {
381 return None;
382 }
383 let default = unsafe { CString::from_raw(info.default_value.cast_mut()) };
384 Some(default.to_str().unwrap().to_string())
385 }
386
387 #[test]
388 fn optional_nullable_arg_reports_a_null_default() {
389 assert_eq!(
390 default_of(&Arg::of::<Option<i64>>("a"), true).as_deref(),
391 Some("null")
392 );
393 assert_eq!(default_of(&Arg::of::<Option<i64>>("a"), false), None);
394 assert_eq!(default_of(&Arg::of::<i64>("a"), true), None);
395 assert_eq!(
396 default_of(&Arg::of::<Option<i64>>("a").is_variadic(), true),
397 None
398 );
399 assert_eq!(
400 default_of(&Arg::of::<Option<i64>>("a").default("5"), true).as_deref(),
401 Some("5")
402 );
403 }
404
405 #[test]
406 fn test_new() {
407 let arg = Arg::new("test", DataType::Long);
408 assert_eq!(arg.name, "test");
409 assert_eq!(arg.r#type, DataType::Long);
410 assert!(!arg.as_ref);
411 assert!(!arg.allow_null);
412 assert!(!arg.variadic);
413 assert!(arg.default_value.is_none());
414 assert!(arg.zval.is_none());
415 assert!(arg.variadic_zvals.is_empty());
416 }
417
418 #[test]
419 fn test_as_ref() {
420 let arg = Arg::new("test", DataType::Long).as_ref();
421 assert!(arg.as_ref);
422 }
423
424 #[test]
425 fn test_is_variadic() {
426 let arg = Arg::new("test", DataType::Long).is_variadic();
427 assert!(arg.variadic);
428 }
429
430 #[test]
431 fn test_allow_null() {
432 let arg = Arg::new("test", DataType::Long).allow_null();
433 assert!(arg.allow_null);
434 }
435
436 #[test]
437 fn test_default() {
438 let arg = Arg::new("test", DataType::Long).default("default");
439 assert_eq!(arg.default_value, Some("default".to_string()));
440
441 }
443
444 #[test]
445 fn test_consume_no_value() {
446 let arg = Arg::new("test", DataType::Long);
447 let result: Result<i32, _> = arg.consume();
448 assert!(result.is_err());
449 assert_eq!(result.unwrap_err().name, "test");
450 }
451
452 #[test]
453 #[cfg(feature = "embed")]
454 fn test_consume() {
455 let mut arg = Arg::new("test", DataType::Long);
456 let mut zval = Zval::from(42);
457 arg.zval = Some(&mut zval);
458
459 let result: Result<i32, _> = arg.consume();
460 assert_eq!(result.unwrap(), 42);
461 }
462
463 #[test]
464 fn test_val_no_value() {
465 let mut arg = Arg::new("test", DataType::Long);
466 let result: Option<i32> = arg.val();
467 assert!(result.is_none());
468 }
469
470 #[test]
471 #[cfg(feature = "embed")]
472 fn test_val() {
473 let mut arg = Arg::new("test", DataType::Long);
474 let mut zval = Zval::from(42);
475 arg.zval = Some(&mut zval);
476
477 let result: Option<i32> = arg.val();
478 assert_eq!(result.unwrap(), 42);
479 }
480
481 #[test]
482 #[cfg(feature = "embed")]
483 fn test_variadic_vals() {
484 let mut arg = Arg::new("test", DataType::Long).is_variadic();
485 let mut zval1 = Zval::from(42);
486 let mut zval2 = Zval::from(43);
487 arg.variadic_zvals.push(Some(&mut zval1));
488 arg.variadic_zvals.push(Some(&mut zval2));
489
490 let result: Vec<i32> = arg.variadic_vals();
491 assert_eq!(result, vec![42, 43]);
492 }
493
494 #[test]
495 #[cfg(feature = "embed")]
496 fn try_variadic_vals_fails_when_a_value_does_not_convert() {
497 let mut arg = Arg::new("test", DataType::Long).is_variadic();
498 let mut long = Zval::from(42);
499 let mut double = Zval::from(1.5);
500 arg.variadic_zvals.push(Some(&mut long));
501 arg.variadic_zvals.push(Some(&mut double));
502
503 assert_eq!(arg.try_variadic_vals::<i64>(), None);
504 }
505
506 #[test]
507 fn test_zval_no_value() {
508 let mut arg = Arg::new("test", DataType::Long);
509 let result = arg.zval();
510 assert!(result.is_none());
511 }
512
513 #[test]
514 #[cfg(feature = "embed")]
515 fn test_zval() {
516 let mut arg = Arg::new("test", DataType::Long);
517 let mut zval = Zval::from(42);
518 arg.zval = Some(&mut zval);
519
520 let result = arg.zval();
521 assert!(result.is_some());
522 assert_eq!(result.unwrap().dereference_mut().long(), Some(42));
523 }
524
525 #[cfg(feature = "embed")]
526 #[test]
527 fn test_try_call_no_value() {
528 let arg = Arg::new("test", DataType::Long);
529 let result = arg.try_call(vec![]);
530 assert!(result.is_err());
531 }
532
533 #[test]
534 #[cfg(feature = "embed")]
535 fn test_try_call_not_callable() {
536 Embed::run(|| {
537 let mut arg = Arg::new("test", DataType::Long);
538 let mut zval = Zval::from(42);
539 arg.zval = Some(&mut zval);
540
541 let result = arg.try_call(vec![]);
542 assert!(result.is_err());
543 });
544 }
545
546 #[test]
549 #[cfg(feature = "embed")]
550 fn test_as_arg_info() {
551 let arg = Arg::new("test", DataType::Long);
552 let arg_info = arg.as_arg_info(false);
553 assert!(arg_info.is_ok());
554
555 let arg_info = arg_info.unwrap();
556 assert!(arg_info.default_value.is_null());
557
558 let r#type = arg_info.type_;
559 assert_eq!(r#type.type_mask, 16);
560
561 unsafe { drop(CString::from_raw(arg_info.name.cast_mut())) };
562 }
563
564 #[test]
565 #[cfg(feature = "embed")]
566 fn test_as_arg_info_with_default() {
567 let arg = Arg::new("test", DataType::Long).default("default");
568 let arg_info = arg.as_arg_info(false);
569 assert!(arg_info.is_ok());
570
571 let arg_info = arg_info.unwrap();
572 assert!(!arg_info.default_value.is_null());
573
574 let r#type = arg_info.type_;
575 assert_eq!(r#type.type_mask, 16);
576
577 unsafe {
578 drop(CString::from_raw(arg_info.name.cast_mut()));
579 drop(CString::from_raw(arg_info.default_value.cast_mut()));
580 }
581 }
582
583 #[test]
584 fn test_param_from_arg() {
585 let arg = Arg::new("test", DataType::Long)
586 .default("default")
587 .allow_null();
588 let param: Parameter = arg.into();
589 assert_eq!(param.name, "test".into());
590 assert_eq!(param.ty, abi::Option::Some(DataType::Long));
591 assert!(param.nullable);
592 assert_eq!(param.default, abi::Option::Some("default".into()));
593 }
594
595 #[test]
596 fn test_arg_parser_new() {
597 let arg_zvals = vec![None, None];
598 let parser = ArgParser::new(arg_zvals);
599 assert_eq!(parser.arg_zvals.len(), 2);
600 assert!(parser.args.is_empty());
601 assert!(parser.min_num_args.is_none());
602 }
603
604 #[test]
605 fn test_arg_parser_arg() {
606 let arg_zvals = vec![None, None];
607 let mut parser = ArgParser::new(arg_zvals);
608 let mut arg = Arg::new("test", DataType::Long);
609 parser = parser.arg(&mut arg);
610 assert_eq!(parser.args.len(), 1);
611 assert_eq!(parser.args[0].name, "test");
612 assert_eq!(parser.args[0].r#type, DataType::Long);
613 }
614
615 }