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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//! A [FieldHandler] may modify a mapped column or expression.
/// Use it to
/// - define your own custom function (through FN)
/// - map the standart filters differently
/// - disallow standart filters
/// - handle fields that do not exist in the struct
/// - handle fields that match multiple columns (full text index)
///
/// ## Example (see full working example in tests)
/// ``` ignore
/// use toql::query::FieldFilter;
/// use toql::table_mapper::FieldHandler;
/// use toql::sql_builder::SqlBuilderError;
/// struct MyHandler {};
///
/// impl FieldHandler for MyHandler {
///     fn build_filter(&self, sql: &SqlExpr, _filter: &FieldFilter)
///     ->Result<Option<SqlExpr>, SqlBuilderError> {
///        --snip--
///     }
/// }
/// let my_handler = MyHandler {};
/// let mapper = TableMapper::new_with_handler(my_handler);
///
use crate::parameter_map::ParameterMap;
use crate::query::field_filter::FieldFilter;
use crate::sql_builder::sql_builder_error::SqlBuilderError;
use crate::sql_expr::{resolver::Resolver, SqlExpr};

pub trait FieldHandler {
    /// Context parameters allow to share information between different handlers
    /// Return sql and params if you want to select it.
    fn build_select(
        &self,
        select: SqlExpr,
        aux_params: &ParameterMap,
    ) -> Result<Option<SqlExpr>, SqlBuilderError> {
        // Replace aux params in select expr. If params are left unresolved, field will not be selected.
        let expr = Resolver::resolve_aux_params(select, aux_params);
        Ok(Some(expr))
    }

    /// Match filter and return SQL expression.
    /// Do not insert parameters in the SQL expression, use `?` instead.
    /// If you miss some arguments, raise an error, typically `SqlBuilderError::FilterInvalid`
    fn build_filter(
        &self,
        select: SqlExpr,
        filter: &FieldFilter,
        aux_params: &ParameterMap,
    ) -> Result<Option<SqlExpr>, SqlBuilderError>;
}

impl std::fmt::Debug for (dyn FieldHandler + std::marker::Send + std::marker::Sync + 'static) {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "FieldHandler()")
    }
}

impl FieldHandler for DefaultFieldHandler {
    fn build_filter(
        &self,
        mut select: SqlExpr,
        filter: &FieldFilter,
        _aux_params: &ParameterMap,
    ) -> Result<Option<SqlExpr>, SqlBuilderError> {
        match filter {
            FieldFilter::Eq(criteria) => {
                select.push_literal(" = ").push_arg(criteria.clone());
                Ok(Some(select))
            }
            FieldFilter::Eqn => {
                select.push_literal(" IS NULL");
                Ok(Some(select))
            }
            FieldFilter::Ne(criteria) => {
                select.push_literal(" <> ").push_arg(criteria.clone());
                Ok(Some(select))
            }
            FieldFilter::Nen => {
                select.push_literal(" IS NOT NULL");
                Ok(Some(select))
            }
            FieldFilter::Ge(criteria) => {
                select.push_literal(" >= ").push_arg(criteria.clone());
                Ok(Some(select))
            }
            FieldFilter::Gt(criteria) => {
                select.push_literal(" > ").push_arg(criteria.clone());
                Ok(Some(select))
            }
            FieldFilter::Le(criteria) => {
                select.push_literal(" <= ").push_arg(criteria.clone());
                Ok(Some(select))
            }
            FieldFilter::Lt(criteria) => {
                select.push_literal(" < ").push_arg(criteria.clone());
                Ok(Some(select))
            }
            FieldFilter::Bw(lower, upper) => {
                select
                    .push_literal(" BETWEEN ")
                    .push_arg(lower.clone())
                    .push_literal(" AND ")
                    .push_arg(upper.clone());
                Ok(Some(select))
            }
            FieldFilter::In(args) => {
                if args.is_empty() {
                    return Ok(None);
                }
                select.push_literal(" IN (");
                for a in args {
                    select.push_arg(a.clone());
                    select.push_literal(", ");
                }
                select.pop(); // remove last ' ,' token
                select.push_literal(")");
                Ok(Some(select))
            }
            FieldFilter::Out(args) => {
                if args.is_empty() {
                    return Ok(None);
                }
                select.push_literal(" NOT IN (");
                for a in args {
                    select.push_arg(a.clone());
                    select.push_literal(", ");
                }
                select.pop(); // remove last ' ,' token
                select.push_literal(")");
                Ok(Some(select))
            }
            //      FieldFilter::Sc(_) => Ok(Some(format!("FIND_IN_SET (?, {})", expression))),
            FieldFilter::Lk(criteria) => {
                select.push_literal(" LIKE ").push_arg(criteria.clone());
                Ok(Some(select))
            }
            FieldFilter::Fn(name, _) => Err(SqlBuilderError::FilterInvalid(name.to_owned())), // Must be implemented by user
        }
    }
}

/// Handles the standart filters as documented in the guide.
/// Returns [FilterInvalid](../sql_builder/enum.SqlBuilderError.html) for any attempt to use FN filters.
#[derive(Debug, Clone)]
pub struct DefaultFieldHandler {}

impl DefaultFieldHandler {
    pub fn new() -> Self {
        Self {}
    }
}

impl Default for DefaultFieldHandler {
    fn default() -> Self {
        Self::new()
    }
}