refactor(firmware): simplify configuration and diagnostics
This commit is contained in:
1 parent
ba9c97ed3b
commit
fdf82a5114
96 files changed
+1612
-2772
No files matched your search
@@ -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" }
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user