use pack_io::{Deserialize, Serialize, decode, encode, peek_version};
#[derive(Debug, Serialize, Deserialize)]
#[pack_io(version = 1)]
struct HandshakeV1 {
client_id: u64,
capabilities: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize)]
#[pack_io(version = 2)]
struct HandshakeV2 {
client_id: u64,
capabilities: Vec<String>,
#[pack_io(since = 2)]
region: Option<String>,
}
fn header(label: &str) {
println!("\n{}", "─".repeat(60));
println!("{label}");
println!("{}", "─".repeat(60));
}
fn main() {
let v1_client_bytes = encode(&HandshakeV1 {
client_id: 1,
capabilities: vec!["tls".into()],
})
.unwrap();
let v2_client_bytes = encode(&HandshakeV2 {
client_id: 2,
capabilities: vec!["tls".into(), "h2".into()],
region: Some("us-east-1".into()),
})
.unwrap();
println!("encoded sizes");
println!(" v1 client: {} bytes", v1_client_bytes.len());
println!(" v2 client: {} bytes", v2_client_bytes.len());
header("peek_version (server-side dispatch)");
println!(
" v1 client wire reports version {}",
peek_version(&v1_client_bytes).unwrap()
);
println!(
" v2 client wire reports version {}",
peek_version(&v2_client_bytes).unwrap()
);
header("same-revision handshake (control case)");
let h1: HandshakeV1 = decode(&v1_client_bytes).unwrap();
let h2: HandshakeV2 = decode(&v2_client_bytes).unwrap();
println!(" v1 server reads v1 client: {h1:?}");
println!(" v2 server reads v2 client: {h2:?}");
header("v1 client → v2 server (forward compat)");
let upgraded: HandshakeV2 = decode(&v1_client_bytes).unwrap();
println!(" decoded as v2: {upgraded:?}");
println!(
" region field defaulted to {:?} (v1 never wrote it)",
upgraded.region
);
header("v2 client → v1 server (backward compat)");
let downgraded: HandshakeV1 = decode(&v2_client_bytes).unwrap();
println!(" decoded as v1: {downgraded:?}");
println!(" v2-only `region` field is invisible to v1 server (skipped via body length)");
println!("\ndone — every cross-version combination succeeded");
}