Initial commit
This commit is contained in:
commit
88f803692a
35
cmd/client/main.go
Normal file
35
cmd/client/main.go
Normal 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
20
cmd/main.go
Normal 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
11
go.mod
Normal 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
14
go.sum
Normal 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
369
haberdasher/service.pb.go
Normal 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
32
haberdasher/service.proto
Normal 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
1392
haberdasher/service.twirp.go
Normal file
File diff suppressed because it is too large
Load Diff
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()
|
||||
}
|
Loading…
Reference in New Issue
Block a user