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("Nenhuma chave de API válida disponível após {tentativas} tentativas")]
12    RetryEsgotado {
13        /// Número de tentativas realizadas antes do esgotamento.
14        tentativas: u32,
15    },
16
17    /// All keys failed due to authentication errors (401/403).
18    #[error("Todas as chaves de API falharam por autenticação")]
19    SemChavesApi,
20
21    /// The API returned an unexpected HTTP status code.
22    #[error("Resposta inválida da API: status {status}")]
23    RespostaInvalida {
24        /// Código de status HTTP retornado pela API.
25        status: u16,
26    },
27
28    /// The API returned HTTP 400 with an error message.
29    #[error("API retornou erro 400: {mensagem}")]
30    ApiRetornou400 {
31        /// Mensagem de erro retornada pela API.
32        mensagem: String,
33    },
34}
35
36/// Alias de `Result` para operações desta crate.
37///
38/// Simplifica assinaturas de funções que retornam [`ErroContext7`].
39pub type Resultado<T> = std::result::Result<T, ErroContext7>;
40
41#[cfg(test)]
42mod testes {
43    use super::*;
44
45    #[test]
46    fn testa_erro_sem_chaves_api_display() {
47        let erro = ErroContext7::SemChavesApi;
48        let mensagem = erro.to_string();
49        assert!(
50            !mensagem.is_empty(),
51            "SemChavesApi deve ter mensagem não-vazia"
52        );
53        assert!(
54            mensagem.to_lowercase().contains("chave")
55                || mensagem.to_lowercase().contains("api")
56                || mensagem.to_lowercase().contains("auth"),
57            "Mensagem deve mencionar chaves/api/auth, obteve: {mensagem}"
58        );
59    }
60
61    #[test]
62    fn testa_erro_retry_esgotado_contem_numero_de_tentativas() {
63        let erro = ErroContext7::RetryEsgotado { tentativas: 3 };
64        let mensagem = erro.to_string();
65        assert!(
66            mensagem.contains('3'),
67            "Mensagem deve conter número de tentativas (3), obteve: {mensagem}"
68        );
69    }
70
71    #[test]
72    fn testa_erro_resposta_invalida_contem_status() {
73        let erro = ErroContext7::RespostaInvalida { status: 500 };
74        let mensagem = erro.to_string();
75        assert!(
76            mensagem.contains("500"),
77            "Mensagem deve conter código de status, obteve: {mensagem}"
78        );
79    }
80
81    #[test]
82    fn testa_erro_api_400_contem_texto_do_erro() {
83        let erro = ErroContext7::ApiRetornou400 {
84            mensagem: "Parâmetro inválido".to_string(),
85        };
86        let mensagem = erro.to_string();
87        assert!(
88            mensagem.contains("Parâmetro inválido"),
89            "Mensagem deve conter texto do erro, obteve: {mensagem}"
90        );
91    }
92
93    #[test]
94    fn testa_resultado_alias_propaga_erro_context7() {
95        fn falha() -> Resultado<()> {
96            Err(ErroContext7::SemChavesApi)
97        }
98        let resultado: Resultado<()> = falha();
99        assert!(resultado.is_err(), "Resultado deve ser Err");
100        let err = resultado.unwrap_err();
101        assert!(matches!(err, ErroContext7::SemChavesApi));
102    }
103}