tawasuyu

tawasuyu/brahman

Rust 582TOML 222Markdown 73JSON 198635 Definiciones

⎇ d3649bfd1a80153652c2a028315369bb4b184024 crates/modules/nakui/core/src/bin/demo.rs Rust · 224 líneas · 6.4 KB Historia Quién tocó qué crudo

chore: absorbe nakui (ERP matemático) en modules/nakui 4d50bfc58 · Sergio ·

1use nakui_core::event_log::{
2    EventLog, ExecuteError, execute_and_log, replay, seed_and_log, verify_log,
3};
4use nakui_core::executor::Executor;
5use nakui_core::store::{MemoryStore, Store};
6use serde_json::json;
7use uuid::Uuid;
8
9fn main() {
10    let module_dir =
11        std::env::var("NAKUI_MODULE").unwrap_or_else(|_| "modules/treasury".into());
12    let exec = Executor::load_module(&module_dir).expect("load module");
13
14    let log_path = std::env::temp_dir().join(format!("nakui_demo_{}.jsonl", Uuid::new_v4()));
15    let mut log = EventLog::open(&log_path).expect("open log");
16    let mut store = MemoryStore::new();
17
18    let caja_a = Uuid::new_v4();
19    let caja_b = Uuid::new_v4();
20    let caja_c = Uuid::new_v4();
21    seed_and_log(
22        &exec,
23        &mut store,
24        &mut log,
25        "Caja",
26        caja_a,
27        json!({
28            "id": caja_a.to_string(),
29            "name": "Caja Principal",
30            "saldo": 200_000_i64,
31            "currency": "USD",
32        }),
33    )
34    .expect("seed A");
35    seed_and_log(
36        &exec,
37        &mut store,
38        &mut log,
39        "Caja",
40        caja_b,
41        json!({
42            "id": caja_b.to_string(),
43            "name": "Caja Chica",
44            "saldo": 50_000_i64,
45            "currency": "USD",
46        }),
47    )
48    .expect("seed B");
49    seed_and_log(
50        &exec,
51        &mut store,
52        &mut log,
53        "Caja",
54        caja_c,
55        json!({
56            "id": caja_c.to_string(),
57            "name": "Caja EUR",
58            "saldo": 30_000_i64,
59            "currency": "EUR",
60        }),
61    )
62    .expect("seed C");
63
64    section("== seed ==");
65    print_caja(&store, "A", caja_a);
66    print_caja(&store, "B", caja_b);
67    print_caja(&store, "C", caja_c);
68
69    section("== A: deposit 50_000 USD ==");
70    run_and_report(
71        &exec,
72        &mut store,
73        &mut log,
74        "register_cash_move",
75        &[("caja", caja_a)],
76        json!({
77            "monto": 50_000_i64,
78            "tipo": "in",
79            "timestamp": "2026-05-04T12:00:00Z",
80            "memo": "deposito A",
81            "movimiento_id": Uuid::new_v4().to_string(),
82        }),
83    );
84    print_caja(&store, "A", caja_a);
85
86    section("== transfer A -> B 100_000 USD ==");
87    run_and_report(
88        &exec,
89        &mut store,
90        &mut log,
91        "transfer_between_cajas",
92        &[("source", caja_a), ("dest", caja_b)],
93        json!({
94            "monto": 100_000_i64,
95            "timestamp": "2026-05-04T12:30:00Z",
96            "memo": "transferencia operativa",
97            "transfer_id": Uuid::new_v4().to_string(),
98        }),
99    );
100    print_caja(&store, "A", caja_a);
101    print_caja(&store, "B", caja_b);
102
103    section("== transfer A -> B 999_999_999 USD (reject: post-check on source) ==");
104    run_and_report(
105        &exec,
106        &mut store,
107        &mut log,
108        "transfer_between_cajas",
109        &[("source", caja_a), ("dest", caja_b)],
110        json!({
111            "monto": 999_999_999_i64,
112            "timestamp": "2026-05-04T13:00:00Z",
113            "memo": "overdraw",
114            "transfer_id": Uuid::new_v4().to_string(),
115        }),
116    );
117
118    section("== transfer A(USD) -> C(EUR) (reject: rhai throws) ==");
119    run_and_report(
120        &exec,
121        &mut store,
122        &mut log,
123        "transfer_between_cajas",
124        &[("source", caja_a), ("dest", caja_c)],
125        json!({
126            "monto": 10_000_i64,
127            "timestamp": "2026-05-04T14:00:00Z",
128            "memo": "USD -> EUR",
129            "transfer_id": Uuid::new_v4().to_string(),
130        }),
131    );
132
133    section("== self-transfer A -> A (reject: DuplicateInputId) ==");
134    run_and_report(
135        &exec,
136        &mut store,
137        &mut log,
138        "transfer_between_cajas",
139        &[("source", caja_a), ("dest", caja_a)],
140        json!({
141            "monto": 1_000_i64,
142            "timestamp": "2026-05-04T15:00:00Z",
143            "memo": "self",
144            "transfer_id": Uuid::new_v4().to_string(),
145        }),
146    );
147
148    section("== final live state ==");
149    print_caja(&store, "A", caja_a);
150    print_caja(&store, "B", caja_b);
151    print_caja(&store, "C", caja_c);
152
153    let entries = log.entries().expect("read log");
154    section(&format!(
155        "== log: {} entries at {} ==",
156        entries.len(),
157        log.path().display()
158    ));
159    for e in &entries {
160        match e {
161            nakui_core::event_log::LogEntry::Seed {
162                seq, entity, id, ..
163            } => println!("  #{:02} seed   {} {}", seq, entity, id),
164            nakui_core::event_log::LogEntry::Morphism {
165                seq,
166                morphism,
167                ops,
168                ..
169            } => println!("  #{:02} morph  {} ({} ops)", seq, morphism, ops.len()),
170        }
171    }
172
173    section("== replay verification (state) ==");
174    let replayed = replay(&log).expect("replay");
175    if store == replayed {
176        println!("  ok: replayed store byte-equal to live store");
177    } else {
178        println!("  MISMATCH: replay diverges from live");
179    }
180
181    section("== determinism verification (ops) ==");
182    match verify_log(&log, &exec) {
183        Ok(()) => println!(
184            "  ok: every logged morphism reproduced its ops on re-execution"
185        ),
186        Err(e) => println!("  nondeterminism detected: {}", e),
187    }
188
189    if std::env::var_os("NAKUI_DEMO_KEEP").is_none() {
190        let _ = std::fs::remove_file(&log_path);
191    } else {
192        println!("\n(NAKUI_DEMO_KEEP set — keeping log at {})", log_path.display());
193    }
194}
195
196fn run_and_report(
197    exec: &Executor,
198    store: &mut MemoryStore,
199    log: &mut EventLog,
200    morphism: &str,
201    inputs: &[(&str, Uuid)],
202    params: serde_json::Value,
203) {
204    match execute_and_log(exec, store, log, morphism, inputs, params) {
205        Ok(ops) => println!("  ok ({} ops, logged at #{})", ops.len(), log.next_seq() - 1),
206        Err(ExecuteError::PreLog(e)) => println!("  rejected: {}", e),
207        Err(ExecuteError::LogAppend(e)) => println!("  LOG APPEND FAILED: {}", e),
208        Err(ExecuteError::PostLogStore(e)) => println!(
209            "  POST-LOG STORE FAILED (log is canonical, store stale): {}",
210            e
211        ),
212    }
213}
214
215fn print_caja(store: &MemoryStore, label: &str, id: Uuid) {
216    let v = store.load("Caja", id).expect("caja exists");
217    let saldo = v.get("saldo").and_then(|v| v.as_i64()).unwrap_or(0);
218    let currency = v.get("currency").and_then(|v| v.as_str()).unwrap_or("?");
219    println!("  {} {}: saldo={} {}", label, id, saldo, currency);
220}
221
222fn section(title: &str) {
223    println!("\n{}", title);
224}