Skip to content

Commit 3371861

Browse files
CLI: Work in progress
1 parent 7f0cd4d commit 3371861

File tree

9 files changed

+493
-12
lines changed

9 files changed

+493
-12
lines changed

pom.xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
4040

4141
<!-- Dependencies versions -->
42+
<jackson.version>3.0.0</jackson.version>
43+
<hibernate-validator.version>9.0.1.Final</hibernate-validator.version>
44+
<picocli.version>4.7.7</picocli.version>
4245
<jspecify.version>1.0.0</jspecify.version>
4346
<junit.version>5.13.4</junit.version>
4447
<assertj.version>3.27.4</assertj.version>
@@ -59,7 +62,23 @@
5962

6063
</properties>
6164

65+
6266
<dependencies>
67+
<dependency>
68+
<groupId>tools.jackson.core</groupId>
69+
<artifactId>jackson-databind</artifactId>
70+
<version>${jackson.version}</version>
71+
</dependency>
72+
<dependency>
73+
<groupId>org.hibernate.validator</groupId>
74+
<artifactId>hibernate-validator</artifactId>
75+
<version>${hibernate-validator.version}</version>
76+
</dependency>
77+
<dependency>
78+
<groupId>info.picocli</groupId>
79+
<artifactId>picocli</artifactId>
80+
<version>${picocli.version}</version>
81+
</dependency>
6382
<dependency>
6483
<groupId>org.jspecify</groupId>
6584
<artifactId>jspecify</artifactId>
@@ -134,6 +153,7 @@
134153
<configuration>
135154
<source>${java.version}</source>
136155
<target>${java.version}</target>
156+
<encoding>UTF-8</encoding>
137157
</configuration>
138158
</plugin>
139159

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

3-
import io.github.computerdaddyguy.jfiletreeprettyprinter.FileTreePrettyPrinter;
3+
import picocli.CommandLine;
44

