use perspective_client::clone;
use perspective_client::utils::PerspectiveResultExt;
use crate::config::*;
use crate::presentation::Presentation;
use crate::renderer::Renderer;
use crate::session::{MissingTable, ResetOptions, Session, TableIntermediateState, probe_table};
use crate::tasks::*;
use crate::workspace::Workspace;
use crate::*;
pub(crate) enum RestoreMode {
Existing { active: bool },
Fresh,
}
#[derive(Clone, Copy)]
pub(crate) enum RestoreErrors {
Publish,
Suppress,
}
type Probed = (
perspective_client::Client,
Option<perspective_client::Table>,
);
async fn probe(
session: &Session,
workspace: &Workspace,
name: &str,
missing: MissingTable,
) -> ApiResult<Probed> {
let current = session.get_client();
let resolved = workspace
.resolve_client_for_table(name, current.as_ref())
.await;
let client = resolved.or(current).into_apierror()?;
let table = probe_table(&client, name, missing).await?;
Ok((client, table))
}
pub(crate) async fn bind_table_task(
session: &Session,
workspace: &Workspace,
name: String,
missing: MissingTable,
) -> ApiResult<()> {
if session
.get_table()
.is_some_and(|t| t.get_name() == name.as_str())
{
return Ok(());
}
let (client, table) = probe(session, workspace, &name, missing).await?;
session.set_client(client);
match table {
Some(table) => session.bind_table(table).await?,
None => session.pend_table(name).await?,
}
session.commit_table_defaults();
Ok(())
}
async fn rebind_table_task(
session: &Session,
renderer: &Renderer,
workspace: &Workspace,
name: String,
missing: MissingTable,
generation: u32,
) -> ApiResult<()> {
let probed = probe(session, workspace, &name, missing).await;
let journal = session.take_pending_load(generation);
let (client, table) = probed?;
let Some(journal) = journal else {
return Ok(());
};
session
.reset(ResetOptions {
config: true,
expressions: true,
stats: true,
table: Some(TableIntermediateState::Reloaded),
})
.await?;
session.set_client(client);
match table {
Some(table) => session.bind_table(table).await?,
None => session.pend_table(name).await?,
}
session.commit_table_defaults();
for delta in journal {
session.commit_view_config(delta)?;
}
session.commit_table_defaults();
if session.get_table().is_none()
&& let Some(plugin) = renderer.active_plugin()
{
plugin.clear().await?;
}
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub(crate) async fn restore_panel(
session: &Session,
renderer: &Renderer,
presentation: &Presentation,
workspace: &Workspace,
mode: RestoreMode,
mut update: ViewerConfigUpdate,
errors: RestoreErrors,
missing: MissingTable,
) -> ApiResult<()> {
let active = matches!(mode, RestoreMode::Existing { active: true });
let fresh = matches!(mode, RestoreMode::Fresh);
match &update.theme {
OptionalUpdate::Update(theme) => renderer.set_theme_stamped(Some(theme.clone())),
OptionalUpdate::SetDefault => {
renderer.set_theme_stamped(presentation.get_default_theme_name().await)
},
OptionalUpdate::Missing => {},
}
if !active {
update.theme = OptionalUpdate::Missing;
}
if !fresh {
tracing::info!("Restoring {update}");
}
let rollback =
matches!(errors, RestoreErrors::Suppress).then(|| session.get_view_config().clone());
let binding_before = (
session.get_table().map(|t| t.get_name().to_owned()),
session.pending_table(),
);
let generation = match &update.table {
OptionalUpdate::Update(name)
if session.is_errored()
|| (!fresh
&& session
.get_table()
.map(|t| t.get_name() != name.as_str())
.unwrap_or(true)) =>
{
Some(session.begin_pending_load())
},
_ => None,
};
let result = restore_and_render(
session,
renderer,
presentation,
RunOrigin::Public,
update.clone(),
{
clone!(session, renderer, update.table, workspace);
async move {
let OptionalUpdate::Update(name) = table else {
return Ok(());
};
match generation {
Some(generation) => {
rebind_table_task(
&session, &renderer, &workspace, name, missing, generation,
)
.await
},
None => bind_table_task(&session, &workspace, name, missing).await,
}
}
},
)
.await;
if let Some(generation) = generation {
session.take_pending_load(generation);
}
if let Err(e) = &result {
match errors {
RestoreErrors::Publish => session.set_error(false, e.clone()).await?,
RestoreErrors::Suppress => {
let binding_after = (
session.get_table().map(|t| t.get_name().to_owned()),
session.pending_table(),
);
if let Some(config) = rollback
&& binding_after == binding_before
{
session.commit_view_config(config.into()).unwrap_or_log();
}
},
}
}
result?;
if fresh {
renderer.resize().await.unwrap_or_log();
}
Ok(())
}