From 48927391f35f8fce7491c372a27421401ecc6a8e Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Wed, 19 Jul 2017 00:38:47 -0700 Subject: [PATCH 1/4] condensed settable interfaces into one --- core/write.go | 33 +++++++++++---------------------- core/write_test.go | 41 ++++------------------------------------- 2 files changed, 15 insertions(+), 59 deletions(-) diff --git a/core/write.go b/core/write.go index 6ff52c1f..797745de 100644 --- a/core/write.go +++ b/core/write.go @@ -13,26 +13,14 @@ const tagName = "survey" // add a few interfaces so users can configure how the prompt values are set type settable interface { - WriteAnswer(value interface{}) error -} - -type fieldsettable interface { - WriteAnswerField(field string, value interface{}) error -} - -func writeByInterfaces(t interface{}, name string, v interface{}) (handled bool, err error) { - if s, ok := t.(settable); ok { - return true, s.WriteAnswer(v) - } - if fs, ok := t.(fieldsettable); ok { - return true, fs.WriteAnswerField(name, v) - } - return false, nil + WriteAnswer(field string, value interface{}) error } func WriteAnswer(t interface{}, name string, v interface{}) (err error) { - if handled, err := writeByInterfaces(t, name, v); handled { - return err + // if the field is a custom type + if s, ok := t.(settable); ok { + // use the interface method + return s.WriteAnswer(name, v) } // the target to write to @@ -59,12 +47,13 @@ func WriteAnswer(t interface{}, name string, v interface{}) (err error) { return err } field := elem.Field(fieldIndex) - if field.CanAddr() { - if handled, err := writeByInterfaces(field.Addr().Interface(), name, v); handled { - return err - } + // handle references to the settable interface aswell + if s, ok := t.(settable); ok && field.CanAddr() { + // use the interface method + return s.WriteAnswer(name, v) } - // copy the value over to the field + + // copy the value over to the normal struct return copy(field, value) case reflect.Map: mapType := reflect.TypeOf(t).Elem() diff --git a/core/write_test.go b/core/write_test.go index acfae704..e3552158 100644 --- a/core/write_test.go +++ b/core/write_test.go @@ -252,44 +252,11 @@ func TestFindFieldIndex_tagOverwriteFieldName(t *testing.T) { } } -type testSettable struct { - Value string -} - -type testSettableStruct struct { - Settable testSettable `survey:"settable"` -} - -func (t *testSettable) WriteAnswer(value interface{}) error { - if v, ok := value.(string); ok { - t.Value = v - return nil - } - return fmt.Errorf("Incompatible type %T", value) -} - -func TestWriteWithSettable(t *testing.T) { - testSet1 := testSettable{} - err := WriteAnswer(&testSet1, "prompt", "stringVal") - assert.Nil(t, err) - assert.Equal(t, "stringVal", testSet1.Value) - - testSet2 := testSettable{} - err = WriteAnswer(&testSet2, "prompt", 123) - assert.Error(t, fmt.Errorf("Incompatible type int64"), err) - assert.Equal(t, "", testSet2.Value) - - testSetStruct := testSettableStruct{} - err = WriteAnswer(&testSetStruct, "settable", "stringVal1") - assert.Nil(t, err) - assert.Equal(t, testSetStruct.Settable.Value, "stringVal1") -} - type testFieldSettable struct { Values map[string]string } -func (t *testFieldSettable) WriteAnswerField(name string, value interface{}) error { +func (t *testFieldSettable) WriteAnswer(name string, value interface{}) error { if t.Values == nil { t.Values = map[string]string{} } @@ -302,12 +269,12 @@ func (t *testFieldSettable) WriteAnswerField(name string, value interface{}) err func TestWriteWithFieldSettable(t *testing.T) { testSet1 := testFieldSettable{} - err := WriteAnswer(&testSet1, "prompt", "stringVal") + err := WriteAnswer(&testSet1, "values", "stringVal") assert.Nil(t, err) - assert.Equal(t, map[string]string{"prompt": "stringVal"}, testSet1.Values) + assert.Equal(t, map[string]string{"values": "stringVal"}, testSet1.Values) testSet2 := testFieldSettable{} - err = WriteAnswer(&testSet2, "prompt", 123) + err = WriteAnswer(&testSet2, "values", 123) assert.Error(t, fmt.Errorf("Incompatible type int64"), err) assert.Equal(t, map[string]string{}, testSet2.Values) } From 71d5ee75a439966554dc5f3115317b69df59f412 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Wed, 19 Jul 2017 00:41:16 -0700 Subject: [PATCH 2/4] removed old interface example from readme --- README.md | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 936c20c6..dc20925c 100644 --- a/README.md +++ b/README.md @@ -171,23 +171,20 @@ prompt := &survey.MultiSelect{..., PageSize: 10} ## Custom Types survey will assign prompt answers to your custom types if they implement one of these interfaces: + ```golang type settable interface { - WriteAnswer(value interface{}) error -} -``` -```golang -type fieldsettable interface { - WriteAnswerField(field string, value interface{}) error + WriteAnswer(field string, value interface{}) error } ``` Here is an example how to use them: + ```golang type MyValue struct { value string } -func (my *MyValue) WriteAnswer(value interface{}) error { +func (my *MyValue) WriteAnswer(name string, value interface{}) error { my.value = value.(string) } @@ -199,23 +196,6 @@ survey.AskOne( &myval, nil, ) -// myval.value should be populated with the prompt result now. -``` - -If you want to capture the name associated with the prompt you can use this form: -```golang -type MyMapValue struct { - value map[string]string -} -func (my *MyMapValue) WriteAnswerField(name string, value interface{}) error { - my.value[name] = value.(string) -} - -mymap := MyMapValue{value: make(map[string]string)} -// qs defined in previous examples -survey.Ask(qs, &mymap) -// mymap.value["name"] and mymap.value["color"] should be populated with -// the corresponding prompt results now. ``` ## Validation From 10ffc361b4a8d494ec0f957a4051f7892c6425a9 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Wed, 19 Jul 2017 00:43:17 -0700 Subject: [PATCH 3/4] reordered readme --- README.md | 61 +++++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index dc20925c..f56cf19d 100644 --- a/README.md +++ b/README.md @@ -63,11 +63,11 @@ func main() { 1. [Confirm](#confirm) 1. [Select](#select) 1. [MultiSelect](#multiselect) -1. [Custom Types](#custom-types) 1. [Validation](#validation) 1. [Built-in Validators](#built-in-validators) 1. [Help Text](#help-text) 1. [Changing the input rune](#changing-the-input-run) +1. [Custom Types](#custom-types) 1. [Customizing Output](#customizing-output) 1. [Versioning](#versioning) @@ -168,36 +168,6 @@ change the global `survey.PageCount`, or set the `PageSize` field on the prompt: prompt := &survey.MultiSelect{..., PageSize: 10} ``` -## Custom Types - -survey will assign prompt answers to your custom types if they implement one of these interfaces: - -```golang -type settable interface { - WriteAnswer(field string, value interface{}) error -} -``` - -Here is an example how to use them: - -```golang -type MyValue struct { - value string -} -func (my *MyValue) WriteAnswer(name string, value interface{}) error { - my.value = value.(string) -} - -myval := MyValue{} -survey.AskOne( - &survey.Input{ - Message: "Enter something:", - }, - &myval, - nil, -) -``` - ## Validation Validating individual responses for a particular question can be done by defining a @@ -264,6 +234,35 @@ surveyCore.HelpInputRune = '^' survey.AskOne(prompt, &number, nil) ``` +## Custom Types + +survey will assign prompt answers to your custom types if they implement one of these interfaces: + +```golang +type settable interface { + WriteAnswer(field string, value interface{}) error +} +``` + +Here is an example how to use them: + +```golang +type MyValue struct { + value string +} +func (my *MyValue) WriteAnswer(name string, value interface{}) error { + my.value = value.(string) +} + +myval := MyValue{} +survey.AskOne( + &survey.Input{ + Message: "Enter something:", + }, + &myval, + nil, +) +``` ## Customizing Output From 8a112f03ccaa28241ecb77d157f47e7dfaba89c6 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Wed, 19 Jul 2017 10:25:20 -0700 Subject: [PATCH 4/4] added test for writing to tagged fields --- core/write_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/write_test.go b/core/write_test.go index e3552158..50c237a7 100644 --- a/core/write_test.go +++ b/core/write_test.go @@ -256,6 +256,10 @@ type testFieldSettable struct { Values map[string]string } +type testTaggedStruct struct { + TaggedValue string `survey:"tagged"` +} + func (t *testFieldSettable) WriteAnswer(name string, value interface{}) error { if t.Values == nil { t.Values = map[string]string{} @@ -277,6 +281,11 @@ func TestWriteWithFieldSettable(t *testing.T) { err = WriteAnswer(&testSet2, "values", 123) assert.Error(t, fmt.Errorf("Incompatible type int64"), err) assert.Equal(t, map[string]string{}, testSet2.Values) + + testSetStruct := testTaggedStruct{} + err = WriteAnswer(&testSetStruct, "tagged", "stringVal1") + assert.Nil(t, err) + assert.Equal(t, testSetStruct.TaggedValue, "stringVal1") } // CONVERSION TESTS