diff --git a/Cargo.lock b/Cargo.lock index 6482292..b6d216f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -25,11 +34,19 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + [[package]] name = "minimon" version = "0.1.0" dependencies = [ "ping", + "regex", + "thiserror", ] [[package]] @@ -97,6 +114,35 @@ dependencies = [ "getrandom", ] +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + [[package]] name = "socket2" version = "0.4.10" diff --git a/Cargo.toml b/Cargo.toml index 117bbe6..b2a058d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,5 @@ edition = "2021" [dependencies] ping = "0.5.2" +regex = "1.10.5" +thiserror = "1.0.61" diff --git a/src/hostname.rs b/src/hostname.rs new file mode 100644 index 0000000..a04fe6e --- /dev/null +++ b/src/hostname.rs @@ -0,0 +1,69 @@ +use ::thiserror::Error; +use regex::Regex; + +const VALID_IP_ADDRESS_PATTERN: &str = r"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"; +const VALID_HOSTNAME_PATTERN: &str = r"^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$"; + +#[derive(Clone, Debug, Error)] +#[error("The hostname \"{0}\" is invalid")] +pub struct InvalidHostnameError(String); + +pub struct Hostname { + hostname: String, +} + +impl Hostname { + pub fn new(hostname: &str) -> Result { + if Self::is_valid_ip(&hostname) || Self::is_valid_hostname(&hostname) { + Ok(Hostname { + hostname: hostname.to_string(), + }) + } else { + Err(InvalidHostnameError(hostname.to_string())) + } + } + + fn is_valid_ip(ip: &str) -> bool { + let re = Regex::new(VALID_IP_ADDRESS_PATTERN).unwrap(); + re.is_match(ip) + } + fn is_valid_hostname(hostname: &str) -> bool { + let re = Regex::new(VALID_HOSTNAME_PATTERN).unwrap(); + re.is_match(hostname) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn valid_ip_adresses() { + assert!(Hostname::new("192.168.0.1").is_ok()); + assert!(Hostname::new("255.127.63.31").is_ok()); + assert!(Hostname::new("1.0.0.1").is_ok()); + assert!(Hostname::new("10.1.10.47").is_ok()); + } + + #[test] + fn invalid_ip_adresses() { + assert!(Hostname::new("::1").is_err()); // We don't support IPv6 yet + } + + #[test] + fn valid_hostnames() { + assert!(Hostname::new("mononobe.fai.st").is_ok()); + assert!(Hostname::new("twink-pad").is_ok()); + assert!(Hostname::new("is-42-the-response").is_ok()); + assert!(Hostname::new("GoB.NaSa-42.eu").is_ok()); + assert!(Hostname::new("256.127.63.31").is_ok()); + } + + #[test] + fn invalid_hostnames() { + assert!(Hostname::new("no..double.dots").is_err()); + assert!(Hostname::new("#illegal-character").is_err()); // We don't support IPv6 yet + assert!(Hostname::new("").is_err()); + assert!(Hostname::new("not-end-with-hyphen-").is_err()); + } +} diff --git a/src/machine.rs b/src/machine.rs index 305a59e..3b6284a 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -1,38 +1,30 @@ -use std::{collections::HashMap, net::IpAddr}; +use crate::hostname::Hostname; +use std::collections::HashMap; pub struct Machine { name: String, - address: IpAddr, + hostname: Hostname, } -enum Status { +pub enum Status { Unreachable, Forbidden, Success(HashMap), } impl Machine { - fn new(name: &str, address: IpAddr) -> Machine { + pub fn new(name: &str, hostname: Hostname) -> Machine { Machine { name: String::from(name), - address, + hostname, } } } -trait Monitor { +pub trait Monitor { fn monitor(machine: &Machine) -> Status; } -#[derive(Debug)] -struct PingMonitor {} - -impl Monitor for PingMonitor { - fn monitor(machine: &Machine) -> Status { - todo!() - } -} - #[derive(Debug)] struct SshMonitor {} diff --git a/src/main.rs b/src/main.rs index 66c2c90..56152c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ +mod hostname; mod machine; - +mod ping_monitor; use machine::Machine; fn main() { diff --git a/src/ping_monitor.rs b/src/ping_monitor.rs new file mode 100644 index 0000000..cbc7a25 --- /dev/null +++ b/src/ping_monitor.rs @@ -0,0 +1,18 @@ +use crate::machine::{Machine, Monitor, Status}; + +#[derive(Debug)] +struct PingMonitor {} + +impl Monitor for PingMonitor { + fn monitor(machine: &Machine) -> Status { + todo!() + } +} + +#[cfg(test)] +mod test { + #[test] + fn monitor_works() { + assert!(true) + } +}