Skip to content

Commit eb5fa9d

Browse files
committed
stricter type checks for section interfaces
Signed-off-by: Teo Koon Peng <[email protected]>
1 parent fe28715 commit eb5fa9d

13 files changed

+179
-98
lines changed

diagram-editor/frontend/add-operation.tsx

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,7 @@ import {
77
import React from 'react';
88
import { v4 as uuidv4 } from 'uuid';
99
import { EditorMode, useEditorMode } from './editor-mode';
10-
import type {
11-
DiagramEditorNode,
12-
SectionInterfaceNode,
13-
SectionInterfaceNodeTypes,
14-
} from './nodes';
10+
import type { DiagramEditorNode } from './nodes';
1511
import {
1612
BufferAccessIcon,
1713
BufferIcon,
@@ -23,16 +19,19 @@ import {
2319
NodeIcon,
2420
ScopeIcon,
2521
SectionBufferIcon,
22+
type SectionBufferNode,
2623
SectionInputIcon,
24+
type SectionInputNode,
2725
SectionOutputIcon,
26+
type SectionOutputNode,
2827
SerializedJoinIcon,
2928
SplitIcon,
3029
TransformIcon,
3130
UnzipIcon,
3231
} from './nodes';
3332
import type { DiagramOperation } from './types/api';
34-
import { joinNamespaces, ROOT_NAMESPACE } from './utils';
3533
import { calculateScopeBounds, LAYOUT_OPTIONS } from './utils/layout';
34+
import { joinNamespaces, ROOT_NAMESPACE } from './utils/namespace';
3635

3736
const StyledOperationButton = styled(Button)({
3837
justifyContent: 'flex-start',
@@ -44,18 +43,48 @@ export interface AddOperationProps {
4443
onAdd?: (change: NodeAddChange<DiagramEditorNode>[]) => void;
4544
}
4645

47-
function createSectionInterfaceChange(
48-
type: SectionInterfaceNodeTypes,
46+
function createSectionInputChange(
4947
remappedId: string,
5048
targetId: string,
5149
position: XYPosition,
52-
): NodeAddChange<SectionInterfaceNode> {
50+
): NodeAddChange<SectionInputNode> {
5351
return {
5452
type: 'add',
5553
item: {
5654
id: uuidv4(),
57-
type,
58-
data: { namespace: ROOT_NAMESPACE, remappedId, targetId },
55+
type: 'sectionInput',
56+
data: { remappedId, targetId },
57+
position,
58+
},
59+
};
60+
}
61+
62+
function createSectionOutputChange(
63+
outputId: string,
64+
position: XYPosition,
65+
): NodeAddChange<SectionOutputNode> {
66+
return {
67+
type: 'add',
68+
item: {
69+
id: uuidv4(),
70+
type: 'sectionOutput',
71+
data: { outputId },
72+
position,
73+
},
74+
};
75+
}
76+
77+
function createSectionBufferChange(
78+
remappedId: string,
79+
targetId: string,
80+
position: XYPosition,
81+
): NodeAddChange<SectionBufferNode> {
82+
return {
83+
type: 'add',
84+
item: {
85+
id: uuidv4(),
86+
type: 'sectionBuffer',
87+
data: { remappedId, targetId },
5988
position,
6089
},
6190
};
@@ -177,10 +206,9 @@ function AddOperation({ parentId, newNodePosition, onAdd }: AddOperationProps) {
177206
startIcon={<SectionInputIcon />}
178207
onClick={() => {
179208
onAdd?.([
180-
createSectionInterfaceChange(
181-
'sectionInput',
182-
'input',
183-
'input',
209+
createSectionInputChange(
210+
'new_input',
211+
'new_input',
184212
newNodePosition,
185213
),
186214
]);
@@ -195,12 +223,7 @@ function AddOperation({ parentId, newNodePosition, onAdd }: AddOperationProps) {
195223
startIcon={<SectionOutputIcon />}
196224
onClick={() => {
197225
onAdd?.([
198-
createSectionInterfaceChange(
199-
'sectionOutput',
200-
'output',
201-
'output',
202-
newNodePosition,
203-
),
226+
createSectionOutputChange('new_output', newNodePosition),
204227
]);
205228
}}
206229
>
@@ -213,10 +236,9 @@ function AddOperation({ parentId, newNodePosition, onAdd }: AddOperationProps) {
213236
startIcon={<SectionBufferIcon />}
214237
onClick={() => {
215238
onAdd?.([
216-
createSectionInterfaceChange(
217-
'sectionBuffer',
218-
'buffer',
219-
'buffer',
239+
createSectionBufferChange(
240+
'new_buffer',
241+
'new_buffer',
220242
newNodePosition,
221243
),
222244
]);

diagram-editor/frontend/auto-layout-button.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import React from 'react';
44
import type { DiagramEditorEdge } from './edges';
55
import type { DiagramEditorNode } from './nodes';
66
import { MaterialSymbol } from './nodes';
7-
import { autoLayout } from './utils';
7+
import { autoLayout } from './utils/auto-layout';
88
import { LAYOUT_OPTIONS } from './utils/layout';
99

1010
export interface AutoLayoutButtonProps {

diagram-editor/frontend/diagram-editor.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ import {
4747
type OperationNode,
4848
} from './nodes';
4949
import { useTemplates } from './templates-provider';
50-
import { exhaustiveCheck, allowEdges as getAllowEdges } from './utils';
5150
import { autoLayout } from './utils/auto-layout';
51+
import { allowEdges as getAllowEdges } from './utils/connection';
52+
import { exhaustiveCheck } from './utils/exhaustive-check';
5253
import { calculateScopeBounds, LAYOUT_OPTIONS } from './utils/layout';
5354
import { loadDiagramJson, loadEmpty, loadTemplate } from './utils/load-diagram';
5455

diagram-editor/frontend/forms/edit-node-form.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useArgs } from '@storybook/preview-api';
22
import type { Meta, StoryObj } from 'storybook-react-rsbuild';
3-
import { ROOT_NAMESPACE } from '../utils';
3+
import { ROOT_NAMESPACE } from '../utils/namespace';
44
import EditNodeForm from './edit-node-form';
55

66
const meta: Meta<typeof EditNodeForm> = {

diagram-editor/frontend/forms/transform-form.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useArgs } from '@storybook/preview-api';
22
import type { Meta, StoryObj } from 'storybook-react-rsbuild';
3-
import { ROOT_NAMESPACE } from '../utils';
3+
import { ROOT_NAMESPACE } from '../utils/namespace';
44
import TransformForm from './transform-form';
55

66
const meta: Meta<typeof TransformForm> = {

diagram-editor/frontend/node-manager.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,9 @@ import {
66
TERMINATE_ID,
77
} from './nodes';
88
import type { NextOperation } from './types/api';
9-
import {
10-
exhaustiveCheck,
11-
isBuiltin,
12-
joinNamespaces,
13-
ROOT_NAMESPACE,
14-
} from './utils';
9+
import { exhaustiveCheck } from './utils/exhaustive-check';
10+
import { joinNamespaces, ROOT_NAMESPACE } from './utils/namespace';
11+
import { isBuiltin } from './utils/operation';
1512

1613
export class NodeManager {
1714
private nodeIdMap: Map<string, DiagramEditorNode> = new Map();
@@ -66,6 +63,9 @@ export class NodeManager {
6663
return this.getNodeFromNamespaceOpId(ROOT_NAMESPACE, opId);
6764
}
6865

66+
/**
67+
* @returns returns `null` if the next operation points to a builtin node not rendered in the editor (e.g. `dispose`, `cancel`).
68+
*/
6969
getNodeFromNextOp(
7070
namespace: string,
7171
nextOp: NextOperation,

diagram-editor/frontend/nodes/icons.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Box, type BoxProps } from '@mui/material';
22
import type React from 'react';
33
import type { DiagramOperation } from '../types/api';
4-
import { exhaustiveCheck } from '../utils';
4+
import { exhaustiveCheck } from '../utils/exhaustive-check';
55

66
export interface MaterialSymbolProps extends BoxProps {
77
symbol: string;

diagram-editor/frontend/nodes/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,14 @@ import UnzipNode from './unzip-node';
2424

2525
export * from './icons';
2626
export type {
27-
SectionInterfaceData,
27+
SectionBufferData,
28+
SectionBufferNode,
29+
SectionInputData,
30+
SectionInputNode,
2831
SectionInterfaceNode,
2932
SectionInterfaceNodeTypes,
33+
SectionOutputData,
34+
SectionOutputNode,
3035
} from './section-node';
3136
export * from './utils';
3237

diagram-editor/frontend/nodes/section-node.tsx

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { NodeProps } from '@xyflow/react';
22
import type { NextOperation } from '../types/api';
33
import type { Node } from '../types/react-flow';
4-
import { isSectionBuilder, type ROOT_NAMESPACE } from '../utils';
4+
import { isSectionBuilder } from '../utils/operation';
55
import type { OperationNode } from '.';
66
import BaseNode from './base-node';
77
import {
@@ -11,13 +11,28 @@ import {
1111
SectionOutputIcon,
1212
} from './icons';
1313

14-
export type SectionInterfaceData = {
15-
// section interfaces is always in the root namespace
16-
namespace: typeof ROOT_NAMESPACE;
14+
export type SectionInputData = {
1715
remappedId: string;
1816
targetId: NextOperation;
1917
};
2018

19+
export type SectionInputNode = Node<SectionInputData, 'sectionInput'>;
20+
21+
export type SectionOutputData = {
22+
outputId: string;
23+
};
24+
25+
export type SectionOutputNode = Node<SectionOutputData, 'sectionOutput'>;
26+
27+
export type SectionBufferData = SectionInputData;
28+
29+
export type SectionBufferNode = Node<SectionBufferData, 'sectionBuffer'>;
30+
31+
export type SectionInterfaceNode =
32+
| SectionInputNode
33+
| SectionOutputNode
34+
| SectionBufferNode;
35+
2136
export function SectionNode(props: NodeProps<OperationNode<'section'>>) {
2237
const label = isSectionBuilder(props.data.op)
2338
? props.data.op.builder
@@ -39,13 +54,7 @@ export type SectionInterfaceNodeTypes =
3954
| 'sectionOutput'
4055
| 'sectionBuffer';
4156

42-
export type SectionInterfaceNode<
43-
K extends SectionInterfaceNodeTypes = SectionInterfaceNodeTypes,
44-
> = Node<SectionInterfaceData, K>;
45-
46-
export function SectionInputNode(
47-
props: NodeProps<SectionInterfaceNode<'sectionInput'>>,
48-
) {
57+
export function SectionInputNode(props: NodeProps<SectionInputNode>) {
4958
return (
5059
<BaseNode
5160
{...props}
@@ -57,23 +66,19 @@ export function SectionInputNode(
5766
);
5867
}
5968

60-
export function SectionOutputNode(
61-
props: NodeProps<SectionInterfaceNode<'sectionOutput'>>,
62-
) {
69+
export function SectionOutputNode(props: NodeProps<SectionOutputNode>) {
6370
return (
6471
<BaseNode
6572
{...props}
6673
color="secondary"
6774
icon={<SectionOutputIcon />}
68-
label={props.data.remappedId}
75+
label="Output"
6976
variant="input"
7077
/>
7178
);
7279
}
7380

74-
export function SectionBufferNode(
75-
props: NodeProps<SectionInterfaceNode<'sectionBuffer'>>,
76-
) {
81+
export function SectionBufferNode(props: NodeProps<SectionBufferNode>) {
7782
return (
7883
<BaseNode
7984
{...props}
Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,51 @@
1-
import type { BuiltinNode, DiagramEditorNode, OperationNode } from '.';
1+
import type {
2+
BuiltinNode,
3+
BuiltinNodeTypes,
4+
DiagramEditorNode,
5+
OperationNode,
6+
OperationNodeTypes,
7+
} from '.';
28

39
export function isOperationData(
410
data: DiagramEditorNode['data'],
511
): data is OperationNode['data'] {
612
return 'type' in data;
713
}
814

15+
const OPERATION_NODE_TYPES = Object.keys({
16+
buffer: null,
17+
buffer_access: null,
18+
fork_clone: null,
19+
fork_result: null,
20+
join: null,
21+
listen: null,
22+
node: null,
23+
scope: null,
24+
section: null,
25+
serialized_join: null,
26+
split: null,
27+
stream_out: null,
28+
transform: null,
29+
unzip: null,
30+
} satisfies Record<OperationNodeTypes, null>);
31+
932
export function isOperationNode(
1033
node: DiagramEditorNode,
1134
): node is OperationNode {
12-
return node.type ? !['start', 'terminate'].includes(node.type) : false;
35+
return OPERATION_NODE_TYPES.includes(node.type);
1336
}
1437

38+
const BUILTIN_NODE_TYPES = Object.keys({
39+
start: null,
40+
terminate: null,
41+
} satisfies Record<BuiltinNodeTypes, null>);
42+
1543
export function isBuiltinNode(node: DiagramEditorNode): node is BuiltinNode {
16-
return node.type ? ['start', 'terminate'].includes(node.type) : false;
44+
return BUILTIN_NODE_TYPES.includes(node.type);
45+
}
46+
47+
export function isScopeNode(
48+
node: DiagramEditorNode,
49+
): node is OperationNode<'scope'> {
50+
return node.type === 'scope';
1751
}

0 commit comments

Comments
 (0)