Better stringification

This commit is contained in:
Tony Garnock-Jones 2022-01-26 16:03:13 +01:00
parent c5094f8b8f
commit 574209966b
4 changed files with 41 additions and 18 deletions

View File

@ -5,7 +5,6 @@ import { ReaderStateOptions } from "./reader";
export type EmbeddedTypeEncode<T> = { export type EmbeddedTypeEncode<T> = {
encode(s: EncoderState, v: T): void; encode(s: EncoderState, v: T): void;
toValue(v: T): Value<GenericEmbedded>;
} }
export type EmbeddedTypeDecode<T> = { export type EmbeddedTypeDecode<T> = {

View File

@ -48,10 +48,6 @@ export function embeddedId(v: any): number {
export const identityEmbeddedTypeEncode: EmbeddedTypeEncode<any> = { export const identityEmbeddedTypeEncode: EmbeddedTypeEncode<any> = {
encode(s: EncoderState, v: any): void { encode(s: EncoderState, v: any): void {
new Encoder(s, this).push(embeddedId(v)); new Encoder(s, this).push(embeddedId(v));
},
toValue(v: any): Value<GenericEmbedded> {
return embeddedId(v);
} }
}; };

View File

@ -1,12 +1,33 @@
import type { GenericEmbedded } from './embedded'; import { GenericEmbedded } from './embedded';
import type { Value } from './values'; import type { Value } from './values';
import { Annotated } from './annotated'; import { Annotated } from './annotated';
import { Bytes } from './bytes'; import { Bytes } from './bytes';
import { KeyedDictionary, KeyedSet } from './dictionary'; import { KeyedDictionary, KeyedSet } from './dictionary';
import { Writer, Writable, WriterOptions } from './writer'; import { Writer, Writable, WriterOptions, EmbeddedWriter, WriterState } from './writer';
import { fromJS } from './fromjs';
export const stringifyEmbeddedWrite: EmbeddedWriter<any> = {
write(s: WriterState, v: any): void {
if (v instanceof GenericEmbedded) {
new Writer(s, this).push(v.generic);
} else {
try {
const o = fromJS(v);
new Writer(s, this).push(v);
return;
} catch {}
try {
s.pieces.push(JSON.stringify(v));
} catch {}
s.pieces.push('⌜' + v + '⌝');
}
}
};
export function stringify<T = GenericEmbedded>(x: any, options?: WriterOptions<T>): string { export function stringify<T = GenericEmbedded>(x: any, options?: WriterOptions<T>): string {
options = { ... (options ?? {}) };
options.embeddedWrite = options.embeddedWrite ?? stringifyEmbeddedWrite;
return Writer.stringify(x as Writable<T>, options); return Writer.stringify(x as Writable<T>, options);
} }

View File

@ -19,7 +19,10 @@ function isIterable<T>(v: any): v is Iterable<T> {
return typeof v === 'object' && v !== null && typeof v[Symbol.iterator] === 'function'; return typeof v === 'object' && v !== null && typeof v[Symbol.iterator] === 'function';
} }
export const genericEmbeddedTypeEncode: EmbeddedTypeEncode<GenericEmbedded> = { export type EmbeddedWriter<T> =
{ write(s: WriterState, v: T): void } | { toValue(v: T): Value<GenericEmbedded> };
export const genericEmbeddedTypeEncode: EmbeddedTypeEncode<GenericEmbedded> & EmbeddedWriter<GenericEmbedded> = {
encode(s: EncoderState, v: GenericEmbedded): void { encode(s: EncoderState, v: GenericEmbedded): void {
new Encoder(s, this).push(v.generic); new Encoder(s, this).push(v.generic);
}, },
@ -29,13 +32,13 @@ export const genericEmbeddedTypeEncode: EmbeddedTypeEncode<GenericEmbedded> = {
} }
}; };
export const neverEmbeddedTypeEncode: EmbeddedTypeEncode<never> = { export const neverEmbeddedTypeEncode: EmbeddedTypeEncode<never> & EmbeddedWriter<never> = {
encode(_s: EncoderState, _v: never): void { encode(_s: EncoderState, _v: never): void {
throw new Error("Embeddeds not permitted encoding Preserves document"); throw new Error("Embeddeds not permitted encoding Preserves document");
}, },
toValue(_v: never): Value<GenericEmbedded> { toValue(_v: never): Value<GenericEmbedded> {
throw new Error("Embeddeds not permitted encoding Preserves document"); throw new Error("Embeddeds not permitted writing Preserves document");
} }
}; };
@ -47,7 +50,7 @@ export interface WriterStateOptions {
} }
export interface WriterOptions<T> extends WriterStateOptions { export interface WriterOptions<T> extends WriterStateOptions {
embeddedEncode?: EmbeddedTypeEncode<T>; embeddedWrite?: EmbeddedWriter<T>;
} }
export class WriterState { export class WriterState {
@ -225,20 +228,20 @@ export function encodeBase64(bs: Uint8Array): string {
export class Writer<T> { export class Writer<T> {
state: WriterState; state: WriterState;
embeddedType: EmbeddedTypeEncode<T>; embeddedWrite: EmbeddedWriter<T>;
constructor(state: WriterState, embeddedType: EmbeddedTypeEncode<T>); constructor(state: WriterState, embeddedWrite: EmbeddedWriter<T>);
constructor(options?: WriterOptions<T>); constructor(options?: WriterOptions<T>);
constructor( constructor(
state_or_options: (WriterState | WriterOptions<T>) = {}, state_or_options: (WriterState | WriterOptions<T>) = {},
embeddedType?: EmbeddedTypeEncode<T> embeddedWrite?: EmbeddedWriter<T>
) { ) {
if (state_or_options instanceof WriterState) { if (state_or_options instanceof WriterState) {
this.state = state_or_options; this.state = state_or_options;
this.embeddedType = embeddedType!; this.embeddedWrite = embeddedWrite!;
} else { } else {
this.state = new WriterState(state_or_options); this.state = new WriterState(state_or_options);
this.embeddedType = state_or_options.embeddedEncode ?? neverEmbeddedTypeEncode; this.embeddedWrite = state_or_options.embeddedWrite ?? neverEmbeddedTypeEncode;
} }
} }
@ -315,8 +318,12 @@ export class Writer<T> {
else { else {
((v: Embedded<T>) => { ((v: Embedded<T>) => {
this.state.pieces.push('#!'); this.state.pieces.push('#!');
new Writer(this.state, genericEmbeddedTypeEncode) if ('write' in this.embeddedWrite) {
.push(this.embeddedType.toValue(v.embeddedValue)); this.embeddedWrite.write(this.state, v.embeddedValue);
} else {
new Writer(this.state, genericEmbeddedTypeEncode)
.push(this.embeddedWrite.toValue(v.embeddedValue));
}
})(v); })(v);
} }
break; break;