paperback 0.0.3 → 0.0.5

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 (56) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/tests.yml +46 -0
  3. data/.rubocop-disables.yml +26 -12
  4. data/CHANGELOG.md +21 -0
  5. data/README.md +27 -6
  6. data/bin/paperback +4 -0
  7. data/lib/paperback/cli.rb +38 -2
  8. data/lib/paperback/document.rb +134 -17
  9. data/lib/paperback/preparer.rb +107 -29
  10. data/lib/paperback/version.rb +2 -1
  11. data/lib/paperback.rb +12 -0
  12. data/paperback.gemspec +10 -7
  13. data/sorbet/config +3 -0
  14. data/sorbet/rbi/annotations/rainbow.rbi +269 -0
  15. data/sorbet/rbi/gems/ast@2.4.2.rbi +584 -0
  16. data/sorbet/rbi/gems/chunky_png@1.4.0.rbi +4498 -0
  17. data/sorbet/rbi/gems/coderay@1.1.3.rbi +3426 -0
  18. data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +1083 -0
  19. data/sorbet/rbi/gems/method_source@1.0.0.rbi +272 -0
  20. data/sorbet/rbi/gems/netrc@0.11.0.rbi +158 -0
  21. data/sorbet/rbi/gems/parallel@1.22.1.rbi +277 -0
  22. data/sorbet/rbi/gems/parser@3.2.0.0.rbi +6963 -0
  23. data/sorbet/rbi/gems/pdf-core@0.4.0.rbi +1682 -0
  24. data/sorbet/rbi/gems/prawn@1.3.0.rbi +5567 -0
  25. data/sorbet/rbi/gems/pry@0.14.1.rbi +9990 -0
  26. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +408 -0
  27. data/sorbet/rbi/gems/rake@13.0.6.rbi +3023 -0
  28. data/sorbet/rbi/gems/rbi@0.0.16.rbi +3008 -0
  29. data/sorbet/rbi/gems/regexp_parser@2.6.1.rbi +3481 -0
  30. data/sorbet/rbi/gems/rexml@3.2.5.rbi +4717 -0
  31. data/sorbet/rbi/gems/rqrcode@0.10.1.rbi +617 -0
  32. data/sorbet/rbi/gems/rspec-core@3.12.0.rbi +10791 -0
  33. data/sorbet/rbi/gems/rspec-expectations@3.12.1.rbi +8106 -0
  34. data/sorbet/rbi/gems/rspec-mocks@3.12.1.rbi +5305 -0
  35. data/sorbet/rbi/gems/rspec-support@3.12.0.rbi +1617 -0
  36. data/sorbet/rbi/gems/rspec@3.12.0.rbi +88 -0
  37. data/sorbet/rbi/gems/rubocop-ast@1.24.1.rbi +6617 -0
  38. data/sorbet/rbi/gems/rubocop@0.93.1.rbi +40848 -0
  39. data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +1234 -0
  40. data/sorbet/rbi/gems/sixword@0.4.0.rbi +536 -0
  41. data/sorbet/rbi/gems/spoom@1.1.15.rbi +2383 -0
  42. data/sorbet/rbi/gems/subprocess@1.5.6.rbi +391 -0
  43. data/sorbet/rbi/gems/tapioca@0.10.5.rbi +3207 -0
  44. data/sorbet/rbi/gems/thor@1.2.1.rbi +3956 -0
  45. data/sorbet/rbi/gems/ttfunk@1.4.0.rbi +1951 -0
  46. data/sorbet/rbi/gems/unicode-display_width@1.8.0.rbi +40 -0
  47. data/sorbet/rbi/gems/unparser@0.6.7.rbi +4524 -0
  48. data/sorbet/rbi/gems/webrick@1.7.0.rbi +2555 -0
  49. data/sorbet/rbi/gems/yard-sorbet@0.8.0.rbi +441 -0
  50. data/sorbet/rbi/gems/yard@0.9.28.rbi +17816 -0
  51. data/sorbet/tapioca/config.yml +13 -0
  52. data/sorbet/tapioca/require.rb +4 -0
  53. data/spec/functional/paperback/cli_spec.rb +195 -0
  54. data/spec/spec_helper.rb +1 -0
  55. data/spec/unit/paperback_spec.rb +1 -0
  56. metadata +91 -7
