govctl 0.9.1

Project governance CLI for RFC, ADR, and Work Item management
use crate::OutputFormat;
use crate::cmd::output::{command_table, print_json_array};
use crate::theme::{SemanticColor, status_semantic};
use crate::ui::stdout_supports_color;
use comfy_table::{Attribute, Cell};
use serde::Serialize;

fn cell(text: &str) -> Cell {
    Cell::new(text)
}

fn id_cell(text: &str, use_colors: bool) -> Cell {
    if use_colors {
        Cell::new(text)
            .fg(SemanticColor::Info.to_comfy())
            .add_attribute(Attribute::Bold)
    } else {
        Cell::new(text)
    }
}

fn status_cell(status: &str, use_colors: bool) -> Cell {
    if use_colors {
        Cell::new(status).fg(status_semantic(status).to_comfy())
    } else {
        Cell::new(status)
    }
}

fn header_cell(text: &str, use_colors: bool) -> Cell {
    if use_colors {
        Cell::new(text).add_attribute(Attribute::Bold)
    } else {
        Cell::new(text)
    }
}

pub(super) fn truncate_chars(s: &str, max: usize) -> String {
    if s.chars().count() <= max {
        s.to_string()
    } else {
        let truncated: String = s.chars().take(max).collect();
        format!("{truncated}")
    }
}

pub(super) fn output_list<T: Serialize>(
    items: &[T],
    headers: &[&str],
    format: OutputFormat,
    to_row: impl Fn(&T) -> Vec<String>,
) {
    match format {
        OutputFormat::Json => {
            print_json_array(items);
        }
        OutputFormat::Plain => {
            for item in items {
                let row = to_row(item);
                println!("{}", row.join("\t"));
            }
        }
        OutputFormat::Table => {
            let use_colors = stdout_supports_color();
            let mut table = command_table();
            table.set_header(
                headers
                    .iter()
                    .map(|h| header_cell(h, use_colors))
                    .collect::<Vec<_>>(),
            );

            for item in items {
                let row = to_row(item);
                table.add_row(
                    row.iter()
                        .enumerate()
                        .map(|(i, v)| {
                            if i == 0 {
                                id_cell(v, use_colors)
                            } else if headers
                                .get(i)
                                .is_some_and(|h| *h == "Status" || *h == "Phase")
                            {
                                status_cell(v, use_colors)
                            } else {
                                cell(v)
                            }
                        })
                        .collect::<Vec<_>>(),
                );
            }

            println!("{table}");
        }
    }
}