Skip to content

Commit ce91e07

Browse files
committed
Fixes #18 Adds support for array parameter values
Attribution now supports parameters whose value is a collection of parameters. The type used in the struct to support this behavior is a Vec whose element types are any other supported parameter value type (e.g. Vec<i64>). As part of this effort, the code used to parse the attribute input went through a major overhaul. This included implementing the 'Parse' trait for the 'ParamVal' enum and no longer using the 'MetaList' from the syn crate. These changes to the parsing logic will grant more flexibility in the future for the types of syntax that can be parsed as attribute input. As part of the implementation of this feature, a new variant to the 'ParamVal' enum was also added. The 'Array' variant stores the value as a Vec<ParamVal>. Theoretically this means that heterogenous array values could be used in the future. The following is an example of attribute args and the corresponding usage of the attribute. NOTE: Pound signs omitted due to commit message constraints. [attr_args] struct AttrArgs { vals: Vec<i64> } // ... [my_attr(vals = [1, 2, 3])] fn foo() { //... } As part of this effort, a new example crate was added that demonstrates the usage of multiple types of parameters.
1 parent 023e1a6 commit ce91e07

File tree

15 files changed

+529
-358
lines changed

15 files changed

+529
-358
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,7 @@ members = [
44
"attribution",
55
"attribution-examples/ez-trace/ez-trace-macros",
66
"attribution-examples/ez-trace/fibonacci",
7+
"attribution-examples/exhaustive-attr/exhaustive-attr",
8+
"attribution-examples/exhaustive-attr/exhaustive-attr-macros",
79
"attribution-macros"
810
]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "exhaustive-attr-macros"
3+
version = "0.1.0"
4+
authors = ["Timothy Flowers <[email protected]>"]
5+
edition = "2018"
6+
7+
[lib]
8+
proc-macro = true
9+
10+
[dependencies]
11+
attribution = { path = "../../../attribution" }
12+
proc-macro2 = "^1.0.0"
13+
quote = "^1.0.0"
14+
syn = "^1.0.0"
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use attribution::attr_args;
2+
use proc_macro::TokenStream;
3+
use proc_macro2::Span;
4+
use quote::ToTokens;
5+
use syn::parse_macro_input;
6+
use syn::parse_quote;
7+
use syn::Expr;
8+
use syn::ItemFn;
9+
use syn::LitBool;
10+
use syn::LitFloat;
11+
use syn::LitInt;
12+
use syn::LitStr;
13+
use syn::Stmt;
14+
15+
#[attr_args]
16+
struct AttributeArgs {
17+
flag: bool,
18+
string: String,
19+
integer: i64,
20+
float: f64,
21+
array_of_integers: Vec<i64>,
22+
}
23+
24+
#[proc_macro_attribute]
25+
pub fn exhaustive(attr: TokenStream, tagged: TokenStream) -> TokenStream {
26+
let attr = parse_macro_input!(attr as AttributeArgs);
27+
let mut item_fn = parse_macro_input!(tagged as ItemFn);
28+
for stmt in make_print_lines(attr) {
29+
item_fn.block.stmts.push(stmt)
30+
}
31+
32+
item_fn.to_token_stream().into()
33+
}
34+
35+
fn make_print_lines(attr: AttributeArgs) -> impl IntoIterator<Item = Stmt> {
36+
let flag_val = LitBool {
37+
value: attr.flag,
38+
span: proc_macro2::Span::call_site(),
39+
};
40+
let string_val = LitStr::new(&attr.string, Span::call_site());
41+
let integer_val = LitInt::new(&attr.integer.to_string(), Span::call_site());
42+
let float_val = LitFloat::new(&attr.float.to_string(), Span::call_site());
43+
let array_of_integers = attr
44+
.array_of_integers
45+
.into_iter()
46+
.map(|i| i.to_string())
47+
.map(|i| LitInt::new(&i, Span::call_site()));
48+
let array_of_integers_val: Expr = parse_quote! { vec![#(#array_of_integers),*] };
49+
vec![
50+
parse_quote! {
51+
println!("flag = {}", #flag_val);
52+
},
53+
parse_quote! {
54+
println!("string = {}", #string_val);
55+
},
56+
parse_quote! {
57+
println!("integer = {}", #integer_val);
58+
},
59+
parse_quote! {
60+
println!("float = {}", #float_val);
61+
},
62+
parse_quote! {
63+
println!("array_of_integers = {:?}", #array_of_integers_val);
64+
},
65+
]
66+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "exhaustive-attr"
3+
version = "0.1.0"
4+
authors = ["Timothy Flowers <[email protected]>"]
5+
edition = "2018"
6+
7+
[dependencies]
8+
exhaustive-attr-macros = { path = "../exhaustive-attr-macros" }
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
use exhaustive_attr_macros::exhaustive;
2+
3+
#[exhaustive(flag = true, string = "foo", integer = 1, float = 4.0, array_of_integers = [1, 2, 3])]
4+
fn main() {
5+
println!("End of main!");
6+
}

attribution/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@ attribution-macros = { version = "0.3.0", path = "../attribution-macros" }
1515
proc-macro2 = "^1.0.0"
1616
quote = "^1.0.0"
1717
syn = { version = "^1.0.0", features = ["full"] }
18+
19+
[dev-dependencies]
20+
syn = { version = "^1.0.0", features = ["full", "extra-traits"] }

attribution/src/conversion.rs

Lines changed: 79 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,16 @@
11
use crate::params::ParamKey;
22
use crate::params::ParamVal;
33
use crate::Parameters;
4-
5-
/// An error that occurs as a result of a failed conversion of a `ParameterVal`
6-
#[derive(Debug)]
7-
pub enum FromParameterValueError {
8-
/// The data that was being attempted for conversion was not of the expected type.
9-
UnexpectedType,
10-
}
11-
12-
/// A trait that is used to create a type from a provided `ParamVal`. This
13-
/// trait should be implemented when a type should be able to be converted from
14-
/// a single `ParamVal` type.
15-
///
16-
/// # Example
17-
/// ```
18-
/// use attribution::FromParameterValue;
19-
/// use attribution::ParamVal;
20-
///
21-
/// let param_val = ParamVal::Bool(true);
22-
/// let from_result = bool::from_parameter_value(param_val);
23-
/// assert_eq!(from_result.unwrap(), true);
24-
/// ```
25-
pub trait FromParameterValue: Sized {
26-
/// Tries to create a type from the provided `ParamVal`
27-
fn from_parameter_value(parameter_val: ParamVal) -> Result<Self, FromParameterValueError>;
28-
}
29-
30-
impl FromParameterValue for u64 {
31-
fn from_parameter_value(parameter_val: ParamVal) -> Result<Self, FromParameterValueError> {
32-
if let ParamVal::UnsignedInt(val) = parameter_val {
33-
Ok(val)
34-
} else {
35-
Err(FromParameterValueError::UnexpectedType)
36-
}
37-
}
38-
}
39-
40-
impl FromParameterValue for bool {
41-
fn from_parameter_value(parameter_val: ParamVal) -> Result<Self, FromParameterValueError> {
42-
if let ParamVal::Bool(val) = parameter_val {
43-
Ok(val)
44-
} else {
45-
Err(FromParameterValueError::UnexpectedType)
46-
}
47-
}
48-
}
49-
50-
impl FromParameterValue for String {
51-
fn from_parameter_value(parameter_val: ParamVal) -> Result<Self, FromParameterValueError> {
52-
if let ParamVal::Str(val) = parameter_val {
53-
Ok(val)
54-
} else {
55-
Err(FromParameterValueError::UnexpectedType)
56-
}
57-
}
58-
}
4+
use core::convert::TryFrom;
5+
use core::convert::TryInto;
596

607
/// An error that occurs as a result of a failed conversion of a `Parameters`
618
/// struct
629
#[derive(Debug)]
63-
pub enum FromParametersError {
10+
pub enum FromParametersError<'a> {
6411
/// Indicates the error ocurred because a value for a specified parameter
6512
/// was not supplied.
66-
MissingParam { param_name: ParamKey },
13+
MissingParam { param_key: &'a ParamKey },
6714

6815
/// Indicates the error occurred because the value that was attempted for conversion
6916
/// was for the incorrect type.
@@ -74,35 +21,73 @@ pub enum FromParametersError {
7421
pub trait FromParameters: Sized {
7522
/// Try to create a type from a parameter struct (`params`) for a paramter
7623
/// of a specific name (`param_name`).
77-
fn from_parameters(
24+
fn from_parameters<'a>(
7825
params: &mut Parameters,
79-
param_name: &ParamKey,
80-
) -> Result<Self, FromParametersError>;
26+
param_key: &'a ParamKey,
27+
) -> Result<Self, FromParametersError<'a>>;
8128
}
8229

83-
impl<T: FromParameterValue> FromParameters for T {
84-
fn from_parameters(
30+
impl<T> FromParameters for T
31+
where
32+
T: TryFrom<ParamVal>,
33+
{
34+
fn from_parameters<'a>(
8535
params: &mut Parameters,
86-
param_name: &ParamKey,
87-
) -> Result<Self, FromParametersError> {
88-
if let Some(parameter_val) = params.remove(&param_name.to_owned().into()) {
89-
T::from_parameter_value(parameter_val).map_err(|err| match err {
90-
FromParameterValueError::UnexpectedType => FromParametersError::UnexpectedType,
91-
})
36+
param_key: &'a ParamKey,
37+
) -> Result<Self, FromParametersError<'a>> {
38+
if let Some(param_val) = params.remove(param_key) {
39+
T::try_from(param_val).map_err(|_| FromParametersError::UnexpectedType)
9240
} else {
93-
Err(FromParametersError::MissingParam {
94-
param_name: param_name.clone(),
95-
})
41+
Err(FromParametersError::MissingParam { param_key })
9642
}
9743
}
9844
}
9945

100-
impl<T: FromParameters> FromParameters for Option<T> {
101-
fn from_parameters(
46+
impl FromParameters for Option<bool> {
47+
fn from_parameters<'a>(
10248
params: &mut Parameters,
103-
param_name: &ParamKey,
104-
) -> Result<Self, FromParametersError> {
105-
Ok(T::from_parameters(params, param_name).ok())
49+
param_key: &'a ParamKey,
50+
) -> Result<Self, FromParametersError<'a>> {
51+
Ok(params
52+
.remove(param_key)
53+
.map(|val| val.try_into().ok())
54+
.flatten())
55+
}
56+
}
57+
58+
impl FromParameters for Option<i64> {
59+
fn from_parameters<'a>(
60+
params: &mut Parameters,
61+
param_key: &'a ParamKey,
62+
) -> Result<Self, FromParametersError<'a>> {
63+
Ok(params
64+
.remove(param_key)
65+
.map(|val| val.try_into().ok())
66+
.flatten())
67+
}
68+
}
69+
70+
impl FromParameters for Option<f64> {
71+
fn from_parameters<'a>(
72+
params: &mut Parameters,
73+
param_key: &'a ParamKey,
74+
) -> Result<Self, FromParametersError<'a>> {
75+
Ok(params
76+
.remove(param_key)
77+
.map(|val| val.try_into().ok())
78+
.flatten())
79+
}
80+
}
81+
82+
impl FromParameters for Option<String> {
83+
fn from_parameters<'a>(
84+
params: &mut Parameters,
85+
param_key: &'a ParamKey,
86+
) -> Result<Self, FromParametersError<'a>> {
87+
Ok(params
88+
.remove(param_key)
89+
.map(|val| val.try_into().ok())
90+
.flatten())
10691
}
10792
}
10893

@@ -116,16 +101,18 @@ mod tests {
116101
fn from_parameters_bool() {
117102
let mut params = Parameters::default();
118103
params.insert("foo".into(), ParamVal::Bool(true));
119-
let output = bool::from_parameters(&mut params, &"foo".into());
104+
let param_key = "foo".into();
105+
let output = bool::from_parameters(&mut params, &param_key);
120106

121107
assert_eq!(output.unwrap(), true);
122108
}
123109

124110
#[test]
125111
fn from_parameters_str() {
126112
let mut params = Parameters::default();
127-
params.insert("foo".into(), ParamVal::UnsignedInt(1));
128-
let output = u64::from_parameters(&mut params, &"foo".into());
113+
params.insert("foo".into(), ParamVal::Int(1));
114+
let param_key = "foo".into();
115+
let output = i64::from_parameters(&mut params, &param_key);
129116

130117
assert_eq!(output.unwrap(), 1);
131118
}
@@ -134,7 +121,8 @@ mod tests {
134121
fn from_parameters_int() {
135122
let mut params = Parameters::default();
136123
params.insert("foo".into(), ParamVal::Str("bar".into()));
137-
let output = String::from_parameters(&mut params, &"foo".into());
124+
let param_key = "foo".into();
125+
let output = String::from_parameters(&mut params, &param_key);
138126

139127
let right: String = "bar".into();
140128
assert_eq!(output.unwrap(), right);
@@ -144,33 +132,36 @@ mod tests {
144132
fn from_parameters_bool_option() {
145133
let mut params = Parameters::default();
146134
params.insert("foo".into(), ParamVal::Bool(true));
147-
let output = Option::<bool>::from_parameters(&mut params, &"foo".into());
135+
let param_key = "foo".into();
136+
let output = Option::<bool>::from_parameters(&mut params, &param_key);
148137

149138
assert_eq!(output.unwrap(), Some(true));
150-
let no_output = Option::<bool>::from_parameters(&mut params, &"foo".into());
139+
let no_output = Option::<bool>::from_parameters(&mut params, &param_key);
151140
assert_eq!(no_output.unwrap(), None);
152141
}
153142

154143
#[test]
155144
fn from_parameters_str_option() {
156145
let mut params = Parameters::default();
157-
params.insert("foo".into(), ParamVal::UnsignedInt(1));
158-
let output = Option::<u64>::from_parameters(&mut params, &"foo".into());
146+
params.insert("foo".into(), ParamVal::Int(1));
147+
let param_key = "foo".into();
148+
let output = Option::<i64>::from_parameters(&mut params, &param_key);
159149

160150
assert_eq!(output.unwrap(), Some(1));
161-
let no_output = Option::<u64>::from_parameters(&mut params, &"foo".into());
151+
let no_output = Option::<i64>::from_parameters(&mut params, &param_key);
162152
assert_eq!(no_output.unwrap(), None);
163153
}
164154

165155
#[test]
166156
fn from_parameters_int_option() {
167157
let mut params = Parameters::default();
168-
params.insert("foo".into(), ParamVal::Str("bar".into()));
169-
let output = Option::<String>::from_parameters(&mut params, &"foo".into());
158+
params.insert("foo".into(), "bar".into());
159+
let param_key = "foo".into();
160+
let output = Option::<String>::from_parameters(&mut params, &param_key);
170161

171162
let right: String = "bar".into();
172163
assert_eq!(output.unwrap(), Some(right));
173-
let no_output = Option::<String>::from_parameters(&mut params, &"foo".into());
164+
let no_output = Option::<String>::from_parameters(&mut params, &param_key);
174165
assert_eq!(no_output.unwrap(), None);
175166
}
176167
}

attribution/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ mod conversion;
55
mod params;
66

77
pub use attribution_macros::attr_args;
8-
pub use conversion::FromParameterValue;
9-
pub use conversion::FromParameterValueError;
108
pub use conversion::FromParameters;
119
pub use conversion::FromParametersError;
1210
pub use params::DynamicParameters;

0 commit comments

Comments
 (0)