add substitute, foreach, sprintf. improve errors a bit
This commit is contained in:
parent
4036ed42f0
commit
22925fc738
|
@ -3,7 +3,7 @@ use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
convert::Infallible,
|
convert::Infallible,
|
||||||
env,
|
env,
|
||||||
fmt::Display,
|
fmt::{Debug, Display},
|
||||||
fs::{self},
|
fs::{self},
|
||||||
io,
|
io,
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
|
@ -33,6 +33,7 @@ use crate::{
|
||||||
window::Window,
|
window::Window,
|
||||||
Align, Client, Direction, Index, Operator, Separator, StringParseError, ToggleBool,
|
Align, Client, Direction, Index, Operator, Separator, StringParseError, ToggleBool,
|
||||||
},
|
},
|
||||||
|
logerr::UnwrapLog,
|
||||||
rule, window_types,
|
rule, window_types,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -52,8 +53,8 @@ pub enum ConfigError {
|
||||||
CommandError(#[from] CommandError),
|
CommandError(#[from] CommandError),
|
||||||
#[error("non-utf8 string error: {0}")]
|
#[error("non-utf8 string error: {0}")]
|
||||||
Utf8StringError(#[from] FromUtf8Error),
|
Utf8StringError(#[from] FromUtf8Error),
|
||||||
#[error("failed parsing keybind: {0}")]
|
#[error("failed parsing keybind [{0}]: [{1}]")]
|
||||||
KeyParseError(#[from] KeyParseError),
|
KeyParseError(String, KeyParseError),
|
||||||
#[error("failed parsing value from string: {0}")]
|
#[error("failed parsing value from string: {0}")]
|
||||||
StringParseError(#[from] StringParseError),
|
StringParseError(#[from] StringParseError),
|
||||||
}
|
}
|
||||||
|
@ -387,9 +388,12 @@ impl Config {
|
||||||
name: &str,
|
name: &str,
|
||||||
f: F,
|
f: F,
|
||||||
default: T,
|
default: T,
|
||||||
) -> T {
|
) -> T
|
||||||
|
where
|
||||||
|
T: Debug,
|
||||||
|
{
|
||||||
match Client::new().get_attr(name.to_string()) {
|
match Client::new().get_attr(name.to_string()) {
|
||||||
Ok(setting) => f(&setting.to_string()).unwrap_or(default),
|
Ok(setting) => f(&setting.to_string()).unwrap_or_log(default),
|
||||||
Err(_) => default,
|
Err(_) => default,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -401,11 +405,11 @@ impl Config {
|
||||||
|f| -> Result<_, Infallible> { Ok(f.to_string()) },
|
|f| -> Result<_, Infallible> { Ok(f.to_string()) },
|
||||||
default.font,
|
default.font,
|
||||||
),
|
),
|
||||||
mod_key: setting(Self::MOD_KEY, Key::from_str, default.mod_key),
|
|
||||||
font_bold: client
|
font_bold: client
|
||||||
.get_attr(ThemeAttr::TitleFont(String::new()).attr_path())
|
.get_attr(ThemeAttr::TitleFont(String::new()).attr_path())
|
||||||
.map(|a| a.to_string())
|
.map(|a| a.to_string())
|
||||||
.unwrap_or(default.font_bold),
|
.unwrap_or_log(default.font_bold),
|
||||||
|
mod_key: setting(Self::MOD_KEY, Key::from_str, default.mod_key),
|
||||||
services: setting(
|
services: setting(
|
||||||
Self::SERVICES,
|
Self::SERVICES,
|
||||||
|v| serde_json::de::from_str(v),
|
|v| serde_json::de::from_str(v),
|
||||||
|
@ -425,14 +429,14 @@ impl Config {
|
||||||
&client
|
&client
|
||||||
.get_attr(attr_path.clone())
|
.get_attr(attr_path.clone())
|
||||||
.map(|t| t.to_string())
|
.map(|t| t.to_string())
|
||||||
.unwrap_or(default.to_string()),
|
.unwrap_or_log(default.to_string()),
|
||||||
)
|
)
|
||||||
.unwrap_or(default.clone())
|
.unwrap_or_log(default.clone())
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
})(),
|
})(),
|
||||||
},
|
},
|
||||||
keybinds: Self::active_keybinds(true).unwrap_or(default.keybinds),
|
keybinds: Self::active_keybinds(true).unwrap_or_log(default.keybinds),
|
||||||
tags: (|| -> Result<Vec<Tag>, _> {
|
tags: (|| -> Result<Vec<Tag>, _> {
|
||||||
Result::<_, ConfigError>::Ok({
|
Result::<_, ConfigError>::Ok({
|
||||||
let mut tags = client
|
let mut tags = client
|
||||||
|
@ -451,7 +455,7 @@ impl Config {
|
||||||
tags
|
tags
|
||||||
})
|
})
|
||||||
})()
|
})()
|
||||||
.unwrap_or(default.tags),
|
.unwrap_or_log(default.tags),
|
||||||
rules: (|| -> Result<Vec<Rule>, ConfigError> {
|
rules: (|| -> Result<Vec<Rule>, ConfigError> {
|
||||||
Ok(
|
Ok(
|
||||||
String::from_utf8(client.execute(HlwmCommand::ListRules)?.stdout)?
|
String::from_utf8(client.execute(HlwmCommand::ListRules)?.stdout)?
|
||||||
|
@ -462,7 +466,7 @@ impl Config {
|
||||||
.collect::<Result<_, _>>()?,
|
.collect::<Result<_, _>>()?,
|
||||||
)
|
)
|
||||||
})()
|
})()
|
||||||
.unwrap_or(default.rules),
|
.unwrap_or_log(default.rules),
|
||||||
settings: (|| -> Result<Vec<_>, CommandError> {
|
settings: (|| -> Result<Vec<_>, CommandError> {
|
||||||
default
|
default
|
||||||
.settings
|
.settings
|
||||||
|
@ -471,7 +475,7 @@ impl Config {
|
||||||
.map(|s| Ok(client.get_setting(s.into())?))
|
.map(|s| Ok(client.get_setting(s.into())?))
|
||||||
.collect::<Result<Vec<_>, CommandError>>()
|
.collect::<Result<Vec<_>, CommandError>>()
|
||||||
})()
|
})()
|
||||||
.unwrap_or(default.settings),
|
.unwrap_or_log(default.settings),
|
||||||
..default
|
..default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -501,7 +505,10 @@ impl Config {
|
||||||
None => false,
|
None => false,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map(|row: &str| Keybind::from_str(row).map_err(|err| err.into()))
|
.map(|row: &str| {
|
||||||
|
Keybind::from_str(row)
|
||||||
|
.map_err(|err| ConfigError::KeyParseError(row.to_string(), err))
|
||||||
|
})
|
||||||
.collect::<Result<Vec<_>, ConfigError>>()
|
.collect::<Result<Vec<_>, ConfigError>>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -602,7 +609,7 @@ impl Inclusive<10> {
|
||||||
|
|
||||||
impl<const MAX: u8> Display for Inclusive<MAX> {
|
impl<const MAX: u8> Display for Inclusive<MAX> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
self.0.fmt(f)
|
write!(f, "{}", self.0.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ use super::{
|
||||||
StringParseError,
|
StringParseError,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Clone, Error)]
|
||||||
pub enum AttributeError {
|
pub enum AttributeError {
|
||||||
#[error("error parsing integer value: {0:?}")]
|
#[error("error parsing integer value: {0:?}")]
|
||||||
ParseIntError(#[from] ParseIntError),
|
ParseIntError(#[from] ParseIntError),
|
||||||
|
@ -60,7 +60,7 @@ impl AttributeOption {
|
||||||
"color" => Ok(Self::Color(None)),
|
"color" => Ok(Self::Color(None)),
|
||||||
"windowid" => Ok(Self::WindowID(None)),
|
"windowid" => Ok(Self::WindowID(None)),
|
||||||
"rectangle" => Ok(Self::Rectangle(None)),
|
"rectangle" => Ok(Self::Rectangle(None)),
|
||||||
"string" | "names" | "regex" => Ok(Self::String(None)),
|
"string" | "names" | "regex" | "font" => Ok(Self::String(None)),
|
||||||
_ => Err(AttributeError::UnknownType(type_string.to_string())),
|
_ => Err(AttributeError::UnknownType(type_string.to_string())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,7 +179,9 @@ impl Attribute {
|
||||||
},
|
},
|
||||||
"color" => Ok(Attribute::Color(Color::from_str(value_string)?)),
|
"color" => Ok(Attribute::Color(Color::from_str(value_string)?)),
|
||||||
"int" => Ok(Attribute::Int(value_string.parse()?)),
|
"int" => Ok(Attribute::Int(value_string.parse()?)),
|
||||||
"string" | "names" | "regex" => Ok(Attribute::String(value_string.to_string())),
|
"string" | "names" | "regex" | "font" => {
|
||||||
|
Ok(Attribute::String(value_string.to_string()))
|
||||||
|
}
|
||||||
"uint" => Ok(Attribute::Uint(value_string.parse()?)),
|
"uint" => Ok(Attribute::Uint(value_string.parse()?)),
|
||||||
"rectangle" => {
|
"rectangle" => {
|
||||||
let parts = value_string.split('x').collect::<Vec<_>>();
|
let parts = value_string.split('x').collect::<Vec<_>>();
|
||||||
|
|
|
@ -95,7 +95,14 @@ pub enum HlwmCommand {
|
||||||
Get(SettingName),
|
Get(SettingName),
|
||||||
/// Emits a custom `hook` to all idling herbstclients
|
/// Emits a custom `hook` to all idling herbstclients
|
||||||
EmitHook(Hook),
|
EmitHook(Hook),
|
||||||
Substitute,
|
/// Replaces all exact occurrences of `identifier` in `command` and its `args` by the value of the `attribute`.
|
||||||
|
/// Note that the `command` also is replaced by the attribute value if it equals `identifier`.
|
||||||
|
/// The replaced command with its arguments then is executed
|
||||||
|
Substitute {
|
||||||
|
identifier: String,
|
||||||
|
attribute_path: String,
|
||||||
|
command: Box<HlwmCommand>,
|
||||||
|
},
|
||||||
Keybind(Keybind),
|
Keybind(Keybind),
|
||||||
Keyunbind(KeyUnbind),
|
Keyunbind(KeyUnbind),
|
||||||
Mousebind(Mousebind),
|
Mousebind(Mousebind),
|
||||||
|
@ -178,6 +185,20 @@ pub enum HlwmCommand {
|
||||||
monitor: Monitor,
|
monitor: Monitor,
|
||||||
pad: Pad,
|
pad: Pad,
|
||||||
},
|
},
|
||||||
|
/// For each child of the given `object` the `command` is called with its `args`, where the `identifier` is replaced by the path of the child
|
||||||
|
#[strum(serialize = "foreach")]
|
||||||
|
ForEach {
|
||||||
|
/// do not print duplicates (some objects can be reached via multiple paths, such as `clients.focus`)
|
||||||
|
unique: bool,
|
||||||
|
/// print `object` and all its children of arbitrary depth in breadth-first search order. This implicitly activates `unique`
|
||||||
|
recursive: bool,
|
||||||
|
/// consider children whose name match the specified REGEX
|
||||||
|
filter_name: Option<String>,
|
||||||
|
identifier: String,
|
||||||
|
object: String,
|
||||||
|
},
|
||||||
|
#[strum(serialize = "sprintf")]
|
||||||
|
Sprintf(Vec<String>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Box<HlwmCommand> {
|
impl FromStr for Box<HlwmCommand> {
|
||||||
|
@ -310,7 +331,6 @@ impl HlwmCommand {
|
||||||
| HlwmCommand::ListRules
|
| HlwmCommand::ListRules
|
||||||
| HlwmCommand::False
|
| HlwmCommand::False
|
||||||
| HlwmCommand::True
|
| HlwmCommand::True
|
||||||
| HlwmCommand::Substitute
|
|
||||||
| HlwmCommand::Mouseunbind
|
| HlwmCommand::Mouseunbind
|
||||||
| HlwmCommand::ListMonitors
|
| HlwmCommand::ListMonitors
|
||||||
| HlwmCommand::ListKeybinds => Ok::<_, CommandParseError>(command.clone()),
|
| HlwmCommand::ListKeybinds => Ok::<_, CommandParseError>(command.clone()),
|
||||||
|
@ -504,6 +524,55 @@ impl HlwmCommand {
|
||||||
HlwmCommand::Pad { monitor: _, pad: _ } => {
|
HlwmCommand::Pad { monitor: _, pad: _ } => {
|
||||||
parse!(monitor: FromStr, pad: FromStrAll => Pad)
|
parse!(monitor: FromStr, pad: FromStrAll => Pad)
|
||||||
}
|
}
|
||||||
|
HlwmCommand::Substitute {
|
||||||
|
identifier: _,
|
||||||
|
attribute_path: _,
|
||||||
|
command: _,
|
||||||
|
} => {
|
||||||
|
parse!(identifier: String, attribute_path: String, command: FromStrAll => Substitute)
|
||||||
|
}
|
||||||
|
HlwmCommand::ForEach {
|
||||||
|
unique: _,
|
||||||
|
recursive: _,
|
||||||
|
filter_name: _,
|
||||||
|
identifier: _,
|
||||||
|
object: _,
|
||||||
|
} => {
|
||||||
|
let (params, args): (Vec<String>, Vec<String>) =
|
||||||
|
args.into_iter().partition(|a| a.starts_with("--"));
|
||||||
|
|
||||||
|
if args.len() < 2 {
|
||||||
|
return Err(CommandParseError::BadArgument {
|
||||||
|
command: command.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let mut args = args.into_iter();
|
||||||
|
let (identifier, object) =
|
||||||
|
(args.next().unwrap(), args.collect::<Vec<_>>().join("\t"));
|
||||||
|
let mut unique = false;
|
||||||
|
let mut recursive = false;
|
||||||
|
let mut filter_name: Option<String> = None;
|
||||||
|
for param in params {
|
||||||
|
match param.as_str() {
|
||||||
|
"--unique" => unique = true,
|
||||||
|
"--recursive" => recursive = true,
|
||||||
|
other => {
|
||||||
|
if let Some(name) = other.strip_prefix("--filter-name=") {
|
||||||
|
filter_name = Some(name.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(HlwmCommand::ForEach {
|
||||||
|
unique,
|
||||||
|
recursive,
|
||||||
|
filter_name,
|
||||||
|
identifier,
|
||||||
|
object,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
HlwmCommand::Sprintf(_) => parse!([Vec<String>] => Sprintf),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
assert_eq!(command.to_string(), parsed_command.to_string());
|
assert_eq!(command.to_string(), parsed_command.to_string());
|
||||||
|
@ -528,10 +597,10 @@ impl HlwmCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Clone, Error)]
|
||||||
pub enum CommandError {
|
pub enum CommandError {
|
||||||
#[error("IO error")]
|
#[error("IO error")]
|
||||||
IoError(#[from] io::Error),
|
IoError(String),
|
||||||
#[error("exited with status code {0}")]
|
#[error("exited with status code {0}")]
|
||||||
StatusCode(i32, Option<String>),
|
StatusCode(i32, Option<String>),
|
||||||
#[error("killed by signal ({signal}); core dumped: {core_dumped}")]
|
#[error("killed by signal ({signal}); core dumped: {core_dumped}")]
|
||||||
|
@ -550,6 +619,12 @@ pub enum CommandError {
|
||||||
Empty,
|
Empty,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for CommandError {
|
||||||
|
fn from(value: io::Error) -> Self {
|
||||||
|
Self::IoError(value.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ToCommandString for HlwmCommand {
|
impl ToCommandString for HlwmCommand {
|
||||||
fn to_command_string(&self) -> String {
|
fn to_command_string(&self) -> String {
|
||||||
let cmd_string = match self {
|
let cmd_string = match self {
|
||||||
|
@ -561,7 +636,6 @@ impl ToCommandString for HlwmCommand {
|
||||||
| HlwmCommand::Reload
|
| HlwmCommand::Reload
|
||||||
| HlwmCommand::Version
|
| HlwmCommand::Version
|
||||||
| HlwmCommand::ListRules
|
| HlwmCommand::ListRules
|
||||||
| HlwmCommand::Substitute
|
|
||||||
| HlwmCommand::Mouseunbind
|
| HlwmCommand::Mouseunbind
|
||||||
| HlwmCommand::True
|
| HlwmCommand::True
|
||||||
| HlwmCommand::False
|
| HlwmCommand::False
|
||||||
|
@ -724,6 +798,41 @@ impl ToCommandString for HlwmCommand {
|
||||||
parts.join("\t")
|
parts.join("\t")
|
||||||
}
|
}
|
||||||
HlwmCommand::Pad { monitor, pad } => format!("{self}\t{monitor}\t{pad}"),
|
HlwmCommand::Pad { monitor, pad } => format!("{self}\t{monitor}\t{pad}"),
|
||||||
|
HlwmCommand::Substitute {
|
||||||
|
identifier,
|
||||||
|
attribute_path,
|
||||||
|
command,
|
||||||
|
} => format!(
|
||||||
|
"{self}\t{identifier}\t{attribute_path}\t{}",
|
||||||
|
command.to_command_string()
|
||||||
|
),
|
||||||
|
HlwmCommand::ForEach {
|
||||||
|
unique,
|
||||||
|
recursive,
|
||||||
|
filter_name,
|
||||||
|
identifier,
|
||||||
|
object,
|
||||||
|
} => {
|
||||||
|
let mut parts = Vec::with_capacity(6);
|
||||||
|
parts.push(self.to_string());
|
||||||
|
parts.push(identifier.to_string());
|
||||||
|
parts.push(object.to_string());
|
||||||
|
if let Some(filter_name) = filter_name {
|
||||||
|
parts.push(format!("--filter-name={filter_name}"));
|
||||||
|
}
|
||||||
|
if *unique {
|
||||||
|
parts.push("--unique".into());
|
||||||
|
}
|
||||||
|
if *recursive {
|
||||||
|
parts.push("--recursive".into());
|
||||||
|
}
|
||||||
|
parts.join("\t")
|
||||||
|
}
|
||||||
|
HlwmCommand::Sprintf(args) => [self.to_string()]
|
||||||
|
.into_iter()
|
||||||
|
.chain(args.into_iter().cloned())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\t"),
|
||||||
};
|
};
|
||||||
if let Some(s) = cmd_string.strip_suffix('\t') {
|
if let Some(s) = cmd_string.strip_suffix('\t') {
|
||||||
return s.to_string();
|
return s.to_string();
|
||||||
|
@ -767,7 +876,6 @@ mod test {
|
||||||
| HlwmCommand::Unlock
|
| HlwmCommand::Unlock
|
||||||
| HlwmCommand::True
|
| HlwmCommand::True
|
||||||
| HlwmCommand::False
|
| HlwmCommand::False
|
||||||
| HlwmCommand::Substitute
|
|
||||||
| HlwmCommand::Mouseunbind => (cmd.clone(), cmd.to_string()),
|
| HlwmCommand::Mouseunbind => (cmd.clone(), cmd.to_string()),
|
||||||
HlwmCommand::Echo(_) => (
|
HlwmCommand::Echo(_) => (
|
||||||
HlwmCommand::Echo(vec!["Hello world!".into()]),
|
HlwmCommand::Echo(vec!["Hello world!".into()]),
|
||||||
|
@ -1004,6 +1112,43 @@ mod test {
|
||||||
},
|
},
|
||||||
"pad\t1\t2\t3\t4\t5".into(),
|
"pad\t1\t2\t3\t4\t5".into(),
|
||||||
),
|
),
|
||||||
|
HlwmCommand::Substitute {
|
||||||
|
identifier: _,
|
||||||
|
attribute_path: _,
|
||||||
|
command: _,
|
||||||
|
} => (
|
||||||
|
HlwmCommand::Substitute {
|
||||||
|
identifier: "MYTITLE".into(),
|
||||||
|
attribute_path: "clients.focus.title".into(),
|
||||||
|
command: Box::new(HlwmCommand::Echo(vec!["MYTITLE".to_string()])),
|
||||||
|
},
|
||||||
|
"substitute\tMYTITLE\tclients.focus.title\techo\tMYTITLE".into(),
|
||||||
|
),
|
||||||
|
HlwmCommand::ForEach {
|
||||||
|
unique: _,
|
||||||
|
recursive: _,
|
||||||
|
filter_name: _,
|
||||||
|
identifier: _,
|
||||||
|
object: _,
|
||||||
|
} => (
|
||||||
|
HlwmCommand::ForEach {
|
||||||
|
unique: true,
|
||||||
|
recursive: true,
|
||||||
|
filter_name: Some(".+".into()),
|
||||||
|
identifier: "CLIENT".into(),
|
||||||
|
object: "clients.".into(),
|
||||||
|
},
|
||||||
|
"foreach\tCLIENT\tclients.\t--filter-name=.+\t--unique\t--recursive".into(),
|
||||||
|
),
|
||||||
|
HlwmCommand::Sprintf(_) => (
|
||||||
|
HlwmCommand::Sprintf(
|
||||||
|
["X", "tag=%s", "tags.focus.name", "rule", "once", "X"]
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
"sprintf\tX\ttag=%s\ttags.focus.name\trule\tonce\tX".into(),
|
||||||
|
),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
for (command, expected_string) in commands {
|
for (command, expected_string) in commands {
|
||||||
|
|
|
@ -250,7 +250,7 @@ impl ToCommandString for MousebindAction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Clone, Error)]
|
||||||
pub enum KeyParseError {
|
pub enum KeyParseError {
|
||||||
#[error("value too short (expected >= 2 parts, got {0} parts)")]
|
#[error("value too short (expected >= 2 parts, got {0} parts)")]
|
||||||
TooShort(usize),
|
TooShort(usize),
|
||||||
|
|
|
@ -101,10 +101,14 @@ impl Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_setting(&self, setting: SettingName) -> Result<Setting, CommandError> {
|
pub fn get_setting(&self, setting: SettingName) -> Result<Setting, CommandError> {
|
||||||
Ok(Setting::from_str(&String::from_utf8(
|
Ok(Setting::from_str(&format!(
|
||||||
self.execute(HlwmCommand::Get(setting))?.stdout,
|
"{setting}\t{}",
|
||||||
)?)
|
String::from_utf8(self.execute(HlwmCommand::Get(setting))?.stdout,)?
|
||||||
.map_err(|_| StringParseError::UnknownValue)?)
|
))
|
||||||
|
.map_err(|err| {
|
||||||
|
error!("failed getting setting [{setting}]: {err}");
|
||||||
|
StringParseError::UnknownValue
|
||||||
|
})?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn query(&self, command: HlwmCommand) -> Result<Vec<String>, CommandError> {
|
pub fn query(&self, command: HlwmCommand) -> Result<Vec<String>, CommandError> {
|
||||||
|
@ -136,7 +140,7 @@ pub trait ToCommandString {
|
||||||
fn to_command_string(&self) -> String;
|
fn to_command_string(&self) -> String;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Clone, Error)]
|
||||||
pub enum StringParseError {
|
pub enum StringParseError {
|
||||||
#[error("unknown value")]
|
#[error("unknown value")]
|
||||||
UnknownValue,
|
UnknownValue,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use log::debug;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{gen_parse, hlwm::command::CommandParseError};
|
use crate::{gen_parse, hlwm::command::CommandParseError};
|
||||||
|
@ -246,6 +247,7 @@ impl FromStr for Setting {
|
||||||
type Err = CommandParseError;
|
type Err = CommandParseError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
debug!("beginning to parse [{s}] as setting");
|
||||||
let ((command, arg), _) = split::on_first_match(s, &['\t', ' '])
|
let ((command, arg), _) = split::on_first_match(s, &['\t', ' '])
|
||||||
.ok_or(CommandParseError::InvalidArgumentCount(0, "setting".into()))?;
|
.ok_or(CommandParseError::InvalidArgumentCount(0, "setting".into()))?;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
use std::{error::Error, fmt::Debug};
|
||||||
|
|
||||||
|
use log::{debug, error};
|
||||||
|
|
||||||
|
pub trait UnwrapLog {
|
||||||
|
type Target;
|
||||||
|
|
||||||
|
fn unwrap_or_log(self, default: Self::Target) -> Self::Target;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E> UnwrapLog for Result<T, E>
|
||||||
|
where
|
||||||
|
T: Debug,
|
||||||
|
E: Error,
|
||||||
|
{
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn unwrap_or_log(self, default: Self::Target) -> Self::Target {
|
||||||
|
match self {
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(err) => {
|
||||||
|
error!(
|
||||||
|
"[{}] unwrap_or_log got error: {err}",
|
||||||
|
std::any::type_name::<Self::Target>()
|
||||||
|
);
|
||||||
|
debug!("^ defaulting to {default:#?}");
|
||||||
|
default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ use log::{error, info};
|
||||||
pub mod cmd;
|
pub mod cmd;
|
||||||
mod config;
|
mod config;
|
||||||
mod hlwm;
|
mod hlwm;
|
||||||
|
pub mod logerr;
|
||||||
mod panel;
|
mod panel;
|
||||||
|
|
||||||
#[derive(Parser, Debug, Clone)]
|
#[derive(Parser, Debug, Clone)]
|
||||||
|
|
Loading…
Reference in New Issue