1use std::{
2 collections::HashMap,
3 path::{Path, PathBuf},
4};
5
6use clap::Parser;
7use clap_stdin::FileOrStdin;
8
9type ValueMap = HashMap<String, serde_json::Value>;
10
11#[derive(Parser, Debug)]
12#[command(version, about)]
13#[allow(clippy::struct_excessive_bools)] pub struct Args {
15 #[arg(required_unless_present_any = ["list_functions", "list_flavors", "list_accents"])]
17 pub template: Option<FileOrStdin>,
18
19 #[arg(long, short)]
21 pub flavor: Option<Flavor>,
22
23 #[arg(long, value_parser = json_map::<ColorOverrides>)]
25 pub color_overrides: Option<ColorOverrides>,
26
27 #[arg(long, value_parser = json_map::<ValueMap>)]
29 pub overrides: Option<ValueMap>,
30
31 #[arg(long, value_name = "EXAMPLE_PATH")]
37 pub check: Option<Option<PathBuf>>,
38
39 #[arg(long)]
41 pub dry_run: bool,
42
43 #[arg(long)]
45 pub list_functions: bool,
46
47 #[arg(long)]
49 pub list_flavors: bool,
50
51 #[arg(long)]
53 pub list_accents: bool,
54
55 #[arg(short, long, default_value = "json")]
57 pub output_format: OutputFormat,
58}
59
60#[derive(Debug, thiserror::Error)]
61enum Error {
62 #[error("Invalid JSON literal argument: {message}")]
63 InvalidJsonLiteralArg { message: String },
64
65 #[error("Invalid JSON file argument: {message}")]
66 InvalidJsonFileArg { message: String },
67
68 #[error("Failed to read file: {path}")]
69 ReadFile {
70 path: String,
71 #[source]
72 source: std::io::Error,
73 },
74}
75
76#[derive(Copy, Clone, Debug, clap::ValueEnum)]
77pub enum Flavor {
78 Latte,
79 Frappe,
80 Macchiato,
81 Mocha,
82}
83
84impl From<Flavor> for catppuccin::FlavorName {
85 fn from(val: Flavor) -> Self {
86 match val {
87 Flavor::Latte => Self::Latte,
88 Flavor::Frappe => Self::Frappe,
89 Flavor::Macchiato => Self::Macchiato,
90 Flavor::Mocha => Self::Mocha,
91 }
92 }
93}
94
95#[derive(Clone, Debug, serde::Deserialize)]
96pub struct ColorOverrides {
97 #[serde(default)]
98 pub all: HashMap<String, String>,
99 #[serde(default)]
100 pub latte: HashMap<String, String>,
101 #[serde(default)]
102 pub frappe: HashMap<String, String>,
103 #[serde(default)]
104 pub macchiato: HashMap<String, String>,
105 #[serde(default)]
106 pub mocha: HashMap<String, String>,
107}
108
109#[derive(Clone, Copy, Debug, clap::ValueEnum)]
110pub enum OutputFormat {
111 Json,
112 Yaml,
113 Markdown,
114 Plain,
115
116 #[clap(hide = true)]
118 MarkdownTable,
119}
120
121fn json_map<T>(s: &str) -> Result<T, Error>
122where
123 T: serde::de::DeserializeOwned,
124{
125 if Path::new(s).is_file() {
126 let s = std::fs::read_to_string(s).map_err(|e| Error::ReadFile {
127 path: s.to_string(),
128 source: e,
129 })?;
130 serde_json::from_str(&s).map_err(|e| Error::InvalidJsonFileArg {
131 message: e.to_string(),
132 })
133 } else {
134 serde_json::from_str(s).map_err(|e| Error::InvalidJsonLiteralArg {
135 message: e.to_string(),
136 })
137 }
138}