refactor(firmware): centralize configuration and diagnostics

This commit is contained in:
Onur Özkan committed 2026-08-22 18:37:18 +03:00
1 parent 7048ed89d0
commit 387fdaad23
140 files changed
+6891 -4541

No files matched your search

+184 -48
View File
@@ -1,63 +1,199 @@
//! Local scalar configuration sections implemented by the derive macro.
//! Generated configuration trees.
use crate::{ConfigValue, ConfigViolation, FieldId, FieldWriteError};
use crate::{ConfigValue, ConfigViolation, FieldDescriptor, FieldId, FieldWriteError};
/// Validates local scalar invariants of one record.
/// One recursively composable configuration tree.
///
/// The derive macro handles finite, nonzero, and inclusive range checks.
/// Cross-field, capability, calibration, and lifecycle invariants belong in a
/// handwritten post-validation hook on the owning vehicle schema.
pub trait ValidateFields {
/// Rejects the first violated scalar or declared post-validation rule.
fn validate_fields(&self) -> Result<(), ConfigViolation>;
}
/// A complete scalar configuration section generated from `#[config(...)]`
/// field attributes.
///
/// Its metadata, default construction, reads, local writes, and scalar
/// validation are all produced from the same field declarations. A section
/// setter never commits a locally invalid value. Cross-section and capability
/// invariants deliberately remain the aggregate candidate's responsibility.
pub trait ConfigSection: ValidateFields {
/// Number of scalar descriptors generated for this section.
/// `#[derive(kaptan_config::Config)]` implements this contract for both scalar
/// records and records composed from other configuration trees. Validation,
/// metadata, field routing, and schema identity therefore come from one source.
pub trait Config {
/// Number of scalar fields in the flattened tree.
const FIELD_COUNT: usize;
/// Builds the section from derive-generated defaults after validating it.
fn default_section() -> Result<Self, ConfigViolation>
where
Self: Sized;
/// Fingerprint generated from the complete declared schema.
const SCHEMA_ID: [u8; 16];
/// Returns a descriptor from the flattened declaration order.
fn descriptor(index: usize) -> Option<&'static FieldDescriptor>;
/// Returns one scalar by stable field identity.
fn field_value(&self, id: FieldId) -> Option<ConfigValue>;
/// Applies one scalar only when this section owns the ID and its local
/// generated validation accepts the new value.
/// Applies one scalar only when its owning record accepts the local value.
/// Whole-tree relational validation is intentionally deferred until the
/// candidate is validated or committed.
fn set_field(&mut self, id: FieldId, value: ConfigValue) -> Result<(), FieldWriteError>;
/// Validates only generated scalar invariants. This intentionally omits
/// a derive `post` hook so staged multi-field edits can remain temporarily
/// cross-field-invalid until aggregate validation/commit.
fn validate_local_fields(&self) -> Result<(), ConfigViolation>;
/// Validates every generated rule and declared post-validation hook.
fn validate(&self) -> Result<(), ConfigViolation>;
}
/// An ordered aggregate of generated configuration sections.
/// Detects duplicate flattened field identities in a generated tree.
#[doc(hidden)]
pub fn __unique_field_ids<C: Config>() -> bool {
let mut index = 0;
while index < C::FIELD_COUNT {
let Some(descriptor) = C::descriptor(index) else {
return false;
};
let mut other = index + 1;
while other < C::FIELD_COUNT {
let Some(other_descriptor) = C::descriptor(other) else {
return false;
};
if descriptor.id == other_descriptor.id {
return false;
}
other += 1;
}
index += 1;
}
C::descriptor(C::FIELD_COUNT).is_none()
}
/// Checks display-key uniqueness across one recursively composed schema.
#[doc(hidden)]
pub fn __unique_display_keys<C: Config>() -> bool {
let mut index = 0;
while index < C::FIELD_COUNT {
let Some(descriptor) = C::descriptor(index) else {
return false;
};
let mut other = index + 1;
while other < C::FIELD_COUNT {
let Some(other_descriptor) = C::descriptor(other) else {
return false;
};
if descriptor.display_key == other_descriptor.display_key {
return false;
}
other += 1;
}
index += 1;
}
true
}
/// Validates the bounded, self-describing enum portion of generated metadata.
#[doc(hidden)]
pub fn __valid_enum_options(descriptors: &[FieldDescriptor]) -> bool {
for descriptor in descriptors {
if descriptor.enum_options.len() > crate::MAX_ENUM_OPTIONS_PER_FIELD {
return false;
}
for (index, option) in descriptor.enum_options.iter().enumerate() {
if option.1.is_empty()
|| option.1.len() > crate::MAX_ENUM_OPTION_LABEL_BYTES
|| descriptor.kind != crate::ValueKind::U32
{
return false;
}
if descriptor.enum_options[..index]
.iter()
.any(|previous| previous.0 == option.0)
{
return false;
}
}
}
true
}
const FINGERPRINT_LEFT_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
const FINGERPRINT_RIGHT_OFFSET: u64 = 0x6c62_272e_07bb_0142;
const FINGERPRINT_LEFT_PRIME: u64 = 0x0000_0100_0000_01b3;
const FINGERPRINT_RIGHT_PRIME: u64 = 0x9e37_79b1_85eb_ca87;
const fn hash_byte(mut state: [u64; 2], byte: u8) -> [u64; 2] {
state[0] = (state[0] ^ byte as u64).wrapping_mul(FINGERPRINT_LEFT_PRIME);
state[1] = (state[1] ^ byte as u64).wrapping_mul(FINGERPRINT_RIGHT_PRIME);
state
}
const fn hash_u64(mut state: [u64; 2], value: u64) -> [u64; 2] {
let bytes = value.to_le_bytes();
let mut index = 0;
while index < bytes.len() {
state = hash_byte(state, bytes[index]);
index += 1;
}
state
}
const fn hash_str(mut state: [u64; 2], value: &str) -> [u64; 2] {
state = hash_u64(state, value.len() as u64);
let bytes = value.as_bytes();
let mut index = 0;
while index < bytes.len() {
state = hash_byte(state, bytes[index]);
index += 1;
}
state
}
const fn hash_value(mut state: [u64; 2], value: Option<ConfigValue>) -> [u64; 2] {
let Some(value) = value else {
return hash_byte(state, 0);
};
state = hash_byte(state, value.kind() as u8);
hash_u64(state, value.raw_bits())
}
const fn finish_fingerprint(state: [u64; 2]) -> [u8; 16] {
let left = state[0].to_le_bytes();
let right = state[1].to_le_bytes();
[
left[0], left[1], left[2], left[3], left[4], left[5], left[6], left[7], right[0], right[1],
right[2], right[3], right[4], right[5], right[6], right[7],
]
}
/// Produces the stable schema fingerprint for one scalar record.
#[doc(hidden)]
pub const fn __schema_id(descriptors: &[FieldDescriptor]) -> [u8; 16] {
let mut state = [FINGERPRINT_LEFT_OFFSET, FINGERPRINT_RIGHT_OFFSET];
state = hash_u64(state, descriptors.len() as u64);
let mut index = 0;
while index < descriptors.len() {
let descriptor = descriptors[index];
state = hash_u64(state, descriptor.id.get() as u64);
state = hash_str(state, descriptor.display_key);
state = hash_byte(state, descriptor.kind as u8);
state = hash_value(state, descriptor.minimum);
state = hash_value(state, descriptor.maximum);
state = hash_u64(state, descriptor.enum_options.len() as u64);
let mut option = 0;
while option < descriptor.enum_options.len() {
state = hash_u64(state, descriptor.enum_options[option].0 as u64);
state = hash_str(state, descriptor.enum_options[option].1);
option += 1;
}
state = hash_str(state, descriptor.unit);
index += 1;
}
finish_fingerprint(state)
}
/// Produces a parent fingerprint from ordered child fingerprints.
///
/// The derive owns routing/default construction so adding a field to an
/// existing section cannot silently bypass local validation. The aggregate
/// owner still defines capability checks and immutable active construction.
pub trait ConfigAggregate: ValidateFields {
/// Builds and fully validates all nested sections from their derived
/// defaults.
fn default_aggregate() -> Result<Self, ConfigViolation>
where
Self: Sized;
/// Returns one nested scalar by stable field identity.
fn field_value(&self, id: FieldId) -> Option<ConfigValue>;
/// Routes one scalar write to its owning generated section. The setter
/// applies local validation only, so a transaction may temporarily hold a
/// cross-field-invalid multi-edit candidate.
fn set_field(&mut self, id: FieldId, value: ConfigValue) -> Result<(), FieldWriteError>;
/// A one-child wrapper is deliberately transparent, so adding or removing a
/// redundant structural wrapper does not invalidate persisted values.
#[doc(hidden)]
pub const fn __combine_schema_ids(children: &[[u8; 16]]) -> [u8; 16] {
if children.len() == 1 {
return children[0];
}
let mut state = [FINGERPRINT_LEFT_OFFSET, FINGERPRINT_RIGHT_OFFSET];
state = hash_u64(state, children.len() as u64);
let mut child = 0;
while child < children.len() {
let mut byte = 0;
while byte < children[child].len() {
state = hash_byte(state, children[child][byte]);
byte += 1;
}
child += 1;
}
finish_fingerprint(state)
}