package generatedtext import ( "bytes" "encoding/json" "fmt" "io" "strings" ) var generatedTextFieldNames = map[string]struct{}{ "summary": {}, "forecast_discussion": {}, "precipitation_timing": {}, } func decodeGeneratedText[T any](data []byte, name string) (T, error) { var value T if err := validateGeneratedTextSize(data, name); err != nil { return value, err } if err := validateGeneratedTextObject(data, name); err != nil { return value, err } decoder := json.NewDecoder(bytes.NewReader(data)) decoder.DisallowUnknownFields() if err := decoder.Decode(&value); err != nil { return value, fmt.Errorf("decode %s generated text: invalid field value", name) } var extra any if err := decoder.Decode(&extra); err != nil { if err != io.EOF { return value, fmt.Errorf("decode %s generated text: invalid JSON", name) } return value, nil } return value, fmt.Errorf("decode %s generated text: multiple JSON values", name) } func validateGeneratedTextObject(data []byte, name string) error { decoder := json.NewDecoder(bytes.NewReader(data)) token, err := decoder.Token() if err != nil { return fmt.Errorf("decode %s generated text: invalid JSON", name) } if delimiter, ok := token.(json.Delim); !ok || delimiter != '{' { return fmt.Errorf("decode %s generated text: expected JSON object", name) } seen := make(map[string]struct{}, len(generatedTextFieldNames)) totalCharacters := 0 for decoder.More() { token, err := decoder.Token() if err != nil { return fmt.Errorf("decode %s generated text: invalid JSON", name) } field, ok := token.(string) if !ok { return fmt.Errorf("decode %s generated text: expected object field name", name) } if _, ok := generatedTextFieldNames[field]; !ok { return fmt.Errorf("decode %s generated text: unsupported field", name) } if _, ok := seen[field]; ok { return fmt.Errorf("decode %s generated text: duplicate %s field", name, generatedTextFieldLabel(field)) } seen[field] = struct{}{} var value json.RawMessage if err := decoder.Decode(&value); err != nil { return fmt.Errorf("decode %s generated text: invalid JSON", name) } if err := validateGeneratedTextField(name, field, value, &totalCharacters); err != nil { return err } } if _, err := decoder.Token(); err != nil { return fmt.Errorf("decode %s generated text: invalid JSON", name) } return nil } func requireGeneratedTextStringField(data []byte, name, field string) error { var fields map[string]json.RawMessage if err := json.Unmarshal(data, &fields); err != nil { return fmt.Errorf("decode %s generated text: invalid JSON", name) } _, ok := fields[field] if !ok { return fmt.Errorf("%s generated text %s is required", name, strings.ReplaceAll(field, "_", " ")) } return nil }