apibrasil 1.0.0

SDK oficial Rust da plataforma APIBrasil: WhatsApp, SMS, consultas de CPF/CNPJ, veiculos, CEP, correios, pagamentos PIX/boleto e mais.
Documentation
//! Bases compartilhadas pelos serviços da SDK: [`BaseService`] (atalhos
//! HTTP), [`DeviceProxyService`] (rotas device-based) e [`CreditService`]
//! (consultas por crédito).

use std::sync::Arc;

use crate::core::{
    CreditResponse, DeviceResponse, HttpClient, IntoBody, Json, Method, RequestOptions, Result,
};

pub mod data;
pub mod messaging;
pub mod platform;

/// Base de todos os serviços — guarda o cliente HTTP compartilhado e as
/// opções em vigor, e expõe os atalhos por verbo.
#[derive(Clone, Debug)]
pub struct BaseService {
    client: Arc<HttpClient>,
    options: RequestOptions,
}

impl BaseService {
    /// Cria a base sobre um cliente HTTP já configurado.
    pub fn new(client: Arc<HttpClient>) -> Self {
        Self {
            client,
            options: RequestOptions::default(),
        }
    }

    /// Cliente HTTP compartilhado.
    pub fn http(&self) -> &Arc<HttpClient> {
        &self.client
    }

    /// Opções aplicadas a todas as chamadas deste serviço.
    pub fn options(&self) -> &RequestOptions {
        &self.options
    }

    /// Devolve uma cópia do serviço que usa `options` em todas as
    /// chamadas.
    pub fn with_options(&self, options: RequestOptions) -> Self {
        Self {
            client: self.client.clone(),
            options,
        }
    }

    /// Monta a URL completa de um caminho.
    pub fn url(&self, path: &str) -> String {
        self.client.url(path)
    }

    /// Executa uma requisição arbitrária no gateway.
    pub async fn request(&self, method: Method, path: &str, body: impl IntoBody) -> Result<Json> {
        self.client
            .request_json(method, path, body.into_body(), &self.options)
            .await
    }

    /// `GET path`.
    pub async fn get(&self, path: &str) -> Result<Json> {
        self.client.get(path, &self.options).await
    }

    /// `GET path` com a query mesclada às opções do serviço.
    pub async fn get_query(&self, path: &str, query: impl IntoBody) -> Result<Json> {
        let options = self.options.merge_query(query.into_body());
        self.client.get(path, &options).await
    }

    /// `POST path`.
    pub async fn post(&self, path: &str, body: impl IntoBody) -> Result<Json> {
        self.client
            .post(path, body.into_body(), &self.options)
            .await
    }

    /// `PUT path`.
    pub async fn put(&self, path: &str, body: impl IntoBody) -> Result<Json> {
        self.client.put(path, body.into_body(), &self.options).await
    }

    /// `PATCH path`.
    pub async fn patch(&self, path: &str, body: impl IntoBody) -> Result<Json> {
        self.client
            .patch(path, body.into_body(), &self.options)
            .await
    }

    /// `DELETE path`.
    pub async fn delete(&self, path: &str, body: impl IntoBody) -> Result<Json> {
        self.client
            .delete(path, body.into_body(), &self.options)
            .await
    }

    /// Baixa os bytes crus de uma rota (PDF, imagens...).
    pub async fn download(&self, path: &str) -> Result<Vec<u8>> {
        self.client
            .bytes(Method::Get, path, None, &self.options)
            .await
    }
}

/// Base dos serviços *device-based* do gateway
/// (`POST /api/v2/{servico}/{action}`). Exigem `Authorization: Bearer` +
/// header `DeviceToken`.
///
/// Todos expõem [`request`](Self::request) como porta de saída genérica —
/// as actions são dinâmicas por provedor. As conhecidas estão em
/// [`crate::generated::SERVICE_ACTIONS`]; a documentação completa fica em
/// <https://doc.apibrasil.io>.
#[derive(Clone, Debug)]
pub struct DeviceProxyService {
    base: BaseService,
    name: String,
}

impl DeviceProxyService {
    /// Cria um serviço device-based para `name`.
    pub fn new(client: Arc<HttpClient>, name: impl Into<String>) -> Self {
        Self {
            base: BaseService::new(client),
            name: name.into(),
        }
    }

    /// Serviço do gateway (ex: `whatsapp`, `sms`, `cep`).
    pub fn name(&self) -> &str {
        &self.name
    }

    /// Devolve uma cópia do serviço que usa `options` em todas as
    /// chamadas.
    pub fn with_options(&self, options: RequestOptions) -> Self {
        Self {
            base: self.base.with_options(options),
            name: self.name.clone(),
        }
    }

    /// Executa uma action do serviço: `POST /{servico}/{action}`.
    pub async fn request(&self, action: &str, body: impl IntoBody) -> Result<DeviceResponse> {
        let path = self.path(action);
        Ok(DeviceResponse(self.base.post(&path, body).await?))
    }

    /// Executa a mesma action de forma assíncrona via fila:
    /// `POST /{servico}/{action}/queue`.
    pub async fn queue(&self, action: &str, body: impl IntoBody) -> Result<DeviceResponse> {
        let path = format!("{}/queue", self.path(action));
        Ok(DeviceResponse(self.base.post(&path, body).await?))
    }

    fn path(&self, action: &str) -> String {
        let action = action.trim_matches('/');
        if action.is_empty() {
            self.name.clone()
        } else {
            format!("{}/{}", self.name, action)
        }
    }
}

impl std::ops::Deref for DeviceProxyService {
    type Target = BaseService;

    fn deref(&self) -> &BaseService {
        &self.base
    }
}

/// Base das consultas por crédito (`POST /consulta/{servico}/credits`).
///
/// Não usam `DeviceToken` — debitam o saldo/créditos da conta.
#[derive(Clone, Debug)]
pub struct CreditService {
    base: BaseService,
}

impl CreditService {
    /// Cria a base das consultas por crédito.
    pub fn new(client: Arc<HttpClient>) -> Self {
        Self {
            base: BaseService::new(client),
        }
    }

    /// Devolve uma cópia do serviço que usa `options` em todas as
    /// chamadas.
    pub fn with_options(&self, options: RequestOptions) -> Self {
        Self {
            base: self.base.with_options(options),
        }
    }

    /// Executa uma consulta: `POST /consulta/{servico}/credits`.
    pub async fn request(&self, service: &str, body: impl IntoBody) -> Result<CreditResponse> {
        let path = format!("consulta/{service}/credits");
        Ok(CreditResponse(self.base.post(&path, body).await?))
    }

    /// Consulta os créditos disponíveis de um serviço:
    /// `GET /consulta/{servico}/credits`.
    pub async fn credits(&self, service: &str) -> Result<CreditResponse> {
        let path = format!("consulta/{service}/credits");
        Ok(CreditResponse(self.base.get(&path).await?))
    }

    /// Converte uma resposta JSON no envelope das consultas por crédito —
    /// atalho para rotas fora do padrão `/consulta/{servico}/credits`.
    pub(crate) async fn credit_post(
        &self,
        path: &str,
        body: impl IntoBody,
    ) -> Result<CreditResponse> {
        Ok(CreditResponse(self.base.post(path, body).await?))
    }
}

impl std::ops::Deref for CreditService {
    type Target = BaseService;

    fn deref(&self) -> &BaseService {
        &self.base
    }
}