Files
kaptan/firmware/libs/kaptan-config-mavlink/src/service/query.rs
T

209 lines
6.5 KiB
Rust

//! Read-only protocol discovery and value-query responses.
use kaptan_config::{
ConfigManager, ConfigModel, ConfigState, ConfigValue, FieldDescriptor, FieldId, Generation,
ProfileStore,
};
use super::{import::SnapshotImporter, session::ConfigProtocol};
use crate::{ProtocolError, ValueSource};
/// Bounded descriptor page returned by a schema discovery request.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct DescriptorPage {
/// Request being answered.
pub request_id: u32,
/// First requested descriptor index.
pub first: u16,
/// Number of descriptors present in `descriptors`.
pub len: u8,
/// Fixed-capacity response page; `None` slots are unused.
pub descriptors: [Option<&'static FieldDescriptor>; 4],
}
/// One typed value read from an explicitly selected configuration snapshot.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ValueResponse {
/// Request being answered.
pub request_id: u32,
/// Transaction session for transaction values, otherwise zero.
pub session_id: u32,
/// Current transaction revision, otherwise zero.
pub revision: u32,
/// Requested stable field identity.
pub field: FieldId,
/// Declared scalar value.
pub value: ConfigValue,
}
/// Correlated protocol/schema discovery response.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ConfigInfo {
/// Request being answered.
pub request_id: u32,
/// Boot-active generation, or `None` for the factory profile.
pub active_generation: Option<Generation>,
/// Last committed generation, or `None` for the factory profile.
pub committed_generation: Option<Generation>,
/// Exact schema identifier expected by profile import.
pub schema_id: [u8; 16],
/// Version carried inside the one raw CBOR profile blob.
pub profile_format: u16,
/// Number of editable fields in the registry.
pub field_count: u16,
/// Current configuration lifecycle state.
pub state: ConfigState,
/// Deployment capabilities such as bounded CBOR import.
pub capability_flags: u8,
}
/// Exact response produced by the custom configuration service.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ServiceResponse {
/// Correlated protocol/schema discovery response.
Info(ConfigInfo),
/// Completion for a mutating operation or rejected query.
Result(crate::ConfigReply),
/// Bounded self-describing schema page.
Descriptors(DescriptorPage),
/// One typed scalar value.
Value(ValueResponse),
}
impl<I> ConfigProtocol<I> {
/// Returns static descriptor metadata by stable schema index.
pub fn descriptor<C, S>(
&self,
manager: &ConfigManager<C, S>,
index: u16,
) -> Option<&'static FieldDescriptor>
where
C: ConfigModel,
S: ProfileStore<C>,
{
manager.descriptor(index)
}
/// Reads one typed value without opening or changing a transaction.
pub fn value<C, S>(
&self,
manager: &ConfigManager<C, S>,
source: ValueSource,
field: FieldId,
) -> Option<ConfigValue>
where
C: ConfigModel,
S: ProfileStore<C>,
{
match source {
ValueSource::Active => manager.active_value(field),
ValueSource::Committed => manager.committed_value(field),
ValueSource::Transaction => manager.transaction_value(field),
}
}
pub(super) fn descriptors<C, S>(
&self,
manager: &ConfigManager<C, S>,
request_id: u32,
first: u16,
requested: u16,
) -> DescriptorPage
where
C: ConfigModel,
S: ProfileStore<C>,
{
let mut descriptors = [None; 4];
let count = requested.min(descriptors.len() as u16);
let mut len = 0;
for offset in 0..count {
let Some(index) = first.checked_add(offset) else {
break;
};
let Some(descriptor) = manager.descriptor(index) else {
break;
};
descriptors[usize::from(len)] = Some(descriptor);
len += 1;
}
DescriptorPage {
request_id,
first,
len,
descriptors,
}
}
pub(super) fn info<C, S>(&self, manager: &ConfigManager<C, S>, request_id: u32) -> ConfigInfo
where
C: ConfigModel,
S: ProfileStore<C>,
I: SnapshotImporter<C>,
{
ConfigInfo {
request_id,
active_generation: manager.active_generation(),
committed_generation: manager.committed_generation(),
schema_id: C::SCHEMA_ID,
profile_format: I::PROFILE_FORMAT,
field_count: manager.field_count(),
state: self.state,
capability_flags: self.capability_flags_for::<C>(),
}
}
pub(super) fn value_response<C, S>(
&self,
manager: &ConfigManager<C, S>,
owner: u64,
request_id: u32,
session_id: u32,
field: FieldId,
source: ValueSource,
) -> ServiceResponse
where
C: ConfigModel,
S: ProfileStore<C>,
{
let (value, reply_session, revision) = match source {
ValueSource::Active => (manager.active_value(field), 0, 0),
ValueSource::Committed => (manager.committed_value(field), 0, 0),
ValueSource::Transaction => {
let Some(session) = self.session else {
return ServiceResponse::Result(self.rejected_for(
request_id,
session_id,
ProtocolError::InvalidSession,
));
};
if session.id != session_id || session.owner != owner {
return ServiceResponse::Result(self.rejected_for(
request_id,
session_id,
ProtocolError::InvalidSession,
));
}
(
manager.transaction_value(field),
session.id,
session.revision,
)
}
};
let Some(value) = value else {
return ServiceResponse::Result(self.rejected_for(
request_id,
session_id,
ProtocolError::UnknownField(field),
));
};
ServiceResponse::Value(ValueResponse {
request_id,
session_id: reply_session,
revision,
field,
value,
})
}
}