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}