Skip to content

Commit 2943d79

Browse files
authored
atddiff: Add an option to output JSON (#370)
* Prepare for JSON output * Make JSON output more amenable to future format changes * Output JSON with '--output-format json' * Update changelog --------- Co-authored-by: Martin Jambon <[email protected]>
1 parent 6dd74b2 commit 2943d79

Some content is hidden

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

41 files changed

+773
-117
lines changed

CHANGES.md

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ Unreleased
77
* atddiff now supports options for filtering the findings based on the
88
direction of the incompatibility (`--backward`, `--forward`) or based on the
99
name of the affected types (`--types`) (#365)
10+
* atddiff: new option `--output-format json` for exporting the results to
11+
JSON (#360)
1012

1113
2.13.0 (2023-10-15)
1214
-------------------

atddiff/Makefile

+6
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,9 @@ build:
1313
.PHONY: test
1414
test:
1515
$(MAKE) -C test
16+
17+
# Update the output format of atddiff by running 'make types'.
18+
# This requires an external installation of the atdgen command.
19+
.PHONY: types
20+
types:
21+
$(MAKE) -C src/lib types

atddiff/src/bin/Atddiff_main.ml

+18
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ type conf = {
1212
filter: Atddiff.filter;
1313
json_defaults_old: bool;
1414
json_defaults_new: bool;
15+
output_format: Atddiff.output_format;
1516
exit_success: bool;
1617
version: bool;
1718
}
@@ -42,6 +43,7 @@ let run conf =
4243
~filter:conf.filter
4344
~json_defaults_old:conf.json_defaults_old
4445
~json_defaults_new:conf.json_defaults_new
46+
~output_format:conf.output_format
4547
conf.old_file conf.new_file in
4648
let exit_code, data =
4749
match out_data with
@@ -151,6 +153,19 @@ let json_defaults_new_term : bool Term.t =
151153
in
152154
Arg.value (Arg.flag info)
153155

156+
let output_format_term : Atddiff.output_format Term.t =
157+
let info =
158+
Arg.info ["output-format"; "f"]
159+
~doc:(
160+
"Output JSON instead of text. The format is specified by the file \
161+
Atddiff_output.atd that's included in the source distribution of \
162+
ATD. At the time of writing, its location is \
163+
https://github.com/ahrefs/atd/blob/master/atddiff/src/lib/Atddiff_output.atd")
164+
in
165+
Arg.value (Arg.opt (Arg.enum ["text", Atddiff.Text;
166+
"json", Atddiff.JSON])
167+
Atddiff.Text info)
168+
154169
let exit_success_term : bool Term.t =
155170
let info =
156171
Arg.info ["exit-success"]
@@ -213,6 +228,7 @@ let cmdline_term run =
213228
old_file new_file out_file
214229
backward forward types
215230
json_defaults json_defaults_old json_defaults_new
231+
output_format
216232
exit_success version =
217233
let filter =
218234
let module A = Atddiff in
@@ -239,6 +255,7 @@ let cmdline_term run =
239255
filter;
240256
json_defaults_old;
241257
json_defaults_new;
258+
output_format;
242259
exit_success;
243260
version;
244261
}
@@ -253,6 +270,7 @@ let cmdline_term run =
253270
$ json_defaults_term
254271
$ json_defaults_old_term
255272
$ json_defaults_new_term
273+
$ output_format_term
256274
$ exit_success_term
257275
$ version_term
258276
)

atddiff/src/lib/Atddiff.ml

+11-14
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
Internal Atddiff library used by the 'atddiff' command.
33
*)
44

5+
module T = Atddiff_output_t
6+
57
type simple_filter =
68
| Affected_type_name of string
79
| Backward
@@ -17,10 +19,7 @@ type output_format = Text | JSON
1719

1820
let version = Version.version
1921

20-
let format_json res : string =
21-
failwith "JSON output: not implemented"
22-
23-
let rec select_finding filter (x : Types.finding * string list) =
22+
let rec select_finding filter (x : T.full_finding) =
2423
match filter with
2524
| Or filters ->
2625
List.exists (fun filter -> select_finding filter x) filters
@@ -29,17 +28,14 @@ let rec select_finding filter (x : Types.finding * string list) =
2928
| Not filter ->
3029
not (select_finding filter x)
3130
| Filter (Affected_type_name name) ->
32-
let _, names = x in
33-
List.mem name names
31+
List.mem name x.affected_types
3432
| Filter Backward ->
35-
let finding, _ = x in
36-
(match finding.direction with
33+
(match x.finding.direction with
3734
| Backward | Both -> true
3835
| Forward -> false
3936
)
4037
| Filter Forward ->
41-
let finding, _ = x in
42-
(match finding.direction with
38+
(match x.finding.direction with
4339
| Forward | Both -> true
4440
| Backward -> false
4541
)
@@ -67,12 +63,13 @@ let compare_files
6763
} in
6864
Compare.asts options ast1 ast2
6965
in
70-
match res with
66+
match res.findings with
7167
| [] -> Ok ()
72-
| res ->
73-
let res = List.filter (select_finding filter) res in
68+
| findings ->
69+
let res : T.result =
70+
{ findings = List.filter (select_finding filter) findings } in
7471
Error (
7572
match output_format with
7673
| Text -> Format_text.to_string res
77-
| JSON -> format_json res
74+
| JSON -> Format_JSON.to_string res ^ "\n"
7875
)

atddiff/src/lib/Types.ml atddiff/src/lib/Atddiff_output.atd

+46-15
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,46 @@
11
(*
22
Type definitions used to build comparison results
3+
4+
We don't derive OCaml serializers from this file with atdgen due to
5+
circular dependencies but we derive the OCaml types by calling atdgen
6+
and keeping the result under source control:
7+
8+
atdgen -t Atddiff_output.atd
9+
10+
This provides an ATD specification to users who consume the JSON
11+
output of the atddiff.
312
*)
413

5-
type direction = Backward | Forward | Both
14+
type position = {
15+
path: string;
16+
line: int;
17+
column: int;
18+
}
19+
20+
type location = {
21+
start: position;
22+
end <ocaml name="end_">: position;
23+
}
24+
25+
type direction = [ Backward | Forward | Both ] <ocaml repr="classic">
626

7-
type incompatibility_kind =
8-
| Missing_field of { field_name: string }
9-
| Missing_variant of { variant_name: string }
10-
| Missing_variant_argument of { variant_name: string }
11-
| Default_required of { field_name: string }
27+
type field_info = {
28+
field_name: string
29+
}
30+
31+
type variant_info = {
32+
variant_name: string
33+
}
34+
35+
type incompatibility_kind = [
36+
| Missing_field of field_info
37+
| Missing_variant of variant_info
38+
| Missing_variant_argument of variant_info
39+
| Default_required of field_info
1240
| Incompatible_type
1341
| Deleted_type
1442
| Added_type
43+
] <ocaml repr="classic">
1544

1645
(*
1746
Important things we want to report:
@@ -49,20 +78,22 @@ type incompatibility_kind =
4978
type finding = {
5079
direction: direction;
5180
kind: incompatibility_kind;
52-
location_old: Atd.Ast.loc option;
53-
location_new: Atd.Ast.loc option;
81+
location_old: location option;
82+
location_new: location option;
5483

5584
(* The description should not mention the affected root type definition
5685
so as to allow the deduplication of findings. *)
5786
description: string;
5887
}
5988

60-
(*
61-
A result is a list of unique findings and the list of root types
62-
affected by the finding.
89+
type full_finding = {
90+
finding: finding;
91+
affected_types: string list;
92+
}
6393

64-
For now, we don't try to identify root type renames so each finding is
65-
associated to just one root type name which exists in one or both versions
66-
of the file.
94+
(*
95+
A result is a list of unique findings.
6796
*)
68-
type result = (finding * string list) list
97+
type result = {
98+
findings: full_finding list;
99+
}

atddiff/src/lib/Atddiff_output_t.ml

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
(* Auto-generated from "Atddiff_output.atd" *)
2+
[@@@ocaml.warning "-27-32-33-35-39"]
3+
4+
type variant_info = { variant_name: string }
5+
6+
type position = { path: string; line: int; column: int }
7+
8+
type location = { start: position; end_ (*atd end *): position }
9+
10+
type field_info = { field_name: string }
11+
12+
type incompatibility_kind =
13+
Missing_field of field_info
14+
| Missing_variant of variant_info
15+
| Missing_variant_argument of variant_info
16+
| Default_required of field_info
17+
| Incompatible_type
18+
| Deleted_type
19+
| Added_type
20+
21+
22+
type direction = Backward | Forward | Both
23+
24+
type finding = {
25+
direction: direction;
26+
kind: incompatibility_kind;
27+
location_old: location option;
28+
location_new: location option;
29+
description: string
30+
}
31+
32+
type full_finding = { finding: finding; affected_types: string list }
33+
34+
type result = { findings: full_finding list }

atddiff/src/lib/Atddiff_output_t.mli

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
(* Auto-generated from "Atddiff_output.atd" *)
2+
[@@@ocaml.warning "-27-32-33-35-39"]
3+
4+
type variant_info = { variant_name: string }
5+
6+
type position = { path: string; line: int; column: int }
7+
8+
type location = { start: position; end_ (*atd end *): position }
9+
10+
type field_info = { field_name: string }
11+
12+
type incompatibility_kind =
13+
Missing_field of field_info
14+
| Missing_variant of variant_info
15+
| Missing_variant_argument of variant_info
16+
| Default_required of field_info
17+
| Incompatible_type
18+
| Deleted_type
19+
| Added_type
20+
21+
22+
type direction = Backward | Forward | Both
23+
24+
type finding = {
25+
direction: direction;
26+
kind: incompatibility_kind;
27+
location_old: location option;
28+
location_new: location option;
29+
description: string
30+
}
31+
32+
type full_finding = { finding: finding; affected_types: string list }
33+
34+
type result = { findings: full_finding list }

0 commit comments

Comments
 (0)