From 3ac8d7a4d59d479c1042adbac3e1c4006bc801ef Mon Sep 17 00:00:00 2001 From: Alban Auzeill Date: Sun, 27 Sep 2020 20:33:55 +0200 Subject: [PATCH] Add --diff --- .../java/com/auzeill/file/StatContext.java | 84 +++++++++++++++++++ src/main/java/com/auzeill/file/Stats.java | 3 +- .../com/auzeill/file/StatContextTest.java | 8 ++ src/test/java/com/auzeill/file/StatsTest.java | 37 ++++++++ 4 files changed, 131 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/auzeill/file/StatContext.java b/src/main/java/com/auzeill/file/StatContext.java index b817dc8..2f87da7 100644 --- a/src/main/java/com/auzeill/file/StatContext.java +++ b/src/main/java/com/auzeill/file/StatContext.java @@ -1,6 +1,7 @@ package com.auzeill.file; import java.io.IOException; +import java.io.PrintStream; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; @@ -8,16 +9,20 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.TimeZone; +import java.util.stream.Collectors; import static java.nio.charset.StandardCharsets.UTF_8; @@ -28,14 +33,17 @@ public class StatContext { private static final String NO_SHA1 = "--no-sha1"; private static final String IGNORE = "--ignore"; private static final String SAVE = "--save"; + private static final String DIFF = "--diff"; public final Path baseDirectory; public final Path statsDirectory; public final Path rootPath; public final boolean computeSha1; public final boolean save; + public final boolean diff; public final Set ignoreSet; public final Map lastFileAttributesMap; + public final List previousPathsToDiff; public StatContext(Path baseDirectory, Path rootPath, boolean computeSha1) { this.baseDirectory = baseDirectory; @@ -43,14 +51,17 @@ public StatContext(Path baseDirectory, Path rootPath, boolean computeSha1) { this.rootPath = rootPath; this.computeSha1 = computeSha1; this.save = false; + this.diff = false; this.ignoreSet = new HashSet<>(); this.lastFileAttributesMap = new HashMap<>(); + this.previousPathsToDiff = Collections.emptyList(); } public StatContext(String[] args) throws IOException { List arguments = new ArrayList<>(Arrays.asList(args)); this.computeSha1 = !arguments.remove(NO_SHA1); this.save = arguments.remove(SAVE); + this.diff = arguments.remove(DIFF); this.ignoreSet = new HashSet<>(); int ignorePos = arguments.lastIndexOf(IGNORE); while (ignorePos != -1 && ignorePos + 1 < arguments.size()) { @@ -103,6 +114,17 @@ public StatContext(String[] args) throws IOException { } } } + + this.previousPathsToDiff = this.lastFileAttributesMap.keySet().stream() + .sorted(Comparator.comparing(StatContext::pathToSort)) + .collect(Collectors.toCollection(LinkedList::new)); + } + + private static String pathToSort(String path) { + if (path.equals(".")) { + return "\uFFFF"; + } + return path.replace('\\', '\uFFFF').replace('/', '\uFFFF'); } public Path newStatSavedPath() { @@ -115,4 +137,66 @@ public boolean include(String relativeLinuxPath) { return !ignoreSet.contains(relativeLinuxPath); } + public void printStats(PrintStream out, FileAttributes attributes) { + if (diff) { + Iterator previousIterator = previousPathsToDiff.iterator(); + boolean compared = false; + while (previousIterator.hasNext()) { + FileAttributes prevAttributes = this.lastFileAttributesMap.get(previousIterator.next()); + int comp = pathToSort(prevAttributes.relativeLinuxPath).compareTo(pathToSort(attributes.relativeLinuxPath)); + if (comp < 0) { + out.println("-del- " + prevAttributes.toString()); + previousIterator.remove(); + } else if (comp == 0) { + compared = true; + previousIterator.remove(); + if (!prevAttributes.toString().equals(attributes.toString())) { + StringBuilder diffLine = new StringBuilder(); + diffLine.append("~mod~ ").append(attributes.relativeLinuxPath).append("|"); + if (prevAttributes.type != attributes.type) { + diffLine.append(" type ").append(prevAttributes.type).append(" -> ").append(attributes.type).append(" |"); + } + if (prevAttributes.size != attributes.size) { + diffLine.append(" size ").append(prevAttributes.size).append(" -> ").append(attributes.size).append(" |"); + } + if (!prevAttributes.owner.equals(attributes.owner)) { + diffLine.append(" owner ").append(prevAttributes.owner).append(" -> ").append(attributes.owner).append(" |"); + } + if (!prevAttributes.group.equals(attributes.group)) { + diffLine.append(" group ").append(prevAttributes.group).append(" -> ").append(attributes.group).append(" |"); + } + if (!prevAttributes.permissions.equals(attributes.permissions)) { + diffLine.append(" permissions ").append(prevAttributes.permissions).append(" -> ").append(attributes.permissions).append(" |"); + } + if (!prevAttributes.modifiedTime.equals(attributes.modifiedTime)) { + diffLine.append(" modifiedTime ").append(prevAttributes.modifiedTime).append(" -> ").append(attributes.modifiedTime).append(" |"); + } + if (!prevAttributes.sha1OrSymbolicLink.equals(attributes.sha1OrSymbolicLink)) { + diffLine.append(" sha1OrSymbolicLink ").append(prevAttributes.sha1OrSymbolicLink).append(" -> ").append(attributes.sha1OrSymbolicLink).append(" |"); + } + out.println(diffLine.toString()); + } + } else { + break; + } + } + if (!compared) { + out.println("+new+ " + attributes.toString()); + } + } else { + out.println(attributes.toString()); + } + } + + public void exitStats(PrintStream out) { + if (diff) { + Iterator previousIterator = previousPathsToDiff.iterator(); + while (previousIterator.hasNext()) { + FileAttributes prevAttributes = this.lastFileAttributesMap.get(previousIterator.next()); + out.println("-del- " + prevAttributes.toString()); + previousIterator.remove(); + } + } + } + } diff --git a/src/main/java/com/auzeill/file/Stats.java b/src/main/java/com/auzeill/file/Stats.java index 99d7f5c..a544b28 100644 --- a/src/main/java/com/auzeill/file/Stats.java +++ b/src/main/java/com/auzeill/file/Stats.java @@ -32,6 +32,7 @@ public static void stats(PrintStream out, String[] args) throws IOException { } else { stats(out, context, context.rootPath); } + context.exitStats(out); } public static FileAttributes stats(PrintStream out, StatContext context, Path path) throws IOException { @@ -61,7 +62,7 @@ public static FileAttributes stats(PrintStream out, StatContext context, Path pa attributes.modifiedTime, context.computeSha1 ? Sha1.digest(allSha1.toString().getBytes(UTF_8)) : ""); } - out.println(attributes.toString()); + context.printStats(out, attributes); return attributes; } else { return null; diff --git a/src/test/java/com/auzeill/file/StatContextTest.java b/src/test/java/com/auzeill/file/StatContextTest.java index 92a4850..66c05b0 100644 --- a/src/test/java/com/auzeill/file/StatContextTest.java +++ b/src/test/java/com/auzeill/file/StatContextTest.java @@ -21,6 +21,7 @@ void one_argument() throws IOException { assertThat(context.baseDirectory.toString()).isEqualTo(Paths.get("src").toRealPath().toString()); assertThat(context.computeSha1).isEqualTo(true); assertThat(context.save).isEqualTo(false); + assertThat(context.diff).isEqualTo(false); } @Test @@ -37,4 +38,11 @@ void save() throws IOException { assertThat(context.save).isEqualTo(true); } + @Test + void diff() throws IOException { + StatContext context = new StatContext(new String[] {"--diff", "src"}); + assertThat(context.baseDirectory.toString()).isEqualTo(Paths.get("src").toRealPath().toString()); + assertThat(context.diff).isEqualTo(true); + } + } diff --git a/src/test/java/com/auzeill/file/StatsTest.java b/src/test/java/com/auzeill/file/StatsTest.java index 365564e..3feaa3d 100644 --- a/src/test/java/com/auzeill/file/StatsTest.java +++ b/src/test/java/com/auzeill/file/StatsTest.java @@ -109,4 +109,41 @@ void reuse_sha1_from_previous_save(@TempDir Path tempDir) throws IOException { ".|d|4|alban|alban|rwx------|2020-09-02T15:43:48.680382Z|158b484ae1f6f64f89da22397d25fbdafad02252" + System.lineSeparator()); } + @Test + void diff_from_previous(@TempDir Path tempDir) throws IOException { + Files.writeString(tempDir.resolve("f1"), "abcd", UTF_8); + Files.writeString(tempDir.resolve("f2"), "efgh", UTF_8); + Files.writeString(tempDir.resolve("f3"), "ijdk", UTF_8); + Files.writeString(tempDir.resolve("f5"), "efgh", UTF_8); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + PrintStream stream = new PrintStream(out, true, UTF_8); + Stats.stats(stream, new String[] { "--save", tempDir.toString() }); + String output = new String(out.toByteArray(), UTF_8); + assertThat(output).matches(tempDir.toString() + File.separator + ".directory-stats" + File.separator + "stat-\\d{4}\\.\\d{2}\\.\\d{2}-\\d{2}h\\d{2}m\\d{2}s\\d{3}" + System.lineSeparator()); + String statPath = output.replaceFirst("[\r\n]+$", ""); + String fileContent = Files.readString(Paths.get(statPath), UTF_8); + assertThat(FileAttributesTest.forceSysFields(fileContent)).isEqualTo("" + + "f1|f|4|alban|alban|rw-r--r--|2020-09-02T15:43:48.680382Z|81fe8bfe87576c3ecb22426f8e57847382917acf" + System.lineSeparator() + + "f2|f|4|alban|alban|rw-r--r--|2020-09-02T15:43:48.680382Z|2aed8aa9f826c21ef07d5ee15b48eea06e9c8a62" + System.lineSeparator() + + "f3|f|4|alban|alban|rw-r--r--|2020-09-02T15:43:48.680382Z|cca5603cae64651971a35bc1488f0d23ddabdff9" + System.lineSeparator() + + "f5|f|4|alban|alban|rw-r--r--|2020-09-02T15:43:48.680382Z|2aed8aa9f826c21ef07d5ee15b48eea06e9c8a62" + System.lineSeparator() + + ".|d|16|alban|alban|rwx------|2020-09-02T15:43:48.680382Z|f81b3e16656c1bd84d7522ed2f83fa996ffd8497" + System.lineSeparator()); + + + Files.delete(tempDir.resolve("f2")); + Files.writeString(tempDir.resolve("f3"), "changed content", UTF_8); + Files.writeString(tempDir.resolve("f4"), "new file content", UTF_8); + + out = new ByteArrayOutputStream(); + stream = new PrintStream(out, true, UTF_8); + Stats.stats(stream, new String[] { "--diff", tempDir.toString() }); + assertThat(FileAttributesTest.forceSysFields(new String(out.toByteArray(), UTF_8))) + .isEqualTo("" + + "-del- f2|f|4|alban|alban|rw-r--r--|2020-09-02T15:43:48.680382Z|2aed8aa9f826c21ef07d5ee15b48eea06e9c8a62" + System.lineSeparator() + + "~mod~ f3| size 4 -> 15 | modifiedTime 2020-09-02T15:43:48.680382Z -> 2020-09-02T15:43:48.680382Z | sha1OrSymbolicLink cca5603cae64651971a35bc1488f0d23ddabdff9 -> 1fa817e97796161063e307eac706bb8b06cf956c |" + System.lineSeparator() + + "+new+ f4|f|16|alban|alban|rw-r--r--|2020-09-02T15:43:48.680382Z|17c494d126c27755e2134a4388178d808b139ce9" + System.lineSeparator() + + "~mod~ .| size 16 -> 39 | modifiedTime 2020-09-02T15:43:48.680382Z -> 2020-09-02T15:43:48.680382Z | sha1OrSymbolicLink f81b3e16656c1bd84d7522ed2f83fa996ffd8497 -> 2d8050016ac5619885aaac0402f8c5d88cceb077 |" + System.lineSeparator()); + } + }