1use super::{Atom, Change, Hashed, Local};
2#[cfg(feature = "zstd")]
3use super::{ChangeError, LocalChange, Offsets};
4use crate::Hash;
5#[cfg(feature = "zstd")]
6use crate::pristine::Hasher;
7
8#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
9pub enum Hunk<Hash, Local> {
10 FileMove {
11 del: Atom<Hash>,
12 add: Atom<Hash>,
13 path: String,
14 },
15 FileDel {
16 del: Atom<Hash>,
17 contents: Option<Atom<Hash>>,
18 path: String,
19 },
20 FileUndel {
21 undel: Atom<Hash>,
22 contents: Option<Atom<Hash>>,
23 path: String,
24 },
25 FileAdd {
26 add_name: Atom<Hash>,
27 add_inode: Atom<Hash>,
28 contents: Option<Atom<Hash>>,
29 path: String,
30 },
31 SolveNameConflict {
32 name: Atom<Hash>,
33 path: String,
34 },
35 UnsolveNameConflict {
36 name: Atom<Hash>,
37 path: String,
38 },
39 Edit {
40 change: Atom<Hash>,
41 local: Local,
42 },
43 Replacement {
44 change: Atom<Hash>,
45 replacement: Atom<Hash>,
46 local: Local,
47 },
48 SolveOrderConflict {
49 change: Atom<Hash>,
50 local: Local,
51 },
52 UnsolveOrderConflict {
53 change: Atom<Hash>,
54 local: Local,
55 },
56 ResurrectZombies {
57 change: Atom<Hash>,
58 local: Local,
59 },
60}
61
62impl<H, L> From<Hunk<H, L>> for super::Hunk<H, L> {
63 fn from(h: Hunk<H, L>) -> Self {
64 let encoding = Some(crate::text_encoding::Encoding(encoding_rs::UTF_8));
65 match h {
66 Hunk::FileMove { del, add, path } => super::Hunk::FileMove { del, add, path },
67 Hunk::FileDel {
68 del,
69 contents,
70 path,
71 } => super::Hunk::FileDel {
72 del,
73 contents,
74 path,
75 encoding,
76 },
77 Hunk::FileUndel {
78 undel,
79 contents,
80 path,
81 } => super::Hunk::FileUndel {
82 undel,
83 contents,
84 path,
85 encoding,
86 },
87 Hunk::FileAdd {
88 add_name,
89 add_inode,
90 contents,
91 path,
92 } => super::Hunk::FileAdd {
93 add_name,
94 add_inode,
95 contents,
96 path,
97 encoding,
98 },
99 Hunk::SolveNameConflict { name, path } => super::Hunk::SolveNameConflict { name, path },
100 Hunk::UnsolveNameConflict { name, path } => {
101 super::Hunk::UnsolveNameConflict { name, path }
102 }
103 Hunk::Edit { change, local } => super::Hunk::Edit {
104 change,
105 local,
106 encoding,
107 },
108 Hunk::Replacement {
109 change,
110 replacement,
111 local,
112 } => super::Hunk::Replacement {
113 change,
114 replacement,
115 local,
116 encoding,
117 },
118 Hunk::SolveOrderConflict { change, local } => {
119 super::Hunk::SolveOrderConflict { change, local }
120 }
121 Hunk::UnsolveOrderConflict { change, local } => {
122 super::Hunk::UnsolveOrderConflict { change, local }
123 }
124 Hunk::ResurrectZombies { change, local } => super::Hunk::ResurrectZombies {
125 change,
126 local,
127 encoding,
128 },
129 }
130 }
131}
132
133impl Change {
134 #[cfg(feature = "zstd")]
136 pub(super) fn deserialize_noenc(
137 offsets: Offsets,
138 mut r: std::fs::File,
139 hash: Option<&Hash>,
140 ) -> Result<Self, ChangeError> {
141 use std::io::Read;
142 let mut buf = vec![0u8; (offsets.unhashed_off - Self::OFFSETS_SIZE) as usize];
143 r.read_exact(&mut buf)?;
144
145 let hashed: Hashed<Hunk<Option<Hash>, Local>, Author> = {
146 let mut s = zstd_seekable::Seekable::init_buf(&buf[..])?;
147 let mut out = vec![0u8; offsets.hashed_len as usize];
148 s.decompress(&mut out[..], 0)?;
149 let mut hasher = Hasher::default();
150 hasher.update(&out);
151 let computed_hash = hasher.finish();
152 if let Some(hash) = hash
153 && &computed_hash != hash
154 {
155 return Err(super::ChangeError::ChangeHashMismatch {
156 claimed: *hash,
157 computed: computed_hash,
158 });
159 }
160 bincode::deserialize_from(&out[..])?
161 };
162 buf.clear();
163 buf.resize((offsets.contents_off - offsets.unhashed_off) as usize, 0);
164 let unhashed = if buf.is_empty() {
165 None
166 } else {
167 r.read_exact(&mut buf)?;
168 let mut s = zstd_seekable::Seekable::init_buf(&buf[..])?;
169 let mut out = vec![0u8; offsets.unhashed_len as usize];
170 s.decompress(&mut out[..], 0)?;
171 serde_json::from_slice(&out).ok()
172 };
173 trace!("unhashed = {:?}", unhashed);
174
175 buf.clear();
176 buf.resize((offsets.total - offsets.contents_off) as usize, 0);
177 let contents = if r.read_exact(&mut buf).is_ok() {
178 let mut s = zstd_seekable::Seekable::init_buf(&buf[..])?;
179 let mut contents = vec![0u8; offsets.contents_len as usize];
180 s.decompress(&mut contents[..], 0)?;
181 contents
182 } else {
183 Vec::new()
184 };
185 trace!("contents = {:?}", contents);
186
187 Ok(LocalChange {
188 offsets,
189 hashed: hashed.into(),
190 unhashed,
191 contents,
192 })
193 }
194}
195
196impl From<Hashed<Hunk<Option<Hash>, Local>, Author>>
197 for Hashed<super::Hunk<Option<Hash>, Local>, super::Author>
198{
199 fn from(hashed: Hashed<Hunk<Option<Hash>, Local>, Author>) -> Self {
200 Hashed {
201 contents_hash: hashed.contents_hash,
202 dependencies: hashed.dependencies,
203 extra_known: hashed.extra_known,
204 header: hashed.header.into(),
205 metadata: hashed.metadata,
206 version: hashed.version,
207 changes: hashed.changes.into_iter().map(|x| x.into()).collect(),
208 }
209 }
210}
211
212use super::ChangeHeader_;
213
214impl From<ChangeHeader_<Author>> for ChangeHeader_<super::Author> {
215 fn from(c: ChangeHeader_<Author>) -> Self {
216 ChangeHeader_ {
217 message: c.message,
218 description: c.description,
219 timestamp: c.timestamp,
220 authors: c.authors.into_iter().map(|x| x.into()).collect(),
221 }
222 }
223}
224
225#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
226pub struct Author {
227 pub name: String,
228 #[serde(default)]
229 pub full_name: Option<String>,
230 #[serde(default)]
231 pub email: Option<String>,
232}
233
234impl From<String> for Author {
235 fn from(name: String) -> Author {
236 Author {
237 name,
238 ..Author::default()
239 }
240 }
241}
242
243impl From<Author> for super::Author {
244 fn from(c: Author) -> Self {
245 let mut b = std::collections::BTreeMap::new();
246 b.insert("name".to_string(), c.name);
247 if let Some(n) = c.full_name {
248 b.insert("full_name".to_string(), n);
249 }
250 if let Some(n) = c.email {
251 b.insert("email".to_string(), n);
252 }
253 super::Author(b)
254 }
255}