Skip to content

Commit 5f05047

Browse files
Refactor & tests
1 parent 23cf113 commit 5f05047

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+663
-168
lines changed

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@
7070
<artifactId>jackson-databind</artifactId>
7171
<version>${jackson.version}</version>
7272
</dependency>
73+
<dependency>
74+
<groupId>tools.jackson.dataformat</groupId>
75+
<artifactId>jackson-dataformat-yaml</artifactId>
76+
<version>${jackson.version}</version>
77+
</dependency>
7378
<dependency>
7479
<groupId>org.hibernate.validator</groupId>
7580
<artifactId>hibernate-validator</artifactId>

src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/cli/ConsoleOutput.java

Lines changed: 0 additions & 52 deletions
This file was deleted.
Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,43 @@
11
package io.github.computerdaddyguy.jfiletreeprettyprinter.cli;
22

3-
import io.github.computerdaddyguy.jfiletreeprettyprinter.cli.options.ExternalOptionsMapper;
4-
import io.github.computerdaddyguy.jfiletreeprettyprinter.cli.options.ExternalOptionsReader;
3+
import io.github.computerdaddyguy.jfiletreeprettyprinter.cli.exception.DefaultExecutionExceptionHandler;
4+
import io.github.computerdaddyguy.jfiletreeprettyprinter.cli.io.ConsoleOutput;
5+
import io.github.computerdaddyguy.jfiletreeprettyprinter.cli.options.OptionsLoader;
6+
import java.util.Objects;
7+
import org.jspecify.annotations.NullMarked;
58
import picocli.CommandLine;
9+
import picocli.CommandLine.IExecutionExceptionHandler;
610

