use std::fmt;
use std::sync::Arc;
use reqwest::{Client, RequestBuilder};
use super::config::RunpodConfig;
use crate::Result;
#[cfg(feature = "tracing")]
use crate::TRACING_TARGET_CLIENT;
use crate::builder::RunpodBuilder;
#[derive(Clone)]
pub struct RunpodClient {
pub(crate) inner: Arc<RunpodClientInner>,
}
#[derive(Debug)]
pub(crate) struct RunpodClientInner {
pub(crate) config: RunpodConfig,
pub(crate) client: Client,
}
impl RunpodClient {
#[cfg_attr(
feature = "tracing",
tracing::instrument(
skip(config),
target = TRACING_TARGET_CLIENT,
fields(api_key = %config.masked_api_key())
)
)]
pub fn new(config: RunpodConfig) -> Result<Self> {
#[cfg(feature = "tracing")]
tracing::debug!(target: TRACING_TARGET_CLIENT, "Creating RunPod client");
let client = if let Some(custom_client) = config.client() {
custom_client
} else {
Client::builder().timeout(config.timeout()).build()?
};
#[cfg(feature = "tracing")]
tracing::info!(target: TRACING_TARGET_CLIENT,
rest_url = %config.rest_url(),
timeout = ?config.timeout(),
api_key = %config.masked_api_key(),
custom_client = config.client().is_some(),
"RunPod client created successfully"
);
let inner = Arc::new(RunpodClientInner { config, client });
Ok(Self { inner })
}
#[cfg(feature = "serverless")]
#[cfg_attr(docsrs, doc(cfg(feature = "serverless")))]
#[cfg_attr(
feature = "tracing",
tracing::instrument(
skip(self),
target = TRACING_TARGET_CLIENT,
fields(method = "GET", path, url)
)
)]
pub(crate) fn get_api(&self, path: &str) -> RequestBuilder {
let url = format!("{}/{}", self.inner.config.api_url(), path);
#[cfg(feature = "tracing")]
tracing::trace!(target: TRACING_TARGET_CLIENT,
url = %url,
method = "GET",
"Creating HTTP GET request to API"
);
self.inner
.client
.get(&url)
.bearer_auth(self.inner.config.api_key())
.timeout(self.inner.config.timeout())
}
#[cfg(feature = "serverless")]
#[cfg_attr(docsrs, doc(cfg(feature = "serverless")))]
#[cfg_attr(
feature = "tracing",
tracing::instrument(
skip(self),
target = TRACING_TARGET_CLIENT,
fields(method = "POST", path, url)
)
)]
pub(crate) fn post_api(&self, path: &str) -> RequestBuilder {
let url = format!("{}/{}", self.inner.config.api_url(), path);
#[cfg(feature = "tracing")]
tracing::trace!(target: TRACING_TARGET_CLIENT,
url = %url,
method = "POST",
"Creating HTTP POST request to API"
);
self.inner
.client
.post(&url)
.bearer_auth(self.inner.config.api_key())
.timeout(self.inner.config.timeout())
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(
skip(self),
target = TRACING_TARGET_CLIENT,
fields(method = "GET", path, url)
)
)]
pub(crate) fn get(&self, path: &str) -> RequestBuilder {
let url = format!("{}{}", self.inner.config.rest_url(), path);
#[cfg(feature = "tracing")]
tracing::trace!(target: TRACING_TARGET_CLIENT,
url = %url,
method = "GET",
"Creating HTTP GET request"
);
self.inner
.client
.get(&url)
.bearer_auth(self.inner.config.api_key())
.timeout(self.inner.config.timeout())
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(
skip(self),
target = TRACING_TARGET_CLIENT,
fields(method = "POST", path, url)
)
)]
pub(crate) fn post(&self, path: &str) -> RequestBuilder {
let url = format!("{}{}", self.inner.config.rest_url(), path);
#[cfg(feature = "tracing")]
tracing::trace!(target: TRACING_TARGET_CLIENT,
url = %url,
method = "POST",
"Creating HTTP POST request"
);
self.inner
.client
.post(&url)
.bearer_auth(self.inner.config.api_key())
.timeout(self.inner.config.timeout())
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(
skip(self),
target = TRACING_TARGET_CLIENT,
fields(method = "PATCH", path, url)
)
)]
pub(crate) fn patch(&self, path: &str) -> RequestBuilder {
let url = format!("{}{}", self.inner.config.rest_url(), path);
#[cfg(feature = "tracing")]
tracing::trace!(target: TRACING_TARGET_CLIENT,
url = %url,
method = "PATCH",
"Creating HTTP PATCH request"
);
self.inner
.client
.patch(&url)
.bearer_auth(self.inner.config.api_key())
.timeout(self.inner.config.timeout())
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(
skip(self),
target = TRACING_TARGET_CLIENT,
fields(method = "DELETE", path, url)
)
)]
pub(crate) fn delete(&self, path: &str) -> RequestBuilder {
let url = format!("{}{}", self.inner.config.rest_url(), path);
#[cfg(feature = "tracing")]
tracing::trace!(target: TRACING_TARGET_CLIENT,
url = %url,
method = "DELETE",
"Creating HTTP DELETE request"
);
self.inner
.client
.delete(&url)
.bearer_auth(self.inner.config.api_key())
.timeout(self.inner.config.timeout())
}
pub fn builder() -> RunpodBuilder {
RunpodConfig::builder()
}
#[cfg_attr(feature = "tracing", tracing::instrument(target = TRACING_TARGET_CLIENT))]
pub fn from_env() -> Result<Self> {
#[cfg(feature = "tracing")]
tracing::debug!(target: TRACING_TARGET_CLIENT, "Creating RunPod client from environment");
let config = RunpodConfig::from_env()?;
Self::new(config)
}
}
impl fmt::Debug for RunpodClient {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut debug_struct = f.debug_struct("RunpodClient");
debug_struct
.field("api_key", &self.inner.config.masked_api_key())
.field("rest_url", &self.inner.config.rest_url())
.field("timeout", &self.inner.config.timeout());
#[cfg(feature = "serverless")]
debug_struct.field("api_url", &self.inner.config.api_url());
debug_struct.finish()
}
}