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#[cfg(test)]
49mod testes {
50    use super::*;
51
52    #[test]
53    fn testa_erro_sem_chaves_api_display() {
54        let erro = ErroContext7::SemChavesApi;
55        let mensagem = erro.to_string();
56        assert!(
57            !mensagem.is_empty(),
58            "SemChavesApi deve ter mensagem não-vazia"
59        );
60        assert!(
61            mensagem.to_lowercase().contains("key")
62                || mensagem.to_lowercase().contains("api")
63                || mensagem.to_lowercase().contains("auth"),
64            "Mensagem deve mencionar key/api/auth, obteve: {mensagem}"
65        );
66    }
67
68    #[test]
69    fn testa_erro_retry_esgotado_contem_numero_de_tentativas() {
70        let erro = ErroContext7::RetryEsgotado { tentativas: 3 };
71        let mensagem = erro.to_string();
72        assert!(
73            mensagem.contains('3'),
74            "Mensagem deve conter número de tentativas (3), obteve: {mensagem}"
75        );
76    }
77
78    #[test]
79    fn testa_erro_resposta_invalida_contem_status() {
80        let erro = ErroContext7::RespostaInvalida { status: 500 };
81        let mensagem = erro.to_string();
82        assert!(
83            mensagem.contains("500"),
84            "Mensagem deve conter código de status, obteve: {mensagem}"
85        );
86    }
87
88    #[test]
89    fn testa_erro_api_400_contem_texto_do_erro() {
90        let erro = ErroContext7::ApiRetornou400 {
91            mensagem: "Parâmetro inválido".to_string(),
92        };
93        let mensagem = erro.to_string();
94        assert!(
95            mensagem.contains("Parâmetro inválido"),
96            "Mensagem deve conter texto do erro, obteve: {mensagem}"
97        );
98    }
99
100    #[test]
101    fn testa_resultado_alias_propaga_erro_context7() {
102        fn falha() -> Result<(), ErroContext7> {
103            Err(ErroContext7::SemChavesApi)
104        }
105        let resultado: Result<(), ErroContext7> = falha();
106        assert!(resultado.is_err(), "Resultado deve ser Err");
107        let err = resultado.unwrap_err();
108        assert!(matches!(err, ErroContext7::SemChavesApi));
109    }
110}