aspera-cli 4.25.2 → 4.25.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +430 -406
- data/CONTRIBUTING.md +104 -93
- data/README.md +6 -3
- data/lib/aspera/api/aoc.rb +15 -11
- data/lib/aspera/api/cos_node.rb +4 -0
- data/lib/aspera/api/faspex.rb +35 -7
- data/lib/aspera/api/node.rb +81 -27
- data/lib/aspera/assert.rb +1 -1
- data/lib/aspera/cli/formatter.rb +27 -11
- data/lib/aspera/cli/hints.rb +7 -0
- data/lib/aspera/cli/manager.rb +51 -30
- data/lib/aspera/cli/plugins/aoc.rb +152 -123
- data/lib/aspera/cli/plugins/base.rb +16 -19
- data/lib/aspera/cli/plugins/config.rb +6 -44
- data/lib/aspera/cli/plugins/faspex.rb +4 -4
- data/lib/aspera/cli/plugins/faspex5.rb +92 -83
- data/lib/aspera/cli/plugins/node.rb +10 -15
- data/lib/aspera/cli/plugins/oauth.rb +26 -25
- data/lib/aspera/cli/plugins/preview.rb +3 -3
- data/lib/aspera/cli/plugins/shares.rb +15 -7
- data/lib/aspera/cli/special_values.rb +1 -0
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +2 -1
- data/lib/aspera/colors.rb +7 -0
- data/lib/aspera/faspex_gw.rb +5 -5
- data/lib/aspera/faspex_postproc.rb +4 -4
- data/lib/aspera/log.rb +12 -11
- data/lib/aspera/markdown.rb +5 -0
- data/lib/aspera/node_simulator.rb +1 -1
- data/lib/aspera/oauth/base.rb +1 -1
- data/lib/aspera/oauth/url_json.rb +2 -2
- data/lib/aspera/rest.rb +68 -48
- data/lib/aspera/rest_error_analyzer.rb +38 -36
- data/lib/aspera/rest_errors_aspera.rb +19 -18
- data/lib/aspera/transfer/spec.rb +1 -0
- data.tar.gz.sig +0 -0
- metadata +2 -2
- metadata.gz.sig +0 -0
data/lib/aspera/api/node.rb
CHANGED
|
@@ -6,16 +6,16 @@ require 'aspera/oauth'
|
|
|
6
6
|
require 'aspera/log'
|
|
7
7
|
require 'aspera/assert'
|
|
8
8
|
require 'aspera/environment'
|
|
9
|
-
require 'zlib'
|
|
10
9
|
require 'base64'
|
|
11
10
|
require 'openssl'
|
|
12
11
|
require 'pathname'
|
|
12
|
+
require 'zlib'
|
|
13
13
|
require 'net/ssh/buffer'
|
|
14
14
|
|
|
15
15
|
module Aspera
|
|
16
16
|
module Api
|
|
17
17
|
# Provides additional functions using node API with gen4 extensions (access keys)
|
|
18
|
-
class Node <
|
|
18
|
+
class Node < Rest
|
|
19
19
|
# Format of node scope : node.<access key>:<scope>
|
|
20
20
|
module Scope
|
|
21
21
|
# Node sub-scopes
|
|
@@ -42,9 +42,10 @@ module Aspera
|
|
|
42
42
|
ACCESS_LEVELS = %w[delete list mkdir preview read rename write].freeze
|
|
43
43
|
# Special HTTP Headers
|
|
44
44
|
HEADER_X_ASPERA_ACCESS_KEY = 'X-Aspera-AccessKey'
|
|
45
|
-
HEADER_X_TOTAL_COUNT = 'X-Total-Count'
|
|
46
45
|
HEADER_X_CACHE_CONTROL = 'X-Aspera-Cache-Control'
|
|
46
|
+
HEADER_X_TOTAL_COUNT = 'X-Total-Count'
|
|
47
47
|
HEADER_X_NEXT_ITER_TOKEN = 'X-Aspera-Next-Iteration-Token'
|
|
48
|
+
HEADER_ACCEPT_VERSION = 'Accept-Version'
|
|
48
49
|
# / in cloud
|
|
49
50
|
PATH_SEPARATOR = '/'
|
|
50
51
|
|
|
@@ -52,22 +53,36 @@ module Aspera
|
|
|
52
53
|
OAuth::Factory.instance.register_decoder(lambda{ |token| Node.decode_bearer_token(token)})
|
|
53
54
|
|
|
54
55
|
# Class instance variable, access with accessors on class
|
|
55
|
-
@
|
|
56
|
-
@use_node_cache = true
|
|
57
|
-
|
|
58
|
-
class << self
|
|
56
|
+
@api_options = {
|
|
59
57
|
# Set to false to read transfer parameters from download_setup
|
|
60
|
-
|
|
58
|
+
standard_ports: true,
|
|
61
59
|
# Set to false to bypass cache in redis
|
|
62
|
-
|
|
60
|
+
cache: true,
|
|
61
|
+
accept_v4: true
|
|
62
|
+
}
|
|
63
|
+
OPTIONS = @api_options.keys.freeze
|
|
64
|
+
|
|
65
|
+
class << self
|
|
66
|
+
attr_reader :api_options
|
|
63
67
|
attr_reader :use_dynamic_key
|
|
64
68
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
def api_options=(h)
|
|
70
|
+
Aspera.assert_type(h, Hash)
|
|
71
|
+
h.each do |k, v|
|
|
72
|
+
Aspera.assert(@api_options.key?(k.to_sym)){"unknown api option: #{k} (#{OPTIONS.join(', ')})"}
|
|
73
|
+
Aspera.assert_type(v, TrueClass, FalseClass){"api options value for #{k} should be boolean"}
|
|
74
|
+
@api_options[k.to_sym] = v
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Adds cache control header for node API /files/:id
|
|
79
|
+
# as globally specified to read request
|
|
80
|
+
# Use like this: read(..., headers: add_cache_control)
|
|
81
|
+
# @param headers [Hash] optional initial headers to add to
|
|
82
|
+
# @return [Hash] headers with cache control header added if needed
|
|
83
|
+
def add_cache_control(headers = {})
|
|
84
|
+
headers[HEADER_X_CACHE_CONTROL] = 'no-cache' unless api_options[:cache]
|
|
85
|
+
headers
|
|
71
86
|
end
|
|
72
87
|
|
|
73
88
|
# Set private key to be used
|
|
@@ -180,8 +195,8 @@ module Aspera
|
|
|
180
195
|
Aspera.assert(!access_key.nil?)
|
|
181
196
|
end
|
|
182
197
|
return {
|
|
183
|
-
|
|
184
|
-
'Authorization'
|
|
198
|
+
HEADER_X_ASPERA_ACCESS_KEY => access_key,
|
|
199
|
+
'Authorization' => bearer_auth
|
|
185
200
|
}
|
|
186
201
|
end
|
|
187
202
|
end
|
|
@@ -248,6 +263,50 @@ module Aspera
|
|
|
248
263
|
return false
|
|
249
264
|
end
|
|
250
265
|
|
|
266
|
+
# Read folder content, with pagination management for gen4, not recursive
|
|
267
|
+
# if `Accept-Version: 4.0` is not specified:
|
|
268
|
+
# if `page` and `per_page` are not specified, then all entries are returned.
|
|
269
|
+
# if either `page` or `per_page` is specified, then both are required, else 400
|
|
270
|
+
# if `Accept-Version: 4.0` is specified:
|
|
271
|
+
# those queries are not available: page (not mentioned), sort, min_size, max_size, min_modified_time, max_modified_time, target_id, target_node_id, files_prefetch_count, page, name_iglob : either ignored or result in API error 400.
|
|
272
|
+
# query include is accepted, but seems to do nothing as access_levels and recursive_counts are already included in results.
|
|
273
|
+
# query `iteration_token` is accepted and allows to get paginated results, with `X-Aspera-Next-Iteration-Token` header in response to get next page token. `X-Aspera-Total-Count` header gives total count of entries.
|
|
274
|
+
def read_folder_content(file_id, query = nil, exception: true, path: nil)
|
|
275
|
+
folder_items = []
|
|
276
|
+
begin
|
|
277
|
+
query ||= {}
|
|
278
|
+
headers = self.class.add_cache_control
|
|
279
|
+
use_v4 = self.class.api_options[:accept_v4]
|
|
280
|
+
return read("files/#{file_id}/files", query, headers: headers) unless use_v4 || query.key?('page') || query.key?('per_page')
|
|
281
|
+
if use_v4
|
|
282
|
+
headers[HEADER_ACCEPT_VERSION] = '4.0'
|
|
283
|
+
query['per_page'] = 1000 unless query.key?('per_page')
|
|
284
|
+
elsif query.key?('per_page') && !query.key?('page')
|
|
285
|
+
query['page'] = 0
|
|
286
|
+
end
|
|
287
|
+
loop do
|
|
288
|
+
RestParameters.instance.spinner_cb.call(folder_items.count)
|
|
289
|
+
data, http = read("files/#{file_id}/files", query, headers: headers, ret: :both)
|
|
290
|
+
folder_items.concat(data)
|
|
291
|
+
if use_v4
|
|
292
|
+
iteration_token = http[HEADER_X_NEXT_ITER_TOKEN]
|
|
293
|
+
break if iteration_token.nil? || iteration_token.empty?
|
|
294
|
+
query['iteration_token'] = iteration_token
|
|
295
|
+
else
|
|
296
|
+
break if data['item_count'].eql?(0)
|
|
297
|
+
query['offset'] += data['item_count']
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
rescue StandardError => e
|
|
301
|
+
raise e if exception
|
|
302
|
+
Log.log.warn{"#{path}: #{e.class} #{e.message}"}
|
|
303
|
+
Log.log.debug{(['Backtrace:'] + e.backtrace).join("\n")}
|
|
304
|
+
ensure
|
|
305
|
+
RestParameters.instance.spinner_cb.call(folder_items.count, action: :success)
|
|
306
|
+
end
|
|
307
|
+
folder_items
|
|
308
|
+
end
|
|
309
|
+
|
|
251
310
|
# Recursively browse in a folder (with non-recursive method)
|
|
252
311
|
# Entries of folders are processed if the processing method returns true
|
|
253
312
|
# Links are processed on the respective node
|
|
@@ -267,14 +326,7 @@ module Aspera
|
|
|
267
326
|
current_item = folders_to_explore.shift
|
|
268
327
|
Log.log.debug{"Exploring #{current_item[:path]}".bg_green}
|
|
269
328
|
# Get folder content
|
|
270
|
-
folder_contents =
|
|
271
|
-
begin
|
|
272
|
-
# TODO: use header
|
|
273
|
-
read("files/#{current_item[:id]}/files", query, **self.class.cache_control)
|
|
274
|
-
rescue StandardError => e
|
|
275
|
-
Log.log.warn{"#{current_item[:path]}: #{e.class} #{e.message}"}
|
|
276
|
-
[]
|
|
277
|
-
end
|
|
329
|
+
folder_contents = read_folder_content(current_item[:id], query, exception: false, path: current_item[:path])
|
|
278
330
|
Log.dump(:folder_contents, folder_contents)
|
|
279
331
|
folder_contents.each do |entry|
|
|
280
332
|
if entry.key?('error')
|
|
@@ -308,7 +360,9 @@ module Aspera
|
|
|
308
360
|
# @param top_file_id [String] id initial file id
|
|
309
361
|
# @param path [String] file or folder path (end with "/" is like setting process_last_link)
|
|
310
362
|
# @param process_last_link [Boolean] if true, follow the last link
|
|
311
|
-
# @return [Hash]
|
|
363
|
+
# @return [Hash] Result data
|
|
364
|
+
# @option return [Rest] :api REST client instance
|
|
365
|
+
# @option return [String] :file_id File identifier
|
|
312
366
|
def resolve_api_fid(top_file_id, path, process_last_link = false)
|
|
313
367
|
Aspera.assert_type(top_file_id, String)
|
|
314
368
|
Aspera.assert_type(path, String)
|
|
@@ -443,7 +497,7 @@ module Aspera
|
|
|
443
497
|
# Add application specific tags (AoC)
|
|
444
498
|
@app_info[:api].add_ts_tags(transfer_spec: transfer_spec, app_info: @app_info) unless @app_info.nil?
|
|
445
499
|
# Add remote host info
|
|
446
|
-
if self.class.
|
|
500
|
+
if self.class.api_options[:standard_ports]
|
|
447
501
|
# Get default TCP/UDP ports and transfer user
|
|
448
502
|
transfer_spec.merge!(Transfer::Spec::AK_TSPEC_BASE)
|
|
449
503
|
# By default: same address as node API
|
data/lib/aspera/assert.rb
CHANGED
|
@@ -93,7 +93,7 @@ module Aspera
|
|
|
93
93
|
# The value is not one of the expected values
|
|
94
94
|
# @param value [Object] The wrong value
|
|
95
95
|
# @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log
|
|
96
|
-
# @param block
|
|
96
|
+
# @param &block [Proc] Additional description in front of message
|
|
97
97
|
def error_unexpected_value(value, type: InternalError)
|
|
98
98
|
report_error(type, "#{"#{yield}: " if block_given?}unexpected value: #{value.inspect}")
|
|
99
99
|
end
|
data/lib/aspera/cli/formatter.rb
CHANGED
|
@@ -128,20 +128,34 @@ module Aspera
|
|
|
128
128
|
@spinner = nil
|
|
129
129
|
end
|
|
130
130
|
|
|
131
|
-
|
|
132
|
-
def long_operation_running(title = '')
|
|
131
|
+
def long_operation(title = nil, action: :spin)
|
|
133
132
|
return unless Environment.terminal?
|
|
134
|
-
if
|
|
133
|
+
return if %i[error data].include?(@options[:display])
|
|
134
|
+
|
|
135
|
+
# Handle the "delayed start" state
|
|
136
|
+
return @spinner = :starting if action == :spin && @spinner.nil?
|
|
137
|
+
|
|
138
|
+
# Cleanup if we try to stop a spinner that never actually started
|
|
139
|
+
@spinner = nil if action != :spin && @spinner == :starting
|
|
140
|
+
return if @spinner.nil?
|
|
141
|
+
|
|
142
|
+
# Initialize the real TTY object if it's currently just the :starting symbol
|
|
143
|
+
if @spinner == :starting
|
|
135
144
|
@spinner = TTY::Spinner.new('[:spinner] :title', format: :classic)
|
|
145
|
+
@spinner.update(title: '')
|
|
136
146
|
@spinner.start
|
|
137
147
|
end
|
|
138
|
-
@spinner.update(title: title)
|
|
139
|
-
@spinner.spin
|
|
140
|
-
end
|
|
141
148
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
149
|
+
@spinner.update(title: title) if title
|
|
150
|
+
|
|
151
|
+
case action
|
|
152
|
+
when :spin
|
|
153
|
+
@spinner.spin
|
|
154
|
+
when :success, :fail
|
|
155
|
+
action == :success ? @spinner.success : @spinner.error
|
|
156
|
+
@spinner.stop
|
|
157
|
+
@spinner = nil
|
|
158
|
+
end
|
|
145
159
|
end
|
|
146
160
|
|
|
147
161
|
def declare_options(options)
|
|
@@ -150,9 +164,9 @@ module Aspera
|
|
|
150
164
|
else
|
|
151
165
|
{}
|
|
152
166
|
end
|
|
167
|
+
options.declare(:display, 'Output only some information', allowed: DISPLAY_LEVELS, handler: {o: self, m: :option_handler}, default: :data)
|
|
153
168
|
options.declare(:format, 'Output format', allowed: DISPLAY_FORMATS, handler: {o: self, m: :option_handler}, default: :table)
|
|
154
169
|
options.declare(:output, 'Destination for results', handler: {o: self, m: :option_handler})
|
|
155
|
-
options.declare(:display, 'Output only some information', allowed: DISPLAY_LEVELS, handler: {o: self, m: :option_handler}, default: :info)
|
|
156
170
|
options.declare(
|
|
157
171
|
:fields, "Comma separated list of: fields, or #{SpecialValues::ALL}, or #{SpecialValues::DEF}", handler: {o: self, m: :option_handler},
|
|
158
172
|
allowed: [String, Array, Regexp, Proc],
|
|
@@ -178,6 +192,8 @@ module Aspera
|
|
|
178
192
|
@options[option_symbol] = value
|
|
179
193
|
# special handling of some options
|
|
180
194
|
case option_symbol
|
|
195
|
+
when :format
|
|
196
|
+
@options[:display] = value.eql?(:table) ? :info : :data
|
|
181
197
|
when :output
|
|
182
198
|
$stdout = if value.eql?('-')
|
|
183
199
|
STDOUT # rubocop:disable Style/GlobalStdStream
|
|
@@ -197,7 +213,7 @@ module Aspera
|
|
|
197
213
|
nil
|
|
198
214
|
end
|
|
199
215
|
|
|
200
|
-
#
|
|
216
|
+
# Main output method
|
|
201
217
|
# data: for requested data, not displayed if level==error
|
|
202
218
|
# info: additional info, displayed if level==info
|
|
203
219
|
# error: always displayed on stderr
|
data/lib/aspera/cli/hints.rb
CHANGED
|
@@ -115,6 +115,13 @@ module Aspera
|
|
|
115
115
|
remediation: [
|
|
116
116
|
'Check your public key in your AoC user profile.'
|
|
117
117
|
]
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
exception: Aspera::RestCallError,
|
|
121
|
+
match: /Please configure ACLs for this URI/,
|
|
122
|
+
remediation: [
|
|
123
|
+
'server must have: asnodeadmin -mu <node user> --acl-add=internal --internal'
|
|
124
|
+
]
|
|
118
125
|
}
|
|
119
126
|
]
|
|
120
127
|
private_constant :ERROR_HINTS
|
data/lib/aspera/cli/manager.rb
CHANGED
|
@@ -12,6 +12,37 @@ require 'optparse'
|
|
|
12
12
|
|
|
13
13
|
module Aspera
|
|
14
14
|
module Cli
|
|
15
|
+
module BoolValue
|
|
16
|
+
# boolean options are set to true/false from the following values
|
|
17
|
+
YES_SYM = :yes
|
|
18
|
+
NO_SYM = :no
|
|
19
|
+
FALSE_VALUES = [NO_SYM, false].freeze
|
|
20
|
+
TRUE_VALUES = [YES_SYM, true].freeze
|
|
21
|
+
private_constant :YES_SYM, :NO_SYM, :FALSE_VALUES, :TRUE_VALUES
|
|
22
|
+
# Boolean values
|
|
23
|
+
# @return [Array<true, false, :yes, :no>]
|
|
24
|
+
ALL = (TRUE_VALUES + FALSE_VALUES).freeze
|
|
25
|
+
TYPES = [FalseClass, TrueClass].freeze
|
|
26
|
+
SYMBOLS = [NO_SYM, YES_SYM].freeze
|
|
27
|
+
# @return `true` if value is a value for `true` in ALL
|
|
28
|
+
def true?(enum)
|
|
29
|
+
Aspera.assert_values(enum, ALL){'boolean'}
|
|
30
|
+
return TRUE_VALUES.include?(enum)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return [:yes, :no]
|
|
34
|
+
def to_sym(enum)
|
|
35
|
+
Aspera.assert_values(enum, ALL){'boolean'}
|
|
36
|
+
return TRUE_VALUES.include?(enum) ? YES_SYM : NO_SYM
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @return `true` if value is a value for `true` or `false` in ALL
|
|
40
|
+
def symbol?(sym)
|
|
41
|
+
return ALL.include?(sym)
|
|
42
|
+
end
|
|
43
|
+
module_function :true?, :to_sym, :symbol?
|
|
44
|
+
end
|
|
45
|
+
|
|
15
46
|
# Constants to be used as parameter `allowed:` for `OptionValue`
|
|
16
47
|
module Allowed
|
|
17
48
|
# This option can be set to a single string or array, multiple times, and gives Array of String
|
|
@@ -20,7 +51,7 @@ module Aspera
|
|
|
20
51
|
TYPES_SYMBOL_ARRAY = [Array, Symbol].freeze
|
|
21
52
|
# Value will be coerced to int
|
|
22
53
|
TYPES_INTEGER = [Integer].freeze
|
|
23
|
-
TYPES_BOOLEAN =
|
|
54
|
+
TYPES_BOOLEAN = BoolValue::TYPES
|
|
24
55
|
# no value at all, it's a switch
|
|
25
56
|
TYPES_NONE = [].freeze
|
|
26
57
|
TYPES_ENUM = [Symbol].freeze
|
|
@@ -74,7 +105,7 @@ module Aspera
|
|
|
74
105
|
@values = allowed[Allowed::TYPES_SYMBOL_ARRAY.length..-1]
|
|
75
106
|
elsif allowed.all?(Class)
|
|
76
107
|
@types = allowed
|
|
77
|
-
@values =
|
|
108
|
+
@values = BoolValue::ALL if allowed.eql?(Allowed::TYPES_BOOLEAN)
|
|
78
109
|
# Default value for array
|
|
79
110
|
@object ||= [] if @types.first.eql?(Array) && !@types.include?(NilClass)
|
|
80
111
|
@object ||= {} if @types.first.eql?(Hash) && !@types.include?(NilClass)
|
|
@@ -110,7 +141,7 @@ module Aspera
|
|
|
110
141
|
Aspera.assert(!@deprecation, type: warn){"Option #{@option} is deprecated: #{@deprecation}"}
|
|
111
142
|
new_value = ExtendedValue.instance.evaluate(value, context: "option: #{@option}", allowed: @types)
|
|
112
143
|
Log.log.trace1{"#{where}: #{@option} <- (#{new_value.class})#{new_value}"}
|
|
113
|
-
new_value =
|
|
144
|
+
new_value = BoolValue.true?(new_value) if @types.eql?(Allowed::TYPES_BOOLEAN)
|
|
114
145
|
new_value = Integer(new_value) if @types.eql?(Allowed::TYPES_INTEGER)
|
|
115
146
|
new_value = [new_value] if @types.eql?(Allowed::TYPES_STRING_ARRAY) && new_value.is_a?(String)
|
|
116
147
|
# Setting a Hash to null set an empty hash
|
|
@@ -142,20 +173,7 @@ module Aspera
|
|
|
142
173
|
# arguments options start with '-', others are commands
|
|
143
174
|
# resolves on extended value syntax
|
|
144
175
|
class Manager
|
|
145
|
-
BOOLEAN_SIMPLE = %i[no yes].freeze
|
|
146
176
|
class << self
|
|
147
|
-
# @return `true` if value is a value for `true` in BOOLEAN_VALUES
|
|
148
|
-
def enum_to_bool(enum)
|
|
149
|
-
Aspera.assert_values(enum, BOOLEAN_VALUES){'boolean'}
|
|
150
|
-
return TRUE_VALUES.include?(enum)
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
# @return :yes ot :no
|
|
154
|
-
def enum_to_yes_no(enum)
|
|
155
|
-
Aspera.assert_values(enum, BOOLEAN_VALUES){'boolean'}
|
|
156
|
-
return TRUE_VALUES.include?(enum) ? BOOL_YES : BOOL_NO
|
|
157
|
-
end
|
|
158
|
-
|
|
159
177
|
# Find shortened string value in allowed symbol list
|
|
160
178
|
def get_from_list(short_value, descr, allowed_values)
|
|
161
179
|
Aspera.assert_type(short_value, String)
|
|
@@ -165,7 +183,7 @@ module Aspera
|
|
|
165
183
|
matching = allowed_values.select{ |i| i.to_s.start_with?(short_value)}
|
|
166
184
|
Aspera.assert(!matching.empty?, multi_choice_assert_msg("unknown value for #{descr}: #{short_value}", allowed_values), type: BadArgument)
|
|
167
185
|
Aspera.assert(matching.length.eql?(1), multi_choice_assert_msg("ambiguous shortcut for #{descr}: #{short_value}", matching), type: BadArgument)
|
|
168
|
-
return
|
|
186
|
+
return BoolValue.true?(matching.first) if allowed_values.eql?(BoolValue::ALL)
|
|
169
187
|
return matching.first
|
|
170
188
|
end
|
|
171
189
|
|
|
@@ -279,11 +297,11 @@ module Aspera
|
|
|
279
297
|
case option_attrs.types
|
|
280
298
|
when Allowed::TYPES_ENUM, Allowed::TYPES_BOOLEAN
|
|
281
299
|
# This option value must be a symbol (or array of symbols)
|
|
282
|
-
set_option(option_symbol,
|
|
300
|
+
set_option(option_symbol, BoolValue.true?(default), where: 'default') if option_attrs.values.eql?(BoolValue::ALL) && !default.nil?
|
|
283
301
|
value = get_option(option_symbol)
|
|
284
302
|
help_values =
|
|
285
303
|
if option_attrs.types.eql?(Allowed::TYPES_BOOLEAN)
|
|
286
|
-
highlight_current_in_list(
|
|
304
|
+
highlight_current_in_list(BoolValue::SYMBOLS, BoolValue.to_sym(value))
|
|
287
305
|
else
|
|
288
306
|
highlight_current_in_list(option_attrs.values, value)
|
|
289
307
|
end
|
|
@@ -312,7 +330,7 @@ module Aspera
|
|
|
312
330
|
|
|
313
331
|
# @param descr [String] description for help
|
|
314
332
|
# @param mandatory [Boolean] if true, raise error if option not set
|
|
315
|
-
# @param multiple [Boolean] if true, return remaining arguments (Array)
|
|
333
|
+
# @param multiple [Boolean] if true, return remaining arguments (Array) unil END
|
|
316
334
|
# @param accept_list [Array, NilClass] list of allowed values (Symbol)
|
|
317
335
|
# @param validation [Class, Array, NilClass] Accepted value type(s) or list of Symbols
|
|
318
336
|
# @param aliases [Hash] map of aliases: key = alias, value = real value
|
|
@@ -327,10 +345,19 @@ module Aspera
|
|
|
327
345
|
descr = "#{descr} (#{validation.join(', ')})" unless validation.nil? || validation.eql?(Allowed::TYPES_STRING)
|
|
328
346
|
result =
|
|
329
347
|
if !@unprocessed_cmd_line_arguments.empty?
|
|
330
|
-
|
|
331
|
-
|
|
348
|
+
if multiple
|
|
349
|
+
index = @unprocessed_cmd_line_arguments.index(SpecialValues::EOA)
|
|
350
|
+
if index.nil?
|
|
351
|
+
values = @unprocessed_cmd_line_arguments.shift(@unprocessed_cmd_line_arguments.length)
|
|
352
|
+
else
|
|
353
|
+
values = @unprocessed_cmd_line_arguments.shift(index)
|
|
354
|
+
@unprocessed_cmd_line_arguments.shift # remove EOA
|
|
355
|
+
end
|
|
356
|
+
else
|
|
357
|
+
values = [@unprocessed_cmd_line_arguments.shift]
|
|
358
|
+
end
|
|
332
359
|
values = values.map{ |v| ExtendedValue.instance.evaluate(v, context: "argument: #{descr}", allowed: validation)}
|
|
333
|
-
#
|
|
360
|
+
# If expecting list and only one arg of type array : it is the list
|
|
334
361
|
values = values.first if multiple && values.length.eql?(1) && values.first.is_a?(Array)
|
|
335
362
|
if accept_list
|
|
336
363
|
allowed_values = [].concat(accept_list)
|
|
@@ -636,12 +663,6 @@ module Aspera
|
|
|
636
663
|
unprocessed_options.delete(k)
|
|
637
664
|
end
|
|
638
665
|
end
|
|
639
|
-
# boolean options are set to true/false from the following values
|
|
640
|
-
BOOL_YES = BOOLEAN_SIMPLE.last
|
|
641
|
-
BOOL_NO = BOOLEAN_SIMPLE.first
|
|
642
|
-
FALSE_VALUES = [BOOL_NO, false].freeze
|
|
643
|
-
TRUE_VALUES = [BOOL_YES, true].freeze
|
|
644
|
-
BOOLEAN_VALUES = (TRUE_VALUES + FALSE_VALUES).freeze
|
|
645
666
|
|
|
646
667
|
# Option name separator on command line, e.g. in --option-blah, third "-"
|
|
647
668
|
OPTION_SEP_LINE = '-'
|
|
@@ -655,7 +676,7 @@ module Aspera
|
|
|
655
676
|
OPTIONS_STOP = '--'
|
|
656
677
|
SOURCE_USER = 'cmdline' # cspell:disable-line
|
|
657
678
|
|
|
658
|
-
private_constant :
|
|
679
|
+
private_constant :OPTION_SEP_LINE, :OPTION_SEP_SYMBOL, :OPTION_VALUE_SEPARATOR, :OPTION_PREFIX, :OPTIONS_STOP, :SOURCE_USER
|
|
659
680
|
end
|
|
660
681
|
end
|
|
661
682
|
end
|