refactor(firmware): simplify configuration and diagnostics

This commit is contained in:
Onur Özkan committed 2026-08-23 00:40:36 +03:00
1 parent ba9c97ed3b
commit fdf82a5114
96 files changed
+1612 -2772

No files matched your search

+1
View File
@@ -10,4 +10,5 @@ workspace = true
[dependencies]
kaptan-config-derive = { path = "../kaptan-config-derive" }
kaptan-err-propagate = { path = "../kaptan-err-propagate" }
kaptan-log = { path = "../kaptan-log" }
+4 -1
View File
@@ -1,7 +1,7 @@
//! Transport-neutral typed configuration fields and descriptors.
use crate::{ConfigViolation, ValidationRule};
use kaptan_log::KaptanError;
use kaptan_err_propagate::KaptanError;
/// Maximum choices representable by one enum-backed field.
///
@@ -234,8 +234,10 @@ pub struct FieldDescriptor {
#[derive(Clone, Copy, Debug, PartialEq, KaptanError)]
pub enum FieldWriteError {
/// No descriptor exposes the requested identity.
#[kaptan_error(id = 1001)]
UnknownField(FieldId),
/// The supplied scalar type differs from the descriptor type.
#[kaptan_error(id = 1002)]
WrongType {
/// Requested field.
field: FieldId,
@@ -245,6 +247,7 @@ pub enum FieldWriteError {
actual: ValueKind,
},
/// A locally validated scalar violated a stable rule.
#[kaptan_error(id = 1003)]
Violation(ConfigViolation),
}
+33 -10
View File
@@ -4,7 +4,7 @@ use crate::{
ConfigModel, ConfigStore, ConfigValue, ConfigViolation, FieldDescriptor, FieldId,
FieldWriteError,
};
use kaptan_log::KaptanError;
use kaptan_err_propagate::KaptanError;
/// Observable state of the bounded configuration transaction slot.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -21,8 +21,10 @@ pub enum ConfigState {
#[derive(Debug, KaptanError)]
pub enum StartupError<E> {
/// The storage backend could not provide a committed record.
#[kaptan_error(id = 1101)]
Store(E),
/// A committed record did not validate against this firmware's schema.
#[kaptan_error(id = 1102)]
InvalidPersisted(ConfigViolation),
}
@@ -30,6 +32,7 @@ pub enum StartupError<E> {
#[derive(Clone, Copy, Debug, Eq, PartialEq, KaptanError)]
pub enum BeginError {
/// Exactly one transaction is already open.
#[kaptan_error(id = 1201)]
TransactionActive,
}
@@ -37,8 +40,10 @@ pub enum BeginError {
#[derive(Clone, Copy, Debug, PartialEq, KaptanError)]
pub enum SetError {
/// No transaction is open.
#[kaptan_error(id = 1301)]
NoTransaction,
/// The candidate rejected the local scalar without mutation.
#[kaptan_error(id = 1302)]
Field(FieldWriteError),
}
@@ -46,10 +51,13 @@ pub enum SetError {
#[derive(Debug, PartialEq, KaptanError)]
pub enum CommitError<E> {
/// No transaction is open.
#[kaptan_error(id = 1401)]
NoTransaction,
/// The complete candidate violates a relational or capability invariant.
#[kaptan_error(id = 1402)]
InvalidCandidate(ConfigViolation),
/// The storage backend could not commit the snapshot.
#[kaptan_error(id = 1403)]
Store(E),
}
@@ -197,6 +205,7 @@ where
return Err(BeginError::TransactionActive);
}
self.transaction = Some(self.committed.clone());
kaptan_log::debug!("edit transaction opened");
Ok(())
}
@@ -219,10 +228,16 @@ where
.transaction
.as_ref()
.ok_or(CommitError::NoTransaction)?;
candidate
.validate(&self.context)
.map(|_| ())
.map_err(CommitError::InvalidCandidate)
match candidate.validate(&self.context) {
Ok(_) => {
kaptan_log::debug!("candidate validated");
Ok(())
}
Err(violation) => {
kaptan_log::warn!("candidate validation failed");
Err(CommitError::InvalidCandidate(violation))
}
}
}
/// Validates and durably commits the complete open transaction.
@@ -231,19 +246,27 @@ where
.transaction
.as_ref()
.ok_or(CommitError::NoTransaction)?;
candidate
.validate(&self.context)
.map_err(CommitError::InvalidCandidate)?;
self.store.persist(candidate).map_err(CommitError::Store)?;
if let Err(violation) = candidate.validate(&self.context) {
kaptan_log::warn!("commit validation failed");
return Err(CommitError::InvalidCandidate(violation));
}
if let Err(error) = self.store.persist(candidate) {
return Err(CommitError::Store(error));
}
self.committed = candidate.clone();
self.transaction = None;
self.restart_required = true;
kaptan_log::info!("configuration saved; restart required");
Ok(())
}
/// Drops the RAM transaction without changing active or committed state.
pub fn abort(&mut self) -> bool {
self.transaction.take().is_some()
let aborted = self.transaction.take().is_some();
if aborted {
kaptan_log::debug!("edit transaction aborted");
}
aborted
}
}
+23 -1
View File
@@ -16,7 +16,7 @@
//! field IDs and therefore also accepts a complete, uniquely identified set in
//! another order.
use kaptan_log::KaptanError;
use kaptan_err_propagate::KaptanError;
use crate::{ConfigModel, ConfigValue, FieldId, FieldWriteError, ValueKind};
@@ -48,14 +48,19 @@ pub const fn scalar_snapshot_length(field_count: u16) -> usize {
#[derive(Clone, Copy, Debug, Eq, PartialEq, KaptanError)]
pub enum SnapshotModelError {
/// `field_count` includes an index with no descriptor.
#[kaptan_error(id = 1501)]
MissingDescriptor(u16),
/// A descriptor exists at the first index after `field_count`.
#[kaptan_error(id = 1502)]
ExtraDescriptor(u16),
/// Two descriptor indexes expose the same stable field identity.
#[kaptan_error(id = 1503)]
DuplicateDescriptor(FieldId),
/// A descriptor has no readable value in the supplied candidate.
#[kaptan_error(id = 1504)]
MissingValue(FieldId),
/// A candidate returned a scalar type different from its descriptor.
#[kaptan_error(id = 1505)]
WrongValueKind {
/// Descriptor identity.
field: FieldId,
@@ -70,6 +75,7 @@ pub enum SnapshotModelError {
#[derive(Clone, Copy, Debug, Eq, PartialEq, KaptanError)]
pub enum ScalarSnapshotEncodeError {
/// The destination cannot hold the complete canonical payload.
#[kaptan_error(id = 1601)]
BufferTooSmall {
/// Exact number of bytes required.
required: usize,
@@ -77,6 +83,7 @@ pub enum ScalarSnapshotEncodeError {
available: usize,
},
/// The model's descriptor/value contract is internally inconsistent.
#[kaptan_error(id = 1602)]
InvalidModel(SnapshotModelError),
}
@@ -84,6 +91,7 @@ pub enum ScalarSnapshotEncodeError {
#[derive(Clone, Copy, Debug, PartialEq, KaptanError)]
pub enum ScalarSnapshotDecodeError {
/// The input ends before the complete header or declared field set.
#[kaptan_error(id = 1701)]
Truncated {
/// Exact number of bytes required once it is known.
required: usize,
@@ -91,6 +99,7 @@ pub enum ScalarSnapshotDecodeError {
available: usize,
},
/// Bytes remain after the exact declared field set.
#[kaptan_error(id = 1702)]
TrailingData {
/// Exact number of bytes required.
expected: usize,
@@ -98,10 +107,13 @@ pub enum ScalarSnapshotDecodeError {
actual: usize,
},
/// The payload does not carry the scalar-snapshot magic.
#[kaptan_error(id = 1703)]
InvalidMagic,
/// The payload uses an unsupported scalar-snapshot format.
#[kaptan_error(id = 1704)]
UnsupportedVersion(u8),
/// The payload belongs to another configuration schema.
#[kaptan_error(id = 1705)]
WrongSchema {
/// Schema compiled into the target model.
expected: [u8; 16],
@@ -109,6 +121,7 @@ pub enum ScalarSnapshotDecodeError {
actual: [u8; 16],
},
/// The payload is not a complete snapshot for this schema.
#[kaptan_error(id = 1706)]
WrongFieldCount {
/// Exact number of fields compiled into the target model.
expected: u16,
@@ -116,18 +129,25 @@ pub enum ScalarSnapshotDecodeError {
actual: u16,
},
/// The target model's descriptor registry is internally inconsistent.
#[kaptan_error(id = 1707)]
InvalidModel(SnapshotModelError),
/// An entry used a reserved stable field identity.
#[kaptan_error(id = 1708)]
InvalidFieldId(u16),
/// An entry names no field in the target schema.
#[kaptan_error(id = 1709)]
UnknownField(FieldId),
/// The complete payload identifies one field more than once.
#[kaptan_error(id = 1710)]
DuplicateField(FieldId),
/// The complete payload does not identify a required schema field.
#[kaptan_error(id = 1711)]
MissingField(FieldId),
/// An entry contains no supported scalar-kind discriminator.
#[kaptan_error(id = 1712)]
InvalidValueKind(u8),
/// An entry's scalar kind differs from the target descriptor.
#[kaptan_error(id = 1713)]
WrongValueKind {
/// Entry identity.
field: FieldId,
@@ -137,6 +157,7 @@ pub enum ScalarSnapshotDecodeError {
actual: ValueKind,
},
/// Scalar bits are not canonical for the entry's declared kind.
#[kaptan_error(id = 1714)]
InvalidValueBits {
/// Entry identity.
field: FieldId,
@@ -144,6 +165,7 @@ pub enum ScalarSnapshotDecodeError {
kind: ValueKind,
},
/// The model rejected an otherwise structurally valid scalar write.
#[kaptan_error(id = 1715)]
InvalidWrite(FieldWriteError),
}