From 009398e6d4757f80ea5721be3b281e24e791162e Mon Sep 17 00:00:00 2001 From: Inanc Gumus Date: Tue, 3 Sep 2019 17:34:21 +0300 Subject: [PATCH] refactor: stringer interface --- interfaces/09-stringer/book.go | 27 +-------- interfaces/09-stringer/main.go | 10 +++- interfaces/09-stringer/timestamp.go | 51 ++++++++++++++++ interfaces/10-marshaler/book.go | 47 +-------------- interfaces/10-marshaler/main.go | 91 ++++++++++++++++++++++++----- 5 files changed, 139 insertions(+), 87 deletions(-) create mode 100644 interfaces/09-stringer/timestamp.go diff --git a/interfaces/09-stringer/book.go b/interfaces/09-stringer/book.go index a7977d2..ee5d00a 100644 --- a/interfaces/09-stringer/book.go +++ b/interfaces/09-stringer/book.go @@ -9,43 +9,20 @@ package main import ( "fmt" - "strconv" - "time" ) type book struct { product - published interface{} + published timestamp } // book satisfies the fmt.Stringer func (b *book) String() string { - p := format(b.published) - // product.String() has a pointer receiver. // That's why you need to manually take the product's address here. // // If you pass: "b.product", Go would pass it as a copy to Sprintf. // In that case, Go can't deference b.product automatically. // It's because: b.product would be different value—a copy. - return fmt.Sprintf("%s - (%v)", &b.product, p) -} - -func format(v interface{}) string { - var t int - - switch v := v.(type) { - case int: - t = v - case string: - t, _ = strconv.Atoi(v) - default: - return "unknown" - } - - // Mon Jan 2 15:04:05 -0700 MST 2006 - const layout = "2006/01" - - u := time.Unix(int64(t), 0) - return u.Format(layout) + return fmt.Sprintf("%s - (%s)", &b.product, b.published) } diff --git a/interfaces/09-stringer/main.go b/interfaces/09-stringer/main.go index 0f0b61a..05e9862 100644 --- a/interfaces/09-stringer/main.go +++ b/interfaces/09-stringer/main.go @@ -11,9 +11,9 @@ import "fmt" func main() { store := list{ - &book{product{"moby dick", 10}, 118281600}, - &book{product{"odyssey", 15}, "733622400"}, - &book{product{"hobbit", 25}, nil}, + &book{product{"moby dick", 10}, toTimestamp(118281600)}, + &book{product{"odyssey", 15}, toTimestamp("733622400")}, + &book{product{"hobbit", 25}, unknown}, &puzzle{product{"rubik's cube", 5}}, &game{product{"minecraft", 20}}, &game{product{"tetris", 5}}, @@ -26,4 +26,8 @@ func main() { // but the Print function can print it. // it's because, the list satisfies the stringer. fmt.Print(store) + + // timestamp is useful even if it's zero. + var ts timestamp + fmt.Println(ts) } diff --git a/interfaces/09-stringer/timestamp.go b/interfaces/09-stringer/timestamp.go new file mode 100644 index 0000000..b8cd2a4 --- /dev/null +++ b/interfaces/09-stringer/timestamp.go @@ -0,0 +1,51 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "strconv" + "time" +) + +// Mon Jan 2 15:04:05 -0700 MST 2006 +const layout = "2006/01" + +// unknown is the zero value of a timestamp. +var unknown = timestamp(time.Time{}) + +// Timestamp prints timestamps, it's a stringer. +// Timestamp is useful even if it's zero. +type timestamp time.Time + +// String makes the timestamp a stringer. +func (ts timestamp) String() string { + t := time.Time(ts) + + if t.IsZero() { + return "unknown" + } + + return t.Format(layout) +} + +// toTimestamp was "book.format" before. +// Now it returns a timestamp value depending on the type of `v`. +func toTimestamp(v interface{}) timestamp { + var t int + + switch v := v.(type) { + case int: + t = v + case string: + t, _ = strconv.Atoi(v) + default: + return unknown + } + + return timestamp(time.Unix(int64(t), 0)) +} diff --git a/interfaces/10-marshaler/book.go b/interfaces/10-marshaler/book.go index 5e9e97a..66de72e 100644 --- a/interfaces/10-marshaler/book.go +++ b/interfaces/10-marshaler/book.go @@ -8,57 +8,14 @@ package main import ( - "encoding/json" "fmt" - "strconv" - "time" ) type book struct { product - Published interface{} + Published timestamp } func (b book) String() string { - p := format(b.Published) - return fmt.Sprintf("%s - (%v)", &b.product, p) -} - -// MarshalJSON is an implementation of the json.Marshaler interface. -// json.Marshal and Decode call this method. -func (b *book) MarshalJSON() ([]byte, error) { - // Calling Marshal creates an infinite loop because Marshal calls - // MarshalJSON again and again. - // - // Declaring a clone type without MarshalJSON fixes the problem. - type jbook book - - // book is convertable to jbook - // jbook's underlying type is book. - jb := (*jbook)(b) - - // Customize the formatting of the published field. - // This will result in the published field of the resulting json. - jb.Published = format(b.Published) - - return json.Marshal(jb) -} - -func format(v interface{}) string { - var t int - - switch v := v.(type) { - case int: - t = v - case string: - t, _ = strconv.Atoi(v) - default: - return "unknown" - } - - // Mon Jan 2 15:04:05 -0700 MST 2006 - const layout = "2006/01" - - u := time.Unix(int64(t), 0) - return u.Format(layout) + return fmt.Sprintf("%s - (%s)", &b.product, b.Published) } diff --git a/interfaces/10-marshaler/main.go b/interfaces/10-marshaler/main.go index 55952a2..ea827a3 100644 --- a/interfaces/10-marshaler/main.go +++ b/interfaces/10-marshaler/main.go @@ -14,22 +14,85 @@ import ( ) func main() { - store := list{ - &book{product{"moby dick", 10}, 118281600}, - &book{product{"odyssey", 15}, "733622400"}, - &book{product{"hobbit", 25}, nil}, - &puzzle{product{"rubik's cube", 5}}, - &game{product{"minecraft", 20}}, - &game{product{"tetris", 5}}, - &toy{product{"yoda", 150}}, - } + // store := list{ + // &book{product{"moby dick", 10}, 118281600}, + // &book{product{"odyssey", 15}, "733622400"}, + // &book{product{"hobbit", 25}, nil}, + // &puzzle{product{"rubik's cube", 5}}, + // &game{product{"minecraft", 20}}, + // &game{product{"tetris", 5}}, + // &toy{product{"yoda", 150}}, + // } - out, err := json.MarshalIndent(store, "", "\t") - if err != nil { - log.Fatalln(err) - } - fmt.Println(string(out)) + // out, err := json.MarshalIndent(store, "", "\t") + // if err != nil { + // log.Fatalln(err) + // } + // fmt.Println(string(out)) // store.discount(.5) // fmt.Print(store) + + encode() +} + +func encode() { + + books := []*book{ + {product{"moby dick", 10}, toTime(118281600)}, + {product{"odyssey", 15}, toTime("733622400")}, + {product{"hobbit", 25}, zeroTimestamp}, + } + + for _, b := range books { + fmt.Println(b) + } + + out, err := json.MarshalIndent(books, "", "\t") + if err != nil { + log.Fatalln(err) + } + + fmt.Println(string(out)) +} + +func decode() { + // books = nil + out := []byte(`[ + { + "Title": "moby dick", + "Price": 10, + "Published": "118281600" + }, + { + "Title": "odyssey", + "Price": 15, + "Published": 733622400 + }, + { + "Title": "hobbit", + "Price": 25, + "Published": -62135596800 + }, + { + "Title": "poka", + "Price": 25, + "Published": "1983/03" + }, + { + "Title": "fuka", + "Price": 25 + } +]`) + + var books []*book + + err := json.Unmarshal(out, &books) + if err != nil { + log.Fatalln(err) + } + + for _, b := range books { + fmt.Println(b) + } }