Wrap Client internals in Arc for cheap cloning

This commit is contained in:
Joakim Hulthe
2026-04-14 14:43:28 +00:00
parent 0d8042287c
commit 6820dd765d

View File

@@ -2,6 +2,7 @@
use crate::apis::{AlbumsApi, AssetsApi, ServerApi, TimelineApi}; use crate::apis::{AlbumsApi, AssetsApi, ServerApi, TimelineApi};
use crate::error::{ImmichError, Result}; use crate::error::{ImmichError, Result};
use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
/// Configuration for the Immich client /// Configuration for the Immich client
@@ -56,13 +57,22 @@ impl Config {
} }
} }
/// Client for making requests to the Immich API /// Internal client data wrapped in Arc for cheap cloning
#[derive(Debug, Clone)] #[derive(Debug)]
pub struct Client { struct ClientInner {
config: Config, config: Config,
http: reqwest::Client, http: reqwest::Client,
} }
/// Client for making requests to the Immich API
///
/// This struct is cheap to clone - it uses an internal Arc to share the underlying
/// HTTP client and configuration.
#[derive(Debug, Clone)]
pub struct Client {
inner: Arc<ClientInner>,
}
impl Client { impl Client {
/// Create a new client with the given configuration /// Create a new client with the given configuration
pub fn new(config: Config) -> Result<Self> { pub fn new(config: Config) -> Result<Self> {
@@ -86,8 +96,10 @@ impl Client {
.build()?; .build()?;
Ok(Self { Ok(Self {
config: Config { base_url, ..config }, inner: Arc::new(ClientInner {
http, config: Config { base_url, ..config },
http,
}),
}) })
} }
@@ -97,38 +109,46 @@ impl Client {
} }
/// Set the API key /// Set the API key
pub fn with_api_key(mut self, api_key: impl Into<String>) -> Self { pub fn with_api_key(self, api_key: impl Into<String>) -> Self {
self.config.api_key = Some(api_key.into()); // We need to create a new ClientInner since we can't modify Arc contents
self let mut config = self.inner.config.clone();
config.api_key = Some(api_key.into());
Self {
inner: Arc::new(ClientInner {
config,
http: self.inner.http.clone(),
}),
}
} }
/// Get the configuration /// Get the configuration
pub fn config(&self) -> &Config { pub fn config(&self) -> &Config {
&self.config &self.inner.config
} }
/// Get the base URL /// Get the base URL
pub fn base_url(&self) -> &str { pub fn base_url(&self) -> &str {
&self.config.base_url &self.inner.config.base_url
} }
/// Check if the client has an API key configured /// Check if the client has an API key configured
pub fn has_api_key(&self) -> bool { pub fn has_api_key(&self) -> bool {
self.config.api_key.is_some() self.inner.config.api_key.is_some()
} }
/// Get the underlying HTTP client /// Get the underlying HTTP client
pub fn http(&self) -> &reqwest::Client { pub fn http(&self) -> &reqwest::Client {
&self.http &self.inner.http
} }
/// Create an authenticated request builder /// Create an authenticated request builder
pub fn request(&self, method: reqwest::Method, path: &str) -> reqwest::RequestBuilder { pub fn request(&self, method: reqwest::Method, path: &str) -> reqwest::RequestBuilder {
let url = format!("{}/api{}", self.config.base_url, path); let url = format!("{}/api{}", self.inner.config.base_url, path);
let mut builder = self.http.request(method, &url); let mut builder = self.inner.http.request(method, &url);
// Add API key authentication // Add API key authentication
if let Some(ref api_key) = self.config.api_key { if let Some(ref api_key) = self.inner.config.api_key {
builder = builder.header("x-api-key", api_key); builder = builder.header("x-api-key", api_key);
} }
@@ -137,7 +157,7 @@ impl Client {
/// Execute a request and handle common error cases /// Execute a request and handle common error cases
pub async fn execute(&self, request: reqwest::Request) -> Result<reqwest::Response> { pub async fn execute(&self, request: reqwest::Request) -> Result<reqwest::Response> {
let response = self.http.execute(request).await?; let response = self.inner.http.execute(request).await?;
if response.status().is_success() { if response.status().is_success() {
Ok(response) Ok(response)