11//! Redis Cloud API client core implementation
2+ //!
3+ //! This module contains the core HTTP client for interacting with the Redis Cloud REST API.
4+ //! It provides authentication handling, request/response processing, and error management.
5+ //!
6+ //! The client is designed around a builder pattern for flexible configuration and supports
7+ //! both typed and untyped API interactions.
28
39use crate :: { CloudError as RestError , Result } ;
410use reqwest:: Client ;
511use serde:: Serialize ;
612use std:: sync:: Arc ;
713
8- /// Redis Cloud API configuration
14+ /// Builder for constructing a CloudClient with custom configuration
15+ ///
16+ /// Provides a fluent interface for configuring API credentials, base URL, timeouts,
17+ /// and other client settings before creating the final CloudClient instance.
18+ ///
19+ /// # Examples
20+ ///
21+ /// ```rust,no_run
22+ /// use redis_cloud::CloudClient;
23+ ///
24+ /// // Basic configuration
25+ /// let client = CloudClient::builder()
26+ /// .api_key("your-api-key")
27+ /// .api_secret("your-api-secret")
28+ /// .build()?;
29+ ///
30+ /// // Advanced configuration
31+ /// let client = CloudClient::builder()
32+ /// .api_key("your-api-key")
33+ /// .api_secret("your-api-secret")
34+ /// .base_url("https://api.redislabs.com/v1".to_string())
35+ /// .timeout(std::time::Duration::from_secs(120))
36+ /// .build()?;
37+ /// # Ok::<(), Box<dyn std::error::Error>>(())
38+ /// ```
939#[ derive( Debug , Clone ) ]
10- pub struct CloudConfig {
11- pub api_key : String ,
12- pub api_secret : String ,
13- pub base_url : String ,
14- pub timeout : std:: time:: Duration ,
40+ pub struct CloudClientBuilder {
41+ api_key : Option < String > ,
42+ api_secret : Option < String > ,
43+ base_url : String ,
44+ timeout : std:: time:: Duration ,
1545}
1646
17- impl Default for CloudConfig {
47+ impl Default for CloudClientBuilder {
1848 fn default ( ) -> Self {
19- CloudConfig {
20- api_key : String :: new ( ) ,
21- api_secret : String :: new ( ) ,
49+ Self {
50+ api_key : None ,
51+ api_secret : None ,
2252 base_url : "https://api.redislabs.com/v1" . to_string ( ) ,
2353 timeout : std:: time:: Duration :: from_secs ( 30 ) ,
2454 }
2555 }
2656}
2757
28- /// Redis Cloud API client
29- #[ derive( Clone ) ]
30- pub struct CloudClient {
31- pub ( crate ) config : CloudConfig ,
32- pub ( crate ) client : Arc < Client > ,
33- }
58+ impl CloudClientBuilder {
59+ /// Create a new builder
60+ pub fn new ( ) -> Self {
61+ Self :: default ( )
62+ }
63+
64+ /// Set the API key
65+ pub fn api_key ( mut self , key : impl Into < String > ) -> Self {
66+ self . api_key = Some ( key. into ( ) ) ;
67+ self
68+ }
69+
70+ /// Set the API secret
71+ pub fn api_secret ( mut self , secret : impl Into < String > ) -> Self {
72+ self . api_secret = Some ( secret. into ( ) ) ;
73+ self
74+ }
75+
76+ /// Set the base URL
77+ pub fn base_url ( mut self , url : impl Into < String > ) -> Self {
78+ self . base_url = url. into ( ) ;
79+ self
80+ }
81+
82+ /// Set the timeout
83+ pub fn timeout ( mut self , timeout : std:: time:: Duration ) -> Self {
84+ self . timeout = timeout;
85+ self
86+ }
87+
88+ /// Build the client
89+ pub fn build ( self ) -> Result < CloudClient > {
90+ let api_key = self
91+ . api_key
92+ . ok_or_else ( || RestError :: ConnectionError ( "API key is required" . to_string ( ) ) ) ?;
93+ let api_secret = self
94+ . api_secret
95+ . ok_or_else ( || RestError :: ConnectionError ( "API secret is required" . to_string ( ) ) ) ?;
3496
35- impl CloudClient {
36- /// Create a new Cloud API client
37- pub fn new ( config : CloudConfig ) -> Result < Self > {
3897 let client = Client :: builder ( )
39- . timeout ( config . timeout )
98+ . timeout ( self . timeout )
4099 . build ( )
41100 . map_err ( |e| RestError :: ConnectionError ( e. to_string ( ) ) ) ?;
42101
43102 Ok ( CloudClient {
44- config,
103+ api_key,
104+ api_secret,
105+ base_url : self . base_url ,
106+ timeout : self . timeout ,
45107 client : Arc :: new ( client) ,
46108 } )
47109 }
110+ }
111+
112+ /// Redis Cloud API client
113+ #[ derive( Clone ) ]
114+ pub struct CloudClient {
115+ pub ( crate ) api_key : String ,
116+ pub ( crate ) api_secret : String ,
117+ pub ( crate ) base_url : String ,
118+ #[ allow( dead_code) ]
119+ pub ( crate ) timeout : std:: time:: Duration ,
120+ pub ( crate ) client : Arc < Client > ,
121+ }
122+
123+ impl CloudClient {
124+ /// Create a new builder for the client
125+ pub fn builder ( ) -> CloudClientBuilder {
126+ CloudClientBuilder :: new ( )
127+ }
48128
49129 /// Make a GET request with API key authentication
50130 pub async fn get < T : serde:: de:: DeserializeOwned > ( & self , path : & str ) -> Result < T > {
51- let url = format ! ( "{}{}" , self . config . base_url, path) ;
131+ let url = format ! ( "{}{}" , self . base_url, path) ;
52132
53133 // Redis Cloud API uses these headers for authentication
54134 let response = self
55135 . client
56136 . get ( & url)
57- . header ( "x-api-key" , & self . config . api_key )
58- . header ( "x-api-secret-key" , & self . config . api_secret )
137+ . header ( "x-api-key" , & self . api_key )
138+ . header ( "x-api-secret-key" , & self . api_secret )
59139 . send ( )
60140 . await ?;
61141
@@ -68,14 +148,14 @@ impl CloudClient {
68148 path : & str ,
69149 body : & B ,
70150 ) -> Result < T > {
71- let url = format ! ( "{}{}" , self . config . base_url, path) ;
151+ let url = format ! ( "{}{}" , self . base_url, path) ;
72152
73153 // Same backwards header naming as GET
74154 let response = self
75155 . client
76156 . post ( & url)
77- . header ( "x-api-key" , & self . config . api_key )
78- . header ( "x-api-secret-key" , & self . config . api_secret )
157+ . header ( "x-api-key" , & self . api_key )
158+ . header ( "x-api-secret-key" , & self . api_secret )
79159 . json ( body)
80160 . send ( )
81161 . await ?;
@@ -89,14 +169,14 @@ impl CloudClient {
89169 path : & str ,
90170 body : & B ,
91171 ) -> Result < T > {
92- let url = format ! ( "{}{}" , self . config . base_url, path) ;
172+ let url = format ! ( "{}{}" , self . base_url, path) ;
93173
94174 // Same backwards header naming as GET
95175 let response = self
96176 . client
97177 . put ( & url)
98- . header ( "x-api-key" , & self . config . api_key )
99- . header ( "x-api-secret-key" , & self . config . api_secret )
178+ . header ( "x-api-key" , & self . api_key )
179+ . header ( "x-api-secret-key" , & self . api_secret )
100180 . json ( body)
101181 . send ( )
102182 . await ?;
@@ -106,14 +186,14 @@ impl CloudClient {
106186
107187 /// Make a DELETE request
108188 pub async fn delete ( & self , path : & str ) -> Result < ( ) > {
109- let url = format ! ( "{}{}" , self . config . base_url, path) ;
189+ let url = format ! ( "{}{}" , self . base_url, path) ;
110190
111191 // Same backwards header naming as GET
112192 let response = self
113193 . client
114194 . delete ( & url)
115- . header ( "x-api-key" , & self . config . api_key )
116- . header ( "x-api-secret-key" , & self . config . api_secret )
195+ . header ( "x-api-key" , & self . api_key )
196+ . header ( "x-api-secret-key" , & self . api_secret )
117197 . send ( )
118198 . await ?;
119199
@@ -150,14 +230,14 @@ impl CloudClient {
150230 path : & str ,
151231 body : serde_json:: Value ,
152232 ) -> Result < serde_json:: Value > {
153- let url = format ! ( "{}{}" , self . config . base_url, path) ;
233+ let url = format ! ( "{}{}" , self . base_url, path) ;
154234
155235 // Use backwards header names for compatibility
156236 let response = self
157237 . client
158238 . patch ( & url)
159- . header ( "x-api-key" , & self . config . api_key )
160- . header ( "x-api-secret-key" , & self . config . api_secret )
239+ . header ( "x-api-key" , & self . api_key )
240+ . header ( "x-api-secret-key" , & self . api_secret )
161241 . json ( & body)
162242 . send ( )
163243 . await ?;
@@ -167,14 +247,14 @@ impl CloudClient {
167247
168248 /// Execute raw DELETE request returning any response body
169249 pub async fn delete_raw ( & self , path : & str ) -> Result < serde_json:: Value > {
170- let url = format ! ( "{}{}" , self . config . base_url, path) ;
250+ let url = format ! ( "{}{}" , self . base_url, path) ;
171251
172252 // Use backwards header names for compatibility
173253 let response = self
174254 . client
175255 . delete ( & url)
176- . header ( "x-api-key" , & self . config . api_key )
177- . header ( "x-api-secret-key" , & self . config . api_secret )
256+ . header ( "x-api-key" , & self . api_key )
257+ . header ( "x-api-secret-key" , & self . api_secret )
178258 . send ( )
179259 . await ?;
180260
0 commit comments