summaryrefslogtreecommitdiff
path: root/src/config_dict.ml
blob: e07c9bca3196a7be30e89f156e32fbca7898bf43 (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
(** The goal here is to produce a JSON rendering to load into a Python dict.
    By abuse of language, the entry point is named config_dict, the Python
    wrapper for which will perform the json.loads().

    The idea here is simple, and abstracts the construction in vyos-1x using
    the xml_ref module:
        fold over the config tree 'a fold over the reference tree'
    The required details to make this work:
        (1) skip tag nodes in the config tree, so as to call the
        corresponding reference tree below the tag node value in the
        depth-first fold.
        (2) use a modified fold_tree_with_path to store a boolean for each
        level, in order to ignore non-sensical (non-existent tag node value)
        or excluded (masked) reference paths below a given level.
        (3) make appropriate use of local vs global paths in the respective
        trees.
 *)

let tree_with_defaults ?(with_first_node=true) ref_tree config_tree mask path =
    let ct_at_path =
        Config_tree.get_subtree ~with_node:with_first_node config_tree path
    in
    let continue l =
        match l with
        | [] -> true
        | x :: _ -> x
    in
    let add_defaults ct p' =
        let ref_path =
            match with_first_node with
            | false -> Reference_tree.refpath ref_tree (path @ p')
            | true -> Reference_tree.refpath ref_tree ((Util.drop_last path) @ p')
        in
        let relative_ref_tree = Reference_tree.get_subtree ref_tree ref_path in
        let ref_tree_walk ((p, c), acc) node =
            let cont = continue c in
            if not cont then ((p, false::c), acc)
            else
            let rev_p = List.rev p in
            let sub_path = p' @ rev_p in
            if not (Util.is_empty rev_p) &&
                (Vytree.is_terminal_path[@alert "-exn"]) mask (ref_path @ rev_p) &&
                not ((Vytree.exists[@alert "-exn"]) ct sub_path)
            then ((p, false::c), acc)
            else
            let data = Vytree.data_of_node node in
            match data.Reference_tree.node_type with
            | `Tag -> ((p, false::c), acc)
            | `Leaf ->
                if (Vytree.exists[@alert "-exn"]) acc sub_path then ((p, cont::c), acc)
                else
                begin
                match data.default_value with
                | None -> ((p, cont::c), acc)
                | Some v ->
                    let acc' =
                        (* The behaviour variant ReplaceValue has no effect in this case
                           as the path does not exist in branch. *)
                        (Config_tree.set[@alert "-exn"]) acc sub_path (Some v) ReplaceValue
                    in ((p, cont::c), acc')
                end
            | _ -> ((p, cont::c), acc)
        in
        Vytree.fold_tree_with_path_and_list ref_tree_walk (([], []), ct) relative_ref_tree
    in
    let config_tree_walk (p, acc) ct =
        let (data: Config_tree.config_node_data) = Vytree.data_of_node ct in
        if with_first_node && Util.is_empty p then (p, acc) else
        if data.tag then (p, acc) else
        let rev_p = List.rev p in
        let ct' = add_defaults acc rev_p
        in (p, ct')
    in Vytree.fold_tree_with_path config_tree_walk ([], ct_at_path) ct_at_path


(* Simple check of config dict result. Modifications 'multi_to_list' and
   'key_mangling' should belong to an extended config tree JSONRenderer *)
let config_dict ?(with_defaults=true) ?(with_first_node=true) rt ct mask path =
    let ht =
        match with_defaults with
        | true -> tree_with_defaults ~with_first_node rt ct mask path
        | false -> Config_tree.get_subtree ~with_node:with_first_node ct path
    in
    Config_tree.render_json ht