1use 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 #[arg(long)]
27 pub ip_address: Option<String>,
28
29 #[arg(long)]
31 pub subnet_mask: Option<String>,
32
33 #[arg(long)]
35 pub prefix_length: Option<usize>,
36
37 #[arg(short, long)]
39 pub conversion_type: Option<ConversionType>,
40}
41
42#[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
61pub 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
103pub 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 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}