Skip to content

Commit d6669e0

Browse files
authored
Allow enabling unstable features with set unstable (#2237)
1 parent 5648142 commit d6669e0

20 files changed

+136
-46
lines changed

README.md

+8-7
Original file line numberDiff line numberDiff line change
@@ -379,11 +379,11 @@ There will never be a `just` 2.0. Any desirable backwards-incompatible changes
379379
will be opt-in on a per-`justfile` basis, so users may migrate at their
380380
leisure.
381381

382-
Features that aren't yet ready for stabilization are gated behind the
383-
`--unstable` flag. Features enabled by `--unstable` may change in backwards
384-
incompatible ways at any time. Unstable features can also be enabled by setting
385-
the environment variable `JUST_UNSTABLE` to any value other than `false`, `0`,
386-
or the empty string.
382+
Features that aren't yet ready for stabilization are marked as unstable and may
383+
be changed or removed at any time. Using unstable features produces an error by
384+
default, which can be suppressed with by passing the `--unstable` flag,
385+
`set unstable`, or setting the environment variable `JUST_UNSTABLE`, to any
386+
value other than `false`, `0`, or the empty string.
387387

388388
Editor Support
389389
--------------
@@ -820,6 +820,7 @@ foo:
820820
| `positional-arguments` | boolean | `false` | Pass positional arguments. |
821821
| `shell` | `[COMMAND, ARGS…]` | - | Set the command used to invoke recipes and evaluate backticks. |
822822
| `tempdir` | string | - | Create temporary directories in `tempdir` instead of the system default temporary directory. |
823+
| `unstable`<sup>master</sup> | boolean | `false` | Enable unstable features. |
823824
| `windows-powershell` | boolean | `false` | Use PowerShell on Windows as default shell. (Deprecated. Use `windows-shell` instead. |
824825
| `windows-shell` | `[COMMAND, ARGS…]` | - | Set the command used to invoke recipes and evaluate backticks. |
825826

@@ -3154,8 +3155,8 @@ Missing source files for optional imports do not produce an error.
31543155
### Modules<sup>1.19.0</sup>
31553156

31563157
A `justfile` can declare modules using `mod` statements. `mod` statements are
3157-
currently unstable, so you'll need to use the `--unstable` flag, or set the
3158-
`JUST_UNSTABLE` environment variable to use them.
3158+
currently unstable, so you'll need to use the `--unstable` flag,
3159+
`set unstable`, or set the `JUST_UNSTABLE` environment variable to use them.
31593160

31603161
If you have the following `justfile`:
31613162

src/analyzer.rs

+5
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ impl<'src> Analyzer<'src> {
3737

3838
let mut warnings = Vec::new();
3939

40+
let mut unstable = BTreeSet::new();
41+
4042
let mut modules: Table<Justfile> = Table::new();
4143

4244
let mut unexports: HashSet<String> = HashSet::new();
@@ -92,6 +94,8 @@ impl<'src> Analyzer<'src> {
9294
doc,
9395
..
9496
} => {
97+
unstable.insert(Unstable::Modules);
98+
9599
if let Some(absolute) = absolute {
96100
define(*name, "module", false)?;
97101
modules.insert(Self::analyze(
@@ -194,6 +198,7 @@ impl<'src> Analyzer<'src> {
194198
settings,
195199
source: root.into(),
196200
unexports,
201+
unstable,
197202
warnings,
198203
})
199204
}

src/argument_parser.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ mod tests {
252252
fs::write(&path, "mod foo").unwrap();
253253
fs::create_dir(tempdir.path().join("foo")).unwrap();
254254
fs::write(tempdir.path().join("foo/mod.just"), "bar:").unwrap();
255-
let compilation = Compiler::compile(true, &loader, &path).unwrap();
255+
let compilation = Compiler::compile(&loader, &path).unwrap();
256256

257257
assert_eq!(
258258
ArgumentParser::parse_arguments(&compilation.justfile, &["foo", "bar"]).unwrap(),
@@ -271,7 +271,7 @@ mod tests {
271271
fs::write(&path, "mod foo").unwrap();
272272
fs::create_dir(tempdir.path().join("foo")).unwrap();
273273
fs::write(tempdir.path().join("foo/mod.just"), "bar:").unwrap();
274-
let compilation = Compiler::compile(true, &loader, &path).unwrap();
274+
let compilation = Compiler::compile(&loader, &path).unwrap();
275275

276276
assert_matches!(
277277
ArgumentParser::parse_arguments(&compilation.justfile, &["foo", "zzz"]).unwrap_err(),
@@ -289,7 +289,7 @@ mod tests {
289289
tempdir.write("foo.just", "bar:");
290290

291291
let loader = Loader::new();
292-
let compilation = Compiler::compile(true, &loader, &tempdir.path().join("justfile")).unwrap();
292+
let compilation = Compiler::compile(&loader, &tempdir.path().join("justfile")).unwrap();
293293

294294
assert_matches!(
295295
ArgumentParser::parse_arguments(&compilation.justfile, &["foo::zzz"]).unwrap_err(),
@@ -307,7 +307,7 @@ mod tests {
307307
tempdir.write("foo.just", "bar:");
308308

309309
let loader = Loader::new();
310-
let compilation = Compiler::compile(true, &loader, &tempdir.path().join("justfile")).unwrap();
310+
let compilation = Compiler::compile(&loader, &tempdir.path().join("justfile")).unwrap();
311311

312312
assert_matches!(
313313
ArgumentParser::parse_arguments(&compilation.justfile, &["foo::bar::baz"]).unwrap_err(),
@@ -323,7 +323,7 @@ mod tests {
323323
tempdir.write("justfile", "");
324324

325325
let loader = Loader::new();
326-
let compilation = Compiler::compile(true, &loader, &tempdir.path().join("justfile")).unwrap();
326+
let compilation = Compiler::compile(&loader, &tempdir.path().join("justfile")).unwrap();
327327

328328
assert_matches!(
329329
ArgumentParser::parse_arguments(&compilation.justfile, &[]).unwrap_err(),
@@ -337,7 +337,7 @@ mod tests {
337337
tempdir.write("justfile", "foo bar:");
338338

339339
let loader = Loader::new();
340-
let compilation = Compiler::compile(true, &loader, &tempdir.path().join("justfile")).unwrap();
340+
let compilation = Compiler::compile(&loader, &tempdir.path().join("justfile")).unwrap();
341341

342342
assert_matches!(
343343
ArgumentParser::parse_arguments(&compilation.justfile, &[]).unwrap_err(),
@@ -355,7 +355,7 @@ mod tests {
355355
tempdir.write("foo.just", "bar:");
356356

357357
let loader = Loader::new();
358-
let compilation = Compiler::compile(true, &loader, &tempdir.path().join("justfile")).unwrap();
358+
let compilation = Compiler::compile(&loader, &tempdir.path().join("justfile")).unwrap();
359359

360360
assert_matches!(
361361
ArgumentParser::parse_arguments(&compilation.justfile, &[]).unwrap_err(),

src/compiler.rs

+4-11
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@ pub(crate) struct Compiler;
44

55
impl Compiler {
66
pub(crate) fn compile<'src>(
7-
unstable: bool,
87
loader: &'src Loader,
98
root: &Path,
109
) -> RunResult<'src, Compilation<'src>> {
1110
let mut asts = HashMap::<PathBuf, Ast>::new();
11+
let mut loaded = Vec::new();
1212
let mut paths = HashMap::<PathBuf, PathBuf>::new();
1313
let mut srcs = HashMap::<PathBuf, &str>::new();
14-
let mut loaded = Vec::new();
1514

1615
let mut stack = Vec::new();
1716
stack.push(Source::root(root));
@@ -42,12 +41,6 @@ impl Compiler {
4241
relative,
4342
..
4443
} => {
45-
if !unstable {
46-
return Err(Error::Unstable {
47-
message: "Modules are currently unstable.".into(),
48-
});
49-
}
50-
5144
let parent = current.path.parent().unwrap();
5245

5346
let import = if let Some(relative) = relative {
@@ -112,9 +105,9 @@ impl Compiler {
112105

113106
Ok(Compilation {
114107
asts,
115-
srcs,
116108
justfile,
117109
root: root.into(),
110+
srcs,
118111
})
119112
}
120113

@@ -225,7 +218,7 @@ recipe_b: recipe_c
225218
let loader = Loader::new();
226219

227220
let justfile_a_path = tmp.path().join("justfile");
228-
let compilation = Compiler::compile(false, &loader, &justfile_a_path).unwrap();
221+
let compilation = Compiler::compile(&loader, &justfile_a_path).unwrap();
229222

230223
assert_eq!(compilation.root_src(), justfile_a);
231224
}
@@ -242,7 +235,7 @@ recipe_b: recipe_c
242235
let loader = Loader::new();
243236

244237
let justfile_a_path = tmp.path().join("justfile");
245-
let loader_output = Compiler::compile(false, &loader, &justfile_a_path).unwrap_err();
238+
let loader_output = Compiler::compile(&loader, &justfile_a_path).unwrap_err();
246239

247240
assert_matches!(loader_output, Error::CircularImport { current, import }
248241
if current == tmp.path().join("subdir").join("b").lexiclean() &&

src/error.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ impl<'src> ColorDisplay for Error<'src> {
460460
}
461461
}
462462
Unstable { message } => {
463-
write!(f, "{message} Invoke `just` with the `--unstable` flag to enable unstable features.")?;
463+
write!(f, "{message} Invoke `just` with `--unstable`, set the `JUST_UNSTABLE` environment variable, or add `set unstable` to your `justfile` to enable unstable features.")?;
464464
}
465465
WriteJustfile { justfile, io_error } => {
466466
let justfile = justfile.display();

src/justfile.rs

+18
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ pub(crate) struct Justfile<'src> {
2727
pub(crate) source: PathBuf,
2828
pub(crate) unexports: HashSet<String>,
2929
pub(crate) warnings: Vec<Warning>,
30+
#[serde(skip)]
31+
pub(crate) unstable: BTreeSet<Unstable>,
3032
}
3133

3234
impl<'src> Justfile<'src> {
@@ -225,6 +227,22 @@ impl<'src> Justfile<'src> {
225227
Ok(())
226228
}
227229

230+
pub(crate) fn check_unstable(&self, config: &Config) -> RunResult<'src> {
231+
if !config.unstable && !self.settings.unstable {
232+
if let Some(unstable) = self.unstable.iter().next() {
233+
return Err(Error::Unstable {
234+
message: unstable.message(),
235+
});
236+
}
237+
}
238+
239+
for module in self.modules.values() {
240+
module.check_unstable(config)?;
241+
}
242+
243+
Ok(())
244+
}
245+
228246
pub(crate) fn get_alias(&self, name: &str) -> Option<&Alias<'src>> {
229247
self.aliases.get(name)
230248
}

src/keyword.rs

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub(crate) enum Keyword {
2626
Tempdir,
2727
True,
2828
Unexport,
29+
Unstable,
2930
WindowsPowershell,
3031
WindowsShell,
3132
X,

src/lib.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ pub(crate) use {
4242
shell::Shell, show_whitespace::ShowWhitespace, source::Source, string_kind::StringKind,
4343
string_literal::StringLiteral, subcommand::Subcommand, suggestion::Suggestion, table::Table,
4444
thunk::Thunk, token::Token, token_kind::TokenKind, unresolved_dependency::UnresolvedDependency,
45-
unresolved_recipe::UnresolvedRecipe, use_color::UseColor, variables::Variables,
46-
verbosity::Verbosity, warning::Warning,
45+
unresolved_recipe::UnresolvedRecipe, unstable::Unstable, use_color::UseColor,
46+
variables::Variables, verbosity::Verbosity, warning::Warning,
4747
},
4848
camino::Utf8Path,
4949
clap::ValueEnum,
@@ -204,6 +204,7 @@ mod token_kind;
204204
mod unindent;
205205
mod unresolved_dependency;
206206
mod unresolved_recipe;
207+
mod unstable;
207208
mod use_color;
208209
mod variables;
209210
mod verbosity;

src/node.rs

+1
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ impl<'src> Node<'src> for Set<'src> {
294294
| Setting::Fallback(value)
295295
| Setting::PositionalArguments(value)
296296
| Setting::Quiet(value)
297+
| Setting::Unstable(value)
297298
| Setting::WindowsPowerShell(value)
298299
| Setting::IgnoreComments(value) => {
299300
set.push_mut(value.to_string());

src/parser.rs

+1
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,7 @@ impl<'run, 'src> Parser<'run, 'src> {
936936
Keyword::IgnoreComments => Some(Setting::IgnoreComments(self.parse_set_bool()?)),
937937
Keyword::PositionalArguments => Some(Setting::PositionalArguments(self.parse_set_bool()?)),
938938
Keyword::Quiet => Some(Setting::Quiet(self.parse_set_bool()?)),
939+
Keyword::Unstable => Some(Setting::Unstable(self.parse_set_bool()?)),
939940
Keyword::WindowsPowershell => Some(Setting::WindowsPowerShell(self.parse_set_bool()?)),
940941
_ => None,
941942
};

src/setting.rs

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub(crate) enum Setting<'src> {
1515
Quiet(bool),
1616
Shell(Shell<'src>),
1717
Tempdir(String),
18+
Unstable(bool),
1819
WindowsPowerShell(bool),
1920
WindowsShell(Shell<'src>),
2021
}
@@ -31,6 +32,7 @@ impl<'src> Display for Setting<'src> {
3132
| Self::IgnoreComments(value)
3233
| Self::PositionalArguments(value)
3334
| Self::Quiet(value)
35+
| Self::Unstable(value)
3436
| Self::WindowsPowerShell(value) => write!(f, "{value}"),
3537
Self::Shell(shell) | Self::WindowsShell(shell) => write!(f, "{shell}"),
3638
Self::DotenvFilename(value) | Self::DotenvPath(value) | Self::Tempdir(value) => {

src/settings.rs

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub(crate) struct Settings<'src> {
2020
pub(crate) quiet: bool,
2121
pub(crate) shell: Option<Shell<'src>>,
2222
pub(crate) tempdir: Option<String>,
23+
pub(crate) unstable: bool,
2324
pub(crate) windows_powershell: bool,
2425
pub(crate) windows_shell: Option<Shell<'src>>,
2526
}
@@ -66,6 +67,9 @@ impl<'src> Settings<'src> {
6667
Setting::Shell(shell) => {
6768
settings.shell = Some(shell);
6869
}
70+
Setting::Unstable(unstable) => {
71+
settings.unstable = unstable;
72+
}
6973
Setting::WindowsPowerShell(windows_powershell) => {
7074
settings.windows_powershell = windows_powershell;
7175
}

src/subcommand.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,9 @@ impl Subcommand {
190190
loader: &'src Loader,
191191
search: &Search,
192192
) -> RunResult<'src, Compilation<'src>> {
193-
let compilation = Compiler::compile(config.unstable, loader, &search.justfile)?;
193+
let compilation = Compiler::compile(loader, &search.justfile)?;
194+
195+
compilation.justfile.check_unstable(config)?;
194196

195197
if config.verbosity.loud() {
196198
for warning in &compilation.justfile.warnings {

src/summary.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ mod full {
2828
pub fn summary(path: &Path) -> io::Result<Result<Summary, String>> {
2929
let loader = Loader::new();
3030

31-
match Compiler::compile(false, &loader, path) {
31+
match Compiler::compile(&loader, path) {
3232
Ok(compilation) => Ok(Ok(Summary::new(&compilation.justfile))),
3333
Err(error) => Ok(Err(if let Error::Compile { compile_error } = error {
3434
compile_error.to_string()

src/unstable.rs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#[derive(Copy, Clone, Debug, PartialEq, Ord, Eq, PartialOrd)]
2+
pub(crate) enum Unstable {
3+
Modules,
4+
}
5+
6+
impl Unstable {
7+
pub(crate) fn message(self) -> String {
8+
match self {
9+
Self::Modules => "Modules are currently unstable.".into(),
10+
}
11+
}
12+
}

tests/fmt.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@ test! {
44
name: unstable_not_passed,
55
justfile: "",
66
args: ("--fmt"),
7-
stderr: "
8-
error: The `--fmt` command is currently unstable. \
9-
Invoke `just` with the `--unstable` flag to enable unstable features.
10-
",
7+
stderr_regex: "error: The `--fmt` command is currently unstable..*",
118
status: EXIT_FAILURE,
129
}
1310

0 commit comments

Comments
 (0)