--!strict
--!nolint LocalUnused
--!nolint ImportUnused
--# selene: allow(empty_if, if_same_then_else, manual_table_clone, unused_variable)
-- This file was @autogenerated by protoc-gen-luau
local proto = require("../../proto")
local typeRegistry = require("../../proto/typeRegistry")

type _Messages = {
	Any: _AnyMessage,
}
local messages: _Messages = {} :: _Messages

type _AnyImpl = {
	__index: _AnyImpl,
	new: (fields: _AnyPartialFields?) -> Any,
	encode: (self: Any) -> buffer,
	decode: (input: buffer) -> Any,
	jsonEncode: (self: Any) -> { [string]: any },
	jsonDecode: (input: { [string]: any }) -> Any,
	descriptor: proto.Descriptor,

	-- Pack a message into an Any.
	--
	-- typeUrlPrefix should be the base URL for the type URL. For example, Google uses
	-- "type.googleapis.com".
	pack: (payload: proto.Message<any, any>, typeUrlPrefix: string) -> Any,

	-- Returns the message contained by the Any (or nil if the Any is empty).
	unpack: (self: Any, registry: typeRegistry.TypeRegistry?) -> proto.Message<any, any>?,

	-- Returns true if and only if the Any contains an object of the type specified by
	-- typeName. If typeName is a full type URL, it will be compared; otherwise,
	-- only the type name will be compared.
	isA: (self: Any, typeName: string) -> boolean,
}

type _AnyFields = {
	type_url: string,
	value: buffer,
}

type _AnyPartialFields = {
	type_url: string?,
	value: buffer?,
}

export type Any = typeof(setmetatable({} :: _AnyFields, {} :: _AnyImpl))
type _AnyMessage = proto.Message<Any, _AnyPartialFields> & proto.CustomJson<Any, { [string]: any }>

