diff --git a/crates/nu-cli/src/cli.rs b/crates/nu-cli/src/cli.rs index e9f22265c6..928e3ee2f3 100644 --- a/crates/nu-cli/src/cli.rs +++ b/crates/nu-cli/src/cli.rs @@ -389,6 +389,7 @@ pub fn create_default_context( whole_stream_command(RunExternalCommand { interactive }), // Random value generation whole_stream_command(Random), + whole_stream_command(RandomBool), whole_stream_command(RandomUUID), ]); diff --git a/crates/nu-cli/src/commands.rs b/crates/nu-cli/src/commands.rs index 9de323cd49..8b8fac0b50 100644 --- a/crates/nu-cli/src/commands.rs +++ b/crates/nu-cli/src/commands.rs @@ -213,7 +213,7 @@ pub(crate) use pivot::Pivot; pub(crate) use prepend::Prepend; pub(crate) use prev::Previous; pub(crate) use pwd::Pwd; -pub(crate) use random::{Random, RandomUUID}; +pub(crate) use random::{Random, RandomBool, RandomUUID}; pub(crate) use range::Range; #[allow(unused_imports)] pub(crate) use reduce_by::ReduceBy; diff --git a/crates/nu-cli/src/commands/random/bool.rs b/crates/nu-cli/src/commands/random/bool.rs new file mode 100644 index 0000000000..147bf3b911 --- /dev/null +++ b/crates/nu-cli/src/commands/random/bool.rs @@ -0,0 +1,97 @@ +use crate::commands::WholeStreamCommand; +use crate::prelude::*; +use nu_errors::ShellError; +use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue}; +use nu_source::Tagged; +use rand::prelude::{thread_rng, Rng}; + +pub struct SubCommand; + +#[derive(Deserialize)] +pub struct BoolArgs { + bias: Option>, +} + +#[async_trait] +impl WholeStreamCommand for SubCommand { + fn name(&self) -> &str { + "random bool" + } + + fn signature(&self) -> Signature { + Signature::build("random bool").named( + "bias", + SyntaxShape::Number, + "Adjusts the probability of a \"true\" outcome", + Some('b'), + ) + } + + fn usage(&self) -> &str { + "Generate a random boolean value" + } + + async fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + bool_command(args, registry).await + } + + fn examples(&self) -> Vec { + vec![ + Example { + description: "Generate a random boolean value", + example: "random bool", + result: None, + }, + Example { + description: "Generate a random boolean value with a 75% chance of \"true\"", + example: "random bool --bias 0.75", + result: None, + }, + ] + } +} + +pub async fn bool_command( + args: CommandArgs, + registry: &CommandRegistry, +) -> Result { + let (BoolArgs { bias }, _) = args.process(®istry).await?; + + let mut probability = 0.5; + + if let Some(prob) = bias { + probability = *prob as f64; + + let probability_is_valid = 0.0 <= probability && probability <= 1.0; + + if !probability_is_valid { + return Err(ShellError::labeled_error( + "The probability is invalid", + "invalid probability", + prob.span(), + )); + } + } + + let mut rng = thread_rng(); + let bool_result: bool = rng.gen_bool(probability); + let bool_untagged_value = UntaggedValue::boolean(bool_result); + + Ok(OutputStream::one(ReturnSuccess::value(bool_untagged_value))) +} + +#[cfg(test)] +mod tests { + use super::SubCommand; + + #[test] + fn examples_work_as_expected() { + use crate::examples::test as test_examples; + + test_examples(SubCommand {}) + } +} diff --git a/crates/nu-cli/src/commands/random/mod.rs b/crates/nu-cli/src/commands/random/mod.rs index b17d5b6c17..80a0b32cb6 100644 --- a/crates/nu-cli/src/commands/random/mod.rs +++ b/crates/nu-cli/src/commands/random/mod.rs @@ -1,5 +1,7 @@ +pub mod bool; pub mod command; pub mod uuid; +pub use self::bool::SubCommand as RandomBool; pub use command::Command as Random; pub use uuid::SubCommand as RandomUUID; diff --git a/crates/nu-cli/src/deserializer.rs b/crates/nu-cli/src/deserializer.rs index 67ce808f68..8d18e7e905 100644 --- a/crates/nu-cli/src/deserializer.rs +++ b/crates/nu-cli/src/deserializer.rs @@ -412,6 +412,15 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> { let i: i64 = int.tagged(value.val.tag).coerce_into("converting to i64")?; visit::, _>(i.tagged(tag), name, fields, visitor) } + Value { + value: UntaggedValue::Primitive(Primitive::Decimal(decimal)), + .. + } => { + let i: f64 = decimal + .tagged(value.val.tag) + .coerce_into("converting to f64")?; + visit::, _>(i.tagged(tag), name, fields, visitor) + } Value { value: UntaggedValue::Primitive(Primitive::String(string)), .. diff --git a/crates/nu-cli/tests/commands/random/bool.rs b/crates/nu-cli/tests/commands/random/bool.rs new file mode 100644 index 0000000000..862ba2840c --- /dev/null +++ b/crates/nu-cli/tests/commands/random/bool.rs @@ -0,0 +1,16 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn generates_a_bool() { + let actual = nu!( + cwd: ".", pipeline( + r#" + random bool + "# + )); + + let output = actual.out; + let is_boolean_output = output == "true" || output == "false"; + + assert!(is_boolean_output); +} diff --git a/crates/nu-cli/tests/commands/random/mod.rs b/crates/nu-cli/tests/commands/random/mod.rs index 14aae177b4..4cf161e026 100644 --- a/crates/nu-cli/tests/commands/random/mod.rs +++ b/crates/nu-cli/tests/commands/random/mod.rs @@ -1 +1,2 @@ +mod bool; mod uuid; diff --git a/crates/nu-cli/tests/commands/random/uuid.rs b/crates/nu-cli/tests/commands/random/uuid.rs index da64a48c9f..725081383c 100644 --- a/crates/nu-cli/tests/commands/random/uuid.rs +++ b/crates/nu-cli/tests/commands/random/uuid.rs @@ -2,7 +2,7 @@ use nu_test_support::{nu, pipeline}; use uuid_crate::Uuid; #[test] -fn makes_valid_uuid4() { +fn generates_valid_uuid4() { let actual = nu!( cwd: ".", pipeline( r#" diff --git a/docs/commands/random.md b/docs/commands/random.md index 6ed7967779..44df1b0bd5 100644 --- a/docs/commands/random.md +++ b/docs/commands/random.md @@ -2,11 +2,33 @@ Use `random` to generate random values +## uuid + * `random uuid`: Generate a random uuid4 string -## Examples +### uuid Examples ```shell > random uuid 8af4de39-acbc-42f0-94d1-7cfad6c01f8b ``` + +## bool + +* `random bool`: Generate a random boolean value + +### bool Flags + +* `-b`, `--bias` \: Adjusts the probability of a "true" outcome + +### bool Examples + +```shell +> random bool +false +``` + +```shell +> random bool --bias 0.75 +true +```