Skip to content

Commit

Permalink
Merge pull request #3 from sonirico/feature/model-registry
Browse files Browse the repository at this point in the history
feat: multimodels
  • Loading branch information
sonirico committed Sep 6, 2022
2 parents d6a1405 + 9eb8e66 commit d4dff81
Show file tree
Hide file tree
Showing 7 changed files with 303 additions and 5 deletions.
87 changes: 87 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,93 @@ func main() {

---

### Multi model parsers & compilers

It is also supported several models being serialized on the same wire. Just employ the multimodel API
to register them.

```go
type (
Animal struct {
Age uint8
Specie string
}

Flat struct {
Price float32
Address string
}
)

const (
AnimalType int = 0
FlatType = 1
)

func (a Animal) ParcoID() int {
return AnimalType
}

func (a Flat) ParcoID() int {
return FlatType
}

func main() {
animalBuilder := parco.Builder[Animal](parco.ObjectFactory[Animal]()).
SmallVarchar(
func(a *Animal) string { return a.Specie },
func(a *Animal, specie string) { a.Specie = specie },
).
UInt8(
func(a *Animal) uint8 { return a.Age },
func(a *Animal, age uint8) { a.Age = age },
)

flatBuilder := parco.Builder[Flat](parco.ObjectFactory[Flat]()).
Float32(
binary.LittleEndian,
func(f *Flat) float32 { return f.Price },
func(f *Flat, price float32) { f.Price = price },
).
SmallVarchar(
func(f *Flat) string { return f.Address },
func(f *Flat, address string) { f.Address = address },
)

parCo := parco.MultiBuilder(parco.UInt8Header()). // Register up to 255 different models
MustRegister(AnimalType, animalBuilder).
MustRegister(FlatType, flatBuilder)

buf := bytes.NewBuffer(nil)

// `Compile` API may be used if your models satisfy the `serializable` interface:
// type seriazable[T comparable] interface{ ParcoID() int }
_ = parCo.Compile(Animal{Age: 10, Specie: "monkeys"}, buf)
_ = parCo.Compile(Flat{Price: 42, Address: "Plaza mayor"}, buf)

// Or, the `CompileAny` can be employed instead by specifying each model ID.
_ = parCo.CompileAny(AnimalType, Animal{Age: 7, Specie: "felix catus"}, buf)

id, something, _ := parCo.Parse(buf)
Print(id, something)
id, something, _ = parCo.Parse(buf)
Print(id, something)
id, something, _ = parCo.Parse(buf)
Print(id, something)
}

func Print(id int, x any) {
switch id {
case AnimalType:
animal := x.(Animal)
log.Println("animal:", animal)
case FlatType:
flat := x.(Flat)
log.Println("flat", flat)
}
}
```

### Supported fields

| Field | Status | Size |
Expand Down
10 changes: 6 additions & 4 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import (
)

var (
ErrNotIntegerType = errors.New("not an integer type")
ErrOverflow = errors.New("bytes overflow")
ErrCannotRead = errors.New("unsufficient bytes read")
ErrCannotWrite = errors.New("unsufficient bytes written")
ErrNotIntegerType = errors.New("not an integer type")
ErrOverflow = errors.New("bytes overflow")
ErrCannotRead = errors.New("unsufficient bytes read")
ErrCannotWrite = errors.New("unsufficient bytes written")
ErrAlreadyRegistered = errors.New("builder is registered already")
ErrUnknownType = errors.New("unknown type")
)

type ErrUnSufficientBytes struct {
Expand Down
89 changes: 89 additions & 0 deletions examples/registry/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package main

import (
"bytes"
"encoding/binary"
"log"

"github.com/sonirico/parco"
)

type (
Animal struct {
Age uint8
Specie string
}

Flat struct {
Price float32
Address string
}
)

const (
AnimalType int = 0
FlatType = 1
)

func (a Animal) ParcoID() int {
return AnimalType
}

func (a Flat) ParcoID() int {
return FlatType
}

func main() {
animalBuilder := parco.Builder[Animal](parco.ObjectFactory[Animal]()).
SmallVarchar(
func(a *Animal) string { return a.Specie },
func(a *Animal, specie string) { a.Specie = specie },
).
UInt8(
func(a *Animal) uint8 { return a.Age },
func(a *Animal, age uint8) { a.Age = age },
)

flatBuilder := parco.Builder[Flat](parco.ObjectFactory[Flat]()).
Float32(
binary.LittleEndian,
func(f *Flat) float32 { return f.Price },
func(f *Flat, price float32) { f.Price = price },
).
SmallVarchar(
func(f *Flat) string { return f.Address },
func(f *Flat, address string) { f.Address = address },
)

parCo := parco.MultiBuilder(parco.UInt8Header()). // Register up to 255 different models
MustRegister(AnimalType, animalBuilder).
MustRegister(FlatType, flatBuilder)

buf := bytes.NewBuffer(nil)

// `Compile` API may be used if your models satisfy the `serializable` interface:
// type seriazable[T comparable] interface{ ParcoID() int }
_ = parCo.Compile(Animal{Age: 10, Specie: "monkeys"}, buf)
_ = parCo.Compile(Flat{Price: 42, Address: "Plaza mayor"}, buf)

// Or, the `CompileAny` can be employed instead by specifying each model ID.
_ = parCo.CompileAny(AnimalType, Animal{Age: 7, Specie: "felix catus"}, buf)

id, something, _ := parCo.Parse(buf)
Print(id, something)
id, something, _ = parCo.Parse(buf)
Print(id, something)
id, something, _ = parCo.Parse(buf)
Print(id, something)
}

func Print(id int, x any) {
switch id {
case AnimalType:
animal := x.(Animal)
log.Println("animal:", animal)
case FlatType:
flat := x.(Flat)
log.Println("flat", flat)
}
}
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ module github.com/sonirico/parco

go 1.19

require github.com/vmihailenco/msgpack/v5 v5.3.5
require (
github.com/pkg/errors v0.9.1
github.com/vmihailenco/msgpack/v5 v5.3.5
)

require github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand Down
12 changes: 12 additions & 0 deletions parco_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,22 @@ func (b ModelBuilder[T]) Compile(value T, w io.Writer) error {
return b.compiler.Compile(value, w)
}

func (b ModelBuilder[T]) CompileAny(value any, w io.Writer) error {
t, ok := value.(T)
if !ok {
return ErrUnknownType
}
return b.compiler.Compile(t, w)
}

func (b ModelBuilder[T]) Parse(r io.Reader) (T, error) {
return b.parser.Parse(r)
}

func (b ModelBuilder[T]) ParseAny(r io.Reader) (any, error) {
return b.parser.Parse(r)
}

func (b ModelBuilder[T]) Parco() (*Parser[T], *Compiler[T]) {
return b.parser, b.compiler
}
Expand Down
103 changes: 103 additions & 0 deletions parco_builder_multi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package parco

import (
"fmt"
"github.com/pkg/errors"
"io"
)

type (
serializable[T comparable] interface {
ParcoID() T
}

parserAny interface {
ParseAny(io.Reader) (any, error)
}

compilerAny interface {
CompileAny(any, io.Writer) error
}

builderAny interface {
parserAny
compilerAny
}

ModelMultiBuilder[T comparable] struct {
header Type[T]

parsers map[T]parserAny

compilers map[T]compilerAny
}
)

func MultiBuilder[T comparable](header Type[T]) *ModelMultiBuilder[T] {
return &ModelMultiBuilder[T]{
header: header,
parsers: make(map[T]parserAny),
compilers: make(map[T]compilerAny),
}
}

func (b *ModelMultiBuilder[T]) Register(id T, builder builderAny) (*ModelMultiBuilder[T], error) {
if _, ok := b.parsers[id]; !ok {
b.parsers[id] = builder
b.compilers[id] = builder
return b, nil
}
return b, errors.Wrapf(ErrAlreadyRegistered, "id: %v", id)
}

func (b *ModelMultiBuilder[T]) MustRegister(id T, builder builderAny) *ModelMultiBuilder[T] {
if _, ok := b.parsers[id]; !ok {
b.parsers[id] = builder
b.compilers[id] = builder
return b
}
panic(fmt.Sprintf("this id is registered already: %v", id))
}

func (b *ModelMultiBuilder[T]) Parse(r io.Reader) (id T, res any, err error) {
id, err = b.header.Parse(r)
if err != nil {
return
}

p, ok := b.parsers[id]

if !ok {
err = errors.Wrapf(ErrUnknownType, "%v", id)
return
}

res, err = p.ParseAny(r)
return
}

func (b *ModelMultiBuilder[T]) Compile(item serializable[T], w io.Writer) (err error) {
return b.compile(item.ParcoID(), item, w)
}

func (b *ModelMultiBuilder[T]) CompileAny(id T, item any, w io.Writer) (err error) {
return b.compile(id, item, w)
}

func (b *ModelMultiBuilder[T]) compile(id T, item any, w io.Writer) (err error) {
err = b.header.Compile(id, w)

if err != nil {
return
}

c, ok := b.compilers[id]

if !ok {
err = errors.Wrapf(ErrUnknownType, "%v", id)
return
}

err = c.CompileAny(item, w)
return
}

0 comments on commit d4dff81

Please sign in to comment.