From e848fc0bbe2a9f1b1d34ab47feea25d44ed5f9d7 Mon Sep 17 00:00:00 2001 From: Ritoban Roy-Chowdhury Date: Fri, 10 Jul 2020 17:11:04 -0700 Subject: [PATCH] Updates `config` to use subcommands (#2146) * First commit updating `config` to use subcommands (#2119) - Implemented `get` subcommand * Implmented `config set` as a subcommand. * Implemented `config set_into` as subcommand * Fixed base `config` command - Instead of outputting help, it now outputs the list of all configuration parameters. * Added `config clear` subcommand * Added `config load` and `config remove` subcommands * Added `config path` subcommand * fixed clippy --- crates/nu-cli/src/cli.rs | 7 + crates/nu-cli/src/commands.rs | 4 +- crates/nu-cli/src/commands/config.rs | 260 ------------------ crates/nu-cli/src/commands/config/clear.rs | 57 ++++ crates/nu-cli/src/commands/config/command.rs | 37 +++ crates/nu-cli/src/commands/config/get.rs | 83 ++++++ crates/nu-cli/src/commands/config/load.rs | 59 ++++ crates/nu-cli/src/commands/config/mod.rs | 17 ++ crates/nu-cli/src/commands/config/path.rs | 49 ++++ crates/nu-cli/src/commands/config/remove.rs | 75 +++++ crates/nu-cli/src/commands/config/set.rs | 68 +++++ crates/nu-cli/src/commands/config/set_into.rs | 98 +++++++ 12 files changed, 553 insertions(+), 261 deletions(-) delete mode 100644 crates/nu-cli/src/commands/config.rs create mode 100644 crates/nu-cli/src/commands/config/clear.rs create mode 100644 crates/nu-cli/src/commands/config/command.rs create mode 100644 crates/nu-cli/src/commands/config/get.rs create mode 100644 crates/nu-cli/src/commands/config/load.rs create mode 100644 crates/nu-cli/src/commands/config/mod.rs create mode 100644 crates/nu-cli/src/commands/config/path.rs create mode 100644 crates/nu-cli/src/commands/config/remove.rs create mode 100644 crates/nu-cli/src/commands/config/set.rs create mode 100644 crates/nu-cli/src/commands/config/set_into.rs diff --git a/crates/nu-cli/src/cli.rs b/crates/nu-cli/src/cli.rs index d0eaf1969c..f675421078 100644 --- a/crates/nu-cli/src/cli.rs +++ b/crates/nu-cli/src/cli.rs @@ -251,6 +251,13 @@ pub fn create_default_context( whole_stream_command(Remove), whole_stream_command(Open), whole_stream_command(Config), + whole_stream_command(ConfigGet), + whole_stream_command(ConfigSet), + whole_stream_command(ConfigSetInto), + whole_stream_command(ConfigClear), + whole_stream_command(ConfigLoad), + whole_stream_command(ConfigRemove), + whole_stream_command(ConfigPath), whole_stream_command(Help), whole_stream_command(History), whole_stream_command(Save), diff --git a/crates/nu-cli/src/commands.rs b/crates/nu-cli/src/commands.rs index 5d70260dfe..b057962903 100644 --- a/crates/nu-cli/src/commands.rs +++ b/crates/nu-cli/src/commands.rs @@ -153,7 +153,9 @@ pub(crate) use cal::Cal; pub(crate) use calc::Calc; pub(crate) use char_::Char; pub(crate) use compact::Compact; -pub(crate) use config::Config; +pub(crate) use config::{ + Config, ConfigClear, ConfigGet, ConfigLoad, ConfigPath, ConfigRemove, ConfigSet, ConfigSetInto, +}; pub(crate) use count::Count; pub(crate) use cp::Cpy; pub(crate) use date::Date; diff --git a/crates/nu-cli/src/commands/config.rs b/crates/nu-cli/src/commands/config.rs deleted file mode 100644 index 7a7c246c2f..0000000000 --- a/crates/nu-cli/src/commands/config.rs +++ /dev/null @@ -1,260 +0,0 @@ -use crate::commands::WholeStreamCommand; -use crate::context::CommandRegistry; -use crate::data::config; -use crate::prelude::*; -use nu_errors::ShellError; -use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; -use nu_source::Tagged; -use std::path::PathBuf; - -pub struct Config; - -#[derive(Deserialize)] -pub struct ConfigArgs { - load: Option>, - set: Option<(Tagged, Value)>, - set_into: Option>, - get: Option>, - clear: Tagged, - remove: Option>, - path: Tagged, -} - -#[async_trait] -impl WholeStreamCommand for Config { - fn name(&self) -> &str { - "config" - } - - fn signature(&self) -> Signature { - Signature::build("config") - .named( - "load", - SyntaxShape::Path, - "load the config from the path given", - Some('l'), - ) - .named( - "set", - SyntaxShape::Any, - "set a value in the config, eg) --set [key value]", - Some('s'), - ) - .named( - "set_into", - SyntaxShape::String, - "sets a variable from values in the pipeline", - Some('i'), - ) - .named( - "get", - SyntaxShape::Any, - "get a value from the config", - Some('g'), - ) - .named( - "remove", - SyntaxShape::Any, - "remove a value from the config", - Some('r'), - ) - .switch("clear", "clear the config", Some('c')) - .switch("path", "return the path to the config file", Some('p')) - } - - fn usage(&self) -> &str { - "Configuration management." - } - - async fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - config(args, registry).await - } - - fn examples(&self) -> Vec { - vec![ - Example { - description: "See all config values", - example: "config", - result: None, - }, - Example { - description: "Set completion_mode to circular", - example: "config --set [completion_mode circular]", - result: None, - }, - Example { - description: "Store the contents of the pipeline as a path", - example: "echo ['/usr/bin' '/bin'] | config --set_into path", - result: None, - }, - Example { - description: "Get the current startup commands", - example: "config --get startup", - result: None, - }, - Example { - description: "Remove the startup commands", - example: "config --remove startup", - result: None, - }, - Example { - description: "Clear the config (be careful!)", - example: "config --clear", - result: None, - }, - Example { - description: "Get the path to the current config file", - example: "config --path", - result: None, - }, - ] - } -} - -pub async fn config( - args: CommandArgs, - registry: &CommandRegistry, -) -> Result { - let name_span = args.call_info.name_tag.clone(); - let name = args.call_info.name_tag.clone(); - let registry = registry.clone(); - - let ( - ConfigArgs { - load, - set, - set_into, - get, - clear, - remove, - path, - }, - input, - ) = args.process(®istry).await?; - - let configuration = if let Some(supplied) = load { - Some(supplied.item().clone()) - } else { - None - }; - - let mut result = crate::data::config::read(name_span, &configuration)?; - - Ok(if let Some(v) = get { - let key = v.to_string(); - let value = result - .get(&key) - .ok_or_else(|| ShellError::labeled_error("Missing key in config", "key", v.tag()))?; - - match value { - Value { - value: UntaggedValue::Table(list), - .. - } => { - let list: Vec<_> = list - .iter() - .map(|x| ReturnSuccess::value(x.clone())) - .collect(); - - futures::stream::iter(list).to_output_stream() - } - x => { - let x = x.clone(); - OutputStream::one(ReturnSuccess::value(x)) - } - } - } else if let Some((key, value)) = set { - result.insert(key.to_string(), value.clone()); - - config::write(&result, &configuration)?; - - OutputStream::one(ReturnSuccess::value( - UntaggedValue::Row(result.into()).into_value(&value.tag), - )) - } else if let Some(v) = set_into { - let rows: Vec = input.collect().await; - let key = v.to_string(); - - if rows.is_empty() { - return Err(ShellError::labeled_error( - "No values given for set_into", - "needs value(s) from pipeline", - v.tag(), - )); - } else if rows.len() == 1 { - // A single value - let value = &rows[0]; - - result.insert(key, value.clone()); - - config::write(&result, &configuration)?; - - OutputStream::one(ReturnSuccess::value( - UntaggedValue::Row(result.into()).into_value(name), - )) - } else { - // Take in the pipeline as a table - let value = UntaggedValue::Table(rows).into_value(name.clone()); - - result.insert(key, value); - - config::write(&result, &configuration)?; - - OutputStream::one(ReturnSuccess::value( - UntaggedValue::Row(result.into()).into_value(name), - )) - } - } else if let Tagged { item: true, tag } = clear { - result.clear(); - - config::write(&result, &configuration)?; - - OutputStream::one(ReturnSuccess::value( - UntaggedValue::Row(result.into()).into_value(tag), - )) - } else if let Tagged { item: true, tag } = path { - let path = config::default_path_for(&configuration)?; - - OutputStream::one(ReturnSuccess::value( - UntaggedValue::Primitive(Primitive::Path(path)).into_value(tag), - )) - } else if let Some(v) = remove { - let key = v.to_string(); - - if result.contains_key(&key) { - result.swap_remove(&key); - config::write(&result, &configuration)?; - futures::stream::iter(vec![ReturnSuccess::value( - UntaggedValue::Row(result.into()).into_value(v.tag()), - )]) - .to_output_stream() - } else { - return Err(ShellError::labeled_error( - "Key does not exist in config", - "key", - v.tag(), - )); - } - } else { - futures::stream::iter(vec![ReturnSuccess::value( - UntaggedValue::Row(result.into()).into_value(name), - )]) - .to_output_stream() - }) -} - -#[cfg(test)] -mod tests { - use super::Config; - - #[test] - fn examples_work_as_expected() { - use crate::examples::test as test_examples; - - test_examples(Config {}) - } -} diff --git a/crates/nu-cli/src/commands/config/clear.rs b/crates/nu-cli/src/commands/config/clear.rs new file mode 100644 index 0000000000..53501255d5 --- /dev/null +++ b/crates/nu-cli/src/commands/config/clear.rs @@ -0,0 +1,57 @@ +use crate::commands::WholeStreamCommand; +use crate::context::CommandRegistry; +use crate::prelude::*; +use nu_errors::ShellError; +use nu_protocol::{ReturnSuccess, Signature, UntaggedValue}; + +pub struct SubCommand; + +#[async_trait] +impl WholeStreamCommand for SubCommand { + fn name(&self) -> &str { + "config clear" + } + + fn signature(&self) -> Signature { + Signature::build("config clear") + } + + fn usage(&self) -> &str { + "clear the config" + } + + async fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + clear(args, registry).await + } + + fn examples(&self) -> Vec { + vec![Example { + description: "Clear the config (be careful!)", + example: "config clear", + result: None, + }] + } +} + +pub async fn clear( + args: CommandArgs, + _registry: &CommandRegistry, +) -> Result { + let name_span = args.call_info.name_tag.clone(); + + // NOTE: None because we are not loading a new config file, we just want to read from the + // existing config + let mut result = crate::data::config::read(name_span, &None)?; + + result.clear(); + + config::write(&result, &None)?; + + Ok(OutputStream::one(ReturnSuccess::value( + UntaggedValue::Row(result.into()).into_value(args.call_info.name_tag), + ))) +} diff --git a/crates/nu-cli/src/commands/config/command.rs b/crates/nu-cli/src/commands/config/command.rs new file mode 100644 index 0000000000..854f3cd0ac --- /dev/null +++ b/crates/nu-cli/src/commands/config/command.rs @@ -0,0 +1,37 @@ +use crate::commands::WholeStreamCommand; +use crate::prelude::*; +use crate::{CommandArgs, CommandRegistry, OutputStream}; +use nu_errors::ShellError; +use nu_protocol::{ReturnSuccess, Signature, UntaggedValue}; + +pub struct Command; + +#[async_trait] +impl WholeStreamCommand for Command { + fn name(&self) -> &str { + "config" + } + + fn signature(&self) -> Signature { + Signature::build("config") + } + + fn usage(&self) -> &str { + "Configuration management." + } + + async fn run( + &self, + args: CommandArgs, + _registry: &CommandRegistry, + ) -> Result { + let name_span = args.call_info.name_tag.clone(); + let name = args.call_info.name_tag; + let result = crate::data::config::read(name_span, &None)?; + + Ok(futures::stream::iter(vec![ReturnSuccess::value( + UntaggedValue::Row(result.into()).into_value(name), + )]) + .to_output_stream()) + } +} diff --git a/crates/nu-cli/src/commands/config/get.rs b/crates/nu-cli/src/commands/config/get.rs new file mode 100644 index 0000000000..d947a2d208 --- /dev/null +++ b/crates/nu-cli/src/commands/config/get.rs @@ -0,0 +1,83 @@ +use crate::commands::WholeStreamCommand; +use crate::context::CommandRegistry; +use crate::prelude::*; +use nu_errors::ShellError; +use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; +use nu_source::Tagged; + +pub struct SubCommand; + +#[derive(Deserialize)] +pub struct GetArgs { + get: Tagged, +} + +#[async_trait] +impl WholeStreamCommand for SubCommand { + fn name(&self) -> &str { + "config get" + } + + fn signature(&self) -> Signature { + Signature::build("config get").required( + "get", + SyntaxShape::Any, + "value to get from the config", + ) + } + + fn usage(&self) -> &str { + "Gets a value from the config" + } + + async fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + get(args, registry).await + } + + fn examples(&self) -> Vec { + vec![Example { + description: "Get the current startup commands", + example: "config get startup", + result: None, + }] + } +} + +pub async fn get( + args: CommandArgs, + registry: &CommandRegistry, +) -> Result { + let name_span = args.call_info.name_tag.clone(); + let (GetArgs { get }, _) = args.process(®istry).await?; + + // NOTE: None because we are not loading a new config file, we just want to read from the + // existing config + let result = crate::data::config::read(name_span, &None)?; + + let key = get.to_string(); + let value = result + .get(&key) + .ok_or_else(|| ShellError::labeled_error("Missing key in config", "key", get.tag()))?; + + Ok(match value { + Value { + value: UntaggedValue::Table(list), + .. + } => { + let list: Vec<_> = list + .iter() + .map(|x| ReturnSuccess::value(x.clone())) + .collect(); + + futures::stream::iter(list).to_output_stream() + } + x => { + let x = x.clone(); + OutputStream::one(ReturnSuccess::value(x)) + } + }) +} diff --git a/crates/nu-cli/src/commands/config/load.rs b/crates/nu-cli/src/commands/config/load.rs new file mode 100644 index 0000000000..2c49fc777a --- /dev/null +++ b/crates/nu-cli/src/commands/config/load.rs @@ -0,0 +1,59 @@ +use crate::commands::WholeStreamCommand; +use crate::context::CommandRegistry; +use crate::prelude::*; +use nu_errors::ShellError; +use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue}; +use nu_source::Tagged; +use std::path::PathBuf; + +pub struct SubCommand; + +#[derive(Deserialize)] +pub struct LoadArgs { + load: Tagged, +} + +#[async_trait] +impl WholeStreamCommand for SubCommand { + fn name(&self) -> &str { + "config load" + } + + fn signature(&self) -> Signature { + Signature::build("config load").required( + "load", + SyntaxShape::Path, + "Path to load the config from", + ) + } + + fn usage(&self) -> &str { + "Loads the config from the path given" + } + + async fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + set(args, registry).await + } +} + +pub async fn set( + args: CommandArgs, + registry: &CommandRegistry, +) -> Result { + let name = args.call_info.name_tag.clone(); + let name_span = args.call_info.name_tag.clone(); + let (LoadArgs { load }, _) = args.process(®istry).await?; + + let configuration = load.item().clone(); + + let result = crate::data::config::read(name_span, &Some(configuration))?; + + Ok(futures::stream::iter(vec![ReturnSuccess::value( + UntaggedValue::Row(result.into()).into_value(name), + )]) + .to_output_stream()) +} diff --git a/crates/nu-cli/src/commands/config/mod.rs b/crates/nu-cli/src/commands/config/mod.rs new file mode 100644 index 0000000000..6d57aeed1a --- /dev/null +++ b/crates/nu-cli/src/commands/config/mod.rs @@ -0,0 +1,17 @@ +pub mod clear; +pub mod command; +pub mod get; +pub mod load; +pub mod path; +pub mod remove; +pub mod set; +pub mod set_into; + +pub use clear::SubCommand as ConfigClear; +pub use command::Command as Config; +pub use get::SubCommand as ConfigGet; +pub use load::SubCommand as ConfigLoad; +pub use path::SubCommand as ConfigPath; +pub use remove::SubCommand as ConfigRemove; +pub use set::SubCommand as ConfigSet; +pub use set_into::SubCommand as ConfigSetInto; diff --git a/crates/nu-cli/src/commands/config/path.rs b/crates/nu-cli/src/commands/config/path.rs new file mode 100644 index 0000000000..0df3abf351 --- /dev/null +++ b/crates/nu-cli/src/commands/config/path.rs @@ -0,0 +1,49 @@ +use crate::commands::WholeStreamCommand; +use crate::context::CommandRegistry; +use crate::prelude::*; +use nu_errors::ShellError; +use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue}; + +pub struct SubCommand; + +#[async_trait] +impl WholeStreamCommand for SubCommand { + fn name(&self) -> &str { + "config path" + } + + fn signature(&self) -> Signature { + Signature::build("config path") + } + + fn usage(&self) -> &str { + "return the path to the config file" + } + + async fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + path(args, registry).await + } + + fn examples(&self) -> Vec { + vec![Example { + description: "Get the path to the current config file", + example: "config path", + result: None, + }] + } +} + +pub async fn path( + args: CommandArgs, + _registry: &CommandRegistry, +) -> Result { + let path = config::default_path()?; + + Ok(OutputStream::one(ReturnSuccess::value( + UntaggedValue::Primitive(Primitive::Path(path)).into_value(args.call_info.name_tag), + ))) +} diff --git a/crates/nu-cli/src/commands/config/remove.rs b/crates/nu-cli/src/commands/config/remove.rs new file mode 100644 index 0000000000..57f8ecdf9c --- /dev/null +++ b/crates/nu-cli/src/commands/config/remove.rs @@ -0,0 +1,75 @@ +use crate::commands::WholeStreamCommand; +use crate::context::CommandRegistry; +use crate::prelude::*; +use nu_errors::ShellError; +use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue}; +use nu_source::Tagged; + +pub struct SubCommand; + +#[derive(Deserialize)] +pub struct RemoveArgs { + remove: Tagged, +} + +#[async_trait] +impl WholeStreamCommand for SubCommand { + fn name(&self) -> &str { + "config remove" + } + + fn signature(&self) -> Signature { + Signature::build("config remove").required( + "remove", + SyntaxShape::Any, + "remove a value from the config", + ) + } + + fn usage(&self) -> &str { + "Removes a value from the config" + } + + async fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + remove(args, registry).await + } + + fn examples(&self) -> Vec { + vec![Example { + description: "Remove the startup commands", + example: "config --remove startup", + result: None, + }] + } +} + +pub async fn remove( + args: CommandArgs, + registry: &CommandRegistry, +) -> Result { + let name_span = args.call_info.name_tag.clone(); + let (RemoveArgs { remove }, _) = args.process(®istry).await?; + + let mut result = crate::data::config::read(name_span, &None)?; + + let key = remove.to_string(); + + if result.contains_key(&key) { + result.swap_remove(&key); + config::write(&result, &None)?; + Ok(futures::stream::iter(vec![ReturnSuccess::value( + UntaggedValue::Row(result.into()).into_value(remove.tag()), + )]) + .to_output_stream()) + } else { + Err(ShellError::labeled_error( + "Key does not exist in config", + "key", + remove.tag(), + )) + } +} diff --git a/crates/nu-cli/src/commands/config/set.rs b/crates/nu-cli/src/commands/config/set.rs new file mode 100644 index 0000000000..fad2d5affe --- /dev/null +++ b/crates/nu-cli/src/commands/config/set.rs @@ -0,0 +1,68 @@ +use crate::commands::WholeStreamCommand; +use crate::context::CommandRegistry; +use crate::prelude::*; +use nu_errors::ShellError; +use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; +use nu_source::Tagged; + +pub struct SubCommand; + +#[derive(Deserialize)] +pub struct SetArgs { + set: (Tagged, Value), +} + +#[async_trait] +impl WholeStreamCommand for SubCommand { + fn name(&self) -> &str { + "config set" + } + + fn signature(&self) -> Signature { + Signature::build("config set").required( + "set", + SyntaxShape::Any, + "sets a value in the config, eg) set [key value]", + ) + } + + fn usage(&self) -> &str { + "Sets a value in the config" + } + + async fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + set(args, registry).await + } + + fn examples(&self) -> Vec { + vec![Example { + description: "Set completion_mode to circular", + example: "config set [completion_mode circular]", + result: None, + }] + } +} + +pub async fn set( + args: CommandArgs, + registry: &CommandRegistry, +) -> Result { + let name_span = args.call_info.name_tag.clone(); + let (SetArgs { set: (key, value) }, _) = args.process(®istry).await?; + + // NOTE: None because we are not loading a new config file, we just want to read from the + // existing config + let mut result = crate::data::config::read(name_span, &None)?; + + result.insert(key.to_string(), value.clone()); + + config::write(&result, &None)?; + + Ok(OutputStream::one(ReturnSuccess::value( + UntaggedValue::Row(result.into()).into_value(&value.tag), + ))) +} diff --git a/crates/nu-cli/src/commands/config/set_into.rs b/crates/nu-cli/src/commands/config/set_into.rs new file mode 100644 index 0000000000..f41d0043de --- /dev/null +++ b/crates/nu-cli/src/commands/config/set_into.rs @@ -0,0 +1,98 @@ +use crate::commands::WholeStreamCommand; +use crate::context::CommandRegistry; +use crate::prelude::*; +use nu_errors::ShellError; +use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; +use nu_source::Tagged; + +pub struct SubCommand; + +#[derive(Deserialize)] +pub struct SetIntoArgs { + set_into: Tagged, +} + +#[async_trait] +impl WholeStreamCommand for SubCommand { + fn name(&self) -> &str { + "config set_into" + } + + fn signature(&self) -> Signature { + Signature::build("config set_into").required( + "set_into", + SyntaxShape::String, + "sets a variable from values in the pipeline", + ) + } + + fn usage(&self) -> &str { + "Sets a value in the config" + } + + async fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + set_into(args, registry).await + } + + fn examples(&self) -> Vec { + vec![Example { + description: "Store the contents of the pipeline as a path", + example: "echo ['/usr/bin' '/bin'] | config set_into path", + result: None, + }] + } +} + +pub async fn set_into( + args: CommandArgs, + registry: &CommandRegistry, +) -> Result { + let name_span = args.call_info.name_tag.clone(); + let name = args.call_info.name_tag.clone(); + + let (SetIntoArgs { set_into: v }, input) = args.process(®istry).await?; + + // NOTE: None because we are not loading a new config file, we just want to read from the + // existing config + let mut result = crate::data::config::read(name_span, &None)?; + + // In the original code, this is set to `Some` if the `--load flag is set` + let configuration = None; + + let rows: Vec = input.collect().await; + let key = v.to_string(); + + Ok(if rows.is_empty() { + return Err(ShellError::labeled_error( + "No values given for set_into", + "needs value(s) from pipeline", + v.tag(), + )); + } else if rows.len() == 1 { + // A single value + let value = &rows[0]; + + result.insert(key, value.clone()); + + config::write(&result, &configuration)?; + + OutputStream::one(ReturnSuccess::value( + UntaggedValue::Row(result.into()).into_value(name), + )) + } else { + // Take in the pipeline as a table + let value = UntaggedValue::Table(rows).into_value(name.clone()); + + result.insert(key, value); + + config::write(&result, &configuration)?; + + OutputStream::one(ReturnSuccess::value( + UntaggedValue::Row(result.into()).into_value(name), + )) + }) +}