├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE.md ├── README.md ├── flake.lock ├── flake.nix ├── justfile └── src ├── app.rs ├── case_extension.rs ├── lib.rs └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /result 3 | 4 | 5 | # Added by cargo 6 | # 7 | # already existing elements were commented out 8 | 9 | #/target 10 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "0.7.20" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "assert_cmd" 16 | version = "2.0.7" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "fa3d466004a8b4cb1bc34044240a2fd29d17607e2e3bd613eb44fd48e8100da3" 19 | dependencies = [ 20 | "bstr", 21 | "doc-comment", 22 | "predicates", 23 | "predicates-core", 24 | "predicates-tree", 25 | "wait-timeout", 26 | ] 27 | 28 | [[package]] 29 | name = "atty" 30 | version = "0.2.14" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 33 | dependencies = [ 34 | "hermit-abi 0.1.19", 35 | "libc", 36 | "winapi", 37 | ] 38 | 39 | [[package]] 40 | name = "autocfg" 41 | version = "1.1.0" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 44 | 45 | [[package]] 46 | name = "bitflags" 47 | version = "1.3.2" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 50 | 51 | [[package]] 52 | name = "bstr" 53 | version = "1.0.1" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "fca0852af221f458706eb0725c03e4ed6c46af9ac98e6a689d5e634215d594dd" 56 | dependencies = [ 57 | "memchr", 58 | "once_cell", 59 | "regex-automata", 60 | "serde", 61 | ] 62 | 63 | [[package]] 64 | name = "cc" 65 | version = "1.0.77" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" 68 | 69 | [[package]] 70 | name = "ccase" 71 | version = "0.4.1" 72 | dependencies = [ 73 | "assert_cmd", 74 | "atty", 75 | "clap", 76 | "convert_case", 77 | "predicates", 78 | ] 79 | 80 | [[package]] 81 | name = "cfg-if" 82 | version = "1.0.0" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 85 | 86 | [[package]] 87 | name = "clap" 88 | version = "4.0.29" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d" 91 | dependencies = [ 92 | "bitflags", 93 | "clap_lex", 94 | "is-terminal", 95 | "once_cell", 96 | "strsim", 97 | "termcolor", 98 | "terminal_size", 99 | ] 100 | 101 | [[package]] 102 | name = "clap_lex" 103 | version = "0.3.0" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" 106 | dependencies = [ 107 | "os_str_bytes", 108 | ] 109 | 110 | [[package]] 111 | name = "convert_case" 112 | version = "0.6.0" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" 115 | dependencies = [ 116 | "rand", 117 | "unicode-segmentation", 118 | ] 119 | 120 | [[package]] 121 | name = "difflib" 122 | version = "0.4.0" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" 125 | 126 | [[package]] 127 | name = "doc-comment" 128 | version = "0.3.3" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" 131 | 132 | [[package]] 133 | name = "either" 134 | version = "1.8.0" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" 137 | 138 | [[package]] 139 | name = "errno" 140 | version = "0.2.8" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" 143 | dependencies = [ 144 | "errno-dragonfly", 145 | "libc", 146 | "winapi", 147 | ] 148 | 149 | [[package]] 150 | name = "errno-dragonfly" 151 | version = "0.1.2" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 154 | dependencies = [ 155 | "cc", 156 | "libc", 157 | ] 158 | 159 | [[package]] 160 | name = "float-cmp" 161 | version = "0.9.0" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" 164 | dependencies = [ 165 | "num-traits", 166 | ] 167 | 168 | [[package]] 169 | name = "getrandom" 170 | version = "0.1.16" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" 173 | dependencies = [ 174 | "cfg-if", 175 | "libc", 176 | "wasi", 177 | ] 178 | 179 | [[package]] 180 | name = "hermit-abi" 181 | version = "0.1.19" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 184 | dependencies = [ 185 | "libc", 186 | ] 187 | 188 | [[package]] 189 | name = "hermit-abi" 190 | version = "0.2.6" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" 193 | dependencies = [ 194 | "libc", 195 | ] 196 | 197 | [[package]] 198 | name = "io-lifetimes" 199 | version = "1.0.3" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" 202 | dependencies = [ 203 | "libc", 204 | "windows-sys", 205 | ] 206 | 207 | [[package]] 208 | name = "is-terminal" 209 | version = "0.4.1" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330" 212 | dependencies = [ 213 | "hermit-abi 0.2.6", 214 | "io-lifetimes", 215 | "rustix", 216 | "windows-sys", 217 | ] 218 | 219 | [[package]] 220 | name = "itertools" 221 | version = "0.10.5" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 224 | dependencies = [ 225 | "either", 226 | ] 227 | 228 | [[package]] 229 | name = "libc" 230 | version = "0.2.138" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" 233 | 234 | [[package]] 235 | name = "linux-raw-sys" 236 | version = "0.1.3" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f" 239 | 240 | [[package]] 241 | name = "memchr" 242 | version = "2.5.0" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 245 | 246 | [[package]] 247 | name = "normalize-line-endings" 248 | version = "0.3.0" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" 251 | 252 | [[package]] 253 | name = "num-traits" 254 | version = "0.2.15" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 257 | dependencies = [ 258 | "autocfg", 259 | ] 260 | 261 | [[package]] 262 | name = "once_cell" 263 | version = "1.16.0" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" 266 | 267 | [[package]] 268 | name = "os_str_bytes" 269 | version = "6.4.1" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" 272 | 273 | [[package]] 274 | name = "ppv-lite86" 275 | version = "0.2.17" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 278 | 279 | [[package]] 280 | name = "predicates" 281 | version = "2.1.4" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "f54fc5dc63ed3bbf19494623db4f3af16842c0d975818e469022d09e53f0aa05" 284 | dependencies = [ 285 | "difflib", 286 | "float-cmp", 287 | "itertools", 288 | "normalize-line-endings", 289 | "predicates-core", 290 | "regex", 291 | ] 292 | 293 | [[package]] 294 | name = "predicates-core" 295 | version = "1.0.5" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2" 298 | 299 | [[package]] 300 | name = "predicates-tree" 301 | version = "1.0.7" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d" 304 | dependencies = [ 305 | "predicates-core", 306 | "termtree", 307 | ] 308 | 309 | [[package]] 310 | name = "rand" 311 | version = "0.7.3" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 314 | dependencies = [ 315 | "getrandom", 316 | "libc", 317 | "rand_chacha", 318 | "rand_core", 319 | "rand_hc", 320 | ] 321 | 322 | [[package]] 323 | name = "rand_chacha" 324 | version = "0.2.2" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 327 | dependencies = [ 328 | "ppv-lite86", 329 | "rand_core", 330 | ] 331 | 332 | [[package]] 333 | name = "rand_core" 334 | version = "0.5.1" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 337 | dependencies = [ 338 | "getrandom", 339 | ] 340 | 341 | [[package]] 342 | name = "rand_hc" 343 | version = "0.2.0" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 346 | dependencies = [ 347 | "rand_core", 348 | ] 349 | 350 | [[package]] 351 | name = "regex" 352 | version = "1.7.0" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" 355 | dependencies = [ 356 | "aho-corasick", 357 | "memchr", 358 | "regex-syntax", 359 | ] 360 | 361 | [[package]] 362 | name = "regex-automata" 363 | version = "0.1.10" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 366 | 367 | [[package]] 368 | name = "regex-syntax" 369 | version = "0.6.28" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" 372 | 373 | [[package]] 374 | name = "rustix" 375 | version = "0.36.5" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588" 378 | dependencies = [ 379 | "bitflags", 380 | "errno", 381 | "io-lifetimes", 382 | "libc", 383 | "linux-raw-sys", 384 | "windows-sys", 385 | ] 386 | 387 | [[package]] 388 | name = "serde" 389 | version = "1.0.149" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055" 392 | 393 | [[package]] 394 | name = "strsim" 395 | version = "0.10.0" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 398 | 399 | [[package]] 400 | name = "termcolor" 401 | version = "1.1.3" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 404 | dependencies = [ 405 | "winapi-util", 406 | ] 407 | 408 | [[package]] 409 | name = "terminal_size" 410 | version = "0.2.3" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "cb20089a8ba2b69debd491f8d2d023761cbf196e999218c591fa1e7e15a21907" 413 | dependencies = [ 414 | "rustix", 415 | "windows-sys", 416 | ] 417 | 418 | [[package]] 419 | name = "termtree" 420 | version = "0.4.0" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" 423 | 424 | [[package]] 425 | name = "unicode-segmentation" 426 | version = "1.10.0" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" 429 | 430 | [[package]] 431 | name = "wait-timeout" 432 | version = "0.2.0" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" 435 | dependencies = [ 436 | "libc", 437 | ] 438 | 439 | [[package]] 440 | name = "wasi" 441 | version = "0.9.0+wasi-snapshot-preview1" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 444 | 445 | [[package]] 446 | name = "winapi" 447 | version = "0.3.9" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 450 | dependencies = [ 451 | "winapi-i686-pc-windows-gnu", 452 | "winapi-x86_64-pc-windows-gnu", 453 | ] 454 | 455 | [[package]] 456 | name = "winapi-i686-pc-windows-gnu" 457 | version = "0.4.0" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 460 | 461 | [[package]] 462 | name = "winapi-util" 463 | version = "0.1.5" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 466 | dependencies = [ 467 | "winapi", 468 | ] 469 | 470 | [[package]] 471 | name = "winapi-x86_64-pc-windows-gnu" 472 | version = "0.4.0" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 475 | 476 | [[package]] 477 | name = "windows-sys" 478 | version = "0.42.0" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" 481 | dependencies = [ 482 | "windows_aarch64_gnullvm", 483 | "windows_aarch64_msvc", 484 | "windows_i686_gnu", 485 | "windows_i686_msvc", 486 | "windows_x86_64_gnu", 487 | "windows_x86_64_gnullvm", 488 | "windows_x86_64_msvc", 489 | ] 490 | 491 | [[package]] 492 | name = "windows_aarch64_gnullvm" 493 | version = "0.42.0" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" 496 | 497 | [[package]] 498 | name = "windows_aarch64_msvc" 499 | version = "0.42.0" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" 502 | 503 | [[package]] 504 | name = "windows_i686_gnu" 505 | version = "0.42.0" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" 508 | 509 | [[package]] 510 | name = "windows_i686_msvc" 511 | version = "0.42.0" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" 514 | 515 | [[package]] 516 | name = "windows_x86_64_gnu" 517 | version = "0.42.0" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" 520 | 521 | [[package]] 522 | name = "windows_x86_64_gnullvm" 523 | version = "0.42.0" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" 526 | 527 | [[package]] 528 | name = "windows_x86_64_msvc" 529 | version = "0.42.0" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" 532 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ccase" 3 | version = "0.4.1" 4 | authors = ["Rutrum "] 5 | edition = "2021" 6 | description = "Command line interface to convert strings into any case" 7 | license = "MIT" 8 | keywords = [ "casing", "case", "string" ] 9 | categories = [ "text-processing", "command-line-utilities" ] 10 | readme = "README.md" 11 | repository = "https://github.com/rutrum/ccase" 12 | documentation = "https://github.com/rutrum/ccase" 13 | 14 | [[bin]] 15 | name = "ccase" 16 | path = "src/main.rs" 17 | 18 | [dependencies] 19 | convert_case = { version = "0.6", features = ["random"] } 20 | clap = { version = "^4.0", features = ["cargo", "color", "wrap_help"] } 21 | atty = "0.2.14" 22 | 23 | [dev-dependencies] 24 | predicates = "2.1.1" 25 | assert_cmd = "2.0.4" 26 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 David Purdum 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `ccase` is a command line utility for converting between string cases 2 | 3 | Convert strings between snake case, kebab case, camel case, title case, pascal case, and so many more. 4 | 5 | ## Use Cases 6 | 7 | ```sh 8 | $ ls ~/roms 9 | donkey_kong_64.z64 10 | kirby_64_the_crystal_shards.z64 11 | super_mario_64.z64 12 | $ ls ~/roms | cut -d '.' -f 1 | ccase -f snake -t title 13 | Donkey Kong 64 14 | Kirby 64 The Crystal Shards 15 | Super Mario 64 16 | ``` 17 | 18 | ```sh 19 | $ ls xmas 20 | 'Family Christmas photo-1999.png' 21 | Family_christmas_photo-2000.png 22 | family-christmas-photo-2001.png 23 | FamilyChristmasPhoto2002.png 24 | $ for f in xmas/*; do mv "$f" $(ccase -f snake "$f"); done 25 | $ ls xmas 26 | family_christmas_photo_1999.png 27 | family_christmas_photo_2000.png 28 | family_christmas_photo_2001.png 29 | family_christmas_photo_2002.png 30 | ``` 31 | 32 | ## Install 33 | 34 | You can install using `cargo`: 35 | ``` 36 | cargo install ccase 37 | ``` 38 | You can also install from the debian provided in the releases. 39 | 40 | ## Usage 41 | 42 | ### `-t, --to ` 43 | 44 | Transform a string in**to** a certain string case. 45 | 46 | ```sh 47 | $ ccase -t snake myVarName 48 | my_var_name 49 | $ ccase -t upper my-var-name 50 | MY VAR NAME 51 | $ ccase -t kebab my_var_name 52 | my-var-name 53 | $ ccase -t pascal "my var name" 54 | MyVarName 55 | ``` 56 | 57 | ### `-f, --from ` 58 | 59 | Splits string based on the boundaries associated with that case. For example, `snake` case splits on underscores `_` and `camel` splits based on lowercase characters followed by uppercase characters `aA`. 60 | 61 | ```sh 62 | $ ccase -f camel -t snake myVar-Name 63 | my_var-name 64 | $ ccase -f snake -t snake myVar-name 65 | myvar-name 66 | $ ccase -f kebab -t snake myVar-name 67 | myvar_name 68 | ``` 69 | 70 | ### `-b, --boundaries ` 71 | 72 | Specify precisely what conditions should be used for splitting a string into words. Whatever boundaries are present in the boundary string will be used against the input. Any example can do, but a nice way to specify is to separate boundaries with a `:`. For example, `aA:a1:1a` will split based on lowercase followed by uppercase, lowercase followed by and preceded by a digit. 73 | 74 | ```sh 75 | $ ccase -b "aA:a1" -t kebab myVar1 76 | my_var_1 77 | $ ccase -b "_:-: " -t camel my_Var-name 78 | my-var-name 79 | $ ccase -b "aAa" -t snake myVar 80 | my_v_ar 81 | $ ccase -b "AAa" -t snake MYVarName # special acronym boundary 82 | my_var_name 83 | ``` 84 | 85 | ### `-p, --pattern ` 86 | 87 | Transforms the words of the input based on a pattern. A pattern describes the order of "word cases". For example, the camel pattern is a lowercase word followed by capitalized words, like in camel case, while the lower pattern is just lowercased words, like in snake case. 88 | 89 | ```sh 90 | $ ccase -p camel my-var-name 91 | myVarName 92 | $ ccase -p capital my-var-name 93 | MyVarName 94 | $ ccase -p sentence my-var-name 95 | Myvarname 96 | ``` 97 | 98 | ### `-d, --delimeter ` 99 | 100 | Join the words using a delimeter. By default, the delimeter is an empty string, as used in camel case. 101 | 102 | ```sh 103 | $ ccase -p camel -d _ my-var-name 104 | my_Var_Name 105 | $ ccase -p capital -d . my-var-name 106 | My.Var.Name 107 | $ ccase -p sentence -d __ my-var-name 108 | My__var__name 109 | ``` 110 | 111 | # How Case Conversion Works 112 | 113 | A _case_ can be defined as a _pattern_ joined with a _delimeter_. Turning a list of words into a certain case happens in two steps. First, each word is transformed by the pattern. Then the words are joined together with the delimeter. 114 | 115 | _Case conversion_ is splitting a single string into words, then performing the transformation and joining. Input is split using _boundaries_. 116 | 117 | ## Step 1: Splitting Input into Words by Boundaries 118 | 119 | Boundaries define how to split input. There are three types of boundaries. There are character based boundaries like hyphen `-`, underscore `_`, and space ` ` that are not part of the final word list, but split the input around the character. There are also boundaries like lower-upper (`aA`) or digit-lower (`1a`) that split the input between the characters, and matching characters are included in the result. Lastly there is the acryonym boundary (`AAa`) that splits between the two uppercase characters (an example of this is in `HTTPRequest`). 120 | 121 | In `ccase`, one can specify boundaries using the `--boundaries` option, or they can define a case to convert _from_ using `--from`. Inputs that are transformed into a certain case will have boundaries that associated with the result. For example, snake case is joined with underscores, so only underscores will be used as boundaries. Strings in camel case on the other hand have boundaries between lowercase and uppercase letters, so the lower-upper (`aA`) boundary is used. 122 | 123 | ## Step 2: Transforming Words with Pattern 124 | 125 | A pattern is a series of _word cases_ that describe how a single word is transformed. For example, the lower word case makes all characters lowercase, the upper word case makes all characters uppercase, and the capital word case makes the first letter uppercase and the remaining lowercase. For example, snake case uses a pattern where all word should be lowercased. This is the lowercase pattern. Camel case uses a pattern where the first word is lowercased, and the remaining words are capitalized. This is called the camel pattern. 126 | 127 | In `ccase`, a pattern can directly specified with `--pattern` or whatever pattern is associated with the case in `--to` option will be used. 128 | 129 | ## Step 3: Joining Words with a Delimeter 130 | 131 | Lastly, words are joined with a string specified as a delimeter. Cases like snake, kebab, and lower use character strings `_`, `-`, and space respectively. The delimeter can also be an empty string, like in camel case. 132 | 133 | In `ccase`, the delimeter can be specified with `--delimeter` or whatever delimeter is associated with the case in the `--to` option. If no delimeter is supplied, the delimeter defaults to an empty string. 134 | 135 | # Cases, Patterns, and Boundaries 136 | 137 | ## List of Cases 138 | 139 | | Case | Example | 140 | | -- | -- | 141 | | upper | UPPER CASE | 142 | | lower | lower case | 143 | | title | Title Case | 144 | | toggle | tOGGLE cASE | 145 | | camel | camelCase | 146 | | pascal | PascalCase | 147 | | uppercamel | UpperCamelCase | 148 | | snake | snake_case | 149 | | uppersnake | UPPER_SNAKE_CASE | 150 | | screamingsnake | SCREAMING_SNAKE_CASE | 151 | | kebab | kebab-case | 152 | | cobol | COBOL-CASE | 153 | | upperkebab | UPPER-KEBAB-CASE | 154 | | train | Train-Case | 155 | | flat | flatcase | 156 | | upperflat | UPPERFLATCASE | 157 | | alternating | aLtErNaTiNg CaSe | 158 | | random | RANDOm cAsE | 159 | | pseudorandom | PseUdO rAnDoM cASe | 160 | 161 | ## List of Patterns 162 | 163 | | Pattern | Definition | 164 | | -- | -- | 165 | | upper | UPPER, UPPER, ... | 166 | | lower | lower, lower, ... | 167 | | capital | Capital, Capital, ... | 168 | | sentence | Capital, lower, lower, ... | 169 | | camel | lower, Capital, Capital, ... | 170 | 171 | ## List of Boundaries 172 | 173 | | Type | Example String | 174 | | -- | -- | 175 | | Hyphen | `-` | 176 | | Underscore | `_` | 177 | | Space | ` ` | 178 | | UpperLower | `Aa` | 179 | | LowerUpper | `aA` | 180 | | DigitUpper | `1A` | 181 | | UpperDigit | `A1` | 182 | | DigitLower | `1a` | 183 | | LowerDigit | `a1` | 184 | | Acronym | `AAa` | 185 | 186 | # Rust Library 187 | 188 | `ccase` is a command line utility built on top of a rust library `convert_case`([github](https://github.com/rutrum/convert-case), [crates.io](https://crates.io/crates/convert_case)). This library and its documentation are the source for the list of cases, patterns, boundaries, and default many behaviors found in this command line utility. 189 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1692447944, 6 | "narHash": "sha256-fkJGNjEmTPvqBs215EQU4r9ivecV5Qge5cF/QDLVn3U=", 7 | "owner": "nixos", 8 | "repo": "nixpkgs", 9 | "rev": "d680ded26da5cf104dd2735a51e88d2d8f487b4d", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "nixos", 14 | "ref": "nixos-unstable", 15 | "repo": "nixpkgs", 16 | "type": "github" 17 | } 18 | }, 19 | "root": { 20 | "inputs": { 21 | "nixpkgs": "nixpkgs", 22 | "utils": "utils" 23 | } 24 | }, 25 | "systems": { 26 | "locked": { 27 | "lastModified": 1681028828, 28 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 29 | "owner": "nix-systems", 30 | "repo": "default", 31 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 32 | "type": "github" 33 | }, 34 | "original": { 35 | "owner": "nix-systems", 36 | "repo": "default", 37 | "type": "github" 38 | } 39 | }, 40 | "utils": { 41 | "inputs": { 42 | "systems": "systems" 43 | }, 44 | "locked": { 45 | "lastModified": 1689068808, 46 | "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", 47 | "owner": "numtide", 48 | "repo": "flake-utils", 49 | "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", 50 | "type": "github" 51 | }, 52 | "original": { 53 | "owner": "numtide", 54 | "repo": "flake-utils", 55 | "type": "github" 56 | } 57 | } 58 | }, 59 | "root": "root", 60 | "version": 7 61 | } 62 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "ccase flake"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 6 | utils.url = "github:numtide/flake-utils"; 7 | }; 8 | 9 | outputs = { self, nixpkgs, utils }: 10 | let 11 | name = "ccase"; 12 | in 13 | utils.lib.eachDefaultSystem (system: 14 | let 15 | pkgs = nixpkgs.legacyPackages.${system}; 16 | lib = pkgs.lib; 17 | in 18 | { 19 | packages.default = pkgs.rustPlatform.buildRustPackage { 20 | pname = name; 21 | version = (lib.importTOML ./Cargo.toml).package.version; 22 | src = ./.; 23 | cargoLock = { 24 | lockFile = ./Cargo.lock; 25 | }; 26 | }; 27 | 28 | devShell = pkgs.mkShell { 29 | name = "ccase"; 30 | buildInputs = with pkgs; [ 31 | just 32 | watchexec 33 | cargo 34 | ]; 35 | }; 36 | } 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | default: run 2 | 3 | test *filter="": 4 | cargo build 5 | cargo test {{filter}} 6 | 7 | help: 8 | cargo run -- --help 9 | 10 | watch: 11 | watchexec -c -w src -- just test 12 | 13 | watch-help: 14 | watchexec -c -w src -- cargo run -q -- -h 15 | 16 | watch-long-help: 17 | watchexec -c -w src -- cargo run -q -- --help 18 | 19 | run *ARGS : 20 | cargo run -- {{ARGS}} 21 | 22 | dry-publish: 23 | cargo publish --dry-run 24 | -------------------------------------------------------------------------------- /src/app.rs: -------------------------------------------------------------------------------- 1 | use crate::{case_value_parser, pattern_value_parser, CaseExtension, PatternExtension}; 2 | use clap::{builder::StyledStr, crate_version, Arg, ArgAction, Command}; 3 | use convert_case::{Case, Casing}; 4 | 5 | pub fn build() -> Command { 6 | Command::new("ccase") 7 | .version(crate_version!()) 8 | .about("Convert between string cases.\nhttps://github.com/rutrum/ccase") 9 | .arg_required_else_help(true) 10 | .args(args::all()) 11 | .override_usage(usage()) 12 | .max_term_width(90) 13 | .after_help(after_help()) 14 | .after_long_help(after_long_help()) 15 | } 16 | 17 | fn usage() -> StyledStr { 18 | StyledStr::from( 19 | "\x1b[1mccase --to\x1b[0m ...\n \ 20 | \x1b[1mccase --to\x1b[0m \x1b[1m--from\x1b[0m ...", 21 | ) 22 | } 23 | 24 | fn after_long_help() -> StyledStr { 25 | let s = format!( 26 | "\x1b[1;4mCase Conversion:\x1b[0m\n \ 27 | Visit the code repository at https://github.com/rutrum/ccase for a full description\n \ 28 | on how strings are converted using boundaries, patterns, and delimeters.\n\n\ 29 | \x1b[1;4mCases:\x1b[0m\n\ 30 | {}\n\ 31 | \x1b[1;4mPatterns:\x1b[0m\n\ 32 | {}\n\ 33 | ", 34 | list_cases(), 35 | list_patterns(), 36 | ); 37 | 38 | StyledStr::from(s) 39 | } 40 | 41 | fn after_help() -> StyledStr { 42 | StyledStr::from( 43 | "\x1b[1;4mCases:\x1b[0m\n See --help for list of cases 44 | ", 45 | ) 46 | } 47 | 48 | fn list_cases() -> String { 49 | let mut s = String::new(); 50 | for case in Case::all_cases() { 51 | let case_str = format!("{:?}", case).to_case(Case::Flat); 52 | let underline_case = format!("\x1b[1m{}\x1b[0m", case_str); 53 | s = format!("{}{:>25} {}\n", s, underline_case, case.name_in_case()) 54 | } 55 | s 56 | } 57 | 58 | fn list_patterns() -> String { 59 | let mut s = String::new(); 60 | for pattern in crate::all_patterns() { 61 | let pattern_str = format!("{:?}", pattern).to_case(Case::Flat); 62 | let underline_pattern = format!("\x1b[1m{}\x1b[0m", pattern_str); 63 | s = format!("{}{:>25} {}\n", s, underline_pattern, pattern.example()) 64 | } 65 | s 66 | } 67 | 68 | mod args { 69 | use super::*; 70 | 71 | pub fn all() -> [Arg; 6] { 72 | [to(), from(), input(), boundaries(), pattern(), delimeter()] 73 | } 74 | 75 | fn to() -> Arg { 76 | Arg::new("to") 77 | .short('t') 78 | .long("to") 79 | .value_name("case") 80 | .help("Case to convert to") 81 | .long_help( 82 | "Convert the input into this case. \ 83 | The input is mutated and joined using the pattern and delimiter of the case.", 84 | ) 85 | .value_parser(case_value_parser) 86 | .required_unless_present("pattern") 87 | } 88 | 89 | fn from() -> Arg { 90 | Arg::new("from") 91 | .short('f') 92 | .long("from") 93 | .value_name("case") 94 | .help("Case to parse input as") 95 | .long_help( 96 | "Parse the input as if it were this case. \ 97 | This means splitting the input based on boundaries found in that case.", 98 | ) 99 | .value_parser(case_value_parser) 100 | } 101 | 102 | fn boundaries() -> Arg { 103 | Arg::new("boundaries") 104 | .short('b') 105 | .long("boundaries") 106 | .value_name("string") 107 | .help("String of boundaries to split input") 108 | .long_help( 109 | "String that contains boundaries on how to split input. \ 110 | Any boundary contained in the string will be used as boundaries \ 111 | for splitting input into words.", 112 | ) 113 | .conflicts_with("from") 114 | } 115 | 116 | fn pattern() -> Arg { 117 | Arg::new("pattern") 118 | .short('p') 119 | .long("pattern") 120 | .help("Pattern to transform words") 121 | .long_help("Transform the words after splitting the input based upon the pattern.") 122 | .conflicts_with("to") 123 | .value_parser(pattern_value_parser) 124 | } 125 | 126 | fn delimeter() -> Arg { 127 | Arg::new("delimeter") 128 | .short('d') 129 | .long("delimeter") 130 | .help("String to join words by") 131 | .long_help("String to join words together after transforming.") 132 | .conflicts_with("to") 133 | .value_name("string") 134 | .allow_hyphen_values(true) 135 | } 136 | 137 | fn input() -> Arg { 138 | Arg::new("input") 139 | .help("The string(s) to convert") 140 | .long_help("The string(s) to convert into the --to case.") 141 | .action(ArgAction::Append) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/case_extension.rs: -------------------------------------------------------------------------------- 1 | use convert_case::{Case, Casing, Pattern}; 2 | 3 | /// Extensions Case with additional functions. 4 | pub trait CaseExtension { 5 | /// The name of the case in the case 6 | fn name_in_case(self) -> String; 7 | 8 | /// If case is an alias to another (one directional) 9 | fn is_alias(&self) -> Option; 10 | 11 | /// Alternative shorter name 12 | fn short_name(&self) -> Option<&str>; 13 | 14 | /// Converts case name to flat case 15 | fn name_to_flat_case(&self) -> String; 16 | } 17 | 18 | impl CaseExtension for Case { 19 | fn name_in_case(self) -> String { 20 | format!("{:?}Case", self).to_case(self) 21 | } 22 | 23 | fn is_alias(&self) -> Option { 24 | use Case::*; 25 | match self { 26 | UpperCamel => Some(Pascal), 27 | UpperKebab => Some(Cobol), 28 | ScreamingSnake => Some(UpperSnake), 29 | _ => None, 30 | } 31 | } 32 | 33 | fn short_name(&self) -> Option<&'static str> { 34 | use Case::*; 35 | match self { 36 | PseudoRandom => Some("pseudo"), 37 | ScreamingSnake => Some("screaming"), 38 | Alternating => Some("alternate"), 39 | _ => None, 40 | } 41 | } 42 | 43 | fn name_to_flat_case(&self) -> String { 44 | format!("{:?}", self).to_case(Case::Flat) 45 | } 46 | } 47 | 48 | pub trait PatternExtension { 49 | /// Prints an example of pattern 50 | fn example(&self) -> String; 51 | } 52 | 53 | impl PatternExtension for Pattern { 54 | fn example(&self) -> String { 55 | use Pattern::*; 56 | match self { 57 | Lowercase => "lower, lower, ...", 58 | Uppercase => "UPPER, UPPER, ...", 59 | Capital => "Capital, Capital, ...", 60 | Sentence => "Capital, lower, lower, ...", 61 | Camel => "lower, Capital, Capital, ...", 62 | Alternating => "aLtErNaTiNg, aLtErNaTiNg, ...", 63 | Toggle => "tOGGLE, tOGGLE, ...", 64 | PseudoRandom => "pSUeDorANdOm, pSUedORaNdoM, ...", 65 | Random => "RanDOM, RAndom, ...", 66 | } 67 | .to_string() 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod app; 2 | mod case_extension; 3 | 4 | pub use app::build as build_app; 5 | pub use case_extension::*; 6 | 7 | use clap::error::{Error, ErrorKind}; 8 | use convert_case::{Case, Casing, Pattern}; 9 | 10 | fn pattern_value_parser(s: &str) -> Result { 11 | let pattern_str = s.to_case(Case::Flat); 12 | for pattern in all_patterns() { 13 | let pattern_in_flat = format!("{:?}", pattern).to_case(Case::Flat); 14 | if pattern_str == pattern_in_flat { 15 | return Ok(pattern); 16 | } 17 | } 18 | Err(Error::raw( 19 | ErrorKind::ValueValidation, 20 | format!( 21 | "'{}' is not a valid pattern. See ccase --help for list of patterns.", 22 | s 23 | ), 24 | )) 25 | } 26 | 27 | fn all_patterns() -> Vec { 28 | use Pattern::*; 29 | vec![ 30 | Lowercase, 31 | Uppercase, 32 | Capital, 33 | Sentence, 34 | Camel, 35 | Alternating, 36 | Toggle, 37 | PseudoRandom, 38 | Random, 39 | ] 40 | } 41 | 42 | fn case_value_parser(s: &str) -> Result { 43 | let case_str = s.to_case(Case::Flat); 44 | for case in Case::all_cases() { 45 | if case_str == case.name_to_flat_case() { 46 | return Ok(case); 47 | } 48 | if let Some(short) = case.short_name() { 49 | if case_str == short { 50 | return Ok(case); 51 | } 52 | } 53 | } 54 | Err(Error::raw( 55 | ErrorKind::ValueValidation, 56 | format!( 57 | "'{}' is not a valid case. See ccase --help for list of cases.", 58 | s 59 | ), 60 | )) 61 | } 62 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::ArgMatches; 2 | use convert_case::{Boundary, Case, Converter, Pattern}; 3 | use std::env; 4 | use std::io::{self, Read}; 5 | 6 | fn main() { 7 | let mut app = ccase::build_app(); 8 | 9 | let missing_error = app.error( 10 | clap::error::ErrorKind::MissingRequiredArgument, 11 | "The following required arguments were not provided:\n \ 12 | \x1b[32m...\x1b[m", 13 | ); 14 | 15 | let args = get_args_with_stdin(); 16 | 17 | let matches = app.get_matches_from(args); 18 | 19 | let inputs = match matches.get_many::("input") { 20 | None => { 21 | if atty::isnt(atty::Stream::Stdin) { 22 | Default::default() 23 | } else { 24 | missing_error.exit(); 25 | } 26 | } 27 | Some(inputs) => inputs, 28 | }; 29 | 30 | /* 31 | inputs.for_each(|input| { 32 | println!("{:?}", input); 33 | convert(&matches, input) 34 | }); 35 | */ 36 | inputs.for_each(|input| convert(&matches, input)); 37 | } 38 | 39 | fn get_args_with_stdin() -> Vec { 40 | let mut args: Vec = env::args_os().map(|x| x.into_string().unwrap()).collect(); 41 | 42 | if atty::isnt(atty::Stream::Stdin) { 43 | let stdin = io::stdin(); 44 | let mut handle = stdin.lock(); 45 | 46 | let mut v = Vec::new(); 47 | handle.read_to_end(&mut v).unwrap(); 48 | 49 | let s = String::from_utf8(v).unwrap(); 50 | 51 | if !s.is_empty() { 52 | for word in s.lines() { 53 | args.push(word.trim_end().to_string()); 54 | } 55 | } 56 | } 57 | 58 | args 59 | } 60 | 61 | fn convert(matches: &ArgMatches, input: &String) { 62 | // check if from or boundaries or none 63 | 64 | let mut conv = Converter::new(); 65 | 66 | if let Some(&from) = matches.get_one::("from") { 67 | // --from 68 | conv = conv.from_case(from); 69 | } else if let Some(boundary_str) = matches.get_one::("boundaries") { 70 | // --boundaries 71 | let boundaries = Boundary::list_from(boundary_str.as_str()); 72 | conv = conv.set_boundaries(&boundaries); 73 | } 74 | 75 | if let Some(&to) = matches.get_one::("to") { 76 | // --to 77 | conv = conv.to_case(to); 78 | } else if let Some(&pattern) = matches.get_one::("pattern") { 79 | // --pattern 80 | conv = conv.set_pattern(pattern); 81 | 82 | if let Some(delim) = matches.get_one::("delimeter") { 83 | // --delimeter 84 | conv = conv.set_delim(delim); 85 | } 86 | } 87 | 88 | println!("{}", conv.convert(input)) 89 | } 90 | 91 | #[cfg(test)] 92 | mod test { 93 | use assert_cmd::{assert::Assert, Command}; 94 | use predicates::str::contains; 95 | 96 | fn ccase(args: &[&str]) -> Assert { 97 | Command::cargo_bin("ccase").unwrap().args(args).assert() 98 | } 99 | 100 | #[test] 101 | fn to_case() { 102 | ccase(&["-t", "snake", "myVarName"]) 103 | .success() 104 | .stdout("my_var_name\n"); 105 | ccase(&["--to", "kebab", "myVarName"]) 106 | .success() 107 | .stdout("my-var-name\n"); 108 | ccase(&["--to", "kebab", "my Var Name"]) 109 | .success() 110 | .stdout("my-var-name\n"); 111 | } 112 | 113 | #[test] 114 | fn from_case() { 115 | ccase(&["-f", "snake", "-t", "pascal", "my_var-name"]) 116 | .success() 117 | .stdout("MyVar-name\n"); 118 | ccase(&["-t", "snake", "--from", "pascal", "myVar-name"]) 119 | .success() 120 | .stdout("my_var-name\n"); 121 | ccase(&["-t", "snake", "--from", "lower", "my Var-name"]) 122 | .success() 123 | .stdout("my_var-name\n"); 124 | } 125 | 126 | #[test] 127 | fn to_required() { 128 | ccase(&["myvarname"]) 129 | .failure() 130 | .stderr(contains("following required arguments")) 131 | .stderr(contains("--to")); 132 | } 133 | 134 | #[test] 135 | fn pattern_only() { 136 | ccase(&["-p", "capital", "MY_VAR_NAME"]) 137 | .success() 138 | .stdout("MyVarName\n"); 139 | ccase(&["-p", "Sentence", "MY_VAR_NAME"]) 140 | .success() 141 | .stdout("Myvarname\n"); 142 | } 143 | 144 | #[test] 145 | fn to_exclusive_with_pattern_delim() { 146 | ccase(&["-t", "snake", "-p", "capital", "MY_VAR_NAME"]) 147 | .failure() 148 | .stderr(contains("--to ")) 149 | .stderr(contains("cannot be used with")) 150 | .stderr(contains("--pattern ")); 151 | ccase(&["-t", "snake", "-d", "-", "MY_VAR_NAME"]) 152 | .failure() 153 | .stderr(contains("--to ")) 154 | .stderr(contains("cannot be used with")) 155 | .stderr(contains("--delimeter ")); 156 | } 157 | 158 | #[test] 159 | fn delimeter() { 160 | ccase(&["-p", "sentence", "-d", ".", "myVarName"]) 161 | .success() 162 | .stdout("My.var.name\n"); 163 | } 164 | 165 | #[ignore] // atty is tricked in test, look at ccase -t snake manually 166 | #[test] 167 | fn input_required() { 168 | ccase(&["-t", "snake"]) 169 | .failure() 170 | .stderr(contains("following required arguments")) 171 | .stderr(contains("input")); 172 | } 173 | 174 | #[test] 175 | fn help_default() { 176 | ccase(&[]).failure().stderr(contains("Usage")); 177 | } 178 | 179 | #[test] 180 | fn case_inputs_not_lower() { 181 | ccase(&["-t", "SNAKE", "myVarName"]) 182 | .success() 183 | .stdout("my_var_name\n"); 184 | ccase(&["-t", "SnAkE", "myVarName"]) 185 | .success() 186 | .stdout("my_var_name\n"); 187 | ccase(&["-t", "snake", "-f", "KEBab", "my-varName"]) 188 | .success() 189 | .stdout("my_varname\n"); 190 | ccase(&["-t", "snake", "-f", "KEBAB", "my-varName"]) 191 | .success() 192 | .stdout("my_varname\n"); 193 | } 194 | 195 | #[test] 196 | fn invalid_case() { 197 | ccase(&["-t", "SNEK", "myVarName"]) 198 | .failure() 199 | .stderr(contains("Invalid value")) 200 | .stderr(contains("--to")); 201 | ccase(&["-t", "snake", "-f", "SNEK", "my-varName"]) 202 | .failure() 203 | .stderr(contains("Invalid value")) 204 | .stderr(contains("--from")); 205 | } 206 | 207 | #[test] 208 | fn invalid_pattern() { 209 | ccase(&["-p", "SENT", "myVarName"]) 210 | .failure() 211 | .stderr(contains("Invalid value")) 212 | .stderr(contains("--pattern")); 213 | ccase(&["-p", "SENT", "-f", "snake", "my-varName"]) 214 | .failure() 215 | .stderr(contains("Invalid value")) 216 | .stderr(contains("--pattern")); 217 | } 218 | 219 | #[test] 220 | fn empty_string_input() { 221 | ccase(&["-t", "snake", r#""#]).success().stdout("\n"); 222 | } 223 | 224 | #[test] 225 | fn boundaries() { 226 | ccase(&["-t", "snake", "-b", "aA", "myVar-Name-Longer"]) 227 | .success() 228 | .stdout("my_var-name-longer\n"); 229 | ccase(&["-t", "snake", "-b", "-", "myVar-Name-Longer"]) 230 | .success() 231 | .stdout("myvar_name_longer\n"); 232 | } 233 | 234 | #[test] 235 | fn from_and_boundaries_exclusive() { 236 | ccase(&["-t", "snake", "-b", "_", "-f", "kebab", "myVar-Name-Longer"]) 237 | .failure() 238 | .stderr(contains("--from")) 239 | .stderr(contains("cannot be used with")) 240 | .stderr(contains("--boundaries")); 241 | } 242 | 243 | #[test] 244 | fn multiple_inputs() { 245 | ccase(&["-t", "snake", "myVarName", "anotherMultiWordToken"]) 246 | .success() 247 | .stdout("my_var_name\nanother_multi_word_token\n"); 248 | } 249 | 250 | mod stdin { 251 | use super::*; 252 | 253 | fn pipe_ccase(stdin: &str, args: &[&str]) -> Assert { 254 | Command::cargo_bin("ccase") 255 | .unwrap() 256 | .args(args) 257 | .write_stdin(stdin) 258 | .assert() 259 | } 260 | 261 | #[test] 262 | fn stdin() { 263 | pipe_ccase("myVarName", &["-t", "snake"]) 264 | .success() 265 | .stdout("my_var_name\n"); 266 | } 267 | 268 | #[test] 269 | fn newline_ending() { 270 | pipe_ccase("myVarName\n", &["-t", "snake"]) 271 | .success() 272 | .stdout("my_var_name\n"); 273 | } 274 | 275 | #[test] 276 | fn empty() { 277 | pipe_ccase(r#""#, &["-t", "snake"]).success().stdout(""); 278 | } 279 | 280 | #[test] 281 | fn multiple_inputs() { 282 | pipe_ccase("myVarName\nanotherMultiWordToken\n", &["-t", "Pascal"]) 283 | .success() 284 | .stdout("MyVarName\nAnotherMultiWordToken\n"); 285 | } 286 | } 287 | } 288 | --------------------------------------------------------------------------------