Skip to content

Commit

Permalink
feat(cli): Add config command
Browse files Browse the repository at this point in the history
Adds a new config command with four subcommands:
- `get`: gets the given keys
- `set`: sets the given key
- `list`: displays the full config
- `default`: displays the default config
  • Loading branch information
tingerrr committed Aug 30, 2024
1 parent 1f86143 commit c88eb2d
Show file tree
Hide file tree
Showing 7 changed files with 294 additions and 0 deletions.
6 changes: 6 additions & 0 deletions crates/typst-test-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use crate::world::SystemWorld;
use crate::{project, ui};

pub mod add;
pub mod config;
pub mod edit;
pub mod init;
pub mod list;
Expand Down Expand Up @@ -813,6 +814,10 @@ pub enum Command {
#[command(visible_alias = "rm")]
Remove(remove::Args),

/// Display and edit config
#[command()]
Config(config::Args),

/// Utility commands
#[command()]
Util(util::Args),
Expand All @@ -830,6 +835,7 @@ impl Command {
Command::List(args) => list::run(ctx, args),
Command::Update(args) => update::run(ctx, args),
Command::Run(args) => run::run(ctx, args),
Command::Config(args) => args.cmd.run(ctx),
Command::Util(args) => args.cmd.run(ctx),
}
}
Expand Down
129 changes: 129 additions & 0 deletions crates/typst-test-cli/src/cli/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use std::collections::BTreeMap;
use std::io;
use std::io::Write;

use serde::Serialize;
use termcolor::{Color, WriteColor};
use thiserror::Error;
use typst_test_lib::config::Config;
use typst_test_stdx::fmt::Separators;

use super::Context;
use crate::error::Failure;
use crate::report::{Report, Verbosity};
use crate::ui;
use crate::ui::{Indented, Ui};

pub mod default;
pub mod get;
pub mod list;
pub mod set;

#[derive(clap::Args, Debug, Clone)]
#[group(id = "config-args")]
pub struct Args {
/// The sub command to run
#[command(subcommand)]
pub cmd: Command,
}

#[derive(clap::Subcommand, Debug, Clone)]
pub enum Command {
/// Get a single config value
#[command()]
Get(get::Args),

/// Set a single config value
#[command()]
Set(set::Args),

/// List the full config
#[command()]
List,

/// Show the default config
#[command()]
Default(default::Args),
}

impl Command {
pub fn run(&self, ctx: &mut Context) -> anyhow::Result<()> {
match self {
Command::Get(args) => get::run(ctx, args),
Command::Set(args) => set::run(ctx, args),
Command::List => list::run(ctx),
Command::Default(args) => default::run(ctx, args),
}
}
}

#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum ConfigJson<'c> {
Pretty {
#[serde(flatten)]
inner: BTreeMap<&'c str, &'c Option<String>>,
},
Toml {
#[serde(flatten)]
inner: &'c Config,
},
}

impl Report for ConfigJson<'_> {
fn report<W: WriteColor>(&self, mut writer: W, verbosity: Verbosity) -> anyhow::Result<()> {
match self {
ConfigJson::Pretty { inner } => {
writeln!(writer, "Config")?;

let writer = &mut Indented::new(writer, 2);
let pad = Ord::min(
inner
.iter()
.map(|(&k, _)| k.len())
.max()
.unwrap_or(usize::MAX),
50,
);

for (&k, &v) in inner {
if verbosity <= Verbosity::Less && v.is_none() {
continue;
}

ui::write_colored(writer, Color::Cyan, |w| write!(w, "{:<pad$}", k))?;
write!(writer, " = ")?;
if let Some(v) = v {
ui::write_colored(writer, Color::Green, |w| writeln!(w, "{:?}", v))?;
} else {
ui::write_colored(writer, Color::Magenta, |w| writeln!(w, "null"))?;
}
}
}
ConfigJson::Toml { inner } => {
let mut doc = toml_edit::DocumentMut::new();
inner.write_into(&mut doc)?;
// NOTE: DocumentMut::fmt writes a newline by itself
write!(&mut writer, "{doc}")?;
}
}

Ok(())
}
}

#[derive(Debug, Error)]
#[error("unknown config keys {0:?}")]
pub struct UnknownConfigKeys(Vec<String>);

impl Failure for UnknownConfigKeys {
fn report(&self, ui: &Ui) -> io::Result<()> {
ui.error_with(|w| {
writeln!(
w,
"Unknown config keys: {}",
Separators::comma_and().with(self.0.iter())
)
})
}
}
27 changes: 27 additions & 0 deletions crates/typst-test-cli/src/cli/config/default.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use typst_test_lib::config;

use super::ConfigJson;
use crate::cli::Context;

