mirror of
https://github.com/nushell/nushell.git
synced 2025-07-01 15:11:52 +02:00
Add decimals to int when using into string --decimals
(#6085)
* Add decimals to int when using `into string --decimals` * Add tests for `into string` when converting int with `--decimals` * Apply formatting * Merge `into_str` test files * Comment out unused code and add TODOs * Use decimal separator depending on system locale * Add test helper to run closure in different locale * Add tests for int-to-string conversion using different locales * Add utils function to get system locale * Add panic message when locking mutex fails * Catch and resume panic later to prevent Mutex poisoning when test fails * Move test to `nu-test-support` to keep `nu-utils` free of `nu-*` dependencies See https://github.com/nushell/nushell/pull/6085#issuecomment-1193131694 * Rename test support fn `with_fake_locale` to `with_locale_override` * Move `get_system_locale()` to `locale` module * Allow overriding locale with special env variable (when not in release) * Use special env var to override locale during testing * Allow callback to return a value in `with_locale_override()` * Allow multiple options in `nu!` macro * Allow to set locale as `nu!` macro option * Use new `locale` option of `nu!` macro instead of `with_locale_override` Using the `locale` options does not lock the `LOCALE_OVERRIDE_MUTEX` mutex in `nu-test-support::locale_override` but instead calls the `nu` command directly with the `NU_LOCALE_OVERRIDE` environment variable. This allows for parallel test excecution. * Fix: Add option identifier for `cwd` in usage of `nu!` macro * Rely on `Display` trait for formatting `nu!` macro command - Removed the `DisplayPath` trait - Implement `Display` for `AbsolutePath`, `RelativePath` and `AbsoluteFile` * Default to locale `en_US.UTF-8` for tests when using `nu!` macro * Add doc comment to `nu!` macro * Format code using `cargo fmt --all` * Pass function directly instead of wrapping the call in a closure https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure * Pass function to `or_else()` instead of calling it inside `or()` https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call * Fix: Add option identifier for `cwd` in usage of `nu!` macro
This commit is contained in:
@ -1,21 +1,120 @@
|
||||
/// Run a command in nu and get it's output
|
||||
///
|
||||
/// The `nu!` macro accepts a number of options like the `cwd` in which the
|
||||
/// command should be run. It is also possible to specify a different `locale`
|
||||
/// to test locale dependent commands.
|
||||
///
|
||||
/// Pass options as the first arguments in the form of `key_1: value_1, key_1:
|
||||
/// value_2, ...`. The options are defined in the `NuOpts` struct inside the
|
||||
/// `nu!` macro.
|
||||
///
|
||||
/// The command can be formatted using `{}` just like `println!` or `format!`.
|
||||
/// Pass the format arguments comma separated after the command itself.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # // NOTE: The `nu!` macro needs the `nu` binary to exist. The test are
|
||||
/// # // therefore only compiled but not run (thats what the `no_run` at
|
||||
/// # // the beginning of this code block is for).
|
||||
/// #
|
||||
/// use nu_test_support::nu;
|
||||
///
|
||||
/// let outcome = nu!(
|
||||
/// "date now | date to-record | get year"
|
||||
/// );
|
||||
///
|
||||
/// let dir = "/";
|
||||
/// let outcome = nu!(
|
||||
/// "ls {} | get name",
|
||||
/// dir,
|
||||
/// );
|
||||
///
|
||||
/// let outcome = nu!(
|
||||
/// cwd: "/",
|
||||
/// "ls | get name",
|
||||
/// );
|
||||
///
|
||||
/// let cell = "size";
|
||||
/// let outcome = nu!(
|
||||
/// locale: "de_DE.UTF-8",
|
||||
/// "ls | into int {}",
|
||||
/// cell,
|
||||
/// );
|
||||
///
|
||||
/// let decimals = 2;
|
||||
/// let outcome = nu!(
|
||||
/// locale: "de_DE.UTF-8",
|
||||
/// "10 | into string --decimals {}",
|
||||
/// decimals,
|
||||
/// );
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! nu {
|
||||
(cwd: $cwd:expr, $path:expr, $($part:expr),*) => {{
|
||||
use $crate::fs::DisplayPath;
|
||||
// In the `@options` phase, we restucture all the
|
||||
// `$field_1: $value_1, $field_2: $value_2, ...`
|
||||
// pairs to a structure like
|
||||
// `@options[ $field_1 => $value_1 ; $field_2 => $value_2 ; ... ]`.
|
||||
// We do this to later distinguish the options from the `$path` and `$part`s.
|
||||
// (See
|
||||
// https://users.rust-lang.org/t/i-dont-think-this-local-ambiguity-when-calling-macro-is-ambiguous/79401?u=x3ro
|
||||
// )
|
||||
//
|
||||
// If there is any special treatment needed for the `$value`, we can just
|
||||
// match for the specific `field` name.
|
||||
(
|
||||
@options [ $($options:tt)* ]
|
||||
cwd: $value:expr,
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
nu!(@options [ $($options)* cwd => $crate::fs::in_directory($value) ; ] $($rest)*)
|
||||
};
|
||||
// For all other options, we call `.into()` on the `$value` and hope for the best. ;)
|
||||
(
|
||||
@options [ $($options:tt)* ]
|
||||
$field:ident : $value:expr,
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
nu!(@options [ $($options)* $field => $value.into() ; ] $($rest)*)
|
||||
};
|
||||
|
||||
let path = format!($path, $(
|
||||
$part.display_path()
|
||||
),*);
|
||||
|
||||
nu!($cwd, &path)
|
||||
// When the `$field: $value,` pairs are all parsed, the next tokens are the `$path` and any
|
||||
// number of `$part`s, potentially followed by a trailing comma.
|
||||
(
|
||||
@options [ $($options:tt)* ]
|
||||
$path:expr
|
||||
$(, $part:expr)*
|
||||
$(,)*
|
||||
) => {{
|
||||
// Here we parse the options into a `NuOpts` struct
|
||||
let opts = nu!(@nu_opts $($options)*);
|
||||
// and format the `$path` using the `$part`s
|
||||
let path = nu!(@format_path $path, $($part),*);
|
||||
// Then finally we go to the `@main` phase, where the actual work is done.
|
||||
nu!(@main opts, path)
|
||||
}};
|
||||
|
||||
(cwd: $cwd:expr, $path:expr) => {{
|
||||
nu!($cwd, $path)
|
||||
// Create the NuOpts struct from the `field => value ;` pairs
|
||||
(@nu_opts $( $field:ident => $value:expr ; )*) => {
|
||||
NuOpts{
|
||||
$(
|
||||
$field: Some($value),
|
||||
)*
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
|
||||
// Helper to format `$path`.
|
||||
(@format_path $path:expr $(,)?) => {
|
||||
// When there are no `$part`s, do not format anything
|
||||
$path
|
||||
};
|
||||
(@format_path $path:expr, $($part:expr),* $(,)?) => {{
|
||||
format!($path, $( $part ),*)
|
||||
}};
|
||||
|
||||
($cwd:expr, $path:expr) => {{
|
||||
pub use itertools::Itertools;
|
||||
// Do the actual work.
|
||||
(@main $opts:expr, $path:expr) => {{
|
||||
pub use std::error::Error;
|
||||
pub use std::io::prelude::*;
|
||||
pub use std::process::{Command, Stdio};
|
||||
@ -36,13 +135,6 @@ macro_rules! nu {
|
||||
output
|
||||
}
|
||||
|
||||
// let commands = &*format!(
|
||||
// "
|
||||
// {}
|
||||
// exit",
|
||||
// $crate::fs::DisplayPath::display_path(&$path)
|
||||
// );
|
||||
|
||||
let test_bins = $crate::fs::binaries();
|
||||
|
||||
let cwd = std::env::current_dir().expect("Could not get current working directory.");
|
||||
@ -64,21 +156,25 @@ macro_rules! nu {
|
||||
Err(_) => panic!("Couldn't join paths for PATH var."),
|
||||
};
|
||||
|
||||
let target_cwd = $crate::fs::in_directory(&$cwd);
|
||||
let target_cwd = $opts.cwd.unwrap_or(".".to_string());
|
||||
let locale = $opts.locale.unwrap_or("en_US.UTF-8".to_string());
|
||||
|
||||
let mut process = match Command::new($crate::fs::executable_path())
|
||||
let mut command = Command::new($crate::fs::executable_path());
|
||||
command
|
||||
.env("PWD", &target_cwd)
|
||||
.env(nu_utils::locale::LOCALE_OVERRIDE_ENV_VAR, locale)
|
||||
.current_dir(target_cwd)
|
||||
.env(NATIVE_PATH_ENV_VAR, paths_joined)
|
||||
// .arg("--skip-plugins")
|
||||
// .arg("--no-history")
|
||||
// .arg("--config-file")
|
||||
// .arg($crate::fs::DisplayPath::display_path(&$crate::fs::fixtures().join("playground/config/default.toml")))
|
||||
.arg(format!("-c {}", escape_quote_string($crate::fs::DisplayPath::display_path(&path))))
|
||||
.arg(format!("-c {}", escape_quote_string(path)))
|
||||
.stdout(Stdio::piped())
|
||||
// .stdin(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.stderr(Stdio::piped());
|
||||
|
||||
let mut process = match command.spawn()
|
||||
{
|
||||
Ok(child) => child,
|
||||
Err(why) => panic!("Can't run test {:?} {}", $crate::fs::executable_path(), why.to_string()),
|
||||
@ -100,6 +196,17 @@ macro_rules! nu {
|
||||
|
||||
$crate::Outcome::new(out,err.into_owned())
|
||||
}};
|
||||
|
||||
// This is the entrypoint for this macro.
|
||||
($($token:tt)*) => {{
|
||||
#[derive(Default)]
|
||||
struct NuOpts {
|
||||
cwd: Option<String>,
|
||||
locale: Option<String>,
|
||||
}
|
||||
|
||||
nu!(@options [ ] $($token)*)
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
|
Reference in New Issue
Block a user