uri_parser/
lib.rs

1//! Module to provide a light URI parser focused on easy access to
2//! and inspection of URI components
3//! 
4//! # Examples
5//! 
6//! ```
7//! use uri_parser::parse_uri;
8//! 
9//! let uri_string = "http://usak:kulisak@www.example.com:8080/root/test?kulo=sak&kde=je&help=no&usi=yes#middle";
10//! let parsed_uri = parse_uri(uri_string).unwrap();
11//! assert_eq!(parsed_uri.port, Some(8080));
12//! assert_eq!(parsed_uri.host, Some("www.example.com"));
13//! assert!(parsed_uri.user.is_some());
14//! let d = parsed_uri.query.unwrap();
15//! let h=d.get("help").unwrap();
16//! assert_eq!(*h, "no");
17//! ```
18//! 
19#[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/// Represents parsed URI structure
31///  URI parts are scheme, user (struct with name and password), host, port
32/// path (represented as std::path::Path), query (HashMap of key, value pairs)
33/// and hash (fragment)
34#[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// FromStr cannot be implemeneted as URI has lifetime param
89// Could implement From, however does not make much sence, as one can get parsing error easily
90// impl <'a> From<&'a str> for URI<'a> {
91    
92//     fn from(s: &'a str) -> Self {
93//         parse_uri(s).unwrap()
94//     }
95// }
96
97#[derive(Debug,PartialEq)]
98pub struct User<'a> {
99    pub name: &'a str,
100    pub password: Option<&'a str>
101}
102
103/// Possible parsing errors
104#[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
123/// Parses URI from string or bytes slice
124/// Returns Result with URI structure or parsing Error
125pub 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        //bytes works
146        let u=b"http://nekde/nekdo";
147        let res = parse_uri(u).unwrap();
148        assert_eq!(res.path, Some(Path::new("/nekdo")));
149
150        // str works
151        let u="http://nekde/nekdo#nekdy";
152        let res = parse_uri(u).unwrap();
153        assert_eq!(res.hash, Some("nekdy"));
154
155        // string works
156
157        // str works
158        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}