#[derive(clap::Args, Debug, Clone)]
#[group(id = "config-set-args")]
pub struct Args {
/// Whether to output the config as a manifest tool section
#[arg(long, conflicts_with = "format")]
toml: bool,
}

pub fn run(ctx: &mut Context, args: &Args) -> anyhow::Result<()> {
let mut config = config::Config::default();
config.set_fallbacks();

ctx.reporter.report(&if args.toml {
ConfigJson::Toml { inner: &config }
} else {
ConfigJson::Pretty {
inner: config.pairs().collect(),
}
})?;

Ok(())
}
45 changes: 45 additions & 0 deletions crates/typst-test-cli/src/cli/config/get.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use typst_test_lib::config::ConfigError;

use super::{ConfigJson, UnknownConfigKeys};
use crate::cli::Context;
use crate::error::OperationFailure;

#[derive(clap::Args, Debug, Clone)]
#[group(id = "config-get-args")]
pub struct Args {
/// The key to get the value for
#[arg(num_args(1..), required = true)]
keys: Vec<String>,
}

pub fn run(ctx: &mut Context, args: &Args) -> anyhow::Result<()> {
let project = ctx.ensure_project()?;

let config = project.config();
let kvs: Vec<_> = args
.keys
.iter()
.map(|key| (key.as_str(), config.get(key)))
.collect();

let mut errors = vec![];
let mut vals = vec![];

for (k, res) in kvs {
match res {
Ok(v) => vals.push((k, v)),
Err(ConfigError::UnknownKey { .. }) => errors.push(k.to_owned()),
Err(err) => anyhow::bail!(err),
}
}

if !errors.is_empty() {
anyhow::bail!(OperationFailure::from(UnknownConfigKeys(errors)));
};

ctx.reporter.report(&ConfigJson::Pretty {
inner: vals.into_iter().collect(),
})?;

Ok(())
}
12 changes: 12 additions & 0 deletions crates/typst-test-cli/src/cli/config/list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use super::ConfigJson;
use crate::cli::Context;

pub fn run(ctx: &mut Context) -> anyhow::Result<()> {
let project = ctx.ensure_project()?;

ctx.reporter.report(&ConfigJson::Pretty {
inner: project.config().pairs().collect(),
})?;

Ok(())
}
71 changes: 71 additions & 0 deletions crates/typst-test-cli/src/cli/config/set.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use serde::Serialize;
use termcolor::{Color, WriteColor};

use crate::cli::config::UnknownConfigKeys;
use crate::cli::Context;
use crate::error::OperationFailure;
use crate::report::{Report, Verbosity};
use crate::ui;

#[derive(clap::Args, Debug, Clone)]
#[group(id = "config-set-args")]
pub struct Args {
/// The key to set
#[arg()]
key: String,

/// The value to set the key to or nothing to unset it
#[arg()]
value: Option<String>,
}

#[derive(Debug, Serialize)]
pub struct SetReport<'c> {
key: &'c str,
old: Option<&'c str>,
new: Option<&'c str>,
}

impl Report for SetReport<'_> {
fn report<W: WriteColor>(&self, mut writer: W, _verbosity: Verbosity) -> anyhow::Result<()> {
write!(writer, "Set ")?;
ui::write_colored(&mut writer, Color::Cyan, |w| write!(w, "{}", self.key))?;
write!(writer, " from ")?;
if let Some(old) = self.old {
ui::write_colored(&mut writer, Color::Green, |w| write!(w, "{:?}", old))?;
} else {
ui::write_colored(&mut writer, Color::Magenta, |w| write!(w, "null"))?;
}
write!(writer, " to ")?;
if let Some(new) = self.new {
ui::write_colored(&mut writer, Color::Green, |w| writeln!(w, "{:?}", new))?;
} else {
ui::write_colored(&mut writer, Color::Magenta, |w| writeln!(w, "null"))?;
}

Ok(())
}
}

pub fn run(ctx: &mut Context, args: &Args) -> anyhow::Result<()> {
let mut project = ctx.ensure_project()?;

// TODO: validation
let Ok(val) = project.config_mut().get_mut(&args.key) else {
anyhow::bail!(OperationFailure::from(UnknownConfigKeys(vec![args
.key
.clone()])));
};

let mut old = args.value.clone();
std::mem::swap(val, &mut old);

project.write_config()?;

ctx.reporter.report(&SetReport {
key: &args.key,
old: old.as_deref(),
new: args.value.as_deref(),
})?;
Ok(())
}
4 changes: 4 additions & 0 deletions crates/typst-test-cli/src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ impl Project {
&self.config
}

pub fn config_mut(&mut self) -> &mut Config {
&mut self.config
}

pub fn manifest(&self) -> Option<&Manifest> {
self.manifest.as_ref()
}
Expand Down

0 comments on commit c88eb2d

Please sign in to comment.