import { isGroup, hasChildren } from '../guards'; import type { Attribute, Datatype, Entity, Group, ScalarShape, Shape, ArrayShape, NumericType, StringType, DType, BooleanType, ComplexType, CompoundType, LinkClass, UnresolvedEntity, GroupWithChildren, PrintableCompoundType, ChildEntity, } from '../models-hdf5'; import { EntityKind, DTypeClass, Endianness } from '../models-hdf5'; import type { NxInterpretation, SilxStyle } from '../models-nexus'; import { buildEntityPath } from '../utils'; import type { MockAttribute, MockDataset, MockValueId } from './models'; import { mockValues } from './values'; /* -------------------------- */ /* ----- TYPES & SHAPES ----- */ export const intType: NumericType = { class: DTypeClass.Integer, endianness: Endianness.LE, size: 32, }; export const unsignedType: NumericType = { class: DTypeClass.Unsigned, endianness: Endianness.LE, size: 16, }; export const floatType: NumericType = { class: DTypeClass.Float, endianness: Endianness.LE, size: 64, }; export const stringType: StringType = { class: DTypeClass.String, charSet: 'ASCII', }; export const compoundType: CompoundType = { class: DTypeClass.Compound, fields: { int: intType }, }; export const booleanType: BooleanType = { class: DTypeClass.Bool, }; export const complexType: ComplexType = { class: DTypeClass.Complex, realType: floatType, imagType: floatType, }; export const printableCompoundType: PrintableCompoundType = { class: DTypeClass.Compound, fields: { string: stringType, int: intType, float: floatType, bool: booleanType, complex: complexType, }, }; /* ---------------------- */ /* ----- ATTRIBUTES ----- */ export function makeAttr( name: string, type: T, shape: S, value: unknown ): MockAttribute { return { name, type, shape, value }; } export function makeScalarAttr( name: string, type: T, value: unknown ): Attribute { return makeAttr(name, type, [], value); } export function makeStrAttr( name: string, value: string ): Attribute { return makeScalarAttr(name, stringType, value); } export function makeIntAttr( name: string, value: number ): Attribute { return makeScalarAttr(name, intType, value); } export function withAttributes( entity: T, attributes: Attribute[] ): T { return { ...entity, attributes: [...entity.attributes, ...attributes], }; } export function withImageAttributes(entity: T): T { return withAttributes(entity, [ makeStrAttr('CLASS', 'IMAGE'), makeStrAttr('IMAGE_VERSION', '1.2'), ]); } /* -------------------- */ /* ----- ENTITIES ----- */ type EntityOpts = Partial>; type GroupOpts = EntityOpts & { isRoot?: boolean; children?: ChildEntity[] }; type DatasetOpts = EntityOpts & { valueId?: MockValueId }; function prefixChildrenPaths( group: GroupWithChildren, parentPath: string ): void { group.children.forEach((c) => { c.path = buildEntityPath(parentPath, c.path.slice(1)); if (isGroup(c) && hasChildren(c)) { prefixChildrenPaths(c, parentPath); } }); } export function makeGroup( name: string, children: ChildEntity[] = [], opts: Omit = {} ): GroupWithChildren { const { attributes = [], link, isRoot = false } = opts; const path = isRoot ? '/' : `/${name}`; const group: GroupWithChildren = { name, path, kind: EntityKind.Group, children, attributes, link, }; prefixChildrenPaths(group, path); return group; } export function makeDataset( name: string, type: T, shape: S, opts: DatasetOpts = {} ): MockDataset { const { attributes = [], valueId = name, link } = opts; return { name, path: `/${name}`, kind: EntityKind.Dataset, attributes, shape, type, value: mockValues[valueId as MockValueId], link, }; } export function makeScalarDataset( name: string, type: T, opts: DatasetOpts = {} ): MockDataset { return makeDataset(name, type, [], opts); } export function makeDatatype( name: string, type: T, opts: EntityOpts = {} ): Datatype { const { attributes = [], link } = opts; return { name, path: `/${name}`, kind: EntityKind.Datatype, attributes, type, link, }; } export function makeUnresolvedEntity( name: string, linkClass: LinkClass, pathToEntity?: string, file?: string ): UnresolvedEntity { return { name, path: `/${name}`, kind: EntityKind.Unresolved, attributes: [], link: { class: linkClass, file, path: pathToEntity }, }; } /* ----------------- */ /* ----- NEXUS ----- */ export function makeNxAxesAttr( axes: string[] ): Attribute { return makeAttr('axes', stringType, [axes.length], axes); } export function makeNxAuxAttr( aux: string[] ): Attribute { return makeAttr('auxiliary_signals', stringType, [aux.length], aux); } export function makeSilxStyleAttr( style: SilxStyle ): Attribute { const { signalScaleType, axisScaleTypes } = style; return makeStrAttr( 'SILX_style', JSON.stringify({ signal_scale_type: signalScaleType, axes_scale_type: axisScaleTypes, }) ); } export function makeNxGroup( name: string, type: 'NXroot' | 'NXentry' | 'NXprocess' | 'NXdata', opts: { defaultPath?: string } & GroupOpts = {} ): GroupWithChildren { const { defaultPath, children, ...groupOpts } = opts; return makeGroup(name, children, { ...groupOpts, attributes: [ ...(groupOpts.attributes ?? []), makeStrAttr('NX_class', type), ...(defaultPath ? [makeStrAttr('default', defaultPath)] : []), ], }); } export function makeNxDataGroup< T extends Record> >( name: string, opts: { signal: MockDataset; errors?: MockDataset; title?: MockDataset; silxStyle?: SilxStyle; } & ( | { axes: T; axesAttr: (Extract | '.')[] } | { axes?: never; axesAttr?: never } ) & ( | { auxiliary: T; auxAttr: Extract[] } | { auxiliary?: never; auxAttr?: never } ) & GroupOpts ): Group { const { signal, title, errors, silxStyle, axes = {}, axesAttr, auxiliary = {}, auxAttr, attributes = [], children = [], ...groupOpts } = opts; return makeNxGroup(name, 'NXdata', { ...groupOpts, attributes: [ makeStrAttr('signal', signal.name), ...(axesAttr ? [makeNxAxesAttr(axesAttr)] : []), ...(silxStyle ? [makeSilxStyleAttr(silxStyle)] : []), ...(auxAttr ? [makeNxAuxAttr(auxAttr)] : []), ...attributes, ], children: [ signal, ...(title ? [title] : []), ...(errors ? [errors] : []), ...Object.values(axes), ...Object.values(auxiliary), ...children, ], }); } export function makeNxDataset( name: string, type: T, dims: number[], opts: { interpretation?: string; longName?: string; units?: string; } & DatasetOpts = {} ): MockDataset { const { interpretation, longName, units, ...datasetOpts } = opts; return makeDataset(name, type, dims, { ...datasetOpts, attributes: [ ...(datasetOpts.attributes ?? []), ...(interpretation ? [makeStrAttr('interpretation', interpretation)] : []), ...(longName ? [makeStrAttr('long_name', longName)] : []), ...(units ? [makeStrAttr('units', units)] : []), ], }); } export function withNxInterpretation< T extends MockDataset >(dataset: T, interpretation: NxInterpretation): T { return withAttributes(dataset, [ makeStrAttr('interpretation', interpretation), ]); }