Erlang basics · Lesson 30 (open contents)
Package the edge and leave clues
Keep `main/1` thin, log safe context, and build an escript.
Run this first
Run it once, change one input, and compare the new result.
%% src/cleaner_cli.erl
-module(cleaner_cli).
-export([main/1]).
main(Args) ->
case run(Args) of
ok -> ok;
{error, _Reason} -> halt(1)
end.
run([Path]) ->
case file:read_file(Path) of
{ok, Text} ->
Lines = binary:split(Text, <<"\n">>, [global, trim]),
finish(Path, [cleaner:parse_line(Line) || Line <- Lines]);
{error, Reason} ->
fail(Reason)
end;
run(_Args) ->
fail(usage).
finish(Path, Results) ->
case [Reason || {error, Reason} <- Results] of
[] ->
Records = [Record || {ok, Record} <- Results],
{ok, Counts} = cleaner:run(Records),
logger:info("cleaned path=~ts count=~B", [Path, length(Records)]),
io:format("levels=~tp~n", [Counts]),
ok;
[Reason | _] ->
fail(Reason)
end.
fail(Reason) ->
logger:error("clean_failed reason=~tp", [Reason]),
{error, Reason}.- The escript reads one file, calls
cleaner, logs the accepted count, prints level totals, and exits with status 1 on failure.
Read from the first line down
- Read
logger:info/2: Write a formatted operational message. - Read
main([Path]): Accept exactly one file argument. - Read
rebar3 escriptize: Package the command after tests pass.
Symbols are not secret signs
logger:info/2Write a formatted operational message.
main([Path])Accept exactly one file argument.
rebar3 escriptizePackage the command after tests pass.
Match each name to its meaning
thin entry point
A CLI entry point parses arguments, calls the API, prints a result, and does little else.
structured context
Logger metadata keeps fields such as path and count separate from the message.
privacy boundary
Operational clues should not expose full user data or secrets.
Why these forms are useful
Run the example first. Predict one result, then change one input and run it again.
A CLI entry point parses arguments, calls the API, prints a result, and does little else.
Logger metadata keeps fields such as path and count separate from the message.
Close the answer and try
Return a usage error when no path is supplied.
main([]) -> ____.Target result: Return {error,usage}.
Stuck? Read one hint
Keep the error tagged.
After you run it, see one answer
main([]) -> {error, usage}.Think about it: Should logs copy every source line?
No. Record enough context to diagnose a run without exposing the full input.
Remember these three lines
- 1
Keep CLI code thin.
- 2
Log decisions and counts.
- 3
Run tests before packaging.