@@ -1,3 +1,4 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'base64'
@@ -12,37 +13,67 @@ module Paperback
12
13
  # Class wrapping functions to prepare data for paperback storage, including
13
14
  # QR code and sixword encoding.
14
15
  class Preparer
16
+ extend T::Sig
17
+
18
+ sig {returns(String)}
15
19
  attr_reader :data
20
+
21
+ sig {returns(T::Hash[String, T.untyped])}
16
22
  attr_reader :labels
23
+
24
+ sig {returns(T::Boolean)}
17
25
  attr_reader :qr_base64
26
+
27
+ sig {returns(T::Boolean)}
18
28
  attr_reader :encrypt
29
+
30
+ sig {returns(T.nilable(String))}
19
31
  attr_reader :passphrase_file
20
32
 
33
+ sig do
34
+ params(
35
+ filename: String,
36
+ encrypt: T::Boolean,
37
+ qr_base64: T::Boolean,
38
+ qr_level: T.nilable(Symbol),
39
+ comment: T.nilable(String),
40
+ passphrase_file: T.nilable(String),
41
+ include_base64: T::Boolean,
42
+ ).void
43
+ end
21
44
  def initialize(filename:, encrypt: true, qr_base64: false, qr_level: nil,
22
- comment: nil, passphrase_file: nil)
45
+ comment: nil, passphrase_file: nil, include_base64: true)
23
46
 
24
47
  log.debug('Preparer#initialize')
25
48
 
49
+ # lazy initializers, all explicitly set to nil
50
+ @log = T.let(nil, T.nilable(Logger))
51
+ @qr_code = T.let(nil, T.nilable(RQRCode::QRCode))
52
+ @sixword_lines = T.let(nil, T.nilable(T::Array[String]))
53
+ @passphrase = T.let(nil, T.nilable(String))
54
+
26
55
  log.info("Reading #{filename.inspect}")
27
56
  plain_data = File.read(filename)
28
57
 
29
58
  log.debug("Read #{plain_data.bytesize} bytes")
30
59
 
31
- @encrypt = encrypt
60
+ @encrypt = T.let(encrypt, T::Boolean)
32
61
 
33
62
  if encrypt
34
63
  @data = self.class.gpg_encrypt(filename: filename, password: passphrase)
35
64
  else
36
- @data = plain_data
65
+ @data = T.let(plain_data, String)
37
66
  end
38
- @sha256 = Digest::SHA256.hexdigest(plain_data)
67
+ @sha256 = T.let(Digest::SHA256.hexdigest(plain_data), String)
39
68
 
40
- @qr_base64 = qr_base64
41
- @qr_level = qr_level
69
+ @qr_base64 = T.let(qr_base64, T::Boolean)
70
+ @qr_level = T.let(qr_level, T.nilable(Symbol))
42
71
 
43
- @passphrase_file = passphrase_file
72
+ @passphrase_file = T.let(passphrase_file, T.nilable(String))
44
73
 
45
- @labels = {}
74
+ @include_base64 = T.let(!!include_base64, T::Boolean)
75
+
76
+ @labels = T.let({}, T::Hash[String, T.untyped])
46
77
  @labels['Filename'] = filename
47
78
  @labels['Backed up'] = Time.now.to_s
48
79
 
@@ -53,16 +84,21 @@ module Paperback
53
84
 
54
85
  @labels['SHA256'] = Digest::SHA256.hexdigest(plain_data)
55
86
 
56
- @document = Paperback::Document.new
87
+ @document = T.let(Paperback::Document.new, Paperback::Document)
57
88
  end
58
89
 
90
+ @log = T.let(nil, T.nilable(Logger))
91
+
92
+ sig {returns(Logger)}
59
93
  def log
60
94
  @log ||= Paperback.class_log(self.class)
61
95
  end
