1use serde::Serialize;
2use serde::ser::Serializer;
3use serde_json::Value;
4
5use crate::scanner::field::Column;
6
7pub trait IntoFilterValue {
8 fn into_filter_value(self) -> Value;
9}
10
11macro_rules! impl_numeric_filter_value {
12 ($($ty:ty),+ $(,)?) => {
13 $(
14 impl IntoFilterValue for $ty {
15 fn into_filter_value(self) -> Value {
16 Value::from(self)
17 }
18 }
19 )+
20 };
21}
22
23impl_numeric_filter_value!(i8, i16, i32, i64, isize, u8, u16, u32, u64);
24
25impl IntoFilterValue for f32 {
26 fn into_filter_value(self) -> Value {
27 Value::from(self as f64)
28 }
29}
30
31impl IntoFilterValue for f64 {
32 fn into_filter_value(self) -> Value {
33 Value::from(self)
34 }
35}
36
37impl IntoFilterValue for bool {
38 fn into_filter_value(self) -> Value {
39 Value::from(self)
40 }
41}
42
43impl IntoFilterValue for Value {
44 fn into_filter_value(self) -> Value {
45 self
46 }
47}
48
49impl IntoFilterValue for String {
50 fn into_filter_value(self) -> Value {
51 Value::from(self)
52 }
53}
54
55impl IntoFilterValue for &str {
56 fn into_filter_value(self) -> Value {
57 Value::from(self)
58 }
59}
60
61impl IntoFilterValue for Column {
62 fn into_filter_value(self) -> Value {
63 Value::from(self.as_str().to_owned())
64 }
65}
66
67impl IntoFilterValue for &Column {
68 fn into_filter_value(self) -> Value {
69 Value::from(self.as_str().to_owned())
70 }
71}
72
73impl<T> IntoFilterValue for Option<T>
74where
75 T: IntoFilterValue,
76{
77 fn into_filter_value(self) -> Value {
78 self.map(IntoFilterValue::into_filter_value)
79 .unwrap_or(Value::Null)
80 }
81}
82
83impl<T> IntoFilterValue for Vec<T>
84where
85 T: IntoFilterValue,
86{
87 fn into_filter_value(self) -> Value {
88 Value::Array(
89 self.into_iter()
90 .map(IntoFilterValue::into_filter_value)
91 .collect(),
92 )
93 }
94}
95
96impl<T, const N: usize> IntoFilterValue for [T; N]
97where
98 T: IntoFilterValue,
99{
100 fn into_filter_value(self) -> Value {
101 Value::Array(
102 self.into_iter()
103 .map(IntoFilterValue::into_filter_value)
104 .collect(),
105 )
106 }
107}
108
109#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
110pub enum FilterOperator {
111 #[serde(rename = "greater")]
112 Greater,
113 #[serde(rename = "egreater")]
114 EGreater,
115 #[serde(rename = "less")]
116 Less,
117 #[serde(rename = "eless")]
118 ELess,
119 #[serde(rename = "equal")]
120 Equal,
121 #[serde(rename = "nequal")]
122 NotEqual,
123 #[serde(rename = "in_range")]
124 InRange,
125 #[serde(rename = "not_in_range")]
126 NotInRange,
127 #[serde(rename = "empty")]
128 Empty,
129 #[serde(rename = "nempty")]
130 NotEmpty,
131 #[serde(rename = "crosses")]
132 Crosses,
133 #[serde(rename = "crosses_above")]
134 CrossesAbove,
135 #[serde(rename = "crosses_below")]
136 CrossesBelow,
137 #[serde(rename = "match")]
138 Match,
139 #[serde(rename = "nmatch")]
140 NotMatch,
141 #[serde(rename = "has")]
142 Has,
143 #[serde(rename = "has_none_of")]
144 HasNoneOf,
145 #[serde(rename = "above%")]
146 AbovePercent,
147 #[serde(rename = "below%")]
148 BelowPercent,
149 #[serde(rename = "in_range%")]
150 InRangePercent,
151 #[serde(rename = "not_in_range%")]
152 NotInRangePercent,
153 #[serde(rename = "in_day_range")]
154 InDayRange,
155 #[serde(rename = "in_week_range")]
156 InWeekRange,
157 #[serde(rename = "in_month_range")]
158 InMonthRange,
159}
160
161#[derive(Debug, Clone, PartialEq, Serialize)]
162pub struct FilterCondition {
163 pub left: Column,
164 pub operation: FilterOperator,
165 pub right: Value,
166}
167
168impl FilterCondition {
169 pub fn new(left: Column, operation: FilterOperator, right: Value) -> Self {
170 Self {
171 left,
172 operation,
173 right,
174 }
175 }
176}
177
178#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
179pub enum LogicalOperator {
180 #[serde(rename = "and")]
181 And,
182 #[serde(rename = "or")]
183 Or,
184}
185
186#[derive(Debug, Clone, PartialEq, Serialize)]
187pub struct FilterTree {
188 pub operator: LogicalOperator,
189 pub operands: Vec<FilterOperand>,
190}
191
192impl FilterTree {
193 pub fn and<I, O>(operands: I) -> Self
194 where
195 I: IntoIterator<Item = O>,
196 O: Into<FilterOperand>,
197 {
198 Self {
199 operator: LogicalOperator::And,
200 operands: operands.into_iter().map(Into::into).collect(),
201 }
202 }
203
204 pub fn or<I, O>(operands: I) -> Self
205 where
206 I: IntoIterator<Item = O>,
207 O: Into<FilterOperand>,
208 {
209 Self {
210 operator: LogicalOperator::Or,
211 operands: operands.into_iter().map(Into::into).collect(),
212 }
213 }
214}
215
216#[derive(Debug, Clone, PartialEq)]
217pub enum FilterOperand {
218 Expression(FilterExpressionOperand),
219 Operation(FilterOperationOperand),
220}
221
222impl Serialize for FilterOperand {
223 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
224 where
225 S: Serializer,
226 {
227 match self {
228 Self::Expression(expression) => expression.serialize(serializer),
229 Self::Operation(operation) => operation.serialize(serializer),
230 }
231 }
232}
233
234impl From<FilterCondition> for FilterOperand {
235 fn from(value: FilterCondition) -> Self {
236 Self::Expression(FilterExpressionOperand { expression: value })
237 }
238}
239
240impl From<FilterTree> for FilterOperand {
241 fn from(value: FilterTree) -> Self {
242 Self::Operation(FilterOperationOperand { operation: value })
243 }
244}
245
246#[derive(Debug, Clone, PartialEq, Serialize)]
247pub struct FilterExpressionOperand {
248 pub expression: FilterCondition,
249}
250
251#[derive(Debug, Clone, PartialEq, Serialize)]
252pub struct FilterOperationOperand {
253 pub operation: FilterTree,
254}
255
256#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
257pub enum SortOrder {
258 #[serde(rename = "asc")]
259 Asc,
260 #[serde(rename = "desc")]
261 Desc,
262}
263
264#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
265pub struct SortSpec {
266 #[serde(rename = "sortBy")]
267 pub sort_by: Column,
268 #[serde(rename = "sortOrder")]
269 pub sort_order: SortOrder,
270 #[serde(skip_serializing_if = "Option::is_none", rename = "nullsFirst")]
271 pub nulls_first: Option<bool>,
272}
273
274impl SortSpec {
275 pub fn new(sort_by: Column, sort_order: SortOrder) -> Self {
276 Self {
277 sort_by,
278 sort_order,
279 nulls_first: None,
280 }
281 }
282
283 pub fn nulls_first(mut self, nulls_first: bool) -> Self {
284 self.nulls_first = Some(nulls_first);
285 self
286 }
287}
288
289#[cfg(test)]
290mod tests {
291 use serde_json::json;
292
293 use super::*;
294
295 #[test]
296 fn serializes_nested_filter_tree_like_tradingview() {
297 let tree = FilterTree::and(vec![
298 FilterOperand::from(Column::from_static("close").ge(200)),
299 FilterOperand::from(FilterTree::or(vec![
300 FilterOperand::from(Column::from_static("RSI").lt(60)),
301 FilterOperand::from(Column::from_static("market_cap_basic").gt(1_000_000_000_u64)),
302 ])),
303 ]);
304
305 let value = serde_json::to_value(tree).unwrap();
306 assert_eq!(
307 value,
308 json!({
309 "operator": "and",
310 "operands": [
311 { "expression": { "left": "close", "operation": "egreater", "right": 200 } },
312 {
313 "operation": {
314 "operator": "or",
315 "operands": [
316 { "expression": { "left": "RSI", "operation": "less", "right": 60 } },
317 { "expression": { "left": "market_cap_basic", "operation": "greater", "right": 1_000_000_000_u64 } }
318 ]
319 }
320 }
321 ]
322 })
323 );
324 }
325}