use indexmap::IndexMap;
use crate::entity::EntityId;
use crate::ops::{IncomingRef, ModifiedMetadata, ModifiedSections, WarningHint};
#[derive(Debug, Clone)]
pub struct CreateEntityArgs {
pub mem: String,
pub title: String,
pub entity_type: String,
pub sections: IndexMap<String, String>,
pub metadata: IndexMap<String, String>,
pub relations: Vec<crate::ops::RelateArg>,
pub dry_run: bool,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct CreateEntityOutcome {
pub id: EntityId,
pub title: String,
pub mem: String,
pub file_path: String,
#[serde(rename = "_hash")]
pub content_hash: String,
pub commit_sha: String,
pub created_date: String,
pub warnings: Vec<WarningHint>,
pub type_guidance: std::collections::BTreeMap<String, Vec<String>>,
pub incoming_count: Option<usize>,
pub incoming: Vec<IncomingRef>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub relations_declared: Vec<RelationDeclared>,
}
#[derive(Debug, Clone)]
pub struct UpdateEntityArgs {
pub id: EntityId,
pub expected_hash: Option<String>,
pub sections: IndexMap<String, String>,
pub append_sections: IndexMap<String, String>,
pub patch_sections: IndexMap<String, crate::ops::PatchArg>,
pub metadata: IndexMap<String, String>,
pub metadata_unset: Vec<String>,
pub dry_run: bool,
pub declare_relations: Vec<crate::ops::RelateArg>,
pub relations_unset: Vec<crate::ops::RelationUnsetArg>,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct UpdateEntityOutcome {
pub id: EntityId,
pub title: String,
pub file_path: String,
#[serde(rename = "_hash")]
pub content_hash: String,
pub commit_sha: String,
pub modified_date: String,
pub modified_sections: ModifiedSections,
pub modified_metadata: ModifiedMetadata,
pub prospective_hash: Option<String>,
pub orphan_stubs_removed: Vec<EntityId>,
pub warnings: Vec<WarningHint>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub relations_declared: Vec<RelationDeclared>,
}
#[derive(Debug, Clone, serde::Serialize, PartialEq, Eq)]
pub struct RelationDeclared {
pub rel_type: String,
pub target: EntityId,
pub target_was_stubbed: bool,
}
#[derive(Debug, Clone)]
pub struct DeleteEntityArgs {
pub id: EntityId,
pub expected_hash: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct DeleteEntityOutcome {
pub id: EntityId,
pub file_path: String,
pub removed_incoming: Vec<String>,
pub relations_removed: usize,
pub commit_sha: String,
pub orphan_stubs_removed: Vec<EntityId>,
pub warnings: Vec<WarningHint>,
}
#[derive(Debug, Clone)]
pub struct RelateEntityArgs {
pub source: EntityId,
pub expected_hash: Option<String>,
pub rel_type: String,
pub target: EntityId,
pub remove: bool,
pub description: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum RelateAction {
Added,
Removed,
NoOpAlreadyPresent,
NoOpAbsent,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct RelateEntityOutcome {
pub from: EntityId,
pub to: EntityId,
pub rel_type: String,
pub action: RelateAction,
#[serde(rename = "_hash")]
pub content_hash: String,
pub commit_sha: String,
pub source: String,
pub warnings: Vec<WarningHint>,
pub orphan_stubs_removed: Vec<EntityId>,
}
#[derive(Debug, Clone)]
pub struct RenameEntityArgs {
pub id: EntityId,
pub expected_hash: Option<String>,
pub new_title: String,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct RenameEntityOutcome {
pub old_id: EntityId,
pub new_id: EntityId,
pub old_path: String,
pub new_path: String,
#[serde(rename = "_hash")]
pub content_hash: String,
pub commit_sha: String,
pub warnings: Vec<WarningHint>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn outcome_types_serialize_to_json() {
let create = CreateEntityOutcome {
id: EntityId("v--e".to_string()),
title: "t".to_string(),
mem: "v".to_string(),
file_path: "v/e.md".to_string(),
content_hash: "h".to_string(),
commit_sha: "sha".to_string(),
created_date: "2026-05-11".to_string(),
warnings: Vec::new(),
type_guidance: std::collections::BTreeMap::new(),
incoming_count: None,
incoming: Vec::new(),
relations_declared: Vec::new(),
};
assert!(serde_json::to_string(&create).is_ok());
let update = UpdateEntityOutcome {
id: EntityId("v--e".to_string()),
title: "t".to_string(),
file_path: "v/e.md".to_string(),
content_hash: "h".to_string(),
commit_sha: "sha".to_string(),
modified_date: "2026-05-11".to_string(),
modified_sections: ModifiedSections::default(),
modified_metadata: ModifiedMetadata::default(),
prospective_hash: None,
orphan_stubs_removed: Vec::new(),
warnings: Vec::new(),
relations_declared: Vec::new(),
};
assert!(serde_json::to_string(&update).is_ok());
let delete = DeleteEntityOutcome {
id: EntityId("v--e".to_string()),
file_path: "v/e.md".to_string(),
removed_incoming: Vec::new(),
commit_sha: "sha".to_string(),
relations_removed: 0,
orphan_stubs_removed: Vec::new(),
warnings: Vec::new(),
};
assert!(serde_json::to_string(&delete).is_ok());
let relate = RelateEntityOutcome {
from: EntityId("v--a".to_string()),
to: EntityId("v--b".to_string()),
rel_type: "PART_OF".to_string(),
action: RelateAction::Added,
content_hash: "h".to_string(),
commit_sha: "sha".to_string(),
source: "explicit".to_string(),
warnings: Vec::new(),
orphan_stubs_removed: Vec::new(),
};
assert!(serde_json::to_string(&relate).is_ok());
let rename = RenameEntityOutcome {
old_id: EntityId("v--a".to_string()),
new_id: EntityId("v--b".to_string()),
old_path: "v/a.md".to_string(),
new_path: "v/b.md".to_string(),
content_hash: "h".to_string(),
commit_sha: "sha".to_string(),
warnings: Vec::new(),
};
let rename_json = serde_json::to_string(&rename).unwrap();
assert!(
rename_json.contains("\"old_path\""),
"RenameEntityOutcome must serialize old_path: {rename_json}",
);
assert!(
rename_json.contains("\"new_path\""),
"RenameEntityOutcome must serialize new_path: {rename_json}",
);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum SetSchemaResult {
Noop,
Switched,
MigrationStarted,
MigrationPending,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct SetSchemaOutcome {
pub mem: String,
pub schema_pin: String,
pub migration_target: Option<String>,
pub outcome: SetSchemaResult,
pub findings: Vec<crate::ops::integrity::IntegrityFinding>,
}