squared 0.5.15 → 0.6.0

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.
@@ -10,9 +10,9 @@ module Squared
10
10
  include Common::Shell
11
11
  extend Forwardable
12
12
 
13
- OPT_NAME = /\A(?:(--)|-)((?(1)[^\[\]=\s-][^\[\]=\s]*|[^\[\]=\s-]))\z/
14
- OPT_VALUE = /\A-{0,2}([^\[\]=\s-][^\[\]=\s]*)(?:=|\s+)(\S.*)\z/
15
- OPT_SINGLE = /\A-([^\[\]=\s-])(.+)\z/
13
+ OPT_NAME = /\A(?:(--)|-)((?(1)[A-Za-z\d]+|[A-Za-z\d]))\z/
14
+ OPT_VALUE = /\A-{0,2}([^= ]+)(?: *= *| +)(.+)\z/
15
+ OPT_SINGLE = /\A-([A-Za-z\d])(.+)\z/
16
16
  private_constant :OPT_NAME, :OPT_VALUE, :OPT_SINGLE
17
17
 
18
18
  class << self
@@ -21,19 +21,33 @@ module Squared
21
21
  include Prompt
22
22
 
23
23
  def append(target, *args, delim: false, escape: false, quote: true, strip: nil, force: true, double: false,
24
- **)
24
+ filter: nil, **)
25
25
  return if (ret = args.flatten).empty?
26
26
 
27
27
  target << '--' if delim && !target.include?('--')
28
28
  if strip
29
29
  pat, s = Array(strip)
30
- ret.map! { |val| val.is_a?(String) ? val.gsub(pat, s || '') : val }
30
+ ret.map! { |val| val.gsub(pat, s || '') }
31
+ end
32
+ ret, err = ret.partition { |val| filter.match?(val) } if filter
33
+ if block_given?
34
+ out = []
35
+ err ||= []
36
+ ret.each do |val|
37
+ case (s = yield val)
38
+ when String
39
+ out << s
40
+ when NilClass, FalseClass
41
+ err << val
42
+ else
43
+ out << val
44
+ end
45
+ end
46
+ ret = out
31
47
  end
32
48
  if escape || quote
33
49
  ret.map! do |val|
34
- if opt?(val)
35
- val
36
- elsif escape
50
+ if escape
37
51
  shell_escape(val, quote: quote, double: double)
38
52
  else
39
53
  shell_quote(val, force: force, double: double)
@@ -45,13 +59,13 @@ module Squared
45
59
  else
46
60
  target.concat(ret)
47
61
  end
48
- ret
62
+ err || ret
49
63
  end
50
64
 
51
65
  def clear(target, opts, pass: true, styles: nil, **kwargs)
52
66
  return if opts.empty?
53
67
 
54
- kwargs[:subject] ||= stripext target.first
68
+ kwargs[:subject] ||= target.first.stripext
55
69
  kwargs[:hint] ||= 'unrecognized'
56
70
  append(target, opts, delim: true) if kwargs.delete(:append)
57
71
  warn log_message(Logger::WARN, opts.join(', '), pass: true, **kwargs)
@@ -86,20 +100,25 @@ module Squared
86
100
  ret.select { |val| single ? val.size == 1 : val.size > 1 }
87
101
  end
88
102
 
89
- def arg?(target, *args, value: false, **)
90
- r, s = args.partition { |val| val.is_a?(Regexp) }
91
- r << matchopts(s, value) unless s.empty?
92
- a = target.to_a
93
- if (n = a.index('--'))
94
- a = a[0..n]
103
+ def uniq!(list, pass = [])
104
+ keys = {}
105
+ list.each_with_index do |val, i|
106
+ j = val =~ OPT_VALUE ? $1 : val
107
+ (keys[j] ||= []) << i unless pass.include?(j)
95
108
  end
96
- r.any? { |pat| a.any?(pat) }
97
- end
109
+ data = keys.map { |item| item[1].size > 1 ? item[1][0..-2] : [] }.reject(&:empty?)
110
+ return if data.empty?
98
111
 
99
- def opt?(val)
100
- return false unless val.is_a?(String)
112
+ data.each { |key| key.each { |i| list[i] = nil } }
113
+ list.compact!
114
+ list
115
+ end
101
116
 
