code wiki / (root) / nx_relate_import.nx

nx_relate_import.nx source

↩ module page · 172 lines · 6727 B

1// nx_relate_import.nx -- IMPORT-EXPORT for the relationship graph (CRM census E6): bring real contacts IN 2// (CSV lines `type,name,tenant` + minimal vCard FN:) and get them OUT (CSV export walked from the store) -- 3// persisted on the sovereign append-only seg_store via the nx_relate_serve pure core. STRICT: a malformed CSV 4// row (fewer than 3 fields) fails the WHOLE import loudly (no partial silent imports). Argless = selftest gate. 5// license_tier: ORIGINAL expect_exit: 0 6import "nx_relate_serve.nx" 7const K_MAGIC_65536: i64 = 65536 8 9// comma-field of line[ls,le) -> out; returns 1 if the field exists 10func ri_csvf(b: *u8, ls: i64, le: i64, idx: i64, out: *u8, cap: i64) -> i64 { 11 var segi: i64 = 0 12 var t: i64 = 0 13 var found: i64 = 0 14 var i: i64 = ls 15 while i < le { 16 if b[i] == (44 as u8) { segi = segi + 1 } else { 17 if segi == idx { 18 found = 1 19 if b[i] != (13 as u8) { if t < cap - 1 { out[t] = b[i]; t = t + 1 } } 20 } 21 } 22 i = i + 1 23 } 24 out[t] = 0 as u8 25 if t == 0 { return found } 26 return 1 27} 28 29// import CSV text (type,name,tenant per line) into the store; returns count or -1 loud on malformed row 30func ri_import_csv(prefix: *u8, txt: *u8) -> i64 { 31 let n: i64 = rv_slen(txt) 32 let ty: *u8 = sys_mmap(128) 33 let nm: *u8 = sys_mmap(256) 34 let tn: *u8 = sys_mmap(128) 35 let w: *i64 = ss_begin() 36 var cnt: i64 = 0 37 var i: i64 = 0 38 while i < n { 39 var j: i64 = i 40 while j < n { if txt[j] == (10 as u8) { break } j = j + 1 } 41 if j > i { 42 let f1: i64 = ri_csvf(txt, i, j, 0, ty, 128) 43 let f2: i64 = ri_csvf(txt, i, j, 1, nm, 256) 44 let f3: i64 = ri_csvf(txt, i, j, 2, tn, 128) 45 if f1 == 0 { return 0 - 1 } 46 if f2 == 0 { return 0 - 1 } 47 if f3 == 0 { return 0 - 1 } 48 rv_save_entity(w, ty, nm, tn) 49 cnt = cnt + 1 50 } 51 i = j + 1 52 } 53 if cnt > 0 { if ss_commit(prefix, w, sys_now_us()) != 0 { return 0 - 2 } } 54 return cnt 55} 56 57// import minimal vCard text: each FN: line -> a person entity (tenant "import"); returns count 58func ri_import_vcard(prefix: *u8, txt: *u8) -> i64 { 59 let n: i64 = rv_slen(txt) 60 let nm: *u8 = sys_mmap(256) 61 let w: *i64 = ss_begin() 62 var cnt: i64 = 0 63 var i: i64 = 0 64 while i < n { 65 var j: i64 = i 66 while j < n { if txt[j] == (10 as u8) { break } j = j + 1 } 67 if j > i + 3 { 68 if txt[i] == (70 as u8) { if txt[i+1] == (78 as u8) { if txt[i+2] == (58 as u8) { 69 var t: i64 = 0 70 var q: i64 = i + 3 71 while q < j { if txt[q] != (13 as u8) { if t < 255 { nm[t] = txt[q]; t = t + 1 } } q = q + 1 } 72 nm[t] = 0 as u8 73 if t > 0 { rv_save_entity(w, "person" as *u8, nm, "import" as *u8); cnt = cnt + 1 } 74 } } } 75 } 76 i = j + 1 77 } 78 if cnt > 0 { if ss_commit(prefix, w, sys_now_us()) != 0 { return 0 - 2 } } 79 return cnt 80} 81 82// export all entities as CSV (type,name,tenant per line); returns length 83func ri_export_csv(prefix: *u8, out: *u8, cap: i64) -> i64 { 84 let segp: *i64 = sys_mmap(16) as *i64 85 let ns: i64 = ss_manifest_dyn(prefix, segp) // uncapped: export the WHOLE store 86 let segs: *i64 = segp[0] as *i64 87 let f1: *u8 = sys_mmap(256) 88 let f2: *u8 = sys_mmap(256) 89 let f3: *u8 = sys_mmap(256) 90 var o: i64 = 0 91 var s: i64 = 0 92 while s < ns { 93 let path: *u8 = sys_mmap(512) 94 var po: i64 = 0 95 po = rv_cat(path, po, prefix) 96 po = rv_cat(path, po, segs[s] as *u8) 97 po = rv_cat(path, po, ".docs" as *u8) 98 path[po] = 0 as u8 99 let szp: *i64 = sys_mmap(16) as *i64 100 let b: *u8 = ss_readall(path, szp) 101 let sz: i64 = szp[0] 102 var j: i64 = 0 103 while j + 9 <= sz { 104 let kl: i64 = ss_r32(b, j + 1) 105 let koff: i64 = j + 5 106 let vl: i64 = ss_r32(b, koff + kl) 107 let voff: i64 = koff + kl + 4 108 if b[j] == 1 { 109 if rv_iskind(b, koff, kl, "ent:" as *u8) == 1 { 110 let rec: *u8 = (b as i64 + voff) as *u8 111 rv_field(rec, vl, "type" as *u8, f1, 256) 112 rv_field(rec, vl, "name" as *u8, f2, 256) 113 rv_field(rec, vl, "tenant" as *u8, f3, 256) 114 o = rv_cat(out, o, f1) 115 if o < cap - 1 { out[o] = 44 as u8; o = o + 1 } 116 o = rv_cat(out, o, f2) 117 if o < cap - 1 { out[o] = 44 as u8; o = o + 1 } 118 o = rv_cat(out, o, f3) 119 if o < cap - 1 { out[o] = 10 as u8; o = o + 1 } 120 } 121 } 122 j = voff + vl 123 } 124 s = s + 1 125 } 126 out[o] = 0 as u8 127 return o 128} 129 130func main() -> i64 { 131 p("=== NX-RELATE-IMPORT SELFTEST (IMPORT-EXPORT: CSV + vCard -> store -> CSV roundtrip) ===\n" as *u8) 132 var ok: i64 = 1 133 let prefix: *u8 = sys_mmap(128) 134 var po: i64 = 0 135 po = rv_cat(prefix, po, "/tmp/relimp_" as *u8) 136 po = rv_catn(prefix, po, sys_now_us()) 137 po = rv_cat(prefix, po, "_" as *u8) 138 prefix[po] = 0 as u8 139 140 // CSV import: 3 rows 141 let csv: *u8 = "person,Rose Andelin,andelinwest\ncompany,Andelin West,andelinwest\norg,First Ward,personal\n" as *u8 142 let c1: i64 = ri_import_csv(prefix, csv) 143 p(" csv imported=" as *u8); pn(c1); p("\n" as *u8) 144 if c1 != 3 { ok = 0 } 145 if rv_count_ent(prefix) != 3 { ok = 0 } 146 147 // vCard import: 2 cards 148 let vc: *u8 = "BEGIN:VCARD\nFN:Marcus Reed\nEND:VCARD\nBEGIN:VCARD\nFN:Emma Andelin\nEND:VCARD\n" as *u8 149 let c2: i64 = ri_import_vcard(prefix, vc) 150 p(" vcard imported=" as *u8); pn(c2); p("\n" as *u8) 151 if c2 != 2 { ok = 0 } 152 if rv_count_ent(prefix) != 5 { ok = 0 } 153 154 // export roundtrip contains the CSV rows 155 let out: *u8 = sys_mmap(K_MAGIC_65536) 156 let on: i64 = ri_export_csv(prefix, out, K_MAGIC_65536) 157 p(" export bytes=" as *u8); pn(on); p("\n" as *u8) 158 if rv_has(out, on, "person,Rose Andelin,andelinwest" as *u8) != 1 { ok = 0 } 159 if rv_has(out, on, "person,Marcus Reed,import" as *u8) != 1 { ok = 0 } 160 161 // malformed row fails LOUD (and imports nothing new) 162 let before: i64 = rv_count_ent(prefix) 163 let badv: i64 = ri_import_csv(prefix, "person-only-no-commas\n" as *u8) 164 p(" malformed csv -> " as *u8); pn(badv); p(" (loud)\n" as *u8) 165 if badv != (0 - 1) { ok = 0 } 166 if rv_count_ent(prefix) != before { ok = 0 } 167 168 p("IMPORT-EXPORT-SELFTEST entities=" as *u8); pn(rv_count_ent(prefix)); p(" " as *u8) 169 if ok == 1 { p("verdict=GREEN\n" as *u8); return 0 } 170 p("verdict=RED\n" as *u8) 171 return 1 172}