diff options
-rw-r--r-- | Cargo.toml | 5 | ||||
-rw-r--r-- | examples/introduction.rs | 131 | ||||
-rw-r--r-- | tests/test_deserialize.rs | 2 | ||||
-rw-r--r-- | tests/test_serialize.rs | 53 |
4 files changed, 162 insertions, 29 deletions
@@ -24,6 +24,11 @@ serde = "0.9.3" serde_derive = "0.9.8" url = "1.0.0" +[dev-dependencies] +# Used for examples/ +rand = "0.3" +serde_urlencoded = "0.4" + [profile.release] debug = true lto = false
\ No newline at end of file diff --git a/examples/introduction.rs b/examples/introduction.rs new file mode 100644 index 0000000..9d949b4 --- /dev/null +++ b/examples/introduction.rs @@ -0,0 +1,131 @@ +extern crate csv; +extern crate rand; +extern crate serde; +#[macro_use] +extern crate serde_derive; +extern crate serde_qs as qs; +extern crate serde_urlencoded as urlencoded; + +use rand::Rng; +use std::collections::HashMap; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +struct Address { + city: String, + postcode: String, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +struct QueryParams { + id: u8, + name: String, + address: Address, + phone: u32, + user_ids: Vec<u8>, +} + + +fn main() { + // Encodes as: + // "user_ids%5B3%5D=4&user_ids%5B2%5D=3&address%5Bcity%5D=Carrot+City&\ + // id=42&address%5Bpostcode%5D=12345&name=Acme&user_ids%5B0%5D=1&\ + // phone=12345&user_ids%5B1%5D=2" + let example_params = QueryParams { + id: 42, + name: "Acme".to_string(), + phone: 12345, + address: Address { + city: "Carrot City".to_string(), + postcode: "12345".to_string(), + }, + user_ids: vec!(1,2,3,4), + }; + + // Naive approach: manually parameters in a map. Painful. + let mut map = HashMap::<&str, &str>::new(); + map.insert("id", "42"); + map.insert("name", "Acme"); + map.insert("phone", "12345"); + map.insert("address[city]", "Carrot City"); + map.insert("address[postcode]", "12345"); + map.insert("user_ids[0]", "1"); + map.insert("user_ids[1]", "2"); + map.insert("user_ids[2]", "3"); + map.insert("user_ids[3]", "4"); + + // Note this will be in some random order due to ordering of keys in map. + let encoded = qs::to_string(&map).unwrap(); + println!("`serde_qs` to_string for map:\n\t{}", encoded); + + // In this form, can also simply use `serde_urlencoded`: + let encoded = urlencoded::to_string(&map).unwrap(); + println!("`serde_urlencoded` to_string for map:\n\t{}", encoded); + + // Given this encoded string, you can recover the original map + // as a list of pairs using serde_urlencoded: + let pairs: Vec<(String, String)> = urlencoded::from_str(&encoded).unwrap(); + println!("`serde_urlencoded` from_str to pairs:\n\t{:?}", pairs); + + // However, the best way is to use serde_qs to deserialize the entire thing + // into a struct: + let params: QueryParams = qs::from_str(&encoded).unwrap(); + assert_eq!(params, example_params); + println!("`serde_qs` from_str to struct:\n\t{:?}", params); + + // Similarly, we can serialize this structure using `serde_qs`: + let encoded = qs::to_string(¶ms).unwrap(); + println!("`serde_qs` to_string for struct:\n\t{:?}", encoded); + + // One nice feature is that this gives deterministic encodings: + let encoded2 = qs::to_string(¶ms).unwrap(); + assert_eq!(encoded, encoded2); + + // An advantage of `serde_qs` for deserializing, is that it is robust against + // different orderings of inputs: + + let mut inputs = vec!["id=42", + "name=Acme", + "phone=12345", + "address[city]=Carrot+City", + "address[postcode]=12345", + "user_ids[0]=1", + "user_ids[1]=2", + "user_ids[2]=3", + "user_ids[3]=4" + ]; + + let mut rng = rand::thread_rng(); + for _ in 0..10 { + let mut acc = String::new(); + rng.shuffle(&mut inputs); + for input in &inputs { + acc += input; + acc += "&"; + } + // remove trailing character + acc.pop(); + let params: QueryParams = qs::from_str(&acc).unwrap(); + assert_eq!(params, example_params); + } + + // By default, `serde_qs` uses arrays with indices to denote position. + // However, if omitted, will use input order: + let encoded = "id=42&name=Acme&phone=12345&address[city]=Carrot+City&\ + address[postcode]=12345&\ + user_ids[]=1&\ + user_ids[]=2&\ + user_ids[]=3&\ + user_ids[]=4"; + let params: QueryParams = qs::from_str(encoded).unwrap(); + assert_eq!(params, example_params); + + // Indices do not necessarily need to be continuous: + let encoded = "id=42&name=Acme&phone=12345&address[city]=Carrot+City&\ + address[postcode]=12345&\ + user_ids[1]=2&\ + user_ids[0]=1&\ + user_ids[12]=3&\ + user_ids[512]=4"; + let params: QueryParams = qs::from_str(encoded).unwrap(); + assert_eq!(params, example_params); +}
\ No newline at end of file diff --git a/tests/test_deserialize.rs b/tests/test_deserialize.rs index c6d4673..1e83589 100644 --- a/tests/test_deserialize.rs +++ b/tests/test_deserialize.rs @@ -15,6 +15,7 @@ struct QueryParams { id: u8, name: String, address: Address, + phone: u32, user_ids: Vec<u8>, } @@ -67,6 +68,7 @@ fn deserialize_struct() { let params = QueryParams { id: 42, name: "Acme".to_string(), + phone: 12345, address: Address { city: "Carrot City".to_string(), postcode: "12345".to_string(), diff --git a/tests/test_serialize.rs b/tests/test_serialize.rs index ccac101..af66903 100644 --- a/tests/test_serialize.rs +++ b/tests/test_serialize.rs @@ -2,43 +2,38 @@ extern crate serde_derive; extern crate serde_qs as qs; -#[derive(Serialize, Deserialize)] -struct Foo { bar: Bar, baz: Baz } -#[derive(Serialize, Deserialize)] -struct Bar { x: u8, y: String } -#[derive(Serialize, Deserialize)] -struct Baz { thing: String, other: u8 } +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +struct Address { + city: String, + postcode: String, +} -#[derive(Serialize, Deserialize)] -struct Complex { x: Vec<u8>, y: Vec<Baz> } +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +struct QueryParams { + id: u8, + name: String, + phone: u32, + address: Address, + user_ids: Vec<u8>, +} #[test] fn serialize_struct() { - let params = Foo { - bar: Bar { - x: 10, - y: "Ten".to_owned() - }, - baz: Baz { - thing: "Thing".to_owned(), - other: 12 - } - }; - - assert_eq!(qs::to_string(¶ms), - Ok(urlencode("bar[x]=10&bar[y]=Ten&baz[thing]=Thing&baz[other]=12"))); - - let params = Complex { - x: vec![0,1,2], - y: vec![params.baz], + let params = QueryParams { + id: 42, + name: "Acme".to_string(), + phone: 12345, + address: Address { + city: "Carrot City".to_string(), + postcode: "12345".to_string(), + }, + user_ids: vec!(1,2,3,4), }; - - assert_eq!(qs::to_string(¶ms), - Ok(urlencode("x[0]=0&x[1]=1&x[2]=2&y[0][thing]=Thing&y[0][other]=12"))); + assert_eq!(qs::to_string(¶ms).unwrap(), urlencode("id=42&name=Acme&phone=12345&address[city]=Carrot+City&address[postcode]=12345&user_ids[0]=1&user_ids[1]=2&user_ids[2]=3&user_ids[3]=4")); } fn urlencode(input: &str) -> String { - str::replace(&str::replace(input, "[", "%5B"), "]", "%5D") + str::replace(&str::replace(input, "[", "%5B"), "]", "%5D") } |