// src/client/terminal.rs use crossterm::event::{self, Event}; use crossterm::{ execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; use crossterm::cursor::{SetCursorStyle, EnableBlinking}; use ratatui::{backend::CrosstermBackend, Terminal}; use std::io::{self, stdout}; use tonic::transport::Channel; // Import the correct clients and proto messages from their respective modules use common::proto::multieko2::adresar::adresar_client::AdresarClient; use common::proto::multieko2::adresar::{AdresarResponse, PostAdresarRequest, PutAdresarRequest}; use common::proto::multieko2::common::{CountResponse, PositionRequest, Empty}; use common::proto::multieko2::table_structure::table_structure_service_client::TableStructureServiceClient; use common::proto::multieko2::table_structure::TableStructureResponse; pub struct AppTerminal { terminal: Terminal>, adresar_client: AdresarClient, table_structure_client: TableStructureServiceClient, } impl AppTerminal { pub fn set_cursor_style( &mut self, style: SetCursorStyle, ) -> Result<(), Box> { execute!( self.terminal.backend_mut(), style, EnableBlinking, )?; Ok(()) } pub async fn new() -> Result> { enable_raw_mode()?; let mut stdout = stdout(); execute!( stdout, EnterAlternateScreen, SetCursorStyle::SteadyBlock )?; let backend = CrosstermBackend::new(stdout); let terminal = Terminal::new(backend)?; // Initialize both gRPC clients let adresar_client = AdresarClient::connect("http://[::1]:50051").await?; let table_structure_client = TableStructureServiceClient::connect("http://[::1]:50051").await?; Ok(Self { terminal, adresar_client, table_structure_client }) } pub fn draw(&mut self, f: F) -> Result<(), Box> where F: FnOnce(&mut ratatui::Frame), { self.terminal.draw(f)?; Ok(()) } pub fn read_event(&self) -> Result> { Ok(event::read()?) } pub fn cleanup(&mut self) -> Result<(), Box> { disable_raw_mode()?; execute!(self.terminal.backend_mut(), LeaveAlternateScreen)?; Ok(()) } pub async fn handle_command( &mut self, action: &str, is_saved: &mut bool, ) -> Result<(bool, String), Box> { match action { "quit" => { if *is_saved { self.cleanup()?; Ok((true, "Exiting.".to_string())) } else { Ok((false, "No changes saved. Use :q! to force quit.".to_string())) } } "force_quit" => { self.cleanup()?; Ok((true, "Force exiting without saving.".to_string())) } "save_and_quit" => { *is_saved = true; self.cleanup()?; Ok((true, "State saved. Exiting.".to_string())) } _ => Ok((false, format!("Action not recognized: {}", action))), } } // Adresar service methods use adresar_client pub async fn get_adresar_count(&mut self) -> Result> { let request = tonic::Request::new(Empty::default()); let response: CountResponse = self.adresar_client.get_adresar_count(request).await?.into_inner(); Ok(response.count as u64) } pub async fn get_adresar_by_position(&mut self, position: u64) -> Result> { let request = tonic::Request::new(PositionRequest { position: position as i64 }); let response: AdresarResponse = self.adresar_client.get_adresar_by_position(request).await?.into_inner(); Ok(response) } pub async fn post_adresar( &mut self, request: PostAdresarRequest, ) -> Result, Box> { let request = tonic::Request::new(request); let response = self.adresar_client.post_adresar(request).await?; Ok(response) } pub async fn put_adresar( &mut self, request: PutAdresarRequest, ) -> Result, Box> { let request = tonic::Request::new(request); let response = self.adresar_client.put_adresar(request).await?; Ok(response) } // Table structure method uses table_structure_client pub async fn get_table_structure( &mut self, ) -> Result> { let request = tonic::Request::new(Empty::default()); let response = self.table_structure_client .get_adresar_table_structure(request) .await?; Ok(response.into_inner()) } }