96
+ sig {returns(Logger)}
62
97
  def self.log
63
98
  @log ||= Paperback.class_log(self)
64
99
  end
65
100
 
101
+ sig {params(output_filename: String, extra_draw_opts: T::Hash[T.untyped, T.untyped]).void}
66
102
  def render(output_filename:, extra_draw_opts: {})
67
103
  log.debug('Preparer#render')
68
104
 
@@ -73,12 +109,20 @@ module Paperback
73
109
  sixword_bytes: data.bytesize,
74
110
  }
75
111
 
112
+ if include_base64?
113
+ opts[:base64_content] = base64_content
114
+ opts[:base64_bytes] = data.bytesize
115
+ end
116
+
76
117
  if encrypt
77
118
  opts[:passphrase_sha] = self.class.truncated_sha256(passphrase)
78
119
  opts[:passphrase_len] = passphrase.length
79
120
  if passphrase_file
80
- File.open(passphrase_file, File::CREAT|File::EXCL|File::WRONLY,
81
- 0400) do |f|
121
+ File.open(
122
+ T.must(passphrase_file),
123
+ File::CREAT | File::EXCL | File::WRONLY,
124
+ 0o400
125
+ ) do |f|
82
126
  f.write(passphrase)
83
127
  end
84
128
  log.info("Wrote passphrase to #{passphrase_file.inspect}")
@@ -91,18 +135,30 @@ module Paperback
91
135
 
92
136
  log.info('Render complete')
93
137
 
94
- if encrypt && !passphrase_file
95
- puts "Passphrase: #{passphrase}"
138
+ if encrypt
139
+ puts 'SHA256(passphrase)[0...16]: ' + opts.fetch(:passphrase_sha)
140
+ if !passphrase_file
141
+ puts "Passphrase: #{passphrase}"
142
+ end
143
+ else
144
+ log.info('Content was not encrypted')
96
145
  end
97
146
  end
98
147
 
148
+ sig {returns(String)}
99
149
  def passphrase
100
150
  raise "Can't have passphrase without encrypt" unless encrypt
101
151
  @passphrase ||= self.class.random_passphrase
102
152
  end
103
153
 
104
- PassChars = [*'a'..'z', *'A'..'Z', *'0'..'9'].freeze
154
+ PassChars = T.let(
155
+ [*'a'..'z', *'A'..'Z', *'0'..'9'].freeze, T::Array[String]
156
+ )
105
157
 
158
+ sig do
159
+ params(entropy_bits: Integer, char_set: T::Array[String])
160
+ .returns(String)
161
+ end
106
162
  def self.random_passphrase(entropy_bits: 256, char_set: PassChars)
107
163
  chars_needed = (entropy_bits / Math.log2(char_set.length)).ceil
108
164
  (0...chars_needed).map {
@@ -111,35 +167,40 @@ module Paperback
111
167
  end
112
168
 
113
169
  # Compute a truncated SHA256 digest
170
+ sig {params(content: String).returns(String)}
114
171
  def self.truncated_sha256(content)
115
172
  Digest::SHA256.hexdigest(content)[0...16]
116
173
  end
117
174
 
175
+ sig {params(filename: String, password: String).returns(String)}
118
176
  def self.gpg_encrypt(filename:, password:)
119
177
  cmd = %w[
120
178
  gpg -c -o - --batch --cipher-algo aes256 --passphrase-fd 0 --
121
179
  ] + [filename]
122
- out = nil
180
+ out = T.let(nil, T.nilable(String))
123
181
 
124
182
  log.debug('+ ' + cmd.join(' '))
125
183
  Subprocess.check_call(cmd, stdin: Subprocess::PIPE,
126
- stdout: Subprocess::PIPE) do |p|
184
+ stdout: Subprocess::PIPE) do |p|
127
185
  out, _err = p.communicate(password)
128
186
  end
129
187
 
130
- out
188
+ T.must(out)
131
189
  end
132
190
 
191
+ sig {params(data: String, strip_comments: T::Boolean).returns(String)}
133
192
  def self.gpg_ascii_enarmor(data, strip_comments: true)
