snix_castore/directoryservice/
from_addr.rs1use std::sync::Arc;
2
3use url::Url;
4
5use crate::composition::{
6 CompositionContext, DeserializeWithRegistry, REG, ServiceBuilder, with_registry,
7};
8
9use super::DirectoryService;
10
11pub async fn from_addr(
24 uri: &str,
25) -> Result<Arc<dyn DirectoryService>, Box<dyn std::error::Error + Send + Sync>> {
26 #[allow(unused_mut)]
27 let mut url = Url::parse(uri).map_err(|e| format!("unable to parse url: {e}"))?;
28
29 let directory_service_config = with_registry(®, || {
30 <DeserializeWithRegistry<Box<dyn ServiceBuilder<Output = dyn DirectoryService>>>>::try_from(
31 url,
32 )
33 })?
34 .0;
35 let directory_service = directory_service_config
36 .build("anonymous", &CompositionContext::blank(®))
37 .await?;
38
39 Ok(directory_service)
40}
41
42#[cfg(test)]
43mod tests {
44 use std::sync::LazyLock;
45
46 use super::from_addr;
47 use rstest::rstest;
48 use tempfile::TempDir;
49
50 static TMPDIR_REDB_1: LazyLock<TempDir> = LazyLock::new(|| TempDir::new().unwrap());
51 static TMPDIR_REDB_2: LazyLock<TempDir> = LazyLock::new(|| TempDir::new().unwrap());
52
53 #[rstest]
54 #[case::unsupported_scheme("http://foo.example/test", false)]
56 #[case::redb_invalid_missing_path("redb://", false)]
58 #[case::redb_invalid_root("redb:///", false)]
60 #[case::redb_invalid_host("redb://foo.example", false)]
62 #[case::redb_valid_path(&format!("redb://{}", &TMPDIR_REDB_1.path().join("foo").to_str().unwrap()), true)]
64 #[case::redb_invalid_host_with_valid_path(&format!("redb://foo.example{}", &TMPDIR_REDB_2.path().join("bar").to_str().unwrap()), false)]
66 #[case::redb_memory_valid("redb+memory:", true)]
68 #[case::redb_memory_invalid_path("redb+memory:/foo/bar", false)]
70 #[case::redb_memory_invalid_authority("redb+memory://", false)]
72 #[case::redb_memory_invalid_authority_path("redb+memory:///foo/bar", false)]
74 #[case::grpc_valid_unix_socket("grpc+unix:///path/to/somewhere", true)]
76 #[case::grpc_invalid_unix_socket_and_host("grpc+unix://host.example/path/to/somewhere", false)]
78 #[case::grpc_valid_ipv6_localhost_port_12345("grpc+http://[::1]:12345", true)]
80 #[case::grpc_valid_http_host_without_port("grpc+http://localhost", true)]
82 #[case::grpc_valid_https_host_without_port("grpc+https://localhost", true)]
84 #[case::grpc_invalid_host_and_path("grpc+http://localhost/some-path", false)]
86 #[cfg_attr(
88 feature = "xp-composition-url-refs",
89 case::anonymous_url_composition("cache://?near=redb%2bmemory:&far=redb%2bmemory:", true)
90 )]
91 #[cfg_attr(
93 not(feature = "xp-composition-url-refs"),
94 case::anonymous_url_composition("cache://?near=redb%2bmemory:&far=redb%2bmemory:", false)
95 )]
96 #[cfg_attr(
98 all(feature = "cloud", feature = "integration"),
99 case::bigtable_valid_url(
100 "bigtable://instance-1?project_id=project-1&table_name=table-1&family_name=cf1",
101 true
102 )
103 )]
104 #[cfg_attr(
106 all(feature = "cloud", feature = "integration"),
107 case::bigtable_valid_url(
108 "bigtable://instance-1?project_id=project-1&table_name=table-1&family_name=cf1&channel_size=10&timeout=10",
109 true
110 )
111 )]
112 #[cfg_attr(
114 all(feature = "cloud", feature = "integration"),
115 case::bigtable_invalid_url("bigtable://instance-1", false)
116 )]
117 #[tokio::test]
118 async fn test_from_addr_tokio(#[case] uri_str: &str, #[case] exp_succeed: bool) {
119 if exp_succeed {
120 from_addr(uri_str).await.expect("should succeed");
121 } else {
122 assert!(from_addr(uri_str).await.is_err(), "should fail");
123 }
124 }
125}