qualia_core_db/entity_view/
entity_id.rs1use crate::hypermedia::fnv60;
7use crate::q_hash;
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
12#[repr(transparent)]
13pub struct EntityId(pub u64);
14
15impl EntityId {
16 pub const fn from_raw(id: u64) -> Self {
17 Self(id)
18 }
19
20 pub const fn raw(self) -> u64 {
21 self.0
22 }
23
24 pub fn is_unset(self) -> bool {
25 self.0 == 0
26 }
27
28 pub fn from_bytes(bytes: &[u8]) -> Self {
30 Self(fnv60(bytes))
31 }
32
33 pub fn from_uri(uri: &str) -> Self {
35 Self::from_bytes(uri.trim().as_bytes())
36 }
37
38 pub fn from_iri_q_hash(iri: &str) -> Self {
41 Self(q_hash(iri))
42 }
43
44 pub fn from_did(did: &str) -> Self {
46 Self::from_uri(did)
47 }
48
49 pub fn from_fragment(parent_uri: &str, fragment_key: &str) -> Self {
51 let mut buf = Vec::with_capacity(parent_uri.len() + fragment_key.len() + 1);
52 buf.extend_from_slice(parent_uri.as_bytes());
53 buf.push(0x1f);
54 buf.extend_from_slice(fragment_key.as_bytes());
55 Self::from_bytes(&buf)
56 }
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
61#[serde(rename_all = "snake_case")]
62#[repr(u8)]
63pub enum EntityKind {
64 #[default]
65 Unknown = 0,
66 Agent = 1,
68 Instrument = 2,
70 Asset = 3,
72 WebLocus = 4,
74 Fragment = 5,
76 Commitment = 6,
78 Layer = 7,
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn uri_ids_are_stable_and_distinct() {
88 let a = EntityId::from_uri("https://example.org/a");
89 let b = EntityId::from_uri("https://example.org/b");
90 assert_eq!(a, EntityId::from_uri("https://example.org/a"));
91 assert_ne!(a, b);
92 assert!(!a.is_unset());
93 }
94
95 #[test]
96 fn fragment_differs_from_parent() {
97 let parent = EntityId::from_uri("urn:doc:lesson");
98 let frag = EntityId::from_fragment("urn:doc:lesson", "span:10-40");
99 assert_ne!(parent, frag);
100 }
101}