134
193
  cmd = %w[gpg --batch --enarmor]
135
- out = nil
194
+ out = T.let(nil, T.nilable(String))
136
195
 
137
196
  log.debug('+ ' + cmd.join(' '))
138
197
  Subprocess.check_call(cmd, stdin: Subprocess::PIPE,
139
- stdout: Subprocess::PIPE) do |p|
198
+ stdout: Subprocess::PIPE) do |p|
140
199
  out, _err = p.communicate(data)
141
200
  end
142
201
 
202
+ out = T.must(out)
203
+
143
204
  if strip_comments
144
205
  out = out.each_line.select { |l| !l.start_with?('Comment: ') }.join
145
206
  end
@@ -147,37 +208,42 @@ module Paperback
147
208
  out
148
209
  end
149
210
 
211
+ sig {params(data: String).returns(String)}
150
212
  def self.gpg_ascii_dearmor(data)
151
213
  cmd = %w[gpg --batch --dearmor]
152
- out = nil
214
+ out = T.let(nil, T.nilable(String))
153
215
 
154
216
  log.debug('+ ' + cmd.join(' '))
155
217
  Subprocess.check_call(cmd, stdin: Subprocess::PIPE,
156
- stdout: Subprocess::PIPE) do |p|
218
+ stdout: Subprocess::PIPE) do |p|
157
219
  out, _err = p.communicate(data)
158
220
  end
159
221
 
160
- out
222
+ T.must(out)
223
+ end
224
+
225
+ # Whether to add the Base64 encoding to the generated document.
226
+ #
227
+ # @return [Boolean]
228
+ sig {returns(T::Boolean)}
229
+ def include_base64?
230
+ !!@include_base64
161
231
  end
162
232
 
163
233
  private
164
234
 
235
+ sig {returns(RQRCode::QRCode)}
165
236
  def qr_code
166
237
  @qr_code ||= qr_code!
167
238
  end
168
239
 
240
+ sig {returns(RQRCode::QRCode)}
169
241
  def qr_code!
170
242
  log.info('Generating QR code')
171
243
 
172
244
  # Base64 encode data prior to QR encoding as requested
173
245
  if qr_base64
174
- if encrypt
175
- # If data is already GPG encrypted, use GPG's base64 armor
176
- input = self.class.gpg_ascii_enarmor(data)
177
- else
178
- # Otherwise do plain Base64
179
- input = Base64.encode64(data)
180
- end
246
+ input = base64_content
181
247
  else
182
248
  input = data
183
249
  end
@@ -198,11 +264,23 @@ module Paperback
198
264
  RQRCode::QRCode.new(input, level: @qr_level)
199
265
  end
200
266
 
267
+ sig {returns(T::Array[String])}
201
268
  def sixword_lines
202
269
  log.info('Encoding with Sixword')
203
270
  @sixword_lines ||=
204
271
  Sixword.pad_encode_to_sentences(data).map(&:downcase)
205
272
  end
206
273
 
274
+ sig {returns(String)}
275
+ def base64_content
276
+ log.debug('Encoding with Base64')
277
+ if encrypt
278
+ # If data is already GPG encrypted, use GPG's base64 armor
279
+ self.class.gpg_ascii_enarmor(data)
280
+ else
281
+ # Otherwise do plain Base64
282
+ Base64.encode64(data)
283
+ end
284
+ end
207
285
  end
208
286
  end
@@ -1,5 +1,6 @@
1
+ # typed: strong
1
2
  # frozen_string_literal: true
2
3
  module Paperback
3
4
  # version string
4
- VERSION = '0.0.3'
5
+ VERSION = '0.0.5'
5
6
  end
data/lib/paperback.rb CHANGED
@@ -1,7 +1,14 @@
1
+ # typed: strict
1
2
  require 'logger'
3
+ require 'sorbet-runtime'
2
4
 
3
5
  # Paperback is a library for creating paper backups of sensitive data.
4
6
  module Paperback
