├── .github └── dependabot.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── cidr-calc-web ├── .gitignore ├── Cargo.toml ├── index.html └── src │ ├── lib.rs │ └── utils.rs └── cidr-calc ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── example-inputs ├── let.txt ├── remove_single.txt ├── remove_single_reverse.txt └── remove_single_v6.txt └── src ├── data.rs ├── eval.rs ├── lib.rs ├── main.rs ├── parser.rs └── syntax.pest /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "08:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anstream" 7 | version = "0.6.11" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" 10 | dependencies = [ 11 | "anstyle", 12 | "anstyle-parse", 13 | "anstyle-query", 14 | "anstyle-wincon", 15 | "colorchoice", 16 | "utf8parse", 17 | ] 18 | 19 | [[package]] 20 | name = "anstyle" 21 | version = "1.0.8" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" 24 | 25 | [[package]] 26 | name = "anstyle-parse" 27 | version = "0.2.3" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" 30 | dependencies = [ 31 | "utf8parse", 32 | ] 33 | 34 | [[package]] 35 | name = "anstyle-query" 36 | version = "1.0.2" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" 39 | dependencies = [ 40 | "windows-sys", 41 | ] 42 | 43 | [[package]] 44 | name = "anstyle-wincon" 45 | version = "3.0.2" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" 48 | dependencies = [ 49 | "anstyle", 50 | "windows-sys", 51 | ] 52 | 53 | [[package]] 54 | name = "anyhow" 55 | version = "1.0.93" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" 58 | 59 | [[package]] 60 | name = "bitflags" 61 | version = "2.4.2" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" 64 | 65 | [[package]] 66 | name = "bitmaps" 67 | version = "2.1.0" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" 70 | dependencies = [ 71 | "typenum", 72 | ] 73 | 74 | [[package]] 75 | name = "block-buffer" 76 | version = "0.10.4" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 79 | dependencies = [ 80 | "generic-array", 81 | ] 82 | 83 | [[package]] 84 | name = "bumpalo" 85 | version = "3.14.0" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" 88 | 89 | [[package]] 90 | name = "cfg-if" 91 | version = "1.0.0" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 94 | 95 | [[package]] 96 | name = "cidr-calculator" 97 | version = "0.2.0" 98 | dependencies = [ 99 | "anyhow", 100 | "clap", 101 | "im", 102 | "pest", 103 | "pest_derive", 104 | "rustyline", 105 | ] 106 | 107 | [[package]] 108 | name = "cidr-calculator-web" 109 | version = "0.1.0" 110 | dependencies = [ 111 | "cidr-calculator", 112 | "console_error_panic_hook", 113 | "wasm-bindgen", 114 | ] 115 | 116 | [[package]] 117 | name = "clap" 118 | version = "4.5.20" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" 121 | dependencies = [ 122 | "clap_builder", 123 | "clap_derive", 124 | ] 125 | 126 | [[package]] 127 | name = "clap_builder" 128 | version = "4.5.20" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" 131 | dependencies = [ 132 | "anstream", 133 | "anstyle", 134 | "clap_lex", 135 | "strsim", 136 | ] 137 | 138 | [[package]] 139 | name = "clap_derive" 140 | version = "4.5.18" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" 143 | dependencies = [ 144 | "heck", 145 | "proc-macro2", 146 | "quote", 147 | "syn", 148 | ] 149 | 150 | [[package]] 151 | name = "clap_lex" 152 | version = "0.7.0" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" 155 | 156 | [[package]] 157 | name = "clipboard-win" 158 | version = "5.2.0" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "12f9a0700e0127ba15d1d52dd742097f821cd9c65939303a44d970465040a297" 161 | dependencies = [ 162 | "error-code", 163 | ] 164 | 165 | [[package]] 166 | name = "colorchoice" 167 | version = "1.0.0" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 170 | 171 | [[package]] 172 | name = "console_error_panic_hook" 173 | version = "0.1.7" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" 176 | dependencies = [ 177 | "cfg-if", 178 | "wasm-bindgen", 179 | ] 180 | 181 | [[package]] 182 | name = "cpufeatures" 183 | version = "0.2.12" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" 186 | dependencies = [ 187 | "libc", 188 | ] 189 | 190 | [[package]] 191 | name = "crypto-common" 192 | version = "0.1.6" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 195 | dependencies = [ 196 | "generic-array", 197 | "typenum", 198 | ] 199 | 200 | [[package]] 201 | name = "digest" 202 | version = "0.10.7" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 205 | dependencies = [ 206 | "block-buffer", 207 | "crypto-common", 208 | ] 209 | 210 | [[package]] 211 | name = "endian-type" 212 | version = "0.1.2" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" 215 | 216 | [[package]] 217 | name = "errno" 218 | version = "0.3.8" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" 221 | dependencies = [ 222 | "libc", 223 | "windows-sys", 224 | ] 225 | 226 | [[package]] 227 | name = "error-code" 228 | version = "3.2.0" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" 231 | 232 | [[package]] 233 | name = "fd-lock" 234 | version = "4.0.2" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" 237 | dependencies = [ 238 | "cfg-if", 239 | "rustix", 240 | "windows-sys", 241 | ] 242 | 243 | [[package]] 244 | name = "generic-array" 245 | version = "0.14.7" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 248 | dependencies = [ 249 | "typenum", 250 | "version_check", 251 | ] 252 | 253 | [[package]] 254 | name = "heck" 255 | version = "0.5.0" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 258 | 259 | [[package]] 260 | name = "home" 261 | version = "0.5.9" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" 264 | dependencies = [ 265 | "windows-sys", 266 | ] 267 | 268 | [[package]] 269 | name = "im" 270 | version = "15.1.0" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" 273 | dependencies = [ 274 | "bitmaps", 275 | "rand_core", 276 | "rand_xoshiro", 277 | "sized-chunks", 278 | "typenum", 279 | "version_check", 280 | ] 281 | 282 | [[package]] 283 | name = "libc" 284 | version = "0.2.153" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" 287 | 288 | [[package]] 289 | name = "linux-raw-sys" 290 | version = "0.4.13" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" 293 | 294 | [[package]] 295 | name = "log" 296 | version = "0.4.20" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 299 | 300 | [[package]] 301 | name = "memchr" 302 | version = "2.7.1" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 305 | 306 | [[package]] 307 | name = "nibble_vec" 308 | version = "0.1.0" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" 311 | dependencies = [ 312 | "smallvec", 313 | ] 314 | 315 | [[package]] 316 | name = "nix" 317 | version = "0.27.1" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" 320 | dependencies = [ 321 | "bitflags", 322 | "cfg-if", 323 | "libc", 324 | ] 325 | 326 | [[package]] 327 | name = "once_cell" 328 | version = "1.19.0" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 331 | 332 | [[package]] 333 | name = "pest" 334 | version = "2.7.14" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" 337 | dependencies = [ 338 | "memchr", 339 | "thiserror", 340 | "ucd-trie", 341 | ] 342 | 343 | [[package]] 344 | name = "pest_derive" 345 | version = "2.7.13" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "4d3a6e3394ec80feb3b6393c725571754c6188490265c61aaf260810d6b95aa0" 348 | dependencies = [ 349 | "pest", 350 | "pest_generator", 351 | ] 352 | 353 | [[package]] 354 | name = "pest_generator" 355 | version = "2.7.13" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "94429506bde1ca69d1b5601962c73f4172ab4726571a59ea95931218cb0e930e" 358 | dependencies = [ 359 | "pest", 360 | "pest_meta", 361 | "proc-macro2", 362 | "quote", 363 | "syn", 364 | ] 365 | 366 | [[package]] 367 | name = "pest_meta" 368 | version = "2.7.13" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "ac8a071862e93690b6e34e9a5fb8e33ff3734473ac0245b27232222c4906a33f" 371 | dependencies = [ 372 | "once_cell", 373 | "pest", 374 | "sha2", 375 | ] 376 | 377 | [[package]] 378 | name = "proc-macro2" 379 | version = "1.0.78" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" 382 | dependencies = [ 383 | "unicode-ident", 384 | ] 385 | 386 | [[package]] 387 | name = "quote" 388 | version = "1.0.35" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 391 | dependencies = [ 392 | "proc-macro2", 393 | ] 394 | 395 | [[package]] 396 | name = "radix_trie" 397 | version = "0.2.1" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" 400 | dependencies = [ 401 | "endian-type", 402 | "nibble_vec", 403 | ] 404 | 405 | [[package]] 406 | name = "rand_core" 407 | version = "0.6.4" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 410 | 411 | [[package]] 412 | name = "rand_xoshiro" 413 | version = "0.6.0" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" 416 | dependencies = [ 417 | "rand_core", 418 | ] 419 | 420 | [[package]] 421 | name = "rustix" 422 | version = "0.38.31" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" 425 | dependencies = [ 426 | "bitflags", 427 | "errno", 428 | "libc", 429 | "linux-raw-sys", 430 | "windows-sys", 431 | ] 432 | 433 | [[package]] 434 | name = "rustyline" 435 | version = "13.0.0" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "02a2d683a4ac90aeef5b1013933f6d977bd37d51ff3f4dad829d4931a7e6be86" 438 | dependencies = [ 439 | "bitflags", 440 | "cfg-if", 441 | "clipboard-win", 442 | "fd-lock", 443 | "home", 444 | "libc", 445 | "log", 446 | "memchr", 447 | "nix", 448 | "radix_trie", 449 | "unicode-segmentation", 450 | "unicode-width", 451 | "utf8parse", 452 | "winapi", 453 | ] 454 | 455 | [[package]] 456 | name = "sha2" 457 | version = "0.10.8" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" 460 | dependencies = [ 461 | "cfg-if", 462 | "cpufeatures", 463 | "digest", 464 | ] 465 | 466 | [[package]] 467 | name = "sized-chunks" 468 | version = "0.6.5" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" 471 | dependencies = [ 472 | "bitmaps", 473 | "typenum", 474 | ] 475 | 476 | [[package]] 477 | name = "smallvec" 478 | version = "1.13.1" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" 481 | 482 | [[package]] 483 | name = "strsim" 484 | version = "0.11.0" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" 487 | 488 | [[package]] 489 | name = "syn" 490 | version = "2.0.48" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" 493 | dependencies = [ 494 | "proc-macro2", 495 | "quote", 496 | "unicode-ident", 497 | ] 498 | 499 | [[package]] 500 | name = "thiserror" 501 | version = "1.0.56" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" 504 | dependencies = [ 505 | "thiserror-impl", 506 | ] 507 | 508 | [[package]] 509 | name = "thiserror-impl" 510 | version = "1.0.56" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" 513 | dependencies = [ 514 | "proc-macro2", 515 | "quote", 516 | "syn", 517 | ] 518 | 519 | [[package]] 520 | name = "typenum" 521 | version = "1.17.0" 522 | source = "registry+https://github.com/rust-lang/crates.io-index" 523 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 524 | 525 | [[package]] 526 | name = "ucd-trie" 527 | version = "0.1.6" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" 530 | 531 | [[package]] 532 | name = "unicode-ident" 533 | version = "1.0.12" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 536 | 537 | [[package]] 538 | name = "unicode-segmentation" 539 | version = "1.10.1" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" 542 | 543 | [[package]] 544 | name = "unicode-width" 545 | version = "0.1.11" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" 548 | 549 | [[package]] 550 | name = "utf8parse" 551 | version = "0.2.1" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 554 | 555 | [[package]] 556 | name = "version_check" 557 | version = "0.9.4" 558 | source = "registry+https://github.com/rust-lang/crates.io-index" 559 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 560 | 561 | [[package]] 562 | name = "wasm-bindgen" 563 | version = "0.2.93" 564 | source = "registry+https://github.com/rust-lang/crates.io-index" 565 | checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" 566 | dependencies = [ 567 | "cfg-if", 568 | "once_cell", 569 | "wasm-bindgen-macro", 570 | ] 571 | 572 | [[package]] 573 | name = "wasm-bindgen-backend" 574 | version = "0.2.93" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" 577 | dependencies = [ 578 | "bumpalo", 579 | "log", 580 | "once_cell", 581 | "proc-macro2", 582 | "quote", 583 | "syn", 584 | "wasm-bindgen-shared", 585 | ] 586 | 587 | [[package]] 588 | name = "wasm-bindgen-macro" 589 | version = "0.2.93" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" 592 | dependencies = [ 593 | "quote", 594 | "wasm-bindgen-macro-support", 595 | ] 596 | 597 | [[package]] 598 | name = "wasm-bindgen-macro-support" 599 | version = "0.2.93" 600 | source = "registry+https://github.com/rust-lang/crates.io-index" 601 | checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" 602 | dependencies = [ 603 | "proc-macro2", 604 | "quote", 605 | "syn", 606 | "wasm-bindgen-backend", 607 | "wasm-bindgen-shared", 608 | ] 609 | 610 | [[package]] 611 | name = "wasm-bindgen-shared" 612 | version = "0.2.93" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" 615 | 616 | [[package]] 617 | name = "winapi" 618 | version = "0.3.9" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 621 | dependencies = [ 622 | "winapi-i686-pc-windows-gnu", 623 | "winapi-x86_64-pc-windows-gnu", 624 | ] 625 | 626 | [[package]] 627 | name = "winapi-i686-pc-windows-gnu" 628 | version = "0.4.0" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 631 | 632 | [[package]] 633 | name = "winapi-x86_64-pc-windows-gnu" 634 | version = "0.4.0" 635 | source = "registry+https://github.com/rust-lang/crates.io-index" 636 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 637 | 638 | [[package]] 639 | name = "windows-sys" 640 | version = "0.52.0" 641 | source = "registry+https://github.com/rust-lang/crates.io-index" 642 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 643 | dependencies = [ 644 | "windows-targets", 645 | ] 646 | 647 | [[package]] 648 | name = "windows-targets" 649 | version = "0.52.0" 650 | source = "registry+https://github.com/rust-lang/crates.io-index" 651 | checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" 652 | dependencies = [ 653 | "windows_aarch64_gnullvm", 654 | "windows_aarch64_msvc", 655 | "windows_i686_gnu", 656 | "windows_i686_msvc", 657 | "windows_x86_64_gnu", 658 | "windows_x86_64_gnullvm", 659 | "windows_x86_64_msvc", 660 | ] 661 | 662 | [[package]] 663 | name = "windows_aarch64_gnullvm" 664 | version = "0.52.0" 665 | source = "registry+https://github.com/rust-lang/crates.io-index" 666 | checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" 667 | 668 | [[package]] 669 | name = "windows_aarch64_msvc" 670 | version = "0.52.0" 671 | source = "registry+https://github.com/rust-lang/crates.io-index" 672 | checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" 673 | 674 | [[package]] 675 | name = "windows_i686_gnu" 676 | version = "0.52.0" 677 | source = "registry+https://github.com/rust-lang/crates.io-index" 678 | checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" 679 | 680 | [[package]] 681 | name = "windows_i686_msvc" 682 | version = "0.52.0" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" 685 | 686 | [[package]] 687 | name = "windows_x86_64_gnu" 688 | version = "0.52.0" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" 691 | 692 | [[package]] 693 | name = "windows_x86_64_gnullvm" 694 | version = "0.52.0" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" 697 | 698 | [[package]] 699 | name = "windows_x86_64_msvc" 700 | version = "0.52.0" 701 | source = "registry+https://github.com/rust-lang/crates.io-index" 702 | checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" 703 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | 'cidr-calc', 5 | 'cidr-calc-web', 6 | ] 7 | resolver = "2" 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CIDR Calculator 2 | 3 | [![Crates.io Version](https://img.shields.io/crates/v/cidr-calculator)](https://crates.io/crates/cidr-calculator) 4 | 5 | CIDR addition / subtraction calculator. Maybe useful for setting route tables when metrics are not available (e.g. wireguard next-hop) 6 | 7 | ``` 8 | > let univ = ::/0 9 | > univ - 2001:da8::/56 10 | [ 11 | ::/3 12 | 2000::/16 13 | 2001::/21 14 | 2001:800::/22 15 | 2001:c00::/24 16 | 2001:d00::/25 17 | 2001:d80::/27 18 | 2001:da0::/29 19 | 2001:da8:0:100::/56 20 | ... 21 | ``` 22 | -------------------------------------------------------------------------------- /cidr-calc-web/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | bin/ 5 | pkg/ 6 | wasm-pack.log 7 | -------------------------------------------------------------------------------- /cidr-calc-web/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cidr-calculator-web" 3 | version = "0.1.0" 4 | authors = ["Liu Xiaoyi "] 5 | edition = "2021" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [features] 11 | default = ["console_error_panic_hook"] 12 | 13 | [dependencies] 14 | wasm-bindgen = "0.2.93" 15 | 16 | # The `console_error_panic_hook` crate provides better debugging of panics by 17 | # logging them with `console.error`. This is great for development, but requires 18 | # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for 19 | # code size when deploying. 20 | console_error_panic_hook = { version = "0.1.7", optional = true } 21 | 22 | cidr-calculator = { path = "../cidr-calc", default-features = false } 23 | -------------------------------------------------------------------------------- /cidr-calc-web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | CIDR Calculator 4 | 5 | 104 | 105 | 108 | 109 | 184 | 185 | 186 |
187 | Loading... 188 |
189 | 190 |
191 | 192 | -------------------------------------------------------------------------------- /cidr-calc-web/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod utils; 2 | 3 | use cidr_calculator::eval::Value; 4 | use wasm_bindgen::prelude::*; 5 | use cidr_calculator::parser::parse_single; 6 | use cidr_calculator::eval::eval_stmt; 7 | use cidr_calculator::eval::Scope; 8 | use cidr_calculator::eval::format; 9 | 10 | #[wasm_bindgen] 11 | #[derive(Default)] 12 | pub struct EvalState { 13 | scope: Scope 14 | } 15 | 16 | #[wasm_bindgen] 17 | pub fn create_state() -> EvalState { 18 | Default::default() 19 | } 20 | 21 | #[wasm_bindgen] 22 | pub fn print_scope(state: &EvalState) -> Vec { 23 | state.scope.keys().map(|e| e.to_owned()).collect() 24 | } 25 | 26 | #[wasm_bindgen] 27 | pub fn eval_input(state: &mut EvalState, input: String) -> Result, String> { 28 | let stmt = parse_single(&input).map_err(|e| e.to_string())?; 29 | let (v, s) = eval_stmt(&stmt, state.scope.clone()).map_err(|e| e.to_string())?; 30 | state.scope = s; 31 | 32 | match v { 33 | Value::Unit => { 34 | Ok(vec![]) 35 | } 36 | v => { 37 | Ok(format(&v).collect()) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /cidr-calc-web/src/utils.rs: -------------------------------------------------------------------------------- 1 | pub fn set_panic_hook() { 2 | // When the `console_error_panic_hook` feature is enabled, we can call the 3 | // `set_panic_hook` function at least once during initialization, and then 4 | // we will get better error messages if our code ever panics. 5 | // 6 | // For more details see 7 | // https://github.com/rustwasm/console_error_panic_hook#readme 8 | #[cfg(feature = "console_error_panic_hook")] 9 | console_error_panic_hook::set_once(); 10 | } 11 | -------------------------------------------------------------------------------- /cidr-calc/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anstream" 7 | version = "0.6.4" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" 10 | dependencies = [ 11 | "anstyle", 12 | "anstyle-parse", 13 | "anstyle-query", 14 | "anstyle-wincon", 15 | "colorchoice", 16 | "utf8parse", 17 | ] 18 | 19 | [[package]] 20 | name = "anstyle" 21 | version = "1.0.4" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" 24 | 25 | [[package]] 26 | name = "anstyle-parse" 27 | version = "0.2.2" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" 30 | dependencies = [ 31 | "utf8parse", 32 | ] 33 | 34 | [[package]] 35 | name = "anstyle-query" 36 | version = "1.0.0" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" 39 | dependencies = [ 40 | "windows-sys", 41 | ] 42 | 43 | [[package]] 44 | name = "anstyle-wincon" 45 | version = "3.0.1" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" 48 | dependencies = [ 49 | "anstyle", 50 | "windows-sys", 51 | ] 52 | 53 | [[package]] 54 | name = "anyhow" 55 | version = "1.0.75" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" 58 | 59 | [[package]] 60 | name = "bitflags" 61 | version = "1.3.2" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 64 | 65 | [[package]] 66 | name = "bitflags" 67 | version = "2.4.1" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" 70 | 71 | [[package]] 72 | name = "bitmaps" 73 | version = "2.1.0" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" 76 | dependencies = [ 77 | "typenum", 78 | ] 79 | 80 | [[package]] 81 | name = "block-buffer" 82 | version = "0.10.4" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 85 | dependencies = [ 86 | "generic-array", 87 | ] 88 | 89 | [[package]] 90 | name = "cfg-if" 91 | version = "1.0.0" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 94 | 95 | [[package]] 96 | name = "cidr-calculator" 97 | version = "0.1.0" 98 | dependencies = [ 99 | "anyhow", 100 | "clap", 101 | "im", 102 | "pest", 103 | "pest_derive", 104 | "rustyline", 105 | ] 106 | 107 | [[package]] 108 | name = "clap" 109 | version = "4.4.6" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" 112 | dependencies = [ 113 | "clap_builder", 114 | "clap_derive", 115 | ] 116 | 117 | [[package]] 118 | name = "clap_builder" 119 | version = "4.4.6" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" 122 | dependencies = [ 123 | "anstream", 124 | "anstyle", 125 | "clap_lex", 126 | "strsim", 127 | ] 128 | 129 | [[package]] 130 | name = "clap_derive" 131 | version = "4.4.2" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" 134 | dependencies = [ 135 | "heck", 136 | "proc-macro2", 137 | "quote", 138 | "syn", 139 | ] 140 | 141 | [[package]] 142 | name = "clap_lex" 143 | version = "0.5.1" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" 146 | 147 | [[package]] 148 | name = "clipboard-win" 149 | version = "4.5.0" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" 152 | dependencies = [ 153 | "error-code", 154 | "str-buf", 155 | "winapi", 156 | ] 157 | 158 | [[package]] 159 | name = "colorchoice" 160 | version = "1.0.0" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 163 | 164 | [[package]] 165 | name = "cpufeatures" 166 | version = "0.2.10" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "3fbc60abd742b35f2492f808e1abbb83d45f72db402e14c55057edc9c7b1e9e4" 169 | dependencies = [ 170 | "libc", 171 | ] 172 | 173 | [[package]] 174 | name = "crypto-common" 175 | version = "0.1.6" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 178 | dependencies = [ 179 | "generic-array", 180 | "typenum", 181 | ] 182 | 183 | [[package]] 184 | name = "digest" 185 | version = "0.10.7" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 188 | dependencies = [ 189 | "block-buffer", 190 | "crypto-common", 191 | ] 192 | 193 | [[package]] 194 | name = "endian-type" 195 | version = "0.1.2" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" 198 | 199 | [[package]] 200 | name = "errno" 201 | version = "0.3.5" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" 204 | dependencies = [ 205 | "libc", 206 | "windows-sys", 207 | ] 208 | 209 | [[package]] 210 | name = "error-code" 211 | version = "2.3.1" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" 214 | dependencies = [ 215 | "libc", 216 | "str-buf", 217 | ] 218 | 219 | [[package]] 220 | name = "fd-lock" 221 | version = "3.0.13" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" 224 | dependencies = [ 225 | "cfg-if", 226 | "rustix", 227 | "windows-sys", 228 | ] 229 | 230 | [[package]] 231 | name = "generic-array" 232 | version = "0.14.7" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 235 | dependencies = [ 236 | "typenum", 237 | "version_check", 238 | ] 239 | 240 | [[package]] 241 | name = "heck" 242 | version = "0.4.1" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 245 | 246 | [[package]] 247 | name = "home" 248 | version = "0.5.5" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" 251 | dependencies = [ 252 | "windows-sys", 253 | ] 254 | 255 | [[package]] 256 | name = "im" 257 | version = "15.1.0" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" 260 | dependencies = [ 261 | "bitmaps", 262 | "rand_core", 263 | "rand_xoshiro", 264 | "sized-chunks", 265 | "typenum", 266 | "version_check", 267 | ] 268 | 269 | [[package]] 270 | name = "libc" 271 | version = "0.2.149" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" 274 | 275 | [[package]] 276 | name = "linux-raw-sys" 277 | version = "0.4.10" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" 280 | 281 | [[package]] 282 | name = "log" 283 | version = "0.4.20" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 286 | 287 | [[package]] 288 | name = "memchr" 289 | version = "2.6.4" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" 292 | 293 | [[package]] 294 | name = "nibble_vec" 295 | version = "0.1.0" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" 298 | dependencies = [ 299 | "smallvec", 300 | ] 301 | 302 | [[package]] 303 | name = "nix" 304 | version = "0.26.4" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" 307 | dependencies = [ 308 | "bitflags 1.3.2", 309 | "cfg-if", 310 | "libc", 311 | ] 312 | 313 | [[package]] 314 | name = "once_cell" 315 | version = "1.18.0" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" 318 | 319 | [[package]] 320 | name = "pest" 321 | version = "2.7.4" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "c022f1e7b65d6a24c0dbbd5fb344c66881bc01f3e5ae74a1c8100f2f985d98a4" 324 | dependencies = [ 325 | "memchr", 326 | "thiserror", 327 | "ucd-trie", 328 | ] 329 | 330 | [[package]] 331 | name = "pest_derive" 332 | version = "2.7.4" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "35513f630d46400a977c4cb58f78e1bfbe01434316e60c37d27b9ad6139c66d8" 335 | dependencies = [ 336 | "pest", 337 | "pest_generator", 338 | ] 339 | 340 | [[package]] 341 | name = "pest_generator" 342 | version = "2.7.4" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "bc9fc1b9e7057baba189b5c626e2d6f40681ae5b6eb064dc7c7834101ec8123a" 345 | dependencies = [ 346 | "pest", 347 | "pest_meta", 348 | "proc-macro2", 349 | "quote", 350 | "syn", 351 | ] 352 | 353 | [[package]] 354 | name = "pest_meta" 355 | version = "2.7.4" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "1df74e9e7ec4053ceb980e7c0c8bd3594e977fde1af91daba9c928e8e8c6708d" 358 | dependencies = [ 359 | "once_cell", 360 | "pest", 361 | "sha2", 362 | ] 363 | 364 | [[package]] 365 | name = "proc-macro2" 366 | version = "1.0.69" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" 369 | dependencies = [ 370 | "unicode-ident", 371 | ] 372 | 373 | [[package]] 374 | name = "quote" 375 | version = "1.0.33" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 378 | dependencies = [ 379 | "proc-macro2", 380 | ] 381 | 382 | [[package]] 383 | name = "radix_trie" 384 | version = "0.2.1" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" 387 | dependencies = [ 388 | "endian-type", 389 | "nibble_vec", 390 | ] 391 | 392 | [[package]] 393 | name = "rand_core" 394 | version = "0.6.4" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 397 | 398 | [[package]] 399 | name = "rand_xoshiro" 400 | version = "0.6.0" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" 403 | dependencies = [ 404 | "rand_core", 405 | ] 406 | 407 | [[package]] 408 | name = "rustix" 409 | version = "0.38.21" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" 412 | dependencies = [ 413 | "bitflags 2.4.1", 414 | "errno", 415 | "libc", 416 | "linux-raw-sys", 417 | "windows-sys", 418 | ] 419 | 420 | [[package]] 421 | name = "rustyline" 422 | version = "12.0.0" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "994eca4bca05c87e86e15d90fc7a91d1be64b4482b38cb2d27474568fe7c9db9" 425 | dependencies = [ 426 | "bitflags 2.4.1", 427 | "cfg-if", 428 | "clipboard-win", 429 | "fd-lock", 430 | "home", 431 | "libc", 432 | "log", 433 | "memchr", 434 | "nix", 435 | "radix_trie", 436 | "scopeguard", 437 | "unicode-segmentation", 438 | "unicode-width", 439 | "utf8parse", 440 | "winapi", 441 | ] 442 | 443 | [[package]] 444 | name = "scopeguard" 445 | version = "1.2.0" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 448 | 449 | [[package]] 450 | name = "sha2" 451 | version = "0.10.8" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" 454 | dependencies = [ 455 | "cfg-if", 456 | "cpufeatures", 457 | "digest", 458 | ] 459 | 460 | [[package]] 461 | name = "sized-chunks" 462 | version = "0.6.5" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" 465 | dependencies = [ 466 | "bitmaps", 467 | "typenum", 468 | ] 469 | 470 | [[package]] 471 | name = "smallvec" 472 | version = "1.11.1" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" 475 | 476 | [[package]] 477 | name = "str-buf" 478 | version = "1.0.6" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" 481 | 482 | [[package]] 483 | name = "strsim" 484 | version = "0.10.0" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 487 | 488 | [[package]] 489 | name = "syn" 490 | version = "2.0.38" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" 493 | dependencies = [ 494 | "proc-macro2", 495 | "quote", 496 | "unicode-ident", 497 | ] 498 | 499 | [[package]] 500 | name = "thiserror" 501 | version = "1.0.50" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" 504 | dependencies = [ 505 | "thiserror-impl", 506 | ] 507 | 508 | [[package]] 509 | name = "thiserror-impl" 510 | version = "1.0.50" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" 513 | dependencies = [ 514 | "proc-macro2", 515 | "quote", 516 | "syn", 517 | ] 518 | 519 | [[package]] 520 | name = "typenum" 521 | version = "1.17.0" 522 | source = "registry+https://github.com/rust-lang/crates.io-index" 523 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 524 | 525 | [[package]] 526 | name = "ucd-trie" 527 | version = "0.1.6" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" 530 | 531 | [[package]] 532 | name = "unicode-ident" 533 | version = "1.0.12" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 536 | 537 | [[package]] 538 | name = "unicode-segmentation" 539 | version = "1.10.1" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" 542 | 543 | [[package]] 544 | name = "unicode-width" 545 | version = "0.1.11" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" 548 | 549 | [[package]] 550 | name = "utf8parse" 551 | version = "0.2.1" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 554 | 555 | [[package]] 556 | name = "version_check" 557 | version = "0.9.4" 558 | source = "registry+https://github.com/rust-lang/crates.io-index" 559 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 560 | 561 | [[package]] 562 | name = "winapi" 563 | version = "0.3.9" 564 | source = "registry+https://github.com/rust-lang/crates.io-index" 565 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 566 | dependencies = [ 567 | "winapi-i686-pc-windows-gnu", 568 | "winapi-x86_64-pc-windows-gnu", 569 | ] 570 | 571 | [[package]] 572 | name = "winapi-i686-pc-windows-gnu" 573 | version = "0.4.0" 574 | source = "registry+https://github.com/rust-lang/crates.io-index" 575 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 576 | 577 | [[package]] 578 | name = "winapi-x86_64-pc-windows-gnu" 579 | version = "0.4.0" 580 | source = "registry+https://github.com/rust-lang/crates.io-index" 581 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 582 | 583 | [[package]] 584 | name = "windows-sys" 585 | version = "0.48.0" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 588 | dependencies = [ 589 | "windows-targets", 590 | ] 591 | 592 | [[package]] 593 | name = "windows-targets" 594 | version = "0.48.5" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 597 | dependencies = [ 598 | "windows_aarch64_gnullvm", 599 | "windows_aarch64_msvc", 600 | "windows_i686_gnu", 601 | "windows_i686_msvc", 602 | "windows_x86_64_gnu", 603 | "windows_x86_64_gnullvm", 604 | "windows_x86_64_msvc", 605 | ] 606 | 607 | [[package]] 608 | name = "windows_aarch64_gnullvm" 609 | version = "0.48.5" 610 | source = "registry+https://github.com/rust-lang/crates.io-index" 611 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 612 | 613 | [[package]] 614 | name = "windows_aarch64_msvc" 615 | version = "0.48.5" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 618 | 619 | [[package]] 620 | name = "windows_i686_gnu" 621 | version = "0.48.5" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 624 | 625 | [[package]] 626 | name = "windows_i686_msvc" 627 | version = "0.48.5" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 630 | 631 | [[package]] 632 | name = "windows_x86_64_gnu" 633 | version = "0.48.5" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 636 | 637 | [[package]] 638 | name = "windows_x86_64_gnullvm" 639 | version = "0.48.5" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 642 | 643 | [[package]] 644 | name = "windows_x86_64_msvc" 645 | version = "0.48.5" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 648 | -------------------------------------------------------------------------------- /cidr-calc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cidr-calculator" 3 | version = "0.2.0" 4 | edition = "2021" 5 | description = "CIDR addition / subtraction calculator. Maybe useful for setting route tables when metrics are not available (e.g. wireguard next-hop)" 6 | license = "MIT" 7 | repository = "https://github.com/CircuitCoder/cidr-calc" 8 | readme = "../README.md" 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | anyhow = "1.0.93" 14 | im = "15.1.0" 15 | pest = "2.7.14" 16 | pest_derive = "2.7.13" 17 | rustyline = { version = "13.0.0", optional = true } 18 | clap = { version = "4.5.20", features = ["derive"], optional = true } 19 | 20 | [features] 21 | default = ["cli"] 22 | cli = ["dep:rustyline", "dep:clap"] 23 | 24 | [[bin]] 25 | name = "cidr-calculator" 26 | required-features = ["cli"] 27 | -------------------------------------------------------------------------------- /cidr-calc/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Liu Xiaoyi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cidr-calc/example-inputs/let.txt: -------------------------------------------------------------------------------- 1 | let meow = ::/0 2 | let meow_meow = 2001:da8::666/128 3 | meow + meow_meow -------------------------------------------------------------------------------- /cidr-calc/example-inputs/remove_single.txt: -------------------------------------------------------------------------------- 1 | 0.0.0.0/0 - 101.6.6.6/32 -------------------------------------------------------------------------------- /cidr-calc/example-inputs/remove_single_reverse.txt: -------------------------------------------------------------------------------- 1 | 101.6.6.6/32 2 | + 0.0.0.0/2 3 | + 64.0.0.0/3 4 | + 96.0.0.0/6 5 | + 100.0.0.0/8 6 | + 101.0.0.0/14 7 | + 101.4.0.0/15 8 | + 101.6.0.0/22 9 | + 101.6.4.0/23 10 | + 101.6.6.0/30 11 | + 101.6.6.4/31 12 | + 101.6.6.7/32 13 | + 101.6.6.8/29 14 | + 101.6.6.16/28 15 | + 101.6.6.32/27 16 | + 101.6.6.64/26 17 | + 101.6.6.128/25 18 | + 101.6.7.0/24 19 | + 101.6.8.0/21 20 | + 101.6.16.0/20 21 | + 101.6.32.0/19 22 | + 101.6.64.0/18 23 | + 101.6.128.0/17 24 | + 101.7.0.0/16 25 | + 101.8.0.0/13 26 | + 101.16.0.0/12 27 | + 101.32.0.0/11 28 | + 101.64.0.0/10 29 | + 101.128.0.0/9 30 | + 102.0.0.0/7 31 | + 104.0.0.0/5 32 | + 112.0.0.0/4 33 | + 128.0.0.0/1 -------------------------------------------------------------------------------- /cidr-calc/example-inputs/remove_single_v6.txt: -------------------------------------------------------------------------------- 1 | ::/0 - 2001:da8::666/128 -------------------------------------------------------------------------------- /cidr-calc/src/data.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Eq, PartialEq)] 2 | pub struct V4(pub u32, pub u8); 3 | 4 | #[derive(Debug, Eq, PartialEq)] 5 | pub struct V6(pub u128, pub u8); 6 | 7 | impl ToString for V4 { 8 | fn to_string(&self) -> String { 9 | // Mask end zeros 10 | if self.1 == 0 { 11 | return "0.0.0.0/0".to_owned(); 12 | } 13 | 14 | let masked = self.0 & !((1u32 << (32 - self.1)) - 1); 15 | format!("{}/{}", masked.to_be_bytes().map(|e| e.to_string()).join("."), self.1) 16 | } 17 | } 18 | 19 | impl ToString for V6 { 20 | fn to_string(&self) -> String { 21 | if self.1 == 0 { 22 | return "::/0".to_owned(); 23 | } 24 | 25 | let masked = self.0 & !((1u128 << (128 - self.1)) - 1); 26 | let mut segs = <[Option::; 8]>::default(); 27 | 28 | for grpidx in 0..8 { 29 | let grp = (masked >> ((8 - grpidx - 1) * 16)) & 0xFFFFu128; 30 | if grp != 0 { 31 | segs[grpidx] = Some(format!("{:x}", grp)); 32 | } 33 | } 34 | 35 | let mut zero_lengths = [0; 8]; 36 | zero_lengths[0] = if segs[0].is_none() { 1 } else { 0 }; 37 | let mut zero_lengths_max = (zero_lengths[0], 0usize); 38 | for grpidx in 1..8 { 39 | zero_lengths[grpidx] = if segs[grpidx].is_none() { 40 | zero_lengths[grpidx - 1] + 1 41 | } else { 42 | 0 43 | }; 44 | 45 | if zero_lengths_max.0 < zero_lengths[grpidx] { 46 | zero_lengths_max = (zero_lengths[grpidx], grpidx); 47 | } 48 | } 49 | 50 | // Format 51 | if zero_lengths_max.0 == 0 { 52 | // No zero segments 53 | format!("{}/{}", segs.map(Option::unwrap).join(":"), self.1) 54 | } else { 55 | let seg_start = zero_lengths_max.1 + 1 - zero_lengths_max.0; 56 | let seg_head = &segs[0..seg_start]; 57 | let seg_tail= &segs[(zero_lengths_max.1 + 1)..8]; 58 | let head = seg_head.iter().map(|e| e.as_ref().map(|i| i.as_str()).unwrap_or("0")).collect::>().join(":"); 59 | let tail = seg_tail.iter().map(|e| e.as_ref().map(|i| i.as_str()).unwrap_or("0")).collect::>().join(":"); 60 | format!("{}::{}/{}", head, tail, self.1) 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /cidr-calc/src/eval.rs: -------------------------------------------------------------------------------- 1 | use std::{rc::Rc, collections::VecDeque, iter}; 2 | 3 | use anyhow::anyhow; 4 | 5 | use crate::{parser::{Expr, Atomic, Stmt}, data::{V4, V6}}; 6 | 7 | #[derive(Clone, Debug)] 8 | struct SetNode { 9 | depth: usize, 10 | covered: bool, 11 | left: Option>>, 12 | right: Option>>, 13 | } 14 | 15 | fn union_option( 16 | lhs: &Option>>, 17 | rhs: &Option>>, 18 | ) -> Option>> { 19 | match (lhs, rhs) { 20 | (None, r) => r.clone(), 21 | (l @ Some(_), None) => l.clone(), 22 | (Some(l), Some(r)) => Some(Rc::new(l.union(r.as_ref()))), 23 | } 24 | } 25 | 26 | fn subtract_option( 27 | lhs: &Option>>, 28 | rhs: &Option>>, 29 | ) -> Option>> { 30 | match (lhs, rhs) { 31 | (None, _) => None, 32 | (l @ Some(_), None) => l.clone(), 33 | (Some(l), Some(r)) => { 34 | let raw = l.subtract(r.as_ref()); 35 | // Subtraction may result in empty set 36 | if !raw.covered && raw.left.is_none() && raw.right.is_none() { 37 | None 38 | } else { 39 | Some(Rc::new(raw)) 40 | } 41 | } 42 | } 43 | } 44 | 45 | impl SetNode { 46 | pub fn union(&self, ano: &SetNode) -> SetNode { 47 | assert_eq!(ano.depth, self.depth); 48 | if self.covered || ano.covered { 49 | return SetNode { 50 | depth: self.depth, 51 | covered: true, 52 | left: None, 53 | right: None, 54 | }; 55 | } 56 | 57 | if self.is_empty() && ano.is_empty() { 58 | return self.clone(); 59 | } 60 | 61 | assert_ne!(self.depth, MAX_DEPTH); 62 | let left = union_option(&self.left, &ano.left); 63 | let right = union_option(&self.right, &ano.right); 64 | let covered = left.as_ref().map_or(false, |i| i.covered) 65 | && right.as_ref().map_or(false, |i| i.covered); 66 | 67 | if covered { 68 | return SetNode { 69 | depth: self.depth, 70 | covered: true, 71 | left: None, 72 | right: None, 73 | }; 74 | } 75 | 76 | SetNode { 77 | depth: self.depth, 78 | covered: false, 79 | left, 80 | right, 81 | } 82 | } 83 | 84 | pub fn subtract(&self, ano: &SetNode) -> SetNode { 85 | assert_eq!(ano.depth, self.depth); 86 | if self.is_empty() || ano.covered { 87 | return SetNode { 88 | depth: self.depth, 89 | covered: false, 90 | left: None, 91 | right: None, 92 | } 93 | } 94 | 95 | if ano.is_empty() { 96 | return self.clone(); 97 | } 98 | 99 | let mut left_ref = &self.left; 100 | let mut right_ref = &self.right; 101 | let full; 102 | if self.covered { 103 | full = Some(Rc::new(SetNode { 104 | depth: self.depth + 1, 105 | covered: true, 106 | left: None, 107 | right: None, 108 | })); 109 | left_ref = &full; 110 | right_ref = &full; 111 | } 112 | 113 | assert_ne!(self.depth, MAX_DEPTH); 114 | let left = subtract_option(left_ref, &ano.left); 115 | let right = subtract_option(right_ref, &ano.right); 116 | let covered = left.as_ref().map_or(false, |i| i.covered) 117 | && right.as_ref().map_or(false, |i| i.covered); 118 | 119 | if covered { 120 | return SetNode { 121 | depth: self.depth, 122 | covered: true, 123 | left: None, 124 | right: None, 125 | }; 126 | } 127 | 128 | SetNode { 129 | depth: self.depth, 130 | covered: false, 131 | left, 132 | right, 133 | } 134 | } 135 | 136 | pub fn is_empty(&self) -> bool { 137 | !self.covered && self.left.is_none() && self.right.is_none() 138 | } 139 | 140 | pub fn is_canonical(&self) -> bool { 141 | todo!() 142 | } 143 | } 144 | 145 | #[derive(Clone)] 146 | struct SetWalkerFrame<'a, const MAX_DEPTH: usize> { 147 | node: &'a SetNode, 148 | inspected_branches: usize, // Inspected branches. Now always 0 / 1 / 2 149 | } 150 | 151 | // Iterator, stack top always pointing at a covered node, except in the terminal state, where the stack is empty 152 | struct SetWalker<'a, const MAX_DEPTH: usize> { 153 | stack: VecDeque> 154 | } 155 | 156 | impl<'a, const MAX_DEPTH: usize> SetWalker<'a, MAX_DEPTH> { 157 | pub fn new(n: &'a SetNode) -> Self { 158 | let mut s = Self { 159 | stack: VecDeque::with_capacity(MAX_DEPTH + 1), 160 | }; 161 | s.stack.push_back(SetWalkerFrame { node: n, inspected_branches: 0 }); 162 | s 163 | } 164 | 165 | fn step(&mut self) { 166 | if self.stack.is_empty() { 167 | assert!(self.stack.is_empty()); 168 | return; 169 | } 170 | 171 | let top_node = self.stack.back_mut().unwrap().node; 172 | let top_inspected = &mut self.stack.back_mut().unwrap().inspected_branches; 173 | match *top_inspected { 174 | 0 => { 175 | *top_inspected += 1; 176 | if let Some(ref cur) = top_node.left { 177 | self.stack.push_back(SetWalkerFrame { node: cur.as_ref(), inspected_branches: 0 }) 178 | } 179 | }, 180 | 1 => { 181 | *top_inspected += 1; 182 | if let Some(ref cur) = top_node.right { 183 | self.stack.push_back(SetWalkerFrame { node: cur.as_ref(), inspected_branches: 0 }) 184 | } 185 | }, 186 | 2 => { 187 | self.stack.pop_back(); 188 | }, 189 | _ => unreachable!() 190 | } 191 | } 192 | } 193 | 194 | impl<'a, const MAX_DEPTH: usize> Iterator for SetWalker<'a, MAX_DEPTH> { 195 | type Item = (u128, usize); // TODO: high percision? 196 | 197 | fn next(&mut self) -> Option { 198 | while match self.stack.back() { 199 | None => false, 200 | Some(inner) => !inner.node.covered || inner.inspected_branches > 0 201 | } { 202 | self.step(); 203 | } 204 | 205 | if self.stack.is_empty() { 206 | return None; 207 | } 208 | 209 | // Arrived at a covered node 210 | assert_eq!(self.stack.back().map(|e| e.node.covered), Some(true)); 211 | 212 | // Serialize stack 213 | let mut addr = 0u128; 214 | let mut len = 0; 215 | for elem in &self.stack { 216 | if elem.inspected_branches == 0 { 217 | // Last one 218 | break; 219 | } 220 | addr <<= 1; 221 | addr |= (elem.inspected_branches - 1) as u128; 222 | len += 1; 223 | } 224 | 225 | if len != 0 { // Avoid UB 226 | addr <<= MAX_DEPTH - len; 227 | } 228 | 229 | self.step(); 230 | 231 | return Some((addr, len)); 232 | } 233 | } 234 | 235 | #[derive(Clone, Debug)] 236 | pub enum Value { 237 | Unit, 238 | V4Set(SetNode<32>), 239 | V6Set(SetNode<128>), 240 | } 241 | 242 | impl Value { 243 | fn is_same_len(&self, ano: &Value) -> bool { 244 | match (self, ano) { 245 | (Value::V4Set(_), Value::V6Set(_)) | (Value::V6Set(_), Value::V4Set(_)) => false, 246 | _ => true, 247 | } 248 | } 249 | 250 | fn union(&self, ano: &Value) -> anyhow::Result { 251 | if !self.is_same_len(&ano) { 252 | return Err(anyhow!("Cannot add a v4 set to a v6 set")); // TODO: diagnostic 253 | } 254 | 255 | match (self, ano) { 256 | (Value::V4Set(l), Value::V4Set(r)) => Ok(Value::V4Set(l.union(r))), 257 | (Value::V6Set(l), Value::V6Set(r)) => Ok(Value::V6Set(l.union(r))), 258 | _ => unreachable!(), 259 | } 260 | } 261 | 262 | fn subtract(&self, ano: &Value) -> anyhow::Result { 263 | if !self.is_same_len(&ano) { 264 | return Err(anyhow!("Cannot subtract a v4 set to a v6 set")); // TODO: diagnostic 265 | } 266 | 267 | match (self, ano) { 268 | (Value::V4Set(l), Value::V4Set(r)) => Ok(Value::V4Set(l.subtract(r))), 269 | (Value::V6Set(l), Value::V6Set(r)) => Ok(Value::V6Set(l.subtract(r))), 270 | _ => unreachable!(), 271 | } 272 | } 273 | } 274 | 275 | fn construct_set_node(addr: u128, len: usize, depth: usize) -> SetNode { 276 | if depth == len { 277 | assert!(depth <= MAX_DEPTH); 278 | return SetNode { 279 | depth, 280 | covered: true, 281 | left: None, 282 | right: None, 283 | }; 284 | } 285 | 286 | assert!(depth < MAX_DEPTH); 287 | 288 | let child = Some(Rc::new(construct_set_node(addr, len, depth + 1))); 289 | let mut cur = SetNode { 290 | depth, 291 | covered: false, 292 | left: None, 293 | right: None, 294 | }; 295 | if (addr >> (MAX_DEPTH - depth - 1)) & 1 == 0 { 296 | cur.left = child; 297 | } else { 298 | cur.right = child; 299 | } 300 | 301 | cur 302 | } 303 | 304 | impl From<&V4> for SetNode<32> { 305 | fn from(value: &V4) -> Self { 306 | construct_set_node(value.0 as u128, value.1 as usize, 0) 307 | } 308 | } 309 | 310 | impl From<(u128, usize)> for V4 { 311 | fn from(value: (u128, usize)) -> Self { 312 | Self(value.0 as u32, value.1 as u8) 313 | } 314 | } 315 | 316 | impl From<&V6> for SetNode<128> { 317 | fn from(value: &V6) -> Self { 318 | construct_set_node(value.0, value.1 as usize, 0) 319 | } 320 | } 321 | 322 | impl From<(u128, usize)> for V6 { 323 | fn from(value: (u128, usize)) -> Self { 324 | Self(value.0, value.1 as u8) 325 | } 326 | } 327 | 328 | #[derive(Clone)] 329 | pub struct Scope { 330 | bindings: im::HashMap, 331 | } 332 | 333 | impl Default for Scope { 334 | fn default() -> Self { 335 | Self { bindings: Default::default() } 336 | } 337 | } 338 | 339 | impl Scope { 340 | pub fn keys<'s>(&'s self) -> impl Iterator + 's { 341 | self.bindings.keys().map(String::as_str) 342 | } 343 | } 344 | 345 | pub fn eval<'a>(stmts: &Vec>) -> anyhow::Result> { 346 | let mut scope = Scope { 347 | bindings: Default::default() 348 | }; 349 | 350 | let mut output = Vec::with_capacity(stmts.len()); 351 | 352 | for stmt in stmts { 353 | let (v, s) = eval_stmt(stmt, scope)?; 354 | scope = s; 355 | output.push(v); 356 | } 357 | 358 | Ok(output) 359 | } 360 | 361 | pub fn format<'a>(v: &'a Value) -> Box + 'a> { 362 | match v { 363 | Value::Unit => Box::new(iter::empty()), 364 | Value::V4Set(s) => Box::new(SetWalker::new(s).map(V4::from).map(|e| e.to_string())), 365 | Value::V6Set(s) => Box::new(SetWalker::new(s).map(V6::from).map(|e| e.to_string())), 366 | } 367 | } 368 | 369 | pub fn eval_stmt<'a>(stmt : &Stmt<'a>, mut s: Scope) -> anyhow::Result<(Value, Scope)> { 370 | match stmt { 371 | Stmt::LetIn { ident, val } => { 372 | let val_evaled = eval_expr(val.as_ref(), s.clone())?; 373 | s.bindings.insert(ident.to_string(), val_evaled); 374 | Ok((Value::Unit, s)) 375 | }, 376 | Stmt::Expr(e) => eval_expr(e, s.clone()).map(|r| (r, s)) 377 | } 378 | } 379 | 380 | fn eval_expr<'a>(expr: &Expr<'a>, s: Scope) -> anyhow::Result { 381 | match expr { 382 | Expr::Addition(lhs, rhs) => { 383 | let lhs = eval_expr(lhs, s.clone())?; 384 | let rhs = eval_expr(rhs, s)?; 385 | lhs.union(&rhs) 386 | } 387 | Expr::Subtraction(lhs, rhs) => { 388 | let lhs = eval_expr(lhs, s.clone())?; 389 | let rhs = eval_expr(rhs, s)?; 390 | lhs.subtract(&rhs) 391 | } 392 | Expr::Atomic(a) => match a { 393 | Atomic::Ident(i) => { 394 | let lookup = s.bindings.get(*i); 395 | lookup.cloned().ok_or_else(|| anyhow!("Identifier not found in scope: {}", *i)) 396 | } 397 | Atomic::V4(v) => Ok(Value::V4Set(v.into())), 398 | Atomic::V6(v) => Ok(Value::V6Set(v.into())), 399 | } 400 | } 401 | } 402 | 403 | #[test] 404 | fn test() { 405 | fn eval_single<'a>(stmt: &Stmt<'a>) -> anyhow::Result { 406 | eval_stmt(stmt, Scope { 407 | bindings: Default::default() 408 | }).map(|e| e.0) 409 | } 410 | use crate::parser::parse_single; 411 | // FIXME: write real test! 412 | println!("{:?}", eval_single(&parse_single("0.0.0.0/0").unwrap())); 413 | println!("{:?}", eval_single(&parse_single("::1/128").unwrap())); 414 | println!("{:?}", eval_single(&parse_single("::1/128 - ::/0").unwrap())); 415 | println!("{:?}", eval_single(&parse_single("0.0.0.0/1").unwrap())); 416 | println!("{:?}", eval_single(&parse_single("0.0.0.0/1 + 128.0.0.0/1").unwrap())); 417 | println!("{:?}", eval_single(&parse_single("0.0.0.0/0 - 101.6.6.6/32").unwrap())); 418 | } -------------------------------------------------------------------------------- /cidr-calc/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod parser; 2 | pub mod eval; 3 | pub mod data; -------------------------------------------------------------------------------- /cidr-calc/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use clap::Parser; 4 | use cidr_calculator::eval::{eval, format, Value, eval_stmt, Scope}; 5 | use cidr_calculator::parser::{parse, parse_single}; 6 | use rustyline::DefaultEditor; 7 | 8 | #[derive(Parser)] 9 | struct Args { 10 | input: Option, // TODO: option, none is repl 11 | } 12 | 13 | fn main() -> anyhow::Result<()> { 14 | let args = Args::parse(); 15 | if args.input.is_none() { 16 | return repl(); 17 | } 18 | 19 | let input = args.input.unwrap(); 20 | let content = std::fs::read_to_string(&input)?; 21 | let parsed = parse(&content)?; 22 | let evaled = eval(&parsed)?; 23 | for value in evaled { 24 | match value { 25 | Value::Unit => {}, 26 | _ => { 27 | println!("[{}]", format(&value).collect::>().join(",")) 28 | } 29 | } 30 | } 31 | 32 | Ok(()) 33 | } 34 | 35 | fn repl() -> anyhow::Result<()> { 36 | let mut rl = DefaultEditor::new()?; 37 | 38 | let mut scope = Scope::default(); 39 | loop { 40 | let line = rl.readline("> "); 41 | match line { 42 | Ok(line) => { 43 | if line == "/s" { 44 | println!("In scope: {}", scope.keys().collect::>().join(", ")); 45 | } else { 46 | let evaled: anyhow::Result<_> = (|| { 47 | let stmt = parse_single(&line)?; 48 | let (v, s) = eval_stmt(&stmt, scope.clone())?; 49 | scope = s; 50 | Ok(v) 51 | })(); 52 | match evaled { 53 | Ok(Value::Unit) => { 54 | continue; 55 | } 56 | Ok(v) => { 57 | println!("["); 58 | for l in format(&v) { 59 | println!("\t{}", l); 60 | } 61 | println!("]"); 62 | } 63 | Err(e) => { 64 | println!("Evaluation error:"); 65 | println!("{}", e); 66 | } 67 | } 68 | } 69 | } 70 | Err(rustyline::error::ReadlineError::Eof) => { 71 | return Ok(()) 72 | } 73 | Err(rustyline::error::ReadlineError::Interrupted) => { 74 | return Err(anyhow::anyhow!("Interrupted")); 75 | } 76 | Err(e) => { 77 | return Err(anyhow::anyhow!("Unexpected error: {}", e)); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /cidr-calc/src/parser.rs: -------------------------------------------------------------------------------- 1 | use pest::{Parser, iterators::Pair}; 2 | use pest_derive::Parser; 3 | use anyhow::anyhow; 4 | 5 | use crate::data::*; 6 | 7 | #[derive(Parser)] 8 | #[grammar="./syntax.pest"] 9 | struct SrcParser; 10 | 11 | #[derive(Debug, PartialEq, Eq)] 12 | pub enum Stmt<'a> { 13 | LetIn { 14 | ident: &'a str, 15 | val: Box>, 16 | }, 17 | Expr(Expr<'a>) 18 | } 19 | 20 | impl<'a> From> for Stmt<'a> { 21 | fn from(value: Expr<'a>) -> Self { 22 | Self::Expr(value) 23 | } 24 | } 25 | 26 | #[derive(Debug, PartialEq, Eq)] 27 | pub enum Expr<'a> { 28 | Addition(Box>, Box>), 29 | Subtraction(Box>, Box>), 30 | Atomic(Atomic<'a>), 31 | } 32 | 33 | #[derive(Debug, PartialEq, Eq)] 34 | pub enum Atomic<'a> { 35 | Ident(&'a str), 36 | V4(V4), 37 | V6(V6), 38 | } 39 | 40 | fn process_v6_half(half: &str) -> (u128, u8) { 41 | if half.len() == 0 { 42 | // Is empty string 43 | return (0, 0); 44 | } 45 | let mut cnt = 0; 46 | let mut result = 0; 47 | for seg in half.split(":") { 48 | let s = u32::from_str_radix(seg, 16).unwrap(); 49 | result = (result << 16) | (s as u128); 50 | cnt += 1; 51 | } 52 | (result, cnt) 53 | } 54 | 55 | fn map_expr<'a>(p: Pair<'a, Rule>) -> anyhow::Result> { 56 | // println!("Processing: {:?}", p.as_rule()); 57 | // TODO: a million assertions 58 | match p.as_rule() { 59 | Rule::ident => Ok(Expr::Atomic(Atomic::Ident(p.as_str()))), 60 | Rule::v4cidr => { 61 | let mut collected: u32 = 0; 62 | let mut segs = p.as_str().split("/"); 63 | let addr = segs.next().unwrap(); 64 | let len = segs.next().unwrap(); 65 | for seg in addr.split(".") { 66 | if seg.len() > 3 { 67 | return Err(anyhow!("Number too big for v4 segment: {}", seg)) 68 | } 69 | let parsed: u32 = seg.parse().unwrap(); 70 | if parsed > 256 { 71 | return Err(anyhow!("Number too big for v4 segment: {}", seg)) 72 | } 73 | collected = collected << 8 | parsed; 74 | } 75 | if len.len() > 2 { 76 | return Err(anyhow!("Number too big for v4 CIDR length: {}", len)) 77 | } 78 | let len_parsed: u32 = len.parse().unwrap(); 79 | if len_parsed > 32 { 80 | return Err(anyhow!("Number too big for v4 CIDR length: {}", len)) 81 | } 82 | Ok(Expr::Atomic(Atomic::V4(V4(collected, len_parsed as u8)))) 83 | }, 84 | Rule::v6cidr => { 85 | let mut split = p.as_str().split("/"); 86 | let addr_str = split.next().unwrap(); 87 | let len_str = split.next().unwrap(); 88 | 89 | let mut halves = addr_str.split("::"); 90 | let first_half = process_v6_half(halves.next().unwrap()); 91 | let second_half = halves.next().map(process_v6_half); 92 | if halves.next().is_some() { 93 | return Err(anyhow!("IPv6 address containing more than one `::`: {}", p.as_str())) 94 | } 95 | let addr = if let Some(second_half) = second_half { 96 | if second_half.1 + first_half.1 > 8 { 97 | return Err(anyhow!("IPv6 address containing too much specified segments: {}", p.as_str())) 98 | } 99 | let first_shifter = 8 - first_half.1; 100 | let collected = if first_shifter == 8 { 101 | // First segment 0. Don't shift, because <<128 is UB 102 | 0 103 | } else { 104 | first_half.0 << (first_shifter as i32 * 16) 105 | }; 106 | collected | second_half.0 107 | } else { 108 | if first_half.1 != 8 { 109 | return Err(anyhow!("IPv6 address containing too little specified segments: {}", p.as_str())) 110 | } 111 | first_half.0 112 | }; 113 | if len_str.len() > 3 { 114 | return Err(anyhow!("Number too big for v6 CIDR length: {}", len_str)) 115 | } 116 | let len_parsed: u32 = len_str.parse().unwrap(); 117 | if len_parsed > 128 { 118 | return Err(anyhow!("Number too big for v6 CIDR length: {}", len_str)) 119 | } 120 | 121 | Ok(Expr::Atomic(Atomic::V6(V6(addr, len_parsed as u8)))) 122 | } 123 | Rule::expr => { 124 | let mut p = p.into_inner(); 125 | let mut collected = map_expr(p.next().unwrap())?; 126 | while let Some(op) = p.next() { 127 | let rhs = map_expr(p.next().unwrap())?; 128 | if op.as_rule() == Rule::add_op { 129 | collected = Expr::Addition(Box::new(collected), Box::new(rhs)); 130 | } else { 131 | collected = Expr::Subtraction(Box::new(collected), Box::new(rhs)); 132 | } 133 | } 134 | Ok(collected) 135 | } 136 | Rule::atomic => map_expr(p.into_inner().next().unwrap()), 137 | Rule::paren_expr => map_expr(p.into_inner().skip(1).next().unwrap()), 138 | e => unreachable!("Excuse me pest? Why am I reading {:?}?", e) 139 | } 140 | } 141 | 142 | fn map_stmt<'a>(p: Pair<'a, Rule>) -> anyhow::Result> { 143 | assert_eq!(p.as_rule(), Rule::stmt); 144 | let p = p.into_inner().next().unwrap(); 145 | match p.as_rule() { 146 | Rule::let_in => { 147 | let mut p = p.into_inner(); 148 | let ident = p.next().unwrap().as_str(); 149 | let val = Box::new(map_expr(p.next().unwrap())?); 150 | Ok(Stmt::LetIn { ident, val }) 151 | }, 152 | Rule::expr => { 153 | map_expr(p).map(Into::into) 154 | }, 155 | e => unreachable!("Excuse me pest? Why am I reading {:?}?", e) 156 | } 157 | } 158 | 159 | pub fn parse_single<'a>(input: &'a str) -> anyhow::Result> { 160 | let raw = SrcParser::parse(Rule::single_stmt, input)?.next().unwrap().into_inner().next().unwrap(); 161 | map_stmt(raw) 162 | } 163 | 164 | pub fn parse<'a>(input: &'a str) -> anyhow::Result>> { 165 | let raw = SrcParser::parse(Rule::multiple_stmt, input)?.next().unwrap(); 166 | raw.into_inner().filter(|e| e.as_rule() != Rule::EOI).map(|p| map_stmt(p)).collect() 167 | } 168 | 169 | #[test] 170 | fn test_parser() { 171 | let parsed = parse_single("0.0.0.0/0"); 172 | assert!(parsed.is_ok()); 173 | assert_eq!(parsed.unwrap(), Stmt::Expr(Expr::Atomic(Atomic::V4(V4(0, 0))))); 174 | 175 | let parsed = parse_single("101.6.6.6/32"); 176 | assert!(parsed.is_ok()); 177 | assert_eq!(parsed.unwrap(), Stmt::Expr(Expr::Atomic(Atomic::V4(V4(1694893574u32, 32))))); 178 | 179 | let parsed = parse_single("::/0"); 180 | assert!(parsed.is_ok()); 181 | assert_eq!(parsed.unwrap(), Stmt::Expr(Expr::Atomic(Atomic::V6(V6(0, 0))))); 182 | 183 | let parsed = parse_single("::1/128"); 184 | assert!(parsed.is_ok()); 185 | assert_eq!(parsed.unwrap(), Stmt::Expr(Expr::Atomic(Atomic::V6(V6(1, 128))))); 186 | 187 | let parsed = parse_single("2001:da8::666/24"); 188 | assert!(parsed.is_ok()); 189 | assert_eq!(parsed.unwrap(), Stmt::Expr(Expr::Atomic(Atomic::V6(V6(42540765143631992628674583454950622822u128, 24))))); 190 | 191 | let example = r#" 192 | let meow = ::/0 193 | let meow_meow = 2001:da8::666/128 194 | meow + meow_meow - meow 195 | "#; 196 | let parsed = parse(example); 197 | // assert!(parsed.is_ok()); 198 | println!("{:?}", parsed) 199 | } -------------------------------------------------------------------------------- /cidr-calc/src/syntax.pest: -------------------------------------------------------------------------------- 1 | WHITESPACE = _{ " " | "\n" | "\r" } 2 | 3 | number = @{ 4 | ASCII_DIGIT+ 5 | } 6 | hex = @{ 7 | ASCII_HEX_DIGIT{1,4} 8 | } 9 | kw = @{ 10 | "let" | "in" 11 | } 12 | ident_head = @{ 13 | ASCII_ALPHA | "_" 14 | } 15 | ident_tail = @{ 16 | ASCII_ALPHANUMERIC | "_" 17 | } 18 | ident = @{ 19 | !(hex ~ ":") 20 | ~ !(kw ~ !ident_tail) 21 | ~ ident_head ~ ident_tail* 22 | } 23 | 24 | v4addr = @{ number ~ "." ~ number ~ "." ~ number ~ "." ~ number } 25 | v4cidr = @{ v4addr ~ "/" ~ number } // No spaces allowed here 26 | 27 | v6seg = @{ hex ~ (":" ~ hex)* } 28 | v6addr = @{ v6seg? ~ ("::" ~ v6seg?)? } 29 | v6cidr = @{ v6addr ~ "/" ~ number } 30 | 31 | paren_expr = { "(" ~ expr ~ ")" } 32 | atomic = { 33 | paren_expr 34 | | ident 35 | | v4cidr 36 | | v6cidr 37 | } 38 | 39 | let_in = { "let" ~ ident ~ "=" ~ expr } 40 | add_op = { "+" } 41 | sub_op = { "-" } 42 | expr = { atomic ~ ((add_op | sub_op) ~ atomic)* } 43 | stmt = { 44 | let_in | expr 45 | } 46 | 47 | multiple_stmt = { SOI ~ stmt+ ~ EOI } 48 | single_stmt = { SOI ~ stmt ~ EOI } --------------------------------------------------------------------------------