dify_client/client.rs
1//! This module contains the implementation of the Dify client.
2//!
3//! The `client` module provides a `Client` struct that represents a client for interacting with the Dify API.
4//! It also includes a `Config` struct that holds the configuration for the client.
5//! The client supports creating requests, executing them, and returning the response.
6//! Additionally, it provides methods for creating form requests and handling multipart data.
7//!
8//! # Examples
9//!
10//! Creating a new client with default configuration:
11//!
12//! ```rust
13//! use dify_client::client::Client;
14//!
15//! let client = Client::new("https://api.dify.ai", "API_KEY");
16//! ```
17//!
18//! Creating a new client with custom configuration:
19//!
20//! ```rust
21//! use dify_client::client::{Client, Config};
22//! use std::time::Duration;
23//!
24//! let config = Config {
25//! base_url: "https://api.dify.ai".into(),
26//! api_key: "API_KEY".into(),
27//! timeout: Duration::from_secs(30),
28//! };
29//!
30//! let client = Client::new_with_config(config);
31//! ```
32use super::{
33 api::Api,
34 http::{header, multipart, Method, Request, Response},
35};
36use anyhow::{bail, Result as AnyResult};
37use std::{sync::Arc, time::Duration};
38
39#[derive(Clone, Debug)]
40/// The configuration for the Dify client.
41pub struct Config {
42 /// The base URL of the Dify API.
43 pub base_url: String,
44 /// The API key for the Dify API.
45 pub api_key: String,
46 /// The timeout for the client requests.
47 pub timeout: Duration,
48}
49
50/// Implements the default configuration for the client.
51impl Default for Config {
52 /// Returns a new instance of `ClientConfig` with default values.
53 fn default() -> Self {
54 Self {
55 base_url: "https://api.dify.ai".into(),
56 api_key: "API_KEY".into(),
57 timeout: Duration::from_secs(30),
58 }
59 }
60}
61
62/// The `Client` struct represents a client for interacting with the Dify API.
63#[derive(Clone, Debug)]
64pub struct Client {
65 /// The configuration for the client.
66 pub config: Arc<Config>,
67 /// The HTTP client for sending requests.
68 http_client: reqwest::Client,
69}
70
71/// The `Client` struct represents a client for interacting with the Dify API.
72impl Client {
73 /// Creates a new `Client` instance with the specified base URL and API key.
74 ///
75 /// # Arguments
76 /// * `base_url` - The base URL of the Dify API.
77 /// * `api_key` - The API key for authentication.
78 ///
79 /// # Returns
80 /// A new `Client` instance.
81 pub fn new(base_url: &str, api_key: &str) -> Self {
82 Self::new_with_config(Config {
83 base_url: base_url.into(),
84 api_key: api_key.into(),
85 ..Config::default()
86 })
87 }
88
89 /// Creates a new `Client` instance with the specified configuration.
90 ///
91 /// # Arguments
92 /// * `c` - The configuration for the client.
93 ///
94 /// # Returns
95 /// A new `Client` instance.
96 pub fn new_with_config(mut c: Config) -> Self {
97 // format the base URL
98 c.base_url = c.base_url.trim_end_matches("/").into();
99 // build the http client
100 let mut builder = reqwest::ClientBuilder::new();
101 if !c.timeout.is_zero() {
102 builder = builder.timeout(c.timeout);
103 }
104 let http_client = builder
105 .default_headers(Self::default_headers(&c))
106 .build()
107 .expect("Failed to create http client");
108
109 Self {
110 config: Arc::new(c),
111 http_client,
112 }
113 }
114
115 /// Returns the default headers for the client.
116 ///
117 /// # Arguments
118 /// * `c` - The configuration for the client.
119 ///
120 /// # Returns
121 /// The default headers for the client.
122 fn default_headers(c: &Config) -> header::HeaderMap {
123 let mut headers = header::HeaderMap::new();
124 headers.insert(
125 header::CACHE_CONTROL,
126 header::HeaderValue::from_static("no-cache"),
127 );
128 headers.insert(
129 header::CONTENT_TYPE,
130 header::HeaderValue::from_static("application/json; charset=utf-8"),
131 );
132
133 let auth = format!("Bearer {}", c.api_key);
134 let mut bearer_auth = header::HeaderValue::from_str(&auth).unwrap();
135 bearer_auth.set_sensitive(true);
136 headers.insert(header::AUTHORIZATION, bearer_auth);
137 headers
138 }
139
140 /// Returns the API for the client.
141 /// The API provides methods for interacting with the Dify API.
142 ///
143 /// # Returns
144 /// The API for the client.
145 pub fn api(&self) -> Api {
146 Api::new(self)
147 }
148
149 /// Creates a request with the specified URL, method, and data.
150 ///
151 /// # Arguments
152 /// * `url` - The URL for the request.
153 /// * `method` - The HTTP method for the request.
154 /// * `data` - The data for the request.
155 ///
156 /// # Returns
157 /// A `Result` containing the request or an error.
158 ///
159 /// # Errors
160 /// Returns an error if the request cannot be created.
161 ///
162 /// # Panics
163 /// Panics if the method is not supported.
164 pub(crate) fn create_request<T>(
165 &self,
166 url: String,
167 method: Method,
168 data: T,
169 ) -> AnyResult<Request>
170 where
171 T: serde::Serialize,
172 {
173 match method {
174 Method::POST => {
175 let r = self.http_client.post(url).json(&data).build()?;
176 Ok(r)
177 }
178 Method::GET => {
179 let r = self.http_client.get(url).query(&data).build()?;
180 Ok(r)
181 }
182 Method::DELETE => {
183 let r = self.http_client.delete(url).json(&data).build()?;
184 Ok(r)
185 }
186 _ => bail!("Method not supported"),
187 }
188 }
189
190 /// Creates a form request with the specified URL and data.
191 ///
192 /// # Arguments
193 /// * `url` - The URL for the request.
194 /// * `data` - The data for the request.
195 ///
196 /// # Returns
197 /// A `Result` containing the request or an error.
198 pub(crate) fn create_multipart_request(
199 &self,
200 url: String,
201 form_data: multipart::Form,
202 ) -> AnyResult<Request> {
203 let r = self.http_client.post(url).multipart(form_data).build()?;
204 Ok(r)
205 }
206
207 /// Executes the specified request and returns the response.
208 ///
209 /// # Arguments
210 /// * `request` - The request to execute.
211 ///
212 /// # Returns
213 /// A `Result` containing the response or an error.
214 pub(crate) async fn execute(&self, request: Request) -> AnyResult<Response> {
215 self.http_client.execute(request).await.map_err(Into::into)
216 }
217}