generated from friendly-fhir/go-template
-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add internal package for evaluate context
When evaluating a FHIRPath parse-tree, it's important to be able to provide some level of contextual information to the evaluation process. This commit adds an internal package to the project that provides a context object that can be used to provide this information.
- Loading branch information
1 parent
017b153
commit abfee32
Showing
2 changed files
with
303 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
/* | ||
Package envcontext provides definitions for Context objects that document | ||
environment variables, as defined in FHIRPath. | ||
*/ | ||
package envcontext | ||
|
||
import "context" | ||
|
||
type exprKey struct{} | ||
type exprEntries map[string]any | ||
|
||
// Lookup retrieves a value from the context by name. | ||
func Lookup(ctx context.Context, name string) (any, bool) { | ||
if ctx == nil { | ||
return nil, false | ||
} | ||
value := ctx.Value(exprKey{}) | ||
if value == nil { | ||
return nil, false | ||
} | ||
|
||
// This case should never logically occur, but is added as a precaution. | ||
entries := value.(exprEntries) | ||
if entries == nil { | ||
return nil, false | ||
} | ||
result, ok := entries[name] | ||
return result, ok | ||
} | ||
|
||
// Get retrieves a value from the context by name. | ||
func Get(ctx context.Context, name string) any { | ||
value, _ := Lookup(ctx, name) | ||
return value | ||
} | ||
|
||
// GetOr retrieves a value from the context by name, or returns a default value. | ||
func GetOr(ctx context.Context, name string, def any) any { | ||
value, ok := Lookup(ctx, name) | ||
if !ok { | ||
return def | ||
} | ||
return value | ||
} | ||
|
||
// WithEntry adds a single environment value by name to the context. | ||
func WithEntry(ctx context.Context, name string, value any) context.Context { | ||
if ctx == nil { | ||
ctx = context.Background() | ||
} | ||
exprCtx := ctx.Value(exprKey{}) | ||
if exprCtx == nil { | ||
exprCtx = exprEntries{} | ||
} | ||
entries := exprCtx.(exprEntries) | ||
entries[name] = value | ||
return context.WithValue(ctx, exprKey{}, entries) | ||
} | ||
|
||
// WithEntries adds multiple environment values by name to the context. | ||
func WithEntries(ctx context.Context, values map[string]any) context.Context { | ||
if ctx == nil { | ||
ctx = context.Background() | ||
} | ||
exprCtx := ctx.Value(exprKey{}) | ||
if exprCtx == nil { | ||
exprCtx = exprEntries{} | ||
} | ||
entries := exprCtx.(exprEntries) | ||
if entries == nil { | ||
entries = exprEntries{} | ||
} | ||
|
||
for key, value := range values { | ||
entries[key] = value | ||
} | ||
return context.WithValue(ctx, exprKey{}, entries) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,225 @@ | ||
package envcontext_test | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/friendly-fhir/go-fhirpath/internal/envcontext" | ||
) | ||
|
||
func TestLookup(t *testing.T) { | ||
testCases := []struct { | ||
name string | ||
ctx context.Context | ||
key string | ||
want any | ||
wantOK bool | ||
}{ | ||
{ | ||
name: "Nil context returns nil", | ||
ctx: nil, | ||
key: "key", | ||
want: nil, | ||
wantOK: false, | ||
}, { | ||
name: "Empty context returns nil", | ||
ctx: context.Background(), | ||
key: "key", | ||
want: nil, | ||
wantOK: false, | ||
}, { | ||
name: "Context with no entries returns nil", | ||
ctx: envcontext.WithEntries(context.Background(), nil), | ||
key: "key", | ||
want: nil, | ||
wantOK: false, | ||
}, { | ||
name: "Context with entry returns value", | ||
ctx: envcontext.WithEntry(context.Background(), "key", "value"), | ||
key: "key", | ||
want: "value", | ||
wantOK: true, | ||
}, { | ||
name: "Context with non-matching entries returns nil", | ||
ctx: envcontext.WithEntry(context.Background(), "other-key", "value"), | ||
key: "key", | ||
want: nil, | ||
wantOK: false, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
got, ok := envcontext.Lookup(tc.ctx, tc.key) | ||
|
||
if got, want := ok, tc.wantOK; got != want { | ||
t.Fatalf("Lookup() ok = %v; want %v", got, want) | ||
} | ||
|
||
if got, want := got, tc.want; got != want { | ||
t.Errorf("Lookup() = %v; want %v", got, want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestGet(t *testing.T) { | ||
testCases := []struct { | ||
name string | ||
ctx context.Context | ||
key string | ||
want any | ||
}{ | ||
{ | ||
name: "Nil context returns nil", | ||
ctx: nil, | ||
key: "key", | ||
want: nil, | ||
}, { | ||
name: "Empty context returns nil", | ||
ctx: context.Background(), | ||
key: "key", | ||
want: nil, | ||
}, { | ||
name: "Context with entry returns value", | ||
ctx: envcontext.WithEntry(context.Background(), "key", "value"), | ||
key: "key", | ||
want: "value", | ||
}, { | ||
name: "Context with non-matching entries returns nil", | ||
ctx: envcontext.WithEntry(context.Background(), "other-key", "value"), | ||
key: "key", | ||
want: nil, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
got := envcontext.Get(tc.ctx, tc.key) | ||
|
||
if got, want := got, tc.want; got != want { | ||
t.Errorf("Get() = %v; want %v", got, want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestGetOr(t *testing.T) { | ||
testCases := []struct { | ||
name string | ||
ctx context.Context | ||
key string | ||
def any | ||
want any | ||
}{ | ||
{ | ||
name: "Nil context returns default", | ||
ctx: nil, | ||
key: "key", | ||
def: "default", | ||
want: "default", | ||
}, { | ||
name: "Empty context returns default", | ||
ctx: context.Background(), | ||
key: "key", | ||
def: "default", | ||
want: "default", | ||
}, { | ||
name: "Context with entry returns value", | ||
ctx: envcontext.WithEntry(context.Background(), "key", "value"), | ||
key: "key", | ||
def: "default", | ||
want: "value", | ||
}, { | ||
name: "Context with non-matching entries returns default", | ||
ctx: envcontext.WithEntry(context.Background(), "other-key", "value"), | ||
key: "key", | ||
def: "default", | ||
want: "default", | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
got := envcontext.GetOr(tc.ctx, tc.key, tc.def) | ||
|
||
if got, want := got, tc.want; got != want { | ||
t.Errorf("GetOr() = %v; want %v", got, want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestWithEntry(t *testing.T) { | ||
testCases := []struct { | ||
name string | ||
ctx context.Context | ||
key string | ||
val any | ||
}{ | ||
{ | ||
name: "Nil context adds entry", | ||
ctx: nil, | ||
key: "key", | ||
val: "value", | ||
}, { | ||
name: "Empty context adds entry", | ||
ctx: context.Background(), | ||
key: "key", | ||
val: "value", | ||
}, { | ||
name: "Context with entry adds entry", | ||
ctx: envcontext.WithEntry(context.Background(), "other-key", "value"), | ||
key: "key", | ||
val: "value", | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
ctx := envcontext.WithEntry(tc.ctx, tc.key, tc.val) | ||
|
||
got := envcontext.Get(ctx, tc.key) | ||
|
||
if got, want := got, tc.val; got != want { | ||
t.Errorf("WithEntry() = %v; want %v", got, want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestWithEntries(t *testing.T) { | ||
testCases := []struct { | ||
name string | ||
ctx context.Context | ||
values map[string]any | ||
}{ | ||
{ | ||
name: "Nil context adds entries", | ||
ctx: nil, | ||
values: map[string]any{"key": "value"}, | ||
}, { | ||
name: "Empty context adds entries", | ||
ctx: context.Background(), | ||
values: map[string]any{"key": "value"}, | ||
}, { | ||
name: "Context with entry adds entries", | ||
ctx: envcontext.WithEntry(context.Background(), "other-key", "value"), | ||
values: map[string]any{"key": "value"}, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
ctx := envcontext.WithEntries(tc.ctx, tc.values) | ||
|
||
for key, val := range tc.values { | ||
got := envcontext.Get(ctx, key) | ||
|
||
if got, want := got, val; got != want { | ||
t.Errorf("WithEntries() = %v; want %v", got, want) | ||
} | ||
} | ||
}) | ||
} | ||
} |