diff --git a/Cargo.toml b/Cargo.toml index 0006173..06eecfc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "tribufu" version = "0.0.4" description = "Tribufu SDK" -repository = "https://github.com/Tribufu/SDK-Rust" +repository = "https://github.com/Tribufu/TribufuRust" authors = ["Tribufu "] license = "Apache-2.0" readme = "README.md" @@ -11,20 +11,23 @@ publish = true exclude = [".github/", ".vscode/", ".editorconfig", ".gitattributes"] +[workspace] +resolver = "2" +members = ["src/*"] + [lib] name = "tribufu" crate-type = ["rlib"] path = "src/lib.rs" +[features] +actix = ["tribufu-actix"] + [dependencies] -alnilam-consts = { version = "0.0.4" } -anyhow = "1.0.75" -chrono = { version = "0.4.22", features = ["serde", "rustc-serialize"] } -derive_more = "0.99.17" -reqwest = { version = "0.11.18", features = ["json", "stream"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = { version = "1.0", features = ["raw_value"] } -serde_with = "3.4.0" +tribufu-api = { path = "./src/api" } +tribufu-constants = { path = "./src/constants" } +tribufu-types = { path = "./src/types" } +tribufu-actix = { path = "./src/actix", optional = true } [dev-dependencies] dotenv = "0.15.0" diff --git a/examples/api.rs b/examples/api.rs new file mode 100644 index 0000000..eb1b2c3 --- /dev/null +++ b/examples/api.rs @@ -0,0 +1,10 @@ +// Copyright (c) Tribufu. All Rights Reserved + +use tribufu::*; + +#[tokio::main] +async fn main() { + let api = TribufuApi::default(); + let games = api.get_games(Some(1)).await.unwrap(); + println!("{:?}", games); +} diff --git a/examples/client.rs b/examples/client.rs deleted file mode 100644 index 152db3b..0000000 --- a/examples/client.rs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Tribufu. All Rights Reserved - -use tribufu::*; - -#[tokio::main] -async fn main() { - match TribufuClient::new(0, "client_secret") { - Ok(client) => println!("client_id: {}", client.id()), - Err(e) => println!("error: {:?}", e), - } -} diff --git a/examples/token.rs b/examples/token.rs index b06bd6d..133240d 100644 --- a/examples/token.rs +++ b/examples/token.rs @@ -1,23 +1,10 @@ // Copyright (c) Tribufu. All Rights Reserved -use dotenv::dotenv; -use std::env; use tribufu::*; #[tokio::main] async fn main() { - dotenv().ok(); - - let client_id = env::var("CLIENT_ID").unwrap().parse::().unwrap(); - let client_secret = env::var("CLIENT_SECRET").unwrap(); - - let mut client = TribufuClient::new(client_id, client_secret).unwrap(); - - client.get_token(None).await.unwrap(); - - let games = client.get_games().await.unwrap(); - - games.iter().for_each(|game| { - println!("{}", game.name); - }); + let api = TribufuApi::with_client_from_env().unwrap_or_default(); + let games = api.get_games(Some(1)).await.unwrap(); + println!("{:?}", games); } diff --git a/src/actix/Cargo.toml b/src/actix/Cargo.toml new file mode 100644 index 0000000..9b7a3db --- /dev/null +++ b/src/actix/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "tribufu-actix" +version = "0.0.4" +description = "Tribufu Actix Extension" +repository = "https://github.com/Tribufu/TribufuRust" +authors = ["Tribufu "] +license = "Apache-2.0" +readme = "README.md" +edition = "2021" +publish = true + +[lib] +name = "tribufu_actix" +crate-type = ["rlib"] +path = "lib.rs" + +[dependencies] +actix-web = { version = "4", features = ["rustls"] } +mintaka-error = { version = "0.0.1" } +tribufu-api = { path = "../api" } diff --git a/src/actix/lib.rs b/src/actix/lib.rs new file mode 100644 index 0000000..a3c5a74 --- /dev/null +++ b/src/actix/lib.rs @@ -0,0 +1,24 @@ +// Copyright (c) Tribufu. All Rights Reserved. + +use actix_web::HttpRequest; +use tribufu_api::TribufuApi; + +pub trait TribufuApiActixExtension { + fn from_actix(req: &HttpRequest) -> Self; +} + +impl TribufuApiActixExtension for TribufuApi { + fn from_actix(req: &HttpRequest) -> Self { + let mut api = Self::with_client_from_env().unwrap_or_default(); + + if let Some(authorization) = req.headers().get("Authorization") { + let authorization = authorization.to_str().unwrap(); + + if authorization.starts_with("Bearer ") { + api = Self::with_user(authorization[7..].to_string()); + } + } + + return api; + } +} diff --git a/src/api/Cargo.toml b/src/api/Cargo.toml new file mode 100644 index 0000000..db97e68 --- /dev/null +++ b/src/api/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "tribufu-api" +version = "0.0.4" +description = "Tribufu API" +repository = "https://github.com/Tribufu/TribufuRust" +authors = ["Tribufu "] +license = "Apache-2.0" +readme = "README.md" +edition = "2021" +publish = true + +[lib] +name = "tribufu_api" +crate-type = ["rlib"] +path = "lib.rs" + +[dependencies] +tribufu-constants = { path = "../constants" } +tribufu-types = { path = "../types" } +base64 = "0.21.5" +mintaka-error = { version = "0.0.1" } +alnilam-consts = { version = "0.0.4" } +anyhow = "1.0.75" +chrono = { version = "0.4.22", features = ["serde", "rustc-serialize"] } +derive_more = "0.99.17" +reqwest = { version = "0.11.18", features = ["json", "stream"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0", features = ["raw_value"] } +serde_with = "3.4.0" diff --git a/src/api/lib.rs b/src/api/lib.rs new file mode 100644 index 0000000..cf6d890 --- /dev/null +++ b/src/api/lib.rs @@ -0,0 +1,394 @@ +// Copyright (c) Tribufu. All Rights Reserved. + +use alnilam_consts::TARGET_TRIPLE; +use base64::engine::general_purpose::STANDARD as BASE64; +use base64::Engine as _; +use mintaka_error::{Error, Result}; +use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION}; +use reqwest::Client; +use std::env; +use tribufu_constants::VERSION; +use tribufu_types::games::Game; +use tribufu_types::oauth2::{OAuth2GrantType, OAuth2TokenRequest, OAuth2TokenResponse}; +use tribufu_types::users::*; + +pub enum Credentials { + Anonymous, + ApiKey { + api_key: String, + }, + Client { + client_id: u64, + client_secret: String, + }, +} + +pub enum Token { + ApiKey { + api_key: String, + }, + Basic { + basic_token: String, + }, + Bearer { + access_token: String, + refresh_token: Option, + }, +} + +pub struct TribufuApi { + base_url: String, + credentials: Credentials, + token: Option, + http: Client, +} + +impl Default for TribufuApi { + fn default() -> Self { + Self::new(Credentials::Anonymous) + } +} + +impl TribufuApi { + const TRIBUFU_API_URL: &'static str = "https://api.tribufu.com"; + + pub fn new(credentials: Credentials) -> Self { + let http = Client::builder() + .user_agent(Self::user_agent()) + .default_headers(Self::default_headers()) + .build() + .unwrap(); + + Self { + base_url: Self::get_base_url(), + credentials, + token: None, + http, + } + } + + pub fn debug_enabled(&self) -> bool { + return cfg!(debug_assertions); + } + + #[inline] + fn user_agent() -> String { + format!( + "Tribufu/{} (+https://api.tribufu.com; {})", + VERSION, TARGET_TRIPLE + ) + } + + #[inline] + fn default_headers() -> HeaderMap { + let mut headers = HeaderMap::new(); + headers.insert("X-Tribufu-Language", HeaderValue::from_static("rust")); + headers.insert("X-Tribufu-Version", HeaderValue::from_static(VERSION)); + headers + } + + fn get_base_url() -> String { + if cfg!(debug_assertions) { + return env::var("TRIBUFU_API_URL") + .unwrap_or_else(|_| Self::TRIBUFU_API_URL.to_string()); + } + + Self::TRIBUFU_API_URL.to_string() + } + + pub fn with_api_key(api_key: String) -> Self { + Self::new(Credentials::ApiKey { api_key }) + } + + pub fn with_client(client_id: u64, client_secret: String) -> Self { + Self::new(Credentials::Client { + client_id, + client_secret, + }) + } + + pub fn with_api_key_from_env() -> Option { + if let Ok(api_key) = env::var("TRIBUFU_API_KEY") { + Some(Self::with_api_key(api_key)) + } else { + None + } + } + + pub fn with_client_from_env() -> Option { + let client_id = env::var("TRIBUFU_CLIENT_ID"); + let client_secret = env::var("TRIBUFU_CLIENT_SECRET"); + + if let (Ok(client_id), Ok(client_secret)) = (client_id, client_secret) { + Some(Self::with_client(client_id.parse().unwrap(), client_secret)) + } else { + None + } + } + + pub fn set_anonymous(&mut self) { + self.credentials = Credentials::Anonymous; + } + + pub fn set_api_key(&mut self, api_key: String) { + self.credentials = Credentials::ApiKey { api_key }; + } + + pub fn set_clients(&mut self, client_id: u64, client_secret: String) { + self.credentials = Credentials::Client { + client_id, + client_secret, + }; + } + + pub fn set_basic_token(&mut self, basic_token: String) { + self.token = Some(Token::Basic { basic_token }); + } + + pub fn set_bearer_token(&mut self, access_token: String, refresh_token: Option) { + self.token = Some(Token::Bearer { + access_token, + refresh_token, + }); + } + + #[inline] + fn headers(&self) -> HeaderMap { + let mut headers = Self::default_headers(); + + match &self.token { + Some(token) => match token { + Token::ApiKey { api_key } => { + headers.insert( + AUTHORIZATION, + HeaderValue::from_str(&format!("ApiKey {}", api_key)).unwrap(), + ); + } + Token::Basic { basic_token } => { + headers.insert( + AUTHORIZATION, + HeaderValue::from_str(&format!("Basic {}", basic_token)).unwrap(), + ); + } + Token::Bearer { access_token, .. } => { + headers.insert( + AUTHORIZATION, + HeaderValue::from_str(&format!("Bearer {}", access_token)).unwrap(), + ); + } + }, + None => {} + } + + headers + } + + pub async fn get_token_with_code( + &mut self, + code: String, + client_id: u64, + client_secret: String, + ) -> Result { + self.get_oauth_token( + OAuth2GrantType::AuthorizationCode, + Some(code), + client_id, + client_secret, + None, + None, + ) + .await + } + + pub async fn get_token_from_password( + &mut self, + username: String, + password: String, + client_id: u64, + client_secret: String, + ) -> Result { + self.get_oauth_token( + OAuth2GrantType::Password, + Some(password), + client_id, + client_secret, + None, + Some(username), + ) + .await + } + + pub async fn get_token_from_passkey( + &mut self, + username: String, + passkey: String, + client_id: u64, + client_secret: String, + ) -> Result { + self.get_oauth_token( + OAuth2GrantType::Passkey, + Some(passkey), + client_id, + client_secret, + None, + Some(username), + ) + .await + } + + pub async fn refresh_token( + &mut self, + refresh_token: String, + client_id: u64, + client_secret: String, + ) -> Result { + self.get_oauth_token( + OAuth2GrantType::RefreshToken, + Some(refresh_token), + client_id, + client_secret, + None, + None, + ) + .await + } + + pub async fn get_client_token( + &mut self, + client_id: u64, + client_secret: String, + ) -> Result { + self.get_oauth_token( + OAuth2GrantType::ClientCredentials, + None, + client_id, + client_secret, + None, + None, + ) + .await + } + + pub async fn get_server_token( + &mut self, + server_id: u64, + client_id: u64, + client_secret: String, + ) -> Result { + self.get_oauth_token( + OAuth2GrantType::ClientCredentials, + None, + client_id, + client_secret, + Some("server_id".to_string()), + Some(server_id.to_string()), + ) + .await + } + + async fn get_oauth_token( + &self, + grant_type: OAuth2GrantType, + grant_value: Option, + client_id: u64, + client_secret: String, + subject_key: Option, + subject_value: Option, + ) -> Result { + let code = if grant_type == OAuth2GrantType::AuthorizationCode { + grant_value.clone() + } else { + None + }; + + let refresh_token = if grant_type == OAuth2GrantType::RefreshToken { + grant_value.clone() + } else { + None + }; + + let mut require_username = false; + + let password = if grant_type == OAuth2GrantType::Password { + require_username = true; + grant_value.clone() + } else { + None + }; + + let passkey = if grant_type == OAuth2GrantType::Passkey { + require_username = true; + grant_value.clone() + } else { + None + }; + + let username = if require_username && subject_value.is_some() { + subject_value.clone() + } else { + None + }; + + let request_body = OAuth2TokenRequest { + grant_type, + code, + refresh_token, + username, + password, + passkey, + client_id: Some(client_id.to_string()), + client_secret: Some(client_secret.clone()), + redirect_uri: None, + }; + + let params = if subject_key.is_some() && subject_value.is_some() { + format!("?{}={}", subject_key.unwrap(), subject_value.unwrap()) + } else { + "".to_string() + }; + + let url = format!("{}/v1/oauth2/token{}", self.base_url, params); + let headers = self.headers(); + let response = self + .http + .post(url) + .headers(headers) + .form(&request_body) + .send() + .await?; + + if response.status() != 200 { + return Err(Error::msg(format!( + "Failed to get token: {}", + response.status() + ))); + } + + Ok(response.json().await?) + } + + pub async fn get_user_info(&self) -> Result { + let url = format!("{}/v1/oauth2/userinfo", self.base_url); + let headers = self.headers(); + let response = self.http.get(url).headers(headers).send().await?; + + Ok(response.json().await?) + } + + pub async fn get_games(&self, page: Option) -> Result> { + let page = page.unwrap_or(1); + let url = format!("{}/v1/packages?page={}", self.base_url, page); + let headers = self.headers(); + let response = self.http.get(url).headers(headers).send().await?; + + Ok(response.json().await?) + } + + pub async fn get_game(&self, id: u64) -> Result { + let url = format!("{}/v1/packages/{}", self.base_url, id); + let headers = self.headers(); + let response = self.http.get(url).headers(headers).send().await?; + + Ok(response.json().await?) + } +} diff --git a/src/client.rs b/src/client.rs deleted file mode 100644 index 9dba9f2..0000000 --- a/src/client.rs +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright (c) Tribufu. All Rights Reserved. - -use crate::games::Game; -use crate::oauth2::*; -use crate::VERSION; -use alnilam_consts::TARGET_TRIPLE; -use anyhow::{Error, Result}; -use reqwest::header; -use reqwest::header::{HeaderMap, HeaderValue}; -use reqwest::Client; - -#[derive(Clone)] -pub struct TribufuClient { - client_id: u64, - client_secret: String, - token: Option, -} - -impl TribufuClient { - //const BASE_URL: &'static str = "https://api.tribufu.com"; - const BASE_URL: &'static str = "http://localhost:5000"; - - pub fn new(id: u64, secret: impl Into) -> Result { - Ok(TribufuClient { - client_id: id, - client_secret: secret.into(), - token: None, - }) - } - - #[inline] - pub fn id(&self) -> u64 { - self.client_id - } - - #[inline] - pub fn user_agent() -> String { - format!( - "Tribufu/{} (+https://api.tribufu.com; {})", - VERSION, TARGET_TRIPLE - ) - } - - #[inline] - fn default_headers() -> HeaderMap { - let mut headers = HeaderMap::new(); - headers.insert("X-Tribufu-Language", HeaderValue::from_static("rust")); - headers.insert("X-Tribufu-Version", HeaderValue::from_static(VERSION)); - headers - } - - fn http_client(&self) -> Result { - let user_agent = Self::user_agent(); - let mut headers = Self::default_headers(); - - if let Some(token) = &self.token { - headers.insert( - header::AUTHORIZATION, - HeaderValue::from_str(&format!("Bearer {}", token.access_token))?, - ); - } - - let http = Client::builder() - .user_agent(user_agent) - .default_headers(headers) - .build()?; - - Ok(http) - } - - pub async fn get_token(&mut self, server_id: Option) -> Result<()> { - let server_id = if let Some(server_id) = server_id { - Some(server_id.to_string()) - } else { - None - }; - - let body = OAuth2TokenRequest { - grant_type: OAuth2GrantType::ClientCredentials, - code: None, - refresh_token: None, - username: None, - password: None, - client_id: Some(self.client_id.to_string()), - client_secret: Some(self.client_secret.clone()), - redirect_uri: None, - server_id, - }; - - let url = format!("{}/v1/oauth2/token", Self::BASE_URL); - let response = self.http_client()?.post(url).form(&body).send().await?; - - if response.status() != 200 { - return Err(Error::msg(format!( - "Failed to get token: {}", - response.status() - ))); - } - - self.token = Some(response.json().await?); - - Ok(()) - } - - pub async fn refresh_token_token(&mut self) -> Result<()> { - let token = if let Some(token) = &self.token { - token - } else { - return Err(Error::msg( - format!("Failed to refresh: self.token == None",), - )); - }; - - if token.refresh_token.is_none() { - return Err(Error::msg(format!( - "Failed to refresh: self.token.refresh_token == None", - ))); - } - - let body = OAuth2TokenRequest { - grant_type: OAuth2GrantType::RefreshToken, - code: None, - refresh_token: token.refresh_token.clone(), - username: None, - password: None, - client_id: Some(self.client_id.to_string()), - client_secret: Some(self.client_secret.clone()), - redirect_uri: None, - server_id: None, - }; - - let url = format!("{}/v1/oauth2/token", Self::BASE_URL); - let response = self.http_client()?.post(url).form(&body).send().await?; - - if response.status() != 200 { - return Err(Error::msg(format!( - "Failed to get token: {}", - response.status() - ))); - } - - self.token = Some(response.json().await?); - - Ok(()) - } - - pub async fn get_games(&self) -> Result> { - let url = format!("{}/v1/packages", Self::BASE_URL); - let response = self.http_client()?.get(url).send().await?; - - Ok(response.json().await?) - } - - pub async fn get_game(&self, id: u64) -> Result { - let url = format!("{}/v1/packages/{}", Self::BASE_URL, id); - let response = self.http_client()?.get(url).send().await?; - - Ok(response.json().await?) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_client() { - let client = TribufuClient::new(0, "client_secret").unwrap(); - assert_eq!(client.id(), 0); - } -} diff --git a/src/constants/Cargo.toml b/src/constants/Cargo.toml new file mode 100644 index 0000000..1eefc86 --- /dev/null +++ b/src/constants/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "tribufu-constants" +version = "0.0.4" +description = "Tribufu Constants" +repository = "https://github.com/Tribufu/TribufuRust" +authors = ["Tribufu "] +license = "Apache-2.0" +readme = "README.md" +edition = "2021" +publish = true + +[lib] +name = "tribufu_constants" +crate-type = ["rlib"] +path = "lib.rs" + +[dependencies] diff --git a/src/constants/lib.rs b/src/constants/lib.rs new file mode 100644 index 0000000..e0fcb7e --- /dev/null +++ b/src/constants/lib.rs @@ -0,0 +1,3 @@ +// Copyright (c) Tribufu. All Rights Reserved. + +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/src/lib.rs b/src/lib.rs index 040dee1..d5ea72e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,5 @@ // Copyright (c) Tribufu. All Rights Reserved. -#![allow(dead_code)] - -pub const VERSION: &str = env!("CARGO_PKG_VERSION"); - -pub mod client; -pub mod games; -pub mod oauth2; -pub mod token; - -pub use client::*; +pub use tribufu_api::*; +pub use tribufu_constants::VERSION; +pub use tribufu_types as types; diff --git a/src/token.rs b/src/token.rs deleted file mode 100644 index 208bb0e..0000000 --- a/src/token.rs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Tribufu. All Rights Reserved. - -use crate::oauth2::OAuth2TokenResponse; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum Credentials { - ApiKey(String), - Token(OAuth2TokenResponse), -} diff --git a/src/types/Cargo.toml b/src/types/Cargo.toml new file mode 100644 index 0000000..1933c92 --- /dev/null +++ b/src/types/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "tribufu-types" +version = "0.0.4" +description = "Tribufu Types" +repository = "https://github.com/Tribufu/TribufuRust" +authors = ["Tribufu "] +license = "Apache-2.0" +readme = "README.md" +edition = "2021" +publish = true + +[lib] +name = "tribufu_types" +crate-type = ["rlib"] +path = "lib.rs" + +[dependencies] +chrono = { version = "0.4.22", features = ["serde", "rustc-serialize"] } +derive_more = "0.99.17" +mintaka-error = { version = "0.0.1" } +serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0", features = ["raw_value"] } +serde_with = "3.4.0" diff --git a/src/games.rs b/src/types/games.rs similarity index 100% rename from src/games.rs rename to src/types/games.rs diff --git a/src/types/lib.rs b/src/types/lib.rs new file mode 100644 index 0000000..b6787df --- /dev/null +++ b/src/types/lib.rs @@ -0,0 +1,5 @@ +// Copyright (c) Tribufu. All Rights Reserved. + +pub mod games; +pub mod oauth2; +pub mod users; diff --git a/src/oauth2.rs b/src/types/oauth2.rs similarity index 88% rename from src/oauth2.rs rename to src/types/oauth2.rs index 07b2067..0c74a83 100644 --- a/src/oauth2.rs +++ b/src/types/oauth2.rs @@ -2,38 +2,39 @@ use serde::{Deserialize, Serialize}; -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum OAuth2ResponseType { Code, Token, } -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum OAuth2ClientType { Confidential, Public, } -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum OAuth2TokenHintType { AccessToken, RefreshToken, } -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum OAuth2GrantType { AuthorizationCode, ClientCredentials, DeviceCode, + Passkey, Password, RefreshToken, } -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum OAuth2AuthorizeError { AccessDenied, @@ -45,7 +46,7 @@ pub enum OAuth2AuthorizeError { UnsupportedResponseType, } -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum OAuth2TokenType { Bearer, @@ -86,34 +87,47 @@ pub struct OAuth2ErrorResponse { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct OAuth2TokenRequest { pub grant_type: OAuth2GrantType, + #[serde(skip_serializing_if = "Option::is_none")] pub code: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub refresh_token: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub username: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub password: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub passkey: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub client_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub client_secret: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub redirect_uri: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub server_id: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct OAuth2TokenResponse { pub token_type: OAuth2TokenType, + pub access_token: String, + #[serde(skip_serializing_if = "Option::is_none")] pub refresh_token: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub scope: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub state: Option, + pub expires_in: u64, } @@ -141,7 +155,7 @@ pub struct OAuth2IntrospectionResponse { } impl OAuth2IntrospectionResponse { - fn inative() -> Self { + pub fn inative() -> Self { Self { active: false, client_id: None, diff --git a/src/types/users.rs b/src/types/users.rs new file mode 100644 index 0000000..599bf66 --- /dev/null +++ b/src/types/users.rs @@ -0,0 +1,41 @@ +// Copyright (c) Tribufu. All Rights Reserved. + +use chrono::{NaiveDate, NaiveDateTime}; +use serde::{Deserialize, Serialize}; +use serde_with::{serde_as, DisplayFromStr}; + +#[repr(u8)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum UserType { + User = 0, + Bot = 1, + Org = 2, +} + +#[serde_as] +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct User { + #[serde_as(as = "DisplayFromStr")] + pub id: u64, + pub uuid: String, + pub name: String, + pub display_name: String, + #[serde(rename = "type")] + pub kind: UserType, + pub public_flags: u64, + pub verified: bool, + pub level: u32, + pub experience: f64, + pub public_birthday: bool, + pub birthday: Option, + pub points: f64, + pub location: Option, + pub photo_url: Option, + pub banner_url: Option, + pub last_online: Option, + pub biography: Option, + pub view_count: u32, + pub created: NaiveDateTime, + pub updated: Option, +}