From b75b87912eb710e71f73380e6b98c312f25a74d5 Mon Sep 17 00:00:00 2001 From: Eugene Blikh Date: Fri, 15 May 2026 18:20:45 +0300 Subject: [PATCH] codegen: EmmyLua type annotations for messages, enums, wrappers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generated Lua modules now carry lua-language-server type annotations: ---@alias integer per enum ---@class per message ---@field per field ---@param / ---@return per wrapper Mappings: bool -> boolean string / bytes -> string float / double -> number all int kinds -> integer (64-bit cdata typed as integer; LSP has no cdata model) enum / message -> (resolves to declared alias/class) repeated T -> T[] map -> table Presence markers (trailing `?` on field name): proto3 explicit optional oneof branches (only one is set at a time) Wrapper signatures cover _new / _encode / _decode / _decode_lazy plus _has_ / _clear_ on optional fields. _decode_lazy returns pb.MessageView, which is declared inline in runtime/pb/lazy.lua along with pb.ArrayView and pb.MapView so cross-file references resolve in any project that requires('pb.lazy'). Class identifiers use proto full names verbatim (e.g. `hello.Person`) so cross-file imports and WKT references both resolve to a single declared `---@class` block — no per-module renaming needed. Pure comment addition: 300/300 luatest + 19/19 jit-trace gate stay green. Generated examples regenerated and committed. --- PLAN.md | 12 +- .../internal/gen/emmylua.go | 209 +++++++++++++++++ cmd/protoc-gen-tarantool/internal/gen/gen.go | 17 +- .../internal/gen/inline.go | 7 +- .../full/conformance/conformance_pb.lua | 79 +++++++ examples/expected/full/hello/hello_pb.lua | 104 +++++++++ .../proto3/test_messages_proto3_pb.lua | 214 ++++++++++++++++++ .../runtime/conformance/conformance_pb.lua | 79 +++++++ examples/expected/runtime/hello/hello_pb.lua | 104 +++++++++ .../proto3/test_messages_proto3_pb.lua | 214 ++++++++++++++++++ runtime/pb/lazy.lua | 24 ++ 11 files changed, 1059 insertions(+), 4 deletions(-) create mode 100644 cmd/protoc-gen-tarantool/internal/gen/emmylua.go diff --git a/PLAN.md b/PLAN.md index 68f0534af11119e07b1cfc8d39f0d51d98e57697..a11e5b640208bfcb06914b9d6e79941387540a68 100644 --- a/PLAN.md +++ b/PLAN.md @@ -212,8 +212,18 @@ fiber and bridges client ↔ handler via `fiber.channel`. All four flavors ### M7 — Developer ergonomics -- [ ] Generated EmmyLua / lua-language-server type annotations so +- [x] Generated EmmyLua / lua-language-server type annotations so `t:Person_encode({name=...})` autocompletes in editors. + Codegen emits `---@class ` per message (with one + `---@field` per field), `---@alias integer` per + enum, and `---@param` / `---@return` on every `_new`, `_encode`, + `_decode`, `_decode_lazy`, `_has_*`, `_clear_*` wrapper. Class + identifiers use proto full names verbatim so cross-file + references resolve. Lazy view types (`pb.MessageView`, + `pb.ArrayView`, `pb.MapView`) are declared inline in + `runtime/pb/lazy.lua` so the LSP sees them. Pure comment + addition — no runtime impact, 300/300 luatest + 19/19 + jit-trace gate stay green. - [ ] `pb.from_pb(file_descriptor_set)` — runtime descriptor parser, lets apps load schemas at runtime without protoc-time codegen. - [ ] JSON encoding per the [proto3 JSON spec][proto3json] (canonical and diff --git a/cmd/protoc-gen-tarantool/internal/gen/emmylua.go b/cmd/protoc-gen-tarantool/internal/gen/emmylua.go new file mode 100644 index 0000000000000000000000000000000000000000..a964766b0111a354464a5c2df98b456b01dd4f88 --- /dev/null +++ b/cmd/protoc-gen-tarantool/internal/gen/emmylua.go @@ -0,0 +1,209 @@ +// EmmyLua / lua-language-server annotation emission. +// +// Annotations are pure comments — they have no runtime effect — but +// they teach the Lua language server about generated message shapes, +// enum aliases, and wrapper function signatures. Users of generated +// code get autocomplete, type-checking, and rename refactoring for +// free in any editor that consumes EmmyLua (VS Code + sumneko, Neovim +// LSP, JetBrains EmmyLua plugin). +// +// Output (per file): +// 1. ---@alias integer (one per enum) +// 2. ---@class (one per message) +// ---@field +// ... +// 3. ---@param / ---@return on each emitted M._* function. +// +// All three blocks are emitted by gen.go at well-defined points so +// they live alongside the code they describe. +// +// Type mapping (proto -> EmmyLua): +// - bool -> boolean +// - string / bytes -> string +// - float / double -> number +// - all int kinds -> integer (64-bit are uint64_t/int64_t cdata +// at runtime; LSP has no cdata model, +// so they're typed as integer with +// an inline note) +// - enum -> (alias declared elsewhere) +// - message -> (class declared elsewhere) +// - repeated -> T[] +// - map -> table +// +// Field presence: +// - proto3 explicit `optional` -> trailing `?` on the field name +// - oneof branch -> trailing `?` (only one is set at a time) +// - everything else -> no marker (proto3 defaults imply +// a typed zero value if absent) + +package gen + +import ( + "fmt" + "strings" + + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/reflect/protoreflect" +) + +// emmyTypeName returns the qualified EmmyLua class/alias identifier for a +// message or enum. We use the proto full name verbatim (with dots), which +// lua-language-server accepts as a class identifier. This keeps cross-file +// references straightforward — every file referring to "hello.Person" +// resolves to the same `---@class hello.Person` block, regardless of which +// generated `.lua` declared it. +func emmyTypeName(td protoreflect.Descriptor) string { + return string(td.FullName()) +} + +// emmyScalarType maps a proto3 scalar kind to its EmmyLua surface type. +func emmyScalarType(k protoreflect.Kind) string { + switch k { + case protoreflect.BoolKind: + return "boolean" + case protoreflect.StringKind, protoreflect.BytesKind: + return "string" + case protoreflect.FloatKind, protoreflect.DoubleKind: + return "number" + default: + // All int kinds. 64-bit are int64_t/uint64_t cdata at runtime, + // but EmmyLua has no model for that; users either compare via + // pb.to_int64/pb.to_uint64 or convert with tonumber for small + // values. Typing as integer matches user mental model. + return "integer" + } +} + +// emmyFieldType returns the surface type for one field, accounting for +// kind (scalar/enum/message/map) and the repeated modifier. +func emmyFieldType(f *protogen.Field) string { + if f.Desc.IsMap() { + kf := f.Message.Fields[0] + vf := f.Message.Fields[1] + return fmt.Sprintf("table<%s, %s>", + emmyFieldTypeSingular(kf), emmyFieldTypeSingular(vf)) + } + t := emmyFieldTypeSingular(f) + if f.Desc.IsList() { + return t + "[]" + } + return t +} + +func emmyFieldTypeSingular(f *protogen.Field) string { + switch { + case f.Message != nil: + return emmyTypeName(f.Message.Desc) + case f.Enum != nil: + return emmyTypeName(f.Enum.Desc) + default: + return emmyScalarType(f.Desc.Kind()) + } +} + +// emmyFieldOptional reports whether the field should carry a trailing `?`. +// Two cases: proto3 explicit optional, and oneof branches (only one is +// set at a time, so every branch is presence-tracked). +func emmyFieldOptional(f *protogen.Field) bool { + if f.Desc.HasOptionalKeyword() { + return true + } + if f.Oneof != nil && !f.Desc.HasOptionalKeyword() { + return true + } + return false +} + +// emitEmmyEnumAlias emits `---@alias integer` for each enum. +// Could be tightened to ` | | ...` but that locks the +// schema in the annotation; users typically write `M.Status.OK` (an +// integer literal) so `integer` is the honest type. +func emitEmmyEnumAlias(w *writer, e *protogen.Enum) { + w.line("---@alias %s integer", emmyTypeName(e.Desc)) +} + +// emitEmmyMessageClass emits the `---@class` block for one message, +// listing every field with its surface type and optional marker. +func emitEmmyMessageClass(w *writer, m *protogen.Message) { + w.line("---@class %s", emmyTypeName(m.Desc)) + for _, f := range m.Fields { + name := string(f.Desc.Name()) + if emmyFieldOptional(f) { + name = name + "?" + } + w.line("---@field %s %s", name, emmyFieldType(f)) + } +} + +// emitEmmyWrappersHeader prefaces the M._new / _encode / _decode / +// _decode_lazy / has_/clear_ stubs with their EmmyLua annotations. Each +// wrapper gets its own `---@param` / `---@return` lines printed +// immediately before the corresponding `function M._*(...)` line. +// +// emitMessageWrappers (runtime mode) and emitInlineMessage (full mode) +// each call into this for the typed prelude; the function bodies stay +// where they were. +func emitEmmyWrapperAnnotations(w *writer, name string, fullName string, kind emmyWrapperKind) { + t := fullName + switch kind { + case wrapperNew: + w.line("---@param t? %s", t) + w.line("---@return %s", t) + case wrapperEncode: + w.line("---@param t %s", t) + w.line("---@return string") + case wrapperDecode: + w.line("---@param b string") + w.line("---@return %s", t) + case wrapperDecodeLazy: + w.line("---@param b string") + w.line("---@return pb.MessageView") + case wrapperHas: + w.line("---@param t %s", t) + w.line("---@return boolean") + case wrapperClear: + w.line("---@param t %s", t) + } +} + +type emmyWrapperKind int + +const ( + wrapperNew emmyWrapperKind = iota + wrapperEncode + wrapperDecode + wrapperDecodeLazy + wrapperHas + wrapperClear +) + +// emitEmmyTypesBlock emits the file's whole types section: every enum +// alias, every message class. Called once per file by GenerateFile, +// after the descriptor pre-declarations and before the wrappers. +func emitEmmyTypesBlock(w *writer, enums []*protogen.Enum, msgs []*protogen.Message) { + if len(enums) == 0 && len(msgs) == 0 { + return + } + w.line("-- EmmyLua / lua-language-server type annotations.") + w.line("-- These are comments — no runtime effect. They give editors") + w.line("-- autocomplete and type-checking for the generated wrappers.") + for _, e := range enums { + emitEmmyEnumAlias(w, e) + } + if len(enums) > 0 { + w.line("") + } + for i, m := range msgs { + emitEmmyMessageClass(w, m) + if i < len(msgs)-1 { + w.line("") + } + } + w.line("") +} + +// emmyMessageFullName is a convenience wrapper used by the wrapper +// emitters to resolve the right `---@class` identifier for a message. +func emmyMessageFullName(m *protogen.Message) string { + return strings.TrimSpace(string(m.Desc.FullName())) +} diff --git a/cmd/protoc-gen-tarantool/internal/gen/gen.go b/cmd/protoc-gen-tarantool/internal/gen/gen.go index f10ee2e663c449c61e79a2f2504c1e70313f093b..300c56f76e837ea2cd9dc26022caa0875b2a13f4 100644 --- a/cmd/protoc-gen-tarantool/internal/gen/gen.go +++ b/cmd/protoc-gen-tarantool/internal/gen/gen.go @@ -92,6 +92,11 @@ func GenerateFile(plug *protogen.Plugin, file *protogen.File, cfg Config) error emitMessageFields(w, file, m, imports, cfg.Prefix) } + // 3a) EmmyLua type annotations (---@class per message, ---@alias per + // enum). Emitted between the descriptors and the wrappers so the + // wrapper annotations a few lines down can reference these class names. + emitEmmyTypesBlock(w, allEnums, allMsgs) + // 4) Wrappers: _new / _encode / _decode. for _, m := range allMsgs { switch cfg.Mode { @@ -375,23 +380,31 @@ func typeRef(file *protogen.File, td protoreflect.Descriptor, selfPath string, i // has_ / clear_ for each explicit-optional field. func emitMessageWrappers(w *writer, file *protogen.File, m *protogen.Message) { name := luaTypeName(m.Desc.FullName(), file.Desc.Package()) + full := emmyMessageFullName(m) + + emitEmmyWrapperAnnotations(w, name, full, wrapperNew) w.line("function M.%s_new(t) return t or {} end", name) + emitEmmyWrapperAnnotations(w, name, full, wrapperEncode) w.line("function M.%s_encode(t) return pb.encode(M.%s_descriptor, t) end", name, name) + emitEmmyWrapperAnnotations(w, name, full, wrapperDecode) w.line("function M.%s_decode(b) return pb.decode(M.%s_descriptor, b) end", name, name) + emitEmmyWrapperAnnotations(w, name, full, wrapperDecodeLazy) w.line("function M.%s_decode_lazy(b) return pb.decode_lazy(M.%s_descriptor, b) end", name, name) - emitOptionalAccessors(w, name, m) + emitOptionalAccessors(w, name, m, full) w.line("") } // emitOptionalAccessors writes M._has_(t) and _clear_(t) // for every field marked with proto3 explicit `optional`. -func emitOptionalAccessors(w *writer, name string, m *protogen.Message) { +func emitOptionalAccessors(w *writer, name string, m *protogen.Message, fullName string) { for _, f := range m.Fields { if !f.Desc.HasOptionalKeyword() { continue } fname := string(f.Desc.Name()) + emitEmmyWrapperAnnotations(w, name, fullName, wrapperHas) w.line("function M.%s_has_%s(t) return t.%s ~= nil end", name, fname, fname) + emitEmmyWrapperAnnotations(w, name, fullName, wrapperClear) w.line("function M.%s_clear_%s(t) t.%s = nil end", name, fname, fname) } } diff --git a/cmd/protoc-gen-tarantool/internal/gen/inline.go b/cmd/protoc-gen-tarantool/internal/gen/inline.go index c0580f26891acd4c919b4d055e5a65b88ca3a555..ecb2495d94e6a7a6f33b2a2a9e9360248303e1ec 100644 --- a/cmd/protoc-gen-tarantool/internal/gen/inline.go +++ b/cmd/protoc-gen-tarantool/internal/gen/inline.go @@ -13,19 +13,23 @@ import ( // each scalar field's encode/decode call resolves to one wire. call. func emitInlineMessage(w *writer, file *protogen.File, m *protogen.Message, imports map[string]string, prefix string) { name := luaTypeName(m.Desc.FullName(), file.Desc.Package()) + full := emmyMessageFullName(m) selfPath := luaPackagePath(file.Desc, prefix) + emitEmmyWrapperAnnotations(w, name, full, wrapperNew) w.line("function M.%s_new(t) return t or {} end", name) w.line("") emitInlineEncode(w, name, m, file, selfPath, imports, prefix) emitInlineDecode(w, name, m, file, selfPath, imports, prefix) + emitEmmyWrapperAnnotations(w, name, full, wrapperDecodeLazy) w.line("function M.%s_decode_lazy(b) return pb.decode_lazy(M.%s_descriptor, b) end", name, name) - emitOptionalAccessors(w, name, m) + emitOptionalAccessors(w, name, m, full) w.line("") } func emitInlineEncode(w *writer, name string, m *protogen.Message, file *protogen.File, selfPath string, imports map[string]string, prefix string) { + emitEmmyWrapperAnnotations(w, name, emmyMessageFullName(m), wrapperEncode) w.line("function M.%s_encode(t)", name) w.line(" if type(t) ~= 'table' then") w.line(" error(\"expected table for %s, got \" .. type(t), 0)", m.Desc.FullName()) @@ -196,6 +200,7 @@ func emitInlineEncodeRepeated(w *writer, f *protogen.Field, tag, fname string, f } func emitInlineDecode(w *writer, name string, m *protogen.Message, file *protogen.File, selfPath string, imports map[string]string, prefix string) { + emitEmmyWrapperAnnotations(w, name, emmyMessageFullName(m), wrapperDecode) w.line("function M.%s_decode(buf)", name) w.line(" if type(buf) ~= 'string' then") w.line(" error(\"expected string for %s decode, got \" .. type(buf), 0)", m.Desc.FullName()) diff --git a/examples/expected/full/conformance/conformance_pb.lua b/examples/expected/full/conformance/conformance_pb.lua index fd1a6d8f55696fc02f64d057f0828f9b533c084e..1795495fc06f761a21bfc638c74072c94d17f5d6 100644 --- a/examples/expected/full/conformance/conformance_pb.lua +++ b/examples/expected/full/conformance/conformance_pb.lua @@ -90,8 +90,51 @@ M.JspbEncodingConfig_descriptor.fields = { } pb.finalize_message(M.JspbEncodingConfig_descriptor) +-- EmmyLua / lua-language-server type annotations. +-- These are comments — no runtime effect. They give editors +-- autocomplete and type-checking for the generated wrappers. +---@alias conformance.WireFormat integer +---@alias conformance.TestCategory integer + +---@class conformance.TestStatus +---@field name string +---@field failure_message string +---@field matched_name string + +---@class conformance.FailureSet +---@field test conformance.TestStatus[] + +---@class conformance.ConformanceRequest +---@field protobuf_payload? string +---@field json_payload? string +---@field jspb_payload? string +---@field text_payload? string +---@field requested_output_format conformance.WireFormat +---@field message_type string +---@field test_category conformance.TestCategory +---@field jspb_encoding_options conformance.JspbEncodingConfig +---@field print_unknown_fields boolean + +---@class conformance.ConformanceResponse +---@field parse_error? string +---@field serialize_error? string +---@field timeout_error? string +---@field runtime_error? string +---@field protobuf_payload? string +---@field json_payload? string +---@field skipped? string +---@field jspb_payload? string +---@field text_payload? string + +---@class conformance.JspbEncodingConfig +---@field use_jspb_array_any_format boolean + +---@param t? conformance.TestStatus +---@return conformance.TestStatus function M.TestStatus_new(t) return t or {} end +---@param t conformance.TestStatus +---@return string function M.TestStatus_encode(t) if type(t) ~= 'table' then error("expected table for conformance.TestStatus, got " .. type(t), 0) @@ -121,6 +164,8 @@ function M.TestStatus_encode(t) return table.concat(out) end +---@param b string +---@return conformance.TestStatus function M.TestStatus_decode(buf) if type(buf) ~= 'string' then error("expected string for conformance.TestStatus decode, got " .. type(buf), 0) @@ -154,10 +199,16 @@ function M.TestStatus_decode(buf) return result end +---@param b string +---@return pb.MessageView function M.TestStatus_decode_lazy(b) return pb.decode_lazy(M.TestStatus_descriptor, b) end +---@param t? conformance.FailureSet +---@return conformance.FailureSet function M.FailureSet_new(t) return t or {} end +---@param t conformance.FailureSet +---@return string function M.FailureSet_encode(t) if type(t) ~= 'table' then error("expected table for conformance.FailureSet, got " .. type(t), 0) @@ -178,6 +229,8 @@ function M.FailureSet_encode(t) return table.concat(out) end +---@param b string +---@return conformance.FailureSet function M.FailureSet_decode(buf) if type(buf) ~= 'string' then error("expected string for conformance.FailureSet decode, got " .. type(buf), 0) @@ -205,10 +258,16 @@ function M.FailureSet_decode(buf) return result end +---@param b string +---@return pb.MessageView function M.FailureSet_decode_lazy(b) return pb.decode_lazy(M.FailureSet_descriptor, b) end +---@param t? conformance.ConformanceRequest +---@return conformance.ConformanceRequest function M.ConformanceRequest_new(t) return t or {} end +---@param t conformance.ConformanceRequest +---@return string function M.ConformanceRequest_encode(t) if type(t) ~= 'table' then error("expected table for conformance.ConformanceRequest, got " .. type(t), 0) @@ -293,6 +352,8 @@ function M.ConformanceRequest_encode(t) return table.concat(out) end +---@param b string +---@return conformance.ConformanceRequest function M.ConformanceRequest_decode(buf) if type(buf) ~= 'string' then error("expected string for conformance.ConformanceRequest decode, got " .. type(buf), 0) @@ -368,10 +429,16 @@ function M.ConformanceRequest_decode(buf) return result end +---@param b string +---@return pb.MessageView function M.ConformanceRequest_decode_lazy(b) return pb.decode_lazy(M.ConformanceRequest_descriptor, b) end +---@param t? conformance.ConformanceResponse +---@return conformance.ConformanceResponse function M.ConformanceResponse_new(t) return t or {} end +---@param t conformance.ConformanceResponse +---@return string function M.ConformanceResponse_encode(t) if type(t) ~= 'table' then error("expected table for conformance.ConformanceResponse, got " .. type(t), 0) @@ -447,6 +514,8 @@ function M.ConformanceResponse_encode(t) return table.concat(out) end +---@param b string +---@return conformance.ConformanceResponse function M.ConformanceResponse_decode(buf) if type(buf) ~= 'string' then error("expected string for conformance.ConformanceResponse decode, got " .. type(buf), 0) @@ -576,10 +645,16 @@ function M.ConformanceResponse_decode(buf) return result end +---@param b string +---@return pb.MessageView function M.ConformanceResponse_decode_lazy(b) return pb.decode_lazy(M.ConformanceResponse_descriptor, b) end +---@param t? conformance.JspbEncodingConfig +---@return conformance.JspbEncodingConfig function M.JspbEncodingConfig_new(t) return t or {} end +---@param t conformance.JspbEncodingConfig +---@return string function M.JspbEncodingConfig_encode(t) if type(t) ~= 'table' then error("expected table for conformance.JspbEncodingConfig, got " .. type(t), 0) @@ -597,6 +672,8 @@ function M.JspbEncodingConfig_encode(t) return table.concat(out) end +---@param b string +---@return conformance.JspbEncodingConfig function M.JspbEncodingConfig_decode(buf) if type(buf) ~= 'string' then error("expected string for conformance.JspbEncodingConfig decode, got " .. type(buf), 0) @@ -622,6 +699,8 @@ function M.JspbEncodingConfig_decode(buf) return result end +---@param b string +---@return pb.MessageView function M.JspbEncodingConfig_decode_lazy(b) return pb.decode_lazy(M.JspbEncodingConfig_descriptor, b) end return M diff --git a/examples/expected/full/hello/hello_pb.lua b/examples/expected/full/hello/hello_pb.lua index 77ecbd9b7a2885189fe60e5454ecebd8147287dd..f30c8ff3db89809cefd45e94ac8aa92781bf015f 100644 --- a/examples/expected/full/hello/hello_pb.lua +++ b/examples/expected/full/hello/hello_pb.lua @@ -93,8 +93,65 @@ M.Person_descriptor.fields = { } pb.finalize_message(M.Person_descriptor) +-- EmmyLua / lua-language-server type annotations. +-- These are comments — no runtime effect. They give editors +-- autocomplete and type-checking for the generated wrappers. +---@alias hello.Status integer + +---@class hello.Result +---@field id integer +---@field text? string +---@field code? integer +---@field details? hello.Address + +---@class hello.HelloRequest +---@field name string + +---@class hello.HelloReply +---@field greeting string + +---@class hello.Event +---@field title string +---@field created_at google.protobuf.Timestamp +---@field duration google.protobuf.Duration +---@field ack google.protobuf.Empty +---@field retry_count google.protobuf.Int32Value +---@field note google.protobuf.StringValue +---@field is_admin google.protobuf.BoolValue +---@field payload google.protobuf.Struct +---@field attribute google.protobuf.Value +---@field tags google.protobuf.ListValue +---@field extension google.protobuf.Any +---@field update_mask google.protobuf.FieldMask + +---@class hello.Address +---@field street string +---@field city string +---@field zip integer +---@field apartment? string + +---@class hello.Person +---@field name string +---@field age integer +---@field emails string[] +---@field status hello.Status +---@field address hello.Address +---@field friends hello.Person[] +---@field lucky_numbers integer[] +---@field avatar string +---@field user_id integer +---@field balance integer +---@field weight_kg number +---@field ages_by_nickname table +---@field nickname_by_age table +---@field addresses_by_label table + +---@param t? hello.Result +---@return hello.Result function M.Result_new(t) return t or {} end +---@param t hello.Result +---@return string function M.Result_encode(t) if type(t) ~= 'table' then error("expected table for hello.Result, got " .. type(t), 0) @@ -134,6 +191,8 @@ function M.Result_encode(t) return table.concat(out) end +---@param b string +---@return hello.Result function M.Result_decode(buf) if type(buf) ~= 'string' then error("expected string for hello.Result decode, got " .. type(buf), 0) @@ -177,10 +236,16 @@ function M.Result_decode(buf) return result end +---@param b string +---@return pb.MessageView function M.Result_decode_lazy(b) return pb.decode_lazy(M.Result_descriptor, b) end +---@param t? hello.HelloRequest +---@return hello.HelloRequest function M.HelloRequest_new(t) return t or {} end +---@param t hello.HelloRequest +---@return string function M.HelloRequest_encode(t) if type(t) ~= 'table' then error("expected table for hello.HelloRequest, got " .. type(t), 0) @@ -198,6 +263,8 @@ function M.HelloRequest_encode(t) return table.concat(out) end +---@param b string +---@return hello.HelloRequest function M.HelloRequest_decode(buf) if type(buf) ~= 'string' then error("expected string for hello.HelloRequest decode, got " .. type(buf), 0) @@ -223,10 +290,16 @@ function M.HelloRequest_decode(buf) return result end +---@param b string +---@return pb.MessageView function M.HelloRequest_decode_lazy(b) return pb.decode_lazy(M.HelloRequest_descriptor, b) end +---@param t? hello.HelloReply +---@return hello.HelloReply function M.HelloReply_new(t) return t or {} end +---@param t hello.HelloReply +---@return string function M.HelloReply_encode(t) if type(t) ~= 'table' then error("expected table for hello.HelloReply, got " .. type(t), 0) @@ -244,6 +317,8 @@ function M.HelloReply_encode(t) return table.concat(out) end +---@param b string +---@return hello.HelloReply function M.HelloReply_decode(buf) if type(buf) ~= 'string' then error("expected string for hello.HelloReply decode, got " .. type(buf), 0) @@ -269,10 +344,16 @@ function M.HelloReply_decode(buf) return result end +---@param b string +---@return pb.MessageView function M.HelloReply_decode_lazy(b) return pb.decode_lazy(M.HelloReply_descriptor, b) end +---@param t? hello.Event +---@return hello.Event function M.Event_new(t) return t or {} end +---@param t hello.Event +---@return string function M.Event_encode(t) if type(t) ~= 'table' then error("expected table for hello.Event, got " .. type(t), 0) @@ -356,6 +437,8 @@ function M.Event_encode(t) return table.concat(out) end +---@param b string +---@return hello.Event function M.Event_decode(buf) if type(buf) ~= 'string' then error("expected string for hello.Event decode, got " .. type(buf), 0) @@ -491,10 +574,16 @@ function M.Event_decode(buf) return result end +---@param b string +---@return pb.MessageView function M.Event_decode_lazy(b) return pb.decode_lazy(M.Event_descriptor, b) end +---@param t? hello.Address +---@return hello.Address function M.Address_new(t) return t or {} end +---@param t hello.Address +---@return string function M.Address_encode(t) if type(t) ~= 'table' then error("expected table for hello.Address, got " .. type(t), 0) @@ -530,6 +619,8 @@ function M.Address_encode(t) return table.concat(out) end +---@param b string +---@return hello.Address function M.Address_decode(buf) if type(buf) ~= 'string' then error("expected string for hello.Address decode, got " .. type(buf), 0) @@ -567,12 +658,21 @@ function M.Address_decode(buf) return result end +---@param b string +---@return pb.MessageView function M.Address_decode_lazy(b) return pb.decode_lazy(M.Address_descriptor, b) end +---@param t hello.Address +---@return boolean function M.Address_has_apartment(t) return t.apartment ~= nil end +---@param t hello.Address function M.Address_clear_apartment(t) t.apartment = nil end +---@param t? hello.Person +---@return hello.Person function M.Person_new(t) return t or {} end +---@param t hello.Person +---@return string function M.Person_encode(t) if type(t) ~= 'table' then error("expected table for hello.Person, got " .. type(t), 0) @@ -721,6 +821,8 @@ function M.Person_encode(t) return table.concat(out) end +---@param b string +---@return hello.Person function M.Person_decode(buf) if type(buf) ~= 'string' then error("expected string for hello.Person decode, got " .. type(buf), 0) @@ -868,6 +970,8 @@ function M.Person_decode(buf) return result end +---@param b string +---@return pb.MessageView function M.Person_decode_lazy(b) return pb.decode_lazy(M.Person_descriptor, b) end -- Service: hello.Greeter diff --git a/examples/expected/full/protobuf_test_messages/proto3/test_messages_proto3_pb.lua b/examples/expected/full/protobuf_test_messages/proto3/test_messages_proto3_pb.lua index 8810a725701f61588ecde64a002add430686e3a2..46d6fe4a2def30bc9e0793d61356d8c11724ed8c 100644 --- a/examples/expected/full/protobuf_test_messages/proto3/test_messages_proto3_pb.lua +++ b/examples/expected/full/protobuf_test_messages/proto3/test_messages_proto3_pb.lua @@ -234,8 +234,186 @@ M.EnumOnlyProto3_descriptor.fields = { } pb.finalize_message(M.EnumOnlyProto3_descriptor) +-- EmmyLua / lua-language-server type annotations. +-- These are comments — no runtime effect. They give editors +-- autocomplete and type-checking for the generated wrappers. +---@alias protobuf_test_messages.proto3.ForeignEnum integer +---@alias protobuf_test_messages.proto3.TestAllTypesProto3.NestedEnum integer +---@alias protobuf_test_messages.proto3.TestAllTypesProto3.AliasedEnum integer +---@alias protobuf_test_messages.proto3.EnumOnlyProto3.Bool integer + +---@class protobuf_test_messages.proto3.TestAllTypesProto3 +---@field optional_int32 integer +---@field optional_int64 integer +---@field optional_uint32 integer +---@field optional_uint64 integer +---@field optional_sint32 integer +---@field optional_sint64 integer +---@field optional_fixed32 integer +---@field optional_fixed64 integer +---@field optional_sfixed32 integer +---@field optional_sfixed64 integer +---@field optional_float number +---@field optional_double number +---@field optional_bool boolean +---@field optional_string string +---@field optional_bytes string +---@field optional_nested_message protobuf_test_messages.proto3.TestAllTypesProto3.NestedMessage +---@field optional_foreign_message protobuf_test_messages.proto3.ForeignMessage +---@field optional_nested_enum protobuf_test_messages.proto3.TestAllTypesProto3.NestedEnum +---@field optional_foreign_enum protobuf_test_messages.proto3.ForeignEnum +---@field optional_aliased_enum protobuf_test_messages.proto3.TestAllTypesProto3.AliasedEnum +---@field optional_string_piece string +---@field optional_cord string +---@field recursive_message protobuf_test_messages.proto3.TestAllTypesProto3 +---@field repeated_int32 integer[] +---@field repeated_int64 integer[] +---@field repeated_uint32 integer[] +---@field repeated_uint64 integer[] +---@field repeated_sint32 integer[] +---@field repeated_sint64 integer[] +---@field repeated_fixed32 integer[] +---@field repeated_fixed64 integer[] +---@field repeated_sfixed32 integer[] +---@field repeated_sfixed64 integer[] +---@field repeated_float number[] +---@field repeated_double number[] +---@field repeated_bool boolean[] +---@field repeated_string string[] +---@field repeated_bytes string[] +---@field repeated_nested_message protobuf_test_messages.proto3.TestAllTypesProto3.NestedMessage[] +---@field repeated_foreign_message protobuf_test_messages.proto3.ForeignMessage[] +---@field repeated_nested_enum protobuf_test_messages.proto3.TestAllTypesProto3.NestedEnum[] +---@field repeated_foreign_enum protobuf_test_messages.proto3.ForeignEnum[] +---@field repeated_string_piece string[] +---@field repeated_cord string[] +---@field packed_int32 integer[] +---@field packed_int64 integer[] +---@field packed_uint32 integer[] +---@field packed_uint64 integer[] +---@field packed_sint32 integer[] +---@field packed_sint64 integer[] +---@field packed_fixed32 integer[] +---@field packed_fixed64 integer[] +---@field packed_sfixed32 integer[] +---@field packed_sfixed64 integer[] +---@field packed_float number[] +---@field packed_double number[] +---@field packed_bool boolean[] +---@field packed_nested_enum protobuf_test_messages.proto3.TestAllTypesProto3.NestedEnum[] +---@field unpacked_int32 integer[] +---@field unpacked_int64 integer[] +---@field unpacked_uint32 integer[] +---@field unpacked_uint64 integer[] +---@field unpacked_sint32 integer[] +---@field unpacked_sint64 integer[] +---@field unpacked_fixed32 integer[] +---@field unpacked_fixed64 integer[] +---@field unpacked_sfixed32 integer[] +---@field unpacked_sfixed64 integer[] +---@field unpacked_float number[] +---@field unpacked_double number[] +---@field unpacked_bool boolean[] +---@field unpacked_nested_enum protobuf_test_messages.proto3.TestAllTypesProto3.NestedEnum[] +---@field map_int32_int32 table +---@field map_int64_int64 table +---@field map_uint32_uint32 table +---@field map_uint64_uint64 table +---@field map_sint32_sint32 table +---@field map_sint64_sint64 table +---@field map_fixed32_fixed32 table +---@field map_fixed64_fixed64 table +---@field map_sfixed32_sfixed32 table +---@field map_sfixed64_sfixed64 table +---@field map_int32_float table +---@field map_int32_double table +---@field map_bool_bool table +---@field map_string_string table +---@field map_string_bytes table +---@field map_string_nested_message table +---@field map_string_foreign_message table +---@field map_string_nested_enum table +---@field map_string_foreign_enum table +---@field oneof_uint32? integer +---@field oneof_nested_message? protobuf_test_messages.proto3.TestAllTypesProto3.NestedMessage +---@field oneof_string? string +---@field oneof_bytes? string +---@field oneof_bool? boolean +---@field oneof_uint64? integer +---@field oneof_float? number +---@field oneof_double? number +---@field oneof_enum? protobuf_test_messages.proto3.TestAllTypesProto3.NestedEnum +---@field oneof_null_value? google.protobuf.NullValue +---@field optional_bool_wrapper google.protobuf.BoolValue +---@field optional_int32_wrapper google.protobuf.Int32Value +---@field optional_int64_wrapper google.protobuf.Int64Value +---@field optional_uint32_wrapper google.protobuf.UInt32Value +---@field optional_uint64_wrapper google.protobuf.UInt64Value +---@field optional_float_wrapper google.protobuf.FloatValue +---@field optional_double_wrapper google.protobuf.DoubleValue +---@field optional_string_wrapper google.protobuf.StringValue +---@field optional_bytes_wrapper google.protobuf.BytesValue +---@field repeated_bool_wrapper google.protobuf.BoolValue[] +---@field repeated_int32_wrapper google.protobuf.Int32Value[] +---@field repeated_int64_wrapper google.protobuf.Int64Value[] +---@field repeated_uint32_wrapper google.protobuf.UInt32Value[] +---@field repeated_uint64_wrapper google.protobuf.UInt64Value[] +---@field repeated_float_wrapper google.protobuf.FloatValue[] +---@field repeated_double_wrapper google.protobuf.DoubleValue[] +---@field repeated_string_wrapper google.protobuf.StringValue[] +---@field repeated_bytes_wrapper google.protobuf.BytesValue[] +---@field optional_duration google.protobuf.Duration +---@field optional_timestamp google.protobuf.Timestamp +---@field optional_field_mask google.protobuf.FieldMask +---@field optional_struct google.protobuf.Struct +---@field optional_any google.protobuf.Any +---@field optional_value google.protobuf.Value +---@field optional_null_value google.protobuf.NullValue +---@field optional_empty google.protobuf.Empty +---@field repeated_duration google.protobuf.Duration[] +---@field repeated_timestamp google.protobuf.Timestamp[] +---@field repeated_fieldmask google.protobuf.FieldMask[] +---@field repeated_struct google.protobuf.Struct[] +---@field repeated_any google.protobuf.Any[] +---@field repeated_value google.protobuf.Value[] +---@field repeated_list_value google.protobuf.ListValue[] +---@field repeated_empty google.protobuf.Empty[] +---@field fieldname1 integer +---@field field_name2 integer +---@field _field_name3 integer +---@field field__name4_ integer +---@field field0name5 integer +---@field field_0_name6 integer +---@field fieldName7 integer +---@field FieldName8 integer +---@field field_Name9 integer +---@field Field_Name10 integer +---@field FIELD_NAME11 integer +---@field FIELD_name12 integer +---@field __field_name13 integer +---@field __Field_name14 integer +---@field field__name15 integer +---@field field__Name16 integer +---@field field_name17__ integer +---@field Field_name18__ integer + +---@class protobuf_test_messages.proto3.TestAllTypesProto3.NestedMessage +---@field a integer +---@field corecursive protobuf_test_messages.proto3.TestAllTypesProto3 + +---@class protobuf_test_messages.proto3.ForeignMessage +---@field c integer + +---@class protobuf_test_messages.proto3.NullHypothesisProto3 + +---@class protobuf_test_messages.proto3.EnumOnlyProto3 + +---@param t? protobuf_test_messages.proto3.TestAllTypesProto3 +---@return protobuf_test_messages.proto3.TestAllTypesProto3 function M.TestAllTypesProto3_new(t) return t or {} end +---@param t protobuf_test_messages.proto3.TestAllTypesProto3 +---@return string function M.TestAllTypesProto3_encode(t) if type(t) ~= 'table' then error("expected table for protobuf_test_messages.proto3.TestAllTypesProto3, got " .. type(t), 0) @@ -1704,6 +1882,8 @@ function M.TestAllTypesProto3_encode(t) return table.concat(out) end +---@param b string +---@return protobuf_test_messages.proto3.TestAllTypesProto3 function M.TestAllTypesProto3_decode(buf) if type(buf) ~= 'string' then error("expected string for protobuf_test_messages.proto3.TestAllTypesProto3 decode, got " .. type(buf), 0) @@ -3439,10 +3619,16 @@ function M.TestAllTypesProto3_decode(buf) return result end +---@param b string +---@return pb.MessageView function M.TestAllTypesProto3_decode_lazy(b) return pb.decode_lazy(M.TestAllTypesProto3_descriptor, b) end +---@param t? protobuf_test_messages.proto3.TestAllTypesProto3.NestedMessage +---@return protobuf_test_messages.proto3.TestAllTypesProto3.NestedMessage function M.TestAllTypesProto3_NestedMessage_new(t) return t or {} end +---@param t protobuf_test_messages.proto3.TestAllTypesProto3.NestedMessage +---@return string function M.TestAllTypesProto3_NestedMessage_encode(t) if type(t) ~= 'table' then error("expected table for protobuf_test_messages.proto3.TestAllTypesProto3.NestedMessage, got " .. type(t), 0) @@ -3466,6 +3652,8 @@ function M.TestAllTypesProto3_NestedMessage_encode(t) return table.concat(out) end +---@param b string +---@return protobuf_test_messages.proto3.TestAllTypesProto3.NestedMessage function M.TestAllTypesProto3_NestedMessage_decode(buf) if type(buf) ~= 'string' then error("expected string for protobuf_test_messages.proto3.TestAllTypesProto3.NestedMessage decode, got " .. type(buf), 0) @@ -3501,10 +3689,16 @@ function M.TestAllTypesProto3_NestedMessage_decode(buf) return result end +---@param b string +---@return pb.MessageView function M.TestAllTypesProto3_NestedMessage_decode_lazy(b) return pb.decode_lazy(M.TestAllTypesProto3_NestedMessage_descriptor, b) end +---@param t? protobuf_test_messages.proto3.ForeignMessage +---@return protobuf_test_messages.proto3.ForeignMessage function M.ForeignMessage_new(t) return t or {} end +---@param t protobuf_test_messages.proto3.ForeignMessage +---@return string function M.ForeignMessage_encode(t) if type(t) ~= 'table' then error("expected table for protobuf_test_messages.proto3.ForeignMessage, got " .. type(t), 0) @@ -3522,6 +3716,8 @@ function M.ForeignMessage_encode(t) return table.concat(out) end +---@param b string +---@return protobuf_test_messages.proto3.ForeignMessage function M.ForeignMessage_decode(buf) if type(buf) ~= 'string' then error("expected string for protobuf_test_messages.proto3.ForeignMessage decode, got " .. type(buf), 0) @@ -3547,10 +3743,16 @@ function M.ForeignMessage_decode(buf) return result end +---@param b string +---@return pb.MessageView function M.ForeignMessage_decode_lazy(b) return pb.decode_lazy(M.ForeignMessage_descriptor, b) end +---@param t? protobuf_test_messages.proto3.NullHypothesisProto3 +---@return protobuf_test_messages.proto3.NullHypothesisProto3 function M.NullHypothesisProto3_new(t) return t or {} end +---@param t protobuf_test_messages.proto3.NullHypothesisProto3 +---@return string function M.NullHypothesisProto3_encode(t) if type(t) ~= 'table' then error("expected table for protobuf_test_messages.proto3.NullHypothesisProto3, got " .. type(t), 0) @@ -3562,6 +3764,8 @@ function M.NullHypothesisProto3_encode(t) return table.concat(out) end +---@param b string +---@return protobuf_test_messages.proto3.NullHypothesisProto3 function M.NullHypothesisProto3_decode(buf) if type(buf) ~= 'string' then error("expected string for protobuf_test_messages.proto3.NullHypothesisProto3 decode, got " .. type(buf), 0) @@ -3584,10 +3788,16 @@ function M.NullHypothesisProto3_decode(buf) return result end +---@param b string +---@return pb.MessageView function M.NullHypothesisProto3_decode_lazy(b) return pb.decode_lazy(M.NullHypothesisProto3_descriptor, b) end +---@param t? protobuf_test_messages.proto3.EnumOnlyProto3 +---@return protobuf_test_messages.proto3.EnumOnlyProto3 function M.EnumOnlyProto3_new(t) return t or {} end +---@param t protobuf_test_messages.proto3.EnumOnlyProto3 +---@return string function M.EnumOnlyProto3_encode(t) if type(t) ~= 'table' then error("expected table for protobuf_test_messages.proto3.EnumOnlyProto3, got " .. type(t), 0) @@ -3599,6 +3809,8 @@ function M.EnumOnlyProto3_encode(t) return table.concat(out) end +---@param b string +---@return protobuf_test_messages.proto3.EnumOnlyProto3 function M.EnumOnlyProto3_decode(buf) if type(buf) ~= 'string' then error("expected string for protobuf_test_messages.proto3.EnumOnlyProto3 decode, got " .. type(buf), 0) @@ -3621,6 +3833,8 @@ function M.EnumOnlyProto3_decode(buf) return result end +---@param b string +---@return pb.MessageView function M.EnumOnlyProto3_decode_lazy(b) return pb.decode_lazy(M.EnumOnlyProto3_descriptor, b) end return M diff --git a/examples/expected/runtime/conformance/conformance_pb.lua b/examples/expected/runtime/conformance/conformance_pb.lua index 2fe09c2772b2f548c8ec995ee96137aef577caf8..6b729f8069dd4c178a8baeba362d3f2ed2fe55fe 100644 --- a/examples/expected/runtime/conformance/conformance_pb.lua +++ b/examples/expected/runtime/conformance/conformance_pb.lua @@ -90,29 +90,108 @@ M.JspbEncodingConfig_descriptor.fields = { } pb.finalize_message(M.JspbEncodingConfig_descriptor) +-- EmmyLua / lua-language-server type annotations. +-- These are comments — no runtime effect. They give editors +-- autocomplete and type-checking for the generated wrappers. +---@alias conformance.WireFormat integer +---@alias conformance.TestCategory integer + +---@class conformance.TestStatus +---@field name string +---@field failure_message string +---@field matched_name string + +---@class conformance.FailureSet +---@field test conformance.TestStatus[] + +---@class conformance.ConformanceRequest +---@field protobuf_payload? string +---@field json_payload? string +---@field jspb_payload? string +---@field text_payload? string +---@field requested_output_format conformance.WireFormat +---@field message_type string +---@field test_category conformance.TestCategory +---@field jspb_encoding_options conformance.JspbEncodingConfig +---@field print_unknown_fields boolean + +---@class conformance.ConformanceResponse +---@field parse_error? string +---@field serialize_error? string +---@field timeout_error? string +---@field runtime_error? string +---@field protobuf_payload? string +---@field json_payload? string +---@field skipped? string +---@field jspb_payload? string +---@field text_payload? string + +---@class conformance.JspbEncodingConfig +---@field use_jspb_array_any_format boolean + +---@param t? conformance.TestStatus +---@return conformance.TestStatus function M.TestStatus_new(t) return t or {} end +---@param t conformance.TestStatus +---@return string function M.TestStatus_encode(t) return pb.encode(M.TestStatus_descriptor, t) end +---@param b string +---@return conformance.TestStatus function M.TestStatus_decode(b) return pb.decode(M.TestStatus_descriptor, b) end +---@param b string +---@return pb.MessageView function M.TestStatus_decode_lazy(b) return pb.decode_lazy(M.TestStatus_descriptor, b) end +---@param t? conformance.FailureSet +---@return conformance.FailureSet function M.FailureSet_new(t) return t or {} end +---@param t conformance.FailureSet +---@return string function M.FailureSet_encode(t) return pb.encode(M.FailureSet_descriptor, t) end +---@param b string +---@return conformance.FailureSet function M.FailureSet_decode(b) return pb.decode(M.FailureSet_descriptor, b) end +---@param b string +---@return pb.MessageView function M.FailureSet_decode_lazy(b) return pb.decode_lazy(M.FailureSet_descriptor, b) end +---@param t? conformance.ConformanceRequest +---@return conformance.ConformanceRequest function M.ConformanceRequest_new(t) return t or {} end +---@param t conformance.ConformanceRequest +---@return string function M.ConformanceRequest_encode(t) return pb.encode(M.ConformanceRequest_descriptor, t) end +---@param b string +---@return conformance.ConformanceRequest function M.ConformanceRequest_decode(b) return pb.decode(M.ConformanceRequest_descriptor, b) end +---@param b string +---@return pb.MessageView function M.ConformanceRequest_decode_lazy(b) return pb.decode_lazy(M.ConformanceRequest_descriptor, b) end +---@param t? conformance.ConformanceResponse +---@return conformance.ConformanceResponse function M.ConformanceResponse_new(t) return t or {} end +---@param t conformance.ConformanceResponse +---@return string function M.ConformanceResponse_encode(t) return pb.encode(M.ConformanceResponse_descriptor, t) end +---@param b string +---@return conformance.ConformanceResponse function M.ConformanceResponse_decode(b) return pb.decode(M.ConformanceResponse_descriptor, b) end +---@param b string +---@return pb.MessageView function M.ConformanceResponse_decode_lazy(b) return pb.decode_lazy(M.ConformanceResponse_descriptor, b) end +---@param t? conformance.JspbEncodingConfig +---@return conformance.JspbEncodingConfig function M.JspbEncodingConfig_new(t) return t or {} end +---@param t conformance.JspbEncodingConfig +---@return string function M.JspbEncodingConfig_encode(t) return pb.encode(M.JspbEncodingConfig_descriptor, t) end +---@param b string +---@return conformance.JspbEncodingConfig function M.JspbEncodingConfig_decode(b) return pb.decode(M.JspbEncodingConfig_descriptor, b) end +---@param b string +---@return pb.MessageView function M.JspbEncodingConfig_decode_lazy(b) return pb.decode_lazy(M.JspbEncodingConfig_descriptor, b) end return M diff --git a/examples/expected/runtime/hello/hello_pb.lua b/examples/expected/runtime/hello/hello_pb.lua index ef767935aa8198223b0f920a9898f3cab882e255..f29a22c59c1442036e57f501157eeb84529e8e33 100644 --- a/examples/expected/runtime/hello/hello_pb.lua +++ b/examples/expected/runtime/hello/hello_pb.lua @@ -93,36 +93,140 @@ M.Person_descriptor.fields = { } pb.finalize_message(M.Person_descriptor) +-- EmmyLua / lua-language-server type annotations. +-- These are comments — no runtime effect. They give editors +-- autocomplete and type-checking for the generated wrappers. +---@alias hello.Status integer + +---@class hello.Result +---@field id integer +---@field text? string +---@field code? integer +---@field details? hello.Address + +---@class hello.HelloRequest +---@field name string + +---@class hello.HelloReply +---@field greeting string + +---@class hello.Event +---@field title string +---@field created_at google.protobuf.Timestamp +---@field duration google.protobuf.Duration +---@field ack google.protobuf.Empty +---@field retry_count google.protobuf.Int32Value +---@field note google.protobuf.StringValue +---@field is_admin google.protobuf.BoolValue +---@field payload google.protobuf.Struct +---@field attribute google.protobuf.Value +---@field tags google.protobuf.ListValue +---@field extension google.protobuf.Any +---@field update_mask google.protobuf.FieldMask + +---@class hello.Address +---@field street string +---@field city string +---@field zip integer +---@field apartment? string + +---@class hello.Person +---@field name string +---@field age integer +---@field emails string[] +---@field status hello.Status +---@field address hello.Address +---@field friends hello.Person[] +---@field lucky_numbers integer[] +---@field avatar string +---@field user_id integer +---@field balance integer +---@field weight_kg number +---@field ages_by_nickname table +---@field nickname_by_age table +---@field addresses_by_label table + +---@param t? hello.Result +---@return hello.Result function M.Result_new(t) return t or {} end +---@param t hello.Result +---@return string function M.Result_encode(t) return pb.encode(M.Result_descriptor, t) end +---@param b string +---@return hello.Result function M.Result_decode(b) return pb.decode(M.Result_descriptor, b) end +---@param b string +---@return pb.MessageView function M.Result_decode_lazy(b) return pb.decode_lazy(M.Result_descriptor, b) end +---@param t? hello.HelloRequest +---@return hello.HelloRequest function M.HelloRequest_new(t) return t or {} end +---@param t hello.HelloRequest +---@return string function M.HelloRequest_encode(t) return pb.encode(M.HelloRequest_descriptor, t) end +---@param b string +---@return hello.HelloRequest function M.HelloRequest_decode(b) return pb.decode(M.HelloRequest_descriptor, b) end +---@param b string +---@return pb.MessageView function M.HelloRequest_decode_lazy(b) return pb.decode_lazy(M.HelloRequest_descriptor, b) end +---@param t? hello.HelloReply +---@return hello.HelloReply function M.HelloReply_new(t) return t or {} end +---@param t hello.HelloReply +---@return string function M.HelloReply_encode(t) return pb.encode(M.HelloReply_descriptor, t) end +---@param b string +---@return hello.HelloReply function M.HelloReply_decode(b) return pb.decode(M.HelloReply_descriptor, b) end +---@param b string +---@return pb.MessageView function M.HelloReply_decode_lazy(b) return pb.decode_lazy(M.HelloReply_descriptor, b) end +---@param t? hello.Event +---@return hello.Event function M.Event_new(t) return t or {} end +---@param t hello.Event +---@return string function M.Event_encode(t) return pb.encode(M.Event_descriptor, t) end +---@param b string +---@return hello.Event function M.Event_decode(b) return pb.decode(M.Event_descriptor, b) end +---@param b string +---@return pb.MessageView function M.Event_decode_lazy(b) return pb.decode_lazy(M.Event_descriptor, b) end +---@param t? hello.Address +---@return hello.Address function M.Address_new(t) return t or {} end +---@param t hello.Address +---@return string function M.Address_encode(t) return pb.encode(M.Address_descriptor, t) end +---@param b string +---@return hello.Address function M.Address_decode(b) return pb.decode(M.Address_descriptor, b) end +---@param b string +---@return pb.MessageView function M.Address_decode_lazy(b) return pb.decode_lazy(M.Address_descriptor, b) end +---@param t hello.Address +---@return boolean function M.Address_has_apartment(t) return t.apartment ~= nil end +---@param t hello.Address function M.Address_clear_apartment(t) t.apartment = nil end +---@param t? hello.Person +---@return hello.Person function M.Person_new(t) return t or {} end +---@param t hello.Person +---@return string function M.Person_encode(t) return pb.encode(M.Person_descriptor, t) end +---@param b string +---@return hello.Person function M.Person_decode(b) return pb.decode(M.Person_descriptor, b) end +---@param b string +---@return pb.MessageView function M.Person_decode_lazy(b) return pb.decode_lazy(M.Person_descriptor, b) end -- Service: hello.Greeter diff --git a/examples/expected/runtime/protobuf_test_messages/proto3/test_messages_proto3_pb.lua b/examples/expected/runtime/protobuf_test_messages/proto3/test_messages_proto3_pb.lua index 126019cb2d0927c0fcce591a5c20f62740f32798..36310c688c95cf9e601c7f588ade2298ca203767 100644 --- a/examples/expected/runtime/protobuf_test_messages/proto3/test_messages_proto3_pb.lua +++ b/examples/expected/runtime/protobuf_test_messages/proto3/test_messages_proto3_pb.lua @@ -234,29 +234,243 @@ M.EnumOnlyProto3_descriptor.fields = { } pb.finalize_message(M.EnumOnlyProto3_descriptor) +-- EmmyLua / lua-language-server type annotations. +-- These are comments — no runtime effect. They give editors +-- autocomplete and type-checking for the generated wrappers. +---@alias protobuf_test_messages.proto3.ForeignEnum integer +---@alias protobuf_test_messages.proto3.TestAllTypesProto3.NestedEnum integer +---@alias protobuf_test_messages.proto3.TestAllTypesProto3.AliasedEnum integer +---@alias protobuf_test_messages.proto3.EnumOnlyProto3.Bool integer + +---@class protobuf_test_messages.proto3.TestAllTypesProto3 +---@field optional_int32 integer +---@field optional_int64 integer +---@field optional_uint32 integer +---@field optional_uint64 integer +---@field optional_sint32 integer +---@field optional_sint64 integer +---@field optional_fixed32 integer +---@field optional_fixed64 integer +---@field optional_sfixed32 integer +---@field optional_sfixed64 integer +---@field optional_float number +---@field optional_double number +---@field optional_bool boolean +---@field optional_string string +---@field optional_bytes string +---@field optional_nested_message protobuf_test_messages.proto3.TestAllTypesProto3.NestedMessage +---@field optional_foreign_message protobuf_test_messages.proto3.ForeignMessage +---@field optional_nested_enum protobuf_test_messages.proto3.TestAllTypesProto3.NestedEnum +---@field optional_foreign_enum protobuf_test_messages.proto3.ForeignEnum +---@field optional_aliased_enum protobuf_test_messages.proto3.TestAllTypesProto3.AliasedEnum +---@field optional_string_piece string +---@field optional_cord string +---@field recursive_message protobuf_test_messages.proto3.TestAllTypesProto3 +---@field repeated_int32 integer[] +---@field repeated_int64 integer[] +---@field repeated_uint32 integer[] +---@field repeated_uint64 integer[] +---@field repeated_sint32 integer[] +---@field repeated_sint64 integer[] +---@field repeated_fixed32 integer[] +---@field repeated_fixed64 integer[] +---@field repeated_sfixed32 integer[] +---@field repeated_sfixed64 integer[] +---@field repeated_float number[] +---@field repeated_double number[] +---@field repeated_bool boolean[] +---@field repeated_string string[] +---@field repeated_bytes string[] +---@field repeated_nested_message protobuf_test_messages.proto3.TestAllTypesProto3.NestedMessage[] +---@field repeated_foreign_message protobuf_test_messages.proto3.ForeignMessage[] +---@field repeated_nested_enum protobuf_test_messages.proto3.TestAllTypesProto3.NestedEnum[] +---@field repeated_foreign_enum protobuf_test_messages.proto3.ForeignEnum[] +---@field repeated_string_piece string[] +---@field repeated_cord string[] +---@field packed_int32 integer[] +---@field packed_int64 integer[] +---@field packed_uint32 integer[] +---@field packed_uint64 integer[] +---@field packed_sint32 integer[] +---@field packed_sint64 integer[] +---@field packed_fixed32 integer[] +---@field packed_fixed64 integer[] +---@field packed_sfixed32 integer[] +---@field packed_sfixed64 integer[] +---@field packed_float number[] +---@field packed_double number[] +---@field packed_bool boolean[] +---@field packed_nested_enum protobuf_test_messages.proto3.TestAllTypesProto3.NestedEnum[] +---@field unpacked_int32 integer[] +---@field unpacked_int64 integer[] +---@field unpacked_uint32 integer[] +---@field unpacked_uint64 integer[] +---@field unpacked_sint32 integer[] +---@field unpacked_sint64 integer[] +---@field unpacked_fixed32 integer[] +---@field unpacked_fixed64 integer[] +---@field unpacked_sfixed32 integer[] +---@field unpacked_sfixed64 integer[] +---@field unpacked_float number[] +---@field unpacked_double number[] +---@field unpacked_bool boolean[] +---@field unpacked_nested_enum protobuf_test_messages.proto3.TestAllTypesProto3.NestedEnum[] +---@field map_int32_int32 table +---@field map_int64_int64 table +---@field map_uint32_uint32 table +---@field map_uint64_uint64 table +---@field map_sint32_sint32 table +---@field map_sint64_sint64 table +---@field map_fixed32_fixed32 table +---@field map_fixed64_fixed64 table +---@field map_sfixed32_sfixed32 table +---@field map_sfixed64_sfixed64 table +---@field map_int32_float table +---@field map_int32_double table +---@field map_bool_bool table +---@field map_string_string table +---@field map_string_bytes table +---@field map_string_nested_message table +---@field map_string_foreign_message table +---@field map_string_nested_enum table +---@field map_string_foreign_enum table +---@field oneof_uint32? integer +---@field oneof_nested_message? protobuf_test_messages.proto3.TestAllTypesProto3.NestedMessage +---@field oneof_string? string +---@field oneof_bytes? string +---@field oneof_bool? boolean +---@field oneof_uint64? integer +---@field oneof_float? number +---@field oneof_double? number +---@field oneof_enum? protobuf_test_messages.proto3.TestAllTypesProto3.NestedEnum +---@field oneof_null_value? google.protobuf.NullValue +---@field optional_bool_wrapper google.protobuf.BoolValue +---@field optional_int32_wrapper google.protobuf.Int32Value +---@field optional_int64_wrapper google.protobuf.Int64Value +---@field optional_uint32_wrapper google.protobuf.UInt32Value +---@field optional_uint64_wrapper google.protobuf.UInt64Value +---@field optional_float_wrapper google.protobuf.FloatValue +---@field optional_double_wrapper google.protobuf.DoubleValue +---@field optional_string_wrapper google.protobuf.StringValue +---@field optional_bytes_wrapper google.protobuf.BytesValue +---@field repeated_bool_wrapper google.protobuf.BoolValue[] +---@field repeated_int32_wrapper google.protobuf.Int32Value[] +---@field repeated_int64_wrapper google.protobuf.Int64Value[] +---@field repeated_uint32_wrapper google.protobuf.UInt32Value[] +---@field repeated_uint64_wrapper google.protobuf.UInt64Value[] +---@field repeated_float_wrapper google.protobuf.FloatValue[] +---@field repeated_double_wrapper google.protobuf.DoubleValue[] +---@field repeated_string_wrapper google.protobuf.StringValue[] +---@field repeated_bytes_wrapper google.protobuf.BytesValue[] +---@field optional_duration google.protobuf.Duration +---@field optional_timestamp google.protobuf.Timestamp +---@field optional_field_mask google.protobuf.FieldMask +---@field optional_struct google.protobuf.Struct +---@field optional_any google.protobuf.Any +---@field optional_value google.protobuf.Value +---@field optional_null_value google.protobuf.NullValue +---@field optional_empty google.protobuf.Empty +---@field repeated_duration google.protobuf.Duration[] +---@field repeated_timestamp google.protobuf.Timestamp[] +---@field repeated_fieldmask google.protobuf.FieldMask[] +---@field repeated_struct google.protobuf.Struct[] +---@field repeated_any google.protobuf.Any[] +---@field repeated_value google.protobuf.Value[] +---@field repeated_list_value google.protobuf.ListValue[] +---@field repeated_empty google.protobuf.Empty[] +---@field fieldname1 integer +---@field field_name2 integer +---@field _field_name3 integer +---@field field__name4_ integer +---@field field0name5 integer +---@field field_0_name6 integer +---@field fieldName7 integer +---@field FieldName8 integer +---@field field_Name9 integer +---@field Field_Name10 integer +---@field FIELD_NAME11 integer +---@field FIELD_name12 integer +---@field __field_name13 integer +---@field __Field_name14 integer +---@field field__name15 integer +---@field field__Name16 integer +---@field field_name17__ integer +---@field Field_name18__ integer + +---@class protobuf_test_messages.proto3.TestAllTypesProto3.NestedMessage +---@field a integer +---@field corecursive protobuf_test_messages.proto3.TestAllTypesProto3 + +---@class protobuf_test_messages.proto3.ForeignMessage +---@field c integer + +---@class protobuf_test_messages.proto3.NullHypothesisProto3 + +---@class protobuf_test_messages.proto3.EnumOnlyProto3 + +---@param t? protobuf_test_messages.proto3.TestAllTypesProto3 +---@return protobuf_test_messages.proto3.TestAllTypesProto3 function M.TestAllTypesProto3_new(t) return t or {} end +---@param t protobuf_test_messages.proto3.TestAllTypesProto3 +---@return string function M.TestAllTypesProto3_encode(t) return pb.encode(M.TestAllTypesProto3_descriptor, t) end +---@param b string +---@return protobuf_test_messages.proto3.TestAllTypesProto3 function M.TestAllTypesProto3_decode(b) return pb.decode(M.TestAllTypesProto3_descriptor, b) end +---@param b string +---@return pb.MessageView function M.TestAllTypesProto3_decode_lazy(b) return pb.decode_lazy(M.TestAllTypesProto3_descriptor, b) end +---@param t? protobuf_test_messages.proto3.TestAllTypesProto3.NestedMessage +---@return protobuf_test_messages.proto3.TestAllTypesProto3.NestedMessage function M.TestAllTypesProto3_NestedMessage_new(t) return t or {} end +---@param t protobuf_test_messages.proto3.TestAllTypesProto3.NestedMessage +---@return string function M.TestAllTypesProto3_NestedMessage_encode(t) return pb.encode(M.TestAllTypesProto3_NestedMessage_descriptor, t) end +---@param b string +---@return protobuf_test_messages.proto3.TestAllTypesProto3.NestedMessage function M.TestAllTypesProto3_NestedMessage_decode(b) return pb.decode(M.TestAllTypesProto3_NestedMessage_descriptor, b) end +---@param b string +---@return pb.MessageView function M.TestAllTypesProto3_NestedMessage_decode_lazy(b) return pb.decode_lazy(M.TestAllTypesProto3_NestedMessage_descriptor, b) end +---@param t? protobuf_test_messages.proto3.ForeignMessage +---@return protobuf_test_messages.proto3.ForeignMessage function M.ForeignMessage_new(t) return t or {} end +---@param t protobuf_test_messages.proto3.ForeignMessage +---@return string function M.ForeignMessage_encode(t) return pb.encode(M.ForeignMessage_descriptor, t) end +---@param b string +---@return protobuf_test_messages.proto3.ForeignMessage function M.ForeignMessage_decode(b) return pb.decode(M.ForeignMessage_descriptor, b) end +---@param b string +---@return pb.MessageView function M.ForeignMessage_decode_lazy(b) return pb.decode_lazy(M.ForeignMessage_descriptor, b) end +---@param t? protobuf_test_messages.proto3.NullHypothesisProto3 +---@return protobuf_test_messages.proto3.NullHypothesisProto3 function M.NullHypothesisProto3_new(t) return t or {} end +---@param t protobuf_test_messages.proto3.NullHypothesisProto3 +---@return string function M.NullHypothesisProto3_encode(t) return pb.encode(M.NullHypothesisProto3_descriptor, t) end +---@param b string +---@return protobuf_test_messages.proto3.NullHypothesisProto3 function M.NullHypothesisProto3_decode(b) return pb.decode(M.NullHypothesisProto3_descriptor, b) end +---@param b string +---@return pb.MessageView function M.NullHypothesisProto3_decode_lazy(b) return pb.decode_lazy(M.NullHypothesisProto3_descriptor, b) end +---@param t? protobuf_test_messages.proto3.EnumOnlyProto3 +---@return protobuf_test_messages.proto3.EnumOnlyProto3 function M.EnumOnlyProto3_new(t) return t or {} end +---@param t protobuf_test_messages.proto3.EnumOnlyProto3 +---@return string function M.EnumOnlyProto3_encode(t) return pb.encode(M.EnumOnlyProto3_descriptor, t) end +---@param b string +---@return protobuf_test_messages.proto3.EnumOnlyProto3 function M.EnumOnlyProto3_decode(b) return pb.decode(M.EnumOnlyProto3_descriptor, b) end +---@param b string +---@return pb.MessageView function M.EnumOnlyProto3_decode_lazy(b) return pb.decode_lazy(M.EnumOnlyProto3_descriptor, b) end return M diff --git a/runtime/pb/lazy.lua b/runtime/pb/lazy.lua index a102181777b5992ae71fa21c23315b7817b628a2..b9b211b09356ddd534dd03f365a5095fd22da693 100644 --- a/runtime/pb/lazy.lua +++ b/runtime/pb/lazy.lua @@ -19,6 +19,30 @@ -- wrapped in an EagerView with the same getter surface, so callers -- don't have to special-case Timestamp/Duration/Struct/etc. +---@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? +---@field iter fun(self: pb.MessageView): fun(): string?, any +---@field names fun(self: pb.MessageView): fun(): 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?, 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 + local wire = require('pb.wire') local codec = require('pb.codec')