-
-
Save palkan/7926149 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(file_utils). | |
-export([recursively_list_dir/1, | |
recursively_list_dir/2, | |
recursively_del_dir/1, | |
dir_traversal/2, | |
dir_traversal/3]). | |
% @type name() = string() | atom() | binary(). | |
-type name() :: string() | atom() | binary(). | |
%% API | |
% @spec (Dir::name()) -> {ok, [string()]} | {error, atom()} | |
% @equiv recursively_list_dir(Dir, false) | |
% | |
% @doc Lists all the files in a directory and recursively in all its | |
% sub directories. Returns {ok, Paths} if successful. Otherwise, | |
% it returns {error, Reason}. Paths is a list of Paths of all the | |
% files and directories in the input directory's subtree. The paths are not | |
% sorted in any order. | |
-spec recursively_list_dir(Dir::name()) -> | |
{ok, [string()]} | {error, atom()}. | |
recursively_list_dir(Dir) -> | |
recursively_list_dir(Dir, false). % default value of FilesOnly is false | |
% @spec (Dir::name(), FilesOnly::boolean()) -> {ok, [string()]} | | |
% {error, atom()} | |
% | |
% @doc Lists all the files in a directory and recursively in all its | |
% sub directories. Returns {ok, Paths} if successful. Otherwise, | |
% it returns {error, Reason}. If FilesOnly is false, Paths is a list of paths | |
% of all the files <b>and directories</b> in the input directory's subtree. | |
% If FilesOnly is true, Paths is a list of paths of only the files in the | |
% input directory's subtree. The paths are not sorted in any order. | |
-spec recursively_list_dir(Dir::name(), FilesOnly::boolean()) -> | |
{ok, [string()]} | {error, atom()}. | |
recursively_list_dir(Dir, FilesOnly) -> | |
case filelib:is_file(Dir) of | |
true -> | |
case filelib:is_dir(Dir) of | |
true -> {ok, dir_traversal([Dir], fun(Path,Acc) -> [Path|Acc] end, FilesOnly)}; | |
false -> {error, enotdir} | |
end; | |
false -> {error, enoent} | |
end. | |
% @spec (Dir::name()) -> ok | {error, atom()} | |
% | |
% @doc Delete all files and directories within Dir (and Dir itself). | |
% Return {error, Reason} if Dir doesn't exist or is not directory. | |
-spec recursively_del_dir(Dir::name()) -> | |
ok | {error, atom()}. | |
recursively_del_dir(Dir) -> | |
case recursively_list_dir(Dir) of | |
{ok,List} -> del_paths(List); | |
Error -> Error | |
end. | |
% @spec (Dir::name(),Fun::function()) -> {ok, Result::list()} | {error, atom()} | |
% @equiv dir_traversal(Dir,Fun,false) | |
% | |
% @doc Traverse through directory from top to bottom and execute Fun on each path. | |
%% Fun must have signature fun(Path::string(), Acc:list()) -> list(). | |
-spec dir_traversal(Dir::name(),Fun::function()) -> | |
{ok,Result::list()} | {error, atom()}. | |
dir_traversal(Dir,Fun) -> dir_traversal(Dir,Fun,false). | |
% @spec (Dir::name(),Fun::function(),FilesOnly::boolean()) -> {ok, Result::list()} | {error, atom()} | |
% @equiv dir_traversal(Dir,Fun,false) | |
% | |
% @doc Traverse through directory from top to bottom and execute Fun on each path. | |
% If FilesOnly is true, then affects only files. | |
% Fun must have signature fun(Path::string(), Acc:list()) -> list(). | |
-spec dir_traversal(Dir::name(),Fun::function(),FilesOnly::boolean()) -> | |
{ok,Result::list()} | {error, atom()}. | |
dir_traversal(Dir,Fun,FilesOnly) -> | |
dir_traversal(Dir,Fun,FilesOnly,[]). | |
%% Internal | |
dir_traversal([], _Fun, _FilesOnly, Acc) -> Acc; | |
dir_traversal([Path|Paths], Fun, FilesOnly, Acc) -> | |
dir_traversal(Paths, Fun, FilesOnly, | |
case filelib:is_dir(Path) of | |
false -> Fun(Path,Acc); | |
true -> | |
{ok, Listing} = file:list_dir(Path), | |
SubPaths = [filename:join(Path, Name) || Name <- Listing], | |
dir_traversal(SubPaths, Fun, FilesOnly, | |
case FilesOnly of | |
true -> Acc; | |
false -> Fun(Path,Acc) | |
end) | |
end). | |
del_paths([]) -> ok; | |
del_paths([Path|Paths]) -> | |
case filelib:is_dir(Path) of | |
true -> file:del_dir(Path); | |
false -> file:delete(Path) | |
end, | |
del_paths(Paths). | |
%% Tests | |
-ifdef(TEST). | |
-include_lib("eunit/include/eunit.hrl"). | |
non_existing_file_returns_error_test() -> | |
?assertEqual({error, enoent}, | |
recursively_list_dir("UnUSuAalfIlEnaMe")), | |
ok. | |
non_directory_input_returns_error_test() -> | |
file:write_file("f1.test", <<"temp test file">>), | |
?assertEqual({error, enotdir}, | |
recursively_list_dir("f1.test")), | |
file:delete("f1.test"), | |
ok. | |
list_test_() -> | |
{"List dir tests", | |
{foreach, | |
fun list_setup/0, | |
fun list_cleanup/1, | |
[ | |
fun list_simple_t/1, | |
fun list_nofiles_t/1, | |
fun list_filesonly_t/1 | |
] | |
} | |
}. | |
del_test_() -> | |
{"delete dir tests", | |
{foreach, | |
fun delete_setup/0, | |
fun list_cleanup/1, | |
[ | |
fun del_no_dir_t/1, | |
fun del_simple_t/1 | |
] | |
} | |
}. | |
list_simple_t(_) -> | |
filelib:ensure_dir("a/b/c/"), | |
file:write_file("a/b/f.test", <<"temp test file">>), | |
[?_assertEqual({ok, ["a/b/f.test","a/b/c","a/b","a"]}, | |
recursively_list_dir("a"))]. | |
list_nofiles_t(_) -> | |
filelib:ensure_dir("a/b/c/"), | |
[?_assertEqual({ok, ["a/b/c", "a/b", "a"]},recursively_list_dir("a"))]. | |
list_filesonly_t(_) -> | |
filelib:ensure_dir("a/b/f.test"), | |
file:write_file("a/b/f.test", <<"hello">>), | |
[?_assertEqual({ok, ["a/b/f.test"]}, | |
recursively_list_dir("a", true))]. | |
list_setup() -> | |
ok. | |
list_cleanup(_) -> | |
file:delete("f1.test"), | |
file:delete("a/b/f.test"), | |
file:del_dir("a/b/c"), | |
file:del_dir("a/b"), | |
file:del_dir("a"), | |
ok. | |
del_no_dir_t(_) -> | |
[ | |
?_assertEqual({error,enoent},recursively_del_dir("UnUSuAalfIlEnaMe")), | |
?_assertEqual({error,enotdir},recursively_del_dir("a/b/f.test")) | |
]. | |
del_simple_t(_) -> | |
[ | |
?_assertEqual(ok,recursively_del_dir("a")), | |
?_assertNot(filelib:is_file("a/b")) | |
]. | |
delete_setup() -> | |
filelib:ensure_dir("a/b/c/"), | |
file:write_file("a/b/f.test", <<"temp test file">>), | |
ok. | |
-endif. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment