-
Notifications
You must be signed in to change notification settings - Fork 35
/
plan-generator.ts
143 lines (122 loc) · 4.22 KB
/
plan-generator.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/**
* @license
* Copyright 2020 Google LLC.
* This code may only be used under the BSD style license found at
* http://polymer.github.io/LICENSE.txt
* Code distributed by Google as part of this project is also
* subject to an additional IP rights grant found at
* http://polymer.github.io/PATENTS.txt
*/
import {Recipe} from '../runtime/recipe/recipe.js';
import {Type} from '../runtime/type.js';
import {Particle} from '../runtime/recipe/particle.js';
import {KotlinGenerationUtils, quote, tryImport} from './kotlin-generation-utils.js';
import {HandleConnection} from '../runtime/recipe/handle-connection.js';
import {StorageKey} from '../runtime/storageNG/storage-key.js';
import {Direction} from '../runtime/manifest-ast-nodes.js';
const ktUtils = new KotlinGenerationUtils();
export class PlanGeneratorError extends Error {
constructor(message: string) {
super(message);
this.name = 'PlanGeneratorError';
}
}
/** Generates plan objects from resolved recipes. */
export class PlanGenerator {
constructor(private resolvedRecipes: Recipe[], private scope: string) {
}
/** Generates a Kotlin file with plan classes derived from resolved recipes. */
generate(): string {
const planOutline = [
this.fileHeader(),
...this.createPlans(),
this.fileFooter()
];
return planOutline.join('\n');
}
/** Converts a resolved recipe into a `Plan` object. */
createPlans(): string[] {
return this.resolvedRecipes.map(recipe => {
const planName = `${recipe.name}Plan`;
const particles = recipe.particles.map(p => this.createParticle(p));
const start = `object ${planName} : `;
return `${start}${ktUtils.applyFun('Plan', [ktUtils.listOf(particles)], start.length)}`;
});
}
/** Generates a Kotlin `Plan.Particle` instantiation from a Particle. */
createParticle(particle: Particle): string {
const spec = particle.spec;
const location = (spec && (spec.implBlobUrl || (spec.implFile && spec.implFile.replace('/', '.')))) || '';
const connectionMappings = Object.entries(particle.connections)
.map(([key, conn]) => `"${key}" to ${this.createHandleConnection(conn)}`);
return ktUtils.applyFun('Particle', [
quote(particle.name),
quote(location),
ktUtils.mapOf(connectionMappings, 12)
]);
}
/** Generates a Kotlin `Plan.HandleConnection` from a HandleConnection. */
createHandleConnection(connection: HandleConnection): string {
const storageKey = this.createStorageKey(connection.handle.storageKey);
const mode = this.createDirection(connection.direction);
const type = this.createType(connection.type);
const ttl = 'null';
return ktUtils.applyFun('HandleConnection', [storageKey, mode, type, ttl], 24);
}
/** Generates a Kotlin `HandleMode` from a Direction. */
createDirection(direction: Direction): string {
switch (direction) {
case 'reads': return 'HandleMode.Read';
case 'writes': return 'HandleMode.Write';
case 'reads writes': return 'HandleMode.ReadWrite';
default: throw new PlanGeneratorError(
`HandleConnection direction '${direction}' is not supported.`);
}
}
/** Generates a Kotlin `StorageKey` from a StorageKey. */
createStorageKey(storageKey: StorageKey | undefined): string {
return `StorageKeyParser.parse("${(storageKey || '').toString()}")`;
}
/** Generates a Kotlin `core.arc.type.Type` from a Type. */
// TODO(alxr): Implement
createType(type: Type): string {
switch (type.tag) {
case 'Collection':
break;
case 'Entity':
break;
case 'Handle':
break;
case 'Reference':
break;
case 'Singleton':
break;
case 'TypeVariable':
break;
case 'Arc':
case 'BigCollection':
case 'Count':
case 'Interface':
case 'Slot':
case 'Tuple':
default:
throw Error(`Type of ${type.tag} is not supported.`);
}
return 'null';
}
fileHeader(): string {
return `\
/* ktlint-disable */
@file:Suppress("PackageName", "TopLevelName")
package ${this.scope}
//
// GENERATED CODE -- DO NOT EDIT
//
${tryImport('arcs.core.data.*', this.scope)}
${tryImport('arcs.core.storage.*', this.scope)}
`;
}
fileFooter(): string {
return ``;
}
}