playground_api/blocking.rs
1//! Holds the blocking version of the Client. Only accessible by enabling the `blocking` feature.
2
3#[cfg(feature = "blocking")]
4use crate::{endpoints::*, error::Error};
5#[cfg(feature = "blocking")]
6use serde::{de::Deserialize, Serialize};
7#[cfg(feature = "blocking")]
8use url::{ParseError, Url};
9
10/// A client for interacting with the Rust playground API.
11///
12/// Holds the base URL and the `reqwest::blocking::Client` struct for all requests.
13#[cfg(feature = "blocking")]
14#[derive(Clone)]
15pub struct Client {
16 url: Url,
17 client: reqwest::blocking::Client,
18}
19
20#[cfg(feature = "blocking")]
21impl Client {
22 /// Creates a new `Client` instance with the given base URL.
23 ///
24 /// Parses the provided string into a `Url`. Returns an error if the URL is invalid.
25 ///
26 /// # Arguments
27 ///
28 /// * `url` - A string slice representing the base URL of the Rust playground API.
29 ///
30 /// # Returns
31 ///
32 /// * `Result<Client, Error>` - On success, returns a `Client` configured with the parsed URL.
33 /// On failure, returns an `Error` if the URL string is invalid.
34 pub fn new(url: &str) -> Result<Client, Error> {
35 let url = Url::parse(url)?;
36 let client = reqwest::blocking::Client::new();
37 Ok(Client { url, client })
38 }
39
40 /// Sends a code execution request to the Rust playground and returns the result.
41 ///
42 /// This asynchronous method takes and [`ExecuteRequest`] struct containing the code
43 /// execution parameters, sends it to the appropriate endpoint on the Rust playground
44 /// via a POST request, and returns the execution result.
45 ///
46 /// # Arguments
47 ///
48 /// * `request` - A reference to an [`ExecuteRequest`] that includes the code snippet
49 /// and configuration options such as edition, crate type, and whether to run or compile.
50 ///
51 /// # Returns
52 ///
53 /// * `Result<ExecuteResponse, Error>` - On success, returns an [`ExecuteResponse`] containing
54 /// the output, errors, and status from the Rust playground. On failure, returns an [`Error`].
55 ///
56 /// # Errors
57 ///
58 /// This function will return an error if the HTTP request fails, if the response cannot be parsed,
59 /// or if the playground service is unavailable.
60 pub fn execute(&self, request: &ExecuteRequest) -> Result<ExecuteResponse, Error> {
61 self.post(request, Endpoints::Execute)
62 }
63
64 /// Sends a code compilation request to the Rust playground and returns the result.
65 ///
66 /// This asynchronous method takes a [`CompileRequest`] containing the code and
67 /// compilation parameters, sends it to the Rust playground's compile endpoint,
68 /// and returns the compilation result.
69 ///
70 /// # Arguments
71 ///
72 /// * `request` - A reference to a [`CompileRequest`] that includes the code and metadata
73 /// such as the toolchain edition, crate type, target, and any compiler settings.
74 ///
75 /// # Returns
76 ///
77 /// * `Result<CompileResponse, Error>` - On success, returns a [`CompileResponse`] containing
78 /// the compiler output, including success/failure status, messages, and possible warnings or errors.
79 /// On failure, returns an [`Error`] describing the issue.
80 ///
81 /// # Errors
82 ///
83 /// Returns an error if the HTTP request fails, if the response cannot be parsed correctly,
84 /// or if the playground service encounters an issue.
85 pub fn compile(&self, request: &CompileRequest) -> Result<CompileResponse, Error> {
86 self.post(request, Endpoints::Compile)
87 }
88
89 /// Sends a code formatting request to the Rust playground and returns the formatted result.
90 ///
91 /// This asynchronous method takes a [`FormatRequest`] containing the Rust code and formatting options,
92 /// sends it to the Rust playground's format endpoint, and returns the formatted code or any errors.
93 ///
94 /// # Arguments
95 ///
96 /// * `request` - A reference to a [`FormatRequest`] that includes the code to be formatted and
97 /// optional parameters like the edition to use.
98 ///
99 /// # Returns
100 ///
101 /// * `Result<FormatResponse, Error>` - On success, returns a [`FormatResponse`] containing the
102 /// formatted code or an error message if the code could not be formatted.
103 /// On failure, returns an [`Error`] representing issues like network failure or parsing problems.
104 ///
105 /// # Errors
106 ///
107 /// This function may return an error if the request fails, the response is invalid,
108 /// or the Rust playground's formatting service encounters a problem.
109 pub fn format(&self, request: &FormatRequest) -> Result<FormatResponse, Error> {
110 self.post(request, Endpoints::Format)
111 }
112
113 /// Sends a Clippy linting request to the Rust playground and returns the analysis result.
114 ///
115 /// This asynchronous method takes a [`ClippyRequest`] containing the Rust code and configuration,
116 /// sends it to the Rust playground's Clippy endpoint, and returns any linter warnings, errors,
117 /// or suggestions provided by Clippy.
118 ///
119 /// # Arguments
120 ///
121 /// * `request` - A reference to a [`ClippyRequest`] that includes the code to be analyzed
122 /// and optional parameters such as edition or crate type.
123 ///
124 /// # Returns
125 ///
126 /// * `Result<ClippyResponse, Error>` - On success, returns a [`ClippyResponse`] containing
127 /// Clippy's diagnostic output (warnings, errors, suggestions). On failure, returns an [`Error`]
128 /// describing what went wrong (e.g., network error, bad request, or service issue).
129 ///
130 /// # Errors
131 ///
132 /// Returns an error if the request cannot be completed, the response is invalid,
133 /// or the Clippy service is unavailable or encounters an internal error.
134 pub fn clippy(&self, request: &ClippyRequest) -> Result<ClippyResponse, Error> {
135 self.post(request, Endpoints::Clippy)
136 }
137
138 /// Sends a Miri request to the Rust playground and returns the result of interpreting the code.
139 ///
140 /// This asynchronous method takes a [`MiriRequest`] containing the Rust code and any
141 /// interpreter-specific options, sends it to the Rust playground's Miri endpoint, and
142 /// returns the result of running the interpreter on the code.
143 ///
144 /// # Arguments
145 ///
146 /// * `request` - A reference to a [`MiriRequest`] that includes the code and metadata
147 /// such as edition, crate type, and other configuration options.
148 ///
149 /// # Returns
150 ///
151 /// * `Result<MiriResponse, Error>` - On success, returns a [`MiriResponse`] containing the
152 /// result of the interpretation. On failure, returns an [`Error`] describing the issue.
153 ///
154 /// # Errors
155 ///
156 /// Returns an error if the request fails, if the response is invalid, or if the Miri service
157 /// encounters an internal issue.
158 pub fn miri(&self, request: &MiriRequest) -> Result<MiriResponse, Error> {
159 self.post(request, Endpoints::Miri)
160 }
161
162 /// Sends a macro expansion request to the Rust playground and returns the result.
163 ///
164 /// This asynchronous method takes a [`MacroExpansionRequest`] with Rust code containing macros,
165 /// sends it to the Rust playground's macro expansion endpoint, and returns the result
166 /// of the expanded macros.
167 ///
168 /// # Arguments
169 ///
170 /// * `request` - A reference to a [`MacroExpansionRequest`] that includes the code and any
171 /// configuration options like the edition to use.
172 ///
173 /// # Returns
174 ///
175 /// * `Result<MacroExpansionResponse, Error>` - On success, returns a [`MacroExpansionResponse`]
176 /// containing the macro-expanded version of the code. On failure, returns an [`Error`] describing
177 /// the issue.
178 ///
179 /// # Errors
180 ///
181 /// Returns an error if the HTTP request fails, if the response is invalid, or if the macro expansion
182 /// service encounters an issue.
183 pub fn macro_expansion(
184 &self,
185 request: &MacroExpansionRequest,
186 ) -> Result<MacroExpansionResponse, Error> {
187 self.post(request, Endpoints::MacroExpansion)
188 }
189
190 /// Retrieves the list of available crates from the Rust playground.
191 ///
192 /// This asynchronous method sends a GET request to the crates endpoint
193 /// and returns a list of crates supported by the playground environment.
194 ///
195 /// # Returns
196 ///
197 /// * `Result<CratesResponse, Error>` - On success, returns a [`CratesResponse`] containing
198 /// the names and versions of available crates. On failure, returns an [`Error`] describing
199 /// the problem.
200 ///
201 /// # Errors
202 ///
203 /// Returns an error if the request fails, if the response cannot be parsed,
204 /// or if the crates service is unavailable.
205 pub fn crates(&self) -> Result<CratesResponse, Error> {
206 self.get(Endpoints::Crates)
207 }
208
209 /// Retrieves the supported versions and metadata of the Rust playground.
210 ///
211 /// This asynchronous method sends a GET request to the versions endpoint and
212 /// returns information about supported Rust versions, targets, and environments.
213 ///
214 /// # Returns
215 ///
216 /// * `Result<VersionsResponse, Error>` - On success, returns a [`VersionsResponse`]
217 /// containing version details. On failure, returns an [`Error`] describing what went wrong.
218 ///
219 /// # Errors
220 ///
221 /// Returns an error if the request cannot be completed, the response is malformed,
222 /// or if the versions service is unavailable.
223 pub fn versions(&self) -> Result<VersionsResponse, Error> {
224 self.get(Endpoints::Versions)
225 }
226
227 /// Creates a GitHub Gist from the provided Rust playground code.
228 ///
229 /// This asynchronous method sends a [`GistCreateRequest`] to the Gist creation endpoint
230 /// and returns a response containing the Gist URL or error information.
231 ///
232 /// # Arguments
233 ///
234 /// * `request` - A reference to a [`GistCreateRequest`] that includes the code to be uploaded
235 /// as a Gist and any additional metadata like description or visibility.
236 ///
237 /// # Returns
238 ///
239 /// * `Result<GistResponse, Error>` - On success, returns a [`GistResponse`] containing
240 /// the Gist ID and URL. On failure, returns an [`Error`] describing what went wrong.
241 ///
242 /// # Errors
243 ///
244 /// Returns an error if the HTTP request fails, if the response is malformed,
245 /// or if the Gist service is unavailable.
246 pub fn gist_create(&self, request: &GistCreateRequest) -> Result<GistResponse, Error> {
247 self.post(request, Endpoints::GistCreate)
248 }
249
250 /// Retrieves an existing GitHub Gist from the Rust playground.
251 ///
252 /// This asynchronous method sends a GET request to the Gist retrieval endpoint
253 /// using the provided Gist ID and returns the contents of the Gist.
254 ///
255 /// # Arguments
256 ///
257 /// * `id` - A `String` representing the unique identifier of the Gist to retrieve.
258 ///
259 /// # Returns
260 ///
261 /// * `Result<GistResponse, Error>` - On success, returns a [`GistResponse`] containing
262 /// the Gist's code and metadata. On failure, returns an [`Error`] describing the issue.
263 ///
264 /// # Errors
265 ///
266 /// Returns an error if the HTTP request fails, if the response is invalid,
267 /// or if the Gist could not be found.
268 pub fn gist_get(&self, id: String) -> Result<GistResponse, Error> {
269 self.get(Endpoints::GistGet(id))
270 }
271
272 /// Sends a POST request with a serialized JSON payload to the specified endpoint,
273 /// and deserializes the response into the expected type.
274 ///
275 /// Used internally to interact with Rust playground endpoints.
276 fn post<T, U>(&self, request: &T, endpoint: Endpoints) -> Result<U, Error>
277 where
278 T: Serialize,
279 U: for<'de> Deserialize<'de>,
280 {
281 let url = self.get_url(endpoint)?;
282 let res = self.client.post(url).json(request).send()?;
283
284 if !res.status().is_success() {
285 return Err(Error::NoSuccess(res.status().as_u16()));
286 }
287
288 let res = res.json::<U>()?;
289 Ok(res)
290 }
291
292 /// Sends a GET request to the specified endpoint, and deserializes the response
293 /// into the expected type.
294 ///
295 /// Used internally to interact with Rust playground endpoints.
296 fn get<U>(&self, endpoint: Endpoints) -> Result<U, Error>
297 where
298 U: for<'de> Deserialize<'de>,
299 {
300 let url = self.get_url(endpoint)?;
301 let res = self.client.get(url).send()?;
302
303 if !res.status().is_success() {
304 return Err(Error::NoSuccess(res.status().as_u16()));
305 }
306
307 let res = res.json::<U>()?;
308 Ok(res)
309 }
310
311 /// Takes an endpoint and returns the correct url.
312 fn get_url(&self, endpoint: Endpoints) -> Result<Url, ParseError> {
313 let url = match endpoint {
314 Endpoints::Execute => self.url.join("execute"),
315 Endpoints::Compile => self.url.join("compile"),
316 Endpoints::Format => self.url.join("format"),
317 Endpoints::Clippy => self.url.join("clippy"),
318 Endpoints::Miri => self.url.join("miri"),
319 Endpoints::Crates => self.url.join("meta/crates"),
320 Endpoints::Versions => self.url.join("meta/versions"),
321 Endpoints::MacroExpansion => self.url.join("macro-expansion"),
322 Endpoints::GistCreate => self.url.join("meta/gist"),
323 Endpoints::GistGet(id) => self.url.join(&format!("meta/gist/{}", id)),
324 }?;
325 Ok(url)
326 }
327}
328
329#[cfg(feature = "blocking")]
330impl Default for Client {
331 /// Creates a `Client` instance with the following url <https://play.rust-lang.org/>
332 fn default() -> Self {
333 let client = reqwest::blocking::Client::new();
334 Self {
335 url: Url::parse("https://play.rust-lang.org/").unwrap(),
336 client,
337 }
338 }
339}