summaryrefslogtreecommitdiff
path: root/lib/vyos1x_renderer.ml
blob: ab3c0052be7a007ce471668ec644a42c9b3a3f8c (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
(* The renderer makes two assumptions about the invariants of the config files:
   that top level nodes are never tag or leaf nodes,
   and that immediate children of tag nodes are never themselves tag nodes.

   They are true in all existing VyOS configs and configs with those invariant
   broken will never load, so these assumptions are safe to make when
   processing existing configs. In configs built from scratch, the user is
   responsible for its validness.

   The original loader behaviour with tag nodes is strange: deep down, after
   config loading, they are indistinguishable from any other nodes, but at load
   time, they fail to validate unless they are formatted as tag nodes in the
   config file, that is, "ethernet eth0 { ..." as opposed to "ethernet { eth0 { ...".

   Since Vyconf makes no distinction between normal nodes and tag nodes other than
   at set command validation and formatting time, I reused the ephemeral flag
   which is never used in VyOS 1.x for marking nodes as tag nodes at parsing time.

 *)

module CT = Config_tree
module VT = Vytree

let make_indent indent level = String.make (level * indent) ' '

let render_values indent_str name values =
    match values with
    | [] -> Printf.sprintf "%s%s { }\n" indent_str name
    | [v] -> Printf.sprintf "%s%s %s\n" indent_str name v
    | _  -> 
        let rendered = List.map (fun s -> Printf.sprintf "%s%s %s" indent_str name s) values in
        let rendered = String.concat "\n" rendered in
        Printf.sprintf "%s\n" rendered

let render_comment indent c =
    match c with
    | None -> ""
    | Some c ->  Printf.sprintf "%s/* %s */\n" indent c

let rec render_node indent level node =
    let open CT in
    let indent_str = make_indent indent level in
    let name = VT.name_of_node node in
    let data = VT.data_of_node node in
    let is_tag = data.ephemeral (* sic! look in the parser *) in 
    let comment = render_comment indent_str data.comment in
    let values = render_values indent_str name data.values in
    let children = VT.children_of_node node in
    match children with
    | [] -> Printf.sprintf "%s%s" comment values
    | _ ->
        if is_tag then 
            begin
                let inner = List.map (render_tag_node indent level name) children in
                String.concat "" inner
            end
        else
            begin
                let inner = List.map (render_node indent (level + 1)) children in
                let inner = String.concat "" inner in
                Printf.sprintf "%s%s%s {\n%s%s}\n" comment indent_str name inner indent_str
            end

and render_tag_node indent level parent node =
    let open CT in
    let indent_str = make_indent indent level in
    let name = VT.name_of_node node in
    let data = VT.data_of_node node in
    let comment = render_comment indent_str data.comment in
    let values = render_values indent_str name data.values in
    let children = VT.children_of_node node in
    match children with
    | [] -> Printf.sprintf "%s\n%s" comment values
    | _ ->
        (* Exploiting the fact that immediate children of tag nodes are
           never themselves tag nodes *)
        let inner = List.map (render_node indent (level + 1)) children in
        let inner = String.concat "" inner in
        Printf.sprintf "%s%s%s %s {\n%s%s}\n" comment indent_str parent name inner indent_str

let render node =
    let children = Vytree.children_of_node node in
    let child_configs = List.map (render_node 4 0) children in
    String.concat "" child_configs