1use std::borrow::Cow;
2use std::fmt;
3
4use serde::{Serialize, Serializer};
5
6use crate::scanner::filter::{
7 FilterCondition, FilterOperator, IntoFilterValue, SortOrder, SortSpec,
8};
9
10#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
11pub struct Column(Cow<'static, str>);
12
13impl Column {
14 pub const fn from_static(name: &'static str) -> Self {
15 Self(Cow::Borrowed(name))
16 }
17
18 pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
19 Self(name.into())
20 }
21
22 pub fn as_str(&self) -> &str {
23 self.0.as_ref()
24 }
25
26 pub fn with_interval(&self, interval: &str) -> Self {
27 Self::new(format!("{}|{interval}", self.as_str()))
28 }
29
30 pub fn with_history(&self, periods: u16) -> Self {
31 Self::new(format!("{}[{periods}]", self.as_str()))
32 }
33
34 pub fn recommendation(&self) -> Self {
35 Self::new(format!("Rec.{}", self.as_str()))
36 }
37
38 pub fn gt(self, value: impl IntoFilterValue) -> FilterCondition {
39 FilterCondition::new(self, FilterOperator::Greater, value.into_filter_value())
40 }
41
42 pub fn ge(self, value: impl IntoFilterValue) -> FilterCondition {
43 FilterCondition::new(self, FilterOperator::EGreater, value.into_filter_value())
44 }
45
46 pub fn lt(self, value: impl IntoFilterValue) -> FilterCondition {
47 FilterCondition::new(self, FilterOperator::Less, value.into_filter_value())
48 }
49
50 pub fn le(self, value: impl IntoFilterValue) -> FilterCondition {
51 FilterCondition::new(self, FilterOperator::ELess, value.into_filter_value())
52 }
53
54 pub fn eq(self, value: impl IntoFilterValue) -> FilterCondition {
55 FilterCondition::new(self, FilterOperator::Equal, value.into_filter_value())
56 }
57
58 pub fn ne(self, value: impl IntoFilterValue) -> FilterCondition {
59 FilterCondition::new(self, FilterOperator::NotEqual, value.into_filter_value())
60 }
61
62 pub fn between(
63 self,
64 lower: impl IntoFilterValue,
65 upper: impl IntoFilterValue,
66 ) -> FilterCondition {
67 FilterCondition::new(
68 self,
69 FilterOperator::InRange,
70 vec![lower.into_filter_value(), upper.into_filter_value()].into_filter_value(),
71 )
72 }
73
74 pub fn not_between(
75 self,
76 lower: impl IntoFilterValue,
77 upper: impl IntoFilterValue,
78 ) -> FilterCondition {
79 FilterCondition::new(
80 self,
81 FilterOperator::NotInRange,
82 vec![lower.into_filter_value(), upper.into_filter_value()].into_filter_value(),
83 )
84 }
85
86 pub fn isin<I, V>(self, values: I) -> FilterCondition
87 where
88 I: IntoIterator<Item = V>,
89 V: IntoFilterValue,
90 {
91 FilterCondition::new(
92 self,
93 FilterOperator::InRange,
94 values
95 .into_iter()
96 .map(IntoFilterValue::into_filter_value)
97 .collect::<Vec<_>>()
98 .into_filter_value(),
99 )
100 }
101
102 pub fn not_in<I, V>(self, values: I) -> FilterCondition
103 where
104 I: IntoIterator<Item = V>,
105 V: IntoFilterValue,
106 {
107 FilterCondition::new(
108 self,
109 FilterOperator::NotInRange,
110 values
111 .into_iter()
112 .map(IntoFilterValue::into_filter_value)
113 .collect::<Vec<_>>()
114 .into_filter_value(),
115 )
116 }
117
118 pub fn crosses(self, value: impl IntoFilterValue) -> FilterCondition {
119 FilterCondition::new(self, FilterOperator::Crosses, value.into_filter_value())
120 }
121
122 pub fn crosses_above(self, value: impl IntoFilterValue) -> FilterCondition {
123 FilterCondition::new(
124 self,
125 FilterOperator::CrossesAbove,
126 value.into_filter_value(),
127 )
128 }
129
130 pub fn crosses_below(self, value: impl IntoFilterValue) -> FilterCondition {
131 FilterCondition::new(
132 self,
133 FilterOperator::CrossesBelow,
134 value.into_filter_value(),
135 )
136 }
137
138 pub fn matches(self, value: impl IntoFilterValue) -> FilterCondition {
139 FilterCondition::new(self, FilterOperator::Match, value.into_filter_value())
140 }
141
142 pub fn empty(self) -> FilterCondition {
143 FilterCondition::new(self, FilterOperator::Empty, serde_json::Value::Null)
144 }
145
146 pub fn not_empty(self) -> FilterCondition {
147 FilterCondition::new(self, FilterOperator::NotEmpty, serde_json::Value::Null)
148 }
149
150 pub fn above_pct(
151 self,
152 base: impl IntoFilterValue,
153 pct: impl IntoFilterValue,
154 ) -> FilterCondition {
155 FilterCondition::new(
156 self,
157 FilterOperator::AbovePercent,
158 vec![base.into_filter_value(), pct.into_filter_value()].into_filter_value(),
159 )
160 }
161
162 pub fn below_pct(
163 self,
164 base: impl IntoFilterValue,
165 pct: impl IntoFilterValue,
166 ) -> FilterCondition {
167 FilterCondition::new(
168 self,
169 FilterOperator::BelowPercent,
170 vec![base.into_filter_value(), pct.into_filter_value()].into_filter_value(),
171 )
172 }
173
174 pub fn sort(self, order: SortOrder) -> SortSpec {
175 SortSpec::new(self, order)
176 }
177}
178
179impl fmt::Display for Column {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181 f.write_str(self.as_str())
182 }
183}
184
185impl Serialize for Column {
186 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
187 where
188 S: Serializer,
189 {
190 serializer.serialize_str(self.as_str())
191 }
192}
193
194impl From<&'static str> for Column {
195 fn from(value: &'static str) -> Self {
196 Self::from_static(value)
197 }
198}
199
200impl From<String> for Column {
201 fn from(value: String) -> Self {
202 Self::new(value)
203 }
204}
205
206impl From<&String> for Column {
207 fn from(value: &String) -> Self {
208 Self::new(value.clone())
209 }
210}
211
212#[derive(Debug, Clone, PartialEq, Eq, Hash)]
213pub struct Market(Cow<'static, str>);
214
215impl Market {
216 pub const fn from_static(name: &'static str) -> Self {
217 Self(Cow::Borrowed(name))
218 }
219
220 pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
221 Self(name.into())
222 }
223
224 pub fn as_str(&self) -> &str {
225 self.0.as_ref()
226 }
227}
228
229impl Serialize for Market {
230 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
231 where
232 S: Serializer,
233 {
234 serializer.serialize_str(self.as_str())
235 }
236}
237
238impl From<&'static str> for Market {
239 fn from(value: &'static str) -> Self {
240 Self::from_static(value)
241 }
242}
243
244impl From<String> for Market {
245 fn from(value: String) -> Self {
246 Self::new(value)
247 }
248}
249
250#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
251pub struct Ticker(Cow<'static, str>);
252
253impl Ticker {
254 pub fn from_parts(exchange: &str, symbol: &str) -> Self {
255 Self(Cow::Owned(format!("{exchange}:{symbol}")))
256 }
257
258 pub const fn from_static(raw: &'static str) -> Self {
259 Self(Cow::Borrowed(raw))
260 }
261
262 pub fn new(raw: impl Into<Cow<'static, str>>) -> Self {
263 Self(raw.into())
264 }
265
266 pub fn as_str(&self) -> &str {
267 self.0.as_ref()
268 }
269}
270
271impl fmt::Display for Ticker {
272 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
273 f.write_str(self.as_str())
274 }
275}
276
277impl Serialize for Ticker {
278 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
279 where
280 S: Serializer,
281 {
282 serializer.serialize_str(self.as_str())
283 }
284}
285
286impl From<&'static str> for Ticker {
287 fn from(value: &'static str) -> Self {
288 Self::from_static(value)
289 }
290}
291
292impl From<String> for Ticker {
293 fn from(value: String) -> Self {
294 Self::new(value)
295 }
296}