4 additive FieldKind/EdgeKeyKind variants addressing M1 dogfood + M4/M5 flags. Backward-compat — existing schemas unchanged. FieldKind::TextPk — TEXT PRIMARY KEY (kei-chat-store UUID sessions) FieldKind::Real + RealDefault(f64) — REAL columns (kei-chat-store cost) FieldKind::TextArchiveEnum — archive verb writes string sentinel instead of 1 EdgeKeyKind::TextPairWithMetadata — src_path/dst_path + id/weight/created_at/extra Archive verb now dispatches: IntegerFlag writes 1, TextArchiveEnum writes sentinel. Link/rank handle TextPairWithMetadata via generic weight+id propagation. PK helpers in verbs/pk.rs abstract integer vs text primary key. Engine decomposed to stay under 200 LOC: - schema.rs (149) + field.rs (94, new) + ddl.rs (157, new) - verbs/pk.rs + create_defaults.rs split from create.rs Tests: 40/40 (was 32, +8 real_text_pk_smoke). Sister crates verified backward-compat: - kei-task 9/9, kei-chat-store 5/5, kei-content-store 4/4 - kei-social-store 5/5, kei-crossdomain 5/5, kei-sage 28/28 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
57 lines
1.7 KiB
Rust
57 lines
1.7 KiB
Rust
//! `delete` verb — hard DELETE by id, OR soft (if schema has an
|
|
//! `archived` integer field, flips it to 1).
|
|
|
|
use crate::error::VerbError;
|
|
use crate::schema::EntitySchema;
|
|
use crate::verbs::pk;
|
|
use rusqlite::Connection;
|
|
use serde_json::{json, Value};
|
|
|
|
pub fn run(
|
|
conn: &Connection,
|
|
schema: &EntitySchema,
|
|
input: Value,
|
|
) -> Result<Value, VerbError> {
|
|
if !schema.verb_enabled("delete") {
|
|
return Err(VerbError::VerbDisabled {
|
|
verb: "delete".into(),
|
|
schema: schema.name.into(),
|
|
});
|
|
}
|
|
let id = pk::extract(schema, &input, "delete")?;
|
|
let soft = input.get("soft").and_then(|v| v.as_bool()).unwrap_or(false);
|
|
|
|
let rows = if soft && has_archived_field(schema) {
|
|
conn.execute(
|
|
&format!(
|
|
"UPDATE {} SET archived = 1 WHERE {}=?1",
|
|
schema.table,
|
|
schema.pk().name
|
|
),
|
|
rusqlite::params![id.as_sql()],
|
|
)?
|
|
} else {
|
|
if schema.fts_columns.is_some() {
|
|
conn.execute(
|
|
&format!("DELETE FROM fts_{} WHERE {}_id=?1", schema.table, schema.table),
|
|
rusqlite::params![id.as_sql()],
|
|
)?;
|
|
}
|
|
conn.execute(
|
|
&format!(
|
|
"DELETE FROM {} WHERE {}=?1",
|
|
schema.table,
|
|
schema.pk().name
|
|
),
|
|
rusqlite::params![id.as_sql()],
|
|
)?
|
|
};
|
|
if rows == 0 {
|
|
return Err(VerbError::not_found_text(schema.name, id.as_string()));
|
|
}
|
|
Ok(json!({ "ok": true, "id": id.as_json() }))
|
|
}
|
|
|
|
fn has_archived_field(schema: &EntitySchema) -> bool {
|
|
schema.fields.iter().any(|f| f.name == "archived")
|
|
}
|