codeberg-cli 0.5.5

CLI Tool for codeberg similar to gh and glab
Documentation
use crate::actions::GlobalArgs;
use crate::paths::berg_data_dir;
use crate::render::json::JsonToStdout;
use crate::types::context::BergContext;
use crate::types::output::OutputMode;
use miette::IntoDiagnostic;

use clap::Parser;

/// List all forgejo instances you're authenticated for
#[derive(Parser, Debug)]
pub struct ListArgs {}

impl ListArgs {
    pub async fn run(self, global_args: GlobalArgs) -> miette::Result<()> {
        let ctx = BergContext::new(self, global_args).await?;
        let data_dir = berg_data_dir()?;

        let instances = data_dir
            .read_dir()
            .into_diagnostic()?
            .filter_map(|dir| {
                let entry = dir.ok()?;

                let logged_in = entry.metadata().is_ok_and(|metadata| metadata.is_dir())
                    && entry
                        .path()
                        .read_dir()
                        .into_iter()
                        .flatten()
                        .any(|content| content.is_ok_and(|content| content.file_name() == "TOKEN"));

                Some(Instance {
                    base_url: entry.file_name().display().to_string(),
                    logged_in,
                })
            })
            .collect::<Vec<_>>();

        match ctx.global_args.output_mode {
            OutputMode::Pretty => {
                present_instance_list(ctx, instances);
            }
            OutputMode::Json => {
                instances.print_json()?;
            }
        }

        Ok(())
    }
}

#[derive(Debug, Clone, serde::Serialize)]
struct Instance {
    base_url: String,
    logged_in: bool,
}

fn present_instance_list(ctx: BergContext<ListArgs>, instances: Vec<Instance>) {
    let issues_empty = instances.is_empty();

    let table = ctx
        .make_table()
        .set_header(vec!["Base URL", "Logged In"])
        .add_rows(instances.into_iter().map(|instance| {
            let Instance {
                base_url,
                logged_in,
            } = instance;
            vec![base_url, String::from(if logged_in { "Yes" } else { "No" })]
        }));

    let header = format!(
        "Instances{}",
        if issues_empty {
            " (empty)"
        } else {
            Default::default()
        }
    );

    let underscore = "=".repeat(header.len());
    let out = [header, underscore, table.show()].join("\n");

    println!("{out}");
}