Add --dump-format json (#992)

This commit is contained in:
Casey Rodarmor 2021-11-17 00:07:48 -08:00 committed by GitHub
parent 53d3c7569c
commit 0ae91884e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1106 additions and 138 deletions

87
Cargo.lock generated
View File

@ -1,7 +1,5 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "0.7.18"
@ -71,9 +69,9 @@ checksum = "52d74260d9bf6944e2208aa46841b4b8f0d7ffc0849a06837b2f510337f86b2b"
[[package]]
name = "cc"
version = "1.0.71"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"
[[package]]
name = "cfg-if"
@ -118,9 +116,9 @@ dependencies = [
[[package]]
name = "ctrlc"
version = "3.2.1"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19c6cedffdc8c03a3346d723eb20bd85a13362bb96dc2ac000842c6381ec7bf"
checksum = "377c9b002a72a0b2c1a18c62e2f3864bdfea4a015e3683a96e24aa45dd6c02d1"
dependencies = [
"nix",
"winapi",
@ -221,6 +219,12 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "itoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "just"
version = "0.10.3"
@ -242,6 +246,8 @@ dependencies = [
"log",
"pretty_assertions",
"regex",
"serde",
"serde_json",
"similar",
"snafu",
"strum",
@ -269,9 +275,9 @@ checksum = "441225017b106b9f902e97947a6d31e44ebcf274b91bdbfb51e5c477fcd468e5"
[[package]]
name = "libc"
version = "0.2.106"
version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673"
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
[[package]]
name = "linked-hash-map"
@ -305,9 +311,9 @@ dependencies = [
[[package]]
name = "nix"
version = "0.23.0"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188"
checksum = "cf1e25ee6b412c2a1e3fcb6a4499a5c1bfe7f43e014bdce9a6b6666e5aa2d187"
dependencies = [
"bitflags",
"cc",
@ -327,9 +333,9 @@ dependencies = [
[[package]]
name = "ppv-lite86"
version = "0.2.15"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "pretty_assertions"
@ -369,18 +375,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.32"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.10"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
@ -481,6 +487,43 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088"
[[package]]
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "serde"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "similar"
version = "2.1.0"
@ -520,9 +563,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "structopt"
version = "0.3.25"
version = "0.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c"
checksum = "bf9d950ef167e25e0bdb073cf1d68e9ad2795ac826f2f3f59647817cf23c0bfa"
dependencies = [
"clap",
"lazy_static",
@ -531,9 +574,9 @@ dependencies = [
[[package]]
name = "structopt-derive"
version = "0.4.18"
version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
checksum = "134d838a2c9943ac3125cf6df165eda53493451b719f3255b2a26b85f772d0ba"
dependencies = [
"heck",
"proc-macro-error",
@ -565,9 +608,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.81"
version = "1.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
checksum = "5239bc68e0fef57495900cfea4e8dc75596d9a319d7e16b1e0a440d24e6fe0a0"
dependencies = [
"proc-macro2",
"quote",

View File

@ -19,6 +19,8 @@ members = [".", "bin/ref-type"]
ansi_term = "0.12.0"
atty = "0.2.0"
camino = "1.0.4"
clap = { version = "2.33.0", features = ["wrap_help"] }
ctrlc = { version = "3.1.1", features = ["termination"] }
derivative = "2.0.0"
dotenv = "0.15.0"
edit-distance = "2.0.0"
@ -28,29 +30,17 @@ lexiclean = "0.0.1"
libc = "0.2.0"
log = "0.4.4"
regex = "1.5.4"
serde = { version = "1.0.130", features = ["derive", "rc"] }
serde_json = "1.0.68"
similar = { version = "2.1.0", features = ["unicode"] }
snafu = "0.6.0"
strum = { version = "0.22.0", features = ["derive"] }
strum_macros = "0.22.0"
target = "2.0.0"
tempfile = "3.0.0"
typed-arena = "2.0.1"
unicode-width = "0.1.0"
[dependencies.clap]
version = "2.33.0"
features = ["wrap_help"]
[dependencies.ctrlc]
version = "3.1.1"
features = ["termination"]
[dependencies.similar]
version = "2.1.0"
features = ["unicode"]
[dependencies.strum]
version = "0.22.0"
features = ["derive"]
[dev-dependencies]
cradle = "0.2.0"
executable-path = "1.0.0"

View File

@ -1730,6 +1730,10 @@ default:
echo foo
```
=== Dumping Justfiles as JSON
The `--dump` command can be used with `--dump-format json` to print a JSON representation of a justfile. The JSON format is currently unstable, so the `--unstable` flag is required.
=== Changelog
A changelog for the latest release is available in link:CHANGELOG.md[]. Changelogs for previous releases are available on https://github.com/casey/just/releases[the releases page]. `just --changelog` can also be used to make a `just` binary print its changelog.

View File

@ -20,7 +20,7 @@ _just() {
case "${cmd}" in
just)
opts=" -q -u -v -e -l -h -V -f -d -c -s --check --dry-run --highlight --no-dotenv --no-highlight --quiet --shell-command --clear-shell-args --unsorted --unstable --verbose --changelog --choose --dump --edit --evaluate --fmt --init --list --summary --variables --help --version --chooser --color --list-heading --list-prefix --justfile --set --shell --shell-arg --working-directory --command --completions --show --dotenv-filename --dotenv-path <ARGUMENTS>... "
opts=" -q -u -v -e -l -h -V -f -d -c -s --check --dry-run --highlight --no-dotenv --no-highlight --quiet --shell-command --clear-shell-args --unsorted --unstable --verbose --changelog --choose --dump --edit --evaluate --fmt --init --list --summary --variables --help --version --chooser --color --dump-format --list-heading --list-prefix --justfile --set --shell --shell-arg --working-directory --command --completions --show --dotenv-filename --dotenv-path <ARGUMENTS>... "
if [[ ${cur} == -* ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@ -41,6 +41,10 @@ _just() {
COMPREPLY=($(compgen -W "auto always never" -- "${cur}"))
return 0
;;
--dump-format)
COMPREPLY=($(compgen -W "just json" -- "${cur}"))
return 0
;;
--list-heading)
COMPREPLY=($(compgen -f "${cur}"))
return 0

View File

@ -16,6 +16,7 @@ edit:completion:arg-completer[just] = [@words]{
&'just'= {
cand --chooser 'Override binary invoked by `--choose`'
cand --color 'Print colorful output'
cand --dump-format 'Dump justfile as <FORMAT>'
cand --list-heading 'Print <TEXT> before list'
cand --list-prefix 'Print <TEXT> before each list item'
cand -f 'Use <JUSTFILE> as justfile'
@ -48,7 +49,7 @@ edit:completion:arg-completer[just] = [@words]{
cand --verbose 'Use verbose output'
cand --changelog 'Print changelog'
cand --choose 'Select one or more recipes to run using a binary. If `--chooser` is not passed the chooser defaults to the value of $JUST_CHOOSER, falling back to `fzf`'
cand --dump 'Print entire justfile'
cand --dump 'Print justfile'
cand -e 'Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`'
cand --edit 'Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`'
cand --evaluate 'Evaluate and print all variables. If a variable name is given as an argument, only print that variable''s value.'

View File

@ -11,6 +11,7 @@ complete -c just -a '(__fish_just_complete_recipes)'
# autogenerated completions
complete -c just -n "__fish_use_subcommand" -l chooser -d 'Override binary invoked by `--choose`'
complete -c just -n "__fish_use_subcommand" -l color -d 'Print colorful output' -r -f -a "auto always never"
complete -c just -n "__fish_use_subcommand" -l dump-format -d 'Dump justfile as <FORMAT>' -r -f -a "just json"
complete -c just -n "__fish_use_subcommand" -l list-heading -d 'Print <TEXT> before list'
complete -c just -n "__fish_use_subcommand" -l list-prefix -d 'Print <TEXT> before each list item'
complete -c just -n "__fish_use_subcommand" -s f -l justfile -d 'Use <JUSTFILE> as justfile'
@ -36,7 +37,7 @@ complete -c just -n "__fish_use_subcommand" -l unstable -d 'Enable unstable feat
complete -c just -n "__fish_use_subcommand" -s v -l verbose -d 'Use verbose output'
complete -c just -n "__fish_use_subcommand" -l changelog -d 'Print changelog'
complete -c just -n "__fish_use_subcommand" -l choose -d 'Select one or more recipes to run using a binary. If `--chooser` is not passed the chooser defaults to the value of $JUST_CHOOSER, falling back to `fzf`'
complete -c just -n "__fish_use_subcommand" -l dump -d 'Print entire justfile'
complete -c just -n "__fish_use_subcommand" -l dump -d 'Print justfile'
complete -c just -n "__fish_use_subcommand" -s e -l edit -d 'Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`'
complete -c just -n "__fish_use_subcommand" -l evaluate -d 'Evaluate and print all variables. If a variable name is given as an argument, only print that variable\'s value.'
complete -c just -n "__fish_use_subcommand" -l fmt -d 'Format and overwrite justfile'

View File

@ -21,6 +21,7 @@ Register-ArgumentCompleter -Native -CommandName 'just' -ScriptBlock {
'just' {
[CompletionResult]::new('--chooser', 'chooser', [CompletionResultType]::ParameterName, 'Override binary invoked by `--choose`')
[CompletionResult]::new('--color', 'color', [CompletionResultType]::ParameterName, 'Print colorful output')
[CompletionResult]::new('--dump-format', 'dump-format', [CompletionResultType]::ParameterName, 'Dump justfile as <FORMAT>')
[CompletionResult]::new('--list-heading', 'list-heading', [CompletionResultType]::ParameterName, 'Print <TEXT> before list')
[CompletionResult]::new('--list-prefix', 'list-prefix', [CompletionResultType]::ParameterName, 'Print <TEXT> before each list item')
[CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Use <JUSTFILE> as justfile')
@ -53,7 +54,7 @@ Register-ArgumentCompleter -Native -CommandName 'just' -ScriptBlock {
[CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'Use verbose output')
[CompletionResult]::new('--changelog', 'changelog', [CompletionResultType]::ParameterName, 'Print changelog')
[CompletionResult]::new('--choose', 'choose', [CompletionResultType]::ParameterName, 'Select one or more recipes to run using a binary. If `--chooser` is not passed the chooser defaults to the value of $JUST_CHOOSER, falling back to `fzf`')
[CompletionResult]::new('--dump', 'dump', [CompletionResultType]::ParameterName, 'Print entire justfile')
[CompletionResult]::new('--dump', 'dump', [CompletionResultType]::ParameterName, 'Print justfile')
[CompletionResult]::new('-e', 'e', [CompletionResultType]::ParameterName, 'Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`')
[CompletionResult]::new('--edit', 'edit', [CompletionResultType]::ParameterName, 'Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`')
[CompletionResult]::new('--evaluate', 'evaluate', [CompletionResultType]::ParameterName, 'Evaluate and print all variables. If a variable name is given as an argument, only print that variable''s value.')

View File

@ -17,6 +17,7 @@ _just() {
local common=(
'--chooser=[Override binary invoked by `--choose`]' \
'--color=[Print colorful output]: :(auto always never)' \
'--dump-format=[Dump justfile as <FORMAT>]: :(just json)' \
'--list-heading=[Print <TEXT> before list]' \
'--list-prefix=[Print <TEXT> before each list item]' \
'-f+[Use <JUSTFILE> as justfile]' \
@ -49,7 +50,7 @@ _just() {
'*--verbose[Use verbose output]' \
'--changelog[Print changelog]' \
'--choose[Select one or more recipes to run using a binary. If `--chooser` is not passed the chooser defaults to the value of $JUST_CHOOSER, falling back to `fzf`]' \
'--dump[Print entire justfile]' \
'--dump[Print justfile]' \
'-e[Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`]' \
'--edit[Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`]' \
'--evaluate[Evaluate and print all variables. If a variable name is given as an argument, only print that variable'\''s value.]' \

View File

@ -1,9 +1,13 @@
use crate::common::*;
/// An alias, e.g. `name := target`
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, PartialEq, Clone, Serialize)]
pub(crate) struct Alias<'src, T = Rc<Recipe<'src>>> {
pub(crate) name: Name<'src>,
#[serde(
bound(serialize = "T: Keyed<'src>"),
serialize_with = "keyed::serialize"
)]
pub(crate) target: T,
}

View File

@ -81,6 +81,16 @@ impl<'src> Analyzer<'src> {
Ok(Justfile {
warnings: ast.warnings,
first: recipes
.values()
.fold(None, |accumulator, next| match accumulator {
None => Some(Rc::clone(next)),
Some(previous) => Some(if previous.line_number() < next.line_number() {
previous
} else {
Rc::clone(next)
}),
}),
aliases,
assignments,
recipes,

View File

@ -1,7 +1,7 @@
use crate::common::*;
/// A binding of `name` to `value`
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Serialize)]
pub(crate) struct Binding<'src, V = String> {
/// Export binding as an environment variable to child processes
pub(crate) export: bool,

View File

@ -27,6 +27,10 @@ pub(crate) use ::{
libc::EXIT_FAILURE,
log::{info, warn},
regex::Regex,
serde::{
ser::{SerializeMap, SerializeSeq},
Serialize, Serializer,
},
snafu::{ResultExt, Snafu},
strum::{Display, EnumString, IntoStaticStr},
typed_arena::Arena,
@ -34,7 +38,7 @@ pub(crate) use ::{
};
// modules
pub(crate) use crate::{completions, config, config_error, setting};
pub(crate) use crate::{completions, config, config_error, keyed, setting};
// functions
pub(crate) use crate::{load_dotenv::load_dotenv, output::output, unindent::unindent};
@ -51,18 +55,18 @@ pub(crate) use crate::{
assignment_resolver::AssignmentResolver, ast::Ast, binding::Binding, color::Color,
compile_error::CompileError, compile_error_kind::CompileErrorKind,
conditional_operator::ConditionalOperator, config::Config, config_error::ConfigError,
count::Count, delimiter::Delimiter, dependency::Dependency, enclosure::Enclosure, error::Error,
evaluator::Evaluator, expression::Expression, fragment::Fragment, function::Function,
function_context::FunctionContext, interrupt_guard::InterruptGuard,
interrupt_handler::InterruptHandler, item::Item, justfile::Justfile, keyword::Keyword,
lexer::Lexer, line::Line, list::List, loader::Loader, name::Name, output_error::OutputError,
parameter::Parameter, parameter_kind::ParameterKind, parser::Parser, platform::Platform,
position::Position, positional::Positional, recipe::Recipe, recipe_context::RecipeContext,
recipe_resolver::RecipeResolver, scope::Scope, search::Search, search_config::SearchConfig,
search_error::SearchError, set::Set, setting::Setting, settings::Settings, shebang::Shebang,
show_whitespace::ShowWhitespace, string_kind::StringKind, string_literal::StringLiteral,
subcommand::Subcommand, suggestion::Suggestion, table::Table, thunk::Thunk, token::Token,
token_kind::TokenKind, unresolved_dependency::UnresolvedDependency,
count::Count, delimiter::Delimiter, dependency::Dependency, dump_format::DumpFormat,
enclosure::Enclosure, error::Error, evaluator::Evaluator, expression::Expression,
fragment::Fragment, function::Function, function_context::FunctionContext,
interrupt_guard::InterruptGuard, interrupt_handler::InterruptHandler, item::Item,
justfile::Justfile, keyword::Keyword, lexer::Lexer, line::Line, list::List, loader::Loader,
name::Name, output_error::OutputError, parameter::Parameter, parameter_kind::ParameterKind,
parser::Parser, platform::Platform, position::Position, positional::Positional, recipe::Recipe,
recipe_context::RecipeContext, recipe_resolver::RecipeResolver, scope::Scope, search::Search,
search_config::SearchConfig, search_error::SearchError, set::Set, setting::Setting,
settings::Settings, shebang::Shebang, show_whitespace::ShowWhitespace, string_kind::StringKind,
string_literal::StringLiteral, subcommand::Subcommand, suggestion::Suggestion, table::Table,
thunk::Thunk, token::Token, token_kind::TokenKind, unresolved_dependency::UnresolvedDependency,
unresolved_recipe::UnresolvedRecipe, use_color::UseColor, variables::Variables,
verbosity::Verbosity, warning::Warning,
};

View File

@ -14,9 +14,12 @@ pub(crate) const DEFAULT_SHELL_ARG: &str = "-cu";
#[derive(Debug, PartialEq)]
pub(crate) struct Config {
pub(crate) color: Color,
pub(crate) check: bool,
pub(crate) color: Color,
pub(crate) dotenv_filename: Option<String>,
pub(crate) dotenv_path: Option<PathBuf>,
pub(crate) dry_run: bool,
pub(crate) dump_format: DumpFormat,
pub(crate) highlight: bool,
pub(crate) invocation_directory: PathBuf,
pub(crate) list_heading: String,
@ -30,8 +33,6 @@ pub(crate) struct Config {
pub(crate) subcommand: Subcommand,
pub(crate) unsorted: bool,
pub(crate) unstable: bool,
pub(crate) dotenv_filename: Option<String>,
pub(crate) dotenv_path: Option<PathBuf>,
pub(crate) verbosity: Verbosity,
}
@ -86,7 +87,10 @@ mod arg {
pub(crate) const CHOOSER: &str = "CHOOSER";
pub(crate) const CLEAR_SHELL_ARGS: &str = "CLEAR-SHELL-ARGS";
pub(crate) const COLOR: &str = "COLOR";
pub(crate) const DOTENV_FILENAME: &str = "DOTENV-FILENAME";
pub(crate) const DOTENV_PATH: &str = "DOTENV-PATH";
pub(crate) const DRY_RUN: &str = "DRY-RUN";
pub(crate) const DUMP_FORMAT: &str = "DUMP-FORMAT";
pub(crate) const HIGHLIGHT: &str = "HIGHLIGHT";
pub(crate) const JUSTFILE: &str = "JUSTFILE";
pub(crate) const LIST_HEADING: &str = "LIST-HEADING";
@ -100,8 +104,6 @@ mod arg {
pub(crate) const SHELL_COMMAND: &str = "SHELL-COMMAND";
pub(crate) const UNSORTED: &str = "UNSORTED";
pub(crate) const UNSTABLE: &str = "UNSTABLE";
pub(crate) const DOTENV_FILENAME: &str = "DOTENV_FILENAME";
pub(crate) const DOTENV_PATH: &str = "DOTENV_PATH";
pub(crate) const VERBOSE: &str = "VERBOSE";
pub(crate) const WORKING_DIRECTORY: &str = "WORKING-DIRECTORY";
@ -109,6 +111,10 @@ mod arg {
pub(crate) const COLOR_AUTO: &str = "auto";
pub(crate) const COLOR_NEVER: &str = "never";
pub(crate) const COLOR_VALUES: &[&str] = &[COLOR_AUTO, COLOR_ALWAYS, COLOR_NEVER];
pub(crate) const DUMP_FORMAT_JSON: &str = "json";
pub(crate) const DUMP_FORMAT_JUST: &str = "just";
pub(crate) const DUMP_FORMAT_VALUES: &[&str] = &[DUMP_FORMAT_JUST, DUMP_FORMAT_JSON];
}
impl Config {
@ -144,6 +150,15 @@ impl Config {
.help("Print what just would do without doing it")
.conflicts_with(arg::QUIET),
)
.arg(
Arg::with_name(arg::DUMP_FORMAT)
.long("dump-format")
.takes_value(true)
.possible_values(arg::DUMP_FORMAT_VALUES)
.default_value(arg::DUMP_FORMAT_JUST)
.value_name("FORMAT")
.help("Dump justfile as <FORMAT>"),
)
.arg(
Arg::with_name(arg::HIGHLIGHT)
.long("highlight")
@ -283,7 +298,7 @@ impl Config {
.arg(
Arg::with_name(cmd::DUMP)
.long("dump")
.help("Print entire justfile"),
.help("Print justfile"),
)
.arg(
Arg::with_name(cmd::EDIT)
@ -367,7 +382,13 @@ impl Config {
}
}
fn color_from_value(value: &str) -> ConfigResult<Color> {
fn color_from_matches(matches: &ArgMatches) -> ConfigResult<Color> {
let value = matches
.value_of(arg::COLOR)
.ok_or_else(|| ConfigError::Internal {
message: "`--color` had no value".to_string(),
})?;
match value {
arg::COLOR_AUTO => Ok(Color::auto()),
arg::COLOR_ALWAYS => Ok(Color::always()),
@ -378,6 +399,22 @@ impl Config {
}
}
fn dump_format_from_matches(matches: &ArgMatches) -> ConfigResult<DumpFormat> {
let value = matches
.value_of(arg::DUMP_FORMAT)
.ok_or_else(|| ConfigError::Internal {
message: "`--dump-format` had no value".to_string(),
})?;
match value {
arg::DUMP_FORMAT_JSON => Ok(DumpFormat::Json),
arg::DUMP_FORMAT_JUST => Ok(DumpFormat::Just),
_ => Err(ConfigError::Internal {
message: format!("Invalid argument `{}` to --dump-format.", value),
}),
}
}
pub(crate) fn from_matches(matches: &ArgMatches) -> ConfigResult<Self> {
let invocation_directory = env::current_dir().context(config_error::CurrentDir)?;
@ -387,11 +424,7 @@ impl Config {
Verbosity::from_flag_occurrences(matches.occurrences_of(arg::VERBOSE))
};
let color = Self::color_from_value(
matches
.value_of(arg::COLOR)
.expect("`--color` had no value"),
)?;
let color = Self::color_from_matches(matches)?;
let set_count = matches.occurrences_of(arg::SET);
let mut overrides = BTreeMap::new();
@ -542,6 +575,7 @@ impl Config {
Ok(Self {
check: matches.is_present(arg::CHECK),
dry_run: matches.is_present(arg::DRY_RUN),
dump_format: Self::dump_format_from_matches(matches)?,
highlight: !matches.is_present(arg::NO_HIGHLIGHT),
shell: matches.value_of(arg::SHELL).unwrap().to_owned(),
load_dotenv: !matches.is_present(arg::NO_DOTENV),
@ -612,7 +646,7 @@ FLAGS:
`fzf`
--clear-shell-args Clear shell arguments
--dry-run Print what just would do without doing it
--dump Print entire justfile
--dump Print justfile
-e, --edit Edit justfile with editor given by $VISUAL or
$EDITOR, falling back to `vim`
--evaluate Evaluate and print all variables. If a variable
@ -646,12 +680,15 @@ OPTIONS:
--completions <SHELL>
Print shell completion script for <SHELL> [possible values: zsh,
bash, fish, powershell, elvish]
--dotenv-filename <DOTENV_FILENAME>
--dotenv-filename <DOTENV-FILENAME>
Search for environment file named <DOTENV-FILENAME> instead of
`.env`
--dotenv-path <DOTENV_PATH>
--dotenv-path <DOTENV-PATH>
Load environment file at <DOTENV-PATH> instead of searching for one
--dump-format <FORMAT>
Dump justfile as <FORMAT> [default: just] [possible values: just,
json]
-f, --justfile <JUSTFILE> Use <JUSTFILE> as justfile
--list-heading <TEXT> Print <TEXT> before list
--list-prefix <TEXT>
@ -693,6 +730,7 @@ ARGS:
args: [$($arg:expr),*],
$(color: $color:expr,)?
$(dry_run: $dry_run:expr,)?
$(dump_format: $dump_format:expr,)?
$(highlight: $highlight:expr,)?
$(search_config: $search_config:expr,)?
$(shell: $shell:expr,)?
@ -712,6 +750,7 @@ ARGS:
let want = Config {
$(color: $color,)?
$(dry_run: $dry_run,)?
$(dump_format: $dump_format,)?
$(highlight: $highlight,)?
$(search_config: $search_config,)?
$(shell: $shell.to_owned(),)?
@ -1101,6 +1140,12 @@ ARGS:
subcommand: Subcommand::Dump,
}
test! {
name: dump_format,
args: ["--dump-format", "json"],
dump_format: DumpFormat::Json,
}
test! {
name: subcommand_edit,
args: ["--edit"],

View File

@ -1,9 +1,10 @@
use crate::common::*;
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Debug, Serialize)]
pub(crate) struct Dependency<'src> {
pub(crate) recipe: Rc<Recipe<'src>>,
pub(crate) arguments: Vec<Expression<'src>>,
#[serde(serialize_with = "keyed::serialize")]
pub(crate) recipe: Rc<Recipe<'src>>,
}
impl<'src> Display for Dependency<'src> {

5
src/dump_format.rs Normal file
View File

@ -0,0 +1,5 @@
#[derive(Debug, PartialEq)]
pub(crate) enum DumpFormat {
Json,
Just,
}

View File

@ -63,6 +63,9 @@ pub(crate) enum Error<'src> {
Dotenv {
dotenv_error: dotenv::Error,
},
DumpJson {
serde_json_error: serde_json::Error,
},
EditorInvoke {
editor: OsString,
io_error: io::Error,
@ -434,6 +437,9 @@ impl<'src> ColorDisplay for Error<'src> {
Dotenv { dotenv_error } => {
write!(f, "Failed to load environment file: {}", dotenv_error)?;
}
DumpJson { serde_json_error } => {
write!(f, "Failed to dump JSON to stdout: {}", serde_json_error)?;
}
EditorInvoke { editor, io_error } => {
write!(
f,

View File

@ -65,3 +65,51 @@ impl<'src> Display for Expression<'src> {
}
}
}
impl<'src> Serialize for Expression<'src> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Self::Backtick { contents, .. } => {
let mut seq = serializer.serialize_seq(None)?;
seq.serialize_element("evaluate")?;
seq.serialize_element(contents)?;
seq.end()
}
Self::Call { thunk } => thunk.serialize(serializer),
Self::Concatination { lhs, rhs } => {
let mut seq = serializer.serialize_seq(None)?;
seq.serialize_element("concatinate")?;
seq.serialize_element(lhs)?;
seq.serialize_element(rhs)?;
seq.end()
}
Self::Conditional {
lhs,
rhs,
then,
otherwise,
operator,
} => {
let mut seq = serializer.serialize_seq(None)?;
seq.serialize_element("if")?;
seq.serialize_element(&operator.to_string())?;
seq.serialize_element(lhs)?;
seq.serialize_element(rhs)?;
seq.serialize_element(then)?;
seq.serialize_element(otherwise)?;
seq.end()
}
Self::Group { contents } => contents.serialize(serializer),
Self::StringLiteral { string_literal } => string_literal.serialize(serializer),
Self::Variable { name } => {
let mut seq = serializer.serialize_seq(None)?;
seq.serialize_element("variable")?;
seq.serialize_element(name)?;
seq.end()
}
}
}
}

View File

@ -8,3 +8,19 @@ pub(crate) enum Fragment<'src> {
/// …an interpolation containing `expression`.
Interpolation { expression: Expression<'src> },
}
impl<'src> Serialize for Fragment<'src> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Self::Text { token } => serializer.serialize_str(token.lexeme()),
Self::Interpolation { expression } => {
let mut seq = serializer.serialize_seq(None)?;
seq.serialize_element(expression)?;
seq.end()
}
}
}
}

View File

@ -1,29 +1,19 @@
use crate::common::*;
#[derive(Debug, PartialEq)]
use serde::Serialize;
#[derive(Debug, PartialEq, Serialize)]
pub(crate) struct Justfile<'src> {
pub(crate) recipes: Table<'src, Rc<Recipe<'src>>>,
pub(crate) assignments: Table<'src, Assignment<'src>>,
pub(crate) aliases: Table<'src, Alias<'src>>,
pub(crate) assignments: Table<'src, Assignment<'src>>,
#[serde(serialize_with = "keyed::serialize_option")]
pub(crate) first: Option<Rc<Recipe<'src>>>,
pub(crate) recipes: Table<'src, Rc<Recipe<'src>>>,
pub(crate) settings: Settings<'src>,
pub(crate) warnings: Vec<Warning>,
}
impl<'src> Justfile<'src> {
pub(crate) fn first(&self) -> Option<&Recipe<'src>> {
let mut first: Option<&Recipe<Dependency>> = None;
for recipe in self.recipes.values() {
if let Some(first_recipe) = first {
if recipe.line_number() < first_recipe.line_number() {
first = Some(recipe);
}
} else {
first = Some(recipe);
}
}
first
}
pub(crate) fn count(&self) -> usize {
self.recipes.len()
}
@ -206,7 +196,7 @@ impl<'src> Justfile<'src> {
let argvec: Vec<&str> = if !arguments.is_empty() {
arguments.iter().map(String::as_str).collect()
} else if let Some(recipe) = self.first() {
} else if let Some(recipe) = &self.first {
let min_arguments = recipe.min_arguments();
if min_arguments > 0 {
return Err(Error::DefaultRecipeRequiresArguments {

View File

@ -9,3 +9,25 @@ impl<'key, T: Keyed<'key>> Keyed<'key> for Rc<T> {
self.as_ref().key()
}
}
pub(crate) fn serialize<'src, S, K>(keyed: &K, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
K: Keyed<'src>,
{
serializer.serialize_str(&keyed.key())
}
pub(crate) fn serialize_option<'src, S, K>(
recipe: &Option<K>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
K: Keyed<'src>,
{
match recipe {
None => serializer.serialize_none(),
Some(keyed) => serialize(keyed, serializer),
}
}

View File

@ -12,6 +12,12 @@
clippy::wildcard_imports
)]
pub use crate::run::run;
// Used in integration tests.
#[doc(hidden)]
pub use unindent::unindent;
#[macro_use]
extern crate lazy_static;
@ -29,6 +35,12 @@ pub mod node;
#[cfg(fuzzing)]
pub(crate) mod fuzzing;
// Used by Janus, https://github.com/casey/janus, a tool
// that analyses all public justfiles on GitHub to avoid
// breaking changes.
#[doc(hidden)]
pub mod summary;
mod alias;
mod analyzer;
mod assignment;
@ -49,6 +61,7 @@ mod config_error;
mod count;
mod delimiter;
mod dependency;
mod dump_format;
mod enclosure;
mod error;
mod evaluator;
@ -107,15 +120,3 @@ mod use_color;
mod variables;
mod verbosity;
mod warning;
pub use crate::run::run;
// Used in integration tests.
#[doc(hidden)]
pub use unindent::unindent;
// Used by Janus, https://github.com/casey/janus, a tool
// that analyses all public justfiles on GitHub to avoid
// breaking changes.
#[doc(hidden)]
pub mod summary;

View File

@ -1,7 +1,8 @@
use crate::common::*;
/// A single line in a recipe body, consisting of any number of `Fragment`s.
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Serialize)]
#[serde(transparent)]
pub(crate) struct Line<'src> {
pub(crate) fragments: Vec<Fragment<'src>>,
}

View File

@ -50,3 +50,12 @@ impl Display for Name<'_> {
write!(f, "{}", self.lexeme())
}
}
impl<'src> Serialize for Name<'src> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.lexeme())
}
}

View File

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

View File

@ -1,7 +1,8 @@
use crate::common::*;
/// Parameters can either be…
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum ParameterKind {
/// …singular, accepting a single argument
Singular,

View File

@ -19,17 +19,17 @@ fn error_from_signal(recipe: &str, line_number: Option<usize>, exit_status: Exit
}
/// A recipe, e.g. `foo: bar baz`
#[derive(PartialEq, Debug, Clone)]
#[derive(PartialEq, Debug, Clone, Serialize)]
pub(crate) struct Recipe<'src, D = Dependency<'src>> {
pub(crate) body: Vec<Line<'src>>,
pub(crate) dependencies: Vec<D>,
pub(crate) doc: Option<&'src str>,
pub(crate) name: Name<'src>,
pub(crate) parameters: Vec<Parameter<'src>>,
pub(crate) priors: usize,
pub(crate) private: bool,
pub(crate) quiet: bool,
pub(crate) shebang: bool,
pub(crate) priors: usize,
}
impl<'src, D> Recipe<'src, D> {
@ -302,12 +302,6 @@ impl<'src, D> Recipe<'src, D> {
}
}
impl<'src, D> Keyed<'src> for Recipe<'src, D> {
fn key(&self) -> &'src str {
self.name.lexeme()
}
}
impl<'src, D: Display> ColorDisplay for Recipe<'src, D> {
fn fmt(&self, f: &mut Formatter, color: Color) -> Result<(), fmt::Error> {
if let Some(doc) = self.doc {
@ -353,3 +347,9 @@ impl<'src, D: Display> ColorDisplay for Recipe<'src, D> {
Ok(())
}
}
impl<'src, D> Keyed<'src> for Recipe<'src, D> {
fn key(&self) -> &'src str {
self.name.lexeme()
}
}

View File

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

View File

@ -1,6 +1,6 @@
use crate::common::*;
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Serialize)]
pub(crate) struct Settings<'src> {
pub(crate) dotenv_load: Option<bool>,
pub(crate) export: bool,

View File

@ -18,3 +18,12 @@ impl Display for StringLiteral<'_> {
)
}
}
impl<'src> Serialize for StringLiteral<'src> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.cooked)
}
}

View File

@ -76,7 +76,7 @@ impl Subcommand {
Command { overrides, .. } | Evaluate { overrides, .. } => {
justfile.run(config, &search, overrides, &[])?
}
Dump => Self::dump(ast),
Dump => Self::dump(config, ast, justfile)?,
Format => Self::format(config, &search, &src, ast)?,
List => Self::list(config, justfile),
Run {
@ -229,8 +229,17 @@ impl Subcommand {
Ok(())
}
fn dump(ast: Ast) {
print!("{}", ast);
fn dump(config: &Config, ast: Ast, justfile: Justfile) -> Result<(), Error<'static>> {
match config.dump_format {
DumpFormat::Json => {
config.require_unstable("The JSON dump format is currently unstable.")?;
serde_json::to_writer(io::stdout(), &justfile)
.map_err(|serde_json_error| Error::DumpJson { serde_json_error })?;
println!();
}
DumpFormat::Just => print!("{}", ast),
}
Ok(())
}
fn edit(search: &Search) -> Result<(), Error<'static>> {

View File

@ -2,7 +2,8 @@ use crate::common::*;
use std::collections::btree_map;
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Serialize)]
#[serde(transparent)]
pub(crate) struct Table<'key, V: Keyed<'key>> {
map: BTreeMap<&'key str, V>,
}

View File

@ -35,6 +35,16 @@ pub(crate) enum Thunk<'src> {
}
impl<'src> Thunk<'src> {
fn name(&self) -> &Name<'src> {
match self {
Self::Nullary { name, .. }
| Self::Unary { name, .. }
| Self::Binary { name, .. }
| Self::BinaryPlus { name, .. }
| Self::Ternary { name, .. } => name,
}
}
pub(crate) fn resolve(
name: Name<'src>,
mut arguments: Vec<Expression<'src>>,
@ -120,3 +130,34 @@ impl Display for Thunk<'_> {
}
}
}
impl<'src> Serialize for Thunk<'src> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(None)?;
seq.serialize_element("call")?;
seq.serialize_element(self.name())?;
match self {
Self::Nullary { .. } => {}
Self::Unary { arg, .. } => seq.serialize_element(&arg)?,
Self::Binary { args, .. } => {
for arg in args {
seq.serialize_element(arg)?;
}
}
Self::BinaryPlus { args, .. } => {
for arg in args.0.iter().map(Box::as_ref).chain(&args.1) {
seq.serialize_element(arg)?;
}
}
Self::Ternary { args, .. } => {
for arg in args {
seq.serialize_element(arg)?;
}
}
}
seq.end()
}
}

