bzr 0.4.2

A CLI for Bugzilla, inspired by gh
Documentation
use std::collections::HashMap;
use std::io::Write;

use crate::types::{BugTemplate, OutputFormat};

use crate::output::formatting::{write_field, write_formatted, write_json, write_optional_field};

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

fn template_summary_line(name: &str, tmpl: &BugTemplate) -> String {
    let mut parts = Vec::new();
    if let Some(p) = &tmpl.product {
        parts.push(format!("product={p}"));
    }
    if let Some(c) = &tmpl.component {
        parts.push(format!("component={c}"));
    }
    if let Some(p) = &tmpl.priority {
        parts.push(format!("priority={p}"));
    }
    if let Some(s) = &tmpl.severity {
        parts.push(format!("severity={s}"));
    }
    if parts.is_empty() {
        name.to_string()
    } else {
        format!("{name} ({})", parts.join(", "))
    }
}

pub fn write_template_saved<W: Write + ?Sized>(
    name: &str,
    verb: &str,
    format: OutputFormat,
    out: &mut W,
) {
    match format {
        OutputFormat::Json => {
            write_json(
                &serde_json::json!({"name": name, "action": verb.to_lowercase()}),
                out,
            );
        }
        OutputFormat::Table => {
            let _ = writeln!(out, "{}", template_saved_message(name, verb));
        }
    }
}

pub fn write_template_list<W: Write + ?Sized, S: ::std::hash::BuildHasher>(
    templates: &HashMap<String, BugTemplate, S>,
    format: OutputFormat,
    out: &mut W,
) {
    write_formatted(templates, format, out, |templates, out| {
        if templates.is_empty() {
            let _ = writeln!(out, "No templates configured.");
            return;
        }
        let mut names: Vec<&str> = templates.keys().map(String::as_str).collect();
        names.sort_unstable();
        for name in names {
            let _ = writeln!(out, "{}", template_summary_line(name, &templates[name]));
        }
    });
}

pub fn write_template_detail<W: Write + ?Sized>(
    name: &str,
    template: &BugTemplate,
    format: OutputFormat,
    out: &mut W,
) {
    #[derive(serde::Serialize)]
    struct TemplateView<'a> {
        name: &'a str,
        #[serde(flatten)]
        template: &'a BugTemplate,
    }

    let view = TemplateView { name, template };
    write_formatted(&view, format, out, |view, out| {
        write_field(out, "Name", view.name);
        write_optional_field(out, "Product", view.template.product.as_deref());
        write_optional_field(out, "Component", view.template.component.as_deref());
        write_optional_field(out, "Version", view.template.version.as_deref());
        write_optional_field(out, "Priority", view.template.priority.as_deref());
        write_optional_field(out, "Severity", view.template.severity.as_deref());
        write_optional_field(out, "Assignee", view.template.assignee.as_deref());
        write_optional_field(out, "OS", view.template.op_sys.as_deref());
        write_optional_field(out, "Platform", view.template.rep_platform.as_deref());
        write_optional_field(out, "Description", view.template.description.as_deref());
    });
}

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