Skip to content

Commit

Permalink
Add support for scalar in versioning (#3053)
Browse files Browse the repository at this point in the history
fix [#3001](#3001)
  • Loading branch information
timotheeguerin authored Mar 26, 2024
1 parent 9b083d0 commit 32e45d4
Show file tree
Hide file tree
Showing 12 changed files with 132 additions and 3 deletions.
7 changes: 7 additions & 0 deletions .chronus/changes/scalar-versioning-2024-2-26-9-56-30.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: feature
packages:
- "@typespec/versioning"
---

Add support for versioning of scalars(Added, removed, renamed)
7 changes: 7 additions & 0 deletions .chronus/changes/scalar-versioning-2024-2-26-9-56-6.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: feature
packages:
- "@typespec/compiler"
---

Experimental projection: Add support for scalars
3 changes: 3 additions & 0 deletions packages/compiler/src/core/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,9 @@ export function createBinder(program: Program): Binder {
case SyntaxKind.ProjectionModelPropertySelector:
selectorString = "modelproperty";
break;
case SyntaxKind.ProjectionScalarSelector:
selectorString = "scalar";
break;
case SyntaxKind.ProjectionOperationSelector:
selectorString = "op";
break;
Expand Down
5 changes: 5 additions & 0 deletions packages/compiler/src/core/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ export function createChecker(program: Program): Checker {
const projectionsByTypeKind = new Map<Type["kind"], ProjectionStatementNode[]>([
["Model", []],
["ModelProperty", []],
["Scalar", []],
["Union", []],
["UnionVariant", []],
["Operation", []],
Expand Down Expand Up @@ -4725,6 +4726,10 @@ export function createChecker(program: Program): Checker {
projectionsByTypeKind.get("ModelProperty")!.push(node);
type.nodeByKind.set("ModelProperty", node);
break;
case SyntaxKind.ProjectionScalarSelector:
projectionsByTypeKind.get("Scalar")!.push(node);
type.nodeByKind.set("Scalar", node);
break;
case SyntaxKind.ProjectionOperationSelector:
projectionsByTypeKind.get("Operation")!.push(node);
type.nodeByKind.set("Operation", node);
Expand Down
12 changes: 11 additions & 1 deletion packages/compiler/src/core/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import {
ProjectionNode,
ProjectionOperationSelectorNode,
ProjectionParameterDeclarationNode,
ProjectionScalarSelectorNode,
ProjectionStatementItem,
ProjectionStatementNode,
ProjectionTupleExpressionNode,
Expand Down Expand Up @@ -2377,6 +2378,7 @@ function createParser(code: string | SourceFile, options: ParseOptions = {}): Pa
| MemberExpressionNode
| ProjectionInterfaceSelectorNode
| ProjectionModelSelectorNode
| ProjectionScalarSelectorNode
| ProjectionModelPropertySelectorNode
| ProjectionOperationSelectorNode
| ProjectionUnionSelectorNode
Expand All @@ -2390,7 +2392,8 @@ function createParser(code: string | SourceFile, options: ParseOptions = {}): Pa
Token.OpKeyword,
Token.InterfaceKeyword,
Token.UnionKeyword,
Token.EnumKeyword
Token.EnumKeyword,
Token.ScalarKeyword
);

switch (selectorTok) {
Expand Down Expand Up @@ -2446,6 +2449,12 @@ function createParser(code: string | SourceFile, options: ParseOptions = {}): Pa
kind: SyntaxKind.ProjectionEnumSelector,
...finishNode(pos),
};
case Token.ScalarKeyword:
nextToken();
return {
kind: SyntaxKind.ProjectionScalarSelector,
...finishNode(pos),
};
default:
// recovery: return a missing identifier to use as the selector
// we don't need to emit a diagnostic here as the `expectTokenOneOf` above
Expand Down Expand Up @@ -3374,6 +3383,7 @@ export function visitChildren<T>(node: Node, cb: NodeCallback<T>): T | undefined
case SyntaxKind.EmptyStatement:
case SyntaxKind.ProjectionModelSelector:
case SyntaxKind.ProjectionModelPropertySelector:
case SyntaxKind.ProjectionScalarSelector:
case SyntaxKind.ProjectionUnionSelector:
case SyntaxKind.ProjectionUnionVariantSelector:
case SyntaxKind.ProjectionInterfaceSelector:
Expand Down
4 changes: 4 additions & 0 deletions packages/compiler/src/core/projection-members.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ export function createProjectionMembers(checker: Checker): {
});
},
},
Scalar: {
...createBaseMembers(),
...createNameableMembers(),
},
Union: {
...createBaseMembers(),
...createNameableMembers(),
Expand Down
7 changes: 7 additions & 0 deletions packages/compiler/src/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,7 @@ export enum SyntaxKind {
ProjectionParameterDeclaration,
ProjectionModelSelector,
ProjectionModelPropertySelector,
ProjectionScalarSelector,
ProjectionOperationSelector,
ProjectionUnionSelector,
ProjectionUnionVariantSelector,
Expand Down Expand Up @@ -902,6 +903,7 @@ export type Node =
| ProjectionExpression
| ProjectionModelSelectorNode
| ProjectionModelPropertySelectorNode
| ProjectionScalarSelectorNode
| ProjectionInterfaceSelectorNode
| ProjectionOperationSelectorNode
| ProjectionEnumSelectorNode
Expand Down Expand Up @@ -1443,6 +1445,10 @@ export interface ProjectionModelSelectorNode extends BaseNode {
readonly kind: SyntaxKind.ProjectionModelSelector;
}

export interface ProjectionScalarSelectorNode extends BaseNode {
readonly kind: SyntaxKind.ProjectionScalarSelector;
}

export interface ProjectionModelPropertySelectorNode extends BaseNode {
readonly kind: SyntaxKind.ProjectionModelPropertySelector;
}
Expand Down Expand Up @@ -1592,6 +1598,7 @@ export interface ProjectionStatementNode extends BaseNode, DeclarationNode {
readonly selector:
| ProjectionModelSelectorNode
| ProjectionModelPropertySelectorNode
| ProjectionScalarSelectorNode
| ProjectionInterfaceSelectorNode
| ProjectionOperationSelectorNode
| ProjectionUnionSelectorNode
Expand Down
2 changes: 2 additions & 0 deletions packages/compiler/src/formatter/print/printer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@ export function printNode(
return "model";
case SyntaxKind.ProjectionModelPropertySelector:
return "modelproperty";
case SyntaxKind.ProjectionScalarSelector:
return "scalar";
case SyntaxKind.ProjectionOperationSelector:
return "op";
case SyntaxKind.ProjectionUnionSelector:
Expand Down
7 changes: 5 additions & 2 deletions packages/compiler/test/parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -824,7 +824,7 @@ describe("compiler: parser", () => {

describe("projections", () => {
describe("selectors", () => {
const selectors = ["model", "op", "interface", "union", "someId"];
const selectors = ["model", "op", "interface", "union", "scalar", "someId"];
const codes = selectors.map((s) => `projection ${s}#tag { }`);
parseEach(codes);
});
Expand Down Expand Up @@ -892,7 +892,10 @@ describe("compiler: parser", () => {

describe("recovery", () => {
parseErrorEach([
[`projection `, [/identifier, 'model', 'op', 'interface', 'union', or 'enum' expected/]],
[
`projection `,
[/identifier, 'model', 'op', 'interface', 'union', 'enum', or 'scalar' expected./],
],
[`projection x `, [/'#' expected/]],
[`projection x#`, [/Identifier expected/]],
[`projection x#f`, [/'{' expected/]],
Expand Down
27 changes: 27 additions & 0 deletions packages/versioning/lib/versioning.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,30 @@ projection enummember#v {
};
}
}

#suppress "projections-are-experimental"
projection scalar#v {
pre to(version) {
if !existsAtVersion(self, version) {
return never;
};
}
to(version) {
if hasDifferentNameAtVersion(self, version) {
self::rename(getNameAtVersion(self, version));
};
if hasDifferentReturnTypeAtVersion(self, version) {
self::changeReturnType(getReturnTypeBeforeVersion(self, version));
};
}
pre from(version) {
if !existsAtVersion(self, version) {
return never;
};
}
from(version) {
if hasDifferentNameAtVersion(self, version) {
self::rename(self::projectionBase::name);
};
}
}
1 change: 1 addition & 0 deletions packages/versioning/src/versioning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,7 @@ export function getVersions(p: Program, t: Type): [Namespace, VersionMap] | [] {
t.kind === "Interface" ||
t.kind === "Model" ||
t.kind === "Union" ||
t.kind === "Scalar" ||
t.kind === "Enum"
) {
if (t.namespace) {
Expand Down
53 changes: 53 additions & 0 deletions packages/versioning/test/versioning.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1829,6 +1829,59 @@ describe("versioning: logic", () => {
}
});

describe("scalars", () => {
it("can be renamed", async () => {
const {
projections: [v1, v2],
} = await versionedScalar(
["v1", "v2"],
`
@renamedFrom(Versions.v2, "oldTest")
scalar test;`
);

strictEqual(v1.name, "oldTest");
strictEqual(v2.name, "test");
});

it("can be added", async () => {
const {
projections: [v1, v2],
} = await versionedScalar(["v1", "v2"], `@added(Versions.v2) scalar test;`);
strictEqual(v1.kind, "Intrinsic");
strictEqual((v1 as any as IntrinsicType).name, "never");
strictEqual(v2.kind, "Scalar");
});

it("can be removed", async () => {
const {
projections: [v1, v2],
} = await versionedScalar(["v1", "v2"], `@removed(Versions.v2) scalar test;`);

strictEqual(v1.kind, "Scalar");
strictEqual(v2.kind, "Intrinsic");
strictEqual((v2 as any as IntrinsicType).name, "never");
});

async function versionedScalar(versions: string[], scalarCode: string) {
const { test } = (await runner.compile(`
@versioned(Versions)
namespace MyService;
enum Versions { ${versions.map((t) => JSON.stringify(t)).join(" , ")} }
@test ${scalarCode}
`)) as { test: Scalar };

return {
source: test,
projections: versions.map((v) => {
return project(test, v);
}),
};
}
});

function assertModelProjectsTo(types: [Model, string][], target: Model) {
types.forEach(([m, version]) => {
const projection = project(m, version, "from");
Expand Down

0 comments on commit 32e45d4

Please sign in to comment.