liboswo/
cfg.rs

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
31// TODO: allow name + scale and name only
32// #[derive(Debug, Deserialize)]
33// enum OutputVariants {
34//     Full(DesiredOutput),
35//     Name(String),
36// }
37
38impl 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}