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-12-12 23:18:22 +00:00
use serde ::{ Deserialize , Deserializer } ;
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-12-12 23:18:22 +00:00
#[ derive(Debug, Clone) ]
2022-08-15 17:06:42 +01:00
pub enum MonitorConfig {
Single ( Config ) ,
Multiple ( Vec < Config > ) ,
}
2022-12-12 23:18:22 +00:00
// Manually implement for better untagged enum error handling:
// currently open pr: https://github.com/serde-rs/serde/pull/1544
impl < ' de > Deserialize < ' de > for MonitorConfig {
fn deserialize < D > ( deserializer : D ) -> std ::result ::Result < Self , D ::Error >
where
D : Deserializer < ' de > ,
{
let content =
< serde ::__private ::de ::Content as serde ::Deserialize > ::deserialize ( deserializer ) ? ;
match < Config as serde ::Deserialize > ::deserialize (
serde ::__private ::de ::ContentRefDeserializer ::< D ::Error > ::new ( & content ) ,
) {
Ok ( config ) = > Ok ( Self ::Single ( config ) ) ,
Err ( outer ) = > match < Vec < Config > as serde ::Deserialize > ::deserialize (
serde ::__private ::de ::ContentRefDeserializer ::< D ::Error > ::new ( & content ) ,
) {
Ok ( config ) = > Ok ( Self ::Multiple ( config ) ) ,
Err ( inner ) = > {
let report = Report ::msg ( format! ( " multi-bar (c): {inner} " ) . replace ( " An error occurred when deserializing: " , " " ) )
. wrap_err ( format! ( " single-bar (b): {outer} " ) . replace ( " An error occurred when deserializing: " , " " ) )
. wrap_err ( " An invalid config was found. The following errors were encountered: " )
. note ( " Both the single-bar (type b / error 1) and multi-bar (type c / error 2) config variants were tried. You can likely ignore whichever of these is not relevant to you. " )
. suggestion ( " Please see https://github.com/JakeStanger/ironbar/wiki/configuration-guide#2-pick-your-use-case for more info on the above " ) ;
Err ( serde ::de ::Error ::custom ( format! ( " {report:?} " ) ) )
}
} ,
}
}
}
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 {
2022-12-11 21:31:45 +00:00
/// Gets the orientation the bar and widgets should use
/// based on this position.
2022-10-15 16:27:25 +01:00
pub fn get_orientation ( self ) -> Orientation {
if self = = Self ::Top | | self = = Self ::Bottom {
Orientation ::Horizontal
} else {
Orientation ::Vertical
}
}
2022-12-11 21:31:45 +00:00
/// Gets the angle that label text should be displayed at
/// based on this position.
2022-10-15 16:27:25 +01:00
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-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
}