Initial commit
This commit is contained in:
179
internal/haberdasherserver/server.go
Normal file
179
internal/haberdasherserver/server.go
Normal file
@ -0,0 +1,179 @@
|
||||
package haberdasherserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/twitchtv/twirp"
|
||||
|
||||
pb "haberdasher-twirp/haberdasher"
|
||||
)
|
||||
|
||||
const dbBucket = "hats"
|
||||
|
||||
type Server struct{}
|
||||
|
||||
type Hat struct {
|
||||
Inches int32 `json:"inchues"`
|
||||
Color string `json:"color"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type HatQuery struct {
|
||||
Limit int32
|
||||
}
|
||||
|
||||
type Store struct {
|
||||
db *bolt.DB
|
||||
}
|
||||
|
||||
func HatToHatModel(h *pb.Hat) Hat {
|
||||
return Hat{
|
||||
Inches: h.Inches,
|
||||
Color: h.Color,
|
||||
Name: h.Name,
|
||||
}
|
||||
}
|
||||
|
||||
func HatModelToHat(h Hat) *pb.Hat {
|
||||
return &pb.Hat{
|
||||
Inches: h.Inches,
|
||||
Color: h.Color,
|
||||
Name: h.Name,
|
||||
}
|
||||
}
|
||||
|
||||
func HatsModelToHats(hs []Hat) (hats *pb.Hats) {
|
||||
hats = &pb.Hats{}
|
||||
for _, h := range hs {
|
||||
hat := HatModelToHat(h)
|
||||
hats.Hats = append(hats.Hats, hat)
|
||||
}
|
||||
return hats
|
||||
}
|
||||
|
||||
func HatQueryToHatQueryModel(q *pb.HatQuery) HatQuery {
|
||||
return HatQuery{
|
||||
Limit: q.Limit,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) MakeHat(ctx context.Context, size *pb.Size) (hat *pb.Hat, err error) {
|
||||
st, _ := NewStore("hat.db", 0600, nil)
|
||||
defer st.Close()
|
||||
|
||||
if size.Inches <= 0 {
|
||||
return nil, twirp.InvalidArgumentError("inches", "I can't make a hat that small!")
|
||||
}
|
||||
|
||||
h := Hat{
|
||||
Inches: size.Inches,
|
||||
Color: []string{"white", "black", "red", "blue"}[rand.Intn(4)],
|
||||
Name: []string{"bowler", "baseball cap", "top hat", "derby"}[rand.Intn(3)],
|
||||
}
|
||||
|
||||
fmt.Printf("made hat: %+v\n", h)
|
||||
|
||||
st.SaveHat(h)
|
||||
|
||||
return HatModelToHat(h), nil
|
||||
}
|
||||
|
||||
func (s *Server) ListHats(ctx context.Context, q *pb.HatQuery) (hats *pb.Hats, err error) {
|
||||
/*
|
||||
var hat *pb.Hat
|
||||
|
||||
hats = &pb.Hats{}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
hat = &pb.Hat{
|
||||
Inches: int32(rand.Intn(12)),
|
||||
Color: []string{"white", "black", "red", "blue"}[rand.Intn(4)],
|
||||
Name: []string{"bowler", "baseball cap", "top hat", "derby"}[rand.Intn(3)],
|
||||
}
|
||||
hats.Hats = append(hats.Hats, hat)
|
||||
}
|
||||
*/
|
||||
|
||||
st, _ := NewStore("hat.db", 0600, nil)
|
||||
defer st.Close()
|
||||
|
||||
hs, err := st.ListHats(HatQueryToHatQueryModel(q))
|
||||
|
||||
return HatsModelToHats(hs), nil
|
||||
}
|
||||
|
||||
func itob(v uint64) []byte {
|
||||
b := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(b, uint64(v))
|
||||
return b
|
||||
}
|
||||
|
||||
func btoi(b []byte) uint64 {
|
||||
return binary.BigEndian.Uint64(b)
|
||||
}
|
||||
|
||||
func NewStore(path string, mode os.FileMode, options *bolt.Options) (s *Store, err error) {
|
||||
s = &Store{}
|
||||
s.db, err = bolt.Open(path, mode, options)
|
||||
if err != nil {
|
||||
fmt.Printf("bolt open: %s", err)
|
||||
}
|
||||
|
||||
s.db.Update(func(tx *bolt.Tx) error {
|
||||
if _, err := tx.CreateBucket([]byte(dbBucket)); err != nil {
|
||||
return fmt.Errorf("create bucket: %s", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return s, err
|
||||
}
|
||||
|
||||
func (s *Store) SaveHat(h Hat) (err error) {
|
||||
return s.db.Update(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte("hats"))
|
||||
|
||||
id, _ := b.NextSequence()
|
||||
|
||||
buf, err := json.Marshal(h)
|
||||
if err != nil {
|
||||
return fmt.Errorf("json marshal: %s", err)
|
||||
}
|
||||
|
||||
return b.Put(itob(id), buf)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Store) ListHats(q HatQuery) (hats []Hat, err error) {
|
||||
var hat Hat
|
||||
var i int
|
||||
|
||||
s.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte("hats"))
|
||||
|
||||
c := b.Cursor()
|
||||
|
||||
for k, v := c.Last(); k != nil && i < int(q.Limit); k, v = c.Prev() {
|
||||
if err := json.Unmarshal(v, &hat); err != nil {
|
||||
return fmt.Errorf("json unmarshal: %s", err)
|
||||
}
|
||||
h := hat
|
||||
hats = append(hats, h)
|
||||
i++
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return hats, nil
|
||||
}
|
||||
|
||||
func (s *Store) Close() (err error) {
|
||||
return s.db.Close()
|
||||
}
|
Reference in New Issue
Block a user