-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add a simple implementation for stringifing and parsing JSON
- Loading branch information
Showing
5 changed files
with
251 additions
and
3 deletions.
There are no files selected for viewing
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { Err, Ok } from '../factory'; | ||
import { ResultJSON } from '../json'; | ||
|
||
describe(`Test fn \`${ResultJSON.serialize.name}\``, () => { | ||
it('should convert a Result to a JSON string', () => { | ||
expect(ResultJSON.serialize(Ok(1))).toStrictEqual({ type: 'ok', value: 1 }); | ||
expect(ResultJSON.serialize(Ok('hello world'))).toStrictEqual({ type: 'ok', value: 'hello world' }); | ||
expect(ResultJSON.serialize(Ok(null))).toStrictEqual({ type: 'ok', value: null }); | ||
expect(ResultJSON.serialize(Ok(undefined))).toStrictEqual({ type: 'ok', value: undefined }); | ||
expect(ResultJSON.serialize(Ok({}))).toStrictEqual({ type: 'ok', value: {} }); | ||
expect(ResultJSON.serialize(Ok([]))).toStrictEqual({ type: 'ok', value: [] }); | ||
|
||
expect(ResultJSON.serialize(Err(1))).toStrictEqual({ type: 'err', value: 1 }); | ||
expect(ResultJSON.serialize(Err('hello world'))).toStrictEqual({ type: 'err', value: 'hello world' }); | ||
expect(ResultJSON.serialize(Err(null))).toStrictEqual({ type: 'err', value: null }); | ||
expect(ResultJSON.serialize(Err(undefined))).toStrictEqual({ type: 'err', value: undefined }); | ||
expect(ResultJSON.serialize(Err({}))).toStrictEqual({ type: 'err', value: {} }); | ||
expect(ResultJSON.serialize(Err([]))).toStrictEqual({ type: 'err', value: [] }); | ||
|
||
expect(ResultJSON.serialize(Ok(Ok(1)))).toStrictEqual({ type: 'ok', value: { type: 'ok', value: 1 } }); | ||
expect(ResultJSON.serialize(Ok(Err(1)))).toStrictEqual({ type: 'ok', value: { type: 'err', value: 1 } }); | ||
expect(ResultJSON.serialize(Err(Ok(1)))).toStrictEqual({ type: 'err', value: { type: 'ok', value: 1 } }); | ||
expect(ResultJSON.serialize(Err(Err(1)))).toStrictEqual({ type: 'err', value: { type: 'err', value: 1 } }); | ||
}); | ||
}); | ||
|
||
describe(`Test fn \`${ResultJSON.deserialize.name}\``, () => { | ||
it('should parse the valid json string', () => { | ||
expect(ResultJSON.deserialize({ type: 'ok', value: 1 })).toStrictEqual(Ok(Ok(1))); | ||
expect(ResultJSON.deserialize({ type: 'ok', value: 'hello world' })).toStrictEqual(Ok(Ok('hello world'))); | ||
expect(ResultJSON.deserialize({ type: 'ok', value: null })).toStrictEqual(Ok(Ok(null))); | ||
expect(ResultJSON.deserialize({ type: 'ok' })).toStrictEqual(Ok(Ok(undefined))); | ||
expect(ResultJSON.deserialize({ type: 'ok', value: undefined })).toStrictEqual(Ok(Ok(undefined))); | ||
expect(ResultJSON.deserialize({ type: 'ok', value: {} })).toStrictEqual(Ok(Ok({}))); | ||
expect(ResultJSON.deserialize({ type: 'ok', value: [] })).toStrictEqual(Ok(Ok([]))); | ||
|
||
expect(ResultJSON.deserialize({ type: 'err', value: 1 })).toStrictEqual(Ok(Err(1))); | ||
expect(ResultJSON.deserialize({ type: 'err', value: 'hello world' })).toStrictEqual(Ok(Err('hello world'))); | ||
expect(ResultJSON.deserialize({ type: 'err', value: null })).toStrictEqual(Ok(Err(null))); | ||
expect(ResultJSON.deserialize({ type: 'err' })).toStrictEqual(Ok(Err(undefined))); | ||
expect(ResultJSON.deserialize({ type: 'err', value: undefined })).toStrictEqual(Ok(Err(undefined))); | ||
expect(ResultJSON.deserialize({ type: 'err', value: {} })).toStrictEqual(Ok(Err({}))); | ||
expect(ResultJSON.deserialize({ type: 'err', value: [] })).toStrictEqual(Ok(Err([]))); | ||
|
||
expect(ResultJSON.deserialize({ type: 'ok', value: { type: 'ok', value: 1 } })).toStrictEqual( | ||
Ok(Ok({ type: 'ok', value: 1 })), | ||
); | ||
expect(ResultJSON.deserialize({ type: 'ok', value: { type: 'err', value: 1 } })).toStrictEqual( | ||
Ok(Ok({ type: 'err', value: 1 })), | ||
); | ||
expect(ResultJSON.deserialize({ type: 'err', value: { type: 'ok', value: 1 } })).toStrictEqual( | ||
Ok(Err({ type: 'ok', value: 1 })), | ||
); | ||
expect(ResultJSON.deserialize({ type: 'err', value: { type: 'err', value: 1 } })).toStrictEqual( | ||
Ok(Err({ type: 'err', value: 1 })), | ||
); | ||
}); | ||
|
||
it('should return `Err` if failed to parse the valid json string', () => { | ||
expect(ResultJSON.deserialize({ value: 1 } as never)).toStrictEqual(Err(new Error('Cannot parse to Result'))); | ||
expect(ResultJSON.deserialize(1 as never)).toStrictEqual( | ||
Err(new TypeError("Cannot use 'in' operator to search for 'type' in 1")), | ||
); | ||
expect(ResultJSON.deserialize('1' as never)).toStrictEqual( | ||
Err(new TypeError("Cannot use 'in' operator to search for 'type' in 1")), | ||
); | ||
expect(ResultJSON.deserialize(true as never)).toStrictEqual( | ||
Err(new TypeError("Cannot use 'in' operator to search for 'type' in true")), | ||
); | ||
expect(ResultJSON.deserialize(false as never)).toStrictEqual( | ||
Err(new TypeError("Cannot use 'in' operator to search for 'type' in false")), | ||
); | ||
expect(ResultJSON.deserialize([] as never)).toStrictEqual(Err(new Error('Cannot parse to Result'))); | ||
}); | ||
}); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export * from './factory'; | ||
export * from './json'; | ||
export * from './resultify'; | ||
export * from './types'; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { Err, Ok } from './factory'; | ||
import { RustlikeResult } from './result'; | ||
import type { Result } from './types'; | ||
|
||
/** | ||
* The type that represents the JSON object structure of `Result`. | ||
* | ||
* `value` may not exist if `T` or `E` is `undefined`. | ||
*/ | ||
export type ResultJson<T, E> = | ||
| (undefined extends T ? { type: 'ok'; value?: T } : { type: 'ok'; value: T }) | ||
| (undefined extends T ? { type: 'err'; value?: E } : { type: 'err'; value: E }); | ||
|
||
/** | ||
* Converts a `Result` to a JSON object. | ||
*/ | ||
function toJSON(result: unknown): unknown { | ||
if (result instanceof RustlikeResult) { | ||
return result.isOk() | ||
? { type: 'ok', value: toJSON(result.unwrapUnchecked()) } | ||
: { type: 'err', value: toJSON(result.unwrapErrUnchecked()) }; | ||
} | ||
return result; | ||
} | ||
|
||
/** | ||
* A simple implementation that convert `Result` to and from JSON object. | ||
* | ||
* The format of the JSON object follows the adjacently tagged enum representation in Rust library Serde. | ||
* https://serde.rs/enum-representations.html#adjacently-tagged | ||
*/ | ||
export const ResultJSON = { | ||
/** | ||
* Converts a `Result` to a JSON object. | ||
* | ||
* The format of the JSON object follows the adjacently tagged enum representation in Rust library Serde. | ||
* https://serde.rs/enum-representations.html#adjacently-tagged | ||
* | ||
* The nested `Result` will be serialized. | ||
*/ | ||
serialize<T, E>(result: Result<unknown, unknown>): ResultJson<T, E> { | ||
return toJSON(result) as ResultJson<T, E>; | ||
}, | ||
|
||
/** | ||
* Converts a JSON object into a `Result`. | ||
* | ||
* This function won't convert any `Result` JSON object inside of `Result`. | ||
* The result of `{"type":"ok","value":{"type":"ok","value":1}}` is `Ok({ type: 'ok', value: 1 })`. | ||
* | ||
* The format of the JSON object follows the adjacently tagged enum representation in Rust library Serde. | ||
* https://serde.rs/enum-representations.html#adjacently-tagged | ||
*/ | ||
deserialize<T, E>(json: ResultJson<unknown, unknown>): Result<Result<T, E>, Error> { | ||
try { | ||
if ('type' in json) { | ||
return json.type === 'ok' ? Ok(Ok(json.value as T)) : Ok(Err(json.value as E)); | ||
} | ||
} catch (err) { | ||
return Err(err as Error); | ||
} | ||
return Err(new Error('Cannot parse to Result')); | ||
}, | ||
}; |