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

@ -4,3 +4,9 @@ version = "0.1.0"
edition = "2024"
[dependencies]
mtp-common = { path = "../common" }
mtp-type-map = { path = "../type-map" }
mtp-codec = { path = "../codec", features = ["registry"] }
mtp-transport = { path = "../transport", features = ["host"] }
registry = { path = "../registry" }
tokio = { version = "1", features = ["full"] }

View file

@ -1,5 +1,108 @@
pub fn add(left: u64, right: u64) -> u64 {
left + right
use mtp_codec::{
CommunicationValue, DataTypeId, DataValue, Version,
registry::{Registry, VersionedCodec},
};
use mtp_common::CommunicationError;
use mtp_transport::{Policy, Receiver, Sender};
// Host configuration.
pub struct HostConfig {
pub ip: String,
pub port: u16,
pub tls_fullchain: Vec<u8>,
pub tls_key: Vec<u8>,
}
// A connection that has completed version negotiation.
pub struct MTPConnection {
pub version: Version,
pub codec: VersionedCodec,
pub sender: Sender,
pub receiver: Receiver,
}
// High-level MTP host with built-in version negotiation.
pub struct MTPHost {
transport: mtp_transport::Host,
registry: Registry,
}
impl MTPHost {
pub async fn new(config: HostConfig) -> Result<Self, CommunicationError> {
let registry = Registry::builtin();
let transport = mtp_transport::host(
config.port,
config.tls_fullchain,
config.tls_key,
Policy::default(),
)
.await?;
Ok(Self {
transport,
registry,
})
}
/*
* Accept an incoming connection, negotiate the protocol version,
* and return a ready-to-use `MTPConnection`.
*
* Returns `None` if the connection is closed or the version is
* incompatible.
*/
pub async fn accept(&mut self) -> Option<MTPConnection> {
let (sender, receiver) = self.transport.next().await?;
/* Read the first message (always encoded with reserved types). */
let first_msg = receiver.receive().await.ok()?;
/*
* 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").
*/
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());
Some(MTPConnection {
version: negotiated,
codec,
sender,
receiver,
})
}
pub fn local_addr(&self) -> std::net::SocketAddr {
self.transport.local_addr()
}
pub fn registry(&self) -> &Registry {
&self.registry
}
}
/*
* Extract the protocol version from an initial `CommunicationValue`.
*
* The client's first message must contain a `Version` data entry
* (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()),
_ => None,
}
}
#[cfg(test)]
@ -7,8 +110,23 @@ mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
fn version_extraction() {
let msg = CommunicationValue::new(mtp_codec::CommunicationTypeId(15))
.add_data(DataTypeId(3), DataValue::Str("2.0".to_string()));
let version = extract_version(&msg);
assert_eq!(version, Some(Version(2, 0)));
}
#[test]
fn version_extraction_returns_none_for_missing() {
let msg = CommunicationValue::new(mtp_codec::CommunicationTypeId(15));
assert!(extract_version(&msg).is_none());
}
#[test]
fn version_extraction_bad_format() {
let msg = CommunicationValue::new(mtp_codec::CommunicationTypeId(15))
.add_data(DataTypeId(3), DataValue::UnsignedNumber(42));
assert!(extract_version(&msg).is_none());
}
}