Skip to content

Commit d02c215

Browse files
committedJan 4, 2024
devtools: refactor tree
1 parent 3026d25 commit d02c215

File tree

2 files changed

+110
-61
lines changed

2 files changed

+110
-61
lines changed
 

‎tmtc-c2a/devtools_frontend/src/components/TelemetryView.tsx

+9-13
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,20 @@
11
import React, { useCallback, useEffect, useMemo, useState } from "react";
2-
import {
3-
TreeBlueprintNamespace,
4-
TreeNamespace,
5-
buildTree,
6-
digTreeBlueprintNamespace,
7-
} from "../tree";
2+
import { TreeNamespace, addToNamespace, mapNamespace } from "../tree";
83

94
import { Tmiv, TmivField } from "../proto/tco_tmiv";
105
import { useClient } from "./Layout";
116
import { useParams } from "react-router-dom";
127
import { Helmet } from "react-helmet-async";
138
import { TelemetrySchema } from "../proto/tmtc_generic_c2a";
149

15-
const buildTelemetryFieldTreeBlueprintFromSchema = (tlm: TelemetrySchema) => {
10+
const buildTelemetryFieldTreeBlueprintFromSchema = (
11+
tlm: TelemetrySchema,
12+
): TreeNamespace<undefined> => {
1613
const fieldNames = tlm.fields.map((f) => f.name);
17-
const root: TreeBlueprintNamespace = new Map();
14+
const root: TreeNamespace<undefined> = new Map();
1815
for (const fieldName of fieldNames) {
1916
const path = fieldName.split(".");
20-
const basename = path.pop()!;
21-
const ns = digTreeBlueprintNamespace(root, path);
22-
ns.set(basename, { type: "leaf", key: fieldName });
17+
addToNamespace(root, path, undefined);
2318
}
2419
return root;
2520
};
@@ -30,7 +25,7 @@ type TelemetryValuePair = {
3025
};
3126

3227
const buildTelemetryFieldTree = (
33-
blueprint: TreeBlueprintNamespace,
28+
blueprint: TreeNamespace<undefined>,
3429
fields: TmivField[],
3530
): TreeNamespace<TelemetryValuePair> => {
3631
const convertedFieldMap = new Map<string, TmivField["value"]>();
@@ -43,7 +38,8 @@ const buildTelemetryFieldTree = (
4338
convertedFieldMap.set(field.name, field.value);
4439
}
4540
}
46-
return buildTree(blueprint, (key) => {
41+
return mapNamespace(blueprint, (path, _key) => {
42+
const key = path.join(".");
4743
const converted = convertedFieldMap.get(key) ?? null;
4844
const raw = rawFieldMap.get(key) ?? null;
4945
return { converted, raw };
+101-48
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,109 @@
1-
export type TreeBlueprintNamespace = Map<string, TreeBlueprintNode>;
2-
export type TreeBlueprintNode =
3-
| { type: "leaf"; key: string }
4-
| { type: "ns"; ns: TreeBlueprintNamespace };
5-
export const digTreeBlueprintNamespace = (
6-
ns: TreeBlueprintNamespace,
7-
path: string[],
8-
): TreeBlueprintNamespace => {
9-
if (path.length === 0) {
10-
return ns;
11-
}
12-
const [pathHead, ...pathTail] = path;
13-
const childNs = (() => {
14-
if (ns.has(pathHead)) {
15-
const node = ns.get(pathHead)!;
16-
switch (node.type) {
17-
case "ns":
18-
return node.ns;
19-
case "leaf": {
20-
const childNs: TreeBlueprintNamespace = new Map();
21-
childNs.set("", node);
22-
ns.set(pathHead, { type: "ns", ns: childNs });
23-
return childNs;
24-
}
25-
}
26-
} else {
27-
const childNs: TreeBlueprintNamespace = new Map();
28-
ns.set(pathHead, { type: "ns", ns: childNs });
29-
return childNs;
30-
}
31-
})();
32-
return digTreeBlueprintNamespace(childNs, pathTail);
33-
};
34-
351
export type TreeNamespace<T> = Map<string, TreeNode<T>>;
362
export type TreeNode<T> =
373
| { type: "leaf"; value: T }
384
| { type: "ns"; ns: TreeNamespace<T> };
395

40-
export const buildTree = <T>(
41-
blueprint: TreeBlueprintNamespace,
42-
getValue: (key: string) => T,
43-
): TreeNamespace<T> => {
44-
const map = new Map();
45-
for (const [key, value] of blueprint) {
46-
switch (value.type) {
47-
case "leaf":
48-
map.set(key, { type: "leaf", value: getValue(value.key) });
49-
break;
50-
case "ns":
51-
map.set(key, { type: "ns", ns: buildTree(value.ns, getValue) });
52-
break;
6+
const makeLeaf = <T>(value: T): TreeNode<T> => ({ type: "leaf", value });
7+
const makeNs = <T>(ns: TreeNamespace<T>): TreeNode<T> => ({ type: "ns", ns });
8+
9+
const mapMapWithKey = <K, T, U>(
10+
map: Map<K, T>,
11+
f: (key: K, value: T) => U,
12+
): Map<K, U> => {
13+
const result = new Map();
14+
for (const [key, value] of map) {
15+
result.set(key, f(key, value));
16+
}
17+
return result;
18+
};
19+
20+
const mapTreeRec = <T, U>(
21+
tree: TreeNode<T>,
22+
path: string[],
23+
f: (path: string[], node: T) => U,
24+
): TreeNode<U> => {
25+
switch (tree.type) {
26+
case "leaf":
27+
return makeLeaf(f(path, tree.value));
28+
case "ns":
29+
return makeNs(
30+
mapMapWithKey(tree.ns, (key, child) =>
31+
mapTreeRec(child, [...path, key], f),
32+
),
33+
);
34+
}
35+
};
36+
37+
export const mapTree = <T, U>(
38+
tree: TreeNode<T>,
39+
f: (path: string[], node: T) => U,
40+
): TreeNode<U> => {
41+
return mapTreeRec(tree, [], f);
42+
};
43+
44+
export const mapNamespace = <T, U>(
45+
ns: TreeNamespace<T>,
46+
f: (path: string[], node: T) => U,
47+
): TreeNamespace<U> => {
48+
const tree = makeNs(ns);
49+
const mappedTree = mapTreeRec(tree, [], f);
50+
if (mappedTree.type === "ns") {
51+
return mappedTree.ns;
52+
} else {
53+
throw new Error("Impossible");
54+
}
55+
};
56+
57+
export const singleton = <T>(path: string[], value: T): TreeNode<T> => {
58+
let tree: TreeNode<T> = { type: "leaf", value };
59+
for (const key of path.toReversed()) {
60+
const ns = new Map();
61+
ns.set(key, tree);
62+
tree = { type: "ns", ns };
63+
}
64+
return tree;
65+
};
66+
67+
export const add = <T>(
68+
tree: TreeNode<T>,
69+
path: string[],
70+
value: T,
71+
): TreeNode<T> => {
72+
switch (tree.type) {
73+
case "leaf":
74+
if (path.length === 0) {
75+
tree.value = value;
76+
return tree;
77+
} else {
78+
const [pathHead, ...pathTail] = path;
79+
const ns = new Map();
80+
ns.set("", tree);
81+
ns.set(pathHead, singleton(pathTail, value));
82+
return {
83+
type: "ns",
84+
ns,
85+
};
86+
}
87+
case "ns":
88+
addToNamespace(tree.ns, path, value);
89+
return tree;
90+
}
91+
};
92+
93+
export const addToNamespace = <T>(
94+
ns: TreeNamespace<T>,
95+
path: string[],
96+
value: T,
97+
): void => {
98+
if (path.length === 0) {
99+
ns.set("", { type: "leaf", value });
100+
} else {
101+
const [pathHead, ...pathTail] = path;
102+
const child = ns.get(pathHead);
103+
if (child === undefined) {
104+
ns.set(pathHead, singleton(pathTail, value));
105+
} else {
106+
ns.set(pathHead, add(child, pathTail, value));
53107
}
54108
}
55-
return map;
56109
};

0 commit comments

Comments
 (0)
Please sign in to comment.