1use serde_json::Value;
5
6use crate::json_text::{Json, NULL};
7
8use crate::column::{ArrayColumn, BooleanColumn, ColumnData, Float64Column, Scalar, StringColumn};
9use crate::error::{PondError, Result};
10use crate::series::{
11 Column, ColumnDef, ColumnKind, IntervalKeys, Key, Labels, TimeRangeKeys, TimeSeries,
12};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
17pub enum KeyOrder {
18 #[default]
20 Strict,
21 Reorder,
24 Clamp,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct JsonOptions {
33 time_zone: String,
34 order: KeyOrder,
35}
36
37impl Default for JsonOptions {
38 fn default() -> Self {
39 Self {
40 time_zone: "UTC".into(),
41 order: KeyOrder::Strict,
42 }
43 }
44}
45
46impl JsonOptions {
47 pub fn new() -> Self {
49 Self::default()
50 }
51
52 pub fn time_zone(mut self, zone: impl Into<String>) -> Self {
56 self.time_zone = zone.into();
57 self
58 }
59
60 pub fn order(mut self, order: KeyOrder) -> Self {
62 self.order = order;
63 self
64 }
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
70pub enum RowFormat {
71 #[default]
73 Array,
74 Object,
76}
77
78const FROM: &str = "from_json";
79const TO: &str = "to_json";
80
81pub(crate) fn invalid(call: &'static str, message: String) -> PondError {
82 PondError::InvalidData { call, message }
83}
84
85impl TimeSeries {
86 pub fn from_json(text: &str, options: &JsonOptions) -> Result<TimeSeries> {
141 read_json(&parse(FROM, text)?, options)
142 }
143
144 pub fn from_json_value(value: &Value, options: &JsonOptions) -> Result<TimeSeries> {
147 read_json(&Json::from_value(value), options)
148 }
149
150 pub fn to_json(&self, format: RowFormat) -> Result<String> {
168 refuse_decimals(TO, self.columns())?;
169 let mut out = Vec::with_capacity(64 + self.len() * 16 * (1 + self.columns().len()));
172 out.extend_from_slice(b"{\"name\":");
173 push_text(&mut out, self.name());
174 out.extend_from_slice(b",\"schema\":");
175 push_schema(&mut out, &self.schema());
176 out.extend_from_slice(b",\"rows\":[");
177 let field = |name: &str| {
179 let mut f = Vec::new();
180 push_text(&mut f, name);
181 f.push(b':');
182 f
183 };
184 let key_field = field(self.key_name());
185 let fields: Vec<Vec<u8>> = self.columns().iter().map(|c| field(c.name())).collect();
186 let object = format == RowFormat::Object;
187 for i in 0..self.len() {
188 if i > 0 {
189 out.push(b',');
190 }
191 out.push(if object { b'{' } else { b'[' });
192 if object {
193 out.extend_from_slice(&key_field);
194 }
195 self.push_key(&mut out, i, format);
196 for (c, column) in self.columns().iter().enumerate() {
197 out.push(b',');
198 if object {
199 out.extend_from_slice(&fields[c]);
200 }
201 push_cell(&mut out, column.data(), i);
202 }
203 out.push(if object { b'}' } else { b']' });
204 }
205 out.extend_from_slice(b"]}");
206 Ok(String::from_utf8(out).expect("JSON text is UTF-8"))
207 }
208
209 pub fn to_json_value(&self, format: RowFormat) -> Result<Value> {
214 Ok(serde_json::from_str(&self.to_json(format)?).expect("to_json writes JSON"))
215 }
216
217 fn push_key(&self, out: &mut Vec<u8>, i: usize, format: RowFormat) {
218 let begin = self.key().begin()[i];
219 let Some(end) = self.key().end() else {
220 push_int(out, begin);
221 return;
222 };
223 let object = format == RowFormat::Object;
224 out.push(if object { b'{' } else { b'[' });
225 if let Key::Interval(k) = self.key() {
226 if object {
227 out.extend_from_slice(b"\"value\":");
228 }
229 match &k.label {
230 Labels::Number(v) => push_number(out, v[i]),
231 Labels::String(s) => push_text(out, s.get(i).expect("labels have no gaps")),
232 }
233 out.push(b',');
234 }
235 if object {
236 out.extend_from_slice(b"\"start\":");
237 }
238 push_int(out, begin);
239 out.extend_from_slice(if object { b",\"end\":" } else { b"," });
240 push_int(out, end[i]);
241 out.push(if object { b'}' } else { b']' });
242 }
243}
244
245fn read_json(value: &Json, options: &JsonOptions) -> Result<TimeSeries> {
247 let name = read_name(FROM, value)?;
248 let schema = read_schema(FROM, value.get("schema"))?;
249 let rows = value
250 .get("rows")
251 .and_then(|v| v.as_array())
252 .ok_or_else(|| invalid(FROM, "expected \"rows\" as an array".into()))?;
253 read_rows(
254 FROM,
255 name,
256 &schema,
257 rows,
258 RowShape::Rows,
259 Some(&options.time_zone),
260 options.order,
261 )
262}
263
264#[derive(Clone, Copy, PartialEq, Eq)]
266pub(crate) enum RowShape {
267 Rows,
270 Points,
272}
273
274pub(crate) fn read_rows(
277 call: &'static str,
278 name: &str,
279 schema: &[(String, ColumnKind, bool)],
280 rows: &[Json],
281 shape: RowShape,
282 zone: Option<&str>,
283 order: KeyOrder,
284) -> Result<TimeSeries> {
285 let (key_name, key_kind) = (&schema[0].0, schema[0].1);
286 if order == KeyOrder::Clamp && key_kind != ColumnKind::Time {
287 return Err(invalid(
288 call,
289 format!(
290 "KeyOrder::Clamp is for point-in-time keys only; this key is '{}' (use KeyOrder::Reorder, or sort the rows first)",
291 key_kind.as_str()
292 ),
293 ));
294 }
295 if key_kind == ColumnKind::Value {
296 return Err(invalid(
297 call,
298 format!("the key '{key_name}' is a 'value' axis: read it with ValueSeries::from_json"),
299 ));
300 }
301 let read_time = |v: &Json, row: usize| read_time(call, v, row, zone);
302
303 let cells = row_cells(call, schema, rows, shape)?;
304 let n = rows.len();
305 let mut begin = Vec::with_capacity(n);
306 let mut end = Vec::with_capacity(n);
307 let mut labels: Vec<LabelCell> = Vec::new();
308 for (i, &key) in cells[0].iter().enumerate() {
309 match key_kind {
310 ColumnKind::Time => {
311 let t = read_time(key, i)?;
312 begin.push(t);
313 }
314 ColumnKind::TimeRange => {
315 let (b, e) = read_span(call, key, i, false)?;
316 begin.push(read_time(b, i)?);
317 end.push(read_time(e, i)?);
318 }
319 _ => {
320 let (b, e) = read_span(call, key, i, true)?;
321 let label = match key {
322 Json::Array(a) => &a[0],
323 _ => key.get("value").unwrap_or(NULL),
324 };
325 labels.push(read_label(call, label, i, labels.first())?);
326 begin.push(read_time(b, i)?);
327 end.push(read_time(e, i)?);
328 }
329 }
330 if let Some(&b) = end.last()
331 && b < *begin.last().expect("pushed with end")
332 {
333 return Err(invalid(
334 call,
335 format!(
336 "the key at row {i} ends ({b}) before it starts ({})",
337 begin[i]
338 ),
339 ));
340 }
341 }
342
343 let order: Option<Vec<usize>> = match order {
345 KeyOrder::Reorder => {
346 let mut order: Vec<usize> = (0..n).collect();
347 order.sort_by_key(|&i| (begin[i], end.get(i).copied().unwrap_or(0)));
348 Some(order)
349 }
350 KeyOrder::Clamp => {
351 for i in 1..n {
352 begin[i] = begin[i].max(begin[i - 1]);
353 }
354 None
355 }
356 KeyOrder::Strict => None,
357 };
358 let pick = |v: &mut Vec<i64>| {
359 if let Some(order) = order.as_ref().filter(|_| !v.is_empty()) {
360 *v = order.iter().map(|&i| v[i]).collect();
361 }
362 };
363 pick(&mut begin);
364 pick(&mut end);
365 let row_at = |r: usize| order.as_ref().map_or(r, |o| o[r]);
366
367 let columns = row_columns(call, &schema[1..], &cells[1..], n, &row_at)?;
368
369 let key = match key_kind {
370 ColumnKind::Time => Key::Time(begin),
371 ColumnKind::TimeRange => Key::TimeRange(TimeRangeKeys { begin, end }),
372 _ => {
373 let label = if matches!(labels.first(), Some(LabelCell::Number(_))) {
374 Labels::Number(
375 (0..n)
376 .map(|r| match labels[row_at(r)] {
377 LabelCell::Number(v) => v,
378 LabelCell::String(_) => unreachable!("one kind"),
379 })
380 .collect(),
381 )
382 } else {
383 Labels::String(StringColumn::from_options((0..n).map(|r| {
384 match &labels[row_at(r)] {
385 LabelCell::String(s) => Some(*s),
386 LabelCell::Number(_) => unreachable!("one kind"),
387 }
388 })))
389 };
390 Key::Interval(IntervalKeys { label, begin, end })
391 }
392 };
393 TimeSeries::with_key(call, name.to_string(), key_name.clone(), key, columns)
394}
395
396pub(crate) fn row_cells<'b, 'a>(
401 call: &'static str,
402 schema: &[(String, ColumnKind, bool)],
403 rows: &'b [Json<'a>],
404 shape: RowShape,
405) -> Result<Vec<Vec<&'b Json<'a>>>> {
406 let n = rows.len();
407 let mut cells: Vec<Vec<&Json>> = vec![Vec::with_capacity(n); schema.len()];
408 let names: Vec<&str> = std::iter::once(match shape {
411 RowShape::Points => "ts",
412 RowShape::Rows => schema[0].0.as_str(),
413 })
414 .chain(schema[1..].iter().map(|c| c.0.as_str()))
415 .collect();
416 let mut column_of: std::collections::HashMap<&str, usize> = Default::default();
417 for (c, name) in names.iter().enumerate().rev() {
418 column_of.insert(name, c);
419 }
420 let mut slots: Vec<&Json> = vec![NULL; schema.len()];
421 for (i, row) in rows.iter().enumerate() {
422 match row {
423 Json::Object(o) => {
424 slots.fill(NULL);
426 for (p, (k, v)) in o.entries().iter().enumerate() {
427 let c = match names.get(p) {
430 Some(name) if *name == k.as_ref() => Some(p),
431 _ => column_of.get(k.as_ref()).copied(),
432 };
433 if let Some(c) = c {
434 slots[c] = v;
435 }
436 }
437 }
438 _ if shape == RowShape::Points => {
439 return Err(invalid(call, format!("point {i} is not an object")));
440 }
441 Json::Array(a) if a.len() != schema.len() => {
442 return Err(invalid(
443 call,
444 format!(
445 "row {i} has {} values; the schema has {} columns",
446 a.len(),
447 schema.len()
448 ),
449 ));
450 }
451 Json::Array(a) => {
452 for (slot, v) in slots.iter_mut().zip(a) {
453 *slot = v;
454 }
455 }
456 _ => {
457 return Err(invalid(
458 call,
459 format!("row {i} is not an array or an object"),
460 ));
461 }
462 }
463 for (cells, v) in cells.iter_mut().zip(&slots) {
464 cells.push(v);
465 }
466 }
467 Ok(cells)
468}
469
470pub(crate) fn row_columns(
475 call: &'static str,
476 schema: &[(String, ColumnKind, bool)],
477 cells: &[Vec<&Json>],
478 n: usize,
479 row_at: &dyn Fn(usize) -> usize,
480) -> Result<Vec<Column>> {
481 let mut columns = Vec::with_capacity(schema.len());
482 for (c, (col_name, kind, required)) in schema.iter().enumerate() {
483 let cell = |r: usize| cells[c][row_at(r)];
484 let wrong = |r: usize, want: &str| {
485 invalid(
486 call,
487 format!(
488 "row {} column '{col_name}': expected {want}, got {}",
489 row_at(r),
490 cells[c][row_at(r)]
491 ),
492 )
493 };
494 let data: ColumnData = match kind {
495 ColumnKind::Number => Float64Column::from_options(
496 (0..n)
497 .map(|r| match cell(r) {
498 Json::Null => Ok(None),
499 Json::Number(v) => Ok(v.as_f64()),
500 _ => Err(wrong(r, "a number")),
501 })
502 .collect::<Result<Vec<_>>>()?,
503 )
504 .into(),
505 ColumnKind::String => StringColumn::from_options(
506 (0..n)
507 .map(|r| match cell(r) {
508 Json::Null => Ok(None),
509 Json::String(v) => Ok(Some(v.as_ref())),
510 _ => Err(wrong(r, "a string")),
511 })
512 .collect::<Result<Vec<_>>>()?,
513 )
514 .into(),
515 ColumnKind::Array => ArrayColumn::from_options(
516 (0..n)
517 .map(|r| match cell(r) {
518 Json::Null => Ok(None),
519 Json::Array(a) => read_array(call, a, row_at(r), col_name).map(Some),
520 _ => Err(wrong(r, "an array")),
521 })
522 .collect::<Result<Vec<_>>>()?,
523 )
524 .into(),
525 _ => BooleanColumn::from_options(
526 (0..n)
527 .map(|r| match cell(r) {
528 Json::Null => Ok(None),
529 Json::Bool(v) => Ok(Some(*v)),
530 _ => Err(wrong(r, "true or false")),
531 })
532 .collect::<Result<Vec<_>>>()?,
533 )
534 .into(),
535 };
536 columns.push(Column::from_data(col_name.clone(), data, *required));
537 }
538 Ok(columns)
539}
540
541pub(crate) fn push_schema(out: &mut Vec<u8>, schema: &[ColumnDef]) {
543 out.push(b'[');
544 for (i, def) in schema.iter().enumerate() {
545 if i > 0 {
546 out.push(b',');
547 }
548 out.extend_from_slice(b"{\"name\":");
549 push_text(out, &def.name);
550 out.extend_from_slice(b",\"kind\":");
551 push_text(out, def.kind.as_str());
552 if i > 0 && !def.required {
553 out.extend_from_slice(b",\"required\":false");
554 }
555 out.push(b'}');
556 }
557 out.push(b']');
558}
559
560pub(crate) fn push_cell(out: &mut Vec<u8>, data: &ColumnData, i: usize) {
562 match data {
563 ColumnData::Number(v) => match v.get(i) {
564 Some(x) => push_number(out, x),
565 None => out.extend_from_slice(b"null"),
566 },
567 ColumnData::String(v) => match v.get(i) {
568 Some(x) => push_text(out, x),
569 None => out.extend_from_slice(b"null"),
570 },
571 ColumnData::Boolean(v) => out.extend_from_slice(match v.get(i) {
572 Some(true) => b"true",
573 Some(false) => b"false",
574 None => b"null",
575 }),
576 ColumnData::Array(v) => match v.get(i) {
577 Some(x) => push_array(out, x),
578 None => out.extend_from_slice(b"null"),
579 },
580 ColumnData::Decimal(_) => unreachable!("decimals are refused first"),
581 }
582}
583
584pub(crate) fn push_array(out: &mut Vec<u8>, cell: &[Scalar]) {
586 out.push(b'[');
587 for (i, e) in cell.iter().enumerate() {
588 if i > 0 {
589 out.push(b',');
590 }
591 match e {
592 Scalar::Number(x) => push_number(out, *x),
593 Scalar::String(x) => push_text(out, x),
594 Scalar::Boolean(x) => out.extend_from_slice(if *x { b"true" } else { b"false" }),
595 }
596 }
597 out.push(b']');
598}
599
600pub(crate) fn refuse_decimals(call: &'static str, columns: &[Column]) -> Result<()> {
602 match columns.iter().find(|c| c.kind() == ColumnKind::Decimal) {
603 Some(c) => Err(PondError::Unsupported {
604 call,
605 message: format!(
606 "column '{}' holds exact decimals, which pond-ts's JSON has no kind for; map it to numbers first, or write Arrow, which keeps it exact",
607 c.name()
608 ),
609 }),
610 None => Ok(()),
611 }
612}
613
614pub(crate) fn push_int(out: &mut Vec<u8>, v: i64) {
615 use std::io::Write;
616 write!(out, "{v}").expect("writing to a Vec cannot fail");
617}
618
619pub(crate) fn push_text(out: &mut Vec<u8>, text: &str) {
621 serde_json::to_writer(out, text).expect("writing to a Vec cannot fail");
622}
623
624pub(crate) fn push_number(out: &mut Vec<u8>, v: f64) {
629 if !v.is_finite() {
630 out.extend_from_slice(b"null");
631 return;
632 }
633 if v == 0.0 {
634 out.push(b'0');
635 return;
636 }
637 if v.fract() == 0.0 && v.abs() < 9_007_199_254_740_992.0 {
639 push_int(out, v as i64); return;
641 }
642 let mut buffer = zmij::Buffer::new();
646 let text = buffer.format_finite(v.abs()).as_bytes();
647 let (mantissa, exp10) = match text.iter().position(|&b| b == b'e') {
648 Some(i) => (
649 &text[..i],
650 std::str::from_utf8(&text[i + 1..])
651 .ok()
652 .and_then(|e| e.parse::<i32>().ok())
653 .expect("zmij writes a whole exponent"),
654 ),
655 None => (text, 0),
656 };
657 let point = mantissa
658 .iter()
659 .position(|&b| b == b'.')
660 .unwrap_or(mantissa.len());
661 let mut all = [0u8; 40];
662 let mut len = 0;
663 for &b in mantissa.iter().filter(|b| **b != b'.') {
664 all[len] = b;
665 len += 1;
666 }
667 let lead = all[..len].iter().take_while(|&&b| b == b'0').count();
668 let mut end = len;
669 while end > lead + 1 && all[end - 1] == b'0' {
670 end -= 1;
671 }
672 let digits = &mut all[lead..end];
673 let n = point as i32 - lead as i32 + exp10;
674 js_tie_to_even(v.abs(), digits, n);
675 let digits: &[u8] = digits;
676 let k = digits.len() as i32;
677 if v < 0.0 {
678 out.push(b'-');
679 }
680 if k <= n && n <= 21 {
681 out.extend_from_slice(digits);
682 out.extend(std::iter::repeat_n(b'0', (n - k) as usize));
683 } else if 0 < n && n <= 21 {
684 out.extend_from_slice(&digits[..n as usize]);
685 out.push(b'.');
686 out.extend_from_slice(&digits[n as usize..]);
687 } else if -6 < n && n <= 0 {
688 out.extend_from_slice(b"0.");
689 out.extend(std::iter::repeat_n(b'0', (-n) as usize));
690 out.extend_from_slice(digits);
691 } else {
692 out.push(digits[0]);
693 if k > 1 {
694 out.push(b'.');
695 out.extend_from_slice(&digits[1..]);
696 }
697 out.push(b'e');
698 out.push(if n - 1 < 0 { b'-' } else { b'+' });
699 push_int(out, i64::from((n - 1).abs()));
700 }
701}
702
703fn js_tie_to_even(v: f64, digits: &mut [u8], n: i32) {
710 let k = digits.len();
711 let Some(exact) = exact_digits(v, k + 1) else {
712 return;
713 };
714 if exact.len() != k + 1 || exact[k] != b'5' {
715 return;
716 }
717 let lower = &exact[..k];
719 let mut upper = lower.to_vec();
720 let mut i = k;
721 loop {
722 if i == 0 {
723 return; }
725 i -= 1;
726 if upper[i] == b'9' {
727 upper[i] = b'0';
728 } else {
729 upper[i] += 1;
730 break;
731 }
732 }
733 let even: &[u8] = if lower[k - 1] % 2 == 0 { lower } else { &upper };
734 if *digits == *even {
735 return;
736 }
737 let mut text = String::from("0.");
738 text.push_str(std::str::from_utf8(even).expect("digits"));
739 if format!("{text}e{n}").parse::<f64>() == Ok(v) {
740 digits.copy_from_slice(even);
741 }
742}
743
744fn exact_digits(v: f64, most: usize) -> Option<Vec<u8>> {
749 let bits = v.to_bits();
750 let raw_exp = ((bits >> 52) & 0x7ff) as i32;
751 let frac = bits & ((1u64 << 52) - 1);
752 let (mut m, mut e) = if raw_exp == 0 {
753 (frac, -1074)
754 } else {
755 (frac | (1u64 << 52), raw_exp - 1075)
756 };
757 let zeros = m.trailing_zeros();
758 m >>= zeros;
759 e += zeros as i32;
760 let value: u128 = if e >= 0 {
761 if e as u32 + (64 - m.leading_zeros()) > 127 {
766 return None;
767 }
768 u128::from(m) << e
769 } else {
770 if -e > 26 {
772 return None;
773 }
774 u128::from(m) * 5u128.pow((-e) as u32)
775 };
776 let text = value.to_string();
777 let significant = text.trim_end_matches('0');
778 (significant.len() <= most).then(|| significant.as_bytes().to_vec())
779}
780
781pub(crate) fn parse<'a>(call: &'static str, text: &'a str) -> Result<Json<'a>> {
783 serde_json::from_str(text).map_err(|e| invalid(call, format!("the text is not JSON: {e}")))
784}
785
786pub(crate) fn read_name<'a>(call: &'static str, value: &'a Json<'_>) -> Result<&'a str> {
788 value
789 .get("name")
790 .and_then(|v| v.as_str())
791 .ok_or_else(|| invalid(call, "expected a \"name\" that is a string".into()))
792}
793
794pub(crate) fn read_array(
796 call: &'static str,
797 cell: &[Json],
798 row: usize,
799 column: &str,
800) -> Result<Vec<Scalar>> {
801 cell.iter()
802 .enumerate()
803 .map(|(e, v)| match v {
804 Json::Number(x) => Ok(Scalar::Number(x.as_f64().expect("always a number"))),
805 Json::String(x) => Ok(Scalar::String(x.to_string())),
806 Json::Bool(x) => Ok(Scalar::Boolean(*x)),
807 _ => Err(invalid(
808 call,
809 format!(
810 "row {row} column '{column}': array element {e} must be a finite number, string, or boolean, got {v}"
811 ),
812 )),
813 })
814 .collect()
815}
816
817pub(crate) fn read_schema(
819 call: &'static str,
820 schema: Option<&Json>,
821) -> Result<Vec<(String, ColumnKind, bool)>> {
822 let entries = schema
823 .and_then(|v| v.as_array())
824 .ok_or_else(|| invalid(call, "expected \"schema\" as an array".into()))?;
825 if entries.is_empty() {
826 return Err(invalid(
827 call,
828 "the schema needs at least a key column".into(),
829 ));
830 }
831 entries
832 .iter()
833 .enumerate()
834 .map(|(c, entry)| {
835 let name = entry
836 .get("name")
837 .and_then(|v| v.as_str())
838 .ok_or_else(|| invalid(call, format!("schema column {c} has no \"name\"")))?;
839 let kind_text = entry.get("kind").and_then(|v| v.as_str()).unwrap_or("");
840 let kind = match (c, kind_text) {
841 (0, "time") => ColumnKind::Time,
842 (0, "timeRange") => ColumnKind::TimeRange,
843 (0, "interval") => ColumnKind::Interval,
844 (0, "value") => ColumnKind::Value,
845 (0, other) => {
846 return Err(invalid(call, format!(
847 "the first schema column is the key, and must be 'time', 'timeRange', 'interval' or (for a ValueSeries) 'value'; got '{other}'"
848 )));
849 }
850 (_, "number") => ColumnKind::Number,
851 (_, "string") => ColumnKind::String,
852 (_, "boolean") => ColumnKind::Boolean,
853 (_, "array") => ColumnKind::Array,
854 (_, other) => {
855 return Err(invalid(call, format!(
856 "column '{name}' has kind '{other}'; value columns are 'number', 'string', 'boolean' or 'array'"
857 )));
858 }
859 };
860 let required = entry.get("required") != Some(&Json::Bool(false));
862 Ok((name.to_string(), kind, required))
863 })
864 .collect()
865}
866
867pub(crate) fn read_time(
870 call: &'static str,
871 v: &Json,
872 row: usize,
873 zone: Option<&str>,
874) -> Result<i64> {
875 match v {
876 Json::Number(n) => {
877 if let Some(t) = n.as_i64() {
878 return Ok(t);
879 }
880 let f = n.as_f64().unwrap_or(f64::NAN);
881 if f.fract() == 0.0 && f.abs() < 9.3e18 {
883 return Ok(f as i64);
884 }
885 Err(invalid(
886 call,
887 format!(
888 "row {row}: the time {n} is not a whole number of milliseconds (pond-rs times are whole milliseconds)"
889 ),
890 ))
891 }
892 Json::String(text) if zone.is_some() => {
893 let zone = zone.unwrap_or_default();
894 crate::parse_time_in(text, zone).map_err(|e| invalid(call, format!("row {row}: {e}")))
895 }
896 other if zone.is_some() => Err(invalid(
897 call,
898 format!(
899 "row {row}: expected a time as a number of milliseconds or as text, got {other}"
900 ),
901 )),
902 other => Err(invalid(
903 call,
904 format!("row {row}: expected a time as a number of milliseconds, got {other}"),
905 )),
906 }
907}
908
909pub(crate) fn read_span<'a>(
912 call: &'static str,
913 v: &'a Json<'a>,
914 row: usize,
915 interval: bool,
916) -> Result<(&'a Json<'a>, &'a Json<'a>)> {
917 match v {
918 Json::Array(a) if !interval && a.len() == 2 => Ok((&a[0], &a[1])),
919 Json::Array(a) if interval && a.len() == 3 => Ok((&a[1], &a[2])),
920 Json::Object(o)
921 if o.contains_key("start")
922 && o.contains_key("end")
923 && o.contains_key("value") == interval =>
924 {
925 Ok((o.get("start").unwrap_or(NULL), o.get("end").unwrap_or(NULL)))
926 }
927 _ if interval => Err(invalid(
928 call,
929 format!(
930 "row {row}: expected an interval as [label, start, end] or {{\"value\", \"start\", \"end\"}}, got {v}"
931 ),
932 )),
933 _ => Err(invalid(
934 call,
935 format!(
936 "row {row}: expected a time range as [start, end] or {{\"start\", \"end\"}}, got {v}"
937 ),
938 )),
939 }
940}
941
942pub(crate) enum LabelCell<'a> {
943 Number(f64),
944 String(&'a str),
945}
946
947pub(crate) fn read_label<'a>(
949 call: &'static str,
950 v: &'a Json<'_>,
951 row: usize,
952 first: Option<&LabelCell>,
953) -> Result<LabelCell<'a>> {
954 let label = match v {
955 Json::String(s) => LabelCell::String(s.as_ref()),
956 Json::Number(n) => LabelCell::Number(n.as_f64().expect("JSON numbers are finite")),
957 other => {
958 return Err(invalid(
959 call,
960 format!("row {row}: an interval label must be text or a number, got {other}"),
961 ));
962 }
963 };
964 match (first, &label) {
965 (Some(LabelCell::Number(_)), LabelCell::String(_))
966 | (Some(LabelCell::String(_)), LabelCell::Number(_)) => Err(invalid(
967 call,
968 format!(
969 "row {row}: interval labels must all be text or all numbers; this one is {v}, unlike row 0's"
970 ),
971 )),
972 _ => Ok(label),
973 }
974}
975
976#[cfg(test)]
977mod tests {
978 use super::*;
979
980 fn time_series(rows: &str) -> String {
981 format!(
982 r#"{{"name":"t","schema":[{{"name":"time","kind":"time"}},{{"name":"v","kind":"number","required":false}}],"rows":{rows}}}"#
983 )
984 }
985
986 #[test]
987 fn numbers_are_written_as_javascript_writes_them() {
988 let text = |v: f64| {
989 let mut out = Vec::new();
990 push_number(&mut out, v);
991 String::from_utf8(out).unwrap()
992 };
993 assert_eq!(text(-0.0), "0");
994 assert_eq!(text(2.0), "2");
995 assert_eq!(text(0.1), "0.1");
996 assert_eq!(text(f64::INFINITY), "null");
997 assert_eq!(text(f64::NAN), "null");
998 assert_eq!(text(1e300), "1e+300"); assert_eq!(text(2f64.powi(53)), "9007199254740992");
1000 assert_eq!(text(1e-7), "1e-7");
1001 assert_eq!(text(1e-6), "0.000001");
1002 assert_eq!(text(-1.5e-6), "-0.0000015");
1003 assert_eq!(text(1e19), "10000000000000000000");
1004 assert_eq!(text(1.2345678901234568e20), "123456789012345680000");
1005 assert_eq!(text(1e21), "1e+21");
1006 assert_eq!(text(-1.5e21), "-1.5e+21");
1007 assert_eq!(text(5e-324), "5e-324");
1008 }
1009
1010 #[test]
1011 fn clamp_and_reorder() {
1012 let text = time_series("[[3000,1],[1000,2],[1000,3]]");
1013 assert!(TimeSeries::from_json(&text, &JsonOptions::new()).is_err());
1014 let s = TimeSeries::from_json(&text, &JsonOptions::new().order(KeyOrder::Clamp)).unwrap();
1015 assert_eq!(s.key().begin(), &[3000, 3000, 3000]);
1016 let s = TimeSeries::from_json(&text, &JsonOptions::new().order(KeyOrder::Reorder)).unwrap();
1017 assert_eq!(s.key().begin(), &[1000, 1000, 3000]);
1018 let v = s.column("v").unwrap().numbers().unwrap();
1019 assert_eq!(
1020 (v.get(0), v.get(1), v.get(2)),
1021 (Some(2.0), Some(3.0), Some(1.0))
1022 );
1023 }
1024
1025 #[test]
1026 fn round_trips_and_refuses_fractions_and_decimals() {
1027 let text = time_series("[[0,1.5],[1000,null]]");
1028 let s = TimeSeries::from_json(&text, &JsonOptions::new()).unwrap();
1029 assert_eq!(s.to_json(RowFormat::Array).unwrap(), text);
1030 assert!(TimeSeries::from_json(&time_series("[[1.5,1]]"), &JsonOptions::new()).is_err());
1031 assert!(TimeSeries::from_json("{", &JsonOptions::new()).is_err());
1032 let d = s
1033 .with_column(Column::required(
1034 "d",
1035 crate::DecimalColumn::from_values(vec![1, 2], 1).unwrap(),
1036 ))
1037 .unwrap();
1038 assert!(d.to_json(RowFormat::Object).is_err());
1039 }
1040}