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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
//! A simple, easy to use Rust wrapper for the Minehut API.
//! 
//! All functions that return/modify data in the crate use async/await.
//! 
//! This module provides functions for:
//! 
//! * Getting Minehut stats in an organized manner from the Minehut API.
//! * Client authentication allowing you to access and or modify more data.
//! 
//! # Example
//! Creating a client instance:
//! 
//! ```no_run
//! // Functions can be seen at the Client struct
//! let client = minehut::Client::new("auth-token", "session-id");
//! 
//! // Do something with it
//! ```

pub mod models;
mod http;
mod handlers;

use std::fmt;

use crate::models::{PlayerDistribution, HomePageStats, SimpleStats, OwnedServer, OwnedServerRes};
pub use crate::handlers::{servers, products};

/// All error types for the crate. Used by the crate to propogate errors. 
/// Users can use it for error handling.
#[derive(Debug)]
pub enum Error {
    /// This is called if Reqwest produces an HTTP error.
    Http(reqwest::Error), 
    /// This is called if Serde failed to parse the response. Usually this
    /// means the data received is not what the crate expected. 
    Json(reqwest::Error),
    /// This means the operation failed.
    FailedOp(String)
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Error::Http(e) => write!(f, "{}", e)?,
            Error::Json(e) => write!(f, "{}", e)?,
            Error::FailedOp(s) => write!(f, "{}", s)?
        }
        Ok(())
    }
}

/// This is the Minehut client struct, it only stores an authentication
/// token and session ID. All data that require authentication can only
/// be ran using this struct.
pub struct Client {
    /// The authentication token.
    auth_token: String,
    /// The session ID, this changes periodically.
    session_id: String
}

impl Client {
    /// Creates a new instance of Client with provided auth and session 
    /// ID. You can get both of these using the inspect element on the
    /// Minehut dashboard.
    /// 
    /// # Arguments
    /// 
    /// * `auth` - Authentication token from Minehut.
    /// * `session_id` - Session ID from Minehut.
    /// 
    /// # Example
    /// 
    /// ```no_run
    /// use minehut::Client;
    /// 
    /// let client = Client::new("auth-token", "session-id");
    /// // Now do something with
    /// ```
    pub fn new(auth_token: String, session_id: String) -> Self {
        Client { 
            auth_token,
            session_id 
        }
    }

    /// Retrieves a server that the client user owns by name using the Minehut
    /// API.
    /// 
    /// # Arguments
    /// 
    /// * `name` - Name of the server.
    /// 
    /// # Example
    /// 
    /// ```no_run
    /// let client = minehut::Client::new("","");
    /// 
    /// match client.my_server("weird-name").await {
    ///     Err(_) => println!("Cannot fetch server"),
    ///     Ok(s) => println!("{s:?}")
    /// }
    /// ```
    /// 
    /// # Error
    /// 
    /// Returns an error if a server could not be found or if found server is not owned
    /// by the client user.
    pub async fn my_server(&self, name: &str) -> Result<OwnedServer, Error> {
        let id = handlers::servers::server_from_name(name).await?.id;

        let server_res = http::req_client_data::<OwnedServerRes>(
            &self,
            &format!("server/{id}/server_data")
        ).await?;

        Ok(server_res.server)
    }
}

/// Gets a `SimpleStats` struct from Minehut asynchronously. Gets simple Minehut
/// statistics.
/// 
/// # Examples
/// 
/// ```
/// async fn print_stats() {
///     // It is safe to assume this will not return an error.
///     // As such we will unwrap() the result.
///     let stats = minehut::simple_stats().await.unwrap();
/// 
///     println!("{} players are on", stats.player_count);
///     println!("{} hosted servers", stats.server_count);
/// }
/// ```
/// 
/// # Error
/// 
/// Returns a HTTP error if it cannot load page, usually a network error.
pub async fn simple_stats() -> Result<SimpleStats, Error> {
    http::req_data::<SimpleStats>("network/simple_stats").await
}

/// Gets a `PlayerDistribution` struct from Minehut asynchronously. Gets the player
/// distribution for Java and Bedrock on Minehut.
/// 
/// # Examples
/// 
/// ```
/// #[tokio::main]
/// async fn main() {
///     // It is safe to assume this will not return an error.
///     // As such, we will unwrap() the result.
///     let dist = minehut::player_distribution().await.unwrap();
/// 
///     println!("{} java players", dist.java_total);
///     println!("{} bedrock servers", dist.bedrock_player_server);
/// }
/// ```
/// 
/// # Error
/// 
/// Returns a HTTP error if it cannot load page, this is usally a network error.
pub async fn player_distribution() -> Result<PlayerDistribution, Error> {
    http::req_data::<PlayerDistribution>("network/players/distribution").await
}

/// Gets a `HomePageStats` from Minehut asynchronously. Returns the home page
/// statistics.
/// 
/// # Examples
/// 
/// ```
/// #[tokio::main]
/// async fn main() {
///     // It is safe to assume this will not return an error.
///     // As such, we will unwrap() the result.
///     let home = minehut::home_page_stats().await.unwrap();
/// 
///     println!("{} users, {} server", home.user_count, home.server_count);
/// }
/// ```
/// 
/// # Error
///  
/// Returns a HTTP error if it cannot load page, this is usally a network error.
pub async fn homepage_stats() -> Result<HomePageStats, Error> {
    http::req_data::<HomePageStats>("network/homepage_stats").await
}