⎇ d3649bfd1a80153652c2a028315369bb4b184024
crates/modules/nakui/core/src/bin/demo.rs
Historia
Quién tocó qué
crudo
chore: absorbe nakui (ERP matemático) en modules/nakui
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}