147 Commits

Author SHA1 Message Date
ca2da8ae30 Uprev for a new release 2020-01-08 10:37:12 +00:00
156ae55da0 Merge pull request #116 from achanda/cleanup
Cleanup comment on IPv6 implementation
2020-01-08 10:35:57 +00:00
4dfdf142d0 Cleanup comment on IPv6 implementation 2020-01-08 10:29:58 +00:00
6851a0b272 Merge pull request #115 from achanda/maintenance
Set a maintenance status
2020-01-06 20:29:55 +00:00
00256cf885 Set a maintenance status
Putting this as passively-maintained since this crate is mostly feature
complete.
2020-01-05 13:27:46 +00:00
80c6794829 Merge pull request #114 from achanda/ipv6-netmask
Check against specific errors in parsing test
2020-01-05 13:00:30 +00:00
18677e3b69 Run cargo fmt 2020-01-05 11:38:28 +00:00
986ee0ba1c Check against specific errors in parsing test 2020-01-05 11:36:49 +00:00
f3ac9ba3fe Merge pull request #113 from achanda/bench-netmask
Add a benchmark for parsing IPv4 netmask
2020-01-05 11:20:31 +00:00
bc1064e5f0 Add a benchmark for parsing IPv4 netmask 2020-01-05 11:14:06 +00:00
ddc9ef2f7b Merge pull request #112 from vorner/netmask-parse
Be able to parse the netmask format
2020-01-04 10:19:35 +00:00
87ea7b24fa Be able to parse the netmask format
Allow accepting the netmask format, eg 192.168.1.0/255.255.255.0, in
addition to the more modern /24 prefix version.

Adding support for IPv4 only, as IPv6 never actually used that format in
practice.

