mega-sharp-sys 0.0.1

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 (76) hide show
  1. checksums.yaml +7 -0
  2. data/dry-schema-1.16.0/CHANGELOG.md +901 -0
  3. data/dry-schema-1.16.0/LICENSE +20 -0
  4. data/dry-schema-1.16.0/README.md +17 -0
  5. data/dry-schema-1.16.0/config/errors.yml +127 -0
  6. data/dry-schema-1.16.0/dry-schema.gemspec +50 -0
  7. data/dry-schema-1.16.0/lib/dry/schema/compiler.rb +51 -0
  8. data/dry-schema-1.16.0/lib/dry/schema/config.rb +83 -0
  9. data/dry-schema-1.16.0/lib/dry/schema/constants.rb +47 -0
  10. data/dry-schema-1.16.0/lib/dry/schema/dsl.rb +517 -0
  11. data/dry-schema-1.16.0/lib/dry/schema/extensions/hints/compiler_methods.rb +19 -0
  12. data/dry-schema-1.16.0/lib/dry/schema/extensions/hints/message_compiler_methods.rb +89 -0
  13. data/dry-schema-1.16.0/lib/dry/schema/extensions/hints/message_set_methods.rb +44 -0
  14. data/dry-schema-1.16.0/lib/dry/schema/extensions/hints/result_methods.rb +47 -0
  15. data/dry-schema-1.16.0/lib/dry/schema/extensions/hints.rb +65 -0
  16. data/dry-schema-1.16.0/lib/dry/schema/extensions/info/schema_compiler.rb +123 -0
  17. data/dry-schema-1.16.0/lib/dry/schema/extensions/info.rb +27 -0
  18. data/dry-schema-1.16.0/lib/dry/schema/extensions/json_schema/schema_compiler.rb +314 -0
  19. data/dry-schema-1.16.0/lib/dry/schema/extensions/json_schema.rb +29 -0
  20. data/dry-schema-1.16.0/lib/dry/schema/extensions/monads.rb +32 -0
  21. data/dry-schema-1.16.0/lib/dry/schema/extensions/struct.rb +62 -0
  22. data/dry-schema-1.16.0/lib/dry/schema/extensions.rb +21 -0
  23. data/dry-schema-1.16.0/lib/dry/schema/json.rb +17 -0
  24. data/dry-schema-1.16.0/lib/dry/schema/key.rb +184 -0
  25. data/dry-schema-1.16.0/lib/dry/schema/key_coercer.rb +41 -0
  26. data/dry-schema-1.16.0/lib/dry/schema/key_map.rb +136 -0
  27. data/dry-schema-1.16.0/lib/dry/schema/key_validator.rb +117 -0
  28. data/dry-schema-1.16.0/lib/dry/schema/macros/array.rb +54 -0
  29. data/dry-schema-1.16.0/lib/dry/schema/macros/core.rb +55 -0
  30. data/dry-schema-1.16.0/lib/dry/schema/macros/dsl.rb +282 -0
  31. data/dry-schema-1.16.0/lib/dry/schema/macros/each.rb +31 -0
  32. data/dry-schema-1.16.0/lib/dry/schema/macros/filled.rb +58 -0
  33. data/dry-schema-1.16.0/lib/dry/schema/macros/hash.rb +36 -0
  34. data/dry-schema-1.16.0/lib/dry/schema/macros/key.rb +50 -0
  35. data/dry-schema-1.16.0/lib/dry/schema/macros/maybe.rb +38 -0
  36. data/dry-schema-1.16.0/lib/dry/schema/macros/optional.rb +22 -0
  37. data/dry-schema-1.16.0/lib/dry/schema/macros/required.rb +17 -0
  38. data/dry-schema-1.16.0/lib/dry/schema/macros/schema.rb +103 -0
  39. data/dry-schema-1.16.0/lib/dry/schema/macros/value.rb +125 -0
  40. data/dry-schema-1.16.0/lib/dry/schema/message/or/abstract.rb +28 -0
  41. data/dry-schema-1.16.0/lib/dry/schema/message/or/multi_path.rb +100 -0
  42. data/dry-schema-1.16.0/lib/dry/schema/message/or/single_path.rb +72 -0
  43. data/dry-schema-1.16.0/lib/dry/schema/message/or.rb +30 -0
  44. data/dry-schema-1.16.0/lib/dry/schema/message.rb +116 -0
  45. data/dry-schema-1.16.0/lib/dry/schema/message_compiler/visitor_opts.rb +56 -0
  46. data/dry-schema-1.16.0/lib/dry/schema/message_compiler.rb +234 -0
  47. data/dry-schema-1.16.0/lib/dry/schema/message_set.rb +144 -0
  48. data/dry-schema-1.16.0/lib/dry/schema/messages/abstract.rb +202 -0
  49. data/dry-schema-1.16.0/lib/dry/schema/messages/i18n.rb +139 -0
  50. data/dry-schema-1.16.0/lib/dry/schema/messages/namespaced.rb +93 -0
  51. data/dry-schema-1.16.0/lib/dry/schema/messages/template.rb +43 -0
  52. data/dry-schema-1.16.0/lib/dry/schema/messages/yaml.rb +209 -0
  53. data/dry-schema-1.16.0/lib/dry/schema/messages.rb +32 -0
  54. data/dry-schema-1.16.0/lib/dry/schema/namespaced_rule.rb +38 -0
  55. data/dry-schema-1.16.0/lib/dry/schema/params.rb +17 -0
  56. data/dry-schema-1.16.0/lib/dry/schema/path.rb +107 -0
  57. data/dry-schema-1.16.0/lib/dry/schema/predicate.rb +102 -0
  58. data/dry-schema-1.16.0/lib/dry/schema/predicate_inferrer.rb +25 -0
  59. data/dry-schema-1.16.0/lib/dry/schema/predicate_registry.rb +22 -0
  60. data/dry-schema-1.16.0/lib/dry/schema/primitive_inferrer.rb +22 -0
  61. data/dry-schema-1.16.0/lib/dry/schema/processor.rb +213 -0
  62. data/dry-schema-1.16.0/lib/dry/schema/processor_steps.rb +141 -0
  63. data/dry-schema-1.16.0/lib/dry/schema/result.rb +203 -0
  64. data/dry-schema-1.16.0/lib/dry/schema/rule_applier.rb +47 -0
  65. data/dry-schema-1.16.0/lib/dry/schema/step.rb +59 -0
  66. data/dry-schema-1.16.0/lib/dry/schema/trace.rb +114 -0
  67. data/dry-schema-1.16.0/lib/dry/schema/type_container.rb +27 -0
  68. data/dry-schema-1.16.0/lib/dry/schema/type_registry.rb +44 -0
  69. data/dry-schema-1.16.0/lib/dry/schema/types.rb +14 -0
  70. data/dry-schema-1.16.0/lib/dry/schema/types_merger.rb +139 -0
  71. data/dry-schema-1.16.0/lib/dry/schema/value_coercer.rb +36 -0
  72. data/dry-schema-1.16.0/lib/dry/schema/version.rb +7 -0
  73. data/dry-schema-1.16.0/lib/dry/schema.rb +114 -0
  74. data/dry-schema-1.16.0/lib/dry-schema.rb +3 -0
  75. data/mega-sharp-sys.gemspec +11 -0
  76. metadata +114 -0
