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