Closes #107.
2019-12-21 16:19:20 +01:00
08126f5103 Merge pull request #109 from mullvad/optional-serde
Make the serde dependency optional
2019-12-13 16:23:08 +00:00
376081d253 Merge pull request #108 from vorner/iterator
Iterator
2019-12-13 16:22:50 +00:00
fa85cdb500 Test without features on CI 2019-12-11 12:51:39 +01:00
fa5deed40b Make the serde dependency optional 2019-12-11 12:51:39 +01:00
5b09d82d43 Fix bug with overflow in iterator 2019-12-11 09:40:55 +01:00
dbf366cfdf Iterators: reproducer for overflow on high end of iteration 2019-12-11 09:40:55 +01:00
6cbb011597 Iterators for the IpNetwork
Version-agnostic iterator in addition to the version specific ones.
2019-12-11 09:40:55 +01:00
f7d9524f70 Make the iterators slightly better
* Implement some useful traits on them.
* Make reference an into-iterator.
2019-12-11 09:40:55 +01:00
e1f8566301 Merge pull request #110 from mullvad/metadata-cleanup
Metadata cleanup
2019-12-10 12:16:02 +00:00
a3197a8411 Remove the "heavily WIP" part of description 2019-12-10 09:40:08 +01:00
0479ca45a9 Add and remove appropriate keywords and categories 2019-12-10 09:40:08 +01:00
11025d3b1a Remove documentation URL that is already default 2019-12-10 09:28:26 +01:00
43a38d4117 Merge pull request #103 from lucab/ups/parse-net-pair
ipnetwork: add netmask constructors
2019-11-28 13:52:55 +00:00
152f1a8393 ipnetwork: add netmask constructors
This adds constructors to build all network types from a network
address and network mask pair.
2019-11-27 17:20:59 +00:00
31fc25f9ce cargo: rustfmt whole project 2019-11-27 15:50:47 +00:00
bdaa8a7843 Merge pull request #101 from achanda/release-v0.15.1
Uprev for a new release
2019-11-23 13:54:22 +00:00
d54f463f86 Uprev for a new release 2019-11-23 13:48:22 +00:00
fb782086ce Merge pull request #100 from LukasKalbertodt/master
Use `slice::iter` instead of `into_iter` to avoid future breakage
2019-10-30 22:17:22 +00:00
26706b3790 Use slice::iter instead of into_iter to avoid future breakage
`an_array.into_iter()` currently just works because of the autoref
feature, which then calls `<[T] as IntoIterator>::into_iter`. But
in the future, arrays will implement `IntoIterator`, too. In order
to avoid problems in the future, the call is replaced by `iter()`
which is shorter and more explicit.
2019-10-30 15:32:11 +01:00
2eed2de227 Merge pull request #99 from achanda/action-config
Add a github action config
2019-10-12 16:28:31 +01:00
7395af2658 Show current git branch 2019-10-12 16:17:29 +01:00
fefd674721 Add a checkout step 2019-10-12 16:11:31 +01:00
20ed033826 Add a github action config 2019-10-09 22:56:14 +01:00
7af6e16f98 Merge pull request #98 from achanda/release-0.15
Uprev for a new release
2019-09-12 20:34:06 +01:00
9e42aaeb3e Uprev for a new release 2019-09-12 20:25:48 +01:00
2b34ba991a Merge pull request #96 from achanda/dependabot/cargo/criterion-0.3.0
Update criterion requirement from 0.2.9 to 0.3.0
2019-08-26 08:25:21 +01:00
2dff6a0863 Update criterion requirement from 0.2.9 to 0.3.0
Updates the requirements on [criterion](https://github.com/bheisler/criterion.rs) to permit the latest version.
- [Release notes](https://github.com/bheisler/criterion.rs/releases)
- [Changelog](https://github.com/bheisler/criterion.rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bheisler/criterion.rs/compare/0.2.9...0.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-08-26 05:35:47 +00:00
a45e76c92f Merge pull request #95 from faern/upgrade-to-rust2018
Upgrade to Rust 2018 edition
2019-03-25 16:57:22 +00:00
a6dfadc21d Manual import cleanup 2019-03-25 15:24:17 +01:00
61eeb5843f Run cargo fix --edition-idioms 2019-03-25 15:15:41 +01:00
d8f3cc1992 Upgrade to edition 2018 2019-03-25 15:15:21 +01:00
c2d4b9aeae Run cargo fix --edition 2019-03-25 15:13:50 +01:00
3479b6f272 Merge pull request #94 from mullvad/fix-deserialization
Deserialize from String rather &str for Ipv4Network and Ipv6Network
2019-02-20 16:49:28 +01:00
10d12cd945 Deserialize from String rather &str for ipnetwork:Ipv{4,6}Network 2019-02-20 11:07:29 +00:00
47a539f2f1 Merge pull request #93 from achanda/new-methods
Implement is_subnet_of, is_supernet_of and overlaps
2019-02-07 18:43:47 +01:00
e7c85b5b81 Implement overlaps for both types 2019-02-06 23:29:46 +00:00
439a0deeb7 Implement is_subnet_of and is_supernet_of 2019-02-06 23:14:07 +00:00
b2c458e6f1 Merge pull request #91 from achanda/contains-bench
Track benchmark for contains
2019-02-03 01:10:09 +01:00
1b06452e88 Optimise contains for IPv4 2019-02-02 23:57:25 +00:00
2b9936fa8e Track benchmark for contains 2019-02-02 23:36:08 +00:00
553fa0ba1c Merge pull request #90 from achanda/parse-bench
Add a benchmark for CIDR parsing
2019-02-03 00:22:15 +01:00
a19f24a9c6 Add a benchmark for CIDR parsing
Also optimise the cidr_parts implementation
2019-02-02 21:54:16 +00:00
aff0419a75 Merge pull request #89 from achanda/release-0.14.0
Prepare new release
2019-02-01 23:22:41 +01:00
1f96439a87 Prepare new release 2019-02-01 22:16:38 +00:00
25d7dc19d1 Merge pull request #88 from mullvad/fix-deserialization
Added custom deserialization and serialization for ipnetwork::IpNetwork
2019-02-01 23:11:31 +01:00
d8ce2e4dbc Added custom deserialization and serialization for ipnetwork::IpNetwork 2019-02-01 12:01:33 +00:00
16c4af9823 Merge pull request #86 from achanda/dependabot/cargo/clippy-0.0.302
Update clippy requirement from 0.0.104 to 0.0.302
2018-12-08 20:39:54 +00:00
72677fbe8f Update clippy requirement from 0.0.104 to 0.0.302
Updates the requirements on [clippy](https://github.com/rust-lang-nursery/rust-clippy) to permit the latest version.
- [Release notes](https://github.com/rust-lang-nursery/rust-clippy/releases)
- [Changelog](https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang-nursery/rust-clippy/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-12-08 20:29:20 +00:00
35331cf7ea Merge pull request #85 from achanda/from-casts
Use from casts in some more places
2018-11-26 11:28:55 +00:00
1d57287c77 Use from casts in some more places 2018-11-26 16:50:33 +05:30
ddec283819 Merge pull request #84 from achanda/change-type
Change return type of Ipv4Network::size to u32
2018-11-24 04:31:13 +00:00
2b21f38171 Change return type of Ipv4Network::size to u32
Also, generalize size over IpvNetwork
NOTE: this is a breaking change
2018-11-21 15:48:29 +00:00
2bd3db84a8 Merge pull request #83 from achanda/network-toplevel
Export network and broadcast for IpNetwork
2018-11-10 21:10:48 +00:00
a8edccafa1 Export network and broadcast for IpNetwork
Adopted from https://github.com/achanda/ipnetwork/pull/78
2018-11-10 21:04:41 +00:00
9ab988715e Merge pull request #81 from achanda/release-0.13.1
Release version 0.13.1
2018-08-31 23:56:27 +01:00
1f3d42d89f Release 0.13.1 2018-08-31 23:27:53 +01:00
e0a9afcb36 Merge pull request #79 from sharksforarms/issue-#62
Parse Ipv4Addr using Ipv4Addr::from_str
2018-05-30 19:01:38 +02:00
5e7eb0bd1b Parse IpAddr using Ipv4Addr::from_str 2018-05-27 19:26:11 -05:00
d5e6b11170 Merge pull request #77 from achanda/cleanup
Remove features from cargo manifest
2018-05-12 15:23:29 +02:00
a406474bea Remove features from cargo manifest 2018-05-12 14:12:42 +01:00
b762304ee4 Merge pull request #76 from achanda/release_0.13
Uprev for release
2018-05-12 15:11:41 +02:00
9ac9df11e2 Uprev for release 2018-05-12 14:04:16 +01:00
98946a8fa9 Merge pull request #75 from achanda/stable-128
Remove the feature flag for 128 bit integers
2018-05-12 15:01:31 +02:00
bbcc53cd94 Remove the feature flag for 128 bit integers
Since it is stable now
2018-05-12 13:53:59 +01:00
36ebedf346 Merge pull request #74 from sharksforarms/serde-issue-73
Serde serialize/deserialize
2018-04-18 23:53:24 +02:00
2286d58728 cargo fmt and clippy 2018-04-17 19:28:45 -05:00
c6ead9d654 remove serde feature flag 2018-04-17 19:28:38 -05:00
de707e35ae Remove with-serde in favor of just 'serde'
https://rust-lang-nursery.github.io/api-guidelines/naming.html#feature-names-are-free-of-placeholder-words-c-feature
2018-04-17 17:03:42 -05:00
4e97146605 No need to implement serialize/deserialize for enum, use serde(untagged) 2018-04-17 06:30:44 -05:00
4414ae4f0f direct serialize/deserialize from string/to string 2018-04-16 19:25:22 -05:00
ac3b11b1d7 ignore .idea/ folder 2018-04-16 19:22:54 -05:00
6b4dc9762b pin version 2018-04-16 19:22:33 -05:00
15fa093618 cargo fmt 2018-04-15 21:15:36 -05:00
811cab642e vector of ip networks 2018-04-15 21:11:01 -05:00
ce26663b1f Add Ipv6 test case 2018-04-15 21:04:13 -05:00
7f684cf06e WARNING: src/lib.rs - IpNetwork::mask (line 83) Code block is not currently run as a test, but will in future versions of rustdoc. Please ensure this code block is a runnable test, or use the ignore directive. 2018-04-15 21:04:00 -05:00
e075fb80f8 Add IPv4 test case 2018-04-15 20:58:52 -05:00
47d8f0d89e Merge pull request #72 from sharksforarms/master
* Default to /32 for an IP
2018-04-16 00:40:14 +02:00
72326bd96f use an option 2018-03-27 21:58:10 -05:00
411c71e900 cargo fmt 2018-03-27 21:48:34 -05:00
cc24cceb1a add test cases 2018-03-27 21:48:29 -05:00
fcdb0c6b87 simpler way of doing it 2018-03-27 21:39:15 -05:00
5fabbc91d5 If there is no prefix, default to number of bits ipv4/32, ipv6/128 2018-03-27 21:36:39 -05:00
82fef85a02 Doc tests 2018-03-27 20:34:12 -05:00
746342c0af Default to /32 for an IP 2018-03-27 20:34:07 -05:00
c0143a2362 Merge pull request #70 from achanda/release_0.12.8
Uprev for release
2018-03-15 08:51:54 +00:00
eddcc026cb Uprev for release 2018-03-15 08:45:16 +00:00
78dc628196 Merge pull request #69 from admwrd/add_serde_feature
Add Serde Feature
2018-03-15 08:41:11 +00:00
c9e25e15fa Add Serde Feature for IpNetwork 2018-03-14 19:00:38 -07:00
3703488473 Merge pull request #68 from tailhook/generic_contains
Add `IpNetwork::contains`
2018-03-05 14:59:27 +00:00
42eed8dbb5 Add IpNetwork::contains 2018-03-05 16:49:32 +02:00
dc0cc2d5a1 Merge pull request #65 from achanda/fmt
Another round of formatting
2017-12-21 17:48:48 +00:00
a3528a8f43 Another round of formatting 2017-12-21 17:45:45 +00:00
226bd7ec00 Merge pull request #64 from achanda/clippy_run
Readability improvements as suggested by clippy
2017-11-08 19:17:17 +00:00
023406419b Readability improvements as suggested by clippy 2017-11-08 16:59:56 +00:00
bafba6ed93 Merge pull request #63 from tshepang/multiple-slashes
avoid misleading message
2017-10-09 11:03:26 +01:00
124330be88 avoid misleading message
Closes #61
2017-10-09 11:59:48 +02:00
5f5d985e7d Merge pull request #60 from achanda/send_sync
Add two tests for send and sync
2017-09-22 18:02:19 +01:00
8ee7f2d1ed Add two tests for send and sync
To prevent future regressions as the library evolves
2017-09-22 17:56:43 +01:00
384f86d590 Merge pull request #58 from achanda/release_0.12.7
Uprev for new release
2017-09-07 18:34:25 +01:00
bfafdc2c2f Uprev for new release 2017-09-07 18:29:18 +01:00
14e28232d7 Merge pull request #57 from achanda/badge
Add travis badge
2017-09-07 18:27:50 +01:00
6e64322655 Add travis badge 2017-09-07 18:24:34 +01:00
d876a9d58a Merge pull request #56 from nrdmn/master
add From<IpAddr> for IpNetwork
2017-08-22 22:27:46 +01:00
9db3d5c30d add From<IpAddr> for IpNetwork 2017-08-22 15:15:18 +00:00
0a99bf969c Merge pull request #55 from bgermann/master
Fix annoying typo
2017-08-14 17:07:41 +01:00
a28098444e Fix annoying typo
prifex -> prefix
2017-08-14 18:05:22 +02:00
f7c963258b Merge pull request #54 from achanda/docs
Add a html_root_url attribute
2017-07-30 12:45:01 +01:00
482aed66e7 Add a html_root_url attribute 2017-07-30 12:38:13 +01:00
d56ab96c53 Merge pull request #53 from achanda/vscode
Ignore vscode
2017-07-30 12:36:18 +01:00
2d9b2d27cb Ignore vscode 2017-07-30 12:30:14 +01:00
1abf10d52b Merge pull request #51 from achanda/release_0.12.6
Uprev for release
2017-07-11 21:59:28 +01:00
f075e0de38 Remove the push to rust-ci
docs.rs takes care of this now
2017-07-11 21:54:41 +01:00
3d020d8b2a Test ipv6 methods in CI 2017-07-11 21:50:25 +01:00
4d2d9a2dd6 Uprev for release 2017-07-11 21:49:06 +01:00
f8aec99e90 Merge pull request #50 from achanda/v6_methods
Add a bunch of new methods for Ipv6Network
2017-07-11 21:46:45 +01:00
910a52b64b Add a size method for Ipv6Network 2017-07-11 21:21:00 +01:00
2ee819a438 Add a broadcast method for Ipv6Network 2017-07-11 21:11:39 +01:00
1171e5ae94 Add a network method for Ipv6Network 2017-07-11 21:01:41 +01:00
99203160b9 Add an iterator for ipv6 network. (#49)
This is feature gated since i128 is not stable yet
2017-07-08 20:28:00 +01:00
fb8f735741 Merge pull request #48 from achanda/categories
Add categories
2017-06-07 11:23:07 +01:00
d7b2a14caa Add categories 2017-06-07 11:21:47 +01:00
fd452cd156 Merge pull request #47 from achanda/v0.12.4
Uprev for release
2017-05-16 11:49:15 +01:00
f0fd68e962 Uprev for release 2017-05-16 11:46:09 +01:00
ae784fe161 Merge pull request #46 from greydot/ord
Add PartialOrd and Ord instances for network types.
2017-05-16 11:42:51 +01:00
ab7d5b32d0 Add PartialOrd and Ord instances for network types. 2017-05-16 10:34:11 +00:00
cdd3dff549 Merge pull request #44 from achanda/release
Uprev for release
2017-04-13 16:41:36 +01:00
ef378f9921 Uprev for release 2017-04-13 16:40:13 +01:00
b70b551561 Merge pull request #43 from faern/fix-doc-test
Fix failing doc test
2017-04-03 09:22:18 +01:00
b442a7d598 Merge pull request #42 from faern/simplify-from-str
Simplify IpNetwork::from_str impl and update docs
2017-04-03 09:22:06 +01:00
5020a99401 Add punctuation 2017-04-02 18:56:25 +02:00
24c9a24dae Fix failing doc test 2017-04-02 17:51:30 +02:00
b3fd9f4f48 Simplify IpNetwork::from_str impl and update docs 2017-04-02 17:50:20 +02:00
04d454b5ba Merge pull request #41 from faern/impl-display
Implement Display for IpNetwork
2017-04-02 16:47:48 +01:00
f61db46006 Implement Display for IpNetwork 2017-04-02 17:30:06 +02:00
10 changed files with 1146 additions and 157 deletions

28
.github/workflows/rust.yml vendored Normal file
View File

@ -0,0 +1,28 @@
name: Rust
on:
pull_request:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
rust: [stable, beta, nightly]
steps:
- uses: hecrj/setup-rust-action@v1
with:
rust-version: ${{ matrix.rust }}
- uses: actions/checkout@master
with:
ref: ${{ github.ref }}
- name: Show current git branch
run: git branch
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
- name: Build docs
run: cargo doc --verbose

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
.gitignore
Cargo.lock
target
.vscode
.idea/

View File

@ -6,12 +6,12 @@ rust:
env:
global:
- secure: gokQ7xIWwmAuEUW3IyS5B/pbZxdFSSDBto5beJ+4ACGcRMDqJ/eCPf1ekSVXME4TWM46uUCjxcdUjYhIhQ6sG4zfWck4u45qRJ5JbIoTvR+ykxhN1j3Zi5x9ptP3ALDbHn2i3v6t9xohORfQpz3dVND5c7thbYDyKP2ZR1sez5c=
- FEATURES: default
script:
- cargo build --verbose --no-default-features
- cargo test --verbose --no-default-features
- cargo build --verbose
- cargo test --verbose
- cargo build --release --verbose
- cargo doc --verbose
after_script:
- curl http://www.rust-ci.org/artifacts/put?t=$RUSTCI_TOKEN | sh

View File

@ -1,17 +1,32 @@
[package]
name = "ipnetwork"
version = "0.12.2"
version = "0.16.0" # When updating version, also modify html_root_url in the lib.rs
authors = ["Abhishek Chanda <abhishek.becs@gmail.com>", "Linus Färnstrand <faern@faern.net>"]
description = "A library to work with IP CIDRs in Rust, heavily WIP"
description = "A library to work with IP CIDRs in Rust"
license = "Apache-2.0"
repository = "https://github.com/achanda/ipnetwork"
keywords = ["network", "ip", "address"]
keywords = ["network", "ip", "address", "cidr"]
readme = "README.md"
documentation = "https://docs.rs/ipnetwork/"
categories = ["network-programming", "parser-implementations"]
edition = "2018"
[dependencies]
clippy = {version = "0.0.104", optional = true}
clippy = {version = "0.0.302", optional = true}
serde = { version = ">=0.8.0, <2.0", optional = true }
[dev-dependencies]
serde_json = "1.0"
serde_derive = ">=0.8.0, <2.0"
criterion = "0.3.0"
[badges]
travis-ci = { repository = "achanda/ipnetwork" }
maintenance = { status = "passively-maintained" }
[features]
default = []
default = ["serde"]
dev = ["clippy"]
[[bench]]
name = "parse_bench"
harness = false

49
benches/parse_bench.rs Normal file
View File

@ -0,0 +1,49 @@
use criterion::{criterion_group, criterion_main, Criterion};
use ipnetwork::{Ipv4Network, Ipv6Network};
use std::net::{Ipv4Addr, Ipv6Addr};
fn parse_ipv4_prefix_benchmark(c: &mut Criterion) {
c.bench_function("parse ipv4 prefix", |b| {
b.iter(|| "127.1.0.0/24".parse::<Ipv4Network>().unwrap())
});
}
fn parse_ipv6_benchmark(c: &mut Criterion) {
c.bench_function("parse ipv6", |b| {
b.iter(|| "FF01:0:0:17:0:0:0:2/64".parse::<Ipv6Network>().unwrap())
});
}
fn parse_ipv4_netmask_benchmark(c: &mut Criterion) {
c.bench_function("parse ipv4 netmask", |b| {
b.iter(|| "127.1.0.0/255.255.255.0".parse::<Ipv4Network>().unwrap())
});
}
fn contains_ipv4_benchmark(c: &mut Criterion) {
c.bench_function("contains ipv4", |b| {
b.iter(|| {
let cidr = "74.125.227.0/25".parse::<Ipv4Network>().unwrap();
cidr.contains(Ipv4Addr::new(74, 125, 227, 4))
})
});
}
fn contains_ipv6_benchmark(c: &mut Criterion) {
c.bench_function("contains ipv6", |b| {
b.iter(|| {
let cidr = "FF01:0:0:17:0:0:0:2/65".parse::<Ipv6Network>().unwrap();
cidr.contains(Ipv6Addr::new(0xff01, 0, 0, 0x17, 0x7fff, 0, 0, 0x2))
})
});
}
criterion_group!(
benches,
parse_ipv4_prefix_benchmark,
parse_ipv6_benchmark,
parse_ipv4_netmask_benchmark,
contains_ipv4_benchmark,
contains_ipv6_benchmark
);
criterion_main!(benches);

View File

@ -1,9 +1,7 @@
use std::net::Ipv4Addr;
use std::fmt;
use std::error::Error;
use std::{error::Error, fmt};
/// Represents a bunch of errors that can occur while working with a `IpNetwork`
#[derive(Debug,Clone,PartialEq,Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IpNetworkError {
InvalidAddr(String),
InvalidPrefix,
@ -11,11 +9,11 @@ pub enum IpNetworkError {
}
impl fmt::Display for IpNetworkError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use IpNetworkError::*;
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use crate::IpNetworkError::*;
match *self {
InvalidAddr(ref s) => write!(f, "invalid address: {}", s),
InvalidPrefix => write!(f, "invalid prifex"),
InvalidPrefix => write!(f, "invalid prefix"),
InvalidCidrFormat(ref s) => write!(f, "invalid cidr format: {}", s),
}
}
@ -23,44 +21,42 @@ impl fmt::Display for IpNetworkError {
impl Error for IpNetworkError {
fn description(&self) -> &str {
use IpNetworkError::*;
use crate::IpNetworkError::*;
match *self {
InvalidAddr(_) => "address is invalid",
InvalidPrefix => "prefix is invalid",
InvalidCidrFormat(_) => "cidr is invalid",
}
}
}
pub fn cidr_parts(cidr: &str) -> Result<(&str, &str), IpNetworkError> {
let parts = cidr.split('/').collect::<Vec<&str>>();
if parts.len() == 2 {
Ok((parts[0], parts[1]))
pub fn cidr_parts(cidr: &str) -> Result<(&str, Option<&str>), IpNetworkError> {
// Try to find a single slash
if let Some(sep) = cidr.find('/') {
let (ip, prefix) = cidr.split_at(sep);
// Error if cidr has multiple slashes
if prefix[1..].find('/').is_some() {
return Err(IpNetworkError::InvalidCidrFormat(format!(
"CIDR must contain a single '/': {}",
cidr
)));
} else {
// Handle the case when cidr has exactly one slash
return Ok((ip, Some(&prefix[1..])));
}
} else {
Err(IpNetworkError::InvalidCidrFormat(format!("CIDR must contain '/': {}", cidr)))
// Handle the case when cidr does not have a slash
return Ok((cidr, None));
}
}
pub fn parse_prefix(prefix: &str, max: u8) -> Result<u8, IpNetworkError> {
let mask = prefix.parse::<u8>().map_err(|_| IpNetworkError::InvalidPrefix)?;
let mask = prefix
.parse::<u8>()
.map_err(|_| IpNetworkError::InvalidPrefix)?;
if mask > max {
Err(IpNetworkError::InvalidPrefix)
} else {
Ok(mask)
}
}
pub fn parse_addr(addr: &str) -> Result<Ipv4Addr, IpNetworkError> {
let addr_parts = addr.split('.').map(|b| b.parse::<u8>());
let mut bytes = [0; 4];
for (i, byte) in addr_parts.enumerate() {
if i >= 4 {
return Err(IpNetworkError::InvalidAddr(format!("More than 4 bytes: {}", addr)));
}
bytes[i] = byte.map_err(|_| {
IpNetworkError::InvalidAddr(format!("All bytes not 0-255: {}", addr))
})?;
}
Ok(Ipv4Addr::new(bytes[0], bytes[1], bytes[2], bytes[3]))
}

View File

@ -1,41 +1,72 @@
use std::fmt;
use std::net::Ipv4Addr;
use std::str::FromStr;
use common::{IpNetworkError, cidr_parts, parse_prefix, parse_addr};
use crate::common::{cidr_parts, parse_prefix, IpNetworkError};
use std::{fmt, net::Ipv4Addr, str::FromStr};
const IPV4_BITS: u8 = 32;
/// Represents a network range where the IP addresses are of v4
#[derive(Debug,Clone,Copy,Hash,PartialEq,Eq)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Ipv4Network {
addr: Ipv4Addr,
prefix: u8,
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Ipv4Network {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = <String>::deserialize(deserializer)?;
Ipv4Network::from_str(&s).map_err(serde::de::Error::custom)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Ipv4Network {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl Ipv4Network {
/// Constructs a new `Ipv4Network` from any `Ipv4Addr` and a prefix denoting the network size.
///
/// If the prefix is larger than 32 this will return an `IpNetworkError::InvalidPrefix`.
pub fn new(addr: Ipv4Addr, prefix: u8) -> Result<Ipv4Network, IpNetworkError> {
if prefix > IPV4_BITS {
Err(IpNetworkError::InvalidPrefix)
} else {
Ok(Ipv4Network {
addr: addr,
prefix: prefix,
})
Ok(Ipv4Network { addr, prefix })
}
}
/// Constructs a new `Ipv4Network` from a network address and a network mask.
///
/// If the netmask is not valid this will return an `IpNetworkError::InvalidPrefix`.
pub fn with_netmask(
netaddr: Ipv4Addr,
netmask: Ipv4Addr,
) -> Result<Ipv4Network, IpNetworkError> {
let prefix = ipv4_mask_to_prefix(netmask)?;
let net = Self {
addr: netaddr,
prefix,
};
Ok(net)
}
/// Returns an iterator over `Ipv4Network`. Each call to `next` will return the next
/// `Ipv4Addr` in the given network. `None` will be returned when there are no more
/// addresses.
pub fn iter(&self) -> Ipv4NetworkIterator {
let start = u32::from(self.network()) as u64;
let end = start + self.size();
let start = u32::from(self.network());
let end = start + (self.size() - 1);
Ipv4NetworkIterator {
next: start,
end: end,
next: Some(start),
end,
}
}
@ -47,6 +78,23 @@ impl Ipv4Network {
self.prefix
}
/// Checks if the given `Ipv4Network` is a subnet of the other.
pub fn is_subnet_of(self, other: Ipv4Network) -> bool {
other.ip() <= self.ip() && other.broadcast() >= self.broadcast()
}
/// Checks if the given `Ipv4Network` is a supernet of the other.
pub fn is_supernet_of(self, other: Ipv4Network) -> bool {
other.is_subnet_of(self)
}
/// Checks if the given `Ipv4Network` is partly contained in other.
pub fn overlaps(self, other: Ipv4Network) -> bool {
other.contains(self.ip())
|| (other.contains(self.broadcast())
|| (self.contains(other.ip()) || (self.contains(other.broadcast()))))
}
/// Returns the mask for this `Ipv4Network`.
/// That means the `prefix` most significant bits will be 1 and the rest 0
///
@ -56,12 +104,14 @@ impl Ipv4Network {
/// use std::net::Ipv4Addr;
/// use ipnetwork::Ipv4Network;
///
/// let net: Ipv4Network = "127.0.0.0".parse().unwrap();
/// assert_eq!(net.mask(), Ipv4Addr::new(255, 255, 255, 255));
/// let net: Ipv4Network = "127.0.0.0/16".parse().unwrap();
/// assert_eq!(net.mask(), Ipv4Addr::new(255, 255, 0, 0));
/// ```
pub fn mask(&self) -> Ipv4Addr {
let prefix = self.prefix;
let mask = !(0xffffffff as u64 >> prefix) as u32;
let mask = !(0xffff_ffff as u64 >> prefix) as u32;
Ipv4Addr::from(mask)
}
@ -114,8 +164,8 @@ impl Ipv4Network {
/// assert!(!net.contains(Ipv4Addr::new(127, 0, 1, 70)));
/// ```
pub fn contains(&self, ip: Ipv4Addr) -> bool {
let net = u32::from(self.network());
let mask = u32::from(self.mask());
let mask = !(0xffff_ffff as u64 >> self.prefix) as u32;
let net = u32::from(self.addr) & mask;
(u32::from(ip) & mask) == net
}
@ -133,9 +183,9 @@ impl Ipv4Network {
/// let tinynet: Ipv4Network = "0.0.0.0/32".parse().unwrap();
/// assert_eq!(tinynet.size(), 1);
/// ```
pub fn size(&self) -> u64 {
let host_bits = (IPV4_BITS - self.prefix) as u32;
(2 as u64).pow(host_bits)
pub fn size(&self) -> u32 {
let host_bits = u32::from(IPV4_BITS - self.prefix);
(2 as u32).pow(host_bits)
}
/// Returns the `n`:th address within this network.
@ -156,7 +206,7 @@ impl Ipv4Network {
/// assert_eq!(net2.nth(256).unwrap(), Ipv4Addr::new(10, 0, 1, 0));
/// ```
pub fn nth(&self, n: u32) -> Option<Ipv4Addr> {
if (n as u64) < self.size() {
if n < self.size() {
let net = u32::from(self.network());
Some(Ipv4Addr::from(net + n))
} else {
@ -166,12 +216,11 @@ impl Ipv4Network {
}
impl fmt::Display for Ipv4Network {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "{}/{}", self.ip(), self.prefix())
}
}
/// Creates an `Ipv4Network` from parsing a string in CIDR notation.
///
/// # Examples
@ -189,38 +238,67 @@ impl FromStr for Ipv4Network {
type Err = IpNetworkError;
fn from_str(s: &str) -> Result<Ipv4Network, IpNetworkError> {
let (addr_str, prefix_str) = cidr_parts(s)?;
let addr = parse_addr(addr_str)?;
let prefix = parse_prefix(prefix_str, IPV4_BITS)?;
let addr = Ipv4Addr::from_str(addr_str)
.map_err(|_| IpNetworkError::InvalidAddr(addr_str.to_string()))?;
let prefix = match prefix_str {
Some(v) => {
if let Ok(netmask) = Ipv4Addr::from_str(v) {
ipv4_mask_to_prefix(netmask)?
} else {
parse_prefix(v, IPV4_BITS)?
}
}
None => IPV4_BITS,
};
Ipv4Network::new(addr, prefix)
}
}
impl From<Ipv4Addr> for Ipv4Network {
fn from(a: Ipv4Addr) -> Ipv4Network {
Ipv4Network {
addr: a,
prefix: 32,
}
}
}
#[derive(Clone, Debug)]
pub struct Ipv4NetworkIterator {
next: u64,
end: u64,
next: Option<u32>,
end: u32,
}
impl Iterator for Ipv4NetworkIterator {
type Item = Ipv4Addr;
fn next(&mut self) -> Option<Ipv4Addr> {
if self.next < self.end {
let next = Ipv4Addr::from(self.next as u32);
self.next += 1;
Some(next)
} else {
let next = self.next?;
self.next = if next == self.end {
None
}
} else {
Some(next + 1)
};
Some(next.into())
}
}
impl IntoIterator for &'_ Ipv4Network {
type IntoIter = Ipv4NetworkIterator;
type Item = Ipv4Addr;
fn into_iter(self) -> Ipv4NetworkIterator {
self.iter()
}
}
/// Converts a `Ipv4Addr` network mask into a prefix.
///
/// If the mask is invalid this will return an `IpNetworkError::InvalidPrefix`.
pub fn ipv4_mask_to_prefix(mask: Ipv4Addr) -> Result<u8, IpNetworkError> {
let mask = u32::from(mask);
let prefix = (!mask).leading_zeros() as u8;
if ((mask as u64) << prefix) & 0xffffffff != 0 {
if (u64::from(mask) << prefix) & 0xffff_ffff != 0 {
Err(IpNetworkError::InvalidPrefix)
} else {
Ok(prefix)
@ -229,10 +307,10 @@ pub fn ipv4_mask_to_prefix(mask: Ipv4Addr) -> Result<u8, IpNetworkError> {
#[cfg(test)]
mod test {
use std::mem;
use std::collections::HashMap;
use std::net::Ipv4Addr;
use super::*;
use std::collections::HashMap;
use std::mem;
use std::net::Ipv4Addr;
#[test]
fn create_v4() {
@ -246,13 +324,6 @@ mod test {
assert!(net.is_err());
}
#[test]
fn parse_v4_0bit() {
let cidr: Ipv4Network = "0/0".parse().unwrap();
assert_eq!(cidr.ip(), Ipv4Addr::new(0, 0, 0, 0));
assert_eq!(cidr.prefix(), 0);
}
#[test]
fn parse_v4_24bit() {
let cidr: Ipv4Network = "127.1.0.0/24".parse().unwrap();
@ -267,6 +338,13 @@ mod test {
assert_eq!(cidr.prefix(), 32);
}
#[test]
fn parse_v4_noprefix() {
let cidr: Ipv4Network = "127.0.0.0".parse().unwrap();
assert_eq!(cidr.ip(), Ipv4Addr::new(127, 0, 0, 0));
assert_eq!(cidr.prefix(), 32);
}
#[test]
fn parse_v4_fail_addr() {
let cidr: Option<Ipv4Network> = "10.a.b/8".parse().ok();
@ -299,27 +377,9 @@ mod test {
}
#[test]
fn size_v4_24bit() {
let net: Ipv4Network = "0/24".parse().unwrap();
assert_eq!(net.size(), 256);
}
#[test]
fn size_v4_1bit() {
let net: Ipv4Network = "0/31".parse().unwrap();
assert_eq!(net.size(), 2);
}
#[test]
fn size_v4_max() {
let net: Ipv4Network = "0/0".parse().unwrap();
assert_eq!(net.size(), 4_294_967_296);
}
#[test]
fn size_v4_min() {
let net: Ipv4Network = "0/32".parse().unwrap();
assert_eq!(net.size(), 1);
fn parse_v4_fail_two_slashes() {
let cidr: Option<Ipv4Network> = "10.1.1.1/24/".parse().ok();
assert_eq!(None, cidr);
}
#[test]
@ -398,14 +458,6 @@ mod test {
assert_eq!(None, iter.next());
}
#[test]
fn iterator_v4_tiny() {
let cidr: Ipv4Network = "10/32".parse().unwrap();
let mut iter = cidr.iter();
assert_eq!(Ipv4Addr::new(10, 0, 0, 0), iter.next().unwrap());
assert_eq!(None, iter.next());
}
// Tests the entire IPv4 space to see if the iterator will stop at the correct place
// and not overflow or wrap around. Ignored since it takes a long time to run.
#[test]
@ -426,10 +478,184 @@ mod test {
assert_eq!(prefix, 25);
}
/// Parse netmask as well as prefix
#[test]
fn parse_netmask() {
let from_netmask: Ipv4Network = "192.168.1.0/255.255.255.0".parse().unwrap();
let from_prefix: Ipv4Network = "192.168.1.0/24".parse().unwrap();
assert_eq!(from_netmask, from_prefix);
}
#[test]
fn parse_netmask_broken_v4() {
assert_eq!(
"192.168.1.0/255.0.255.0".parse::<Ipv4Network>(),
Err(IpNetworkError::InvalidPrefix)
);
}
#[test]
fn invalid_v4_mask_to_prefix() {
let mask = Ipv4Addr::new(255, 0, 255, 0);
let prefix = ipv4_mask_to_prefix(mask);
assert!(prefix.is_err());
}
#[test]
fn ipv4network_with_netmask() {
{
// Positive test-case.
let addr = Ipv4Addr::new(127, 0, 0, 1);
let mask = Ipv4Addr::new(255, 0, 0, 0);
let net = Ipv4Network::with_netmask(addr, mask).unwrap();
let expected = Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 1), 8).unwrap();
assert_eq!(net, expected);
}
{
// Negative test-case.
let addr = Ipv4Addr::new(127, 0, 0, 1);
let mask = Ipv4Addr::new(255, 0, 255, 0);
Ipv4Network::with_netmask(addr, mask).unwrap_err();
}
}
#[test]
fn ipv4network_from_ipv4addr() {
let net = Ipv4Network::from(Ipv4Addr::new(127, 0, 0, 1));
let expected = Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap();
assert_eq!(net, expected);
}
#[test]
fn test_send() {
fn assert_send<T: Send>() {}
assert_send::<Ipv4Network>();
}
#[test]
fn test_sync() {
fn assert_sync<T: Sync>() {}
assert_sync::<Ipv4Network>();
}
// Tests from cpython https://github.com/python/cpython/blob/e9bc4172d18db9c182d8e04dd7b033097a994c06/Lib/test/test_ipaddress.py
#[test]
fn test_is_subnet_of() {
let mut test_cases: HashMap<(Ipv4Network, Ipv4Network), bool> = HashMap::new();
test_cases.insert(
(
"10.0.0.0/30".parse().unwrap(),
"10.0.1.0/24".parse().unwrap(),
),
false,
);
test_cases.insert(
(
"10.0.0.0/30".parse().unwrap(),
"10.0.0.0/24".parse().unwrap(),
),
true,
);
test_cases.insert(
(
"10.0.0.0/30".parse().unwrap(),
"10.0.1.0/24".parse().unwrap(),
),
false,
);
test_cases.insert(
(
"10.0.1.0/24".parse().unwrap(),
"10.0.0.0/30".parse().unwrap(),
),
false,
);
for (key, val) in test_cases.iter() {
let (src, dest) = (key.0, key.1);
assert_eq!(
src.is_subnet_of(dest),
*val,
"testing with {} and {}",
src,
dest
);
}
}
#[test]
fn test_is_supernet_of() {
let mut test_cases: HashMap<(Ipv4Network, Ipv4Network), bool> = HashMap::new();
test_cases.insert(
(
"10.0.0.0/30".parse().unwrap(),
"10.0.1.0/24".parse().unwrap(),
),
false,
);
test_cases.insert(
(
"10.0.0.0/30".parse().unwrap(),
"10.0.0.0/24".parse().unwrap(),
),
false,
);
test_cases.insert(
(
"10.0.0.0/30".parse().unwrap(),
"10.0.1.0/24".parse().unwrap(),
),
false,
);
test_cases.insert(
(
"10.0.0.0/24".parse().unwrap(),
"10.0.0.0/30".parse().unwrap(),
),
true,
);
for (key, val) in test_cases.iter() {
let (src, dest) = (key.0, key.1);
assert_eq!(
src.is_supernet_of(dest),
*val,
"testing with {} and {}",
src,
dest
);
}
}
#[test]
fn test_overlaps() {
let other: Ipv4Network = "1.2.3.0/30".parse().unwrap();
let other2: Ipv4Network = "1.2.2.0/24".parse().unwrap();
let other3: Ipv4Network = "1.2.2.64/26".parse().unwrap();
let skynet: Ipv4Network = "1.2.3.0/24".parse().unwrap();
assert_eq!(skynet.overlaps(other), true);
assert_eq!(skynet.overlaps(other2), false);
assert_eq!(other2.overlaps(other3), true);
}
#[test]
fn edges() {
let low: Ipv4Network = "0.0.0.0/24".parse().unwrap();
let low_addrs: Vec<Ipv4Addr> = low.iter().collect();
assert_eq!(256, low_addrs.len());
assert_eq!("0.0.0.0".parse::<Ipv4Addr>().unwrap(), low_addrs[0]);
assert_eq!("0.0.0.255".parse::<Ipv4Addr>().unwrap(), low_addrs[255]);
let high: Ipv4Network = "255.255.255.0/24".parse().unwrap();
let high_addrs: Vec<Ipv4Addr> = high.iter().collect();
assert_eq!(256, high_addrs.len());
assert_eq!("255.255.255.0".parse::<Ipv4Addr>().unwrap(), high_addrs[0]);
assert_eq!(
"255.255.255.255".parse::<Ipv4Addr>().unwrap(),
high_addrs[255]
);
}
}

View File

@ -1,34 +1,117 @@
use std::cmp;
use std::fmt;
use std::net::Ipv6Addr;
use std::str::FromStr;
use common::{IpNetworkError, cidr_parts, parse_prefix};
use crate::common::{cidr_parts, parse_prefix, IpNetworkError};
use std::{cmp, fmt, net::Ipv6Addr, str::FromStr};
const IPV6_BITS: u8 = 128;
const IPV6_SEGMENT_BITS: u8 = 16;
/// Represents a network range where the IP addresses are of v6
#[derive(Debug,Clone,Copy,Hash,PartialEq,Eq)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Ipv6Network {
addr: Ipv6Addr,
prefix: u8,
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Ipv6Network {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = <String>::deserialize(deserializer)?;
Ipv6Network::from_str(&s).map_err(serde::de::Error::custom)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Ipv6Network {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl Ipv6Network {
/// Constructs a new `Ipv6Network` from any `Ipv6Addr` and a prefix denoting the network size.
///
/// If the prefix is larger than 128 this will return an `IpNetworkError::InvalidPrefix`.
pub fn new(addr: Ipv6Addr, prefix: u8) -> Result<Ipv6Network, IpNetworkError> {
if prefix > IPV6_BITS {
Err(IpNetworkError::InvalidPrefix)
} else {
Ok(Ipv6Network {
addr: addr,
prefix: prefix,
})
Ok(Ipv6Network { addr, prefix })
}
}
/// Constructs a new `Ipv6Network` from a network address and a network mask.
///
/// If the netmask is not valid this will return an `IpNetworkError::InvalidPrefix`.
pub fn with_netmask(netaddr: Ipv6Addr, netmask: Ipv6Addr) -> Result<Self, IpNetworkError> {
let prefix = ipv6_mask_to_prefix(netmask)?;
let net = Self {
addr: netaddr,
prefix,
};
Ok(net)
}
/// Returns an iterator over `Ipv6Network`. Each call to `next` will return the next
/// `Ipv6Addr` in the given network. `None` will be returned when there are no more
/// addresses.
pub fn iter(&self) -> Ipv6NetworkIterator {
let dec = u128::from(self.addr);
let max = u128::max_value();
let prefix = self.prefix;
let mask = max.checked_shl(u32::from(IPV6_BITS - prefix)).unwrap_or(0);
let start: u128 = dec & mask;
let mask = max.checked_shr(u32::from(prefix)).unwrap_or(0);
let end: u128 = dec | mask;
Ipv6NetworkIterator {
next: Some(start),
end: end,
}
}
/// Returns the address of the network denoted by this `Ipv6Network`.
/// This means the lowest possible IPv6 address inside of the network.
///
/// # Examples
///
/// ```
/// use std::net::Ipv6Addr;
/// use ipnetwork::Ipv6Network;
///
/// let net: Ipv6Network = "2001:db8::/96".parse().unwrap();
/// assert_eq!(net.network(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0));
/// ```
pub fn network(&self) -> Ipv6Addr {
let mask = u128::from(self.mask());
let ip = u128::from(self.addr) & mask;
Ipv6Addr::from(ip)
}
/// Returns the broadcast address of this `Ipv6Network`.
/// This means the highest possible IPv4 address inside of the network.
///
/// # Examples
///
/// ```
/// use std::net::Ipv6Addr;
/// use ipnetwork::Ipv6Network;
///
/// let net: Ipv6Network = "2001:db8::/96".parse().unwrap();
/// assert_eq!(net.broadcast(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xffff, 0xffff));
/// ```
pub fn broadcast(&self) -> Ipv6Addr {
let mask = u128::from(self.mask());
let broadcast = u128::from(self.addr) | !mask;
Ipv6Addr::from(broadcast)
}
pub fn ip(&self) -> Ipv6Addr {
self.addr
}
@ -37,6 +120,23 @@ impl Ipv6Network {
self.prefix
}
/// Checks if the given `Ipv6Network` is a subnet of the other.
pub fn is_subnet_of(self, other: Ipv6Network) -> bool {
other.ip() <= self.ip() && other.broadcast() >= self.broadcast()
}
/// Checks if the given `Ipv6Network` is a supernet of the other.
pub fn is_supernet_of(self, other: Ipv6Network) -> bool {
other.is_subnet_of(self)
}
/// Checks if the given `Ipv6Network` is partly contained in other.
pub fn overlaps(self, other: Ipv6Network) -> bool {
other.contains(self.ip())
|| (other.contains(self.broadcast())
|| (self.contains(other.ip()) || (self.contains(other.broadcast()))))
}
/// Returns the mask for this `Ipv6Network`.
/// That means the `prefix` most significant bits will be 1 and the rest 0
///
@ -46,6 +146,8 @@ impl Ipv6Network {
/// use std::net::Ipv6Addr;
/// use ipnetwork::Ipv6Network;
///
/// let net: Ipv6Network = "ff01::0".parse().unwrap();
/// assert_eq!(net.mask(), Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff));
/// let net: Ipv6Network = "ff01::0/32".parse().unwrap();
/// assert_eq!(net.mask(), Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0));
/// ```
@ -76,7 +178,30 @@ impl Ipv6Network {
let a = self.addr.segments();
let b = ip.segments();
let addrs = Iterator::zip(a.iter(), b.iter());
self.mask().segments().iter().zip(addrs).all(|(mask, (a, b))| a & mask == b & mask)
self.mask()
.segments()
.iter()
.zip(addrs)
.all(|(mask, (a, b))| a & mask == b & mask)
}
/// Returns number of possible host addresses in this `Ipv6Network`.
///
/// # Examples
///
/// ```
/// use std::net::Ipv6Addr;
/// use ipnetwork::Ipv6Network;
///
/// let net: Ipv6Network = "ff01::0/32".parse().unwrap();
/// assert_eq!(net.size(), 79228162514264337593543950336);
///
/// let tinynet: Ipv6Network = "ff01::0/128".parse().unwrap();
/// assert_eq!(tinynet.size(), 1);
/// ```
pub fn size(&self) -> u128 {
let host_bits = u32::from(IPV6_BITS - self.prefix);
(2 as u128).pow(host_bits)
}
}
@ -84,14 +209,55 @@ impl FromStr for Ipv6Network {
type Err = IpNetworkError;
fn from_str(s: &str) -> Result<Ipv6Network, IpNetworkError> {
let (addr_str, prefix_str) = cidr_parts(s)?;
let addr = Ipv6Addr::from_str(addr_str).map_err(|_| IpNetworkError::InvalidAddr(addr_str.to_string()))?;
let prefix = parse_prefix(prefix_str, IPV6_BITS)?;
let addr = Ipv6Addr::from_str(addr_str)
.map_err(|_| IpNetworkError::InvalidAddr(addr_str.to_string()))?;
let prefix = match prefix_str {
Some(v) => parse_prefix(v, IPV6_BITS)?,
None => IPV6_BITS,
};
Ipv6Network::new(addr, prefix)
}
}
impl From<Ipv6Addr> for Ipv6Network {
fn from(a: Ipv6Addr) -> Ipv6Network {
Ipv6Network {
addr: a,
prefix: 128,
}
}
}
#[derive(Clone, Debug)]
pub struct Ipv6NetworkIterator {
next: Option<u128>,
end: u128,
}
impl Iterator for Ipv6NetworkIterator {
type Item = Ipv6Addr;
fn next(&mut self) -> Option<Ipv6Addr> {
let next = self.next?;
self.next = if next == self.end {
None
} else {
Some(next + 1)
};
Some(next.into())
}
}
impl IntoIterator for &'_ Ipv6Network {
type IntoIter = Ipv6NetworkIterator;
type Item = Ipv6Addr;
fn into_iter(self) -> Ipv6NetworkIterator {
self.iter()
}
}
impl fmt::Display for Ipv6Network {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "{}/{}", self.ip(), self.prefix())
}
}
@ -100,7 +266,7 @@ impl fmt::Display for Ipv6Network {
/// If the mask is invalid this will return an `IpNetworkError::InvalidPrefix`.
pub fn ipv6_mask_to_prefix(mask: Ipv6Addr) -> Result<u8, IpNetworkError> {
let mask = mask.segments();
let mut mask_iter = mask.into_iter();
let mut mask_iter = mask.iter();
// Count the number of set bits from the start of the address
let mut prefix = 0;
@ -133,8 +299,9 @@ pub fn ipv6_mask_to_prefix(mask: Ipv6Addr) -> Result<u8, IpNetworkError> {
#[cfg(test)]
mod test {
use std::net::Ipv6Addr;
use super::*;
use std::collections::HashMap;
use std::net::Ipv6Addr;
#[test]
fn create_v6() {
@ -142,6 +309,14 @@ mod test {
assert_eq!(cidr.prefix(), 24);
}
#[test]
fn parse_netmask_broken_v6() {
assert_eq!(
"FF01:0:0:17:0:0:0:2/255.255.255.0".parse::<Ipv6Network>(),
Err(IpNetworkError::InvalidPrefix)
);
}
#[test]
fn create_v6_invalid_prefix() {
let cidr = Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 129);
@ -162,6 +337,13 @@ mod test {
assert_eq!(cidr.prefix(), 64);
}
#[test]
fn parse_v6_noprefix() {
let cidr: Ipv6Network = "::1".parse().unwrap();
assert_eq!(cidr.ip(), Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
assert_eq!(cidr.prefix(), 128);
}
#[test]
fn parse_v6_fail_addr() {
let cidr: Option<Ipv6Network> = "2001::1::/8".parse().ok();
@ -174,6 +356,12 @@ mod test {
assert_eq!(None, cidr);
}
#[test]
fn parse_v6_fail_two_slashes() {
let cidr: Option<Ipv6Network> = "::1/24/".parse().ok();
assert_eq!(None, cidr);
}
#[test]
fn mask_v6() {
let cidr = Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), 40).unwrap();
@ -208,4 +396,219 @@ mod test {
let prefix = ipv6_mask_to_prefix(mask);
assert!(prefix.is_err());
}
#[test]
fn ipv6network_with_netmask() {
{
// Positive test-case.
let addr = Ipv6Addr::new(0xff01, 0, 0, 0x17, 0, 0, 0, 0x2);
let mask = Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0, 0, 0, 0, 0);
let net = Ipv6Network::with_netmask(addr, mask).unwrap();
let expected =
Ipv6Network::new(Ipv6Addr::new(0xff01, 0, 0, 0x17, 0, 0, 0, 0x2), 48).unwrap();
assert_eq!(net, expected);
}
{
// Negative test-case.
let addr = Ipv6Addr::new(0xff01, 0, 0, 0x17, 0, 0, 0, 0x2);
let mask = Ipv6Addr::new(0, 0, 0xffff, 0xffff, 0, 0, 0, 0);
Ipv6Network::with_netmask(addr, mask).unwrap_err();
}
}
#[test]
fn iterator_v6() {
let cidr: Ipv6Network = "2001:db8::/126".parse().unwrap();
let mut iter = cidr.iter();
assert_eq!(
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
iter.next().unwrap()
);
assert_eq!(
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1),
iter.next().unwrap()
);
assert_eq!(
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2),
iter.next().unwrap()
);
assert_eq!(
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 3),
iter.next().unwrap()
);
assert_eq!(None, iter.next());
}
#[test]
fn iterator_v6_tiny() {
let cidr: Ipv6Network = "2001:db8::/128".parse().unwrap();
let mut iter = cidr.iter();
assert_eq!(
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
iter.next().unwrap()
);
assert_eq!(None, iter.next());
}
#[test]
fn iterator_v6_huge() {
let cidr: Ipv6Network = "2001:db8::/0".parse().unwrap();
let mut iter = cidr.iter();
assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), iter.next().unwrap());
assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), iter.next().unwrap());
assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 2), iter.next().unwrap());
}
#[test]
fn network_v6() {
let cidr: Ipv6Network = "2001:db8::0/96".parse().unwrap();
let net = cidr.network();
let expected: Ipv6Addr = "2001:db8::".parse().unwrap();
assert_eq!(net, expected);
}
#[test]
fn broadcast_v6() {
let cidr: Ipv6Network = "2001:db8::0/96".parse().unwrap();
let net = cidr.broadcast();
let expected: Ipv6Addr = "2001:db8::ffff:ffff".parse().unwrap();
assert_eq!(net, expected);
}
#[test]
fn size_v6() {
let cidr: Ipv6Network = "2001:db8::0/96".parse().unwrap();
assert_eq!(cidr.size(), 4294967296);
}
#[test]
fn ipv6network_from_ipv6addr() {
let net = Ipv6Network::from(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
let expected = Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 128).unwrap();
assert_eq!(net, expected);
}
#[test]
fn test_send() {
fn assert_send<T: Send>() {}
assert_send::<Ipv6Network>();
}
#[test]
fn test_sync() {
fn assert_sync<T: Sync>() {}
assert_sync::<Ipv6Network>();
}
// Tests from cpython https://github.com/python/cpython/blob/e9bc4172d18db9c182d8e04dd7b033097a994c06/Lib/test/test_ipaddress.py
#[test]
fn test_is_subnet_of() {
let mut test_cases: HashMap<(Ipv6Network, Ipv6Network), bool> = HashMap::new();
test_cases.insert(
(
"2000:999::/56".parse().unwrap(),
"2000:aaa::/48".parse().unwrap(),
),
false,
);
test_cases.insert(
(
"2000:aaa::/56".parse().unwrap(),
"2000:aaa::/48".parse().unwrap(),
),
true,
);
test_cases.insert(
(
"2000:bbb::/56".parse().unwrap(),
"2000:aaa::/48".parse().unwrap(),
),
false,
);
test_cases.insert(
(
"2000:aaa::/48".parse().unwrap(),
"2000:aaa::/56".parse().unwrap(),
),
false,
);
for (key, val) in test_cases.iter() {
let (src, dest) = (key.0, key.1);
assert_eq!(
src.is_subnet_of(dest),
*val,
"testing with {} and {}",
src,
dest
);
}
}
#[test]
fn test_is_supernet_of() {
let mut test_cases: HashMap<(Ipv6Network, Ipv6Network), bool> = HashMap::new();
test_cases.insert(
(
"2000:999::/56".parse().unwrap(),
"2000:aaa::/48".parse().unwrap(),
),
false,
);
test_cases.insert(
(
"2000:aaa::/56".parse().unwrap(),
"2000:aaa::/48".parse().unwrap(),
),
false,
);
test_cases.insert(
(
"2000:bbb::/56".parse().unwrap(),
"2000:aaa::/48".parse().unwrap(),
),
false,
);
test_cases.insert(
(
"2000:aaa::/48".parse().unwrap(),
"2000:aaa::/56".parse().unwrap(),
),
true,
);
for (key, val) in test_cases.iter() {
let (src, dest) = (key.0, key.1);
assert_eq!(
src.is_supernet_of(dest),
*val,
"testing with {} and {}",
src,
dest
);
}
}
#[test]
fn test_overlaps() {
let other: Ipv6Network = "2001:DB8:ACAD::1/64".parse().unwrap();
let other2: Ipv6Network = "2001:DB8:ACAD::20:2/64".parse().unwrap();
assert_eq!(other2.overlaps(other), true);
}
#[test]
fn edges() {
let low: Ipv6Network = "::0/120".parse().unwrap();
let low_addrs: Vec<Ipv6Addr> = low.iter().collect();
assert_eq!(256, low_addrs.len());
let high: Ipv6Network = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00/120"
.parse()
.unwrap();
let high_addrs: Vec<Ipv6Addr> = high.iter().collect();
assert_eq!(256, high_addrs.len());
}
}

View File

@ -1,30 +1,58 @@
//! The `ipnetwork` crate provides a set of APIs to work with IP CIDRs in
//! Rust. Implementation for IPv4 is more or less stable, IPv6 implementation
//! is still WIP.
//! Rust.
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]
#![crate_type = "lib"]
#![doc(html_root_url = "https://docs.rs/ipnetwork/0.16.0")]
use std::net::IpAddr;
use std::{fmt, net::IpAddr, str::FromStr};
mod common;
mod ipv4;
mod ipv6;
mod common;
use std::str::FromStr;
pub use ipv4::{Ipv4Network, ipv4_mask_to_prefix};
pub use ipv6::{Ipv6Network, ipv6_mask_to_prefix};
pub use common::IpNetworkError;
pub use crate::common::IpNetworkError;
use crate::ipv4::Ipv4NetworkIterator;
pub use crate::ipv4::{ipv4_mask_to_prefix, Ipv4Network};
use crate::ipv6::Ipv6NetworkIterator;
pub use crate::ipv6::{ipv6_mask_to_prefix, Ipv6Network};
/// Represents a generic network range. This type can have two variants:
/// the v4 and the v6 case.
#[derive(Debug,Clone,Copy,Hash,PartialEq,Eq)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum IpNetwork {
V4(Ipv4Network),
V6(Ipv6Network),
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for IpNetwork {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = <String>::deserialize(deserializer)?;
IpNetwork::from_str(&s).map_err(serde::de::Error::custom)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for IpNetwork {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
/// Represents a generic network size. For IPv4, the max size is a u32 and for IPv6, it is a u128
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum NetworkSize {
V4(u32),
V6(u128),
}
impl IpNetwork {
/// Constructs a new `IpNetwork` from a given `IpAddr` and a prefix denoting the
/// network size. If the prefix is larger than 32 (for IPv4) or 128 (for IPv6), this
@ -37,6 +65,14 @@ impl IpNetwork {
}
}
/// Constructs a new `IpNetwork` from a network address and a network mask.
///
/// If the netmask is not valid this will return an `IpNetworkError::InvalidPrefix`.
pub fn with_netmask(netaddr: IpAddr, netmask: IpAddr) -> Result<Self, IpNetworkError> {
let prefix = ip_mask_to_prefix(netmask)?;
Self::new(netaddr, prefix)
}
/// Returns the IP part of a given `IpNetwork`
pub fn ip(&self) -> IpAddr {
match *self {
@ -51,7 +87,10 @@ impl IpNetwork {
/// ```
/// use ipnetwork::IpNetwork;
///
/// assert_eq!(IpNetwork::V4("10.9.0.1".parse().unwrap()).prefix(), 32u8);
/// assert_eq!(IpNetwork::V4("10.9.0.32/16".parse().unwrap()).prefix(), 16u8);
///
/// assert_eq!(IpNetwork::V6("ff01::0".parse().unwrap()).prefix(), 128u8);
/// assert_eq!(IpNetwork::V6("ff01::0/32".parse().unwrap()).prefix(), 32u8);
/// ```
pub fn prefix(&self) -> u8 {
@ -61,15 +100,65 @@ impl IpNetwork {
}
}
/// Returns the mask of the given `IpNetwork`
/// Returns the address of the network denoted by this `IpNetwork`.
/// This means the lowest possible IP address inside of the network.
///
/// # Examples
///
/// # Example
/// ```
/// use std::net::{Ipv4Addr, Ipv6Addr};
/// use ipnetwork::IpNetwork;
///
/// assert_eq!(IpNetwork::V4("10.9.0.32/16".parse().unwrap()).mask(), 16u8);
/// assert_eq!(IpNetwork::V6("ff01::0/32".parse().unwrap()).mask(), 32u8);
///```
/// let net: IpNetwork = "10.1.9.32/16".parse().unwrap();
/// assert_eq!(net.network(), Ipv4Addr::new(10, 1, 0, 0));
/// let net: IpNetwork = "2001:db8::/96".parse().unwrap();
/// assert_eq!(net.network(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0));
/// ```
pub fn network(&self) -> IpAddr {
match *self {
IpNetwork::V4(ref a) => IpAddr::V4(a.network()),
IpNetwork::V6(ref a) => IpAddr::V6(a.network()),
}
}
/// Returns the broadcasting address of this `IpNetwork`.
/// This means the highest possible IP address inside of the network.
///
/// # Examples
///
/// ```
/// use std::net::Ipv4Addr;
/// use ipnetwork::{IpNetwork, Ipv4Network};
///
/// let net: Ipv4Network = "10.9.0.32/16".parse().unwrap();
/// assert_eq!(net.broadcast(), Ipv4Addr::new(10, 9, 255, 255));
/// ```
pub fn broadcast(&self) -> IpAddr {
match *self {
IpNetwork::V4(ref a) => IpAddr::V4(a.broadcast()),
IpNetwork::V6(ref a) => IpAddr::V6(a.broadcast()),
}
}
/// Returns the mask for this `IpNetwork`.
/// That means the `prefix` most significant bits will be 1 and the rest 0
///
/// # Example
///
/// ```
/// use ipnetwork::IpNetwork;
/// use std::net::{Ipv4Addr, Ipv6Addr};
///
/// let v4_net: IpNetwork = "10.9.0.1".parse().unwrap();
/// assert_eq!(v4_net.mask(), Ipv4Addr::new(255, 255, 255, 255));
/// let v4_net: IpNetwork = "10.9.0.32/16".parse().unwrap();
/// assert_eq!(v4_net.mask(), Ipv4Addr::new(255, 255, 0, 0));
///
/// let v6_net: IpNetwork = "ff01::0".parse().unwrap();
/// assert_eq!(v6_net.mask(), Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff));
/// let v6_net: IpNetwork = "ff01::0/32".parse().unwrap();
/// assert_eq!(v6_net.mask(), Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0));
/// ```
pub fn mask(&self) -> IpAddr {
match *self {
IpNetwork::V4(ref a) => IpAddr::V4(a.mask()),
@ -114,32 +203,86 @@ impl IpNetwork {
IpNetwork::V6(_) => true,
}
}
// TODO(abhishek) when TryFrom is stable, implement it for IpNetwork to
// variant conversions. Then use that to implement a generic is_subnet_of
// is_supernet_of, overlaps
/// Checks if a given `IpAddr` is in this `IpNetwork`
///
/// # Examples
///
/// ```
/// use std::net::IpAddr;
/// use ipnetwork::IpNetwork;
///
/// let net: IpNetwork = "127.0.0.0/24".parse().unwrap();
/// let ip1: IpAddr = "127.0.0.1".parse().unwrap();
/// let ip2: IpAddr = "172.0.0.1".parse().unwrap();
/// let ip4: IpAddr = "::1".parse().unwrap();
/// assert!(net.contains(ip1));
/// assert!(!net.contains(ip2));
/// assert!(!net.contains(ip4));
/// ```
pub fn contains(&self, ip: IpAddr) -> bool {
match (*self, ip) {
(IpNetwork::V4(net), IpAddr::V4(ip)) => net.contains(ip),
(IpNetwork::V6(net), IpAddr::V6(ip)) => net.contains(ip),
_ => false,
}
}
/// Returns the number of possible host addresses in this `IpAddr`
///
/// # Examples
///
/// ```
/// use ipnetwork::{IpNetwork, NetworkSize};
///
///
/// let net: IpNetwork = "127.0.0.0/24".parse().unwrap();
/// assert_eq!(net.size(), NetworkSize::V4(256))
/// ```
pub fn size(&self) -> NetworkSize {
match *self {
IpNetwork::V4(ref ip) => NetworkSize::V4(ip.size()),
IpNetwork::V6(ref ip) => NetworkSize::V6(ip.size()),
}
}
/// Returns an iterator over the addresses contained in the network.
///
/// This lists all the addresses in the network range, in ascending order.
pub fn iter(&self) -> IpNetworkIterator {
let inner = match self {
IpNetwork::V4(ip) => IpNetworkIteratorInner::V4(ip.iter()),
IpNetwork::V6(ip) => IpNetworkIteratorInner::V6(ip.iter()),
};
IpNetworkIterator { inner }
}
}
/// Constructs a new `IpNetwork` from a given &str with a prefix denoting the
/// network size. If the prefix is larger than 32 (for IPv4) or 128 (for IPv6), this
/// will raise an `IpNetworkError::InvalidPrefix` error.
/// Tries to parse the given string into a `IpNetwork`. Will first try to parse
/// it as an `Ipv4Network` and if that fails as an `Ipv6Network`. If both
/// fails it will return an `InvalidAddr` error.
///
/// # Examples
///
/// ```
/// use std::net::Ipv4Addr;
/// use ipnetwork::Ipv4Network;
/// use ipnetwork::IpNetwork;
/// use ipnetwork::{IpNetwork, Ipv4Network};
///
/// let expected = IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(10, 1, 9, 32), 16).unwrap());
/// let from_cidr: IpNetwork = "10.1.9.32/16".parse().unwrap();
/// assert_eq!(expected.ip(), from_cidr.ip());
/// assert_eq!(expected.prefix(), from_cidr.prefix());
/// assert_eq!(expected, from_cidr);
/// ```
impl FromStr for IpNetwork {
type Err = IpNetworkError;
fn from_str(s: &str) -> Result<IpNetwork, IpNetworkError> {
let v4addr: Result<Ipv4Network, IpNetworkError> = s.parse();
let v6addr: Result<Ipv6Network, IpNetworkError> = s.parse();
if v4addr.is_ok() {
Ok(IpNetwork::V4(v4addr.unwrap()))
} else if v6addr.is_ok() {
Ok(IpNetwork::V6(v6addr.unwrap()))
if let Ok(net) = Ipv4Network::from_str(s) {
Ok(IpNetwork::V4(net))
} else if let Ok(net) = Ipv6Network::from_str(s) {
Ok(IpNetwork::V6(net))
} else {
Err(IpNetworkError::InvalidAddr(s.to_string()))
}
@ -158,6 +301,52 @@ impl From<Ipv6Network> for IpNetwork {
}
}
impl From<IpAddr> for IpNetwork {
fn from(addr: IpAddr) -> IpNetwork {
match addr {
IpAddr::V4(a) => IpNetwork::V4(Ipv4Network::from(a)),
IpAddr::V6(a) => IpNetwork::V6(Ipv6Network::from(a)),
}
}
}
impl fmt::Display for IpNetwork {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
IpNetwork::V4(net) => net.fmt(f),
IpNetwork::V6(net) => net.fmt(f),
}
}
}
#[derive(Clone, Debug)]
enum IpNetworkIteratorInner {
V4(Ipv4NetworkIterator),
V6(Ipv6NetworkIterator),
}
pub struct IpNetworkIterator {
inner: IpNetworkIteratorInner,
}
impl Iterator for IpNetworkIterator {
type Item = IpAddr;
fn next(&mut self) -> Option<IpAddr> {
match &mut self.inner {
IpNetworkIteratorInner::V4(iter) => iter.next().map(IpAddr::V4),
IpNetworkIteratorInner::V6(iter) => iter.next().map(IpAddr::V6),
}
}
}
impl IntoIterator for &'_ IpNetwork {
type IntoIter = IpNetworkIterator;
type Item = IpAddr;
fn into_iter(self) -> IpNetworkIterator {
self.iter()
}
}
/// Converts a `IpAddr` network mask into a prefix.
/// If the mask is invalid this will return an `IpNetworkError::InvalidPrefix`.
pub fn ip_mask_to_prefix(mask: IpAddr) -> Result<u8, IpNetworkError> {
@ -166,3 +355,17 @@ pub fn ip_mask_to_prefix(mask: IpAddr) -> Result<u8, IpNetworkError> {
IpAddr::V6(mask) => ipv6_mask_to_prefix(mask),
}
}
#[cfg(test)]
mod test {
#[test]
#[cfg(feature = "serde")]
fn deserialize_from_serde_json_value() {
use super::*;
let network = IpNetwork::from_str("0.0.0.0/0").unwrap();
let val: serde_json::value::Value =
serde_json::from_str(&serde_json::to_string(&network).unwrap()).unwrap();
let _deser: IpNetwork = serde_json::from_value(val)
.expect("Fails to deserialize from json_value::value::Value");
}
}

67
tests/test_json.rs Normal file
View File

@ -0,0 +1,67 @@
#![cfg(feature = "serde")]
#[cfg(test)]
mod tests {
use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network};
use serde_derive::{Deserialize, Serialize};
use std::net::{Ipv4Addr, Ipv6Addr};
#[test]
fn test_ipv4_json() {
let json_string = r#"{"ipnetwork":"127.1.0.0/24"}"#;
#[derive(Serialize, Deserialize)]
struct MyStruct {
ipnetwork: Ipv4Network,
}
let mystruct: MyStruct = ::serde_json::from_str(json_string).unwrap();
assert_eq!(mystruct.ipnetwork.ip(), Ipv4Addr::new(127, 1, 0, 0));
assert_eq!(mystruct.ipnetwork.prefix(), 24);
assert_eq!(::serde_json::to_string(&mystruct).unwrap(), json_string);
}
#[test]
fn test_ipv6_json() {
let json_string = r#"{"ipnetwork":"::1/0"}"#;
#[derive(Serialize, Deserialize)]
struct MyStruct {
ipnetwork: Ipv6Network,
}
let mystruct: MyStruct = ::serde_json::from_str(json_string).unwrap();
assert_eq!(
mystruct.ipnetwork.ip(),
Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)
);
assert_eq!(mystruct.ipnetwork.prefix(), 0);
assert_eq!(::serde_json::to_string(&mystruct).unwrap(), json_string);
}
#[test]
fn test_ipnetwork_json() {
let json_string = r#"{"ipnetwork":["127.1.0.0/24","::1/0"]}"#;
#[derive(Serialize, Deserialize)]
struct MyStruct {
ipnetwork: Vec<IpNetwork>,
}
let mystruct: MyStruct = ::serde_json::from_str(json_string).unwrap();
assert_eq!(mystruct.ipnetwork[0].ip(), Ipv4Addr::new(127, 1, 0, 0));
assert_eq!(mystruct.ipnetwork[0].prefix(), 24);
assert_eq!(
mystruct.ipnetwork[1].ip(),
Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)
);
assert_eq!(mystruct.ipnetwork[1].prefix(), 0);
assert_eq!(::serde_json::to_string(&mystruct).unwrap(), json_string);
}
}