summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Scott <sam.scott89@gmail.com>2017-03-12 13:52:25 -0400
committerSam Scott <sam.scott89@gmail.com>2017-03-12 13:52:25 -0400
commit317b8b17f3e3656cdc64fc6435889d005aa9a8af (patch)
tree5ad198434680593d4eaf7b677ed2da0e29367d98
parent0f1e379af7d42f237ca0989a4445b9abf1de5d0a (diff)
Expand out example and a bit of testing.
-rw-r--r--Cargo.toml5
-rw-r--r--examples/introduction.rs131
-rw-r--r--tests/test_deserialize.rs2
-rw-r--r--tests/test_serialize.rs53
4 files changed, 162 insertions, 29 deletions
diff --git a/Cargo.toml b/Cargo.toml
index caa5de6..d7cdc60 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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(&params).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(&params).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(&params),
- 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(&params),
- Ok(urlencode("x[0]=0&x[1]=1&x[2]=2&y[0][thing]=Thing&y[0][other]=12")));
+ assert_eq!(qs::to_string(&params).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")
}