Use stable rustfmt instead of nightly (#967)

This commit is contained in:
Casey Rodarmor 2021-09-16 16:44:40 +03:00 committed by GitHub
parent ca2b596c90
commit 629c75ff04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 537 additions and 523 deletions

View File

@ -80,7 +80,7 @@ jobs:
- name: Install Rust Toolchain - name: Install Rust Toolchain
run: | run: |
rustup component add clippy rustup component add clippy rustfmt
rustup target add ${{ matrix.target }} rustup target add ${{ matrix.target }}
rustup default "`cat rust-toolchain`-${{ matrix.target }}" rustup default "`cat rust-toolchain`-${{ matrix.target }}"
@ -108,16 +108,8 @@ jobs:
- name: Clippy - name: Clippy
run: cargo clippy --all --all-targets --all-features run: cargo clippy --all --all-targets --all-features
- name: Install Rustfmt Toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
target: ${{ matrix.target }}
profile: minimal
components: rustfmt
- name: Format - name: Format
run: cargo +nightly fmt --all -- --check run: cargo fmt --all -- --check
- name: Completion Scripts - name: Completion Scripts
if: ${{ matrix.os != 'windows-2016' }} if: ${{ matrix.os != 'windows-2016' }}

View File

@ -31,7 +31,7 @@ build:
cargo lbuild cargo lbuild
fmt: fmt:
cargo +nightly fmt --all cargo fmt --all
watch +COMMAND='ltest': watch +COMMAND='ltest':
cargo watch --clear --exec "{{COMMAND}}" cargo watch --clear --exec "{{COMMAND}}"
@ -94,7 +94,6 @@ install:
install-dev-deps: install-dev-deps:
rustup install nightly rustup install nightly
rustup update nightly rustup update nightly
rustup run nightly cargo install clippy
cargo +nightly install cargo-fuzz cargo +nightly install cargo-fuzz
cargo install cargo-check cargo install cargo-check
cargo install cargo-limit cargo install cargo-limit

View File

@ -1,21 +1,6 @@
comment_width = 80 edition = "2018"
edition = "2018" max_width = 100
error_on_line_overflow = true newline_style = "Unix"
error_on_unformatted = true tab_spaces = 2
format_code_in_doc_comments = true use_field_init_shorthand = true
format_macro_bodies = true use_try_shorthand = true
format_strings = true
imports_granularity = "Crate"
match_arm_blocks = false
match_block_trailing_comma = true
max_width = 100
newline_style = "Unix"
normalize_comments = true
overflow_delimited_expr = true
reorder_impl_items = true
struct_field_align_threshold = 20
tab_spaces = 2
unstable_features = true
use_field_init_shorthand = true
use_try_shorthand = true
wrap_comments = true

View File

@ -3,7 +3,7 @@ use crate::common::*;
/// An alias, e.g. `name := target` /// An alias, e.g. `name := target`
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub(crate) struct Alias<'src, T = Rc<Recipe<'src>>> { pub(crate) struct Alias<'src, T = Rc<Recipe<'src>>> {
pub(crate) name: Name<'src>, pub(crate) name: Name<'src>,
pub(crate) target: T, pub(crate) target: T,
} }

View File

@ -4,10 +4,10 @@ use CompileErrorKind::*;
#[derive(Default)] #[derive(Default)]
pub(crate) struct Analyzer<'src> { pub(crate) struct Analyzer<'src> {
recipes: Table<'src, UnresolvedRecipe<'src>>, recipes: Table<'src, UnresolvedRecipe<'src>>,
assignments: Table<'src, Assignment<'src>>, assignments: Table<'src, Assignment<'src>>,
aliases: Table<'src, Alias<'src, Name<'src>>>, aliases: Table<'src, Alias<'src, Name<'src>>>,
sets: Table<'src, Set<'src>>, sets: Table<'src, Set<'src>>,
} }
impl<'src> Analyzer<'src> { impl<'src> Analyzer<'src> {
@ -21,20 +21,20 @@ impl<'src> Analyzer<'src> {
Item::Alias(alias) => { Item::Alias(alias) => {
self.analyze_alias(&alias)?; self.analyze_alias(&alias)?;
self.aliases.insert(alias); self.aliases.insert(alias);
}, }
Item::Assignment(assignment) => { Item::Assignment(assignment) => {
self.analyze_assignment(&assignment)?; self.analyze_assignment(&assignment)?;
self.assignments.insert(assignment); self.assignments.insert(assignment);
}, }
Item::Comment(_) => (), Item::Comment(_) => (),
Item::Recipe(recipe) => { Item::Recipe(recipe) => {
self.analyze_recipe(&recipe)?; self.analyze_recipe(&recipe)?;
self.recipes.insert(recipe); self.recipes.insert(recipe);
}, }
Item::Set(set) => { Item::Set(set) => {
self.analyze_set(&set)?; self.analyze_set(&set)?;
self.sets.insert(set); self.sets.insert(set);
}, }
} }
} }
@ -65,17 +65,17 @@ impl<'src> Analyzer<'src> {
match set.value { match set.value {
Setting::DotenvLoad(dotenv_load) => { Setting::DotenvLoad(dotenv_load) => {
settings.dotenv_load = Some(dotenv_load); settings.dotenv_load = Some(dotenv_load);
}, }
Setting::Export(export) => { Setting::Export(export) => {
settings.export = export; settings.export = export;
}, }
Setting::PositionalArguments(positional_arguments) => { Setting::PositionalArguments(positional_arguments) => {
settings.positional_arguments = positional_arguments; settings.positional_arguments = positional_arguments;
}, }
Setting::Shell(shell) => { Setting::Shell(shell) => {
assert!(settings.shell.is_none()); assert!(settings.shell.is_none());
settings.shell = Some(shell); settings.shell = Some(shell);
}, }
} }
} }
@ -92,7 +92,7 @@ impl<'src> Analyzer<'src> {
if let Some(original) = self.recipes.get(recipe.name.lexeme()) { if let Some(original) = self.recipes.get(recipe.name.lexeme()) {
return Err(recipe.name.token().error(DuplicateRecipe { return Err(recipe.name.token().error(DuplicateRecipe {
recipe: original.name(), recipe: original.name(),
first: original.line_number(), first: original.line_number(),
})); }));
} }
@ -102,7 +102,7 @@ impl<'src> Analyzer<'src> {
for parameter in &recipe.parameters { for parameter in &recipe.parameters {
if parameters.contains(parameter.name.lexeme()) { if parameters.contains(parameter.name.lexeme()) {
return Err(parameter.name.token().error(DuplicateParameter { return Err(parameter.name.token().error(DuplicateParameter {
recipe: recipe.name.lexeme(), recipe: recipe.name.lexeme(),
parameter: parameter.name.lexeme(), parameter: parameter.name.lexeme(),
})); }));
} }
@ -166,7 +166,7 @@ impl<'src> Analyzer<'src> {
if let Some(original) = self.sets.get(set.name.lexeme()) { if let Some(original) = self.sets.get(set.name.lexeme()) {
return Err(set.name.error(DuplicateSet { return Err(set.name.error(DuplicateSet {
setting: original.name.lexeme(), setting: original.name.lexeme(),
first: original.name.line, first: original.name.line,
})); }));
} }
@ -181,7 +181,7 @@ impl<'src> Analyzer<'src> {
// Make sure the alias doesn't conflict with any recipe // Make sure the alias doesn't conflict with any recipe
if let Some(recipe) = recipes.get(alias.name.lexeme()) { if let Some(recipe) = recipes.get(alias.name.lexeme()) {
return Err(token.error(AliasShadowsRecipe { return Err(token.error(AliasShadowsRecipe {
alias: alias.name.lexeme(), alias: alias.name.lexeme(),
recipe_line: recipe.line_number(), recipe_line: recipe.line_number(),
})); }));
} }
@ -190,7 +190,7 @@ impl<'src> Analyzer<'src> {
match recipes.get(alias.target.lexeme()) { match recipes.get(alias.target.lexeme()) {
Some(target) => Ok(alias.resolve(Rc::clone(target))), Some(target) => Ok(alias.resolve(Rc::clone(target))),
None => Err(token.error(UnknownAliasTarget { None => Err(token.error(UnknownAliasTarget {
alias: alias.name.lexeme(), alias: alias.name.lexeme(),
target: alias.target.lexeme(), target: alias.target.lexeme(),
})), })),
} }

View File

@ -4,8 +4,8 @@ use CompileErrorKind::*;
pub(crate) struct AssignmentResolver<'src: 'run, 'run> { pub(crate) struct AssignmentResolver<'src: 'run, 'run> {
assignments: &'run Table<'src, Assignment<'src>>, assignments: &'run Table<'src, Assignment<'src>>,
stack: Vec<&'src str>, stack: Vec<&'src str>,
evaluated: BTreeSet<&'src str>, evaluated: BTreeSet<&'src str>,
} }
impl<'src: 'run, 'run> AssignmentResolver<'src, 'run> { impl<'src: 'run, 'run> AssignmentResolver<'src, 'run> {
@ -38,12 +38,12 @@ impl<'src: 'run, 'run> AssignmentResolver<'src, 'run> {
} else { } else {
let message = format!("attempted to resolve unknown assignment `{}`", name); let message = format!("attempted to resolve unknown assignment `{}`", name);
let token = Token { let token = Token {
src: "", src: "",
offset: 0, offset: 0,
line: 0, line: 0,
column: 0, column: 0,
length: 0, length: 0,
kind: TokenKind::Unspecified, kind: TokenKind::Unspecified,
}; };
return Err(CompileError { return Err(CompileError {
kind: Internal { message }, kind: Internal { message },
@ -74,26 +74,26 @@ impl<'src: 'run, 'run> AssignmentResolver<'src, 'run> {
} else { } else {
Err(name.token().error(UndefinedVariable { variable })) Err(name.token().error(UndefinedVariable { variable }))
} }
}, }
Expression::Call { thunk } => match thunk { Expression::Call { thunk } => match thunk {
Thunk::Nullary { .. } => Ok(()), Thunk::Nullary { .. } => Ok(()),
Thunk::Unary { arg, .. } => self.resolve_expression(arg), Thunk::Unary { arg, .. } => self.resolve_expression(arg),
Thunk::Binary { args: [a, b], .. } => { Thunk::Binary { args: [a, b], .. } => {
self.resolve_expression(a)?; self.resolve_expression(a)?;
self.resolve_expression(b) self.resolve_expression(b)
}, }
Thunk::Ternary { Thunk::Ternary {
args: [a, b, c], .. args: [a, b, c], ..
} => { } => {
self.resolve_expression(a)?; self.resolve_expression(a)?;
self.resolve_expression(b)?; self.resolve_expression(b)?;
self.resolve_expression(c) self.resolve_expression(c)
}, }
}, },
Expression::Concatination { lhs, rhs } => { Expression::Concatination { lhs, rhs } => {
self.resolve_expression(lhs)?; self.resolve_expression(lhs)?;
self.resolve_expression(rhs) self.resolve_expression(rhs)
}, }
Expression::Conditional { Expression::Conditional {
lhs, lhs,
rhs, rhs,
@ -105,7 +105,7 @@ impl<'src: 'run, 'run> AssignmentResolver<'src, 'run> {
self.resolve_expression(rhs)?; self.resolve_expression(rhs)?;
self.resolve_expression(then)?; self.resolve_expression(then)?;
self.resolve_expression(otherwise) self.resolve_expression(otherwise)
}, }
Expression::StringLiteral { .. } | Expression::Backtick { .. } => Ok(()), Expression::StringLiteral { .. } | Expression::Backtick { .. } => Ok(()),
Expression::Group { contents } => self.resolve_expression(contents), Expression::Group { contents } => self.resolve_expression(contents),
} }

View File

@ -6,7 +6,7 @@ use crate::common::*;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct Ast<'src> { pub(crate) struct Ast<'src> {
/// Items in the justfile /// Items in the justfile
pub(crate) items: Vec<Item<'src>>, pub(crate) items: Vec<Item<'src>>,
/// Non-fatal warnings encountered during parsing /// Non-fatal warnings encountered during parsing
pub(crate) warnings: Vec<Warning>, pub(crate) warnings: Vec<Warning>,
} }

View File

@ -6,9 +6,9 @@ pub(crate) struct Binding<'src, V = String> {
/// Export binding as an environment variable to child processes /// Export binding as an environment variable to child processes
pub(crate) export: bool, pub(crate) export: bool,
/// Binding name /// Binding name
pub(crate) name: Name<'src>, pub(crate) name: Name<'src>,
/// Binding value /// Binding value
pub(crate) value: V, pub(crate) value: V,
} }
impl<'src, V> Keyed<'src> for Binding<'src, V> { impl<'src, V> Keyed<'src> for Binding<'src, V> {

View File

@ -6,8 +6,8 @@ use atty::Stream;
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub(crate) struct Color { pub(crate) struct Color {
use_color: UseColor, use_color: UseColor,
atty: bool, atty: bool,
style: Style, style: Style,
} }
impl Color { impl Color {
@ -120,8 +120,8 @@ impl Default for Color {
fn default() -> Self { fn default() -> Self {
Self { Self {
use_color: UseColor::Auto, use_color: UseColor::Auto,
atty: false, atty: false,
style: Style::new(), style: Style::new(),
} }
} }
} }

View File

@ -3,7 +3,7 @@ use crate::common::*;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub(crate) struct CompileError<'src> { pub(crate) struct CompileError<'src> {
pub(crate) token: Token<'src>, pub(crate) token: Token<'src>,
pub(crate) kind: CompileErrorKind<'src>, pub(crate) kind: CompileErrorKind<'src>,
} }
impl<'src> CompileError<'src> { impl<'src> CompileError<'src> {
@ -26,11 +26,11 @@ impl Display for CompileError<'_> {
alias, alias,
recipe_line.ordinal(), recipe_line.ordinal(),
)?; )?;
}, }
BacktickShebang => { BacktickShebang => {
write!(f, "Backticks may not start with `#!`")?; write!(f, "Backticks may not start with `#!`")?;
}, }
CircularRecipeDependency { recipe, ref circle } => CircularRecipeDependency { recipe, ref circle } => {
if circle.len() == 2 { if circle.len() == 2 {
write!(f, "Recipe `{}` depends on itself", recipe)?; write!(f, "Recipe `{}` depends on itself", recipe)?;
} else { } else {
@ -40,11 +40,12 @@ impl Display for CompileError<'_> {
recipe, recipe,
circle.join(" -> ") circle.join(" -> ")
)?; )?;
}, }
}
CircularVariableDependency { CircularVariableDependency {
variable, variable,
ref circle, ref circle,
} => } => {
if circle.len() == 2 { if circle.len() == 2 {
write!(f, "Variable `{}` is defined in terms of itself", variable)?; write!(f, "Variable `{}` is defined in terms of itself", variable)?;
} else { } else {
@ -54,7 +55,8 @@ impl Display for CompileError<'_> {
variable, variable,
circle.join(" -> ") circle.join(" -> ")
)?; )?;
}, }
}
DependencyArgumentCountMismatch { DependencyArgumentCountMismatch {
dependency, dependency,
found, found,
@ -77,7 +79,7 @@ impl Display for CompileError<'_> {
} else { } else {
write!(f, "at most {} {}", max, Count("argument", *max))?; write!(f, "at most {} {}", max, Count("argument", *max))?;
} }
}, }
DeprecatedEquals => { DeprecatedEquals => {
writeln!( writeln!(
f, f,
@ -87,7 +89,7 @@ impl Display for CompileError<'_> {
f, f,
"Please see this issue for more details: https://github.com/casey/just/issues/379" "Please see this issue for more details: https://github.com/casey/just/issues/379"
)?; )?;
}, }
DuplicateAlias { alias, first } => { DuplicateAlias { alias, first } => {
write!( write!(
f, f,
@ -96,14 +98,14 @@ impl Display for CompileError<'_> {
first.ordinal(), first.ordinal(),
self.token.line.ordinal(), self.token.line.ordinal(),
)?; )?;
}, }
DuplicateParameter { recipe, parameter } => { DuplicateParameter { recipe, parameter } => {
write!( write!(
f, f,
"Recipe `{}` has duplicate parameter `{}`", "Recipe `{}` has duplicate parameter `{}`",
recipe, parameter recipe, parameter
)?; )?;
}, }
DuplicateRecipe { recipe, first } => { DuplicateRecipe { recipe, first } => {
write!( write!(
f, f,
@ -112,7 +114,7 @@ impl Display for CompileError<'_> {
first.ordinal(), first.ordinal(),
self.token.line.ordinal() self.token.line.ordinal()
)?; )?;
}, }
DuplicateSet { setting, first } => { DuplicateSet { setting, first } => {
write!( write!(
f, f,
@ -121,10 +123,10 @@ impl Display for CompileError<'_> {
first.ordinal(), first.ordinal(),
self.token.line.ordinal(), self.token.line.ordinal(),
)?; )?;
}, }
DuplicateVariable { variable } => { DuplicateVariable { variable } => {
write!(f, "Variable `{}` has multiple definitions", variable)?; write!(f, "Variable `{}` has multiple definitions", variable)?;
}, }
ExpectedKeyword { expected, found } => write!( ExpectedKeyword { expected, found } => write!(
f, f,
"Expected keyword {} but found identifier `{}`", "Expected keyword {} but found identifier `{}`",
@ -133,7 +135,7 @@ impl Display for CompileError<'_> {
)?, )?,
ExtraLeadingWhitespace => { ExtraLeadingWhitespace => {
write!(f, "Recipe line has extra leading whitespace")?; write!(f, "Recipe line has extra leading whitespace")?;
}, }
FunctionArgumentCountMismatch { FunctionArgumentCountMismatch {
function, function,
found, found,
@ -147,7 +149,7 @@ impl Display for CompileError<'_> {
Count("argument", *found), Count("argument", *found),
expected expected
)?; )?;
}, }
InconsistentLeadingWhitespace { expected, found } => { InconsistentLeadingWhitespace { expected, found } => {
write!( write!(
f, f,
@ -156,7 +158,7 @@ impl Display for CompileError<'_> {
ShowWhitespace(expected), ShowWhitespace(expected),
ShowWhitespace(found) ShowWhitespace(found)
)?; )?;
}, }
Internal { ref message } => { Internal { ref message } => {
write!( write!(
f, f,
@ -164,7 +166,7 @@ impl Display for CompileError<'_> {
consider filing an issue: https://github.com/casey/just/issues/new", consider filing an issue: https://github.com/casey/just/issues/new",
message message
)?; )?;
}, }
InvalidEscapeSequence { character } => { InvalidEscapeSequence { character } => {
let representation = match character { let representation = match character {
'`' => r"\`".to_owned(), '`' => r"\`".to_owned(),
@ -174,7 +176,7 @@ impl Display for CompileError<'_> {
_ => character.escape_default().collect(), _ => character.escape_default().collect(),
}; };
write!(f, "`\\{}` is not a valid escape sequence", representation)?; write!(f, "`\\{}` is not a valid escape sequence", representation)?;
}, }
MismatchedClosingDelimiter { MismatchedClosingDelimiter {
open, open,
open_line, open_line,
@ -187,7 +189,7 @@ impl Display for CompileError<'_> {
open.open(), open.open(),
open_line.ordinal(), open_line.ordinal(),
)?; )?;
}, }
MixedLeadingWhitespace { whitespace } => { MixedLeadingWhitespace { whitespace } => {
write!( write!(
f, f,
@ -195,73 +197,73 @@ impl Display for CompileError<'_> {
consist of tabs or spaces, but not both", consist of tabs or spaces, but not both",
ShowWhitespace(whitespace) ShowWhitespace(whitespace)
)?; )?;
}, }
ParameterFollowsVariadicParameter { parameter } => { ParameterFollowsVariadicParameter { parameter } => {
write!(f, "Parameter `{}` follows variadic parameter", parameter)?; write!(f, "Parameter `{}` follows variadic parameter", parameter)?;
}, }
ParameterShadowsVariable { parameter } => { ParameterShadowsVariable { parameter } => {
write!( write!(
f, f,
"Parameter `{}` shadows variable of the same name", "Parameter `{}` shadows variable of the same name",
parameter parameter
)?; )?;
}, }
RequiredParameterFollowsDefaultParameter { parameter } => { RequiredParameterFollowsDefaultParameter { parameter } => {
write!( write!(
f, f,
"Non-default parameter `{}` follows default parameter", "Non-default parameter `{}` follows default parameter",
parameter parameter
)?; )?;
}, }
UndefinedVariable { variable } => { UndefinedVariable { variable } => {
write!(f, "Variable `{}` not defined", variable)?; write!(f, "Variable `{}` not defined", variable)?;
}, }
UnexpectedCharacter { expected } => { UnexpectedCharacter { expected } => {
write!(f, "Expected character `{}`", expected)?; write!(f, "Expected character `{}`", expected)?;
}, }
UnexpectedClosingDelimiter { close } => { UnexpectedClosingDelimiter { close } => {
write!(f, "Unexpected closing delimiter `{}`", close.close())?; write!(f, "Unexpected closing delimiter `{}`", close.close())?;
}, }
UnexpectedEndOfToken { expected } => { UnexpectedEndOfToken { expected } => {
write!(f, "Expected character `{}` but found end-of-file", expected)?; write!(f, "Expected character `{}` but found end-of-file", expected)?;
}, }
UnexpectedToken { UnexpectedToken {
ref expected, ref expected,
found, found,
} => { } => {
write!(f, "Expected {}, but found {}", List::or(expected), found)?; write!(f, "Expected {}, but found {}", List::or(expected), found)?;
}, }
UnknownAliasTarget { alias, target } => { UnknownAliasTarget { alias, target } => {
write!(f, "Alias `{}` has an unknown target `{}`", alias, target)?; write!(f, "Alias `{}` has an unknown target `{}`", alias, target)?;
}, }
UnknownDependency { recipe, unknown } => { UnknownDependency { recipe, unknown } => {
write!( write!(
f, f,
"Recipe `{}` has unknown dependency `{}`", "Recipe `{}` has unknown dependency `{}`",
recipe, unknown recipe, unknown
)?; )?;
}, }
UnknownFunction { function } => { UnknownFunction { function } => {
write!(f, "Call to unknown function `{}`", function)?; write!(f, "Call to unknown function `{}`", function)?;
}, }
UnknownSetting { setting } => { UnknownSetting { setting } => {
write!(f, "Unknown setting `{}`", setting)?; write!(f, "Unknown setting `{}`", setting)?;
}, }
UnknownStartOfToken => { UnknownStartOfToken => {
write!(f, "Unknown start of token:")?; write!(f, "Unknown start of token:")?;
}, }
UnpairedCarriageReturn => { UnpairedCarriageReturn => {
write!(f, "Unpaired carriage return")?; write!(f, "Unpaired carriage return")?;
}, }
UnterminatedBacktick => { UnterminatedBacktick => {
write!(f, "Unterminated backtick")?; write!(f, "Unterminated backtick")?;
}, }
UnterminatedInterpolation => { UnterminatedInterpolation => {
write!(f, "Unterminated interpolation")?; write!(f, "Unterminated interpolation")?;
}, }
UnterminatedString => { UnterminatedString => {
write!(f, "Unterminated string")?; write!(f, "Unterminated string")?;
}, }
} }
Ok(()) Ok(())

