ldapquery 0.0.6

A builder for ldap queries
Documentation
use std::ops::{BitAnd, BitOr};
use std::vec;
use crate::{combine_query, LdapValue};



pub trait ToLdapFilter {
  fn to_ldap_filter(&self) -> String;
}

#[derive(Debug, Clone)]
pub struct Condition<'a, V: LdapValue = &'a str> {
  attribute: &'a str,
  value: Option<V>,
  not: bool,
}

impl<'a, V: LdapValue> Condition<'a, V> {
  pub fn new(attribute: &str) -> Condition<V> {
    Condition {
      attribute,
      value: None,
      not: false
    }
  }

  pub fn value(mut self, value: V) -> Condition<'a, V> {
    self.value = Some(value);
    self.clone()
  }

  pub fn not(mut self) -> Condition<'a, V> {
    self.not = !self.not;
    self.clone()
  }
}

impl<'a, V: LdapValue> From<&Condition<'a, V>> for Group<'a, V> {
  fn from(value: &Condition<'a, V>) -> Self {
    Group {
      t: ConditionType::Single,
      conditions: vec![GroupWrapper::Condition(value.clone())],
      not: false
    }
  }
}

impl<'a, V: LdapValue> BitAnd for Condition<'a, V> {
  type Output = Group<'a, V>;

  fn bitand(self, rhs: Self) -> Self::Output {
    Group::to_group(self, rhs, ConditionType::And)
  }
}

impl<'a, V: LdapValue> BitAnd for &Condition<'a, V> {
  type Output = Group<'a, V>;

  fn bitand(self, rhs: Self) -> Self::Output {
    self.clone() & rhs.clone()
  }
}

impl<'a, V: LdapValue> BitOr for Condition<'a, V> {
  type Output = Group<'a, V>;

  fn bitor(self, rhs: Self) -> Self::Output {
    Group::to_group(self, rhs, ConditionType::Or)
  }
}

impl<'a, V: LdapValue> BitOr for &Condition<'a, V> {
  type Output = Group<'a, V>;

  fn bitor(self, rhs: Self) -> Self::Output {
    self.clone() | rhs.clone()
  }
}

impl<'a, V: LdapValue> BitOr<&Group<'a, V>> for &Condition<'a, V> {
  type Output = Group<'a, V>;

  fn bitor(self, rhs: &Group<'a, V>) -> Self::Output {
    rhs.clone().add_to_group(self.clone().clone(), ConditionType::Or)
  }
}

impl<'a, V: LdapValue> BitOr<&Condition<'a, V>> for &Group<'a, V> {
  type Output = Group<'a, V>;

  fn bitor(self, rhs: &Condition<'a, V>) -> Self::Output {
    self.clone().add_to_group(rhs.clone().clone(), ConditionType::Or)
  }
}

impl<'a, V: LdapValue> BitAnd<&Group<'a, V>> for &Condition<'a, V> {
  type Output = Group<'a, V>;

  fn bitand(self, rhs: &Group<'a, V>) -> Self::Output {
    rhs.clone().add_to_group(self.clone().clone(), ConditionType::And)
  }
}

impl<'a, V: LdapValue> BitAnd<&Condition<'a, V>> for &Group<'a, V> {
  type Output = Group<'a, V>;

  fn bitand(self, rhs: &Condition<'a, V>) -> Self::Output {
    self.clone().add_to_group(rhs.clone().clone(), ConditionType::And)
  }
}

combine_query!{BitOr, bitor, ConditionType::Or, Condition, Group}
combine_query!{BitOr, bitor, ConditionType::Or, Group, Condition}
combine_query!{BitAnd, bitand, ConditionType::And, Condition, Group}
combine_query!{BitAnd, bitand, ConditionType::And, Group, Condition}


impl<V: LdapValue> ToLdapFilter for &Condition<'_, V> {
  fn to_ldap_filter(&self) -> String {
    let inner_query = format!("{}={}", &self.attribute, &self.value.as_ref().map(|v| v.to_string()).unwrap_or(String::from("*")));
    let query = if self.not {
      format!("!({})", inner_query)
    } else {
      inner_query
    };
    format!("({})", query)
  }
}

