#[macro_use]
extern crate nom;
use nom::IResult;
use std::str::{self};
use std::path::Path;
use std::collections::HashMap;
use std::fmt::{self, Display};
mod parser;
#[derive(Debug,PartialEq)]
pub struct URI<'a> {
pub scheme: &'a str,
pub user: Option<User<'a>>,
pub host: Option<&'a str>,
pub port: Option<u16>,
pub path: Option<&'a Path>,
pub query: Option<HashMap<&'a str, &'a str>>,
pub hash: Option<&'a str>
}
impl <'a> Display for URI<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f,"{}:", self.scheme)?;
if self.user.is_some() || self.host.is_some() {
write!(f,"//")?;
}
if let Some(User{name, password}) = self.user {
write!(f,"{}",name)?;
if let Some(pwd) = password {
write!(f, ":{}", pwd)?
}
write!(f,"@")?;
}
if let Some(host) = self.host {
write!(f,"{}", host)?;
}
if let Some(port) = self.port {
write!(f, ":{}", port)?;
}
if let Some(path) = self.path {
write!(f, "{}", path.display())?;
}
if let Some(ref query) = self.query {
write!(f,"?")?;
let mut prev = false;
for (key,val) in query.iter() {
if prev {
write!(f,"&")?;
} else {
prev = true;
}
write!(f,"{}={}", key,val)?;
}
}
if let Some(hash) = self.hash {
write!(f,"#{}", hash)?;
}
Ok(())
}
}
#[derive(Debug,PartialEq)]
pub struct User<'a> {
pub name: &'a str,
pub password: Option<&'a str>
}
#[derive(Debug,PartialEq)]
pub enum Error {
Parse(nom::Err),
Incomplete,
NotFullyParsed
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}: {:?}", <Error as std::error::Error>::description(self), self)
}
}
impl std::error::Error for Error {
fn description(&self) -> &str {
"URI parsing error"
}
}
pub fn parse_uri<T: AsRef<[u8]>+?Sized>(uri_string: &T) -> Result<URI,Error> {
let b:&[u8] = uri_string.as_ref();
match parser::uri(b) {
IResult::Done(remaining, u) => if remaining.is_empty() {
Ok(u)
} else {
Err(Error::NotFullyParsed)
},
IResult::Error(e) => Err(Error::Parse(e)),
IResult::Incomplete(_) => Err(Error::Incomplete)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_uri() {
let u=b"http://nekde/nekdo";
let res = parse_uri(u).unwrap();
assert_eq!(res.path, Some(Path::new("/nekdo")));
let u="http://nekde/nekdo#nekdy";
let res = parse_uri(u).unwrap();
assert_eq!(res.hash, Some("nekdy"));
let u="http://nekde:123/nekdo#nekdy".to_owned();
let res = parse_uri(&u).unwrap();
assert_eq!(res.port, Some(123));
}
#[test]
fn test_display() {
let u = "http://usak:kulisak@www.example.com:8080/root/test?kulo=sak&kde=je&help=no&usi=yes#middle";
let us = parse_uri(u).unwrap();
let u2 = format!("{}",us);
println!("{}",us);
let us2 = parse_uri(&u2).unwrap();
assert_eq!(us, us2);
}
}