blob: 9e24c3234ac103319dc59d75439671c30cdc2c4f (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
|
open Diff
module ValueS = Config_tree.ValueS
let tree_at_path path node =
(* raises:
[Vytree.Empty_path]
[Empty_comparison]
alert exn Vytree.get:
[Vytree.Empty_path] allow raise
[Vytree.Nonexistent_path] catch and raise Empty_comparison
*)
try
let node = (Vytree.get[@alert "-exn"]) node path in
Vytree.make_full Config_tree.default_data "" [node]
with Vytree.Nonexistent_path -> raise Empty_comparison
let path_to_string (path: string list) =
Printf.sprintf "[%s]\n" (Util.string_of_list path)
let marked_render mark node =
let lines = Config_tree.render_config node in
let l = String.split_on_char '\n' lines in
let m =
List.map (fun s -> if (String.length s) > 0 then mark ^ s else s) l in
String.concat "\n" m
let added_lines ?(cmds=false) node path =
(* alert exn Config_tree.render_commands:
[Vytree.Nonexistent_path] not possible on root path
*)
if not cmds then marked_render "+ " (tree_at_path path node)
else
((Config_tree.render_commands[@alert "-exn"]) ~op:Set node []) ^ "\n"
let removed_lines ?(cmds=false) node path =
(* alert exn Config_tree.render_commands:
[Vytree.Nonexistent_path] not possible on root path
*)
if not cmds then marked_render "- " (tree_at_path path node)
else
((Config_tree.render_commands[@alert "-exn"]) ~op:Delete node []) ^ "\n"
let order_commands (strl: string) =
let l = String.split_on_char '\n' strl in
let del = List.filter (fun s -> (s <> "") && (s.[0] = 'd')) l in
let set = List.filter (fun s -> (s <> "") && (s.[0] = 's')) l in
(String.concat "\n" del) ^ "\n" ^ (String.concat "\n" set) ^ "\n"
module Diff_compare = struct
type t = { left: Config_tree.t;
right: Config_tree.t;
skel: Config_tree.t;
ppath: string list;
udiff: string;
cmds: bool;
}
let make_init l r cmds = { left = l;
right = r;
skel = Config_tree.default;
ppath = [];
udiff = "";
cmds = cmds;
}
let diff_func ?descent:_ (path : string list) res (m : change) =
(* raises no exception:
clone will always be called on extant path of left or right
alert exn Vytree.get_values:
[Vytree.Empty_path] not possible as pattern Updated implies non-empty path
[Vytree.Nonexistent_path] not possible as pattern Updated implies path exists
*)
let ppath_l = Util.drop_last path
in
let ppath_s =
if (ppath_l <> res.ppath) then path_to_string ppath_l
else ""
in
let str_diff =
if not res.cmds then res.udiff ^ ppath_s
else res.udiff
in
match m with
| Added ->
let str_diff =
let add_tree = (Config_tree.clone[@alert "-exn"]) res.right res.skel path in
str_diff ^ (added_lines ~cmds:res.cmds add_tree path)
in
{ res with ppath = ppath_l; udiff = str_diff; }
| Subtracted ->
let str_diff =
let sub_tree = (Config_tree.clone[@alert "-exn"]) res.left res.skel path in
str_diff ^ (removed_lines ~cmds:res.cmds sub_tree path)
in
{ res with ppath = ppath_l; udiff = str_diff; }
| Unchanged -> res
| Updated v ->
let ov = (Config_tree.get_values[@alert "-exn"]) res.left path in
match ov, v with
| [_], [_] ->
let str_diff =
let sub_tree = (Config_tree.clone[@alert "-exn"]) res.left res.skel path in
str_diff ^ (removed_lines ~cmds:res.cmds sub_tree path)
in
let str_diff =
let add_tree = (Config_tree.clone[@alert "-exn"]) res.right res.skel path in
str_diff ^ (added_lines ~cmds:res.cmds add_tree path)
in
{ res with ppath = ppath_l; udiff = str_diff; }
| _, _ -> let ov_set = ValueS.of_list ov in
let v_set = ValueS.of_list v in
let sub_vals = ValueS.elements (ValueS.diff ov_set v_set) in
let add_vals = ValueS.elements (ValueS.diff v_set ov_set) in
let str_diff =
if not (Util.is_empty sub_vals) then
let sub_tree =
(Config_tree.clone[@alert "-exn"]) ~set_values:(Some sub_vals) res.left res.skel path
in str_diff ^ (removed_lines ~cmds:res.cmds sub_tree path)
else str_diff
in
let str_diff =
if not (Util.is_empty add_vals) then
let add_tree =
(Config_tree.clone[@alert "-exn"]) ~set_values:(Some add_vals) res.right res.skel path
in str_diff ^ (added_lines ~cmds:res.cmds add_tree path)
else str_diff
in
{ res with ppath = ppath_l; udiff = str_diff; }
end
module D = Diff(Diff_compare)
let add_empty_path src_node dest_node path =
(Config_tree.clone[@alert "-exn"]) ~descent:false ~set_values:(Some []) src_node dest_node path
let compare_at_path_maybe_empty left right path =
let left =
try
tree_at_path path left
with Empty_comparison ->
try
let left = add_empty_path right left path in
tree_at_path path left
with Vytree.Nonexistent_path ->
raise Empty_comparison
and right =
try
tree_at_path path right
with Empty_comparison ->
try
let right = add_empty_path left right path in
tree_at_path path right
with Vytree.Nonexistent_path ->
raise Empty_comparison
in (left, right)
let diff_compare ?(cmds=false) path left right =
(* raises:
[Incommensurable],
[Empty_comparison] from compare_at_path_maybe_empty
*)
if (Vytree.name_of_node left) <> (Vytree.name_of_node right) then
raise Incommensurable
else
let (left, right) =
if (path <> []) then
compare_at_path_maybe_empty left right path
else (left, right) in
let init = Diff_compare.make_init left right cmds in
let diff_strs = D.diff init left right in
let strs =
if cmds then order_commands diff_strs.udiff
else diff_strs.udiff
in
strs
|