1use crate::error::{ChartError, Result};
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6use std::any::Any;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10pub struct TimeRange {
11 pub start: DateTime<Utc>,
12 pub end: DateTime<Utc>,
13}
14
15impl TimeRange {
16 pub fn new(start: DateTime<Utc>, end: DateTime<Utc>) -> Result<Self> {
17 if start >= end {
18 return Err(ChartError::data_range("Start time must be before end time"));
19 }
20 Ok(Self { start, end })
21 }
22
23 pub fn duration(&self) -> chrono::Duration {
24 self.end - self.start
25 }
26
27 pub fn contains(&self, time: DateTime<Utc>) -> bool {
28 time >= self.start && time <= self.end
29 }
30
31 pub fn overlaps(&self, other: &TimeRange) -> bool {
32 self.start < other.end && other.start < self.end
33 }
34}
35
36pub trait Series: std::fmt::Debug + Send + Sync {
38 fn name(&self) -> &str;
39 fn len(&self) -> usize;
40 fn is_empty(&self) -> bool {
41 self.len() == 0
42 }
43
44 fn get_x(&self, index: usize) -> f64;
46
47 fn get_y(&self, index: usize) -> f64;
49
50 fn get_x_range(&self) -> (f64, f64);
52
53 fn get_y_range(&self, x_min: f64, x_max: f64) -> (f64, f64);
55
56 fn find_index(&self, x: f64) -> Option<usize>;
58
59 fn as_any(&self) -> &dyn Any;
61}
62
63pub trait RangeSeries: Series {
66 fn get_range(&self, index: usize) -> (f64, f64, f64, f64);
71
72 fn get_auxiliary(&self, index: usize) -> Option<f64> {
74 let _ = index;
75 None
76 }
77}
78
79#[derive(Debug)]
81pub struct ChartData {
82 pub main_series: Box<dyn Series>,
84 pub overlays: Vec<Box<dyn Series>>,
86
87 visible_range: Option<TimeRange>,
89 y_bounds: Option<(f64, f64)>,
91}
92
93impl ChartData {
94 pub fn new(series: Box<dyn Series>) -> Self {
96 Self {
97 main_series: series,
98 overlays: Vec::new(),
99 visible_range: None,
100 y_bounds: None,
101 }
102 }
103
104 pub fn symbol(&self) -> &str {
106 self.main_series.name()
107 }
108
109 pub fn len(&self) -> usize {
111 self.main_series.len()
112 }
113
114 pub fn is_empty(&self) -> bool {
116 self.main_series.is_empty()
117 }
118
119 pub fn time_range(&self) -> Option<TimeRange> {
121 if self.is_empty() {
122 return None;
123 }
124
125 let (min_x, max_x) = self.main_series.get_x_range();
126
127 let start = DateTime::from_timestamp(min_x as i64, 0)?;
129 let end = DateTime::from_timestamp(max_x as i64, 0)?;
130
131 TimeRange::new(start, end).ok()
132 }
133
134 pub fn y_bounds(&self) -> Option<(f64, f64)> {
136 if self.is_empty() {
137 return None;
138 }
139
140 if let Some(bounds) = self.y_bounds {
142 return Some(bounds);
143 }
144
145 let (min_x, max_x) = if let Some(range) = self.visible_range {
147 (range.start.timestamp() as f64, range.end.timestamp() as f64)
148 } else {
149 self.main_series.get_x_range()
150 };
151
152 Some(self.main_series.get_y_range(min_x, max_x))
153 }
154
155 pub fn set_visible_range(&mut self, range: TimeRange) {
157 self.visible_range = Some(range);
158 self.y_bounds = None; }
160
161 pub fn clear_visible_range(&mut self) {
163 self.visible_range = None;
164 self.y_bounds = None;
165 }
166
167 pub fn visible_indices(&self) -> Option<(usize, usize)> {
169 let range = self.visible_range?;
170 let start_ts = range.start.timestamp() as f64;
171 let end_ts = range.end.timestamp() as f64;
172
173 let start_idx = self.main_series.find_index(start_ts)?;
174 let end_idx = self.main_series.find_index(end_ts)?.min(self.len());
175
176 Some((start_idx, end_idx))
177 }
178}