use std::{
collections::HashSet,
ops::{Deref, DerefMut},
};
use crate::{
db::{EntryId, EntryMut, EntryRef, Value},
Database,
};
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
#[cfg_attr(feature = "serialization", derive(serde::Serialize))]
pub struct AttachmentId(usize);
impl AttachmentId {
pub(crate) fn new(id: usize) -> Self {
AttachmentId(id)
}
pub fn id(&self) -> usize {
self.0
}
pub(crate) fn next_free(database: &Database) -> Self {
let mut id = 0;
while database.attachments.contains_key(&AttachmentId(id)) {
id += 1;
}
AttachmentId(id)
}
}
impl std::fmt::Display for AttachmentId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serialization", derive(serde::Serialize))]
pub struct Attachment {
pub(crate) id: AttachmentId,
pub(crate) entries: HashSet<(EntryId, Option<usize>)>,
pub data: Value<Vec<u8>>,
}
impl Attachment {
pub fn id(&self) -> AttachmentId {
self.id
}
}
impl Deref for Attachment {
type Target = Value<Vec<u8>>;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl DerefMut for Attachment {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
pub struct AttachmentRef<'a> {
database: &'a Database,
id: AttachmentId,
}
impl AttachmentRef<'_> {
pub(crate) fn new(database: &Database, id: AttachmentId) -> AttachmentRef<'_> {
AttachmentRef { 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))
})
}
}
impl Deref for AttachmentRef<'_> {
type Target = Attachment;
fn deref(&self) -> &Self::Target {
self.database
.attachments
.get(&self.id)
.expect("AttachmentRef points to non-existent attachment")
}
}
pub struct AttachmentMut<'a> {
database: &'a mut Database,
id: AttachmentId,
}
impl AttachmentMut<'_> {
pub(crate) fn new(database: &mut Database, id: AttachmentId) -> AttachmentMut<'_> {
AttachmentMut { database, id }
}
pub fn as_ref(&self) -> AttachmentRef<'_> {
AttachmentRef {
database: self.database,
id: self.id,
}
}
pub fn edit(&mut self, f: impl FnOnce(&mut AttachmentMut<'_>)) -> &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 remove(mut self) {
let id = self.id;
self.foreach_entry_mut(
|mut entry| {
let mut attachments_to_remove = Vec::new();
for (name, attachment_id) in &entry.attachments {
if *attachment_id == id {
attachments_to_remove.push(name.clone());
}
}
for name in attachments_to_remove {
entry.attachments.remove(&name);
}
},
true,
);
self.database.attachments.remove(&self.id);
}
}
impl Deref for AttachmentMut<'_> {
type Target = Attachment;
fn deref(&self) -> &Self::Target {
self.database
.attachments
.get(&self.id)
.expect("AttachmentMut points to non-existent attachment")
}
}
impl DerefMut for AttachmentMut<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.database
.attachments
.get_mut(&self.id)
.expect("AttachmentMut points to non-existent attachment")
}
}