From 1dff110a1f524d1989a1c6be15d707cd6eca5cd7 Mon Sep 17 00:00:00 2001 From: Gary Sassano <10464497+garysassano@users.noreply.github.com> Date: Mon, 11 Sep 2023 14:40:25 +0200 Subject: [PATCH] chore(sdk): add `Json.entries()` (#4092) Closes #3142 *By submitting this pull request, I confirm that my contribution is made under the terms of the [Wing Cloud Contribution License](https://github.com/winglang/wing/blob/main/CONTRIBUTION_LICENSE.md)*. --- docs/docs/03-language-reference.md | 55 +++++++++++-------- .../02-std/api-reference.md | 25 +++++++-- examples/tests/sdk_tests/std/json.w | 17 ++++++ .../snapshots/completions/json_statics.snap | 16 +++++- .../static_completions_after_expression.snap | 16 +++++- .../static_json_after_expression.snap | 16 +++++- ...tatic_json_after_expression_statement.snap | 16 +++++- libs/wingsdk/src/std/json.ts | 45 +++++++++------ .../sdk_tests/std/json.w_compile_tf-aws.md | 53 ++++++++++++++++++ .../sdk_tests/std/json.w_test_sim.md | 13 +++-- 10 files changed, 216 insertions(+), 56 deletions(-) diff --git a/docs/docs/03-language-reference.md b/docs/docs/03-language-reference.md index e4c77432f5d..791cd393c1c 100644 --- a/docs/docs/03-language-reference.md +++ b/docs/docs/03-language-reference.md @@ -59,12 +59,12 @@ import TOCInline from '@theme/TOCInline'; #### 1.1.1 Primitive Types -| Name | Extra information | -| ------ | ---------------------------------- | -| `void` | represents the absence of a type | -| `num` | represents numbers (doubles) | -| `str` | UTF-16 encoded strings | -| `bool` | represents true or false | +| Name | Extra information | +| ------ | -------------------------------- | +| `void` | represents the absence of a type | +| `num` | represents numbers (doubles) | +| `str` | UTF-16 encoded strings | +| `bool` | represents true or false | > ```TS > let x = 1; // x is a num @@ -221,10 +221,10 @@ log("${jsonObj.get("boom").get("dude").get("world")}"); // ERROR: Cannot read properties of undefined (reading 'world') ``` -To obtain an array of all the keys within a JSON object use the `Json.keys(o)` method. +To obtain an array of all the keys, use `Json.keys(o)`: ```TS -let j = Json { hello: 123, world: [ 1, 2, 3 ] }; +let j = Json { hello: 123, world: [1, 2, 3] }; assert(Json.keys(j).at(0) == "hello"); assert(Json.keys(j).at(1) == "world"); ``` @@ -232,12 +232,24 @@ assert(Json.keys(j).at(1) == "world"); To obtain an array of all the values, use `Json.values(o)`: ```TS -assert(Json.values(j).equals([ Json 123, Json [ 1, 2, 3 ] ])); +assert(Json.values(j).at(0) == 123); +assert(Json.values(j).at(1) == [1, 2, 3]); ``` > NOTE: `values()` returns an array inside a `Json` object because at the moment we > cannot represent heterogenous arrays in Wing. +To obtain an array of all key/value pairs, use `Json.entries(o)`: + +```TS +assert(Json.entries(j).at(0).getAt(0) == "hello"); +assert(Json.entries(j).at(0).getAt(1) == 123); +assert(Json.entries(j).at(1).getAt(0) == "world"); +assert(Json.entries(j).at(1).getAt(1) == [1, 2, 3]); +``` +> NOTE: `entries()` returns an array inside a `Json` object because at the moment we +> cannot represent heterogenous arrays in Wing. + ##### 1.1.4.3 Assignment from native types It is also possible to assign the native `str`, `num`, `bool` and `Array` values and they will @@ -424,7 +436,6 @@ my object is: { The following features are not yet implemented, but we are planning to add them in the future: * Array/Set/Map.fromJson() - see https://github.com/winglang/wing/issues/1796 to track. -* Json.entries() - see https://github.com/winglang/wing/issues/3142 to track. * Equality, diff and patch - see https://github.com/winglang/wing/issues/3140 to track. [`▲ top`][top] @@ -513,10 +524,10 @@ log("UTC: ${t1.utc.toIso())}"); // output: 2023-02-09T06:21:03.000Z ### 1.2 Utility Functions -| Name | Extra information | -| -------- | -------------------------------------------------------- | -| `log` | logs str | -| `assert` | checks a condition and _throws_ if evaluated to false | +| Name | Extra information | +| -------- | ----------------------------------------------------- | +| `log` | logs str | +| `assert` | checks a condition and _throws_ if evaluated to false | > ```TS > log("Hello ${name}"); @@ -1785,7 +1796,7 @@ When calling **extern** function, the parameter and return types are **assumed** | Built-in Wing type | TypeScript type | -|---------------------------|-----------------------------------------------------------------------| +| ------------------------- | --------------------------------------------------------------------- | | `void` | `undefined` | | `nil` | `null` | | `any` | `any` | @@ -1797,12 +1808,12 @@ When calling **extern** function, the parameter and return types are **assumed** | `Array`, `MutArray` | `Array` | | `Json`, `MutJson` | `string ⏐ number ⏐ boolean ⏐ null ⏐ Json[] ⏐ { [key: string]: Json }` | -| User-defined Wing type | TypeScript type | -|-------------------------|----------------------------------------------------------------------------------------| -| `class` | `class`, only with members whose phase is compatible with the function signature | -| `interface` | `interface`, only with members whose phase is compatible with the function signature | -| `struct` | `interface` | -| `enum` | `string`-based enum-like `Object` | +| User-defined Wing type | TypeScript type | +| ---------------------- | ------------------------------------------------------------------------------------ | +| `class` | `class`, only with members whose phase is compatible with the function signature | +| `interface` | `interface`, only with members whose phase is compatible with the function signature | +| `struct` | `interface` | +| `enum` | `string`-based enum-like `Object` | [`▲ top`][top] @@ -2071,7 +2082,7 @@ Ternary or conditional operators are not supported. | Operator | Notes | | -------------------- | ------------------------------------------------- | | () | Parentheses | -| ** | Power | +| ** | Power | | -x | Unary minus | | \*, /, \\, % | Multiplication, Division, Floor division, Modulus | | +, - | Addition, Subtraction | diff --git a/docs/docs/04-standard-library/02-std/api-reference.md b/docs/docs/04-standard-library/02-std/api-reference.md index 686bb2d6e9c..1b13d96e7d3 100644 --- a/docs/docs/04-standard-library/02-std/api-reference.md +++ b/docs/docs/04-standard-library/02-std/api-reference.md @@ -816,8 +816,9 @@ The index of the element in the Json Array to return. | deepCopy | Creates an immutable deep copy of the Json. | | deepCopyMut | Creates a mutable deep copy of the Json. | | delete | Deletes a key in a given Json. | +| entries | Returns the entries from the Json. | | has | Checks if a Json object has a given key. | -| keys | Returns the keys from the Json object. | +| keys | Returns the keys from the Json. | | parse | Parse a string into a Json. | | stringify | Formats Json as string. | | tryParse | Try to parse a string into a Json. | @@ -881,6 +882,22 @@ the key to delete. --- +##### `entries` + +```wing +Json.entries(json: Json); +``` + +Returns the entries from the Json. + +###### `json`Required + +- *Type:* Json + +map to get the entries from. + +--- + ##### `has` ```wing @@ -911,13 +928,13 @@ The key to check. Json.keys(json: any); ``` -Returns the keys from the Json object. +Returns the keys from the Json. ###### `json`Required - *Type:* any -to get keys from. +map to get the keys from. --- @@ -987,7 +1004,7 @@ Returns the values from the Json. - *Type:* Json -to get values from. +map to get the values from. --- diff --git a/examples/tests/sdk_tests/std/json.w b/examples/tests/sdk_tests/std/json.w index 6d57a24db06..bd544d69ea8 100644 --- a/examples/tests/sdk_tests/std/json.w +++ b/examples/tests/sdk_tests/std/json.w @@ -85,6 +85,23 @@ test "stringify()" { assert(stringifiedIndent == "{\n \"a\": 1,\n \"b\": 2\n}"); } +test "keys(), values(), entries()" { + let obj = Json { + a: 1, + b: [3, 7, 9], + c: { foo: "bar" } + }; + + let entries = Json.entries(obj); + let keys = Json.keys(obj); + let values = Json.values(obj); + + for i in 0..entries.length { + assert(entries.at(i).getAt(0) == keys.at(i)); + assert(entries.at(i).getAt(1) == values.at(i)); + } +} + //----------------------------------------------------------------------------- // tryParse() assert(Json.tryParse(nil) == nil); diff --git a/libs/wingc/src/lsp/snapshots/completions/json_statics.snap b/libs/wingc/src/lsp/snapshots/completions/json_statics.snap index 71d42d4ff90..80911624093 100644 --- a/libs/wingc/src/lsp/snapshots/completions/json_statics.snap +++ b/libs/wingc/src/lsp/snapshots/completions/json_statics.snap @@ -37,6 +37,18 @@ source: libs/wingc/src/lsp/completions.rs command: title: triggerParameterHints command: editor.action.triggerParameterHints +- label: entries + kind: 2 + detail: "(json: Json): Array" + documentation: + kind: markdown + value: "```wing\nstatic entries: (json: Json): Array\n```\n---\nReturns the entries from the Json.\n\n\n### Returns\nthe entries as Array consisting of enumerable [key, value] pairs" + sortText: ff|entries + insertText: entries($0) + insertTextFormat: 2 + command: + title: triggerParameterHints + command: editor.action.triggerParameterHints - label: has kind: 2 detail: "(json: Json, key: str): bool" @@ -54,7 +66,7 @@ source: libs/wingc/src/lsp/completions.rs detail: "(json: any): Array" documentation: kind: markdown - value: "```wing\nstatic keys: (json: any): Array\n```\n---\nReturns the keys from the Json object.\n\n\n### Returns\nthe keys from the Json object as string array" + value: "```wing\nstatic keys: (json: any): Array\n```\n---\nReturns the keys from the Json.\n\n\n### Returns\nthe keys as Array" sortText: ff|keys insertText: keys($0) insertTextFormat: 2 @@ -102,7 +114,7 @@ source: libs/wingc/src/lsp/completions.rs detail: "(json: Json): Array" documentation: kind: markdown - value: "```wing\nstatic values: (json: Json): Array\n```\n---\nReturns the values from the Json.\n\n\n### Returns\nthe values from the Json as array of Json" + value: "```wing\nstatic values: (json: Json): Array\n```\n---\nReturns the values from the Json.\n\n\n### Returns\nthe values as Array" sortText: ff|values insertText: values($0) insertTextFormat: 2 diff --git a/libs/wingc/src/lsp/snapshots/completions/static_completions_after_expression.snap b/libs/wingc/src/lsp/snapshots/completions/static_completions_after_expression.snap index 71d42d4ff90..80911624093 100644 --- a/libs/wingc/src/lsp/snapshots/completions/static_completions_after_expression.snap +++ b/libs/wingc/src/lsp/snapshots/completions/static_completions_after_expression.snap @@ -37,6 +37,18 @@ source: libs/wingc/src/lsp/completions.rs command: title: triggerParameterHints command: editor.action.triggerParameterHints +- label: entries + kind: 2 + detail: "(json: Json): Array" + documentation: + kind: markdown + value: "```wing\nstatic entries: (json: Json): Array\n```\n---\nReturns the entries from the Json.\n\n\n### Returns\nthe entries as Array consisting of enumerable [key, value] pairs" + sortText: ff|entries + insertText: entries($0) + insertTextFormat: 2 + command: + title: triggerParameterHints + command: editor.action.triggerParameterHints - label: has kind: 2 detail: "(json: Json, key: str): bool" @@ -54,7 +66,7 @@ source: libs/wingc/src/lsp/completions.rs detail: "(json: any): Array" documentation: kind: markdown - value: "```wing\nstatic keys: (json: any): Array\n```\n---\nReturns the keys from the Json object.\n\n\n### Returns\nthe keys from the Json object as string array" + value: "```wing\nstatic keys: (json: any): Array\n```\n---\nReturns the keys from the Json.\n\n\n### Returns\nthe keys as Array" sortText: ff|keys insertText: keys($0) insertTextFormat: 2 @@ -102,7 +114,7 @@ source: libs/wingc/src/lsp/completions.rs detail: "(json: Json): Array" documentation: kind: markdown - value: "```wing\nstatic values: (json: Json): Array\n```\n---\nReturns the values from the Json.\n\n\n### Returns\nthe values from the Json as array of Json" + value: "```wing\nstatic values: (json: Json): Array\n```\n---\nReturns the values from the Json.\n\n\n### Returns\nthe values as Array" sortText: ff|values insertText: values($0) insertTextFormat: 2 diff --git a/libs/wingc/src/lsp/snapshots/completions/static_json_after_expression.snap b/libs/wingc/src/lsp/snapshots/completions/static_json_after_expression.snap index 71d42d4ff90..80911624093 100644 --- a/libs/wingc/src/lsp/snapshots/completions/static_json_after_expression.snap +++ b/libs/wingc/src/lsp/snapshots/completions/static_json_after_expression.snap @@ -37,6 +37,18 @@ source: libs/wingc/src/lsp/completions.rs command: title: triggerParameterHints command: editor.action.triggerParameterHints +- label: entries + kind: 2 + detail: "(json: Json): Array" + documentation: + kind: markdown + value: "```wing\nstatic entries: (json: Json): Array\n```\n---\nReturns the entries from the Json.\n\n\n### Returns\nthe entries as Array consisting of enumerable [key, value] pairs" + sortText: ff|entries + insertText: entries($0) + insertTextFormat: 2 + command: + title: triggerParameterHints + command: editor.action.triggerParameterHints - label: has kind: 2 detail: "(json: Json, key: str): bool" @@ -54,7 +66,7 @@ source: libs/wingc/src/lsp/completions.rs detail: "(json: any): Array" documentation: kind: markdown - value: "```wing\nstatic keys: (json: any): Array\n```\n---\nReturns the keys from the Json object.\n\n\n### Returns\nthe keys from the Json object as string array" + value: "```wing\nstatic keys: (json: any): Array\n```\n---\nReturns the keys from the Json.\n\n\n### Returns\nthe keys as Array" sortText: ff|keys insertText: keys($0) insertTextFormat: 2 @@ -102,7 +114,7 @@ source: libs/wingc/src/lsp/completions.rs detail: "(json: Json): Array" documentation: kind: markdown - value: "```wing\nstatic values: (json: Json): Array\n```\n---\nReturns the values from the Json.\n\n\n### Returns\nthe values from the Json as array of Json" + value: "```wing\nstatic values: (json: Json): Array\n```\n---\nReturns the values from the Json.\n\n\n### Returns\nthe values as Array" sortText: ff|values insertText: values($0) insertTextFormat: 2 diff --git a/libs/wingc/src/lsp/snapshots/completions/static_json_after_expression_statement.snap b/libs/wingc/src/lsp/snapshots/completions/static_json_after_expression_statement.snap index 71d42d4ff90..80911624093 100644 --- a/libs/wingc/src/lsp/snapshots/completions/static_json_after_expression_statement.snap +++ b/libs/wingc/src/lsp/snapshots/completions/static_json_after_expression_statement.snap @@ -37,6 +37,18 @@ source: libs/wingc/src/lsp/completions.rs command: title: triggerParameterHints command: editor.action.triggerParameterHints +- label: entries + kind: 2 + detail: "(json: Json): Array" + documentation: + kind: markdown + value: "```wing\nstatic entries: (json: Json): Array\n```\n---\nReturns the entries from the Json.\n\n\n### Returns\nthe entries as Array consisting of enumerable [key, value] pairs" + sortText: ff|entries + insertText: entries($0) + insertTextFormat: 2 + command: + title: triggerParameterHints + command: editor.action.triggerParameterHints - label: has kind: 2 detail: "(json: Json, key: str): bool" @@ -54,7 +66,7 @@ source: libs/wingc/src/lsp/completions.rs detail: "(json: any): Array" documentation: kind: markdown - value: "```wing\nstatic keys: (json: any): Array\n```\n---\nReturns the keys from the Json object.\n\n\n### Returns\nthe keys from the Json object as string array" + value: "```wing\nstatic keys: (json: any): Array\n```\n---\nReturns the keys from the Json.\n\n\n### Returns\nthe keys as Array" sortText: ff|keys insertText: keys($0) insertTextFormat: 2 @@ -102,7 +114,7 @@ source: libs/wingc/src/lsp/completions.rs detail: "(json: Json): Array" documentation: kind: markdown - value: "```wing\nstatic values: (json: Json): Array\n```\n---\nReturns the values from the Json.\n\n\n### Returns\nthe values from the Json as array of Json" + value: "```wing\nstatic values: (json: Json): Array\n```\n---\nReturns the values from the Json.\n\n\n### Returns\nthe values as Array" sortText: ff|values insertText: values($0) insertTextFormat: 2 diff --git a/libs/wingsdk/src/std/json.ts b/libs/wingsdk/src/std/json.ts index 0ae6496fbbd..c8fe61f6d78 100644 --- a/libs/wingsdk/src/std/json.ts +++ b/libs/wingsdk/src/std/json.ts @@ -20,18 +20,44 @@ export class Json { } /** - * Returns the keys from the Json object. + * Returns the keys from the Json. * * @macro (Object.keys($args$)) * - * @param json to get keys from - * @returns the keys from the Json object as string array + * @param json map to get the keys from + * @returns the keys as Array */ public static keys(json: Json | MutJson): string[] { json; throw new Error("Macro"); } + /** + * Returns the values from the Json. + * + * @macro (Object.values($args$)) + * + * @param json map to get the values from + * @returns the values as Array + */ + public static values(json: Json): Json[] { + json; + throw new Error("Macro"); + } + + /** + * Returns the entries from the Json. + * + * @macro (Object.entries($args$)) + * + * @param json map to get the entries from + * @returns the entries as Array consisting of enumerable [key, value] pairs + */ + public static entries(json: Json): Json[] { + json; + throw new Error("Macro"); + } + /** * Deletes a key in a given Json * @@ -89,19 +115,6 @@ export class Json { throw new Error("Macro"); } - /** - * Returns the values from the Json. - * - * @macro (Object.values($args$)) - * - * @param json to get values from - * @returns the values from the Json as array of Json - */ - public static values(json: Json): Json[] { - json; - throw new Error("Macro"); - } - /** * Parse a string into a Json * diff --git a/tools/hangar/__snapshots__/test_corpus/sdk_tests/std/json.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/sdk_tests/std/json.w_compile_tf-aws.md index 083f47ee13c..20c7928e326 100644 --- a/tools/hangar/__snapshots__/test_corpus/sdk_tests/std/json.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/sdk_tests/std/json.w_compile_tf-aws.md @@ -149,6 +149,31 @@ module.exports = function({ $std_Json }) { ``` +## inflight.$Closure6-1.js +```js +module.exports = function({ $std_Json }) { + class $Closure6 { + constructor({ }) { + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle() { + const obj = ({"a": 1,"b": [3, 7, 9],"c": ({"foo": "bar"})}); + const entries = (Object.entries(obj)); + const keys = (Object.keys(obj)); + const values = (Object.values(obj)); + for (const i of ((s,e,i) => { function* iterator(start,end,inclusive) { let i = start; let limit = inclusive ? ((end < start) ? end - 1 : end + 1) : end; while (i < limit) yield i++; while (i > limit) yield i--; }; return iterator(s,e,i); })(0,entries.length,false)) { + {((cond) => {if (!cond) throw new Error("assertion failed: entries.at(i).getAt(0) == keys.at(i)")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(((obj, args) => { if (obj[args] === undefined) throw new Error("Index out of bounds"); return obj[args] })((await entries.at(i)), 0),(await keys.at(i)))))}; + {((cond) => {if (!cond) throw new Error("assertion failed: entries.at(i).getAt(1) == values.at(i)")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(((obj, args) => { if (obj[args] === undefined) throw new Error("Index out of bounds"); return obj[args] })((await entries.at(i)), 1),(await values.at(i)))))}; + } + } + } + return $Closure6; +} + +``` + ## main.tf.json ```json { @@ -323,11 +348,39 @@ class $Root extends $stdlib.std.Resource { return ["handle", "$inflight_init"]; } } + class $Closure6 extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + (std.Node.of(this)).hidden = true; + } + static _toInflightType(context) { + return ` + require("./inflight.$Closure6-1.js")({ + $std_Json: ${context._lift(std.Json)}, + }) + `; + } + _toInflight() { + return ` + (await (async () => { + const $Closure6Client = ${$Closure6._toInflightType(this)}; + const client = new $Closure6Client({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `; + } + _getInflightOps() { + return ["handle", "$inflight_init"]; + } + } this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:get()",new $Closure1(this,"$Closure1")); this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:getAt()",new $Closure2(this,"$Closure2")); this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:set()",new $Closure3(this,"$Closure3")); this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:setAt()",new $Closure4(this,"$Closure4")); this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:stringify()",new $Closure5(this,"$Closure5")); + this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:keys(), values(), entries()",new $Closure6(this,"$Closure6")); {((cond) => {if (!cond) throw new Error("assertion failed: Json.tryParse(nil) == nil")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(((args) => { try { return (args === undefined) ? undefined : JSON.parse(args); } catch (err) { return undefined; } })(undefined),undefined)))}; {((cond) => {if (!cond) throw new Error("assertion failed: Json.tryParse(\"boom\") == nil")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(((args) => { try { return (args === undefined) ? undefined : JSON.parse(args); } catch (err) { return undefined; } })("boom"),undefined)))}; {((cond) => {if (!cond) throw new Error("assertion failed: Json.tryParse(\"\") == nil")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(((args) => { try { return (args === undefined) ? undefined : JSON.parse(args); } catch (err) { return undefined; } })(""),undefined)))}; diff --git a/tools/hangar/__snapshots__/test_corpus/sdk_tests/std/json.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/sdk_tests/std/json.w_test_sim.md index 9f8b8b3e1df..1e398fba930 100644 --- a/tools/hangar/__snapshots__/test_corpus/sdk_tests/std/json.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/sdk_tests/std/json.w_test_sim.md @@ -2,14 +2,15 @@ ## stdout.log ```log -pass ─ json.wsim » root/env0/test:get() -pass ─ json.wsim » root/env1/test:getAt() -pass ─ json.wsim » root/env2/test:set() -pass ─ json.wsim » root/env3/test:setAt() -pass ─ json.wsim » root/env4/test:stringify() +pass ─ json.wsim » root/env0/test:get() +pass ─ json.wsim » root/env1/test:getAt() +pass ─ json.wsim » root/env2/test:set() +pass ─ json.wsim » root/env3/test:setAt() +pass ─ json.wsim » root/env4/test:stringify() +pass ─ json.wsim » root/env5/test:keys(), values(), entries() -Tests 5 passed (5) +Tests 6 passed (6) Test Files 1 passed (1) Duration ```