moxml 0.5.76 → 0.5.77

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2df70c844d6a4b34a8b6088ab84555def6c66f73b6c975d4252f1337df9db206
4
- data.tar.gz: 1ca3ee46f0c416f463eeea9d68954b17d9121c6816dc654c9d3e4ba420343e10
3
+ metadata.gz: 0a7016ee5591571ecbf834dd6f948592967264e18abff80246d8527898090ce7
4
+ data.tar.gz: fb1f4b91c68dde1883a010e38cf35cbc3d21e396f990b4677e755a1dbf10ab90
5
5
  SHA512:
6
- metadata.gz: 4c8288a741d4230ebe5f228ea2b7085ef74177b527bf661dd35395a27c41515150949fadfc15f267f0eec9d3ac74c3b779c942b62ad8c5ff4dcc8c85a0398e5b
7
- data.tar.gz: a547141ac0046f1d1ece0e55eecba71e5c1ec124a91179a45d2e4e4a57d8cfe8a50422b24a91060267bafc04d4ce8cb1efcd507ebcdb5520959540697850bb9d
6
+ metadata.gz: 74b8599a7b3a8e0e7dcef25c6375a712cfe52b8788f361ca7689ddd6823691135a8b33d70ac2a343304361d3615093c98842b53fe6c7a1f60934a6e9df082304
7
+ data.tar.gz: d643d89e4c8ee3ce3ab583cb0b0155363ff8b1bd130133b2836f340a0af46dd7adf8873160dbb56edcb32a14db6786a05de4c06559f0ea48dfff29a6e124c0e8
@@ -403,6 +403,18 @@ namespace_validation_mode: :strict)
403
403
  nil
404
404
  end
405
405
 
406
+ # Parse diagnostics (issue #271): recover-class events the
407
+ # engine recorded during the parse — duplicate-attribute
408
+ # recoveries and siblings — as [{ kind:, message: }] in
409
+ # record order, [] for a clean parse. Reads must happen
410
+ # while the native document is alive (the engine owns the
411
+ # list); adapters without the surface answer []. Distinct
412
+ # from #parse_errors (the strict-parse failure channel):
413
+ # diagnostics describe RECOVERED documents.
414
+ def parse_diagnostics(_native_doc)
415
+ []
416
+ end
417
+
406
418
  # Recover-mode parse diagnostics (issue #147): the error
407
419
  # messages the engine recorded while parsing, [] when the
408
420
  # parse was clean. Engines with a native recover channel
@@ -333,11 +333,39 @@ module Moxml
333
333
 
334
334
  root.visit do |node, entering, depth|
335
335
  if entering && depth.positive?
336
- yield Moxml::Node.wrap_with(node, context, self)
336
+ yield wrap_binding_node(node, context)
337
337
  end
338
338
  end
339
339
  end
340
340
 
341
+ # Fast mint for walk-yielded binding nodes (#312): one
342
+ # class-keyed hash answers wrapper class AND type — the
343
+ # generic wrap path's node_type probe, wrap_native detour,
344
+ # type map, and tap all drop out. Identity rides the
345
+ # @moxml_wrapper ivar (cache-stable nodes, #270).
346
+ BINDING_FAST_WRAP = {
347
+ ::Leptris::XML::Element => [Moxml::Wrappers::Element, :element],
348
+ ::Leptris::XML::Text => [Moxml::Wrappers::Text, :text],
349
+ ::Leptris::XML::CDATA => [Moxml::Wrappers::Cdata, :cdata],
350
+ ::Leptris::XML::Comment => [Moxml::Wrappers::Comment, :comment],
351
+ ::Leptris::XML::ProcessingInstruction =>
352
+ [Moxml::Wrappers::ProcessingInstruction, :processing_instruction],
353
+ }.freeze
354
+
355
+ def wrap_binding_node(node, context)
356
+ if node.instance_variable_defined?(:@moxml_wrapper)
357
+ cached = node.instance_variable_get(:@moxml_wrapper)
358
+ return cached if cached
359
+ end
360
+
361
+ entry = BINDING_FAST_WRAP[node.class]
362
+ return nil unless entry
363
+
364
+ wrapper = entry[0].new(node, context, self, entry[1])
365
+ node.instance_variable_set(:@moxml_wrapper, wrapper)
366
+ wrapper
367
+ end
368
+
341
369
  def record_native_doc(root_native, doc)
342
370
  @native_doc_roots[root_native] = doc
343
371
  end
@@ -842,6 +870,39 @@ module Moxml
842
870
  attachments.get(native_doc, :parse_errors) || NO_PARSE_ERRORS
843
871
  end
844
872
 
