Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion aria/contents/docs/libra/command/config/index.mdx
Original file line number Diff line number Diff line change
@@ -1,4 +1,53 @@
---
title: The [config] Command
description:
description: Manage the configuration of the libra repository
---

### Usage

`libra config` [OPTIONS] `<key>` `<value_pattern>`

### Description

Manage configuration entries by adding, retrieving, listing, or deleting them in the repository. Supports pattern matching on key and value fields for flexible querying and deletion. Only one mode (`--add`, `--get`, `--get-all`, `--unset`, `--unset-all`, or `--list`) can be used at a time.

### Arguments

- `<key>` – The key string of the configuration entry, should be like `configuration.[name].key`.
_Required unless `--list` is used._

- `<value_pattern>` – The value or the pattern to match against configuration values.
_Required unless `--list` is used._

### Options

- `--add`
Add a configuration entry to the database.
_Requires `<value_pattern>`._

- `--get`
Get a single configuration entry matching the key and value pattern.

- `--get-all`
Get all configuration entries matching the key and value pattern.

- `--unset`
Remove a single configuration entry matching the key and value pattern.

- `--unset-all`
Remove all configuration entries matching the key and value pattern.

- `-l`, `--list`
List all configuration entries from the database.
_Does not require `key` or `value_pattern`._

- `-d`, `--default <value>`
Return the given default value if no matching key is found.
_Only valid with `--get` `--get-all`._

- `-h`, `--help`
Print help information.

<Note type="note" title="Note">
Only one of `--add`, `--get`, `--get-all`, `--unset`, `--unset-all`, or `--list` can be used at a time. Use `--default` only with `--get` and `--get-all`.
</Note>
42 changes: 36 additions & 6 deletions libra/src/command/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ pub struct ConfigArgs {
/// the value or the possible value pattern of the configuration entry
#[clap(value_name("value_pattern"), required_unless_present("mode"))]
pub valuepattern: Option<String>,
/// If the target key is not present, return the given default value.
/// This is only valid when `get` is set.
#[clap(long, short = 'd', requires = "get")]
pub default: Option<String>,
}

impl ConfigArgs {
pub fn validate(&self) -> Result<(), String> {
// validate the default value is only present when get is set
if self.default.is_some() && !(self.get || self.get_all) {
return Err("default value is only valid when get (get_all) is set".to_string());
}
Ok(())
}
}

