rttp_client 0.1.0

Rust http client lib
Documentation
use std::fmt;
use std::time::SystemTime;

use crate::error;

#[derive(Clone)]
pub struct Cookie {
  name: String,
  value: String,
  expires: Option<SystemTime>,
  path: Option<String>,
  domain: Option<String>,
  secure: bool,
  http_only: bool,
  persistent: bool,
  host_only: bool,
  same_site: Option<String>,
}

impl Cookie {
  pub fn name(&self) -> &String { &self.name }
  pub fn value(&self) -> &String { &self.value }
  pub fn expires(&self) -> &Option<SystemTime> { &self.expires }
  pub fn path(&self) -> &Option<String> { &self.path }
  pub fn domain(&self) -> &Option<String> { &self.domain }
  pub fn secure(&self) -> bool { self.secure }
  pub fn http_only(&self) -> bool { self.http_only }
  pub fn persistent(&self) -> bool { self.persistent }
  pub fn host_only(&self) -> bool { self.host_only }
  pub fn same_site(&self) -> &Option<String> { &self.same_site }

  pub fn string(&self) -> String {
    let mut text = format!(
      "{}={}",
      self.name,
      self.value,
    );
    if let Some(path) = &self.path {
      text.push_str(&format!("; path={}", path));
    }
    if let Some(domain) = &self.domain {
      text.push_str(&format!("; domain={}", domain));
    }
    if self.persistent {
      if let Some(expires) = self.expires {
        let http_date = httpdate::fmt_http_date(expires);
        text.push_str(&format!("; expires={}", http_date));
      } else {
        text.push_str("; max-age=0")
      }
    }
    if self.secure {
      text.push_str("; secure")
    }
    if self.http_only {
      text.push_str("; httpOnly")
    }
    if self.host_only {
      text.push_str("; hostOnly")
    }
    if let Some(same_site) = &self.same_site {
      text.push_str(&format!("; SameSite={}", same_site));
    }
    text
  }
}

impl Cookie {
  pub fn builder() -> CookieBuilder {
    CookieBuilder::new()
  }

  pub fn parse<S: AsRef<str>>(text: S) -> error::Result<Self> {
    let mut builder = Cookie::builder();
    let parts: Vec<&str> = text.as_ref().split(";").collect();
    for item in parts {
      let nvs: Vec<&str> = item.split("=").collect();
      let name = nvs.get(0).ok_or(error::bad_cookie("Cookie not have name"))?.trim();
      let value: String = nvs.iter().enumerate()
        .filter(|(ix, _)| *ix > 0)
        .map(|(_, v)| *v)
        .collect::<Vec<&str>>()
        .join("=");
      let value = value.trim();
      match name {
        "expires" => {
          let value = value.replace("-", " ");
          match httpdate::parse_http_date(&value[..]) {
            Ok(v) => {
              builder.expires(v);
            }
            Err(e) => eprintln!("=> {:?}", e)
          }
        }
        "path" => {
          builder.path(value);
        }
        "domain" => {
          builder.domain(value);
        }
        "secure" => {
          if value.is_empty() {
            builder.secure(true);
          } else {
            builder.secure(value.parse().map_err(|_| error::bad_cookie("Cookie secure can not parse to bool"))?);
          }
        }
        "http_only" | "httponly" | "httpOnly" => {
          if value.is_empty() {
            builder.http_only(true);
          } else {
            builder.http_only(value.parse().map_err(|_| error::bad_cookie("Cookie httpOnly can not parse to bool"))?);
          }
        }
        "host_only" | "hostonly" | "hostOnly" => {
          if value.is_empty() {
            builder.host_only(true);
          } else {
            builder.host_only(value.parse().map_err(|_| error::bad_cookie("Cookie hostOnly can not parse to bool"))?);
          }
        }
        "same_site" | "SameSite" | "samSite" => {
          builder.same_site(value);
        }
        _ => {
          builder.name(name);
          builder.value(value);
        }
      }
    }
    Ok(builder.build())
  }
}


impl fmt::Debug for Cookie {
  #[inline]
  fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
    fmt::Debug::fmt(&self.string(), formatter)
  }
}

impl fmt::Display for Cookie {
  #[inline]
  fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
    fmt::Debug::fmt(&self.string(), formatter)
  }
}


pub trait ToCookie {
  fn to_cookie(&self) -> error::Result<Cookie>;
}

impl ToCookie for Cookie {
  fn to_cookie(&self) -> error::Result<Cookie> {
    Ok(self.clone())
  }
}

impl ToCookie for String {
  fn to_cookie(&self) -> error::Result<Cookie> {
    (&self[..]).to_cookie()
  }
}

impl ToCookie for &str {
  fn to_cookie(&self) -> error::Result<Cookie> {
    Cookie::parse(self)
  }
}


#[derive(Clone)]
pub struct CookieBuilder {
  cookie: Cookie
}

impl CookieBuilder {
  pub fn new() -> Self {
    Self {
      cookie: Cookie {
        name: "".to_string(),
        value: "".to_string(),
        expires: None,
        path: None,
        domain: None,
        secure: false,
        http_only: false,
        persistent: false,
        host_only: false,
        same_site: None,
      }
    }
  }

  pub fn build(&self) -> Cookie {
    self.cookie.clone()
  }

  pub fn same_site<S: AsRef<str>>(&mut self, same_site: S) -> &mut Self {
    self.cookie.same_site = Some(same_site.as_ref().to_owned());
    self
  }
  pub fn name<S: AsRef<str>>(&mut self, name: S) -> &mut Self {
    self.cookie.name = name.as_ref().to_owned();
    self
  }
  pub fn value<S: AsRef<str>>(&mut self, value: S) -> &mut Self {
    self.cookie.value = value.as_ref().to_owned();
    self
  }
  pub fn expires(&mut self, expires: SystemTime) -> &mut Self {
    self.cookie.expires = Some(expires);
    self.cookie.persistent = true;
    self
  }
  pub fn path<S: AsRef<str>>(&mut self, path: S) -> &mut Self {
    self.cookie.path = Some(path.as_ref().to_owned());
    self
  }
  pub fn domain<S: AsRef<str>>(&mut self, domain: S) -> &mut Self {
    self.cookie.domain = Some(domain.as_ref().to_owned());
    self
  }
  pub fn secure(&mut self, secure: bool) -> &mut Self {
    self.cookie.secure = secure;
    self
  }
  pub fn http_only(&mut self, http_only: bool) -> &mut Self {
    self.cookie.http_only = http_only;
    self
  }
  pub fn host_only(&mut self, host_only: bool) -> &mut Self {
    self.cookie.host_only = host_only;
    self
  }
}

impl AsRef<Cookie> for Cookie {
  fn as_ref(&self) -> &Cookie {
    self
  }
}

impl AsRef<Cookie> for CookieBuilder {
  fn as_ref(&self) -> &Cookie {
    &self.cookie
  }
}