Skip to content

Commit a8ece64

Browse files
authored
Implement an --extension/-e option to prefer custom file extensions (#54)
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
1 parent d6b5ae1 commit a8ece64

File tree

13 files changed

+152
-24
lines changed

13 files changed

+152
-24
lines changed

docs/bundle.markdown

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ Bundling
22
========
33

44
```sh
5-
jsonschema bundle <schema.json> [--http/-h] [--verbose/-v] [--resolve/-r <schema.json> ...]
5+
jsonschema bundle <schema.json>
6+
[--http/-h] [--verbose/-v] [--resolve/-r <schema.json> ...]
67
```
78

89
A schema may contain references to remote schemas outside the scope of the

docs/format.markdown

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ Formatting
22
==========
33

44
```sh
5-
jsonschema fmt [schemas-or-directories...] [--check|-c] [--verbose/-v]
5+
jsonschema fmt [schemas-or-directories...]
6+
[--check/-c] [--verbose/-v] [--extension/-e <extension>]
67
```
78

89
Schemas are code. As such, they are expected follow consistent stylistic
@@ -56,6 +57,12 @@ jsonschema fmt path/to/schemas/
5657
jsonschema fmt
5758
```
5859

60+
### Format every `.schema.json` file in the current directory (recursively)
61+
62+
```sh
63+
jsonschema fmt --extension .schema.json
64+
```
65+
5966
### Check that a single JSON Schema is properly formatted
6067

6168
```sh

docs/lint.markdown

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ Linting
22
=======
33

44
```sh
5-
jsonschema lint [schemas-or-directories...] [--fix|-f] [--verbose/-v]
5+
jsonschema lint [schemas-or-directories...]
6+
[--fix/-f] [--verbose/-v] [--extension/-e <extension>]
67
```
78

89
JSON Schema is a surprisingly expressive schema language. Like with traditional
@@ -61,6 +62,12 @@ jsonschema lint path/to/schemas/
6162
jsonschema lint
6263
```
6364

65+
### Lint every `.schema.json` file in the current directory (recursively)
66+
67+
```sh
68+
jsonschema lint --extension .schema.json
69+
```
70+
6471
### Fix lint warnings on a single schema
6572

6673
```sh

docs/test.markdown

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ Testing
66
> to support *every* dialect of JSON Schema from Draft 0 to Draft 2020-12 soon.
77
88
```sh
9-
jsonschema test [schemas-or-directories...] [--http/-h] [--metaschema/-m] [--verbose/-v] [--resolve/-r <schema.json> ...]
9+
jsonschema test [schemas-or-directories...]
10+
[--http/-h] [--metaschema/-m] [--verbose/-v] [--resolve/-r <schema.json> ...]
11+
[--extension/-e <extension>]
1012
```
1113

1214
Schemas are code. As such, you should run an automated unit testing suite
@@ -66,6 +68,12 @@ jsonschema test path/to/tests/
6668
jsonschema test
6769
```
6870

71+
### Run every `.test.json` test definition in the current directory (recursively)
72+
73+
```sh
74+
jsonschema test --extension .test.json
75+
```
76+
6977
### Run a single test definition validating the schemas against their metaschemas
7078

7179
```sh

docs/validate.markdown

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ Validating
66
> to support *every* dialect of JSON Schema from Draft 0 to Draft 2020-12 soon.
77
88
```sh
9-
jsonschema validate <schema.json> [instance.json] [--http/-h] [--metaschema/-m] [--verbose/-v] [--resolve/-r <schema.json> ...]
9+
jsonschema validate <schema.json>
10+
[instance.json] [--http/-h] [--metaschema/-m] [--verbose/-v] [--resolve/-r <schema.json> ...]
1011
```
1112

1213
The most popular use case of JSON Schema is to validate JSON documents. The
@@ -53,7 +54,7 @@ jsonschema validate path/to/my/schema.json path/to/my/instance.json
5354
### Validate a JSON Schema against it meta-schema
5455

5556
```sh
56-
jsonschema validate path/to/my/schema.json
57+
jsonschema validate path/to/my/schema.json
5758
```
5859

5960
### Validate a JSON instance against a schema plus the schema against its meta-schema

src/command_fmt.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ auto intelligence::jsonschema::cli::fmt(
1313
const std::span<const std::string> &arguments) -> int {
1414
const auto options{parse_options(arguments, {"c", "check"})};
1515

16-
for (const auto &entry : for_each_json(options.at(""))) {
16+
for (const auto &entry :
17+
for_each_json(options.at(""), parse_extensions(options))) {
1718
if (options.contains("c") || options.contains("check")) {
1819
log_verbose(options) << "Checking: " << entry.first.string() << "\n";
1920
std::ifstream input{entry.first};

src/command_lint.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ auto intelligence::jsonschema::cli::lint(
2121
bool result{true};
2222

2323
if (options.contains("f") || options.contains("fix")) {
24-
for (const auto &entry : for_each_json(options.at(""))) {
24+
for (const auto &entry :
25+
for_each_json(options.at(""), parse_extensions(options))) {
2526
log_verbose(options) << "Linting: " << entry.first.string() << "\n";
2627
auto copy = entry.second;
2728
bundle.apply(copy, sourcemeta::jsontoolkit::default_schema_walker,
@@ -32,7 +33,8 @@ auto intelligence::jsonschema::cli::lint(
3233
output << std::endl;
3334
}
3435
} else {
35-
for (const auto &entry : for_each_json(options.at(""))) {
36+
for (const auto &entry :
37+
for_each_json(options.at(""), parse_extensions(options))) {
3638
log_verbose(options) << "Linting: " << entry.first.string() << "\n";
3739
const bool subresult = bundle.check(
3840
entry.second, sourcemeta::jsontoolkit::default_schema_walker,

src/command_test.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ auto intelligence::jsonschema::cli::test(
1515
const auto test_resolver{
1616
resolver(options, options.contains("h") || options.contains("http"))};
1717

18-
for (const auto &entry : for_each_json(options.at(""))) {
18+
for (const auto &entry :
19+
for_each_json(options.at(""), parse_extensions(options))) {
1920
const sourcemeta::jsontoolkit::JSON test{
2021
sourcemeta::jsontoolkit::from_file(entry.first)};
2122
CLI_ENSURE(test.is_object(), "The test document must be an object")

src/main.cc

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,30 +30,37 @@ Global Options:
3030
was passed.
3131
3232
test [schemas-or-directories...] [--http/-h] [--metaschema/-m]
33+
[--extension/-e <extension>]
3334
3435
A schema test runner inspired by the official JSON Schema test suite.
3536
Passing directories as input will run every `.json` file in such
3637
directory (recursively) as a test. If no argument is passed, run every
3738
`.json` file in the current working directory (recursively) as a test.
38-
The `--http/-h` option enables resolving remote schemas over the HTTP
39-
protocol. The `--metaschema/-m` option checks that the given schema is
40-
valid with respects to its dialect metaschema.
39+
The `--http/-h` option enables resolving remote schemas over the HTTP
40+
protocol. The `--metaschema/-m` option checks that the given schema is
41+
valid with respects to its dialect metaschema. When scanning
42+
directories, the `--extension/-e` option is used to prefer a file
43+
extension other than `.json`. This option can be set multiple times.
4144
42-
fmt [schemas-or-directories...] [--check/-c]
45+
fmt [schemas-or-directories...] [--check/-c] [--extension/-e <extension>]
4346
4447
Format the input schemas in-place. Passing directories as input means
4548
to format every `.json` file in such directory (recursively). If no
4649
argument is passed, format every `.json` file in the current working
4750
directory (recursively). The `--check/-c` option will check if the given
48-
schemas adhere to the desired formatting without modifying them.
51+
schemas adhere to the desired formatting without modifying them. When
52+
scanning directories, the `--extension/-e` option is used to prefer a
53+
file extension other than `.json`. This option can be set multiple times.
4954
50-
lint [schemas-or-directories...] [--fix/-f]
55+
lint [schemas-or-directories...] [--fix/-f] [--extension/-e <extension>]
5156
5257
Lint the input schemas. Passing directories as input means to lint
5358
every `.json` file in such directory (recursively). If no argument is
5459
passed, lint every `.json` file in the current working directory
5560
(recursively). The `--fix/-f` option will attempt to automatically
56-
fix the linter errors.
61+
fix the linter errors. When scanning directories, the `--extension/-e`
62+
option is used to prefer a file extension other than `.json`. This option
63+
can be set multiple times.
5764
5865
bundle <schema.json> [--http/-h]
5966

src/utils.cc

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,30 @@
55

66
#include "utils.h"
77

8+
#include <algorithm> // std::any_of
89
#include <cassert> // assert
910
#include <fstream> // std::ofstream
1011
#include <iostream> // std::cerr
1112
#include <optional> // std::optional, std::nullopt
13+
#include <set> // std::set
1214
#include <sstream> // std::ostringstream
1315
#include <stdexcept> // std::runtime_error
1416

1517
namespace {
1618

1719
auto handle_json_entry(
1820
const std::filesystem::path &entry_path,
21+
const std::set<std::string> &extensions,
1922
std::vector<std::pair<std::filesystem::path, sourcemeta::jsontoolkit::JSON>>
2023
&result) -> void {
2124
if (std::filesystem::is_directory(entry_path)) {
2225
for (auto const &entry :
2326
std::filesystem::recursive_directory_iterator{entry_path}) {
2427
if (!std::filesystem::is_directory(entry) &&
25-
entry.path().extension() == ".json") {
28+
std::any_of(extensions.cbegin(), extensions.cend(),
29+
[&entry](const auto &extension) {
30+
return entry.path().string().ends_with(extension);
31+
})) {
2632
result.emplace_back(entry.path(),
2733
sourcemeta::jsontoolkit::from_file(entry.path()));
2834
}
@@ -34,26 +40,42 @@ auto handle_json_entry(
3440
throw std::runtime_error(error.str());
3541
}
3642

37-
result.emplace_back(entry_path,
38-
sourcemeta::jsontoolkit::from_file(entry_path));
43+
if (std::any_of(extensions.cbegin(), extensions.cend(),
44+
[&entry_path](const auto &extension) {
45+
return entry_path.string().ends_with(extension);
46+
})) {
47+
result.emplace_back(entry_path,
48+
sourcemeta::jsontoolkit::from_file(entry_path));
49+
}
3950
}
4051
}
4152

53+
auto normalize_extension(const std::string &extension) -> std::string {
54+
if (extension.starts_with('.')) {
55+
return extension;
56+
}
57+
58+
std::ostringstream result;
59+
result << '.' << extension;
60+
return result.str();
61+
}
62+
4263
} // namespace
4364

4465
namespace intelligence::jsonschema::cli {
4566

46-
auto for_each_json(const std::vector<std::string> &arguments)
67+
auto for_each_json(const std::vector<std::string> &arguments,
68+
const std::set<std::string> &extensions)
4769
-> std::vector<
4870
std::pair<std::filesystem::path, sourcemeta::jsontoolkit::JSON>> {
4971
std::vector<std::pair<std::filesystem::path, sourcemeta::jsontoolkit::JSON>>
5072
result;
5173

5274
if (arguments.empty()) {
53-
handle_json_entry(std::filesystem::current_path(), result);
75+
handle_json_entry(std::filesystem::current_path(), extensions, result);
5476
} else {
5577
for (const auto &entry : arguments) {
56-
handle_json_entry(entry, result);
78+
handle_json_entry(entry, extensions, result);
5779
}
5880
}
5981

@@ -206,4 +228,29 @@ auto log_verbose(const std::map<std::string, std::vector<std::string>> &options)
206228
return null_stream;
207229
}
208230

231+
auto parse_extensions(const std::map<std::string, std::vector<std::string>>
232+
&options) -> std::set<std::string> {
233+
std::set<std::string> result;
234+
235+
if (options.contains("extension")) {
236+
for (const auto &extension : options.at("extension")) {
237+
log_verbose(options) << "Using extension: " << extension << "\n";
238+
result.insert(normalize_extension(extension));
239+
}
240+
}
241+
242+
if (options.contains("e")) {
243+
for (const auto &extension : options.at("e")) {
244+
log_verbose(options) << "Using extension: " << extension << "\n";
245+
result.insert(normalize_extension(extension));
246+
}
247+
}
248+
249+
if (result.empty()) {
250+
result.insert({".json"});
251+
}
252+
253+
return result;
254+
}
255+
209256
} // namespace intelligence::jsonschema::cli

0 commit comments

Comments
 (0)