Compare commits
10 Commits
2bacbddadb
...
f5bb82dea3
Author | SHA1 | Date | |
---|---|---|---|
|
f5bb82dea3 | ||
|
de1256f1bd | ||
|
77a6e02964 | ||
|
9d2c6b8858 | ||
|
d14aae1c29 | ||
|
db52d95146 | ||
|
b8044e789b | ||
|
a045cf0c6a | ||
|
cc731f46ea | ||
|
8678565b04 |
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
@ -32,7 +32,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Completion Scripts
|
- name: Completion Scripts
|
||||||
run: |
|
run: |
|
||||||
./bin/generate-completions
|
./bin/update-completions
|
||||||
git diff --no-ext-diff --quiet --exit-code
|
git diff --no-ext-diff --quiet --exit-code
|
||||||
./tests/completions/just.bash
|
./tests/completions/just.bash
|
||||||
|
|
||||||
|
8
.github/workflows/release.yaml
vendored
8
.github/workflows/release.yaml
vendored
@ -20,6 +20,7 @@ jobs:
|
|||||||
- armv7-unknown-linux-musleabihf
|
- armv7-unknown-linux-musleabihf
|
||||||
- x86_64-apple-darwin
|
- x86_64-apple-darwin
|
||||||
- x86_64-pc-windows-msvc
|
- x86_64-pc-windows-msvc
|
||||||
|
- aarch64-pc-windows-msvc
|
||||||
- x86_64-unknown-linux-musl
|
- x86_64-unknown-linux-musl
|
||||||
include:
|
include:
|
||||||
- target: aarch64-apple-darwin
|
- target: aarch64-apple-darwin
|
||||||
@ -39,6 +40,8 @@ jobs:
|
|||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
- target: x86_64-pc-windows-msvc
|
- target: x86_64-pc-windows-msvc
|
||||||
os: windows-latest
|
os: windows-latest
|
||||||
|
- target: aarch64-pc-windows-msvc
|
||||||
|
os: windows-latest
|
||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
- target: x86_64-unknown-linux-musl
|
- target: x86_64-unknown-linux-musl
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
@ -61,6 +64,11 @@ jobs:
|
|||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install gcc-arm-linux-gnueabihf
|
sudo apt-get install gcc-arm-linux-gnueabihf
|
||||||
|
|
||||||
|
- name: Install AArch64 Toolchain (Windows)
|
||||||
|
if: ${{ matrix.target == 'aarch64-pc-windows-msvc' }}
|
||||||
|
run: |
|
||||||
|
rustup target add aarch64-pc-windows-msvc
|
||||||
|
|
||||||
- name: Ref Type
|
- name: Ref Type
|
||||||
id: ref-type
|
id: ref-type
|
||||||
run: cargo run --package ref-type -- --reference ${{ github.ref }} >> $GITHUB_OUTPUT
|
run: cargo run --package ref-type -- --reference ${{ github.ref }} >> $GITHUB_OUTPUT
|
||||||
|
158
Cargo.lock
generated
158
Cargo.lock
generated
@ -11,6 +11,21 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android-tzdata"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android_system_properties"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ansi_term"
|
name = "ansi_term"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
@ -92,6 +107,12 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
@ -139,6 +160,12 @@ dependencies = [
|
|||||||
"regex-automata 0.1.10",
|
"regex-automata 0.1.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "camino"
|
name = "camino"
|
||||||
version = "1.1.7"
|
version = "1.1.7"
|
||||||
@ -163,6 +190,20 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.38"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
||||||
|
dependencies = [
|
||||||
|
"android-tzdata",
|
||||||
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
|
"num-traits",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-targets 0.52.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "2.34.0"
|
version = "2.34.0"
|
||||||
@ -237,6 +278,12 @@ version = "0.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
|
checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.12"
|
version = "0.2.12"
|
||||||
@ -500,6 +547,29 @@ version = "2.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone"
|
||||||
|
version = "0.1.60"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
|
||||||
|
dependencies = [
|
||||||
|
"android_system_properties",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"iana-time-zone-haiku",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone-haiku"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is_terminal_polyfill"
|
name = "is_terminal_polyfill"
|
||||||
version = "1.70.0"
|
version = "1.70.0"
|
||||||
@ -512,6 +582,15 @@ version = "1.0.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
|
||||||
|
dependencies = [
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "just"
|
name = "just"
|
||||||
version = "1.27.0"
|
version = "1.27.0"
|
||||||
@ -519,6 +598,7 @@ dependencies = [
|
|||||||
"ansi_term",
|
"ansi_term",
|
||||||
"blake3",
|
"blake3",
|
||||||
"camino",
|
"camino",
|
||||||
|
"chrono",
|
||||||
"clap 4.5.4",
|
"clap 4.5.4",
|
||||||
"clap_complete",
|
"clap_complete",
|
||||||
"clap_mangen",
|
"clap_mangen",
|
||||||
@ -623,6 +703,15 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_cpus"
|
name = "num_cpus"
|
||||||
version = "1.16.0"
|
version = "1.16.0"
|
||||||
@ -633,6 +722,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "option-ext"
|
name = "option-ext"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -1173,6 +1268,60 @@ version = "0.11.0+wasi-snapshot-preview1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.92"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.92"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.66",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.92"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.92"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.66",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.92"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "which"
|
name = "which"
|
||||||
version = "6.0.1"
|
version = "6.0.1"
|
||||||
@ -1207,6 +1356,15 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-core"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.52.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.48.0"
|
version = "0.48.0"
|
||||||
|
@ -21,6 +21,7 @@ members = [".", "crates/*"]
|
|||||||
ansi_term = "0.12.0"
|
ansi_term = "0.12.0"
|
||||||
blake3 = { version = "1.5.0", features = ["rayon", "mmap"] }
|
blake3 = { version = "1.5.0", features = ["rayon", "mmap"] }
|
||||||
camino = "1.0.4"
|
camino = "1.0.4"
|
||||||
|
chrono = "0.4.38"
|
||||||
clap = { version = "4.0.0", features = ["env", "wrap_help"] }
|
clap = { version = "4.0.0", features = ["env", "wrap_help"] }
|
||||||
clap_complete = "4.0.0"
|
clap_complete = "4.0.0"
|
||||||
clap_mangen = "0.2.20"
|
clap_mangen = "0.2.20"
|
||||||
|
145
README.md
145
README.md
@ -656,6 +656,24 @@ Available recipes:
|
|||||||
lint
|
lint
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Recipes in submodules can be listed with `just --list PATH`, where `PATH` is a
|
||||||
|
space- or `::`-separated module path:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cat justfile
|
||||||
|
mod foo
|
||||||
|
$ cat foo.just
|
||||||
|
mod bar
|
||||||
|
$ cat bar.just
|
||||||
|
baz:
|
||||||
|
$ just --unstable foo bar
|
||||||
|
Available recipes:
|
||||||
|
baz
|
||||||
|
$ just --unstable foo::bar
|
||||||
|
Available recipes:
|
||||||
|
baz
|
||||||
|
```
|
||||||
|
|
||||||
`just --summary` is more concise:
|
`just --summary` is more concise:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@ -793,7 +811,7 @@ foo:
|
|||||||
| `allow-duplicate-variables` | boolean | `false` | Allow variables appearing later in a `justfile` to override earlier variables with the same name. |
|
| `allow-duplicate-variables` | boolean | `false` | Allow variables appearing later in a `justfile` to override earlier variables with the same name. |
|
||||||
| `dotenv-filename` | string | - | Load a `.env` file with a custom name, if present. |
|
| `dotenv-filename` | string | - | Load a `.env` file with a custom name, if present. |
|
||||||
| `dotenv-load` | boolean | `false` | Load a `.env` file, if present. |
|
| `dotenv-load` | boolean | `false` | Load a `.env` file, if present. |
|
||||||
| `dotenv-path` | string | - | Load a `.env` file from a custom path, if present. Overrides `dotenv-filename`. |
|
| `dotenv-path` | string | - | Load a `.env` file from a custom path and error if not present. Overrides `dotenv-filename`. |
|
||||||
| `export` | boolean | `false` | Export all variables as environment variables. |
|
| `export` | boolean | `false` | Export all variables as environment variables. |
|
||||||
| `fallback` | boolean | `false` | Search `justfile` in parent directory if the first recipe on the command line is not found. |
|
| `fallback` | boolean | `false` | Search `justfile` in parent directory if the first recipe on the command line is not found. |
|
||||||
| `ignore-comments` | boolean | `false` | Ignore recipe lines beginning with `#`. |
|
| `ignore-comments` | boolean | `false` | Ignore recipe lines beginning with `#`. |
|
||||||
@ -862,7 +880,9 @@ bar
|
|||||||
If `dotenv-load`, `dotenv-filename` or `dotenv-path` is set, `just` will load
|
If `dotenv-load`, `dotenv-filename` or `dotenv-path` is set, `just` will load
|
||||||
environment variables from a file.
|
environment variables from a file.
|
||||||
|
|
||||||
If `dotenv-path` is set, `just` will look for a file at the given path.
|
If `dotenv-path` is set, `just` will look for a file at the given path. It is
|
||||||
|
an error if a dotenv file is not found at `dotenv-path`, but not an error if a
|
||||||
|
dotenv file is not found with `dotenv-filename`.
|
||||||
|
|
||||||
Otherwise, `just` looks for a file named `.env` by default, unless
|
Otherwise, `just` looks for a file named `.env` by default, unless
|
||||||
`dotenv-filename` set, in which case the value of `dotenv-filename` is used.
|
`dotenv-filename` set, in which case the value of `dotenv-filename` is used.
|
||||||
@ -2201,7 +2221,87 @@ foo $bar:
|
|||||||
echo $bar
|
echo $bar
|
||||||
```
|
```
|
||||||
|
|
||||||
### Running Recipes at the End of a Recipe
|
### Dependencies
|
||||||
|
|
||||||
|
Dependencies run before recipes that depend on them:
|
||||||
|
|
||||||
|
```just
|
||||||
|
a: b
|
||||||
|
@echo A
|
||||||
|
|
||||||
|
b:
|
||||||
|
@echo B
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
$ just a
|
||||||
|
B
|
||||||
|
A
|
||||||
|
```
|
||||||
|
|
||||||
|
In a given invocation of `just`, a recipe with the same arguments will only run
|
||||||
|
once, regardless of how many times it appears in the command-line invocation,
|
||||||
|
or how many times it appears as a dependency:
|
||||||
|
|
||||||
|
```just
|
||||||
|
a:
|
||||||
|
@echo A
|
||||||
|
|
||||||
|
b: a
|
||||||
|
@echo B
|
||||||
|
|
||||||
|
c: a
|
||||||
|
@echo C
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
$ just a a a a a
|
||||||
|
A
|
||||||
|
$ just b c
|
||||||
|
A
|
||||||
|
B
|
||||||
|
C
|
||||||
|
```
|
||||||
|
|
||||||
|
Multiple recipes may depend on a recipe that performs some kind of setup, and
|
||||||
|
when those recipes run, that setup will only be performed once:
|
||||||
|
|
||||||
|
```just
|
||||||
|
build:
|
||||||
|
cc main.c
|
||||||
|
|
||||||
|
test-foo: build
|
||||||
|
./a.out --test foo
|
||||||
|
|
||||||
|
test-bar: build
|
||||||
|
./a.out --test bar
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
$ just test-foo test-bar
|
||||||
|
cc main.c
|
||||||
|
./a.out --test foo
|
||||||
|
./a.out --test bar
|
||||||
|
```
|
||||||
|
|
||||||
|
Recipes in a given run are only skipped when they receive the same arguments:
|
||||||
|
|
||||||
|
```just
|
||||||
|
build:
|
||||||
|
cc main.c
|
||||||
|
|
||||||
|
test TEST: build
|
||||||
|
./a.out --test {{TEST}}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
$ just test foo test bar
|
||||||
|
cc main.c
|
||||||
|
./a.out --test foo
|
||||||
|
./a.out --test bar
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Running Recipes at the End of a Recipe
|
||||||
|
|
||||||
Normal dependencies of a recipes always run before a recipe starts. That is to
|
Normal dependencies of a recipes always run before a recipe starts. That is to
|
||||||
say, the dependee always runs before the depender. These dependencies are
|
say, the dependee always runs before the depender. These dependencies are
|
||||||
@ -2238,7 +2338,7 @@ echo 'D!'
|
|||||||
D!
|
D!
|
||||||
```
|
```
|
||||||
|
|
||||||
### Running Recipes in the Middle of a Recipe
|
#### Running Recipes in the Middle of a Recipe
|
||||||
|
|
||||||
`just` doesn't support running recipes in the middle of another recipe, but you
|
`just` doesn't support running recipes in the middle of another recipe, but you
|
||||||
can call `just` recursively in the middle of a recipe. Given the following
|
can call `just` recursively in the middle of a recipe. Given the following
|
||||||
@ -3272,6 +3372,43 @@ Since `set windows-shell` has higher precedence than `set shell`, you can use
|
|||||||
`set windows-shell` to pick a shell on Windows, and `set shell` to pick a shell
|
`set windows-shell` to pick a shell on Windows, and `set shell` to pick a shell
|
||||||
for all other platforms.
|
for all other platforms.
|
||||||
|
|
||||||
|
### Timestamps
|
||||||
|
|
||||||
|
`just` can print timestamps before each recipe commands:
|
||||||
|
|
||||||
|
```just
|
||||||
|
recipe:
|
||||||
|
echo one
|
||||||
|
sleep 2
|
||||||
|
echo two
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
$ just --timestamp recipe
|
||||||
|
[07:28:46] echo one
|
||||||
|
one
|
||||||
|
[07:28:46] sleep 2
|
||||||
|
[07:28:48] echo two
|
||||||
|
two
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, timestamps are formatted as `HH:MM:SS`. The format can be changed
|
||||||
|
with `--timestamp-format`:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ just --timestamp recipe --timestamp-format '%H:%M:%S%.3f %Z'
|
||||||
|
[07:32:11:.349 UTC] echo one
|
||||||
|
one
|
||||||
|
[07:32:11:.350 UTC] sleep 2
|
||||||
|
[07:32:13:.352 UTC] echo two
|
||||||
|
two
|
||||||
|
```
|
||||||
|
|
||||||
|
The argument to `--timestamp-format` is a `strftime`-style format string, see
|
||||||
|
the
|
||||||
|
[`chrono` library docs](https://docs.rs/chrono/latest/chrono/format/strftime/index.html)
|
||||||
|
for details.
|
||||||
|
|
||||||
Changelog
|
Changelog
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
set -euxo pipefail
|
set -euxo pipefail
|
||||||
|
|
||||||
|
cargo build
|
||||||
|
|
||||||
for script in completions/*; do
|
for script in completions/*; do
|
||||||
shell=${script##*.}
|
shell=${script##*.}
|
||||||
if [ $shell == nu ]; then
|
if [ $shell == nu ]; then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
cargo run -- --completions $shell > $script
|
./target/debug/just --completions $shell > $script
|
||||||
done
|
done
|
@ -29,8 +29,8 @@ _just() {
|
|||||||
done
|
done
|
||||||
|
|
||||||
case "${cmd}" in
|
case "${cmd}" in
|
||||||
"$1")
|
just)
|
||||||
opts="-n -f -q -u -v -d -c -e -l -s -E -g -h -V --check --chooser --color --command-color --yes --dry-run --dump-format --highlight --list-heading --list-prefix --no-aliases --no-deps --no-dotenv --no-highlight --justfile --quiet --set --shell --shell-arg --shell-command --clear-shell-args --unsorted --unstable --verbose --working-directory --changelog --choose --command --completions --dump --edit --evaluate --fmt --init --list --groups --man --show --summary --variables --dotenv-filename --dotenv-path --global-justfile --help --version [ARGUMENTS]..."
|
opts="-n -f -q -u -v -d -c -e -l -s -E -g -h -V --check --chooser --color --command-color --yes --dry-run --dump-format --highlight --list-heading --list-prefix --no-aliases --no-deps --no-dotenv --no-highlight --justfile --quiet --set --shell --shell-arg --shell-command --clear-shell-args --unsorted --unstable --verbose --working-directory --changelog --choose --command --completions --dump --edit --evaluate --fmt --init --list --groups --man --show --summary --variables --dotenv-filename --dotenv-path --global-justfile --timestamp --timestamp-format --help --version [ARGUMENTS]..."
|
||||||
if [[ ${cur} == -* ]] ; then
|
if [[ ${cur} == -* ]] ; then
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||||
return 0
|
return 0
|
||||||
@ -116,6 +116,14 @@ _just() {
|
|||||||
COMPREPLY=($(compgen -W "bash elvish fish powershell zsh" -- "${cur}"))
|
COMPREPLY=($(compgen -W "bash elvish fish powershell zsh" -- "${cur}"))
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
--list)
|
||||||
|
COMPREPLY=($(compgen -f "${cur}"))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-l)
|
||||||
|
COMPREPLY=($(compgen -f "${cur}"))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
--show)
|
--show)
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
COMPREPLY=($(compgen -f "${cur}"))
|
||||||
return 0
|
return 0
|
||||||
@ -136,6 +144,10 @@ _just() {
|
|||||||
COMPREPLY=($(compgen -f "${cur}"))
|
COMPREPLY=($(compgen -f "${cur}"))
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
--timestamp-format)
|
||||||
|
COMPREPLY=($(compgen -f "${cur}"))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
COMPREPLY=()
|
COMPREPLY=()
|
||||||
;;
|
;;
|
||||||
|
@ -33,11 +33,14 @@ set edit:completion:arg-completer[just] = {|@words|
|
|||||||
cand -c 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set'
|
cand -c 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set'
|
||||||
cand --command 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set'
|
cand --command 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set'
|
||||||
cand --completions 'Print shell completion script for <SHELL>'
|
cand --completions 'Print shell completion script for <SHELL>'
|
||||||
cand -s 'Show information about <RECIPE>'
|
cand -l 'List available recipes'
|
||||||
cand --show 'Show information about <RECIPE>'
|
cand --list 'List available recipes'
|
||||||
|
cand -s 'Show recipe at <PATH>'
|
||||||
|
cand --show 'Show recipe at <PATH>'
|
||||||
cand --dotenv-filename 'Search for environment file named <DOTENV-FILENAME> instead of `.env`'
|
cand --dotenv-filename 'Search for environment file named <DOTENV-FILENAME> instead of `.env`'
|
||||||
cand -E 'Load <DOTENV-PATH> as environment file instead of searching for one'
|
cand -E 'Load <DOTENV-PATH> as environment file instead of searching for one'
|
||||||
cand --dotenv-path 'Load <DOTENV-PATH> as environment file instead of searching for one'
|
cand --dotenv-path 'Load <DOTENV-PATH> as environment file instead of searching for one'
|
||||||
|
cand --timestamp-format 'Timestamp format string'
|
||||||
cand --check 'Run `--fmt` in ''check'' mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required.'
|
cand --check 'Run `--fmt` in ''check'' mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required.'
|
||||||
cand --yes 'Automatically confirm all recipes.'
|
cand --yes 'Automatically confirm all recipes.'
|
||||||
cand -n 'Print what just would do without doing it'
|
cand -n 'Print what just would do without doing it'
|
||||||
@ -64,14 +67,13 @@ set edit:completion:arg-completer[just] = {|@words|
|
|||||||
cand --evaluate 'Evaluate and print all variables. If a variable name is given as an argument, only print that variable''s value.'
|
cand --evaluate 'Evaluate and print all variables. If a variable name is given as an argument, only print that variable''s value.'
|
||||||
cand --fmt 'Format and overwrite justfile'
|
cand --fmt 'Format and overwrite justfile'
|
||||||
cand --init 'Initialize new justfile in project root'
|
cand --init 'Initialize new justfile in project root'
|
||||||
cand -l 'List available recipes and their arguments'
|
|
||||||
cand --list 'List available recipes and their arguments'
|
|
||||||
cand --groups 'List recipe groups'
|
cand --groups 'List recipe groups'
|
||||||
cand --man 'Print man page'
|
cand --man 'Print man page'
|
||||||
cand --summary 'List names of available recipes'
|
cand --summary 'List names of available recipes'
|
||||||
cand --variables 'List names of variables'
|
cand --variables 'List names of variables'
|
||||||
cand -g 'Use global justfile'
|
cand -g 'Use global justfile'
|
||||||
cand --global-justfile 'Use global justfile'
|
cand --global-justfile 'Use global justfile'
|
||||||
|
cand --timestamp 'Print recipe command timestamps'
|
||||||
cand -h 'Print help'
|
cand -h 'Print help'
|
||||||
cand --help 'Print help'
|
cand --help 'Print help'
|
||||||
cand -V 'Print version'
|
cand -V 'Print version'
|
||||||
|
@ -48,9 +48,11 @@ complete -c just -l shell-arg -d 'Invoke shell with <SHELL-ARG> as an argument'
|
|||||||
complete -c just -s d -l working-directory -d 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set' -r -F
|
complete -c just -s d -l working-directory -d 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set' -r -F
|
||||||
complete -c just -s c -l command -d 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set' -r
|
complete -c just -s c -l command -d 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set' -r
|
||||||
complete -c just -l completions -d 'Print shell completion script for <SHELL>' -r -f -a "{bash '',elvish '',fish '',powershell '',zsh ''}"
|
complete -c just -l completions -d 'Print shell completion script for <SHELL>' -r -f -a "{bash '',elvish '',fish '',powershell '',zsh ''}"
|
||||||
complete -c just -s s -l show -d 'Show information about <RECIPE>' -r
|
complete -c just -s l -l list -d 'List available recipes' -r
|
||||||
|
complete -c just -s s -l show -d 'Show recipe at <PATH>' -r
|
||||||
complete -c just -l dotenv-filename -d 'Search for environment file named <DOTENV-FILENAME> instead of `.env`' -r
|
complete -c just -l dotenv-filename -d 'Search for environment file named <DOTENV-FILENAME> instead of `.env`' -r
|
||||||
complete -c just -s E -l dotenv-path -d 'Load <DOTENV-PATH> as environment file instead of searching for one' -r -F
|
complete -c just -s E -l dotenv-path -d 'Load <DOTENV-PATH> as environment file instead of searching for one' -r -F
|
||||||
|
complete -c just -l timestamp-format -d 'Timestamp format string' -r
|
||||||
complete -c just -l check -d 'Run `--fmt` in \'check\' mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required.'
|
complete -c just -l check -d 'Run `--fmt` in \'check\' mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required.'
|
||||||
complete -c just -l yes -d 'Automatically confirm all recipes.'
|
complete -c just -l yes -d 'Automatically confirm all recipes.'
|
||||||
complete -c just -s n -l dry-run -d 'Print what just would do without doing it'
|
complete -c just -s n -l dry-run -d 'Print what just would do without doing it'
|
||||||
@ -72,11 +74,11 @@ complete -c just -s e -l edit -d 'Edit justfile with editor given by $VISUAL or
|
|||||||
complete -c just -l evaluate -d 'Evaluate and print all variables. If a variable name is given as an argument, only print that variable\'s value.'
|
complete -c just -l evaluate -d 'Evaluate and print all variables. If a variable name is given as an argument, only print that variable\'s value.'
|
||||||
complete -c just -l fmt -d 'Format and overwrite justfile'
|
complete -c just -l fmt -d 'Format and overwrite justfile'
|
||||||
complete -c just -l init -d 'Initialize new justfile in project root'
|
complete -c just -l init -d 'Initialize new justfile in project root'
|
||||||
complete -c just -s l -l list -d 'List available recipes and their arguments'
|
|
||||||
complete -c just -l groups -d 'List recipe groups'
|
complete -c just -l groups -d 'List recipe groups'
|
||||||
complete -c just -l man -d 'Print man page'
|
complete -c just -l man -d 'Print man page'
|
||||||
complete -c just -l summary -d 'List names of available recipes'
|
complete -c just -l summary -d 'List names of available recipes'
|
||||||
complete -c just -l variables -d 'List names of variables'
|
complete -c just -l variables -d 'List names of variables'
|
||||||
complete -c just -s g -l global-justfile -d 'Use global justfile'
|
complete -c just -s g -l global-justfile -d 'Use global justfile'
|
||||||
|
complete -c just -l timestamp -d 'Print recipe command timestamps'
|
||||||
complete -c just -s h -l help -d 'Print help'
|
complete -c just -s h -l help -d 'Print help'
|
||||||
complete -c just -s V -l version -d 'Print version'
|
complete -c just -s V -l version -d 'Print version'
|
||||||
|
@ -36,11 +36,14 @@ Register-ArgumentCompleter -Native -CommandName 'just' -ScriptBlock {
|
|||||||
[CompletionResult]::new('-c', 'c', [CompletionResultType]::ParameterName, 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set')
|
[CompletionResult]::new('-c', 'c', [CompletionResultType]::ParameterName, 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set')
|
||||||
[CompletionResult]::new('--command', 'command', [CompletionResultType]::ParameterName, 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set')
|
[CompletionResult]::new('--command', 'command', [CompletionResultType]::ParameterName, 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set')
|
||||||
[CompletionResult]::new('--completions', 'completions', [CompletionResultType]::ParameterName, 'Print shell completion script for <SHELL>')
|
[CompletionResult]::new('--completions', 'completions', [CompletionResultType]::ParameterName, 'Print shell completion script for <SHELL>')
|
||||||
[CompletionResult]::new('-s', 's', [CompletionResultType]::ParameterName, 'Show information about <RECIPE>')
|
[CompletionResult]::new('-l', 'l', [CompletionResultType]::ParameterName, 'List available recipes')
|
||||||
[CompletionResult]::new('--show', 'show', [CompletionResultType]::ParameterName, 'Show information about <RECIPE>')
|
[CompletionResult]::new('--list', 'list', [CompletionResultType]::ParameterName, 'List available recipes')
|
||||||
|
[CompletionResult]::new('-s', 's', [CompletionResultType]::ParameterName, 'Show recipe at <PATH>')
|
||||||
|
[CompletionResult]::new('--show', 'show', [CompletionResultType]::ParameterName, 'Show recipe at <PATH>')
|
||||||
[CompletionResult]::new('--dotenv-filename', 'dotenv-filename', [CompletionResultType]::ParameterName, 'Search for environment file named <DOTENV-FILENAME> instead of `.env`')
|
[CompletionResult]::new('--dotenv-filename', 'dotenv-filename', [CompletionResultType]::ParameterName, 'Search for environment file named <DOTENV-FILENAME> instead of `.env`')
|
||||||
[CompletionResult]::new('-E', 'E ', [CompletionResultType]::ParameterName, 'Load <DOTENV-PATH> as environment file instead of searching for one')
|
[CompletionResult]::new('-E', 'E ', [CompletionResultType]::ParameterName, 'Load <DOTENV-PATH> as environment file instead of searching for one')
|
||||||
[CompletionResult]::new('--dotenv-path', 'dotenv-path', [CompletionResultType]::ParameterName, 'Load <DOTENV-PATH> as environment file instead of searching for one')
|
[CompletionResult]::new('--dotenv-path', 'dotenv-path', [CompletionResultType]::ParameterName, 'Load <DOTENV-PATH> as environment file instead of searching for one')
|
||||||
|
[CompletionResult]::new('--timestamp-format', 'timestamp-format', [CompletionResultType]::ParameterName, 'Timestamp format string')
|
||||||
[CompletionResult]::new('--check', 'check', [CompletionResultType]::ParameterName, 'Run `--fmt` in ''check'' mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required.')
|
[CompletionResult]::new('--check', 'check', [CompletionResultType]::ParameterName, 'Run `--fmt` in ''check'' mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required.')
|
||||||
[CompletionResult]::new('--yes', 'yes', [CompletionResultType]::ParameterName, 'Automatically confirm all recipes.')
|
[CompletionResult]::new('--yes', 'yes', [CompletionResultType]::ParameterName, 'Automatically confirm all recipes.')
|
||||||
[CompletionResult]::new('-n', 'n', [CompletionResultType]::ParameterName, 'Print what just would do without doing it')
|
[CompletionResult]::new('-n', 'n', [CompletionResultType]::ParameterName, 'Print what just would do without doing it')
|
||||||
@ -67,14 +70,13 @@ Register-ArgumentCompleter -Native -CommandName 'just' -ScriptBlock {
|
|||||||
[CompletionResult]::new('--evaluate', 'evaluate', [CompletionResultType]::ParameterName, 'Evaluate and print all variables. If a variable name is given as an argument, only print that variable''s value.')
|
[CompletionResult]::new('--evaluate', 'evaluate', [CompletionResultType]::ParameterName, 'Evaluate and print all variables. If a variable name is given as an argument, only print that variable''s value.')
|
||||||
[CompletionResult]::new('--fmt', 'fmt', [CompletionResultType]::ParameterName, 'Format and overwrite justfile')
|
[CompletionResult]::new('--fmt', 'fmt', [CompletionResultType]::ParameterName, 'Format and overwrite justfile')
|
||||||
[CompletionResult]::new('--init', 'init', [CompletionResultType]::ParameterName, 'Initialize new justfile in project root')
|
[CompletionResult]::new('--init', 'init', [CompletionResultType]::ParameterName, 'Initialize new justfile in project root')
|
||||||
[CompletionResult]::new('-l', 'l', [CompletionResultType]::ParameterName, 'List available recipes and their arguments')
|
|
||||||
[CompletionResult]::new('--list', 'list', [CompletionResultType]::ParameterName, 'List available recipes and their arguments')
|
|
||||||
[CompletionResult]::new('--groups', 'groups', [CompletionResultType]::ParameterName, 'List recipe groups')
|
[CompletionResult]::new('--groups', 'groups', [CompletionResultType]::ParameterName, 'List recipe groups')
|
||||||
[CompletionResult]::new('--man', 'man', [CompletionResultType]::ParameterName, 'Print man page')
|
[CompletionResult]::new('--man', 'man', [CompletionResultType]::ParameterName, 'Print man page')
|
||||||
[CompletionResult]::new('--summary', 'summary', [CompletionResultType]::ParameterName, 'List names of available recipes')
|
[CompletionResult]::new('--summary', 'summary', [CompletionResultType]::ParameterName, 'List names of available recipes')
|
||||||
[CompletionResult]::new('--variables', 'variables', [CompletionResultType]::ParameterName, 'List names of variables')
|
[CompletionResult]::new('--variables', 'variables', [CompletionResultType]::ParameterName, 'List names of variables')
|
||||||
[CompletionResult]::new('-g', 'g', [CompletionResultType]::ParameterName, 'Use global justfile')
|
[CompletionResult]::new('-g', 'g', [CompletionResultType]::ParameterName, 'Use global justfile')
|
||||||
[CompletionResult]::new('--global-justfile', 'global-justfile', [CompletionResultType]::ParameterName, 'Use global justfile')
|
[CompletionResult]::new('--global-justfile', 'global-justfile', [CompletionResultType]::ParameterName, 'Use global justfile')
|
||||||
|
[CompletionResult]::new('--timestamp', 'timestamp', [CompletionResultType]::ParameterName, 'Print recipe command timestamps')
|
||||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
||||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
||||||
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
||||||
|
@ -31,11 +31,14 @@ _just() {
|
|||||||
'*-c+[Run an arbitrary command with the working directory, \`.env\`, overrides, and exports set]: : ' \
|
'*-c+[Run an arbitrary command with the working directory, \`.env\`, overrides, and exports set]: : ' \
|
||||||
'*--command=[Run an arbitrary command with the working directory, \`.env\`, overrides, and exports set]: : ' \
|
'*--command=[Run an arbitrary command with the working directory, \`.env\`, overrides, and exports set]: : ' \
|
||||||
'*--completions=[Print shell completion script for <SHELL>]:SHELL:(bash elvish fish powershell zsh)' \
|
'*--completions=[Print shell completion script for <SHELL>]:SHELL:(bash elvish fish powershell zsh)' \
|
||||||
'-s+[Show information about <RECIPE>]: :(_just_commands)' \
|
'()-l+[List available recipes]' \
|
||||||
'--show=[Show information about <RECIPE>]: :(_just_commands)' \
|
'()--list=[List available recipes]' \
|
||||||
|
'-s+[Show recipe at <PATH>]: :(_just_commands)' \
|
||||||
|
'--show=[Show recipe at <PATH>]: :(_just_commands)' \
|
||||||
'(-E --dotenv-path)--dotenv-filename=[Search for environment file named <DOTENV-FILENAME> instead of \`.env\`]: : ' \
|
'(-E --dotenv-path)--dotenv-filename=[Search for environment file named <DOTENV-FILENAME> instead of \`.env\`]: : ' \
|
||||||
'-E+[Load <DOTENV-PATH> as environment file instead of searching for one]: :_files' \
|
'-E+[Load <DOTENV-PATH> as environment file instead of searching for one]: :_files' \
|
||||||
'--dotenv-path=[Load <DOTENV-PATH> as environment file instead of searching for one]: :_files' \
|
'--dotenv-path=[Load <DOTENV-PATH> as environment file instead of searching for one]: :_files' \
|
||||||
|
'--timestamp-format=[Timestamp format string]: : ' \
|
||||||
'--check[Run \`--fmt\` in '\''check'\'' mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required.]' \
|
'--check[Run \`--fmt\` in '\''check'\'' mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required.]' \
|
||||||
'--yes[Automatically confirm all recipes.]' \
|
'--yes[Automatically confirm all recipes.]' \
|
||||||
'(-q --quiet)-n[Print what just would do without doing it]' \
|
'(-q --quiet)-n[Print what just would do without doing it]' \
|
||||||
@ -62,14 +65,13 @@ _just() {
|
|||||||
'--evaluate[Evaluate and print all variables. If a variable name is given as an argument, only print that variable'\''s value.]' \
|
'--evaluate[Evaluate and print all variables. If a variable name is given as an argument, only print that variable'\''s value.]' \
|
||||||
'--fmt[Format and overwrite justfile]' \
|
'--fmt[Format and overwrite justfile]' \
|
||||||
'--init[Initialize new justfile in project root]' \
|
'--init[Initialize new justfile in project root]' \
|
||||||
'-l[List available recipes and their arguments]' \
|
|
||||||
'--list[List available recipes and their arguments]' \
|
|
||||||
'--groups[List recipe groups]' \
|
'--groups[List recipe groups]' \
|
||||||
'--man[Print man page]' \
|
'--man[Print man page]' \
|
||||||
'--summary[List names of available recipes]' \
|
'--summary[List names of available recipes]' \
|
||||||
'--variables[List names of variables]' \
|
'--variables[List names of variables]' \
|
||||||
'(-f --justfile -d --working-directory)-g[Use global justfile]' \
|
'(-f --justfile -d --working-directory)-g[Use global justfile]' \
|
||||||
'(-f --justfile -d --working-directory)--global-justfile[Use global justfile]' \
|
'(-f --justfile -d --working-directory)--global-justfile[Use global justfile]' \
|
||||||
|
'--timestamp[Print recipe command timestamps]' \
|
||||||
'-h[Print help]' \
|
'-h[Print help]' \
|
||||||
'--help[Print help]' \
|
'--help[Print help]' \
|
||||||
'-V[Print version]' \
|
'-V[Print version]' \
|
||||||
|
4
justfile
4
justfile
@ -164,8 +164,8 @@ watch-readme:
|
|||||||
just render-readme
|
just render-readme
|
||||||
fswatch -ro README.adoc | xargs -n1 -I{} just render-readme
|
fswatch -ro README.adoc | xargs -n1 -I{} just render-readme
|
||||||
|
|
||||||
generate-completions:
|
update-completions:
|
||||||
./bin/generate-completions
|
./bin/update-completions
|
||||||
|
|
||||||
test-completions:
|
test-completions:
|
||||||
./tests/completions/just.bash
|
./tests/completions/just.bash
|
||||||
|
@ -166,6 +166,7 @@ impl<'src> Analyzer<'src> {
|
|||||||
name,
|
name,
|
||||||
recipes,
|
recipes,
|
||||||
settings,
|
settings,
|
||||||
|
source: root.into(),
|
||||||
warnings,
|
warnings,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -47,10 +47,10 @@ pub(crate) const ZSH_COMPLETION_REPLACEMENTS: &[(&str, &str)] = &[
|
|||||||
r"'*--set=[Override <VARIABLE> with <VALUE>]: :(_just_variables)' \",
|
r"'*--set=[Override <VARIABLE> with <VALUE>]: :(_just_variables)' \",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"'()-s+[Show information about <RECIPE>]:RECIPE: ' \
|
r"'()-s+[Show recipe at <PATH>]:PATH: ' \
|
||||||
'()--show=[Show information about <RECIPE>]:RECIPE: ' \",
|
'()--show=[Show recipe at <PATH>]:PATH: ' \",
|
||||||
r"'-s+[Show information about <RECIPE>]: :(_just_commands)' \
|
r"'-s+[Show recipe at <PATH>]: :(_just_commands)' \
|
||||||
'--show=[Show information about <RECIPE>]: :(_just_commands)' \",
|
'--show=[Show recipe at <PATH>]: :(_just_commands)' \",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"'*::ARGUMENTS -- Overrides and recipe(s) to run, defaulting to the first recipe in the \
|
"'*::ARGUMENTS -- Overrides and recipe(s) to run, defaulting to the first recipe in the \
|
||||||
@ -202,7 +202,6 @@ pub(crate) const BASH_COMPLETION_REPLACEMENTS: &[(&str, &str)] = &[
|
|||||||
fi
|
fi
|
||||||
fi"#,
|
fi"#,
|
||||||
),
|
),
|
||||||
(r" just)", r#" "$1")"#),
|
|
||||||
(
|
(
|
||||||
r"local i cur prev opts cmd",
|
r"local i cur prev opts cmd",
|
||||||
r"local i cur prev words cword opts cmd",
|
r"local i cur prev words cword opts cmd",
|
||||||
|
116
src/config.rs
116
src/config.rs
@ -2,6 +2,7 @@ use {
|
|||||||
super::*,
|
super::*,
|
||||||
clap::{
|
clap::{
|
||||||
builder::{styling::AnsiColor, FalseyValueParser, PossibleValuesParser, Styles},
|
builder::{styling::AnsiColor, FalseyValueParser, PossibleValuesParser, Styles},
|
||||||
|
parser::ValuesRef,
|
||||||
value_parser, Arg, ArgAction, ArgGroup, ArgMatches, Command,
|
value_parser, Arg, ArgAction, ArgGroup, ArgMatches, Command,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -39,6 +40,8 @@ pub(crate) struct Config {
|
|||||||
pub(crate) shell_args: Option<Vec<String>>,
|
pub(crate) shell_args: Option<Vec<String>>,
|
||||||
pub(crate) shell_command: bool,
|
pub(crate) shell_command: bool,
|
||||||
pub(crate) subcommand: Subcommand,
|
pub(crate) subcommand: Subcommand,
|
||||||
|
pub(crate) timestamp: bool,
|
||||||
|
pub(crate) timestamp_format: String,
|
||||||
pub(crate) unsorted: bool,
|
pub(crate) unsorted: bool,
|
||||||
pub(crate) unstable: bool,
|
pub(crate) unstable: bool,
|
||||||
pub(crate) verbosity: Verbosity,
|
pub(crate) verbosity: Verbosity,
|
||||||
@ -79,9 +82,8 @@ mod cmd {
|
|||||||
VARIABLES,
|
VARIABLES,
|
||||||
];
|
];
|
||||||
|
|
||||||
pub(crate) const ARGLESS: &[&str] = &[
|
pub(crate) const ARGLESS: &[&str] =
|
||||||
CHANGELOG, DUMP, EDIT, FORMAT, INIT, LIST, MAN, SUMMARY, VARIABLES,
|
&[CHANGELOG, DUMP, EDIT, FORMAT, INIT, MAN, SUMMARY, VARIABLES];
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod arg {
|
mod arg {
|
||||||
@ -109,6 +111,8 @@ mod arg {
|
|||||||
pub(crate) const SHELL: &str = "SHELL";
|
pub(crate) const SHELL: &str = "SHELL";
|
||||||
pub(crate) const SHELL_ARG: &str = "SHELL-ARG";
|
pub(crate) const SHELL_ARG: &str = "SHELL-ARG";
|
||||||
pub(crate) const SHELL_COMMAND: &str = "SHELL-COMMAND";
|
pub(crate) const SHELL_COMMAND: &str = "SHELL-COMMAND";
|
||||||
|
pub(crate) const TIMESTAMP: &str = "TIMESTAMP";
|
||||||
|
pub(crate) const TIMESTAMP_FORMAT: &str = "TIMESTAMP_FORMAT";
|
||||||
pub(crate) const UNSORTED: &str = "UNSORTED";
|
pub(crate) const UNSORTED: &str = "UNSORTED";
|
||||||
pub(crate) const UNSTABLE: &str = "UNSTABLE";
|
pub(crate) const UNSTABLE: &str = "UNSTABLE";
|
||||||
pub(crate) const VERBOSE: &str = "VERBOSE";
|
pub(crate) const VERBOSE: &str = "VERBOSE";
|
||||||
@ -415,8 +419,11 @@ impl Config {
|
|||||||
Arg::new(cmd::LIST)
|
Arg::new(cmd::LIST)
|
||||||
.short('l')
|
.short('l')
|
||||||
.long("list")
|
.long("list")
|
||||||
.action(ArgAction::SetTrue)
|
.num_args(0..)
|
||||||
.help("List available recipes and their arguments"),
|
.value_name("PATH")
|
||||||
|
.action(ArgAction::Set)
|
||||||
|
.conflicts_with(arg::ARGUMENTS)
|
||||||
|
.help("List available recipes"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(cmd::GROUPS)
|
Arg::new(cmd::GROUPS)
|
||||||
@ -434,10 +441,11 @@ impl Config {
|
|||||||
Arg::new(cmd::SHOW)
|
Arg::new(cmd::SHOW)
|
||||||
.short('s')
|
.short('s')
|
||||||
.long("show")
|
.long("show")
|
||||||
|
.num_args(1..)
|
||||||
.action(ArgAction::Set)
|
.action(ArgAction::Set)
|
||||||
.value_name("RECIPE")
|
.value_name("PATH")
|
||||||
.conflicts_with(arg::ARGUMENTS)
|
.conflicts_with(arg::ARGUMENTS)
|
||||||
.help("Show information about <RECIPE>"),
|
.help("Show recipe at <PATH>"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(cmd::SUMMARY)
|
Arg::new(cmd::SUMMARY)
|
||||||
@ -482,6 +490,21 @@ impl Config {
|
|||||||
.conflicts_with(arg::WORKING_DIRECTORY)
|
.conflicts_with(arg::WORKING_DIRECTORY)
|
||||||
.help("Use global justfile")
|
.help("Use global justfile")
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(arg::TIMESTAMP)
|
||||||
|
.action(ArgAction::SetTrue)
|
||||||
|
.long("timestamp")
|
||||||
|
.env("JUST_TIMESTAMP")
|
||||||
|
.help("Print recipe command timestamps")
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(arg::TIMESTAMP_FORMAT)
|
||||||
|
.action(ArgAction::Set)
|
||||||
|
.long("timestamp-format")
|
||||||
|
.env("JUST_TIMESTAMP_FORMAT")
|
||||||
|
.default_value("%H:%M:%S")
|
||||||
|
.help("Timestamp format string")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn color_from_matches(matches: &ArgMatches) -> ConfigResult<Color> {
|
fn color_from_matches(matches: &ArgMatches) -> ConfigResult<Color> {
|
||||||
@ -537,6 +560,18 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_module_path(path: ValuesRef<String>) -> ConfigResult<ModulePath> {
|
||||||
|
path
|
||||||
|
.clone()
|
||||||
|
.map(|s| (*s).as_str())
|
||||||
|
.collect::<Vec<&str>>()
|
||||||
|
.as_slice()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|()| ConfigError::ModulePath {
|
||||||
|
path: path.cloned().collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn search_config(matches: &ArgMatches, positional: &Positional) -> ConfigResult<SearchConfig> {
|
fn search_config(matches: &ArgMatches, positional: &Positional) -> ConfigResult<SearchConfig> {
|
||||||
if matches.get_flag(arg::GLOBAL_JUSTFILE) {
|
if matches.get_flag(arg::GLOBAL_JUSTFILE) {
|
||||||
return Ok(SearchConfig::GlobalJustfile);
|
return Ok(SearchConfig::GlobalJustfile);
|
||||||
@ -654,14 +689,18 @@ impl Config {
|
|||||||
Subcommand::Format
|
Subcommand::Format
|
||||||
} else if matches.get_flag(cmd::INIT) {
|
} else if matches.get_flag(cmd::INIT) {
|
||||||
Subcommand::Init
|
Subcommand::Init
|
||||||
} else if matches.get_flag(cmd::LIST) {
|
} else if let Some(path) = matches.get_many::<String>(cmd::LIST) {
|
||||||
Subcommand::List
|
Subcommand::List {
|
||||||
|
path: Self::parse_module_path(path)?,
|
||||||
|
}
|
||||||
} else if matches.get_flag(cmd::GROUPS) {
|
} else if matches.get_flag(cmd::GROUPS) {
|
||||||
Subcommand::Groups
|
Subcommand::Groups
|
||||||
} else if matches.get_flag(cmd::MAN) {
|
} else if matches.get_flag(cmd::MAN) {
|
||||||
Subcommand::Man
|
Subcommand::Man
|
||||||
} else if let Some(name) = matches.get_one::<String>(cmd::SHOW).map(Into::into) {
|
} else if let Some(path) = matches.get_many::<String>(cmd::SHOW) {
|
||||||
Subcommand::Show { name }
|
Subcommand::Show {
|
||||||
|
path: Self::parse_module_path(path)?,
|
||||||
|
}
|
||||||
} else if matches.get_flag(cmd::EVALUATE) {
|
} else if matches.get_flag(cmd::EVALUATE) {
|
||||||
if positional.arguments.len() > 1 {
|
if positional.arguments.len() > 1 {
|
||||||
return Err(ConfigError::SubcommandArguments {
|
return Err(ConfigError::SubcommandArguments {
|
||||||
@ -723,6 +762,11 @@ impl Config {
|
|||||||
shell_args,
|
shell_args,
|
||||||
shell_command: matches.get_flag(arg::SHELL_COMMAND),
|
shell_command: matches.get_flag(arg::SHELL_COMMAND),
|
||||||
subcommand,
|
subcommand,
|
||||||
|
timestamp: matches.get_flag(arg::TIMESTAMP),
|
||||||
|
timestamp_format: matches
|
||||||
|
.get_one::<String>(arg::TIMESTAMP_FORMAT)
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
unsorted: matches.get_flag(arg::UNSORTED),
|
unsorted: matches.get_flag(arg::UNSORTED),
|
||||||
unstable,
|
unstable,
|
||||||
verbosity,
|
verbosity,
|
||||||
@ -1263,30 +1307,42 @@ mod tests {
|
|||||||
test! {
|
test! {
|
||||||
name: subcommand_list_long,
|
name: subcommand_list_long,
|
||||||
args: ["--list"],
|
args: ["--list"],
|
||||||
subcommand: Subcommand::List,
|
subcommand: Subcommand::List{ path: ModulePath { path: Vec::new(), spaced: false } },
|
||||||
}
|
}
|
||||||
|
|
||||||
test! {
|
test! {
|
||||||
name: subcommand_list_short,
|
name: subcommand_list_short,
|
||||||
args: ["-l"],
|
args: ["-l"],
|
||||||
subcommand: Subcommand::List,
|
subcommand: Subcommand::List{ path: ModulePath { path: Vec::new(), spaced: false } },
|
||||||
|
}
|
||||||
|
|
||||||
|
test! {
|
||||||
|
name: subcommand_list_arguments,
|
||||||
|
args: ["--list", "bar"],
|
||||||
|
subcommand: Subcommand::List{ path: ModulePath { path: vec!["bar".into()], spaced: false } },
|
||||||
}
|
}
|
||||||
|
|
||||||
test! {
|
test! {
|
||||||
name: subcommand_show_long,
|
name: subcommand_show_long,
|
||||||
args: ["--show", "build"],
|
args: ["--show", "build"],
|
||||||
subcommand: Subcommand::Show { name: String::from("build") },
|
subcommand: Subcommand::Show { path: ModulePath { path: vec!["build".into()], spaced: false } },
|
||||||
}
|
}
|
||||||
|
|
||||||
test! {
|
test! {
|
||||||
name: subcommand_show_short,
|
name: subcommand_show_short,
|
||||||
args: ["-s", "build"],
|
args: ["-s", "build"],
|
||||||
subcommand: Subcommand::Show { name: String::from("build") },
|
subcommand: Subcommand::Show { path: ModulePath { path: vec!["build".into()], spaced: false } },
|
||||||
}
|
}
|
||||||
|
|
||||||
error! {
|
test! {
|
||||||
name: subcommand_show_no_arg,
|
name: subcommand_show_multiple_args,
|
||||||
args: ["--show"],
|
args: ["--show", "foo", "bar"],
|
||||||
|
subcommand: Subcommand::Show {
|
||||||
|
path: ModulePath {
|
||||||
|
path: vec!["foo".into(), "bar".into()],
|
||||||
|
spaced: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
test! {
|
test! {
|
||||||
@ -1501,16 +1557,6 @@ mod tests {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
error! {
|
|
||||||
name: list_arguments,
|
|
||||||
args: ["--list", "bar"],
|
|
||||||
error: ConfigError::SubcommandArguments { subcommand, arguments },
|
|
||||||
check: {
|
|
||||||
assert_eq!(subcommand, cmd::LIST);
|
|
||||||
assert_eq!(arguments, &["bar"]);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
error! {
|
error! {
|
||||||
name: dump_arguments,
|
name: dump_arguments,
|
||||||
args: ["--dump", "bar"],
|
args: ["--dump", "bar"],
|
||||||
@ -1571,20 +1617,6 @@ mod tests {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
error_matches! {
|
|
||||||
name: show_arguments,
|
|
||||||
args: ["--show", "foo", "bar"],
|
|
||||||
error: error,
|
|
||||||
check: {
|
|
||||||
assert_eq!(error.kind(), clap::error::ErrorKind::ArgumentConflict);
|
|
||||||
assert_eq!(error.context().collect::<Vec<_>>(), vec![
|
|
||||||
(ContextKind::InvalidArg, &ContextValue::String("--show <RECIPE>".into())),
|
|
||||||
(ContextKind::PriorArg, &ContextValue::String("[ARGUMENTS]...".into())),
|
|
||||||
(ContextKind::Usage, &ContextValue::StyledStr("\u{1b}[33mUsage:\u{1b}[0m \u{1b}[32mjust\u{1b}[0m \u{1b}[32m--show\u{1b}[0m\u{1b}[32m \u{1b}[0m\u{1b}[32m<RECIPE>\u{1b}[0m \u{1b}[32m[ARGUMENTS]...\u{1b}[0m".into())),
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
error! {
|
error! {
|
||||||
name: summary_arguments,
|
name: summary_arguments,
|
||||||
args: ["--summary", "bar"],
|
args: ["--summary", "bar"],
|
||||||
|
@ -6,11 +6,12 @@ pub(crate) enum ConfigError {
|
|||||||
#[snafu(display("Failed to get current directory: {}", source))]
|
#[snafu(display("Failed to get current directory: {}", source))]
|
||||||
CurrentDir { source: io::Error },
|
CurrentDir { source: io::Error },
|
||||||
#[snafu(display(
|
#[snafu(display(
|
||||||
"Internal config error, this may indicate a bug in just: {} \
|
"Internal config error, this may indicate a bug in just: {message} \
|
||||||
consider filing an issue: https://github.com/casey/just/issues/new",
|
consider filing an issue: https://github.com/casey/just/issues/new",
|
||||||
message
|
|
||||||
))]
|
))]
|
||||||
Internal { message: String },
|
Internal { message: String },
|
||||||
|
#[snafu(display("Invalid module path `{}`", path.join(" ")))]
|
||||||
|
ModulePath { path: Vec<String> },
|
||||||
#[snafu(display(
|
#[snafu(display(
|
||||||
"Path-prefixed recipes may not be used with `--working-directory` or `--justfile`."
|
"Path-prefixed recipes may not be used with `--working-directory` or `--justfile`."
|
||||||
))]
|
))]
|
||||||
|
@ -160,6 +160,9 @@ pub(crate) enum Error<'src> {
|
|||||||
recipe: &'src str,
|
recipe: &'src str,
|
||||||
line_number: Option<usize>,
|
line_number: Option<usize>,
|
||||||
},
|
},
|
||||||
|
UnknownSubmodule {
|
||||||
|
path: ModulePath,
|
||||||
|
},
|
||||||
UnknownOverrides {
|
UnknownOverrides {
|
||||||
overrides: Vec<String>,
|
overrides: Vec<String>,
|
||||||
},
|
},
|
||||||
@ -432,6 +435,9 @@ impl<'src> ColorDisplay for Error<'src> {
|
|||||||
write!(f, "Recipe `{recipe}` failed for an unknown reason")?;
|
write!(f, "Recipe `{recipe}` failed for an unknown reason")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
UnknownSubmodule { path } => {
|
||||||
|
write!(f, "Justfile does not contain submodule `{path}`")?;
|
||||||
|
}
|
||||||
UnknownOverrides { overrides } => {
|
UnknownOverrides { overrides } => {
|
||||||
let count = Count("Variable", overrides.len());
|
let count = Count("Variable", overrides.len());
|
||||||
let overrides = List::and_ticked(overrides);
|
let overrides = List::and_ticked(overrides);
|
||||||
|
@ -4,9 +4,10 @@ pub(crate) struct Evaluator<'src: 'run, 'run> {
|
|||||||
pub(crate) assignments: Option<&'run Table<'src, Assignment<'src>>>,
|
pub(crate) assignments: Option<&'run Table<'src, Assignment<'src>>>,
|
||||||
pub(crate) config: &'run Config,
|
pub(crate) config: &'run Config,
|
||||||
pub(crate) dotenv: &'run BTreeMap<String, String>,
|
pub(crate) dotenv: &'run BTreeMap<String, String>,
|
||||||
|
pub(crate) module_source: &'run Path,
|
||||||
pub(crate) scope: Scope<'src, 'run>,
|
pub(crate) scope: Scope<'src, 'run>,
|
||||||
pub(crate) settings: &'run Settings<'run>,
|
|
||||||
pub(crate) search: &'run Search,
|
pub(crate) search: &'run Search,
|
||||||
|
pub(crate) settings: &'run Settings<'run>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src, 'run> Evaluator<'src, 'run> {
|
impl<'src, 'run> Evaluator<'src, 'run> {
|
||||||
@ -14,17 +15,19 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
|||||||
assignments: &'run Table<'src, Assignment<'src>>,
|
assignments: &'run Table<'src, Assignment<'src>>,
|
||||||
config: &'run Config,
|
config: &'run Config,
|
||||||
dotenv: &'run BTreeMap<String, String>,
|
dotenv: &'run BTreeMap<String, String>,
|
||||||
overrides: Scope<'src, 'run>,
|
module_source: &'run Path,
|
||||||
settings: &'run Settings<'run>,
|
scope: Scope<'src, 'run>,
|
||||||
search: &'run Search,
|
search: &'run Search,
|
||||||
|
settings: &'run Settings<'run>,
|
||||||
) -> RunResult<'src, Scope<'src, 'run>> {
|
) -> RunResult<'src, Scope<'src, 'run>> {
|
||||||
let mut evaluator = Self {
|
let mut evaluator = Self {
|
||||||
scope: overrides,
|
|
||||||
assignments: Some(assignments),
|
assignments: Some(assignments),
|
||||||
config,
|
config,
|
||||||
dotenv,
|
dotenv,
|
||||||
settings,
|
module_source,
|
||||||
|
scope,
|
||||||
search,
|
search,
|
||||||
|
settings,
|
||||||
};
|
};
|
||||||
|
|
||||||
for assignment in assignments.values() {
|
for assignment in assignments.values() {
|
||||||
@ -250,21 +253,23 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn evaluate_parameters(
|
pub(crate) fn evaluate_parameters(
|
||||||
|
arguments: &[String],
|
||||||
config: &'run Config,
|
config: &'run Config,
|
||||||
dotenv: &'run BTreeMap<String, String>,
|
dotenv: &'run BTreeMap<String, String>,
|
||||||
|
module_source: &'run Path,
|
||||||
parameters: &[Parameter<'src>],
|
parameters: &[Parameter<'src>],
|
||||||
arguments: &[String],
|
|
||||||
scope: &'run Scope<'src, 'run>,
|
scope: &'run Scope<'src, 'run>,
|
||||||
settings: &'run Settings,
|
|
||||||
search: &'run Search,
|
search: &'run Search,
|
||||||
|
settings: &'run Settings,
|
||||||
) -> RunResult<'src, (Scope<'src, 'run>, Vec<String>)> {
|
) -> RunResult<'src, (Scope<'src, 'run>, Vec<String>)> {
|
||||||
let mut evaluator = Self {
|
let mut evaluator = Self {
|
||||||
assignments: None,
|
assignments: None,
|
||||||
|
config,
|
||||||
|
dotenv,
|
||||||
|
module_source,
|
||||||
scope: scope.child(),
|
scope: scope.child(),
|
||||||
search,
|
search,
|
||||||
settings,
|
settings,
|
||||||
dotenv,
|
|
||||||
config,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut scope = scope.child();
|
let mut scope = scope.child();
|
||||||
@ -307,17 +312,19 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
|||||||
pub(crate) fn recipe_evaluator(
|
pub(crate) fn recipe_evaluator(
|
||||||
config: &'run Config,
|
config: &'run Config,
|
||||||
dotenv: &'run BTreeMap<String, String>,
|
dotenv: &'run BTreeMap<String, String>,
|
||||||
|
module_source: &'run Path,
|
||||||
scope: &'run Scope<'src, 'run>,
|
scope: &'run Scope<'src, 'run>,
|
||||||
settings: &'run Settings,
|
|
||||||
search: &'run Search,
|
search: &'run Search,
|
||||||
) -> Evaluator<'src, 'run> {
|
settings: &'run Settings,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
assignments: None,
|
assignments: None,
|
||||||
|
config,
|
||||||
|
dotenv,
|
||||||
|
module_source,
|
||||||
scope: Scope::child(scope),
|
scope: Scope::child(scope),
|
||||||
search,
|
search,
|
||||||
settings,
|
settings,
|
||||||
dotenv,
|
|
||||||
config,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,6 +67,8 @@ pub(crate) fn get(name: &str) -> Option<Function> {
|
|||||||
"kebabcase" => Unary(kebabcase),
|
"kebabcase" => Unary(kebabcase),
|
||||||
"lowercamelcase" => Unary(lowercamelcase),
|
"lowercamelcase" => Unary(lowercamelcase),
|
||||||
"lowercase" => Unary(lowercase),
|
"lowercase" => Unary(lowercase),
|
||||||
|
"module_directory" => Nullary(module_directory),
|
||||||
|
"module_file" => Nullary(module_file),
|
||||||
"num_cpus" => Nullary(num_cpus),
|
"num_cpus" => Nullary(num_cpus),
|
||||||
"os" => Nullary(os),
|
"os" => Nullary(os),
|
||||||
"os_family" => Nullary(os_family),
|
"os_family" => Nullary(os_family),
|
||||||
@ -406,6 +408,44 @@ fn lowercase(_context: Context, s: &str) -> Result<String, String> {
|
|||||||
Ok(s.to_lowercase())
|
Ok(s.to_lowercase())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn module_directory(context: Context) -> Result<String, String> {
|
||||||
|
context
|
||||||
|
.evaluator
|
||||||
|
.search
|
||||||
|
.justfile
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.join(context.evaluator.module_source)
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.to_str()
|
||||||
|
.map(str::to_owned)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
format!(
|
||||||
|
"Module directory is not valid unicode: {}",
|
||||||
|
context.evaluator.module_source.parent().unwrap().display(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn module_file(context: Context) -> Result<String, String> {
|
||||||
|
context
|
||||||
|
.evaluator
|
||||||
|
.search
|
||||||
|
.justfile
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.join(context.evaluator.module_source)
|
||||||
|
.to_str()
|
||||||
|
.map(str::to_owned)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
format!(
|
||||||
|
"Module file path is not valid unicode: {}",
|
||||||
|
context.evaluator.module_source.display(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn num_cpus(_context: Context) -> Result<String, String> {
|
fn num_cpus(_context: Context) -> Result<String, String> {
|
||||||
let num = num_cpus::get();
|
let num = num_cpus::get();
|
||||||
Ok(num.to_string())
|
Ok(num.to_string())
|
||||||
|
@ -3,9 +3,10 @@ use {super::*, serde::Serialize};
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Invocation<'src: 'run, 'run> {
|
struct Invocation<'src: 'run, 'run> {
|
||||||
arguments: Vec<&'run str>,
|
arguments: Vec<&'run str>,
|
||||||
|
module_source: &'run Path,
|
||||||
recipe: &'run Recipe<'src>,
|
recipe: &'run Recipe<'src>,
|
||||||
settings: &'run Settings<'src>,
|
|
||||||
scope: &'run Scope<'src, 'run>,
|
scope: &'run Scope<'src, 'run>,
|
||||||
|
settings: &'run Settings<'src>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize)]
|
#[derive(Debug, PartialEq, Serialize)]
|
||||||
@ -21,6 +22,8 @@ pub(crate) struct Justfile<'src> {
|
|||||||
pub(crate) name: Option<Name<'src>>,
|
pub(crate) name: Option<Name<'src>>,
|
||||||
pub(crate) recipes: Table<'src, Rc<Recipe<'src>>>,
|
pub(crate) recipes: Table<'src, Rc<Recipe<'src>>>,
|
||||||
pub(crate) settings: Settings<'src>,
|
pub(crate) settings: Settings<'src>,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub(crate) source: PathBuf,
|
||||||
pub(crate) warnings: Vec<Warning>,
|
pub(crate) warnings: Vec<Warning>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,9 +109,10 @@ impl<'src> Justfile<'src> {
|
|||||||
&self.assignments,
|
&self.assignments,
|
||||||
config,
|
config,
|
||||||
dotenv,
|
dotenv,
|
||||||
|
&self.source,
|
||||||
scope,
|
scope,
|
||||||
&self.settings,
|
|
||||||
search,
|
search,
|
||||||
|
&self.settings,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,10 +280,12 @@ impl<'src> Justfile<'src> {
|
|||||||
let mut ran = Ran::default();
|
let mut ran = Ran::default();
|
||||||
for invocation in invocations {
|
for invocation in invocations {
|
||||||
let context = RecipeContext {
|
let context = RecipeContext {
|
||||||
settings: invocation.settings,
|
|
||||||
config,
|
config,
|
||||||
|
dotenv: &dotenv,
|
||||||
|
module_source: invocation.module_source,
|
||||||
scope: invocation.scope,
|
scope: invocation.scope,
|
||||||
search,
|
search,
|
||||||
|
settings: invocation.settings,
|
||||||
};
|
};
|
||||||
|
|
||||||
Self::run_recipe(
|
Self::run_recipe(
|
||||||
@ -290,7 +296,6 @@ impl<'src> Justfile<'src> {
|
|||||||
.map(str::to_string)
|
.map(str::to_string)
|
||||||
.collect::<Vec<String>>(),
|
.collect::<Vec<String>>(),
|
||||||
&context,
|
&context,
|
||||||
&dotenv,
|
|
||||||
&mut ran,
|
&mut ran,
|
||||||
invocation.recipe,
|
invocation.recipe,
|
||||||
search,
|
search,
|
||||||
@ -346,6 +351,7 @@ impl<'src> Justfile<'src> {
|
|||||||
recipe,
|
recipe,
|
||||||
arguments: Vec::new(),
|
arguments: Vec::new(),
|
||||||
scope,
|
scope,
|
||||||
|
module_source: &self.source,
|
||||||
},
|
},
|
||||||
depth,
|
depth,
|
||||||
)));
|
)));
|
||||||
@ -373,6 +379,7 @@ impl<'src> Justfile<'src> {
|
|||||||
recipe,
|
recipe,
|
||||||
scope: parent,
|
scope: parent,
|
||||||
settings: &self.settings,
|
settings: &self.settings,
|
||||||
|
module_source: &self.source,
|
||||||
},
|
},
|
||||||
depth,
|
depth,
|
||||||
)))
|
)))
|
||||||
@ -394,6 +401,7 @@ impl<'src> Justfile<'src> {
|
|||||||
recipe,
|
recipe,
|
||||||
scope: parent,
|
scope: parent,
|
||||||
settings: &self.settings,
|
settings: &self.settings,
|
||||||
|
module_source: &self.source,
|
||||||
},
|
},
|
||||||
depth + argument_count,
|
depth + argument_count,
|
||||||
)))
|
)))
|
||||||
@ -410,7 +418,6 @@ impl<'src> Justfile<'src> {
|
|||||||
fn run_recipe(
|
fn run_recipe(
|
||||||
arguments: &[String],
|
arguments: &[String],
|
||||||
context: &RecipeContext<'src, '_>,
|
context: &RecipeContext<'src, '_>,
|
||||||
dotenv: &BTreeMap<String, String>,
|
|
||||||
ran: &mut Ran<'src>,
|
ran: &mut Ran<'src>,
|
||||||
recipe: &Recipe<'src>,
|
recipe: &Recipe<'src>,
|
||||||
search: &Search,
|
search: &Search,
|
||||||
@ -426,19 +433,26 @@ impl<'src> Justfile<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (outer, positional) = Evaluator::evaluate_parameters(
|
let (outer, positional) = Evaluator::evaluate_parameters(
|
||||||
context.config,
|
|
||||||
dotenv,
|
|
||||||
&recipe.parameters,
|
|
||||||
arguments,
|
arguments,
|
||||||
|
context.config,
|
||||||
|
context.dotenv,
|
||||||
|
context.module_source,
|
||||||
|
&recipe.parameters,
|
||||||
context.scope,
|
context.scope,
|
||||||
context.settings,
|
|
||||||
search,
|
search,
|
||||||
|
context.settings,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let scope = outer.child();
|
let scope = outer.child();
|
||||||
|
|
||||||
let mut evaluator =
|
let mut evaluator = Evaluator::recipe_evaluator(
|
||||||
Evaluator::recipe_evaluator(context.config, dotenv, &scope, context.settings, search);
|
context.config,
|
||||||
|
context.dotenv,
|
||||||
|
context.module_source,
|
||||||
|
&scope,
|
||||||
|
search,
|
||||||
|
context.settings,
|
||||||
|
);
|
||||||
|
|
||||||
if !context.config.no_dependencies {
|
if !context.config.no_dependencies {
|
||||||
for Dependency { recipe, arguments } in recipe.dependencies.iter().take(recipe.priors) {
|
for Dependency { recipe, arguments } in recipe.dependencies.iter().take(recipe.priors) {
|
||||||
@ -447,11 +461,11 @@ impl<'src> Justfile<'src> {
|
|||||||
.map(|argument| evaluator.evaluate_expression(argument))
|
.map(|argument| evaluator.evaluate_expression(argument))
|
||||||
.collect::<RunResult<Vec<String>>>()?;
|
.collect::<RunResult<Vec<String>>>()?;
|
||||||
|
|
||||||
Self::run_recipe(&arguments, context, dotenv, ran, recipe, search)?;
|
Self::run_recipe(&arguments, context, ran, recipe, search)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
recipe.run(context, dotenv, scope.child(), search, &positional)?;
|
recipe.run(context, &scope, &positional)?;
|
||||||
|
|
||||||
if !context.config.no_dependencies {
|
if !context.config.no_dependencies {
|
||||||
let mut ran = Ran::default();
|
let mut ran = Ran::default();
|
||||||
@ -463,11 +477,12 @@ impl<'src> Justfile<'src> {
|
|||||||
evaluated.push(evaluator.evaluate_expression(argument)?);
|
evaluated.push(evaluator.evaluate_expression(argument)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::run_recipe(&evaluated, context, dotenv, &mut ran, recipe, search)?;
|
Self::run_recipe(&evaluated, context, &mut ran, recipe, search)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ran.ran(&recipe.namepath, arguments.to_vec());
|
ran.ran(&recipe.namepath, arguments.to_vec());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,12 +273,12 @@ impl<'src> Lexer<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// True if `c` can be the first character of an identifier
|
/// True if `c` can be the first character of an identifier
|
||||||
fn is_identifier_start(c: char) -> bool {
|
pub(crate) fn is_identifier_start(c: char) -> bool {
|
||||||
matches!(c, 'a'..='z' | 'A'..='Z' | '_')
|
matches!(c, 'a'..='z' | 'A'..='Z' | '_')
|
||||||
}
|
}
|
||||||
|
|
||||||
/// True if `c` can be a continuation character of an identifier
|
/// True if `c` can be a continuation character of an identifier
|
||||||
fn is_identifier_continue(c: char) -> bool {
|
pub(crate) fn is_identifier_continue(c: char) -> bool {
|
||||||
Self::is_identifier_start(c) || matches!(c, '0'..='9' | '-')
|
Self::is_identifier_start(c) || matches!(c, '0'..='9' | '-')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
18
src/lib.rs
18
src/lib.rs
@ -25,14 +25,15 @@ pub(crate) use {
|
|||||||
evaluator::Evaluator, expression::Expression, fragment::Fragment, function::Function,
|
evaluator::Evaluator, expression::Expression, fragment::Fragment, function::Function,
|
||||||
interrupt_guard::InterruptGuard, interrupt_handler::InterruptHandler, item::Item,
|
interrupt_guard::InterruptGuard, interrupt_handler::InterruptHandler, item::Item,
|
||||||
justfile::Justfile, keyed::Keyed, keyword::Keyword, lexer::Lexer, line::Line, list::List,
|
justfile::Justfile, keyed::Keyed, keyword::Keyword, lexer::Lexer, line::Line, list::List,
|
||||||
load_dotenv::load_dotenv, loader::Loader, name::Name, namepath::Namepath, ordinal::Ordinal,
|
load_dotenv::load_dotenv, loader::Loader, module_path::ModulePath, name::Name,
|
||||||
output::output, output_error::OutputError, parameter::Parameter, parameter_kind::ParameterKind,
|
namepath::Namepath, ordinal::Ordinal, output::output, output_error::OutputError,
|
||||||
parser::Parser, platform::Platform, platform_interface::PlatformInterface, position::Position,
|
parameter::Parameter, parameter_kind::ParameterKind, parser::Parser, platform::Platform,
|
||||||
positional::Positional, ran::Ran, range_ext::RangeExt, recipe::Recipe,
|
platform_interface::PlatformInterface, position::Position, positional::Positional, ran::Ran,
|
||||||
recipe_context::RecipeContext, recipe_resolver::RecipeResolver,
|
range_ext::RangeExt, recipe::Recipe, recipe_context::RecipeContext,
|
||||||
recipe_signature::RecipeSignature, scope::Scope, search::Search, search_config::SearchConfig,
|
recipe_resolver::RecipeResolver, recipe_signature::RecipeSignature, scope::Scope,
|
||||||
search_error::SearchError, set::Set, setting::Setting, settings::Settings, shebang::Shebang,
|
search::Search, search_config::SearchConfig, search_error::SearchError, set::Set,
|
||||||
shell::Shell, show_whitespace::ShowWhitespace, source::Source, string_kind::StringKind,
|
setting::Setting, settings::Settings, shebang::Shebang, shell::Shell,
|
||||||
|
show_whitespace::ShowWhitespace, source::Source, string_kind::StringKind,
|
||||||
string_literal::StringLiteral, subcommand::Subcommand, suggestion::Suggestion, table::Table,
|
string_literal::StringLiteral, subcommand::Subcommand, suggestion::Suggestion, table::Table,
|
||||||
thunk::Thunk, token::Token, token_kind::TokenKind, unresolved_dependency::UnresolvedDependency,
|
thunk::Thunk, token::Token, token_kind::TokenKind, unresolved_dependency::UnresolvedDependency,
|
||||||
unresolved_recipe::UnresolvedRecipe, use_color::UseColor, variables::Variables,
|
unresolved_recipe::UnresolvedRecipe, use_color::UseColor, variables::Variables,
|
||||||
@ -152,6 +153,7 @@ mod line;
|
|||||||
mod list;
|
mod list;
|
||||||
mod load_dotenv;
|
mod load_dotenv;
|
||||||
mod loader;
|
mod loader;
|
||||||
|
mod module_path;
|
||||||
mod name;
|
mod name;
|
||||||
mod namepath;
|
mod namepath;
|
||||||
mod ordinal;
|
mod ordinal;
|
||||||
|
100
src/module_path.rs
Normal file
100
src/module_path.rs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub(crate) struct ModulePath {
|
||||||
|
pub(crate) path: Vec<String>,
|
||||||
|
pub(crate) spaced: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&[&str]> for ModulePath {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(path: &[&str]) -> Result<Self, Self::Error> {
|
||||||
|
let spaced = path.len() > 1;
|
||||||
|
|
||||||
|
let path = if path.len() == 1 {
|
||||||
|
let first = path[0];
|
||||||
|
|
||||||
|
if first.starts_with(':') || first.ends_with(':') || first.contains(":::") {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
first
|
||||||
|
.split("::")
|
||||||
|
.map(str::to_string)
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
} else {
|
||||||
|
path.iter().map(|s| (*s).to_string()).collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
for name in &path {
|
||||||
|
if name.is_empty() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, c) in name.chars().enumerate() {
|
||||||
|
if i == 0 {
|
||||||
|
if !Lexer::is_identifier_start(c) {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
} else if !Lexer::is_identifier_continue(c) {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self { path, spaced })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ModulePath {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
for (i, name) in self.path.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
if self.spaced {
|
||||||
|
write!(f, " ")?;
|
||||||
|
} else {
|
||||||
|
write!(f, "::")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write!(f, "{name}")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn try_from_ok() {
|
||||||
|
#[track_caller]
|
||||||
|
fn case(path: &[&str], expected: &[&str], display: &str) {
|
||||||
|
let actual = ModulePath::try_from(path).unwrap();
|
||||||
|
assert_eq!(actual.path, expected);
|
||||||
|
assert_eq!(actual.to_string(), display);
|
||||||
|
}
|
||||||
|
|
||||||
|
case(&[], &[], "");
|
||||||
|
case(&["foo"], &["foo"], "foo");
|
||||||
|
case(&["foo0"], &["foo0"], "foo0");
|
||||||
|
case(&["foo", "bar"], &["foo", "bar"], "foo bar");
|
||||||
|
case(&["foo::bar"], &["foo", "bar"], "foo::bar");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn try_from_err() {
|
||||||
|
#[track_caller]
|
||||||
|
fn case(path: &[&str]) {
|
||||||
|
assert!(ModulePath::try_from(path).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
case(&[":foo"]);
|
||||||
|
case(&["foo:"]);
|
||||||
|
case(&["foo:::bar"]);
|
||||||
|
case(&["0foo"]);
|
||||||
|
case(&["f$oo"]);
|
||||||
|
case(&[""]);
|
||||||
|
}
|
||||||
|
}
|
@ -18,11 +18,11 @@ use {super::*, TokenKind::*};
|
|||||||
/// All methods starting with `parse_*` parse and return a language construct.
|
/// All methods starting with `parse_*` parse and return a language construct.
|
||||||
///
|
///
|
||||||
/// The parser tracks an expected set of tokens as it parses. This set contains
|
/// The parser tracks an expected set of tokens as it parses. This set contains
|
||||||
/// all tokens which would have been accepted at the current point in the parse.
|
/// all tokens which would have been accepted at the current point in the
|
||||||
/// Whenever the parser tests for a token that would be accepted, but does not
|
/// parse. Whenever the parser tests for a token that would be accepted, but
|
||||||
/// find it, it adds that token to the set. When the parser accepts a token, the
|
/// does not find it, it adds that token to the set. When the parser accepts a
|
||||||
/// set is cleared. If the parser finds a token which is unexpected, the
|
/// token, the set is cleared. If the parser finds a token which is unexpected,
|
||||||
/// contents of the set is printed in the resultant error message.
|
/// the elements of the set are printed in the resultant error message.
|
||||||
pub(crate) struct Parser<'run, 'src> {
|
pub(crate) struct Parser<'run, 'src> {
|
||||||
expected_tokens: BTreeSet<TokenKind>,
|
expected_tokens: BTreeSet<TokenKind>,
|
||||||
file_depth: u32,
|
file_depth: u32,
|
||||||
|
@ -147,9 +147,7 @@ impl<'src, D> Recipe<'src, D> {
|
|||||||
pub(crate) fn run<'run>(
|
pub(crate) fn run<'run>(
|
||||||
&self,
|
&self,
|
||||||
context: &RecipeContext<'src, 'run>,
|
context: &RecipeContext<'src, 'run>,
|
||||||
dotenv: &BTreeMap<String, String>,
|
scope: &Scope<'src, 'run>,
|
||||||
scope: Scope<'src, 'run>,
|
|
||||||
search: &'run Search,
|
|
||||||
positional: &[String],
|
positional: &[String],
|
||||||
) -> RunResult<'src, ()> {
|
) -> RunResult<'src, ()> {
|
||||||
let config = &context.config;
|
let config = &context.config;
|
||||||
@ -164,20 +162,25 @@ impl<'src, D> Recipe<'src, D> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let evaluator =
|
let evaluator = Evaluator::recipe_evaluator(
|
||||||
Evaluator::recipe_evaluator(context.config, dotenv, &scope, context.settings, search);
|
context.config,
|
||||||
|
context.dotenv,
|
||||||
|
context.module_source,
|
||||||
|
scope,
|
||||||
|
context.search,
|
||||||
|
context.settings,
|
||||||
|
);
|
||||||
|
|
||||||
if self.shebang {
|
if self.shebang {
|
||||||
self.run_shebang(context, dotenv, &scope, positional, config, evaluator)
|
self.run_shebang(context, scope, positional, config, evaluator)
|
||||||
} else {
|
} else {
|
||||||
self.run_linewise(context, dotenv, &scope, positional, config, evaluator)
|
self.run_linewise(context, scope, positional, config, evaluator)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_linewise<'run>(
|
fn run_linewise<'run>(
|
||||||
&self,
|
&self,
|
||||||
context: &RecipeContext<'src, 'run>,
|
context: &RecipeContext<'src, 'run>,
|
||||||
dotenv: &BTreeMap<String, String>,
|
|
||||||
scope: &Scope<'src, 'run>,
|
scope: &Scope<'src, 'run>,
|
||||||
positional: &[String],
|
positional: &[String],
|
||||||
config: &Config,
|
config: &Config,
|
||||||
@ -234,12 +237,24 @@ impl<'src, D> Recipe<'src, D> {
|
|||||||
|| (context.settings.quiet && !self.no_quiet())
|
|| (context.settings.quiet && !self.no_quiet())
|
||||||
|| config.verbosity.quiet())
|
|| config.verbosity.quiet())
|
||||||
{
|
{
|
||||||
let color = if config.highlight {
|
let color = config
|
||||||
config.color.command(config.command_color)
|
.highlight
|
||||||
} else {
|
.then(|| config.color.command(config.command_color))
|
||||||
config.color
|
.unwrap_or(config.color)
|
||||||
};
|
.stderr();
|
||||||
eprintln!("{}", color.stderr().paint(command));
|
|
||||||
|
if config.timestamp {
|
||||||
|
eprint!(
|
||||||
|
"[{}] ",
|
||||||
|
color.paint(
|
||||||
|
&chrono::Local::now()
|
||||||
|
.format(&config.timestamp_format)
|
||||||
|
.to_string()
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
eprintln!("{}", color.paint(command));
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.dry_run {
|
if config.dry_run {
|
||||||
@ -264,7 +279,7 @@ impl<'src, D> Recipe<'src, D> {
|
|||||||
cmd.stdout(Stdio::null());
|
cmd.stdout(Stdio::null());
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.export(context.settings, dotenv, scope);
|
cmd.export(context.settings, context.dotenv, scope);
|
||||||
|
|
||||||
match InterruptHandler::guard(|| cmd.status()) {
|
match InterruptHandler::guard(|| cmd.status()) {
|
||||||
Ok(exit_status) => {
|
Ok(exit_status) => {
|
||||||
@ -298,7 +313,6 @@ impl<'src, D> Recipe<'src, D> {
|
|||||||
pub(crate) fn run_shebang<'run>(
|
pub(crate) fn run_shebang<'run>(
|
||||||
&self,
|
&self,
|
||||||
context: &RecipeContext<'src, 'run>,
|
context: &RecipeContext<'src, 'run>,
|
||||||
dotenv: &BTreeMap<String, String>,
|
|
||||||
scope: &Scope<'src, 'run>,
|
scope: &Scope<'src, 'run>,
|
||||||
positional: &[String],
|
positional: &[String],
|
||||||
config: &Config,
|
config: &Config,
|
||||||
@ -411,7 +425,7 @@ impl<'src, D> Recipe<'src, D> {
|
|||||||
command.args(positional);
|
command.args(positional);
|
||||||
}
|
}
|
||||||
|
|
||||||
command.export(context.settings, dotenv, scope);
|
command.export(context.settings, context.dotenv, scope);
|
||||||
|
|
||||||
// run it!
|
// run it!
|
||||||
match InterruptHandler::guard(|| command.status()) {
|
match InterruptHandler::guard(|| command.status()) {
|
||||||
|
@ -2,6 +2,8 @@ use super::*;
|
|||||||
|
|
||||||
pub(crate) struct RecipeContext<'src: 'run, 'run> {
|
pub(crate) struct RecipeContext<'src: 'run, 'run> {
|
||||||
pub(crate) config: &'run Config,
|
pub(crate) config: &'run Config,
|
||||||
|
pub(crate) dotenv: &'run BTreeMap<String, String>,
|
||||||
|
pub(crate) module_source: &'run Path,
|
||||||
pub(crate) scope: &'run Scope<'src, 'run>,
|
pub(crate) scope: &'run Scope<'src, 'run>,
|
||||||
pub(crate) search: &'run Search,
|
pub(crate) search: &'run Search,
|
||||||
pub(crate) settings: &'run Settings<'src>,
|
pub(crate) settings: &'run Settings<'src>,
|
||||||
|
@ -31,14 +31,16 @@ pub(crate) enum Subcommand {
|
|||||||
Format,
|
Format,
|
||||||
Groups,
|
Groups,
|
||||||
Init,
|
Init,
|
||||||
List,
|
List {
|
||||||
|
path: ModulePath,
|
||||||
|
},
|
||||||
Man,
|
Man,
|
||||||
Run {
|
Run {
|
||||||
arguments: Vec<String>,
|
arguments: Vec<String>,
|
||||||
overrides: BTreeMap<String, String>,
|
overrides: BTreeMap<String, String>,
|
||||||
},
|
},
|
||||||
Show {
|
Show {
|
||||||
name: String,
|
path: ModulePath,
|
||||||
},
|
},
|
||||||
Summary,
|
Summary,
|
||||||
Variables,
|
Variables,
|
||||||
@ -88,8 +90,8 @@ impl Subcommand {
|
|||||||
Dump => Self::dump(config, ast, justfile)?,
|
Dump => Self::dump(config, ast, justfile)?,
|
||||||
Format => Self::format(config, &search, src, ast)?,
|
Format => Self::format(config, &search, src, ast)?,
|
||||||
Groups => Self::groups(config, justfile),
|
Groups => Self::groups(config, justfile),
|
||||||
List => Self::list(config, 0, justfile),
|
List { path } => Self::list(config, justfile, path)?,
|
||||||
Show { ref name } => Self::show(config, name, justfile)?,
|
Show { path } => Self::show(config, justfile, path)?,
|
||||||
Summary => Self::summary(config, justfile),
|
Summary => Self::summary(config, justfile),
|
||||||
Variables => Self::variables(justfile),
|
Variables => Self::variables(justfile),
|
||||||
Changelog | Completions { .. } | Edit | Init | Man | Run { .. } => unreachable!(),
|
Changelog | Completions { .. } | Edit | Init | Man | Run { .. } => unreachable!(),
|
||||||
@ -477,16 +479,19 @@ impl Subcommand {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list(config: &Config, level: usize, justfile: &Justfile) {
|
fn list(config: &Config, mut module: &Justfile, path: &ModulePath) -> Result<(), Error<'static>> {
|
||||||
|
for name in &path.path {
|
||||||
|
module = module
|
||||||
|
.modules
|
||||||
|
.get(name)
|
||||||
|
.ok_or_else(|| Error::UnknownSubmodule { path: path.clone() })?;
|
||||||
|
}
|
||||||
|
|
||||||
let aliases = if config.no_aliases {
|
let aliases = if config.no_aliases {
|
||||||
BTreeMap::new()
|
BTreeMap::new()
|
||||||
} else {
|
} else {
|
||||||
let mut aliases = BTreeMap::<&str, Vec<&str>>::new();
|
let mut aliases = BTreeMap::<&str, Vec<&str>>::new();
|
||||||
for alias in justfile
|
for alias in module.aliases.values().filter(|alias| !alias.is_private()) {
|
||||||
.aliases
|
|
||||||
.values()
|
|
||||||
.filter(|alias| !alias.is_private())
|
|
||||||
{
|
|
||||||
aliases
|
aliases
|
||||||
.entry(alias.target.name.lexeme())
|
.entry(alias.target.name.lexeme())
|
||||||
.or_default()
|
.or_default()
|
||||||
@ -498,7 +503,7 @@ impl Subcommand {
|
|||||||
let signature_widths = {
|
let signature_widths = {
|
||||||
let mut signature_widths: BTreeMap<&str, usize> = BTreeMap::new();
|
let mut signature_widths: BTreeMap<&str, usize> = BTreeMap::new();
|
||||||
|
|
||||||
for (name, recipe) in &justfile.recipes {
|
for (name, recipe) in &module.recipes {
|
||||||
if !recipe.is_public() {
|
if !recipe.is_public() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -526,13 +531,11 @@ impl Subcommand {
|
|||||||
.max()
|
.max()
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
if level == 0 {
|
print!("{}", config.list_heading);
|
||||||
print!("{}", config.list_heading);
|
|
||||||
}
|
|
||||||
|
|
||||||
let groups = {
|
let groups = {
|
||||||
let mut groups = BTreeMap::<Option<String>, Vec<&Recipe>>::new();
|
let mut groups = BTreeMap::<Option<String>, Vec<&Recipe>>::new();
|
||||||
for recipe in justfile.public_recipes(config) {
|
for recipe in module.public_recipes(config) {
|
||||||
let recipe_groups = recipe.groups();
|
let recipe_groups = recipe.groups();
|
||||||
if recipe_groups.is_empty() {
|
if recipe_groups.is_empty() {
|
||||||
groups.entry(None).or_default().push(recipe);
|
groups.entry(None).or_default().push(recipe);
|
||||||
@ -553,7 +556,7 @@ impl Subcommand {
|
|||||||
let no_groups = groups.contains_key(&None) && groups.len() == 1;
|
let no_groups = groups.contains_key(&None) && groups.len() == 1;
|
||||||
|
|
||||||
if !no_groups {
|
if !no_groups {
|
||||||
print!("{}", config.list_prefix.repeat(level + 1));
|
print!("{}", config.list_prefix);
|
||||||
if let Some(group_name) = group {
|
if let Some(group_name) = group {
|
||||||
println!("[{group_name}]");
|
println!("[{group_name}]");
|
||||||
} else {
|
} else {
|
||||||
@ -577,7 +580,7 @@ impl Subcommand {
|
|||||||
for line in doc.lines() {
|
for line in doc.lines() {
|
||||||
println!(
|
println!(
|
||||||
"{}{} {}",
|
"{}{} {}",
|
||||||
config.list_prefix.repeat(level + 1),
|
config.list_prefix,
|
||||||
config.color.stdout().doc().paint("#"),
|
config.color.stdout().doc().paint("#"),
|
||||||
config.color.stdout().doc().paint(line),
|
config.color.stdout().doc().paint(line),
|
||||||
);
|
);
|
||||||
@ -587,7 +590,7 @@ impl Subcommand {
|
|||||||
|
|
||||||
print!(
|
print!(
|
||||||
"{}{}",
|
"{}{}",
|
||||||
config.list_prefix.repeat(level + 1),
|
config.list_prefix,
|
||||||
RecipeSignature { name, recipe }.color_display(config.color.stdout())
|
RecipeSignature { name, recipe }.color_display(config.color.stdout())
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -607,29 +610,39 @@ impl Subcommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i, module) in justfile.modules(config).into_iter().enumerate() {
|
for submodule in module.modules(config) {
|
||||||
if i + groups.len() > 0 {
|
println!("{}{} ...", config.list_prefix, submodule.name(),);
|
||||||
println!();
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("{}{}:", config.list_prefix.repeat(level + 1), module.name());
|
|
||||||
Self::list(config, level + 1, module);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show<'src>(config: &Config, name: &str, justfile: &Justfile<'src>) -> Result<(), Error<'src>> {
|
fn show<'src>(
|
||||||
if let Some(alias) = justfile.get_alias(name) {
|
config: &Config,
|
||||||
let recipe = justfile.get_recipe(alias.target.name.lexeme()).unwrap();
|
mut module: &Justfile<'src>,
|
||||||
|
path: &ModulePath,
|
||||||
|
) -> Result<(), Error<'src>> {
|
||||||
|
for name in &path.path[0..path.path.len() - 1] {
|
||||||
|
module = module
|
||||||
|
.modules
|
||||||
|
.get(name)
|
||||||
|
.ok_or_else(|| Error::UnknownSubmodule { path: path.clone() })?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = path.path.last().unwrap();
|
||||||
|
|
||||||
|
if let Some(alias) = module.get_alias(name) {
|
||||||
|
let recipe = module.get_recipe(alias.target.name.lexeme()).unwrap();
|
||||||
println!("{alias}");
|
println!("{alias}");
|
||||||
println!("{}", recipe.color_display(config.color.stdout()));
|
println!("{}", recipe.color_display(config.color.stdout()));
|
||||||
Ok(())
|
Ok(())
|
||||||
} else if let Some(recipe) = justfile.get_recipe(name) {
|
} else if let Some(recipe) = module.get_recipe(name) {
|
||||||
println!("{}", recipe.color_display(config.color.stdout()));
|
println!("{}", recipe.color_display(config.color.stdout()));
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::UnknownRecipes {
|
Err(Error::UnknownRecipes {
|
||||||
recipes: vec![name.to_owned()],
|
recipes: vec![name.to_owned()],
|
||||||
suggestion: justfile.suggest_recipe(name),
|
suggestion: module.suggest_recipe(name),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -904,3 +904,126 @@ fn source_directory() {
|
|||||||
.stdout_regex(r".*[/\\]foo\n")
|
.stdout_regex(r".*[/\\]foo\n")
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn module_paths() {
|
||||||
|
Test::new()
|
||||||
|
.write(
|
||||||
|
"foo/bar.just",
|
||||||
|
"
|
||||||
|
imf := module_file()
|
||||||
|
imd := module_directory()
|
||||||
|
|
||||||
|
import-outer: import-inner
|
||||||
|
|
||||||
|
@import-inner pmf=module_file() pmd=module_directory():
|
||||||
|
echo import
|
||||||
|
echo '{{ imf }}'
|
||||||
|
echo '{{ imd }}'
|
||||||
|
echo '{{ pmf }}'
|
||||||
|
echo '{{ pmd }}'
|
||||||
|
echo '{{ module_file() }}'
|
||||||
|
echo '{{ module_directory() }}'
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.write(
|
||||||
|
"baz/mod.just",
|
||||||
|
"
|
||||||
|
import 'foo/bar.just'
|
||||||
|
|
||||||
|
mmf := module_file()
|
||||||
|
mmd := module_directory()
|
||||||
|
|
||||||
|
outer: inner
|
||||||
|
|
||||||
|
@inner pmf=module_file() pmd=module_directory():
|
||||||
|
echo module
|
||||||
|
echo '{{ mmf }}'
|
||||||
|
echo '{{ mmd }}'
|
||||||
|
echo '{{ pmf }}'
|
||||||
|
echo '{{ pmd }}'
|
||||||
|
echo '{{ module_file() }}'
|
||||||
|
echo '{{ module_directory() }}'
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.write(
|
||||||
|
"baz/foo/bar.just",
|
||||||
|
"
|
||||||
|
imf := module_file()
|
||||||
|
imd := module_directory()
|
||||||
|
|
||||||
|
import-outer: import-inner
|
||||||
|
|
||||||
|
@import-inner pmf=module_file() pmd=module_directory():
|
||||||
|
echo import
|
||||||
|
echo '{{ imf }}'
|
||||||
|
echo '{{ imd }}'
|
||||||
|
echo '{{ pmf }}'
|
||||||
|
echo '{{ pmd }}'
|
||||||
|
echo '{{ module_file() }}'
|
||||||
|
echo '{{ module_directory() }}'
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
import 'foo/bar.just'
|
||||||
|
mod baz
|
||||||
|
|
||||||
|
rmf := module_file()
|
||||||
|
rmd := module_directory()
|
||||||
|
|
||||||
|
outer: inner
|
||||||
|
|
||||||
|
@inner pmf=module_file() pmd=module_directory():
|
||||||
|
echo root
|
||||||
|
echo '{{ rmf }}'
|
||||||
|
echo '{{ rmd }}'
|
||||||
|
echo '{{ pmf }}'
|
||||||
|
echo '{{ pmd }}'
|
||||||
|
echo '{{ module_file() }}'
|
||||||
|
echo '{{ module_directory() }}'
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.test_round_trip(false)
|
||||||
|
.args([
|
||||||
|
"--unstable",
|
||||||
|
"outer",
|
||||||
|
"import-outer",
|
||||||
|
"baz",
|
||||||
|
"outer",
|
||||||
|
"baz",
|
||||||
|
"import-outer",
|
||||||
|
])
|
||||||
|
.stdout_regex(
|
||||||
|
r"root
|
||||||
|
.*[/\\]just-test-tempdir......[/\\]justfile
|
||||||
|
.*[/\\]just-test-tempdir......
|
||||||
|
.*[/\\]just-test-tempdir......[/\\]justfile
|
||||||
|
.*[/\\]just-test-tempdir......
|
||||||
|
.*[/\\]just-test-tempdir......[/\\]justfile
|
||||||
|
.*[/\\]just-test-tempdir......
|
||||||
|
import
|
||||||
|
.*[/\\]just-test-tempdir......[/\\]justfile
|
||||||
|
.*[/\\]just-test-tempdir......
|
||||||
|
.*[/\\]just-test-tempdir......[/\\]justfile
|
||||||
|
.*[/\\]just-test-tempdir......
|
||||||
|
.*[/\\]just-test-tempdir......[/\\]justfile
|
||||||
|
.*[/\\]just-test-tempdir......
|
||||||
|
module
|
||||||
|
.*[/\\]just-test-tempdir......[/\\]baz[/\\]mod.just
|
||||||
|
.*[/\\]just-test-tempdir......[/\\]baz
|
||||||
|
.*[/\\]just-test-tempdir......[/\\]baz[/\\]mod.just
|
||||||
|
.*[/\\]just-test-tempdir......[/\\]baz
|
||||||
|
.*[/\\]just-test-tempdir......[/\\]baz[/\\]mod.just
|
||||||
|
.*[/\\]just-test-tempdir......[/\\]baz
|
||||||
|
import
|
||||||
|
.*[/\\]just-test-tempdir......[/\\]baz[/\\]mod.just
|
||||||
|
.*[/\\]just-test-tempdir......[/\\]baz
|
||||||
|
.*[/\\]just-test-tempdir......[/\\]baz[/\\]mod.just
|
||||||
|
.*[/\\]just-test-tempdir......[/\\]baz
|
||||||
|
.*[/\\]just-test-tempdir......[/\\]baz[/\\]mod.just
|
||||||
|
.*[/\\]just-test-tempdir......[/\\]baz
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
@ -144,31 +144,3 @@ fn list_groups_with_custom_prefix() {
|
|||||||
)
|
)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn list_with_groups_in_modules() {
|
|
||||||
Test::new()
|
|
||||||
.justfile(
|
|
||||||
"
|
|
||||||
[group('FOO')]
|
|
||||||
foo:
|
|
||||||
|
|
||||||
mod bar
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.write("bar.just", "[group('BAZ')]\nbaz:")
|
|
||||||
.test_round_trip(false)
|
|
||||||
.args(["--unstable", "--list"])
|
|
||||||
.stdout(
|
|
||||||
"
|
|
||||||
Available recipes:
|
|
||||||
[FOO]
|
|
||||||
foo
|
|
||||||
|
|
||||||
bar:
|
|
||||||
[BAZ]
|
|
||||||
baz
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
@ -102,6 +102,7 @@ mod string;
|
|||||||
mod subsequents;
|
mod subsequents;
|
||||||
mod summary;
|
mod summary;
|
||||||
mod tempdir;
|
mod tempdir;
|
||||||
|
mod timestamps;
|
||||||
mod undefined_variables;
|
mod undefined_variables;
|
||||||
mod unstable;
|
mod unstable;
|
||||||
#[cfg(target_family = "windows")]
|
#[cfg(target_family = "windows")]
|
||||||
|
189
tests/list.rs
189
tests/list.rs
@ -1,54 +1,5 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn list_displays_recipes_in_submodules() {
|
|
||||||
Test::new()
|
|
||||||
.write("foo.just", "bar:\n @echo FOO")
|
|
||||||
.justfile(
|
|
||||||
"
|
|
||||||
mod foo
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.test_round_trip(false)
|
|
||||||
.arg("--unstable")
|
|
||||||
.arg("--list")
|
|
||||||
.stdout(
|
|
||||||
"
|
|
||||||
Available recipes:
|
|
||||||
foo:
|
|
||||||
bar
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn modules_are_space_separated_in_output() {
|
|
||||||
Test::new()
|
|
||||||
.write("foo.just", "foo:")
|
|
||||||
.write("bar.just", "bar:")
|
|
||||||
.justfile(
|
|
||||||
"
|
|
||||||
mod foo
|
|
||||||
|
|
||||||
mod bar
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.test_round_trip(false)
|
|
||||||
.args(["--unstable", "--list"])
|
|
||||||
.stdout(
|
|
||||||
"
|
|
||||||
Available recipes:
|
|
||||||
bar:
|
|
||||||
bar
|
|
||||||
|
|
||||||
foo:
|
|
||||||
foo
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn modules_unsorted() {
|
fn modules_unsorted() {
|
||||||
Test::new()
|
Test::new()
|
||||||
@ -66,72 +17,13 @@ fn modules_unsorted() {
|
|||||||
.stdout(
|
.stdout(
|
||||||
"
|
"
|
||||||
Available recipes:
|
Available recipes:
|
||||||
foo:
|
foo ...
|
||||||
foo
|
bar ...
|
||||||
|
|
||||||
bar:
|
|
||||||
bar
|
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn module_recipe_list_alignment_ignores_private_recipes() {
|
|
||||||
Test::new()
|
|
||||||
.write(
|
|
||||||
"foo.just",
|
|
||||||
"
|
|
||||||
# foos
|
|
||||||
foo:
|
|
||||||
@echo FOO
|
|
||||||
|
|
||||||
[private]
|
|
||||||
barbarbar:
|
|
||||||
@echo BAR
|
|
||||||
|
|
||||||
@_bazbazbaz:
|
|
||||||
@echo BAZ
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.justfile("mod foo")
|
|
||||||
.test_round_trip(false)
|
|
||||||
.arg("--unstable")
|
|
||||||
.arg("--list")
|
|
||||||
.stdout(
|
|
||||||
"
|
|
||||||
Available recipes:
|
|
||||||
foo:
|
|
||||||
foo # foos
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn nested_modules_are_properly_indented() {
|
|
||||||
Test::new()
|
|
||||||
.write("foo.just", "mod bar")
|
|
||||||
.write("bar.just", "baz:\n @echo FOO")
|
|
||||||
.justfile(
|
|
||||||
"
|
|
||||||
mod foo
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.test_round_trip(false)
|
|
||||||
.arg("--unstable")
|
|
||||||
.arg("--list")
|
|
||||||
.stdout(
|
|
||||||
"
|
|
||||||
Available recipes:
|
|
||||||
foo:
|
|
||||||
bar:
|
|
||||||
baz
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unsorted_list_order() {
|
fn unsorted_list_order() {
|
||||||
Test::new()
|
Test::new()
|
||||||
@ -254,3 +146,80 @@ fn unsorted_list_order() {
|
|||||||
)
|
)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_submodule() {
|
||||||
|
Test::new()
|
||||||
|
.write("foo.just", "bar:")
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
mod foo
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.test_round_trip(false)
|
||||||
|
.args(["--unstable", "--list", "foo"])
|
||||||
|
.stdout(
|
||||||
|
"
|
||||||
|
Available recipes:
|
||||||
|
bar
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_nested_submodule() {
|
||||||
|
Test::new()
|
||||||
|
.write("foo.just", "mod bar")
|
||||||
|
.write("bar.just", "baz:")
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
mod foo
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.test_round_trip(false)
|
||||||
|
.args(["--unstable", "--list", "foo", "bar"])
|
||||||
|
.stdout(
|
||||||
|
"
|
||||||
|
Available recipes:
|
||||||
|
baz
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
Test::new()
|
||||||
|
.write("foo.just", "mod bar")
|
||||||
|
.write("bar.just", "baz:")
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
mod foo
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.test_round_trip(false)
|
||||||
|
.args(["--unstable", "--list", "foo::bar"])
|
||||||
|
.stdout(
|
||||||
|
"
|
||||||
|
Available recipes:
|
||||||
|
baz
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_invalid_path() {
|
||||||
|
Test::new()
|
||||||
|
.args(["--unstable", "--list", "$hello"])
|
||||||
|
.stderr("error: Invalid module path `$hello`\n")
|
||||||
|
.status(1)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_unknown_submodule() {
|
||||||
|
Test::new()
|
||||||
|
.args(["--unstable", "--list", "hello"])
|
||||||
|
.stderr("error: Justfile does not contain submodule `hello`\n")
|
||||||
|
.status(1)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
@ -100,3 +100,27 @@ a Z="\t z":
|
|||||||
stderr: "error: Justfile does not contain recipe `fooooooo`.\n",
|
stderr: "error: Justfile does not contain recipe `fooooooo`.\n",
|
||||||
status: EXIT_FAILURE,
|
status: EXIT_FAILURE,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn show_recipe_at_path() {
|
||||||
|
Test::new()
|
||||||
|
.write("foo.just", "bar:\n @echo MODULE")
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
mod foo
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.test_round_trip(false)
|
||||||
|
.args(["--unstable", "--show", "foo::bar"])
|
||||||
|
.stdout("bar:\n @echo MODULE\n")
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn show_invalid_path() {
|
||||||
|
Test::new()
|
||||||
|
.args(["--show", "$hello"])
|
||||||
|
.stderr("error: Invalid module path `$hello`\n")
|
||||||
|
.status(1)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
31
tests/timestamps.rs
Normal file
31
tests/timestamps.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn print_timestamps() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
recipe:
|
||||||
|
echo 'one'
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.arg("--timestamp")
|
||||||
|
.stderr_regex(concat!(r"\[\d\d:\d\d:\d\d\] echo 'one'", "\n"))
|
||||||
|
.stdout("one\n")
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn print_timestamps_with_format_string() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
recipe:
|
||||||
|
echo 'one'
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.args(["--timestamp", "--timestamp-format", "%H:%M:%S.%3f"])
|
||||||
|
.stderr_regex(concat!(r"\[\d\d:\d\d:\d\d\.\d\d\d] echo 'one'", "\n"))
|
||||||
|
.stdout("one\n")
|
||||||
|
.run();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user