@@ -23,13 +23,24 @@ module Diff_compare = struct
2323 }
2424end
2525
26+ module Diff_show = struct
27+ type t = { left : Config_tree .t ;
28+ right : Config_tree .t ;
29+ base_path : string list ;
30+ open_blocks : string list list ;
31+ config_diff : string ;
32+ }
33+ end
34+
2635type _ diff_result =
2736 | Diff_tree : Diff_tree .t -> Diff_tree .t diff_result
2837 | Diff_compare : Diff_compare .t -> Diff_compare .t diff_result
38+ | Diff_show : Diff_show .t -> Diff_show .t diff_result
2939
3040let eval_diff_result : type a. a diff_result -> a = function
3141 | Diff_tree x -> x
3242 | Diff_compare x -> x
43+ | Diff_show x -> x
3344
3445type 'a diff_func = ?recurse:bool -> string list -> 'a diff_result -> change -> 'a diff_result
3546
@@ -46,6 +57,12 @@ let make_diff_compare l r = Diff_compare { left = l; right = r;
4657 udiff = " " ;
4758 }
4859
60+ let make_diff_show l r path = Diff_show { left = l; right = r;
61+ base_path = path;
62+ open_blocks = [] ;
63+ config_diff = " " ;
64+ }
65+
4966let name_of n = Vytree. name_of_node n
5067let data_of n = Vytree. data_of_node n
5168let children_of n = Vytree. children_of_node n
@@ -319,6 +336,7 @@ let get_tagged_delete_tree dt =
319336(* the following builds a diff_func to return a unified diff string of
320337 configs or config commands for use in the config-mode 'compare' command
321338 *)
339+
322340let list_but_last l =
323341 let len = List. length l in
324342 List. filteri (fun i _ -> i < len - 1 ) l
@@ -466,6 +484,230 @@ let diff_compare ?(cmds=false) path left right =
466484 in
467485 strs
468486
487+ (* the following builds a diff_func for 'show config' *)
488+
489+ let annotate_rendered change rendered =
490+ let mark =
491+ match change with
492+ | Unchanged -> " "
493+ | Added -> " +"
494+ | Subtracted -> " -"
495+ | Updated _ -> " >"
496+ in
497+ let lst = String. split_on_char '\n' rendered in
498+ let marked = List. map (fun x -> match x with "" -> x | _ -> mark ^ x) lst in
499+ String. concat " \n " marked
500+
501+ let get_level_at_path node path =
502+ (* alert exn Config_tree.is_tag_value:
503+ [Vytree.Empty_path] called in pattern path non-empty
504+ [Vytree.Nonexistent_path] function diff never calls diff_func on nonexistent path
505+ *)
506+ let f level p =
507+ if (Config_tree. is_tag_value[@ alert " -exn" ]) node p then level
508+ else level + 1
509+ in
510+ match path with
511+ | [] -> 0
512+ | _ ->
513+ List. fold_left f 0 (Util. flag path) - 1
514+
515+ let render_level_open indent node path =
516+ (* alert exn Config_tree.is_tag, Config_tree.is_tag_value:
517+ [Vytree.Empty_path] called in branch path non-empty
518+ [Vytree.Nonexistent_path] function diff never calls diff_func on nonexistent path
519+ *)
520+ if Util. is_empty path || (Config_tree. is_tag[@ alert " -exn" ]) node path then
521+ " "
522+ else
523+ let level = get_level_at_path node path in
524+ let indent_str = Config_tree. make_indent indent level in
525+ if (Config_tree. is_tag_value[@ alert " -exn" ]) node path then
526+ let tag_node =
527+ match Util. get_last_n path 1 with
528+ | None -> (* not possible as path non-empty *) " none"
529+ | Some n -> n
530+ in
531+ let tag_value =
532+ match Util. get_last path with
533+ | None -> (* not possible as path non-empty *) " none"
534+ | Some v -> v
535+ in
536+ Printf. sprintf " %s%s %s {\n " indent_str tag_node tag_value
537+ else
538+ let name =
539+ match Util. get_last path with
540+ | None -> (* not possible as path non-empty *) " none"
541+ | Some v -> v
542+ in
543+ Printf. sprintf " %s%s {\n " indent_str name
544+
545+ let render_level_close indent node path =
546+ (* alert exn Config_tree.is_tag:
547+ [Vytree.Empty_path] called in branch path non-empty
548+ [Vytree.Nonexistent_path] function diff never calls diff_func on nonexistent path
549+ *)
550+ if Util. is_empty path || (Config_tree. is_tag[@ alert " -exn" ]) node path then
551+ " "
552+ else
553+ let level = get_level_at_path node path in
554+ let indent_str = Config_tree. make_indent indent level in
555+ Printf. sprintf " %s}\n " indent_str
556+
557+ let config_diff (rt : Reference_tree.t ) ?(recurse =true ) (path : string list ) (Diff_show res ) (m : change ) =
558+ (* alert exn Vytree.get, Reference_tree.refpath, Config_tree.get_values,
559+ Reference_tree.is_multi, Config_tree.is_tag_value:
560+ [Vytree.Empty_path] checked at only point possible (Unchanged)
561+ [Vytree.Nonexistent_path] function diff never calls diff_func on nonexistent path
562+ *)
563+
564+ let indent = 4 in
565+ (* the only subtlety in all this is the bookkeeping of closing open
566+ braces at correct level:
567+ (1) a rendered line with open brace will occur before the next
568+ depth-first step
569+ (2) at each return to (local) root, the path is checked for the
570+ matching closing brace
571+ explicitly: the record field open_blocks is a list of open paths
572+ ordered by reverse inclusion, which are closed when the path being
573+ passed to config_diff no longer contains that element
574+ *)
575+ let rec close_blocks s l =
576+ match l with
577+ | [] -> s, []
578+ | h :: tl ->
579+ if Util. is_sublist h path then
580+ s, l
581+ else
582+ let rendered = render_level_close indent res.left h in
583+ let s' = s ^ annotate_rendered Unchanged rendered
584+ in close_blocks s' tl
585+ in
586+ let diff_str, rev_blocks = close_blocks res.config_diff res.open_blocks
587+ in
588+ match m with
589+ | Added ->
590+ let node =
591+ if (Config_tree. is_tag_value[@ alert " -exn" ]) res.right path then
592+ (Vytree. get[@ alert " -exn" ]) res.right (Util. drop_last path)
593+ else
594+ (Vytree. get[@ alert " -exn" ]) res.right path
595+ in
596+ let level = get_level_at_path res.right path in
597+ let rendered =
598+ Config_tree. render_node indent level node
599+ in
600+ let rev_diff = diff_str ^ annotate_rendered m rendered in
601+ Diff_show {res with config_diff = rev_diff; open_blocks = rev_blocks;}
602+ | Subtracted ->
603+ let node =
604+ if (Config_tree. is_tag_value[@ alert " -exn" ]) res.left path then
605+ (Vytree. get[@ alert " -exn" ]) res.left (Util. drop_last path)
606+ else
607+ (Vytree. get[@ alert " -exn" ]) res.left path
608+ in
609+ let level = get_level_at_path res.left path in
610+ let rendered =
611+ Config_tree. render_node indent level node
612+ in
613+ let rev_diff = diff_str ^ annotate_rendered m rendered in
614+ Diff_show {res with config_diff = rev_diff; open_blocks = rev_blocks;}
615+ | Unchanged ->
616+ begin
617+ match recurse with
618+ | false ->
619+ let rendered = render_level_open indent res.left path in
620+ let rev_diff = diff_str ^ annotate_rendered m rendered in
621+ Diff_show {res with config_diff = rev_diff; open_blocks = path ::rev_blocks}
622+ | true ->
623+ match path with
624+ | [] -> (* case left = right *)
625+ let rendered =
626+ Config_tree. render_config res.left
627+ in
628+ let rev_diff = diff_str ^ annotate_rendered m rendered in
629+ Diff_show {res with config_diff = rev_diff; open_blocks = rev_blocks;}
630+ | _ ->
631+ let level = get_level_at_path res.left path in
632+ let node = (Vytree. get[@ alert " -exn" ]) res.left path in
633+ let rendered =
634+ Config_tree. render_node indent level node
635+ in
636+ let rev_diff = diff_str ^ annotate_rendered m rendered in
637+ Diff_show {res with config_diff = rev_diff; open_blocks = rev_blocks;}
638+ end
639+ | Updated v ->
640+ let refp =
641+ (Reference_tree. refpath[@ alert " -exn" ]) rt (res.base_path @ path) in
642+ let multi = (Reference_tree. is_multi[@ alert " -exn" ]) rt refp in
643+ let level = get_level_at_path res.left path in
644+ let indent_str =
645+ Config_tree. make_indent indent level
646+ in
647+ let name =
648+ match Util. get_last path with
649+ | None -> (* not possible *) " none"
650+ | Some n -> n
651+ in
652+ match multi with
653+ | false ->
654+ let rendered =
655+ Config_tree. render_values indent_str name v
656+ in
657+ let rev_diff = diff_str ^ annotate_rendered m rendered in
658+ Diff_show {res with config_diff = rev_diff; open_blocks = rev_blocks;}
659+ | true ->
660+ let ov = (Config_tree. get_values[@ alert " -exn" ]) res.left path in
661+ let ov_set = ValueS. of_list ov in
662+ let v_set = ValueS. of_list v in
663+ let sub_vals = ValueS. elements (ValueS. diff ov_set v_set) in
664+ let add_vals = ValueS. elements (ValueS. diff v_set ov_set) in
665+ let inter_vals = ValueS. elements (ValueS. inter ov_set v_set) in
666+ let sub_rendered =
667+ match sub_vals with
668+ | [] -> " "
669+ | _ ->
670+ Config_tree. render_values indent_str name sub_vals
671+ in
672+ let sub_diff = annotate_rendered Subtracted sub_rendered in
673+ let add_rendered =
674+ match add_vals with
675+ | [] -> " "
676+ | _ ->
677+ Config_tree. render_values indent_str name add_vals
678+ in
679+ let add_diff = annotate_rendered Added add_rendered in
680+ let inter_rendered =
681+ match inter_vals with
682+ | [] -> " "
683+ | _ ->
684+ Config_tree. render_values indent_str name inter_vals
685+ in
686+ let inter_diff = annotate_rendered Unchanged inter_rendered in
687+ let value_diff = sub_diff ^ inter_diff ^ add_diff in
688+ let rev_diff = diff_str ^ value_diff in
689+ Diff_show {res with config_diff = rev_diff; open_blocks = rev_blocks;}
690+
691+ (* call recursive diff on config_trees with config_diff as the diff_func *)
692+ let diff_show rt path left right =
693+ (* raises:
694+ [Incommensurable]
695+ [Empty_comparison]
696+ *)
697+ if (name_of left) <> (name_of right) then
698+ raise Incommensurable
699+ else
700+ let (left, right) =
701+ if not (Util. is_empty path) then
702+ (Config_tree. get_subtree left path, Config_tree. get_subtree right path)
703+ else (left, right)
704+ in
705+ let config_show = make_diff_show left right path in
706+ let d = diff [] (config_diff rt) config_show (Option. some left, Option. some right)
707+ in
708+ let diff_show_result = eval_diff_result d in
709+ diff_show_result.config_diff
710+
469711(* mask function; mask applied on right *)
470712let mask_func ?recurse :_ (path : string list) (Diff_tree res) (m : change) =
471713 (* alert exn Vytree.delete:
0 commit comments