diff --git a/src/analysis/weights.js b/src/analysis/weights.js index 6e734a7..8f589b4 100644 --- a/src/analysis/weights.js +++ b/src/analysis/weights.js @@ -1,6 +1,8 @@ // @flow +import * as MapUtil from "../util/map"; import {type NodeAddressT, type EdgeAddressT} from "../core/graph"; +import {toCompat, fromCompat, type Compatible} from "../util/compat"; /** * Represents the weight for a particular Node (or NodeType). @@ -45,3 +47,30 @@ export function defaultWeights(): Weights { nodeManualWeights: new Map(), }; } + +export type WeightsJSON = Compatible<{| + +nodeTypeWeights: {[NodeAddressT]: NodeWeight}, + +edgeTypeWeights: {[EdgeAddressT]: EdgeWeight}, + +nodeManualWeights: {[NodeAddressT]: NodeWeight}, +|}>; +const COMPAT_INFO = {type: "sourcecred/weights", version: "0.1.0"}; + +export function toJSON(weights: Weights): WeightsJSON { + return toCompat(COMPAT_INFO, { + nodeTypeWeights: MapUtil.toObject(weights.nodeTypeWeights), + edgeTypeWeights: MapUtil.toObject(weights.edgeTypeWeights), + nodeManualWeights: MapUtil.toObject(weights.nodeManualWeights), + }); +} + +export function fromJSON(json: WeightsJSON): Weights { + const {nodeTypeWeights, edgeTypeWeights, nodeManualWeights} = fromCompat( + COMPAT_INFO, + json + ); + return { + nodeTypeWeights: MapUtil.fromObject(nodeTypeWeights), + edgeTypeWeights: MapUtil.fromObject(edgeTypeWeights), + nodeManualWeights: MapUtil.fromObject(nodeManualWeights), + }; +} diff --git a/src/analysis/weights.test.js b/src/analysis/weights.test.js new file mode 100644 index 0000000..14bd91a --- /dev/null +++ b/src/analysis/weights.test.js @@ -0,0 +1,67 @@ +// @flow + +import stringify from "json-stable-stringify"; +import {NodeAddress, EdgeAddress} from "../core/graph"; +import {toJSON, fromJSON, defaultWeights} from "./weights"; + +describe("analysis/weights", () => { + describe("toJSON/fromJSON", () => { + it("works for the default weights", () => { + const weights = defaultWeights(); + const json = toJSON(weights); + const jsonString = stringify(json, {space: 4}); + expect(jsonString).toMatchInlineSnapshot(` +"[ + { + \\"type\\": \\"sourcecred/weights\\", + \\"version\\": \\"0.1.0\\" + }, + { + \\"edgeTypeWeights\\": { + }, + \\"nodeManualWeights\\": { + }, + \\"nodeTypeWeights\\": { + } + } +]" +`); + expect(weights).toEqual(fromJSON(json)); + }); + + it("works for non-default weights", () => { + const weights = defaultWeights(); + weights.nodeTypeWeights.set(NodeAddress.empty, 32); + weights.edgeTypeWeights.set(EdgeAddress.empty, { + forwards: 7, + backwards: 9, + }); + weights.nodeManualWeights.set(NodeAddress.fromParts(["foo"]), 42); + const json = toJSON(weights); + const jsonString = stringify(json, {space: 4}); + expect(jsonString).toMatchInlineSnapshot(` +"[ + { + \\"type\\": \\"sourcecred/weights\\", + \\"version\\": \\"0.1.0\\" + }, + { + \\"edgeTypeWeights\\": { + \\"E\\\\u0000\\": { + \\"backwards\\": 9, + \\"forwards\\": 7 + } + }, + \\"nodeManualWeights\\": { + \\"N\\\\u0000foo\\\\u0000\\": 42 + }, + \\"nodeTypeWeights\\": { + \\"N\\\\u0000\\": 32 + } + } +]" +`); + expect(weights).toEqual(fromJSON(json)); + }); + }); +});