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}