commit 14f1c545754eb39cc5ee43fefb00d446c8d3e2e5 Author: antifallobst Date: Wed Nov 1 19:57:36 2023 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b2266d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea + +/target +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..00749bb --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "nerdcult-sdk" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.75" +thiserror = "1.0.50" +serde = { version = "1.0.190", features = ["derive"] } +serde_json = "1.0.108" +reqwest = { version = "0.11.22", features = ["json"] } \ No newline at end of file diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..5d4323d --- /dev/null +++ b/src/error.rs @@ -0,0 +1,25 @@ +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum AuthError { + #[error("Failed to authenticate: the API complains about a malformed request")] + BadRequest, + + #[error("Failed to authenticate: unprocessable API response")] + BadResponse, + + #[error("Failed to authenticate: failed to send request")] + RequestSend(reqwest::Error), + + #[error("Failed to authenticate: blocked for security reasons")] + Forbidden, + + #[error("Failed to authenticate: wrong password")] + WrongPassword, + + #[error("Failed to authenticate: account not found")] + NotFound, + + #[error("Failed to authenticate: account not verified")] + NotVerified, +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..f09dee2 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,74 @@ +mod error; +pub mod request; +pub mod response; + +use crate::{error::AuthError, request::AccountAuthenticate}; +use anyhow::Result; +use reqwest::StatusCode; +use std::fmt::Debug; + +#[derive(Debug, Clone)] +pub struct Session { + base_url: String, + auth_token: Option, + client: reqwest::Client, +} + +impl Default for Session { + fn default() -> Self { + Self { + base_url: "https://api.nerdcult.net".to_string(), + auth_token: None, + client: reqwest::Client::new(), + } + } +} + +impl Session { + pub fn with_base_url(base_url: String) -> Self { + Self { + base_url, + auth_token: None, + client: reqwest::Client::new(), + } + } + + pub async fn authenticate( + &mut self, + username: String, + password: String, + ) -> Result<(), AuthError> { + let url = format!("{}/account/authenticate", &self.base_url); + + let body = AccountAuthenticate { username, password }; + + let request = self.client.post(&url).json(&body); + let response = request + .send() + .await + .map_err(|e| AuthError::RequestSend(e))?; + + match response.status() { + StatusCode::OK => { + self.auth_token = Some( + response + .json::() + .await + .map_err(|_| AuthError::BadResponse)? + .token, + ); + Ok(()) + } + StatusCode::BAD_REQUEST => Err(AuthError::BadRequest), + StatusCode::UNAUTHORIZED => Err(AuthError::WrongPassword), + StatusCode::FORBIDDEN => Err(AuthError::Forbidden), + StatusCode::NOT_FOUND => Err(AuthError::NotFound), + StatusCode::FAILED_DEPENDENCY => Err(AuthError::NotVerified), + _ => Err(AuthError::BadResponse), + } + } + + pub fn is_authenticated(&self) -> bool { + self.auth_token.is_some() + } +} diff --git a/src/request.rs b/src/request.rs new file mode 100644 index 0000000..9f361bf --- /dev/null +++ b/src/request.rs @@ -0,0 +1,7 @@ +use serde::Serialize; + +#[derive(Debug, Serialize, Clone)] +pub struct AccountAuthenticate { + pub username: String, + pub password: String, +} \ No newline at end of file diff --git a/src/response.rs b/src/response.rs new file mode 100644 index 0000000..83c6572 --- /dev/null +++ b/src/response.rs @@ -0,0 +1,6 @@ +use serde::Deserialize; + +#[derive(Debug, Deserialize, Clone)] +pub struct AccountAuthenticateResponse { + pub token: String, +}