Skip to main content

ollama_oxide/http/
client_config.rs

1//! HTTP client configuration
2
3use std::time::Duration;
4
5use crate::{Error, Result};
6use url::Url;
7
8/// Validates that a URL is well-formed and uses http or https scheme
9fn validate_base_url(base_url: &str) -> Result<()> {
10    let url = Url::parse(base_url)?;
11    if url.scheme() != "http" && url.scheme() != "https" {
12        return Err(Error::InvalidUrlError(
13            url::ParseError::RelativeUrlWithoutBase,
14        ));
15    }
16    Ok(())
17}
18
19/// Configuration for Ollama HTTP client
20///
21/// This struct allows customization of the HTTP client behavior including
22/// base URL, timeout, and retry settings. All constructors validate that
23/// the base URL is well-formed and uses http or https scheme.
24///
25/// # Examples
26///
27/// ```no_run
28/// use ollama_oxide::ClientConfig;
29/// use std::time::Duration;
30///
31/// // Use default configuration (http://localhost:11434)
32/// let config = ClientConfig::default();
33///
34/// // Custom configuration
35/// let config = ClientConfig::new(
36///     "http://example.com:8080".to_string(),
37///     Duration::from_secs(60),
38///     5,
39/// )?;
40///
41/// // Just a custom URL
42/// let config = ClientConfig::with_base_url("http://example.com:8080".to_string())?;
43/// # Ok::<(), ollama_oxide::Error>(())
44/// ```
45#[derive(Debug, Clone)]
46pub struct ClientConfig {
47    /// Base URL for Ollama API (validated: must be http or https)
48    base_url: String,
49
50    /// Request timeout duration
51    timeout: Duration,
52
53    /// Maximum retry attempts on failure (0 = no retries)
54    max_retries: u32,
55}
56
57impl Default for ClientConfig {
58    fn default() -> Self {
59        Self {
60            base_url: "http://localhost:11434".to_string(),
61            timeout: Duration::from_secs(30),
62            max_retries: 3,
63        }
64    }
65}
66
67impl ClientConfig {
68    /// Creates a new `ClientConfig` with all attributes specified.
69    ///
70    /// # Errors
71    ///
72    /// Returns an error if the base URL is invalid or uses an unsupported scheme.
73    ///
74    /// # Examples
75    ///
76    /// ```no_run
77    /// use ollama_oxide::ClientConfig;
78    /// use std::time::Duration;
79    ///
80    /// let config = ClientConfig::new(
81    ///     "http://example.com:8080".to_string(),
82    ///     Duration::from_secs(60),
83    ///     5,
84    /// )?;
85    /// # Ok::<(), ollama_oxide::Error>(())
86    /// ```
87    pub fn new(base_url: String, timeout: Duration, max_retries: u32) -> Result<Self> {
88        validate_base_url(&base_url)?;
89        Ok(Self {
90            base_url,
91            timeout,
92            max_retries,
93        })
94    }
95
96    /// Creates a new `ClientConfig` with only `base_url`, using defaults for `timeout` (30s) and `max_retries` (3).
97    ///
98    /// # Errors
99    ///
100    /// Returns an error if the base URL is invalid or uses an unsupported scheme.
101    ///
102    /// # Examples
103    ///
104    /// ```no_run
105    /// use ollama_oxide::ClientConfig;
106    ///
107    /// let config = ClientConfig::with_base_url("http://example.com:8080".to_string())?;
108    /// assert_eq!(config.timeout(), std::time::Duration::from_secs(30));
109    /// assert_eq!(config.max_retries(), 3);
110    /// # Ok::<(), ollama_oxide::Error>(())
111    /// ```
112    pub fn with_base_url(base_url: String) -> Result<Self> {
113        validate_base_url(&base_url)?;
114        Ok(Self {
115            base_url,
116            ..Self::default()
117        })
118    }
119
120    /// Creates a new `ClientConfig` with `base_url` and `timeout`, using the default `max_retries` (3).
121    ///
122    /// # Errors
123    ///
124    /// Returns an error if the base URL is invalid or uses an unsupported scheme.
125    ///
126    /// # Examples
127    ///
128    /// ```no_run
129    /// use ollama_oxide::ClientConfig;
130    /// use std::time::Duration;
131    ///
132    /// let config = ClientConfig::with_base_url_and_timeout(
133    ///     "http://example.com:8080".to_string(),
134    ///     Duration::from_secs(60),
135    /// )?;
136    /// assert_eq!(config.max_retries(), 3);
137    /// # Ok::<(), ollama_oxide::Error>(())
138    /// ```
139    pub fn with_base_url_and_timeout(base_url: String, timeout: Duration) -> Result<Self> {
140        validate_base_url(&base_url)?;
141        Ok(Self {
142            base_url,
143            timeout,
144            ..Self::default()
145        })
146    }
147
148    /// Returns the base URL
149    #[inline]
150    pub fn base_url(&self) -> &str {
151        &self.base_url
152    }
153
154    /// Returns the request timeout duration
155    #[inline]
156    pub fn timeout(&self) -> Duration {
157        self.timeout
158    }
159
160    /// Returns the maximum retry attempts
161    #[inline]
162    pub fn max_retries(&self) -> u32 {
163        self.max_retries
164    }
165
166    /// Build full URL from base URL and endpoint path
167    ///
168    /// # Examples
169    ///
170    /// ```no_run
171    /// use ollama_oxide::ClientConfig;
172    ///
173    /// let config = ClientConfig::default();
174    /// let url = config.url("/api/version");
175    /// assert_eq!(url, "http://localhost:11434/api/version");
176    /// ```
177    #[inline]
178    pub fn url(&self, endpoint: &str) -> String {
179        format!("{}{}", self.base_url, endpoint)
180    }
181}