Compare commits

...

10 Commits

Author SHA1 Message Date
Greg Shuflin
b81d106c91 Parse module 2024-07-09 09:39:53 -07:00
Casey Rodarmor
458805e283
Allow mod path to be directory containing module source (#2238) 2024-07-08 22:38:25 +00:00
Casey Rodarmor
d6669e0b97
Allow enabling unstable features with set unstable (#2237) 2024-07-08 03:45:03 +00:00
Casey Rodarmor
564814208f
Lexiclean search directory so .. does not check the current directory (#2236)
If the search directory was `..`, for example in the invocation
`just ../foo`, we would wind up checking the justfile in the current
directory since we did `INVOCATION_DIRECTORY/..`.ancestors(), which
would first return `INVOCATION_DIRECTORY`.

Instead, lexiclean the result of joining th invocation directory with
the search directory, so `..` is removed, and `ancestors()` doesn't
return the invocation directory.
2024-07-08 02:12:07 +00:00
Casey Rodarmor
f1020b4e6a
Allow abbreviating functions ending in _directory to _dir (#2235) 2024-07-07 22:47:18 +00:00
Casey Rodarmor
5e9f46e855
Release 1.30.1 (#2232)
- Bump version: 1.30.0 → 1.30.1
- Update changelog
- Update changelog contributor credits
2024-07-07 04:24:32 +00:00
Casey Rodarmor
241e7b46a5
Fix function argument count mismatch error message (#2231) 2024-07-07 04:19:36 +00:00
Casey Rodarmor
0c9b159aa4
Bump version to 1.30.0 (#2229) 2024-07-06 23:15:18 +00:00
Casey Rodarmor
d2f66815da
Release 1.30.0 (#2228)
- Bump version: 1.29.1 → 1.30.0
- Update changelog
- Update changelog contributor credits
- Update dependencies
- Update version references in readme
2024-07-06 23:11:25 +00:00
Casey Rodarmor
50e8874e0e
Tweak readme (#2227) 2024-07-06 23:03:08 +00:00
31 changed files with 611 additions and 224 deletions

View File

@ -1,6 +1,56 @@
Changelog Changelog
========= =========
[1.30.1](https://github.com/casey/just/releases/tag/1.30.1) - 2024-07-06
------------------------------------------------------------------------
### Fixed
- Fix function argument count mismatch error message ([#2231](https://github.com/casey/just/pull/2231) by [casey](https://github.com/casey))
[1.30.0](https://github.com/casey/just/releases/tag/1.30.0) - 2024-07-06
------------------------------------------------------------------------
### Fixed
- Allow comments after `mod` statements ([#2201](https://github.com/casey/just/pull/2201) by [casey](https://github.com/casey))
### Changed
- Allow unstable features with `--summary` ([#2210](https://github.com/casey/just/pull/2210) by [casey](https://github.com/casey))
- Don't analyze comments when `ignore-comments` is set ([#2180](https://github.com/casey/just/pull/2180) by [casey](https://github.com/casey))
- List recipes by group in group justfile order with `just --list --unsorted` ([#2164](https://github.com/casey/just/pull/2164) by [casey](https://github.com/casey))
- List groups in source order with `just --groups --unsorted` ([#2160](https://github.com/casey/just/pull/2160) by [casey](https://github.com/casey))
### Added
- Avoid `install` and add 32-bit arm targets to `install.sh` ([#2214](https://github.com/casey/just/pull/2214) by [CramBL](https://github.com/CramBL))
- Give modules doc comments for `--list` ([#2199](https://github.com/casey/just/pull/2199) by [Spatenheinz](https://github.com/Spatenheinz))
- Add `datetime()` and `datetime_utc()` functions ([#2167](https://github.com/casey/just/pull/2167) by [casey](https://github.com/casey))
- Allow setting more command-line options with environment variables ([#2161](https://github.com/casey/just/pull/2161) by [casey](https://github.com/casey))
### Library
- Don't exit process in `run()` on argument parse error ([#2176](https://github.com/casey/just/pull/2176) by [casey](https://github.com/casey))
- Allow passing command-line arguments into `run()` ([#2173](https://github.com/casey/just/pull/2173) by [casey](https://github.com/casey))
- Ignore env_logger initialization errors ([#2170](https://github.com/casey/just/pull/2170) by [EnigmaCurry](https://github.com/EnigmaCurry))
### Misc
- Tweak readme ([#2227](https://github.com/casey/just/pull/2227) by [casey](https://github.com/casey))
- Add development guide to readme ([#2226](https://github.com/casey/just/pull/2226) by [casey](https://github.com/casey))
- Add shell-expanded string syntax to grammar ([#2223](https://github.com/casey/just/pull/2223) by [casey](https://github.com/casey))
- Add recipe for testing bash completion script ([#2221](https://github.com/casey/just/pull/2221) by [casey](https://github.com/casey))
- Fix use of `justfile_directory()` in readme ([#2219](https://github.com/casey/just/pull/2219) by [casey](https://github.com/casey))
- Use default values for `--list-heading` and `--list-prefix` ([#2213](https://github.com/casey/just/pull/2213) by [casey](https://github.com/casey))
- Use `clap::ValueParser` ([#2211](https://github.com/casey/just/pull/2211) by [neunenak](https://github.com/neunenak))
- Document module doc comments in readme ([#2208](https://github.com/casey/just/pull/2208) by [casey](https://github.com/casey))
- Use `-and` instead of `&&` in PowerShell completion script ([#2204](https://github.com/casey/just/pull/2204) by [casey](https://github.com/casey))
- Fix readme formatting ([#2203](https://github.com/casey/just/pull/2203) by [casey](https://github.com/casey))
- Link to justfiles on GitHub in readme ([#2198](https://github.com/casey/just/pull/2198) by [bukowa](https://github.com/bukowa))
- Link to modules when first introduced in readme ([#2193](https://github.com/casey/just/pull/2193) by [casey](https://github.com/casey))
- Update `softprops/action-gh-release` ([#2183](https://github.com/casey/just/pull/2183) by [app/dependabot](https://github.com/app/dependabot))
- Document remote justfile workaround ([#2175](https://github.com/casey/just/pull/2175) by [casey](https://github.com/casey))
- Document library interface ([#2174](https://github.com/casey/just/pull/2174) by [casey](https://github.com/casey))
- Remove dependency on cradle ([#2169](https://github.com/casey/just/pull/2169) by [nc7s](https://github.com/nc7s))
- Add note to readme about quoting paths on Windows ([#2166](https://github.com/casey/just/pull/2166) by [casey](https://github.com/casey))
- Add missing changelog credits ([#2163](https://github.com/casey/just/pull/2163) by [casey](https://github.com/casey))
- Credit myself in changelog ([#2162](https://github.com/casey/just/pull/2162) by [casey](https://github.com/casey))
[1.29.1](https://github.com/casey/just/releases/tag/1.29.1) - 2024-06-14 [1.29.1](https://github.com/casey/just/releases/tag/1.29.1) - 2024-06-14
------------------------------------------------------------------------ ------------------------------------------------------------------------

158
Cargo.lock generated
View File

@ -121,9 +121,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.5.0" version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]] [[package]]
name = "blake3" name = "blake3"
@ -174,9 +174,9 @@ checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.99" version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@ -201,7 +201,7 @@ dependencies = [
"js-sys", "js-sys",
"num-traits", "num-traits",
"wasm-bindgen", "wasm-bindgen",
"windows-targets 0.52.5", "windows-targets 0.52.6",
] ]
[[package]] [[package]]
@ -221,9 +221,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.7" version = "4.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -231,9 +231,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.7" version = "4.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -244,23 +244,23 @@ dependencies = [
[[package]] [[package]]
name = "clap_complete" name = "clap_complete"
version = "4.5.5" version = "4.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2020fa13af48afc65a9a87335bda648309ab3d154cd03c7ff95b378c7ed39c4" checksum = "1d598e88f6874d4b888ed40c71efbcbf4076f1dfbae128a08a8c9e45f710605d"
dependencies = [ dependencies = [
"clap 4.5.7", "clap 4.5.8",
] ]
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.5" version = "4.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
dependencies = [ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.66", "syn 2.0.69",
] ]
[[package]] [[package]]
@ -271,11 +271,11 @@ checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
[[package]] [[package]]
name = "clap_mangen" name = "clap_mangen"
version = "0.2.21" version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74b70fc13e60c0e1d490dc50eb73a749be6d81f4ef03783df1d9b7b0c62bc937" checksum = "f50dde5bc0c853d6248de457e5eb6e5a674a54b93810a34ded88d882ca1fe2de"
dependencies = [ dependencies = [
"clap 4.5.7", "clap 4.5.8",
"roff", "roff",
] ]
@ -407,15 +407,15 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
[[package]] [[package]]
name = "edit-distance" name = "edit-distance"
version = "2.1.0" version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbbaaaf38131deb9ca518a274a45bfdb8771f139517b073b16c2d3d32ae5037b" checksum = "853fc7035888bd1c9320f3a05bfe7f344f49b8766a4bb4209b1ac5f0503d9577"
[[package]] [[package]]
name = "either" name = "either"
version = "1.12.0" version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]] [[package]]
name = "env_filter" name = "env_filter"
@ -591,13 +591,13 @@ dependencies = [
[[package]] [[package]]
name = "just" name = "just"
version = "1.29.1" version = "1.30.1"
dependencies = [ dependencies = [
"ansi_term", "ansi_term",
"blake3", "blake3",
"camino", "camino",
"chrono", "chrono",
"clap 4.5.7", "clap 4.5.8",
"clap_complete", "clap_complete",
"clap_mangen", "clap_mangen",
"ctrlc", "ctrlc",
@ -635,9 +635,9 @@ dependencies = [
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "lexiclean" name = "lexiclean"
@ -657,7 +657,7 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [ dependencies = [
"bitflags 2.5.0", "bitflags 2.6.0",
"libc", "libc",
] ]
@ -669,9 +669,9 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.21" version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]] [[package]]
name = "memchr" name = "memchr"
@ -694,7 +694,7 @@ version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
dependencies = [ dependencies = [
"bitflags 2.5.0", "bitflags 2.6.0",
"cfg-if", "cfg-if",
"cfg_aliases", "cfg_aliases",
"libc", "libc",
@ -779,9 +779,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.85" version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -792,7 +792,7 @@ version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b"
dependencies = [ dependencies = [
"bitflags 2.5.0", "bitflags 2.6.0",
"getopts", "getopts",
"memchr", "memchr",
"unicase", "unicase",
@ -933,7 +933,7 @@ version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [ dependencies = [
"bitflags 2.5.0", "bitflags 2.6.0",
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
@ -960,29 +960,29 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.203" version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.203" version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.66", "syn 2.0.69",
] ]
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.117" version = "1.0.120"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@ -1037,7 +1037,7 @@ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.66", "syn 2.0.69",
] ]
[[package]] [[package]]
@ -1078,9 +1078,9 @@ dependencies = [
[[package]] [[package]]
name = "strum" name = "strum"
version = "0.26.2" version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
dependencies = [ dependencies = [
"strum_macros", "strum_macros",
] ]
@ -1095,7 +1095,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "rustversion",
"syn 2.0.66", "syn 2.0.69",
] ]
[[package]] [[package]]
@ -1111,9 +1111,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.66" version = "2.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" checksum = "201fcda3845c23e8212cd466bfebf0bd20694490fc0356ae8e428e0824a915a6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1183,7 +1183,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.66", "syn 2.0.69",
] ]
[[package]] [[package]]
@ -1240,9 +1240,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.8.0" version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439"
dependencies = [ dependencies = [
"getrandom", "getrandom",
] ]
@ -1286,7 +1286,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.66", "syn 2.0.69",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -1308,7 +1308,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.66", "syn 2.0.69",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -1359,7 +1359,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [ dependencies = [
"windows-targets 0.52.5", "windows-targets 0.52.6",
] ]
[[package]] [[package]]
@ -1377,7 +1377,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [ dependencies = [
"windows-targets 0.52.5", "windows-targets 0.52.6",
] ]
[[package]] [[package]]
@ -1397,18 +1397,18 @@ dependencies = [
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.52.5" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm 0.52.5", "windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.5", "windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.5", "windows_i686_gnu 0.52.6",
"windows_i686_gnullvm", "windows_i686_gnullvm",
"windows_i686_msvc 0.52.5", "windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.5", "windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.5", "windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.5", "windows_x86_64_msvc 0.52.6",
] ]
[[package]] [[package]]
@ -1419,9 +1419,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.52.5" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
@ -1431,9 +1431,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.52.5" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
@ -1443,15 +1443,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.52.5" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]] [[package]]
name = "windows_i686_gnullvm" name = "windows_i686_gnullvm"
version = "0.52.5" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
@ -1461,9 +1461,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.52.5" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
@ -1473,9 +1473,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.52.5" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
@ -1485,9 +1485,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.52.5" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
@ -1497,9 +1497,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.52.5" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]] [[package]]
name = "winsafe" name = "winsafe"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "just" name = "just"
version = "1.29.1" version = "1.30.1"
authors = ["Casey Rodarmor <casey@rodarmor.com>"] authors = ["Casey Rodarmor <casey@rodarmor.com>"]
autotests = false autotests = false
categories = ["command-line-utilities", "development-tools"] categories = ["command-line-utilities", "development-tools"]

View File

@ -379,11 +379,11 @@ There will never be a `just` 2.0. Any desirable backwards-incompatible changes
will be opt-in on a per-`justfile` basis, so users may migrate at their will be opt-in on a per-`justfile` basis, so users may migrate at their
leisure. leisure.
Features that aren't yet ready for stabilization are gated behind the Features that aren't yet ready for stabilization are marked as unstable and may
`--unstable` flag. Features enabled by `--unstable` may change in backwards be changed or removed at any time. Using unstable features produces an error by
incompatible ways at any time. Unstable features can also be enabled by setting default, which can be suppressed with by passing the `--unstable` flag,
the environment variable `JUST_UNSTABLE` to any value other than `false`, `0`, `set unstable`, or setting the environment variable `JUST_UNSTABLE`, to any
or the empty string. value other than `false`, `0`, or the empty string.
Editor Support Editor Support
-------------- --------------
@ -820,6 +820,7 @@ foo:
| `positional-arguments` | boolean | `false` | Pass positional arguments. | | `positional-arguments` | boolean | `false` | Pass positional arguments. |
| `shell` | `[COMMAND, ARGS…]` | - | Set the command used to invoke recipes and evaluate backticks. | | `shell` | `[COMMAND, ARGS…]` | - | Set the command used to invoke recipes and evaluate backticks. |
| `tempdir` | string | - | Create temporary directories in `tempdir` instead of the system default temporary directory. | | `tempdir` | string | - | Create temporary directories in `tempdir` instead of the system default temporary directory. |
| `unstable`<sup>master</sup> | boolean | `false` | Enable unstable features. |
| `windows-powershell` | boolean | `false` | Use PowerShell on Windows as default shell. (Deprecated. Use `windows-shell` instead. | | `windows-powershell` | boolean | `false` | Use PowerShell on Windows as default shell. (Deprecated. Use `windows-shell` instead. |
| `windows-shell` | `[COMMAND, ARGS…]` | - | Set the command used to invoke recipes and evaluate backticks. | | `windows-shell` | `[COMMAND, ARGS…]` | - | Set the command used to invoke recipes and evaluate backticks. |
@ -1349,6 +1350,11 @@ Done!
`just` provides a few built-in functions that might be useful when writing `just` provides a few built-in functions that might be useful when writing
recipes. recipes.
All functions ending in `_directory` can be abbreviated to `_dir`. So
`home_directory()` can also be written as `home_dir()`. In addition,
`invocation_directory_native()` can be abbreviated to
`invocation_dir_native()`.
#### System Information #### System Information
- `arch()` — Instruction set architecture. Possible values are: `"aarch64"`, - `arch()` — Instruction set architecture. Possible values are: `"aarch64"`,
@ -1645,8 +1651,8 @@ which will halt execution.
#### Datetime #### Datetime
- `datetime(format)`<sup>master</sup> - Return local time with `format`. - `datetime(format)`<sup>1.30.0</sup> - Return local time with `format`.
- `datetime_utc(format)`<sup>master</sup> - Return UTC time with `format`. - `datetime_utc(format)`<sup>1.30.0</sup> - Return UTC time with `format`.
The arguments to `datetime` and `datetime_utc` are `strftime`-style format The arguments to `datetime` and `datetime_utc` are `strftime`-style format
strings, see the strings, see the
@ -3149,8 +3155,8 @@ Missing source files for optional imports do not produce an error.
### Modules<sup>1.19.0</sup> ### Modules<sup>1.19.0</sup>
A `justfile` can declare modules using `mod` statements. `mod` statements are A `justfile` can declare modules using `mod` statements. `mod` statements are
currently unstable, so you'll need to use the `--unstable` flag, or set the currently unstable, so you'll need to use the `--unstable` flag,
`JUST_UNSTABLE` environment variable to use them. `set unstable`, or set the `JUST_UNSTABLE` environment variable to use them.
If you have the following `justfile`: If you have the following `justfile`:
@ -3198,7 +3204,10 @@ mod foo 'PATH'
Which loads the module's source file from `PATH`, instead of from the usual Which loads the module's source file from `PATH`, instead of from the usual
locations. A leading `~/` in `PATH` is replaced with the current user's home locations. A leading `~/` in `PATH` is replaced with the current user's home
directory. directory. `PATH` may point to the module source file itself, or to a directory
containing the module source file with the name `mod.just`, `justfile`, or
`.justfile`. In the latter two cases, the module file may have any
capitalization.
Environment files are only loaded for the root justfile, and loaded environment Environment files are only loaded for the root justfile, and loaded environment
variables are available in submodules. Settings in submodules that affect variables are available in submodules. Settings in submodules that affect
@ -3229,7 +3238,7 @@ mod? foo 'baz.just'
``` ```
Modules may be given doc comments which appear in `--list` Modules may be given doc comments which appear in `--list`
output<sup>master</sup>: output<sup>1.30.0</sup>:
```mf ```mf
# foo is a great module! # foo is a great module!
@ -3751,9 +3760,9 @@ binary from the outside by invoking `just` on a given `justfile` and set of
command-line arguments, and checking the output. command-line arguments, and checking the output.
You should write whichever type of tests are easiest to write for your feature You should write whichever type of tests are easiest to write for your feature
while still providing test good coverage. while still providing good test coverage.
Unit tests are useful for testing new Rust functions that are used internally, Unit tests are useful for testing new Rust functions that are used internally
and as an aid for development. A good example are the unit tests which cover and as an aid for development. A good example are the unit tests which cover
the the
[`unindent()` function](https://github.com/casey/just/blob/master/src/unindent.rs), [`unindent()` function](https://github.com/casey/just/blob/master/src/unindent.rs),
@ -3765,7 +3774,7 @@ Integration tests are useful for making sure that the final behavior of the
`just` binary is correct. `unindent()` is also covered by integration tests `just` binary is correct. `unindent()` is also covered by integration tests
which make sure that evaluating a triple-quoted string produces the correct which make sure that evaluating a triple-quoted string produces the correct
unindented value. However, there are not integration tests for all possible unindented value. However, there are not integration tests for all possible
cases, since these are covered by faster, more concise unit tests that call cases. These are covered by faster, more concise unit tests that call
`unindent()` directly. `unindent()` directly.
Existing integration tests are in two forms, those that use the `test!` macro Existing integration tests are in two forms, those that use the `test!` macro
@ -3817,8 +3826,7 @@ checking the program's stdout, stderr, and exit code .
9. Enjoy the sweet feeling of your PR getting merged! 9. Enjoy the sweet feeling of your PR getting merged!
Feel free at any time to open a draft PR with your changes for discussion and Feel free to open a draft PR at any time for discussion and feedback.
feedback.
### Hints ### Hints

View File

@ -37,6 +37,8 @@ impl<'src> Analyzer<'src> {
let mut warnings = Vec::new(); let mut warnings = Vec::new();
let mut unstable = BTreeSet::new();
let mut modules: Table<Justfile> = Table::new(); let mut modules: Table<Justfile> = Table::new();
let mut unexports: HashSet<String> = HashSet::new(); let mut unexports: HashSet<String> = HashSet::new();
@ -92,6 +94,8 @@ impl<'src> Analyzer<'src> {
doc, doc,
.. ..
} => { } => {
unstable.insert(Unstable::Modules);
if let Some(absolute) = absolute { if let Some(absolute) = absolute {
define(*name, "module", false)?; define(*name, "module", false)?;
modules.insert(Self::analyze( modules.insert(Self::analyze(
@ -194,6 +198,7 @@ impl<'src> Analyzer<'src> {
settings, settings,
source: root.into(), source: root.into(),
unexports, unexports,
unstable,
warnings, warnings,
}) })
} }

View File

@ -252,7 +252,7 @@ mod tests {
fs::write(&path, "mod foo").unwrap(); fs::write(&path, "mod foo").unwrap();
fs::create_dir(tempdir.path().join("foo")).unwrap(); fs::create_dir(tempdir.path().join("foo")).unwrap();
fs::write(tempdir.path().join("foo/mod.just"), "bar:").unwrap(); fs::write(tempdir.path().join("foo/mod.just"), "bar:").unwrap();
let compilation = Compiler::compile(true, &loader, &path).unwrap(); let compilation = Compiler::compile(&loader, &path).unwrap();
assert_eq!( assert_eq!(
ArgumentParser::parse_arguments(&compilation.justfile, &["foo", "bar"]).unwrap(), ArgumentParser::parse_arguments(&compilation.justfile, &["foo", "bar"]).unwrap(),
@ -271,7 +271,7 @@ mod tests {
fs::write(&path, "mod foo").unwrap(); fs::write(&path, "mod foo").unwrap();
fs::create_dir(tempdir.path().join("foo")).unwrap(); fs::create_dir(tempdir.path().join("foo")).unwrap();
fs::write(tempdir.path().join("foo/mod.just"), "bar:").unwrap(); fs::write(tempdir.path().join("foo/mod.just"), "bar:").unwrap();
let compilation = Compiler::compile(true, &loader, &path).unwrap(); let compilation = Compiler::compile(&loader, &path).unwrap();
assert_matches!( assert_matches!(
ArgumentParser::parse_arguments(&compilation.justfile, &["foo", "zzz"]).unwrap_err(), ArgumentParser::parse_arguments(&compilation.justfile, &["foo", "zzz"]).unwrap_err(),
@ -289,7 +289,7 @@ mod tests {
tempdir.write("foo.just", "bar:"); tempdir.write("foo.just", "bar:");
let loader = Loader::new(); let loader = Loader::new();
let compilation = Compiler::compile(true, &loader, &tempdir.path().join("justfile")).unwrap(); let compilation = Compiler::compile(&loader, &tempdir.path().join("justfile")).unwrap();
assert_matches!( assert_matches!(
ArgumentParser::parse_arguments(&compilation.justfile, &["foo::zzz"]).unwrap_err(), ArgumentParser::parse_arguments(&compilation.justfile, &["foo::zzz"]).unwrap_err(),
@ -307,7 +307,7 @@ mod tests {
tempdir.write("foo.just", "bar:"); tempdir.write("foo.just", "bar:");
let loader = Loader::new(); let loader = Loader::new();
let compilation = Compiler::compile(true, &loader, &tempdir.path().join("justfile")).unwrap(); let compilation = Compiler::compile(&loader, &tempdir.path().join("justfile")).unwrap();
assert_matches!( assert_matches!(
ArgumentParser::parse_arguments(&compilation.justfile, &["foo::bar::baz"]).unwrap_err(), ArgumentParser::parse_arguments(&compilation.justfile, &["foo::bar::baz"]).unwrap_err(),
@ -323,7 +323,7 @@ mod tests {
tempdir.write("justfile", ""); tempdir.write("justfile", "");
let loader = Loader::new(); let loader = Loader::new();
let compilation = Compiler::compile(true, &loader, &tempdir.path().join("justfile")).unwrap(); let compilation = Compiler::compile(&loader, &tempdir.path().join("justfile")).unwrap();
assert_matches!( assert_matches!(
ArgumentParser::parse_arguments(&compilation.justfile, &[]).unwrap_err(), ArgumentParser::parse_arguments(&compilation.justfile, &[]).unwrap_err(),
@ -337,7 +337,7 @@ mod tests {
tempdir.write("justfile", "foo bar:"); tempdir.write("justfile", "foo bar:");
let loader = Loader::new(); let loader = Loader::new();
let compilation = Compiler::compile(true, &loader, &tempdir.path().join("justfile")).unwrap(); let compilation = Compiler::compile(&loader, &tempdir.path().join("justfile")).unwrap();
assert_matches!( assert_matches!(
ArgumentParser::parse_arguments(&compilation.justfile, &[]).unwrap_err(), ArgumentParser::parse_arguments(&compilation.justfile, &[]).unwrap_err(),
@ -355,7 +355,7 @@ mod tests {
tempdir.write("foo.just", "bar:"); tempdir.write("foo.just", "bar:");
let loader = Loader::new(); let loader = Loader::new();
let compilation = Compiler::compile(true, &loader, &tempdir.path().join("justfile")).unwrap(); let compilation = Compiler::compile(&loader, &tempdir.path().join("justfile")).unwrap();
assert_matches!( assert_matches!(
ArgumentParser::parse_arguments(&compilation.justfile, &[]).unwrap_err(), ArgumentParser::parse_arguments(&compilation.justfile, &[]).unwrap_err(),

View File

@ -66,7 +66,7 @@ pub(crate) enum CompileErrorKind<'src> {
FunctionArgumentCountMismatch { FunctionArgumentCountMismatch {
function: &'src str, function: &'src str,
found: usize, found: usize,
expected: Range<usize>, expected: RangeInclusive<usize>,
}, },
Include, Include,
InconsistentLeadingWhitespace { InconsistentLeadingWhitespace {

View File

@ -4,14 +4,13 @@ pub(crate) struct Compiler;
impl Compiler { impl Compiler {
pub(crate) fn compile<'src>( pub(crate) fn compile<'src>(
unstable: bool,
loader: &'src Loader, loader: &'src Loader,
root: &Path, root: &Path,
) -> RunResult<'src, Compilation<'src>> { ) -> RunResult<'src, Compilation<'src>> {
let mut asts = HashMap::<PathBuf, Ast>::new(); let mut asts = HashMap::<PathBuf, Ast>::new();
let mut loaded = Vec::new();
let mut paths = HashMap::<PathBuf, PathBuf>::new(); let mut paths = HashMap::<PathBuf, PathBuf>::new();
let mut srcs = HashMap::<PathBuf, &str>::new(); let mut srcs = HashMap::<PathBuf, &str>::new();
let mut loaded = Vec::new();
let mut stack = Vec::new(); let mut stack = Vec::new();
stack.push(Source::root(root)); stack.push(Source::root(root));
@ -42,25 +41,14 @@ impl Compiler {
relative, relative,
.. ..
} => { } => {
if !unstable {
return Err(Error::Unstable {
message: "Modules are currently unstable.".into(),
});
}
let parent = current.path.parent().unwrap(); let parent = current.path.parent().unwrap();
let import = if let Some(relative) = relative { let relative = relative
let path = parent.join(Self::expand_tilde(&relative.cooked)?); .as_ref()
.map(|relative| Self::expand_tilde(&relative.cooked))
.transpose()?;
if path.is_file() { let import = Self::find_module_file(parent, *name, relative.as_deref())?;
Some(path)
} else {
None
}
} else {
Self::find_module_file(parent, *name)?
};
if let Some(import) = import { if let Some(import) = import {
if current.file_path.contains(&import) { if current.file_path.contains(&import) {
@ -112,25 +100,69 @@ impl Compiler {
Ok(Compilation { Ok(Compilation {
asts, asts,
srcs,
justfile, justfile,
root: root.into(), root: root.into(),
srcs,
}) })
} }
fn find_module_file<'src>(parent: &Path, module: Name<'src>) -> RunResult<'src, Option<PathBuf>> { fn find_module_file<'src>(
let mut candidates = vec![format!("{module}.just"), format!("{module}/mod.just")] parent: &Path,
.into_iter() module: Name<'src>,
.filter(|path| parent.join(path).is_file()) path: Option<&Path>,
.collect::<Vec<String>>(); ) -> RunResult<'src, Option<PathBuf>> {
let mut candidates = Vec::new();
let directory = parent.join(module.lexeme()); if let Some(path) = path {
let full = parent.join(path);
if directory.exists() { if full.is_file() {
let entries = fs::read_dir(&directory).map_err(|io_error| SearchError::Io { return Ok(Some(full));
}
candidates.push((path.join("mod.just"), true));
for name in search::JUSTFILE_NAMES {
candidates.push((path.join(name), false));
}
} else {
candidates.push((format!("{module}.just").into(), true));
candidates.push((format!("{module}/mod.just").into(), true));
for name in search::JUSTFILE_NAMES {
candidates.push((format!("{module}/{name}").into(), false));
}
}
let mut grouped = BTreeMap::<PathBuf, Vec<(PathBuf, bool)>>::new();
for (candidate, case_sensitive) in candidates {
let candidate = parent.join(candidate).lexiclean();
grouped
.entry(candidate.parent().unwrap().into())
.or_default()
.push((candidate, case_sensitive));
}
let mut found = Vec::new();
for (directory, candidates) in grouped {
let entries = match fs::read_dir(&directory) {
Ok(entries) => entries,
Err(io_error) => {
if io_error.kind() == io::ErrorKind::NotFound {
continue;
}
return Err(
SearchError::Io {
io_error, io_error,
directory: directory.clone(), directory,
})?; }
.into(),
);
}
};
for entry in entries { for entry in entries {
let entry = entry.map_err(|io_error| SearchError::Io { let entry = entry.map_err(|io_error| SearchError::Io {
@ -139,22 +171,34 @@ impl Compiler {
})?; })?;
if let Some(name) = entry.file_name().to_str() { if let Some(name) = entry.file_name().to_str() {
for justfile_name in search::JUSTFILE_NAMES { for (candidate, case_sensitive) in &candidates {
if name.eq_ignore_ascii_case(justfile_name) { let candidate_name = candidate.file_name().unwrap().to_str().unwrap();
candidates.push(format!("{module}/{name}"));
let eq = if *case_sensitive {
name == candidate_name
} else {
name.eq_ignore_ascii_case(candidate_name)
};
if eq {
found.push(candidate.parent().unwrap().join(name));
} }
} }
} }
} }
} }
match candidates.as_slice() { if found.len() > 1 {
[] => Ok(None), found.sort();
[file] => Ok(Some(parent.join(file).lexiclean())), Err(Error::AmbiguousModuleFile {
found => Err(Error::AmbiguousModuleFile { found: found
found: found.into(), .into_iter()
.map(|found| found.strip_prefix(parent).unwrap().into())
.collect(),
module, module,
}), })
} else {
Ok(found.into_iter().next())
} }
} }
@ -225,7 +269,7 @@ recipe_b: recipe_c
let loader = Loader::new(); let loader = Loader::new();
let justfile_a_path = tmp.path().join("justfile"); let justfile_a_path = tmp.path().join("justfile");
let compilation = Compiler::compile(false, &loader, &justfile_a_path).unwrap(); let compilation = Compiler::compile(&loader, &justfile_a_path).unwrap();
assert_eq!(compilation.root_src(), justfile_a); assert_eq!(compilation.root_src(), justfile_a);
} }
@ -242,11 +286,91 @@ recipe_b: recipe_c
let loader = Loader::new(); let loader = Loader::new();
let justfile_a_path = tmp.path().join("justfile"); let justfile_a_path = tmp.path().join("justfile");
let loader_output = Compiler::compile(false, &loader, &justfile_a_path).unwrap_err(); let loader_output = Compiler::compile(&loader, &justfile_a_path).unwrap_err();
assert_matches!(loader_output, Error::CircularImport { current, import } assert_matches!(loader_output, Error::CircularImport { current, import }
if current == tmp.path().join("subdir").join("b").lexiclean() && if current == tmp.path().join("subdir").join("b").lexiclean() &&
import == tmp.path().join("justfile").lexiclean() import == tmp.path().join("justfile").lexiclean()
); );
} }
#[test]
fn find_module_file() {
#[track_caller]
fn case(path: Option<&str>, files: &[&str], expected: Result<Option<&str>, &[&str]>) {
let module = Name {
token: Token {
column: 0,
kind: TokenKind::Identifier,
length: 3,
line: 0,
offset: 0,
path: Path::new(""),
src: "foo",
},
};
let tempdir = tempfile::tempdir().unwrap();
for file in files {
if let Some(parent) = Path::new(file).parent() {
fs::create_dir_all(tempdir.path().join(parent)).unwrap();
}
fs::write(tempdir.path().join(file), "").unwrap();
}
let actual = Compiler::find_module_file(tempdir.path(), module, path.map(Path::new));
match expected {
Err(expected) => match actual.unwrap_err() {
Error::AmbiguousModuleFile { found, .. } => {
assert_eq!(
found,
expected
.iter()
.map(|expected| expected.replace('/', std::path::MAIN_SEPARATOR_STR).into())
.collect::<Vec<PathBuf>>()
);
}
_ => panic!("unexpected error"),
},
Ok(Some(expected)) => assert_eq!(
actual.unwrap().unwrap(),
tempdir
.path()
.join(expected.replace('/', std::path::MAIN_SEPARATOR_STR))
),
Ok(None) => assert_eq!(actual.unwrap(), None),
}
}
case(None, &["foo.just"], Ok(Some("foo.just")));
case(None, &["FOO.just"], Ok(None));
case(None, &["foo/mod.just"], Ok(Some("foo/mod.just")));
case(None, &["foo/MOD.just"], Ok(None));
case(None, &["foo/justfile"], Ok(Some("foo/justfile")));
case(None, &["foo/JUSTFILE"], Ok(Some("foo/JUSTFILE")));
case(None, &["foo/.justfile"], Ok(Some("foo/.justfile")));
case(None, &["foo/.JUSTFILE"], Ok(Some("foo/.JUSTFILE")));
case(
None,
&["foo/.justfile", "foo/justfile"],
Err(&["foo/.justfile", "foo/justfile"]),
);
case(None, &["foo/JUSTFILE"], Ok(Some("foo/JUSTFILE")));
case(Some("bar"), &["bar"], Ok(Some("bar")));
case(Some("bar"), &["bar/mod.just"], Ok(Some("bar/mod.just")));
case(Some("bar"), &["bar/justfile"], Ok(Some("bar/justfile")));
case(Some("bar"), &["bar/JUSTFILE"], Ok(Some("bar/JUSTFILE")));
case(Some("bar"), &["bar/.justfile"], Ok(Some("bar/.justfile")));
case(Some("bar"), &["bar/.JUSTFILE"], Ok(Some("bar/.JUSTFILE")));
case(
Some("bar"),
&["bar/justfile", "bar/mod.just"],
Err(&["bar/justfile", "bar/mod.just"]),
);
}
} }

View File

@ -4,7 +4,7 @@ use super::*;
pub(crate) enum Error<'src> { pub(crate) enum Error<'src> {
AmbiguousModuleFile { AmbiguousModuleFile {
module: Name<'src>, module: Name<'src>,
found: Vec<String>, found: Vec<PathBuf>,
}, },
ArgumentCountMismatch { ArgumentCountMismatch {
recipe: &'src str, recipe: &'src str,
@ -262,7 +262,7 @@ impl<'src> ColorDisplay for Error<'src> {
AmbiguousModuleFile { module, found } => AmbiguousModuleFile { module, found } =>
write!(f, write!(f,
"Found multiple source files for module `{module}`: {}", "Found multiple source files for module `{module}`: {}",
List::and_ticked(found), List::and_ticked(found.iter().map(|path| path.display())),
)?, )?,
ArgumentCountMismatch { recipe, found, min, max, .. } => { ArgumentCountMismatch { recipe, found, min, max, .. } => {
let count = Count("argument", *found); let count = Count("argument", *found);
@ -460,7 +460,7 @@ impl<'src> ColorDisplay for Error<'src> {
} }
} }
Unstable { message } => { Unstable { message } => {
write!(f, "{message} Invoke `just` with the `--unstable` flag to enable unstable features.")?; write!(f, "{message} Invoke `just` with `--unstable`, set the `JUST_UNSTABLE` environment variable, or add `set unstable` to your `justfile` to enable unstable features.")?;
} }
WriteJustfile { justfile, io_error } => { WriteJustfile { justfile, io_error } => {
let justfile = justfile.display(); let justfile = justfile.display();

View File

@ -32,7 +32,15 @@ impl<'src: 'run, 'run> Context<'src, 'run> {
} }
pub(crate) fn get(name: &str) -> Option<Function> { pub(crate) fn get(name: &str) -> Option<Function> {
let function = match name { let name = if let Some(prefix) = name.strip_suffix("_dir") {
format!("{prefix}_directory")
} else if let Some(prefix) = name.strip_suffix("_dir_native") {
format!("{prefix}_directory_native")
} else {
name.into()
};
let function = match name.as_str() {
"absolute_path" => Unary(absolute_path), "absolute_path" => Unary(absolute_path),
"append" => Binary(append), "append" => Binary(append),
"arch" => Nullary(arch), "arch" => Nullary(arch),
@ -108,15 +116,15 @@ pub(crate) fn get(name: &str) -> Option<Function> {
} }
impl Function { impl Function {
pub(crate) fn argc(&self) -> Range<usize> { pub(crate) fn argc(&self) -> RangeInclusive<usize> {
match *self { match *self {
Nullary(_) => 0..0, Nullary(_) => 0..=0,
Unary(_) => 1..1, Unary(_) => 1..=1,
UnaryOpt(_) => 1..2, UnaryOpt(_) => 1..=2,
UnaryPlus(_) => 1..usize::MAX, UnaryPlus(_) => 1..=usize::MAX,
Binary(_) => 2..2, Binary(_) => 2..=2,
BinaryPlus(_) => 2..usize::MAX, BinaryPlus(_) => 2..=usize::MAX,
Ternary(_) => 3..3, Ternary(_) => 3..=3,
} }
} }
} }

View File

@ -13,6 +13,7 @@ pub(crate) enum Item<'src> {
relative: StringLiteral<'src>, relative: StringLiteral<'src>,
}, },
Module { Module {
attributes: BTreeSet<Attribute<'src>>,
absolute: Option<PathBuf>, absolute: Option<PathBuf>,
doc: Option<&'src str>, doc: Option<&'src str>,
name: Name<'src>, name: Name<'src>,

View File

@ -27,6 +27,8 @@ pub(crate) struct Justfile<'src> {
pub(crate) source: PathBuf, pub(crate) source: PathBuf,
pub(crate) unexports: HashSet<String>, pub(crate) unexports: HashSet<String>,
pub(crate) warnings: Vec<Warning>, pub(crate) warnings: Vec<Warning>,
#[serde(skip)]
pub(crate) unstable: BTreeSet<Unstable>,
} }
impl<'src> Justfile<'src> { impl<'src> Justfile<'src> {
@ -225,6 +227,22 @@ impl<'src> Justfile<'src> {
Ok(()) Ok(())
} }
pub(crate) fn check_unstable(&self, config: &Config) -> RunResult<'src> {
if !config.unstable && !self.settings.unstable {
if let Some(unstable) = self.unstable.iter().next() {
return Err(Error::Unstable {
message: unstable.message(),
});
}
}
for module in self.modules.values() {
module.check_unstable(config)?;
}
Ok(())
}
pub(crate) fn get_alias(&self, name: &str) -> Option<&Alias<'src>> { pub(crate) fn get_alias(&self, name: &str) -> Option<&Alias<'src>> {
self.aliases.get(name) self.aliases.get(name)
} }

View File

@ -26,6 +26,7 @@ pub(crate) enum Keyword {
Tempdir, Tempdir,
True, True,
Unexport, Unexport,
Unstable,
WindowsPowershell, WindowsPowershell,
WindowsShell, WindowsShell,
X, X,

View File

@ -42,8 +42,8 @@ pub(crate) use {
shell::Shell, show_whitespace::ShowWhitespace, source::Source, string_kind::StringKind, 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, unstable::Unstable, use_color::UseColor,
verbosity::Verbosity, warning::Warning, variables::Variables, verbosity::Verbosity, warning::Warning,
}, },
camino::Utf8Path, camino::Utf8Path,
clap::ValueEnum, clap::ValueEnum,
@ -204,6 +204,7 @@ mod token_kind;
mod unindent; mod unindent;
mod unresolved_dependency; mod unresolved_dependency;
mod unresolved_recipe; mod unresolved_recipe;
mod unstable;
mod use_color; mod use_color;
mod variables; mod variables;
mod verbosity; mod verbosity;

View File

@ -294,6 +294,7 @@ impl<'src> Node<'src> for Set<'src> {
| Setting::Fallback(value) | Setting::Fallback(value)
| Setting::PositionalArguments(value) | Setting::PositionalArguments(value)
| Setting::Quiet(value) | Setting::Quiet(value)
| Setting::Unstable(value)
| Setting::WindowsPowerShell(value) | Setting::WindowsPowerShell(value)
| Setting::IgnoreComments(value) => { | Setting::IgnoreComments(value) => {
set.push_mut(value.to_string()); set.push_mut(value.to_string());

View File

@ -373,27 +373,7 @@ impl<'run, 'src> Parser<'run, 'src> {
|| self.next_are(&[Identifier, QuestionMark]) => || self.next_are(&[Identifier, QuestionMark]) =>
{ {
let doc = pop_doc_comment(&mut items, eol_since_last_comment); let doc = pop_doc_comment(&mut items, eol_since_last_comment);
items.push(self.parse_module(BTreeSet::new(), doc)?);
self.presume_keyword(Keyword::Mod)?;
let optional = self.accepted(QuestionMark)?;
let name = self.parse_name()?;
let relative = if self.next_is(StringToken) || self.next_are(&[Identifier, StringToken])
{
Some(self.parse_string_literal()?)
} else {
None
};
items.push(Item::Module {
absolute: None,
doc,
name,
optional,
relative,
});
} }
Some(Keyword::Set) Some(Keyword::Set)
if self.next_are(&[Identifier, Identifier, ColonEquals]) if self.next_are(&[Identifier, Identifier, ColonEquals])
@ -430,6 +410,17 @@ impl<'run, 'src> Parser<'run, 'src> {
Some(Keyword::Alias) if self.next_are(&[Identifier, Identifier, ColonEquals]) => { Some(Keyword::Alias) if self.next_are(&[Identifier, Identifier, ColonEquals]) => {
items.push(Item::Alias(self.parse_alias(attributes)?)); items.push(Item::Alias(self.parse_alias(attributes)?));
} }
Some(Keyword::Mod)
if self.next_are(&[Identifier, Identifier, Comment])
|| self.next_are(&[Identifier, Identifier, Eof])
|| self.next_are(&[Identifier, Identifier, Eol])
|| self.next_are(&[Identifier, Identifier, Identifier, StringToken])
|| self.next_are(&[Identifier, Identifier, StringToken])
|| self.next_are(&[Identifier, QuestionMark]) =>
{
let doc = pop_doc_comment(&mut items, eol_since_last_comment);
items.push(self.parse_module(attributes, doc)?);
}
_ => { _ => {
let quiet = self.accepted(At)?; let quiet = self.accepted(At)?;
let doc = pop_doc_comment(&mut items, eol_since_last_comment); let doc = pop_doc_comment(&mut items, eol_since_last_comment);
@ -454,6 +445,33 @@ impl<'run, 'src> Parser<'run, 'src> {
} }
} }
fn parse_module(
&mut self,
attributes: BTreeSet<Attribute<'src>>,
doc: Option<&'src str>,
) -> CompileResult<'src, Item<'src>> {
self.presume_keyword(Keyword::Mod)?;
let optional = self.accepted(QuestionMark)?;
let name = self.parse_name()?;
let relative = if self.next_is(StringToken) || self.next_are(&[Identifier, StringToken]) {
Some(self.parse_string_literal()?)
} else {
None
};
Ok(Item::Module {
attributes,
absolute: None,
doc,
name,
optional,
relative,
})
}
/// Parse an alias, e.g `alias name := target` /// Parse an alias, e.g `alias name := target`
fn parse_alias( fn parse_alias(
&mut self, &mut self,
@ -936,6 +954,7 @@ impl<'run, 'src> Parser<'run, 'src> {
Keyword::IgnoreComments => Some(Setting::IgnoreComments(self.parse_set_bool()?)), Keyword::IgnoreComments => Some(Setting::IgnoreComments(self.parse_set_bool()?)),
Keyword::PositionalArguments => Some(Setting::PositionalArguments(self.parse_set_bool()?)), Keyword::PositionalArguments => Some(Setting::PositionalArguments(self.parse_set_bool()?)),
Keyword::Quiet => Some(Setting::Quiet(self.parse_set_bool()?)), Keyword::Quiet => Some(Setting::Quiet(self.parse_set_bool()?)),
Keyword::Unstable => Some(Setting::Unstable(self.parse_set_bool()?)),
Keyword::WindowsPowershell => Some(Setting::WindowsPowerShell(self.parse_set_bool()?)), Keyword::WindowsPowershell => Some(Setting::WindowsPowerShell(self.parse_set_bool()?)),
_ => None, _ => None,
}; };
@ -2568,7 +2587,7 @@ mod tests {
kind: FunctionArgumentCountMismatch { kind: FunctionArgumentCountMismatch {
function: "arch", function: "arch",
found: 1, found: 1,
expected: 0..0, expected: 0..=0,
}, },
} }
@ -2582,7 +2601,7 @@ mod tests {
kind: FunctionArgumentCountMismatch { kind: FunctionArgumentCountMismatch {
function: "env_var", function: "env_var",
found: 0, found: 0,
expected: 1..1, expected: 1..=1,
}, },
} }
@ -2596,7 +2615,7 @@ mod tests {
kind: FunctionArgumentCountMismatch { kind: FunctionArgumentCountMismatch {
function: "env", function: "env",
found: 3, found: 3,
expected: 1..2, expected: 1..=2,
}, },
} }
@ -2610,7 +2629,7 @@ mod tests {
kind: FunctionArgumentCountMismatch { kind: FunctionArgumentCountMismatch {
function: "env", function: "env",
found: 0, found: 0,
expected: 1..2, expected: 1..=2,
}, },
} }
@ -2624,7 +2643,7 @@ mod tests {
kind: FunctionArgumentCountMismatch { kind: FunctionArgumentCountMismatch {
function: "env_var_or_default", function: "env_var_or_default",
found: 1, found: 1,
expected: 2..2, expected: 2..=2,
}, },
} }
@ -2638,7 +2657,7 @@ mod tests {
kind: FunctionArgumentCountMismatch { kind: FunctionArgumentCountMismatch {
function: "join", function: "join",
found: 1, found: 1,
expected: 2..usize::MAX, expected: 2..=usize::MAX,
}, },
} }
@ -2652,7 +2671,7 @@ mod tests {
kind: FunctionArgumentCountMismatch { kind: FunctionArgumentCountMismatch {
function: "replace", function: "replace",
found: 1, found: 1,
expected: 3..3, expected: 3..=3,
}, },
} }
} }

View File

@ -10,16 +10,14 @@ pub(crate) trait RangeExt<T> {
pub(crate) struct DisplayRange<T>(T); pub(crate) struct DisplayRange<T>(T);
impl Display for DisplayRange<&Range<usize>> { impl Display for DisplayRange<&RangeInclusive<usize>> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if self.0.start == self.0.end { if self.0.start() == self.0.end() {
write!(f, "0")?; write!(f, "{}", self.0.start())?;
} else if self.0.start == self.0.end - 1 { } else if *self.0.end() == usize::MAX {
write!(f, "{}", self.0.start)?; write!(f, "{} or more", self.0.start())?;
} else if self.0.end == usize::MAX {
write!(f, "{} or more", self.0.start)?;
} else { } else {
write!(f, "{} to {}", self.0.start, self.0.end - 1)?; write!(f, "{} to {}", self.0.start(), self.0.end())?;
} }
Ok(()) Ok(())
} }
@ -76,10 +74,10 @@ mod tests {
assert!(!(1..1).contains(&1)); assert!(!(1..1).contains(&1));
assert!((1..1).is_empty()); assert!((1..1).is_empty());
assert!((5..5).is_empty()); assert!((5..5).is_empty());
assert_eq!((1..1).display().to_string(), "0"); assert_eq!((0..=0).display().to_string(), "0");
assert_eq!((1..2).display().to_string(), "1"); assert_eq!((1..=1).display().to_string(), "1");
assert_eq!((5..6).display().to_string(), "5"); assert_eq!((5..=5).display().to_string(), "5");
assert_eq!((5..10).display().to_string(), "5 to 9"); assert_eq!((5..=9).display().to_string(), "5 to 9");
assert_eq!((1..usize::MAX).display().to_string(), "1 or more"); assert_eq!((1..=usize::MAX).display().to_string(), "1 or more");
} }
} }

View File

@ -4,6 +4,7 @@ const DEFAULT_JUSTFILE_NAME: &str = JUSTFILE_NAMES[0];
pub(crate) const JUSTFILE_NAMES: [&str; 2] = ["justfile", ".justfile"]; pub(crate) const JUSTFILE_NAMES: [&str; 2] = ["justfile", ".justfile"];
const PROJECT_ROOT_CHILDREN: &[&str] = &[".bzr", ".git", ".hg", ".svn", "_darcs"]; const PROJECT_ROOT_CHILDREN: &[&str] = &[".bzr", ".git", ".hg", ".svn", "_darcs"];
#[derive(Debug)]
pub(crate) struct Search { pub(crate) struct Search {
pub(crate) justfile: PathBuf, pub(crate) justfile: PathBuf,
pub(crate) working_directory: PathBuf, pub(crate) working_directory: PathBuf,

View File

@ -15,6 +15,7 @@ pub(crate) enum Setting<'src> {
Quiet(bool), Quiet(bool),
Shell(Shell<'src>), Shell(Shell<'src>),
Tempdir(String), Tempdir(String),
Unstable(bool),
WindowsPowerShell(bool), WindowsPowerShell(bool),
WindowsShell(Shell<'src>), WindowsShell(Shell<'src>),
} }
@ -31,6 +32,7 @@ impl<'src> Display for Setting<'src> {
| Self::IgnoreComments(value) | Self::IgnoreComments(value)
| Self::PositionalArguments(value) | Self::PositionalArguments(value)
| Self::Quiet(value) | Self::Quiet(value)
| Self::Unstable(value)
| Self::WindowsPowerShell(value) => write!(f, "{value}"), | Self::WindowsPowerShell(value) => write!(f, "{value}"),
Self::Shell(shell) | Self::WindowsShell(shell) => write!(f, "{shell}"), Self::Shell(shell) | Self::WindowsShell(shell) => write!(f, "{shell}"),
Self::DotenvFilename(value) | Self::DotenvPath(value) | Self::Tempdir(value) => { Self::DotenvFilename(value) | Self::DotenvPath(value) | Self::Tempdir(value) => {

View File

@ -20,6 +20,7 @@ pub(crate) struct Settings<'src> {
pub(crate) quiet: bool, pub(crate) quiet: bool,
pub(crate) shell: Option<Shell<'src>>, pub(crate) shell: Option<Shell<'src>>,
pub(crate) tempdir: Option<String>, pub(crate) tempdir: Option<String>,
pub(crate) unstable: bool,
pub(crate) windows_powershell: bool, pub(crate) windows_powershell: bool,
pub(crate) windows_shell: Option<Shell<'src>>, pub(crate) windows_shell: Option<Shell<'src>>,
} }
@ -66,6 +67,9 @@ impl<'src> Settings<'src> {
Setting::Shell(shell) => { Setting::Shell(shell) => {
settings.shell = Some(shell); settings.shell = Some(shell);
} }
Setting::Unstable(unstable) => {
settings.unstable = unstable;
}
Setting::WindowsPowerShell(windows_powershell) => { Setting::WindowsPowerShell(windows_powershell) => {
settings.windows_powershell = windows_powershell; settings.windows_powershell = windows_powershell;
} }

View File

@ -110,9 +110,10 @@ impl Subcommand {
) { ) {
let starting_path = match &config.search_config { let starting_path = match &config.search_config {
SearchConfig::FromInvocationDirectory => config.invocation_directory.clone(), SearchConfig::FromInvocationDirectory => config.invocation_directory.clone(),
SearchConfig::FromSearchDirectory { search_directory } => { SearchConfig::FromSearchDirectory { search_directory } => config
env::current_dir().unwrap().join(search_directory) .invocation_directory
} .join(search_directory)
.lexiclean(),
_ => unreachable!(), _ => unreachable!(),
}; };
@ -189,7 +190,9 @@ impl Subcommand {
loader: &'src Loader, loader: &'src Loader,
search: &Search, search: &Search,
) -> RunResult<'src, Compilation<'src>> { ) -> RunResult<'src, Compilation<'src>> {
let compilation = Compiler::compile(config.unstable, loader, &search.justfile)?; let compilation = Compiler::compile(loader, &search.justfile)?;
compilation.justfile.check_unstable(config)?;
if config.verbosity.loud() { if config.verbosity.loud() {
for warning in &compilation.justfile.warnings { for warning in &compilation.justfile.warnings {

View File

@ -28,7 +28,7 @@ mod full {
pub fn summary(path: &Path) -> io::Result<Result<Summary, String>> { pub fn summary(path: &Path) -> io::Result<Result<Summary, String>> {
let loader = Loader::new(); let loader = Loader::new();
match Compiler::compile(false, &loader, path) { match Compiler::compile(&loader, path) {
Ok(compilation) => Ok(Ok(Summary::new(&compilation.justfile))), Ok(compilation) => Ok(Ok(Summary::new(&compilation.justfile))),
Err(error) => Ok(Err(if let Error::Compile { compile_error } = error { Err(error) => Ok(Err(if let Error::Compile { compile_error } = error {
compile_error.to_string() compile_error.to_string()

12
src/unstable.rs Normal file
View File

@ -0,0 +1,12 @@
#[derive(Copy, Clone, Debug, PartialEq, Ord, Eq, PartialOrd)]
pub(crate) enum Unstable {
Modules,
}
impl Unstable {
pub(crate) fn message(self) -> String {
match self {
Self::Modules => "Modules are currently unstable.".into(),
}
}
}

View File

@ -4,10 +4,7 @@ test! {
name: unstable_not_passed, name: unstable_not_passed,
justfile: "", justfile: "",
args: ("--fmt"), args: ("--fmt"),
stderr: " stderr_regex: "error: The `--fmt` command is currently unstable..*",
error: The `--fmt` command is currently unstable. \
Invoke `just` with the `--unstable` flag to enable unstable features.
",
status: EXIT_FAILURE, status: EXIT_FAILURE,
} }

View File

@ -1050,3 +1050,51 @@ fn is_dependency() {
.stdout("beta false\ngamma true\n") .stdout("beta false\ngamma true\n")
.run(); .run();
} }
#[test]
fn unary_argument_count_mismamatch_error_message() {
Test::new()
.justfile("x := datetime()")
.args(["--evaluate"])
.stderr(
"
error: Function `datetime` called with 0 arguments but takes 1
justfile:1:6
1 x := datetime()
^^^^^^^^
",
)
.status(EXIT_FAILURE)
.run();
}
#[test]
fn dir_abbreviations_are_accepted() {
Test::new()
.justfile(
"
abbreviated := justfile_dir()
unabbreviated := justfile_directory()
@foo:
# {{ assert(abbreviated == unabbreviated, 'fail') }}
",
)
.run();
}
#[test]
fn invocation_dir_native_abbreviation_is_accepted() {
Test::new()
.justfile(
"
abbreviated := invocation_directory_native()
unabbreviated := invocation_dir_native()
@foo:
# {{ assert(abbreviated == unabbreviated, 'fail') }}
",
)
.run();
}

View File

@ -56,7 +56,9 @@ fn alias() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"ignore_comments": false, "ignore_comments": false,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -98,6 +100,7 @@ fn assignment() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -153,6 +156,7 @@ fn body() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -220,6 +224,7 @@ fn dependencies() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -325,6 +330,7 @@ fn dependency_argument() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -392,6 +398,7 @@ fn duplicate_recipes() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -437,6 +444,7 @@ fn duplicate_variables() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -485,6 +493,7 @@ fn doc_comment() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -519,6 +528,7 @@ fn empty_justfile() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -674,6 +684,7 @@ fn parameters() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -762,6 +773,7 @@ fn priors() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -810,6 +822,7 @@ fn private() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -858,6 +871,7 @@ fn quiet() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -921,6 +935,7 @@ fn settings() {
"command": "a", "command": "a",
}, },
"tempdir": null, "tempdir": null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -972,6 +987,7 @@ fn shebang() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir": null, "tempdir": null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -1020,6 +1036,7 @@ fn simple() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir": null, "tempdir": null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -1070,6 +1087,7 @@ fn attribute() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"ignore_comments": false, "ignore_comments": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
@ -1136,6 +1154,7 @@ fn module() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"ignore_comments": false, "ignore_comments": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
@ -1158,6 +1177,7 @@ fn module() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"ignore_comments": false, "ignore_comments": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,

View File

@ -19,7 +19,7 @@ pub(crate) use {
fs, fs,
io::Write, io::Write,
iter, iter,
path::{Path, PathBuf, MAIN_SEPARATOR}, path::{Path, PathBuf, MAIN_SEPARATOR, MAIN_SEPARATOR_STR},
process::{Command, Stdio}, process::{Command, Stdio},
str, str,
}, },

View File

@ -8,10 +8,8 @@ fn modules_are_unstable() {
mod foo mod foo
", ",
) )
.stderr( .write("foo.just", "")
"error: Modules are currently unstable. \ .stderr_regex("error: Modules are currently unstable..*")
Invoke `just` with the `--unstable` flag to enable unstable features.\n",
)
.status(EXIT_FAILURE) .status(EXIT_FAILURE)
.run(); .run();
} }
@ -441,12 +439,13 @@ fn modules_require_unambiguous_file() {
.status(EXIT_FAILURE) .status(EXIT_FAILURE)
.stderr( .stderr(
" "
error: Found multiple source files for module `foo`: `foo.just` and `foo/justfile` error: Found multiple source files for module `foo`: `foo/justfile` and `foo.just`
justfile:1:5 justfile:1:5
1 mod foo 1 mod foo
^^^ ^^^
", "
.replace('/', MAIN_SEPARATOR_STR),
) )
.run(); .run();
} }
@ -566,6 +565,23 @@ fn modules_may_specify_path() {
.run(); .run();
} }
#[test]
fn modules_may_specify_path_to_directory() {
Test::new()
.write("commands/bar/mod.just", "foo:\n @echo FOO")
.justfile(
"
mod foo 'commands/bar'
",
)
.test_round_trip(false)
.arg("--unstable")
.arg("foo")
.arg("foo")
.stdout("FOO\n")
.run();
}
#[test] #[test]
fn modules_with_paths_are_dumped_correctly() { fn modules_with_paths_are_dumped_correctly() {
Test::new() Test::new()

View File

@ -143,6 +143,22 @@ fn single_upwards() {
search_test(path, &["../"]); search_test(path, &["../"]);
} }
#[test]
fn double_upwards() {
let tmp = temptree! {
justfile: "default:\n\techo ok",
foo: {
bar: {
justfile: "default:\n\techo foo",
},
},
};
let path = tmp.path().join("foo/bar");
search_test(path, &["../default"]);
}
#[test] #[test]
fn find_dot_justfile() { fn find_dot_justfile() {
Test::new() Test::new()

View File

@ -150,7 +150,7 @@ impl Test {
} }
pub(crate) fn stderr_regex(mut self, stderr_regex: impl AsRef<str>) -> Self { pub(crate) fn stderr_regex(mut self, stderr_regex: impl AsRef<str>) -> Self {
self.stderr_regex = Some(Regex::new(&format!("^{}$", stderr_regex.as_ref())).unwrap()); self.stderr_regex = Some(Regex::new(&format!("^(?s){}$", stderr_regex.as_ref())).unwrap());
self self
} }

View File

@ -30,7 +30,7 @@ default:
.args(["--fmt"]) .args(["--fmt"])
.env("JUST_UNSTABLE", val) .env("JUST_UNSTABLE", val)
.status(EXIT_FAILURE) .status(EXIT_FAILURE)
.stderr("error: The `--fmt` command is currently unstable. Invoke `just` with the `--unstable` flag to enable unstable features.\n") .stderr_regex("error: The `--fmt` command is currently unstable.*")
.run(); .run();
} }
} }
@ -45,6 +45,40 @@ default:
.justfile(justfile) .justfile(justfile)
.args(["--fmt"]) .args(["--fmt"])
.status(EXIT_FAILURE) .status(EXIT_FAILURE)
.stderr("error: The `--fmt` command is currently unstable. Invoke `just` with the `--unstable` flag to enable unstable features.\n") .stderr_regex("error: The `--fmt` command is currently unstable.*")
.run();
}
#[test]
fn set_unstable_with_setting() {
Test::new()
.justfile(
"
set unstable
mod foo
",
)
.write("foo.just", "@bar:\n echo BAR")
.args(["foo", "bar"])
.stdout("BAR\n")
.run();
}
#[test]
fn unstable_setting_does_not_affect_submodules() {
Test::new()
.justfile(
"
set unstable
mod foo
",
)
.write("foo.just", "mod bar")
.write("bar.just", "baz:\n echo hello")
.args(["foo", "bar"])
.stderr_regex("error: Modules are currently unstable.*")
.status(EXIT_FAILURE)
.run(); .run();
} }