[OCaml] Mobile-friendly clone of cgit.
feat Accept partial configuration files
Three keys were required even though every field has a default, so a file overriding only the port was rejected with "missing key". All keys are now optional; a present key with the wrong type is still an error.
Changed files
lib/config.ml
@@ -86,27 +86,14 @@
86
86
Out_channel.with_open_text file (fun channel ->
87
87
Printer.string_of_table table |> Printf.fprintf channel "%s\n")
88
88
89
Removed:
(* Key lookups come in two flavours. [required_*] fails when the key is absent,
90
Removed:
for settings with no sensible default; [optional_*] falls back to [~default].
91
Removed:
Both fail when the key is present but holds the wrong type, so a typo is
92
Removed:
never silently ignored.
89
Added:
(* Key lookups fall back to [~default] when the key is absent, so a partial
90
Added:
file overriding one setting is valid. A key that is present but holds the
91
Added:
wrong type is always an error, so a typo is never silently ignored.
93
92
94
93
These are deliberately not named [*_opt]: by OCaml convention that suffix
95
94
means the function returns an option, whereas here the optionality is the
96
95
key's, not the result's. *)
97
96
98
Removed:
let required_string table key =
99
Removed:
match Types.Table.find_opt (Min.key key) table with
100
Removed:
| Some (Types.TString value) -> Ok value
101
Removed:
| Some _ -> Error (Invalid_value ("expected string for key: " ^ key))
102
Removed:
| None -> Error (Invalid_value ("missing key: " ^ key))
103
Removed:
104
Removed:
let required_int table key =
105
Removed:
match Types.Table.find_opt (Min.key key) table with
106
Removed:
| Some (Types.TInt value) -> Ok value
107
Removed:
| Some _ -> Error (Invalid_value ("expected int for key: " ^ key))
108
Removed:
| None -> Error (Invalid_value ("missing key: " ^ key))
109
Removed:
110
97
let optional_string table key ~default =
111
98
match Types.Table.find_opt (Min.key key) table with
112
99
| Some (Types.TString value) -> Ok value
@@ -131,12 +118,19 @@
131
118
132
119
let of_table table =
133
120
let ( let* ) = Result.bind in
134
Removed:
let* git_project_root = required_string table "git_project_root" in
121
Added:
let* git_project_root =
122
Added:
optional_string table "git_project_root" ~default:default.git_project_root
123
Added:
in
135
124
let* user_name =
136
125
optional_string table "user_name" ~default:default.user_name
137
126
in
138
Removed:
let* default_branch = required_string table "default_branch" in
139
Removed:
let* commits_max_displayed = required_int table "commits_max_displayed" in
127
Added:
let* default_branch =
128
Added:
optional_string table "default_branch" ~default:default.default_branch
129
Added:
in
130
Added:
let* commits_max_displayed =
131
Added:
optional_int table "commits_max_displayed"
132
Added:
~default:default.commits_max_displayed
133
Added:
in
140
134
(* [ogit_root_title] is the current key; a bare [title] is still accepted so
141
135
that configuration files predating the rename keep working. *)
142
136
let* root_title =
test/test_config.ml
@@ -54,6 +54,37 @@
54
54
Alcotest.(check int) "default port" 8081 config.port
55
55
| Error error -> fail_config_error error)
56
56
57
Added:
(* Every key has a default, so a file overriding a single setting is valid;
58
Added:
the untouched settings keep their defaults. *)
59
Added:
let test_partial_file () =
60
Added:
with_temp_file "ogit-partial" ".toml" (fun file ->
61
Added:
Out_channel.with_open_text file (fun channel ->
62
Added:
output_string channel "port = 9999\n");
63
Added:
match Ogit.Config.read_file ~file () with
64
Added:
| Ok config ->
65
Added:
Alcotest.(check int) "overridden port" 9999 config.port;
66
Added:
Alcotest.(check string)
67
Added:
"default root" Ogit.Config.default.git_project_root
68
Added:
config.git_project_root;
69
Added:
Alcotest.(check string)
70
Added:
"default branch" Ogit.Config.default.default_branch
71
Added:
config.default_branch;
72
Added:
Alcotest.(check int)
73
Added:
"default commits" Ogit.Config.default.commits_max_displayed
74
Added:
config.commits_max_displayed
75
Added:
| Error error -> fail_config_error error)
76
Added:
77
Added:
(* A key that is present with the wrong type is still an error: only absence
78
Added:
falls back to the default. *)
79
Added:
let test_mistyped_key () =
80
Added:
with_temp_file "ogit-mistyped" ".toml" (fun file ->
81
Added:
Out_channel.with_open_text file (fun channel ->
82
Added:
output_string channel "git_project_root = 5\n");
83
Added:
match Ogit.Config.read_file ~file () with
84
Added:
| Error (Ogit.Config.Invalid_value _) -> ()
85
Added:
| Error error -> fail_config_error error
86
Added:
| Ok _ -> Alcotest.fail "mistyped key was accepted")
87
Added:
57
88
let test_malformed () =
58
89
with_temp_file "ogit-malformed" ".toml" (fun file ->
59
90
Out_channel.with_open_text file (fun channel ->
@@ -142,6 +173,8 @@
142
173
[
143
174
Alcotest.test_case "round trip" `Quick test_round_trip;
144
175
Alcotest.test_case "backward compat" `Quick test_backward_compat;
176
Added:
Alcotest.test_case "partial file" `Quick test_partial_file;
177
Added:
Alcotest.test_case "mistyped key" `Quick test_mistyped_key;
145
178
Alcotest.test_case "malformed" `Quick test_malformed;
146
179
Alcotest.test_case "invalid value" `Quick test_invalid_value;
147
180
Alcotest.test_case "port too high" `Quick test_invalid_port_high;