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> = {
encode(s: EncoderState, v: T): void;
toValue(v: T): Value<GenericEmbedded>;
}
export type EmbeddedTypeDecode<T> = {

View File

@ -48,10 +48,6 @@ export function embeddedId(v: any): number {
export const identityEmbeddedTypeEncode: EmbeddedTypeEncode<any> = {
encode(s: EncoderState, v: any): void {
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 { Annotated } from './annotated';
import { Bytes } from './bytes';
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 {
options = { ... (options ?? {}) };
options.embeddedWrite = options.embeddedWrite ?? stringifyEmbeddedWrite;
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';
}
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 {
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 {
throw new Error("Embeddeds not permitted encoding Preserves document");
},
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 {
embeddedEncode?: EmbeddedTypeEncode<T>;
embeddedWrite?: EmbeddedWriter<T>;
}
export class WriterState {
@ -225,20 +228,20 @@ export function encodeBase64(bs: Uint8Array): string {
export class Writer<T> {
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(
state_or_options: (WriterState | WriterOptions<T>) = {},
embeddedType?: EmbeddedTypeEncode<T>
embeddedWrite?: EmbeddedWriter<T>
) {
if (state_or_options instanceof WriterState) {
this.state = state_or_options;
this.embeddedType = embeddedType!;
this.embeddedWrite = embeddedWrite!;
} else {
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 {
((v: Embedded<T>) => {
this.state.pieces.push('#!');
new Writer(this.state, genericEmbeddedTypeEncode)
.push(this.embeddedType.toValue(v.embeddedValue));
if ('write' in this.embeddedWrite) {
this.embeddedWrite.write(this.state, v.embeddedValue);
} else {
new Writer(this.state, genericEmbeddedTypeEncode)
.push(this.embeddedWrite.toValue(v.embeddedValue));
}
})(v);
}
break;