mirror of
https://github.com/ktims/rs-aggregate.git
synced 2024-11-15 23:27:18 -08:00
More refactoring, add tests, aggregate6 compatibility
This commit is contained in:
parent
d8b48aba9a
commit
8034822ec8
520
Cargo.lock
generated
520
Cargo.lock
generated
@ -2,12 +2,81 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "0.7.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "assert_cmd"
|
||||||
|
version = "2.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0b2340f55d9661d76793b2bfc2eb0e62689bd79d067a95707ea762afd5e9dd"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"bstr",
|
||||||
|
"doc-comment",
|
||||||
|
"predicates",
|
||||||
|
"predicates-core",
|
||||||
|
"predicates-tree",
|
||||||
|
"wait-timeout",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "assert_fs"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e9d5bf7e5441c6393b5a9670a5036abe6b4847612f594b870f7332dbf10cf6fa"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"doc-comment",
|
||||||
|
"globwalk",
|
||||||
|
"predicates",
|
||||||
|
"predicates-core",
|
||||||
|
"predicates-tree",
|
||||||
|
"tempfile",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d5dd14596c0e5b954530d0e6f1fd99b89c03e313aa2086e8da4303701a09e1cf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bstr"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"once_cell",
|
||||||
|
"regex-automata",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.79"
|
version = "1.0.79"
|
||||||
@ -15,12 +84,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "cfg-if"
|
||||||
version = "4.1.10"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ce38afc168d8665cfc75c7b1dd9672e50716a137f433f070991619744a67342a"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.1.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42dfd32784433290c51d92c438bb72ea5063797fc3cc9a21a8c4346bebbb2098"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.0.1",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
"clap_lex",
|
"clap_lex",
|
||||||
"is-terminal",
|
"is-terminal",
|
||||||
@ -62,6 +137,24 @@ dependencies = [
|
|||||||
"windows-sys 0.42.0",
|
"windows-sys 0.42.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "difflib"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "doc-comment"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.2.8"
|
version = "0.2.8"
|
||||||
@ -83,6 +176,155 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
|
||||||
|
dependencies = [
|
||||||
|
"instant",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "float-cmp"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.3.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-channel"
|
||||||
|
version = "0.3.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-core"
|
||||||
|
version = "0.3.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-executor"
|
||||||
|
version = "0.3.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-io"
|
||||||
|
version = "0.3.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-macro"
|
||||||
|
version = "0.3.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-sink"
|
||||||
|
version = "0.3.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-task"
|
||||||
|
version = "0.3.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-timer"
|
||||||
|
version = "3.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-util"
|
||||||
|
version = "0.3.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"futures-macro",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"memchr",
|
||||||
|
"pin-project-lite",
|
||||||
|
"pin-utils",
|
||||||
|
"slab",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "globset"
|
||||||
|
version = "0.4.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"bstr",
|
||||||
|
"fnv",
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "globwalk"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"ignore",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@ -95,6 +337,32 @@ version = "0.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ignore"
|
||||||
|
version = "0.4.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492"
|
||||||
|
dependencies = [
|
||||||
|
"globset",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"memchr",
|
||||||
|
"regex",
|
||||||
|
"same-file",
|
||||||
|
"thread_local",
|
||||||
|
"walkdir",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instant"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-lifetimes"
|
name = "io-lifetimes"
|
||||||
version = "1.0.8"
|
version = "1.0.8"
|
||||||
@ -133,6 +401,21 @@ dependencies = [
|
|||||||
"windows-sys 0.45.0",
|
"windows-sys 0.45.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.140"
|
version = "0.2.140"
|
||||||
@ -145,6 +428,36 @@ version = "0.1.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "normalize-line-endings"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.17.1"
|
version = "1.17.1"
|
||||||
@ -157,6 +470,49 @@ version = "6.4.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-utils"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "predicates"
|
||||||
|
version = "3.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ba7d6ead3e3966038f68caa9fc1f860185d95a793180bbcfe0d0da47b3961ed"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"difflib",
|
||||||
|
"float-cmp",
|
||||||
|
"itertools",
|
||||||
|
"normalize-line-endings",
|
||||||
|
"predicates-core",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "predicates-core"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "predicates-tree"
|
||||||
|
version = "1.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf"
|
||||||
|
dependencies = [
|
||||||
|
"predicates-core",
|
||||||
|
"termtree",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-error"
|
name = "proc-macro-error"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
@ -199,23 +555,95 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rs-aggregate"
|
name = "rs-aggregate"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"assert_cmd",
|
||||||
|
"assert_fs",
|
||||||
"clap",
|
"clap",
|
||||||
"clio",
|
"clio",
|
||||||
|
"glob",
|
||||||
"ipnet",
|
"ipnet",
|
||||||
"iprange",
|
"iprange",
|
||||||
|
"predicates",
|
||||||
|
"rstest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rstest"
|
||||||
|
version = "0.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b07f2d176c472198ec1e6551dc7da28f1c089652f66a7b722676c2238ebc0edf"
|
||||||
|
dependencies = [
|
||||||
|
"futures",
|
||||||
|
"futures-timer",
|
||||||
|
"rstest_macros",
|
||||||
|
"rustc_version",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rstest_macros"
|
||||||
|
version = "0.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7229b505ae0706e64f37ffc54a9c163e11022a6636d58fe1f3f52018257ff9f7"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustc_version",
|
||||||
|
"syn",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.36.9"
|
version = "0.36.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc"
|
checksum = "2fe885c3a125aa45213b68cc1472a49880cb5923dc23f522ad2791b882228778"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 1.3.2",
|
||||||
"errno",
|
"errno",
|
||||||
"io-lifetimes",
|
"io-lifetimes",
|
||||||
"libc",
|
"libc",
|
||||||
@ -223,6 +651,36 @@ dependencies = [
|
|||||||
"windows-sys 0.45.0",
|
"windows-sys 0.45.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "same-file"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.157"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "707de5fcf5df2b5788fca98dd7eab490bc2fd9b7ef1404defc462833b83f25ca"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slab"
|
||||||
|
version = "0.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
@ -240,6 +698,19 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempfile"
|
||||||
|
version = "3.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"fastrand",
|
||||||
|
"redox_syscall",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys 0.42.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termcolor"
|
name = "termcolor"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@ -249,6 +720,22 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termtree"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.8"
|
version = "1.0.8"
|
||||||
@ -261,6 +748,25 @@ version = "0.9.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wait-timeout"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "2.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
|
||||||
|
dependencies = [
|
||||||
|
"same-file",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
15
Cargo.toml
15
Cargo.toml
@ -1,9 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rs-aggregate"
|
name = "rs-aggregate"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
authors = ["Keenan Tims <ktims@gotroot.ca>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
description = "Aggregate a list of IP prefixes into their minimum equivalent representation"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
readme = "README.md"
|
||||||
|
repository = "https://github.com/ktims/rs-aggregate"
|
||||||
|
license = "MIT"
|
||||||
|
categories = ["network-programming"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.1.8", features = ["derive"] }
|
clap = { version = "4.1.8", features = ["derive"] }
|
||||||
@ -11,5 +15,12 @@ clio = { version = "0.2.7", features = ["clap-parse"] }
|
|||||||
ipnet = "2.7.1"
|
ipnet = "2.7.1"
|
||||||
iprange = "0.6.7"
|
iprange = "0.6.7"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
assert_cmd = "2.0.10"
|
||||||
|
assert_fs = "1.0.12"
|
||||||
|
predicates = "3.0.1"
|
||||||
|
rstest = "0.16.0"
|
||||||
|
glob = "0.3.1"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "rs-aggregate"
|
name = "rs-aggregate"
|
20
README.md
20
README.md
@ -1,6 +1,24 @@
|
|||||||
# rs-aggregate
|
# rs-aggregate
|
||||||
rs-aggregate will aggregate an unsorted list of IP prefixes
|
rs-aggregate will aggregate an unsorted list of IP prefixes
|
||||||
|
|
||||||
Intended to be [aggregate6](https://github.com/job/aggregate6) with better performance.
|
Intended to be a drop-in replacement for [aggregate6](https://github.com/job/aggregate6) with better performance.
|
||||||
|
|
||||||
Takes a list of whitespace-separated IPs or IP networks and aggregates them to their minimal representation.
|
Takes a list of whitespace-separated IPs or IP networks and aggregates them to their minimal representation.
|
||||||
|
|
||||||
|
## Known discrepancies with `aggregate6`
|
||||||
|
|
||||||
|
* `rs-aggregate` accepts subnet and wilcard mask formats in addition to CIDR, ie all these are valid and equivalent:
|
||||||
|
* `1.1.1.0/255.255.255.0`
|
||||||
|
* `1.1.1.0/0.0.0.255`
|
||||||
|
* `1.1.1.0/24`
|
||||||
|
* `-m/--max-prefixlen` supports different maximums for each address family as ipv4,ipv6 format
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
Performance comparison of `rs-aggregate` vs `aggregate6`. A speedup of >100x is achieved on DFZ data.
|
||||||
|
|
||||||
|
Full DFZ (1154968 total, 202729 aggregates):
|
||||||
|
![dfz perf comparison](perfdata/perfcomp_all.png)
|
||||||
|
|
||||||
|
IPv4 DFZ (968520 total, 154061 aggregates):
|
||||||
|
![ipv4 dfz perf comparison](perfdata/perfcomp_v4.png)
|
313
src/iputils.rs
313
src/iputils.rs
@ -1,7 +1,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
error::Error,
|
error::Error,
|
||||||
fmt::Display,
|
fmt::Display,
|
||||||
net::{IpAddr, Ipv4Addr},
|
net::{IpAddr, Ipv4Addr, Ipv6Addr},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -19,21 +19,23 @@ impl IpBothRange {
|
|||||||
IpBothRange::default()
|
IpBothRange::default()
|
||||||
}
|
}
|
||||||
pub fn add(&mut self, net: IpOrNet) {
|
pub fn add(&mut self, net: IpOrNet) {
|
||||||
match net {
|
match net.net {
|
||||||
IpOrNet::IpNet(net) => match net {
|
|
||||||
IpNet::V4(v4_net) => drop(self.v4.add(v4_net)),
|
IpNet::V4(v4_net) => drop(self.v4.add(v4_net)),
|
||||||
IpNet::V6(v6_net) => drop(self.v6.add(v6_net)),
|
IpNet::V6(v6_net) => drop(self.v6.add(v6_net)),
|
||||||
},
|
|
||||||
IpOrNet::IpAddr(addr) => match addr {
|
|
||||||
IpAddr::V4(v4_addr) => drop(self.v4.add(v4_addr.into())),
|
|
||||||
IpAddr::V6(v6_addr) => drop(self.v6.add(v6_addr.into())),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn simplify(&mut self) {
|
pub fn simplify(&mut self) {
|
||||||
self.v4.simplify();
|
self.v4.simplify();
|
||||||
self.v6.simplify();
|
self.v6.simplify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn v4_iter(&self) -> IpRangeIter<Ipv4Net> {
|
||||||
|
self.v4.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn v6_iter(&self) -> IpRangeIter<Ipv6Net> {
|
||||||
|
self.v6.iter()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct IpBothRangeIter<'a> {
|
pub struct IpBothRangeIter<'a> {
|
||||||
@ -76,9 +78,9 @@ impl<'a> IntoIterator for &'a IpBothRange {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum IpOrNet {
|
#[derive(Debug, PartialEq)]
|
||||||
IpNet(IpNet),
|
pub struct IpOrNet {
|
||||||
IpAddr(IpAddr),
|
pub net: IpNet,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -145,13 +147,13 @@ impl IpOrNet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn prefix_len(&self) -> u8 {
|
pub fn prefix_len(&self) -> u8 {
|
||||||
match self {
|
self.net.prefix_len()
|
||||||
Self::IpNet(net) => net.prefix_len(),
|
|
||||||
Self::IpAddr(addr) => match addr {
|
|
||||||
IpAddr::V4(_) => 32,
|
|
||||||
IpAddr::V6(_) => 128,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
pub fn is_ipv4(&self) -> bool {
|
||||||
|
self.net.network().is_ipv4()
|
||||||
|
}
|
||||||
|
pub fn is_ipv6(&self) -> bool {
|
||||||
|
self.net.network().is_ipv6()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,14 +168,287 @@ impl FromStr for IpOrNet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for IpOrNet {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.net.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<IpNet> for IpOrNet {
|
impl From<IpNet> for IpOrNet {
|
||||||
fn from(net: IpNet) -> Self {
|
fn from(net: IpNet) -> Self {
|
||||||
IpOrNet::IpNet(net)
|
IpOrNet { net }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<IpAddr> for IpOrNet {
|
impl From<IpAddr> for IpOrNet {
|
||||||
fn from(addr: IpAddr) -> Self {
|
fn from(addr: IpAddr) -> Self {
|
||||||
IpOrNet::IpAddr(addr)
|
IpOrNet { net: addr.into() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Ipv4Net> for IpOrNet {
|
||||||
|
fn from(net: Ipv4Net) -> Self {
|
||||||
|
IpOrNet { net: net.into() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Ipv6Net> for IpOrNet {
|
||||||
|
fn from(net: Ipv6Net) -> Self {
|
||||||
|
IpOrNet { net: net.into() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Ipv4Addr> for IpOrNet {
|
||||||
|
fn from(addr: Ipv4Addr) -> Self {
|
||||||
|
IpOrNet {
|
||||||
|
net: IpAddr::from(addr).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Ipv6Addr> for IpOrNet {
|
||||||
|
fn from(addr: Ipv6Addr) -> Self {
|
||||||
|
IpOrNet {
|
||||||
|
net: IpAddr::from(addr).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PrefixlenPair {
|
||||||
|
pub v4: u8,
|
||||||
|
pub v6: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PrefixlenPair {
|
||||||
|
fn default() -> Self {
|
||||||
|
PrefixlenPair { v4: 32, v6: 128 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for PrefixlenPair {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(format!("{},{}", self.v4, self.v6).as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<IpOrNet> for PrefixlenPair {
|
||||||
|
fn eq(&self, other: &IpOrNet) -> bool {
|
||||||
|
match other.net {
|
||||||
|
IpNet::V4(net) => self.v4 == net.prefix_len(),
|
||||||
|
IpNet::V6(net) => self.v6 == net.prefix_len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<PrefixlenPair> for PrefixlenPair {
|
||||||
|
fn eq(&self, other: &PrefixlenPair) -> bool {
|
||||||
|
self.v4 == other.v4 && self.v6 == other.v6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd<IpOrNet> for PrefixlenPair {
|
||||||
|
fn ge(&self, other: &IpOrNet) -> bool {
|
||||||
|
match other.net {
|
||||||
|
IpNet::V4(net) => self.v4 >= net.prefix_len(),
|
||||||
|
IpNet::V6(net) => self.v6 >= net.prefix_len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn gt(&self, other: &IpOrNet) -> bool {
|
||||||
|
match other.net {
|
||||||
|
IpNet::V4(net) => self.v4 > net.prefix_len(),
|
||||||
|
IpNet::V6(net) => self.v6 > net.prefix_len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn le(&self, other: &IpOrNet) -> bool {
|
||||||
|
match other.net {
|
||||||
|
IpNet::V4(net) => self.v4 <= net.prefix_len(),
|
||||||
|
IpNet::V6(net) => self.v6 <= net.prefix_len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn lt(&self, other: &IpOrNet) -> bool {
|
||||||
|
match other.net {
|
||||||
|
IpNet::V4(net) => self.v4 < net.prefix_len(),
|
||||||
|
IpNet::V6(net) => self.v6 < net.prefix_len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn partial_cmp(&self, other: &IpOrNet) -> Option<std::cmp::Ordering> {
|
||||||
|
match other.net {
|
||||||
|
IpNet::V4(net) => self.v4.partial_cmp(&net.prefix_len()),
|
||||||
|
IpNet::V6(net) => self.v6.partial_cmp(&net.prefix_len()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ParsePrefixlenError {
|
||||||
|
msg: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ParsePrefixlenError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(self.msg.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for ParsePrefixlenError {}
|
||||||
|
|
||||||
|
impl FromStr for PrefixlenPair {
|
||||||
|
type Err = ParsePrefixlenError;
|
||||||
|
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||||
|
match s.split_once(',') {
|
||||||
|
Some(pair) => {
|
||||||
|
let v4 = u8::from_str(pair.0).or(Err(ParsePrefixlenError {
|
||||||
|
msg: "Unable to parse integer".to_owned(),
|
||||||
|
}))?;
|
||||||
|
let v6 = u8::from_str(pair.1).or(Err(ParsePrefixlenError {
|
||||||
|
msg: "Unable to parse integer".to_owned(),
|
||||||
|
}))?;
|
||||||
|
if v4 > 32 || v6 > 128 {
|
||||||
|
return Err(ParsePrefixlenError {
|
||||||
|
msg: "Invalid prefix length".to_owned(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(PrefixlenPair { v4, v6 })
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let len = u8::from_str(s).or(Err(ParsePrefixlenError {
|
||||||
|
msg: "Unable to parse integer".to_owned(),
|
||||||
|
}))?;
|
||||||
|
if len > 128 {
|
||||||
|
return Err(ParsePrefixlenError {
|
||||||
|
msg: "Invalid prefix length".to_owned(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(PrefixlenPair { v4: len, v6: len })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use core::panic;
|
||||||
|
use std::net::Ipv6Addr;
|
||||||
|
const TEST_V4_ADDR: Ipv4Addr = Ipv4Addr::new(198, 51, 100, 123);
|
||||||
|
const TEST_V6_ADDR: Ipv6Addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0x23ab, 0xf007);
|
||||||
|
|
||||||
|
const TEST_V4_NET: Ipv4Net = match Ipv4Net::new(Ipv4Addr::new(192, 0, 2, 0), 24) {
|
||||||
|
Ok(net) => net,
|
||||||
|
Err(_) => panic!("Couldn't unwrap test vector"),
|
||||||
|
};
|
||||||
|
const TEST_V6_NET: Ipv6Net =
|
||||||
|
match Ipv6Net::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0x23ab, 0), 64) {
|
||||||
|
Ok(net) => net,
|
||||||
|
Err(_) => panic!("Couldn't unwrap test vector"),
|
||||||
|
};
|
||||||
|
|
||||||
|
const TEST_V4_ALLNET: Ipv4Net = match Ipv4Net::new(Ipv4Addr::new(0, 0, 0, 0), 0) {
|
||||||
|
Ok(net) => net,
|
||||||
|
Err(_) => panic!("Couldn't unwrap test vector"),
|
||||||
|
};
|
||||||
|
|
||||||
|
const TEST_V6_ALLNET: Ipv6Net = match Ipv6Net::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), 0) {
|
||||||
|
Ok(net) => net,
|
||||||
|
Err(_) => panic!("Couldn't unwrap test vector"),
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn parse_bare_v4() {
|
||||||
|
let ip: IpOrNet = "198.51.100.123".parse().unwrap();
|
||||||
|
assert_eq!(ip, TEST_V4_ADDR.into());
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_bare_v6() {
|
||||||
|
let ip: IpOrNet = "2001:db8::23ab:f007".parse().unwrap();
|
||||||
|
assert_eq!(ip, TEST_V6_ADDR.into());
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_cidr_v4() {
|
||||||
|
let net: IpOrNet = "192.0.2.0/24".parse().unwrap();
|
||||||
|
assert_eq!(net, TEST_V4_NET.into());
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_cidr_v4_min() {
|
||||||
|
let net: IpOrNet = "0.0.0.0/0".parse().unwrap();
|
||||||
|
assert_eq!(net, TEST_V4_ALLNET.into());
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_cidr_v4_max() {
|
||||||
|
let net: IpOrNet = "198.51.100.123/32".parse().unwrap();
|
||||||
|
assert_eq!(net, TEST_V4_ADDR.into());
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_cidr_v6() {
|
||||||
|
let net: IpOrNet = "2001:db8::23ab:0/64".parse().unwrap();
|
||||||
|
assert_eq!(net, TEST_V6_NET.into());
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_cidr_v6_min() {
|
||||||
|
let net: IpOrNet = "::/0".parse().unwrap();
|
||||||
|
assert_eq!(net, TEST_V6_ALLNET.into());
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_netmask_v4() {
|
||||||
|
let net: IpOrNet = "192.0.2.0/255.255.255.0".parse().unwrap();
|
||||||
|
assert_eq!(net, TEST_V4_NET.into());
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_wildmask_v4() {
|
||||||
|
let net: IpOrNet = "192.0.2.0/0.0.0.255".parse().unwrap();
|
||||||
|
assert_eq!(net, TEST_V4_NET.into());
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn reject_v4_mask_v6() {
|
||||||
|
let _net: IpOrNet = "2001:db8::23ab:0/255.255.255.0".parse().unwrap();
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn reject_v6_mask_v6() {
|
||||||
|
let _net: IpOrNet = "2001:db8::23ab:0/ffff:ffff:ffff:ffff:ffff:ffff:ffff:0"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn reject_v4_invalid_pfxlen() {
|
||||||
|
let _net: IpOrNet = "192.0.2.0/33".parse().unwrap();
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn reject_v6_invalid_pfxlen() {
|
||||||
|
let _net: IpOrNet = "2001:db8::32ab:0/129".parse().unwrap();
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_single_prefixlen() {
|
||||||
|
let pfxlen: PrefixlenPair = "20".parse().unwrap();
|
||||||
|
assert_eq!(pfxlen, PrefixlenPair { v4: 20, v6: 20 });
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_pair_prefixlen() {
|
||||||
|
let pfxlen: PrefixlenPair = "20,32".parse().unwrap();
|
||||||
|
assert_eq!(pfxlen, PrefixlenPair { v4: 20, v6: 32 });
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn reject_single_prefixlen_invalid() {
|
||||||
|
let _pfxlen: PrefixlenPair = "129".parse().unwrap();
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn reject_pair_prefixlen_invalid_v4() {
|
||||||
|
let _pfxlen: PrefixlenPair = "33,32".parse().unwrap();
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn reject_pair_prefixlen_invalid_v6() {
|
||||||
|
let _pfxlen: PrefixlenPair = "32,129".parse().unwrap();
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn reject_single_prefixlen_negative() {
|
||||||
|
let _pfxlen: PrefixlenPair = "-32".parse().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
72
src/main.rs
72
src/main.rs
@ -2,7 +2,7 @@ extern crate ipnet;
|
|||||||
extern crate iprange;
|
extern crate iprange;
|
||||||
|
|
||||||
mod iputils;
|
mod iputils;
|
||||||
use iputils::{IpBothRange, IpOrNet};
|
use iputils::{IpBothRange, IpOrNet, PrefixlenPair};
|
||||||
|
|
||||||
use clio::*;
|
use clio::*;
|
||||||
use std::io::BufRead;
|
use std::io::BufRead;
|
||||||
@ -13,27 +13,37 @@ use clap::Parser;
|
|||||||
#[command(author, version, about, long_about=None)]
|
#[command(author, version, about, long_about=None)]
|
||||||
struct Args {
|
struct Args {
|
||||||
#[clap(value_parser, default_value = "-")]
|
#[clap(value_parser, default_value = "-")]
|
||||||
input: Input,
|
input: Vec<Input>,
|
||||||
#[arg(
|
#[structopt(
|
||||||
short,
|
short,
|
||||||
long,
|
long,
|
||||||
default_value = "128",
|
default_value = "32,128",
|
||||||
help = "Sets the maximum prefix length for entries read. Longer prefixes will be discarded prior to processing."
|
help = "Maximum prefix length for prefixes read. Single value applies to IPv4 and IPv6, comma-separated [IPv4],[IPv6]."
|
||||||
)]
|
)]
|
||||||
max_prefixlen: u8,
|
max_prefixlen: PrefixlenPair,
|
||||||
#[arg(short, long, help = "truncate IP/mask to network/mask (else ignore)")]
|
#[arg(short, long, help = "truncate IP/mask to network/mask (else ignore)")]
|
||||||
truncate: bool,
|
truncate: bool,
|
||||||
#[arg(id="4", short, help = "Only output IPv4 prefixes", conflicts_with("6"))]
|
#[arg(
|
||||||
|
id = "4",
|
||||||
|
short,
|
||||||
|
help = "Only output IPv4 prefixes",
|
||||||
|
conflicts_with("6")
|
||||||
|
)]
|
||||||
only_v4: bool,
|
only_v4: bool,
|
||||||
#[arg(id="6", short, help = "Only output IPv6 prefixes", conflicts_with("4"))]
|
#[arg(
|
||||||
|
id = "6",
|
||||||
|
short,
|
||||||
|
help = "Only output IPv6 prefixes",
|
||||||
|
conflicts_with("4")
|
||||||
|
)]
|
||||||
only_v6: bool,
|
only_v6: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Args {
|
impl Default for Args {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Args {
|
Args {
|
||||||
input: clio::Input::default(),
|
input: Vec::from([clio::Input::default()]),
|
||||||
max_prefixlen: 128,
|
max_prefixlen: PrefixlenPair::default(),
|
||||||
truncate: false,
|
truncate: false,
|
||||||
only_v4: false,
|
only_v4: false,
|
||||||
only_v6: false,
|
only_v6: false,
|
||||||
@ -48,56 +58,64 @@ struct IpParseError {
|
|||||||
problem: String,
|
problem: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Errors = Vec<IpParseError>;
|
// type Errors = Vec<IpParseError>;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct App {
|
struct App {
|
||||||
args: Args,
|
args: Args,
|
||||||
prefixes: IpBothRange,
|
prefixes: IpBothRange,
|
||||||
errors: Errors,
|
// errors: Errors,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
fn add_prefix(&mut self, pfx: IpOrNet) {
|
fn add_prefix(&mut self, pfx: IpOrNet) {
|
||||||
// Parser accepts host bits set, so detect that case and error if not truncate mode
|
// Parser accepts host bits set, so detect that case and error if not truncate mode
|
||||||
|
// Note: aggregate6 errors in this case regardless of -4, -6 so do the same
|
||||||
if !self.args.truncate {
|
if !self.args.truncate {
|
||||||
match pfx {
|
if pfx.net.addr() != pfx.net.network() {
|
||||||
IpOrNet::IpNet(net) => {
|
eprintln!("ERROR: '{}' is not a valid IP network, ignoring.", pfx);
|
||||||
if net.addr() != net.network() {
|
|
||||||
eprintln!("ERROR: '{}' is not a valid IP network, ignoring.", net);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IpOrNet::IpAddr(_) => (),
|
// Don't bother saving if we won't display.
|
||||||
|
if self.args.only_v4 && pfx.is_ipv6() {
|
||||||
|
return;
|
||||||
|
} else if self.args.only_v6 && pfx.is_ipv4() {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
if self.args.max_prefixlen >= pfx {
|
||||||
if pfx.prefix_len() <= self.args.max_prefixlen {
|
|
||||||
self.prefixes.add(pfx);
|
self.prefixes.add(pfx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn simplify_input(&mut self) {
|
fn consume_input(&mut self, input: &mut Input) {
|
||||||
for line in self.args.input.to_owned().lock().lines() {
|
for line in input.lock().lines() {
|
||||||
for net in line.unwrap().split_whitespace().to_owned() {
|
for net in line.unwrap().split_whitespace().to_owned() {
|
||||||
let pnet = net.parse::<IpOrNet>();
|
let pnet = net.parse::<IpOrNet>();
|
||||||
match pnet {
|
match pnet {
|
||||||
Ok(pnet) => self.add_prefix(pnet),
|
Ok(pnet) => self.add_prefix(pnet),
|
||||||
Err(e) => {
|
Err(_e) => {
|
||||||
self.errors.push(IpParseError {
|
// self.errors.push(IpParseError {
|
||||||
ip: net.to_string(),
|
// ip: net.to_string(),
|
||||||
problem: e.to_string(),
|
// problem: e.to_string(),
|
||||||
});
|
// });
|
||||||
eprintln!("ERROR: '{}' is not a valid IP network, ignoring.", net);
|
eprintln!("ERROR: '{}' is not a valid IP network, ignoring.", net);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
fn simplify_inputs(&mut self) {
|
||||||
|
let inputs = self.args.input.to_owned();
|
||||||
|
for mut input in inputs {
|
||||||
|
self.consume_input(&mut input);
|
||||||
|
}
|
||||||
self.prefixes.simplify();
|
self.prefixes.simplify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main(&mut self) {
|
fn main(&mut self) {
|
||||||
self.args = Args::parse();
|
self.args = Args::parse();
|
||||||
|
|
||||||
self.simplify_input();
|
self.simplify_inputs();
|
||||||
|
|
||||||
for net in &self.prefixes {
|
for net in &self.prefixes {
|
||||||
println!("{}", net);
|
println!("{}", net);
|
||||||
|
202729
test-data/dfz_combined/expected
Normal file
202729
test-data/dfz_combined/expected
Normal file
File diff suppressed because it is too large
Load Diff
1154968
test-data/dfz_combined/input
Normal file
1154968
test-data/dfz_combined/input
Normal file
File diff suppressed because it is too large
Load Diff
33575
test-data/max_pfxlen/expected
Normal file
33575
test-data/max_pfxlen/expected
Normal file
File diff suppressed because it is too large
Load Diff
1
test-data/max_pfxlen/input
Symbolic link
1
test-data/max_pfxlen/input
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../dfz_combined/input
|
54049
test-data/max_pfxlen_split/expected
Normal file
54049
test-data/max_pfxlen_split/expected
Normal file
File diff suppressed because it is too large
Load Diff
1
test-data/max_pfxlen_split/input
Symbolic link
1
test-data/max_pfxlen_split/input
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../dfz_combined/input
|
21586
test-data/multi_input/expected
Normal file
21586
test-data/multi_input/expected
Normal file
File diff suppressed because it is too large
Load Diff
50000
test-data/multi_input/input1
Normal file
50000
test-data/multi_input/input1
Normal file
File diff suppressed because it is too large
Load Diff
50000
test-data/multi_input/input2
Normal file
50000
test-data/multi_input/input2
Normal file
File diff suppressed because it is too large
Load Diff
154061
test-data/v4_only/expected
Normal file
154061
test-data/v4_only/expected
Normal file
File diff suppressed because it is too large
Load Diff
1
test-data/v4_only/input
Symbolic link
1
test-data/v4_only/input
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../dfz_combined/input
|
48668
test-data/v6_only/expected
Normal file
48668
test-data/v6_only/expected
Normal file
File diff suppressed because it is too large
Load Diff
1
test-data/v6_only/input
Symbolic link
1
test-data/v6_only/input
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../dfz_combined/input
|
86
tests/cli.rs
Normal file
86
tests/cli.rs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
use assert_cmd::Command;
|
||||||
|
use glob::glob;
|
||||||
|
use predicates::prelude::*; // Used for writing assertions
|
||||||
|
use rstest::*;
|
||||||
|
use std::{error::Error, fs::File, io::Read, path::Path};
|
||||||
|
|
||||||
|
// Really should normalize the data (lex sort) before comparison
|
||||||
|
#[rstest]
|
||||||
|
#[case("test-data/dfz_combined", "")] // Basic aggregation test
|
||||||
|
#[case("test-data/max_pfxlen", "-m 20")] // Filter on prefix length
|
||||||
|
#[case("test-data/max_pfxlen_split", "-m 20,32")] // Filter on prefix length (split v4/v6)
|
||||||
|
#[case("test-data/v4_only", "-4")] // Filter v4 only
|
||||||
|
#[case("test-data/v6_only", "-6")] // Filter v4 only
|
||||||
|
fn dfz_test(#[case] path: &str, #[case] args: &str) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut cmd = Command::cargo_bin("rs-aggregate")?;
|
||||||
|
let in_path = Path::new(path).join("input");
|
||||||
|
let expect_path = Path::new(path).join("expected");
|
||||||
|
let mut expect_file = File::open(expect_path)?;
|
||||||
|
let mut expect_data: Vec<u8> =
|
||||||
|
Vec::with_capacity(expect_file.metadata()?.len().try_into().unwrap());
|
||||||
|
expect_file.read_to_end(&mut expect_data)?;
|
||||||
|
|
||||||
|
let assert = cmd
|
||||||
|
.arg(in_path)
|
||||||
|
.args(args.split_whitespace())
|
||||||
|
.timeout(std::time::Duration::from_secs(30))
|
||||||
|
.assert();
|
||||||
|
|
||||||
|
assert
|
||||||
|
.success()
|
||||||
|
.stdout(predicate::eq(expect_data))
|
||||||
|
.stderr(predicate::str::is_empty());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case("2001:db8::23ab:f007/64", "2001:db8::/64")]
|
||||||
|
#[case("198.51.100.123/24", "198.51.100.0/24")]
|
||||||
|
fn truncate_test(#[case] input: &str, #[case] expect: &str) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut cmd = Command::cargo_bin("rs-aggregate")?;
|
||||||
|
|
||||||
|
let assert = cmd.write_stdin(input).assert();
|
||||||
|
assert
|
||||||
|
.success()
|
||||||
|
.stdout(predicate::str::is_empty())
|
||||||
|
.stderr(predicate::eq(format!(
|
||||||
|
"ERROR: '{}' is not a valid IP network, ignoring.\n",
|
||||||
|
input
|
||||||
|
)));
|
||||||
|
|
||||||
|
let assert = cmd.arg("-t").write_stdin(input).assert();
|
||||||
|
assert
|
||||||
|
.success()
|
||||||
|
.stdout(predicate::eq(format!("{}\n", expect)))
|
||||||
|
.stderr(predicate::str::is_empty());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case("test-data/multi_input", "")]
|
||||||
|
fn multi_input_test(#[case] path: &str, #[case] args: &str) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut cmd = Command::cargo_bin("rs-aggregate")?;
|
||||||
|
|
||||||
|
let inputs = glob((path.to_owned() + "/input*").as_str())?;
|
||||||
|
|
||||||
|
let expect_path = Path::new(path).join("expected");
|
||||||
|
let mut expect_file = File::open(expect_path)?;
|
||||||
|
let mut expect_data: Vec<u8> =
|
||||||
|
Vec::with_capacity(expect_file.metadata()?.len().try_into().unwrap());
|
||||||
|
expect_file.read_to_end(&mut expect_data)?;
|
||||||
|
|
||||||
|
let assert = cmd
|
||||||
|
.args(args.split_whitespace())
|
||||||
|
.args(inputs.map(|x| x.unwrap()))
|
||||||
|
.timeout(std::time::Duration::from_secs(30))
|
||||||
|
.assert();
|
||||||
|
|
||||||
|
assert
|
||||||
|
.success()
|
||||||
|
.stdout(predicate::eq(expect_data))
|
||||||
|
.stderr(predicate::str::is_empty());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user