require Logger
defmodule SecretsWatcherFileSystem.Backend do
@moduledoc """
A behaviour module for implementing different file system backend.
"""
@callback bootstrap() :: :ok | {:error, atom()}
@callback supported_systems() :: [{atom(), atom()}]
@callback known_events() :: [atom()]
@doc """
Get and validate backend module.
Returns `{:ok, backend_module}` upon success and `{:error, reason}` upon
failure.
When `nil` is given, will return default backend by OS.
When a custom module is given, make sure `start_link/1`, `bootstrap/0` and
`supported_system/0` are defnied.
"""
@spec backend(atom) :: {:ok, atom()} | {:error, atom()}
def backend(backend) do
with {:ok, module} <- backend_module(backend),
:ok <- validate_os(backend, module),
:ok <- module.bootstrap
do
{:ok, module}
else
{:error, reason} -> {:error, reason}
end
end
defp backend_module(nil) do
case :os.type() do
{:unix, :darwin} -> :fs_mac
{:unix, :linux} -> :fs_inotify
{:unix, :freebsd} -> :fs_inotify
{:unix, :openbsd} -> :fs_inotify
{:win32, :nt} -> :fs_windows
system -> {:unsupported_system, system}
end |> backend_module
end
defp backend_module(:fs_mac), do: {:ok, SecretsWatcherFileSystem.Backends.FSMac}
defp backend_module(:fs_inotify), do: {:ok, SecretsWatcherFileSystem.Backends.FSInotify}
defp backend_module(:fs_windows), do: {:ok, SecretsWatcherFileSystem.Backends.FSWindows}
defp backend_module(:fs_poll), do: {:ok, SecretsWatcherFileSystem.Backends.FSPoll}
defp backend_module({:unsupported_system, system}) do
Logger.error "I'm so sorry but `file_system` does NOT support your current system #{inspect system} for now."
{:error, :unsupported_system}
end
defp backend_module(module) do
functions = module.__info__(:functions)
{:start_link, 1} in functions &&
{:bootstrap, 0} in functions &&
{:supported_systems, 0} in functions ||
raise "illegal backend"
rescue
_ ->
Logger.error "You are using custom backend `#{inspect module}`, make sure it's a legal file_system backend module."
{:error, :illegal_backend}
end
defp validate_os(backend, module) do
os_type = :os.type()
if os_type in module.supported_systems() do
:ok
else
Logger.error "The backend `#{backend}` you are using does NOT support your current system #{inspect os_type}."
{:error, :unsupported_system}
end
end
end