Types Creation

This commit is contained in:
Alex Emmet 2026-06-21 22:57:50 +02:00
commit 8337fa3d8f
22 changed files with 1141 additions and 202 deletions

View file

@ -5,3 +5,4 @@ edition = "2024"
[dependencies]
mtp-common = { path = "../common" }
mtp-type-map = { path = "../type-map" }

View file

@ -1,94 +1,128 @@
use mtp_common::RegistryError;
use std::collections::HashMap;
use std::collections::BTreeMap;
#[repr(transparent)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct CommTypeId(pub u16);
#[repr(transparent)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct DataTypeId(pub u16);
pub use mtp_type_map::{
CommunicationType, CommunicationTypeId, DataType, DataTypeId, TypeMap, Version,
};
/*
* # Reserved Internal Namespace
* These are NEVER assigned by a RegistryConfig. They are fixed
* across all versions for version negotiation & security.
* Reserved communication-type namespace (0..32).
* These are fixed across all protocol versions for version negotiation
* and security. They are never assigned by user configuration.
*/
pub const INTERNAL_COMM_RESERVED: std::ops::Range<u16> = 0..32;
pub const INTERNAL_COMM_RESERVED: std::ops::Range<u16> = 0..16;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum InternalCommType {
VersionNegotiate = 0,
SecurityHandshake = 1,
RegistrySync = 2,
// 3-15 reserved for future internal use
}
impl InternalCommType {
pub fn as_id(self) -> CommTypeId {
CommTypeId(self as u16)
}
}
/* Reserved data-type namespace (0..32). */
pub const INTERNAL_DATA_RESERVED: std::ops::Range<u16> = 0..32;
/*
* Multi-version type-map registry.
*
* Stores one `TypeMap` per protocol version and provides version
* negotiation for the host. Hosts build this from the compiled-in
* type maps (via `Registry::builtin()` or programmatically).
*/
#[derive(Clone, Debug)]
// (major , minor , patch );
pub struct Version(pub u16, pub u16, pub u16);
pub struct Registry {
pub version: Version,
comm_name_to_id: HashMap<String, u16>,
comm_id_to_name: HashMap<u16, String>,
data_name_to_id: HashMap<String, u16>,
data_id_to_name: HashMap<u16, String>,
}
pub struct RegistryConfig {
pub version: Version,
pub communication_types: HashMap<String, u16>,
pub data_types: HashMap<String, u16>,
versions: BTreeMap<Version, TypeMap>,
}
impl Registry {
pub fn from_config(cfg: RegistryConfig) -> Result<Self, RegistryError> {
for (name, &id) in &cfg.communication_types {
if INTERNAL_COMM_RESERVED.contains(&id) {
return Err(RegistryError::ReservedCommId(id, name.clone()));
}
pub fn new() -> Self {
Self {
versions: BTreeMap::new(),
}
}
let mut comm_id_to_name = HashMap::with_capacity(cfg.communication_types.len());
for (name, id) in &cfg.communication_types {
comm_id_to_name.insert(*id, name.clone());
// Register a type map, keyed by its version.
pub fn register(&mut self, typemap: TypeMap) {
self.versions.insert(typemap.version.clone(), typemap);
}
// Look up the type map for an exact version.
pub fn get(&self, version: &Version) -> Option<&TypeMap> {
self.versions.get(version)
}
// Check if a specific version is supported.
pub fn supports(&self, version: &Version) -> bool {
self.versions.contains_key(version)
}
/*
* Negotiate the highest mutually supported version from a list.
*
* For single-version clients (the typical case) this is equivalent
* to checking `supports(&client_versions[0])`.
*/
pub fn negotiate(&self, client_versions: &[Version]) -> Option<Version> {
client_versions
.iter()
.filter(|v| self.versions.contains_key(v))
.max()
.cloned()
}
// Return the latest (highest) registered version's type map.
pub fn latest(&self) -> Option<&TypeMap> {
self.versions.last_key_value().map(|(_, v)| v)
}
/*
* Build the registry from the compiled-in configuration.
*
* Uses the `TypeMap::vX_Y()` constructors generated by
* `type-map/build.rs` from the YAML config.
*/
pub fn builtin() -> Self {
let mut r = Self::new();
for tm in mtp_type_map::builtin_type_maps() {
r.register(tm);
}
r
}
}
let mut data_id_to_name = HashMap::with_capacity(cfg.data_types.len());
for (name, id) in &cfg.data_types {
data_id_to_name.insert(*id, name.clone());
}
Ok(Self {
version: cfg.version,
comm_name_to_id: cfg.communication_types,
comm_id_to_name,
data_name_to_id: cfg.data_types,
data_id_to_name,
})
impl Default for Registry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
pub fn resolve_comm(&self, name: &str) -> Option<CommTypeId> {
self.comm_name_to_id.get(name).copied().map(CommTypeId)
#[test]
fn builtin_contains_versions() {
let r = Registry::builtin();
// At least the reserved-only fallback should exist
assert!(r.supports(&Version(0, 0)));
}
#[test]
fn negotiate_finds_highest() {
let mut r = Registry::new();
r.register(TypeMap::new(Version(0, 0)));
r.register(TypeMap::new(Version(1, 0)));
r.register(TypeMap::new(Version(2, 0)));
pub fn parse_comm(&self, id: CommTypeId) -> Option<&str> {
self.comm_id_to_name.get(&id.0).map(|s| s.as_str())
let client = &[Version(0, 0)];
assert_eq!(r.negotiate(client), Some(Version(0, 0)));
let client = &[Version(1, 0), Version(0, 0)];
assert_eq!(r.negotiate(client), Some(Version(1, 0)));
let client = &[Version(5, 0)];
assert_eq!(r.negotiate(client), None);
}
pub fn resolve_data(&self, name: &str) -> Option<DataTypeId> {
self.data_name_to_id.get(name).copied().map(DataTypeId)
}
#[test]
fn latest_returns_highest() {
let mut r = Registry::new();
r.register(TypeMap::new(Version(0, 0)));
r.register(TypeMap::new(Version(2, 0)));
r.register(TypeMap::new(Version(1, 0)));
pub fn parse_data(&self, id: DataTypeId) -> Option<&str> {
self.data_id_to_name.get(&id.0).map(|s| s.as_str())
assert_eq!(r.latest().unwrap().version, Version(2, 0));
}
}