%% -*- coding: utf-8 -*-


%% @doc ファイル名の一部を指定してファイルの操作.
%%
%% ディレクトリのリスト(検索パス)と拡張子(接尾辞)のリストに基づき
%% 実在するファイルを探す(find)、開く(open)、読む(read)。


-module(findf_lib).

-export([find_file/3, open_file/3, read_file/3]).

% -compile(export_all).


%% @headerfile "../include/findf_types.hrl"
-include("../include/findf_types.hrl").

%% @doc ファイル名の一部から、
%% ディレクトリ・リストと拡張子リストをもとに、
%% 実在するファイルを探す.
%%
%% @spec (Name::string(), Dirs::[string()], Exts::[string()])
%%       -> string()
%% @throws file_error()
find_file(_Name, [], _Exts) ->
      % 探すべき場所がない
    throw(enoent); % POSIXエラーコード
find_file(Name, [Dir | RestDirs], Exts) ->
    case find_file_within(Name, Dir, Exts) of
      not_found -> find_file(Name, RestDirs, Exts);
      FilePath -> FilePath
    end.

%% @doc 単一のディレクトリ内で、
%% 拡張子リストをもとに、
%% 実在するファイルを探す.
%% (内部的関数なので、戻り値not_foundを使用).
%%
%% @spec (string(), string(), Exts::[string()])
%%       -> not_found | string()


find_file_within(_Name, _Dir, []) -> not_found;
find_file_within(Name, Dir, [Ext | RestExts]) ->
    FilePath = filename:join(Dir, string:concat(Name, Ext)),
    case filelib:is_regular(FilePath) of
      true -> FilePath;
      false -> find_file_within(Name, Dir, RestExts)
    end.

%% @type file_function() = function().
%% ファイル名文字列をただ1つの引数とするファイル操作関数.
%% 実際には、file:open/1 と file:read_file/1 .


%% @spec (string(), [string()], [string()], file_function())
%%       -> any()
%% @throws file_error()


find_file_and_do(Name, Dirs, Exts, Fun) ->
    FilePath = find_file(Name, Dirs, Exts),
    case Fun(FilePath) of
      {ok, Value} -> Value;
      {error, Reason} -> throw(Reason)
    end.

%% @doc find_file/3 で見つかったファイルをオープンする.
%% @spec (string(), [string()], [string()]) -> pid()
%% @throws file_error()


open_file(Name, Dirs, Exts) ->
    find_file_and_do(Name, Dirs, Exts, fun (X) -> file:open(X, [read]) end).

%% @doc find_file/3 で見つかったファイルを一括リードして、
%% 内容をバイナリとして返す.
%% @spec (string(), [string()], [string()]) -> binary()
%% @throws file_error()


read_file(Name, Dirs, Exts) ->
    find_file_and_do(Name, Dirs, Exts, fun (X) -> file:read_file(X) end).