lutaml-model 0.6.7 → 0.7.2

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 (148) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-repos-todo.json +7 -0
  3. data/.github/workflows/dependent-repos.json +17 -9
  4. data/.rubocop.yml +1 -1
  5. data/.rubocop_todo.yml +51 -65
  6. data/Gemfile +4 -1
  7. data/README.adoc +5083 -2612
  8. data/RELEASE_NOTES.adoc +346 -0
  9. data/docs/custom_adapters.adoc +144 -0
  10. data/lib/lutaml/model/attribute.rb +101 -16
  11. data/lib/lutaml/model/choice.rb +7 -0
  12. data/lib/lutaml/model/comparable_model.rb +48 -9
  13. data/lib/lutaml/model/config.rb +48 -42
  14. data/lib/lutaml/model/error/collection_count_out_of_range_error.rb +1 -1
  15. data/lib/lutaml/model/error/polymorphic_error.rb +14 -0
  16. data/lib/lutaml/model/error.rb +1 -0
  17. data/lib/lutaml/model/format_registry.rb +41 -0
  18. data/lib/lutaml/model/hash/document.rb +11 -0
  19. data/lib/lutaml/model/hash/mapping.rb +19 -0
  20. data/lib/lutaml/model/hash/mapping_rule.rb +9 -0
  21. data/lib/lutaml/model/hash/standard_adapter.rb +17 -0
  22. data/lib/lutaml/model/hash/transform.rb +8 -0
  23. data/lib/lutaml/model/hash.rb +21 -0
  24. data/lib/lutaml/model/json/document.rb +11 -0
  25. data/lib/lutaml/model/json/mapping.rb +19 -0
  26. data/lib/lutaml/model/json/mapping_rule.rb +9 -0
  27. data/lib/lutaml/model/{json_adapter → json}/multi_json_adapter.rb +4 -5
  28. data/lib/lutaml/model/{json_adapter/standard_json_adapter.rb → json/standard_adapter.rb} +5 -3
  29. data/lib/lutaml/model/json/transform.rb +8 -0
  30. data/lib/lutaml/model/json.rb +21 -0
  31. data/lib/lutaml/model/key_value_document.rb +27 -0
  32. data/lib/lutaml/model/{key_value_mapping.rb → mapping/key_value_mapping.rb} +64 -16
  33. data/lib/lutaml/model/{key_value_mapping_rule.rb → mapping/key_value_mapping_rule.rb} +18 -2
  34. data/lib/lutaml/model/mapping/mapping.rb +13 -0
  35. data/lib/lutaml/model/mapping/mapping_rule.rb +300 -0
  36. data/lib/lutaml/model/schema/xml_compiler.rb +15 -15
  37. data/lib/lutaml/model/sequence.rb +2 -2
  38. data/lib/lutaml/model/serialization_adapter.rb +22 -0
  39. data/lib/lutaml/model/serialize.rb +219 -444
  40. data/lib/lutaml/model/services/logger.rb +54 -0
  41. data/lib/lutaml/model/services/transformer.rb +48 -0
  42. data/lib/lutaml/model/services.rb +2 -0
  43. data/lib/lutaml/model/toml/document.rb +11 -0
  44. data/lib/lutaml/model/toml/mapping.rb +27 -0
  45. data/lib/lutaml/model/toml/mapping_rule.rb +9 -0
  46. data/lib/lutaml/model/{toml_adapter → toml}/toml_rb_adapter.rb +3 -3
  47. data/lib/lutaml/model/toml/tomlib_adapter.rb +19 -0
  48. data/lib/lutaml/model/toml/transform.rb +8 -0
  49. data/lib/lutaml/model/toml.rb +30 -0
  50. data/lib/lutaml/model/transform/key_value_transform.rb +291 -0
  51. data/lib/lutaml/model/transform/xml_transform.rb +239 -0
  52. data/lib/lutaml/model/transform.rb +78 -0
  53. data/lib/lutaml/model/type/date.rb +1 -1
  54. data/lib/lutaml/model/type/date_time.rb +2 -2
  55. data/lib/lutaml/model/type/hash.rb +1 -1
  56. data/lib/lutaml/model/type/time.rb +2 -2
  57. data/lib/lutaml/model/type/time_without_date.rb +2 -2
  58. data/lib/lutaml/model/type/value.rb +6 -9
  59. data/lib/lutaml/model/uninitialized_class.rb +64 -0
  60. data/lib/lutaml/model/utils.rb +44 -0
  61. data/lib/lutaml/model/validation.rb +1 -0
  62. data/lib/lutaml/model/version.rb +1 -1
  63. data/lib/lutaml/model/{xml_adapter → xml}/builder/nokogiri.rb +2 -2
  64. data/lib/lutaml/model/{xml_adapter → xml}/builder/oga.rb +10 -10
  65. data/lib/lutaml/model/{xml_adapter → xml}/builder/ox.rb +1 -1
  66. data/lib/lutaml/model/{xml_adapter/xml_document.rb → xml/document.rb} +41 -21
  67. data/lib/lutaml/model/xml/element.rb +32 -0
  68. data/lib/lutaml/model/xml/mapping.rb +410 -0
  69. data/lib/lutaml/model/xml/mapping_rule.rb +141 -0
  70. data/lib/lutaml/model/xml/nokogiri_adapter.rb +232 -0
  71. data/lib/lutaml/model/{xml_adapter → xml}/oga/document.rb +1 -1
  72. data/lib/lutaml/model/{xml_adapter → xml}/oga/element.rb +3 -1
  73. data/lib/lutaml/model/xml/oga_adapter.rb +171 -0
  74. data/lib/lutaml/model/xml/ox_adapter.rb +215 -0
  75. data/lib/lutaml/model/xml/transform.rb +8 -0
  76. data/lib/lutaml/model/{xml_adapter → xml}/xml_attribute.rb +1 -1
  77. data/lib/lutaml/model/{xml_adapter → xml}/xml_element.rb +23 -10
  78. data/lib/lutaml/model/{xml_adapter → xml}/xml_namespace.rb +1 -1
  79. data/lib/lutaml/model/xml.rb +31 -0
  80. data/lib/lutaml/model/xml_adapter/element.rb +11 -25
  81. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +6 -223
  82. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +13 -163
  83. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +10 -207
  84. data/lib/lutaml/model/yaml/document.rb +10 -0
  85. data/lib/lutaml/model/yaml/mapping.rb +19 -0
  86. data/lib/lutaml/model/yaml/mapping_rule.rb +9 -0
  87. data/lib/lutaml/model/{yaml_adapter/standard_yaml_adapter.rb → yaml/standard_adapter.rb} +4 -3
  88. data/lib/lutaml/model/yaml/transform.rb +8 -0
  89. data/lib/lutaml/model/yaml.rb +21 -0
  90. data/lib/lutaml/model.rb +40 -4
  91. data/lutaml-model.gemspec +0 -4
  92. data/spec/benchmarks/xml_parsing_benchmark_spec.rb +7 -7
  93. data/spec/fixtures/person.rb +5 -5
  94. data/spec/lutaml/model/attribute_spec.rb +37 -1
  95. data/spec/lutaml/model/cdata_spec.rb +9 -9
  96. data/spec/lutaml/model/collection_spec.rb +50 -2
  97. data/spec/lutaml/model/comparable_model_spec.rb +92 -27
  98. data/spec/lutaml/model/custom_bibtex_adapter_spec.rb +598 -0
  99. data/spec/lutaml/model/custom_vobject_adapter_spec.rb +1226 -0
  100. data/spec/lutaml/model/defaults_spec.rb +1 -1
  101. data/spec/lutaml/model/enum_spec.rb +1 -1
  102. data/spec/lutaml/model/group_spec.rb +333 -20
  103. data/spec/lutaml/model/hash/adapter_spec.rb +255 -0
  104. data/spec/lutaml/model/json_adapter_spec.rb +6 -6
  105. data/spec/lutaml/model/key_value_mapping_spec.rb +65 -3
  106. data/spec/lutaml/model/mixed_content_spec.rb +24 -24
  107. data/spec/lutaml/model/multiple_mapping_spec.rb +5 -5
  108. data/spec/lutaml/model/ordered_content_spec.rb +6 -6
  109. data/spec/lutaml/model/polymorphic_spec.rb +526 -0
  110. data/spec/lutaml/model/render_empty_spec.rb +194 -0
  111. data/spec/lutaml/model/render_nil_spec.rb +206 -22
  112. data/spec/lutaml/model/root_mappings_spec.rb +3 -3
  113. data/spec/lutaml/model/schema/xml_compiler_spec.rb +6 -6
  114. data/spec/lutaml/model/serializable_spec.rb +179 -103
  115. data/spec/lutaml/model/simple_model_spec.rb +9 -9
  116. data/spec/lutaml/model/toml_adapter_spec.rb +6 -6
  117. data/spec/lutaml/model/toml_spec.rb +51 -0
  118. data/spec/lutaml/model/transformation_spec.rb +72 -15
  119. data/spec/lutaml/model/uninitialized_class_spec.rb +96 -0
  120. data/spec/lutaml/model/value_map_spec.rb +240 -0
  121. data/spec/lutaml/model/xml/namespace/nested_with_explicit_namespace_spec.rb +85 -0
  122. data/spec/lutaml/model/xml/namespace_spec.rb +57 -0
  123. data/spec/lutaml/model/xml/xml_element_spec.rb +93 -0
  124. data/spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb +2 -2
  125. data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +2 -2
  126. data/spec/lutaml/model/xml_adapter/ox_adapter_spec.rb +2 -2
  127. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +6 -6
  128. data/spec/lutaml/model/xml_adapter_spec.rb +6 -6
  129. data/spec/lutaml/model/xml_mapping_rule_spec.rb +105 -5
  130. data/spec/lutaml/model/xml_mapping_spec.rb +70 -16
  131. data/spec/lutaml/model/xml_spec.rb +63 -0
  132. data/spec/lutaml/model/yaml_adapter_spec.rb +3 -5
  133. data/spec/sample_model_spec.rb +3 -3
  134. data/spec/spec_helper.rb +3 -3
  135. metadata +76 -59
  136. data/lib/lutaml/model/json_adapter/json_document.rb +0 -20
  137. data/lib/lutaml/model/json_adapter/json_object.rb +0 -28
  138. data/lib/lutaml/model/loggable.rb +0 -15
  139. data/lib/lutaml/model/mapping_rule.rb +0 -109
  140. data/lib/lutaml/model/toml_adapter/toml_document.rb +0 -20
  141. data/lib/lutaml/model/toml_adapter/toml_object.rb +0 -28
  142. data/lib/lutaml/model/toml_adapter/tomlib_adapter.rb +0 -20
  143. data/lib/lutaml/model/toml_adapter.rb +0 -6
  144. data/lib/lutaml/model/xml_mapping.rb +0 -307
  145. data/lib/lutaml/model/xml_mapping_rule.rb +0 -122
  146. data/lib/lutaml/model/yaml_adapter/yaml_document.rb +0 -20
  147. data/lib/lutaml/model/yaml_adapter/yaml_object.rb +0 -28
  148. data/lib/lutaml/model/yaml_adapter.rb +0 -8
