use std::{ collections::HashMap, io::Read, path::PathBuf, sync::{Arc, RwLock}, }; use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; use urlencoding::decode; use uuid::Uuid; use crate::types::{AppError, Message, Tabletop, RGB}; #[derive(Debug)] struct WebsocketClient { sender: Option<UnboundedSender<Message>>, } #[derive(Debug)] pub struct AppState { pub image_base: PathBuf, pub tabletop: Tabletop, pub clients: HashMap<String, WebsocketClient>, } #[derive(Clone, Debug)] pub struct Core(pub Arc<RwLock<AppState>>); impl Core { pub fn new() -> Self { Self(Arc::new(RwLock::new(AppState { image_base: PathBuf::from("/home/savanni/Pictures"), tabletop: Tabletop { background_color: RGB{ red: 0xca, green: 0xb9, blue: 0xbb }, background_image: None, }, clients: HashMap::new(), }))) } pub fn register_client(&self) -> String { let mut state = self.0.write().unwrap(); let uuid = Uuid::new_v4().simple().to_string(); let client = WebsocketClient { sender: None }; state.clients.insert(uuid.clone(), client); uuid } pub fn unregister_client(&self, client_id: String) { let mut state = self.0.write().unwrap(); let _ = state.clients.remove(&client_id); } pub fn connect_client(&self, client_id: String) -> UnboundedReceiver<Message> { let mut state = self.0.write().unwrap(); match state.clients.get_mut(&client_id) { Some(client) => { let (tx, rx) = unbounded_channel(); client.sender = Some(tx); rx } None => { unimplemented!(); } } } pub fn get_file(&self, file_name: String) -> Vec<u8> { let file_name = decode(&file_name).expect("UTF-8"); let mut full_path = self.0.read().unwrap().image_base.clone(); full_path.push(file_name.to_string()); let mut content: Vec<u8> = Vec::new(); let mut file = std::fs::File::open(&full_path).unwrap(); file.read_to_end(&mut content).unwrap(); content } pub fn available_images(&self) -> Vec<String> { std::fs::read_dir(&self.0.read().unwrap().image_base) .unwrap() .filter_map(|entry| match entry { Ok(entry_) => match mime_guess::from_path(entry_.path()).first() { Some(mime) if mime.type_() == mime::IMAGE => Some( entry_ .path() .file_name() .and_then(|filename| filename.to_str()) .and_then(|filename| Some(filename.to_owned())) .unwrap(), ), _ => None, }, Err(_) => None, }) .collect() } pub fn set_background_image(&self, path: String) -> Result<(), AppError> { let tabletop = { let mut state = self.0.write().unwrap(); state.tabletop.background_image = Some(path.clone()); state.tabletop.clone() }; self.publish(Message::UpdateTabletop(tabletop)); Ok(()) } pub fn publish(&self, message: Message) { let state = self.0.read().unwrap(); state.clients.values().for_each(|client| { if let Some(ref sender) = client.sender { let _ = sender.send(message.clone()); } }); } }