jules_core/builder/
mod.rs1use crate::client::ClientRequest;
4use crate::errors::{SDKError, ValidationError};
5use crate::response::ClientResponse;
6use crate::traits::Client;
7use std::future::Future;
8use std::time::Duration;
9
10#[derive(Clone)]
12pub struct ClientBuilder {
13 base_url: Option<String>,
14 timeout: Duration,
15 auth_token: Option<String>,
16}
17
18impl std::fmt::Debug for ClientBuilder {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 f.debug_struct("ClientBuilder")
21 .field("base_url", &self.base_url)
22 .field("timeout", &self.timeout)
23 .field(
24 "auth_token",
25 &self.auth_token.as_ref().map(|_| "***REDACTED***"),
26 )
27 .finish()
28 }
29}
30
31impl Default for ClientBuilder {
32 fn default() -> Self {
33 Self {
34 base_url: None,
35 timeout: Duration::from_secs(30),
36 auth_token: None,
37 }
38 }
39}
40
41impl ClientBuilder {
42 #[must_use]
44 pub fn new() -> Self {
45 Self::default()
46 }
47
48 #[must_use]
50 pub fn base_url(mut self, base_url: impl Into<String>) -> Self {
51 self.base_url = Some(base_url.into());
52 self
53 }
54
55 #[must_use]
57 pub fn timeout(mut self, timeout: Duration) -> Self {
58 self.timeout = timeout;
59 self
60 }
61
62 #[must_use]
64 pub fn auth_token(mut self, auth_token: impl Into<String>) -> Self {
65 self.auth_token = Some(auth_token.into());
66 self
67 }
68
69 pub fn build(self) -> Result<BuiltClient, SDKError> {
74 let base_url = self
75 .base_url
76 .ok_or_else(|| SDKError::Validation(ValidationError::new("base_url is required")))?;
77
78 let auth_token = self
79 .auth_token
80 .ok_or_else(|| SDKError::Validation(ValidationError::new("auth_token is required")))?;
81
82 Ok(BuiltClient {
83 base_url,
84 timeout: self.timeout,
85 auth_token,
86 })
87 }
88}
89
90#[derive(Clone)]
92pub struct BuiltClient {
93 base_url: String,
94 timeout: Duration,
95 auth_token: String,
96}
97
98impl std::fmt::Debug for BuiltClient {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 f.debug_struct("BuiltClient")
101 .field("base_url", &self.base_url)
102 .field("timeout", &self.timeout)
103 .field("auth_token", &"***REDACTED***")
104 .finish()
105 }
106}
107
108impl BuiltClient {
109 #[must_use]
111 pub fn base_url(&self) -> &str {
112 &self.base_url
113 }
114
115 #[must_use]
117 pub fn timeout(&self) -> Duration {
118 self.timeout
119 }
120
121 #[must_use]
123 pub fn auth_token(&self) -> &str {
124 &self.auth_token
125 }
126}
127
128impl Client for BuiltClient {
129 #[allow(clippy::manual_async_fn)]
130 fn send_request(
131 &self,
132 request: ClientRequest,
133 ) -> impl Future<Output = Result<ClientResponse, SDKError>> + Send {
134 async move {
135 let _ = request;
136 Err(SDKError::Validation(ValidationError::new(
137 "Not implemented",
138 )))
139 }
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn test_client_builder_defaults() {
149 let builder = ClientBuilder::new();
150 assert_eq!(builder.timeout, Duration::from_secs(30));
151 assert!(builder.base_url.is_none());
152 assert!(builder.auth_token.is_none());
153 }
154
155 #[test]
156 fn test_client_builder_validation_error_url() {
157 let builder = ClientBuilder::new().auth_token("secret");
158 let result = builder.build();
159 assert!(result.is_err());
160 if let Err(SDKError::Validation(e)) = result {
161 assert_eq!(e.message, "base_url is required");
162 } else {
163 panic!("Expected validation error");
164 }
165 }
166
167 #[test]
168 fn test_client_builder_validation_error_auth() {
169 let builder = ClientBuilder::new().base_url("https://api.example.com");
170 let result = builder.build();
171 assert!(result.is_err());
172 if let Err(SDKError::Validation(e)) = result {
173 assert_eq!(e.message, "auth_token is required");
174 } else {
175 panic!("Expected validation error");
176 }
177 }
178
179 #[test]
180 fn test_client_builder_success() {
181 let builder = ClientBuilder::new()
182 .base_url("https://api.example.com")
183 .auth_token("secret")
184 .timeout(Duration::from_secs(10));
185
186 let client = builder.build().unwrap();
187 assert_eq!(client.base_url, "https://api.example.com");
188 assert_eq!(client.auth_token, "secret");
189 assert_eq!(client.timeout, Duration::from_secs(10));
190 }
191
192 #[tokio::test]
193 async fn test_built_client_send_request() {
194 let builder = ClientBuilder::new()
195 .base_url("https://api.example.com")
196 .auth_token("secret");
197 let client = builder.build().unwrap();
198 let request = ClientRequest::default();
199 let result = client.send_request(request).await;
200
201 assert!(result.is_err());
202 if let Err(SDKError::Validation(e)) = result {
203 assert_eq!(e.message, "Not implemented");
204 } else {
205 panic!("Expected validation error with message 'Not implemented'");
206 }
207 }
208}
209pub mod request;
211pub use request::RequestBuilder;
212
213pub mod conversation;
215pub use conversation::ConversationBuilder;
216
217pub mod message;
219pub use message::MessageBuilder;