use crate::encoding::Encoding;
use crate::sample::Duration;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Ordinal(pub u32);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct InterfaceNo(pub u32);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct CatalogHash(pub [u8; 32]);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct CatalogRef {
pub name: &'static str,
pub hash: CatalogHash,
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Kind {
Signal = 1,
Event = 2,
Command = 3,
Query = 4,
Fixed = 5,
}
pub trait Interface {
const CATALOG: &'static CatalogRef;
const NUMBER: InterfaceNo;
const PROVISIONAL: bool;
const NAME: &'static str;
const MEMBERS: &'static [Member];
}
pub trait Interaction {
type Iface: Interface;
const MEMBER: &'static Member;
}
pub trait Signal: Interaction {
type Payload;
fn init() -> Self::Payload;
}
pub trait Event: Interaction {
type Payload;
}
pub trait Fixed: Interaction {
type Payload;
}
pub trait Command: Interaction {
type Args;
#[allow(
clippy::result_unit_err,
reason = "the failing method decides the contract error, so the error carries no value"
)]
fn require(args: &Self::Args) -> Result<(), ()>;
}
pub trait Query: Interaction {
type Args;
type Reply;
#[allow(
clippy::result_unit_err,
reason = "the failing method decides the contract error, so the error carries no value"
)]
fn require(args: &Self::Args) -> Result<(), ()>;
#[allow(
clippy::result_unit_err,
reason = "the failing method decides the contract error, so the error carries no value"
)]
fn ensure(args: &Self::Args, reply: &Self::Reply) -> Result<(), ()>;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Member {
pub ordinal: Ordinal,
pub kind: Kind,
pub name: &'static str,
pub timing: Option<Timing>,
pub payloads: &'static [PayloadInfo],
}
impl Member {
pub fn call_deadline(&self) -> Option<Duration> {
self.timing.and_then(|timing| timing.max)
}
pub fn reservation<E: Encoding>(&self) -> Result<u64, Unsized> {
let mut total: u64 = 0;
for payload in self.payloads {
let size = E::max_size(&payload.max_size).ok_or(Unsized {
ordinal: self.ordinal,
member: self.name,
type_name: payload.type_name,
})?;
total = total.saturating_add(u64::from(size));
}
Ok(total)
}
}
pub fn table_budget<E: Encoding>(members: &[Member]) -> Result<u64, Unsized> {
let mut total: u64 = 0;
for member in members {
total = total.saturating_add(member.reservation::<E>()?);
}
Ok(total)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct Unsized {
pub ordinal: Ordinal,
pub member: &'static str,
pub type_name: &'static str,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TimingMode {
StrictPeriodic,
Range,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Timing {
pub mode: TimingMode,
pub min: Option<Duration>,
pub max: Option<Duration>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PayloadInfo {
pub type_name: &'static str,
pub max_size: EncodedSizes,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct EncodedSizes {
pub proto3: Option<u32>,
pub flatbuffers: Option<u32>,
pub repr_c: Option<u32>,
}
#[cfg(test)]
mod tests {
use super::{CatalogHash, CatalogRef, Kind};
#[test]
fn catalog_refs_are_equal_only_when_name_and_hash_are_equal() {
let base = CatalogRef {
name: "vehicle",
hash: CatalogHash([1; 32]),
};
let same = CatalogRef {
name: "vehicle",
hash: CatalogHash([1; 32]),
};
let other_hash = CatalogRef {
name: "vehicle",
hash: CatalogHash([2; 32]),
};
let other_name = CatalogRef {
name: "cabin",
hash: CatalogHash([1; 32]),
};
assert_eq!(base, same);
assert_ne!(base, other_hash);
assert_ne!(base, other_name);
}
#[test]
fn kind_values_are_the_language_order_from_one() {
assert_eq!(Kind::Signal as u8, 1);
assert_eq!(Kind::Event as u8, 2);
assert_eq!(Kind::Command as u8, 3);
assert_eq!(Kind::Query as u8, 4);
assert_eq!(Kind::Fixed as u8, 5);
}
}