nvrs/api/
mod.rs

1//! this module handles management & communication with sources, also knows as APIs
2
3#[cfg(feature = "aur")]
4mod aur;
5#[cfg(feature = "crates-io")]
6mod crates_io;
7#[cfg(feature = "gitea")]
8mod gitea;
9#[cfg(feature = "github")]
10mod github;
11#[cfg(feature = "gitlab")]
12mod gitlab;
13#[cfg(feature = "regex")]
14mod regex;
15#[cfg(feature = "shell")]
16mod shell;
17
18/// struct containing the API name & a pointer to API's `get_latest` function
19pub struct Api {
20    /// name of the API
21    pub name: &'static str,
22    /// pointer to the API's `get_latest` function
23    pub(crate) func: fn(ApiArgs) -> ReleaseFuture,
24}
25
26/// arguments passed to a source
27pub(crate) struct ApiArgs {
28    pub request_client: reqwest::Client,
29    /// name of the package
30    pub package: String,
31    pub use_max_tag: Option<bool>,
32    /// arguments passed to the source
33    pub args: Vec<String>,
34    pub api_key: String, // empty String if none
35}
36
37/// this is what `get_latest`s return
38#[derive(Debug)]
39pub struct Release {
40    /// name of the package
41    pub name: String,
42    /// version of the package
43    pub tag: Option<String>,
44    /// url to the version's source
45    pub url: String,
46}
47
48// this is necessary because we need to store a reference to an async function in `Api`
49type ReleaseFuture =
50    std::pin::Pin<Box<dyn std::future::Future<Output = crate::error::Result<Release>> + Send>>;
51
52fn setup_headers() -> reqwest::header::HeaderMap {
53    use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT};
54
55    let mut headers = HeaderMap::new();
56    headers.insert(USER_AGENT, HeaderValue::from_static("nvrs"));
57
58    headers
59}
60
61fn match_statuscode(status: &reqwest::StatusCode, package: String) -> crate::error::Result<()> {
62    use crate::error;
63    use reqwest::StatusCode;
64
65    match status.to_owned() {
66        StatusCode::OK => Ok(()),
67        StatusCode::FORBIDDEN => Err(error::Error::RequestForbidden(package)),
68        _ => Err(error::Error::RequestNotOK(package, status.to_string())),
69    }
70}
71
72/// public list of available sources
73pub const API_LIST: &[Api] = &[
74    #[cfg(feature = "aur")]
75    Api {
76        name: "aur",
77        func: aur::get_latest,
78    },
79    #[cfg(feature = "crates-io")]
80    Api {
81        name: "cratesio",
82        func: crates_io::get_latest,
83    },
84    #[cfg(feature = "gitea")]
85    Api {
86        name: "gitea",
87        func: gitea::get_latest,
88    },
89    #[cfg(feature = "github")]
90    Api {
91        name: "github",
92        func: github::get_latest,
93    },
94    #[cfg(feature = "gitlab")]
95    Api {
96        name: "gitlab",
97        func: gitlab::get_latest,
98    },
99    #[cfg(feature = "regex")]
100    Api {
101        name: "regex",
102        func: regex::get_latest,
103    },
104    #[cfg(feature = "shell")]
105    Api {
106        name: "shell",
107        func: shell::get,
108    },
109];
110
111#[test]
112fn statuscode_matching_test() {
113    use reqwest::StatusCode;
114
115    assert!(match_statuscode(&StatusCode::OK, String::new()).is_ok());
116    assert!(match_statuscode(&StatusCode::IM_A_TEAPOT, String::new()).is_err());
117}