Files
react-native/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/StructCollector.js
T
Christoph Purrer b444f0e44e react-native-code-gen Add Enum Type support for C++ TurboModules
Summary:
There are cases where we want to pass an enum type into a TurboModule method which is handy to restrict certain arguments to a restricted set of values, e.g. restricting quality to ```enum Quality { SD, HD, }```

Approach:
- We are not generating an ```enum``` type in C++ but rather just cast type safe to the corresponding member type.

- We don't support mixed enum types at this time, e.g. ```export enum StringOption { One = 'one', Two = 2, Three = 'three', };``` will not work. See: https://www.typescriptlang.org/docs/handbook/enums.html#heterogeneous-enums

- We only support untyped (default to String), String, and Number enum properties

This is for C++ only, Java and ObjC are not supported atm.

Changelog:
[General][Added] - react-native-code-gen Add Enum Type support for C++ TurboModules

Reviewed By: RSNara

Differential Revision: D38880963

fbshipit-source-id: f2399b29948306bc555429b6f96c43ea4c39c46e
2022-08-30 00:48:06 -07:00

207 lines
5.5 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
'use strict';
import type {
Nullable,
NativeModuleObjectTypeAnnotation,
NativeModuleStringTypeAnnotation,
NativeModuleNumberTypeAnnotation,
NativeModuleInt32TypeAnnotation,
NativeModuleDoubleTypeAnnotation,
NativeModuleFloatTypeAnnotation,
NativeModuleBooleanTypeAnnotation,
NativeModuleGenericObjectTypeAnnotation,
ReservedTypeAnnotation,
NativeModuleTypeAliasTypeAnnotation,
NativeModuleArrayTypeAnnotation,
NativeModuleBaseTypeAnnotation,
} from '../../../CodegenSchema';
import type {AliasResolver} from '../Utils';
const {capitalize} = require('../../Utils');
const {
unwrapNullable,
wrapNullable,
} = require('../../../parsers/flow/modules/utils');
type StructContext = 'CONSTANTS' | 'REGULAR';
export type RegularStruct = $ReadOnly<{
context: 'REGULAR',
name: string,
properties: $ReadOnlyArray<StructProperty>,
}>;
export type ConstantsStruct = $ReadOnly<{
context: 'CONSTANTS',
name: string,
properties: $ReadOnlyArray<StructProperty>,
}>;
export type Struct = RegularStruct | ConstantsStruct;
export type StructProperty = $ReadOnly<{
name: string,
optional: boolean,
typeAnnotation: Nullable<StructTypeAnnotation>,
}>;
export type StructTypeAnnotation =
| NativeModuleStringTypeAnnotation
| NativeModuleNumberTypeAnnotation
| NativeModuleInt32TypeAnnotation
| NativeModuleDoubleTypeAnnotation
| NativeModuleFloatTypeAnnotation
| NativeModuleBooleanTypeAnnotation
| NativeModuleGenericObjectTypeAnnotation
| ReservedTypeAnnotation
| NativeModuleTypeAliasTypeAnnotation
| NativeModuleArrayTypeAnnotation<Nullable<StructTypeAnnotation>>;
class StructCollector {
_structs: Map<string, Struct> = new Map();
process(
structName: string,
structContext: StructContext,
resolveAlias: AliasResolver,
nullableTypeAnnotation: Nullable<NativeModuleBaseTypeAnnotation>,
): Nullable<StructTypeAnnotation> {
const [typeAnnotation, nullable] = unwrapNullable(nullableTypeAnnotation);
switch (typeAnnotation.type) {
case 'ObjectTypeAnnotation': {
this._insertStruct(
structName,
structContext,
resolveAlias,
typeAnnotation,
);
return wrapNullable(nullable, {
type: 'TypeAliasTypeAnnotation',
name: structName,
});
}
case 'ArrayTypeAnnotation': {
if (typeAnnotation.elementType == null) {
return wrapNullable(nullable, {
type: 'ArrayTypeAnnotation',
});
}
return wrapNullable(nullable, {
type: 'ArrayTypeAnnotation',
elementType: this.process(
structName + 'Element',
structContext,
resolveAlias,
typeAnnotation.elementType,
),
});
}
case 'TypeAliasTypeAnnotation': {
this._insertAlias(typeAnnotation.name, structContext, resolveAlias);
return wrapNullable(nullable, typeAnnotation);
}
case 'EnumDeclaration':
throw new Error('Enum types are unsupported in structs');
case 'MixedTypeAnnotation':
throw new Error('Mixed types are unsupported in structs');
case 'UnionTypeAnnotation':
throw new Error('Union types are unsupported in structs');
default: {
return wrapNullable(nullable, typeAnnotation);
}
}
}
_insertAlias(
aliasName: string,
structContext: StructContext,
resolveAlias: AliasResolver,
): void {
const usedStruct = this._structs.get(aliasName);
if (usedStruct == null) {
this._insertStruct(
aliasName,
structContext,
resolveAlias,
resolveAlias(aliasName),
);
} else if (usedStruct.context !== structContext) {
throw new Error(
`Tried to use alias '${aliasName}' in a getConstants() return type and inside a regular struct.`,
);
}
}
_insertStruct(
structName: string,
structContext: StructContext,
resolveAlias: AliasResolver,
objectTypeAnnotation: NativeModuleObjectTypeAnnotation,
): void {
// $FlowFixMe[missing-type-arg]
const properties = objectTypeAnnotation.properties.map<
$ReadOnly<{
name: string,
optional: boolean,
typeAnnotation: Nullable<StructTypeAnnotation>,
}>,
>(property => {
const propertyStructName = structName + capitalize(property.name);
return {
...property,
typeAnnotation: this.process(
propertyStructName,
structContext,
resolveAlias,
property.typeAnnotation,
),
};
});
switch (structContext) {
case 'REGULAR':
this._structs.set(structName, {
name: structName,
context: 'REGULAR',
properties: properties,
});
break;
case 'CONSTANTS':
this._structs.set(structName, {
name: structName,
context: 'CONSTANTS',
properties: properties,
});
break;
default:
(structContext: empty);
throw new Error(`Detected an invalid struct context: ${structContext}`);
}
}
getAllStructs(): $ReadOnlyArray<Struct> {
return [...this._structs.values()];
}
getStruct(name: string): ?Struct {
return this._structs.get(name);
}
}
module.exports = {
StructCollector,
};