import { Graph, GraphNode } from "./Model"; import * as fs from "fs"; type GraphStorage = Graph & { $schema: string; }; export function save(fileName: string, graph: Graph): void { const content = JSON.stringify(prepareForWrite(graph)); fs.writeFileSync(fileName, content); } function prepareForWrite(graph: Graph): GraphStorage { return { ...graph, $schema: "./graph.schema.json" } } function parseGraph(data: any): Graph { return { nextId: parsePrimitiveProperty<Graph, number>("int", "nextId", 1, data), nodes: parseArrayProperty<Graph, GraphNode>("nodes", parseGraphNode, data), }; } function parseGraphNode(data: any): GraphNode { return { id: parsePrimitiveProperty<GraphNode, number>("int", "id", 0, data), name: parsePrimitiveProperty<GraphNode, string>("string", "name", "", data), description: parsePrimitiveProperty<GraphNode, string>("string", "description", "", data), }; } function parseArrayProperty<O, T>(fieldName: keyof O, itemParser: (item: any) => T, data: any): T[] { if(fieldName in data) { const valueList = data[fieldName]; if(Array.isArray(valueList)) { return valueList.map(itemParser); } } return []; } function parsePrimitiveProperty<O, T = string | number>(type: T extends string ? "string" : "int", fieldName: keyof O, defaultValue: T, obj: any): T { if(fieldName in obj) { const fieldValue = obj[fieldName]; if(typeof fieldValue === type) { return fieldValue; } } return defaultValue; } function canReadFile(path: string): boolean { try { fs.accessSync(path, fs.constants.R_OK); return true; } catch (error) { return false; } } const emptyGraph: () => Graph = () => ({ nodes: [], nextId: 1, }); export function load(fileName: string): Graph { if (!canReadFile(fileName)) { return emptyGraph(); } const content = fs.readFileSync(fileName).toString(); const data = JSON.parse(content); return parseGraph(data); }