require Logger
defmodule FileSystem.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, FileSystem.Backends.FSMac}
defp backend_module(:fs_inotify), do: {:ok, FileSystem.Backends.FSInotify}
defp backend_module(:fs_windows), do: {:ok, FileSystem.Backends.FSWindows}
defp backend_module(:fs_poll), do: {:ok, FileSystem.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