chore: absorbe nakui (ERP matemático) en modules/nakui
- crates/modules/nakui/core/: el crate nakui-core (4 bins, tests).
Deps directas (serde, rhai, surrealdb, petgraph, sha2, uuid, tokio,
thiserror v1) — no convertidas a workspace = true en esta pasada.
- crates/modules/nakui/modules/{inventory,sales,treasury}/: datos
declarativos del dominio (nsmc.json, schema.k, morphisms/) que el
crate consume — no son crates.
cargo check -p nakui-core: 0 errores.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct FieldPath {
|
||||
pub entity: String,
|
||||
pub id: Uuid,
|
||||
pub field: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(tag = "op", rename_all = "snake_case")]
|
||||
pub enum FieldOp {
|
||||
Set {
|
||||
path: FieldPath,
|
||||
value: Value,
|
||||
},
|
||||
Create {
|
||||
entity: String,
|
||||
id: Uuid,
|
||||
data: Value,
|
||||
},
|
||||
Delete {
|
||||
entity: String,
|
||||
id: Uuid,
|
||||
},
|
||||
}
|
||||
|
||||
impl FieldOp {
|
||||
/// Token a manifest's `writes` list matches against.
|
||||
/// "Caja.saldo" for field updates, "Movimiento" for whole-record ops.
|
||||
pub fn capability_token(&self) -> String {
|
||||
match self {
|
||||
FieldOp::Set { path, .. } => format!("{}.{}", path.entity, path.field),
|
||||
FieldOp::Create { entity, .. } => entity.clone(),
|
||||
FieldOp::Delete { entity, .. } => entity.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply only the ops that target `(entity, id)` to `state` and return the
|
||||
/// new value. Returns `None` if a Delete op removes the target — callers
|
||||
/// should skip post-checks against a deleted entity rather than running
|
||||
/// them against the stale prior state.
|
||||
pub fn simulate_on(state: &Value, entity: &str, id: Uuid, ops: &[FieldOp]) -> Option<Value> {
|
||||
let mut s: Option<Value> = Some(state.clone());
|
||||
for op in ops {
|
||||
match op {
|
||||
FieldOp::Set { path, value } if path.entity == entity && path.id == id => {
|
||||
if let Some(Value::Object(map)) = s.as_mut() {
|
||||
map.insert(path.field.clone(), value.clone());
|
||||
}
|
||||
}
|
||||
FieldOp::Create {
|
||||
entity: e,
|
||||
id: i,
|
||||
data,
|
||||
} if e == entity && *i == id => {
|
||||
s = Some(data.clone());
|
||||
}
|
||||
FieldOp::Delete {
|
||||
entity: e,
|
||||
id: i,
|
||||
} if e == entity && *i == id => {
|
||||
s = None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
s
|
||||
}
|
||||
Reference in New Issue
Block a user