use serde::{Deserialize, Serialize};
use std::{fmt, io::Result as IoResult, str::FromStr};
use nom::{character::complete::char, combinator::opt, error::ParseError, sequence::pair, IResult};
pub mod schema;
pub use self::schema::{parse_schema, UriSchema};
pub mod domain;
pub use self::domain::{parse_domain, parse_port, Domain};
pub mod params;
pub use self::params::{parse_param, parse_params, UriParam};
pub mod auth;
pub use self::auth::{parse_uriauth, UriAuth};
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct Uri {
pub schema: Option<UriSchema>,
pub host: Domain,
pub auth: Option<UriAuth>,
pub parameters: Vec<UriParam>,
}
impl Uri {
pub fn new(schema: UriSchema, host: Domain) -> Uri {
Uri {
schema: Some(schema),
host,
auth: None,
parameters: vec![],
}
}
pub fn new_schemaless(host: Domain) -> Uri {
Uri {
schema: None,
host,
auth: None,
parameters: vec![],
}
}
pub fn sip(host: Domain) -> Uri {
Uri::new(UriSchema::Sip, host)
}
pub fn sips(host: Domain) -> Uri {
Uri::new(UriSchema::Sips, host)
}
pub fn auth(mut self, auth: UriAuth) -> Uri {
self.auth = Some(auth);
self
}
pub fn authless(mut self) -> Uri {
self.auth = None;
self
}
pub fn parameter(mut self, p: UriParam) -> Uri {
self.parameters.push(p);
self
}
pub fn parameters(mut self, p: Vec<UriParam>) -> Uri {
self.parameters = p;
self
}
pub fn schemaless(mut self) -> Uri {
self.schema = None;
self
}
pub fn schema(mut self, schema: UriSchema) -> Uri {
self.schema = Some(schema);
self
}
pub fn host(&self) -> String {
format!("{}", self.host)
}
pub fn host_and_params(&self) -> IoResult<String> {
let mut host = self.host();
for param in &self.parameters {
host += &format!("{}", param);
}
Ok(host)
}
}
impl fmt::Display for Uri {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(schema) = self.schema {
write!(f, "{}:", schema)?;
}
if let Some(auth) = &self.auth {
write!(f, "{}@{}", auth, self.host)?;
} else {
write!(f, "{}", self.host)?;
}
for param in &self.parameters {
write!(f, "{}", param)?;
}
Ok(())
}
}
pub fn parse_uri<'a, E: ParseError<&'a [u8]>>(input: &'a [u8]) -> IResult<&'a [u8], Uri, E> {
let (input, schema) = opt(pair(parse_schema::<E>, char(':')))(input)?;
let (input, auth) = opt(parse_uriauth::<E>)(input)?;
let (input, host) = parse_domain::<E>(input)?;
let (input, parameters) = parse_params::<E>(input)?;
Ok((
input,
Uri {
schema: schema.map(|item| item.0),
host,
parameters,
auth,
},
))
}
impl FromStr for Uri {
type Err = nom::Err<nom::error::ErrorKind>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(parse_uri(s.as_bytes()).map_err(map_error)?.1)
}
}
fn map_error(n: nom::Err<(&[u8], nom::error::ErrorKind)>) -> nom::Err<nom::error::ErrorKind> {
match n {
nom::Err::Error((_, a)) => nom::Err::Error(a),
nom::Err::Failure((_, b)) => nom::Err::Failure(b),
nom::Err::Incomplete(a) => nom::Err::Incomplete(a),
}
}