cli: Add `rad config get`

Gets a specific config value.
This commit is contained in:
cloudhead 2024-02-07 17:11:05 +01:00
parent e5fcbba4c0
commit 137961c1f7
No known key found for this signature in database
3 changed files with 64 additions and 5 deletions

View File

@ -6,7 +6,8 @@ $ rad config
{ {
"publicExplorer": "https://app.radicle.xyz/nodes/$host/$rid$path", "publicExplorer": "https://app.radicle.xyz/nodes/$host/$rid$path",
"preferredSeeds": [ "preferredSeeds": [
"z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@seed.radicle.garden:8776" "z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@seed.radicle.garden:8776",
"z6MksmpU5b1dS7oaqF2bHXhQi1DWy2hB7Mh9CuN7y1DN6QSz@seed.radicle.xyz:8776"
], ],
"web": { "web": {
"pinned": { "pinned": {
@ -49,3 +50,15 @@ $ rad config
} }
} }
``` ```
You can also get any value in the configuration by path, eg.
```
$ rad config get node.alias
alice
$ rad config get preferredSeeds
z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@seed.radicle.garden:8776
z6MksmpU5b1dS7oaqF2bHXhQi1DWy2hB7Mh9CuN7y1DN6QSz@seed.radicle.xyz:8776
$ rad config get node.limits.routingMaxSize
1000
```

View File

@ -1,5 +1,6 @@
#![allow(clippy::or_fun_call)] #![allow(clippy::or_fun_call)]
use std::ffi::OsString; use std::ffi::OsString;
use std::path::Path;
use std::process; use std::process;
use anyhow::anyhow; use anyhow::anyhow;
@ -16,9 +17,11 @@ pub const HELP: Help = Help {
Usage Usage
rad config [<option>...] rad config [<option>...]
rad config show rad config show [<option>...]
rad config init rad config init [<option>...]
rad config edit rad config edit [<option>...]
rad config get <key> [<option>...]
If no argument is specified, prints the current radicle configuration as JSON. If no argument is specified, prints the current radicle configuration as JSON.
To initialize a new configuration file, use `rad config init`. To initialize a new configuration file, use `rad config init`.
@ -34,6 +37,7 @@ Options
enum Operation { enum Operation {
#[default] #[default]
Show, Show,
Get(String),
Init, Init,
Edit, Edit,
} }
@ -59,6 +63,12 @@ impl Args for Options {
"show" => op = Some(Operation::Show), "show" => op = Some(Operation::Show),
"edit" => op = Some(Operation::Edit), "edit" => op = Some(Operation::Edit),
"init" => op = Some(Operation::Init), "init" => op = Some(Operation::Init),
"get" => {
let value = parser.value()?;
let key = value.to_string_lossy();
op = Some(Operation::Get(key.to_string()));
}
unknown => anyhow::bail!("unknown operation '{unknown}'"), unknown => anyhow::bail!("unknown operation '{unknown}'"),
}, },
_ => return Err(anyhow!(arg.unexpected())), _ => return Err(anyhow!(arg.unexpected())),
@ -82,6 +92,12 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
Operation::Show => { Operation::Show => {
term::json::to_pretty(&profile.config, path.as_path())?.print(); term::json::to_pretty(&profile.config, path.as_path())?.print();
} }
Operation::Get(key) => {
let data = serde_json::to_value(profile.config)?;
if let Some(value) = get_value(&data, &key) {
print_value(value)?;
}
}
Operation::Init => { Operation::Init => {
if path.try_exists()? { if path.try_exists()? {
anyhow::bail!("configuration file already exists at `{}`", path.display()); anyhow::bail!("configuration file already exists at `{}`", path.display());
@ -104,3 +120,29 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
Ok(()) Ok(())
} }
/// Get JSON value under a path.
fn get_value<'a>(data: &'a serde_json::Value, path: &'a str) -> Option<&'a serde_json::Value> {
path.split('.').try_fold(data, |acc, key| {
if let serde_json::Value::Object(obj) = acc {
obj.get(key)
} else {
None
}
})
}
/// Print a JSON Value.
fn print_value(value: &serde_json::Value) -> anyhow::Result<()> {
match value {
serde_json::Value::Null => {}
serde_json::Value::Bool(b) => term::print(b),
serde_json::Value::Array(a) => a.iter().try_for_each(print_value)?,
serde_json::Value::Number(n) => term::print(n),
serde_json::Value::String(s) => term::print(s),
serde_json::Value::Object(o) => {
term::json::to_pretty(&o, Path::new("config.json"))?.print()
}
}
Ok(())
}

View File

@ -5,6 +5,7 @@ use std::{env, thread, time};
use radicle::git; use radicle::git;
use radicle::node; use radicle::node;
use radicle::node::address::Store as _; use radicle::node::address::Store as _;
use radicle::node::config::seeds::{RADICLE_COMMUNITY_NODE, RADICLE_TEAM_NODE};
use radicle::node::routing::Store as _; use radicle::node::routing::Store as _;
use radicle::node::Handle as _; use radicle::node::Handle as _;
use radicle::node::{Alias, DEFAULT_TIMEOUT}; use radicle::node::{Alias, DEFAULT_TIMEOUT};
@ -238,7 +239,10 @@ fn rad_inspect() {
fn rad_config() { fn rad_config() {
let mut environment = Environment::new(); let mut environment = Environment::new();
let alias = Alias::new("alice"); let alias = Alias::new("alice");
let profile = environment.profile(profile::Config::new(alias)); let profile = environment.profile(profile::Config {
preferred_seeds: vec![RADICLE_COMMUNITY_NODE.clone(), RADICLE_TEAM_NODE.clone()],
..profile::Config::new(alias)
});
let working = tempfile::tempdir().unwrap(); let working = tempfile::tempdir().unwrap();
test( test(