rasn1 0.16.2 → 0.17.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.
data/lib/rasn1/model.rb CHANGED
@@ -6,6 +6,8 @@
6
6
  # Copyright (C) 2024 LemonTree55 <lenontree@proton.me>
7
7
  # This program is published under MIT license.
8
8
 
9
+ require_relative 'model/accel'
10
+
9
11
  module RASN1
10
12
  # @abstract
11
13
  # {Model} class is a base class to define ASN.1 models.
@@ -16,7 +18,8 @@ module RASN1
16
18
  # room [0] IMPLICIT INTEGER OPTIONAL,
17
19
  # house [1] EXPLICIT INTEGER DEFAULT 0
18
20
  # }
19
- # you may create your model like this:
21
+ #
22
+ # You may create your model like this:
20
23
  # class Record < RASN1::Model
21
24
  # sequence(:record,
22
25
  # content: [integer(:id),
@@ -24,6 +27,15 @@ module RASN1
24
27
  # integer(:house, explicit: 1, default: 0)])
25
28
  # end
26
29
  #
30
+ # You may also use block style definition:
31
+ # class Record < RASN1::Model
32
+ # sequence :record do
33
+ # integer :id
34
+ # integer :room, implicit: 0, optional: true
35
+ # integer :house, explicit: 1, default: 0
36
+ # end
37
+ # end
38
+ #
27
39
  # In a model, each element must have a unique name.
28
40
  #
29
41
  # === Parse a DER-encoded string
@@ -47,10 +59,12 @@ module RASN1
47
59
  # == Create a more complex model
48
60
  # Models may be nested. For example:
49
61
  # class Record2 < RASN1::Model
50
- # sequence(:record2,
51
- # content: [boolean(:rented, default: false),
52
- # model(:a_record, Record)])
62
+ # sequence :record2 do
63
+ # boolean :rented, default: false)
64
+ # model :a_record, Record
65
+ # end
53
66
  # end
67
+ #
54
68
  # Set values like this:
55
69
  # record2 = Record2.new
56
70
  # record2[:rented] = true
@@ -67,26 +81,37 @@ module RASN1
67
81
  # this method.
68
82
  # @author Sylvain Daubert
69
83
  # @author adfoster-r7 ModelValidationError, track source location for dynamic class methods
84
+ # @since 0.17 block notation
70
85
  class Model # rubocop:disable Metrics/ClassLength
71
86
  # @private Base Element
72
- BaseElem = Struct.new(:name, :proc, :content) do
73
- # @param [String,Symbol] name
74
- # @param [Proc] proc
75
- # @param [Array,nil] content
76
- def initialize(name, proc, content)
87
+ class BaseElem
88
+ attr_accessor :name
89
+ attr_reader :proc, :content, :block
90
+
91
+ # @param name [String,Symbol]
92
+ # @param proc [Proc]
93
+ # @param content [Array,nil]
94
+ def initialize(name, proc, content, &block)
95
+ @name = name
96
+ @proc = proc
97
+ @content = content
98
+ @block = block
77
99
  check_duplicates(content.map(&:name) + [name]) unless content.nil?
78
- super
79
100
  end
80
101
 
81
102
  private
82
103
 
83
- # @return [Array<String>] The duplicate names found in the array
104
+ # @param names [Array<String, Symbol>]
105
+ # @return [Array<String,Symbol>] The duplicate names found in the array
84
106
  def find_all_duplicate_names(names)
85
107
  names.group_by { |name| name }
86
108
  .select { |_name, values| values.length > 1 }
87
109
  .keys
88
110
  end
89
111
 
112
+ # @param names [Array<String, Symbol>]
113
+ # @return [void]
114
+ # @raise [ModelValidationError] duplicate names in model definition
90
115
  def check_duplicates(names)
91
116
  duplicates = find_all_duplicate_names(names)
92
117
  raise ModelValidationError, "Duplicate name #{duplicates.first} found" if duplicates.any?
@@ -107,184 +132,42 @@ module RASN1
107
132
  # @private Sequence types
108
133
  SEQUENCE_TYPES = [Types::Sequence, Types::SequenceOf, Types::Set, Types::SetOf].freeze
109
134
 
