Initial commit

This commit is contained in:
Ryan Cavicchioni 2021-07-26 09:00:25 -05:00
commit 88f803692a
Signed by: ryanc
GPG Key ID: 877EEDAF9245103D
9 changed files with 2060 additions and 0 deletions

35
cmd/client/main.go Normal file
View File

@ -0,0 +1,35 @@
package main
import (
"context"
"fmt"
"net/http"
"os"
"haberdasher-twirp/haberdasher"
)
func main() {
var host string = "http://localhost:8080"
if len(os.Args) > 1 {
host = os.Args[1]
}
client := haberdasher.NewHaberdasherProtobufClient(host, &http.Client{})
hat, err := client.MakeHat(context.Background(), &haberdasher.Size{Inches: 12})
if err != nil {
fmt.Printf("oh no: %v\n", err)
os.Exit(1)
}
fmt.Printf("I have a nice new hat: %+v\n", hat)
hats, err := client.ListHats(context.Background(), &haberdasher.HatQuery{Limit: 20})
if err != nil {
fmt.Printf("oh no: %v\n", err)
os.Exit(1)
}
for i, h := range hats.Hats {
fmt.Printf("%d: %+v\n", i, h)
}
}

20
cmd/main.go Normal file
View File

@ -0,0 +1,20 @@
package main
import (
"net/http"
"os"
"haberdasher-twirp/haberdasher"
"haberdasher-twirp/internal/haberdasherserver"
)
func main() {
var bind string = ":8080"
if len(os.Args) > 1 {
bind = os.Args[1]
}
server := &haberdasherserver.Server{}
twirpHandler := haberdasher.NewHaberdasherServer(server)
http.ListenAndServe(bind, twirpHandler)
}

11
go.mod Normal file
View File

@ -0,0 +1,11 @@
module haberdasher-twirp
go 1.16
require (
github.com/boltdb/bolt v1.3.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/twitchtv/twirp v8.1.0+incompatible // indirect
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
google.golang.org/protobuf v1.27.1 // indirect
)

14
go.sum Normal file
View File

@ -0,0 +1,14 @@
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/twitchtv/twirp v8.1.0+incompatible h1:KGXanpa9LXdVE/V5P/tA27rkKFmXRGCtSNT7zdeeVOY=
github.com/twitchtv/twirp v8.1.0+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=

369
haberdasher/service.pb.go Normal file
View File

