mirror of
https://github.com/lukaszraczylo/go-telegram.git
synced 2026-10-12 00:45:02 +00:00
The generator gated all union decoder emission behind knownDiscriminators, a hand-curated map of 13 entries. Any sealed union absent from that map received no decoder, so decoding must fail. Bot API v10 added RichBlock and RichText, which were absent. Roughly 8.6 percent of Telegram updates failed with HTTP 400. The generator now derives discriminators from the specification instead of a fixed list. A gate check fails the build when a union resolves no discriminator. Eleven unions gain decoders that never had one: BotCommandScope, InputMedia, InputPaidMedia, InputPollMedia, InputPollOptionMedia, InputProfilePhoto, InputRichBlock, InputStoryContent, PassportElementError, RichBlock and RichText. RichText can also appear as a bare string or an array of RichText. The scraper now extracts these alternate shapes into the intermediate representation. The emitter adds RichTextPlain and RichTextSequence. The decoder dispatches on the first JSON byte before it reads any discriminator. Unions without alternates must regenerate byte for byte. The golden test proves this. Claude-Session: https://claude.ai/code/session_01Fu2wXqGUrfVBi5aLafKk8U
203 lines
6.8 KiB
Cheetah
203 lines
6.8 KiB
Cheetah
// Code generated by cmd/genapi. DO NOT EDIT.
|
|
|
|
//go:build !ignore_autogenerated
|
|
|
|
// Package api contains the Telegram Bot API object types and method
|
|
// wrappers, generated from the live documentation by cmd/genapi.
|
|
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"github.com/goccy/go-json"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
var _ = io.Discard // keep import even if no fields use io
|
|
var _ = json.Marshal // keep import for UnmarshalXxx helpers
|
|
var _ = fmt.Errorf // keep import for UnmarshalXxx helpers
|
|
var _ = bytes.TrimLeft // keep import for shape-dispatching UnmarshalXxx helpers (unions with alternates)
|
|
|
|
{{range .Types}}
|
|
{{- $td := . -}}
|
|
{{if .OneOf}}
|
|
// {{.Name}} is a union type. The following concrete variants implement
|
|
// it:
|
|
{{range .OneOf}}// - {{.}}
|
|
{{end}}{{range unionAlternates .}}// - {{.Name}} ({{if .IsString}}bare JSON string{{else}}JSON array of {{$td.Name}}{{end}})
|
|
{{end}}//
|
|
{{docComment .Doc -}}
|
|
type {{.Name}} interface{ is{{.Name}}() }
|
|
|
|
{{range .OneOf}}
|
|
// is{{$td.Name}} is the marker method that makes {{.}} implement {{$td.Name}}.
|
|
func (*{{.}}) is{{$td.Name}}() {}
|
|
{{end}}
|
|
{{range unionAlternates .}}
|
|
// {{.Name}} implements {{$td.Name}} for the {{if .IsString}}bare JSON string{{else}}JSON array of {{$td.Name}}{{end}} wire
|
|
// shape Telegram documents for this union, alongside its object variants.
|
|
type {{.Name}} struct {
|
|
{{if .IsString}} Text string
|
|
{{else}} Items []{{$td.Name}}
|
|
{{end}}}
|
|
|
|
// is{{$td.Name}} is the marker method that makes {{.Name}} implement {{$td.Name}}.
|
|
func (*{{.Name}}) is{{$td.Name}}() {}
|
|
|
|
{{if .IsString}}// MarshalJSON encodes {{.Name}} back to its bare JSON string wire shape.
|
|
func (v *{{.Name}}) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(v.Text)
|
|
}
|
|
{{else}}// MarshalJSON encodes {{.Name}} back to its bare JSON array wire shape.
|
|
func (v *{{.Name}}) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(v.Items)
|
|
}
|
|
{{end}}
|
|
{{end}}
|
|
{{if hasDiscriminator .Name}}
|
|
{{$alts := unionAlternates .}}{{if $alts}}// Unmarshal{{.Name}} decodes a {{.Name}} from JSON. Telegram documents
|
|
// this type as either a bare JSON string, a JSON array of {{.Name}}, or
|
|
// an object dispatched by its "{{discriminatorField .Name}}" field;
|
|
// leading whitespace is trimmed and the first remaining byte selects
|
|
// the shape.
|
|
{{else}}// Unmarshal{{.Name}} decodes a {{.Name}} from JSON by inspecting the
|
|
// "{{discriminatorField .Name}}" field and dispatching to the correct concrete type.
|
|
{{end}}func Unmarshal{{.Name}}(data []byte) ({{.Name}}, error) {
|
|
{{if $alts}} trimmed := bytes.TrimLeft(data, " \t\r\n")
|
|
if len(trimmed) == 0 {
|
|
return nil, fmt.Errorf("{{.Name}}: empty JSON value")
|
|
}
|
|
switch trimmed[0] {
|
|
{{range $alts}}{{if .IsString}} case '"':
|
|
var s string
|
|
if err := json.Unmarshal(data, &s); err != nil {
|
|
return nil, fmt.Errorf("{{$td.Name}}: decoding string: %w", err)
|
|
}
|
|
return &{{.Name}}{Text: s}, nil
|
|
{{else}} case '[':
|
|
var raws []json.RawMessage
|
|
if err := json.Unmarshal(data, &raws); err != nil {
|
|
return nil, fmt.Errorf("{{$td.Name}}: decoding array: %w", err)
|
|
}
|
|
items := make([]{{$td.Name}}, 0, len(raws))
|
|
for i, r := range raws {
|
|
v, err := Unmarshal{{$td.Name}}(r)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("{{$td.Name}}: decoding array[%d]: %w", i, err)
|
|
}
|
|
items = append(items, v)
|
|
}
|
|
return &{{.Name}}{Items: items}, nil
|
|
{{end}}{{end}} case '{':
|
|
// fall through to the discriminator switch below.
|
|
default:
|
|
return nil, fmt.Errorf("{{.Name}}: unrecognised JSON value starting with %q", trimmed[0])
|
|
}
|
|
{{end}} var probe struct {
|
|
V string `json:"{{discriminatorField .Name}}"`
|
|
}
|
|
if err := json.Unmarshal(data, &probe); err != nil {
|
|
return nil, err
|
|
}
|
|
var v {{.Name}}
|
|
switch probe.V {
|
|
{{range $val, $typ := discriminatorMap .Name}} case {{printf "%q" $val}}:
|
|
v = &{{$typ}}{}
|
|
{{end}} default:
|
|
return nil, fmt.Errorf("{{.Name}}: unknown {{discriminatorField .Name}} %q", probe.V)
|
|
}
|
|
if err := json.Unmarshal(data, v); err != nil {
|
|
return nil, err
|
|
}
|
|
return v, nil
|
|
}
|
|
{{end}}
|
|
{{if isMaybeInaccessibleMessage .Name}}
|
|
// UnmarshalMaybeInaccessibleMessage decodes a JSON object into the correct
|
|
// MaybeInaccessibleMessage variant. Telegram uses the date field as a
|
|
// discriminator: date == 0 indicates InaccessibleMessage; any other value
|
|
// indicates a real Message.
|
|
func UnmarshalMaybeInaccessibleMessage(data []byte) (MaybeInaccessibleMessage, error) {
|
|
var probe struct {
|
|
Date int64 `json:"date"`
|
|
}
|
|
if err := json.Unmarshal(data, &probe); err != nil {
|
|
return nil, fmt.Errorf("MaybeInaccessibleMessage: %w", err)
|
|
}
|
|
if probe.Date == 0 {
|
|
v := &InaccessibleMessage{}
|
|
if err := json.Unmarshal(data, v); err != nil {
|
|
return nil, fmt.Errorf("InaccessibleMessage: %w", err)
|
|
}
|
|
return v, nil
|
|
}
|
|
v := &Message{}
|
|
if err := json.Unmarshal(data, v); err != nil {
|
|
return nil, fmt.Errorf("Message: %w", err)
|
|
}
|
|
return v, nil
|
|
}
|
|
{{end}}
|
|
{{else}}
|
|
{{docComment .Doc -}}
|
|
type {{.Name}} struct {
|
|
{{range .Fields}}{{docComment .Doc}}{{goField $td.Name .}}
|
|
{{end}}}
|
|
{{if variantHasDisc .Name}}
|
|
// MarshalJSON encodes {{.Name}} with the discriminator field
|
|
// "{{variantDiscField .Name}}" forced to {{printf "%q" (variantDiscValue .Name)}}.
|
|
// The hardcoded value frees callers from setting {{variantDiscGoField .Name}} by hand —
|
|
// any user-supplied value on the struct literal is overridden so a typo
|
|
// can't slip through to Telegram.
|
|
func (v *{{.Name}}) MarshalJSON() ([]byte, error) {
|
|
type alias {{.Name}}
|
|
return json.Marshal(&struct {
|
|
{{variantDiscGoField .Name}} string `json:"{{variantDiscField .Name}}"`
|
|
*alias
|
|
}{
|
|
{{variantDiscGoField .Name}}: {{printf "%q" (variantDiscValue .Name)}},
|
|
alias: (*alias)(v),
|
|
})
|
|
}
|
|
{{end}}
|
|
{{$unionFields := unionFields .}}{{if $unionFields}}
|
|
// UnmarshalJSON decodes {{.Name}} by dispatching union-typed fields
|
|
// ({{range $i, $u := $unionFields}}{{if $i}}, {{end}}{{$u.Field.Name}}{{end}}) through their concrete UnmarshalXxx helpers.
|
|
func (m *{{.Name}}) UnmarshalJSON(data []byte) error {
|
|
type Alias {{.Name}}
|
|
aux := &struct {
|
|
{{range $unionFields}}{{.Field.Name}} json.RawMessage `json:"{{.Field.JSONName}},omitempty"`
|
|
{{end}}*Alias
|
|
}{Alias: (*Alias)(m)}
|
|
if err := json.Unmarshal(data, aux); err != nil {
|
|
return err
|
|
}
|
|
{{range $unionFields}}{{$f := .Field}}{{$u := .UnionName}}
|
|
if len(aux.{{$f.Name}}) > 0 && string(aux.{{$f.Name}}) != "null" {
|
|
{{if isArrayUnion $f.Type}}var raws []json.RawMessage
|
|
if err := json.Unmarshal(aux.{{$f.Name}}, &raws); err != nil {
|
|
return fmt.Errorf("decoding {{$f.JSONName}}: %w", err)
|
|
}
|
|
decoded := make([]{{$u}}, 0, len(raws))
|
|
for i, r := range raws {
|
|
v, err := Unmarshal{{$u}}(r)
|
|
if err != nil {
|
|
return fmt.Errorf("decoding {{$f.JSONName}}[%d]: %w", i, err)
|
|
}
|
|
decoded = append(decoded, v)
|
|
}
|
|
m.{{$f.Name}} = decoded
|
|
{{else}}v, err := Unmarshal{{$u}}(aux.{{$f.Name}})
|
|
if err != nil {
|
|
return fmt.Errorf("decoding {{$f.JSONName}}: %w", err)
|
|
}
|
|
m.{{$f.Name}} = v
|
|
{{end}}
|
|
}
|
|
{{end}}
|
|
return nil
|
|
}
|
|
{{end}}
|
|
{{end}}
|
|
{{end}}
|