1use std::collections::HashMap;
2
3use crate::paste::{debug, pastebin, Paste};
4use anyhow::{Error, Result};
5use either::Either;
6use serde::{Deserialize, Serialize};
7
8#[derive(Serialize, Deserialize, Debug)]
9pub struct PasterConfig {
10 pub version: String,
12 pub default: String,
14 pub dest: HashMap<String, DestinationConfig>,
16}
17
18#[derive(Serialize, Deserialize, Clone, Debug)]
20#[serde(tag = "type")]
21pub enum DestinationConfig {
22 Pastebin(pastebin::PastebinConfig),
24 Debug(debug::DebugConfig),
26}
27
28pub trait GetByKey {
29 fn get_by_key(&mut self, key: &str) -> Result<Either<&mut String, &mut dyn GetByKey>>;
30}
31
32impl GetByKey for PasterConfig {
34 fn get_by_key(&mut self, key: &str) -> Result<Either<&mut String, &mut dyn GetByKey>> {
35 if key == "default" {
36 Ok(Either::Left(&mut self.default))
37 } else if key == "dest" {
38 Ok(Either::Right(&mut self.dest))
39 } else {
40 Err(Error::msg("Key not found"))
41 }
42 }
43}
44
45impl<T> GetByKey for HashMap<String, T>
46where
47 T: GetByKey,
48{
49 fn get_by_key(&mut self, key: &str) -> Result<Either<&mut String, &mut dyn GetByKey>> {
50 self.get_mut(key)
51 .map(|x| Either::<&mut String, &mut dyn GetByKey>::Right(x))
52 .ok_or(Error::msg("Key not found"))
53 }
54}
55
56impl GetByKey for DestinationConfig {
57 fn get_by_key(&mut self, key: &str) -> Result<Either<&mut String, &mut dyn GetByKey>> {
58 match self {
59 DestinationConfig::Pastebin(x) => x.get_by_key(key),
61 DestinationConfig::Debug(x) => x.get_by_key(key),
62 }
63 }
64}
65
66impl From<DestinationConfig> for Box<dyn Paste> {
67 fn from(val: DestinationConfig) -> Self {
68 match val {
69 DestinationConfig::Pastebin(x) => x.into(),
71 DestinationConfig::Debug(x) => x.into(),
72 }
73 }
74}
75
76impl Default for PasterConfig {
77 fn default() -> Self {
78 let default = String::from("pastebin");
79 let mut destinations = HashMap::new();
80 destinations.insert(
81 default.clone(),
82 DestinationConfig::Pastebin(pastebin::PastebinConfig {
83 dev_key: String::from("<your dev API key>"),
84 user_key: None,
85 }),
86 );
87
88 PasterConfig {
89 version: String::from("1"),
90 default,
91 dest: destinations,
92 }
93 }
94}
95
96pub fn update_config_value(config: &mut PasterConfig, key: &str, value: String) -> Result<()> {
97 update_value_by_key(config, key, value)
98}
99
100fn update_value_by_key(config: &mut dyn GetByKey, key: &str, value: String) -> Result<()> {
101 match key.split_once('.') {
102 Some((left, right)) => match config.get_by_key(left)? {
103 Either::Left(_) => Err(Error::msg("Expected nested structure but got plain")),
104 Either::Right(subconfig) => update_value_by_key(subconfig, right, value),
105 },
106 None => match config.get_by_key(key)? {
107 Either::Left(val) => {
108 *val = value;
109 Ok(())
110 }
111 Either::Right(_) => Err(Error::msg("Expected plain value but got nested")),
112 },
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn update_plain() -> Result<()> {
122 let mut config = PasterConfig::default();
123 update_value_by_key(&mut config, "default", String::from("test"))?;
124 assert_eq!(config.default, "test");
125 Ok(())
126 }
127
128 #[test]
129 fn update_nested() -> Result<()> {
130 let mut config = PasterConfig::default();
131 update_value_by_key(&mut config, "dest.pastebin.dev_key", String::from("test"))?;
132 match config.dest.get("pastebin").unwrap() {
133 DestinationConfig::Pastebin(pastebin::PastebinConfig {
134 dev_key,
135 user_key: _,
136 }) => {
137 assert_eq!(dev_key, "test");
138 }
139 DestinationConfig::Debug(_) => panic!(),
140 }
141 Ok(())
142 }
143
144 #[test]
145 fn update_unknown_key() {
146 let mut config = PasterConfig::default();
147 assert!(update_value_by_key(&mut config, "unknown", String::from("test")).is_err());
148 }
149
150 #[test]
151 fn update_plain_as_nested() {
152 let mut config = PasterConfig::default();
153 assert!(
154 update_value_by_key(&mut config, "default.something", String::from("test")).is_err()
155 );
156 }
157
158 #[test]
159 fn update_nested_as_plain() {
160 let mut config = PasterConfig::default();
161 assert!(update_value_by_key(&mut config, "dest", String::from("test")).is_err());
162 }
163}