use serde::Deserialize;
use super::schema::{invalid_value, SetConfigError};
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct TerminalConfig {
pub default_profile: String,
pub height: u16,
pub confirm_quit: String,
pub confirm_close: String,
pub profile: Vec<TerminalProfile>,
}
impl TerminalConfig {
#[must_use]
pub fn has_profile(&self, name: &str) -> bool {
name.trim().is_empty()
|| self
.profile
.iter()
.any(|p| p.name.eq_ignore_ascii_case(name.trim()))
}
pub(super) fn validate(&self) -> Result<(), SetConfigError> {
if !TERMINAL_HEIGHT_RANGE.contains(&self.height) {
return Err(invalid_value(
"terminal.height",
&self.height.to_string(),
TERMINAL_HEIGHT_REASON,
));
}
parse_confirm("terminal.confirm_quit", &self.confirm_quit)?;
parse_confirm("terminal.confirm_close", &self.confirm_close)?;
for (index, profile) in self.profile.iter().enumerate() {
let key = format!("terminal.profile[{}]", index + 1);
if profile.name.trim().is_empty() {
return Err(invalid_value(&key, "", "needs a name"));
}
if profile
.command
.first()
.map_or(true, |p| p.trim().is_empty())
{
return Err(invalid_value(
&key,
&profile.name,
"needs a command, e.g. command = [\"pwsh.exe\", \"-NoLogo\"]",
));
}
if self.profile[..index]
.iter()
.any(|p| p.name.eq_ignore_ascii_case(&profile.name))
{
return Err(invalid_value(&key, &profile.name, "is a duplicate name"));
}
}
if !self.has_profile(&self.default_profile) {
return Err(invalid_value(
"terminal.default_profile",
&self.default_profile,
"names no [[terminal.profile]]",
));
}
Ok(())
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct TerminalProfile {
pub name: String,
pub command: Vec<String>,
}
pub(super) const TERMINAL_HEIGHT_RANGE: std::ops::RangeInclusive<u16> = 10..=90;
pub(super) const TERMINAL_HEIGHT_REASON: &str = "must be a percentage between 10 and 90";
pub(super) fn parse_confirm(key: &str, value: &str) -> Result<String, SetConfigError> {
crate::terminal::ConfirmPolicy::parse(value)
.ok_or_else(|| invalid_value(key, value, crate::terminal::CONFIRM_REASON))?;
Ok(value.trim().to_ascii_lowercase())
}
pub(super) fn parse_terminal_chord(key: &str, value: &str) -> Result<(), SetConfigError> {
crate::terminal::keys::KeyChord::parse(value)
.map(|_| ())
.ok_or_else(|| invalid_value(key, value, crate::terminal::keys::KEY_CHORD_REASON))
}