├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── src ├── _tests │ ├── tests_config.rs │ └── tests_run_new.rs ├── app_cmd.rs ├── config │ ├── mod.rs │ └── runner.rs ├── error.rs ├── exec.rs ├── main.rs ├── prelude.rs ├── run_dev.rs ├── run_new.rs └── utils │ ├── froms.rs │ └── mod.rs └── tmpl └── Awesome.toml /.gitignore: -------------------------------------------------------------------------------- 1 | # By Default, Ignore any .*, except .gitignore 2 | .* 3 | !.gitignore 4 | 5 | # --- Rust 6 | target/ 7 | 8 | # --- Safety net 9 | dist/ 10 | _* 11 | !src/_tests 12 | __pycache__/ 13 | *.parquet 14 | *.map 15 | *.zip 16 | *.gz 17 | *.tar 18 | *.tgz 19 | 20 | # videos 21 | *.mov 22 | *.mp4 23 | 24 | # images 25 | *.icns 26 | *.ico 27 | *.jpeg 28 | *.jpg 29 | *.png -------------------------------------------------------------------------------- /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 = "addr2line" 7 | version = "0.20.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.0.2" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "anstream" 31 | version = "0.3.2" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" 34 | dependencies = [ 35 | "anstyle", 36 | "anstyle-parse", 37 | "anstyle-query", 38 | "anstyle-wincon", 39 | "colorchoice", 40 | "is-terminal", 41 | "utf8parse", 42 | ] 43 | 44 | [[package]] 45 | name = "anstyle" 46 | version = "1.0.1" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" 49 | 50 | [[package]] 51 | name = "anstyle-parse" 52 | version = "0.2.1" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" 55 | dependencies = [ 56 | "utf8parse", 57 | ] 58 | 59 | [[package]] 60 | name = "anstyle-query" 61 | version = "1.0.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" 64 | dependencies = [ 65 | "windows-sys", 66 | ] 67 | 68 | [[package]] 69 | name = "anstyle-wincon" 70 | version = "1.0.1" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" 73 | dependencies = [ 74 | "anstyle", 75 | "windows-sys", 76 | ] 77 | 78 | [[package]] 79 | name = "anyhow" 80 | version = "1.0.71" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" 83 | 84 | [[package]] 85 | name = "autocfg" 86 | version = "1.1.0" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 89 | 90 | [[package]] 91 | name = "awesome-app" 92 | version = "0.1.5" 93 | dependencies = [ 94 | "aho-corasick", 95 | "anyhow", 96 | "clap", 97 | "regex", 98 | "serde", 99 | "serde_derive", 100 | "sysinfo", 101 | "thiserror", 102 | "tokio", 103 | "toml", 104 | ] 105 | 106 | [[package]] 107 | name = "backtrace" 108 | version = "0.3.68" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" 111 | dependencies = [ 112 | "addr2line", 113 | "cc", 114 | "cfg-if", 115 | "libc", 116 | "miniz_oxide", 117 | "object", 118 | "rustc-demangle", 119 | ] 120 | 121 | [[package]] 122 | name = "bitflags" 123 | version = "1.3.2" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 126 | 127 | [[package]] 128 | name = "bitflags" 129 | version = "2.3.3" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" 132 | 133 | [[package]] 134 | name = "bytes" 135 | version = "1.4.0" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" 138 | 139 | [[package]] 140 | name = "cc" 141 | version = "1.0.79" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 144 | 145 | [[package]] 146 | name = "cfg-if" 147 | version = "1.0.0" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 150 | 151 | [[package]] 152 | name = "clap" 153 | version = "4.3.10" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "384e169cc618c613d5e3ca6404dda77a8685a63e08660dcc64abaf7da7cb0c7a" 156 | dependencies = [ 157 | "clap_builder", 158 | ] 159 | 160 | [[package]] 161 | name = "clap_builder" 162 | version = "4.3.10" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "ef137bbe35aab78bdb468ccfba75a5f4d8321ae011d34063770780545176af2d" 165 | dependencies = [ 166 | "anstream", 167 | "anstyle", 168 | "clap_lex", 169 | "once_cell", 170 | "strsim", 171 | ] 172 | 173 | [[package]] 174 | name = "clap_lex" 175 | version = "0.5.0" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" 178 | 179 | [[package]] 180 | name = "colorchoice" 181 | version = "1.0.0" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 184 | 185 | [[package]] 186 | name = "core-foundation-sys" 187 | version = "0.8.4" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" 190 | 191 | [[package]] 192 | name = "crossbeam-channel" 193 | version = "0.5.8" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" 196 | dependencies = [ 197 | "cfg-if", 198 | "crossbeam-utils", 199 | ] 200 | 201 | [[package]] 202 | name = "crossbeam-deque" 203 | version = "0.8.3" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" 206 | dependencies = [ 207 | "cfg-if", 208 | "crossbeam-epoch", 209 | "crossbeam-utils", 210 | ] 211 | 212 | [[package]] 213 | name = "crossbeam-epoch" 214 | version = "0.9.15" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" 217 | dependencies = [ 218 | "autocfg", 219 | "cfg-if", 220 | "crossbeam-utils", 221 | "memoffset", 222 | "scopeguard", 223 | ] 224 | 225 | [[package]] 226 | name = "crossbeam-utils" 227 | version = "0.8.16" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" 230 | dependencies = [ 231 | "cfg-if", 232 | ] 233 | 234 | [[package]] 235 | name = "either" 236 | version = "1.8.1" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" 239 | 240 | [[package]] 241 | name = "equivalent" 242 | version = "1.0.0" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" 245 | 246 | [[package]] 247 | name = "errno" 248 | version = "0.3.1" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" 251 | dependencies = [ 252 | "errno-dragonfly", 253 | "libc", 254 | "windows-sys", 255 | ] 256 | 257 | [[package]] 258 | name = "errno-dragonfly" 259 | version = "0.1.2" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 262 | dependencies = [ 263 | "cc", 264 | "libc", 265 | ] 266 | 267 | [[package]] 268 | name = "gimli" 269 | version = "0.27.3" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" 272 | 273 | [[package]] 274 | name = "hashbrown" 275 | version = "0.14.0" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" 278 | 279 | [[package]] 280 | name = "hermit-abi" 281 | version = "0.3.1" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" 284 | 285 | [[package]] 286 | name = "indexmap" 287 | version = "2.0.0" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" 290 | dependencies = [ 291 | "equivalent", 292 | "hashbrown", 293 | ] 294 | 295 | [[package]] 296 | name = "is-terminal" 297 | version = "0.4.8" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "24fddda5af7e54bf7da53067d6e802dbcc381d0a8eef629df528e3ebf68755cb" 300 | dependencies = [ 301 | "hermit-abi", 302 | "rustix", 303 | "windows-sys", 304 | ] 305 | 306 | [[package]] 307 | name = "libc" 308 | version = "0.2.147" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" 311 | 312 | [[package]] 313 | name = "linux-raw-sys" 314 | version = "0.4.3" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" 317 | 318 | [[package]] 319 | name = "lock_api" 320 | version = "0.4.10" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" 323 | dependencies = [ 324 | "autocfg", 325 | "scopeguard", 326 | ] 327 | 328 | [[package]] 329 | name = "memchr" 330 | version = "2.5.0" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 333 | 334 | [[package]] 335 | name = "memoffset" 336 | version = "0.9.0" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" 339 | dependencies = [ 340 | "autocfg", 341 | ] 342 | 343 | [[package]] 344 | name = "miniz_oxide" 345 | version = "0.7.1" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" 348 | dependencies = [ 349 | "adler", 350 | ] 351 | 352 | [[package]] 353 | name = "mio" 354 | version = "0.8.8" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" 357 | dependencies = [ 358 | "libc", 359 | "wasi", 360 | "windows-sys", 361 | ] 362 | 363 | [[package]] 364 | name = "ntapi" 365 | version = "0.4.1" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" 368 | dependencies = [ 369 | "winapi", 370 | ] 371 | 372 | [[package]] 373 | name = "num_cpus" 374 | version = "1.16.0" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 377 | dependencies = [ 378 | "hermit-abi", 379 | "libc", 380 | ] 381 | 382 | [[package]] 383 | name = "object" 384 | version = "0.31.1" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" 387 | dependencies = [ 388 | "memchr", 389 | ] 390 | 391 | [[package]] 392 | name = "once_cell" 393 | version = "1.18.0" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" 396 | 397 | [[package]] 398 | name = "parking_lot" 399 | version = "0.12.1" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 402 | dependencies = [ 403 | "lock_api", 404 | "parking_lot_core", 405 | ] 406 | 407 | [[package]] 408 | name = "parking_lot_core" 409 | version = "0.9.8" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" 412 | dependencies = [ 413 | "cfg-if", 414 | "libc", 415 | "redox_syscall", 416 | "smallvec", 417 | "windows-targets", 418 | ] 419 | 420 | [[package]] 421 | name = "pin-project-lite" 422 | version = "0.2.10" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" 425 | 426 | [[package]] 427 | name = "proc-macro2" 428 | version = "1.0.63" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" 431 | dependencies = [ 432 | "unicode-ident", 433 | ] 434 | 435 | [[package]] 436 | name = "quote" 437 | version = "1.0.29" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" 440 | dependencies = [ 441 | "proc-macro2", 442 | ] 443 | 444 | [[package]] 445 | name = "rayon" 446 | version = "1.7.0" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" 449 | dependencies = [ 450 | "either", 451 | "rayon-core", 452 | ] 453 | 454 | [[package]] 455 | name = "rayon-core" 456 | version = "1.11.0" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" 459 | dependencies = [ 460 | "crossbeam-channel", 461 | "crossbeam-deque", 462 | "crossbeam-utils", 463 | "num_cpus", 464 | ] 465 | 466 | [[package]] 467 | name = "redox_syscall" 468 | version = "0.3.5" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" 471 | dependencies = [ 472 | "bitflags 1.3.2", 473 | ] 474 | 475 | [[package]] 476 | name = "regex" 477 | version = "1.8.4" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" 480 | dependencies = [ 481 | "aho-corasick", 482 | "memchr", 483 | "regex-syntax", 484 | ] 485 | 486 | [[package]] 487 | name = "regex-syntax" 488 | version = "0.7.2" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" 491 | 492 | [[package]] 493 | name = "rustc-demangle" 494 | version = "0.1.23" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 497 | 498 | [[package]] 499 | name = "rustix" 500 | version = "0.38.2" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "aabcb0461ebd01d6b79945797c27f8529082226cb630a9865a71870ff63532a4" 503 | dependencies = [ 504 | "bitflags 2.3.3", 505 | "errno", 506 | "libc", 507 | "linux-raw-sys", 508 | "windows-sys", 509 | ] 510 | 511 | [[package]] 512 | name = "scopeguard" 513 | version = "1.1.0" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 516 | 517 | [[package]] 518 | name = "serde" 519 | version = "1.0.164" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" 522 | 523 | [[package]] 524 | name = "serde_derive" 525 | version = "1.0.164" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" 528 | dependencies = [ 529 | "proc-macro2", 530 | "quote", 531 | "syn", 532 | ] 533 | 534 | [[package]] 535 | name = "serde_spanned" 536 | version = "0.6.3" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" 539 | dependencies = [ 540 | "serde", 541 | ] 542 | 543 | [[package]] 544 | name = "signal-hook-registry" 545 | version = "1.4.1" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" 548 | dependencies = [ 549 | "libc", 550 | ] 551 | 552 | [[package]] 553 | name = "smallvec" 554 | version = "1.10.0" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 557 | 558 | [[package]] 559 | name = "socket2" 560 | version = "0.4.9" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" 563 | dependencies = [ 564 | "libc", 565 | "winapi", 566 | ] 567 | 568 | [[package]] 569 | name = "strsim" 570 | version = "0.10.0" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 573 | 574 | [[package]] 575 | name = "syn" 576 | version = "2.0.23" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" 579 | dependencies = [ 580 | "proc-macro2", 581 | "quote", 582 | "unicode-ident", 583 | ] 584 | 585 | [[package]] 586 | name = "sysinfo" 587 | version = "0.29.3" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "5bcd0346f90b6bc83526c7b180039a8acd26a5c848cc556d457f6472eb148122" 590 | dependencies = [ 591 | "cfg-if", 592 | "core-foundation-sys", 593 | "libc", 594 | "ntapi", 595 | "once_cell", 596 | "rayon", 597 | "winapi", 598 | ] 599 | 600 | [[package]] 601 | name = "thiserror" 602 | version = "1.0.40" 603 | source = "registry+https://github.com/rust-lang/crates.io-index" 604 | checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" 605 | dependencies = [ 606 | "thiserror-impl", 607 | ] 608 | 609 | [[package]] 610 | name = "thiserror-impl" 611 | version = "1.0.40" 612 | source = "registry+https://github.com/rust-lang/crates.io-index" 613 | checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" 614 | dependencies = [ 615 | "proc-macro2", 616 | "quote", 617 | "syn", 618 | ] 619 | 620 | [[package]] 621 | name = "tokio" 622 | version = "1.29.1" 623 | source = "registry+https://github.com/rust-lang/crates.io-index" 624 | checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" 625 | dependencies = [ 626 | "autocfg", 627 | "backtrace", 628 | "bytes", 629 | "libc", 630 | "mio", 631 | "num_cpus", 632 | "parking_lot", 633 | "pin-project-lite", 634 | "signal-hook-registry", 635 | "socket2", 636 | "tokio-macros", 637 | "windows-sys", 638 | ] 639 | 640 | [[package]] 641 | name = "tokio-macros" 642 | version = "2.1.0" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" 645 | dependencies = [ 646 | "proc-macro2", 647 | "quote", 648 | "syn", 649 | ] 650 | 651 | [[package]] 652 | name = "toml" 653 | version = "0.7.5" 654 | source = "registry+https://github.com/rust-lang/crates.io-index" 655 | checksum = "1ebafdf5ad1220cb59e7d17cf4d2c72015297b75b19a10472f99b89225089240" 656 | dependencies = [ 657 | "serde", 658 | "serde_spanned", 659 | "toml_datetime", 660 | "toml_edit", 661 | ] 662 | 663 | [[package]] 664 | name = "toml_datetime" 665 | version = "0.6.3" 666 | source = "registry+https://github.com/rust-lang/crates.io-index" 667 | checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" 668 | dependencies = [ 669 | "serde", 670 | ] 671 | 672 | [[package]] 673 | name = "toml_edit" 674 | version = "0.19.11" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7" 677 | dependencies = [ 678 | "indexmap", 679 | "serde", 680 | "serde_spanned", 681 | "toml_datetime", 682 | "winnow", 683 | ] 684 | 685 | [[package]] 686 | name = "unicode-ident" 687 | version = "1.0.9" 688 | source = "registry+https://github.com/rust-lang/crates.io-index" 689 | checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" 690 | 691 | [[package]] 692 | name = "utf8parse" 693 | version = "0.2.1" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 696 | 697 | [[package]] 698 | name = "wasi" 699 | version = "0.11.0+wasi-snapshot-preview1" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 702 | 703 | [[package]] 704 | name = "winapi" 705 | version = "0.3.9" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 708 | dependencies = [ 709 | "winapi-i686-pc-windows-gnu", 710 | "winapi-x86_64-pc-windows-gnu", 711 | ] 712 | 713 | [[package]] 714 | name = "winapi-i686-pc-windows-gnu" 715 | version = "0.4.0" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 718 | 719 | [[package]] 720 | name = "winapi-x86_64-pc-windows-gnu" 721 | version = "0.4.0" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 724 | 725 | [[package]] 726 | name = "windows-sys" 727 | version = "0.48.0" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 730 | dependencies = [ 731 | "windows-targets", 732 | ] 733 | 734 | [[package]] 735 | name = "windows-targets" 736 | version = "0.48.1" 737 | source = "registry+https://github.com/rust-lang/crates.io-index" 738 | checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" 739 | dependencies = [ 740 | "windows_aarch64_gnullvm", 741 | "windows_aarch64_msvc", 742 | "windows_i686_gnu", 743 | "windows_i686_msvc", 744 | "windows_x86_64_gnu", 745 | "windows_x86_64_gnullvm", 746 | "windows_x86_64_msvc", 747 | ] 748 | 749 | [[package]] 750 | name = "windows_aarch64_gnullvm" 751 | version = "0.48.0" 752 | source = "registry+https://github.com/rust-lang/crates.io-index" 753 | checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" 754 | 755 | [[package]] 756 | name = "windows_aarch64_msvc" 757 | version = "0.48.0" 758 | source = "registry+https://github.com/rust-lang/crates.io-index" 759 | checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" 760 | 761 | [[package]] 762 | name = "windows_i686_gnu" 763 | version = "0.48.0" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" 766 | 767 | [[package]] 768 | name = "windows_i686_msvc" 769 | version = "0.48.0" 770 | source = "registry+https://github.com/rust-lang/crates.io-index" 771 | checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" 772 | 773 | [[package]] 774 | name = "windows_x86_64_gnu" 775 | version = "0.48.0" 776 | source = "registry+https://github.com/rust-lang/crates.io-index" 777 | checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" 778 | 779 | [[package]] 780 | name = "windows_x86_64_gnullvm" 781 | version = "0.48.0" 782 | source = "registry+https://github.com/rust-lang/crates.io-index" 783 | checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" 784 | 785 | [[package]] 786 | name = "windows_x86_64_msvc" 787 | version = "0.48.0" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" 790 | 791 | [[package]] 792 | name = "winnow" 793 | version = "0.4.7" 794 | source = "registry+https://github.com/rust-lang/crates.io-index" 795 | checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" 796 | dependencies = [ 797 | "memchr", 798 | ] 799 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "awesome-app" 3 | version = "0.1.5" 4 | authors = ["Jeremy Chone "] 5 | license = "MIT OR Apache-2.0" 6 | description = "Rust CLI to create Awesome Desktop And Cloud Applications with Rust." 7 | categories = ["gui", "web-programming", "command-line-utilities", "config"] 8 | edition = "2021" 9 | homepage = "https://awesomeapp.dev" 10 | repository = "https://github.com/awesomeapp-dev/" 11 | 12 | [dependencies] 13 | tokio = { version = "1", features = ["full"] } 14 | clap = {version = "4", features = ["cargo"]} 15 | thiserror = "1" 16 | aho-corasick = "1" 17 | regex = "1.6" 18 | toml = "0.7" 19 | serde = "1" 20 | serde_derive = "1" 21 | sysinfo = "0.29" 22 | 23 | [dev-dependencies] 24 | anyhow = "1" 25 | 26 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2022 Jeremy Chone 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Jeremy Chone 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Rust CLI to create Awesome Applications with Rust. 2 | 3 | More info at [awesomeapp.dev](https://awesomeapp.dev) 4 | 5 | Install 6 | 7 | ```sh 8 | > cargo install awesome-app 9 | ``` 10 | 11 | Create your first app: 12 | 13 | ```sh 14 | # Create your application 15 | > awesome-app new my-app 16 | 17 | # Go to created folder 18 | > cd my-app 19 | 20 | # Start the build and hot-reload dev 21 | > awesome-app dev 22 | 23 | # Once done, open `my-app/` in your Coding Environment (e.g., VSCode) 24 | 25 | # Happy Coding! 26 | ``` 27 | 28 |
29 | [This repo on GitHub](https://github.com/awesomeapp-dev/awesome-app-cli) -------------------------------------------------------------------------------- /src/_tests/tests_config.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Context; 2 | 3 | use super::Config; 4 | use crate::config::AWESOME_TMPL; 5 | 6 | #[test] 7 | fn test_config_parsing() -> anyhow::Result<()> { 8 | // --- Exec 9 | let config = toml::from_str::(AWESOME_TMPL)?; 10 | 11 | let dev_runners = config.dev.and_then(|v| v.runners).context("Should have dev runners.")?; 12 | 13 | // --- Checks 14 | // Number of dev runners. 15 | assert_eq!(dev_runners.len(), 7, "Number of runners."); 16 | 17 | // Second runner. 18 | let runner = dev_runners.get(1).unwrap(); // Should be the 'tauri_icons'. 19 | assert_eq!(runner.name, "tauri_icons"); 20 | 21 | Ok(()) 22 | } 23 | -------------------------------------------------------------------------------- /src/_tests/tests_run_new.rs: -------------------------------------------------------------------------------- 1 | use crate::run_new::clear_awesome_toml_from_gitignore; 2 | 3 | const TEST_GITIGNORE: &str = " 4 | # --- tauri dist build assets 5 | dist/js/ 6 | dist/css/ 7 | 8 | # --- Awesome.toml 9 | # From the template app code, Awesome.toml is ignored as this should be generated 10 | # by 'awesome-app new` or 'awesome-app dev' if not present. 11 | # 12 | # However, in full application code, this could be committed (and line below commented), as it could be changed per project. 13 | Awesome.toml 14 | 15 | # --- No png except in dist or the app-icon. 16 | # For generating the app icons, npm run tauri icon src-tauri/icons/app-icon.png) 17 | *.png 18 | "; 19 | 20 | #[test] 21 | fn test_gitignore_remove_awesome() -> anyhow::Result<()> { 22 | let result = clear_awesome_toml_from_gitignore(TEST_GITIGNORE); 23 | 24 | assert!(!result.contains("Awesome.toml"), "should not container Awesome.toml"); 25 | 26 | Ok(()) 27 | } 28 | -------------------------------------------------------------------------------- /src/app_cmd.rs: -------------------------------------------------------------------------------- 1 | use clap::{crate_version, Arg, Command}; 2 | 3 | pub const VERSION: &str = crate_version!(); 4 | 5 | pub fn app_cmd() -> Command { 6 | Command::new("awesome-app") 7 | .version(VERSION) 8 | .about("Awesome Desktop App Scaffolder") 9 | .subcommand(sub_new()) 10 | .subcommand(sub_dev()) 11 | } 12 | 13 | fn sub_new() -> Command { 14 | Command::new("new") 15 | .about("Build new tauri app from template base") 16 | .arg(Arg::new("name").help("App/project name (no space)")) 17 | } 18 | 19 | fn sub_dev() -> Command { 20 | Command::new("dev").about("Starts hot-reload developement") 21 | } 22 | -------------------------------------------------------------------------------- /src/config/mod.rs: -------------------------------------------------------------------------------- 1 | //! This is the module Awesome.toml 2 | use crate::exec::prompt; 3 | use crate::prelude::*; 4 | use crate::VERSION; 5 | use serde_derive::Deserialize; 6 | use std::fs; 7 | use std::path::Path; 8 | 9 | mod runner; 10 | 11 | // --- re-exports 12 | pub use runner::*; // Wide for now. 13 | 14 | // --- Consts 15 | const AWESOME_TMPL: &str = include_str!("../../tmpl/Awesome.toml"); 16 | const AWESOME_FILE_NAME: &str = "Awesome.toml"; 17 | const SRC_TAURI_DIR: &str = "src-tauri"; 18 | const PACKAGE_JSON_FILE: &str = "package.json"; 19 | 20 | // --- Config Types 21 | #[derive(Debug, Deserialize)] 22 | pub struct Config { 23 | pub cli_version: Option, 24 | pub dev: Option, 25 | } 26 | 27 | #[derive(Debug, Deserialize)] 28 | pub struct Dev { 29 | pub runners: Option>, 30 | } 31 | 32 | // --- Awesome.toml generator / parser 33 | 34 | pub fn ensure_awesome_toml(root_dir: &Path) -> Result { 35 | // --- Check that we run in the root folder. 36 | if !root_dir.join(SRC_TAURI_DIR).is_dir() && !root_dir.join(PACKAGE_JSON_FILE).is_file() { 37 | return Err(Error::DirNotValid); 38 | } 39 | 40 | // --- Create if not present. 41 | let awesome_file = root_dir.join(AWESOME_FILE_NAME); 42 | if !awesome_file.is_file() { 43 | create_awesome_toml(&awesome_file)?; 44 | } 45 | 46 | // --- Load and validate. 47 | let toml_str = fs::read_to_string(&awesome_file)?; 48 | parse_awesome_toml(&toml_str).or_else(|_| { 49 | let res = prompt( 50 | "Awesome.toml needs to be udpated (probably an old format).\nDo you want to udpate the Awesome.toml Y/N (Y)? ", 51 | Some("y"), 52 | )?; 53 | 54 | if res == "Y" || res == "y" { 55 | // Backup the old file. 56 | let ver = VERSION.replace('.', "-"); 57 | let backup_file_name = f!(".Awesome-backup-before-{ver}.toml"); 58 | let old_file = root_dir.join(&backup_file_name); 59 | fs::copy(&awesome_file, old_file)?; 60 | println!("Backup not working Awesome.toml as '{backup_file_name}'"); 61 | 62 | // Create the new Awesome.toml 63 | println!("Creating new Awesome.toml"); 64 | create_awesome_toml(&awesome_file)?; 65 | let toml_str = fs::read_to_string(&awesome_file)?; 66 | 67 | // Parse and return. 68 | parse_awesome_toml(&toml_str) 69 | } else { 70 | Err(Error::AwesomeTomlInvalid) 71 | } 72 | }) 73 | } 74 | 75 | fn create_awesome_toml(awesome_file: &Path) -> Result<()> { 76 | let mut content = String::new(); 77 | content.push_str(&format!( 78 | r#"# Generated by 'awesome-app' command line. 79 | awesome_app_version = "{VERSION}" 80 | 81 | "# 82 | )); 83 | content.push_str(AWESOME_TMPL); 84 | 85 | fs::write(awesome_file, &content)?; 86 | 87 | Ok(()) 88 | } 89 | 90 | fn parse_awesome_toml(toml_str: &str) -> Result { 91 | let config = toml::from_str::(toml_str).map_err(Error::FailParsingConfig)?; 92 | 93 | let number_of_dev_runners = config 94 | .dev 95 | .as_ref() 96 | .and_then(|d| d.runners.as_ref()) 97 | .map(|rs| rs.len()) 98 | .unwrap_or_default(); 99 | 100 | if number_of_dev_runners == 0 { 101 | Err(Error::AwesomeTomlInvalid) 102 | } else { 103 | Ok(config) 104 | } 105 | } 106 | 107 | // region: --- Tests 108 | #[cfg(test)] 109 | #[path = "../_tests/tests_config.rs"] 110 | mod tests; 111 | // endregion: --- Tests 112 | -------------------------------------------------------------------------------- /src/config/runner.rs: -------------------------------------------------------------------------------- 1 | use crate::exec::spawn_and_wait; 2 | use crate::prelude::*; 3 | use serde_derive::Deserialize; 4 | use std::path::Path; 5 | use std::time::Duration; 6 | use tokio::process::{Child, Command}; 7 | use tokio::time::sleep; 8 | 9 | #[derive(Debug, Deserialize)] 10 | pub struct Runner { 11 | pub name: String, 12 | 13 | pub working_dir: Option, 14 | pub cmd: String, 15 | pub args: Option>, 16 | 17 | pub when: Option, 18 | 19 | #[serde(default)] 20 | pub wait_before: u64, 21 | 22 | #[serde(default)] 23 | pub concurrent: bool, 24 | 25 | #[serde(default)] 26 | pub end_all_on_exit: bool, 27 | } 28 | 29 | #[derive(Debug, Deserialize)] 30 | pub struct When { 31 | no_file_at: Option, 32 | } 33 | 34 | // region: --- Runner Impl 35 | #[cfg(target_os = "windows")] 36 | const NPM_CMD: &str = "npm.cmd"; 37 | #[cfg(not(target_os = "windows"))] 38 | const NPM_CMD: &str = "npm"; 39 | 40 | impl Runner { 41 | pub async fn exec(&self) -> Result> { 42 | // --- Process the wait_before. 43 | if self.wait_before > 0 { 44 | println!( 45 | "Waiting {}ms (from runner {}.wait_before property)", 46 | self.wait_before, self.name 47 | ); 48 | sleep(Duration::from_millis(self.wait_before)).await; 49 | } 50 | 51 | // --- Compute the cmd name. 52 | // Note: Special handling of "npm" which on Windows must be called as "npm.cmd" 53 | // TODO: Needs to generalize this. Could be more downstream, on ProgramNotFound error. 54 | let cmd_str: &str = self.cmd.as_ref(); 55 | let cmd_str = if cmd_str.starts_with("npm") && cmd_str != NPM_CMD { 56 | NPM_CMD 57 | } else { 58 | cmd_str 59 | }; 60 | 61 | // --- Compute the cmd args and working dir 62 | let args: Vec<&str> = W(&self.args).into(); 63 | let cwd = self.working_dir.as_ref().map(Path::new); 64 | 65 | // --- Execute the command 66 | if !self.concurrent { 67 | spawn_and_wait(cwd, cmd_str, args.as_slice(), true)?; 68 | Ok(None) 69 | } 70 | // start the concurrent mode and add it in the concurrent watch list. 71 | else { 72 | let mut cmd = Command::new(cmd_str); 73 | cmd.args(args); 74 | let child = cmd.spawn()?; 75 | Ok(Some(child)) 76 | } 77 | } 78 | 79 | pub fn should_run(&self, root_dir: &Path) -> Result { 80 | let no_file_at = self.when.as_ref().and_then(|w| w.no_file_at.as_ref()); 81 | 82 | if let Some(no_file_at) = no_file_at { 83 | let no_file = root_dir.join(no_file_at); 84 | if Path::exists(&no_file) { 85 | return Ok(ShouldRun::No(f!("Path '{no_file_at}' found."))); 86 | } 87 | } 88 | 89 | Ok(ShouldRun::Yes) 90 | } 91 | } 92 | 93 | pub enum ShouldRun { 94 | Yes, 95 | No(String), // reason 96 | } 97 | // endregion: --- Runner Impl 98 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::process::ExitStatus; 2 | 3 | #[derive(thiserror::Error, Debug)] 4 | pub enum Error { 5 | #[error("This directory does not seem to be a awesome-app directory. Make sure to run awesome-app from the root project directory.")] 6 | DirNotValid, 7 | 8 | #[error("Fail to execute {0} cause: {1}")] 9 | Exec(String, String), 10 | 11 | #[error("Path not safe to delete {0}")] 12 | PathNotSafeToDelete(String), 13 | 14 | #[error("Directory {0} already exist. Cancelling.")] 15 | DirAlreadyExist(String), 16 | 17 | #[error("git command line not found. Required for awesome-app.")] 18 | GitNotPresent, 19 | 20 | #[error("Fail to parse Awesome.toml. Cause: {0}")] 21 | FailParsingConfig(toml::de::Error), 22 | 23 | #[error("Awesome.toml does not seem to be valid.")] 24 | AwesomeTomlInvalid, 25 | 26 | #[error("Fail to read line")] 27 | StdinFailToReadLine, 28 | 29 | #[error(transparent)] 30 | IOError(#[from] std::io::Error), 31 | } 32 | 33 | type ExecWithExitStatus<'a> = (&'a str, &'a [&'a str], ExitStatus); 34 | 35 | impl<'a> From> for Error { 36 | fn from(val: ExecWithExitStatus) -> Self { 37 | Error::Exec(val.0.to_string(), "".to_string()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/exec.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use std::io::{self, stdin, Write}; 3 | use std::path::Path; 4 | use std::process::{Command, Stdio}; 5 | 6 | pub fn prompt(message: &str, default: Option<&str>) -> Result { 7 | print!("{message}"); 8 | let _ = io::stdout().flush(); 9 | 10 | let mut buf = String::new(); 11 | stdin().read_line(&mut buf).map_err(|_| Error::StdinFailToReadLine)?; 12 | 13 | let val = buf.trim(); 14 | 15 | let val = match (val.is_empty(), default) { 16 | (true, Some(default)) => default, 17 | (false, _) => val, 18 | (true, None) => val, // return the empty string (TODO: might want to return error) 19 | }; 20 | 21 | Ok(val.to_string()) 22 | } 23 | 24 | pub fn spawn_and_wait(cwd: Option<&Path>, cmd_str: &str, args: &[&str], print_exec: bool) -> Result<()> { 25 | let mut cmd = build_cmd(cwd, cmd_str, args); 26 | 27 | if print_exec { 28 | println!("> executing: {} {}", cmd_str, args.join(" ")); 29 | } 30 | 31 | match cmd.spawn()?.wait() { 32 | Ok(status) => { 33 | if !status.success() { 34 | Err((cmd_str, args, status).into()) 35 | } else { 36 | Ok(()) 37 | } 38 | } 39 | Err(ex) => Err(ex.into()), 40 | } 41 | } 42 | 43 | pub fn build_cmd(cwd: Option<&Path>, cmd: &str, args: &[&str]) -> Command { 44 | let mut cmd = Command::new(cmd); 45 | if let Some(cwd) = cwd { 46 | cmd.current_dir(cwd); 47 | } 48 | cmd.args(args); 49 | cmd 50 | } 51 | 52 | pub fn spawn_output(cwd: Option<&Path>, cmd_str: &str, args: &[&str], print_exec: bool) -> Result { 53 | if print_exec { 54 | println!("> executing: {} {}", cmd_str, args.join(" ")); 55 | } 56 | let mut cmd = build_cmd(cwd, cmd_str, args); 57 | 58 | match cmd.stdout(Stdio::piped()).output() { 59 | Err(ex) => Err(ex.into()), 60 | Ok(output) => { 61 | let txt = if output.status.success() { 62 | String::from_utf8(output.stdout) 63 | } else { 64 | String::from_utf8(output.stderr) 65 | }; 66 | 67 | match txt { 68 | Err(ex) => Err(Error::Exec(s!(cmd_str), f!("{ex:?}"))), 69 | Ok(txt) => Ok(txt), 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // #![allow(unused)] 2 | use crate::prelude::*; 3 | use app_cmd::app_cmd; 4 | use run_dev::run_dev; 5 | use run_new::run_new; 6 | 7 | mod app_cmd; 8 | mod config; 9 | mod error; 10 | mod exec; 11 | mod prelude; 12 | mod run_dev; 13 | mod run_new; 14 | mod utils; 15 | 16 | pub use app_cmd::VERSION; 17 | 18 | fn main() { 19 | match cmd_run() { 20 | Ok(_) => (), 21 | Err(err) => println!("FAIL - {err}"), 22 | } 23 | } 24 | 25 | fn cmd_run() -> Result<()> { 26 | let app = app_cmd().get_matches(); 27 | 28 | match app.subcommand() { 29 | Some(("new", sub_cmd)) => run_new(sub_cmd)?, 30 | Some(("dev", sub_cmd)) => run_dev(sub_cmd)?, 31 | _ => { 32 | // needs cmd_app version as the orginal got consumed by get_matches 33 | app_cmd().print_long_help()?; 34 | println!("\n"); 35 | } 36 | } 37 | 38 | Ok(()) 39 | } 40 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::error::Error; 2 | pub use std::format as f; 3 | 4 | pub type Result = std::result::Result; 5 | 6 | // Generic Wrapper struct for newtype pattern, mostly for external type to type From/TryFrom conversions 7 | pub struct W(pub T); 8 | 9 | macro_rules! s { 10 | () => { 11 | String::new() 12 | }; 13 | ($x:expr $(,)?) => { 14 | ToString::to_string(&$x) 15 | }; 16 | } 17 | 18 | pub(crate) use s; 19 | -------------------------------------------------------------------------------- /src/run_dev.rs: -------------------------------------------------------------------------------- 1 | use crate::config::{ensure_awesome_toml, ShouldRun}; 2 | use crate::prelude::*; 3 | use clap::ArgMatches; 4 | use std::collections::HashMap; 5 | use std::path::Path; 6 | use std::time::Duration; 7 | use sysinfo::{Pid, PidExt, Process, ProcessExt, ProcessRefreshKind, System, SystemExt}; 8 | use tokio::process::Child; 9 | use tokio::time::sleep; 10 | 11 | const WATCH_CHILD_DELAY: u64 = 3000; // in ms 12 | 13 | #[tokio::main] 14 | pub async fn run_dev(_sub_cmd: &ArgMatches) -> Result<()> { 15 | // TODO: needs to get it from the params. 16 | let root_dir = Path::new("."); 17 | 18 | // Read or create/read the Awesome.toml config with the dev runners. 19 | let config = ensure_awesome_toml(root_dir)?; 20 | 21 | // Vec to keep track of the concurrent processes. 22 | struct RunnerConcurrentSpawn { 23 | name: String, 24 | child: Child, 25 | end_all_on_exit: bool, 26 | } 27 | let mut children_to_watch: Vec = Vec::new(); 28 | 29 | // --- Exec each runner. 30 | if let Some(runners) = config.dev.and_then(|v| v.runners) { 31 | for runner in runners.iter() { 32 | println!("==== Running runner: {}", runner.name); 33 | 34 | match runner.should_run(root_dir)? { 35 | ShouldRun::No(reason) => println!("Skip running runner '{}' because {reason}", runner.name), 36 | ShouldRun::Yes => { 37 | // exec the runner. 38 | // returns a child if process is concurrent. 39 | let child = runner.exec().await?; 40 | 41 | // if concurrent, keep an eye on this child. 42 | if let Some(child) = child { 43 | children_to_watch.push(RunnerConcurrentSpawn { 44 | name: runner.name.to_string(), 45 | child, 46 | end_all_on_exit: runner.end_all_on_exit, 47 | }); 48 | } 49 | } 50 | } 51 | } 52 | } 53 | 54 | // --- Watch processes when concurrent to end_all_on_exit when flagged. 55 | // TODO: Probably need to change that to avoid doing polling. 56 | // Strategy: Tokio Spawn for the child with mpsc for the end_all event. 57 | if !children_to_watch.is_empty() { 58 | let mut end_all = false; 59 | 60 | let mut sys = System::new(); 61 | 62 | 'main: loop { 63 | // --- Check if any children is down. 64 | for RunnerConcurrentSpawn { 65 | child, end_all_on_exit, .. 66 | } in children_to_watch.iter_mut() 67 | { 68 | let status = child.try_wait()?; 69 | if status.is_some() && *end_all_on_exit { 70 | end_all = true; 71 | } 72 | } 73 | 74 | // --- If end_all true, then, we terminate all. 75 | if end_all { 76 | for RunnerConcurrentSpawn { name, child, .. } in children_to_watch.iter_mut() { 77 | if (child.try_wait()?).is_none() { 78 | terminate_process_tree(&mut sys, name, child).await? 79 | } 80 | } 81 | break 'main; 82 | } 83 | 84 | sleep(Duration::from_millis(WATCH_CHILD_DELAY)).await; 85 | } 86 | } 87 | 88 | Ok(()) 89 | } 90 | 91 | /// Terminate this process and all of its children. 92 | async fn terminate_process_tree(sys: &mut System, name: &str, proc: &mut Child) -> Result<()> { 93 | if let Some(proc_id) = proc.id() { 94 | let proc_pid = Pid::from_u32(proc_id); 95 | 96 | // --- Fetch the children 97 | sys.refresh_processes_specifics(ProcessRefreshKind::everything().without_cpu()); 98 | let sys_processes = sys.processes(); 99 | let children = find_descendant(sys_processes, &proc_pid); 100 | 101 | // --- Terminate the parent 102 | match proc.kill().await { 103 | Ok(_) => (), 104 | Err(ex) => println!("Warning - error while stopping runner {name}. Cause: {ex}"), 105 | }; 106 | 107 | // --- Terminate the children 108 | for (pid, _) in children { 109 | if let Some(process) = sys.process(pid) { 110 | let _ = process.kill(); 111 | } 112 | } 113 | } 114 | 115 | Ok(()) 116 | } 117 | 118 | fn find_descendant(sys_processes: &HashMap, root_pid: &Pid) -> Vec<(Pid, String)> { 119 | let mut children: HashMap = HashMap::new(); 120 | 121 | // NOTE: For now, going a little brute force, but this should be exhaustive 122 | // and does not really have significant performance impact for the usecase. 123 | 'main: loop { 124 | let mut cycle_has = false; 125 | for (pid, p) in sys_processes.iter() { 126 | if let Some(parent_pid) = p.parent() { 127 | if !children.contains_key(pid) && (parent_pid == *root_pid || children.contains_key(&parent_pid)) { 128 | children.insert(*pid, p.name().to_string()); 129 | cycle_has = true; 130 | } 131 | } 132 | } 133 | // if this cycle did not find anything, we can break the search. 134 | if !cycle_has { 135 | break 'main; 136 | } 137 | } 138 | 139 | children.into_iter().collect() 140 | } 141 | -------------------------------------------------------------------------------- /src/run_new.rs: -------------------------------------------------------------------------------- 1 | use crate::config::ensure_awesome_toml; 2 | use crate::exec::{prompt, spawn_and_wait, spawn_output}; 3 | use crate::prelude::*; 4 | use crate::utils::{path_joins, safer_remove_dir_all}; 5 | use crate::Error; 6 | use aho_corasick::AhoCorasick; 7 | use clap::ArgMatches; 8 | use regex::bytes::Regex; 9 | use std::fs; 10 | use std::path::Path; 11 | use std::str::from_utf8; 12 | 13 | const DEFAULT_APP_NAME: &str = "awesome-app"; 14 | const DEFAULT_WIN_TITLE: &str = "Awesome App"; 15 | 16 | // const FILE_PACKAGE: &str = "package.json"; 17 | // const FILE_TAURI_CONF: &str = "src-tauri/Tauri.toml"; 18 | // const FILE_VAPP: &str = "src-ui/src/views/v-app.ts"; 19 | 20 | const FILES: &[&str; 3] = &["package.json", "src-tauri/Tauri.toml", "src-ui/src/view/app-v.ts"]; 21 | 22 | const GIT_DIR: &str = ".git"; 23 | 24 | const GIT_TMPL_BASE: &str = "https://github.com/rust-awesome-app/template-app-base.git"; 25 | // const GIT_TMPL_MIN: &'static str = "https://github.com/jeremychone/rust-awesome-app-template-min.git"; 26 | 27 | struct Conf<'a> { 28 | app_name: &'a str, 29 | win_title: &'a str, 30 | } 31 | 32 | pub fn run_new(sub_cmd: &ArgMatches) -> Result<()> { 33 | check_git()?; 34 | 35 | // --- Get the name 36 | let app_name = sub_cmd.get_one::("name"); 37 | 38 | let app_name = match app_name { 39 | Some(name) => name.to_string(), 40 | None => prompt( 41 | &f!("What is your app name? ({DEFAULT_APP_NAME}): "), 42 | Some(DEFAULT_APP_NAME), 43 | )?, 44 | }; 45 | 46 | // --- Get the title 47 | let app_title = prompt(&f!("What should the window title be? ({app_name}): "), Some(&app_name))?; 48 | 49 | // --- Compute the app dir 50 | let re = Regex::new(r"[^A-Za-z0-9]").unwrap(); 51 | let app_dir_name = re.replace_all(app_name.as_bytes(), "-".as_bytes()); 52 | // remove the last '-' chars 53 | let app_dir_name = Regex::new(r"[-]+$") 54 | .unwrap() 55 | .replace_all(&app_dir_name, "".as_bytes()) 56 | .into_owned(); 57 | let app_dir_name = from_utf8(&app_dir_name).unwrap().to_lowercase(); 58 | let app_dir = Path::new(&app_dir_name); 59 | 60 | // check if the dir already exist 61 | if app_dir.exists() { 62 | return Err(Error::DirAlreadyExist(s!(app_dir.to_string_lossy()))); 63 | } 64 | 65 | // --- Do the git clone 66 | println!("Cloning rust-awesome- (base app template)"); 67 | // git clone --depth 1 --branch 68 | spawn_and_wait( 69 | None, 70 | "git", 71 | &["clone", "--depth", "1", "--branch", "main", GIT_TMPL_BASE, &app_dir_name], 72 | true, 73 | )?; 74 | 75 | // --- Replace the parts 76 | replace_parts( 77 | app_dir, 78 | Conf { 79 | app_name: &app_name, 80 | win_title: &app_title, 81 | }, 82 | )?; 83 | 84 | // --- Add the Awesome.toml 85 | ensure_awesome_toml(app_dir)?; 86 | 87 | // --- Remove the git folder 88 | let git_dir = app_dir.join(GIT_DIR); 89 | println!("Delete template git directory ({})", git_dir.to_string_lossy()); 90 | safer_remove_dir_all(&git_dir)?; 91 | 92 | // --- Clear the Awesome.toml from the .gitignore 93 | let gitignore_file = app_dir.join(".gitignore"); 94 | let content = fs::read_to_string(&gitignore_file)?; 95 | let content = clear_awesome_toml_from_gitignore(&content); 96 | fs::write(&gitignore_file, content)?; 97 | 98 | // --- Do the git init and initial 99 | spawn_and_wait(Some(app_dir), "git", &["init", "."], true)?; 100 | spawn_and_wait(Some(app_dir), "git", &["add", "-A", "."], true)?; 101 | spawn_and_wait(Some(app_dir), "git", &["commit", "-a", "-m", ". initial"], true)?; 102 | 103 | println!( 104 | " 105 | Next steps: 106 | 107 | > cd {app_dir_name} 108 | 109 | > awesome-app dev 110 | 111 | First compile takes a little while, but then, hot-reload dev !!! 112 | 113 | Open the {app_dir_name} in your IDE. 114 | 115 | Happy coding! 116 | " 117 | ); 118 | 119 | Ok(()) 120 | } 121 | 122 | fn replace_parts(dir: &Path, conf: Conf) -> Result<()> { 123 | let files = FILES.iter().map(|f| path_joins(dir, f)).collect::>(); 124 | 125 | let patterns = &[DEFAULT_APP_NAME, DEFAULT_WIN_TITLE]; 126 | // TODO: unwrap safety check. 127 | let ac = AhoCorasick::new(patterns).unwrap(); 128 | let replace_by = &[conf.app_name, conf.win_title]; 129 | 130 | for file in files { 131 | let content = fs::read_to_string(&file)?; 132 | let res = ac.replace_all_bytes(content.as_bytes(), replace_by); 133 | let new_content = std::str::from_utf8(&res).unwrap(); 134 | 135 | if content != new_content { 136 | println!("File updated: '{}'", file.to_string_lossy()); 137 | fs::write(file, new_content)?; 138 | } else { 139 | println!("File skipped (nothing changed): '{}'", file.to_string_lossy()); 140 | } 141 | } 142 | 143 | Ok(()) 144 | } 145 | 146 | fn clear_awesome_toml_from_gitignore(content: &str) -> String { 147 | let rx = Regex::new(r"(?m)^# --- Awesome.toml(.|\n)*^Awesome.toml\n\n").expect("Invalid regex"); 148 | let content = rx.replace(content.as_bytes(), "".as_bytes()); 149 | let content: &str = from_utf8(&content).unwrap(); 150 | content.to_string() 151 | } 152 | 153 | // region: --- Utils 154 | 155 | fn check_git() -> Result<()> { 156 | spawn_output(None, "git", &["--version"], false).map_err(|_| Error::GitNotPresent)?; 157 | Ok(()) 158 | } 159 | // endregion: --- Utils 160 | 161 | // region: --- Tests 162 | #[cfg(test)] 163 | #[path = "./_tests/tests_run_new.rs"] 164 | mod tests; 165 | // endregion: --- Tests 166 | -------------------------------------------------------------------------------- /src/utils/froms.rs: -------------------------------------------------------------------------------- 1 | //! Various From implementations with the W() new type pattern. 2 | //! 3 | 4 | use crate::prelude::*; 5 | 6 | /// Translate a &'a Option> to a Vec<&'a str> 7 | impl<'a> From>>> for Vec<&'a str> { 8 | fn from(val: W<&'a Option>>) -> Self { 9 | val 10 | .0 11 | .as_ref() 12 | .map(|v| v.iter().map(String::as_ref).collect::>()) 13 | .unwrap_or_default() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use std::fs; 3 | use std::path::{Path, PathBuf}; 4 | 5 | mod froms; 6 | 7 | // --- re-exports 8 | 9 | // region: --- File & Path Utils 10 | const DELETE_MUST_CONTAINS_ANY_OF: &[&str; 1] = &[".git"]; 11 | 12 | /// Rudementary hardcoded way to check if a path seems to be safe to be deleted. 13 | /// Designed to be application specific. 14 | /// (far from perfect, but better than nothing) 15 | pub fn safer_remove_dir_all(dir: &Path) -> Result<()> { 16 | let dir_str = dir.to_string_lossy(); 17 | 18 | DELETE_MUST_CONTAINS_ANY_OF 19 | .iter() 20 | .find(|v| dir_str.contains(*v)) 21 | .ok_or_else(|| Error::PathNotSafeToDelete(s!(dir_str)))?; 22 | 23 | // TODO: Make error more informative 24 | fs::remove_dir_all(dir)?; 25 | Ok(()) 26 | } 27 | 28 | /// Create a new PathBuf from a root and a '/' delimited components 29 | pub fn path_joins(root: &Path, sub_path: &str) -> PathBuf { 30 | let parts = sub_path.split('/'); 31 | let mut path = root.to_owned(); 32 | for part in parts { 33 | path.push(part) 34 | } 35 | path 36 | } 37 | 38 | // endregion: --- File & Path Utils 39 | -------------------------------------------------------------------------------- /tmpl/Awesome.toml: -------------------------------------------------------------------------------- 1 | # If 'awesome-app dev' is not working with this Awesome.toml, rename or delete it, 2 | # and run again 'awesome-app dev' and this file will be generated again. 3 | 4 | # Note 1: Runners executed in order. Default is blocking except if concurrent = true. 5 | # Note 2: end_all_on_exit = true, for concurrent runners tells to end the session if this specific process ends. 6 | # Note 3: Eventually, [[runners.build]] will also be available. Splitting the two makes it less convoluted and more flexible. 7 | 8 | [[dev.runners]] 9 | name = "npm_install" 10 | cmd = "npm" 11 | args = ["install"] 12 | 13 | [[dev.runners]] 14 | name = "tauri_icons" 15 | when.no_file_at = "src-tauri/icons/32x32.png" 16 | cmd = "npm" 17 | args = ["run", "tauri", "icon", "src-tauri/icons/app-icon.png"] 18 | 19 | [[dev.runners]] 20 | name = "cargo_build" 21 | working_dir = "src-tauri/" 22 | cmd = "cargo" 23 | args = ["build"] 24 | 25 | [[dev.runners]] 26 | name = "pcss" 27 | cmd = "npm" 28 | args = ["run", "pcss", "--", "-w"] 29 | concurrent = true 30 | # if this process exit, end the dev session 31 | end_all_on_exit = true 32 | 33 | [[dev.runners]] 34 | name = "rollup" 35 | cmd = "npm" 36 | args = ["run", "rollup", "--", "-w"] 37 | concurrent = true 38 | end_all_on_exit = true 39 | 40 | [[dev.runners]] 41 | name = "localhost" 42 | cmd = "npm" 43 | args = ["run", "localhost"] 44 | concurrent = true 45 | end_all_on_exit = true 46 | 47 | [[dev.runners]] 48 | name = "tauri_dev" 49 | wait_before = 2000 # wait in ms, before getting called 50 | cmd = "npm" 51 | args = ["run", "tauri", "dev"] 52 | concurrent = true 53 | end_all_on_exit = true --------------------------------------------------------------------------------