initial commit

This commit is contained in:
Marc Planard 2022-11-23 13:54:59 +01:00
commit 24616479be
3 changed files with 131 additions and 0 deletions

8
Cargo.toml Normal file
View File

@ -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]

93
src/ip.rs Normal file
View File

@ -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<Self> {
let tbl : Vec<u8> = 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);
}

30
src/main.rs Normal file
View File

@ -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);
}