openai_rust_sdk/api/base/
config.rs

1//! Configuration utilities for the HTTP client
2
3use crate::error::{OpenAIError, Result};
4
5/// Default OpenAI API base URL
6pub const DEFAULT_BASE_URL: &str = "https://api.openai.com";
7
8/// Configuration builder for HTTP client
9#[derive(Debug, Clone)]
10pub struct ClientConfig {
11    /// OpenAI API key for authentication
12    pub api_key: String,
13    /// Base URL for API requests
14    pub base_url: String,
15}
16
17impl ClientConfig {
18    /// Create a new client configuration with the given API key
19    pub fn new<S: Into<String>>(api_key: S) -> Result<Self> {
20        let api_key = Self::validate_api_key(api_key)?;
21        Ok(Self {
22            api_key,
23            base_url: DEFAULT_BASE_URL.to_string(),
24        })
25    }
26
27    /// Create a new client configuration with custom base URL
28    pub fn new_with_base_url<S: Into<String>>(api_key: S, base_url: S) -> Result<Self> {
29        let api_key = Self::validate_api_key(api_key)?;
30        Ok(Self {
31            api_key,
32            base_url: base_url.into(),
33        })
34    }
35
36    /// Validate API key and return it if valid
37    fn validate_api_key<S: Into<String>>(api_key: S) -> Result<String> {
38        let api_key = api_key.into();
39        if api_key.trim().is_empty() {
40            return Err(OpenAIError::authentication("API key cannot be empty"));
41        }
42        Ok(api_key)
43    }
44
45    /// Set a custom base URL
46    pub fn with_base_url<S: Into<String>>(mut self, base_url: S) -> Self {
47        self.base_url = base_url.into();
48        self
49    }
50
51    /// Get the API key
52    #[must_use]
53    pub fn api_key(&self) -> &str {
54        &self.api_key
55    }
56
57    /// Get the base URL
58    #[must_use]
59    pub fn base_url(&self) -> &str {
60        &self.base_url
61    }
62}
63
64/// Trait for request types that can be validated
65pub trait Validate {
66    /// Validates the request and returns an error message if invalid
67    fn validate(&self) -> std::result::Result<(), String>;
68}
69
70/// Validates a request object that implements a validate method
71///
72/// This helper consolidates the common pattern of calling `validate()` on request objects
73/// and mapping validation errors to `OpenAIError::InvalidRequest`.
74///
75/// # Arguments
76///
77/// * `request` - Any object that implements a `validate()` method returning `Result<(), String>`
78///
79/// # Returns
80///
81/// * `Result<()>` - Ok if validation passes, InvalidRequest error if validation fails
82///
83/// # Example
84///
85/// ```rust,ignore
86/// validate_request(&request)?;
87/// ```
88pub fn validate_request<T>(request: &T) -> Result<()>
89where
90    T: Validate,
91{
92    request.validate().map_err(OpenAIError::InvalidRequest)
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn test_client_config_new() {
101        let config = ClientConfig::new("test-key").unwrap();
102        assert_eq!(config.api_key(), "test-key");
103        assert_eq!(config.base_url(), DEFAULT_BASE_URL);
104    }
105
106    #[test]
107    fn test_client_config_with_base_url() {
108        let config = ClientConfig::new_with_base_url("test-key", "https://custom.api.com").unwrap();
109        assert_eq!(config.api_key(), "test-key");
110        assert_eq!(config.base_url(), "https://custom.api.com");
111    }
112
113    #[test]
114    fn test_client_config_empty_api_key() {
115        let result = ClientConfig::new("");
116        assert!(result.is_err());
117    }
118
119    #[test]
120    fn test_validate_request_success() {
121        struct TestRequest {
122            valid: bool,
123        }
124
125        impl Validate for TestRequest {
126            fn validate(&self) -> std::result::Result<(), String> {
127                if self.valid {
128                    Ok(())
129                } else {
130                    Err("Invalid request".to_string())
131                }
132            }
133        }
134
135        let valid_request = TestRequest { valid: true };
136        let result = validate_request(&valid_request);
137        assert!(result.is_ok());
138    }
139
140    #[test]
141    fn test_validate_request_failure() {
142        struct TestRequest {
143            valid: bool,
144        }
145
146        impl Validate for TestRequest {
147            fn validate(&self) -> std::result::Result<(), String> {
148                if self.valid {
149                    Ok(())
150                } else {
151                    Err("Invalid request".to_string())
152                }
153            }
154        }
155
156        let invalid_request = TestRequest { valid: false };
157        let result = validate_request(&invalid_request);
158        assert!(result.is_err());
159
160        if let Err(e) = result {
161            if let OpenAIError::InvalidRequest(msg) = e {
162                assert_eq!(msg, "Invalid request");
163            } else {
164                panic!("Expected InvalidRequest error, got: {:?}", e);
165            }
166        }
167    }
168}