package codept-lib

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file fault.ml

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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
module S = Map.Make (String)
module Level = struct
type t = Format_tags.t
let info = Format_tags.Info
let notification = Format_tags.Notification
let warning = Format_tags.Warning
let error = Format_tags.Error
let critical = Format_tags.Critical

let of_string =
  function
  | "info" | "0" -> info
  | "notification" | "1" -> notification
  | "warning" | "2" -> warning
  | "error" | "3" -> error
  | "critical" | "4" -> critical
  | _ -> info

let to_int = function
  | Format_tags.Info -> 0
  | Format_tags.Notification -> 1
  | Format_tags.Warning -> 2
  | Format_tags.Error -> 3
  | Format_tags.Critical -> 4
  | _ -> 0

  let to_string = Format_tags.to_string

end

type 'a itag = ..
module type tag = sig
  type arg
  type _ itag+= E: arg itag
end
type 'a tag = (module tag with type arg = 'a)
type dtag = Dyn: 'a tag -> dtag [@@unboxed]
type fault = Err: 'a tag * 'a -> fault
type t = fault

let tag (type t) (): t tag =
  (module struct type arg = t type _ itag += E: arg itag end)


type 'a printer = Format.formatter -> 'a -> unit

type value = Fmt : 'a printer * 'a -> value
exception Fatal of value

module Log = struct

  let msg lvl simple fatal critical printer ppf x=
    let title = if critical then fatal else simple in
    Format.fprintf ppf "@[[%a]: %a@]@."
      (Format_tags.tagged lvl) title
    printer x

  let kcritical x = msg Level.critical "Critical error" "Critical error" x

  let kerror x = msg Level.error "Error" "Fatal error" x
  let kwarning x = msg Level.warning "Warning" "Fatal warning" x
  let knotification x = msg Level.notification "Notification" "Fatal notification" x
  let kinfo x = msg Level.info "Misc" "Fatal accident" x

  let critical fmt = kcritical true fmt
  let error fmt = kerror false fmt
  let warning fmt = kwarning false fmt
  let notification fmt = kwarning false fmt
  let info fmt = kinfo false fmt

end

type log_info = { silent:Level.t; level:Level.t; exit:Level.t}
let log i printer ppf x =
  let fns = Log.[| kinfo; knotification; kwarning; kerror; kcritical |] in
  let fn x = fns.(Level.to_int i.level) (i.level >= i.exit) printer ppf x in
  if i.level < i.silent then ()
  else if i.level >= Level.critical then
    Log.critical printer ppf x
  else if i.level >= i.exit then
    (fn x; raise (Fatal (Fmt (printer, x))))
  else
    fn x

type explanation = string

type 'a info = {
  tag: 'a tag;
  path: string list;
  expl: explanation;
  printer: Format.formatter -> 'a -> unit
}

let info path expl printer =
  let tag = tag () in
  { tag; path; expl; printer }

let emit info x = Err(info.tag,x)

type dyn_info = Info: 'a info -> dyn_info [@@unboxed]

let check (type a) ((module X): a tag) (Info info) =
  let module Y = (val info.tag) in
  match Y.E with
  | X.E -> Some (info:a info)
  | _ -> None

module Policy = struct

  type map =
    | Level of {expl: explanation; lvl: Level.t option}
    | Map of {expl:explanation; lvl:Level.t option; map: map S.t}

  module Register = Map.Make(struct type t = dtag let compare=compare end)
  type t = {
    silent: Level.t;
    exit:Level.t;
    map:map;
    register: dyn_info Register.t
  }
  type policy = t

  let make ~silent ~exit =
    let map = Level { expl = ""; lvl = Some Level.critical } in
    { silent; exit; map; register = Register.empty }

  let find_info tag p =
    Register.find (Dyn tag) p.register

  let rec find_lvl default pol l  =
    let with_default = Option.default default in
    match pol, l with
    | Level h, _ -> with_default h.lvl
    | Map m, a :: q  ->
      begin
        let h = with_default m.lvl in
        try find_lvl h (S.find a m.map) q with
          Not_found -> h
      end
    | Map m, [] -> with_default m.lvl

  let level_info {map; exit; _ } (Info error) =
    find_lvl exit map error.path

  let level p (Err(tag,_)) =
    let info = find_info tag p in
    level_info p info

  let rec set ?lvl ?expl path env = match path, env with
    | [], Level l -> Level {expl = Option.default l.expl expl;lvl }
    | [], Map m -> Map { m with lvl; expl = Option.default m.expl expl }
    | a :: q, Level l ->
      Map{ lvl = None; expl = "";
           map=S.singleton a @@ set ?lvl ?expl q @@ Level l
         }
    | a :: q, Map m ->
      let env' =
        match S.find a m.map with
        | x -> x
        | exception Not_found -> Level {lvl=m.lvl; expl = m.expl}
      in
      let elt = set ?lvl ?expl q env' in
      let map = S.add a elt m.map in
      Map{m with map}

  let set ?lvl ?expl p pol = { pol with map = set ?lvl ?expl p pol.map }

  let register ?lvl error policy =
    let policy = set ?lvl ~expl:error.expl error.path policy in
    let register = Register.add (Dyn error.tag) (Info error) policy.register in
    { policy with register }

  let pp_lvl ppf = function
    | None -> ()
    | Some lvl ->
      let name = Level.to_string lvl in
      Pp.fp ppf "[%a]" Format_tags.(tagged lvl) name

  let rec pp_map ppf = function
    | name, Level {expl; lvl} ->
      Pp.fp ppf "@;@<1>%s%a%a:@;@[<hov>%a@]"
        "−"
        Format_tags.(tagged Title) name
        pp_lvl lvl
        Format.pp_print_text expl
    | name, Map { lvl; expl; map } ->
      Pp.fp ppf "@;@<1>%s%a%a:%s@; @[<v2> %a @]"
        "−"
        Format_tags.(tagged Title) name
        pp_lvl lvl
        expl
        Pp.( list ~sep:(s "@;") @@ pp_map) (S.bindings map)


  let pp ppf pol = Pp.fp ppf "%a@." pp_map ("Policy",pol.map)

  let set_exit exit p = { p with exit }
  let set_silent silent p = {p with silent}

  end

type handler = { policy:Policy.t; err_formatter:Format.formatter }
let register = Policy.register
let handle {policy;err_formatter} (Err(tag,e)) =
  let info = Policy.find_info tag policy in
  let log_info = {
    level = Policy.level_info policy info;
    silent = policy.silent;
    exit = policy.exit;
  } in
  Option.iter (fun info -> log log_info info.printer err_formatter e) (check tag info)

let raise h info x = handle h (emit info x)

let is_silent policy info =
  Policy.level_info policy (Info info) <= policy.silent