From cfb2cc057c32330be0ca0a68cfbd245cb2b8e31b Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 5 Sep 2018 10:55:48 +0300 Subject: Update to latest gomatrix. Things are broken --- Gopkg.lock | 40 +- interface/matrix.go | 6 +- interface/ui.go | 8 +- matrix/matrix.go | 69 ++- matrix/pushrules/condition.go | 19 +- matrix/pushrules/pushrules.go | 16 +- matrix/pushrules/rule.go | 3 +- matrix/rooms/member.go | 63 --- matrix/rooms/member_test.go | 17 - matrix/rooms/room.go | 65 ++- matrix/sync.go | 17 +- ui/commands.go | 5 +- ui/messages/base.go | 9 +- ui/messages/expandedtextmessage.go | 3 +- ui/messages/imagemessage.go | 3 +- ui/messages/parser/htmlparser.go | 9 +- ui/messages/parser/parser.go | 48 +-- ui/messages/textmessage.go | 5 +- ui/room-view.go | 31 +- ui/view-main.go | 2 +- vendor/github.com/davecgh/go-spew/LICENSE | 2 +- vendor/github.com/davecgh/go-spew/spew/bypass.go | 187 ++++----- .../github.com/davecgh/go-spew/spew/bypasssafe.go | 2 +- vendor/github.com/davecgh/go-spew/spew/common.go | 2 +- vendor/github.com/davecgh/go-spew/spew/dump.go | 10 +- vendor/github.com/davecgh/go-spew/spew/format.go | 4 +- .../github.com/disintegration/imaging/effects.go | 36 +- .../github.com/disintegration/imaging/helpers.go | 272 ------------ vendor/github.com/disintegration/imaging/io.go | 463 +++++++++++++++++++++ vendor/github.com/disintegration/imaging/tools.go | 34 ++ .../github.com/lucasb-eyer/go-colorful/README.md | 33 +- .../github.com/lucasb-eyer/go-colorful/colors.go | 7 +- vendor/github.com/mattn/go-runewidth/runewidth.go | 14 +- vendor/github.com/renstrom/fuzzysearch/LICENSE | 2 +- .../github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go | 5 +- .../stretchr/testify/assert/assertion_format.go | 139 ++++++- .../testify/assert/assertion_format.go.tmpl | 1 + .../stretchr/testify/assert/assertion_forward.go | 278 ++++++++++++- .../testify/assert/assertion_forward.go.tmpl | 1 + .../stretchr/testify/assert/assertions.go | 214 ++++++++-- .../stretchr/testify/assert/http_assertions.go | 22 +- vendor/github.com/zyedidia/clipboard/LICENSE | 4 + vendor/github.com/zyedidia/clipboard/README.md | 4 + vendor/golang.org/x/image/bmp/writer.go | 122 +++++- vendor/golang.org/x/net/html/parse.go | 103 ++--- vendor/gopkg.in/toast.v1/toast.go | 5 +- vendor/maunium.net/go/gomatrix/client.go | 145 +++++-- vendor/maunium.net/go/gomatrix/events.go | 455 ++++++++++++++++---- vendor/maunium.net/go/gomatrix/reply.go | 96 +++++ vendor/maunium.net/go/gomatrix/requests.go | 8 +- vendor/maunium.net/go/gomatrix/responses.go | 2 +- vendor/maunium.net/go/gomatrix/room.go | 20 +- vendor/maunium.net/go/gomatrix/sync.go | 15 +- vendor/maunium.net/go/maulogger/LICENSE | 21 + vendor/maunium.net/go/maulogger/README.md | 6 + vendor/maunium.net/go/maulogger/logger.go | 219 ++++++++++ 56 files changed, 2461 insertions(+), 930 deletions(-) delete mode 100644 matrix/rooms/member.go delete mode 100644 matrix/rooms/member_test.go delete mode 100644 vendor/github.com/disintegration/imaging/helpers.go create mode 100644 vendor/github.com/disintegration/imaging/io.go create mode 100644 vendor/maunium.net/go/gomatrix/reply.go create mode 100644 vendor/maunium.net/go/maulogger/LICENSE create mode 100644 vendor/maunium.net/go/maulogger/README.md create mode 100644 vendor/maunium.net/go/maulogger/logger.go diff --git a/Gopkg.lock b/Gopkg.lock index 86d61b9..25c518f 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -4,14 +4,14 @@ [[projects]] name = "github.com/davecgh/go-spew" packages = ["spew"] - revision = "346938d642f2ec3594ed81d874461961cd0faa76" - version = "v1.1.0" + revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" + version = "v1.1.1" [[projects]] name = "github.com/disintegration/imaging" packages = ["."] - revision = "bbcee2f5c9d5e94ca42c8b50ec847fec64a6c134" - version = "v1.4.2" + revision = "0bd5694c78c9c3d9a3cd06a706a8f3c59296a9ac" + version = "v1.5.0" [[projects]] branch = "master" @@ -26,16 +26,16 @@ version = "v1.5.1" [[projects]] - branch = "master" name = "github.com/lucasb-eyer/go-colorful" packages = ["."] - revision = "8abd3beca3a7f5039809449d6013a0254ac22bb1" + revision = "345fbb3dbcdb252d9985ee899a84963c0fa24c82" + version = "v1.0" [[projects]] name = "github.com/mattn/go-runewidth" packages = ["."] - revision = "9e777a8366cce605130a531d2cd6363d07ad7317" - version = "v0.0.2" + revision = "ce7b0b5c7b45a81508558cd1dba6bb1e4ddb51bb" + version = "v0.0.3" [[projects]] branch = "master" @@ -50,10 +50,10 @@ version = "v1.0.0" [[projects]] - branch = "master" name = "github.com/renstrom/fuzzysearch" packages = ["fuzzy"] - revision = "500e0fce37a81072d9bf4ec1bf5d32f52c807282" + revision = "b18e754edff4833912ef4dce9eaca885bd3f0de1" + version = "v1.0.1" [[projects]] branch = "master" @@ -64,14 +64,14 @@ [[projects]] name = "github.com/stretchr/testify" packages = ["assert"] - revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" - version = "v1.2.1" + revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686" + version = "v1.2.2" [[projects]] branch = "master" name = "github.com/zyedidia/clipboard" packages = ["."] - revision = "4611e809d8b1a3051c11d11f4b610c44df73fa38" + revision = "bd31d747117d04b4e25b61f73e1ea4faeea3c56a" [[projects]] branch = "master" @@ -85,7 +85,7 @@ "vp8l", "webp" ] - revision = "f315e440302883054d0c2bd85486878cb4f8572c" + revision = "c73c2afc3b812cdd6385de5a50616511c4a3d458" [[projects]] branch = "master" @@ -94,7 +94,7 @@ "html", "html/atom" ] - revision = "dfa909b99c79129e1100513e5cd36307665e5723" + revision = "8a410e7b638dca158bf9e766925842f6651ff828" [[projects]] name = "golang.org/x/text" @@ -118,7 +118,7 @@ branch = "v1" name = "gopkg.in/toast.v1" packages = ["."] - revision = "b700e246b8b6d3e13554091e540e1019e26389f1" + revision = "0a84660828b24d25b35525c9a1f1f51267f8da91" [[projects]] name = "gopkg.in/yaml.v2" @@ -130,7 +130,13 @@ branch = "master" name = "maunium.net/go/gomatrix" packages = ["."] - revision = "b491397f18b90e34ef54b8b3598666564b90b01a" + revision = "d651abc3ecb4bbd85a6b17b710632e73ddbbc6aa" + +[[projects]] + name = "maunium.net/go/maulogger" + packages = ["."] + revision = "64f0aa33b6c51313e15575257db71dec44fe7988" + version = "v1.0" [[projects]] branch = "master" diff --git a/interface/matrix.go b/interface/matrix.go index 08b3d61..0538df9 100644 --- a/interface/matrix.go +++ b/interface/matrix.go @@ -33,14 +33,14 @@ type MatrixContainer interface { Logout() SendPreferencesToMatrix() - SendMessage(roomID, msgtype, message string) (string, error) - SendMarkdownMessage(roomID, msgtype, message string) (string, error) + SendMessage(roomID string, msgtype gomatrix.MessageType, message string) (string, error) + SendMarkdownMessage(roomID string, msgtype gomatrix.MessageType, message string) (string, error) SendTyping(roomID string, typing bool) MarkRead(roomID, eventID string) JoinRoom(roomID, server string) (*rooms.Room, error) LeaveRoom(roomID string) error - GetHistory(roomID, prevBatch string, limit int) ([]gomatrix.Event, string, error) + GetHistory(roomID, prevBatch string, limit int) ([]*gomatrix.Event, string, error) GetRoom(roomID string) *rooms.Room Download(mxcURL string) ([]byte, string, string, error) diff --git a/interface/ui.go b/interface/ui.go index b106229..929dc65 100644 --- a/interface/ui.go +++ b/interface/ui.go @@ -73,8 +73,8 @@ type RoomView interface { SetTyping(users []string) UpdateUserList() - NewMessage(id, sender, msgtype, text string, timestamp time.Time) Message - NewTempMessage(msgtype, text string) Message + NewMessage(id, sender string, msgtype gomatrix.MessageType, text string, timestamp time.Time) Message + NewTempMessage(msgtype gomatrix.MessageType, text string) Message AddMessage(message Message, direction MessageDirection) AddServiceMessage(message string) } @@ -111,8 +111,8 @@ type Message interface { SetID(id string) ID() string - SetType(msgtype string) - Type() string + SetType(msgtype gomatrix.MessageType) + Type() gomatrix.MessageType NotificationContent() string diff --git a/matrix/matrix.go b/matrix/matrix.go index 9b90ffe..060becd 100644 --- a/matrix/matrix.go +++ b/matrix/matrix.go @@ -175,6 +175,8 @@ func (c *Container) PushRules() *pushrules.PushRuleset { return c.config.PushRules } +var AccountDataGomuksPreferences = gomatrix.NewEventType("net.maunium.gomuks.preferences") + // OnLogin initializes the syncer and updates the room list. func (c *Container) OnLogin() { c.ui.OnLogin() @@ -183,14 +185,14 @@ func (c *Container) OnLogin() { debug.Print("Initializing syncer") c.syncer = NewGomuksSyncer(c.config) - c.syncer.OnEventType("m.room.message", c.HandleMessage) - c.syncer.OnEventType("m.room.member", c.HandleMembership) - c.syncer.OnEventType("m.receipt", c.HandleReadReceipt) - c.syncer.OnEventType("m.typing", c.HandleTyping) - c.syncer.OnEventType("m.direct", c.HandleDirectChatInfo) - c.syncer.OnEventType("m.push_rules", c.HandlePushRules) - c.syncer.OnEventType("m.tag", c.HandleTag) - c.syncer.OnEventType("net.maunium.gomuks.preferences", c.HandlePreferences) + c.syncer.OnEventType(gomatrix.EventMessage, c.HandleMessage) + c.syncer.OnEventType(gomatrix.StateMember, c.HandleMembership) + c.syncer.OnEventType(gomatrix.EphemeralEventReceipt, c.HandleReadReceipt) + c.syncer.OnEventType(gomatrix.EphemeralEventTyping, c.HandleTyping) + c.syncer.OnEventType(gomatrix.AccountDataDirectChats, c.HandleDirectChatInfo) + c.syncer.OnEventType(gomatrix.AccountDataPushRules, c.HandlePushRules) + c.syncer.OnEventType(gomatrix.AccountDataRoomTags, c.HandleTag) + c.syncer.OnEventType(AccountDataGomuksPreferences, c.HandlePreferences) c.syncer.InitDoneCallback = func() { c.config.AuthCache.InitialSyncDone = true c.config.SaveAuthCache() @@ -301,10 +303,10 @@ func (c *Container) HandleMembership(source EventSource, evt *gomatrix.Event) { } func (c *Container) processOwnMembershipChange(evt *gomatrix.Event) { - membership, _ := evt.Content["membership"].(string) - prevMembership := "leave" + membership := evt.Content.Membership + prevMembership := gomatrix.MembershipLeave if evt.Unsigned.PrevContent != nil { - prevMembership, _ = evt.Unsigned.PrevContent["membership"].(string) + prevMembership = evt.Unsigned.PrevContent.Membership } debug.Printf("Processing own membership change: %s->%s in %s", prevMembership, membership, evt.RoomID) if membership == prevMembership { @@ -326,7 +328,7 @@ func (c *Container) processOwnMembershipChange(evt *gomatrix.Event) { func (c *Container) parseReadReceipt(evt *gomatrix.Event) (largestTimestampEvent string) { var largestTimestamp int64 - for eventID, rawContent := range evt.Content { + for eventID, rawContent := range evt.Content.Raw { content, ok := rawContent.(map[string]interface{}) if !ok { continue @@ -368,7 +370,7 @@ func (c *Container) HandleReadReceipt(source EventSource, evt *gomatrix.Event) { func (c *Container) parseDirectChatInfo(evt *gomatrix.Event) map[*rooms.Room]bool { directChats := make(map[*rooms.Room]bool) - for _, rawRoomIDList := range evt.Content { + for _, rawRoomIDList := range evt.Content.Raw { roomIDList, ok := rawRoomIDList.([]interface{}) if !ok { continue @@ -416,15 +418,12 @@ func (c *Container) HandlePushRules(source EventSource, evt *gomatrix.Event) { func (c *Container) HandleTag(source EventSource, evt *gomatrix.Event) { room := c.config.GetRoom(evt.RoomID) - tags, _ := evt.Content["tags"].(map[string]interface{}) - newTags := make([]rooms.RoomTag, len(tags)) + newTags := make([]rooms.RoomTag, len(evt.Content.RoomTags)) index := 0 - for tag, infoifc := range tags { - info, _ := infoifc.(map[string]interface{}) + for tag, info := range evt.Content.RoomTags { order := "0.5" - rawOrder, ok := info["order"] - if ok { - order = fmt.Sprintf("%v", rawOrder) + if len(info.Order) > 0 { + order = info.Order } newTags[index] = rooms.RoomTag{ Tag: tag, @@ -440,13 +439,7 @@ func (c *Container) HandleTag(source EventSource, evt *gomatrix.Event) { // HandleTyping is the event handler for the m.typing event. func (c *Container) HandleTyping(source EventSource, evt *gomatrix.Event) { - users := evt.Content["user_ids"].([]interface{}) - - strUsers := make([]string, len(users)) - for i, user := range users { - strUsers[i] = user.(string) - } - c.ui.MainView().SetTyping(evt.RoomID, strUsers) + c.ui.MainView().SetTyping(evt.RoomID, evt.Content.TypingUserIDs) } func (c *Container) MarkRead(roomID, eventID string) { @@ -455,11 +448,11 @@ func (c *Container) MarkRead(roomID, eventID string) { } // SendMessage sends a message with the given text to the given room. -func (c *Container) SendMessage(roomID, msgtype, text string) (string, error) { +func (c *Container) SendMessage(roomID string, msgtype gomatrix.MessageType, text string) (string, error) { defer debug.Recover() c.SendTyping(roomID, false) - resp, err := c.client.SendMessageEvent(roomID, "m.room.message", - gomatrix.TextMessage{MsgType: msgtype, Body: text}) + resp, err := c.client.SendMessageEvent(roomID, gomatrix.EventMessage, + gomatrix.Content{MsgType: msgtype, Body: text}) if err != nil { return "", err } @@ -498,7 +491,7 @@ var roomRegex = regexp.MustCompile("\\[.+?]\\(https://matrix.to/#/(#.+?:[^/]+?)\ // // If the given text contains markdown formatting symbols, it will be rendered into HTML before sending. // Otherwise, it will be sent as plain text. -func (c *Container) SendMarkdownMessage(roomID, msgtype, text string) (string, error) { +func (c *Container) SendMarkdownMessage(roomID string, msgtype gomatrix.MessageType, text string) (string, error) { defer debug.Recover() html := c.renderMarkdown(text) @@ -511,12 +504,12 @@ func (c *Container) SendMarkdownMessage(roomID, msgtype, text string) (string, e text = roomRegex.ReplaceAllString(text, "$1") c.SendTyping(roomID, false) - resp, err := c.client.SendMessageEvent(roomID, "m.room.message", - map[string]interface{}{ - "msgtype": msgtype, - "body": text, - "format": "org.matrix.custom.html", - "formatted_body": html, + resp, err := c.client.SendMessageEvent(roomID, gomatrix.EventMessage, + gomatrix.Content{ + MsgType: msgtype, + Body: text, + Format: gomatrix.FormatHTML, + FormattedBody: html, }) if err != nil { return "", err @@ -567,7 +560,7 @@ func (c *Container) LeaveRoom(roomID string) error { } // GetHistory fetches room history. -func (c *Container) GetHistory(roomID, prevBatch string, limit int) ([]gomatrix.Event, string, error) { +func (c *Container) GetHistory(roomID, prevBatch string, limit int) ([]*gomatrix.Event, string, error) { resp, err := c.client.Messages(roomID, prevBatch, "", 'b', limit) if err != nil { return nil, "", err diff --git a/matrix/pushrules/condition.go b/matrix/pushrules/condition.go index 08958a3..22d59aa 100644 --- a/matrix/pushrules/condition.go +++ b/matrix/pushrules/condition.go @@ -23,14 +23,13 @@ import ( "maunium.net/go/gomatrix" "maunium.net/go/gomuks/lib/glob" - "maunium.net/go/gomuks/matrix/rooms" ) // Room is an interface with the functions that are needed for processing room-specific push conditions type Room interface { - GetMember(mxid string) *rooms.Member - GetMembers() map[string]*rooms.Member - GetSessionOwner() *rooms.Member + GetMember(mxid string) *gomatrix.Member + GetMembers() map[string]*gomatrix.Member + GetSessionOwner() string } // PushCondKind is the type of a push condition. @@ -89,7 +88,7 @@ func (cond *PushCondition) matchValue(room Room, event *gomatrix.Event) bool { switch key { case "type": - return pattern.MatchString(event.Type) + return pattern.MatchString(event.Type.String()) case "sender": return pattern.MatchString(event.Sender) case "room_id": @@ -100,7 +99,7 @@ func (cond *PushCondition) matchValue(room Room, event *gomatrix.Event) bool { } return pattern.MatchString(*event.StateKey) case "content": - val, _ := event.Content[subkey].(string) + val, _ := event.Content.Raw[subkey].(string) return pattern.MatchString(val) default: return false @@ -108,12 +107,12 @@ func (cond *PushCondition) matchValue(room Room, event *gomatrix.Event) bool { } func (cond *PushCondition) matchDisplayName(room Room, event *gomatrix.Event) bool { - member := room.GetSessionOwner() - if member == nil || member.UserID == event.Sender { + ownerID := room.GetSessionOwner() + if ownerID == event.Sender { return false } - text, _ := event.Content["body"].(string) - return strings.Contains(text, member.DisplayName) + member := room.GetMember(ownerID) + return strings.Contains(event.Content.Body, member.Displayname) } func (cond *PushCondition) matchMemberCount(room Room, event *gomatrix.Event) bool { diff --git a/matrix/pushrules/pushrules.go b/matrix/pushrules/pushrules.go index 876713b..b383c66 100644 --- a/matrix/pushrules/pushrules.go +++ b/matrix/pushrules/pushrules.go @@ -21,19 +21,17 @@ func GetScopedPushRules(client *gomatrix.Client, scope string) (resp *PushRulese return } +type contentWithRuleset struct { + Ruleset *PushRuleset `json:"global"` +} + // EventToPushRules converts a m.push_rules event to a PushRuleset by passing the data through JSON. func EventToPushRules(event *gomatrix.Event) (*PushRuleset, error) { - content, _ := event.Content["global"] - raw, err := json.Marshal(content) - if err != nil { - return nil, err - } - - ruleset := &PushRuleset{} - err = json.Unmarshal(raw, ruleset) + content := &contentWithRuleset{} + err := json.Unmarshal(event.Content.VeryRaw, content) if err != nil { return nil, err } - return ruleset, nil + return content.Ruleset, nil } diff --git a/matrix/pushrules/rule.go b/matrix/pushrules/rule.go index 5ede895..71f71e5 100644 --- a/matrix/pushrules/rule.go +++ b/matrix/pushrules/rule.go @@ -154,6 +154,5 @@ func (rule *PushRule) matchPattern(room Room, event *gomatrix.Event) bool { if err != nil { return false } - text, _ := event.Content["body"].(string) - return pattern.MatchString(text) + return pattern.MatchString(event.Content.Body) } diff --git a/matrix/rooms/member.go b/matrix/rooms/member.go deleted file mode 100644 index aee7533..0000000 --- a/matrix/rooms/member.go +++ /dev/null @@ -1,63 +0,0 @@ -// gomuks - A terminal Matrix client written in Go. -// Copyright (C) 2018 Tulir Asokan -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package rooms - -import ( - "maunium.net/go/gomatrix" -) - -// Membership is an enum specifying the membership state of a room member. -type Membership string - -// The allowed membership states as specified in spec section 10.5.5. -const ( - MembershipJoin Membership = "join" - MembershipLeave Membership = "leave" - MembershipInvite Membership = "invite" - MembershipBan Membership = "ban" - MembershipKnock Membership = "knock" -) - -// Member represents a member in a room. -type Member struct { - // The MXID of the member. - UserID string `json:"-"` - // The membership status. Defaults to leave. - Membership Membership `json:"membership"` - // The display name of the user. Defaults to the user ID. - DisplayName string `json:"displayname"` - // The avatar URL of the user. Defaults to an empty string. - AvatarURL string `json:"avatar_url"` -} - -// eventToRoomMember converts a m.room.member state event into a Member object. -func eventToRoomMember(userID string, event *gomatrix.Event) *Member { - membership, _ := event.Content["membership"].(string) - avatarURL, _ := event.Content["avatar_url"].(string) - - displayName, _ := event.Content["displayname"].(string) - if len(displayName) == 0 { - displayName = userID - } - - return &Member{ - UserID: userID, - Membership: Membership(membership), - DisplayName: displayName, - AvatarURL: avatarURL, - } -} diff --git a/matrix/rooms/member_test.go b/matrix/rooms/member_test.go deleted file mode 100644 index c3a4cfe..0000000 --- a/matrix/rooms/member_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// gomuks - A terminal Matrix client written in Go. -// Copyright (C) 2018 Tulir Asokan -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package rooms_test diff --git a/matrix/rooms/room.go b/matrix/rooms/room.go index 598f0ea..bd233f8 100644 --- a/matrix/rooms/room.go +++ b/matrix/rooms/room.go @@ -82,10 +82,10 @@ type Room struct { LastReceivedMessage time.Time // MXID -> Member cache calculated from membership events. - memberCache map[string]*Member + memberCache map[string]*gomatrix.Member // The first non-SessionUserID member in the room. Calculated at // the same time as memberCache. - firstMemberCache *Member + firstMemberCache *gomatrix.Member // The name of the room. Calculated from the state event name, // canonical_alias or alias or the member cache. nameCache string @@ -222,25 +222,25 @@ func (room *Room) UpdateState(event *gomatrix.Event) { room.State[event.Type] = make(map[string]*gomatrix.Event) } switch event.Type { - case "m.room.name": + case gomatrix.StateRoomName: room.nameCache = "" - case "m.room.canonical_alias": + case gomatrix.StateCanonicalAlias: if room.nameCacheSource >= CanonicalAliasRoomName { room.nameCache = "" } room.canonicalAliasCache = "" - case "m.room.aliases": + case gomatrix.StateAliases: if room.nameCacheSource >= AliasRoomName { room.nameCache = "" } room.aliasesCache = nil - case "m.room.member": + case gomatrix.StateMember: room.memberCache = nil room.firstMemberCache = nil if room.nameCacheSource >= MemberRoomName { room.nameCache = "" } - case "m.room.topic": + case gomatrix.StateTopic: room.topicCache = "" } @@ -248,7 +248,7 @@ func (room *Room) UpdateState(event *gomatrix.Event) { if event.StateKey != nil { stateKey = *event.StateKey } - if event.Type != "m.room.member" { + if event.Type != gomatrix.StateMember { debug.Printf("Updating state %s#%s for %s", event.Type, stateKey, room.ID) } @@ -260,14 +260,14 @@ func (room *Room) UpdateState(event *gomatrix.Event) { } // GetStateEvent returns the state event for the given type/state_key combo, or nil. -func (room *Room) GetStateEvent(eventType string, stateKey string) *gomatrix.Event { +func (room *Room) GetStateEvent(eventType gomatrix.EventType, stateKey string) *gomatrix.Event { stateEventMap, _ := room.State[eventType] event, _ := stateEventMap[stateKey] return event } // GetStateEvents returns the state events for the given type. -func (room *Room) GetStateEvents(eventType string) map[string]*gomatrix.Event { +func (room *Room) GetStateEvents(eventType gomatrix.EventType) map[string]*gomatrix.Event { stateEventMap, _ := room.State[eventType] return stateEventMap } @@ -275,9 +275,9 @@ func (room *Room) GetStateEvents(eventType string) map[string]*gomatrix.Event { // GetTopic returns the topic of the room. func (room *Room) GetTopic() string { if len(room.topicCache) == 0 { - topicEvt := room.GetStateEvent("m.room.topic", "") + topicEvt := room.GetStateEvent(gomatrix.StateTopic, "") if topicEvt != nil { - room.topicCache, _ = topicEvt.Content["topic"].(string) + room.topicCache = topicEvt.Content.Topic } } return room.topicCache @@ -285,9 +285,9 @@ func (room *Room) GetTopic() string { func (room *Room) GetCanonicalAlias() string { if len(room.canonicalAliasCache) == 0 { - canonicalAliasEvt := room.GetStateEvent("m.room.canonical_alias", "") + canonicalAliasEvt := room.GetStateEvent(gomatrix.StateCanonicalAlias, "") if canonicalAliasEvt != nil { - room.canonicalAliasCache, _ = canonicalAliasEvt.Content["alias"].(string) + room.canonicalAliasCache = canonicalAliasEvt.Content.Alias } else { room.canonicalAliasCache = "-" } @@ -301,17 +301,10 @@ func (room *Room) GetCanonicalAlias() string { // GetAliases returns the list of aliases that point to this room. func (room *Room) GetAliases() []string { if room.aliasesCache == nil { - aliasEvents := room.GetStateEvents("m.room.aliases") + aliasEvents := room.GetStateEvents(gomatrix.StateAliases) room.aliasesCache = []string{} for _, event := range aliasEvents { - aliases, _ := event.Content["aliases"].([]interface{}) - - newAliases := make([]string, len(room.aliasesCache)+len(aliases)) - copy(newAliases, room.aliasesCache) - for index, alias := range aliases { - newAliases[len(room.aliasesCache)+index], _ = alias.(string) - } - room.aliasesCache = newAliases + room.aliasesCache = append(room.aliasesCache, event.Content.Aliases...) } } return room.aliasesCache @@ -319,9 +312,9 @@ func (room *Room) GetAliases() []string { // updateNameFromNameEvent updates the room display name to be the name set in the name event. func (room *Room) updateNameFromNameEvent() { - nameEvt := room.GetStateEvent("m.room.name", "") + nameEvt := room.GetStateEvent(gomatrix.StateRoomName, "") if nameEvt != nil { - room.nameCache, _ = nameEvt.Content["name"].(string) + room.nameCache = nameEvt.Content.Name } } @@ -353,9 +346,9 @@ func (room *Room) updateNameFromMembers() { } else if room.firstMemberCache == nil { room.nameCache = "Room" } else if len(members) == 2 { - room.nameCache = room.firstMemberCache.DisplayName + room.nameCache = room.firstMemberCache.Displayname } else { - firstMember := room.firstMemberCache.DisplayName + firstMember := room.firstMemberCache.Displayname room.nameCache = fmt.Sprintf("%s and %d others", firstMember, len(members)-2) } } @@ -391,18 +384,18 @@ func (room *Room) GetTitle() string { } // createMemberCache caches all member events into a easily processable MXID -> *Member map. -func (room *Room) createMemberCache() map[string]*Member { - cache := make(map[string]*Member) - events := room.GetStateEvents("m.room.member") +func (room *Room) createMemberCache() map[string]*gomatrix.Member { + cache := make(map[string]*gomatrix.Member) + events := room.GetStateEvents(gomatrix.StateMember) room.firstMemberCache = nil if events != nil { for userID, event := range events { - member := eventToRoomMember(userID, event) + member := &event.Content.Member if room.firstMemberCache == nil && userID != room.SessionUserID { room.firstMemberCache = member } if member.Membership != "leave" { - cache[member.UserID] = member + cache[userID] = member } } } @@ -414,7 +407,7 @@ func (room *Room) createMemberCache() map[string]*Member { // // The members are returned from the cache. // If the cache is empty, it is updated first. -func (room *Room) GetMembers() map[string]*Member { +func (room *Room) GetMembers() map[string]*gomatrix.Member { if len(room.memberCache) == 0 || room.firstMemberCache == nil { room.createMemberCache() } @@ -423,7 +416,7 @@ func (room *Room) GetMembers() map[string]*Member { // GetMember returns the member with the given MXID. // If the member doesn't exist, nil is returned. -func (room *Room) GetMember(userID string) *Member { +func (room *Room) GetMember(userID string) *gomatrix.Member { if len(room.memberCache) == 0 { room.createMemberCache() } @@ -432,8 +425,8 @@ func (room *Room) GetMember(userID string) *Member { } // GetSessionOwner returns the Member instance of the user whose session this room was created for. -func (room *Room) GetSessionOwner() *Member { - return room.GetMember(room.SessionUserID) +func (room *Room) GetSessionOwner() string { + return room.SessionUserID } // NewRoom creates a new Room with the given ID diff --git a/matrix/sync.go b/matrix/sync.go index 2037fcd..6d5def7 100644 --- a/matrix/sync.go +++ b/matrix/sync.go @@ -51,7 +51,7 @@ type EventHandler func(source EventSource, event *gomatrix.Event) // pattern to notify callers about incoming events. See GomuksSyncer.OnEventType for more information. type GomuksSyncer struct { Session SyncerSession - listeners map[string][]EventHandler // event type to listeners array + listeners map[gomatrix.EventType][]EventHandler // event type to listeners array FirstSyncDone bool InitDoneCallback func() } @@ -60,7 +60,7 @@ type GomuksSyncer struct { func NewGomuksSyncer(session SyncerSession) *GomuksSyncer { return &GomuksSyncer{ Session: session, - listeners: make(map[string][]EventHandler), + listeners: make(map[gomatrix.EventType][]EventHandler), FirstSyncDone: false, } } @@ -114,20 +114,11 @@ func (s *GomuksSyncer) processSyncEvents(room *rooms.Room, events []*gomatrix.Ev } } -func isState(event *gomatrix.Event) bool { - switch event.Type { - case "m.room.member", "m.room.name", "m.room.topic", "m.room.aliases", "m.room.canonical_alias": - return true - default: - return false - } -} - func (s *GomuksSyncer) processSyncEvent(room *rooms.Room, event *gomatrix.Event, source EventSource) { if room != nil { event.RoomID = room.ID } - if isState(event) { + if event.Type.Class == gomatrix.StateEventType { room.UpdateState(event) } s.notifyListeners(source, event) @@ -135,7 +126,7 @@ func (s *GomuksSyncer) processSyncEvent(room *rooms.Room, event *gomatrix.Event, // OnEventType allows callers to be notified when there are new events for the given event type. // There are no duplicate checks. -func (s *GomuksSyncer) OnEventType(eventType string, callback EventHandler) { +func (s *GomuksSyncer) OnEventType(eventType gomatrix.EventType, callback EventHandler) { _, exists := s.listeners[eventType] if !exists { s.listeners[eventType] = []EventHandler{} diff --git a/ui/commands.go b/ui/commands.go index 2827666..b80fa07 100644 --- a/ui/commands.go +++ b/ui/commands.go @@ -19,6 +19,7 @@ package ui import ( "encoding/json" "fmt" + "maunium.net/go/gomatrix" "strings" "unicode" @@ -132,7 +133,7 @@ func cmdSendEvent(cmd *Command) { return } roomID := cmd.Args[0] - eventType := cmd.Args[1] + eventType := gomatrix.NewEventType(cmd.Args[1]) rawContent := strings.Join(cmd.Args[2:], "") debug.Print(roomID, eventType, rawContent) @@ -161,7 +162,7 @@ func cmdSetState(cmd *Command) { } roomID := cmd.Args[0] - eventType := cmd.Args[1] + eventType := gomatrix.NewEventType(cmd.Args[1]) stateKey := cmd.Args[2] if stateKey == "-" { stateKey = "" diff --git a/ui/messages/base.go b/ui/messages/base.go index 6049ae3..c9da389 100644 --- a/ui/messages/base.go +++ b/ui/messages/base.go @@ -18,6 +18,7 @@ package messages import ( "encoding/gob" + "maunium.net/go/gomatrix" "time" "maunium.net/go/gomuks/config" @@ -33,7 +34,7 @@ func init() { type BaseMessage struct { MsgID string - MsgType string + MsgType gomatrix.MessageType MsgSenderID string MsgSender string MsgSenderColor tcell.Color @@ -47,7 +48,7 @@ type BaseMessage struct { prevPrefs config.UserPreferences } -func newBaseMessage(id, sender, displayname, msgtype string, timestamp time.Time) BaseMessage { +func newBaseMessage(id, sender, displayname string, msgtype gomatrix.MessageType, timestamp time.Time) BaseMessage { return BaseMessage{ MsgSenderID: sender, MsgSender: displayname, @@ -194,11 +195,11 @@ func (msg *BaseMessage) SetID(id string) { msg.MsgID = id } -func (msg *BaseMessage) Type() string { +func (msg *BaseMessage) Type() gomatrix.MessageType { return msg.MsgType } -func (msg *BaseMessage) SetType(msgtype string) { +func (msg *BaseMessage) SetType(msgtype gomatrix.MessageType) { msg.MsgType = msgtype } diff --git a/ui/messages/expandedtextmessage.go b/ui/messages/expandedtextmessage.go index c69f3dc..2e77a24 100644 --- a/ui/messages/expandedtextmessage.go +++ b/ui/messages/expandedtextmessage.go @@ -18,6 +18,7 @@ package messages import ( "encoding/gob" + "maunium.net/go/gomatrix" "time" "maunium.net/go/gomuks/config" @@ -34,7 +35,7 @@ type ExpandedTextMessage struct { } // NewExpandedTextMessage creates a new ExpandedTextMessage object with the provided values and the default state. -func NewExpandedTextMessage(id, sender, displayname, msgtype string, text tstring.TString, timestamp time.Time) UIMessage { +func NewExpandedTextMessage(id, sender, displayname string, msgtype gomatrix.MessageType, text tstring.TString, timestamp time.Time) UIMessage { return &ExpandedTextMessage{ BaseMessage: newBaseMessage(id, sender, displayname, msgtype, timestamp), MsgText: text, diff --git a/ui/messages/imagemessage.go b/ui/messages/imagemessage.go index dcbe1c5..a17c842 100644 --- a/ui/messages/imagemessage.go +++ b/ui/messages/imagemessage.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/gob" "fmt" + "maunium.net/go/gomatrix" "time" "image/color" @@ -47,7 +48,7 @@ type ImageMessage struct { } // NewImageMessage creates a new ImageMessage object with the provided values and the default state. -func NewImageMessage(matrix ifc.MatrixContainer, id, sender, displayname, msgtype, body, homeserver, fileID string, data []byte, timestamp time.Time) UIMessage { +func NewImageMessage(matrix ifc.MatrixContainer, id, sender, displayname string, msgtype gomatrix.MessageType, body, homeserver, fileID string, data []byte, timestamp time.Time) UIMessage { return &ImageMessage{ newBaseMessage(id, sender, displayname, msgtype, timestamp), body, diff --git a/ui/messages/parser/htmlparser.go b/ui/messages/parser/htmlparser.go index c8b000e..bcde14d 100644 --- a/ui/messages/parser/htmlparser.go +++ b/ui/messages/parser/htmlparser.go @@ -22,6 +22,7 @@ import ( "regexp" "strings" + "github.com/lucasb-eyer/go-colorful" "golang.org/x/net/html" "maunium.net/go/gomatrix" "maunium.net/go/gomuks/matrix/rooms" @@ -29,7 +30,6 @@ import ( "maunium.net/go/gomuks/ui/widget" "maunium.net/go/tcell" "strconv" - "github.com/lucasb-eyer/go-colorful" ) var matrixToURL = regexp.MustCompile("^(?:https?://)?(?:www\\.)?matrix\\.to/#/([#@!].*)") @@ -173,7 +173,7 @@ func (parser *htmlParser) linkToTString(node *html.Node, stripLinebreak bool) ts pillTarget := match[1] if pillTarget[0] == '@' { if member := parser.room.GetMember(pillTarget); member != nil { - return tstring.NewColorTString(member.DisplayName, widget.GetHashColor(member.UserID)) + return tstring.NewColorTString(member.Displayname, widget.GetHashColor(pillTarget)) } } return tstring.NewTString(pillTarget) @@ -271,14 +271,13 @@ func (parser *htmlParser) Parse(htmlData string) tstring.TString { // ParseHTMLMessage parses a HTML-formatted Matrix event into a UIMessage. func ParseHTMLMessage(room *rooms.Room, evt *gomatrix.Event, senderDisplayname string) tstring.TString { - htmlData, _ := evt.Content["formatted_body"].(string) + htmlData := evt.Content.FormattedBody htmlData = strings.Replace(htmlData, "\t", " ", -1) parser := htmlParser{room} str := parser.Parse(htmlData) - msgtype, _ := evt.Content["msgtype"].(string) - if msgtype == "m.emote" { + if evt.Content.MsgType == gomatrix.MsgEmote { str = tstring.Join([]tstring.TString{ tstring.NewTString("* "), tstring.NewColorTString(senderDisplayname, widget.GetHashColor(evt.Sender)), diff --git a/ui/messages/parser/parser.go b/ui/messages/parser/parser.go index c84a7f4..1d7ced3 100644 --- a/ui/messages/parser/parser.go +++ b/ui/messages/parser/parser.go @@ -33,9 +33,9 @@ import ( func ParseEvent(matrix ifc.MatrixContainer, room *rooms.Room, evt *gomatrix.Event) messages.UIMessage { switch evt.Type { - case "m.room.message": + case gomatrix.EventMessage: return ParseMessage(matrix, room, evt) - case "m.room.member": + case gomatrix.StateMember: return ParseMembershipEvent(room, evt) } return nil @@ -53,32 +53,28 @@ func ParseMessage(matrix ifc.MatrixContainer, room *rooms.Room, evt *gomatrix.Ev displayname := evt.Sender member := room.GetMember(evt.Sender) if member != nil { - displayname = member.DisplayName + displayname = member.Displayname } - msgtype, _ := evt.Content["msgtype"].(string) - text, _ := evt.Content["body"].(string) ts := unixToTime(evt.Timestamp) - switch msgtype { + switch evt.Content.MsgType { case "m.text", "m.notice", "m.emote": - format, hasFormat := evt.Content["format"].(string) - if hasFormat && format == "org.matrix.custom.html" { + if evt.Content.Format == gomatrix.FormatHTML { text := ParseHTMLMessage(room, evt, displayname) - return messages.NewExpandedTextMessage(evt.ID, evt.Sender, displayname, msgtype, text, ts) + return messages.NewExpandedTextMessage(evt.ID, evt.Sender, displayname, evt.Content.MsgType, text, ts) } - text = strings.Replace(text, "\t", " ", -1) - return messages.NewTextMessage(evt.ID, evt.Sender, displayname, msgtype, text, ts) + evt.Content.Body = strings.Replace(evt.Content.Body, "\t", " ", -1) + return messages.NewTextMessage(evt.ID, evt.Sender, displayname, evt.Content.MsgType, evt.Content.Body, ts) case "m.image": - url, _ := evt.Content["url"].(string) - data, hs, id, err := matrix.Download(url) + data, hs, id, err := matrix.Download(evt.Content.URL) if err != nil { - debug.Printf("Failed to download %s: %v", url, err) + debug.Printf("Failed to download %s: %v", evt.Content.URL, err) } - return messages.NewImageMessage(matrix, evt.ID, evt.Sender, displayname, msgtype, text, hs, id, data, ts) + return messages.NewImageMessage(matrix, evt.ID, evt.Sender, displayname, evt.Content.MsgType, evt.Content.Body, hs, id, data, ts) } return nil } -func getMembershipChangeMessage(evt *gomatrix.Event, membership, prevMembership, senderDisplayname, displayname, prevDisplayname string) (sender string, text tstring.TString) { +func getMembershipChangeMessage(evt *gomatrix.Event, membership, prevMembership gomatrix.Membership, senderDisplayname, displayname, prevDisplayname string) (sender string, text tstring.TString) { switch membership { case "invite": sender = "---" @@ -92,12 +88,11 @@ func getMembershipChangeMessage(evt *gomatrix.Event, membership, prevMembership, case "leave": sender = "<--" if evt.Sender != *evt.StateKey { - if prevMembership == "ban" { + if prevMembership == gomatrix.MembershipBan { text = tstring.NewColorTString(fmt.Sprintf("%s unbanned %s", senderDisplayname, displayname), tcell.ColorGreen) text.Colorize(len(senderDisplayname)+len(" unbanned "), len(displayname), widget.GetHashColor(*evt.StateKey)) } else { - reason, _ := evt.Content["reason"].(string) - text = tstring.NewColorTString(fmt.Sprintf("%s kicked %s: %s", senderDisplayname, displayname, reason), tcell.ColorRed) + text = tstring.NewColorTString(fmt.Sprintf("%s kicked %s: %s", senderDisplayname, displayname, evt.Content.Reason), tcell.ColorRed) text.Colorize(len(senderDisplayname)+len(" kicked "), len(displayname), widget.GetHashColor(*evt.StateKey)) } text.Colorize(0, len(senderDisplayname), widget.GetHashColor(evt.Sender)) @@ -109,8 +104,7 @@ func getMembershipChangeMessage(evt *gomatrix.Event, membership, prevMembership, text.Colorize(0, len(displayname), widget.GetHashColor(*evt.StateKey)) } case "ban": - reason, _ := evt.Content["reason"].(string) - text = tstring.NewColorTString(fmt.Sprintf("%s banned %s: %s", senderDisplayname, displayname, reason), tcell.ColorRed) + text = tstring.NewColorTString(fmt.Sprintf("%s banned %s: %s", senderDisplayname, displayname, evt.Content.Reason), tcell.ColorRed) text.Colorize(len(senderDisplayname)+len(" banned "), len(displayname), widget.GetHashColor(*evt.StateKey)) text.Colorize(0, len(senderDisplayname), widget.GetHashColor(evt.Sender)) } @@ -121,20 +115,20 @@ func getMembershipEventContent(room *rooms.Room, evt *gomatrix.Event) (sender st member := room.GetMember(evt.Sender) senderDisplayname := evt.Sender if member != nil { - senderDisplayname = member.DisplayName + senderDisplayname = member.Displayname } - membership, _ := evt.Content["membership"].(string) - displayname, _ := evt.Content["displayname"].(string) + membership := evt.Content.Membership + displayname := evt.Content.Displayname if len(displayname) == 0 { displayname = *evt.StateKey } - prevMembership := "leave" + prevMembership := gomatrix.MembershipLeave prevDisplayname := *evt.StateKey if evt.Unsigned.PrevContent != nil { - prevMembership, _ = evt.Unsigned.PrevContent["membership"].(string) - prevDisplayname, _ = evt.Unsigned.PrevContent["displayname"].(string) + prevMembership = evt.Unsigned.PrevContent.Membership + prevDisplayname = evt.Unsigned.PrevContent.Displayname if len(prevDisplayname) == 0 { prevDisplayname = *evt.StateKey } diff --git a/ui/messages/textmessage.go b/ui/messages/textmessage.go index d268688..d5ce324 100644 --- a/ui/messages/textmessage.go +++ b/ui/messages/textmessage.go @@ -19,6 +19,7 @@ package messages import ( "encoding/gob" "fmt" + "maunium.net/go/gomatrix" "time" "maunium.net/go/gomuks/config" @@ -37,7 +38,7 @@ type TextMessage struct { } // NewTextMessage creates a new UITextMessage object with the provided values and the default state. -func NewTextMessage(id, sender, displayname, msgtype, text string, timestamp time.Time) UIMessage { +func NewTextMessage(id, sender, displayname string, msgtype gomatrix.MessageType, text string, timestamp time.Time) UIMessage { return &TextMessage{ BaseMessage: newBaseMessage(id, sender, displayname, msgtype, timestamp), MsgText: text, @@ -57,7 +58,7 @@ func (msg *TextMessage) getCache() tstring.TString { return msg.cache } -func (msg *TextMessage) SetType(msgtype string) { +func (msg *TextMessage) SetType(msgtype gomatrix.MessageType) { msg.BaseMessage.SetType(msgtype) msg.cache = nil } diff --git a/ui/room-view.go b/ui/room-view.go index bb8f5ec..4b2cd48 100644 --- a/ui/room-view.go +++ b/ui/room-view.go @@ -18,6 +18,7 @@ package ui import ( "fmt" + "maunium.net/go/gomatrix" "path/filepath" "sort" "strconv" @@ -242,7 +243,7 @@ func (view *RoomView) SetTyping(users []string) { for index, user := range users { member := view.Room.GetMember(user) if member != nil { - users[index] = member.DisplayName + users[index] = member.Displayname } } view.typing = users @@ -255,14 +256,14 @@ type completion struct { func (view *RoomView) autocompleteUser(existingText string) (completions []completion) { textWithoutPrefix := strings.TrimPrefix(existingText, "@") - for _, user := range view.Room.GetMembers() { - if user.DisplayName == textWithoutPrefix || user.UserID == existingText { + for userID, user := range view.Room.GetMembers() { + if user.Displayname == textWithoutPrefix || userID == existingText { // Exact match, return that. - return []completion{{user.DisplayName, user.UserID}} + return []completion{{user.Displayname, userID}} } - if strings.HasPrefix(user.DisplayName, textWithoutPrefix) || strings.HasPrefix(user.UserID, existingText) { - completions = append(completions, completion{user.DisplayName, user.UserID}) + if strings.HasPrefix(user.Displayname, textWithoutPrefix) || strings.HasPrefix(userID, existingText) { + completions = append(completions, completion{user.Displayname, userID}) } } return @@ -330,12 +331,12 @@ func (view *RoomView) MxRoom() *rooms.Room { func (view *RoomView) UpdateUserList() { var joined strings.Builder var invited strings.Builder - for _, user := range view.Room.GetMembers() { + for userID, user := range view.Room.GetMembers() { if user.Membership == "join" { - joined.WriteString(widget.AddColor(user.DisplayName, widget.GetHashColorName(user.UserID))) + joined.WriteString(widget.AddColor(user.Displayname, widget.GetHashColorName(userID))) joined.WriteRune('\n') } else if user.Membership == "invite" { - invited.WriteString(widget.AddColor(user.DisplayName, widget.GetHashColorName(user.UserID))) + invited.WriteString(widget.AddColor(user.Displayname, widget.GetHashColorName(userID))) invited.WriteRune('\n') } } @@ -346,26 +347,26 @@ func (view *RoomView) UpdateUserList() { } } -func (view *RoomView) newUIMessage(id, sender, msgtype, text string, timestamp time.Time) messages.UIMessage { +func (view *RoomView) newUIMessage(id, sender string, msgtype gomatrix.MessageType, text string, timestamp time.Time) messages.UIMessage { member := view.Room.GetMember(sender) displayname := sender if member != nil { - displayname = member.DisplayName + displayname = member.Displayname } msg := messages.NewTextMessage(id, sender, displayname, msgtype, text, timestamp) return msg } -func (view *RoomView) NewMessage(id, sender, msgtype, text string, timestamp time.Time) ifc.Message { +func (view *RoomView) NewMessage(id, sender string, msgtype gomatrix.MessageType, text string, timestamp time.Time) ifc.Message { return view.newUIMessage(id, sender, msgtype, text, timestamp) } -func (view *RoomView) NewTempMessage(msgtype, text string) ifc.Message { +func (view *RoomView) NewTempMessage(msgtype gomatrix.MessageType, text string) ifc.Message { now := time.Now() id := strconv.FormatInt(now.UnixNano(), 10) sender := "" - if ownerMember := view.Room.GetSessionOwner(); ownerMember != nil { - sender = ownerMember.DisplayName + if ownerMember := view.Room.GetMember(view.Room.GetSessionOwner()); ownerMember != nil { + sender = ownerMember.Displayname } message := view.newUIMessage(id, sender, msgtype, text, now) message.SetState(ifc.MessageStateSending) diff --git a/ui/view-main.go b/ui/view-main.go index f38043a..c5abb19 100644 --- a/ui/view-main.go +++ b/ui/view-main.go @@ -497,7 +497,7 @@ func (view *MainView) LoadHistory(room string) { } roomView.Room.PrevBatch = prevBatch for _, evt := range history { - message := view.ParseEvent(roomView, &evt) + message := view.ParseEvent(roomView, evt) if message != nil { roomView.AddMessage(message, ifc.PrependMessage) } diff --git a/vendor/github.com/davecgh/go-spew/LICENSE b/vendor/github.com/davecgh/go-spew/LICENSE index c836416..bc52e96 100644 --- a/vendor/github.com/davecgh/go-spew/LICENSE +++ b/vendor/github.com/davecgh/go-spew/LICENSE @@ -2,7 +2,7 @@ ISC License Copyright (c) 2012-2016 Dave Collins -Permission to use, copy, modify, and distribute this software for any +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. diff --git a/vendor/github.com/davecgh/go-spew/spew/bypass.go b/vendor/github.com/davecgh/go-spew/spew/bypass.go index 8a4a658..7929947 100644 --- a/vendor/github.com/davecgh/go-spew/spew/bypass.go +++ b/vendor/github.com/davecgh/go-spew/spew/bypass.go @@ -16,7 +16,9 @@ // when the code is not running on Google App Engine, compiled by GopherJS, and // "-tags safe" is not added to the go build command line. The "disableunsafe" // tag is deprecated and thus should not be used. -// +build !js,!appengine,!safe,!disableunsafe +// Go versions prior to 1.4 are disabled because they use a different layout +// for interfaces which make the implementation of unsafeReflectValue more complex. +// +build !js,!appengine,!safe,!disableunsafe,go1.4 package spew @@ -34,80 +36,49 @@ const ( ptrSize = unsafe.Sizeof((*byte)(nil)) ) +type flag uintptr + var ( - // offsetPtr, offsetScalar, and offsetFlag are the offsets for the - // internal reflect.Value fields. These values are valid before golang - // commit ecccf07e7f9d which changed the format. The are also valid - // after commit 82f48826c6c7 which changed the format again to mirror - // the original format. Code in the init function updates these offsets - // as necessary. - offsetPtr = uintptr(ptrSize) - offsetScalar = uintptr(0) - offsetFlag = uintptr(ptrSize * 2) - - // flagKindWidth and flagKindShift indicate various bits that the - // reflect package uses internally to track kind information. - // - // flagRO indicates whether or not the value field of a reflect.Value is - // read-only. - // - // flagIndir indicates whether the value field of a reflect.Value is - // the actual data or a pointer to the data. - // - // These values are valid before golang commit 90a7c3c86944 which - // changed their positions. Code in the init function updates these - // flags as necessary. - flagKindWidth = uintptr(5) - flagKindShift = uintptr(flagKindWidth - 1) - flagRO = uintptr(1 << 0) - flagIndir = uintptr(1 << 1) + // flagRO indicates whether the value field of a reflect.Value + // is read-only. + flagRO flag + + // flagAddr indicates whether the address of the reflect.Value's + // value may be taken. + flagAddr flag ) -func init() { - // Older versions of reflect.Value stored small integers directly in the - // ptr field (which is named val in the older versions). Versions - // between commits ecccf07e7f9d and 82f48826c6c7 added a new field named - // scalar for this purpose which unfortunately came before the flag - // field, so the offset of the flag field is different for those - // versions. - // - // This code constructs a new reflect.Value from a known small integer - // and checks if the size of the reflect.Value struct indicates it has - // the scalar field. When it does, the offsets are updated accordingly. - vv := reflect.ValueOf(0xf00) - if unsafe.Sizeof(vv) == (ptrSize * 4) { - offsetScalar = ptrSize * 2 - offsetFlag = ptrSize * 3 - } +// flagKindMask holds the bits that make up the kind +// part of the flags field. In all the supported versions, +// it is in the lower 5 bits. +const flagKindMask = flag(0x1f) - // Commit 90a7c3c86944 changed the flag positions such that the low - // order bits are the kind. This code extracts the kind from the flags - // field and ensures it's the correct type. When it's not, the flag - // order has been changed to the newer format, so the flags are updated - // accordingly. - upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag) - upfv := *(*uintptr)(upf) - flagKindMask := uintptr((1<>flagKindShift != uintptr(reflect.Int) { - flagKindShift = 0 - flagRO = 1 << 5 - flagIndir = 1 << 6 - - // Commit adf9b30e5594 modified the flags to separate the - // flagRO flag into two bits which specifies whether or not the - // field is embedded. This causes flagIndir to move over a bit - // and means that flagRO is the combination of either of the - // original flagRO bit and the new bit. - // - // This code detects the change by extracting what used to be - // the indirect bit to ensure it's set. When it's not, the flag - // order has been changed to the newer format, so the flags are - // updated accordingly. - if upfv&flagIndir == 0 { - flagRO = 3 << 5 - flagIndir = 1 << 7 - } +// Different versions of Go have used different +// bit layouts for the flags type. This table +// records the known combinations. +var okFlags = []struct { + ro, addr flag +}{{ + // From Go 1.4 to 1.5 + ro: 1 << 5, + addr: 1 << 7, +}, { + // Up to Go tip. + ro: 1<<5 | 1<<6, + addr: 1 << 8, +}} + +var flagValOffset = func() uintptr { + field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") + if !ok { + panic("reflect.Value has no flag field") } + return field.Offset +}() + +// flagField returns a pointer to the flag field of a reflect.Value. +func flagField(v *reflect.Value) *flag { + return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) } // unsafeReflectValue converts the passed reflect.Value into a one that bypasses @@ -119,34 +90,56 @@ func init() { // This allows us to check for implementations of the Stringer and error // interfaces to be used for pretty printing ordinarily unaddressable and // inaccessible values such as unexported struct fields. -func unsafeReflectValue(v reflect.Value) (rv reflect.Value) { - indirects := 1 - vt := v.Type() - upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr) - rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag)) - if rvf&flagIndir != 0 { - vt = reflect.PtrTo(v.Type()) - indirects++ - } else if offsetScalar != 0 { - // The value is in the scalar field when it's not one of the - // reference types. - switch vt.Kind() { - case reflect.Uintptr: - case reflect.Chan: - case reflect.Func: - case reflect.Map: - case reflect.Ptr: - case reflect.UnsafePointer: - default: - upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + - offsetScalar) - } +func unsafeReflectValue(v reflect.Value) reflect.Value { + if !v.IsValid() || (v.CanInterface() && v.CanAddr()) { + return v } + flagFieldPtr := flagField(&v) + *flagFieldPtr &^= flagRO + *flagFieldPtr |= flagAddr + return v +} - pv := reflect.NewAt(vt, upv) - rv = pv - for i := 0; i < indirects; i++ { - rv = rv.Elem() +// Sanity checks against future reflect package changes +// to the type or semantics of the Value.flag field. +func init() { + field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") + if !ok { + panic("reflect.Value has no flag field") + } + if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { + panic("reflect.Value flag field has changed kind") + } + type t0 int + var t struct { + A t0 + // t0 will have flagEmbedRO set. + t0 + // a will have flagStickyRO set + a t0 + } + vA := reflect.ValueOf(t).FieldByName("A") + va := reflect.ValueOf(t).FieldByName("a") + vt0 := reflect.ValueOf(t).FieldByName("t0") + + // Infer flagRO from the difference between the flags + // for the (otherwise identical) fields in t. + flagPublic := *flagField(&vA) + flagWithRO := *flagField(&va) | *flagField(&vt0) + flagRO = flagPublic ^ flagWithRO + + // Infer flagAddr from the difference between a value + // taken from a pointer and not. + vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A") + flagNoPtr := *flagField(&vA) + flagPtr := *flagField(&vPtrA) + flagAddr = flagNoPtr ^ flagPtr + + // Check that the inferred flags tally with one of the known versions. + for _, f := range okFlags { + if flagRO == f.ro && flagAddr == f.addr { + return + } } - return rv + panic("reflect.Value read-only flag has changed semantics") } diff --git a/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go b/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go index 1fe3cf3..205c28d 100644 --- a/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go +++ b/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go @@ -16,7 +16,7 @@ // when the code is running on Google App Engine, compiled by GopherJS, or // "-tags safe" is added to the go build command line. The "disableunsafe" // tag is deprecated and thus should not be used. -// +build js appengine safe disableunsafe +// +build js appengine safe disableunsafe !go1.4 package spew diff --git a/vendor/github.com/davecgh/go-spew/spew/common.go b/vendor/github.com/davecgh/go-spew/spew/common.go index 7c519ff..1be8ce9 100644 --- a/vendor/github.com/davecgh/go-spew/spew/common.go +++ b/vendor/github.com/davecgh/go-spew/spew/common.go @@ -180,7 +180,7 @@ func printComplex(w io.Writer, c complex128, floatPrecision int) { w.Write(closeParenBytes) } -// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x' +// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x' // prefix to Writer w. func printHexPtr(w io.Writer, p uintptr) { // Null pointer. diff --git a/vendor/github.com/davecgh/go-spew/spew/dump.go b/vendor/github.com/davecgh/go-spew/spew/dump.go index df1d582..f78d89f 100644 --- a/vendor/github.com/davecgh/go-spew/spew/dump.go +++ b/vendor/github.com/davecgh/go-spew/spew/dump.go @@ -35,16 +35,16 @@ var ( // cCharRE is a regular expression that matches a cgo char. // It is used to detect character arrays to hexdump them. - cCharRE = regexp.MustCompile("^.*\\._Ctype_char$") + cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`) // cUnsignedCharRE is a regular expression that matches a cgo unsigned // char. It is used to detect unsigned character arrays to hexdump // them. - cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$") + cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`) // cUint8tCharRE is a regular expression that matches a cgo uint8_t. // It is used to detect uint8_t arrays to hexdump them. - cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$") + cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`) ) // dumpState contains information about the state of a dump operation. @@ -143,10 +143,10 @@ func (d *dumpState) dumpPtr(v reflect.Value) { // Display dereferenced value. d.w.Write(openParenBytes) switch { - case nilFound == true: + case nilFound: d.w.Write(nilAngleBytes) - case cycleFound == true: + case cycleFound: d.w.Write(circularBytes) default: diff --git a/vendor/github.com/davecgh/go-spew/spew/format.go b/vendor/github.com/davecgh/go-spew/spew/format.go index c49875b..b04edb7 100644 --- a/vendor/github.com/davecgh/go-spew/spew/format.go +++ b/vendor/github.com/davecgh/go-spew/spew/format.go @@ -182,10 +182,10 @@ func (f *formatState) formatPtr(v reflect.Value) { // Display dereferenced value. switch { - case nilFound == true: + case nilFound: f.fs.Write(nilAngleBytes) - case cycleFound == true: + case cycleFound: f.fs.Write(circularShortBytes) default: diff --git a/vendor/github.com/disintegration/imaging/effects.go b/vendor/github.com/disintegration/imaging/effects.go index b16781f..149cfeb 100644 --- a/vendor/github.com/disintegration/imaging/effects.go +++ b/vendor/github.com/disintegration/imaging/effects.go @@ -38,9 +38,13 @@ func blurHorizontal(img image.Image, kernel []float64) *image.NRGBA { parallel(0, src.h, func(ys <-chan int) { scanLine := make([]uint8, src.w*4) + scanLineF := make([]float64, len(scanLine)) for y := range ys { src.scan(0, y, src.w, y+1, scanLine) - for x := 0; x < src.w; x++ { + for i, v := range scanLine { + scanLineF[i] = float64(v) + } + for x, idx := 0, 0; x < src.w; x, idx = x+1, idx+4 { min := x - radius if min < 0 { min = 0 @@ -55,10 +59,10 @@ func blurHorizontal(img image.Image, kernel []float64) *image.NRGBA { i := ix * 4 weight := kernel[absint(x-ix)] wsum += weight - wa := float64(scanLine[i+3]) * weight - r += float64(scanLine[i+0]) * wa - g += float64(scanLine[i+1]) * wa - b += float64(scanLine[i+2]) * wa + wa := scanLineF[i+3] * weight + r += scanLineF[i+0] * wa + g += scanLineF[i+1] * wa + b += scanLineF[i+2] * wa a += wa } if a != 0 { @@ -67,12 +71,12 @@ func blurHorizontal(img image.Image, kernel []float64) *image.NRGBA { b /= a } - j := y*dst.Stride + x*4 - dst.Pix[j+0] = clamp(r) - dst.Pix[j+1] = clamp(g) - dst.Pix[j+2] = clamp(b) - dst.Pix[j+3] = clamp(a / wsum) + scanLine[idx+0] = clamp(r) + scanLine[idx+1] = clamp(g) + scanLine[idx+2] = clamp(b) + scanLine[idx+3] = clamp(a / wsum) } + copy(dst.Pix[y*dst.Stride:], scanLine) } }) @@ -86,8 +90,12 @@ func blurVertical(img image.Image, kernel []float64) *image.NRGBA { parallel(0, src.w, func(xs <-chan int) { scanLine := make([]uint8, src.h*4) + scanLineF := make([]float64, len(scanLine)) for x := range xs { src.scan(x, 0, x+1, src.h, scanLine) + for i, v := range scanLine { + scanLineF[i] = float64(v) + } for y := 0; y < src.h; y++ { min := y - radius if min < 0 { @@ -103,10 +111,10 @@ func blurVertical(img image.Image, kernel []float64) *image.NRGBA { i := iy * 4 weight := kernel[absint(y-iy)] wsum += weight - wa := float64(scanLine[i+3]) * weight - r += float64(scanLine[i+0]) * wa - g += float64(scanLine[i+1]) * wa - b += float64(scanLine[i+2]) * wa + wa := scanLineF[i+3] * weight + r += scanLineF[i+0] * wa + g += scanLineF[i+1] * wa + b += scanLineF[i+2] * wa a += wa } if a != 0 { diff --git a/vendor/github.com/disintegration/imaging/helpers.go b/vendor/github.com/disintegration/imaging/helpers.go deleted file mode 100644 index dcb4d7e..0000000 --- a/vendor/github.com/disintegration/imaging/helpers.go +++ /dev/null @@ -1,272 +0,0 @@ -package imaging - -import ( - "bytes" - "errors" - "image" - "image/color" - "image/draw" - "image/gif" - "image/jpeg" - "image/png" - "io" - "os" - "path/filepath" - "strings" - - "golang.org/x/image/bmp" - "golang.org/x/image/tiff" -) - -// Format is an image file format. -type Format int - -// Image file formats. -const ( - JPEG Format = iota - PNG - GIF - TIFF - BMP -) - -func (f Format) String() string { - switch f { - case JPEG: - return "JPEG" - case PNG: - return "PNG" - case GIF: - return "GIF" - case TIFF: - return "TIFF" - case BMP: - return "BMP" - default: - return "Unsupported" - } -} - -var formatFromExt = map[string]Format{ - ".jpg": JPEG, - ".jpeg": JPEG, - ".png": PNG, - ".tif": TIFF, - ".tiff": TIFF, - ".bmp": BMP, - ".gif": GIF, -} - -// FormatFromFilename parses image format from filename extension: -// "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported. -func FormatFromFilename(filename string) (Format, error) { - ext := strings.ToLower(filepath.Ext(filename)) - if f, ok := formatFromExt[ext]; ok { - return f, nil - } - return -1, ErrUnsupportedFormat -} - -var ( - // ErrUnsupportedFormat means the given image format (or file extension) is unsupported. - ErrUnsupportedFormat = errors.New("imaging: unsupported image format") -) - -type fileSystem interface { - Create(string) (io.WriteCloser, error) - Open(string) (io.ReadCloser, error) -} - -type localFS struct{} - -func (localFS) Create(name string) (io.WriteCloser, error) { return os.Create(name) } -func (localFS) Open(name string) (io.ReadCloser, error) { return os.Open(name) } - -var fs fileSystem = localFS{} - -// Decode reads an image from r. -func Decode(r io.Reader) (image.Image, error) { - img, _, err := image.Decode(r) - return img, err -} - -// Open loads an image from file -func Open(filename string) (image.Image, error) { - file, err := fs.Open(filename) - if err != nil { - return nil, err - } - defer file.Close() - return Decode(file) -} - -type encodeConfig struct { - jpegQuality int - gifNumColors int - gifQuantizer draw.Quantizer - gifDrawer draw.Drawer - pngCompressionLevel png.CompressionLevel -} - -var defaultEncodeConfig = encodeConfig{ - jpegQuality: 95, - gifNumColors: 256, - gifQuantizer: nil, - gifDrawer: nil, - pngCompressionLevel: png.DefaultCompression, -} - -// EncodeOption sets an optional parameter for the Encode and Save functions. -type EncodeOption func(*encodeConfig) - -// JPEGQuality returns an EncodeOption that sets the output JPEG quality. -// Quality ranges from 1 to 100 inclusive, higher is better. Default is 95. -func JPEGQuality(quality int) EncodeOption { - return func(c *encodeConfig) { - c.jpegQuality = quality - } -} - -// GIFNumColors returns an EncodeOption that sets the maximum number of colors -// used in the GIF-encoded image. It ranges from 1 to 256. Default is 256. -func GIFNumColors(numColors int) EncodeOption { - return func(c *encodeConfig) { - c.gifNumColors = numColors - } -} - -// GIFQuantizer returns an EncodeOption that sets the quantizer that is used to produce -// a palette of the GIF-encoded image. -func GIFQuantizer(quantizer draw.Quantizer) EncodeOption { - return func(c *encodeConfig) { - c.gifQuantizer = quantizer - } -} - -// GIFDrawer returns an EncodeOption that sets the drawer that is used to convert -// the source image to the desired palette of the GIF-encoded image. -func GIFDrawer(drawer draw.Drawer) EncodeOption { - return func(c *encodeConfig) { - c.gifDrawer = drawer - } -} - -// PNGCompressionLevel returns an EncodeOption that sets the compression level -// of the PNG-encoded image. Default is png.DefaultCompression. -func PNGCompressionLevel(level png.CompressionLevel) EncodeOption { - return func(c *encodeConfig) { - c.pngCompressionLevel = level - } -} - -// Encode writes the image img to w in the specified format (JPEG, PNG, GIF, TIFF or BMP). -func Encode(w io.Writer, img image.Image, format Format, opts ...EncodeOption) error { - cfg := defaultEncodeConfig - for _, option := range opts { - option(&cfg) - } - - var err error - switch format { - case JPEG: - var rgba *image.RGBA - if nrgba, ok := img.(*image.NRGBA); ok { - if nrgba.Opaque() { - rgba = &image.RGBA{ - Pix: nrgba.Pix, - Stride: nrgba.Stride, - Rect: nrgba.Rect, - } - } - } - if rgba != nil { - err = jpeg.Encode(w, rgba, &jpeg.Options{Quality: cfg.jpegQuality}) - } else { - err = jpeg.Encode(w, img, &jpeg.Options{Quality: cfg.jpegQuality}) - } - - case PNG: - enc := png.Encoder{CompressionLevel: cfg.pngCompressionLevel} - err = enc.Encode(w, img) - - case GIF: - err = gif.Encode(w, img, &gif.Options{ - NumColors: cfg.gifNumColors, - Quantizer: cfg.gifQuantizer, - Drawer: cfg.gifDrawer, - }) - - case TIFF: - err = tiff.Encode(w, img, &tiff.Options{Compression: tiff.Deflate, Predictor: true}) - - case BMP: - err = bmp.Encode(w, img) - - default: - err = ErrUnsupportedFormat - } - return err -} - -// Save saves the image to file with the specified filename. -// The format is determined from the filename extension: "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported. -// -// Examples: -// -// // Save the image as PNG. -// err := imaging.Save(img, "out.png") -// -// // Save the image as JPEG with optional quality parameter set to 80. -// err := imaging.Save(img, "out.jpg", imaging.JPEGQuality(80)) -// -func Save(img image.Image, filename string, opts ...EncodeOption) (err error) { - f, err := FormatFromFilename(filename) - if err != nil { - return err - } - file, err := fs.Create(filename) - if err != nil { - return err - } - - defer func() { - cerr := file.Close() - if err == nil { - err = cerr - } - }() - - return Encode(file, img, f, opts...) -} - -// New creates a new image with the specified width and height, and fills it with the specified color. -func New(width, height int, fillColor color.Color) *image.NRGBA { - if width <= 0 || height <= 0 { - return &image.NRGBA{} - } - - c := color.NRGBAModel.Convert(fillColor).(color.NRGBA) - if (c == color.NRGBA{0, 0, 0, 0}) { - return image.NewNRGBA(image.Rect(0, 0, width, height)) - } - - return &image.NRGBA{ - Pix: bytes.Repeat([]byte{c.R, c.G, c.B, c.A}, width*height), - Stride: 4 * width, - Rect: image.Rect(0, 0, width, height), - } -} - -// Clone returns a copy of the given image. -func Clone(img image.Image) *image.NRGBA { - src := newScanner(img) - dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) - size := src.w * 4 - parallel(0, src.h, func(ys <-chan int) { - for y := range ys { - i := y * dst.Stride - src.scan(0, y, src.w, y+1, dst.Pix[i:i+size]) - } - }) - return dst -} diff --git a/vendor/github.com/disintegration/imaging/io.go b/vendor/github.com/disintegration/imaging/io.go new file mode 100644 index 0000000..557bf2f --- /dev/null +++ b/vendor/github.com/disintegration/imaging/io.go @@ -0,0 +1,463 @@ +package imaging + +import ( + "encoding/binary" + "errors" + "image" + "image/draw" + "image/gif" + "image/jpeg" + "image/png" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "golang.org/x/image/bmp" + "golang.org/x/image/tiff" +) + +// Format is an image file format. +type Format int + +// Image file formats. +const ( + JPEG Format = iota + PNG + GIF + TIFF + BMP +) + +func (f Format) String() string { + switch f { + case JPEG: + return "JPEG" + case PNG: + return "PNG" + case GIF: + return "GIF" + case TIFF: + return "TIFF" + case BMP: + return "BMP" + default: + return "Unsupported" + } +} + +var formatFromExt = map[string]Format{ + "jpg": JPEG, + "jpeg": JPEG, + "png": PNG, + "tif": TIFF, + "tiff": TIFF, + "bmp": BMP, + "gif": GIF, +} + +// FormatFromExtension parses image format from extension: +// "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported. +func FormatFromExtension(ext string) (Format, error) { + if f, ok := formatFromExt[strings.ToLower(strings.TrimPrefix(ext, "."))]; ok { + return f, nil + } + return -1, ErrUnsupportedFormat +} + +// FormatFromFilename parses image format from filename extension: +// "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported. +func FormatFromFilename(filename string) (Format, error) { + ext := filepath.Ext(filename) + return FormatFromExtension(ext) +} + +var ( + // ErrUnsupportedFormat means the given image format (or file extension) is unsupported. + ErrUnsupportedFormat = errors.New("imaging: unsupported image format") +) + +type fileSystem interface { + Create(string) (io.WriteCloser, error) + Open(string) (io.ReadCloser, error) +} + +type localFS struct{} + +func (localFS) Create(name string) (io.WriteCloser, error) { return os.Create(name) } +func (localFS) Open(name string) (io.ReadCloser, error) { return os.Open(name) } + +var fs fileSystem = localFS{} + +type decodeConfig struct { + autoOrientation bool +} + +var defaultDecodeConfig = decodeConfig{ + autoOrientation: false, +} + +// DecodeOption sets an optional parameter for the Decode and Open functions. +type DecodeOption func(*decodeConfig) + +// AutoOrientation returns a DecodeOption that sets the auto-orientation mode. +// If auto-orientation is enabled, the image will be transformed after decoding +// according to the EXIF orientation tag (if present). By default it's disabled. +func AutoOrientation(enabled bool) DecodeOption { + return func(c *decodeConfig) { + c.autoOrientation = enabled + } +} + +// Decode reads an image from r. +func Decode(r io.Reader, opts ...DecodeOption) (image.Image, error) { + cfg := defaultDecodeConfig + for _, option := range opts { + option(&cfg) + } + + if !cfg.autoOrientation { + img, _, err := image.Decode(r) + return img, err + } + + var orient orientation + pr, pw := io.Pipe() + r = io.TeeReader(r, pw) + done := make(chan struct{}) + go func() { + defer close(done) + orient = readOrientation(pr) + io.Copy(ioutil.Discard, pr) + }() + + img, _, err := image.Decode(r) + pw.Close() + <-done + if err != nil { + return nil, err + } + + return fixOrientation(img, orient), nil +} + +// Open loads an image from file. +// +// Examples: +// +// // Load an image from file. +// img, err := imaging.Open("test.jpg") +// +// // Load an image and transform it depending on the EXIF orientation tag (if present). +// img, err := imaging.Open("test.jpg", imaging.AutoOrientation(true)) +// +func Open(filename string, opts ...DecodeOption) (image.Image, error) { + file, err := fs.Open(filename) + if err != nil { + return nil, err + } + defer file.Close() + return Decode(file, opts...) +} + +type encodeConfig struct { + jpegQuality int + gifNumColors int + gifQuantizer draw.Quantizer + gifDrawer draw.Drawer + pngCompressionLevel png.CompressionLevel +} + +var defaultEncodeConfig = encodeConfig{ + jpegQuality: 95, + gifNumColors: 256, + gifQuantizer: nil, + gifDrawer: nil, + pngCompressionLevel: png.DefaultCompression, +} + +// EncodeOption sets an optional parameter for the Encode and Save functions. +type EncodeOption func(*encodeConfig) + +// JPEGQuality returns an EncodeOption that sets the output JPEG quality. +// Quality ranges from 1 to 100 inclusive, higher is better. Default is 95. +func JPEGQuality(quality int) EncodeOption { + return func(c *encodeConfig) { + c.jpegQuality = quality + } +} + +// GIFNumColors returns an EncodeOption that sets the maximum number of colors +// used in the GIF-encoded image. It ranges from 1 to 256. Default is 256. +func GIFNumColors(numColors int) EncodeOption { + return func(c *encodeConfig) { + c.gifNumColors = numColors + } +} + +// GIFQuantizer returns an EncodeOption that sets the quantizer that is used to produce +// a palette of the GIF-encoded image. +func GIFQuantizer(quantizer draw.Quantizer) EncodeOption { + return func(c *encodeConfig) { + c.gifQuantizer = quantizer + } +} + +// GIFDrawer returns an EncodeOption that sets the drawer that is used to convert +// the source image to the desired palette of the GIF-encoded image. +func GIFDrawer(drawer draw.Drawer) EncodeOption { + return func(c *encodeConfig) { + c.gifDrawer = drawer + } +} + +// PNGCompressionLevel returns an EncodeOption that sets the compression level +// of the PNG-encoded image. Default is png.DefaultCompression. +func PNGCompressionLevel(level png.CompressionLevel) EncodeOption { + return func(c *encodeConfig) { + c.pngCompressionLevel = level + } +} + +// Encode writes the image img to w in the specified format (JPEG, PNG, GIF, TIFF or BMP). +func Encode(w io.Writer, img image.Image, format Format, opts ...EncodeOption) error { + cfg := defaultEncodeConfig + for _, option := range opts { + option(&cfg) + } + + var err error + switch format { + case JPEG: + var rgba *image.RGBA + if nrgba, ok := img.(*image.NRGBA); ok { + if nrgba.Opaque() { + rgba = &image.RGBA{ + Pix: nrgba.Pix, + Stride: nrgba.Stride, + Rect: nrgba.Rect, + } + } + } + if rgba != nil { + err = jpeg.Encode(w, rgba, &jpeg.Options{Quality: cfg.jpegQuality}) + } else { + err = jpeg.Encode(w, img, &jpeg.Options{Quality: cfg.jpegQuality}) + } + + case PNG: + enc := png.Encoder{CompressionLevel: cfg.pngCompressionLevel} + err = enc.Encode(w, img) + + case GIF: + err = gif.Encode(w, img, &gif.Options{ + NumColors: cfg.gifNumColors, + Quantizer: cfg.gifQuantizer, + Drawer: cfg.gifDrawer, + }) + + case TIFF: + err = tiff.Encode(w, img, &tiff.Options{Compression: tiff.Deflate, Predictor: true}) + + case BMP: + err = bmp.Encode(w, img) + + default: + err = ErrUnsupportedFormat + } + return err +} + +// Save saves the image to file with the specified filename. +// The format is determined from the filename extension: +// "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported. +// +// Examples: +// +// // Save the image as PNG. +// err := imaging.Save(img, "out.png") +// +// // Save the image as JPEG with optional quality parameter set to 80. +// err := imaging.Save(img, "out.jpg", imaging.JPEGQuality(80)) +// +func Save(img image.Image, filename string, opts ...EncodeOption) (err error) { + f, err := FormatFromFilename(filename) + if err != nil { + return err + } + file, err := fs.Create(filename) + if err != nil { + return err + } + + defer func() { + cerr := file.Close() + if err == nil { + err = cerr + } + }() + + return Encode(file, img, f, opts...) +} + +// orientation is an EXIF flag that specifies the transformation +// that should be applied to image to display it correctly. +type orientation int + +const ( + orientationUnspecified = 0 + orientationNormal = 1 + orientationFlipH = 2 + orientationRotate180 = 3 + orientationFlipV = 4 + orientationTranspose = 5 + orientationRotate270 = 6 + orientationTransverse = 7 + orientationRotate90 = 8 +) + +// readOrientation tries to read the orientation EXIF flag from image data in r. +// If the EXIF data block is not found or the orientation flag is not found +// or any other error occures while reading the data, it returns the +// orientationUnspecified (0) value. +func readOrientation(r io.Reader) orientation { + const ( + markerSOI = 0xffd8 + markerAPP1 = 0xffe1 + exifHeader = 0x45786966 + byteOrderBE = 0x4d4d + byteOrderLE = 0x4949 + orientationTag = 0x0112 + ) + + // Check if JPEG SOI marker is present. + var soi uint16 + if err := binary.Read(r, binary.BigEndian, &soi); err != nil { + return orientationUnspecified + } + if soi != markerSOI { + return orientationUnspecified // Missing JPEG SOI marker. + } + + // Find JPEG APP1 marker. + for { + var marker, size uint16 + if err := binary.Read(r, binary.BigEndian, &marker); err != nil { + return orientationUnspecified + } + if err := binary.Read(r, binary.BigEndian, &size); err != nil { + return orientationUnspecified + } + if marker>>8 != 0xff { + return orientationUnspecified // Invalid JPEG marker. + } + if marker == markerAPP1 { + break + } + if size < 2 { + return orientationUnspecified // Invalid block size. + } + if _, err := io.CopyN(ioutil.Discard, r, int64(size-2)); err != nil { + return orientationUnspecified + } + } + + // Check if EXIF header is present. + var header uint32 + if err := binary.Read(r, binary.BigEndian, &header); err != nil { + return orientationUnspecified + } + if header != exifHeader { + return orientationUnspecified + } + if _, err := io.CopyN(ioutil.Discard, r, 2); err != nil { + return orientationUnspecified + } + + // Read byte order information. + var ( + byteOrderTag uint16 + byteOrder binary.ByteOrder + ) + if err := binary.Read(r, binary.BigEndian, &byteOrderTag); err != nil { + return orientationUnspecified + } + switch byteOrderTag { + case byteOrderBE: + byteOrder = binary.BigEndian + case byteOrderLE: + byteOrder = binary.LittleEndian + default: + return orientationUnspecified // Invalid byte order flag. + } + if _, err := io.CopyN(ioutil.Discard, r, 2); err != nil { + return orientationUnspecified + } + + // Skip the EXIF offset. + var offset uint32 + if err := binary.Read(r, byteOrder, &offset); err != nil { + return orientationUnspecified + } + if offset < 8 { + return orientationUnspecified // Invalid offset value. + } + if _, err := io.CopyN(ioutil.Discard, r, int64(offset-8)); err != nil { + return orientationUnspecified + } + + // Read the number of tags. + var numTags uint16 + if err := binary.Read(r, byteOrder, &numTags); err != nil { + return orientationUnspecified + } + + // Find the orientation tag. + for i := 0; i < int(numTags); i++ { + var tag uint16 + if err := binary.Read(r, byteOrder, &tag); err != nil { + return orientationUnspecified + } + if tag != orientationTag { + if _, err := io.CopyN(ioutil.Discard, r, 10); err != nil { + return orientationUnspecified + } + continue + } + if _, err := io.CopyN(ioutil.Discard, r, 6); err != nil { + return orientationUnspecified + } + var val uint16 + if err := binary.Read(r, byteOrder, &val); err != nil { + return orientationUnspecified + } + if val < 1 || val > 8 { + return orientationUnspecified // Invalid tag value. + } + return orientation(val) + } + return orientationUnspecified // Missing orientation tag. +} + +// fixOrientation applies a transform to img corresponding to the given orientation flag. +func fixOrientation(img image.Image, o orientation) image.Image { + switch o { + case orientationNormal: + case orientationFlipH: + img = FlipH(img) + case orientationFlipV: + img = FlipV(img) + case orientationRotate90: + img = Rotate90(img) + case orientationRotate180: + img = Rotate180(img) + case orientationRotate270: + img = Rotate270(img) + case orientationTranspose: + img = Transpose(img) + case orientationTransverse: + img = Transverse(img) + } + return img +} diff --git a/vendor/github.com/disintegration/imaging/tools.go b/vendor/github.com/disintegration/imaging/tools.go index fae1fa1..7887946 100644 --- a/vendor/github.com/disintegration/imaging/tools.go +++ b/vendor/github.com/disintegration/imaging/tools.go @@ -1,10 +1,44 @@ package imaging import ( + "bytes" "image" + "image/color" "math" ) +// New creates a new image with the specified width and height, and fills it with the specified color. +func New(width, height int, fillColor color.Color) *image.NRGBA { + if width <= 0 || height <= 0 { + return &image.NRGBA{} + } + + c := color.NRGBAModel.Convert(fillColor).(color.NRGBA) + if (c == color.NRGBA{0, 0, 0, 0}) { + return image.NewNRGBA(image.Rect(0, 0, width, height)) + } + + return &image.NRGBA{ + Pix: bytes.Repeat([]byte{c.R, c.G, c.B, c.A}, width*height), + Stride: 4 * width, + Rect: image.Rect(0, 0, width, height), + } +} + +// Clone returns a copy of the given image. +func Clone(img image.Image) *image.NRGBA { + src := newScanner(img) + dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) + size := src.w * 4 + parallel(0, src.h, func(ys <-chan int) { + for y := range ys { + i := y * dst.Stride + src.scan(0, y, src.w, y+1, dst.Pix[i:i+size]) + } + }) + return dst +} + // Anchor is the anchor point for image alignment. type Anchor int diff --git a/vendor/github.com/lucasb-eyer/go-colorful/README.md b/vendor/github.com/lucasb-eyer/go-colorful/README.md index c170435..8c89c38 100644 --- a/vendor/github.com/lucasb-eyer/go-colorful/README.md +++ b/vendor/github.com/lucasb-eyer/go-colorful/README.md @@ -124,17 +124,16 @@ Because a `colorful.Color` implements Go's `color.Color` interface (found in the `image/color` package), it can be used anywhere that expects a `color.Color`. Furthermore, you can convert anything that implements the `color.Color` interface -into a `colorful.Color` using the `MakeColorful` function: +into a `colorful.Color` using the `MakeColor` function: ```go -c := colorful.MakeColor(color.Gray16{12345}) +c, ok := colorful.MakeColor(color.Gray16{12345}) ``` -**Caveat:** Be aware that for this latter conversion (using `MakeColor`) hits a corner-case -when alpha is exactly zero. Because `color.Color` uses pre-multiplied alpha colors, -this means the RGB values are lost and it's impossible to recover them. The current -implementation `panic()`s in that case, see [issue 21](https://github.com/lucasb-eyer/go-colorful/issues/21) -for discussion and suggesting alternatives. +**Caveat:** Be aware that this latter conversion (using `MakeColor`) hits a +corner-case when alpha is exactly zero. Because `color.Color` uses pre-multiplied +alpha colors, this means the RGB values are lost (set to 0) and it's impossible +to recover them. In such a case `MakeColor` will return `false` as its second value. ### Comparing colors In the RGB color space, the Euclidian distance between colors *doesn't* correspond @@ -456,16 +455,26 @@ but that is outside the scope of this library. Thanks to [@ZirconiumX](https://github.com/ZirconiumX) for starting this investigation, see [issue #18](https://github.com/lucasb-eyer/go-colorful/issues/18) for details. -### Q: `MakeColor` panics when alpha is zero! -A: Because in that case, the conversion is undefined. See -[issue 21](https://github.com/lucasb-eyer/go-colorful/issues/21) -as well as the short caveat discussion in the ["The `color.Color` interface"](README.md#the-colorcolor-interface) section above. +### Q: Why would `MakeColor` ever fail!? +A: `MakeColor` fails when the alpha channel is zero. In that case, the +conversion is undefined. See [issue 21](https://github.com/lucasb-eyer/go-colorful/issues/21) +as well as the short caveat note in the ["The `color.Color` interface"](README.md#the-colorcolor-interface) +section above. Who? ==== This library has been developed by Lucas Beyer with contributions from -Bastien Dejean (@baskerville) and Phil Kulak (@pkulak). +Bastien Dejean (@baskerville), Phil Kulak (@pkulak) and Christian Muehlhaeuser (@muesli). + +Release Notes +============= + +### Version 1.0 +- API Breaking change in `MakeColor`: instead of `panic`ing when alpha is zero, it now returns a secondary, boolean return value indicating success. See [the color.Color interface](https://github.com/lucasb-eyer/go-colorful#the-colorcolor-interface) section and [this FAQ entry](https://github.com/lucasb-eyer/go-colorful#q-why-would-makecolor-ever-fail) for details. + +### Version 0.9 +- Initial version number after having ignored versioning for a long time :) License: MIT ============ diff --git a/vendor/github.com/lucasb-eyer/go-colorful/colors.go b/vendor/github.com/lucasb-eyer/go-colorful/colors.go index 7469cf7..febf94c 100644 --- a/vendor/github.com/lucasb-eyer/go-colorful/colors.go +++ b/vendor/github.com/lucasb-eyer/go-colorful/colors.go @@ -22,8 +22,11 @@ func (col Color) RGBA() (r, g, b, a uint32) { } // Constructs a colorful.Color from something implementing color.Color -func MakeColor(col color.Color) Color { +func MakeColor(col color.Color) (Color, bool) { r, g, b, a := col.RGBA() + if a == 0 { + return Color{0, 0, 0}, false + } // Since color.Color is alpha pre-multiplied, we need to divide the // RGB values by alpha again in order to get back the original RGB. @@ -34,7 +37,7 @@ func MakeColor(col color.Color) Color { b *= 0xffff b /= a - return Color{float64(r) / 65535.0, float64(g) / 65535.0, float64(b) / 65535.0} + return Color{float64(r) / 65535.0, float64(g) / 65535.0, float64(b) / 65535.0}, true } // Might come in handy sometimes to reduce boilerplate code. diff --git a/vendor/github.com/mattn/go-runewidth/runewidth.go b/vendor/github.com/mattn/go-runewidth/runewidth.go index 2164497..82568a1 100644 --- a/vendor/github.com/mattn/go-runewidth/runewidth.go +++ b/vendor/github.com/mattn/go-runewidth/runewidth.go @@ -1,13 +1,24 @@ package runewidth +import "os" + var ( // EastAsianWidth will be set true if the current locale is CJK - EastAsianWidth = IsEastAsian() + EastAsianWidth bool // DefaultCondition is a condition in current locale DefaultCondition = &Condition{EastAsianWidth} ) +func init() { + env := os.Getenv("RUNEWIDTH_EASTASIAN") + if env == "" { + EastAsianWidth = IsEastAsian() + } else { + EastAsianWidth = env == "1" + } +} + type interval struct { first rune last rune @@ -55,6 +66,7 @@ var private = table{ var nonprint = table{ {0x0000, 0x001F}, {0x007F, 0x009F}, {0x00AD, 0x00AD}, {0x070F, 0x070F}, {0x180B, 0x180E}, {0x200B, 0x200F}, + {0x2028, 0x2029}, {0x202A, 0x202E}, {0x206A, 0x206F}, {0xD800, 0xDFFF}, {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFE, 0xFFFF}, } diff --git a/vendor/github.com/renstrom/fuzzysearch/LICENSE b/vendor/github.com/renstrom/fuzzysearch/LICENSE index 9cc7533..dee3d1d 100644 --- a/vendor/github.com/renstrom/fuzzysearch/LICENSE +++ b/vendor/github.com/renstrom/fuzzysearch/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 Peter Renström +Copyright (c) 2018 Peter Lithammer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go b/vendor/github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go index 76b1c6e..33d4898 100644 --- a/vendor/github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go +++ b/vendor/github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go @@ -112,10 +112,7 @@ Outer: } // Count up remaining char - for len(target) > 0 { - target = target[utf8.RuneLen(rune(target[0])):] - runeDiff++ - } + runeDiff += utf8.RuneCountInString(target) return runeDiff } diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index ae06a54..aa1c2b9 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -13,6 +13,9 @@ import ( // Conditionf uses a Comparison to assert a complex condition. func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return Condition(t, comp, append([]interface{}{msg}, args...)...) } @@ -23,11 +26,17 @@ func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bo // assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") // assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return Contains(t, s, contains, append([]interface{}{msg}, args...)...) } // DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return DirExists(t, path, append([]interface{}{msg}, args...)...) } @@ -37,6 +46,9 @@ func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool { // // assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return ElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...) } @@ -45,6 +57,9 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string // // assert.Emptyf(t, obj, "error message %s", "formatted") func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return Empty(t, object, append([]interface{}{msg}, args...)...) } @@ -56,6 +71,9 @@ func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) boo // referenced values (as opposed to the memory addresses). Function equality // cannot be determined and will always fail. func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return Equal(t, expected, actual, append([]interface{}{msg}, args...)...) } @@ -65,6 +83,9 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar // actualObj, err := SomeFunction() // assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...) } @@ -73,6 +94,9 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args // // assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123)) func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return EqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) } @@ -83,6 +107,9 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri // assert.Equal(t, expectedErrorf, err) // } func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return Error(t, err, append([]interface{}{msg}, args...)...) } @@ -90,16 +117,25 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { // // assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123)) func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return Exactly(t, expected, actual, append([]interface{}{msg}, args...)...) } // Failf reports a failure through func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return Fail(t, failureMessage, append([]interface{}{msg}, args...)...) } // FailNowf fails test func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return FailNow(t, failureMessage, append([]interface{}{msg}, args...)...) } @@ -107,31 +143,43 @@ func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{} // // assert.Falsef(t, myBool, "error message %s", "formatted") func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return False(t, value, append([]interface{}{msg}, args...)...) } // FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return FileExists(t, path, append([]interface{}{msg}, args...)...) } // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContainsf(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return HTTPBodyContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...) } // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContainsf(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return HTTPBodyNotContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...) } @@ -141,6 +189,9 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u // // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return HTTPError(t, handler, method, url, values, append([]interface{}{msg}, args...)...) } @@ -150,6 +201,9 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, // // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...) } @@ -159,6 +213,9 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return HTTPSuccess(t, handler, method, url, values, append([]interface{}{msg}, args...)...) } @@ -166,6 +223,9 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin // // assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return Implements(t, interfaceObject, object, append([]interface{}{msg}, args...)...) } @@ -173,31 +233,49 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms // // assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return InDelta(t, expected, actual, delta, append([]interface{}{msg}, args...)...) } // InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. func InDeltaMapValuesf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return InDeltaMapValues(t, expected, actual, delta, append([]interface{}{msg}, args...)...) } // InDeltaSlicef is the same as InDelta, except it compares two slices. func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return InDeltaSlice(t, expected, actual, delta, append([]interface{}{msg}, args...)...) } // InEpsilonf asserts that expected and actual have a relative error less than epsilon func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return InEpsilon(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) } // InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) } // IsTypef asserts that the specified objects are of the same type. func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return IsType(t, expectedType, object, append([]interface{}{msg}, args...)...) } @@ -205,6 +283,9 @@ func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg strin // // assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...) } @@ -213,6 +294,9 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int // // assert.Lenf(t, mySlice, 3, "error message %s", "formatted") func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return Len(t, object, length, append([]interface{}{msg}, args...)...) } @@ -220,6 +304,9 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf // // assert.Nilf(t, err, "error message %s", "formatted") func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return Nil(t, object, append([]interface{}{msg}, args...)...) } @@ -230,6 +317,9 @@ func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool // assert.Equal(t, expectedObj, actualObj) // } func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return NoError(t, err, append([]interface{}{msg}, args...)...) } @@ -240,6 +330,9 @@ func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool { // assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") // assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return NotContains(t, s, contains, append([]interface{}{msg}, args...)...) } @@ -250,6 +343,9 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a // assert.Equal(t, "two", obj[1]) // } func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return NotEmpty(t, object, append([]interface{}{msg}, args...)...) } @@ -260,6 +356,9 @@ func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...) } @@ -267,6 +366,9 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, // // assert.NotNilf(t, err, "error message %s", "formatted") func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return NotNil(t, object, append([]interface{}{msg}, args...)...) } @@ -274,6 +376,9 @@ func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bo // // assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return NotPanics(t, f, append([]interface{}{msg}, args...)...) } @@ -282,6 +387,9 @@ func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bo // assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") // assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...) } @@ -290,11 +398,17 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. // // assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return NotSubset(t, list, subset, append([]interface{}{msg}, args...)...) } // NotZerof asserts that i is not the zero value for its type. func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return NotZero(t, i, append([]interface{}{msg}, args...)...) } @@ -302,6 +416,9 @@ func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { // // assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return Panics(t, f, append([]interface{}{msg}, args...)...) } @@ -310,6 +427,9 @@ func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool // // assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...) } @@ -318,6 +438,9 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str // assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") // assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return Regexp(t, rx, str, append([]interface{}{msg}, args...)...) } @@ -326,6 +449,9 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in // // assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return Subset(t, list, subset, append([]interface{}{msg}, args...)...) } @@ -333,6 +459,9 @@ func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args // // assert.Truef(t, myBool, "error message %s", "formatted") func Truef(t TestingT, value bool, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return True(t, value, append([]interface{}{msg}, args...)...) } @@ -340,10 +469,16 @@ func Truef(t TestingT, value bool, msg string, args ...interface{}) bool { // // assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...) } // Zerof asserts that i is the zero value for its type. func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return Zero(t, i, append([]interface{}{msg}, args...)...) } diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl b/vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl index c5cc66f..d2bb0b8 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl @@ -1,4 +1,5 @@ {{.CommentFormat}} func {{.DocInfo.Name}}f(t TestingT, {{.ParamsFormat}}) bool { + if h, ok := t.(tHelper); ok { h.Helper() } return {{.DocInfo.Name}}(t, {{.ForwardedParamsFormat}}) } diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index ffa5428..de39f79 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -13,11 +13,17 @@ import ( // Condition uses a Comparison to assert a complex condition. func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Condition(a.t, comp, msgAndArgs...) } // Conditionf uses a Comparison to assert a complex condition. func (a *Assertions) Conditionf(comp Comparison, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Conditionf(a.t, comp, msg, args...) } @@ -28,6 +34,9 @@ func (a *Assertions) Conditionf(comp Comparison, msg string, args ...interface{} // a.Contains(["Hello", "World"], "World") // a.Contains({"Hello": "World"}, "Hello") func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Contains(a.t, s, contains, msgAndArgs...) } @@ -38,16 +47,25 @@ func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs .. // a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") // a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Containsf(a.t, s, contains, msg, args...) } // DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return DirExists(a.t, path, msgAndArgs...) } // DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return DirExistsf(a.t, path, msg, args...) } @@ -57,6 +75,9 @@ func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) bo // // a.ElementsMatch([1, 3, 2, 3], [1, 3, 3, 2]) func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return ElementsMatch(a.t, listA, listB, msgAndArgs...) } @@ -66,6 +87,9 @@ func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndA // // a.ElementsMatchf([1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return ElementsMatchf(a.t, listA, listB, msg, args...) } @@ -74,6 +98,9 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st // // a.Empty(obj) func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Empty(a.t, object, msgAndArgs...) } @@ -82,6 +109,9 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { // // a.Emptyf(obj, "error message %s", "formatted") func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Emptyf(a.t, object, msg, args...) } @@ -93,6 +123,9 @@ func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) // referenced values (as opposed to the memory addresses). Function equality // cannot be determined and will always fail. func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Equal(a.t, expected, actual, msgAndArgs...) } @@ -102,6 +135,9 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs // actualObj, err := SomeFunction() // a.EqualError(err, expectedErrorString) func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return EqualError(a.t, theError, errString, msgAndArgs...) } @@ -111,6 +147,9 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ... // actualObj, err := SomeFunction() // a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return EqualErrorf(a.t, theError, errString, msg, args...) } @@ -119,6 +158,9 @@ func (a *Assertions) EqualErrorf(theError error, errString string, msg string, a // // a.EqualValues(uint32(123), int32(123)) func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return EqualValues(a.t, expected, actual, msgAndArgs...) } @@ -127,6 +169,9 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn // // a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123)) func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return EqualValuesf(a.t, expected, actual, msg, args...) } @@ -138,6 +183,9 @@ func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg // referenced values (as opposed to the memory addresses). Function equality // cannot be determined and will always fail. func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Equalf(a.t, expected, actual, msg, args...) } @@ -148,6 +196,9 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string // assert.Equal(t, expectedError, err) // } func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Error(a.t, err, msgAndArgs...) } @@ -158,6 +209,9 @@ func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { // assert.Equal(t, expectedErrorf, err) // } func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Errorf(a.t, err, msg, args...) } @@ -165,6 +219,9 @@ func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { // // a.Exactly(int32(123), int64(123)) func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Exactly(a.t, expected, actual, msgAndArgs...) } @@ -172,26 +229,41 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg // // a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123)) func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Exactlyf(a.t, expected, actual, msg, args...) } // Fail reports a failure through func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Fail(a.t, failureMessage, msgAndArgs...) } // FailNow fails test func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return FailNow(a.t, failureMessage, msgAndArgs...) } // FailNowf fails test func (a *Assertions) FailNowf(failureMessage string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return FailNowf(a.t, failureMessage, msg, args...) } // Failf reports a failure through func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Failf(a.t, failureMessage, msg, args...) } @@ -199,6 +271,9 @@ func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{ // // a.False(myBool) func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return False(a.t, value, msgAndArgs...) } @@ -206,56 +281,77 @@ func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { // // a.Falsef(myBool, "error message %s", "formatted") func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Falsef(a.t, value, msg, args...) } // FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return FileExists(a.t, path, msgAndArgs...) } // FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return FileExistsf(a.t, path, msg, args...) } // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// a.HTTPBodyContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky") +// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...) } // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// a.HTTPBodyContainsf(myHandler, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return HTTPBodyContainsf(a.t, handler, method, url, values, str, msg, args...) } // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// a.HTTPBodyNotContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky") +// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...) } // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// a.HTTPBodyNotContainsf(myHandler, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return HTTPBodyNotContainsf(a.t, handler, method, url, values, str, msg, args...) } @@ -265,6 +361,9 @@ func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method strin // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return HTTPError(a.t, handler, method, url, values, msgAndArgs...) } @@ -274,6 +373,9 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri // // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return HTTPErrorf(a.t, handler, method, url, values, msg, args...) } @@ -283,6 +385,9 @@ func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url str // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...) } @@ -292,6 +397,9 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s // // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return HTTPRedirectf(a.t, handler, method, url, values, msg, args...) } @@ -301,6 +409,9 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...) } @@ -310,6 +421,9 @@ func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url st // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return HTTPSuccessf(a.t, handler, method, url, values, msg, args...) } @@ -317,6 +431,9 @@ func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url s // // a.Implements((*MyInterface)(nil), new(MyObject)) func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Implements(a.t, interfaceObject, object, msgAndArgs...) } @@ -324,6 +441,9 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, // // a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Implementsf(a.t, interfaceObject, object, msg, args...) } @@ -331,26 +451,41 @@ func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{} // // a.InDelta(math.Pi, (22 / 7.0), 0.01) func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return InDelta(a.t, expected, actual, delta, msgAndArgs...) } // InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. func (a *Assertions) InDeltaMapValues(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return InDeltaMapValues(a.t, expected, actual, delta, msgAndArgs...) } // InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. func (a *Assertions) InDeltaMapValuesf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return InDeltaMapValuesf(a.t, expected, actual, delta, msg, args...) } // InDeltaSlice is the same as InDelta, except it compares two slices. func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) } // InDeltaSlicef is the same as InDelta, except it compares two slices. func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return InDeltaSlicef(a.t, expected, actual, delta, msg, args...) } @@ -358,36 +493,57 @@ func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, del // // a.InDeltaf(math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return InDeltaf(a.t, expected, actual, delta, msg, args...) } // InEpsilon asserts that expected and actual have a relative error less than epsilon func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) } // InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) } // InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. func (a *Assertions) InEpsilonSlicef(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return InEpsilonSlicef(a.t, expected, actual, epsilon, msg, args...) } // InEpsilonf asserts that expected and actual have a relative error less than epsilon func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return InEpsilonf(a.t, expected, actual, epsilon, msg, args...) } // IsType asserts that the specified objects are of the same type. func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return IsType(a.t, expectedType, object, msgAndArgs...) } // IsTypef asserts that the specified objects are of the same type. func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return IsTypef(a.t, expectedType, object, msg, args...) } @@ -395,6 +551,9 @@ func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg s // // a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return JSONEq(a.t, expected, actual, msgAndArgs...) } @@ -402,6 +561,9 @@ func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interf // // a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return JSONEqf(a.t, expected, actual, msg, args...) } @@ -410,6 +572,9 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. // // a.Len(mySlice, 3) func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Len(a.t, object, length, msgAndArgs...) } @@ -418,6 +583,9 @@ func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface // // a.Lenf(mySlice, 3, "error message %s", "formatted") func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Lenf(a.t, object, length, msg, args...) } @@ -425,6 +593,9 @@ func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...in // // a.Nil(err) func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Nil(a.t, object, msgAndArgs...) } @@ -432,6 +603,9 @@ func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { // // a.Nilf(err, "error message %s", "formatted") func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Nilf(a.t, object, msg, args...) } @@ -442,6 +616,9 @@ func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) b // assert.Equal(t, expectedObj, actualObj) // } func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return NoError(a.t, err, msgAndArgs...) } @@ -452,6 +629,9 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { // assert.Equal(t, expectedObj, actualObj) // } func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return NoErrorf(a.t, err, msg, args...) } @@ -462,6 +642,9 @@ func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool { // a.NotContains(["Hello", "World"], "Earth") // a.NotContains({"Hello": "World"}, "Earth") func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return NotContains(a.t, s, contains, msgAndArgs...) } @@ -472,6 +655,9 @@ func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs // a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") // a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return NotContainsf(a.t, s, contains, msg, args...) } @@ -482,6 +668,9 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin // assert.Equal(t, "two", obj[1]) // } func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return NotEmpty(a.t, object, msgAndArgs...) } @@ -492,6 +681,9 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) boo // assert.Equal(t, "two", obj[1]) // } func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return NotEmptyf(a.t, object, msg, args...) } @@ -502,6 +694,9 @@ func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return NotEqual(a.t, expected, actual, msgAndArgs...) } @@ -512,6 +707,9 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return NotEqualf(a.t, expected, actual, msg, args...) } @@ -519,6 +717,9 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str // // a.NotNil(err) func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return NotNil(a.t, object, msgAndArgs...) } @@ -526,6 +727,9 @@ func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool // // a.NotNilf(err, "error message %s", "formatted") func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return NotNilf(a.t, object, msg, args...) } @@ -533,6 +737,9 @@ func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{} // // a.NotPanics(func(){ RemainCalm() }) func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return NotPanics(a.t, f, msgAndArgs...) } @@ -540,6 +747,9 @@ func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool // // a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return NotPanicsf(a.t, f, msg, args...) } @@ -548,6 +758,9 @@ func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{} // a.NotRegexp(regexp.MustCompile("starts"), "it's starting") // a.NotRegexp("^start", "it's not starting") func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return NotRegexp(a.t, rx, str, msgAndArgs...) } @@ -556,6 +769,9 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in // a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") // a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return NotRegexpf(a.t, rx, str, msg, args...) } @@ -564,6 +780,9 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg // // a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return NotSubset(a.t, list, subset, msgAndArgs...) } @@ -572,16 +791,25 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs // // a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return NotSubsetf(a.t, list, subset, msg, args...) } // NotZero asserts that i is not the zero value for its type. func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return NotZero(a.t, i, msgAndArgs...) } // NotZerof asserts that i is not the zero value for its type. func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return NotZerof(a.t, i, msg, args...) } @@ -589,6 +817,9 @@ func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) bo // // a.Panics(func(){ GoCrazy() }) func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Panics(a.t, f, msgAndArgs...) } @@ -597,6 +828,9 @@ func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { // // a.PanicsWithValue("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return PanicsWithValue(a.t, expected, f, msgAndArgs...) } @@ -605,6 +839,9 @@ func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgA // // a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return PanicsWithValuef(a.t, expected, f, msg, args...) } @@ -612,6 +849,9 @@ func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg // // a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Panicsf(a.t, f, msg, args...) } @@ -620,6 +860,9 @@ func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) b // a.Regexp(regexp.MustCompile("start"), "it's starting") // a.Regexp("start...$", "it's not starting") func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Regexp(a.t, rx, str, msgAndArgs...) } @@ -628,6 +871,9 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter // a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") // a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Regexpf(a.t, rx, str, msg, args...) } @@ -636,6 +882,9 @@ func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args . // // a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Subset(a.t, list, subset, msgAndArgs...) } @@ -644,6 +893,9 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... // // a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Subsetf(a.t, list, subset, msg, args...) } @@ -651,6 +903,9 @@ func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, a // // a.True(myBool) func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return True(a.t, value, msgAndArgs...) } @@ -658,6 +913,9 @@ func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { // // a.Truef(myBool, "error message %s", "formatted") func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Truef(a.t, value, msg, args...) } @@ -665,6 +923,9 @@ func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool { // // a.WithinDuration(time.Now(), time.Now(), 10*time.Second) func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return WithinDuration(a.t, expected, actual, delta, msgAndArgs...) } @@ -672,15 +933,24 @@ func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta // // a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return WithinDurationf(a.t, expected, actual, delta, msg, args...) } // Zero asserts that i is the zero value for its type. func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Zero(a.t, i, msgAndArgs...) } // Zerof asserts that i is the zero value for its type. func (a *Assertions) Zerof(i interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } return Zerof(a.t, i, msg, args...) } diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl b/vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl index 99f9acf..188bb9e 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl @@ -1,4 +1,5 @@ {{.CommentWithoutT "a"}} func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool { + if h, ok := a.t.(tHelper); ok { h.Helper() } return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) } diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index 47bda77..5bdec56 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -27,6 +27,22 @@ type TestingT interface { Errorf(format string, args ...interface{}) } +// ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful +// for table driven tests. +type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) bool + +// ValueAssertionFunc is a common function prototype when validating a single value. Can be useful +// for table driven tests. +type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) bool + +// BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful +// for table driven tests. +type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool + +// ValuesAssertionFunc is a common function prototype when validating an error value. Can be useful +// for table driven tests. +type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool + // Comparison a custom function that returns true on success and false on failure type Comparison func() (success bool) @@ -38,21 +54,23 @@ type Comparison func() (success bool) // // This function does no assertion of any kind. func ObjectsAreEqual(expected, actual interface{}) bool { - if expected == nil || actual == nil { return expected == actual } - if exp, ok := expected.([]byte); ok { - act, ok := actual.([]byte) - if !ok { - return false - } else if exp == nil || act == nil { - return exp == nil && act == nil - } - return bytes.Equal(exp, act) + + exp, ok := expected.([]byte) + if !ok { + return reflect.DeepEqual(expected, actual) } - return reflect.DeepEqual(expected, actual) + act, ok := actual.([]byte) + if !ok { + return false + } + if exp == nil || act == nil { + return exp == nil && act == nil + } + return bytes.Equal(exp, act) } // ObjectsAreEqualValues gets whether two objects are equal, or if their @@ -156,21 +174,6 @@ func isTest(name, prefix string) bool { return !unicode.IsLower(rune) } -// getWhitespaceString returns a string that is long enough to overwrite the default -// output from the go testing framework. -func getWhitespaceString() string { - - _, file, line, ok := runtime.Caller(1) - if !ok { - return "" - } - parts := strings.Split(file, "/") - file = parts[len(parts)-1] - - return strings.Repeat(" ", len(fmt.Sprintf("%s:%d: ", file, line))) - -} - func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { if len(msgAndArgs) == 0 || msgAndArgs == nil { return "" @@ -195,7 +198,7 @@ func indentMessageLines(message string, longestLabelLen int) string { // no need to align first line because it starts at the correct location (after the label) if i != 0 { // append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab - outBuf.WriteString("\n\r\t" + strings.Repeat(" ", longestLabelLen+1) + "\t") + outBuf.WriteString("\n\t" + strings.Repeat(" ", longestLabelLen+1) + "\t") } outBuf.WriteString(scanner.Text()) } @@ -209,6 +212,9 @@ type failNower interface { // FailNow fails test func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } Fail(t, failureMessage, msgAndArgs...) // We cannot extend TestingT with FailNow() and @@ -227,8 +233,11 @@ func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool // Fail reports a failure through func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } content := []labeledContent{ - {"Error Trace", strings.Join(CallerInfo(), "\n\r\t\t\t")}, + {"Error Trace", strings.Join(CallerInfo(), "\n\t\t\t")}, {"Error", failureMessage}, } @@ -244,7 +253,7 @@ func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { content = append(content, labeledContent{"Messages", message}) } - t.Errorf("%s", "\r"+getWhitespaceString()+labeledOutput(content...)) + t.Errorf("\n%s", ""+labeledOutput(content...)) return false } @@ -256,7 +265,7 @@ type labeledContent struct { // labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner: // -// \r\t{{label}}:{{align_spaces}}\t{{content}}\n +// \t{{label}}:{{align_spaces}}\t{{content}}\n // // The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label. // If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this @@ -272,7 +281,7 @@ func labeledOutput(content ...labeledContent) string { } var output string for _, v := range content { - output += "\r\t" + v.label + ":" + strings.Repeat(" ", longestLabel-len(v.label)) + "\t" + indentMessageLines(v.content, longestLabel) + "\n" + output += "\t" + v.label + ":" + strings.Repeat(" ", longestLabel-len(v.label)) + "\t" + indentMessageLines(v.content, longestLabel) + "\n" } return output } @@ -281,6 +290,9 @@ func labeledOutput(content ...labeledContent) string { // // assert.Implements(t, (*MyInterface)(nil), new(MyObject)) func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } interfaceType := reflect.TypeOf(interfaceObject).Elem() if object == nil { @@ -295,6 +307,9 @@ func Implements(t TestingT, interfaceObject interface{}, object interface{}, msg // IsType asserts that the specified objects are of the same type. func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) { return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %v", reflect.TypeOf(expectedType), reflect.TypeOf(object)), msgAndArgs...) @@ -311,6 +326,9 @@ func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs // referenced values (as opposed to the memory addresses). Function equality // cannot be determined and will always fail. func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } if err := validateEqualArgs(expected, actual); err != nil { return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)", expected, actual, err), msgAndArgs...) @@ -349,6 +367,9 @@ func formatUnequalValues(expected, actual interface{}) (e string, a string) { // // assert.EqualValues(t, uint32(123), int32(123)) func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } if !ObjectsAreEqualValues(expected, actual) { diff := diff(expected, actual) @@ -366,12 +387,15 @@ func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interfa // // assert.Exactly(t, int32(123), int64(123)) func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } aType := reflect.TypeOf(expected) bType := reflect.TypeOf(actual) if aType != bType { - return Fail(t, fmt.Sprintf("Types expected to match exactly\n\r\t%v != %v", aType, bType), msgAndArgs...) + return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...) } return Equal(t, expected, actual, msgAndArgs...) @@ -382,6 +406,9 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} // // assert.NotNil(t, err) func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } if !isNil(object) { return true } @@ -407,6 +434,9 @@ func isNil(object interface{}) bool { // // assert.Nil(t, err) func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } if isNil(object) { return true } @@ -446,6 +476,9 @@ func isEmpty(object interface{}) bool { // // assert.Empty(t, obj) func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } pass := isEmpty(object) if !pass { @@ -463,6 +496,9 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { // assert.Equal(t, "two", obj[1]) // } func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } pass := !isEmpty(object) if !pass { @@ -490,6 +526,9 @@ func getLen(x interface{}) (ok bool, length int) { // // assert.Len(t, mySlice, 3) func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } ok, l := getLen(object) if !ok { return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", object), msgAndArgs...) @@ -505,6 +544,14 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) // // assert.True(t, myBool) func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if h, ok := t.(interface { + Helper() + }); ok { + h.Helper() + } if value != true { return Fail(t, "Should be true", msgAndArgs...) @@ -518,6 +565,9 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { // // assert.False(t, myBool) func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } if value != false { return Fail(t, "Should be false", msgAndArgs...) @@ -534,6 +584,9 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } if err := validateEqualArgs(expected, actual); err != nil { return Fail(t, fmt.Sprintf("Invalid operation: %#v != %#v (%s)", expected, actual, err), msgAndArgs...) @@ -592,6 +645,9 @@ func includeElement(list interface{}, element interface{}) (ok, found bool) { // assert.Contains(t, ["Hello", "World"], "World") // assert.Contains(t, {"Hello": "World"}, "Hello") func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } ok, found := includeElement(s, contains) if !ok { @@ -612,6 +668,9 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo // assert.NotContains(t, ["Hello", "World"], "Earth") // assert.NotContains(t, {"Hello": "World"}, "Earth") func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } ok, found := includeElement(s, contains) if !ok { @@ -630,6 +689,9 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) // // assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { + if h, ok := t.(tHelper); ok { + h.Helper() + } if subset == nil { return true // we consider nil to be equal to the nil set } @@ -671,6 +733,9 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok // // assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { + if h, ok := t.(tHelper); ok { + h.Helper() + } if subset == nil { return Fail(t, fmt.Sprintf("nil is the empty set which is a subset of every set"), msgAndArgs...) } @@ -713,6 +778,9 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) // // assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) { + if h, ok := t.(tHelper); ok { + h.Helper() + } if isEmpty(listA) && isEmpty(listB) { return true } @@ -763,6 +831,9 @@ func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface // Condition uses a Comparison to assert a complex condition. func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } result := comp() if !result { Fail(t, "Condition failed!", msgAndArgs...) @@ -800,9 +871,12 @@ func didPanic(f PanicTestFunc) (bool, interface{}) { // // assert.Panics(t, func(){ GoCrazy() }) func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } if funcDidPanic, panicValue := didPanic(f); !funcDidPanic { - return Fail(t, fmt.Sprintf("func %#v should panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...) + return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) } return true @@ -813,13 +887,16 @@ func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { // // assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } funcDidPanic, panicValue := didPanic(f) if !funcDidPanic { - return Fail(t, fmt.Sprintf("func %#v should panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...) + return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) } if panicValue != expected { - return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%v\n\r\tPanic value:\t%v", f, expected, panicValue), msgAndArgs...) + return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%#v\n\tPanic value:\t%#v", f, expected, panicValue), msgAndArgs...) } return true @@ -829,9 +906,12 @@ func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndAr // // assert.NotPanics(t, func(){ RemainCalm() }) func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } if funcDidPanic, panicValue := didPanic(f); funcDidPanic { - return Fail(t, fmt.Sprintf("func %#v should not panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...) + return Fail(t, fmt.Sprintf("func %#v should not panic\n\tPanic value:\t%v", f, panicValue), msgAndArgs...) } return true @@ -841,6 +921,9 @@ func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { // // assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } dt := expected.Sub(actual) if dt < -delta || dt > delta { @@ -890,6 +973,9 @@ func toFloat(x interface{}) (float64, bool) { // // assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } af, aok := toFloat(expected) bf, bok := toFloat(actual) @@ -916,6 +1002,9 @@ func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs // InDeltaSlice is the same as InDelta, except it compares two slices. func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } if expected == nil || actual == nil || reflect.TypeOf(actual).Kind() != reflect.Slice || reflect.TypeOf(expected).Kind() != reflect.Slice { @@ -937,6 +1026,9 @@ func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAn // InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. func InDeltaMapValues(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } if expected == nil || actual == nil || reflect.TypeOf(actual).Kind() != reflect.Map || reflect.TypeOf(expected).Kind() != reflect.Map { @@ -994,6 +1086,9 @@ func calcRelativeError(expected, actual interface{}) (float64, error) { // InEpsilon asserts that expected and actual have a relative error less than epsilon func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } actualEpsilon, err := calcRelativeError(expected, actual) if err != nil { return Fail(t, err.Error(), msgAndArgs...) @@ -1008,6 +1103,9 @@ func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAnd // InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } if expected == nil || actual == nil || reflect.TypeOf(actual).Kind() != reflect.Slice || reflect.TypeOf(expected).Kind() != reflect.Slice { @@ -1038,6 +1136,9 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m // assert.Equal(t, expectedObj, actualObj) // } func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } if err != nil { return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...) } @@ -1052,6 +1153,9 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { // assert.Equal(t, expectedError, err) // } func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } if err == nil { return Fail(t, "An error is expected but got nil.", msgAndArgs...) @@ -1066,6 +1170,9 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { // actualObj, err := SomeFunction() // assert.EqualError(t, err, expectedErrorString) func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } if !Error(t, theError, msgAndArgs...) { return false } @@ -1099,6 +1206,9 @@ func matchRegexp(rx interface{}, str interface{}) bool { // assert.Regexp(t, regexp.MustCompile("start"), "it's starting") // assert.Regexp(t, "start...$", "it's not starting") func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } match := matchRegexp(rx, str) @@ -1114,6 +1224,9 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface // assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") // assert.NotRegexp(t, "^start", "it's not starting") func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } match := matchRegexp(rx, str) if match { @@ -1126,6 +1239,9 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf // Zero asserts that i is the zero value for its type. func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...) } @@ -1134,6 +1250,9 @@ func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { // NotZero asserts that i is not the zero value for its type. func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } if i == nil || reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { return Fail(t, fmt.Sprintf("Should not be zero, but was %v", i), msgAndArgs...) } @@ -1142,6 +1261,9 @@ func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { // FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } info, err := os.Lstat(path) if err != nil { if os.IsNotExist(err) { @@ -1157,6 +1279,9 @@ func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { // DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } info, err := os.Lstat(path) if err != nil { if os.IsNotExist(err) { @@ -1174,6 +1299,9 @@ func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { // // assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } var expectedJSONAsInterface, actualJSONAsInterface interface{} if err := json.Unmarshal([]byte(expected), &expectedJSONAsInterface); err != nil { @@ -1212,12 +1340,18 @@ func diff(expected interface{}, actual interface{}) string { return "" } - if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array { + if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array && ek != reflect.String { return "" } - e := spewConfig.Sdump(expected) - a := spewConfig.Sdump(actual) + var e, a string + if ek != reflect.String { + e = spewConfig.Sdump(expected) + a = spewConfig.Sdump(actual) + } else { + e = expected.(string) + a = actual.(string) + } diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ A: difflib.SplitLines(e), @@ -1254,3 +1388,7 @@ var spewConfig = spew.ConfigState{ DisableCapacities: true, SortKeys: true, } + +type tHelper interface { + Helper() +} diff --git a/vendor/github.com/stretchr/testify/assert/http_assertions.go b/vendor/github.com/stretchr/testify/assert/http_assertions.go index 3101e78..df46fa7 100644 --- a/vendor/github.com/stretchr/testify/assert/http_assertions.go +++ b/vendor/github.com/stretchr/testify/assert/http_assertions.go @@ -12,10 +12,11 @@ import ( // an error if building a new request fails. func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) { w := httptest.NewRecorder() - req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) + req, err := http.NewRequest(method, url, nil) if err != nil { return -1, err } + req.URL.RawQuery = values.Encode() handler(w, req) return w.Code, nil } @@ -26,6 +27,9 @@ func httpCode(handler http.HandlerFunc, method, url string, values url.Values) ( // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } code, err := httpCode(handler, method, url, values) if err != nil { Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) @@ -46,6 +50,9 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } code, err := httpCode(handler, method, url, values) if err != nil { Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) @@ -66,6 +73,9 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu // // Returns whether the assertion was successful (true) or not (false). func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } code, err := httpCode(handler, method, url, values) if err != nil { Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) @@ -95,10 +105,13 @@ func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) s // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") +// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } body := HTTPBody(handler, method, url, values) contains := strings.Contains(body, fmt.Sprint(str)) @@ -112,10 +125,13 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") +// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } body := HTTPBody(handler, method, url, values) contains := strings.Contains(body, fmt.Sprint(str)) diff --git a/vendor/github.com/zyedidia/clipboard/LICENSE b/vendor/github.com/zyedidia/clipboard/LICENSE index dee3257..bdec9a5 100644 --- a/vendor/github.com/zyedidia/clipboard/LICENSE +++ b/vendor/github.com/zyedidia/clipboard/LICENSE @@ -1,3 +1,7 @@ +Copyright (c) 2018 Zachary Yedidia. Modifications to atotto/clipboard. + +Original license: + Copyright (c) 2013 Ato Araki. All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/vendor/github.com/zyedidia/clipboard/README.md b/vendor/github.com/zyedidia/clipboard/README.md index 6e2d15f..8691eb1 100644 --- a/vendor/github.com/zyedidia/clipboard/README.md +++ b/vendor/github.com/zyedidia/clipboard/README.md @@ -1,3 +1,7 @@ +# This is a fork of atotto/clipboard + +This fork is used for `zyedidia/micro` and has some modifications, namely: support for the primary clipboard on linux and support for an internal clipboard if the system clipboard is not available. + [![Build Status](https://travis-ci.org/atotto/clipboard.svg?branch=master)](https://travis-ci.org/atotto/clipboard) [![Build Status](https://drone.io/github.com/atotto/clipboard/status.png)](https://drone.io/github.com/atotto/clipboard/latest) [![GoDoc](https://godoc.org/github.com/atotto/clipboard?status.svg)](http://godoc.org/github.com/atotto/clipboard) diff --git a/vendor/golang.org/x/image/bmp/writer.go b/vendor/golang.org/x/image/bmp/writer.go index 6947968..f07b39d 100644 --- a/vendor/golang.org/x/image/bmp/writer.go +++ b/vendor/golang.org/x/image/bmp/writer.go @@ -49,20 +49,91 @@ func encodePaletted(w io.Writer, pix []uint8, dx, dy, stride, step int) error { return nil } -func encodeRGBA(w io.Writer, pix []uint8, dx, dy, stride, step int) error { +func encodeRGBA(w io.Writer, pix []uint8, dx, dy, stride, step int, opaque bool) error { buf := make([]byte, step) - for y := dy - 1; y >= 0; y-- { - min := y*stride + 0 - max := y*stride + dx*4 - off := 0 - for i := min; i < max; i += 4 { - buf[off+2] = pix[i+0] - buf[off+1] = pix[i+1] - buf[off+0] = pix[i+2] - off += 3 + if opaque { + for y := dy - 1; y >= 0; y-- { + min := y*stride + 0 + max := y*stride + dx*4 + off := 0 + for i := min; i < max; i += 4 { + buf[off+2] = pix[i+0] + buf[off+1] = pix[i+1] + buf[off+0] = pix[i+2] + off += 3 + } + if _, err := w.Write(buf); err != nil { + return err + } } - if _, err := w.Write(buf); err != nil { - return err + } else { + for y := dy - 1; y >= 0; y-- { + min := y*stride + 0 + max := y*stride + dx*4 + off := 0 + for i := min; i < max; i += 4 { + a := uint32(pix[i+3]) + if a == 0 { + buf[off+2] = 0 + buf[off+1] = 0 + buf[off+0] = 0 + buf[off+3] = 0 + off += 4 + continue + } else if a == 0xff { + buf[off+2] = pix[i+0] + buf[off+1] = pix[i+1] + buf[off+0] = pix[i+2] + buf[off+3] = 0xff + off += 4 + continue + } + buf[off+2] = uint8(((uint32(pix[i+0]) * 0xffff) / a) >> 8) + buf[off+1] = uint8(((uint32(pix[i+1]) * 0xffff) / a) >> 8) + buf[off+0] = uint8(((uint32(pix[i+2]) * 0xffff) / a) >> 8) + buf[off+3] = uint8(a) + off += 4 + } + if _, err := w.Write(buf); err != nil { + return err + } + } + } + return nil +} + +func encodeNRGBA(w io.Writer, pix []uint8, dx, dy, stride, step int, opaque bool) error { + buf := make([]byte, step) + if opaque { + for y := dy - 1; y >= 0; y-- { + min := y*stride + 0 + max := y*stride + dx*4 + off := 0 + for i := min; i < max; i += 4 { + buf[off+2] = pix[i+0] + buf[off+1] = pix[i+1] + buf[off+0] = pix[i+2] + off += 3 + } + if _, err := w.Write(buf); err != nil { + return err + } + } + } else { + for y := dy - 1; y >= 0; y-- { + min := y*stride + 0 + max := y*stride + dx*4 + off := 0 + for i := min; i < max; i += 4 { + buf[off+2] = pix[i+0] + buf[off+1] = pix[i+1] + buf[off+0] = pix[i+2] + buf[off+3] = pix[i+3] + off += 4 + } + if _, err := w.Write(buf); err != nil { + return err + } } } return nil @@ -105,6 +176,7 @@ func Encode(w io.Writer, m image.Image) error { var step int var palette []byte + var opaque bool switch m := m.(type) { case *image.Gray: step = (d.X + 3) &^ 3 @@ -134,6 +206,28 @@ func Encode(w io.Writer, m image.Image) error { h.fileSize += uint32(len(palette)) + h.imageSize h.pixOffset += uint32(len(palette)) h.bpp = 8 + case *image.RGBA: + opaque = m.Opaque() + if opaque { + step = (3*d.X + 3) &^ 3 + h.bpp = 24 + } else { + step = 4 * d.X + h.bpp = 32 + } + h.imageSize = uint32(d.Y * step) + h.fileSize += h.imageSize + case *image.NRGBA: + opaque = m.Opaque() + if opaque { + step = (3*d.X + 3) &^ 3 + h.bpp = 24 + } else { + step = 4 * d.X + h.bpp = 32 + } + h.imageSize = uint32(d.Y * step) + h.fileSize += h.imageSize default: step = (3*d.X + 3) &^ 3 h.imageSize = uint32(d.Y * step) @@ -160,7 +254,9 @@ func Encode(w io.Writer, m image.Image) error { case *image.Paletted: return encodePaletted(w, m.Pix, d.X, d.Y, m.Stride, step) case *image.RGBA: - return encodeRGBA(w, m.Pix, d.X, d.Y, m.Stride, step) + return encodeRGBA(w, m.Pix, d.X, d.Y, m.Stride, step, opaque) + case *image.NRGBA: + return encodeNRGBA(w, m.Pix, d.X, d.Y, m.Stride, step, opaque) } return encode(w, m, step) } diff --git a/vendor/golang.org/x/net/html/parse.go b/vendor/golang.org/x/net/html/parse.go index d23e05e..4b1fa42 100644 --- a/vendor/golang.org/x/net/html/parse.go +++ b/vendor/golang.org/x/net/html/parse.go @@ -209,27 +209,6 @@ loop: p.oe = p.oe[:i+1] } -// generateAllImpliedEndTags pops nodes off the stack of open elements as long as -// the top node has a tag name of caption, colgroup, dd, div, dt, li, optgroup, option, p, rb, -// rp, rt, rtc, span, tbody, td, tfoot, th, thead or tr. -func (p *parser) generateAllImpliedEndTags() { - var i int - for i = len(p.oe) - 1; i >= 0; i-- { - n := p.oe[i] - if n.Type == ElementNode { - switch n.DataAtom { - // TODO: remove this divergence from the HTML5 spec - case a.Caption, a.Colgroup, a.Dd, a.Div, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb, - a.Rp, a.Rt, a.Rtc, a.Span, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr: - continue - } - } - break - } - - p.oe = p.oe[:i+1] -} - // addChild adds a child node n to the top element, and pushes n onto the stack // of open elements if it is an element node. func (p *parser) addChild(n *Node) { @@ -276,7 +255,7 @@ func (p *parser) fosterParent(n *Node) { } } - if template != nil && (table == nil || j < i) { + if template != nil && (table == nil || j > i) { template.AppendChild(n) return } @@ -679,11 +658,16 @@ func inHeadIM(p *parser) bool { if !p.oe.contains(a.Template) { return true } - p.generateAllImpliedEndTags() - if n := p.oe.top(); n.DataAtom != a.Template { - return true + // TODO: remove this divergence from the HTML5 spec. + // + // See https://bugs.chromium.org/p/chromium/issues/detail?id=829668 + p.generateImpliedEndTags() + for i := len(p.oe) - 1; i >= 0; i-- { + if n := p.oe[i]; n.Namespace == "" && n.DataAtom == a.Template { + p.oe = p.oe[:i] + break + } } - p.popUntil(defaultScope, a.Template) p.clearActiveFormattingElements() p.templateStack.pop() p.resetInsertionMode() @@ -860,9 +844,13 @@ func inBodyIM(p *parser) bool { // The newline, if any, will be dealt with by the TextToken case. p.framesetOK = false case a.Form: - if p.oe.contains(a.Template) || p.form == nil { - p.popUntil(buttonScope, a.P) - p.addElement() + if p.form != nil && !p.oe.contains(a.Template) { + // Ignore the token + return true + } + p.popUntil(buttonScope, a.P) + p.addElement() + if !p.oe.contains(a.Template) { p.form = p.top() } case a.Li: @@ -1070,13 +1058,7 @@ func inBodyIM(p *parser) bool { p.acknowledgeSelfClosingTag() } return true - case a.Frame: - // TODO: remove this divergence from the HTML5 spec. - if p.oe.contains(a.Template) { - p.addElement() - return true - } - case a.Caption, a.Col, a.Colgroup, a.Head, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr: + case a.Caption, a.Col, a.Colgroup, a.Frame, a.Head, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr: // Ignore the token. default: p.reconstructActiveFormattingElements() @@ -1098,12 +1080,13 @@ func inBodyIM(p *parser) bool { p.popUntil(defaultScope, p.tok.DataAtom) case a.Form: if p.oe.contains(a.Template) { - if !p.oe.contains(a.Form) { + i := p.indexOfElementInScope(defaultScope, a.Form) + if i == -1 { // Ignore the token. return true } p.generateImpliedEndTags() - if p.tok.DataAtom == a.Form { + if p.oe[i].DataAtom != a.Form { // Ignore the token. return true } @@ -1346,9 +1329,6 @@ func textIM(p *parser) bool { // Section 12.2.6.4.9. func inTableIM(p *parser) bool { switch p.tok.Type { - case ErrorToken: - // Stop parsing. - return true case TextToken: p.tok.Data = strings.Replace(p.tok.Data, "\x00", "", -1) switch p.oe.top().DataAtom { @@ -1443,6 +1423,8 @@ func inTableIM(p *parser) bool { case DoctypeToken: // Ignore the token. return true + case ErrorToken: + return inBodyIM(p) } p.fosterParenting = true @@ -1545,6 +1527,8 @@ func inColumnGroupIM(p *parser) bool { case a.Template: return inHeadIM(p) } + case ErrorToken: + return inBodyIM(p) } if p.oe.top().DataAtom != a.Colgroup { return true @@ -1709,9 +1693,6 @@ func inCellIM(p *parser) bool { // Section 12.2.6.4.16. func inSelectIM(p *parser) bool { switch p.tok.Type { - case ErrorToken: - // Stop parsing. - return true case TextToken: p.addText(strings.Replace(p.tok.Data, "\x00", "", -1)) case StartTagToken: @@ -1775,6 +1756,8 @@ func inSelectIM(p *parser) bool { case DoctypeToken: // Ignore the token. return true + case ErrorToken: + return inBodyIM(p) } return true @@ -1841,15 +1824,26 @@ func inTemplateIM(p *parser) bool { // Ignore the token. return true } + case ErrorToken: + if !p.oe.contains(a.Template) { + // Ignore the token. + return true + } + // TODO: remove this divergence from the HTML5 spec. + // + // See https://bugs.chromium.org/p/chromium/issues/detail?id=829668 + p.generateImpliedEndTags() + for i := len(p.oe) - 1; i >= 0; i-- { + if n := p.oe[i]; n.Namespace == "" && n.DataAtom == a.Template { + p.oe = p.oe[:i] + break + } + } + p.clearActiveFormattingElements() + p.templateStack.pop() + p.resetInsertionMode() + return false } - if !p.oe.contains(a.Template) { - // Ignore the token. - return true - } - p.popUntil(defaultScope, a.Template) - p.clearActiveFormattingElements() - p.templateStack.pop() - p.resetInsertionMode() return false } @@ -1923,11 +1917,6 @@ func inFramesetIM(p *parser) bool { p.acknowledgeSelfClosingTag() case a.Noframes: return inHeadIM(p) - case a.Template: - // TODO: remove this divergence from the HTML5 spec. - // - // See https://bugs.chromium.org/p/chromium/issues/detail?id=829668 - return inTemplateIM(p) } case EndTagToken: switch p.tok.DataAtom { diff --git a/vendor/gopkg.in/toast.v1/toast.go b/vendor/gopkg.in/toast.v1/toast.go index 1782e5e..7ea32c9 100644 --- a/vendor/gopkg.in/toast.v1/toast.go +++ b/vendor/gopkg.in/toast.v1/toast.go @@ -11,6 +11,7 @@ import ( "text/template" "github.com/nu7hatch/gouuid" + "syscall" ) var toastTemplate *template.Template @@ -347,7 +348,9 @@ func invokeTemporaryScript(content string) error { if err != nil { return err } - if err = exec.Command("PowerShell", "-ExecutionPolicy", "Bypass", "-File", file).Run(); err != nil { + cmd := exec.Command("PowerShell", "-ExecutionPolicy", "Bypass", "-File", file) + cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} + if err = cmd.Run(); err != nil { return err } return nil diff --git a/vendor/maunium.net/go/gomatrix/client.go b/vendor/maunium.net/go/gomatrix/client.go index 7725ac3..0806138 100644 --- a/vendor/maunium.net/go/gomatrix/client.go +++ b/vendor/maunium.net/go/gomatrix/client.go @@ -6,13 +6,16 @@ package gomatrix import ( "bytes" "encoding/json" + "errors" "fmt" "io" "io/ioutil" + "maunium.net/go/maulogger" "net/http" "net/url" "path" "strconv" + "strings" "sync" "time" ) @@ -26,6 +29,7 @@ type Client struct { Client *http.Client // The underlying HTTP client which will be used to make HTTP requests. Syncer Syncer // The thing which can process /sync responses Store Storer // The thing which can store rooms/tokens/ids + Logger maulogger.Logger // The ?user_id= query parameter for application services. This must be set *prior* to calling a method. If this is empty, // no user_id parameter will be sent. @@ -39,6 +43,7 @@ type Client struct { // HTTPError An HTTP Error response, which may wrap an underlying native Go Error. type HTTPError struct { WrappedError error + RespError *RespError Message string Code int } @@ -177,6 +182,14 @@ func (cli *Client) StopSync() { cli.incrementSyncingID() } +func (cli *Client) LogRequest(req *http.Request, body string) { + if cli.Logger == nil { + return + } + + cli.Logger.Debugfln("%s %s %s", req.Method, req.URL.Path, body) +} + // MakeRequest makes a JSON HTTP request to the given URL. // If "resBody" is not nil, the response body will be json.Unmarshalled into it. // @@ -186,12 +199,14 @@ func (cli *Client) StopSync() { func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{}, resBody interface{}) ([]byte, error) { var req *http.Request var err error + logBody := "{}" if reqBody != nil { var jsonStr []byte jsonStr, err = json.Marshal(reqBody) if err != nil { return nil, err } + logBody = string(jsonStr) req, err = http.NewRequest(method, httpURL, bytes.NewBuffer(jsonStr)) } else { req, err = http.NewRequest(method, httpURL, nil) @@ -201,6 +216,7 @@ func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{ return nil, err } req.Header.Set("Content-Type", "application/json") + cli.LogRequest(req, logBody) res, err := cli.Client.Do(req) if res != nil { defer res.Body.Close() @@ -211,9 +227,11 @@ func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{ contents, err := ioutil.ReadAll(res.Body) if res.StatusCode/100 != 2 { // not 2xx var wrap error - var respErr RespError - if _ = json.Unmarshal(contents, &respErr); respErr.ErrCode != "" { + respErr := &RespError{} + if _ = json.Unmarshal(contents, respErr); respErr.ErrCode != "" { wrap = respErr + } else { + respErr = nil } // If we failed to decode as RespError, don't just drop the HTTP body, include it in the @@ -227,6 +245,7 @@ func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{ Code: res.StatusCode, Message: msg, WrappedError: wrap, + RespError: respErr, } } if err != nil { @@ -342,7 +361,7 @@ func (cli *Client) RegisterDummy(req *ReqRegister) (*RespRegister, error) { } } if res == nil { - return nil, fmt.Errorf("registration failed: does this server support m.login.dummy?") + return nil, fmt.Errorf("registration failed: does this server support m.login.dummy? ") } return res, nil } @@ -442,17 +461,38 @@ func (cli *Client) SetAvatarURL(url string) (err error) { // SendMessageEvent sends a message event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid // contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal. -func (cli *Client) SendMessageEvent(roomID string, eventType string, contentJSON interface{}) (resp *RespSendEvent, err error) { +func (cli *Client) SendMessageEvent(roomID string, eventType EventType, contentJSON interface{}) (resp *RespSendEvent, err error) { + txnID := txnID() + urlPath := cli.BuildURL("rooms", roomID, "send", eventType.String(), txnID) + _, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp) + return +} + +// SendMessageEvent sends a message event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid +// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal. +func (cli *Client) SendMassagedMessageEvent(roomID string, eventType EventType, contentJSON interface{}, ts int64) (resp *RespSendEvent, err error) { txnID := txnID() - urlPath := cli.BuildURL("rooms", roomID, "send", eventType, txnID) + urlPath := cli.BuildURLWithQuery([]string{"rooms", roomID, "send", eventType.String(), txnID}, map[string]string{ + "ts": strconv.FormatInt(ts, 10), + }) _, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp) return } // SendStateEvent sends a state event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-state-eventtype-statekey // contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal. -func (cli *Client) SendStateEvent(roomID, eventType, stateKey string, contentJSON interface{}) (resp *RespSendEvent, err error) { - urlPath := cli.BuildURL("rooms", roomID, "state", eventType, stateKey) +func (cli *Client) SendStateEvent(roomID string, eventType EventType, stateKey string, contentJSON interface{}) (resp *RespSendEvent, err error) { + urlPath := cli.BuildURL("rooms", roomID, "state", eventType.String(), stateKey) + _, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp) + return +} + +// SendStateEvent sends a state event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-state-eventtype-statekey +// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal. +func (cli *Client) SendMassagedStateEvent(roomID string, eventType EventType, stateKey string, contentJSON interface{}, ts int64) (resp *RespSendEvent, err error) { + urlPath := cli.BuildURLWithQuery([]string{"rooms", roomID, "state", eventType.String(), stateKey}, map[string]string{ + "ts": strconv.FormatInt(ts, 10), + }) _, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp) return } @@ -460,37 +500,39 @@ func (cli *Client) SendStateEvent(roomID, eventType, stateKey string, contentJSO // SendText sends an m.room.message event into the given room with a msgtype of m.text // See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-text func (cli *Client) SendText(roomID, text string) (*RespSendEvent, error) { - return cli.SendMessageEvent(roomID, "m.room.message", - TextMessage{"m.text", text}) + return cli.SendMessageEvent(roomID, EventMessage, Content{ + MsgType: MsgText, + Body: text, + }) } // SendImage sends an m.room.message event into the given room with a msgtype of m.image // See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-image func (cli *Client) SendImage(roomID, body, url string) (*RespSendEvent, error) { - return cli.SendMessageEvent(roomID, "m.room.message", - ImageMessage{ - MsgType: "m.image", - Body: body, - URL: url, - }) + return cli.SendMessageEvent(roomID, EventMessage, Content{ + MsgType: MsgImage, + Body: body, + URL: url, + }) } // SendVideo sends an m.room.message event into the given room with a msgtype of m.video // See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-video func (cli *Client) SendVideo(roomID, body, url string) (*RespSendEvent, error) { - return cli.SendMessageEvent(roomID, "m.room.message", - VideoMessage{ - MsgType: "m.video", - Body: body, - URL: url, - }) + return cli.SendMessageEvent(roomID, EventMessage, Content{ + MsgType: MsgVideo, + Body: body, + URL: url, + }) } // SendNotice sends an m.room.message event into the given room with a msgtype of m.notice // See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-notice func (cli *Client) SendNotice(roomID, text string) (*RespSendEvent, error) { - return cli.SendMessageEvent(roomID, "m.room.message", - TextMessage{"m.notice", text}) + return cli.SendMessageEvent(roomID, EventMessage, Content{ + MsgType: MsgNotice, + Body: text, + }) } // RedactEvent redacts the given event. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid @@ -569,11 +611,18 @@ func (cli *Client) UserTyping(roomID string, typing bool, timeout int64) (resp * return } +func (cli *Client) SetPresence(status string) (err error) { + req := ReqPresence{Presence: status} + u := cli.BuildURL("presence", cli.UserID, "status") + _, err = cli.MakeRequest("PUT", u, req, nil) + return +} + // StateEvent gets a single state event in a room. It will attempt to JSON unmarshal into the given "outContent" struct with // the HTTP response body, or return an error. // See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey -func (cli *Client) StateEvent(roomID, eventType, stateKey string, outContent interface{}) (err error) { - u := cli.BuildURL("rooms", roomID, "state", eventType, stateKey) +func (cli *Client) StateEvent(roomID string, eventType EventType, stateKey string, outContent interface{}) (err error) { + u := cli.BuildURL("rooms", roomID, "state", eventType.String(), stateKey) _, err = cli.MakeRequest("GET", u, nil, outContent) return } @@ -587,18 +636,48 @@ func (cli *Client) UploadLink(link string) (*RespMediaUpload, error) { if err != nil { return nil, err } - return cli.UploadToContentRepo(res.Body, res.Header.Get("Content-Type"), res.ContentLength) + return cli.Upload(res.Body, res.Header.Get("Content-Type"), res.ContentLength) +} + +func (cli *Client) Download(mxcURL string) (io.ReadCloser, error) { + if !strings.HasPrefix(mxcURL, "mxc://") { + return nil, errors.New("invalid Matrix content URL") + } + parts := strings.Split(mxcURL[len("mxc://"):], "/") + if len(parts) != 2 { + return nil, errors.New("invalid Matrix content URL") + } + u := cli.BuildBaseURL("_matrix/media/r0/download", parts[0], parts[1]) + resp, err := cli.Client.Get(u) + if err != nil { + return nil, err + } + return resp.Body, nil +} + +func (cli *Client) DownloadBytes(mxcURL string) ([]byte, error) { + resp, err := cli.Download(mxcURL) + if err != nil { + return nil, err + } + defer resp.Close() + return ioutil.ReadAll(resp) +} + +func (cli *Client) UploadBytes(data []byte, contentType string) (*RespMediaUpload, error) { + return cli.Upload(bytes.NewReader(data), contentType, int64(len(data))) } // UploadToContentRepo uploads the given bytes to the content repository and returns an MXC URI. // See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-media-r0-upload -func (cli *Client) UploadToContentRepo(content io.Reader, contentType string, contentLength int64) (*RespMediaUpload, error) { +func (cli *Client) Upload(content io.Reader, contentType string, contentLength int64) (*RespMediaUpload, error) { req, err := http.NewRequest("POST", cli.BuildBaseURL("_matrix/media/r0/upload"), content) if err != nil { return nil, err } req.Header.Set("Content-Type", contentType) req.ContentLength = contentLength + cli.LogRequest(req, fmt.Sprintf("%d bytes", contentLength)) res, err := cli.Client.Do(req) if res != nil { defer res.Body.Close() @@ -666,6 +745,18 @@ func (cli *Client) Messages(roomID, from, to string, dir rune, limit int) (resp return } +func (cli *Client) GetEvent(roomID, eventID string) (resp *Event, err error) { + urlPath := cli.BuildURL("rooms", roomID, "event", eventID) + _, err = cli.MakeRequest("GET", urlPath, nil, &resp) + return +} + +func (cli *Client) MarkRead(roomID, eventID string) (err error) { + urlPath := cli.BuildURL("rooms", roomID, "receipt", "m.read", eventID) + _, err = cli.MakeRequest("POST", urlPath, struct{}{}, nil) + return +} + // TurnServer returns turn server details and credentials for the client to use when initiating calls. // See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-voip-turnserver func (cli *Client) TurnServer() (resp *RespTurnServer, err error) { diff --git a/vendor/maunium.net/go/gomatrix/events.go b/vendor/maunium.net/go/gomatrix/events.go index 7233c7c..30166cd 100644 --- a/vendor/maunium.net/go/gomatrix/events.go +++ b/vendor/maunium.net/go/gomatrix/events.go @@ -1,110 +1,413 @@ package gomatrix import ( - "html" - "regexp" + "encoding/json" + "strings" + "sync" +) + +type EventTypeClass int + +const ( + // Normal message events + MessageEventType EventTypeClass = iota + // State events + StateEventType + // Ephemeral events + EphemeralEventType + // Account data events + AccountDataEventType + // Unknown events + UnknownEventType +) + +type EventType struct { + Type string + Class EventTypeClass +} + +func NewEventType(name string) EventType { + evtType := EventType{Type: name} + evtType.Class = evtType.GuessClass() + return evtType +} + +func (et *EventType) IsState() bool { + return et.Class == StateEventType +} + +func (et *EventType) IsEphemeral() bool { + return et.Class == EphemeralEventType +} + +func (et *EventType) IsCustom() bool { + return !strings.HasPrefix(et.Type, "m.") +} + +func (et *EventType) GuessClass() EventTypeClass { + switch et.Type { + case StateAliases.Type, StateCanonicalAlias.Type, StateCreate.Type, StateJoinRules.Type, StateMember.Type, + StatePowerLevels.Type, StateRoomName.Type, StateRoomAvatar.Type, StateTopic.Type, StatePinnedEvents.Type: + return StateEventType + case EphemeralEventReceipt.Type, EphemeralEventTyping.Type: + return EphemeralEventType + case AccountDataDirectChats.Type, AccountDataPushRules.Type, AccountDataRoomTags.Type: + return AccountDataEventType + case EventRedaction.Type, EventMessage.Type, EventSticker.Type: + return MessageEventType + default: + return UnknownEventType + } +} + +func (et *EventType) UnmarshalJSON(data []byte) error { + err := json.Unmarshal(data, &et.Type) + if err != nil { + return err + } + et.Class = et.GuessClass() + return nil +} + +func (et *EventType) MarshalJSON() ([]byte, error) { + return json.Marshal(&et.Type) +} + +func (et *EventType) String() string { + return et.Type +} + +// State events +var ( + StateAliases = EventType{"m.room.aliases", StateEventType} + StateCanonicalAlias = EventType{"m.room.canonical_alias", StateEventType} + StateCreate = EventType{"m.room.create", StateEventType} + StateJoinRules = EventType{"m.room.join_rules", StateEventType} + StateMember = EventType{"m.room.member", StateEventType} + StatePowerLevels = EventType{"m.room.power_levels", StateEventType} + StateRoomName = EventType{"m.room.name", StateEventType} + StateTopic = EventType{"m.room.topic", StateEventType} + StateRoomAvatar = EventType{"m.room.avatar", StateEventType} + StatePinnedEvents = EventType{"m.room.pinned_events", StateEventType} +) + +// Message events +var ( + EventRedaction = EventType{"m.room.redaction", MessageEventType} + EventMessage = EventType{"m.room.message", MessageEventType} + EventSticker = EventType{"m.sticker", MessageEventType} +) + +// Ephemeral events +var ( + EphemeralEventReceipt = EventType{"m.receipt", EphemeralEventType} + EphemeralEventTyping = EventType{"m.receipt", EphemeralEventType} +) + +// Account data events +var ( + AccountDataDirectChats = EventType{"m.direct", AccountDataEventType} + AccountDataPushRules = EventType{"m.push_rules", AccountDataEventType} + AccountDataRoomTags = EventType{"m.tag", AccountDataEventType} +) + +type MessageType string + +// Msgtypes +const ( + MsgText MessageType = "m.text" + MsgEmote = "m.emote" + MsgNotice = "m.notice" + MsgImage = "m.image" + MsgLocation = "m.location" + MsgVideo = "m.video" + MsgAudio = "m.audio" + MsgFile = "m.file" +) + +type Format string + +// Message formats +const ( + FormatHTML Format = "org.matrix.custom.html" ) // Event represents a single Matrix event. type Event struct { - StateKey *string `json:"state_key,omitempty"` // The state key for the event. Only present on State Events. - Sender string `json:"sender"` // The user ID of the sender of the event - Type string `json:"type"` // The event type - Timestamp int64 `json:"origin_server_ts"` // The unix timestamp when this message was sent by the origin server - ID string `json:"event_id"` // The unique ID of this event - RoomID string `json:"room_id"` // The room the event was sent to. May be nil (e.g. for presence) - Content map[string]interface{} `json:"content"` // The JSON content of the event. - Redacts string `json:"redacts,omitempty"` // The event ID that was redacted if a m.room.redaction event - Unsigned Unsigned `json:"unsigned,omitempty"` // Unsigned content set by own homeserver. + StateKey *string `json:"state_key,omitempty"` // The state key for the event. Only present on State Events. + Sender string `json:"sender"` // The user ID of the sender of the event + Type EventType `json:"type"` // The event type + Timestamp int64 `json:"origin_server_ts"` // The unix timestamp when this message was sent by the origin server + ID string `json:"event_id"` // The unique ID of this event + RoomID string `json:"room_id"` // The room the event was sent to. May be nil (e.g. for presence) + Content Content `json:"content"` // The JSON content of the event. + Redacts string `json:"redacts,omitempty"` // The event ID that was redacted if a m.room.redaction event + Unsigned Unsigned `json:"unsigned,omitempty"` // Unsigned content set by own homeserver. + + InviteRoomState []StrippedState `json:"invite_room_state"` +} + +func (evt *Event) GetStateKey() string { + if evt.StateKey != nil { + return *evt.StateKey + } + return "" +} + +type StrippedState struct { + Content Content `json:"content"` + Type EventType `json:"type"` + StateKey string `json:"state_key"` } type Unsigned struct { - PrevContent map[string]interface{} `json:"prev_content,omitempty"` - PrevSender string `json:"prev_sender,omitempty"` - ReplacesState string `json:"replaces_state,omitempty"` - Age int64 `json:"age"` + PrevContent *Content `json:"prev_content,omitempty"` + PrevSender string `json:"prev_sender,omitempty"` + ReplacesState string `json:"replaces_state,omitempty"` + Age int64 `json:"age,omitempty"` +} + +type Content struct { + VeryRaw json.RawMessage `json:"-"` + Raw map[string]interface{} `json:"-"` + + MsgType MessageType `json:"msgtype,omitempty"` + Body string `json:"body,omitempty"` + Format Format `json:"format,omitempty"` + FormattedBody string `json:"formatted_body,omitempty"` + + Info *FileInfo `json:"info,omitempty"` + URL string `json:"url,omitempty"` + + // Membership key for easy access in m.room.member events + Membership Membership `json:"membership,omitempty"` + + RelatesTo *RelatesTo `json:"m.relates_to,omitempty"` + + PowerLevels + Member + Aliases []string `json:"aliases,omitempty"` + CanonicalAlias + RoomName + RoomTopic + + RoomTags Tags `json:"tags,omitempty"` + TypingUserIDs []string `json:"user_ids,omitempty"` } -// Body returns the value of the "body" key in the event content if it is -// present and is a string. -func (event *Event) Body() (body string, ok bool) { - value, exists := event.Content["body"] - if !exists { - return +type serializableContent Content + +func (content *Content) UnmarshalJSON(data []byte) error { + content.VeryRaw = data + if err := json.Unmarshal(data, &content.Raw); err != nil { + return err } - body, ok = value.(string) + return json.Unmarshal(data, (*serializableContent)(content)) +} + +func (content *Content) UnmarshalPowerLevels() (pl PowerLevels, err error) { + err = json.Unmarshal(content.VeryRaw, &pl) return } -// MessageType returns the value of the "msgtype" key in the event content if -// it is present and is a string. -func (event *Event) MessageType() (msgtype string, ok bool) { - value, exists := event.Content["msgtype"] - if !exists { - return - } - msgtype, ok = value.(string) +func (content *Content) UnmarshalMember() (m Member, err error) { + err = json.Unmarshal(content.VeryRaw, &m) + return +} + +func (content *Content) UnmarshalCanonicalAlias() (ca CanonicalAlias, err error) { + err = json.Unmarshal(content.VeryRaw, &ca) return } -// TextMessage is the contents of a Matrix formated message event. -type TextMessage struct { - MsgType string `json:"msgtype"` - Body string `json:"body"` +func (content *Content) GetInfo() *FileInfo { + if content.Info == nil { + content.Info = &FileInfo{} + } + return content.Info } -// ImageInfo contains info about an image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image -type ImageInfo struct { - Height uint `json:"h,omitempty"` - Width uint `json:"w,omitempty"` - Mimetype string `json:"mimetype,omitempty"` - Size uint `json:"size,omitempty"` +type Tags map[string]struct { + Order string `json:"order"` } -// VideoInfo contains info about a video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video -type VideoInfo struct { - Mimetype string `json:"mimetype,omitempty"` - ThumbnailInfo ImageInfo `json:"thumbnail_info"` - ThumbnailURL string `json:"thumbnail_url,omitempty"` - Height uint `json:"h,omitempty"` - Width uint `json:"w,omitempty"` - Duration uint `json:"duration,omitempty"` - Size uint `json:"size,omitempty"` +type RoomName struct { + Name string `json:"name,omitempty"` +} + +type RoomTopic struct { + Topic string `json:"topic,omitempty"` +} + +// Membership is an enum specifying the membership state of a room member. +type Membership string + +// The allowed membership states as specified in spec section 10.5.5. +const ( + MembershipJoin Membership = "join" + MembershipLeave Membership = "leave" + MembershipInvite Membership = "invite" + MembershipBan Membership = "ban" + MembershipKnock Membership = "knock" +) + +type Member struct { + Membership Membership `json:"membership,omitempty"` + AvatarURL string `json:"avatar_url,omitempty"` + Displayname string `json:"displayname,omitempty"` + ThirdPartyInvite *ThirdPartyInvite `json:"third_party_invite,omitempty"` + Reason string `json:"reason,omitempty"` +} + +type ThirdPartyInvite struct { + DisplayName string `json:"display_name"` + Signed struct { + Token string `json:"token"` + Signatures json.RawMessage `json:"signatures"` + MXID string `json:"mxid"` + } +} + +type CanonicalAlias struct { + Alias string `json:"alias,omitempty"` +} + +type PowerLevels struct { + usersLock sync.RWMutex `json:"-"` + Users map[string]int `json:"users,omitempty"` + UsersDefault int `json:"users_default,omitempty"` + + eventsLock sync.RWMutex `json:"-"` + Events map[string]int `json:"events,omitempty"` + EventsDefault int `json:"events_default,omitempty"` + + StateDefaultPtr *int `json:"state_default,omitempty"` + + InvitePtr *int `json:"invite,omitempty"` + KickPtr *int `json:"kick,omitempty"` + BanPtr *int `json:"ban,omitempty"` + RedactPtr *int `json:"redact,omitempty"` +} + +func (pl *PowerLevels) Invite() int { + if pl.InvitePtr != nil { + return *pl.InvitePtr + } + return 50 +} + +func (pl *PowerLevels) Kick() int { + if pl.KickPtr != nil { + return *pl.KickPtr + } + return 50 } -// VideoMessage is an m.video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video -type VideoMessage struct { - MsgType string `json:"msgtype"` - Body string `json:"body"` - URL string `json:"url"` - Info VideoInfo `json:"info"` +func (pl *PowerLevels) Ban() int { + if pl.BanPtr != nil { + return *pl.BanPtr + } + return 50 +} + +func (pl *PowerLevels) Redact() int { + if pl.RedactPtr != nil { + return *pl.RedactPtr + } + return 50 +} + +func (pl *PowerLevels) StateDefault() int { + if pl.StateDefaultPtr != nil { + return *pl.StateDefaultPtr + } + return 50 +} + +func (pl *PowerLevels) GetUserLevel(userID string) int { + pl.usersLock.RLock() + defer pl.usersLock.RUnlock() + level, ok := pl.Users[userID] + if !ok { + return pl.UsersDefault + } + return level +} + +func (pl *PowerLevels) SetUserLevel(userID string, level int) { + pl.usersLock.Lock() + defer pl.usersLock.Unlock() + if level == pl.UsersDefault { + delete(pl.Users, userID) + } else { + pl.Users[userID] = level + } +} + +func (pl *PowerLevels) EnsureUserLevel(userID string, level int) bool { + existingLevel := pl.GetUserLevel(userID) + if existingLevel != level { + pl.SetUserLevel(userID, level) + return true + } + return false } -// ImageMessage is an m.image event -type ImageMessage struct { - MsgType string `json:"msgtype"` - Body string `json:"body"` - URL string `json:"url"` - Info ImageInfo `json:"info"` +func (pl *PowerLevels) GetEventLevel(eventType EventType) int { + pl.eventsLock.RLock() + defer pl.eventsLock.RUnlock() + level, ok := pl.Events[eventType.String()] + if !ok { + if eventType.IsState() { + return pl.StateDefault() + } + return pl.EventsDefault + } + return level } -// An HTMLMessage is the contents of a Matrix HTML formated message event. -type HTMLMessage struct { - Body string `json:"body"` - MsgType string `json:"msgtype"` - Format string `json:"format"` - FormattedBody string `json:"formatted_body"` +func (pl *PowerLevels) SetEventLevel(eventType EventType, level int) { + pl.eventsLock.Lock() + defer pl.eventsLock.Unlock() + if (eventType.IsState() && level == pl.StateDefault()) || (!eventType.IsState() && level == pl.EventsDefault) { + delete(pl.Events, eventType.String()) + } else { + pl.Events[eventType.String()] = level + } +} + +func (pl *PowerLevels) EnsureEventLevel(eventType EventType, level int) bool { + existingLevel := pl.GetEventLevel(eventType) + if existingLevel != level { + pl.SetEventLevel(eventType, level) + return true + } + return false } -var htmlRegex = regexp.MustCompile("<[^<]+?>") +type FileInfo struct { + MimeType string `json:"mimetype,omitempty"` + ThumbnailInfo *FileInfo `json:"thumbnail_info,omitempty"` + ThumbnailURL string `json:"thumbnail_url,omitempty"` + Height int `json:"h,omitempty"` + Width int `json:"w,omitempty"` + Duration uint `json:"duration,omitempty"` + Size int `json:"size,omitempty"` +} -// GetHTMLMessage returns an HTMLMessage with the body set to a stripped version of the provided HTML, in addition -// to the provided HTML. -func GetHTMLMessage(msgtype, htmlText string) HTMLMessage { - return HTMLMessage{ - Body: html.UnescapeString(htmlRegex.ReplaceAllLiteralString(htmlText, "")), - MsgType: msgtype, - Format: "org.matrix.custom.html", - FormattedBody: htmlText, +func (fileInfo *FileInfo) GetThumbnailInfo() *FileInfo { + if fileInfo.ThumbnailInfo == nil { + fileInfo.ThumbnailInfo = &FileInfo{} } + return fileInfo.ThumbnailInfo +} + +type RelatesTo struct { + InReplyTo InReplyTo `json:"m.in_reply_to,omitempty"` +} + +type InReplyTo struct { + EventID string `json:"event_id,omitempty"` + // Not required, just for future-proofing + RoomID string `json:"room_id,omitempty"` } diff --git a/vendor/maunium.net/go/gomatrix/reply.go b/vendor/maunium.net/go/gomatrix/reply.go new file mode 100644 index 0000000..6985421 --- /dev/null +++ b/vendor/maunium.net/go/gomatrix/reply.go @@ -0,0 +1,96 @@ +package gomatrix + +import ( + "fmt" + "regexp" + "strings" + + "golang.org/x/net/html" +) + +var HTMLReplyFallbackRegex = regexp.MustCompile(`^[\s\S]+?`) + +func TrimReplyFallbackHTML(html string) string { + return HTMLReplyFallbackRegex.ReplaceAllString(html, "") +} + +func TrimReplyFallbackText(text string) string { + if !strings.HasPrefix(text, "> ") || !strings.Contains(text, "\n") { + return text + } + + lines := strings.Split(text, "\n") + for len(lines) > 0 && strings.HasPrefix(lines[0], "> ") { + lines = lines[1:] + } + return strings.TrimSpace(strings.Join(lines, "\n")) +} + +func (content *Content) RemoveReplyFallback() { + if len(content.GetReplyTo()) > 0 { + if content.Format == FormatHTML { + content.FormattedBody = TrimReplyFallbackHTML(content.FormattedBody) + } + content.Body = TrimReplyFallbackText(content.Body) + } +} + +func (content *Content) GetReplyTo() string { + if content.RelatesTo != nil { + return content.RelatesTo.InReplyTo.EventID + } + return "" +} + +const ReplyFormat = `
+In reply to +%s +%s +
+` + +func (evt *Event) GenerateReplyFallbackHTML() string { + body := evt.Content.FormattedBody + if len(body) == 0 { + body = html.EscapeString(evt.Content.Body) + } + + senderDisplayName := evt.Sender + + return fmt.Sprintf(ReplyFormat, evt.RoomID, evt.ID, evt.Sender, senderDisplayName, body) +} + +func (evt *Event) GenerateReplyFallbackText() string { + body := evt.Content.Body + lines := strings.Split(strings.TrimSpace(body), "\n") + firstLine, lines := lines[0], lines[1:] + + senderDisplayName := evt.Sender + + var fallbackText strings.Builder + fmt.Fprintf(&fallbackText, "> <%s> %s", senderDisplayName, firstLine) + for _, line := range lines { + fmt.Fprintf(&fallbackText, "\n> %s", line) + } + fallbackText.WriteString("\n\n") + return fallbackText.String() +} + +func (content *Content) SetReply(inReplyTo *Event) { + if content.RelatesTo == nil { + content.RelatesTo = &RelatesTo{} + } + content.RelatesTo.InReplyTo = InReplyTo{ + EventID: inReplyTo.ID, + RoomID: inReplyTo.RoomID, + } + + if content.MsgType == MsgText || content.MsgType == MsgNotice { + if len(content.FormattedBody) == 0 || content.Format != FormatHTML { + content.FormattedBody = html.EscapeString(content.Body) + content.Format = FormatHTML + } + content.FormattedBody = inReplyTo.GenerateReplyFallbackHTML() + content.FormattedBody + content.Body = inReplyTo.GenerateReplyFallbackText() + content.Body + } +} diff --git a/vendor/maunium.net/go/gomatrix/requests.go b/vendor/maunium.net/go/gomatrix/requests.go index af99a22..d8e10a6 100644 --- a/vendor/maunium.net/go/gomatrix/requests.go +++ b/vendor/maunium.net/go/gomatrix/requests.go @@ -31,7 +31,7 @@ type ReqCreateRoom struct { Invite []string `json:"invite,omitempty"` Invite3PID []ReqInvite3PID `json:"invite_3pid,omitempty"` CreationContent map[string]interface{} `json:"creation_content,omitempty"` - InitialState []Event `json:"initial_state,omitempty"` + InitialState []*Event `json:"initial_state,omitempty"` Preset string `json:"preset,omitempty"` IsDirect bool `json:"is_direct,omitempty"` } @@ -74,5 +74,9 @@ type ReqUnbanUser struct { // ReqTyping is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid type ReqTyping struct { Typing bool `json:"typing"` - Timeout int64 `json:"timeout"` + Timeout int64 `json:"timeout,omitempty"` } + +type ReqPresence struct { + Presence string `json:"presence"` +} \ No newline at end of file diff --git a/vendor/maunium.net/go/gomatrix/responses.go b/vendor/maunium.net/go/gomatrix/responses.go index 6d43bd3..9524d62 100644 --- a/vendor/maunium.net/go/gomatrix/responses.go +++ b/vendor/maunium.net/go/gomatrix/responses.go @@ -64,7 +64,7 @@ type RespJoinedMembers struct { // RespMessages is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages type RespMessages struct { Start string `json:"start"` - Chunk []Event `json:"chunk"` + Chunk []*Event `json:"chunk"` End string `json:"end"` } diff --git a/vendor/maunium.net/go/gomatrix/room.go b/vendor/maunium.net/go/gomatrix/room.go index c9b2351..80a91d8 100644 --- a/vendor/maunium.net/go/gomatrix/room.go +++ b/vendor/maunium.net/go/gomatrix/room.go @@ -3,7 +3,7 @@ package gomatrix // Room represents a single Matrix room. type Room struct { ID string - State map[string]map[string]*Event + State map[EventType]map[string]*Event } // UpdateState updates the room's current state with the given Event. This will clobber events based @@ -17,7 +17,7 @@ func (room Room) UpdateState(event *Event) { } // GetStateEvent returns the state event for the given type/state_key combo, or nil. -func (room Room) GetStateEvent(eventType string, stateKey string) *Event { +func (room Room) GetStateEvent(eventType EventType, stateKey string) *Event { stateEventMap, _ := room.State[eventType] event, _ := stateEventMap[stateKey] return event @@ -25,17 +25,11 @@ func (room Room) GetStateEvent(eventType string, stateKey string) *Event { // GetMembershipState returns the membership state of the given user ID in this room. If there is // no entry for this member, 'leave' is returned for consistency with left users. -func (room Room) GetMembershipState(userID string) string { - state := "leave" - event := room.GetStateEvent("m.room.member", userID) +func (room Room) GetMembershipState(userID string) Membership { + state := MembershipLeave + event := room.GetStateEvent(StateMember, userID) if event != nil { - membershipState, found := event.Content["membership"] - if found { - mState, isString := membershipState.(string) - if isString { - state = mState - } - } + state = event.Content.Membership } return state } @@ -45,6 +39,6 @@ func NewRoom(roomID string) *Room { // Init the State map and return a pointer to the Room return &Room{ ID: roomID, - State: make(map[string]map[string]*Event), + State: make(map[EventType]map[string]*Event), } } diff --git a/vendor/maunium.net/go/gomatrix/sync.go b/vendor/maunium.net/go/gomatrix/sync.go index e1233a4..09170d7 100644 --- a/vendor/maunium.net/go/gomatrix/sync.go +++ b/vendor/maunium.net/go/gomatrix/sync.go @@ -25,7 +25,7 @@ type Syncer interface { type DefaultSyncer struct { UserID string Store Storer - listeners map[string][]OnEventListener // event type to listeners array + listeners map[EventType][]OnEventListener // event type to listeners array } // OnEventListener can be used with DefaultSyncer.OnEventType to be informed of incoming events. @@ -36,7 +36,7 @@ func NewDefaultSyncer(userID string, store Storer) *DefaultSyncer { return &DefaultSyncer{ UserID: userID, Store: store, - listeners: make(map[string][]OnEventListener), + listeners: make(map[EventType][]OnEventListener), } } @@ -88,7 +88,7 @@ func (s *DefaultSyncer) ProcessResponse(res *RespSync, since string) (err error) // OnEventType allows callers to be notified when there are new events for the given event type. // There are no duplicate checks. -func (s *DefaultSyncer) OnEventType(eventType string, callback OnEventListener) { +func (s *DefaultSyncer) OnEventType(eventType EventType, callback OnEventListener) { _, exists := s.listeners[eventType] if !exists { s.listeners[eventType] = []OnEventListener{} @@ -112,13 +112,8 @@ func (s *DefaultSyncer) shouldProcessResponse(resp *RespSync, since string) bool for roomID, roomData := range resp.Rooms.Join { for i := len(roomData.Timeline.Events) - 1; i >= 0; i-- { e := roomData.Timeline.Events[i] - if e.Type == "m.room.member" && e.StateKey != nil && *e.StateKey == s.UserID { - m := e.Content["membership"] - mship, ok := m.(string) - if !ok { - continue - } - if mship == "join" { + if e.Type == StateMember && e.GetStateKey() == s.UserID { + if e.Content.Membership == "join" { _, ok := resp.Rooms.Join[roomID] if !ok { continue diff --git a/vendor/maunium.net/go/maulogger/LICENSE b/vendor/maunium.net/go/maulogger/LICENSE new file mode 100644 index 0000000..c9739fb --- /dev/null +++ b/vendor/maunium.net/go/maulogger/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Tulir Asokan + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/maunium.net/go/maulogger/README.md b/vendor/maunium.net/go/maulogger/README.md new file mode 100644 index 0000000..68fe253 --- /dev/null +++ b/vendor/maunium.net/go/maulogger/README.md @@ -0,0 +1,6 @@ +# maulogger +A logger in Go. + +Docs: [godoc.org/maunium.net/go/maulogger](https://godoc.org/maunium.net/go/maulogger) + +Go get: `go get maunium.net/go/maulogger` diff --git a/vendor/maunium.net/go/maulogger/logger.go b/vendor/maunium.net/go/maulogger/logger.go new file mode 100644 index 0000000..e887237 --- /dev/null +++ b/vendor/maunium.net/go/maulogger/logger.go @@ -0,0 +1,219 @@ +package maulog + +import ( + "bufio" + "fmt" + "os" + "time" +) + +// Level is the severity level of a log entry. +type Level struct { + Name string + Severity, Color int +} + +// LogWriter writes to the log with an optional prefix +type LogWriter struct { + Level Level + Prefix string +} + +func (lw LogWriter) Write(p []byte) (n int, err error) { + log(lw.Level, fmt.Sprint(lw.Prefix, string(p))) + return len(p), nil +} + +// GetColor gets the ANSI escape color code for the log level. +func (lvl Level) GetColor() []byte { + if lvl.Color < 0 { + return []byte("") + } + return []byte(fmt.Sprintf("\x1b[%dm", lvl.Color)) +} + +// GetReset gets the ANSI escape reset code. +func (lvl Level) GetReset() []byte { + if lvl.Color < 0 { + return []byte("") + } + return []byte("\x1b[0m") +} + +var ( + // Debug is the level for debug messages. + Debug = Level{Name: "DEBUG", Color: 36, Severity: 0} + // Info is the level for basic log messages. + Info = Level{Name: "INFO", Color: -1, Severity: 10} + // Warn is the level saying that something went wrong, but the program will continue operating mostly normally. + Warn = Level{Name: "WARN", Color: 33, Severity: 50} + // Error is the level saying that something went wrong and the program may not operate as expected, but will still continue. + Error = Level{Name: "ERROR", Color: 31, Severity: 100} + // Fatal is the level saying that something went wrong and the program will not operate normally. + Fatal = Level{Name: "FATAL", Color: 35, Severity: 9001} +) + +// PrintLevel tells the first severity level at which messages should be printed to stdout +var PrintLevel = 10 + +// PrintDebug means PrintLevel = 0, kept for backwards compatibility +var PrintDebug = false + +// FileTimeformat is the time format used in log file names. +var FileTimeformat = "2006-01-02" + +// FileformatArgs is an undocumented integer. +var FileformatArgs = 3 + +// Fileformat is the format used for log file names. +var Fileformat = func(now string, i int) string { return fmt.Sprintf("%[1]s-%02[2]d.log", now, i) } + +// Timeformat is the time format used in logging. +var Timeformat = "15:04:05 02.01.2006" + +var writer *bufio.Writer +var lines int + +// InitWithWriter initializes MauLogger with the given writer. +func InitWithWriter(w *bufio.Writer) { + writer = w +} + +// Init initializes MauLogger. +func Init() { + // Find the next file name. + now := time.Now().Format(FileTimeformat) + i := 1 + for ; ; i++ { + if _, err := os.Stat(Fileformat(now, i)); os.IsNotExist(err) { + break + } + if i == 99 { + i = 1 + break + } + } + // Open the file + file, err := os.OpenFile(Fileformat(now, i), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0700) + if err != nil { + panic(err) + } + if file == nil { + panic(os.ErrInvalid) + } + // Create a writer + writer = bufio.NewWriter(file) +} + +// Debugf formats and logs a debug message. +func Debugf(message string, args ...interface{}) { + logln(Debug, fmt.Sprintf(message, args...)) +} + +// Printf formats and logs a string in the Info log level. +func Printf(message string, args ...interface{}) { + Infof(message, args...) +} + +// Infof formats and logs a string in the Info log level. +func Infof(message string, args ...interface{}) { + logln(Info, fmt.Sprintf(message, args...)) +} + +// Warnf formats and logs a string in the Warn log level. +func Warnf(message string, args ...interface{}) { + logln(Warn, fmt.Sprintf(message, args...)) +} + +// Errorf formats and logs a string in the Error log level. +func Errorf(message string, args ...interface{}) { + logln(Error, fmt.Sprintf(message, args...)) +} + +// Fatalf formats and logs a string in the Fatal log level. +func Fatalf(message string, args ...interface{}) { + logln(Fatal, fmt.Sprintf(message, args...)) +} + +// Logf formats and logs a message in the given log level. +func Logf(level Level, message string, args ...interface{}) { + logln(level, fmt.Sprintf(message, args...)) +} + +// Debugln logs a debug message. +func Debugln(args ...interface{}) { + log(Debug, fmt.Sprintln(args...)) +} + +// Println logs a string in the Info log level. +func Println(args ...interface{}) { + Infoln(args...) +} + +// Infoln logs a string in the Info log level. +func Infoln(args ...interface{}) { + log(Info, fmt.Sprintln(args...)) +} + +// Warnln logs a string in the Warn log level. +func Warnln(args ...interface{}) { + log(Warn, fmt.Sprintln(args...)) +} + +// Errorln logs a string in the Error log level. +func Errorln(args ...interface{}) { + log(Error, fmt.Sprintln(args...)) +} + +// Fatalln logs a string in the Fatal log level. +func Fatalln(args ...interface{}) { + log(Fatal, fmt.Sprintln(args...)) +} + +// Logln logs a message in the given log level. +func Logln(level Level, args ...interface{}) { + log(level, fmt.Sprintln(args...)) +} + +func logln(level Level, message string) { + log(level, fmt.Sprintln(message)) +} + +func log(level Level, message string) { + // Prefix the message with the timestamp and log level. + msg := []byte(fmt.Sprintf("[%[1]s] [%[2]s] %[3]s", time.Now().Format(Timeformat), level.Name, message)) + + if writer != nil { + // Write it to the log file. + _, err := writer.Write(msg) + if err != nil { + panic(err) + } + lines++ + // Flush the file if needed + if lines == 5 { + lines = 0 + writer.Flush() + } + } + + // Print to stdout using correct color + if level.Severity >= PrintLevel || PrintDebug { + if level.Severity >= Error.Severity { + os.Stderr.Write(level.GetColor()) + os.Stderr.Write(msg) + os.Stderr.Write(level.GetReset()) + } else { + os.Stdout.Write(level.GetColor()) + os.Stdout.Write(msg) + os.Stdout.Write(level.GetReset()) + } + } +} + +// Shutdown cleans up the logger. +func Shutdown() { + if writer != nil { + writer.Flush() + } +} -- cgit v1.2.3