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
let hash = Str.regexp "#"
let section_marker = Str.regexp "\\[[ \\t]*\\(.+\\)[ \\t]*\\]"
let value_marker = Str.regexp "\\(.+\\)=\\(.+\\)"
exception InvalidFormat of string
let load_ini path =
Eio.Path.with_lines path (fun lines ->
let sections : (string * (string * string) list) list ref = ref [] in
let section : string option ref = ref None in
let values : (string * string) list ref = ref [] in
let push_section () =
if Option.is_some !section then begin
sections := (!section |> Option.get, !values |> List.rev) :: !sections;
section := None;
values := []
end
in
Seq.iteri
(fun line_no line ->
let = line |> Str.split hash |> List.hd_opt |> Option.value ~default:"" in
if Str.string_match section_marker without_comment 0 then begin
push_section ();
section := Some (Str.matched_group 1 without_comment)
end
else if Str.string_match value_marker without_comment 0 then begin
if Option.is_none !section then
raise (InvalidFormat (Fmt.str "value without section on line %d" line_no));
let key = Str.matched_group 1 without_comment |> String.trim in
let value = Str.matched_group 2 without_comment |> String.trim in
values := (key, value) :: !values
end
else if not (without_comment |> String.trim |> String.length = 0) then begin
raise (InvalidFormat (Fmt.str "could not parse line %d" line_no))
end)
lines;
push_section ();
!sections |> List.rev)