11+
@NullMarked
712
class FileTreePrettyPrinterCommandLine {
813

9-
public static void main(String[] args) {
10-
System.exit(executeCommand(args));
11-
}
14+
private final CommandLine cmd;
1215

13-
static int executeCommand(String[] args) {
14-
var reader = ExternalOptionsReader.createDefault();
15-
var mapper = ExternalOptionsMapper.createDefault();
16+
FileTreePrettyPrinterCommandLine(ConsoleOutput output, OptionsLoader optionsLoader, IExecutionExceptionHandler exHandler) {
17+
super();
18+
Objects.requireNonNull(output, "output is null");
19+
Objects.requireNonNull(optionsLoader, "optionsLoader is null");
20+
Objects.requireNonNull(exHandler, "exHandler is null");
1621

17-
var cmd = new CommandLine(new PrettyPrintCommand(reader, mapper));
22+
cmd = new CommandLine(new PrettyPrintCommand(output, optionsLoader));
23+
cmd.setExecutionExceptionHandler(exHandler);
24+
}
1825

26+
int executeCommand(String[] args) {
1927
return cmd.execute(args);
2028
}
2129

30+
// -------------------------------------------------------------------------------
31+
32+
public static void main(String[] args) {
33+
34+
var output = ConsoleOutput.createDefault();
35+
var optionsLoader = OptionsLoader.createDefault(output);
36+
var exHandler = new DefaultExecutionExceptionHandler(output);
37+
38+
var cli = new FileTreePrettyPrinterCommandLine(output, optionsLoader, exHandler);
39+
40+
System.exit(cli.executeCommand(args));
41+
}
42+
2243
}

src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/cli/PrettyPrintCommand.java

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
package io.github.computerdaddyguy.jfiletreeprettyprinter.cli;
22

33
import io.github.computerdaddyguy.jfiletreeprettyprinter.FileTreePrettyPrinter;
4-
import io.github.computerdaddyguy.jfiletreeprettyprinter.cli.options.ExternalOptionsException;
5-
import io.github.computerdaddyguy.jfiletreeprettyprinter.cli.options.ExternalOptionsMapper;
6-
import io.github.computerdaddyguy.jfiletreeprettyprinter.cli.options.ExternalOptionsReader;
4+
import io.github.computerdaddyguy.jfiletreeprettyprinter.cli.io.ConsoleOutput;
5+
import io.github.computerdaddyguy.jfiletreeprettyprinter.cli.options.OptionsLoader;
76
import io.github.computerdaddyguy.jfiletreeprettyprinter.options.PrettyPrintOptions;
8-
import jakarta.validation.ConstraintViolationException;
97
import java.io.File;
108
import java.nio.file.Path;
119
import java.util.Objects;
@@ -27,12 +25,12 @@
2725
@NullMarked
2826
class PrettyPrintCommand implements Callable<Integer> {
2927

30-
private final ExternalOptionsReader reader;
31-
private final ExternalOptionsMapper mapper;
28+
private final ConsoleOutput output;
29+
private final OptionsLoader optionsLoader;
3230

33-
public PrettyPrintCommand(ExternalOptionsReader reader, ExternalOptionsMapper mapper) {
34-
this.reader = Objects.requireNonNull(reader, "reader is null");
35-
this.mapper = Objects.requireNonNull(mapper, "mapper is null");
31+
public PrettyPrintCommand(ConsoleOutput output, OptionsLoader optionsLoader) {
32+
this.output = Objects.requireNonNull(output, "output is null");
33+
this.optionsLoader = Objects.requireNonNull(optionsLoader, "optionsLoader is null");
3634
}
3735

3836
// ---------- CLI args ----------
@@ -53,36 +51,22 @@ public PrettyPrintCommand(ExternalOptionsReader reader, ExternalOptionsMapper ma
5351
@Override
5452
public Integer call() throws Exception {
5553

56-
var output = new ConsoleOutput(debug);
54+
output.enableDebug(this.debug);
5755

5856
var targetPath = detectTargetPath(output);
5957
if (!targetPath.toFile().exists()) {
6058
output.printError("Path not found: %s", targetPath);
6159
return 1;
6260
}
6361

64-
PrettyPrintOptions options;
65-
try {
66-
Path optionsPath = optionsFile == null ? null : optionsFile.toPath().toAbsolutePath().normalize();
67-
var externalOptions = reader.readExternalOptions(output, targetPath, optionsPath);
68-
options = mapper.mapToOptions(targetPath, externalOptions);
69-
} catch (ExternalOptionsException e) {
70-
output.printError(e.getMessage() + ": " + optionsFile.toString());
71-
return 1;
72-
} catch (ConstraintViolationException e) {
73-
output.printError(e.getMessage() + ": " + optionsFile.toString());
74-
return 1;
75-
}
62+
Path optionsPath = optionsFile == null ? null : optionsFile.toPath().toAbsolutePath().normalize();
63+
PrettyPrintOptions options = optionsLoader.loadOptions(targetPath, optionsPath);
7664

77-
try {
78-
var printer = FileTreePrettyPrinter.builder().withOptions(options).build();
79-
var result = printer.prettyPrint(targetPath);
80-
output.print(result);
81-
return 0;
82-
} catch (Exception e) {
83-
output.printError("Error while pretty printing: " + e.getMessage());
84-
return 1;
85-
}
65+
var printer = FileTreePrettyPrinter.builder().withOptions(options).build();
66+
var result = printer.prettyPrint(targetPath);
67+
output.print(result);
68+
69+
return 0;
8670
}
8771

8872
private Path detectTargetPath(ConsoleOutput output) {

src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/cli/options/RecordUtils.java renamed to src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/cli/RecordUtils.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.github.computerdaddyguy.jfiletreeprettyprinter.cli.options;
1+
package io.github.computerdaddyguy.jfiletreeprettyprinter.cli;
22

33
import java.util.ArrayList;
44
import java.util.Collection;
@@ -7,7 +7,7 @@
77
import org.jspecify.annotations.Nullable;
88

99
@NullMarked
10-
class RecordUtils {
10+
public class RecordUtils {
1111

1212
private RecordUtils() {
1313
// Helper class
@@ -16,7 +16,7 @@ private RecordUtils() {
1616
/**
1717
* Inspired from: https://sormuras.github.io/blog/2020-05-06-records-to-text-block.html
1818
*/
19-
static String toTextBlock(@Nullable Record rec) {
19+
public static String toTextBlock(@Nullable Record rec) {
2020
if (rec == null) {
2121
return "null";
2222
}
@@ -26,6 +26,9 @@ static String toTextBlock(@Nullable Record rec) {
2626
}
2727

2828
private static void toTextBlock(List<String> lines, String shift, @Nullable String attrName, Object value, String indent) {
29+
if (value == null) {
30+
return;
31+
}
2932
var nested = value.getClass();
3033
if (nested.isRecord()) {
3134
toTextBlockRecord(lines, shift, attrName, (Record) value, indent);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package io.github.computerdaddyguy.jfiletreeprettyprinter.cli.exception;
2+
3+
import io.github.computerdaddyguy.jfiletreeprettyprinter.cli.io.ConsoleOutput;
4+
import jakarta.validation.ConstraintViolationException;
5+
import java.util.Objects;
6+
import org.jspecify.annotations.NullMarked;
7+
import picocli.CommandLine;
8+
import picocli.CommandLine.IExecutionExceptionHandler;
9+
import picocli.CommandLine.ParseResult;
10+
11+
@NullMarked
12+
public class DefaultExecutionExceptionHandler implements IExecutionExceptionHandler {
13+
14+
private final ConsoleOutput output;
15+
16+
public DefaultExecutionExceptionHandler(ConsoleOutput output) {
17+
this.output = Objects.requireNonNull(output, "output is null");
18+
}
19+
20+
@Override
21+
public int handleExecutionException(Exception ex, CommandLine commandLine, ParseResult fullParseResult) throws Exception {
22+
return switch (ex) {
23+
case ExternalOptionsException extOptionEx -> handleExternalOptionsException(extOptionEx);
24+
case ConstraintViolationException violationEx -> handleConstraintViolationException(violationEx);
25+
default -> handleDefaultException(ex);
26+
};
27+
}
28+
29+
private int handleExternalOptionsException(ExternalOptionsException e) {
30+
output.printError(e.getMessage() + ": " + e.getOptionsPath().toString());
31+
e.printStackTrace();
32+
return 1;
33+
}
34+
35+
private int handleConstraintViolationException(ConstraintViolationException e) {
36+
output.printError(e.getMessage() + ": TODO");
37+
return 1;
38+
}
39+
40+
private int handleDefaultException(Exception e) {
41+
output.printError("Error while pretty printing: " + e.getMessage());
42+
return 1;
43+
}
44+
45+
}

src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/cli/options/ExternalOptionsException.java renamed to src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/cli/exception/ExternalOptionsException.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
package io.github.computerdaddyguy.jfiletreeprettyprinter.cli.options;
1+
package io.github.computerdaddyguy.jfiletreeprettyprinter.cli.exception;
22

33
import java.nio.file.Path;
44
import org.jspecify.annotations.NullMarked;
55

66
@NullMarked
77
public class ExternalOptionsException extends RuntimeException {
88

9-
private final Path optionsPath;
9+
private final transient Path optionsPath;
1010

1111
public ExternalOptionsException(Path optionsPath, String message, Throwable cause) {
1212
super(message, cause);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.github.computerdaddyguy.jfiletreeprettyprinter.cli.io;
2+
3+
import org.jspecify.annotations.NullMarked;
4+
5+
/**
6+
* An interface abstracting the console output - usefull to unit test the CLI output!
7+
*/
8+
@NullMarked
9+
public interface ConsoleOutput {
10+
11+
boolean isDebugEnabled();
12+
13+
void enableDebug(boolean enabled);
14+
15+
void print(String msg, Object... args);
16+
17+
void printDebug(String msg, Object... args);
18+
19+
void printError(String msg, Object... args);
20+
21+
public static ConsoleOutput createDefault() {
22+
return new DefaultConsoleOutput(System.out, System.err);
23+
}
24+
25+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package io.github.computerdaddyguy.jfiletreeprettyprinter.cli.io;
2+
3+
import java.io.PrintStream;
4+
import java.util.Objects;
5+
import java.util.logging.Logger;
6+
import org.jspecify.annotations.NullMarked;
7+
8+
@NullMarked
9+
public class DefaultConsoleOutput implements ConsoleOutput {
10+
11+
private static final Logger ROOT_LOGGER = Logger.getLogger("");
12+
13+
private boolean debug;
14+
private final PrintStream out;
15+
private final PrintStream err;
16+
17+
public DefaultConsoleOutput(PrintStream out, PrintStream err) {
18+
this.out = Objects.requireNonNull(out, "out stream is null");
19+
this.err = Objects.requireNonNull(err, "err stream is null");
20+
disableAllLoggers();
21+
}
22+
23+
/**
24+
* Require otherwise some embedded loggers (i.e. (Hibernate validator) print in the console.
25+
*/
26+
private void disableAllLoggers() {
27+
ROOT_LOGGER.setLevel(java.util.logging.Level.OFF);
28+
Logger.getGlobal().setLevel(java.util.logging.Level.OFF);
29+
Logger.getLogger("org.hibernate.validator").setLevel(java.util.logging.Level.OFF);
30+
}
31+
32+
// ---------------------------------------------------------
33+
34+
@Override
35+
public boolean isDebugEnabled() {
36+
return this.debug;
37+
}
38+
39+
@Override
40+
public void enableDebug(boolean enabled) {
41+
this.debug = enabled;
42+
}
43+
44+
@Override
45+
public void print(String msg, Object... args) {
46+
printOut(msg, args);
47+
}
48+
49+
@Override
50+
public void printDebug(String msg, Object... args) {
51+
// Could not (yet) make Logger level work within native image, so fallback to plain console output (for now)
52+
if (debug) {
53+
printOut("[DEBUG] " + msg, args);
54+
}
55+
}
56+
57+
@Override
58+
public void printError(String msg, Object... args) {
59+
printErr("[ERROR] " + msg, args);
60+
}
61+
62+
// ---------------------------------------------------------
63+
64+
private void printOut(String msg, Object... args) {
65+
printfln(out, msg, args);
66+
}
67+
68+
private void printErr(String msg, Object... args) {
69+
printfln(err, msg, args);
70+
}
71+
72+
private void printfln(PrintStream dest, String msg, Object... args) {
73+
dest.printf(msg + "\n", args);// Because "printf" does not print line return
74+
}
75+
76+
}

0 commit comments

Comments
 (0)