• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

valkyrjaio / sindri-java / 30419723982

29 Jul 2026 03:29AM UTC coverage: 95.429% (-4.5%) from 99.952%
30419723982

Pull #62

github

web-flow
Merge 31a86ee9f into 6968676cc
Pull Request #62: [Cli] Generate the argument and option parameters a command declares

574 of 618 branches covered (92.88%)

Branch coverage included in aggregate %.

93 of 154 new or added lines in 2 files covered. (60.39%)

1618 of 1679 relevant lines covered (96.37%)

4.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

59.28
/src/main/java/io/sindri/ast/CliRouteParameterReader.java
1
/*
2
 * This file is part of the Sindri package.
3
 *
4
 * (c) Melech Mizrachi <melechmizrachi@gmail.com>
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9

10
package io.sindri.ast;
11

12
import com.github.javaparser.ast.body.MethodDeclaration;
13
import com.github.javaparser.ast.expr.AnnotationExpr;
14
import com.github.javaparser.ast.expr.ArrayInitializerExpr;
15
import com.github.javaparser.ast.expr.Expression;
16
import com.github.javaparser.ast.expr.MemberValuePair;
17
import com.github.javaparser.ast.expr.NormalAnnotationExpr;
18
import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr;
19
import io.sindri.ast.abstract_.AstReader;
20
import io.sindri.ast.contract.CliRouteParameterReaderContract;
21
import io.sindri.ast.data.CliArgumentParameterData;
22
import io.sindri.ast.data.CliOptionParameterData;
23
import java.util.ArrayList;
24
import java.util.List;
25
import java.util.Map;
26
import org.jspecify.annotations.Nullable;
27

28
/**
29
 * Reads the argument and option parameters a CLI command declares.
30
 *
31
 * <p>Each is declared by annotating the command method, and both annotations are repeatable, so a
32
 * command may carry several of either directly or wrapped in their container annotation.
33
 */
