use std::rc::Rc;
use perspective_client::config::{ColumnType, Filter, ViewConfig};
use perspective_client::{Client, Description, Table};
use super::TableErrorState;
use super::metadata::SessionMetadata;
#[derive(Clone, Default)]
pub struct PanelState {
pub chrome: Chrome,
pub binding: Binding,
pub overlay: Rc<Vec<OverlayClause>>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct OverlayClause {
pub filter: Filter,
pub column_type: Option<ColumnType>,
}
impl From<Filter> for OverlayClause {
fn from(filter: Filter) -> Self {
Self {
filter,
column_type: None,
}
}
}
pub fn effective(
config: &ViewConfig,
overlay: &[OverlayClause],
type_of: &dyn Fn(&str) -> Option<ColumnType>,
) -> (ViewConfig, Vec<usize>) {
let mut effective = config.clone();
let mut skipped = Vec::new();
for (idx, clause) in overlay.iter().enumerate() {
let applies = match (type_of(clause.filter.column()), clause.column_type) {
(Some(listener), Some(broadcaster)) => listener == broadcaster,
(Some(_), None) => true,
(None, _) => false,
};
if applies {
effective.filter.push(clause.filter.clone());
} else {
skipped.push(idx);
}
}
(effective, skipped)
}
#[derive(Clone, Default)]
pub struct Chrome {
pub title: Option<String>,
}
#[derive(Clone)]
pub enum Binding {
Unbound {
client: Option<Client>,
config: Rc<ViewConfig>,
},
Awaiting {
client: Client,
name: String,
config: Rc<ViewConfig>,
},
Bound(Rc<BoundState>),
Lost {
prior: Rc<Binding>,
error: TableErrorState,
},
}
impl Default for Binding {
fn default() -> Self {
Self::Unbound {
client: None,
config: Rc::default(),
}
}
}
#[derive(Clone)]
pub struct BoundState {
pub table: Table,
pub metadata: Rc<SessionMetadata>,
pub config: Rc<ViewConfig>,
pub description: Option<(Rc<ViewConfig>, Rc<Description>)>,
}
impl Binding {
fn live(&self) -> &Binding {
match self {
Binding::Lost { prior, .. } => prior,
binding => binding,
}
}
fn map_live(&self, f: impl FnOnce(&Binding) -> Binding) -> Binding {
match self {
Binding::Lost { prior, error } => Binding::Lost {
prior: Rc::new(f(prior)),
error: error.clone(),
},
binding => f(binding),
}
}
}
impl PanelState {
pub fn config(&self) -> &Rc<ViewConfig> {
match self.binding.live() {
Binding::Unbound { config, .. } | Binding::Awaiting { config, .. } => config,
Binding::Bound(bound) => &bound.config,
Binding::Lost { .. } => unreachable!("`Lost::prior` is never `Lost`"),
}
}
pub fn client(&self) -> Option<Client> {
match self.binding.live() {
Binding::Unbound { client, .. } => client.clone(),
Binding::Awaiting { client, .. } => Some(client.clone()),
Binding::Bound(bound) => Some(bound.table.get_client()),
Binding::Lost { .. } => unreachable!("`Lost::prior` is never `Lost`"),
}
}
pub fn bound(&self) -> Option<&Rc<BoundState>> {
match self.binding.live() {
Binding::Bound(bound) => Some(bound),
_ => None,
}
}
pub fn awaiting(&self) -> Option<&str> {
match self.binding.live() {
Binding::Awaiting { name, .. } => Some(name),
_ => None,
}
}
pub fn lost(&self) -> Option<&TableErrorState> {
match &self.binding {
Binding::Lost { error, .. } => Some(error),
_ => None,
}
}
pub fn with_lost(&self, error: TableErrorState) -> Self {
Self {
binding: Binding::Lost {
prior: Rc::new(self.binding.live().clone()),
error,
},
..self.clone()
}
}
pub fn recovered(&self) -> Self {
Self {
binding: self.binding.live().clone(),
..self.clone()
}
}
pub fn with_config(&self, config: Rc<ViewConfig>) -> Self {
let binding = self.binding.map_live(|binding| match binding {
Binding::Unbound { client, .. } => Binding::Unbound {
client: client.clone(),
config,
},
Binding::Awaiting { client, name, .. } => Binding::Awaiting {
client: client.clone(),
name: name.clone(),
config,
},
Binding::Bound(bound) => Binding::Bound(Rc::new(BoundState {
config,
description: None,
..(**bound).clone()
})),
Binding::Lost { .. } => unreachable!("`Lost::prior` is never `Lost`"),
});
Self {
binding,
..self.clone()
}
}
pub fn unbound(&self) -> Self {
self.unbound_on(self.client())
}
pub fn unbound_on(&self, client: Option<Client>) -> Self {
Self {
binding: Binding::Unbound {
client,
config: self.config().clone(),
},
..self.clone()
}
}
pub fn awaiting_table(&self, client: Client, name: String) -> Self {
Self {
binding: Binding::Awaiting {
client,
name,
config: self.config().clone(),
},
..self.clone()
}
}
pub fn with_overlay(&self, overlay: Rc<Vec<OverlayClause>>) -> Self {
Self {
overlay,
..self.with_description(None)
}
}
pub fn bound_to(&self, table: Table, metadata: SessionMetadata) -> Self {
Self {
binding: Binding::Bound(Rc::new(BoundState {
table,
metadata: Rc::new(metadata),
config: self.config().clone(),
description: None,
})),
..self.clone()
}
}
pub fn with_metadata(&self, metadata: Rc<SessionMetadata>) -> Self {
let binding = self.binding.map_live(|binding| match binding {
Binding::Bound(bound) => Binding::Bound(Rc::new(BoundState {
metadata,
..(**bound).clone()
})),
binding => binding.clone(),
});
Self {
binding,
..self.clone()
}
}
pub fn with_description(&self, description: Option<(Rc<ViewConfig>, Rc<Description>)>) -> Self {
let binding = self.binding.map_live(|binding| match binding {
Binding::Bound(bound) => Binding::Bound(Rc::new(BoundState {
description,
..(**bound).clone()
})),
binding => binding.clone(),
});
Self {
binding,
..self.clone()
}
}
pub fn with_title(&self, title: Option<String>) -> Self {
Self {
chrome: Chrome { title },
..self.clone()
}
}
}