From ed8749940aff17e409149487c940285afdffa7f6 Mon Sep 17 00:00:00 2001 From: Aiden Fine Date: Fri, 7 Aug 2026 13:14:13 -0400 Subject: [PATCH] grpc support --- go.mod | 9 +- go.sum | 12 +++ main.go | 42 ++++++++ pb/task.pb.go | 241 +++++++++++++++++++++++++++++++++++++++++++++ pb/task_grpc.pb.go | 121 +++++++++++++++++++++++ proto/task.proto | 23 +++++ 6 files changed, 446 insertions(+), 2 deletions(-) create mode 100644 pb/task.pb.go create mode 100644 pb/task_grpc.pb.go create mode 100644 proto/task.proto diff --git a/go.mod b/go.mod index 12d3b08..4e9d8ea 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module zeno -go 1.23.5 +go 1.25.0 require github.com/charmbracelet/lipgloss v1.1.0 @@ -16,5 +16,10 @@ require ( github.com/muesli/termenv v0.16.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect - golang.org/x/sys v0.30.0 // indirect + golang.org/x/net v0.55.0 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/text v0.37.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/grpc v1.83.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect ) diff --git a/go.sum b/go.sum index 63e27fc..3040382 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,18 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ= +google.golang.org/grpc v1.83.0/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= diff --git a/main.go b/main.go index f199d50..89607a7 100644 --- a/main.go +++ b/main.go @@ -1,19 +1,61 @@ package main import ( + "context" "fmt" "net" "strings" + "sync" + "zeno/pb" "zeno/src/aof" "zeno/src/handler" "zeno/src/resp" "zeno/src/writer" + + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" ) +// gRPC taskServer +type taskServer struct { + pb.UnimplementedTaskServiceServer + mu sync.Mutex + tasks []*pb.Task + counter int +} + +func (s *taskServer) SendTask(ctx context.Context, req *pb.SendTaskRequest) (*pb.SendTaskResponse, error) { + s.mu.Lock() + defer s.mu.Unlock() + + task := req.GetTask() + s.counter++ + task.Id = fmt.Sprintf("%d", s.counter) + s.tasks = append(s.tasks, task) + + fmt.Printf("gRPC: received task #%s - %s\n", task.Id, task.Title) + return &pb.SendTaskResponse{Success: true}, nil +} + // main in-mem db goes like this // Client -> TCP Request -> RESP deserialze -> commands hander -> RESP serialze -> Response func main() { + go func() { + lis, err := net.Listen("tcp", ":6380") + if err != nil { + fmt.Println("gRPC listen error:", err) + return + } + grpcServer := grpc.NewServer() + pb.RegisterTaskServiceServer(grpcServer, &taskServer{}) + reflection.Register(grpcServer) + fmt.Println("gRPC server running on port: 6380") + if err := grpcServer.Serve(lis); err != nil { + fmt.Println("gRPC serve error:", err) + } + }() + fmt.Println("Running on port: 6379") l, err := net.Listen("tcp", ":6379") if err != nil { diff --git a/pb/task.pb.go b/pb/task.pb.go new file mode 100644 index 0000000..b6757e3 --- /dev/null +++ b/pb/task.pb.go @@ -0,0 +1,241 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v7.35.1 +// source: task.proto + +package pb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +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 Task struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"` + Completed bool `protobuf:"varint,3,opt,name=completed,proto3" json:"completed,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Task) Reset() { + *x = Task{} + mi := &file_task_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Task) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Task) ProtoMessage() {} + +func (x *Task) ProtoReflect() protoreflect.Message { + mi := &file_task_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Task.ProtoReflect.Descriptor instead. +func (*Task) Descriptor() ([]byte, []int) { + return file_task_proto_rawDescGZIP(), []int{0} +} + +func (x *Task) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Task) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *Task) GetCompleted() bool { + if x != nil { + return x.Completed + } + return false +} + +type SendTaskRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Task *Task `protobuf:"bytes,1,opt,name=task,proto3" json:"task,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SendTaskRequest) Reset() { + *x = SendTaskRequest{} + mi := &file_task_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SendTaskRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendTaskRequest) ProtoMessage() {} + +func (x *SendTaskRequest) ProtoReflect() protoreflect.Message { + mi := &file_task_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendTaskRequest.ProtoReflect.Descriptor instead. +func (*SendTaskRequest) Descriptor() ([]byte, []int) { + return file_task_proto_rawDescGZIP(), []int{1} +} + +func (x *SendTaskRequest) GetTask() *Task { + if x != nil { + return x.Task + } + return nil +} + +type SendTaskResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SendTaskResponse) Reset() { + *x = SendTaskResponse{} + mi := &file_task_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SendTaskResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendTaskResponse) ProtoMessage() {} + +func (x *SendTaskResponse) ProtoReflect() protoreflect.Message { + mi := &file_task_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendTaskResponse.ProtoReflect.Descriptor instead. +func (*SendTaskResponse) Descriptor() ([]byte, []int) { + return file_task_proto_rawDescGZIP(), []int{2} +} + +func (x *SendTaskResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +var File_task_proto protoreflect.FileDescriptor + +const file_task_proto_rawDesc = "" + + "\n" + + "\n" + + "task.proto\x12\x04task\"J\n" + + "\x04Task\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x14\n" + + "\x05title\x18\x02 \x01(\tR\x05title\x12\x1c\n" + + "\tcompleted\x18\x03 \x01(\bR\tcompleted\"1\n" + + "\x0fSendTaskRequest\x12\x1e\n" + + "\x04task\x18\x01 \x01(\v2\n" + + ".task.TaskR\x04task\",\n" + + "\x10SendTaskResponse\x12\x18\n" + + "\asuccess\x18\x01 \x01(\bR\asuccess2H\n" + + "\vTaskService\x129\n" + + "\bSendTask\x12\x15.task.SendTaskRequest\x1a\x16.task.SendTaskResponseB\tZ\azeno/pbb\x06proto3" + +var ( + file_task_proto_rawDescOnce sync.Once + file_task_proto_rawDescData []byte +) + +func file_task_proto_rawDescGZIP() []byte { + file_task_proto_rawDescOnce.Do(func() { + file_task_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_task_proto_rawDesc), len(file_task_proto_rawDesc))) + }) + return file_task_proto_rawDescData +} + +var file_task_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_task_proto_goTypes = []any{ + (*Task)(nil), // 0: task.Task + (*SendTaskRequest)(nil), // 1: task.SendTaskRequest + (*SendTaskResponse)(nil), // 2: task.SendTaskResponse +} +var file_task_proto_depIdxs = []int32{ + 0, // 0: task.SendTaskRequest.task:type_name -> task.Task + 1, // 1: task.TaskService.SendTask:input_type -> task.SendTaskRequest + 2, // 2: task.TaskService.SendTask:output_type -> task.SendTaskResponse + 2, // [2:3] is the sub-list for method output_type + 1, // [1:2] 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_task_proto_init() } +func file_task_proto_init() { + if File_task_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_task_proto_rawDesc), len(file_task_proto_rawDesc)), + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_task_proto_goTypes, + DependencyIndexes: file_task_proto_depIdxs, + MessageInfos: file_task_proto_msgTypes, + }.Build() + File_task_proto = out.File + file_task_proto_goTypes = nil + file_task_proto_depIdxs = nil +} diff --git a/pb/task_grpc.pb.go b/pb/task_grpc.pb.go new file mode 100644 index 0000000..fbe2570 --- /dev/null +++ b/pb/task_grpc.pb.go @@ -0,0 +1,121 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.2 +// - protoc v7.35.1 +// source: task.proto + +package pb + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + TaskService_SendTask_FullMethodName = "/task.TaskService/SendTask" +) + +// TaskServiceClient is the client API for TaskService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type TaskServiceClient interface { + SendTask(ctx context.Context, in *SendTaskRequest, opts ...grpc.CallOption) (*SendTaskResponse, error) +} + +type taskServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewTaskServiceClient(cc grpc.ClientConnInterface) TaskServiceClient { + return &taskServiceClient{cc} +} + +func (c *taskServiceClient) SendTask(ctx context.Context, in *SendTaskRequest, opts ...grpc.CallOption) (*SendTaskResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SendTaskResponse) + err := c.cc.Invoke(ctx, TaskService_SendTask_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// TaskServiceServer is the server API for TaskService service. +// All implementations must embed UnimplementedTaskServiceServer +// for forward compatibility. +type TaskServiceServer interface { + SendTask(context.Context, *SendTaskRequest) (*SendTaskResponse, error) + mustEmbedUnimplementedTaskServiceServer() +} + +// UnimplementedTaskServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedTaskServiceServer struct{} + +func (UnimplementedTaskServiceServer) SendTask(context.Context, *SendTaskRequest) (*SendTaskResponse, error) { + return nil, status.Error(codes.Unimplemented, "method SendTask not implemented") +} +func (UnimplementedTaskServiceServer) mustEmbedUnimplementedTaskServiceServer() {} +func (UnimplementedTaskServiceServer) testEmbeddedByValue() {} + +// UnsafeTaskServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to TaskServiceServer will +// result in compilation errors. +type UnsafeTaskServiceServer interface { + mustEmbedUnimplementedTaskServiceServer() +} + +func RegisterTaskServiceServer(s grpc.ServiceRegistrar, srv TaskServiceServer) { + // If the following call panics, it indicates UnimplementedTaskServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&TaskService_ServiceDesc, srv) +} + +func _TaskService_SendTask_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SendTaskRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TaskServiceServer).SendTask(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TaskService_SendTask_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TaskServiceServer).SendTask(ctx, req.(*SendTaskRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// TaskService_ServiceDesc is the grpc.ServiceDesc for TaskService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var TaskService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "task.TaskService", + HandlerType: (*TaskServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SendTask", + Handler: _TaskService_SendTask_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "task.proto", +} diff --git a/proto/task.proto b/proto/task.proto new file mode 100644 index 0000000..395acdd --- /dev/null +++ b/proto/task.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +option go_package = "zeno/pb"; + +package task; + +message Task { + string id = 1; + string title = 2; + bool completed = 3; +} + +message SendTaskRequest { + Task task = 1; +} + +message SendTaskResponse { + bool success = 1; +} + +service TaskService { + rpc SendTask(SendTaskRequest) returns (SendTaskResponse); +}