Skip to content

Commit

Permalink
refactor: use serde_yaml to update config and remove GetByKey trait
Browse files Browse the repository at this point in the history
  • Loading branch information
BugLight committed Mar 5, 2024
1 parent b69f896 commit d49417d
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 122 deletions.
70 changes: 56 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ either = "1.8"
clap = { version = "4.2.4", features = ["derive"] }
confy = { version = "0.5.1", default-features = false, features = ["yaml_conf"] }
serde = { version = "1.0", features = ["derive"] }
serde_yaml = { version = "0.9" }
reqwest = { version = "0.11.18", features = ["blocking", "multipart", "native-tls-vendored"] }

[dev-dependencies]
Expand Down
106 changes: 38 additions & 68 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use std::collections::HashMap;

use crate::paste::{debug, pastebin, Paste};
use anyhow::{Error, Result};
use either::Either;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
Expand All @@ -25,44 +24,6 @@ pub enum DestinationConfig {
Debug(debug::DebugConfig),
}

pub trait GetByKey {
fn get_by_key(&mut self, key: &str) -> Result<Either<&mut String, &mut dyn GetByKey>>;
}

// TODO: Make derive macro
impl GetByKey for PasterConfig {
fn get_by_key(&mut self, key: &str) -> Result<Either<&mut String, &mut dyn GetByKey>> {
if key == "default" {
Ok(Either::Left(&mut self.default))
} else if key == "dest" {
Ok(Either::Right(&mut self.dest))
} else {
Err(Error::msg("Key not found"))
}
}
}

impl<T> GetByKey for HashMap<String, T>
where
T: GetByKey,
{
fn get_by_key(&mut self, key: &str) -> Result<Either<&mut String, &mut dyn GetByKey>> {
self.get_mut(key)
.map(|x| Either::<&mut String, &mut dyn GetByKey>::Right(x))
.ok_or(Error::msg("Key not found"))
}
}

impl GetByKey for DestinationConfig {
fn get_by_key(&mut self, key: &str) -> Result<Either<&mut String, &mut dyn GetByKey>> {
match self {
// TODO: There must be some clever way to do it
DestinationConfig::Pastebin(x) => x.get_by_key(key),
DestinationConfig::Debug(x) => x.get_by_key(key),
}
}
}

