aether-agent-cli 0.1.7

CLI and ACP server for the Aether AI coding agent
Documentation
use acp_utils::config_option_id::ConfigOptionId;
use llm::ReasoningEffort;
use std::fmt;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConfigSetting {
    Mode(String),
    Model(String),
    ReasoningEffort(Option<ReasoningEffort>),
}

impl ConfigSetting {
    pub fn parse(config_id: &str, value: &str) -> Result<Self, ConfigSettingError> {
        let id: ConfigOptionId =
            config_id.parse().map_err(|_| ConfigSettingError::UnknownConfigId(config_id.to_string()))?;

        match id {
            ConfigOptionId::Mode => Ok(Self::Mode(value.to_string())),
            ConfigOptionId::Model => Ok(Self::Model(value.to_string())),
            ConfigOptionId::ReasoningEffort => {
                let effort = ReasoningEffort::parse(value).map_err(|_| ConfigSettingError::InvalidValue {
                    config_id: config_id.to_string(),
                    value: value.to_string(),
                })?;
                Ok(Self::ReasoningEffort(effort))
            }
        }
    }

    pub fn config_id(&self) -> ConfigOptionId {
        match self {
            Self::Mode(_) => ConfigOptionId::Mode,
            Self::Model(_) => ConfigOptionId::Model,
            Self::ReasoningEffort(_) => ConfigOptionId::ReasoningEffort,
        }
    }
}

#[derive(Debug)]
pub enum ConfigSettingError {
    UnknownConfigId(String),
    InvalidValue { config_id: String, value: String },
}

impl fmt::Display for ConfigSettingError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::UnknownConfigId(id) => write!(f, "Unknown config option: {id}"),
            Self::InvalidValue { config_id, value } => {
                write!(f, "Invalid value '{value}' for config option '{config_id}'")
            }
        }
    }
}

impl std::error::Error for ConfigSettingError {}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parse_mode() {
        let setting = ConfigSetting::parse("mode", "Planner").unwrap();
        assert_eq!(setting, ConfigSetting::Mode("Planner".to_string()));
        assert_eq!(setting.config_id(), ConfigOptionId::Mode);
    }

    #[test]
    fn parse_model() {
        let setting = ConfigSetting::parse("model", "anthropic:claude-sonnet-4-5").unwrap();
        assert_eq!(setting, ConfigSetting::Model("anthropic:claude-sonnet-4-5".to_string()));
        assert_eq!(setting.config_id(), ConfigOptionId::Model);
    }

    #[test]
    fn parse_reasoning_effort_high() {
        let setting = ConfigSetting::parse("reasoning_effort", "high").unwrap();
        assert_eq!(setting, ConfigSetting::ReasoningEffort(Some(ReasoningEffort::High)));
        assert_eq!(setting.config_id(), ConfigOptionId::ReasoningEffort);
    }

    #[test]
    fn parse_reasoning_effort_none() {
        let setting = ConfigSetting::parse("reasoning_effort", "none").unwrap();
        assert_eq!(setting, ConfigSetting::ReasoningEffort(None));
    }

    #[test]
    fn parse_reasoning_effort_empty() {
        let setting = ConfigSetting::parse("reasoning_effort", "").unwrap();
        assert_eq!(setting, ConfigSetting::ReasoningEffort(None));
    }

    #[test]
    fn unknown_config_id_returns_error() {
        let err = ConfigSetting::parse("unknown", "value").unwrap_err();
        assert!(matches!(err, ConfigSettingError::UnknownConfigId(ref id) if id == "unknown"));
        assert!(err.to_string().contains("unknown"));
    }

    #[test]
    fn invalid_reasoning_effort_returns_error() {
        let err = ConfigSetting::parse("reasoning_effort", "max").unwrap_err();
        assert!(matches!(err, ConfigSettingError::InvalidValue { .. }));
        assert!(err.to_string().contains("max"));
        assert!(err.to_string().contains("reasoning_effort"));
    }

    #[test]
    fn config_id_round_trip() {
        let cases: Vec<(&str, &str)> = vec![("mode", "test"), ("model", "test"), ("reasoning_effort", "low")];
        for (id_str, value) in cases {
            let setting = ConfigSetting::parse(id_str, value).unwrap();
            assert_eq!(setting.config_id().as_str(), id_str);
        }
    }
}