newswrap/
client.rs

1//! Hacker News API client bindings and various methods for interacting. The client is an async-first HTTP client and
2//! application authors should expect to bring an async runtime of their choosing. There are currently no plans to provide
3//! a blocking API, though this may change in future releases.
4
5use std::time::Duration;
6
7use crate::{
8    http::InternalHttpClient, items::client::HackerNewsItemClient,
9    realtime::client::HackerNewsRealtimeClient, users::client::HackerNewsUserClient,
10};
11
12/// Version information for the Hacker News API containing the base URLs.
13#[derive(Debug, Clone, Copy)]
14pub enum ApiVersion {
15    /// Represents version 0 of the Hacker News API.
16    V0,
17}
18
19/// All outgoing requests will have a user-agent associated to newswrap for request visibility.
20const USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),);
21
22/// Current URL of the API.
23const API_BASE_URL: &str = "https://hacker-news.firebaseio.com/v0";
24
25/// Default timeout for requests the API.
26const DEFAULT_TIMEOUT_SECONDS: u64 = 10;
27
28/// A wrapping HTTP client for Hacker News Firebase API and real-time data.
29/// A client instance should only be instantiated once in an application's
30/// lifecycle, seeking to reuse it where possible. Clients can be configured
31/// with requests timeouts in seconds, defaulting to 10 seconds.
32///
33/// ```
34/// use newswrap::client::HackerNewsClient;
35///
36/// // Default client with 10 second timeout
37/// let default_timeout_client = HackerNewsClient::new();
38///
39/// // Or, with an optional timeout
40/// let custom_client = HackerNewsClient::new_with_timeout_secs(10);
41/// let custom_client_with_duration = HackerNewsClient::new_with_timeout_duration(std::time::Duration::from_millis(400));
42/// ```
43#[derive(Debug)]
44pub struct HackerNewsClient {
45    /// An internal item client for interacting with items.
46    pub items: HackerNewsItemClient,
47    /// An internal user client for interacting with users.
48    pub users: HackerNewsUserClient,
49    /// An internal realtime client for interacting with live data.
50    pub realtime: HackerNewsRealtimeClient,
51    /// The internal version of the Hacker News API your client will target.
52    pub version: ApiVersion,
53}
54
55impl Default for HackerNewsClient {
56    fn default() -> Self {
57        Self::new()
58    }
59}
60
61impl HackerNewsClient {
62    /// Internally constructs the client allowing for flexibility in configuring the timeout.
63    fn new_client(timeout: std::time::Duration) -> Self {
64        let client = reqwest::ClientBuilder::new()
65            .timeout(timeout)
66            .user_agent(USER_AGENT)
67            .build()
68            // TODO: probably move this to a builder of sorts, if this panics we have much bigger problems
69            .unwrap();
70
71        let internal_client = InternalHttpClient::new(client, API_BASE_URL);
72        let item_client = HackerNewsItemClient::new(internal_client.clone());
73        let user_client: HackerNewsUserClient = HackerNewsUserClient::new(internal_client.clone());
74        let realtime_client = HackerNewsRealtimeClient::new(internal_client);
75
76        Self {
77            items: item_client,
78            users: user_client,
79            realtime: realtime_client,
80            version: ApiVersion::V0,
81        }
82    }
83
84    /// Constructs a new client pointing to the latest Hacker News API version.
85    pub fn new() -> Self {
86        let duration = std::time::Duration::from_secs(DEFAULT_TIMEOUT_SECONDS);
87        Self::new_client(duration)
88    }
89
90    /// Constructs a new client pointing to the latest Hacker News API version with the configured request timeout in seconds.
91    pub fn new_with_timeout_secs(timeout: u64) -> Self {
92        let duration = std::time::Duration::from_secs(timeout);
93        Self::new_client(duration)
94    }
95
96    /// Constructs a new client pointing to the latest Hacker News API version with the configured request timeout duration.
97    pub fn new_with_timeout_duration(duration: Duration) -> Self {
98        Self::new_client(duration)
99    }
100}