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>
133 where
134 T: FromZvalMut<'a>,
135 {
136 self.variadic_zvals
137 .iter_mut()
138 .filter_map(|zv| zv.as_mut())
139 .filter_map(|zv| T::from_zval_mut(zv.dereference_mut()))
140 .collect()
141 }
142
143 #[allow(clippy::mut_mut)]
151 pub fn zval(&mut self) -> Option<&mut &'a mut Zval> {
152 self.zval.as_mut()
153 }
154
155 #[allow(clippy::inline_always)]
173 #[inline(always)]
174 pub fn try_call(&self, params: Vec<&dyn IntoZvalDyn>) -> Result<Zval> {
175 self.zval.as_ref().ok_or(Error::Callable)?.try_call(params)
176 }
177
178 pub(crate) fn as_arg_info(&self, optional: bool) -> Result<ArgInfo> {
183 let default_value = match &self.default_value {
184 Some(val) => Some(val.as_str()),
185 None if optional && self.allow_null && !self.variadic => Some("null"),
186 None => None,
187 };
188 Ok(ArgInfo {
189 name: CString::new(self.name.as_str())?.into_raw(),
190 type_: ZendType::empty_from_type(
191 self.r#type,
192 self.as_ref,
193 self.variadic,
194 self.allow_null,
195 )
196 .ok_or(Error::ZvalConversion(self.r#type))?,
197 default_value: match default_value {
198 Some(val) => CString::new(val)?.into_raw(),
199 None => ptr::null(),
200 },
201 })
202 }
203}
204
205impl From<Arg<'_>> for Parameter {
206 fn from(val: Arg<'_>) -> Self {
207 Parameter {
208 name: val.name.into(),
209 ty: Some(val.r#type).into(),
210 nullable: val.allow_null,
211 variadic: val.variadic,
212 default: val.default_value.map(abi::RString::from).into(),
213 }
214 }
215}
216
217pub type ArgInfo = zend_internal_arg_info;
219
220#[must_use]
226pub const fn required_count(omittable: &[bool]) -> usize {
227 let mut required = omittable.len();
228 while required > 0 && omittable[required - 1] {
229 required -= 1;
230 }
231 required
232}
233
234#[must_use]
236pub struct ArgParser<'a, 'b> {
237 args: Vec<&'b mut Arg<'a>>,
238 min_num_args: Option<usize>,
239 arg_zvals: Vec<Option<&'a mut Zval>>,
240}
241
242impl<'a, 'b> ArgParser<'a, 'b> {
243 pub fn new(arg_zvals: Vec<Option<&'a mut Zval>>) -> Self {
245 ArgParser {
246 args: vec![],
247 min_num_args: None,
248 arg_zvals,
249 }
250 }
251
252 pub fn arg(mut self, arg: &'b mut Arg<'a>) -> Self {
258 self.args.push(arg);
259 self
260 }
261
262 pub fn not_required(mut self) -> Self {
264 self.min_num_args = Some(self.args.len());
265 self
266 }
267
268 pub fn required_args(mut self, count: usize) -> Self {
270 self.min_num_args = Some(count);
271 self
272 }
273
274 pub fn parse(mut self) -> Result<()> {
294 let max_num_args = self.args.len();
295 let mut min_num_args = self.min_num_args.unwrap_or(max_num_args);
296 let num_args = self.arg_zvals.len();
297 let has_variadic = self.args.last().is_some_and(|arg| arg.variadic);
298 if has_variadic {
299 min_num_args = min_num_args.saturating_sub(1);
300 }
301
302 if num_args < min_num_args || (!has_variadic && num_args > max_num_args) {
303 unsafe {
306 zend_wrong_parameters_count_error(
307 min_num_args.try_into()?,
308 max_num_args.try_into()?,
309 );
310 };
311 return Err(Error::IncorrectArguments(num_args, min_num_args));
312 }
313
314 for (i, arg_zval) in self.arg_zvals.into_iter().enumerate() {
315 let arg = match self.args.get_mut(i) {
316 Some(arg) => Some(arg),
317 None => self.args.last_mut().filter(|arg| arg.variadic),
319 };
320 if let Some(arg) = arg {
321 if arg.variadic {
322 arg.variadic_zvals.push(arg_zval);
323 } else {
324 arg.zval = arg_zval;
325 }
326 }
327 }
328
329 Ok(())
330 }
331}
332
333#[cfg(test)]
334mod tests {
335 #![allow(clippy::unwrap_used)]
336 #[cfg(feature = "embed")]
337 use crate::embed::Embed;
338
339 use super::*;
340
341 #[test]
342 fn required_count_keeps_omittable_args_before_a_required_one() {
343 assert_eq!(required_count(&[]), 0);
344 assert_eq!(required_count(&[false, false]), 2);
345 assert_eq!(required_count(&[false, true]), 1);
346 assert_eq!(required_count(&[true, false, true, true]), 2);
347 assert_eq!(required_count(&[true, true]), 0);
348 }
349
350 #[test]
351 fn of_reads_metadata_from_the_type() {
352 let arg = Arg::of::<Option<i64>>("maybe");
353 assert_eq!(arg.r#type, DataType::Long);
354 assert!(arg.allow_null);
355 assert!(!arg.as_ref);
356 let arg = Arg::of::<&mut Zval>("target");
357 assert_eq!(arg.r#type, DataType::Mixed);
358 assert!(!arg.allow_null);
359 assert!(arg.as_ref);
360 }
361
362 fn default_of(arg: &Arg<'_>, optional: bool) -> Option<String> {
363 let info = arg.as_arg_info(optional).unwrap();
364 unsafe { drop(CString::from_raw(info.name.cast_mut())) };
365 if info.default_value.is_null() {
366 return None;
367 }
368 let default = unsafe { CString::from_raw(info.default_value.cast_mut()) };
369 Some(default.to_str().unwrap().to_string())
370 }
371
372 #[test]
373 fn optional_nullable_arg_reports_a_null_default() {
374 assert_eq!(
375 default_of(&Arg::of::<Option<i64>>("a"), true).as_deref(),
376 Some("null")
377 );
378 assert_eq!(default_of(&Arg::of::<Option<i64>>("a"), false), None);
379 assert_eq!(default_of(&Arg::of::<i64>("a"), true), None);
380 assert_eq!(
381 default_of(&Arg::of::<Option<i64>>("a").is_variadic(), true),
382 None
383 );
384 assert_eq!(
385 default_of(&Arg::of::<Option<i64>>("a").default("5"), true).as_deref(),
386 Some("5")
387 );
388 }
389
390 #[test]
391 fn test_new() {
392 let arg = Arg::new("test", DataType::Long);
393 assert_eq!(arg.name, "test");
394 assert_eq!(arg.r#type, DataType::Long);
395 assert!(!arg.as_ref);
396 assert!(!arg.allow_null);
397 assert!(!arg.variadic);
398 assert!(arg.default_value.is_none());
399 assert!(arg.zval.is_none());
400 assert!(arg.variadic_zvals.is_empty());
401 }
402
403 #[test]
404 fn test_as_ref() {
405 let arg = Arg::new("test", DataType::Long).as_ref();
406 assert!(arg.as_ref);
407 }
408
409 #[test]
410 fn test_is_variadic() {
411 let arg = Arg::new("test", DataType::Long).is_variadic();
412 assert!(arg.variadic);
413 }
414
415 #[test]
416 fn test_allow_null() {
417 let arg = Arg::new("test", DataType::Long).allow_null();
418 assert!(arg.allow_null);
419 }
420
421 #[test]
422 fn test_default() {
423 let arg = Arg::new("test", DataType::Long).default("default");
424 assert_eq!(arg.default_value, Some("default".to_string()));
425
426 }
428
429 #[test]
430 fn test_consume_no_value() {
431 let arg = Arg::new("test", DataType::Long);
432 let result: Result<i32, _> = arg.consume();
433 assert!(result.is_err());
434 assert_eq!(result.unwrap_err().name, "test");
435 }
436
437 #[test]
438 #[cfg(feature = "embed")]
439 fn test_consume() {
440 let mut arg = Arg::new("test", DataType::Long);
441 let mut zval = Zval::from(42);
442 arg.zval = Some(&mut zval);
443
444 let result: Result<i32, _> = arg.consume();
445 assert_eq!(result.unwrap(), 42);
446 }
447
448 #[test]
449 fn test_val_no_value() {
450 let mut arg = Arg::new("test", DataType::Long);
451 let result: Option<i32> = arg.val();
452 assert!(result.is_none());
453 }
454
455 #[test]
456 #[cfg(feature = "embed")]
457 fn test_val() {
458 let mut arg = Arg::new("test", DataType::Long);
459 let mut zval = Zval::from(42);
460 arg.zval = Some(&mut zval);
461
462 let result: Option<i32> = arg.val();
463 assert_eq!(result.unwrap(), 42);
464 }
465
466 #[test]
467 #[cfg(feature = "embed")]
468 fn test_variadic_vals() {
469 let mut arg = Arg::new("test", DataType::Long).is_variadic();
470 let mut zval1 = Zval::from(42);
471 let mut zval2 = Zval::from(43);
472 arg.variadic_zvals.push(Some(&mut zval1));
473 arg.variadic_zvals.push(Some(&mut zval2));
474
475 let result: Vec<i32> = arg.variadic_vals();
476 assert_eq!(result, vec![42, 43]);
477 }
478
479 #[test]
480 fn test_zval_no_value() {
481 let mut arg = Arg::new("test", DataType::Long);
482 let result = arg.zval();
483 assert!(result.is_none());
484 }
485
486 #[test]
487 #[cfg(feature = "embed")]
488 fn test_zval() {
489 let mut arg = Arg::new("test", DataType::Long);
490 let mut zval = Zval::from(42);
491 arg.zval = Some(&mut zval);
492
493 let result = arg.zval();
494 assert!(result.is_some());
495 assert_eq!(result.unwrap().dereference_mut().long(), Some(42));
496 }
497
498 #[cfg(feature = "embed")]
499 #[test]
500 fn test_try_call_no_value() {
501 let arg = Arg::new("test", DataType::Long);
502 let result = arg.try_call(vec![]);
503 assert!(result.is_err());
504 }
505
506 #[test]
507 #[cfg(feature = "embed")]
508 fn test_try_call_not_callable() {
509 Embed::run(|| {
510 let mut arg = Arg::new("test", DataType::Long);
511 let mut zval = Zval::from(42);
512 arg.zval = Some(&mut zval);
513
514 let result = arg.try_call(vec![]);
515 assert!(result.is_err());
516 });
517 }
518
519 #[test]
522 #[cfg(feature = "embed")]
523 fn test_as_arg_info() {
524 let arg = Arg::new("test", DataType::Long);
525 let arg_info = arg.as_arg_info(false);
526 assert!(arg_info.is_ok());
527
528 let arg_info = arg_info.unwrap();
529 assert!(arg_info.default_value.is_null());
530
531 let r#type = arg_info.type_;
532 assert_eq!(r#type.type_mask, 16);
533
534 unsafe { drop(CString::from_raw(arg_info.name.cast_mut())) };
535 }
536
537 #[test]
538 #[cfg(feature = "embed")]
539 fn test_as_arg_info_with_default() {
540 let arg = Arg::new("test", DataType::Long).default("default");
541 let arg_info = arg.as_arg_info(false);
542 assert!(arg_info.is_ok());
543
544 let arg_info = arg_info.unwrap();
545 assert!(!arg_info.default_value.is_null());
546
547 let r#type = arg_info.type_;
548 assert_eq!(r#type.type_mask, 16);
549
550 unsafe {
551 drop(CString::from_raw(arg_info.name.cast_mut()));
552 drop(CString::from_raw(arg_info.default_value.cast_mut()));
553 }
554 }
555
556 #[test]
557 fn test_param_from_arg() {
558 let arg = Arg::new("test", DataType::Long)
559 .default("default")
560 .allow_null();
561 let param: Parameter = arg.into();
562 assert_eq!(param.name, "test".into());
563 assert_eq!(param.ty, abi::Option::Some(DataType::Long));
564 assert!(param.nullable);
565 assert_eq!(param.default, abi::Option::Some("default".into()));
566 }
567
568 #[test]
569 fn test_arg_parser_new() {
570 let arg_zvals = vec![None, None];
571 let parser = ArgParser::new(arg_zvals);
572 assert_eq!(parser.arg_zvals.len(), 2);
573 assert!(parser.args.is_empty());
574 assert!(parser.min_num_args.is_none());
575 }
576
577 #[test]
578 fn test_arg_parser_arg() {
579 let arg_zvals = vec![None, None];
580 let mut parser = ArgParser::new(arg_zvals);
581 let mut arg = Arg::new("test", DataType::Long);
582 parser = parser.arg(&mut arg);
583 assert_eq!(parser.args.len(), 1);
584 assert_eq!(parser.args[0].name, "test");
585 assert_eq!(parser.args[0].r#type, DataType::Long);
586 }
587
588 }