View File

@ -3,7 +3,7 @@ use crate::common::*;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub(crate) enum CompileErrorKind<'src> { pub(crate) enum CompileErrorKind<'src> {
AliasShadowsRecipe { AliasShadowsRecipe {
alias: &'src str, alias: &'src str,
recipe_line: usize, recipe_line: usize,
}, },
BacktickShebang, BacktickShebang,
@ -13,13 +13,13 @@ pub(crate) enum CompileErrorKind<'src> {
}, },
CircularVariableDependency { CircularVariableDependency {
variable: &'src str, variable: &'src str,
circle: Vec<&'src str>, circle: Vec<&'src str>,
}, },
DependencyArgumentCountMismatch { DependencyArgumentCountMismatch {
dependency: &'src str, dependency: &'src str,
found: usize, found: usize,
min: usize, min: usize,
max: usize, max: usize,
}, },
DeprecatedEquals, DeprecatedEquals,
DuplicateAlias { DuplicateAlias {
@ -27,33 +27,33 @@ pub(crate) enum CompileErrorKind<'src> {
first: usize, first: usize,
}, },
DuplicateParameter { DuplicateParameter {
recipe: &'src str, recipe: &'src str,
parameter: &'src str, parameter: &'src str,
}, },
DuplicateRecipe { DuplicateRecipe {
recipe: &'src str, recipe: &'src str,
first: usize, first: usize,
}, },
DuplicateSet { DuplicateSet {
setting: &'src str, setting: &'src str,
first: usize, first: usize,
}, },
DuplicateVariable { DuplicateVariable {
variable: &'src str, variable: &'src str,
}, },
ExpectedKeyword { ExpectedKeyword {
expected: Vec<Keyword>, expected: Vec<Keyword>,
found: &'src str, found: &'src str,
}, },
ExtraLeadingWhitespace, ExtraLeadingWhitespace,
FunctionArgumentCountMismatch { FunctionArgumentCountMismatch {
function: &'src str, function: &'src str,
found: usize, found: usize,
expected: usize, expected: usize,
}, },
InconsistentLeadingWhitespace { InconsistentLeadingWhitespace {
expected: &'src str, expected: &'src str,
found: &'src str, found: &'src str,
}, },
Internal { Internal {
message: String, message: String,
@ -62,8 +62,8 @@ pub(crate) enum CompileErrorKind<'src> {
character: char, character: char,
}, },
MismatchedClosingDelimiter { MismatchedClosingDelimiter {
close: Delimiter, close: Delimiter,
open: Delimiter, open: Delimiter,
open_line: usize, open_line: usize,
}, },
MixedLeadingWhitespace { MixedLeadingWhitespace {
@ -92,14 +92,14 @@ pub(crate) enum CompileErrorKind<'src> {
}, },
UnexpectedToken { UnexpectedToken {
expected: Vec<TokenKind>, expected: Vec<TokenKind>,
found: TokenKind, found: TokenKind,
}, },
UnknownAliasTarget { UnknownAliasTarget {
alias: &'src str, alias: &'src str,
target: &'src str, target: &'src str,
}, },
UnknownDependency { UnknownDependency {
recipe: &'src str, recipe: &'src str,
unknown: &'src str, unknown: &'src str,
}, },
UnknownFunction { UnknownFunction {

View File

@ -14,24 +14,24 @@ pub(crate) const DEFAULT_SHELL_ARG: &str = "-cu";
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub(crate) struct Config { pub(crate) struct Config {
pub(crate) color: Color, pub(crate) color: Color,
pub(crate) dry_run: bool, pub(crate) dry_run: bool,
pub(crate) highlight: bool, pub(crate) highlight: bool,
pub(crate) invocation_directory: PathBuf, pub(crate) invocation_directory: PathBuf,
pub(crate) list_heading: String, pub(crate) list_heading: String,
pub(crate) list_prefix: String, pub(crate) list_prefix: String,
pub(crate) load_dotenv: bool, pub(crate) load_dotenv: bool,
pub(crate) search_config: SearchConfig, pub(crate) search_config: SearchConfig,
pub(crate) shell: String, pub(crate) shell: String,
pub(crate) shell_args: Vec<String>, pub(crate) shell_args: Vec<String>,
pub(crate) shell_command: bool, pub(crate) shell_command: bool,
pub(crate) shell_present: bool, pub(crate) shell_present: bool,
pub(crate) subcommand: Subcommand, pub(crate) subcommand: Subcommand,
pub(crate) unsorted: bool, pub(crate) unsorted: bool,
pub(crate) unstable: bool, pub(crate) unstable: bool,
pub(crate) dotenv_filename: Option<String>, pub(crate) dotenv_filename: Option<String>,
pub(crate) dotenv_path: Option<PathBuf>, pub(crate) dotenv_path: Option<PathBuf>,
pub(crate) verbosity: Verbosity, pub(crate) verbosity: Verbosity,
} }
mod cmd { mod cmd {
@ -416,15 +416,17 @@ impl Config {
match (justfile, working_directory) { match (justfile, working_directory) {
(None, None) => SearchConfig::FromInvocationDirectory, (None, None) => SearchConfig::FromInvocationDirectory,
(Some(justfile), None) => SearchConfig::WithJustfile { justfile }, (Some(justfile), None) => SearchConfig::WithJustfile { justfile },
(Some(justfile), Some(working_directory)) => (Some(justfile), Some(working_directory)) => {
SearchConfig::WithJustfileAndWorkingDirectory { SearchConfig::WithJustfileAndWorkingDirectory {
justfile, justfile,
working_directory, working_directory,
}, }
(None, Some(_)) => }
(None, Some(_)) => {
return Err(ConfigError::internal( return Err(ConfigError::internal(
"--working-directory set without --justfile", "--working-directory set without --justfile",
)), ))
}
} }
} }
}; };
@ -432,26 +434,26 @@ impl Config {
for subcommand in cmd::ARGLESS { for subcommand in cmd::ARGLESS {
if matches.is_present(subcommand) { if matches.is_present(subcommand) {
match (!overrides.is_empty(), !positional.arguments.is_empty()) { match (!overrides.is_empty(), !positional.arguments.is_empty()) {
(false, false) => {}, (false, false) => {}
(true, false) => { (true, false) => {
return Err(ConfigError::SubcommandOverrides { return Err(ConfigError::SubcommandOverrides {
subcommand, subcommand,
overrides, overrides,
}); });
}, }
(false, true) => { (false, true) => {
return Err(ConfigError::SubcommandArguments { return Err(ConfigError::SubcommandArguments {
arguments: positional.arguments, arguments: positional.arguments,
subcommand, subcommand,
}); });
}, }
(true, true) => { (true, true) => {
return Err(ConfigError::SubcommandOverridesAndArguments { return Err(ConfigError::SubcommandOverridesAndArguments {
arguments: positional.arguments, arguments: positional.arguments,
subcommand, subcommand,
overrides, overrides,
}); });
}, }
} }
} }
} }
@ -497,7 +499,7 @@ impl Config {
if positional.arguments.len() > 1 { if positional.arguments.len() > 1 {
return Err(ConfigError::SubcommandArguments { return Err(ConfigError::SubcommandArguments {
subcommand: cmd::EVALUATE, subcommand: cmd::EVALUATE,
arguments: positional arguments: positional
.arguments .arguments
.into_iter() .into_iter()
.skip(1) .skip(1)

View File

@ -23,7 +23,7 @@ pub(crate) enum ConfigError {
))] ))]
SubcommandArguments { SubcommandArguments {
subcommand: &'static str, subcommand: &'static str,
arguments: Vec<String>, arguments: Vec<String>,
}, },
#[snafu(display( #[snafu(display(
"`--{}` used with unexpected overrides: {}", "`--{}` used with unexpected overrides: {}",
@ -32,7 +32,7 @@ pub(crate) enum ConfigError {
))] ))]
SubcommandOverrides { SubcommandOverrides {
subcommand: &'static str, subcommand: &'static str,
overrides: BTreeMap<String, String>, overrides: BTreeMap<String, String>,
}, },
#[snafu(display( #[snafu(display(
"`--{}` used with unexpected overrides: {}; and arguments: {}", "`--{}` used with unexpected overrides: {}; and arguments: {}",
@ -42,8 +42,8 @@ pub(crate) enum ConfigError {
] ]
SubcommandOverridesAndArguments { SubcommandOverridesAndArguments {
subcommand: &'static str, subcommand: &'static str,
overrides: BTreeMap<String, String>, overrides: BTreeMap<String, String>,
arguments: Vec<String>, arguments: Vec<String>,
}, },
} }

