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
99impl FilterTerm {
100    pub fn is_null(&self) -> bool {
101        matches!(self, FilterTerm::Scalar(Scalar::Null))
102    }
103}
104
105#[derive(Clone, Deserialize, Debug, PartialEq, Serialize, TS)]
106#[serde()]
107pub struct Filter(String, String, #[serde(default)] FilterTerm);
108
109impl Filter {
110    pub fn new<T>(column: &str, op: &str, term: T) -> Self
111    where
112        FilterTerm: From<T>,
113    {
114        Filter(column.to_string(), op.to_string(), term.into())
115    }
116
117    pub fn column(&self) -> &str {
118        self.0.as_str()
119    }
120
121    pub fn op(&self) -> &str {
122        self.1.as_str()
123    }
124
125    pub fn term(&self) -> &FilterTerm {
126        &self.2
127    }
128
129    pub fn column_mut(&mut self) -> &mut String {
130        &mut self.0
131    }
132
133    pub fn op_mut(&mut self) -> &mut String {
134        &mut self.1
135    }
136
137    pub fn term_mut(&mut self) -> &mut FilterTerm {
138        &mut self.2
139    }
140}
141
142#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, TS)]
143pub enum FilterReducer {
144    #[serde(rename = "and")]
145    And,
146    #[serde(rename = "or")]
147    Or,
148}
149
150impl Default for FilterReducer {
151    fn default() -> Self {
152        Self::And
153    }
154}
155
156impl From<Scalar> for proto::Scalar {
157    fn from(value: Scalar) -> Self {
158        match value {
159            Scalar::Float(x) => proto::Scalar {
160                scalar: Some(scalar::Scalar::Float(x)),
161            },
162            Scalar::String(x) => proto::Scalar {
163                scalar: Some(scalar::Scalar::String(x)),
164            },
165            Scalar::Bool(x) => proto::Scalar {
166                scalar: Some(scalar::Scalar::Bool(x)),
167            },
168            Scalar::Null => proto::Scalar {
169                scalar: Some(scalar::Scalar::Null(0)),
170            },
171        }
172    }
173}
174
175impl From<proto::Scalar> for Scalar {
176    fn from(value: proto::Scalar) -> Self {
177        match value.scalar {
178            Some(scalar::Scalar::Bool(x)) => Scalar::Bool(x),
179            Some(scalar::Scalar::String(x)) => Scalar::String(x),
180            Some(scalar::Scalar::Float(x)) => Scalar::Float(x),
181            Some(scalar::Scalar::Null(_)) => Scalar::Null,
182            None => Scalar::Null,
183        }
184    }
185}
186
187impl From<Filter> for proto::view_config::Filter {
188    fn from(value: Filter) -> Self {
189        proto::view_config::Filter {
190            column: value.0,
191            op: value.1,
192            value: match value.2 {
193                FilterTerm::Scalar(x) => vec![x.into()],
194                FilterTerm::Array(x) => x.into_iter().map(|x| x.into()).collect(),
195            },
196        }
197    }
198}
199
200impl From<proto::view_config::Filter> for Filter {
201    fn from(value: proto::view_config::Filter) -> Self {
202        Filter(
203            value.column,
204            value.op,
205            if value.value.len() == 1 {
206                FilterTerm::Scalar(value.value.into_iter().next().unwrap().into())
207            } else {
208                FilterTerm::Array(value.value.into_iter().map(|x| x.into()).collect())
209            },
210        )
211    }
212}