runtime/globals/console/
table.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 */
6
7use std::borrow::Cow;
8use std::fmt;
9use std::fmt::{Display, Formatter};
10use std::iter::{empty, once, repeat_with};
11
12use either::Either;
13use indexmap::IndexSet;
14use ion::conversions::FromValue as _;
15use ion::format::key::{KeyDisplay, format_key};
16use ion::format::{Config, format_value};
17use ion::{Context, Object, OwnedKey, Result};
18
19fn combine_keys(_: &Context, indexes: IndexSet<i32>, headers: IndexSet<String>) -> IndexSet<OwnedKey<'_>> {
20	let mut indexes: Vec<i32> = indexes.into_iter().collect();
21	indexes.sort_unstable();
22
23	let mut keys: IndexSet<OwnedKey> = indexes.into_iter().map(OwnedKey::Int).collect();
24	keys.extend(headers.into_iter().map(OwnedKey::String));
25	keys
26}
27
28pub(crate) fn sort_keys<'cx, I: IntoIterator<Item = Result<OwnedKey<'cx>>>>(
29	cx: &'cx Context, unsorted: I,
30) -> ion::Result<IndexSet<OwnedKey<'cx>>> {
31	let mut indexes = IndexSet::<i32>::new();
32	let mut headers = IndexSet::<String>::new();
33
34	for key in unsorted {
35		match key {
36			Ok(OwnedKey::Int(index)) => indexes.insert(index),
37			Ok(OwnedKey::String(header)) => headers.insert(header),
38			Err(e) => return Err(e),
39			_ => false,
40		};
41	}
42
43	Ok(combine_keys(cx, indexes, headers))
44}
45
46pub(crate) enum Cell<'cx> {
47	Key(KeyDisplay<'cx>),
48	String(Cow<'static, str>),
49}
50
51impl Display for Cell<'_> {
52	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
53		match self {
54			Cell::Key(key) => key.fmt(f),
55			Cell::String(string) => Display::fmt(string, f),
56		}
57	}
58}
59
60pub(crate) fn get_cells<'cx>(
61	cx: &'cx Context, object: &Object, rows: &'cx IndexSet<OwnedKey>, columns: &'cx IndexSet<OwnedKey>,
62	has_values: bool,
63) -> impl IntoIterator<Item = impl IntoIterator<Item = Cell<'cx>>> {
64	rows.iter().map(move |row| {
65		let Ok(Some(value)) = object.get(cx, row) else {
66			return Either::Left(empty());
67		};
68		let key = Cell::Key(format_key(cx, Config::default(), row));
69
70		if let Ok(object) = Object::from_value(cx, &value, true, ()) {
71			let cells = columns.iter().map(move |column| match object.get(cx, column) {
72				Ok(Some(val)) => Cell::String(Cow::Owned(
73					format_value(cx, Config::default().multiline(false).quoted(true), &val).to_string(),
74				)),
75				_ => Cell::String(Cow::Borrowed("")),
76			});
77
78			let cells = once(key).chain(cells);
79			let cells = if has_values {
80				Either::Left(cells.chain(once(Cell::String(Cow::Borrowed("")))))
81			} else {
82				Either::Right(cells)
83			};
84
85			Either::Right(Either::Right(cells))
86		} else {
87			let cells = once(key).chain(repeat_with(|| Cell::String(Cow::Borrowed(""))).take(columns.len()));
88			let cells = if has_values {
89				Either::Left(cells.chain(once(Cell::String(Cow::Owned(
90					format_value(cx, Config::default().multiline(false).quoted(true), &value).to_string(),
91				)))))
92			} else {
93				Either::Right(cells)
94			};
95			Either::Right(Either::Left(cells))
96		}
97	})
98}