utapi_rs/
config.rs

1use serde::{Deserialize, Serialize};
2use std::str::FromStr;
3
4/// Configuration for the Uploadthing service.
5///
6/// This struct contains all the necessary configurations required
7/// to interact with the Uploadthing API. It includes the host URL,
8/// optional user agent, API key, and version information.
9#[derive(Debug, Serialize, Deserialize, Clone)]
10pub struct UploadthingConfig {
11    /// The host URL of the Uploadthing service.
12    pub host: String,
13    /// An optional user agent string to be sent with each request.
14    /// This can be used for identifying the client to the server.
15    pub user_agent: Option<String>,
16    /// An optional API key for authentication with the Uploadthing service.
17    /// If provided, it will be included in the headers of each request.
18    pub api_key: Option<ApiKey>,
19    /// An optional version string to be sent with each request.
20    /// This can represent the version of the client application.
21    pub version: Option<String>,
22}
23
24/// The version of the current crate, taken directly from the Cargo package metadata.
25/// This constant is used to provide version information in user agents and other
26/// parts of the application that may require knowledge of the current version.
27pub const VERSION: &str = env!("CARGO_PKG_VERSION");
28
29#[derive(Debug, Serialize, Deserialize)]
30/// Represents an API key used for authenticating with the Uploadthing service.
31///
32/// This struct holds the actual API key and an optional prefix.
33/// The prefix can be used to add a specific identifier before the key
34/// when sending it in the request header, but it is not required.
35#[derive(Clone)]
36pub struct ApiKey {
37    /// An optional prefix to be added to the API key.
38    /// This can be used to distinguish between different types of keys.
39    pub prefix: Option<String>,
40
41    /// The API key string that is used for authentication.
42    pub key: String,
43}
44
45impl ApiKey {
46    /// Constructs an `ApiKey` instance from an environment variable.
47    ///
48    /// This function attempts to create an `ApiKey` by reading the value
49    /// of the environment variable `UPLOADTHING_SECRET`. If the variable
50    /// is set, it returns `Some(ApiKey)` with the key set to the value
51    /// of the environment variable, and no prefix. If the environment
52    /// variable is not set, it returns `None`.
53    ///
54    /// # Examples
55    ///
56    /// ```
57    /// // Assuming the environment variable `UPLOADTHING_SECRET` is set to "secret123"
58    /// let api_key = ApiKey::from_env().unwrap();
59    /// assert_eq!(api_key.key, "secret123");
60    /// assert_eq!(api_key.prefix, None);
61    /// ```
62    pub fn from_env() -> Option<ApiKey> {
63        match std::env::var("UPLOADTHING_SECRET") {
64            Ok(key) => Some(ApiKey { prefix: None, key }),
65            Err(_) => None,
66        }
67    }
68}
69
70impl Default for ApiKey {
71    /// Provides a default `ApiKey` by trying to read from the environment variable.
72    ///
73    /// This implementation uses `ApiKey::from_env()` to attempt to create an `ApiKey`.
74    /// If the environment variable is not set, this will panic. It is generally expected
75    /// that the environment variable is set if this method is used.
76    ///
77    /// # Panics
78    ///
79    /// This function will panic if the environment variable `UPLOADTHING_SECRET` is not set.
80    ///
81    /// # Examples
82    ///
83    /// ```
84    /// // Assuming the environment variable `UPLOADTHING_SECRET` is set
85    /// let api_key = ApiKey::default();
86    /// // Use the `api_key` as needed
87    /// ```
88    fn default() -> ApiKey {
89        Self::from_env().expect("UPLOADTHING_SECRET environment variable is not set")
90    }
91}
92
93impl FromStr for ApiKey {
94    type Err = ();
95
96    /// Creates an `ApiKey` instance from a string slice.
97    ///
98    /// This function attempts to parse the given string slice into an `ApiKey`
99    /// by checking for a colon which separates the optional prefix and the key.
100    /// If a colon is found, the part before the colon is set as the prefix and
101    /// the part after the colon is set as the key. If no colon is found, the
102    /// entire string is set as the key with no prefix.
103    ///
104    /// # Examples
105    ///
106    /// ```
107    /// let api_key: ApiKey = "Bearer:secret123".parse().unwrap();
108    /// assert_eq!(api_key.prefix, Some("Bearer".to_string()));
109    /// assert_eq!(api_key.key, "secret123".to_string());
110    ///
111    /// let api_key: ApiKey = "secret123".parse().unwrap();
112    /// assert_eq!(api_key.prefix, None);
113    /// assert_eq!(api_key.key, "secret123".to_string());
114    /// ```
115    fn from_str(s: &str) -> Result<Self, Self::Err> {
116        let parts: Vec<&str> = s.splitn(2, ':').collect();
117        let (prefix, key) = match parts.as_slice() {
118            [prefix, key] => (Some(prefix.to_string()), key.to_string()),
119            [key] => (None, key.to_string()),
120            _ => return Err(()),
121        };
122
123        Ok(ApiKey { prefix, key })
124    }
125}
126
127impl std::fmt::Display for ApiKey {
128    /// Formats the `ApiKey` for display purposes.
129    ///
130    /// This implementation will only display the `key` field of the `ApiKey`.
131    /// The prefix is not included in the output. This is generally used when
132    /// the API key needs to be included in a header or similar context where
133    /// the prefix is not required.
134    ///
135    /// # Examples
136    ///
137    /// ```
138    /// let api_key = ApiKey {
139    ///     prefix: Some(String::from("Bearer")),
140    ///     key: String::from("secret123"),
141    /// };
142    /// assert_eq!(format!("{}", api_key), "secret123");
143    /// ```
144    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
145        write!(f, "{}", self.key)
146    }
147}
148
149impl UploadthingConfig {
150    /// Creates a new `UploadthingConfig` with default values.
151    ///
152    /// This is a convenience method that calls `UploadthingConfig::default()`
153    /// to create a new configuration instance with the default host, user agent,
154    /// and version values. The API key is not set by default.
155    ///
156    /// # Examples
157    ///
158    /// ```
159    /// let config = UploadthingConfig::new();
160    /// // The `config` now contains the default settings.
161    /// ```
162    pub fn new() -> UploadthingConfig {
163        UploadthingConfig::default()
164    }
165
166    /// Creates a builder for `UploadthingConfig`.
167    ///
168    /// This method returns an instance of `UploadthingConfigBuilder` which can be
169    /// used to set various configuration options before building the final
170    /// `UploadthingConfig` instance.
171    ///
172    /// # Examples
173    ///
174    /// ```
175    /// let builder = UploadthingConfig::builder();
176    /// let config = builder.host("https://customhost.com")
177    ///                     .user_agent("CustomUserAgent/1.0")
178    ///                     .api_key("my_api_key")
179    ///                     .version("1.0.0")
180    ///                     .build();
181    /// // The `config` is now customized with the provided settings.
182    /// ```
183    pub fn builder() -> UploadthingConfigBuilder {
184        UploadthingConfigBuilder::new()
185    }
186}
187
188impl Default for UploadthingConfig {
189    /// Provides default values for `UploadthingConfig`.
190    ///
191    /// This implementation sets default values for the host, user agent, and version.
192    /// The default host is set to "<https://uploadthing.com>". The user agent is constructed
193    /// using the crate version and the name "utapi-rs". The version is set to the crate's
194    /// current version. The API key is attempted to be retrieved from the environment
195    /// variable `UPLOADTHING_SECRET`. If the environment variable is not set, the API key
196    /// will be `None`.
197    ///
198    /// # Returns
199    ///
200    /// Returns an `UploadthingConfig` instance with default values set.
201    ///
202    /// # Examples
203    ///
204    /// ```
205    /// let default_config = UploadthingConfig::default();
206    /// assert_eq!(default_config.host, "https://uploadthing.com");
207    /// // Other fields are set to their respective defaults
208    /// ```
209    fn default() -> UploadthingConfig {
210        UploadthingConfig {
211            host: "https://uploadthing.com".to_string(),
212            // User agent includes the crate name and version for identification purposes.
213            user_agent: Some(format!("utapi-rs/{}/rust", VERSION)),
214            // Attempt to retrieve the API key from the environment variable.
215            // api_key: std::env::var("UPLOADTHING_SECRET").ok(),
216            api_key: ApiKey::from_env(),
217            // Version is set to the current crate version.
218            version: Some(VERSION.to_string()),
219        }
220    }
221}
222
223/// A builder for `UploadthingConfig`.
224///
225/// This builder allows for a fluent interface to construct a `UploadthingConfig`
226/// instance with custom values. It starts with default values and allows the
227/// user to set properties such as host, user agent, API key, and version
228/// before building the final configuration object.
229pub struct UploadthingConfigBuilder {
230    // Internal `UploadthingConfig` to apply settings to.
231    config: UploadthingConfig,
232}
233
234impl UploadthingConfigBuilder {
235    /// Creates a new `UploadthingConfigBuilder` with default configuration values.
236    ///
237    /// This uses `UploadthingConfig::default()` to initialize the internal config
238    /// with default values.
239    pub fn new() -> Self {
240        UploadthingConfigBuilder {
241            config: UploadthingConfig::default(),
242        }
243    }
244
245    /// Sets the host URL in the configuration.
246    ///
247    /// # Arguments
248    ///
249    /// * `host` - A string slice that holds the URL of the Uploadthing service.
250    ///
251    /// # Examples
252    ///
253    /// ```
254    /// let builder = UploadthingConfigBuilder::new().host("https://example.com");
255    /// ```
256    pub fn host(mut self, host: &str) -> Self {
257        self.config.host = host.to_string();
258        self
259    }
260
261    /// Sets the user agent in the configuration.
262    ///
263    /// # Arguments
264    ///
265    /// * `user_agent` - A string slice that represents the user agent to be sent with each request.
266    ///
267    /// # Examples
268    ///
269    /// ```
270    /// let builder = UploadthingConfigBuilder::new().user_agent("MyUploader/1.0");
271    /// ```
272    pub fn user_agent(mut self, user_agent: &str) -> Self {
273        self.config.user_agent = Some(user_agent.to_string());
274        self
275    }
276
277    /// Sets the API key in the configuration.
278    ///
279    /// This method allows the user to provide an API key as a string slice
280    /// that will be used for authenticating with the Uploadthing service.
281    /// It attempts to parse the provided string into an `ApiKey` instance
282    /// using the `FromStr` trait implementation for `ApiKey`. If parsing is
283    /// successful, the `ApiKey` is wrapped in a `Some` and set in the configuration.
284    /// If parsing fails, it falls back to the default `ApiKey` which is obtained
285    /// from the environment variable `UPLOADTHING_SECRET`.
286    ///
287    /// # Arguments
288    ///
289    /// * `api_key` - A string slice that represents the API key for authenticating with the service.
290    ///
291    /// # Examples
292    ///
293    /// ```
294    /// let builder = UploadthingConfigBuilder::new().api_key("your_api_key");
295    /// ```
296    ///
297    /// # Panics
298    ///
299    /// This method will panic if the provided `api_key` string is not in a valid format
300    /// and there is no `UPLOADTHING_SECRET` environment variable set to fall back on.
301    pub fn api_key(mut self, api_key: &str) -> Self {
302        // Attempt to parse the provided API key string.
303        // If parsing fails, fall back to the default which may panic if the environment variable is not set.
304        self.config.api_key = Some(ApiKey::from_str(api_key).unwrap_or_default());
305        self
306    }
307
308    /// Sets the version in the configuration.
309    ///
310    /// # Arguments
311    ///
312    /// * `version` - A string slice that represents the version of the client application.
313    ///
314    /// # Examples
315    ///
316    /// ```
317    /// let builder = UploadthingConfigBuilder::new().version("2.0.0");
318    /// ```
319    pub fn version(mut self, version: &str) -> Self {
320        self.config.version = Some(version.to_string());
321        self
322    }
323
324    /// Builds the `UploadthingConfig` with the current settings of the builder.
325    ///
326    /// Consumes the builder and returns the configured `UploadthingConfig` instance.
327    ///
328    /// # Examples
329    ///
330    /// ```
331    /// let config = UploadthingConfigBuilder::new()
332    ///     .host("https://example.com")
333    ///     .user_agent("MyUploader/1.0")
334    ///     .api_key("your_api_key")
335    ///     .version("2.0.0")
336    ///     .build();
337    /// ```
338    pub fn build(self) -> UploadthingConfig {
339        self.config
340    }
341}
342
343/// Implements the `Default` trait for `UploadthingConfigBuilder`.
344///
345/// This allows for the creation of a builder with the default configuration
346/// by calling `UploadthingConfigBuilder::default()`.
347impl Default for UploadthingConfigBuilder {
348    /// Returns a new `UploadthingConfigBuilder` initialized with default values.
349    fn default() -> Self {
350        Self::new()
351    }
352}