├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── color_plot ├── drawille.rs ├── mod.rs └── textplots │ ├── mod.rs │ ├── scale.rs │ └── utils.rs ├── lib.rs └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "adler" 7 | version = "1.0.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 10 | 11 | [[package]] 12 | name = "ahash" 13 | version = "0.8.6" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" 16 | dependencies = [ 17 | "cfg-if", 18 | "once_cell", 19 | "version_check", 20 | "zerocopy", 21 | ] 22 | 23 | [[package]] 24 | name = "aho-corasick" 25 | version = "1.0.5" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" 28 | dependencies = [ 29 | "memchr", 30 | ] 31 | 32 | [[package]] 33 | name = "alloc-no-stdlib" 34 | version = "2.0.4" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" 37 | 38 | [[package]] 39 | name = "alloc-stdlib" 40 | version = "0.2.2" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" 43 | dependencies = [ 44 | "alloc-no-stdlib", 45 | ] 46 | 47 | [[package]] 48 | name = "allocator-api2" 49 | version = "0.2.16" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" 52 | 53 | [[package]] 54 | name = "android-tzdata" 55 | version = "0.1.1" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 58 | 59 | [[package]] 60 | name = "android_system_properties" 61 | version = "0.1.5" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 64 | dependencies = [ 65 | "libc", 66 | ] 67 | 68 | [[package]] 69 | name = "arrayvec" 70 | version = "0.7.4" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" 73 | 74 | [[package]] 75 | name = "autocfg" 76 | version = "1.1.0" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 79 | 80 | [[package]] 81 | name = "bindgen" 82 | version = "0.68.1" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" 85 | dependencies = [ 86 | "bitflags 2.4.0", 87 | "cexpr", 88 | "clang-sys", 89 | "lazy_static", 90 | "lazycell", 91 | "peeking_take_while", 92 | "proc-macro2", 93 | "quote", 94 | "regex", 95 | "rustc-hash", 96 | "shlex", 97 | "syn", 98 | ] 99 | 100 | [[package]] 101 | name = "bit-set" 102 | version = "0.8.0" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" 105 | dependencies = [ 106 | "bit-vec", 107 | ] 108 | 109 | [[package]] 110 | name = "bit-vec" 111 | version = "0.8.0" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" 114 | 115 | [[package]] 116 | name = "bitflags" 117 | version = "1.3.2" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 120 | 121 | [[package]] 122 | name = "bitflags" 123 | version = "2.4.0" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" 126 | 127 | [[package]] 128 | name = "brotli" 129 | version = "7.0.0" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" 132 | dependencies = [ 133 | "alloc-no-stdlib", 134 | "alloc-stdlib", 135 | "brotli-decompressor", 136 | ] 137 | 138 | [[package]] 139 | name = "brotli-decompressor" 140 | version = "4.0.1" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" 143 | dependencies = [ 144 | "alloc-no-stdlib", 145 | "alloc-stdlib", 146 | ] 147 | 148 | [[package]] 149 | name = "bumpalo" 150 | version = "3.13.0" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" 153 | 154 | [[package]] 155 | name = "byteorder" 156 | version = "1.4.3" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 159 | 160 | [[package]] 161 | name = "bytes" 162 | version = "1.5.0" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" 165 | 166 | [[package]] 167 | name = "cc" 168 | version = "1.0.83" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" 171 | dependencies = [ 172 | "libc", 173 | ] 174 | 175 | [[package]] 176 | name = "cexpr" 177 | version = "0.6.0" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" 180 | dependencies = [ 181 | "nom", 182 | ] 183 | 184 | [[package]] 185 | name = "cfg-if" 186 | version = "1.0.0" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 189 | 190 | [[package]] 191 | name = "cfg_aliases" 192 | version = "0.2.1" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 195 | 196 | [[package]] 197 | name = "chrono" 198 | version = "0.4.35" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" 201 | dependencies = [ 202 | "android-tzdata", 203 | "iana-time-zone", 204 | "num-traits", 205 | "pure-rust-locales", 206 | "serde", 207 | "windows-targets 0.52.6", 208 | ] 209 | 210 | [[package]] 211 | name = "chrono-humanize" 212 | version = "0.2.3" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "799627e6b4d27827a814e837b9d8a504832086081806d45b1afa34dc982b023b" 215 | dependencies = [ 216 | "chrono", 217 | ] 218 | 219 | [[package]] 220 | name = "clang-sys" 221 | version = "1.6.1" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" 224 | dependencies = [ 225 | "glob", 226 | "libc", 227 | "libloading", 228 | ] 229 | 230 | [[package]] 231 | name = "core-foundation-sys" 232 | version = "0.8.7" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 235 | 236 | [[package]] 237 | name = "crc32fast" 238 | version = "1.3.2" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 241 | dependencies = [ 242 | "cfg-if", 243 | ] 244 | 245 | [[package]] 246 | name = "crossbeam-deque" 247 | version = "0.8.3" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" 250 | dependencies = [ 251 | "cfg-if", 252 | "crossbeam-epoch", 253 | "crossbeam-utils", 254 | ] 255 | 256 | [[package]] 257 | name = "crossbeam-epoch" 258 | version = "0.9.15" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" 261 | dependencies = [ 262 | "autocfg", 263 | "cfg-if", 264 | "crossbeam-utils", 265 | "memoffset", 266 | "scopeguard", 267 | ] 268 | 269 | [[package]] 270 | name = "crossbeam-utils" 271 | version = "0.8.16" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" 274 | dependencies = [ 275 | "cfg-if", 276 | ] 277 | 278 | [[package]] 279 | name = "crossterm" 280 | version = "0.28.1" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" 283 | dependencies = [ 284 | "bitflags 2.4.0", 285 | "crossterm_winapi", 286 | "mio", 287 | "parking_lot", 288 | "rustix 0.38.44", 289 | "signal-hook", 290 | "signal-hook-mio", 291 | "winapi", 292 | ] 293 | 294 | [[package]] 295 | name = "crossterm_winapi" 296 | version = "0.9.1" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" 299 | dependencies = [ 300 | "winapi", 301 | ] 302 | 303 | [[package]] 304 | name = "dirs" 305 | version = "5.0.1" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" 308 | dependencies = [ 309 | "dirs-sys", 310 | ] 311 | 312 | [[package]] 313 | name = "dirs-sys" 314 | version = "0.4.1" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" 317 | dependencies = [ 318 | "libc", 319 | "option-ext", 320 | "redox_users", 321 | "windows-sys 0.48.0", 322 | ] 323 | 324 | [[package]] 325 | name = "doctest-file" 326 | version = "1.0.0" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" 329 | 330 | [[package]] 331 | name = "either" 332 | version = "1.9.0" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" 335 | 336 | [[package]] 337 | name = "equivalent" 338 | version = "1.0.1" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 341 | 342 | [[package]] 343 | name = "erased-serde" 344 | version = "0.3.31" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c" 347 | dependencies = [ 348 | "serde", 349 | ] 350 | 351 | [[package]] 352 | name = "errno" 353 | version = "0.3.10" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" 356 | dependencies = [ 357 | "libc", 358 | "windows-sys 0.59.0", 359 | ] 360 | 361 | [[package]] 362 | name = "fancy-regex" 363 | version = "0.14.0" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298" 366 | dependencies = [ 367 | "bit-set", 368 | "regex-automata 0.4.6", 369 | "regex-syntax 0.8.2", 370 | ] 371 | 372 | [[package]] 373 | name = "flate2" 374 | version = "1.0.28" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" 377 | dependencies = [ 378 | "crc32fast", 379 | "miniz_oxide", 380 | ] 381 | 382 | [[package]] 383 | name = "fnv" 384 | version = "1.0.7" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 387 | 388 | [[package]] 389 | name = "getrandom" 390 | version = "0.2.10" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" 393 | dependencies = [ 394 | "cfg-if", 395 | "libc", 396 | "wasi", 397 | ] 398 | 399 | [[package]] 400 | name = "glob" 401 | version = "0.3.1" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" 404 | 405 | [[package]] 406 | name = "hashbrown" 407 | version = "0.14.3" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" 410 | dependencies = [ 411 | "ahash", 412 | "allocator-api2", 413 | ] 414 | 415 | [[package]] 416 | name = "hashbrown" 417 | version = "0.15.0" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" 420 | 421 | [[package]] 422 | name = "heck" 423 | version = "0.5.0" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 426 | 427 | [[package]] 428 | name = "hex" 429 | version = "0.4.3" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 432 | 433 | [[package]] 434 | name = "iana-time-zone" 435 | version = "0.1.57" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" 438 | dependencies = [ 439 | "android_system_properties", 440 | "core-foundation-sys", 441 | "iana-time-zone-haiku", 442 | "js-sys", 443 | "wasm-bindgen", 444 | "windows 0.48.0", 445 | ] 446 | 447 | [[package]] 448 | name = "iana-time-zone-haiku" 449 | version = "0.1.2" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 452 | dependencies = [ 453 | "cc", 454 | ] 455 | 456 | [[package]] 457 | name = "indexmap" 458 | version = "2.8.0" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" 461 | dependencies = [ 462 | "equivalent", 463 | "hashbrown 0.15.0", 464 | ] 465 | 466 | [[package]] 467 | name = "interprocess" 468 | version = "2.2.1" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "d2f4e4a06d42fab3e85ab1b419ad32b09eab58b901d40c57935ff92db3287a13" 471 | dependencies = [ 472 | "doctest-file", 473 | "libc", 474 | "recvmsg", 475 | "widestring", 476 | "windows-sys 0.52.0", 477 | ] 478 | 479 | [[package]] 480 | name = "inventory" 481 | version = "0.3.12" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "e1be380c410bf0595e94992a648ea89db4dd3f3354ba54af206fd2a68cf5ac8e" 484 | 485 | [[package]] 486 | name = "is_ci" 487 | version = "1.2.0" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" 490 | 491 | [[package]] 492 | name = "itertools" 493 | version = "0.13.0" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" 496 | dependencies = [ 497 | "either", 498 | ] 499 | 500 | [[package]] 501 | name = "itoa" 502 | version = "1.0.9" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" 505 | 506 | [[package]] 507 | name = "js-sys" 508 | version = "0.3.64" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" 511 | dependencies = [ 512 | "wasm-bindgen", 513 | ] 514 | 515 | [[package]] 516 | name = "lazy_static" 517 | version = "1.4.0" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 520 | 521 | [[package]] 522 | name = "lazycell" 523 | version = "1.3.0" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" 526 | 527 | [[package]] 528 | name = "libc" 529 | version = "0.2.171" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" 532 | 533 | [[package]] 534 | name = "libloading" 535 | version = "0.7.4" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" 538 | dependencies = [ 539 | "cfg-if", 540 | "winapi", 541 | ] 542 | 543 | [[package]] 544 | name = "libproc" 545 | version = "0.14.2" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "229004ebba9d1d5caf41623f1523b6d52abb47d9f6ab87f7e6fc992e3b854aef" 548 | dependencies = [ 549 | "bindgen", 550 | "errno", 551 | "libc", 552 | ] 553 | 554 | [[package]] 555 | name = "linux-raw-sys" 556 | version = "0.4.15" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" 559 | 560 | [[package]] 561 | name = "linux-raw-sys" 562 | version = "0.9.3" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" 565 | 566 | [[package]] 567 | name = "lock_api" 568 | version = "0.4.12" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 571 | dependencies = [ 572 | "autocfg", 573 | "scopeguard", 574 | ] 575 | 576 | [[package]] 577 | name = "log" 578 | version = "0.4.20" 579 | source = "registry+https://github.com/rust-lang/crates.io-index" 580 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 581 | 582 | [[package]] 583 | name = "lru" 584 | version = "0.12.1" 585 | source = "registry+https://github.com/rust-lang/crates.io-index" 586 | checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7" 587 | dependencies = [ 588 | "hashbrown 0.14.3", 589 | ] 590 | 591 | [[package]] 592 | name = "lscolors" 593 | version = "0.17.0" 594 | source = "registry+https://github.com/rust-lang/crates.io-index" 595 | checksum = "53304fff6ab1e597661eee37e42ea8c47a146fca280af902bb76bff8a896e523" 596 | dependencies = [ 597 | "nu-ansi-term", 598 | ] 599 | 600 | [[package]] 601 | name = "mach2" 602 | version = "0.4.1" 603 | source = "registry+https://github.com/rust-lang/crates.io-index" 604 | checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8" 605 | dependencies = [ 606 | "libc", 607 | ] 608 | 609 | [[package]] 610 | name = "memchr" 611 | version = "2.7.4" 612 | source = "registry+https://github.com/rust-lang/crates.io-index" 613 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 614 | 615 | [[package]] 616 | name = "memoffset" 617 | version = "0.9.0" 618 | source = "registry+https://github.com/rust-lang/crates.io-index" 619 | checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" 620 | dependencies = [ 621 | "autocfg", 622 | ] 623 | 624 | [[package]] 625 | name = "miette" 626 | version = "7.5.0" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "1a955165f87b37fd1862df2a59547ac542c77ef6d17c666f619d1ad22dd89484" 629 | dependencies = [ 630 | "cfg-if", 631 | "miette-derive", 632 | "owo-colors 4.0.0", 633 | "supports-color", 634 | "supports-hyperlinks", 635 | "supports-unicode", 636 | "terminal_size", 637 | "textwrap", 638 | "thiserror 1.0.57", 639 | "unicode-width", 640 | ] 641 | 642 | [[package]] 643 | name = "miette-derive" 644 | version = "7.5.0" 645 | source = "registry+https://github.com/rust-lang/crates.io-index" 646 | checksum = "bf45bf44ab49be92fd1227a3be6fc6f617f1a337c06af54981048574d8783147" 647 | dependencies = [ 648 | "proc-macro2", 649 | "quote", 650 | "syn", 651 | ] 652 | 653 | [[package]] 654 | name = "minimal-lexical" 655 | version = "0.2.1" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 658 | 659 | [[package]] 660 | name = "miniz_oxide" 661 | version = "0.7.1" 662 | source = "registry+https://github.com/rust-lang/crates.io-index" 663 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" 664 | dependencies = [ 665 | "adler", 666 | ] 667 | 668 | [[package]] 669 | name = "mio" 670 | version = "1.0.3" 671 | source = "registry+https://github.com/rust-lang/crates.io-index" 672 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 673 | dependencies = [ 674 | "libc", 675 | "log", 676 | "wasi", 677 | "windows-sys 0.52.0", 678 | ] 679 | 680 | [[package]] 681 | name = "nix" 682 | version = "0.29.0" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" 685 | dependencies = [ 686 | "bitflags 2.4.0", 687 | "cfg-if", 688 | "cfg_aliases", 689 | "libc", 690 | ] 691 | 692 | [[package]] 693 | name = "nom" 694 | version = "7.1.3" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 697 | dependencies = [ 698 | "memchr", 699 | "minimal-lexical", 700 | ] 701 | 702 | [[package]] 703 | name = "ntapi" 704 | version = "0.4.1" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" 707 | dependencies = [ 708 | "winapi", 709 | ] 710 | 711 | [[package]] 712 | name = "nu-ansi-term" 713 | version = "0.50.0" 714 | source = "registry+https://github.com/rust-lang/crates.io-index" 715 | checksum = "dd2800e1520bdc966782168a627aa5d1ad92e33b984bf7c7615d31280c83ff14" 716 | dependencies = [ 717 | "windows-sys 0.48.0", 718 | ] 719 | 720 | [[package]] 721 | name = "nu-derive-value" 722 | version = "0.103.0" 723 | source = "registry+https://github.com/rust-lang/crates.io-index" 724 | checksum = "8f1f5198366892552a9a827a61a27e31543a0827c55ccfb6bf060489cec80d25" 725 | dependencies = [ 726 | "heck", 727 | "proc-macro-error2", 728 | "proc-macro2", 729 | "quote", 730 | "syn", 731 | ] 732 | 733 | [[package]] 734 | name = "nu-engine" 735 | version = "0.103.0" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "0cb715bb4c18e4259d21c5b710f04f7190c9803211e2a0baa31ec3a5841daa56" 738 | dependencies = [ 739 | "log", 740 | "nu-glob", 741 | "nu-path", 742 | "nu-protocol", 743 | "nu-utils", 744 | ] 745 | 746 | [[package]] 747 | name = "nu-glob" 748 | version = "0.103.0" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "904fa576593ed75439eec561f62824bbe55f4a05f1c8239309a939d43e0ad704" 751 | dependencies = [ 752 | "nu-protocol", 753 | ] 754 | 755 | [[package]] 756 | name = "nu-path" 757 | version = "0.103.0" 758 | source = "registry+https://github.com/rust-lang/crates.io-index" 759 | checksum = "e6e3a55f26e42d1f98fbb4f41fa4fcc7dee1f61f13c5eabda5ca90e78825b2fa" 760 | dependencies = [ 761 | "dirs", 762 | "omnipath", 763 | "pwd", 764 | "ref-cast", 765 | ] 766 | 767 | [[package]] 768 | name = "nu-plugin" 769 | version = "0.103.0" 770 | source = "registry+https://github.com/rust-lang/crates.io-index" 771 | checksum = "5f35f2290c077441edfde50745b501ba5ffad11217d5d01168cf1ab1b0e4c03d" 772 | dependencies = [ 773 | "log", 774 | "nix", 775 | "nu-engine", 776 | "nu-plugin-core", 777 | "nu-plugin-protocol", 778 | "nu-protocol", 779 | "nu-utils", 780 | "thiserror 2.0.12", 781 | ] 782 | 783 | [[package]] 784 | name = "nu-plugin-core" 785 | version = "0.103.0" 786 | source = "registry+https://github.com/rust-lang/crates.io-index" 787 | checksum = "5ba6f1d1c7f6ca9852c26e8e65a0f530b8fa3a1237a6c62de089ccaf6c1645fe" 788 | dependencies = [ 789 | "interprocess", 790 | "log", 791 | "nu-plugin-protocol", 792 | "nu-protocol", 793 | "rmp-serde", 794 | "serde", 795 | "serde_json", 796 | "windows 0.56.0", 797 | ] 798 | 799 | [[package]] 800 | name = "nu-plugin-protocol" 801 | version = "0.103.0" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | checksum = "88bef165a59909561b349fb3eda7e16afae8f8d06d6c99527b4545c086b51f87" 804 | dependencies = [ 805 | "nu-protocol", 806 | "nu-utils", 807 | "rmp-serde", 808 | "semver", 809 | "serde", 810 | "typetag", 811 | ] 812 | 813 | [[package]] 814 | name = "nu-protocol" 815 | version = "0.103.0" 816 | source = "registry+https://github.com/rust-lang/crates.io-index" 817 | checksum = "ca35b5860d171e8e0994d42373f62fc99fb7a0b205e5d8a38897e2869d5f6ab7" 818 | dependencies = [ 819 | "brotli", 820 | "bytes", 821 | "chrono", 822 | "chrono-humanize", 823 | "dirs", 824 | "dirs-sys", 825 | "fancy-regex", 826 | "heck", 827 | "indexmap", 828 | "log", 829 | "lru", 830 | "memchr", 831 | "miette", 832 | "nix", 833 | "nu-derive-value", 834 | "nu-path", 835 | "nu-system", 836 | "nu-utils", 837 | "num-format", 838 | "os_pipe", 839 | "rmp-serde", 840 | "serde", 841 | "serde_json", 842 | "strum", 843 | "strum_macros", 844 | "thiserror 2.0.12", 845 | "typetag", 846 | "web-time", 847 | "windows-sys 0.48.0", 848 | ] 849 | 850 | [[package]] 851 | name = "nu-system" 852 | version = "0.103.0" 853 | source = "registry+https://github.com/rust-lang/crates.io-index" 854 | checksum = "70bb9b1c59acd274bd36b4879e1e03491a3ee2f24689a9070c66fbd8aed23b27" 855 | dependencies = [ 856 | "chrono", 857 | "itertools", 858 | "libc", 859 | "libproc", 860 | "log", 861 | "mach2", 862 | "nix", 863 | "ntapi", 864 | "procfs", 865 | "sysinfo", 866 | "web-time", 867 | "windows 0.56.0", 868 | ] 869 | 870 | [[package]] 871 | name = "nu-utils" 872 | version = "0.103.0" 873 | source = "registry+https://github.com/rust-lang/crates.io-index" 874 | checksum = "2f01345a3c94f75397020250286c536e1b306cb714b2931c1a1c9a3318254793" 875 | dependencies = [ 876 | "crossterm", 877 | "crossterm_winapi", 878 | "fancy-regex", 879 | "log", 880 | "lscolors", 881 | "nix", 882 | "num-format", 883 | "serde", 884 | "serde_json", 885 | "strip-ansi-escapes", 886 | "sys-locale", 887 | "unicase", 888 | ] 889 | 890 | [[package]] 891 | name = "nu_plugin_plot" 892 | version = "0.103.0" 893 | dependencies = [ 894 | "fnv", 895 | "nu-plugin", 896 | "nu-protocol", 897 | "owo-colors 3.5.0", 898 | "term_size", 899 | ] 900 | 901 | [[package]] 902 | name = "num-format" 903 | version = "0.4.4" 904 | source = "registry+https://github.com/rust-lang/crates.io-index" 905 | checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" 906 | dependencies = [ 907 | "arrayvec", 908 | "itoa", 909 | ] 910 | 911 | [[package]] 912 | name = "num-traits" 913 | version = "0.2.16" 914 | source = "registry+https://github.com/rust-lang/crates.io-index" 915 | checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" 916 | dependencies = [ 917 | "autocfg", 918 | ] 919 | 920 | [[package]] 921 | name = "omnipath" 922 | version = "0.1.6" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "80adb31078122c880307e9cdfd4e3361e6545c319f9b9dcafcb03acd3b51a575" 925 | 926 | [[package]] 927 | name = "once_cell" 928 | version = "1.20.2" 929 | source = "registry+https://github.com/rust-lang/crates.io-index" 930 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 931 | 932 | [[package]] 933 | name = "option-ext" 934 | version = "0.2.0" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" 937 | 938 | [[package]] 939 | name = "os_pipe" 940 | version = "1.2.1" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" 943 | dependencies = [ 944 | "libc", 945 | "windows-sys 0.59.0", 946 | ] 947 | 948 | [[package]] 949 | name = "owo-colors" 950 | version = "3.5.0" 951 | source = "registry+https://github.com/rust-lang/crates.io-index" 952 | checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" 953 | 954 | [[package]] 955 | name = "owo-colors" 956 | version = "4.0.0" 957 | source = "registry+https://github.com/rust-lang/crates.io-index" 958 | checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" 959 | 960 | [[package]] 961 | name = "parking_lot" 962 | version = "0.12.3" 963 | source = "registry+https://github.com/rust-lang/crates.io-index" 964 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 965 | dependencies = [ 966 | "lock_api", 967 | "parking_lot_core", 968 | ] 969 | 970 | [[package]] 971 | name = "parking_lot_core" 972 | version = "0.9.10" 973 | source = "registry+https://github.com/rust-lang/crates.io-index" 974 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 975 | dependencies = [ 976 | "cfg-if", 977 | "libc", 978 | "redox_syscall 0.5.10", 979 | "smallvec", 980 | "windows-targets 0.52.6", 981 | ] 982 | 983 | [[package]] 984 | name = "paste" 985 | version = "1.0.14" 986 | source = "registry+https://github.com/rust-lang/crates.io-index" 987 | checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" 988 | 989 | [[package]] 990 | name = "peeking_take_while" 991 | version = "0.1.2" 992 | source = "registry+https://github.com/rust-lang/crates.io-index" 993 | checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" 994 | 995 | [[package]] 996 | name = "proc-macro-error-attr2" 997 | version = "2.0.0" 998 | source = "registry+https://github.com/rust-lang/crates.io-index" 999 | checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" 1000 | dependencies = [ 1001 | "proc-macro2", 1002 | "quote", 1003 | ] 1004 | 1005 | [[package]] 1006 | name = "proc-macro-error2" 1007 | version = "2.0.1" 1008 | source = "registry+https://github.com/rust-lang/crates.io-index" 1009 | checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" 1010 | dependencies = [ 1011 | "proc-macro-error-attr2", 1012 | "proc-macro2", 1013 | "quote", 1014 | "syn", 1015 | ] 1016 | 1017 | [[package]] 1018 | name = "proc-macro2" 1019 | version = "1.0.94" 1020 | source = "registry+https://github.com/rust-lang/crates.io-index" 1021 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" 1022 | dependencies = [ 1023 | "unicode-ident", 1024 | ] 1025 | 1026 | [[package]] 1027 | name = "procfs" 1028 | version = "0.17.0" 1029 | source = "registry+https://github.com/rust-lang/crates.io-index" 1030 | checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f" 1031 | dependencies = [ 1032 | "bitflags 2.4.0", 1033 | "chrono", 1034 | "flate2", 1035 | "hex", 1036 | "procfs-core", 1037 | "rustix 0.38.44", 1038 | ] 1039 | 1040 | [[package]] 1041 | name = "procfs-core" 1042 | version = "0.17.0" 1043 | source = "registry+https://github.com/rust-lang/crates.io-index" 1044 | checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec" 1045 | dependencies = [ 1046 | "bitflags 2.4.0", 1047 | "chrono", 1048 | "hex", 1049 | ] 1050 | 1051 | [[package]] 1052 | name = "pure-rust-locales" 1053 | version = "0.8.1" 1054 | source = "registry+https://github.com/rust-lang/crates.io-index" 1055 | checksum = "1190fd18ae6ce9e137184f207593877e70f39b015040156b1e05081cdfe3733a" 1056 | 1057 | [[package]] 1058 | name = "pwd" 1059 | version = "1.4.0" 1060 | source = "registry+https://github.com/rust-lang/crates.io-index" 1061 | checksum = "72c71c0c79b9701efe4e1e4b563b2016dd4ee789eb99badcb09d61ac4b92e4a2" 1062 | dependencies = [ 1063 | "libc", 1064 | "thiserror 1.0.57", 1065 | ] 1066 | 1067 | [[package]] 1068 | name = "quote" 1069 | version = "1.0.35" 1070 | source = "registry+https://github.com/rust-lang/crates.io-index" 1071 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 1072 | dependencies = [ 1073 | "proc-macro2", 1074 | ] 1075 | 1076 | [[package]] 1077 | name = "rayon" 1078 | version = "1.9.0" 1079 | source = "registry+https://github.com/rust-lang/crates.io-index" 1080 | checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" 1081 | dependencies = [ 1082 | "either", 1083 | "rayon-core", 1084 | ] 1085 | 1086 | [[package]] 1087 | name = "rayon-core" 1088 | version = "1.12.1" 1089 | source = "registry+https://github.com/rust-lang/crates.io-index" 1090 | checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 1091 | dependencies = [ 1092 | "crossbeam-deque", 1093 | "crossbeam-utils", 1094 | ] 1095 | 1096 | [[package]] 1097 | name = "recvmsg" 1098 | version = "1.0.0" 1099 | source = "registry+https://github.com/rust-lang/crates.io-index" 1100 | checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" 1101 | 1102 | [[package]] 1103 | name = "redox_syscall" 1104 | version = "0.2.16" 1105 | source = "registry+https://github.com/rust-lang/crates.io-index" 1106 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 1107 | dependencies = [ 1108 | "bitflags 1.3.2", 1109 | ] 1110 | 1111 | [[package]] 1112 | name = "redox_syscall" 1113 | version = "0.5.10" 1114 | source = "registry+https://github.com/rust-lang/crates.io-index" 1115 | checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" 1116 | dependencies = [ 1117 | "bitflags 2.4.0", 1118 | ] 1119 | 1120 | [[package]] 1121 | name = "redox_users" 1122 | version = "0.4.3" 1123 | source = "registry+https://github.com/rust-lang/crates.io-index" 1124 | checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" 1125 | dependencies = [ 1126 | "getrandom", 1127 | "redox_syscall 0.2.16", 1128 | "thiserror 1.0.57", 1129 | ] 1130 | 1131 | [[package]] 1132 | name = "ref-cast" 1133 | version = "1.0.24" 1134 | source = "registry+https://github.com/rust-lang/crates.io-index" 1135 | checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" 1136 | dependencies = [ 1137 | "ref-cast-impl", 1138 | ] 1139 | 1140 | [[package]] 1141 | name = "ref-cast-impl" 1142 | version = "1.0.24" 1143 | source = "registry+https://github.com/rust-lang/crates.io-index" 1144 | checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" 1145 | dependencies = [ 1146 | "proc-macro2", 1147 | "quote", 1148 | "syn", 1149 | ] 1150 | 1151 | [[package]] 1152 | name = "regex" 1153 | version = "1.9.5" 1154 | source = "registry+https://github.com/rust-lang/crates.io-index" 1155 | checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" 1156 | dependencies = [ 1157 | "aho-corasick", 1158 | "memchr", 1159 | "regex-automata 0.3.8", 1160 | "regex-syntax 0.7.5", 1161 | ] 1162 | 1163 | [[package]] 1164 | name = "regex-automata" 1165 | version = "0.3.8" 1166 | source = "registry+https://github.com/rust-lang/crates.io-index" 1167 | checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" 1168 | dependencies = [ 1169 | "aho-corasick", 1170 | "memchr", 1171 | "regex-syntax 0.7.5", 1172 | ] 1173 | 1174 | [[package]] 1175 | name = "regex-automata" 1176 | version = "0.4.6" 1177 | source = "registry+https://github.com/rust-lang/crates.io-index" 1178 | checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" 1179 | dependencies = [ 1180 | "aho-corasick", 1181 | "memchr", 1182 | "regex-syntax 0.8.2", 1183 | ] 1184 | 1185 | [[package]] 1186 | name = "regex-syntax" 1187 | version = "0.7.5" 1188 | source = "registry+https://github.com/rust-lang/crates.io-index" 1189 | checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" 1190 | 1191 | [[package]] 1192 | name = "regex-syntax" 1193 | version = "0.8.2" 1194 | source = "registry+https://github.com/rust-lang/crates.io-index" 1195 | checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" 1196 | 1197 | [[package]] 1198 | name = "rmp" 1199 | version = "0.8.14" 1200 | source = "registry+https://github.com/rust-lang/crates.io-index" 1201 | checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" 1202 | dependencies = [ 1203 | "byteorder", 1204 | "num-traits", 1205 | "paste", 1206 | ] 1207 | 1208 | [[package]] 1209 | name = "rmp-serde" 1210 | version = "1.3.0" 1211 | source = "registry+https://github.com/rust-lang/crates.io-index" 1212 | checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" 1213 | dependencies = [ 1214 | "byteorder", 1215 | "rmp", 1216 | "serde", 1217 | ] 1218 | 1219 | [[package]] 1220 | name = "rustc-hash" 1221 | version = "1.1.0" 1222 | source = "registry+https://github.com/rust-lang/crates.io-index" 1223 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 1224 | 1225 | [[package]] 1226 | name = "rustix" 1227 | version = "0.38.44" 1228 | source = "registry+https://github.com/rust-lang/crates.io-index" 1229 | checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" 1230 | dependencies = [ 1231 | "bitflags 2.4.0", 1232 | "errno", 1233 | "libc", 1234 | "linux-raw-sys 0.4.15", 1235 | "windows-sys 0.59.0", 1236 | ] 1237 | 1238 | [[package]] 1239 | name = "rustix" 1240 | version = "1.0.3" 1241 | source = "registry+https://github.com/rust-lang/crates.io-index" 1242 | checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" 1243 | dependencies = [ 1244 | "bitflags 2.4.0", 1245 | "errno", 1246 | "libc", 1247 | "linux-raw-sys 0.9.3", 1248 | "windows-sys 0.59.0", 1249 | ] 1250 | 1251 | [[package]] 1252 | name = "rustversion" 1253 | version = "1.0.20" 1254 | source = "registry+https://github.com/rust-lang/crates.io-index" 1255 | checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" 1256 | 1257 | [[package]] 1258 | name = "ryu" 1259 | version = "1.0.15" 1260 | source = "registry+https://github.com/rust-lang/crates.io-index" 1261 | checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" 1262 | 1263 | [[package]] 1264 | name = "scopeguard" 1265 | version = "1.2.0" 1266 | source = "registry+https://github.com/rust-lang/crates.io-index" 1267 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1268 | 1269 | [[package]] 1270 | name = "semver" 1271 | version = "1.0.22" 1272 | source = "registry+https://github.com/rust-lang/crates.io-index" 1273 | checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" 1274 | 1275 | [[package]] 1276 | name = "serde" 1277 | version = "1.0.210" 1278 | source = "registry+https://github.com/rust-lang/crates.io-index" 1279 | checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" 1280 | dependencies = [ 1281 | "serde_derive", 1282 | ] 1283 | 1284 | [[package]] 1285 | name = "serde_derive" 1286 | version = "1.0.210" 1287 | source = "registry+https://github.com/rust-lang/crates.io-index" 1288 | checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" 1289 | dependencies = [ 1290 | "proc-macro2", 1291 | "quote", 1292 | "syn", 1293 | ] 1294 | 1295 | [[package]] 1296 | name = "serde_json" 1297 | version = "1.0.105" 1298 | source = "registry+https://github.com/rust-lang/crates.io-index" 1299 | checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" 1300 | dependencies = [ 1301 | "itoa", 1302 | "ryu", 1303 | "serde", 1304 | ] 1305 | 1306 | [[package]] 1307 | name = "shlex" 1308 | version = "1.2.0" 1309 | source = "registry+https://github.com/rust-lang/crates.io-index" 1310 | checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" 1311 | 1312 | [[package]] 1313 | name = "signal-hook" 1314 | version = "0.3.17" 1315 | source = "registry+https://github.com/rust-lang/crates.io-index" 1316 | checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" 1317 | dependencies = [ 1318 | "libc", 1319 | "signal-hook-registry", 1320 | ] 1321 | 1322 | [[package]] 1323 | name = "signal-hook-mio" 1324 | version = "0.2.4" 1325 | source = "registry+https://github.com/rust-lang/crates.io-index" 1326 | checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" 1327 | dependencies = [ 1328 | "libc", 1329 | "mio", 1330 | "signal-hook", 1331 | ] 1332 | 1333 | [[package]] 1334 | name = "signal-hook-registry" 1335 | version = "1.4.2" 1336 | source = "registry+https://github.com/rust-lang/crates.io-index" 1337 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 1338 | dependencies = [ 1339 | "libc", 1340 | ] 1341 | 1342 | [[package]] 1343 | name = "smallvec" 1344 | version = "1.14.0" 1345 | source = "registry+https://github.com/rust-lang/crates.io-index" 1346 | checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" 1347 | 1348 | [[package]] 1349 | name = "strip-ansi-escapes" 1350 | version = "0.2.0" 1351 | source = "registry+https://github.com/rust-lang/crates.io-index" 1352 | checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa" 1353 | dependencies = [ 1354 | "vte", 1355 | ] 1356 | 1357 | [[package]] 1358 | name = "strum" 1359 | version = "0.26.3" 1360 | source = "registry+https://github.com/rust-lang/crates.io-index" 1361 | checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" 1362 | 1363 | [[package]] 1364 | name = "strum_macros" 1365 | version = "0.26.4" 1366 | source = "registry+https://github.com/rust-lang/crates.io-index" 1367 | checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" 1368 | dependencies = [ 1369 | "heck", 1370 | "proc-macro2", 1371 | "quote", 1372 | "rustversion", 1373 | "syn", 1374 | ] 1375 | 1376 | [[package]] 1377 | name = "supports-color" 1378 | version = "3.0.0" 1379 | source = "registry+https://github.com/rust-lang/crates.io-index" 1380 | checksum = "9829b314621dfc575df4e409e79f9d6a66a3bd707ab73f23cb4aa3a854ac854f" 1381 | dependencies = [ 1382 | "is_ci", 1383 | ] 1384 | 1385 | [[package]] 1386 | name = "supports-hyperlinks" 1387 | version = "3.0.0" 1388 | source = "registry+https://github.com/rust-lang/crates.io-index" 1389 | checksum = "2c0a1e5168041f5f3ff68ff7d95dcb9c8749df29f6e7e89ada40dd4c9de404ee" 1390 | 1391 | [[package]] 1392 | name = "supports-unicode" 1393 | version = "3.0.0" 1394 | source = "registry+https://github.com/rust-lang/crates.io-index" 1395 | checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" 1396 | 1397 | [[package]] 1398 | name = "syn" 1399 | version = "2.0.100" 1400 | source = "registry+https://github.com/rust-lang/crates.io-index" 1401 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 1402 | dependencies = [ 1403 | "proc-macro2", 1404 | "quote", 1405 | "unicode-ident", 1406 | ] 1407 | 1408 | [[package]] 1409 | name = "sys-locale" 1410 | version = "0.3.1" 1411 | source = "registry+https://github.com/rust-lang/crates.io-index" 1412 | checksum = "e801cf239ecd6ccd71f03d270d67dd53d13e90aab208bf4b8fe4ad957ea949b0" 1413 | dependencies = [ 1414 | "libc", 1415 | ] 1416 | 1417 | [[package]] 1418 | name = "sysinfo" 1419 | version = "0.33.1" 1420 | source = "registry+https://github.com/rust-lang/crates.io-index" 1421 | checksum = "4fc858248ea01b66f19d8e8a6d55f41deaf91e9d495246fd01368d99935c6c01" 1422 | dependencies = [ 1423 | "core-foundation-sys", 1424 | "libc", 1425 | "memchr", 1426 | "ntapi", 1427 | "rayon", 1428 | "windows 0.56.0", 1429 | ] 1430 | 1431 | [[package]] 1432 | name = "term_size" 1433 | version = "0.3.2" 1434 | source = "registry+https://github.com/rust-lang/crates.io-index" 1435 | checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" 1436 | dependencies = [ 1437 | "libc", 1438 | "winapi", 1439 | ] 1440 | 1441 | [[package]] 1442 | name = "terminal_size" 1443 | version = "0.4.2" 1444 | source = "registry+https://github.com/rust-lang/crates.io-index" 1445 | checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" 1446 | dependencies = [ 1447 | "rustix 1.0.3", 1448 | "windows-sys 0.59.0", 1449 | ] 1450 | 1451 | [[package]] 1452 | name = "textwrap" 1453 | version = "0.16.1" 1454 | source = "registry+https://github.com/rust-lang/crates.io-index" 1455 | checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" 1456 | dependencies = [ 1457 | "unicode-linebreak", 1458 | "unicode-width", 1459 | ] 1460 | 1461 | [[package]] 1462 | name = "thiserror" 1463 | version = "1.0.57" 1464 | source = "registry+https://github.com/rust-lang/crates.io-index" 1465 | checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" 1466 | dependencies = [ 1467 | "thiserror-impl 1.0.57", 1468 | ] 1469 | 1470 | [[package]] 1471 | name = "thiserror" 1472 | version = "2.0.12" 1473 | source = "registry+https://github.com/rust-lang/crates.io-index" 1474 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 1475 | dependencies = [ 1476 | "thiserror-impl 2.0.12", 1477 | ] 1478 | 1479 | [[package]] 1480 | name = "thiserror-impl" 1481 | version = "1.0.57" 1482 | source = "registry+https://github.com/rust-lang/crates.io-index" 1483 | checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" 1484 | dependencies = [ 1485 | "proc-macro2", 1486 | "quote", 1487 | "syn", 1488 | ] 1489 | 1490 | [[package]] 1491 | name = "thiserror-impl" 1492 | version = "2.0.12" 1493 | source = "registry+https://github.com/rust-lang/crates.io-index" 1494 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 1495 | dependencies = [ 1496 | "proc-macro2", 1497 | "quote", 1498 | "syn", 1499 | ] 1500 | 1501 | [[package]] 1502 | name = "typetag" 1503 | version = "0.2.13" 1504 | source = "registry+https://github.com/rust-lang/crates.io-index" 1505 | checksum = "80960fd143d4c96275c0e60b08f14b81fbb468e79bc0ef8fbda69fb0afafae43" 1506 | dependencies = [ 1507 | "erased-serde", 1508 | "inventory", 1509 | "once_cell", 1510 | "serde", 1511 | "typetag-impl", 1512 | ] 1513 | 1514 | [[package]] 1515 | name = "typetag-impl" 1516 | version = "0.2.13" 1517 | source = "registry+https://github.com/rust-lang/crates.io-index" 1518 | checksum = "bfc13d450dc4a695200da3074dacf43d449b968baee95e341920e47f61a3b40f" 1519 | dependencies = [ 1520 | "proc-macro2", 1521 | "quote", 1522 | "syn", 1523 | ] 1524 | 1525 | [[package]] 1526 | name = "unicase" 1527 | version = "2.8.0" 1528 | source = "registry+https://github.com/rust-lang/crates.io-index" 1529 | checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" 1530 | 1531 | [[package]] 1532 | name = "unicode-ident" 1533 | version = "1.0.11" 1534 | source = "registry+https://github.com/rust-lang/crates.io-index" 1535 | checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" 1536 | 1537 | [[package]] 1538 | name = "unicode-linebreak" 1539 | version = "0.1.5" 1540 | source = "registry+https://github.com/rust-lang/crates.io-index" 1541 | checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" 1542 | 1543 | [[package]] 1544 | name = "unicode-width" 1545 | version = "0.1.11" 1546 | source = "registry+https://github.com/rust-lang/crates.io-index" 1547 | checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" 1548 | 1549 | [[package]] 1550 | name = "utf8parse" 1551 | version = "0.2.1" 1552 | source = "registry+https://github.com/rust-lang/crates.io-index" 1553 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 1554 | 1555 | [[package]] 1556 | name = "version_check" 1557 | version = "0.9.4" 1558 | source = "registry+https://github.com/rust-lang/crates.io-index" 1559 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1560 | 1561 | [[package]] 1562 | name = "vte" 1563 | version = "0.11.1" 1564 | source = "registry+https://github.com/rust-lang/crates.io-index" 1565 | checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" 1566 | dependencies = [ 1567 | "utf8parse", 1568 | "vte_generate_state_changes", 1569 | ] 1570 | 1571 | [[package]] 1572 | name = "vte_generate_state_changes" 1573 | version = "0.1.1" 1574 | source = "registry+https://github.com/rust-lang/crates.io-index" 1575 | checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" 1576 | dependencies = [ 1577 | "proc-macro2", 1578 | "quote", 1579 | ] 1580 | 1581 | [[package]] 1582 | name = "wasi" 1583 | version = "0.11.0+wasi-snapshot-preview1" 1584 | source = "registry+https://github.com/rust-lang/crates.io-index" 1585 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1586 | 1587 | [[package]] 1588 | name = "wasm-bindgen" 1589 | version = "0.2.87" 1590 | source = "registry+https://github.com/rust-lang/crates.io-index" 1591 | checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" 1592 | dependencies = [ 1593 | "cfg-if", 1594 | "wasm-bindgen-macro", 1595 | ] 1596 | 1597 | [[package]] 1598 | name = "wasm-bindgen-backend" 1599 | version = "0.2.87" 1600 | source = "registry+https://github.com/rust-lang/crates.io-index" 1601 | checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" 1602 | dependencies = [ 1603 | "bumpalo", 1604 | "log", 1605 | "once_cell", 1606 | "proc-macro2", 1607 | "quote", 1608 | "syn", 1609 | "wasm-bindgen-shared", 1610 | ] 1611 | 1612 | [[package]] 1613 | name = "wasm-bindgen-macro" 1614 | version = "0.2.87" 1615 | source = "registry+https://github.com/rust-lang/crates.io-index" 1616 | checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" 1617 | dependencies = [ 1618 | "quote", 1619 | "wasm-bindgen-macro-support", 1620 | ] 1621 | 1622 | [[package]] 1623 | name = "wasm-bindgen-macro-support" 1624 | version = "0.2.87" 1625 | source = "registry+https://github.com/rust-lang/crates.io-index" 1626 | checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" 1627 | dependencies = [ 1628 | "proc-macro2", 1629 | "quote", 1630 | "syn", 1631 | "wasm-bindgen-backend", 1632 | "wasm-bindgen-shared", 1633 | ] 1634 | 1635 | [[package]] 1636 | name = "wasm-bindgen-shared" 1637 | version = "0.2.87" 1638 | source = "registry+https://github.com/rust-lang/crates.io-index" 1639 | checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" 1640 | 1641 | [[package]] 1642 | name = "web-time" 1643 | version = "1.1.0" 1644 | source = "registry+https://github.com/rust-lang/crates.io-index" 1645 | checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 1646 | dependencies = [ 1647 | "js-sys", 1648 | "wasm-bindgen", 1649 | ] 1650 | 1651 | [[package]] 1652 | name = "widestring" 1653 | version = "1.1.0" 1654 | source = "registry+https://github.com/rust-lang/crates.io-index" 1655 | checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" 1656 | 1657 | [[package]] 1658 | name = "winapi" 1659 | version = "0.3.9" 1660 | source = "registry+https://github.com/rust-lang/crates.io-index" 1661 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1662 | dependencies = [ 1663 | "winapi-i686-pc-windows-gnu", 1664 | "winapi-x86_64-pc-windows-gnu", 1665 | ] 1666 | 1667 | [[package]] 1668 | name = "winapi-i686-pc-windows-gnu" 1669 | version = "0.4.0" 1670 | source = "registry+https://github.com/rust-lang/crates.io-index" 1671 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1672 | 1673 | [[package]] 1674 | name = "winapi-x86_64-pc-windows-gnu" 1675 | version = "0.4.0" 1676 | source = "registry+https://github.com/rust-lang/crates.io-index" 1677 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1678 | 1679 | [[package]] 1680 | name = "windows" 1681 | version = "0.48.0" 1682 | source = "registry+https://github.com/rust-lang/crates.io-index" 1683 | checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" 1684 | dependencies = [ 1685 | "windows-targets 0.48.5", 1686 | ] 1687 | 1688 | [[package]] 1689 | name = "windows" 1690 | version = "0.56.0" 1691 | source = "registry+https://github.com/rust-lang/crates.io-index" 1692 | checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132" 1693 | dependencies = [ 1694 | "windows-core", 1695 | "windows-targets 0.52.6", 1696 | ] 1697 | 1698 | [[package]] 1699 | name = "windows-core" 1700 | version = "0.56.0" 1701 | source = "registry+https://github.com/rust-lang/crates.io-index" 1702 | checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6" 1703 | dependencies = [ 1704 | "windows-implement", 1705 | "windows-interface", 1706 | "windows-result", 1707 | "windows-targets 0.52.6", 1708 | ] 1709 | 1710 | [[package]] 1711 | name = "windows-implement" 1712 | version = "0.56.0" 1713 | source = "registry+https://github.com/rust-lang/crates.io-index" 1714 | checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" 1715 | dependencies = [ 1716 | "proc-macro2", 1717 | "quote", 1718 | "syn", 1719 | ] 1720 | 1721 | [[package]] 1722 | name = "windows-interface" 1723 | version = "0.56.0" 1724 | source = "registry+https://github.com/rust-lang/crates.io-index" 1725 | checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" 1726 | dependencies = [ 1727 | "proc-macro2", 1728 | "quote", 1729 | "syn", 1730 | ] 1731 | 1732 | [[package]] 1733 | name = "windows-result" 1734 | version = "0.1.1" 1735 | source = "registry+https://github.com/rust-lang/crates.io-index" 1736 | checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" 1737 | dependencies = [ 1738 | "windows-targets 0.52.6", 1739 | ] 1740 | 1741 | [[package]] 1742 | name = "windows-sys" 1743 | version = "0.48.0" 1744 | source = "registry+https://github.com/rust-lang/crates.io-index" 1745 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 1746 | dependencies = [ 1747 | "windows-targets 0.48.5", 1748 | ] 1749 | 1750 | [[package]] 1751 | name = "windows-sys" 1752 | version = "0.52.0" 1753 | source = "registry+https://github.com/rust-lang/crates.io-index" 1754 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1755 | dependencies = [ 1756 | "windows-targets 0.52.6", 1757 | ] 1758 | 1759 | [[package]] 1760 | name = "windows-sys" 1761 | version = "0.59.0" 1762 | source = "registry+https://github.com/rust-lang/crates.io-index" 1763 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1764 | dependencies = [ 1765 | "windows-targets 0.52.6", 1766 | ] 1767 | 1768 | [[package]] 1769 | name = "windows-targets" 1770 | version = "0.48.5" 1771 | source = "registry+https://github.com/rust-lang/crates.io-index" 1772 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 1773 | dependencies = [ 1774 | "windows_aarch64_gnullvm 0.48.5", 1775 | "windows_aarch64_msvc 0.48.5", 1776 | "windows_i686_gnu 0.48.5", 1777 | "windows_i686_msvc 0.48.5", 1778 | "windows_x86_64_gnu 0.48.5", 1779 | "windows_x86_64_gnullvm 0.48.5", 1780 | "windows_x86_64_msvc 0.48.5", 1781 | ] 1782 | 1783 | [[package]] 1784 | name = "windows-targets" 1785 | version = "0.52.6" 1786 | source = "registry+https://github.com/rust-lang/crates.io-index" 1787 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1788 | dependencies = [ 1789 | "windows_aarch64_gnullvm 0.52.6", 1790 | "windows_aarch64_msvc 0.52.6", 1791 | "windows_i686_gnu 0.52.6", 1792 | "windows_i686_gnullvm", 1793 | "windows_i686_msvc 0.52.6", 1794 | "windows_x86_64_gnu 0.52.6", 1795 | "windows_x86_64_gnullvm 0.52.6", 1796 | "windows_x86_64_msvc 0.52.6", 1797 | ] 1798 | 1799 | [[package]] 1800 | name = "windows_aarch64_gnullvm" 1801 | version = "0.48.5" 1802 | source = "registry+https://github.com/rust-lang/crates.io-index" 1803 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 1804 | 1805 | [[package]] 1806 | name = "windows_aarch64_gnullvm" 1807 | version = "0.52.6" 1808 | source = "registry+https://github.com/rust-lang/crates.io-index" 1809 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1810 | 1811 | [[package]] 1812 | name = "windows_aarch64_msvc" 1813 | version = "0.48.5" 1814 | source = "registry+https://github.com/rust-lang/crates.io-index" 1815 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 1816 | 1817 | [[package]] 1818 | name = "windows_aarch64_msvc" 1819 | version = "0.52.6" 1820 | source = "registry+https://github.com/rust-lang/crates.io-index" 1821 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1822 | 1823 | [[package]] 1824 | name = "windows_i686_gnu" 1825 | version = "0.48.5" 1826 | source = "registry+https://github.com/rust-lang/crates.io-index" 1827 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 1828 | 1829 | [[package]] 1830 | name = "windows_i686_gnu" 1831 | version = "0.52.6" 1832 | source = "registry+https://github.com/rust-lang/crates.io-index" 1833 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1834 | 1835 | [[package]] 1836 | name = "windows_i686_gnullvm" 1837 | version = "0.52.6" 1838 | source = "registry+https://github.com/rust-lang/crates.io-index" 1839 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1840 | 1841 | [[package]] 1842 | name = "windows_i686_msvc" 1843 | version = "0.48.5" 1844 | source = "registry+https://github.com/rust-lang/crates.io-index" 1845 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 1846 | 1847 | [[package]] 1848 | name = "windows_i686_msvc" 1849 | version = "0.52.6" 1850 | source = "registry+https://github.com/rust-lang/crates.io-index" 1851 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1852 | 1853 | [[package]] 1854 | name = "windows_x86_64_gnu" 1855 | version = "0.48.5" 1856 | source = "registry+https://github.com/rust-lang/crates.io-index" 1857 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 1858 | 1859 | [[package]] 1860 | name = "windows_x86_64_gnu" 1861 | version = "0.52.6" 1862 | source = "registry+https://github.com/rust-lang/crates.io-index" 1863 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1864 | 1865 | [[package]] 1866 | name = "windows_x86_64_gnullvm" 1867 | version = "0.48.5" 1868 | source = "registry+https://github.com/rust-lang/crates.io-index" 1869 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 1870 | 1871 | [[package]] 1872 | name = "windows_x86_64_gnullvm" 1873 | version = "0.52.6" 1874 | source = "registry+https://github.com/rust-lang/crates.io-index" 1875 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1876 | 1877 | [[package]] 1878 | name = "windows_x86_64_msvc" 1879 | version = "0.48.5" 1880 | source = "registry+https://github.com/rust-lang/crates.io-index" 1881 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 1882 | 1883 | [[package]] 1884 | name = "windows_x86_64_msvc" 1885 | version = "0.52.6" 1886 | source = "registry+https://github.com/rust-lang/crates.io-index" 1887 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1888 | 1889 | [[package]] 1890 | name = "zerocopy" 1891 | version = "0.7.26" 1892 | source = "registry+https://github.com/rust-lang/crates.io-index" 1893 | checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0" 1894 | dependencies = [ 1895 | "zerocopy-derive", 1896 | ] 1897 | 1898 | [[package]] 1899 | name = "zerocopy-derive" 1900 | version = "0.7.26" 1901 | source = "registry+https://github.com/rust-lang/crates.io-index" 1902 | checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" 1903 | dependencies = [ 1904 | "proc-macro2", 1905 | "quote", 1906 | "syn", 1907 | ] 1908 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Max Brown"] 3 | description = "Plot graphs in nushell using numerical lists." 4 | repository = "https://github.com/euphrasiologist/nu_plugin_plot" 5 | edition = "2021" 6 | license = "MIT" 7 | name = "nu_plugin_plot" 8 | version = "0.103.0" 9 | 10 | [dependencies] 11 | nu-plugin = "0.103.0" 12 | nu-protocol = { version = "0.103.0", features = ["plugin"] } 13 | owo-colors = "3.5.0" 14 | fnv = "1.0.7" 15 | term_size = "0.3.2" 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Max Brown 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `nu_plugin_plot` 2 | 3 | A small nu plugin to plot a list as a line graph. 4 | 5 | ## Install 6 | 7 | Not yet on crates.io, so you'll have to clone this repository. I assume you have Rust, and are inside a nushell instance. 8 | 9 | ```console 10 | git clone https://github.com/Euphrasiologist/nu_plugin_plot 11 | cd nu_plugin_plot 12 | ``` 13 | ```console 14 | cargo build --release 15 | plugin add ./target/release/nu_plugin_plot 16 | plugin use plot 17 | ``` 18 | ```console 19 | # test commands 20 | plot -h 21 | hist -h 22 | xyplot -h 23 | ``` 24 | 25 | ## Help 26 | 27 | `plot`, `hist`, and `xyplot` have very similar helps, so I'll print out just plot here. 28 | 29 | ```console 30 | Render an ASCII plot from a list of values. 31 | 32 | Usage: 33 | > plot {flags} 34 | 35 | Flags: 36 | -h, --help - Display the help message for this command 37 | --width - The maximum width of the plot. 38 | --height - The maximum height of the plot. 39 | -t, --title - Provide a title to the plot. 40 | -l, --legend - Plot a tiny, maybe useful legend. 41 | -b, --bars - Change lines to bars. 42 | -s, --steps - Change lines to steps. 43 | -p, --points - Change lines to points. 44 | ``` 45 | 46 | ## Examples 47 | in the following, we define a bunch of sine wave as follows 48 | ```nushell 49 | let one = (seq 0.0 0.01 20.0 | math sin) 50 | let two = (seq 1.0 0.01 21.0 | math sin) 51 | let three = (seq 2.0 0.01 22.0 | math sin) 52 | let four = (seq 3.0 0.01 23.0 | math sin) 53 | ``` 54 | 55 | ### Basic plots 56 | - plot a single line 57 | ```nushell 58 | $one | plot 59 | ``` 60 | ``` 61 | ⡁ ⢀⡴⠋⠉⠳⣄ ⢀⡴⠋⠉⠳⣄ ⢀⡴⠋⠉⠳⣄ ⢀⡄ 1.0 62 | ⠄ ⢠⠏ ⠈⢦ ⢠⠏ ⠈⢦ ⢠⠏ ⠘⢦ ⣠⠏ 63 | ⠂ ⢠⠏ ⠈⢧ ⢠⠏ ⠈⢧ ⢠⠃ ⠈⢧ ⢰⠃ 64 | ⡁⢀⠏ ⠈⣇ ⢠⠏ ⠈⣆ ⢠⠏ ⠈⣆ ⢠⠇ 65 | ⠄⡞ ⠘⡆ ⢀⡞ ⠘⡆ ⢀⡎ ⠘⡄ ⢀⡎ 66 | ⡾⠁ ⠸⡄ ⡼ ⠹⡀ ⡼ ⢹⡀ ⡜ 67 | ⡁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈⢳⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⣱⠋ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⢳⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⣸⠁⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⢻ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈⣸⠁⠈ ⠁⠈ 68 | ⠄ ⢧ ⢠⠇ ⠈⢧ ⢠⠃ ⠈⢇ ⢠⠃ 69 | ⠂ ⠈⣆ ⢀⡎ ⠘⣆ ⢀⠏ ⠘⣆ ⢠⠏ 70 | ⡁ ⠘⣆ ⢀⡞ ⠘⣆ ⢀⡞ ⠘⡄ ⢀⡞ 71 | ⠄ ⠘⣆ ⢀⡞ ⠘⣆ ⢀⠞ ⠙⣆ ⢠⠞ 72 | ⠂ ⠈⠳⣄⣀⡴⠋ ⠈⠳⣄⣀⡴⠋ ⠈⠣⣄⣀⡴⠋ -1.0 73 | 0.0 2000.0 74 | ``` 75 | 76 | - plot two lines 77 | ```nushell 78 | [$one $two] | plot 79 | ``` 80 | ``` 81 | ⣡⠞⠉⠙⢦⡴⠋⠉⠳⣄ ⣠⠞⠉⠙⢦⡴⠋⠉⠳⣄ ⣠⠞⠉⠙⢦⡴⠋⠉⠳⣄ ⣠⠞⠉⠙⢦⡄ 1.0 82 | ⠅ ⢠⠏⠹⡄ ⠈⢦ ⡼⠁ ⢠⠏⠹⡄ ⠈⢦ ⡼⠁ ⢠⠏⠱⡄ ⠘⢦ ⡼⠁ ⣠⠏⠁ 83 | ⠂ ⢠⠏ ⠹⡄ ⠈⢧ ⡼⠁ ⢠⠏ ⠹⡄ ⠈⢧ ⡼⠁ ⢠⠃ ⠹⡄ ⠈⢧ ⡜ ⢰⠃ 84 | ⡁⢀⠏ ⠹⡀ ⠈⣇ ⡸⠁ ⢠⠏ ⠹⡀ ⠈⣆ ⡼⠁ ⢠⠏ ⢹⡀ ⠈⣆ ⡼⠁ ⢠⠇ 85 | ⠄⡞ ⢳⡀ ⠘⡆ ⢰⠃ ⢀⡞ ⢳ ⠘⡆ ⣰⠃ ⢀⡎ ⢳ ⠘⡄ ⣰⠃ ⢀⡎ 86 | ⡾⠁ ⢧ ⠸⡄ ⢠⠏ ⡼ ⢧ ⠹⡀ ⢠⠇ ⡼ ⠈⢇ ⢹⡀ ⢠⠇ ⡜ 87 | ⡁⠈ ⠁⠈ ⠁⠈ ⠙⣎ ⠁⠈⢳⠁⠈ ⠁⠈ ⠁⠈⡞⠁⠈ ⣱⠋ ⠁⠈ ⠁⠈ ⠁⠘⡆⠁⠈ ⢳⠈ ⠁⠈ ⠁⠈⢀⡟⠈ ⠁⣸⠁⠁⠈ ⠁⠈ ⠁⠈⠘⡇⠈ ⠁⢻ ⠁⠈ ⠁⠈ ⢁⡎ ⠁⠈⣸⠁⠈ ⠁⠈ 88 | ⠄ ⠸⡄ ⢧ ⡼ ⢠⠇ ⠸⡄ ⠈⢧ ⡼ ⢠⠃ ⠹⡄ ⠈⢇ ⡼ ⢠⠃ 89 | ⠂ ⢹⡀ ⠈⣆ ⣰⠃ ⢀⡎ ⢳⡀ ⠘⣆ ⣰⠁ ⢀⠏ ⢳⡀ ⠘⣆ ⣸⠁ ⢠⠏ 90 | ⡁ ⢳⡀ ⠘⣆ ⣰⠃ ⢀⡞ ⢳⡀ ⠘⣆ ⣰⠃ ⢀⡞ ⢳⡀ ⠘⡄ ⣰⠃ ⢀⡞ 91 | ⠄ ⠳⡀ ⠘⣆⣰⠃ ⢀⡞ ⢳⡀ ⠘⣆⣰⠃ ⢀⠞ ⢳⡀ ⠙⣆⡴⠃ ⢠⠞ 92 | ⠂ ⠙⢦⣀⣠⠞⠳⣄⣀⡴⠋ ⠙⢦⣀⣠⠞⠳⣄⣀⡴⠋ ⠙⢦⣀⣠⠞⠣⣄⣀⡴⠋ -1.0 93 | 0.0 2000.0 94 | ``` 95 | 96 | - plot four lines with a legend and title 97 | ```nushell 98 | [$one $two $three $four] | plot -l -t "Four sine lines!" 99 | ``` 100 | ``` 101 | Four sine lines! 102 | ⣥⠞⠉⠙⢦⡴⠋⠉⠳⣄ ⣠⠞⠉⠙⢦⡴⠋⠉⠳⣤⠞⠉⠙⢦⡴⠋⠉⠳⣄ ⣠⠞⠉⠙⢦⡴⠋⠉⠳⣤⠞⠉⠙⢦⡴⠋⠉⠳⣄ ⣠⠞⠉⠙⢦⡴⠋⠉⠳⣤⠞⠉⠙⢦⡄ 1.0 103 | ⠍⢧ ⢠⠏⠹⡄ ⠈⢦ ⡴⠃ ⢠⠎⠹⡄ ⡼⠉⢧ ⢠⠏⠹⡄ ⠈⢦ ⡴⠁ ⢠⠏⠹⡄ ⡼⠉⢧ ⢠⠏⠱⡄ ⠘⢦ ⡴⠁ ⢠⠏⠹⡄ ⡼⠉⢦ ⣠⠏⠁ 104 | ⠂ ⢣⠏ ⠹⡄ ⠈⢧ ⡼⠁ ⢠⠏ ⠘⡼⠁ ⠈⢧⠏ ⠹⡄ ⠈⢧ ⡼⠁ ⢠⠏ ⠹⡼⠁ ⠈⢧⠃ ⠹⡄ ⠈⢧ ⡼⠁ ⢠⠏ ⠹⡜ ⠈⢷⠃ 105 | ⡁⢀⠏⢧ ⠹⡀ ⠈⣇ ⣰⠃ ⢀⡎ ⡸⠹⡄ ⢠⠏⢧ ⠹⡀ ⠈⣆ ⣰⠁ ⢀⠏ ⡼⠹⡄ ⢠⠏⢇ ⢹⡀ ⠈⣆ ⣸⠁ ⢀⠏ ⡼⠹⡀ ⢠⠏⣇ 106 | ⠄⡞ ⠘⣆ ⢳⡀ ⠘⡆ ⢠⠇ ⡞ ⢰⠃ ⢱⣀⡞ ⠘⣆ ⢳ ⠘⡆ ⢰⠃ ⡞ ⣰⠃ ⢳⣀⡎ ⠘⡆ ⢳ ⠘⡄ ⢰⠃ ⡞ ⣰⠃ ⢳⢀⡎ ⠘⡆ 107 | ⣿⠁ ⠸⡄ ⢧ ⠸⣄⡏ ⡸⠁ ⢠⠏ ⣿ ⠸⡄ ⢧ ⠹⣀⠏ ⡼ ⢠⠇ ⣿ ⠸⡄ ⠈⢇ ⢹⣀⠏ ⡼ ⢠⠇ ⣿ ⠹⡀ 108 | ⡉⣏ ⠁⠈⢹⡁⠈ ⠙⣎ ⠁⠈⣿⠁⠈ ⢱⠋ ⠁⠈⡞⠁⠈ ⣱⠋⣆⠁⠈ ⢳⡈ ⠁⠘⡆⠁⠈ ⣿⠈ ⠁⢸⠃⠁⠈⢀⡟⠈ ⠁⣸⠉⣇⠈ ⠁⢻ ⠁⠈⠘⡇⠈ ⠁⣿ ⠁⠈⣰⠃⠈ ⢁⡎ ⠁⠈⣸⠙⣎ ⠁⠈⠁ 109 | ⠄⠘⡄ ⢧ ⠸⡄ ⣸⠁⢧ ⢀⠏ ⡼ ⢠⠇ ⠘⡄ ⢧ ⠸⡄ ⡸⠉⢧ ⢠⠇ ⡼ ⢠⠃ ⠸⡄ ⢧ ⠹⡄ ⡼⠈⢇ ⢠⠇ ⡼ ⢠⠃ ⠸⡄ 110 | ⠂ ⠹⡄ ⠈⢇ ⢹⣰⠃ ⠈⣆⡞ ⣰⠃ ⢀⡎ ⠹⡀ ⠈⣇ ⢳⣰⠃ ⠘⣆⡞ ⣰⠁ ⢀⠏ ⢹⡀ ⠈⣆ ⢳⣰⠃ ⠘⣆⡏ ⣸⠁ ⢠⠏ ⢱⡀ 111 | ⡁ ⢳⡀ ⠘⣆ ⢠⢳⡀ ⡞⣆ ⣰⠃ ⢀⡞ ⢳⡀ ⠘⣆ ⣰⢳⡀ ⢀⡞⣆ ⣰⠃ ⢀⡞ ⢳⡀ ⠘⣆ ⣰⢳⡀ ⢀⡞⡄ ⣰⠃ ⢀⡞ ⢳⡀ 112 | ⠄ ⠱⡄ ⠘⢦⣠⠋ ⠳⣀⡞ ⠘⣆⣰⠃ ⢀⡞ ⠳⡄ ⠘⣆⣰⠃ ⢳⣀⡞ ⠘⣆⣰⠃ ⢀⠞ ⢳⡀ ⠘⣆⣰⠃ ⢳⣀⡞ ⠙⣆⡴⠃ ⢠⠞ ⢳⡀ 113 | ⠂ ⠙⢦⣀⣠⠜⠳⣄⣀⡴⠛⢦⣀⣠⠞⠳⣄⣀⡴⠋ ⠙⢦⣀⣠⠞⠳⣄⣀⡴⠛⢦⣀⣠⠞⠳⣄⣀⡴⠋ ⠙⢦⣀⣠⠞⠳⣄⣀⡴⠛⢦⣀⣠⠞⠣⣄⣀⡴⠋ -1.0 114 | 0.0 2000.0 115 | Line 1: --- Line 2: --- Line 3: --- Line 4: --- 116 | ``` 117 | 118 | ### Bivariate 'xyplot' 119 | > **Note** 120 | > input must be a two element nested list 121 | 122 | - make a nice ellipse 123 | ```nushell 124 | [$one $two] | xyplot 125 | ``` 126 | ``` 127 | ⡁⢀⣀⣀⣀⡤⠤⠤⠤⠤⠖⠒⠒⠒⠒⠒⠚⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠒⠒⠒⠒⠦⠤⠤⣀⣀ 1.0 128 | ⢀⣀⣀⡤⠤⠤⠖⠒⠒⠋⠉⠍⠁ ⠈⠉⠓⠦⣀ 129 | ⢀⣀⣠⠤⠤⠒⠒⠋⠉⠉ ⠂ ⠘⡆ 130 | ⢀⣀⡤⠤⠒⠒⠉⠉ ⡁ ⣰⠃ 131 | ⣀⡤⠴⠒⠋⠉ ⠄ ⢀⡤⠞⠁ 132 | ⣀⡤⠔⠚⠉ ⠂ ⣀⡤⠖⠉ 133 | ⠁⠈ ⢁⣨⠖⠋⠉ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈⡁⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈ ⠁⠈⢀⣡⠬⠖⠋⠉ ⠁⠈ ⠁⠈ 134 | ⣠⠖⠋ ⠄ ⢀⣀⡤⠴⠒⠋⠉ 135 | ⡼⠁ ⠂ ⢀⣀⣠⠤⠖⠒⠋⠉ 136 | ⢧ ⡁ ⢀⣀⣀⡤⠤⠔⠒⠚⠉⠉ 137 | ⠈⠑⠦⣄⣀ ⣄⣀⣀⡤⠤⠤⠖⠒⠒⠋⠉⠉ 138 | ⠈⠉⠓⠒⠒⠦⠤⠤⠤⢤⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣠⠤⠤⠤⠤⠤⠤⠔⠒⠒⠒⠒⠉⠉⠉⠉⠂ -1.0 139 | -1.0 1.0 140 | ``` 141 | 142 | - bivariate line plot and diagonal dots! 143 | ```nushell 144 | [(seq 1 100) (seq 1 100 | reverse)] | xyplot -p 145 | ``` 146 | ``` 147 | ⠁⠁⠒⠠⠠⢀⢀ 100.0 148 | ⠁⠁⠂⠂⠤⢀⢀ 149 | ⠈⠈⠂⠂⠂⠄⢄⢀ 150 | ⠈⠈⠐⠂⠄⠄⡀⡀ 151 | ⠈⠈⠐⠐⠠⠄⡀⡀⡀ 152 | ⠉⠐⠐⠠⠠⡀⡀ 153 | ⠁⠑⠐⠠⠠⢀⡀ 154 | ⠁⠁⠁⠒⠠⠠⢀⢀ 155 | ⠁⠁⠂⠂⠤⢀⢀ 156 | ⠈⠈⠂⠂⠄⠄⢄⢀ 157 | ⠈⠈⠐⠂⠄⠄⡀⡀ 158 | ⠈⠈⠐⠐⠠⠄⡀⡀ 1.0 159 | 1.0 100.0 160 | ``` 161 | 162 | ### Plot histograms 163 | in this section, we define the following lists 164 | ```nushell 165 | let r1 = (seq 1 100 | each { random int ..30}) 166 | let r2 = (seq 1 100 | each { random int ..30}) 167 | ``` 168 | 169 | - compare two uniform distributions 170 | ```nushell 171 | [$r1 $r2] | hist 172 | ``` 173 | ``` 174 | ⡁ ⢰⢇ 12.0 175 | ⠄ ⡎⠘⡄ 176 | ⠂ ⢀⠔⡇ ⢀⢾ ⢰⠁ ⠸⡀ 177 | ⡁ ⡠⠊ ⠸⡀ ⡜⠈⡆ ⡜ ⢱ 178 | ⠍⠑⠢⢄⣀⠔⠁ ⢇ ⡸ ⢱ ⢠⠃ ⢣ 179 | ⠂ ⠈ ⠘⡄ ⡔⢇ ⢀⠾⡀ ⢀⡾⡄ ⢠⠚⢄ ⢰⠁⡠⠚⡌⡆ ⢀ ⡜ ⡔⠉⠑⠒⠬⢆⣀ 180 | ⡁ ⢣ ⢀⠜ ⡈⢆ ⡎ ⢣ ⡼ ⢫⢆ ⡠⠃⢀⣀⠱⡀ ⢠⢃⠎ ⠈⢧ ⢠⠋⠢⡀ ⢀⠇⡜ ⠉⠒⠤⣀⣀⣀⣀⣀ 181 | ⠕⢄ ⠈⡆ ⢀⠎⢠⠊⠑⢌⢆ ⡜ ⠈⣇ ⣸⠃ ⢏⢢ ⡰⠁⡰⠁ ⠉⠚⠦⡞⠁ ⠘⣦ ⡎⢀⣀⠑⢄ ⡸⡜ ⠈⠑⠢⢄ 182 | ⠂⠈⠢⡀ ⢱⢠⠃⡔⠁ ⠈⠪⣆ ⡸ ⠸⡆ ⢰⡏ ⠈⡆⠣⡜ ⡰⠁ ⢣⠣⣀⡠⡜⠊⠁ ⠉⠚⠦⡟ 183 | ⡁ ⠑⢄⣀⣀⣀⣀⣀⡠⠔⠊⢧⠊ ⠙⢶⠁ ⢻⡄⢠⡻ ⠘⡄ ⡜ ⠸⡀ ⢰⠁ 184 | ⠄ ⠈⡷⣮⠃ ⠱⡜ ⢇⢠⠃ 185 | ⡂⢀ ⡀⢀ ⡀⢀ ⡀⢀ ⡀⢀ ⡀⢀ ⡀⢀ ⡀⢀ ⡀⢀ ⡀⢀ ⡸⣎ ⡀⢀ ⡀⢀ ⡀⢀ ⡀⢀ ⡀⢀ ⡀⢀ ⡀⢀ ⡀⢀ ⡸⣎ ⡀⢀ ⡀⢀ ⡀⢀ ⡀⢀ ⡀⢀ ⡀⢀ ⡀⢀ ⡀⢀ ⡀⢀ ⡀⢀ 0.0 186 | 0.0 30.0 187 | ``` 188 | 189 | - `-b` for bars 190 | ```nushell 191 | [$r1 $r2] | hist -b 192 | ``` 193 | ``` 194 | ⡁ ⡏⠉⠉⠉⡇ 12.0 195 | ⠄ ⡇ ⡇ 196 | ⠂ ⢸⠉⠉⠉⠉⡇ ⡏⠉⠉⠉⢹ ⡇ ⡇ 197 | ⡁ ⢸ ⡇ ⡇ ⢸ ⡇ ⡇ 198 | ⡇ ⢸ ⡇ ⡇ ⢸ ⡇ ⡇ 199 | ⡏⠉⠉⠉⢹ ⡇ ⢸⠉⠉⠉⠉⡇ ⢸⠉⠉⠉⢹ ⡏⠉⠉⠉⢹ ⡏⠉⠉⠉⢹ ⡏⠉⠉⠉⢹ ⣀⣀⣀⣀⣀ ⡏⠉⠉⠉⣇⣀⣀⣀⣀ 200 | ⡇ ⢸ ⡇ ⢸⣀⣀⣀⣀⡇ ⢸ ⢸ ⡇ ⢸ ⣇⣀⣀⣀⣸ ⡇ ⢸ ⡇ ⢸ ⡇ ⡇ ⢸⣀⣀⣀⣀⣀⣀⣀⣀⣀ 201 | ⡇ ⢸ ⡇ ⢸ ⡇ ⢸ ⢸ ⡇ ⢸ ⡇ ⢸⣀⣀⣀⣀⡇ ⢸ ⣇⣀⣀⣀⣸ ⡇ ⡇ ⢸ ⣇⣀⣀⣀⣸ 202 | ⡇ ⢸ ⣇⣀⣀⣀⣸ ⡇ ⢸ ⢸ ⡇ ⢸⣀⣀⣀⣀⡇ ⢸ ⡇ ⢸⣀⣀⣀⣀⡇ ⢸⣀⣀⣀⣀⡇ ⡇ ⢸ ⡇ ⢸ 203 | ⣇⣀⣀⣀⣸⣀⣀⣀⣀⣇⣀⣀⣀⣸ ⣇⣀⣀⣀⣸ ⢸ ⡇ ⢸ ⡇ ⢸ ⡇ ⢸ ⡇ ⢸ ⡇ ⡇ ⢸ ⡇ ⢸ 204 | ⡇ ⢸ ⡇ ⢸ ⡇ ⢸ ⢸⣀⣀⣀⣀⡇ ⢸⣀⣀⣀⣀⡇ ⢸ ⡇ ⢸ ⡇ ⢸ ⡇ ⡇ ⢸ ⡇ ⢸ 205 | ⡇⢀ ⡀⢸ ⡀⢀ ⡇⢀ ⡀⢸ ⡀⢀ ⡇⢀ ⡀⢸ ⡀⢀⢸⣀⣀⣀⣀⣇ ⡀⢀⢸⡀⢀ ⡀⣇ ⡀⢀⢸⡀⢀ ⡀⣇ ⡀⢀⢸⣀⣀⣀⣀⣇ ⡀⢀⢸⡀⢀ ⡀⣇ ⡀⢀⡇⡀⢀ ⣸⢀ ⡀⢀⡇⡀⢀ ⣸⢀ ⡀⢀ 0.0 206 | 0.0 30.0 207 | ``` 208 | 209 | - up the number of bins 210 | ```nushell 211 | [$r1 $r2] | hist -b --bins 50 212 | ``` 213 | ``` 214 | ⡁ ⡏⠉⡇ 8.0 215 | ⠄ ⢠⠤⢤ ⡤⠤⡄ ⡇ ⡇ 216 | ⠂ ⢸ ⢸ ⡇ ⡇ ⡇ ⡇ 217 | ⡁ ⢸ ⢸ ⡇ ⡇ ⡏⢹ ⡇ ⡇ ⡏⢹ 218 | ⡄ ⢸ ⢸ ⡇ ⡇ ⢠⠤⢤ ⡤⢤⠤⢤ ⡇⢸ ⡧⠤⡇ ⡇⢸ ⢠⠤⢤ 219 | ⡇ ⢸ ⢸ ⡇ ⡇ ⣀⣀⣀⣀ ⣀⣀⡀⢸ ⢸ ⡇⢸ ⢸ ⢀⣀⣀ ⣀⣀⡇⢸ ⢀⣀⡀ ⢀⣀⡇ ⡇ ⡇⢸ ⣀⣀⡀⢸⣀⣸ 220 | ⡇ ⢸ ⢸ ⡇ ⡇ ⡇ ⡇⢸ ⡇ ⡇⢸ ⢸ ⡇⢸ ⢸ ⢸ ⢸ ⡇ ⡇⢸ ⢸ ⡇ ⢸ ⡇ ⡇ ⡇⢸ ⡇ ⡇⢸ ⢸ 221 | ⡗⠒⡆⢸ ⢸ ⡇ ⡗⢲ ⢰⠒⡆ ⡇ ⡗⢺ ⡇ ⡇⢸⠒⢺ ⡇⢸ ⢸ ⡖⠒⡖⢲ ⢸⠒⢺ ⡇ ⡗⢺ ⢸ ⡇ ⡖⢲ ⢰⠒⢲⠒⡆ ⡖⢲ ⢸ ⡇ ⡇ ⡇⢸ ⡗⠒⡇⢸ ⢸⠒⡆ 222 | ⡇ ⡇⢸⣀⣸ ⡇ ⣇⣸ ⢸⣀⡇ ⡇ ⡇⢸ ⢀⣀⣇⣀⡇⢸ ⢸ ⣇⣸⣀⣸ ⡇ ⣇⣸ ⢸ ⢸ ⡇ ⡇⢸ ⢸ ⡇ ⡇⢸ ⢸ ⢸ ⡇ ⡇⢸ ⢸⣀⡇ ⡇ ⡇⢸ ⢀⣀⡇ ⡇⢸ ⢸ ⡇ 223 | ⡇ ⡇⢸ ⢸ ⡇ ⡇⢸ ⢸ ⡇ ⡇ ⡇⢸ ⢸ ⡇ ⡇⢸ ⢸ ⡇⢸ ⢸ ⡇ ⡇⢸ ⢸ ⢸ ⡇ ⡇⢸ ⢸ ⡇ ⡇⢸ ⢸ ⢸ ⡇ ⡇⢸ ⢸ ⡇ ⡇ ⡇⢸ ⢸ ⡇ ⡇⢸ ⢸ ⡇ 224 | ⡇ ⡇⢸ ⢸ ⡇ ⡇⢸ ⢸ ⡇ ⡗⠒⡇⢸ ⢸ ⡇ ⡇⢸ ⢸ ⢰⠒⡆ ⡇⢸ ⢸ ⡗⠒⡇⢸ ⢸ ⢸ ⡇ ⡇⢸ ⢸ ⡇ ⡇⢸ ⢸ ⢸⠒⡇ ⡇⢸ ⢸ ⡇ ⡇ ⡇⢸ ⢸⠒⡇ ⡇⢸ ⢸ ⡇ 225 | ⡇⢀⣇⣸⢀⢸⣀⣇⣀⡇⢸⣀⣸⢀⣇⣀⣇ ⡇⢸⣀⣸⢀⡇⡀⣇⣸⡀⢸⣀⣸⣀⣇⣀⣇⢸⡀⢸⣀⡇⢀⡇⣸⣀⣸⡀⢸⣀⡇⢀⡇⣸⣀⣸⡀⣇⣀⣇⣸⣀⣸⢀⢸⡀⣇⣀⡇⢸⣀⣸⢀⡇⡀⣇⣀⡇⢸⣀⣸⢀⡇⡀⣇⣸⡀⢸ ⡇⢀ 0.0 226 | 0.0 30.0 227 | ``` 228 | 229 | ### If you've got R installed (& Rscript) 230 | go crazy! 231 | forget `ggplot`! 232 | ```nushell 233 | let x = (Rscript -e "cat(dnorm(seq(-4, 4, length=100)))" | into string | split row ' ' | into decimal) 234 | let y = (Rscript -e "cat(dnorm(seq(-3, 6, length=100)))" | into string | split row ' ' | into decimal) 235 | 236 | [$x $y] | plot -bl -t "Two normal distributions" 237 | ``` 238 | 239 | ## Features 240 | Plot: 241 | 242 | - [x] a single numeric list 243 | - [x] a list of numeric lists 244 | - [x] with colour support 245 | - [x] with legend 246 | - [x] with title 247 | - [x] scatter plots (as a list of two numeric lists) 248 | - [x] histogram (list rendered as a bar chart) 249 | - [ ] nested xyplot (i.e. multiple xyplots on the same plot...) 250 | - [ ] records..? 251 | 252 | Please help me make this better! Submit issues/PR's, happy to chat. 253 | 254 | The color rendering inside nushell is slightly confusing - you may notice I've included my own modified copies of `textplots` and `drawille` in the source code. This is because their color rendering method was not working inside the plugin - I still don't know why. 255 | -------------------------------------------------------------------------------- /src/color_plot/drawille.rs: -------------------------------------------------------------------------------- 1 | //! A PERSONAL FORK OF 'drawille' VERSION 0.3.0 2 | //! WITH ANSI COLOR SUPPORT 3 | //! 4 | //! `drawille` – a terminal graphics library for Rust, based on the Python library 5 | //! [drawille](https://github.com/asciimoo/drawille). 6 | //! 7 | //! This crate provides an interface for utilising Braille characters to draw a picture to a 8 | //! terminal, allowing for much smaller pixels but losing proper colour support. 9 | //! 10 | //! # Example 11 | //! 12 | //! ``` 13 | //! extern crate drawille; 14 | //! 15 | //! use drawille::Canvas; 16 | //! 17 | //! fn main() { 18 | //! let mut canvas = Canvas::new(10, 10); 19 | //! canvas.set(5, 4); 20 | //! canvas.line(2, 2, 8, 8); 21 | //! assert_eq!(canvas.frame(), [ 22 | //! " ⢄ ", 23 | //! " ⠙⢄ ", 24 | //! " ⠁ "].join("\n")); 25 | //! } 26 | //! ``` 27 | use std::char; 28 | use std::cmp; 29 | 30 | use fnv::FnvHashMap; 31 | pub use owo_colors::AnsiColors as PixelColor; 32 | use owo_colors::OwoColorize; 33 | 34 | // extern crate colored; 35 | // pub use colored::Color as PixelColor; 36 | // use colored::Colorize; 37 | 38 | static PIXEL_MAP: [[u8; 2]; 4] = [[0x01, 0x08], [0x02, 0x10], [0x04, 0x20], [0x40, 0x80]]; 39 | 40 | /// A canvas object that can be used to draw to the terminal using Braille characters. 41 | #[derive(Clone, Debug, PartialEq, Eq)] 42 | pub struct Canvas { 43 | chars: FnvHashMap<(u16, u16), (u8, char, bool, PixelColor)>, 44 | width: u16, 45 | height: u16, 46 | } 47 | 48 | impl Canvas { 49 | /// Creates a new `Canvas` with the given width and height. 50 | /// 51 | /// Note that the `Canvas` can still draw outside the given dimensions (expanding the canvas) 52 | /// if a pixel is set outside the dimensions. 53 | pub fn new(width: u32, height: u32) -> Canvas { 54 | Canvas { 55 | chars: FnvHashMap::default(), 56 | width: (width / 2) as u16, 57 | height: (height / 4) as u16, 58 | } 59 | } 60 | 61 | /// Clears the canvas. 62 | pub fn clear(&mut self) { 63 | self.chars.clear(); 64 | } 65 | 66 | /// Sets a pixel at the specified coordinates. 67 | pub fn set(&mut self, x: u32, y: u32) { 68 | let (row, col) = ((x / 2) as u16, (y / 4) as u16); 69 | let a = self 70 | .chars 71 | .entry((row, col)) 72 | .or_insert((0, ' ', false, PixelColor::White)); 73 | a.0 |= PIXEL_MAP[y as usize % 4][x as usize % 2]; 74 | a.1 = ' '; 75 | a.2 = false; 76 | a.3 = PixelColor::White; 77 | } 78 | 79 | /// Sets a pixel at the specified coordinates. 80 | /// specifying the color of the braille char 81 | pub fn set_colored(&mut self, x: u32, y: u32, color: PixelColor) { 82 | let (row, col) = ((x / 2) as u16, (y / 4) as u16); 83 | let a = self 84 | .chars 85 | .entry((row, col)) 86 | .or_insert((0, ' ', false, PixelColor::White)); 87 | a.0 |= PIXEL_MAP[y as usize % 4][x as usize % 2]; 88 | a.1 = ' '; 89 | a.2 = true; 90 | a.3 = color; 91 | } 92 | 93 | /// Sets a letter at the specified coordinates. 94 | pub fn set_char(&mut self, x: u32, y: u32, c: char) { 95 | let (row, col) = ((x / 2) as u16, (y / 4) as u16); 96 | let a = self 97 | .chars 98 | .entry((row, col)) 99 | .or_insert((0, ' ', false, PixelColor::White)); 100 | a.0 = 0; 101 | a.1 = c; 102 | a.2 = false; 103 | a.3 = PixelColor::White; 104 | } 105 | 106 | /// Draws text at the specified coordinates (top-left of the text) up to max_width length 107 | pub fn text(&mut self, x: u32, y: u32, max_width: u32, text: &str) { 108 | for (i, c) in text.chars().enumerate() { 109 | let w = i as u32 * 2; 110 | if w > max_width { 111 | return; 112 | } 113 | self.set_char(x + w, y, c); 114 | } 115 | } 116 | 117 | /// Deletes a pixel at the specified coordinates. 118 | pub fn unset(&mut self, x: u32, y: u32) { 119 | let (row, col) = ((x / 2) as u16, (y / 4) as u16); 120 | let a = self 121 | .chars 122 | .entry((row, col)) 123 | .or_insert((0, ' ', false, PixelColor::White)); 124 | a.0 &= !PIXEL_MAP[y as usize % 4][x as usize % 2]; 125 | } 126 | 127 | /// Toggles a pixel at the specified coordinates. 128 | pub fn toggle(&mut self, x: u32, y: u32) { 129 | let (row, col) = ((x / 2) as u16, (y / 4) as u16); 130 | let a = self 131 | .chars 132 | .entry((row, col)) 133 | .or_insert((0, ' ', false, PixelColor::White)); 134 | a.0 ^= PIXEL_MAP[y as usize % 4][x as usize % 2]; 135 | } 136 | 137 | /// Detects whether the pixel at the given coordinates is set. 138 | pub fn get(&self, x: u32, y: u32) -> bool { 139 | let (row, col) = ((x / 2) as u16, (y / 4) as u16); 140 | self.chars.get(&(row, col)).map_or(false, |a| { 141 | let dot_index = PIXEL_MAP[y as usize % 4][x as usize % 2]; 142 | a.0 & dot_index != 0 143 | }) 144 | } 145 | 146 | /// Returns a `Vec` of each row of the `Canvas`. 147 | /// 148 | /// Note that each row is actually four pixels high due to the fact that a single Braille 149 | /// character spans two by four pixels. 150 | pub fn rows(&self) -> Vec { 151 | let mut maxrow = self.width; 152 | let mut maxcol = self.height; 153 | for &(x, y) in self.chars.keys() { 154 | if x > maxrow { 155 | maxrow = x; 156 | } 157 | if y > maxcol { 158 | maxcol = y; 159 | } 160 | } 161 | 162 | let mut result = Vec::with_capacity(maxcol as usize + 1); 163 | for y in 0..=maxcol { 164 | let mut row = String::with_capacity(maxrow as usize + 1); 165 | for x in 0..=maxrow { 166 | let cell = 167 | self.chars 168 | .get(&(x, y)) 169 | .cloned() 170 | .unwrap_or((0, ' ', false, PixelColor::White)); 171 | match cell { 172 | (0, _, _, _) => row.push(cell.1), 173 | (_, _, false, _) => row.push(char::from_u32(0x2800 + cell.0 as u32).unwrap()), 174 | (_, _, true, _) => { 175 | row = format!( 176 | "{0}{1}", 177 | row, 178 | String::from(char::from_u32(0x2800 + cell.0 as u32).unwrap()) 179 | .color(cell.3) 180 | ) 181 | } 182 | }; 183 | } 184 | result.push(row); 185 | } 186 | result 187 | } 188 | 189 | /// Draws the canvas to a `String` and returns it. 190 | pub fn frame(&self) -> String { 191 | self.rows().join("\n") 192 | } 193 | 194 | /// Draws a line from `(x1, y1)` to `(x2, y2)` onto the `Canvas`. 195 | pub fn line(&mut self, x1: u32, y1: u32, x2: u32, y2: u32) { 196 | let xdiff = cmp::max(x1, x2) - cmp::min(x1, x2); 197 | let ydiff = cmp::max(y1, y2) - cmp::min(y1, y2); 198 | let xdir = if x1 <= x2 { 1 } else { -1 }; 199 | let ydir = if y1 <= y2 { 1 } else { -1 }; 200 | 201 | let r = cmp::max(xdiff, ydiff); 202 | 203 | for i in 0..=r { 204 | let mut x = x1 as i32; 205 | let mut y = y1 as i32; 206 | 207 | if ydiff != 0 { 208 | y += ((i * ydiff) / r) as i32 * ydir; 209 | } 210 | if xdiff != 0 { 211 | x += ((i * xdiff) / r) as i32 * xdir; 212 | } 213 | 214 | self.set(x as u32, y as u32); 215 | } 216 | } 217 | 218 | /// Draws a line from `(x1, y1)` to `(x2, y2)` onto the `Canvas` 219 | /// specifying the color of the line 220 | pub fn line_colored(&mut self, x1: u32, y1: u32, x2: u32, y2: u32, color: PixelColor) { 221 | let xdiff = cmp::max(x1, x2) - cmp::min(x1, x2); 222 | let ydiff = cmp::max(y1, y2) - cmp::min(y1, y2); 223 | let xdir = if x1 <= x2 { 1 } else { -1 }; 224 | let ydir = if y1 <= y2 { 1 } else { -1 }; 225 | 226 | let r = cmp::max(xdiff, ydiff); 227 | 228 | for i in 0..=r { 229 | let mut x = x1 as i32; 230 | let mut y = y1 as i32; 231 | 232 | if ydiff != 0 { 233 | y += ((i * ydiff) / r) as i32 * ydir; 234 | } 235 | if xdiff != 0 { 236 | x += ((i * xdiff) / r) as i32 * xdir; 237 | } 238 | 239 | self.set_colored(x as u32, y as u32, color); 240 | } 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /src/color_plot/mod.rs: -------------------------------------------------------------------------------- 1 | //! Forks of some nice open source plotting 2 | //! and drawing Rust libraries so we can get 3 | //! ANSI color support. 4 | pub mod drawille; 5 | pub mod textplots; 6 | -------------------------------------------------------------------------------- /src/color_plot/textplots/mod.rs: -------------------------------------------------------------------------------- 1 | //! A PERSONAL FORK OF 'textplots' VERSION 0.8.0 WITH 2 | //! SUPPORT FOR ANSI COLORS IN NUSHELL. 3 | //! 4 | //! Terminal plotting library for using in CLI applications. 5 | //! Should work well in any unicode terminal with monospaced font. 6 | //! 7 | //! It is inspired by [TextPlots.jl](https://github.com/sunetos/TextPlots.jl) which is inspired by [Drawille](https://github.com/asciimoo/drawille). 8 | //! 9 | //! Currently it features only drawing line plots on Braille canvas, but could be extended 10 | //! to support other canvas and chart types just like [UnicodePlots.jl](https://github.com/Evizero/UnicodePlots.jl) 11 | //! or any other cool terminal plotting library. 12 | //! 13 | //! Contributions are very much welcome! 14 | //! 15 | //! # Usage 16 | //! ```toml 17 | //! [dependencies] 18 | //! textplots = "0.6" 19 | //! ``` 20 | //! 21 | //! ```rust 22 | //! use textplots::{Chart, Plot, Shape}; 23 | //! 24 | //! println!("y = sin(x) / x"); 25 | //! 26 | //! Chart::default() 27 | //! .lineplot(&Shape::Continuous(Box::new(|x| x.sin() / x))) 28 | //! .display(); 29 | //! ``` 30 | //! 31 | //! It will display something like this: 32 | //! 33 | //! 34 | //! 35 | //! Default viewport size is 120 x 60 points, with X values ranging from -10 to 10. 36 | //! You can override the defaults calling `new`. 37 | //! 38 | //! ```rust 39 | //! use textplots::{Chart, Plot, Shape}; 40 | //! 41 | //! println!("y = cos(x), y = sin(x) / 2"); 42 | //! 43 | //! Chart::new(180, 60, -5.0, 5.0) 44 | //! .lineplot(&Shape::Continuous(Box::new(|x| x.cos()))) 45 | //! .lineplot(&Shape::Continuous(Box::new(|x| x.sin() / 2.0))) 46 | //! .display(); 47 | //! ``` 48 | //! 49 | //! 50 | //! 51 | //! You could also plot series of points. See [Shape](enum.Shape.html) and [examples](https://github.com/loony-bean/textplots-rs/tree/master/examples) for more details. 52 | //! 53 | //! 54 | 55 | pub mod scale; 56 | pub mod utils; 57 | 58 | use super::drawille::Canvas as BrailleCanvas; 59 | use super::drawille::PixelColor; 60 | use scale::Scale; 61 | use std::cmp; 62 | use std::default::Default; 63 | use std::f32; 64 | 65 | /// How the chart will do the ranging on axes 66 | #[derive(PartialEq)] 67 | enum ChartRangeMethod { 68 | /// Automatically ranges based on input data 69 | AutoRange, 70 | /// Has a fixed range between the given min & max 71 | FixedRange, 72 | } 73 | 74 | /// Controls the drawing. 75 | pub struct Chart<'a> { 76 | /// Canvas width in points. 77 | width: u32, 78 | /// Canvas height in points. 79 | height: u32, 80 | /// X-axis start value. 81 | xmin: f32, 82 | /// X-axis end value. 83 | xmax: f32, 84 | /// Y-axis start value (potentially calculated automatically). 85 | ymin: f32, 86 | /// Y-axis end value (potentially calculated automatically). 87 | ymax: f32, 88 | /// The type of y axis ranging we'll do 89 | y_ranging: ChartRangeMethod, 90 | /// Collection of shapes to be presented on the canvas. 91 | shapes: Vec<(&'a Shape<'a>, Option)>, 92 | /// Underlying canvas object. 93 | canvas: BrailleCanvas, 94 | } 95 | 96 | /// Specifies different kinds of plotted data. 97 | pub enum Shape<'a> { 98 | /// Real value function. 99 | Continuous(Box f32 + 'a>), 100 | /// Points of a scatter plot. 101 | Points(&'a [(f32, f32)]), 102 | /// Points connected with lines. 103 | Lines(&'a [(f32, f32)]), 104 | /// Points connected in step fashion. 105 | Steps(&'a [(f32, f32)]), 106 | /// Points represented with bars. 107 | Bars(&'a [(f32, f32)]), 108 | } 109 | 110 | /// Provides an interface for drawing plots. 111 | pub trait Plot<'a> { 112 | /// Draws a [line chart](https://en.wikipedia.org/wiki/Line_chart) of points connected by straight line segments. 113 | fn lineplot(&'a mut self, shape: &'a Shape) -> &'a mut Chart; 114 | } 115 | 116 | /// Provides an interface for drawing colored plots. 117 | pub trait ColorPlot<'a> { 118 | /// Draws a [line chart](https://en.wikipedia.org/wiki/Line_chart) of points connected by straight line segments using the specified color 119 | fn linecolorplot(&'a mut self, shape: &'a Shape, color: PixelColor) -> &'a mut Chart; 120 | } 121 | 122 | impl<'a> Default for Chart<'a> { 123 | fn default() -> Self { 124 | Self::new(120, 60, -10.0, 10.0) 125 | } 126 | } 127 | 128 | impl<'a> Chart<'a> { 129 | /// Creates a new `Chart` object. 130 | /// 131 | /// # Panics 132 | /// 133 | /// Panics if `width` or `height` is less than 32. 134 | pub fn new(width: u32, height: u32, xmin: f32, xmax: f32) -> Self { 135 | if width < 32 { 136 | panic!("width should be more then 32, {} is provided", width); 137 | } 138 | 139 | if height < 32 { 140 | panic!("height should be more then 32, {} is provided", height); 141 | } 142 | 143 | Self { 144 | xmin, 145 | xmax, 146 | ymin: f32::INFINITY, 147 | ymax: f32::NEG_INFINITY, 148 | y_ranging: ChartRangeMethod::AutoRange, 149 | width, 150 | height, 151 | shapes: Vec::new(), 152 | canvas: BrailleCanvas::new(width, height), 153 | } 154 | } 155 | 156 | /// Creates a new `Chart` object with fixed y axis range. 157 | /// 158 | /// # Panics 159 | /// 160 | /// Panics if `width` or `height` is less than 32. 161 | pub fn new_with_y_range( 162 | width: u32, 163 | height: u32, 164 | xmin: f32, 165 | xmax: f32, 166 | ymin: f32, 167 | ymax: f32, 168 | ) -> Self { 169 | if width < 32 { 170 | panic!("width should be more then 32, {} is provided", width); 171 | } 172 | 173 | if height < 32 { 174 | panic!("height should be more then 32, {} is provided", height); 175 | } 176 | 177 | Self { 178 | xmin, 179 | xmax, 180 | ymin, 181 | ymax, 182 | y_ranging: ChartRangeMethod::FixedRange, 183 | width, 184 | height, 185 | shapes: Vec::new(), 186 | canvas: BrailleCanvas::new(width, height), 187 | } 188 | } 189 | 190 | /// Displays bounding rect. 191 | fn borders(&mut self) { 192 | let w = self.width; 193 | let h = self.height; 194 | 195 | self.vline(0); 196 | self.vline(w); 197 | self.hline(0); 198 | self.hline(h); 199 | } 200 | 201 | /// Draws vertical line. 202 | fn vline(&mut self, i: u32) { 203 | if i <= self.width { 204 | for j in 0..=self.height { 205 | if j % 3 == 0 { 206 | self.canvas.set(i, j); 207 | } 208 | } 209 | } 210 | } 211 | 212 | /// Draws horizontal line. 213 | fn hline(&mut self, j: u32) { 214 | if j <= self.height { 215 | for i in 0..=self.width { 216 | if i % 3 == 0 { 217 | self.canvas.set(i, self.height - j); 218 | } 219 | } 220 | } 221 | } 222 | 223 | #[allow(clippy::inherent_to_string)] 224 | pub fn to_string(&mut self) -> String { 225 | self.figures(); 226 | self.axis(); 227 | 228 | let mut frame = self.canvas.frame(); 229 | if let Some(idx) = frame.find('\n') { 230 | frame.insert_str(idx, &format!(" {0:.1}", self.ymax)); 231 | frame.push_str(&format!( 232 | " {0:.1}\n{1: = 0.0 { 259 | self.vline(x_scale.linear(0.0) as u32); 260 | } 261 | if self.ymin <= 0.0 && self.ymax >= 0.0 { 262 | self.hline(y_scale.linear(0.0) as u32); 263 | } 264 | } 265 | 266 | // Show figures. 267 | pub fn figures(&mut self) { 268 | for (shape, color) in &self.shapes { 269 | let x_scale = Scale::new(self.xmin..self.xmax, 0.0..self.width as f32); 270 | let y_scale = Scale::new(self.ymin..self.ymax, 0.0..self.height as f32); 271 | 272 | // translate (x, y) points into screen coordinates 273 | let points: Vec<_> = match shape { 274 | Shape::Continuous(f) => (0..self.width) 275 | .filter_map(|i| { 276 | let x = x_scale.inv_linear(i as f32); 277 | let y = f(x); 278 | if y.is_normal() { 279 | let j = y_scale.linear(y).round(); 280 | Some((i, self.height - j as u32)) 281 | } else { 282 | None 283 | } 284 | }) 285 | .collect(), 286 | Shape::Points(dt) | Shape::Lines(dt) | Shape::Steps(dt) | Shape::Bars(dt) => dt 287 | .iter() 288 | .filter_map(|(x, y)| { 289 | let i = x_scale.linear(*x).round() as u32; 290 | let j = y_scale.linear(*y).round() as u32; 291 | if i <= self.width && j <= self.height { 292 | Some((i, self.height - j)) 293 | } else { 294 | None 295 | } 296 | }) 297 | .collect(), 298 | }; 299 | 300 | // display segments 301 | match shape { 302 | Shape::Continuous(_) | Shape::Lines(_) => { 303 | for pair in points.windows(2) { 304 | let (x1, y1) = pair[0]; 305 | let (x2, y2) = pair[1]; 306 | if let Some(color) = color { 307 | self.canvas.line_colored(x1, y1, x2, y2, *color); 308 | } else { 309 | self.canvas.line(x1, y1, x2, y2); 310 | } 311 | } 312 | } 313 | Shape::Points(_) => { 314 | for (x, y) in points { 315 | self.canvas.set(x, y); 316 | } 317 | } 318 | Shape::Steps(_) => { 319 | for pair in points.windows(2) { 320 | let (x1, y1) = pair[0]; 321 | let (x2, y2) = pair[1]; 322 | 323 | if let Some(color) = color { 324 | self.canvas.line_colored(x1, y2, x2, y2, *color); 325 | self.canvas.line_colored(x1, y1, x1, y2, *color); 326 | } else { 327 | self.canvas.line(x1, y2, x2, y2); 328 | self.canvas.line(x1, y1, x1, y2); 329 | } 330 | } 331 | } 332 | Shape::Bars(_) => { 333 | for pair in points.windows(2) { 334 | let (x1, y1) = pair[0]; 335 | let (x2, y2) = pair[1]; 336 | 337 | if let Some(color) = color { 338 | self.canvas.line_colored(x1, y2, x2, y2, *color); 339 | self.canvas.line_colored(x1, y1, x1, y2, *color); 340 | self.canvas.line_colored(x1, self.height, x1, y1, *color); 341 | self.canvas.line_colored(x2, self.height, x2, y2, *color); 342 | } else { 343 | self.canvas.line(x1, y2, x2, y2); 344 | self.canvas.line(x1, y1, x1, y2); 345 | self.canvas.line(x1, self.height, x1, y1); 346 | self.canvas.line(x2, self.height, x2, y2); 347 | } 348 | } 349 | } 350 | } 351 | } 352 | } 353 | 354 | /// Return the frame. 355 | pub fn frame(&self) -> String { 356 | self.canvas.frame() 357 | } 358 | 359 | fn rescale(&mut self, shape: &Shape) { 360 | // rescale ymin and ymax 361 | let x_scale = Scale::new(self.xmin..self.xmax, 0.0..self.width as f32); 362 | 363 | let ys: Vec<_> = match shape { 364 | Shape::Continuous(f) => (0..self.width) 365 | .filter_map(|i| { 366 | let x = x_scale.inv_linear(i as f32); 367 | let y = f(x); 368 | if y.is_normal() { 369 | Some(y) 370 | } else { 371 | None 372 | } 373 | }) 374 | .collect(), 375 | Shape::Points(dt) | Shape::Lines(dt) | Shape::Steps(dt) | Shape::Bars(dt) => dt 376 | .iter() 377 | .filter_map(|(x, y)| { 378 | if *x >= self.xmin && *x <= self.xmax { 379 | Some(*y) 380 | } else { 381 | None 382 | } 383 | }) 384 | .collect(), 385 | }; 386 | 387 | let ymax = *ys 388 | .iter() 389 | .max_by(|x, y| x.partial_cmp(y).unwrap_or(cmp::Ordering::Equal)) 390 | .unwrap_or(&0.0); 391 | let ymin = *ys 392 | .iter() 393 | .min_by(|x, y| x.partial_cmp(y).unwrap_or(cmp::Ordering::Equal)) 394 | .unwrap_or(&0.0); 395 | 396 | self.ymin = f32::min(self.ymin, ymin); 397 | self.ymax = f32::max(self.ymax, ymax); 398 | } 399 | } 400 | 401 | impl<'a> ColorPlot<'a> for Chart<'a> { 402 | fn linecolorplot(&'a mut self, shape: &'a Shape, color: PixelColor) -> &'a mut Chart { 403 | self.shapes.push((shape, Some(color))); 404 | if self.y_ranging == ChartRangeMethod::AutoRange { 405 | self.rescale(shape); 406 | } 407 | self 408 | } 409 | } 410 | 411 | impl<'a> Plot<'a> for Chart<'a> { 412 | fn lineplot(&'a mut self, shape: &'a Shape) -> &'a mut Chart { 413 | self.shapes.push((shape, None)); 414 | if self.y_ranging == ChartRangeMethod::AutoRange { 415 | self.rescale(shape); 416 | } 417 | self 418 | } 419 | } 420 | -------------------------------------------------------------------------------- /src/color_plot/textplots/scale.rs: -------------------------------------------------------------------------------- 1 | //! Transformations between domain and range. 2 | 3 | use std::ops::Range; 4 | 5 | /// Holds mapping between domain and range of the function. 6 | pub struct Scale { 7 | domain: Range, 8 | range: Range, 9 | } 10 | 11 | impl Scale { 12 | /// Translates value from domain to range scale. 13 | /// ``` 14 | /// # use textplots::scale::Scale; 15 | /// assert_eq!(-0.8, Scale::new(0_f32..10_f32, -1_f32..1_f32).linear(1.0)); 16 | /// ``` 17 | pub fn linear(&self, x: f32) -> f32 { 18 | let p = (x - self.domain.start) / (self.domain.end - self.domain.start); 19 | let r = self.range.start + p * (self.range.end - self.range.start); 20 | r.clamp(self.range.start, self.range.end) 21 | } 22 | 23 | /// Translates value from range to domain scale. 24 | /// ``` 25 | /// # use textplots::scale::Scale; 26 | /// assert_eq!(5.5, Scale::new(0_f32..10_f32, -1_f32..1_f32).inv_linear(0.1)); 27 | /// ``` 28 | pub fn inv_linear(&self, i: f32) -> f32 { 29 | let p = (i - self.range.start) / (self.range.end - self.range.start); 30 | let d = self.domain.start + p * (self.domain.end - self.domain.start); 31 | d.clamp(self.domain.start, self.domain.end) 32 | } 33 | 34 | pub fn new(domain: Range, range: Range) -> Self { 35 | Scale { domain, range } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/color_plot/textplots/utils.rs: -------------------------------------------------------------------------------- 1 | //! Helpers for passing the data into plots. 2 | //! 3 | //! Merely a bunch of functions hanging around while the library API is taking shape. 4 | 5 | /// Transforms points into frequency distribution (for using in histograms). 6 | /// Values outside of [`min`, `max`] interval are ignored, and everything that 7 | /// falls into the specified interval is grouped into `bins` number of buckets of equal width. 8 | /// 9 | /// ``` 10 | /// # use textplots::utils::histogram; 11 | /// assert_eq!(vec![(0.0, 1.0), (5.0, 1.0)], histogram( &[ (0.0, 0.0), (9.0, 9.0), (10.0, 10.0) ], 0.0, 10.0, 2 )); 12 | /// ``` 13 | pub fn histogram(data: &[(f32, f32)], min: f32, max: f32, bins: usize) -> Vec<(f32, f32)> { 14 | let mut output = vec![0; bins]; 15 | 16 | let step = (max - min) / bins as f32; 17 | 18 | for &(_x, y) in data.iter() { 19 | if y < min || y > max { 20 | continue; 21 | } 22 | 23 | let bucket_id = ((y - min) / step) as usize; 24 | if bucket_id < output.len() { 25 | output[bucket_id] += 1; 26 | } 27 | } 28 | 29 | output 30 | .into_iter() 31 | .enumerate() 32 | .map(|(x, y)| ((min + (x as f32) * step), y as f32)) 33 | .collect() 34 | } 35 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A small crate to plot an ASCII 2 | //! representation of a List data type from nushell 3 | //! 4 | //! Three commands are supplied. 5 | //! - `plot` plots a 1-dimensional numeric list/nested list 6 | //! - `hist` plots a 1-dimensional numeric list/nested list 7 | //! - `xyplot` plots a 2-dimensional numeric list (nested list with length == 2) 8 | 9 | use nu_plugin::{EvaluatedCall, Plugin, SimplePluginCommand}; 10 | use nu_protocol::{Category, LabeledError, Signature, SyntaxShape, Type, Value}; 11 | pub mod color_plot; 12 | 13 | use color_plot::drawille::PixelColor; 14 | use color_plot::textplots::{utils::histogram, Chart, ColorPlot, Plot, Shape}; 15 | use owo_colors::OwoColorize; 16 | 17 | 18 | /// So the chart is not hard up against the left of the terminal. 19 | const TAB: &str = " "; 20 | 21 | /// Colors, five of them. 22 | const COLORS: &[PixelColor] = &[ 23 | PixelColor::BrightWhite, 24 | PixelColor::BrightRed, 25 | PixelColor::BrightBlue, 26 | PixelColor::BrightYellow, 27 | PixelColor::Cyan, 28 | ]; 29 | 30 | /// The command line options. 31 | /// 32 | /// These apply to `plot`, `hist`, and `xyplot`. 33 | struct CliOpts { 34 | /// The maximum y height of the plot. 35 | height_op: Option, 36 | /// The maximum x width of the plot. 37 | width_op: Option, 38 | /// Add a legend to the plot. 39 | legend: bool, 40 | /// Render a step plot, instead of a line plot. 41 | steps: bool, 42 | /// Render a bar plot, instead of a line plot. 43 | bars: bool, 44 | /// Render single points, instead of line plot. 45 | points: bool, 46 | /// Add a title to the plot. 47 | title: Option, 48 | /// Number of bins in the histogram 49 | bins: Option, 50 | } 51 | 52 | /// Parse the command line options. 53 | fn parse_cli_opts(call: &EvaluatedCall) -> Result { 54 | // scale the width and height to the size of the terminal unless specified on the cli 55 | let height_op: Option = call.get_flag("height").map(|e| e.map(|f: i64| f as u32))?; 56 | let width_op: Option = call.get_flag("width").map(|e| e.map(|f: i64| f as u32))?; 57 | 58 | let mut height: Option; 59 | let mut width: Option; 60 | 61 | if let Some((w, h)) = term_size::dimensions() { 62 | // don't know why I need to scale this, but I do - I hope it works 63 | // as intended for other terminals. 64 | height = Some(height_op.unwrap_or((h as f32 * 1.7) as u32)); 65 | width = Some(width_op.unwrap_or((w as f32 * 1.7) as u32)); 66 | 67 | // textplot panics if either of these are below 32 units. 68 | if height.unwrap() < 32 { 69 | height = Some(32); 70 | } 71 | if width.unwrap() < 32 { 72 | width = Some(32); 73 | } 74 | } else { 75 | // we couldnt detect terminal size for some reason 76 | height = height_op; 77 | width = width_op; 78 | } 79 | 80 | let legend = call.has_flag("legend")?; 81 | let steps = call.has_flag("steps")?; 82 | let bars = call.has_flag("bars")?; 83 | let points = call.has_flag("points")?; 84 | let bins: Option = call.get_flag("bins").map(|e| e.map(|f: i64| f as u32))?; 85 | let title: Option = call.get_flag("title")?; 86 | 87 | Ok(CliOpts { 88 | height_op: height, 89 | width_op: width, 90 | legend, 91 | steps, 92 | bars, 93 | points, 94 | bins, 95 | title, 96 | }) 97 | } 98 | 99 | /// The shape of the plot. Default is `Shape::Lines`, 100 | /// but also includes `Shape::Bars` and `Shape::Steps`. 101 | fn chart_shape<'a>( 102 | steps: bool, 103 | bars: bool, 104 | points: bool, 105 | call: &EvaluatedCall, 106 | v: &'a [(f32, f32)], 107 | ) -> Result, LabeledError> { 108 | match (steps, bars, points) { 109 | (true, false, false) => Ok(Shape::Steps(v)), 110 | (false, true, false) => Ok(Shape::Bars(v)), 111 | (false, false, true) => Ok(Shape::Points(v)), 112 | (false, false, false) => Ok(Shape::Lines(v)), 113 | _ => Err(LabeledError::new("Shape must be either steps or bars or points, not more than one. Check your flags!").with_label("Chart shape error", call.head)), 114 | } 115 | } 116 | 117 | /// Check the chart shape is Okay. If not returns an error. 118 | fn check_chart_shape<'a>( 119 | steps: bool, 120 | bars: bool, 121 | points: bool, 122 | call: &EvaluatedCall, 123 | ) -> Result<(), LabeledError> { 124 | match (steps, bars, points) { 125 | (true, false, false) => Ok(()), 126 | (false, true, false) => Ok(()), 127 | (false, false, true) => Ok(()), 128 | (false, false, false) => Ok(()), 129 | _ => Err(LabeledError::new("Shape must be either steps or bars or points, not more than one. Check your flags!").with_label("Chart shape error", call.head)), 130 | } 131 | } 132 | 133 | /// Return the minimum and the maximum of a slice of `f32`. 134 | fn min_max(series: &[f32]) -> (f32, f32) { 135 | let min = series 136 | .iter() 137 | .fold(std::f32::MAX, |accu, &x| if x < accu { x } else { accu }); 138 | let max = series 139 | .iter() 140 | .fold(std::f32::MIN, |accu, &x| if x > accu { x } else { accu }); 141 | (min, max) 142 | } 143 | 144 | /// Get the type of a `Value`, and its length if it's a list. 145 | fn get_value_type_or_list_length(val: &Value) -> (Type, Option) { 146 | let typ = val.get_type(); 147 | let len = match val.as_list() { 148 | Ok(l) => Some(l.len()), 149 | Err(_) => None, 150 | }; 151 | 152 | (typ, len) 153 | } 154 | 155 | /// Check a list of values for equality of type, 156 | /// length. Return the type. 157 | fn check_equality_of_list( 158 | l: &[Value], 159 | call: &EvaluatedCall, 160 | ) -> Result<(Type, Option), LabeledError> { 161 | let mut types = vec![]; 162 | let mut len_ops = vec![]; 163 | 164 | for val in l { 165 | let (typ, len_op) = get_value_type_or_list_length(val); 166 | types.push(typ); 167 | len_ops.push(len_op); 168 | } 169 | 170 | // check types are all the same 171 | // e.g. Int/Float/List 172 | let first_type = &types[0]; 173 | let check_type_pass = types.iter().all(|e| e == first_type); 174 | 175 | if !check_type_pass { 176 | return Err(LabeledError::new("Can't plot a list of multiple types.").with_label("Type differences.", call.head) ); 177 | } 178 | 179 | let first_len_op = &len_ops[0]; 180 | let check_len_pass = len_ops.iter().all(|e| e == first_len_op); 181 | 182 | if !check_len_pass { 183 | return Err(LabeledError::new("Can't plot a list of differing length lists.").with_label("List length differences.", call.head)); 184 | } 185 | 186 | if let Some(_len) = first_len_op { 187 | // *should* always index + unwrap without panicking... 188 | let inner_type = l[0].as_list()?[0].get_type(); 189 | match inner_type { 190 | Type::Float | Type::Int => (), 191 | _ => { 192 | return Err(LabeledError::new("Nested list elements not float or int.").with_label("Incorrect type.", call.head)); 193 | } 194 | } 195 | } 196 | 197 | Ok((first_type.clone(), *first_len_op)) 198 | } 199 | 200 | pub struct PluginPlot {} 201 | 202 | struct CommandPlot; 203 | struct CommandHist; 204 | struct CommandXyplot; 205 | 206 | impl Plugin for PluginPlot { 207 | fn version(&self) -> String { 208 | env!("CARGO_PKG_VERSION").into() 209 | } 210 | fn commands(&self) -> Vec>> { 211 | vec![ 212 | Box::new(CommandPlot), Box::new(CommandHist), Box::new(CommandXyplot) 213 | ] 214 | } 215 | } 216 | 217 | trait Plotter { 218 | fn plot( 219 | &self, 220 | call: &EvaluatedCall, 221 | input: &Value, 222 | ) -> Result; 223 | fn plot_nested( 224 | &self, 225 | call: &EvaluatedCall, 226 | input: &Value, 227 | ) -> Result; 228 | } 229 | 230 | impl Plotter for CommandPlot { 231 | fn plot( 232 | &self, 233 | call: &EvaluatedCall, 234 | input: &Value, 235 | ) -> Result { 236 | let CliOpts { 237 | height_op, 238 | width_op, 239 | legend, 240 | steps, 241 | bars, 242 | points, 243 | title, 244 | bins: _, 245 | } = parse_cli_opts(call)?; 246 | 247 | let max_x = width_op.unwrap_or(200); 248 | let max_y = height_op.unwrap_or(50); 249 | 250 | let values = input.as_list()?; 251 | 252 | let v: Result, LabeledError> = values 253 | .iter() 254 | .enumerate() 255 | .map(|(i, e)| match e { 256 | Value::Int { .. } => Ok((i as f32, e.as_int()? as f32)), 257 | Value::Float { .. } => Ok((i as f32, e.as_float()? as f32)), 258 | e => Err(LabeledError::new(format!("Got {}, need integer or float.", e.get_type())).with_label("Incorrect type supplied", call.head)), 259 | }) 260 | .collect(); 261 | 262 | let min_max_x = { 263 | let x: Vec = v.clone().unwrap().iter().map(|e| e.0).collect(); 264 | min_max(&x) 265 | }; 266 | 267 | let chart_data = v; 268 | 269 | let mut chart = Chart::new(max_x, max_y, min_max_x.0, min_max_x.1) 270 | .lineplot(&chart_shape(steps, bars, points, call, &chart_data?)?) 271 | .to_string(); 272 | 273 | if let Some(t) = title { 274 | chart = TAB.to_owned() + &t + "\n" + &chart; 275 | } 276 | chart = TAB.to_owned() + &chart.replace('\n', &format!("\n{}", TAB)); 277 | 278 | if legend { 279 | chart += &format!("Line 1: {}", "---".white()); 280 | } 281 | 282 | Ok(Value::string(chart, call.head)) 283 | } 284 | 285 | fn plot_nested<'a>( 286 | &self, 287 | call: &EvaluatedCall, 288 | input: &Value, 289 | ) -> Result { 290 | let CliOpts { 291 | height_op, 292 | width_op, 293 | legend, 294 | steps, 295 | bars, 296 | points, 297 | title, 298 | bins: _, 299 | } = parse_cli_opts(call)?; 300 | 301 | let max_x = width_op.unwrap_or(200); 302 | let max_y = height_op.unwrap_or(50); 303 | 304 | let values = input.as_list()?; 305 | if values.len() > 5 { 306 | return Err(LabeledError::new("Nested list can't contain more than 5 inner lists.").with_label("Nested list error.", call.head)); 307 | } 308 | 309 | let mut data = vec![]; 310 | 311 | for val in values { 312 | let list = val.as_list()?; 313 | 314 | let v: Result, LabeledError> = list 315 | .iter() 316 | .enumerate() 317 | .map(|(i, e)| match e { 318 | Value::Int { .. } => Ok((i as f32, e.as_int()? as f32)), 319 | Value::Float { .. } => Ok((i as f32, e.as_float()? as f32)), 320 | e => Err(LabeledError::new(format!("Got {}, need integer or float.", e.get_type())).with_label("Incorrect type supplied.", call.head)), 321 | }) 322 | .collect(); 323 | 324 | let min_max_x = { 325 | let x: Vec = v.clone()?.iter().map(|e| e.0).collect(); 326 | let y: Option> = None; 327 | (min_max(&x), y) 328 | }; 329 | 330 | data.push((min_max_x, v?)); 331 | } 332 | 333 | let min_all: Vec = data.iter().map(|((e, _), _)| e.0).collect(); 334 | let max_all: Vec = data.iter().map(|((e, _), _)| e.1).collect(); 335 | 336 | let min = min_all.iter().fold(f32::INFINITY, |a, &b| a.min(b)); 337 | let max = *max_all.iter().max_by(|a, b| a.total_cmp(b)).unwrap(); 338 | 339 | // copying data structure again here but wanted to be explicit. 340 | let chart_data: Vec> = data.iter().map(|(_, e)| e.clone()).collect(); 341 | 342 | // let shapes = chart_data.into_iter().map(|data| chart_shape(steps, bars, points, call, &data)); 343 | check_chart_shape(steps, bars, points, call)?; 344 | let shapes: Vec = (&chart_data) 345 | .iter() 346 | .map(|data| chart_shape(steps, bars, points, call, data).unwrap()) 347 | .collect(); 348 | let charts = (&shapes).iter() 349 | .enumerate() 350 | .fold(&mut Chart::new(max_x, max_y, min, max), |chart, (i, shape)| { 351 | chart.linecolorplot(shape, COLORS[i]) 352 | }) 353 | .to_string(); 354 | 355 | let mut final_chart = TAB.to_owned() + &charts.replace('\n', &format!("\n{}", TAB)); 356 | 357 | if let Some(t) = title { 358 | final_chart = TAB.to_owned() + &t + "\n" + &final_chart; 359 | } 360 | 361 | if legend { 362 | for (l, (_, _)) in data.iter().enumerate() { 363 | let col: PixelColor = COLORS[l]; 364 | final_chart += &format!("Line {}: {} ", l + 1, "---".color(col)); 365 | } 366 | } 367 | 368 | Ok(Value::string(final_chart, call.head)) 369 | } 370 | } 371 | 372 | 373 | impl SimplePluginCommand for CommandPlot { 374 | type Plugin = PluginPlot; 375 | 376 | fn name(&self) -> &str { 377 | "plot" 378 | } 379 | 380 | fn signature(&self) -> nu_protocol::Signature { 381 | Signature::build("plot") 382 | .description("Render an ASCII plot from a list of values.") 383 | .named( 384 | "width", 385 | SyntaxShape::Number, 386 | "The maximum width of the plot.", 387 | None, 388 | ) 389 | .named( 390 | "height", 391 | SyntaxShape::Number, 392 | "The maximum height of the plot.", 393 | None, 394 | ) 395 | .named( 396 | "title", 397 | SyntaxShape::String, 398 | "Provide a title to the plot.", 399 | Some('t'), 400 | ) 401 | .switch("legend", "Plot a tiny, maybe useful legend.", Some('l')) 402 | .switch("bars", "Change lines to bars.", Some('b')) 403 | .switch("steps", "Change lines to steps.", Some('s')) 404 | .switch("points", "Change lines to points.", Some('p')) 405 | .category(Category::Experimental) 406 | } 407 | 408 | fn description(&self) -> &str { 409 | "Render an ASCII plot from a list of values." 410 | } 411 | 412 | fn run( 413 | &self, 414 | _plugin: &Self::Plugin, 415 | _engine: &nu_plugin::EngineInterface, 416 | call: &EvaluatedCall, 417 | input: &Value, 418 | ) -> Result { 419 | match input.as_list() { 420 | Ok(list) => { 421 | if list.is_empty() { 422 | return Err(LabeledError::new("Can't plot a zero element list.").with_label( "No elements in the list.", call.head)); 423 | } 424 | let (value_type, list_len_op) = check_equality_of_list(list, call)?; 425 | 426 | // if in fact we have a nested list 427 | if let Some(_len) = list_len_op { 428 | // we haven't implemented this yet 429 | self.plot_nested(call, input) 430 | } else { 431 | // we have a normal plot, single list of numbers 432 | match value_type { 433 | Type::Float | Type::Int => self.plot(call, input), 434 | e => Err(LabeledError::new(format!("List type is {}, but should be float or int.", e)).with_label("Incorrect List type.", call.head)), 435 | } 436 | } 437 | }, 438 | Err(e) => Err(LabeledError::new(format!("Input type should be a list: {}.", e)).with_label( "Incorrect input type.", call.head)), 439 | } 440 | } 441 | } 442 | 443 | impl Plotter for CommandHist { 444 | fn plot( 445 | &self, 446 | call: &EvaluatedCall, 447 | input: &Value, 448 | ) -> Result { 449 | let CliOpts { 450 | height_op, 451 | width_op, 452 | legend, 453 | steps, 454 | bars, 455 | points, 456 | title, 457 | bins, 458 | } = parse_cli_opts(call)?; 459 | 460 | let max_x = width_op.unwrap_or(200); 461 | let max_y = height_op.unwrap_or(50); 462 | 463 | let values = input.as_list()?; 464 | 465 | let v: Result, LabeledError> = values 466 | .iter() 467 | .enumerate() 468 | .map(|(i, e)| match e { 469 | Value::Int { .. } => Ok((i as f32, e.as_int()? as f32)), 470 | Value::Float { .. } => Ok((i as f32, e.as_float()? as f32)), 471 | e => Err(LabeledError::new(format!("Got {}, need integer or float.", e.get_type())).with_label("Incorrect type supplied", call.head)), 472 | }) 473 | .collect(); 474 | 475 | let (min, max) = min_max( 476 | &v.clone() 477 | .unwrap() 478 | .iter() 479 | .map(|(_, e)| *e) 480 | .collect::>(), 481 | ); 482 | let chart_data: Vec<(f32, f32)> = histogram( 483 | &v.unwrap(), 484 | min, 485 | max, 486 | bins.map(|e| e as usize).unwrap_or(20), 487 | ); 488 | let min_max_x = (min, max); 489 | 490 | 491 | let mut chart = Chart::new(max_x, max_y, min_max_x.0, min_max_x.1) 492 | .lineplot(&chart_shape(steps, bars, points, call, &chart_data)?) 493 | .to_string(); 494 | 495 | if let Some(t) = title { 496 | chart = TAB.to_owned() + &t + "\n" + &chart; 497 | } 498 | chart = TAB.to_owned() + &chart.replace('\n', &format!("\n{}", TAB)); 499 | 500 | if legend { 501 | chart += &format!("Line 1: {}", "---".white()); 502 | } 503 | 504 | Ok(Value::string(chart, call.head)) 505 | } 506 | 507 | fn plot_nested( 508 | &self, 509 | call: &EvaluatedCall, 510 | input: &Value, 511 | ) -> Result { 512 | let CliOpts { 513 | height_op, 514 | width_op, 515 | legend, 516 | steps, 517 | bars, 518 | points, 519 | title, 520 | bins, 521 | } = parse_cli_opts(call)?; 522 | 523 | let max_x = width_op.unwrap_or(200); 524 | let max_y = height_op.unwrap_or(50); 525 | 526 | let values = input.as_list()?; 527 | if values.len() > 5 { 528 | return Err(LabeledError::new("Nested list can't contain more than 5 inner lists.").with_label("Nested list error.", call.head)); 529 | } 530 | 531 | let mut data = vec![]; 532 | 533 | for val in values { 534 | let list = val.as_list()?; 535 | 536 | let v: Result, LabeledError> = list 537 | .iter() 538 | .enumerate() 539 | .map(|(i, e)| match e { 540 | Value::Int { .. } => Ok((i as f32, e.as_int()? as f32)), 541 | Value::Float { .. } => Ok((i as f32, e.as_float()? as f32)), 542 | e => Err(LabeledError::new(format!("Got {}, need integer or float.", e.get_type())).with_label("Incorrect type supplied.", call.head)), 543 | }) 544 | .collect(); 545 | 546 | let x: Vec = v.clone()?.iter().map(|e| e.0).collect(); 547 | let y: Option> = None; 548 | let min_max_x = (min_max(&x), y); 549 | 550 | data.push((min_max_x, v?)); 551 | } 552 | 553 | // copying data structure again here but wanted to be explicit. 554 | let mut mins = 0.0; 555 | let mut maxs = 0.0; 556 | 557 | for (i, (_, el)) in data.iter().enumerate() { 558 | let (min, max) = min_max(&el.iter().map(|(_, e)| *e).collect::>()); 559 | if i == 0 { 560 | maxs = max; 561 | mins = min; 562 | } else { 563 | if max > maxs { 564 | maxs = max; 565 | } 566 | if min < mins { 567 | mins = min; 568 | } 569 | } 570 | } 571 | let (min, max) = (mins, maxs); 572 | 573 | let hist_data: Vec> = data 574 | .iter() 575 | .map(|(_, e)| histogram(e, mins, maxs, bins.map(|e| e as usize).unwrap_or(20))) 576 | .collect(); 577 | 578 | check_chart_shape(steps, bars, points, call)?; 579 | let shapes: Vec = (&hist_data) 580 | .iter() 581 | .map(|data| chart_shape(steps, bars, points, call, data).unwrap()) 582 | .collect(); 583 | let charts = (&shapes).iter() 584 | .enumerate() 585 | .fold(&mut Chart::new(max_x, max_y, min, max), |chart, (i, shape)| { 586 | chart.linecolorplot(shape, COLORS[i]) 587 | }) 588 | .to_string(); 589 | 590 | let mut final_chart = TAB.to_owned() + &charts.replace('\n', &format!("\n{}", TAB)); 591 | 592 | if let Some(t) = title { 593 | final_chart = TAB.to_owned() + &t + "\n" + &final_chart; 594 | } 595 | 596 | if legend { 597 | for (l, (_, _)) in data.iter().enumerate() { 598 | let col: PixelColor = COLORS[l]; 599 | final_chart += &format!("Line {}: {} ", l + 1, "---".color(col)); 600 | } 601 | } 602 | 603 | Ok(Value::string(final_chart, call.head)) 604 | } 605 | } 606 | 607 | impl SimplePluginCommand for CommandHist { 608 | type Plugin = PluginPlot; 609 | 610 | fn name(&self) -> &str { 611 | "hist" 612 | } 613 | 614 | fn signature(&self) -> nu_protocol::Signature { 615 | Signature::build("hist") 616 | .description("Render an ASCII histogram from a list of values.") 617 | .named( 618 | "width", 619 | SyntaxShape::Number, 620 | "The maximum width of the plot.", 621 | None, 622 | ) 623 | .named( 624 | "height", 625 | SyntaxShape::Number, 626 | "The maximum height of the plot.", 627 | None, 628 | ) 629 | .named( 630 | "title", 631 | SyntaxShape::String, 632 | "Provide a title to the plot.", 633 | Some('t'), 634 | ) 635 | .named( 636 | "bins", 637 | SyntaxShape::Number, 638 | "The number of bins in the histogram, default is 20.", 639 | None, 640 | ) 641 | .switch("legend", "Plot a tiny, maybe useful legend.", Some('l')) 642 | .switch("bars", "Change lines to bars.", Some('b')) 643 | .switch("steps", "Change lines to steps.", Some('s')) 644 | .category(Category::Experimental) 645 | } 646 | 647 | fn description(&self) -> &str { 648 | "Render an ASCII histogram from a list of values." 649 | } 650 | 651 | fn run( 652 | &self, 653 | _plugin: &Self::Plugin, 654 | _engine: &nu_plugin::EngineInterface, 655 | call: &EvaluatedCall, 656 | input: &Value, 657 | ) -> Result { 658 | match input.as_list() { 659 | Ok(list) => { 660 | if list.is_empty() { 661 | return Err(LabeledError::new("Can't plot a zero element list.").with_label( "No elements in the list.", call.head)); 662 | } 663 | let (value_type, list_len_op) = check_equality_of_list(list, call)?; 664 | 665 | // if in fact we have a nested list 666 | if let Some(_len) = list_len_op { 667 | // we haven't implemented this yet 668 | self.plot_nested(call, input) 669 | } else { 670 | // we have a normal plot, single list of numbers 671 | match value_type { 672 | Type::Float | Type::Int => self.plot(call, input), 673 | e => Err(LabeledError::new(format!("List type is {}, but should be float or int.", e)).with_label("Incorrect List type.", call.head)), 674 | } 675 | } 676 | }, 677 | Err(e) => Err(LabeledError::new(format!("Input type should be a list: {}.", e)).with_label( "Incorrect input type.", call.head)), 678 | } 679 | } 680 | } 681 | 682 | impl Plotter for CommandXyplot { 683 | fn plot( 684 | &self, 685 | call: &EvaluatedCall, 686 | _input: &Value, 687 | ) -> Result { 688 | Err(LabeledError::new( "Doesn't make sense to plot an xyplot with a single list of values.").with_label("Plot type error.", call.head)) 689 | } 690 | 691 | fn plot_nested( 692 | &self, 693 | call: &EvaluatedCall, 694 | input: &Value, 695 | ) -> Result { 696 | let CliOpts { 697 | height_op, 698 | width_op, 699 | legend, 700 | steps, 701 | bars, 702 | points, 703 | title, 704 | bins: _, 705 | } = parse_cli_opts(call)?; 706 | 707 | let max_x = width_op.unwrap_or(200); 708 | let max_y = height_op.unwrap_or(50); 709 | 710 | let values = input.as_list()?; 711 | if values.len() > 5 { 712 | return Err(LabeledError::new("Nested list can't contain more than 5 inner lists.").with_label("Nested list error.", call.head)); 713 | } 714 | 715 | let mut data = vec![]; 716 | 717 | for val in values { 718 | let list = val.as_list()?; 719 | 720 | let v: Result, LabeledError> = list 721 | .iter() 722 | .enumerate() 723 | .map(|(i, e)| match e { 724 | Value::Int { .. } => Ok((i as f32, e.as_int()? as f32)), 725 | Value::Float { .. } => Ok((i as f32, e.as_float()? as f32)), 726 | e => Err(LabeledError::new(format!("Got {}, need integer or float.", e.get_type())).with_label("Incorrect type supplied.", call.head)), 727 | }) 728 | .collect(); 729 | 730 | let min_max_x = { 731 | let x: Vec = v.clone()?.iter().map(|e| e.0).collect(); 732 | let temp: Vec = v.clone()?.iter().map(|e| e.1).collect(); 733 | let y = Some(min_max(&temp)); 734 | (min_max(&x), y) 735 | }; 736 | 737 | data.push((min_max_x, v?)); 738 | } 739 | if data.len() != 2 { 740 | return Err(LabeledError::new("xyplot requires a nested list of length 2.").with_label( "Wrong number of dimensions in xyplot.", call.head)); 741 | } 742 | 743 | let (min, max) = { 744 | // only interested in the first list 745 | let (_, xy_x) = &data[0].0; 746 | xy_x.unwrap() 747 | }; 748 | 749 | let y: Vec = data[1].1.iter().map(|e| e.1).collect(); 750 | let xy: Vec<(f32, f32)> = data[0].1.iter().map(|e| e.1).zip(y).collect(); 751 | let chart_data = vec![xy]; 752 | 753 | let mut chart = Chart::new(max_x, max_y, min, max); 754 | 755 | let charts = chart 756 | .lineplot(&chart_shape(steps, bars, points, call, &chart_data[0])?) 757 | .to_string(); 758 | 759 | 760 | let mut final_chart = TAB.to_owned() + &charts.replace('\n', &format!("\n{}", TAB)); 761 | 762 | if let Some(t) = title { 763 | final_chart = TAB.to_owned() + &t + "\n" + &final_chart; 764 | } 765 | 766 | if legend { 767 | for (l, (_, _)) in data.iter().enumerate() { 768 | let col: PixelColor = COLORS[l]; 769 | final_chart += &format!("Line {}: {} ", l + 1, "---".color(col)); 770 | } 771 | } 772 | 773 | Ok(Value::string(final_chart, call.head)) 774 | } 775 | } 776 | 777 | impl SimplePluginCommand for CommandXyplot { 778 | type Plugin = PluginPlot; 779 | 780 | fn name(&self) -> &str { 781 | "xyplot" 782 | } 783 | 784 | fn signature(&self) -> nu_protocol::Signature { 785 | Signature::build("xyplot") 786 | .description("Render an ASCII xy plot from a list of values.") 787 | .named( 788 | "width", 789 | SyntaxShape::Number, 790 | "The maximum width of the plot.", 791 | None, 792 | ) 793 | .named( 794 | "height", 795 | SyntaxShape::Number, 796 | "The maximum height of the plot.", 797 | None, 798 | ) 799 | .named( 800 | "title", 801 | SyntaxShape::String, 802 | "Provide a title to the plot.", 803 | Some('t'), 804 | ) 805 | .switch("legend", "Plot a tiny, maybe useful legend.", Some('l')) 806 | .switch("bars", "Change lines to bars.", Some('b')) 807 | .switch("steps", "Change lines to steps.", Some('s')) 808 | .switch("points", "Change lines to points.", Some('p')) 809 | .category(Category::Experimental) 810 | } 811 | 812 | fn description(&self) -> &str { 813 | "Render an ASCII xy plot from a list of values." 814 | } 815 | 816 | fn run( 817 | &self, 818 | _plugin: &Self::Plugin, 819 | _engine: &nu_plugin::EngineInterface, 820 | call: &EvaluatedCall, 821 | input: &Value, 822 | ) -> Result { 823 | match input.as_list() { 824 | Ok(list) => { 825 | if list.is_empty() { 826 | return Err(LabeledError::new("Can't plot a zero element list.").with_label( "No elements in the list.", call.head)); 827 | } 828 | let (value_type, list_len_op) = check_equality_of_list(list, call)?; 829 | 830 | // if in fact we have a nested list 831 | if let Some(_len) = list_len_op { 832 | // we haven't implemented this yet 833 | self.plot_nested(call, input) 834 | } else { 835 | // we have a normal plot, single list of numbers 836 | match value_type { 837 | Type::Float | Type::Int => self.plot(call, input), 838 | e => Err(LabeledError::new(format!("List type is {}, but should be float or int.", e)).with_label("Incorrect List type.", call.head)), 839 | } 840 | } 841 | }, 842 | Err(e) => Err(LabeledError::new(format!("Input type should be a list: {}.", e)).with_label( "Incorrect input type.", call.head)), 843 | } 844 | } 845 | } 846 | 847 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use nu_plugin::{serve_plugin, MsgPackSerializer}; 2 | use nu_plugin_plot::PluginPlot; 3 | 4 | fn main() { 5 | serve_plugin(&PluginPlot {}, MsgPackSerializer {}) 6 | } 7 | --------------------------------------------------------------------------------