graph-ecs/src/query-parser/types.js
2024-11-26 15:38:27 -06:00

252 lines
4.6 KiB
JavaScript

import { assocPath, is } from '../fn.js'
class Value {
#value
constructor(value) {
this.#value = value
}
toString() {
return this.#value.toString()
}
get value() {
return this.#value?.value ?? this.#value
}
equals(other) {
if (other == null) {
return false
}
const value = this.value
return value === other
|| value === other.value
}
}
export class Identifier extends Value {
constructor(value) {
super(value)
}
from(component) {
return component
}
}
export class ObjectIdentifier extends Identifier {
constructor(value) {
super(value)
}
from(component) {
return new Proxy(component, {
get(target, entity, _receiver) {
return Object.fromEntries(
Object.entries(target).map(([k, v]) => [k, v[entity]])
)
}
})
}
}
export class Property extends Identifier {
constructor(value) {
super(value)
}
from(component) {
return component[this.value]
}
}
export class Name extends Identifier {
constructor(value) {
super(value)
}
}
export class Label extends Identifier {
constructor(value) {
super(value)
}
}
export class SelectedGraph extends Identifier {
constructor(value) {
super(value)
}
}
export class Literal {
constructor(value) {
this.value = value
}
}
export class Alias extends Identifier {
constructor(value, alias) {
super(value)
this.alias = alias
}
}
export class ObjectPath extends Identifier {
constructor(value, ...path) {
super(value)
this.path = path
}
toString() {
return `${this.value}.${this.path.join('.')}`
}
from(component) {
return this.path.reduce((acc, v) => v.from(acc), component)
}
equals(value) {
if (is(ObjectPath, value)) {
return value != null
&& this.path.length === value.path.length
&& super.equals(value)
&& this.path.every((x, i) => x === value.path[i])
} else {
return super.equals(value)
}
}
}
class GraphObject {
constructor(name, label, properties = []) {
this.name = name
this.label = label
this.properties = properties
}
}
export class Component extends GraphObject {
constructor(name, label, properties) {
super(name, label, properties)
}
}
export class Node {
constructor(components) {
this.components = components
}
}
export const Direction = Object.freeze({
Left: 0,
Right: 1
})
export class Edge {
constructor(components) {
this.components = components
}
}
export class DirectedEdge extends Edge {
constructor(components, direction) {
super(components)
this.direction = direction
}
static fromEdge(edge, direction) {
return new DirectedEdge(edge.components, direction)
}
}
export class Relationship {
constructor(left, edge, right) {
const [from, to] = edge.direction === Direction.Right ? [left, right] : [right, left]
this.from = from
this.to = to
this.edge = edge
}
}
const isExactly = (Type, value) => Object.getPrototypeOf(value) === Type.prototype
export class ReturnValues {
constructor(...args) {
this.values = args.map(x => isExactly(Identifier, x) ? new ObjectIdentifier(x) : x)
}
#isTag(type) {
type && Object.getOwnPropertySymbols(type).find(
(s) => s.description === 'tagStore'
) || false
}
#forComponentType(value, type) {
if (this.#isTag(type)) {
return {}
} else if (typeof type === 'function') {
return type
} else {
return entity => value.from(type)[entity]
}
}
// TODO: adjust froms to accent entity, return data with built up objects as necessary
from(components) {
const values = components.map(cmp => ({ ...cmp, rv: this.findName(cmp.name) }))
.map(({ type, rv }) => new ReturnValue(rv, this.#forComponentType(rv, type)))
return entity => values.reduce((acc, rv) => Object.assign(acc, rv.withEntity(entity)), {})
}
find(fn) {
return this.values.find(fn)
}
findName(name) {
return this.find(value => value.equals(name))
}
filter(fn) {
return this.values.filter(fn)
}
includes(value) {
return this.find(x => x.equals(value))
}
}
class ReturnValue {
#value
#getter
constructor(value, getter) {
this.#value = value
this.#getter = getter
}
withEntity(entity) {
return assocPath(this.#value.toString(), this.#getter(entity), {})
}
}
export class KeyValuePair {
constructor(key, value) {
this.key = key
this.value = value
}
}
export class Query {
constructor(use, match, returnValues) {
this.use = use
this.match = match
this.returnValues = returnValues
}
}