110
- # Define helper methods to define models
111
- module Accel
112
- # @return [Hash]
113
- attr_reader :options
114
-
115
- # Use another model in this model
116
- # @param [String,Symbol] name
117
- # @param [Class] model_klass
118
- # @return [Elem]
119
- def model(name, model_klass)
120
- @root = ModelElem.new(name, model_klass)
121
- end
122
-
123
- # Use a {Wrapper} around a {Types::Base} or a {Model} object
124
- # @param [Types::Base,Model] element
125
- # @param [Hash] options
126
- # @return [WrapElem]
127
- # @since 0.12
128
- def wrapper(element, options={})
129
- @root = WrapElem.new(element, options)
130
- end
131
-
132
- # Update options of root element.
133
- # May be used when subclassing.
134
- # class Model1 < RASN1::Model
135
- # sequence :seq, implicit: 0,
136
- # content: [bool(:bool), integer(:int)]
137
- # end
138
- #
139
- # # same as Model1 but with implicit tag set to 1
140
- # class Model2 < Model1
141
- # root_options implicit: 1
142
- # end
143
- # @param [Hash] options
144
- # @return [void]
145
- # @since 0.12.0 may change name through +:name+
146
- def root_options(options)
147
- @options = options
148
- return unless options.key?(:name)
149
-
150
- @root = @root.dup
151
- @root.name = options[:name]
152
- end
153
-
154
- # On inheritance, create +@root+ class variable
155
- # @param [Class] klass
156
- # @return [void]
157
- def inherited(klass)
158
- super
159
- root = @root
160
- klass.class_eval { @root = root }
161
- end
162
-
163
- # @private
164
- # @param [String,Symbol] accel_name
165
- # @param [Class] klass
166
- # @since 0.11.0
167
- # @since 0.12.0 track source location on error (adfoster-r7)
168
- def define_type_accel_base(accel_name, klass)
169
- singleton_class.class_eval <<-EVAL, __FILE__, __LINE__ + 1
170
- def #{accel_name}(name, options={}) # def sequence(name, type, options)
171
- options[:name] = name
172
- proc = proc do |opts|
173
- #{klass}.new(options.merge(opts)) # Sequence.new(options.merge(opts))
174
- end
175
- @root = BaseElem.new(name, proc, options[:content])
176
- end
177
- EVAL
178
- end
179
-
180
- # @private
181
- # @param [String,Symbol] accel_name
182
- # @param [Class] klass
183
- # @since 0.11.0
184
- # @since 0.12.0 track source location on error (adfoster-r7)
185
- def define_type_accel_of(accel_name, klass)
186
- singleton_class.class_eval <<-EVAL, __FILE__, __LINE__ + 1
187
- def #{accel_name}_of(name, type, options={}) # def sequence_of(name, type, options)
188
- options[:name] = name
189
- proc = proc do |opts|
190
- #{klass}.new(type, options.merge(opts)) # SequenceOf.new(type, options.merge(opts))
191
- end
192
- @root = BaseElem.new(name, proc, nil)
193
- end
194
- EVAL
195
- end
196
-
197
- # Define an accelarator to access a type in a model definition
198
- # @param [String] accel_name
199
- # @param [Class] klass class to instanciate
200
- # @since 0.11.0
201
- # @since 0.12.0 track source location on error (adfoster-r7)
202
- def define_type_accel(accel_name, klass)
203
- if klass < Types::SequenceOf
204
- define_type_accel_of(accel_name, klass)
205
- else
206
- define_type_accel_base(accel_name, klass)
207
- end
208
- end
209
-
210
- # @param [Symbol,String] name name of object in model
211
- # @param [Hash] options
212
- # @return [Elem]
213
- # @note This method is named +objectid+ and not +object_id+ to not override
214
- # +Object#object_id+.
215
- # @see Types::ObjectId#initialize
216
- def objectid(name, options={})
217
- options[:name] = name
218
- proc = proc { |opts| Types::ObjectId.new(options.merge(opts)) }
219
- @root = BaseElem.new(name, proc, nil)
220
- end
221
-
222
- # @param [Symbol,String] name name of object in model
223
- # @param [Hash] options
224
- # @return [Elem]
225
- # @see Types::Any#initialize
226
- def any(name, options={})
227
- options[:name] = name
228
- proc = proc { |opts| Types::Any.new(options.merge(opts)) }
229
- @root = BaseElem.new(name, proc, nil)
230
- end
231
-
232
- # Give type name (aka class name)
233
- # @return [String]
234
- def type
235
- return @type if defined? @type
236
-
237
- @type = self.to_s.gsub(/.*::/, '')
238
- end
239
-
240
- # Parse a DER/BER encoded string
241
- # @param [String] str
242
- # @param [Boolean] ber accept BER encoding or not
243
- # @return [Model]
244
- # @raise [ASN1Error] error on parsing
245
- def parse(str, ber: false)
246
- model = new
247
- model.parse!(str, ber: ber)
248
- model
249
- end
250
- end
251
-
252
135
  extend Accel
