Values, Cleaning, Docs, Tests, Example (Current Example is Wrong)
This commit is contained in:
parent
775282caf4
commit
c2a7afe6c1
37 changed files with 1693 additions and 520 deletions
|
|
@ -6,7 +6,9 @@ build = "build.rs"
|
|||
|
||||
[features]
|
||||
default = []
|
||||
multi-version = []
|
||||
# Enables multi-version type-map constructors, builtin_type_maps(), and
|
||||
# the Registry struct for version negotiation. Used by host, not client.
|
||||
registry = []
|
||||
|
||||
[dependencies]
|
||||
|
||||
|
|
|
|||
|
|
@ -24,45 +24,143 @@ struct ReservedEntry {
|
|||
}
|
||||
|
||||
const RESERVED_COMM_TYPES: &[ReservedEntry] = &[
|
||||
ReservedEntry { name: "Error", id: 0 },
|
||||
ReservedEntry { name: "ErrorParsing", id: 1 },
|
||||
ReservedEntry { name: "ErrorBadVersion", id: 2 },
|
||||
ReservedEntry { name: "Disconnect", id: 3 },
|
||||
ReservedEntry { name: "Redirect", id: 4 },
|
||||
ReservedEntry { name: "Shutdown", id: 5 },
|
||||
ReservedEntry { name: "BadRequest", id: 6 },
|
||||
ReservedEntry { name: "Unauthorized", id: 7 },
|
||||
ReservedEntry { name: "Forbidden", id: 8 },
|
||||
ReservedEntry { name: "NotFound", id: 9 },
|
||||
ReservedEntry { name: "TooManyRequests", id: 10 },
|
||||
ReservedEntry { name: "InternalServerError", id: 11 },
|
||||
ReservedEntry { name: "BadGateway", id: 12 },
|
||||
ReservedEntry { name: "ServiceUnavailable", id: 13 },
|
||||
ReservedEntry { name: "GatewayTimeout", id: 14 },
|
||||
ReservedEntry { name: "Identification", id: 15 },
|
||||
ReservedEntry { name: "IdentificationResponse", id: 16 },
|
||||
ReservedEntry { name: "Register", id: 17 },
|
||||
ReservedEntry { name: "RegisterResponse", id: 18 },
|
||||
ReservedEntry { name: "Ping", id: 19 },
|
||||
ReservedEntry { name: "Pong", id: 20 },
|
||||
ReservedEntry {
|
||||
name: "Error",
|
||||
id: 0,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "ErrorParsing",
|
||||
id: 1,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "ErrorBadVersion",
|
||||
id: 2,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "Disconnect",
|
||||
id: 3,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "Redirect",
|
||||
id: 4,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "Shutdown",
|
||||
id: 5,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "BadRequest",
|
||||
id: 6,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "Unauthorized",
|
||||
id: 7,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "Forbidden",
|
||||
id: 8,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "NotFound",
|
||||
id: 9,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "TooManyRequests",
|
||||
id: 10,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "InternalServerError",
|
||||
id: 11,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "BadGateway",
|
||||
id: 12,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "ServiceUnavailable",
|
||||
id: 13,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "GatewayTimeout",
|
||||
id: 14,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "Identification",
|
||||
id: 15,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "IdentificationResponse",
|
||||
id: 16,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "Register",
|
||||
id: 17,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "RegisterResponse",
|
||||
id: 18,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "Ping",
|
||||
id: 19,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "Pong",
|
||||
id: 20,
|
||||
},
|
||||
];
|
||||
|
||||
const RESERVED_DATA_TYPES: &[ReservedEntry] = &[
|
||||
ReservedEntry { name: "Error", id: 0 },
|
||||
ReservedEntry { name: "ErrorParsing", id: 1 },
|
||||
ReservedEntry { name: "ErrorMessage", id: 2 },
|
||||
ReservedEntry { name: "Version", id: 3 },
|
||||
ReservedEntry { name: "Description", id: 4 },
|
||||
ReservedEntry { name: "Timestamp", id: 5 },
|
||||
ReservedEntry {
|
||||
name: "Error",
|
||||
id: 0,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "ErrorParsing",
|
||||
id: 1,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "ErrorMessage",
|
||||
id: 2,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "Version",
|
||||
id: 3,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "Description",
|
||||
id: 4,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "Timestamp",
|
||||
id: 5,
|
||||
},
|
||||
ReservedEntry { name: "Id", id: 6 },
|
||||
ReservedEntry { name: "Nonce", id: 7 },
|
||||
ReservedEntry { name: "PublicKeys", id: 8 },
|
||||
ReservedEntry { name: "Signature", id: 9 },
|
||||
ReservedEntry {
|
||||
name: "ClientNonce",
|
||||
id: 7,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "ServerNonce",
|
||||
id: 8,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "PublicKeys",
|
||||
id: 9,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "Signature",
|
||||
id: 10,
|
||||
},
|
||||
ReservedEntry {
|
||||
name: "Connected",
|
||||
id: 11,
|
||||
},
|
||||
];
|
||||
|
||||
fn main() {
|
||||
let out = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());
|
||||
let multi_version = std::env::var("CARGO_FEATURE_MULTI_VERSION").is_ok();
|
||||
let multi_version = std::env::var("CARGO_FEATURE_REGISTRY").is_ok();
|
||||
|
||||
let config = match std::env::var("MTP_TYPE_MAPS") {
|
||||
Ok(config_path) => {
|
||||
|
|
@ -251,6 +349,33 @@ fn generate_comm_type_enum(out: &mut String, user_names: &BTreeSet<&str>) {
|
|||
writeln!(out, "}}").unwrap();
|
||||
writeln!(out).unwrap();
|
||||
|
||||
writeln!(out, "impl CommunicationType {{").unwrap();
|
||||
writeln!(out, " pub fn from_name(s: &str) -> Option<Self> {{").unwrap();
|
||||
writeln!(out, " match s {{").unwrap();
|
||||
|
||||
for entry in RESERVED_COMM_TYPES {
|
||||
writeln!(
|
||||
out,
|
||||
" \"{}\" => Some(CommunicationType::{}),",
|
||||
entry.name, entry.name
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
for name in user_names {
|
||||
writeln!(
|
||||
out,
|
||||
" \"{}\" => Some(CommunicationType::{}),",
|
||||
name, name
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
writeln!(out, " _ => None,").unwrap();
|
||||
writeln!(out, " }}").unwrap();
|
||||
writeln!(out, " }}").unwrap();
|
||||
writeln!(out, "}}").unwrap();
|
||||
writeln!(out).unwrap();
|
||||
|
||||
writeln!(out, "impl std::fmt::Display for CommunicationType {{").unwrap();
|
||||
writeln!(
|
||||
out,
|
||||
|
|
@ -303,6 +428,33 @@ fn generate_data_type_enum(out: &mut String, user_names: &BTreeSet<&str>) {
|
|||
writeln!(out, "}}").unwrap();
|
||||
writeln!(out).unwrap();
|
||||
|
||||
writeln!(out, "impl DataType {{").unwrap();
|
||||
writeln!(out, " pub fn from_name(s: &str) -> Option<Self> {{").unwrap();
|
||||
writeln!(out, " match s {{").unwrap();
|
||||
|
||||
for entry in RESERVED_DATA_TYPES {
|
||||
writeln!(
|
||||
out,
|
||||
" \"{}\" => Some(DataType::{}),",
|
||||
entry.name, entry.name
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
for name in user_names {
|
||||
writeln!(
|
||||
out,
|
||||
" \"{}\" => Some(DataType::{}),",
|
||||
name, name
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
writeln!(out, " _ => None,").unwrap();
|
||||
writeln!(out, " }}").unwrap();
|
||||
writeln!(out, " }}").unwrap();
|
||||
writeln!(out, "}}").unwrap();
|
||||
writeln!(out).unwrap();
|
||||
|
||||
writeln!(out, "impl std::fmt::Display for DataType {{").unwrap();
|
||||
writeln!(
|
||||
out,
|
||||
|
|
@ -429,11 +581,7 @@ fn generate_single_version_lookup(out: &mut String, config: &Config) {
|
|||
)
|
||||
.unwrap();
|
||||
writeln!(out, " match self.version {{").unwrap();
|
||||
writeln!(
|
||||
out,
|
||||
" PROTOCOL_VERSION => match ct {{"
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(out, " PROTOCOL_VERSION => match ct {{").unwrap();
|
||||
for entry in RESERVED_COMM_TYPES {
|
||||
writeln!(
|
||||
out,
|
||||
|
|
@ -466,11 +614,7 @@ fn generate_single_version_lookup(out: &mut String, config: &Config) {
|
|||
)
|
||||
.unwrap();
|
||||
writeln!(out, " match self.version {{").unwrap();
|
||||
writeln!(
|
||||
out,
|
||||
" PROTOCOL_VERSION => match dt {{"
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(out, " PROTOCOL_VERSION => match dt {{").unwrap();
|
||||
for entry in RESERVED_DATA_TYPES {
|
||||
writeln!(
|
||||
out,
|
||||
|
|
@ -529,7 +673,11 @@ fn generate_reverse_lookups(
|
|||
}
|
||||
}
|
||||
|
||||
writeln!(out, "pub fn communication_type_name(id: u16) -> Option<&'static str> {{").unwrap();
|
||||
writeln!(
|
||||
out,
|
||||
"pub fn communication_type_name(id: u16) -> Option<&'static str> {{"
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(out, " match id {{").unwrap();
|
||||
for (id, name) in &id_to_comm {
|
||||
writeln!(out, " {} => Some(\"{}\"),", id, name).unwrap();
|
||||
|
|
@ -557,7 +705,11 @@ fn generate_reverse_lookups(
|
|||
}
|
||||
}
|
||||
|
||||
writeln!(out, "pub fn data_type_name(id: u16) -> Option<&'static str> {{").unwrap();
|
||||
writeln!(
|
||||
out,
|
||||
"pub fn data_type_name(id: u16) -> Option<&'static str> {{"
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(out, " match id {{").unwrap();
|
||||
for (id, name) in &id_to_data {
|
||||
writeln!(out, " {} => Some(\"{}\"),", id, name).unwrap();
|
||||
|
|
@ -570,7 +722,11 @@ fn generate_reverse_lookups(
|
|||
|
||||
fn generate_enum_conversion_methods(out: &mut String) {
|
||||
writeln!(out, "impl CommunicationType {{").unwrap();
|
||||
writeln!(out, " pub fn to_id(self, tm: &TypeMap) -> CommunicationTypeId {{").unwrap();
|
||||
writeln!(
|
||||
out,
|
||||
" pub fn to_id(self, tm: &TypeMap) -> CommunicationTypeId {{"
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(
|
||||
out,
|
||||
" CommunicationTypeId(tm.comm_id_enum(self).expect(\"CommunicationType not found in this TypeMap version\"))"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,40 @@
|
|||
pub const INTERNAL_COMM_RESERVED: std::ops::Range<u16> = 0..32;
|
||||
pub const INTERNAL_DATA_RESERVED: std::ops::Range<u16> = 0..32;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct CommunicationTypeId(pub u16);
|
||||
|
||||
impl CommunicationTypeId {
|
||||
pub fn try_new(id: u16) -> Result<Self, ()> {
|
||||
if INTERNAL_COMM_RESERVED.contains(&id) {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(Self(id))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_reserved(&self) -> bool {
|
||||
INTERNAL_COMM_RESERVED.contains(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct DataTypeId(pub u16);
|
||||
|
||||
impl DataTypeId {
|
||||
pub fn try_new(id: u16) -> Result<Self, ()> {
|
||||
if INTERNAL_DATA_RESERVED.contains(&id) {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(Self(id))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_reserved(&self) -> bool {
|
||||
INTERNAL_DATA_RESERVED.contains(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Version(pub u16, pub u16);
|
||||
|
||||
|
|
@ -43,3 +74,109 @@ impl TypeMap {
|
|||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/types.rs"));
|
||||
|
||||
/* ============================= REGISTRY ============================= */
|
||||
#[cfg(feature = "registry")]
|
||||
pub use registry::*;
|
||||
|
||||
#[cfg(feature = "registry")]
|
||||
mod registry {
|
||||
use std::collections::BTreeMap;
|
||||
use crate::{Version, TypeMap, builtin_type_maps};
|
||||
|
||||
/*
|
||||
* 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(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register(&mut self, typemap: TypeMap) {
|
||||
self.versions.insert(typemap.version.clone(), typemap);
|
||||
}
|
||||
|
||||
pub fn get(&self, version: &Version) -> Option<&TypeMap> {
|
||||
self.versions.get(version)
|
||||
}
|
||||
|
||||
pub fn supports(&self, version: &Version) -> bool {
|
||||
self.versions.contains_key(version)
|
||||
}
|
||||
|
||||
pub fn negotiate(&self, client_versions: &[Version]) -> Option<Version> {
|
||||
client_versions
|
||||
.iter()
|
||||
.filter(|v| self.versions.contains_key(v))
|
||||
.max()
|
||||
.cloned()
|
||||
}
|
||||
|
||||
pub fn latest(&self) -> Option<&TypeMap> {
|
||||
self.versions.last_key_value().map(|(_, v)| v)
|
||||
}
|
||||
|
||||
pub fn builtin() -> Self {
|
||||
let mut r = Self::new();
|
||||
for tm in builtin_type_maps() {
|
||||
r.register(tm);
|
||||
}
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Registry {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn builtin_contains_versions() {
|
||||
let r = Registry::builtin();
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue