gorails 0.1.1 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -1
  3. data/Gemfile.lock +1 -6
  4. data/README.md +41 -12
  5. data/bin/update-deps +95 -0
  6. data/exe/gorails +2 -1
  7. data/gorails.gemspec +0 -2
  8. data/lib/gorails/commands/railsbytes.rb +45 -4
  9. data/lib/gorails/commands/version.rb +15 -0
  10. data/lib/gorails/commands.rb +2 -5
  11. data/lib/gorails/version.rb +1 -1
  12. data/lib/gorails.rb +11 -20
  13. data/vendor/deps/cli-kit/REVISION +1 -0
  14. data/vendor/deps/cli-kit/lib/cli/kit/args/definition.rb +301 -0
  15. data/vendor/deps/cli-kit/lib/cli/kit/args/evaluation.rb +237 -0
  16. data/vendor/deps/cli-kit/lib/cli/kit/args/parser/node.rb +131 -0
  17. data/vendor/deps/cli-kit/lib/cli/kit/args/parser.rb +128 -0
  18. data/vendor/deps/cli-kit/lib/cli/kit/args/tokenizer.rb +132 -0
  19. data/vendor/deps/cli-kit/lib/cli/kit/args.rb +15 -0
  20. data/vendor/deps/cli-kit/lib/cli/kit/base_command.rb +29 -0
  21. data/vendor/deps/cli-kit/lib/cli/kit/command_help.rb +256 -0
  22. data/vendor/deps/cli-kit/lib/cli/kit/command_registry.rb +141 -0
  23. data/vendor/deps/cli-kit/lib/cli/kit/config.rb +137 -0
  24. data/vendor/deps/cli-kit/lib/cli/kit/core_ext.rb +30 -0
  25. data/vendor/deps/cli-kit/lib/cli/kit/error_handler.rb +165 -0
  26. data/vendor/deps/cli-kit/lib/cli/kit/executor.rb +99 -0
  27. data/vendor/deps/cli-kit/lib/cli/kit/ini.rb +94 -0
  28. data/vendor/deps/cli-kit/lib/cli/kit/levenshtein.rb +89 -0
  29. data/vendor/deps/cli-kit/lib/cli/kit/logger.rb +95 -0
  30. data/vendor/deps/cli-kit/lib/cli/kit/opts.rb +284 -0
  31. data/vendor/deps/cli-kit/lib/cli/kit/resolver.rb +67 -0
  32. data/vendor/deps/cli-kit/lib/cli/kit/sorbet_runtime_stub.rb +142 -0
  33. data/vendor/deps/cli-kit/lib/cli/kit/support/test_helper.rb +253 -0
  34. data/vendor/deps/cli-kit/lib/cli/kit/support.rb +10 -0
  35. data/vendor/deps/cli-kit/lib/cli/kit/system.rb +350 -0
  36. data/vendor/deps/cli-kit/lib/cli/kit/util.rb +133 -0
  37. data/vendor/deps/cli-kit/lib/cli/kit/version.rb +7 -0
  38. data/vendor/deps/cli-kit/lib/cli/kit.rb +151 -0
  39. data/vendor/deps/cli-ui/REVISION +1 -0
  40. data/vendor/deps/cli-ui/lib/cli/ui/ansi.rb +180 -0
  41. data/vendor/deps/cli-ui/lib/cli/ui/color.rb +98 -0
  42. data/vendor/deps/cli-ui/lib/cli/ui/formatter.rb +216 -0
  43. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_stack.rb +116 -0
  44. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style/box.rb +176 -0
  45. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style/bracket.rb +149 -0
  46. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style.rb +112 -0
  47. data/vendor/deps/cli-ui/lib/cli/ui/frame.rb +300 -0
  48. data/vendor/deps/cli-ui/lib/cli/ui/glyph.rb +92 -0
  49. data/vendor/deps/cli-ui/lib/cli/ui/os.rb +58 -0
  50. data/vendor/deps/cli-ui/lib/cli/ui/printer.rb +72 -0
  51. data/vendor/deps/cli-ui/lib/cli/ui/progress.rb +102 -0
  52. data/vendor/deps/cli-ui/lib/cli/ui/prompt/interactive_options.rb +534 -0
  53. data/vendor/deps/cli-ui/lib/cli/ui/prompt/options_handler.rb +36 -0
  54. data/vendor/deps/cli-ui/lib/cli/ui/prompt.rb +354 -0
  55. data/vendor/deps/cli-ui/lib/cli/ui/sorbet_runtime_stub.rb +143 -0
  56. data/vendor/deps/cli-ui/lib/cli/ui/spinner/async.rb +46 -0
  57. data/vendor/deps/cli-ui/lib/cli/ui/spinner/spin_group.rb +292 -0
  58. data/vendor/deps/cli-ui/lib/cli/ui/spinner.rb +82 -0
  59. data/vendor/deps/cli-ui/lib/cli/ui/stdout_router.rb +264 -0
  60. data/vendor/deps/cli-ui/lib/cli/ui/terminal.rb +53 -0
  61. data/vendor/deps/cli-ui/lib/cli/ui/truncater.rb +107 -0
  62. data/vendor/deps/cli-ui/lib/cli/ui/version.rb +6 -0
  63. data/vendor/deps/cli-ui/lib/cli/ui/widgets/base.rb +37 -0
  64. data/vendor/deps/cli-ui/lib/cli/ui/widgets/status.rb +75 -0
  65. data/vendor/deps/cli-ui/lib/cli/ui/widgets.rb +91 -0
  66. data/vendor/deps/cli-ui/lib/cli/ui/wrap.rb +63 -0
  67. data/vendor/deps/cli-ui/lib/cli/ui.rb +356 -0
  68. metadata +59 -30
