2022-08-14 14:30:13 +01:00
|
|
|
use crate::modules::clock::ClockModule;
|
2022-10-16 22:16:48 +01:00
|
|
|
use crate::modules::custom::CustomModule;
|
2022-08-14 20:40:11 +01:00
|
|
|
use crate::modules::focused::FocusedModule;
|
2022-08-14 14:30:13 +01:00
|
|
|
use crate::modules::launcher::LauncherModule;
|
|
|
|
use crate::modules::mpd::MpdModule;
|
|
|
|
use crate::modules::script::ScriptModule;
|
|
|
|
use crate::modules::sysinfo::SysInfoModule;
|
|
|
|
use crate::modules::tray::TrayModule;
|
|
|
|
use crate::modules::workspaces::WorkspacesModule;
|
2022-11-28 21:55:08 +00:00
|
|
|
use crate::script::ScriptInput;
|
2022-08-21 23:36:07 +01:00
|
|
|
use color_eyre::eyre::{Context, ContextCompat};
|
|
|
|
use color_eyre::{eyre, Help, Report};
|
2022-08-14 14:30:13 +01:00
|
|
|
use dirs::config_dir;
|
2022-08-21 23:36:07 +01:00
|
|
|
use eyre::Result;
|
2022-10-15 16:27:25 +01:00
|
|
|
use gtk::Orientation;
|
2022-08-14 14:30:13 +01:00
|
|
|
use serde::Deserialize;
|
2022-08-15 17:06:42 +01:00
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use std::{env, fs};
|
2022-10-16 12:58:11 +01:00
|
|
|
use tracing::instrument;
|
2022-08-14 14:30:13 +01:00
|
|
|
|
2022-11-28 21:55:08 +00:00
|
|
|
#[derive(Debug, Deserialize, Clone)]
|
|
|
|
pub struct CommonConfig {
|
|
|
|
pub show_if: Option<ScriptInput>,
|
|
|
|
pub on_click: Option<ScriptInput>,
|
|
|
|
pub tooltip: Option<String>,
|
|
|
|
}
|
|
|
|
|
2022-08-14 14:30:13 +01:00
|
|
|
#[derive(Debug, Deserialize, Clone)]
|
2022-11-28 22:27:31 +00:00
|
|
|
#[serde(tag = "type", rename_all = "snake_case")]
|
2022-08-14 14:30:13 +01:00
|
|
|
pub enum ModuleConfig {
|
|
|
|
Clock(ClockModule),
|
|
|
|
Mpd(MpdModule),
|
|
|
|
Tray(TrayModule),
|
|
|
|
Workspaces(WorkspacesModule),
|
|
|
|
SysInfo(SysInfoModule),
|
|
|
|
Launcher(LauncherModule),
|
|
|
|
Script(ScriptModule),
|
2022-08-14 20:40:11 +01:00
|
|
|
Focused(FocusedModule),
|
2022-10-16 22:16:48 +01:00
|
|
|
Custom(CustomModule),
|
2022-08-14 14:30:13 +01:00
|
|
|
}
|
|
|
|
|
2022-08-15 17:06:42 +01:00
|
|
|
#[derive(Debug, Deserialize, Clone)]
|
|
|
|
#[serde(untagged)]
|
|
|
|
pub enum MonitorConfig {
|
|
|
|
Single(Config),
|
|
|
|
Multiple(Vec<Config>),
|
|
|
|
}
|
|
|
|
|
2022-10-15 16:27:25 +01:00
|
|
|
#[derive(Debug, Deserialize, Copy, Clone, PartialEq, Eq)]
|
2022-11-28 22:27:31 +00:00
|
|
|
#[serde(rename_all = "snake_case")]
|
2022-08-14 15:56:21 +01:00
|
|
|
pub enum BarPosition {
|
|
|
|
Top,
|
2022-08-14 20:40:11 +01:00
|
|
|
Bottom,
|
2022-10-15 16:27:25 +01:00
|
|
|
Left,
|
|
|
|
Right,
|
2022-08-14 15:56:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for BarPosition {
|
|
|
|
fn default() -> Self {
|
2022-08-15 21:11:17 +01:00
|
|
|
Self::Bottom
|
2022-08-14 15:56:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-15 16:27:25 +01:00
|
|
|
impl BarPosition {
|
|
|
|
pub fn get_orientation(self) -> Orientation {
|
|
|
|
if self == Self::Top || self == Self::Bottom {
|
|
|
|
Orientation::Horizontal
|
|
|
|
} else {
|
|
|
|
Orientation::Vertical
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const fn get_angle(self) -> f64 {
|
|
|
|
match self {
|
|
|
|
Self::Top | Self::Bottom => 0.0,
|
|
|
|
Self::Left => 90.0,
|
|
|
|
Self::Right => 270.0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-16 12:58:11 +01:00
|
|
|
#[derive(Debug, Deserialize, Clone)]
|
2022-08-14 14:30:13 +01:00
|
|
|
pub struct Config {
|
2022-08-14 15:56:21 +01:00
|
|
|
#[serde(default = "default_bar_position")]
|
|
|
|
pub position: BarPosition,
|
2022-10-15 16:27:25 +01:00
|
|
|
#[serde(default = "default_true")]
|
|
|
|
pub anchor_to_edges: bool,
|
2022-08-14 20:41:38 +01:00
|
|
|
#[serde(default = "default_bar_height")]
|
|
|
|
pub height: i32,
|
2022-08-14 15:56:21 +01:00
|
|
|
|
2022-10-15 16:27:25 +01:00
|
|
|
pub start: Option<Vec<ModuleConfig>>,
|
2022-08-14 14:30:13 +01:00
|
|
|
pub center: Option<Vec<ModuleConfig>>,
|
2022-10-15 16:27:25 +01:00
|
|
|
pub end: Option<Vec<ModuleConfig>>,
|
2022-08-14 14:30:13 +01:00
|
|
|
|
2022-08-15 17:06:42 +01:00
|
|
|
pub monitors: Option<HashMap<String, MonitorConfig>>,
|
2022-08-14 14:30:13 +01:00
|
|
|
}
|
|
|
|
|
2022-08-14 15:56:21 +01:00
|
|
|
const fn default_bar_position() -> BarPosition {
|
|
|
|
BarPosition::Bottom
|
|
|
|
}
|
|
|
|
|
2022-08-14 20:41:38 +01:00
|
|
|
const fn default_bar_height() -> i32 {
|
|
|
|
42
|
|
|
|
}
|
|
|
|
|
2022-08-14 14:30:13 +01:00
|
|
|
impl Config {
|
2022-08-28 16:57:41 +01:00
|
|
|
/// Attempts to load the config file from file,
|
|
|
|
/// parse it and return a new instance of `Self`.
|
2022-10-16 12:58:11 +01:00
|
|
|
#[instrument]
|
2022-08-21 23:36:07 +01:00
|
|
|
pub fn load() -> Result<Self> {
|
2022-11-06 22:53:48 +00:00
|
|
|
let config_path = env::var("IRONBAR_CONFIG").map_or_else(
|
|
|
|
|_| Self::try_find_config(),
|
|
|
|
|config_path| {
|
|
|
|
let path = PathBuf::from(config_path);
|
|
|
|
if path.exists() {
|
|
|
|
Ok(path)
|
|
|
|
} else {
|
|
|
|
Err(Report::msg(format!(
|
|
|
|
"Specified config file does not exist: {}",
|
|
|
|
path.display()
|
|
|
|
))
|
|
|
|
.note("Config file was specified using `IRONBAR_CONFIG` environment variable"))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)?;
|
2022-08-14 14:30:13 +01:00
|
|
|
|
2022-08-21 23:36:07 +01:00
|
|
|
Self::load_file(&config_path)
|
|
|
|
}
|
|
|
|
|
2022-08-28 16:57:41 +01:00
|
|
|
/// Attempts to discover the location of the config file
|
|
|
|
/// by checking each valid format's extension.
|
|
|
|
///
|
|
|
|
/// Returns the path of the first valid match, if any.
|
2022-10-16 12:58:11 +01:00
|
|
|
#[instrument]
|
2022-08-21 23:36:07 +01:00
|
|
|
fn try_find_config() -> Result<PathBuf> {
|
|
|
|
let config_dir = config_dir().wrap_err("Failed to locate user config dir")?;
|
|
|
|
|
|
|
|
let extensions = vec!["json", "toml", "yaml", "yml", "corn"];
|
2022-08-14 14:30:13 +01:00
|
|
|
|
2022-08-21 23:36:07 +01:00
|
|
|
let file = extensions.into_iter().find_map(|extension| {
|
|
|
|
let full_path = config_dir
|
|
|
|
.join("ironbar")
|
|
|
|
.join(format!("config.{extension}"));
|
2022-08-15 17:06:42 +01:00
|
|
|
|
2022-08-21 23:36:07 +01:00
|
|
|
if Path::exists(&full_path) {
|
|
|
|
Some(full_path)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2022-11-06 22:53:48 +00:00
|
|
|
file.map_or_else(
|
|
|
|
|| {
|
|
|
|
Err(Report::msg("Could not find config file")
|
|
|
|
.suggestion("Ironbar does not include a configuration out of the box")
|
|
|
|
.suggestion("A guide on writing a config can be found on the wiki:")
|
|
|
|
.suggestion("https://github.com/JakeStanger/ironbar/wiki/configuration-guide"))
|
|
|
|
},
|
|
|
|
Ok,
|
|
|
|
)
|
2022-08-15 17:06:42 +01:00
|
|
|
}
|
2022-08-14 14:30:13 +01:00
|
|
|
|
2022-08-28 16:57:41 +01:00
|
|
|
/// Loads the config file at the specified path
|
|
|
|
/// and parses it into `Self` based on its extension.
|
2022-08-21 23:36:07 +01:00
|
|
|
fn load_file(path: &Path) -> Result<Self> {
|
|
|
|
let file = fs::read(path).wrap_err("Failed to read config file")?;
|
|
|
|
let extension = path
|
|
|
|
.extension()
|
|
|
|
.unwrap_or_default()
|
|
|
|
.to_str()
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
match extension {
|
|
|
|
"json" => serde_json::from_slice(&file).wrap_err("Invalid JSON config"),
|
|
|
|
"toml" => toml::from_slice(&file).wrap_err("Invalid TOML config"),
|
|
|
|
"yaml" | "yml" => serde_yaml::from_slice(&file).wrap_err("Invalid YAML config"),
|
2022-11-28 22:23:11 +00:00
|
|
|
"corn" => libcorn::from_slice(&file).wrap_err("Invalid Corn config"),
|
2022-08-21 23:36:07 +01:00
|
|
|
_ => unreachable!(),
|
2022-08-15 17:06:42 +01:00
|
|
|
}
|
2022-08-14 14:30:13 +01:00
|
|
|
}
|
|
|
|
}
|
2022-08-14 20:40:11 +01:00
|
|
|
|
|
|
|
pub const fn default_false() -> bool {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
pub const fn default_true() -> bool {
|
|
|
|
true
|
|
|
|
}
|