Skip to main content

parse

Function parse 

Source
pub fn parse(input: &str) -> Result<NetworkConfig, ParseError>
Expand description

Parse a Cisco IOS show running-config string into a NetworkConfig.

The parser is lenient: unrecognized commands are preserved in NetworkConfig::unknown_blocks rather than causing an error.

§Errors

Returns ParseError::EmptyInput if the input contains no non-whitespace, non-comment lines.

Examples found in repository?
examples/parse.rs (line 24)
12fn main() {
13    let raw = match env::args().nth(1) {
14        Some(path) => fs::read_to_string(&path)
15            .unwrap_or_else(|e| { eprintln!("Cannot read {path}: {e}"); std::process::exit(1) }),
16        None => {
17            let mut buf = String::new();
18            io::stdin().read_to_string(&mut buf)
19                .unwrap_or_else(|e| { eprintln!("Cannot read stdin: {e}"); std::process::exit(1) });
20            buf
21        }
22    };
23
24    let cfg = match parse(&raw) {
25        Ok(c) => c,
26        Err(e) => { eprintln!("Parse error: {e}"); std::process::exit(1) }
27    };
28
29    // ── Header ────────────────────────────────────────────────────────────────
30    println!("=== ios-config summary ===\n");
31    if let Some(h) = &cfg.hostname      { println!("Hostname    : {h}"); }
32    if let Some(d) = &cfg.domain_name   { println!("Domain      : {d}"); }
33
34    // ── Interfaces ────────────────────────────────────────────────────────────
35    println!("\n── Interfaces ({}) ──", cfg.interfaces.len());
36    for iface in &cfg.interfaces {
37        let state = if iface.shutdown { "shutdown" } else { "up" };
38        let addrs: Vec<_> = iface.addresses.iter()
39            .map(|a| a.prefix.to_string())
40            .collect();
41        let addr_str = if addrs.is_empty() {
42            "no ip address".to_string()
43        } else {
44            addrs.join(", ")
45        };
46        let desc = iface.description.as_deref().unwrap_or("-");
47        println!("  {:<32} [{state:^8}]  {addr_str}  \"{desc}\"",
48            iface.name.original);
49    }
50
51    // ── Routing ───────────────────────────────────────────────────────────────
52    if !cfg.routing.static_routes.is_empty() {
53        println!("\n── Static routes ({}) ──", cfg.routing.static_routes.len());
54        for r in &cfg.routing.static_routes {
55            print!("  {}", r.prefix);
56            match &r.next_hop {
57                ios_config::NextHop::Ip(ip)          => print!(" via {ip}"),
58                ios_config::NextHop::Interface(i)    => print!(" dev {i}"),
59                ios_config::NextHop::IpAndInterface(ip, i) => print!(" via {ip} dev {i}"),
60                ios_config::NextHop::Null0            => print!(" Null0"),
61            }
62            if let Some(name) = &r.name { print!(" ({name})"); }
63            println!();
64        }
65    }
66
67    for ospf in &cfg.routing.ospf {
68        println!("\n── OSPF process {} ──", ospf.process_id);
69        if let Some(rid) = &ospf.router_id { println!("  router-id {rid}"); }
70        let net_count: usize = ospf.areas.iter().map(|a| a.networks.len()).sum();
71        println!("  networks  : {net_count} across {} area(s)", ospf.areas.len());
72        println!("  passive   : {}", ospf.passive_interfaces.join(", "));
73    }
74
75    if let Some(bgp) = &cfg.routing.bgp {
76        println!("\n── BGP AS{} ──", bgp.asn);
77        println!("  neighbors : {}", bgp.neighbors.len());
78        println!("  peer-groups: {}", bgp.peer_groups.len());
79        println!("  address-families: {}", bgp.address_families.len());
80    }
81
82    // ── Security ──────────────────────────────────────────────────────────────
83    if !cfg.acls.is_empty() {
84        println!("\n── ACLs ({}) ──", cfg.acls.len());
85        for acl in &cfg.acls {
86            println!("  {:?} ({} entries)", acl.name, acl.entries.len());
87        }
88    }
89
90    if !cfg.nat.is_empty() {
91        println!("\n── NAT ({} rules) ──", cfg.nat.len());
92    }
93
94    // ── Management ────────────────────────────────────────────────────────────
95    if !cfg.ntp.is_empty() {
96        let servers: Vec<_> = cfg.ntp.iter().map(|n| {
97            if n.prefer { format!("{} (prefer)", n.address) } else { n.address.to_string() }
98        }).collect();
99        println!("\n── NTP: {} ──", servers.join(", "));
100    }
101
102    if let Some(snmp) = &cfg.snmp {
103        println!("\n── SNMP ──");
104        if let Some(loc) = &snmp.location { println!("  location: {loc}"); }
105        println!("  communities: {}", snmp.communities.len());
106    }
107
108    // ── Unknowns ─────────────────────────────────────────────────────────────
109    if !cfg.unknown_blocks.is_empty() {
110        println!("\n── Unrecognized commands ({}) ──", cfg.unknown_blocks.len());
111        for u in cfg.unknown_blocks.iter().take(10) {
112            println!("  [{}] {}", u.context, u.raw.trim());
113        }
114        if cfg.unknown_blocks.len() > 10 {
115            println!("  … and {} more", cfg.unknown_blocks.len() - 10);
116        }
117    }
118
119    println!("\nDone.");
120}