Skip to main content

hot_dev/
client.rs

1use std::sync::Arc;
2use std::time::Duration;
3
4use reqwest::{Method, RequestBuilder, Response};
5use serde_json::Value;
6
7use crate::resources::{
8    BuildsResource, ContextResource, DomainsResource, EnvResource, EventsResource, FilesResource,
9    OrgResource, ProjectsResource, RunsResource, ServiceKeysResource, SessionsResource,
10    StreamsResource, TasksResource,
11};
12use crate::transport::{Transport, DEFAULT_BASE_URL};
13use crate::{JsonObject, Result};
14
15/// Hot API v1 client. Construct it with [`HotClient::builder`]; the resource
16/// accessors mirror the Hot API resources.
17#[derive(Clone)]
18pub struct HotClient {
19    transport: Arc<Transport>,
20}
21
22/// Builder for [`HotClient`].
23pub struct HotClientBuilder {
24    token: String,
25    base_url: String,
26    timeout: Option<Duration>,
27    client: Option<reqwest::Client>,
28}
29
30impl HotClient {
31    /// Returns a client with default configuration.
32    pub fn new(token: impl Into<String>) -> HotClient {
33        HotClient::builder(token).build()
34    }
35
36    /// Returns a builder using the given Hot API key, session token, or
37    /// service key.
38    pub fn builder(token: impl Into<String>) -> HotClientBuilder {
39        HotClientBuilder {
40            token: token.into(),
41            base_url: DEFAULT_BASE_URL.to_string(),
42            timeout: None,
43            client: None,
44        }
45    }
46
47    /// The configured base URL without the /v1 suffix.
48    pub fn base_url(&self) -> &str {
49        self.transport.base_url()
50    }
51
52    pub fn builds(&self) -> BuildsResource {
53        BuildsResource::new(self.transport.clone())
54    }
55
56    pub fn context(&self) -> ContextResource {
57        ContextResource::new(self.transport.clone())
58    }
59
60    pub fn domains(&self) -> DomainsResource {
61        DomainsResource::new(self.transport.clone())
62    }
63
64    pub fn env(&self) -> EnvResource {
65        EnvResource::new(self.transport.clone())
66    }
67
68    pub fn events(&self) -> EventsResource {
69        EventsResource::new(self.transport.clone())
70    }
71
72    pub fn files(&self) -> FilesResource {
73        FilesResource::new(self.transport.clone())
74    }
75
76    pub fn org(&self) -> OrgResource {
77        OrgResource::new(self.transport.clone())
78    }
79
80    pub fn projects(&self) -> ProjectsResource {
81        ProjectsResource::new(self.transport.clone())
82    }
83
84    pub fn runs(&self) -> RunsResource {
85        RunsResource::new(self.transport.clone())
86    }
87
88    pub fn service_keys(&self) -> ServiceKeysResource {
89        ServiceKeysResource::new(self.transport.clone())
90    }
91
92    pub fn sessions(&self) -> SessionsResource {
93        SessionsResource::new(self.transport.clone())
94    }
95
96    pub fn streams(&self) -> StreamsResource {
97        StreamsResource::new(self.transport.clone())
98    }
99
100    pub fn tasks(&self) -> TasksResource {
101        TasksResource::new(self.transport.clone())
102    }
103
104    /// Calls a JSON API endpoint that does not yet have a resource helper.
105    /// `path` is relative to /v1 and the returned object is the response
106    /// envelope.
107    pub async fn request(
108        &self,
109        method: Method,
110        path: &str,
111        body: Option<&Value>,
112        query: &[(&str, &str)],
113    ) -> Result<JsonObject> {
114        self.transport.request_json(method, path, body, query).await
115    }
116
117    /// Returns an authorized [`RequestBuilder`] for a path relative to /v1,
118    /// for endpoints needing full control (raw bodies, custom headers).
119    /// Send it with [`HotClient::execute`] to get API error mapping.
120    pub fn request_builder(&self, method: Method, path: &str) -> RequestBuilder {
121        self.transport.request_builder(method, path)
122    }
123
124    /// Sends a request built with [`HotClient::request_builder`], mapping
125    /// non-2xx responses to [`crate::ApiError`].
126    pub async fn execute(&self, builder: RequestBuilder) -> Result<Response> {
127        self.transport.execute(builder).await
128    }
129}
130
131impl HotClientBuilder {
132    /// Overrides the base URL (default `https://api.hot.dev`). For local
133    /// development with `hot dev`, use `http://localhost:4681`.
134    pub fn base_url(mut self, base_url: impl Into<String>) -> Self {
135        self.base_url = base_url.into();
136        self
137    }
138
139    /// Bounds each JSON request. Not applied to streaming or raw requests.
140    pub fn timeout(mut self, timeout: Duration) -> Self {
141        self.timeout = Some(timeout);
142        self
143    }
144
145    /// Overrides the underlying [`reqwest::Client`], e.g. to configure
146    /// proxies or connection pools. Do not set a client-wide timeout on a
147    /// client used for SSE subscriptions — it would sever long-lived streams.
148    pub fn http_client(mut self, client: reqwest::Client) -> Self {
149        self.client = Some(client);
150        self
151    }
152
153    pub fn build(self) -> HotClient {
154        HotClient {
155            transport: Arc::new(Transport::new(
156                self.token,
157                self.base_url,
158                self.timeout,
159                self.client,
160            )),
161        }
162    }
163}