@@ -0,0 +1,517 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/initializer"
4
+ require "dry/schema/constants"
5
+
6
+ module Dry
7
+ module Schema
8
+ # The schema definition DSL class
9
+ #
10
+ # The DSL is exposed by:
11
+ # - `Schema.define`
12
+ # - `Schema.Params`
13
+ # - `Schema.JSON`
14
+ # - `Schema::Params.define` - use with sub-classes
15
+ # - `Schema::JSON.define` - use with sub-classes
16
+ #
17
+ # @example class-based definition
18
+ # class UserSchema < Dry::Schema::Params
19
+ # define do
20
+ # required(:name).filled
21
+ # required(:age).filled(:integer, gt: 18)
22
+ # end
23
+ # end
24
+ #
25
+ # user_schema = UserSchema.new
26
+ # user_schema.(name: 'Jame', age: 21)
27
+ #
28
+ # @example instance-based definition shortcut
29
+ # UserSchema = Dry::Schema.Params do
30
+ # required(:name).filled
31
+ # required(:age).filled(:integer, gt: 18)
32
+ # end
33
+ #
34
+ # UserSchema.(name: 'Jame', age: 21)
35
+ #
36
+ # @api public
37
+ class DSL
38
+ Types = Schema::Types
39
+
40
+ extend ::Dry::Initializer
41
+
42
+ # @return [Compiler] The type of the processor (Params, JSON, or a custom sub-class)
43
+ option :processor_type, default: -> { Processor }
44
+
45
+ # @return [Array] An array with macros defined within the DSL
46
+ option :macros, default: -> { EMPTY_ARRAY.dup }
47
+
48
+ # @return [Compiler] A key=>type map defined within the DSL
49
+ option :types, default: -> { EMPTY_HASH.dup }
50
+
51
+ # @return [Array] Optional parent DSL objects, that will be used to merge keys and rules
52
+ option :parent, Types::Coercible::Array, default: -> { EMPTY_ARRAY.dup }, as: :parents
53
+
54
+ # @return [Config] Configuration object exposed via `#configure` method
55
+ option :config, optional: true, default: proc { default_config }
56
+
57
+ # @return [ProcessorSteps] Steps for the processor
58
+ option :steps, default: proc { ProcessorSteps.new }
59
+
60
+ # @return [Path, Array] Path under which the schema is defined
61
+ option :path, -> *args { Path[*args] if args.any? }, default: proc { EMPTY_ARRAY }
62
+
63
+ # Build a new DSL object and evaluate provided block
64
+ #
65
+ # @param [Hash] options
66
+ # @option options [Class] :processor The processor type
67
+ # (`Params`, `JSON` or a custom sub-class)
68
+ # @option options [Compiler] :compiler An instance of a rule compiler
69
+ # (must be compatible with `Schema::Compiler`) (optional)
70
+ # @option options [Array[DSL]] :parent One or more instances of the parent DSL (optional)
71
+ # @option options [Config] :config A configuration object (optional)
72
+ #
73
+ # @see Schema.define
74
+ # @see Schema.Params
75
+ # @see Schema.JSON
76
+ # @see Processor.define
77
+ #
78
+ # @return [DSL]
79
+ #
80
+ # @api public
81
+ def self.new(**options, &)
82
+ dsl = super
83
+ dsl.instance_eval(&) if block_given?
84
+ dsl.instance_variable_set("@compiler", options[:compiler]) if options[:compiler]
85
+ dsl
86
+ end
87
+
88
+ # Provide customized configuration for your schema
89
+ #
90
+ # @example
91
+ # Dry::Schema.define do
92
+ # configure do |config|
93
+ # config.messages.backend = :i18n
94
+ # end
95
+ # end
96
+ #
97
+ # @see Config
98
+ #
99
+ # @return [DSL]
100
+ #
101
+ # @api public
102
+ def configure(&)
103
+ config.configure(&)
104
+ self
105
+ end
106
+
107
+ # @api private
108
+ def compiler
109
+ @compiler ||= Compiler.new(predicates)
110
+ end
111
+
112
+ # @api private
113
+ def predicates
114
+ @predicates ||= config.predicates
115
+ end
116
+
117
+ # Return a macro with the provided name
118
+ #
119
+ # @param [Symbol] name
120
+ #
121
+ # @return [Macros::Core]
122
+ #
123
+ # @api public
124
+ def [](name)
125
+ macros.detect { |macro| macro.name.equal?(name) }
126
+ end
127
+
128
+ # Define a required key
129
+ #
130
+ # @example
131
+ # required(:name).filled
132
+ #
133
+ # required(:age).value(:integer)
134
+ #
135
+ # required(:user_limit).value(:integer, gt?: 0)
136
+ #
137
+ # required(:tags).filled { array? | str? }
138
+ #
139
+ # @param [Symbol] name The key name
140
+ #
141
+ # @return [Macros::Required]
142
+ #
143
+ # @api public
144
+ def required(name, &)
145
+ key(name, macro: Macros::Required, &)
146
+ end
147
+
148
+ # Define an optional key
149
+ #
150
+ # This works exactly the same as `required` except that if a key is not present
151
+ # rules will not be applied
152
+ #
153
+ # @see DSL#required
154
+ #
155
+ # @param [Symbol] name The key name
156
+ #
157
+ # @return [Macros::Optional]
158
+ #
159
+ # @api public
160
+ def optional(name, &)
161
+ key(name, macro: Macros::Optional, &)
162
+ end
163
+
164
+ # A generic method for defining keys
165
+ #
166
+ # @param [Symbol] name The key name
167
+ # @param [Class] macro The macro sub-class (ie `Macros::Required` or
168
+ # any other `Macros::Key` subclass)
169
+ #
170
+ # @return [Macros::Key]
171
+ #
172
+ # @api public
173
+ def key(name, macro:, &)
174
+ raise ArgumentError, "Key +#{name}+ is not a symbol" unless name.is_a?(::Symbol)
175
+
176
+ set_type(name, Types::Any.meta(default: true))
177
+
178
+ macro = macro.new(
179
+ name: name,
180
+ compiler: compiler,
181
+ schema_dsl: self,
182
+ filter_schema_dsl: filter_schema_dsl
183
+ )
184
+
185
+ macro.value(&) if block_given?
186
+ macros << macro
187
+ macro
188
+ end
189
+
190
+ # Build a processor based on DSL's definitions
191
+ #
192
+ # @return [Processor, Params, JSON]
193
+ #
194
+ # @api private
195
+ def call
196
+ all_steps = parents.map(&:steps) + [steps]
197
+
198
+ result_steps = all_steps.inject { |result, steps| result.merge(steps) }
199
+
200
+ result_steps[:key_validator] = key_validator if config.validate_keys
201
+ result_steps[:key_coercer] = key_coercer
202
+ result_steps[:value_coercer] = value_coercer
203
+ result_steps[:rule_applier] = rule_applier
204
+ result_steps[:filter_schema] = filter_schema.rule_applier if filter_rules?
205
+
206
+ processor_type.new(schema_dsl: self, steps: result_steps)
207
+ end
208
+
209
+ # Merge with another dsl
210
+ #
211
+ # @return [DSL]
212
+ #
213
+ # @api private
214
+ def merge(other)
215
+ new(
216
+ parent: parents + other.parents,
217
+ macros: macros + other.macros,
218
+ types: types.merge(other.types),
219
+ steps: steps.merge(other.steps)
220
+ )
221
+ end
222
+
223
+ # Cast this DSL into a rule object
224
+ #
225
+ # @return [RuleApplier]
226
+ def to_rule
227
+ call.to_rule
228
+ end
229
+
230
+ # A shortcut for defining an array type with a member
231
+ #
232
+ # @example
233
+ # required(:tags).filled(array[:string])
234
+ #
235
+ # @return [Dry::Types::Array::Member]
236
+ #
237
+ # @api public
238
+ def array
239
+ -> member_type { type_registry["array"].of(resolve_type(member_type)) }
240
+ end
241
+
242
+ # Method allows steps injection to the processor
243
+ #
244
+ # @example
245
+ # before(:rule_applier) do |input|
246
+ # input.compact
247
+ # end
248
+ #
249
+ # @return [DSL]
250
+ #
251
+ # @api public
252
+ def before(key, &)
253
+ steps.before(key, &)
254
+ self
255
+ end
256
+
257
+ # Method allows steps injection to the processor
258
+ #
259
+ # @example
260
+ # after(:rule_applier) do |input|
261
+ # input.compact
262
+ # end
263
+ #
264
+ # @return [DSL]
265
+ #
266
+ # @api public
267
+ def after(key, &)
268
+ steps.after(key, &)
269
+ self
270
+ end
271
+
272
+ # The parent (last from parents) which is used for copying non mergeable configuration
273
+ #
274
+ # @return DSL
275
+ #
276
+ # @api public
277
+ def parent
278
+ @parent ||= parents.last
279
+ end
280
+
281
+ # Return type schema used by the value coercer
282
+ #
283
+ # @return [Dry::Types::Lax]
284
+ #
285
+ # @api private
286
+ def type_schema
287
+ strict_type_schema.lax
288
+ end
289
+
290
+ # Return type schema used when composing subschemas
291
+ #
292
+ # @return [Dry::Types::Schema]
293
+ #
294
+ # @api private
295
+ def strict_type_schema
296
+ type_registry["hash"].schema(types)
297
+ end
298
+
299
+ # Return a new DSL instance using the same processor type
300
+ #
301
+ # @return [Dry::Types::Safe]
302
+ #
303
+ # @api private
304
+ def new(klass: self.class, **options, &)
305
+ klass.new(**options, processor_type: processor_type, config: config, &)
306
+ end
307
+
308
+ # Set a type for the given key name
309
+ #
310
+ # @param [Symbol] name The key name
311
+ # @param [Symbol, Array<Symbol>, Dry::Types::Type] spec The type spec or a type object
312
+ #
313
+ # @return [Dry::Types::Safe]
314
+ #
315
+ # @api private
316
+ def set_type(name, spec)
317
+ type = resolve_type(spec)
318
+ meta = {required: false, maybe: type.optional?}
319
+
320
+ @types[name] = type.meta(meta)
321
+ end
322
+
323
+ # Check if a custom type was set under provided key name
324
+ #
325
+ # @return [Bool]
326
+ #
327
+ # @api private
328
+ def custom_type?(name)
329
+ !types[name].meta[:default].equal?(true)
330
+ end
331
+
332
+ # Resolve type object from the provided spec
333
+ #
334
+ # @param [Symbol, Array<Symbol>, Dry::Types::Type] spec
335
+ #
336
+ # @return [Dry::Types::Type]
337
+ #
338
+ # @api private
339
+ def resolve_type(spec)
340
+ case spec
341
+ when ::Dry::Types::Type then spec
342
+ when ::Array then spec.map { |s| resolve_type(s) }.reduce(:|)
343
+ else
344
+ type_registry[spec]
345
+ end
346
+ end
347
+
348
+ # @api private
349
+ def filter_schema
350
+ filter_schema_dsl.call
351
+ end
352
+
353
+ # Build an input schema DSL used by `filter` API
354
+ #
355
+ # @see Macros::Value#filter
356
+ #
357
+ # @api private
358
+ def filter_schema_dsl
359
+ @filter_schema_dsl ||= new(parent: parent_filter_schemas)
360
+ end
361
+
362
+ # Check if any filter rules were defined
363
+ #
364
+ # @api private
365
+ def filter_rules?
366
+ if instance_variable_defined?("@filter_schema_dsl") && !filter_schema_dsl.macros.empty?
367
+ return true
368
+ end
369
+
370
+ parents.any?(&:filter_rules?)
371
+ end
372
+
373
+ # This DSL's type map merged with any parent type maps
374
+ #
375
+ # @api private
376
+ def types
377
+ [*parents.map(&:types), @types].reduce(:merge)
378
+ end
379
+
380
+ # @api private
381
+ def merge_types(op_class, lhs, rhs)
382
+ types_merger.(op_class, lhs, rhs)
383
+ end
384
+
385
+ protected
386
+
387
+ # Build a rule applier
388
+ #
389
+ # @return [RuleApplier]
390
+ #
391
+ # @api protected
392
+ def rule_applier
393
+ RuleApplier.new(rules, config: config.finalize!)
394
+ end
395
+
396
+ # Build rules from defined macros
397
+ #
398
+ # @see #rule_applier
399
+ #
400
+ # @api protected
401
+ def rules
402
+ parent_rules.merge(macros.to_h { [_1.name, _1.to_rule] }.compact)
403
+ end
404
+
405
+ # Build a key map from defined types
406
+ #
407
+ # @api protected
408
+ def key_map(types = self.types)
409
+ keys = types.map { |key, type| key_spec(key, type) }
410
+ km = KeyMap.new(keys)
411
+
412
+ if key_map_type
413
+ km.public_send(key_map_type)
414
+ else
415
+ km
416
+ end
417
+ end
418
+
419
+ private
420
+
421
+ # @api private
422
+ def parent_filter_schemas
423
+ parents.select(&:filter_rules?).map(&:filter_schema)
424
+ end
425
+
426
+ # Build a key validator
427
+ #
428
+ # @return [KeyValidator]
429
+ #
430
+ # @api private
431
+ def key_validator
432
+ KeyValidator.new(key_map: key_map)
433
+ end
434
+
435
+ # Build a key coercer
436
+ #
437
+ # @return [KeyCoercer]
438
+ #
439
+ # @api private
440
+ def key_coercer
441
+ KeyCoercer.symbolized(key_map)
442
+ end
443
+
444
+ # Build a value coercer
445
+ #
446
+ # @return [ValueCoercer]
447
+ #
448
+ # @api private
449
+ def value_coercer
450
+ ValueCoercer.new(type_schema)
451
+ end
452
+
453
+ # Return type registry configured by the processor type
454
+ #
455
+ # @api private
456
+ def type_registry
457
+ @type_registry ||= TypeRegistry.new(
458
+ config.types,
459
+ processor_type.config.type_registry_namespace
460
+ )
461
+ end
462
+
463
+ # Return key map type configured by the processor type
464
+ #
465
+ # @api private
466
+ def key_map_type
467
+ processor_type.config.key_map_type
468
+ end
469
+
470
+ # Build a key spec needed by the key map
471
+ #
472
+ # TODO: we need a key-map compiler using Types AST
473
+ #
474
+ # @api private
475
+ def key_spec(name, type)
476
+ if type.respond_to?(:keys)
477
+ {name => key_map(type.name_key_map)}
478
+ elsif type.respond_to?(:member)
479
+ kv = key_spec(name, type.member)
480
+ kv.equal?(name) ? name : kv.flatten(1)
481
+ elsif type.meta[:maybe] && type.respond_to?(:right)
482
+ key_spec(name, type.right)
483
+ elsif type.respond_to?(:type)
484
+ key_spec(name, type.type)
485
+ else
486
+ name
487
+ end
488
+ end
489
+
490
+ # @api private
491
+ def parent_rules
492
+ parents.reduce({}) { |rules, parent| rules.merge(parent.rules) }
493
+ end
494
+
495
+ # @api private
496
+ def parent_key_map
497
+ parents.reduce([]) { |key_map, parent| parent.key_map + key_map }
498
+ end
499
+
500
+ # @api private
501
+ def default_config
502
+ parents.each_cons(2) do |left, right|
503
+ unless left.config == right.config
504
+ raise ::ArgumentError,
505
+ "Parent configs differ, left=#{left.inspect}, right=#{right.inspect}"
506
+ end
507
+ end
508
+
509
+ (parent || Schema).config.dup
510
+ end
511
+
512
+ def types_merger
513
+ @types_merger ||= TypesMerger.new(type_registry)
514
+ end
515
+ end
516
+ end
517
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Schema
5
+ module Extensions
6
+ module Hints
7
+ # Tweaks AND visitor to enable :hints
8
+ #
9
+ # @api private
10
+ module CompilerMethods
11
+ # @api private
12
+ def visit_and(node)
13
+ super.with(hints: true)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Schema
5
+ module Extensions
6
+ module Hints
7
+ # Adds support for processing [:hint, ...] nodes produced by dry-logic
8
+ #
9
+ # @api private
10
+ module MessageCompilerMethods
11
+ HINT_TYPE_EXCLUSION = %i[
12
+ key? nil? bool? str? int? float? decimal?
13
+ date? date_time? time? hash? array?
14
+ ].freeze
15
+
16
+ HINT_OTHER_EXCLUSION = %i[format? filled?].freeze
17
+
18
+ # @api private
19
+ attr_reader :hints
20
+
21
+ # @api private
22
+ def initialize(*, **)
23
+ super
24
+ @hints = @options.fetch(:hints, true)
25
+ end
26
+
27
+ # @api private
28
+ def hints?
29
+ hints.equal?(true)
30
+ end
31
+
32
+ # @api private
33
+ def filter(messages, opts)
34
+ Array(messages).flatten.reject { |msg| exclude?(msg, opts) }.uniq
35
+ end
36
+
37
+ # @api private
38
+ # rubocop: disable Metrics/AbcSize
39
+ # rubocop: disable Metrics/PerceivedComplexity
40
+ # rubocop: disable Metrics/CyclomaticComplexity
41
+ def exclude?(messages, opts)
42
+ Array(messages).all? do |msg|
43
+ hints = opts.hints.reject { |h|
44
+ msg.eql?(h) || h.predicate.eql?(:filled?)
45
+ }
46
+
47
+ key_failure = opts.key_failure?(msg.path)
48
+ predicate = msg.predicate
49
+
50
+ (HINT_TYPE_EXCLUSION.include?(predicate) && !key_failure) ||
51
+ (msg.predicate == :filled? && key_failure) ||
52
+ (!key_failure && HINT_TYPE_EXCLUSION.include?(predicate) &&
53
+ !hints.empty? && hints.any? { |hint| hint.path == msg.path }) ||
54
+ HINT_OTHER_EXCLUSION.include?(predicate)
55
+ end
56
+ end
57
+ # rubocop: enable Metrics/CyclomaticComplexity
58
+ # rubocop: enable Metrics/PerceivedComplexity
59
+ # rubocop: enable Metrics/AbcSize
60
+
61
+ # @api private
62
+ def message_type(options)
63
+ options[:message_type].equal?(:hint) ? Hint : Message
64
+ end
65
+
66
+ # @api private
67
+ def visit_hint(node, opts)
68
+ if hints?
69
+ filter(visit(node, opts.(message_type: :hint)), opts)
70
+ end
71
+ end
72
+
73
+ # @api private
74
+ def visit_predicate(node, opts)
75
+ message = super
76
+ opts.current_messages << message
77
+ message
78
+ end
79
+
80
+ # @api private
81
+ def visit_each(_node, _opts)
82
+ # TODO: we can still generate a hint for elements here!
83
+ []
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Schema
5
+ module Extensions
6
+ module Hints
7
+ # Hint extensions for MessageSet
8
+ #
9
+ # @api public
10
+ module MessageSetMethods
11
+ # Filtered message hints from all messages
12
+ #
13
+ # @return [Array<Message::Hint>]
14
+ attr_reader :hints
15
+
16
+ # Configuration option to enable/disable showing errors
17
+ #
18
+ # @return [Boolean]
19
+ attr_reader :failures
20
+
21
+ # @api private
22
+ def initialize(messages, options = EMPTY_HASH)
23
+ super
24
+ @hints = messages.select(&:hint?)
25
+ @failures = options.fetch(:failures, true)
26
+ end
27
+
28
+ # Dump message set to a hash with either all messages or just hints
29
+ #
30
+ # @see MessageSet#to_h
31
+ # @see ResultMethods#hints
32
+ #
33
+ # @return [Hash<Symbol=>Array<String>>]
34
+ #
35
+ # @api public
36
+ def to_h
37
+ @to_h ||= failures ? messages_map : messages_map(hints)
38
+ end
39
+ alias_method :to_hash, :to_h
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Schema
5
+ module Extensions
6
+ module Hints
7
+ # Get errors exclusively without hints
8
+ #
9
+ # @api public
10
+ module ResultMethods
11
+ # Return error messages exclusively
12
+ #
13
+ # @see Result#errors
14
+ #
15
+ # @return [MessageSet]
16
+ #
17
+ # @api public
18
+ def errors(options = EMPTY_HASH)
19
+ message_set(options.merge(hints: false))
20
+ end
21
+
22
+ # Get all messages including hints
23
+ #
24
+ # @see #message_set
25
+ #
26
+ # @return [MessageSet]
27
+ #
28
+ # @api public
29
+ def messages(options = EMPTY_HASH)
30
+ message_set(options)
31
+ end
32
+
33
+ # Get hints exclusively without errors
34
+ #
35
+ # @see #message_set
36
+ #
37
+ # @return [MessageSet]
38
+ #
39
+ # @api public
40
+ def hints(options = EMPTY_HASH)
41
+ message_set(options.merge(failures: false))
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end