Skip to main content

khive_types/
substrate.rs

1//! Substrate discriminant — the 3 data types in khive (ADR-004).
2//!
3//! Full substrate structs live in the sibling modules (`note`, `entity`,
4//! `event`). This module provides the discriminant for typed dispatch and
5//! persistence.
6
7use core::fmt;
8use core::str::FromStr;
9
10/// The 3 substrate types in khive OSS (ADR-004).
11///
12/// - **Note**: temporal-referential records (observations, insights, decisions)
13/// - **Entity**: graph nodes with properties and typed links
14/// - **Event**: universal system log — every verb execution produces one
15#[repr(u8)]
16#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
19pub enum SubstrateKind {
20    Note = 0,
21    Entity = 1,
22    Event = 2,
23}
24
25pub const SUBSTRATE_COUNT: usize = 3;
26
27impl SubstrateKind {
28    pub const ALL: [SubstrateKind; SUBSTRATE_COUNT] = [
29        SubstrateKind::Note,
30        SubstrateKind::Entity,
31        SubstrateKind::Event,
32    ];
33
34    #[inline]
35    pub const fn name(self) -> &'static str {
36        match self {
37            Self::Note => "note",
38            Self::Entity => "entity",
39            Self::Event => "event",
40        }
41    }
42
43    #[inline]
44    pub const fn from_u8(v: u8) -> Option<Self> {
45        match v {
46            0 => Some(Self::Note),
47            1 => Some(Self::Entity),
48            2 => Some(Self::Event),
49            _ => None,
50        }
51    }
52}
53
54impl fmt::Display for SubstrateKind {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        f.write_str(self.name())
57    }
58}
59
60impl FromStr for SubstrateKind {
61    type Err = SubstrateError;
62
63    fn from_str(s: &str) -> Result<Self, Self::Err> {
64        match s {
65            "note" | "Note" => Ok(Self::Note),
66            "entity" | "Entity" => Ok(Self::Entity),
67            "event" | "Event" => Ok(Self::Event),
68            _ => Err(SubstrateError::UnknownKind),
69        }
70    }
71}
72
73#[derive(Clone, Copy, Debug, PartialEq, Eq)]
74pub enum SubstrateError {
75    UnknownKind,
76}
77
78impl fmt::Display for SubstrateError {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        match self {
81            Self::UnknownKind => f.write_str("unknown substrate kind"),
82        }
83    }
84}
85
86#[cfg(feature = "std")]
87impl std::error::Error for SubstrateError {}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn all_variants() {
95        assert_eq!(SubstrateKind::ALL.len(), SUBSTRATE_COUNT);
96        for (i, &kind) in SubstrateKind::ALL.iter().enumerate() {
97            assert_eq!(kind as u8, i as u8);
98            assert_eq!(SubstrateKind::from_u8(i as u8), Some(kind));
99        }
100    }
101
102    #[test]
103    fn parse_roundtrip() {
104        for kind in SubstrateKind::ALL {
105            let parsed: SubstrateKind = kind.name().parse().unwrap();
106            assert_eq!(parsed, kind);
107        }
108    }
109
110    #[test]
111    fn out_of_range() {
112        assert_eq!(SubstrateKind::from_u8(3), None);
113        assert_eq!(SubstrateKind::from_u8(255), None);
114    }
115}