Skip to content

Commit 5a4e21f

Browse files
feat: add IPv4 address conversion (#994)
1 parent a69a04f commit 5a4e21f

File tree

3 files changed

+265
-0
lines changed

3 files changed

+265
-0
lines changed

DIRECTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
* [Hexadecimal to Binary](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/hexadecimal_to_binary.rs)
7878
* [Hexadecimal to Decimal](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/hexadecimal_to_decimal.rs)
7979
* [Hexadecimal to Octal](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/hexadecimal_to_octal.rs)
80+
* [IPv4 Conversion](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/ipv4_conversion.rs)
8081
* [Length Conversion](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/length_conversion.rs)
8182
* [Octal to Binary](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/octal_to_binary.rs)
8283
* [Octal to Decimal](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/octal_to_decimal.rs)

src/conversions/ipv4_conversion.rs

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
/// Module for converting between IPv4 addresses and their decimal representations
2+
///
3+
/// This module provides functions to convert IPv4 addresses to decimal integers
4+
/// and vice versa.
5+
///
6+
/// Reference: https://www.geeksforgeeks.org/convert-ip-address-to-integer-and-vice-versa/
7+
use std::num::ParseIntError;
8+
9+
/// Errors that can occur during IPv4 address conversion
10+
#[derive(Debug, PartialEq)]
11+
pub enum Ipv4Error {
12+
/// The IPv4 address does not have exactly 4 octets
13+
InvalidFormat,
14+
/// An octet value is greater than 255
15+
InvalidOctet(u32),
16+
/// The decimal value is out of valid range
17+
InvalidDecimal,
18+
/// Failed to parse an octet as a number
19+
ParseError,
20+
}
21+
22+
impl std::fmt::Display for Ipv4Error {
23+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24+
match self {
25+
Ipv4Error::InvalidFormat => write!(f, "Invalid IPv4 address format"),
26+
Ipv4Error::InvalidOctet(octet) => write!(f, "Invalid IPv4 octet {octet}"),
27+
Ipv4Error::InvalidDecimal => write!(f, "Invalid decimal IPv4 address"),
28+
Ipv4Error::ParseError => write!(f, "Failed to parse octet"),
29+
}
30+
}
31+
}
32+
33+
impl std::error::Error for Ipv4Error {}
34+
35+
impl From<ParseIntError> for Ipv4Error {
36+
fn from(_: ParseIntError) -> Self {
37+
Ipv4Error::ParseError
38+
}
39+
}
40+
41+
/// Convert an IPv4 address to its decimal representation.
42+
///
43+
/// The conversion is performed by treating each octet as 8 bits and combining
44+
/// them into a 32-bit unsigned integer.
45+
///
46+
/// # Arguments
47+
///
48+
/// * `ipv4_address` - A string slice representing an IPv4 address (e.g., "192.168.0.1")
49+
///
50+
/// # Returns
51+
///
52+
/// * `Ok(u32)` - The decimal representation of the IP address
53+
/// * `Err(Ipv4Error)` - If the input format is invalid or contains invalid octets
54+
pub fn ipv4_to_decimal(ipv4_address: &str) -> Result<u32, Ipv4Error> {
55+
let octets: Vec<&str> = ipv4_address.split('.').collect();
56+
57+
if octets.len() != 4 {
58+
return Err(Ipv4Error::InvalidFormat);
59+
}
60+
61+
let mut decimal_ipv4: u32 = 0;
62+
63+
for octet_str in octets {
64+
let octet: u32 = octet_str.parse()?;
65+
66+
if octet > 255 {
67+
return Err(Ipv4Error::InvalidOctet(octet));
68+
}
69+
70+
decimal_ipv4 = (decimal_ipv4 << 8) + octet;
71+
}
72+
73+
Ok(decimal_ipv4)
74+
}
75+
76+
/// Alternative implementation to convert an IPv4 address to its decimal representation
77+
/// using hexadecimal conversion.
78+
///
79+
/// This function converts each octet to a two-digit hexadecimal string, concatenates
80+
/// them, and then parses the result as a hexadecimal number.
81+
///
82+
/// # Arguments
83+
///
84+
/// * `ipv4_address` - A string slice representing an IPv4 address
85+
///
86+
/// # Returns
87+
///
88+
/// * `Ok(u32)` - The decimal representation of the IP address
89+
/// * `Err(Ipv4Error)` - If the input is invalid
90+
pub fn alt_ipv4_to_decimal(ipv4_address: &str) -> Result<u32, Ipv4Error> {
91+
let octets: Vec<&str> = ipv4_address.split('.').collect();
92+
93+
if octets.len() != 4 {
94+
return Err(Ipv4Error::InvalidFormat);
95+
}
96+
97+
let hex_string: String = octets
98+
.iter()
99+
.map(|octet| {
100+
let num: u32 = octet.parse().map_err(|_| Ipv4Error::ParseError)?;
101+
if num > 255 {
102+
return Err(Ipv4Error::InvalidOctet(num));
103+
}
104+
Ok(format!("{num:02x}"))
105+
})
106+
.collect::<Result<Vec<String>, Ipv4Error>>()?
107+
.join("");
108+
109+
u32::from_str_radix(&hex_string, 16).map_err(|_| Ipv4Error::ParseError)
110+
}
111+
112+
/// Convert a decimal representation of an IP address to its IPv4 format.
113+
///
114+
/// The conversion extracts each octet by masking the lower 8 bits and right-shifting.
115+
///
116+
/// # Arguments
117+
///
118+
/// * `decimal_ipv4` - An unsigned 32-bit integer representing the decimal IP address
119+
///
120+
/// # Returns
121+
///
122+
/// * `Ok(String)` - The IPv4 representation of the decimal IP address
123+
pub fn decimal_to_ipv4(decimal_ipv4: u32) -> Result<String, Ipv4Error> {
124+
let mut ip_parts = Vec::new();
125+
let mut num = decimal_ipv4;
126+
127+
for _ in 0..4 {
128+
ip_parts.push((num & 255).to_string());
129+
num >>= 8;
130+
}
131+
132+
ip_parts.reverse();
133+
Ok(ip_parts.join("."))
134+
}
135+
136+
#[cfg(test)]
137+
mod tests {
138+
use super::*;
139+
140+
#[test]
141+
fn test_ipv4_to_decimal_valid() {
142+
assert_eq!(ipv4_to_decimal("192.168.0.1").unwrap(), 3232235521);
143+
assert_eq!(ipv4_to_decimal("10.0.0.255").unwrap(), 167772415);
144+
assert_eq!(ipv4_to_decimal("0.0.0.0").unwrap(), 0);
145+
assert_eq!(ipv4_to_decimal("255.255.255.255").unwrap(), 4294967295);
146+
assert_eq!(ipv4_to_decimal("8.8.8.8").unwrap(), 134744072);
147+
}
148+
149+
#[test]
150+
fn test_ipv4_to_decimal_invalid_format() {
151+
assert_eq!(ipv4_to_decimal("10.0.255"), Err(Ipv4Error::InvalidFormat));
152+
assert_eq!(ipv4_to_decimal("10.0.0.0.1"), Err(Ipv4Error::InvalidFormat));
153+
assert_eq!(ipv4_to_decimal(""), Err(Ipv4Error::InvalidFormat));
154+
assert_eq!(ipv4_to_decimal("192.168.0"), Err(Ipv4Error::InvalidFormat));
155+
}
156+
157+
#[test]
158+
fn test_ipv4_to_decimal_invalid_octet() {
159+
assert_eq!(
160+
ipv4_to_decimal("10.0.0.256"),
161+
Err(Ipv4Error::InvalidOctet(256))
162+
);
163+
assert_eq!(
164+
ipv4_to_decimal("300.168.0.1"),
165+
Err(Ipv4Error::InvalidOctet(300))
166+
);
167+
assert_eq!(
168+
ipv4_to_decimal("192.168.256.1"),
169+
Err(Ipv4Error::InvalidOctet(256))
170+
);
171+
}
172+
173+
#[test]
174+
fn test_ipv4_to_decimal_parse_error() {
175+
assert_eq!(ipv4_to_decimal("192.168.0.abc"), Err(Ipv4Error::ParseError));
176+
assert_eq!(ipv4_to_decimal("a.b.c.d"), Err(Ipv4Error::ParseError));
177+
}
178+
179+
#[test]
180+
fn test_alt_ipv4_to_decimal_valid() {
181+
assert_eq!(alt_ipv4_to_decimal("192.168.0.1").unwrap(), 3232235521);
182+
assert_eq!(alt_ipv4_to_decimal("10.0.0.255").unwrap(), 167772415);
183+
assert_eq!(alt_ipv4_to_decimal("0.0.0.0").unwrap(), 0);
184+
assert_eq!(alt_ipv4_to_decimal("255.255.255.255").unwrap(), 4294967295);
185+
}
186+
187+
#[test]
188+
fn test_alt_ipv4_to_decimal_invalid() {
189+
assert_eq!(
190+
alt_ipv4_to_decimal("10.0.255"),
191+
Err(Ipv4Error::InvalidFormat)
192+
);
193+
assert_eq!(
194+
alt_ipv4_to_decimal("10.0.0.256"),
195+
Err(Ipv4Error::InvalidOctet(256))
196+
);
197+
}
198+
199+
#[test]
200+
fn test_decimal_to_ipv4_valid() {
201+
assert_eq!(decimal_to_ipv4(3232235521).unwrap(), "192.168.0.1");
202+
assert_eq!(decimal_to_ipv4(167772415).unwrap(), "10.0.0.255");
203+
assert_eq!(decimal_to_ipv4(0).unwrap(), "0.0.0.0");
204+
assert_eq!(decimal_to_ipv4(4294967295).unwrap(), "255.255.255.255");
205+
assert_eq!(decimal_to_ipv4(134744072).unwrap(), "8.8.8.8");
206+
assert_eq!(decimal_to_ipv4(2886794752).unwrap(), "172.16.254.0");
207+
}
208+
209+
#[test]
210+
fn test_round_trip_conversion() {
211+
let test_addresses = vec![
212+
"192.168.0.1",
213+
"10.0.0.255",
214+
"172.16.254.1",
215+
"8.8.8.8",
216+
"255.255.255.255",
217+
"0.0.0.0",
218+
"127.0.0.1",
219+
"1.2.3.4",
220+
];
221+
222+
for addr in test_addresses {
223+
let decimal = ipv4_to_decimal(addr).unwrap();
224+
let result = decimal_to_ipv4(decimal).unwrap();
225+
assert_eq!(addr, result, "Round trip failed for {addr}");
226+
}
227+
}
228+
229+
#[test]
230+
fn test_both_methods_agree() {
231+
let test_addresses = vec![
232+
"192.168.0.1",
233+
"10.0.0.255",
234+
"172.16.254.1",
235+
"8.8.8.8",
236+
"255.255.255.255",
237+
"0.0.0.0",
238+
];
239+
240+
for addr in test_addresses {
241+
let result1 = ipv4_to_decimal(addr).unwrap();
242+
let result2 = alt_ipv4_to_decimal(addr).unwrap();
243+
assert_eq!(result1, result2, "Methods disagree for address: {addr}");
244+
}
245+
}
246+
247+
#[test]
248+
fn test_edge_cases() {
249+
// All zeros
250+
assert_eq!(ipv4_to_decimal("0.0.0.0").unwrap(), 0);
251+
assert_eq!(decimal_to_ipv4(0).unwrap(), "0.0.0.0");
252+
253+
// All 255s (max value)
254+
assert_eq!(ipv4_to_decimal("255.255.255.255").unwrap(), 4294967295);
255+
assert_eq!(decimal_to_ipv4(4294967295).unwrap(), "255.255.255.255");
256+
257+
// Common private ranges
258+
assert_eq!(ipv4_to_decimal("10.0.0.0").unwrap(), 167772160);
259+
assert_eq!(ipv4_to_decimal("172.16.0.0").unwrap(), 2886729728);
260+
assert_eq!(ipv4_to_decimal("192.168.0.0").unwrap(), 3232235520);
261+
}
262+
}

src/conversions/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod decimal_to_octal;
77
mod hexadecimal_to_binary;
88
mod hexadecimal_to_decimal;
99
mod hexadecimal_to_octal;
10+
mod ipv4_conversion;
1011
mod length_conversion;
1112
mod octal_to_binary;
1213
mod octal_to_decimal;
@@ -26,6 +27,7 @@ pub use self::decimal_to_octal::decimal_to_octal;
2627
pub use self::hexadecimal_to_binary::hexadecimal_to_binary;
2728
pub use self::hexadecimal_to_decimal::hexadecimal_to_decimal;
2829
pub use self::hexadecimal_to_octal::hexadecimal_to_octal;
30+
pub use self::ipv4_conversion::{alt_ipv4_to_decimal, decimal_to_ipv4, ipv4_to_decimal, Ipv4Error};
2931
pub use self::length_conversion::length_conversion;
3032
pub use self::octal_to_binary::octal_to_binary;
3133
pub use self::octal_to_decimal::octal_to_decimal;

0 commit comments

Comments
 (0)