From 6820dd765d91237adf05d3ce11204b869db50d16 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Tue, 14 Apr 2026 14:43:28 +0000 Subject: [PATCH] Wrap Client internals in Arc for cheap cloning --- src/client.rs | 52 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/src/client.rs b/src/client.rs index 90a5e5e..973b1df 100644 --- a/src/client.rs +++ b/src/client.rs @@ -2,6 +2,7 @@ use crate::apis::{AlbumsApi, AssetsApi, ServerApi, TimelineApi}; use crate::error::{ImmichError, Result}; +use std::sync::Arc; use std::time::Duration; /// Configuration for the Immich client @@ -56,13 +57,22 @@ impl Config { } } -/// Client for making requests to the Immich API -#[derive(Debug, Clone)] -pub struct Client { +/// Internal client data wrapped in Arc for cheap cloning +#[derive(Debug)] +struct ClientInner { config: Config, 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, +} + impl Client { /// Create a new client with the given configuration pub fn new(config: Config) -> Result { @@ -86,8 +96,10 @@ impl Client { .build()?; Ok(Self { - config: Config { base_url, ..config }, - http, + inner: Arc::new(ClientInner { + config: Config { base_url, ..config }, + http, + }), }) } @@ -97,38 +109,46 @@ impl Client { } /// Set the API key - pub fn with_api_key(mut self, api_key: impl Into) -> Self { - self.config.api_key = Some(api_key.into()); - self + pub fn with_api_key(self, api_key: impl Into) -> Self { + // We need to create a new ClientInner since we can't modify Arc contents + 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 pub fn config(&self) -> &Config { - &self.config + &self.inner.config } /// Get the base URL pub fn base_url(&self) -> &str { - &self.config.base_url + &self.inner.config.base_url } /// Check if the client has an API key configured 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 pub fn http(&self) -> &reqwest::Client { - &self.http + &self.inner.http } /// Create an authenticated request builder pub fn request(&self, method: reqwest::Method, path: &str) -> reqwest::RequestBuilder { - let url = format!("{}/api{}", self.config.base_url, path); - let mut builder = self.http.request(method, &url); + let url = format!("{}/api{}", self.inner.config.base_url, path); + let mut builder = self.inner.http.request(method, &url); // 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); } @@ -137,7 +157,7 @@ impl Client { /// Execute a request and handle common error cases pub async fn execute(&self, request: reqwest::Request) -> Result { - let response = self.http.execute(request).await?; + let response = self.inner.http.execute(request).await?; if response.status().is_success() { Ok(response)