impl From<DestinationConfig> for Box<dyn Paste> {
fn from(val: DestinationConfig) -> Self {
match val {
Expand Down Expand Up @@ -93,23 +54,25 @@ impl Default for PasterConfig {
}
}

pub fn update_config_value(config: &mut PasterConfig, key: &str, value: String) -> Result<()> {
update_value_by_key(config, key, value)
}

fn update_value_by_key(config: &mut dyn GetByKey, key: &str, value: String) -> Result<()> {
match key.split_once('.') {
Some((left, right)) => match config.get_by_key(left)? {
Either::Left(_) => Err(Error::msg("Expected nested structure but got plain")),
Either::Right(subconfig) => update_value_by_key(subconfig, right, value),
},
None => match config.get_by_key(key)? {
Either::Left(val) => {
*val = value;
Ok(())
pub fn update_config_value(config: &mut serde_yaml::Value, key: &str, value: String) -> Result<()> {
if let serde_yaml::Value::Mapping(map) = config {
if let Some((head, tail)) = key.split_once('.') {
let config = map
.get_mut(head)
.ok_or(Error::msg("Unknown key: ".to_string() + head))?;
update_config_value(config, tail, value)
} else {
match map.get_mut(key) {
Some(serde_yaml::Value::String(val)) => {
*val = value;
Ok(())
}
Some(_) => Err(Error::msg("Expected string")),
None => Err(Error::msg("Unknown key: ".to_string() + key)),
}
Either::Right(_) => Err(Error::msg("Expected plain value but got nested")),
},
}
} else {
Err(Error::msg("Expected mapping"))
}
}

Expand All @@ -119,16 +82,20 @@ mod tests {

#[test]
fn update_plain() -> Result<()> {
let mut config = PasterConfig::default();
update_value_by_key(&mut config, "default", String::from("test"))?;
let mut config = serde_yaml::to_value(PasterConfig::default())?;
update_config_value(&mut config, "default", String::from("test"))?;

let config: PasterConfig = serde_yaml::from_value(config)?;
assert_eq!(config.default, "test");
Ok(())
}

#[test]
fn update_nested() -> Result<()> {
let mut config = PasterConfig::default();
update_value_by_key(&mut config, "dest.pastebin.dev_key", String::from("test"))?;
let mut config = serde_yaml::to_value(PasterConfig::default())?;
update_config_value(&mut config, "dest.pastebin.dev_key", String::from("test"))?;

let config: PasterConfig = serde_yaml::from_value(config)?;
match config.dest.get("pastebin").unwrap() {
DestinationConfig::Pastebin(pastebin::PastebinConfig {
dev_key,
Expand All @@ -142,22 +109,25 @@ mod tests {
}

#[test]
fn update_unknown_key() {
let mut config = PasterConfig::default();
assert!(update_value_by_key(&mut config, "unknown", String::from("test")).is_err());
fn update_unknown_key() -> Result<()> {
let mut config = serde_yaml::to_value(PasterConfig::default())?;
assert!(update_config_value(&mut config, "unknown", String::from("test")).is_err());
Ok(())
}

#[test]
fn update_plain_as_nested() {
let mut config = PasterConfig::default();
fn update_plain_as_nested() -> Result<()> {
let mut config = serde_yaml::to_value(PasterConfig::default())?;
assert!(
update_value_by_key(&mut config, "default.something", String::from("test")).is_err()
update_config_value(&mut config, "default.something", String::from("test")).is_err()
);
Ok(())
}

#[test]
fn update_nested_as_plain() {
let mut config = PasterConfig::default();
assert!(update_value_by_key(&mut config, "dest", String::from("test")).is_err());
fn update_nested_as_plain() -> Result<()> {
let mut config = serde_yaml::to_value(PasterConfig::default())?;
assert!(update_config_value(&mut config, "dest", String::from("test")).is_err());
Ok(())
}
}
29 changes: 14 additions & 15 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,8 @@ enum Commands {
Config { key: String, value: String },
}

fn config_command(
mut config: PasterConfig,
config_path: &PathBuf,
key: &str,
value: String,
) -> Result<()> {
fn config_command(config_path: &PathBuf, key: &str, value: String) -> Result<()> {
let mut config: serde_yaml::Value = serde_yaml::from_reader(File::open(config_path)?)?;
paster::config::update_config_value(&mut config, key, value)
.with_context(|| "Update config failed")?;
confy::store_path(config_path, config).with_context(|| "Store config failed")?;
Expand All @@ -53,27 +49,30 @@ fn paster_command(config: PasterConfig, dest: Option<String>, file: Option<PathB

let reader = BufReader::new(input);

match config.dest.get(&dest) {
None => Err(Error::msg("Unknown destination name")),
Some(destination) => {
let paste: Box<dyn Paste> = destination.clone().into();
let url = paste.paste(Box::new(reader))?;
println!("{}", url);
Ok(())
}
if let Some(destination) = config.dest.get(&dest) {
let paste: Box<dyn Paste> = destination.clone().into();
let url = paste.paste(Box::new(reader))?;
println!("{}", url);
Ok(())
} else {
Err(Error::msg("Unknown destination name"))
}
}

fn main() -> Result<()> {
// 1. Parse CLI arguments.
let args = Cli::parse();

// 2. Load config (new config with defaults is created if none exists).
let config_path = args
.config
.unwrap_or(PathBuf::from("/etc/paster/config.yaml"));
let config: PasterConfig =
confy::load_path(config_path.clone()).with_context(|| "Load config failed")?;

// 3. Execute CLI command.
match args.command {
Some(Commands::Config { key, value }) => config_command(config, &config_path, &key, value),
Some(Commands::Config { key, value }) => config_command(&config_path, &key, value),
None => paster_command(config, args.dest, args.file),
}
}
Loading

0 comments on commit d49417d

Please sign in to comment.