Skip to content

Commit 87251d6

Browse files
committed
Type-check attribute objects and values
1 parent 4823e0a commit 87251d6

File tree

9 files changed

+237
-192
lines changed

9 files changed

+237
-192
lines changed

packages/app/src/explorer/utils.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import {
22
assertDefined,
33
assertNonNull,
4-
assertStr,
4+
hasScalarShape,
5+
hasStringType,
56
isGroup,
67
} from '@h5web/shared/guards';
78
import { type ChildEntity } from '@h5web/shared/hdf5-models';
89
import { type KeyboardEvent } from 'react';
910

1011
import { type AttrValuesStore } from '../providers/models';
11-
import { hasAttribute } from '../utils';
12+
import { findAttribute, getAttributeValue, hasAttribute } from '../utils';
1213

1314
const SUPPORTED_NX_CLASSES = new Set(['NXdata', 'NXentry', 'NXprocess']);
1415

@@ -26,13 +27,17 @@ export function needsNxBadge(
2627
return true;
2728
}
2829

29-
const nxClass = attrValuesStore.getSingle(entity, 'NX_class');
30-
if (nxClass) {
31-
assertStr(nxClass);
32-
return SUPPORTED_NX_CLASSES.has(nxClass);
30+
const nxClassAttr = findAttribute(entity, 'NX_class');
31+
if (
32+
!nxClassAttr ||
33+
!hasScalarShape(nxClassAttr) ||
34+
!hasStringType(nxClassAttr)
35+
) {
36+
return false;
3337
}
3438

35-
return false;
39+
const nxClass = getAttributeValue(entity, nxClassAttr, attrValuesStore);
40+
return SUPPORTED_NX_CLASSES.has(nxClass);
3641
}
3742

3843
function getButtonList(

packages/app/src/hooks.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { type DimensionMapping, getSliceSelection } from '@h5web/lib';
2-
import { assertDatasetValue, isDefined } from '@h5web/shared/guards';
2+
import { assertValue, isDefined } from '@h5web/shared/guards';
33
import {
44
type ArrayShape,
55
type Dataset,
@@ -48,7 +48,7 @@ export function useDatasetValue<D extends Dataset<ArrayShape | ScalarShape>>(
4848
// If `selection` is undefined, the entire dataset will be fetched
4949
const value = valuesStore.get({ dataset, selection });
5050

51-
assertDatasetValue(value, dataset);
51+
assertValue(value, dataset);
5252
return value;
5353
}
5454

@@ -74,7 +74,7 @@ export function useDatasetsValues<D extends Dataset<ArrayShape | ScalarShape>>(
7474
}
7575

7676
const value = valuesStore.get({ dataset, selection });
77-
assertDatasetValue(value, dataset);
77+
assertValue(value, dataset);
7878
return value;
7979
});
8080
}

packages/app/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ export {
167167
assertPrintableType,
168168
assertCompoundType,
169169
assertScalarValue,
170-
assertDatasetValue,
170+
assertValue as assertDatasetValue,
171171
} from '@h5web/shared/guards';
172172

173173
// Undocumented

packages/app/src/utils.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,33 @@
1-
import { type Entity } from '@h5web/shared/hdf5-models';
1+
import { assertValue } from '@h5web/shared/guards';
2+
import {
3+
type Attribute,
4+
type Entity,
5+
type Value,
6+
} from '@h5web/shared/hdf5-models';
27

3-
import { type AttrName } from './providers/models';
8+
import { type AttrName, type AttrValuesStore } from './providers/models';
49

510
export function hasAttribute(entity: Entity, attributeName: AttrName): boolean {
611
return entity.attributes.some((attr) => attr.name === attributeName);
712
}
813

