teksilo_data/
chart_source.rs1use crate::{ChartChange, ChartModel, ChartWindow, SeriesId, SeriesView};
7use teksilo_core::color_prop::ColorProp;
8use teksilo_core::{ObserverHandle, Signal};
9
10#[derive(Debug)]
15pub enum ChartSource<T: 'static> {
16 Model(ChartModel<T>),
18 Window(ChartWindow<T>),
20}
21
22impl<T: 'static> Clone for ChartSource<T> {
23 fn clone(&self) -> Self {
24 match self {
25 Self::Model(model) => Self::Model(model.clone()),
26 Self::Window(window) => Self::Window(window.clone()),
27 }
28 }
29}
30
31impl<T: 'static> From<ChartModel<T>> for ChartSource<T> {
32 fn from(model: ChartModel<T>) -> Self {
33 Self::Model(model)
34 }
35}
36impl<T: 'static> From<ChartWindow<T>> for ChartSource<T> {
37 fn from(window: ChartWindow<T>) -> Self {
38 Self::Window(window)
39 }
40}
41
42impl<T: 'static> ChartSource<T> {
43 fn source(&self) -> ChartModel<T> {
44 match self {
45 Self::Model(model) => model.clone(),
46 Self::Window(window) => window.source(),
47 }
48 }
49
50 pub fn with_all_series<R>(&self, f: impl FnOnce(&[SeriesView<'_, T>]) -> R) -> R {
52 match self {
53 Self::Model(model) => model.with_all_series(f),
54 Self::Window(window) => window.with_all_series(f),
55 }
56 }
57
58 pub fn with_series_view<R>(
60 &self,
61 series: SeriesId,
62 f: impl FnOnce(SeriesView<'_, T>) -> R,
63 ) -> Option<R> {
64 match self {
65 Self::Model(model) => model.with_series_view(series, f),
66 Self::Window(window) => window.with_series_view(series, f),
67 }
68 }
69
70 pub fn with_series<R>(
72 &self,
73 series: SeriesId,
74 f: impl FnOnce(&str, Option<&ColorProp>, bool) -> R,
75 ) -> Option<R> {
76 self.source().with_series(series, f)
77 }
78
79 pub fn set_series_visible(&self, series: SeriesId, visible: bool) {
81 self.source().set_series_visible(series, visible);
82 }
83
84 pub fn series_ids(&self) -> Vec<SeriesId> {
86 self.source().series_ids()
87 }
88 pub fn series_count(&self) -> usize {
90 self.source().series_count()
91 }
92 pub fn series_id_at(&self, index: usize) -> Option<SeriesId> {
94 self.source().series_id_at(index)
95 }
96 pub fn only_series(&self) -> Option<SeriesId> {
98 self.source().only_series()
99 }
100 pub fn point_count(&self, series: SeriesId) -> usize {
102 self.with_series_view(series, |view| view.points.len())
103 .unwrap_or(0)
104 }
105
106 pub fn structure_version(&self) -> Signal<u64> {
108 match self {
109 Self::Model(model) => model.structure_version(),
110 Self::Window(window) => window.structure_version(),
111 }
112 }
113
114 pub fn style_version(&self) -> Signal<u64> {
116 match self {
117 Self::Model(model) => model.style_version(),
118 Self::Window(window) => window.style_version(),
119 }
120 }
121
122 pub fn observe_changes(&self, f: impl Fn(&ChartChange) + 'static) -> ObserverHandle {
124 match self {
125 Self::Model(model) => model.observe_changes(f),
126 Self::Window(window) => window.observe_changes(f),
127 }
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn window_borrows_points_and_tracks_structure_separately_from_style() {
137 struct NonClone;
138 let model = ChartModel::new();
139 let series = model.add_series("sensor");
140 for value in 0..4 {
141 model.push_point(series, NonClone, value as f32);
142 }
143 let window = ChartWindow::new(model.clone(), 2);
144 let input = ChartSource::from(window.clone());
145 model.with_series_view(series, |original| {
146 input.with_series_view(series, |view| {
147 assert!(std::ptr::eq(&original.points[2], &view.points[0]));
148 assert_eq!(view.points.len(), 2);
149 });
150 });
151 let version = input.structure_version().get();
152 window.set_window_size(1);
153 assert!(input.structure_version().get() > version);
154 assert_eq!(input.point_count(series), 1);
155 let version = input.structure_version().get();
156 model.set_series_pattern(series, crate::SeriesPattern::Dotted);
157 assert_eq!(input.structure_version().get(), version);
158 assert!(input.style_version().get() > 0);
159 input.set_series_visible(series, false);
160 assert_eq!(
161 model.with_series(series, |_, _, visible| visible),
162 Some(false)
163 );
164 model.remove_series(series);
165 assert_eq!(input.series_count(), 0);
166 assert!(input.with_series_view(series, |_| ()).is_none());
167 }
168}