• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rzcastilho / do_it / d1b46013b803ab7f9a20d4704d6ac78055349496

22 Sep 2023 02:02AM UTC coverage: 77.397% (+3.8%) from 73.616%
d1b46013b803ab7f9a20d4704d6ac78055349496

push

github

web-flow
Explicitly declare commands (#12)

* feat: :sparkles: explicitly declare commands

* feat: :bookmark: update version abd fix example

7 of 7 new or added lines in 2 files covered. (100.0%)

226 of 292 relevant lines covered (77.4%)

22.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/lib/do_it/main_command.ex
1
defmodule DoIt.MainCommand do
2
  @moduledoc false
3

4
  defmacro __using__(opts) do
5
    quote do
6
      Module.register_attribute(__MODULE__, :commands, accumulate: true, persist: false)
7

8
      Module.register_attribute(__MODULE__, :command_descriptions,
9
        accumulate: true,
10
        persist: false
11
      )
12

13
      case List.keyfind(unquote(opts), :description, 0) do
14
        {:description, description} ->
15
          Module.put_attribute(__MODULE__, :description, description)
16

17
        _ ->
18
          raise(
19
            DoIt.MainCommandDefinitionError,
20
            "description is required for main command definition"
21
          )
22
      end
23

24
      case List.keyfind(unquote(opts), :version, 0) do
25
        nil -> Module.put_attribute(__MODULE__, :version, {:version, unquote(version_number())})
26
        version -> Module.put_attribute(__MODULE__, :version, version)
27
      end
28

29
      import unquote(__MODULE__), only: [command: 1]
30
      @before_compile unquote(__MODULE__)
31
    end
32
  end
33

34
  defmacro __before_compile__(env) do
35
    compile(Module.get_attribute(env.module, :commands))
×
36
  end
37

38
  def compile([]), do: raise(DoIt.CommandDefinitionError, message: "define at least a command")
×
39

40
  def compile(commands) do
41
    commands_ast =
×
42
      for module <- commands do
×
43
        with {command, _} <- module.command() do
×
44
          quote do
45
            def main([unquote(command) | args]) do
46
              apply(String.to_existing_atom("#{unquote(module)}"), :do_it, [
47
                args,
48
                %{
49
                  env: System.get_env(),
50
                  config: DoIt.Commfig.get_data()
51
                }
52
              ])
53
            end
54
          end
55
        end
56
      end
57

58
    quote do
59
      def help() do
60
        DoIt.Output.print_help(
61
          app: Application.get_application(__MODULE__),
62
          commands: @command_descriptions,
63
          main_description: @description
64
        )
65
      end
66

67
      gen_app_callback? =
68
        case Code.ensure_compiled(Burrito.Util.Args) do
69
          {:module, _module} -> true
70
          {:error, _reason} -> false
71
        end
72

73
      if gen_app_callback? do
74
        def start(_type, _args) do
75
          Burrito.Util.Args.get_arguments()
76
          |> main()
77

78
          System.halt(0)
79
        end
80
      end
81

82
      def main(["version" | _]), do: IO.puts("#{inspect(@version)}")
83
      def main(["help" | _]), do: help()
84

85
      unquote(commands_ast)
86

87
      def main([unknown | _]) do
88
        IO.puts("invalid command #{unknown}")
89
        help()
90
      end
91

92
      def main([]) do
93
        IO.puts("no command provided")
94
        help()
95
      end
96

97
      def main(_), do: help()
98
      def main(), do: help()
99
    end
100
  end
101

102
  defmacro command(module) do
103
    quote do
104
      @commands unquote(module)
105
      with {name, description} <- unquote(module).command() do
106
        @command_descriptions %{name: name, description: description}
107
      end
108
    end
109
  end
110

111
  def version_number() do
112
    case File.read!("mix.exs") |> Code.string_to_quoted!() do
×
113
      {:defmodule, _, [{:__aliases__, _, [_, :MixProject]}, [do: {:__block__, [], do_block}]]} ->
114
        case do_block
×
115
             |> find_project_func()
116
             |> List.keyfind(:version, 0, :undefined) do
117
          {:version, version} when is_binary(version) ->
118
            version
×
119

120
          {:version, {:@, _, _}} ->
121
            case do_block
×
122
                 |> find_module_attribute(:version)
123
                 |> List.first() do
124
              nil -> :undefined
125
              version -> version
×
126
            end
127

128
          _ ->
129
            :undefined
130
        end
131

132
      _ ->
133
        :undefined
134
    end
135
  end
136

137
  def find_project_func([{:def, _, [{:project, _, nil}, [do: body]]} | _]) do
138
    body
×
139
  end
140

141
  def find_project_func([_ | rest]) do
142
    find_project_func(rest)
×
143
  end
144

145
  def find_project_func([]), do: []
146

147
  def find_module_attribute([{:@, _, [{attribute, _, value}]} | _], attribute) do
148
    value
×
149
  end
150

151
  def find_module_attribute([_ | rest], attr) do
152
    find_module_attribute(rest, attr)
×
153
  end
154

155
  def find_module_attribute([], _), do: []
156
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc