The cross-platform version manager toolset
crates.io/crates/hyper-jump
1use std::borrow::Cow;
2
3use anyhow::anyhow;
4use anyhow::Result;
5use reqwest::Client;
6use serde::de::DeserializeOwned;
7use serde::Deserialize;
8use serde::Serialize;
9
10/// Represents an error response from the GitHub API.
11///
12/// This struct contains information about an error response from the GitHub
13/// API, including the error message and the URL of the documentation related to
14/// the error.
15///
16/// # Fields
17///
18/// * `message: String` - The error message from the GitHub API.
19/// * `documentation_url: String` - The URL of the documentation related to the
20/// error.
21///
22/// # Example
23///
24/// ```rust
25/// let error_response = ErrorResponse {
26/// message: "Not Found".to_string(),
27/// documentation_url: "https://docs.github.com/rest".to_string(),
28/// };
29/// println!("The error message is {}", error_response.message);
30/// println!(
31/// "The documentation URL is {}",
32/// error_response.documentation_url
33/// );
34/// ```
35#[derive(Debug, Deserialize, Serialize)]
36pub struct ErrorResponse {
37 pub message: String,
38 pub documentation_url: String,
39}
40
41pub async fn api(client: Option<&Client>, url: String) -> Result<String> {
42 let response = client
43 .expect("Client not found")
44 .get(url)
45 .header(reqwest::header::USER_AGENT, "hyper-jump")
46 .header(reqwest::header::ACCEPT, "application/vnd.github.v3+json")
47 .send()
48 .await?
49 .error_for_status()?
50 .text()
51 .await?;
52
53 Ok(response)
54}
55
56/// Deserializes a JSON response from the GitHub API.
57///
58/// # Parameters
59///
60/// * `response: String` - The JSON response from the GitHub API as a string.
61///
62/// # Returns
63///
64/// * `Result<T>` - The deserialized response as the specified type `T`, or an
65/// error if the response could not be deserialized or contains an error
66/// message.
67///
68/// # Errors
69///
70/// This function will return an error if the response contains a "message"
71/// field (indicating an error from the GitHub API), or if the response could
72/// not be deserialized into the specified type `T`.
73///
74/// # Example
75///
76/// ```rust
77/// let response = "{\"data\": \"some data\"}";
78/// let result: Result<MyType> = deserialize_response(response);
79/// match result {
80/// Ok(data) => println!("Received data: {:?}", data),
81/// Err(e) => println!("An error occurred: {:?}", e),
82/// }
83/// ```
84pub fn deserialize_response<T: DeserializeOwned>(response: String) -> Result<T> {
85 let value: serde_json::Value = serde_json::from_str(&response)?;
86 if value.get("message").is_some() {
87 let result: ErrorResponse = serde_json::from_value(value)?;
88 if result.documentation_url.contains("rate-limiting") {
89 return Err(anyhow!("Rate limited by GitHub API"));
90 }
91
92 return Err(anyhow!(result.message));
93 }
94
95 Ok(serde_json::from_value(value)?)
96}