bzr 0.1.0

A CLI for Bugzilla, inspired by gh
Documentation
use crate::cli::FieldAction;
use crate::error::Result;
use crate::output;
use crate::types::ApiMode;
use crate::types::OutputFormat;

pub async fn execute(
    action: &FieldAction,
    server: Option<&str>,
    format: OutputFormat,
    api: Option<ApiMode>,
) -> Result<()> {
    match action {
        FieldAction::Aliases => {
            output::print_field_aliases(crate::client::FIELD_ALIASES, format);
            return Ok(());
        }
        FieldAction::List { name } => {
            let client = super::shared::connect_and_configure(server, api).await?;
            let values = client.get_field_values(name).await?;
            if values.is_empty() && format == OutputFormat::Table {
                #[expect(clippy::print_stdout)]
                {
                    println!("No values for field '{name}'.");
                }
            } else {
                output::print_field_values(&values, format);
            }
        }
    }
    Ok(())
}

#[cfg(test)]
#[expect(clippy::unwrap_used)]
mod tests {
    use wiremock::matchers::{method, path};
    use wiremock::{Mock, ResponseTemplate};

    use crate::cli::FieldAction;
    use crate::test_helpers::{capture_stdout, extract_json, setup_test_env};
    use crate::types::OutputFormat;

    #[tokio::test]
    async fn field_list_returns_values() {
        let (_lock, mock, _tmp) = setup_test_env().await;

        Mock::given(method("GET"))
            .and(path("/rest/field/bug/bug_status"))
            .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
                "fields": [{
                    "name": "bug_status",
                    "values": [
                        {"name": "NEW"},
                        {"name": "ASSIGNED"},
                        {"name": "RESOLVED"}
                    ]
                }]
            })))
            .mount(&mock)
            .await;

        let action = FieldAction::List {
            name: "status".to_string(),
        };
        let (result, output) =
            capture_stdout(super::execute(&action, None, OutputFormat::Json, None)).await;
        assert!(result.is_ok());
        let parsed = extract_json(&output);
        assert!(parsed.as_array().unwrap().len() >= 3);
        assert_eq!(parsed[0]["name"], "NEW");
    }

    #[tokio::test]
    async fn field_aliases_succeeds_without_server() {
        let action = FieldAction::Aliases;
        let (result, output) =
            capture_stdout(super::execute(&action, None, OutputFormat::Json, None)).await;
        assert!(result.is_ok());
        let parsed = extract_json(&output);
        let arr = parsed.as_array().unwrap();
        assert!(!arr.is_empty());
        assert_eq!(arr[0]["alias"], "file_loc");
        assert_eq!(arr[0]["api_name"], "bug_file_loc");
    }

    #[tokio::test]
    async fn field_list_http_500_returns_error() {
        let (_lock, mock, _tmp) = setup_test_env().await;

        Mock::given(method("GET"))
            .and(path("/rest/field/bug/bug_status"))
            .respond_with(ResponseTemplate::new(500).set_body_string("Internal Server Error"))
            .mount(&mock)
            .await;

        let action = FieldAction::List {
            name: "status".to_string(),
        };
        let result = super::execute(&action, None, OutputFormat::Json, None).await;
        assert!(result.is_err());
    }
}