Rewrite to a API trait.

This commit is contained in:
2026-01-11 14:12:54 +01:00
parent ea9f05b048
commit 88f8cf76ef
45 changed files with 1384 additions and 1030 deletions

View File

@@ -108,6 +108,15 @@ dependencies = [
"cc",
]
[[package]]
name = "colored"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "core-foundation"
version = "0.9.4"
@@ -221,21 +230,6 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "futures"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.31"
@@ -243,7 +237,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
@@ -252,17 +245,6 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-executor"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.31"
@@ -298,7 +280,6 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
@@ -332,6 +313,7 @@ dependencies = [
name = "gamenight-api-client-rs"
version = "0.1.0"
dependencies = [
"async-trait",
"reqwest",
"serde",
"serde_json",
@@ -346,8 +328,8 @@ dependencies = [
"async-trait",
"chrono",
"clear_screen",
"colored",
"dyn-clone",
"futures",
"gamenight-api-client-rs",
"inquire",
"jsonwebtoken",
@@ -996,12 +978,14 @@ dependencies = [
"sync_wrapper",
"tokio",
"tokio-native-tls",
"tokio-util",
"tower",
"tower-http",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-streams",
"web-sys",
]
@@ -1236,9 +1220,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "syn"
version = "2.0.113"
version = "2.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678faa00651c9eb72dd2020cbdf275d92eccb2400d568e419efdd64838145cb4"
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
dependencies = [
"proc-macro2",
"quote",
@@ -1386,6 +1370,19 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
]
[[package]]
name = "tower"
version = "0.5.2"
@@ -1604,6 +1601,19 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-streams"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
dependencies = [
"futures-util",
"js-sys",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "web-sys"
version = "0.3.83"

View File

@@ -15,4 +15,4 @@ jsonwebtoken = "9.3"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
clear_screen = "0.1"
futures = "0.3"
colored = "3.0"

View File

@@ -0,0 +1,20 @@
use std::fmt::Display;
use colored::Colorize;
use crate::domain::location::Location;
use crate::domain::owned_games::OwnedGames;
pub struct EligableGames<'a>(pub &'a OwnedGames, pub &'a Location);
impl Display for EligableGames<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (k, v) in self.0.0.iter() {
writeln!(f, "{}:", k)?;
for (g, l) in v.iter() {
if l.as_ref().is_none_or(|x| x.id == self.1.id) {
writeln!(f, "\t{}", g.name.green())?;
}
}
}
Ok(())
}
}

View File

@@ -1,6 +1,8 @@
use std::fmt::{Display, Formatter};
use chrono::{DateTime, Local};
use uuid::Uuid;
use crate::domain::eligable_games::EligableGames;
use crate::domain::location::Location;
use crate::domain::owned_games::OwnedGames;
use crate::domain::participants::Participants;
use crate::domain::user::User;
@@ -9,6 +11,7 @@ use crate::domain::user::User;
pub struct Gamenight {
pub id: Uuid,
pub name: String,
pub location: Option<Location>,
pub start_time: DateTime<Local>,
pub organizer: User,
pub participants: Participants,
@@ -21,7 +24,12 @@ impl Display for Gamenight {
writeln!(f, "Organizer: {}", self.organizer)?;
writeln!(f, "Start: {}", self.start_time.to_rfc3339())?;
writeln!(f, "Participants: {}", self.participants)?;
write!(f, "Owned games:\n{}", self.owned_games)?;
if let Some(location) = &self.location {
write!(f, "Eligable games:\n{}", EligableGames(&self.owned_games, location))?;
} else {
write!(f, "Owned games: \n{}", self.owned_games)?;
}
Ok(())
}
}

View File

@@ -7,6 +7,7 @@ use uuid::Uuid;
pub struct GamenightSelectData {
pub id: Uuid,
pub name: String,
pub location_id: Option<Uuid>,
pub start_time: DateTime<Local>,
pub owner_id: Uuid,
}

View File

@@ -2,6 +2,7 @@ use std::fmt::Display;
use gamenight_api_client_rs::models;
use uuid::Uuid;
use crate::flows::FlowError;
#[derive(Clone)]
pub struct Location {
@@ -11,14 +12,15 @@ pub struct Location {
pub note: Option<String>,
}
impl From<models::Location> for Location {
fn from(location: models::Location) -> Self {
Self {
id: Uuid::parse_str(&location.id).unwrap(),
name: location.name,
address: location.address,
note: location.note
}
impl TryFrom<models::Location> for Location {
type Error = FlowError;
fn try_from(value: models::Location) -> Result<Self, Self::Error> {
Ok(Self {
id: Uuid::parse_str(&value.id)?,
name: value.name,
address: value.address,
note: value.note
})
}
}

View File

@@ -6,4 +6,5 @@ pub mod game;
pub mod owned_games;
pub mod location;
pub mod location_select_data;
pub mod gamenight;
pub mod gamenight;
mod eligable_games;

View File

@@ -1,15 +1,16 @@
use std::{collections::HashMap, fmt::Display};
use crate::domain::game::Game;
use crate::domain::location::Location;
#[derive(Clone)]
pub struct OwnedGames(pub HashMap<String, Vec<Game>>);
pub struct OwnedGames (pub HashMap<String, Vec<(Game, Option<Location>)>>);
impl Display for OwnedGames {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (k,v) in &self.0 {
write!(f, "{k}:\n")?;
for g in v {
for (g, _) in v {
write!(f, "\t{}\n", g.name)?;
}
}

View File

@@ -3,7 +3,7 @@ use std::fmt::Display;
use super::*;
use crate::flows::own::Own;
use async_trait::async_trait;
use gamenight_api_client_rs::{apis::default_api::game_post, models::AddGameRequestBody};
use gamenight_api_client_rs::models::AddGameRequestBody;
use inquire::Text;
@@ -24,7 +24,7 @@ impl<'a> Flow<'a> for AddGame {
let add_game_request = AddGameRequestBody {
name
};
let game_id_response = game_post(&state.api_configuration, Some(add_game_request)).await?;
let game_id_response = state.api.game_post(Some(add_game_request)).await?;
let own_flow = Own::new(Uuid::parse_str(&game_id_response.game_id)?);
return self.continue_with(state, &own_flow).await;

View File

@@ -1,8 +1,7 @@
use gamenight_api_client_rs::{apis::default_api::post_gamenight, models};
use inquire::{CustomType, DateSelect, Text};
use chrono::{self, Local, NaiveTime};
use gamenight_api_client_rs::models;
use super::*;
#[derive(Clone)]
@@ -32,7 +31,7 @@ impl<'a> Flow<'a> for AddGamenight {
.to_utc()
.to_rfc3339();
let add_gamenight = models::AddGamenightRequestBody::new(name, datetime);
post_gamenight(&state.api_configuration, Some(add_gamenight)).await?;
state.api.post_gamenight(Some(add_gamenight)).await?;
clear_screen::clear();
return Ok((FlowOutcome::Successful, state))

View File

@@ -1,7 +1,7 @@
use std::{ffi::OsStr, fmt::Display};
use async_trait::async_trait;
use gamenight_api_client_rs::{apis::default_api::{location_authorize_post, location_post}, models::{authorize_location_request_body::Op::Grant, AddLocationRequestBody, AuthorizeLocationRequestBody}};
use gamenight_api_client_rs::models::{authorize_location_request_body::Op::Grant, AddLocationRequestBody, AuthorizeLocationRequestBody};
use inquire::{Editor, Text};
@@ -44,7 +44,7 @@ impl<'a> Flow<'a> for AddLocation {
note
};
let location_id = location_post(&state.api_configuration, Some(add_location_request)).await?;
let location_id = state.api.location_post(Some(add_location_request)).await?;
let add_authorize_request = AuthorizeLocationRequestBody {
location_id: location_id.location_id.to_string(),
@@ -52,7 +52,7 @@ impl<'a> Flow<'a> for AddLocation {
op: Grant
};
location_authorize_post(&state.api_configuration, Some(add_authorize_request)).await?;
state.api.location_authorize_post(Some(add_authorize_request)).await?;
}
Ok((FlowOutcome::Cancelled, state))
}

View File

@@ -1,12 +1,9 @@
use std::fmt::Display;
use async_trait::async_trait;
use gamenight_api_client_rs::apis::configuration::Configuration;
use inquire::Text;
use crate::{domain::config::{Config, Instance}, flows::{gamenight_menu::GamenightMenu, login::Login, FlowError}};
use super::{Flow, FlowOutcome, FlowResult, GamenightState};
use crate::{domain::config::{Config, Instance}, flows::{gamenight_menu::GamenightMenu, login::Login, FlowError}};
use async_trait::async_trait;
use inquire::Text;
#[derive(Clone)]
pub struct Connect {
@@ -50,37 +47,37 @@ impl Connect {
}
pub async fn try_refresh_token_if_exists(&self, instance: &mut Instance, api_configuration: &mut Configuration, config: &mut Config, instance_name: &String) -> Result<bool, FlowError> {
pub async fn try_refresh_token_if_exists<'a>(&self, instance: &mut Instance, state: &'a mut GamenightState, instance_name: &String) -> Result<(bool, &'a mut GamenightState), FlowError> {
if let Some(token) = &instance.token {
api_configuration.bearer_access_token = Some(token.clone());
let result = gamenight_api_client_rs::apis::default_api::post_token(api_configuration).await;
let state = state.set_bearer_access_token(Some(token.clone()));
let result = state.api.post_token().await;
if let Ok(token) = result {
let instance = config.instances.iter_mut().find(|x| x.name == *instance_name).unwrap();
let instance = state.gamenight_configuration.instances.iter_mut().find(|x| x.name == *instance_name).unwrap();
instance.token = token.jwt_token.clone();
api_configuration.bearer_access_token = token.jwt_token.clone();
Config::save(config)?;
Ok(true)
let state = state.set_bearer_access_token(token.jwt_token.clone());
Config::save(&state.gamenight_configuration)?;
Ok((true, state))
}
else {
Ok(false)
Ok((false, state))
}
} else {
Ok(false)
Ok((false, state))
}
}
pub fn update_state_on_logon(&self, instance: &mut Instance, api_configuration: &mut Configuration, config: &mut Config, instance_name: &String) -> Result<(), FlowError> {
pub fn update_state_on_logon(&self, instance: &mut Instance, state: &mut GamenightState, instance_name: &String) -> Result<(), FlowError> {
if self.instance.is_none() {
instance.token = Some(api_configuration.bearer_access_token.clone().unwrap());
config.instances.push(instance.clone());
instance.token = Some(state.api_configuration.bearer_access_token.clone().unwrap());
state.gamenight_configuration.instances.push(instance.clone());
}
else {
let instance = config.instances.iter_mut().find(|x| x.name == *instance_name).unwrap();
instance.token = Some(api_configuration.bearer_access_token.clone().unwrap());
let instance = state.gamenight_configuration.instances.iter_mut().find(|x| x.name == *instance_name).unwrap();
instance.token = Some(state.api_configuration.bearer_access_token.clone().unwrap());
}
config.last_instance = Some(instance_name.clone());
state.gamenight_configuration.last_instance = Some(instance_name.clone());
Config::save(config)?;
Config::save(&state.gamenight_configuration)?;
Ok(())
}
}
@@ -98,9 +95,10 @@ impl<'a> Flow<'a> for Connect {
};
let instance_name = instance.name.clone();
state.api_configuration.base_path = instance.url.clone();
let state = state.set_api_base_path(instance.url.clone());
if self.try_refresh_token_if_exists(&mut instance, &mut state.api_configuration, &mut state.gamenight_configuration, &instance_name).await? {
let (token_refreshed, state) = self.try_refresh_token_if_exists(&mut instance, state, &instance_name).await?;
if token_refreshed {
let gamenight_menu_flow = GamenightMenu::new();
gamenight_menu_flow.run(state).await
}
@@ -109,7 +107,7 @@ impl<'a> Flow<'a> for Connect {
let (outcome, state) = login_flow.run(state).await?;
if outcome == FlowOutcome::Successful {
self.update_state_on_logon(&mut instance, &mut state.api_configuration, &mut state.gamenight_configuration, &instance_name)?;
self.update_state_on_logon(&mut instance, state, &instance_name)?;
let gamenight_menu_flow = GamenightMenu::new();
gamenight_menu_flow.run(state).await

View File

@@ -1,7 +1,7 @@
use std::fmt::Display;
use async_trait::async_trait;
use gamenight_api_client_rs::{apis::default_api::disown_post, models::GameId};
use gamenight_api_client_rs::models::GameId;
use uuid::Uuid;
use super::{Flow, FlowOutcome, FlowResult, GamenightState};
@@ -22,7 +22,7 @@ impl Disown {
#[async_trait]
impl<'a> Flow<'a> for Disown {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
let _ = disown_post(&state.api_configuration, Some(GameId{game_id: self.game_id.to_string()})).await?;
let _ = state.api.disown_post(Some(GameId{game_id: self.game_id.to_string()})).await?;
clear_screen::clear();
Ok((FlowOutcome::Successful, state))

View File

@@ -1,7 +1,7 @@
use std::fmt::Display;
use async_trait::async_trait;
use gamenight_api_client_rs::{apis::default_api::join_post, models::GamenightId};
use gamenight_api_client_rs::models::GamenightId;
use uuid::Uuid;
use super::{Flow, FlowOutcome, FlowResult, GamenightState};
@@ -22,7 +22,7 @@ impl Join {
#[async_trait]
impl<'a> Flow<'a> for Join {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
let _ = join_post(&state.api_configuration, Some(GamenightId{gamenight_id: self.gamenight_id.to_string()})).await?;
let _ = state.api.join_post(Some(GamenightId{gamenight_id: self.gamenight_id.to_string()})).await?;
clear_screen::clear();
Ok((FlowOutcome::Successful, state))

View File

@@ -1,7 +1,7 @@
use std::fmt::Display;
use async_trait::async_trait;
use gamenight_api_client_rs::{apis::default_api::leave_post, models::GamenightId};
use gamenight_api_client_rs::models::GamenightId;
use uuid::Uuid;
use super::{Flow, FlowOutcome, FlowResult, GamenightState};
@@ -22,7 +22,7 @@ impl Leave {
#[async_trait]
impl<'a> Flow<'a> for Leave {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
let _ = leave_post(&state.api_configuration, Some(GamenightId{gamenight_id: self.gamenight_id.to_string()})).await?;
let _ = state.api.leave_post(Some(GamenightId{gamenight_id: self.gamenight_id.to_string()})).await?;
clear_screen::clear();
Ok((FlowOutcome::Successful, state))

View File

@@ -1,6 +1,5 @@
use chrono::DateTime;
use gamenight_api_client_rs::apis::default_api::get_gamenights;
use inquire::Select;
use uuid::Uuid;
@@ -22,7 +21,7 @@ impl ListGamenights {
#[async_trait]
impl<'a> Flow<'a> for ListGamenights {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
let response = get_gamenights(&state.api_configuration).await?;
let response = state.api.get_gamenights().await?;
let mut view_flows: Vec<Box<dyn Flow<'_> + Send>> = vec![];
@@ -30,6 +29,10 @@ impl<'a> Flow<'a> for ListGamenights {
let gamenight = GamenightSelectData {
id: Uuid::parse_str(&response.id)?,
name: response.name.clone(),
location_id: match &response.location_id {
None => None,
Some(x) => Some(Uuid::parse_str(x)?)
},
start_time:DateTime::parse_from_rfc3339(&response.datetime)?.into(),
owner_id: Uuid::parse_str(&response.owner_id)?
};

View File

@@ -1,7 +1,6 @@
use std::fmt::Display;
use async_trait::async_trait;
use gamenight_api_client_rs::apis::default_api::games_get;
use inquire::{ui::RenderConfig, Select};
use crate::flows::{view_game::ViewGame, exit::Exit};
@@ -22,7 +21,7 @@ impl ListGames {
#[async_trait]
impl<'a> Flow<'a> for ListGames {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
let games = games_get(&state.api_configuration).await?;
let games = state.api.games_get().await?;
let mut flows = games.into_iter().map(|game| -> Box<dyn Flow + Send> {
Box::new(ViewGame::new(game.into()))

View File

@@ -1,7 +1,6 @@
use std::fmt::Display;
use async_trait::async_trait;
use gamenight_api_client_rs::apis::default_api::locations_get;
use inquire::{ui::RenderConfig, Select};
use crate::flows::{exit::Exit, view_location::ViewLocation};
@@ -22,10 +21,10 @@ impl ListLocations {
#[async_trait]
impl<'a> Flow<'a> for ListLocations {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
let locations = locations_get(&state.api_configuration).await?;
let locations = state.api.locations_get().await?;
let mut flows = locations.into_iter().map(|location| -> Box<dyn Flow + Send> {
Box::new(ViewLocation::new(location.into()))
Box::new(ViewLocation::new(location.try_into().unwrap()))
}).collect::<Vec::<Box::<dyn Flow + Send>>>();
flows.push(Box::new(Exit::new()));

View File

@@ -1,7 +1,7 @@
use std::fmt::Display;
use async_trait::async_trait;
use gamenight_api_client_rs::{apis::default_api::{self, authorized_location_user_ids_get, users_get}, models::{AuthorizeLocationRequestBody, LocationId, User}};
use gamenight_api_client_rs::models::{AuthorizeLocationRequestBody, LocationId, User};
use inquire::MultiSelect;
use uuid::Uuid;
@@ -47,11 +47,11 @@ impl<'a> TryFrom<&'a User> for AuthorizeMultiSelectStruct<'a> {
#[async_trait]
impl<'a> Flow<'a> for LocationAuthorize {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
let users = users_get(&state.api_configuration).await?;
let users = state.api.users_get().await?;
let location_id = LocationId {
location_id: self.location_id.to_string()
};
let authorized_user_ids = authorized_location_user_ids_get(&state.api_configuration, Some(location_id)).await?;
let authorized_user_ids = state.api.authorized_location_user_ids_get(Some(location_id)).await?;
let authorized_user_ids : Vec<String> = authorized_user_ids.into_iter().map(|x| x.user_id).collect();
let options: Vec<AuthorizeMultiSelectStruct> = users.iter().map(|x| {x.try_into()}).collect::<Result<Vec<AuthorizeMultiSelectStruct>, FlowError>>()?;
@@ -67,7 +67,7 @@ impl<'a> Flow<'a> for LocationAuthorize {
if let Some(selections) = &selections {
for selection in selections {
if authorized_users.iter().find(|x| {x.id == selection.id.to_string()}).is_none() {
default_api::location_authorize_post(&state.api_configuration, Some(
state.api.location_authorize_post(Some(
AuthorizeLocationRequestBody {
location_id: self.location_id.to_string(),
user_id: selection.id.to_string(),
@@ -78,7 +78,7 @@ impl<'a> Flow<'a> for LocationAuthorize {
}
for authorized_user in authorized_users {
if selections.iter().find(|x| {x.id.to_string() == authorized_user.id}).is_none() {
default_api::location_authorize_post(&state.api_configuration, Some(
state.api.location_authorize_post(Some(
AuthorizeLocationRequestBody {
location_id: self.location_id.to_string(),
user_id: authorized_user.id.to_string(),

View File

@@ -1,5 +1,5 @@
use async_trait::async_trait;
use gamenight_api_client_rs::{apis::{configuration::Configuration, default_api::get_token}, models};
use gamenight_api_client_rs::models;
use inquire::{Password, Text};
use super::*;
@@ -18,7 +18,6 @@ impl Login {
#[async_trait]
impl<'a> Flow<'a> for Login {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
let configuration = Configuration::new();
let username = Text::new("What is your login?").prompt()?;
let password = Password::new("what is your password?")
@@ -27,11 +26,11 @@ impl<'a> Flow<'a> for Login {
let login = models::Login::new(username, password);
let result = get_token(&configuration, Some(login)).await?;
let result = state.api.get_token(Some(login)).await?;
clear_screen::clear();
if let Some(token) = result.jwt_token {
state.api_configuration.bearer_access_token = Some(token);
state.set_bearer_access_token(Some(token));
Ok((FlowOutcome::Successful, state))
} else {
Err(FlowError{error: "Unexpected response".to_string()})

View File

@@ -1,5 +1,6 @@
use gamenight_api_client_rs::apis::default_api::{DefaultApi, DefaultApiClient};
use std::{fmt::Display, num::ParseIntError};
use std::sync::{Arc, MutexGuard, PoisonError};
use async_trait::async_trait;
use chrono::ParseError;
use gamenight_api_client_rs::apis::{configuration::Configuration, Error};
@@ -38,10 +39,12 @@ mod location_authorize;
mod remove_game;
pub struct GamenightState {
api_configuration: Configuration,
api_configuration: Arc<Configuration>,
api: Box<dyn DefaultApi>,
gamenight_configuration: Config,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
exp: i64,
@@ -50,8 +53,10 @@ pub struct Claims {
impl GamenightState {
pub fn new() -> Self{
let config = Arc::new(Configuration::new());
Self {
api_configuration: Configuration::new(),
api_configuration: config.clone(),
api: Box::new(DefaultApiClient::new(config.clone())),
gamenight_configuration: Config::new()
}
}
@@ -67,6 +72,22 @@ impl GamenightState {
Ok(claims.uid)
}
pub fn set_bearer_access_token(&mut self, token: Option<String>) -> &mut Self {
let mut config : Configuration = Arc::<Configuration>::into_inner(self.api_configuration.clone()).unwrap_or(Configuration::new());
config.bearer_access_token = token;
self.api_configuration = Arc::from(config);
self.api = Box::new(DefaultApiClient::new(self.api_configuration.clone()));
self
}
pub fn set_api_base_path(&mut self, base_path: String) -> &mut Self {
let mut config : Configuration = Arc::<Configuration>::into_inner(self.api_configuration.clone()).unwrap_or(Configuration::new());
config.base_path = base_path;
self.api_configuration = Arc::from(config);
self.api = Box::new(DefaultApiClient::new(self.api_configuration.clone()));
self
}
}
impl Default for GamenightState {
@@ -136,6 +157,30 @@ impl From<jsonwebtoken::errors::Error> for FlowError {
}
}
impl From<PoisonError<&mut Configuration>> for FlowError {
fn from(value: PoisonError<&mut Configuration>) -> Self {
Self {
error: value.to_string()
}
}
}
impl From<PoisonError<Configuration>> for FlowError {
fn from(value: PoisonError<Configuration>) -> Self {
Self {
error: value.to_string()
}
}
}
impl From<PoisonError<std::sync::MutexGuard<'_, Configuration>>> for FlowError {
fn from(value: PoisonError<MutexGuard<'_, Configuration>>) -> Self {
Self {
error: value.to_string()
}
}
}
#[derive(PartialEq)]
pub enum FlowOutcome {
Successful,

View File

@@ -3,9 +3,7 @@ use std::fmt::Display;
use super::{Flow, FlowError, FlowOutcome, FlowResult, GamenightState};
use crate::domain::location_select_data::LocationSelectData;
use async_trait::async_trait;
use gamenight_api_client_rs::apis::default_api::locations_get;
use gamenight_api_client_rs::models::OwnGameRequestBody;
use gamenight_api_client_rs::apis::default_api::own_post;
use inquire::{Confirm, Select};
use uuid::Uuid;
@@ -35,13 +33,13 @@ impl<'a> Flow<'a> for Own {
if owned {
if let Some(willing_to_travel) = Confirm::new("Are you willing to travel with this game?").prompt_skippable()? {
if !willing_to_travel {
let locations = locations_get(&state.api_configuration).await?.iter().map(|x| { x.try_into() }).collect::<Result<Vec<LocationSelectData>, FlowError>>()?;
let locations = state.api.locations_get().await?.iter().map(|x| { x.try_into() }).collect::<Result<Vec<LocationSelectData>, FlowError>>()?;
if let Some(location) = Select::new("What location can this game be played?", locations).prompt_skippable()? {
own_game_request.location_id = Some(location.id.to_string());
}
}
}
let _ = own_post(&state.api_configuration, Some(own_game_request)).await?;
let _ = state.api.own_post(Some(own_game_request)).await?;
}
}

View File

@@ -25,7 +25,7 @@ impl<'a> Flow<'a> for RemoveGame {
let req = GameId {
game_id: self.game.id.to_string()
};
gamenight_api_client_rs::apis::default_api::game_delete(&state.api_configuration, Some(req)).await?;
state.api.game_delete(Some(req)).await?;
//Hack to return to right stack item, skipping detail view that doesn't exist.
Ok((FlowOutcome::Abort, state))
}

View File

@@ -1,7 +1,7 @@
use std::fmt::Display;
use async_trait::async_trait;
use gamenight_api_client_rs::{apis::default_api::rename_game_post, models::RenameGameRequestBody};
use gamenight_api_client_rs::models::RenameGameRequestBody;
use inquire::Text;
use crate::domain::game::Game;
@@ -32,7 +32,7 @@ impl<'a> Flow<'a> for RenameGame {
id: self.game.id.to_string(),
name
};
rename_game_post(&state.api_configuration, Some(req)).await?;
state.api.rename_game_post(Some(req)).await?;
return Ok((FlowOutcome::Successful, state))
}

View File

@@ -1,5 +1,5 @@
use gamenight_api_client_rs::{apis::default_api::{game_get, owned_games_get}, models::{GameId, UserId}};
use gamenight_api_client_rs::models::{GameId, UserId};
use inquire::Select;
use uuid::Uuid;
@@ -24,15 +24,15 @@ impl ViewGame {
impl<'a> Flow<'a> for ViewGame {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
let game_id = GameId{ game_id: self.game.id.to_string() };
let game: Game = game_get(&state.api_configuration, Some(game_id)).await?.into();
let game: Game = state.api.game_get(Some(game_id)).await?.into();
println!("{}", game);
let my_uid = state.get_user_id()?;
let request = UserId{ user_id: my_uid.to_string() };
let owned_games: Vec<Uuid> = owned_games_get(&state.api_configuration, Some(request)).await?
let owned_games: Vec<Uuid> = state.api.owned_games_get(Some(request)).await?
.iter().map(|x| -> Result<Uuid, FlowError> {
Ok(Uuid::parse_str(x)?)
Ok(Uuid::parse_str(&x.game_id)?)
}).collect::<Result<Vec<Uuid>, FlowError>>()?;
let own_or_disown: Box<dyn Flow<'a> + Send> =

View File

@@ -1,13 +1,11 @@
use crate::domain::gamenight::Gamenight;
use std::collections::HashMap;
use futures::future::join_all;
use gamenight_api_client_rs::{apis::default_api::{game_get, owned_games_get, participants_get, user_get, GameGetError}, models::{self, GameId, GamenightId, UserId}};
use gamenight_api_client_rs::models::{GameId, GamenightId, LocationId, OwnedGame, UserId};
use inquire::Select;
use crate::{domain::{game::Game, gamenight_select_data::GamenightSelectData, owned_games::OwnedGames, participants::Participants}, flows::{exit::Exit, join::Join, leave::Leave}};
use super::*;
use crate::{domain::{gamenight_select_data::GamenightSelectData, owned_games::OwnedGames, participants::Participants}, flows::{exit::Exit, join::Join, leave::Leave}};
#[derive(Clone)]
pub struct ViewGamenight {
@@ -30,25 +28,35 @@ impl<'a> Flow<'a> for ViewGamenight {
id: self.gamenight_select_data.id,
start_time: self.gamenight_select_data.start_time,
name: self.gamenight_select_data.name.clone(),
organizer: user_get(&state.api_configuration, Some(UserId{user_id: self.gamenight_select_data.owner_id.to_string()})).await?.try_into()?,
location: None,
organizer: state.api.user_get(Some(UserId{user_id: self.gamenight_select_data.owner_id.to_string()})).await?.try_into()?,
participants: Participants(vec![]),
owned_games: OwnedGames(HashMap::new())
};
let participants = participants_get(&state.api_configuration, Some(GamenightId{gamenight_id: gamenight.id.to_string()})).await?;
gamenight.location = match self.gamenight_select_data.location_id {
None => None,
Some(l) => Some(state.api.location_get(Some(LocationId{location_id: l.to_string()})).await?.try_into()?)
};
let participants = state.api.participants_get(Some(GamenightId{gamenight_id: gamenight.id.to_string()})).await?;
for participant in participants.participants.iter() {
gamenight.participants.0.push(user_get(&state.api_configuration, Some(UserId{user_id: participant.clone()})).await?.try_into()?);
gamenight.participants.0.push(state.api.user_get(Some(UserId{user_id: participant.clone()})).await?.try_into()?);
}
for user in &gamenight.participants.0 {
let request = UserId{ user_id: user.id.to_string() };
let games: Vec<Game> = join_all(owned_games_get(&state.api_configuration, Some(request)).await?
.iter().map(async |game_id| -> Result<models::Game, Error<GameGetError>> {
let request = GameId{ game_id: game_id.clone() };
game_get(&state.api_configuration, Some(request)).await
})).await.into_iter().collect::<Result<Vec<models::Game>, Error<GameGetError>>>()?.into_iter().map(Into::into).collect();
gamenight.owned_games.0.insert(user.username.clone(), games);
let owned_games_refs: Vec<OwnedGame> = state.api.owned_games_get(Some(request)).await?;
let mut owned_games = vec![];
for owned_games_ref in owned_games_refs {
let game = state.api.game_get(Some(GameId{ game_id: owned_games_ref.game_id.clone()})).await?.into();
let location = match owned_games_ref.location_id {
None => None,
Some(x) => Some(state.api.location_get(Some(LocationId{location_id: x})).await?.try_into()?)
};
owned_games.push((game, location));
}
gamenight.owned_games.0.insert(user.username.clone(), owned_games);
}
println!("{}", gamenight);

View File

@@ -6,9 +6,9 @@ use flows::{main::Main, Flow, GamenightState};
#[tokio::main]
async fn main() {
let mut state = GamenightState::new();
let mainflow = Main::new();
let main_flow = Main::new();
clear_screen::clear();
if let Err(x) = mainflow.run(&mut state).await {
if let Err(x) = main_flow.run(&mut state).await {
println!("{}", x.error);
}
}