@ -0,0 +1,369 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.14.0
// source: haberdasher/service.proto
package haberdasher
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type HatQuery struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Limit int32 `protobuf:"varint,1,opt,name=limit,proto3" json:"limit,omitempty"`
}
func (x *HatQuery) Reset() {
*x = HatQuery{}
if protoimpl.UnsafeEnabled {
mi := &file_haberdasher_service_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *HatQuery) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*HatQuery) ProtoMessage() {}
func (x *HatQuery) ProtoReflect() protoreflect.Message {
mi := &file_haberdasher_service_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use HatQuery.ProtoReflect.Descriptor instead.
func (*HatQuery) Descriptor() ([]byte, []int) {
return file_haberdasher_service_proto_rawDescGZIP(), []int{0}
}
func (x *HatQuery) GetLimit() int32 {
if x != nil {
return x.Limit
}
return 0
}
// Size of a Hat, in inches.
type Size struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Inches int32 `protobuf:"varint,1,opt,name=inches,proto3" json:"inches,omitempty"` // must be > 0
}
func (x *Size) Reset() {
*x = Size{}
if protoimpl.UnsafeEnabled {
mi := &file_haberdasher_service_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Size) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Size) ProtoMessage() {}
func (x *Size) ProtoReflect() protoreflect.Message {
mi := &file_haberdasher_service_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Size.ProtoReflect.Descriptor instead.
func (*Size) Descriptor() ([]byte, []int) {
return file_haberdasher_service_proto_rawDescGZIP(), []int{1}
}
func (x *Size) GetInches() int32 {
if x != nil {
return x.Inches
}
return 0
}
// A Hat is a piece of headwear made by a Haberdasher.
type Hat struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Inches int32 `protobuf:"varint,1,opt,name=inches,proto3" json:"inches,omitempty"`
Color string `protobuf:"bytes,2,opt,name=color,proto3" json:"color,omitempty"` // anything but "invisible"
Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` // i.e. "bowler"
}
func (x *Hat) Reset() {
*x = Hat{}
if protoimpl.UnsafeEnabled {
mi := &file_haberdasher_service_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Hat) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Hat) ProtoMessage() {}
func (x *Hat) ProtoReflect() protoreflect.Message {
mi := &file_haberdasher_service_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Hat.ProtoReflect.Descriptor instead.
func (*Hat) Descriptor() ([]byte, []int) {
return file_haberdasher_service_proto_rawDescGZIP(), []int{2}
}
func (x *Hat) GetInches() int32 {
if x != nil {
return x.Inches
}
return 0
}
func (x *Hat) GetColor() string {
if x != nil {
return x.Color
}
return ""
}
func (x *Hat) GetName() string {
if x != nil {
return x.Name
}
return ""
}
type Hats struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Hats []*Hat `protobuf:"bytes,1,rep,name=hats,proto3" json:"hats,omitempty"`
}
func (x *Hats) Reset() {
*x = Hats{}
if protoimpl.UnsafeEnabled {
mi := &file_haberdasher_service_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Hats) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Hats) ProtoMessage() {}
func (x *Hats) ProtoReflect() protoreflect.Message {
mi := &file_haberdasher_service_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Hats.ProtoReflect.Descriptor instead.
func (*Hats) Descriptor() ([]byte, []int) {
return file_haberdasher_service_proto_rawDescGZIP(), []int{3}
}
func (x *Hats) GetHats() []*Hat {
if x != nil {
return x.Hats
}
return nil
}
var File_haberdasher_service_proto protoreflect.FileDescriptor
var file_haberdasher_service_proto_rawDesc = []byte{
0x0a, 0x19, 0x68, 0x61, 0x62, 0x65, 0x72, 0x64, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2f, 0x73, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x74, 0x77, 0x69,
0x72, 0x70, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x68, 0x61, 0x62, 0x65, 0x72,
0x64, 0x61, 0x73, 0x68, 0x65, 0x72, 0x22, 0x20, 0x0a, 0x08, 0x48, 0x61, 0x74, 0x51, 0x75, 0x65,
0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x1e, 0x0a, 0x04, 0x53, 0x69, 0x7a, 0x65,
0x12, 0x16, 0x0a, 0x06, 0x69, 0x6e, 0x63, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,
0x52, 0x06, 0x69, 0x6e, 0x63, 0x68, 0x65, 0x73, 0x22, 0x47, 0x0a, 0x03, 0x48, 0x61, 0x74, 0x12,
0x16, 0x0a, 0x06, 0x69, 0x6e, 0x63, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52,
0x06, 0x69, 0x6e, 0x63, 0x68, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x12, 0x0a,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x22, 0x3a, 0x0a, 0x04, 0x48, 0x61, 0x74, 0x73, 0x12, 0x32, 0x0a, 0x04, 0x68, 0x61, 0x74,
0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x77, 0x69, 0x72, 0x70, 0x2e,
0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x68, 0x61, 0x62, 0x65, 0x72, 0x64, 0x61, 0x73,
0x68, 0x65, 0x72, 0x2e, 0x48, 0x61, 0x74, 0x52, 0x04, 0x68, 0x61, 0x74, 0x73, 0x32, 0xab, 0x01,
0x0a, 0x0b, 0x48, 0x61, 0x62, 0x65, 0x72, 0x64, 0x61, 0x73, 0x68, 0x65, 0x72, 0x12, 0x4a, 0x0a,
0x07, 0x4d, 0x61, 0x6b, 0x65, 0x48, 0x61, 0x74, 0x12, 0x1f, 0x2e, 0x74, 0x77, 0x69, 0x72, 0x70,
0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x68, 0x61, 0x62, 0x65, 0x72, 0x64, 0x61,
0x73, 0x68, 0x65, 0x72, 0x2e, 0x53, 0x69, 0x7a, 0x65, 0x1a, 0x1e, 0x2e, 0x74, 0x77, 0x69, 0x72,
0x70, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x68, 0x61, 0x62, 0x65, 0x72, 0x64,
0x61, 0x73, 0x68, 0x65, 0x72, 0x2e, 0x48, 0x61, 0x74, 0x12, 0x50, 0x0a, 0x08, 0x4c, 0x69, 0x73,
0x74, 0x48, 0x61, 0x74, 0x73, 0x12, 0x23, 0x2e, 0x74, 0x77, 0x69, 0x72, 0x70, 0x2e, 0x65, 0x78,
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x68, 0x61, 0x62, 0x65, 0x72, 0x64, 0x61, 0x73, 0x68, 0x65,
0x72, 0x2e, 0x48, 0x61, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x1a, 0x1f, 0x2e, 0x74, 0x77, 0x69,
0x72, 0x70, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x68, 0x61, 0x62, 0x65, 0x72,
0x64, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2e, 0x48, 0x61, 0x74, 0x73, 0x42, 0x1f, 0x5a, 0x1d, 0x68,
0x61, 0x62, 0x65, 0x72, 0x64, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x74, 0x77, 0x69, 0x72, 0x70,
0x2f, 0x68, 0x61, 0x62, 0x65, 0x72, 0x64, 0x61, 0x73, 0x68, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
var (
file_haberdasher_service_proto_rawDescOnce sync.Once
file_haberdasher_service_proto_rawDescData = file_haberdasher_service_proto_rawDesc
)
func file_haberdasher_service_proto_rawDescGZIP() []byte {
file_haberdasher_service_proto_rawDescOnce.Do(func() {
file_haberdasher_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_haberdasher_service_proto_rawDescData)
})
return file_haberdasher_service_proto_rawDescData
}
var file_haberdasher_service_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_haberdasher_service_proto_goTypes = []interface{}{
(*HatQuery)(nil), // 0: twirp.example.haberdasher.HatQuery
(*Size)(nil), // 1: twirp.example.haberdasher.Size
(*Hat)(nil), // 2: twirp.example.haberdasher.Hat
(*Hats)(nil), // 3: twirp.example.haberdasher.Hats
}
var file_haberdasher_service_proto_depIdxs = []int32{
2, // 0: twirp.example.haberdasher.Hats.hats:type_name -> twirp.example.haberdasher.Hat
1, // 1: twirp.example.haberdasher.Haberdasher.MakeHat:input_type -> twirp.example.haberdasher.Size
0, // 2: twirp.example.haberdasher.Haberdasher.ListHats:input_type -> twirp.example.haberdasher.HatQuery
2, // 3: twirp.example.haberdasher.Haberdasher.MakeHat:output_type -> twirp.example.haberdasher.Hat
3, // 4: twirp.example.haberdasher.Haberdasher.ListHats:output_type -> twirp.example.haberdasher.Hats
3, // [3:5] is the sub-list for method output_type
1, // [1:3] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_haberdasher_service_proto_init() }
func file_haberdasher_service_proto_init() {
if File_haberdasher_service_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_haberdasher_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*HatQuery); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_haberdasher_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Size); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_haberdasher_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Hat); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_haberdasher_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Hats); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_haberdasher_service_proto_rawDesc,
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_haberdasher_service_proto_goTypes,
DependencyIndexes: file_haberdasher_service_proto_depIdxs,
MessageInfos: file_haberdasher_service_proto_msgTypes,
}.Build()
File_haberdasher_service_proto = out.File
file_haberdasher_service_proto_rawDesc = nil
file_haberdasher_service_proto_goTypes = nil
file_haberdasher_service_proto_depIdxs = nil
}

32
haberdasher/service.proto Normal file
View File

@ -0,0 +1,32 @@
syntax = "proto3";
package twirp.example.haberdasher;
option go_package = "haberdasher-twirp/haberdasher";
// Haberdasher service makes hats for clients.
service Haberdasher {
// MakeHat produces a hat of mysterious, randomly-selected color!
rpc MakeHat(Size) returns (Hat);
rpc ListHats(HatQuery) returns (Hats);
}
message HatQuery {
int32 limit = 1;
}
// Size of a Hat, in inches.
message Size {
int32 inches = 1; // must be > 0
}
// A Hat is a piece of headwear made by a Haberdasher.
message Hat {
int32 inches = 1;
string color = 2; // anything but "invisible"
string name = 3; // i.e. "bowler"
}
message Hats {
repeated Hat hats = 1;
}

1392
haberdasher/service.twirp.go Normal file

File diff suppressed because it is too large Load Diff

View 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()
}

8
tools.go Normal file
View File

@ -0,0 +1,8 @@
// +build tools
package tools
import (
_ "google.golang.org/protobuf/cmd/protoc-gen-go"
_ "github.com/twitchtv/twirp/protoc-gen-twirp"
)