1#[macro_use]
20extern crate nom;
21
22use nom::IResult;
23use std::str::{self};
24use std::path::Path;
25use std::collections::HashMap;
26use std::fmt::{self, Display};
27
28mod parser;
29
30#[derive(Debug,PartialEq)]
35pub struct URI<'a> {
36 pub scheme: &'a str,
37 pub user: Option<User<'a>>,
38 pub host: Option<&'a str>,
39 pub port: Option<u16>,
40 pub path: Option<&'a Path>,
41 pub query: Option<HashMap<&'a str, &'a str>>,
42 pub hash: Option<&'a str>
43}
44
45impl <'a> Display for URI<'a> {
46 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
47 write!(f,"{}:", self.scheme)?;
48 if self.user.is_some() || self.host.is_some() {
49 write!(f,"//")?;
50 }
51 if let Some(User{name, password}) = self.user {
52 write!(f,"{}",name)?;
53 if let Some(pwd) = password {
54 write!(f, ":{}", pwd)?
55 }
56 write!(f,"@")?;
57 }
58 if let Some(host) = self.host {
59 write!(f,"{}", host)?;
60 }
61 if let Some(port) = self.port {
62 write!(f, ":{}", port)?;
63 }
64 if let Some(path) = self.path {
65 write!(f, "{}", path.display())?;
66 }
67 if let Some(ref query) = self.query {
68 write!(f,"?")?;
69 let mut prev = false;
70 for (key,val) in query.iter() {
71 if prev {
72 write!(f,"&")?;
73 } else {
74 prev = true;
75 }
76 write!(f,"{}={}", key,val)?;
77 }
78
79 }
80 if let Some(hash) = self.hash {
81 write!(f,"#{}", hash)?;
82 }
83 Ok(())
84 }
85}
86
87
88#[derive(Debug,PartialEq)]
98pub struct User<'a> {
99 pub name: &'a str,
100 pub password: Option<&'a str>
101}
102
103#[derive(Debug,PartialEq)]
105pub enum Error {
106 Parse(nom::Err),
107 Incomplete,
108 NotFullyParsed
109}
110
111impl fmt::Display for Error {
112fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
113 write!(f, "{}: {:?}", <Error as std::error::Error>::description(self), self)
114}
115}
116
117impl std::error::Error for Error {
118fn description(&self) -> &str {
119 "URI parsing error"
120}
121}
122
123pub fn parse_uri<T: AsRef<[u8]>+?Sized>(uri_string: &T) -> Result<URI,Error> {
126 let b:&[u8] = uri_string.as_ref();
127 match parser::uri(b) {
128 IResult::Done(remaining, u) => if remaining.is_empty() {
129 Ok(u)
130 } else {
131 Err(Error::NotFullyParsed)
132 },
133 IResult::Error(e) => Err(Error::Parse(e)),
134 IResult::Incomplete(_) => Err(Error::Incomplete)
135 }
136}
137
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn test_parse_uri() {
145 let u=b"http://nekde/nekdo";
147 let res = parse_uri(u).unwrap();
148 assert_eq!(res.path, Some(Path::new("/nekdo")));
149
150 let u="http://nekde/nekdo#nekdy";
152 let res = parse_uri(u).unwrap();
153 assert_eq!(res.hash, Some("nekdy"));
154
155 let u="http://nekde:123/nekdo#nekdy".to_owned();
159 let res = parse_uri(&u).unwrap();
160 assert_eq!(res.port, Some(123));
161
162 }
163
164 #[test]
165 fn test_display() {
166 let u = "http://usak:kulisak@www.example.com:8080/root/test?kulo=sak&kde=je&help=no&usi=yes#middle";
167 let us = parse_uri(u).unwrap();
168 let u2 = format!("{}",us);
169 println!("{}",us);
170 let us2 = parse_uri(&u2).unwrap();
171 assert_eq!(us, us2);
172
173 }
174}