From 01bbbcdcb87690293a523ccc4b29764f0fb9cd9d Mon Sep 17 00:00:00 2001 From: Gareth Johnson Date: Wed, 27 Nov 2024 10:52:20 +0000 Subject: [PATCH] Add containment ref support --- spec/core/HNamespace.spec.ts | 19 +++++++++ spec/core/Util.spec.ts | 78 ++++++++++++++++++++++++++++++++++++ src/core/HNamespace.ts | 26 ++++++++++++ src/core/util.ts | 57 ++++++++++++++++++++++++++ 4 files changed, 180 insertions(+) diff --git a/spec/core/HNamespace.spec.ts b/spec/core/HNamespace.spec.ts index 7d7e463..6d7bc7f 100644 --- a/spec/core/HNamespace.spec.ts +++ b/spec/core/HNamespace.spec.ts @@ -1926,4 +1926,23 @@ describe('HNamespace', function (): void { expect(tags).toEqual(['vav', 'equip']) }) }) // #newDict() + + describe('#getContainmentRefs()', function (): void { + it('returns a list of all the containment refs for a dict', function (): void { + expect( + defs + .getContainmentRefs() + .map((def) => def.defName) + .sort() + ).toEqual(['equipRef', 'siteRef', 'spaceRef']) + }) + }) // #getContainmentRefs() + + describe('#findContainmentRefs()', function (): void { + it('returns a list of all the containment refs for a def', function (): void { + expect( + defs.findContainmentRefs('site').map((def) => def.defName) + ).toEqual(['siteRef']) + }) + }) // #findContainmentRefs() }) diff --git a/spec/core/Util.spec.ts b/spec/core/Util.spec.ts index 26b3d9e..7af2a01 100644 --- a/spec/core/Util.spec.ts +++ b/spec/core/Util.spec.ts @@ -12,6 +12,7 @@ import { disKey, makeDefaultValue, toKind, + addContainmentRefs, } from '../../src/core/util' import { Kind } from '../../src/core/Kind' import { HBool } from '../../src/core/HBool' @@ -32,6 +33,9 @@ import { HDict } from '../../src/core/HDict' import { HVal } from '../../src/core/HVal' import { HNa } from '../../src/core/HNa' import { HGrid } from '../../src/core/HGrid' +import { HNamespace } from '../../src/core/HNamespace' +import { ZincReader } from '../../src/core/ZincReader' +import { readFile } from './file' describe('util', function (): void { describe('makeValue()', function (): void { @@ -763,4 +767,78 @@ describe('util', function (): void { expect(disKey('pod:key', i18n)).toBeUndefined() }) }) // disKey() + + describe('addContainmentRefs()', () => { + let defs: HNamespace + + beforeAll(() => { + const zinc = readFile('./defsWithFeatures.zinc') + const grid = ZincReader.readValue(zinc) as HGrid + defs = new HNamespace(grid) + }) + + it('adds a siteRef to a floor', () => { + const dict = new HDict() + + const refName = addContainmentRefs( + dict, + new HDict({ + id: HRef.make('site'), + site: HMarker.make(), + }), + defs + ) + + expect(refName).toBe('siteRef') + + expect(dict.toJSON()).toEqual({ + siteRef: { _kind: Kind.Ref, val: 'site' }, + }) + }) + + it('adds a siteRef and an equipRef to a floor', () => { + const dict = new HDict() + + const refName = addContainmentRefs( + dict, + new HDict({ + id: HRef.make('equip'), + siteRef: HRef.make('site'), + equip: HMarker.make(), + }), + defs + ) + + expect(refName).toBe('equipRef') + + expect(dict.toJSON()).toEqual({ + siteRef: { _kind: Kind.Ref, val: 'site' }, + equipRef: { _kind: Kind.Ref, val: 'equip' }, + }) + }) + + it('adds a siteRef, equipRef and a spaceRef to a floor', () => { + const dict = new HDict() + + const refName = addContainmentRefs( + dict, + new HDict({ + id: HRef.make('floor'), + siteRef: HRef.make('site'), + equipRef: HRef.make('equip'), + space: HMarker.make(), + floor: HMarker.make(), + }), + defs + ) + + expect(refName).toBe('spaceRef') + + expect(dict.toJSON()).toEqual({ + siteRef: { _kind: Kind.Ref, val: 'site' }, + equipRef: { _kind: Kind.Ref, val: 'equip' }, + spaceRef: { _kind: Kind.Ref, val: 'floor' }, + }) + }) + }) // addContainmentRefs() }) diff --git a/src/core/HNamespace.ts b/src/core/HNamespace.ts index af5cc53..0fea3be 100644 --- a/src/core/HNamespace.ts +++ b/src/core/HNamespace.ts @@ -1647,4 +1647,30 @@ export class HNamespace { return dict } + + /** + * Return all the containment refs available. + * + * @returns A list of all the containment refs. + */ + @memoize() + public getContainmentRefs(): HDict[] { + return this.allSubTypesOf('ref').filter((def) => def.has('containedBy')) + } + + /** + * Return all the containment refs for the specified def. + * + * Please note, this will filter out any defs that are marked as deprecated. + * + * @param name The name of the def to search the ref for. + * @returns The containment defs. + */ + public findContainmentRefs(name: string | HSymbol): HDict[] { + return this.getContainmentRefs().filter( + (def) => + !def.has('deprecated') && + this.fits(name, def.get('containedBy') as HSymbol) + ) + } } diff --git a/src/core/util.ts b/src/core/util.ts index 8f35c1c..6b61f01 100644 --- a/src/core/util.ts +++ b/src/core/util.ts @@ -43,6 +43,7 @@ import { HSymbol } from './HSymbol' import { HList } from './HList' import { HGrid } from './HGrid' import { Scanner } from '../util/Scanner' +import { HNamespace } from './HNamespace' /** * Make the haystack value based on the supplied data. @@ -526,3 +527,59 @@ export function disKey( const [, pod, disKey] = /^([^:]+)::([^:]+)$/.exec(key.trim()) ?? [] return pod && disKey ? i18n(pod, disKey) : undefined } + +/** + * Add the containment refs to a record. + * + * This ensures all the containment refs are set up accordingly to the project haystack + * specification as well as additional refs that are defined in the defs namespace. + * + * - https://project-haystack.org/doc/docHaystack/Equips + * - https://project-haystack.org/doc/docHaystack/Points + * - https://project-haystack.org/doc/docHaystack/Spaces + * + * @param dict The new record. + * @param parent The parent record. + * @param namespace The defs namespace. + * @returns The primary containment ref name. + */ +export function addContainmentRefs( + dict: HDict, + parent: HDict, + namespace: HNamespace +): string { + const siteRef = parent.has('site') + ? parent.get('id') + : parent.get('siteRef') + + if (siteRef) { + dict.set('siteRef', siteRef) + } + + const equipRef = parent.has('equip') + ? parent.get('id') + : parent.get('equipRef') + + if (equipRef) { + dict.set('equipRef', equipRef) + } + + const spaceRef = + parent.has('space') || parent.has('floor') + ? parent.get('id') + : parent.get('spaceRef') + + if (spaceRef) { + dict.set('spaceRef', spaceRef) + } + + const refName = namespace.findContainmentRefs( + namespace.reflect(parent).type.defName + )?.[0]?.defName + + if (refName && !dict.has(refName)) { + dict.set(refName, parent.get('id') as HRef) + } + + return refName ?? '' +}