bridgev2/portal: allow access to private methods

This commit is contained in:
Tulir Asokan 2024-07-30 16:29:10 +03:00
commit 27449f2562
3 changed files with 442 additions and 16 deletions

View file

@ -2153,7 +2153,7 @@ func (portal *Portal) ProcessChatInfoChange(ctx context.Context, sender EventSen
portal.UpdateInfo(ctx, change.ChatInfo, source, intent, ts)
}
if change.MemberChanges != nil {
err := portal.SyncParticipants(ctx, change.MemberChanges, source, intent, ts)
err := portal.syncParticipants(ctx, change.MemberChanges, source, intent, ts)
if err != nil {
zerolog.Ctx(ctx).Err(err).Msg("Failed to sync room members")
}
@ -2274,7 +2274,7 @@ type UserLocalPortalInfo struct {
Tag *event.RoomTag
}
func (portal *Portal) UpdateName(ctx context.Context, name string, sender MatrixAPI, ts time.Time) bool {
func (portal *Portal) updateName(ctx context.Context, name string, sender MatrixAPI, ts time.Time) bool {
if portal.Name == name && (portal.NameSet || portal.MXID == "") {
return false
}
@ -2283,7 +2283,7 @@ func (portal *Portal) UpdateName(ctx context.Context, name string, sender Matrix
return true
}
func (portal *Portal) UpdateTopic(ctx context.Context, topic string, sender MatrixAPI, ts time.Time) bool {
func (portal *Portal) updateTopic(ctx context.Context, topic string, sender MatrixAPI, ts time.Time) bool {
if portal.Topic == topic && (portal.TopicSet || portal.MXID == "") {
return false
}
@ -2292,7 +2292,7 @@ func (portal *Portal) UpdateTopic(ctx context.Context, topic string, sender Matr
return true
}
func (portal *Portal) UpdateAvatar(ctx context.Context, avatar *Avatar, sender MatrixAPI, ts time.Time) bool {
func (portal *Portal) updateAvatar(ctx context.Context, avatar *Avatar, sender MatrixAPI, ts time.Time) bool {
if portal.AvatarID == avatar.ID && (portal.AvatarSet || portal.MXID == "") {
return false
}
@ -2407,7 +2407,7 @@ func (portal *Portal) sendRoomMeta(ctx context.Context, sender MatrixAPI, ts tim
return true
}
func (portal *Portal) GetInitialMemberList(ctx context.Context, members *ChatMemberList, source *UserLogin, pl *event.PowerLevelsEventContent) (invite, functional []id.UserID, err error) {
func (portal *Portal) getInitialMemberList(ctx context.Context, members *ChatMemberList, source *UserLogin, pl *event.PowerLevelsEventContent) (invite, functional []id.UserID, err error) {
if members == nil {
invite = []id.UserID{source.UserMXID}
return
@ -2480,7 +2480,7 @@ func (portal *Portal) updateOtherUser(ctx context.Context, members *ChatMemberLi
return false
}
func (portal *Portal) SyncParticipants(ctx context.Context, members *ChatMemberList, source *UserLogin, sender MatrixAPI, ts time.Time) error {
func (portal *Portal) syncParticipants(ctx context.Context, members *ChatMemberList, source *UserLogin, sender MatrixAPI, ts time.Time) error {
var loginsInPortal []*UserLogin
var err error
if members.CheckAllLogins {
@ -2721,7 +2721,7 @@ func (portal *Portal) UpdateDisappearingSetting(ctx context.Context, setting dat
return true
}
func (portal *Portal) UpdateParent(ctx context.Context, newParent networkid.PortalID, source *UserLogin) bool {
func (portal *Portal) updateParent(ctx context.Context, newParent networkid.PortalID, source *UserLogin) bool {
if portal.ParentID == newParent {
return false
}
@ -2773,8 +2773,8 @@ func (portal *Portal) UpdateInfoFromGhost(ctx context.Context, ghost *Ghost) (ch
return
}
}
changed = portal.UpdateName(ctx, ghost.Name, nil, time.Time{}) || changed
changed = portal.UpdateAvatar(ctx, &Avatar{
changed = portal.updateName(ctx, ghost.Name, nil, time.Time{}) || changed
changed = portal.updateAvatar(ctx, &Avatar{
ID: ghost.AvatarID,
MXC: ghost.AvatarMXC,
Hash: ghost.AvatarHash,
@ -2787,28 +2787,28 @@ func (portal *Portal) UpdateInfo(ctx context.Context, info *ChatInfo, source *Us
changed := false
if info.Name != nil {
portal.NameIsCustom = true
changed = portal.UpdateName(ctx, *info.Name, sender, ts) || changed
changed = portal.updateName(ctx, *info.Name, sender, ts) || changed
}
if info.Topic != nil {
changed = portal.UpdateTopic(ctx, *info.Topic, sender, ts) || changed
changed = portal.updateTopic(ctx, *info.Topic, sender, ts) || changed
}
if info.Avatar != nil {
portal.NameIsCustom = true
changed = portal.UpdateAvatar(ctx, info.Avatar, sender, ts) || changed
changed = portal.updateAvatar(ctx, info.Avatar, sender, ts) || changed
}
changed = portal.UpdateInfoFromGhost(ctx, nil) || changed
if info.Disappear != nil {
changed = portal.UpdateDisappearingSetting(ctx, *info.Disappear, sender, ts, false, false) || changed
}
if info.ParentID != nil {
changed = portal.UpdateParent(ctx, *info.ParentID, source) || changed
changed = portal.updateParent(ctx, *info.ParentID, source) || changed
}
if info.JoinRule != nil {
// TODO change detection instead of spamming this every time?
portal.sendRoomMeta(ctx, sender, ts, event.StateJoinRules, "", info.JoinRule)
}
if info.Members != nil && portal.MXID != "" && source != nil {
err := portal.SyncParticipants(ctx, info.Members, source, nil, time.Time{})
err := portal.syncParticipants(ctx, info.Members, source, nil, time.Time{})
if err != nil {
zerolog.Ctx(ctx).Err(err).Msg("Failed to sync room members")
}
@ -2903,7 +2903,7 @@ func (portal *Portal) createMatrixRoomInLoop(ctx context.Context, source *UserLo
},
Users: map[id.UserID]int{},
}
initialMembers, extraFunctionalMembers, err := portal.GetInitialMemberList(ctx, info.Members, source, powerLevels)
initialMembers, extraFunctionalMembers, err := portal.getInitialMemberList(ctx, info.Members, source, powerLevels)
if err != nil {
log.Err(err).Msg("Failed to process participant list for portal creation")
return err
@ -3026,7 +3026,7 @@ func (portal *Portal) createMatrixRoomInLoop(ctx context.Context, source *UserLo
}
}
} else {
err = portal.SyncParticipants(ctx, info.Members, source, nil, time.Time{})
err = portal.syncParticipants(ctx, info.Members, source, nil, time.Time{})
if err != nil {
log.Err(err).Msg("Failed to sync participants after room creation")
}

266
bridgev2/portalinternal.go Normal file
View file

@ -0,0 +1,266 @@
// GENERATED BY portalinternal_generate.go; DO NOT EDIT
//go:generate go run portalinternal_generate.go
//go:generate goimports -w portalinternal.go
package bridgev2
import (
"context"
"time"
"github.com/rs/zerolog"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/bridgev2/database"
"maunium.net/go/mautrix/bridgev2/networkid"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
type PortalInternals Portal
// Deprecated: portal internals should be used carefully and only when necessary.
func (portal *Portal) Internal() *PortalInternals {
return (*PortalInternals)(portal)
}
func (portal *PortalInternals) UpdateLogger() {
(*Portal)(portal).updateLogger()
}
func (portal *PortalInternals) QueueEvent(ctx context.Context, evt portalEvent) {
(*Portal)(portal).queueEvent(ctx, evt)
}
func (portal *PortalInternals) EventLoop() {
(*Portal)(portal).eventLoop()
}
func (portal *PortalInternals) HandleCreateEvent(evt *portalCreateEvent) {
(*Portal)(portal).handleCreateEvent(evt)
}
func (portal *PortalInternals) SendSuccessStatus(ctx context.Context, evt *event.Event) {
(*Portal)(portal).sendSuccessStatus(ctx, evt)
}
func (portal *PortalInternals) SendErrorStatus(ctx context.Context, evt *event.Event, err error) {
(*Portal)(portal).sendErrorStatus(ctx, evt, err)
}
func (portal *PortalInternals) HandleMatrixEvent(sender *User, evt *event.Event) {
(*Portal)(portal).handleMatrixEvent(sender, evt)
}
func (portal *PortalInternals) HandleMatrixReceipts(ctx context.Context, evt *event.Event) {
(*Portal)(portal).handleMatrixReceipts(ctx, evt)
}
func (portal *PortalInternals) HandleMatrixReadReceipt(ctx context.Context, user *User, eventID id.EventID, receipt event.ReadReceipt) {
(*Portal)(portal).handleMatrixReadReceipt(ctx, user, eventID, receipt)
}
func (portal *PortalInternals) HandleMatrixTyping(ctx context.Context, evt *event.Event) {
(*Portal)(portal).handleMatrixTyping(ctx, evt)
}
func (portal *PortalInternals) SendTypings(ctx context.Context, userIDs []id.UserID, typing bool) {
(*Portal)(portal).sendTypings(ctx, userIDs, typing)
}
func (portal *PortalInternals) PeriodicTypingUpdater() {
(*Portal)(portal).periodicTypingUpdater()
}
func (portal *PortalInternals) CheckMessageContentCaps(ctx context.Context, caps *NetworkRoomCapabilities, content *event.MessageEventContent, evt *event.Event) bool {
return (*Portal)(portal).checkMessageContentCaps(ctx, caps, content, evt)
}
func (portal *PortalInternals) HandleMatrixMessage(ctx context.Context, sender *UserLogin, origSender *OrigSender, evt *event.Event) {
(*Portal)(portal).handleMatrixMessage(ctx, sender, origSender, evt)
}
func (portal *PortalInternals) HandleMatrixEdit(ctx context.Context, sender *UserLogin, origSender *OrigSender, evt *event.Event, content *event.MessageEventContent, caps *NetworkRoomCapabilities) {
(*Portal)(portal).handleMatrixEdit(ctx, sender, origSender, evt, content, caps)
}
func (portal *PortalInternals) HandleMatrixReaction(ctx context.Context, sender *UserLogin, evt *event.Event) {
(*Portal)(portal).handleMatrixReaction(ctx, sender, evt)
}
func (portal *PortalInternals) HandleMatrixRedaction(ctx context.Context, sender *UserLogin, origSender *OrigSender, evt *event.Event) {
(*Portal)(portal).handleMatrixRedaction(ctx, sender, origSender, evt)
}
func (portal *PortalInternals) HandleRemoteEvent(source *UserLogin, evt RemoteEvent) {
(*Portal)(portal).handleRemoteEvent(source, evt)
}
func (portal *PortalInternals) GetIntentAndUserMXIDFor(ctx context.Context, sender EventSender, source *UserLogin, otherLogins []*UserLogin, evtType RemoteEventType) (intent MatrixAPI, extraUserID id.UserID) {
return (*Portal)(portal).getIntentAndUserMXIDFor(ctx, sender, source, otherLogins, evtType)
}
func (portal *PortalInternals) GetRelationMeta(ctx context.Context, currentMsg networkid.MessageID, replyToPtr *networkid.MessageOptionalPartID, threadRootPtr *networkid.MessageID, isBatchSend bool) (replyTo, threadRoot, prevThreadEvent *database.Message) {
return (*Portal)(portal).getRelationMeta(ctx, currentMsg, replyToPtr, threadRootPtr, isBatchSend)
}
func (portal *PortalInternals) ApplyRelationMeta(content *event.MessageEventContent, replyTo, threadRoot, prevThreadEvent *database.Message) {
(*Portal)(portal).applyRelationMeta(content, replyTo, threadRoot, prevThreadEvent)
}
func (portal *PortalInternals) SendConvertedMessage(ctx context.Context, id networkid.MessageID, intent MatrixAPI, senderID networkid.UserID, converted *ConvertedMessage, ts time.Time, logContext func(*zerolog.Event) *zerolog.Event) []*database.Message {
return (*Portal)(portal).sendConvertedMessage(ctx, id, intent, senderID, converted, ts, logContext)
}
func (portal *PortalInternals) CheckPendingMessage(ctx context.Context, evt RemoteMessage) (bool, *database.Message) {
return (*Portal)(portal).checkPendingMessage(ctx, evt)
}
func (portal *PortalInternals) HandleRemoteUpsert(ctx context.Context, source *UserLogin, evt RemoteMessageUpsert, existing []*database.Message) bool {
return (*Portal)(portal).handleRemoteUpsert(ctx, source, evt, existing)
}
func (portal *PortalInternals) HandleRemoteMessage(ctx context.Context, source *UserLogin, evt RemoteMessage) {
(*Portal)(portal).handleRemoteMessage(ctx, source, evt)
}
func (portal *PortalInternals) SendRemoteErrorNotice(ctx context.Context, intent MatrixAPI, err error, ts time.Time, evtTypeName string) {
(*Portal)(portal).sendRemoteErrorNotice(ctx, intent, err, ts, evtTypeName)
}
func (portal *PortalInternals) HandleRemoteEdit(ctx context.Context, source *UserLogin, evt RemoteEdit) {
(*Portal)(portal).handleRemoteEdit(ctx, source, evt)
}
func (portal *PortalInternals) SendConvertedEdit(ctx context.Context, targetID networkid.MessageID, senderID networkid.UserID, converted *ConvertedEdit, intent MatrixAPI, ts time.Time) {
(*Portal)(portal).sendConvertedEdit(ctx, targetID, senderID, converted, intent, ts)
}
func (portal *PortalInternals) GetTargetMessagePart(ctx context.Context, evt RemoteEventWithTargetMessage) (*database.Message, error) {
return (*Portal)(portal).getTargetMessagePart(ctx, evt)
}
func (portal *PortalInternals) GetTargetReaction(ctx context.Context, evt RemoteReactionRemove) (*database.Reaction, error) {
return (*Portal)(portal).getTargetReaction(ctx, evt)
}
func (portal *PortalInternals) HandleRemoteReactionSync(ctx context.Context, source *UserLogin, evt RemoteReactionSync) {
(*Portal)(portal).handleRemoteReactionSync(ctx, source, evt)
}
func (portal *PortalInternals) HandleRemoteReaction(ctx context.Context, source *UserLogin, evt RemoteReaction) {
(*Portal)(portal).handleRemoteReaction(ctx, source, evt)
}
func (portal *PortalInternals) SendConvertedReaction(ctx context.Context, senderID networkid.UserID, intent MatrixAPI, targetMessage *database.Message, emojiID networkid.EmojiID, emoji string, ts time.Time, dbMetadata any, extraContent map[string]any, logContext func(*zerolog.Event) *zerolog.Event) {
(*Portal)(portal).sendConvertedReaction(ctx, senderID, intent, targetMessage, emojiID, emoji, ts, dbMetadata, extraContent, logContext)
}
func (portal *PortalInternals) GetIntentForMXID(ctx context.Context, userID id.UserID) (MatrixAPI, error) {
return (*Portal)(portal).getIntentForMXID(ctx, userID)
}
func (portal *PortalInternals) HandleRemoteReactionRemove(ctx context.Context, source *UserLogin, evt RemoteReactionRemove) {
(*Portal)(portal).handleRemoteReactionRemove(ctx, source, evt)
}
func (portal *PortalInternals) HandleRemoteMessageRemove(ctx context.Context, source *UserLogin, evt RemoteMessageRemove) {
(*Portal)(portal).handleRemoteMessageRemove(ctx, source, evt)
}
func (portal *PortalInternals) RedactMessageParts(ctx context.Context, parts []*database.Message, intent MatrixAPI, ts time.Time) {
(*Portal)(portal).redactMessageParts(ctx, parts, intent, ts)
}
func (portal *PortalInternals) HandleRemoteReadReceipt(ctx context.Context, source *UserLogin, evt RemoteReceipt) {
(*Portal)(portal).handleRemoteReadReceipt(ctx, source, evt)
}
func (portal *PortalInternals) HandleRemoteMarkUnread(ctx context.Context, source *UserLogin, evt RemoteMarkUnread) {
(*Portal)(portal).handleRemoteMarkUnread(ctx, source, evt)
}
func (portal *PortalInternals) HandleRemoteDeliveryReceipt(ctx context.Context, source *UserLogin, evt RemoteReceipt) {
(*Portal)(portal).handleRemoteDeliveryReceipt(ctx, source, evt)
}
func (portal *PortalInternals) HandleRemoteTyping(ctx context.Context, source *UserLogin, evt RemoteTyping) {
(*Portal)(portal).handleRemoteTyping(ctx, source, evt)
}
func (portal *PortalInternals) HandleRemoteChatInfoChange(ctx context.Context, source *UserLogin, evt RemoteChatInfoChange) {
(*Portal)(portal).handleRemoteChatInfoChange(ctx, source, evt)
}
func (portal *PortalInternals) HandleRemoteChatResync(ctx context.Context, source *UserLogin, evt RemoteChatResync) {
(*Portal)(portal).handleRemoteChatResync(ctx, source, evt)
}
func (portal *PortalInternals) HandleRemoteChatDelete(ctx context.Context, source *UserLogin, evt RemoteChatDelete) {
(*Portal)(portal).handleRemoteChatDelete(ctx, source, evt)
}
func (portal *PortalInternals) HandleRemoteBackfill(ctx context.Context, source *UserLogin, backfill RemoteBackfill) {
(*Portal)(portal).handleRemoteBackfill(ctx, source, backfill)
}
func (portal *PortalInternals) UpdateName(ctx context.Context, name string, sender MatrixAPI, ts time.Time) bool {
return (*Portal)(portal).updateName(ctx, name, sender, ts)
}
func (portal *PortalInternals) UpdateTopic(ctx context.Context, topic string, sender MatrixAPI, ts time.Time) bool {
return (*Portal)(portal).updateTopic(ctx, topic, sender, ts)
}
func (portal *PortalInternals) UpdateAvatar(ctx context.Context, avatar *Avatar, sender MatrixAPI, ts time.Time) bool {
return (*Portal)(portal).updateAvatar(ctx, avatar, sender, ts)
}
func (portal *PortalInternals) GetBridgeInfo() (string, event.BridgeEventContent) {
return (*Portal)(portal).getBridgeInfo()
}
func (portal *PortalInternals) SendStateWithIntentOrBot(ctx context.Context, sender MatrixAPI, eventType event.Type, stateKey string, content *event.Content, ts time.Time) (resp *mautrix.RespSendEvent, err error) {
return (*Portal)(portal).sendStateWithIntentOrBot(ctx, sender, eventType, stateKey, content, ts)
}
func (portal *PortalInternals) SendRoomMeta(ctx context.Context, sender MatrixAPI, ts time.Time, eventType event.Type, stateKey string, content any) bool {
return (*Portal)(portal).sendRoomMeta(ctx, sender, ts, eventType, stateKey, content)
}
func (portal *PortalInternals) GetInitialMemberList(ctx context.Context, members *ChatMemberList, source *UserLogin, pl *event.PowerLevelsEventContent) (invite, functional []id.UserID, err error) {
return (*Portal)(portal).getInitialMemberList(ctx, members, source, pl)
}
func (portal *PortalInternals) UpdateOtherUser(ctx context.Context, members *ChatMemberList) (changed bool) {
return (*Portal)(portal).updateOtherUser(ctx, members)
}
func (portal *PortalInternals) SyncParticipants(ctx context.Context, members *ChatMemberList, source *UserLogin, sender MatrixAPI, ts time.Time) error {
return (*Portal)(portal).syncParticipants(ctx, members, source, sender, ts)
}
func (portal *PortalInternals) UpdateUserLocalInfo(ctx context.Context, info *UserLocalPortalInfo, source *UserLogin) {
(*Portal)(portal).updateUserLocalInfo(ctx, info, source)
}
func (portal *PortalInternals) UpdateParent(ctx context.Context, newParent networkid.PortalID, source *UserLogin) bool {
return (*Portal)(portal).updateParent(ctx, newParent, source)
}
func (portal *PortalInternals) LockedUpdateInfoFromGhost(ctx context.Context, ghost *Ghost) {
(*Portal)(portal).lockedUpdateInfoFromGhost(ctx, ghost)
}
func (portal *PortalInternals) CreateMatrixRoomInLoop(ctx context.Context, source *UserLogin, info *ChatInfo) error {
return (*Portal)(portal).createMatrixRoomInLoop(ctx, source, info)
}
func (portal *PortalInternals) UnlockedDelete(ctx context.Context) error {
return (*Portal)(portal).unlockedDelete(ctx)
}
func (portal *PortalInternals) UnlockedDeleteCache() {
(*Portal)(portal).unlockedDeleteCache()
}

View file

@ -0,0 +1,160 @@
// Copyright (c) 2024 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//go:build ignore
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
"strings"
"go.mau.fi/util/exerrors"
)
const header = `// GENERATED BY portalinternal_generate.go; DO NOT EDIT
//go:generate go run portalinternal_generate.go
//go:generate goimports -w portalinternal.go
package bridgev2
`
const postImportHeader = `
type PortalInternals Portal
// Deprecated: portal internals should be used carefully and only when necessary.
func (portal *Portal) Internal() *PortalInternals {
return (*PortalInternals)(portal)
}
`
func getTypeName(expr ast.Expr) string {
switch e := expr.(type) {
case *ast.Ident:
return e.Name
case *ast.StarExpr:
return "*" + getTypeName(e.X)
case *ast.ArrayType:
return "[]" + getTypeName(e.Elt)
case *ast.MapType:
return fmt.Sprintf("map[%s]%s", getTypeName(e.Key), getTypeName(e.Value))
case *ast.ChanType:
return fmt.Sprintf("chan %s", getTypeName(e.Value))
case *ast.FuncType:
var params []string
for _, param := range e.Params.List {
params = append(params, getTypeName(param.Type))
}
var results []string
if e.Results != nil {
for _, result := range e.Results.List {
results = append(results, getTypeName(result.Type))
}
}
return fmt.Sprintf("func(%s) %s", strings.Join(params, ", "), strings.Join(results, ", "))
case *ast.SelectorExpr:
return fmt.Sprintf("%s.%s", getTypeName(e.X), e.Sel.Name)
default:
panic(fmt.Errorf("unknown type %T", e))
}
}
func main() {
fset := token.NewFileSet()
f := exerrors.Must(parser.ParseFile(fset, "portal.go", nil, parser.SkipObjectResolution))
file := exerrors.Must(os.OpenFile("portalinternal.go", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644))
write := func(str string) {
exerrors.Must(file.WriteString(str))
}
writef := func(format string, args ...any) {
exerrors.Must(fmt.Fprintf(file, format, args...))
}
write(header)
write("import (\n")
for _, i := range f.Imports {
write("\t")
if i.Name != nil {
writef("%s ", i.Name.Name)
}
writef("%s\n", i.Path.Value)
}
write(")\n")
write(postImportHeader)
ast.Inspect(f, func(node ast.Node) (retVal bool) {
retVal = true
funcDecl, ok := node.(*ast.FuncDecl)
if !ok || funcDecl.Name.IsExported() {
return
}
if funcDecl.Recv == nil || len(funcDecl.Recv.List) == 0 || len(funcDecl.Recv.List[0].Names) == 0 ||
funcDecl.Recv.List[0].Names[0].Name != "portal" {
return
}
writef("\nfunc (portal *PortalInternals) %s%s(", strings.ToUpper(funcDecl.Name.Name[0:1]), funcDecl.Name.Name[1:])
for i, param := range funcDecl.Type.Params.List {
if i != 0 {
write(", ")
}
for j, name := range param.Names {
if j != 0 {
write(", ")
}
write(name.Name)
}
if len(param.Names) > 0 {
write(" ")
}
write(getTypeName(param.Type))
}
write(") ")
if funcDecl.Type.Results != nil && len(funcDecl.Type.Results.List) > 0 {
needsParentheses := len(funcDecl.Type.Results.List) > 1 || len(funcDecl.Type.Results.List[0].Names) > 0
if needsParentheses {
write("(")
}
for i, result := range funcDecl.Type.Results.List {
if i != 0 {
write(", ")
}
for j, name := range result.Names {
if j != 0 {
write(", ")
}
write(name.Name)
}
if len(result.Names) > 0 {
write(" ")
}
write(getTypeName(result.Type))
}
if needsParentheses {
write(")")
}
write(" ")
}
write("{\n\t")
if funcDecl.Type.Results != nil {
write("return ")
}
writef("(*Portal)(portal).%s(", funcDecl.Name.Name)
for i, param := range funcDecl.Type.Params.List {
for j, name := range param.Names {
if i != 0 || j != 0 {
write(", ")
}
write(name.Name)
}
}
write(")\n}\n")
return
})
exerrors.PanicIfNotNil(file.Close())
}