2022-06-18 21:56:31 -07:00
use super ::* ;
Gargantuan refactor (#522)
- Instead of changing the current directory with `env::set_current_dir`
to be implicitly inherited by subprocesses, we now use
`Command::current_dir` to set it explicitly. This feels much better,
since we aren't dependent on the implicit state of the process's
current directory.
- Subcommand execution is much improved.
- Added a ton of tests for config parsing, config execution, working
dir, and search dir.
- Error messages are improved. Many more will be colored.
- The Config is now onwed, instead of borrowing from the arguments and
the `clap::ArgMatches` object. This is a huge ergonomic improvement,
especially in tests, and I don't think anyone will notice.
- `--edit` now uses `$VISUAL`, `$EDITOR`, or `vim`, in that order,
matching git, which I think is what most people will expect.
- Added a cute `tmptree!{}` macro, for creating temporary directories
populated with directories and files for tests.
- Admitted that grammer is LL(k) and I don't know what `k` is.
2019-11-09 21:43:20 -08:00
2021-07-26 01:26:06 -07:00
#[ derive(Debug) ]
pub ( crate ) enum Error < ' src > {
ArgumentCountMismatch {
2021-09-16 06:44:40 -07:00
recipe : & ' src str ,
2021-07-26 01:26:06 -07:00
parameters : Vec < Parameter < ' src > > ,
2021-09-16 06:44:40 -07:00
found : usize ,
min : usize ,
max : usize ,
2021-07-26 01:26:06 -07:00
} ,
Backtick {
2021-09-16 06:44:40 -07:00
token : Token < ' src > ,
2021-07-26 01:26:06 -07:00
output_error : OutputError ,
} ,
ChooserInvoke {
2021-09-16 06:44:40 -07:00
shell_binary : String ,
2021-07-26 01:26:06 -07:00
shell_arguments : String ,
2021-09-16 06:44:40 -07:00
chooser : OsString ,
io_error : io ::Error ,
2021-07-26 01:26:06 -07:00
} ,
ChooserRead {
2021-09-16 06:44:40 -07:00
chooser : OsString ,
2021-07-26 01:26:06 -07:00
io_error : io ::Error ,
} ,
ChooserStatus {
chooser : OsString ,
2021-09-16 06:44:40 -07:00
status : ExitStatus ,
2021-07-26 01:26:06 -07:00
} ,
ChooserWrite {
2021-09-16 06:44:40 -07:00
chooser : OsString ,
2021-07-26 01:26:06 -07:00
io_error : io ::Error ,
} ,
2023-01-12 19:25:28 -08:00
CircularInclude {
current : PathBuf ,
include : PathBuf ,
} ,
2021-07-26 01:26:06 -07:00
Code {
2021-09-16 06:44:40 -07:00
recipe : & ' src str ,
2021-07-26 01:26:06 -07:00
line_number : Option < usize > ,
2021-09-16 06:44:40 -07:00
code : i32 ,
2022-10-31 00:52:03 -07:00
print_message : bool ,
2021-07-26 01:26:06 -07:00
} ,
CommandInvoke {
2021-09-16 06:44:40 -07:00
binary : OsString ,
2021-07-26 01:26:06 -07:00
arguments : Vec < OsString > ,
2021-09-16 06:44:40 -07:00
io_error : io ::Error ,
2021-07-26 01:26:06 -07:00
} ,
CommandStatus {
2021-09-16 06:44:40 -07:00
binary : OsString ,
2021-07-26 01:26:06 -07:00
arguments : Vec < OsString > ,
2021-09-16 06:44:40 -07:00
status : ExitStatus ,
2021-07-26 01:26:06 -07:00
} ,
Compile {
compile_error : CompileError < ' src > ,
} ,
Config {
config_error : ConfigError ,
} ,
Cygpath {
2021-09-16 06:44:40 -07:00
recipe : & ' src str ,
2021-07-26 01:26:06 -07:00
output_error : OutputError ,
} ,
DefaultRecipeRequiresArguments {
2021-09-16 06:44:40 -07:00
recipe : & ' src str ,
2021-07-26 01:26:06 -07:00
min_arguments : usize ,
} ,
Dotenv {
2022-12-14 20:32:27 -08:00
dotenv_error : dotenvy ::Error ,
2021-07-26 01:26:06 -07:00
} ,
2021-11-17 00:07:48 -08:00
DumpJson {
serde_json_error : serde_json ::Error ,
} ,
2021-07-26 01:26:06 -07:00
EditorInvoke {
2021-09-16 06:44:40 -07:00
editor : OsString ,
2021-07-26 01:26:06 -07:00
io_error : io ::Error ,
} ,
EditorStatus {
editor : OsString ,
status : ExitStatus ,
} ,
EvalUnknownVariable {
2021-09-16 06:44:40 -07:00
variable : String ,
2021-07-26 01:26:06 -07:00
suggestion : Option < Suggestion < ' src > > ,
} ,
2021-10-31 21:27:59 -07:00
FormatCheckFoundDiff ,
2021-07-26 01:26:06 -07:00
FunctionCall {
function : Name < ' src > ,
2021-09-16 06:44:40 -07:00
message : String ,
2021-07-26 01:26:06 -07:00
} ,
2023-01-12 19:25:28 -08:00
IncludeMissingPath {
file : PathBuf ,
line : usize ,
} ,
2021-07-26 01:26:06 -07:00
InitExists {
justfile : PathBuf ,
} ,
Internal {
message : String ,
} ,
2023-01-12 19:25:28 -08:00
InvalidDirective {
line : String ,
} ,
2021-07-26 01:26:06 -07:00
Io {
2021-09-16 06:44:40 -07:00
recipe : & ' src str ,
2021-07-26 01:26:06 -07:00
io_error : io ::Error ,
} ,
Load {
2021-09-16 06:44:40 -07:00
path : PathBuf ,
2021-07-26 01:26:06 -07:00
io_error : io ::Error ,
} ,
NoChoosableRecipes ,
NoRecipes ,
2021-09-16 16:45:56 -07:00
RegexCompile {
source : regex ::Error ,
} ,
2021-07-26 01:26:06 -07:00
Search {
search_error : SearchError ,
} ,
Shebang {
2021-09-16 06:44:40 -07:00
recipe : & ' src str ,
command : String ,
2021-07-26 01:26:06 -07:00
argument : Option < String > ,
io_error : io ::Error ,
} ,
Signal {
2021-09-16 06:44:40 -07:00
recipe : & ' src str ,
2021-07-26 01:26:06 -07:00
line_number : Option < usize > ,
2021-09-16 06:44:40 -07:00
signal : i32 ,
2021-07-26 01:26:06 -07:00
} ,
TmpdirIo {
2021-09-16 06:44:40 -07:00
recipe : & ' src str ,
2021-07-26 01:26:06 -07:00
io_error : io ::Error ,
} ,
Unknown {
2021-09-16 06:44:40 -07:00
recipe : & ' src str ,
2021-07-26 01:26:06 -07:00
line_number : Option < usize > ,
} ,
UnknownOverrides {
overrides : Vec < String > ,
} ,
UnknownRecipes {
2021-09-16 06:44:40 -07:00
recipes : Vec < String > ,
2021-07-26 01:26:06 -07:00
suggestion : Option < Suggestion < ' src > > ,
} ,
Unstable {
message : String ,
} ,
WriteJustfile {
justfile : PathBuf ,
io_error : io ::Error ,
} ,
}
impl < ' src > Error < ' src > {
pub ( crate ) fn code ( & self ) -> Option < i32 > {
match self {
Self ::Code { code , .. }
| Self ::Backtick {
output_error : OutputError ::Code ( code ) ,
..
} = > Some ( * code ) ,
Self ::ChooserStatus { status , .. } | Self ::EditorStatus { status , .. } = > status . code ( ) ,
_ = > None ,
}
}
fn context ( & self ) -> Option < Token < ' src > > {
match self {
Self ::Backtick { token , .. } = > Some ( * token ) ,
Self ::Compile { compile_error } = > Some ( compile_error . context ( ) ) ,
Self ::FunctionCall { function , .. } = > Some ( function . token ( ) ) ,
_ = > None ,
}
}
pub ( crate ) fn internal ( message : impl Into < String > ) -> Self {
Self ::Internal {
message : message . into ( ) ,
}
}
2022-10-25 16:32:36 -07:00
2022-10-31 00:52:03 -07:00
pub ( crate ) fn print_message ( & self ) -> bool {
! matches! (
2022-10-25 16:32:36 -07:00
self ,
Error ::Code {
2022-10-31 00:52:03 -07:00
print_message : false ,
2022-10-25 16:32:36 -07:00
..
}
)
}
2021-07-26 01:26:06 -07:00
}
impl < ' src > From < CompileError < ' src > > for Error < ' src > {
fn from ( compile_error : CompileError < ' src > ) -> Self {
Self ::Compile { compile_error }
}
}
impl < ' src > From < ConfigError > for Error < ' src > {
fn from ( config_error : ConfigError ) -> Self {
Self ::Config { config_error }
}
}
2022-12-14 20:32:27 -08:00
impl < ' src > From < dotenvy ::Error > for Error < ' src > {
fn from ( dotenv_error : dotenvy ::Error ) -> Error < ' src > {
2021-07-26 01:26:06 -07:00
Self ::Dotenv { dotenv_error }
}
}
impl < ' src > From < SearchError > for Error < ' src > {
fn from ( search_error : SearchError ) -> Self {
Self ::Search { search_error }
}
}
2021-07-28 18:06:57 -07:00
impl < ' src > ColorDisplay for Error < ' src > {
fn fmt ( & self , f : & mut Formatter , color : Color ) -> fmt ::Result {
2021-07-26 01:26:06 -07:00
use Error ::* ;
2023-10-16 20:18:25 -07:00
let error = color . error ( ) . paint ( " error " ) ;
let message = color . message ( ) . prefix ( ) ;
write! ( f , " {error}: {message} " ) ? ;
2021-07-28 18:06:57 -07:00
2021-07-26 01:26:06 -07:00
match self {
2023-10-16 20:18:25 -07:00
ArgumentCountMismatch { recipe , found , min , max , .. } = > {
let count = Count ( " argument " , * found ) ;
2021-07-26 01:26:06 -07:00
if min = = max {
let expected = min ;
2023-10-16 20:18:25 -07:00
let only = if expected < found { " only " } else { " " } ;
write! ( f , " Recipe `{recipe}` got {found} {count} but {only}takes {expected} " ) ? ;
2021-07-26 01:26:06 -07:00
} else if found < min {
2023-10-16 20:18:25 -07:00
write! ( f , " Recipe `{recipe}` got {found} {count} but takes at least {min} " ) ? ;
2021-07-26 01:26:06 -07:00
} else if found > max {
2023-10-16 20:18:25 -07:00
write! ( f , " Recipe `{recipe}` got {found} {count} but takes at most {max} " ) ? ;
2021-09-16 06:44:40 -07:00
}
}
2021-07-26 01:26:06 -07:00
Backtick { output_error , .. } = > match output_error {
2023-10-16 20:18:25 -07:00
OutputError ::Code ( code ) = > write! ( f , " Backtick failed with exit code {code} " ) ? ,
OutputError ::Signal ( signal ) = > write! ( f , " Backtick was terminated by signal {signal} " ) ? ,
OutputError ::Unknown = > write! ( f , " Backtick failed for an unknown reason " ) ? ,
OutputError ::Io ( io_error ) = > match io_error . kind ( ) {
io ::ErrorKind ::NotFound = > write! ( f , " Backtick could not be run because just could not find the shell: \n {io_error} " ) ,
io ::ErrorKind ::PermissionDenied = > write! ( f , " Backtick could not be run because just could not run the shell: \n {io_error} " ) ,
_ = > write! ( f , " Backtick could not be run because of an IO error while launching the shell: \n {io_error} " ) ,
} ? ,
OutputError ::Utf8 ( utf8_error ) = > write! ( f , " Backtick succeeded but stdout was not utf8: {utf8_error} " ) ? ,
}
ChooserInvoke { shell_binary , shell_arguments , chooser , io_error } = > {
let chooser = chooser . to_string_lossy ( ) ;
write! ( f , " Chooser `{shell_binary} {shell_arguments} {chooser}` invocation failed: {io_error} " ) ? ;
2021-09-16 06:44:40 -07:00
}
2021-07-26 01:26:06 -07:00
ChooserRead { chooser , io_error } = > {
2023-10-16 20:18:25 -07:00
let chooser = chooser . to_string_lossy ( ) ;
write! ( f , " Failed to read output from chooser `{chooser}`: {io_error} " ) ? ;
2021-09-16 06:44:40 -07:00
}
2021-07-26 01:26:06 -07:00
ChooserStatus { chooser , status } = > {
2023-10-16 20:18:25 -07:00
let chooser = chooser . to_string_lossy ( ) ;
write! ( f , " Chooser `{chooser}` failed: {status} " ) ? ;
2021-09-16 06:44:40 -07:00
}
2021-07-26 01:26:06 -07:00
ChooserWrite { chooser , io_error } = > {
2023-10-16 20:18:25 -07:00
let chooser = chooser . to_string_lossy ( ) ;
write! ( f , " Failed to write to chooser `{chooser}`: {io_error} " ) ? ;
2021-09-16 06:44:40 -07:00
}
2023-01-12 19:25:28 -08:00
CircularInclude { current , include } = > {
2023-10-16 20:18:25 -07:00
let include = include . display ( ) ;
let current = current . display ( ) ;
write! ( f , " Include `{include}` in `{current}` is a circular include " ) ? ;
}
Code { recipe , line_number , code , .. } = > {
2021-07-26 01:26:06 -07:00
if let Some ( n ) = line_number {
2023-10-16 20:18:25 -07:00
write! ( f , " Recipe `{recipe}` failed on line {n} with exit code {code} " ) ? ;
2021-07-26 01:26:06 -07:00
} else {
2022-12-15 16:53:21 -08:00
write! ( f , " Recipe `{recipe}` failed with exit code {code} " ) ? ;
2021-09-16 06:44:40 -07:00
}
}
2023-10-16 20:18:25 -07:00
CommandInvoke { binary , arguments , io_error } = > {
let cmd = format_cmd ( binary , arguments ) ;
write! ( f , " Failed to invoke {cmd}: {io_error} " ) ? ;
}
CommandStatus { binary , arguments , status } = > {
let cmd = format_cmd ( binary , arguments ) ;
write! ( f , " Command {cmd} failed: {status} " ) ? ;
2021-09-16 06:44:40 -07:00
}
2021-07-26 01:26:06 -07:00
Compile { compile_error } = > Display ::fmt ( compile_error , f ) ? ,
Config { config_error } = > Display ::fmt ( config_error , f ) ? ,
2023-10-16 20:18:25 -07:00
Cygpath { recipe , output_error } = > match output_error {
OutputError ::Code ( code ) = > write! ( f , " Cygpath failed with exit code {code} while translating recipe `{recipe}` shebang interpreter path " ) ? ,
OutputError ::Signal ( signal ) = > write! ( f , " Cygpath terminated by signal {signal} while translating recipe `{recipe}` shebang interpreter path " ) ? ,
OutputError ::Unknown = > write! ( f , " Cygpath experienced an unknown failure while translating recipe `{recipe}` shebang interpreter path " ) ? ,
2021-07-26 01:26:06 -07:00
OutputError ::Io ( io_error ) = > {
match io_error . kind ( ) {
2023-10-16 20:18:25 -07:00
io ::ErrorKind ::NotFound = > write! ( f , " Could not find `cygpath` executable to translate recipe `{recipe}` shebang interpreter path: \n {io_error} " ) ,
io ::ErrorKind ::PermissionDenied = > write! ( f , " Could not run `cygpath` executable to translate recipe `{recipe}` shebang interpreter path: \n {io_error} " ) ,
2022-12-15 16:53:21 -08:00
_ = > write! ( f , " Could not run `cygpath` executable: \n {io_error} " ) ,
2021-07-26 01:26:06 -07:00
} ? ;
2021-09-16 06:44:40 -07:00
}
2023-10-16 20:18:25 -07:00
OutputError ::Utf8 ( utf8_error ) = > write! ( f , " Cygpath successfully translated recipe `{recipe}` shebang interpreter path, but output was not utf8: {utf8_error} " ) ? ,
}
DefaultRecipeRequiresArguments { recipe , min_arguments } = > {
let count = Count ( " argument " , * min_arguments ) ;
write! ( f , " Recipe `{recipe}` cannot be used as default recipe since it requires at least {min_arguments} {count}. " ) ? ;
2021-09-16 06:44:40 -07:00
}
2021-07-26 01:26:06 -07:00
Dotenv { dotenv_error } = > {
2022-12-15 16:53:21 -08:00
write! ( f , " Failed to load environment file: {dotenv_error} " ) ? ;
2021-09-16 06:44:40 -07:00
}
2021-11-17 00:07:48 -08:00
DumpJson { serde_json_error } = > {
2022-12-15 16:53:21 -08:00
write! ( f , " Failed to dump JSON to stdout: {serde_json_error} " ) ? ;
2021-11-17 00:07:48 -08:00
}
2021-07-26 01:26:06 -07:00
EditorInvoke { editor , io_error } = > {
2023-10-16 20:18:25 -07:00
let editor = editor . to_string_lossy ( ) ;
write! ( f , " Editor `{editor}` invocation failed: {io_error} " ) ? ;
2021-09-16 06:44:40 -07:00
}
2021-07-26 01:26:06 -07:00
EditorStatus { editor , status } = > {
2023-10-16 20:18:25 -07:00
let editor = editor . to_string_lossy ( ) ;
write! ( f , " Editor `{editor}` failed: {status} " ) ? ;
2021-09-16 06:44:40 -07:00
}
2023-10-16 20:18:25 -07:00
EvalUnknownVariable { variable , suggestion } = > {
2022-12-15 16:53:21 -08:00
write! ( f , " Justfile does not contain variable `{variable}`. " ) ? ;
2023-10-16 20:18:25 -07:00
if let Some ( suggestion ) = suggestion {
2022-12-15 16:53:21 -08:00
write! ( f , " \n {suggestion} " ) ? ;
2021-07-26 01:26:06 -07:00
}
2021-09-16 06:44:40 -07:00
}
2021-10-31 21:27:59 -07:00
FormatCheckFoundDiff = > {
write! ( f , " Formatted justfile differs from original. " ) ? ;
}
2021-07-26 01:26:06 -07:00
FunctionCall { function , message } = > {
2023-10-16 20:18:25 -07:00
let function = function . lexeme ( ) ;
write! ( f , " Call to function `{function}` failed: {message} " ) ? ;
}
IncludeMissingPath { file : justfile , line } = > {
let line = line . ordinal ( ) ;
let justfile = justfile . display ( ) ;
write! ( f , " !include directive on line {line} of `{justfile}` has no argument " ) ? ;
}
2021-07-26 01:26:06 -07:00
InitExists { justfile } = > {
write! ( f , " Justfile `{}` already exists " , justfile . display ( ) ) ? ;
2021-09-16 06:44:40 -07:00
}
2021-07-26 01:26:06 -07:00
Internal { message } = > {
2023-10-16 20:18:25 -07:00
write! ( f , " Internal runtime error, this may indicate a bug in just: {message} \
consider filing an issue : https ://github.com/casey/just/issues/new")?;
2021-09-16 06:44:40 -07:00
}
2023-01-12 19:25:28 -08:00
InvalidDirective { line } = > {
write! ( f , " Invalid directive: {line} " ) ? ;
}
2021-07-26 01:26:06 -07:00
Io { recipe , io_error } = > {
match io_error . kind ( ) {
2023-10-16 20:18:25 -07:00
io ::ErrorKind ::NotFound = > write! ( f , " Recipe `{recipe}` could not be run because just could not find the shell: {io_error} " ) ,
io ::ErrorKind ::PermissionDenied = > write! ( f , " Recipe `{recipe}` could not be run because just could not run the shell: {io_error} " ) ,
_ = > write! ( f , " Recipe `{recipe}` could not be run because of an IO error while launching the shell: {io_error} " ) ,
2021-07-26 01:26:06 -07:00
} ? ;
2021-09-16 06:44:40 -07:00
}
2021-07-26 01:26:06 -07:00
Load { io_error , path } = > {
2023-10-16 20:18:25 -07:00
let path = path . display ( ) ;
write! ( f , " Failed to read justfile at `{path}`: {io_error} " ) ? ;
2021-09-16 16:45:56 -07:00
}
2023-10-16 20:18:25 -07:00
NoChoosableRecipes = > write! ( f , " Justfile contains no choosable recipes. " ) ? ,
NoRecipes = > write! ( f , " Justfile contains no recipes. " ) ? ,
RegexCompile { source } = > write! ( f , " {source} " ) ? ,
2021-07-26 01:26:06 -07:00
Search { search_error } = > Display ::fmt ( search_error , f ) ? ,
2023-10-16 20:18:25 -07:00
Shebang { recipe , command , argument , io_error } = > {
2021-07-26 01:26:06 -07:00
if let Some ( argument ) = argument {
2023-10-16 20:18:25 -07:00
write! ( f , " Recipe `{recipe}` with shebang `#!{command} {argument}` execution error: {io_error} " ) ? ;
2021-07-26 01:26:06 -07:00
} else {
2023-10-16 20:18:25 -07:00
write! ( f , " Recipe `{recipe}` with shebang `#!{command}` execution error: {io_error} " ) ? ;
2021-09-16 06:44:40 -07:00
}
}
2023-10-16 20:18:25 -07:00
Signal { recipe , line_number , signal } = > {
2021-07-26 01:26:06 -07:00
if let Some ( n ) = line_number {
2023-10-16 20:18:25 -07:00
write! ( f , " Recipe `{recipe}` was terminated on line {n} by signal {signal} " ) ? ;
2021-07-26 01:26:06 -07:00
} else {
2022-12-15 16:53:21 -08:00
write! ( f , " Recipe `{recipe}` was terminated by signal {signal} " ) ? ;
2021-09-16 06:44:40 -07:00
}
}
2023-10-16 20:18:25 -07:00
TmpdirIo { recipe , io_error } = > {
write! ( f , " Recipe `{recipe}` could not be run because of an IO error while trying to create a temporary \
directory or write a file to that directory ` :{ io_error } " )?;
}
Unknown { recipe , line_number } = > {
2021-07-26 01:26:06 -07:00
if let Some ( n ) = line_number {
2023-10-16 20:18:25 -07:00
write! ( f , " Recipe `{recipe}` failed on line {n} for an unknown reason " ) ? ;
2021-07-26 01:26:06 -07:00
} else {
2022-12-15 16:53:21 -08:00
write! ( f , " Recipe `{recipe}` failed for an unknown reason " ) ? ;
2021-09-16 06:44:40 -07:00
}
}
2021-07-26 01:26:06 -07:00
UnknownOverrides { overrides } = > {
2023-10-16 20:18:25 -07:00
let count = Count ( " Variable " , overrides . len ( ) ) ;
let overrides = List ::and_ticked ( overrides ) ;
write! ( f , " {count} {overrides} overridden on the command line but not present in justfile " ) ? ;
}
UnknownRecipes { recipes , suggestion } = > {
let count = Count ( " recipe " , recipes . len ( ) ) ;
let recipes = List ::or_ticked ( recipes ) ;
write! ( f , " Justfile does not contain {count} {recipes}. " ) ? ;
if let Some ( suggestion ) = suggestion {
2022-12-15 16:53:21 -08:00
write! ( f , " \n {suggestion} " ) ? ;
2021-07-26 01:26:06 -07:00
}
2021-09-16 06:44:40 -07:00
}
2021-07-26 01:26:06 -07:00
Unstable { message } = > {
2023-10-16 20:18:25 -07:00
write! ( f , " {message} Invoke `just` with the `--unstable` flag to enable unstable features. " ) ? ;
2021-09-16 06:44:40 -07:00
}
2021-07-26 01:26:06 -07:00
WriteJustfile { justfile , io_error } = > {
2023-10-16 20:18:25 -07:00
let justfile = justfile . display ( ) ;
write! ( f , " Failed to write justfile to `{justfile}`: {io_error} " ) ? ;
2021-09-16 06:44:40 -07:00
}
2021-07-26 01:26:06 -07:00
}
2021-07-28 18:06:57 -07:00
write! ( f , " {} " , color . message ( ) . suffix ( ) ) ? ;
2021-07-28 18:27:47 -07:00
if let ArgumentCountMismatch {
recipe , parameters , ..
} = self
{
writeln! ( f ) ? ;
2023-06-12 09:53:55 -07:00
write! ( f , " {}: \n just {recipe} " , color . message ( ) . paint ( " usage " ) ) ? ;
2021-07-28 18:27:47 -07:00
for param in parameters {
write! ( f , " {} " , param . color_display ( color ) ) ? ;
}
}
2021-07-28 18:06:57 -07:00
if let Some ( token ) = self . context ( ) {
writeln! ( f ) ? ;
write! ( f , " {} " , token . color_display ( color . error ( ) ) ) ? ;
}
2021-07-26 01:26:06 -07:00
Ok ( ( ) )
Gargantuan refactor (#522)
- Instead of changing the current directory with `env::set_current_dir`
to be implicitly inherited by subprocesses, we now use
`Command::current_dir` to set it explicitly. This feels much better,
since we aren't dependent on the implicit state of the process's
current directory.
- Subcommand execution is much improved.
- Added a ton of tests for config parsing, config execution, working
dir, and search dir.
- Error messages are improved. Many more will be colored.
- The Config is now onwed, instead of borrowing from the arguments and
the `clap::ArgMatches` object. This is a huge ergonomic improvement,
especially in tests, and I don't think anyone will notice.
- `--edit` now uses `$VISUAL`, `$EDITOR`, or `vim`, in that order,
matching git, which I think is what most people will expect.
- Added a cute `tmptree!{}` macro, for creating temporary directories
populated with directories and files for tests.
- Admitted that grammer is LL(k) and I don't know what `k` is.
2019-11-09 21:43:20 -08:00
}
}
2023-10-16 20:18:25 -07:00
fn format_cmd ( binary : & OsString , arguments : & Vec < OsString > ) -> String {
iter ::once ( binary )
. chain ( arguments )
. map ( | value | Enclosure ::tick ( value . to_string_lossy ( ) ) . to_string ( ) )
. collect ::< Vec < String > > ( )
. join ( " " )
}