873
+ # leptris#1200 recover diagnostics (binding FFI >= the
874
+ # 1.9.206 surface adoption; probed, not version-gated —
875
+ # lockstep trains have shipped gems without faces).
876
+ DIAG_KINDS = %i[
877
+ invalid not_allowed_anywhere not_allowed_here
878
+ not_allowed_yet incomplete missing_required_attr
879
+ attr_not_allowed attr_value_invalid
880
+ char_content_invalid recover
881
+ ].freeze
882
+
883
+ def parse_diagnostics(native_doc)
884
+ ffi = ::Leptris::XML::FFI
885
+ return [] unless native_doc.is_a?(::Leptris::XML::Document) &&
886
+ ffi.respond_to?(:leptris_document_parse_diag_count)
887
+
888
+ doc = native_doc.c_ptr
889
+ count = ffi.leptris_document_parse_diag_count(doc)
890
+ return [] if count.zero?
891
+
892
+ kind_ptr = ::FFI::MemoryPointer.new(:int)
893
+ diagnostics = []
894
+ count.times do |i|
895
+ buffer = ::FFI::MemoryPointer.new(512)
896
+ next unless ffi.leptris_document_parse_diag(
897
+ doc, i, kind_ptr, buffer, 512
898
+ ) == 1
899
+
900
+ kind = DIAG_KINDS[kind_ptr.read_int] || :unknown
901
+ diagnostics << { kind: kind, message: buffer.read_string }
902
+ end
903
+ diagnostics
904
+ end
905
+
845
906
  def create_document(_native_doc = nil)
846
907
  ::Leptris::XML::Document.create
847
908
  end
@@ -58,6 +58,16 @@ module Moxml
58
58
  # reports the fatal error that emptied it; Nokogiri reports its
59
59
  # recover-mode syntax errors; engines without an error channel
60
60
  # answer [].
61
+ # Recover-class diagnostics the engine recorded during this
62
+ # document's parse (issue #271): duplicate-attribute
63
+ # recoveries and siblings, as [{ kind:, message: }] in record
64
+ # order. Read while the document is alive — the engine owns
65
+ # the list. [] when the parse was clean or the adapter has no
66
+ # diagnostic surface.
67
+ def parse_diagnostics
68
+ adapter.parse_diagnostics(@native)
69
+ end
70
+
61
71
  def parse_errors
62
72
  adapter.parse_errors(@native)
63
73
  end
data/lib/moxml/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Moxml
4
- VERSION = "0.5.76"
4
+ VERSION = "0.5.77"
5
5
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # Document#parse_diagnostics (issue #271, option A — additive):
6
+ # recover-class events the engine recorded during the parse, as
7
+ # [{ kind:, message: }]. Clean parses answer []; adapters without
8
+ # the surface answer []. Reads happen through the live native
9
+ # document (the engine owns the list — read before free).
10
+ RSpec.describe Moxml::Document do
11
+ def with_leptris
12
+ ctx = Moxml.new(:leptris)
13
+ yield ctx
14
+ rescue StandardError, LoadError
15
+ skip "leptris adapter unavailable"
16
+ end
17
+
18
+ it "answers [] for a clean parse" do
19
+ with_leptris do |ctx|
20
+ expect(ctx.parse("<r><a/></r>").parse_diagnostics).to eq([])
21
+ end
22
+ end
23
+
24
+ it "surfaces duplicate-attribute recoveries with kind and message" do
25
+ with_leptris do |ctx|
26
+ doc = ctx.parse(%(<r a="1" a="2"/>), recover: true) ||
27
+ ctx.parse(%(<r a="1" a="2"/>))
28
+ next skip "engine recovered without diagnostics" if doc.nil?
29
+
30
+ diags = doc.parse_diagnostics
31
+ expect(diags).not_to be_empty
32
+ expect(diags.first[:kind]).to eq(:recover)
33
+ expect(diags.first[:message]).to be_a(String)
34
+ end
35
+ end
36
+
37
+ it "answers [] on adapters without the surface" do
38
+ ctx = Moxml.new(:rexml)
39
+ expect(ctx.parse("<r/>").parse_diagnostics).to eq([])
40
+ rescue StandardError, LoadError
41
+ skip "rexml adapter unavailable"
42
+ end
43
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moxml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.76
4
+ version: 0.5.77
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -466,6 +466,7 @@ files:
466
466
  - spec/moxml/doctype_spec.rb
467
467
  - spec/moxml/document_builder_spec.rb
468
468
  - spec/moxml/document_free_spec.rb
469
+ - spec/moxml/document_parse_diagnostics_spec.rb
469
470
  - spec/moxml/document_spec.rb
470
471
  - spec/moxml/element_attribute_pairs_spec.rb
471
472
  - spec/moxml/element_spec.rb