use std::collections::{HashMap, VecDeque};
use std::hash::Hash;
use snafu::{prelude::*, Location};
use crate::wl_backend::{WlGenericId, WlHead, WlMode};
use super::{WlBaseHead, WlBaseMode};
pub trait ForeignId {
type Id: std::fmt::Debug + Clone + PartialEq + Eq + Hash;
fn foreign_id(&self) -> Self::Id;
}
#[derive(Clone, Debug)]
pub struct StoreHead<H, I>
where
H: ForeignId<Id = I>,
{
pub base: WlBaseHead,
pub(crate) current_mode: Option<I>,
modes: Vec<I>,
id: WlGenericId,
pub(crate) foreign_head: H,
_foreign_id: I,
}
#[derive(Clone, Debug)]
pub struct StoreMode<M, I>
where
M: ForeignId<Id = I>,
{
pub base: WlBaseMode,
id: WlGenericId,
pub(crate) foreign_mode: M,
_foreign_id: I,
}
#[derive(Clone, Debug)]
pub struct WlStore<H, M, I>
where
H: ForeignId<Id = I>,
M: ForeignId<Id = I>,
{
heads: HashMap<I, StoreHead<H, I>>,
modes: HashMap<I, StoreMode<M, I>>,
mode_id_head_id: HashMap<I, I>,
store_key_to_foreign_key: HashMap<WlGenericId, I>,
id_counter: usize,
}
impl<H, M, I> Default for WlStore<H, M, I>
where
H: ForeignId<Id = I>,
M: ForeignId<Id = I>,
{
fn default() -> Self {
Self {
heads: Default::default(),
modes: Default::default(),
mode_id_head_id: Default::default(),
store_key_to_foreign_key: Default::default(),
id_counter: Default::default(),
}
}
}
impl<H, M, I> WlStore<H, M, I>
where
H: ForeignId<Id = I>,
M: ForeignId<Id = I>,
I: std::fmt::Debug + Clone + PartialEq + Eq + Hash,
{
pub fn export(&self) -> Result<VecDeque<WlHead>, WlStoreError<I>> {
self.heads
.values()
.map(|head| self.export_head(head))
.collect()
}
pub fn heads_count(&self) -> usize {
self.heads.len()
}
pub fn head(&self, head_id: I) -> Result<&StoreHead<H, I>, WlStoreError<I>> {
self.heads
.get(&head_id)
.context(HeadNotFoundCtx { head_id })
}
pub fn mode(&self, mode_id: I) -> Result<&StoreMode<M, I>, WlStoreError<I>> {
self.modes
.get(&mode_id)
.context(ModeNotFoundCtx { mode_id })
}
pub fn mode_store_key(
&self,
mode_id: WlGenericId,
) -> Result<&StoreMode<M, I>, WlStoreError<I>> {
let mode_id = self
.store_key_to_foreign_key
.get(&mode_id)
.context(UnknownStoreKeyCtx { key: mode_id })?;
self.mode(mode_id.clone())
}
pub fn head_store_key(
&self,
head_id: WlGenericId,
) -> Result<&StoreHead<H, I>, WlStoreError<I>> {
let head_id = self
.store_key_to_foreign_key
.get(&head_id)
.context(UnknownStoreKeyCtx { key: head_id })?;
self.head(head_id.clone())
}
pub fn head_mut(&mut self, head_id: I) -> Result<&mut StoreHead<H, I>, WlStoreError<I>> {
self.heads
.get_mut(&head_id)
.context(HeadNotFoundCtx { head_id })
}
pub fn mode_mut(&mut self, mode_id: I) -> Result<&mut StoreMode<M, I>, WlStoreError<I>> {
self.modes
.get_mut(&mode_id)
.context(ModeNotFoundCtx { mode_id })
}
pub fn insert_head(&mut self, foreign_head: H) {
let foreign_head_id = foreign_head.foreign_id();
let store_head = StoreHead {
base: Default::default(),
current_mode: Default::default(),
modes: Default::default(),
id: self.new_store_id(),
foreign_head,
_foreign_id: foreign_head_id.clone(),
};
self.store_key_to_foreign_key
.insert(store_head.id, foreign_head_id.clone());
self.heads.insert(foreign_head_id, store_head);
}
pub fn insert_mode(&mut self, foreign_head_id: I, foreign_mode: M) {
let foreign_mode_id = foreign_mode.foreign_id();
let store_mode = StoreMode {
base: Default::default(),
id: self.new_store_id(),
foreign_mode,
_foreign_id: foreign_mode_id.clone(),
};
self.store_key_to_foreign_key
.insert(store_mode.id, foreign_mode_id.clone());
self.heads
.entry(foreign_head_id.clone())
.and_modify(|head| head.modes.push(foreign_mode_id.clone()));
self.mode_id_head_id
.insert(foreign_mode_id.clone(), foreign_head_id);
self.modes.insert(foreign_mode_id, store_mode);
}
pub fn remove_mode(&mut self, mode_id: &I) -> Result<(), WlStoreError<I>> {
let head_id = self.mode_id_head_id.remove(mode_id).ok_or(
ModeNotFoundCtx {
mode_id: mode_id.clone(),
}
.build(),
)?;
let head = self
.heads
.get_mut(&head_id)
.ok_or(HeadNotFoundCtx { head_id }.build())?;
if let Some(c_mode_id) = &head.current_mode {
if *c_mode_id == *mode_id {
head.current_mode = None;
}
}
head.modes.retain(|id| *id != *mode_id);
Ok(())
}
pub fn remove_head(&mut self, head_id: &I) {
self.heads.remove(head_id);
}
fn export_head(&self, head: &StoreHead<H, I>) -> Result<WlHead, WlStoreError<I>> {
let StoreHead {
base,
current_mode: store_head_current_mode,
modes,
id,
foreign_head: _,
_foreign_id: _,
} = head;
let mut current_mode: Option<WlMode> = None;
if let Some(current_mode_id) = store_head_current_mode {
let store_mode = self.mode(current_mode_id.clone())?;
current_mode = Some(WlMode {
base: store_mode.base,
id: store_mode.id,
});
}
let modes = modes
.iter()
.map(|mi| self.mode(mi.clone()))
.filter_map(|r| r.ok())
.map(|m| WlMode {
base: m.base,
id: m.id,
})
.collect();
let wl_head = WlHead {
base: base.clone(),
current_mode,
modes,
id: *id,
};
Ok(wl_head)
}
fn new_store_id(&mut self) -> WlGenericId {
self.id_counter += 1;
WlGenericId(self.id_counter)
}
}
#[derive(Debug, Snafu)]
#[snafu(context(suffix(Ctx)))]
#[snafu(visibility(pub(crate)))]
pub enum WlStoreError<I: std::fmt::Debug> {
#[snafu(display("[{location}] Cannot find head in store: {head_id:?}"))]
HeadNotFound { location: Location, head_id: I },
#[snafu(display("[{location}] Cannot find mode in store: {mode_id:?}"))]
ModeNotFound { location: Location, mode_id: I },
#[snafu(display("[{location}] Cannot find store key: {key:?}"))]
UnknownStoreKey {
location: Location,
key: WlGenericId,
},
}