use std::fmt;
use super::bpf_builder::{BpfFilterBuilder, MatchFrag};
pub(crate) fn render(builder: &BpfFilterBuilder, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let body = render_to_string(builder);
if builder.negated {
let body_is_composite = builder.fragments.len() + builder.or_branches.len() > 1
|| !builder.or_branches.is_empty();
if body.is_empty() {
f.write_str("not ()")
} else if body_is_composite {
write!(f, "not ({body})")
} else {
write!(f, "not {body}")
}
} else {
f.write_str(&body)
}
}
fn render_to_string(builder: &BpfFilterBuilder) -> String {
let mut parts: Vec<String> = builder.fragments.iter().map(frag_to_string).collect();
for branch in &builder.or_branches {
parts.push(format!("({})", render_branch(branch)));
}
parts.join(" and ")
}
fn render_branch(branch: &BpfFilterBuilder) -> String {
let body = render_to_string(branch);
if branch.negated {
let composite =
branch.fragments.len() + branch.or_branches.len() > 1 || !branch.or_branches.is_empty();
if body.is_empty() {
"not ()".to_string()
} else if composite {
format!("not ({body})")
} else {
format!("not {body}")
}
} else {
body
}
}
fn frag_to_string(frag: &MatchFrag) -> String {
match frag {
MatchFrag::EthType(0x0800) => "ip".to_string(),
MatchFrag::EthType(0x86DD) => "ip6".to_string(),
MatchFrag::EthType(0x0806) => "arp".to_string(),
MatchFrag::EthType(n) => format!("ether proto 0x{n:04x}"),
MatchFrag::Vlan => "vlan".to_string(),
MatchFrag::VlanId(id) => format!("vlan {id}"),
MatchFrag::IpProto(6) => "tcp".to_string(),
MatchFrag::IpProto(17) => "udp".to_string(),
MatchFrag::IpProto(1) => "icmp".to_string(),
MatchFrag::IpProto(58) => "icmp6".to_string(),
MatchFrag::IpProto(n) => format!("ip proto {n}"),
MatchFrag::SrcHost(addr) => format!("src host {addr}"),
MatchFrag::DstHost(addr) => format!("dst host {addr}"),
MatchFrag::AnyHost(addr) => format!("host {addr}"),
MatchFrag::SrcNet(net) => format!("src net {net}"),
MatchFrag::DstNet(net) => format!("dst net {net}"),
MatchFrag::AnyNet(net) => format!("net {net}"),
MatchFrag::SrcPort(p) => format!("src port {p}"),
MatchFrag::DstPort(p) => format!("dst port {p}"),
MatchFrag::AnyPort(p) => format!("port {p}"),
}
}