diff --git a/.luarc.json b/.luarc.json new file mode 100644 index 0000000000000000000000000000000000000000..071c52a685daed33cb7a0e9c2a592c36587a8a58 --- /dev/null +++ b/.luarc.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json", + "runtime": { + "version": "LuaJIT", + "path": [ + "runtime/?.lua", + "runtime/?/init.lua", + "examples/expected/?.lua", + "examples/expected/?/init.lua", + "test/?.lua", + "?.lua", + "?/init.lua" + ] + }, + "diagnostics": { + "globals": [ + "box" + ], + "disable": [ + "lowercase-global" + ] + }, + "workspace": { + "checkThirdParty": false, + "ignoreDir": [ + ".rocks", + ".beads", + "protoc-gen-tarantool", + "docker", + "bench/starwing", + "bench/c_accel/*.dylib", + "bench/c_accel/*.so" + ] + } +} diff --git a/runtime/pb/_types.lua b/runtime/pb/_types.lua new file mode 100644 index 0000000000000000000000000000000000000000..f346d6ca02ce6230f029e6d11b88f7d2a49caeb4 --- /dev/null +++ b/runtime/pb/_types.lua @@ -0,0 +1,218 @@ +---@meta +-- +-- Type declarations consumed by lua-language-server (sumneko/LuaLS) so +-- editors and LLM assistants get parameter / return types for the pb +-- runtime. The @meta directive prevents this file from being loaded at +-- run time; only the LSP reads it. +-- +-- The descriptor shape mirrors docs/codegen.md ยง "The descriptor table". +-- Keep them aligned -- if you add a field there, add it here too. + +---@class pb.FieldKey +---@field kind 'scalar' +---@field proto_type 'int32'|'uint32'|'sint32'|'fixed32'|'sfixed32'|'int64'|'uint64'|'sint64'|'fixed64'|'sfixed64'|'bool'|'string'|'bytes'|'float'|'double' + +---@class pb.FieldValue +---@field kind 'scalar'|'message'|'enum' +---@field proto_type? string +---@field message? pb.Descriptor +---@field enum? pb.EnumDescriptor + +---@class pb.Field +---@field name string +---@field id integer +---@field kind 'scalar'|'message'|'enum'|'map'|'group' +---@field proto_type? string # set when kind == 'scalar' +---@field message? pb.Descriptor # set when kind == 'message' or 'group' +---@field enum? pb.EnumDescriptor # set when kind == 'enum' +---@field key? pb.FieldKey # set when kind == 'map' +---@field value? pb.FieldValue # set when kind == 'map' +---@field repeated? boolean +---@field packed? boolean +---@field optional? boolean # proto3 explicit-optional +---@field required? boolean # proto2 only +---@field oneof? string # name of the containing oneof, if any +---@field oneof_siblings? string[] # filled by pb.finalize_message +---@field default_value? any # proto2 only +---@field key_dedup? boolean # filled by pb.finalize_message for cdata-keyed maps + +---@class pb.EnumDescriptor +---@field name string +---@field by_name table +---@field by_value table + +---@class pb.OneofDescriptor +---@field name string +---@field members string[] + +---@class pb.Descriptor +---@field name string +---@field fields pb.Field[] +---@field field_by_id? table # filled by pb.finalize_message +---@field field_by_name? table # filled by pb.finalize_message +---@field oneofs? table # raw form; flattened to oneofs_list +---@field oneofs_list? pb.OneofDescriptor[] # filled by pb.finalize_message +---@field reserved_names? table +---@field encode? fun(t: table): string # WKT override +---@field decode? fun(b: string): table # WKT override +---@field text? fun(t: table, opts?: pb.TextOpts): string +---@field json_encode? fun(t: table): any +---@field json_decode? fun(v: any): table +---@field c_plan? table # internal: C-accel dispatch +---@field extensions_by_id? table +---@field extensions_by_full_name? table +---@field extensions_list? pb.Field[] + +---@class pb.TextOpts +---@field single_line? boolean +---@field indent? string + +---@class pb.FileSet +---@field files table # filename -> generated-shaped module +---@field order string[] # filenames in declaration order +---@field lookup fun(full_name: string): pb.Descriptor? + +---@class pb.Module +---@field encode fun(desc: pb.Descriptor, t: table): string +---@field decode fun(desc: pb.Descriptor, b: string): table +---@field decode_lazy fun(desc: pb.Descriptor, b: string): pb.MessageView +---@field lazy pb.Lazy +---@field wire pb.Wire +---@field codec table # internal: shared with generated code +---@field wkt pb.Wkt +---@field NULL userdata # google.protobuf.Value null sentinel +---@field register fun(full_name: string, desc: pb.Descriptor) +---@field lookup fun(full_name: string): pb.Descriptor? +---@field any pb.Any +---@field grpc pb.Grpc +---@field c_runtime? table # set when PB_ENABLE_C=1 and load succeeds +---@field parse fun(source: string): table # AST -> runtime module +---@field from_pb fun(bytes: string): pb.FileSet +---@field parser table +---@field dynamic table +---@field fileset table +---@field json pb.Json +---@field text pb.Text +---@field WIRE_VARINT integer +---@field WIRE_I64 integer +---@field WIRE_LEN integer +---@field WIRE_I32 integer +---@field to_uint64 fun(v: any): ffi.cdata* +---@field to_int64 fun(v: any): ffi.cdata* +---@field field_names fun(tbl: table): table +---@field enum fun(name: string, values: table): pb.EnumDescriptor +---@field finalize_message fun(desc: pb.Descriptor): pb.Descriptor +---@field register_extension fun(extendee_desc: pb.Descriptor, ext: pb.Field) + +---@class pb.Any +---@field pack fun(t: table, type_url: string): table # returns google.protobuf.Any shape +---@field unpack fun(any_msg: table): table?, pb.Descriptor? + +---@class pb.Wkt +---@field NULL userdata +---@field register fun(full_name: string, desc: pb.Descriptor) +---@field lookup fun(full_name: string): pb.Descriptor? + +---@class pb.Json +---@field encode fun(desc: pb.Descriptor, t: table, opts?: table): string +---@field decode fun(desc: pb.Descriptor, s: string, opts?: table): table + +---@class pb.Text +---@field encode fun(desc: pb.Descriptor, t: table, opts?: pb.TextOpts): string + +---@class pb.Wire +---@field WIRE_VARINT integer +---@field WIRE_I64 integer +---@field WIRE_LEN integer +---@field WIRE_I32 integer +---@field encode_varint fun(n: integer|ffi.cdata*): string +---@field decode_varint fun(b: string, pos: integer): integer|ffi.cdata*, integer +---@field encode_tag fun(field_id: integer, wire_type: integer): string +---@field decode_tag fun(b: string, pos: integer): integer, integer, integer +---@field encode_string fun(s: string): string +---@field decode_string fun(b: string, pos: integer): string, integer +---@field encode_double fun(v: number): string +---@field decode_double fun(b: string, pos: integer): number, integer +---@field encode_float fun(v: number): string +---@field decode_float fun(b: string, pos: integer): number, integer +---@field to_uint64 fun(v: any): ffi.cdata* +---@field to_int64 fun(v: any): ffi.cdata* +---@field skip_field fun(b: string, pos: integer, wire_type: integer, field_id: integer): integer + +---@class pb.Lazy +---@field build fun(desc: pb.Descriptor, b: string): pb.MessageView +---@field MessageView pb.MessageView +---@field ArrayView pb.ArrayView +---@field MapView pb.MapView + +-- MessageView: zero-copy lazy decode of a single protobuf message. +-- `:get(name)` decodes the named field on demand; untouched fields +-- stay as raw bytes and are passed through verbatim on :encode(). +-- Field names passed to :get / :has / :set / :which should be sourced +-- from the strict M._fields table emitted by the codegen -- +-- typos error at the read site instead of returning silent nil. +-- See docs/api-modes.md. +-- +-- Method bodies live in runtime/pb/lazy.lua; the @class block there +-- merges with this one. Same shape for ArrayView and MapView. +---@class pb.MessageView +---@field get fun(self: pb.MessageView, name: string): any +---@field has fun(self: pb.MessageView, name: string): boolean +---@field which fun(self: pb.MessageView, oneof_name: string): string|nil +---@field iter fun(self: pb.MessageView): fun(): string|nil, any +---@field names fun(self: pb.MessageView): string[] +---@field set fun(self: pb.MessageView, name: string, value: any) +---@field is_dirty fun(self: pb.MessageView): boolean +---@field totable fun(self: pb.MessageView): table +---@field encode fun(self: pb.MessageView): string + +---@class pb.ArrayView +---@field len fun(self: pb.ArrayView): integer +---@field at fun(self: pb.ArrayView, i: integer): any +---@field iter fun(self: pb.ArrayView): fun(): integer|nil, any +---@field tolist fun(self: pb.ArrayView): any[] + +---@class pb.MapView +---@field get fun(self: pb.MapView, k: any): any +---@field has fun(self: pb.MapView, k: any): boolean +---@field keys fun(self: pb.MapView): any[] +---@field iter fun(self: pb.MapView): fun(): any, any +---@field totable fun(self: pb.MapView): table + +---@class pb.Grpc +---@field loopback fun(server: pb.GrpcServer): pb.GrpcTransport +---@field multiplex fun(servers: table): pb.GrpcTransport +---@field grpc_error fun(code: integer, message?: string, details?: table): pb.GrpcError + +---@class pb.GrpcServer +---@field service pb.ServiceDescriptor +---@field methods table + +---@class pb.ServiceDescriptor +---@field name string +---@field methods pb.MethodDescriptor[] + +---@class pb.MethodDescriptor +---@field name string +---@field path string +---@field input pb.Descriptor +---@field output pb.Descriptor +---@field client_streaming boolean +---@field server_streaming boolean + +-- Transport contract. Each method takes a path (string), a request +-- (bytes or a stream) and an optional context table, and returns a +-- response (bytes or a stream). Custom transports (HTTP/2, iproto, +-- etc.) implement this interface. +---@class pb.GrpcTransport +---@field unary fun(self: pb.GrpcTransport, path: string, req: string, ctx?: table): string +---@field client_streaming fun(self: pb.GrpcTransport, path: string, req_iter: fun():string|nil, ctx?: table): string +---@field server_streaming fun(self: pb.GrpcTransport, path: string, req: string, ctx?: table): fun():string|nil +---@field bidi_streaming fun(self: pb.GrpcTransport, path: string, req_iter: fun():string|nil, ctx?: table): fun():string|nil + +---@class pb.GrpcError +---@field code integer +---@field message string +---@field details? table + +return {} diff --git a/runtime/pb/init.lua b/runtime/pb/init.lua index 8bd5b70c40dc899f11ff7578b2306fcd62f91df9..cce0b9766df28f9adaf5da8ff54934ac3c69e8df 100644 --- a/runtime/pb/init.lua +++ b/runtime/pb/init.lua @@ -29,6 +29,7 @@ local ok, mod = pcall(require, 'pb.c_runtime') if ok then c_runtime = mod end end +---@type pb.Module return { -- High-level codec encode = codec.encode, diff --git a/runtime/pb/lazy.lua b/runtime/pb/lazy.lua index 3ae02cab15a96f2bb784c59cba83f949cc6151db..aa21858b45db8af25fc122df1183c9cc68b42eba 100644 --- a/runtime/pb/lazy.lua +++ b/runtime/pb/lazy.lua @@ -160,6 +160,7 @@ -- For packed, they're scanned out of the packed payload at construct time. -- :at(i) decodes from bytes[_starts[i]] using the field's known kind. -- --------------------------------------------------------------------------- +---@class pb.ArrayView local ArrayView = {} ArrayView.__index = ArrayView @@ -259,6 +260,7 @@ -- entry. Each entry contains the (key, value) sub-fields and is decoded -- on first :get/:has/:iter to build a key->value cache. -- --------------------------------------------------------------------------- +---@class pb.MapView local MapView = {} MapView.__index = MapView @@ -390,6 +392,7 @@ -- --------------------------------------------------------------------------- -- MessageView (top-level) -- --------------------------------------------------------------------------- +---@class pb.MessageView local MessageView = {} MessageView.__index = MessageView