Repository: AlecAivazis/survey
Pull request #240 (2019-08-01 13:52:31)

fix(core): Allow to use `time.Duration` struct fields
## Description

We are trying to use this package to initialize a `time.Duration` field but getting an error when the answer is written to the target struct by [core/write.go#L189-L190](https://github.com/AlecAivazis/survey/blob/0f64ceaa253b1069b6c235e74e056213e7a567e2/core/write.go#L189-L190). This PR adds support for struct fields of type `time.Duration`.

## Example

```go
type Options struct {
	Timeout time.Duration
}

survey.Ask([]*survey.Question{{...}}, new(Options))
```

* Answering with a string in an integer format (for example, **30**) returns the following error: `reflect.Set: value of type int64 is not assignable to type time.Duration`

* Answering with a string in a valid duration format (for example, **30s**) returns the following error: `strconv.ParseInt: parsing "30s": invalid syntax`

AlecAivazis: Nice! Looks like we have an issue in travis that's preventing your build from passing. I'll try to figure out what's going on so we can get this merged ASAP
AlecAivazis: Okay so the latest PR should fix your build problem. Mind rebasing on the latest master?
liranp: Done. Thank you!

Patch:
diff --git a/core/write.go b/core/write.go
index f09db97..8aac529 100644
--- a/core/write.go
+++ b/core/write.go
@@ -6,6 +6,7 @@ import (
 	"reflect"
 	"strconv"
 	"strings"
+	"time"
 )
 
 // the tag used to denote the name of the question
@@ -187,7 +188,11 @@ func copy(t reflect.Value, v reflect.Value) (err error) {
 				castVal = int32(val64)
 			}
 		case reflect.Int64:
-			castVal, casterr = strconv.ParseInt(vString, 10, 64)
+			if t.Type() == reflect.TypeOf(time.Duration(0)) {
+				castVal, casterr = time.ParseDuration(vString)
+			} else {
+				castVal, casterr = strconv.ParseInt(vString, 10, 64)
+			}
 		case reflect.Uint:
 			var val64 uint64
 			val64, casterr = strconv.ParseUint(vString, 10, 8)


Test patch:
diff --git a/core/write_test.go b/core/write_test.go
index c89ec82..f02a500 100644
--- a/core/write_test.go
+++ b/core/write_test.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"reflect"
 	"testing"
+	"time"
 
 	"github.com/stretchr/testify/assert"
 )
@@ -613,10 +614,11 @@ func TestWrite_canStringToFloat64(t *testing.T) {
 func TestWrite_canConvertStructFieldTypes(t *testing.T) {
 	// the struct to hold the answer
 	ptr := struct {
-		Name   string
-		Age    uint
-		Male   bool
-		Height float64
+		Name    string
+		Age     uint
+		Male    bool
+		Height  float64
+		Timeout time.Duration
 	}{}
 
 	// write the values as strings
@@ -624,6 +626,7 @@ func TestWrite_canConvertStructFieldTypes(t *testing.T) {
 	check(t, WriteAnswer(&ptr, "age", "22"))
 	check(t, WriteAnswer(&ptr, "male", "true"))
 	check(t, WriteAnswer(&ptr, "height", "6.2"))
+	check(t, WriteAnswer(&ptr, "timeout", "30s"))
 
 	// make sure we changed the fields
 	if ptr.Name != "Bob" {
@@ -641,6 +644,10 @@ func TestWrite_canConvertStructFieldTypes(t *testing.T) {
 	if ptr.Height != 6.2 {
 		t.Error("Did not mutate Height when writing answer.")
 	}
+
+	if ptr.Timeout != time.Duration(30*time.Second) {
+		t.Error("Did not mutate Timeout when writing answer.")
+	}
 }
 
 func check(t *testing.T, err error) {