use std::sync::Arc;
#[cfg(not(target_arch = "wasm32"))]
use myko::report::export_tree::{EntityTreeExport, ExportEntityTree};
#[cfg(not(target_arch = "wasm32"))]
use myko::{
command::{CommandContext, CommandError, CommandHandler},
prelude::Uuid,
};
use myko::{inventory, myko_command, myko_item};
use serde_json::Value;
use crate::{Project, ProjectId};
#[cfg(not(target_arch = "wasm32"))]
pub type SnapshotPreCaptureFn =
fn(&CommandContext, root_type: &str, root_id: &str) -> Result<(), CommandError>;
#[cfg(not(target_arch = "wasm32"))]
pub struct SnapshotPreCaptureRegistration {
pub contributor: SnapshotPreCaptureFn,
}
#[cfg(not(target_arch = "wasm32"))]
inventory::collect!(SnapshotPreCaptureRegistration);
#[cfg(not(target_arch = "wasm32"))]
pub type SnapshotPostExportFn = fn(
export: &mut EntityTreeExport,
ctx: &CommandContext,
root_type: &str,
root_id: &str,
) -> Result<(), CommandError>;
#[cfg(not(target_arch = "wasm32"))]
pub struct SnapshotPostExportRegistration {
pub contributor: SnapshotPostExportFn,
}
#[cfg(not(target_arch = "wasm32"))]
inventory::collect!(SnapshotPostExportRegistration);
#[myko_item]
pub struct Snapshot {
#[searchable]
pub name: String,
#[serde(default)]
pub description: Option<String>,
#[belongs_to(Project)]
#[exclude_from_tree]
pub scope_id: ProjectId,
pub root_type: String,
pub root_id: String,
pub data: Value,
}
#[myko_command(SnapshotId)]
pub struct CreateSnapshot {
pub name: String,
#[serde(default)]
pub description: Option<String>,
pub project_id: ProjectId,
pub root_type: String,
pub root_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional = nullable)]
pub as_of: Option<String>,
}
#[cfg(not(target_arch = "wasm32"))]
impl CommandHandler for CreateSnapshot {
fn execute(self, ctx: CommandContext) -> Result<SnapshotId, CommandError> {
let as_of = self.as_of.map(|s| Arc::from(s.as_str()));
eprintln!(
"[CreateSnapshot] root_type={} root_id={} as_of={:?}",
self.root_type, self.root_id, as_of
);
let is_live = as_of.is_none();
if is_live {
for registration in inventory::iter::<SnapshotPreCaptureRegistration> {
(registration.contributor)(&ctx, &self.root_type, &self.root_id)?;
}
}
let mut tree = ctx.exec_report(ExportEntityTree {
root_type: self.root_type.clone().into(),
root_id: self.root_id.clone().into(),
as_of,
})?;
eprintln!("[CreateSnapshot] exported {} entities", tree.entities.len());
if is_live {
for registration in inventory::iter::<SnapshotPostExportRegistration> {
(registration.contributor)(&mut tree, &ctx, &self.root_type, &self.root_id)?;
}
}
let id = SnapshotId(Uuid::new_v4().to_string().into());
let data = serde_json::to_value(&tree).map_err(|e| CommandError {
tx: ctx.tx().to_string(),
command_id: ctx.command_id.to_string(),
message: format!("Failed to serialize tree: {e}"),
})?;
let snapshot = Snapshot {
id: id.clone(),
name: self.name,
description: self.description,
scope_id: self.project_id,
root_type: self.root_type,
root_id: self.root_id,
data,
};
ctx.emit_set(&snapshot)?;
Ok(id)
}
}