1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
mod private
{
use crate::error::{ XaiError, Result };
use crate::secret::Secret;
use url::Url;
use reqwest::header;
use std::time::Duration;
/// Default base URL for XAI API.
///
/// All API requests will be made to endpoints under this base URL.
/// Trailing slash is required for proper URL joining with paths starting with `/`.
pub const DEFAULT_BASE_URL : &str = "https://api.x.ai/v1/";
/// Default request timeout in seconds.
///
/// Requests exceeding this duration will be cancelled with a timeout error.
pub const DEFAULT_TIMEOUT_SECS : u64 = 30;
/// Environment configuration trait for XAI API client.
///
/// This trait abstracts the environment configuration, allowing for different
/// implementations (production, testing, custom configurations).
///
/// # Required Methods
///
/// - `api_key()` - Returns the API authentication key
/// - `base_url()` - Returns the base URL for API requests
/// - `timeout()` - Returns the request timeout duration
/// - `headers()` - Constructs the HTTP headers for requests
///
/// # Trait Bounds
///
/// Implementations must be `Send + Sync + 'static` to support async operations
/// and thread safety.
pub trait XaiEnvironment : Send + Sync + 'static
{
/// Returns the API authentication key.
fn api_key( &self ) -> &Secret;
/// Returns the base URL for API requests.
fn base_url( &self ) -> &Url;
/// Returns the request timeout duration.
fn timeout( &self ) -> Duration;
/// Constructs HTTP headers for API requests.
///
/// Default implementation includes:
/// - Authorization header with Bearer token
/// - Content-Type : application/json
///
/// # Errors
///
/// Returns `XaiError::Http` if header construction fails.
fn headers( &self ) -> Result< header::HeaderMap >
{
let mut headers = header::HeaderMap::new();
// Authorization : Bearer xai-...
let auth_value = format!( "Bearer {secret}", secret = self.api_key().expose_secret() );
headers.insert(
header::AUTHORIZATION,
auth_value.parse()
.map_err( |e| XaiError::Http( format!( "Invalid authorization header : {e}" ) ) )?
);
// Content-Type : application/json
headers.insert(
header::CONTENT_TYPE,
"application/json".parse()
.map_err( |e| XaiError::Http( format!( "Invalid content-type header : {e}" ) ) )?
);
Ok( headers )
}
}
/// Default implementation of XAI environment configuration.
///
/// Provides a standard environment setup with configurable base URL and timeout.
///
/// # Examples
///
/// ```no_run
/// use api_xai::{ XaiEnvironmentImpl, Secret };
///
/// // Basic setup with defaults
/// let secret = Secret::load_with_fallbacks( "XAI_API_KEY" )?;
/// let env = XaiEnvironmentImpl::new( secret )?;
/// # Ok::<(), Box< dyn std::error::Error > >(())
/// ```
///
/// ```no_run
/// use api_xai::{ XaiEnvironmentImpl, Secret };
///
/// // Custom configuration
/// let secret = Secret::load_with_fallbacks( "XAI_API_KEY" )?;
/// let env = XaiEnvironmentImpl::new( secret )?
/// .with_timeout( std::time::Duration::from_secs( 60 ) );
/// # Ok::<(), Box< dyn std::error::Error > >(())
/// ```
#[ derive( Debug, Clone ) ]
pub struct XaiEnvironmentImpl
{
api_key : Secret,
base_url : Url,
timeout : Duration,
}
impl XaiEnvironmentImpl
{
/// Creates a new environment with default configuration.
///
/// # Arguments
///
/// * `api_key` - XAI API authentication key
///
/// # Default Values
///
/// - Base URL: `https://api.x.ai/v1`
/// - Timeout : 30 seconds
///
/// # Errors
///
/// Returns `XaiError::UrlParse` if the default base URL is invalid
/// (this should never happen in practice).
///
/// # Examples
///
/// ```no_run
/// use api_xai::{ XaiEnvironmentImpl, Secret };
///
/// let secret = Secret::load_with_fallbacks( "XAI_API_KEY" )?;
/// let env = XaiEnvironmentImpl::new( secret )?;
/// # Ok::<(), Box< dyn std::error::Error > >(())
/// ```
pub fn new( api_key : Secret ) -> Result< Self >
{
Ok( Self
{
api_key,
base_url : Url::parse( DEFAULT_BASE_URL )?,
timeout : Duration::from_secs( DEFAULT_TIMEOUT_SECS ),
} )
}
/// Sets a custom base URL.
///
/// Use this to configure a different API endpoint (e.g., for testing
/// or proxy configurations).
///
/// # Arguments
///
/// * `base_url` - Custom base URL for API requests
///
/// # Examples
///
/// ```no_run
/// use api_xai::{ XaiEnvironmentImpl, Secret };
/// use url::Url;
///
/// let secret = Secret::load_with_fallbacks( "XAI_API_KEY" )?;
/// let custom_url = Url::parse( "https://custom.api.endpoint/v1" )?;
///
/// let env = XaiEnvironmentImpl::new( secret )?
/// .with_base_url( custom_url );
/// # Ok::<(), Box< dyn std::error::Error > >(())
/// ```
#[ must_use ]
pub fn with_base_url( mut self, base_url : Url ) -> Self
{
self.base_url = base_url;
self
}
/// Sets a custom timeout duration.
///
/// Use this to configure longer timeouts for slow connections or
/// shorter timeouts for fast-fail behavior.
///
/// # Arguments
///
/// * `timeout` - Custom timeout duration
///
/// # Examples
///
/// ```no_run
/// use api_xai::{ XaiEnvironmentImpl, Secret };
/// use std::time::Duration;
///
/// let secret = Secret::load_with_fallbacks( "XAI_API_KEY" )?;
///
/// // 60 second timeout
/// let env = XaiEnvironmentImpl::new( secret )?
/// .with_timeout( Duration::from_secs( 60 ) );
/// # Ok::<(), Box< dyn std::error::Error > >(())
/// ```
#[ must_use ]
pub fn with_timeout( mut self, timeout : Duration ) -> Self
{
self.timeout = timeout;
self
}
}
impl XaiEnvironment for XaiEnvironmentImpl
{
fn api_key( &self ) -> &Secret
{
&self.api_key
}
fn base_url( &self ) -> &Url
{
&self.base_url
}
fn timeout( &self ) -> Duration
{
self.timeout
}
}
}
crate::mod_interface!
{
exposed use
{
DEFAULT_BASE_URL,
DEFAULT_TIMEOUT_SECS,
XaiEnvironment,
XaiEnvironmentImpl,
};
}