iasc/
lib.rs

1//! # Ip address And Subnet mask Converter
2//! This CLI tool can convert some Network's address to another address.
3//!
4//! ## Usage
5//! ```shell
6//! iasc --conversion-type prefix-to-subnet --prefix-length 24
7//! # Output
8//! 255.255.255.0
9//! ```
10//! ```shell
11//! iasc --conversion-type subnet-to-prefix --subnet-mask "255.255.255.0"
12//! # Output
13//! 24
14//! ```
15
16use clap::Parser;
17use clap::ValueEnum;
18use std::net::Ipv4Addr;
19
20pub type SubnetMask = Ipv4Addr;
21
22#[derive(Debug, Parser)]
23#[command(version, about, author)]
24pub struct Args {
25    /// Specify the IP address.
26    #[arg(long)]
27    pub ip_address: Option<String>,
28
29    /// Specify the subnet mask.
30    #[arg(long)]
31    pub subnet_mask: Option<String>,
32
33    /// Specify the prefix length.
34    #[arg(long)]
35    pub prefix_length: Option<usize>,
36
37    /// Specify the conversion type.
38    #[arg(short, long)]
39    pub conversion_type: Option<ConversionType>,
40}
41
42/// This enum is used whether the user wants to convert subnet mask to prefix length or prefix length to subnet mask.
43#[derive(Clone, Debug, ValueEnum)]
44pub enum ConversionType {
45    SubnetToPrefix,
46    PrefixToSubnet,
47}
48
49impl std::str::FromStr for ConversionType {
50    type Err = String;
51
52    fn from_str(s: &str) -> Result<Self, Self::Err> {
53        match s {
54            "subnet-to-prefix" => Ok(ConversionType::SubnetToPrefix),
55            "prefix-to-subnet" => Ok(ConversionType::PrefixToSubnet),
56            _ => Err(String::from("Invalid conversion type")),
57        }
58    }
59}
60
61/// Calculate the prefix length from the given subnet mask.
62pub fn subnet_to_prefix(subnet: SubnetMask) -> Result<PrefixLength, String> {
63    let subnet_string: String = subnet.to_string();
64    match &subnet_string[..] {
65        "255.255.255.255" => Ok(PrefixLength::new(32)?),
66        "255.255.255.254" => Ok(PrefixLength::new(31)?),
67        "255.255.255.252" => Ok(PrefixLength::new(30)?),
68        "255.255.255.248" => Ok(PrefixLength::new(29)?),
69        "255.255.255.240" => Ok(PrefixLength::new(28)?),
70        "255.255.255.224" => Ok(PrefixLength::new(27)?),
71        "255.255.255.192" => Ok(PrefixLength::new(26)?),
72        "255.255.255.128" => Ok(PrefixLength::new(25)?),
73        "255.255.255.0" => Ok(PrefixLength::new(24)?),
74        "255.255.254.0" => Ok(PrefixLength::new(23)?),
75        "255.255.252.0" => Ok(PrefixLength::new(22)?),
76        "255.255.248.0" => Ok(PrefixLength::new(21)?),
77        "255.255.240.0" => Ok(PrefixLength::new(20)?),
78        "255.255.224.0" => Ok(PrefixLength::new(19)?),
79        "255.255.192.0" => Ok(PrefixLength::new(18)?),
80        "255.255.128.0" => Ok(PrefixLength::new(17)?),
81        "255.255.0.0" => Ok(PrefixLength::new(16)?),
82        "255.254.0.0" => Ok(PrefixLength::new(15)?),
83        "255.252.0.0" => Ok(PrefixLength::new(14)?),
84        "255.248.0.0" => Ok(PrefixLength::new(13)?),
85        "255.240.0.0" => Ok(PrefixLength::new(12)?),
86        "255.224.0.0" => Ok(PrefixLength::new(11)?),
87        "255.192.0.0" => Ok(PrefixLength::new(10)?),
88        "255.128.0.0" => Ok(PrefixLength::new(9)?),
89        "255.0.0.0" => Ok(PrefixLength::new(8)?),
90        "254.0.0.0" => Ok(PrefixLength::new(7)?),
91        "252.0.0.0" => Ok(PrefixLength::new(6)?),
92        "248.0.0.0" => Ok(PrefixLength::new(5)?),
93        "240.0.0.0" => Ok(PrefixLength::new(4)?),
94        "224.0.0.0" => Ok(PrefixLength::new(3)?),
95        "192.0.0.0" => Ok(PrefixLength::new(2)?),
96        "128.0.0.0" => Ok(PrefixLength::new(1)?),
97        _ => Err(String::from(
98            "Cannot calcurate the SubnetMask... Perhaps, do you input invalid SubnetMask?",
99        )),
100    }
101}
102
103/// Calculate the subnet mask from the given prefix length.
104pub fn prefix_to_subnet(prefix: PrefixLength) -> Result<SubnetMask, String> {
105    let prefix_string: String = prefix.to_string();
106    match &prefix_string[..] {
107        "32" => Ok(SubnetMask::new(255, 255, 255, 255)),
108        "31" => Ok(SubnetMask::new(255, 255, 255, 254)),
109        "30" => Ok(SubnetMask::new(255, 255, 255, 252)),
110        "29" => Ok(SubnetMask::new(255, 255, 255, 248)),
111        "28" => Ok(SubnetMask::new(255, 255, 255, 240)),
112        "27" => Ok(SubnetMask::new(255, 255, 255, 224)),
113        "26" => Ok(SubnetMask::new(255, 255, 255, 192)),
114        "25" => Ok(SubnetMask::new(255, 255, 255, 128)),
115        "24" => Ok(SubnetMask::new(255, 255, 255, 0)),
116        "23" => Ok(SubnetMask::new(255, 255, 254, 0)),
117        "22" => Ok(SubnetMask::new(255, 255, 252, 0)),
118        "21" => Ok(SubnetMask::new(255, 255, 248, 0)),
119        "20" => Ok(SubnetMask::new(255, 255, 240, 0)),
120        "19" => Ok(SubnetMask::new(255, 255, 224, 0)),
121        "18" => Ok(SubnetMask::new(255, 255, 192, 0)),
122        "17" => Ok(SubnetMask::new(255, 255, 128, 0)),
123        "16" => Ok(SubnetMask::new(255, 255, 0, 0)),
124        "15" => Ok(SubnetMask::new(255, 254, 0, 0)),
125        "14" => Ok(SubnetMask::new(255, 252, 0, 0)),
126        "13" => Ok(SubnetMask::new(255, 248, 0, 0)),
127        "12" => Ok(SubnetMask::new(255, 240, 0, 0)),
128        "11" => Ok(SubnetMask::new(255, 224, 0, 0)),
129        "10" => Ok(SubnetMask::new(255, 192, 0, 0)),
130        "9" => Ok(SubnetMask::new(255, 128, 0, 0)),
131        "8" => Ok(SubnetMask::new(255, 0, 0, 0)),
132        "7" => Ok(SubnetMask::new(254, 0, 0, 0)),
133        "6" => Ok(SubnetMask::new(252, 0, 0, 0)),
134        "5" => Ok(SubnetMask::new(248, 0, 0, 0)),
135        "4" => Ok(SubnetMask::new(240, 0, 0, 0)),
136        "3" => Ok(SubnetMask::new(224, 0, 0, 0)),
137        "2" => Ok(SubnetMask::new(192, 0, 0, 0)),
138        "1" => Ok(SubnetMask::new(128, 0, 0, 0)),
139        _ => Err(String::from(
140            "Cannot calcurate the PrefixLength... Perhaps, do you input invalid PrefixLength?",
141        )),
142    }
143}
144
145#[derive(Debug)]
146pub struct PrefixLength {
147    /// The length of the prefix.
148    pub length: u8,
149}
150
151impl std::fmt::Display for PrefixLength {
152    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153        write!(f, "{}", self.length)
154    }
155}
156
157impl PrefixLength {
158    pub fn new(length: usize) -> Result<Self, String> {
159        match length {
160            0 => Err(String::from("Prefix length must not be 0")),
161            1..=32 => Ok(PrefixLength {
162                length: length as u8,
163            }),
164            33.. => Err(String::from("Prefix length must not be greater than 32")),
165        }
166    }
167}
168
169#[cfg(test)]
170mod test {
171    use crate::{prefix_to_subnet, subnet_to_prefix, PrefixLength, SubnetMask};
172
173    #[test]
174    fn test_prefix_to_subnet() {
175        let prefix: PrefixLength = PrefixLength::new(24).unwrap();
176
177        assert_eq!(
178            prefix_to_subnet(prefix).unwrap().to_string(),
179            "255.255.255.0"
180        );
181    }
182
183    #[test]
184    #[should_panic(expected = "Prefix length must not be greater than 32")]
185    fn test_invalid_prefix() {
186        let _invalid_prefix: PrefixLength = PrefixLength::new(33).unwrap();
187    }
188
189    #[test]
190    #[should_panic(expected = "Prefix length must not be 0")]
191    fn test_zero_prefix() {
192        let _zero_prefix: PrefixLength = PrefixLength::new(0).unwrap();
193    }
194
195    #[test]
196    fn test_subnet_to_prefix() {
197        const SUBNET: SubnetMask = SubnetMask::new(255, 255, 255, 0);
198        const INVALID_SUBNET: SubnetMask = SubnetMask::new(255, 255, 255, 123);
199
200        assert_eq!(subnet_to_prefix(SUBNET).unwrap().to_string(), "24");
201        assert_eq!(
202            subnet_to_prefix(INVALID_SUBNET).unwrap_err(),
203            "Cannot calcurate the SubnetMask... Perhaps, do you input invalid SubnetMask?"
204        );
205    }
206}