Skip to content

Commit 3801cbf

Browse files
committed
config: (de)serialization
1 parent 1c5515e commit 3801cbf

File tree

7 files changed

+113
-16
lines changed

7 files changed

+113
-16
lines changed

src/cli.rs

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ use clap::builder::PossibleValuesParser;
66
use clap::builder::TypedValueParser as _;
77
use clap::{value_parser, Args, Command, Parser, ValueHint};
88
use clap_complete::{generate, Generator, Shell};
9+
use merge::Merge;
910
use num_format::CustomFormat;
1011
use onefetch_image::ImageProtocol;
1112
use onefetch_manifest::ManifestType;
1213
use regex::Regex;
13-
use serde::Serialize;
14-
use merge::Merge;
14+
use serde::{de::Visitor, Serializer, Deserialize, Serialize};
1515
use std::env;
1616
use std::io;
1717
use std::path::PathBuf;
@@ -21,17 +21,19 @@ use strum::IntoEnumIterator;
2121
const COLOR_RESOLUTIONS: [&str; 5] = ["16", "32", "64", "128", "256"];
2222
pub const NO_BOTS_DEFAULT_REGEX_PATTERN: &str = r"(?:-|\s)[Bb]ot$|\[[Bb]ot\]";
2323

24-
#[derive(Clone, Debug, Parser, PartialEq, Eq, Merge)]
24+
#[derive(Clone, Debug, Parser, PartialEq, Eq, Merge, Serialize, Deserialize)]
2525
#[command(version, about)]
2626
pub struct CliOptions {
2727
/// Run as if onefetch was started in <input> instead of the current working directory
2828
#[arg(default_value = ".", hide_default_value = true, value_hint = ValueHint::DirPath)]
2929
#[merge(skip)]
30+
#[serde(skip)]
3031
pub input: PathBuf,
3132
/// Specify a custom path to a config file.
3233
/// Default config is located at ${HOME}/.config/onefetch/config.conf.
3334
#[arg(long, value_hint = ValueHint::AnyPath)]
3435
#[merge(skip)]
36+
#[serde(skip)]
3537
pub config_path: Option<PathBuf>,
3638
#[command(flatten)]
3739
pub info: InfoCliOptions,
@@ -49,7 +51,7 @@ pub struct CliOptions {
4951
pub other: OtherCliOptions,
5052
}
5153

52-
#[derive(Clone, Debug, Args, PartialEq, Eq, Merge)]
54+
#[derive(Clone, Debug, Args, PartialEq, Eq, Merge, Serialize, Deserialize)]
5355
#[command(next_help_heading = "INFO")]
5456
pub struct InfoCliOptions {
5557
/// Allows you to disable FIELD(s) from appearing in the output
@@ -132,7 +134,7 @@ pub struct InfoCliOptions {
132134
pub r#type: Vec<LanguageType>,
133135
}
134136

135-
#[derive(Clone, Debug, Args, PartialEq, Eq, Merge)]
137+
#[derive(Clone, Debug, Args, PartialEq, Eq, Merge, Serialize, Deserialize)]
136138
#[command(next_help_heading = "ASCII")]
137139
pub struct AsciiCliOptions {
138140
/// Takes a non-empty STRING as input to replace the ASCII logo
@@ -164,6 +166,7 @@ pub struct AsciiCliOptions {
164166
hide_possible_values = true
165167
)]
166168
#[merge(skip)]
169+
#[serde(skip)]
167170
pub ascii_language: Option<Language>,
168171
/// Specify when to use true color
169172
///
@@ -173,7 +176,7 @@ pub struct AsciiCliOptions {
173176
pub true_color: When,
174177
}
175178

176-
#[derive(Clone, Debug, Args, PartialEq, Eq, Merge)]
179+
#[derive(Clone, Debug, Args, PartialEq, Eq, Merge, Serialize, Deserialize)]
177180
#[command(next_help_heading = "IMAGE")]
178181
pub struct ImageCliOptions {
179182
/// Path to the IMAGE file
@@ -183,6 +186,7 @@ pub struct ImageCliOptions {
183186
/// Which image PROTOCOL to use
184187
#[arg(long, value_enum, requires = "image", value_name = "PROTOCOL")]
185188
#[merge(skip)]
189+
#[serde(skip)]
186190
pub image_protocol: Option<ImageProtocol>,
187191
/// VALUE of color resolution to use with SIXEL backend
188192
#[arg(
@@ -197,7 +201,7 @@ pub struct ImageCliOptions {
197201
pub color_resolution: usize,
198202
}
199203

200-
#[derive(Clone, Debug, Args, PartialEq, Eq, Merge)]
204+
#[derive(Clone, Debug, Args, PartialEq, Eq, Merge, Serialize, Deserialize)]
201205
#[command(next_help_heading = "TEXT FORMATTING")]
202206
pub struct TextForamttingCliOptions {
203207
/// Changes the text colors (X X X...)
@@ -229,7 +233,7 @@ pub struct TextForamttingCliOptions {
229233
#[merge(strategy = merge::bool::overwrite_false)]
230234
pub no_bold: bool,
231235
}
232-
#[derive(Clone, Debug, Args, PartialEq, Eq, Default, Merge)]
236+
#[derive(Clone, Debug, Args, PartialEq, Eq, Default, Merge, Serialize, Deserialize)]
233237
#[command(next_help_heading = "VISUALS")]
234238
pub struct VisualsCliOptions {
235239
/// Hides the color palette
@@ -248,29 +252,33 @@ pub struct VisualsCliOptions {
248252
pub nerd_fonts: bool,
249253
}
250254

251-
#[derive(Clone, Debug, Args, PartialEq, Eq, Default, Merge)]
255+
#[derive(Clone, Debug, Args, PartialEq, Eq, Default, Merge, Serialize, Deserialize)]
252256
#[command(next_help_heading = "DEVELOPER")]
253257
pub struct DeveloperCliOptions {
254258
/// Outputs Onefetch in a specific format
255259
#[arg(long, short, value_name = "FORMAT", value_enum)]
256260
#[merge(skip)]
261+
#[serde(skip)]
257262
pub output: Option<SerializationFormat>,
258263
/// If provided, outputs the completion file for given SHELL
259264
#[arg(long = "generate", value_name = "SHELL", value_enum)]
260265
#[merge(skip)]
266+
#[serde(skip)]
261267
pub completion: Option<Shell>,
262268
}
263269

264-
#[derive(Clone, Debug, Args, PartialEq, Eq, Default, Merge)]
270+
#[derive(Clone, Debug, Args, PartialEq, Eq, Default, Merge, Serialize, Deserialize)]
265271
#[command(next_help_heading = "OTHER")]
266272
pub struct OtherCliOptions {
267273
/// Prints out supported languages
268274
#[arg(long, short)]
269275
#[merge(skip)]
276+
#[serde(skip)]
270277
pub languages: bool,
271278
/// Prints out supported package managers
272279
#[arg(long, short)]
273280
#[merge(skip)]
281+
#[serde(skip)]
274282
pub package_managers: bool,
275283
}
276284

@@ -386,14 +394,14 @@ pub fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
386394
generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout());
387395
}
388396

389-
#[derive(clap::ValueEnum, Clone, PartialEq, Eq, Debug)]
397+
#[derive(clap::ValueEnum, Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
390398
pub enum When {
391399
Auto,
392400
Never,
393401
Always,
394402
}
395403

396-
#[derive(clap::ValueEnum, Clone, PartialEq, Eq, Debug, Serialize, Copy)]
404+
#[derive(clap::ValueEnum, Clone, PartialEq, Eq, Debug, Serialize, Deserialize, Copy)]
397405
pub enum NumberSeparator {
398406
Plain,
399407
Comma,
@@ -512,3 +520,37 @@ impl FromStr for MyRegex {
512520
Ok(MyRegex(Regex::new(s)?))
513521
}
514522
}
523+
524+
impl Serialize for MyRegex {
525+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
526+
where
527+
S: Serializer,
528+
{serializer.serialize_str(self.0.as_str())}
529+
}
530+
531+
pub struct RegVisitor;
532+
533+
impl <'de>Visitor<'de> for RegVisitor {
534+
type Value = MyRegex;
535+
536+
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
537+
formatter.write_str("regex")
538+
}
539+
540+
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
541+
where
542+
E: serde::de::Error, {
543+
match MyRegex::from_str(v) {
544+
Ok(regex) => Ok(regex),
545+
Err(error) => Err(serde::de::Error::custom(error))
546+
}
547+
}
548+
}
549+
550+
impl <'de>Deserialize<'de> for MyRegex {
551+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
552+
where
553+
D: serde::Deserializer<'de>, {
554+
deserializer.deserialize_str(RegVisitor)
555+
}
556+
}

src/config.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use crate::cli::CliOptions;
2+
use anyhow::{anyhow, Result};
3+
use dirs::config_dir;
4+
use merge::Merge;
5+
use std::{
6+
fs::{self, create_dir, File},
7+
io::{BufReader, Read},
8+
path::Path,
9+
};
10+
11+
pub fn read_cfg<P: AsRef<Path>>(path: &P) -> Result<CliOptions> {
12+
let file = File::open(&path)?;
13+
let mut buf_reader = BufReader::new(file);
14+
let mut contents = String::new();
15+
buf_reader.read_to_string(&mut contents)?;
16+
Ok(toml::from_str(contents.as_str()).unwrap())
17+
}
18+
19+
pub fn load_cfg<P: AsRef<Path>>(path: Option<&P>) -> Result<CliOptions> {
20+
match path {
21+
Some(config_path) => read_cfg(config_path),
22+
None => {
23+
let mut default_path = config_dir().unwrap();
24+
default_path.push("/onefetch/config.toml");
25+
if default_path.exists() {
26+
read_cfg(&default_path)
27+
} else {
28+
create_dir(&default_path)?;
29+
write_default(&default_path);
30+
Err(anyhow!(
31+
"Configuration file at {:?} does not exist!",
32+
default_path
33+
))
34+
}
35+
}
36+
}
37+
}
38+
39+
pub fn write_default<P: AsRef<Path>>(default: &P) {
40+
let toml = toml::to_string(&CliOptions::default()).expect("Config is not serializable!");
41+
fs::write(default, toml).expect("Could not write config!")
42+
}
43+
44+
pub fn populate_cfg(cfg: CliOptions) -> CliOptions {
45+
match load_cfg(cfg.config_path.as_ref()) {
46+
Ok(mut config) => {
47+
config.merge(cfg);
48+
config
49+
},
50+
Err(_) => cfg
51+
}
52+
}

src/info/langs/language.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use crate::info::utils::info_field::InfoField;
22
use owo_colors::OwoColorize;
3-
use serde::Serialize;
43
use tokei;
54

65
include!(concat!(env!("OUT_DIR"), "/language.rs"));

src/info/langs/language.tera

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use owo_colors::{
55
use std::fmt;
66
use std::fmt::Write;
77
use strum::EnumIter;
8+
use serde::{Serialize, Deserialize};
89

910
pub struct Colors {
1011
basic_colors: Vec<DynColors>,
@@ -13,7 +14,7 @@ pub struct Colors {
1314

1415
const DEFAULT_CHIP_ICON: char = '\u{25CF}';
1516

16-
#[derive(Clone, PartialEq, Eq, Debug, clap::ValueEnum)]
17+
#[derive(Clone, PartialEq, Eq, Debug, clap::ValueEnum, Deserialize, Serialize)]
1718
pub enum LanguageType {
1819
Programming,
1920
Markup,

src/info/utils/info_field.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::{info::utils::get_style, ui::text_colors::TextColors};
22
use owo_colors::OwoColorize;
3+
use serde::{Deserialize, Serialize};
34
use std::fmt;
45

56
#[typetag::serialize]
@@ -55,7 +56,7 @@ pub trait InfoField {
5556
}
5657
}
5758

58-
#[derive(Clone, clap::ValueEnum, Debug, Eq, PartialEq)]
59+
#[derive(Clone, clap::ValueEnum, Debug, Serialize, Deserialize, Eq, PartialEq)]
5960
pub enum InfoType {
6061
Project,
6162
Description,

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Lib is present to allow for benchmarks and integration tests
22
pub mod cli;
3+
pub mod config;
34
pub mod info;
45
pub mod ui;

src/main.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use anyhow::Result;
44
use clap::{CommandFactory, Parser};
55
use human_panic::setup_panic;
66
use onefetch::cli::{self, CliOptions};
7+
use onefetch::config;
78
use onefetch::info::build_info;
89
use onefetch::ui::printer::Printer;
910
use std::io;
@@ -14,7 +15,7 @@ fn main() -> Result<()> {
1415
#[cfg(windows)]
1516
enable_ansi_support::enable_ansi_support()?;
1617

17-
let cli_options = cli::CliOptions::parse();
18+
let cli_options = config::populate_cfg(cli::CliOptions::parse());
1819

1920
if cli_options.other.languages {
2021
return cli::print_supported_languages();

0 commit comments

Comments
 (0)