sway_groups_config/
lib.rs1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6#[serde(default)]
7#[derive(Default)]
8pub struct SwaygConfig {
9 pub defaults: DefaultsConfig,
10 pub bar: BarConfig,
11 #[serde(default)]
15 pub assign: Vec<AssignRule>,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
20#[serde(rename_all = "lowercase")]
21pub enum MatchType {
22 #[default]
23 Exact,
24 Regex,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
29pub struct AssignRule {
30 #[serde(rename = "match")]
32 pub match_pattern: String,
33 #[serde(default)]
35 pub match_type: MatchType,
36 #[serde(default)]
39 pub groups: Vec<String>,
40 #[serde(default)]
42 pub global: bool,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
46#[serde(default)]
47pub struct DefaultsConfig {
48 pub default_group: String,
49 pub default_workspace: String,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
53#[serde(default)]
54pub struct BarConfig {
55 pub workspaces: BarSectionConfig,
56 pub groups: BarSectionConfig,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
60#[serde(default)]
61pub struct BarSectionConfig {
62 pub socket_instance: String,
63 pub display: BarDisplay,
64 pub show_global: bool,
65 pub show_empty: bool,
66}
67
68#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
69#[serde(rename_all = "lowercase")]
70pub enum BarDisplay {
71 All,
72 Active,
73 None,
74}
75
76
77impl Default for DefaultsConfig {
78 fn default() -> Self {
79 Self {
80 default_group: "0".to_string(),
81 default_workspace: "0".to_string(),
82 }
83 }
84}
85
86impl Default for BarConfig {
87 fn default() -> Self {
88 Self {
89 workspaces: BarSectionConfig {
90 socket_instance: "swayg_workspaces".to_string(),
91 display: BarDisplay::All,
92 show_global: true,
93 show_empty: true,
94 },
95 groups: BarSectionConfig {
96 socket_instance: "swayg_groups".to_string(),
97 display: BarDisplay::All,
98 show_global: true,
99 show_empty: true,
100 },
101 }
102 }
103}
104
105impl Default for BarSectionConfig {
106 fn default() -> Self {
107 Self {
108 socket_instance: String::new(),
109 display: BarDisplay::All,
110 show_global: true,
111 show_empty: true,
112 }
113 }
114}
115
116impl SwaygConfig {
117 pub fn matching_rules(&self, ws_name: &str) -> Vec<&AssignRule> {
119 self.assign
120 .iter()
121 .filter(|rule| match rule.match_type {
122 MatchType::Exact => rule.match_pattern == ws_name,
123 MatchType::Regex => regex::Regex::new(&rule.match_pattern)
124 .map(|re| re.is_match(ws_name))
125 .unwrap_or(false),
126 })
127 .collect()
128 }
129
130 pub fn config_path() -> Option<PathBuf> {
131 let dirs = directories::ProjectDirs::from("com", "swayg", "swayg")?;
132 Some(dirs.config_dir().join("config.toml"))
133 }
134
135 pub fn load() -> anyhow::Result<Self> {
136 if let Ok(env_path) = std::env::var("SWAYG_CONFIG") {
137 return Self::load_from(std::path::Path::new(&env_path));
138 }
139 let path = Self::config_path()
140 .ok_or_else(|| anyhow::anyhow!("Could not determine config directory"))?;
141 Self::load_from(&path)
142 }
143
144 pub fn load_from(path: &std::path::Path) -> anyhow::Result<Self> {
145 if !path.exists() {
146 return Ok(Self::default());
147 }
148 let content = std::fs::read_to_string(path)?;
149 let config: SwaygConfig = toml::from_str(&content)?;
150 Ok(config)
151 }
152
153 pub fn dump(&self) -> anyhow::Result<String> {
154 let mut output = String::new();
155 output.push_str("# swayg configuration\n");
156 output.push_str("# Place at: ~/.config/swayg/config.toml\n\n");
157 output.push_str(&toml::to_string_pretty(self)?);
158 Ok(output)
159 }
160
161 pub fn dump_to(&self, path: &std::path::Path) -> anyhow::Result<()> {
162 if let Some(parent) = path.parent() {
163 std::fs::create_dir_all(parent)?;
164 }
165 let content = self.dump()?;
166 std::fs::write(path, content)?;
167 Ok(())
168 }
169}