From 51dfbaec8ed02e8ab6c17dc127cbaa7c8d35ecf4 Mon Sep 17 00:00:00 2001
From: Connor Poole <connor.poole@gmail.com>
Date: Wed, 19 Oct 2022 06:55:31 +0000
Subject: [PATCH] fix: allow hscan to continue past first failure

---
 internal/hscan/hscan.go      | 11 ++++++++++-
 internal/hscan/hscan_test.go | 17 +++++++++++++++++
 2 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/internal/hscan/hscan.go b/internal/hscan/hscan.go
index 852c8bd525..77477eafd7 100644
--- a/internal/hscan/hscan.go
+++ b/internal/hscan/hscan.go
@@ -67,6 +67,9 @@ func Struct(dst interface{}) (StructValue, error) {
 
 // Scan scans the results from a key-value Redis map result set to a destination struct.
 // The Redis keys are matched to the struct's field with the `redis` tag.
+// This method will attempt to unmarshal each field and will return an error if any of the 
+// fields cannot be unmarshalled. The destination struct will have the failed fields set to
+// their zero value.
 func Scan(dst interface{}, keys []interface{}, vals []interface{}) error {
 	if len(keys) != len(vals) {
 		return errors.New("args should have the same number of keys and vals")
@@ -77,6 +80,8 @@ func Scan(dst interface{}, keys []interface{}, vals []interface{}) error {
 		return err
 	}
 
+	scanErrors := []error{}
+
 	// Iterate through the (key, value) sequence.
 	for i := 0; i < len(vals); i++ {
 		key, ok := keys[i].(string)
@@ -90,10 +95,14 @@ func Scan(dst interface{}, keys []interface{}, vals []interface{}) error {
 		}
 
 		if err := strct.Scan(key, val); err != nil {
-			return err
+			scanErrors = append(scanErrors, err)
 		}
 	}
 
+	if len(scanErrors) > 0 {
+		return fmt.Errorf("scan errors: %v", scanErrors)
+	}
+
 	return nil
 }
 
diff --git a/internal/hscan/hscan_test.go b/internal/hscan/hscan_test.go
index ab4c0e1dc1..c51bd1939b 100644
--- a/internal/hscan/hscan_test.go
+++ b/internal/hscan/hscan_test.go
@@ -175,4 +175,21 @@ var _ = Describe("Scan", func() {
 		Expect(Scan(&d, i{"bool"}, i{""})).To(HaveOccurred())
 		Expect(Scan(&d, i{"bool"}, i{"123"})).To(HaveOccurred())
 	})
+
+	It("does not stop scanning on first failure", func() {
+		var d data
+
+		keys := i{"bool", "string", "int"}
+		vals := i{"-1", "foobar", "123"}
+
+		err := Scan(&d, keys, vals)
+		Expect(err).To(HaveOccurred())
+
+		Expect(d).To(Equal(data{
+			Bool:   false,
+			String: "foobar",
+			Int:    123,
+		}))
+
+	})
 })