253
136
 
254
137
  # @!method sequence(name, options)
255
138
  # @!scope class
256
- # @param [Symbol,String] name name of object in model
257
- # @param [Hash] options
139
+ # @param name [Symbol,String] name of object in model
140
+ # @param options [Hash]
258
141
  # @return [Elem]
259
142
  # @see Types::Sequence#initialize
260
143
  # @!method set(name, options)
261
144
  # @!scope class
262
- # @param [Symbol,String] name name of object in model
263
- # @param [Hash] options
145
+ # @param name [Symbol,String] name of object in model
146
+ # @param options [Hash]
264
147
  # @return [Elem]
265
148
  # @see Types::Set#initialize
266
149
  # @!method choice(name, options)
267
150
  # @!scope class
268
- # @param [Symbol,String] name name of object in model
269
- # @param [Hash] options
151
+ # @param name [Symbol,String] name of object in model
152
+ # @param options [Hash]
270
153
  # @return [Elem]
271
154
  # @see Types::Choice#initialize
272
155
  %w[sequence set choice].each do |type|
273
- self.define_type_accel_base(type, Types.const_get(type.capitalize))
156
+ self.define_type_accel_container(type, Types.const_get(type.capitalize))
274
157
  end
275
158
 
276
159
  # @!method sequence_of(name, type, options)
277
160
  # @!scope class
278
- # @param [Symbol,String] name name of object in model
279
- # @param [Model, Types::Base] type type for SEQUENCE OF
280
- # @param [Hash] options
161
+ # @param name [Symbol,String] name of object in model
162
+ # @param type [Model, Types::Base] type for SEQUENCE OF
163
+ # @param options [Hash]
281
164
  # @return [Elem]
282
165
  # @see Types::SequenceOf#initialize
283
166
  # @!method set_of(name, type, options)
284
167
  # @!scope class
285
- # @param [Symbol,String] name name of object in model
286
- # @param [Model, Types::Base] type type for SET OF
287
- # @param [Hash] options
168
+ # @param name [Symbol,String] name of object in model
169
+ # @param type [Model, Types::Base] type for SET OF
170
+ # @param options [Hash]
288
171
  # @return [Elem]
289
172
  # @see Types::SetOf#initialize
290
173
  %w[sequence set].each do |type|
@@ -293,80 +176,80 @@ module RASN1
293
176
 
294
177
  # @!method boolean(name, options)
295
178
  # @!scope class
296
- # @param [Symbol,String] name name of object in model
297
- # @param [Hash] options
179
+ # @param name [Symbol,String] name of object in model
180
+ # @param options [Hash]
298
181
  # @return [Elem]
299
182
  # @see Types::Boolean#initialize
300
183
  # @!method integer(name, options)
301
184
  # @!scope class
302
- # @param [Symbol,String] name name of object in model
303
- # @param [Hash] options
185
+ # @param name [Symbol,String] name of object in model
186
+ # @param options [Hash]
304
187
  # @return [Elem]
305
188
  # @see Types::Integer#initialize
306
189
  # @!method bit_string(name, options)
307
190
  # @!scope class
308
- # @param [Symbol,String] name name of object in model
309
- # @param [Hash] options
191
+ # @param name [Symbol,String] name of object in model
192
+ # @param options [Hash]
310
193
  # @return [Elem]
311
194
  # @see Types::BitString#initialize
312
195
  # @!method bmp_string(name, options)
313
196
  # @!scope class
314
- # @param [Symbol,String] name name of object in model
315
- # @param [Hash] options
197
+ # @param name [Symbol,String] name of object in model
198
+ # @param options [Hash]
316
199
  # @return [Elem]
317
200
  # @see Types::BmpString#initialize
318
201
  # @!method octet_string(name, options)
319
202
  # @!scope class