102
- val.start_with?('-') && (OPT_NAME.match?(val) || OPT_VALUE.match?(val) || OPT_SINGLE.match?(val))
117
+ def arg?(target, *args, value: false, **)
118
+ r, s = args.partition { |val| val.is_a?(Regexp) }
119
+ r << matchopts(s, value) unless s.empty?
120
+ s = target.to_a.compact
121
+ r.any? { |pat| s.any?(pat) }
103
122
  end
104
123
 
105
124
  def pattern?(val)
@@ -121,23 +140,22 @@ module Squared
121
140
  end
122
141
 
123
142
  def shortopt(*group)
124
- group.map! { |s| Regexp.escape(s.delete_prefix('-')) }
125
- "-(?:#{group.join('|')})(?:\\z|[^ =]| +[^ -])"
143
+ group.map! { |s| s.delete_prefix('-') }
144
+ "-(?:#{Regexp.escape(group.join('|'))})(?:\\z|[^ =]| +[^ -])"
126
145
  end
127
146
 
128
147
  def longopt(*group, value)
129
- group.map! { |s| Regexp.escape(s.delete_prefix('--')) }
130
- "--(?:#{group.join('|')})(?:#{value ? '=[^ ]| +[^ -]' : '[= ]|\z'})"
148
+ group.map! { |s| s.delete_prefix('--') }
149
+ "--(?:#{Regexp.escape(group.join('|'))})(?:#{value ? '=[^ ]| +[^ -]' : '[= ]|\z'})"
131
150
  end
132
151
  end
133
152
 
134
- attr_reader :target, :extras, :found, :errors, :values, :project, :path
153
+ attr_reader :target, :extras, :found, :errors, :values, :project, :path, :sep
135
154
 
136
155
  def_delegators :@target, :+, :-, :<<, :any?, :none?, :include?, :add, :add?, :find, :find_all, :find_index,
137
156
  :merge, :delete, :delete?, :delete_if, :grep, :grep_v, :inspect, :to_a, :to_s
138
- def_delegators :@extras, :empty?, :each, :each_with_index, :partition, :dup, :first, :last, :shift, :unshift,
139
- :pop, :push, :concat, :index, :join, :map, :map!, :detect, :select, :select!, :reject, :size,
140
- :delete_at
157
+ def_delegators :@extras, :empty?, :each, :each_with_index, :partition, :dup, :first, :shift, :unshift,
158
+ :pop, :push, :concat, :index, :join, :map, :map!, :detect, :select, :select!, :reject, :size
141
159
 
142
160
  def_delegator :@extras, :delete, :remove
143
161
  def_delegator :@extras, :delete_at, :remove_at
@@ -145,10 +163,11 @@ module Squared
145
163
  def_delegator :@extras, :find_all, :detect_all
146
164
  def_delegator :@extras, :find_index, :detect_index
147
165
 
148
- def initialize(opts, list, target = Set.new, project: nil, path: nil, **kwargs, &blk)
166
+ def initialize(opts, list, target = Set.new, project: nil, path: nil, sep: '=', **kwargs, &blk)
149
167
  @target = target.is_a?(Set) ? target : target.to_set
150
168
  @project = project
151
169
  @path = path || project&.path
170
+ @sep = sep
152
171
  @errors = []
153
172
  @found = []
154
173
  parse(list, opts, **kwargs, &blk)
@@ -182,7 +201,7 @@ module Squared
182
201
  .each do |val|
183
202
  if (n = val.index('='))
184
203
  flag = val[0, n]
185
- case val[n + 1]
204
+ case val[n.succ]
186
205
  when 'e'
187
206
  e << flag
188
207
  when 'b'
@@ -207,7 +226,7 @@ module Squared
207
226
  else
208
227
  next
209
228
  end
210
- m << flag if flag.size == 1 && val[n + 2] == 'm'
229
+ m << flag if val[n + 2] == 'm'
211
230
  bare << flag if val.end_with?('?')
212
231
  else
213
232
  bare << val
@@ -216,7 +235,7 @@ module Squared
216
235
  no = (no || []).map { |val| (n = val.index('=')) ? val[0, n] : val }
217
236
  bare.concat(no)
