use std::{cmp, fmt, vec, slice};
use std::collections::HashSet;
#[cfg(feature = "petgraph")]
use petgraph::Graph;
#[cfg(feature = "petgraph")]
use std::collections::HashMap;
use serde_json;
use Oid;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct Response {
pub warnings: Vec<Error>,
pub from: u64,
pub until: u64,
pub edges: Vec<Edge>,
}
impl Response {
pub fn nodes(&self) -> HashSet<Oid> {
let mut oids = HashSet::new();
for edge in &self.edges {
oids.insert(edge.from.clone());
oids.insert(edge.to.clone());
}
oids
}
pub fn iter<'a>(&'a self) -> slice::Iter<'a, Edge> {
self.edges.iter()
}
pub fn is_complete(&self) -> bool {
self.warnings.is_empty()
}
}
impl Default for Response {
fn default() -> Self {
Response {
warnings: vec![],
from: 0,
until: 0,
edges: vec![],
}
}
}
impl IntoIterator for Response {
type Item = Edge;
type IntoIter = vec::IntoIter<Edge>;
fn into_iter(self) -> Self::IntoIter {
self.edges.into_iter()
}
}
impl<'a> IntoIterator for &'a Response {
type Item = &'a Edge;
type IntoIter = slice::Iter<'a, Edge>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[cfg(feature = "petgraph")]
impl From<Response> for Graph<Oid, Edge> {
fn from(val: Response) -> Self {
let mut graph = Graph::new();
let nodes = val.nodes();
let mut node_idx = HashMap::with_capacity(nodes.len());
for node in nodes {
node_idx.insert(node.clone(), graph.add_node(node));
}
for edge in val.edges {
graph.add_edge(
*node_idx.get(&edge.from).unwrap(),
*node_idx.get(&edge.to).unwrap(),
edge
);
}
graph
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Error {
pub message: String,
#[serde(rename = "type")]
pub error_type: String,
#[serde(default)]
pub(crate) properties: Option<serde_json::Value>,
}
impl Error {
pub fn new<M: Into<String>, E: Into<String>>(message: M, error_type: E) -> Self {
Error {
message: message.into(),
error_type: error_type.into(),
properties: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Edge {
pub from: Oid,
pub to: Oid,
pub weight: usize,
#[serde(default)]
pub annotations: EdgeAnnotations,
}
impl Edge {
pub fn contains(&self, oid: &Oid) -> bool {
self.from == *oid || self.to == *oid
}
pub fn to_tuple(&self) -> (Oid, Oid) {
(self.from.clone(), self.to.clone())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct EdgeAnnotations {
pub appearances: Option<Vec<Appearance>>,
pub protocols: Option<Vec<ProtocolAnnotation>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, Hash, Serialize, Deserialize)]
pub struct Appearance {
pub walk: u16,
pub step: u16,
}
impl Appearance {
pub fn new(walk: u16, step: u16) -> Self {
Appearance {
walk: walk,
step: step,
}
}
}
impl PartialOrd for Appearance {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
let walk = self.walk.cmp(&other.walk);
if walk == cmp::Ordering::Equal {
Some(self.step.cmp(&other.step))
} else {
Some(walk)
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ProtocolAnnotation {
pub weight: u32,
pub protocol: ProtocolStack,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ProtocolStack(Vec<String>);
impl ProtocolStack {
pub fn protocols(&self) -> &[String] {
&self.0
}
}
impl fmt::Display for ProtocolStack {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let val = &self.0;
let len = val.len();
if val.is_empty() {
write!(f, "OTHER")
} else if len > 1 && val[len - 1] == "OTHER" {
write!(f, "{}", val[len - 2])
} else {
write!(f, "{}", val[len - 1])
}
}
}
impl From<Vec<&'static str>> for ProtocolStack {
fn from(vals: Vec<&'static str>) -> Self {
ProtocolStack(vals.into_iter().map(String::from).collect())
}
}
#[cfg(test)]
mod tests {
use super::{Appearance, ProtocolStack};
#[test]
fn protocol_fmt_http() {
assert_eq!(
"HTTP",
&format!("{}", ProtocolStack::from(vec!["IPv4", "TCP", "HTTP"]))
);
assert_eq!(
"HTTP",
&format!("{}", ProtocolStack::from(vec!["IPv6", "TCP", "HTTP"]))
);
}
#[test]
fn protocol_fmt_unknowns() {
assert_eq!(
"TCP",
&format!("{}", ProtocolStack::from(vec!["IPv4", "TCP", "OTHER"]))
);
assert_eq!("OTHER", &format!("{}", ProtocolStack::from(vec!["OTHER"])));
}
#[test]
fn order_appearances() {
let mut items = vec![
Appearance::new(1, 2),
Appearance::new(0, 2),
Appearance::new(1, 0),
Appearance::new(0, 1),
];
items.sort();
assert_eq!(items, vec![
Appearance::new(0, 1),
Appearance::new(0, 2),
Appearance::new(1, 0),
Appearance::new(1, 2),
]);
}
}