use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use perspective_js::utils::ApiError;
use super::plugin_config::PluginScopedConfig;
use crate::config::{ColumnConfigFieldUpdate, PluginStaticConfig};
#[derive(Clone, Default)]
pub struct RendererState {
pub plugin: Option<PluginRef>,
pub buckets: Rc<HashMap<String, PluginScopedConfig>>,
pub theme: Option<String>,
}
#[derive(Clone)]
pub struct PluginRef {
pub idx: usize,
pub static_config: Rc<PluginStaticConfig>,
}
impl RendererState {
pub fn with_theme(&self, theme: Option<String>) -> Self {
Self {
theme,
..self.clone()
}
}
pub fn with_plugin(&self, plugin: PluginRef) -> Self {
Self {
plugin: Some(plugin),
..self.clone()
}
}
pub fn without_plugins(&self) -> Self {
Self {
plugin: None,
buckets: Rc::default(),
..self.clone()
}
}
pub fn bucket(&self, name: &str) -> PluginScopedConfig {
self.buckets.get(name).cloned().unwrap_or_default()
}
pub fn with_bucket(&self, name: &str, bucket: PluginScopedConfig) -> Self {
let mut buckets = (*self.buckets).clone();
buckets.insert(name.to_owned(), bucket);
Self {
buckets: Rc::new(buckets),
..self.clone()
}
}
}
#[derive(Clone)]
pub struct RenderError(pub ApiError);
impl PartialEq for RenderError {
fn eq(&self, other: &Self) -> bool {
self.0.to_string() == other.0.to_string()
}
}
impl std::fmt::Debug for RenderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("RenderError")
.field(&self.0.to_string())
.finish()
}
}
#[derive(Clone, Default)]
pub enum RenderOutcome {
#[default]
Never,
Ok,
Failed(RenderError),
}
#[derive(Clone)]
pub enum StagedEdit {
PluginField(ColumnConfigFieldUpdate),
ColumnField {
column: String,
update: ColumnConfigFieldUpdate,
},
PluginConfig(serde_json::Map<String, serde_json::Value>),
Theme(Option<String>),
}
#[derive(Default)]
pub(super) struct StagedEdits {
next_id: std::cell::Cell<u64>,
edits: RefCell<Vec<(u64, StagedEdit)>>,
}
impl StagedEdits {
pub fn stage(self: &Rc<Self>, edit: StagedEdit) -> Staged {
let id = self.next_id.get();
self.next_id.set(id + 1);
self.edits.borrow_mut().push((id, edit));
Staged {
id,
edits: Rc::downgrade(self),
}
}
pub fn pending(&self) -> Vec<StagedEdit> {
self.edits.borrow().iter().map(|x| x.1.clone()).collect()
}
}
pub struct Staged {
id: u64,
edits: std::rc::Weak<StagedEdits>,
}
impl Drop for Staged {
fn drop(&mut self) {
if let Some(edits) = self.edits.upgrade() {
edits.edits.borrow_mut().retain(|x| x.0 != self.id);
}
}
}
pub(super) fn project_bucket(
mut bucket: PluginScopedConfig,
pending: Vec<StagedEdit>,
) -> PluginScopedConfig {
for edit in pending {
match edit {
StagedEdit::PluginField(update) => {
for key in &update.keys {
match update.value.get(key) {
Some(value) => {
bucket.plugin.insert(key.clone(), value.clone());
},
None => {
bucket.plugin.remove(key);
},
}
}
},
StagedEdit::ColumnField { column, update } => {
let entry = bucket.columns.entry(column.clone()).or_default();
for key in &update.keys {
entry.remove(key);
}
for (key, value) in update.value {
if update.keys.contains(&key) {
entry.insert(key, value);
}
}
if entry.is_empty() {
bucket.columns.remove(&column);
}
},
StagedEdit::PluginConfig(map) => bucket.plugin.extend(map),
StagedEdit::Theme(_) => {},
}
}
bucket
}