1# Copyright 2016-2023 The Khronos Group Inc. 2# 3# SPDX-License-Identifier: Apache-2.0 4 5require 'asciidoctor/extensions' unless RUBY_ENGINE == 'opal' 6 7include ::Asciidoctor 8 9module Asciidoctor 10 11require 'json' 12class ValidUsageToJsonTreeprocessor < Extensions::Treeprocessor 13 def process document 14 map = {} 15 16 map['version info'] = { 17 'schema version' => 2, 18 'api version' => document.attr('revnumber'), 19 'comment' => document.attr('revremark'), 20 'date' => document.attr('revdate') 21 } 22 23 map['validation'] = {} 24 25 parent = '' 26 error_found = false 27 28 29 30 # Iterate through all blocks and process valid usage blocks 31 # Use find_by so that sub-blocks that this script processes can be skipped 32 document.find_by do |block| 33 34 # Keep track of all attributes defined throughout the document, as Asciidoctor will not do this automatically. 35 # See https://discuss.asciidoctor.org/asciidoctorj-and-document-attributes-tp5960p6525.html 36 document.playback_attributes(block.attributes) 37 38 # Track the parent block for each subsequent valid usage block 39 if block.context == :open and block.attributes['refpage'] 40 parent = block.attributes['refpage'] 41 end 42 43 # Filter out anything that is not a refpage 44 if block.context == :sidebar && (block.title == "Valid Usage" || block.title == "Valid Usage (Implicit)") 45 46 # Iterate through all the VU lists in each block 47 block.blocks.each do |list| 48 49 # Play back list attributes 50 document.playback_attributes(list.attributes) 51 52 # Iterate through all the items in the block, tracking which extensions are enabled/disabled. 53 list.blocks.each do |item| 54 55 # Attribute definitions split lists, so no need to play back attributes between list items 56 57 # Look for converted anchors 58 match = /<a id=\"(VUID-[^"]+)\"[^>]*><\/a>(.*)/m.match(item.text) 59 60 if (match != nil) 61 vuid = match[1] 62 text = match[2] 63 64 # Remove newlines present in the asciidoctor source 65 text.gsub!("\n", ' ') 66 67 # Append text for all the subbullets 68 text += item.content 69 70 # Strip any excess leading/trailing whitespace 71 text.strip! 72 73 # Generate the table entry 74 entry = {'vuid' => vuid, 'text' => text, 'page' => 'vkspec' } 75 76 # Initialize the database if necessary 77 if map['validation'][parent] == nil 78 map['validation'][parent] = {} 79 end 80 81 # For legacy schema reasons, put everything in "core" entry section 82 entry_section = 'core' 83 84 # Initialize the entry section if necessary 85 if map['validation'][parent][entry_section] == nil 86 map['validation'][parent][entry_section] = [] 87 end 88 89 # Check for duplicate entries 90 if map['validation'][parent][entry_section].include? entry 91 error_found = true 92 puts "VU Extraction Treeprocessor: ERROR - Valid Usage statement '#{entry}' is duplicated in the specification with VUID '#{vuid}'." 93 end 94 95 # Add the entry 96 map['validation'][parent][entry_section] << entry 97 else 98 puts "VU Extraction Treeprocessor: WARNING - Valid Usage statement without a VUID found: " 99 puts item.text 100 end 101 end 102 end 103 # This block's sub blocks have been handled through iteration, so return true to avoid the main loop re-processing them 104 true 105 else 106 # This block was not what we were looking for, so let asciidoctor continue iterating through its sub blocks 107 false 108 end 109 end 110 111 112 # Generate the json 113 json = JSON.pretty_generate(map) 114 outfile = document.attr('json_output') 115 116 # Verify the json against the schema, if the required gem is installed 117 begin 118 require 'json-schema' 119 120 # Read the schema in and validate against it 121 schema = IO.read(File.join(File.dirname(__FILE__), 'vu_schema.json')) 122 errors = JSON::Validator.fully_validate(schema, json, :errors_as_objects => true) 123 124 # Output errors if there were any 125 if errors != [] 126 error_found = true 127 puts 'VU Extraction JSON Validator: ERROR - Validation of the json schema failed' 128 puts 129 puts 'It is likely that there is an invalid or malformed entry in the specification text,' 130 puts 'see below error messages for details, and use their VUIDs and text to correlate them to their location in the specification.' 131 puts 132 133 errors.each do |error| 134 puts error.to_s 135 end 136 end 137 rescue LoadError 138 puts 'VU Extraction JSON Validator: WARNING - "json-schema" gem missing - skipping verification of json output' 139 # error handling code here 140 end 141 142 # Write the file and exit - no further processing required. 143 IO.write(outfile, json) 144 145 if (error_found) 146 exit! 1 147 end 148 exit! 0 149 end 150end 151end 152