View File

@ -2,7 +2,7 @@ use crate::common::*;
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
pub(crate) struct Dependency<'src> { pub(crate) struct Dependency<'src> {
pub(crate) recipe: Rc<Recipe<'src>>, pub(crate) recipe: Rc<Recipe<'src>>,
pub(crate) arguments: Vec<Expression<'src>>, pub(crate) arguments: Vec<Expression<'src>>,
} }

View File

@ -5,7 +5,7 @@ use crate::common::*;
pub struct Enclosure<T: Display> { pub struct Enclosure<T: Display> {
enclosure: &'static str, enclosure: &'static str,
value: T, value: T,
} }
impl<T: Display> Enclosure<T> { impl<T: Display> Enclosure<T> {

View File

@ -3,48 +3,48 @@ use crate::common::*;
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum Error<'src> { pub(crate) enum Error<'src> {
ArgumentCountMismatch { ArgumentCountMismatch {
recipe: &'src str, recipe: &'src str,
parameters: Vec<Parameter<'src>>, parameters: Vec<Parameter<'src>>,
found: usize, found: usize,
min: usize, min: usize,
max: usize, max: usize,
}, },
Backtick { Backtick {
token: Token<'src>, token: Token<'src>,
output_error: OutputError, output_error: OutputError,
}, },
ChooserInvoke { ChooserInvoke {
shell_binary: String, shell_binary: String,
shell_arguments: String, shell_arguments: String,
chooser: OsString, chooser: OsString,
io_error: io::Error, io_error: io::Error,
}, },
ChooserRead { ChooserRead {
chooser: OsString, chooser: OsString,
io_error: io::Error, io_error: io::Error,
}, },
ChooserStatus { ChooserStatus {
chooser: OsString, chooser: OsString,
status: ExitStatus, status: ExitStatus,
}, },
ChooserWrite { ChooserWrite {
chooser: OsString, chooser: OsString,
io_error: io::Error, io_error: io::Error,
}, },
Code { Code {
recipe: &'src str, recipe: &'src str,
line_number: Option<usize>, line_number: Option<usize>,
code: i32, code: i32,
}, },
CommandInvoke { CommandInvoke {
binary: OsString, binary: OsString,
arguments: Vec<OsString>, arguments: Vec<OsString>,
io_error: io::Error, io_error: io::Error,
}, },
CommandStatus { CommandStatus {
binary: OsString, binary: OsString,
arguments: Vec<OsString>, arguments: Vec<OsString>,
status: ExitStatus, status: ExitStatus,
}, },
Compile { Compile {
compile_error: CompileError<'src>, compile_error: CompileError<'src>,
@ -53,18 +53,18 @@ pub(crate) enum Error<'src> {
config_error: ConfigError, config_error: ConfigError,
}, },
Cygpath { Cygpath {
recipe: &'src str, recipe: &'src str,
output_error: OutputError, output_error: OutputError,
}, },
DefaultRecipeRequiresArguments { DefaultRecipeRequiresArguments {
recipe: &'src str, recipe: &'src str,
min_arguments: usize, min_arguments: usize,
}, },
Dotenv { Dotenv {
dotenv_error: dotenv::Error, dotenv_error: dotenv::Error,
}, },
EditorInvoke { EditorInvoke {
editor: OsString, editor: OsString,
io_error: io::Error, io_error: io::Error,
}, },
EditorStatus { EditorStatus {
@ -72,12 +72,12 @@ pub(crate) enum Error<'src> {
status: ExitStatus, status: ExitStatus,
}, },
EvalUnknownVariable { EvalUnknownVariable {
variable: String, variable: String,
suggestion: Option<Suggestion<'src>>, suggestion: Option<Suggestion<'src>>,
}, },
FunctionCall { FunctionCall {
function: Name<'src>, function: Name<'src>,
message: String, message: String,
}, },
InitExists { InitExists {
justfile: PathBuf, justfile: PathBuf,
@ -86,11 +86,11 @@ pub(crate) enum Error<'src> {
message: String, message: String,
}, },
Io { Io {
recipe: &'src str, recipe: &'src str,
io_error: io::Error, io_error: io::Error,
}, },
Load { Load {
path: PathBuf, path: PathBuf,
io_error: io::Error, io_error: io::Error,
}, },
NoChoosableRecipes, NoChoosableRecipes,
@ -99,29 +99,29 @@ pub(crate) enum Error<'src> {
search_error: SearchError, search_error: SearchError,
}, },
Shebang { Shebang {
recipe: &'src str, recipe: &'src str,
command: String, command: String,
argument: Option<String>, argument: Option<String>,
io_error: io::Error, io_error: io::Error,
}, },
Signal { Signal {
recipe: &'src str, recipe: &'src str,
line_number: Option<usize>, line_number: Option<usize>,
signal: i32, signal: i32,
}, },
TmpdirIo { TmpdirIo {
recipe: &'src str, recipe: &'src str,
io_error: io::Error, io_error: io::Error,
}, },
Unknown { Unknown {
recipe: &'src str, recipe: &'src str,
line_number: Option<usize>, line_number: Option<usize>,
}, },
UnknownOverrides { UnknownOverrides {
overrides: Vec<String>, overrides: Vec<String>,
}, },
UnknownRecipes { UnknownRecipes {
recipes: Vec<String>, recipes: Vec<String>,
suggestion: Option<Suggestion<'src>>, suggestion: Option<Suggestion<'src>>,
}, },
Unstable { Unstable {
@ -204,7 +204,7 @@ impl<'src> ColorDisplay for Error<'src> {
min, min,
max, max,
.. ..
} => } => {
if min == max { if min == max {
let expected = min; let expected = min;
write!( write!(
@ -234,17 +234,18 @@ impl<'src> ColorDisplay for Error<'src> {
Count("argument", *found), Count("argument", *found),
max max
)?; )?;
}, }
}
Backtick { output_error, .. } => match output_error { Backtick { output_error, .. } => match output_error {
OutputError::Code(code) => { OutputError::Code(code) => {
write!(f, "Backtick failed with exit code {}", code)?; write!(f, "Backtick failed with exit code {}", code)?;
}, }
OutputError::Signal(signal) => { OutputError::Signal(signal) => {
write!(f, "Backtick was terminated by signal {}", signal)?; write!(f, "Backtick was terminated by signal {}", signal)?;
}, }
OutputError::Unknown => { OutputError::Unknown => {
write!(f, "Backtick failed for an unknown reason")?; write!(f, "Backtick failed for an unknown reason")?;
}, }
OutputError::Io(io_error) => { OutputError::Io(io_error) => {
match io_error.kind() { match io_error.kind() {
io::ErrorKind::NotFound => write!( io::ErrorKind::NotFound => write!(
@ -263,14 +264,14 @@ impl<'src> ColorDisplay for Error<'src> {
io_error io_error
), ),
}?; }?;
}, }
OutputError::Utf8(utf8_error) => { OutputError::Utf8(utf8_error) => {
write!( write!(
f, f,
"Backtick succeeded but stdout was not utf8: {}", "Backtick succeeded but stdout was not utf8: {}",
utf8_error utf8_error
)?; )?;
}, }
}, },
ChooserInvoke { ChooserInvoke {
shell_binary, shell_binary,
@ -286,7 +287,7 @@ impl<'src> ColorDisplay for Error<'src> {
chooser.to_string_lossy(), chooser.to_string_lossy(),
io_error, io_error,
)?; )?;
}, }
ChooserRead { chooser, io_error } => { ChooserRead { chooser, io_error } => {
write!( write!(
f, f,
@ -294,7 +295,7 @@ impl<'src> ColorDisplay for Error<'src> {
chooser.to_string_lossy(), chooser.to_string_lossy(),
io_error io_error
)?; )?;
}, }
ChooserStatus { chooser, status } => { ChooserStatus { chooser, status } => {
write!( write!(
f, f,
@ -302,7 +303,7 @@ impl<'src> ColorDisplay for Error<'src> {
chooser.to_string_lossy(), chooser.to_string_lossy(),
status status
)?; )?;
}, }
ChooserWrite { chooser, io_error } => { ChooserWrite { chooser, io_error } => {
write!( write!(
f, f,
@ -310,12 +311,12 @@ impl<'src> ColorDisplay for Error<'src> {
chooser.to_string_lossy(), chooser.to_string_lossy(),
io_error io_error
)?; )?;
}, }
Code { Code {
recipe, recipe,
line_number, line_number,
code, code,
} => } => {
if let Some(n) = line_number { if let Some(n) = line_number {
write!( write!(
f, f,
@ -324,7 +325,8 @@ impl<'src> ColorDisplay for Error<'src> {
)?; )?;
} else { } else {
write!(f, "Recipe `{}` failed with exit code {}", recipe, code)?; write!(f, "Recipe `{}` failed with exit code {}", recipe, code)?;
}, }
}
CommandInvoke { CommandInvoke {
binary, binary,
arguments, arguments,
@ -340,7 +342,7 @@ impl<'src> ColorDisplay for Error<'src> {
.join(" "), .join(" "),
io_error, io_error,
)?; )?;
}, }
CommandStatus { CommandStatus {
binary, binary,
arguments, arguments,
@ -356,7 +358,7 @@ impl<'src> ColorDisplay for Error<'src> {
.join(" "), .join(" "),
status, status,
)?; )?;
}, }
Compile { compile_error } => Display::fmt(compile_error, f)?, Compile { compile_error } => Display::fmt(compile_error, f)?,
Config { config_error } => Display::fmt(config_error, f)?, Config { config_error } => Display::fmt(config_error, f)?,
Cygpath { Cygpath {
@ -370,7 +372,7 @@ impl<'src> ColorDisplay for Error<'src> {
path", path",
code, recipe code, recipe
)?; )?;
}, }
OutputError::Signal(signal) => { OutputError::Signal(signal) => {
write!( write!(
f, f,
@ -378,7 +380,7 @@ impl<'src> ColorDisplay for Error<'src> {
path", path",
signal, recipe signal, recipe
)?; )?;
}, }
OutputError::Unknown => { OutputError::Unknown => {
write!( write!(
f, f,
@ -386,7 +388,7 @@ impl<'src> ColorDisplay for Error<'src> {
interpreter path", interpreter path",
recipe recipe
)?; )?;
}, }
OutputError::Io(io_error) => { OutputError::Io(io_error) => {
match io_error.kind() { match io_error.kind() {
io::ErrorKind::NotFound => write!( io::ErrorKind::NotFound => write!(
@ -403,7 +405,7 @@ impl<'src> ColorDisplay for Error<'src> {
), ),
_ => write!(f, "Could not run `cygpath` executable:\n{}", io_error), _ => write!(f, "Could not run `cygpath` executable:\n{}", io_error),
}?; }?;
}, }
OutputError::Utf8(utf8_error) => { OutputError::Utf8(utf8_error) => {
write!( write!(
f, f,
@ -411,7 +413,7 @@ impl<'src> ColorDisplay for Error<'src> {
not utf8: {}", not utf8: {}",
recipe, utf8_error recipe, utf8_error
)?; )?;
}, }
}, },
DefaultRecipeRequiresArguments { DefaultRecipeRequiresArguments {
recipe, recipe,
@ -424,10 +426,10 @@ impl<'src> ColorDisplay for Error<'src> {
min_arguments, min_arguments,
Count("argument", *min_arguments), Count("argument", *min_arguments),
)?; )?;
}, }
Dotenv { dotenv_error } => { Dotenv { dotenv_error } => {
write!(f, "Failed to load environment file: {}", dotenv_error)?; write!(f, "Failed to load environment file: {}", dotenv_error)?;
}, }
EditorInvoke { editor, io_error } => { EditorInvoke { editor, io_error } => {
write!( write!(
f, f,
@ -435,7 +437,7 @@ impl<'src> ColorDisplay for Error<'src> {
editor.to_string_lossy(), editor.to_string_lossy(),
io_error io_error
)?; )?;
}, }
EditorStatus { editor, status } => { EditorStatus { editor, status } => {
write!( write!(
f, f,
@ -443,7 +445,7 @@ impl<'src> ColorDisplay for Error<'src> {
editor.to_string_lossy(), editor.to_string_lossy(),
status status
)?; )?;
}, }
EvalUnknownVariable { EvalUnknownVariable {
variable, variable,
suggestion, suggestion,
@ -452,7 +454,7 @@ impl<'src> ColorDisplay for Error<'src> {
if let Some(suggestion) = *suggestion { if let Some(suggestion) = *suggestion {
write!(f, "\n{}", suggestion)?; write!(f, "\n{}", suggestion)?;
} }
}, }
FunctionCall { function, message } => { FunctionCall { function, message } => {
write!( write!(
f, f,
@ -460,10 +462,10 @@ impl<'src> ColorDisplay for Error<'src> {
function.lexeme(), function.lexeme(),
message message
)?; )?;
}, }
InitExists { justfile } => { InitExists { justfile } => {
write!(f, "Justfile `{}` already exists", justfile.display())?; write!(f, "Justfile `{}` already exists", justfile.display())?;
}, }
Internal { message } => { Internal { message } => {
write!( write!(
f, f,
@ -471,7 +473,7 @@ impl<'src> ColorDisplay for Error<'src> {
consider filing an issue: https://github.com/casey/just/issues/new", consider filing an issue: https://github.com/casey/just/issues/new",
message message
)?; )?;
}, }
Io { recipe, io_error } => { Io { recipe, io_error } => {
match io_error.kind() { match io_error.kind() {
io::ErrorKind::NotFound => write!( io::ErrorKind::NotFound => write!(
@ -490,7 +492,7 @@ impl<'src> ColorDisplay for Error<'src> {
recipe, io_error recipe, io_error
), ),
}?; }?;
}, }
Load { io_error, path } => { Load { io_error, path } => {
write!( write!(
f, f,
@ -498,20 +500,20 @@ impl<'src> ColorDisplay for Error<'src> {
path.display(), path.display(),
io_error io_error
)?; )?;
}, }
NoChoosableRecipes => { NoChoosableRecipes => {
write!(f, "Justfile contains no choosable recipes.")?; write!(f, "Justfile contains no choosable recipes.")?;
}, }
NoRecipes => { NoRecipes => {
write!(f, "Justfile contains no recipes.")?; write!(f, "Justfile contains no recipes.")?;
}, }
Search { search_error } => Display::fmt(search_error, f)?, Search { search_error } => Display::fmt(search_error, f)?,
Shebang { Shebang {
recipe, recipe,
command, command,
argument, argument,
io_error, io_error,
} => } => {
if let Some(argument) = argument { if let Some(argument) = argument {
write!( write!(
f, f,
@ -524,12 +526,13 @@ impl<'src> ColorDisplay for Error<'src> {
"Recipe `{}` with shebang `#!{}` execution error: {}", "Recipe `{}` with shebang `#!{}` execution error: {}",
recipe, command, io_error recipe, command, io_error
)?; )?;
}, }
}
Signal { Signal {
recipe, recipe,
line_number, line_number,
signal, signal,
} => } => {
if let Some(n) = line_number { if let Some(n) = line_number {
write!( write!(
f, f,
@ -538,7 +541,8 @@ impl<'src> ColorDisplay for Error<'src> {
)?; )?;
} else { } else {
write!(f, "Recipe `{}` was terminated by signal {}", recipe, signal)?; write!(f, "Recipe `{}` was terminated by signal {}", recipe, signal)?;
}, }
}
TmpdirIo { recipe, io_error } => write!( TmpdirIo { recipe, io_error } => write!(
f, f,
"Recipe `{}` could not be run because of an IO error while trying to create a temporary \ "Recipe `{}` could not be run because of an IO error while trying to create a temporary \
@ -548,7 +552,7 @@ impl<'src> ColorDisplay for Error<'src> {
Unknown { Unknown {
recipe, recipe,
line_number, line_number,
} => } => {
if let Some(n) = line_number { if let Some(n) = line_number {
write!( write!(
f, f,
@ -557,7 +561,8 @@ impl<'src> ColorDisplay for Error<'src> {
)?; )?;
} else { } else {
write!(f, "Recipe `{}` failed for an unknown reason", recipe)?; write!(f, "Recipe `{}` failed for an unknown reason", recipe)?;
}, }
}
UnknownOverrides { overrides } => { UnknownOverrides { overrides } => {
write!( write!(
f, f,
@ -565,7 +570,7 @@ impl<'src> ColorDisplay for Error<'src> {
Count("Variable", overrides.len()), Count("Variable", overrides.len()),
List::and_ticked(overrides), List::and_ticked(overrides),
)?; )?;
}, }
UnknownRecipes { UnknownRecipes {
recipes, recipes,
suggestion, suggestion,
@ -579,14 +584,14 @@ impl<'src> ColorDisplay for Error<'src> {
if let Some(suggestion) = *suggestion { if let Some(suggestion) = *suggestion {
write!(f, "\n{}", suggestion)?; write!(f, "\n{}", suggestion)?;
} }
}, }
Unstable { message } => { Unstable { message } => {
write!( write!(
f, f,
"{} Invoke `just` with the `--unstable` flag to enable unstable features.", "{} Invoke `just` with the `--unstable` flag to enable unstable features.",
message message
)?; )?;
}, }
WriteJustfile { justfile, io_error } => { WriteJustfile { justfile, io_error } => {
write!( write!(
f, f,
@ -594,7 +599,7 @@ impl<'src> ColorDisplay for Error<'src> {
justfile.display(), justfile.display(),
io_error io_error
)?; )?;
}, }
} }
write!(f, "{}", color.message().suffix())?; write!(f, "{}", color.message().suffix())?;

View File

@ -2,11 +2,11 @@ use crate::common::*;
pub(crate) struct Evaluator<'src: 'run, 'run> { pub(crate) struct Evaluator<'src: 'run, 'run> {
assignments: Option<&'run Table<'src, Assignment<'src>>>, assignments: Option<&'run Table<'src, Assignment<'src>>>,
config: &'run Config, config: &'run Config,
dotenv: &'run BTreeMap<String, String>, dotenv: &'run BTreeMap<String, String>,
scope: Scope<'src, 'run>, scope: Scope<'src, 'run>,
settings: &'run Settings<'run>, settings: &'run Settings<'run>,
search: &'run Search, search: &'run Search,
} }
impl<'src, 'run> Evaluator<'src, 'run> { impl<'src, 'run> Evaluator<'src, 'run> {
@ -64,22 +64,23 @@ impl<'src, 'run> Evaluator<'src, 'run> {
message: format!("attempted to evaluate undefined variable `{}`", variable), message: format!("attempted to evaluate undefined variable `{}`", variable),
}) })
} }
}, }
Expression::Call { thunk } => { Expression::Call { thunk } => {
use Thunk::*; use Thunk::*;
let context = FunctionContext { let context = FunctionContext {
dotenv: self.dotenv, dotenv: self.dotenv,
invocation_directory: &self.config.invocation_directory, invocation_directory: &self.config.invocation_directory,
search: self.search, search: self.search,
}; };
match thunk { match thunk {
Nullary { name, function, .. } => Nullary { name, function, .. } => {
function(&context).map_err(|message| Error::FunctionCall { function(&context).map_err(|message| Error::FunctionCall {
function: *name, function: *name,
message, message,
}), })
}
Unary { Unary {
name, name,
function, function,
@ -121,16 +122,18 @@ impl<'src, 'run> Evaluator<'src, 'run> {
message, message,
}), }),
} }
}, }
Expression::StringLiteral { string_literal } => Ok(string_literal.cooked.clone()), Expression::StringLiteral { string_literal } => Ok(string_literal.cooked.clone()),
Expression::Backtick { contents, token } => Expression::Backtick { contents, token } => {
if self.config.dry_run { if self.config.dry_run {
Ok(format!("`{}`", contents)) Ok(format!("`{}`", contents))
} else { } else {
Ok(self.run_backtick(contents, token)?) Ok(self.run_backtick(contents, token)?)
}, }
Expression::Concatination { lhs, rhs } => }
Ok(self.evaluate_expression(lhs)? + &self.evaluate_expression(rhs)?), Expression::Concatination { lhs, rhs } => {
Ok(self.evaluate_expression(lhs)? + &self.evaluate_expression(rhs)?)
}
Expression::Conditional { Expression::Conditional {
lhs, lhs,
rhs, rhs,
@ -146,7 +149,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
} else { } else {
self.evaluate_expression(otherwise) self.evaluate_expression(otherwise)
} }
}, }
Expression::Group { contents } => self.evaluate_expression(contents), Expression::Group { contents } => self.evaluate_expression(contents),
} }
} }
@ -192,10 +195,10 @@ impl<'src, 'run> Evaluator<'src, 'run> {
} else { } else {
evaluated += &lexeme; evaluated += &lexeme;
} }
}, }
Fragment::Interpolation { expression } => { Fragment::Interpolation { expression } => {
evaluated += &self.evaluate_expression(expression)?; evaluated += &self.evaluate_expression(expression)?;
}, }
} }
} }
Ok(evaluated) Ok(evaluated)

