[WIP] Version Parsing
This commit is contained in:
parent
8337fa3d8f
commit
907b9f04db
9 changed files with 133 additions and 40 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,2 +1,4 @@
|
|||
**target/
|
||||
**Cargo.lock
|
||||
*.pem
|
||||
*.key
|
||||
|
|
|
|||
|
|
@ -10,9 +10,8 @@ use mtp_transport::{Policy, Receiver, Sender};
|
|||
* which type maps are available, and the client always advertises
|
||||
* the latest one.
|
||||
*/
|
||||
pub const PROTOCOL_VERSION: Version = Version(2, 0);
|
||||
pub use mtp_type_map::PROTOCOL_VERSION;
|
||||
|
||||
/* Client configuration. */
|
||||
pub struct ClientConfig {
|
||||
pub url: String,
|
||||
pub server_cert: Option<Vec<u8>>,
|
||||
|
|
@ -25,7 +24,6 @@ pub struct MTPConnection {
|
|||
pub receiver: Receiver,
|
||||
}
|
||||
|
||||
// High-level MTP client.
|
||||
pub struct MTPClient;
|
||||
|
||||
impl MTPClient {
|
||||
|
|
@ -42,11 +40,7 @@ impl MTPClient {
|
|||
let (sender, receiver) =
|
||||
mtp_transport::connect(&config.url, config.server_cert, Policy::default()).await?;
|
||||
|
||||
/*
|
||||
* Build the initial identification message with the protocol
|
||||
* version. Uses only reserved type IDs (0-31), which are
|
||||
* identical across all protocol versions.
|
||||
*/
|
||||
// Build the initial identification message with the protocol version.
|
||||
let version_str = format!("{}", PROTOCOL_VERSION);
|
||||
let ident = CommunicationValue::new(mtp_codec::CommunicationTypeId(15))
|
||||
.add_data(DataTypeId(3), DataValue::Str(version_str))
|
||||
|
|
@ -61,13 +55,3 @@ impl MTPClient {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn protocol_version_is_compiled_in() {
|
||||
assert_eq!(PROTOCOL_VERSION, Version(2, 0));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use std::io::{Cursor, Read};
|
|||
|
||||
use crate::data_value::DataValue;
|
||||
use crate::rand_u32;
|
||||
use mtp_type_map::{CommunicationTypeId, DataTypeId};
|
||||
use mtp_type_map::{CommunicationType, CommunicationTypeId, DataType, DataTypeId, TypeMap};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CommunicationValue {
|
||||
|
|
@ -13,6 +13,7 @@ pub struct CommunicationValue {
|
|||
sender: u64,
|
||||
receiver: u64,
|
||||
data: BTreeMap<DataTypeId, DataValue>,
|
||||
type_map: Option<TypeMap>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
@ -24,9 +25,27 @@ impl CommunicationValue {
|
|||
sender: 0,
|
||||
receiver: 0,
|
||||
data: BTreeMap::new(),
|
||||
type_map: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_comm(comm_type: CommunicationType, tm: &TypeMap) -> Self {
|
||||
let id = comm_type.to_id(tm);
|
||||
Self {
|
||||
id: rand_u32(),
|
||||
comm_type: id,
|
||||
sender: 0,
|
||||
receiver: 0,
|
||||
data: BTreeMap::new(),
|
||||
type_map: Some(tm.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_comm_default(comm_type: CommunicationType) -> Self {
|
||||
let tm = TypeMap::latest();
|
||||
Self::from_comm(comm_type, &tm)
|
||||
}
|
||||
|
||||
pub fn with_id(mut self, p0: u32) -> Self {
|
||||
self.id = p0;
|
||||
self
|
||||
|
|
@ -67,6 +86,20 @@ impl CommunicationValue {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn add_typed(mut self, data: DataType, tm: &TypeMap, value: DataValue) -> Self {
|
||||
self.data.insert(data.to_id(tm), value);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_typed_default(mut self, data: DataType, value: DataValue) -> Self {
|
||||
let tm = self
|
||||
.type_map
|
||||
.clone()
|
||||
.unwrap_or_else(TypeMap::latest);
|
||||
self.data.insert(data.to_id(&tm), value);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn get_data(&self, data_type: DataTypeId) -> &DataValue {
|
||||
self.data.get(&data_type).unwrap_or(&DataValue::Null)
|
||||
}
|
||||
|
|
@ -193,15 +226,16 @@ impl CommunicationValue {
|
|||
sender,
|
||||
receiver,
|
||||
data,
|
||||
type_map: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================ TESTS ================================ */
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::data_value::DataValue;
|
||||
use mtp_type_map::{CommunicationTypeId, DataTypeId};
|
||||
|
||||
fn roundtrip(cv: CommunicationValue) -> CommunicationValue {
|
||||
let bytes = cv.to_bytes();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "mtp-crypto"
|
||||
version = "0.2.0"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
|
|
|
|||
|
|
@ -62,13 +62,11 @@ impl MTPHost {
|
|||
* Extract the client's version from the first message.
|
||||
* The client is expected to send DataType::Version (reserved ID 3)
|
||||
* as a DataValue::Str("X.Y").
|
||||
*
|
||||
* Then negotiate the version for single-version clients
|
||||
*/
|
||||
let client_version = extract_version(&first_msg)?;
|
||||
|
||||
/*
|
||||
* Negotiate; for single-version clients, this is a simple
|
||||
* membership check.
|
||||
*/
|
||||
let negotiated = self.registry.negotiate(&[client_version])?;
|
||||
|
||||
let codec = VersionedCodec::new(self.registry.clone());
|
||||
|
|
@ -97,7 +95,6 @@ impl MTPHost {
|
|||
* (reserved ID 3) mapping to `DataValue::Str("major.minor")`.
|
||||
*/
|
||||
fn extract_version(msg: &CommunicationValue) -> Option<Version> {
|
||||
/* Reserved type: DataType::Version has fixed wire ID 3 */
|
||||
let value = msg.get_data(DataTypeId(3));
|
||||
match value {
|
||||
DataValue::Str(s) => Version::parse(s.as_str()),
|
||||
|
|
@ -105,6 +102,7 @@ fn extract_version(msg: &CommunicationValue) -> Option<Version> {
|
|||
}
|
||||
}
|
||||
|
||||
/* ================================ TESTS ================================ */
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -5,13 +5,11 @@ pub use mtp_type_map::{
|
|||
};
|
||||
|
||||
/*
|
||||
* Reserved communication-type namespace (0..32).
|
||||
* 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;
|
||||
|
||||
/* Reserved data-type namespace (0..32). */
|
||||
pub const INTERNAL_DATA_RESERVED: std::ops::Range<u16> = 0..32;
|
||||
|
||||
/*
|
||||
|
|
@ -88,6 +86,7 @@ impl Default for Registry {
|
|||
}
|
||||
}
|
||||
|
||||
/* ================================ TESTS ================================ */
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ pub mod client;
|
|||
pub mod connection;
|
||||
pub mod connection_handle;
|
||||
|
||||
pub use client::connect;
|
||||
pub use connection::{Policy, Receiver, SendMode, Sender};
|
||||
|
||||
pub use client::connect;
|
||||
pub use connection_handle::ConnectionHandle;
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
|
|
|
|||
|
|
@ -151,15 +151,25 @@ const RESERVED_DATA_TYPES: &[ReservedEntry] = &[
|
|||
];
|
||||
|
||||
fn main() {
|
||||
let config_path = std::env::var("MTP_TYPE_MAPS").expect(
|
||||
"MTP_TYPE_MAPS environment variable must be set to the path of a type-maps.yaml file",
|
||||
);
|
||||
|
||||
let content = std::fs::read_to_string(&config_path).expect("Failed to read type-maps.yaml");
|
||||
|
||||
let config: Config = serde_yaml::from_str(&content).expect("Failed to parse type-maps.yaml");
|
||||
|
||||
let out = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());
|
||||
|
||||
let config = match std::env::var("MTP_TYPE_MAPS") {
|
||||
Ok(config_path) => {
|
||||
let content =
|
||||
std::fs::read_to_string(&config_path).expect("Failed to read type-maps.yaml");
|
||||
serde_yaml::from_str(&content).expect("Failed to parse type-maps.yaml")
|
||||
}
|
||||
Err(_) => {
|
||||
eprintln!(
|
||||
"warning: MTP_TYPE_MAPS not set; generating types with reserved entries only"
|
||||
);
|
||||
Config {
|
||||
protocol_version: String::new(),
|
||||
type_maps: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let code = generate(&config);
|
||||
std::fs::write(out.join("types.rs"), code).unwrap();
|
||||
}
|
||||
|
|
@ -187,7 +197,7 @@ fn generate(config: &Config) -> String {
|
|||
let mut out = String::new();
|
||||
writeln!(
|
||||
out,
|
||||
"// Auto-generated by type-map/build.rs, they should not be edited manually"
|
||||
"// Auto-generated by type-map/build.rs, this file should not be edited manually"
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(out).unwrap();
|
||||
|
|
@ -206,15 +216,39 @@ fn generate(config: &Config) -> String {
|
|||
|
||||
let sorted = sorted_versions(config);
|
||||
|
||||
generate_protocol_version(&mut out, config);
|
||||
generate_comm_type_enum(&mut out, &all_comm_names);
|
||||
generate_data_type_enum(&mut out, &all_data_names);
|
||||
generate_type_map_constructors(&mut out, &sorted);
|
||||
generate_lookup_methods(&mut out, config, &sorted);
|
||||
generate_builtin_type_maps(&mut out, &sorted);
|
||||
generate_enum_conversion_methods(&mut out, &sorted);
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
fn generate_protocol_version(out: &mut String, config: &Config) {
|
||||
let (major, minor) = if config.protocol_version.is_empty() {
|
||||
(0, 0)
|
||||
} else {
|
||||
let parts: Vec<&str> = config.protocol_version.split('.').collect();
|
||||
if parts.len() == 2 {
|
||||
let maj = parts[0].parse::<u16>().unwrap_or(0);
|
||||
let min = parts[1].parse::<u16>().unwrap_or(0);
|
||||
(maj, min)
|
||||
} else {
|
||||
(0, 0)
|
||||
}
|
||||
};
|
||||
writeln!(
|
||||
out,
|
||||
"pub const PROTOCOL_VERSION: Version = Version({}, {});",
|
||||
major, minor
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(out).unwrap();
|
||||
}
|
||||
|
||||
fn generate_comm_type_enum(out: &mut String, user_names: &BTreeSet<&str>) {
|
||||
writeln!(
|
||||
out,
|
||||
|
|
@ -343,6 +377,22 @@ fn generate_type_map_constructors(out: &mut String, sorted_versions: &[(String,
|
|||
writeln!(out, "}}").unwrap();
|
||||
writeln!(out).unwrap();
|
||||
}
|
||||
|
||||
let (latest_major, latest_minor) = match sorted_versions.last() {
|
||||
Some((_key, major, minor)) => (*major, *minor),
|
||||
None => (0, 0),
|
||||
};
|
||||
writeln!(out, "impl TypeMap {{").unwrap();
|
||||
writeln!(out, " pub fn latest() -> Self {{").unwrap();
|
||||
writeln!(
|
||||
out,
|
||||
" Self {{ version: Version({}, {}) }}",
|
||||
latest_major, latest_minor
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(out, " }}").unwrap();
|
||||
writeln!(out, "}}").unwrap();
|
||||
writeln!(out).unwrap();
|
||||
}
|
||||
|
||||
fn generate_lookup_methods(
|
||||
|
|
@ -351,7 +401,7 @@ fn generate_lookup_methods(
|
|||
sorted_versions: &[(String, u16, u16)],
|
||||
) {
|
||||
// comm_id_enum
|
||||
writeln!(out, "#[allow(unreachable_patterns)]").unwrap();
|
||||
writeln!(out, "#[allow(unreachable_patterns, unused_variables)]").unwrap();
|
||||
writeln!(out, "impl TypeMap {{").unwrap();
|
||||
writeln!(
|
||||
out,
|
||||
|
|
@ -392,6 +442,7 @@ fn generate_lookup_methods(
|
|||
writeln!(out).unwrap();
|
||||
|
||||
// data_id_enum
|
||||
writeln!(out, " #[allow(unused_variables)]").unwrap();
|
||||
writeln!(
|
||||
out,
|
||||
" pub fn data_id_enum(&self, dt: DataType) -> Option<u16> {{"
|
||||
|
|
@ -438,3 +489,27 @@ fn generate_builtin_type_maps(out: &mut String, sorted_versions: &[(String, u16,
|
|||
writeln!(out, "}}").unwrap();
|
||||
writeln!(out).unwrap();
|
||||
}
|
||||
|
||||
fn generate_enum_conversion_methods(out: &mut String, _sorted_versions: &[(String, u16, u16)]) {
|
||||
writeln!(out, "impl CommunicationType {{").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\"))"
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(out, " }}").unwrap();
|
||||
writeln!(out, "}}").unwrap();
|
||||
writeln!(out).unwrap();
|
||||
|
||||
writeln!(out, "impl DataType {{").unwrap();
|
||||
writeln!(out, " pub fn to_id(self, tm: &TypeMap) -> DataTypeId {{").unwrap();
|
||||
writeln!(
|
||||
out,
|
||||
" DataTypeId(tm.data_id_enum(self).expect(\"DataType not found in this TypeMap version\"))"
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(out, " }}").unwrap();
|
||||
writeln!(out, "}}").unwrap();
|
||||
writeln!(out).unwrap();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ impl std::fmt::Display for Version {
|
|||
* Type-to-wire-ID mappings are generated at compile time and dispatched
|
||||
* via `comm_id_enum` / `data_id_enum` based on `self.version`.
|
||||
*/
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct TypeMap {
|
||||
pub version: Version,
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue