-
Notifications
You must be signed in to change notification settings - Fork 0
/
cli.ex
99 lines (87 loc) · 2.47 KB
/
cli.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
defmodule Desel.CLI do
@struct [
help: :boolean
]
@help """
Usage:
desel [file] [expression ...] List elements by expressions
desel - [expression ...] Read text from stdin
"""
def main(args \\ []) do
args
|> parse_args
|> response
end
defp parse_args(args) do
args
|> OptionParser.parse(struct: @struct)
end
defp response({_, _, [{illeagal, _}|_]}) do
panic """
illeagal option -- #{to_string(illeagal)}
"""
end
defp response({[help: true], _, _}) do
IO.write(@help)
end
defp response({opts, ["-" | expression], _}) do
lines = IO.stream(:stdio, :line) |> Enum.to_list()
desel = Enum.join(lines)
lines = Enum.map(lines, &String.trim_trailing(&1, "\n"))
process(opts, desel, lines, expression)
end
defp response({opts, [file | expression], _}) when length(expression) != 0 do
case File.read(file) do
{:ok, desel} ->
lines = String.split(desel, "\n")
process(opts, desel, lines, expression)
{:error, reason} ->
panic """
failed to read the file: #{to_string(reason)}
"""
end
end
defp response({_, _, _}) do
panic @help
end
defp process(_opts, desel, lines, expression) do
alias Desel.Parser
alias Desel.Data
ast = case Parser.parse(desel) do
{:ok, ast, _, _} -> ast
{:error, message, position} ->
line = Enum.at(lines, position.vertical)
line_num = "#{to_string(position.vertical)}: "
spaces = position.horizontal + String.length(line_num)
spaces = String.duplicate(" ", spaces)
panic """
failed to parse the input
#{line_num}#{line}
#{spaces}^ #{message}
"""
end
data = Data.from_ast(ast)
expression = Enum.join(expression, " ")
expression = case Parser.parse_expression("(#{expression})") do
{:ok, ast, _, _} -> ast
{:error, message, position} ->
spaces = String.duplicate(" ", position.horizontal - 1) # "("
panic """
failed to parse the given expression
#{expression}
#{spaces}^ #{message}
"""
end
{_data, elements} = case Data.elements_by(data, expression) do
{:ok, data, elements} -> {data, elements}
{:error, _data, message} ->
panic message
end
Enum.each(elements, &IO.puts(&1))
end
defp panic(message) do
message = String.trim_trailing(message, "\n")
IO.puts(:stderr, "desel: #{message}")
System.halt(1)
end
end