From f693fa9ba2c170e112fb5a6d10406eb124f675f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mose=20M=C3=BCller?= Date: Thu, 16 Nov 2023 09:14:01 +0100 Subject: [PATCH] frontend: adds stateUtils module --- frontend/src/utils/stateUtils.ts | 148 +++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 frontend/src/utils/stateUtils.ts diff --git a/frontend/src/utils/stateUtils.ts b/frontend/src/utils/stateUtils.ts new file mode 100644 index 0000000..5fd2161 --- /dev/null +++ b/frontend/src/utils/stateUtils.ts @@ -0,0 +1,148 @@ +export interface SerializedValue { + type: string; + value: Record | Array>; + readonly: boolean; + doc: string | null; + async?: boolean; + parameters?: unknown; +} +export type State = Record | null; + +export function setNestedValueByPath( + serializationDict: Record, + path: string, + serializedValue: SerializedValue +): Record { + const parentPathParts = path.split('.').slice(0, -1); + const attrName = path.split('.').pop(); + + if (!attrName) { + throw new Error('Invalid path'); + } + + let currentSerializedValue: SerializedValue; + const newSerializationDict: Record = JSON.parse( + JSON.stringify(serializationDict) + ); + + let currentDict = newSerializationDict; + + try { + for (const pathPart of parentPathParts) { + currentSerializedValue = getNextLevelDictByKey(currentDict, pathPart, false); + // @ts-expect-error The value will be of type SerializedValue as we are still + // looping through the parent parts + currentDict = currentSerializedValue['value']; + } + + currentSerializedValue = getNextLevelDictByKey(currentDict, attrName, true); + + Object.assign(currentSerializedValue, serializedValue); + return newSerializationDict; + } catch (error) { + console.error(error); + return currentDict; + } +} + +function getNextLevelDictByKey( + serializationDict: Record, + attrName: string, + allowAppend: boolean = false +): SerializedValue { + const [key, index] = parseListAttrAndIndex(attrName); + let currentDict: SerializedValue; + + try { + if (index !== null) { + if (!serializationDict[key] || !Array.isArray(serializationDict[key]['value'])) { + throw new Error(`Expected an array at '${key}', but found something else.`); + } + + if (index < serializationDict[key]['value'].length) { + currentDict = serializationDict[key]['value'][index]; + } else if (allowAppend && index === serializationDict[key]['value'].length) { + // Appending to list + // @ts-expect-error When the index is not null, I expect an array + serializationDict[key]['value'].push({}); + currentDict = serializationDict[key]['value'][index]; + } else { + throw new Error(`Index out of range for '${key}[${index}]'.`); + } + } else { + if (!serializationDict[key]) { + throw new Error(`Key '${key}' not found.`); + } + currentDict = serializationDict[key]; + } + } catch (error) { + throw new Error(`Error occurred trying to access '${attrName}': ${error}`); + } + + if (typeof currentDict !== 'object' || currentDict === null) { + throw new Error( + `Expected a dictionary at '${attrName}', but found type '${typeof currentDict}' instead.` + ); + } + + return currentDict; +} + +function parseListAttrAndIndex(attrString: string): [string, number | null] { + let index: number | null = null; + let attrName = attrString; + + if (attrString.includes('[') && attrString.endsWith(']')) { + const parts = attrString.split('['); + attrName = parts[0]; + const indexPart = parts[1].slice(0, -1); // Removes the closing ']' + + if (!isNaN(parseInt(indexPart))) { + index = parseInt(indexPart); + } else { + console.error(`Invalid index format in key: ${attrString}`); + } + } + + return [attrName, index]; +} + +const serializationDict = { + attr_list: { + type: 'list', + value: [ + { type: 'int', value: 1, readonly: false, doc: null }, + { type: 'int', value: 2, readonly: false, doc: null }, + { + type: 'Quantity', + value: { magnitude: 1.0, unit: 'ms' }, + readonly: false, + doc: null + } + ], + readonly: false, + doc: null + }, + read_sensor_data: { + type: 'method', + value: null, + readonly: true, + doc: null, + async: true, + parameters: {} + }, + readout_wait_time: { + type: 'Quantity', + value: { magnitude: 1.0, unit: 'ms' }, + readonly: false, + doc: null + } +}; +const attrName: string = 'attr_list[2]'; // example attribute name + +try { + const result = getNextLevelDictByKey(serializationDict, attrName); + console.log(result); +} catch (error) { + console.error(error); +}