Skip to main content

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, Default, 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    #[default]
34    Null,
35}
36
37impl From<&str> for Scalar {
38    fn from(value: &str) -> Self {
39        Self::String(value.into())
40    }
41}
42
43impl Display for Scalar {
44    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
45        match self {
46            Self::Float(x) => write!(fmt, "{x}"),
47            Self::String(x) => write!(fmt, "{x}"),
48            Self::Bool(x) => write!(fmt, "{x}"),
49            Self::Null => write!(fmt, ""),
50        }
51    }
52}
53
54#[derive(Clone, Deserialize, Debug, PartialEq, Serialize, TS)]
55#[serde(untagged)]
56pub enum FilterTerm {
57    Array(Vec<Scalar>),
58    Scalar(#[serde(default)] Scalar),
59}
60
61impl<'a, T> From<T> for FilterTerm
62where
63    T: AsRef<[&'a str]>,
64{
65    fn from(value: T) -> Self {
66        Self::Array(value.as_ref().iter().map(|x| (*x).into()).collect())
67    }
68}
69
70impl Default for FilterTerm {
71    fn default() -> Self {
72        Self::Scalar(Scalar::Null)
73    }
74}
75
76impl Display for FilterTerm {
77    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
78        match self {
79            Self::Scalar(x) => {
80                write!(fmt, "{x}")?;
81            },
82            Self::Array(xs) => write!(
83                fmt,
84                "{}",
85                Itertools::intersperse(xs.iter().map(|x| format!("{x}")), ",".to_owned())
86                    .collect::<String>()
87            )?,
88        }
89
90        Ok(())
91    }
92}
93
94impl FilterTerm {
95    pub fn is_null(&self) -> bool {
96        matches!(self, FilterTerm::Scalar(Scalar::Null))
97    }
98}
99
100#[derive(Clone, Deserialize, Debug, PartialEq, Serialize, TS)]
101#[serde()]
102pub struct Filter(String, String, #[serde(default)] FilterTerm);
103
104impl Filter {
105    pub fn new<T>(column: &str, op: &str, term: T) -> Self
106    where
107        FilterTerm: From<T>,
108    {
109        Filter(column.to_string(), op.to_string(), term.into())
110    }
111
112    pub fn column(&self) -> &str {
113        self.0.as_str()
114    }
115
116    pub fn op(&self) -> &str {
117        self.1.as_str()
118    }
119
120    pub fn term(&self) -> &FilterTerm {
121        &self.2
122    }
123
124    pub fn column_mut(&mut self) -> &mut String {
125        &mut self.0
126    }
127
128    pub fn op_mut(&mut self) -> &mut String {
129        &mut self.1
130    }
131
132    pub fn term_mut(&mut self) -> &mut FilterTerm {
133        &mut self.2
134    }
135}
136
137#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq, TS)]
138pub enum FilterReducer {
139    #[default]
140    #[serde(rename = "and")]
141    And,
142    #[serde(rename = "or")]
143    Or,
144}
145
146impl From<Scalar> for proto::Scalar {
147    fn from(value: Scalar) -> Self {
148        match value {
149            Scalar::Float(x) => proto::Scalar {
150                scalar: Some(scalar::Scalar::Float(x)),
151            },
152            Scalar::String(x) => proto::Scalar {
153                scalar: Some(scalar::Scalar::String(x)),
154            },
155            Scalar::Bool(x) => proto::Scalar {
156                scalar: Some(scalar::Scalar::Bool(x)),
157            },
158            Scalar::Null => proto::Scalar {
159                scalar: Some(scalar::Scalar::Null(0)),
160            },
161        }
162    }
163}
164
165impl From<proto::Scalar> for Scalar {
166    fn from(value: proto::Scalar) -> Self {
167        match value.scalar {
168            Some(scalar::Scalar::Bool(x)) => Scalar::Bool(x),
169            Some(scalar::Scalar::String(x)) => Scalar::String(x),
170            Some(scalar::Scalar::Float(x)) => Scalar::Float(x),
171            Some(scalar::Scalar::Null(_)) => Scalar::Null,
172            None => Scalar::Null,
173        }
174    }
175}
176
177impl From<Filter> for proto::view_config::Filter {
178    fn from(value: Filter) -> Self {
179        proto::view_config::Filter {
180            column: value.0,
181            op: value.1,
182            value: match value.2 {
183                FilterTerm::Scalar(x) => vec![x.into()],
184                FilterTerm::Array(x) => x.into_iter().map(|x| x.into()).collect(),
185            },
186        }
187    }
188}
189
190impl From<proto::view_config::Filter> for Filter {
191    fn from(value: proto::view_config::Filter) -> Self {
192        Filter(
193            value.column,
194            value.op,
195            if value.value.len() == 1 {
196                FilterTerm::Scalar(value.value.into_iter().next().unwrap().into())
197            } else {
198                FilterTerm::Array(value.value.into_iter().map(|x| x.into()).collect())
199            },
200        )
201    }
202}