From 06f769ac998d4414508754caafba6a9fecc8fdd8 Mon Sep 17 00:00:00 2001 From: clizia Date: Wed, 5 Mar 2025 16:34:18 +0100 Subject: [PATCH] tedioso --- Cargo.lock | 209 ++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/bin/client.rs | 148 ++++++++++++-------------------- src/card.rs | 4 +- src/client.rs | 108 +++++++++++++++++++----- src/lib.rs | 2 +- src/player.rs | 2 +- src/ui.rs | 104 +++++++++++++++++++++++ 8 files changed, 460 insertions(+), 118 deletions(-) create mode 100644 src/ui.rs diff --git a/Cargo.lock b/Cargo.lock index e454473..0bb09ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "anstream" version = "0.6.18" @@ -269,6 +275,21 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "castaway" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" +dependencies = [ + "rustversion", +] + [[package]] name = "cc" version = "1.2.15" @@ -330,6 +351,20 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "compact_str" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", +] + [[package]] name = "console-api" version = "0.5.0" @@ -450,6 +485,41 @@ dependencies = [ "typenum", ] +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.95", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.95", +] + [[package]] name = "digest" version = "0.10.7" @@ -485,6 +555,7 @@ dependencies = [ "hyper-util", "petname", "rand 0.9.0-beta.1", + "ratatui", "reqwest", "serde", "serde_json", @@ -548,6 +619,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "foreign-types" version = "0.3.2" @@ -724,6 +801,11 @@ name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] [[package]] name = "hdrhistogram" @@ -1051,6 +1133,12 @@ dependencies = [ "syn 2.0.95", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "1.0.3" @@ -1092,6 +1180,25 @@ dependencies = [ "hashbrown 0.15.2", ] +[[package]] +name = "indoc" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" + +[[package]] +name = "instability" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d" +dependencies = [ + "darling", + "indoc", + "proc-macro2", + "quote", + "syn 2.0.95", +] + [[package]] name = "ipnet" version = "2.11.0" @@ -1113,6 +1220,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.14.0" @@ -1178,6 +1294,15 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.2", +] + [[package]] name = "matchers" version = "0.1.0" @@ -1372,6 +1497,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1550,6 +1681,27 @@ dependencies = [ "zerocopy 0.8.14", ] +[[package]] +name = "ratatui" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" +dependencies = [ + "bitflags 2.6.0", + "cassowary", + "compact_str", + "crossterm", + "indoc", + "instability", + "itertools 0.13.0", + "lru", + "paste", + "strum", + "unicode-segmentation", + "unicode-truncate", + "unicode-width 0.2.0", +] + [[package]] name = "redox_syscall" version = "0.5.8" @@ -1923,12 +2075,40 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.95", +] + [[package]] name = "subtle" version = "2.6.1" @@ -2278,6 +2458,35 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-truncate" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" +dependencies = [ + "itertools 0.13.0", + "unicode-segmentation", + "unicode-width 0.1.14", +] + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "untrusted" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index e3674ef..14a2a6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,3 +34,4 @@ petname = "2.0.2" crossterm = "0.28.1" tracing = "0.1.41" tracing-subscriber = "0.3.19" +ratatui = "0.29.0" diff --git a/src/bin/client.rs b/src/bin/client.rs index 02c02ce..7aefcd6 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -1,6 +1,6 @@ use std::{io::{Stdout, Write}, ops::BitOrAssign, time::Duration}; -use axum::response::sse::Event; +use axum::response::{sse::Event, AppendHeaders}; use clap::builder::NonEmptyStringValueParser; use lib::{client::Client, message::{Message, MessageKind}, message_read::MessageReader, player::Player}; use crossterm::{ @@ -8,107 +8,67 @@ use crossterm::{ disable_raw_mode, enable_raw_mode, Clear, ClearType, EnableLineWrap, EnterAlternateScreen } }; +use ratatui::{ + buffer::Buffer, + layout::Rect, + style::Stylize, + symbols::border, + text::{Line, Text}, + widgets::{Block, Paragraph, Widget}, + DefaultTerminal, + Frame, +}; -const HELP: &str = r#"CIAOTERPIA -atm posso fare questo: -(ricorda di premere i bottoni) -- '1' mando giocante -- '2' mando lobbando (futuro) -"#; +// async fn delete_player(player: &Player, client: reqwest::Client, addr: &str) -> anyhow::Result<()> { +// let player_id = &player.name; +// let response = client +// .get([&addr, "/delete/", &player_id].concat()) +// .send().await; -fn create_message_player(player: &Player) -> anyhow::Result { - Message::new( - &player.get_name(), - MessageKind::Test, - player.encode()? - ) -} +// if let Err(e) = &response { +// println!("{e}\n"); +// return Ok(()) +// } -async fn delete_player(player: &Player, client: reqwest::Client, addr: &str) -> anyhow::Result<()> { - let player_id = &player.name; - let response = client - .get([&addr, "/delete/", &player_id].concat()) - .send().await; +// println!("{}", response?.text().await?); - if let Err(e) = &response { - println!("{e}\n"); - return Ok(()) - } +// Ok(()) +// } - println!("{}", response?.text().await?); +// async fn handle_terminal(player: &Player, stdout: &mut Stdout, client: reqwest::Client, addr: &str) -> anyhow::Result<()> { +// 'handler: loop { +// while let Ok(_event_happened) = poll(Duration::from_millis(2)) { +// match event::read().expect("diomerds") { +// TermEvent::Key(key) => { +// match (key.code, key.modifiers) { +// (KeyCode::Char('c'), KeyModifiers::CONTROL) => break 'handler, +// (KeyCode::Char('h'), KeyModifiers::NONE) => { +// for line in HELP.split('\n') { +// println!("{line}") +// } +// }, +// (KeyCode::Char('1'), KeyModifiers::NONE) => { +// send_player(&player, client.clone(), addr).await?; +// }, +// (KeyCode::Char('2'), KeyModifiers::NONE) => { +// delete_player(&player, client.clone(), addr).await?; +// }, +// (_, _) => println!("{:?}\n", key), +// } +// }, +// _ => continue, +// } +// } +// } - Ok(()) -} - -async fn send_player(player: &Player, client: reqwest::Client, addr: &str) -> anyhow::Result<()> { - let player_message = create_message_player(&player)?; - let response = client - .post([&addr, "/create/player"].concat()) - .header("Content-Type", "application/json") - .body(player_message.encode()?) - .send().await; - - if let Err(e) = &response { - println!("{e}\n"); - return Ok(()) - } - - println!("{}", response?.text().await?); - - Ok(()) -} -async fn handle_terminal(player: &Player, stdout: &mut Stdout, client: reqwest::Client, addr: &str) -> anyhow::Result<()> { - 'handler: loop { - while let Ok(_event_happened) = poll(Duration::from_millis(2)) { - match event::read().expect("diomerds") { - TermEvent::Key(key) => { - match (key.code, key.modifiers) { - (KeyCode::Char('c'), KeyModifiers::CONTROL) => break 'handler, - (KeyCode::Char('h'), KeyModifiers::NONE) => { - for line in HELP.split('\n') { - println!("{line}") - } - }, - (KeyCode::Char('1'), KeyModifiers::NONE) => { - send_player(&player, client.clone(), addr).await?; - }, - (KeyCode::Char('2'), KeyModifiers::NONE) => { - delete_player(&player, client.clone(), addr).await?; - }, - (_, _) => println!("{:?}\n", key), - } - }, - _ => continue, - } - } - } - - Ok(()) -} +// Ok(()) +// } #[tokio::main] pub async fn main() -> anyhow::Result<()> { let mut stdout = std::io::stdout(); - - enable_raw_mode()?; - execute!( - stdout, - Clear(ClearType::All), - EnableLineWrap, - cursor::Show, - cursor::EnableBlinking, - EnterAlternateScreen, - )?; - - let player = Player::new()?; - - let addr = "http://127.0.0.1:8080"; - let client = reqwest::Client::new(); - - // println!("{}", HELP); - - handle_terminal(&player, &mut stdout, client.clone(), &addr).await?; - disable_raw_mode()?; - - Ok(()) + let mut terminal = ratatui::init(); + let app_result = Client::default().run(&mut terminal).await; + ratatui::restore(); + app_result } diff --git a/src/card.rs b/src/card.rs index ef7f5e6..bfe47ce 100644 --- a/src/card.rs +++ b/src/card.rs @@ -2,13 +2,13 @@ use std::fmt::Display; use serde::{Serialize, Deserialize}; -#[derive(Serialize, Deserialize, PartialEq, Eq, Hash, Clone)] +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Clone)] pub struct Card { pub suit: Suit, pub value: u8, } -#[derive(Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] pub enum Suit { Hearts, Diamonds, diff --git a/src/client.rs b/src/client.rs index 82ee267..4ba93fd 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,33 +1,101 @@ -use tokio::{io::AsyncWriteExt, net::TcpStream}; +use ratatui::crossterm::{event::{self, Event, KeyCode, KeyEvent, KeyEventKind}}; +use ratatui::{ + buffer::Buffer, + layout::{Constraint, Flex, Layout, Rect}, + style::Stylize, + symbols::border, + text::{Line, Text}, + widgets::{Block, Borders, Clear, Paragraph, Widget, Wrap}, + DefaultTerminal, + Frame, +}; -use crate::{message::Message, player::Player}; +use crate::{message::{Message, MessageKind}, player::Player, ui::ui}; +#[derive(Debug, Default)] pub struct Client { - pub server_host: String, - pub server_port: u16, - pub stream: TcpStream, + pub addr: String, + pub client: reqwest::Client, + pub user: Option, + pub name: String, + pub exit: bool, + pub popup: bool, + pub response: String, } impl Client { - pub async fn connect(host: impl Into, port: u16) -> anyhow::Result { - let host = host.into(); - let address = format!("{}:{}", host, port); - let stream = TcpStream::connect(address).await?; + pub async fn run(&mut self, terminal: &mut DefaultTerminal) -> anyhow::Result<()> { + self.addr = "http://120.0.0.1:8080".to_string(); + self.client = reqwest::Client::new(); - Ok(Self { - server_host: host, - server_port: port, - stream, - }) - } + while !self.exit { + terminal.draw(|frame| ui(frame, self))?; + crate::ui::handle_events(self).await?; + } - pub async fn send_message(&mut self, message: Message) -> anyhow::Result<()> { - let message_string = message.encode()?; - self.stream.write_all(&message_string.as_bytes()).await?; Ok(()) } - pub async fn create_player() -> Option { - todo!() + pub fn exit(&mut self) { + self.exit = true; + } + + pub async fn send_player(&mut self, player: &Player, addr: &str) -> anyhow::Result<()> { + let player_message = Message::new( + &player.get_name(), + MessageKind::CreatePlayer, + player.encode()? + )?; + + let response = self.client + .post([&addr, "/create/player"].concat()) + .header("Content-Type", "application/json") + .body(player_message.encode()?) + .send().await; + + if let Err(e) = &response { + self.response = e.to_string(); + return Ok(()) + } else { + self.response = format!("{:?}", response?); + } + + Ok(()) + } +} + +impl Widget for &Client { + fn render(self, area: Rect, buf: &mut Buffer) { + let title = Line::from(" durak tui test fre ".bold()); + + let instructions = Line::from(vec![ + " create player ".into(), + "1".blue().bold(), + " create lobby ".into(), + "2".blue().bold(), + " quit ".into(), + "".blue().bold(), + ]); + + let block = Block::bordered() + .title(title.centered()) + .title_bottom(instructions.centered()) + .border_set(border::THICK); + + let infos = Text::from(vec![ + Line::from(vec![ + "connected to: ".into(), + self.addr.clone().yellow(), + ]), + Line::from(vec![ + "username: ".into(), + self.name.clone().yellow(), + ]), + ]); + + Paragraph::new(infos) + .left_aligned() + .block(block) + .render(area, buf); } } diff --git a/src/lib.rs b/src/lib.rs index 3efbd9a..c781f55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,6 @@ pub mod player; pub mod message; pub mod message_read; pub mod constant; -pub mod routine; pub mod db; pub mod router; +pub mod ui; diff --git a/src/player.rs b/src/player.rs index 6da6631..513aa46 100644 --- a/src/player.rs +++ b/src/player.rs @@ -6,7 +6,7 @@ use axum::Json; use crate::card::Card; -#[derive(Serialize, Deserialize, Eq, PartialEq, Hash, Clone)] +#[derive(Default, Debug, Serialize, Deserialize, Eq, PartialEq, Hash, Clone)] pub struct Player { pub id: String, pub name: String, diff --git a/src/ui.rs b/src/ui.rs new file mode 100644 index 0000000..ab45194 --- /dev/null +++ b/src/ui.rs @@ -0,0 +1,104 @@ +use ratatui::{ + layout::{ + Constraint, + Direction, + Layout, + Rect + }, + style::{Color, Style, Stylize}, + text::{Line, Span, Text}, + widgets::{Block, Borders, Clear, List, ListItem, Paragraph, Widget, Wrap}, + Frame, +}; +use ratatui::crossterm::{event::{self, Event, KeyCode, KeyEvent, KeyEventKind}}; + +use crate::{client::Client, player::Player}; + +async fn handle_key_event(mut client: &mut Client, key_event: KeyEvent) { + match key_event.code { + KeyCode::Char('q') => Client::exit(client), + KeyCode::Char('1') => { + if !client.name.is_empty() { + println!("user already created"); + return + } + + client.user = Some(Player::new().expect("toto")); + client.name = client.user.clone().unwrap().name; + }, + KeyCode::Char('2') => { + todo!() + }, + KeyCode::Char('3') => { + if let Some(player) = client.user.clone() { + let addr = client.addr.clone(); + Client::send_player(&mut client, &player, &addr).await.unwrap(); + return + } + } + _ => {} + } +} + +pub async fn handle_events(client: &mut Client) -> anyhow::Result<()> { + match event::read()? { + Event::Key(key) if key.kind == KeyEventKind::Press => { + handle_key_event(client, key).await + } + _ => {} + }; + + Ok(()) +} + +pub fn ui(frame: &mut Frame, app: &Client) { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Min(3), + Constraint::Min(1), + ]) + .split(frame.area()); + + let instructions = Line::from(vec![ + " 1".blue().bold(), + " create player ".into(), + "2".blue().bold(), + " create lobby ".into(), + "3".blue().bold(), + " send player ".into(), + "".blue().bold(), + " quit ".into(), + ]); + + let infos = Text::from(vec![ + Line::from(vec![ + "connected to: ".into(), + app.addr.clone().yellow(), + ]), + Line::from(vec![ + "username: ".into(), + app.name.clone().yellow(), + ]), + ]); + + let title = Line::from(" durak tui test fre ".bold()); + let top_block = Block::default() + .title(title.centered()) + .borders(Borders::ALL) + .style(Style::default()); + frame.render_widget(Paragraph::new(infos).block(top_block), chunks[0]); + + let server_message = Text::from(vec![ + Line::from(vec![ + "message: ".into(), + app.response.clone().blue(), + ]) + ]); + + let btm_block = Block::default() + .title_bottom(instructions.centered()) + .borders(Borders::ALL) + .style(Style::default()); + frame.render_widget(Paragraph::new(server_message).block(btm_block), chunks[1]); +}