mirror of
https://github.com/status-im/sourcecred.git
synced 2025-01-27 21:06:09 +00:00
add support for parsing compatible objects (#1867)
This adds Combo parsing support to the compatible module. Now, rather than writing `fromJSON` methods which implicitly take any, we can instead write typesafe `parseJSON` methods which will parse compatible headers, and then choose a version-appropriate parser. Test plan: Added unit tests; `yarn test` passes.
This commit is contained in:
parent
67b74d7cfe
commit
a8f353b33f
@ -1,5 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import * as C from "./combo";
|
||||
export opaque type Compatible<T> = [CompatInfo, T];
|
||||
type CompatInfo = {|
|
||||
+type: string,
|
||||
@ -43,3 +44,34 @@ export function fromCompat<T>(
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const headerParser = C.object({type: C.string, version: C.string});
|
||||
const wrappedParser = C.tuple([headerParser, C.raw]);
|
||||
|
||||
export function compatibleParser<T>(
|
||||
expectedType: string,
|
||||
handlers: {+[version: string]: C.Parser<T>}
|
||||
): C.Parser<T> {
|
||||
return new C.Parser((x) => {
|
||||
const wrapResult = wrappedParser.parse(x);
|
||||
if (!wrapResult.ok) {
|
||||
return {ok: false, err: `unable to unwrap compatible: ${wrapResult.err}`};
|
||||
}
|
||||
const [{type, version}, raw] = wrapResult.value;
|
||||
if (type !== expectedType) {
|
||||
return {
|
||||
ok: false,
|
||||
err: `expected type "${expectedType}" but got "${type}"`,
|
||||
};
|
||||
}
|
||||
if (!Object.prototype.hasOwnProperty.call(handlers, version)) {
|
||||
return {ok: false, err: `no "${type}/${version}" handler`};
|
||||
}
|
||||
const parseResult = handlers[version].parse(raw);
|
||||
if (parseResult.ok) {
|
||||
return parseResult;
|
||||
} else {
|
||||
return {ok: false, err: `${type}/${version}: ${parseResult.err}`};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// @flow
|
||||
|
||||
import {toCompat, fromCompat} from "./compat";
|
||||
import * as C from "./combo";
|
||||
import {toCompat, fromCompat, compatibleParser} from "./compat";
|
||||
import type {Compatible} from "./compat";
|
||||
|
||||
describe("util/compat", () => {
|
||||
@ -72,6 +73,53 @@ describe("util/compat", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("compatibleParser", () => {
|
||||
function parseCompatible(x, type, handlers) {
|
||||
return compatibleParser(type, handlers).parseOrThrow(x);
|
||||
}
|
||||
it("throws on json missing a compatible header", () => {
|
||||
expect(() => parseCompatible("foo", "unused", {})).toThrow(
|
||||
"unable to unwrap compatible"
|
||||
);
|
||||
});
|
||||
it("throws on json with an invalid compatible header", () => {
|
||||
expect(() =>
|
||||
parseCompatible([{type: "foo", version: 3}, {}], "unused", {})
|
||||
).toThrow("unable to unwrap compatible");
|
||||
});
|
||||
it("throws on json with the wrong type", () => {
|
||||
expect(() =>
|
||||
parseCompatible(
|
||||
(toCompat({type: "foo", version: "1.0.0"}, {}): any),
|
||||
"bar",
|
||||
{}
|
||||
)
|
||||
).toThrow(`expected type "bar" but got "foo"`);
|
||||
});
|
||||
it("throws on json with no matching version handler", () => {
|
||||
expect(() =>
|
||||
parseCompatible(
|
||||
(toCompat({type: "foo", version: "1.0.0"}, {}): any),
|
||||
"foo",
|
||||
{}
|
||||
)
|
||||
).toThrow(`no "foo/1.0.0" handler`);
|
||||
});
|
||||
it("uses the correct version handler", () => {
|
||||
const object = {v1: 5, v2: 6};
|
||||
const parseV1 = C.object({v1: C.number});
|
||||
const parseV2 = C.object({v2: C.number});
|
||||
const parsers = {v1: parseV1, v2: parseV2};
|
||||
const compatInfo1 = {type: "foo", version: "v1"};
|
||||
const compatInfo2 = {type: "foo", version: "v2"};
|
||||
const compatV1 = toCompat(compatInfo1, object);
|
||||
const compatV2 = toCompat(compatInfo2, object);
|
||||
const parse = (x) => parseCompatible((x: any), "foo", parsers);
|
||||
expect(parse(compatV1)).toEqual({v1: 5});
|
||||
expect(parse(compatV2)).toEqual({v2: 6});
|
||||
});
|
||||
});
|
||||
|
||||
describe("composable versioning", () => {
|
||||
class InnerV1 {
|
||||
x: number;
|
||||
|
Loading…
x
Reference in New Issue
Block a user