@@ -0,0 +1,133 @@
1
+ # typed: true
2
+ require 'cli/kit'
3
+
4
+ module CLI
5
+ module Kit
6
+ module Util
7
+ class << self
8
+ extend T::Sig
9
+ #
10
+ # Converts an integer representing bytes into a human readable format
11
+ #
12
+ sig { params(bytes: Integer, precision: Integer, space: T::Boolean).returns(String) }
13
+ def to_filesize(bytes, precision: 2, space: false)
14
+ to_si_scale(bytes, 'B', precision: precision, space: space, factor: 1024)
15
+ end
16
+
17
+ # Converts a number to a human readable format on the SI scale
18
+ #
19
+ sig do
20
+ params(number: Numeric, unit: String, factor: Integer, precision: Integer,
21
+ space: T::Boolean).returns(String)
22
+ end
23
+ def to_si_scale(number, unit = '', factor: 1000, precision: 2, space: false)
24
+ raise ArgumentError, 'factor should only be 1000 or 1024' unless [1000, 1024].include?(factor)
25
+
26
+ small_scale = ['m', 'µ', 'n', 'p', 'f', 'a', 'z', 'y']
27
+ big_scale = ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
28
+ negative = number < 0
29
+ number = number.abs.to_f
30
+
31
+ if number == 0.0 || number.between?(1, factor)
32
+ prefix = ''
33
+ scale = 0
34
+ else
35
+ scale = Math.log(number, factor).floor
36
+ if number < 1
37
+ index = [-scale - 1, small_scale.length].min
38
+ scale = -(index + 1)
39
+ prefix = T.must(small_scale[index])
40
+ else
41
+ index = [scale - 1, big_scale.length].min
42
+ scale = index + 1
43
+ prefix = T.must(big_scale[index])
44
+ end
45
+ end
46
+
47
+ divider = (factor**scale)
48
+ fnum = (number / divider.to_f).round(precision)
49
+
50
+ # Trim useless decimal
51
+ fnum = fnum.to_i if (fnum.to_i.to_f * divider.to_f) == number
52
+
53
+ fnum = -fnum if negative
54
+ if space
55
+ prefix = ' ' + prefix
56
+ end
57
+
58
+ "#{fnum}#{prefix}#{unit}"
59
+ end
60
+
61
+ # Dir.chdir, when invoked in block form, complains when we call chdir
62
+ # again recursively. There's no apparent good reason for this, so we
63
+ # simply implement our own block form of Dir.chdir here.
64
+ sig do
65
+ type_parameters(:T).params(dir: String, block: T.proc.returns(T.type_parameter(:T)))
66
+ .returns(T.type_parameter(:T))
67
+ end
68
+ def with_dir(dir, &block)
69
+ prev = Dir.pwd
70
+ begin
71
+ Dir.chdir(dir)
72
+ yield
73
+ ensure
74
+ Dir.chdir(prev)
75
+ end
76
+ end
77
+
78
+ # Must call retry_after on the result in order to execute the block
79
+ #
80
+ # Example usage:
81
+ #
82
+ # CLI::Kit::Util.begin do
83
+ # might_raise_if_costly_prep_not_done()
84
+ # end.retry_after(ExpectedError) do
85
+ # costly_prep()
86
+ # end
87
+ sig do
88
+ type_parameters(:T).params(block_that_might_raise: T.proc.returns(T.type_parameter(:T)))
89
+ .returns(Retrier[T.type_parameter(:T)])
90
+ end
91
+ def begin(&block_that_might_raise)
92
+ Retrier.new(block_that_might_raise)
93
+ end
94
+ end
95
+
96
+ class Retrier
97
+ extend T::Sig
98
+ extend T::Generic
99
+
100
+ BlockReturnType = type_member
101
+
102
+ sig { params(block_that_might_raise: T.proc.returns(BlockReturnType)).void }
103
+ def initialize(block_that_might_raise)
104
+ @block_that_might_raise = block_that_might_raise
105
+ end
106
+
107
+ sig do
108
+ params(
109
+ exception: T.class_of(Exception),
110
+ retries: Integer,
111
+ before_retry: T.nilable(T.proc.params(e: Exception).void)
112
+ ).returns(BlockReturnType)
113
+ end
114
+ def retry_after(exception = StandardError, retries: 1, &before_retry)
115
+ @block_that_might_raise.call
116
+ rescue exception => e
117
+ raise if (retries -= 1) < 0
118
+
119
+ if before_retry
120
+ if before_retry.arity == 0
121
+ T.cast(before_retry, T.proc.void).call
122
+ else
123
+ T.cast(before_retry, T.proc.params(e: Exception).void).call(e)
124
+ end
125
+ end
126
+ retry
127
+ end
128
+ end
129
+
130
+ private_constant :Retrier
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,7 @@
1
+ # typed: true
2
+
3
+ module CLI
4
+ module Kit
5
+ VERSION = '4.0.0'
6
+ end
7
+ end
@@ -0,0 +1,151 @@
1
+ # typed: true
2
+ require 'cli/ui'
3
+
4
+ unless defined?(T)
5
+ require('cli/kit/sorbet_runtime_stub')
6
+ end
7
+
8
+ require 'cli/kit/core_ext'
9
+
10
+ module CLI
11
+ module Kit
12
+ extend T::Sig
13
+
14
+ autoload :Args, 'cli/kit/args'
15
+ autoload :BaseCommand, 'cli/kit/base_command'
16
+ autoload :CommandRegistry, 'cli/kit/command_registry'
17
+ autoload :CommandHelp, 'cli/kit/command_help'
18
+ autoload :Config, 'cli/kit/config'
19
+ autoload :ErrorHandler, 'cli/kit/error_handler'
20
+ autoload :Executor, 'cli/kit/executor'
21
+ autoload :Ini, 'cli/kit/ini'
22
+ autoload :Levenshtein, 'cli/kit/levenshtein'
23
+ autoload :Logger, 'cli/kit/logger'
24
+ autoload :Opts, 'cli/kit/opts'
25
+ autoload :Resolver, 'cli/kit/resolver'
26
+ autoload :Support, 'cli/kit/support'
27
+ autoload :System, 'cli/kit/system'
28
+ autoload :Util, 'cli/kit/util'
29
+
30
+ EXIT_FAILURE_BUT_NOT_BUG = 30
31
+ EXIT_BUG = 1
32
+ EXIT_SUCCESS = 0
33
+
34
+ # Abort, Bug, AbortSilent, and BugSilent are four ways of immediately bailing
35
+ # on command-line execution when an unrecoverable error occurs.
36
+ #
37
+ # Note that these don't inherit from StandardError, and so are not caught by
38
+ # a bare `rescue => e`.
39
+ #
40
+ # * Abort prints its message in red and exits 1;
41
+ # * Bug additionally submits the exception to the exception_reporter passed to
42
+ # `CLI::Kit::ErrorHandler.new`
43
+ # * AbortSilent and BugSilent do the same as above, but do not print
44
+ # messages before exiting.
45
+ #
46
+ # Treat these like panic() in Go:
47
+ # * Don't rescue them. Use a different Exception class if you plan to recover;
48
+ # * Provide a useful message, since it will be presented in brief to the
49
+ # user, and will be useful for debugging.
50
+ # * Avoid using it if it does actually make sense to recover from an error.
51
+ #
52
+ # Additionally:
53
+ # * Do not subclass these.
54
+ # * Only use AbortSilent or BugSilent if you prefer to print a more
55
+ # contextualized error than Abort or Bug would present to the user.
56
+ # * In general, don't attach a message to AbortSilent or BugSilent.
57
+ # * Never raise GenericAbort directly.
58
+ # * Think carefully about whether Abort or Bug is more appropriate. Is this
59
+ # a bug in the tool? Or is it just user error, transient network
60
+ # failure, etc.?
61
+ # * One case where it's ok to rescue (cli-kit internals or tests aside):
62
+ # 1. rescue Abort or Bug
63
+ # 2. Print a contextualized error message
64
+ # 3. Re-raise AbortSilent or BugSilent respectively.
65
+ #
66
+ # These aren't the only exceptions that can carry this 'bug' and 'silent'
67
+ # metadata, however:
68
+ #
69
+ # If you raise an exception with `CLI::Kit.raise(..., bug: x, silent: y)`,
70
+ # those last two (optional) keyword arguments will attach the metadata to
71
+ # whatever exception you raise. This is interpreted later in the
72
+ # ErrorHandler to decide how to print output and whether to submit the
73
+ # exception to bugsnag.
74
+ GenericAbort = Class.new(Exception) # rubocop:disable Lint/InheritException
75
+
76
+ class Abort < GenericAbort # bug:false; silent: false
77
+ extend(T::Sig)
78
+
79
+ sig { returns(T::Boolean) }
80
+ def bug?
81
+ false
82
+ end
83
+ end
84
+
85
+ class Bug < GenericAbort # bug:true; silent:false
86
+ end
87
+
88
+ class BugSilent < GenericAbort # bug:true; silent:true
89
+ extend(T::Sig)
90
+
91
+ sig { returns(T::Boolean) }
92
+ def silent?
93
+ true
94
+ end
95
+ end
96
+
97
+ class AbortSilent < GenericAbort # bug:false; silent:true
98
+ extend(T::Sig)
99
+
100
+ sig { returns(T::Boolean) }
101
+ def bug?
102
+ false
103
+ end
104
+
105
+ sig { returns(T::Boolean) }
106
+ def silent?
107
+ true
108
+ end
109
+ end
110
+
111
+ # Mirrors the API of Kernel#raise, but with the addition of a few new
112
+ # optional keyword arguments. `bug` and `silent` attach metadata to the
113
+ # exception being raised, which is interpreted later in the ErrorHandler to
114
+ # decide what to print and whether to submit to bugsnag.
115
+ #
116
+ # `depth` is used to trim leading elements of the backtrace. If you wrap
117
+ # this method in your own wrapper, you'll want to pass `depth: 2`, for
118
+ # example.
119
+ sig do
120
+ params(
121
+ exception: T.any(Class, String, Exception),
122
+ string: T.untyped,
123
+ array: T.nilable(T::Array[String]),
124
+ cause: T.nilable(Exception),
125
+ bug: T.nilable(T::Boolean),
126
+ silent: T.nilable(T::Boolean),
127
+ depth: Integer,
128
+ ).returns(T.noreturn)
129
+ end
130
+ def self.raise(
131
+ # default arguments
132
+ exception = T.unsafe(nil), string = T.unsafe(nil), array = T.unsafe(nil), cause: $ERROR_INFO,
133
+ # new arguments
134
+ bug: nil, silent: nil, depth: 1
135
+ )
136
+ if array
137
+ T.unsafe(Kernel).raise(exception, string, array, cause: cause)
138
+ elsif string
139
+ T.unsafe(Kernel).raise(exception, string, Kernel.caller(depth), cause: cause)
140
+ elsif exception.is_a?(String)
141
+ T.unsafe(Kernel).raise(RuntimeError, exception, Kernel.caller(depth), cause: cause)
142
+ else
143
+ T.unsafe(Kernel).raise(exception, exception.message, Kernel.caller(depth), cause: cause)
144
+ end
145
+ rescue Exception => e # rubocop:disable Lint/RescueException
146
+ e.bug!(bug) unless bug.nil?
147
+ e.silent!(silent) unless silent.nil?
148
+ Kernel.raise(e, cause: cause)
149
+ end
150
+ end
151
+ end
@@ -0,0 +1 @@
1
+ c9b77b8073f9242ba8d0c8c5651264810b23cb0d
@@ -0,0 +1,180 @@
1
+ # typed: true
2
+ require 'cli/ui'
3
+
4
+ module CLI
5
+ module UI
6
+ module ANSI
7
+ extend T::Sig
8
+
9
+ ESC = "\x1b"
10
+
11
+ # ANSI escape sequences (like \x1b[31m) have zero width.
12
+ # when calculating the padding width, we must exclude them.
13
+ # This also implements a basic version of utf8 character width calculation like
14
+ # we could get for real from something like utf8proc.
15
+ #
16
+ sig { params(str: String).returns(Integer) }
17
+ def self.printing_width(str)
18
+ zwj = T.let(false, T::Boolean)
19
+ strip_codes(str).codepoints.reduce(0) do |acc, cp|
20
+ if zwj
21
+ zwj = false
22
+ next acc
23
+ end
24
+ case cp
25
+ when 0x200d # zero-width joiner
26
+ zwj = true
27
+ acc
28
+ when "\n"
29
+ acc
30
+ else
31
+ acc + 1
32
+ end
33
+ end
34
+ end
35
+
36
+ # Strips ANSI codes from a str
37
+ #
38
+ # ==== Attributes
39
+ #
40
+ # - +str+ - The string from which to strip codes
41
+ #
42
+ sig { params(str: String).returns(String) }
43
+ def self.strip_codes(str)
44
+ str.gsub(/\x1b\[[\d;]+[A-z]|\r/, '')
45
+ end
46
+
47
+ # Returns an ANSI control sequence
48
+ #
49
+ # ==== Attributes
50
+ #
51
+ # - +args+ - Argument to pass to the ANSI control sequence
52
+ # - +cmd+ - ANSI control sequence Command
53
+ #
54
+ sig { params(args: String, cmd: String).returns(String) }
55
+ def self.control(args, cmd)
56
+ ESC + '[' + args + cmd
57
+ end
58
+
59
+ # https://en.wikipedia.org/wiki/ANSI_escape_code#graphics
60
+ sig { params(params: String).returns(String) }
61
+ def self.sgr(params)
62
+ control(params, 'm')
63
+ end
64
+
65
+ # Cursor Movement
66
+
67
+ # Move the cursor up n lines
68
+ #
69
+ # ==== Attributes
70
+ #
71
+ # * +n+ - number of lines by which to move the cursor up
72
+ #
73
+ sig { params(n: Integer).returns(String) }
74
+ def self.cursor_up(n = 1)
75
+ return '' if n.zero?
76
+
77
+ control(n.to_s, 'A')
78
+ end
79
+
80
+ # Move the cursor down n lines
81
+ #
82
+ # ==== Attributes
83
+ #
84
+ # * +n+ - number of lines by which to move the cursor down
85
+ #
86
+ sig { params(n: Integer).returns(String) }
87
+ def self.cursor_down(n = 1)
88
+ return '' if n.zero?
89
+
90
+ control(n.to_s, 'B')
91
+ end
92
+
93
+ # Move the cursor forward n columns
94
+ #
95
+ # ==== Attributes
96
+ #
97
+ # * +n+ - number of columns by which to move the cursor forward
98
+ #
99
+ sig { params(n: Integer).returns(String) }
100
+ def self.cursor_forward(n = 1)
101
+ return '' if n.zero?
102
+
103
+ control(n.to_s, 'C')
104
+ end
105
+
106
+ # Move the cursor back n columns
107
+ #
108
+ # ==== Attributes
109
+ #
110
+ # * +n+ - number of columns by which to move the cursor back
111
+ #
112
+ sig { params(n: Integer).returns(String) }
113
+ def self.cursor_back(n = 1)
114
+ return '' if n.zero?
115
+
116
+ control(n.to_s, 'D')
117
+ end
118
+
119
+ # Move the cursor to a specific column
120
+ #
121
+ # ==== Attributes
122
+ #
123
+ # * +n+ - The column to move to
124
+ #
125
+ sig { params(n: Integer).returns(String) }
126
+ def self.cursor_horizontal_absolute(n = 1)
127
+ cmd = control(n.to_s, 'G')
128
+ cmd += cursor_back if CLI::UI::OS.current.shift_cursor_back_on_horizontal_absolute?
129
+ cmd
130
+ end
131
+
132
+ # Show the cursor
133
+ #
134
+ sig { returns(String) }
135
+ def self.show_cursor
136
+ control('', '?25h')
137
+ end
138
+
139
+ # Hide the cursor
140
+ #
141
+ sig { returns(String) }
142
+ def self.hide_cursor
143
+ control('', '?25l')
144
+ end
145
+
146
+ # Save the cursor position
147
+ #
148
+ sig { returns(String) }
149
+ def self.cursor_save
150
+ control('', 's')
151
+ end
152
+
153
+ # Restore the saved cursor position
154
+ #
155
+ sig { returns(String) }
156
+ def self.cursor_restore
157
+ control('', 'u')
158
+ end
159
+
160
+ # Move to the next line
161
+ #
162
+ sig { returns(String) }
163
+ def self.next_line
164
+ cursor_down + cursor_horizontal_absolute
165
+ end
166
+
167
+ # Move to the previous line
168
+ #
169
+ sig { returns(String) }
170
+ def self.previous_line
171
+ cursor_up + cursor_horizontal_absolute
172
+ end
173
+
174
+ sig { returns(String) }
175
+ def self.clear_to_end_of_line
176
+ control('', 'K')
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,98 @@
1
+ # typed: true
2
+ require 'cli/ui'
3
+
4
+ module CLI
5
+ module UI
6
+ class Color
7
+ extend T::Sig
8
+
9
+ sig { returns(String) }
10
+ attr_reader :sgr, :code
11
+
12
+ sig { returns(Symbol) }
13
+ attr_reader :name
14
+
15
+ # Creates a new color mapping
16
+ # Signatures can be found here:
17
+ # https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
18
+ #
19
+ # ==== Attributes
20
+ #
21
+ # * +sgr+ - The color signature
22
+ # * +name+ - The name of the color
23
+ #
24
+ sig { params(sgr: String, name: Symbol).void }
25
+ def initialize(sgr, name)
26
+ @sgr = sgr
27
+ @code = CLI::UI::ANSI.sgr(sgr)
28
+ @name = name
29
+ end
30
+
31
+ RED = new('31', :red)
32
+ GREEN = new('32', :green)
33
+ YELLOW = new('33', :yellow)
34
+ # default blue is low-contrast against black in some default terminal color scheme
35
+ BLUE = new('94', :blue) # 9x = high-intensity fg color x
36
+ MAGENTA = new('35', :magenta)
37
+ CYAN = new('36', :cyan)
38
+ RESET = new('0', :reset)
39
+ BOLD = new('1', :bold)
40
+ WHITE = new('97', :white)
41
+
42
+ # 240 is very dark gray; 255 is very light gray. 244 is somewhat dark.
43
+ GRAY = new('38;5;244', :grey)
44
+
45
+ MAP = {
46
+ red: RED,
47
+ green: GREEN,
48
+ yellow: YELLOW,
49
+ blue: BLUE,
50
+ magenta: MAGENTA,
51
+ cyan: CYAN,
52
+ reset: RESET,
53
+ bold: BOLD,
54
+ gray: GRAY,
55
+ }.freeze
56
+
57
+ class InvalidColorName < ArgumentError
58
+ extend T::Sig
59
+
60
+ sig { params(name: Symbol).void }
61
+ def initialize(name)
62
+ super
63
+ @name = name
64
+ end
65
+
66
+ sig { returns(String) }
67
+ def message
68
+ keys = Color.available.map(&:inspect).join(',')
69
+ "invalid color: #{@name.inspect} " \
70
+ "-- must be one of CLI::UI::Color.available (#{keys})"
71
+ end
72
+ end
73
+
74
+ # Looks up a color code by name
75
+ #
76
+ # ==== Raises
77
+ # Raises a InvalidColorName if the color is not available
78
+ # You likely need to add it to the +MAP+ or you made a typo
79
+ #
80
+ # ==== Returns
81
+ # Returns a color code
82
+ #
83
+ sig { params(name: T.any(Symbol, String)).returns(Color) }
84
+ def self.lookup(name)
85
+ MAP.fetch(name.to_sym)
86
+ rescue KeyError
87
+ raise InvalidColorName, name
88
+ end
89
+
90
+ # All available colors by name
91
+ #
92
+ sig { returns(T::Array[Symbol]) }
93
+ def self.available
94
+ MAP.keys
95
+ end
96
+ end
97
+ end
98
+ end