snix_build/buildservice/
from_addr.rs1use super::{BuildService, DummyBuildService, grpc::GRPCBuildService};
2use snix_castore::{blobservice::BlobService, directoryservice::DirectoryService};
3use url::Url;
4
5#[cfg(target_os = "linux")]
6use super::oci::OCIBuildService;
7
8#[cfg_attr(target_os = "macos", allow(unused_variables))]
18pub async fn from_addr<BS, DS>(
19 uri: &str,
20 blob_service: BS,
21 directory_service: DS,
22) -> std::io::Result<Box<dyn BuildService>>
23where
24 BS: BlobService + Send + Sync + Clone + 'static,
25 DS: DirectoryService + Send + Sync + Clone + 'static,
26{
27 let url = Url::parse(uri)
28 .map_err(|e| std::io::Error::other(format!("unable to parse url: {}", e)))?;
29
30 Ok(match url.scheme() {
31 "dummy" => Box::<DummyBuildService>::default(),
33 #[cfg(target_os = "linux")]
34 "oci" => {
35 if url.path().is_empty() {
37 Err(std::io::Error::other("oci needs a bundle dir as path"))?
38 }
39
40 Box::new(OCIBuildService::new(
43 url.path().into(),
44 blob_service,
45 directory_service,
46 ))
47 }
48 scheme => {
49 if scheme.starts_with("grpc+") {
50 let client = crate::proto::build_service_client::BuildServiceClient::new(
51 snix_castore::tonic::channel_from_url(&url)
52 .await
53 .map_err(std::io::Error::other)?,
54 );
55 Box::new(GRPCBuildService::from_client(client))
58 } else {
59 Err(std::io::Error::other(format!(
60 "unknown scheme: {}",
61 url.scheme()
62 )))?
63 }
64 }
65 })
66}
67
68#[cfg(test)]
69mod tests {
70 use super::from_addr;
71 use rstest::rstest;
72 use snix_castore::{
73 blobservice::{BlobService, MemoryBlobService},
74 directoryservice::{DirectoryService, MemoryDirectoryService},
75 };
76 use std::sync::Arc;
77 #[cfg(target_os = "linux")]
78 use std::sync::LazyLock;
79 #[cfg(target_os = "linux")]
80 use tempfile::TempDir;
81
82 #[cfg(target_os = "linux")]
83 static TMPDIR_OCI_1: LazyLock<TempDir> = LazyLock::new(|| TempDir::new().unwrap());
84
85 #[rstest]
86 #[case::unsupported_scheme("http://foo.example/test", false)]
88 #[case::valid_dummy("dummy://", true)]
90 #[case::grpc_valid_unix_socket("grpc+unix:///path/to/somewhere", true)]
92 #[case::grpc_invalid_unix_socket_and_host("grpc+unix://host.example/path/to/somewhere", false)]
94 #[case::grpc_valid_ipv6_localhost_port_12345("grpc+http://[::1]:12345", true)]
96 #[case::grpc_valid_http_host_without_port("grpc+http://localhost", true)]
98 #[case::grpc_valid_https_host_without_port("grpc+https://localhost", true)]
100 #[case::grpc_invalid_host_and_path("grpc+http://localhost/some-path", false)]
102 #[cfg_attr(target_os = "linux", case::oci_missing_bundle_dir("oci://", false))]
104 #[cfg_attr(target_os = "linux", case::oci_bundle_path(&format!("oci://{}", TMPDIR_OCI_1.path().to_str().unwrap()), true))]
106 #[tokio::test]
107 async fn test_from_addr(#[case] uri_str: &str, #[case] exp_succeed: bool) {
108 let blob_service: Arc<dyn BlobService> = Arc::from(MemoryBlobService::default());
109 let directory_service: Arc<dyn DirectoryService> =
110 Arc::from(MemoryDirectoryService::default());
111
112 let resp = from_addr(uri_str, blob_service, directory_service).await;
113
114 if exp_succeed {
115 resp.expect("should succeed");
116 } else {
117 assert!(resp.is_err(), "should fail");
118 }
119 }
120}