124 lines
3.6 KiB
Rust
124 lines
3.6 KiB
Rust
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());
|
|
}
|
|
});
|
|
}
|
|
}
|