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 = gr:attr(s, model.id_to_uri.content_type):iter()() or NT local rmod = model.from_uri(ctype) if not rmod then error("No type definition for " .. ctype.data) end local report = { id = s.data, ctype = ctype.data, notices = {}, warnings = {}, errors = {} } for fname, rules in pairs(rmod.properties or NT) do local values values = gr:attr(s, model.id_to_uri[fname]) -- Cardinality if rules.min_cardinality or rules.max_cardinality then min_card = rules.min_cardinality or 0 if #values < min_card then table.insert(report.errors, { E_CARD, ("Too few values for %s: expected %d, got %d"):format( fname, min_card, #values) }) end max_card = rules.max_cardinality or math.huge if #values > max_card then table.insert(report.errors, { E_CARD, ("Too many values for %s: expected %d, got %d"):format( fname, max_card, #values) }) end end -- From this point on, if there are no values, skip other criteria. if #values == 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