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}