View File

@ -11,7 +11,7 @@ pub(crate) enum Expression<'src> {
/// `contents` /// `contents`
Backtick { Backtick {
contents: String, contents: String,
token: Token<'src>, token: Token<'src>,
}, },
/// `name(arguments)` /// `name(arguments)`
Call { thunk: Thunk<'src> }, Call { thunk: Thunk<'src> },
@ -22,11 +22,11 @@ pub(crate) enum Expression<'src> {
}, },
/// `if lhs == rhs { then } else { otherwise }` /// `if lhs == rhs { then } else { otherwise }`
Conditional { Conditional {
lhs: Box<Expression<'src>>, lhs: Box<Expression<'src>>,
rhs: Box<Expression<'src>>, rhs: Box<Expression<'src>>,
then: Box<Expression<'src>>, then: Box<Expression<'src>>,
otherwise: Box<Expression<'src>>, otherwise: Box<Expression<'src>>,
inverted: bool, inverted: bool,
}, },
/// `(contents)` /// `(contents)`
Group { contents: Box<Expression<'src>> }, Group { contents: Box<Expression<'src>> },

View File

@ -1,7 +1,7 @@
use crate::common::*; use crate::common::*;
pub(crate) struct FunctionContext<'run> { pub(crate) struct FunctionContext<'run> {
pub(crate) dotenv: &'run BTreeMap<String, String>, pub(crate) dotenv: &'run BTreeMap<String, String>,
pub(crate) invocation_directory: &'run Path, pub(crate) invocation_directory: &'run Path,
pub(crate) search: &'run Search, pub(crate) search: &'run Search,
} }

View File

@ -1,9 +1,9 @@
use crate::common::*; use crate::common::*;
pub(crate) struct InterruptHandler { pub(crate) struct InterruptHandler {
blocks: u32, blocks: u32,
interrupted: bool, interrupted: bool,
verbosity: Verbosity, verbosity: Verbosity,
} }
impl InterruptHandler { impl InterruptHandler {
@ -29,15 +29,15 @@ impl InterruptHandler {
.color_display(Color::auto().stderr()) .color_display(Color::auto().stderr())
); );
std::process::exit(EXIT_FAILURE); std::process::exit(EXIT_FAILURE);
}, }
} }
} }
fn new() -> Self { fn new() -> Self {
Self { Self {
blocks: 0, blocks: 0,
interrupted: false, interrupted: false,
verbosity: Verbosity::default(), verbosity: Verbosity::default(),
} }
} }

View File

