avalanche_installer/
github.rs

1use std::{
2    io::{self, Error, ErrorKind},
3    time::Duration,
4};
5
6use reqwest::ClientBuilder;
7use serde::{Deserialize, Serialize};
8
9/// ref. https://github.com/ava-labs/avalanchego/releases
10/// ref. https://api.github.com/repos/ava-labs/avalanchego/releases/latest
11pub async fn fetch_latest_release(org: &str, repo: &str) -> io::Result<ReleaseResponse> {
12    let ep = format!(
13        "https://api.github.com/repos/{}/{}/releases/latest",
14        org, repo
15    );
16    log::info!("fetching {}", ep);
17
18    let cli = ClientBuilder::new()
19        .user_agent(env!("CARGO_PKG_NAME"))
20        .danger_accept_invalid_certs(true)
21        .timeout(Duration::from_secs(15))
22        .connection_verbose(true)
23        .build()
24        .map_err(|e| {
25            Error::new(
26                ErrorKind::Other,
27                format!("failed ClientBuilder build {}", e),
28            )
29        })?;
30    let resp =
31        cli.get(&ep).send().await.map_err(|e| {
32            Error::new(ErrorKind::Other, format!("failed ClientBuilder send {}", e))
33        })?;
34    let out = resp
35        .bytes()
36        .await
37        .map_err(|e| Error::new(ErrorKind::Other, format!("failed ClientBuilder send {}", e)))?;
38    let out: Vec<u8> = out.into();
39
40    let resp: ReleaseResponse = match serde_json::from_slice(&out) {
41        Ok(p) => p,
42        Err(e) => {
43            return Err(Error::new(
44                ErrorKind::Other,
45                format!("failed to decode {}", e),
46            ));
47        }
48    };
49    Ok(resp)
50}
51
52/// ref. https://api.github.com/repos/ava-labs/avalanchego/releases/latest
53#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
54#[serde(rename_all = "snake_case")]
55pub struct ReleaseResponse {
56    /// Sometimes empty for github API consistency issue.
57    pub tag_name: Option<String>,
58    /// Sometimes empty for github API consistency issue.
59    pub assets: Option<Vec<Asset>>,
60
61    #[serde(default)]
62    pub prerelease: bool,
63}
64
65impl Default for ReleaseResponse {
66    fn default() -> Self {
67        Self::default()
68    }
69}
70
71impl ReleaseResponse {
72    pub fn default() -> Self {
73        Self {
74            tag_name: None,
75            assets: None,
76            prerelease: false,
77        }
78    }
79}
80
81/// ref. https://api.github.com/repos/ava-labs/avalanchego/releases/latest
82#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
83#[serde(rename_all = "snake_case")]
84pub struct Asset {
85    pub name: String,
86    pub browser_download_url: String,
87}