1use std::fmt;
2
3#[cfg(test)]
4use quickcheck::{Arbitrary, Gen};
5
6#[cfg(test)]
7use PrintableHunk::*;
8
9use super::*;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum StringFragment<'a> {
13 Literal(&'a str),
14 EscapedChar(char),
15}
16
17#[derive(Copy, Clone, Debug, Eq, PartialEq)]
18pub enum PrintablePerms {
19 IsDir,
20 IsExecutable,
21 IsFile,
22}
23
24#[derive(Copy, Clone, Debug, Eq, PartialEq)]
25pub struct PrintableEdgeFlags {
26 pub block: bool,
27 pub folder: bool,
28 pub deleted: bool,
29}
30
31#[derive(Clone, Debug, Eq, PartialEq)]
32pub struct PrintableEdge {
33 pub previous: PrintableEdgeFlags,
34 pub flag: PrintableEdgeFlags,
35 pub from: PrintablePos,
36 pub to_start: PrintablePos,
37 pub to_end: u64,
38 pub introduced_by: usize,
39}
40
41#[derive(Clone, Debug, Eq, PartialEq)]
42pub struct PrintableNewVertex {
43 pub up_context: Vec<PrintablePos>,
44 pub start: u64,
45 pub end: u64,
46 pub down_context: Vec<PrintablePos>,
47}
48
49#[derive(Clone, Debug, Eq, PartialEq)]
50pub enum PrintableAtom {
51 NewVertex(PrintableNewVertex),
52 Edges(Vec<PrintableEdge>),
53}
54
55#[derive(Copy, Clone, Debug, Eq, PartialEq)]
56pub struct PrintablePos(pub usize, pub u64);
57
58#[derive(PartialEq, Eq, Clone, Debug)]
62pub enum PrintableHunk {
63 FileMoveV {
64 path: String,
65 name: String,
66 perms: PrintablePerms,
67 pos: PrintablePos,
68 up_context: Vec<PrintablePos>,
69 down_context: Vec<PrintablePos>,
70 del: Vec<PrintableEdge>,
71 },
72 FileMoveE {
73 path: String,
74 pos: PrintablePos,
75 add: Vec<PrintableEdge>,
76 del: Vec<PrintableEdge>,
77 },
78 FileAddition {
79 name: String,
80 parent: String,
81 perms: PrintablePerms,
82 encoding: Option<Encoding>,
83 up_context: Vec<PrintablePos>,
84 start: u64,
85 end: u64,
86 contents: Vec<u8>,
87 },
88 FileDel {
89 path: String,
90 pos: PrintablePos,
91 encoding: Option<Encoding>,
92 del_edges: Vec<PrintableEdge>,
93 content_edges: Vec<PrintableEdge>,
94 contents: Vec<u8>,
95 },
96 FileUndel {
97 path: String,
98 pos: PrintablePos,
99 encoding: Option<Encoding>,
100 undel_edges: Vec<PrintableEdge>,
101 content_edges: Vec<PrintableEdge>,
102 contents: Vec<u8>,
103 },
104 Edit {
105 path: String,
106 line: usize,
107 pos: PrintablePos,
108 encoding: Option<Encoding>,
109 change: PrintableAtom,
110 contents: Vec<u8>,
111 },
112 Replace {
113 path: String,
114 line: usize,
115 pos: PrintablePos,
116 encoding: Option<Encoding>,
117 change: Vec<PrintableEdge>,
118 replacement: PrintableNewVertex,
119 change_contents: Vec<u8>,
120 replacement_contents: Vec<u8>,
121 },
122 SolveNameConflict {
123 path: String,
124 pos: PrintablePos,
125 names: Vec<String>,
126 edges: Vec<PrintableEdge>,
127 },
128 UnsolveNameConflict {
129 path: String,
130 pos: PrintablePos,
131 names: Vec<String>,
132 edges: Vec<PrintableEdge>,
133 },
134 SolveOrderConflict {
135 path: String,
136 line: usize,
137 pos: PrintablePos,
138 encoding: Option<Encoding>,
139 change: PrintableNewVertex,
140 contents: Vec<u8>,
141 },
142 UnsolveOrderConflict {
143 path: String,
144 line: usize,
145 pos: PrintablePos,
146 encoding: Option<Encoding>,
147 change: Vec<PrintableEdge>,
148 contents: Vec<u8>,
149 },
150 ResurrectZombies {
151 path: String,
152 line: usize,
153 pos: PrintablePos,
154 encoding: Option<Encoding>,
155 change: Vec<PrintableEdge>,
156 contents: Vec<u8>,
157 },
158 AddRoot {
159 start: u64,
160 },
161 DelRoot {
162 name: Vec<PrintableEdge>,
163 inode: Vec<PrintableEdge>,
164 },
165}
166
167#[derive(PartialEq, Eq, Clone, Debug)]
168pub struct PrintableDep {
169 pub type_: DepType,
170 pub hash: String,
171}
172
173#[derive(PartialEq, Eq, Clone, Debug)]
175pub enum DepType {
176 Numbered(usize, bool), ExtraKnown,
178 ExtraUnknown,
179}
180
181pub struct Escaped<'a>(pub &'a str);
182
183impl<'a> fmt::Display for Escaped<'a> {
184 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
185 write!(fmt, "\"")?;
186 for c in self.0.chars() {
187 if c == '\n' {
188 write!(fmt, "\\n")?
189 } else if c == '\r' {
190 write!(fmt, "\\r")?
191 } else if c == '\t' {
192 write!(fmt, "\\t")?
193 } else if c == '\u{08}' {
194 write!(fmt, "\\b")?
195 } else if c == '\u{0C}' {
196 write!(fmt, "\\f")?
197 } else if c == '\\' {
198 write!(fmt, "\\\\")?
199 } else if c == '"' {
200 write!(fmt, "\\\"")?
201 } else {
202 write!(fmt, "{}", c)?
203 }
204 }
205 write!(fmt, "\"")?;
206 Ok(())
207 }
208}
209
210impl PrintablePerms {
211 pub fn from_metadata(perms: InodeMetadata) -> Self {
212 if perms.0 & 0o1000 == 0o1000 {
213 PrintablePerms::IsDir
214 } else if perms.0 & 0o100 == 0o100 {
215 PrintablePerms::IsExecutable
216 } else {
217 PrintablePerms::IsFile
218 }
219 }
220
221 pub fn to_metadata(self) -> InodeMetadata {
222 InodeMetadata(match self {
223 PrintablePerms::IsDir => 0o1100,
224 PrintablePerms::IsExecutable => 0o100,
225 PrintablePerms::IsFile => 0o0,
226 })
227 }
228}
229
230impl fmt::Display for PrintablePos {
231 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
232 write!(fmt, "{}.{}", self.0, self.1)
233 }
234}
235
236impl fmt::Display for PrintablePerms {
237 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
238 write!(
239 fmt,
240 "{}",
241 match self {
242 PrintablePerms::IsDir => " +dx",
243 PrintablePerms::IsExecutable => " +x",
244 PrintablePerms::IsFile => "",
245 }
246 )
247 }
248}
249
250impl PrintableEdgeFlags {
251 pub fn from(ef: EdgeFlags) -> Self {
252 assert!(!ef.contains(EdgeFlags::PARENT));
253 assert!(!ef.contains(EdgeFlags::PSEUDO));
254 Self {
255 block: ef.contains(EdgeFlags::BLOCK),
256 folder: ef.contains(EdgeFlags::FOLDER),
257 deleted: ef.contains(EdgeFlags::DELETED),
258 }
259 }
260
261 pub fn to(self) -> EdgeFlags {
263 let mut f = EdgeFlags::empty();
264 if self.block {
265 f |= EdgeFlags::BLOCK;
266 }
267 if self.folder {
268 f |= EdgeFlags::FOLDER;
269 }
270 if self.deleted {
271 f |= EdgeFlags::DELETED;
272 }
273 f
274 }
275}
276
277impl fmt::Display for PrintableEdgeFlags {
278 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
279 if self.block {
280 write!(fmt, "B")?;
281 }
282 if self.folder {
283 write!(fmt, "F")?;
284 }
285 if self.deleted {
286 write!(fmt, "D")?;
287 }
288 Ok(())
289 }
290}
291
292impl fmt::Display for PrintableEdge {
293 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
294 write!(
295 fmt,
296 "{}:{} {} -> {}:{}/{}",
297 self.previous, self.flag, self.from, self.to_start, self.to_end, self.introduced_by
298 )
299 }
300}
301
302impl fmt::Display for PrintableNewVertex {
303 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
304 write!(fmt, " up")?;
305 for c in self.up_context.iter() {
306 write!(fmt, " {}", c)?
307 }
308 write!(fmt, ", new {}:{}, down", self.start, self.end)?;
309 for c in self.down_context.iter() {
310 write!(fmt, " {}", c)?;
311 }
312 Ok(())
313 }
314}
315
316impl fmt::Display for PrintableAtom {
317 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
318 match self {
319 PrintableAtom::NewVertex(x) => write!(fmt, "{}", x),
320 PrintableAtom::Edges(x) => {
321 for (i, edge) in x.iter().enumerate() {
322 if i > 0 {
323 write!(fmt, ", ")?;
324 }
325 write!(fmt, "{}", edge)?;
326 }
327 Ok(())
328 }
329 }
330 }
331}
332
333impl PrintableHunk {
334 pub fn write<W: WriteChangeLine>(&self, w: &mut W) -> Result<(), std::io::Error> {
335 use PrintableHunk::*;
336 match self {
337 FileMoveV {
338 path,
339 name,
340 perms,
341 pos,
342 up_context,
343 down_context,
344 del,
345 } => {
346 writeln!(
347 w,
348 "Moved: {} {} {} {}",
349 Escaped(path),
350 Escaped(name),
351 perms,
352 pos,
353 )?;
354 writeln!(w, "{}", PrintableAtom::Edges(del.to_vec()))?;
355 write!(w, "up")?;
356 for c in up_context.iter() {
357 write!(w, " {}", c)?
358 }
359 write!(w, ", down")?;
360 for c in down_context.iter() {
361 write!(w, " {}", c)?
362 }
363 writeln!(w)?;
364 }
365 FileMoveE {
366 path,
367 pos,
368 add,
369 del,
370 } => {
371 writeln!(w, "Moved: {} {}", Escaped(path), pos)?;
372 writeln!(w, "{}", PrintableAtom::Edges(add.to_vec()))?;
373 writeln!(w, "{}", PrintableAtom::Edges(del.to_vec()))?;
374 }
375 FileAddition {
376 name,
377 parent,
378 perms,
379 encoding,
380 up_context,
381 start,
382 end,
383 contents,
384 } => {
385 write!(
386 w,
387 "File addition: {} in {}{} {}\n up",
388 Escaped(name),
389 Escaped(parent),
390 perms,
391 Escaped(encoding_label(encoding)),
392 )?;
393 for c in up_context.iter() {
394 write!(w, " {}", c)?
395 }
396 writeln!(w, ", new {}:{}", start, end)?;
397 print_contents(w, "+", contents, encoding)?;
398 }
399 FileDel {
400 path,
401 pos,
402 encoding,
403 del_edges,
404 content_edges,
405 contents,
406 } => {
407 writeln!(
408 w,
409 "File deletion: {} {} {}",
410 Escaped(path),
411 pos,
412 Escaped(encoding_label(encoding)),
413 )?;
414 writeln!(w, "{}", PrintableAtom::Edges(del_edges.to_vec()))?;
415 if !content_edges.is_empty() {
416 writeln!(w, "{}", PrintableAtom::Edges(content_edges.to_vec()))?;
417 }
418 print_contents(w, "-", contents, encoding)?;
419 }
420 FileUndel {
421 path,
422 pos,
423 encoding,
424 undel_edges,
425 content_edges,
426 contents,
427 } => {
428 writeln!(
429 w,
430 "File un-deletion: {} {} {}",
431 Escaped(path),
432 pos,
433 Escaped(encoding_label(encoding)),
434 )?;
435 writeln!(w, "{}", PrintableAtom::Edges(undel_edges.to_vec()))?;
436 if !content_edges.is_empty() {
437 writeln!(w, "{}", PrintableAtom::Edges(content_edges.to_vec()))?;
438 }
439 print_contents(w, "+", contents, encoding)?;
440 }
441
442 Edit {
443 path,
444 line,
445 pos,
446 encoding,
447 change,
448 contents,
449 } => {
450 writeln!(
451 w,
452 "Edit in {}:{} {} {}",
453 Escaped(path),
454 line,
455 pos,
456 Escaped(encoding_label(encoding))
457 )?;
458 writeln!(w, "{}", change)?;
459 let sign = if let PrintableAtom::Edges(e) = change {
460 if e.is_empty() || e[0].flag.deleted {
461 "-"
462 } else {
463 "+"
464 }
465 } else {
466 "+"
467 };
468 print_contents(w, sign, contents, encoding)?;
469 }
470
471 Replace {
472 path,
473 line,
474 pos,
475 encoding,
476 change,
477 replacement,
478 change_contents,
479 replacement_contents,
480 } => {
481 writeln!(
482 w,
483 "Replacement in {}:{} {} {}",
484 Escaped(path),
485 line,
486 pos,
487 Escaped(encoding_label(encoding))
488 )?;
489 writeln!(w, "{}", PrintableAtom::Edges(change.clone()))?;
490 writeln!(w, "{}", PrintableAtom::NewVertex(replacement.clone()))?;
491 print_contents(w, "-", change_contents, encoding)?;
492 print_contents(w, "+", replacement_contents, encoding)?;
493 }
494 SolveNameConflict {
495 path,
496 pos,
497 names,
498 edges,
499 } => {
500 write!(w, "Solving a name conflict in {} {}: ", Escaped(path), pos,)?;
501 write_names(w, names)?;
502 writeln!(w)?;
503 writeln!(w, "{}", PrintableAtom::Edges(edges.clone()))?;
504 }
505 UnsolveNameConflict {
506 path,
507 pos,
508 names,
509 edges,
510 } => {
511 write!(
512 w,
513 "Un-solving a name conflict in {} {}: ",
514 Escaped(path),
515 pos,
516 )?;
517 write_names(w, names)?;
518 writeln!(w)?;
519 writeln!(w, "{}", PrintableAtom::Edges(edges.clone()))?;
520 }
521 SolveOrderConflict {
522 path,
523 line,
524 pos,
525 encoding,
526 change,
527 contents,
528 } => {
529 writeln!(
530 w,
531 "Solving an order conflict in {}:{} {} {}",
532 Escaped(path),
533 line,
534 pos,
535 Escaped(encoding_label(encoding)),
536 )?;
537 writeln!(w, "{}", change)?;
538 print_contents(w, "+", contents, encoding)?;
539 }
540 UnsolveOrderConflict {
541 path,
542 line,
543 pos,
544 encoding,
545 change,
546 contents,
547 } => {
548 writeln!(
549 w,
550 "Un-solving an order conflict in {}:{} {} {}",
551 Escaped(path),
552 line,
553 pos,
554 Escaped(encoding_label(encoding))
555 )?;
556 writeln!(w, "{}", PrintableAtom::Edges(change.clone()))?;
557 print_contents(w, "-", contents, encoding)?;
558 }
559 ResurrectZombies {
560 path,
561 line,
562 pos,
563 encoding,
564 change,
565 contents,
566 } => {
567 writeln!(
568 w,
569 "Resurrecting zombie lines in {}:{} {} {}",
570 Escaped(path),
571 line,
572 pos,
573 Escaped(encoding_label(encoding))
574 )?;
575 writeln!(w, "{}", PrintableAtom::Edges(change.clone()))?;
576 print_contents(w, "+", contents, encoding)?;
577 }
578 AddRoot { start } => {
579 writeln!(
580 w,
581 "Root add\n up {}, new {}:{}",
582 PrintablePos(1, 0),
583 start,
584 start,
585 )?;
586 }
587 DelRoot { name, inode } => {
588 writeln!(w, "Root del",)?;
589 writeln!(w, "{}", PrintableAtom::Edges(name.to_vec()))?;
590 writeln!(w, "{}", PrintableAtom::Edges(inode.to_vec()))?;
591 }
592 };
593 Ok(())
594 }
595}
596
597pub fn write_names<W: std::io::Write>(w: &mut W, names: &[String]) -> Result<(), std::io::Error> {
598 for (i, name) in names.iter().enumerate() {
599 if i > 0 {
600 write!(w, ", ")?;
601 }
602 write!(w, "{}", Escaped(name))?;
603 }
604 Ok(())
605}
606
607pub fn get_encoding(contents: &[u8]) -> Option<Encoding> {
608 let mut detector = chardetng::EncodingDetector::new();
609 detector.feed(contents, true);
610 crate::get_valid_encoding(&detector, None, true, contents).map(Encoding)
611}
612
613fn print_contents<W: WriteChangeLine>(
614 w: &mut W,
615 prefix: &str,
616 contents: &[u8],
617 encoding: &Option<Encoding>,
618) -> Result<(), std::io::Error> {
619 if contents.is_empty() {
620 return Ok(());
621 }
622 if let Some(encoding) = encoding {
623 let dec = encoding.decode(contents);
624 let ends_with_newline = dec.ends_with("\n");
625 let dec = if ends_with_newline {
626 &dec[..dec.len() - 1]
627 } else {
628 &dec
629 };
630 for a in dec.split('\n') {
631 w.write_change_line(prefix, a)?;
632 }
633 if !ends_with_newline {
634 writeln!(w, "\\")?;
635 }
636 Ok(())
637 } else if contents.len() <= 4096 {
638 writeln!(w, "{}b{}", prefix, data_encoding::BASE64.encode(contents))
639 } else {
640 Ok(())
641 }
642}
643
644#[cfg(test)]
647#[rustfmt::skip]
648impl Arbitrary for PrintableHunk {
650 fn arbitrary(g: &mut Gen) -> Self {
651 fn f<A: Arbitrary>(g: &mut Gen) -> A {
652 Arbitrary::arbitrary(g)
653 }
654
655 fix_encoding(Gen::new(g.size()).choose(&[
656 FileMoveV {
657 path: f(g), name: f(g), perms: f(g), pos: f(g), up_context: f(g), down_context: f(g), del: f(g),
658 },
659 FileMoveE {
660 path: f(g), pos: f(g), add: f(g), del: f(g),
661 },
662 FileAddition {
663 name: f(g), parent: f(g), perms: f(g), encoding: f(g), up_context: f(g), start: f(g), end: f(g), contents: f(g),
664 },
665 FileDel {
666 path: f(g), pos: f(g), encoding: f(g), del_edges: f(g), content_edges: f(g), contents: f(g),
667 },
668 FileUndel {
669 path: f(g), pos: f(g), encoding: f(g), undel_edges: f(g), content_edges: f(g), contents: f(g),
670 },
671 Edit {
672 path: f(g), line: f(g), pos: f(g), encoding: f(g), change: f(g), contents: f(g),
673 },
674 Replace {
675 path: f(g), line: f(g), pos: f(g), encoding: f(g), change: f(g), replacement: f(g), change_contents: f(g), replacement_contents: f(g),
676 },
677 SolveNameConflict {
678 path: f(g), pos: f(g), names: f(g), edges: f(g),
679 },
680 UnsolveNameConflict {
681 path: f(g), pos: f(g), names: f(g), edges: f(g),
682 },
683 SolveOrderConflict {
684 path: f(g), line: f(g), pos: f(g), encoding: f(g), change: f(g), contents: f(g),
685 },
686 UnsolveOrderConflict {
687 path: f(g), line: f(g), pos: f(g), encoding: f(g), change: f(g), contents: f(g),
688 },
689 ResurrectZombies {
690 path: f(g), line: f(g), pos: f(g), encoding: f(g), change: f(g), contents: f(g),
691 },
692 AddRoot {
693 start: f(g),
694 },
695 DelRoot {
696 name: f(g), inode: f(g)
697 },
698 ])
699 .unwrap().clone())
700 }
701
702 fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
706 use std::iter::empty;
707 match self.clone() {
708 _ => Box::new(empty()),
715 }
716 }
717}
718
719#[cfg(test)]
720fn fix_encoding(mut hunk: PrintableHunk) -> PrintableHunk {
723 match &mut hunk {
725 FileAddition {
726 encoding, contents, ..
727 } => *encoding = get_encoding(contents),
728 FileDel {
729 encoding, contents, ..
730 } => *encoding = get_encoding(contents),
731 FileUndel {
732 encoding, contents, ..
733 } => *encoding = get_encoding(contents),
734 Edit {
735 encoding, contents, ..
736 } => *encoding = get_encoding(contents),
737 Replace { encoding, .. } => *encoding = None,
738 SolveOrderConflict {
739 encoding, contents, ..
740 } => *encoding = get_encoding(contents),
741 UnsolveOrderConflict {
742 encoding, contents, ..
743 } => *encoding = get_encoding(contents),
744 ResurrectZombies {
745 encoding, contents, ..
746 } => *encoding = get_encoding(contents),
747 _ => (),
748 };
749 hunk
750}
751
752#[cfg(test)]
753impl Arbitrary for PrintablePerms {
754 fn arbitrary(g: &mut Gen) -> Self {
755 *g.choose(&[
756 PrintablePerms::IsDir,
757 PrintablePerms::IsExecutable,
758 PrintablePerms::IsFile,
759 ])
760 .unwrap()
761 }
762}
763
764#[cfg(test)]
765impl Arbitrary for PrintablePos {
766 fn arbitrary(g: &mut Gen) -> Self {
767 PrintablePos(usize::arbitrary(g), u64::arbitrary(g))
768 }
769 fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
770 Box::new(
771 self.0
772 .shrink()
773 .zip(self.1.shrink())
774 .map(|(a, b)| PrintablePos(a, b)),
775 )
776 }
777}
778
779#[cfg(test)]
780impl Arbitrary for PrintableEdge {
781 fn arbitrary(g: &mut Gen) -> Self {
782 Self {
783 previous: Arbitrary::arbitrary(g),
784 flag: Arbitrary::arbitrary(g),
785 from: Arbitrary::arbitrary(g),
786 to_start: Arbitrary::arbitrary(g),
787 to_end: Arbitrary::arbitrary(g),
788 introduced_by: Arbitrary::arbitrary(g),
789 }
790 }
791 fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
792 let Self {
793 previous,
794 flag,
795 from,
796 to_start,
797 to_end,
798 introduced_by,
799 } = self.clone();
800 Box::new(
801 (previous, flag, from, to_start, to_end, introduced_by)
802 .shrink()
803 .map(
804 |(previous, flag, from, to_start, to_end, introduced_by)| Self {
805 previous,
806 flag,
807 from,
808 to_start,
809 to_end,
810 introduced_by,
811 },
812 ),
813 )
814 }
815}
816
817#[cfg(test)]
818impl Arbitrary for PrintableNewVertex {
819 fn arbitrary(g: &mut Gen) -> Self {
820 Self {
821 up_context: Arbitrary::arbitrary(g),
822 start: Arbitrary::arbitrary(g),
823 end: Arbitrary::arbitrary(g),
824 down_context: Arbitrary::arbitrary(g),
825 }
826 }
827 fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
828 let Self {
829 up_context,
830 start,
831 end,
832 down_context,
833 } = self.clone();
834 Box::new((up_context, start, end, down_context).shrink().map(
835 |(up_context, start, end, down_context)| Self {
836 up_context,
837 start,
838 end,
839 down_context,
840 },
841 ))
842 }
843}
844
845#[cfg(test)]
846impl Arbitrary for PrintableAtom {
847 fn arbitrary(g: &mut Gen) -> Self {
848 Gen::new(g.size())
849 .choose(&[
850 PrintableAtom::NewVertex(Arbitrary::arbitrary(g)),
851 PrintableAtom::Edges(Arbitrary::arbitrary(g)),
852 ])
853 .unwrap()
854 .clone()
855 }
856 fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
857 match self {
858 PrintableAtom::NewVertex(x) => {
859 Box::new(x.shrink().map(|x| PrintableAtom::NewVertex(x)))
860 }
861 PrintableAtom::Edges(x) => Box::new(x.shrink().map(|x| PrintableAtom::Edges(x))),
862 }
863 }
864}
865
866#[cfg(test)]
867impl Arbitrary for PrintableEdgeFlags {
868 fn arbitrary(g: &mut Gen) -> Self {
869 Self {
870 block: Arbitrary::arbitrary(g),
871 folder: Arbitrary::arbitrary(g),
872 deleted: Arbitrary::arbitrary(g),
873 }
874 }
875 fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
876 Box::new((self.block, self.folder, self.deleted).shrink().map(
877 |(block, folder, deleted)| Self {
878 block,
879 folder,
880 deleted,
881 },
882 ))
883 }
884}