@ -2,11 +2,11 @@ use crate::common::*;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub(crate) struct Justfile<'src> { pub(crate) struct Justfile<'src> {
pub(crate) recipes: Table<'src, Rc<Recipe<'src>>>, pub(crate) recipes: Table<'src, Rc<Recipe<'src>>>,
pub(crate) assignments: Table<'src, Assignment<'src>>, pub(crate) assignments: Table<'src, Assignment<'src>>,
pub(crate) aliases: Table<'src, Alias<'src>>, pub(crate) aliases: Table<'src, Alias<'src>>,
pub(crate) settings: Settings<'src>, pub(crate) settings: Settings<'src>,
pub(crate) warnings: Vec<Warning>, pub(crate) warnings: Vec<Warning>,
} }
impl<'src> Justfile<'src> { impl<'src> Justfile<'src> {
@ -33,16 +33,19 @@ impl<'src> Justfile<'src> {
.recipes .recipes
.keys() .keys()
.map(|name| { .map(|name| {
(edit_distance(name, input), Suggestion { (
name, edit_distance(name, input),
target: None, Suggestion { name, target: None },
}) )
}) })
.chain(self.aliases.iter().map(|(name, alias)| { .chain(self.aliases.iter().map(|(name, alias)| {
(edit_distance(name, input), Suggestion { (
name, edit_distance(name, input),
target: Some(alias.target.name.lexeme()), Suggestion {
}) name,
target: Some(alias.target.name.lexeme()),
},
)
})) }))
.filter(|(distance, _suggestion)| distance < &3) .filter(|(distance, _suggestion)| distance < &3)
.collect::<Vec<(usize, Suggestion)>>(); .collect::<Vec<(usize, Suggestion)>>();
@ -59,10 +62,10 @@ impl<'src> Justfile<'src> {
.assignments .assignments
.keys() .keys()
.map(|name| { .map(|name| {
(edit_distance(name, input), Suggestion { (
name, edit_distance(name, input),
target: None, Suggestion { name, target: None },
}) )
}) })
.filter(|(distance, _suggestion)| distance < &3) .filter(|(distance, _suggestion)| distance < &3)
.collect::<Vec<(usize, Suggestion)>>(); .collect::<Vec<(usize, Suggestion)>>();
@ -168,7 +171,7 @@ impl<'src> Justfile<'src> {
}; };
return Ok(()); return Ok(());
}, }
Subcommand::Evaluate { variable, .. } => { Subcommand::Evaluate { variable, .. } => {
if let Some(variable) = variable { if let Some(variable) = variable {
if let Some(value) = scope.value(variable) { if let Some(value) = scope.value(variable) {
@ -176,7 +179,7 @@ impl<'src> Justfile<'src> {
} else { } else {
return Err(Error::EvalUnknownVariable { return Err(Error::EvalUnknownVariable {
suggestion: self.suggest_variable(&variable), suggestion: self.suggest_variable(&variable),
variable: variable.clone(), variable: variable.clone(),
}); });
} }
} else { } else {
@ -197,8 +200,8 @@ impl<'src> Justfile<'src> {
} }
return Ok(()); return Ok(());
}, }
_ => {}, _ => {}
} }
let argvec: Vec<&str> = if !arguments.is_empty() { let argvec: Vec<&str> = if !arguments.is_empty() {
@ -231,11 +234,11 @@ impl<'src> Justfile<'src> {
let argument_count = cmp::min(tail.len(), recipe.max_arguments()); let argument_count = cmp::min(tail.len(), recipe.max_arguments());
if !argument_range.range_contains(&argument_count) { if !argument_range.range_contains(&argument_count) {
return Err(Error::ArgumentCountMismatch { return Err(Error::ArgumentCountMismatch {
recipe: recipe.name(), recipe: recipe.name(),
parameters: recipe.parameters.clone(), parameters: recipe.parameters.clone(),
found: tail.len(), found: tail.len(),
min: recipe.min_arguments(), min: recipe.min_arguments(),
max: recipe.max_arguments(), max: recipe.max_arguments(),
}); });
} }
grouped.push((recipe, &tail[0..argument_count])); grouped.push((recipe, &tail[0..argument_count]));

View File

@ -13,27 +13,27 @@ use TokenKind::*;
/// bad. /// bad.
pub(crate) struct Lexer<'src> { pub(crate) struct Lexer<'src> {
/// Source text /// Source text
src: &'src str, src: &'src str,
/// Char iterator /// Char iterator
chars: Chars<'src>, chars: Chars<'src>,
/// Tokens /// Tokens
tokens: Vec<Token<'src>>, tokens: Vec<Token<'src>>,
/// Current token start /// Current token start
token_start: Position, token_start: Position,
/// Current token end /// Current token end
token_end: Position, token_end: Position,
/// Next character to be lexed /// Next character to be lexed
next: Option<char>, next: Option<char>,
/// Next indent will start a recipe body /// Next indent will start a recipe body
recipe_body_pending: bool, recipe_body_pending: bool,
/// Inside recipe body /// Inside recipe body
recipe_body: bool, recipe_body: bool,
/// Indentation stack /// Indentation stack
indentation: Vec<&'src str>, indentation: Vec<&'src str>,
/// Interpolation token start stack /// Interpolation token start stack
interpolation_stack: Vec<Token<'src>>, interpolation_stack: Vec<Token<'src>>,
/// Current open delimiters /// Current open delimiters
open_delimiters: Vec<(Delimiter, usize)>, open_delimiters: Vec<(Delimiter, usize)>,
} }
impl<'src> Lexer<'src> { impl<'src> Lexer<'src> {
@ -50,7 +50,7 @@ impl<'src> Lexer<'src> {
let start = Position { let start = Position {
offset: 0, offset: 0,
column: 0, column: 0,
line: 0, line: 0,
}; };
Lexer { Lexer {
@ -86,7 +86,7 @@ impl<'src> Lexer<'src> {
self.next = self.chars.next(); self.next = self.chars.next();
Ok(()) Ok(())
}, }
None => Err(self.internal_error("Lexer advanced past end of text")), None => Err(self.internal_error("Lexer advanced past end of text")),
} }
} }
@ -202,12 +202,12 @@ impl<'src> Lexer<'src> {
fn internal_error(&self, message: impl Into<String>) -> CompileError<'src> { fn internal_error(&self, message: impl Into<String>) -> CompileError<'src> {
// Use `self.token_end` as the location of the error // Use `self.token_end` as the location of the error
let token = Token { let token = Token {
src: self.src, src: self.src,
offset: self.token_end.offset, offset: self.token_end.offset,
line: self.token_end.line, line: self.token_end.line,
column: self.token_end.column, column: self.token_end.column,
length: 0, length: 0,
kind: Unspecified, kind: Unspecified,
}; };
CompileError { CompileError {
kind: CompileErrorKind::Internal { kind: CompileErrorKind::Internal {
@ -226,11 +226,12 @@ impl<'src> Lexer<'src> {
UnterminatedString | UnterminatedBacktick => { UnterminatedString | UnterminatedBacktick => {
let kind = match StringKind::from_token_start(self.lexeme()) { let kind = match StringKind::from_token_start(self.lexeme()) {
Some(kind) => kind, Some(kind) => kind,
None => None => {
return self.internal_error("Lexer::error: expected string or backtick token start"), return self.internal_error("Lexer::error: expected string or backtick token start")
}
}; };
kind.delimiter().len() kind.delimiter().len()
}, }
// highlight the full token // highlight the full token
_ => self.lexeme().len(), _ => self.lexeme().len(),
}; };
@ -250,7 +251,7 @@ impl<'src> Lexer<'src> {
fn unterminated_interpolation_error(interpolation_start: Token<'src>) -> CompileError<'src> { fn unterminated_interpolation_error(interpolation_start: Token<'src>) -> CompileError<'src> {
CompileError { CompileError {
token: interpolation_start, token: interpolation_start,
kind: UnterminatedInterpolation, kind: UnterminatedInterpolation,
} }
} }
@ -304,7 +305,7 @@ impl<'src> Lexer<'src> {
} else { } else {
self.lex_normal(first)?; self.lex_normal(first)?;
}; };
}, }
None => break, None => break,
} }
} }
@ -411,7 +412,7 @@ impl<'src> Lexer<'src> {
}; };
Ok(()) Ok(())
}, }
Continue => { Continue => {
if !self.indentation().is_empty() { if !self.indentation().is_empty() {
for _ in self.indentation().chars() { for _ in self.indentation().chars() {
@ -422,7 +423,7 @@ impl<'src> Lexer<'src> {
} }
Ok(()) Ok(())
}, }
Decrease => { Decrease => {
while self.indentation() != whitespace { while self.indentation() != whitespace {
self.lex_dedent(); self.lex_dedent();
@ -437,14 +438,14 @@ impl<'src> Lexer<'src> {
} }
Ok(()) Ok(())
}, }
Mixed { whitespace } => { Mixed { whitespace } => {
for _ in whitespace.chars() { for _ in whitespace.chars() {
self.advance()?; self.advance()?;
} }
Err(self.error(MixedLeadingWhitespace { whitespace })) Err(self.error(MixedLeadingWhitespace { whitespace }))
}, }
Inconsistent => { Inconsistent => {
for _ in whitespace.chars() { for _ in whitespace.chars() {
self.advance()?; self.advance()?;
@ -452,9 +453,9 @@ impl<'src> Lexer<'src> {
Err(self.error(InconsistentLeadingWhitespace { Err(self.error(InconsistentLeadingWhitespace {
expected: self.indentation(), expected: self.indentation(),
found: whitespace, found: whitespace,
})) }))
}, }
Increase => { Increase => {
while self.next_is_whitespace() { while self.next_is_whitespace() {
self.advance()?; self.advance()?;
@ -472,7 +473,7 @@ impl<'src> Lexer<'src> {
} }
Ok(()) Ok(())
}, }
} }
} }
@ -504,7 +505,7 @@ impl<'src> Lexer<'src> {
_ => { _ => {
self.advance()?; self.advance()?;
Err(self.error(UnknownStartOfToken)) Err(self.error(UnknownStartOfToken))
}, }
} }
} }
@ -585,7 +586,7 @@ impl<'src> Lexer<'src> {
.interpolation_stack .interpolation_stack
.push(self.tokens[self.tokens.len() - 1]); .push(self.tokens[self.tokens.len() - 1]);
Ok(()) Ok(())
}, }
EndOfFile => Ok(()), EndOfFile => Ok(()),
} }
} }
@ -644,11 +645,12 @@ impl<'src> Lexer<'src> {
BracketR => self.close_delimiter(Bracket)?, BracketR => self.close_delimiter(Bracket)?,
ParenL => self.open_delimiter(Paren), ParenL => self.open_delimiter(Paren),
ParenR => self.close_delimiter(Paren)?, ParenR => self.close_delimiter(Paren)?,
_ => _ => {
return Err(self.internal_error(format!( return Err(self.internal_error(format!(
"Lexer::lex_delimiter called with non-delimiter token: `{}`", "Lexer::lex_delimiter called with non-delimiter token: `{}`",
kind, kind,
))), )))
}
} }
// Emit the delimiter token // Emit the delimiter token
@ -947,8 +949,9 @@ mod tests {
Dedent | Eof => "", Dedent | Eof => "",
// Variable lexemes // Variable lexemes
Text | StringToken | Backtick | Identifier | Comment | Unspecified => Text | StringToken | Backtick | Identifier | Comment | Unspecified => {
panic!("Token {:?} has no default lexeme", kind), panic!("Token {:?} has no default lexeme", kind)
}
} }
} }
@ -992,7 +995,7 @@ mod tests {
kind, kind,
}; };
assert_eq!(have, want); assert_eq!(have, want);
}, }
} }
} }

View File

@ -5,21 +5,21 @@ use crate::common::*;
pub struct List<T: Display, I: Iterator<Item = T> + Clone> { pub struct List<T: Display, I: Iterator<Item = T> + Clone> {
conjunction: &'static str, conjunction: &'static str,
values: I, values: I,
} }
impl<T: Display, I: Iterator<Item = T> + Clone> List<T, I> { impl<T: Display, I: Iterator<Item = T> + Clone> List<T, I> {
pub fn or<II: IntoIterator<Item = T, IntoIter = I>>(values: II) -> List<T, I> { pub fn or<II: IntoIterator<Item = T, IntoIter = I>>(values: II) -> List<T, I> {
List { List {
conjunction: "or", conjunction: "or",
values: values.into_iter(), values: values.into_iter(),
} }
} }
pub fn and<II: IntoIterator<Item = T, IntoIter = I>>(values: II) -> List<T, I> { pub fn and<II: IntoIterator<Item = T, IntoIter = I>>(values: II) -> List<T, I> {
List { List {
conjunction: "and", conjunction: "and",
values: values.into_iter(), values: values.into_iter(),
} }
} }
@ -68,11 +68,11 @@ impl<T: Display, I: Iterator<Item = T> + Clone> Display for List<T, I> {
write!(f, ", {}", c)?; write!(f, ", {}", c)?;
current = Some(n); current = Some(n);
next = values.next(); next = values.next();
}, }
(Some(c), None) => { (Some(c), None) => {
write!(f, ", {} {}", self.conjunction, c)?; write!(f, ", {} {}", self.conjunction, c)?;
return Ok(()); return Ok(());
}, }
_ => unreachable!("Iterator was fused, but returned Some after None"), _ => unreachable!("Iterator was fused, but returned Some after None"),
} }
} }

View File

@ -6,9 +6,9 @@ use crate::common::*;
pub(crate) struct Name<'src> { pub(crate) struct Name<'src> {
pub(crate) offset: usize, pub(crate) offset: usize,
pub(crate) length: usize, pub(crate) length: usize,
pub(crate) line: usize, pub(crate) line: usize,
pub(crate) column: usize, pub(crate) column: usize,
pub(crate) src: &'src str, pub(crate) src: &'src str,
} }
impl<'src> Name<'src> { impl<'src> Name<'src> {
@ -20,12 +20,12 @@ impl<'src> Name<'src> {
/// Turn this name back into a token /// Turn this name back into a token
pub(crate) fn token(&self) -> Token<'src> { pub(crate) fn token(&self) -> Token<'src> {
Token { Token {
kind: TokenKind::Identifier, kind: TokenKind::Identifier,
offset: self.offset, offset: self.offset,
length: self.length, length: self.length,
line: self.line, line: self.line,
column: self.column, column: self.column,
src: self.src, src: self.src,
} }
} }
@ -34,9 +34,9 @@ impl<'src> Name<'src> {
Name { Name {
offset: token.offset, offset: token.offset,
length: token.length, length: token.length,
line: token.line, line: token.line,
column: token.column, column: token.column,
src: token.src, src: token.src,
} }
} }

View File

@ -71,7 +71,7 @@ impl<'src> Node<'src> for Expression<'src> {
tree.push_mut(then.tree()); tree.push_mut(then.tree());
tree.push_mut(otherwise.tree()); tree.push_mut(otherwise.tree());
tree tree
}, }
Expression::Call { thunk } => { Expression::Call { thunk } => {
use Thunk::*; use Thunk::*;
@ -82,14 +82,14 @@ impl<'src> Node<'src> for Expression<'src> {
Unary { name, arg, .. } => { Unary { name, arg, .. } => {
tree.push_mut(name.lexeme()); tree.push_mut(name.lexeme());
tree.push_mut(arg.tree()); tree.push_mut(arg.tree());
}, }
Binary { Binary {
name, args: [a, b], .. name, args: [a, b], ..
} => { } => {
tree.push_mut(name.lexeme()); tree.push_mut(name.lexeme());
tree.push_mut(a.tree()); tree.push_mut(a.tree());
tree.push_mut(b.tree()); tree.push_mut(b.tree());
}, }
Ternary { Ternary {
name, name,
args: [a, b, c], args: [a, b, c],
@ -99,11 +99,11 @@ impl<'src> Node<'src> for Expression<'src> {
tree.push_mut(a.tree()); tree.push_mut(a.tree());
tree.push_mut(b.tree()); tree.push_mut(b.tree());
tree.push_mut(c.tree()); tree.push_mut(c.tree());
}, }
} }
tree tree
}, }
Expression::Variable { name } => Tree::atom(name.lexeme()), Expression::Variable { name } => Tree::atom(name.lexeme()),
Expression::StringLiteral { Expression::StringLiteral {
string_literal: StringLiteral { cooked, .. }, string_literal: StringLiteral { cooked, .. },
@ -213,14 +213,15 @@ impl<'src> Node<'src> for Set<'src> {
set.push_mut(self.name.lexeme().replace('-', "_")); set.push_mut(self.name.lexeme().replace('-', "_"));
match &self.value { match &self.value {
DotenvLoad(value) | Export(value) | PositionalArguments(value) => DotenvLoad(value) | Export(value) | PositionalArguments(value) => {
set.push_mut(value.to_string()), set.push_mut(value.to_string());
}
Shell(setting::Shell { command, arguments }) => { Shell(setting::Shell { command, arguments }) => {
set.push_mut(Tree::string(&command.cooked)); set.push_mut(Tree::string(&command.cooked));
for argument in arguments { for argument in arguments {
set.push_mut(Tree::string(&argument.cooked)); set.push_mut(Tree::string(&argument.cooked));
} }
}, }
} }
set set

View File

@ -28,7 +28,7 @@ pub(crate) fn output(mut command: Command) -> Result<String, OutputError> {
.to_owned(), .to_owned(),
), ),
} }
}, }
Err(io_error) => Err(OutputError::Io(io_error)), Err(io_error) => Err(OutputError::Io(io_error)),
} }
} }

View File

