restructure to be a littel more flexible
[lwes-erlang/github-mirror.git] / src / lwes_file_watcher.erl
blob5f8cff789fc4d91bb66cdcff0e334108c20da822
1 -module (lwes_file_watcher).
3 %% API
4 -export ([ scan/1,
5 changes/1,
6 changes/2
7 ]).
9 % The goal of this modules is to watch directories and files, and alert
10 % interested parties about when things change.
12 % The usage is as follows
14 % 1. get a scan right now
16 % Scan = scan (["/tmp"]),
18 % 2. see what changed from an empty scan
20 % changes (Scan)
22 % 3. save a scan then see what changed the next time
24 % changes (OldScan, NewScan)
26 % Changes will return a list of the form
28 % [ {dir, added, "dir"},
29 % {dir, removed, "dir"},
30 % {file, added, "file"},
31 % {file, removed, "file"},
32 % {file, changed, "file"}
33 % ]
35 %%====================================================================
36 %% API
37 %%====================================================================
38 scan (FilePaths) ->
39 scan_filepaths (FilePaths).
41 changes (Scan) ->
42 file_changes ([], Scan).
44 changes (OldScan, NewScan) ->
45 file_changes (OldScan, NewScan).
47 %%====================================================================
48 %% Internal
49 %%====================================================================
50 scan_filepaths (L) ->
51 ordsets:from_list (lists:flatten (scan_filepaths (L, []))).
53 scan_filepaths ([First = [_|_]|Rest], Accum) ->
54 scan_filepaths (Rest, [ scan_filepath (First) | Accum ]);
55 scan_filepaths ([First], Accum) ->
56 [ scan_filepath (First) | Accum ];
57 scan_filepaths (First, Accum) ->
58 [ scan_filepath (First) | Accum ].
60 % non-tail recursively scan directory/file and return whether it is a
61 % file, a directory or missing
62 scan_filepath ([]) ->
63 [];
64 scan_filepath (FilePath) ->
65 case filelib:is_dir (FilePath) of
66 true ->
67 case file:list_dir (FilePath) of
68 {ok, Files} ->
69 [ { dir, FilePath } |
70 lists:foldl (
71 fun (F, A) ->
72 FP = filename:join ([FilePath, F]),
73 [ scan_filepath (FP) | A ]
74 end,
75 [],
76 Files
79 { error, _ } ->
81 end;
82 false ->
83 case filelib:is_regular (FilePath) of
84 true ->
85 [ { file, FilePath, filelib:last_modified (FilePath) } ];
86 false ->
87 [ ]
88 end
89 end.
91 file_changes (OldScan, NewScan) ->
92 file_changes (OldScan, NewScan, []).
94 file_changes ([], [], Accum) ->
95 lists:reverse (Accum);
96 file_changes (OL = [], [{dir, D} | NR], Accum) ->
97 file_changes (OL, NR, [ {dir, added, D} | Accum ]);
98 file_changes (OL = [], [{file, F, _} | NR], Accum) ->
99 file_changes (OL, NR, [ {file, added, F} | Accum ]);
100 file_changes ([{dir, D} | OR], NL = [], Accum) ->
101 file_changes (OR, NL, [ {dir, removed, D} | Accum ]);
102 file_changes ([{file, F, _} | OR], NL = [], Accum) ->
103 file_changes (OR, NL, [ {file, removed, F} | Accum ]);
104 file_changes ([O|OR], [N|NR], Accum) when O =:= N ->
105 file_changes (OR, NR, Accum);
106 file_changes ([{file, FP, OLM}|OR], [{file, FP, NLM}|NR], Accum)
107 when OLM =/= NLM ->
108 file_changes (OR, NR, [ {file, changed, FP} | Accum ]);
109 file_changes ([O = {file, F, _}|OR], NF = [N|_], Accum) when O < N ->
110 file_changes (OR, NF, [ {file, removed, F} | Accum ]);
111 file_changes ([O = {dir, D}|OR], NF = [N|_], Accum) when O < N ->
112 file_changes (OR, NF, [ {dir, removed, D} | Accum ]);
113 file_changes (OF = [O|_], [N = {file, F, _}|NR], Accum) when O > N ->
114 file_changes (OF, NR, [ {file, added, F} | Accum ]);
115 file_changes (OF = [O|_], [N = {dir, D}|NR], Accum) when O > N ->
116 file_changes (OF, NR, [ {dir, added, D} | Accum ]).
118 %%====================================================================
119 %% Test functions
120 %%====================================================================
121 -ifdef(TEST).
122 -include_lib ("eunit/include/eunit.hrl").
124 % write tests here :)
126 -endif.