View File

@ -54,3 +54,16 @@ See https://github.com/casey/just/issues/469 for more details.")?;
Ok(())
}
}
impl Serialize for Warning {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("message", &self.color_display(Color::never()).to_string())?;
map.end()
}
}

View File

@ -31,7 +31,7 @@ test! {
error: The argument '--command <COMMAND>' requires a value but none was supplied
USAGE:
just{} --color <COLOR> --shell <SHELL> --shell-arg <SHELL-ARG>... \
just{} --color <COLOR> --dump-format <FORMAT> --shell <SHELL> --shell-arg <SHELL-ARG>... \
<--changelog|--choose|--command <COMMAND>|--completions <SHELL>|--dump|--edit|\
--evaluate|--fmt|--init|--list|--show <RECIPE>|--summary|--variables>

View File

@ -11,16 +11,19 @@ pub(crate) use std::{
str,
};
pub(crate) use cradle::input::Input;
pub(crate) use executable_path::executable_path;
pub(crate) use just::unindent;
pub(crate) use libc::{EXIT_FAILURE, EXIT_SUCCESS};
pub(crate) use pretty_assertions::Comparison;
pub(crate) use regex::Regex;
pub(crate) use tempfile::TempDir;
pub(crate) use temptree::{temptree, tree, Tree};
pub(crate) use which::which;
pub(crate) use yaml_rust::YamlLoader;
pub(crate) use ::{
cradle::input::Input,
executable_path::executable_path,
just::unindent,
libc::{EXIT_FAILURE, EXIT_SUCCESS},
pretty_assertions::Comparison,
regex::Regex,
serde_json::{json, Value},
tempfile::TempDir,
temptree::{temptree, tree, Tree},
which::which,
yaml_rust::YamlLoader,
};
pub(crate) use crate::{
assert_stdout::assert_stdout, assert_success::assert_success, tempdir::tempdir, test::Test,

683
tests/json.rs Normal file
View File

@ -0,0 +1,683 @@
use crate::common::*;
fn test(justfile: &str, value: Value) {
Test::new()
.justfile(justfile)
.args(&["--dump", "--dump-format", "json", "--unstable"])
.stdout(format!("{}\n", serde_json::to_string(&value).unwrap()))
.run();
}
#[test]
fn alias() {
test(
"
alias f := foo
foo:
",
json!({
"first": "foo",
"aliases": {
"f": {
"name": "f",
"target": "foo",
}
},
"assignments": {},
"recipes": {
"foo": {
"body": [],
"dependencies": [],
"doc": null,
"name": "foo",
"parameters": [],
"priors": 0,
"private": false,
"quiet": false,
"shebang": false,
}
},
"settings": {
"dotenv_load": null,
"export": false,
"positional_arguments": false,
"shell": null,
},
"warnings": [],
}),
);
}
#[test]
fn assignment() {
test(
"foo := 'bar'",
json!({
"aliases": {},
"assignments": {
"foo": {
"export": false,
"name": "foo",
"value": "bar",
}
},
"first": null,
"recipes": {},
"settings": {
"dotenv_load": null,
"export": false,
"positional_arguments": false,
"shell": null,
},
"warnings": [],
}),
);
}
#[test]
fn body() {
test(
"
foo:
bar
abc{{ 'xyz' }}def
",
json!({
"aliases": {},
"assignments": {},
"first": "foo",
"recipes": {
"foo": {
"body": [
["bar"],
["abc", ["xyz"], "def"],
],
"dependencies": [],
"doc": null,
"name": "foo",
"parameters": [],
"priors": 0,
"private": false,
"quiet": false,
"shebang": false,
}
},
"settings": {
"dotenv_load": null,
"export": false,
"positional_arguments": false,
"shell": null,
},
"warnings": [],
}),
);
}
#[test]
fn dependencies() {
test(
"
foo:
bar: foo
",
json!({
"aliases": {},
"assignments": {},
"first": "foo",
"recipes": {
"bar": {
"doc": null,
"name": "bar",
"body": [],
"dependencies": [{
"arguments": [],
"recipe": "foo"
}],
"parameters": [],
"priors": 1,
"private": false,
"quiet": false,
"shebang": false,
},
"foo": {
"body": [],
"dependencies": [],
"doc": null,
"name": "foo",
"parameters": [],
"priors": 0,
"private": false,
"quiet": false,
"shebang": false,
}
},
"settings": {
"dotenv_load": null,
"export": false,
"positional_arguments": false,
"shell": null,
},
"warnings": [],
}),
);
}
#[test]
fn dependency_argument() {
test(
"
x := 'foo'
foo *args:
bar: (
foo
'baz'
('baz')
('a' + 'b')
`echo`
x
if 'a' == 'b' { 'c' } else { 'd' }
arch()
env_var('foo')
join('a', 'b')
replace('a', 'b', 'c')
)
",
json!({
"aliases": {},
"first": "foo",
"assignments": {
"x": {
"export": false,
"name": "x",
"value": "foo",
},
},
"recipes": {
"bar": {
"doc": null,
"name": "bar",
"body": [],
"dependencies": [{
"arguments": [
"baz",
"baz",
["concatinate", "a", "b"],
["evaluate", "echo"],
["variable", "x"],
["if", "==", "a", "b", "c", "d"],
["call", "arch"],
["call", "env_var", "foo"],
["call", "join", "a", "b"],
["call", "replace", "a", "b", "c"],
],
"recipe": "foo"
}],
"parameters": [],
"priors": 1,
"private": false,
"quiet": false,
"shebang": false,
},
"foo": {
"body": [],
"dependencies": [],
"doc": null,
"name": "foo",
"parameters": [
{
"name": "args",
"export": false,
"default": null,
"kind": "star",
}
],
"priors": 0,
"private": false,
"quiet": false,
"shebang": false,
}
},
"settings": {
"dotenv_load": null,
"export": false,
"positional_arguments": false,
"shell": null,
},
"warnings": [],
}),
);
}
#[test]
fn doc_comment() {
test(
"# hello\nfoo:",
json!({
"aliases": {},
"first": "foo",
"assignments": {},
"recipes": {
"foo": {
"body": [],
"dependencies": [],
"doc": "hello",
"name": "foo",
"parameters": [],
"priors": 0,
"private": false,
"quiet": false,
"shebang": false,
}
},
"settings": {
"dotenv_load": null,
"export": false,
"positional_arguments": false,
"shell": null,
},
"warnings": [],
}),
);
}
#[test]
fn empty_justfile() {
test(
"",
json!({
"aliases": {},
"assignments": {},
"first": null,
"recipes": {},
"settings": {
"dotenv_load": null,
"export": false,
"positional_arguments": false,
"shell": null,
},
"warnings": [],
}),
);
}
#[test]
fn parameters() {
test(
"
a:
b x:
c x='y':
d +x:
e *x:
f $x:
",
json!({
"aliases": {},
"first": "a",
"assignments": {},
"recipes": {
"a": {
"body": [],
"dependencies": [],
"doc": null,
"name": "a",
"parameters": [],
"priors": 0,
"private": false,
"quiet": false,
"shebang": false,
},
"b": {
"body": [],
"dependencies": [],
"doc": null,
"name": "b",
"parameters": [
{
"name": "x",
"export": false,
"default": null,
"kind": "singular",
},
],
"priors": 0,
"private": false,
"quiet": false,
"shebang": false,
},
"c": {
"body": [],
"dependencies": [],
"doc": null,
"name": "c",
"parameters": [
{
"name": "x",
"export": false,
"default": "y",
"kind": "singular",
}
],
"priors": 0,
"private": false,
"quiet": false,
"shebang": false,
},
"d": {
"body": [],
"dependencies": [],
"doc": null,
"name": "d",
"parameters": [
{
"name": "x",
"export": false,
"default": null,
"kind": "plus",
}
],
"priors": 0,
"private": false,
"quiet": false,
"shebang": false,
},
"e": {
"body": [],
"dependencies": [],
"doc": null,
"name": "e",
"parameters": [
{
"name": "x",
"export": false,
"default": null,
"kind": "star",
}
],
"priors": 0,
"private": false,
"quiet": false,
"shebang": false,
},
"f": {
"body": [],
"dependencies": [],
"doc": null,
"name": "f",
"parameters": [
{
"name": "x",
"export": true,
"default": null,
"kind": "singular",
}
],
"priors": 0,
"private": false,
"quiet": false,
"shebang": false,
},
},
"settings": {
"dotenv_load": null,
"export": false,
"positional_arguments": false,
"shell": null,
},
"warnings": [],
}),
);
}
#[test]
fn priors() {
test(
"
a:
b: a && c
c:
",
json!({
"aliases": {},
"assignments": {},
"first": "a",
"recipes": {
"a": {
"body": [],
"dependencies": [],
"doc": null,
"name": "a",
"parameters": [],
"priors": 0,
"private": false,
"quiet": false,
"shebang": false,
},
"b": {
"body": [],
"dependencies": [
{
"arguments": [],
"recipe": "a",
},
{
"arguments": [],
"recipe": "c",
}
],
"doc": null,
"name": "b",
"private": false,
"quiet": false,
"shebang": false,
"parameters": [],
"priors": 1,
},
"c": {
"body": [],
"dependencies": [],
"doc": null,
"name": "c",
"parameters": [],
"private": false,
"quiet": false,
"shebang": false,
"parameters": [],
"priors": 0,
},
},
"settings": {
"dotenv_load": null,
"export": false,
"positional_arguments": false,
"shell": null,
},
"warnings": [],
}),
);
}
#[test]
fn private() {
test(
"_foo:",
json!({
"aliases": {},
"assignments": {},
"first": "_foo",
"recipes": {
"_foo": {
"body": [],
"dependencies": [],
"doc": null,
"name": "_foo",
"parameters": [],
"priors": 0,
"private": true,
"quiet": false,
"shebang": false,
}
},
"settings": {
"dotenv_load": null,
"export": false,
"positional_arguments": false,
"shell": null,
},
"warnings": [],
}),
);
}
#[test]
fn quiet() {
test(
"@foo:",
json!({
"aliases": {},
"assignments": {},
"first": "foo",
"recipes": {
"foo": {
"body": [],
"dependencies": [],
"doc": null,
"name": "foo",
"parameters": [],
"priors": 0,
"private": false,
"quiet": true,
"shebang": false,
}
},
"settings": {
"dotenv_load": null,
"export": false,
"positional_arguments": false,
"shell": null,
},
"warnings": [],
}),
);
}
#[test]
fn requires_unstable() {
Test::new()
.justfile("foo:")
.args(&["--dump", "--dump-format", "json"])
.stderr("error: The JSON dump format is currently unstable. Invoke `just` with the `--unstable` flag to enable unstable features.\n")
.status(EXIT_FAILURE)
.run();
}
#[test]
fn settings() {
test(
"
set dotenv-load
set export
set positional-arguments
set shell := ['a', 'b', 'c']
foo:
#!bar
",
json!({
"aliases": {},
"assignments": {},
"first": "foo",
"recipes": {
"foo": {
"body": [["#!bar"]],
"dependencies": [],
"doc": null,
"name": "foo",
"parameters": [],
"priors": 0,
"private": false,
"quiet": false,
"shebang": true,
}
},
"settings": {
"dotenv_load": true,
"export": true,
"positional_arguments": true,
"shell": {
"arguments": ["b", "c"],
"command": "a",
},
},
"warnings": [],
}),
);
}
#[test]
fn shebang() {
test(
"
foo:
#!bar
",
json!({
"aliases": {},
"assignments": {},
"first": "foo",
"recipes": {
"foo": {
"body": [["#!bar"]],
"dependencies": [],
"doc": null,
"name": "foo",
"parameters": [],
"priors": 0,
"private": false,
"quiet": false,
"shebang": true,
}
},
"settings": {
"dotenv_load": null,
"export": false,
"positional_arguments": false,
"shell": null,
},
"warnings": [],
}),
);
}
#[test]
fn simple() {
test(
"foo:",
json!({
"aliases": {},
"assignments": {},
"first": "foo",
"recipes": {
"foo": {
"body": [],
"dependencies": [],
"doc": null,
"name": "foo",
"parameters": [],
"priors": 0,
"private": false,
"quiet": false,
"shebang": false,
}
},
"settings": {
"dotenv_load": null,
"export": false,
"positional_arguments": false,
"shell": null,
},
"warnings": [],
}),
);
}

View File

@ -23,6 +23,7 @@ mod functions;
mod init;
mod interrupts;
mod invocation_directory;
mod json;
mod misc;
mod positional_arguments;
mod quiet;