-
Notifications
You must be signed in to change notification settings - Fork 18
WIP: replace() function
#172
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| "@env-spec/parser": patch | ||
| "varlock": patch | ||
| --- | ||
|
|
||
| added `replace()` function to replace a string with support for ref() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,10 +58,21 @@ describe('function calls', functionValueTests({ | |
| ITEM: 'foo-val', | ||
| }, | ||
| }, | ||
| 'replace()': { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This file is testing how the parser itself handles parsing functions, and we don't need to define or test the new resolver within the parser. The tests we want to add are in env-graph |
||
| input: 'ITEM=replace("foo", "f", "b")', | ||
| expected: { ITEM: 'boo' }, | ||
| }, | ||
| 'nested function calls - array': { | ||
| input: 'OTHERVAL=d\nITEM=concat("a", fallback("", "b"), exec("echo c"), ref(OTHERVAL))', | ||
| expected: { ITEM: 'abcd' }, | ||
| }, | ||
| 'nested function calls - replace()': { | ||
| input: 'FOO=foo-val\nITEM=replace(ref("FOO"), "f", "b")', | ||
| expected: { | ||
| FOO: 'foo-val', | ||
| ITEM: 'boo-val', | ||
| }, | ||
| }, | ||
| 'nested function calls - key/value': { | ||
| input: 'ITEM=remap("foo", zzz=aaa, bar=fallback("", "foo"))', | ||
| expected: { ITEM: 'bar' }, | ||
|
|
@@ -135,6 +146,51 @@ describe('ref expansion', functionValueTests({ | |
| }, | ||
| })); | ||
|
|
||
| describe('replace expansion', functionValueTests({ | ||
| 'ref expansion - unquoted': { | ||
| input: 'OTHER=foo\nITEM=replace($OTHER, "f", "b")', | ||
| expected: { | ||
| OTHER: 'foo', | ||
| ITEM: 'boo', | ||
| }, | ||
| }, | ||
| 'ref expansion within quotes - double quotes': { | ||
| input: 'OTHER=foo\nITEM="${replace($OTHER, "f", "b")}"', | ||
| expected: { | ||
| OTHER: 'foo', | ||
| ITEM: 'boo', | ||
| }, | ||
| }, | ||
| 'ref expansion within quotes - backtick': { | ||
| input: 'OTHER=foo\nITEM=`${replace($OTHER, "f", "b")}`', | ||
| expected: { | ||
| OTHER: 'foo', | ||
| ITEM: 'boo', | ||
| }, | ||
| }, | ||
| 'ref expansion within quotes - single quotes (NOT EXPANDED)': { | ||
| input: "OTHER=foo\nITEM='${replace($OTHER, 'f', 'b')}'", | ||
| expected: { | ||
| OTHER: 'foo', | ||
| ITEM: '${replace($OTHER, "f", "b")}', | ||
| }, | ||
| }, | ||
| 'ref expansion - simple (no brackets)': { | ||
| input: 'OTHER=foo\nITEM=replace($OTHER, "f", "b")', | ||
| expected: { | ||
| OTHER: 'foo', | ||
| ITEM: 'boo', | ||
| }, | ||
| }, | ||
| 'ref expansion - with brackets': { | ||
| input: 'FOO=foo\nITEM=replace($FOO, "f", "b")', | ||
| expected: { | ||
| FOO: 'foo', | ||
| ITEM: 'boo', | ||
| }, | ||
| }, | ||
| })); | ||
|
|
||
| describe('complex cases', functionValueTests({ | ||
| 'multiple expansions': { | ||
| input: 'FOO=foo\nBAR=bar\nITEM=${FOO}-$BAR-$(echo baz)-${UNDEF:-qux}', | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -251,6 +251,42 @@ export class RefResolver extends Resolver { | |
| } | ||
| } | ||
|
|
||
| export class ReplaceResolver extends Resolver { | ||
| static fnName = 'replace'; | ||
| label = 'replace'; | ||
| icon = 'mdi-light:content-duplicate'; | ||
|
|
||
| private originalString?: string; | ||
| private searchString?: string; | ||
| private replacementString?: string; | ||
|
|
||
| async _process() { | ||
| if (this.fnArgs.length !== 1) { | ||
| throw new SchemaError('replace() expects three child args'); | ||
| } | ||
| if (!(this.fnArgs[0] instanceof StaticValueResolver)) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is checking that the args are all static, as in So within _process, we are basically just checking if args look right, but without necessarily knowing what the actual values are. (We only can check values if the arg was static). Then within _resolve, we can get the actual values, do some additional error checks, and then return our result.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also note - this isnt getting tested yet because you had added the tests in the parser lib. |
||
| throw new SchemaError('replace() expects the first arg to be the original string'); | ||
| } | ||
| if (!(this.fnArgs[1] instanceof StaticValueResolver)) { | ||
| throw new SchemaError('replace() expects the second arg to be the string to search for'); | ||
| } | ||
| if (!(this.fnArgs[2] instanceof StaticValueResolver)) { | ||
| throw new SchemaError('replace() expects the third arg to be the replacement string'); | ||
| } | ||
|
|
||
| this.originalString = String(await this.fnArgs[0].resolve()); | ||
| this.searchString = String(await this.fnArgs[1].resolve()); | ||
| this.replacementString = String(await this.fnArgs[2].resolve()); | ||
| } | ||
|
|
||
| protected async _resolve() { | ||
| if (!this.originalString) throw new Error('expected originalString to be set'); | ||
| if (!this.searchString) throw new Error('expected searchString to be set'); | ||
| if (!this.replacementString) throw new Error('expected replacementString to be set'); | ||
| return this.originalString.replace(this.searchString, this.replacementString); | ||
| } | ||
| } | ||
|
|
||
| // regex() is only used internally as function args to be used by other functions | ||
| // we will check final resoled values to make sure they are not regexes | ||
| export class RegexResolver extends Resolver { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't actually need this defined here at all. This is just a very simple implementation of a few functions which are used to test the parser itself.