unitycatalog-rs 0.0.1

Rust-based clients for interacting with Unity Catalog
Documentation
use reqwest::{Client, header::HeaderMap, Method};
use url::Url;
use crate::errors::{UCRSError, UCRSResult};
use serde::de::DeserializeOwned;
use serde::Serialize;

pub struct RequestClient {
    pub base_url: Url,
    client: Client
}

impl RequestClient {
    pub fn new(base_url: &str, disable_ssl: bool) -> UCRSResult<Self> {
        let base_url = Url::parse(base_url)
            .map_err(|e| UCRSError::MalformedURL(e))?;

        let client = Client::builder()
            .danger_accept_invalid_certs(disable_ssl)
            .build()
            .map_err(|e| {
                UCRSError::ClientBuildError(e)
            })?;

        Ok(Self {
            base_url,
            client
        })
    }

    pub fn new_with_headers(base_url: &str, disable_ssl: bool, headers: HeaderMap) -> UCRSResult<Self> {
        let base_url = Url::parse(base_url)
            .map_err(|e| UCRSError::MalformedURL(e))?;

        let client = Client::builder()
            .danger_accept_invalid_certs(disable_ssl)
            .default_headers(headers)
            .build()
            .map_err(|e| {
                UCRSError::ClientBuildError(e)
            })?;

        Ok(Self {
            base_url,
            client
        })
    }

    pub fn new_with_client(base_url: &str, client: Client) -> UCRSResult<Self> {
        let base_url = Url::parse(base_url)
            .map_err(|e| UCRSError::MalformedURL(e))?;

        Ok(Self {
            base_url,
            client
        })

    }

    pub async fn get<B, R>(&self, route: Url, body: Option<B>) -> UCRSResult<R>
        where 
            B: Serialize + std::fmt::Debug,
            R: DeserializeOwned {
        self.request(route, Method::GET,  body).await
    }

    pub async fn post<B, R>(&self, route: Url, body: Option<B>) -> UCRSResult<R>
        where 
            B: Serialize + std::fmt::Debug,
            R: DeserializeOwned {
        self.request(route, Method::POST,  body).await
    }

    pub async fn delete<B, R>(&self, route: Url, body: Option<B>) -> UCRSResult<R>
        where 
            B: Serialize + std::fmt::Debug,
            R: DeserializeOwned {
        self.request(route, Method::DELETE,  body).await
    }

    pub async fn patch<B, R>(&self, route: Url, body: Option<B>) -> UCRSResult<R>
        where 
            B: Serialize + std::fmt::Debug,
            R: DeserializeOwned {
        self.request(route, Method::PATCH,  body).await
    }

    #[tracing::instrument(skip(self))]
    async fn request<B, R>(&self, route: Url, method: reqwest::Method, body: Option<B>) -> UCRSResult<R> 
        where 
            B: Serialize + std::fmt::Debug,
            R: DeserializeOwned{
        let request = self.client.request(method, route);
        let body = body.map(|b| {
            serde_json::to_string(&b).map_err(|be| {
                UCRSError::JSONFormattingError(be)
            })
        });

        let request = match body {
            Some(b) => {
                let b = b?;
                eprintln!("Body is {}", b);
                request
                    .body(b)
                    .header("Content-Type", "application/json")
                    .header("Accept", "application/json")
            },
            None => request
        };
        
        let response = request.send().await
            .map_err(|e| UCRSError::RequestError(e))?;

        if let Err(e) = response.error_for_status_ref() {
            let response_body = response.text().await
                .map_err(|e| UCRSError::RequestError(e))?;
            Err(UCRSError::RequestErrorWithResponse(e, response_body))
        } else {
            let response_body = response.json::<R>().await
                .map_err(|e| UCRSError::JSONParsingError(e))?;
            Ok(response_body)
        }
    }
}