desktopd 1.0.0

Browse sway/i3 windows and browser tabs.
Documentation
use crate::browser::*;
use crate::message::*;
use crate::sway::types::*;
use async_std::net::SocketAddr;
use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
use log::info;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

pub type Tx = UnboundedSender<DesktopdMessage>;
pub type Rx = UnboundedReceiver<DesktopdMessage>;
pub type TabId = usize;
pub type WindowId = usize;

pub struct State {
    peers: HashMap<SocketAddr, (Option<ConnectionType>, Tx)>,
    tabs: HashMap<WindowId, HashMap<TabId, BrowserTab>>,
    windows: HashMap<WindowId, SwayWindow>,
}

impl State {
    pub fn new() -> State {
        State {
            peers: HashMap::new(),
            tabs: HashMap::new(),
            windows: HashMap::new(),
        }
    }

    pub fn add_peer(&mut self, addr: SocketAddr, tx: Tx) {
        self.peers.insert(addr, (None, tx));
    }

    pub fn get_stale_peers(&self, id: &str) -> Vec<SocketAddr> {
        self.peers
            .iter()
            .filter(|(_, (conn, _))| conn.as_ref().map(|it| it.has_id(&id)).unwrap_or(false))
            .map(|(addr, _)| addr.clone())
            .collect::<Vec<SocketAddr>>()
    }

    pub fn mark_browser(&mut self, id: String, addr: &SocketAddr) {
        let stale = self.get_stale_peers(&id);

        for conn in stale {
            if let Some(_) = self.peers.remove(&conn) {
                info!("removing stale browser connection");
            }
        }

        if let Some((_, tx)) = self.peers.remove(addr) {
            info!("marking {} as browser with id {}", addr, id);
            self.peers
                .insert(*addr, (Some(ConnectionType::Browser { id }), tx));
        }
    }

    pub fn mark_cli(&mut self, addr: &SocketAddr) {
        if let Some((_, tx)) = self.peers.remove(addr) {
            self.peers.insert(*addr, (Some(ConnectionType::Cli), tx));
        }
    }

    pub fn remove_peer(&mut self, addr: &SocketAddr) -> Option<(Option<ConnectionType>, Tx)> {
        self.peers.remove(addr)
    }

    pub fn find_peer(&self, addr: &SocketAddr) -> Option<Tx> {
        if self.peers.contains_key(addr) {
            Some(self.peers[addr].1.clone())
        } else {
            None
        }
    }

    pub fn get_browser_windows(&self) -> Vec<&SwayWindow> {
        self.windows
            .iter()
            .filter(|(_, win)| win.is_browser())
            .map(|(_, win)| win)
            .collect::<Vec<&SwayWindow>>()
    }

    pub fn get_browser_connections(&self) -> Vec<(SocketAddr, Tx)> {
        self.peers.iter().fold(vec![], |mut out, (addr, (t, tx))| {
            if let Some(ConnectionType::Browser { .. }) = t {
                out.push((*addr, tx.clone()));
            }
            out
        })
    }

    pub fn get_focused(&self) -> Vec<&SwayWindow> {
        self.windows
            .iter()
            .filter(|(_, win)| win.focused)
            .map(|(_, win)| win)
            .collect::<Vec<&SwayWindow>>()
    }

    pub fn remove_focused(&mut self) -> Vec<SwayWindow> {
        let mut out = vec![];
        let ids = self
            .get_focused()
            .iter()
            .map(|win| win.id)
            .collect::<Vec<usize>>();

        for id in ids {
            if let Some(focused) = self.windows.remove(&id) {
                out.push(focused)
            }
        }
        out
    }

    pub fn add_window(&mut self, win: SwayWindow) {
        self.windows.insert(win.id, win);
    }

    pub fn remove_window(&mut self, id: &WindowId) {
        self.windows.remove(id);
    }

    pub fn clients(&self) -> Vec<DesktopdClient> {
        let tabs = self
            .tabs
            .iter()
            .flat_map(|(_, inner)| inner.iter().map(|(_, tabs)| tabs))
            .map(|tab| DesktopdClient::Tab { data: tab.clone() })
            .collect::<Vec<DesktopdClient>>();

        let mut windows = self
            .windows
            .iter()
            .map(|(_, win)| DesktopdClient::Window { data: win.clone() })
            .collect::<Vec<DesktopdClient>>();

        windows.extend(tabs);
        windows
    }

    pub fn add_tab(&mut self, tab: BrowserTab) {
        if self.tabs.contains_key(&tab.window_id) {
            self.tabs.get_mut(&tab.window_id).map(|inner| {
                inner.insert(tab.id, tab);
            });
        } else {
            let mut map = HashMap::new();
            let window_id = tab.window_id;
            map.insert(tab.id, tab);
            self.tabs.insert(window_id, map);
        }
    }

    pub fn remove_tab(&mut self, tab: BrowserTabRef) {
        if let Some(mut tabs) = self.tabs.remove(&tab.window_id) {
            tabs.remove(&tab.tab_id);
            self.tabs.insert(tab.window_id, tabs);
        }
    }

    pub fn find_tab(&self, tab: &BrowserTabRef) -> Option<&BrowserTab> {
        self.tabs
            .get(&tab.window_id)
            .map(|tabs| tabs.get(&tab.tab_id))
            .flatten()
    }
}

pub type GlobalState = Arc<Mutex<State>>;