snix_build/buildservice/
from_addr.rs1#[cfg(target_os = "linux")]
2use crate::buildservice::bwrap::BubblewrapBuildService;
3
4use super::{BuildService, DummyBuildService, grpc::GRPCBuildService};
5use snix_castore::{blobservice::BlobService, directoryservice::DirectoryService};
6use url::Url;
7
8#[cfg(target_os = "linux")]
9use super::oci::OCIBuildService;
10#[cfg(all(not(target_os = "linux"), doc))]
11struct OCIBuildService;
12#[cfg(all(not(target_os = "linux"), doc))]
13struct BubblewrapBuildService;
14
15#[cfg_attr(target_os = "macos", allow(unused_variables))]
26pub async fn from_addr<BS, DS>(
27 uri: &str,
28 blob_service: BS,
29 directory_service: DS,
30) -> std::io::Result<Box<dyn BuildService>>
31where
32 BS: BlobService + Send + Sync + Clone + 'static,
33 DS: DirectoryService + Send + Sync + Clone + 'static,
34{
35 let url =
36 Url::parse(uri).map_err(|e| std::io::Error::other(format!("unable to parse url: {e}")))?;
37
38 Ok(match url.scheme() {
39 "dummy" => Box::<DummyBuildService>::default(),
41 #[cfg(target_os = "linux")]
42 "oci" => {
43 if url.path().is_empty() {
45 Err(std::io::Error::other("oci needs a bundle dir as path"))?
46 }
47
48 Box::new(OCIBuildService::new(
51 url.path().into(),
52 blob_service,
53 directory_service,
54 ))
55 }
56 #[cfg(target_os = "linux")]
57 "bwrap" => {
58 if url.path().is_empty() {
60 Err(std::io::Error::other("bwap needs a bundle dir as path"))?
61 }
62
63 Box::new(BubblewrapBuildService::new(
64 url.path().into(),
65 blob_service,
66 directory_service,
67 ))
68 }
69 scheme => {
70 if scheme.starts_with("grpc+") {
71 let client = crate::proto::build_service_client::BuildServiceClient::new(
72 snix_castore::tonic::channel_from_url(&url)
73 .await
74 .map_err(std::io::Error::other)?,
75 );
76 Box::new(GRPCBuildService::from_client(client))
79 } else {
80 Err(std::io::Error::other(format!(
81 "unknown scheme: {}",
82 url.scheme()
83 )))?
84 }
85 }
86 })
87}
88
89#[cfg(test)]
90mod tests {
91 use super::from_addr;
92 use rstest::rstest;
93 use snix_castore::blobservice::{BlobService, MemoryBlobService};
94 use std::sync::Arc;
95 #[cfg(target_os = "linux")]
96 use std::sync::LazyLock;
97 #[cfg(target_os = "linux")]
98 use tempfile::TempDir;
99
100 #[cfg(target_os = "linux")]
101 static TMPDIR_OCI_1: LazyLock<TempDir> = LazyLock::new(|| TempDir::new().unwrap());
102
103 #[rstest]
104 #[case::unsupported_scheme("http://foo.example/test", false)]
106 #[case::valid_dummy("dummy://", true)]
108 #[case::grpc_valid_unix_socket("grpc+unix:/path/to/somewhere", true)]
110 #[case::grpc_invalid_unix_socket_and_host("grpc+unix://host.example/path/to/somewhere", false)]
112 #[case::grpc_valid_ipv6_localhost_port_12345("grpc+http://[::1]:12345", true)]
114 #[case::grpc_valid_http_host_without_port("grpc+http://localhost", true)]
116 #[case::grpc_valid_https_host_without_port("grpc+https://localhost", true)]
118 #[case::grpc_invalid_host_and_path("grpc+http://localhost/some-path", false)]
120 #[cfg_attr(target_os = "linux", case::oci_missing_bundle_dir("oci://", false))]
122 #[cfg_attr(target_os = "linux", case::oci_bundle_path(&format!("oci://{}", TMPDIR_OCI_1.path().to_str().unwrap()), true))]
124 #[tokio::test]
125 async fn test_from_addr(#[case] uri_str: &str, #[case] exp_succeed: bool) {
126 let blob_service: Arc<dyn BlobService> = Arc::from(MemoryBlobService::default());
127 let directory_service = snix_castore::utils::gen_test_directory_service();
128
129 let resp = from_addr(uri_str, blob_service, directory_service).await;
130
131 if exp_succeed {
132 resp.expect("should succeed");
133 } else {
134 assert!(resp.is_err(), "should fail");
135 }
136 }
137}