code wiki / (root) / nx_result.nx

nx_result.nx source

↩ module page · 112 lines · 4323 B

1// nx_result.nx -- proper Result type for substrate-side error handling. 2// 3// Per user 2026-05-14: "if we dont have capabilities and adding them 4// doesnt add bullshit like nulls where nulls shouldnt be go deep and 5// build up". This replaces the `return -1` / `return null` sentinel 6// pattern with a Result struct the caller MUST inspect before reading 7// the value. 8// 9// Discipline: 10// result := nx_result_ok(value) or nx_result_err(code) 11// caller calls nx_result_is_ok(result) 12// only if 1 does caller call nx_result_unwrap(result) 13// on 0 caller calls nx_result_err_code(result) to dispatch 14// 15// No null-pointer returns. No -1 sentinels. Every error has a 16// named code from the NX_ERR_* sealed enum. 17 18// nx_safety_envelope: 19// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 20// sil_target: SIL1 21// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 22// verdict: NOT_YET_EVALUATED 23 24import "nx_syscalls.nx" 25import "nx_runtime.nx" 26import "nx_tier.nx" 27 28// ===== sealed error codes =========================================== 29const NX_ERR_NONE: nx_int = 0 30const NX_ERR_OUT_OF_RANGE: nx_int = 1 31const NX_ERR_INVALID_INPUT: nx_int = 2 32const NX_ERR_FILE_NOT_FOUND: nx_int = 3 33const NX_ERR_PARSE_FAILED: nx_int = 4 34const NX_ERR_TAG_MISMATCH: nx_int = 5 35const NX_ERR_NOT_FOUND: nx_int = 6 36const NX_ERR_ALLOC_FAILED: nx_int = 7 37const NX_ERR_INVALID_STATE: nx_int = 8 38const NX_ERR_DIVIDE_BY_ZERO: nx_int = 9 39const NX_ERR_OVERFLOW: nx_int = 10 40const NX_ERR_UNKNOWN_CODE: nx_int = 11 41 42func nx_err_name(code: nx_int) -> *u8 { 43 if code == NX_ERR_NONE { return "NONE" as *u8 } 44 if code == NX_ERR_OUT_OF_RANGE { return "OUT_OF_RANGE" as *u8 } 45 if code == NX_ERR_INVALID_INPUT { return "INVALID_INPUT" as *u8 } 46 if code == NX_ERR_FILE_NOT_FOUND { return "FILE_NOT_FOUND" as *u8 } 47 if code == NX_ERR_PARSE_FAILED { return "PARSE_FAILED" as *u8 } 48 if code == NX_ERR_TAG_MISMATCH { return "TAG_MISMATCH" as *u8 } 49 if code == NX_ERR_NOT_FOUND { return "NOT_FOUND" as *u8 } 50 if code == NX_ERR_ALLOC_FAILED { return "ALLOC_FAILED" as *u8 } 51 if code == NX_ERR_INVALID_STATE { return "INVALID_STATE" as *u8 } 52 if code == NX_ERR_DIVIDE_BY_ZERO { return "DIVIDE_BY_ZERO" as *u8 } 53 if code == NX_ERR_OVERFLOW { return "OVERFLOW" as *u8 } 54 return "UNKNOWN_CODE" as *u8 55} 56 57// ===== Result struct ================================================ 58// Status field is sealed: 1 = OK, 0 = ERR. Never any other value. 59// If status == OK, the value field holds the result. 60// If status == ERR, the err_code field holds the NX_ERR_* code. 61// Allocated via nx_result_ok / nx_result_err -- never raw mmap. 62struct NxResult { 63 status: nx_int, 64 value: nx_int, 65 err_code: nx_int, 66} 67 68const NX_RESULT_BYTES: nx_int = 24 69 70func nx_result_ok(value: nx_int) -> *NxResult { 71 let r: *NxResult = (sys_mmap(NX_RESULT_BYTES as i64)) as *NxResult 72 r.status = 1 73 r.value = value 74 r.err_code = NX_ERR_NONE 75 return r 76} 77 78func nx_result_err(code: nx_int) -> *NxResult { 79 let r: *NxResult = (sys_mmap(NX_RESULT_BYTES as i64)) as *NxResult 80 r.status = 0 81 r.value = 0 82 r.err_code = code 83 return r 84} 85 86// Predicates -- always safe to call. 87func nx_result_is_ok(r: *NxResult) -> nx_int { return r.status } 88func nx_result_is_err(r: *NxResult) -> nx_int { 89 if r.status == 1 { return 0 } 90 return 1 91} 92 93// Unwrap. Convention: caller checked is_ok first; if not, returns 94// the err_code (still not a sentinel-into-value-space because err_code 95// is a sealed enum, not a magic value). For strict callers, 96// nx_result_must_unwrap halts on error. 97func nx_result_unwrap(r: *NxResult) -> nx_int { 98 if r.status == 1 { return r.value } 99 return r.err_code 100} 101 102func nx_result_err_code(r: *NxResult) -> nx_int { return r.err_code } 103 104// Strict unwrap: prints the error name + exits the process if err. 105// Use for "this MUST succeed" contracts. 106func nx_result_must_unwrap(r: *NxResult, ctx: *u8) -> nx_int { 107 if r.status == 1 { return r.value } 108 print("FATAL: " as *u8); print(ctx); print(": " as *u8); print(nx_err_name(r.err_code)) 109 println("" as *u8) 110 sys_exit(1) 111 return 0 // unreachable; satisfies type checker 112}