Skip to content

Commit

Permalink
Merge pull request #72 from swan-io/json
Browse files Browse the repository at this point in the history
fromJSON/toJSON
  • Loading branch information
bloodyowl authored Apr 20, 2024
2 parents b750f05 + d3a5cbd commit 7074d59
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 36 deletions.
19 changes: 18 additions & 1 deletion src/AsyncData.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { keys, values } from "./Dict";
import { Option, Result } from "./OptionResult";
import { LooseRecord } from "./types";
import { BOXED_TYPE } from "./symbols";
import { JsonAsyncData, LooseRecord } from "./types";
import { zip } from "./ZipUnzip";

class __AsyncData<A> {
Expand Down Expand Up @@ -92,6 +93,14 @@ class __AsyncData<A> {
// @ts-ignore
value != null && value.__boxed_type__ === "AsyncData";

static fromJSON = <A>(value: JsonAsyncData<A>) => {
return value.tag === "NotAsked"
? AsyncData.NotAsked()
: value.tag === "Loading"
? AsyncData.Loading()
: AsyncData.Done(value.value);
};

map<B>(this: AsyncData<A>, func: (value: A) => B): AsyncData<B> {
if (this === NOT_ASKED || this === LOADING) {
return this as unknown as AsyncData<B>;
Expand Down Expand Up @@ -266,6 +275,14 @@ class __AsyncData<A> {
isNotAsked(this: AsyncData<A>): this is NotAsked<A> {
return this === NOT_ASKED;
}

toJSON(this: AsyncData<A>): JsonAsyncData<A> {
return this.match<JsonAsyncData<A>>({
NotAsked: () => ({ [BOXED_TYPE]: "AsyncData", tag: "NotAsked" }),
Loading: () => ({ [BOXED_TYPE]: "AsyncData", tag: "Loading" }),
Done: (value) => ({ [BOXED_TYPE]: "AsyncData", tag: "Done", value }),
});
}
}

// @ts-expect-error
Expand Down
1 change: 1 addition & 0 deletions src/Boxed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export { Future } from "./Future";
export { Lazy } from "./Lazy";
export { Option, Result } from "./OptionResult";
export * as Serializer from "./Serializer";
export { JsonAsyncData, JsonOption, JsonResult } from "./types";
27 changes: 26 additions & 1 deletion src/OptionResult.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { keys, values } from "./Dict";
import { LooseRecord } from "./types";
import { BOXED_TYPE } from "./symbols";
import { JsonOption, JsonResult, LooseRecord } from "./types";
import { zip } from "./ZipUnzip";

class __Option<A> {
Expand Down Expand Up @@ -99,6 +100,10 @@ class __Option<A> {
: a.tag === b.tag;
};

static fromJSON = <A>(value: JsonOption<A>) => {
return value.tag === "None" ? Option.None() : Option.Some(value.value);
};

/**
* Returns the Option containing the value from the callback
*
Expand Down Expand Up @@ -222,6 +227,13 @@ class __Option<A> {
isNone(this: Option<A>): this is None<A> {
return this === NONE;
}

toJSON(this: Option<A>): JsonOption<A> {
return this.match<JsonOption<A>>({
None: () => ({ [BOXED_TYPE]: "Option", tag: "None" }),
Some: (value) => ({ [BOXED_TYPE]: "Option", tag: "Some", value }),
});
}
}

// @ts-expect-error
Expand Down Expand Up @@ -387,6 +399,12 @@ class __Result<A, E> {
return false;
};

static fromJSON = <A, E>(value: JsonResult<A, E>) => {
return value.tag === "Ok"
? Result.Ok(value.value)
: Result.Error(value.error);
};

/**
* Returns the Result containing the value from the callback
*
Expand Down Expand Up @@ -522,6 +540,13 @@ class __Result<A, E> {
isError(this: Result<A, E>): this is Error<A, E> {
return this.tag === "Error";
}

toJSON(this: Result<A, E>): JsonResult<A, E> {
return this.match<JsonResult<A, E>>({
Ok: (value) => ({ [BOXED_TYPE]: "Result", tag: "Ok", value }),
Error: (error) => ({ [BOXED_TYPE]: "Result", tag: "Error", error }),
});
}
}

// @ts-expect-error
Expand Down
41 changes: 7 additions & 34 deletions src/Serializer.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,13 @@
import { AsyncData } from "./AsyncData";
import { Option, Result } from "./OptionResult";
import { BOXED_TYPE } from "./symbols";

export const encode = (value: any, indent?: number | undefined) => {
return JSON.stringify(
value,
function (key, value) {
if (value == null) {
return;
}
if (value.__boxed_type__ === "Option") {
return {
__boxed_type__: "Option",
tag: value.tag,
value: value.value,
};
}
if (value.__boxed_type__ === "Result") {
return {
__boxed_type__: "Result",
tag: value.tag,
value: value.value,
error: value.error,
};
}
if (value.__boxed_type__ === "AsyncData") {
return {
__boxed_type__: "AsyncData",
tag: value.tag,
value: value.value,
};
(key, value) => {
if (typeof value[BOXED_TYPE] === "string") {
return { ...value, __boxed_type__: value[BOXED_TYPE] };
}
return value;
},
Expand All @@ -42,19 +21,13 @@ export const decode = (value: string) => {
return value;
}
if (value.__boxed_type__ === "Option") {
return value.tag === "Some" ? Option.Some(value.value) : Option.None();
return Option.fromJSON(value);
}
if (value.__boxed_type__ === "Result") {
return value.tag === "Ok"
? Result.Ok(value.value)
: Result.Error(value.error);
return Result.fromJSON(value);
}
if (value.__boxed_type__ === "AsyncData") {
return value.tag === "NotAsked"
? AsyncData.NotAsked()
: value.tag === "Loading"
? AsyncData.Loading()
: AsyncData.Done(value.value);
return AsyncData.fromJSON(value);
}
return value;
});
Expand Down
1 change: 1 addition & 0 deletions src/symbols.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const BOXED_TYPE = Symbol.for("__boxed_type__");
11 changes: 11 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
export type LooseRecord<T> = Record<PropertyKey, T>;

export type JsonResult<A, E> =
| { tag: "Ok"; value: A }
| { tag: "Error"; error: E };

export type JsonAsyncData<A> =
| { tag: "NotAsked" }
| { tag: "Loading" }
| { tag: "Done"; value: A };

export type JsonOption<A> = { tag: "None" } | { tag: "Some"; value: A };

0 comments on commit 7074d59

Please sign in to comment.