defmodule RuntimeProvider do
@moduledoc """
Defines a ConfigProvider with validation.
Only `yaml` and `yml` are supported now.
"""
# 这里有两种实现方式,我们选择了第 2 种:
# 1. 优先获取 path ,读取文件后,使用 DSL 直接拿到 config
# 2. 使用 DSL 保存定义,最终读取 path ,整体解析
# 理由是因为想要从 provider 中提前获取 path 信息,需要使用一些 trick 。
# 如下链接代码可知,需要用到代码里定义的 private key ,比较抵触。
# https://github.com/elixir-lang/elixir/blob/b63f8f541e9d8951dbbcb39a8551bd74a3fe9a59/lib/elixir/lib/config/provider.ex#L187
@doc """
Defines a ConfigProvier.
Ignored if there is no file on the path.
"""
defmacro __using__(_opts) do
quote do
import RuntimeProvider.Definition
@definitions []
@before_compile RuntimeProvider
end
end
defmacro __before_compile__(_env) do
quote generated: true do
@behaviour Config.Provider
@impl true
def init(path), do: path
@impl true
def load(config, path) do
if File.exists?(path) do
content = read_file!(path)
file_config = RuntimeProvider.Parser.parse(content, @definitions)
Config.Reader.merge(config, file_config)
else
IO.puts(
"Could not read file #{IO.chardata_to_string(path)}: no such file or directory. Ignore runtime configuration."
)
config
end
end
defp read_file!(path) do
case Path.extname(path) do
ext when ext in [".yml", ".yaml"] ->
{:ok, _} = Application.ensure_all_started(:yaml_elixir)
YamlElixir.read_from_file!(path)
end
end
end
end
end