mtp/type-map/build.rs
Alois d9ad5e5b3d
Some checks failed
CI / rustfmt (push) Successful in 17s
CI / wasm build (push) Successful in 1m15s
CI / clippy (push) Successful in 1m30s
CI / test (push) Successful in 1m48s
CI / example (push) Successful in 1m32s
CI / duplicate code (push) Failing after 29s
CI / web client (push) Failing after 30s
CI / cargo-machete (push) Successful in 1m7s
CI / cargo-deny (push) Failing after 2m23s
(feat): add max message size to wasm
(feat): add pq key generation to wasm
(qol): update gitignores
2026-06-28 13:08:37 +02:00

1058 lines
32 KiB
Rust
Executable file

use serde::Deserialize;
use std::collections::{BTreeMap, BTreeSet};
use std::fmt::Write;
#[derive(Deserialize)]
struct Config {
#[allow(dead_code)]
protocol_version: String,
type_maps: BTreeMap<String, TypeMapConfig>,
}
#[derive(Deserialize)]
#[serde(rename_all = "PascalCase")]
struct TypeMapConfig {
#[serde(default)]
communication_types: BTreeMap<String, u16>,
#[serde(default)]
data_types: BTreeMap<String, u16>,
}
struct ReservedEntry {
name: &'static str,
id: u16,
}
const RESERVED_COMM_TYPES: &[ReservedEntry] = &[
ReservedEntry {
name: "Identification",
id: 0,
},
ReservedEntry {
name: "IdentificationResponse",
id: 1,
},
ReservedEntry {
name: "Register",
id: 2,
},
ReservedEntry {
name: "RegisterResponse",
id: 3,
},
ReservedEntry {
name: "Challenge",
id: 4,
},
ReservedEntry {
name: "ChallengeResponse",
id: 5,
},
ReservedEntry {
name: "Ping",
id: 6,
},
ReservedEntry {
name: "Pong",
id: 7,
},
ReservedEntry {
name: "Disconnect",
id: 8,
},
ReservedEntry {
name: "Redirect",
id: 9,
},
ReservedEntry {
name: "Shutdown",
id: 10,
},
ReservedEntry {
name: "Error",
id: 11,
},
ReservedEntry {
name: "ErrorParsing",
id: 12,
},
ReservedEntry {
name: "ErrorBadVersion",
id: 13,
},
ReservedEntry {
name: "BadRequest",
id: 14,
},
ReservedEntry {
name: "Unauthorized",
id: 15,
},
ReservedEntry {
name: "Forbidden",
id: 16,
},
ReservedEntry {
name: "NotFound",
id: 17,
},
ReservedEntry {
name: "TooManyRequests",
id: 18,
},
ReservedEntry {
name: "InternalServerError",
id: 19,
},
ReservedEntry {
name: "BadGateway",
id: 20,
},
ReservedEntry {
name: "ServiceUnavailable",
id: 21,
},
ReservedEntry {
name: "GatewayTimeout",
id: 22,
},
];
const RESERVED_DATA_TYPES: &[ReservedEntry] = &[
ReservedEntry {
name: "Version",
id: 0,
},
ReservedEntry { name: "Id", id: 1 },
ReservedEntry {
name: "ClientNonce",
id: 2,
},
ReservedEntry {
name: "ServerNonce",
id: 3,
},
ReservedEntry {
name: "PublicKeys",
id: 4,
},
ReservedEntry {
name: "Signature",
id: 5,
},
ReservedEntry {
name: "PqSignature",
id: 6,
},
ReservedEntry {
name: "Description",
id: 7,
},
ReservedEntry {
name: "Connected",
id: 8,
},
ReservedEntry {
name: "Timestamp",
id: 9,
},
ReservedEntry {
name: "Error",
id: 10,
},
ReservedEntry {
name: "ErrorParsing",
id: 11,
},
ReservedEntry {
name: "ErrorMessage",
id: 12,
},
];
fn main() {
let out = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());
let multi_version = std::env::var("CARGO_FEATURE_REGISTRY").is_ok();
println!("cargo:rerun-if-env-changed=MTP_TYPE_MAPS");
let config = match std::env::var("MTP_TYPE_MAPS") {
Ok(config_path) => {
println!("cargo:rerun-if-changed={}", 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(_) => {
eprint!("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, multi_version);
std::fs::write(out.join("types.rs"), code).unwrap();
}
fn sorted_versions(config: &Config) -> Vec<(String, u16, u16)> {
let mut versions: Vec<(String, u16, u16)> = config
.type_maps
.keys()
.filter_map(|k| {
let parts: Vec<&str> = k.split('.').collect();
if parts.len() == 2 {
let major = parts[0].parse::<u16>().ok()?;
let minor = parts[1].parse::<u16>().ok()?;
Some((k.clone(), major, minor))
} else {
None
}
})
.collect();
versions.sort_by_key(|(_, major, minor)| (*major, *minor));
versions
}
fn generate(config: &Config, multi_version: bool) -> String {
let mut out = String::new();
writeln!(
out,
"// Auto-generated by type-map/build.rs, this file should not be edited manually"
)
.unwrap();
writeln!(out).unwrap();
let (all_comm_names, all_data_names, sorted) = if multi_version {
let mut all_comm_names: BTreeSet<&str> = BTreeSet::new();
let mut all_data_names: BTreeSet<&str> = BTreeSet::new();
for tm in config.type_maps.values() {
for name in tm.communication_types.keys() {
all_comm_names.insert(name.as_str());
}
for name in tm.data_types.keys() {
all_data_names.insert(name.as_str());
}
}
let sorted = sorted_versions(config);
(all_comm_names, all_data_names, sorted)
} else {
let mut all_comm_names: BTreeSet<&str> = BTreeSet::new();
let mut all_data_names: BTreeSet<&str> = BTreeSet::new();
if let Some(tm) = config.type_maps.get(&config.protocol_version) {
for name in tm.communication_types.keys() {
all_comm_names.insert(name.as_str());
}
for name in tm.data_types.keys() {
all_data_names.insert(name.as_str());
}
}
(all_comm_names, all_data_names, vec![])
};
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_latest_method(&mut out, config);
if multi_version {
generate_versioned_constructors(&mut out, &sorted);
generate_builtin_type_maps(&mut out, &sorted);
generate_lookup_methods(&mut out, config, &sorted);
generate_all_types_methods(&mut out, config, &sorted);
} else {
generate_single_version_lookup(&mut out, config);
generate_all_types_methods_single(&mut out, config);
}
generate_enum_conversion_methods(&mut out);
generate_reverse_lookups(&mut out, config, &sorted, multi_version);
generate_id_display_impls(&mut out);
out
}
fn generate_protocol_version(out: &mut String, config: &Config) {
let (major, minor) = parse_protocol_version(&config.protocol_version);
writeln!(
out,
"pub const PROTOCOL_VERSION: Version = Version({}, {});",
major, minor
)
.unwrap();
writeln!(out).unwrap();
}
fn generate_latest_method(out: &mut String, config: &Config) {
let (major, minor) = parse_protocol_version(&config.protocol_version);
writeln!(out, "impl TypeMap {{").unwrap();
writeln!(out, " pub fn latest() -> Self {{").unwrap();
writeln!(
out,
" Self {{ version: Version({}, {}) }}",
major, minor
)
.unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
}
fn parse_protocol_version(version: &str) -> (u16, u16) {
let Some((major, minor)) = version.split_once('.') else {
return (0, 0);
};
(
major.parse::<u16>().unwrap_or(0),
minor.parse::<u16>().unwrap_or(0),
)
}
fn generate_comm_type_enum(out: &mut String, user_names: &BTreeSet<&str>) {
writeln!(
out,
"#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]"
)
.unwrap();
writeln!(out, "pub enum CommunicationType {{").unwrap();
for entry in RESERVED_COMM_TYPES {
writeln!(out, " {},", entry.name).unwrap();
}
for name in user_names {
writeln!(out, " {},", name).unwrap();
}
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
writeln!(out, "impl CommunicationType {{").unwrap();
writeln!(out, " pub fn name(self) -> &'static str {{").unwrap();
writeln!(out, " match self {{").unwrap();
for entry in RESERVED_COMM_TYPES {
writeln!(
out,
" CommunicationType::{} => \"{}\",",
entry.name, entry.name
)
.unwrap();
}
for name in user_names {
writeln!(
out,
" CommunicationType::{} => \"{}\",",
name, name
)
.unwrap();
}
writeln!(out, " }}").unwrap();
writeln!(out, " }}").unwrap();
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,
" fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{"
)
.unwrap();
writeln!(out, " f.write_str(self.name())").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
}
fn generate_data_type_enum(out: &mut String, user_names: &BTreeSet<&str>) {
writeln!(out, "/// Data type identifier; compile-time checked.").unwrap();
writeln!(
out,
"#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]"
)
.unwrap();
writeln!(out, "pub enum DataType {{").unwrap();
for entry in RESERVED_DATA_TYPES {
writeln!(out, " {},", entry.name).unwrap();
}
for name in user_names {
writeln!(out, " {},", name).unwrap();
}
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
writeln!(out, "impl DataType {{").unwrap();
writeln!(out, " pub fn name(self) -> &'static str {{").unwrap();
writeln!(out, " match self {{").unwrap();
for entry in RESERVED_DATA_TYPES {
writeln!(
out,
" DataType::{} => \"{}\",",
entry.name, entry.name
)
.unwrap();
}
for name in user_names {
writeln!(out, " DataType::{} => \"{}\",", name, name).unwrap();
}
writeln!(out, " }}").unwrap();
writeln!(out, " }}").unwrap();
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,
" fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{"
)
.unwrap();
writeln!(out, " f.write_str(self.name())").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
}
fn generate_versioned_constructors(out: &mut String, sorted_versions: &[(String, u16, u16)]) {
for (version_key, _major, _minor) in sorted_versions {
let version_ident = version_key.replace('.', "_");
writeln!(out, "impl TypeMap {{").unwrap();
writeln!(out, " pub fn v{}() -> Self {{", version_ident).unwrap();
let parts: Vec<&str> = version_key.split('.').collect();
let major = parts[0];
let minor = parts[1];
writeln!(
out,
" Self {{ version: Version({}, {}) }}",
major, minor
)
.unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
}
}
fn generate_lookup_methods(
out: &mut String,
config: &Config,
sorted_versions: &[(String, u16, u16)],
) {
writeln!(out, "#[allow(unreachable_patterns, unused_variables)]").unwrap();
writeln!(out, "impl TypeMap {{").unwrap();
writeln!(
out,
" pub fn comm_id_enum(&self, ct: CommunicationType) -> Option<u16> {{"
)
.unwrap();
writeln!(out, " match self.version {{").unwrap();
for (version_key, major, minor) in sorted_versions {
let tm_cfg = &config.type_maps[version_key];
writeln!(
out,
" Version({}, {}) => match ct {{",
major, minor
)
.unwrap();
for entry in RESERVED_COMM_TYPES {
writeln!(
out,
" CommunicationType::{} => Some({}),",
entry.name, entry.id
)
.unwrap();
}
for (name, id) in &tm_cfg.communication_types {
writeln!(
out,
" CommunicationType::{} => Some({}),",
name, id
)
.unwrap();
}
writeln!(out, " _ => None,").unwrap();
writeln!(out, " }},").unwrap();
}
writeln!(out, " _ => None,").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out).unwrap();
writeln!(out, " #[allow(unused_variables)]").unwrap();
writeln!(
out,
" pub fn data_id_enum(&self, dt: DataType) -> Option<u16> {{"
)
.unwrap();
writeln!(out, " match self.version {{").unwrap();
for (version_key, major, minor) in sorted_versions {
let tm_cfg = &config.type_maps[version_key];
writeln!(
out,
" Version({}, {}) => match dt {{",
major, minor
)
.unwrap();
for entry in RESERVED_DATA_TYPES {
writeln!(
out,
" DataType::{} => Some({}),",
entry.name, entry.id
)
.unwrap();
}
for (name, id) in &tm_cfg.data_types {
writeln!(out, " DataType::{} => Some({}),", name, id).unwrap();
}
writeln!(out, " _ => None,").unwrap();
writeln!(out, " }},").unwrap();
}
writeln!(out, " _ => None,").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out).unwrap();
// Reverse lookups: wire ID → enum variant (version-aware)
writeln!(
out,
" pub fn comm_enum_id(&self, id: u16) -> Option<CommunicationType> {{"
)
.unwrap();
writeln!(out, " match self.version {{").unwrap();
for (version_key, major, minor) in sorted_versions {
let tm_cfg = &config.type_maps[version_key];
writeln!(
out,
" Version({}, {}) => match id {{",
major, minor
)
.unwrap();
for entry in RESERVED_COMM_TYPES {
writeln!(
out,
" {} => Some(CommunicationType::{}),",
entry.id, entry.name
)
.unwrap();
}
for (name, id) in &tm_cfg.communication_types {
writeln!(
out,
" {} => Some(CommunicationType::{}),",
id, name
)
.unwrap();
}
writeln!(out, " _ => None,").unwrap();
writeln!(out, " }},").unwrap();
}
writeln!(out, " _ => None,").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out).unwrap();
writeln!(
out,
" pub fn data_enum_id(&self, id: u16) -> Option<DataType> {{"
)
.unwrap();
writeln!(out, " match self.version {{").unwrap();
for (version_key, major, minor) in sorted_versions {
let tm_cfg = &config.type_maps[version_key];
writeln!(
out,
" Version({}, {}) => match id {{",
major, minor
)
.unwrap();
for entry in RESERVED_DATA_TYPES {
writeln!(
out,
" {} => Some(DataType::{}),",
entry.id, entry.name
)
.unwrap();
}
for (name, id) in &tm_cfg.data_types {
writeln!(out, " {} => Some(DataType::{}),", id, name).unwrap();
}
writeln!(out, " _ => None,").unwrap();
writeln!(out, " }},").unwrap();
}
writeln!(out, " _ => None,").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
}
fn generate_single_version_lookup(out: &mut String, config: &Config) {
let tm_cfg = config.type_maps.get(&config.protocol_version);
writeln!(out, "#[allow(unreachable_patterns, unused_variables)]").unwrap();
writeln!(out, "impl TypeMap {{").unwrap();
writeln!(
out,
" pub fn comm_id_enum(&self, ct: CommunicationType) -> Option<u16> {{"
)
.unwrap();
writeln!(out, " match self.version {{").unwrap();
writeln!(out, " PROTOCOL_VERSION => match ct {{").unwrap();
for entry in RESERVED_COMM_TYPES {
writeln!(
out,
" CommunicationType::{} => Some({}),",
entry.name, entry.id
)
.unwrap();
}
if let Some(tm_cfg) = tm_cfg {
for (name, id) in &tm_cfg.communication_types {
writeln!(
out,
" CommunicationType::{} => Some({}),",
name, id
)
.unwrap();
}
}
writeln!(out, " _ => None,").unwrap();
writeln!(out, " }},").unwrap();
writeln!(out, " _ => None,").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out).unwrap();
writeln!(out, " #[allow(unused_variables)]").unwrap();
writeln!(
out,
" pub fn data_id_enum(&self, dt: DataType) -> Option<u16> {{"
)
.unwrap();
writeln!(out, " match self.version {{").unwrap();
writeln!(out, " PROTOCOL_VERSION => match dt {{").unwrap();
for entry in RESERVED_DATA_TYPES {
writeln!(
out,
" DataType::{} => Some({}),",
entry.name, entry.id
)
.unwrap();
}
if let Some(tm_cfg) = tm_cfg {
for (name, id) in &tm_cfg.data_types {
writeln!(out, " DataType::{} => Some({}),", name, id).unwrap();
}
}
writeln!(out, " _ => None,").unwrap();
writeln!(out, " }},").unwrap();
writeln!(out, " _ => None,").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out).unwrap();
// Reverse lookups: wire ID → enum variant (single version)
writeln!(
out,
" pub fn comm_enum_id(&self, id: u16) -> Option<CommunicationType> {{"
)
.unwrap();
writeln!(out, " match self.version {{").unwrap();
writeln!(out, " PROTOCOL_VERSION => match id {{").unwrap();
for entry in RESERVED_COMM_TYPES {
writeln!(
out,
" {} => Some(CommunicationType::{}),",
entry.id, entry.name
)
.unwrap();
}
if let Some(tm_cfg) = tm_cfg {
for (name, id) in &tm_cfg.communication_types {
writeln!(
out,
" {} => Some(CommunicationType::{}),",
id, name
)
.unwrap();
}
}
writeln!(out, " _ => None,").unwrap();
writeln!(out, " }},").unwrap();
writeln!(out, " _ => None,").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out).unwrap();
writeln!(
out,
" pub fn data_enum_id(&self, id: u16) -> Option<DataType> {{"
)
.unwrap();
writeln!(out, " match self.version {{").unwrap();
writeln!(out, " PROTOCOL_VERSION => match id {{").unwrap();
for entry in RESERVED_DATA_TYPES {
writeln!(
out,
" {} => Some(DataType::{}),",
entry.id, entry.name
)
.unwrap();
}
if let Some(tm_cfg) = tm_cfg {
for (name, id) in &tm_cfg.data_types {
writeln!(out, " {} => Some(DataType::{}),", id, name).unwrap();
}
}
writeln!(out, " _ => None,").unwrap();
writeln!(out, " }},").unwrap();
writeln!(out, " _ => None,").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
}
fn generate_builtin_type_maps(out: &mut String, sorted_versions: &[(String, u16, u16)]) {
writeln!(out, "pub fn builtin_type_maps() -> Vec<TypeMap> {{").unwrap();
writeln!(out, " vec![").unwrap();
for (version_key, _major, _minor) in sorted_versions {
let ident = version_key.replace('.', "_");
writeln!(out, " TypeMap::v{}(),", ident).unwrap();
}
writeln!(out, " ]").unwrap();
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
}
fn generate_reverse_lookups(
out: &mut String,
config: &Config,
sorted_versions: &[(String, u16, u16)],
multi_version: bool,
) {
let mut id_to_comm: BTreeMap<u16, String> = BTreeMap::new();
for entry in RESERVED_COMM_TYPES {
id_to_comm.insert(entry.id, entry.name.to_string());
}
if multi_version {
for (version_key, _major, _minor) in sorted_versions {
if let Some(tm_cfg) = config.type_maps.get(version_key) {
for (name, id) in &tm_cfg.communication_types {
id_to_comm.insert(*id, name.clone());
}
}
}
} else if let Some(tm_cfg) = config.type_maps.get(&config.protocol_version) {
for (name, id) in &tm_cfg.communication_types {
id_to_comm.insert(*id, name.clone());
}
}
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();
}
writeln!(out, " _ => None,").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
let mut id_to_data: BTreeMap<u16, String> = BTreeMap::new();
for entry in RESERVED_DATA_TYPES {
id_to_data.insert(entry.id, entry.name.to_string());
}
if multi_version {
for (version_key, _major, _minor) in sorted_versions {
if let Some(tm_cfg) = config.type_maps.get(version_key) {
for (name, id) in &tm_cfg.data_types {
id_to_data.insert(*id, name.clone());
}
}
}
} else if let Some(tm_cfg) = config.type_maps.get(&config.protocol_version) {
for (name, id) in &tm_cfg.data_types {
id_to_data.insert(*id, name.clone());
}
}
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();
}
writeln!(out, " _ => None,").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
}
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,
" 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();
}
fn generate_all_types_methods(
out: &mut String,
config: &Config,
sorted_versions: &[(String, u16, u16)],
) {
writeln!(out, "#[allow(unreachable_patterns)]").unwrap();
writeln!(out, "impl TypeMap {{").unwrap();
writeln!(
out,
" pub fn all_comm_types(&self) -> &'static [CommunicationType] {{"
)
.unwrap();
writeln!(out, " match self.version {{").unwrap();
for (version_key, major, minor) in sorted_versions {
let tm_cfg = &config.type_maps[version_key];
write!(out, " Version({}, {}) => &[", major, minor).unwrap();
let mut first = true;
for entry in RESERVED_COMM_TYPES {
if !first {
write!(out, ", ").unwrap();
}
write!(out, "CommunicationType::{}", entry.name).unwrap();
first = false;
}
for name in tm_cfg.communication_types.keys() {
if !first {
write!(out, ", ").unwrap();
}
write!(out, "CommunicationType::{}", name).unwrap();
first = false;
}
writeln!(out, "],").unwrap();
}
writeln!(out, " _ => &[],").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out).unwrap();
writeln!(
out,
" pub fn all_data_types(&self) -> &'static [DataType] {{"
)
.unwrap();
writeln!(out, " match self.version {{").unwrap();
for (version_key, major, minor) in sorted_versions {
let tm_cfg = &config.type_maps[version_key];
write!(out, " Version({}, {}) => &[", major, minor).unwrap();
let mut first = true;
for entry in RESERVED_DATA_TYPES {
if !first {
write!(out, ", ").unwrap();
}
write!(out, "DataType::{}", entry.name).unwrap();
first = false;
}
for name in tm_cfg.data_types.keys() {
if !first {
write!(out, ", ").unwrap();
}
write!(out, "DataType::{}", name).unwrap();
first = false;
}
writeln!(out, "],").unwrap();
}
writeln!(out, " _ => &[],").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
}
fn generate_all_types_methods_single(out: &mut String, config: &Config) {
let tm_cfg = config.type_maps.get(&config.protocol_version);
writeln!(out, "#[allow(unreachable_patterns)]").unwrap();
writeln!(out, "impl TypeMap {{").unwrap();
writeln!(
out,
" pub fn all_comm_types(&self) -> &'static [CommunicationType] {{"
)
.unwrap();
writeln!(out, " match self.version {{").unwrap();
write!(out, " PROTOCOL_VERSION => &[").unwrap();
let mut first = true;
for entry in RESERVED_COMM_TYPES {
if !first {
write!(out, ", ").unwrap();
}
write!(out, "CommunicationType::{}", entry.name).unwrap();
first = false;
}
if let Some(tm_cfg) = tm_cfg {
for name in tm_cfg.communication_types.keys() {
if !first {
write!(out, ", ").unwrap();
}
write!(out, "CommunicationType::{}", name).unwrap();
first = false;
}
}
writeln!(out, "],").unwrap();
writeln!(out, " _ => &[],").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out).unwrap();
writeln!(
out,
" pub fn all_data_types(&self) -> &'static [DataType] {{"
)
.unwrap();
writeln!(out, " match self.version {{").unwrap();
write!(out, " PROTOCOL_VERSION => &[").unwrap();
let mut first = true;
for entry in RESERVED_DATA_TYPES {
if !first {
write!(out, ", ").unwrap();
}
write!(out, "DataType::{}", entry.name).unwrap();
first = false;
}
if let Some(tm_cfg) = tm_cfg {
for name in tm_cfg.data_types.keys() {
if !first {
write!(out, ", ").unwrap();
}
write!(out, "DataType::{}", name).unwrap();
first = false;
}
}
writeln!(out, "],").unwrap();
writeln!(out, " _ => &[],").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
}
fn generate_id_display_impls(out: &mut String) {
writeln!(out, "impl std::fmt::Display for CommunicationTypeId {{").unwrap();
writeln!(
out,
" fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{"
)
.unwrap();
writeln!(out, " match communication_type_name(self.0) {{").unwrap();
writeln!(out, " Some(name) => f.write_str(name),").unwrap();
writeln!(
out,
" None => write!(f, \"CommTypeId({{}})\", self.0),"
)
.unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
writeln!(out, "impl std::fmt::Display for DataTypeId {{").unwrap();
writeln!(
out,
" fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{"
)
.unwrap();
writeln!(out, " match data_type_name(self.0) {{").unwrap();
writeln!(out, " Some(name) => f.write_str(name),").unwrap();
writeln!(
out,
" None => write!(f, \"DataTypeId({{}})\", self.0),"
)
.unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
}