Added some more gamenight-cli Flows.

This commit is contained in:
2025-05-02 22:58:45 +02:00
parent db25dc0aed
commit 6699dcf392
11 changed files with 309 additions and 66 deletions

View File

@@ -1,5 +1,6 @@
use super::*;
#[derive(Clone)]
pub struct Abort {
}

View File

@@ -0,0 +1,44 @@
use gamenight_api_client_rs::{apis::default_api::post_gamenight, models};
use inquire::{Text, DateSelect};
use chrono::{self, Local, NaiveTime};
use super::*;
#[derive(Clone)]
pub struct AddGamenight {
}
impl AddGamenight {
pub fn new() -> Self {
Self {}
}
}
#[async_trait]
impl<'a> Flow<'a> for AddGamenight {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
let mut add_gamenight = models::AddGamenightRequestBody::new();
add_gamenight.name = Some(Text::new("What should we call your gamenight").prompt()?);
add_gamenight.datetime = Some(DateSelect::new("When is your gamenight").prompt()?
.and_time(NaiveTime::default())
.and_local_timezone(Local)
.earliest()
.unwrap()
.to_utc()
.to_rfc3339());
post_gamenight(&state.configuration, Some(add_gamenight)).await?;
Ok((FlowOutcome::Successful, state))
}
}
impl Display for AddGamenight {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Add Gamenight.")
}
}

View File

@@ -1,8 +1,12 @@
use gamenight_api_client_rs::apis::default_api::get_gamenights;
use inquire::Select;
use super::*;
use crate::flows::view_gamenight::ViewGamenight;
use super::{abort::Abort, *};
#[derive(Clone)]
pub struct ListGamenights {
}
@@ -17,8 +21,16 @@ impl ListGamenights {
impl<'a> Flow<'a> for ListGamenights {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
let response = get_gamenights(&state.configuration).await?;
println!("{:?}", response);
Ok((FlowOutcome::Successful, state))
let mut view_flows = response.into_iter().map(|gamenight| -> Box<dyn Flow<'a> + Send> {
Box::new(ViewGamenight::new(gamenight))
}).collect::<Vec<Box<dyn Flow<'a> + Send>>>();
view_flows.push(Box::new(Abort::new()));
let choice = Select::new("What gamenight would you like to view?", view_flows)
.prompt_skippable()?;
handle_choice_option(&choice, self, state).await
}
}

View File

@@ -4,6 +4,7 @@ use inquire::{Password, Text};
use super::*;
#[derive(Clone)]
pub struct Login {
}

View File

@@ -1,7 +1,7 @@
use super::{abort::Abort, list_gamenights::ListGamenights, login::Login, main_menu::MainMenu, *};
use super::{abort::Abort, add_gamenight::AddGamenight, list_gamenights::ListGamenights, login::Login, main_menu::MainMenu, *};
#[derive(Clone)]
pub struct Main {
login: Box<dyn for<'a> Flow<'a>>,
main_menu: MainMenu
@@ -11,6 +11,7 @@ impl Main {
pub fn new() -> Self {
let mut main_menu = MainMenu::new();
main_menu.menu.push(Box::new(ListGamenights::new()));
main_menu.menu.push(Box::new(AddGamenight::new()));
main_menu.menu.push(Box::new(Abort::new()));
Self {
login: Box::new(Login::new()),

View File

@@ -3,6 +3,7 @@ use inquire::{ui::RenderConfig, Select};
use super::*;
#[derive(Clone)]
pub struct MainMenu {
pub menu: Vec<Box<dyn for<'a> Flow<'a> + Send>>
}
@@ -22,7 +23,7 @@ unsafe impl Send for MainMenu {
#[async_trait]
impl<'a> Flow<'a> for MainMenu {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
let choice = Select::new("What would you like to do?", self.menu)
let choice = Select::new("What would you like to do?", self.menu.clone())
.with_help_message("Select the action you want to take or quit the program")
.with_render_config(RenderConfig {
option_index_prefix: inquire::ui::IndexPrefix::Simple,
@@ -30,19 +31,7 @@ impl<'a> Flow<'a> for MainMenu {
})
.prompt_skippable()?;
if let Some(choice) = choice {
let (outcome, new_state) = choice.run(state).await?;
if outcome == FlowOutcome::Abort {
Ok((outcome, new_state))
}
else {
self.run(new_state).await
}
}
else {
return Ok((FlowOutcome::Abort, state))
}
handle_choice_option(&choice, self, state).await
}
}

View File

@@ -1,14 +1,18 @@
use std::fmt::Display;
use async_trait::async_trait;
use chrono::ParseError;
use gamenight_api_client_rs::apis::configuration::Configuration;
use inquire::InquireError;
use dyn_clone::DynClone;
pub mod main;
pub mod login;
mod login;
mod main_menu;
mod abort;
mod list_gamenights;
mod add_gamenight;
mod view_gamenight;
pub struct GamenightState {
configuration: Configuration,
@@ -43,6 +47,14 @@ impl<T> From<gamenight_api_client_rs::apis::Error<T>> for FlowError {
}
}
impl From<ParseError> for FlowError {
fn from(value: ParseError) -> Self {
Self {
error: value.to_string()
}
}
}
#[derive(PartialEq)]
pub enum FlowOutcome {
Successful,
@@ -53,7 +65,29 @@ pub enum FlowOutcome {
type FlowResult<'a> = Result<(FlowOutcome, &'a mut GamenightState), FlowError>;
dyn_clone::clone_trait_object!(for<'a> Flow<'a>);
#[async_trait]
pub trait Flow<'a>: Sync + Display {
pub trait Flow<'a>: Sync + Display + DynClone + Send {
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?;
if outcome == FlowOutcome::Abort {
Ok((FlowOutcome::Successful, new_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::Abort, state))
}
}

View File

@@ -0,0 +1,43 @@
use chrono::DateTime;
use gamenight_api_client_rs::models::Gamenight;
use super::*;
#[derive(Clone)]
pub struct ViewGamenight {
gamenight: Gamenight
}
impl ViewGamenight {
pub fn new(gamenight: Gamenight) -> Self {
Self {
gamenight
}
}
pub fn gamenight_localtime(&self) -> Result<String, FlowError> {
let datetime = DateTime::parse_from_rfc3339(&self.gamenight.datetime)?;
let offset = *chrono::offset::Local::now().offset();
Ok(format!("{}", datetime.naive_local().checked_add_offset(offset).unwrap().format("%d-%m-%Y %H:%M")))
}
}
#[async_trait]
impl<'a> Flow<'a> for ViewGamenight {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
println!("Name: {}", self.gamenight.name);
println!("When: {}", self.gamenight_localtime()?);
Ok((FlowOutcome::Successful, state))
}
}
impl Display for ViewGamenight {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} {}", self.gamenight.name, self.gamenight.datetime)
}
}