218
237
  if underscore
219
- tr = ->(list) { list.map { |val| val.tr('-', '_') } }
238
+ tr = ->(a) { a.map { |val| val.tr('-', '_') } }
220
239
  @values.concat(tr.call(@values))
221
240
  bare.concat(tr.call(bare))
222
241
  e.concat(tr.call(e))
@@ -236,7 +255,7 @@ module Squared
236
255
  [f, /\A\d*(?:\.\d+)?\z/],
237
256
  [si, /\A-?\d+\z/]
238
257
  ].freeze
239
- numcheck = ->(k, v) { numtype.any? { |flag, pat| flag.include?(k) && pat.match?(v) } }
258
+ numcheck = ->(k, v) { numtype.any? { |flag, pat| flag.include?(k) && v.match?(pat) } }
240
259
  skip = false
241
260
  opts.each do |opt|
242
261
  next skip = true if opt == '--'
@@ -254,21 +273,21 @@ module Squared
254
273
  val = $2
255
274
  merge = m.include?(key)
256
275
  if e.include?(key)
257
- add shell_option(key, val, merge: merge)
276
+ add shell_option(key, val, merge: merge, sep: sep)
258
277
  elsif q.include?(key)
259
- add quote_option(key, val, double: qq.include?(key), merge: merge)
278
+ add quote_option(key, val, double: qq.include?(key), merge: merge, sep: sep)
260
279
  elsif p.include?(key)
