Skip to content

Commit 6680e1b

Browse files
committed
feat: extend framework services
- allow filetype to be inferred - calculate exit code based on result
1 parent 62da16e commit 6680e1b

File tree

8 files changed

+313
-90
lines changed

8 files changed

+313
-90
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2024 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless.cli;
17+
18+
import com.diffplug.spotless.cli.core.SpotlessActionContext;
19+
20+
public interface SpotlessActionContextProvider {
21+
22+
SpotlessActionContext spotlessActionContext();
23+
}

cli/src/main/java/com/diffplug/spotless/cli/SpotlessCLI.java

Lines changed: 63 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818
import java.nio.charset.Charset;
1919
import java.nio.file.Path;
2020
import java.util.List;
21-
import java.util.function.BiConsumer;
22-
import java.util.stream.Collectors;
2321

2422
import javax.annotation.Nonnull;
2523

@@ -28,6 +26,8 @@
2826
import com.diffplug.spotless.LineEnding;
2927
import com.diffplug.spotless.LintState;
3028
import com.diffplug.spotless.ThrowingEx;
29+
import com.diffplug.spotless.cli.core.SpotlessActionContext;
30+
import com.diffplug.spotless.cli.core.TargetFileTypeInferer;
3131
import com.diffplug.spotless.cli.core.TargetResolver;
3232
import com.diffplug.spotless.cli.execution.SpotlessExecutionStrategy;
3333
import com.diffplug.spotless.cli.help.OptionConstants;
@@ -41,7 +41,7 @@
4141
@Command(name = "spotless", mixinStandardHelpOptions = true, versionProvider = SpotlessCLIVersionProvider.class, description = "Runs spotless", subcommandsRepeatable = true, subcommands = {
4242
LicenseHeader.class,
4343
RemoveMeLaterSubCommand.class})
44-
public class SpotlessCLI implements SpotlessAction, SpotlessCommand {
44+
public class SpotlessCLI implements SpotlessAction, SpotlessCommand, SpotlessActionContextProvider {
4545

4646
@CommandLine.Option(names = {"-V", "--version"}, versionHelp = true, description = "Print version information and exit.")
4747
boolean versionRequested;
@@ -71,70 +71,46 @@ public Integer executeSpotlessAction(@Nonnull List<FormatterStep> formatterSteps
7171
.steps(formatterSteps)
7272
.build()) {
7373

74-
ResultType resultType = targetResolver.resolveTargets() // TODO result
74+
ResultType resultType = targetResolver.resolveTargets()
7575
.parallel() // needed?
7676
.map(path -> ThrowingEx.get(() -> new Result(path, LintState.of(formatter, path.toFile())))) // TODO handle suppressions, see SpotlessTaskImpl
7777
.map(result -> this.handleResult(formatter, result))
7878
.reduce(ResultType.CLEAN, ResultType::combineWith);
79-
System.out.println("Hello " + getClass().getSimpleName() + ", abc! Files: " + new TargetResolver(targets).resolveTargets().collect(Collectors.toList()));
80-
System.out.println("result: " + resultType);
81-
formatterSteps.forEach(step -> System.out.println("Step: " + step));
82-
return 0;
79+
return spotlessMode.translateResultTypeToExitCode(resultType);
8380
}
8481
}
8582

8683
private ResultType handleResult(Formatter formatter, Result result) {
8784
if (result.lintState.isClean()) {
88-
System.out.println("File is clean: " + result.target.toFile().getName());
85+
// System.out.println("File is clean: " + result.target.toFile().getName());
8986
return ResultType.CLEAN;
9087
}
9188
if (result.lintState.getDirtyState().didNotConverge()) {
92-
System.out.println("File did not converge: " + result.target.toFile().getName());
89+
System.err.println("File did not converge: " + result.target.toFile().getName()); // TODO: where to print the output to?
9390
return ResultType.DID_NOT_CONVERGE;
9491
}
95-
this.spotlessMode.action.accept(formatter, result);
92+
this.spotlessMode.handleResult(formatter, result);
9693
return ResultType.DIRTY;
9794

98-
/*
99-
if (lintState.getDirtyState().isClean()) {
100-
// Remove previous output if it exists
101-
Files.deleteIfExists(cleanFile.toPath());
102-
} else if (lintState.getDirtyState().didNotConverge()) {
103-
getLogger().warn("Skipping '{}' because it does not converge. Run {@code spotlessDiagnose} to understand why", relativePath);
104-
} else {
105-
Path parentDir = cleanFile.toPath().getParent();
106-
if (parentDir == null) {
107-
throw new IllegalStateException("Every file has a parent folder. But not: " + cleanFile);
108-
}
109-
Files.createDirectories(parentDir);
110-
// Need to copy the original file to the tmp location just to remember the file attributes
111-
Files.copy(input.toPath(), cleanFile.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
95+
}
11296

113-
getLogger().info(String.format("Writing clean file: %s", cleanFile));
114-
lintState.getDirtyState().writeCanonicalTo(cleanFile);
115-
}
116-
if (!lintState.isHasLints()) {
117-
Files.deleteIfExists(lintFile.toPath());
118-
} else {
119-
LinkedHashMap<String, List<Lint>> lints = lintState.getLintsByStep(formatter);
120-
SerializableMisc.toFile(lints, lintFile);
121-
}
122-
*/
97+
private TargetResolver targetResolver() {
98+
return new TargetResolver(targets);
12399
}
124100

125-
// private void writeBack(Result result) {
126-
// if (result.updated != null) {
127-
// ThrowingEx.run(() -> Files.writeString(result.target, result.updated, Charset.defaultCharset())); // TODO charset!
128-
// }
129-
// System.out.println("Writing back to file:" + result.target + " with content:\n" + result.updated);
130-
// }
101+
@Override
102+
public SpotlessActionContext spotlessActionContext() {
103+
TargetResolver targetResolver = targetResolver();
104+
TargetFileTypeInferer targetFileTypeInferer = new TargetFileTypeInferer(targetResolver);
105+
return new SpotlessActionContext(targetFileTypeInferer.inferTargetFileType());
106+
}
131107

132108
public static void main(String... args) {
133109
if (args.length == 0) {
134110
// args = new String[]{"--version"};
135111
// args = new String[]{"license-header", "--header-file", "CHANGES.md", "--delimiter-for", "java", "license-header", "--header", "abc"};
136112

137-
args = new String[]{"--mode=CHECK", "--target", "src/poc/java/**/*.java", "--encoding=UTF-8", "license-header", "--header", "abc", "--delimiter-for", "java", "license-header", "--header-file", "TestHeader.txt"};
113+
args = new String[]{"--mode=CHECK", "--target", "src/poc/java/**/*.java", "--encoding=UTF-8", "license-header", "--header", "abc", "license-header", "--header-file", "TestHeader.txt"};
138114
// args = new String[]{"--version"};
139115
}
140116
int exitCode = new CommandLine(new SpotlessCLI())
@@ -145,19 +121,54 @@ public static void main(String... args) {
145121
}
146122

147123
private enum SpotlessMode {
148-
CHECK(((formatter, result) -> {
149-
if (result.lintState.isHasLints()) {
150-
result.lintState.asStringOneLine(result.target.toFile(), formatter);
151-
} else {
152-
System.out.println(String.format("%s is violating formatting rules.", result.target));
124+
CHECK {
125+
@Override
126+
void handleResult(Formatter formatter, Result result) {
127+
if (result.lintState.isHasLints()) {
128+
result.lintState.asStringOneLine(result.target.toFile(), formatter);
129+
} else {
130+
System.out.println(String.format("%s is violating formatting rules.", result.target));
131+
}
153132
}
154-
})), APPLY(((formatter, result) -> ThrowingEx.run(() -> result.lintState.getDirtyState().writeCanonicalTo(result.target.toFile()))));
155133

156-
private final BiConsumer<Formatter, Result> action;
134+
@Override
135+
Integer translateResultTypeToExitCode(ResultType resultType) {
136+
if (resultType == ResultType.CLEAN) {
137+
return 0;
138+
}
139+
if (resultType == ResultType.DIRTY) {
140+
return 1;
141+
}
142+
if (resultType == ResultType.DID_NOT_CONVERGE) {
143+
return -1;
144+
}
145+
throw new IllegalStateException("Unexpected result type: " + resultType);
146+
}
147+
},
148+
APPLY {
149+
@Override
150+
void handleResult(Formatter formatter, Result result) {
151+
ThrowingEx.run(() -> result.lintState.getDirtyState().writeCanonicalTo(result.target.toFile()));
152+
}
157153

158-
SpotlessMode(BiConsumer<Formatter, Result> action) {
159-
this.action = action;
160-
}
154+
@Override
155+
Integer translateResultTypeToExitCode(ResultType resultType) {
156+
if (resultType == ResultType.CLEAN) {
157+
return 0;
158+
}
159+
if (resultType == ResultType.DIRTY) {
160+
return 0;
161+
}
162+
if (resultType == ResultType.DID_NOT_CONVERGE) {
163+
return -1;
164+
}
165+
throw new IllegalStateException("Unexpected result type: " + resultType);
166+
}
167+
};
168+
169+
abstract void handleResult(Formatter formatter, Result result);
170+
171+
abstract Integer translateResultTypeToExitCode(ResultType resultType);
161172

162173
}
163174

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2024 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless.cli.core;
17+
18+
import java.util.Objects;
19+
20+
import javax.annotation.Nonnull;
21+
22+
public class SpotlessActionContext {
23+
24+
private final TargetFileTypeInferer.TargetFileType targetFileType;
25+
26+
public SpotlessActionContext(@Nonnull TargetFileTypeInferer.TargetFileType targetFileType) {
27+
this.targetFileType = Objects.requireNonNull(targetFileType);
28+
}
29+
30+
@Nonnull
31+
public TargetFileTypeInferer.TargetFileType targetFileType() {
32+
return targetFileType;
33+
}
34+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Copyright 2024 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless.cli.core;
17+
18+
import java.util.Arrays;
19+
import java.util.Objects;
20+
21+
import javax.annotation.Nonnull;
22+
23+
public class TargetFileTypeInferer {
24+
25+
private final TargetResolver targetResolver;
26+
27+
public TargetFileTypeInferer(TargetResolver targetResolver) {
28+
this.targetResolver = Objects.requireNonNull(targetResolver);
29+
}
30+
31+
public TargetFileType inferTargetFileType() {
32+
return targetResolver.resolveTargets()
33+
.limit(5) // only check the first n files
34+
.map(this::inferFileType)
35+
.reduce(this::reduceFileType)
36+
.orElseGet(TargetFileType::unknown);
37+
}
38+
39+
private TargetFileType reduceFileType(TargetFileType fileType1, TargetFileType fileType2) {
40+
if (Objects.equals(fileType1, fileType2)) {
41+
return fileType1;
42+
}
43+
return TargetFileType.unknown();
44+
}
45+
46+
private TargetFileType inferFileType(@Nonnull java.nio.file.Path path) {
47+
String fileName = path.getFileName().toString();
48+
int lastDotIndex = fileName.lastIndexOf('.');
49+
if (lastDotIndex == -1) {
50+
return TargetFileType.unknown();
51+
}
52+
String fileExtension = fileName.substring(lastDotIndex + 1);
53+
return new TargetFileType(fileExtension);
54+
}
55+
56+
public final static class TargetFileType {
57+
private final String fileExtension;
58+
59+
private TargetFileType(String fileExtension) {
60+
this.fileExtension = fileExtension;
61+
}
62+
63+
public String fileExtension() {
64+
return fileExtension;
65+
}
66+
67+
public FileType fileType() {
68+
return FileType.fromFileExtension(fileExtension);
69+
}
70+
71+
@Override
72+
public boolean equals(Object o) {
73+
if (this == o)
74+
return true;
75+
if (o == null || getClass() != o.getClass())
76+
return false;
77+
TargetFileType that = (TargetFileType) o;
78+
return Objects.equals(fileExtension, that.fileExtension);
79+
}
80+
81+
@Override
82+
public int hashCode() {
83+
return Objects.hashCode(fileExtension);
84+
}
85+
86+
static TargetFileType unknown() {
87+
return new TargetFileType(null);
88+
}
89+
90+
static TargetFileType fromExtension(String fileExtension) {
91+
return new TargetFileType(fileExtension);
92+
}
93+
}
94+
95+
public enum FileType {
96+
JAVA, CPP, ANTLR4("g4"), GROOVY, PROTOBUF("proto"), KOTLIN("kt"), UNDETERMINED("");
97+
98+
private final String fileExtensionOverride;
99+
100+
FileType() {
101+
this.fileExtensionOverride = null;
102+
}
103+
104+
FileType(String fileExtensionOverride) {
105+
this.fileExtensionOverride = fileExtensionOverride;
106+
}
107+
108+
public String fileExtension() {
109+
return fileExtensionOverride == null ? name().toLowerCase() : fileExtensionOverride;
110+
}
111+
112+
public static FileType fromFileExtension(String fileExtension) {
113+
if (fileExtension == null || fileExtension.isEmpty()) {
114+
return UNDETERMINED;
115+
}
116+
return Arrays.stream(values())
117+
.filter(fileType -> fileType.fileExtension().equalsIgnoreCase(fileExtension))
118+
.findFirst()
119+
.orElse(UNDETERMINED);
120+
}
121+
}
122+
}

0 commit comments

Comments
 (0)