Source file utils.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

(* Lists all keys of an assoc list *)
let assoc_keys xs = List.fold_left (fun acc (x, _) -> x :: acc) [] xs

(* Try to nicely handle floats that have a fractional part of zero
   and are essentially integers in disguise. *)
let string_of_float f =
  if f = (Float.round f) then int_of_float f |> string_of_int
  else string_of_float f

let escape_string ?(exclude=[]) s =
  let add_escaped_char buf c =
    let add c = Buffer.add_string buf c in
    (* Since TOML allows multi-line strings that can contain non-escaped
       newlines, line feeds (\r) and tabs,
       we need a way to exclude characters from escaping. *)
    if List.find_opt ((=) c) exclude |> Option.is_some then Buffer.add_char buf c else
    match c with
    | '\\' -> add "\\\\"
    | '"'  -> add "\\\""
    | '\n' -> add "\\n"
    | '\r' -> add "\\r"
    | '\t' -> add "\\t"
    | '\b' -> add "\\b"
    | '\x00' .. '\x1F' | '\x7F' as c ->
      let char_code = Char.code c in
      let char_str =
        (* The TOML spec only allows escapes of the form \uXXXX or \uXXXXXXXX *)
        if char_code > 0xFFFF then Printf.sprintf "\\u%08x" char_code
        else Printf.sprintf "\\u%04x" char_code
      in add char_str
    | _ -> Buffer.add_char buf c
  in
  let buf = Buffer.create 4096 in
  let () = String.iter (add_escaped_char buf) s in
  Buffer.contents buf

let valid_bare_key s =
  let good_for_bare_key c =
    match c with
    | 'a' .. 'z' | 'A' .. 'Z' | '-' | '_' -> true
    | _ -> false
  in
  (* The spec says that empty keys are "valid but discouraged",
     so we have to handle then. *)
  if s = "" then false else
  String.to_seq s |> Seq.fold_left (fun acc c -> acc && (good_for_bare_key c)) true

let make_printable_key k =
  if not (valid_bare_key k) then Printf.sprintf "\"%s\"" (escape_string k)
  else k

let string_of_path ps =
  List.map make_printable_key ps |> String.concat "."

let split_list xs =
  let rec aux acc xs =
    match xs with
    | [] -> [], None
    | x :: [] -> (List.rev acc), (Some x)
    | x :: xs' -> aux (x :: acc) xs'
  in aux [] xs