@ -4,13 +4,13 @@ use crate::common::*;
#[derive(PartialEq, Debug, Clone)] #[derive(PartialEq, Debug, Clone)]
pub(crate) struct Parameter<'src> { pub(crate) struct Parameter<'src> {
/// The parameter name /// The parameter name
pub(crate) name: Name<'src>, pub(crate) name: Name<'src>,
/// The kind of parameter /// The kind of parameter
pub(crate) kind: ParameterKind, pub(crate) kind: ParameterKind,
/// An optional default expression /// An optional default expression
pub(crate) default: Option<Expression<'src>>, pub(crate) default: Option<Expression<'src>>,
/// Export parameter as environment variable /// Export parameter as environment variable
pub(crate) export: bool, pub(crate) export: bool,
} }
impl<'src> ColorDisplay for Parameter<'src> { impl<'src> ColorDisplay for Parameter<'src> {

View File

@ -27,9 +27,9 @@ use TokenKind::*;
/// contents of the set is printed in the resultant error message. /// contents of the set is printed in the resultant error message.
pub(crate) struct Parser<'tokens, 'src> { pub(crate) struct Parser<'tokens, 'src> {
/// Source tokens /// Source tokens
tokens: &'tokens [Token<'src>], tokens: &'tokens [Token<'src>],
/// Index of the next un-parsed token /// Index of the next un-parsed token
next: usize, next: usize,
/// Current expected tokens /// Current expected tokens
expected: BTreeSet<TokenKind>, expected: BTreeSet<TokenKind>,
} }
@ -58,7 +58,7 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
fn unexpected_token(&self) -> CompileResult<'src, CompileError<'src>> { fn unexpected_token(&self) -> CompileResult<'src, CompileError<'src>> {
self.error(CompileErrorKind::UnexpectedToken { self.error(CompileErrorKind::UnexpectedToken {
expected: self.expected.iter().cloned().collect::<Vec<TokenKind>>(), expected: self.expected.iter().cloned().collect::<Vec<TokenKind>>(),
found: self.next()?.kind, found: self.next()?.kind,
}) })
} }
@ -101,10 +101,11 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
let mut rest = self.rest(); let mut rest = self.rest();
for kind in kinds { for kind in kinds {
match rest.next() { match rest.next() {
Some(token) => Some(token) => {
if token.kind != *kind { if token.kind != *kind {
return false; return false;
}, }
}
None => return false, None => return false,
} }
} }
@ -322,22 +323,27 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
break; break;
} else if self.next_is(Identifier) { } else if self.next_is(Identifier) {
match Keyword::from_lexeme(next.lexeme()) { match Keyword::from_lexeme(next.lexeme()) {
Some(Keyword::Alias) if self.next_are(&[Identifier, Identifier, Equals]) => Some(Keyword::Alias) if self.next_are(&[Identifier, Identifier, Equals]) => {
return Err(self.get(2)?.error(CompileErrorKind::DeprecatedEquals)), return Err(self.get(2)?.error(CompileErrorKind::DeprecatedEquals))
Some(Keyword::Alias) if self.next_are(&[Identifier, Identifier, ColonEquals]) => }
items.push(Item::Alias(self.parse_alias()?)), Some(Keyword::Alias) if self.next_are(&[Identifier, Identifier, ColonEquals]) => {
Some(Keyword::Export) if self.next_are(&[Identifier, Identifier, Equals]) => items.push(Item::Alias(self.parse_alias()?));
return Err(self.get(2)?.error(CompileErrorKind::DeprecatedEquals)), }
Some(Keyword::Export) if self.next_are(&[Identifier, Identifier, Equals]) => {
return Err(self.get(2)?.error(CompileErrorKind::DeprecatedEquals))
}
Some(Keyword::Export) if self.next_are(&[Identifier, Identifier, ColonEquals]) => { Some(Keyword::Export) if self.next_are(&[Identifier, Identifier, ColonEquals]) => {
self.presume_keyword(Keyword::Export)?; self.presume_keyword(Keyword::Export)?;
items.push(Item::Assignment(self.parse_assignment(true)?)); items.push(Item::Assignment(self.parse_assignment(true)?));
}, }
Some(Keyword::Set) Some(Keyword::Set)
if self.next_are(&[Identifier, Identifier, ColonEquals]) if self.next_are(&[Identifier, Identifier, ColonEquals])
|| self.next_are(&[Identifier, Identifier, Eol]) || self.next_are(&[Identifier, Identifier, Eol])
|| self.next_are(&[Identifier, Identifier, Eof]) => || self.next_are(&[Identifier, Identifier, Eof]) =>
items.push(Item::Set(self.parse_set()?)), {
_ => items.push(Item::Set(self.parse_set()?));
}
_ => {
if self.next_are(&[Identifier, Equals]) { if self.next_are(&[Identifier, Equals]) {
return Err(self.get(1)?.error(CompileErrorKind::DeprecatedEquals)); return Err(self.get(1)?.error(CompileErrorKind::DeprecatedEquals));
} else if self.next_are(&[Identifier, ColonEquals]) { } else if self.next_are(&[Identifier, ColonEquals]) {
@ -345,7 +351,8 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
} else { } else {
let doc = pop_doc_comment(&mut items, eol_since_last_comment); let doc = pop_doc_comment(&mut items, eol_since_last_comment);
items.push(Item::Recipe(self.parse_recipe(doc, false)?)); items.push(Item::Recipe(self.parse_recipe(doc, false)?));
}, }
}
} }
} else if self.accepted(At)? { } else if 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);
@ -516,13 +523,13 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
'r' => cooked.push('\r'), 'r' => cooked.push('\r'),
't' => cooked.push('\t'), 't' => cooked.push('\t'),
'\\' => cooked.push('\\'), '\\' => cooked.push('\\'),
'\n' => {}, '\n' => {}
'"' => cooked.push('"'), '"' => cooked.push('"'),
other => { other => {
return Err( return Err(
token.error(CompileErrorKind::InvalidEscapeSequence { character: other }), token.error(CompileErrorKind::InvalidEscapeSequence { character: other }),
); );
}, }
} }
escape = false; escape = false;
} else if c == '\\' { } else if c == '\\' {
@ -715,7 +722,7 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
} else { } else {
return Err(identifier.error(CompileErrorKind::ExpectedKeyword { return Err(identifier.error(CompileErrorKind::ExpectedKeyword {
expected: vec![Keyword::True, Keyword::False], expected: vec![Keyword::True, Keyword::False],
found: identifier.lexeme(), found: identifier.lexeme(),
})); }));
}; };
@ -859,7 +866,7 @@ mod tests {
kind, kind,
}; };
assert_eq!(have, want); assert_eq!(have, want);
}, }
} }
} }

View File

@ -3,5 +3,5 @@
pub(crate) struct Position { pub(crate) struct Position {
pub(crate) offset: usize, pub(crate) offset: usize,
pub(crate) column: usize, pub(crate) column: usize,
pub(crate) line: usize, pub(crate) line: usize,
} }

View File

@ -29,11 +29,11 @@ use crate::common::*;
#[cfg_attr(test, derive(PartialEq, Debug))] #[cfg_attr(test, derive(PartialEq, Debug))]
pub struct Positional { pub struct Positional {
/// Overrides from values of the form `[a-zA-Z_][a-zA-Z0-9_-]*=.*` /// Overrides from values of the form `[a-zA-Z_][a-zA-Z0-9_-]*=.*`
pub overrides: Vec<(String, String)>, pub overrides: Vec<(String, String)>,
/// An argument equal to '.', '..', or ending with `/` /// An argument equal to '.', '..', or ending with `/`
pub search_directory: Option<String>, pub search_directory: Option<String>,
/// Everything else /// Everything else
pub arguments: Vec<String>, pub arguments: Vec<String>,
} }
impl Positional { impl Positional {

View File

@ -21,15 +21,15 @@ fn error_from_signal(recipe: &str, line_number: Option<usize>, exit_status: Exit
/// A recipe, e.g. `foo: bar baz` /// A recipe, e.g. `foo: bar baz`
#[derive(PartialEq, Debug, Clone)] #[derive(PartialEq, Debug, Clone)]
pub(crate) struct Recipe<'src, D = Dependency<'src>> { pub(crate) struct Recipe<'src, D = Dependency<'src>> {
pub(crate) body: Vec<Line<'src>>, pub(crate) body: Vec<Line<'src>>,
pub(crate) dependencies: Vec<D>, pub(crate) dependencies: Vec<D>,
pub(crate) doc: Option<&'src str>, pub(crate) doc: Option<&'src str>,
pub(crate) name: Name<'src>, pub(crate) name: Name<'src>,
pub(crate) parameters: Vec<Parameter<'src>>, pub(crate) parameters: Vec<Parameter<'src>>,
pub(crate) private: bool, pub(crate) private: bool,
pub(crate) quiet: bool, pub(crate) quiet: bool,
pub(crate) shebang: bool, pub(crate) shebang: bool,
pub(crate) priors: usize, pub(crate) priors: usize,
} }
impl<'src, D> Recipe<'src, D> { impl<'src, D> Recipe<'src, D> {
@ -116,7 +116,7 @@ impl<'src, D> Recipe<'src, D> {
.prefix("just") .prefix("just")
.tempdir() .tempdir()
.map_err(|error| Error::TmpdirIo { .map_err(|error| Error::TmpdirIo {
recipe: self.name(), recipe: self.name(),
io_error: error, io_error: error,
})?; })?;
let mut path = tmp.path().to_path_buf(); let mut path = tmp.path().to_path_buf();
@ -125,7 +125,7 @@ impl<'src, D> Recipe<'src, D> {
{ {
let mut f = fs::File::create(&path).map_err(|error| Error::TmpdirIo { let mut f = fs::File::create(&path).map_err(|error| Error::TmpdirIo {
recipe: self.name(), recipe: self.name(),
io_error: error, io_error: error,
})?; })?;
let mut text = String::new(); let mut text = String::new();
@ -153,14 +153,14 @@ impl<'src, D> Recipe<'src, D> {
f.write_all(text.as_bytes()) f.write_all(text.as_bytes())
.map_err(|error| Error::TmpdirIo { .map_err(|error| Error::TmpdirIo {
recipe: self.name(), recipe: self.name(),
io_error: error, io_error: error,
})?; })?;
} }
// make the script executable // make the script executable
Platform::set_execute_permission(&path).map_err(|error| Error::TmpdirIo { Platform::set_execute_permission(&path).map_err(|error| Error::TmpdirIo {
recipe: self.name(), recipe: self.name(),
io_error: error, io_error: error,
})?; })?;
@ -181,7 +181,7 @@ impl<'src, D> Recipe<'src, D> {
// run it! // run it!
match InterruptHandler::guard(|| command.status()) { match InterruptHandler::guard(|| command.status()) {
Ok(exit_status) => Ok(exit_status) => {
if let Some(code) = exit_status.code() { if let Some(code) = exit_status.code() {
if code != 0 { if code != 0 {
return Err(Error::Code { return Err(Error::Code {
@ -192,7 +192,8 @@ impl<'src, D> Recipe<'src, D> {
} }
} else { } else {
return Err(error_from_signal(self.name(), None, exit_status)); return Err(error_from_signal(self.name(), None, exit_status));
}, }
}
Err(io_error) => { Err(io_error) => {
return Err(Error::Shebang { return Err(Error::Shebang {
recipe: self.name(), recipe: self.name(),
@ -200,7 +201,7 @@ impl<'src, D> Recipe<'src, D> {
argument: shebang.argument.map(String::from), argument: shebang.argument.map(String::from),
io_error, io_error,
}); });
}, }
}; };
} else { } else {
let mut lines = self.body.iter().peekable(); let mut lines = self.body.iter().peekable();
@ -274,7 +275,7 @@ impl<'src, D> Recipe<'src, D> {
cmd.export(context.settings, dotenv, &scope); cmd.export(context.settings, dotenv, &scope);
match InterruptHandler::guard(|| cmd.status()) { match InterruptHandler::guard(|| cmd.status()) {
Ok(exit_status) => Ok(exit_status) => {
if let Some(code) = exit_status.code() { if let Some(code) = exit_status.code() {
if code != 0 && !infallable_command { if code != 0 && !infallable_command {
return Err(Error::Code { return Err(Error::Code {
@ -289,13 +290,14 @@ impl<'src, D> Recipe<'src, D> {
Some(line_number), Some(line_number),
exit_status, exit_status,
)); ));
}, }
}
Err(io_error) => { Err(io_error) => {
return Err(Error::Io { return Err(Error::Io {
recipe: self.name(), recipe: self.name(),
io_error, io_error,
}); });
}, }
}; };
} }
} }

View File

@ -1,8 +1,8 @@
use crate::common::*; use crate::common::*;
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) scope: Scope<'src, 'run>, pub(crate) scope: 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>,
} }

View File

@ -4,8 +4,8 @@ use CompileErrorKind::*;
pub(crate) struct RecipeResolver<'src: 'run, 'run> { pub(crate) struct RecipeResolver<'src: 'run, 'run> {
unresolved_recipes: Table<'src, UnresolvedRecipe<'src>>, unresolved_recipes: Table<'src, UnresolvedRecipe<'src>>,
resolved_recipes: Table<'src, Rc<Recipe<'src>>>, resolved_recipes: Table<'src, Rc<Recipe<'src>>>,
assignments: &'run Table<'src, Assignment<'src>>, assignments: &'run Table<'src, Assignment<'src>>,
} }
impl<'src: 'run, 'run> RecipeResolver<'src, 'run> { impl<'src: 'run, 'run> RecipeResolver<'src, 'run> {
@ -107,7 +107,7 @@ impl<'src: 'run, 'run> RecipeResolver<'src, 'run> {
} else { } else {
// dependency is unknown // dependency is unknown
return Err(dependency.recipe.error(UnknownDependency { return Err(dependency.recipe.error(UnknownDependency {
recipe: recipe.name(), recipe: recipe.name(),
unknown: name, unknown: name,
})); }));
} }

View File

@ -2,21 +2,21 @@ use crate::common::*;
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct Scope<'src: 'run, 'run> { pub(crate) struct Scope<'src: 'run, 'run> {
parent: Option<&'run Scope<'src, 'run>>, parent: Option<&'run Scope<'src, 'run>>,
bindings: Table<'src, Binding<'src, String>>, bindings: Table<'src, Binding<'src, String>>,
} }
impl<'src, 'run> Scope<'src, 'run> { impl<'src, 'run> Scope<'src, 'run> {
pub(crate) fn child(&'run self) -> Scope<'src, 'run> { pub(crate) fn child(&'run self) -> Scope<'src, 'run> {
Scope { Scope {
parent: Some(self), parent: Some(self),
bindings: Table::new(), bindings: Table::new(),
} }
} }
pub(crate) fn new() -> Scope<'src, 'run> { pub(crate) fn new() -> Scope<'src, 'run> {
Scope { Scope {
parent: None, parent: None,
bindings: Table::new(), bindings: Table::new(),
} }
} }

View File

@ -7,7 +7,7 @@ const JUSTFILE_NAMES: &[&str] = &["justfile", ".justfile"];
const PROJECT_ROOT_CHILDREN: &[&str] = &[".bzr", ".git", ".hg", ".svn", "_darcs"]; const PROJECT_ROOT_CHILDREN: &[&str] = &[".bzr", ".git", ".hg", ".svn", "_darcs"];
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,
} }
@ -26,7 +26,7 @@ impl Search {
justfile, justfile,
working_directory, working_directory,
}) })
}, }
SearchConfig::FromSearchDirectory { search_directory } => { SearchConfig::FromSearchDirectory { search_directory } => {
let search_directory = Self::clean(invocation_directory, search_directory); let search_directory = Self::clean(invocation_directory, search_directory);
@ -39,7 +39,7 @@ impl Search {
justfile, justfile,
working_directory, working_directory,
}) })
}, }
SearchConfig::WithJustfile { justfile } => { SearchConfig::WithJustfile { justfile } => {
let justfile = Self::clean(invocation_directory, justfile); let justfile = Self::clean(invocation_directory, justfile);
@ -50,13 +50,13 @@ impl Search {
justfile, justfile,
working_directory, working_directory,
}) })
}, }
SearchConfig::WithJustfileAndWorkingDirectory { SearchConfig::WithJustfileAndWorkingDirectory {
justfile, justfile,
working_directory, working_directory,
} => Ok(Self { } => Ok(Self {
justfile: Self::clean(invocation_directory, justfile), justfile: Self::clean(invocation_directory, justfile),
working_directory: Self::clean(invocation_directory, working_directory), working_directory: Self::clean(invocation_directory, working_directory),
}), }),
} }
@ -76,7 +76,7 @@ impl Search {
justfile, justfile,
working_directory, working_directory,
}) })
}, }
SearchConfig::FromSearchDirectory { search_directory } => { SearchConfig::FromSearchDirectory { search_directory } => {
let search_directory = Self::clean(invocation_directory, search_directory); let search_directory = Self::clean(invocation_directory, search_directory);
@ -89,7 +89,7 @@ impl Search {
justfile, justfile,
working_directory, working_directory,
}) })
}, }
SearchConfig::WithJustfile { justfile } => { SearchConfig::WithJustfile { justfile } => {
let justfile = Self::clean(invocation_directory, justfile); let justfile = Self::clean(invocation_directory, justfile);
@ -100,13 +100,13 @@ impl Search {
justfile, justfile,
working_directory, working_directory,
}) })
}, }
SearchConfig::WithJustfileAndWorkingDirectory { SearchConfig::WithJustfileAndWorkingDirectory {
justfile, justfile,
working_directory, working_directory,
} => Ok(Self { } => Ok(Self {
justfile: Self::clean(invocation_directory, justfile), justfile: Self::clean(invocation_directory, justfile),
working_directory: Self::clean(invocation_directory, working_directory), working_directory: Self::clean(invocation_directory, working_directory),
}), }),
} }
@ -206,7 +206,7 @@ mod tests {
fn not_found() { fn not_found() {
let tmp = testing::tempdir(); let tmp = testing::tempdir();
match Search::justfile(tmp.path()) { match Search::justfile(tmp.path()) {
Err(SearchError::NotFound) => {}, Err(SearchError::NotFound) => {}
_ => panic!("No justfile found error was expected"), _ => panic!("No justfile found error was expected"),
} }
} }
@ -226,7 +226,7 @@ mod tests {
fs::write(&path, "default:\n\techo ok").unwrap(); fs::write(&path, "default:\n\techo ok").unwrap();
path.pop(); path.pop();
match Search::justfile(path.as_path()) { match Search::justfile(path.as_path()) {
Err(SearchError::MultipleCandidates { .. }) => {}, Err(SearchError::MultipleCandidates { .. }) => {}
_ => panic!("Multiple candidates error was expected"), _ => panic!("Multiple candidates error was expected"),
} }
} }
@ -301,7 +301,7 @@ mod tests {
path.pop(); path.pop();
path.push(DEFAULT_JUSTFILE_NAME); path.push(DEFAULT_JUSTFILE_NAME);
assert_eq!(found_path, path); assert_eq!(found_path, path);
}, }
Err(err) => panic!("No errors were expected: {}", err), Err(err) => panic!("No errors were expected: {}", err),
} }
} }