do
	local _AnyImpl = {}
	_AnyImpl.__index = _AnyImpl

	function _AnyImpl.new(data: _AnyPartialFields?): Any
		return setmetatable({
			type_url = if data == nil or data.type_url == nil then "" else data.type_url,
			value = if data == nil or data.value == nil then buffer.create(0) else data.value,
		}, _AnyImpl :: _AnyImpl)
	end

	function _AnyImpl.encode(self: Any): buffer
		local output = buffer.create(0)
		local cursor = 0

		if self.type_url ~= nil and self.type_url ~= "" then
			output, cursor = proto.writeTag(output, cursor, 1, proto.wireTypes.lengthDelimited)
			output, cursor = proto.writeString(output, cursor, self.type_url)
		end

		if self.value ~= nil and buffer.len(self.value) > 0 then
			output, cursor = proto.writeTag(output, cursor, 2, proto.wireTypes.lengthDelimited)
			output, cursor = proto.writeBuffer(output, cursor, self.value, buffer.len(self.value))
		end

		local shrunkBuffer = buffer.create(cursor)
		buffer.copy(shrunkBuffer, 0, output, 0, cursor)
		return shrunkBuffer
	end

	function _AnyImpl.decode(input: buffer): Any
		local self = _AnyImpl.new()
		local cursor = 0

		while cursor < buffer.len(input) do
			local field, wireType
			field, wireType, cursor = proto.readTag(input, cursor)

			if wireType == proto.wireTypes.varint then
				-- No fields

				local _
				_, cursor = proto.readVarInt(input, cursor)
			elseif wireType == proto.wireTypes.lengthDelimited then
				if field == 1 then
					local value
					value, cursor = proto.readBuffer(input, cursor)
					self.type_url = proto.decodeString(value)
					continue
				elseif field == 2 then
					local value
					value, cursor = proto.readBuffer(input, cursor)
					self.value = value
					continue
				end

				local length
				length, cursor = proto.readVarInt(input, cursor)

				cursor += length
			elseif wireType == proto.wireTypes.i32 then
				-- No fields

				local _
				_, cursor = proto.readFixed32(input, cursor)
			elseif wireType == proto.wireTypes.i64 then
				-- No fields

				local _
				_, cursor = proto.readFixed64(input, cursor)
			else
				error("Unsupported wire type: " .. wireType)
			end
		end

		return self
	end

	function _AnyImpl.jsonEncode(input: Any): { [string]: any }
		local unpacked = input:unpack(typeRegistry.default)
		assert(unpacked ~= nil, "Cannot JSON-encode empty Any")

		local json = unpacked:jsonEncode()

		if typeof(json) == "string" then
			return { ["@type"] = input.type_url, ["value"] = json }
		end

		json["@type"] = input.type_url

		return json
	end

	function _AnyImpl.jsonDecode(input: { [string]: any }): Any
		local typeUrl = input["@type"]
		local typeName = typeUrl:match("([^/]+)$")
		local payloadType = typeRegistry.default:findMessage(typeName)

		if payloadType == nil then
			error(`Could not find message type \`{typeName}\` for Any`)
		end

		local typePrefix = typeUrl:sub(1, #typeUrl - #typeName - 1)

		local json = table.clone(input)
		-- For WKTs with special JSON representations, we need to decode input["value"] rather than
		-- input.
		if typeName:match("^google%.protobuf%.") and input["value"] ~= nil then
			json = input["value"]
		else
			-- Clear this because it's not part of the JSON encoding of the payload itself.
			json["@type"] = nil
		end

		return _AnyImpl.pack(payloadType.jsonDecode(json) :: proto.Message<unknown, unknown>, typePrefix)
	end

	type _AnyJsonDecode = typeof(_AnyImpl.jsonDecode) -- Luau bug: It doesn't recognize this for some reason

	_AnyImpl.descriptor = {
		name = "Any",
		fullName = "google.protobuf.Any",
	}

	function _AnyImpl.pack(payload: proto.Message<any, any>, typeUrlPrefix: string): Any
		return _AnyImpl.new({
			type_url = typeUrlPrefix .. "/" .. payload.descriptor.fullName,
			value = payload:encode(),
		})
	end

	-- Luau: It refuses to believe these are the same unpack, for some reason
	local anyUnpack: typeof(({} :: _AnyImpl).unpack) = function(
		self: Any,
		registry: typeRegistry.TypeRegistry?
	): proto.Message<any, any>?
		if self.value == nil then
			return nil
		end

		if registry == nil then
			registry = typeRegistry.default
		end
		assert(registry ~= nil, "Luau")

		local typeName = _AnyImpl.typeUrlToTypeName(self.type_url)
		local payloadType = registry:findMessage(typeName)

		if payloadType == nil then
			error(`Unknown type: \`{typeName}\``)
		end

		return payloadType.decode(self.value) :: proto.Message<any, any>
	end

	_AnyImpl.unpack = anyUnpack

	function _AnyImpl.isA(self: Any, typeName: string): boolean
		if self.type_url == typeName then
			return true
		end

		local suffix = "/" .. typeName
		return self.type_url:sub(-#suffix) == suffix
	end

	function _AnyImpl.typeUrlToTypeName(typeUrl: string): string
		local typeName = typeUrl:match("([^/]+)$")
		if typeName == nil then
			error(`Invalid type URL: \`{typeUrl}\``)
		end
		return typeName
	end

	messages.Any = _AnyImpl :: any -- Luau: Not sure why this intersection fails.

	typeRegistry.default:register(messages.Any)
end

return {
	Any = messages.Any,
}


--!strict
--!nolint LocalUnused
--!nolint ImportUnused
--# selene: allow(empty_if, if_same_then_else, manual_table_clone, unused_variable)
-- This file was @autogenerated by protoc-gen-luau
local proto = require("../../proto")
local typeRegistry = require("../../proto/typeRegistry")

type _Messages = {
	Duration: _DurationMessage,
}
local messages: _Messages = {} :: _Messages

type _DurationImpl = {
	__index: _DurationImpl,
	new: (fields: _DurationPartialFields?) -> Duration,
	encode: (self: Duration) -> buffer,
	decode: (input: buffer) -> Duration,
	jsonEncode: (self: Duration) -> string,
	jsonDecode: (input: string) -> Duration,
	descriptor: proto.Descriptor,
}

type _DurationFields = {
	seconds: number,
	nanos: number,
}

type _DurationPartialFields = {
	seconds: number?,
	nanos: number?,
}

export type Duration = typeof(setmetatable({} :: _DurationFields, {} :: _DurationImpl))
type _DurationMessage = proto.Message<Duration, _DurationPartialFields> & proto.CustomJson<Duration, string>

do
	local _DurationImpl = {}
	_DurationImpl.__index = _DurationImpl

	function _DurationImpl.new(data: _DurationPartialFields?): Duration
		return setmetatable({
			seconds = if data == nil or data.seconds == nil then 0 else data.seconds,
			nanos = if data == nil or data.nanos == nil then 0 else data.nanos,
		}, _DurationImpl :: _DurationImpl)
	end

	function _DurationImpl.encode(self: Duration): buffer
		local output = buffer.create(0)
		local cursor = 0

		if self.seconds ~= nil and self.seconds ~= 0 then
			output, cursor = proto.writeTag(output, cursor, 1, proto.wireTypes.varint)
			output, cursor = proto.writeVarInt(output, cursor, self.seconds)
		end

		if self.nanos ~= nil and self.nanos ~= 0 then
			output, cursor = proto.writeTag(output, cursor, 2, proto.wireTypes.varint)
			output, cursor = proto.writeVarInt(output, cursor, self.nanos)
		end

		local shrunkBuffer = buffer.create(cursor)
		buffer.copy(shrunkBuffer, 0, output, 0, cursor)
		return shrunkBuffer
	end

	function _DurationImpl.decode(input: buffer): Duration
		local self = _DurationImpl.new()
		local cursor = 0

		while cursor < buffer.len(input) do
			local field, wireType
			field, wireType, cursor = proto.readTag(input, cursor)

			if wireType == proto.wireTypes.varint then
				if field == 1 then
					local value
					value, cursor = proto.readVarIntI64(input, cursor)
					self.seconds = value
					continue
				elseif field == 2 then
					local value
					value, cursor = proto.readVarIntI32(input, cursor)
					self.nanos = value
					continue
				end

				local _
				_, cursor = proto.readVarInt(input, cursor)
			elseif wireType == proto.wireTypes.lengthDelimited then
				-- No fields

				local length
				length, cursor = proto.readVarInt(input, cursor)

				cursor += length
			elseif wireType == proto.wireTypes.i32 then
				-- No fields

				local _
				_, cursor = proto.readFixed32(input, cursor)
			elseif wireType == proto.wireTypes.i64 then
				-- No fields

				local _
				_, cursor = proto.readFixed64(input, cursor)
			else
				error("Unsupported wire type: " .. wireType)
			end
		end

		return self
	end

	-- Converts a number of nanos to a string representation.
	-- Sign and trailing zeroes are dropped.
	-- 500000000 -> ".5s"
	-- 500000001 -> ".500000001s"
	-- 0 -> ".0s"
	-- 1 -> ".000000001s"
	-- -1 -> "-.000000001s"
	local function serializeFractionalNanos(nanos: number): string
		nanos = nanos * math.sign(nanos)

		local nanosText = tostring(nanos)
		local nanosTextLength = #nanosText

		local leadingZeroes = 9 - nanosTextLength
		local leadingZeroesText = string.rep("0", leadingZeroes)

		-- remove trailing zeroes
		while nanosText:sub(-1) == "0" do
			nanosText = nanosText:sub(1, -2)
		end

		return `.{leadingZeroesText}{nanosText}s`
	end

	local function deserializeFractionalNanos(nanosText: string): number
		local nanos = assert(tonumber(nanosText), "Invalid nanos string received, couldn't turn into a number")
		for digit = #nanosText + 1, 9 do
			nanos *= 10
		end

		return nanos
	end

	function _DurationImpl.jsonEncode(duration: Duration): string
		assert(
			duration.seconds == nil or (duration.seconds >= -315576000000 and duration.seconds <= 315576000000),
			"Seconds out of range"
		)
		assert(
			duration.nanos == nil or (duration.nanos >= -999999999 and duration.nanos <= 999999999),
			"Nanoseconds out of range"
		)

		-- TODO: For durations of one second or more, a non-zero value for the nanos field must be of the same sign as the seconds field.
		if duration.nanos == nil or duration.nanos == 0 or duration.nanos % 1e9 == 0 then
			return `{duration.seconds}s`
		end

		local nanoSign = if duration.seconds == 0 and math.sign(duration.nanos) == -1 then "-" else ""
		return string.format("%s%d%s", nanoSign, duration.seconds or 0, serializeFractionalNanos(duration.nanos))
	end

	function _DurationImpl.jsonDecode(anyValue: any): Duration
		local maxSeconds = 315576000000
		local minSeconds = -315576000000
		local durationString: string = anyValue
		local simpleSecondsText = string.match(durationString, "^([%-0-9]+)s$")
		local seconds = 0
		local nanos = 0
		if simpleSecondsText ~= nil then
			seconds = assert(
				tonumber(simpleSecondsText),
				"Invalid duration string received--was formatted as just having seconds, but wasn't a properly formatted int"
			)
		else
			local secondsText, nanosText = string.match(durationString, "^([%-0-9]+)%.([0-9]+)s$")
			assert(secondsText ~= nil, "Invalid duration string received--seconds provided are invalid")
			assert(nanosText ~= nil, "Invalid duration string received--nanos provided are invalid")

			seconds = assert(tonumber(secondsText), "Invalid duration string received--seconds provided are invalid")
			nanos = deserializeFractionalNanos(nanosText) * (secondsText:sub(1, 1) == "-" and -1 or 1)
		end

		if seconds > maxSeconds then
			error(`Duration seconds cannot exceed {maxSeconds}`)
		elseif seconds < minSeconds then
			error(`Duration seconds cannot be less than {minSeconds}`)
		end

		return _DurationImpl.new({
			seconds = seconds,
			nanos = nanos,
		})
	end

	_DurationImpl.descriptor = {
		name = "Duration",
		fullName = "google.protobuf.Duration",
	}

	messages.Duration = _DurationImpl :: any -- Luau: Not sure why this intersection fails.

	typeRegistry.default:register(messages.Duration)
end

return {
	Duration = messages.Duration,
}


--!strict
--!nolint LocalUnused
--!nolint ImportUnused
--# selene: allow(empty_if, if_same_then_else, manual_table_clone, unused_variable)
-- This file was @autogenerated by protoc-gen-luau
local proto = require("../../proto")
local typeRegistry = require("../../proto/typeRegistry")

type _Messages = {
	FieldMask: _FieldMaskMessage,
}
local messages: _Messages = {} :: _Messages

type _FieldMaskImpl = {
	__index: _FieldMaskImpl,
	new: (fields: _FieldMaskPartialFields?) -> FieldMask,
	encode: (self: FieldMask) -> buffer,
	decode: (input: buffer) -> FieldMask,
	jsonEncode: (self: FieldMask) -> string,
	jsonDecode: (input: string) -> FieldMask,
	descriptor: proto.Descriptor,
}

type _FieldMaskFields = {
	paths: { string },
}

type _FieldMaskPartialFields = {
	paths: { string }?,
}

export type FieldMask = typeof(setmetatable({} :: _FieldMaskFields, {} :: _FieldMaskImpl))
type _FieldMaskMessage = proto.Message<FieldMask, _FieldMaskPartialFields> & proto.CustomJson<FieldMask, string>

do
	local _FieldMaskImpl = {}
	_FieldMaskImpl.__index = _FieldMaskImpl

	function _FieldMaskImpl.new(data: _FieldMaskPartialFields?): FieldMask
		return setmetatable({
			paths = if data == nil or data.paths == nil then {} else data.paths,
		}, _FieldMaskImpl :: _FieldMaskImpl)
	end

	function _FieldMaskImpl.encode(self: FieldMask): buffer
		local output = buffer.create(0)
		local cursor = 0

		if self.paths ~= nil and #self.paths > 0 then
			for _, value in self.paths do
				output, cursor = proto.writeTag(output, cursor, 1, proto.wireTypes.lengthDelimited)
				output, cursor = proto.writeString(output, cursor, value)
			end
		end

		local shrunkBuffer = buffer.create(cursor)
		buffer.copy(shrunkBuffer, 0, output, 0, cursor)
		return shrunkBuffer
	end

	function _FieldMaskImpl.decode(input: buffer): FieldMask
		local self = _FieldMaskImpl.new()
		local cursor = 0

		while cursor < buffer.len(input) do
			local field, wireType
			field, wireType, cursor = proto.readTag(input, cursor)

			if wireType == proto.wireTypes.varint then
				-- No fields

				local _
				_, cursor = proto.readVarInt(input, cursor)
			elseif wireType == proto.wireTypes.lengthDelimited then
				if field == 1 then
					local value
					value, cursor = proto.readBuffer(input, cursor)
					table.insert(self.paths, proto.decodeString(value))
					continue
				end

				local length
				length, cursor = proto.readVarInt(input, cursor)

				cursor += length
			elseif wireType == proto.wireTypes.i32 then
				-- No fields

				local _
				_, cursor = proto.readFixed32(input, cursor)
			elseif wireType == proto.wireTypes.i64 then
				-- No fields

				local _
				_, cursor = proto.readFixed64(input, cursor)
			else
				error("Unsupported wire type: " .. wireType)
			end
		end

		return self
	end

	function _FieldMaskImpl.jsonEncode(fieldMask: FieldMask): string
		return table.concat(fieldMask.paths, ",")
	end

	function _FieldMaskImpl.jsonDecode(anyInput: any): FieldMask
		local input: string = anyInput

		local paths = {}
		for path in string.gmatch(input, "[^,]+") do
			table.insert(paths, path)
		end

		return _FieldMaskImpl.new({
			paths = paths,
		})
	end

	_FieldMaskImpl.descriptor = {
		name = "FieldMask",
		fullName = "google.protobuf.FieldMask",
	}

	messages.FieldMask = _FieldMaskImpl :: any -- Luau: Not sure why this intersection fails.

	typeRegistry.default:register(messages.FieldMask)
end

return {
	FieldMask = messages.FieldMask,
}


--!strict
--!nolint LocalUnused
--!nolint ImportUnused
--# selene: allow(empty_if, if_same_then_else, manual_table_clone, unused_variable)
-- This file was @autogenerated by protoc-gen-luau
local proto = require("../../proto")
local typeRegistry = require("../../proto/typeRegistry")

type _Messages = {
	Struct: _StructMessage,
	Struct_FieldsEntry: _Struct_FieldsEntryMessage,
	Value: _ValueMessage,
	ListValue: _ListValueMessage,
	NullValue: _NullValueMessage,
}
local messages: _Messages = {} :: _Messages

type _StructImpl = {
	__index: _StructImpl,
	new: (fields: _StructPartialFields?) -> Struct,
	encode: (self: Struct) -> buffer,
	decode: (input: buffer) -> Struct,
	jsonEncode: (self: Struct) -> { [string]: any },
	jsonDecode: (input: { [string]: any }) -> Struct,
	descriptor: proto.Descriptor,
}

type _StructFields = {
	fields: { [string]: Value },
}

type _StructPartialFields = {
	fields: { [string]: Value }?,
}

export type Struct = typeof(setmetatable({} :: _StructFields, {} :: _StructImpl))
type _StructMessage = proto.Message<Struct, _StructPartialFields> & proto.CustomJson<Struct, { [string]: any }>

type _Struct_FieldsEntryImpl = {
	__index: _Struct_FieldsEntryImpl,
	new: (fields: _Struct_FieldsEntryPartialFields?) -> Struct_FieldsEntry,
	encode: (self: Struct_FieldsEntry) -> buffer,
	decode: (input: buffer) -> Struct_FieldsEntry,
	jsonEncode: (self: Struct_FieldsEntry) -> { [string]: any },
	jsonDecode: (input: { [string]: any }) -> Struct_FieldsEntry,
	descriptor: proto.Descriptor,
}

type _Struct_FieldsEntryFields = {
	key: string,
	value: Value?,
}

type _Struct_FieldsEntryPartialFields = {
	key: string?,
	value: Value?,
}

export type Struct_FieldsEntry = typeof(setmetatable({} :: _Struct_FieldsEntryFields, {} :: _Struct_FieldsEntryImpl))
type _Struct_FieldsEntryMessage = proto.Message<Struct_FieldsEntry, _Struct_FieldsEntryPartialFields>

type _ValueImpl = {
	__index: _ValueImpl,
	new: (fields: _ValuePartialFields?) -> Value,
	encode: (self: Value) -> buffer,
	decode: (input: buffer) -> Value,
	jsonEncode: (self: Value) -> any,
	jsonDecode: (input: any) -> Value,
	descriptor: proto.Descriptor,
}

type _ValueFields = {
	kind: (
		{ type: "null_value", value: NullValue }
		| { type: "number_value", value: number }
		| { type: "string_value", value: string }
		| { type: "bool_value", value: boolean }
		| { type: "struct_value", value: Struct }
		| { type: "list_value", value: ListValue }
	)?,
}

type _ValuePartialFields = {
	kind: (
		{ type: "null_value", value: NullValue }
		| { type: "number_value", value: number }
		| { type: "string_value", value: string }
		| { type: "bool_value", value: boolean }
		| { type: "struct_value", value: Struct }
		| { type: "list_value", value: ListValue }
	)?,
}

export type Value = typeof(setmetatable({} :: _ValueFields, {} :: _ValueImpl))
type _ValueMessage = proto.Message<Value, _ValuePartialFields> & proto.CustomJson<Value, any>

type _ListValueImpl = {
	__index: _ListValueImpl,
	new: (fields: _ListValuePartialFields?) -> ListValue,
	encode: (self: ListValue) -> buffer,
	decode: (input: buffer) -> ListValue,
	jsonEncode: (self: ListValue) -> { any },
	jsonDecode: (input: { any }) -> ListValue,
	descriptor: proto.Descriptor,
}

type _ListValueFields = {
	values: { Value },
}

type _ListValuePartialFields = {
	values: { Value }?,
}

export type ListValue = typeof(setmetatable({} :: _ListValueFields, {} :: _ListValueImpl))
type _ListValueMessage = proto.Message<ListValue, _ListValuePartialFields> & proto.CustomJson<ListValue, { any }>

type _NullValueMessage = proto.Enum<NullValue>
export type NullValue = "NULL_VALUE" | number -- Unknown

do
	local _StructImpl = {}
	_StructImpl.__index = _StructImpl

	function _StructImpl.new(data: _StructPartialFields?): Struct
		return setmetatable({
			fields = if data == nil or data.fields == nil then {} else data.fields,
		}, _StructImpl :: _StructImpl)
	end

	function _StructImpl.encode(self: Struct): buffer
		local output = buffer.create(0)
		local cursor = 0

		if self.fields ~= nil and next(self.fields) ~= nil then
			for key, value in self.fields do
				local mapBuffer = buffer.create(0)
				local mapCursor = 0
				mapBuffer, mapCursor = proto.writeTag(mapBuffer, mapCursor, 1, proto.wireTypes.lengthDelimited)
				mapBuffer, mapCursor = proto.writeString(mapBuffer, mapCursor, key)
				local encoded = value:encode()
				mapBuffer, mapCursor = proto.writeTag(mapBuffer, mapCursor, 2, proto.wireTypes.lengthDelimited)
				mapBuffer, mapCursor = proto.writeBuffer(mapBuffer, mapCursor, encoded, buffer.len(encoded))
				output, cursor = proto.writeTag(output, cursor, 1, proto.wireTypes.lengthDelimited)
				output, cursor = proto.writeBuffer(output, cursor, mapBuffer, mapCursor)
			end
		end

		local shrunkBuffer = buffer.create(cursor)
		buffer.copy(shrunkBuffer, 0, output, 0, cursor)
		return shrunkBuffer
	end

	function _StructImpl.decode(input: buffer): Struct
		local self = _StructImpl.new()
		local cursor = 0

		while cursor < buffer.len(input) do
			local field, wireType
			field, wireType, cursor = proto.readTag(input, cursor)

			if wireType == proto.wireTypes.varint then
				-- No fields

				local _
				_, cursor = proto.readVarInt(input, cursor)
			elseif wireType == proto.wireTypes.lengthDelimited then
				if field == 1 then
					local value
					value, cursor = proto.readBuffer(input, cursor)

					local mapEntry = messages.Struct_FieldsEntry.decode(value)

					local keyDefault = ""
					local valueDefault = messages.Value.new()

					self.fields[mapEntry.key or keyDefault] = mapEntry.value or valueDefault

					continue
				end

				local length
				length, cursor = proto.readVarInt(input, cursor)

				cursor += length
			elseif wireType == proto.wireTypes.i32 then
				-- No fields

				local _
				_, cursor = proto.readFixed32(input, cursor)
			elseif wireType == proto.wireTypes.i64 then
				-- No fields

				local _
				_, cursor = proto.readFixed64(input, cursor)
			else
				error("Unsupported wire type: " .. wireType)
			end
		end

		return self
	end

	function _StructImpl.jsonEncode(struct: Struct): { [string]: any }
		local serialized = {}

		for key, value in struct.fields do
			serialized[key] = messages.Value.jsonEncode(value)
		end

		return serialized
	end

	function _StructImpl.jsonDecode(input: { [string]: any }): Struct
		local fields = {}

		for key, serializedValue in input do
			fields[key] = messages.Value.jsonDecode(serializedValue)
		end

		return _StructImpl.new({
			fields = fields,
		})
	end

	_StructImpl.descriptor = {
		name = "Struct",
		fullName = "google.protobuf.Struct",
	}

	messages.Struct = _StructImpl :: any -- Luau: Not sure why this intersection fails.

	typeRegistry.default:register(messages.Struct)
end

do
	local _Struct_FieldsEntryImpl = {}
	_Struct_FieldsEntryImpl.__index = _Struct_FieldsEntryImpl

	function _Struct_FieldsEntryImpl.new(data: _Struct_FieldsEntryPartialFields?): Struct_FieldsEntry
		return setmetatable({
			key = if data == nil or data.key == nil then "" else data.key,
			value = if data == nil or data.value == nil then nil else data.value,
		}, _Struct_FieldsEntryImpl :: _Struct_FieldsEntryImpl)
	end

	function _Struct_FieldsEntryImpl.encode(self: Struct_FieldsEntry): buffer
		local output = buffer.create(0)
		local cursor = 0

		if self.key ~= nil and self.key ~= "" then
			output, cursor = proto.writeTag(output, cursor, 1, proto.wireTypes.lengthDelimited)
			output, cursor = proto.writeString(output, cursor, self.key)
		end

		if self.value ~= nil then
			local encoded = self.value:encode()
			output, cursor = proto.writeTag(output, cursor, 2, proto.wireTypes.lengthDelimited)
			output, cursor = proto.writeBuffer(output, cursor, encoded, buffer.len(encoded))
		end

		local shrunkBuffer = buffer.create(cursor)
		buffer.copy(shrunkBuffer, 0, output, 0, cursor)
		return shrunkBuffer
	end

	function _Struct_FieldsEntryImpl.decode(input: buffer): Struct_FieldsEntry
		local self = _Struct_FieldsEntryImpl.new()
		local cursor = 0

		while cursor < buffer.len(input) do
			local field, wireType
			field, wireType, cursor = proto.readTag(input, cursor)

			if wireType == proto.wireTypes.varint then
				-- No fields

				local _
				_, cursor = proto.readVarInt(input, cursor)
			elseif wireType == proto.wireTypes.lengthDelimited then
				if field == 1 then
					local value
					value, cursor = proto.readBuffer(input, cursor)
					self.key = proto.decodeString(value)
					continue
				elseif field == 2 then
					local value
					value, cursor = proto.readBuffer(input, cursor)
					self.value = messages.Value.decode(value)
					continue
				end

				local length
				length, cursor = proto.readVarInt(input, cursor)

				cursor += length
			elseif wireType == proto.wireTypes.i32 then
				-- No fields

				local _
				_, cursor = proto.readFixed32(input, cursor)
			elseif wireType == proto.wireTypes.i64 then
				-- No fields

				local _
				_, cursor = proto.readFixed64(input, cursor)
			else
				error("Unsupported wire type: " .. wireType)
			end
		end

		return self
	end

	function _Struct_FieldsEntryImpl.jsonEncode(self: Struct_FieldsEntry): any
		local output = {}

		if self.key ~= nil and self.key ~= "" then
			output.key = self.key
		end

		if self.value ~= nil then
			output.value = self.value:jsonEncode()
		end

		return output
	end

	function _Struct_FieldsEntryImpl.jsonDecode(input: { [string]: any }): Struct_FieldsEntry
		local self = _Struct_FieldsEntryImpl.new()

		if input.key ~= nil then
			self.key = input.key
		end

		if input.value ~= nil then
			self.value = messages.Value.jsonDecode(input.value)
		end

		return self
	end

	_Struct_FieldsEntryImpl.descriptor = {
		name = "Struct_FieldsEntry",
		fullName = "google.protobuf.FieldsEntry",
	}

	messages.Struct_FieldsEntry = _Struct_FieldsEntryImpl :: any -- Luau: Not sure why this intersection fails.

	typeRegistry.default:register(messages.Struct_FieldsEntry)
end

do
	local _ValueImpl = {}
	_ValueImpl.__index = _ValueImpl

	function _ValueImpl.new(data: _ValuePartialFields?): Value
		return setmetatable({
			kind = if data == nil or data.kind == nil then nil else data.kind,
		}, _ValueImpl :: _ValueImpl)
	end

	function _ValueImpl.encode(self: Value): buffer
		local output = buffer.create(0)
		local cursor = 0

		if self.kind ~= nil then
			if self.kind.type == "null_value" then
				output, cursor = proto.writeTag(output, cursor, 1, proto.wireTypes.varint)
				output, cursor = proto.writeVarInt(output, cursor, messages.NullValue.toNumber(self.kind.value :: any))
			elseif self.kind.type == "number_value" then
				output, cursor = proto.writeTag(output, cursor, 2, proto.wireTypes.i64)
				output, cursor = proto.writeDouble(output, cursor, self.kind.value)
			elseif self.kind.type == "string_value" then
				output, cursor = proto.writeTag(output, cursor, 3, proto.wireTypes.lengthDelimited)
				output, cursor = proto.writeString(output, cursor, self.kind.value)
			elseif self.kind.type == "bool_value" then
				output, cursor = proto.writeTag(output, cursor, 4, proto.wireTypes.varint)
				output, cursor = proto.writeVarInt(output, cursor, if self.kind.value then 1 else 0)
			elseif self.kind.type == "struct_value" then
				local encoded = self.kind.value:encode()
				output, cursor = proto.writeTag(output, cursor, 5, proto.wireTypes.lengthDelimited)
				output, cursor = proto.writeBuffer(output, cursor, encoded, buffer.len(encoded))
			elseif self.kind.type == "list_value" then
				local encoded = self.kind.value:encode()
				output, cursor = proto.writeTag(output, cursor, 6, proto.wireTypes.lengthDelimited)
				output, cursor = proto.writeBuffer(output, cursor, encoded, buffer.len(encoded))
			end
		end

		local shrunkBuffer = buffer.create(cursor)
		buffer.copy(shrunkBuffer, 0, output, 0, cursor)
		return shrunkBuffer
	end

	function _ValueImpl.decode(input: buffer): Value
		local self = _ValueImpl.new()
		local cursor = 0

		while cursor < buffer.len(input) do
			local field, wireType
			field, wireType, cursor = proto.readTag(input, cursor)

			if wireType == proto.wireTypes.varint then
				if field == 1 then
					local value
					value, cursor = proto.readVarIntI32(input, cursor)
					self.kind = {
						type = "null_value",
						value = (messages.NullValue.fromNumber(value) or value) :: any --[[ Luau: Enums are a string intersection which Luau is quick to dismantle ]],
					}
					continue
				elseif field == 4 then
					local value
					value, cursor = proto.readVarInt(input, cursor)
					self.kind = { type = "bool_value", value = value ~= 0 }
					continue
				end

				local _
				_, cursor = proto.readVarInt(input, cursor)
			elseif wireType == proto.wireTypes.lengthDelimited then
				if field == 3 then
					local value
					value, cursor = proto.readBuffer(input, cursor)
					self.kind = { type = "string_value", value = proto.decodeString(value) }
					continue
				elseif field == 5 then
					local value
					value, cursor = proto.readBuffer(input, cursor)
					self.kind = { type = "struct_value", value = messages.Struct.decode(value) }
					continue
				elseif field == 6 then
					local value
					value, cursor = proto.readBuffer(input, cursor)
					self.kind = { type = "list_value", value = messages.ListValue.decode(value) }
					continue
				end

				local length
				length, cursor = proto.readVarInt(input, cursor)

				cursor += length
			elseif wireType == proto.wireTypes.i32 then
				-- No fields

				local _
				_, cursor = proto.readFixed32(input, cursor)
			elseif wireType == proto.wireTypes.i64 then
				if field == 2 then
					local value
					value, cursor = proto.readDouble(input, cursor)
					self.kind = { type = "number_value", value = value }
					continue
				end

				local _
				_, cursor = proto.readFixed64(input, cursor)
			else
				error("Unsupported wire type: " .. wireType)
			end
		end

		return self
	end

	function _ValueImpl.jsonEncode(input: Value): any
		local kind = input.kind

		if kind == nil or kind.type == "null_value" then
			return nil
		elseif kind.type == "number_value" or kind.type == "string_value" or kind.type == "bool_value" then
			return kind.value
		elseif kind.type == "list_value" then
			return messages.ListValue.jsonEncode(kind.value)
		elseif kind.type == "struct_value" then
			return messages.Struct.jsonEncode(kind.value)
		else
			local exhaustiveMatch: never = kind.type
			return exhaustiveMatch
		end
	end

	function _ValueImpl.jsonDecode(input: any): Value
		if input == nil then
			return _ValueImpl.new({ kind = { type = "null_value", value = "NULL_VALUE" } })
		elseif typeof(input) == "number" then
			return _ValueImpl.new({ kind = { type = "number_value", value = input } })
		elseif typeof(input) == "string" then
			return _ValueImpl.new({ kind = { type = "string_value", value = input } })
		elseif typeof(input) == "boolean" then
			return _ValueImpl.new({ kind = { type = "bool_value", value = input } })
		elseif typeof(input) == "table" then
			local isArray = true
			local count = 0
			for _ in input do
				count += 1
				if count > #input then
					isArray = false
					break
				end
			end

			isArray = count == #input

			if isArray then
				return _ValueImpl.new({ kind = { type = "list_value", value = messages.ListValue.jsonDecode(input) } })
			else
				return _ValueImpl.new({ kind = { type = "struct_value", value = messages.Struct.jsonDecode(input) } })
			end
		else
			error(`Unknown input type: {typeof(input)}`)
		end
	end

	_ValueImpl.descriptor = {
		name = "Value",
		fullName = "google.protobuf.Value",
	}

	messages.Value = _ValueImpl :: any -- Luau: Not sure why this intersection fails.

	typeRegistry.default:register(messages.Value)
end

do
	local _ListValueImpl = {}
	_ListValueImpl.__index = _ListValueImpl

	function _ListValueImpl.new(data: _ListValuePartialFields?): ListValue
		return setmetatable({
			values = if data == nil or data.values == nil then {} else data.values,
		}, _ListValueImpl :: _ListValueImpl)
	end

	function _ListValueImpl.encode(self: ListValue): buffer
		local output = buffer.create(0)
		local cursor = 0

		if self.values ~= nil and #self.values > 0 then
			for _, value in self.values do
				local encoded = value:encode()
				output, cursor = proto.writeTag(output, cursor, 1, proto.wireTypes.lengthDelimited)
				output, cursor = proto.writeBuffer(output, cursor, encoded, buffer.len(encoded))
			end
		end

		local shrunkBuffer = buffer.create(cursor)
		buffer.copy(shrunkBuffer, 0, output, 0, cursor)
		return shrunkBuffer
	end

	function _ListValueImpl.decode(input: buffer): ListValue
		local self = _ListValueImpl.new()
		local cursor = 0

		while cursor < buffer.len(input) do
			local field, wireType
			field, wireType, cursor = proto.readTag(input, cursor)

			if wireType == proto.wireTypes.varint then
				-- No fields

				local _
				_, cursor = proto.readVarInt(input, cursor)
			elseif wireType == proto.wireTypes.lengthDelimited then
				if field == 1 then
					local value
					value, cursor = proto.readBuffer(input, cursor)
					table.insert(self.values, messages.Value.decode(value))
					continue
				end

				local length
				length, cursor = proto.readVarInt(input, cursor)

				cursor += length
			elseif wireType == proto.wireTypes.i32 then
				-- No fields

				local _
				_, cursor = proto.readFixed32(input, cursor)
			elseif wireType == proto.wireTypes.i64 then
				-- No fields

				local _
				_, cursor = proto.readFixed64(input, cursor)
			else
				error("Unsupported wire type: " .. wireType)
			end
		end

		return self
	end

	function _ListValueImpl.jsonEncode(list: ListValue): { any }
		local serialized = {}

		for _, value in list.values do
			table.insert(serialized, messages.Value.jsonEncode(value))
		end

		return serialized
	end

	function _ListValueImpl.jsonDecode(anyValue: any): ListValue
		local input: { any } = anyValue

		local values = {}

		for _, value in input do
			table.insert(values, messages.Value.jsonDecode(value))
		end

		return _ListValueImpl.new({
			values = values,
		})
	end

	_ListValueImpl.descriptor = {
		name = "ListValue",
		fullName = "google.protobuf.ListValue",
	}

	messages.ListValue = _ListValueImpl :: any -- Luau: Not sure why this intersection fails.

	typeRegistry.default:register(messages.ListValue)
end

messages.NullValue = {
	fromNumber = function(value: number): NullValue?
		if value == 0 then
			return "NULL_VALUE"
		else
			return nil
		end
	end,

	toNumber = function(self: NullValue): number
		if self == "NULL_VALUE" then
			return 0
		else
			return self
		end
	end,

	fromName = function(name: string): NullValue?
		if name == "NULL_VALUE" then
			return "NULL_VALUE"
		else
			return nil
		end
	end,
}

return {
	Struct = messages.Struct,
	Value = messages.Value,
	ListValue = messages.ListValue,
	NullValue = messages.NullValue,
}


--!strict
--!nolint LocalUnused
--!nolint ImportUnused
--# selene: allow(empty_if, if_same_then_else, manual_table_clone, unused_variable)
-- This file was @autogenerated by protoc-gen-luau
local proto = require("../../proto")
local typeRegistry = require("../../proto/typeRegistry")

type _Messages = {
	Timestamp: _TimestampMessage,
}
local messages: _Messages = {} :: _Messages

type _TimestampImpl = {
	__index: _TimestampImpl,
	new: (fields: _TimestampPartialFields?) -> Timestamp,
	encode: (self: Timestamp) -> buffer,
	decode: (input: buffer) -> Timestamp,
	jsonEncode: (self: Timestamp) -> string,
	jsonDecode: (input: string) -> Timestamp,
	descriptor: proto.Descriptor,
}

type _TimestampFields = {
	seconds: number,
	nanos: number,
}

type _TimestampPartialFields = {
	seconds: number?,
	nanos: number?,
}

export type Timestamp = typeof(setmetatable({} :: _TimestampFields, {} :: _TimestampImpl))
type _TimestampMessage = proto.Message<Timestamp, _TimestampPartialFields> & proto.CustomJson<Timestamp, string>

do
	local _TimestampImpl = {}
	_TimestampImpl.__index = _TimestampImpl

	function _TimestampImpl.new(data: _TimestampPartialFields?): Timestamp
		return setmetatable({
			seconds = if data == nil or data.seconds == nil then 0 else data.seconds,
			nanos = if data == nil or data.nanos == nil then 0 else data.nanos,
		}, _TimestampImpl :: _TimestampImpl)
	end

	function _TimestampImpl.encode(self: Timestamp): buffer
		local output = buffer.create(0)
		local cursor = 0

		if self.seconds ~= nil and self.seconds ~= 0 then
			output, cursor = proto.writeTag(output, cursor, 1, proto.wireTypes.varint)
			output, cursor = proto.writeVarInt(output, cursor, self.seconds)
		end

		if self.nanos ~= nil and self.nanos ~= 0 then
			output, cursor = proto.writeTag(output, cursor, 2, proto.wireTypes.varint)
			output, cursor = proto.writeVarInt(output, cursor, self.nanos)
		end

		local shrunkBuffer = buffer.create(cursor)
		buffer.copy(shrunkBuffer, 0, output, 0, cursor)
		return shrunkBuffer
	end

	function _TimestampImpl.decode(input: buffer): Timestamp
		local self = _TimestampImpl.new()
		local cursor = 0

		while cursor < buffer.len(input) do
			local field, wireType
			field, wireType, cursor = proto.readTag(input, cursor)

			if wireType == proto.wireTypes.varint then
				if field == 1 then
					local value
					value, cursor = proto.readVarIntI64(input, cursor)
					self.seconds = value
					continue
				elseif field == 2 then
					local value
					value, cursor = proto.readVarIntI32(input, cursor)
					self.nanos = value
					continue
				end

				local _
				_, cursor = proto.readVarInt(input, cursor)
			elseif wireType == proto.wireTypes.lengthDelimited then
				-- No fields

				local length
				length, cursor = proto.readVarInt(input, cursor)

				cursor += length
			elseif wireType == proto.wireTypes.i32 then
				-- No fields

				local _
				_, cursor = proto.readFixed32(input, cursor)
			elseif wireType == proto.wireTypes.i64 then
				-- No fields

				local _
				_, cursor = proto.readFixed64(input, cursor)
			else
				error("Unsupported wire type: " .. wireType)
			end
		end

		return self
	end

	local function serializeFractionalNanos(nanos: number): string
		if nanos % 1e6 == 0 then
			return string.format(".%03d", nanos)
		elseif nanos % 1e3 == 0 then
			return string.format(".%06d", nanos)
		else
			return string.format(".%09d", nanos)
		end
	end

	local function deserializeFractionalNanos(nanosText: string): number
		local nanos = assert(tonumber(nanosText), "Invalid nanos string received, couldn't turn into a number")
		for digit = #nanosText + 1, 9 do
			nanos += 10 ^ (digit - 1)
		end

		return nanos
	end

	function _TimestampImpl.jsonEncode(timestamp: Timestamp): string
		if timestamp.seconds > 253402300799 then
			error("Invalid timestamp provided: years after 9999 are not supported")
		end

		if timestamp.seconds < -62135596800 then
			error("Invalid timestamp provided: years before 0001 are not supported")
		end

		local dateInfo = os.date("!*t", timestamp.seconds or 0)
		if dateInfo == nil then
			error("Invalid timestamp provided: exceeded supported timestamp range on this platform")
		end

		return string.format(
			"%04d-%02d-%02dT%02d:%02d:%02d%sZ",
			dateInfo.year,
			dateInfo.month,
			dateInfo.day,
			dateInfo.hour,
			dateInfo.min,
			dateInfo.sec,
			if timestamp.nanos == 0 then "" else serializeFractionalNanos(timestamp.nanos)
		)
	end

	function _TimestampImpl.jsonDecode(anyValue: any): Timestamp
		local text: string = anyValue

		local year, month, day, hour, min, sec, nanosWithDot =
			string.match(text, "^([0-9]+)-([0-9]+)-([0-9]+)T([0-9]+):([0-9]+):([0-9]+)(.*)Z$")

		if
			year == nil
			or month == nil
			or day == nil
			or hour == nil
			or min == nil
			or sec == nil
			or nanosWithDot == nil
		then
			error("Invalid timestamp provided")
		end

		local nanos = 0
		if #nanosWithDot ~= 0 then
			local nanosText = string.match(nanosWithDot, "^%.([0-9]+)$")
			if nanosText == nil then
				error("Invalid nanoseconds provided in timestamp: `" .. nanosWithDot .. "`")
			end

			nanos = deserializeFractionalNanos(nanosText)
		end

		local totalSeconds = os.time({
			year = assert(tonumber(year), "Invalid formatted year"),
			month = assert(tonumber(month), "Invalid formatted month"),
			day = assert(tonumber(day), "Invalid formatted day"),
			hour = assert(tonumber(hour), "Invalid formatted hour"),
			min = assert(tonumber(min), "Invalid formatted min"),
			sec = assert(tonumber(sec), "Invalid formatted sec"),
		})

		return _TimestampImpl.new({
			seconds = totalSeconds,
			nanos = nanos,
		})
	end

	_TimestampImpl.descriptor = {
		name = "Timestamp",
		fullName = "google.protobuf.Timestamp",
	}

	messages.Timestamp = _TimestampImpl :: any -- Luau: Not sure why this intersection fails.

	typeRegistry.default:register(messages.Timestamp)
end

return {
	Timestamp = messages.Timestamp,
}


--!strict
--!nolint LocalUnused
--!nolint ImportUnused
--# selene: allow(empty_if, if_same_then_else, manual_table_clone, unused_variable)
-- This file was @autogenerated by protoc-gen-luau
local proto = require("../../proto")
local typeRegistry = require("../../proto/typeRegistry")

type _Messages = {
	DoubleValue: _DoubleValueMessage,
	FloatValue: _FloatValueMessage,
	Int64Value: _Int64ValueMessage,
	UInt64Value: _UInt64ValueMessage,
	Int32Value: _Int32ValueMessage,
	UInt32Value: _UInt32ValueMessage,
	BoolValue: _BoolValueMessage,
	StringValue: _StringValueMessage,
	BytesValue: _BytesValueMessage,
}
local messages: _Messages = {} :: _Messages

type _DoubleValueImpl = {
	__index: _DoubleValueImpl,
	new: (fields: _DoubleValuePartialFields?) -> DoubleValue,
	encode: (self: DoubleValue) -> buffer,
	decode: (input: buffer) -> DoubleValue,
	jsonEncode: (self: DoubleValue) -> number,
	jsonDecode: (input: number) -> DoubleValue,
	descriptor: proto.Descriptor,
}

type _DoubleValueFields = {
	value: number,
}

type _DoubleValuePartialFields = {
	value: number?,
}

export type DoubleValue = typeof(setmetatable({} :: _DoubleValueFields, {} :: _DoubleValueImpl))
type _DoubleValueMessage = proto.Message<DoubleValue, _DoubleValuePartialFields> & proto.CustomJson<DoubleValue, number>

type _FloatValueImpl = {
	__index: _FloatValueImpl,
	new: (fields: _FloatValuePartialFields?) -> FloatValue,
	encode: (self: FloatValue) -> buffer,
	decode: (input: buffer) -> FloatValue,
	jsonEncode: (self: FloatValue) -> number,
	jsonDecode: (input: number) -> FloatValue,
	descriptor: proto.Descriptor,
}

type _FloatValueFields = {
	value: number,
}

type _FloatValuePartialFields = {
	value: number?,
}

export type FloatValue = typeof(setmetatable({} :: _FloatValueFields, {} :: _FloatValueImpl))
type _FloatValueMessage = proto.Message<FloatValue, _FloatValuePartialFields> & proto.CustomJson<FloatValue, number>

type _Int64ValueImpl = {
	__index: _Int64ValueImpl,
	new: (fields: _Int64ValuePartialFields?) -> Int64Value,
	encode: (self: Int64Value) -> buffer,
	decode: (input: buffer) -> Int64Value,
	jsonEncode: (self: Int64Value) -> number,
	jsonDecode: (input: number) -> Int64Value,
	descriptor: proto.Descriptor,
}

type _Int64ValueFields = {
	value: number,
}

type _Int64ValuePartialFields = {
	value: number?,
}

export type Int64Value = typeof(setmetatable({} :: _Int64ValueFields, {} :: _Int64ValueImpl))
type _Int64ValueMessage = proto.Message<Int64Value, _Int64ValuePartialFields> & proto.CustomJson<Int64Value, number>

type _UInt64ValueImpl = {
	__index: _UInt64ValueImpl,
	new: (fields: _UInt64ValuePartialFields?) -> UInt64Value,
	encode: (self: UInt64Value) -> buffer,
	decode: (input: buffer) -> UInt64Value,
	jsonEncode: (self: UInt64Value) -> number,
	jsonDecode: (input: number) -> UInt64Value,
	descriptor: proto.Descriptor,
}

type _UInt64ValueFields = {
	value: number,
}

type _UInt64ValuePartialFields = {
	value: number?,
}

export type UInt64Value = typeof(setmetatable({} :: _UInt64ValueFields, {} :: _UInt64ValueImpl))
type _UInt64ValueMessage = proto.Message<UInt64Value, _UInt64ValuePartialFields> & proto.CustomJson<UInt64Value, number>

type _Int32ValueImpl = {
	__index: _Int32ValueImpl,
	new: (fields: _Int32ValuePartialFields?) -> Int32Value,
	encode: (self: Int32Value) -> buffer,
	decode: (input: buffer) -> Int32Value,
	jsonEncode: (self: Int32Value) -> number,
	jsonDecode: (input: number) -> Int32Value,
	descriptor: proto.Descriptor,
}

type _Int32ValueFields = {
	value: number,
}

type _Int32ValuePartialFields = {
	value: number?,
}

export type Int32Value = typeof(setmetatable({} :: _Int32ValueFields, {} :: _Int32ValueImpl))
type _Int32ValueMessage = proto.Message<Int32Value, _Int32ValuePartialFields> & proto.CustomJson<Int32Value, number>

type _UInt32ValueImpl = {
	__index: _UInt32ValueImpl,
	new: (fields: _UInt32ValuePartialFields?) -> UInt32Value,
	encode: (self: UInt32Value) -> buffer,
	decode: (input: buffer) -> UInt32Value,
	jsonEncode: (self: UInt32Value) -> number,
	jsonDecode: (input: number) -> UInt32Value,
	descriptor: proto.Descriptor,
}

type _UInt32ValueFields = {
	value: number,
}

type _UInt32ValuePartialFields = {
	value: number?,
}

export type UInt32Value = typeof(setmetatable({} :: _UInt32ValueFields, {} :: _UInt32ValueImpl))
type _UInt32ValueMessage = proto.Message<UInt32Value, _UInt32ValuePartialFields> & proto.CustomJson<UInt32Value, number>

type _BoolValueImpl = {
	__index: _BoolValueImpl,
	new: (fields: _BoolValuePartialFields?) -> BoolValue,
	encode: (self: BoolValue) -> buffer,
	decode: (input: buffer) -> BoolValue,
	jsonEncode: (self: BoolValue) -> boolean,
	jsonDecode: (input: boolean) -> BoolValue,
	descriptor: proto.Descriptor,
}

type _BoolValueFields = {
	value: boolean,
}

type _BoolValuePartialFields = {
	value: boolean?,
}

export type BoolValue = typeof(setmetatable({} :: _BoolValueFields, {} :: _BoolValueImpl))
type _BoolValueMessage = proto.Message<BoolValue, _BoolValuePartialFields> & proto.CustomJson<BoolValue, boolean>

type _StringValueImpl = {
	__index: _StringValueImpl,
	new: (fields: _StringValuePartialFields?) -> StringValue,
	encode: (self: StringValue) -> buffer,
	decode: (input: buffer) -> StringValue,
	jsonEncode: (self: StringValue) -> string,
	jsonDecode: (input: string) -> StringValue,
	descriptor: proto.Descriptor,
}

type _StringValueFields = {
	value: string,
}

type _StringValuePartialFields = {
	value: string?,
}

export type StringValue = typeof(setmetatable({} :: _StringValueFields, {} :: _StringValueImpl))
type _StringValueMessage = proto.Message<StringValue, _StringValuePartialFields> & proto.CustomJson<StringValue, string>

type _BytesValueImpl = {
	__index: _BytesValueImpl,
	new: (fields: _BytesValuePartialFields?) -> BytesValue,
	encode: (self: BytesValue) -> buffer,
	decode: (input: buffer) -> BytesValue,
	jsonEncode: (self: BytesValue) -> string,
	jsonDecode: (input: string) -> BytesValue,
	descriptor: proto.Descriptor,
}

type _BytesValueFields = {
	value: buffer,
}

type _BytesValuePartialFields = {
	value: buffer?,
}

export type BytesValue = typeof(setmetatable({} :: _BytesValueFields, {} :: _BytesValueImpl))
type _BytesValueMessage = proto.Message<BytesValue, _BytesValuePartialFields> & proto.CustomJson<BytesValue, string>

do
	local _DoubleValueImpl = {}
	_DoubleValueImpl.__index = _DoubleValueImpl

	function _DoubleValueImpl.new(data: _DoubleValuePartialFields?): DoubleValue
		return setmetatable({
			value = if data == nil or data.value == nil then 0 else data.value,
		}, _DoubleValueImpl :: _DoubleValueImpl)
	end

	function _DoubleValueImpl.encode(self: DoubleValue): buffer
		local output = buffer.create(0)
		local cursor = 0

		if self.value ~= nil and self.value ~= 0 then
			output, cursor = proto.writeTag(output, cursor, 1, proto.wireTypes.i64)
			output, cursor = proto.writeDouble(output, cursor, self.value)
		end

		local shrunkBuffer = buffer.create(cursor)
		buffer.copy(shrunkBuffer, 0, output, 0, cursor)
		return shrunkBuffer
	end

	function _DoubleValueImpl.decode(input: buffer): DoubleValue
		local self = _DoubleValueImpl.new()
		local cursor = 0

		while cursor < buffer.len(input) do
			local field, wireType
			field, wireType, cursor = proto.readTag(input, cursor)

			if wireType == proto.wireTypes.varint then
				-- No fields

				local _
				_, cursor = proto.readVarInt(input, cursor)
			elseif wireType == proto.wireTypes.lengthDelimited then
				-- No fields

				local length
				length, cursor = proto.readVarInt(input, cursor)

				cursor += length
			elseif wireType == proto.wireTypes.i32 then
				-- No fields

				local _
				_, cursor = proto.readFixed32(input, cursor)
			elseif wireType == proto.wireTypes.i64 then
				if field == 1 then
					local value
					value, cursor = proto.readDouble(input, cursor)
					self.value = value
					continue
				end

				local _
				_, cursor = proto.readFixed64(input, cursor)
			else
				error("Unsupported wire type: " .. wireType)
			end
		end

		return self
	end

	function _DoubleValueImpl.jsonEncode(self: DoubleValue): number
		return self.value
	end

	function _DoubleValueImpl.jsonDecode(anyValue: any): DoubleValue
		local value: number = anyValue
		return _DoubleValueImpl.new({
			value = value,
		})
	end

	_DoubleValueImpl.descriptor = {
		name = "DoubleValue",
		fullName = "google.protobuf.DoubleValue",
	}

	messages.DoubleValue = _DoubleValueImpl :: any -- Luau: Not sure why this intersection fails.

	typeRegistry.default:register(messages.DoubleValue)
end

do
	local _FloatValueImpl = {}
	_FloatValueImpl.__index = _FloatValueImpl

	function _FloatValueImpl.new(data: _FloatValuePartialFields?): FloatValue
		return setmetatable({
			value = if data == nil or data.value == nil then 0 else data.value,
		}, _FloatValueImpl :: _FloatValueImpl)
	end

	function _FloatValueImpl.encode(self: FloatValue): buffer
		local output = buffer.create(0)
		local cursor = 0

		if self.value ~= nil and self.value ~= 0 then
			output, cursor = proto.writeTag(output, cursor, 1, proto.wireTypes.i32)
			output, cursor = proto.writeFloat(output, cursor, self.value)
		end

		local shrunkBuffer = buffer.create(cursor)
		buffer.copy(shrunkBuffer, 0, output, 0, cursor)
		return shrunkBuffer
	end

	function _FloatValueImpl.decode(input: buffer): FloatValue
		local self = _FloatValueImpl.new()
		local cursor = 0

		while cursor < buffer.len(input) do
			local field, wireType
			field, wireType, cursor = proto.readTag(input, cursor)

			if wireType == proto.wireTypes.varint then
				-- No fields

				local _
				_, cursor = proto.readVarInt(input, cursor)
			elseif wireType == proto.wireTypes.lengthDelimited then
				-- No fields

				local length
				length, cursor = proto.readVarInt(input, cursor)

				cursor += length
			elseif wireType == proto.wireTypes.i32 then
				if field == 1 then
					local value
					value, cursor = proto.readFloat(input, cursor)
					self.value = value
					continue
				end

				local _
				_, cursor = proto.readFixed32(input, cursor)
			elseif wireType == proto.wireTypes.i64 then
				-- No fields

				local _
				_, cursor = proto.readFixed64(input, cursor)
			else
				error("Unsupported wire type: " .. wireType)
			end
		end

		return self
	end

	function _FloatValueImpl.jsonEncode(self: FloatValue): number
		return self.value
	end

	function _FloatValueImpl.jsonDecode(anyValue: any): FloatValue
		local value: number = anyValue
		return _FloatValueImpl.new({
			value = value,
		})
	end

	_FloatValueImpl.descriptor = {
		name = "FloatValue",
		fullName = "google.protobuf.FloatValue",
	}

	messages.FloatValue = _FloatValueImpl :: any -- Luau: Not sure why this intersection fails.

	typeRegistry.default:register(messages.FloatValue)
end

do
	local _Int64ValueImpl = {}
	_Int64ValueImpl.__index = _Int64ValueImpl

	function _Int64ValueImpl.new(data: _Int64ValuePartialFields?): Int64Value
		return setmetatable({
			value = if data == nil or data.value == nil then 0 else data.value,
		}, _Int64ValueImpl :: _Int64ValueImpl)
	end

	function _Int64ValueImpl.encode(self: Int64Value): buffer
		local output = buffer.create(0)
		local cursor = 0

		if self.value ~= nil and self.value ~= 0 then
			output, cursor = proto.writeTag(output, cursor, 1, proto.wireTypes.varint)
			output, cursor = proto.writeVarInt(output, cursor, self.value)
		end

		local shrunkBuffer = buffer.create(cursor)
		buffer.copy(shrunkBuffer, 0, output, 0, cursor)
		return shrunkBuffer
	end

	function _Int64ValueImpl.decode(input: buffer): Int64Value
		local self = _Int64ValueImpl.new()
		local cursor = 0

		while cursor < buffer.len(input) do
			local field, wireType
			field, wireType, cursor = proto.readTag(input, cursor)

			if wireType == proto.wireTypes.varint then
				if field == 1 then
					local value
					value, cursor = proto.readVarIntI64(input, cursor)
					self.value = value
					continue
				end

				local _
				_, cursor = proto.readVarInt(input, cursor)
			elseif wireType == proto.wireTypes.lengthDelimited then
				-- No fields

				local length
				length, cursor = proto.readVarInt(input, cursor)

				cursor += length
			elseif wireType == proto.wireTypes.i32 then
				-- No fields

				local _
				_, cursor = proto.readFixed32(input, cursor)
			elseif wireType == proto.wireTypes.i64 then
				-- No fields

				local _
				_, cursor = proto.readFixed64(input, cursor)
			else
				error("Unsupported wire type: " .. wireType)
			end
		end

		return self
	end

	function _Int64ValueImpl.jsonEncode(self: Int64Value): number
		return self.value
	end

	function _Int64ValueImpl.jsonDecode(anyValue: any): Int64Value
		local value: number = anyValue
		return _Int64ValueImpl.new({
			value = value,
		})
	end

	_Int64ValueImpl.descriptor = {
		name = "Int64Value",
		fullName = "google.protobuf.Int64Value",
	}

	messages.Int64Value = _Int64ValueImpl :: any -- Luau: Not sure why this intersection fails.

	typeRegistry.default:register(messages.Int64Value)
end

do
	local _UInt64ValueImpl = {}
	_UInt64ValueImpl.__index = _UInt64ValueImpl

	function _UInt64ValueImpl.new(data: _UInt64ValuePartialFields?): UInt64Value
		return setmetatable({
			value = if data == nil or data.value == nil then 0 else data.value,
		}, _UInt64ValueImpl :: _UInt64ValueImpl)
	end

	function _UInt64ValueImpl.encode(self: UInt64Value): buffer
		local output = buffer.create(0)
		local cursor = 0

		if self.value ~= nil and self.value ~= 0 then
			output, cursor = proto.writeTag(output, cursor, 1, proto.wireTypes.varint)
			output, cursor = proto.writeVarInt(output, cursor, self.value)
		end

		local shrunkBuffer = buffer.create(cursor)
		buffer.copy(shrunkBuffer, 0, output, 0, cursor)
		return shrunkBuffer
	end

	function _UInt64ValueImpl.decode(input: buffer): UInt64Value
		local self = _UInt64ValueImpl.new()
		local cursor = 0

		while cursor < buffer.len(input) do
			local field, wireType
			field, wireType, cursor = proto.readTag(input, cursor)

			if wireType == proto.wireTypes.varint then
				if field == 1 then
					local value
					value, cursor = proto.readVarIntU64(input, cursor)
					self.value = value
					continue
				end

				local _
				_, cursor = proto.readVarInt(input, cursor)
			elseif wireType == proto.wireTypes.lengthDelimited then
				-- No fields

				local length
				length, cursor = proto.readVarInt(input, cursor)

				cursor += length
			elseif wireType == proto.wireTypes.i32 then
				-- No fields

				local _
				_, cursor = proto.readFixed32(input, cursor)
			elseif wireType == proto.wireTypes.i64 then
				-- No fields

				local _
				_, cursor = proto.readFixed64(input, cursor)
			else
				error("Unsupported wire type: " .. wireType)
			end
		end

		return self
	end

	function _UInt64ValueImpl.jsonEncode(self: UInt64Value): number
		return self.value
	end

	function _UInt64ValueImpl.jsonDecode(anyValue: any): UInt64Value
		local value: number = anyValue
		return _UInt64ValueImpl.new({
			value = value,
		})
	end

	_UInt64ValueImpl.descriptor = {
		name = "UInt64Value",
		fullName = "google.protobuf.UInt64Value",
	}

	messages.UInt64Value = _UInt64ValueImpl :: any -- Luau: Not sure why this intersection fails.

	typeRegistry.default:register(messages.UInt64Value)
end

do
	local _Int32ValueImpl = {}
	_Int32ValueImpl.__index = _Int32ValueImpl

	function _Int32ValueImpl.new(data: _Int32ValuePartialFields?): Int32Value
		return setmetatable({
			value = if data == nil or data.value == nil then 0 else data.value,
		}, _Int32ValueImpl :: _Int32ValueImpl)
	end

	function _Int32ValueImpl.encode(self: Int32Value): buffer
		local output = buffer.create(0)
		local cursor = 0

		if self.value ~= nil and self.value ~= 0 then
			output, cursor = proto.writeTag(output, cursor, 1, proto.wireTypes.varint)
			output, cursor = proto.writeVarInt(output, cursor, self.value)
		end

		local shrunkBuffer = buffer.create(cursor)
		buffer.copy(shrunkBuffer, 0, output, 0, cursor)
		return shrunkBuffer
	end

	function _Int32ValueImpl.decode(input: buffer): Int32Value
		local self = _Int32ValueImpl.new()
		local cursor = 0

		while cursor < buffer.len(input) do
			local field, wireType
			field, wireType, cursor = proto.readTag(input, cursor)

			if wireType == proto.wireTypes.varint then
				if field == 1 then
					local value
					value, cursor = proto.readVarIntI32(input, cursor)
					self.value = value
					continue
				end

				local _
				_, cursor = proto.readVarInt(input, cursor)
			elseif wireType == proto.wireTypes.lengthDelimited then
				-- No fields

				local length
				length, cursor = proto.readVarInt(input, cursor)

				cursor += length
			elseif wireType == proto.wireTypes.i32 then
				-- No fields

				local _
				_, cursor = proto.readFixed32(input, cursor)
			elseif wireType == proto.wireTypes.i64 then
				-- No fields

				local _
				_, cursor = proto.readFixed64(input, cursor)
			else
				error("Unsupported wire type: " .. wireType)
			end
		end

		return self
	end

	function _Int32ValueImpl.jsonEncode(self: Int32Value): number
		return self.value
	end

	function _Int32ValueImpl.jsonDecode(anyValue: any): Int32Value
		local value: number = anyValue
		return _Int32ValueImpl.new({
			value = value,
		})
	end

	_Int32ValueImpl.descriptor = {
		name = "Int32Value",
		fullName = "google.protobuf.Int32Value",
	}

	messages.Int32Value = _Int32ValueImpl :: any -- Luau: Not sure why this intersection fails.

	typeRegistry.default:register(messages.Int32Value)
end

do
	local _UInt32ValueImpl = {}
	_UInt32ValueImpl.__index = _UInt32ValueImpl

	function _UInt32ValueImpl.new(data: _UInt32ValuePartialFields?): UInt32Value
		return setmetatable({
			value = if data == nil or data.value == nil then 0 else data.value,
		}, _UInt32ValueImpl :: _UInt32ValueImpl)
	end

	function _UInt32ValueImpl.encode(self: UInt32Value): buffer
		local output = buffer.create(0)
		local cursor = 0

		if self.value ~= nil and self.value ~= 0 then
			output, cursor = proto.writeTag(output, cursor, 1, proto.wireTypes.varint)
			output, cursor = proto.writeVarInt(output, cursor, self.value)
		end

		local shrunkBuffer = buffer.create(cursor)
		buffer.copy(shrunkBuffer, 0, output, 0, cursor)
		return shrunkBuffer
	end

	function _UInt32ValueImpl.decode(input: buffer): UInt32Value
		local self = _UInt32ValueImpl.new()
		local cursor = 0

		while cursor < buffer.len(input) do
			local field, wireType
			field, wireType, cursor = proto.readTag(input, cursor)

			if wireType == proto.wireTypes.varint then
				if field == 1 then
					local value
					value, cursor = proto.readVarIntU32(input, cursor)
					self.value = value
					continue
				end

				local _
				_, cursor = proto.readVarInt(input, cursor)
			elseif wireType == proto.wireTypes.lengthDelimited then
				-- No fields

				local length
				length, cursor = proto.readVarInt(input, cursor)

				cursor += length
			elseif wireType == proto.wireTypes.i32 then
				-- No fields

				local _
				_, cursor = proto.readFixed32(input, cursor)
			elseif wireType == proto.wireTypes.i64 then
				-- No fields

				local _
				_, cursor = proto.readFixed64(input, cursor)
			else
				error("Unsupported wire type: " .. wireType)
			end
		end

		return self
	end

	function _UInt32ValueImpl.jsonEncode(self: UInt32Value): number
		return self.value
	end

	function _UInt32ValueImpl.jsonDecode(anyValue: any): UInt32Value
		local value: number = anyValue
		return _UInt32ValueImpl.new({
			value = value,
		})
	end

	_UInt32ValueImpl.descriptor = {
		name = "UInt32Value",
		fullName = "google.protobuf.UInt32Value",
	}

	messages.UInt32Value = _UInt32ValueImpl :: any -- Luau: Not sure why this intersection fails.

	typeRegistry.default:register(messages.UInt32Value)
end

do
	local _BoolValueImpl = {}
	_BoolValueImpl.__index = _BoolValueImpl

	function _BoolValueImpl.new(data: _BoolValuePartialFields?): BoolValue
		return setmetatable({
			value = if data == nil or data.value == nil then false else data.value,
		}, _BoolValueImpl :: _BoolValueImpl)
	end

	function _BoolValueImpl.encode(self: BoolValue): buffer
		local output = buffer.create(0)
		local cursor = 0

		if self.value then
			output, cursor = proto.writeTag(output, cursor, 1, proto.wireTypes.varint)
			output, cursor = proto.writeVarInt(output, cursor, if self.value then 1 else 0)
		end

		local shrunkBuffer = buffer.create(cursor)
		buffer.copy(shrunkBuffer, 0, output, 0, cursor)
		return shrunkBuffer
	end

	function _BoolValueImpl.decode(input: buffer): BoolValue
		local self = _BoolValueImpl.new()
		local cursor = 0

		while cursor < buffer.len(input) do
			local field, wireType
			field, wireType, cursor = proto.readTag(input, cursor)

			if wireType == proto.wireTypes.varint then
				if field == 1 then
					local value
					value, cursor = proto.readVarInt(input, cursor)
					self.value = value ~= 0
					continue
				end

				local _
				_, cursor = proto.readVarInt(input, cursor)
			elseif wireType == proto.wireTypes.lengthDelimited then
				-- No fields

				local length
				length, cursor = proto.readVarInt(input, cursor)

				cursor += length
			elseif wireType == proto.wireTypes.i32 then
				-- No fields

				local _
				_, cursor = proto.readFixed32(input, cursor)
			elseif wireType == proto.wireTypes.i64 then
				-- No fields

				local _
				_, cursor = proto.readFixed64(input, cursor)
			else
				error("Unsupported wire type: " .. wireType)
			end
		end

		return self
	end

	function _BoolValueImpl.jsonEncode(self: BoolValue): boolean
		return self.value
	end

	function _BoolValueImpl.jsonDecode(anyValue: any): BoolValue
		local value: boolean = anyValue
		return _BoolValueImpl.new({
			value = value,
		})
	end

	_BoolValueImpl.descriptor = {
		name = "BoolValue",
		fullName = "google.protobuf.BoolValue",
	}

	messages.BoolValue = _BoolValueImpl :: any -- Luau: Not sure why this intersection fails.

	typeRegistry.default:register(messages.BoolValue)
end

do
	local _StringValueImpl = {}
	_StringValueImpl.__index = _StringValueImpl

	function _StringValueImpl.new(data: _StringValuePartialFields?): StringValue
		return setmetatable({
			value = if data == nil or data.value == nil then "" else data.value,
		}, _StringValueImpl :: _StringValueImpl)
	end

	function _StringValueImpl.encode(self: StringValue): buffer
		local output = buffer.create(0)
		local cursor = 0

		if self.value ~= nil and self.value ~= "" then
			output, cursor = proto.writeTag(output, cursor, 1, proto.wireTypes.lengthDelimited)
			output, cursor = proto.writeString(output, cursor, self.value)
		end

		local shrunkBuffer = buffer.create(cursor)
		buffer.copy(shrunkBuffer, 0, output, 0, cursor)
		return shrunkBuffer
	end

	function _StringValueImpl.decode(input: buffer): StringValue
		local self = _StringValueImpl.new()
		local cursor = 0

		while cursor < buffer.len(input) do
			local field, wireType
			field, wireType, cursor = proto.readTag(input, cursor)

			if wireType == proto.wireTypes.varint then
				-- No fields

				local _
				_, cursor = proto.readVarInt(input, cursor)
			elseif wireType == proto.wireTypes.lengthDelimited then
				if field == 1 then
					local value
					value, cursor = proto.readBuffer(input, cursor)
					self.value = proto.decodeString(value)
					continue
				end

				local length
				length, cursor = proto.readVarInt(input, cursor)

				cursor += length
			elseif wireType == proto.wireTypes.i32 then
				-- No fields

				local _
				_, cursor = proto.readFixed32(input, cursor)
			elseif wireType == proto.wireTypes.i64 then
				-- No fields

				local _
				_, cursor = proto.readFixed64(input, cursor)
			else
				error("Unsupported wire type: " .. wireType)
			end
		end

		return self
	end

	function _StringValueImpl.jsonEncode(self: StringValue): string
		return self.value
	end

	function _StringValueImpl.jsonDecode(anyValue: any): StringValue
		local value: string = anyValue
		return _StringValueImpl.new({
			value = value,
		})
	end

	_StringValueImpl.descriptor = {
		name = "StringValue",
		fullName = "google.protobuf.StringValue",
	}

	messages.StringValue = _StringValueImpl :: any -- Luau: Not sure why this intersection fails.

	typeRegistry.default:register(messages.StringValue)
end

do
	local _BytesValueImpl = {}
	_BytesValueImpl.__index = _BytesValueImpl

	function _BytesValueImpl.new(data: _BytesValuePartialFields?): BytesValue
		return setmetatable({
			value = if data == nil or data.value == nil then buffer.create(0) else data.value,
		}, _BytesValueImpl :: _BytesValueImpl)
	end

	function _BytesValueImpl.encode(self: BytesValue): buffer
		local output = buffer.create(0)
		local cursor = 0

		if self.value ~= nil and buffer.len(self.value) > 0 then
			output, cursor = proto.writeTag(output, cursor, 1, proto.wireTypes.lengthDelimited)
			output, cursor = proto.writeBuffer(output, cursor, self.value, buffer.len(self.value))
		end

		local shrunkBuffer = buffer.create(cursor)
		buffer.copy(shrunkBuffer, 0, output, 0, cursor)
		return shrunkBuffer
	end

	function _BytesValueImpl.decode(input: buffer): BytesValue
		local self = _BytesValueImpl.new()
		local cursor = 0

		while cursor < buffer.len(input) do
			local field, wireType
			field, wireType, cursor = proto.readTag(input, cursor)

			if wireType == proto.wireTypes.varint then
				-- No fields

				local _
				_, cursor = proto.readVarInt(input, cursor)
			elseif wireType == proto.wireTypes.lengthDelimited then
				if field == 1 then
					local value
					value, cursor = proto.readBuffer(input, cursor)
					self.value = value
					continue
				end

				local length
				length, cursor = proto.readVarInt(input, cursor)

				cursor += length
			elseif wireType == proto.wireTypes.i32 then
				-- No fields

				local _
				_, cursor = proto.readFixed32(input, cursor)
			elseif wireType == proto.wireTypes.i64 then
				-- No fields

				local _
				_, cursor = proto.readFixed64(input, cursor)
			else
				error("Unsupported wire type: " .. wireType)
			end
		end

		return self
	end

	function _BytesValueImpl.jsonEncode(self: BytesValue): string
		return buffer.tostring(self.value)
	end

	function _BytesValueImpl.jsonDecode(anyValue: any): BytesValue
		local value: string = anyValue
		return _BytesValueImpl.new({
			value = buffer.fromstring(value),
		})
	end

	_BytesValueImpl.descriptor = {
		name = "BytesValue",
		fullName = "google.protobuf.BytesValue",
	}

	messages.BytesValue = _BytesValueImpl :: any -- Luau: Not sure why this intersection fails.

	typeRegistry.default:register(messages.BytesValue)
end

return {
	DoubleValue = messages.DoubleValue,
	FloatValue = messages.FloatValue,
	Int64Value = messages.Int64Value,
	UInt64Value = messages.UInt64Value,
	Int32Value = messages.Int32Value,
	UInt32Value = messages.UInt32Value,
	BoolValue = messages.BoolValue,
	StringValue = messages.StringValue,
	BytesValue = messages.BytesValue,
}


--!strict
local serde = require("@lune/serde")

local function isArray(input: { [any]: any })
	local count = 0
	for _ in input do
		count += 1
	end

	return count == #input
end

-- Re-implementation of jsonEncode that guarantees JSON etc are round tripped
local function jsonEncode(input: any): string
	if typeof(input) == "table" then
		if next(input) ~= nil and isArray(input) then
			local items = {}
			for _, value in input do
				table.insert(items, jsonEncode(value))
			end

			return `[{table.concat(items, ",")}]`
		else
			local items = {}
			for key, value in input do
				assert(typeof(key) == "string", "JSON key needs to be a string")
				table.insert(items, `\"{key}\":{jsonEncode(value)}`)
			end

			return `\{{table.concat(items, ",")}\}`
		end
	elseif typeof(input) == "number" or typeof(input) == "boolean" then
		return tostring(input)
	elseif typeof(input) == "string" then
		return serde.encode("json", input)
	else
		error(`Unknown input type: {tostring(input)}`)
	end
end

return jsonEncode


--!strict
-- TODO: This file is NOT RAN! Needs to be fixed in a properly run test file.
local proto = require("@self")

local function assertEquals<T>(x: T, y: T)
	if x ~= y then
		error(`{x} ~= {y}`, 2)
	end
end

local function formatHex(input: string): string
	local bytes = {}

	for index = 1, #input do
		table.insert(bytes, string.format("%02x", string.byte(input:sub(index, index + 1))))
	end

	return table.concat(bytes, " ")
end

local function assertBufferEquals(input: buffer, bufferLength: number, expected: string)
	local shrunkBuffer = buffer.create(bufferLength)
	buffer.copy(shrunkBuffer, 0, input, 0, bufferLength)

	local bufferString = buffer.tostring(shrunkBuffer)

	if bufferString ~= expected then
		error(`Buffers don't match\n{formatHex(bufferString)}\n{formatHex(expected)}`, 2)
	end
end

do
	local number, newCursor = proto.readVarInt(buffer.fromstring("\x96\x01"), 0)
	assertEquals(number, 150)
	assertEquals(newCursor, 2)
end

do
	local output, newCursor = proto.writeVarInt(buffer.create(0), 0, 150)
	assertEquals(newCursor, 2)
	assertBufferEquals(output, newCursor, "\x96\x01")
end

do
	local output, newCursor = proto.writeVarInt(buffer.create(0), 0, -150)
	assertEquals(newCursor, 10)
	assertBufferEquals(output, newCursor, "\xEA\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01")
end

do
	local output, newCursor = proto.writeVarInt(buffer.create(0), 0, -2 ^ 60)
	assertEquals(newCursor, 10)
	assertBufferEquals(output, newCursor, "\x80\x80\x80\x80\x80\x80\x80\x80\xF0\x01")
end

do
	local index, wireType, newCursor = proto.readTag(buffer.fromstring("\x08"), 0)
	assertEquals(index, 1)
	assertEquals(wireType, 0)
	assertEquals(newCursor, 1)
end

do
	local output, newCursor = proto.writeTag(buffer.create(0), 0, 1, proto.wireTypes.varint)
	assertBufferEquals(output, newCursor, "\x08")
	assertEquals(newCursor, 1)
end

do
	local number, newCursor = proto.readVarIntI32(buffer.fromstring("\x9C\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"), 0)
	assertEquals(number, -100)
	assertEquals(newCursor, 10)
end

do
	local number, newCursor = proto.readVarIntI32(buffer.fromstring("\x80\x80\x80\x80\xF8\xFF\xFF\xFF\xFF\x01"), 0)
	assertEquals(number, -2 ^ 31)
	assertEquals(newCursor, 10)
end

do
	local number, newCursor = proto.readVarIntI32(buffer.fromstring("\xFF\xFF\xFF\xFF\x07"), 0)
	assertEquals(number, 2 ^ 31 - 1)
	assertEquals(newCursor, 5)
end

do
	local number, newCursor = proto.readVarIntU32(buffer.fromstring("\x00"), 0)
	assertEquals(number, 0)
	assertEquals(newCursor, 1)
end

do
	local number, newCursor = proto.readVarIntU32(buffer.fromstring("\xFF\xFF\xFF\xFF\x0F"), 0)
	assertEquals(number, 2 ^ 32 - 1)
	assertEquals(newCursor, 5)
end

do
	local number, newCursor = proto.readVarIntI64(buffer.fromstring("\x80\x80\x80\x80\x80\x80\x80\x80\x10"), 0)
	assertEquals(number, 2 ^ 60)
	assertEquals(newCursor, 9)
end

do
	local number, newCursor = proto.readVarIntI64(buffer.fromstring("\x80\x80\x80\x80\x80\x80\x80\x80\xF0\x01"), 0)
	assertEquals(number, -2 ^ 60)
	assertEquals(newCursor, 10)
end

do
	local number, newCursor = proto.readVarIntU64(buffer.fromstring("\x00"), 0)
	assertEquals(number, 0)
	assertEquals(newCursor, 1)
end

do
	local number, newCursor = proto.readVarIntU64(buffer.fromstring("\x80\x80\x80\x80\x80\x80\x80\x80\x10"), 0)
	assertEquals(number, 2 ^ 60)
	assertEquals(newCursor, 9)
end

do
	local output, newCursor = proto.writeVarIntZigZag(buffer.create(0), 0, 0)
	assertBufferEquals(output, newCursor, "\x00")
	assertEquals(newCursor, 1)
end

do
	local output, newCursor = proto.writeVarIntZigZag(buffer.create(0), 0, 2 ^ 60)
	assertBufferEquals(output, newCursor, "\x80\x80\x80\x80\x80\x80\x80\x80\x20")
	assertEquals(newCursor, 9)
end

do
	local output, newCursor = proto.writeVarIntZigZag(buffer.create(0), 0, -2 ^ 60)
	assertBufferEquals(output, newCursor, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x1F")
	assertEquals(newCursor, 9)
end

do
	local number, newCursor = proto.readVarIntS32(buffer.fromstring("\xFE\xFF\xFF\xFF\x0F"), 0)
	assertEquals(number, 2 ^ 31 - 1)
	assertEquals(newCursor, 5)
end

do
	local number, newCursor = proto.readVarIntS32(buffer.fromstring("\xFF\xFF\xFF\xFF\x0F"), 0)
	assertEquals(number, -2 ^ 31)
	assertEquals(newCursor, 5)
end

do
	local number, newCursor = proto.readVarIntS64(buffer.fromstring("\x80\x80\x80\x80\x80\x80\x80\x80\x20"), 0)
	assertEquals(number, 2 ^ 60)
	assertEquals(newCursor, 9)
end

do
	local number, newCursor = proto.readVarIntS64(buffer.fromstring("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x1F"), 0)
	assertEquals(number, -2 ^ 60)
	assertEquals(newCursor, 9)
end

do
	local output, newCursor = proto.writeString(buffer.create(0), 0, "testing")
	assertBufferEquals(output, newCursor, "\x07testing")
	assertEquals(newCursor, 8)
end


function _AnyImpl.jsonEncode(input: Any): { [string]: any }
	local unpacked = input:unpack(typeRegistry.default)
	assert(unpacked ~= nil, "Cannot JSON-encode empty Any")

	local json = unpacked:jsonEncode()

	if typeof(json) == "string" then
		return { ["@type"] = input.type_url, ["value"] = json }
	end

	json["@type"] = input.type_url

	return json
end

function _AnyImpl.jsonDecode(input: { [string]: any }): Any
	local typeUrl = input["@type"]
	local typeName = typeUrl:match("([^/]+)$")
	local payloadType = typeRegistry.default:findMessage(typeName)

	if payloadType == nil then
		error(`Could not find message type \`{typeName}\` for Any`)
	end

	local typePrefix = typeUrl:sub(1, #typeUrl - #typeName - 1)

	local json = table.clone(input)
	-- For WKTs with special JSON representations, we need to decode input["value"] rather than
	-- input.
	if typeName:match("^google%.protobuf%.") and input["value"] ~= nil then
		json = input["value"]
	else
		-- Clear this because it's not part of the JSON encoding of the payload itself.
		json["@type"] = nil
	end

	return _AnyImpl.pack(payloadType.jsonDecode(json) :: proto.Message<unknown, unknown>, typePrefix)
end

type _AnyJsonDecode = typeof(_AnyImpl.jsonDecode) -- Luau bug: It doesn't recognize this for some reason


function _AnyImpl.pack(payload: proto.Message<any, any>, typeUrlPrefix: string): Any
	return _AnyImpl.new({
		type_url = typeUrlPrefix .. "/" .. payload.descriptor.fullName,
		value = payload:encode(),
	})
end

-- Luau: It refuses to believe these are the same unpack, for some reason
local anyUnpack: typeof(({} :: _AnyImpl).unpack) = function(
	self: Any,
	registry: typeRegistry.TypeRegistry?
): proto.Message<any, any>?
	if self.value == nil then
		return nil
	end

	if registry == nil then
		registry = typeRegistry.default
	end
	assert(registry ~= nil, "Luau")

	local typeName = _AnyImpl.typeUrlToTypeName(self.type_url)
	local payloadType = registry:findMessage(typeName)

	if payloadType == nil then
		error(`Unknown type: \`{typeName}\``)
	end

	return payloadType.decode(self.value) :: proto.Message<any, any>
end

_AnyImpl.unpack = anyUnpack

function _AnyImpl.isA(self: Any, typeName: string): boolean
	if self.type_url == typeName then
		return true
	end

	local suffix = "/" .. typeName
	return self.type_url:sub(-#suffix) == suffix
end

function _AnyImpl.typeUrlToTypeName(typeUrl: string): string
	local typeName = typeUrl:match("([^/]+)$")
	if typeName == nil then
		error(`Invalid type URL: \`{typeUrl}\``)
	end
	return typeName
end


function _BytesValueImpl.jsonEncode(self: BytesValue): string
	return buffer.tostring(self.value)
end

function _BytesValueImpl.jsonDecode(anyValue: any): BytesValue
	local value: string = anyValue
	return _BytesValueImpl.new({
		value = buffer.fromstring(value),
	})
end


-- Converts a number of nanos to a string representation.
-- Sign and trailing zeroes are dropped.
-- 500000000 -> ".5s"
-- 500000001 -> ".500000001s"
-- 0 -> ".0s"
-- 1 -> ".000000001s"
-- -1 -> "-.000000001s"
local function serializeFractionalNanos(nanos: number): string
	nanos = nanos * math.sign(nanos)

	local nanosText = tostring(nanos)
	local nanosTextLength = #nanosText

	local leadingZeroes = 9 - nanosTextLength
	local leadingZeroesText = string.rep("0", leadingZeroes)

	-- remove trailing zeroes
	while nanosText:sub(-1) == "0" do
		nanosText = nanosText:sub(1, -2)
	end

	return `.{leadingZeroesText}{nanosText}s`
end

local function deserializeFractionalNanos(nanosText: string): number
	local nanos = assert(tonumber(nanosText), "Invalid nanos string received, couldn't turn into a number")
	for digit = #nanosText + 1, 9 do
		nanos *= 10
	end

	return nanos
end

function _DurationImpl.jsonEncode(duration: Duration): string
	assert(
		duration.seconds == nil or (duration.seconds >= -315576000000 and duration.seconds <= 315576000000),
		"Seconds out of range"
	)
	assert(
		duration.nanos == nil or (duration.nanos >= -999999999 and duration.nanos <= 999999999),
		"Nanoseconds out of range"
	)

	-- TODO: For durations of one second or more, a non-zero value for the nanos field must be of the same sign as the seconds field.
	if duration.nanos == nil or duration.nanos == 0 or duration.nanos % 1e9 == 0 then
		return `{duration.seconds}s`
	end

	local nanoSign = if duration.seconds == 0 and math.sign(duration.nanos) == -1 then "-" else ""
	return string.format("%s%d%s", nanoSign, duration.seconds or 0, serializeFractionalNanos(duration.nanos))
end

function _DurationImpl.jsonDecode(anyValue: any): Duration
	local maxSeconds = 315576000000
	local minSeconds = -315576000000
	local durationString: string = anyValue
	local simpleSecondsText = string.match(durationString, "^([%-0-9]+)s$")
	local seconds = 0
	local nanos = 0
	if simpleSecondsText ~= nil then
		seconds = assert(
			tonumber(simpleSecondsText),
			"Invalid duration string received--was formatted as just having seconds, but wasn't a properly formatted int"
		)
	else
		local secondsText, nanosText = string.match(durationString, "^([%-0-9]+)%.([0-9]+)s$")
		assert(secondsText ~= nil, "Invalid duration string received--seconds provided are invalid")
		assert(nanosText ~= nil, "Invalid duration string received--nanos provided are invalid")

		seconds = assert(tonumber(secondsText), "Invalid duration string received--seconds provided are invalid")
		nanos = deserializeFractionalNanos(nanosText) * (secondsText:sub(1, 1) == "-" and -1 or 1)
	end

	if seconds > maxSeconds then
		error(`Duration seconds cannot exceed {maxSeconds}`)
	elseif seconds < minSeconds then
		error(`Duration seconds cannot be less than {minSeconds}`)
	end

	return _DurationImpl.new({
		seconds = seconds,
		nanos = nanos,
	})
end


function _FieldMaskImpl.jsonEncode(fieldMask: FieldMask): string
	return table.concat(fieldMask.paths, ",")
end

function _FieldMaskImpl.jsonDecode(anyInput: any): FieldMask
	local input: string = anyInput

	local paths = {}
	for path in string.gmatch(input, "[^,]+") do
		table.insert(paths, path)
	end

	return _FieldMaskImpl.new({
		paths = paths,
	})
end


function _ListValueImpl.jsonEncode(list: ListValue): { any }
	local serialized = {}

	for _, value in list.values do
		table.insert(serialized, messages.Value.jsonEncode(value))
	end

	return serialized
end

function _ListValueImpl.jsonDecode(anyValue: any): ListValue
	local input: { any } = anyValue

	local values = {}

	for _, value in input do
		table.insert(values, messages.Value.jsonDecode(value))
	end

	return _ListValueImpl.new({
		values = values,
	})
end


function _StructImpl.jsonEncode(struct: Struct): { [string]: any }
	local serialized = {}

	for key, value in struct.fields do
		serialized[key] = messages.Value.jsonEncode(value)
	end

	return serialized
end

function _StructImpl.jsonDecode(input: { [string]: any }): Struct
	local fields = {}

	for key, serializedValue in input do
		fields[key] = messages.Value.jsonDecode(serializedValue)
	end

	return _StructImpl.new({
		fields = fields,
	})
end


local function serializeFractionalNanos(nanos: number): string
	if nanos % 1e6 == 0 then
		return string.format(".%03d", nanos)
	elseif nanos % 1e3 == 0 then
		return string.format(".%06d", nanos)
	else
		return string.format(".%09d", nanos)
	end
end

local function deserializeFractionalNanos(nanosText: string): number
	local nanos = assert(tonumber(nanosText), "Invalid nanos string received, couldn't turn into a number")
	for digit = #nanosText + 1, 9 do
		nanos += 10 ^ (digit - 1)
	end

	return nanos
end

function _TimestampImpl.jsonEncode(timestamp: Timestamp): string
	if timestamp.seconds > 253402300799 then
		error("Invalid timestamp provided: years after 9999 are not supported")
	end

	if timestamp.seconds < -62135596800 then
		error("Invalid timestamp provided: years before 0001 are not supported")
	end

	local dateInfo = os.date("!*t", timestamp.seconds or 0)
	if dateInfo == nil then
		error("Invalid timestamp provided: exceeded supported timestamp range on this platform")
	end

	return string.format(
		"%04d-%02d-%02dT%02d:%02d:%02d%sZ",
		dateInfo.year,
		dateInfo.month,
		dateInfo.day,
		dateInfo.hour,
		dateInfo.min,
		dateInfo.sec,
		if timestamp.nanos == 0 then "" else serializeFractionalNanos(timestamp.nanos)
	)
end

function _TimestampImpl.jsonDecode(anyValue: any): Timestamp
	local text: string = anyValue

	local year, month, day, hour, min, sec, nanosWithDot =
		string.match(text, "^([0-9]+)-([0-9]+)-([0-9]+)T([0-9]+):([0-9]+):([0-9]+)(.*)Z$")

	if year == nil or month == nil or day == nil or hour == nil or min == nil or sec == nil or nanosWithDot == nil then
		error("Invalid timestamp provided")
	end

	local nanos = 0
	if #nanosWithDot ~= 0 then
		local nanosText = string.match(nanosWithDot, "^%.([0-9]+)$")
		if nanosText == nil then
			error("Invalid nanoseconds provided in timestamp: `" .. nanosWithDot .. "`")
		end

		nanos = deserializeFractionalNanos(nanosText)
	end

	local totalSeconds = os.time({
		year = assert(tonumber(year), "Invalid formatted year"),
		month = assert(tonumber(month), "Invalid formatted month"),
		day = assert(tonumber(day), "Invalid formatted day"),
		hour = assert(tonumber(hour), "Invalid formatted hour"),
		min = assert(tonumber(min), "Invalid formatted min"),
		sec = assert(tonumber(sec), "Invalid formatted sec"),
	})

	return _TimestampImpl.new({
		seconds = totalSeconds,
		nanos = nanos,
	})
end


function _ValueImpl.jsonEncode(input: Value): any
	local kind = input.kind

	if kind == nil or kind.type == "null_value" then
		return nil
	elseif kind.type == "number_value" or kind.type == "string_value" or kind.type == "bool_value" then
		return kind.value
	elseif kind.type == "list_value" then
		return messages.ListValue.jsonEncode(kind.value)
	elseif kind.type == "struct_value" then
		return messages.Struct.jsonEncode(kind.value)
	else
		local exhaustiveMatch: never = kind.type
		return exhaustiveMatch
	end
end

function _ValueImpl.jsonDecode(input: any): Value
	if input == nil then
		return _ValueImpl.new({ kind = { type = "null_value", value = "NULL_VALUE" } })
	elseif typeof(input) == "number" then
		return _ValueImpl.new({ kind = { type = "number_value", value = input } })
	elseif typeof(input) == "string" then
		return _ValueImpl.new({ kind = { type = "string_value", value = input } })
	elseif typeof(input) == "boolean" then
		return _ValueImpl.new({ kind = { type = "bool_value", value = input } })
	elseif typeof(input) == "table" then
		local isArray = true
		local count = 0
		for _ in input do
			count += 1
			if count > #input then
				isArray = false
				break
			end
		end

		isArray = count == #input

		if isArray then
			return _ValueImpl.new({ kind = { type = "list_value", value = messages.ListValue.jsonDecode(input) } })
		else
			return _ValueImpl.new({ kind = { type = "struct_value", value = messages.Struct.jsonDecode(input) } })
		end
	else
		error(`Unknown input type: {typeof(input)}`)
	end
end


--!strict
local tests = require("./tests")

local deeper_include_me2 = require("./samples/deeper/include_me2")
local forwards_compatibility = require("./samples/forwards_compatibility")
local include_me = require("./samples/include_me")
local kitchen_sink = require("./samples/kitchen_sink")
local recursive = require("./samples/recursive")
local same_package = require("./samples/same_package")
local typeRegistry = require("./samples/proto/typeRegistry")

local assertEquals = tests.assertEquals
local assertNotEquals = tests.assertNotEquals
local describe = tests.describe
local it = tests.it

describe("json deserializing", function()
	it("should deserialize and reserialize primitives", function()
		local json = {
			int32 = 1,

			-- These need to be included because JSON output currently includes enum values.
			aliasedEnum = 0,
			enum = 0,
			nestedEnum = 0,
			enum2 = 0,
		}

		local kitchenSink = kitchen_sink.KitchenSink.jsonDecode(json)

		assertEquals(kitchenSink.int32, 1)
		assertEquals(kitchenSink:jsonEncode(), json)
	end)

	it("should deserialize recursive protos", function()
		local json = {
			b = {
				a = {
					number = 100,
				},
			},
		}

		local decoded = recursive.A.jsonDecode(json)
		assert(decoded.b ~= nil, "No decoded.b")
		assert(decoded.b.a ~= nil, "No decoded.b.a")

		assertEquals(decoded.b.a.number, 100)
	end)
end)

describe("descriptors can return", function()
	it("full message name", function()
		assertEquals(kitchen_sink.KitchenSink.new().descriptor.fullName, "my.package.KitchenSink")
	end)

	it("unqualified message name", function()
		assertEquals(kitchen_sink.KitchenSink.new().descriptor.name, "KitchenSink")
	end)
end)

describe("type registry", function()
	describe("created ad hoc", function()
		it("should find messages by name", function()
			local registry = typeRegistry.TypeRegistry.new({
				kitchen_sink.KitchenSink,
			})

			assertNotEquals(registry:findMessage("my.package.KitchenSink"), nil)

			-- Timestamp is not in the registry.
			assertEquals(registry:findMessage("google.protobuf.Timestamp"), nil)
		end)
	end)

	describe("default registry", function()
		it("should find messages by name", function()
			assertNotEquals(typeRegistry.default:findMessage("my.package.KitchenSink"), nil)
		end)
	end)
end)

describe("binary encoding", function()
	it("should encode and decode a message with a single field", function()
		local kitchenSink = kitchen_sink.KitchenSink.new({
			int32 = 1,
		})

		local encoded = kitchenSink:encode()
		local decoded = kitchen_sink.KitchenSink.decode(encoded)

		assertEquals(decoded.int32, 1)
	end)

	it("should encode and decode a complete message", function()
		local kitchenSink = kitchen_sink.KitchenSink.new({
			int32 = 1,
			optionalInt32 = 2,
			oneofValue = { type = "y", value = "hello" },
			enum = "Two",
			enum2 = "One",
			ints = { 1, 2, 3 },
			map = { [true] = "true", [false] = "false" },
			nestedMessage = kitchen_sink.KitchenSink_NestedMessage.new({ x = 7 }),
			includedMessage = include_me.IncludedMessage.new({ x = 8 }),
			superNestedMessage = include_me.IncludedMessage_SuperNestedMessage.new({ y = 9 }),
			nestedEnum = "Zero",
			float = 1.25,
			double = 2.5,
			samePackageNestedMessage = same_package.SamePackageNestedMessage.new({ x = 42 }),
			deeperNestedMessage = deeper_include_me2.DeeperNestedMessage.new({ x = 1, y = "One" }),

			messages = {
				kitchen_sink.KitchenSink_NestedMessage.new({ x = 1 }),
				kitchen_sink.KitchenSink_NestedMessage.new({ x = 2 }),
			},

			sint32 = 1,
			sint64 = 2,

			fixed32 = 1,
			fixed64 = 2,

			sfixed32 = 1,

			-- sfixed64 isn't yet supported by this protoc plugin.
			-- sfixed64 = 2,

			floats = { 1.25, 2.5 },

			mapWithSetup = { [1] = 1, [2] = 4, [3] = 9 },
			mapWithEnums = { [1] = "One", [2] = "Two" },

			aliasedEnum = "AliasedEnum_One",

			NotCamelCase = 11,

			strings = { "a", "b", "c" },
			bools = { true, false, true },

			customJson = "foo",

			bytes = buffer.fromstring("hello"),
		})

		local encoded = kitchenSink:encode()
		local decoded = kitchen_sink.KitchenSink.decode(encoded)

		assertEquals(decoded, kitchenSink)
	end)
end)

it("should accept fields passed to new", function()
	local kitchenSink = kitchen_sink.KitchenSink.new({
		int32 = 100,
	})

	assertEquals(kitchenSink.int32, 100)
	assertEquals(kitchenSink.float, 0)
end)

describe("forwards compatibility", function()
	local twoOfEachBuffer = forwards_compatibility.TwoOfEach
		.new({
			int32 = 100,
			fixed32 = 100,
			fixed64 = 100,
			repeated_int32 = { 100, 200, 300 },
			int32_2 = 100,
			fixed32_2 = 100,
			fixed64_2 = 100,
			repeated_int32_2 = { 100, 200, 300 },
			int32_3 = 100,
		})
		:encode()

	it("should support unused fields", function()
		local oneOfEach = forwards_compatibility.OneOfEach.decode(twoOfEachBuffer)

		assertEquals(oneOfEach.int32, 100)
		assertEquals(oneOfEach.fixed32, 100)
		assertEquals(oneOfEach.fixed64, 100)
		assertEquals(oneOfEach.repeated_int32, { 100, 200, 300 })
	end)

	it("should support unused wire types", function()
		local justInt = forwards_compatibility.JustInt.decode(twoOfEachBuffer)

		assertEquals(justInt.int32, 100)
	end)
end)

tests.finish()


--!strict
-- Validates that generated protos preserve field names as-is when no field_name_case option is passed.
local tests = require("./tests")
local field_case_test = require("./samples/field_case_test")
local assertEquals = tests.assertEquals
local describe = tests.describe
local it = tests.it

describe("field_name_case preserve (default)", function()
	it("should accept and expose fields with original names", function()
		local msg = field_case_test.FieldCaseTest.new({
			string_value = "hello",
			other_field = 42,
			NON_SNAKE_CASE_FIELD = true,
		})
		assertEquals(msg.string_value, "hello")
		assertEquals(msg.other_field, 42)
		assertEquals(msg.NON_SNAKE_CASE_FIELD, true)
	end)

	it("should round-trip encode and decode with preserved field names", function()
		local msg = field_case_test.FieldCaseTest.new({
			string_value = "test",
			other_field = 1,
			NON_SNAKE_CASE_FIELD = false,
		})
		local encoded = msg:encode()
		local decoded = field_case_test.FieldCaseTest.decode(encoded)
		assertEquals(decoded.string_value, "test")
		assertEquals(decoded.other_field, 1)
		assertEquals(decoded.NON_SNAKE_CASE_FIELD, false)
	end)
end)

tests.finish()


--!strict
-- Validates that generated protos use camelCase field names (field_name_case=camel).
local tests = require("./tests")
local field_case_test = require("./samples/field_case_test_camel/field_case_test")
local assertEquals = tests.assertEquals
local describe = tests.describe
local it = tests.it

describe("field_name_case camel", function()
	it("should accept and expose fields via camelCase", function()
		local msg = field_case_test.FieldCaseTest.new({
			stringValue = "hello",
			otherField = 42,
			nonSnakeCaseField = true,
		})
		assertEquals(msg.stringValue, "hello")
		assertEquals(msg.otherField, 42)
		assertEquals(msg.nonSnakeCaseField, true)
	end)

	it("should round-trip encode and decode with camelCase fields", function()
		local msg = field_case_test.FieldCaseTest.new({
			stringValue = "test",
			otherField = 1,
			nonSnakeCaseField = false,
		})
		local encoded = msg:encode()
		local decoded = field_case_test.FieldCaseTest.decode(encoded)
		assertEquals(decoded.stringValue, "test")
		assertEquals(decoded.otherField, 1)
		assertEquals(decoded.nonSnakeCaseField, false)
	end)
end)

tests.finish()


--!strict
-- Validates that generated protos use normalized snake_case (field_name_case=snake).
local tests = require("./tests")
local field_case_test = require("./samples/field_case_test_snake/field_case_test")
local assertEquals = tests.assertEquals
local describe = tests.describe
local it = tests.it

describe("field_name_case snake", function()
	it("should accept and expose fields via normalized snake_case", function()
		local msg = field_case_test.FieldCaseTest.new({
			string_value = "hello",
			other_field = 42,
			non_snake_case_field = true,
		})
		assertEquals(msg.string_value, "hello")
		assertEquals(msg.other_field, 42)
		assertEquals(msg.non_snake_case_field, true)
	end)

	it("should round-trip encode and decode with snake_case fields", function()
		local msg = field_case_test.FieldCaseTest.new({
			string_value = "test",
			other_field = 1,
			non_snake_case_field = false,
		})
		local encoded = msg:encode()
		local decoded = field_case_test.FieldCaseTest.decode(encoded)
		assertEquals(decoded.string_value, "test")
		assertEquals(decoded.other_field, 1)
		assertEquals(decoded.non_snake_case_field, false)
	end)
end)

tests.finish()