261
280
  if val.match?(/\A(["']).+\1\z/)
262
- add shell_option(key, val, escape: false, merge: merge)
281
+ add shell_option(key, val, escape: false, merge: merge, sep: sep)
263
282
  elsif path
264
- add quote_option(key, path + val, merge: merge)
283
+ add quote_option(key, path + val, merge: merge, sep: sep)
265
284
  else
266
285
  push opt
267
286
  end
268
287
  elsif b.include?(key) || (bl.include?(key) && %w[true false].include?(val)) || numcheck.call(key, val)
269
- add basic_option(key, val, merge: merge)
288
+ add basic_option(key, val, merge: merge, sep: sep)
270
289
  elsif merge
271
- add basic_option(key, val, merge: true)
290
+ add basic_option(key, val, merge: true, sep: sep)
272
291
  else
273
292
  push opt
274
293
  end
@@ -280,28 +299,33 @@ module Squared
280
299
  skip = true if first&.any? { |s| s.is_a?(Regexp) ? opt.match?(s) : !opt.include?(s) }
281
300
  end
282
301
  end
283
- @values = @values.empty? ? /\A\s+\z/ : /\A(#{@values.join('|')})=(.+)\z/m
302
+ @values = @values.empty? ? /\A\s+\z/ : /\A(#{@values.join('|')})#{sep}(.+)\z/m
284
303
  @extras.each_with_index(&blk) if block_given?
285
304
  self
286
305
  end
287
306
 
288
- def swap(opts = nil)
307
+ def swap(opts = nil, &blk)
289
308
  unless opts
290
309
  opts = found
291
310
  @found = []
292
311
  end
312
+ opts.sort!(&blk) if block_given?
293
313
  @extras = opts
294
314
  self
295
315
  end
296
316
 
297
- def append(*args, **kwargs)
317
+ def append(*args, **kwargs, &blk)
298
318
  args = extras if args.empty?
299
- OptionPartition.append(target, *args, **kwargs)
319
+ out = OptionPartition.append(target, *args, **kwargs, &blk)
320
+ errors.concat(out) if out && (block_given? || kwargs[:filter])
300
321
  self
301
322
  end
302
323
 
303
324
  def append_any(*args, quote: true, **kwargs)
304
325
  (args.empty? ? extras : args.flatten).each do |val|
326
+ val = yield val if block_given?
327
+ next unless val.is_a?(String)
328
+
305
329
  if exist?(val)
306
330
  add_path(val, **kwargs)
307
331
  elsif quote
@@ -319,7 +343,7 @@ module Squared
319
343
  end
320
344
 
321
345
  def values_of(*args, strict: true, first: false, last: false)
322
- eq, s = strict ? ['=', '[^ ]+'] : ['(?:=| +)', '[^-][^ ]*']
346
+ eq, s = strict ? [sep, '[^ ]+'] : ['(?:=| +)', '[^-][^ ]*']
323
347
  g = ["\"((?:[^\"]|(?<=\\\\)\"(?!$#{windows? ? '| ' : ''}))*)\""]
324
348
  g << "'((?:[^']|'\\\\'')*)'" unless windows?
325
349
  g << "(#{s})"
@@ -358,22 +382,18 @@ module Squared
358
382
  def clear(opts = nil, errors: false, **kwargs)
359
383
  styles = project.theme[:inline] if project
360
384
  if errors
361
- OptionPartition.clear(target, self.errors, styles: styles, **kwargs)
362
- self.errors.clear
385
+ OptionPartition.clear(target, @errors, styles: styles, **kwargs)
386
+ @errors.clear
363
387
  return self unless opts
364
388
  end
365
389
  opts ||= extras
366
- OptionPartition.clear(target, if found.empty?
367
- opts
368
- else
369
- opts.reject { |val| found.include?(val) }
370
- end, styles: styles, **kwargs)
390
+ OptionPartition.clear(target, opts - found, styles: styles, **kwargs)
371
391
  opts.clear
372
392
  self
373
393
  end
374
394
 
375
395
  def adjoin(*args, with: nil, start: false)
376
- i = -1
396
+ index = -1
377
397
  temp = to_a
378
398
  if with
379
399
  pat = case with
@@ -384,53 +404,114 @@ module Squared
384
404
  else
385
405
  with
386
406
  end
387
- temp.each_with_index do |val, index|
407
+ temp.each_with_index do |val, i|
388
408
  if val.to_s.match?(pat)
389
- i = index + (start.is_a?(Numeric) ? start : 1)
409
+ index = i + (start.is_a?(Numeric) ? start : 1)
390
410
  break
391
411
  end
392
412
  end
393
413
  else
394
- temp.each_with_index do |val, index|
395
- if i == 0
414
+ temp.each_with_index do |val, i|
415
+ if index == 0
396
416
  next unless val.start_with?('-')
397
417
 
398
- i = index
418
+ index = i
399
419
  break
400
- elsif index > 0 && !val.start_with?('-')
420
+ elsif i > 0 && !val.start_with?('-')
401
421
  if start
402
- i = index + (start.is_a?(Numeric) ? start : 1)
422
+ index = i + (start.is_a?(Numeric) ? start : 1)
403
423
  break
404
424
  end
405
- i = 0
425
+ index = 0
406
426
  end
407
427
  end
408
428
  end
409
- if i > 0
429
+ if index > 0
410
430
  if args.empty?
411
431
  args = dup
412
432
  reset
413
433
  else
414
434
  args.each { |val| remove val }
415
435
  end
416
- args = temp[0...i] + args + temp[i..-1]
436
+ args = temp[0...index] + args + temp[index..-1]
417
437
  target.clear
418
438
  end
419
439
  merge args
420
440
  self
421
441
  end
422
442
 
423
- def add_path(*args, **kwargs)
424
- add shell_quote(path ? path.join(*args) : File.join(*args), option: false, **kwargs)
443
+ def add_path(*args, force: true, double: false, **kwargs)
444
+ if args.empty?
445
+ args = select { |s| s.is_a?(String) }
446
+ args.map! { |val| path + val } if path
447
+ append(args, force: force, **kwargs)
448
+ else
449
+ add shell_quote(path ? path.join(*args) : File.join(*args), option: false, force: force, double: double)
450
+ end
425
451
  self
426
452
  end
427
453
 
428
454
  def add_quote(*args, **kwargs)
429
- args.compact!
430
- merge(args.map! { |val| val == '--' || OptionPartition.opt?(val) ? val : shell_quote(val, **kwargs) })
455
+ merge(args.compact.map! { |val| val == '--' ? val : shell_quote(val, **kwargs) })
431
456
  self
432
457
  end
433
458
 
459
+ def add_option(flag, val = nil, **kwargs)
460
+ add shell_option(flag, val, **kwargs)
461
+ self
462
+ end
463
+
464
+ def add_first(fallback = nil, prefix: nil, path: false, quote: false, reverse: false, expect: false, **kwargs)
465
+ val = (reverse ? pop : shift) || fallback
466
+ if val
467
+ val.delete_prefix!(prefix) if prefix
468
+ return self if block_given? && !(val = yield val).is_a?(String)
469
+
470
+ if path
471
+ add_path(val, **kwargs)
472
+ elsif quote
473
+ add_quote(val, **kwargs)
474
+ else
475
+ add val
476
+ end
477
+ elsif expect
478
+ raise(expect.is_a?(String) ? expect : 'no value provided')
479
+ end
480
+ self
481
+ end
482
+
483
+ def last(val = nil, &blk)
484
+ unless block_given?
485
+ case val
486
+ when NilClass
487
+ return extras.last
488
+ when Numeric
489
+ return extras.last(val)
490
+ when String, Array, Regexp
491
+ val = OptionPartition.send(:matchopts, val) unless val.is_a?(Regexp)
492
+ blk = proc { |s| s&.match?(val) }
493
+ else
494
+ raise TypeError, "unknown: #{val}"
495
+ end
496
+ end
497
+ ret = find_all(&blk)
498
+ unless ret.empty?
499
+ ret = case val
500
+ when NilClass
501
+ ret.first(1)
502
+ when Numeric
503
+ ret.first(val)
504
+ else
505
+ ret
506
+ end
507
+ ret.each do |opt|
508
+ delete opt
509
+ add opt
510
+ end
511
+ end
512
+ val.nil? ? ret.first : ret
513
+ end
514
+
434
515
  def splice(*exclude, quote: true, delim: true, path: false, pattern: false, &blk)
435
516
  found, other = if block_given?
436
517
  partition(&blk)
@@ -464,7 +545,7 @@ module Squared
464
545
  self
465
546
  end
466
547
 
467
- def append?(key, val = nil, type: nil, force: false, **kwargs)
548
+ def append?(key, val = nil, type: nil, force: false, sep: '=', **kwargs)
468
549
  return false unless force || !arg?(key)
469
550
 
470
551
  val = yield self if block_given?
@@ -473,11 +554,11 @@ module Squared
473
554
  type ||= :quote if kwargs.empty?
474
555
  add case type
475
556
  when :quote
476
- quote_option(key, val)
557
+ quote_option(key, val, sep: sep)
477
558
  when :basic
478
- basic_option(key, val)
559
+ basic_option(key, val, sep: sep)
479
560
  else
480
- shell_option(key, val, **kwargs)
561
+ shell_option(key, val, sep: sep, **kwargs)
481
562
  end
482
563
  true
483
564
  end
@@ -486,24 +567,23 @@ module Squared
486
567
  OptionPartition.arg?(target, *args, **kwargs)
487
568
  end
488
569
 
489
- def exist?(*args, add: false, first: false, last: false)
490
- return false unless path
491
- return path.join(*args).exist? unless args.empty?
570
+ def exist?(*args, add: false, first: false, last: false, glob: false)
571
+ return with_glob?(File.join(*args), glob) unless args.empty?
492
572
 
493
573
  if first || last
494
- return false unless (val = first ? self.first : self.last).is_a?(String)
574
+ return false unless (val = first ? self.first : self.last)
495
575
 
496
- path.join(val).exist?.tap do |ret|
576
+ with_glob?(val, glob).tap do |ret|
497
577
  next unless add && ret
498
578
 
499
- add_path(first ? shift : pop)
579
+ add_first(path: true, reverse: !first)
500
580
  end
501
581
  else
502
- each_with_index do |val, index|
503
- next unless val.is_a?(String) && path.join(val).exist?
582
+ each_with_index do |val, i|
583
+ next unless with_glob?(val, glob)
504
584
 
505
585
  if add
506
- remove_at index
586
+ remove_at i
507
587
  add_path val
508
588
  end
509
589
  return true
@@ -525,6 +605,12 @@ module Squared
525
605
  val[OPT_VALUE, 1] || val
526
606
  end
527
607
 
608
+ def with_glob?(val, glob = true)
609
+ return false unless path && val.is_a?(String) && !val.empty?
610
+
611
+ path.join(val).exist? || (glob && !path.glob(val).empty?)
612
+ end
613
+
528
614
  def windows?
529
615
  Rake::Win32.windows?
530
616
  end
@@ -535,46 +621,40 @@ module Squared
535
621
  super[/[^:]+\z/, 0]
536
622
  end
537
623
 
538
- alias to_ary to_a
539
-
540
- attr_reader :delim, :extras
624
+ attr_reader :delim
541
625
 
542
- def initialize(data = [], delim: ' ', partition: '--', uniq: /\A--?[^\[\]=\s-][^\[\]=\s]*(?:=|\s+)\S/)
543
- @delim = delim
544
- @partition = partition
545
- @uniq = uniq
546
- @extras = []
626
+ def initialize(data = [], delim: ' ')
547
627
  super(data.compact)
628
+ @delim = delim
548
629
  end
549
630
 
550
631
  def last(val, pat)
551
- (@last ||= []).append([val, pat, $1]) if val =~ pat
632
+ (@last ||= []).push([val, pat, $1]) if val =~ pat
552
633
  self << val
553
634
  end
554
635
 
555
636
  def pass(&blk)
556
- ret = to_ary.map!(&:to_s).reject(&:empty?)
637
+ ret = to_a.map!(&:to_s).reject(&:empty?)
557
638
  @last&.each do |val, pat, key|
558
- i = []
559
- j = nil
560
- ret.each_with_index do |opt, index|
639
+ items = []
640
+ index = nil
641
+ ret.each_with_index do |opt, i|
561
642
  if opt == val
562
- j = index
563
- elsif j && opt[pat, 1] == key
564
- i << index
643
+ index = i
644
+ elsif index && opt[pat, 1] == key
645
+ items << i
565
646
  end
566
647
  end
567
- next unless j && !i.empty?
648
+ next unless index && !items.empty?
568
649
 
569
- val = ret[j]
570
- cur = j
571
- i.each do |k|
650
+ val = ret[index]
651
+ cur = index
652
+ items.each do |k|
572
653
  ret[cur] = ret[k]
573
654
  cur = k
574
655
  end
575
- ret[i.last] = val
656
+ ret[items.last] = val
576
657
  end
577
- ret.concat(extras.map(&:to_s).reject(&:empty?)) unless extras.empty?
578
658
  return ret unless block_given?
579
659
 
580
660
  ret.reject(&blk)
@@ -596,73 +676,18 @@ module Squared
596
676
 
597
677
  def temp(*args, &blk)
598
678
  args.compact!
599
- pass(&blk)
600
- .concat(args)
601
- .join(@delim)
679
+ ret = pass(&blk)
680
+ ret = Set.new(ret.concat(args)).to_a unless args.empty?
681
+ ret.join(@delim)
602
682
  end
603
683
 
604
684
  def done
605
685
  to_s.tap { clear }
606
686
  end
607
687
 
608
- def merge(enum)
609
- if !extras.empty?
610
- extras.concat(enum.to_a)
611
- self
612
- elsif (n = enum.find_index { |val| extras?(val) })
613
- data = enum.to_a
614
- @extras = if n == 0
615
- data
616
- else
617
- super(data[0...n])
618
- data[n..-1]
619
- end
620
- self
621
- else
622
- super
623
- end
624
- end
625
-
626
- def <<(obj)
627
- extras!(obj) || super
628
- end
629
-
630
- def add?(obj)
631
- extras!(obj) || super
632
- end
633
-
634
- def to_a
635
- pass
636
- end
637
-
638
688
  def to_s
639
689
  pass.join(@delim)
640
690
  end
641
-
642
- def to_enum(*args)
643
- pass.to_enum(*args)
644
- end
645
-
646
- def to_json(*args)
647
- pass.to_json(*args)
648
- end
649
-
650
- def to_yaml(*args)
651
- pass.to_yaml(*args)
652
- end
653
-
654
- private
655
-
656
- def extras!(obj)
657
- return if extras.empty? && !extras?(obj)
658
-
659
- extras << obj unless !extras.include?(@partition) && include?(obj) && @uniq.match?(obj.to_s)
660
- self
661
- end
662
-
663
- def extras?(obj)
664
- obj == @partition || (include?(obj) && !@uniq.match?(obj.to_s))
665
- end
666
691
  end
667
692
  end
668
693
  end