gorails 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -1
  3. data/Gemfile.lock +1 -1
  4. data/bin/update-deps +95 -0
  5. data/exe/gorails +3 -2
  6. data/lib/gorails/commands/railsbytes.rb +10 -10
  7. data/lib/gorails/commands.rb +1 -4
  8. data/lib/gorails/version.rb +1 -1
  9. data/lib/gorails.rb +11 -20
  10. data/vendor/deps/cli-kit/REVISION +1 -0
  11. data/vendor/deps/cli-kit/lib/cli/kit/args/definition.rb +301 -0
  12. data/vendor/deps/cli-kit/lib/cli/kit/args/evaluation.rb +237 -0
  13. data/vendor/deps/cli-kit/lib/cli/kit/args/parser/node.rb +131 -0
  14. data/vendor/deps/cli-kit/lib/cli/kit/args/parser.rb +128 -0
  15. data/vendor/deps/cli-kit/lib/cli/kit/args/tokenizer.rb +132 -0
  16. data/vendor/deps/cli-kit/lib/cli/kit/args.rb +15 -0
  17. data/vendor/deps/cli-kit/lib/cli/kit/base_command.rb +29 -0
  18. data/vendor/deps/cli-kit/lib/cli/kit/command_help.rb +256 -0
  19. data/vendor/deps/cli-kit/lib/cli/kit/command_registry.rb +141 -0
  20. data/vendor/deps/cli-kit/lib/cli/kit/config.rb +137 -0
  21. data/vendor/deps/cli-kit/lib/cli/kit/core_ext.rb +30 -0
  22. data/vendor/deps/cli-kit/lib/cli/kit/error_handler.rb +165 -0
  23. data/vendor/deps/cli-kit/lib/cli/kit/executor.rb +99 -0
  24. data/vendor/deps/cli-kit/lib/cli/kit/ini.rb +94 -0
  25. data/vendor/deps/cli-kit/lib/cli/kit/levenshtein.rb +89 -0
  26. data/vendor/deps/cli-kit/lib/cli/kit/logger.rb +95 -0
  27. data/vendor/deps/cli-kit/lib/cli/kit/opts.rb +284 -0
  28. data/vendor/deps/cli-kit/lib/cli/kit/resolver.rb +67 -0
  29. data/vendor/deps/cli-kit/lib/cli/kit/sorbet_runtime_stub.rb +142 -0
  30. data/vendor/deps/cli-kit/lib/cli/kit/support/test_helper.rb +253 -0
  31. data/vendor/deps/cli-kit/lib/cli/kit/support.rb +10 -0
  32. data/vendor/deps/cli-kit/lib/cli/kit/system.rb +350 -0
  33. data/vendor/deps/cli-kit/lib/cli/kit/util.rb +133 -0
  34. data/vendor/deps/cli-kit/lib/cli/kit/version.rb +7 -0
  35. data/vendor/deps/cli-kit/lib/cli/kit.rb +151 -0
  36. data/vendor/deps/cli-ui/REVISION +1 -0
  37. data/vendor/deps/cli-ui/lib/cli/ui/ansi.rb +180 -0
  38. data/vendor/deps/cli-ui/lib/cli/ui/color.rb +98 -0
  39. data/vendor/deps/cli-ui/lib/cli/ui/formatter.rb +216 -0
  40. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_stack.rb +116 -0
  41. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style/box.rb +176 -0
  42. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style/bracket.rb +149 -0
  43. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style.rb +112 -0
  44. data/vendor/deps/cli-ui/lib/cli/ui/frame.rb +300 -0
  45. data/vendor/deps/cli-ui/lib/cli/ui/glyph.rb +92 -0
  46. data/vendor/deps/cli-ui/lib/cli/ui/os.rb +58 -0
  47. data/vendor/deps/cli-ui/lib/cli/ui/printer.rb +72 -0
  48. data/vendor/deps/cli-ui/lib/cli/ui/progress.rb +102 -0
  49. data/vendor/deps/cli-ui/lib/cli/ui/prompt/interactive_options.rb +534 -0
  50. data/vendor/deps/cli-ui/lib/cli/ui/prompt/options_handler.rb +36 -0
  51. data/vendor/deps/cli-ui/lib/cli/ui/prompt.rb +354 -0
  52. data/vendor/deps/cli-ui/lib/cli/ui/sorbet_runtime_stub.rb +143 -0
  53. data/vendor/deps/cli-ui/lib/cli/ui/spinner/async.rb +46 -0
  54. data/vendor/deps/cli-ui/lib/cli/ui/spinner/spin_group.rb +292 -0
  55. data/vendor/deps/cli-ui/lib/cli/ui/spinner.rb +82 -0
  56. data/vendor/deps/cli-ui/lib/cli/ui/stdout_router.rb +264 -0
  57. data/vendor/deps/cli-ui/lib/cli/ui/terminal.rb +53 -0
  58. data/vendor/deps/cli-ui/lib/cli/ui/truncater.rb +107 -0
  59. data/vendor/deps/cli-ui/lib/cli/ui/version.rb +6 -0
  60. data/vendor/deps/cli-ui/lib/cli/ui/widgets/base.rb +37 -0
  61. data/vendor/deps/cli-ui/lib/cli/ui/widgets/status.rb +75 -0
  62. data/vendor/deps/cli-ui/lib/cli/ui/widgets.rb +91 -0
  63. data/vendor/deps/cli-ui/lib/cli/ui/wrap.rb +63 -0
  64. data/vendor/deps/cli-ui/lib/cli/ui.rb +356 -0
  65. metadata +57 -1
@@ -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