1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use itertools::Itertools;
use std::fmt::{self, Display};

/// SQL statment with boolean logic.
pub struct PredicateStatement<'s> {
    statement: &'static str,
    predicates: &'s [Box<dyn Display>],
}

impl fmt::Display for PredicateStatement<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{} {}",
            self.statement,
            self.predicates.iter().join("\nAND ")
        )
    }
}

/// Collection of boolean predicates.
pub struct Predicates(Vec<Box<dyn Display>>);

impl IntoIterator for Predicates {
    type Item = Box<dyn Display>;
    type IntoIter = std::vec::IntoIter<Box<dyn Display>>;

    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}

impl Predicates {
    /// Create empty collection.
    pub fn new() -> Predicates {
        Predicates(Vec::new())
    }

    /// Creates collection containing given predicate.
    pub fn from<S: Display + 'static>(predicate: S) -> Predicates {
        Predicates::new().and(predicate)
    }

    /// Crates collection containing given predicates.
    pub fn from_all<S, I, IT>(predicates: I) -> Self
    where
        S: Display + 'static,
        I: IntoIterator<Item = S, IntoIter = IT>,
        IT: Iterator<Item = S>,
    {
        Predicates::new().and_all(predicates)
    }

    /// Gets WHERE statement with predicates.
    pub fn as_where<'s>(&'s self) -> PredicateStatement<'s> {
        PredicateStatement {
            statement: "WHERE",
            predicates: &self.0,
        }
    }

    /// Appends predicate.
    pub fn and_push<S: Display + 'static>(&mut self, predicate: S) {
        self.and_extend(Some(predicate))
    }

    /// Appends all predicates.
    pub fn and_extend<S, I, IT>(&mut self, predicates: I) -> ()
    where
        S: Display + 'static,
        I: IntoIterator<Item = S, IntoIter = IT>,
        IT: Iterator<Item = S>,
    {
        self.0.extend(
            predicates
                .into_iter()
                .map(|c| Box::new(c) as Box<dyn Display>),
        );
    }

    /// Appends predicate with fluid API.
    pub fn and<S: Display + 'static>(mut self, predicate: S) -> Self {
        self.and_push(predicate);
        self
    }

    /// Appends all predicates with fluid API.
    pub fn and_all<S, I, IT>(mut self, predicates: I) -> Self
    where
        S: Display + 'static,
        I: IntoIterator<Item = S, IntoIter = IT>,
        IT: Iterator<Item = S>,
    {
        self.and_extend(predicates);
        self
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        assert_eq!(
            Predicates::from("foo = 'bar'")
                .and("baz")
                .and_all(["hello", "world"].iter())
                .and_all(Predicates::from_all(["abc", "123"].iter()))
                .as_where()
                .to_string(),
            "WHERE foo = \'bar\'\nAND baz\nAND hello\nAND world\nAND abc\nAND 123"
        );
    }
}