From c5019be170260bd5afe4927fc1c3e8ea63237263 Mon Sep 17 00:00:00 2001 From: Alex Komoroske Date: Sun, 9 Jul 2023 14:42:14 -0700 Subject: [PATCH] Add `else` to `retrieve` and `var`. If the named variable isn't set, then (and only then) an else clause is executed to return a value. This is useful for defaults, but also for sub-expressions where it only does things like prompting the user if the value isn't set. Part of #36. --- README.md | 2 ++ seed-schema.json | 42 ++++++++++++++++++++++++++++++++++++++++++ src/grow.ts | 10 ++++++++-- src/types.ts | 4 +++- test/base/a_test.json | 42 ++++++++++++++++++++++++++++++++++++++++++ test/base/test.ts | 32 ++++++++++++++++++++++++++++++++ 6 files changed, 129 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c54cf0a..401684b 100644 --- a/README.md +++ b/README.md @@ -554,6 +554,7 @@ Returns a variable from environment. See also `let`. Required parameters: - `name` - A named variable in environment to get. +- `else` - If the key does not exist, execute this sub-expression and return its value instead. #### let @@ -605,6 +606,7 @@ Unlike `let`, this affects multiple runs. See also `store` and `delete`. Required parameters: - `store` - (optional) The ID of the store to store in. If omitted, will use environment.store. By convention you should name a store like `komoroske.com:name`, to avoid collisions. - `key` - The key to retrieve. +- `else` - If the key does not exist, execute this sub-expression and return its value instead. Environment: - `store` - The default store ID to use if one is not provided. diff --git a/seed-schema.json b/seed-schema.json index 404571b..344800f 100644 --- a/seed-schema.json +++ b/seed-schema.json @@ -2057,6 +2057,27 @@ "description": "The name of the variable in environment to fetch" } ] + }, + "else": { + "anyOf": [ + { + "$ref": "#/definitions/seedData" + }, + { + "$ref": "#/definitions/seedData/anyOf/0/properties/prompt/anyOf/1" + }, + { + "anyOf": [ + { + "not": {} + }, + { + "$ref": "#/definitions/seedData/anyOf/5/properties/value/anyOf/2/anyOf/1/additionalProperties/anyOf/2" + } + ], + "description": "The sub-expression to execute to get a value if value is undefined" + } + ] } }, "required": [ @@ -2350,6 +2371,27 @@ "description": "The name of the variable in environment to retrieve" } ] + }, + "else": { + "anyOf": [ + { + "$ref": "#/definitions/seedData" + }, + { + "$ref": "#/definitions/seedData/anyOf/0/properties/prompt/anyOf/1" + }, + { + "anyOf": [ + { + "not": {} + }, + { + "$ref": "#/definitions/seedData/anyOf/5/properties/value/anyOf/2/anyOf/1/additionalProperties/anyOf/2" + } + ], + "description": "The sub-expression to execute to get a value if value is undefined" + } + ] } }, "required": [ diff --git a/src/grow.ts b/src/grow.ts index c418b5d..ae101de 100644 --- a/src/grow.ts +++ b/src/grow.ts @@ -586,7 +586,11 @@ const growVar = async (seed : Seed, env : Environment) : Promise, env : Environment) : Promise => { @@ -632,7 +636,9 @@ const growRetrieve = async (seed : Seed, env : Environment) : const storeID = env.getStoreID(rawStoreID); const key = extractString(await getProperty(seed, env, data.name)); const result = seed.garden.profile.retrieve(storeID, key); - if (result === undefined) return null; + if (result === undefined) { + return await getProperty(seed, env, data.else, null); + } return result; }; diff --git a/src/types.ts b/src/types.ts index 8c9c4a0..938a7cb 100644 --- a/src/types.ts +++ b/src/types.ts @@ -768,7 +768,8 @@ export type SeedDataThrow = z.infer; const seedDataConfigVar = { type: z.literal('var'), properties: { - name: varName.describe('The name of the variable in environment to fetch') + name: varName.describe('The name of the variable in environment to fetch'), + else: inputValue.optional().describe('The sub-expression to execute to get a value if value is undefined'), } }; @@ -823,6 +824,7 @@ const seedDataConfigRetrieve = { properties: { store: storeID.optional().describe('The store ID to use'), name: storeKey.describe('The name of the variable in environment to retrieve'), + else: inputValue.optional().describe('The sub-expression to execute to get a value if value is undefined'), } }; diff --git a/test/base/a_test.json b/test/base/a_test.json index 1c03f07..116bdd9 100644 --- a/test/base/a_test.json +++ b/test/base/a_test.json @@ -279,6 +279,48 @@ "throw-test": { "type": "throw", "error": "This is an error" + }, + "var-with-value-set": { + "type": "let", + "name": "val", + "value": 3, + "block": { + "type": "var", + "name": "val", + "else": { + "type": "throw", + "error": "This shouldn't happen since val is set" + } + } + }, + "var-without-value-set": { + "type": "var", + "name": "val", + "else": 5 + }, + "retrieve-with-value-set": { + "type": "array", + "return": "last", + "items": [ + { + "type": "store", + "name": "val", + "value": 3 + }, + { + "type": "retrieve", + "name": "val", + "else": { + "type": "throw", + "error": "This shouldn't happen since val is set" + } + } + ] + }, + "retrieve-without-value-set": { + "type": "retrieve", + "name": "val", + "else": 5 } } } \ No newline at end of file diff --git a/test/base/test.ts b/test/base/test.ts index 4cf7ed5..97d34ae 100644 --- a/test/base/test.ts +++ b/test/base/test.ts @@ -845,6 +845,38 @@ Suffix`; }); }); + it('var set else test', async () => { + const garden = loadTestGarden(); + const seed = await garden.seed('var-with-value-set'); + const actual = await seed.grow(); + const golden = 3; + assert.deepStrictEqual(actual, golden); + }); + + it('var unset else test', async () => { + const garden = loadTestGarden(); + const seed = await garden.seed('var-without-value-set'); + const actual = await seed.grow(); + const golden = 5; + assert.deepStrictEqual(actual, golden); + }); + + it('retrieve set else test', async () => { + const garden = loadTestGarden(); + const seed = await garden.seed('retrieve-with-value-set'); + const actual = await seed.grow(); + const golden = 3; + assert.deepStrictEqual(actual, golden); + }); + + it('retrieve unset else test', async () => { + const garden = loadTestGarden(); + const seed = await garden.seed('retrieve-without-value-set'); + const actual = await seed.grow(); + const golden = 5; + assert.deepStrictEqual(actual, golden); + }); + }); describe('expandSeedPacket tests', () => {