qualia_core_db/governance/
modal_kind.rs1use crate::indexing::QuinIndex;
19use crate::{q_hash, NQuin};
20
21pub const HAS_MODALITY_KIND: u64 = q_hash("https://ns.webcivics.net/cml/hasModalityKind");
23
24pub const KIND_DICTIONARY: u64 = q_hash("https://ns.webcivics.net/kind/DictionaryHash");
27pub const KIND_WEBIZEN: u64 = q_hash("https://ns.webcivics.net/kind/WebizenId");
28pub const KIND_DID_Q42: u64 = q_hash("https://ns.webcivics.net/kind/DidQ42");
29pub const KIND_DID: u64 = q_hash("https://ns.webcivics.net/kind/Did");
30pub const KIND_CONTENT_HASH: u64 = q_hash("https://ns.webcivics.net/kind/ContentHash");
31pub const KIND_CLUSTER_NODE: u64 = q_hash("https://ns.webcivics.net/kind/ClusterNode");
32
33#[inline]
38pub fn tag_kind(identifier: u64, kind: u64) -> NQuin {
39 NQuin {
40 subject: identifier,
41 predicate: HAS_MODALITY_KIND,
42 object: kind,
43 context: 0,
44 metadata: 0,
45 parity: identifier ^ HAS_MODALITY_KIND ^ kind,
46 }
47}
48
49#[inline]
53pub fn resolve_kind(index: &QuinIndex, identifier: u64) -> Option<u64> {
54 index.object_of(identifier, HAS_MODALITY_KIND)
55}
56
57pub fn kind_name(kind: u64) -> Option<&'static str> {
60 match kind {
61 KIND_DICTIONARY => Some("DictionaryHash"),
62 KIND_WEBIZEN => Some("WebizenId"),
63 KIND_DID_Q42 => Some("DidQ42"),
64 KIND_DID => Some("Did"),
65 KIND_CONTENT_HASH => Some("ContentHash"),
66 KIND_CLUSTER_NODE => Some("ClusterNode"),
67 _ => None,
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn resolves_dictionary_identifier_kind() {
77 let alice = q_hash("https://example.org/alice"); let idx = QuinIndex::from_slice(&[tag_kind(alice, KIND_DICTIONARY)]);
79 assert_eq!(resolve_kind(&idx, alice), Some(KIND_DICTIONARY));
80 }
81
82 #[test]
83 fn resolves_full_64bit_identifier_kind() {
84 let content_id: u64 = 0xF234_5678_9ABC_DEF0;
89 assert_ne!(content_id >> 60, 0, "test id genuinely uses the top nibble");
90 let idx = QuinIndex::from_slice(&[tag_kind(content_id, KIND_CONTENT_HASH)]);
91 assert_eq!(resolve_kind(&idx, content_id), Some(KIND_CONTENT_HASH));
92 }
93
94 #[test]
95 fn distinct_identifiers_keep_distinct_kinds() {
96 let a = q_hash("did:q42:aaa");
97 let b: u64 = 0x8000_0000_0000_0001; let idx = QuinIndex::from_slice(&[tag_kind(a, KIND_DID_Q42), tag_kind(b, KIND_WEBIZEN)]);
99 assert_eq!(resolve_kind(&idx, a), Some(KIND_DID_Q42));
100 assert_eq!(resolve_kind(&idx, b), Some(KIND_WEBIZEN));
101 }
102
103 #[test]
104 fn unkinded_identifier_resolves_none() {
105 let idx = QuinIndex::from_slice(&[]);
106 assert_eq!(resolve_kind(&idx, q_hash("x")), None);
107 }
108
109 #[test]
110 fn kinds_are_distinct_and_tag_free() {
111 let kinds = [
113 KIND_DICTIONARY,
114 KIND_WEBIZEN,
115 KIND_DID_Q42,
116 KIND_DID,
117 KIND_CONTENT_HASH,
118 KIND_CLUSTER_NODE,
119 HAS_MODALITY_KIND,
120 ];
121 for (i, a) in kinds.iter().enumerate() {
122 assert_eq!(a >> 60, 0, "kind term must be a pure 60-bit identifier");
123 for b in &kinds[i + 1..] {
124 assert_ne!(a, b, "kind terms must be distinct");
125 }
126 }
127 }
128}