use std::fs; use std::path::PathBuf; fn values(input: &str) -> std::collections::BTreeMap { input .split(';') .filter_map(|item| item.split_once('=')) .map(|(key, value)| (key.to_owned(), value.to_owned())) .collect() } fn evaluate(kind: &str, input: &str) -> &'static str { let values = values(input); match kind { "version" if matches!(input, "1" | "0" | "-1") => "valid", "version" => "invalid:unsupported_version", "page" => match values.get("limit").and_then(|value| value.parse::().ok()) { Some(limit) if (1..=100).contains(&limit) => "valid", _ => "invalid:invalid_limit", }, "manifest" if ["provider_url", "vm_address", "password", "private_key"] .iter() .any(|key| values.contains_key(*key)) => "invalid:forbidden_field", "manifest" if values.get("version").map(String::as_str) == Some("1") && values.contains_key("gateway_id") && values.get("grant").map_or(false, |value| value.len() >= 43) && values.get("purpose").map(String::as_str) == Some("launch") => "valid", "manifest" => "invalid:invalid_manifest", "clipboard" if values.get("encoding").map(String::as_str) == Some("utf-8") && !values.contains_key("file") => "valid", "clipboard" => "invalid:unsupported_clipboard", "event" if values.get("version").map(String::as_str) != Some("1") => { "invalid:unsupported_version" } "event" if values.get("after").and_then(|value| value.parse::().ok()).is_some() && values.get("earliest").and_then(|value| value.parse::().ok()).is_some() && values["after"].parse::().unwrap() > 0 && values["earliest"].parse::().unwrap() > 0 && values["after"].parse::().unwrap() < values["earliest"].parse::().unwrap() - 1 => { "invalid:gap" } "event" if values.get("payload_bytes").and_then(|value| value.parse::().ok()).map_or(true, |size| size > 16384) => { "invalid:payload_limit" } "event" if values.get("sequence").and_then(|value| value.parse::().ok()).map_or(true, |sequence| sequence < 1) || !values.contains_key("correlation_id") => "invalid:required", "event" => "valid", "tunnel" if matches!(values.get("offered").map(String::as_str), Some("1") | Some("0") | Some("-1")) && values.get("feature").map(String::as_str) == Some("control.v1") => "valid", "tunnel" if values.get("feature").map(String::as_str) != Some("control.v1") => { "invalid:unsupported_feature" } "tunnel" => "invalid:unsupported_version", "datagram" => classify_datagram(values.get("hex").map(String::as_str).unwrap_or_default()), _ => "invalid:unknown_kind", } } fn decode_hex(input: &str) -> Option> { if input.len() % 2 != 0 { return None; } (0..input.len()) .step_by(2) .map(|index| u8::from_str_radix(&input[index..index + 2], 16).ok()) .collect() } fn classify_datagram(encoded: &str) -> &'static str { let raw = match decode_hex(encoded) { Some(raw) => raw, None => return "invalid:hex", }; if raw.len() < 21 { return "invalid:truncated"; } if raw[0..2] != *b"VD" { return "invalid:magic"; } if raw[2] != 1 { return "invalid:unsupported_version"; } let limit = match raw[3] { 1 => 1024, 2 => 2048, 3 => 65515, _ => return "invalid:unknown_channel", }; if raw[4] != 0 { return "invalid:flags"; } if raw[18] == 0 || raw[17] >= raw[18] { return "invalid:fragment"; } let payload_length = ((raw[19] as usize) << 8) | raw[20] as usize; if payload_length > limit { return "invalid:payload_limit"; } if raw.len() != 21 + payload_length { return "invalid:length_mismatch"; } if raw.len() > 65536 { return "invalid:frame_limit"; } "valid" } fn normalized_digest(results: &[String]) -> String { let mut value: u64 = 14695981039346656037; for result in results { for byte in format!("{result}\n").bytes() { value ^= u64::from(byte); value = value.wrapping_mul(1099511628211); } } format!("{value:016x}") } fn fixture_hash() -> String { let text = fs::read_to_string("fixtures/manifest.json").expect("fixture manifest"); text.split("\"corpus_sha256\": \"") .nth(1) .and_then(|value| value.split('"').next()) .expect("fixture hash") .to_owned() } fn main() { let mut paths: Vec = fs::read_dir("fixtures/conformance") .expect("fixture corpus") .map(|entry| entry.expect("fixture entry").path()) .filter(|path| path.extension().and_then(|value| value.to_str()) == Some("tsv")) .collect(); paths.sort(); let mut results = Vec::new(); for path in paths { let text = fs::read_to_string(path).expect("fixture file"); let mut lines = text.lines(); assert_eq!(lines.next(), Some("id\tversion\tkind\tinput\texpected")); for line in lines { let fields: Vec<&str> = line.split('\t').collect(); assert_eq!(fields.len(), 5); let actual = evaluate(fields[2], fields[3]); assert_eq!(actual, fields[4], "{}", fields[0]); results.push(format!("{}\t{}", fields[0], actual)); } } println!( "Rust conformance passed normalized={} fixtures={}", normalized_digest(&results), fixture_hash() ); }