use super::profile::UblxOverlay;
pub const LAYOUT_PCT_MAX: u16 = 100;
#[derive(Clone, Debug)]
pub struct HotReloadError {
pub field: &'static str,
pub message: String,
}
impl std::fmt::Display for HotReloadError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}: {}", self.field, self.message)
}
}
#[must_use]
pub fn first_validation_error_message(errors: &[HotReloadError]) -> String {
if errors.is_empty() {
"invalid config".to_string()
} else {
let n = errors.len();
let numbered = errors
.iter()
.enumerate()
.map(|(i, e)| format!("{}) {}", i + 1, e))
.collect::<Vec<_>>()
.join("\n");
format!("Found {n} error(s):\n{numbered}")
}
}
#[derive(Clone, Debug, Default)]
pub struct ReloadResult {
pub applied: bool,
pub validation_errors: Vec<HotReloadError>,
}
pub fn validate_hot_reload_overlay(
overlay: &UblxOverlay,
valid_theme_names: &[&str],
) -> Result<(), Vec<HotReloadError>> {
let mut errors = Vec::new();
if let Some(ref name) = overlay.theme {
let name = name.trim();
if !name.is_empty() && !valid_theme_names.contains(&name) {
errors.push(HotReloadError {
field: "theme",
message: format!("invalid: \"{name}\"; run `ublx --themes` to list valid options"),
});
}
}
if let Some(ref layout) = overlay.layout {
for (field, pct) in [
("layout.left_pct", layout.left_pct),
("layout.middle_pct", layout.middle_pct),
("layout.right_pct", layout.right_pct),
] {
if pct > LAYOUT_PCT_MAX {
errors.push(HotReloadError {
field,
message: format!("must be 0–{LAYOUT_PCT_MAX} (got {pct})"),
});
}
}
let sum =
u32::from(layout.left_pct) + u32::from(layout.middle_pct) + u32::from(layout.right_pct);
if errors.iter().all(|e| !e.field.starts_with("layout")) && sum != u32::from(LAYOUT_PCT_MAX)
{
errors.push(HotReloadError {
field: "layout",
message: format!(
"left_pct + middle_pct + right_pct must = {LAYOUT_PCT_MAX} (got {sum})"
),
});
}
}
if let Some(a) = overlay.bg_opacity
&& (!a.is_finite() || !(0.0..=1.0).contains(&a))
{
errors.push(HotReloadError {
field: "bg_opacity",
message: format!("must be between 0.0 and 1.0 (got {a})"),
});
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}