Skip to main content

context7_cli/
errors.rs

1/// Error types for the context7-cli library.
2///
3/// [`ErroContext7`] is used for all structured errors within the library.
4/// Binary code uses [`anyhow::Result`] for flexible propagation.
5use thiserror::Error;
6
7/// Structured errors for the Context7 API client.
8#[derive(Debug, Error)]
9pub enum ErroContext7 {
10    /// All available API keys have been exhausted after the given number of attempts.
11    #[error("No valid API key available after {tentativas} attempts")]
12    RetryEsgotado {
13        /// Number of attempts made before exhaustion.
14        tentativas: u32,
15    },
16
17    /// All keys failed due to authentication errors (401/403).
18    #[error("All API keys failed due to authentication errors")]
19    SemChavesApi,
20
21    /// The API returned an unexpected HTTP status code.
22    #[error("Invalid API response: status {status}")]
23    RespostaInvalida {
24        /// HTTP status code returned by the API.
25        status: u16,
26    },
27
28    /// The API returned HTTP 400 with an error message.
29    #[error("API returned error 400: {mensagem}")]
30    ApiRetornou400 {
31        /// Error message returned by the API.
32        mensagem: String,
33    },
34
35    /// The requested library was not found (HTTP 404).
36    #[error("Library not found: {library_id}")]
37    BibliotecaNaoEncontrada {
38        /// Library identifier that was not found.
39        library_id: String,
40    },
41
42    /// A keys operation failed (e.g., invalid index, no keys stored).
43    /// The caller already printed a user-friendly message; this signals exit code 1.
44    #[error("")]
45    OperacaoKeysFalhou,
46}
47
48/// Alias de `Result` para operações desta crate.
49///
50/// Simplifica assinaturas de funções que retornam [`ErroContext7`].
51pub type Resultado<T> = std::result::Result<T, ErroContext7>;
52
53#[cfg(test)]
54mod testes {
55    use super::*;
56
57    #[test]
58    fn testa_erro_sem_chaves_api_display() {
59        let erro = ErroContext7::SemChavesApi;
60        let mensagem = erro.to_string();
61        assert!(
62            !mensagem.is_empty(),
63            "SemChavesApi deve ter mensagem não-vazia"
64        );
65        assert!(
66            mensagem.to_lowercase().contains("key")
67                || mensagem.to_lowercase().contains("api")
68                || mensagem.to_lowercase().contains("auth"),
69            "Mensagem deve mencionar key/api/auth, obteve: {mensagem}"
70        );
71    }
72
73    #[test]
74    fn testa_erro_retry_esgotado_contem_numero_de_tentativas() {
75        let erro = ErroContext7::RetryEsgotado { tentativas: 3 };
76        let mensagem = erro.to_string();
77        assert!(
78            mensagem.contains('3'),
79            "Mensagem deve conter número de tentativas (3), obteve: {mensagem}"
80        );
81    }
82
83    #[test]
84    fn testa_erro_resposta_invalida_contem_status() {
85        let erro = ErroContext7::RespostaInvalida { status: 500 };
86        let mensagem = erro.to_string();
87        assert!(
88            mensagem.contains("500"),
89            "Mensagem deve conter código de status, obteve: {mensagem}"
90        );
91    }
92
93    #[test]
94    fn testa_erro_api_400_contem_texto_do_erro() {
95        let erro = ErroContext7::ApiRetornou400 {
96            mensagem: "Parâmetro inválido".to_string(),
97        };
98        let mensagem = erro.to_string();
99        assert!(
100            mensagem.contains("Parâmetro inválido"),
101            "Mensagem deve conter texto do erro, obteve: {mensagem}"
102        );
103    }
104
105    #[test]
106    fn testa_resultado_alias_propaga_erro_context7() {
107        fn falha() -> Resultado<()> {
108            Err(ErroContext7::SemChavesApi)
109        }
110        let resultado: Resultado<()> = falha();
111        assert!(resultado.is_err(), "Resultado deve ser Err");
112        let err = resultado.unwrap_err();
113        assert!(matches!(err, ErroContext7::SemChavesApi));
114    }
115}