nautilus_persistence/catalog/factory.rs
1// -------------------------------------------------------------------------------------------------
2// Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3// https://nautechsystems.io
4//
5// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6// You may not use this file except in compliance with the License.
7// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Catalog factory registry primitives.
17
18use std::sync::Arc;
19
20use ahash::AHashMap;
21use indexmap::IndexMap;
22use nautilus_core::Params;
23
24use crate::catalog::traits::DataCatalog;
25
26/// Conventional name of the Parquet catalog factory registration.
27pub const PARQUET_CATALOG_FACTORY_NAME: &str = "Parquet";
28
29/// Minimal connection-config supplied to [`CatalogFactory`].
30#[derive(Debug, Clone)]
31pub struct CatalogConnectConfig {
32 /// Resolved URI for the catalog backend (e.g. `file:///tmp/cat`, `s3://bucket/cat`).
33 pub uri: String,
34 /// Optional storage-backend options (credentials, region, ...) passed to `object_store`.
35 pub storage_options: Option<AHashMap<String, String>>,
36 /// Backend-specific catalog parameters.
37 pub params: Option<Params>,
38}
39
40impl CatalogConnectConfig {
41 /// Creates a new [`CatalogConnectConfig`].
42 #[must_use]
43 pub fn new(uri: impl Into<String>, storage_options: Option<AHashMap<String, String>>) -> Self {
44 Self {
45 uri: uri.into(),
46 storage_options,
47 params: None,
48 }
49 }
50
51 /// Builds a [`CatalogConnectConfig`] from `path` + optional `fs_protocol`.
52 #[must_use]
53 pub fn from_path_and_protocol(
54 path: &str,
55 fs_protocol: Option<&str>,
56 storage_options: Option<AHashMap<String, String>>,
57 ) -> Self {
58 let uri = match fs_protocol {
59 _ if path.contains("://") => path.to_string(),
60 Some(protocol) => format!("{protocol}://{path}"),
61 None => path.to_string(),
62 };
63 Self::new(uri, storage_options)
64 }
65}
66
67/// Factory for a named catalog backend.
68pub type CatalogFactory =
69 Arc<dyn Fn(&CatalogConnectConfig) -> anyhow::Result<DataCatalog> + Send + Sync>;
70
71/// Ordered registry of catalog factories keyed by name.
72pub type CatalogFactoryRegistry = IndexMap<String, CatalogFactory>;
73
74#[cfg(test)]
75mod tests {
76 use rstest::rstest;
77
78 use super::*;
79
80 #[rstest]
81 fn from_path_and_protocol_joins_scheme() {
82 let cfg = CatalogConnectConfig::from_path_and_protocol("bucket/cat", Some("s3"), None);
83 assert_eq!(cfg.uri, "s3://bucket/cat");
84
85 let cfg = CatalogConnectConfig::from_path_and_protocol("/tmp/cat", None, None);
86 assert_eq!(cfg.uri, "/tmp/cat");
87
88 let cfg = CatalogConnectConfig::from_path_and_protocol(
89 "postgres://user:pass@localhost/catalog",
90 Some("file"),
91 None,
92 );
93 assert_eq!(cfg.uri, "postgres://user:pass@localhost/catalog");
94 }
95}