View File

@ -14,7 +14,7 @@ pub(crate) enum SearchConfig {
WithJustfile { justfile: PathBuf }, WithJustfile { justfile: PathBuf },
/// Use user-specified justfile and working directory. /// Use user-specified justfile and working directory.
WithJustfileAndWorkingDirectory { WithJustfileAndWorkingDirectory {
justfile: PathBuf, justfile: PathBuf,
working_directory: PathBuf, working_directory: PathBuf,
}, },
} }

View File

@ -10,7 +10,7 @@ pub(crate) enum SearchError {
))] ))]
Io { Io {
directory: PathBuf, directory: PathBuf,
io_error: io::Error, io_error: io::Error,
}, },
#[snafu(display("Justfile path had no parent: {}", path.display()))] #[snafu(display("Justfile path had no parent: {}", path.display()))]
JustfileHadNoParent { path: PathBuf }, JustfileHadNoParent { path: PathBuf },

View File

@ -2,7 +2,7 @@ use crate::common::*;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct Set<'src> { pub(crate) struct Set<'src> {
pub(crate) name: Name<'src>, pub(crate) name: Name<'src>,
pub(crate) value: Setting<'src>, pub(crate) value: Setting<'src>,
} }

View File

@ -10,7 +10,7 @@ pub(crate) enum Setting<'src> {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub(crate) struct Shell<'src> { pub(crate) struct Shell<'src> {
pub(crate) command: StringLiteral<'src>, pub(crate) command: StringLiteral<'src>,
pub(crate) arguments: Vec<StringLiteral<'src>>, pub(crate) arguments: Vec<StringLiteral<'src>>,
} }

View File

@ -2,19 +2,19 @@ use crate::common::*;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub(crate) struct Settings<'src> { pub(crate) struct Settings<'src> {
pub(crate) dotenv_load: Option<bool>, pub(crate) dotenv_load: Option<bool>,
pub(crate) export: bool, pub(crate) export: bool,
pub(crate) positional_arguments: bool, pub(crate) positional_arguments: bool,
pub(crate) shell: Option<setting::Shell<'src>>, pub(crate) shell: Option<setting::Shell<'src>>,
} }
impl<'src> Settings<'src> { impl<'src> Settings<'src> {
pub(crate) fn new() -> Settings<'src> { pub(crate) fn new() -> Settings<'src> {
Settings { Settings {
dotenv_load: None, dotenv_load: None,
export: false, export: false,
positional_arguments: false, positional_arguments: false,
shell: None, shell: None,
} }
} }

View File

@ -1,7 +1,7 @@
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub(crate) struct Shebang<'line> { pub(crate) struct Shebang<'line> {
pub(crate) interpreter: &'line str, pub(crate) interpreter: &'line str,
pub(crate) argument: Option<&'line str>, pub(crate) argument: Option<&'line str>,
} }
impl<'line> Shebang<'line> { impl<'line> Shebang<'line> {

View File

@ -3,7 +3,7 @@ use crate::common::*;
#[derive(Debug, PartialEq, Clone, Copy, Ord, PartialOrd, Eq)] #[derive(Debug, PartialEq, Clone, Copy, Ord, PartialOrd, Eq)]
pub(crate) struct StringKind { pub(crate) struct StringKind {
delimiter: StringDelimiter, delimiter: StringDelimiter,
indented: bool, indented: bool,
} }
#[derive(Debug, PartialEq, Clone, Copy, Ord, PartialOrd, Eq)] #[derive(Debug, PartialEq, Clone, Copy, Ord, PartialOrd, Eq)]
@ -57,8 +57,9 @@ impl StringKind {
pub(crate) fn unterminated_error_kind(self) -> CompileErrorKind<'static> { pub(crate) fn unterminated_error_kind(self) -> CompileErrorKind<'static> {
match self.delimiter { match self.delimiter {
StringDelimiter::QuoteDouble | StringDelimiter::QuoteSingle => StringDelimiter::QuoteDouble | StringDelimiter::QuoteSingle => {
CompileErrorKind::UnterminatedString, CompileErrorKind::UnterminatedString
}
StringDelimiter::Backtick => CompileErrorKind::UnterminatedBacktick, StringDelimiter::Backtick => CompileErrorKind::UnterminatedBacktick,
} }
} }

View File

@ -2,8 +2,8 @@ use crate::common::*;
#[derive(PartialEq, Debug, Clone)] #[derive(PartialEq, Debug, Clone)]
pub(crate) struct StringLiteral<'src> { pub(crate) struct StringLiteral<'src> {
pub(crate) kind: StringKind, pub(crate) kind: StringKind,
pub(crate) raw: &'src str, pub(crate) raw: &'src str,
pub(crate) cooked: String, pub(crate) cooked: String,
} }

View File

@ -7,11 +7,11 @@ pub(crate) enum Subcommand {
Changelog, Changelog,
Choose { Choose {
overrides: BTreeMap<String, String>, overrides: BTreeMap<String, String>,
chooser: Option<String>, chooser: Option<String>,
}, },
Command { Command {
arguments: Vec<OsString>, arguments: Vec<OsString>,
binary: OsString, binary: OsString,
overrides: BTreeMap<String, String>, overrides: BTreeMap<String, String>,
}, },
Completions { Completions {
@ -21,7 +21,7 @@ pub(crate) enum Subcommand {
Edit, Edit,
Evaluate { Evaluate {
overrides: BTreeMap<String, String>, overrides: BTreeMap<String, String>,
variable: Option<String>, variable: Option<String>,
}, },
Format, Format,
Init, Init,
@ -45,10 +45,10 @@ impl Subcommand {
Changelog => { Changelog => {
Self::changelog(); Self::changelog();
return Ok(()); return Ok(());
}, }
Completions { shell } => return Self::completions(&shell), Completions { shell } => return Self::completions(&shell),
Init => return Self::init(config), Init => return Self::init(config),
_ => {}, _ => {}
} }
let search = Search::find(&config.search_config, &config.invocation_directory)?; let search = Search::find(&config.search_config, &config.invocation_directory)?;
@ -70,8 +70,9 @@ impl Subcommand {
} }
match self { match self {
Choose { overrides, chooser } => Choose { overrides, chooser } => {
Self::choose(config, justfile, &search, overrides, chooser.as_deref())?, Self::choose(config, justfile, &search, overrides, chooser.as_deref())?;
}
Command { overrides, .. } => justfile.run(config, &search, overrides, &[])?, Command { overrides, .. } => justfile.run(config, &search, overrides, &[])?,
Dump => Self::dump(ast), Dump => Self::dump(ast),
Evaluate { overrides, .. } => justfile.run(config, &search, overrides, &[])?, Evaluate { overrides, .. } => justfile.run(config, &search, overrides, &[])?,
@ -135,7 +136,7 @@ impl Subcommand {
chooser, chooser,
io_error, io_error,
}); });
}, }
}; };
for recipe in recipes { for recipe in recipes {
@ -153,7 +154,7 @@ impl Subcommand {
Ok(output) => output, Ok(output) => output,
Err(io_error) => { Err(io_error) => {
return Err(Error::ChooserRead { io_error, chooser }); return Err(Error::ChooserRead { io_error, chooser });
}, }
}; };
if !output.status.success() { if !output.status.success() {
@ -200,23 +201,26 @@ impl Subcommand {
let mut script = String::from_utf8(buffer).expect("Clap completion not UTF-8"); let mut script = String::from_utf8(buffer).expect("Clap completion not UTF-8");
match shell { match shell {
Shell::Bash => Shell::Bash => {
for (needle, replacement) in completions::BASH_COMPLETION_REPLACEMENTS { for (needle, replacement) in completions::BASH_COMPLETION_REPLACEMENTS {
replace(&mut script, needle, replacement)?; replace(&mut script, needle, replacement)?;
}, }
}
Shell::Fish => { Shell::Fish => {
script.insert_str(0, completions::FISH_RECIPE_COMPLETIONS); script.insert_str(0, completions::FISH_RECIPE_COMPLETIONS);
}, }
Shell::PowerShell => Shell::PowerShell => {
for (needle, replacement) in completions::POWERSHELL_COMPLETION_REPLACEMENTS { for (needle, replacement) in completions::POWERSHELL_COMPLETION_REPLACEMENTS {
replace(&mut script, needle, replacement)?; replace(&mut script, needle, replacement)?;
}, }
}
Shell::Zsh => Shell::Zsh => {
for (needle, replacement) in completions::ZSH_COMPLETION_REPLACEMENTS { for (needle, replacement) in completions::ZSH_COMPLETION_REPLACEMENTS {
replace(&mut script, needle, replacement)?; replace(&mut script, needle, replacement)?;
}, }
Shell::Elvish => {}, }
Shell::Elvish => {}
} }
println!("{}", script.trim()); println!("{}", script.trim());
@ -363,7 +367,7 @@ impl Subcommand {
_ => { _ => {
let alias_doc = format!("alias for `{}`", recipe.name); let alias_doc = format!("alias for `{}`", recipe.name);
print_doc(&alias_doc); print_doc(&alias_doc);
}, }
} }
println!(); println!();
} }
@ -381,7 +385,7 @@ impl Subcommand {
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: justfile.suggest_recipe(name),
}) })
} }

View File

@ -2,7 +2,7 @@ use crate::common::*;
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct Suggestion<'src> { pub(crate) struct Suggestion<'src> {
pub(crate) name: &'src str, pub(crate) name: &'src str,
pub(crate) target: Option<&'src str>, pub(crate) target: Option<&'src str>,
} }

View File

