use std::{
collections::HashSet,
ops::{Deref, DerefMut},
};
use thiserror::Error;
use uuid::Uuid;
use crate::{
db::{EntryId, EntryMut, EntryRef, GroupId, GroupMut, GroupRef},
Database,
};
#[derive(Debug, Eq, PartialEq, Clone)]
#[cfg_attr(feature = "serialization", derive(serde::Serialize))]
pub enum Icon {
BuiltIn(usize),
Custom(CustomIconId),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serialization", derive(serde::Serialize))]
pub struct CustomIconId(Uuid);
impl std::fmt::Display for CustomIconId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl CustomIconId {
pub(crate) fn new() -> Self {
Self(Uuid::new_v4())
}
pub(crate) const fn from_uuid(uuid: Uuid) -> Self {
Self(uuid)
}
pub fn uuid(&self) -> Uuid {
self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serialization", derive(serde::Serialize))]
pub struct CustomIcon {
pub(crate) id: CustomIconId,
pub(crate) entries: HashSet<(EntryId, Option<usize>)>,
pub(crate) groups: HashSet<GroupId>,
pub data: Vec<u8>,
}
impl CustomIcon {
pub fn id(&self) -> CustomIconId {
self.id
}
}
impl Deref for CustomIcon {
type Target = Vec<u8>;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl DerefMut for CustomIcon {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
pub struct CustomIconRef<'a> {
database: &'a Database,
id: CustomIconId,
}
impl CustomIconRef<'_> {
pub(crate) fn new(database: &Database, id: CustomIconId) -> CustomIconRef<'_> {
CustomIconRef { database, id }
}
pub fn database(&self) -> &Database {
self.database
}
pub fn entries(&self, include_historical: bool) -> impl Iterator<Item = EntryRef<'_>> {
self.entries.iter().filter_map(move |&(id, history_index)| {
if !include_historical && history_index.is_some() {
return None;
}
Some(EntryRef::new_historical(self.database, id, history_index))
})
}
pub fn groups(&self) -> impl Iterator<Item = GroupRef<'_>> {
self.groups
.iter()
.map(move |&id| GroupRef::new(self.database, id))
}
}
impl Deref for CustomIconRef<'_> {
type Target = CustomIcon;
fn deref(&self) -> &Self::Target {
self.database
.custom_icons
.get(&self.id)
.expect("Custom icon ID always valid")
}
}
pub struct CustomIconMut<'a> {
database: &'a mut Database,
id: CustomIconId,
}
impl CustomIconMut<'_> {
pub(crate) fn new(database: &mut Database, id: CustomIconId) -> CustomIconMut<'_> {
CustomIconMut { database, id }
}
pub fn as_ref(&self) -> CustomIconRef<'_> {
CustomIconRef {
database: self.database,
id: self.id,
}
}
pub fn edit(&mut self, f: impl FnOnce(&mut CustomIconMut<'_>)) -> &mut Self {
f(self);
self
}
pub fn database_mut(&mut self) -> &mut Database {
self.database
}
pub fn foreach_entry_mut<F>(&mut self, mut f: F, include_historical: bool)
where
F: FnMut(EntryMut<'_>),
{
let entries: Vec<(EntryId, Option<usize>)> = self.entries.iter().copied().collect();
for (id, history_index) in entries {
if !include_historical && history_index.is_some() {
continue;
}
f(EntryMut::new_historical(self.database, id, history_index));
}
}
pub fn foreach_group_mut<F>(&mut self, mut f: F)
where
F: FnMut(GroupMut<'_>),
{
let groups: Vec<GroupId> = self.groups.iter().copied().collect();
for id in groups {
f(GroupMut::new(self.database, id));
}
}
pub fn remove(mut self) {
let id = self.id;
self.foreach_entry_mut(
|mut entry| {
if entry.icon == Some(Icon::Custom(id)) {
entry.icon = None;
}
},
true,
);
self.foreach_group_mut(|mut group| {
if group.icon == Some(Icon::Custom(id)) {
group.icon = None;
}
});
self.database.custom_icons.remove(&id);
}
}
impl Deref for CustomIconMut<'_> {
type Target = CustomIcon;
fn deref(&self) -> &Self::Target {
self.database
.custom_icons
.get(&self.id)
.expect("Custom icon ID always valid")
}
}
impl DerefMut for CustomIconMut<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.database
.custom_icons
.get_mut(&self.id)
.expect("Custom icon ID always valid")
}
}
#[derive(Error, Debug)]
#[error("Custom icon {0} not found")]
pub struct CustomIconNotFoundError(pub(crate) CustomIconId);