class DataTemplate {
static RE_NUMBER;
static RAW_OBJECT = Object.getPrototypeOf({});
static TypeOf(value) {
const t = value === null ? "null" : typeof value;
return t !== "object"
? t
: value instanceof Array
? "array"
: value instanceof Slot
? "slot"
: Object.getPrototypeOf(value) === DataTemplate.RAW_OBJECT
? "rawobject"
: t;
}
static Walk(value, callback, path = []) {
if (value === undefined || value === null) {
callback(value, path, false);
} else if (value instanceof Slot) {
callback(value, path, true);
} else if (value instanceof Array) {
for (let k = 0; k < value.length; k++) {
DataTemplate.Walk(
value[k],
callback,
path && path.length ? [...path, k] : [k]
);
}
} else if (
typeof value === "object" &&
Object.getPrototypeOf(value) === DataTemplate.RAW_OBJECT
) {
for (let k in value) {
DataTemplate.Walk(
value[k],
callback,
path && path.length ? [...path, k] : [k]
);
}
}
}
static Holes(template) {
const res = new Map();
DataTemplate.Walk(
template,
(value, path, isSlot) => isSlot && res.set(value, new DataPath(path))
);
return res;
}
static Extract(template, value, matched = new Map()) {
if (template == undefined || template == null || value === undefined) {
return matched;
} else if (template instanceof Slot) {
matched.set(template, value);
return matched;
} else if (value === undefined || value === null) {
return matched;
} else if (template instanceof Array) {
for (let k = 0; k < template.length; k++) {
DataTemplate.Match(template[k], value[k], matched);
}
} else if (
typeof value === "object" &&
Object.getPrototypeOf(value) === DataTemplate.RAW_OBJECT
) {
for (let k in template) {
DataTemplate.Match(template[k], value[k], matched);
}
}
return matched;
}
static Match(template, value, matched = new Map()) {
const tt = DataTemplate.TypeOf(template);
if (tt === "slot") {
matched.set(template, value);
return matched;
}
const tv = DataTemplate.TypeOf(value);
console.log({ tv, tt, template, value, matched });
if (tt !== tv) {
return false;
} else {
switch (tt) {
case "array":
case "rawobject":
for (let i in template) {
if (DataTemplate.Match(template[i], value[i], matched) === false) {
return false;
}
}
return matched;
default:
return template == value ? matched : false;
}
}
}
constructor(template) {
this.template = template;
this.type =
template === null || template === undefined
? 0
: template instanceof Object
? template instanceof Array
? 3
: template instanceof Slot
? 2
: Object.getPrototypeOf(template) === DataTemplate.RAW_OBJECT
? 4
: 1
: 1;
this.holes = DataTemplate.Holes(template);
this.pattern = undefined;
for (let [slot, path] of this.holes.entries()) {
this.pattern = path.apply(this.pattern, slot);
}
}
extract(value) {
return DataTemplate.Extract(this.pattern, value);
}
match(value) {
return DataTemplate.Match(this.template, value);
}
apply(mapping) {
switch (this.type) {
case 0:
case 1:
return this.template;
case 2:
return mapping.get(this.holes[0]);
case 3:
case 4:
const res = this.type === 3 ? [...this.template] : { ...this.template };
for (const [slot, path] of this.holes.entries()) {
path.apply(res, mapping.get(slot));
}
return res;
default:
onError(`DataTemplate.apply(): Unsupported type ${this.type}`, {
type: this.type,
mapping
});
}
}
}