From 4c9645a10d40986d85c01f54153a552d1871b173 Mon Sep 17 00:00:00 2001 From: Inanc Gumus Date: Mon, 2 Sep 2019 22:33:34 +0300 Subject: [PATCH] add: marshaler interface --- interfaces/10-marshaler/_decoder.go | 60 +++++++++++++++ interfaces/10-marshaler/_decoder_reader.go | 85 ++++++++++++++++++++++ interfaces/10-marshaler/_main.go | 42 +++++++++++ interfaces/10-marshaler/_main_reader.go | 30 ++++++++ interfaces/10-marshaler/book.go | 64 ++++++++++++++++ interfaces/10-marshaler/game.go | 12 +++ interfaces/10-marshaler/list.go | 70 ++++++++++++++++++ interfaces/10-marshaler/main.go | 35 +++++++++ interfaces/10-marshaler/money.go | 16 ++++ interfaces/10-marshaler/product.go | 25 +++++++ interfaces/10-marshaler/puzzle.go | 12 +++ interfaces/10-marshaler/toy.go | 12 +++ 12 files changed, 463 insertions(+) create mode 100644 interfaces/10-marshaler/_decoder.go create mode 100644 interfaces/10-marshaler/_decoder_reader.go create mode 100644 interfaces/10-marshaler/_main.go create mode 100644 interfaces/10-marshaler/_main_reader.go create mode 100644 interfaces/10-marshaler/book.go create mode 100644 interfaces/10-marshaler/game.go create mode 100644 interfaces/10-marshaler/list.go create mode 100644 interfaces/10-marshaler/main.go create mode 100644 interfaces/10-marshaler/money.go create mode 100644 interfaces/10-marshaler/product.go create mode 100644 interfaces/10-marshaler/puzzle.go create mode 100644 interfaces/10-marshaler/toy.go diff --git a/interfaces/10-marshaler/_decoder.go b/interfaces/10-marshaler/_decoder.go new file mode 100644 index 0000000..75f7669 --- /dev/null +++ b/interfaces/10-marshaler/_decoder.go @@ -0,0 +1,60 @@ +// 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 ( + "encoding/json" + "reflect" +) + +type decoder map[string]interface{} + +type schema struct { + Category string + Item json.RawMessage +} + +func (d decoder) decode(data []byte) (list, error) { + var schemas []schema + + if err := json.Unmarshal(data, &schemas); err != nil { + return nil, err + } + + return d.products(schemas) +} + +func (d decoder) products(ss []schema) (list, error) { + var store list + + for _, s := range ss { + prod, err := d.product(s.Category, s.Item) + + if err != nil { + return nil, err + } + + store = append(store, prod) + } + + return store, nil +} + +func (d decoder) product(kind string, data []byte) (item, error) { + p := d.newProduct(kind) + + err := json.Unmarshal(data, p) + + return p, err +} + +func (d decoder) newProduct(kind string) item { + t := reflect.TypeOf(d[kind]) + v := reflect.New(t) + return v.Interface().(item) +} diff --git a/interfaces/10-marshaler/_decoder_reader.go b/interfaces/10-marshaler/_decoder_reader.go new file mode 100644 index 0000000..a0f9e5b --- /dev/null +++ b/interfaces/10-marshaler/_decoder_reader.go @@ -0,0 +1,85 @@ +// 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 ( + "encoding/json" + "io" + "io/ioutil" + "os" + "reflect" +) + +type decoder map[string]interface{} + +type schema struct { + Category string + Item json.RawMessage +} + +func (d decoder) fromFile(path string) (list, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + + return d.readFile(f) +} + +func (d decoder) readFile(f *os.File) (list, error) { + return d.read(f) +} + +func (d decoder) read(r io.Reader) (list, error) { + data, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + + return d.decode(data) +} + +func (d decoder) decode(data []byte) (list, error) { + var schemas []schema + + if err := json.Unmarshal(data, &schemas); err != nil { + return nil, err + } + + return d.products(schemas) +} + +func (d decoder) products(ss []schema) (list, error) { + var store list + + for _, s := range ss { + prod, err := d.product(s.Category, s.Item) + + if err != nil { + return nil, err + } + + store = append(store, prod) + } + + return store, nil +} + +func (d decoder) product(kind string, data []byte) (item, error) { + p := d.newProduct(kind) + + err := json.Unmarshal(data, p) + + return p, err +} + +func (d decoder) newProduct(kind string) item { + t := reflect.TypeOf(d[kind]) + v := reflect.New(t) + return v.Interface().(item) +} diff --git a/interfaces/10-marshaler/_main.go b/interfaces/10-marshaler/_main.go new file mode 100644 index 0000000..9679ed2 --- /dev/null +++ b/interfaces/10-marshaler/_main.go @@ -0,0 +1,42 @@ +// 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 ( + "fmt" + "io/ioutil" + "log" + "os" +) + +func main() { + dec := make(decoder) + dec["book"] = book{} + dec["game"] = game{} + dec["puzzle"] = puzzle{} + dec["toy"] = toy{} + + data, err := ioutil.ReadAll(os.Stdin) + if err != nil { + log.Fatalln(err) + } + + store, err := dec.decode(data) + if err != nil { + log.Fatalln(err) + } + + fmt.Print(store) + + // store.discount(.5) + // fmt.Print(store) + + // ---- + // data, err := save(store) + // fmt.Println(string(data), err) +} diff --git a/interfaces/10-marshaler/_main_reader.go b/interfaces/10-marshaler/_main_reader.go new file mode 100644 index 0000000..9dc25be --- /dev/null +++ b/interfaces/10-marshaler/_main_reader.go @@ -0,0 +1,30 @@ +// 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 ( + "fmt" + "log" + "os" +) + +func main() { + dec := decoder{ + "book": book{}, + "game": game{}, + "puzzle": puzzle{}, + "toy": toy{}, + } + + store, err := dec.read(os.Stdin) + if err != nil { + log.Fatalln(err) + } + + fmt.Print(store) +} diff --git a/interfaces/10-marshaler/book.go b/interfaces/10-marshaler/book.go new file mode 100644 index 0000000..5e9e97a --- /dev/null +++ b/interfaces/10-marshaler/book.go @@ -0,0 +1,64 @@ +// 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 ( + "encoding/json" + "fmt" + "strconv" + "time" +) + +type book struct { + product + Published interface{} +} + +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) +} diff --git a/interfaces/10-marshaler/game.go b/interfaces/10-marshaler/game.go new file mode 100644 index 0000000..3604b15 --- /dev/null +++ b/interfaces/10-marshaler/game.go @@ -0,0 +1,12 @@ +// 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 + +type game struct { + product +} diff --git a/interfaces/10-marshaler/list.go b/interfaces/10-marshaler/list.go new file mode 100644 index 0000000..a7289ae --- /dev/null +++ b/interfaces/10-marshaler/list.go @@ -0,0 +1,70 @@ +// 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 ( + "encoding/json" + "fmt" + "reflect" + "strings" +) + +type item interface { + discount(float64) + fmt.Stringer +} + +// schema adds an additional category field +// to the product types. +type schema struct { + // Category is the product category field. + Category string + + // Item is an interface value. + // Here, we are embedding an interface value. + Item item +} + +type list []item + +// MarshalJSON is an implementation of the json.Marshaler interface. +// json.Marshal and Decode call this method. +func (l list) MarshalJSON() ([]byte, error) { + var schemas []schema + + for _, it := range l { + // TypeOf -> finds the dynamic type of "it" + // Elem -> returns the element type of the pointer + // Name -> returns the type name as string + cat := reflect.TypeOf(it).Elem().Name() + schemas = append(schemas, schema{cat, it}) + } + + return json.Marshal(schemas) +} + +func (l list) String() string { + if len(l) == 0 { + return "Sorry. We're waiting for delivery 🚚." + } + + var str strings.Builder + + for _, it := range l { + str.WriteString(it.String()) + str.WriteRune('\n') + } + + return str.String() +} + +func (l list) discount(ratio float64) { + for _, it := range l { + it.discount(ratio) + } +} diff --git a/interfaces/10-marshaler/main.go b/interfaces/10-marshaler/main.go new file mode 100644 index 0000000..55952a2 --- /dev/null +++ b/interfaces/10-marshaler/main.go @@ -0,0 +1,35 @@ +// 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 ( + "encoding/json" + "fmt" + "log" +) + +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}}, + } + + out, err := json.MarshalIndent(store, "", "\t") + if err != nil { + log.Fatalln(err) + } + fmt.Println(string(out)) + + // store.discount(.5) + // fmt.Print(store) +} diff --git a/interfaces/10-marshaler/money.go b/interfaces/10-marshaler/money.go new file mode 100644 index 0000000..095a039 --- /dev/null +++ b/interfaces/10-marshaler/money.go @@ -0,0 +1,16 @@ +// 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 "fmt" + +type money float64 + +func (m money) String() string { + return fmt.Sprintf("$%.2f", m) +} diff --git a/interfaces/10-marshaler/product.go b/interfaces/10-marshaler/product.go new file mode 100644 index 0000000..2fd584d --- /dev/null +++ b/interfaces/10-marshaler/product.go @@ -0,0 +1,25 @@ +// 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 ( + "fmt" +) + +type product struct { + Title string + Price money +} + +func (p *product) String() string { + return fmt.Sprintf("%-15s: %s", p.Title, p.Price) +} + +func (p *product) discount(ratio float64) { + p.Price *= money(1 - ratio) +} diff --git a/interfaces/10-marshaler/puzzle.go b/interfaces/10-marshaler/puzzle.go new file mode 100644 index 0000000..acbaa47 --- /dev/null +++ b/interfaces/10-marshaler/puzzle.go @@ -0,0 +1,12 @@ +// 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 + +type puzzle struct { + product +} diff --git a/interfaces/10-marshaler/toy.go b/interfaces/10-marshaler/toy.go new file mode 100644 index 0000000..ab5613d --- /dev/null +++ b/interfaces/10-marshaler/toy.go @@ -0,0 +1,12 @@ +// 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 + +type toy struct { + product +}