diff --git a/src/__tests__/codecs.test.ts b/src/__tests__/codecs.test.ts index 42be920..a82effc 100644 --- a/src/__tests__/codecs.test.ts +++ b/src/__tests__/codecs.test.ts @@ -43,6 +43,22 @@ describe('Codecs', () => { }); }); + describe('set codec', () => { + it('encodes', () => { + expect(codecs.set(['a', 'b']).encode('a')).toStrictEqual('a'); + expect(codecs.set(['a', 'b']).encode(undefined)).toStrictEqual(''); + }); + + it('decodes', () => { + expect(codecs.set(['a', 'b']).decode('a')).toStrictEqual('a'); + expect(codecs.set(['a', 'b']).decode('b')).toStrictEqual('b'); + expect(codecs.set(['a', 'b']).decode('')).toStrictEqual(undefined); + expect(codecs.set(['a', 'b']).decode('no match')).toStrictEqual( + undefined, + ); + }); + }); + describe('oneOf codec', () => { it('throws if two options has the same string representation', () => { expect(() => { diff --git a/src/codecs.ts b/src/codecs.ts index b389fd2..5d35a6f 100644 --- a/src/codecs.ts +++ b/src/codecs.ts @@ -34,3 +34,18 @@ export const oneOf = string }>( }, ); }; + +export const set = (valid: readonly T[]) => { + const VALID = new Set(valid); + + return createCodec( + (source) => source ?? '', + (source: string) => { + if (VALID.has(source as T)) { + return source as T; + } + + return undefined; + }, + ); +};