1#![cfg_attr(not(feature = "std"), no_std)]
85#![cfg_attr(docsrs, feature(doc_cfg))]
86
87#[cfg(all(not(feature = "std"), feature = "serde"))]
88extern crate alloc;
89
90#[cfg(all(not(feature = "std"), feature = "serde"))]
91use alloc::{string::String, vec::Vec};
92
93#[cfg(feature = "serde")]
94use serde::{Deserialize, Serialize};
95
96pub mod trust_hierarchy;
98
99pub const TRUST_RAW: u8 = 0x00;
101pub const TRUST_VERIFIED: u8 = 0x01;
102pub const TRUST_ANOMALY: u8 = 0x02;
103pub const TRUST_ENTERPRISE: u8 = 0xFF;
104
105pub const PREDICATE_NORMAL: u32 = 0x00000001;
106pub const PREDICATE_WARNING: u32 = 0x00000002;
107pub const PREDICATE_CRITICAL: u32 = 0x00000003;
108pub const PREDICATE_CONSISTENCY: u32 = 0x00000004;
109pub const PREDICATE_TRIANGULATED: u32 = 0x00000005;
110pub const PREDICATE_ANOMALY: u32 = 0x00000006;
111pub const PREDICATE_TREND_STABLE: u32 = 0x00000007;
112pub const PREDICATE_TREND_RISING_FAST: u32 = 0x00000008;
113pub const PREDICATE_SENSOR_STUCK_WARNING: u32 = 0x00000009;
114pub const PREDICATE_API_DATA_INCONSISTENCY: u32 = 0x0000000A;
115pub const PREDICATE_POTENTIAL_DATA_ANOMALY: u32 = 0x0000000B;
116
117pub const TELEMETRY_WATER_LEVEL_MM: u16 = 0x0001;
118pub const TELEMETRY_TEMPERATURE_C: u16 = 0x0002;
119pub const TELEMETRY_HUMIDITY_PERCENT: u16 = 0x0003;
120pub const TELEMETRY_PRESSURE_PA: u16 = 0x0004;
121pub const TELEMETRY_PRECIPITATION_MM: u16 = 0x0005;
122
123pub const HEADER_VERIFIED: u16 = 0x8000;
124pub const HEADER_SIGNED: u16 = 0x4000;
125pub const HEADER_ENCRYPTED: u16 = 0x2000;
126pub const HEADER_RAW_DATA: u16 = 0x0001;
127
128pub const TAG_HYDROLOGICAL: u32 = 0x000001;
129pub const TAG_METEOROLOGICAL: u32 = 0x000002;
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
133#[repr(C)]
134pub struct SemanticAtom {
135 pub entity_id: u32,
136 pub telemetry_info: u32,
137 pub predicate_id: u32,
138 pub value_mm: u32,
139 pub timestamp_us: u64,
140 pub tag_trust: u32,
141 pub pqc_anchor: u32,
142}
143
144impl SemanticAtom {
145 #[inline]
146 pub fn new(
147 entity_id: u32,
148 telemetry_type: u16,
149 predicate_id: u32,
150 value_mm: u32,
151 timestamp_us: u64,
152 tag_id: u32,
153 trust_level: u8,
154 ) -> Self {
155 let telemetry_info = (telemetry_type as u32) << 16;
156 let tag_trust = ((tag_id & 0x00FFFFFF) << 8) | (trust_level as u32);
157
158 Self {
159 entity_id,
160 telemetry_info,
161 predicate_id,
162 value_mm,
163 timestamp_us,
164 tag_trust,
165 pqc_anchor: 0,
166 }
167 }
168
169 #[inline]
170 pub fn telemetry_type(&self) -> u16 {
171 (self.telemetry_info >> 16) as u16
172 }
173 #[inline]
174 pub fn tag_id(&self) -> u32 {
175 self.tag_trust >> 8
176 }
177 #[inline]
178 pub fn trust_level(&self) -> u8 {
179 (self.tag_trust & 0xFF) as u8
180 }
181 #[inline]
182 pub fn set_pqc_anchor(&mut self, anchor: u32) {
183 self.pqc_anchor = anchor;
184 }
185
186 #[inline]
187 pub fn is_critical_or_warning(&self) -> bool {
188 matches!(
189 self.predicate_id,
190 PREDICATE_WARNING
191 | PREDICATE_CRITICAL
192 | PREDICATE_API_DATA_INCONSISTENCY
193 | PREDICATE_POTENTIAL_DATA_ANOMALY
194 )
195 }
196
197 #[inline]
198 pub fn get_value(&self) -> f64 {
199 self.value_mm as f64 / 100.0
200 }
201
202 #[inline]
203 pub fn to_bytes(&self) -> [u8; 32] {
204 let mut bytes = [0u8; 32];
205 bytes[0..4].copy_from_slice(&self.entity_id.to_le_bytes());
206 bytes[4..8].copy_from_slice(&self.telemetry_info.to_le_bytes());
207 bytes[8..12].copy_from_slice(&self.predicate_id.to_le_bytes());
208 bytes[12..16].copy_from_slice(&self.value_mm.to_le_bytes());
209 bytes[16..24].copy_from_slice(&self.timestamp_us.to_le_bytes());
210 bytes[24..28].copy_from_slice(&self.tag_trust.to_le_bytes());
211 bytes[28..32].copy_from_slice(&self.pqc_anchor.to_le_bytes());
212 bytes
213 }
214
215 pub fn from_bytes(bytes: &[u8]) -> Result<Self, &'static str> {
216 if bytes.len() != 32 {
217 return Err("Invalid length");
218 }
219 Ok(Self {
220 entity_id: u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
221 telemetry_info: u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
222 predicate_id: u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]),
223 value_mm: u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]),
224 timestamp_us: u64::from_le_bytes([
225 bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22],
226 bytes[23],
227 ]),
228 tag_trust: u32::from_le_bytes([bytes[24], bytes[25], bytes[26], bytes[27]]),
229 pqc_anchor: u32::from_le_bytes([bytes[28], bytes[29], bytes[30], bytes[31]]),
230 })
231 }
232
233 #[cfg(feature = "pqc")]
234 pub fn compute_hash(&self) -> [u8; 32] {
235 use sha2::{Digest, Sha256};
236 let mut hasher = Sha256::new();
237 hasher.update(self.to_bytes());
238 hasher.finalize().into()
239 }
240}
241
242#[derive(Debug, Clone, PartialEq)]
245#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
246pub enum AtomError {
247 InvalidAtom(&'static str),
248 InvalidEntity(u32),
249 InvalidPredicate(u32),
250 PhysicsViolation(&'static str),
251 InvalidTimestamp,
252 CryptographicError(&'static str),
253 InvalidLength(usize),
254}
255
256impl core::fmt::Display for AtomError {
257 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
258 match self {
259 AtomError::InvalidAtom(msg) => write!(f, "Invalid atom: {}", msg),
260 AtomError::InvalidEntity(id) => write!(f, "Invalid entity ID: 0x{:08X}", id),
261 AtomError::InvalidPredicate(id) => write!(f, "Invalid predicate ID: 0x{:08X}", id),
262 AtomError::PhysicsViolation(msg) => write!(f, "Physics violation: {}", msg),
263 AtomError::InvalidTimestamp => write!(f, "Invalid timestamp"),
264 AtomError::CryptographicError(msg) => write!(f, "Cryptographic error: {}", msg),
265 AtomError::InvalidLength(len) => write!(f, "Invalid length: {} (expected 32)", len),
266 }
267 }
268}
269
270#[cfg(feature = "std")]
271impl std::error::Error for AtomError {}
272
273pub type BsaResult<T> = Result<T, AtomError>;
274
275#[cfg(feature = "serde")]
278#[derive(Debug, Clone, Serialize, Deserialize)]
279pub struct Entity {
280 pub id: u32,
281 pub name: String,
282 pub description: String,
283 pub source: Option<String>,
284}
285
286#[cfg(feature = "serde")]
287#[derive(Debug, Clone, Serialize, Deserialize)]
288pub struct Predicate {
289 pub id: u32,
290 pub name: String,
291 pub description: String,
292 pub tag: String,
293}
294
295#[cfg(feature = "serde")]
296#[derive(Debug, Clone, Serialize, Deserialize)]
297pub struct Config {
298 pub entities: Vec<Entity>,
299 pub predicates: Vec<Predicate>,
300}