55
public class FileTreePrettyPrinterCommandLine {
66

77
public static void main(String[] args) {
8-
String path = ".";
9-
if (args != null && args.length > 0) {
10-
path = args[0];
11-
}
12-
try {
13-
var printer = FileTreePrettyPrinter.createDefault();
14-
var result = printer.prettyPrint(path);
15-
System.out.println(result);
16-
} catch (Exception e) {
17-
System.err.print("Error while pretty printing: " + e.getMessage());
18-
}
8+
int exitCode = new CommandLine(new PrettyPrintCommand()).execute(args);
9+
System.exit(exitCode);
1910
}
2011

2112
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package io.github.computerdaddyguy.jfiletreeprettyprinter.cli;
2+
3+
import io.github.computerdaddyguy.jfiletreeprettyprinter.FileTreePrettyPrinter;
4+
import io.github.computerdaddyguy.jfiletreeprettyprinter.cli.options.ExternalOptionsReader;
5+
import java.io.File;
6+
import java.nio.file.Path;
7+
import java.util.List;
8+
import java.util.concurrent.Callable;
9+
import java.util.logging.Level;
10+
import java.util.logging.LogManager;
11+
import java.util.logging.Logger;
12+
import picocli.CommandLine.Command;
13+
import picocli.CommandLine.Option;
14+
import picocli.CommandLine.Parameters;
15+
16+
// @formatter:off
17+
@Command(
18+
name = "prettyprint",
19+
mixinStandardHelpOptions = true,
20+
version = "checksum 4.0",
21+
description = "Prints the checksum (SHA-256 by default) of a file to STDOUT."
22+
)
23+
// @formatter:on
24+
public class PrettyPrintCommand implements Callable<Integer> {
25+
26+
@Parameters(index = "0", description = "The path to pretty print", arity = "0")
27+
private File file;
28+
29+
@Option(names = { "-o", "--options" }, paramLabel = "OPTIONS", description = "the options file", arity = "0")
30+
private File optionsFile;
31+
32+
@Override
33+
public Integer call() throws Exception {
34+
35+
LogManager.getLogManager().reset();
36+
Logger.getLogger("org.hibernate.validator").setLevel(Level.OFF);
37+
38+
var reader = new ExternalOptionsReader();
39+
reader.readOptions();
40+
41+
Path path = detectPathToPrint();
42+
if (optionsFile != null) {
43+
path = optionsFile.toPath();
44+
}
45+
try {
46+
var printer = FileTreePrettyPrinter.createDefault();
47+
var result = printer.prettyPrint(path);
48+
System.out.println(result);
49+
} catch (Exception e) {
50+
System.err.print("Error while pretty printing: " + e.getMessage());
51+
return 1;
52+
}
53+
54+
return 0;
55+
}
56+
57+
private static Path detectPathToPrint() {
58+
for (var p : List.of("tmp", "target/tmp")) {
59+
var path = Path.of(p);
60+
if (path.toFile().exists()) {
61+
return path;
62+
}
63+
}
64+
return Path.of(".");
65+
66+
}
67+
68+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package io.github.computerdaddyguy.jfiletreeprettyprinter.cli;
2+
3+
import java.util.ArrayList;
4+
import java.util.Collection;
5+
import java.util.List;
6+
7+
public class RecordUtils {
8+
9+
/**
10+
* Inspired from: https://sormuras.github.io/blog/2020-05-06-records-to-text-block.html
11+
*/
12+
public static String toTextBlock(Record record) {
13+
var lines = new ArrayList<String>();
14+
toTextBlock(lines, "", null, record, " ");
15+
return String.join(System.lineSeparator(), lines);
16+
}
17+
18+
private static void toTextBlock(List<String> lines, String shift, String attrName, Object value, String indent) {
19+
var nested = value.getClass();
20+
if (nested.isRecord()) {
21+
toTextBlockRecord(lines, shift, attrName, (Record) value, indent);
22+
} else if (value instanceof Collection coll) {
23+
toTextBlockColl(lines, shift, attrName, coll, indent);
24+
} else {
25+
lines.add(String.format("%s%s = %s", shift, attrName, value));
26+
}
27+
}
28+
29+
private static void toTextBlockRecord(List<String> lines, String shift, String attrName, Record record, String indent) {
30+
if (attrName == null) {
31+
lines.add(String.format("%s%s", shift, record.getClass().getSimpleName()));
32+
} else {
33+
lines.add(String.format("%s%s -> %s", shift, attrName, record.getClass().getSimpleName()));
34+
}
35+
36+
var components = record.getClass().getRecordComponents();
37+
38+
for (var component : components) {
39+
var compName = component.getName();
40+
try {
41+
var value = component.getAccessor().invoke(record);
42+
toTextBlock(lines, shift + indent, compName, value, indent);
43+
} catch (ReflectiveOperationException e) {
44+
lines.add("// Reflection over " + component + " failed: " + e);
45+
}
46+
}
47+
}
48+
49+
private static void toTextBlockColl(List<String> lines, String shift, String attrName, Collection<?> coll, String indent) {
50+
if (attrName == null) {
51+
lines.add(String.format("%s[", shift));
52+
} else {
53+
lines.add(String.format("%s%s = [", shift, attrName));
54+
}
55+
int i = 0;
56+
for (var item : coll) {
57+
toTextBlock(lines, shift + indent, "[" + i + "]", item, indent);
58+
i++;
59+
}
60+
lines.add(String.format("%s]", shift));
61+
}
62+
63+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package io.github.computerdaddyguy.jfiletreeprettyprinter.cli.options;
2+
3+
import io.github.computerdaddyguy.jfiletreeprettyprinter.cli.RecordUtils;
4+
import io.github.computerdaddyguy.jfiletreeprettyprinter.cli.options.model.ExternalOptions;
5+
import jakarta.validation.ConstraintViolationException;
6+
import jakarta.validation.Validation;
7+
import java.nio.file.Path;
8+
import java.util.logging.Level;
9+
import java.util.logging.LogManager;
10+
import java.util.logging.Logger;
11+
import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator;
12+
import tools.jackson.databind.json.JsonMapper;
13+
14+
public class ExternalOptionsReader {
15+
16+
public void readOptions() {
17+
18+
/*
19+
* READ
20+
*/
21+
var mapper = JsonMapper.builder()
22+
// .withConfigOverride(
23+
// Object.class, cfg -> {
24+
// cfg.setNullHandling(JsonSetter.Value.forValueNulls(Nulls.FAIL, Nulls.FAIL));
25+
// }
26+
// )
27+
.build();
28+
var path = ".prettyprint";
29+
ExternalOptions externalOptions = mapper.readValue(Path.of(path), ExternalOptions.class);
30+
31+
/*
32+
* VALIDATE
33+
*/
34+
var validatorFactory = Validation.byDefaultProvider()
35+
.configure()
36+
.messageInterpolator(new ParameterMessageInterpolator())
37+
.buildValidatorFactory();
38+
var validator = validatorFactory.getValidator();
39+
40+
var violations = validator.validate(externalOptions);
41+
if (!violations.isEmpty()) {
42+
throw new ConstraintViolationException(violations);
43+
}
44+
45+
/*
46+
* PROCESS
47+
*/
48+
System.out.println("OK -->\n" + RecordUtils.toTextBlock(externalOptions));
49+
}
50+
51+
public static void main(String[] args) {
52+
LogManager.getLogManager().reset();
53+
Logger.getLogger("org.hibernate.validator").setLevel(Level.OFF);
54+
55+
var reader = new ExternalOptionsReader();
56+
reader.readOptions();
57+
}
58+
59+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package io.github.computerdaddyguy.jfiletreeprettyprinter.cli.options.model;
2+
3+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
4+
import com.fasterxml.jackson.annotation.JsonTypeName;
5+
import jakarta.validation.Valid;
6+
import jakarta.validation.constraints.NotEmpty;
7+
import jakarta.validation.constraints.NotNull;
8+
import java.util.List;
9+
10+
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
11+
public sealed interface ChildLimit {
12+
13+
/**
14+
*
15+
* @param limit
16+
*/
17+
@JsonTypeName("static")
18+
public record StaticLimit(
19+
@NotNull Integer limit
20+
) implements ChildLimit {
21+
22+
}
23+
24+
/**
25+
*
26+
*/
27+
@JsonTypeName("dynamic")
28+
public record DynamicLimit(
29+
@Valid @NotNull @NotEmpty List<@Valid @NotNull DynamicLimitItem> limits
30+
) implements ChildLimit {
31+
32+
}
33+
34+
/**
35+
*
36+
*/
37+
public record DynamicLimitItem(
38+
@NotNull Integer limit,
39+
@Valid @NotNull Matcher matcher
40+
) {
41+
42+
}
43+
44+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.github.computerdaddyguy.jfiletreeprettyprinter.cli.options.model;
2+
3+
import jakarta.validation.Valid;
4+
5+
public record ExternalOptions(
6+
Boolean emojis,
7+
@Valid ChildLimit childLimit
8+
) {
9+
10+
public ExternalOptions(Boolean emojis, ChildLimit childLimit) {
11+
this.emojis = emojis == null ? false : emojis;
12+
this.childLimit = childLimit;
13+
}
14+
15+
}

0 commit comments

Comments
 (0)