Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for scalar in versioning #3053

Merged
merged 5 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading