Last active
August 7, 2020 23:44
-
-
Save leandro-manifesto/cac78114030756383462b6c7b641aaf2 to your computer and use it in GitHub Desktop.
Custom type converter with @marcj/marshal
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { DateTime } from 'luxon'; | |
import { PropertyCompilerSchema } from '@marcj/marshal'; | |
import { CustomConverterCompiler, registerCustomConverterCompiler } from './register'; | |
class DateTimeConverter implements CustomConverterCompiler { | |
public check(accessor: string, schema: PropertyCompilerSchema, utils: { | |
reserveVariable: () => string; | |
raise: (code: string, message: string) => string; | |
}) { | |
const variableName = utils.reserveVariable(); | |
return { | |
template: ` | |
if (typeof ${accessor} === 'string') { | |
const ${variableName} = DateTime.fromISO(${accessor}); | |
if (!${variableName}.isValid) { | |
${utils.raise('invalid-datetime', 'DateTime must be in ISO format')} | |
} | |
} else { | |
${utils.raise('invalid-datetime', 'DateTime must be a string in ISO format')} | |
} | |
`, | |
context: { DateTime }, | |
}; | |
} | |
public to(setter: string, accessor: string, schema: PropertyCompilerSchema, reserveVariable: () => string) { | |
const variableName = reserveVariable(); | |
return { | |
template: ` | |
if (typeof ${accessor} === 'string') { | |
const ${variableName} = DateTime.fromISO(${accessor}); | |
if (${variableName}.isValid) { | |
${setter} = ${variableName}; | |
} else { | |
throw new Error('Invalid ISO DateTime given in property ${schema.name}: ' + value.invalidExplanation); | |
} | |
} else { | |
throw new Error('Invalid ISO DateTime given in property ${schema.name}: got a ' + (typeof ${accessor})); | |
} | |
`, | |
context: { DateTime }, | |
}; | |
} | |
public from(setter: string, accessor: string) { | |
return `${setter} = ${accessor}.toISO();`; | |
} | |
} | |
registerCustomConverterCompiler(DateTime, DateTimeConverter); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
compilerConvertClassToX, | |
compilerXToClass, | |
jitValidate, | |
registerCheckerCompiler, | |
registerConverterCompiler, | |
TypeCheckerCompiler, | |
TypeConverterCompiler, | |
} from '@marcj/marshal'; | |
export interface AbstractType<T> { | |
prototype: T; | |
} | |
export interface CustomConverterCompiler { | |
check: TypeCheckerCompiler; | |
to: TypeConverterCompiler; | |
from: TypeConverterCompiler; | |
} | |
export interface CustomConverterCompilerType extends AbstractType<CustomConverterCompiler> { | |
new (): CustomConverterCompiler; | |
} | |
const COMPILERS = new WeakMap<AbstractType<unknown>, CustomConverterCompiler>(); | |
export function registerCustomConverterCompiler(type: AbstractType<unknown>, compilerType: CustomConverterCompilerType): void { | |
COMPILERS.set(type, new compilerType()); | |
} | |
registerCheckerCompiler('class', (accessor, schema, utils) => { | |
const { resolveClassType } = schema; | |
if (resolveClassType != null) { | |
const converter = COMPILERS.get(resolveClassType); | |
if (converter != null) { | |
return converter.check(accessor, schema, utils); | |
} | |
} | |
// fallback to default implementation | |
// copied from marshal source: https://github.com/super-hornet/super-hornet.ts/blob/cb358d5d3d7764e3defff9fc744ee9bd6a819b1d/packages/marshal/src/jit-validation-templates.ts#L103 | |
const classType = utils.reserveVariable(); | |
return { | |
template: ` | |
if ('object' === typeof ${accessor} && 'function' !== typeof ${accessor}.slice) { | |
jitValidate(${classType})(${accessor}, ${utils.path}, _errors); | |
} else { | |
${utils.raise('invalid_type', 'Type is not an object')}; | |
} | |
`, | |
context: { | |
[classType]: resolveClassType, | |
jitValidate, | |
}, | |
}; | |
}); | |
const compilerPlainToClass = compilerXToClass('plain'); | |
registerConverterCompiler('plain', 'class', 'class', (setter, accessor, schema, reserveVariable, context) => { | |
const { resolveClassType } = schema; | |
if (resolveClassType != null) { | |
const converter = COMPILERS.get(resolveClassType); | |
if (converter != null) { | |
return converter.to(setter, accessor, schema, reserveVariable, context); | |
} | |
} | |
// fallback to default implementation | |
return compilerPlainToClass(setter, accessor, schema, reserveVariable); | |
}); | |
const compilerClassToPlain = compilerConvertClassToX('plain'); | |
registerConverterCompiler('class', 'plain', 'class', (setter, accessor, schema, reserveVariable, context) => { | |
const { resolveClassType } = schema; | |
if (resolveClassType != null) { | |
const compiler = COMPILERS.get(resolveClassType); | |
if (compiler != null) { | |
return compiler.from(setter, accessor, schema, reserveVariable, context); | |
} | |
} | |
// fallback to default implementation | |
return compilerClassToPlain(setter, accessor, schema, reserveVariable); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment