bzr 0.3.0

A CLI for Bugzilla, inspired by gh
Documentation
use std::collections::HashMap;
use std::io::{self, Write as _};

use crate::types::{OutputFormat, QueryKind, SavedQuery};

use super::formatting::{
    print_field, print_formatted, print_json, print_list_field, print_optional_field,
};

fn kind_label(kind: &QueryKind) -> &'static str {
    match kind {
        QueryKind::List => "list",
        QueryKind::Search => "search",
        QueryKind::Url => "url",
    }
}

fn query_saved_message(name: &str, verb: &str) -> String {
    format!("{verb} query '{name}'")
}

fn query_summary_line(name: &str, q: &SavedQuery) -> String {
    let mut parts = vec![format!("kind={}", kind_label(&q.kind))];
    if !q.product.is_empty() {
        parts.push(format!("product={}", q.product.join(",")));
    }
    if !q.status.is_empty() {
        parts.push(format!("status={}", q.status.join(",")));
    }
    if let Some(qs) = &q.quicksearch {
        parts.push(format!("search=\"{qs}\""));
    }
    if let Some(limit) = q.limit {
        parts.push(format!("limit={limit}"));
    }
    if !q.raw_params.is_empty() {
        parts.push(format!("{} raw params", q.raw_params.len()));
    }
    format!("{name} ({})", parts.join(", "))
}

pub fn print_query_saved(name: &str, verb: &str, format: OutputFormat) {
    match format {
        OutputFormat::Json => {
            print_json(&serde_json::json!({"name": name, "action": verb.to_lowercase()}));
        }
        OutputFormat::Table => {
            let _ = writeln!(io::stdout(), "{}", query_saved_message(name, verb));
        }
    }
}

pub fn print_query_list(queries: &HashMap<String, SavedQuery>, format: OutputFormat) {
    print_formatted(queries, format, |queries| {
        if queries.is_empty() {
            let _ = writeln!(io::stdout(), "No saved queries configured.");
            return;
        }
        let mut names: Vec<&str> = queries.keys().map(String::as_str).collect();
        names.sort_unstable();
        for name in names {
            let _ = writeln!(io::stdout(), "{}", query_summary_line(name, &queries[name]));
        }
    });
}

pub fn print_query_detail(name: &str, query: &SavedQuery, format: OutputFormat) {
    #[derive(serde::Serialize)]
    struct QueryView<'a> {
        name: &'a str,
        #[serde(flatten)]
        query: &'a SavedQuery,
    }

    let view = QueryView { name, query };
    print_formatted(&view, format, |view| {
        print_field("Name", view.name);
        print_field("Kind", kind_label(&view.query.kind));
        print_optional_field("Source URL", view.query.source_url.as_deref());
        print_optional_field("Server", view.query.server.as_deref());
        print_list_field("Product", &view.query.product);
        print_list_field("Component", &view.query.component);
        print_list_field("Status", &view.query.status);
        print_list_field("Assignee", &view.query.assignee);
        print_list_field("Creator", &view.query.creator);
        print_list_field("Priority", &view.query.priority);
        print_list_field("Severity", &view.query.severity);
        print_optional_field("Search", view.query.quicksearch.as_deref());
        if let Some(limit) = view.query.limit {
            print_field("Limit", &limit.to_string());
        }
        print_optional_field("Fields", view.query.fields.as_deref());
        print_optional_field("Exclude", view.query.exclude_fields.as_deref());
        if !view.query.raw_params.is_empty() {
            print_field("Raw params", &view.query.raw_params.len().to_string());
        }
    });
}

#[cfg(test)]
#[path = "query_tests.rs"]
mod tests;