Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_namex/src/db.rs

17.4 KiB, 53 runs

created by r1870400018:519, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1use crate::id::NamexId;
2
3use oxedyne_fe2o3_core::{
4 prelude::*,
5 map::MapMut,
6};
7use oxedyne_fe2o3_jdat::{
8 prelude::*,
9 daticle::Vek,
10 string::{
11 dec::DecoderConfig,
12 enc::{
13 ByteEncoding,
14 EncoderConfig,
15 },
16 },
17 usr::{
18 UsrKind,
19 UsrKindCode,
20 UsrKindId,
21 UsrKinds,
22 },
23};
24use oxedyne_fe2o3_text::string::Stringer;
25
26use std::{
27 collections::BTreeMap,
28 fmt,
29 io::Write,
30 path::Path,
31};
32
33
34#[derive(Clone, Debug, Default, PartialEq)]
35pub struct Entity {
36 // All lists maximum 10 members.
37 pub nams: Vec<String>, // Preferred names.
38 pub lang: String, // Language.
39 pub desc: Option<String>, // A brief description (<= 100 words) of the entity.
40 pub vers: Option<String>, // A string specifying the version.
41 pub tags: Option<Vec<String>>, // Words or phrases helpful for categorisation (>= 1).
42 pub tim1: Option<String>, // Origin date in Holocene Era format e.g. YYYYY-MM-DD.
43 pub tim2: Option<String>, // End date in Holocene Era format e.g. YYYYY-MM-DD.
44 pub refs: Option<Vec<String>>, // A list of references to more detailed information.
45 pub alts: Option<BTreeMap<String, String>>, // Alternative identifiers.
46 pub lnks: Option<BTreeMap<NamexId, String>>, // Relationships to other entities.
47}
48
49impl Entity {
50
51 pub fn to_datmap<
52 M1: MapMut<MapKey, Entity> + Clone + fmt::Debug + Default + Send,
53 M2: MapMut<NamexId, Entity> + Clone + fmt::Debug + Default + Send,
54 >(
55 &self,
56 )
57 -> Outcome<Dat>
58 {
59 let mut map = BTreeMap::new();
60 let ukinds = res!(Namex::<M1, M2>::ukinds());
61 for (klab, ukid) in ukinds.label_to_id_map() {
62 match klab.as_str() {
63 "nams" => {
64 map.insert(
65 oxedyne_fe2o3_jdat::map::MapKey::new(100, Dat::Usr(ukid.clone(), None)),
66 dat!(self.nams.clone()),
67 );
68 },
69 "lang" => {
70 map.insert(
71 oxedyne_fe2o3_jdat::map::MapKey::new(200, Dat::Usr(ukid.clone(), None)),
72 dat!(self.lang.clone()),
73 );
74 },
75 "desc" => if let Some(desc) = &self.desc {
76 map.insert(
77 oxedyne_fe2o3_jdat::map::MapKey::new(300, Dat::Usr(ukid.clone(), None)),
78 dat!(desc.clone()),
79 );
80 },
81 "vers" => if let Some(vers) = &self.vers {
82 map.insert(
83 oxedyne_fe2o3_jdat::map::MapKey::new(350, Dat::Usr(ukid.clone(), None)),
84 dat!(vers.clone()),
85 );
86 },
87 "tim1" => if let Some(tim1) = &self.tim1 {
88 map.insert(
89 oxedyne_fe2o3_jdat::map::MapKey::new(400, Dat::Usr(ukid.clone(), None)),
90 dat!(tim1.clone()),
91 );
92 },
93 "tim2" => if let Some(tim2) = &self.tim2 {
94 map.insert(
95 oxedyne_fe2o3_jdat::map::MapKey::new(500, Dat::Usr(ukid.clone(), None)),
96 dat!(tim2.clone()),
97 );
98 },
99 "refs" => if let Some(refs) = &self.refs {
100 map.insert(
101 oxedyne_fe2o3_jdat::map::MapKey::new(600, Dat::Usr(ukid.clone(), None)),
102 dat!(refs.clone()),
103 );
104 },
105 "alts" => if let Some(alts) = &self.alts {
106 map.insert(
107 oxedyne_fe2o3_jdat::map::MapKey::new(650, Dat::Usr(ukid.clone(), None)),
108 {
109 let mut map = BTreeMap::new();
110 for (k, v) in alts {
111 map.insert(k.clone(), v.clone());
112 }
113 dat!(map)
114 }
115 );
116 },
117 "lnks" => if let Some(lnks) = &self.lnks {
118 map.insert(
119 oxedyne_fe2o3_jdat::map::MapKey::new(700, Dat::Usr(ukid.clone(), None)),
120 {
121 // Keys are written in the canonical base64 text form, not as
122 // `b32` daticles, whose quoted form uses a different alphabet.
123 let mut map = BTreeMap::new();
124 for (k, v) in lnks {
125 map.insert(k.to_string(), v.clone());
126 }
127 dat!(map)
128 }
129 );
130 },
131 "tags" => if let Some(tags) = &self.tags {
132 map.insert(
133 oxedyne_fe2o3_jdat::map::MapKey::new(800, Dat::Usr(ukid.clone(), None)),
134 dat!(tags.clone()),
135 );
136 },
137 _ => (),
138 }
139 }
140 Ok(Dat::OrdMap(map))
141 }
142}
143
144#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
145pub struct MapKey {
146 ord: u64,
147 id: NamexId,
148}
149
150impl MapKey {
151 pub fn new(ord: u64, id: NamexId) -> Self {
152 Self {
153 ord,
154 id,
155 }
156 }
157 pub fn ord(&self) -> u64 { self.ord }
158 pub fn id(&self) -> &NamexId { &self.id }
159}
160
161#[derive(Clone, Debug, Default, PartialEq)]
162pub struct Namex<
163 M1: MapMut<MapKey, Entity> + Clone + fmt::Debug + Default + Send,
164 M2: MapMut<NamexId, Entity> + Clone + fmt::Debug + Default + Send,
165> {
166 ordered: M1,
167 map: M2,
168}
169
170/// The Namex is distributed, meaning that users can store, manage and update what they regard
171/// as the entire codex, or more likely, a part of it which we call a "tagset". For better
172/// efficiency, tagsets are subsets of the codex resulting from boolean searches of the "(tags)"
173/// field. Dividing the monolithic namex into smaller tagsets for specific purposes makes for
174/// faster loading and search.
175// 256 bits vs chars
176// binary 32: -
177// base64 44: 4IaH4F8elJw60EkIr2N9+S1avkvUDHX5IaH1GkEKoXQ=
178// hex 64: e08687e05f1e949c3ad04908af637df92d5abe4bd40c75f921a1f51a410aa174
179// decimal 78: 101555773374134092463836759906846316697927485927201504874204680125044748886388
180impl<
181 M1: MapMut<MapKey, Entity> + Clone + fmt::Debug + Default + Send,
182 M2: MapMut<NamexId, Entity> + Clone + fmt::Debug + Default + Send,
183>
184 Namex<M1, M2>
185{
186 pub fn by_id(&self, id: &NamexId) -> Option<&Entity> {
187 self.map.get(id)
188 }
189
190 pub fn ukinds() -> Outcome<UsrKinds<
191 BTreeMap<UsrKindCode, UsrKind>,
192 BTreeMap<String, UsrKindId>
193 >> {
194 let mut uks = UsrKinds::new(BTreeMap::new(), BTreeMap::new());
195 res!(uks.add(res!(Self::usr_key("nams"))));
196 res!(uks.add(res!(Self::usr_key("lang"))));
197 res!(uks.add(res!(Self::usr_key("desc"))));
198 res!(uks.add(res!(Self::usr_key("vers"))));
199 res!(uks.add(res!(Self::usr_key("tim1"))));
200 res!(uks.add(res!(Self::usr_key("tim2"))));
201 res!(uks.add(res!(Self::usr_key("refs"))));
202 res!(uks.add(res!(Self::usr_key("alts"))));
203 res!(uks.add(res!(Self::usr_key("lnks"))));
204 res!(uks.add(res!(Self::usr_key("tags"))));
205 Ok(uks)
206 }
207
208 /// The `UsrKindId` code establishes an order when these kinds are used for `Map` keys, but
209 /// will be overridden by the order indices in `Entity::to_datmap` when forming an `OrdMap`.
210 /// The codes here have been set to differ from the ordinals in `to_datmap` just to emphasise
211 /// the difference.
212 pub fn usr_key(label: &str) -> Outcome<UsrKindId> {
213 Ok(match label {
214 "nams" => UsrKindId::new(10, Some("nams"), None),
215 "lang" => UsrKindId::new(20, Some("lang"), None),
216 "desc" => UsrKindId::new(30, Some("desc"), None),
217 "vers" => UsrKindId::new(35, Some("vers"), None),
218 "tim1" => UsrKindId::new(40, Some("tim1"), None),
219 "tim2" => UsrKindId::new(50, Some("tim2"), None),
220 "refs" => UsrKindId::new(60, Some("refs"), None),
221 "alts" => UsrKindId::new(65, Some("alts"), None),
222 "lnks" => UsrKindId::new(70, Some("lnks"), None),
223 "tags" => UsrKindId::new(80, Some("tags"), None),
224 _ => return Err(err!(
225 "Unrecognised usr key '{}'.", label;
226 Input, Invalid, Unknown, Bug)),
227 })
228 }
229
230 pub fn load<P: AsRef<Path>>(path: P) -> Outcome<Self> {
231 let s = res!(std::fs::read_to_string(path.as_ref()));
232 let mut jdat_dec = DecoderConfig::<_, _>::jdat(Some(res!(Namex::<M1, M2>::ukinds())));
233 // The registry preserves the order of entities and of each entity's attributes, so the
234 // decoder must retain the ordinals rather than collapsing maps to plain `Map`s.
235 jdat_dec.use_ordmaps = true;
236 let dat = res!(Dat::decode_string_with_config(s, &jdat_dec));
237 if let Dat::OrdMap(map0) = dat {
238 let mut map1 = M1::default();
239 let mut map2 = M2::default();
240 for (mk, vdat) in map0 {
241 let kdat = mk.dat();
242 let k = res!(NamexId::try_from(kdat));
243 // Manually build Entity from datmap.
244 let mut entity = Entity::default();
245 match res!(vdat.map_get_type(
246 &res!(Dat::try_from((res!(Self::usr_key("nams")), None))),
247 &[&Kind::List, &Kind::Vek],
248 )) {
249 Some(Dat::List(vecdat)) | Some(Dat::Vek(Vek(vecdat))) => {
250 let mut vecstr = Vec::new();
251 for d in vecdat {
252 let s = try_extract_dat!(d, Str);
253 vecstr.push(s.clone());
254 }
255 entity.nams = vecstr;
256 },
257 _ => return Err(err!(
258 "The key {:?} is missing required attribute 'nams'.", kdat;
259 Input, Missing)),
260 }
261 match res!(vdat.map_get_type(
262 &res!(Dat::try_from((res!(Self::usr_key("lang")), None))),
263 &[&Kind::Str],
264 )) {
265 Some(Dat::Str(s)) => entity.lang = s.clone(),
266 _ => return Err(err!(
267 "The key {:?} is missing required attribute 'lang'.", kdat;
268 Input, Missing)),
269 }
270 if let Some(Dat::Str(s)) = res!(vdat.map_get_type(
271 &res!(Dat::try_from((res!(Self::usr_key("desc")), None))),
272 &[&Kind::Str],
273 )) {
274 entity.desc = Some(s.clone());
275 }
276 if let Some(Dat::Str(s)) = res!(vdat.map_get_type(
277 &res!(Dat::try_from((res!(Self::usr_key("vers")), None))),
278 &[&Kind::Str],
279 )) {
280 entity.vers = Some(s.clone());
281 }
282 match res!(vdat.map_get_type(
283 &res!(Dat::try_from((res!(Self::usr_key("tags")), None))),
284 &[&Kind::List, &Kind::Vek],
285 )) {
286 Some(Dat::List(vecdat)) | Some(Dat::Vek(Vek(vecdat))) => {
287 let mut vecstr = Vec::new();
288 for d in vecdat {
289 let s = try_extract_dat!(d, Str);
290 vecstr.push(s.clone());
291 }
292 entity.tags = Some(vecstr);
293 },
294 _ => (),
295 }
296 if let Some(Dat::Str(s)) = res!(vdat.map_get_type(
297 &res!(Dat::try_from((res!(Self::usr_key("tim1")), None))),
298 &[&Kind::Str],
299 )) {
300 entity.tim1 = Some(s.clone()); // TODO validate date
301 }
302 if let Some(Dat::Str(s)) = res!(vdat.map_get_type(
303 &res!(Dat::try_from((res!(Self::usr_key("tim2")), None))),
304 &[&Kind::Str],
305 )) {
306 entity.tim2 = Some(s.clone()); // TODO validate date
307 }
308 match res!(vdat.map_get_type(&dat!("refs"), &[&Kind::List, &Kind::Vek])) {
309 Some(Dat::List(vecdat)) | Some(Dat::Vek(Vek(vecdat))) => {
310 let mut vecstr = Vec::new();
311 for d in vecdat {
312 let s = try_extract_dat!(d, Str);
313 vecstr.push(s.clone());
314 }
315 entity.tags = Some(vecstr);
316 },
317 _ => (),
318 }
319 match res!(vdat.map_get_type(
320 &res!(Dat::try_from((res!(Self::usr_key("refs")), None))),
321 &[&Kind::List, &Kind::Vek],
322 )) {
323 Some(Dat::List(vecdat)) | Some(Dat::Vek(Vek(vecdat))) => {
324 let mut vecstr = Vec::new();
325 for d in vecdat {
326 let s = try_extract_dat!(d, Str);
327 vecstr.push(s.clone());
328 }
329 entity.refs = Some(vecstr);
330 },
331 _ => (),
332 }
333 match res!(vdat.map_get_type(
334 &res!(Dat::try_from((res!(Self::usr_key("alts")), None))),
335 &[&Kind::OrdMap],
336 )) {
337 Some(Dat::OrdMap(datordmap)) => {
338 let mut altsmap = BTreeMap::new();
339 for (mapkey, vdat2) in datordmap {
340 let k2 = try_extract_dat!(mapkey.dat(), Str);
341 let v2 = try_extract_dat!(vdat2, Str);
342 altsmap.insert(k2.clone(), v2.clone());
343 }
344 entity.alts = Some(altsmap);
345 },
346 _ => (),
347 }
348 match res!(vdat.map_get_type(
349 &res!(Dat::try_from((res!(Self::usr_key("lnks")), None))),
350 &[&Kind::OrdMap],
351 )) {
352 Some(Dat::OrdMap(datordmap)) => {
353 let mut linkmap = BTreeMap::new();
354 for (mapkey, vdat2) in datordmap {
355 let mkdat = res!(NamexId::try_from(mapkey.dat()));
356 let v2 = try_extract_dat!(vdat2, Str);
357 linkmap.insert(mkdat.clone(), v2.clone());
358 }
359 entity.lnks = Some(linkmap);
360 },
361 _ => (),
362 }
363 map1.insert(
364 MapKey {
365 ord: mk.ord(),
366 id: NamexId::from(k.clone()),
367 },
368 entity.clone(),
369 );
370 map2.insert(
371 NamexId::from(k),
372 entity,
373 );
374 }
375 Ok(Namex {
376 ordered: map1,
377 map: map2,
378 })
379 } else {
380 Err(err!(
381 "Require a Daticle::Map, found a {}.", dat.kind();
382 Invalid, Input))
383 }
384 }
385
386 pub fn to_datmap(&self) -> Outcome<Dat> {
387 let mut map = BTreeMap::new();
388 for (umk, v) in self.ordered.iter() {
389 map.insert(
390 oxedyne_fe2o3_jdat::map::MapKey::new(umk.ord(), dat!(umk.id().to_string())),
391 res!(v.to_datmap::<M1, M2>()),
392 );
393 }
394 Ok(Dat::OrdMap(map))
395 }
396
397 pub fn export_jdat(&self) -> Outcome<Vec<String>> {
398 let dat = res!(self.to_datmap());
399 let mut jdat_enc = EncoderConfig::<_, _>::jdat(Some(res!(Namex::<M1, M2>::ukinds())));
400 jdat_enc.hide_usr_types = false;
401 let jdatstr = res!(dat.encode_string_with_config(&jdat_enc));
402 let lines = Stringer::new(jdatstr).to_lines("\t");
403 Ok(lines)
404 }
405
406 pub fn export_json(&self) -> Outcome<Vec<String>> {
407 let mut dat = res!(self.to_datmap());
408 // Scrub all strings in the daticle of escape characters.
409 res!(dat.normalise_string_values(" "));
410 let mut json_enc = EncoderConfig::<_, _>::json(Some(res!(Namex::<M1, M2>::ukinds())));
411 json_enc.byte_encoding = ByteEncoding::Base2x;
412 let jsonstr = res!(dat.encode_string_with_config(&json_enc));
413 let lines = Stringer::new(jsonstr).to_lines("\t");
414 Ok(lines)
415 }
416
417 pub fn to_file(
418 &self,
419 lines: Vec<String>,
420 path: &str,
421 )
422 -> Outcome<()>
423 {
424 let mut file = res!(std::fs::File::create(path));
425 info!("Writing namex to file {}...", path);
426 for mut line in lines {
427 line.push_str("\n");
428 res!(file.write(line.as_bytes()));
429 }
430 Ok(())
431 }
432}
433
434//pub enum Relationship {
435// Inheritance, // "is a"
436// Composition, // "has a"
437// Aggregation, // "is part of", "is contained in"
438// Ownership, // "is owned by"
439// Control, // "is controlled by"
440// Association, // "is associated with"
441// Dependency, // "depends on"
442// Creation, // "was created by"
443// Sibling, // "has the same creator as"
444//}