-
Notifications
You must be signed in to change notification settings - Fork 152
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implementation for field.Field (#279)
This is a linked-list-based implementation. This is one of many possible approaches. The approach attempts to prevent/limit the mutability of the fields outside the `field` package. Also, in the package the contents of `linkedField struct` are not modified after the struct has been created. Include example usage (tests) to field package. See #273
- Loading branch information
1 parent
7d56f3c
commit 6c70295
Showing
4 changed files
with
137 additions
and
4 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
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
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,47 @@ | ||
package field_test | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
. "gopkg.in/check.v1" | ||
|
||
"github.com/kanisterio/kanister/pkg/field" | ||
) | ||
|
||
type FieldSuite struct{} | ||
|
||
var _ = Suite(&FieldSuite{}) | ||
|
||
func ExampleNew() { | ||
f := field.New("foo", "bar") | ||
fmt.Print(f) | ||
// Output: ["foo":"bar"] | ||
} | ||
|
||
func ExampleAdd() { | ||
f := field.New("foo", "bar") | ||
f = field.Add(f, "baz", "x") | ||
fmt.Print(f) | ||
// Output: ["foo":"bar","baz":"x"] | ||
} | ||
|
||
type M = field.M | ||
|
||
func ExampleContext() { | ||
ctx := field.Context(context.Background(), "foo", "bar") | ||
fmt.Print(field.FromContext(ctx)) | ||
// Output: ["foo":"bar"] | ||
} | ||
|
||
func ExampleAddMapToContext() { | ||
ctx := field.AddMapToContext(context.Background(), M{"foo": "bar"}) | ||
fmt.Print(field.FromContext(ctx)) | ||
// Output: ["foo":"bar"] | ||
} | ||
|
||
func ExampleAddMapToContext_multiple() { | ||
ctx := field.AddMapToContext(context.Background(), M{"foo": "bar", "x": "y"}) | ||
// Output is not specified because the order of the fields in 'M' is non-deterministic | ||
fmt.Print(field.FromContext(ctx)) | ||
} |
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,87 @@ | ||
package field | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
) | ||
|
||
// Fields are exposed as an interface instead of directly as pointer to this | ||
// struct. That enforces the immutability of fields outside this package. Once | ||
// a field is created, its contents cannot be changed or overwritten outside | ||
// this package by simply assigning one field to another, via | ||
// f1 = f2 or *fp1 = *fp2. | ||
type field struct { | ||
key string | ||
value interface{} | ||
} | ||
|
||
var _ fmt.Stringer = field{} | ||
|
||
func (f field) Key() string { | ||
return f.key | ||
} | ||
|
||
func (f field) Value() interface{} { | ||
return f.value | ||
} | ||
|
||
func (f field) String() string { | ||
return fmt.Sprintf("%q:%q", f.key, f.value) | ||
} | ||
|
||
type linkedField struct { | ||
field | ||
prev *linkedField | ||
} | ||
|
||
var _ fmt.Stringer = (*linkedField)(nil) | ||
|
||
func newField(prev Fields, key string, value interface{}) *linkedField { | ||
return &linkedField{prev: asLinkedFields(prev), field: field{key: key, value: value}} | ||
} | ||
|
||
func (f *linkedField) Fields() []Field { | ||
return f.fields(0) | ||
} | ||
|
||
func (f *linkedField) fields(n int) []Field { | ||
if f != nil { | ||
return append(f.prev.fields(n+1), f.field) | ||
} | ||
return make([]Field, 0, n) | ||
} | ||
|
||
func (f *linkedField) String() string { | ||
var b strings.Builder | ||
b.WriteByte('[') | ||
if f != nil { | ||
f.buildString(&b) | ||
} | ||
b.WriteByte(']') | ||
return b.String() | ||
} | ||
|
||
func (f *linkedField) buildString(b *strings.Builder) { | ||
if f.prev != nil { | ||
f.prev.buildString(b) | ||
b.WriteByte(',') | ||
} | ||
fmt.Fprintf(b, "%q:%q", f.key, f.value) | ||
} | ||
|
||
func asLinkedFields(fs Fields) *linkedField { | ||
if f, ok := fs.(*linkedField); ok { | ||
return f | ||
} | ||
return toLinkedFields(nil, fs) | ||
} | ||
|
||
func toLinkedFields(prev *linkedField, fs Fields) *linkedField { | ||
if fs == nil { | ||
return prev | ||
} | ||
for _, o := range fs.Fields() { | ||
prev = &linkedField{prev: prev, field: field{key: o.Key(), value: o.Value()}} | ||
} | ||
return prev | ||
} |