1use codegen::CallSite;
2use extract::{Context, Error, Extract, ExtractFuture};
3use extract::bytes::ExtractBytes;
4use percent_encoding;
5use std::borrow::Cow;
6use util::BufStream;
7
8use futures::{Poll, Async};
9
10#[derive(Debug)]
11pub struct ExtractString<B> {
12 inner: Option<ExtractBytes<Vec<u8>, B>>,
13 decode: bool,
14 item: Option<String>,
15}
16
17impl<B: BufStream> Extract<B> for String {
18 type Future = ExtractString<B>;
19
20 fn extract(ctx: &Context) -> Self::Future {
21 use codegen::Source::*;
22
23 let inner = Vec::extract(ctx);
24
25 match ctx.callsite().source() {
26 Capture(_) | QueryString => {
27 ExtractString {
28 inner: Some(inner),
29 decode: true,
30 item: None,
31 }
32 }
33 _ => {
34 ExtractString {
35 inner: Some(inner),
36 decode: false,
37 item: None,
38 }
39 }
40 }
41 }
42
43 fn extract_body(ctx: &Context, body: B) -> Self::Future {
44 ExtractString {
45 inner: Some(Vec::extract_body(ctx, body)),
46 decode: false,
47 item: None,
48 }
49 }
50
51 fn requires_body(callsite: &CallSite) -> bool {
52 <Vec<u8> as Extract<B>>::requires_body(callsite)
53 }
54}
55
56impl<B> ExtractFuture for ExtractString<B>
57where
58 B: BufStream,
59{
60 type Item = String;
61
62 fn poll(&mut self) -> Poll<(), Error> {
63 try_ready!(self.inner.as_mut().unwrap().poll());
64
65 let bytes = self.inner.take().unwrap().extract();
66
67 let mut string = String::from_utf8(bytes)
68 .map_err(|_| {
69 Error::invalid_argument(&"invalid UTF-8 string")
70 })?;
71
72 if self.decode {
73 string = decode(&string)?;
74 }
75
76 self.item = Some(string);
77
78 Ok(Async::Ready(()))
79 }
80
81 fn extract(self) -> String {
82 self.item.unwrap()
83 }
84}
85
86fn decode(s: &str) -> Result<String, Error> {
87 percent_encoding::percent_decode(s.as_bytes())
88 .decode_utf8()
89 .map(Cow::into_owned)
90 .map_err(|e| Error::invalid_argument(&e))
91}
92
93#[cfg(test)]
94mod test {
95 use super::*;
96
97 #[test]
98 fn extract() {
99 assert_eq!("hello, world", decode("hello,%20world").unwrap());
100 assert!(decode("%ff").unwrap_err().is_invalid_argument());
101 }
102}