|
1 | 1 | use xmlrpc::common::Value;
|
2 | 2 |
|
3 |
| -macro_rules! parse_xmlrpc_value_recursive { |
| 3 | +macro_rules! validate_xmlrpc_value_recursive { |
4 | 4 | ( $v:ident, ( $($T:tt),* ) ) => (
|
5 | 5 | match $v {
|
6 | 6 | &Value::Array(ref a) => {
|
7 | 7 | // Create a tuple whose elements are recursively filled in
|
8 | 8 | let mut ii: isize = -1;
|
9 | 9 | ( $(
|
10 | 10 | if ii >= a.len() as isize {
|
11 |
| - return Err("Not enough elements in array"); |
| 11 | + return Err("Not enough elements in array".to_string()); |
12 | 12 | } else {
|
13 | 13 | ii += 1;
|
14 | 14 | let ref e = a[ii as usize];
|
15 |
| - parse_xmlrpc_value_recursive!(e, $T) |
| 15 | + validate_xmlrpc_value_recursive!(e, $T) |
16 | 16 | }
|
17 | 17 | ),* )
|
18 | 18 | },
|
19 |
| - _ => return Err("Expected array, found something else"), |
| 19 | + _ => return Err("Expected array, found something else".to_string()), |
20 | 20 | }
|
21 | 21 | );
|
22 | 22 | ( $v:ident, i32 ) => ( match $v {
|
23 | 23 | &Value::Int(ref x) => x.clone(),
|
24 |
| - _ => return Err("Expected int; found something else"), |
| 24 | + _ => return Err("Expected int; found something else".to_string()), |
25 | 25 | } );
|
26 | 26 | ( $v:ident, String ) => ( match $v {
|
27 | 27 | &Value::String(ref x) => x.clone(),
|
28 |
| - _ => return Err("Expected string; found something else"), |
| 28 | + _ => return Err("Expected string; found something else".to_string()), |
| 29 | + } ); |
| 30 | + ( $v:ident, f64 ) => ( match $v { |
| 31 | + &Value::Double(ref x) => x.clone(), |
| 32 | + _ => return Err("Expected double; found something else".to_string()), |
29 | 33 | } );
|
30 | 34 | }
|
31 | 35 |
|
32 | 36 | /// Just calls the macro that does the real parsing, and wraps the result in Ok()
|
33 | 37 | /// since we expect a Result<...> at the top level.
|
34 |
| -macro_rules! parse_xmlrpc_value_top_level { |
| 38 | +macro_rules! validate_xmlrpc_value_top_level { |
35 | 39 | ( $v:ident, $T:tt ) => (
|
36 |
| - Ok(parse_xmlrpc_value_recursive!($v, $T)) |
| 40 | + Ok(validate_xmlrpc_value_recursive!($v, $T)) |
37 | 41 | );
|
38 | 42 | }
|
39 | 43 |
|
40 |
| -macro_rules! parse_xmlrpc_value { |
| 44 | +/// Validate an xmlrpc value using a type specified as nested tuples. |
| 45 | +macro_rules! validate_xmlrpc_value { |
41 | 46 | ( $v:ident, $T:tt ) => (
|
42 | 47 | {
|
43 | 48 | // From here on down, we pass references
|
44 | 49 | let v_ref: &Value = & $v;
|
45 |
| - (|&:| {parse_xmlrpc_value_top_level!(v_ref, $T)})() |
| 50 | + |
| 51 | + // Wrap the validation call in a closure so that we can return errors |
| 52 | + // when we want to stop validating early. |
| 53 | + (|&:| {validate_xmlrpc_value_top_level!(v_ref, $T)})() |
46 | 54 | }
|
47 | 55 | );
|
48 | 56 | }
|
49 | 57 |
|
| 58 | +/// Validate an xmlrpc response using a type specified as nested tuples. |
| 59 | +macro_rules! validate_xmlrpc_response { |
| 60 | + ( $response:ident, $T:tt ) => ( |
| 61 | + match $response { |
| 62 | + Response::Fault {fault_code, fault_string} => Err(format!( |
| 63 | + "Fault: {} ({})", fault_string, fault_code)), |
| 64 | + Response::Success {param} => validate_xmlrpc_value!(param, $T), |
| 65 | + } |
| 66 | + ); |
| 67 | +} |
| 68 | + |
| 69 | +/// Validate an xmlrpc request using a type specified as nested tuples. |
| 70 | +macro_rules! validate_xmlrpc_request { |
| 71 | + ( $request:ident, $T:tt ) => ( |
| 72 | + validate_xmlrpc_value!(xmlrpc::Value::Array($request.params), $T), |
| 73 | + ); |
| 74 | +} |
| 75 | + |
50 | 76 | #[cfg(test)]
|
51 | 77 | mod tests {
|
52 |
| - use xmlrpc::common::Value; |
| 78 | + use xmlrpc::common::{Value, Request, Response}; |
53 | 79 |
|
54 | 80 | #[test]
|
55 |
| - fn test_parse_value() { |
| 81 | + fn test_validate_xmlrpc_value() { |
56 | 82 | let v = Value::Array(vec![
|
57 | 83 | Value::Int(1i32),
|
58 | 84 | Value::Int(3i32)
|
59 | 85 | ]);
|
60 | 86 |
|
61 |
| - let x = parse_xmlrpc_value!(v, (i32, i32)); |
| 87 | + let x = validate_xmlrpc_value!(v, (i32, i32)); |
62 | 88 | assert_eq!(x, Ok((1i32, 3i32)));
|
63 | 89 | }
|
| 90 | + |
| 91 | + #[test] |
| 92 | + fn test_validate_xmlrpc_value_incorrect_types() { |
| 93 | + let v = Value::Array(vec![ |
| 94 | + Value::Int(1i32), |
| 95 | + Value::Int(3i32) |
| 96 | + ]); |
| 97 | + |
| 98 | + let x = validate_xmlrpc_value!(v, (i32, String)); |
| 99 | + match x { |
| 100 | + Ok(_) => panic!("Validation succeeded with incorrect types"), |
| 101 | + Err(_) => {}, |
| 102 | + }; |
| 103 | + } |
| 104 | + |
| 105 | + #[test] |
| 106 | + fn test_validate_xmlrpc_response_success() { |
| 107 | + let v = Response::Success { |
| 108 | + param: Value::Array(vec![ |
| 109 | + Value::String("foo".to_string()), |
| 110 | + Value::Array(vec![ |
| 111 | + Value::Int(3i32), |
| 112 | + Value::Double(3.14) ])]) }; |
| 113 | + |
| 114 | + let x = validate_xmlrpc_response!(v, (String, (i32, f64))); |
| 115 | + match x { |
| 116 | + Ok((x0, (x1, x2))) => assert_eq!((x0, (x1, x2)), ("foo".to_string(), (3i32, 3.14))), |
| 117 | + Err(err) => panic!(err), |
| 118 | + } |
| 119 | + } |
| 120 | + |
| 121 | + #[test] |
| 122 | + fn test_validate_xmlrpc_response_fault() { |
| 123 | + let v = Response::Fault {fault_code: 22, fault_string: "some_fault".to_string()}; |
| 124 | + |
| 125 | + let x = validate_xmlrpc_response!(v, (String, (i32, f64))); |
| 126 | + match x { |
| 127 | + Ok(_) => panic!("Validation succeeded when it should have found fault"), |
| 128 | + Err(_) => {}, |
| 129 | + } |
| 130 | + } |
64 | 131 | }
|
0 commit comments