keepass/db/types/
attachment.rs1use std::{
2 collections::HashSet,
3 ops::{Deref, DerefMut},
4};
5
6use crate::{
7 db::{EntryId, EntryMut, EntryRef, Value},
8 Database,
9};
10
11#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
13#[cfg_attr(feature = "serialization", derive(serde::Serialize))]
14pub struct AttachmentId(usize);
15
16impl AttachmentId {
17 pub(crate) fn new(id: usize) -> Self {
18 AttachmentId(id)
19 }
20
21 pub fn id(&self) -> usize {
23 self.0
24 }
25
26 pub(crate) fn next_free(database: &Database) -> Self {
27 let mut id = 0;
28 while database.attachments.contains_key(&AttachmentId(id)) {
29 id += 1;
30 }
31 AttachmentId(id)
32 }
33}
34
35impl std::fmt::Display for AttachmentId {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 write!(f, "{}", self.0)
38 }
39}
40
41#[derive(Debug, PartialEq, Eq, Clone)]
46#[cfg_attr(feature = "serialization", derive(serde::Serialize))]
47pub struct Attachment {
48 pub(crate) id: AttachmentId,
49
50 pub(crate) entries: HashSet<(EntryId, Option<usize>)>,
53
54 pub data: Value<Vec<u8>>,
56}
57
58impl Attachment {
59 pub fn id(&self) -> AttachmentId {
61 self.id
62 }
63}
64
65impl Deref for Attachment {
66 type Target = Value<Vec<u8>>;
67
68 fn deref(&self) -> &Self::Target {
69 &self.data
70 }
71}
72
73impl DerefMut for Attachment {
74 fn deref_mut(&mut self) -> &mut Self::Target {
75 &mut self.data
76 }
77}
78
79pub struct AttachmentRef<'a> {
81 database: &'a Database,
82 id: AttachmentId,
83}
84
85impl AttachmentRef<'_> {
86 pub(crate) fn new(database: &Database, id: AttachmentId) -> AttachmentRef<'_> {
87 AttachmentRef { database, id }
88 }
89
90 pub fn database(&self) -> &Database {
92 self.database
93 }
94
95 pub fn entries(&self, include_historical: bool) -> impl Iterator<Item = EntryRef<'_>> {
101 self.entries.iter().filter_map(move |&(id, history_index)| {
102 if !include_historical && history_index.is_some() {
103 return None;
104 }
105
106 Some(EntryRef::new_historical(self.database, id, history_index))
107 })
108 }
109}
110
111impl Deref for AttachmentRef<'_> {
112 type Target = Attachment;
113
114 fn deref(&self) -> &Self::Target {
115 #[allow(clippy::expect_used)]
117 self.database
118 .attachments
119 .get(&self.id)
120 .expect("AttachmentRef points to non-existent attachment")
121 }
122}
123
124pub struct AttachmentMut<'a> {
126 database: &'a mut Database,
127 id: AttachmentId,
128}
129
130impl AttachmentMut<'_> {
131 pub(crate) fn new(database: &mut Database, id: AttachmentId) -> AttachmentMut<'_> {
132 AttachmentMut { database, id }
133 }
134
135 pub fn as_ref(&self) -> AttachmentRef<'_> {
137 AttachmentRef {
138 database: self.database,
139 id: self.id,
140 }
141 }
142
143 pub fn edit(&mut self, f: impl FnOnce(&mut AttachmentMut<'_>)) -> &mut Self {
145 f(self);
146 self
147 }
148
149 pub fn database_mut(&mut self) -> &mut Database {
151 self.database
152 }
153
154 pub fn foreach_entry_mut<F>(&mut self, mut f: F, include_historical: bool)
160 where
161 F: FnMut(EntryMut<'_>),
162 {
163 let entries: Vec<(EntryId, Option<usize>)> = self.entries.iter().copied().collect();
164 for (id, history_index) in entries {
165 if !include_historical && history_index.is_some() {
166 continue;
167 }
168
169 f(EntryMut::new_historical(self.database, id, history_index));
170 }
171 }
172
173 pub fn remove(mut self) {
175 let id = self.id;
176
177 self.foreach_entry_mut(
178 |mut entry| {
179 let mut attachments_to_remove = Vec::new();
180 for (name, attachment_id) in &entry.attachments {
181 if *attachment_id == id {
182 attachments_to_remove.push(name.clone());
183 }
184 }
185
186 for name in attachments_to_remove {
187 entry.attachments.remove(&name);
188 }
189 },
190 true,
191 );
192
193 self.database.attachments.remove(&self.id);
194 }
195}
196
197impl Deref for AttachmentMut<'_> {
198 type Target = Attachment;
199
200 fn deref(&self) -> &Self::Target {
201 #[allow(clippy::expect_used)]
203 self.database
204 .attachments
205 .get(&self.id)
206 .expect("AttachmentMut points to non-existent attachment")
207 }
208}
209
210impl DerefMut for AttachmentMut<'_> {
211 fn deref_mut(&mut self) -> &mut Self::Target {
212 #[allow(clippy::expect_used)]
214 self.database
215 .attachments
216 .get_mut(&self.id)
217 .expect("AttachmentMut points to non-existent attachment")
218 }
219}