cidr_block 0.1.0

A library for CIDR block manipulation in Rust which provides utilities to parse, validate, and manipulate CIDR notation and get individual ip addresses.
Documentation
# CIDR Block

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

A Rust library for CIDR block manipulation that provides utilities to parse, validate, and manipulate CIDR notation and get individual IP addresses.

## Features

- Calculate the number of IP addresses in IPv4 and IPv6 CIDR blocks
- Generate the next IP address within a CIDR block with wrapping behavior
- Support for both IPv4 and IPv6 address families
- Trait-based extensions for IP address types
- Error handling for invalid CIDR blocks and prefix lengths

## Installation

Add this to your `Cargo.toml`:

```toml
[dependencies]
cidr_block = "0.1.0"
```

Or use the cargo command:

```bash
cargo add cidr_block
```

## Usage Examples

### IPv4 Examples

#### Calculate the number of IP addresses in a CIDR block

```rust
use cidr_block::v4_num_ips;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Calculate how many IP addresses are in a /24 network
    let num_ips = v4_num_ips("192.168.1.0/24")?;
    assert_eq!(num_ips, 256); // A /24 network has 256 IP addresses

    // Calculate how many IP addresses are in a /16 network
    let num_ips = v4_num_ips("10.0.0.0/16")?;
    assert_eq!(num_ips, 65536); // A /16 network has 65536 IP addresses
    
    Ok(())
}
```

#### Get the next IP address in a CIDR block

```rust
use cidr_block::next_ip_v4;
use std::net::Ipv4Addr;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Parse an IP address
    let ip: Ipv4Addr = "192.168.1.1".parse()?;
    
    // Get the next IP address in the /24 network (increment by 1)
    let next_ip = next_ip_v4(ip, 24, 1)?;
    assert_eq!(next_ip, "192.168.1.2".parse::<Ipv4Addr>()?);
    
    // Skip 10 IP addresses
    let next_ip = next_ip_v4(ip, 24, 10)?;
    assert_eq!(next_ip, "192.168.1.11".parse::<Ipv4Addr>()?);
    
    // Demonstrate wrapping behavior at the end of the CIDR block
    let last_ip: Ipv4Addr = "192.168.1.255".parse()?;
    let next_ip = next_ip_v4(last_ip, 24, 1)?;
    assert_eq!(next_ip, "192.168.1.0".parse::<Ipv4Addr>()?);
    
    Ok(())
}
```

#### Using the trait-based API

```rust
use cidr_block::IPExt;
use std::net::Ipv4Addr;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Calculate number of IPs using the trait method
    let num_ips = Ipv4Addr::num_ips("192.168.1.0/24")?;
    assert_eq!(num_ips, 256);
    
    // Get the next IP using the trait method
    let ip: Ipv4Addr = "192.168.1.1".parse()?;
    let next_ip = ip.next_ip(24, 1)?;
    assert_eq!(next_ip, "192.168.1.2".parse::<Ipv4Addr>()?);
    
    Ok(())
}
```

### IPv6 Examples

#### Calculate the number of IP addresses in a CIDR block

```rust
use cidr_block::v6_num_ips;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Calculate how many IP addresses are in a /64 network
    let num_ips = v6_num_ips("2001:db8::/64")?;
    assert_eq!(num_ips, 18_446_744_073_709_551_616u128); // 2^64 addresses
    
    // Calculate how many IP addresses are in a /128 network (just one)
    let num_ips = v6_num_ips("2001:db8::1/128")?;
    assert_eq!(num_ips, 1);
    
    Ok(())
}
```

#### Get the next IP address in a CIDR block

```rust
use cidr_block::next_ip_v6;
use std::net::Ipv6Addr;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Parse an IPv6 address
    let ip: Ipv6Addr = "2001:db8::1".parse()?;
    
    // Get the next IP address in the /64 network (increment by 1)
    let next_ip = next_ip_v6(ip, 64, 1)?;
    assert_eq!(next_ip, "2001:db8::2".parse::<Ipv6Addr>()?);
    
    // Skip 10 IP addresses
    let next_ip = next_ip_v6(ip, 64, 10)?;
    assert_eq!(next_ip, "2001:db8::b".parse::<Ipv6Addr>()?);
    
    // Demonstrate wrapping behavior with large increments
    let ip: Ipv6Addr = "2001:db8::ffff".parse()?;
    let next_ip = next_ip_v6(ip, 64, 2)?;
    assert_eq!(next_ip, "2001:db8::1".parse::<Ipv6Addr>()?);
    
    Ok(())
}
```