@@ -1,307 +0,0 @@
1
- require_relative "xml_mapping_rule"
2
-
3
- module Lutaml
4
- module Model
5
- class XmlMapping
6
- include Lutaml::Model::Loggable
7
-
8
- TYPES = {
9
- attribute: :map_attribute,
10
- element: :map_element,
11
- content: :map_content,
12
- all_content: :map_all,
13
- }.freeze
14
-
15
- attr_reader :root_element,
16
- :namespace_uri,
17
- :namespace_prefix,
18
- :mixed_content,
19
- :ordered,
20
- :element_sequence
21
-
22
- def initialize
23
- @elements = {}
24
- @attributes = {}
25
- @element_sequence = []
26
- @content_mapping = nil
27
- @raw_mapping = nil
28
- @mixed_content = false
29
- end
30
-
31
- alias mixed_content? mixed_content
32
- alias ordered? ordered
33
-
34
- def root(name, mixed: false, ordered: false)
35
- @root_element = name
36
- @mixed_content = mixed
37
- @ordered = ordered || mixed # mixed contenet will always be ordered
38
- end
39
-
40
- def root?
41
- !!root_element
42
- end
43
-
44
- def no_root
45
- @no_root = true
46
- end
47
-
48
- def no_root?
49
- !!@no_root
50
- end
51
-
52
- def prefixed_root
53
- if namespace_uri && namespace_prefix
54
- "#{namespace_prefix}:#{root_element}"
55
- else
56
- root_element
57
- end
58
- end
59
-
60
- def namespace(uri, prefix = nil)
61
- raise Lutaml::Model::NoRootNamespaceError if no_root?
62
-
63
- @namespace_uri = uri
64
- @namespace_prefix = prefix
65
- end
66
-
67
- # rubocop:disable Metrics/ParameterLists
68
- def map_element(
69
- name,
70
- to: nil,
71
- render_nil: false,
72
- render_default: false,
73
- with: {},
74
- delegate: nil,
75
- cdata: false,
76
- namespace: (namespace_set = false
77
- nil),
78
- prefix: (prefix_set = false
79
- nil),
80
- transform: {}
81
- )
82
- validate!(name, to, with, type: TYPES[:element])
83
-
84
- rule = XmlMappingRule.new(
85
- name,
86
- to: to,
87
- render_nil: render_nil,
88
- render_default: render_default,
89
- with: with,
90
- delegate: delegate,
91
- cdata: cdata,
92
- namespace: namespace,
93
- default_namespace: namespace_uri,
94
- prefix: prefix,
95
- namespace_set: namespace_set != false,
96
- prefix_set: prefix_set != false,
97
- transform: transform,
98
- )
99
- @elements[rule.namespaced_name] = rule
100
- end
101
-
102
- def map_attribute(
103
- name,
104
- to: nil,
105
- render_nil: false,
106
- render_default: false,
107
- with: {},
108
- delegate: nil,
109
- namespace: (namespace_set = false
110
- nil),
111
- prefix: (prefix_set = false
112
- nil)
113
- )
114
- validate!(name, to, with, type: TYPES[:attribute])
115
- warn_auto_handling(name) if name == "schemaLocation"
116
-
117
- rule = XmlMappingRule.new(
118
- name,
119
- to: to,
120
- render_nil: render_nil,
121
- render_default: render_default,
122
- with: with,
123
- delegate: delegate,
124
- namespace: namespace,
125
- prefix: prefix,
126
- attribute: true,
127
- default_namespace: namespace_uri,
128
- namespace_set: namespace_set != false,
129
- prefix_set: prefix_set != false,
130
- )
131
- @attributes[rule.namespaced_name] = rule
132
- end
133
-
134
- # rubocop:enable Metrics/ParameterLists
135
-
136
- def map_content(
137
- to: nil,
138
- render_nil: false,
139
- render_default: false,
140
- with: {},
141
- delegate: nil,
142
- mixed: false,
143
- cdata: false
144
- )
145
- validate!("content", to, with, type: TYPES[:content])
146
-
147
- @content_mapping = XmlMappingRule.new(
148
- nil,
149
- to: to,
150
- render_nil: render_nil,
151
- render_default: render_default,
152
- with: with,
153
- delegate: delegate,
154
- mixed_content: mixed,
155
- cdata: cdata,
156
- )
157
- end
158
-
159
- def map_all(
160
- to:,
161
- render_nil: false,
162
- render_default: false,
163
- delegate: nil,
164
- with: {},
165
- namespace: (namespace_set = false
166
- nil),
167
- prefix: (prefix_set = false
168
- nil)
169
- )
170
- validate!(Constants::RAW_MAPPING_KEY, to, with, type: TYPES[:all_content])
171
-
172
- rule = XmlMappingRule.new(
173
- Constants::RAW_MAPPING_KEY,
174
- to: to,
175
- render_nil: render_nil,
176
- render_default: render_default,
177
- with: with,
178
- delegate: delegate,
179
- namespace: namespace,
180
- prefix: prefix,
181
- default_namespace: namespace_uri,
182
- namespace_set: namespace_set != false,
183
- prefix_set: prefix_set != false,
184
- )
185
-
186
- @raw_mapping = rule
187
- end
188
-
189
- alias map_all_content map_all
190
-
191
- def sequence(&block)
192
- @element_sequence << Sequence.new(self).tap do |s|
193
- s.instance_eval(&block)
194
- end
195
- end
196
-
197
- def import_model_mappings(model)
198
- raise Lutaml::Model::ImportModelWithRootError.new(model) if model.root?
199
-
200
- mappings = model.mappings_for(:xml)
201
- @elements.merge!(mappings.instance_variable_get(:@elements))
202
- @attributes.merge!(mappings.instance_variable_get(:@attributes))
203
- (@element_sequence << mappings.element_sequence).flatten!
204
- end
205
-
206
- def validate!(key, to, with, type: nil)
207
- validate_mappings!(type)
208
-
209
- if to.nil? && with.empty?
210
- msg = ":to or :with argument is required for mapping '#{key}'"
211
- raise IncorrectMappingArgumentsError.new(msg)
212
- end
213
-
214
- if !with.empty? && (with[:from].nil? || with[:to].nil?)
215
- msg = ":with argument for mapping '#{key}' requires :to and :from keys"
216
- raise IncorrectMappingArgumentsError.new(msg)
217
- end
218
- end
219
-
220
- def validate_mappings!(type)
221
- if !@raw_mapping.nil? && type != TYPES[:attribute]
222
- raise StandardError, "#{type} is not allowed, only #{TYPES[:attribute]} " \
223
- "is allowed with #{TYPES[:all_content]}"
224
- end
225
-
226
- if !(elements.empty? && content_mapping.nil?) && type == TYPES[:all_content]
227
- raise StandardError, "#{TYPES[:all_content]} is not allowed with other mappings"
228
- end
229
- end
230
-
231
- def elements
232
- @elements.values
233
- end
234
-
235
- def attributes
236
- @attributes.values
237
- end
238
-
239
- def content_mapping
240
- @content_mapping
241
- end
242
-
243
- def raw_mapping
244
- @raw_mapping
245
- end
246
-
247
- def mappings
248
- elements + attributes + [content_mapping, raw_mapping].compact
249
- end
250
-
251
- def element(name)
252
- elements.detect do |rule|
253
- name == rule.to
254
- end
255
- end
256
-
257
- def attribute(name)
258
- attributes.detect do |rule|
259
- name == rule.to
260
- end
261
- end
262
-
263
- def find_by_name(name)
264
- if ["text", "#cdata-section"].include?(name.to_s)
265
- content_mapping
266
- else
267
- mappings.detect do |rule|
268
- rule.name == name.to_s || rule.name == name.to_sym
269
- end
270
- end
271
- end
272
-
273
- def deep_dup
274
- self.class.new.tap do |xml_mapping|
275
- xml_mapping.root(@root_element.dup, mixed: @mixed_content,
276
- ordered: @ordered)
277
- xml_mapping.namespace(@namespace_uri.dup, @namespace_prefix.dup)
278
-
279
- attributes_to_dup.each do |var_name|
280
- value = instance_variable_get(var_name)
281
- xml_mapping.instance_variable_set(var_name, Utils.deep_dup(value))
282
- end
283
- end
284
- end
285
-
286
- def attributes_to_dup
287
- @attributes_to_dup ||= %i[
288
- @content_mapping
289
- @raw_mapping
290
- @element_sequence
291
- @attributes
292
- @elements
293
- ]
294
- end
295
-
296
- def dup_mappings(mappings)
297
- new_mappings = {}
298
-
299
- mappings.each do |key, mapping_rule|
300
- new_mappings[key] = mapping_rule.deep_dup
301
- end
302
-
303
- new_mappings
304
- end
305
- end
306
- end
307
- end
@@ -1,122 +0,0 @@
1
- require_relative "mapping_rule"
2
-
3
- module Lutaml
4
- module Model
5
- class XmlMappingRule < MappingRule
6
- attr_reader :namespace, :prefix, :mixed_content, :default_namespace, :cdata
7
-
8
- def initialize(
9
- name,
10
- to:,
11
- render_nil: false,
12
- render_default: false,
13
- with: {},
14
- delegate: nil,
15
- namespace: nil,
16
- prefix: nil,
17
- mixed_content: false,
18
- cdata: false,
19
- namespace_set: false,
20
- prefix_set: false,
21
- attribute: false,
22
- default_namespace: nil,
23
- transform: {}
24
- )
25
- super(
26
- name,
27
- to: to,
28
- render_nil: render_nil,
29
- render_default: render_default,
30
- with: with,
31
- delegate: delegate,
32
- attribute: attribute,
33
- transform: transform
34
- )
35
-
36
- @namespace = if namespace.to_s == "inherit"
37
- # we are using inherit_namespace in xml builder by
38
- # default so no need to do anything here.
39
- else
40
- namespace
41
- end
42
- @prefix = prefix
43
- @mixed_content = mixed_content
44
- @cdata = cdata
45
-
46
- @default_namespace = default_namespace
47
-
48
- @namespace_set = namespace_set
49
- @prefix_set = prefix_set
50
- end
51
-
52
- def namespace_set?
53
- !!@namespace_set
54
- end
55
-
56
- def prefix_set?
57
- !!@prefix_set
58
- end
59
-
60
- def content_mapping?
61
- name.nil?
62
- end
63
-
64
- def content_key
65
- cdata ? "#cdata-section" : "text"
66
- end
67
-
68
- def mixed_content?
69
- !!@mixed_content
70
- end
71
-
72
- def prefixed_name
73
- rule_name = multiple_mappings? ? name.first : name
74
- if prefix
75
- "#{prefix}:#{rule_name}"
76
- else
77
- rule_name
78
- end
79
- end
80
-
81
- def namespaced_names(parent_namespace = nil)
82
- if multiple_mappings?
83
- name.map { |rule_name| namespaced_name(parent_namespace, rule_name) }
84
- else
85
- [namespaced_name(parent_namespace)]
86
- end
87
- end
88
-
89
- def namespaced_name(parent_namespace = nil, name = self.name)
90
- if name == "lang"
91
- "#{prefix}:#{name}"
92
- elsif namespace_set? || @attribute
93
- [namespace, name].compact.join(":")
94
- elsif default_namespace
95
- "#{default_namespace}:#{name}"
96
- else
97
- [parent_namespace, name].compact.join(":")
98
- end
99
- end
100
-
101
- def deep_dup
102
- self.class.new(
103
- name.dup,
104
- to: to,
105
- render_nil: render_nil,
106
- render_default: render_default,
107
- with: Utils.deep_dup(custom_methods),
108
- delegate: delegate,
109
- namespace: namespace.dup,
110
- prefix: prefix.dup,
111
- mixed_content: mixed_content,
112
- cdata: cdata,
113
- namespace_set: namespace_set?,
114
- prefix_set: prefix_set?,
115
- attribute: attribute,
116
- default_namespace: default_namespace.dup,
117
- transform: transform.dup,
118
- )
119
- end
120
- end
121
- end
122
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "yaml_object"
4
-
5
- module Lutaml
6
- module Model
7
- module YamlAdapter
8
- # Base class for YAML documents
9
- class YamlDocument < YamlObject
10
- def self.parse(yaml, _options = {})
11
- raise NotImplementedError, "Subclasses must implement `parse`."
12
- end
13
-
14
- def to_yaml(*args)
15
- raise NotImplementedError, "Subclasses must implement `to_yaml`."
16
- end
17
- end
18
- end
19
- end
20
- end
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Lutaml
4
- module Model
5
- module YamlAdapter
6
- # Base class for YAML objects
7
- class YamlObject
8
- attr_reader :attributes
9
-
10
- def initialize(attributes = {})
11
- @attributes = attributes
12
- end
13
-
14
- def [](key)
15
- @attributes[key]
16
- end
17
-
18
- def []=(key, value)
19
- @attributes[key] = value
20
- end
21
-
22
- def to_h
23
- @attributes
24
- end
25
- end
26
- end
27
- end
28
- end
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Lutaml
4
- module Model
5
- module YamlAdapter
6
- end
7
- end
8
- end