Skip to main content

chart/
data.rs

1//! A table of named, equal-length columns.
2
3use std::{
4    collections::HashMap,
5    sync::{
6        Arc,
7        atomic::{AtomicU64, Ordering},
8    },
9};
10
11use gpui::SharedString;
12
13static GENERATIONS: AtomicU64 = AtomicU64::new(1);
14
15/// Columns by name, each `len` rows long. Cloning is one reference count.
16///
17/// Every change takes a generation no other table has had, and a clone shares
18/// its original's, so equal generations mean equal contents. A table rebuilt
19/// from the same values is a new generation: hold the table, not the values
20/// it was built from, to keep what was computed from it.
21#[derive(Clone, Debug, Default)]
22pub struct Data(Arc<Table>);
23
24#[derive(Clone, Debug, Default)]
25struct Table {
26    fields: Vec<(SharedString, Column)>,
27    len: usize,
28    generation: u64,
29}
30
31#[derive(Clone, Debug)]
32pub enum Column {
33    /// Quantities and times. A time is milliseconds since the Unix epoch, UTC.
34    /// `NaN` is a missing value.
35    Number(Arc<[f64]>),
36    Text(Text),
37}
38
39/// Strings stored as codes into `names`, each distinct string once, in the
40/// order first seen.
41#[derive(Clone, Debug)]
42pub struct Text {
43    pub codes: Arc<[u32]>,
44    pub names: Arc<[SharedString]>,
45}
46
47impl Text {
48    pub fn get(&self, row: usize) -> &SharedString {
49        &self.names[self.codes[row] as usize]
50    }
51}
52
53impl<S: Into<SharedString>> FromIterator<S> for Text {
54    fn from_iter<I: IntoIterator<Item = S>>(values: I) -> Self {
55        let mut index = HashMap::new();
56        let mut names = Vec::new();
57        let codes = values
58            .into_iter()
59            .map(|value| {
60                let value = value.into();
61                *index.entry(value.clone()).or_insert_with(|| {
62                    names.push(value);
63                    names.len() as u32 - 1
64                })
65            })
66            .collect();
67        Self {
68            codes,
69            names: names.into(),
70        }
71    }
72}
73
74impl Column {
75    pub fn len(&self) -> usize {
76        match self {
77            Self::Number(values) => values.len(),
78            Self::Text(text) => text.codes.len(),
79        }
80    }
81
82    pub fn is_empty(&self) -> bool {
83        self.len() == 0
84    }
85}
86
87impl Data {
88    pub fn new() -> Self {
89        Self::default()
90    }
91
92    /// Adds `field`, or replaces the column of that name in place.
93    ///
94    /// # Panics
95    ///
96    /// If the table already has a column and `column` is not as long.
97    pub fn with(mut self, field: impl Into<SharedString>, column: Column) -> Self {
98        let field = field.into();
99        let table = Arc::make_mut(&mut self.0);
100        if let Some((name, other)) = table.fields.iter().find(|(name, _)| *name != field) {
101            assert_eq!(
102                column.len(),
103                other.len(),
104                "column `{field}` is not as long as `{name}`"
105            );
106        }
107        table.len = column.len();
108        match table.fields.iter_mut().find(|(name, _)| *name == field) {
109            Some((_, slot)) => *slot = column,
110            None => table.fields.push((field, column)),
111        }
112        table.generation = GENERATIONS.fetch_add(1, Ordering::Relaxed);
113        self
114    }
115
116    pub fn number(self, field: impl Into<SharedString>, values: impl Into<Arc<[f64]>>) -> Self {
117        self.with(field, Column::Number(values.into()))
118    }
119
120    pub fn text<S: Into<SharedString>>(
121        self,
122        field: impl Into<SharedString>,
123        values: impl IntoIterator<Item = S>,
124    ) -> Self {
125        self.with(field, Column::Text(values.into_iter().collect()))
126    }
127
128    pub fn column(&self, field: &str) -> Option<&Column> {
129        self.0
130            .fields
131            .iter()
132            .find(|(name, _)| name == field)
133            .map(|(_, column)| column)
134    }
135
136    /// In the order they were first added.
137    pub fn fields(&self) -> impl Iterator<Item = (&SharedString, &Column)> {
138        self.0.fields.iter().map(|(name, column)| (name, column))
139    }
140
141    /// Rows. 0 for a table with no columns.
142    pub fn len(&self) -> usize {
143        self.0.len
144    }
145
146    pub fn is_empty(&self) -> bool {
147        self.0.len == 0
148    }
149
150    /// 0 for a table with no columns.
151    pub fn generation(&self) -> u64 {
152        self.0.generation
153    }
154}