14+
export function findAttribute(
15+
entity: Entity,
16+
attributeName: AttrName,
17+
): Attribute | undefined {
18+
return entity.attributes.find((attr) => attr.name === attributeName);
19+
}
20+
21+
export function getAttributeValue<A extends Attribute>(
22+
entity: Entity,
23+
attribute: A,
24+
attrValuesStore: AttrValuesStore,
25+
): Value<A> {
26+
const value = attrValuesStore.get(entity)[attribute.name];
27+
assertValue(value, attribute);
28+
return value;
29+
}
30+
931
export function enableBigIntSerialization(): void {
1032
// eslint-disable-next-line no-extend-native
1133
Object.defineProperty(BigInt.prototype, 'toJSON', {

packages/app/src/vis-packs/core/hooks.ts

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { type DimensionMapping } from '@h5web/lib';
22
import { createMemo } from '@h5web/shared/createMemo';
3-
import { isDefined } from '@h5web/shared/guards';
3+
import {
4+
hasNumericType,
5+
hasScalarShape,
6+
isDefined,
7+
isScalarShape,
8+
} from '@h5web/shared/guards';
49
import {
510
type ArrayValue,
611
type Dataset,
@@ -22,6 +27,7 @@ import {
2227
bigIntTypedArrayFromDType,
2328
typedArrayFromDType,
2429
} from '../../providers/utils';
30+
import { findAttribute, getAttributeValue } from '../../utils';
2531
import { applyMapping, getBaseArray, toNumArray } from './utils';
2632

2733
export const useToNumArray = createMemo(toNumArray);
@@ -99,27 +105,38 @@ export function useMappedArrays(
99105
export function useIgnoreFillValue(dataset: Dataset): IgnoreValue | undefined {
100106
const { attrValuesStore } = useDataContext();
101107

102-
const rawFillValue = attrValuesStore.getSingle(dataset, '_FillValue');
103-
104108
return useMemo(() => {
105-
const wrappedFillValue = castArray(rawFillValue);
109+
const fillValueAttr = findAttribute(dataset, '_FillValue');
110+
111+
if (
112+
!fillValueAttr ||
113+
!hasScalarShape(fillValueAttr) ||
114+
!hasNumericType(fillValueAttr)
115+
) {
116+
return undefined;
117+
}
118+
119+
const rawFillValue = getAttributeValue(
120+
dataset,
121+
fillValueAttr,
122+
attrValuesStore,
123+
);
106124

107125
const DTypedArray = bigIntTypedArrayFromDType(dataset.type)
108126
? Float64Array // matches `useToNumArray` logic
109127
: typedArrayFromDType(dataset.type);
110128

111129
// Cast fillValue in the type of the dataset values to be able to use `===` for the comparison
112-
const fillValue =
113-
DTypedArray && typeof wrappedFillValue[0] === 'number'
114-
? new DTypedArray(wrappedFillValue as number[])[0]
115-
: undefined;
130+
const fillValue = DTypedArray
131+
? new DTypedArray([Number(rawFillValue)])[0]
132+
: undefined;
116133

117134
if (fillValue === undefined) {
118135
return undefined;
119136
}
120137

121138
return (val) => val === fillValue;
122-
}, [dataset, rawFillValue]);
139+
}, [dataset, attrValuesStore]);
123140
}
124141

125142
export function useExportEntries<F extends ExportFormat[]>(

packages/app/src/vis-packs/nexus/containers/NxHeatmapContainer.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { DimensionMapper, getSliceSelection } from '@h5web/lib';
22
import {
3-
assertDatasetValue,
3+
assertValue,
44
assertGroup,
55
assertMinDims,
66
assertNumericLikeType,
@@ -81,7 +81,7 @@ function NxHeatmapContainer(props: VisContainerProps) {
8181
const { signal, axisValues, title } = nxValues;
8282

8383
if (hasComplexType(selectedDataset)) {
84-
assertDatasetValue(signal, selectedDataset);
84+
assertValue(signal, selectedDataset);
8585

8686
return (
8787
<MappedComplexHeatmapVis
@@ -98,7 +98,7 @@ function NxHeatmapContainer(props: VisContainerProps) {
9898
}
9999

100100
assertNumericLikeType(selectedDataset);
101-
assertDatasetValue(signal, selectedDataset);
101+
assertValue(signal, selectedDataset);
102102
return (
103103
<MappedHeatmapVis
104104
dataset={selectedDataset}

packages/shared/src/guards.test.ts

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { describe, expect, it } from 'vitest';
22

3-
import { assertDatasetValue, assertScalarValue } from './guards';
3+
import { assertValue, assertScalarValue } from './guards';
44
import {
55
boolType,
66
compoundType,
@@ -111,40 +111,34 @@ describe('assertScalarValue', () => {
111111
describe('assertDatasetValue', () => {
112112
it('should not throw when value satisfies dataset type and shape', () => {
113113
expect(() =>
114-
assertDatasetValue(0, dataset('foo', intType(), [])),
114+
assertValue(0, dataset('foo', intType(), [])),
115115
).not.toThrowError();
116116

117117
expect(() =>
118-
assertDatasetValue(0n, dataset('foo', intType(false, 64), [])),
118+
assertValue(0n, dataset('foo', intType(false, 64), [])),
119119
).not.toThrowError();
120120

121121
expect(() =>
122-
assertDatasetValue('', dataset('foo', strType(), [])),
122+
assertValue('', dataset('foo', strType(), [])),
123123
).not.toThrowError();
124124

125125
expect(() =>
126-
assertDatasetValue(
127-
[true, false],
128-
dataset('foo', boolType(intType()), [2]),
129-
),
126+
assertValue([true, false], dataset('foo', boolType(intType()), [2])),
130127
).not.toThrowError();
131128

132129
expect(() =>
133-
assertDatasetValue(
134-
Float32Array.from([0, 1]),
135-
dataset('foo', floatType(), [2]),
136-
),
130+
assertValue(Float32Array.from([0, 1]), dataset('foo', floatType(), [2])),
137131
).not.toThrowError();
138132

139133
expect(() =>
140-
assertDatasetValue(
134+
assertValue(
141135
BigInt64Array.from([0n, 1n]),
142136
dataset('foo', intType(true, 64), [2]),
143137
),
144138
).not.toThrowError();
145139

146140
expect(() =>
147-
assertDatasetValue(
141+
assertValue(
148142
Float32Array.from([0, 1]), // big ints can be returned as any kind of numbers
149143
dataset('foo', intType(true, 64), [2]),
150144
),
@@ -154,18 +148,15 @@ describe('assertDatasetValue', () => {
154148
describe('assertDatasetValue', () => {
155149
it("should throw when value doesn't satisfy dataset type and shape", () => {
156150
expect(() =>
157-
assertDatasetValue(
158-
true,
159-
dataset('foo', enumType(intType(), { FOO: 0 }), []),
160-
),
151+
assertValue(true, dataset('foo', enumType(intType(), { FOO: 0 }), [])),
161152
).toThrowError('Expected number');
162153

163154
expect(() =>
164-
assertDatasetValue(['foo', 'bar'], dataset('foo', intType(), [2])),
155+
assertValue(['foo', 'bar'], dataset('foo', intType(), [2])),
165156
).toThrowError('Expected number');
166157

167158
expect(() =>
168-
assertDatasetValue(
159+
assertValue(
169160
BigInt64Array.from([0n, 1n]),
170161
dataset('foo', intType(), [2]),
171162
),

0 commit comments

Comments
 (0)