320
- # @param [Symbol,String] name name of object in model
321
- # @param [Hash] options
203
+ # @param name [Symbol,String] name of object in model
204
+ # @param options [Hash]
322
205
  # @return [Elem]
323
206
  # @see Types::OctetString#initialize
324
207
  # @!method null(name, options)
325
208
  # @!scope class
326
- # @param [Symbol,String] name name of object in model
327
- # @param [Hash] options
209
+ # @param name [Symbol,String] name of object in model
210
+ # @param options [Hash]
328
211
  # @return [Elem]
329
212
  # @see Types::Null#initialize
330
213
  # @!method enumerated(name, options)
331
214
  # @!scope class
332
- # @param [Symbol,String] name name of object in model
333
- # @param [Hash] options
215
+ # @param name [Symbol,String] name of object in model
216
+ # @param options [Hash]
334
217
  # @return [Elem]
335
218
  # @see Types::Enumerated#initialize
336
219
  # @!method universal_string(name, options)
337
220
  # @!scope class
338
- # @param [Symbol,String] name name of object in model
339
- # @param [Hash] options
221
+ # @param name [Symbol,String] name of object in model
222
+ # @param options [Hash]
340
223
  # @return [Elem]
341
224
  # @see Types::UniversalString#initialize
342
225
  # @!method utf8_string(name, options)
343
226
  # @!scope class
344
- # @param [Symbol,String] name name of object in model
345
- # @param [Hash] options
227
+ # @param name [Symbol,String] name of object in model
228
+ # @param options [Hash]
346
229
  # @return [Elem]
347
230
  # @see Types::Utf8String#initialize
348
231
  # @!method numeric_string(name, options)
349
232
  # @!scope class
350
- # @param [Symbol,String] name name of object in model
351
- # @param [Hash] options
233
+ # @param name [Symbol,String] name of object in model
234
+ # @param options [Hash]
352
235
  # @return [Elem]
353
236
  # @see Types::NumericString#initialize
354
237
  # @!method printable_string(name, options)
355
238
  # @!scope class
356
- # @param [Symbol,String] name name of object in model
357
- # @param [Hash] options
239
+ # @param name [Symbol,String] name of object in model
240
+ # @param options [Hash]
358
241
  # @return [Elem]
359
242
  # @see Types::PrintableString#initialize
360
243
  # @!method visible_string(name, options)
361
244
  # @!scope class
362
- # @param [Symbol,String] name name of object in model
363
- # @param [Hash] options
245
+ # @param name [Symbol,String] name of object in model
246
+ # @param options [Hash]
364
247
  # @return [Elem]
365
248
  # @see Types::VisibleString#initialize
366
249
  # @!method ia5_string(name, options)
367
250
  # @!scope class
368
- # @param [Symbol,String] name name of object in model
369
- # @param [Hash] options
251
+ # @param name [Symbol,String] name of object in model
252
+ # @param options [Hash]
370
253
  # @return [Elem]
371
254
  # @see Types::IA5String#initialize
372
255
  Types.primitives.each do |prim|
@@ -380,7 +263,7 @@ module RASN1
380
263
  attr_reader :root
381
264
 
382
265
  # Create a new instance of a {Model}
383
- # @param [Hash] args
266
+ # @param args [Hash]
384
267
  def initialize(args={})
385
268
  @elements = {}
386
269
  generate_root(args)
@@ -390,11 +273,11 @@ module RASN1
390
273
 
391
274
  # @overload [](name)
392
275
  # Access an element of the model by its name
393
- # @param [Symbol] name
276
+ # @param name [Symbol]
394
277
  # @return [Model, Types::Base, Wrapper]
395
278
  # @overload [](idx)
396
279
  # Access an element of root element by its index. Root element must be a {Types::Sequence} or {Types::SequenceOf}.
397
- # @param [Integer] idx
280
+ # @param idx [Integer]
398
281
  # @return [Model, Types::Base, Wrapper]
399
282
  def [](name_or_idx)
400
283
  case name_or_idx
@@ -409,8 +292,8 @@ module RASN1
409
292
  end
410
293
 
411
294
  # Set value of element +name+. Element should be a {Types::Base}.
412
- # @param [String,Symbol] name
413
- # @param [Object] value
295
+ # @param name [String,Symbol]
296
+ # @param value [Object]
414
297
  # @return [Object] value
415
298
  def []=(name, value)
416
299
  # Here, use #[] to force generation for lazy elements
