local pp = require "pl.pretty" local term = require "volksdata.term" local pkar = require "pocket_archive" local model = require "pocket_archive.model" local logger = pkar.logger local dbg = require "debugger" local E_TYPE = "Type error" local E_CARD = "Cardinality error" local E_RANGE = "Range error" local M = {} M.validate = function(gr, s) _, ctype = next(gr:attr(s, pkar.CONTENT_TYPE_P)) local rmod = model.parse_model(ctype.data) if not rmod then error("No type definition for ", ctype.data) end local report = {notices = {}, warnings = {}, errors = {}} for fname, rules in pairs(rmod.properties or NT) do local values values = gr:attr(s, term.new_iriref_ns(fname)) -- Cardinality local card = 0 for _, v in pairs(values) do card = card + 1 end if rules.min_cardinality or rules.max_cardinality then min_card = rules.min_cardinality or 0 if card < min_card then table.insert(report.errors, { E_CARD, ("Too few values for %s: expected %d, got %d"):format( fname, min_card, card) }) end max_card = rules.max_cardinality or math.huge if card > max_card then table.insert(report.errors, { E_CARD, ("Too many values for %s: expected %d, got %d"):format( fname, max_card, card) }) end end -- From this point on, if there are no values, skip other criteria. if card == 0 then goto skip_prop end -- Type if rules.type then -- String type accepts any value. if rules.type == "number" then for v in pairs(values) do if type(v) ~= "number" then table.insert( report.errors, { E_TYPE, ("Number expected for %s; got: %s") :format(fname, v) }) end end elseif rules.type == "resource" then for _, v in ipairs(values) do if v:sub(1,4) ~= "par" then table.insert( report.errors, { E_TYPE, ("`par:` prefix expected for %s; got: %s") :format(fname, v) }) end end end end -- Range if rules.range then for _, v in ipairs(values) do end end ::skip_prop:: end if #report.errors > 0 then report.max_level = "ERROR" elseif #report.warnings > 0 then report.max_level = "WARN" elseif #report.notices > 0 then report.max_level = "NOTICE" end return report end return M