Skip to content

Commit

Permalink
Add reading mode
Browse files Browse the repository at this point in the history
  • Loading branch information
equation314 committed Dec 23, 2024
1 parent 8f40133 commit 551dbf6
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 25 deletions.
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,26 @@
### Executable Usage

```text
axconfig-gen [OPTIONS] --spec <SPEC>
Usage: axconfig-gen [OPTIONS] <SPEC>...
Arguments:
<SPEC>... Paths to the config specification files
Options:
-s, --spec <SPEC> Path to the config specification file
-c, --oldconfig <OLDCONFIG> Path to the old config file
-o, --output <OUTPUT> Path to the output config file
-f, --fmt <FMT> The output format [default: toml] [possible values: toml, rust]
-w, --write <CONFIG> Setting a config item with format `table.key=value`
-r, --read <RD_CONFIG> Getting a config item with format `table.key`
-w, --write <WR_CONFIG> Setting a config item with format `table.key=value`
-v, --verbose Verbose mode
-h, --help Print help
-V, --version Print version
```

For example, to generate a config file `.axconfig.toml` from the config specifications distributed in `a.toml` and `b.toml`, you can run:

```sh
axconfig-gen -s a.toml -s b.toml -o .axconfig.toml -f toml
```console
$ axconfig-gen a.toml b.toml -o .axconfig.toml -f toml
```

See [defconfig.toml](example-configs/defconfig.toml) for an example of a config specification file.
Expand Down
13 changes: 8 additions & 5 deletions axconfig-gen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,26 @@ A TOML-based configuration generation tool for [ArceOS](https://github.com/arceo
### Executable Usage

```text
axconfig-gen [OPTIONS] --spec <SPEC>
Usage: axconfig-gen [OPTIONS] <SPEC>...
Arguments:
<SPEC>... Paths to the config specification files
Options:
-s, --spec <SPEC> Path to the config specification file
-c, --oldconfig <OLDCONFIG> Path to the old config file
-o, --output <OUTPUT> Path to the output config file
-f, --fmt <FMT> The output format [default: toml] [possible values: toml, rust]
-w, --write <CONFIG> Setting a config item with format `table.key=value`
-r, --read <RD_CONFIG> Getting a config item with format `table.key`
-w, --write <WR_CONFIG> Setting a config item with format `table.key=value`
-v, --verbose Verbose mode
-h, --help Print help
-V, --version Print version
```

For example, to generate a config file `.axconfig.toml` from the config specifications distributed in `a.toml` and `b.toml`, you can run:

```sh
axconfig-gen -s a.toml -s b.toml -o .axconfig.toml -f toml
```console
$ axconfig-gen a.toml b.toml -o .axconfig.toml -f toml
```

See [defconfig.toml](https://github.com/arceos-org/axconfig-gen/blob/main/example-configs/defconfig.toml) for an example of a config specification file.
Expand Down
5 changes: 5 additions & 0 deletions axconfig-gen/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ impl Config {
}
}

/// Returns whether the config object contains no items.
pub fn is_empty(&self) -> bool {
self.global.is_empty() && self.tables.is_empty()
}

