1 | const _ = require('lodash');
|
2 | const objectHash = require('object-hash');
|
3 | const flowTypes = require('./flow-types');
|
4 |
|
5 | const shouldOmit = {
|
6 | optional: true,
|
7 | variance: true,
|
8 | exact: true,
|
9 | static: true
|
10 | };
|
11 | const {omit, collectAllNodes, setTo} = require('./annotation-utils');
|
12 |
|
13 | function rustTypeBorrows(annotation) {
|
14 | switch (annotation.type) {
|
15 | case 'GenericTypeAnnotation':
|
16 | case 'ObjectTypeAnnotation':
|
17 | case 'UnionTypeAnnotation':
|
18 | return true;
|
19 | default:
|
20 | return false;
|
21 | }
|
22 | }
|
23 |
|
24 | function isHashMap(annotation) {
|
25 | return annotation.type === 'ObjectTypeAnnotation' && annotation.indexers.length === 1;
|
26 | }
|
27 |
|
28 | function isStruct(annotation) {
|
29 | return annotation.type === 'ObjectTypeAnnotation' && annotation.indexers.length === 0;
|
30 | }
|
31 |
|
32 |
|
33 | function flowAnnotationToRustType(annotation) {
|
34 | const fail = () => {
|
35 | throw new Error(`unknown annotation ${JSON.stringify(annotation, null, 2)}`);
|
36 | };
|
37 | switch (annotation.type) {
|
38 | case 'NullableTypeAnnotation':
|
39 | return `Option<${flowAnnotationToRustType(annotation.typeAnnotation)}>`;
|
40 | case 'VoidTypeAnnotation':
|
41 | return '()';
|
42 | case 'BooleanTypeAnnotation':
|
43 | return 'bool';
|
44 | case 'StringTypeAnnotation':
|
45 | return 'StringSymbol';
|
46 | case 'NumberTypeAnnotation':
|
47 | return 'f64';
|
48 | case 'NullLiteralTypeAnnotation':
|
49 | return 'Option<f64>';
|
50 | case 'UnionTypeAnnotation':
|
51 | return `enum ${annotation.name} {
|
52 | ${annotation.types
|
53 | .map((type, index) => {
|
54 | const name = _.upperFirst(_.get(type, 'name.id', `e${index}`));
|
55 | return `${name}(${flowAnnotationToRustType(type)})`;
|
56 | })
|
57 | .join('\n')}
|
58 | }`;
|
59 | case 'GenericTypeAnnotation':
|
60 | return annotation.id.name;
|
61 | case 'ObjectTypeProperty':
|
62 | return `#[serde(deserialize_state)]
|
63 | #[serde(serialize_state)]
|
64 | ${annotation.key.name}: ${flowAnnotationToRustType(annotation.value)},`;
|
65 | case 'ObjectTypeAnnotation':
|
66 | if (annotation.indexers.length === 0) {
|
67 | return `
|
68 | #[derive(Debug, DeserializeState,SerializeState, Default)]
|
69 | #[serde(de_parameters = "S")]
|
70 | #[serde(bound(deserialize = "S: ToSymbol"))]
|
71 | #[serde(deserialize_state = "S")]
|
72 | #[serde(serialize_state = "StringInterner<StringSymbol>")]
|
73 | struct ${annotation.name} {
|
74 | ${annotation.properties.map(t => flowAnnotationToRustType(t)).join('\n')}
|
75 | }`;
|
76 | } else if (annotation.indexers.length === 1) {
|
77 | return `HashMap<${flowAnnotationToRustType(annotation.indexers[0].key)},${flowAnnotationToRustType(
|
78 | annotation.indexers[0].value
|
79 | )}>`;
|
80 | }
|
81 | fail();
|
82 | default:
|
83 | fail();
|
84 | }
|
85 | }
|
86 |
|
87 | function sortUnionsAndProperties({expr, types}) {
|
88 | collectAllNodes({expr, types}, node => node.type === 'UnionTypeAnnotation').forEach(node =>
|
89 | setTo(node, Object.assign({}, node, {types: _.sortBy(node.types, objectHash)}))
|
90 | );
|
91 | collectAllNodes({expr, types}, node => node.type === 'ObjectTypeAnnotation').forEach(node =>
|
92 | setTo(node, Object.assign({}, node, {properties: _.sortBy(node.properties, objectHash)}))
|
93 | );
|
94 | return {expr, types};
|
95 | }
|
96 |
|
97 | function replaceTypeRefsWithFull({expr, types}) {
|
98 | collectAllNodes(
|
99 | {expr, types},
|
100 | node => node.type === 'GenericTypeAnnotation' && node.id.type === 'Identifier' && types[node.id.name]
|
101 | ).forEach(node => {
|
102 | setTo(node, _.cloneDeep(types[node.id.name]));
|
103 | });
|
104 | return {expr, types};
|
105 | }
|
106 |
|
107 | function replaceFullWithTypeRefs({expr, types}) {
|
108 | const hashToTypes = _(types)
|
109 | .mapValues(type => objectHash(type))
|
110 | .invert()
|
111 | .value();
|
112 | const cnt = 0;
|
113 | const allNodesWithTypes = collectAllNodes(
|
114 | {expr, types},
|
115 | node => node.type === 'UnionTypeAnnotation' || node.type === 'ObjectTypeAnnotation' && node.indexers.length === 0
|
116 | );
|
117 |
|
118 | const allNodesHashed = allNodesWithTypes.map(node => objectHash(node));
|
119 |
|
120 | allNodesWithTypes.forEach((node, idx) => {
|
121 | const hash = allNodesHashed[idx];
|
122 | if (!hashToTypes[hash]) {
|
123 | const typeName = `Type${hash}`;
|
124 | hashToTypes[hash] = `Type${hash}`;
|
125 | types[typeName] = node;
|
126 | }
|
127 | });
|
128 | allNodesWithTypes.forEach((node, idx) => {
|
129 | const hash = allNodesHashed[idx];
|
130 | if (node !== types[hashToTypes[hash]]) {
|
131 | setTo(node, {
|
132 | type: 'GenericTypeAnnotation',
|
133 | typeParameters: null,
|
134 | id: {type: 'Identifier', name: hashToTypes[hash]}
|
135 | });
|
136 | }
|
137 | });
|
138 | _.forEach(types, (type, name) => type.name = name);
|
139 | return {expr, types};
|
140 | }
|
141 |
|
142 | const extractionPipeline = [replaceTypeRefsWithFull, sortUnionsAndProperties, replaceFullWithTypeRefs];
|
143 |
|
144 | function extractAllTypeDeclerations(annotations) {
|
145 | for (let i = 0; i < extractionPipeline.length; i++) {
|
146 | annotations = extractionPipeline[i](_.cloneDeep(annotations));
|
147 | }
|
148 |
|
149 | return annotations;
|
150 | }
|
151 |
|
152 | module.exports = {flowAnnotationToRustType, extractAllTypeDeclerations, rustTypeBorrows, isHashMap, isStruct};
|