#### Using the trait-based API

```rust
use cidr_block::IPExt;
use std::net::Ipv6Addr;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Calculate number of IPs using the trait method
    let num_ips = Ipv6Addr::num_ips("2001:db8::/64")?;
    
    // Get the next IP using the trait method
    let ip: Ipv6Addr = "2001:db8::1".parse()?;
    let next_ip = ip.next_ip(64, 1)?;
    assert_eq!(next_ip, "2001:db8::2".parse::<Ipv6Addr>()?);
    
    Ok(())
}
```

## API Reference

### IPv4 Functions

#### `v4_num_ips(block: &str) -> Result<u32, Error>`

Calculates the number of IP addresses in an IPv4 CIDR block.

- **Parameters**:
  - `block`: A string slice that represents the IPv4 CIDR block (e.g., "192.168.0.0/24").
- **Returns**:
  - `Ok(u32)`: The number of IP addresses in the block.
  - `Err(Error)`: If the block format is invalid or if the prefix length is out of range.

#### `next_ip_v4(ip: Ipv4Addr, prefix_length: u8, n: u32) -> Result<Ipv4Addr, Error>`

Calculates the next IPv4 address within the same CIDR block, wrapping around if necessary.

- **Parameters**:
  - `ip`: An `Ipv4Addr` that represents the starting IPv4 address.
  - `prefix_length`: The prefix length of the CIDR block (e.g., 24 for a /24 network).
  - `n`: The number of addresses to skip.
- **Returns**:
  - `Ok(Ipv4Addr)`: The next IPv4 address within the CIDR block.
  - `Err(Error)`: If the prefix length is invalid (greater than 32).

### IPv6 Functions

#### `v6_num_ips(block: &str) -> Result<u128, Error>`

Calculates the number of IP addresses in an IPv6 CIDR block.

- **Parameters**:
  - `block`: A string slice that represents the IPv6 CIDR block (e.g., "2001:db8::/64").
- **Returns**:
  - `Ok(u128)`: The number of IP addresses in the block.
  - `Err(Error)`: If the block format is invalid or if the prefix length is out of range.

#### `next_ip_v6(ip: Ipv6Addr, prefix_length: u8, n: u128) -> Result<Ipv6Addr, Error>`

Calculates the next IPv6 address within the same CIDR block, wrapping around if necessary.

- **Parameters**:
  - `ip`: An `Ipv6Addr` that represents the starting IPv6 address.
  - `prefix_length`: The prefix length of the CIDR block (e.g., 64 for a /64 network).
  - `n`: The number of addresses to skip.
- **Returns**:
  - `Ok(Ipv6Addr)`: The next IPv6 address within the CIDR block.
  - `Err(Error)`: If the prefix length is invalid (greater than 128).

### Trait-based API

#### `IPExt` Trait

The `IPExt` trait provides a unified interface for both IPv4 and IPv6 address types.

```rust
pub trait IPExt: Clone + Copy {
    type Error;
    type LenType;

    fn next_ip(&self, prefix_length: u8, n: Self::LenType) -> Result<Self, Self::Error>
    where
        Self: Sized;

    fn num_ips(block: &str) -> Result<Self::LenType, Self::Error>
    where
        Self: Sized;
}
```

This trait is implemented for both `Ipv4Addr` and `Ipv6Addr` types, allowing for a more ergonomic API when working with different IP address families.

### Error Types

#### `Error` Enum

```rust
pub enum Error {
    InvalidBlock,
    InvalidPrefixLength,
}
```

- **InvalidBlock**: Returned when the CIDR block format is invalid.
- **InvalidPrefixLength**: Returned when the prefix length is out of range (greater than 32 for IPv4 or greater than 128 for IPv6).

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.