1use super::entry_data::EntryData;
4use crate::{
5 blob::BlobId,
6 error::{Error, Result},
7 protocol::Bump,
8 version_vector::VersionVector,
9};
10use serde::Deserialize;
11use std::{
12 cmp::Ordering,
13 collections::{
14 btree_map::{self, Entry},
15 BTreeMap,
16 },
17};
18
19pub const VERSION: u64 = 2;
21
22#[derive(Clone, Debug)]
23pub(super) struct Content {
24 entries: v2::Entries,
25}
26
27impl Content {
28 pub fn empty() -> Self {
29 Self {
30 entries: BTreeMap::new(),
31 }
32 }
33
34 pub fn deserialize(mut input: &[u8]) -> Result<Self> {
35 let version = vint64::decode(&mut input).map_err(|_| Error::MalformedDirectory)?;
36 let entries = match version {
37 VERSION => deserialize_entries(input),
38 1 => Ok(v2::from_v1(deserialize_entries(input)?)),
39 0 => Ok(v2::from_v1(v1::from_v0(deserialize_entries(input)?))),
40 _ => Err(Error::StorageVersionMismatch),
41 };
42
43 Ok(Self { entries: entries? })
44 }
45
46 pub fn serialize(&self) -> Vec<u8> {
47 let mut output = Vec::new();
48 output.extend_from_slice(vint64::encode(VERSION).as_ref());
49 bincode::serialize_into(&mut output, &self.entries)
50 .expect("failed to serialize directory content");
51 output
52 }
53
54 pub fn iter(&self) -> btree_map::Iter<String, EntryData> {
55 self.entries.iter()
56 }
57
58 pub fn get_key_value(&self, name: &str) -> Option<(&String, &EntryData)> {
59 self.entries.get_key_value(name)
60 }
61
62 pub fn get_mut(&mut self, name: &str) -> Option<&mut EntryData> {
63 self.entries.get_mut(name)
64 }
65
66 pub fn insert(
69 &mut self,
70 name: String,
71 new_data: EntryData,
72 ) -> Result<VersionVector, EntryExists> {
73 match self.entries.entry(name) {
74 Entry::Vacant(entry) => {
75 let diff = new_data.version_vector().clone();
76 entry.insert(new_data);
77 Ok(diff)
78 }
79 Entry::Occupied(mut entry) => {
80 check_replace(entry.get(), &new_data)?;
81 let diff = new_data
82 .version_vector()
83 .saturating_sub(entry.get().version_vector());
84 entry.insert(new_data);
85 Ok(diff)
86 }
87 }
88 }
89
90 pub fn check_insert(
93 &self,
94 name: &str,
95 new_data: &EntryData,
96 ) -> Result<Option<BlobId>, EntryExists> {
97 if let Some(old_data) = self.entries.get(name) {
98 check_replace(old_data, new_data)
99 } else {
100 Ok(None)
101 }
102 }
103
104 pub fn bump(&mut self, name: &str, bump: Bump) -> Result<VersionVector> {
107 Ok(bump.apply(
108 self.entries
109 .get_mut(name)
110 .ok_or(Error::EntryNotFound)?
111 .version_vector_mut(),
112 ))
113 }
114
115 pub fn initial_version_vector(&self, name: &str) -> VersionVector {
117 if let Some(EntryData::Tombstone(entry)) = self.entries.get(name) {
118 entry.version_vector.clone()
119 } else {
120 VersionVector::new()
121 }
122 }
123}
124
125#[derive(Debug)]
126pub(crate) enum EntryExists {
127 Same,
130 Different,
132}
133
134impl From<EntryExists> for Error {
135 fn from(_: EntryExists) -> Self {
136 Self::EntryExists
137 }
138}
139
140impl<'a> IntoIterator for &'a Content {
141 type Item = <Self::IntoIter as Iterator>::Item;
142 type IntoIter = btree_map::Iter<'a, String, EntryData>;
143
144 fn into_iter(self) -> Self::IntoIter {
145 self.iter()
146 }
147}
148
149fn deserialize_entries<'a, T: Deserialize<'a>>(input: &'a [u8]) -> Result<T, Error> {
150 bincode::deserialize(input).map_err(|_| Error::MalformedDirectory)
151}
152
153fn check_replace(old: &EntryData, new: &EntryData) -> Result<Option<BlobId>, EntryExists> {
154 match (
157 new.version_vector().partial_cmp(old.version_vector()),
158 new,
159 old,
160 ) {
161 (Some(Ordering::Greater), _, _) => Ok(old.blob_id().copied()),
162 (Some(Ordering::Equal | Ordering::Less), EntryData::File(new), EntryData::File(old))
163 if new.blob_id == old.blob_id =>
164 {
165 Err(EntryExists::Same)
166 }
167 (
168 Some(Ordering::Equal | Ordering::Less),
169 EntryData::Directory(new),
170 EntryData::Directory(old),
171 ) if new.blob_id == old.blob_id => Err(EntryExists::Same),
172 (
173 Some(Ordering::Equal | Ordering::Less),
174 EntryData::Tombstone(new),
175 EntryData::Tombstone(old),
176 ) if new.cause == old.cause => Err(EntryExists::Same),
177 (
178 Some(Ordering::Equal | Ordering::Less),
179 EntryData::File(_) | EntryData::Directory(_) | EntryData::Tombstone(_),
180 EntryData::File(_) | EntryData::Directory(_) | EntryData::Tombstone(_),
181 ) => Err(EntryExists::Different),
182 (None, _, _) => Err(EntryExists::Different),
183 }
184}
185
186mod v2 {
187 use super::{
188 super::entry_data::{EntryData, EntryTombstoneData, TombstoneCause},
189 v1,
190 };
191 use std::collections::BTreeMap;
192
193 pub(super) type Entries = BTreeMap<String, EntryData>;
194
195 pub(super) fn from_v1(v1: v1::Entries) -> Entries {
196 v1.into_iter()
197 .map(|(name, data)| {
198 let data = match data {
199 v1::EntryData::File(data) => EntryData::File(data),
200 v1::EntryData::Directory(data) => EntryData::Directory(data),
201 v1::EntryData::Tombstone(v1::EntryTombstoneData { version_vector }) => {
202 EntryData::Tombstone(EntryTombstoneData {
203 cause: TombstoneCause::Removed,
204 version_vector,
205 })
206 }
207 };
208
209 (name, data)
210 })
211 .collect()
212 }
213}
214
215mod v1 {
216 use super::v0;
217 use std::collections::BTreeMap;
218 pub(super) use v0::{EntryData, EntryTombstoneData};
219
220 pub(super) type Entries = BTreeMap<String, v0::EntryData>;
221
222 pub(super) fn from_v0(v0: v0::Entries) -> Entries {
223 use crate::conflict;
224
225 let mut v1 = BTreeMap::new();
226
227 for (name, versions) in v0 {
228 if versions.len() <= 1 {
229 if let Some(data) = versions.into_values().next() {
231 v1.insert(name, data);
232 }
233 } else {
234 for (author_id, data) in versions {
237 v1.insert(conflict::create_unique_name(&name, &author_id), data);
238 }
239 }
240 }
241
242 v1
243 }
244}
245
246mod v0 {
247 use super::super::entry_data::{EntryDirectoryData, EntryFileData};
248 use crate::{crypto::sign::PublicKey, version_vector::VersionVector};
249 use serde::Deserialize;
250 use std::collections::BTreeMap;
251
252 pub(super) type Entries = BTreeMap<String, BTreeMap<PublicKey, EntryData>>;
253
254 #[derive(Deserialize)]
255 pub(super) enum EntryData {
256 File(EntryFileData),
257 Directory(EntryDirectoryData),
258 Tombstone(EntryTombstoneData),
259 }
260
261 #[derive(Deserialize)]
262 pub(super) struct EntryTombstoneData {
263 pub version_vector: VersionVector,
264 }
265}