1use std::convert::From;
2use std::fmt;
3use std::str::FromStr;
4
5use futures;
6use hyper::{self, Client};
7use hyper::rt::{self, Future, Stream};
8
9use serde_json;
10
11#[derive(PartialEq, Debug)]
13pub struct IP(u8, u8, u8, u8);
14
15impl FromStr for IP {
16 type Err = IPParseError;
17
18 fn from_str(s: &str) -> Result<Self, Self::Err> {
19 let mut octets = Vec::new();
20 for octet_str in s.split('.') {
21 match u8::from_str(octet_str) {
22 Ok(octet) => octets.push(octet),
23 Err(_) => return Err(IPParseError::OctetOutOfRange),
24 }
25 }
26
27 if octets.len() != 4 {
28 return Err(IPParseError::WrongNumOctets);
29 }
30
31 Ok(IP(octets[0], octets[1], octets[2], octets[3]))
32 }
33}
34
35impl fmt::Display for IP {
36 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37 write!(f, "{}.{}.{}.{}", self.0, self.1, self.2, self.3)
38 }
39}
40
41#[derive(Debug, PartialEq)]
42pub enum IPParseError {
43 OctetOutOfRange,
44 WrongNumOctets,
45}
46
47#[derive(Debug)]
49pub enum IpifyError {
50 RequestError,
52 ParseError,
54}
55
56impl From<hyper::Error> for IpifyError {
57 fn from(_: hyper::Error) -> IpifyError {
58 IpifyError::RequestError
59 }
60}
61
62impl From<IPParseError> for IpifyError {
63 fn from(_: IPParseError) -> IpifyError {
64 IpifyError::ParseError
65 }
66}
67
68impl From<::std::str::Utf8Error> for IpifyError {
69 fn from(_: ::std::str::Utf8Error) -> IpifyError {
70 IpifyError::ParseError
71 }
72}
73
74#[derive(Serialize, Deserialize, Debug, PartialEq)]
75struct IpifyResponse {
76 origin: String,
77}
78
79impl IpifyResponse {
80 fn from_str(json: &str) -> Self {
81 serde_json::from_str(json).unwrap()
82 }
83}
84
85pub fn get_ip() -> impl Future<Item=IP, Error=IpifyError> {
89 let uri = "http://httpbin.org/ip".parse().unwrap();
90 let client = Client::new();
91 client
92 .get(uri)
93 .map_err(|e|{ e.into() })
94 .and_then(|res| {
95 res
96 .into_body()
97 .concat2()
98 .map_err(|e|{ e.into() })
99 })
100 .and_then(|body_bytes| {
101 ::std::str::from_utf8(&body_bytes)
102 .map(|bytes| { bytes.to_owned() })
103 .map_err(|e| { e.into() })
104 })
105 .and_then(|body| {
106 let response = IpifyResponse::from_str(&body);
107 IP::from_str(&response.origin)
108 .map_err(|e|{ e.into() })
109 })
110}
111
112
113#[cfg(test)]
114mod test {
115 use super::*;
116
117 #[test]
118 fn test_deserialize() {
119 let actual = IpifyResponse::from_str("{\"origin\": \"fartturdbutt\"}");
120 let expected = IpifyResponse{ origin: String::from("fartturdbutt") };
121 assert!(actual == expected);
122 }
123
124 #[test]
125 fn test_parse_ip() {
126 let expected = Ok(IP(127, 0, 0, 1));
127 let actual = IP::from_str("127.0.0.1");
128
129 assert!(expected == actual);
130 }
131
132 #[test]
133 fn test_fail_parse_ip() {
134 let expected = Err(IPParseError::OctetOutOfRange);
135 let actual = IP::from_str("256.0.0.1");
136 assert!(expected == actual);
137
138 let expected = Err(IPParseError::WrongNumOctets);
139 let actual = IP::from_str("127.0.0.1.1");
140 assert!(expected == actual);
141
142 let expected = Err(IPParseError::OctetOutOfRange);
143 let actual = IP::from_str("256.0.0.1.1");
144 assert!(expected == actual);
145 }
146
147 #[test]
148 fn test_ipify() {
149 let f = futures::future::ok::<(), ()>(())
150 .map(|_| {
151 let future = get_ip();
152 future.wait().expect("failed to successfully resolve future");
153 });
154
155 rt::run(f);
156 }
157}