blob: e6b5235eddc80cdaeaf34bbe86de36957e26fcb7 [file] [log] [blame]
# Copyright 2016-2023 The Khronos Group Inc.
#
# SPDX-License-Identifier: Apache-2.0
require 'asciidoctor/extensions' unless RUBY_ENGINE == 'opal'
include ::Asciidoctor
module Asciidoctor
require 'json'
class ValidUsageToJsonTreeprocessor < Extensions::Treeprocessor
def process document
map = {}
map['version info'] = {
'schema version' => 2,
'api version' => document.attr('revnumber'),
'comment' => document.attr('revremark'),
'date' => document.attr('revdate')
}
map['validation'] = {}
parent = ''
error_found = false
# Iterate through all blocks and process valid usage blocks
# Use find_by so that sub-blocks that this script processes can be skipped
document.find_by do |block|
# Keep track of all attributes defined throughout the document, as Asciidoctor will not do this automatically.
# See https://discuss.asciidoctor.org/asciidoctorj-and-document-attributes-tp5960p6525.html
document.playback_attributes(block.attributes)
# Track the parent block for each subsequent valid usage block
if block.context == :open and block.attributes['refpage']
parent = block.attributes['refpage']
end
# Filter out anything that is not a refpage
if block.context == :sidebar && (block.title == "Valid Usage" || block.title == "Valid Usage (Implicit)")
# Iterate through all the VU lists in each block
block.blocks.each do |list|
# Play back list attributes
document.playback_attributes(list.attributes)
# Iterate through all the items in the block, tracking which extensions are enabled/disabled.
list.blocks.each do |item|
# Attribute definitions split lists, so no need to play back attributes between list items
# Look for converted anchors
match = /<a id=\"(VUID-[^"]+)\"[^>]*><\/a>(.*)/m.match(item.text)
if (match != nil)
vuid = match[1]
text = match[2]
# Remove newlines present in the asciidoctor source
text.gsub!("\n", ' ')
# Append text for all the subbullets
text += item.content
# Strip any excess leading/trailing whitespace
text.strip!
# Generate the table entry
entry = {'vuid' => vuid, 'text' => text, 'page' => 'vkspec' }
# Initialize the database if necessary
if map['validation'][parent] == nil
map['validation'][parent] = {}
end
# For legacy schema reasons, put everything in "core" entry section
entry_section = 'core'
# Initialize the entry section if necessary
if map['validation'][parent][entry_section] == nil
map['validation'][parent][entry_section] = []
end
# Check for duplicate entries
if map['validation'][parent][entry_section].include? entry
error_found = true
puts "VU Extraction Treeprocessor: ERROR - Valid Usage statement '#{entry}' is duplicated in the specification with VUID '#{vuid}'."
end
# Add the entry
map['validation'][parent][entry_section] << entry
else
puts "VU Extraction Treeprocessor: WARNING - Valid Usage statement without a VUID found: "
puts item.text
end
end
end
# This block's sub blocks have been handled through iteration, so return true to avoid the main loop re-processing them
true
else
# This block was not what we were looking for, so let asciidoctor continue iterating through its sub blocks
false
end
end
# Generate the json
json = JSON.pretty_generate(map)
outfile = document.attr('json_output')
# Verify the json against the schema, if the required gem is installed
begin
require 'json-schema'
# Read the schema in and validate against it
schema = IO.read(File.join(File.dirname(__FILE__), 'vu_schema.json'))
errors = JSON::Validator.fully_validate(schema, json, :errors_as_objects => true)
# Output errors if there were any
if errors != []
error_found = true
puts 'VU Extraction JSON Validator: ERROR - Validation of the json schema failed'
puts
puts 'It is likely that there is an invalid or malformed entry in the specification text,'
puts 'see below error messages for details, and use their VUIDs and text to correlate them to their location in the specification.'
puts
errors.each do |error|
puts error.to_s
end
end
rescue LoadError
puts 'VU Extraction JSON Validator: WARNING - "json-schema" gem missing - skipping verification of json output'
# error handling code here
end
# Write the file and exit - no further processing required.
IO.write(outfile, json)
if (error_found)
exit! 1
end
exit! 0
end
end
end