aboutsummaryrefslogtreecommitdiff
path: root/learn-you-some-erlang/release/erlcount-1.0/src
diff options
context:
space:
mode:
authorTrygve Laugstøl <trygvis@inamo.no>2024-02-23 07:08:18 +0100
committerTrygve Laugstøl <trygvis@inamo.no>2024-02-23 07:08:18 +0100
commit5a9cdd3cc89507d4d74f8bded56ce5e037b3b56e (patch)
tree982ca2e7f9ac4e8c350dfb5c4f60bcfdfff5afaf /learn-you-some-erlang/release/erlcount-1.0/src
parent05ae56e5e89abf2993f84e6d52b250131f247c35 (diff)
downloaderlang-workshop-5a9cdd3cc89507d4d74f8bded56ce5e037b3b56e.tar.gz
erlang-workshop-5a9cdd3cc89507d4d74f8bded56ce5e037b3b56e.tar.bz2
erlang-workshop-5a9cdd3cc89507d4d74f8bded56ce5e037b3b56e.tar.xz
erlang-workshop-5a9cdd3cc89507d4d74f8bded56ce5e037b3b56e.zip
wip
Diffstat (limited to 'learn-you-some-erlang/release/erlcount-1.0/src')
-rw-r--r--learn-you-some-erlang/release/erlcount-1.0/src/erlcount.erl9
-rw-r--r--learn-you-some-erlang/release/erlcount-1.0/src/erlcount_counter.erl35
-rw-r--r--learn-you-some-erlang/release/erlcount-1.0/src/erlcount_dispatch.erl86
-rw-r--r--learn-you-some-erlang/release/erlcount-1.0/src/erlcount_lib.erl55
-rw-r--r--learn-you-some-erlang/release/erlcount-1.0/src/erlcount_sup.erl17
5 files changed, 202 insertions, 0 deletions
diff --git a/learn-you-some-erlang/release/erlcount-1.0/src/erlcount.erl b/learn-you-some-erlang/release/erlcount-1.0/src/erlcount.erl
new file mode 100644
index 0000000..16a9f23
--- /dev/null
+++ b/learn-you-some-erlang/release/erlcount-1.0/src/erlcount.erl
@@ -0,0 +1,9 @@
+-module(erlcount).
+-behaviour(application).
+-export([start/2, stop/1]).
+
+start(normal, _Args) ->
+ erlcount_sup:start_link().
+
+stop(_State) ->
+ ok.
diff --git a/learn-you-some-erlang/release/erlcount-1.0/src/erlcount_counter.erl b/learn-you-some-erlang/release/erlcount-1.0/src/erlcount_counter.erl
new file mode 100644
index 0000000..c42fd4d
--- /dev/null
+++ b/learn-you-some-erlang/release/erlcount-1.0/src/erlcount_counter.erl
@@ -0,0 +1,35 @@
+-module(erlcount_counter).
+-behaviour(gen_server).
+-export([start_link/4]).
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-record(state, {dispatcher, ref, file, re}).
+
+start_link(DispatcherPid, Ref, FileName, Regex) ->
+ gen_server:start_link(?MODULE, [DispatcherPid, Ref, FileName, Regex], []).
+
+init([DispatcherPid, Ref, FileName, Regex]) ->
+ self() ! start,
+ {ok, #state{dispatcher=DispatcherPid,
+ ref = Ref,
+ file = FileName,
+ re = Regex}}.
+
+handle_call(_Msg, _From, State) ->
+ {noreply, State}.
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+handle_info(start, S = #state{re=Re, ref=Ref}) ->
+ {ok, Bin} = file:read_file(S#state.file),
+ Count = erlcount_lib:regex_count(Re, Bin),
+ erlcount_dispatch:complete(S#state.dispatcher, Re, Ref, Count),
+ {stop, normal, S}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
diff --git a/learn-you-some-erlang/release/erlcount-1.0/src/erlcount_dispatch.erl b/learn-you-some-erlang/release/erlcount-1.0/src/erlcount_dispatch.erl
new file mode 100644
index 0000000..c125da3
--- /dev/null
+++ b/learn-you-some-erlang/release/erlcount-1.0/src/erlcount_dispatch.erl
@@ -0,0 +1,86 @@
+-module(erlcount_dispatch).
+-behaviour(gen_fsm).
+-export([start_link/0, complete/4]).
+-export([init/1, dispatching/2, listening/2, handle_event/3,
+ handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
+
+-define(POOL, erlcount).
+-record(data, {regex=[], refs=[]}).
+
+%%% PUBLIC API
+start_link() ->
+ gen_fsm:start_link(?MODULE, [], []).
+
+complete(Pid, Regex, Ref, Count) ->
+ gen_fsm:send_all_state_event(Pid, {complete, Regex, Ref, Count}).
+
+%%% GEN_FSM
+%% Two states: dispatching and listening
+init([]) ->
+ %% Move the get_env stuff to the supervisor's init.
+ {ok, Re} = application:get_env(regex),
+ {ok, Dir} = application:get_env(directory),
+ {ok, MaxFiles} = application:get_env(max_files),
+ ppool:start_pool(?POOL, MaxFiles, {erlcount_counter, start_link, []}),
+ case lists:all(fun valid_regex/1, Re) of
+ true ->
+ %% creates a regex entry of the form [{Re, Count}]
+ self() ! {start, Dir},
+ {ok, dispatching, #data{regex=[{R,0} || R <- Re]}};
+ false ->
+ {stop, invalid_regex}
+ end.
+
+dispatching({continue, File, Continuation}, Data = #data{regex=Re, refs=Refs}) ->
+ F = fun({Regex,_Count}, NewRefs) ->
+ Ref = make_ref(),
+ ppool:async_queue(?POOL, [self(), Ref, File, Regex]),
+ [Ref|NewRefs]
+ end,
+ NewRefs = lists:foldl(F, Refs, Re),
+ gen_fsm:send_event(self(), Continuation()),
+ {next_state, dispatching, Data#data{refs = NewRefs}};
+dispatching(done, Data) ->
+ %% This is a special case. We can not assume that all messages have NOT
+ %% been received by the time we hit 'done'. As such, we directly move to
+ %% listening/2 without waiting for an external event.
+ listening(done, Data).
+
+listening(done, #data{regex=Re, refs=[]}) -> % all received!
+ [io:format("Regex ~s has ~p results~n", [R,C]) || {R, C} <- Re],
+ {stop, normal, done};
+listening(done, Data) -> % entries still missing
+ {next_state, listening, Data}.
+
+handle_event({complete, Regex, Ref, Count}, State, Data = #data{regex=Re, refs=Refs}) ->
+ {Regex, OldCount} = lists:keyfind(Regex, 1, Re),
+ NewRe = lists:keyreplace(Regex, 1, Re, {Regex, OldCount+Count}),
+ NewData = Data#data{regex=NewRe, refs=Refs--[Ref]},
+ case State of
+ dispatching ->
+ {next_state, dispatching, NewData};
+ listening ->
+ listening(done, NewData)
+ end.
+
+handle_sync_event(Event, _From, State, Data) ->
+ io:format("Unexpected event: ~p~n", [Event]),
+ {next_state, State, Data}.
+
+handle_info({start, Dir}, State, Data) ->
+ gen_fsm:send_event(self(), erlcount_lib:find_erl(Dir)),
+ {next_state, State, Data}.
+
+terminate(_Reason, _State, _Data) ->
+ init:stop().
+
+code_change(_OldVsn, State, Data, _Extra) ->
+ {ok, State, Data}.
+
+%%% PRIVATE
+valid_regex(Re) ->
+ try re:run("", Re) of
+ _ -> true
+ catch
+ error:badarg -> false
+ end.
diff --git a/learn-you-some-erlang/release/erlcount-1.0/src/erlcount_lib.erl b/learn-you-some-erlang/release/erlcount-1.0/src/erlcount_lib.erl
new file mode 100644
index 0000000..70c062f
--- /dev/null
+++ b/learn-you-some-erlang/release/erlcount-1.0/src/erlcount_lib.erl
@@ -0,0 +1,55 @@
+-module(erlcount_lib).
+-export([find_erl/1, regex_count/2]).
+-include_lib("kernel/include/file.hrl").
+
+%% Finds all files ending in .erl
+find_erl(Directory) ->
+ find_erl(Directory, queue:new()).
+
+regex_count(Re, Str) ->
+ case re:run(Str, Re, [global]) of
+ nomatch -> 0;
+ {match, List} -> length(List)
+ end.
+
+%%% Private
+%% Dispatches based on file type
+find_erl(Name, Queue) ->
+ {ok, F = #file_info{}} = file:read_file_info(Name),
+ case F#file_info.type of
+ directory -> handle_directory(Name, Queue);
+ regular -> handle_regular_file(Name, Queue);
+ _Other -> dequeue_and_run(Queue)
+ end.
+
+%% Opens directories and enqueues files in there
+handle_directory(Dir, Queue) ->
+ case file:list_dir(Dir) of
+ {ok, []} ->
+ dequeue_and_run(Queue);
+ {ok, Files} ->
+ dequeue_and_run(enqueue_many(Dir, Files, Queue))
+ end.
+
+%% Checks if the file finishes in .erl
+handle_regular_file(Name, Queue) ->
+ case filename:extension(Name) of
+ ".erl" ->
+ {continue, Name, fun() -> dequeue_and_run(Queue) end};
+ _NonErl ->
+ dequeue_and_run(Queue)
+ end.
+
+%% Pops an item from the queue and runs it.
+dequeue_and_run(Queue) ->
+ case queue:out(Queue) of
+ {empty, _} -> done;
+ {{value, File}, NewQueue} -> find_erl(File, NewQueue)
+ end.
+
+%% Adds a bunch of items to the queue.
+enqueue_many(Path, Files, Queue) ->
+ F = fun(File, Q) -> queue:in(filename:join(Path,File), Q) end,
+ lists:foldl(F, Queue, Files).
+
+
diff --git a/learn-you-some-erlang/release/erlcount-1.0/src/erlcount_sup.erl b/learn-you-some-erlang/release/erlcount-1.0/src/erlcount_sup.erl
new file mode 100644
index 0000000..b8633a3
--- /dev/null
+++ b/learn-you-some-erlang/release/erlcount-1.0/src/erlcount_sup.erl
@@ -0,0 +1,17 @@
+-module(erlcount_sup).
+-behaviour(supervisor).
+-export([start_link/0, init/1]).
+
+start_link() ->
+ supervisor:start_link(?MODULE, []).
+
+init([]) ->
+ MaxRestart = 5,
+ MaxTime = 100,
+ {ok, {{one_for_one, MaxRestart, MaxTime},
+ [{dispatch,
+ {erlcount_dispatch, start_link, []},
+ transient,
+ 60000,
+ worker,
+ [erlcount_dispatch]}]}}.