7
+ extend T::Sig
8
+
9
+ @log = T.let(nil, T.nilable(Logger))
10
+
11
+ sig {returns(Logger)}
5
12
  def self.log
6
13
  return @log if @log
7
14
  @log = Logger.new(STDERR)
@@ -10,6 +17,7 @@ module Paperback
10
17
  @log
11
18
  end
12
19
 
20
+ sig {params(klass: Class, stream: IO).returns(Logger)}
13
21
  def self.class_log(klass, stream=STDERR)
14
22
  log = Logger.new(stream)
15
23
  log.progname = klass.name
@@ -17,10 +25,14 @@ module Paperback
17
25
  log
18
26
  end
19
27
 
28
+ @log_level = T.let(nil, T.nilable(Integer))
29
+
30
+ sig {returns(Integer)}
20
31
  def self.log_level
21
32
  @log_level ||= Logger::INFO
22
33
  end
23
34
 
35
+ sig {params(val: Integer).returns(Integer)}
24
36
  def self.log_level=(val)
25
37
  @log_level = val
26
38
  end
data/paperback.gemspec CHANGED
@@ -1,8 +1,6 @@
1
- # coding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path('../lib', __FILE__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require 'paperback/version'
3
+ require_relative 'lib/paperback/version'
6
4
 
7
5
  Gem::Specification.new do |spec|
8
6
  spec.name = 'paperback'
@@ -17,22 +15,27 @@ Gem::Specification.new do |spec|
17
15
  spec.homepage = 'https://github.com/ab/paperback'
18
16
  spec.license = 'GPL-3'
19
17
 
20
- spec.files = `git ls-files`.split($/)
18
+ spec.files = `git ls-files`.split($/).reject { |s|
19
+ s.start_with?('sample/')
20
+ }
21
21
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
22
22
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
23
23
  spec.require_paths = ['lib']
24
24
 
25
- spec.add_development_dependency 'bundler', '~> 1.3'
25
+ spec.add_development_dependency 'bundler', '~> 2.0'
26
26
  spec.add_development_dependency 'pry'
27
27
  spec.add_development_dependency 'rake'
28
28
  spec.add_development_dependency 'rspec', '~> 3.0'
29
29
  spec.add_development_dependency 'rubocop', '~> 0.50'
30
+ spec.add_development_dependency 'sorbet', '~> 0.5'
31
+ spec.add_development_dependency 'tapioca', '~> 0.10'
30
32
  spec.add_development_dependency 'yard'
31
33
 
32
34
  spec.add_dependency('prawn', '~> 1.3')
33
35
  spec.add_dependency('rqrcode', '~> 0.10')
34
36
  spec.add_dependency('sixword', '~> 0.3')
37
+ spec.add_dependency('sorbet-runtime', '~> 0.5')
35
38
  spec.add_dependency('subprocess', '~> 1.3')
36
39
 
37
- spec.required_ruby_version = '>= 2.0'
40
+ spec.required_ruby_version = '>= 2.7'
38
41
  end
