context7-cli 0.2.0

CLI client for the Context7 API — search libraries and documentation from the terminal
Documentation
/// Terminal output formatting.
///
/// This is the **only** module allowed to call `println!` or `eprintln!`.
/// All coloured formatting via the `colored` crate is centralised here.
/// All user-facing strings are resolved via [`crate::i18n::t`].
use colored::Colorize;

use crate::api::{DocumentationSnippet, LibrarySearchResult, RespostaDocumentacao};
use crate::i18n::{idioma_atual, t, Idioma, Mensagem};
use crate::storage::ChaveArmazenada;

// ─── BIBLIOTECA ───────────────────────────────────────────────────────────────

/// Prints the list of libraries returned by the search endpoint.
///
/// Displays index, library ID (bold), title (dimmed), optional description
/// (italic), and optional trust score.
pub fn exibir_bibliotecas_formatado(resultados: &[LibrarySearchResult]) {
    if resultados.is_empty() {
        println!("{}", t(Mensagem::NenhumaBibliotecaEncontrada).yellow());
        return;
    }

    println!("{}", t(Mensagem::BibliotecasEncontradas).green().bold());
    println!("{}", "".repeat(60).dimmed());

    for (i, lib) in resultados.iter().enumerate() {
        let numero = format!("{}.", i + 1);
        println!("{} {} {}", numero.cyan(), lib.id.bold(), lib.title.dimmed());

        if let Some(desc) = &lib.description {
            println!("   {}", desc.italic());
        }

        if let Some(score) = lib.trust_score {
            println!("   {} {:.1}", t(Mensagem::ConfiancaScore).dimmed(), score);
        }

        println!();
    }
}

// ─── DOCUMENTAÇÃO ─────────────────────────────────────────────────────────────

/// Prints structured documentation from the docs endpoint.
///
/// Iterates over `snippets` when present; falls back to raw `content`.
pub fn exibir_documentacao_formatada(doc: &RespostaDocumentacao) {
    if let Some(snippets) = &doc.snippets {
        if snippets.is_empty() {
            println!("{}", t(Mensagem::NenhumaDocumentacaoEncontrada).yellow());
            return;
        }

        println!("{}", t(Mensagem::TituloDocumentacao).green().bold());
        println!("{}", "".repeat(60).dimmed());

        for snippet in snippets {
            exibir_snippet(snippet);
        }
    } else if let Some(conteudo) = &doc.content {
        println!("{}", conteudo);
    } else {
        println!("{}", t(Mensagem::SemConteudoDisponivel).yellow());
    }
}

/// Prints a single documentation snippet with its source URLs.
fn exibir_snippet(snippet: &DocumentationSnippet) {
    println!("{}", snippet.content);

    if let Some(urls) = &snippet.source_urls {
        println!("\n{}", t(Mensagem::TituloFontes).dimmed());
        for url in urls {
            println!("  {}", url.blue().bold());
        }
    }

    println!();
}

// ─── CHAVES ───────────────────────────────────────────────────────────────────

/// Prints all stored keys with 1-based indices and masked values.
pub fn exibir_chaves_mascaradas(chaves: &[ChaveArmazenada], mascarar: impl Fn(&str) -> String) {
    println!(
        "{}",
        format!("{} {}", chaves.len(), t(Mensagem::ContadorChaves))
            .green()
            .bold()
    );
    println!("{}", "".repeat(60).dimmed());

    let rotulo_adicionada = match idioma_atual() {
        Idioma::English => "added:",
        Idioma::Portugues => "adicionada:",
    };

    for (i, chave) in chaves.iter().enumerate() {
        println!(
            "  {}  {}  {}",
            format!("[{}]", i + 1).cyan(),
            mascarar(&chave.value).bold(),
            format!("({} {})", rotulo_adicionada, chave.added_at).dimmed()
        );
    }
}

/// Prints the "no keys stored" hint message.
pub fn exibir_nenhuma_chave() {
    println!("{}", t(Mensagem::NenhumaChaveArmazenada).yellow());
    println!("{}", t(Mensagem::UsarKeysAdd).cyan());
}

/// Prints the "no keys to remove" message.
pub fn exibir_nenhuma_chave_para_remover() {
    println!("{}", t(Mensagem::NenhumaChaveParaRemover).yellow());
}

/// Prints an invalid index error.
pub fn exibir_indice_invalido(indice: usize, total: usize) {
    println!(
        "{}",
        format!(
            "{} {} {} {}.",
            t(Mensagem::IndiceInvalido),
            indice,
            "/",
            total
        )
        .red()
    );
}

/// Prints the success message for `keys add`.
pub fn exibir_chave_adicionada(caminho: &std::path::Path) {
    println!(
        "{} {}",
        t(Mensagem::ChaveAdicionada),
        caminho.display().to_string().green()
    );
}

/// Prints the success message for `keys remove`.
pub fn exibir_chave_removida(chave_mascarada: &str) {
    println!(
        "{} {}",
        chave_mascarada.bold(),
        t(Mensagem::ChaveRemovidaSucesso)
    );
}

/// Prints the cancellation message for `keys clear`.
pub fn exibir_operacao_cancelada() {
    println!("{}", t(Mensagem::OperacaoCancelada).yellow());
}

/// Prints the success message for `keys clear`.
pub fn exibir_chaves_removidas() {
    println!("{}", t(Mensagem::TodasChavesRemovidas).green());
}

/// Prints an "XDG not supported" error for `keys path`.
pub fn exibir_xdg_nao_suportado() {
    println!("{}", t(Mensagem::SistemaXdgNaoSuportado).red());
}

/// Prints the success message for `keys import`.
pub fn exibir_importacao_concluida(importadas: usize, total: usize) {
    println!(
        "{}",
        format!(
            "{}/{} {}",
            importadas,
            total,
            t(Mensagem::ChavesImportadasSucesso)
        )
        .green()
    );
}