@ -36,7 +36,7 @@ pub fn summary(path: &Path) -> Result<Result<Summary, String>, io::Error> {
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Debug, Clone)] #[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Debug, Clone)]
pub struct Summary { pub struct Summary {
pub assignments: BTreeMap<String, Assignment>, pub assignments: BTreeMap<String, Assignment>,
pub recipes: BTreeMap<String, Recipe>, pub recipes: BTreeMap<String, Recipe>,
} }
impl Summary { impl Summary {
@ -51,7 +51,7 @@ impl Summary {
} }
Summary { Summary {
recipes: justfile recipes: justfile
.recipes .recipes
.into_iter() .into_iter()
.map(|(name, recipe)| { .map(|(name, recipe)| {
@ -72,13 +72,13 @@ impl Summary {
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Debug, Clone)] #[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Debug, Clone)]
pub struct Recipe { pub struct Recipe {
pub aliases: Vec<String>, pub aliases: Vec<String>,
pub dependencies: Vec<Dependency>, pub dependencies: Vec<Dependency>,
pub lines: Vec<Line>, pub lines: Vec<Line>,
pub private: bool, pub private: bool,
pub quiet: bool, pub quiet: bool,
pub shebang: bool, pub shebang: bool,
pub parameters: Vec<Parameter>, pub parameters: Vec<Parameter>,
} }
impl Recipe { impl Recipe {
@ -101,16 +101,16 @@ impl Recipe {
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Debug, Clone)] #[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Debug, Clone)]
pub struct Parameter { pub struct Parameter {
pub kind: ParameterKind, pub kind: ParameterKind,
pub name: String, pub name: String,
pub default: Option<Expression>, pub default: Option<Expression>,
} }
impl Parameter { impl Parameter {
fn new(parameter: &full::Parameter) -> Parameter { fn new(parameter: &full::Parameter) -> Parameter {
Parameter { Parameter {
kind: ParameterKind::new(parameter.kind), kind: ParameterKind::new(parameter.kind),
name: parameter.name.lexeme().to_owned(), name: parameter.name.lexeme().to_owned(),
default: parameter.default.as_ref().map(Expression::new), default: parameter.default.as_ref().map(Expression::new),
} }
} }
@ -167,14 +167,14 @@ impl Fragment {
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Debug, Clone)] #[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Debug, Clone)]
pub struct Assignment { pub struct Assignment {
pub exported: bool, pub exported: bool,
pub expression: Expression, pub expression: Expression,
} }
impl Assignment { impl Assignment {
fn new(assignment: &full::Assignment) -> Assignment { fn new(assignment: &full::Assignment) -> Assignment {
Assignment { Assignment {
exported: assignment.export, exported: assignment.export,
expression: Expression::new(&assignment.value), expression: Expression::new(&assignment.value),
} }
} }
@ -186,7 +186,7 @@ pub enum Expression {
command: String, command: String,
}, },
Call { Call {
name: String, name: String,
arguments: Vec<Expression>, arguments: Vec<Expression>,
}, },
Concatination { Concatination {
@ -194,11 +194,11 @@ pub enum Expression {
rhs: Box<Expression>, rhs: Box<Expression>,
}, },
Conditional { Conditional {
lhs: Box<Expression>, lhs: Box<Expression>,
rhs: Box<Expression>, rhs: Box<Expression>,
then: Box<Expression>, then: Box<Expression>,
otherwise: Box<Expression>, otherwise: Box<Expression>,
inverted: bool, inverted: bool,
}, },
String { String {
text: String, text: String,
@ -217,17 +217,17 @@ impl Expression {
}, },
Call { thunk } => match thunk { Call { thunk } => match thunk {
full::Thunk::Nullary { name, .. } => Expression::Call { full::Thunk::Nullary { name, .. } => Expression::Call {
name: name.lexeme().to_owned(), name: name.lexeme().to_owned(),
arguments: Vec::new(), arguments: Vec::new(),
}, },
full::Thunk::Unary { name, arg, .. } => Expression::Call { full::Thunk::Unary { name, arg, .. } => Expression::Call {
name: name.lexeme().to_owned(), name: name.lexeme().to_owned(),
arguments: vec![Expression::new(arg)], arguments: vec![Expression::new(arg)],
}, },
full::Thunk::Binary { full::Thunk::Binary {
name, args: [a, b], .. name, args: [a, b], ..
} => Expression::Call { } => Expression::Call {
name: name.lexeme().to_owned(), name: name.lexeme().to_owned(),
arguments: vec![Expression::new(a), Expression::new(b)], arguments: vec![Expression::new(a), Expression::new(b)],
}, },
full::Thunk::Ternary { full::Thunk::Ternary {
@ -235,7 +235,7 @@ impl Expression {
args: [a, b, c], args: [a, b, c],
.. ..
} => Expression::Call { } => Expression::Call {
name: name.lexeme().to_owned(), name: name.lexeme().to_owned(),
arguments: vec![Expression::new(a), Expression::new(b), Expression::new(c)], arguments: vec![Expression::new(a), Expression::new(b), Expression::new(c)],
}, },
}, },
@ -250,11 +250,11 @@ impl Expression {
then, then,
otherwise, otherwise,
} => Expression::Conditional { } => Expression::Conditional {
lhs: Box::new(Expression::new(lhs)), lhs: Box::new(Expression::new(lhs)),
rhs: Box::new(Expression::new(rhs)), rhs: Box::new(Expression::new(rhs)),
then: Box::new(Expression::new(then)), then: Box::new(Expression::new(then)),
otherwise: Box::new(Expression::new(otherwise)), otherwise: Box::new(Expression::new(otherwise)),
inverted: *inverted, inverted: *inverted,
}, },
StringLiteral { string_literal } => Expression::String { StringLiteral { string_literal } => Expression::String {
text: string_literal.cooked.clone(), text: string_literal.cooked.clone(),
@ -269,14 +269,14 @@ impl Expression {
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Debug, Clone)] #[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Debug, Clone)]
pub struct Dependency { pub struct Dependency {
pub recipe: String, pub recipe: String,
pub arguments: Vec<Expression>, pub arguments: Vec<Expression>,
} }
impl Dependency { impl Dependency {
fn new(dependency: &full::Dependency) -> Dependency { fn new(dependency: &full::Dependency) -> Dependency {
Dependency { Dependency {
recipe: dependency.recipe.name().to_owned(), recipe: dependency.recipe.name().to_owned(),
arguments: dependency.arguments.iter().map(Expression::new).collect(), arguments: dependency.arguments.iter().map(Expression::new).collect(),
} }
} }

View File

@ -82,7 +82,7 @@ pub(crate) fn analysis_error(
kind, kind,
}; };
assert_eq!(have, want); assert_eq!(have, want);
}, }
} }
} }

View File

@ -4,27 +4,27 @@ use crate::common::*;
#[derivative(Debug, Clone, PartialEq = "feature_allow_slow_enum")] #[derivative(Debug, Clone, PartialEq = "feature_allow_slow_enum")]
pub(crate) enum Thunk<'src> { pub(crate) enum Thunk<'src> {
Nullary { Nullary {
name: Name<'src>, name: Name<'src>,
#[derivative(Debug = "ignore", PartialEq = "ignore")] #[derivative(Debug = "ignore", PartialEq = "ignore")]
function: fn(&FunctionContext) -> Result<String, String>, function: fn(&FunctionContext) -> Result<String, String>,
}, },
Unary { Unary {
name: Name<'src>, name: Name<'src>,
#[derivative(Debug = "ignore", PartialEq = "ignore")] #[derivative(Debug = "ignore", PartialEq = "ignore")]
function: fn(&FunctionContext, &str) -> Result<String, String>, function: fn(&FunctionContext, &str) -> Result<String, String>,
arg: Box<Expression<'src>>, arg: Box<Expression<'src>>,
}, },
Binary { Binary {
name: Name<'src>, name: Name<'src>,
#[derivative(Debug = "ignore", PartialEq = "ignore")] #[derivative(Debug = "ignore", PartialEq = "ignore")]
function: fn(&FunctionContext, &str, &str) -> Result<String, String>, function: fn(&FunctionContext, &str, &str) -> Result<String, String>,
args: [Box<Expression<'src>>; 2], args: [Box<Expression<'src>>; 2],
}, },
Ternary { Ternary {
name: Name<'src>, name: Name<'src>,
#[derivative(Debug = "ignore", PartialEq = "ignore")] #[derivative(Debug = "ignore", PartialEq = "ignore")]
function: fn(&FunctionContext, &str, &str, &str) -> Result<String, String>, function: fn(&FunctionContext, &str, &str, &str) -> Result<String, String>,
args: [Box<Expression<'src>>; 3], args: [Box<Expression<'src>>; 3],
}, },
} }
@ -52,7 +52,7 @@ impl<'src> Thunk<'src> {
args: [a, b], args: [a, b],
name, name,
}) })
}, }
(Function::Ternary(function), 3) => { (Function::Ternary(function), 3) => {
let c = Box::new(arguments.pop().unwrap()); let c = Box::new(arguments.pop().unwrap());
let b = Box::new(arguments.pop().unwrap()); let b = Box::new(arguments.pop().unwrap());
@ -62,10 +62,10 @@ impl<'src> Thunk<'src> {
args: [a, b, c], args: [a, b, c],
name, name,
}) })
}, }
_ => Err(name.error(CompileErrorKind::FunctionArgumentCountMismatch { _ => Err(name.error(CompileErrorKind::FunctionArgumentCountMismatch {
function: name.lexeme(), function: name.lexeme(),
found: arguments.len(), found: arguments.len(),
expected: function.argc(), expected: function.argc(),
})), })),
} }

View File

@ -4,10 +4,10 @@ use crate::common::*;
pub(crate) struct Token<'src> { pub(crate) struct Token<'src> {
pub(crate) offset: usize, pub(crate) offset: usize,
pub(crate) length: usize, pub(crate) length: usize,
pub(crate) line: usize, pub(crate) line: usize,
pub(crate) column: usize, pub(crate) column: usize,
pub(crate) src: &'src str, pub(crate) src: &'src str,
pub(crate) kind: TokenKind, pub(crate) kind: TokenKind,
} }
impl<'src> Token<'src> { impl<'src> Token<'src> {
@ -65,15 +65,16 @@ impl<'src> ColorDisplay for Token<'src> {
space_width.max(1), space_width.max(1),
color.suffix() color.suffix()
)?; )?;
}, }
None => None => {
if self.offset != self.src.len() { if self.offset != self.src.len() {
write!( write!(
f, f,
"internal error: Error has invalid line number: {}", "internal error: Error has invalid line number: {}",
line_number line_number
)?; )?;
}, }
}
} }
Ok(()) Ok(())

View File

@ -37,37 +37,41 @@ pub(crate) enum TokenKind {
impl Display for TokenKind { impl Display for TokenKind {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
use TokenKind::*; use TokenKind::*;
write!(f, "{}", match *self { write!(
AmpersandAmpersand => "'&&'", f,
Asterisk => "'*'", "{}",
At => "'@'", match *self {
Backtick => "backtick", AmpersandAmpersand => "'&&'",
BangEquals => "'!='", Asterisk => "'*'",
BraceL => "'{'", At => "'@'",
BraceR => "'}'", Backtick => "backtick",
BracketL => "'['", BangEquals => "'!='",
BracketR => "']'", BraceL => "'{'",
Colon => "':'", BraceR => "'}'",
ColonEquals => "':='", BracketL => "'['",
Comma => "','", BracketR => "']'",
Comment => "comment", Colon => "':'",
Dedent => "dedent", ColonEquals => "':='",
Dollar => "'$'", Comma => "','",
Eof => "end of file", Comment => "comment",
Eol => "end of line", Dedent => "dedent",
Equals => "'='", Dollar => "'$'",
EqualsEquals => "'=='", Eof => "end of file",
Identifier => "identifier", Eol => "end of line",
Indent => "indent", Equals => "'='",
InterpolationEnd => "'}}'", EqualsEquals => "'=='",
InterpolationStart => "'{{'", Identifier => "identifier",
ParenL => "'('", Indent => "indent",
ParenR => "')'", InterpolationEnd => "'}}'",
Plus => "'+'", InterpolationStart => "'{{'",
StringToken => "string", ParenL => "'('",
Text => "command text", ParenR => "')'",
Unspecified => "unspecified", Plus => "'+'",
Whitespace => "whitespace", StringToken => "string",
}) Text => "command text",
Unspecified => "unspecified",
Whitespace => "whitespace",
}
)
} }
} }

View File

@ -92,7 +92,7 @@ impl<'text> Tree<'text> {
Tree::List(mut children) => { Tree::List(mut children) => {
children.push(tree.into()); children.push(tree.into());
Tree::List(children) Tree::List(children)
}, }
Tree::Atom(text) => Tree::List(vec![Tree::Atom(text), tree.into()]), Tree::Atom(text) => Tree::List(vec![Tree::Atom(text), tree.into()]),
} }
} }
@ -137,7 +137,7 @@ impl Display for Tree<'_> {
} }
write!(f, ")") write!(f, ")")
}, }
Tree::Atom(text) => write!(f, "{}", text), Tree::Atom(text) => write!(f, "{}", text),
} }
} }

View File

@ -2,7 +2,7 @@ use crate::common::*;
#[derive(PartialEq, Debug, Clone)] #[derive(PartialEq, Debug, Clone)]
pub(crate) struct UnresolvedDependency<'src> { pub(crate) struct UnresolvedDependency<'src> {
pub(crate) recipe: Name<'src>, pub(crate) recipe: Name<'src>,
pub(crate) arguments: Vec<Expression<'src>>, pub(crate) arguments: Vec<Expression<'src>>,
} }

View File

@ -26,9 +26,9 @@ impl<'src> UnresolvedRecipe<'src> {
.recipe .recipe
.error(CompileErrorKind::DependencyArgumentCountMismatch { .error(CompileErrorKind::DependencyArgumentCountMismatch {
dependency: unresolved.recipe.lexeme(), dependency: unresolved.recipe.lexeme(),
found: unresolved.arguments.len(), found: unresolved.arguments.len(),
min: resolved.min_arguments(), min: resolved.min_arguments(),
max: resolved.max_arguments(), max: resolved.max_arguments(),
}), }),
); );
} }
@ -39,7 +39,7 @@ impl<'src> UnresolvedRecipe<'src> {
.into_iter() .into_iter()
.zip(resolved) .zip(resolved)
.map(|(unresolved, resolved)| Dependency { .map(|(unresolved, resolved)| Dependency {
recipe: resolved, recipe: resolved,
arguments: unresolved.arguments, arguments: unresolved.arguments,
}) })
.collect(); .collect();

View File

@ -31,17 +31,17 @@ impl<'expression, 'src> Iterator for Variables<'expression, 'src> {
self.stack.push(then); self.stack.push(then);
self.stack.push(otherwise); self.stack.push(otherwise);
self.next() self.next()
}, }
Some(Expression::Variable { name, .. }) => Some(name.token()), Some(Expression::Variable { name, .. }) => Some(name.token()),
Some(Expression::Concatination { lhs, rhs }) => { Some(Expression::Concatination { lhs, rhs }) => {
self.stack.push(lhs); self.stack.push(lhs);
self.stack.push(rhs); self.stack.push(rhs);
self.next() self.next()
}, }
Some(Expression::Group { contents }) => { Some(Expression::Group { contents }) => {
self.stack.push(contents); self.stack.push(contents);
self.next() self.next()
}, }
} }
} }
} }

View File

@ -41,7 +41,7 @@ adding the following line to your shell rc file:
export JUST_SUPPRESS_DOTENV_LOAD_WARNING=1 export JUST_SUPPRESS_DOTENV_LOAD_WARNING=1
See https://github.com/casey/just/issues/469 for more details.")?; See https://github.com/casey/just/issues/469 for more details.")?;
}, }
} }
write!(f, "{}", message.suffix())?; write!(f, "{}", message.suffix())?;