Skip to main content

context7_cli/
output.rs

1/// Terminal output formatting.
2///
3/// This is the **only** module allowed to call `println!` or `eprintln!`.
4/// All coloured formatting via the `colored` crate is centralised here.
5/// All user-facing strings are resolved via [`crate::i18n::t`].
6use colored::Colorize;
7
8use crate::api::{DocumentationSnippet, LibrarySearchResult, RespostaDocumentacao};
9use crate::i18n::{idioma_atual, t, Idioma, Mensagem};
10use crate::storage::ChaveArmazenada;
11
12// ─── BIBLIOTECA ───────────────────────────────────────────────────────────────
13
14/// Prints the list of libraries returned by the search endpoint.
15///
16/// Displays index, library ID (bold), title (dimmed), optional description
17/// (italic), and optional trust score.
18pub fn exibir_bibliotecas_formatado(resultados: &[LibrarySearchResult]) {
19    if resultados.is_empty() {
20        println!("{}", t(Mensagem::NenhumaBibliotecaEncontrada).yellow());
21        return;
22    }
23
24    println!("{}", t(Mensagem::BibliotecasEncontradas).green().bold());
25    println!("{}", "─".repeat(60).dimmed());
26
27    for (i, lib) in resultados.iter().enumerate() {
28        let numero = format!("{}.", i + 1);
29        println!("{} {} {}", numero.cyan(), lib.id.bold(), lib.title.dimmed());
30
31        if let Some(desc) = &lib.description {
32            println!("   {}", desc.italic());
33        }
34
35        if let Some(score) = lib.trust_score {
36            println!("   {} {:.1}", t(Mensagem::ConfiancaScore).dimmed(), score);
37        }
38
39        println!();
40    }
41}
42
43// ─── DOCUMENTAÇÃO ─────────────────────────────────────────────────────────────
44
45/// Prints structured documentation from the docs endpoint.
46///
47/// Iterates over `snippets`. Shows a "no documentation found" message if empty.
48pub fn exibir_documentacao_formatada(doc: &RespostaDocumentacao) {
49    let snippets = match &doc.snippets {
50        Some(s) if !s.is_empty() => s,
51        _ => {
52            println!("{}", t(Mensagem::NenhumaDocumentacaoEncontrada).yellow());
53            return;
54        }
55    };
56
57    println!("{}", t(Mensagem::TituloDocumentacao).green().bold());
58    println!("{}", "─".repeat(60).dimmed());
59
60    for snippet in snippets {
61        exibir_snippet(snippet);
62    }
63}
64
65/// Prints a single documentation snippet with formatted fields.
66///
67/// Display order: page_title → code_title → code_description → code_list blocks → code_id (source)
68fn exibir_snippet(snippet: &DocumentationSnippet) {
69    if let Some(titulo_pagina) = &snippet.page_title {
70        println!("{}", format!("## {}", titulo_pagina).green().bold());
71    }
72
73    if let Some(titulo_codigo) = &snippet.code_title {
74        println!("{}", format!("▸ {}", titulo_codigo).cyan());
75    }
76
77    if let Some(descricao) = &snippet.code_description {
78        println!("  {}", descricao.dimmed().italic());
79    }
80
81    if let Some(blocos) = &snippet.code_list {
82        for bloco in blocos {
83            println!("```{}", bloco.language);
84            println!("{}", bloco.code);
85            println!("```");
86        }
87    }
88
89    if let Some(source) = &snippet.code_id {
90        println!("{}", source.blue().bold().dimmed());
91    }
92
93    println!();
94}
95
96// ─── CHAVES ───────────────────────────────────────────────────────────────────
97
98/// Prints all stored keys with 1-based indices and masked values.
99pub fn exibir_chaves_mascaradas(chaves: &[ChaveArmazenada], mascarar: impl Fn(&str) -> String) {
100    println!(
101        "{}",
102        format!("{} {}", chaves.len(), t(Mensagem::ContadorChaves))
103            .green()
104            .bold()
105    );
106    println!("{}", "─".repeat(60).dimmed());
107
108    let rotulo_adicionada = match idioma_atual() {
109        Idioma::English => "added:",
110        Idioma::Portugues => "adicionada:",
111    };
112
113    for (i, chave) in chaves.iter().enumerate() {
114        println!(
115            "  {}  {}  {}",
116            format!("[{}]", i + 1).cyan(),
117            mascarar(&chave.value).bold(),
118            format!("({} {})", rotulo_adicionada, chave.added_at).dimmed()
119        );
120    }
121}
122
123/// Prints the "no keys stored" hint message.
124pub fn exibir_nenhuma_chave() {
125    println!("{}", t(Mensagem::NenhumaChaveArmazenada).yellow());
126    println!("{}", t(Mensagem::UsarKeysAdd).cyan());
127}
128
129/// Prints the "no keys to remove" message.
130pub fn exibir_nenhuma_chave_para_remover() {
131    println!("{}", t(Mensagem::NenhumaChaveParaRemover).yellow());
132}
133
134/// Prints an invalid index error.
135pub fn exibir_indice_invalido(indice: usize, total: usize) {
136    println!(
137        "{}",
138        format!(
139            "{} {} {} {}.",
140            t(Mensagem::IndiceInvalido),
141            indice,
142            "/",
143            total
144        )
145        .red()
146    );
147}
148
149/// Prints the success message for `keys add`.
150pub fn exibir_chave_adicionada(caminho: &std::path::Path) {
151    println!(
152        "{} {}",
153        t(Mensagem::ChaveAdicionada),
154        caminho.display().to_string().green()
155    );
156}
157
158/// Prints the success message for `keys remove`.
159pub fn exibir_chave_removida(chave_mascarada: &str) {
160    println!(
161        "{} {}",
162        chave_mascarada.bold(),
163        t(Mensagem::ChaveRemovidaSucesso)
164    );
165}
166
167/// Prints the cancellation message for `keys clear`.
168pub fn exibir_operacao_cancelada() {
169    println!("{}", t(Mensagem::OperacaoCancelada).yellow());
170}
171
172/// Prints the success message for `keys clear`.
173pub fn exibir_chaves_removidas() {
174    println!("{}", t(Mensagem::TodasChavesRemovidas).green());
175}
176
177/// Prints an "XDG not supported" error for `keys path`.
178pub fn exibir_xdg_nao_suportado() {
179    println!("{}", t(Mensagem::SistemaXdgNaoSuportado).red());
180}
181
182/// Prints the success message for `keys import`.
183pub fn exibir_importacao_concluida(importadas: usize, total: usize) {
184    println!(
185        "{}",
186        format!(
187            "{}/{} {}",
188            importadas,
189            total,
190            t(Mensagem::ChavesImportadasSucesso)
191        )
192        .green()
193    );
194}