fn new_table(&mut self, name: &str, comments: &str) -> ConfigResult<&mut ConfigTable> {
if name == Self::GLOBAL_TABLE_NAME {
return Err(ConfigErr::Other(format!(
Expand Down
63 changes: 48 additions & 15 deletions axconfig-gen/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use clap::Parser;
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
/// Path to the config specification file
#[arg(short, long, required = true)]
/// Paths to the config specification files
#[arg(required = true)]
spec: Vec<String>,

/// Path to the old config file
Expand All @@ -29,20 +29,32 @@ struct Args {
)]
fmt: OutputFormat,

/// Getting a config item with format `table.key`
#[arg(short, long, id = "RD_CONFIG")]
read: Vec<String>,

/// Setting a config item with format `table.key=value`
#[arg(short, long, id = "CONFIG")]
#[arg(short, long, id = "WR_CONFIG")]
write: Vec<String>,

/// Verbose mode
#[arg(short, long)]
verbose: bool,
}

fn parse_config_write_cmd(cmd: &str) -> Result<(String, String, String), String> {
let (item, value) = cmd.split_once('=').ok_or_else(|| {
fn parse_config_read_arg(arg: &str) -> Result<(String, String), String> {
if let Some((table, key)) = arg.split_once('.') {
Ok((table.into(), key.into()))
} else {
Ok((Config::GLOBAL_TABLE_NAME.into(), arg.into()))
}
}

fn parse_config_write_arg(arg: &str) -> Result<(String, String, String), String> {
let (item, value) = arg.split_once('=').ok_or_else(|| {
format!(
"Invalid config setting command `{}`, expected `table.key=value`",
cmd
arg
)
})?;
if let Some((table, key)) = item.split_once('.') {
Expand Down Expand Up @@ -77,46 +89,67 @@ fn main() -> io::Result<()> {

let mut config = Config::new();
for spec in args.spec {
debug!("Reading config spec from {:?}", spec);
debug!("[DEBUG] Reading config spec from {:?}", spec);
let spec_toml = std::fs::read_to_string(spec)?;
let sub_config = unwrap!(Config::from_toml(&spec_toml));
unwrap!(config.merge(&sub_config));
}

if let Some(oldconfig_path) = args.oldconfig {
debug!("Loading old config from {:?}", oldconfig_path);
debug!("[DEBUG] Loading old config from {:?}", oldconfig_path);
let oldconfig_toml = std::fs::read_to_string(oldconfig_path)?;
let oldconfig = unwrap!(Config::from_toml(&oldconfig_toml));

let (untouched, extra) = unwrap!(config.update(&oldconfig));
for item in &untouched {
eprintln!(
"Warning: config item `{}` not set in the old config, using default value",
"[WARN] config item `{}` not set in the old config, using default value",
item.item_name(),
);
}
for item in &extra {
eprintln!(
"Warning: config item `{}` not found in the specification, ignoring",
"[WARN] config item `{}` not found in the specification, ignoring",
item.item_name(),
);
}
}

for cmd in args.write {
let (table, key, value) = unwrap!(parse_config_write_cmd(&cmd));
for arg in &args.write {
let (table, key, value) = unwrap!(parse_config_write_arg(arg));
if table == Config::GLOBAL_TABLE_NAME {
debug!("Setting config item `{}` to `{}`", key, value);
debug!("[DEBUG] Setting config item `{}` to `{}`", key, value);
} else {
debug!("Setting config item `{}.{}` to `{}`", table, key, value);
debug!(
"[DEBUG] Setting config item `{}.{}` to `{}`",
table, key, value
);
}
let new_value = unwrap!(ConfigValue::new(&value));
let item = unwrap!(config
.config_at_mut(&table, &key)
.ok_or("Config item not found"));
.ok_or_else(|| format!("Config item `{}` not found", arg)));
unwrap!(item.value_mut().update(new_value));
}

for arg in &args.read {
let (table, key) = unwrap!(parse_config_read_arg(arg));
if table == Config::GLOBAL_TABLE_NAME {
debug!("[DEBUG] Getting config item `{}`", key);
} else {
debug!("[DEBUG] Getting config item `{}.{}`", table, key);
}
let item = unwrap!(config
.config_at(&table, &key)
.ok_or_else(|| format!("Config item `{}` not found", arg)));
println!("{}", item.value().to_toml_value());
}

if !args.read.is_empty() {
debug!("[DEBUG] In reading mode, no output");
return Ok(());
}

let output = unwrap!(config.dump(args.fmt));
if let Some(path) = args.output.map(PathBuf::from) {
if let Ok(oldconfig) = std::fs::read_to_string(&path) {
Expand Down

0 comments on commit 551dbf6

Please sign in to comment.