Skip to content

Commit

Permalink
fix: support CtxAppend to work with array in context
Browse files Browse the repository at this point in the history
  • Loading branch information
tperale committed Dec 21, 2024
1 parent f3dbb7b commit 80d6030
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 11 deletions.
20 changes: 19 additions & 1 deletion src/decorators/__tests__/context.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect } from '@jest/globals'
import { CtxSet, CtxGet, useContextGet, useContextSet } from '../context'
import { CtxSet, CtxGet, useContextGet, useContextSet, CtxAppend } from '../context'
import Meta from '../../metadatas'

function testContextGet (TargetClass: new () => any, field: string, ctx: object, expected: any) {
Expand Down Expand Up @@ -68,6 +68,24 @@ describe('@Ctx: functions', () => {
const ctx = { }
testContextGet(TestClass, 'data', ctx, 0)
})
it('should append the value to an array', () => {
class TestClass {
@CtxAppend('foo')
data: number
}
const ctx: any = {}
testContextSet(TestClass, 'data', 0, ctx)
expect(ctx.foo).toEqual([0])
})
it('should concat the value to the existing array', () => {
class TestClass {
@CtxAppend('foo')
data: number[]
}
const ctx: any = { foo: [1, 2] }
testContextSet(TestClass, 'data', [3, 4], ctx)
expect(ctx.foo).toEqual([1, 2, 3, 4])
})
it('should throw when accessing non existing properties', () => {
class TestClass {
@CtxGet('test.foo')
Expand Down
67 changes: 57 additions & 10 deletions src/decorators/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,44 @@ export function CtxSet<This extends object, Value> (keyGetter: CtxKeyFunction<Th
return ctxPropertyFunctionDecoratorFactory<This, Value> ('ctx-set', CtxType.CtxSetter, keyGetter, undefined, opt)
}

/**
* `@CtxAppend` decorator append the value of the decorated property into a
* shared 'context' during the reading phase.
*
* It works the same way as `@CtxSet` but will append the data to an array
* instead of an object property.
*
* @example
*
*
* ```typescript
* class Record {
* @CtxAppend('Content')
* @Relation(PrimitiveSymbol.u8)
* data: number
* }
*
* class Protocol {
* @Until(EOF)
* records: Record[]
* }
* ```
*
* @param {CtxKeyFunction<This>} keyGetter Either a string formatted as
* recursive key or a function that returns that string based on the
* instance value.
* @param {Partial<CtxOptions>} [opt] Optional configuration.
* @returns {DecoratorType<This, Value>} The property decorator function.
*
* @category Decorators
*/
export function CtxAppend<This extends object, Value> (keyGetter: CtxKeyFunction<This> | string, opt?: Partial<CtxOptions>): DecoratorType<This, Value> {
return ctxPropertyFunctionDecoratorFactory<This, Value> ('ctx-set', CtxType.CtxSetter, keyGetter, undefined, {
...opt,
base_type: 'array',
})
}

/**
* useContextGet execute an array of `Ctx` decorator metadata.
*
Expand Down Expand Up @@ -272,18 +310,27 @@ export function useContextSet<This, Value> (metaCtx: Array<Ctx<This, Value>>, pr
const lastKey = accessors[accessors.length - 1]
const ref = accessors.slice(0, -1).reduce((acc: any, key: string) => {
if (Object.prototype.hasOwnProperty.call(acc, key) === false) {
if (x.options.base_type == 'array') {
Object.defineProperty(acc, key, {
value: []
})
} else {
Object.defineProperty(acc, key, {
value: {}
})
}
Object.defineProperty(acc, key, {
value: {}
})
}
return acc[key]
}, ctx)
ref[lastKey] = propertyValue

if (x.options.base_type == 'array') {
if (Object.prototype.hasOwnProperty.call(ref, lastKey) === false) {
Object.defineProperty(ref, lastKey, {
value: []
})
}

if (Array.isArray(propertyValue)) {
ref[lastKey] = ref[lastKey].concat(propertyValue)
} else {
ref[lastKey].push(propertyValue)
}
} else {
ref[lastKey] = propertyValue
}
})
}

0 comments on commit 80d6030

Please sign in to comment.