bzr 0.2.0

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

use colored::Colorize;

use super::formatting::{print_formatted, truncate};
use crate::types::{Classification, OutputFormat};

pub fn print_classification(classification: &Classification, format: OutputFormat) {
    print_formatted(classification, format, |classification| {
        let _ = writeln!(
            io::stdout(),
            "{} {}\n{}\n",
            "Classification".bold(),
            classification.name.bold(),
            classification.description,
        );
        if !classification.products.is_empty() {
            let _ = writeln!(io::stdout(), "{}:", "Products".bold());
            for p in &classification.products {
                let _ = writeln!(
                    io::stdout(),
                    "  {} - {}",
                    p.name,
                    truncate(&p.description, 60)
                );
            }
        }
    });
}

#[cfg(test)]
#[expect(clippy::unwrap_used)]
mod tests {
    use crate::types::{Classification, ClassificationProduct};

    #[test]
    fn print_classification_json() {
        let classification = Classification {
            id: 1,
            name: "Software".into(),
            description: "Software products".into(),
            sort_key: 0,
            products: vec![ClassificationProduct {
                id: 10,
                name: "Widget".into(),
                description: "Widget product".into(),
            }],
        };
        let json = serde_json::to_string(&classification).unwrap();
        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
        assert_eq!(parsed["name"], "Software");
        assert_eq!(parsed["products"][0]["name"], "Widget");
    }

    #[test]
    fn classification_text_format_fields() {
        let classification = Classification {
            id: 1,
            name: "Software".into(),
            description: "Software products".into(),
            sort_key: 0,
            products: vec![
                ClassificationProduct {
                    id: 10,
                    name: "Widget".into(),
                    description: "A long description that should be truncated by the formatter when displayed".into(),
                },
                ClassificationProduct {
                    id: 11,
                    name: "Gadget".into(),
                    description: "Short desc".into(),
                },
            ],
        };
        assert_eq!(classification.products.len(), 2);
        assert_eq!(classification.products[0].name, "Widget");
        assert_eq!(classification.products[1].name, "Gadget");
    }

    #[test]
    fn print_classification_json_empty_products() {
        let classification = Classification {
            id: 2,
            name: "Empty".into(),
            description: "No products".into(),
            sort_key: 0,
            products: vec![],
        };
        let json = serde_json::to_string(&classification).unwrap();
        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
        assert_eq!(parsed["products"].as_array().unwrap().len(), 0);
    }

    #[cfg(unix)]
    #[tokio::test]
    async fn print_classification_table_omits_products_header_when_empty() {
        let _lock = crate::ENV_LOCK.lock().await;
        let with_products = Classification {
            id: 1,
            name: "Software".into(),
            description: "Software products".into(),
            sort_key: 0,
            products: vec![ClassificationProduct {
                id: 10,
                name: "Widget".into(),
                description: "A widget".into(),
            }],
        };
        let empty = Classification {
            id: 2,
            name: "Empty".into(),
            description: "Nothing here".into(),
            sort_key: 0,
            products: vec![],
        };

        let ((), populated) = crate::test_helpers::capture_stdout(async {
            super::print_classification(&with_products, crate::types::OutputFormat::Table);
        })
        .await;
        let ((), bare) = crate::test_helpers::capture_stdout(async {
            super::print_classification(&empty, crate::types::OutputFormat::Table);
        })
        .await;

        assert!(populated.contains("Products"));
        assert!(populated.contains("Widget"));
        assert!(!bare.contains("Products"));
    }
}