127 lines
3.6 KiB
Rust
127 lines
3.6 KiB
Rust
use std::collections::BTreeMap;
|
|
|
|
pub use mtp_type_map::{
|
|
CommunicationType, CommunicationTypeId, DataType, DataTypeId, TypeMap, Version,
|
|
};
|
|
|
|
/*
|
|
* Reserved communication & data-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_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)]
|
|
pub struct Registry {
|
|
versions: BTreeMap<Version, TypeMap>,
|
|
}
|
|
|
|
impl Registry {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
versions: BTreeMap::new(),
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|
|
}
|
|
|
|
impl Default for Registry {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
/* ================================ TESTS ================================ */
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[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)));
|
|
|
|
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);
|
|
}
|
|
|
|
#[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)));
|
|
|
|
assert_eq!(r.latest().unwrap().version, Version(2, 0));
|
|
}
|
|
}
|