perspective_client/config/
filters.rs

1// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
3// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
4// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
5// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
6// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7// ┃ Copyright (c) 2017, the Perspective Authors.                              ┃
8// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9// ┃ This file is part of the Perspective library, distributed under the terms ┃
10// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
13use std::fmt::Display;
14
15use itertools::Itertools;
16use serde::{Deserialize, Serialize};
17use ts_rs::TS;
18
19use crate::proto;
20use crate::proto::scalar;
21
22/// This type represents the ViewConfig serializable type, which must be JSON
23/// safe.
24#[derive(Clone, Deserialize, Debug, PartialEq, Serialize, TS)]
25#[serde(untagged)]
26pub enum Scalar {
27    Float(f64),
28    String(String),
29    Bool(bool),
30    // DateTime(i64),
31    // Date(String),
32    // Int(i32),
33    Null,
34}
35
36impl From<&str> for Scalar {
37    fn from(value: &str) -> Self {
38        Self::String(value.into())
39    }
40}
41
42impl Default for Scalar {
43    fn default() -> Self {
44        Self::Null
45    }
46}
47
48impl Display for Scalar {
49    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
50        match self {
51            Self::Float(x) => write!(fmt, "{}", x),
52            Self::String(x) => write!(fmt, "{}", x),
53            Self::Bool(x) => write!(fmt, "{}", x),
54            Self::Null => write!(fmt, ""),
55        }
56    }
57}
58
59#[derive(Clone, Deserialize, Debug, PartialEq, Serialize, TS)]
60#[serde(untagged)]
61pub enum FilterTerm {
62    Array(Vec<Scalar>),
63    Scalar(#[serde(default)] Scalar),
64}
65
66impl<'a, T> From<T> for FilterTerm
67where
68    T: AsRef<[&'a str]>,
69{
70    fn from(value: T) -> Self {
71        Self::Array(value.as_ref().iter().map(|x| (*x).into()).collect())
72    }
73}
74
75impl Default for FilterTerm {
76    fn default() -> Self {
77        Self::Scalar(Scalar::Null)
78    }
79}
80
81impl Display for FilterTerm {
82    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
83        match self {
84            Self::Scalar(x) => {
85                write!(fmt, "{}", x)?;
86            },
87            Self::Array(xs) => write!(
88                fmt,
89                "{}",
90                Itertools::intersperse(xs.iter().map(|x| format!("{}", x)), ",".to_owned())
91                    .collect::<String>()
92            )?,
93        }
94
95        Ok(())
96    }
97}
98
99#[derive(Clone, Deserialize, Debug, PartialEq, Serialize, TS)]
100#[serde()]
101pub struct Filter(String, String, #[serde(default)] FilterTerm);
102
103impl Filter {
104    pub fn new<T>(column: &str, op: &str, term: T) -> Self
105    where
106        FilterTerm: From<T>,
107    {
108        Filter(column.to_string(), op.to_string(), term.into())
109    }
110
111    pub fn column(&self) -> &str {
112        self.0.as_str()
113    }
114
115    pub fn op(&self) -> &str {
116        self.1.as_str()
117    }
118
119    pub fn term(&self) -> &FilterTerm {
120        &self.2
121    }
122
123    pub fn column_mut(&mut self) -> &mut String {
124        &mut self.0
125    }
126
127    pub fn op_mut(&mut self) -> &mut String {
128        &mut self.1
129    }
130
131    pub fn term_mut(&mut self) -> &mut FilterTerm {
132        &mut self.2
133    }
134}
135
136#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, TS)]
137pub enum FilterReducer {
138    #[serde(rename = "and")]
139    And,
140    #[serde(rename = "or")]
141    Or,
142}
143
144impl Default for FilterReducer {
145    fn default() -> Self {
146        Self::And
147    }
148}
149
150impl From<Scalar> for proto::Scalar {
151    fn from(value: Scalar) -> Self {
152        match value {
153            Scalar::Float(x) => proto::Scalar {
154                scalar: Some(scalar::Scalar::Float(x)),
155            },
156            Scalar::String(x) => proto::Scalar {
157                scalar: Some(scalar::Scalar::String(x)),
158            },
159            Scalar::Bool(x) => proto::Scalar {
160                scalar: Some(scalar::Scalar::Bool(x)),
161            },
162            Scalar::Null => proto::Scalar {
163                scalar: Some(scalar::Scalar::Null(0)),
164            },
165        }
166    }
167}
168
169impl From<proto::Scalar> for Scalar {
170    fn from(value: proto::Scalar) -> Self {
171        match value.scalar {
172            Some(scalar::Scalar::Bool(x)) => Scalar::Bool(x),
173            Some(scalar::Scalar::String(x)) => Scalar::String(x),
174            Some(scalar::Scalar::Float(x)) => Scalar::Float(x),
175            Some(scalar::Scalar::Null(_)) => Scalar::Null,
176            None => Scalar::Null,
177        }
178    }
179}
180
181impl From<Filter> for proto::view_config::Filter {
182    fn from(value: Filter) -> Self {
183        proto::view_config::Filter {
184            column: value.0,
185            op: value.1,
186            value: match value.2 {
187                FilterTerm::Scalar(x) => vec![x.into()],
188                FilterTerm::Array(x) => x.into_iter().map(|x| x.into()).collect(),
189            },
190        }
191    }
192}
193
194impl From<proto::view_config::Filter> for Filter {
195    fn from(value: proto::view_config::Filter) -> Self {
196        Filter(
197            value.column,
198            value.op,
199            if value.value.len() == 1 {
200                FilterTerm::Scalar(value.value.into_iter().next().unwrap().into())
201            } else {
202                FilterTerm::Array(value.value.into_iter().map(|x| x.into()).collect())
203            },
204        )
205    }
206}