diff options
| author | John Estabrook <jestabro@vyos.io> | 2025-10-27 22:42:37 -0500 |
|---|---|---|
| committer | John Estabrook <jestabro@vyos.io> | 2025-11-03 08:11:05 -0600 |
| commit | 2aea7ddbc092b03dbf07dce385786b9c5c16a881 (patch) | |
| tree | 3c837d8e16cb52d1d7bb85ea4afc5ffb913a81d0 /src | |
| parent | 6ced0e36b3f98675d82007f8c77193e2836c4b60 (diff) | |
| download | vyos1x-config-2aea7ddbc092b03dbf07dce385786b9c5c16a881.tar.gz vyos1x-config-2aea7ddbc092b03dbf07dce385786b9c5c16a881.zip | |
T7915: add compiler alerts and annotations
Diffstat (limited to 'src')
| -rw-r--r-- | src/config_diff.ml | 134 | ||||
| -rw-r--r-- | src/config_diff.mli | 18 | ||||
| -rw-r--r-- | src/config_file.ml | 5 | ||||
| -rw-r--r-- | src/config_tree.ml | 210 | ||||
| -rw-r--r-- | src/config_tree.mli | 36 | ||||
| -rw-r--r-- | src/generate.ml | 82 | ||||
| -rw-r--r-- | src/generate.mli | 13 | ||||
| -rw-r--r-- | src/internal.ml | 13 | ||||
| -rw-r--r-- | src/internal.mli | 15 | ||||
| -rw-r--r-- | src/parser.ml | 3 | ||||
| -rw-r--r-- | src/parser.mli | 2 | ||||
| -rw-r--r-- | src/reference_tree.ml | 208 | ||||
| -rw-r--r-- | src/reference_tree.mli | 37 | ||||
| -rw-r--r-- | src/tree_alg.ml | 24 | ||||
| -rw-r--r-- | src/tree_alg.mli | 217 | ||||
| -rw-r--r-- | src/value_checker.ml | 7 | ||||
| -rw-r--r-- | src/value_checker.mli | 1 | ||||
| -rw-r--r-- | src/vylist.mli | 10 | ||||
| -rw-r--r-- | src/vytree.ml | 86 | ||||
| -rw-r--r-- | src/vytree.mli | 38 |
20 files changed, 1046 insertions, 113 deletions
diff --git a/src/config_diff.ml b/src/config_diff.ml index 73c379b..b0e48d3 100644 --- a/src/config_diff.ml +++ b/src/config_diff.ml @@ -154,26 +154,40 @@ let rec diff (path : string list) (f : 'a diff_func) (res: 'a result) ((left_nod (* copy node paths between trees *) let rec clone_path ?(recurse=true) ?(set_values=None) old_root new_root path_done path_remaining = + (* raises: + [Vytree.Nonexistent_path] + alert exn Vytree.get: + [Vytree.Empty_path] not possible as clone_path called by clone with non-empty path + [Vytree.Nonexistent_path] allow raise + alert exn Vytree.insert: + [Vytree.Empty_path] not possible as clone_path called by clone with non-empty path + [Not_found] not possible for postion=Lexical + [Vytree.Duplicate_child] not possible as calls are on path complement + [Vytree.Insert_error] not possible as calls are on path_existing @ [name] + *) match path_remaining with | [] | [_] -> let path_total = path_done @ path_remaining in - let old_node = Vytree.get old_root path_total in + let old_node = (Vytree.get[@alert "-exn"]) old_root path_total in let data = match set_values with | Some v -> { (data_of old_node) with Config_tree.values = v } | None -> data_of old_node in if recurse then - Vytree.insert ~position:Lexical ~children:(children_of old_node) new_root path_total data + (Vytree.insert[@alert "-exn"]) ~position:Lexical ~children:(children_of old_node) new_root path_total data else - Vytree.insert ~position:Lexical new_root path_total data + (Vytree.insert[@alert "-exn"]) ~position:Lexical new_root path_total data | name :: names -> let path_done = path_done @ [name] in - let old_node = Vytree.get old_root path_done in - let new_root = Vytree.insert ~position:Lexical new_root path_done (data_of old_node) in + let old_node = (Vytree.get[@alert "-exn"]) old_root path_done in + let new_root = (Vytree.insert[@alert "-exn"]) ~position:Lexical new_root path_done (data_of old_node) in clone_path ~recurse:recurse ~set_values:set_values old_root new_root path_done names let clone ?(recurse=true) ?(set_values=None) old_root new_root path = + (* raises: + [Vytree.Nonexistent_path] from clone_path + *) match path with | [] -> if recurse then old_root else new_root | _ -> @@ -183,6 +197,12 @@ let clone ?(recurse=true) ?(set_values=None) old_root new_root path = (* define the diff_func *) let decorate_trees ?(recurse=true) (path : string list) (Diff_tree 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 + *) match m with | Added -> Diff_tree {res with add = clone res.right res.add path; } | Subtracted -> @@ -192,7 +212,7 @@ let decorate_trees ?(recurse=true) (path : string list) (Diff_tree res) (m : cha Diff_tree {res with inter = clone ~recurse:recurse res.left res.inter path; } | Updated v -> (* if in this case, node at path is guaranteed to exist *) - let ov = Config_tree.get_values res.left path in + let ov = (Config_tree.get_values[@alert "-exn"]) res.left path in match ov, v with | [_], [_] -> Diff_tree {res with sub = clone res.left res.sub path; del = clone res.left res.del path; @@ -236,13 +256,24 @@ let decorate_trees ?(recurse=true) (path : string list) (Diff_tree res) (m : cha (* get sub trees for path-relative comparison *) 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 node path in + let node = (Vytree.get[@alert "-exn"]) node path in make Config_tree.default_data "" [node] with Vytree.Nonexistent_path -> raise Empty_comparison (* call recursive diff on config_trees with decorate_trees as the diff_func *) let compare path left right = + (* raises: + [Empty_comparison] from tree_at_path + [Incommensurable] + *) if (name_of left) <> (name_of right) then raise Incommensurable else @@ -254,6 +285,10 @@ let compare path left right = (* wrapper to return diff trees *) let diff_tree path left right = + (* raises: + [Incommensurable], + [Empty_comparison] from compare + *) let trees = compare path left right in let add_node = make Config_tree.default_data "add" (children_of (trees.add)) in let sub_node = make Config_tree.default_data "sub" (children_of (trees.sub)) in @@ -266,9 +301,23 @@ let diff_tree path left right = we need a hybrid tree between the 'del' tree and the 'sub' tree, namely: in case the del tree has a terminal tag node (== all tag values have been removed) add tag node values for proper removal in commit execution - *) + *) let get_tagged_delete_tree dt = + (* alert exn Config_tree.is_tag: + [Vytree.Empty_path] not possible in pattern non-empty path + [Vytree.Nonexistent_path] not possible in fold_tree_with_path + alert exn Vytree.is_terminal_path: + [Vytree.Empty_path] not possible in pattern non-empty path + alert exn Vytree.children_of_path: + [Vytree.Empty_path] not possible in pattern non-empty path + [Vytree.Nonexistent_path] not possible in super-tree of fold_tree_with_path arg + alert exn Vytree.insert: + [Vytree.Empty_path]: not possible since called on pattern path non-empty + [Not_found]: not possible for postion=Lexical + [Vytree.Duplicate_child]: not possible by condition is_terminal_path + [Vytree.Insert_error]: not possible since constructed iteratively from existing path + *) let del_tree = Config_tree.get_subtree dt ["del"] in let sub_tree = Config_tree.get_subtree dt ["sub"] in let f (p, a) _t = @@ -276,10 +325,10 @@ let get_tagged_delete_tree dt = match q with | [] -> (p, a) | _ -> - if Config_tree.is_tag a q && Vytree.is_terminal_path a q then - let children = Vytree.children_of_path sub_tree q in + if (Config_tree.is_tag[@alert "-exn"]) a q && (Vytree.is_terminal_path[@alert "-exn"]) a q then + let children = (Vytree.children_of_path[@alert "-exn"]) sub_tree q in let insert_child path node name = - Vytree.insert ~position:Lexical node (path @ [name]) Config_tree.default_data + (Vytree.insert[@alert "-exn"]) ~position:Lexical node (path @ [name]) Config_tree.default_data in let a' = List.fold_left (insert_child q) a children in (p, a') @@ -307,14 +356,20 @@ let marked_render mark node = 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 ~op:Set node []) ^ "\n" + ((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 ~op:Delete node []) ^ "\n" + ((Config_tree.render_commands[@alert "-exn"]) ~op:Delete node []) ^ "\n" let order_commands (strl: string) = let l = String.split_on_char '\n' strl in @@ -323,6 +378,12 @@ let order_commands (strl: string) = (String.concat "\n" del) ^ "\n" ^ (String.concat "\n" set) ^ "\n" let unified_diff ?(cmds=false) ?recurse:_ (path : string list) (Diff_string 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 = list_but_last path in let ppath_s = @@ -348,7 +409,7 @@ let unified_diff ?(cmds=false) ?recurse:_ (path : string list) (Diff_string res) Diff_string { res with ppath = ppath_l; udiff = str_diff; } | Unchanged -> Diff_string (res) | Updated v -> - let ov = Config_tree.get_values res.left path in + let ov = (Config_tree.get_values[@alert "-exn"]) res.left path in match ov, v with | [_], [_] -> let str_diff = @@ -405,6 +466,10 @@ let compare_at_path_maybe_empty left right path = in (left, right) let show_diff ?(cmds=false) path left right = + (* raises: + [Incommensurable], + [Empty_comparison] from compare_at_path_maybe_empty + *) if (name_of left) <> (name_of right) then raise Incommensurable else @@ -425,19 +490,34 @@ let show_diff ?(cmds=false) path left right = (* mask function; mask applied on right *) let mask_func ?recurse:_ (path : string list) (Diff_tree res) (m : change) = + (* alert exn Vytree.delete: + [Vytree.Empty_path] not possible since Unchanged pattern is only empty path + [Vytree.Nonexistent_path] not possible as called on existing config paths (res.left) + alert exn Vytree.is_terminal_path: + [Vytree.Empty_path] not possible since Unchanged pattern is only empty path + *) match m with | Added -> Diff_tree (res) | Subtracted -> (match path with - | [_] -> Diff_tree {res with left = Vytree.delete res.left path} - | _ -> if not (Vytree.is_terminal_path res.right (list_but_last path)) then - Diff_tree {res with left = Vytree.delete res.left path} - else Diff_tree (res)) + | [_] -> + Diff_tree {res with left = (Vytree.delete[@alert "-exn"]) res.left path} + | _ -> + if not ((Vytree.is_terminal_path[@alert "-exn"]) res.right (list_but_last path)) then + Diff_tree {res with left = (Vytree.delete[@alert "-exn"]) res.left path} + else Diff_tree (res)) | Unchanged -> Diff_tree (res) | Updated _ -> Diff_tree (res) (* call recursive diff with mask_func; mask applied on right *) let mask_tree left right = + (* raises: + [Empty_comparison] from diff + [Incommensurable] + *) + if (name_of left) <> (name_of right) then + raise Incommensurable + else let trees = make_diff_trees left right in let d = diff [] mask_func trees (Option.some left, Option.some right) in @@ -450,14 +530,28 @@ let union_of_values (n : Config_tree.t) (m : Config_tree.t) = ValueS.elements (ValueS.union set_n set_m) let tree_union s t = + (* raises: + [Tree_alg.Incompatible_union] + [Tree_alg.Nonexistent_child] should not be reachable + alert exn Tree_alg.ConfigAlg.tree_union: + [Tree_alg.Incompatible_union] allow raise + [Tree_alg.Nonexistent_child] allow raise; should not be reachable + *) let f u v = let values = union_of_values u v in let data = {(data_of v) with Config_tree.values = values} in Vytree.make_full data (name_of v) (children_of v) in - Tree_alg.ConfigAlg.tree_union s t f + (Tree_alg.ConfigAlg.tree_union[@alert "-exn"]) s t f let tree_merge ?(destructive=false) s t = + (* raises: + [Tree_alg.Incompatible_union] + [Tree_alg.Nonexistent_child] should not be reachable + alert exn Tree_alg.ConfigAlg.tree_union: + [Tree_alg.Incompatible_union] allow raise + [Tree_alg.Nonexistent_child] allow raise; should not be reachable + *) let f u v = let data = match destructive with @@ -465,4 +559,4 @@ let tree_merge ?(destructive=false) s t = | true -> data_of v in Vytree.make_full data (name_of v) (children_of v) in - Tree_alg.ConfigAlg.tree_union s t f + (Tree_alg.ConfigAlg.tree_union[@alert "-exn"]) s t f diff --git a/src/config_diff.mli b/src/config_diff.mli index fdc662c..98e0ee3 100644 --- a/src/config_diff.mli +++ b/src/config_diff.mli @@ -42,10 +42,28 @@ exception Empty_comparison exception Nonexistent_child val clone : ?recurse:bool -> ?set_values:string list option -> Config_tree.t -> Config_tree.t ->string list -> Config_tree.t +[@@alert exn "Vytree.Nonexistent_path"] + val diff_tree : string list -> Config_tree.t -> Config_tree.t -> Config_tree.t +[@@alert exn "Config_diff.Incommensurable"] +[@@alert exn "Config_diff.Empty_comparison"] + val show_diff : ?cmds:bool -> string list -> Config_tree.t -> Config_tree.t -> string +[@@alert exn "Config_diff.Incommensurable"] +[@@alert exn "Config_diff.Empty_comparison"] + val tree_union : Config_tree.t -> Config_tree.t -> Config_tree.t +[@@alert exn "Tree_alg.Incompatible_union"] +[@@alert exn "Tree_alg.Nonexistent_child"] + val tree_merge : ?destructive:bool -> Config_tree.t -> Config_tree.t -> Config_tree.t +[@@alert exn "Tree_alg.Incompatible_union"] +[@@alert exn "Tree_alg.Nonexistent_child"] + val mask_tree : Config_tree.t -> Config_tree.t -> Config_tree.t +[@@alert exn "Config_diff.Incommensurable"] +[@@alert exn "Config_diff.Empty_comparison"] + val make_diff_cstore : Config_tree.t -> Config_tree.t -> int -> Diff_cstore.t result + val get_tagged_delete_tree : Config_tree.t -> Config_tree.t diff --git a/src/config_file.ml b/src/config_file.ml index abf08ef..548f03a 100644 --- a/src/config_file.ml +++ b/src/config_file.ml @@ -27,6 +27,9 @@ let strip_version s = | [] -> Error "Failure stripping version string from config" let load_config file = + (* alert exn Parser.from_string: + [Util.Syntax_error] caught + *) try let chan = open_in file in let s = really_input_string chan (in_channel_length chan) in @@ -36,7 +39,7 @@ let load_config file = | Ok t -> escape_backslash t | Error msg -> raise (Sys_error msg) in - let config = Parser.from_string s in + let config = (Parser.from_string[@alert "-exn"]) s in Ok config with | Sys_error msg -> Error msg diff --git a/src/config_tree.ml b/src/config_tree.ml index 8c9e783..425ddf1 100644 --- a/src/config_tree.ml +++ b/src/config_tree.ml @@ -32,38 +32,84 @@ let op_to_string op = | Delete -> "delete" let replace_value node path value = - let data = {default_data with values=[value]; leaf=true} in - Vytree.update node path data + (* alert exn Vytree.update: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let data = {default_data with values=[value]; leaf=true} in + (Vytree.update[@alert "-exn"]) node path data let add_value node path value = - let node' = Vytree.get node path in - let data = Vytree.data_of_node node' in - let values = data.values in - match (Vylist.find (fun x -> x = value) values) with - | Some _ -> raise Duplicate_value - | None -> + (* alert exn Vytree.get; Vytree.update: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let node' = (Vytree.get[@alert "-exn"]) node path in + let data = Vytree.data_of_node node' in + let values = data.values in + match (Vylist.find (fun x -> x = value) values) with + | Some _ -> raise Duplicate_value + | None -> let values = values @ [value] in - Vytree.update node path ({data with values=values; leaf=true}) + (Vytree.update[@alert "-exn"]) node path ({data with values=values; leaf=true}) let delete_value node path value = - let data = Vytree.data_of_node @@ Vytree.get node path in + (* alert exn Vytree.update: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let data = Vytree.data_of_node @@ (Vytree.get[@alert "-exn"]) node path in let values = Vylist.remove (fun x -> x = value) data.values in - Vytree.update node path {data with values=values; leaf=true} + (Vytree.update[@alert "-exn"]) node path {data with values=values; leaf=true} let set_value node path value behaviour = + (* raises + [Config_tree.Duplicate_value] from add_value + [Vytree.Empty_path], + [Vytree.Nonexistent_path] from add_value; replace_value + *) match behaviour with | AddValue -> add_value node path value | ReplaceValue -> replace_value node path value let create_node node path = - if (Vytree.exists node path) then raise Useless_set + (* raises + [Vytree.Empty_path] + [Useless_set] + alert exn Vytree.exists: + [Vytree.Empty_path] check and raise + alert exn Vytree.insert_multi_level + [Vytree.Empty_path] check and raise + [Not_found] not possible since position=Default + [Vytree.Duplicate_child] not possible since path_remaining is complement + [Vytree.Insert_error] not possible since path_existing exists + *) + if Util.is_empty path then raise Vytree.Empty_path + else + if ((Vytree.exists[@alert "-exn"]) node path) then raise Useless_set else let path_existing = Vytree.get_existent_path node path in let path_remaining = Vylist.complement path path_existing in - Vytree.insert_multi_level default_data node path_existing path_remaining default_data + (Vytree.insert_multi_level[@alert "-exn"]) default_data node path_existing path_remaining default_data let set node path value behaviour = - if (Vytree.exists node path) then + (* raises: + [Config_tree.Duplicate_value], + [Vytree.Empty_path] + [Useless_set] + avoids: + [Vytree.Nonexistent_path] from set_value, since called if Vytree.exists + alert exn Vytree.exists: + [Vytree.Empty_path] check and raise + alert exn Vytree.insert_muilt_level: + [Vytree.Empty_path] check and raise + [Not_found] not possible since position=Default + [Vytree.Duplicate_child] not possible since path_remaining is complement + [Vytree.Insert_error] not possible since path_existing exists + *) + if Util.is_empty path then raise Vytree.Empty_path + else + if ((Vytree.exists[@alert "-exn"]) node path) then (match value with | None -> raise Useless_set | Some v -> set_value node path v behaviour) @@ -72,69 +118,134 @@ let set node path value behaviour = let path_remaining = Vylist.complement path path_existing in let values = match value with None -> [] | Some v -> [v] in let end_data = {default_data with values=values; leaf=true} in - Vytree.insert_multi_level ~position:Lexical default_data node path_existing path_remaining end_data + (* alert exn Vytree.insert_muilt_level: + [Vytree.Empty_path] allow raise of Vytree.Empty_path + [Not_found] not possible since position=Default + [Vytree.Duplicate_child] not possible since path_remaining is complement + [Vytree.Insert_error] not possible since path_existing exists + *) + (Vytree.insert_multi_level[@alert "-exn"]) ~position:Lexical default_data node path_existing path_remaining end_data let get_values node path = - let node' = Vytree.get node path in + (* alert exn Vytree.get: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let node' = (Vytree.get[@alert "-exn"]) node path in let data = Vytree.data_of_node node' in data.values let get_value node path = + (* raises + [Vytree.Empty_path], + [Vytree.Nonexistent_path] from get_values + [Node_has_no_value] + *) let values = get_values node path in match values with | [] -> raise Node_has_no_value | x :: _ -> x let value_exists node path value = - if not (Vytree.exists node path) then false - else let node' = Vytree.get node path in + (* alert exn Vytree.get: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] checked + *) + if not ((Vytree.exists[@alert "-exn"]) node path) then false + else let node' = (Vytree.get[@alert "-exn"]) node path in let data = Vytree.data_of_node node' in Vylist.in_list data.values value let delete node path value = + (* raises: + [Vytree.Nonexistent_path] from get_values; delete_value + [Vytree.Empty_path] + [No_such_value] + + alert exn Vytree.delete + [Vytree.Empty_path] check and raise + [Vytree.Nonexistent_path] allow raise + *) + if Util.is_empty path then raise Vytree.Empty_path + else match value with | Some v -> (let values = get_values node path in if Vylist.in_list values v then (match values with - | [_] -> Vytree.delete node path + | [_] -> (Vytree.delete[@alert "-exn"]) node path | _ -> delete_value node path v) else raise No_such_value) | None -> - Vytree.delete node path + (Vytree.delete[@alert "-exn"]) node path let set_comment node path comment = - let data = Vytree.get_data node path in - Vytree.update node path {data with comment=comment} + (* alert exn Vytree.get_data; Vytree.update: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let data = (Vytree.get_data[@alert "-exn"]) node path in + (Vytree.update[@alert "-exn"]) node path {data with comment=comment} let get_comment node path = - let data = Vytree.get_data node path in + (* alert exn Vytree.get_data: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let data = (Vytree.get_data[@alert "-exn"]) node path in data.comment let set_tag node path tag = - let data = Vytree.get_data node path in - Vytree.update node path {data with tag=tag} + (* alert exn Vytree.get_data; Vytree.update: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let data = (Vytree.get_data[@alert "-exn"]) node path in + (Vytree.update[@alert "-exn"]) node path {data with tag=tag} let is_tag node path = - let data = Vytree.get_data node path in + (* alert exn Vytree.get_data: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let data = (Vytree.get_data[@alert "-exn"]) node path in data.tag let is_tag_value node path = + (* raises + [Vytree.Empty_path], + [Vytree.Nonexistent_path] from is_tag + *) match path with | [] | [_] -> false | _ -> is_tag node (Util.drop_last path) let set_leaf node path leaf = - let data = Vytree.get_data node path in - Vytree.update node path {data with leaf=leaf} + (* alert exn Vytree.get_data; Vytree.update: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let data = (Vytree.get_data[@alert "-exn"]) node path in + (Vytree.update[@alert "-exn"]) node path {data with leaf=leaf} let is_leaf node path = - let data = Vytree.get_data node path in + (* alert exn Vytree.get_data: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let data = (Vytree.get_data[@alert "-exn"]) node path in data.leaf let get_subtree ?(with_node=false) node path = + (* alert exn Vytree.get: + [Vytree.Empty_path] checked + [Vytree.Nonexistent_path] caught + *) + match path with + | [] -> node + | _ -> try - let n = Vytree.get node path in + let n = (Vytree.get[@alert "-exn"]) node path in if with_node then Vytree.make_full default_data "" [n] else @@ -142,12 +253,15 @@ let get_subtree ?(with_node=false) node path = with Vytree.Nonexistent_path -> make "" let value_paths_of_tree node = + (* alert exn Vytree.is_terminal_path: + [Vytree.Empty_path] not possible in pattern path non-empty + *) let func ct (p, a) _t = match p with | [] -> (p, a) | _ -> let q = List.rev p in - if not (Vytree.is_terminal_path ct q) then + if not ((Vytree.is_terminal_path[@alert "-exn"]) ct q) then (p, a) else let vals = get_values ct q in @@ -163,9 +277,17 @@ let value_paths_of_tree node = in List.rev (snd (Vytree.fold_tree_with_path (func node) ([], []) node)) let prune_delete node path = + (* raises: + [Vytree.Nonexistent_path] from is_tag_value; delete + [Vytree.Empty_path] + alert exn Vytree.is_terminal_path: + [Vytree.Empty_path] check and raise + *) + if Util.is_empty path then raise Vytree.Empty_path + else if is_tag_value node path then let tag_path = Util.drop_last path in - let terminal = Vytree.is_terminal_path node tag_path in + let terminal = (Vytree.is_terminal_path[@alert "-exn"]) node tag_path in match terminal with | true -> delete node tag_path None | false -> node @@ -180,6 +302,10 @@ struct List.append path [v] |> String.concat " " |> Printf.sprintf "%s %s" (op_to_string op) let rec render_commands ?(op=Set) path ct = + (* alert exn Vytree.get: + [Vytree.Empty_path] not possible as called on non-empty children + [Vytree.Nonexistent_path] not possible as called on non-empty children + *) let new_path = List.append path [Vytree.name_of_node ct] in let new_path_str = String.concat " " new_path in let data = Vytree.data_of_node ct in @@ -211,7 +337,9 @@ struct in if comment_cmd = "" then cmds else Printf.sprintf "%s\n%s" cmds comment_cmd | _ :: _ -> - let children = List.map (fun n -> Vytree.get ct [n]) child_names in + let children = + List.map (fun n -> (Vytree.get[@alert "-exn"]) ct [n]) child_names + in let rendered_children = List.map (render_commands ~op:op new_path) children in let cmds = String.concat "\n" rendered_children in if comment_cmd = "" then cmds else Printf.sprintf "%s\n%s" cmds comment_cmd @@ -320,10 +448,16 @@ module JSONRenderer = struct end (* JSONRenderer *) let render_commands ?(op=Set) node path = + (* raises: + [Vytree.Nonexistent_path] + alert exn Vytree.get: + [Vytree.Empty_path] not possible as called on pattern non-empty path + [Vytree.Nonexistent_path] allow raise + *) let node = match path with | [] -> node - | _ -> Vytree.get node path + | _ -> (Vytree.get[@alert "-exn"]) node path in let children = Vytree.children_of_node node in let commands = List.map (Renderer.render_commands ~op:op path) children in @@ -332,10 +466,14 @@ let render_commands ?(op=Set) node path = let render_config ?(ord_val=false) = Renderer.render_config ~ord_val:ord_val let render_at_level node path = - let node = + (* alert exn Vytree.get: + [Vytree.Empty_path] not possible as called on pattern non-empty path + [Vytree.Nonexistent_path] allow raise + *) + let node = match path with | [] -> node - | _ -> Vytree.get node path + | _ -> (Vytree.get[@alert "-exn"]) node path in render_config node diff --git a/src/config_tree.mli b/src/config_tree.mli index 5d3db3c..c320186 100644 --- a/src/config_tree.mli +++ b/src/config_tree.mli @@ -22,38 +22,73 @@ val default : t val make : string -> t val create_node : t -> string list -> t +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Config_tree.Useless_set"] val set : t -> string list -> string option -> value_behaviour -> t +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Config_tree.Useless_set"] +[@@alert exn "Config_tree.Duplicate_value"] val delete : t -> string list -> string option -> t +[@@alert exn "Vytree.Nonexistent_path"] +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Config_tree.No_such_value"] + +val replace_value : t -> string list -> string -> t +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val prune_delete : t -> string list -> t +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val get_values : t -> string list -> string list +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val get_value : t -> string list -> string +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] +[@@alert exn "Config_tree.Node_has_no_value"] val value_exists : t -> string list -> string -> bool +[@@alert exn "Vytree.Empty_path"] val set_comment : t -> string list -> string option -> t +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val get_comment : t -> string list -> string option +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val set_tag : t -> string list -> bool -> t +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val is_tag : t -> string list -> bool +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val is_tag_value : t -> string list -> bool +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val set_leaf : t -> string list -> bool -> t +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val is_leaf : t -> string list -> bool +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val get_subtree : ?with_node:bool -> t -> string list -> t val value_paths_of_tree : t -> string list list val render_commands : ?op:command -> t -> string list -> string +[@@alert exn "Vytree.Nonexistent_path"] val render_config : ?ord_val:bool -> t -> string @@ -62,3 +97,4 @@ val render_json : t -> string val render_json_ast : t -> string val render_at_level : t -> string list -> string +[@@alert exn "Vytree.Nonexistent_path"] diff --git a/src/generate.ml b/src/generate.ml index 28f0be1..d4d3f0e 100644 --- a/src/generate.ml +++ b/src/generate.ml @@ -1,10 +1,14 @@ (* Load interface definitions from a directory into a reference tree *) + exception Load_error of string exception Write_error of string module I = Internal.Make(Reference_tree) let load_interface_definitions dir = + (* alert exn Reference_tree.load_from_xml: + [Reference_tree.Bad_interface_definition] caught + *) let open Reference_tree in let dir_paths = FileUtil.ls dir in let relative_paths = @@ -15,7 +19,7 @@ let load_interface_definitions dir = with Sys_error no_dir_msg -> Error no_dir_msg in let load_aux tree file = - load_from_xml tree file + (load_from_xml[@alert "-exn"]) tree file in try begin match absolute_paths with | Ok paths -> Ok (List.fold_left load_aux default paths) @@ -23,6 +27,11 @@ let load_interface_definitions dir = with Bad_interface_definition msg -> Error msg let interface_definitions_to_cache from_dir cache_path = + (* raises: + [Write_error] + alert exn Internal.write_internal: + [Internecl.Write_error] caught + *) let ref_tree_result = load_interface_definitions from_dir in @@ -31,11 +40,21 @@ let interface_definitions_to_cache from_dir cache_path = | Ok ref -> ref | Error msg -> raise (Load_error msg) in - I.write_internal ref_tree cache_path + try + (I.write_internal[@alert "-exn"]) ref_tree cache_path + with Internal.Write_error msg -> raise (Write_error msg) let reference_tree_cache_to_json cache_path render_file = + (* raises: + [Load_error] + [Write_error] + alert exn Internal.read_internal: + [Internal.Read_error] caught + *) let ref_tree = - I.read_internal cache_path + try + (I.read_internal[@alert "-exn"]) cache_path + with Internal.Read_error msg -> raise (Load_error msg) in let out = Reference_tree.render_json ref_tree in let oc = @@ -47,23 +66,65 @@ let reference_tree_cache_to_json cache_path render_file = close_out oc let merge_reference_tree_cache cache_dir primary_name result_name = + (* raises: + [Tree_alg.Incompatible_union], + [Tree_alg.Nonexistent_child] from Tree_alg.RefAlg.tree_union + [Load_error] + [Write_error] + alert exn Internal.read_internal: + [Internal.Read_error] caught + alert exn Internal.write_internal: + [Internal.Write_error] caught + alert exn Tree_alg.RefAlg.tree_union: + [Tree_alg.Incompatible_union] allow raise + [Tree_alg.Nonexistent_child] allow raise + *) let file_arr = Sys.readdir cache_dir in let file_list' = Array.to_list file_arr in let file_list = List.filter (fun x -> x <> primary_name && x <> result_name) file_list' in let file_path_list = List.map (FilePath.concat cache_dir) file_list in - let primary_tree = I.read_internal (FilePath.concat cache_dir primary_name) in - let ref_trees = List.map I.read_internal file_path_list in + let primary_tree = + try + (I.read_internal[@alert "-exn"]) (FilePath.concat cache_dir primary_name) + with Internal.Read_error msg -> raise (Load_error msg) + in + let ref_trees = + try + List.map (I.read_internal[@alert "-exn"]) file_path_list + with Internal.Read_error msg -> raise (Load_error msg) + in match ref_trees with | [] -> - I.write_internal primary_tree (FilePath.concat cache_dir result_name) + begin + try + (I.write_internal[@alert "-exn"]) + primary_tree + (FilePath.concat cache_dir result_name) + with Internal.Write_error msg -> raise (Write_error msg) + end | _ -> let f _ v = v in - let res = List.fold_left (fun p r -> Tree_alg.RefAlg.tree_union r p f) primary_tree ref_trees in - I.write_internal res (FilePath.concat cache_dir result_name) + let res = + List.fold_left + (fun p r -> (Tree_alg.RefAlg.tree_union[@alert "-exn"]) r p f) + primary_tree + ref_trees + in + try + (I.write_internal[@alert "-exn"]) + res + (FilePath.concat cache_dir result_name) + with Internal.Write_error msg -> raise (Write_error msg) let reference_tree_to_json ?(internal_cache="") from_dir to_file = + (* raises: + [Load_error] + [Write_error] + alert exn Internal.write_internal: + [Internal.Write_error] caught + *) let ref_tree_result = load_interface_definitions from_dir in @@ -82,4 +143,7 @@ let reference_tree_to_json ?(internal_cache="") from_dir to_file = close_out oc; match internal_cache with | "" -> () - | _ -> I.write_internal ref_tree internal_cache + | _ -> + try + (I.write_internal[@alert "-exn"]) ref_tree internal_cache + with Internal.Write_error msg -> raise (Write_error msg) diff --git a/src/generate.mli b/src/generate.mli index 4243ef0..3fe0c60 100644 --- a/src/generate.mli +++ b/src/generate.mli @@ -2,7 +2,20 @@ exception Load_error of string exception Write_error of string val load_interface_definitions : string -> (Reference_tree.t, string) result + val reference_tree_to_json : ?internal_cache:string -> string -> string -> unit +[@@alert exn "Generate.Load_error"] +[@@alert exn "Generate.Write_error"] + val interface_definitions_to_cache : string -> string -> unit +[@@alert exn "Generate.Write_error"] + val reference_tree_cache_to_json : string -> string -> unit +[@@alert exn "Generate.Load_error"] +[@@alert exn "Generate.Write_error"] + val merge_reference_tree_cache : string -> string -> string -> unit +[@@alert exn "Generate.Load_error"] +[@@alert exn "Generate.Write_error"] +[@@alert exn "Tree_alg.Incompatible_union"] +[@@alert exn "Tree_alg.Nonexistent_child"] diff --git a/src/internal.ml b/src/internal.ml index cd7e968..bb3ab66 100644 --- a/src/internal.ml +++ b/src/internal.ml @@ -17,16 +17,27 @@ module type T = module type FI = functor (M: T) -> sig val write_internal : M.t -> string -> unit + [@@alert exn "Internal.Write_error"] val write_internal_atomic : M.t -> string -> unit + [@@alert exn "Internal.Write_error"] val read_internal : string -> M.t + [@@alert exn "Internal.Read_error"] val replace_internal : string -> string -> unit + [@@alert exn "Internal.Write_error"] end module Make : FI = functor (M: T) -> struct let write_internal x file_name = let yt = M.to_yojson x in let ys = Yojson.Safe.to_string yt in - let fd = Unix.openfile file_name [Unix.O_CREAT;Unix.O_WRONLY] 0o664 in + let fd = + try + Unix.openfile file_name [Unix.O_CREAT;Unix.O_WRONLY] 0o664 + with Unix.Unix_error (e,f,p) -> + let out = + Printf.sprintf "%s %s: %s" (Unix.error_message e) f p + in raise (Write_error out) + in let oc = Unix.out_channel_of_descr fd in let () = Unix.ftruncate fd 0 in let () = Printf.fprintf oc "%s" ys in diff --git a/src/internal.mli b/src/internal.mli index 4fc9b10..030bdaf 100644 --- a/src/internal.mli +++ b/src/internal.mli @@ -11,10 +11,17 @@ module type T = module type FI = functor (M : T) -> sig - val write_internal : M.t -> string -> unit - val write_internal_atomic : M.t -> string -> unit - val read_internal : string -> M.t - val replace_internal : string -> string -> unit + val write_internal : M.t -> string -> unit + [@@alert exn "Internal.Write_error"] + + val write_internal_atomic : M.t -> string -> unit + [@@alert exn "Internal.Write_error"] + + val read_internal : string -> M.t + [@@alert exn "Internal.Read_error"] + + val replace_internal : string -> string -> unit + [@@alert exn "Internal.Write_error"] end module Make : FI diff --git a/src/parser.ml b/src/parser.ml index f659b34..e1c86e6 100644 --- a/src/parser.ml +++ b/src/parser.ml @@ -23,6 +23,9 @@ let rec parse vy_inside_node lexbuf (checkpoint : Config_tree.t I.checkpoint) = raise (Syntax_error (None, "invalid syntax (parser rejected the input)")) let from_string s = + (* raises: + [Syntax_error] from parse + *) let vy_inside_node = false in let lexbuf = Lexing.from_string s in parse vy_inside_node lexbuf (Vyos1x_parser.Incremental.config lexbuf.lex_curr_p) diff --git a/src/parser.mli b/src/parser.mli new file mode 100644 index 0000000..15813a4 --- /dev/null +++ b/src/parser.mli @@ -0,0 +1,2 @@ +val from_string : string -> Config_tree.t +[@@alert exn "Util.Syntax_error"] diff --git a/src/reference_tree.ml b/src/reference_tree.ml index 794fe8f..5107eb8 100644 --- a/src/reference_tree.ml +++ b/src/reference_tree.ml @@ -224,6 +224,13 @@ let data_from_xml d x = in Xml.fold aux d x let rec insert_from_xml basepath reftree xml = + (* raises: + [Bad_interface_definition] + alert exn Vytree.insert_or_update; Vytree.insert_maybe: + [Vytree.Empty_path] not possible as all nodes have nodeNameAttr by schema + [Not_found] not possible for position=Default + [Vytree.Insert_error] not possible for recursive fold over children + *) match xml with | Xml.Element ("syntaxVersion", _, _) -> reftree | Xml.Element (_, _, _) -> @@ -248,9 +255,9 @@ let rec insert_from_xml basepath reftree xml = let path = basepath @ [name] in let new_tree = if data <> default_data then - Vytree.insert_or_update reftree path data + (Vytree.insert_or_update[@alert "-exn"]) reftree path data else - Vytree.insert_maybe reftree path data + (Vytree.insert_maybe[@alert "-exn"]) reftree path data in (match node_type with | Leaf -> new_tree @@ -262,6 +269,9 @@ let rec insert_from_xml basepath reftree xml = | _ -> raise (Bad_interface_definition "PCData not allowed here") let load_from_xml reftree file = + (* raises: + [Bad_interface_definition] from insert_from_xml and explicit + *) let xml_to_reftree xml reftree = match xml with | Xml.Element ("interfaceDefinition", _, children) -> @@ -306,6 +316,9 @@ let format_out l = doesn't exist in the reference tree *) let validate_path validators_dir node path = + (* raises: + [Validation_error] + *) let show_path p = Printf.sprintf "[%s]" @@ Util.string_of_list (List.rev p) in @@ -452,57 +465,139 @@ let split_path node path = in aux node path [] let is_multi reftree path = - let data = Vytree.get_data reftree path in + (* raises: + [Vytree.Empty_path] + [Vytree.Nonexistent_path] + alert exn Vytree.get_data: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let data = (Vytree.get_data[@alert "-exn"]) reftree path in data.multi let is_hidden reftree path = - let data = Vytree.get_data reftree path in + (* raises: + [Vytree.Empty_path] + [Vytree.Nonexistent_path] + alert exn Vytree.get_data: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let data = (Vytree.get_data[@alert "-exn"]) reftree path in data.hidden let is_secret reftree path = - let data = Vytree.get_data reftree path in + (* raises: + [Vytree.Empty_path] + [Vytree.Nonexistent_path] + alert exn Vytree.get_data: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let data = (Vytree.get_data[@alert "-exn"]) reftree path in data.secret let is_tag reftree path = - let data = Vytree.get_data reftree path in + (* raises: + [Vytree.Empty_path] + [Vytree.Nonexistent_path] + alert exn Vytree.get_data: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let data = (Vytree.get_data[@alert "-exn"]) reftree path in match data.node_type with | Tag -> true | _ -> false let is_leaf reftree path = - let data = Vytree.get_data reftree path in + (* raises: + [Vytree.Empty_path] + [Vytree.Nonexistent_path] + alert exn Vytree.get_data: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let data = (Vytree.get_data[@alert "-exn"]) reftree path in match data.node_type with | Leaf -> true | _ -> false let is_valueless reftree path = - let data = Vytree.get_data reftree path in + (* raises: + [Vytree.Empty_path] + [Vytree.Nonexistent_path] + alert exn Vytree.get_data: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let data = (Vytree.get_data[@alert "-exn"]) reftree path in data.valueless let get_owner reftree path = - let data = Vytree.get_data reftree path in + (* raises: + [Vytree.Empty_path] + [Vytree.Nonexistent_path] + alert exn Vytree.get_data: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let data = (Vytree.get_data[@alert "-exn"]) reftree path in data.owner let get_priority reftree path = - let data = Vytree.get_data reftree path in + (* raises: + [Vytree.Empty_path] + [Vytree.Nonexistent_path] + alert exn Vytree.get_data: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let data = (Vytree.get_data[@alert "-exn"]) reftree path in data.priority let get_help_string reftree path = - let data = Vytree.get_data reftree path in + (* raises: + [Vytree.Empty_path] + [Vytree.Nonexistent_path] + alert exn Vytree.get_data: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let data = (Vytree.get_data[@alert "-exn"]) reftree path in data.help let get_value_help reftree path = - let data = Vytree.get_data reftree path in + (* raises: + [Vytree.Empty_path] + [Vytree.Nonexistent_path] + alert exn Vytree.get_data: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let data = (Vytree.get_data[@alert "-exn"]) reftree path in data.value_help let get_completion_data reftree path = + (* raises: + [Vytree.Empty_path] + [Vytree.Nonexistent_path] + alert exn Vytree.get: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) let aux node = let data = Vytree.data_of_node node in (data.node_type, data.multi, data.help) - in List.map aux (Vytree.children_of_node @@ Vytree.get reftree path) + in + List.map aux (Vytree.children_of_node @@ (Vytree.get[@alert "-exn"]) reftree path) (* Convert from config path to reference tree path *) let refpath reftree path = + (* raises: + [Vytree.Empty_path], + [Vytree.Nonexistent_path] from is_tag + *) let rec aux acc p = match acc, p with | [], h :: tl -> aux (acc @ [h]) tl @@ -520,30 +615,55 @@ let flag path = List.mapi (fun k _ -> aux path k) path let set_tag_data rtree ctree path = - let ext = Vytree.exists ctree path in + (* raises: + [Vytree.Empty_path], + [Vytree.Nonexistent_path] from refpath; is_tag; and + alert exn Vytree.exists: + [Vytree.Empty_path] allow raise + alert exn Config_tree.is_tag_value; Config_tree.set_tag: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let ext = (Vytree.exists[@alert "-exn"]) ctree path in match ext with | false -> ctree | true -> let set_tag rt ct p = let refp = refpath rt p in - if is_tag rt refp && not (Config_tree.is_tag_value ct p) - then Config_tree.set_tag ct p true + if is_tag rt refp && not ((Config_tree.is_tag_value[@alert "-exn"]) ct p) + then (Config_tree.set_tag[@alert "-exn"]) ct p true else ct in List.fold_left (set_tag rtree) ctree (flag path) let set_leaf_data rtree ctree path = - let ext = Vytree.exists ctree path in + (* raises: + [Vytree.Empty_path], + [Vytree.Nonexistent_path] from refpath; is_leaf; and + alert exn Vytree.exists: + [Vytree.Empty_path] allow raise + alert exn Config_tree.set_leaf: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) + let ext = (Vytree.exists[@alert "-exn"]) ctree path in match ext with | false -> ctree | true -> let refp = refpath rtree path in - if is_leaf rtree refp then Config_tree.set_leaf ctree path true + if is_leaf rtree refp then (Config_tree.set_leaf[@alert "-exn"]) ctree path true else ctree let get_ceil_data f reftree path = + (* raises: + [Vytree.Empty_path] + [Vytree.Nonexistent_path] + alert exn Vytree.get_data: + [Vytree.Empty_path] allow raise + [Vytree.Nonexistent_path] allow raise + *) let data_of_path d p = - let data = Vytree.get_data reftree p in + let data = (Vytree.get_data[@alert "-exn"]) reftree p in match (f data) with | Some d' -> Some d' | None -> d @@ -561,6 +681,20 @@ let get_ceil_data f reftree path = Numbered comments list constraints as described above validate_path. *) let validate_tree_at_path validators_dir rt ct path value = + (* raises: + [Validation_error] + alert exn Vytree.exists: + [Vytree.Empty_path] ruled out in branch + alert exn Vytree.get: + [Vytree.Empty_path] ruled out in branch + [Vytree.Nonexistent_path] catch and raise Validation_error + alert exn Config_tree.is_tag; + Config_tree.is_tag_value; + Vytree.get_data; + Config_tree.is_leaf: + [Vytree.Empty_path] ruled out + [Vytree.Nonexistent_path] ruled out + *) if Util.is_empty path then () else let show_path p = @@ -570,13 +704,13 @@ let validate_tree_at_path validators_dir rt ct path value = (* 6. It's a node that is neither leaf nor tag value with a name that doesn't exist in the reference tree *) - if not (Vytree.exists rt refp) then + if not ((Vytree.exists[@alert "-exn"]) rt refp) then let msg = Printf.sprintf "Path %s is not in reference tree\n" (show_path path) in raise (Validation_error msg) else let node = try - Vytree.get ct path + (Vytree.get[@alert "-exn"]) ct path with Vytree.Nonexistent_path -> let msg = Printf.sprintf "Path %s is not in config file\n" (show_path path) in raise (Validation_error msg) @@ -586,7 +720,7 @@ let validate_tree_at_path validators_dir rt ct path value = let ct_data = Vytree.data_of_node node in let values = ct_data.Config_tree.values in let values_empty = Util.is_empty values in - if Config_tree.is_tag ct path then + if (Config_tree.is_tag[@alert "-exn"]) ct path then (* 1. It's a tag node without a child *) if childless then let msg = @@ -594,8 +728,8 @@ let validate_tree_at_path validators_dir rt ct path value = in raise (Validation_error msg) else () else - if (Config_tree.is_tag_value ct path) then - let rt_data = Vytree.get_data rt (refpath rt (Util.drop_last path)) in + if ((Config_tree.is_tag_value[@alert "-exn"]) ct path) then + let rt_data = (Vytree.get_data[@alert "-exn"]) rt (refpath rt (Util.drop_last path)) in let tag_value = match (Util.get_last path) with | Some v -> v @@ -613,8 +747,8 @@ let validate_tree_at_path validators_dir rt ct path value = let ret = format_out [show_path path; out; rt_data.constraint_error_message] in raise (Validation_error ret) else - if Config_tree.is_leaf ct path then - let rt_data = Vytree.get_data rt (refpath rt path) in + if (Config_tree.is_leaf[@alert "-exn"]) ct path then + let rt_data = (Vytree.get_data[@alert "-exn"]) rt (refpath rt path) in (* 4. It's a valueless leaf node with a value *) if is_valueless rt refp then if not values_empty then @@ -656,6 +790,17 @@ let validate_tree_at_path validators_dir rt ct path value = let validate_tree_filter dir rt ct = (* validate and filter invalid paths *) + (* catches: + [Validation_error] from validate_tree_at_path + alert exn Config_tree.delete; Config_tree.prune_delete: + [Vytree.Empty_path] not possible as validate_tree_at_path ignores + [Vytree.Nonexistent_path] not possible as extant in tree + alert exn Vytree.exists: + [Vytree.Empty_path] not possible as non-empty in branch + alert exn Vytree.get_data: + [Vytree.Empty_path] not possible as non-empty in branch + [Vytree.Nonexistent_path] not possible in fold_tree_with_path + *) let try_validate (p, (ctree, out)) value = let q = List.rev p in try @@ -663,8 +808,8 @@ let validate_tree_filter dir rt ct = (p, (ctree, out)) with Validation_error x -> let ct' = - Config_tree.delete ctree q value |> - (fun c -> Config_tree.prune_delete c q) + (Config_tree.delete[@alert "-exn"]) ctree q value |> + (fun c -> (Config_tree.prune_delete[@alert "-exn"]) c q) in (p, (ct', out ^ x)) in @@ -674,10 +819,10 @@ let validate_tree_filter dir rt ct = else let q = List.rev p in (* the path may have been removed in previous iteration *) - if not (Vytree.exists ctree q) then + if not ((Vytree.exists[@alert "-exn"]) ctree q) then (p, (ctree, out)) else - let data = Vytree.get_data ct q in + let data = (Vytree.get_data[@alert "-exn"]) ct q in let values = data.Config_tree.values in match values with | [] -> @@ -692,6 +837,9 @@ let validate_tree_filter dir rt ct = tree, out let validate_tree dir rt ct = + (* raises: + [Validation_error] from validate_tree_at_path + *) let _, out = validate_tree_filter dir rt ct in out diff --git a/src/reference_tree.mli b/src/reference_tree.mli index 1a72ad9..42d9021 100644 --- a/src/reference_tree.mli +++ b/src/reference_tree.mli @@ -50,45 +50,78 @@ val default_data : ref_node_data val default : t val load_from_xml : t -> string -> t +[@@alert exn "Reference_tree.Bad_interface_definition"] val find_xml_child : string -> Xml_light_types.xml -> Xml_light_types.xml option val validate_path : string -> t -> string list -> unit +[@@alert exn "Reference_tree.Validation_error"] val validate_tree_filter : string -> t -> Config_tree.t -> Config_tree.t * string val validate_tree : string -> t -> Config_tree.t -> string +[@@alert exn "Reference_tree.Validation_error"] val split_path : t -> string list -> string list * string option val is_multi : t -> string list -> bool +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val is_hidden : t -> string list -> bool +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val is_secret : t -> string list -> bool +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val is_tag : t -> string list -> bool +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val is_leaf : t -> string list -> bool +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val is_valueless : t -> string list -> bool +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val get_owner : t -> string list -> string option +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val get_priority : t -> string list -> string option +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val get_help_string : t -> string list -> string +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val get_value_help : t -> string list -> (string * string) list +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val get_completion_data : t -> string list -> (node_type * bool * string) list +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val refpath : t -> string list -> string list - -val get_ceil_data : (ref_node_data -> string option) -> t -> string list -> string option +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val set_tag_data : t -> Config_tree.t -> string list -> Config_tree.t +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val set_leaf_data : t -> Config_tree.t -> string list -> Config_tree.t +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] + +val get_ceil_data : (ref_node_data -> string option) -> t -> string list -> string option +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val render_json : t -> string diff --git a/src/tree_alg.ml b/src/tree_alg.ml index 23af99d..a734285 100644 --- a/src/tree_alg.ml +++ b/src/tree_alg.ml @@ -30,12 +30,26 @@ module Alg (D: Data) (T: Tree with module D = D) = struct let find_child n c = Vytree.find n (Vytree.name_of_node c) - let insert_child n c = Vytree.insert ~position:Vytree.Lexical ~children:(Vytree.children_of_node c) n [(Vytree.name_of_node c)] (Vytree.data_of_node c) + let insert_child n c = + (* alert exn Vytree.insert: + [Vytree.Empty_path] not possible as called on name_of_node for existing child + [Not_found] not possible for postion=Lexical + [Vytree.Duplicate_child] not possible as find_child is None + [Vytree.Insert_error] not possible as no intermediary nodes + *) + (Vytree.insert[@alert "-exn"]) ~position:Vytree.Lexical ~children:(Vytree.children_of_node c) n [(Vytree.name_of_node c)] (Vytree.data_of_node c) let replace_child n c = - Vytree.replace n c + (* alert exn Vytree.replace: + [Not_found] not possible as child name already present + *) + (Vytree.replace[@alert "-exn"]) n c let rec tree_union s t f = + (* raises: + [Incompatible_union] + [Nonexistent_child] + *) if (Vytree.name_of_node s) <> (Vytree.name_of_node t) then raise Incompatible_union else @@ -44,14 +58,16 @@ module Alg (D: Data) (T: Tree with module D = D) = struct let t_c = find_child t c in match s_c, t_c with | Some child, None -> - insert_child t child + insert_child t child | None, Some _ -> t | Some u, Some v -> if (Vytree.data_of_node u <> Vytree.data_of_node v) then replace_child t (tree_union u (f u v) f) else replace_child t (tree_union u v f) - | None, None -> raise Nonexistent_child + | None, None -> + (* Not possible in fold over union_of_children *) + raise Nonexistent_child in List.fold_left (fun x c -> child_of_union s x c) t (union_of_children s t) end diff --git a/src/tree_alg.mli b/src/tree_alg.mli new file mode 100644 index 0000000..6a59fc0 --- /dev/null +++ b/src/tree_alg.mli @@ -0,0 +1,217 @@ +exception Incompatible_union +exception Nonexistent_child + +module type Data = sig type t end + +module type Tree = sig module D : Data type t = D.t Vytree.t end + +module Tree_impl : + functor (D : Data) -> + sig module D : sig type t = D.t end type t = D.t Vytree.t end + +module Alg : + functor (D : Data) + (T : sig module D : sig type t = D.t end type t = D.t Vytree.t end) -> + sig + module TreeOrd : + sig + type t = T.t + val compare : 'a Vytree.t -> 'b Vytree.t -> int + end + module SetT : + sig + type elt = TreeOrd.t + type t = Set.Make(TreeOrd).t + val empty : t + val is_empty : t -> bool + val mem : elt -> t -> bool + val add : elt -> t -> t + val singleton : elt -> t + val remove : elt -> t -> t + val union : t -> t -> t + val inter : t -> t -> t + val disjoint : t -> t -> bool + val diff : t -> t -> t + val compare : t -> t -> int + val equal : t -> t -> bool + val subset : t -> t -> bool + val iter : (elt -> unit) -> t -> unit + val map : (elt -> elt) -> t -> t + val fold : (elt -> 'a -> 'a) -> t -> 'a -> 'a + val for_all : (elt -> bool) -> t -> bool + val exists : (elt -> bool) -> t -> bool + val filter : (elt -> bool) -> t -> t + val filter_map : (elt -> elt option) -> t -> t + val partition : (elt -> bool) -> t -> t * t + val cardinal : t -> int + val elements : t -> elt list + val min_elt : t -> elt + val min_elt_opt : t -> elt option + val max_elt : t -> elt + val max_elt_opt : t -> elt option + val choose : t -> elt + val choose_opt : t -> elt option + val split : elt -> t -> t * bool * t + val find : elt -> t -> elt + val find_opt : elt -> t -> elt option + val find_first : (elt -> bool) -> t -> elt + val find_first_opt : (elt -> bool) -> t -> elt option + val find_last : (elt -> bool) -> t -> elt + val find_last_opt : (elt -> bool) -> t -> elt option + val of_list : elt list -> t + val to_seq_from : elt -> t -> elt Seq.t + val to_seq : t -> elt Seq.t + val to_rev_seq : t -> elt Seq.t + val add_seq : elt Seq.t -> t -> t + val of_seq : elt Seq.t -> t + end + val union_of_children : D.t Vytree.t -> D.t Vytree.t -> SetT.elt list + val find_child : 'a Vytree.t -> 'b Vytree.t -> 'a Vytree.t option + val insert_child : 'a Vytree.t -> 'a Vytree.t -> 'a Vytree.t + val replace_child : 'a Vytree.t -> 'a Vytree.t -> 'a Vytree.t + val tree_union : + D.t Vytree.t -> + D.t Vytree.t -> + (D.t Vytree.t -> D.t Vytree.t -> D.t Vytree.t) -> D.t Vytree.t + end + +module ConfigData : sig type t = Config_tree.config_node_data end + +module RefData : sig type t = Reference_tree.ref_node_data end + +module ConfigAlg : + sig + module TreeOrd : + sig + type t = Tree_impl(ConfigData).t + val compare : 'a Vytree.t -> 'b Vytree.t -> int + end + module SetT : + sig + type elt = TreeOrd.t + type t = Set.Make(TreeOrd).t + val empty : t + val is_empty : t -> bool + val mem : elt -> t -> bool + val add : elt -> t -> t + val singleton : elt -> t + val remove : elt -> t -> t + val union : t -> t -> t + val inter : t -> t -> t + val disjoint : t -> t -> bool + val diff : t -> t -> t + val compare : t -> t -> int + val equal : t -> t -> bool + val subset : t -> t -> bool + val iter : (elt -> unit) -> t -> unit + val map : (elt -> elt) -> t -> t + val fold : (elt -> 'a -> 'a) -> t -> 'a -> 'a + val for_all : (elt -> bool) -> t -> bool + val exists : (elt -> bool) -> t -> bool + val filter : (elt -> bool) -> t -> t + val filter_map : (elt -> elt option) -> t -> t + val partition : (elt -> bool) -> t -> t * t + val cardinal : t -> int + val elements : t -> elt list + val min_elt : t -> elt + val min_elt_opt : t -> elt option + val max_elt : t -> elt + val max_elt_opt : t -> elt option + val choose : t -> elt + val choose_opt : t -> elt option + val split : elt -> t -> t * bool * t + val find : elt -> t -> elt + val find_opt : elt -> t -> elt option + val find_first : (elt -> bool) -> t -> elt + val find_first_opt : (elt -> bool) -> t -> elt option + val find_last : (elt -> bool) -> t -> elt + val find_last_opt : (elt -> bool) -> t -> elt option + val of_list : elt list -> t + val to_seq_from : elt -> t -> elt Seq.t + val to_seq : t -> elt Seq.t + val to_rev_seq : t -> elt Seq.t + val add_seq : elt Seq.t -> t -> t + val of_seq : elt Seq.t -> t + end + val union_of_children : + ConfigData.t Vytree.t -> ConfigData.t Vytree.t -> TreeOrd.t list + val find_child : 'a Vytree.t -> 'b Vytree.t -> 'a Vytree.t option + val insert_child : 'a Vytree.t -> 'a Vytree.t -> 'a Vytree.t + val replace_child : 'a Vytree.t -> 'a Vytree.t -> 'a Vytree.t + val tree_union : + ConfigData.t Vytree.t -> + ConfigData.t Vytree.t -> + (ConfigData.t Vytree.t -> + ConfigData.t Vytree.t -> ConfigData.t Vytree.t) -> + ConfigData.t Vytree.t + [@@alert exn "Tree_alg.Incompatible_union"] + [@@alert exn "Tree_alg.Nonexistent_child"] + end + +module RefAlg : + sig + module TreeOrd : + sig + type t = Tree_impl(RefData).t + val compare : 'a Vytree.t -> 'b Vytree.t -> int + end + module SetT : + sig + type elt = TreeOrd.t + type t = Set.Make(TreeOrd).t + val empty : t + val is_empty : t -> bool + val mem : elt -> t -> bool + val add : elt -> t -> t + val singleton : elt -> t + val remove : elt -> t -> t + val union : t -> t -> t + val inter : t -> t -> t + val disjoint : t -> t -> bool + val diff : t -> t -> t + val compare : t -> t -> int + val equal : t -> t -> bool + val subset : t -> t -> bool + val iter : (elt -> unit) -> t -> unit + val map : (elt -> elt) -> t -> t + val fold : (elt -> 'a -> 'a) -> t -> 'a -> 'a + val for_all : (elt -> bool) -> t -> bool + val exists : (elt -> bool) -> t -> bool + val filter : (elt -> bool) -> t -> t + val filter_map : (elt -> elt option) -> t -> t + val partition : (elt -> bool) -> t -> t * t + val cardinal : t -> int + val elements : t -> elt list + val min_elt : t -> elt + val min_elt_opt : t -> elt option + val max_elt : t -> elt + val max_elt_opt : t -> elt option + val choose : t -> elt + val choose_opt : t -> elt option + val split : elt -> t -> t * bool * t + val find : elt -> t -> elt + val find_opt : elt -> t -> elt option + val find_first : (elt -> bool) -> t -> elt + val find_first_opt : (elt -> bool) -> t -> elt option + val find_last : (elt -> bool) -> t -> elt + val find_last_opt : (elt -> bool) -> t -> elt option + val of_list : elt list -> t + val to_seq_from : elt -> t -> elt Seq.t + val to_seq : t -> elt Seq.t + val to_rev_seq : t -> elt Seq.t + val add_seq : elt Seq.t -> t -> t + val of_seq : elt Seq.t -> t + end + val union_of_children : + RefData.t Vytree.t -> RefData.t Vytree.t -> TreeOrd.t list + val find_child : 'a Vytree.t -> 'b Vytree.t -> 'a Vytree.t option + val insert_child : 'a Vytree.t -> 'a Vytree.t -> 'a Vytree.t + val replace_child : 'a Vytree.t -> 'a Vytree.t -> 'a Vytree.t + val tree_union : + RefData.t Vytree.t -> + RefData.t Vytree.t -> + (RefData.t Vytree.t -> RefData.t Vytree.t -> RefData.t Vytree.t) -> + RefData.t Vytree.t + [@@alert exn "Tree_alg.Incompatible_union"] + [@@alert exn "Tree_alg.Nonexistent_child"] + end diff --git a/src/value_checker.ml b/src/value_checker.ml index c066088..c07d760 100644 --- a/src/value_checker.ml +++ b/src/value_checker.ml @@ -9,6 +9,9 @@ type value_constraint = exception Bad_validator of string let validate_value dir buf value_constraint value = + (* raises: + [Bad_validator] + *) match value_constraint with | Regex s -> (try @@ -44,6 +47,8 @@ let validate_value dir buf value_constraint value = (* If no constraints given, consider it valid. Otherwise consider it valid if it satisfies at least one constraint *) let validate_any validators constraints value = + (* raises no error; catches [Bad_validator] from validate_value + *) let buf = Buffer.create 4096 in let validate_exists validators constraints value = match constraints with @@ -65,6 +70,8 @@ let validate_any validators constraints value = (* If no constraints given, consider it valid. Otherwise consider it valid if it satisfies all constraints *) let validate_all validators constraints value = + (* raises no error; catches [Bad_validator] from validate_value + *) let buf = Buffer.create 4096 in let validate_forall validators constraints value = match constraints with diff --git a/src/value_checker.mli b/src/value_checker.mli index d4ae516..8d888fd 100644 --- a/src/value_checker.mli +++ b/src/value_checker.mli @@ -8,6 +8,7 @@ type value_constraint = exception Bad_validator of string val validate_value : string -> Buffer.t -> value_constraint -> string -> bool +[@@alert exn "Value_checker.Bad_validator"] val validate_any : string -> value_constraint list -> string -> string option diff --git a/src/vylist.mli b/src/vylist.mli index 54c0806..76ec5ed 100644 --- a/src/vylist.mli +++ b/src/vylist.mli @@ -1,8 +1,18 @@ val find : ('a -> bool) -> 'a list -> 'a option + val remove : ('a -> bool) -> 'a list -> 'a list + val replace : ?force:bool -> ('a -> bool) -> 'a -> 'a list -> 'a list +[@@alert exn "Not_found"] + val insert_before : ('a -> bool) -> 'a -> 'a list -> 'a list +[@@alert exn "Not_found"] + val insert_after : ('a -> bool) -> 'a -> 'a list -> 'a list +[@@alert exn "Not_found"] + val insert_compare : ('a -> 'a -> int) -> 'a -> 'a list -> 'a list + val complement : 'a list -> 'a list -> 'a list + val in_list : 'a list -> 'a -> bool diff --git a/src/vytree.ml b/src/vytree.ml index 0f6e24a..a65fe6f 100644 --- a/src/vytree.ml +++ b/src/vytree.ml @@ -20,13 +20,20 @@ let data_of_node node = node.data let children_of_node node = node.children let insert_immediate ?(position=Default) node name data children = + (* alert exn Vylist.insert_before; Vylist.insert_after: + [Not_found] allow raise + *) let new_node = make_full data name children in let children' = match position with | Default -> new_node :: node.children | End -> node.children @ [new_node] - | Before s -> Vylist.insert_before (fun x -> x.name = s) new_node node.children - | After s -> Vylist.insert_after (fun x -> x.name = s) new_node node.children + | Before s -> + (* allow raise of Not_found *) + (Vylist.insert_before[@alert "-exn"]) (fun x -> x.name = s) new_node node.children + | After s -> + (* allow raise of Not_found *) + (Vylist.insert_after[@alert "-exn"]) (fun x -> x.name = s) new_node node.children | Lexical -> Vylist.insert_compare (fun x y -> Util.lexical_numeric_compare x.name y.name) new_node node.children in { node with children = children' } @@ -39,20 +46,35 @@ let adopt node child = { node with children = child :: node.children } let replace node child = + (* alert exn Vylist.replace: + [Not_found] allow raise + *) let children = node.children in let name = child.name in - let children' = Vylist.replace (fun x -> x.name = name) child children in + let children' = + (* allow raise of Not_found *) + (Vylist.replace[@alert "-exn"]) (fun x -> x.name = name) child children + in { node with children = children' } let replace_full node child name = + (* alert exn Vylist.replace: + [Not_found] allow raise + *) let children = node.children in - let children' = Vylist.replace (fun x -> x.name = name) child children in + let children' = + (* allow raise of Not_found *) + (Vylist.replace[@alert "-exn"]) (fun x -> x.name = name) child children + in { node with children = children' } let find node name = Vylist.find (fun x -> x.name = name) node.children let find_or_fail node name = + (* raises + [Nonexistent_path] + *) let child = find node name in match child with | None -> raise Nonexistent_path @@ -62,6 +84,10 @@ let list_children node = List.map (fun x -> x.name) node.children let rec do_with_child fn node path = + (* raises + [Nonexistent_path] from find_or_fail + [Empty_path] + *) match path with | [] -> raise Empty_path | [name] -> fn node name @@ -71,6 +97,12 @@ let rec do_with_child fn node path = replace node new_node let rec insert ?(position=Default) ?(children=[]) node path data = + (* raises + [Not_found] from insert_immediate + [Empty_path] + [Duplicate_child] + [Insert_error] + *) match path with | [] -> raise Empty_path | [name] -> @@ -89,10 +121,17 @@ let rec insert ?(position=Default) ?(children=[]) node path data = raise (Insert_error s) let insert_maybe ?(position=Default) node path data = + (* raises + [Empty_path], + [Not_found], + [Insert_error] from insert + *) try insert ~position:position node path data with Duplicate_child -> node let sorted_children_of_node cmp node = + (* raises no exn, as find_or_fail cannot fail + *) let names = list_children node in let names = List.sort cmp names in List.map (find_or_fail node) names @@ -154,6 +193,13 @@ let delete node path = do_with_child delete_immediate node path let rename node path newname = + (* raises + [Not_found] from replace_full + [Nonexistent_child] from find_or_fail; do_with_child + [Empty_path] + *) + if Util.is_empty path then raise Empty_path + else let rename_immediate newname' node' name' = let child = find_or_fail node' name' in let child = { child with name=newname' } in @@ -172,14 +218,26 @@ let insert_or_update ?(position=Default) node path data = with Duplicate_child -> update node path data let rec get node path = + (* raises + [Empty_path], + [Nonexistent_path] from find_or_fail + *) match path with | [] -> raise Empty_path | [name] -> find_or_fail node name | name :: names -> get (find_or_fail node name) names -let get_data node path = data_of_node @@ get node path +let get_data node path = + (* raises + [Empty_path], + [Nonexistent_path] from get + *) + data_of_node @@ get node path let exists node path = + (* raises + [Empty_path] from get + *) try ignore (get node path); true with Nonexistent_path -> false @@ -195,20 +253,38 @@ let get_existent_path node path = in List.rev (aux node path []) let children_of_path node path = + (* raises + [Empty_path], + [Nonexistent_path] from get + *) let node' = get node path in list_children node' let copy node old_path new_path = + (* raises + [Empty_path] from exists + [Nonexistent_path] from get + [Insert_error] from insert + *) if exists node new_path then raise Duplicate_child else let child = get node old_path in insert ~position:End ~children:child.children node new_path child.data let move node path position = + (* raises + [Empty_path], + [Nonexistent_path] from get; delete + [Not_found], + [Insert_error] from insert + *) let child = get node path in let node = delete node path in insert ~position:position ~children:child.children node path child.data let is_terminal_path node path = + (* raises + [Empty_path] from get + *) try let n = get node path in match (children_of_node n) with diff --git a/src/vytree.mli b/src/vytree.mli index cce169c..c23f1a7 100644 --- a/src/vytree.mli +++ b/src/vytree.mli @@ -15,48 +15,84 @@ val data_of_node : 'a t -> 'a val children_of_node : 'a t -> 'a t list val find : 'a t -> string -> 'a t option -val find_or_fail : 'a t -> string -> 'a t val adopt : 'a t -> 'a t -> 'a t val replace : 'a t -> 'a t -> 'a t +[@@alert exn "Not_found"] val insert : ?position:position -> ?children:('a t list) -> 'a t -> string list -> 'a -> 'a t +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Not_found possible if position Before/After"] +[@@alert exn "Vytree.Duplicate_child"] +[@@alert exn "Vytree.Insert_error"] val insert_maybe : ?position:position -> 'a t -> string list -> 'a -> 'a t +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Not_found possible if position Before/After"] +[@@alert exn "Vytree.Insert_error"] val insert_or_update : ?position:position -> 'a t -> string list -> 'a -> 'a t +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Not_found possible if position Before/After"] +[@@alert exn "Vytree.Insert_error"] val insert_multi_level : ?position:position -> 'a -> 'a t -> string list -> string list -> 'a -> 'a t +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Not_found possible if position Before/After"] +[@@alert exn "Vytree.Duplicate_child possible if path_remaining not complement"] +[@@alert exn "Vytree.Insert_error possible if path_done existence not guaranteed"] val merge_children : ('a -> 'a -> 'a) -> (string -> string -> int) -> 'a t -> 'a t val delete : 'a t -> string list -> 'a t +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val update : 'a t -> string list -> 'a -> 'a t +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val rename : 'a t -> string list -> string -> 'a t +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Not_found"] +[@@alert exn "Vytree.Nonexistent_path"] val list_children : 'a t -> string list val get : 'a t -> string list -> 'a t +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val get_existent_path : 'a t -> string list -> string list val get_data : 'a t -> string list -> 'a +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val exists : 'a t -> string list -> bool +[@@alert exn "Vytree.Empty_path"] val children_of_path : 'a t -> string list -> string list +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] val sorted_children_of_node : (string -> string -> int) -> 'a t -> ('a t) list val sort_children : (string -> string -> int) -> 'a t -> 'a t val copy : 'a t -> string list -> string list -> 'a t +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] +[@@alert exn "Vytree.Insert_error"] val move : 'a t -> string list -> position -> 'a t +[@@alert exn "Vytree.Empty_path"] +[@@alert exn "Vytree.Nonexistent_path"] +[@@alert exn "Not_found possible if position Before/After"] +[@@alert exn "Vytree.Insert_error"] val is_terminal_path : 'a t -> string list -> bool +[@@alert exn "Vytree.Empty_path"] val fold_tree_with_path: (string list * 'acc -> 'b t -> string list * 'acc) -> string list * 'acc -> 'b t -> string list * 'acc |
