diff --git a/Cargo.lock b/Cargo.lock index 6d2cfec..13074cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -332,6 +332,7 @@ dependencies = [ "enum-as-inner", "lazy_static", "log", + "rand 0.8.3", "regex", "thiserror", ] @@ -368,6 +369,17 @@ dependencies = [ "wasi 0.9.0+wasi-snapshot-preview1", ] +[[package]] +name = "getrandom" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", +] + [[package]] name = "glob" version = "0.3.0" @@ -556,6 +568,12 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + [[package]] name = "proc-macro2" version = "1.0.24" @@ -593,6 +611,28 @@ dependencies = [ "winapi", ] +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.2", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.2", +] + [[package]] name = "rand_core" version = "0.3.1" @@ -608,6 +648,24 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" +[[package]] +name = "rand_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +dependencies = [ + "getrandom 0.2.2", +] + +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core 0.6.2", +] + [[package]] name = "rdrand" version = "0.4.0" @@ -629,7 +687,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" dependencies = [ - "getrandom", + "getrandom 0.1.16", "redox_syscall", "rust-argon2", ] @@ -791,7 +849,7 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" dependencies = [ - "rand", + "rand 0.4.6", "remove_dir_all", ] diff --git a/espanso-render/Cargo.toml b/espanso-render/Cargo.toml index 825eec2..3940d0d 100644 --- a/espanso-render/Cargo.toml +++ b/espanso-render/Cargo.toml @@ -11,4 +11,5 @@ thiserror = "1.0.23" regex = "1.4.3" lazy_static = "1.4.0" chrono = "0.4.19" -enum-as-inner = "0.3.3" \ No newline at end of file +enum-as-inner = "0.3.3" +rand = "0.8.3" \ No newline at end of file diff --git a/espanso-render/src/extension/mod.rs b/espanso-render/src/extension/mod.rs index 9711f62..836fe76 100644 --- a/espanso-render/src/extension/mod.rs +++ b/espanso-render/src/extension/mod.rs @@ -22,4 +22,5 @@ pub mod echo; pub mod clipboard; pub mod shell; pub mod script; +pub mod random; mod util; \ No newline at end of file diff --git a/espanso-render/src/extension/random.rs b/espanso-render/src/extension/random.rs new file mode 100644 index 0000000..91fd49a --- /dev/null +++ b/espanso-render/src/extension/random.rs @@ -0,0 +1,110 @@ +/* + * This file is part of espanso. + * + * Copyright (C) 2019-2021 Federico Terzi + * + * espanso is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * espanso is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with espanso. If not, see . + */ + +use crate::{Extension, ExtensionOutput, ExtensionResult, Params, Value}; +use rand::seq::SliceRandom; +use thiserror::Error; + +pub struct RandomExtension {} + +#[allow(clippy::new_without_default)] +impl RandomExtension { + pub fn new() -> Self { + Self {} + } +} + +impl Extension for RandomExtension { + fn name(&self) -> &str { + "random" + } + + fn calculate( + &self, + _: &crate::Context, + _: &crate::Scope, + params: &Params, + ) -> crate::ExtensionResult { + if let Some(Value::Array(choices)) = params.get("choices") { + let choices: Vec = choices + .iter() + .filter_map(|arg| arg.as_string()) + .cloned() + .collect(); + + if let Some(choice) = choices.choose(&mut rand::thread_rng()) { + ExtensionResult::Success(ExtensionOutput::Single(choice.clone())) + } else { + ExtensionResult::Error(RandomExtensionError::SelectionError.into()) + } + } else { + ExtensionResult::Error(RandomExtensionError::MissingChoicesParameter.into()) + } + } +} + +#[derive(Error, Debug)] +pub enum RandomExtensionError { + #[error("missing 'choices' parameter")] + MissingChoicesParameter, + + #[error("could not select a choice randomly")] + SelectionError, +} + +#[cfg(test)] +mod tests { + use super::*; + use std::iter::FromIterator; + + #[test] + fn random_works_correctly() { + let extension = RandomExtension::new(); + + let param = Params::from_iter( + vec![( + "choices".to_string(), + Value::Array(vec![ + Value::String("first".to_string()), + Value::String("second".to_string()), + Value::String("third".to_string()), + ]), + )] + .into_iter(), + ); + assert!(matches!( + extension + .calculate(&Default::default(), &Default::default(), ¶m) + .into_success() + .unwrap(), + ExtensionOutput::Single(result) if ["first", "second", "third"].contains(&result.as_str()) + )); + } + + #[test] + fn missing_echo_parameter() { + let extension = RandomExtension::new(); + + let param = Params::new(); + assert!(matches!( + extension.calculate(&Default::default(), &Default::default(), ¶m), + ExtensionResult::Error(_) + )); + } +}