Skip to content

Commit 38e4464

Browse files
committed
use nimble options for config validation
1 parent 4945abc commit 38e4464

1 file changed

Lines changed: 81 additions & 126 deletions

File tree

lib/shinkai/config.ex

Lines changed: 81 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,61 @@ defmodule Shinkai.Config do
55

66
@top_level_keys [:rtmp, :server, :hls]
77

8-
@default_config [
9-
rtmp: [
10-
enabled: true,
11-
port: 1935
8+
@rtmp_schema [
9+
enabled: [
10+
type: :boolean,
11+
default: true,
12+
doc: "Enable or disable rtmp"
1213
],
13-
server: [
14-
enabled: true,
15-
port: 8888,
16-
certfile: nil,
17-
keyfile: nil
14+
port: [
15+
type: {:in, 0..(2 ** 16 - 1)},
16+
default: 1935,
17+
doc: "RTMP listening port"
18+
]
19+
]
20+
21+
@server_schema [
22+
enabled: [
23+
type: :boolean,
24+
default: true,
25+
doc: "Enable or disable http(s) server"
26+
],
27+
port: [
28+
type: {:in, 0..(2 ** 16 - 1)},
29+
default: 8888,
30+
doc: "http port"
31+
],
32+
certfile: [
33+
type: :string,
34+
doc: "https certificate"
35+
],
36+
keyfile: [
37+
type: :string,
38+
doc: "https private key certificate"
39+
]
40+
]
41+
42+
@hls_schema [
43+
storage_dir: [
44+
type: :string,
45+
default: "/tmp/shinkai/hls"
46+
],
47+
max_segments: [
48+
type: :non_neg_integer,
49+
default: 7,
50+
doc: "Max segments to keep in live playlists"
51+
],
52+
segment_duration: [
53+
type: :non_neg_integer,
54+
default: 2000
55+
],
56+
part_duration: [
57+
type: :non_neg_integer,
58+
default: 300
1859
],
19-
hls: [
20-
storage_dir: "/tmp/shinkai/hls",
21-
max_segments: 7,
22-
segment_duration: 2_000,
23-
part_duration: 500,
24-
segment_type: :fmp4
60+
segment_type: [
61+
type: {:custom, __MODULE__, :validate_hls_segment_type, []},
62+
default: :fmp4
2563
]
2664
]
2765

@@ -48,10 +86,10 @@ defmodule Shinkai.Config do
4886

4987
app_configs = Enum.map(@top_level_keys, &{&1, Application.get_env(:shinkai, &1, [])})
5088

51-
Enum.map(@default_config, fn {key, config} ->
52-
config
89+
Enum.map(@top_level_keys, fn key ->
90+
[]
5391
|> Keyword.merge(app_configs[key])
54-
|> Keyword.merge(user_config[key] || [])
92+
|> Keyword.merge(user_config[key])
5593
|> then(&{key, &1})
5694
end)
5795
end
@@ -80,7 +118,9 @@ defmodule Shinkai.Config do
80118

81119
case Map.keys(config) -- keys do
82120
[] ->
83-
Enum.map(config, fn {key, value} -> {String.to_atom(key), value} end)
121+
@top_level_keys
122+
|> Enum.map(&{&1, []})
123+
|> Keyword.merge(Enum.map(config, fn {key, value} -> {String.to_atom(key), value} end))
84124

85125
invalid_keys ->
86126
raise ArgumentError, """
@@ -96,128 +136,43 @@ defmodule Shinkai.Config do
96136
defp parse_and_validate([], acc), do: acc
97137

98138
defp parse_and_validate([{:hls, hls_config} | rest], acc) do
99-
hls_config = parse_and_validate_hls(hls_config)
139+
hls_config = do_parse_and_validate(hls_config, @hls_schema)
100140
parse_and_validate(rest, [{:hls, hls_config} | acc])
101141
end
102142

103143
defp parse_and_validate([{:server, server_config} | rest], acc) do
104-
server_config = parse_and_validate_server(server_config)
144+
server_config = do_parse_and_validate(server_config, @server_schema)
105145
parse_and_validate(rest, [{:server, server_config} | acc])
106146
end
107147

108148
defp parse_and_validate([{:rtmp, rtmp_config} | rest], acc) do
109-
rtmp_config = parse_and_validate_rtmp(rtmp_config)
149+
rtmp_config = do_parse_and_validate(rtmp_config, @rtmp_schema)
110150
parse_and_validate(rest, [{:rtmp, rtmp_config} | acc])
111151
end
112152

113-
defp parse_and_validate_hls(config, acc \\ [])
153+
defp do_parse_and_validate(config, schame) do
154+
config = config || []
114155

115-
defp parse_and_validate_hls(nil, _acc), do: []
116-
defp parse_and_validate_hls([], acc), do: acc
156+
cond do
157+
Keyword.keyword?(config) ->
158+
NimbleOptions.validate!(config, schame)
117159

118-
defp parse_and_validate_hls(config, acc) when is_map(config) do
119-
parse_and_validate_hls(Map.to_list(config), acc)
120-
end
121-
122-
defp parse_and_validate_hls([{:segment_type, value} | rest], acc)
123-
when value in [:fmp4, :mpeg_ts, :low_latency] do
124-
parse_and_validate_hls(rest, [{:segment_type, value} | acc])
125-
end
126-
127-
defp parse_and_validate_hls([{"segment_type", value} | rest], acc)
128-
when value in ["fmp4", "mpeg_ts", "low_latency"] do
129-
parse_and_validate_hls(rest, [{:segment_type, String.to_atom(value)} | acc])
130-
end
131-
132-
defp parse_and_validate_hls([{key, value} | rest], acc)
133-
when key in ["segment_duration", :segment_duration] and is_integer(value) and value >= 1000 do
134-
parse_and_validate_hls(rest, [{:segment_duration, value} | acc])
135-
end
136-
137-
defp parse_and_validate_hls([{key, value} | rest], acc)
138-
when key in ["max_segments", :max_segments] and is_integer(value) and value > 3 do
139-
parse_and_validate_hls(rest, [{:max_segments, value} | acc])
140-
end
160+
is_map(config) ->
161+
config
162+
|> Keyword.new(fn {k, v} -> {String.to_existing_atom(k), v} end)
163+
|> NimbleOptions.validate!(schame)
141164

142-
defp parse_and_validate_hls([{key, value} | rest], acc)
143-
when key in ["part_duration", :part_duration] and is_integer(value) and value >= 100 and
144-
value < 1000 do
145-
parse_and_validate_hls(rest, [{:part_duration, value} | acc])
146-
end
147-
148-
defp parse_and_validate_hls([{key, value} | rest], acc)
149-
when key in ["storage_dir", :storage_dir] do
150-
parse_and_validate_hls(rest, [{:storage_dir, value} | acc])
151-
end
152-
153-
defp parse_and_validate_hls([{key, value} | _rest], _acc) do
154-
raise ArgumentError, """
155-
Invalid HLS configuration key or value detected.
156-
Key: #{inspect(key)}, Value: #{inspect(value)}.
157-
"""
158-
end
159-
160-
defp parse_and_validate_hls(config, _acc) do
161-
raise ArgumentError, """
162-
Invalid HLS configuration format detected.
163-
Config: #{inspect(config)}.
164-
"""
165-
end
166-
167-
# HTTP server
168-
defp parse_and_validate_server(config, acc \\ [])
169-
defp parse_and_validate_server(nil, _acc), do: []
170-
defp parse_and_validate_server([], acc), do: acc
171-
172-
defp parse_and_validate_server(config, acc) when is_map(config) do
173-
parse_and_validate_server(Map.to_list(config), acc)
174-
end
175-
176-
defp parse_and_validate_server([{key, value} | rest], acc)
177-
when key in ["enabled", :enabled] and is_boolean(value) do
178-
parse_and_validate_server(rest, [{:enabled, value} | acc])
179-
end
180-
181-
defp parse_and_validate_server([{key, value} | rest], acc)
182-
when key in [:port, "port"] and is_integer(value) and value > 0 and value < 65_536 do
183-
parse_and_validate_server(rest, [{:port, value} | acc])
184-
end
185-
186-
defp parse_and_validate_server([{key, value} | rest], acc)
187-
when key in ["certfile", "keyfile", :certfile, :keyfile] do
188-
parse_and_validate_server(rest, [{String.to_atom(key), value} | acc])
189-
end
190-
191-
defp parse_and_validate_server([{key, value} | _rest], _acc) do
192-
raise ArgumentError, """
193-
Invalid Server configuration key or value detected.
194-
Key: #{inspect(key)}, Value: #{inspect(value)}.
195-
"""
196-
end
197-
198-
# RTMP
199-
defp parse_and_validate_rtmp(config, acc \\ [])
200-
defp parse_and_validate_rtmp(nil, _acc), do: []
201-
defp parse_and_validate_rtmp([], acc), do: acc
202-
203-
defp parse_and_validate_rtmp(config, acc) when is_map(config) do
204-
parse_and_validate_rtmp(Map.to_list(config), acc)
205-
end
206-
207-
defp parse_and_validate_rtmp([{key, value} | rest], acc)
208-
when key in ["enabled", :enabled] and is_boolean(value) do
209-
parse_and_validate_rtmp(rest, [{:enabled, value} | acc])
210-
end
211-
212-
defp parse_and_validate_rtmp([{key, value} | rest], acc)
213-
when key in [:port, "port"] and is_integer(value) and value > 0 and value < 65_536 do
214-
parse_and_validate_rtmp(rest, [{:port, value} | acc])
165+
true ->
166+
raise "Expected a map or keyword list received: #{inspect(config)}"
167+
end
215168
end
216169

217-
defp parse_and_validate_rtmp([{key, value} | _rest], _acc) do
218-
raise ArgumentError, """
219-
Invalid RTMP configuration key or value detected.
220-
Key: #{inspect(key)}, Value: #{inspect(value)}.
221-
"""
170+
@doc false
171+
def validate_hls_segment_type(value) do
172+
cond do
173+
value in [:mpeg_ts, :fmp4, :low_latency] -> {:ok, value}
174+
value in ["mpeg_ts", "fmp4", "low_latency"] -> {:ok, String.to_atom(value)}
175+
true -> {:error, value}
176+
end
222177
end
223178
end

0 commit comments

Comments
 (0)