data/sorbet/config ADDED
@@ -0,0 +1,3 @@
1
+ --dir
2
+ .
3
+ --ignore=vendor/
@@ -0,0 +1,269 @@
1
+ # typed: strict
2
+
3
+ # DO NOT EDIT MANUALLY
4
+ # This file was pulled from a central RBI files repository.
5
+ # Please run `bin/tapioca annotations` to update it.
6
+
7
+ module Rainbow
8
+ # @shim: https://github.com/sickill/rainbow/blob/master/lib/rainbow.rb#L10-L12
9
+ sig { returns(T::Boolean) }
10
+ attr_accessor :enabled
11
+
12
+ class Color
13
+ sig { returns(Symbol) }
14
+ attr_reader :ground
15
+
16
+ sig { params(ground: Symbol, values: T.any([Integer], [Integer, Integer, Integer])).returns(Color) }
17
+ def self.build(ground, values); end
18
+
19
+ sig { params(hex: String).returns([Integer, Integer, Integer]) }
20
+ def self.parse_hex_color(hex); end
21
+
22
+ class Indexed < Rainbow::Color
23
+ sig { returns(Integer) }
24
+ attr_reader :num
25
+
26
+ sig { params(ground: Symbol, num: Integer).void }
27
+ def initialize(ground, num); end
28
+
29
+ sig { returns(T::Array[Integer]) }
30
+ def codes; end
31
+ end
32
+
33
+ class Named < Rainbow::Color::Indexed
34
+ NAMES = T.let(nil, T::Hash[Symbol, Integer])
35
+
36
+ sig { params(ground: Symbol, name: Symbol).void }
37
+ def initialize(ground, name); end
38
+
39
+ sig { returns(T::Array[Symbol]) }
40
+ def self.color_names; end
41
+
42
+ sig { returns(String) }
43
+ def self.valid_names; end
44
+ end
45
+
46
+ class RGB < Rainbow::Color::Indexed
47
+ sig { returns(Integer) }
48
+ attr_reader :r, :g, :b
49
+
50
+ sig { params(ground: Symbol, values: Integer).void }
51
+ def initialize(ground, *values); end
52
+
53
+ sig { returns(T::Array[Integer]) }
54
+ def codes; end
55
+
56
+ sig { params(value: Numeric).returns(Integer) }
57
+ def self.to_ansi_domain(value); end
58
+ end
59
+
60
+ class X11Named < Rainbow::Color::RGB
61
+ include Rainbow::X11ColorNames
62
+
63
+ sig { returns(T::Array[Symbol]) }
64
+ def self.color_names; end
65
+
66
+ sig { returns(String) }
67
+ def self.valid_names; end
68
+
69
+ sig { params(ground: Symbol, name: Symbol).void }
70
+ def initialize(ground, name); end
71
+ end
72
+ end
73
+
74
+ sig { returns(Wrapper) }
75
+ def self.global; end
76
+
77
+ sig { returns(T::Boolean) }
78
+ def self.enabled; end
79
+
80
+ sig { params(value: T::Boolean).returns(T::Boolean) }
81
+ def self.enabled=(value); end
82
+
83
+ sig { params(string: String).returns(String) }
84
+ def self.uncolor(string); end
85
+
86
+ class NullPresenter < String
87
+ sig { params(values: T.any([Integer], [Integer, Integer, Integer])).returns(NullPresenter) }
88
+ def color(*values); end
89
+
90
+ sig { params(values: T.any([Integer], [Integer, Integer, Integer])).returns(NullPresenter) }
91
+ def foreground(*values); end
92
+
93
+ sig { params(values: T.any([Integer], [Integer, Integer, Integer])).returns(NullPresenter) }
94
+ def fg(*values); end
95
+
96
+ sig { params(values: T.any([Integer], [Integer, Integer, Integer])).returns(NullPresenter) }
97
+ def background(*values); end
98
+
99
+ sig { params(values: T.any([Integer], [Integer, Integer, Integer])).returns(NullPresenter) }
100
+ def bg(*values); end
101
+
102
+ sig { returns(NullPresenter) }
103
+ def reset; end
104
+
105
+ sig { returns(NullPresenter) }
106
+ def bright; end
107
+
108
+ sig { returns(NullPresenter) }
109
+ def faint; end
110
+
111
+ sig { returns(NullPresenter) }
112
+ def italic; end
113
+
114
+ sig { returns(NullPresenter) }
115
+ def underline; end
116
+
117
+ sig { returns(NullPresenter) }
118
+ def blink; end
119
+
120
+ sig { returns(NullPresenter) }
121
+ def inverse; end
122
+
123
+ sig { returns(NullPresenter) }
124
+ def hide; end
125
+
126
+ sig { returns(NullPresenter) }
127
+ def cross_out; end
128
+
129
+ sig { returns(NullPresenter) }
130
+ def black; end
131
+
132
+ sig { returns(NullPresenter) }
133
+ def red; end
134
+
135
+ sig { returns(NullPresenter) }
136
+ def green; end
137
+
138
+ sig { returns(NullPresenter) }
139
+ def yellow; end
140
+
141
+ sig { returns(NullPresenter) }
142
+ def blue; end
143
+
144
+ sig { returns(NullPresenter) }
145
+ def magenta; end
146
+
147
+ sig { returns(NullPresenter) }
148
+ def cyan; end
149
+
150
+ sig { returns(NullPresenter) }
151
+ def white; end
152
+
153
+ sig { returns(NullPresenter) }
154
+ def bold; end
155
+
156
+ sig { returns(NullPresenter) }
157
+ def dark; end
158
+
159
+ sig { returns(NullPresenter) }
160
+ def strike; end
161
+ end
162
+
163
+ class Presenter < String
164
+ TERM_EFFECTS = T.let(nil, T::Hash[Symbol, Integer])
165
+
166
+ sig { params(values: T.any([Integer], [Integer, Integer, Integer])).returns(Presenter) }
167
+ def color(*values); end
168
+
169
+ sig { params(values: T.any([Integer], [Integer, Integer, Integer])).returns(Presenter) }
170
+ def foreground(*values); end
171
+
172
+ sig { params(values: T.any([Integer], [Integer, Integer, Integer])).returns(Presenter) }
173
+ def fg(*values); end
174
+
175
+ sig { params(values: T.any([Integer], [Integer, Integer, Integer])).returns(Presenter) }
176
+ def background(*values); end
177
+
178
+ sig { params(values: T.any([Integer], [Integer, Integer, Integer])).returns(Presenter) }
179
+ def bg(*values); end
180
+
181
+ sig { returns(Presenter) }
182
+ def reset; end
183
+
184
+ sig { returns(Presenter) }
185
+ def bright; end
186
+
187
+ sig { returns(Presenter) }
188
+ def faint; end
189
+
190
+ sig { returns(Presenter) }
191
+ def italic; end
192
+
193
+ sig { returns(Presenter) }
194
+ def underline; end
195
+
196
+ sig { returns(Presenter) }
197
+ def blink; end
198
+
199
+ sig { returns(Presenter) }
200
+ def inverse; end
201
+
202
+ sig { returns(Presenter) }
203
+ def hide; end
204
+
205
+ sig { returns(Presenter) }
206
+ def cross_out; end
207
+
208
+ sig { returns(Presenter) }
209
+ def black; end
210
+
211
+ sig { returns(Presenter) }
212
+ def red; end
213
+
214
+ sig { returns(Presenter) }
215
+ def green; end
216
+
217
+ sig { returns(Presenter) }
218
+ def yellow; end
219
+
220
+ sig { returns(Presenter) }
221
+ def blue; end
222
+
223
+ sig { returns(Presenter) }
224
+ def magenta; end
225
+
226
+ sig { returns(Presenter) }
227
+ def cyan; end
228
+
229
+ sig { returns(Presenter) }
230
+ def white; end
231
+
232
+ sig { returns(Presenter) }
233
+ def bold; end
234
+
235
+ sig { returns(Presenter) }
236
+ def dark; end
237
+
238
+ sig { returns(Presenter) }
239
+ def strike; end
240
+ end
241
+
242
+ class StringUtils
243
+ sig { params(string: String, codes: T::Array[Integer]).returns(String) }
244
+ def self.wrap_with_sgr(string, codes); end
245
+
246
+ sig { params(string: String).returns(String) }
247
+ def self.uncolor(string); end
248
+ end
249
+
250
+ VERSION = T.let(nil, String)
251
+
252
+ class Wrapper
253
+ sig { returns(T::Boolean) }
254
+ attr_accessor :enabled
255
+
256
+ sig { params(enabled: T::Boolean).void }
257
+ def initialize(enabled = true); end
258
+
259
+ sig { params(string: String).returns(T.any(Rainbow::Presenter, Rainbow::NullPresenter)) }
260
+ def wrap(string); end
261
+ end
262
+
263
+ module X11ColorNames
264
+ NAMES = T.let(nil, T::Hash[Symbol, [Integer, Integer, Integer]])
265
+ end
266
+ end
267
+
268
+ sig { params(string: String).returns(Rainbow::Presenter) }
269
+ def Rainbow(string); end