From bad267bdcaafabc53ebbeb74bf3eebeadfebb302 Mon Sep 17 00:00:00 2001 From: Brian Terlson Date: Thu, 18 May 2023 16:19:46 -0700 Subject: [PATCH 01/10] implement decimal scalar --- docs/language-basics/built-in-types.md | 34 ++++++++++--------- docs/language-basics/type-literals.md | 2 +- docs/language-basics/type-relations.md | 2 ++ packages/compiler/core/checker.ts | 14 ++++++++ packages/compiler/core/parser.ts | 5 +-- packages/compiler/core/types.ts | 4 +++ packages/compiler/lib/lib.tsp | 10 ++++++ packages/compiler/test/checker/scalar.test.ts | 14 ++++++++ packages/openapi3/src/openapi.ts | 4 +++ 9 files changed, 70 insertions(+), 19 deletions(-) diff --git a/docs/language-basics/built-in-types.md b/docs/language-basics/built-in-types.md index 350dd2857a3..8da181be28f 100644 --- a/docs/language-basics/built-in-types.md +++ b/docs/language-basics/built-in-types.md @@ -11,22 +11,24 @@ Built in types are related to each other according to the rules described in [ty ## Numeric types -| Type | Range | Description | -| --------- | ------------------------------------------------------------------------------------------------------------ | ----------------------------------------- | -| `numeric` | | Parent type for all numeric types | -| `integer` | | A whole-number | -| `float` | | A number with decimal value | -| `int64` | `-9,223,372,036,854,775,808` to `9,223,372,036,854,775,807` | A 64-bit integer | -| `int32` | `-2,147,483,648` to `2,147,483,647` | A 32-bit integer | -| `int16` | `-32,768` to `32,767` | A 16-bit integer | -| `int8` | `-128` to `127` | A 8-bit integer | -| `safeint` | −9007199254740991 (−(253 − 1)) to 9007199254740991 (253 − 1) | An integer that can be serialized to JSON | -| `uint64` | `0` to `18,446,744,073,709,551,615` | Unsigned 64-bit integer | -| `uint32` | `0` to `4,294,967,295` | Unsigned 32-bit integer | -| `uint16` | `0` to `65,535` | Unsigned 16-bit integer | -| `uint8` | `0` to `255 ` | Unsigned 8-bit integer | -| `float32` | ±1.5 x 1045 to ±3.4 x 1038 | A 32 bit floating point number | -| `float64` | ±5.0 × 10−324 to ±1.7 × 10308 | A 64 bit floating point number | +| Type | Range | Description | +| ------------ | ------------------------------------------------------------------------------------------------------------ | ----------------------------------------- | +| `numeric` | | Parent type for all numeric types | +| `integer` | | A whole-number | +| `float` | | A binary number | +| `int64` | `-9,223,372,036,854,775,808` to `9,223,372,036,854,775,807` | A 64-bit integer | +| `int32` | `-2,147,483,648` to `2,147,483,647` | A 32-bit integer | +| `int16` | `-32,768` to `32,767` | A 16-bit integer | +| `int8` | `-128` to `127` | A 8-bit integer | +| `safeint` | −9007199254740991 (−(253 − 1)) to 9007199254740991 (253 − 1) | An integer that can be serialized to JSON | +| `uint64` | `0` to `18,446,744,073,709,551,615` | Unsigned 64-bit integer | +| `uint32` | `0` to `4,294,967,295` | Unsigned 32-bit integer | +| `uint16` | `0` to `65,535` | Unsigned 16-bit integer | +| `uint8` | `0` to `255 ` | Unsigned 8-bit integer | +| `float32` | ±1.5 x 1045 to ±3.4 x 1038 | A 32 bit floating point number | +| `float64` | ±5.0 × 10−324 to ±1.7 × 10308 | A 64 bit floating point number | +| `decimal` | | A decimal number | +| `decimal128` | 34 decimal digits with an exponent range from `-6143` to `6144` | A 128 bit decimal number | ## Date and time types diff --git a/docs/language-basics/type-literals.md b/docs/language-basics/type-literals.md index 7e4edfd0b91..6b7e725f1bb 100644 --- a/docs/language-basics/type-literals.md +++ b/docs/language-basics/type-literals.md @@ -64,7 +64,7 @@ two ## Numeric literal -Numeric literals can be declare by using the raw number (`interger` or `float`) +Numeric literals can be declare by using the raw number ```typespec alias Kilo = 1000; diff --git a/docs/language-basics/type-relations.md b/docs/language-basics/type-relations.md index 0315835a363..03f7fca3e49 100644 --- a/docs/language-basics/type-relations.md +++ b/docs/language-basics/type-relations.md @@ -29,6 +29,8 @@ graph RL float --> numeric float32 --> float float64 --> float + decimal --> numeric + decimal128 --> decimal end string --> unknown boolean --> unknown diff --git a/packages/compiler/core/checker.ts b/packages/compiler/core/checker.ts index 247e6da1bb5..d4034eefb80 100644 --- a/packages/compiler/core/checker.ts +++ b/packages/compiler/core/checker.ts @@ -4472,9 +4472,20 @@ export function createChecker(program: Program): Checker { type = createType({ kind: "Boolean", value }); break; case "number": + let valueAsString: string; + if (node) { + compilerAssert( + node.kind === SyntaxKind.NumericLiteral, + "Must pass numeric literal node or undefined when creating a numeric literal type" + ); + valueAsString = node.valueAsString; + } else { + valueAsString = String(value); + } type = createType({ kind: "Number", value, + valueAsString, }); break; } @@ -4775,6 +4786,9 @@ export function createChecker(program: Program): Checker { } if (target.name === "numeric") return true; + if (target.name === "decimal") return true; + if (target.name === "decimal128") return true; + const isInt = Number.isInteger(source.value); if (target.name === "integer") return isInt; if (target.name === "float") return true; diff --git a/packages/compiler/core/parser.ts b/packages/compiler/core/parser.ts index da0536b1eb6..baeb6fc7043 100644 --- a/packages/compiler/core/parser.ts +++ b/packages/compiler/core/parser.ts @@ -1354,13 +1354,14 @@ function createParser(code: string | SourceFile, options: ParseOptions = {}): Pa function parseNumericLiteral(): NumericLiteralNode { const pos = tokenPos(); - const text = tokenValue(); - const value = Number(text); + const valueAsString = tokenValue(); + const value = Number(valueAsString); parseExpected(Token.NumericLiteral); return { kind: SyntaxKind.NumericLiteral, value, + valueAsString, ...finishNode(pos), }; } diff --git a/packages/compiler/core/types.ts b/packages/compiler/core/types.ts index ca9a8548703..c68d84e092e 100644 --- a/packages/compiler/core/types.ts +++ b/packages/compiler/core/types.ts @@ -184,6 +184,8 @@ export type IntrinsicScalarName = | "safeint" | "float32" | "float64" + | "decimal" + | "decimal128" | "string" | "plainDate" | "plainTime" @@ -454,6 +456,7 @@ export interface NumericLiteral extends BaseType { kind: "Number"; node?: NumericLiteralNode; value: number; + valueAsString: string; } export interface BooleanLiteral extends BaseType { @@ -1204,6 +1207,7 @@ export interface StringLiteralNode extends BaseNode { export interface NumericLiteralNode extends BaseNode { readonly kind: SyntaxKind.NumericLiteral; readonly value: number; + readonly valueAsString: string; } export interface BooleanLiteralNode extends BaseNode { diff --git a/packages/compiler/lib/lib.tsp b/packages/compiler/lib/lib.tsp index 78bc8ef912d..6fa6b8e6b81 100644 --- a/packages/compiler/lib/lib.tsp +++ b/packages/compiler/lib/lib.tsp @@ -75,6 +75,16 @@ scalar float64 extends float; */ scalar float32 extends float64; +/** + * A decimal number with any length and precision. + */ +scalar decimal extends numeric; + +/** + * A 128-bit decimal number. + */ +scalar decimal128 extends decimal; + /** * A sequence of textual characters. */ diff --git a/packages/compiler/test/checker/scalar.test.ts b/packages/compiler/test/checker/scalar.test.ts index facacdca37a..00869805a68 100644 --- a/packages/compiler/test/checker/scalar.test.ts +++ b/packages/compiler/test/checker/scalar.test.ts @@ -1,4 +1,5 @@ import { ok, strictEqual } from "assert"; +import { Model, NumericLiteral } from "../../core/index.js"; import { BasicTestRunner, createTestHost, @@ -62,6 +63,19 @@ describe("compiler: scalars", () => { strictEqual(B.kind, "Scalar" as const); }); + it.only("allows a decimal to have a default value", async () => { + const { A } = (await runner.compile(` + @test model A { + x: decimal = 42; + } + `)) as { A: Model }; + + const def = A.properties.get("x")!.default! as NumericLiteral; + strictEqual(def.kind, "Number" as const); + strictEqual(def.value, 42); + strictEqual(def.valueAsString, "42"); + }); + describe("custom scalars and default values", () => { it("allows custom numeric scalar to have a default value", async () => { const { S, M } = await runner.compile(` diff --git a/packages/openapi3/src/openapi.ts b/packages/openapi3/src/openapi.ts index a42006e0771..0696077fd99 100644 --- a/packages/openapi3/src/openapi.ts +++ b/packages/openapi3/src/openapi.ts @@ -1844,6 +1844,10 @@ function createOAPIEmitter(program: Program, options: ResolvedOpenAPI3EmitterOpt return { type: "number", format: "double" }; case "float32": return { type: "number", format: "float" }; + case "decimal": + return { type: "string" }; + case "decimal128": + return { type: "string" }; case "string": return { type: "string" }; case "boolean": From ffb2a529e01bfdeaa000f82f587a5b3c8751961e Mon Sep 17 00:00:00 2001 From: Brian Terlson Date: Thu, 18 May 2023 16:22:26 -0700 Subject: [PATCH 02/10] changes: --- .../@typespec/compiler/decimal_2023-05-18-23-22.json | 10 ++++++++++ .../@typespec/openapi3/decimal_2023-05-18-23-22.json | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 common/changes/@typespec/compiler/decimal_2023-05-18-23-22.json create mode 100644 common/changes/@typespec/openapi3/decimal_2023-05-18-23-22.json diff --git a/common/changes/@typespec/compiler/decimal_2023-05-18-23-22.json b/common/changes/@typespec/compiler/decimal_2023-05-18-23-22.json new file mode 100644 index 00000000000..37c83c1fc11 --- /dev/null +++ b/common/changes/@typespec/compiler/decimal_2023-05-18-23-22.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@typespec/compiler", + "comment": "Add decimal and decimal128 built-in scalar types.", + "type": "none" + } + ], + "packageName": "@typespec/compiler" +} \ No newline at end of file diff --git a/common/changes/@typespec/openapi3/decimal_2023-05-18-23-22.json b/common/changes/@typespec/openapi3/decimal_2023-05-18-23-22.json new file mode 100644 index 00000000000..b7112f13668 --- /dev/null +++ b/common/changes/@typespec/openapi3/decimal_2023-05-18-23-22.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@typespec/openapi3", + "comment": "Support decimal and decimal128 scalar types.", + "type": "none" + } + ], + "packageName": "@typespec/openapi3" +} \ No newline at end of file From 04c171f32406f52cebbd1c3dd801522803fa1183 Mon Sep 17 00:00:00 2001 From: Brian Terlson Date: Thu, 18 May 2023 17:04:45 -0700 Subject: [PATCH 03/10] handle primitive type defaults --- packages/openapi3/src/openapi.ts | 54 ++++++++++++++----- .../openapi3/test/primitive-types.test.ts | 2 + 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/packages/openapi3/src/openapi.ts b/packages/openapi3/src/openapi.ts index 0696077fd99..a1ec21b23c0 100644 --- a/packages/openapi3/src/openapi.ts +++ b/packages/openapi3/src/openapi.ts @@ -337,7 +337,7 @@ function createOAPIEmitter(program: Program, options: ResolvedOpenAPI3EmitterOpt } const variable: OpenAPI3ServerVariable = { - default: prop.default ? getDefaultValue(prop.default) : "", + default: prop.default ? getDefaultValue(prop.type, prop.default) : "", description: getDoc(program, prop), }; @@ -1104,7 +1104,7 @@ function createOAPIEmitter(program: Program, options: ResolvedOpenAPI3EmitterOpt } const schema = applyIntrinsicDecorators(param, typeSchema); if (param.default) { - schema.default = getDefaultValue(param.default); + schema.default = getDefaultValue(param.type, param.default); } // Description is already provided in the parameter itself. delete schema.description; @@ -1440,25 +1440,51 @@ function createOAPIEmitter(program: Program, options: ResolvedOpenAPI3EmitterOpt return type.kind === "Boolean" || type.kind === "String" || type.kind === "Number"; } - function getDefaultValue(type: Type): any { - switch (type.kind) { + function getDefaultValue(type: Type, defaultType: Type): any { + switch (defaultType.kind) { case "String": - return type.value; + return defaultType.value; case "Number": - return type.value; + compilerAssert(type.kind === "Scalar", "setting scalar default to non-scalar value"); + const base = getStdBaseScalar(type); + compilerAssert(base, "not allowed to assign default to custom scalars"); + + if (base.name === "decimal" || base.name === "decimal128") { + return defaultType.valueAsString; + } + + return defaultType.value; case "Boolean": - return type.value; + return defaultType.value; case "Tuple": - return type.values.map(getDefaultValue); + compilerAssert(type.kind === "Tuple", "setting tuple default to non-tuple value"); + + return defaultType.values.map((defaultTupleValue, index) => + getDefaultValue(type.values[index], defaultTupleValue) + ); case "EnumMember": - return type.value ?? type.name; + return defaultType.value ?? defaultType.name; default: reportDiagnostic(program, { code: "invalid-default", - format: { type: type.kind }, - target: type, + format: { type: defaultType.kind }, + target: defaultType, }); } + + function getDefaultValueOfType(type: Type, defaultType: Type) {} + + function getStdBaseScalar(scalar: Scalar): Scalar | null { + let current: Scalar | undefined = scalar; + while (current) { + if (program.checker.isStdType(current)) { + return current; + } + current = current.baseScalar; + } + + return null; + } } function includeDerivedModel(model: Model): boolean { @@ -1563,7 +1589,7 @@ function createOAPIEmitter(program: Program, options: ResolvedOpenAPI3EmitterOpt } if (prop.default) { - additionalProps.default = getDefaultValue(prop.default); + additionalProps.default = getDefaultValue(prop.type, prop.default); } if (isReadonlyProperty(program, prop)) { @@ -1845,9 +1871,9 @@ function createOAPIEmitter(program: Program, options: ResolvedOpenAPI3EmitterOpt case "float32": return { type: "number", format: "float" }; case "decimal": - return { type: "string" }; + return { type: "string", format: "decimal" }; case "decimal128": - return { type: "string" }; + return { type: "string", format: "decimal128" }; case "string": return { type: "string" }; case "boolean": diff --git a/packages/openapi3/test/primitive-types.test.ts b/packages/openapi3/test/primitive-types.test.ts index 30ac71340f3..b2e027c740c 100644 --- a/packages/openapi3/test/primitive-types.test.ts +++ b/packages/openapi3/test/primitive-types.test.ts @@ -25,6 +25,8 @@ describe("openapi3: primitives", () => { ["plainTime", { type: "string", format: "time" }], ["duration", { type: "string", format: "duration" }], ["bytes", { type: "string", format: "byte" }], + ["decimal", { type: "string", format: "decimal" }], + ["decimal128", { type: "string", format: "decimal128" }], ]; for (const [name, expectedSchema] of cases) { From e27dbd634bd30d3abe0255de8ad52000af68b97d Mon Sep 17 00:00:00 2001 From: Brian Terlson Date: Mon, 22 May 2023 16:23:40 -0700 Subject: [PATCH 04/10] update tests --- packages/compiler/test/checker/model.test.ts | 2 +- .../compiler/test/checker/relation.test.ts | 27 +++++++++++++++++++ packages/compiler/test/checker/scalar.test.ts | 2 +- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/compiler/test/checker/model.test.ts b/packages/compiler/test/checker/model.test.ts index 9ed46d68569..0709005e354 100644 --- a/packages/compiler/test/checker/model.test.ts +++ b/packages/compiler/test/checker/model.test.ts @@ -107,7 +107,7 @@ describe("compiler: models", () => { ["boolean", `false`, { kind: "Boolean", value: false, isFinished: false }], ["boolean", `true`, { kind: "Boolean", value: true, isFinished: false }], ["string", `"foo"`, { kind: "String", value: "foo", isFinished: false }], - ["int32", `123`, { kind: "Number", value: 123, isFinished: false }], + ["int32", `123`, { kind: "Number", value: 123, valueAsString: "123", isFinished: false }], ]; for (const [type, defaultValue, expectedValue] of testCases) { diff --git a/packages/compiler/test/checker/relation.test.ts b/packages/compiler/test/checker/relation.test.ts index 5cd6297c32e..4738244078b 100644 --- a/packages/compiler/test/checker/relation.test.ts +++ b/packages/compiler/test/checker/relation.test.ts @@ -134,6 +134,8 @@ describe("compiler: checker: type relations", () => { "uint16", "uint32", "uint64", + "decimal", + "decimal128", "string", "numeric", "float", @@ -420,6 +422,8 @@ describe("compiler: checker: type relations", () => { "float", "float32", "float64", + "decimal", + "decimal128", ].forEach((x) => { it(`can assign ${x}`, async () => { await expectTypeAssignable({ source: x, target: "numeric" }); @@ -446,6 +450,29 @@ describe("compiler: checker: type relations", () => { }); }); + describe("decimal target", () => { + it("can assign decimal", async () => { + await expectTypeAssignable({ source: "decimal", target: "decimal" }); + }); + it("can assign decimal128", async () => { + await expectTypeAssignable({ source: "decimal128", target: "decimal" }); + }); + it("can assign numeric literals", async () => { + await expectTypeAssignable({ source: "-2147483448", target: "decimal" }); + await expectTypeAssignable({ source: "2147483448", target: "decimal" }); + }); + }); + + describe("decimal128 target", () => { + it("can assign decimal128", async () => { + await expectTypeAssignable({ source: "decimal128", target: "decimal128" }); + }); + it("can assign numeric literals", async () => { + await expectTypeAssignable({ source: "-2147483448", target: "decimal128" }); + await expectTypeAssignable({ source: "2147483448", target: "decimal128" }); + }); + }); + describe("object target", () => { ["object", "Record", "Record"].forEach((x) => { it(`can assign ${x}`, async () => { diff --git a/packages/compiler/test/checker/scalar.test.ts b/packages/compiler/test/checker/scalar.test.ts index 00869805a68..b9763e2809e 100644 --- a/packages/compiler/test/checker/scalar.test.ts +++ b/packages/compiler/test/checker/scalar.test.ts @@ -63,7 +63,7 @@ describe("compiler: scalars", () => { strictEqual(B.kind, "Scalar" as const); }); - it.only("allows a decimal to have a default value", async () => { + it("allows a decimal to have a default value", async () => { const { A } = (await runner.compile(` @test model A { x: decimal = 42; From d4a7c098b5ad186c333aa53937a0e31ec6aa7b8a Mon Sep 17 00:00:00 2001 From: Brian Terlson Date: Mon, 22 May 2023 16:30:30 -0700 Subject: [PATCH 05/10] Update docs/language-basics/type-literals.md Co-authored-by: Mark Cowlishaw --- docs/language-basics/type-literals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/language-basics/type-literals.md b/docs/language-basics/type-literals.md index 6b7e725f1bb..cf6ee9af228 100644 --- a/docs/language-basics/type-literals.md +++ b/docs/language-basics/type-literals.md @@ -64,7 +64,7 @@ two ## Numeric literal -Numeric literals can be declare by using the raw number +Numeric literals can be declared by using the raw number ```typespec alias Kilo = 1000; From 82f1d5c42f219160f322965ab7ac4afbcdca4a02 Mon Sep 17 00:00:00 2001 From: Brian Terlson Date: Mon, 22 May 2023 16:49:39 -0700 Subject: [PATCH 06/10] fix lint, add test --- packages/compiler/test/checker/relation.test.ts | 2 ++ packages/openapi3/src/openapi.ts | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/compiler/test/checker/relation.test.ts b/packages/compiler/test/checker/relation.test.ts index 4738244078b..e7556b04064 100644 --- a/packages/compiler/test/checker/relation.test.ts +++ b/packages/compiler/test/checker/relation.test.ts @@ -460,6 +460,7 @@ describe("compiler: checker: type relations", () => { it("can assign numeric literals", async () => { await expectTypeAssignable({ source: "-2147483448", target: "decimal" }); await expectTypeAssignable({ source: "2147483448", target: "decimal" }); + await expectTypeAssignable({ source: "2147483448.12390812", target: "decimal" }); }); }); @@ -470,6 +471,7 @@ describe("compiler: checker: type relations", () => { it("can assign numeric literals", async () => { await expectTypeAssignable({ source: "-2147483448", target: "decimal128" }); await expectTypeAssignable({ source: "2147483448", target: "decimal128" }); + await expectTypeAssignable({ source: "2147483448.12390812", target: "decimal128" }); }); }); diff --git a/packages/openapi3/src/openapi.ts b/packages/openapi3/src/openapi.ts index a1ec21b23c0..9a2916f4237 100644 --- a/packages/openapi3/src/openapi.ts +++ b/packages/openapi3/src/openapi.ts @@ -1472,8 +1472,6 @@ function createOAPIEmitter(program: Program, options: ResolvedOpenAPI3EmitterOpt }); } - function getDefaultValueOfType(type: Type, defaultType: Type) {} - function getStdBaseScalar(scalar: Scalar): Scalar | null { let current: Scalar | undefined = scalar; while (current) { From b5058090f2439c6d3de62f4ee9ca6d38f8436743 Mon Sep 17 00:00:00 2001 From: Brian Terlson Date: Mon, 22 May 2023 17:06:26 -0700 Subject: [PATCH 07/10] update proto tests --- packages/protobuf/test/scenarios/array-nested/diagnostics.txt | 2 +- .../test/scenarios/illegal field reservations/diagnostics.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/protobuf/test/scenarios/array-nested/diagnostics.txt b/packages/protobuf/test/scenarios/array-nested/diagnostics.txt index 8a512bd0037..179a8fdbf91 100644 --- a/packages/protobuf/test/scenarios/array-nested/diagnostics.txt +++ b/packages/protobuf/test/scenarios/array-nested/diagnostics.txt @@ -1 +1 @@ -/test/.tsp/lib/lib.tsp:118:1 - error @typespec/protobuf/nested-array: nested arrays are not supported by the Protobuf emitter \ No newline at end of file +/test/.tsp/lib/lib.tsp:128:1 - error @typespec/protobuf/nested-array: nested arrays are not supported by the Protobuf emitter \ No newline at end of file diff --git a/packages/protobuf/test/scenarios/illegal field reservations/diagnostics.txt b/packages/protobuf/test/scenarios/illegal field reservations/diagnostics.txt index 1bcc74ba904..ab3800ed9cd 100644 --- a/packages/protobuf/test/scenarios/illegal field reservations/diagnostics.txt +++ b/packages/protobuf/test/scenarios/illegal field reservations/diagnostics.txt @@ -1,2 +1,2 @@ -/test/.tsp/lib/lib.tsp:78:1 - error @typespec/protobuf/illegal-reservation: reservation value must be a string literal, uint32 literal, or a tuple of two uint32 literals denoting a range +/test/.tsp/lib/lib.tsp:88:1 - error @typespec/protobuf/illegal-reservation: reservation value must be a string literal, uint32 literal, or a tuple of two uint32 literals denoting a range /test/.tsp/lib/lib.tsp:48:1 - error @typespec/protobuf/illegal-reservation: reservation value must be a string literal, uint32 literal, or a tuple of two uint32 literals denoting a range \ No newline at end of file From b7647235344deaff34f6f05ed9447a9c284a10a3 Mon Sep 17 00:00:00 2001 From: Brian Terlson Date: Mon, 22 May 2023 17:16:23 -0700 Subject: [PATCH 08/10] no changes changes --- .../@typespec/protobuf/decimal_2023-05-23-00-16.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 common/changes/@typespec/protobuf/decimal_2023-05-23-00-16.json diff --git a/common/changes/@typespec/protobuf/decimal_2023-05-23-00-16.json b/common/changes/@typespec/protobuf/decimal_2023-05-23-00-16.json new file mode 100644 index 00000000000..8f48e4e9521 --- /dev/null +++ b/common/changes/@typespec/protobuf/decimal_2023-05-23-00-16.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@typespec/protobuf", + "comment": "", + "type": "none" + } + ], + "packageName": "@typespec/protobuf" +} \ No newline at end of file From adac9699b74a21f94c576a34fcb5eead3c493fd0 Mon Sep 17 00:00:00 2001 From: Brian Terlson Date: Mon, 22 May 2023 18:55:52 -0700 Subject: [PATCH 09/10] fix tuple/array defaults, add tests --- packages/openapi3/src/openapi.ts | 19 ++++++-- packages/openapi3/test/array.test.ts | 68 ++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/packages/openapi3/src/openapi.ts b/packages/openapi3/src/openapi.ts index 9a2916f4237..2ede095ca94 100644 --- a/packages/openapi3/src/openapi.ts +++ b/packages/openapi3/src/openapi.ts @@ -31,6 +31,7 @@ import { interpolatePath, IntrinsicScalarName, IntrinsicType, + isArrayModelType, isDeprecated, isErrorType, isGlobalNamespace, @@ -1457,11 +1458,21 @@ function createOAPIEmitter(program: Program, options: ResolvedOpenAPI3EmitterOpt case "Boolean": return defaultType.value; case "Tuple": - compilerAssert(type.kind === "Tuple", "setting tuple default to non-tuple value"); - - return defaultType.values.map((defaultTupleValue, index) => - getDefaultValue(type.values[index], defaultTupleValue) + compilerAssert( + type.kind === "Tuple" || (type.kind === "Model" && isArrayModelType(program, type)), + "setting tuple default to non-tuple value" ); + + if (type.kind === "Tuple") { + return defaultType.values.map((defaultTupleValue, index) => + getDefaultValue(type.values[index], defaultTupleValue) + ); + } else { + return defaultType.values.map((defaultTuplevalue) => + getDefaultValue(type.indexer!.value, defaultTuplevalue) + ); + } + case "EnumMember": return defaultType.value ?? defaultType.name; default: diff --git a/packages/openapi3/test/array.test.ts b/packages/openapi3/test/array.test.ts index 49bde26932a..3405cd79606 100644 --- a/packages/openapi3/test/array.test.ts +++ b/packages/openapi3/test/array.test.ts @@ -76,4 +76,72 @@ describe("openapi3: Array", () => { "x-typespec-name": "string[]", }); }); + + it.only("can specify array defaults using tuple syntax", async () => { + const res = await oapiForModel( + "Pet", + ` + model Pet { + names: string[] = ["bismarck"]; + decimals: decimal[] = [123, 456.7]; + decimal128s: decimal128[] = [123, 456.7]; + }; + ` + ); + + deepStrictEqual(res.schemas.Pet.properties.names, { + type: "array", + items: { type: "string" }, + "x-typespec-name": "string[]", + default: ["bismarck"], + }); + + deepStrictEqual(res.schemas.Pet.properties.decimals, { + type: "array", + items: { type: "string", format: "decimal" }, + "x-typespec-name": "decimal[]", + default: ["123", "456.7"], + }); + + deepStrictEqual(res.schemas.Pet.properties.decimal128s, { + type: "array", + items: { type: "string", format: "decimal128" }, + "x-typespec-name": "decimal128[]", + default: ["123", "456.7"], + }); + }); + + it.only("can specify tuple defaults using tuple syntax", async () => { + const res = await oapiForModel( + "Pet", + ` + model Pet { + names: [string, int32] = ["bismarck", 12]; + decimals: [string, decimal] = ["hi", 456.7]; + decimal128s: [string, decimal128] = ["hi", 456.7]; + }; + ` + ); + + deepStrictEqual(res.schemas.Pet.properties.names, { + type: "array", + items: {}, + "x-typespec-name": "[string, int32]", + default: ["bismarck", 12], + }); + + deepStrictEqual(res.schemas.Pet.properties.decimals, { + type: "array", + items: {}, + "x-typespec-name": "[string, decimal]", + default: ["hi", "456.7"], + }); + + deepStrictEqual(res.schemas.Pet.properties.decimal128s, { + type: "array", + items: {}, + "x-typespec-name": "[string, decimal128]", + default: ["hi", "456.7"], + }); + }); }); From a9f66f9e47b875ba7a4da93badf7c84d058cd0b5 Mon Sep 17 00:00:00 2001 From: Brian Terlson Date: Mon, 22 May 2023 19:22:17 -0700 Subject: [PATCH 10/10] only :( --- packages/openapi3/test/array.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/openapi3/test/array.test.ts b/packages/openapi3/test/array.test.ts index 3405cd79606..2220c9cc5c7 100644 --- a/packages/openapi3/test/array.test.ts +++ b/packages/openapi3/test/array.test.ts @@ -77,7 +77,7 @@ describe("openapi3: Array", () => { }); }); - it.only("can specify array defaults using tuple syntax", async () => { + it("can specify array defaults using tuple syntax", async () => { const res = await oapiForModel( "Pet", ` @@ -111,7 +111,7 @@ describe("openapi3: Array", () => { }); }); - it.only("can specify tuple defaults using tuple syntax", async () => { + it("can specify tuple defaults using tuple syntax", async () => { const res = await oapiForModel( "Pet", `