1use crate::alive::{Graph, VertexId};
2use crate::change::*;
3use crate::pristine::*;
4use crate::{HashMap, HashSet};
5use std::collections::hash_map::Entry;
6
7#[derive(Debug, Error)]
8pub enum MissingError<TxnError: std::error::Error + 'static> {
9 #[error(transparent)]
10 Txn(TxnError),
11 #[error(transparent)]
12 Block(#[from] BlockError<TxnError>),
13 #[error(transparent)]
14 Inconsistent(#[from] InconsistentChange<TxnError>),
15}
16
17impl<T: std::error::Error + 'static> std::convert::From<TxnErr<T>> for MissingError<T> {
18 fn from(e: TxnErr<T>) -> Self {
19 MissingError::Txn(e.0)
20 }
21}
22
23pub type LoadedGraph = (Graph, HashMap<Vertex<ChangeId>, VertexId>);
24impl Workspace {
25 pub fn load_graph<T: GraphTxnT>(
26 &mut self,
27 txn: &T,
28 channel: &T::Graph,
29 inode: Position<Option<Hash>>,
30 ) -> Result<Option<&LoadedGraph>, InconsistentChange<T::GraphError>> {
31 if let Some(change) = inode.change {
32 match self.graphs.0.entry(inode) {
33 Entry::Occupied(e) => Ok(Some(e.into_mut())),
34 Entry::Vacant(v) => {
35 let pos = Position {
36 change: if let Some(&i) = txn.get_internal(&change.into())? {
37 i
38 } else {
39 return Err(InconsistentChange::UndeclaredDep);
40 },
41 pos: inode.pos,
42 };
43 let mut graph = crate::alive::retrieve(txn, channel, pos, false)?;
44 graph.tarjan();
45 let mut ids = HashMap::default();
46 for (i, l) in graph.lines.iter().enumerate() {
47 ids.insert(l.vertex, VertexId(i));
48 }
49 Ok(Some(v.insert((graph, ids))))
50 }
51 }
52 } else {
53 Ok(None)
54 }
55 }
56}
57
58pub(crate) fn has_missing_context_nondeleted<T: GraphMutTxnT>(
59 txn: &T,
60 channel: &T::Graph,
61 change_id: ChangeId,
62 e: &NewEdge<Option<Hash>>,
63) -> Result<bool, MissingError<T::GraphError>> {
64 if e.flag.contains(EdgeFlags::FOLDER) {
65 return Ok(false);
66 }
67 let source = *txn.find_block_end(channel, internal_pos(txn, &e.from, change_id)?)?;
68 let target = *txn.find_block(channel, internal_pos(txn, &e.to.start_pos(), change_id)?)?;
69 debug!(
70 "repair_context_nondeleted source {:?} target {:?} e {:?}",
71 source, target, e
72 );
73 if is_alive(txn, channel, &source)? && e.flag.contains(EdgeFlags::BLOCK) {
74 Ok(iter_adjacent(
75 txn,
76 channel,
77 target,
78 EdgeFlags::DELETED,
79 EdgeFlags::all() - EdgeFlags::PARENT,
80 )?
81 .next()
82 .is_some())
83 } else {
84 Ok(true)
85 }
86}
87
88pub(crate) fn has_missing_context_deleted<T: GraphMutTxnT, K>(
89 txn: &T,
90 channel: &T::Graph,
91 change_id: ChangeId,
92 mut known: K,
93 e: &NewEdge<Option<Hash>>,
94) -> Result<bool, MissingError<T::GraphError>>
95where
96 K: FnMut(Hash) -> bool,
97{
98 if e.flag.contains(EdgeFlags::FOLDER) {
99 return Ok(false);
100 }
101 debug!("repair_context_deleted {:?}", e);
102 let mut pos = internal_pos(txn, &e.to.start_pos(), change_id)?;
103 while let Ok(&dest_vertex) = txn.find_block(channel, pos) {
104 debug!("repair_context_deleted, dest_vertex = {:?}", dest_vertex);
105
106 if has_unknown_children(txn, channel, dest_vertex, change_id, &mut known)? {
107 return Ok(true);
108 }
109
110 if dest_vertex.end < e.to.end {
111 pos.pos = dest_vertex.end
112 } else {
113 break;
114 }
115 }
116 Ok(false)
117}
118
119pub(crate) fn detect_folder_conflict_resolutions<T: GraphMutTxnT>(
127 txn: &mut T,
128 channel: &mut T::Graph,
129 ws: &mut Workspace,
130 change_id: ChangeId,
131 change: &Change,
132) -> Result<(), MissingError<T::GraphError>> {
133 for change_ in change.changes.iter() {
134 for change_ in change_.iter() {
135 if let Atom::EdgeMap(ref n) = *change_ {
136 for edge in n.edges.iter() {
137 if !edge.flag.contains(EdgeFlags::DELETED) {
138 continue;
139 }
140 detect_folder_conflict_resolution(txn, channel, ws, change_id, &n.inode, edge)?
141 }
142 }
143 }
144 }
145 Ok(())
146}
147
148fn detect_folder_conflict_resolution<T: GraphMutTxnT>(
149 txn: &mut T,
150 channel: &mut T::Graph,
151 ws: &mut Workspace,
152 change_id: ChangeId,
153 inode: &Position<Option<Hash>>,
154 e: &NewEdge<Option<Hash>>,
155) -> Result<(), MissingError<T::GraphError>> {
156 let mut stack = vec![if e.flag.contains(EdgeFlags::FOLDER) {
157 if e.to.is_empty() {
158 internal_pos(txn, &e.to.start_pos(), change_id)?
159 } else {
160 internal_pos(txn, &e.from, change_id)?
161 }
162 } else {
163 internal_pos(txn, inode, change_id)?
164 }];
165 let len = ws.pseudo.len();
166 while let Some(pos) = stack.pop() {
167 let dest_vertex = match txn.find_block_end(channel, pos) {
168 Ok(&dest_vertex) => {
169 if !dest_vertex.is_empty() {
170 continue;
171 }
172 dest_vertex
173 }
174 _ => {
175 continue;
176 }
177 };
178 let f0 = EdgeFlags::FOLDER | EdgeFlags::PARENT;
180 let f1 = EdgeFlags::FOLDER | EdgeFlags::PARENT | EdgeFlags::BLOCK;
181 if let Some(e) = iter_adjacent(txn, channel, dest_vertex, f0, f1)?
182 .filter_map(|e| e.ok())
183 .find(|e| !e.flag().contains(EdgeFlags::PSEUDO))
184 {
185 debug!("is_alive: {:?}", e);
186 continue;
187 }
188 let f0 = EdgeFlags::empty();
191 let f1 = EdgeFlags::FOLDER | EdgeFlags::BLOCK | EdgeFlags::PSEUDO;
192 if let Some(e) = iter_adjacent(txn, channel, dest_vertex, f0, f1)?.next() {
193 debug!("child is_alive: {:?}", e);
194 continue;
195 }
196 let f = EdgeFlags::FOLDER | EdgeFlags::PARENT | EdgeFlags::PSEUDO;
199 for e in iter_adjacent(txn, channel, dest_vertex, f, f)? {
200 let e = e?;
201 ws.pseudo.push((dest_vertex, *e));
202 let gr = *txn.find_block_end(channel, e.dest()).unwrap();
203 for e in iter_adjacent(txn, channel, gr, f, f)? {
204 let e = e?;
205 ws.pseudo.push((gr, *e));
206 stack.push(e.dest())
207 }
208 }
209 }
210 for (v, e) in ws.pseudo.drain(len..) {
217 let p = *txn.find_block_end(channel, e.dest())?;
218 debug!(
219 "detect folder conflict resolution, deleting {:?} → {:?} {:?}",
220 v, e, p
221 );
222 del_graph_with_rev(
223 txn,
224 channel,
225 e.flag() - EdgeFlags::PARENT,
226 p,
227 v,
228 e.introduced_by(),
229 )?;
230 }
231 Ok(())
232}
233
234pub type UnknownParent = (
235 Vertex<ChangeId>,
236 Vertex<ChangeId>,
237 Position<Option<Hash>>,
238 EdgeFlags,
239);
240
241type AliveUp = (Option<HashSet<Vertex<ChangeId>>>, HashSet<Vertex<ChangeId>>);
242
243#[derive(Default)]
244pub struct Workspace {
245 pub unknown_parents: Vec<UnknownParent>,
246 unknown: Vec<SerializedEdge>,
247 pub parents: HashSet<SerializedEdge>,
248 pub pseudo: Vec<(Vertex<ChangeId>, SerializedEdge)>,
249 repaired: HashSet<Vertex<ChangeId>>,
250 pub graphs: Graphs,
251 pub covered_parents: HashSet<(Vertex<ChangeId>, Vertex<ChangeId>)>,
252 pub files: HashSet<Vertex<ChangeId>>,
253 alive_down_cache: HashMap<Vertex<ChangeId>, Option<HashSet<Vertex<ChangeId>>>>,
254 alive_up_cache: HashMap<Vertex<ChangeId>, AliveUp>,
255 missing_down: Vec<Vertex<ChangeId>>,
256}
257
258pub type GraphWithCache = (Graph, HashMap<Vertex<ChangeId>, crate::alive::VertexId>);
259
260#[derive(Debug, Default)]
261pub struct Graphs(pub HashMap<Position<Option<Hash>>, GraphWithCache>);
262
263impl Graphs {
264 pub(crate) fn get(&self, inode: Position<Option<Hash>>) -> Option<&GraphWithCache> {
265 self.0.get(&inode)
266 }
267
268 pub fn split(
269 &mut self,
270 inode: Position<Option<Hash>>,
271 vertex: Vertex<ChangeId>,
272 mid: ChangePosition,
273 ) {
274 if let Some((_, vids)) = self.0.get_mut(&inode)
275 && let Some(vid) = vids.remove(&vertex)
276 {
277 vids.insert(Vertex { end: mid, ..vertex }, vid);
278 vids.insert(
279 Vertex {
280 start: mid,
281 ..vertex
282 },
283 vid,
284 );
285 }
286 }
287}
288
289impl Workspace {
290 pub fn clear(&mut self) {
291 self.unknown.clear();
292 self.unknown_parents.clear();
293 self.pseudo.clear();
294 self.parents.clear();
295 self.graphs.0.clear();
296 self.repaired.clear();
297 self.covered_parents.clear();
298 self.alive_up_cache.clear();
299 self.alive_down_cache.clear();
300 self.missing_down.clear();
301 }
302 pub fn assert_empty(&self) {
303 assert!(self.unknown.is_empty());
304 assert!(self.unknown_parents.is_empty());
305 assert!(self.pseudo.is_empty());
306 assert!(self.parents.is_empty());
307 assert!(self.graphs.0.is_empty());
308 assert!(self.repaired.is_empty());
309 assert!(self.covered_parents.is_empty());
310 assert!(self.alive_up_cache.is_empty());
311 assert!(self.alive_down_cache.is_empty());
312 assert!(self.missing_down.is_empty());
313 }
314}
315
316fn has_unknown_children<T: GraphTxnT, K>(
317 txn: &T,
318 channel: &T::Graph,
319 dest_vertex: Vertex<ChangeId>,
320 change_id: ChangeId,
321 known: &mut K,
322) -> Result<bool, TxnErr<T::GraphError>>
323where
324 K: FnMut(Hash) -> bool,
325{
326 for v in iter_alive_children(txn, channel, dest_vertex)? {
327 let v = v?;
328 debug!(
329 "collect_unknown_children dest_vertex = {:?}, v = {:?}",
330 dest_vertex, v
331 );
332 if v.introduced_by() == change_id || v.dest().change.is_root() {
333 continue;
334 }
335 if v.introduced_by().is_root() {
336 continue;
337 }
338 let mut not_del_by_change = true;
339 for e in iter_adjacent(
340 txn,
341 channel,
342 dest_vertex,
343 EdgeFlags::PARENT | EdgeFlags::DELETED,
344 EdgeFlags::all(),
345 )? {
346 let e = e?;
347 if e.introduced_by() == v.introduced_by() {
348 not_del_by_change = false;
349 break;
350 }
351 }
352 if not_del_by_change {
353 let intro = txn
354 .get_external(&v.introduced_by())
355 .optional()?
356 .unwrap()
357 .into();
358 if !known(intro) {
359 return Ok(true);
360 }
361 }
362 }
363 Ok(false)
364}