Skip to content

Commit

Permalink
add serializable feature
Browse files Browse the repository at this point in the history
  • Loading branch information
ljkgpxs committed Feb 14, 2023
1 parent 2ce02d4 commit f2fdd19
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 3 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# 0.2.0

* fixed: cargo test failed
* Route class now is serializable with serde

# 0.1.2

* add code block type for documents
Expand Down
12 changes: 10 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "winroute"
version = "0.1.2"
version = "0.2.0"
edition = "2021"
author = "ljkgpxs <ljkgpxs@gmail.com>"
authors = ["ljkgpxs <ljkgpxs@gmail.com>"]
description = "This crate is a utilities of high level of interface for manipulating and observing Windows's routing table"
repository = "https://github.com/ljkgpxs/winroute"
homepage = "https://github.com/ljkgpxs/winroute"
Expand All @@ -18,6 +18,14 @@ license = "Apache-2.0"

[dependencies]
crossbeam-channel = "0.5"
serde = {version = "1.0", features = ["derive"], optional = true}

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["iphlpapi", "netioapi"] }

[dev-dependencies]
serde_json = {version = "1.0"}

[features]
default = ["serializable"]
serializable = ["serde"]
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,7 @@ loop {
let event = recvier.recv().unwrap();
println!("{:?}", event);
}
```
```

# Features
* `serializable`: This feature is enabled by default, it implemented `serde`'s `Serialize` and `Deserialize`, this feature requires an additional dependency on `serde`
112 changes: 112 additions & 0 deletions src/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use std::{
};

/// Routing data structure, including destination address, gateway and other information
#[cfg_attr(feature = "serializable", derive(serde::Serialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Route {
/// Network address of the destination. `0.0.0.0` with a prefix of `0` is considered a default route.
Expand All @@ -41,13 +42,20 @@ pub struct Route {

/// The locally unique identifier (LUID) for the network interface associated with this IP route entry.
pub luid: Option<u64>,

/// The IP version number, the value is 4 or 6
pub version: u8,
}

impl Route {
/// Create a route that matches a given destination network.
///
/// Either the gateway or interface should be set before attempting to add to a routing table.
pub fn new(destination: IpAddr, prefix: u8) -> Self {
let version = match destination {
IpAddr::V4(_) => 4,
IpAddr::V6(_) => 6,
};
Self {
destination,
prefix,
Expand All @@ -58,12 +66,17 @@ impl Route {
ifindex: None,
metric: None,
luid: None,
version,
}
}

/// destination setter
pub fn destination(mut self, destination: IpAddr) -> Self {
self.destination = destination;
self.version = match destination {
IpAddr::V4(_) => 4,
IpAddr::V6(_) => 6,
};
self
}

Expand Down Expand Up @@ -111,6 +124,76 @@ impl Display for Route {
}
}

#[cfg(feature = "serializable")]
struct CustomVisitor;

#[cfg(feature = "serializable")]
impl<'de> serde::Deserialize<'de> for Route {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_any(CustomVisitor)
}
}
#[cfg(feature = "serializable")]
impl<'de> serde::de::Visitor<'de> for CustomVisitor {
type Value = Route;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "a map with keys 'first' and 'second'")
}

fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
let mut route = Route::new(IpAddr::V4("0.0.0.0".parse().unwrap()), 0);
while let Some(key) = map.next_key()? {
match key {
"destination" => {
let dest_ip: String = map.next_value()?;
let res = dest_ip.parse::<IpAddr>();
if res.is_ok() {
route = route.destination(res.unwrap());
}
}
"prefix" => {
route = route.prefix(map.next_value()?);
}
"gateway" => {
let v = map.next_value();
if v.is_ok() {
route = route.gateway(v.unwrap());
}
}
"ifindex" => {
let v = map.next_value();
if v.is_ok() {
route = route.ifindex(v.unwrap());
}
}
"luid" => {
let v = map.next_value();
if v.is_ok() {
route = route.luid(v.unwrap());
}
}
"metric" => {
let v = map.next_value();
if v.is_ok() {
route = route.metric(v.unwrap());
}
}
_ => {
let _: serde::de::IgnoredAny = map.next_value()?;
}
}
}
Ok(route)
}
}

#[cfg(test)]
pub mod test_route {
use super::Route;
Expand All @@ -128,6 +211,7 @@ pub mod test_route {
"192.168.0.0/24 gateway 172.1.1.254 metric Some(1)",
route.to_string()
);
assert_eq!(4, route.version);

let route = Route::new("192.168.1.0".parse().unwrap(), 32);
assert_eq!(
Expand All @@ -140,5 +224,33 @@ pub mod test_route {
fn testv6() {
let route = Route::new("fe80:9464::".parse().unwrap(), 32);
assert_eq!("fe80:9464::/32 gateway :: metric None", route.to_string());
assert_eq!(6, route.version);
}

#[test]
#[cfg(feature = "serializable")]
fn test_serializable() {
let route = Route::new("192.168.1.0".parse().unwrap(), 32)
.destination("192.168.0.0".parse().unwrap())
.prefix(24)
.gateway("172.1.1.254".parse().unwrap())
.ifindex(1)
.luid(123456)
.metric(1);
let res = serde_json::to_string(&route).expect("Failed to serialize Route Object");
assert_eq!("{\"destination\":\"192.168.0.0\",\"prefix\":24,\"gateway\":\"172.1.1.254\",\"ifindex\":1,\"metric\":1,\"luid\":123456,\"version\":4}", res);
let route: Route = serde_json::from_str(&res).unwrap();
assert_eq!(
"192.168.0.0/24 gateway 172.1.1.254 metric Some(1)",
route.to_string()
);
assert_eq!(4, route.version);

let route = Route::new("fe80:9464::".parse().unwrap(), 32);
let res = serde_json::to_string(&route).expect("Failed to serialize Route Object");
assert_eq!("{\"destination\":\"fe80:9464::\",\"prefix\":32,\"gateway\":\"::\",\"ifindex\":null,\"metric\":null,\"luid\":null,\"version\":6}", res);
let route: Route = serde_json::from_str(&res).unwrap();
assert_eq!("fe80:9464::/32 gateway :: metric None", route.to_string());
assert_eq!(6, route.version);
}
}

0 comments on commit f2fdd19

Please sign in to comment.