#[cfg(feature = "serde")]
fn main() {
use synta::{Decoder, Element, Encoding};
println!("=== Certificate Serde Round-trip Example ===\n");
let pem = std::fs::read_to_string("tests/vectors/test_certificate.pem")
.expect("Cannot read tests/vectors/test_certificate.pem — run from the repo root");
let der = synta_certificate::pem_to_der(pem.as_bytes())
.into_iter()
.next()
.expect("Failed to decode PEM data");
println!("1. Loaded certificate: {} DER bytes\n", der.len());
let mut decoder = Decoder::new(&der, Encoding::Der);
let element: Element = decoder.decode().expect("Failed to decode ASN.1");
println!("2. Original ASN.1 Element tree (from synta):");
print_element_tree(&element, 1);
let node_count_element = count_element_nodes(&element);
println!(" [{} total nodes]\n", node_count_element);
let json = serde_json::to_string_pretty(&element).expect("Failed to serialize to JSON");
println!("3. Serialized to JSON ({} bytes)\n", json.len());
let value: serde_json::Value = serde_json::from_str(&json).expect("Failed to deserialize JSON");
println!("4. Deserialized serde_json::Value tree (interpreted):");
print_json_tree(&value, 1);
let node_count_json = count_json_nodes(&value);
println!(" [{} total nodes]\n", node_count_json);
println!("5. Comparison:");
if node_count_element == node_count_json {
println!(
" Trees are structurally identical ({} nodes each).",
node_count_element
);
} else {
println!(
" Trees differ: Element tree has {} nodes, JSON tree has {} nodes.",
node_count_element, node_count_json
);
}
let re_json = serde_json::to_string_pretty(&value).expect("Failed to re-serialize");
let re_value: serde_json::Value = serde_json::from_str(&re_json).expect("Failed to re-parse");
if value == re_value {
println!(" JSON round-trip verified: re-serialized data is equivalent.");
} else {
println!(" JSON round-trip MISMATCH: re-serialized data differs.");
}
}
#[cfg(feature = "serde")]
fn print_element_tree(el: &synta::Element<'_>, depth: usize) {
use synta::{bytes_to_hex, Element};
let pad = " ".repeat(depth);
match el {
Element::Boolean(v) => println!("{}Boolean: {}", pad, v.value()),
Element::Integer(v) => println!("{}Integer: {}", pad, bytes_to_hex(v.as_bytes())),
Element::BitString(v) => println!(
"{}BitString: {} bytes ({} unused bits)",
pad,
v.as_bytes().len(),
v.unused_bits()
),
Element::OctetString(v) => {
println!("{}OctetString: {}", pad, bytes_to_hex(v.as_bytes()))
}
Element::Null(_) => println!("{}Null", pad),
Element::Real(v) => println!("{}Real: {}", pad, v.value()),
Element::ObjectIdentifier(v) => println!("{}OID: {}", pad, v),
Element::Utf8String(v) => println!("{}Utf8String: {:?}", pad, v.as_str()),
Element::PrintableString(v) => println!("{}PrintableString: {:?}", pad, v.as_str()),
Element::IA5String(v) => println!("{}IA5String: {:?}", pad, v.as_str()),
Element::UtcTime(v) => println!(
"{}UtcTime: {:02}{:02}{:02}{:02}{:02}{:02}Z",
pad,
v.year % 100,
v.month,
v.day,
v.hour,
v.minute,
v.second
),
Element::GeneralizedTime(v) => println!(
"{}GeneralizedTime: {:04}{:02}{:02}{:02}{:02}{:02}Z",
pad, v.year, v.month, v.day, v.hour, v.minute, v.second
),
Element::NumericString(v) => println!("{}NumericString: {:?}", pad, v.as_str()),
Element::TeletexString(v) => {
println!("{}TeletexString: {}", pad, bytes_to_hex(v.as_bytes()))
}
Element::VisibleString(v) => println!("{}VisibleString: {:?}", pad, v.as_str()),
Element::GeneralString(v) => {
println!("{}GeneralString: {}", pad, bytes_to_hex(v.as_bytes()))
}
Element::UniversalString(v) => println!("{}UniversalString: {:?}", pad, v.as_str()),
Element::BmpString(v) => println!("{}BmpString: {:?}", pad, v.as_str()),
Element::Sequence(seq) => {
let children: Vec<_> = seq.iter().flatten().collect();
println!("{}Sequence ({} elements):", pad, children.len());
for child in children {
print_element_tree(&child, depth + 1);
}
}
Element::Set(set) => {
let children: Vec<_> = set.iter().flatten().collect();
println!("{}Set ({} elements):", pad, children.len());
for child in children {
print_element_tree(&child, depth + 1);
}
}
Element::Tagged(tag, inner) => {
println!("{}Tagged [{:?} {}]:", pad, tag.class(), tag.number());
print_element_tree(inner, depth + 1);
}
Element::Raw(tag, bytes) => {
println!(
"{}Raw [{:?} {}]: {}",
pad,
tag.class(),
tag.number(),
bytes_to_hex(bytes)
);
}
}
}
#[cfg(feature = "serde")]
fn count_element_nodes(el: &synta::Element<'_>) -> usize {
use synta::Element;
match el {
Element::Sequence(seq) => {
1 + seq
.iter()
.flatten()
.map(|el| count_element_nodes(&el))
.sum::<usize>()
}
Element::Set(set) => {
1 + set
.iter()
.flatten()
.map(|el| count_element_nodes(&el))
.sum::<usize>()
}
Element::Tagged(_, inner) => 1 + count_element_nodes(inner),
_ => 1,
}
}
#[cfg(feature = "serde")]
fn print_json_tree(val: &serde_json::Value, depth: usize) {
use serde_json::Value;
let pad = " ".repeat(depth);
let type_name = val.get("type").and_then(Value::as_str).unwrap_or("Unknown");
match type_name {
"Sequence" | "Set" => {
if let Some(arr) = val.get("value").and_then(Value::as_array) {
println!("{}{} ({} elements):", pad, type_name, arr.len());
for child in arr {
print_json_tree(child, depth + 1);
}
} else {
println!("{}{}: (empty)", pad, type_name);
}
}
"Tagged" => {
let tag = val.get("tag").cloned().unwrap_or(Value::Null);
let class = tag.get("class").and_then(Value::as_str).unwrap_or("?");
let number = tag.get("number").and_then(Value::as_u64).unwrap_or(0);
println!("{}Tagged [{} {}]:", pad, class, number);
if let Some(inner) = val.get("value") {
print_json_tree(inner, depth + 1);
}
}
_ => {
let leaf = val.get("value").unwrap_or(&Value::Null);
println!("{}{}: {}", pad, type_name, leaf);
}
}
}
#[cfg(feature = "serde")]
fn count_json_nodes(val: &serde_json::Value) -> usize {
use serde_json::Value;
let type_name = val.get("type").and_then(Value::as_str).unwrap_or("Unknown");
match type_name {
"Sequence" | "Set" => {
let children = val
.get("value")
.and_then(Value::as_array)
.map(|a| a.iter().map(count_json_nodes).sum::<usize>())
.unwrap_or(0);
1 + children
}
"Tagged" => {
let inner_count = val.get("value").map(count_json_nodes).unwrap_or(0);
1 + inner_count
}
_ => 1,
}
}
#[cfg(not(feature = "serde"))]
fn main() {
println!("This example requires the 'serde' feature.");
println!("Run with: cargo run --example serde_cert --features serde");
}