context7-cli 0.3.0

Search library documentation from your terminal — zero runtime, bilingual (EN/PT), multi-key rotation
Documentation
/// Error types for the context7-cli library.
///
/// [`ErroContext7`] is used for all structured errors within the library.
/// Binary code uses [`anyhow::Result`] for flexible propagation.
use thiserror::Error;

/// Structured errors for the Context7 API client.
#[derive(Debug, Error)]
pub enum ErroContext7 {
    /// All available API keys have been exhausted after the given number of attempts.
    #[error("No valid API key available after {tentativas} attempts")]
    RetryEsgotado {
        /// Number of attempts made before exhaustion.
        tentativas: u32,
    },

    /// All keys failed due to authentication errors (401/403).
    #[error("All API keys failed due to authentication errors")]
    SemChavesApi,

    /// The API returned an unexpected HTTP status code.
    #[error("Invalid API response: status {status}")]
    RespostaInvalida {
        /// HTTP status code returned by the API.
        status: u16,
    },

    /// The API returned HTTP 400 with an error message.
    #[error("API returned error 400: {mensagem}")]
    ApiRetornou400 {
        /// Error message returned by the API.
        mensagem: String,
    },

    /// The requested library was not found (HTTP 404).
    #[error("Library not found: {library_id}")]
    BibliotecaNaoEncontrada {
        /// Library identifier that was not found.
        library_id: String,
    },

    /// A keys operation failed (e.g., invalid index, no keys stored).
    /// The caller already printed a user-friendly message; this signals exit code 1.
    #[error("")]
    OperacaoKeysFalhou,
}

/// Alias de `Result` para operações desta crate.
///
/// Simplifica assinaturas de funções que retornam [`ErroContext7`].
pub type Resultado<T> = std::result::Result<T, ErroContext7>;

#[cfg(test)]
mod testes {
    use super::*;

    #[test]
    fn testa_erro_sem_chaves_api_display() {
        let erro = ErroContext7::SemChavesApi;
        let mensagem = erro.to_string();
        assert!(
            !mensagem.is_empty(),
            "SemChavesApi deve ter mensagem não-vazia"
        );
        assert!(
            mensagem.to_lowercase().contains("key")
                || mensagem.to_lowercase().contains("api")
                || mensagem.to_lowercase().contains("auth"),
            "Mensagem deve mencionar key/api/auth, obteve: {mensagem}"
        );
    }

    #[test]
    fn testa_erro_retry_esgotado_contem_numero_de_tentativas() {
        let erro = ErroContext7::RetryEsgotado { tentativas: 3 };
        let mensagem = erro.to_string();
        assert!(
            mensagem.contains('3'),
            "Mensagem deve conter número de tentativas (3), obteve: {mensagem}"
        );
    }

    #[test]
    fn testa_erro_resposta_invalida_contem_status() {
        let erro = ErroContext7::RespostaInvalida { status: 500 };
        let mensagem = erro.to_string();
        assert!(
            mensagem.contains("500"),
            "Mensagem deve conter código de status, obteve: {mensagem}"
        );
    }

    #[test]
    fn testa_erro_api_400_contem_texto_do_erro() {
        let erro = ErroContext7::ApiRetornou400 {
            mensagem: "Parâmetro inválido".to_string(),
        };
        let mensagem = erro.to_string();
        assert!(
            mensagem.contains("Parâmetro inválido"),
            "Mensagem deve conter texto do erro, obteve: {mensagem}"
        );
    }

    #[test]
    fn testa_resultado_alias_propaga_erro_context7() {
        fn falha() -> Resultado<()> {
            Err(ErroContext7::SemChavesApi)
        }
        let resultado: Resultado<()> = falha();
        assert!(resultado.is_err(), "Resultado deve ser Err");
        let err = resultado.unwrap_err();
        assert!(matches!(err, ErroContext7::SemChavesApi));
    }
}