commit 24616479beda6a6fa9529cfec90d98e1425f995b Author: Marc Planard Date: Wed Nov 23 13:54:59 2022 +0100 initial commit diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..fb38bfa --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "ipcalc" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/ip.rs b/src/ip.rs new file mode 100644 index 0000000..2d5e3e5 --- /dev/null +++ b/src/ip.rs @@ -0,0 +1,93 @@ +use std::fmt::{self,Display}; // needed to implement Display for Ip +use std::convert::From; // needed to implement u32::from(&ip) + +// Debug needed for {:?} format +// PartialEq and Eq allows comparisons (usefull for unit tests) +#[derive(Debug,PartialEq,Eq)] +pub enum Ip { + V4([u8 ; 4]), + // V6([u16 ; 8]) // not implemented yet... +} + +// implement methods on the Ip type +// 'new' and 'from_str' are arbitrary and could be named differently +impl Ip { + pub fn new(a: u8, b: u8, c: u8, d: u8) -> Self { + Ip::V4([a, b, c, d]) + } + + pub fn from_str(s: &str) -> Option { + let tbl : Vec = s.split('.') + .filter_map(|s| s.parse().ok()) + .collect(); + Some(Ip::V4(tbl.try_into().ok()?)) + } + + // totaly useless but an example of an object method :) + pub fn is_even(&self) -> bool { + let Ip::V4([_,_,_,n]) = self; + n % 2 == 0 + } +} + +// allows conversion from ip to u32 with this syntax: +// `let nbr = u32::from(&ip);` +// use the std::convert::From trait +impl From<&Ip> for u32 { + fn from(src: &Ip) -> u32 { + let mut ip : u32 = 0; + let Ip::V4(array) = src; + for (idx, n) in array.iter().enumerate() { + ip += (*n as u32) << (8 * (3 - idx)); + } + ip + } +} + +// Display is a standard trait used by format! and println! and others +// to display objects... +impl Display for Ip { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Ip::V4(arr) => write!(f, "{}.{}.{}.{}", + arr[0], arr[1], arr[2], arr[3]), + /* + Ip::V6(arr) => write!(f, + "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", + arr[0], arr[1], arr[2], arr[3], + arr[4], arr[5], arr[6], arr[7]) + */ + } + } +} + +// Some unit tests, run with: +// `$ cargo test` +#[test] +fn test_ip_new() { + let ip = Ip::new(127, 0, 0, 1); + assert_eq!(ip, Ip::V4([127, 0, 0, 1])); +} + +#[test] +fn test_ip_from_str() { + let ip1 = Ip::from_str("127.0.0.1"); + assert_eq!(ip1, Some(Ip::V4([127, 0, 0, 1]))); + + let ip2 = Ip::from_str("512.0.0.1"); + assert_eq!(ip2, None); + +} + +#[test] +fn test_ip_is_even() { + assert_eq!(Ip::new(127, 0, 0, 1).is_even(), false); + assert_eq!(Ip::new(192, 168, 0, 2).is_even(), true); +} + +#[test] +fn test_u32_from_ip() { + let ip = Ip::new(127, 0, 0, 1); + let nbr = u32::from(&ip); + assert_eq!(nbr, 0x7f000001); +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d75ab05 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,30 @@ +mod ip; // declare that there is a ip.rs module needed by this project + +use ip::Ip; // import the Ip type from the ip module + +fn main() { + let Some(arg) = std::env::args().nth(1) else { + println!("we need one argument."); + return; + }; + + let Some(ip) = Ip::from_str(&arg) else { + println!("'{}' is is not a valid ipv4 address.", arg); + return; + }; + + let nbr = u32::from(&ip); + + println!("Canonical: {}", ip); + println!("Debug....: {:?}", ip); + println!("Binary...: {:b}", nbr); + println!("Hexa.....: 0x{:x}", nbr); + + let comment = match ip { + Ip::V4([127, 0, 0, 1]) => "This is you.", + Ip::V4([192,168,_,_]) => "This is a local area network", + Ip::V4([10,_,_,_]) => "This is a big local area network", + _ => "Nothing special to say about this address" + }; + println!("{}", comment); +}