phc/
params.rs

1use std::fmt::Display;
2use std::str::FromStr;
3
4/// A type that transforms hash function parameters to and from the raw,
5/// serialized version.
6pub trait Param {
7    /// The type of this parameter.
8    type Output;
9
10    /// The parameter's name.
11    fn name(&self) -> &str;
12
13    /// The default value for the parameter if it isn't included, or None if
14    /// the parameter is required.
15    fn default(&self) -> Option<Self::Output>;
16
17    /// Extract the parameter's value from a serialized string, returning the
18    /// value or None if parsing failed.
19    fn extract(&self, raw: &str) -> Option<Self::Output>;
20
21    /// Serialize a value to a string.
22    fn serialize(&self, value: Self::Output) -> Option<String>;
23}
24
25/// A generic parameter, suitable for most uses.
26///
27/// # Examples
28/// You can create a GenParam with the `param!` macro:
29/// ```
30/// # #[macro_use] extern crate phc; fn main() {
31/// # use phc::GenParam;
32/// let param_i: GenParam<u32> = param!(i);
33/// let param_j = param!(j: bool);
34/// let param_k = param!(k = false);
35/// # }
36/// ```
37///
38/// GenParam implements Param for any type that implements FromStr.
39pub struct GenParam<'a, T> {
40    /// The name of the param.
41    name: &'a str,
42
43    /// The default param value.
44    default: Option<T>,
45}
46
47impl<'a, T> GenParam<'a, T> {
48    /// Create a new param without a default value.
49    pub fn new(name: &'a str) -> GenParam<'a, T> {
50        GenParam {
51            name,
52            default: None,
53        }
54    }
55
56    /// Create a new param with a default value.
57    pub fn with_default(name: &'a str, default: T) -> GenParam<'a, T> {
58        GenParam {
59            name,
60            default: Some(default),
61        }
62    }
63}
64
65impl<'a, T> Param for GenParam<'a, T>
66where
67    T: Clone + Display + FromStr + PartialEq,
68{
69    type Output = T;
70
71    fn name(&self) -> &str {
72        &self.name
73    }
74
75    fn default(&self) -> Option<Self::Output> {
76        self.default.clone()
77    }
78
79    fn extract(&self, raw: &str) -> Option<Self::Output> {
80        raw.parse().ok()
81    }
82
83    fn serialize(&self, value: Self::Output) -> Option<String> {
84        match &self.default {
85            None => Some(value.to_string()),
86            Some(default) => if value != *default {
87                Some(value.to_string())
88            } else {
89                None
90            },
91        }
92    }
93}
94
95/// Macro...
96#[macro_export]
97macro_rules! param {
98    ($name:ident) => {
99        $crate::GenParam::new(stringify!($name))
100    };
101    ($name:ident: $t:ty) => {
102        $crate::GenParam::<$t>::new(stringify!($name))
103    };
104    ($name:ident = $def:expr) => {
105        $crate::GenParam::with_default(stringify!($name), $def)
106    };
107    ($name:ident: $t:ty = $def:expr) => {
108        $crate::GenParam::<$t>::with_default(stringify!($name), $def)
109    };
110}
111
112pub struct RawParamSlice<'a>(&'a [(&'a str, &'a str)]);
113
114impl<'a> RawParamSlice<'a> {
115    fn next_if_key(&mut self, key: &str) -> Option<&str> {
116        match self.0.split_first() {
117            Some(((ref k, ref v), rest)) if *k == key => {
118                self.0 = rest;
119                Some(v)
120            }
121            _ => None,
122        }
123    }
124
125    pub fn extract_single<P>(&mut self, param: &P) -> Option<P::Output>
126    where
127        P: Param,
128    {
129        match self.next_if_key(param.name()) {
130            None => param.default(),
131            Some(val) => param.extract(val),
132        }
133    }
134}
135
136pub trait ParamSet {
137    type Params;
138
139    // TODO: Make this return a Result.
140    fn extract(&self, raw: RawParamSlice) -> Option<Self::Params>;
141}
142
143pub(crate) fn extract_param_set<P>(param_set: &P, raw: &[(&str, &str)]) -> Option<P::Params>
144where
145    P: ParamSet,
146{
147    param_set.extract(RawParamSlice(raw))
148}
149
150impl<P> ParamSet for P
151where
152    P: Param,
153{
154    type Params = P::Output;
155
156    fn extract(&self, mut raw: RawParamSlice) -> Option<Self::Params> {
157        raw.extract_single(self)
158    }
159}
160
161macro_rules! tuple_param_set {
162    ($( $name:ident: $t:ident ),+ $(,)*) => {
163        impl<$( $t ),+> ParamSet for ($( $t,)+)
164        where $(
165            $t: Param,
166        )* {
167            type Params = ($( $t::Output, )+);
168
169            fn extract(&self, mut raw: RawParamSlice) -> Option<Self::Params> {
170                let ($( $name, )+) = self;
171                Some(($(raw.extract_single($name)?,)+))
172            }
173        }
174    }
175}
176
177tuple_param_set!(a: A);
178tuple_param_set!(a: A, b: B);
179tuple_param_set!(a: A, b: B, c: C);
180tuple_param_set!(a: A, b: B, c: C, d: D);
181tuple_param_set!(a: A, b: B, c: C, d: D, e: E);
182tuple_param_set!(a: A, b: B, c: C, d: D, e: E, f: F);
183tuple_param_set!(a: A, b: B, c: C, d: D, e: E, f: F, g: G);
184tuple_param_set!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
185
186#[macro_export]
187macro_rules! param_set {
188    (@($name:ident, $( $inp:tt )*) -> ($( $body:tt )*)) => {
189        param_set!(@($( $inp )*) -> ($( $body )* param!($name),))
190    };
191    (@($name:ident: $t:ty, $( $inp:tt )*) -> ($( $body:tt )*)) => {
192        param_set!(@($( $inp )*) -> ($( $body )* param!($name: $t),))
193    };
194    (@($name:ident = $def:expr, $( $inp:tt )*) -> ($( $body:tt )*)) => {
195        param_set!(@($( $inp )*) -> ($( $body )* param!($name = $def),))
196    };
197    (@($name:ident: $t:ty = $def:expr, $( $inp:tt )*) -> ($( $body:tt )*)) => {
198        param_set!(@($( $inp )*) -> ($( $body )* param!($name: $t = $def)))
199    };
200    (@($name:ident) -> ($( $body:tt )*)) => {
201        param_set!(@() -> ($( $body )* param!($name)))
202    };
203    (@($name:ident: $t:ty) -> ($( $body:tt )*)) => {
204        param_set!(@() -> ($( $body )* param!($name: $t)))
205    };
206    (@($name:ident = $def:expr) -> ($( $body:tt )*)) => {
207        param_set!(@() -> ($( $body )* param!($name = $def)))
208    };
209    (@($name:ident: $t:ty = $def:expr) -> ($( $body:tt )*)) => {
210        param_set!(@() -> ($( $body )* param!($name: $t = $def)))
211    };
212    (@() -> $body:expr) => {
213        $body
214    };
215    ($( $inp:tt )*) => {
216        param_set!(@($( $inp )*) -> ())
217    };
218}
219
220#[cfg(test)]
221mod tests {
222    use super::extract_param_set;
223
224    #[test]
225    fn param_set_variants() {
226        let params = param_set!(a: u8, b: u32, c = true, y: &str = "Hello!");
227    }
228
229    #[test]
230    fn two_param_set_two_parses() {
231        let params = param_set!(i: u32, mem = true);
232        assert_eq!(
233            extract_param_set(&params, &[("i", "10000")]),
234            Some((10000, true))
235        );
236    }
237}