From 6cf505ac71d96ebb5e8c2f5f689cffdbe22d06c7 Mon Sep 17 00:00:00 2001 From: David Wilson Date: Thu, 25 Mar 2021 11:23:03 -0700 Subject: [PATCH] Add `@minValue` and `@maxValue` decorators for numeric types --- packages/adl/lib/decorators.ts | 49 ++++++++++++++++++++ packages/adl/test/decorators/range-limits.ts | 29 ++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 packages/adl/test/decorators/range-limits.ts diff --git a/packages/adl/lib/decorators.ts b/packages/adl/lib/decorators.ts index 03310afb7..7b654f990 100644 --- a/packages/adl/lib/decorators.ts +++ b/packages/adl/lib/decorators.ts @@ -112,6 +112,55 @@ export function getMaxLength(target: Type): number | undefined { return maxLengthValues.get(target); } +// -- @minValue decorator --------------------- + +const minValues = new Map(); + +export function isNumericType(target: Type): boolean { + const intrinsicType = getIntrinsicType(target); + return ( + intrinsicType !== undefined && ["int32", "int64", "float32", "float64"].includes(intrinsicType) + ); +} + +export function minValue(program: Program, target: Type, minValue: number) { + if (target.kind === "Model" || target.kind === "ModelProperty") { + // Is it ultimately a numeric type? + if (isNumericType(target)) { + minValues.set(target, minValue); + } else { + throw new Error("Cannot apply @minValue to a non-numeric type"); + } + } else { + throw new Error("Cannot apply @minValue to anything that isn't a Model or ModelProperty"); + } +} + +export function getMinValue(target: Type): number | undefined { + return minValues.get(target); +} + +// -- @maxValue decorator --------------------- + +const maxValues = new Map(); + +export function maxValue(program: Program, target: Type, maxValue: number) { + if (target.kind === "Model" || target.kind === "ModelProperty") { + // Is it ultimately a numeric type? + if (isNumericType(target)) { + maxValues.set(target, maxValue); + } else { + throw new Error("Cannot apply @maxValue to a non-numeric type"); + } + } else { + throw new Error("Cannot apply @maxValue to anything that isn't a Model or ModelProperty"); + } +} + +export function getMaxValue(target: Type): number | undefined { + return maxValues.get(target); +} + // -- @secret decorator --------------------- const secretTypes = new Map(); diff --git a/packages/adl/test/decorators/range-limits.ts b/packages/adl/test/decorators/range-limits.ts new file mode 100644 index 000000000..faa30ac0c --- /dev/null +++ b/packages/adl/test/decorators/range-limits.ts @@ -0,0 +1,29 @@ +import { strictEqual, ok } from "assert"; +import { ModelType, Type } from "../../compiler/types.js"; +import { createTestHost, TestHost } from "../test-host.js"; +import { getMinValue, getMaxValue } from "../../lib/decorators.js"; + +describe("range limiting decorators", () => { + let testHost: TestHost; + + beforeEach(async () => { + testHost = await createTestHost(); + }); + + it("applies @minimum and @maximum decorators", async () => { + testHost.addAdlFile( + "a.adl", + ` + @test model A { @minValue 15 foo: int32; @maxValue 55 boo: float32; } + @test model B { @maxValue 20 bar: int64; @minValue 23 car: float64; } + ` + ); + + const { A, B } = (await testHost.compile("./")) as { A: ModelType; B: ModelType }; + + strictEqual(getMinValue(A.properties.get("foo")!), 15); + strictEqual(getMaxValue(A.properties.get("boo")!), 55); + strictEqual(getMaxValue(B.properties.get("bar")!), 20); + strictEqual(getMinValue(B.properties.get("car")!), 23); + }); +});