codex-wrapper 0.1.0

A type-safe Codex CLI wrapper for Rust
Documentation
use crate::Codex;
use crate::command::CodexCommand;
use crate::error::Result;
use crate::exec::{self, CommandOutput};

#[derive(Debug, Clone, Default)]
pub struct LoginCommand {
    config_overrides: Vec<String>,
    enabled_features: Vec<String>,
    disabled_features: Vec<String>,
    with_api_key: bool,
    device_auth: bool,
}

impl LoginCommand {
    #[must_use]
    pub fn new() -> Self {
        Self::default()
    }

    #[must_use]
    pub fn config(mut self, key_value: impl Into<String>) -> Self {
        self.config_overrides.push(key_value.into());
        self
    }

    #[must_use]
    pub fn enable(mut self, feature: impl Into<String>) -> Self {
        self.enabled_features.push(feature.into());
        self
    }

    #[must_use]
    pub fn disable(mut self, feature: impl Into<String>) -> Self {
        self.disabled_features.push(feature.into());
        self
    }

    #[must_use]
    pub fn with_api_key(mut self) -> Self {
        self.with_api_key = true;
        self
    }

    #[must_use]
    pub fn device_auth(mut self) -> Self {
        self.device_auth = true;
        self
    }
}

impl CodexCommand for LoginCommand {
    type Output = CommandOutput;

    fn args(&self) -> Vec<String> {
        let mut args = vec!["login".into()];
        push_common(
            &mut args,
            &self.config_overrides,
            &self.enabled_features,
            &self.disabled_features,
        );
        if self.with_api_key {
            args.push("--with-api-key".into());
        }
        if self.device_auth {
            args.push("--device-auth".into());
        }
        args
    }

    async fn execute(&self, codex: &Codex) -> Result<CommandOutput> {
        exec::run_codex(codex, self.args()).await
    }
}

#[derive(Debug, Clone, Default)]
pub struct LoginStatusCommand {
    config_overrides: Vec<String>,
    enabled_features: Vec<String>,
    disabled_features: Vec<String>,
}

impl LoginStatusCommand {
    #[must_use]
    pub fn new() -> Self {
        Self::default()
    }

    #[must_use]
    pub fn config(mut self, key_value: impl Into<String>) -> Self {
        self.config_overrides.push(key_value.into());
        self
    }

    #[must_use]
    pub fn enable(mut self, feature: impl Into<String>) -> Self {
        self.enabled_features.push(feature.into());
        self
    }

    #[must_use]
    pub fn disable(mut self, feature: impl Into<String>) -> Self {
        self.disabled_features.push(feature.into());
        self
    }
}

impl CodexCommand for LoginStatusCommand {
    type Output = CommandOutput;

    fn args(&self) -> Vec<String> {
        let mut args = vec!["login".into(), "status".into()];
        push_common(
            &mut args,
            &self.config_overrides,
            &self.enabled_features,
            &self.disabled_features,
        );
        args
    }

    async fn execute(&self, codex: &Codex) -> Result<CommandOutput> {
        exec::run_codex(codex, self.args()).await
    }
}

#[derive(Debug, Clone, Default)]
pub struct LogoutCommand;

impl LogoutCommand {
    #[must_use]
    pub fn new() -> Self {
        Self
    }
}

impl CodexCommand for LogoutCommand {
    type Output = CommandOutput;

    fn args(&self) -> Vec<String> {
        vec!["logout".into()]
    }

    async fn execute(&self, codex: &Codex) -> Result<CommandOutput> {
        exec::run_codex(codex, self.args()).await
    }
}

fn push_common(
    args: &mut Vec<String>,
    configs: &[String],
    enabled: &[String],
    disabled: &[String],
) {
    for value in configs {
        args.push("-c".into());
        args.push(value.clone());
    }
    for value in enabled {
        args.push("--enable".into());
        args.push(value.clone());
    }
    for value in disabled {
        args.push("--disable".into());
        args.push(value.clone());
    }
}

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

    #[test]
    fn login_args() {
        assert_eq!(
            LoginCommand::new().with_api_key().args(),
            vec!["login", "--with-api-key"]
        );
    }

    #[test]
    fn login_status_args() {
        assert_eq!(LoginStatusCommand::new().args(), vec!["login", "status"]);
    }

    #[test]
    fn logout_args() {
        assert_eq!(LogoutCommand::new().args(), vec!["logout"]);
    }
}