impl<V: LdapValue> ToLdapFilter for Condition<'_, V> {
  fn to_ldap_filter(&self) -> String {
    (&self).to_ldap_filter()
  }
}

#[derive(Debug, Clone, PartialEq)]
enum ConditionType {
  Single,
  And,
  Or
}

#[derive(Debug, Clone)]
enum GroupWrapper<'a, V: LdapValue> {
  Condition(Condition<'a, V>),
  Group(Group<'a, V>),
}

impl<'a, V: LdapValue> ToLdapFilter for GroupWrapper<'a, V> {
  fn to_ldap_filter(&self) -> String {
    match self {
      GroupWrapper::Condition(c) => c.to_ldap_filter(),
      GroupWrapper::Group(g) => g.to_ldap_filter()
    }
  }
}

impl<'a, V: LdapValue> From<Condition<'a, V>> for GroupWrapper<'a, V> {
  fn from(value: Condition<'a, V>) -> Self {
    GroupWrapper::Condition(value)
  }
}

impl<'a, V: LdapValue> From<&Condition<'a, V>> for GroupWrapper<'a, V> {
  fn from(value: &Condition<'a, V>) -> Self {
    value.clone().into()
  }
}

impl<'a, V: LdapValue> From<Group<'a, V>> for GroupWrapper<'a, V> {
  fn from(value: Group<'a, V>) -> Self {
    GroupWrapper::Group(value)
  }
}

impl<'a, V: LdapValue> From<&Group<'a, V>> for GroupWrapper<'a, V> {
  fn from(value: &Group<'a, V>) -> Self {
    value.clone().into()
  }
}

#[derive(Debug, Clone)]
pub struct Group<'a, V: LdapValue> {
  t: ConditionType,
  conditions: Vec<GroupWrapper<'a, V>>,
  not: bool,
}

impl<'a, V: LdapValue> Group<'a, V> {

  pub fn not(mut self) -> Self {
    self.not = !self.not;
    self
  }

  fn to_group<L: Into<GroupWrapper<'a, V>>, R: Into<GroupWrapper<'a, V>>>(lhs: L, rhs: R, op: ConditionType) -> Self {
    Group {
      t: op,
      conditions: vec![lhs.into(), rhs.into()],
      not: false
    }
  }

  fn add_to_group(&mut self, rhs: Condition<'a, V>, op: ConditionType) -> Group<'a, V> {
    if self.t == op {
      self.conditions.push(rhs.into());
      self.clone()
    } else {
      Group {
        t: op,
        conditions: vec![self.clone().into(), rhs.into()],
        not: false
      }
    }
  }
}

impl<'a, V: LdapValue> BitAnd for Group<'a, V> {
  type Output = Group<'a, V>;

  fn bitand(self, rhs: Self) -> Self::Output {
    Group::to_group(&self, &rhs, ConditionType::And)
  }
}

impl<'a, V: LdapValue> BitOr for Group<'a, V> {
  type Output = Group<'a, V>;

  fn bitor(self, rhs: Self) -> Self::Output {
    Group::to_group(&self, &rhs, ConditionType::Or)
  }
}

impl<V: LdapValue> ToLdapFilter for &Group<'_, V> {
  fn to_ldap_filter(&self) -> String {
    let query = if self.conditions.len() == 0 {
      String::from("()")
    } else if self.conditions.len() == 1 {
      self.conditions[0].to_ldap_filter()
    } else {
      let conditions = self.conditions.iter().fold(String::new(), |str, b| format!("{}{}", str, b.to_ldap_filter()));
      match self.t {
        ConditionType::Single => conditions,
        ConditionType::And => format!("(&{})", conditions),
        ConditionType::Or => format!("(|{})", conditions)
      }
    };

    if self.not {
      format!("(!{})", query)
    } else {
      query
    }
  }
}

impl<V: LdapValue> ToLdapFilter for Group<'_, V> {
  fn to_ldap_filter(&self) -> String {
    (&self).to_ldap_filter()
  }
}