1use color_eyre::{eyre::Context, Result};
2use serde::Deserialize;
3use std::{
4 collections::HashMap,
5 ops::Deref,
6 path::{Path, PathBuf},
7};
8
9#[derive(Debug, Deserialize)]
10pub struct Cfgs(HashMap<String, Vec<DesiredOutput>>);
11
12#[derive(Debug, Deserialize)]
13struct Outputs {
14 pub outputs: Vec<DesiredOutput>,
15}
16
17impl Deref for Cfgs {
18 type Target = HashMap<String, Vec<DesiredOutput>>;
19
20 fn deref(&self) -> &Self::Target {
21 &self.0
22 }
23}
24
25#[derive(Debug, Deserialize, Clone)]
26pub struct DesiredOutput {
27 pub name: String,
28 pub scale: Option<f64>,
29}
30
31impl TryFrom<&toml_edit::Table> for Cfgs {
39 type Error = color_eyre::Report;
40
41 fn try_from(table: &toml_edit::Table) -> std::result::Result<Self, Self::Error> {
42 let cfg: Result<HashMap<String, Vec<DesiredOutput>>> = table
43 .into_iter()
44 .map(|(name, inner)| {
45 let output_str = inner
46 .as_table()
47 .map(|t| t.to_string())
48 .unwrap_or(inner.as_str().unwrap_or("").to_string());
49 let out: Outputs = toml_edit::de::from_str(&output_str).wrap_err_with(|| {
50 format!(
51 "Missing outputs in configuration {}: {}",
52 &name,
53 &inner.to_string(),
54 )
55 })?;
56 let name = name.to_string();
57 Ok((name, out.outputs))
58 })
59 .collect();
60 Ok(Cfgs(cfg?))
61 }
62}
63
64impl Cfgs {
65 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
66 let cfg_str = std::fs::read_to_string(&path)
67 .wrap_err_with(|| format!("Failed to read {}", path.as_ref().display()))?;
68 let cfgs_doc: toml_edit::Document = cfg_str
69 .parse()
70 .wrap_err("Failed to parse configurtion file")?;
71 let cfgs = cfgs_doc.as_table();
72 Self::try_from(cfgs)
73 }
74
75 pub fn find(&self, key: &str) -> Option<&Vec<DesiredOutput>> {
76 self.0.get(key)
77 }
78
79 pub fn default_path() -> PathBuf {
80 dirs::config_dir()
81 .unwrap_or("/etc/xdg/".into())
82 .join("oswo.toml")
83 }
84}
85
86impl std::fmt::Display for Cfgs {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 self.0.iter().try_fold((), |_, (name, setup)| {
89 let setup_str = setup
90 .iter()
91 .map(|o| format!("{}", o))
92 .collect::<Vec<_>>()
93 .join("\n ");
94 write!(f, "{}:\n {}\n", name, setup_str)
95 })
96 }
97}
98
99impl std::fmt::Display for DesiredOutput {
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101 write!(f, "{} (scale: {})", self.name, self.scale.unwrap_or(1.0))
102 }
103}