#[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 = 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, parent: 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 { match self.kind { HostKind::Client { ref ssid, .. } => format!("\"{name}\" -- \"{parent}\" [label=\"{ssid}\"]", name=self.name, parent=self.parent, ssid=ssid).into(), // hier eventuelle ausnahmen für bestimmte typen die keine parents haben eintragen // mainly template und sol _ => format!("\"{name}\" -- \"{parent}\"", name=self.name, parent=self.parent).into(), } } } #[derive(Debug)] enum HostKind { Client { mac: String, subnet: String, coordinates: Option<[f64;2]>, ssid: String }, AccessPoint { mac: String, ssid: 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 DotRecord for (A,B) { fn recordify(self) -> String { format!("{{{}|{}}}", self.0.recordify(), self.1.recordify()) } } impl DotRecord for (A,B,C) { fn recordify(self) -> String { format!("{{{}|{}|{}}}", self.0.recordify(), self.1.recordify(), self.2.recordify()) } } impl DotRecord for (A,B,C,D) { fn recordify(self) -> String { format!("{{{}|{}|{}|{}}}", self.0.recordify(), self.1.recordify(), self.2.recordify(), self.3.recordify()) } } impl 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()) } }