@@ -420,7 +303,8 @@ module RASN1
420
303
  end
421
304
 
422
305
  # clone @elements and initialize @root from this new @element.
423
- def initialize_copy(_other)
306
+ # @return [void]
307
+ def initialize_copy(*)
424
308
  @elements = @elements.clone
425
309
  @root = @elements[@root_name]
426
310
  end
@@ -455,8 +339,8 @@ module RASN1
455
339
  end
456
340
 
457
341
  # Parse a DER/BER encoded string, and modify object in-place.
458
- # @param [String] der
459
- # @param [Boolean] ber accept BER encoding or not
342
+ # @param der [String]
343
+ # @param ber [Boolean] accept BER encoding or not
460
344
  # @return [Integer] number of parsed bytes
461
345
  # @raise [ASN1Error] error on parsing
462
346
  def parse!(der, ber: false)
@@ -464,6 +348,7 @@ module RASN1
464
348
  end
465
349
 
466
350
  # @private
351
+ # @!macro do_parse
467
352
  # @see Types::Base#do_parse
468
353
  def do_parse(der, ber: false)
469
354
  root.do_parse(der, ber: ber)
@@ -474,8 +359,8 @@ module RASN1
474
359
  # @return [Object,nil]
475
360
  # @overload value(name, *args)
476
361
  # Direct access to the value of +name+ (nested) element of model.
477
- # @param [String,Symbol] name
478
- # @param [Array<Integer,String,Symbol>] args more argument to access element. May be
362
+ # @param name [String,Symbol]
363
+ # @param args [Array<Integer,String,Symbol>] more argument to access element. May be
479
364
  # used to access content of a SequenceOf or a SetOf
480
365
  # @return [Object,nil]
481
366
  # @return [Object,nil]
@@ -508,10 +393,11 @@ module RASN1
508
393
  end
509
394
  end
510
395
 
511
- # Return a hash image of model
512
- # @return [Hash]
513
396
  # Delegate some methods to root element
514
- # @param [Symbol] meth
397
+ # @param meth [Symbol]
398
+ # @param args [Array]
399
+ # @param kwargs [Hash]
400
+ # @return [Object]
515
401
  def method_missing(meth, *args, **kwargs)
516
402
  if root.respond_to?(meth)
517
403
  root.send(meth, *args, **kwargs)
@@ -520,18 +406,20 @@ module RASN1
520
406
  end
521
407
  end
522
408
 
409
+ # @param meth [Symbol]
523
410
  # @return [Boolean]
524
411
  def respond_to_missing?(meth, *)
525
412
  root.respond_to?(meth) || super
526
413
  end
527
414
 
415
+ # @param level [Integer]
528
416
  # @return [String]
529
417
  def inspect(level=0)
530
418
  "#{' ' * level}(#{type}) #{root.inspect(-level)}"
531
419
  end
532
420
 
533
421
  # Objects are equal if they have same class AND same DER
534
- # @param [Base] other
422
+ # @param other [Base]
535
423
  # @return [Boolean]
536
424
  def ==(other)
537
425
  (other.class == self.class) && (other.to_der == self.to_der)
@@ -540,7 +428,7 @@ module RASN1
540
428
  protected
541
429
 
542
430
  # Initialize model elements from +args+
543
- # @param [Hash,Array] args
431
+ # @param args [Hash,Array]
544
432
  # @return [void]
545
433
  def lazy_initialize(args)
546
434
  case args
@@ -552,7 +440,7 @@ module RASN1
552
440
  end
553
441
 
554
442
  # Initialize an element from a hash
555
- # @param [Hash] args
443
+ # @param args [Hash]
556
444
  # @return [void]
557
445
  def lazy_initialize_hash(args)
558
446
  args.each do |name, value|
@@ -568,7 +456,7 @@ module RASN1
568
456
  end
569
457
 
570
458
  # Initialize an sequence element from an array
571
- # @param [Array] ary
459
+ # @param ary [Array]
572
460
  # @return [void]
573
461
  def lazy_initialize_array(ary)
574
462
  raise Error, 'Only sequence types may be initialized with an array' unless SEQUENCE_TYPES.any? { |klass| root.is_a?(klass) }
@@ -579,7 +467,7 @@ module RASN1
579
467
  end
580
468
 
581
469
  # Give a (nested) element from its name
