use std::collections::BTreeMap;
use std::fmt;
pub use egui::{Color32, Rgba};
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;
use crate::timestamp::{Interval, Timestamp};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
pub struct EntryID(Vec<i64>);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub enum EntryIndex {
Summary,
Slot(u64),
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DataSourceInfo {
pub entry_info: EntryInfo,
pub interval: Interval,
pub tile_set: TileSet,
pub field_schema: FieldSchema,
#[serde(default)]
pub warning_message: Option<String>,
#[serde(default)]
pub nonempty_tiles: NonemptyTiles,
#[serde(default = "SampleFormat::center")]
pub sample_format: SampleFormat,
}
impl DataSourceInfo {
pub fn is_empty_tile(&self, entry_id: &EntryID, tile_id: TileID) -> bool {
self.nonempty_tiles.is_empty_tile(entry_id, tile_id)
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum EntryInfo {
Panel {
short_name: String,
long_name: String,
summary: Option<Box<EntryInfo>>,
slots: Vec<EntryInfo>,
},
Slot {
short_name: String,
long_name: String,
max_rows: u64,
},
Summary {
color: Color32,
},
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct NonemptyTiles(BTreeMap<EntryID, BTreeSet<TileID>>);
impl NonemptyTiles {
pub fn new() -> Self {
Default::default()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn append(&mut self, other: &mut NonemptyTiles) {
self.0.append(&mut other.0);
}
pub fn is_empty_tile(&self, entry_id: &EntryID, tile_id: TileID) -> bool {
!self
.0
.get(entry_id)
.is_none_or(|tiles| tiles.contains(&tile_id))
}
pub fn mark_nonempty(&mut self, entry_id: &EntryID, tile_id: TileID) {
self.0
.entry(entry_id.to_owned())
.or_default()
.insert(tile_id);
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Deserialize, Serialize)]
pub enum SampleFormat {
Start,
Center,
}
impl SampleFormat {
pub(crate) const fn center() -> Self {
Self::Center
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Default, Deserialize, Serialize)]
pub struct UtilPoint {
pub time: Timestamp,
pub util: f32,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ItemLink {
pub item_uid: ItemUID,
pub title: String,
pub interval: Interval,
pub entry_id: EntryID,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub struct FieldID(usize);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub struct FieldSchema {
field_ids: BTreeMap<String, FieldID>,
field_names: BTreeMap<FieldID, String>,
searchable: BTreeSet<FieldID>,
}
impl FieldSchema {
pub fn new() -> Self {
Self {
field_ids: BTreeMap::new(),
field_names: BTreeMap::new(),
searchable: BTreeSet::new(),
}
}
pub fn insert(&mut self, field_name: String, searchable: bool) -> FieldID {
if let Some(field_id) = self.field_ids.get(&field_name) {
return *field_id;
}
let next_id = FieldID(
self.field_names
.last_key_value()
.map(|(v, _)| v.0 + 1)
.unwrap_or(0),
);
self.field_ids.insert(field_name.clone(), next_id);
self.field_names.insert(next_id, field_name);
if searchable {
self.searchable.insert(next_id);
}
next_id
}
pub fn get_id(&self, field_name: &str) -> Option<FieldID> {
self.field_ids.get(field_name).copied()
}
pub fn get_name(&self, field_id: FieldID) -> Option<&str> {
self.field_names.get(&field_id).map(|x| x.as_str())
}
pub fn contains_id(&self, field_id: FieldID) -> bool {
self.field_names.contains_key(&field_id)
}
pub fn contains_name(&self, field_name: &str) -> bool {
self.field_ids.contains_key(field_name)
}
pub fn field_names(&self) -> &BTreeMap<FieldID, String> {
&self.field_names
}
pub fn searchable(&self) -> &BTreeSet<FieldID> {
&self.searchable
}
}
impl Default for FieldSchema {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum Field {
I64(i64),
U64(u64),
String(String),
Interval(Interval),
ItemLink(ItemLink),
Vec(Vec<Field>),
Empty,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub struct ItemUID(pub u64);
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Item {
pub item_uid: ItemUID,
pub interval: Interval,
pub color: Color32,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ItemField(
pub FieldID,
pub Field,
#[serde(default)] pub Option<Color32>,
);
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ItemMeta {
pub item_uid: ItemUID,
pub original_interval: Interval,
pub title: String,
pub fields: Vec<ItemField>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
pub struct TileID(pub Interval);
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub struct TileSet {
pub tiles: Vec<Vec<TileID>>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct SummaryTileData {
pub utilization: Vec<UtilPoint>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct SummaryTile {
pub entry_id: EntryID,
pub tile_id: TileID,
pub data: SummaryTileData,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct SlotTileData {
pub items: Vec<Vec<Item>>, }
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct SlotTile {
pub entry_id: EntryID,
pub tile_id: TileID,
pub data: SlotTileData,
}
impl SlotTile {
pub fn is_empty(&self) -> bool {
self.data.items.iter().all(|row| row.is_empty())
}
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct SlotMetaTileData {
pub items: Vec<Vec<ItemMeta>>, }
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct SlotMetaTile {
pub entry_id: EntryID,
pub tile_id: TileID,
pub data: SlotMetaTileData,
}
impl SlotMetaTile {
pub fn is_empty(&self) -> bool {
self.data.items.iter().all(|row| row.is_empty())
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DataSourceDescription {
pub source_locator: Vec<String>,
}
pub type Result<T> = std::result::Result<T, String>;
pub trait DataSource {
fn fetch_description(&self) -> DataSourceDescription;
fn fetch_info(&self) -> Result<DataSourceInfo>;
fn fetch_summary_tile(
&self,
entry_id: &EntryID,
tile_id: TileID,
full: bool,
) -> Result<SummaryTile>;
fn fetch_slot_tile(&self, entry_id: &EntryID, tile_id: TileID, full: bool) -> Result<SlotTile>;
fn fetch_slot_meta_tile(
&self,
entry_id: &EntryID,
tile_id: TileID,
full: bool,
) -> Result<SlotMetaTile>;
}
impl EntryID {
pub fn root() -> Self {
Self(Vec::new())
}
pub fn summary(&self) -> Self {
let mut result = self.clone();
result.0.push(-1);
result
}
pub fn child(&self, index: u64) -> Self {
let mut result = self.clone();
result
.0
.push(index.try_into().expect("unable to fit in i64"));
result
}
pub fn level(&self) -> u64 {
self.0.len() as u64
}
pub fn last_slot_index(&self) -> Option<u64> {
let last = self.0.last()?;
(*last).try_into().ok()
}
pub fn slot_index(&self, level: u64) -> Option<u64> {
let last = self.0.get(level as usize)?;
(*last).try_into().ok()
}
pub fn last_index(&self) -> Option<EntryIndex> {
let last = self.0.last()?;
Some(
(*last)
.try_into()
.map_or(EntryIndex::Summary, EntryIndex::Slot),
)
}
pub fn index(&self, level: u64) -> Option<EntryIndex> {
let last = self.0.get(level as usize)?;
Some(
(*last)
.try_into()
.map_or(EntryIndex::Summary, EntryIndex::Slot),
)
}
pub fn has_prefix(&self, prefix: &EntryID) -> bool {
if prefix.0.len() > self.0.len() {
return false;
}
for (a, b) in self.0.iter().zip(prefix.0.iter()) {
if a != b {
return false;
}
}
true
}
pub fn from_slug(s: &str) -> std::result::Result<Self, std::num::ParseIntError> {
let elts: std::result::Result<Vec<_>, _> = s.split('_').map(|x| x.parse::<i64>()).collect();
Ok(Self(elts?))
}
}
impl EntryInfo {
pub fn get(&self, entry_id: &EntryID) -> Option<&EntryInfo> {
let mut result = self;
for i in 0..entry_id.level() {
match (entry_id.index(i)?, result) {
(EntryIndex::Summary, EntryInfo::Panel { summary, .. }) => {
return summary.as_deref();
}
(EntryIndex::Slot(j), EntryInfo::Panel { slots, .. }) => {
result = slots.get(j as usize)?;
}
_ => panic!("EntryID and EntryInfo do not match"),
}
}
Some(result)
}
pub fn nodes(&self) -> u64 {
if let EntryInfo::Panel { slots, .. } = self {
slots.len() as u64
} else {
unreachable!()
}
}
pub fn kinds(&self) -> Vec<String> {
if let EntryInfo::Panel { slots: nodes, .. } = self {
let mut result = Vec::new();
let mut set = BTreeSet::new();
for node in nodes {
if let EntryInfo::Panel { slots: kinds, .. } = node {
for kind in kinds {
if let EntryInfo::Panel { short_name, .. } = kind {
if set.insert(short_name) {
result.push(short_name.clone());
}
} else {
unreachable!();
}
}
} else {
unreachable!();
}
}
return result;
}
unreachable!()
}
}
#[derive(Debug)]
pub enum SlugParseError {
ParseInt(std::num::ParseIntError),
TooFewValues,
TooManyValues,
}
impl fmt::Display for SlugParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SlugParseError::ParseInt(..) => write!(f, "parse error"),
SlugParseError::TooFewValues => write!(f, "too few values"),
SlugParseError::TooManyValues => write!(f, "too many values"),
}
}
}
impl std::error::Error for SlugParseError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
SlugParseError::ParseInt(e) => Some(e),
SlugParseError::TooFewValues => None,
SlugParseError::TooManyValues => None,
}
}
}
impl From<std::num::ParseIntError> for SlugParseError {
fn from(e: std::num::ParseIntError) -> SlugParseError {
SlugParseError::ParseInt(e)
}
}
impl TileID {
pub fn from_slug(s: &str) -> std::result::Result<Self, SlugParseError> {
let elts: std::result::Result<Vec<i64>, _> =
s.split('_').map(|x| x.parse::<i64>()).collect();
match elts?.as_slice() {
[start, stop] => Ok(Self(Interval::new(Timestamp(*start), Timestamp(*stop)))),
[_] => Err(SlugParseError::TooFewValues),
[] => Err(SlugParseError::TooFewValues),
_ => Err(SlugParseError::TooManyValues),
}
}
}
pub struct EntryIDSlug<'a>(pub &'a EntryID);
impl fmt::Display for EntryIDSlug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, e) in self.0.0.iter().enumerate() {
write!(f, "{}", e)?;
if i < self.0.0.len() - 1 {
write!(f, "_")?;
}
}
Ok(())
}
}
pub struct TileIDSlug(pub TileID);
impl fmt::Display for TileIDSlug {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0.0.start.0)?;
write!(f, "_")?;
write!(f, "{}", self.0.0.stop.0)
}
}
impl EntryID {
pub(crate) fn shift_level0(&self, level0_offset: i64) -> EntryID {
assert!(!self.0.is_empty());
assert_ne!(self.0[0], -1);
let mut result = self.clone();
result.0[0] += level0_offset;
assert!(result.0[0] >= 0);
result
}
}