#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
#![cfg(feature = "unity-catalog")]
use crate::catalog::CatalogError;
use crate::catalog::rest_catalog_wrapper::KrishivRestCatalog;
#[derive(Debug, Clone)]
pub struct UnityCatalog {
inner: KrishivRestCatalog,
catalog_name: String,
}
impl UnityCatalog {
pub async fn new(
host: &str,
catalog_name: &str,
token: Option<&str>,
) -> Result<Self, CatalogError> {
let iceberg_uri = format!(
"{}/api/2.1/unity-catalog/iceberg/",
host.trim_end_matches('/')
);
let inner = KrishivRestCatalog::new(&iceberg_uri, catalog_name, token).await?;
Ok(Self {
inner,
catalog_name: catalog_name.to_owned(),
})
}
pub async fn from_env() -> Result<Self, CatalogError> {
let host = std::env::var("KRISHIV_UNITY_HOST").map_err(|_| {
CatalogError::InvalidConfiguration {
message: "KRISHIV_UNITY_HOST is required for the Unity Catalog backend".into(),
}
})?;
let catalog_name =
std::env::var("KRISHIV_UNITY_CATALOG_NAME").unwrap_or_else(|_| "main".into());
let token = std::env::var("KRISHIV_UNITY_TOKEN").ok();
Self::new(&host, &catalog_name, token.as_deref()).await
}
pub fn catalog_name(&self) -> &str {
&self.catalog_name
}
pub fn as_rest_catalog(&self) -> &KrishivRestCatalog {
&self.inner
}
}
impl std::ops::Deref for UnityCatalog {
type Target = KrishivRestCatalog;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_env_fails_without_host() {
unsafe {
std::env::remove_var("KRISHIV_UNITY_HOST");
}
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
let result = rt.block_on(UnityCatalog::from_env());
assert!(result.is_err());
let err = result.unwrap_err();
match err {
CatalogError::InvalidConfiguration { message } => {
assert!(message.contains("KRISHIV_UNITY_HOST"), "{message}");
}
other => panic!("expected InvalidConfiguration, got: {other}"),
}
}
#[test]
fn catalog_name_default_is_main() {
unsafe {
std::env::set_var("KRISHIV_UNITY_HOST", "https://test.unitycatalog.invalid");
std::env::remove_var("KRISHIV_UNITY_CATALOG_NAME");
std::env::remove_var("KRISHIV_UNITY_TOKEN");
}
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
let catalog = rt
.block_on(UnityCatalog::from_env())
.expect("construction must succeed even without network");
assert_eq!(catalog.catalog_name(), "main");
unsafe {
std::env::remove_var("KRISHIV_UNITY_HOST");
}
}
}