1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
use crate::error::Error;
use crate::pagination::ListResponse;
use reqwest::{ClientBuilder, RequestBuilder};

mod error;
pub mod pagination;
pub mod v2;

const BASE_URL: &str = "https://services.nvd.nist.gov/rest/json/";

#[derive(Debug, Clone)]
pub struct NVDApi {
  base_path: String,
  version: String,
  client: reqwest::Client,
}

pub enum ApiVersion {
  V2_0,
}

impl Default for ApiVersion {
  fn default() -> Self {
    Self::V2_0
  }
}

impl ToString for ApiVersion {
  fn to_string(&self) -> String {
    match self {
      ApiVersion::V2_0 => String::from("2.0"),
    }
  }
}

impl NVDApi {
  pub fn new(api_token: Option<String>, version: ApiVersion) -> Result<Self, Error> {
    let mut headers = reqwest::header::HeaderMap::new();
    if let Some(api_token) = api_token {
      let mut auth_value = reqwest::header::HeaderValue::from_str(&format!("Bearer {api_token}"))
        .map_err(|source| Error::InvalidApiToken { source })?;
      auth_value.set_sensitive(true);
      headers.insert(reqwest::header::AUTHORIZATION, auth_value);
    }
    let api_client = ClientBuilder::new()
      .default_headers(headers)
      .build()
      .map_err(|source| Error::BuildingClient { source })?;
    Ok(NVDApi {
      base_path: BASE_URL.to_owned(),
      version: version.to_string(),
      client: api_client,
    })
  }
}

impl NVDApi {
  pub async fn request(&self, request: RequestBuilder) -> Result<ListResponse, Error> {
    let request = request.build()?;
    let json = self
      .client
      .execute(request)
      .await
      .map_err(|source| Error::RequestFailed { source })?
      .text()
      .await
      .map_err(|source| Error::ResponseIo { source })?;
    let result = serde_json::from_str(&json).map_err(|source| Error::JsonParse { source })?;
    Ok(result)
  }
}