Skip to main content

rvf_types/
profile.rs

1//! Hardware and domain profile identifiers.
2
3/// Hardware profile ID (stored in root manifest `profile_id` for hardware tier).
4///
5/// Determines the runtime behaviour profile (memory budget, tier policy, etc.).
6#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8#[repr(u8)]
9pub enum ProfileId {
10    /// Generic / minimal profile.
11    Generic = 0,
12    /// Core profile (moderate resources).
13    Core = 1,
14    /// Hot profile (high-performance, memory-rich).
15    Hot = 2,
16    /// Full profile (all features enabled).
17    Full = 3,
18}
19
20impl TryFrom<u8> for ProfileId {
21    type Error = u8;
22
23    fn try_from(value: u8) -> Result<Self, Self::Error> {
24        match value {
25            0 => Ok(Self::Generic),
26            1 => Ok(Self::Core),
27            2 => Ok(Self::Hot),
28            3 => Ok(Self::Full),
29            other => Err(other),
30        }
31    }
32}
33
34/// Domain profile discriminator (semantic overlay on the RVF substrate).
35///
36/// Stored in the root manifest `profile_id` field and declared in PROFILE_SEG.
37#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
38#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
39#[repr(u8)]
40pub enum DomainProfile {
41    /// Generic / unspecified domain.
42    Generic = 0,
43    /// Genomics (RVDNA) -- codon, k-mer, motif, structure embeddings.
44    Rvdna = 1,
45    /// Language / text (RVText) -- sentence, paragraph, document embeddings.
46    RvText = 2,
47    /// Graph / network (RVGraph) -- node, edge, subgraph embeddings.
48    RvGraph = 3,
49    /// Vision / imagery (RVVision) -- patch, image, object embeddings.
50    RvVision = 4,
51}
52
53impl DomainProfile {
54    /// The 4-byte magic number associated with each domain profile.
55    pub const fn magic(self) -> u32 {
56        match self {
57            Self::Generic => 0x0000_0000,
58            Self::Rvdna => 0x5244_4E41,   // "RDNA"
59            Self::RvText => 0x5254_5854,   // "RTXT"
60            Self::RvGraph => 0x5247_5248,  // "RGRH"
61            Self::RvVision => 0x5256_4953, // "RVIS"
62        }
63    }
64}
65
66impl DomainProfile {
67    /// The canonical file extension for this domain profile.
68    pub const fn extension(&self) -> &'static str {
69        match self {
70            Self::Generic => "rvf",
71            Self::Rvdna => "rvdna",
72            Self::RvText => "rvtext",
73            Self::RvGraph => "rvgraph",
74            Self::RvVision => "rvvis",
75        }
76    }
77
78    /// Look up a domain profile from a file extension (case-insensitive).
79    pub fn from_extension(ext: &str) -> Option<Self> {
80        // Manual case-insensitive comparison for no_std compatibility
81        let ext_bytes = ext.as_bytes();
82        if eq_ignore_ascii_case(ext_bytes, b"rvf") {
83            Some(Self::Generic)
84        } else if eq_ignore_ascii_case(ext_bytes, b"rvdna") {
85            Some(Self::Rvdna)
86        } else if eq_ignore_ascii_case(ext_bytes, b"rvtext") {
87            Some(Self::RvText)
88        } else if eq_ignore_ascii_case(ext_bytes, b"rvgraph") {
89            Some(Self::RvGraph)
90        } else if eq_ignore_ascii_case(ext_bytes, b"rvvis") {
91            Some(Self::RvVision)
92        } else {
93            None
94        }
95    }
96}
97
98/// Case-insensitive ASCII byte comparison.
99fn eq_ignore_ascii_case(a: &[u8], b: &[u8]) -> bool {
100    if a.len() != b.len() {
101        return false;
102    }
103    a.iter().zip(b.iter()).all(|(x, y)| x.eq_ignore_ascii_case(y))
104}
105
106impl TryFrom<u8> for DomainProfile {
107    type Error = u8;
108
109    fn try_from(value: u8) -> Result<Self, Self::Error> {
110        match value {
111            0 => Ok(Self::Generic),
112            1 => Ok(Self::Rvdna),
113            2 => Ok(Self::RvText),
114            3 => Ok(Self::RvGraph),
115            4 => Ok(Self::RvVision),
116            other => Err(other),
117        }
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    #[test]
126    fn profile_id_round_trip() {
127        for raw in 0..=3u8 {
128            let p = ProfileId::try_from(raw).unwrap();
129            assert_eq!(p as u8, raw);
130        }
131        assert_eq!(ProfileId::try_from(4), Err(4));
132    }
133
134    #[test]
135    fn domain_profile_round_trip() {
136        for raw in 0..=4u8 {
137            let d = DomainProfile::try_from(raw).unwrap();
138            assert_eq!(d as u8, raw);
139        }
140        assert_eq!(DomainProfile::try_from(5), Err(5));
141    }
142
143    #[test]
144    fn domain_extension_round_trip() {
145        let profiles = [
146            DomainProfile::Generic,
147            DomainProfile::Rvdna,
148            DomainProfile::RvText,
149            DomainProfile::RvGraph,
150            DomainProfile::RvVision,
151        ];
152        for p in profiles {
153            let ext = p.extension();
154            let back = DomainProfile::from_extension(ext).unwrap();
155            assert_eq!(back, p, "round-trip failed for {ext}");
156        }
157    }
158
159    #[test]
160    fn domain_extension_case_insensitive() {
161        assert_eq!(DomainProfile::from_extension("RVDNA"), Some(DomainProfile::Rvdna));
162        assert_eq!(DomainProfile::from_extension("RvF"), Some(DomainProfile::Generic));
163        assert_eq!(DomainProfile::from_extension("RvText"), Some(DomainProfile::RvText));
164    }
165
166    #[test]
167    fn domain_extension_unknown() {
168        assert_eq!(DomainProfile::from_extension("txt"), None);
169        assert_eq!(DomainProfile::from_extension(""), None);
170    }
171
172    #[test]
173    fn domain_magic_values() {
174        assert_eq!(&DomainProfile::Rvdna.magic().to_be_bytes(), b"RDNA");
175        assert_eq!(&DomainProfile::RvText.magic().to_be_bytes(), b"RTXT");
176        assert_eq!(&DomainProfile::RvGraph.magic().to_be_bytes(), b"RGRH");
177        assert_eq!(&DomainProfile::RvVision.magic().to_be_bytes(), b"RVIS");
178    }
179}