@shexjs/util
Version:
Shape Expressions validation and utilities.
177 lines (163 loc) • 7.14 kB
JavaScript
const ShExHumanErrorWriterCjsModule = (function () {
const ShExTerm = require("@shexjs/term");
const XSD = {}
XSD._namespace = "http://www.w3.org/2001/XMLSchema#";
["anyURI", "string"].forEach(p => {
XSD[p] = XSD._namespace+p;
});
return class ShExHumanErrorWriter {
write (val) {
const _HumanErrorWriter = this;
if (Array.isArray(val)) {
return val.reduce((ret, e) => {
const nested = _HumanErrorWriter.write(e).map(s => " " + s);
return ret.length ? ret.concat(["AND"]).concat(nested) : nested;
}, []);
}
if (typeof val === "string")
return [val];
switch (val.type) {
case "FailureList":
return val.errors.reduce((ret, e) => {
return ret.concat(_HumanErrorWriter.write(e));
}, []);
case "Failure":
return ["validating " + val.node + " as " + val.shape + ":"].concat(errorList(val.errors).reduce((ret, e) => {
const nested = _HumanErrorWriter.write(e).map(s => " " + s);
return ret.length > 0 ? ret.concat([" OR"]).concat(nested) : nested.map(s => " " + s);
}, []));
case "TypeMismatch": {
const nested = Array.isArray(val.errors) ?
val.errors.reduce((ret, e) => {
return ret.concat((typeof e === "string" ? [e] : _HumanErrorWriter.write(e)).map(s => " " + s));
}, []) :
" " + (typeof e === "string" ? [val.errors] : _HumanErrorWriter.write(val.errors));
return ["validating " + n3ify(val.triple.object) + ":"].concat(nested);
}
case "RestrictionError": {
const nested = val.errors.constructor === Array ?
val.errors.reduce((ret, e) => {
return ret.concat((typeof e === "string" ? [e] : _HumanErrorWriter.write(e)).map(s => " " + s));
}, []) :
" " + (typeof e === "string" ? [val.errors] : _HumanErrorWriter.write(val.errors));
return ["validating restrictions on " + n3ify(val.focus) + ":"].concat(nested);
}
case "ShapeAndFailure":
return Array.isArray(val.errors) ?
val.errors.reduce((ret, e) => {
return ret.concat((typeof e === "string" ? [e] : _HumanErrorWriter.write(e)).map(s => " " + s));
}, []) :
" " + (typeof e === "string" ? [val.errors] : _HumanErrorWriter.write(val.errors));
case "ShapeOrFailure":
return Array.isArray(val.errors) ?
val.errors.reduce((ret, e) => {
return ret.concat(" OR " + (typeof e === "string" ? [e] : _HumanErrorWriter.write(e)));
}, []) :
" OR " + (typeof e === "string" ? [val.errors] : _HumanErrorWriter.write(val.errors));
case "ShapeNotFailure":
return ["Node " + val.errors.node + " expected to NOT pass " + val.errors.shape];
case "ExcessTripleViolation":
return ["validating " + n3ify(val.triple.object) + ": exceeds cardinality"];
case "ClosedShapeViolation":
return ["Unexpected triple(s): {"].concat(
val.unexpectedTriples.map(t => {
return " " + t.subject + " " + t.predicate + " " + n3ify(t.object) + " ."
})
).concat(["}"]);
case "NodeConstraintViolation":
return ["NodeConstraintError: expected to " + this.nodeConstraintToSimple(val.shapeExpr).join(', ')];
case "MissingProperty":
return ["Missing property: " + val.property];
case "NegatedProperty":
return ["Unexpected property: " + val.property];
case "AbstractShapeFailure":
return ["Abstract Shape: " + val.shape];
case "SemActFailure": {
const nested = Array.isArray(val.errors) ?
val.errors.reduce((ret, e) => {
return ret.concat((typeof e === "string" ? [e] : _HumanErrorWriter.write(e)).map(s => " " + s));
}, []) :
" " + (typeof e === "string" ? [val.errors] : _HumanErrorWriter.write(val.errors));
return ["rejected by semantic action:"].concat(nested);
}
case "SemActViolation":
return [val.message];
default:
debugger; // console.log(val);
throw Error("unknown shapeExpression type \"" + val.type + "\" in " + JSON.stringify(val));
}
function errorList (errors) {
return errors.reduce(function (acc, e) {
const attrs = Object.keys(e);
return acc.concat(
(attrs.length === 1 && attrs[0] === "errors")
? errorList(e.errors)
: e);
}, []);
}
}
nodeConstraintToSimple (nc) {
const elts = [];
if ('nodeKind' in nc) elts.push(`be a ${nc.nodeKind.toUpperCase()}`);
if ('datatype' in nc) elts.push(`have datatype ${nc.datatype}`);
if ('length' in nc) elts.push(`have length ${nc.length}`);
if ('minlength' in nc) elts.push(`have length at least ${nc.length}`);
if ('maxlength' in nc) elts.push(`have length at most ${nc.length}`);
if ('pattern' in nc) elts.push(`match regex /${nc.pattern}/${nc.flags ? nc.flags : ''}`);
if ('mininclusive' in nc) elts.push(`have value at least ${nc.mininclusive}`);
if ('minexclusive' in nc) elts.push(`have value more than ${nc.minexclusive}`);
if ('maxinclusive' in nc) elts.push(`have value at most ${nc.maxinclusive}`);
if ('maxexclusive' in nc) elts.push(`have value less than ${nc.maxexclusive}`);
if ('totaldigits' in nc) elts.push(`have have ${nc.totaldigits} digits`);
if ('fractiondigits' in nc) elts.push(`have have ${nc.fractiondigits} digits after the decimal`);
if ('values' in nc) elts.push(`have a value in [${trim(this.valuesToSimple(nc.values).join(', '), 80, /[, ]^>/)}]`);
return elts;
}
// static
valuesToSimple (values) {
return values.map(v => {
// non stems
/* IRIREF */ if (typeof v === 'string') return `<${v}>`;
/* ObjectLiteral */ if ('value' in v) return this.objectLiteralToSimple(v);
/* Language */ if (v.type === 'Language') return `literal with langauge tag ${v.languageTag}`;
// stems and stem ranges
const [undefined, type, range] = v.type.match(/^(Iri|Literal|Language)Stem(Range)?$/);
let str = type.toLowerCase();
if (typeof v.stem !== "object")
str += ` starting with ${v.stem}`
if ("exclusions" in v)
str += ` excluding ${
v.exclusions.map(excl => typeof excl === "string"
? excl
: "anything starting with " + excl.stem).join(' or ')
}`;
return str;
})
}
objectLiteralToSimple (v) {
return `"${v}` +
('type' in v && v.type !== XSD.string ? `^^<${v.type}>` : '') +
('language' in v ? `@${v.language}` : '')
}
}
function trim (str, desired, skip) {
if (str.length <= desired)
return str;
--desired; // leave room for '…'
while (desired > 0 && str[desired].match(skip))
--desired;
return str.slice(0, desired) + '…';
}
function n3ify (ldterm) {
if (typeof ldterm !== "object")
return ldterm;
const ret = "\"" + ldterm.value + "\"";
if ("language" in ldterm)
return ret + "@" + ldterm.language;
if ("type" in ldterm)
return ret + "^^" + ldterm.type;
return ret;
}
})()
if (typeof require !== 'undefined' && typeof exports !== 'undefined')
module.exports = ShExHumanErrorWriterCjsModule; // node environment