lance_csr_catalog/namespace/
directory.rs1use async_trait::async_trait;
4use lance_namespace::models::{DescribeTableRequest, DescribeTableResponse};
5use lance_namespace::{Error as NamespaceError, LanceNamespace, Result};
6
7#[derive(Debug, Clone)]
9pub struct DirNamespace {
10 base_uri: String,
11}
12
13impl DirNamespace {
14 pub fn new(base_uri: impl Into<String>) -> Self {
18 let uri = base_uri.into();
19 let clean_uri = uri.trim_end_matches('/').to_string();
20 Self {
21 base_uri: clean_uri,
22 }
23 }
24
25 pub fn base_uri(&self) -> &str {
27 &self.base_uri
28 }
29}
30
31#[async_trait]
32impl LanceNamespace for DirNamespace {
33 fn namespace_id(&self) -> String {
34 format!("DirNamespace {{ base_uri: '{}' }}", self.base_uri)
35 }
36
37 async fn describe_table(&self, request: DescribeTableRequest) -> Result<DescribeTableResponse> {
38 let id = request.id.ok_or_else(|| {
39 NamespaceError::invalid_input(
40 "DirNamespace requires the table identifier to be provided",
41 )
42 })?;
43
44 if id.len() != 1 {
45 return Err(NamespaceError::invalid_input(format!(
46 "DirNamespace expects identifiers with a single component, got {:?}",
47 id
48 )));
49 }
50
51 let table_name = &id[0];
52 let location = format!("{}/{}.lance", self.base_uri, table_name);
53
54 let mut response = DescribeTableResponse::new();
55 response.location = Some(location);
56 response.storage_options = None;
57 Ok(response)
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[tokio::test]
66 async fn describe_table_returns_clean_location() {
67 let namespace = DirNamespace::new("s3://bucket/path/");
68 let mut request = DescribeTableRequest::new();
69 request.id = Some(vec!["users".to_string()]);
70
71 let response = namespace.describe_table(request).await.unwrap();
72 assert_eq!(
73 response.location.as_deref(),
74 Some("s3://bucket/path/users.lance")
75 );
76 }
77
78 #[tokio::test]
79 async fn describe_table_rejects_missing_identifier() {
80 let namespace = DirNamespace::new("file:///tmp");
81 let request = DescribeTableRequest::new();
82
83 let err = namespace.describe_table(request).await.unwrap_err();
84 assert!(
85 err.to_string()
86 .contains("DirNamespace requires the table identifier"),
87 "unexpected error: {err}"
88 );
89 }
90
91 #[tokio::test]
92 async fn describe_table_rejects_multi_component_identifier() {
93 let namespace = DirNamespace::new("memory://namespace");
94 let mut request = DescribeTableRequest::new();
95 request.id = Some(vec!["foo".into(), "bar".into()]);
96
97 let err = namespace.describe_table(request).await.unwrap_err();
98 assert!(
99 err.to_string()
100 .contains("expects identifiers with a single component"),
101 "unexpected error: {err}"
102 );
103 }
104}