1use crate::errors::*;
2use crate::traits::{Close, High, Low, Open, Volume};
3
4#[derive(Debug, Clone)]
29pub struct DataItem {
30 open: f64,
31 high: f64,
32 low: f64,
33 close: f64,
34 volume: f64,
35}
36
37impl DataItem {
38 pub fn builder() -> DataItemBuilder {
39 DataItemBuilder::new()
40 }
41}
42
43impl Open for DataItem {
44 fn open(&self) -> f64 {
45 self.open
46 }
47}
48
49impl High for DataItem {
50 fn high(&self) -> f64 {
51 self.high
52 }
53}
54
55impl Low for DataItem {
56 fn low(&self) -> f64 {
57 self.low
58 }
59}
60
61impl Close for DataItem {
62 fn close(&self) -> f64 {
63 self.close
64 }
65}
66
67impl Volume for DataItem {
68 fn volume(&self) -> f64 {
69 self.volume
70 }
71}
72
73pub struct DataItemBuilder {
74 open: Option<f64>,
75 high: Option<f64>,
76 low: Option<f64>,
77 close: Option<f64>,
78 volume: Option<f64>,
79}
80
81impl DataItemBuilder {
82 pub fn new() -> Self {
83 Self {
84 open: None,
85 high: None,
86 low: None,
87 close: None,
88 volume: None,
89 }
90 }
91
92 pub fn open(mut self, val: f64) -> Self {
93 self.open = Some(val);
94 self
95 }
96
97 pub fn high(mut self, val: f64) -> Self {
98 self.high = Some(val);
99 self
100 }
101
102 pub fn low(mut self, val: f64) -> Self {
103 self.low = Some(val);
104 self
105 }
106
107 pub fn close(mut self, val: f64) -> Self {
108 self.close = Some(val);
109 self
110 }
111
112 pub fn volume(mut self, val: f64) -> Self {
113 self.volume = Some(val);
114 self
115 }
116
117 pub fn build(self) -> Result<DataItem> {
118 if let (Some(open), Some(high), Some(low), Some(close), Some(volume)) =
119 (self.open, self.high, self.low, self.close, self.volume)
120 {
121 if low <= open
123 && low <= close
124 && low <= high
125 && high >= open
126 && high >= close
127 && volume >= 0.0
128 && low >= 0.0
129 {
130 let item = DataItem {
131 open,
132 high,
133 low,
134 close,
135 volume,
136 };
137 Ok(item)
138 } else {
139 Err(Error::from_kind(ErrorKind::DataItemInvalid))
140 }
141 } else {
142 Err(Error::from_kind(ErrorKind::DataItemIncomplete))
143 }
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn test_builder() {
153 fn assert_valid((open, high, low, close, volume): (f64, f64, f64, f64, f64)) {
154 let result = DataItem::builder()
155 .open(open)
156 .high(high)
157 .low(low)
158 .close(close)
159 .volume(volume)
160 .build();
161 assert!(result.is_ok());
162 }
163
164 fn assert_invalid((open, high, low, close, volume): (f64, f64, f64, f64, f64)) {
165 let result = DataItem::builder()
166 .open(open)
167 .high(high)
168 .low(low)
169 .close(close)
170 .volume(volume)
171 .build();
172 assert!(result.is_err());
173 }
174
175 let valid_records = vec![
176 (20.0, 25.0, 15.0, 21.0, 7500.0),
178 (10.0, 10.0, 10.0, 10.0, 10.0),
179 (0.0, 0.0, 0.0, 0.0, 0.0),
180 ];
181 for record in valid_records {
182 assert_valid(record)
183 }
184
185 let invalid_records = vec![
186 (-1.0, 25.0, 15.0, 21.0, 7500.0),
188 (20.0, -1.0, 15.0, 21.0, 7500.0),
189 (20.0, 25.0, -1.0, 21.0, 7500.0),
190 (20.0, 25.0, 15.0, -1.0, 7500.0),
191 (20.0, 25.0, 15.0, 21.0, -1.0),
192 (14.9, 25.0, 15.0, 21.0, 7500.0),
193 (25.1, 25.0, 15.0, 21.0, 7500.0),
194 (20.0, 25.0, 15.0, 14.9, 7500.0),
195 (20.0, 25.0, 15.0, 25.1, 7500.0),
196 (20.0, 15.0, 25.0, 21.0, 7500.0),
197 ];
198 for record in invalid_records {
199 assert_invalid(record)
200 }
201 }
202}