Automatically check for optional parameters

No longer need to annotate the optional parameter
This commit is contained in:
David Cole 2021-08-23 20:59:44 +12:00
parent b1787ebd0a
commit 07e8d32538
4 changed files with 92 additions and 9 deletions

View File

@ -55,8 +55,9 @@ pub fn parser(args: AttributeArgs, input: ItemFn) -> Result<(TokenStream, Functi
Span::call_site(),
);
let args = build_args(inputs, &attr_args.defaults)?;
let optional = find_optional_parameter(args.iter(), attr_args.optional);
let arg_definitions = build_arg_definitions(&args);
let arg_parser = build_arg_parser(args.iter(), &attr_args.optional)?;
let arg_parser = build_arg_parser(args.iter(), &optional)?;
let arg_accessors = build_arg_accessors(&args);
let return_handler = build_return_handler(output);
@ -88,7 +89,7 @@ pub fn parser(args: AttributeArgs, input: ItemFn) -> Result<(TokenStream, Functi
name: ident.to_string(),
ident: internal_ident.to_string(),
args,
optional: attr_args.optional,
optional,
output: return_type,
};
@ -131,6 +132,27 @@ fn build_arg_definitions(args: &[Arg]) -> Vec<TokenStream> {
.collect()
}
pub fn find_optional_parameter<'a>(
args: impl DoubleEndedIterator<Item = &'a Arg>,
optional: Option<String>,
) -> Option<String> {
if optional.is_some() {
return optional;
}
let mut optional = None;
for arg in args.rev() {
if arg.nullable {
optional.replace(arg.name.clone());
} else {
break;
}
}
optional
}
pub fn build_arg_parser<'a>(
args: impl Iterator<Item = &'a Arg>,
optional: &Option<String>,
@ -284,7 +306,12 @@ impl Arg {
_ => path.to_token_stream().to_string(),
};
Some(Arg::new(name, &stringified, seg.ident == "Option", default))
Some(Arg::new(
name,
&stringified,
seg.ident == "Option" || default.is_some(),
default,
))
}
Type::Reference(ref_) => {
// Returning references is invalid, so let's just create our arg

View File

@ -59,6 +59,13 @@ pub fn parser(input: &mut ImplItemMethod) -> Result<(TokenStream, Method)> {
let internal_ident = Ident::new(&format!("_internal_php_{}", ident), Span::call_site());
let args = build_args(inputs, &defaults)?;
let optional = function::find_optional_parameter(
args.iter().filter_map(|arg| match arg {
Arg::Typed(arg) => Some(arg),
_ => None,
}),
optional,
);
let (arg_definitions, is_static) = build_arg_definitions(&args);
let arg_parser = build_arg_parser(args.iter(), &optional)?;
let arg_accessors = build_arg_accessors(&args);

View File

@ -8,15 +8,15 @@ return types.
## Optional parameters
Optional parameters can be used by setting the Rust parameter type to
`Option<T>` and then passing the name of the first optional parameter into the
macro options. Note that all parameters after the given parameter will be
optional as well, and therefore must be of the type `Option<T>`.
Optional parameters can be used by setting the Rust parameter type to a variant
of `Option<T>`. The macro will then figure out which parameters are optional by
using the last consecutive arguments that are a variant of `Option<T>` or have a
default value.
```rust
# extern crate ext_php_rs;
# use ext_php_rs::prelude::*;
#[php_function(optional = "age")]
#[php_function]
pub fn greet(name: String, age: Option<i32>) -> String {
let mut greeting = format!("Hello, {}!", name);
@ -35,13 +35,59 @@ default, it does not need to be a variant of `Option`:
```rust
# extern crate ext_php_rs;
# use ext_php_rs::prelude::*;
#[php_function(optional = "offset", defaults(offset = 0))]
#[php_function(defaults(offset = 0))]
pub fn rusty_strpos(haystack: &str, needle: &str, offset: i64) -> Option<usize> {
let haystack: String = haystack.chars().skip(offset as usize).collect();
haystack.find(needle)
}
```
Note that if there is a non-optional argument after an argument that is a
variant of `Option<T>`, the `Option<T>` argument will be deemed a nullable
argument rather than an optional argument.
```rust
# extern crate ext_php_rs;
# use ext_php_rs::prelude::*;
/// `age` will be deemed required and nullable rather than optional.
#[php_function]
pub fn greet(name: String, age: Option<i32>, description: String) -> String {
let mut greeting = format!("Hello, {}!", name);
if let Some(age) = age {
greeting += &format!(" You are {} years old.", age);
}
greeting += &format!(" {}.", description);
greeting
}
```
You can also specify the optional arguments if you want to have nullable
arguments before optional arguments. This is done through an attribute
parameter:
```rust
# extern crate ext_php_rs;
# use ext_php_rs::prelude::*;
/// `age` will be deemed required and nullable rather than optional,
/// while description will be optional.
#[php_function(optional = "description")]
pub fn greet(name: String, age: Option<i32>, description: Option<String>) -> String {
let mut greeting = format!("Hello, {}!", name);
if let Some(age) = age {
greeting += &format!(" You are {} years old.", age);
}
if let Some(description) = description {
greeting += &format!(" {}.", description);
}
greeting
}
```
## Throwing exceptions
Exceptions can be thrown from inside a function which returns a `Result<T, E>`,

View File

@ -27,6 +27,9 @@ attributes:
- `#[public]`, `#[protected]` and `#[private]` - Sets the visibility of the
method.
The `#[defaults]` and `#[optional]` attributes operate the same as the
equivalent function attribute parameters.
## Constants
Constants are defined as regular Rust `impl` constants. Any type that implements