582
- # @param [String, Symbol] name
470
+ # @param name [String, Symbol]
583
471
  # @return [Model, Types::Base, nil]
584
472
  def by_name(name)
585
473
  elt = self[name]
@@ -597,9 +485,11 @@ module RASN1
597
485
 
598
486
  private
599
487
 
488
+ # @param args [Hash]
489
+ # @return [void]
600
490
  def generate_root(args)
601
491
  opts = args.slice(:name, :explicit, :implicit, :optional, :class, :default, :constructed, :tag_value)
602
- root = self.class.class_eval { @root }
492
+ root = self.class.root
603
493
  root_options = self.class.options || {}
604
494
  root_options.merge!(opts)
605
495
  @root_name = args[:name] || root.name
@@ -607,6 +497,9 @@ module RASN1
607
497
  @elements[@root_name] = @root
608
498
  end
609
499
 
500
+ # @param elt [BaseElem, ModelElem,WrapElem]
501
+ # @param opts [Hash]
502
+ # @return[Types::Base,Model,Wrapper]
610
503
  def generate_element(elt, opts={})
611
504
  case elt
612
505
  when BaseElem
@@ -619,6 +512,9 @@ module RASN1
619
512
  end
620
513
  end
621
514
 
515
+ # @param elt [WrapElem]
516
+ # @param opts [Hash]
517
+ # @return[Wrapper]
622
518
  def generate_wrapper_element(elt, opts)
623
519
  wrapped = elt.element.is_a?(ModelElem) ? elt.element.klass : generate_element(elt.element)
624
520
  options = elt.options.merge(opts)
@@ -629,6 +525,9 @@ module RASN1
629
525
  wrapper
630
526
  end
631
527
 
528
+ # @param elt [BaseElem]
529
+ # @param opts [Hash]
530
+ # @return[Types::Base]
632
531
  def generate_base_element(elt, opts)
633
532
  element = elt.proc.call(opts)
634
533
  return element if elt.content.nil?
@@ -643,6 +542,8 @@ module RASN1
643
542
  # @author sdaubert
644
543
  # @author lemontree55
645
544
  # @author adfoster-r7
545
+ # @param element [Model, Types::Base, Wrapper,nil]
546
+ # @return [Hash]
646
547
  def private_to_h(element=nil) # rubocop:disable Metrics/CyclomaticComplexity
647
548
  my_element = element || root
648
549
  value = case my_element
@@ -666,6 +567,8 @@ module RASN1
666
567
  end
667
568
  end
668
569
 
570
+ # @param elt [Model]
571
+ # @return [Hash]
669
572
  def model_to_h(elt)
670
573
  hsh = elt.to_h
671
574
  if root.is_a?(Types::Choice)
@@ -675,6 +578,8 @@ module RASN1
675
578
  end
676
579
  end
677
580
 
581
+ # @param elt [Types::SequenceOf]
582
+ # @return [Array<Hash>]
678
583
  def sequence_of_to_h(elt)
679
584
  if elt.of_type < Model
680
585
  elt.value&.map { |el| el.to_h.values.first }
@@ -683,6 +588,8 @@ module RASN1
683
588
  end
684
589
  end
685
590
 
591
+ # @param seq [Types::Sequence]
592
+ # @return [Hash]
686
593
  def sequence_to_h(seq)
687
594
  ary = seq.value&.map do |el|
688
595
  next if el.optional? && el.value.nil?
@@ -699,6 +606,9 @@ module RASN1
699
606
  ary.compact.to_h
700
607
  end
701
608
 
609
+ # @param elt [Types::Choice]
610
+ # @return [Hash]
611
+ # @raise [ChoiceError] no chosen element
702
612
  def choice_to_h(elt)
703
613
  raise ChoiceError.new(elt) if elt.chosen.nil?
704
614
 
@@ -706,10 +616,14 @@ module RASN1
706
616
  { chosen.name => private_to_h(chosen) }
707
617
  end
708
618
 
619
+ # @param key [String,Symbol]
620
+ # @return [Symbol]
709
621
  def unwrap_keyname(key)
710
622
  key.to_s.delete_suffix('_wrapper').to_sym
711
623
  end
712
624
 
625
+ # @param wrap [Wrapper]
626
+ # @return [Hash]
713
627
  def wrapper_to_h(wrap)
714
628
  el = wrap.element
715
629
  case el