From c79d13f5e854ff84b4fe61a45ce666ee1c8479da Mon Sep 17 00:00:00 2001
From: alisdair sullivan <alisdairsullivan@yahoo.ca>
Date: Tue, 27 Jan 2015 20:52:35 -0800
Subject: factor out task/args parsing from `do` for use in other meta commands

---
 src/rebar_prv_do.erl |  8 ++------
 src/rebar_utils.erl  | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 56 insertions(+), 7 deletions(-)

(limited to 'src')

diff --git a/src/rebar_prv_do.erl b/src/rebar_prv_do.erl
index 5dec7d8..83e57a1 100644
--- a/src/rebar_prv_do.erl
+++ b/src/rebar_prv_do.erl
@@ -32,13 +32,12 @@ init(State) ->
 
 -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
 do(State) ->
-    Tasks = args_to_tasks(rebar_state:command_args(State)),
+    Tasks = rebar_utils:args_to_tasks(rebar_state:command_args(State)),
     do_tasks(Tasks, State).
 
 do_tasks([], State) ->
     {ok, State};
-do_tasks([TaskArgs | Tail], State) ->
-    [TaskStr | Args] = string:tokens(TaskArgs, " "),
+do_tasks([{TaskStr, Args}|Tail], State) ->
     Task = list_to_atom(TaskStr),
     State1 = rebar_state:set(State, task, Task),
     State2 = rebar_state:command_args(State1, Args),
@@ -52,6 +51,3 @@ do_tasks([TaskArgs | Tail], State) ->
 -spec format_error(any()) -> iolist().
 format_error(Reason) ->
     io_lib:format("~p", [Reason]).
-
-args_to_tasks(Args) ->
-    [string:strip(T) || T <- string:tokens(string:join(Args, " "), ",")].
diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl
index d899dc5..85f59d6 100644
--- a/src/rebar_utils.erl
+++ b/src/rebar_utils.erl
@@ -47,7 +47,8 @@
          deprecated/4,
          erl_opts/1,
          indent/1,
-         cleanup_code_path/1]).
+         cleanup_code_path/1,
+         args_to_tasks/1]).
 
 %% for internal use only
 -export([otp_release/0]).
@@ -218,6 +219,12 @@ erl_opts(Config) ->
             [debug_info|Opts]
     end.
 
+%% for use by `do` task
+
+%% note: this does not handle the case where you have an argument that
+%%  was enclosed in quotes and might have commas but should not be split.
+args_to_tasks(Args) -> new_task(Args, []).
+
 %% ====================================================================
 %% Internal functions
 %% ====================================================================
@@ -467,3 +474,49 @@ cleanup_code_path(OrigPath) ->
         _ ->
             code:set_path(OrigPath)
     end.
+
+new_task([], Acc) -> lists:reverse(Acc);
+new_task([TaskList|Rest], Acc) ->
+    case re:split(TaskList, ",", [{return, list}, {parts, 2}]) of
+        %% `do` consumes all remaining args
+        ["do" = Task|RestArgs] ->
+            lists:reverse([{Task, RestArgs ++ Rest}|Acc]);
+        %% single task terminated by a comma
+        [Task, ""]    -> new_task(Rest, [{Task, []}|Acc]);
+        %% sequence of two or more tasks
+        [Task, More]  -> new_task([More|Rest], [{Task, []}|Acc]);
+        %% single task not terminated by a comma
+        [Task]        -> arg_or_flag(Rest, [{Task, []}|Acc])
+    end.
+
+arg_or_flag([], [{Task, Args}|Acc]) ->
+    lists:reverse([{Task, lists:reverse(Args)}|Acc]);
+%% case where you have `foo , bar`
+arg_or_flag([","|Rest], Acc) -> new_task(Rest, Acc);
+%% case where you have `foo ,bar`
+arg_or_flag(["," ++ Task|Rest], Acc) -> new_task([Task|Rest], Acc);
+%% a flag
+arg_or_flag(["-" ++ _ = Flag|Rest], [{Task, Args}|Acc]) ->
+    case maybe_ends_in_comma(Flag) of
+        false   -> arg_or_flag(Rest, [{Task, [Flag|Args]}|Acc]);
+        NewFlag -> new_task(Rest, [{Task,
+                                    lists:reverse([NewFlag|Args])}|Acc])
+    end;
+%% an argument or a sequence of arguments
+arg_or_flag([ArgList|Rest], [{Task, Args}|Acc]) ->
+    case re:split(ArgList, ",", [{return, list}, {parts, 2}]) of
+        %% single arg terminated by a comma
+        [Arg, ""]   -> new_task(Rest, [{Task,
+                                        lists:reverse([Arg|Args])}|Acc]);
+        %% sequence of two or more args/tasks
+        [Arg, More] -> new_task([More|Rest], [{Task,
+                                              lists:reverse([Arg|Args])}|Acc]);
+        %% single arg not terminated by a comma
+        [Arg] -> arg_or_flag(Rest, [{Task, [Arg|Args]}|Acc])
+    end.
+
+maybe_ends_in_comma(H) ->
+    case lists:reverse(H) of
+        "," ++ Flag -> lists:reverse(Flag);
+        _           -> false
+    end.
-- 
cgit v1.1