34
public class CliRouteParameterReader extends AstReader implements CliRouteParameterReaderContract {
3✔
35

36
    @Override
37
    public List<CliArgumentParameterData> updateArguments(
38
            MethodDeclaration method, Map<String, String> importMap, String pkg) {
39
        List<CliArgumentParameterData> arguments = new ArrayList<>();
4✔
40

41
        for (AnnotationExpr annotation : method.getAnnotations()) {
11✔
42
            switch (annotation.getNameAsString()) {
9!
43
                case "ArgumentParameter" -> addArgument(arguments, annotation);
5✔
44
                case "ArgumentParameters" -> {
NEW
45
                    for (AnnotationExpr inner : extractAnnotationArray(annotation)) {
×
NEW
46
                        addArgument(arguments, inner);
×
NEW
47
                    }
×
NEW
48
                }
×
49
                default -> {}
50
            }
51
        }
1✔
52

53
        return arguments;
2✔
54
    }
55

56
    @Override
57
    public List<CliOptionParameterData> updateOptions(
58
            MethodDeclaration method, Map<String, String> importMap, String pkg) {
59
        List<CliOptionParameterData> options = new ArrayList<>();
4✔
60

61
        for (AnnotationExpr annotation : method.getAnnotations()) {
11✔
62
            switch (annotation.getNameAsString()) {
9!
63
                case "OptionParameter" -> addOption(options, annotation);
5✔
64
                case "OptionParameters" -> {
NEW
65
                    for (AnnotationExpr inner : extractAnnotationArray(annotation)) {
×
NEW
66
                        addOption(options, inner);
×
NEW
67
                    }
×
NEW
68
                }
×
69
                default -> {}
70
            }
71
        }
1✔
72

73
        return options;
2✔
74
    }
75

76
    private void addArgument(List<CliArgumentParameterData> arguments, AnnotationExpr annotation) {
77
        CliArgumentParameterData argument = parseArgument(annotation);
4✔
78

79
        if (argument != null) {
2!
80
            arguments.add(argument);
4✔
81
        }
82
    }
1✔
83

84
    private void addOption(List<CliOptionParameterData> options, AnnotationExpr annotation) {
85
        CliOptionParameterData option = parseOption(annotation);
4✔
86

87
        if (option != null) {
2!
88
            options.add(option);
4✔
89
        }
90
    }
1✔
91

92
    private @Nullable CliArgumentParameterData parseArgument(AnnotationExpr annotation) {
93
        if (!(annotation instanceof NormalAnnotationExpr normal)) {
7!
NEW
94
            return null;
×
95
        }
96

97
        String name = "";
2✔
98
        String description = "";
2✔
99
        String mode = "OPTIONAL";
2✔
100
        String valueMode = "DEFAULT";
2✔
101

102
        for (MemberValuePair pair : normal.getPairs()) {
11✔
103
            switch (pair.getNameAsString()) {
9!
104
                case "name" -> name = extractStringLiteral(pair.getValue());
6✔
105
                case "description" -> description = extractStringLiteral(pair.getValue());
6✔
106
                case "mode" -> mode = extractEnumName(pair.getValue());
6✔
107
                case "valueMode" -> valueMode = extractEnumName(pair.getValue());
6✔
108
                default -> {}
109
            }
110
        }
1✔
111

112
        if (name.isEmpty()) {
3!
NEW
113
            return null;
×
114
        }
115

116
        return new CliArgumentParameterData(name, description, null, mode, valueMode);
9✔
117
    }
118

119
    private @Nullable CliOptionParameterData parseOption(AnnotationExpr annotation) {
120
        if (!(annotation instanceof NormalAnnotationExpr normal)) {
7!
NEW
121
            return null;
×
122
        }
123

124
        String name = "";
2✔
125
        String description = "";
2✔
126
        String valueDisplayName = "";
2✔
127
        String defaultValue = "";
2✔
128
        List<String> shortNames = List.of();
2✔
129
        List<String> validValues = List.of();
2✔
130
        String mode = "OPTIONAL";
2✔
131
        String valueMode = "DEFAULT";
2✔
132

133
        for (MemberValuePair pair : normal.getPairs()) {
11✔
134
            switch (pair.getNameAsString()) {
9!
135
                case "name" -> name = extractStringLiteral(pair.getValue());
6✔
136
                case "description" -> description = extractStringLiteral(pair.getValue());
6✔
137
                case "valueDisplayName" -> valueDisplayName = extractStringLiteral(pair.getValue());
6✔
138
                case "defaultValue" -> defaultValue = extractStringLiteral(pair.getValue());
6✔
139
                case "shortNames" -> shortNames = extractStringArray(pair.getValue());
6✔
140
                case "validValues" -> validValues = extractStringArray(pair.getValue());
6✔
141
                case "mode" -> mode = extractEnumName(pair.getValue());
6✔
142
                case "valueMode" -> valueMode = extractEnumName(pair.getValue());
6✔
143
                default -> {}
144
            }
145
        }
1✔
146

147
        if (name.isEmpty()) {
3!
NEW
148
            return null;
×
149
        }
150

151
        return new CliOptionParameterData(
13✔
152
                name,
153
                description,
154
                valueDisplayName,
155
                null,
156
                defaultValue,
157
                shortNames,
158
                validValues,
159
                mode,
160
                valueMode);
161
    }
162

163
    /** Extract the annotations a container annotation wraps in its {@code value} member. */
164
    private List<AnnotationExpr> extractAnnotationArray(AnnotationExpr annotation) {
NEW
165
        Expression value = null;
×
166

NEW
167
        if (annotation instanceof SingleMemberAnnotationExpr single) {
×
NEW
168
            value = single.getMemberValue();
×
NEW
169
        } else if (annotation instanceof NormalAnnotationExpr normal) {
×
NEW
170
            for (MemberValuePair pair : normal.getPairs()) {
×
NEW
171
                if (pair.getNameAsString().equals("value")) {
×
NEW
172
                    value = pair.getValue();
×
173
                }
NEW
174
            }
×
175
        }
176

NEW
177
        List<AnnotationExpr> result = new ArrayList<>();
×
178

NEW
179
        if (value instanceof ArrayInitializerExpr array) {
×
NEW
180
            for (Expression item : array.getValues()) {
×
NEW
181
                if (item instanceof AnnotationExpr inner) {
×
NEW
182
                    result.add(inner);
×
183
                }
NEW
184
            }
×
NEW
185
        } else if (value instanceof AnnotationExpr inner) {
×
NEW
186
            result.add(inner);
×
187
        }
188

NEW
189
        return result;
×
190
    }
191

192
    /** Extract a string array member, which may also be written as a single string. */
193
    private List<String> extractStringArray(Expression expr) {
194
        List<String> values = new ArrayList<>();
4✔
195

196
        if (expr.isArrayInitializerExpr()) {
3!
197
            for (Expression value : expr.asArrayInitializerExpr().getValues()) {
12✔
198
                values.add(extractStringLiteral(value));
6✔
199
            }
1✔
200

201
            return values;
2✔
202
        }
203

NEW
204
        values.add(extractStringLiteral(expr));
×
205

NEW
206
        return values;
×
207
    }
208

209
    /** Extract an enum member's case name, e.g. {@code REQUIRED} from {@code Mode.REQUIRED}. */
210
    private String extractEnumName(Expression expr) {
211
        if (expr.isFieldAccessExpr()) {
3!
212
            return expr.asFieldAccessExpr().getNameAsString();
4✔
213
        }
214

NEW
215
        if (expr.isNameExpr()) {
×
NEW
216
            return expr.asNameExpr().getNameAsString();
×
217
        }
218

NEW
219
        return expr.toString();
×
220
    }
221
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc