Adds renaming games functionality

This commit is contained in:
2025-07-12 17:07:33 +02:00
parent 28f7306d57
commit 3f99b68d62
30 changed files with 502 additions and 178 deletions

View File

@@ -45,9 +45,9 @@ dependencies = [
[[package]]
name = "autocfg"
version = "1.4.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "backtrace"
@@ -84,9 +84,9 @@ checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "bumpalo"
version = "3.18.1"
version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "byteorder"
@@ -102,9 +102,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "cc"
version = "1.2.26"
version = "1.2.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac"
checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362"
dependencies = [
"shlex",
]
@@ -381,9 +381,9 @@ dependencies = [
[[package]]
name = "hyper-util"
version = "0.1.14"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb"
checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df"
dependencies = [
"base64",
"bytes",
@@ -552,6 +552,17 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "io-uring"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013"
dependencies = [
"bitflags 2.9.1",
"cfg-if",
"libc",
]
[[package]]
name = "ipnet"
version = "2.11.0"
@@ -601,9 +612,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.172"
version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[package]]
name = "litemap"
@@ -825,24 +836,24 @@ dependencies = [
[[package]]
name = "r-efi"
version = "5.2.0"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "redox_syscall"
version = "0.5.12"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
dependencies = [
"bitflags 2.9.1",
]
[[package]]
name = "reqwest"
version = "0.12.20"
version = "0.12.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813"
checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531"
dependencies = [
"base64",
"bytes",
@@ -1013,6 +1024,12 @@ dependencies = [
"time",
]
[[package]]
name = "slab"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
[[package]]
name = "smallvec"
version = "1.15.1"
@@ -1037,9 +1054,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "syn"
version = "2.0.102"
version = "2.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6397daf94fa90f058bd0fd88429dd9e5738999cca8d701813c80723add80462"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
dependencies = [
"proc-macro2",
"quote",
@@ -1088,12 +1105,11 @@ dependencies = [
[[package]]
name = "thread_local"
version = "1.1.8"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
@@ -1139,17 +1155,19 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.45.1"
version = "1.46.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17"
dependencies = [
"backtrace",
"bytes",
"io-uring",
"libc",
"mio 1.0.4",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"slab",
"socket2",
"tokio-macros",
"windows-sys 0.52.0",
@@ -1459,9 +1477,9 @@ dependencies = [
[[package]]
name = "windows-link"
version = "0.1.1"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]]
name = "windows-result"

View File

@@ -0,0 +1,26 @@
use std::fmt::Display;
use gamenight_api_client_rs::models;
use uuid::Uuid;
#[derive(Clone)]
pub struct Game {
pub id: Uuid,
pub name: String
}
impl From<models::Game> for Game {
fn from(game: models::Game) -> Self {
Self {
id: Uuid::parse_str(&game.id).unwrap(),
name: game.name
}
}
}
impl Display for Game {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Name: {}", self.name)
}
}

View File

@@ -1,4 +1,5 @@
pub mod gamenight;
pub mod user;
pub mod config;
pub mod participants;
pub mod participants;
pub mod game;

View File

@@ -1,7 +1,7 @@
use std::fmt::Display;
use async_trait::async_trait;
use gamenight_api_client_rs::{apis::default_api::game_post, models::{AddGameRequestBody, Game}};
use gamenight_api_client_rs::{apis::default_api::game_post, models::AddGameRequestBody};
use inquire::{Confirm, Text};

View File

@@ -1,31 +0,0 @@
use gamenight_api_client_rs::models::Game;
use super::*;
#[derive(Clone)]
pub struct EditGame {
game: Game
}
impl EditGame {
pub fn new(game: Game) -> Self {
Self {
game
}
}
}
#[async_trait]
impl<'a> Flow<'a> for EditGame {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
todo!()
}
}
impl Display for EditGame {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "")
}
}

View File

@@ -38,8 +38,7 @@ impl<'a> Flow<'a> for GamenightMenu {
})
.prompt_skippable()?;
clear_screen::clear();
handle_choice_option(&choice, self, state).await
self.continue_choice(state, &choice).await
}
}

View File

@@ -34,9 +34,8 @@ impl<'a> Flow<'a> for Games {
..Default::default()
})
.prompt_skippable()?;
clear_screen::clear();
handle_choice_option(&choice, self, state).await
self.continue_choice(state, &choice).await
}
}

View File

@@ -40,8 +40,7 @@ impl<'a> Flow<'a> for ListGamenights {
let choice = Select::new("What gamenight would you like to view?", view_flows).prompt_skippable()?;
clear_screen::clear();
handle_choice_option(&choice, self, state).await
self.continue_choice(state, &choice).await
}
}

View File

@@ -4,7 +4,7 @@ use async_trait::async_trait;
use gamenight_api_client_rs::apis::default_api::games_get;
use inquire::{ui::RenderConfig, Select};
use crate::flows::{edit_game::EditGame, exit::Exit};
use crate::flows::{view_game::ViewGame, exit::Exit};
use super::*;
@@ -25,7 +25,7 @@ impl<'a> Flow<'a> for ListGames {
let games = games_get(&state.api_configuration).await?;
let mut flows = games.into_iter().map(|game| -> Box<dyn Flow + Send> {
Box::new(EditGame::new(game))
Box::new(ViewGame::new(game.into()))
}).collect::<Vec::<Box::<dyn Flow + Send>>>();
flows.push(Box::new(Exit::new()));
@@ -38,9 +38,7 @@ impl<'a> Flow<'a> for ListGames {
})
.prompt_skippable()?;
clear_screen::clear();
handle_choice_option(&choice, self, state).await
self.continue_choice(state, &choice).await
}
}

View File

@@ -50,8 +50,7 @@ impl<'a> Flow<'a> for Main {
})
.prompt_skippable()?;
clear_screen::clear();
handle_choice_option(&choice, self, state).await
self.continue_choice(state, &choice).await
}
}

View File

@@ -23,7 +23,8 @@ mod settings;
mod games;
mod list_games;
mod add_game;
mod edit_game;
mod view_game;
mod rename_game;
pub struct GamenightState {
api_configuration: Configuration,
@@ -111,25 +112,27 @@ dyn_clone::clone_trait_object!(for<'a> Flow<'a>);
#[async_trait]
pub trait Flow<'a>: Sync + DynClone + Send + Display {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a>;
}
async fn handle_choice<'a>(choice: &Box<dyn Flow<'a> + Send>, flow: &dyn Flow<'a>, state: &'a mut GamenightState) -> FlowResult<'a> {
let (outcome, new_state) = choice.run(state).await?;
async fn continue_choice(&self, state: &'a mut GamenightState, choice: &Option<Box<dyn Flow<'a> + Send>>) -> FlowResult<'a> {
clear_screen::clear();
if let Some(choice) = choice {
let (outcome, new_state) = choice.run(state).await?;
if outcome == FlowOutcome::Abort {
Ok((FlowOutcome::Successful, new_state))
if outcome == FlowOutcome::Abort {
Ok((FlowOutcome::Successful, new_state))
}
else {
self.run(new_state).await
}
}
else {
Ok((FlowOutcome::Cancelled, state))
}
}
else {
flow.run(new_state).await
}
}
async fn handle_choice_option<'a>(choice: &Option<Box<dyn Flow<'a> + Send>>, flow: &dyn Flow<'a>, state: &'a mut GamenightState) -> FlowResult<'a> {
if let Some(choice) = choice {
handle_choice(choice, flow, state).await
}
else {
Ok((FlowOutcome::Cancelled, state))
async fn continue_with(&self, state: &'a mut GamenightState, other: &dyn Flow<'a>) -> FlowResult<'a> {
clear_screen::clear();
other.run(state).await
}
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a>;
}

View File

@@ -0,0 +1,47 @@
use std::fmt::Display;
use async_trait::async_trait;
use gamenight_api_client_rs::{apis::default_api::rename_game_post, models::RenameGameRequestBody};
use inquire::Text;
use crate::domain::game::Game;
use super::{Flow, FlowOutcome, FlowResult, GamenightState};
#[derive(Clone)]
pub struct RenameGame {
pub game: Game
}
impl RenameGame {
pub fn new(game: Game) -> Self {
Self{
game
}
}
}
#[async_trait]
impl<'a> Flow<'a> for RenameGame {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
if let Some(name) = Text::new(&format!("Rename {} to:", self.game.name))
.with_initial_value(&format!("{}", self.game.name))
.prompt_skippable()?
{
let req = RenameGameRequestBody {
id: self.game.id.to_string(),
name
};
rename_game_post(&state.api_configuration, Some(req)).await?;
return Ok((FlowOutcome::Successful, state))
}
Ok((FlowOutcome::Cancelled, state))
}
}
impl Display for RenameGame {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Rename")
}
}

View File

@@ -0,0 +1,46 @@
use gamenight_api_client_rs::{apis::default_api::game_get, models::GameId};
use inquire::Select;
use crate::{domain::game::Game, flows::{exit::Exit, rename_game::RenameGame}};
use super::*;
#[derive(Clone)]
pub struct ViewGame {
game: Game
}
impl ViewGame {
pub fn new(game: Game) -> Self {
Self {
game
}
}
}
#[async_trait]
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();
println!("{}", game);
let options: Vec<Box<dyn Flow<'a> + Send>> = vec![
Box::new(RenameGame::new(game.clone())),
Box::new(Exit::new())
];
let choice = Select::new("What do you want to do:", options)
.prompt_skippable()?;
self.continue_choice(state, &choice).await
}
}
impl Display for ViewGame {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.game.name)
}
}

View File

@@ -77,8 +77,7 @@ impl<'a> Flow<'a> for ViewGamenight {
let choice = Select::new("What do you want to do:", options)
.prompt_skippable()?;
clear_screen::clear();
handle_choice_option(&choice, self, state).await
self.continue_choice(state, &choice).await
}
}