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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//! # Kosmos: A client library for apis of the cosmos.

//! Kosmos is a client library for a whole host of apis dealing with the

//! universe. This includes apis for our earth e.g. Geomagnetism data, sea

//! level data, as well as apis dealing with space. Specific services are exposed under

//! their respective owning organization, e.g. Nasa's Astronomy Photo of the

//! Day requires you to build a `NasaClient`. This is because most organizations

//! share an auth strategy over all their services.

//!

//! The apis are behind feature flags but all are included by default. If you

//! want to cherry-pick apis, use `deafult-features = false`.

//!

//! Organizations (and feature flags) with APIs we support (so far)

//! - [`nasa`] NASA Open APIs

//!

//! [`nasa`]: ./nasa/struct.NasaClient.html

//!

//! #### Lookup an Asteroid

//! ```no_run

//! # use kosmos::Kosmos;

//! # async fn run() -> kosmos::Result<()> {

//! // Get asteroid 3542519 from the NASA NeoWS

//! let asteroid = Kosmos::new().nasa().neo().lookup(3542519).await;

//! # Ok(())

//! # }

//! ```


pub mod error;
mod from_response;
#[cfg(feature = "nasa")]
pub mod nasa;

pub use self::error::Error;
use from_response::FromResponse;
use serde::Serialize;
use snafu::*;

pub type Result<T, E = Error> = std::result::Result<T, E>;

#[derive(Debug, Clone)]
pub struct Kosmos {
    client: reqwest::Client,
}

impl Kosmos {
    pub fn new() -> Self {
        Self {
            client: reqwest::ClientBuilder::new()
                .user_agent("kosmos")
                .build()
                .unwrap(),
        }
    }

    #[cfg(feature = "nasa")]
    pub fn nasa(&self) -> nasa::NasaClient {
        nasa::NasaClient::new(self)
    }

    pub(crate) async fn get<R, A, P>(&self, url: A, parameters: Option<&P>) -> Result<R>
    where
        A: AsRef<str>,
        P: Serialize + ?Sized,
        R: FromResponse,
    {
        let url = url::Url::parse(url.as_ref()).context(error::Url)?;
        let response = self._get(url, parameters).await?;
        R::from_response(response).await
    }

    pub(crate) async fn _get<P: Serialize + ?Sized>(
        &self,
        url: impl reqwest::IntoUrl,
        parameters: Option<&P>,
    ) -> Result<reqwest::Response> {
        let mut request = self.client.get(url);

        if let Some(parameters) = parameters {
            request = request.query(parameters);
        }

        self.execute(request).await
    }

    pub(crate) async fn execute(
        &self,
        request: reqwest::RequestBuilder,
    ) -> Result<reqwest::Response> {
        request.send().await.context(error::Http)
    }
}