Packages
github_issues
0.2.5
0.4.71
0.4.70
0.4.69
0.4.68
0.4.66
0.4.65
0.4.64
0.4.63
0.4.62
0.4.61
0.4.60
0.4.59
0.4.58
0.4.56
0.4.55
0.4.54
0.4.53
0.4.52
0.4.51
0.4.50
0.4.48
0.4.47
0.4.46
0.4.45
0.4.44
0.4.43
0.4.42
0.4.41
0.4.40
0.4.39
0.4.38
0.4.37
0.4.36
0.4.35
0.4.34
0.4.33
0.4.32
0.4.31
0.4.30
0.4.29
0.4.28
0.4.27
0.4.26
0.4.25
0.4.24
0.4.23
0.4.22
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.24
0.3.23
0.3.22
0.3.21
0.3.20
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.0
0.2.5
0.2.3
0.2.2
0.2.0
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.4
0.1.2
0.1.1
0.1.0
Writes GitHub Issues to "stdio" in a table with borders and colors.
Current section
Files
Jump to
Current section
Files
lib/github/issues/cli.ex
# Based on the book "Programming Elixir" by Dave Thomas.
defmodule GitHub.Issues.CLI do
@moduledoc """
Handles the command line parsing and the dispatch to
the various functions that end up generating a table
of the first or last _n_ issues of a GitHub project.
"""
import Logger, only: [error: 1]
alias GitHub.Issues
alias IO.ANSI.Table.{Formatter, Style}
@type bell :: boolean
@type count :: integer
@type parsed ::
{Issues.user, Issues.project, count, bell, Style.t, Formatter.column_width}
| :help
@app Mix.Project.config[:app]
@aliases Application.get_env @app, :aliases
@count Application.get_env @app, :default_count
@escript Mix.Local.name_for :escript, Mix.Project.config
@help_attrs Application.get_env @app, :help_attrs
@strict Application.get_env @app, :strict
@switches Application.get_env @app, :default_switches
@doc """
Parses and processes the `command line arguments`.
## Parameters
- `argv` - command line arguments (list)
"""
@spec main([String.t]) :: :ok | no_return
def main(argv) do
with {user, project, count, bell, style, max_width} <- parse(argv),
{:ok, issues} <- Issues.fetch(user, project)
do
Formatter.print_table(issues, count, bell, style, max_width: max_width)
else
:help -> help()
{:error, text} -> log_error(text)
end
end
@spec log_error(String.t) :: no_return
defp log_error(text) do
error "Error fetching from GitHub - #{text}"
Process.sleep(1_000) # ensure message logged before exiting
System.halt(2)
end
@spec help :: no_return
defp help do
# Examples of usage on Windows:
# escript gi --help
# escript gi elixir-lang elixir 7 --last
# escript gi elixir-lang elixir 7 --last --max-width=50
# escript gi myfreeweb httpotion --bell
# escript gi dynamo dynamo -lb 8 -t GREEN
# escript gi dynamo dynamo -bl 9 --table-style=dark
# escript gi dynamo dynamo 11 -blt light
# Examples of usage on macOS:
# ./gi laravel elixir
{types, texts} = case :os.type do
{:win32, _} ->
{ [:section, :normal, :command, :normal],
["usage:", " ", "escript", " #{@escript}"]
}
_ -> # e.g. {:unix, _}
{ [:section, :normal],
["usage:", " ./#{@escript}"]
}
end
filler = String.duplicate " ", String.length Enum.join(texts)
prefix = help_format(types, texts)
line_user_project = help_format(
[:switch, :arg, :normal, :arg],
["[(-h | --help)] ", "<github-user>", " ", "<github-project>"]
)
line_count = help_format(
[:switch, :normal, :arg, :normal, :switch],
["[(-l | --last)]", " ", "<count>", " ", "[(-b | --bell)]"]
)
line_table_style = help_format(
[:switch, :arg, :switch],
["[(-t | --table-style)=", "<table-style>", "]"]
)
line_max_width = help_format(
[:switch, :arg, :switch],
["[(-m | --max-width)=", "<max-width>", "]"]
)
line_where = help_format(
[:section],
["where:"]
)
line_default_count = help_format(
[:normal, :arg, :normal, :value],
[" - default ", "<count>", " is ", "#{@count}"]
)
line_default_table_style = help_format(
[:normal, :arg, :normal, :value],
[" - default ", "<table-style>", " is ", "#{@switches[:table_style]}"]
)
line_default_max_width = help_format(
[:normal, :arg, :normal, :value],
[" - default ", "<max-width>", " is ", "#{@switches[:max_width]}"]
)
line_table_style_one_of = help_format(
[:normal, :arg, :normal],
[" - ", "<table-style>", " is one of:"]
)
IO.write """
#{prefix} #{line_user_project}
#{filler} #{line_count}
#{filler} #{line_table_style}
#{filler} #{line_max_width}
#{line_where}
#{line_default_count}
#{line_default_table_style}
#{line_default_max_width}
#{line_table_style_one_of}
"""
template = help_format(
[:normal, :value, :normal],
["\s\s\s\s• ", "&tag", "&filler - ¬e"]
)
Style.texts "#{template}", &IO.puts/1
System.halt(0)
end
@spec help_format([atom], [String.t]) :: maybe_improper_list
defp help_format(types, texts) do
types
|> Enum.map(&@help_attrs[&1])
|> Enum.zip(texts)
|> Enum.map(&Tuple.to_list/1)
|> IO.ANSI.format
end
@doc """
Parses the `command line arguments`.
`argv` can be `-h` or `--help`, which returns `:help`. Otherwise
it is a GitHub user name, project name, and optionally the number
of issues to format (the first _n_ ones). To format the last _n_
issues, specify switch `--last` which will return a negative count.
Returns either a tuple of
`{user, project, count, bell, table_style, max_width}`
or `:help` if `--help` was given.
## Parameters
- `argv` - command line arguments (list)
## Switches
- `-h` or `--help` - for help
- `-l` or `--last` - to format the last _n_ issues
- `-b` or `--bell` - to ring the bell
- `-t` or `--table-style` - to apply a specific table style
- `-m` or `--max-width` - to cap column widths
## Table styles
#{Style.texts "\s\s- `&tag`&filler - ¬e\n"}
## Examples
iex> alias GitHub.Issues.CLI
iex> CLI.parse ["-h"]
:help
iex> alias GitHub.Issues.CLI
iex> CLI.parse ["user", "project", "99"]
{"user", "project", 99, false, :medium, 88}
iex> alias GitHub.Issues.CLI
iex> CLI.parse ["user", "project", "88", "--last", "--bell"]
{"user", "project", -88, true, :medium, 88}
iex> alias GitHub.Issues.CLI
iex> CLI.parse ["user", "project", "6", "--table-style", "dark"]
{"user", "project", 6, false, :dark, 88}
"""
@spec parse([String.t]) :: parsed
def parse(argv) do
argv
|> OptionParser.parse(strict: @strict, aliases: @aliases)
|> reformat
end
@spec reformat({Keyword.t, [String.t], [tuple]}) :: parsed
defp reformat {switches, args, []} do
with {user, project, count} <- normalize(args),
%{help: false, last: last, bell: bell, table_style: table_style,
max_width: max_width
} <- Map.merge(Map.new(@switches), Map.new(switches)),
{:ok, style} <- Style.style_for(table_style)
do
{user, project, last && -count || count, bell, style, max_width}
else
_ -> :help
end
end
defp reformat(_), do: :help
@spec normalize([String.t]) ::
{Issues.user, Issues.project, non_neg_integer} | :error
defp normalize [user, project, count] do
with {int, ""} when int >= 0 <- Integer.parse(count) do
{user, project, int}
else
_ -> :error
end
end
defp normalize [user, project] do
{user, project, @count}
end
defp normalize(_) do
:error
end
end