1use crate::column::{
6 ArrayColumn, BooleanColumn, ColumnData, DecimalColumn, Float64Column, StringColumn,
7};
8use crate::error::{PondError, Result};
9use std::sync::Arc;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum ColumnKind {
14 Time,
16 TimeRange,
18 Interval,
20 Value,
23 Number,
25 Decimal,
27 String,
29 Boolean,
31 Array,
34}
35
36impl ColumnKind {
37 pub fn as_str(self) -> &'static str {
41 match self {
42 ColumnKind::Time => "time",
43 ColumnKind::TimeRange => "timeRange",
44 ColumnKind::Interval => "interval",
45 ColumnKind::Value => "value",
46 ColumnKind::Number => "number",
47 ColumnKind::Decimal => "decimal",
48 ColumnKind::String => "string",
49 ColumnKind::Boolean => "boolean",
50 ColumnKind::Array => "array",
51 }
52 }
53}
54
55#[derive(Debug, Clone, PartialEq, Eq)]
57pub struct ColumnDef {
58 pub name: String,
60 pub kind: ColumnKind,
62 pub required: bool,
64}
65
66impl ColumnDef {
67 pub fn required(name: impl Into<String>, kind: ColumnKind) -> Self {
69 Self {
70 name: name.into(),
71 kind,
72 required: true,
73 }
74 }
75
76 pub fn optional(name: impl Into<String>, kind: ColumnKind) -> Self {
78 Self {
79 name: name.into(),
80 kind,
81 required: false,
82 }
83 }
84}
85
86#[derive(Debug, Clone, PartialEq, Eq)]
89pub struct TimeRangeKeys {
90 pub begin: Vec<i64>,
92 pub end: Vec<i64>,
94}
95
96#[derive(Debug, Clone, PartialEq)]
99pub struct IntervalKeys {
100 pub label: Labels,
102 pub begin: Vec<i64>,
104 pub end: Vec<i64>,
106}
107
108#[derive(Debug, Clone, PartialEq)]
112pub enum Labels {
113 Number(Vec<f64>),
116 String(StringColumn),
118}
119
120impl Labels {
121 pub fn len(&self) -> usize {
123 match self {
124 Labels::Number(v) => v.len(),
125 Labels::String(c) => c.len(),
126 }
127 }
128
129 pub fn is_empty(&self) -> bool {
131 self.len() == 0
132 }
133
134 pub fn numbers(&self) -> Option<&[f64]> {
136 match self {
137 Labels::Number(v) => Some(v),
138 Labels::String(_) => None,
139 }
140 }
141
142 pub fn strings(&self) -> Option<&StringColumn> {
144 match self {
145 Labels::Number(_) => None,
146 Labels::String(c) => Some(c),
147 }
148 }
149}
150
151impl From<Vec<f64>> for Labels {
152 fn from(v: Vec<f64>) -> Self {
153 Labels::Number(v)
154 }
155}
156
157impl From<Vec<i64>> for Labels {
160 fn from(v: Vec<i64>) -> Self {
161 Labels::Number(v.into_iter().map(|x| x as f64).collect())
162 }
163}
164
165impl From<Vec<&str>> for Labels {
166 fn from(v: Vec<&str>) -> Self {
167 Labels::String(StringColumn::from_options(v.into_iter().map(Some)))
168 }
169}
170
171impl From<Vec<String>> for Labels {
172 fn from(v: Vec<String>) -> Self {
173 Labels::String(StringColumn::from_options(v.into_iter().map(Some)))
174 }
175}
176
177impl From<StringColumn> for Labels {
178 fn from(c: StringColumn) -> Self {
179 Labels::String(c)
180 }
181}
182
183#[derive(Debug, Clone, PartialEq)]
185pub enum Key {
186 Time(Vec<i64>),
188 TimeRange(TimeRangeKeys),
190 Interval(IntervalKeys),
192}
193
194impl Key {
195 pub fn len(&self) -> usize {
197 self.begin().len()
198 }
199
200 pub fn is_empty(&self) -> bool {
202 self.len() == 0
203 }
204
205 pub fn begin(&self) -> &[i64] {
207 match self {
208 Key::Time(t) => t,
209 Key::TimeRange(k) => &k.begin,
210 Key::Interval(k) => &k.begin,
211 }
212 }
213
214 pub fn end(&self) -> Option<&[i64]> {
216 match self {
217 Key::Time(_) => None,
218 Key::TimeRange(k) => Some(&k.end),
219 Key::Interval(k) => Some(&k.end),
220 }
221 }
222
223 pub(crate) fn rows(&self, range: std::ops::Range<usize>) -> Key {
225 match self {
226 Key::Time(t) => Key::Time(t[range].to_vec()),
227 Key::TimeRange(k) => Key::TimeRange(TimeRangeKeys {
228 begin: k.begin[range.clone()].to_vec(),
229 end: k.end[range].to_vec(),
230 }),
231 Key::Interval(k) => Key::Interval(IntervalKeys {
232 label: match &k.label {
233 Labels::Number(v) => Labels::Number(v[range.clone()].to_vec()),
234 Labels::String(s) => {
235 let rows: Vec<Option<usize>> = range.clone().map(Some).collect();
236 Labels::String(s.take(&rows))
237 }
238 },
239 begin: k.begin[range.clone()].to_vec(),
240 end: k.end[range].to_vec(),
241 }),
242 }
243 }
244
245 pub(crate) fn gather(sources: &[&Key], picks: &[(u32, u32)]) -> Key {
248 let pick = |v: &dyn Fn(&Key) -> &[i64]| -> Vec<i64> {
249 let cols: Vec<&[i64]> = sources.iter().map(|k| v(k)).collect();
250 picks
251 .iter()
252 .map(|&(s, r)| cols[s as usize][r as usize])
253 .collect()
254 };
255 let begin = pick(&|k| k.begin());
256 match sources[0] {
257 Key::Time(_) => Key::Time(begin),
258 Key::TimeRange(_) => Key::TimeRange(TimeRangeKeys {
259 begin,
260 end: pick(&|k| k.end().expect("same kind")),
261 }),
262 Key::Interval(first) => {
263 let labels: Vec<&Labels> = sources
264 .iter()
265 .map(|k| match k {
266 Key::Interval(i) => &i.label,
267 _ => unreachable!("same kind"),
268 })
269 .collect();
270 let label = match &first.label {
271 Labels::Number(_) => Labels::Number(
272 picks
273 .iter()
274 .map(|&(s, r)| {
275 labels[s as usize].numbers().expect("same kind")[r as usize]
276 })
277 .collect(),
278 ),
279 Labels::String(_) => {
280 Labels::String(StringColumn::from_options(picks.iter().map(|&(s, r)| {
281 labels[s as usize]
282 .strings()
283 .expect("same kind")
284 .get(r as usize)
285 })))
286 }
287 };
288 Key::Interval(IntervalKeys {
289 label,
290 begin,
291 end: pick(&|k| k.end().expect("same kind")),
292 })
293 }
294 }
295 }
296
297 pub(crate) fn take(&self, rows: &[usize]) -> Key {
299 let pick = |v: &[i64]| rows.iter().map(|&i| v[i]).collect::<Vec<i64>>();
300 match self {
301 Key::Time(t) => Key::Time(pick(t)),
302 Key::TimeRange(k) => Key::TimeRange(TimeRangeKeys {
303 begin: pick(&k.begin),
304 end: pick(&k.end),
305 }),
306 Key::Interval(k) => Key::Interval(IntervalKeys {
307 label: match &k.label {
308 Labels::Number(v) => Labels::Number(rows.iter().map(|&i| v[i]).collect()),
309 Labels::String(s) => {
310 let picks: Vec<Option<usize>> = rows.iter().map(|&i| Some(i)).collect();
311 Labels::String(s.take(&picks))
312 }
313 },
314 begin: pick(&k.begin),
315 end: pick(&k.end),
316 }),
317 }
318 }
319
320 pub(crate) fn kind(&self) -> ColumnKind {
321 match self {
322 Key::Time(_) => ColumnKind::Time,
323 Key::TimeRange(_) => ColumnKind::TimeRange,
324 Key::Interval(_) => ColumnKind::Interval,
325 }
326 }
327}
328
329#[derive(Debug, Clone, PartialEq)]
345pub struct Column {
346 pub(crate) name: String,
347 pub(crate) required: bool,
348 pub(crate) data: ColumnData,
349}
350
351impl Column {
352 pub fn required(name: impl Into<String>, values: impl Into<ColumnData>) -> Self {
356 Self {
357 name: name.into(),
358 required: true,
359 data: values.into(),
360 }
361 }
362
363 pub fn optional(name: impl Into<String>, values: impl Into<ColumnData>) -> Self {
367 Self {
368 name: name.into(),
369 required: false,
370 data: values.into(),
371 }
372 }
373
374 pub fn optional_raw(name: impl Into<String>, values: Vec<f64>) -> Self {
378 Self::optional(name, values)
379 }
380
381 pub(crate) fn from_data(name: String, data: impl Into<ColumnData>, required: bool) -> Self {
383 Self {
384 name,
385 required,
386 data: data.into(),
387 }
388 }
389
390 pub fn name(&self) -> &str {
392 &self.name
393 }
394
395 pub fn is_required(&self) -> bool {
397 self.required
398 }
399
400 pub fn kind(&self) -> ColumnKind {
403 self.data.kind()
404 }
405
406 pub fn data(&self) -> &ColumnData {
408 &self.data
409 }
410
411 pub fn numbers(&self) -> Result<&Float64Column> {
413 match &self.data {
414 ColumnData::Number(c) => Ok(c),
415 _ => Err(self.wrong_kind("Column::numbers", "number")),
416 }
417 }
418
419 pub fn strings(&self) -> Result<&StringColumn> {
421 match &self.data {
422 ColumnData::String(c) => Ok(c),
423 _ => Err(self.wrong_kind("Column::strings", "string")),
424 }
425 }
426
427 pub fn decimals(&self) -> Result<&DecimalColumn> {
429 match &self.data {
430 ColumnData::Decimal(c) => Ok(c),
431 _ => Err(self.wrong_kind("Column::decimals", "decimal")),
432 }
433 }
434
435 pub fn booleans(&self) -> Result<&BooleanColumn> {
437 match &self.data {
438 ColumnData::Boolean(c) => Ok(c),
439 _ => Err(self.wrong_kind("Column::booleans", "boolean")),
440 }
441 }
442
443 pub fn arrays(&self) -> Result<&ArrayColumn> {
445 match &self.data {
446 ColumnData::Array(c) => Ok(c),
447 _ => Err(self.wrong_kind("Column::arrays", "array")),
448 }
449 }
450
451 fn wrong_kind(&self, call: &'static str, wanted: &str) -> PondError {
452 PondError::WrongKind {
453 call,
454 column: self.name.clone(),
455 kind: self.kind().as_str(),
456 reason: format!("this call needs a {wanted} column"),
457 }
458 }
459}
460
461#[derive(Debug, Clone, PartialEq)]
463pub struct TimeSeries {
464 name: String,
465 key_name: String,
466 key: Arc<Key>,
469 columns: Vec<Column>,
470}
471
472pub const MAX_TIME_MS: i64 = 8_640_000_000_000_000;
479
480impl TimeSeries {
481 pub fn new(name: impl Into<String>, time: Vec<i64>, columns: Vec<Column>) -> Result<Self> {
485 Self::with_key(
486 "TimeSeries::new",
487 name.into(),
488 "time".into(),
489 Key::Time(time),
490 columns,
491 )
492 }
493
494 pub fn from_intervals(
515 name: impl Into<String>,
516 keys: IntervalKeys,
517 columns: Vec<Column>,
518 ) -> Result<Self> {
519 Self::with_key(
520 "TimeSeries::from_intervals",
521 name.into(),
522 "interval".into(),
523 Key::Interval(keys),
524 columns,
525 )
526 }
527
528 pub fn from_time_ranges(
544 name: impl Into<String>,
545 keys: TimeRangeKeys,
546 columns: Vec<Column>,
547 ) -> Result<Self> {
548 Self::with_key(
549 "TimeSeries::from_time_ranges",
550 name.into(),
551 "timeRange".into(),
552 Key::TimeRange(keys),
553 columns,
554 )
555 }
556
557 pub(crate) fn with_key(
559 call: &'static str,
560 name: String,
561 key_name: String,
562 key: Key,
563 columns: Vec<Column>,
564 ) -> Result<Self> {
565 let invalid = |message: String| PondError::InvalidData { call, message };
566 let rows = key.len();
567 if let Key::Interval(k) = &key {
568 if k.label.len() != rows {
569 return Err(invalid(
570 "interval labels and begins have different lengths".into(),
571 ));
572 }
573 match &k.label {
574 Labels::Number(v) => {
575 if let Some(i) = v.iter().position(|l| !l.is_finite()) {
576 return Err(invalid(format!(
577 "interval label at row {i} is {}; number labels must be finite",
578 v[i]
579 )));
580 }
581 }
582 Labels::String(c) => {
583 if let Some(i) = c.iter().position(|l| l.is_none()) {
584 return Err(invalid(format!(
585 "interval label at row {i} is missing; every row needs a label"
586 )));
587 }
588 }
589 }
590 }
591 if let Some(end) = key.end() {
592 if end.len() != rows {
593 return Err(invalid("key begins and ends have different lengths".into()));
594 }
595 if let Some(i) = (0..rows).find(|&i| end[i] < key.begin()[i]) {
596 return Err(invalid(format!(
597 "key at row {i} ends ({}) before it begins ({})",
598 end[i],
599 key.begin()[i]
600 )));
601 }
602 }
603 let out_of_range = |t: &i64| !(-MAX_TIME_MS..=MAX_TIME_MS).contains(t);
604 for edge in [key.begin(), key.end().unwrap_or(&[])] {
605 if let Some(i) = edge.iter().position(out_of_range) {
606 return Err(invalid(format!(
607 "time {} ms at row {i} is outside the supported range ±{MAX_TIME_MS} ms (years -271821 to 275760, as JavaScript dates)",
608 edge[i]
609 )));
610 }
611 }
612 let order = |i: usize| (key.begin()[i], key.end().map_or(0, |e| e[i]));
615 if let Some(i) = (1..rows).find(|&i| order(i) < order(i - 1)) {
616 let show = |i: usize| match key.end() {
617 None => format!("{}", key.begin()[i]),
618 Some(e) => format!("[{}, {}]", key.begin()[i], e[i]),
619 };
620 return Err(invalid(format!(
621 "key is not in order: row {i} ({}) sorts before row {} ({}); keys are ordered by begin, then end",
622 show(i),
623 i - 1,
624 show(i - 1)
625 )));
626 }
627 Self::check_columns(call, &key_name, rows, &columns)?;
628 Ok(Self {
629 name,
630 key_name,
631 key: Arc::new(key),
632 columns,
633 })
634 }
635
636 pub fn name(&self) -> &str {
638 &self.name
639 }
640
641 pub fn len(&self) -> usize {
643 self.key.len()
644 }
645
646 pub fn is_empty(&self) -> bool {
648 self.key.is_empty()
649 }
650
651 pub fn key(&self) -> &Key {
653 &self.key
654 }
655
656 pub fn key_name(&self) -> &str {
658 &self.key_name
659 }
660
661 pub fn schema(&self) -> Vec<ColumnDef> {
663 std::iter::once(ColumnDef {
664 name: self.key_name.clone(),
665 kind: self.key.kind(),
666 required: true,
667 })
668 .chain(self.columns.iter().map(|c| ColumnDef {
669 name: c.name.clone(),
670 kind: c.kind(),
671 required: c.required,
672 }))
673 .collect()
674 }
675
676 pub fn columns(&self) -> &[Column] {
678 &self.columns
679 }
680
681 pub fn column(&self, name: &str) -> Result<&Column> {
685 Ok(&self.columns[self.column_index("TimeSeries::column", name)?])
686 }
687
688 pub(crate) fn column_index(&self, call: &'static str, name: &str) -> Result<usize> {
689 self.columns
690 .iter()
691 .position(|c| c.name == name)
692 .ok_or_else(|| PondError::UnknownColumn {
693 call,
694 column: name.to_string(),
695 available: self.columns.iter().map(|c| c.name.clone()).collect(),
696 })
697 }
698
699 pub(crate) fn check_columns(
702 call: &'static str,
703 key_name: &str,
704 rows: usize,
705 columns: &[Column],
706 ) -> Result<()> {
707 let invalid = |message: String| PondError::InvalidData { call, message };
708 for (c, column) in columns.iter().enumerate() {
709 if column.name == key_name {
710 return Err(invalid(format!(
711 "value column '{}' has the same name as the key",
712 column.name
713 )));
714 }
715 if columns[..c].iter().any(|other| other.name == column.name) {
716 return Err(invalid(format!("column '{}' appears twice", column.name)));
717 }
718 if column.data.len() != rows {
719 return Err(invalid(format!(
720 "column '{}' has {} values but the key has {rows}",
721 column.name,
722 column.data.len()
723 )));
724 }
725 if let ColumnData::Array(a) = &column.data
726 && let Some((r, e)) = a.first_bad_element()
727 {
728 return Err(invalid(format!(
729 "column '{}' row {r}: array element {e} must be a finite number, string, or boolean",
730 column.name
731 )));
732 }
733 if column.required && column.data.missing_count() > 0 {
734 let i = (0..rows)
735 .find(|&i| !column.data.is_present(i))
736 .expect("a gap exists");
737 return Err(invalid(format!(
738 "required column '{}' has a gap at row {i}; use Column::optional if gaps are allowed",
739 column.name
740 )));
741 }
742 }
743 Ok(())
744 }
745
746 pub(crate) fn from_parts(
747 name: String,
748 key_name: String,
749 key: impl Into<Arc<Key>>,
750 columns: Vec<Column>,
751 ) -> Self {
752 let key = key.into();
753 debug_assert!(columns.iter().all(|c| c.data.len() == key.len()));
754 Self {
755 name,
756 key_name,
757 key,
758 columns,
759 }
760 }
761
762 pub(crate) fn shared_key(&self) -> Arc<Key> {
764 Arc::clone(&self.key)
765 }
766}
767
768#[cfg(test)]
769mod tests {
770 use super::*;
771
772 fn ranges(begin: Vec<i64>, end: Vec<i64>) -> Result<TimeSeries> {
773 TimeSeries::from_time_ranges("r", TimeRangeKeys { begin, end }, vec![])
774 }
775
776 fn intervals(label: Labels) -> Result<TimeSeries> {
777 let keys = IntervalKeys {
778 label,
779 begin: vec![0, 10],
780 end: vec![10, 20],
781 };
782 TimeSeries::from_intervals("i", keys, vec![])
783 }
784
785 #[test]
786 fn range_keys_are_checked_as_pond_ts_checks_them() {
787 assert!(ranges(vec![0, 0, 5], vec![2, 9, 5]).is_ok());
789 let e = ranges(vec![0, 0], vec![9, 2]).unwrap_err().to_string();
790 assert!(
791 e.contains("row 1 ([0, 2]) sorts before row 0 ([0, 9])"),
792 "{e}"
793 );
794 let e = ranges(vec![5], vec![4]).unwrap_err().to_string();
795 assert!(e.contains("ends (4) before it begins (5)"), "{e}");
796 assert!(ranges(vec![0, 1], vec![1]).is_err());
797 assert!(ranges(vec![0], vec![MAX_TIME_MS + 1]).is_err());
798 }
799
800 #[test]
801 fn interval_labels_are_checked() {
802 assert!(intervals(vec![1.5, 1.5].into()).is_ok());
804 assert!(intervals(vec!["a", "a"].into()).is_ok());
805 let e = intervals(vec![1.0, f64::NAN].into())
806 .unwrap_err()
807 .to_string();
808 assert!(e.contains("label at row 1"), "{e}");
809 let gap = Labels::String(StringColumn::from_options([Some("a"), None]));
810 let e = intervals(gap).unwrap_err().to_string();
811 assert!(e.contains("label at row 1 is missing"), "{e}");
812 assert!(intervals(vec![1.0].into()).is_err());
813 }
814}