better error handling using Result instead of panic

This commit is contained in:
2025-01-02 00:42:54 +00:00
parent 6004c1a2d3
commit 778392f729
5 changed files with 66 additions and 45 deletions

2
Cargo.lock generated
View File

@@ -192,7 +192,7 @@ dependencies = [
[[package]] [[package]]
name = "swayout" name = "swayout"
version = "1.2.1" version = "1.2.2"
dependencies = [ dependencies = [
"kdl", "kdl",
"serde", "serde",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "swayout" name = "swayout"
version = "1.2.1" version = "1.2.2"
edition = "2021" edition = "2021"
description = "A tool to configure sway outputs" description = "A tool to configure sway outputs"
authors = ["Stephen Byrne"] authors = ["Stephen Byrne"]

View File

@@ -135,7 +135,7 @@ pub fn print_layout_names() {
/// or the name of an output. /// or the name of an output.
/// Return true of any outputs were enabled. /// Return true of any outputs were enabled.
/// Return false if there were no outputs to enable and thus nothing was done. /// Return false if there were no outputs to enable and thus nothing was done.
pub fn apply_layout(layout_name: &String) -> bool { pub fn apply_layout(layout_name: &String) -> Result<(), String> {
let config = get_config(); let config = get_config();
let outputs = get_outputs(); let outputs = get_outputs();
let monitor_states = get_monitor_states(&config, &outputs); let monitor_states = get_monitor_states(&config, &outputs);
@@ -151,15 +151,21 @@ pub fn apply_layout(layout_name: &String) -> bool {
.find(|layout| &layout.name == layout_name) .find(|layout| &layout.name == layout_name)
{ {
// The layout is defined in the config. // The layout is defined in the config.
layout for (monitor_name, output_config) in &layout.outputs {
.outputs // there is probably a syntactic sugar for this?
.iter() let monitor_state = if let Some(monitor_state) =
.for_each(|(monitor_name, output_config)| { get_monitor_state_by_name(&monitor_states, &monitor_name)
let monitor_state = get_monitor_state_by_name(&monitor_states, &monitor_name) {
.unwrap_or_else(||panic!("Cannot find monitor '{}' for layout '{}'", monitor_name, layout_name)); monitor_state
} else {
return Err(format!(
"Cannot find monitor '{}' for layout '{}'",
monitor_name, layout_name
));
};
if monitor_state.is_lid_closed { if monitor_state.is_lid_closed {
panic!("Monitor '{}' is closed", monitor_name); return Err(format!("Monitor '{}' is closed.", monitor_name));
} }
// find the output for the monitor to get the output name. // find the output for the monitor to get the output name.
@@ -169,15 +175,18 @@ pub fn apply_layout(layout_name: &String) -> bool {
if let Some(output) = output_opt { if let Some(output) = output_opt {
output_config_map.insert(&output.name, &output_config); output_config_map.insert(&output.name, &output_config);
} else { } else {
panic!("Missing output for monitor '{monitor_name}'"); return Err(format!("Missing output for monitor '{}'.", monitor_name));
}
} }
});
} else { } else {
// The layout is not defined in the rules. // The layout is not defined in the rules.
// See if it is a monitor name... // See if it is a monitor name...
if let Some(monitor_state) = get_monitor_state_by_name(&monitor_states, &layout_name) { if let Some(monitor_state) = get_monitor_state_by_name(&monitor_states, &layout_name) {
if monitor_state.is_lid_closed { if monitor_state.is_lid_closed {
panic!("Monitor '{}' is closed", &monitor_state.monitor.name); return Err(format!(
"Monitor '{}' is closed.",
&monitor_state.monitor.name
));
} }
// It is a monitor name. Find the matching output... // It is a monitor name. Find the matching output...
@@ -187,29 +196,34 @@ pub fn apply_layout(layout_name: &String) -> bool {
{ {
output_config_map.insert(&output.name, &output.output_config); output_config_map.insert(&output.name, &output.output_config);
} else { } else {
panic!("Could not find output for monitor '{layout_name}'") return Err(format!(
"Could not find output for monitor '{}'.",
layout_name
));
} }
} else { } else {
// See if it is an output name... // See if it is an output name...
if let Some(output) = outputs.iter().find(|output| &output.name == layout_name) { if let Some(output) = outputs.iter().find(|output| &output.name == layout_name) {
output_config_map.insert(&output.name, &output.output_config); output_config_map.insert(&output.name, &output.output_config);
} else { } else {
panic!("Could not find layout, monitor, or output '{layout_name}'") return Err(format!(
"Could not find layout, monitor, or output '{}.'",
layout_name
));
} }
} }
} }
if ! output_config_map.is_empty() { if output_config_map.is_empty() {
apply_outputs(&outputs, &output_config_map); return Err("No outputs would be enabled. Do nothing.".to_string());
true
} else {
false
} }
apply_outputs(&outputs, &output_config_map)
} }
/// Apply the first automatic layout for which all outputs are available. /// Apply the first automatic layout for which all outputs are available.
/// Return the name of the layout applied or None if no automatic layout was available. /// Return the name of the layout applied or None if no automatic layout was available.
pub fn apply_automatic() -> Option<String> { pub fn apply_automatic() -> Result<String,String> {
let outputs = get_outputs(); let outputs = get_outputs();
let config = get_config(); let config = get_config();
@@ -219,12 +233,10 @@ pub fn apply_automatic() -> Option<String> {
.iter() .iter()
.find(|layout| layout.automatic) .find(|layout| layout.automatic)
{ {
if apply_layout(&layout.name) {
Some(String::from(&layout.name)) apply_layout(&layout.name)?;
Ok(String::from(&layout.name))
} else { } else {
None Err("No automatic layouts available.".to_string())
}
} else {
None
} }
} }

View File

@@ -8,14 +8,18 @@ fn main() {
} else if args.len() == 2 { } else if args.len() == 2 {
let arg = &args[1]; let arg = &args[1];
if arg == "--automatic" { if arg == "--automatic" {
if let Some(layout_name) = swayout::apply_automatic() { match swayout::apply_automatic() {
Ok(layout_name) => {
println!("{}", layout_name); println!("{}", layout_name);
} else { }
eprintln!("no automatic layout available"); Err(e) => {
eprintln!("{}", e);
process::exit(2) process::exit(2)
} }
}
} else { } else {
if ! swayout::apply_layout(&args[1]) { if let Err(e) = swayout::apply_layout(&args[1]) {
eprintln!("{}", e);
process::exit(3) process::exit(3)
} }
} }

View File

@@ -67,7 +67,10 @@ fn get_mode(output: &Value) -> String {
} }
/// Apply the specified outputs. Enable all outputs in [outputs], disable others. /// Apply the specified outputs. Enable all outputs in [outputs], disable others.
pub fn apply_outputs(all_outputs: &Vec<Output>, outputs: &HashMap<&String, &OutputConfig>) { pub fn apply_outputs(
all_outputs: &Vec<Output>,
outputs: &HashMap<&String, &OutputConfig>,
) -> Result<(), String> {
// set enabled outputs first, then set disabled outputs. // set enabled outputs first, then set disabled outputs.
// That way if some work before an error, you have at least one output enabled. // That way if some work before an error, you have at least one output enabled.
@@ -85,7 +88,7 @@ pub fn apply_outputs(all_outputs: &Vec<Output>, outputs: &HashMap<&String, &Outp
}); });
if enabled.is_empty() { if enabled.is_empty() {
panic!("No enabled outputs!"); return Err("No enabled outputs!".to_string());
} }
let mut cmd = Command::new("swaymsg"); let mut cmd = Command::new("swaymsg");
@@ -117,4 +120,6 @@ pub fn apply_outputs(all_outputs: &Vec<Output>, outputs: &HashMap<&String, &Outp
.for_each(|arg| print!("{} ", arg.to_str().unwrap())); .for_each(|arg| print!("{} ", arg.to_str().unwrap()));
cmd.output().expect("swaymsg output failed"); cmd.output().expect("swaymsg output failed");
Ok(())
} }