use uuid::Uuid;
use khive_storage::SqlStatement;
#[derive(Debug, Clone)]
pub struct PlanStatement {
pub statement: SqlStatement,
pub guard: Option<AffectedRowGuard>,
}
#[derive(Debug, Clone)]
pub struct PlanPredicate {
pub description: String,
pub statement: SqlStatement,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AffectedRowGuard {
pub expected_min: u64,
pub expected_max: Option<u64>,
}
impl AffectedRowGuard {
pub fn exactly(n: u64) -> Self {
Self {
expected_min: n,
expected_max: Some(n),
}
}
pub fn at_least_one() -> Self {
Self {
expected_min: 1,
expected_max: None,
}
}
pub fn holds_for(&self, affected: u64) -> bool {
affected >= self.expected_min
&& self.expected_max.map(|max| affected <= max).unwrap_or(true)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PostCommitEffect {
None,
ReindexEntity { entity_id: Uuid },
ReindexNote { note_id: Uuid, version: i64 },
NoteChanged { note_id: Uuid, kind: String },
GtdAudit {
task_id: Uuid,
from_status: String,
to_status: String,
note: Option<String>,
namespace: String,
},
NoteDeleted { note_id: Uuid, kind: String },
}
#[derive(Debug, Clone)]
pub struct EdgeNaturalKey {
pub(crate) namespace: String,
pub(crate) canon_source_id: Uuid,
pub(crate) canon_target_id: Uuid,
pub(crate) relation: khive_storage::EdgeRelation,
}
impl EdgeNaturalKey {
pub fn namespace(&self) -> &str {
&self.namespace
}
pub fn canon_source_id(&self) -> Uuid {
self.canon_source_id
}
pub fn canon_target_id(&self) -> Uuid {
self.canon_target_id
}
pub fn relation(&self) -> khive_storage::EdgeRelation {
self.relation
}
}
#[derive(Debug, Clone)]
pub struct UpdatePlan {
pub(crate) target_id: Uuid,
pub(crate) statements: Vec<PlanStatement>,
pub(crate) post_commit: PostCommitEffect,
pub(crate) edge_natural_key: Option<EdgeNaturalKey>,
pub(crate) idempotent_noop: bool,
pub(crate) entity_guard: Option<crate::entity_write::EntityWriteGuard>,
pub(crate) note_guard: Option<crate::note_write::NoteWriteGuard>,
pub(crate) note_vector_purge: Option<crate::note_write::NoteVectors>,
pub(crate) note_embedding_inheritance: Option<crate::note_write::NoteEmbeddingInheritance>,
pub(crate) graph_effects: Vec<NoteUpdateStatement>,
}
#[derive(Debug, Clone)]
pub(crate) enum NoteUpdateStatement {
Write(PlanStatement),
Assert(PlanStatement),
}
impl UpdatePlan {
pub fn target_id(&self) -> Uuid {
self.target_id
}
pub fn edge_natural_key(&self) -> Option<&EdgeNaturalKey> {
self.edge_natural_key.as_ref()
}
pub fn post_commit(&self) -> &PostCommitEffect {
&self.post_commit
}
pub fn is_idempotent_noop(&self) -> bool {
self.idempotent_noop
}
}
#[derive(Debug, Clone)]
pub struct AddEntityPlan {
pub(crate) entity_id: Uuid,
pub(crate) statements: Vec<PlanStatement>,
pub(crate) post_commit: PostCommitEffect,
}
impl AddEntityPlan {
pub fn entity_id(&self) -> Uuid {
self.entity_id
}
}
#[derive(Debug, Clone)]
pub struct AddNotePlan {
pub(crate) note_id: Uuid,
pub(crate) note_guard: Option<crate::note_write::NoteWriteGuard>,
pub(crate) statements: Vec<PlanStatement>,
pub(crate) post_commit: PostCommitEffect,
}
impl AddNotePlan {
pub fn note_id(&self) -> Uuid {
self.note_id
}
}
#[derive(Debug, Clone)]
pub struct DeletePlan {
pub(crate) target_id: Uuid,
pub(crate) statements: Vec<PlanStatement>,
pub(crate) post_commit: PostCommitEffect,
}
impl DeletePlan {
pub fn target_id(&self) -> Uuid {
self.target_id
}
}
#[derive(Debug, Clone)]
pub struct LinkPlan {
pub(crate) source_id: Uuid,
pub(crate) target_id: Uuid,
pub(crate) statements: Vec<PlanStatement>,
pub(crate) disposition: khive_storage::EdgeUpsertDisposition,
}
impl LinkPlan {
pub fn source_id(&self) -> Uuid {
self.source_id
}
pub fn target_id(&self) -> Uuid {
self.target_id
}
pub fn disposition(&self) -> khive_storage::EdgeUpsertDisposition {
self.disposition
}
}
#[derive(Debug, Clone)]
pub struct MergePlan {
pub(crate) into_id: Uuid,
pub(crate) from_id: Uuid,
pub(crate) rewires: Vec<PlanPredicate>,
pub(crate) lifecycle: Vec<PlanStatement>,
}
impl MergePlan {
pub fn into_id(&self) -> Uuid {
self.into_id
}
pub fn from_id(&self) -> Uuid {
self.from_id
}
}
#[derive(Debug, Clone)]
pub struct GtdTransitionPlan {
pub(crate) task_id: Uuid,
pub(crate) statements: Vec<PlanStatement>,
pub(crate) idempotent_noop: bool,
pub(crate) post_commit: PostCommitEffect,
}
impl GtdTransitionPlan {
pub fn new(
task_id: Uuid,
statements: Vec<PlanStatement>,
idempotent_noop: bool,
post_commit: PostCommitEffect,
) -> Self {
Self {
task_id,
statements,
idempotent_noop,
post_commit,
}
}
pub fn task_id(&self) -> Uuid {
self.task_id
}
pub fn statements(&self) -> &[PlanStatement] {
&self.statements
}
pub fn is_idempotent_noop(&self) -> bool {
self.idempotent_noop
}
pub fn post_commit(&self) -> &PostCommitEffect {
&self.post_commit
}
}
#[derive(Debug, Clone)]
pub struct GtdCompletePlan {
pub(crate) task_id: Uuid,
pub(crate) statements: Vec<PlanStatement>,
pub(crate) post_commit: PostCommitEffect,
}
impl GtdCompletePlan {
pub fn new(
task_id: Uuid,
statements: Vec<PlanStatement>,
post_commit: PostCommitEffect,
) -> Self {
Self {
task_id,
statements,
post_commit,
}
}
pub fn task_id(&self) -> Uuid {
self.task_id
}
pub fn statements(&self) -> &[PlanStatement] {
&self.statements
}
pub fn post_commit(&self) -> &PostCommitEffect {
&self.post_commit
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GovernanceOp {
Propose,
Review,
Withdraw,
}
#[derive(Debug, Clone)]
pub struct GovernancePlan {
pub(crate) op: GovernanceOp,
pub(crate) proposal_id: Uuid,
pub(crate) statements: Vec<PlanStatement>,
}
impl GovernancePlan {
pub fn op(&self) -> GovernanceOp {
self.op
}
pub fn proposal_id(&self) -> Uuid {
self.proposal_id
}
}
#[cfg(test)]
#[path = "atomic_plan_tests.rs"]
mod tests;