#![allow(clippy::module_name_repetitions)]
use crate::error::{Error as IriError, Result as IriResult};
use crate::{Authority, Fragment, Host, Path, Port, Query, QueryPart, Scheme, IRI};
use std::convert::TryFrom;
use std::str::FromStr;
pub struct IriBuilder {
scheme: Option<Scheme>,
host: Option<Host>,
user_name: Option<String>,
password: Option<String>,
port: Option<Port>,
path: Option<Path>,
query: Option<Query>,
fragment: Option<Fragment>,
}
impl Default for IriBuilder {
fn default() -> Self {
Self {
scheme: None,
host: None,
user_name: None,
password: None,
port: None,
path: None,
query: None,
fragment: None,
}
}
}
impl TryFrom<&mut IriBuilder> for IRI {
type Error = IriError;
fn try_from(builder: &mut IriBuilder) -> Result<Self, Self::Error> {
let mut iri = match &builder.path {
None => IRI::default(),
Some(path) => IRI::new(path),
};
let mut authority = Authority::default();
if let Some(user_name) = &builder.user_name {
authority.set_user_name(user_name)?;
}
if let Some(password) = &builder.password {
authority.set_password(password)?;
}
if let Some(host) = &builder.host {
authority.set_host(host.clone());
}
if let Some(port) = builder.port {
authority.set_port(port);
}
if !authority.is_empty() {
iri.set_authority(Some(authority));
}
if let Some(scheme) = &builder.scheme {
iri.set_scheme(Some(scheme.clone()));
}
if let Some(query) = &builder.query {
iri.set_query(Some(query.clone()));
}
if let Some(fragment) = &builder.fragment {
iri.set_fragment(Some(fragment.clone()));
}
Ok(iri)
}
}
impl IriBuilder {
pub fn scheme(&mut self, scheme: &Scheme) -> &mut Self {
self.scheme = Some(scheme.clone());
self
}
pub fn scheme_str(&mut self, scheme: &str) -> IriResult<&mut Self> {
self.scheme = Some(Scheme::from_str(scheme)?);
Ok(self)
}
pub fn host_name(&mut self, host: &str) -> IriResult<&mut Self> {
self.host = Some(Host::Name(host.to_string()));
Ok(self)
}
pub fn ip_address(&mut self, host: &str) -> IriResult<&mut Self> {
self.host = Some(Host::IPV4(host.to_string()));
Ok(self)
}
pub fn ipv6_address(&mut self, host: &str) -> IriResult<&mut Self> {
self.host = Some(Host::IPV6(host.to_string()));
Ok(self)
}
pub fn port(&mut self, port: Port) -> &mut Self {
self.port = Some(port);
self
}
pub fn user(&mut self, user_name: &str, password: &str) -> &mut Self {
self.user_name = Some(user_name.to_string());
self.password = Some(password.to_string());
self
}
pub fn user_name(&mut self, user_name: &str) -> &mut Self {
self.user_name = Some(user_name.to_string());
self
}
pub fn password(&mut self, password: &str) -> &mut Self {
self.password = Some(password.to_string());
self
}
pub fn path(&mut self, path: &Path) -> &mut Self {
self.path = Some(path.clone());
self
}
pub fn path_str(&mut self, path: &str) -> IriResult<&mut Self> {
self.path = Some(Path::from_str(path)?);
Ok(self)
}
pub fn append_segment(&mut self, segment: &str) -> &mut Self {
match &mut self.path {
None => self.path = Some(Path::from_str(segment).unwrap()),
Some(path) => path.push(segment).unwrap(),
}
self
}
pub fn query(&mut self, query: &Query) -> &mut Self {
self.query = Some(query.clone());
self
}
pub fn query_str(&mut self, query: &str) -> IriResult<&mut Self> {
self.query = Some(Query::from_str(query)?);
Ok(self)
}
pub fn append_query(&mut self, key: &str, value: &str) -> &mut Self {
let part = QueryPart::with_value(key, value);
match &mut self.query {
None => self.query = Some(Query::new(&part)),
Some(query) => query.push(&part),
}
self
}
pub fn fragment(&mut self, fragment: &Fragment) -> &mut Self {
self.fragment = Some(fragment.clone());
self
}
pub fn fragment_str(&mut self, fragment: &str) -> IriResult<&mut Self> {
self.fragment = Some(Fragment::from_str(fragment)?);
Ok(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::KnownSchemes;
use std::convert::TryInto;
#[test]
fn test_http_url() {
fn inner() -> IriResult<IRI> {
let mut builder = IriBuilder::default();
let result: IriResult<IRI> = builder
.scheme(&KnownSchemes::Https.into())
.user_name("john.doe")
.host_name("www.example.com")?
.port(123)
.path_str("/forum/questions/")?
.query_str("tag=networking&order=newest")?
.fragment_str("top")?
.try_into();
assert!(result.is_ok());
result
}
let iri = inner().unwrap();
assert_eq!(
iri.to_string(),
"https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top"
.to_string()
);
}
#[test]
fn test_ldap_url() {
let mut builder = IriBuilder::default();
let result: IriResult<IRI> = builder
.scheme(&KnownSchemes::Ldap.into())
.host_name("[2001:db8::7]")
.unwrap()
.path_str("/c=GB")
.unwrap()
.query_str("objectClass?one")
.unwrap()
.try_into();
assert!(result.is_ok());
let iri = result.unwrap();
assert_eq!(
iri.to_string(),
"ldap://[2001:db8::7]/c=GB?objectClass?one".to_string()
);
}
#[test]
fn test_mailto_iri() {
let mut builder = IriBuilder::default();
let result: IriResult<IRI> = builder
.scheme(&KnownSchemes::Mailto.into())
.path_str("John.Doe@example.com")
.unwrap()
.try_into();
assert!(result.is_ok());
let iri = result.unwrap();
assert_eq!(iri.to_string(), "mailto:John.Doe@example.com".to_string());
}
#[test]
fn test_news_iri() {
let mut builder = IriBuilder::default();
let result: IriResult<IRI> = builder
.scheme(&KnownSchemes::News.into())
.path_str("comp.infosystems.www.servers.unix")
.unwrap()
.try_into();
assert!(result.is_ok());
let iri = result.unwrap();
assert_eq!(
iri.to_string(),
"news:comp.infosystems.www.servers.unix".to_string()
);
}
#[test]
fn test_tel_iri() {
let mut builder = IriBuilder::default();
let result: IriResult<IRI> = builder
.scheme(&KnownSchemes::Tel.into())
.path_str("+1-816-555-1212")
.unwrap()
.try_into();
assert!(result.is_ok());
let iri = result.unwrap();
assert_eq!(iri.to_string(), "tel:+1-816-555-1212".to_string());
}
#[test]
fn test_telnet_iri() {
let mut builder = IriBuilder::default();
let result: IriResult<IRI> = builder
.scheme(&KnownSchemes::Telnet.into())
.ip_address("192.0.2.16")
.unwrap()
.port(80)
.path_str("/")
.unwrap()
.try_into();
assert!(result.is_ok());
let iri = result.unwrap();
assert_eq!(iri.to_string(), "telnet://192.0.2.16:80/".to_string());
}
#[test]
fn test_urn_iri() {
let mut builder = IriBuilder::default();
let result: IriResult<IRI> = builder
.scheme(&KnownSchemes::Urn.into())
.path_str("oasis:names:specification:docbook:dtd:xml:4.1.2")
.unwrap()
.try_into();
assert!(result.is_ok());
let iri = result.unwrap();
assert_eq!(
iri.to_string(),
"urn:oasis:names:specification:docbook:dtd:xml:4.1.2".to_string()
);
}
}