pub struct Key {
Expand All @@ -37,6 +51,10 @@ pub struct Key {
}

pub async fn execute(args: ConfigArgs) {
if let Err(e) = args.validate() {
eprintln!("error: {}", e);
return;
}
if args.list {
list_config().await;
} else {
Expand All @@ -45,9 +63,9 @@ pub async fn execute(args: ConfigArgs) {
if args.add {
add_config(&key, &args.valuepattern.unwrap()).await;
} else if args.get {
get_config(&key, args.valuepattern.as_deref()).await;
get_config(&key, args.default.as_deref(), args.valuepattern.as_deref()).await;
} else if args.get_all {
get_all_config(&key, args.valuepattern.as_deref()).await;
get_all_config(&key, args.default.as_deref(), args.valuepattern.as_deref()).await;
} else if args.unset {
unset_config(&key, args.valuepattern.as_deref()).await;
} else if args.unset_all {
Expand Down Expand Up @@ -113,7 +131,7 @@ async fn set_config(key: &Key, value: &str) {
}

/// Get the first configuration by the given key and value pattern
async fn get_config(key: &Key, valuepattern: Option<&str>) {
async fn get_config(key: &Key, default: Option<&str>, valuepattern: Option<&str>) {
let value: Option<String> =
config::Config::get(&key.configuration, key.name.as_deref(), &key.key).await;
if let Some(v) = value {
Expand All @@ -126,22 +144,34 @@ async fn get_config(key: &Key, valuepattern: Option<&str>) {
// if value pattern is not present, just print it
println!("{}", v);
}
} else if let Some(default_value) = default {
// if value is not exits just return the default value if it's present
println!("{}", default_value);
}
}

/// Get all the configurations by the given key and value pattern
async fn get_all_config(key: &Key, valuepattern: Option<&str>) {
async fn get_all_config(key: &Key, default: Option<&str>, valuepattern: Option<&str>) {
let values: Vec<String> =
config::Config::get_all(&key.configuration, key.name.as_deref(), &key.key).await;
let mut matched_any = false;
for value in values {
if let Some(vp) = valuepattern {
// for each value, check if it matches the pattern
if value.contains(vp) {
println!("{}", value)
println!("{}", value);
matched_any = true;
}
} else {
// print all if value pattern is not present
println!("{}", value)
matched_any = true;
println!("{}", value);
}
}
if !matched_any {
if let Some(default_value) = default {
// if no value matches the pattern, print the default value if it's present
println!("{}", default_value);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion libra/src/utils/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub fn try_get_storage_path(path: Option<PathBuf>) -> Result<PathBuf, io::Error>
if !path.pop() {
return Err(io::Error::new(
io::ErrorKind::NotFound,
format!("{:?} is not a git repository", orig),
format!("{:?} is not a libra repository", orig),
));
}
}
Expand Down
147 changes: 147 additions & 0 deletions libra/tests/command/config_test.rs
Original file line number Diff line number Diff line change
@@ -1 +1,148 @@
use libra::command::config;
use serial_test::serial;
use tempfile::tempdir;

use super::*;
#[tokio::test]
#[serial]
async fn test_config_get_failed() {
let temp_path = tempdir().unwrap();
// start a new libra repository in a temporary directory
test::setup_with_new_libra_in(temp_path.path()).await;

let args = config::ConfigArgs {
add: true,
get: false,
get_all: false,
unset: false,
unset_all: false,
list: false,
key: Some("user.name".to_string()),
valuepattern: Some("value".to_string()),
default: Some("erasernoob".to_string()),
};
config::execute(args).await;
}

#[tokio::test]
#[serial]
async fn test_config_get_all() {
let temp_path = tempdir().unwrap();
// start a new libra repository in a temporary directory
test::setup_with_new_libra_in(temp_path.path()).await;

// set the current working directory to the temporary path
let _guard = test::ChangeDirGuard::new(temp_path.path());

// Add the config first
let arg1 = config::ConfigArgs {
add: true,
get: false,
get_all: false,
unset: false,
unset_all: false,
list: false,
key: Some("user.name".to_string()),
valuepattern: Some("erasernoob".to_string()),
default: None,
};
config::execute(arg1).await;

let args = config::ConfigArgs {
add: false,
get: true,
get_all: false,
unset: false,
unset_all: false,
list: false,
key: Some("user.name".to_string()),
valuepattern: None,
default: None,
};
config::execute(args).await;
}

#[tokio::test]
#[serial]
async fn test_config_get_all_with_default() {
let temp_path = tempdir().unwrap();
// start a new libra repository in a temporary directory
test::setup_with_new_libra_in(temp_path.path()).await;

// set the current working directory to the temporary path
let _guard = test::ChangeDirGuard::new(temp_path.path());

let args = config::ConfigArgs {
add: false,
get: false,
get_all: true,
unset: false,
unset_all: false,
list: false,
key: Some("user.name".to_string()),
valuepattern: Some("value".to_string()),
default: Some("erasernoob".to_string()),
};
config::execute(args).await;
}

#[tokio::test]
#[serial]
async fn test_config_get() {
let temp_path = tempdir().unwrap();
// start a new libra repository in a temporary directory
test::setup_with_new_libra_in(temp_path.path()).await;

// set the current working directory to the temporary path
let _guard = test::ChangeDirGuard::new(temp_path.path());

// Add the config first
let arg1 = config::ConfigArgs {
add: true,
get: false,
get_all: false,
unset: false,
unset_all: false,
list: false,
key: Some("user.name".to_string()),
valuepattern: Some("erasernoob".to_string()),
default: None,
};
config::execute(arg1).await;

let args = config::ConfigArgs {
add: false,
get: true,
get_all: false,
unset: false,
unset_all: false,
list: false,
key: Some("user.name".to_string()),
valuepattern: None,
default: None,
};
config::execute(args).await;
}

#[tokio::test]
#[serial]
async fn test_config_get_with_default() {
let temp_path = tempdir().unwrap();
// start a new libra repository in a temporary directory
test::setup_with_new_libra_in(temp_path.path()).await;

let _guard = test::ChangeDirGuard::new(temp_path.path());

let args = config::ConfigArgs {
add: false,
get: true,
get_all: false,
unset: false,
unset_all: false,
list: false,
key: Some("user.name".to_string()),
valuepattern: None,
default: Some("erasernoob".to_string()),
};
config::execute(args).await;
}
Loading