Closed harrysolovay closed 2 years ago
Something that could be used both by codec derivation and (TODO) typegen:
import * as $ from "../_deps/scale.ts"; import * as M from "./Metadata.ts"; import { Visitor } from "./TyVisitor.ts"; export const $null = $.dummy(null); export type DeriveCodec = (tyI: number) => $.Codec<any>; export class DeriveCodecVisitor extends Visitor<$.Codec<any>> { emptyTuple = $null; unit = $null; emptyStruct = $null; emptyUnion = $.never as unknown as $.Codec<unknown>; compact = $.compact; bitSequence = $.bitSequence; u8a = $.uint8array; singleElTuple = (field: number) => { return this.visit(field); }; multipleElTuple = (fields: number[]) => { return $.tuple(...fields.map(this.visit)); }; tupleStruct = (ty: M.Ty & M.StructTyDef) => { return $.tuple(...ty.fields.map((field) => this.visit(field.ty))); }; objectStruct = (ty: M.Ty & M.StructTyDef) => { return $.object(...ty.fields.map((field): $.Field => [field.name!, this.visit(field.ty)])); }; option = (ty: M.Ty & M.UnionTyDef) => { return $.option(this.visit(ty.params[0]!.ty!)); }; result = (ty: M.Ty & M.UnionTyDef) => { return $.result( this.visit(ty.params[0]!.ty!), $.instance(ChainError, ["value", this.visit(ty.params[1]!.ty!)]), ); }; allMembersEmptyUnionMember = (member: M.UnionTyDefMember) => { return $.dummy(member.name); }; emptyUnionMember = (member: M.UnionTyDefMember) => { return $.dummy({ type: member.name }); }; tupleUnionMember = (member: M.UnionTyDefMember) => { const $value = this.tupleFromFields(member.fields.map((f) => f.ty)); return $.transform( $value, ({ value }: { value: unknown }) => value, (value) => ({ type: member.name, value }), ); }; objectUnionMember = (member: M.UnionTyDefMember) => { const memberFields = member.fields.map((field, i) => { return [ field.name || i, $.deferred(() => this.visit(field.ty)), ] as [ string, $.Codec<unknown>, ]; }); return $.object(["type", $.dummy(member.name)], ...memberFields); }; union = (ty: M.Ty & M.UnionTyDef, ...members: $.Codec<unknown>[]) => { const memberIByTag: Record<string, number> = {}; const memberIByDiscriminant: Record<number, number> = {}; ty.members.forEach((member, i) => { memberIByTag[member.name] = member.i; memberIByDiscriminant[member.i] = i; }); return union( (member) => { const tag = typeof member === "string" ? member : (member as any).type; const discriminant = memberIByTag[tag]; if (discriminant === undefined) { throw new Error( `Invalid tag ${JSON.stringify(tag)}, expected one of ${ JSON.stringify(Object.keys(memberIByTag)) }`, ); } return discriminant; }, (discriminant) => { const i = memberIByDiscriminant[discriminant]; if (i === undefined) { throw new Error( `Invalid discriminant ${discriminant}, expected one of ${ JSON.stringify(Object.keys(memberIByDiscriminant)) }`, ); } return i; }, ...members, ) as unknown as $.Codec<any>; }; array = (ty: M.Ty & M.SequenceTyDef) => { return $.array(this.visit(ty.typeParam)); }; sizedU8a = (len: number) => { return $.sizedUint8array(len); }; sizedArray = (ty: M.Ty & M.SizedArrayTyDef) => { return $.sizedArray(this.visit(ty.typeParam), ty.len); }; cycle = (tyI: number) => { return $.deferred(() => this.visit(tyI)); }; getPrimitive = (ty: M.Ty & M.PrimitiveTyDef) => { if (ty.kind === "char") return $.str; return $[ty.kind]; }; } export function DeriveCodec(metadata: M.Metadata) { return new DeriveCodecVisitor(metadata).visit; } export class ChainError extends Error { constructor(readonly value: unknown) { super(); } } type NativeUnion<MemberCodecs extends $.Codec<any>[]> = $.Native<MemberCodecs[number]>; // TODO: get rid of this upon fixing in SCALE impl function union<Members extends $.Codec<any>[]>( discriminate: (value: NativeUnion<Members>) => number, getIndexOfDiscriminant: (discriminant: number) => number, ...members: [...Members] ): $.Codec<NativeUnion<Members>> { return $.createCodec({ _metadata: [union, discriminate, getIndexOfDiscriminant, ...members], _staticSize: 1 + Math.max(...members.map((x) => x._staticSize)), _encode(buffer, value) { const discriminant = discriminate(value); buffer.array[buffer.index++] = discriminant; const $member = members[discriminant]!; $member._encode(buffer, value); }, _decode(buffer) { const discriminant = buffer.array[buffer.index++]!; const indexOfDiscriminant = getIndexOfDiscriminant(discriminant); const $member = members[indexOfDiscriminant]; if (!$member) { throw new Error(`No such member codec matching the discriminant \`${discriminant}\``); } return $member._decode(buffer); }, }); }
Fixed by #195
Something that could be used both by codec derivation and (TODO) typegen: