
hab das error-handling vereinfacht, es panict jetzt sofort sobald ein fehler gefunden wurde, anstatt die fehler zurückzugeben. ratio ist, dass die fehler eh nicht behandelt werden können, und sonst nur weiter oben im call-stack zum abbruch geführt hätten. parst jetzt ein bischen mehr attribute, es ist im code einstellbar beim fehlen welcher attribute gepanict werden soll und bei welchen nur eine zeile auf stderr gesetzt werden soll; schreibt bei fehlenden attributen jetzt eine warnung auf stderr. kann jetzt auch die in verzeichnisse aufgeteilten einträge verarbeiten
148 lines
4.4 KiB
Rust
148 lines
4.4 KiB
Rust
#[macro_use]
|
|
extern crate serde_derive;
|
|
extern crate serde_yaml;
|
|
use std::path::Path;
|
|
|
|
mod parsing;
|
|
|
|
fn main() {
|
|
//read the files
|
|
//let paths = [Path::new("test.yml")];
|
|
let filenames: Vec<_> = std::env::args_os().skip(1).collect();
|
|
let hosts: Vec<Host> = filenames.iter()
|
|
.map(Path::new)
|
|
.map(|path| Host::from_file(&path))
|
|
.collect();
|
|
|
|
//build dot-graph
|
|
let nodes = hosts.iter().map(Host::to_dot_node);
|
|
let edges = hosts.iter().flat_map(Host::to_dot_edge);
|
|
|
|
//render it
|
|
println!("graph antennen {{");
|
|
for node in nodes {
|
|
println!("{}", node);
|
|
}
|
|
for edge in edges {
|
|
println!("{}", edge);
|
|
}
|
|
println!("}}");
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Host {
|
|
name: String,
|
|
ip: String,
|
|
kind: HostKind,
|
|
}
|
|
impl Host {
|
|
fn from_file(path: &Path) -> Self {
|
|
let (host, hostname) = parsing::RawHost::from_file(path);
|
|
host.parse(hostname)
|
|
}
|
|
fn to_dot_node(&self) -> String {
|
|
let mut attributes = vec!
|
|
[ "shape=record".to_string()
|
|
, "style=filled".into()
|
|
];
|
|
|
|
let record;
|
|
|
|
// type-specific handling
|
|
use HostKind::*;
|
|
match self.kind {
|
|
Client {coordinates, ref subnet, ref mac, .. } =>
|
|
{ attributes.push("fillcolor=lightgray".into())
|
|
; if let Some(coordinates) = coordinates
|
|
{ attributes.push(format!("pos=\"{breitengrad},{längengrad}\""
|
|
, längengrad=coordinates[0], breitengrad=coordinates[1]))
|
|
}
|
|
; record =
|
|
( self.name.clone()
|
|
, (self.kind.name(), mac.clone())
|
|
, (self.ip.clone(), subnet.clone())
|
|
, ("no ipv6", "no 6-subnet")
|
|
).recordify()
|
|
},
|
|
AccessPoint { ref mac, .. } => record =
|
|
( self.name.clone()
|
|
, (self.kind.name(), mac.clone() )
|
|
, self.ip.clone()
|
|
).recordify(),
|
|
_ => record = (self.name.clone(), self.kind.name(), self.ip.clone()).recordify(),
|
|
};
|
|
attributes.push(format!("label=\"{}\"", record));
|
|
|
|
let attributes = attributes.join(", ");
|
|
|
|
format!("\"{name}\" [{attributes}]", name=self.name, attributes=attributes)
|
|
}
|
|
fn to_dot_edge(&self) -> Option<String> {
|
|
match self.kind {
|
|
HostKind::Client { ref parent, ref ssid, .. } =>
|
|
format!("\"{name}\" -- \"{parent}\" [label=\"{ssid}\"]", name=self.name, parent=parent, ssid=ssid).into(),
|
|
HostKind::AccessPoint { ref parent, .. } =>
|
|
format!("\"{name}\" -- \"{parent}\"", name=self.name, parent=parent).into(),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum HostKind {
|
|
Client { mac: String, subnet: String, coordinates: Option<[f64;2]>, parent: String, ssid: String },
|
|
AccessPoint { mac: String, ssid: String, parent: String },
|
|
Service,
|
|
Other,
|
|
}
|
|
|
|
impl HostKind {
|
|
fn name(&self) -> &'static str {
|
|
use HostKind::*;
|
|
match *self {
|
|
Client { .. } => "Client",
|
|
AccessPoint { .. } => "AccessPoint",
|
|
Service => "Service",
|
|
Other => "Other",
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/** dieser Trait ermöglicht es verschachtelte tupel in ein label für einen record-shaped-node für
|
|
dotfiles umzuwandeln. inputwerte werden nicht escaped
|
|
|
|
assert_eq!(
|
|
("a", ("b", "c", "d"), ("e","f"), "g").recordify(),
|
|
"{a|{b|c|d}|{e|f}|g}".into())
|
|
*/
|
|
pub trait DotRecord {
|
|
fn recordify(self) -> String;
|
|
}
|
|
|
|
impl DotRecord for String {
|
|
fn recordify(self) -> String { self }
|
|
}
|
|
impl<'a> DotRecord for &'a str {
|
|
fn recordify(self) -> String { self.into() }
|
|
}
|
|
impl<A: DotRecord, B: DotRecord> DotRecord for (A,B) {
|
|
fn recordify(self) -> String {
|
|
format!("{{{}|{}}}", self.0.recordify(), self.1.recordify())
|
|
}
|
|
}
|
|
impl<A: DotRecord, B: DotRecord, C: DotRecord> DotRecord for (A,B,C) {
|
|
fn recordify(self) -> String {
|
|
format!("{{{}|{}|{}}}", self.0.recordify(), self.1.recordify(), self.2.recordify())
|
|
}
|
|
}
|
|
impl<A: DotRecord, B: DotRecord, C: DotRecord, D: DotRecord> DotRecord for (A,B,C,D) {
|
|
fn recordify(self) -> String {
|
|
format!("{{{}|{}|{}|{}}}", self.0.recordify(), self.1.recordify(), self.2.recordify(), self.3.recordify())
|
|
}
|
|
}
|
|
impl<A: DotRecord, B: DotRecord, C: DotRecord, D: DotRecord, E: DotRecord> DotRecord for (A,B,C,D,E) {
|
|
fn recordify(self) -> String {
|
|
format!("{{{}|{}|{}|{}|{}}}", self.0.recordify(), self.1.recordify(), self.2.recordify(), self.3.recordify(), self.4.recordify())
|
|
}
|
|
}
|