podman_rest_client/
podman_rest_client.rs1use std::str::FromStr;
2
3use hyper_util::client::legacy::Client;
4use hyper_util::rt::TokioExecutor;
5
6use crate::api_common::config::HasConfig;
7use crate::api_common::ClientConfig;
8use crate::api_common::Config as APIConfig;
9use crate::api_common::Connector;
10use crate::config::Config;
11use crate::error::ClientError;
12#[cfg(feature = "v4")]
13use crate::impl_crate_v4_traits;
14#[cfg(feature = "v5")]
15use crate::impl_crate_v5_traits;
16#[cfg(feature = "ssh")]
17use crate::ssh;
18#[cfg(feature = "uds")]
19use crate::unix_socket;
20
21const BASE_PATH: &str = "http://d/v5.1.0";
22
23pub struct PodmanRestClient {
24 config: Box<dyn ClientConfig>,
25}
26
27#[cfg(feature = "v5")]
28impl_crate_v5_traits!(PodmanRestClient);
29
30impl HasConfig for PodmanRestClient {
31 fn get_config(&self) -> &dyn ClientConfig {
32 &*self.config
33 }
34}
35
36#[cfg(feature = "v4")]
37impl_crate_v4_traits!(PodmanRestClient);
38
39impl PodmanRestClient {
40 pub async fn new(config: Config) -> Result<Self, ClientError> {
41 let (scheme, rest) = config
42 .uri
43 .split_once("://")
44 .ok_or(ClientError::InvalidScheme)?;
45
46 match scheme {
47 #[cfg(feature = "uds")]
48 "unix" => Ok(PodmanRestClient::new_unix(rest)),
49 #[cfg(not(feature = "uds"))]
50 "uds" => Err(ClientError::UdsFeatureFlagNotEnabled),
51 #[cfg(feature = "ssh")]
52 "ssh" => PodmanRestClient::new_ssh(config.uri, config.identity_file).await,
53 #[cfg(not(feature = "ssh"))]
54 "ssh" => Err(ClientError::SshFeatureFlagNotEnabled),
55 _ => Err(ClientError::InvalidScheme),
56 }
57 }
58
59 #[cfg_attr(docsrs, doc(cfg(feature = "ssh")))]
60 #[cfg(feature = "ssh")]
61 pub async fn new_ssh(
62 uri: String,
63 key_path: Option<String>,
64 ) -> Result<PodmanRestClient, ClientError> {
65 let uri = hyper::Uri::from_str(&uri)?;
66
67 let user_name = uri.authority().and_then(|authority| {
68 if let Some((user_name, _)) = authority.to_string().split_once('@') {
69 Some(user_name.to_string())
70 } else {
71 None
72 }
73 });
74
75 let user_name = user_name.ok_or(ClientError::SshUserNameRequired)?;
76 let key_path = key_path.ok_or(ClientError::SshKeyPathRequired)?;
77 let host = uri.host().ok_or(ClientError::SshHostRequired)?;
78 let address = uri
79 .port()
80 .map_or(host.to_string(), |port| format!("{}:{}", host, port));
81
82 let connector = ssh::SshConnector::new(&user_name, &key_path, &address, uri.path()).await?;
83
84 Ok(PodmanRestClient::new_connector(connector))
85 }
86
87 #[cfg_attr(docsrs, doc(cfg(feature = "uds")))]
88 #[cfg(feature = "uds")]
89 pub fn new_unix(path: &str) -> PodmanRestClient {
90 let connector = unix_socket::UnixConnector::new(path);
91
92 PodmanRestClient::new_connector(connector)
93 }
94
95 fn new_connector<C: Connector>(connector: C) -> PodmanRestClient {
96 let client = Client::builder(TokioExecutor::new()).build(connector);
97
98 PodmanRestClient {
99 config: Box::new(APIConfig {
100 base_path: BASE_PATH.to_string(),
101 ..APIConfig::with_client(client)
102 }),
103 }
104 }
105
106 #[cfg(feature = "v5")]
107 pub fn v5(&self) -> &dyn crate::v5::Client {
108 self
109 }
110
111 #[cfg_attr(docsrs, doc(cfg(feature = "v4")))]
112 #[cfg(feature = "v4")]
113 pub fn v4(&self) -> &dyn crate::v4::Client {
114 self
115 }
116}