1use crate::HashMap;
2use std::collections::hash_map::Entry;
3use std::io::BufRead;
4
5use super::*;
6use crate::change::parse::*;
7use crate::change::printable::*;
8use crate::changestore::*;
9
10#[derive(Debug, Error)]
11pub enum TextDeError {
12 #[error(transparent)]
13 Io(#[from] std::io::Error),
14 #[error(transparent)]
15 TomlDe(#[from] toml::de::Error),
16 #[error(transparent)]
17 Nom(#[from] nom::Err<nom::error::Error<String>>),
18 #[error("Missing dependency [{0}]")]
19 MissingChange(usize),
20 #[error("Byte position {0} from this change missing")]
21 MissingPosition(u64),
22}
23
24#[derive(Debug, Error)]
25pub enum TextDeErrorDeps<T: GraphTxnT> {
26 #[error(transparent)]
27 TextDe(#[from] TextDeError),
28 #[error(transparent)]
29 MakeChange(#[from] MakeChangeError<T>),
30}
31
32#[derive(Debug, Error)]
33pub enum TextSerError<C: std::error::Error + 'static> {
34 #[error(transparent)]
35 C(C),
36 #[error(transparent)]
37 Io(#[from] std::io::Error),
38 #[error(transparent)]
39 TomlSer(#[from] toml::ser::Error),
40 #[error("Missing contents in change {:?}", h)]
41 MissingContents { h: Hash },
42 #[error(transparent)]
43 Change(#[from] ChangeError),
44 #[error("Invalid change")]
45 InvalidChange,
46}
47
48impl LocalChange<Hunk<Option<Hash>, Local>, Author> {
49 const DEPS_LINE: &'static str = "# Dependencies\n";
50 const HUNKS_LINE: &'static str = "# Hunks\n";
51
52 pub fn write_all_deps_old<F: FnMut(Hash) -> Result<(), ChangeError>>(
53 &self,
54 mut f: F,
55 ) -> Result<(), ChangeError> {
56 for c in self.changes.iter() {
57 for c in c.iter() {
58 match *c {
59 Atom::NewVertex(ref n) => {
60 for change in n
61 .up_context
62 .iter()
63 .chain(n.down_context.iter())
64 .map(|c| c.change)
65 .chain(std::iter::once(n.inode.change))
66 .flatten()
67 {
68 if let Hash::None = change {
69 continue;
70 }
71 f(change)?
72 }
73 }
74 Atom::EdgeMap(ref e) => {
75 for edge in e.edges.iter() {
76 for change in &[
77 edge.from.change,
78 edge.to.change,
79 edge.introduced_by,
80 e.inode.change,
81 ] {
82 if let Some(change) = *change {
83 if let Hash::None = change {
84 continue;
85 }
86 f(change)?
87 }
88 }
89 }
90 }
91 }
92 }
93 }
94 Ok(())
95 }
96
97 pub fn write<W: WriteChangeLine, C: ChangeStore>(
98 &self,
99 changes: &C,
100 hash: Option<Hash>,
101 write_header: bool,
102 mut w: W,
103 ) -> Result<(), TextSerError<C::Error>> {
104 fn change_message<C: ChangeStore>(changes: &C, change: &Hash) -> String {
105 match changes.get_header(change) {
106 Ok(h) => h.message.lines().next().unwrap_or("").to_string(),
107 Err(e) => format!("[couldn't get change description: {}]", e),
108 }
109 }
110
111 if let Some(h) = hash {
112 let mut hasher = Hasher::default();
114 hasher.update(&self.contents);
115 let hash = hasher.finish();
116 if hash != self.contents_hash {
117 return Err(TextSerError::MissingContents { h });
118 }
119 }
120
121 if write_header {
122 let s = toml::ser::to_string_pretty(&self.header)?;
123 writeln!(w, "{}", s)?;
124 }
125 let mut hashes = HashMap::default();
126 let mut i = 2;
127 let mut needs_newline = false;
128 if !self.dependencies.is_empty() {
129 w.write_all(Self::DEPS_LINE.as_bytes())?;
130 needs_newline = true;
131 for dep in self.dependencies.iter() {
132 hashes.insert(*dep, i);
133 writeln!(
134 w,
135 "[{}] {} # {}",
136 i,
137 dep.to_base32(),
138 change_message(changes, dep)
139 )?;
140 i += 1;
141 }
142 }
143
144 self.write_all_deps(|change| {
145 if let Entry::Vacant(e) = hashes.entry(change) {
146 e.insert(i);
147 if !needs_newline {
148 w.write_all(Self::DEPS_LINE.as_bytes())?;
149 needs_newline = true;
150 }
151 writeln!(
152 w,
153 "[{}]+{} # {}",
154 i,
155 change.to_base32(),
156 change_message(changes, &change)
157 )?;
158 i += 1;
159 }
160 Ok(())
161 })?;
162
163 if !self.extra_known.is_empty() {
164 needs_newline = true;
165 for dep in self.extra_known.iter() {
166 writeln!(
167 w,
168 "[*] {} # {}",
169 dep.to_base32(),
170 change_message(changes, dep)
171 )?;
172 #[allow(unused)]
173 {
174 i += 1;
175 }
176 }
177 }
178
179 if !self.changes.is_empty() {
180 if needs_newline {
181 w.write_all(b"\n")?
182 }
183 w.write_all(Self::HUNKS_LINE.as_bytes())?;
184 for (n, rec) in self.changes.iter().enumerate() {
185 write!(w, "\n{}. ", n + 1)?;
186 rec.write(changes, &hashes, &self.contents, &mut w)?
187 }
188 }
189 Ok(())
190 }
191}
192
193impl Change {
194 pub fn read_and_deps<
195 R: BufRead,
196 T: ChannelTxnT + DepsTxnT<DepsError = <T as GraphTxnT>::GraphError>,
197 >(
198 r: R,
199 updatables: &mut HashMap<usize, crate::InodeUpdate>,
200 txn: &T,
201 channel: &ChannelRef<T>,
202 ) -> Result<Self, TextDeErrorDeps<T>> {
203 let (mut change, extra_dependencies) = Self::read_impl(r, updatables)?;
204 let (mut deps, extra) = dependencies(txn, &channel.read(), change.hashed.changes.iter())?;
205 deps.extend(extra_dependencies);
206 change.hashed.dependencies = deps;
207 change.hashed.extra_known = extra;
208 Ok(change)
209 }
210
211 pub fn read<R: BufRead>(
212 r: R,
213 updatables: &mut HashMap<usize, crate::InodeUpdate>,
214 ) -> Result<Self, TextDeError> {
215 Ok(Self::read_impl(r, updatables)?.0)
216 }
217
218 fn read_impl<R: BufRead>(
219 mut r: R,
220 updatables: &mut HashMap<usize, crate::InodeUpdate>,
221 ) -> Result<(Self, HashSet<Hash>), TextDeError> {
222 let mut s = String::new();
225 r.read_to_string(&mut s)?;
226 let i = &s;
227
228 let (i, m_header) = parse_header(i).map_err(|e| e.to_owned())?;
230 let header = m_header?;
231
232 let (i, deps) = parse_dependencies(i).map_err(|e| e.to_owned())?;
234
235 let (_, hunks) = parse_hunks(i).map_err(|e| e.to_owned())?;
237
238 Change::update(header, deps, hunks, updatables)
239 }
240
241 fn update(
242 header: ChangeHeader,
243 dependencies: Vec<PrintableDep>,
244 hunks: Vec<(u64, PrintableHunk)>,
245 updatables: &mut HashMap<usize, crate::InodeUpdate>,
246 ) -> Result<(Self, HashSet<Hash>), TextDeError> {
247 let mut change = Change {
249 offsets: Offsets::default(),
250 hashed: Hashed {
251 version: VERSION,
252 header,
253 dependencies: Vec::new(),
254 extra_known: Vec::new(),
255 metadata: Vec::new(),
256 changes: Vec::new(),
257 contents_hash: Hasher::default().finish(),
258 },
259 unhashed: None,
260 contents: Vec::new(),
261 };
262
263 let mut deps = HashMap::default();
265 let mut extra_dependencies = HashSet::default();
266 for dep in dependencies {
267 debug!("dep hash {:?}", dep.hash);
268 let hash = Hash::from_base32(dep.hash.as_bytes()).unwrap();
269 match dep.type_ {
270 DepType::Numbered(n, false) => {
271 change.hashed.dependencies.push(hash);
272 deps.insert(n, hash);
273 }
274 DepType::Numbered(n, true) => {
275 deps.insert(n, hash);
276 }
277 DepType::ExtraKnown => {
278 change.hashed.extra_known.push(hash);
279 }
280 DepType::ExtraUnknown => {
281 extra_dependencies.insert(hash);
282 }
283 }
284 }
285
286 let mut contents = Vec::new();
288 let mut offsets = HashMap::default();
289 for (n, hunk) in hunks {
290 let res =
291 Hunk::from_printable(updatables, &mut contents, &deps, &mut offsets, (n, hunk))?;
292 debug!("res = {:?}", res);
293 change.hashed.changes.push(res);
294 }
295 change.contents = contents;
296 change.contents_hash = {
297 let mut hasher = Hasher::default();
298 hasher.update(&change.contents);
299 hasher.finish()
300 };
301 Ok((change, extra_dependencies))
302 }
303}
304
305pub fn to_printable_new_vertex(
306 atom: &Atom<Option<Hash>>,
307 hashes: &HashMap<Hash, usize>,
308) -> PrintableNewVertex {
309 if let PrintableAtom::NewVertex(v) = to_printable_atom(atom, hashes) {
310 v
311 } else {
312 panic!("PrintableAtom::NewVertex expected here")
313 }
314}
315
316pub fn to_printable_edge_map(
317 atom: &Atom<Option<Hash>>,
318 hashes: &HashMap<Hash, usize>,
319) -> Vec<PrintableEdge> {
320 if let PrintableAtom::Edges(v) = to_printable_atom(atom, hashes) {
321 v
322 } else {
323 panic!("PrintableAtom::Edges expected here")
324 }
325}
326
327fn to_printable_atom(atom: &Atom<Option<Hash>>, hashes: &HashMap<Hash, usize>) -> PrintableAtom {
329 match atom {
330 Atom::NewVertex(new_vertex) => PrintableAtom::NewVertex(PrintableNewVertex {
331 up_context: new_vertex
332 .up_context
333 .iter()
334 .map(|c| to_printable_pos(hashes, *c))
335 .collect(),
336 start: new_vertex.start.0.as_u64(),
337 end: new_vertex.end.0.as_u64(),
338 down_context: new_vertex
339 .down_context
340 .iter()
341 .map(|c| to_printable_pos(hashes, *c))
342 .collect(),
343 }),
344 Atom::EdgeMap(edge_map) => PrintableAtom::Edges(
345 edge_map
346 .edges
347 .iter()
348 .map(|c| PrintableEdge {
349 previous: PrintableEdgeFlags::from(c.previous),
350 flag: PrintableEdgeFlags::from(c.flag),
351 from: to_printable_pos(hashes, c.from),
352 to_start: to_printable_pos(hashes, c.to.start_pos()),
353 to_end: c.to.end.0.as_u64(),
354 introduced_by: *hashes.get(&c.introduced_by.unwrap()).unwrap_or_else(|| {
355 panic!("introduced_by = {:?}, not found", c.introduced_by)
356 }),
357 })
358 .collect(),
359 ),
360 }
361}
362
363fn from_printable_edge_map(
364 edges: &[PrintableEdge],
365 changes: &HashMap<usize, Hash>,
366) -> Result<Vec<NewEdge<Option<Hash>>>, TextDeError> {
367 let mut res = Vec::new();
368 for edge in edges {
369 let Position { change, pos } = from_printable_pos(changes, edge.to_start)?;
370 res.push(NewEdge {
371 previous: edge.previous.to(),
372 flag: edge.flag.to(),
373 from: from_printable_pos(changes, edge.from)?,
374 to: Vertex {
375 change,
376 start: pos,
377 end: ChangePosition(L64(edge.to_end.to_le())),
378 },
379 introduced_by: change_ref(changes, edge.introduced_by)?,
380 })
381 }
382 Ok(res)
383}
384
385impl Hunk<Option<Hash>, Local> {
386 fn write<W: WriteChangeLine, C: ChangeStore>(
387 &self,
388 changes: &C,
389 hashes: &HashMap<Hash, usize>,
390 change_contents: &[u8],
391 w: &mut W,
392 ) -> Result<(), TextSerError<C::Error>> {
393 use self::text_changes::*;
394 debug!("write {:?}", self);
395 match self {
396 Hunk::FileMove { del, add, path } => match add {
397 Atom::NewVertex(add) => {
398 let FileMetadata {
399 basename: name,
400 metadata,
401 ..
402 } = FileMetadata::read(&change_contents[add.start.0.into()..add.end.0.into()]);
403 PrintableHunk::FileMoveV {
404 path: path.to_string(),
405 name: name.to_string(),
406 perms: PrintablePerms::from_metadata(metadata),
407 pos: to_printable_pos(hashes, del.inode()),
408 up_context: to_printable_pos_vec(hashes, &add.up_context),
409 down_context: to_printable_pos_vec(hashes, &add.down_context),
410 del: to_printable_edge_map(del, hashes),
411 }
412 }
413 Atom::EdgeMap(_) => PrintableHunk::FileMoveE {
414 path: path.to_string(),
415 pos: to_printable_pos(hashes, del.inode()),
416 add: to_printable_edge_map(add, hashes),
417 del: to_printable_edge_map(del, hashes),
418 },
419 },
420 Hunk::FileDel {
421 del,
422 contents,
423 path,
424 encoding,
425 } => {
426 debug!("file del");
427 let (contents_data, content_edges) = if let Some(c) = contents {
428 (
429 get_change_contents(changes, c, change_contents)?,
430 to_printable_edge_map(c, hashes),
431 )
432 } else {
433 (Vec::new(), Vec::new())
434 };
435
436 PrintableHunk::FileDel {
437 path: path.to_string(),
438 pos: to_printable_pos(hashes, del.inode()),
439 encoding: encoding.clone(),
440 del_edges: to_printable_edge_map(del, hashes),
441 content_edges,
442 contents: contents_data,
443 }
444 }
445 Hunk::FileUndel {
446 undel,
447 contents,
448 path,
449 encoding,
450 } => {
451 debug!("file undel");
452 let (contents_data, content_edges) = if let Some(c) = contents {
453 (
454 get_change_contents(changes, c, change_contents)?,
455 to_printable_edge_map(c, hashes),
456 )
457 } else {
458 (Vec::new(), Vec::new())
459 };
460
461 PrintableHunk::FileUndel {
462 path: path.to_string(),
463 pos: to_printable_pos(hashes, undel.inode()),
464 encoding: encoding.clone(),
465 undel_edges: to_printable_edge_map(undel, hashes),
466 content_edges,
467 contents: contents_data,
468 }
469 }
470 Hunk::FileAdd {
471 add_name,
472 contents,
473 path,
474 encoding,
475 ..
476 } => {
477 if let Atom::NewVertex(n) = add_name {
478 debug!("add_name {:?}", n);
479 let (name, metadata) = if n.start == n.end {
480 ("", InodeMetadata::DIR)
481 } else {
482 let FileMetadata {
483 basename: name,
484 metadata: perms,
485 ..
486 } = FileMetadata::read(&change_contents[n.start.0.into()..n.end.0.into()]);
487 (name, perms)
488 };
489
490 let contents = if let Some(Atom::NewVertex(n)) = contents {
491 change_contents[n.start.us()..n.end.us()].to_vec()
492 } else {
493 Vec::new()
494 };
495 assert!(n.down_context.is_empty());
496
497 PrintableHunk::FileAddition {
498 name: name.to_string(),
499 parent: crate::path::parent(path).unwrap_or("").to_string(),
500 perms: PrintablePerms::from_metadata(metadata),
501 encoding: encoding.clone(),
502 up_context: to_printable_pos_vec(hashes, &n.up_context),
503 start: n.start.0.as_u64(),
504 end: n.end.0.as_u64(),
505 contents,
506 }
507 } else {
508 panic!("Invalid Hunk::FileAdd field add_name: {:?}", add_name);
509 }
510 }
511 Hunk::Edit {
512 change,
513 local,
514 encoding,
515 } => {
516 debug!("edit");
517 PrintableHunk::Edit {
518 path: local.path.clone(),
519 line: local.line,
520 pos: to_printable_pos(hashes, change.inode()),
521 encoding: encoding.clone(),
522 change: to_printable_atom(change, hashes),
523 contents: get_change_contents(changes, change, change_contents)?,
524 }
525 }
526 Hunk::Replacement {
527 change,
528 replacement,
529 local,
530 encoding,
531 } => {
532 debug!("replacement");
533 PrintableHunk::Replace {
534 path: local.path.clone(),
535 line: local.line,
536 pos: to_printable_pos(hashes, change.inode()),
537 encoding: encoding.clone(),
538 change: to_printable_edge_map(change, hashes),
539 replacement: to_printable_new_vertex(replacement, hashes),
540 change_contents: get_change_contents(changes, change, change_contents)?,
541 replacement_contents: get_change_contents(
542 changes,
543 replacement,
544 change_contents,
545 )?,
546 }
547 }
548 Hunk::SolveNameConflict { name, path } => PrintableHunk::SolveNameConflict {
549 path: path.clone(),
550 pos: to_printable_pos(hashes, name.inode()),
551 names: get_deleted_names(changes, name)?,
552 edges: to_printable_edge_map(name, hashes),
553 },
554 Hunk::UnsolveNameConflict { name, path } => PrintableHunk::UnsolveNameConflict {
555 path: path.clone(),
556 pos: to_printable_pos(hashes, name.inode()),
557 names: get_deleted_names(changes, name)?,
558 edges: to_printable_edge_map(name, hashes),
559 },
560 Hunk::SolveOrderConflict { change, local } => {
561 let contents = get_change_contents(changes, change, change_contents)?;
563 let encoding = get_encoding(&contents);
564 PrintableHunk::SolveOrderConflict {
565 path: local.path.clone(),
566 line: local.line,
567 pos: to_printable_pos(hashes, change.inode()),
568 encoding: encoding.clone(),
569 change: to_printable_new_vertex(change, hashes),
570 contents: get_change_contents(changes, change, change_contents)?,
571 }
572 }
573 Hunk::UnsolveOrderConflict { change, local } => {
574 let contents = get_change_contents(changes, change, change_contents)?;
576 let encoding = get_encoding(&contents);
577 PrintableHunk::UnsolveOrderConflict {
578 path: local.path.clone(),
579 line: local.line,
580 pos: to_printable_pos(hashes, change.inode()),
581 encoding: encoding.clone(),
582 change: to_printable_edge_map(change, hashes),
583 contents: get_change_contents(changes, change, change_contents)?,
584 }
585 }
586 Hunk::ResurrectZombies {
587 change,
588 local,
589 encoding,
590 } => PrintableHunk::ResurrectZombies {
591 path: local.path.clone(),
592 line: local.line,
593 pos: to_printable_pos(hashes, change.inode()),
594 encoding: encoding.clone(),
595 change: to_printable_edge_map(change, hashes),
596 contents: get_change_contents(changes, change, change_contents)?,
597 },
598 Hunk::AddRoot { name, .. } => {
599 if let Atom::NewVertex(n) = name {
600 PrintableHunk::AddRoot {
601 start: n.start.0.as_u64(),
602 }
603 } else {
604 unreachable!()
605 }
606 }
607 Hunk::DelRoot { inode, name } => PrintableHunk::DelRoot {
608 name: to_printable_edge_map(name, hashes),
609 inode: to_printable_edge_map(inode, hashes),
610 },
611 }
612 .write(w)?;
613 Ok(())
614 }
615}
616
617impl Hunk<Option<Hash>, Local> {
618 fn from_printable(
619 updatables: &mut HashMap<usize, crate::InodeUpdate>,
620 contents_: &mut Vec<u8>,
621 changes: &HashMap<usize, Hash>,
622 offsets: &mut HashMap<u64, ChangePosition>,
623 (hunk_id, hunk): (u64, PrintableHunk),
624 ) -> Result<Self, TextDeError> {
625 debug!("from_printable {:?}", hunk);
626 match hunk {
627 PrintableHunk::FileMoveV {
628 path,
629 name,
630 perms,
631 pos,
632 up_context,
633 down_context,
634 del,
635 } => {
636 let mut add = default_newvertex();
637 add.start = ChangePosition(contents_.len().into());
638 add.flag = EdgeFlags::FOLDER | EdgeFlags::BLOCK;
639 let meta = FileMetadata {
640 metadata: InodeMetadata(match perms {
641 PrintablePerms::IsDir => 0o1100,
643 PrintablePerms::IsExecutable => 0o100,
644 PrintablePerms::IsFile => 0,
645 }),
646 basename: &name,
647 encoding: None,
648 };
649 meta.write(contents_);
650 add.end = ChangePosition(contents_.len().into());
651 add.up_context = from_printable_pos_vec_offsets(changes, offsets, &up_context)?;
652 add.down_context = from_printable_pos_vec_offsets(changes, offsets, &down_context)?;
653 contents_.push(0);
654
655 Ok(Hunk::FileMove {
656 add: Atom::NewVertex(add),
657 del: Atom::EdgeMap(EdgeMap {
658 inode: from_printable_pos(changes, pos)?,
659 edges: from_printable_edge_map(&del, changes)?,
660 }),
661 path,
662 })
663 }
664 PrintableHunk::FileMoveE {
665 path,
666 pos,
667 add,
668 del,
669 } => {
670 let inode = from_printable_pos(changes, pos)?;
671 Ok(Hunk::FileMove {
672 add: Atom::EdgeMap(EdgeMap {
673 inode,
674 edges: from_printable_edge_map(&add, changes)?,
675 }),
676 del: Atom::EdgeMap(EdgeMap {
677 inode,
678 edges: from_printable_edge_map(&del, changes)?,
679 }),
680 path,
681 })
682 }
683 PrintableHunk::FileAddition {
684 name,
685 parent,
686 perms,
687 encoding,
688 up_context,
689 start,
690 end,
691 contents,
692 } => {
693 let meta = FileMetadata {
694 metadata: InodeMetadata(match perms {
695 PrintablePerms::IsDir => 0o1100,
696 PrintablePerms::IsExecutable => 0o100,
697 PrintablePerms::IsFile => 0,
698 }),
699 basename: &name,
700 encoding: encoding.clone(),
701 };
702
703 let mut add_name = {
704 let mut x = default_newvertex();
705 x.start = ChangePosition(contents_.len().into());
706 meta.write(contents_);
707 x.end = ChangePosition(contents_.len().into());
708 x.flag = EdgeFlags::FOLDER | EdgeFlags::BLOCK;
709 x
710 };
711
712 let add_inode = {
713 let mut x = default_newvertex();
714 x.flag = EdgeFlags::FOLDER | EdgeFlags::BLOCK;
715 x.up_context.push(Position {
716 change: None,
717 pos: ChangePosition(contents_.len().into()),
718 });
719
720 contents_.push(0);
721 x.start = ChangePosition(contents_.len().into());
722 x.end = ChangePosition(contents_.len().into());
723 contents_.push(0);
724 x
725 };
726
727 if let Entry::Occupied(mut e) = updatables.entry(hunk_id as usize)
728 && let crate::InodeUpdate::Add { pos, .. } = e.get_mut()
729 {
730 offsets.insert(pos.0.into(), add_inode.start);
731 *pos = add_inode.start
732 }
733
734 add_name.up_context =
736 from_printable_pos_vec_offsets(changes, offsets, &up_context)?;
737 offsets.insert(start, add_name.start);
738 offsets.insert(end, add_name.end);
739 offsets.insert(end + 1, add_name.end + 1);
740
741 let contents = if !contents.is_empty() {
743 let mut x = default_newvertex();
744 let inode = Position {
748 change: None,
749 pos: ChangePosition((contents_.len() - 1).into()),
750 };
751 x.up_context.push(inode);
752 x.inode = inode;
753 x.flag = EdgeFlags::BLOCK;
754 x.start = ChangePosition(contents_.len().into());
755 contents_.extend(&contents);
756 x.end = ChangePosition(contents_.len().into());
757 Some(Atom::NewVertex(x))
758 } else {
759 None
760 };
761 contents_.push(0);
762
763 Ok(Hunk::FileAdd {
764 add_name: Atom::NewVertex(add_name),
765 add_inode: Atom::NewVertex(add_inode),
766 contents,
767 path: if parent.is_empty() {
768 name
769 } else {
770 parent + "/" + &name
771 },
772 encoding,
773 })
774 }
775 PrintableHunk::FileDel {
776 path,
777 pos,
778 encoding,
779 del_edges,
780 content_edges,
781 contents: _,
782 } => Ok(Hunk::FileDel {
783 del: Atom::EdgeMap(EdgeMap {
784 edges: from_printable_edge_map(&del_edges, changes)?,
785 inode: from_printable_pos(changes, pos)?,
786 }),
787 contents: if content_edges.is_empty() {
788 None
789 } else {
790 Some(Atom::EdgeMap(EdgeMap {
791 edges: from_printable_edge_map(&content_edges, changes)?,
792 inode: from_printable_pos(changes, pos)?,
793 }))
794 },
795 path,
796 encoding,
797 }),
798 PrintableHunk::FileUndel {
799 path,
800 pos,
801 encoding,
802 undel_edges,
803 content_edges,
804 contents: _,
805 } => Ok(Hunk::FileUndel {
806 undel: Atom::EdgeMap(EdgeMap {
807 edges: from_printable_edge_map(&undel_edges, changes)?,
808 inode: from_printable_pos(changes, pos)?,
809 }),
810 contents: if content_edges.is_empty() {
811 None
812 } else {
813 Some(Atom::EdgeMap(EdgeMap {
814 edges: from_printable_edge_map(&content_edges, changes)?,
815 inode: from_printable_pos(changes, pos)?,
816 }))
817 },
818 path,
819 encoding,
820 }),
821 PrintableHunk::Edit {
822 path,
823 line,
824 pos,
825 encoding,
826 change,
827 contents,
828 } => {
829 let inode = from_printable_pos(changes, pos)?;
830 let change = match change {
831 PrintableAtom::NewVertex(new_vertex) => {
832 assert!(!contents.is_empty());
833 let mut x = default_newvertex();
834 x.inode = inode;
835 x.flag = EdgeFlags::BLOCK;
836 x.up_context = from_printable_pos_vec_offsets(
837 changes,
838 offsets,
839 &new_vertex.up_context,
840 )?;
841 x.down_context = from_printable_pos_vec_offsets(
842 changes,
843 offsets,
844 &new_vertex.down_context,
845 )?;
846 x.start = ChangePosition(contents_.len().into());
847 contents_.extend(&contents);
848 x.end = ChangePosition(contents_.len().into());
849 contents_.push(0);
850 Atom::NewVertex(x)
851 }
852 PrintableAtom::Edges(edges) => Atom::EdgeMap(EdgeMap {
853 edges: from_printable_edge_map(&edges, changes)?,
854 inode,
855 }),
856 };
857
858 Ok(Hunk::Edit {
859 change,
860 local: Local { path, line },
861 encoding,
862 })
863 }
864 PrintableHunk::Replace {
865 path,
866 line,
867 pos,
868 encoding,
869 change,
870 replacement,
871 change_contents: _,
872 replacement_contents,
873 } => {
874 let inode = from_printable_pos(changes, pos)?;
875
876 let replacement = {
877 let mut x = default_newvertex();
878 x.inode = inode;
879 x.flag = EdgeFlags::BLOCK;
880 x.up_context =
881 from_printable_pos_vec_offsets(changes, offsets, &replacement.up_context)?;
882 x.down_context = from_printable_pos_vec_offsets(
883 changes,
884 offsets,
885 &replacement.down_context,
886 )?;
887 x.start = ChangePosition(contents_.len().into());
888 contents_.extend(&replacement_contents);
889 x.end = ChangePosition(contents_.len().into());
890 Atom::NewVertex(x)
891 };
892 contents_.push(0);
893
894 Ok(Hunk::Replacement {
895 change: Atom::EdgeMap(EdgeMap {
896 edges: from_printable_edge_map(&change, changes)?,
897 inode,
898 }),
899 replacement,
900 local: Local { path, line },
901 encoding,
902 })
903 }
904 PrintableHunk::SolveNameConflict {
905 path,
906 pos,
907 names: _,
908 edges,
909 } => Ok(Hunk::SolveNameConflict {
910 name: Atom::EdgeMap(EdgeMap {
911 inode: from_printable_pos(changes, pos)?,
912 edges: from_printable_edge_map(&edges, changes)?,
913 }),
914 path,
915 }),
916 PrintableHunk::UnsolveNameConflict {
917 path,
918 pos,
919 names: _,
920 edges,
921 } => Ok(Hunk::UnsolveNameConflict {
922 name: Atom::EdgeMap(EdgeMap {
923 inode: from_printable_pos(changes, pos)?,
924 edges: from_printable_edge_map(&edges, changes)?,
925 }),
926 path,
927 }),
928 PrintableHunk::SolveOrderConflict {
929 path,
930 line,
931 pos,
932 encoding: _,
933 change,
934 contents,
935 } => {
936 let mut c = default_newvertex();
939 c.inode = from_printable_pos(changes, pos)?;
940 c.up_context =
941 from_printable_pos_vec_offsets(changes, offsets, &change.up_context)?;
942 c.down_context =
943 from_printable_pos_vec_offsets(changes, offsets, &change.down_context)?;
944 c.start = ChangePosition(contents_.len().into());
945 c.end = ChangePosition((contents_.len() as u64 + change.end - change.start).into());
946 offsets.insert(change.end, c.end);
947 c.start = ChangePosition(contents_.len().into());
948 contents_.extend(&contents);
949 c.end = ChangePosition(contents_.len().into());
950 contents_.push(0);
951
952 Ok(Hunk::SolveOrderConflict {
953 change: Atom::NewVertex(c),
954 local: Local { path, line },
955 })
956 }
957 PrintableHunk::UnsolveOrderConflict {
958 path,
959 line,
960 pos,
961 encoding: _,
962 change,
963 contents: _,
964 } => Ok(Hunk::UnsolveOrderConflict {
965 change: Atom::EdgeMap(EdgeMap {
966 edges: from_printable_edge_map(&change, changes)?,
967 inode: from_printable_pos(changes, pos)?,
968 }),
969 local: Local { path, line },
970 }),
971 PrintableHunk::ResurrectZombies {
972 path,
973 line,
974 pos,
975 encoding,
976 change,
977 contents: _,
978 } => Ok(Hunk::ResurrectZombies {
979 change: Atom::EdgeMap(EdgeMap {
980 edges: from_printable_edge_map(&change, changes)?,
981 inode: from_printable_pos(changes, pos)?,
982 }),
983 local: Local { path, line },
984 encoding,
985 }),
986 PrintableHunk::AddRoot { start } => {
987 contents_.push(0);
988 let root_inode = Position {
989 change: Some(Hash::None),
990 pos: ChangePosition(contents_.len().into()),
991 };
992 contents_.push(0);
993 let inode = contents_.len();
994 contents_.push(0);
995 if let Entry::Occupied(mut e) = updatables.entry(hunk_id as usize)
996 && let crate::InodeUpdate::Add { pos, .. } = e.get_mut()
997 {
998 offsets.insert(pos.0.into(), ChangePosition((start + 1).into()));
999 *pos = ChangePosition((start + 1).into())
1000 }
1001 Ok(Hunk::AddRoot {
1002 name: Atom::NewVertex(NewVertex {
1003 up_context: vec![root_inode],
1004 down_context: Vec::new(),
1005 start: ChangePosition(start.into()),
1006 end: ChangePosition(start.into()),
1007 flag: EdgeFlags::FOLDER | EdgeFlags::BLOCK,
1008 inode: root_inode,
1009 }),
1010 inode: Atom::NewVertex(NewVertex {
1011 up_context: vec![Position {
1012 change: None,
1013 pos: ChangePosition(start.into()),
1014 }],
1015 down_context: Vec::new(),
1016 start: ChangePosition(inode.into()),
1017 end: ChangePosition(inode.into()),
1018 flag: EdgeFlags::FOLDER | EdgeFlags::BLOCK,
1019 inode: root_inode,
1020 }),
1021 })
1022 }
1023 PrintableHunk::DelRoot { name, inode } => {
1024 let root_inode = PrintablePos(1, 0);
1025 Ok(Hunk::DelRoot {
1026 name: Atom::EdgeMap(EdgeMap {
1027 edges: from_printable_edge_map(&name, changes)?,
1028 inode: from_printable_pos(changes, root_inode)?,
1029 }),
1030 inode: Atom::EdgeMap(EdgeMap {
1031 edges: from_printable_edge_map(&inode, changes)?,
1032 inode: from_printable_pos(changes, root_inode)?,
1033 }),
1034 })
1035 }
1036 }
1037 }
1038}
1039
1040pub fn default_newvertex() -> NewVertex<Option<Hash>> {
1041 NewVertex {
1042 start: ChangePosition(L64(0)),
1043 end: ChangePosition(L64(0)),
1044 flag: EdgeFlags::empty(),
1045 up_context: Vec::new(),
1046 down_context: Vec::new(),
1047 inode: Position {
1048 change: Some(Hash::None),
1049 pos: ChangePosition(L64(0)),
1050 },
1051 }
1052}
1053
1054pub fn from_printable_pos_vec_offsets(
1056 changes: &HashMap<usize, Hash>,
1057 offsets: &HashMap<u64, ChangePosition>,
1058 s: &[PrintablePos],
1059) -> Result<Vec<Position<Option<Hash>>>, TextDeError> {
1060 let mut v = Vec::new();
1061 for PrintablePos(change, pos) in s {
1062 let pos = if *change == 0 {
1063 if let Some(&pos) = offsets.get(pos) {
1064 pos
1065 } else {
1066 debug!("inconsistent change: {:?} {:?}", s, offsets);
1067 return Err(TextDeError::MissingPosition(*pos));
1068 }
1069 } else {
1070 ChangePosition(L64(pos.to_le()))
1071 };
1072 v.push(Position {
1073 change: change_ref(changes, *change)?,
1074 pos,
1075 })
1076 }
1077 Ok(v)
1078}
1079
1080fn change_ref(changes: &HashMap<usize, Hash>, change: usize) -> Result<Option<Hash>, TextDeError> {
1081 debug!("change_ref {:?} {:?}", changes, change);
1082 if change == 0 {
1083 Ok(None)
1084 } else if change == 1 {
1085 Ok(Some(Hash::None))
1086 } else if let Some(&c) = changes.get(&change) {
1087 Ok(Some(c))
1088 } else {
1089 Err(TextDeError::MissingChange(change))
1090 }
1091}
1092
1093pub fn from_printable_pos(
1094 changes: &HashMap<usize, Hash>,
1095 pos: PrintablePos,
1096) -> Result<Position<Option<Hash>>, TextDeError> {
1097 Ok(Position {
1098 change: change_ref(changes, pos.0)?,
1099 pos: ChangePosition(L64(pos.1.to_le())),
1100 })
1101}
1102
1103pub trait WriteChangeLine: std::io::Write {
1104 fn write_change_line(&mut self, pref: &str, contents: &str) -> Result<(), std::io::Error> {
1105 writeln!(self, "{} {}", pref, contents)
1106 }
1107 fn write_change_line_binary(
1108 &mut self,
1109 pref: &str,
1110 contents: &[u8],
1111 ) -> Result<(), std::io::Error> {
1112 writeln!(self, "{}b{}", pref, data_encoding::BASE64.encode(contents))
1113 }
1114}
1115
1116impl WriteChangeLine for &mut Vec<u8> {}
1117impl WriteChangeLine for &mut std::io::Stderr {}
1118impl WriteChangeLine for &mut std::io::Stdout {}
1119
1120pub fn get_change_contents<C: ChangeStore>(
1121 changes: &C,
1122 change: &Atom<Option<Hash>>,
1123 change_contents: &[u8],
1124) -> Result<Vec<u8>, TextSerError<C::Error>> {
1125 debug!("get_change_contents {:?}", change);
1126 match change {
1127 Atom::NewVertex(n) => Ok(change_contents[n.start.us()..n.end.us()].to_vec()),
1128 Atom::EdgeMap(n) if n.edges.is_empty() => Err(TextSerError::InvalidChange),
1129 Atom::EdgeMap(n) if n.edges[0].flag.contains(EdgeFlags::DELETED) => {
1130 let mut buf = Vec::new();
1131 let mut z = 0;
1132 let mut current = None;
1133 for e in n.edges.iter() {
1134 if Some(e.to) == current {
1135 continue;
1136 }
1137 let sz = e.to.end - e.to.start;
1138 buf.resize(z + sz, 0);
1139 changes
1140 .get_contents_ext(e.to, &mut buf[z..])
1141 .map_err(TextSerError::C)?;
1142 z += sz;
1143 current = Some(e.to)
1144 }
1145 Ok(buf)
1146 }
1147 _ => Ok(Vec::new()),
1148 }
1149}
1150
1151pub fn get_deleted_names<C: ChangeStore>(
1152 changes: &C,
1153 del: &Atom<Option<Hash>>,
1154) -> Result<Vec<String>, TextSerError<C::Error>> {
1155 let mut res = Vec::new();
1156 let mut h = HashSet::new();
1157 if let Atom::EdgeMap(e) = del {
1158 let mut tmp = Vec::new();
1159 for d in e.edges.iter() {
1160 if !h.insert(d.to) {
1161 continue;
1162 }
1163 tmp.resize(d.to.end - d.to.start, 0);
1164 changes
1165 .get_contents_ext(d.to, &mut tmp)
1166 .map_err(TextSerError::C)?;
1167 if !tmp.is_empty() {
1168 let FileMetadata { basename: name, .. } = FileMetadata::read(&tmp);
1169 res.push(name.to_string());
1170 }
1171 }
1172 }
1173 Ok(res)
1174}
1175
1176pub fn to_printable_pos(
1177 hashes: &HashMap<Hash, usize>,
1178 pos: Position<Option<Hash>>,
1179) -> PrintablePos {
1180 let change = if let Some(Hash::None) = pos.change {
1181 1
1182 } else if let Some(ref c) = pos.change {
1183 *hashes.get(c).unwrap()
1184 } else {
1185 0
1186 };
1187 PrintablePos(change, pos.pos.0.as_u64())
1188}
1189
1190pub fn to_printable_pos_vec(
1191 hashes: &HashMap<Hash, usize>,
1192 pos: &[Position<Option<Hash>>],
1193) -> Vec<PrintablePos> {
1194 pos.iter().map(|c| to_printable_pos(hashes, *c)).collect()
1195}