Compare commits
	
		
			16 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | ad4d461606 | ||
|   | 67905089ba | ||
|   | f2483af561 | ||
|   | c28b87641e | ||
|   | f8e6a69d6e | ||
|   | 54216cec4b | ||
|   | 12989bbd99 | ||
|   | 38d09dba2e | ||
|   | fafd0c68e9 | ||
|   | 41195c8e48 | ||
|   | a97804548e | ||
|   | ba653c0841 | ||
|   | 5b191f78a0 | ||
|   | 83ef61287e | ||
|   | 3527e09bc5 | ||
|   | ddc5b3268f | 
| @@ -35,7 +35,7 @@ before_script: | |||||||
| # set -e enabled in bash.  | # set -e enabled in bash.  | ||||||
| script: | script: | ||||||
|   - test -z $(gofmt -s -l $GO_FILES)  # Fail if a .go file hasn't been formatted with gofmt |   - test -z $(gofmt -s -l $GO_FILES)  # Fail if a .go file hasn't been formatted with gofmt | ||||||
|   #- go test -v -race $PKGS            # Run all the tests with the race detector enabled |   - go test -v -race $PKGS            # Run all the tests with the race detector enabled | ||||||
|   - go vet $PKGS                      # go vet is the official Go static analyzer |   - go vet $PKGS                      # go vet is the official Go static analyzer | ||||||
|   - megacheck $PKGS                   # "go vet on steroids" + linter |   - megacheck $PKGS                   # "go vet on steroids" + linter | ||||||
|   - /bin/bash ci/bintray.sh |   - /bin/bash ci/bintray.sh | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
								
							| @@ -13,6 +13,7 @@ Has a REST API. | |||||||
| # Table of Contents | # Table of Contents | ||||||
|  * [Features](#features) |  * [Features](#features) | ||||||
|  * [Requirements](#requirements) |  * [Requirements](#requirements) | ||||||
|  |  * [Screenshots](https://github.com/42wim/matterbridge/wiki/) | ||||||
|  * [Installing](#installing) |  * [Installing](#installing) | ||||||
|    * [Binaries](#binaries) |    * [Binaries](#binaries) | ||||||
|    * [Building](#building) |    * [Building](#building) | ||||||
| @@ -47,9 +48,12 @@ Accounts to one of the supported bridges | |||||||
| * [Matrix](https://matrix.org) | * [Matrix](https://matrix.org) | ||||||
| * [Steam](https://store.steampowered.com/) | * [Steam](https://store.steampowered.com/) | ||||||
|  |  | ||||||
|  | # Screenshots | ||||||
|  | See https://github.com/42wim/matterbridge/wiki | ||||||
|  |  | ||||||
| # Installing | # Installing | ||||||
| ## Binaries | ## Binaries | ||||||
| * Latest stable release [v0.16.3](https://github.com/42wim/matterbridge/releases/latest) | * Latest stable release [v1.0.0](https://github.com/42wim/matterbridge/releases/latest) | ||||||
| * Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)   | * Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)   | ||||||
|  |  | ||||||
| ## Building | ## Building | ||||||
| @@ -160,6 +164,10 @@ See [changelog.md](https://github.com/42wim/matterbridge/blob/master/changelog.m | |||||||
|  |  | ||||||
| See [FAQ](https://github.com/42wim/matterbridge/wiki/FAQ) | See [FAQ](https://github.com/42wim/matterbridge/wiki/FAQ) | ||||||
|  |  | ||||||
|  | Want to tip ?  | ||||||
|  | * eth: 0xb3f9b5387c66ad6be892bcb7bbc67862f3abc16f | ||||||
|  | * btc: 1N7cKHj5SfqBHBzDJ6kad4BzeqUBBS2zhs | ||||||
|  |  | ||||||
| # Thanks | # Thanks | ||||||
| Matterbridge wouldn't exist without these libraries: | Matterbridge wouldn't exist without these libraries: | ||||||
| * discord - https://github.com/bwmarrin/discordgo | * discord - https://github.com/bwmarrin/discordgo | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ const ( | |||||||
| 	EVENT_JOIN_LEAVE      = "join_leave" | 	EVENT_JOIN_LEAVE      = "join_leave" | ||||||
| 	EVENT_FAILURE         = "failure" | 	EVENT_FAILURE         = "failure" | ||||||
| 	EVENT_REJOIN_CHANNELS = "rejoin_channels" | 	EVENT_REJOIN_CHANNELS = "rejoin_channels" | ||||||
|  | 	EVENT_USER_ACTION     = "user_action" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type Message struct { | type Message struct { | ||||||
| @@ -33,7 +34,6 @@ type ChannelInfo struct { | |||||||
| 	Account     string | 	Account     string | ||||||
| 	Direction   string | 	Direction   string | ||||||
| 	ID          string | 	ID          string | ||||||
| 	GID         map[string]bool |  | ||||||
| 	SameChannel map[string]bool | 	SameChannel map[string]bool | ||||||
| 	Options     ChannelOptions | 	Options     ChannelOptions | ||||||
| } | } | ||||||
| @@ -77,6 +77,7 @@ type Protocol struct { | |||||||
| 	UseSASL                bool   // IRC | 	UseSASL                bool   // IRC | ||||||
| 	UseTLS                 bool   // IRC | 	UseTLS                 bool   // IRC | ||||||
| 	UseFirstName           bool   // telegram | 	UseFirstName           bool   // telegram | ||||||
|  | 	UseUserName            bool   // discord | ||||||
| 	UseInsecureURL         bool   // telegram | 	UseInsecureURL         bool   // telegram | ||||||
| 	WebhookBindAddress     string // mattermost, slack | 	WebhookBindAddress     string // mattermost, slack | ||||||
| 	WebhookURL             string // mattermost, slack | 	WebhookURL             string // mattermost, slack | ||||||
|   | |||||||
| @@ -114,6 +114,9 @@ func (b *bdiscord) Send(msg config.Message) error { | |||||||
| 		flog.Errorf("Could not find channelID for %v", msg.Channel) | 		flog.Errorf("Could not find channelID for %v", msg.Channel) | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  | 	if msg.Event == config.EVENT_USER_ACTION { | ||||||
|  | 		msg.Text = "_" + msg.Text + "_" | ||||||
|  | 	} | ||||||
| 	if b.Config.WebhookURL == "" { | 	if b.Config.WebhookURL == "" { | ||||||
| 		flog.Debugf("Broadcasting using token (API)") | 		flog.Debugf("Broadcasting using token (API)") | ||||||
| 		b.c.ChannelMessageSend(channelID, msg.Username+msg.Text) | 		b.c.ChannelMessageSend(channelID, msg.Username+msg.Text) | ||||||
| @@ -171,11 +174,19 @@ func (b *bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat | |||||||
| 		text = m.ContentWithMentionsReplaced() | 		text = m.ContentWithMentionsReplaced() | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	channelName := b.getChannelName(m.ChannelID) | 	rmsg := config.Message{Account: b.Account, Avatar: "https://cdn.discordapp.com/avatars/" + m.Author.ID + "/" + m.Author.Avatar + ".jpg", | ||||||
|  | 		UserID: m.Author.ID} | ||||||
|  |  | ||||||
|  | 	rmsg.Channel = b.getChannelName(m.ChannelID) | ||||||
| 	if b.UseChannelID { | 	if b.UseChannelID { | ||||||
| 		channelName = "ID:" + m.ChannelID | 		rmsg.Channel = "ID:" + m.ChannelID | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !b.Config.UseUserName { | ||||||
|  | 		rmsg.Username = b.getNick(m.Author) | ||||||
|  | 	} else { | ||||||
|  | 		rmsg.Username = m.Author.Username | ||||||
| 	} | 	} | ||||||
| 	username := b.getNick(m.Author) |  | ||||||
|  |  | ||||||
| 	if b.Config.ShowEmbeds && m.Message.Embeds != nil { | 	if b.Config.ShowEmbeds && m.Message.Embeds != nil { | ||||||
| 		for _, embed := range m.Message.Embeds { | 		for _, embed := range m.Message.Embeds { | ||||||
| @@ -188,10 +199,14 @@ func (b *bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	text, ok := b.replaceAction(text) | ||||||
|  | 	if ok { | ||||||
|  | 		rmsg.Event = config.EVENT_USER_ACTION | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	rmsg.Text = text | ||||||
| 	flog.Debugf("Sending message from %s on %s to gateway", m.Author.Username, b.Account) | 	flog.Debugf("Sending message from %s on %s to gateway", m.Author.Username, b.Account) | ||||||
| 	b.Remote <- config.Message{Username: username, Text: text, Channel: channelName, | 	b.Remote <- rmsg | ||||||
| 		Account: b.Account, Avatar: "https://cdn.discordapp.com/avatars/" + m.Author.ID + "/" + m.Author.Avatar + ".jpg", |  | ||||||
| 		UserID: m.Author.ID} |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *bdiscord) memberUpdate(s *discordgo.Session, m *discordgo.GuildMemberUpdate) { | func (b *bdiscord) memberUpdate(s *discordgo.Session, m *discordgo.GuildMemberUpdate) { | ||||||
| @@ -283,6 +298,13 @@ func (b *bdiscord) replaceChannelMentions(text string) string { | |||||||
| 	return text | 	return text | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (b *bdiscord) replaceAction(text string) (string, bool) { | ||||||
|  | 	if strings.HasPrefix(text, "_") && strings.HasSuffix(text, "_") { | ||||||
|  | 		return strings.Replace(text, "_", "", -1), true | ||||||
|  | 	} | ||||||
|  | 	return text, false | ||||||
|  | } | ||||||
|  |  | ||||||
| func (b *bdiscord) stripCustomoji(text string) string { | func (b *bdiscord) stripCustomoji(text string) string { | ||||||
| 	// <:doge:302803592035958784> | 	// <:doge:302803592035958784> | ||||||
| 	re := regexp.MustCompile("<(:.*?:)[0-9]+>") | 	re := regexp.MustCompile("<(:.*?:)[0-9]+>") | ||||||
|   | |||||||
| @@ -81,8 +81,13 @@ func (b *Bgitter) JoinChannel(channel string) error { | |||||||
| 				// check for ZWSP to see if it's not an echo | 				// check for ZWSP to see if it's not an echo | ||||||
| 				if !strings.HasSuffix(ev.Message.Text, "") { | 				if !strings.HasSuffix(ev.Message.Text, "") { | ||||||
| 					flog.Debugf("Sending message from %s on %s to gateway", ev.Message.From.Username, b.Account) | 					flog.Debugf("Sending message from %s on %s to gateway", ev.Message.From.Username, b.Account) | ||||||
| 					b.Remote <- config.Message{Username: ev.Message.From.Username, Text: ev.Message.Text, Channel: room, | 					rmsg := config.Message{Username: ev.Message.From.Username, Text: ev.Message.Text, Channel: room, | ||||||
| 						Account: b.Account, Avatar: b.getAvatar(ev.Message.From.Username), UserID: ev.Message.From.ID} | 						Account: b.Account, Avatar: b.getAvatar(ev.Message.From.Username), UserID: ev.Message.From.ID} | ||||||
|  | 					if strings.HasPrefix(ev.Message.Text, "@"+ev.Message.From.Username) { | ||||||
|  | 						rmsg.Event = config.EVENT_USER_ACTION | ||||||
|  | 						rmsg.Text = strings.Replace(rmsg.Text, "@"+ev.Message.From.Username+" ", "", -1) | ||||||
|  | 					} | ||||||
|  | 					b.Remote <- rmsg | ||||||
| 				} | 				} | ||||||
| 			case *gitter.GitterConnectionClosed: | 			case *gitter.GitterConnectionClosed: | ||||||
| 				flog.Errorf("connection with gitter closed for room %s", room) | 				flog.Errorf("connection with gitter closed for room %s", room) | ||||||
|   | |||||||
| @@ -3,13 +3,13 @@ package birc | |||||||
| import ( | import ( | ||||||
| 	"crypto/tls" | 	"crypto/tls" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"github.com/42wim/go-ircevent" | ||||||
| 	"github.com/42wim/matterbridge/bridge/config" | 	"github.com/42wim/matterbridge/bridge/config" | ||||||
| 	log "github.com/Sirupsen/logrus" | 	log "github.com/Sirupsen/logrus" | ||||||
| 	"github.com/paulrosania/go-charset/charset" | 	"github.com/paulrosania/go-charset/charset" | ||||||
| 	_ "github.com/paulrosania/go-charset/data" | 	_ "github.com/paulrosania/go-charset/data" | ||||||
| 	"github.com/saintfish/chardet" | 	"github.com/saintfish/chardet" | ||||||
| 	ircm "github.com/sorcix/irc" | 	ircm "github.com/sorcix/irc" | ||||||
| 	"github.com/thoj/go-ircevent" |  | ||||||
| 	"io" | 	"io" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
| 	"regexp" | 	"regexp" | ||||||
| @@ -124,9 +124,6 @@ func (b *Birc) JoinChannel(channel string) error { | |||||||
|  |  | ||||||
| func (b *Birc) Send(msg config.Message) error { | func (b *Birc) Send(msg config.Message) error { | ||||||
| 	flog.Debugf("Receiving %#v", msg) | 	flog.Debugf("Receiving %#v", msg) | ||||||
| 	if msg.Account == b.Account { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	if strings.HasPrefix(msg.Text, "!") { | 	if strings.HasPrefix(msg.Text, "!") { | ||||||
| 		b.Command(&msg) | 		b.Command(&msg) | ||||||
| 	} | 	} | ||||||
| @@ -138,7 +135,7 @@ func (b *Birc) Send(msg config.Message) error { | |||||||
| 			if len(b.Local) == b.Config.MessageQueue-1 { | 			if len(b.Local) == b.Config.MessageQueue-1 { | ||||||
| 				text = text + " <message clipped>" | 				text = text + " <message clipped>" | ||||||
| 			} | 			} | ||||||
| 			b.Local <- config.Message{Text: text, Username: msg.Username, Channel: msg.Channel} | 			b.Local <- config.Message{Text: text, Username: msg.Username, Channel: msg.Channel, Event: msg.Event} | ||||||
| 		} else { | 		} else { | ||||||
| 			flog.Debugf("flooding, dropping message (queue at %d)", len(b.Local)) | 			flog.Debugf("flooding, dropping message (queue at %d)", len(b.Local)) | ||||||
| 		} | 		} | ||||||
| @@ -151,7 +148,11 @@ func (b *Birc) doSend() { | |||||||
| 	throttle := time.NewTicker(rate) | 	throttle := time.NewTicker(rate) | ||||||
| 	for msg := range b.Local { | 	for msg := range b.Local { | ||||||
| 		<-throttle.C | 		<-throttle.C | ||||||
| 		b.i.Privmsg(msg.Channel, msg.Username+msg.Text) | 		if msg.Event == config.EVENT_USER_ACTION { | ||||||
|  | 			b.i.Action(msg.Channel, msg.Username+msg.Text) | ||||||
|  | 		} else { | ||||||
|  | 			b.i.Privmsg(msg.Channel, msg.Username+msg.Text) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -247,10 +248,12 @@ func (b *Birc) handlePrivMsg(event *irc.Event) { | |||||||
| 	if event.Nick == b.Nick { | 	if event.Nick == b.Nick { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  | 	rmsg := config.Message{Username: event.Nick, Channel: event.Arguments[0], Account: b.Account, UserID: event.User + "@" + event.Host} | ||||||
| 	flog.Debugf("handlePrivMsg() %s %s %#v", event.Nick, event.Message(), event) | 	flog.Debugf("handlePrivMsg() %s %s %#v", event.Nick, event.Message(), event) | ||||||
| 	msg := "" | 	msg := "" | ||||||
| 	if event.Code == "CTCP_ACTION" { | 	if event.Code == "CTCP_ACTION" { | ||||||
| 		msg = event.Nick + " " | 		//	msg = event.Nick + " " | ||||||
|  | 		rmsg.Event = config.EVENT_USER_ACTION | ||||||
| 	} | 	} | ||||||
| 	msg += event.Message() | 	msg += event.Message() | ||||||
| 	// strip IRC colors | 	// strip IRC colors | ||||||
| @@ -279,7 +282,8 @@ func (b *Birc) handlePrivMsg(event *irc.Event) { | |||||||
| 	msg = string(output) | 	msg = string(output) | ||||||
|  |  | ||||||
| 	flog.Debugf("Sending message from %s on %s to gateway", event.Arguments[0], b.Account) | 	flog.Debugf("Sending message from %s on %s to gateway", event.Arguments[0], b.Account) | ||||||
| 	b.Remote <- config.Message{Username: event.Nick, Text: msg, Channel: event.Arguments[0], Account: b.Account, UserID: event.User + "@" + event.Host} | 	rmsg.Text = msg | ||||||
|  | 	b.Remote <- rmsg | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Birc) handleTopicWhoTime(event *irc.Event) { | func (b *Birc) handleTopicWhoTime(event *irc.Event) { | ||||||
|   | |||||||
| @@ -78,6 +78,11 @@ func (b *Bmatrix) Send(msg config.Message) error { | |||||||
| 	flog.Debugf("Receiving %#v", msg) | 	flog.Debugf("Receiving %#v", msg) | ||||||
| 	channel := b.getRoomID(msg.Channel) | 	channel := b.getRoomID(msg.Channel) | ||||||
| 	flog.Debugf("Sending to channel %s", channel) | 	flog.Debugf("Sending to channel %s", channel) | ||||||
|  | 	if msg.Event == config.EVENT_USER_ACTION { | ||||||
|  | 		b.mc.SendMessageEvent(channel, "m.room.message", | ||||||
|  | 			matrix.TextMessage{"m.emote", msg.Username + msg.Text}) | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
| 	b.mc.SendText(channel, msg.Username+msg.Text) | 	b.mc.SendText(channel, msg.Username+msg.Text) | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| @@ -95,7 +100,7 @@ func (b *Bmatrix) getRoomID(channel string) string { | |||||||
| func (b *Bmatrix) handlematrix() error { | func (b *Bmatrix) handlematrix() error { | ||||||
| 	syncer := b.mc.Syncer.(*matrix.DefaultSyncer) | 	syncer := b.mc.Syncer.(*matrix.DefaultSyncer) | ||||||
| 	syncer.OnEventType("m.room.message", func(ev *matrix.Event) { | 	syncer.OnEventType("m.room.message", func(ev *matrix.Event) { | ||||||
| 		if ev.Content["msgtype"].(string) == "m.text" && ev.Sender != b.UserID { | 		if (ev.Content["msgtype"].(string) == "m.text" || ev.Content["msgtype"].(string) == "m.emote") && ev.Sender != b.UserID { | ||||||
| 			b.RLock() | 			b.RLock() | ||||||
| 			channel, ok := b.RoomMap[ev.RoomID] | 			channel, ok := b.RoomMap[ev.RoomID] | ||||||
| 			b.RUnlock() | 			b.RUnlock() | ||||||
| @@ -108,8 +113,12 @@ func (b *Bmatrix) handlematrix() error { | |||||||
| 				re := regexp.MustCompile("(.*?):.*") | 				re := regexp.MustCompile("(.*?):.*") | ||||||
| 				username = re.ReplaceAllString(username, `$1`) | 				username = re.ReplaceAllString(username, `$1`) | ||||||
| 			} | 			} | ||||||
|  | 			rmsg := config.Message{Username: username, Text: ev.Content["body"].(string), Channel: channel, Account: b.Account, UserID: ev.Sender} | ||||||
|  | 			if ev.Content["msgtype"].(string) == "m.emote" { | ||||||
|  | 				rmsg.Event = config.EVENT_USER_ACTION | ||||||
|  | 			} | ||||||
| 			flog.Debugf("Sending message from %s on %s to gateway", ev.Sender, b.Account) | 			flog.Debugf("Sending message from %s on %s to gateway", ev.Sender, b.Account) | ||||||
| 			b.Remote <- config.Message{Username: username, Text: ev.Content["body"].(string), Channel: channel, Account: b.Account, UserID: ev.Sender} | 			b.Remote <- rmsg | ||||||
| 		} | 		} | ||||||
| 		flog.Debugf("Received: %#v", ev) | 		flog.Debugf("Received: %#v", ev) | ||||||
| 	}) | 	}) | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ import ( | |||||||
| 	"github.com/42wim/matterbridge/matterclient" | 	"github.com/42wim/matterbridge/matterclient" | ||||||
| 	"github.com/42wim/matterbridge/matterhook" | 	"github.com/42wim/matterbridge/matterhook" | ||||||
| 	log "github.com/Sirupsen/logrus" | 	log "github.com/Sirupsen/logrus" | ||||||
|  | 	"strings" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type MMhook struct { | type MMhook struct { | ||||||
| @@ -117,6 +118,9 @@ func (b *Bmattermost) JoinChannel(channel string) error { | |||||||
|  |  | ||||||
| func (b *Bmattermost) Send(msg config.Message) error { | func (b *Bmattermost) Send(msg config.Message) error { | ||||||
| 	flog.Debugf("Receiving %#v", msg) | 	flog.Debugf("Receiving %#v", msg) | ||||||
|  | 	if msg.Event == config.EVENT_USER_ACTION { | ||||||
|  | 		msg.Text = "*" + msg.Text + "*" | ||||||
|  | 	} | ||||||
| 	nick := msg.Username | 	nick := msg.Username | ||||||
| 	message := msg.Text | 	message := msg.Text | ||||||
| 	channel := msg.Channel | 	channel := msg.Channel | ||||||
| @@ -152,8 +156,14 @@ func (b *Bmattermost) handleMatter() { | |||||||
| 		go b.handleMatterClient(mchan) | 		go b.handleMatterClient(mchan) | ||||||
| 	} | 	} | ||||||
| 	for message := range mchan { | 	for message := range mchan { | ||||||
|  | 		rmsg := config.Message{Username: message.Username, Channel: message.Channel, Account: b.Account, UserID: message.UserID} | ||||||
|  | 		text, ok := b.replaceAction(message.Text) | ||||||
|  | 		if ok { | ||||||
|  | 			rmsg.Event = config.EVENT_USER_ACTION | ||||||
|  | 		} | ||||||
|  | 		rmsg.Text = text | ||||||
| 		flog.Debugf("Sending message from %s on %s to gateway", message.Username, b.Account) | 		flog.Debugf("Sending message from %s on %s to gateway", message.Username, b.Account) | ||||||
| 		b.Remote <- config.Message{Text: message.Text, Username: message.Username, Channel: message.Channel, Account: b.Account, UserID: message.UserID} | 		b.Remote <- rmsg | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -174,6 +184,10 @@ func (b *Bmattermost) handleMatterClient(mchan chan *MMMessage) { | |||||||
| 		// only listen to message from our team | 		// only listen to message from our team | ||||||
| 		if (message.Raw.Event == "posted" || message.Raw.Event == "post_edited") && | 		if (message.Raw.Event == "posted" || message.Raw.Event == "post_edited") && | ||||||
| 			b.mc.User.Username != message.Username && message.Raw.Data["team_id"].(string) == b.TeamId { | 			b.mc.User.Username != message.Username && message.Raw.Data["team_id"].(string) == b.TeamId { | ||||||
|  | 			// if the message has reactions don't repost it (for now, until we can correlate reaction with message) | ||||||
|  | 			if message.Post.HasReactions { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
| 			flog.Debugf("Receiving from matterclient %#v", message) | 			flog.Debugf("Receiving from matterclient %#v", message) | ||||||
| 			m := &MMMessage{} | 			m := &MMMessage{} | ||||||
| 			m.UserID = message.UserID | 			m.UserID = message.UserID | ||||||
| @@ -222,3 +236,10 @@ func (b *Bmattermost) apiLogin() error { | |||||||
| 	go b.mc.StatusLoop() | 	go b.mc.StatusLoop() | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (b *Bmattermost) replaceAction(text string) (string, bool) { | ||||||
|  | 	if strings.HasPrefix(text, "*") && strings.HasSuffix(text, "*") { | ||||||
|  | 		return strings.Replace(text, "*", "", -1), true | ||||||
|  | 	} | ||||||
|  | 	return text, false | ||||||
|  | } | ||||||
|   | |||||||
| @@ -127,6 +127,9 @@ func (b *Bslack) JoinChannel(channel string) error { | |||||||
|  |  | ||||||
| func (b *Bslack) Send(msg config.Message) error { | func (b *Bslack) Send(msg config.Message) error { | ||||||
| 	flog.Debugf("Receiving %#v", msg) | 	flog.Debugf("Receiving %#v", msg) | ||||||
|  | 	if msg.Event == config.EVENT_USER_ACTION { | ||||||
|  | 		msg.Text = "_" + msg.Text + "_" | ||||||
|  | 	} | ||||||
| 	nick := msg.Username | 	nick := msg.Username | ||||||
| 	message := msg.Text | 	message := msg.Text | ||||||
| 	channel := msg.Channel | 	channel := msg.Channel | ||||||
| @@ -231,6 +234,9 @@ func (b *Bslack) handleSlack() { | |||||||
| 			text = html.UnescapeString(text) | 			text = html.UnescapeString(text) | ||||||
| 			flog.Debugf("Sending message from %s on %s to gateway", message.Username, b.Account) | 			flog.Debugf("Sending message from %s on %s to gateway", message.Username, b.Account) | ||||||
| 			msg := config.Message{Text: text, Username: message.Username, Channel: message.Channel, Account: b.Account, Avatar: b.getAvatar(message.Username), UserID: message.UserID} | 			msg := config.Message{Text: text, Username: message.Username, Channel: message.Channel, Account: b.Account, Avatar: b.getAvatar(message.Username), UserID: message.UserID} | ||||||
|  | 			if message.Raw.SubType == "me_message" { | ||||||
|  | 				msg.Event = config.EVENT_USER_ACTION | ||||||
|  | 			} | ||||||
| 			b.Remote <- msg | 			b.Remote <- msg | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -77,6 +77,7 @@ func (b *Btelegram) Send(msg config.Message) error { | |||||||
|  |  | ||||||
| func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) { | func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) { | ||||||
| 	for update := range updates { | 	for update := range updates { | ||||||
|  | 		flog.Debugf("Receiving from telegram: %#v", update.Message) | ||||||
| 		var message *tgbotapi.Message | 		var message *tgbotapi.Message | ||||||
| 		username := "" | 		username := "" | ||||||
| 		channel := "" | 		channel := "" | ||||||
|   | |||||||
| @@ -133,6 +133,7 @@ func (b *Bxmpp) xmppKeepAlive() chan bool { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Bxmpp) handleXmpp() error { | func (b *Bxmpp) handleXmpp() error { | ||||||
|  | 	var ok bool | ||||||
| 	done := b.xmppKeepAlive() | 	done := b.xmppKeepAlive() | ||||||
| 	defer close(done) | 	defer close(done) | ||||||
| 	nodelay := time.Time{} | 	nodelay := time.Time{} | ||||||
| @@ -154,8 +155,13 @@ func (b *Bxmpp) handleXmpp() error { | |||||||
| 					nick = s[1] | 					nick = s[1] | ||||||
| 				} | 				} | ||||||
| 				if nick != b.Config.Nick && v.Stamp == nodelay && v.Text != "" { | 				if nick != b.Config.Nick && v.Stamp == nodelay && v.Text != "" { | ||||||
|  | 					rmsg := config.Message{Username: nick, Text: v.Text, Channel: channel, Account: b.Account, UserID: v.Remote} | ||||||
|  | 					rmsg.Text, ok = b.replaceAction(rmsg.Text) | ||||||
|  | 					if ok { | ||||||
|  | 						rmsg.Event = config.EVENT_USER_ACTION | ||||||
|  | 					} | ||||||
| 					flog.Debugf("Sending message from %s on %s to gateway", nick, b.Account) | 					flog.Debugf("Sending message from %s on %s to gateway", nick, b.Account) | ||||||
| 					b.Remote <- config.Message{Username: nick, Text: v.Text, Channel: channel, Account: b.Account, UserID: v.Remote} | 					b.Remote <- rmsg | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		case xmpp.Presence: | 		case xmpp.Presence: | ||||||
| @@ -163,3 +169,10 @@ func (b *Bxmpp) handleXmpp() error { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (b *Bxmpp) replaceAction(text string) (string, bool) { | ||||||
|  | 	if strings.HasPrefix(text, "/me ") { | ||||||
|  | 		return strings.Replace(text, "/me ", "", -1), true | ||||||
|  | 	} | ||||||
|  | 	return text, false | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								changelog.md
									
									
									
									
									
								
							| @@ -1,3 +1,18 @@ | |||||||
|  | # v1.0.0 | ||||||
|  | ## New features | ||||||
|  | * general: Add action support for slack,mattermost,irc,gitter,matrix,xmpp,discord. #199 | ||||||
|  | * discord: Shows the username instead of the server nickname #234 | ||||||
|  |  | ||||||
|  | # v1.0.0-rc1 | ||||||
|  | ## New features | ||||||
|  | * general: Add action support for slack,mattermost,irc,gitter,matrix,xmpp,discord. #199 | ||||||
|  |  | ||||||
|  | ## Bugfix | ||||||
|  | * general: Handle same account in multiple gateways better | ||||||
|  | * mattermost: ignore edited messages with reactions | ||||||
|  | * mattermost: Fix double posting of edited messages by using lru cache | ||||||
|  | * irc: update vendor | ||||||
|  |  | ||||||
| # v0.16.3 | # v0.16.3 | ||||||
| ## Bugfix | ## Bugfix | ||||||
| * general: Fix in/out logic. Closes #224  | * general: Fix in/out logic. Closes #224  | ||||||
|   | |||||||
| @@ -5,8 +5,8 @@ import ( | |||||||
| 	"github.com/42wim/matterbridge/bridge" | 	"github.com/42wim/matterbridge/bridge" | ||||||
| 	"github.com/42wim/matterbridge/bridge/config" | 	"github.com/42wim/matterbridge/bridge/config" | ||||||
| 	log "github.com/Sirupsen/logrus" | 	log "github.com/Sirupsen/logrus" | ||||||
| 	"github.com/peterhellberg/emojilib" |  | ||||||
| 	//	"github.com/davecgh/go-spew/spew" | 	//	"github.com/davecgh/go-spew/spew" | ||||||
|  | 	"github.com/peterhellberg/emojilib" | ||||||
| 	"regexp" | 	"regexp" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
| @@ -14,62 +14,33 @@ import ( | |||||||
|  |  | ||||||
| type Gateway struct { | type Gateway struct { | ||||||
| 	*config.Config | 	*config.Config | ||||||
| 	MyConfig        *config.Gateway | 	Router         *Router | ||||||
| 	Bridges         map[string]*bridge.Bridge | 	MyConfig       *config.Gateway | ||||||
| 	Channels        map[string]*config.ChannelInfo | 	Bridges        map[string]*bridge.Bridge | ||||||
| 	ChannelOptions  map[string]config.ChannelOptions | 	Channels       map[string]*config.ChannelInfo | ||||||
| 	Names           map[string]bool | 	ChannelOptions map[string]config.ChannelOptions | ||||||
| 	Name            string | 	Message        chan config.Message | ||||||
| 	Message         chan config.Message | 	Name           string | ||||||
| 	DestChannelFunc func(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func New(cfg *config.Config) *Gateway { | func New(cfg config.Gateway, r *Router) *Gateway { | ||||||
| 	gw := &Gateway{} | 	gw := &Gateway{Channels: make(map[string]*config.ChannelInfo), Message: r.Message, | ||||||
| 	gw.Config = cfg | 		Router: r, Bridges: make(map[string]*bridge.Bridge), Config: r.Config} | ||||||
| 	gw.Channels = make(map[string]*config.ChannelInfo) | 	gw.AddConfig(&cfg) | ||||||
| 	gw.Message = make(chan config.Message) |  | ||||||
| 	gw.Bridges = make(map[string]*bridge.Bridge) |  | ||||||
| 	gw.Names = make(map[string]bool) |  | ||||||
| 	gw.DestChannelFunc = gw.getDestChannel |  | ||||||
| 	return gw | 	return gw | ||||||
| } | } | ||||||
|  |  | ||||||
| func (gw *Gateway) AddBridge(cfg *config.Bridge) error { | func (gw *Gateway) AddBridge(cfg *config.Bridge) error { | ||||||
| 	for _, br := range gw.Bridges { | 	br := gw.Router.getBridge(cfg.Account) | ||||||
| 		if br.Account == cfg.Account { | 	if br == nil { | ||||||
| 			gw.mapChannelsToBridge(br) | 		br = bridge.New(gw.Config, cfg, gw.Message) | ||||||
| 			err := br.JoinChannels() |  | ||||||
| 			if err != nil { |  | ||||||
| 				return fmt.Errorf("Bridge %s failed to join channel: %v", br.Account, err) |  | ||||||
| 			} |  | ||||||
| 			return nil |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 	log.Infof("Starting bridge: %s ", cfg.Account) |  | ||||||
| 	br := bridge.New(gw.Config, cfg, gw.Message) |  | ||||||
| 	gw.mapChannelsToBridge(br) | 	gw.mapChannelsToBridge(br) | ||||||
| 	gw.Bridges[cfg.Account] = br | 	gw.Bridges[cfg.Account] = br | ||||||
| 	err := br.Connect() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return fmt.Errorf("Bridge %s failed to start: %v", br.Account, err) |  | ||||||
| 	} |  | ||||||
| 	err = br.JoinChannels() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return fmt.Errorf("Bridge %s failed to join channel: %v", br.Account, err) |  | ||||||
| 	} |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (gw *Gateway) AddConfig(cfg *config.Gateway) error { | func (gw *Gateway) AddConfig(cfg *config.Gateway) error { | ||||||
| 	if gw.Names[cfg.Name] { |  | ||||||
| 		return fmt.Errorf("Gateway with name %s already exists", cfg.Name) |  | ||||||
| 	} |  | ||||||
| 	if cfg.Name == "" { |  | ||||||
| 		return fmt.Errorf("%s", "Gateway without name found") |  | ||||||
| 	} |  | ||||||
| 	log.Infof("Starting gateway: %s", cfg.Name) |  | ||||||
| 	gw.Names[cfg.Name] = true |  | ||||||
| 	gw.Name = cfg.Name | 	gw.Name = cfg.Name | ||||||
| 	gw.MyConfig = cfg | 	gw.MyConfig = cfg | ||||||
| 	gw.mapChannels() | 	gw.mapChannels() | ||||||
| @@ -90,39 +61,6 @@ func (gw *Gateway) mapChannelsToBridge(br *bridge.Bridge) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (gw *Gateway) Start() error { |  | ||||||
| 	go gw.handleReceive() |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (gw *Gateway) handleReceive() { |  | ||||||
| 	for msg := range gw.Message { |  | ||||||
| 		if msg.Event == config.EVENT_FAILURE { |  | ||||||
| 			for _, br := range gw.Bridges { |  | ||||||
| 				if msg.Account == br.Account { |  | ||||||
| 					go gw.reconnectBridge(br) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if msg.Event == config.EVENT_REJOIN_CHANNELS { |  | ||||||
| 			for _, br := range gw.Bridges { |  | ||||||
| 				if msg.Account == br.Account { |  | ||||||
| 					br.Joined = make(map[string]bool) |  | ||||||
| 					br.JoinChannels() |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		if !gw.ignoreMessage(&msg) { |  | ||||||
| 			msg.Timestamp = time.Now() |  | ||||||
| 			gw.modifyMessage(&msg) |  | ||||||
| 			for _, br := range gw.Bridges { |  | ||||||
| 				gw.handleMessage(msg, br) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (gw *Gateway) reconnectBridge(br *bridge.Bridge) { | func (gw *Gateway) reconnectBridge(br *bridge.Bridge) { | ||||||
| 	br.Disconnect() | 	br.Disconnect() | ||||||
| 	time.Sleep(time.Second * 5) | 	time.Sleep(time.Second * 5) | ||||||
| @@ -146,8 +84,7 @@ func (gw *Gateway) mapChannelConfig(cfg []config.Bridge, direction string) { | |||||||
| 		ID := br.Channel + br.Account | 		ID := br.Channel + br.Account | ||||||
| 		if _, ok := gw.Channels[ID]; !ok { | 		if _, ok := gw.Channels[ID]; !ok { | ||||||
| 			channel := &config.ChannelInfo{Name: br.Channel, Direction: direction, ID: ID, Options: br.Options, Account: br.Account, | 			channel := &config.ChannelInfo{Name: br.Channel, Direction: direction, ID: ID, Options: br.Options, Account: br.Account, | ||||||
| 				GID: make(map[string]bool), SameChannel: make(map[string]bool)} | 				SameChannel: make(map[string]bool)} | ||||||
| 			channel.GID[gw.Name] = true |  | ||||||
| 			channel.SameChannel[gw.Name] = br.SameChannel | 			channel.SameChannel[gw.Name] = br.SameChannel | ||||||
| 			gw.Channels[channel.ID] = channel | 			gw.Channels[channel.ID] = channel | ||||||
| 		} else { | 		} else { | ||||||
| @@ -156,10 +93,10 @@ func (gw *Gateway) mapChannelConfig(cfg []config.Bridge, direction string) { | |||||||
| 				gw.Channels[ID].Direction = "inout" | 				gw.Channels[ID].Direction = "inout" | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		gw.Channels[ID].GID[gw.Name] = true |  | ||||||
| 		gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel | 		gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (gw *Gateway) mapChannels() error { | func (gw *Gateway) mapChannels() error { | ||||||
| 	gw.mapChannelConfig(gw.MyConfig.In, "in") | 	gw.mapChannelConfig(gw.MyConfig.In, "in") | ||||||
| 	gw.mapChannelConfig(gw.MyConfig.Out, "out") | 	gw.mapChannelConfig(gw.MyConfig.Out, "out") | ||||||
| @@ -184,8 +121,6 @@ func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []con | |||||||
| 		if _, ok := gw.Channels[getChannelID(*msg)]; !ok { | 		if _, ok := gw.Channels[getChannelID(*msg)]; !ok { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 		// add gateway to message |  | ||||||
| 		gw.validGatewayDest(msg, channel) |  | ||||||
|  |  | ||||||
| 		// do samechannelgateway logic | 		// do samechannelgateway logic | ||||||
| 		if channel.SameChannel[msg.Gateway] { | 		if channel.SameChannel[msg.Gateway] { | ||||||
| @@ -213,7 +148,8 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) { | |||||||
| 	} | 	} | ||||||
| 	originchannel := msg.Channel | 	originchannel := msg.Channel | ||||||
| 	origmsg := msg | 	origmsg := msg | ||||||
| 	for _, channel := range gw.DestChannelFunc(&msg, *dest) { | 	channels := gw.getDestChannel(&msg, *dest) | ||||||
|  | 	for _, channel := range channels { | ||||||
| 		// do not send to ourself | 		// do not send to ourself | ||||||
| 		if channel.ID == getChannelID(origmsg) { | 		if channel.ID == getChannelID(origmsg) { | ||||||
| 			continue | 			continue | ||||||
| @@ -234,6 +170,10 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (gw *Gateway) ignoreMessage(msg *config.Message) bool { | func (gw *Gateway) ignoreMessage(msg *config.Message) bool { | ||||||
|  | 	// if we don't have the bridge, ignore it | ||||||
|  | 	if _, ok := gw.Bridges[msg.Account]; !ok { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
| 	if msg.Text == "" { | 	if msg.Text == "" { | ||||||
| 		log.Debugf("ignoring empty message %#v from %s", msg, msg.Account) | 		log.Debugf("ignoring empty message %#v from %s", msg, msg.Account) | ||||||
| 		return true | 		return true | ||||||
| @@ -301,6 +241,7 @@ func (gw *Gateway) modifyAvatar(msg config.Message, dest *bridge.Bridge) string | |||||||
| func (gw *Gateway) modifyMessage(msg *config.Message) { | func (gw *Gateway) modifyMessage(msg *config.Message) { | ||||||
| 	// replace :emoji: to unicode | 	// replace :emoji: to unicode | ||||||
| 	msg.Text = emojilib.Replace(msg.Text) | 	msg.Text = emojilib.Replace(msg.Text) | ||||||
|  | 	msg.Gateway = gw.Name | ||||||
| } | } | ||||||
|  |  | ||||||
| func getChannelID(msg config.Message) string { | func getChannelID(msg config.Message) string { | ||||||
| @@ -308,35 +249,7 @@ func getChannelID(msg config.Message) string { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (gw *Gateway) validGatewayDest(msg *config.Message, channel *config.ChannelInfo) bool { | func (gw *Gateway) validGatewayDest(msg *config.Message, channel *config.ChannelInfo) bool { | ||||||
| 	GIDmap := gw.Channels[getChannelID(*msg)].GID | 	return msg.Gateway == gw.Name | ||||||
|  |  | ||||||
| 	// gateway is specified in message (probably from api) |  | ||||||
| 	if msg.Gateway != "" { |  | ||||||
| 		return channel.GID[msg.Gateway] |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// check if we are running a samechannelgateway. |  | ||||||
| 	// if it is and the channel name matches it's ok, otherwise we shouldn't use this channel. |  | ||||||
| 	for k := range GIDmap { |  | ||||||
| 		if channel.SameChannel[k] { |  | ||||||
| 			if msg.Channel == channel.Name { |  | ||||||
| 				// add the gateway to our message |  | ||||||
| 				msg.Gateway = k |  | ||||||
| 				return true |  | ||||||
| 			} else { |  | ||||||
| 				return false |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	// check if we are in the correct gateway |  | ||||||
| 	for k := range GIDmap { |  | ||||||
| 		if channel.GID[k] { |  | ||||||
| 			// add the gateway to our message |  | ||||||
| 			msg.Gateway = k |  | ||||||
| 			return true |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return false |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func isApi(account string) bool { | func isApi(account string) bool { | ||||||
|   | |||||||
							
								
								
									
										288
									
								
								gateway/gateway_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										288
									
								
								gateway/gateway_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,288 @@ | |||||||
|  | package gateway | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"github.com/42wim/matterbridge/bridge/config" | ||||||
|  | 	"github.com/BurntSushi/toml" | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | 	"strconv" | ||||||
|  |  | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var testconfig = ` | ||||||
|  | [irc.freenode] | ||||||
|  | [mattermost.test] | ||||||
|  | [gitter.42wim] | ||||||
|  | [discord.test] | ||||||
|  | [slack.test] | ||||||
|  |  | ||||||
|  | [[gateway]] | ||||||
|  |     name = "bridge1" | ||||||
|  |     enable=true | ||||||
|  |      | ||||||
|  |     [[gateway.inout]] | ||||||
|  |     account = "irc.freenode" | ||||||
|  |     channel = "#wimtesting" | ||||||
|  |      | ||||||
|  |     [[gateway.inout]] | ||||||
|  |     account="gitter.42wim" | ||||||
|  |     channel="42wim/testroom" | ||||||
|  |     #channel="matterbridge/Lobby" | ||||||
|  |  | ||||||
|  |     [[gateway.inout]] | ||||||
|  |     account = "discord.test" | ||||||
|  |     channel = "general" | ||||||
|  |      | ||||||
|  |     [[gateway.inout]] | ||||||
|  |     account="slack.test" | ||||||
|  |     channel="testing" | ||||||
|  | 	` | ||||||
|  |  | ||||||
|  | var testconfig2 = ` | ||||||
|  | [irc.freenode] | ||||||
|  | [mattermost.test] | ||||||
|  | [gitter.42wim] | ||||||
|  | [discord.test] | ||||||
|  | [slack.test] | ||||||
|  |  | ||||||
|  | [[gateway]] | ||||||
|  |     name = "bridge1" | ||||||
|  |     enable=true | ||||||
|  |      | ||||||
|  |     [[gateway.in]] | ||||||
|  |     account = "irc.freenode" | ||||||
|  |     channel = "#wimtesting" | ||||||
|  |      | ||||||
|  |     [[gateway.in]] | ||||||
|  |     account="gitter.42wim" | ||||||
|  |     channel="42wim/testroom" | ||||||
|  |  | ||||||
|  |     [[gateway.inout]] | ||||||
|  |     account = "discord.test" | ||||||
|  |     channel = "general" | ||||||
|  |      | ||||||
|  |     [[gateway.out]] | ||||||
|  |     account="slack.test" | ||||||
|  |     channel="testing" | ||||||
|  | [[gateway]] | ||||||
|  |     name = "bridge2" | ||||||
|  |     enable=true | ||||||
|  |      | ||||||
|  |     [[gateway.in]] | ||||||
|  |     account = "irc.freenode" | ||||||
|  |     channel = "#wimtesting2" | ||||||
|  |      | ||||||
|  |     [[gateway.out]] | ||||||
|  |     account="gitter.42wim" | ||||||
|  |     channel="42wim/testroom" | ||||||
|  |  | ||||||
|  |     [[gateway.out]] | ||||||
|  |     account = "discord.test" | ||||||
|  |     channel = "general2" | ||||||
|  | 	` | ||||||
|  | var testconfig3 = ` | ||||||
|  | [irc.zzz] | ||||||
|  | [telegram.zzz] | ||||||
|  | [slack.zzz] | ||||||
|  | [[gateway]] | ||||||
|  | name="bridge" | ||||||
|  | enable=true | ||||||
|  |  | ||||||
|  |     [[gateway.inout]] | ||||||
|  |     account="irc.zzz" | ||||||
|  |     channel="#main"		 | ||||||
|  |  | ||||||
|  |     [[gateway.inout]] | ||||||
|  |     account="telegram.zzz" | ||||||
|  |     channel="-1111111111111" | ||||||
|  |  | ||||||
|  |     [[gateway.inout]] | ||||||
|  |     account="slack.zzz" | ||||||
|  |     channel="irc"	 | ||||||
|  | 	 | ||||||
|  | [[gateway]] | ||||||
|  | name="announcements" | ||||||
|  | enable=true | ||||||
|  | 	 | ||||||
|  |     [[gateway.in]] | ||||||
|  |     account="telegram.zzz" | ||||||
|  |     channel="-2222222222222"	 | ||||||
|  | 	 | ||||||
|  |     [[gateway.out]] | ||||||
|  |     account="irc.zzz" | ||||||
|  |     channel="#main"		 | ||||||
|  | 	 | ||||||
|  |     [[gateway.out]] | ||||||
|  |     account="irc.zzz" | ||||||
|  |     channel="#main-help"	 | ||||||
|  |  | ||||||
|  |     [[gateway.out]] | ||||||
|  |     account="telegram.zzz" | ||||||
|  |     channel="--333333333333"	 | ||||||
|  |  | ||||||
|  |     [[gateway.out]] | ||||||
|  |     account="slack.zzz" | ||||||
|  |     channel="general"		 | ||||||
|  | 	 | ||||||
|  | [[gateway]] | ||||||
|  | name="bridge2" | ||||||
|  | enable=true | ||||||
|  |  | ||||||
|  |     [[gateway.inout]] | ||||||
|  |     account="irc.zzz" | ||||||
|  |     channel="#main-help"	 | ||||||
|  |  | ||||||
|  |     [[gateway.inout]] | ||||||
|  |     account="telegram.zzz" | ||||||
|  |     channel="--444444444444"	 | ||||||
|  |  | ||||||
|  | 	 | ||||||
|  | [[gateway]] | ||||||
|  | name="bridge3" | ||||||
|  | enable=true | ||||||
|  |  | ||||||
|  |     [[gateway.inout]] | ||||||
|  |     account="irc.zzz" | ||||||
|  |     channel="#main-telegram"	 | ||||||
|  |  | ||||||
|  |     [[gateway.inout]] | ||||||
|  |     account="telegram.zzz" | ||||||
|  |     channel="--333333333333" | ||||||
|  | ` | ||||||
|  |  | ||||||
|  | func maketestRouter(input string) *Router { | ||||||
|  | 	var cfg *config.Config | ||||||
|  | 	if _, err := toml.Decode(input, &cfg); err != nil { | ||||||
|  | 		fmt.Println(err) | ||||||
|  | 	} | ||||||
|  | 	r, err := NewRouter(cfg) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Println(err) | ||||||
|  | 	} | ||||||
|  | 	return r | ||||||
|  | } | ||||||
|  | func TestNewRouter(t *testing.T) { | ||||||
|  | 	var cfg *config.Config | ||||||
|  | 	if _, err := toml.Decode(testconfig, &cfg); err != nil { | ||||||
|  | 		fmt.Println(err) | ||||||
|  | 	} | ||||||
|  | 	r, err := NewRouter(cfg) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Println(err) | ||||||
|  | 	} | ||||||
|  | 	assert.Equal(t, 1, len(r.Gateways)) | ||||||
|  | 	assert.Equal(t, 4, len(r.Gateways["bridge1"].Bridges)) | ||||||
|  | 	assert.Equal(t, 4, len(r.Gateways["bridge1"].Channels)) | ||||||
|  |  | ||||||
|  | 	r = maketestRouter(testconfig2) | ||||||
|  | 	assert.Equal(t, 2, len(r.Gateways)) | ||||||
|  | 	assert.Equal(t, 4, len(r.Gateways["bridge1"].Bridges)) | ||||||
|  | 	assert.Equal(t, 3, len(r.Gateways["bridge2"].Bridges)) | ||||||
|  | 	assert.Equal(t, 4, len(r.Gateways["bridge1"].Channels)) | ||||||
|  | 	assert.Equal(t, 3, len(r.Gateways["bridge2"].Channels)) | ||||||
|  | 	assert.Equal(t, &config.ChannelInfo{Name: "42wim/testroom", Direction: "out", | ||||||
|  | 		ID: "42wim/testroomgitter.42wim", Account: "gitter.42wim", | ||||||
|  | 		SameChannel: map[string]bool{"bridge2": false}}, | ||||||
|  | 		r.Gateways["bridge2"].Channels["42wim/testroomgitter.42wim"]) | ||||||
|  | 	assert.Equal(t, &config.ChannelInfo{Name: "42wim/testroom", Direction: "in", | ||||||
|  | 		ID: "42wim/testroomgitter.42wim", Account: "gitter.42wim", | ||||||
|  | 		SameChannel: map[string]bool{"bridge1": false}}, | ||||||
|  | 		r.Gateways["bridge1"].Channels["42wim/testroomgitter.42wim"]) | ||||||
|  | 	assert.Equal(t, &config.ChannelInfo{Name: "general", Direction: "inout", | ||||||
|  | 		ID: "generaldiscord.test", Account: "discord.test", | ||||||
|  | 		SameChannel: map[string]bool{"bridge1": false}}, | ||||||
|  | 		r.Gateways["bridge1"].Channels["generaldiscord.test"]) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGetDestChannel(t *testing.T) { | ||||||
|  | 	r := maketestRouter(testconfig2) | ||||||
|  | 	msg := &config.Message{Text: "test", Channel: "general", Account: "discord.test", Gateway: "bridge1", Protocol: "discord", Username: "test"} | ||||||
|  | 	for _, br := range r.Gateways["bridge1"].Bridges { | ||||||
|  | 		switch br.Account { | ||||||
|  | 		case "discord.test": | ||||||
|  | 			assert.Equal(t, []config.ChannelInfo{{Name: "general", Account: "discord.test", Direction: "inout", ID: "generaldiscord.test", SameChannel: map[string]bool{"bridge1": false}, Options: config.ChannelOptions{Key: ""}}}, | ||||||
|  | 				r.Gateways["bridge1"].getDestChannel(msg, *br)) | ||||||
|  | 		case "slack.test": | ||||||
|  | 			assert.Equal(t, []config.ChannelInfo{{Name: "testing", Account: "slack.test", Direction: "out", ID: "testingslack.test", SameChannel: map[string]bool{"bridge1": false}, Options: config.ChannelOptions{Key: ""}}}, | ||||||
|  | 				r.Gateways["bridge1"].getDestChannel(msg, *br)) | ||||||
|  | 		case "gitter.42wim": | ||||||
|  | 			assert.Equal(t, []config.ChannelInfo(nil), r.Gateways["bridge1"].getDestChannel(msg, *br)) | ||||||
|  | 		case "irc.freenode": | ||||||
|  | 			assert.Equal(t, []config.ChannelInfo(nil), r.Gateways["bridge1"].getDestChannel(msg, *br)) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGetDestChannelAdvanced(t *testing.T) { | ||||||
|  | 	r := maketestRouter(testconfig3) | ||||||
|  | 	var msgs []*config.Message | ||||||
|  | 	i := 0 | ||||||
|  | 	for _, gw := range r.Gateways { | ||||||
|  | 		for _, channel := range gw.Channels { | ||||||
|  | 			msgs = append(msgs, &config.Message{Text: "text" + strconv.Itoa(i), Channel: channel.Name, Account: channel.Account, Gateway: gw.Name, Username: "user" + strconv.Itoa(i)}) | ||||||
|  | 			i++ | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	hits := make(map[string]int) | ||||||
|  | 	for _, gw := range r.Gateways { | ||||||
|  | 		for _, br := range gw.Bridges { | ||||||
|  | 			for _, msg := range msgs { | ||||||
|  | 				channels := gw.getDestChannel(msg, *br) | ||||||
|  | 				if gw.Name != msg.Gateway { | ||||||
|  | 					assert.Equal(t, []config.ChannelInfo(nil), channels) | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  | 				switch gw.Name { | ||||||
|  | 				case "bridge": | ||||||
|  | 					if (msg.Channel == "#main" || msg.Channel == "-1111111111111" || msg.Channel == "irc") && (msg.Account == "irc.zzz" || msg.Account == "telegram.zzz" || msg.Account == "slack.zzz") { | ||||||
|  | 						hits[gw.Name]++ | ||||||
|  | 						switch br.Account { | ||||||
|  | 						case "irc.zzz": | ||||||
|  | 							assert.Equal(t, []config.ChannelInfo{{Name: "#main", Account: "irc.zzz", Direction: "inout", ID: "#mainirc.zzz", SameChannel: map[string]bool{"bridge": false}, Options: config.ChannelOptions{Key: ""}}}, channels) | ||||||
|  | 						case "telegram.zzz": | ||||||
|  | 							assert.Equal(t, []config.ChannelInfo{{Name: "-1111111111111", Account: "telegram.zzz", Direction: "inout", ID: "-1111111111111telegram.zzz", SameChannel: map[string]bool{"bridge": false}, Options: config.ChannelOptions{Key: ""}}}, channels) | ||||||
|  | 						case "slack.zzz": | ||||||
|  | 							assert.Equal(t, []config.ChannelInfo{{Name: "irc", Account: "slack.zzz", Direction: "inout", ID: "ircslack.zzz", SameChannel: map[string]bool{"bridge": false}, Options: config.ChannelOptions{Key: ""}}}, channels) | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				case "bridge2": | ||||||
|  | 					if (msg.Channel == "#main-help" || msg.Channel == "--444444444444") && (msg.Account == "irc.zzz" || msg.Account == "telegram.zzz") { | ||||||
|  | 						hits[gw.Name]++ | ||||||
|  | 						switch br.Account { | ||||||
|  | 						case "irc.zzz": | ||||||
|  | 							assert.Equal(t, []config.ChannelInfo{{Name: "#main-help", Account: "irc.zzz", Direction: "inout", ID: "#main-helpirc.zzz", SameChannel: map[string]bool{"bridge2": false}, Options: config.ChannelOptions{Key: ""}}}, channels) | ||||||
|  | 						case "telegram.zzz": | ||||||
|  | 							assert.Equal(t, []config.ChannelInfo{{Name: "--444444444444", Account: "telegram.zzz", Direction: "inout", ID: "--444444444444telegram.zzz", SameChannel: map[string]bool{"bridge2": false}, Options: config.ChannelOptions{Key: ""}}}, channels) | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				case "bridge3": | ||||||
|  | 					if (msg.Channel == "#main-telegram" || msg.Channel == "--333333333333") && (msg.Account == "irc.zzz" || msg.Account == "telegram.zzz") { | ||||||
|  | 						hits[gw.Name]++ | ||||||
|  | 						switch br.Account { | ||||||
|  | 						case "irc.zzz": | ||||||
|  | 							assert.Equal(t, []config.ChannelInfo{{Name: "#main-telegram", Account: "irc.zzz", Direction: "inout", ID: "#main-telegramirc.zzz", SameChannel: map[string]bool{"bridge3": false}, Options: config.ChannelOptions{Key: ""}}}, channels) | ||||||
|  | 						case "telegram.zzz": | ||||||
|  | 							assert.Equal(t, []config.ChannelInfo{{Name: "--333333333333", Account: "telegram.zzz", Direction: "inout", ID: "--333333333333telegram.zzz", SameChannel: map[string]bool{"bridge3": false}, Options: config.ChannelOptions{Key: ""}}}, channels) | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				case "announcements": | ||||||
|  | 					if msg.Channel != "-2222222222222" && msg.Account != "telegram" { | ||||||
|  | 						assert.Equal(t, []config.ChannelInfo(nil), channels) | ||||||
|  | 						continue | ||||||
|  | 					} | ||||||
|  | 					hits[gw.Name]++ | ||||||
|  | 					switch br.Account { | ||||||
|  | 					case "irc.zzz": | ||||||
|  | 						assert.Equal(t, []config.ChannelInfo{{Name: "#main", Account: "irc.zzz", Direction: "out", ID: "#mainirc.zzz", SameChannel: map[string]bool{"announcements": false}, Options: config.ChannelOptions{Key: ""}}, {Name: "#main-help", Account: "irc.zzz", Direction: "out", ID: "#main-helpirc.zzz", SameChannel: map[string]bool{"announcements": false}, Options: config.ChannelOptions{Key: ""}}}, channels) | ||||||
|  | 					case "slack.zzz": | ||||||
|  | 						assert.Equal(t, []config.ChannelInfo{{Name: "general", Account: "slack.zzz", Direction: "out", ID: "generalslack.zzz", SameChannel: map[string]bool{"announcements": false}, Options: config.ChannelOptions{Key: ""}}}, channels) | ||||||
|  | 					case "telegram.zzz": | ||||||
|  | 						assert.Equal(t, []config.ChannelInfo{{Name: "--333333333333", Account: "telegram.zzz", Direction: "out", ID: "--333333333333telegram.zzz", SameChannel: map[string]bool{"announcements": false}, Options: config.ChannelOptions{Key: ""}}}, channels) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	assert.Equal(t, map[string]int{"bridge3": 4, "bridge": 9, "announcements": 3, "bridge2": 4}, hits) | ||||||
|  | } | ||||||
							
								
								
									
										106
									
								
								gateway/router.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								gateway/router.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | |||||||
|  | package gateway | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"github.com/42wim/matterbridge/bridge" | ||||||
|  | 	"github.com/42wim/matterbridge/bridge/config" | ||||||
|  | 	"github.com/42wim/matterbridge/gateway/samechannel" | ||||||
|  | 	log "github.com/Sirupsen/logrus" | ||||||
|  | 	//	"github.com/davecgh/go-spew/spew" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type Router struct { | ||||||
|  | 	Gateways map[string]*Gateway | ||||||
|  | 	Message  chan config.Message | ||||||
|  | 	*config.Config | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewRouter(cfg *config.Config) (*Router, error) { | ||||||
|  | 	r := &Router{} | ||||||
|  | 	r.Config = cfg | ||||||
|  | 	r.Message = make(chan config.Message) | ||||||
|  | 	r.Gateways = make(map[string]*Gateway) | ||||||
|  | 	sgw := samechannelgateway.New(cfg) | ||||||
|  | 	gwconfigs := sgw.GetConfig() | ||||||
|  |  | ||||||
|  | 	for _, entry := range append(gwconfigs, cfg.Gateway...) { | ||||||
|  | 		if !entry.Enable { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if entry.Name == "" { | ||||||
|  | 			return nil, fmt.Errorf("%s", "Gateway without name found") | ||||||
|  | 		} | ||||||
|  | 		if _, ok := r.Gateways[entry.Name]; ok { | ||||||
|  | 			return nil, fmt.Errorf("Gateway with name %s already exists", entry.Name) | ||||||
|  | 		} | ||||||
|  | 		r.Gateways[entry.Name] = New(entry, r) | ||||||
|  | 	} | ||||||
|  | 	return r, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *Router) Start() error { | ||||||
|  | 	m := make(map[string]*bridge.Bridge) | ||||||
|  | 	for _, gw := range r.Gateways { | ||||||
|  | 		for _, br := range gw.Bridges { | ||||||
|  | 			m[br.Account] = br | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	for _, br := range m { | ||||||
|  | 		log.Infof("Starting bridge: %s ", br.Account) | ||||||
|  | 		err := br.Connect() | ||||||
|  | 		if err != nil { | ||||||
|  | 			return fmt.Errorf("Bridge %s failed to start: %v", br.Account, err) | ||||||
|  | 		} | ||||||
|  | 		err = br.JoinChannels() | ||||||
|  | 		if err != nil { | ||||||
|  | 			return fmt.Errorf("Bridge %s failed to join channel: %v", br.Account, err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	go r.handleReceive() | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *Router) getBridge(account string) *bridge.Bridge { | ||||||
|  | 	for _, gw := range r.Gateways { | ||||||
|  | 		if br, ok := gw.Bridges[account]; ok { | ||||||
|  | 			return br | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *Router) handleReceive() { | ||||||
|  | 	for msg := range r.Message { | ||||||
|  | 		if msg.Event == config.EVENT_FAILURE { | ||||||
|  | 		Loop: | ||||||
|  | 			for _, gw := range r.Gateways { | ||||||
|  | 				for _, br := range gw.Bridges { | ||||||
|  | 					if msg.Account == br.Account { | ||||||
|  | 						go gw.reconnectBridge(br) | ||||||
|  | 						break Loop | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if msg.Event == config.EVENT_REJOIN_CHANNELS { | ||||||
|  | 			for _, gw := range r.Gateways { | ||||||
|  | 				for _, br := range gw.Bridges { | ||||||
|  | 					if msg.Account == br.Account { | ||||||
|  | 						br.Joined = make(map[string]bool) | ||||||
|  | 						br.JoinChannels() | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		for _, gw := range r.Gateways { | ||||||
|  | 			if !gw.ignoreMessage(&msg) { | ||||||
|  | 				msg.Timestamp = time.Now() | ||||||
|  | 				gw.modifyMessage(&msg) | ||||||
|  | 				for _, br := range gw.Bridges { | ||||||
|  | 					gw.handleMessage(msg, br) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										31
									
								
								gateway/samechannel/samechannel_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								gateway/samechannel/samechannel_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | package samechannelgateway | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"github.com/42wim/matterbridge/bridge/config" | ||||||
|  | 	"github.com/BurntSushi/toml" | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  |  | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var testconfig = ` | ||||||
|  | [mattermost.test] | ||||||
|  | [slack.test] | ||||||
|  |  | ||||||
|  | [[samechannelgateway]] | ||||||
|  |    enable = true | ||||||
|  |    name = "blah" | ||||||
|  |       accounts = [ "mattermost.test","slack.test" ] | ||||||
|  |       channels = [ "testing","testing2","testing10"] | ||||||
|  | ` | ||||||
|  |  | ||||||
|  | func TestGetConfig(t *testing.T) { | ||||||
|  | 	var cfg *config.Config | ||||||
|  | 	if _, err := toml.Decode(testconfig, &cfg); err != nil { | ||||||
|  | 		fmt.Println(err) | ||||||
|  | 	} | ||||||
|  | 	sgw := New(cfg) | ||||||
|  | 	configs := sgw.GetConfig() | ||||||
|  | 	assert.Equal(t, []config.Gateway{{Name: "blah", Enable: true, In: []config.Bridge(nil), Out: []config.Bridge(nil), InOut: []config.Bridge{{Account: "mattermost.test", Channel: "testing", Options: config.ChannelOptions{Key: ""}, SameChannel: true}, {Account: "mattermost.test", Channel: "testing2", Options: config.ChannelOptions{Key: ""}, SameChannel: true}, {Account: "mattermost.test", Channel: "testing10", Options: config.ChannelOptions{Key: ""}, SameChannel: true}, {Account: "slack.test", Channel: "testing", Options: config.ChannelOptions{Key: ""}, SameChannel: true}, {Account: "slack.test", Channel: "testing2", Options: config.ChannelOptions{Key: ""}, SameChannel: true}, {Account: "slack.test", Channel: "testing10", Options: config.ChannelOptions{Key: ""}, SameChannel: true}}}}, configs) | ||||||
|  | } | ||||||
| @@ -5,14 +5,13 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"github.com/42wim/matterbridge/bridge/config" | 	"github.com/42wim/matterbridge/bridge/config" | ||||||
| 	"github.com/42wim/matterbridge/gateway" | 	"github.com/42wim/matterbridge/gateway" | ||||||
| 	"github.com/42wim/matterbridge/gateway/samechannel" |  | ||||||
| 	log "github.com/Sirupsen/logrus" | 	log "github.com/Sirupsen/logrus" | ||||||
| 	"github.com/google/gops/agent" | 	"github.com/google/gops/agent" | ||||||
| 	"strings" | 	"strings" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	version = "0.16.3" | 	version = "1.0.0" | ||||||
| 	githash string | 	githash string | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -43,20 +42,11 @@ func main() { | |||||||
| 		log.Println("WARNING: THIS IS A DEVELOPMENT VERSION. Things may break.") | 		log.Println("WARNING: THIS IS A DEVELOPMENT VERSION. Things may break.") | ||||||
| 	} | 	} | ||||||
| 	cfg := config.NewConfig(*flagConfig) | 	cfg := config.NewConfig(*flagConfig) | ||||||
|  | 	r, err := gateway.NewRouter(cfg) | ||||||
| 	g := gateway.New(cfg) | 	if err != nil { | ||||||
| 	sgw := samechannelgateway.New(cfg) | 		log.Fatalf("Starting gateway failed: %s", err) | ||||||
| 	gwconfigs := sgw.GetConfig() |  | ||||||
| 	for _, gw := range append(gwconfigs, cfg.Gateway...) { |  | ||||||
| 		if !gw.Enable { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		err := g.AddConfig(&gw) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatalf("Starting gateway failed: %s", err) |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 	err := g.Start() | 	err = r.Start() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatalf("Starting gateway failed: %s", err) | 		log.Fatalf("Starting gateway failed: %s", err) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -443,6 +443,10 @@ Server="yourservername" | |||||||
| #OPTIONAL (default false) | #OPTIONAL (default false) | ||||||
| ShowEmbeds=false | ShowEmbeds=false | ||||||
|  |  | ||||||
|  | #Shows the username (minus the discriminator) instead of the server nickname | ||||||
|  | #OPTIONAL (default false) | ||||||
|  | UseUserName=false | ||||||
|  |  | ||||||
| #Specify WebhookURL. If given, will relay messages using the Webhook, which gives a better look to messages. | #Specify WebhookURL. If given, will relay messages using the Webhook, which gives a better look to messages. | ||||||
| #OPTIONAL (default empty) | #OPTIONAL (default empty) | ||||||
| WebhookURL="Yourwebhooktokenhere" | WebhookURL="Yourwebhooktokenhere" | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| package matterclient | package matterclient | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"crypto/md5" | ||||||
| 	"crypto/tls" | 	"crypto/tls" | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"errors" | 	"errors" | ||||||
| @@ -16,6 +17,7 @@ import ( | |||||||
| 	log "github.com/Sirupsen/logrus" | 	log "github.com/Sirupsen/logrus" | ||||||
|  |  | ||||||
| 	"github.com/gorilla/websocket" | 	"github.com/gorilla/websocket" | ||||||
|  | 	"github.com/hashicorp/golang-lru" | ||||||
| 	"github.com/jpillora/backoff" | 	"github.com/jpillora/backoff" | ||||||
| 	"github.com/mattermost/platform/model" | 	"github.com/mattermost/platform/model" | ||||||
| ) | ) | ||||||
| @@ -66,6 +68,7 @@ type MMClient struct { | |||||||
| 	WsPingChan    chan *model.WebSocketResponse | 	WsPingChan    chan *model.WebSocketResponse | ||||||
| 	ServerVersion string | 	ServerVersion string | ||||||
| 	OnWsConnect   func() | 	OnWsConnect   func() | ||||||
|  | 	lruCache      *lru.Cache | ||||||
| } | } | ||||||
|  |  | ||||||
| func New(login, pass, team, server string) *MMClient { | func New(login, pass, team, server string) *MMClient { | ||||||
| @@ -73,6 +76,7 @@ func New(login, pass, team, server string) *MMClient { | |||||||
| 	mmclient := &MMClient{Credentials: cred, MessageChan: make(chan *Message, 100), Users: make(map[string]*model.User)} | 	mmclient := &MMClient{Credentials: cred, MessageChan: make(chan *Message, 100), Users: make(map[string]*model.User)} | ||||||
| 	mmclient.log = log.WithFields(log.Fields{"module": "matterclient"}) | 	mmclient.log = log.WithFields(log.Fields{"module": "matterclient"}) | ||||||
| 	log.SetFormatter(&log.TextFormatter{FullTimestamp: true}) | 	log.SetFormatter(&log.TextFormatter{FullTimestamp: true}) | ||||||
|  | 	mmclient.lruCache, _ = lru.New(500) | ||||||
| 	return mmclient | 	return mmclient | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -270,7 +274,10 @@ func (m *MMClient) WsReceiver() { | |||||||
| 			m.log.Debugf("WsReceiver event: %#v", event) | 			m.log.Debugf("WsReceiver event: %#v", event) | ||||||
| 			msg := &Message{Raw: &event, Team: m.Credentials.Team} | 			msg := &Message{Raw: &event, Team: m.Credentials.Team} | ||||||
| 			m.parseMessage(msg) | 			m.parseMessage(msg) | ||||||
| 			m.MessageChan <- msg | 			// check if we didn't empty the message | ||||||
|  | 			if msg.Text != "" { | ||||||
|  | 				m.MessageChan <- msg | ||||||
|  | 			} | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -306,6 +313,13 @@ func (m *MMClient) parseResponse(rmsg model.WebSocketResponse) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (m *MMClient) parseActionPost(rmsg *Message) { | func (m *MMClient) parseActionPost(rmsg *Message) { | ||||||
|  | 	// add post to cache, if it already exists don't relay this again. | ||||||
|  | 	// this should fix reposts | ||||||
|  | 	if ok, _ := m.lruCache.ContainsOrAdd(digestString(rmsg.Raw.Data["post"].(string)), true); ok { | ||||||
|  | 		m.log.Debugf("message %#v in cache, not processing again", rmsg.Raw.Data["post"].(string)) | ||||||
|  | 		rmsg.Text = "" | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
| 	data := model.PostFromJson(strings.NewReader(rmsg.Raw.Data["post"].(string))) | 	data := model.PostFromJson(strings.NewReader(rmsg.Raw.Data["post"].(string))) | ||||||
| 	// we don't have the user, refresh the userlist | 	// we don't have the user, refresh the userlist | ||||||
| 	if m.GetUser(data.UserId) == nil { | 	if m.GetUser(data.UserId) == nil { | ||||||
| @@ -860,3 +874,7 @@ func supportedVersion(version string) bool { | |||||||
| 	} | 	} | ||||||
| 	return false | 	return false | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func digestString(s string) string { | ||||||
|  | 	return fmt.Sprintf("%x", md5.Sum([]byte(s))) | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										111
									
								
								vendor/github.com/thoj/go-ircevent/irc.go → vendor/github.com/42wim/go-ircevent/irc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										111
									
								
								vendor/github.com/thoj/go-ircevent/irc.go → vendor/github.com/42wim/go-ircevent/irc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -87,6 +87,17 @@ func (irc *Connection) readLoop() { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Unescape tag values as defined in the IRCv3.2 message tags spec | ||||||
|  | // http://ircv3.net/specs/core/message-tags-3.2.html | ||||||
|  | func unescapeTagValue(value string) string { | ||||||
|  | 	value = strings.Replace(value, "\\:", ";", -1) | ||||||
|  | 	value = strings.Replace(value, "\\s", " ", -1) | ||||||
|  | 	value = strings.Replace(value, "\\\\", "\\", -1) | ||||||
|  | 	value = strings.Replace(value, "\\r", "\r", -1) | ||||||
|  | 	value = strings.Replace(value, "\\n", "\n", -1) | ||||||
|  | 	return value | ||||||
|  | } | ||||||
|  | 
 | ||||||
| //Parse raw irc messages | //Parse raw irc messages | ||||||
| func parseToEvent(msg string) (*Event, error) { | func parseToEvent(msg string) (*Event, error) { | ||||||
| 	msg = strings.TrimSuffix(msg, "\n") //Remove \r\n | 	msg = strings.TrimSuffix(msg, "\n") //Remove \r\n | ||||||
| @@ -95,6 +106,26 @@ func parseToEvent(msg string) (*Event, error) { | |||||||
| 	if len(msg) < 5 { | 	if len(msg) < 5 { | ||||||
| 		return nil, errors.New("Malformed msg from server") | 		return nil, errors.New("Malformed msg from server") | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	if msg[0] == '@' { | ||||||
|  | 		// IRCv3 Message Tags | ||||||
|  | 		if i := strings.Index(msg, " "); i > -1 { | ||||||
|  | 			event.Tags = make(map[string]string) | ||||||
|  | 			tags := strings.Split(msg[1:i], ";") | ||||||
|  | 			for _, data := range tags { | ||||||
|  | 				parts := strings.SplitN(data, "=", 2) | ||||||
|  | 				if len(parts) == 1 { | ||||||
|  | 					event.Tags[parts[0]] = "" | ||||||
|  | 				} else { | ||||||
|  | 					event.Tags[parts[0]] = unescapeTagValue(parts[1]) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			msg = msg[i+1 : len(msg)] | ||||||
|  | 		} else { | ||||||
|  | 			return nil, errors.New("Malformed msg from server") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if msg[0] == ':' { | 	if msg[0] == ':' { | ||||||
| 		if i := strings.Index(msg, " "); i > -1 { | 		if i := strings.Index(msg, " "); i > -1 { | ||||||
| 			event.Source = msg[1:i] | 			event.Source = msg[1:i] | ||||||
| @@ -430,26 +461,84 @@ func (irc *Connection) Connect(server string) error { | |||||||
| 		irc.pwrite <- fmt.Sprintf("PASS %s\r\n", irc.Password) | 		irc.pwrite <- fmt.Sprintf("PASS %s\r\n", irc.Password) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	resChan := make(chan *SASLResult) | 	err = irc.negotiateCaps() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	irc.pwrite <- fmt.Sprintf("NICK %s\r\n", irc.nick) | ||||||
|  | 	irc.pwrite <- fmt.Sprintf("USER %s 0.0.0.0 0.0.0.0 :%s\r\n", irc.user, irc.user) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Negotiate IRCv3 capabilities | ||||||
|  | func (irc *Connection) negotiateCaps() error { | ||||||
|  | 	saslResChan := make(chan *SASLResult) | ||||||
|  | 	if irc.UseSASL { | ||||||
|  | 		irc.RequestCaps = append(irc.RequestCaps, "sasl") | ||||||
|  | 		irc.setupSASLCallbacks(saslResChan) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(irc.RequestCaps) == 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	cap_chan := make(chan bool, len(irc.RequestCaps)) | ||||||
|  | 	irc.AddCallback("CAP", func(e *Event) { | ||||||
|  | 		if len(e.Arguments) != 3 { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		command := e.Arguments[1] | ||||||
|  | 
 | ||||||
|  | 		if command == "LS" { | ||||||
|  | 			missing_caps := len(irc.RequestCaps) | ||||||
|  | 			for _, cap_name := range strings.Split(e.Arguments[2], " ") { | ||||||
|  | 				for _, req_cap := range irc.RequestCaps { | ||||||
|  | 					if cap_name == req_cap { | ||||||
|  | 						irc.pwrite <- fmt.Sprintf("CAP REQ :%s\r\n", cap_name) | ||||||
|  | 						missing_caps-- | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			for i := 0; i < missing_caps; i++ { | ||||||
|  | 				cap_chan <- true | ||||||
|  | 			} | ||||||
|  | 		} else if command == "ACK" || command == "NAK" { | ||||||
|  | 			for _, cap_name := range strings.Split(strings.TrimSpace(e.Arguments[2]), " ") { | ||||||
|  | 				if cap_name == "" { | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				if command == "ACK" { | ||||||
|  | 					irc.AcknowledgedCaps = append(irc.AcknowledgedCaps, cap_name) | ||||||
|  | 				} | ||||||
|  | 				cap_chan <- true | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	irc.pwrite <- "CAP LS\r\n" | ||||||
|  | 
 | ||||||
| 	if irc.UseSASL { | 	if irc.UseSASL { | ||||||
| 		irc.setupSASLCallbacks(resChan) |  | ||||||
| 		irc.pwrite <- fmt.Sprintf("CAP LS\r\n") |  | ||||||
| 		// request SASL |  | ||||||
| 		irc.pwrite <- fmt.Sprintf("CAP REQ :sasl\r\n") |  | ||||||
| 		// if sasl request doesn't complete in 15 seconds, close chan and timeout |  | ||||||
| 		select { | 		select { | ||||||
| 		case res := <-resChan: | 		case res := <-saslResChan: | ||||||
| 			if res.Failed { | 			if res.Failed { | ||||||
| 				close(resChan) | 				close(saslResChan) | ||||||
| 				return res.Err | 				return res.Err | ||||||
| 			} | 			} | ||||||
| 		case <-time.After(time.Second * 15): | 		case <-time.After(time.Second * 15): | ||||||
| 			close(resChan) | 			close(saslResChan) | ||||||
| 			return errors.New("SASL setup timed out. This shouldn't happen.") | 			return errors.New("SASL setup timed out. This shouldn't happen.") | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	irc.pwrite <- fmt.Sprintf("NICK %s\r\n", irc.nick) | 
 | ||||||
| 	irc.pwrite <- fmt.Sprintf("USER %s 0.0.0.0 0.0.0.0 :%s\r\n", irc.user, irc.user) | 	// Wait for all capabilities to be ACKed or NAKed before ending negotiation | ||||||
|  | 	for i := 0; i < len(irc.RequestCaps); i++ { | ||||||
|  | 		<-cap_chan | ||||||
|  | 	} | ||||||
|  | 	irc.pwrite <- fmt.Sprintf("CAP END\r\n") | ||||||
|  | 
 | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -43,7 +43,6 @@ func (irc *Connection) setupSASLCallbacks(result chan<- *SASLResult) { | |||||||
| 		result <- &SASLResult{true, errors.New(e.Arguments[1])} | 		result <- &SASLResult{true, errors.New(e.Arguments[1])} | ||||||
| 	}) | 	}) | ||||||
| 	irc.AddCallback("903", func(e *Event) { | 	irc.AddCallback("903", func(e *Event) { | ||||||
| 		irc.SendRaw("CAP END") |  | ||||||
| 		result <- &SASLResult{false, nil} | 		result <- &SASLResult{false, nil} | ||||||
| 	}) | 	}) | ||||||
| 	irc.AddCallback("904", func(e *Event) { | 	irc.AddCallback("904", func(e *Event) { | ||||||
| @@ -15,20 +15,22 @@ import ( | |||||||
| type Connection struct { | type Connection struct { | ||||||
| 	sync.Mutex | 	sync.Mutex | ||||||
| 	sync.WaitGroup | 	sync.WaitGroup | ||||||
| 	Debug        bool | 	Debug            bool | ||||||
| 	Error        chan error | 	Error            chan error | ||||||
| 	Password     string | 	Password         string | ||||||
| 	UseTLS       bool | 	UseTLS           bool | ||||||
| 	UseSASL      bool | 	UseSASL          bool | ||||||
| 	SASLLogin    string | 	RequestCaps      []string | ||||||
| 	SASLPassword string | 	AcknowledgedCaps []string | ||||||
| 	SASLMech     string | 	SASLLogin        string | ||||||
| 	TLSConfig    *tls.Config | 	SASLPassword     string | ||||||
| 	Version      string | 	SASLMech         string | ||||||
| 	Timeout      time.Duration | 	TLSConfig        *tls.Config | ||||||
| 	PingFreq     time.Duration | 	Version          string | ||||||
| 	KeepAlive    time.Duration | 	Timeout          time.Duration | ||||||
| 	Server       string | 	PingFreq         time.Duration | ||||||
|  | 	KeepAlive        time.Duration | ||||||
|  | 	Server           string | ||||||
| 
 | 
 | ||||||
| 	socket net.Conn | 	socket net.Conn | ||||||
| 	pwrite chan string | 	pwrite chan string | ||||||
| @@ -59,6 +61,7 @@ type Event struct { | |||||||
| 	Source     string //<host> | 	Source     string //<host> | ||||||
| 	User       string //<usr> | 	User       string //<usr> | ||||||
| 	Arguments  []string | 	Arguments  []string | ||||||
|  | 	Tags       map[string]string | ||||||
| 	Connection *Connection | 	Connection *Connection | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
							
								
								
									
										212
									
								
								vendor/github.com/hashicorp/golang-lru/2q.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								vendor/github.com/hashicorp/golang-lru/2q.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,212 @@ | |||||||
|  | package lru | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"sync" | ||||||
|  |  | ||||||
|  | 	"github.com/hashicorp/golang-lru/simplelru" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	// Default2QRecentRatio is the ratio of the 2Q cache dedicated | ||||||
|  | 	// to recently added entries that have only been accessed once. | ||||||
|  | 	Default2QRecentRatio = 0.25 | ||||||
|  |  | ||||||
|  | 	// Default2QGhostEntries is the default ratio of ghost | ||||||
|  | 	// entries kept to track entries recently evicted | ||||||
|  | 	Default2QGhostEntries = 0.50 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // TwoQueueCache is a thread-safe fixed size 2Q cache. | ||||||
|  | // 2Q is an enhancement over the standard LRU cache | ||||||
|  | // in that it tracks both frequently and recently used | ||||||
|  | // entries separately. This avoids a burst in access to new | ||||||
|  | // entries from evicting frequently used entries. It adds some | ||||||
|  | // additional tracking overhead to the standard LRU cache, and is | ||||||
|  | // computationally about 2x the cost, and adds some metadata over | ||||||
|  | // head. The ARCCache is similar, but does not require setting any | ||||||
|  | // parameters. | ||||||
|  | type TwoQueueCache struct { | ||||||
|  | 	size       int | ||||||
|  | 	recentSize int | ||||||
|  |  | ||||||
|  | 	recent      *simplelru.LRU | ||||||
|  | 	frequent    *simplelru.LRU | ||||||
|  | 	recentEvict *simplelru.LRU | ||||||
|  | 	lock        sync.RWMutex | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // New2Q creates a new TwoQueueCache using the default | ||||||
|  | // values for the parameters. | ||||||
|  | func New2Q(size int) (*TwoQueueCache, error) { | ||||||
|  | 	return New2QParams(size, Default2QRecentRatio, Default2QGhostEntries) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // New2QParams creates a new TwoQueueCache using the provided | ||||||
|  | // parameter values. | ||||||
|  | func New2QParams(size int, recentRatio float64, ghostRatio float64) (*TwoQueueCache, error) { | ||||||
|  | 	if size <= 0 { | ||||||
|  | 		return nil, fmt.Errorf("invalid size") | ||||||
|  | 	} | ||||||
|  | 	if recentRatio < 0.0 || recentRatio > 1.0 { | ||||||
|  | 		return nil, fmt.Errorf("invalid recent ratio") | ||||||
|  | 	} | ||||||
|  | 	if ghostRatio < 0.0 || ghostRatio > 1.0 { | ||||||
|  | 		return nil, fmt.Errorf("invalid ghost ratio") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Determine the sub-sizes | ||||||
|  | 	recentSize := int(float64(size) * recentRatio) | ||||||
|  | 	evictSize := int(float64(size) * ghostRatio) | ||||||
|  |  | ||||||
|  | 	// Allocate the LRUs | ||||||
|  | 	recent, err := simplelru.NewLRU(size, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	frequent, err := simplelru.NewLRU(size, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	recentEvict, err := simplelru.NewLRU(evictSize, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Initialize the cache | ||||||
|  | 	c := &TwoQueueCache{ | ||||||
|  | 		size:        size, | ||||||
|  | 		recentSize:  recentSize, | ||||||
|  | 		recent:      recent, | ||||||
|  | 		frequent:    frequent, | ||||||
|  | 		recentEvict: recentEvict, | ||||||
|  | 	} | ||||||
|  | 	return c, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TwoQueueCache) Get(key interface{}) (interface{}, bool) { | ||||||
|  | 	c.lock.Lock() | ||||||
|  | 	defer c.lock.Unlock() | ||||||
|  |  | ||||||
|  | 	// Check if this is a frequent value | ||||||
|  | 	if val, ok := c.frequent.Get(key); ok { | ||||||
|  | 		return val, ok | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// If the value is contained in recent, then we | ||||||
|  | 	// promote it to frequent | ||||||
|  | 	if val, ok := c.recent.Peek(key); ok { | ||||||
|  | 		c.recent.Remove(key) | ||||||
|  | 		c.frequent.Add(key, val) | ||||||
|  | 		return val, ok | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// No hit | ||||||
|  | 	return nil, false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TwoQueueCache) Add(key, value interface{}) { | ||||||
|  | 	c.lock.Lock() | ||||||
|  | 	defer c.lock.Unlock() | ||||||
|  |  | ||||||
|  | 	// Check if the value is frequently used already, | ||||||
|  | 	// and just update the value | ||||||
|  | 	if c.frequent.Contains(key) { | ||||||
|  | 		c.frequent.Add(key, value) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Check if the value is recently used, and promote | ||||||
|  | 	// the value into the frequent list | ||||||
|  | 	if c.recent.Contains(key) { | ||||||
|  | 		c.recent.Remove(key) | ||||||
|  | 		c.frequent.Add(key, value) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// If the value was recently evicted, add it to the | ||||||
|  | 	// frequently used list | ||||||
|  | 	if c.recentEvict.Contains(key) { | ||||||
|  | 		c.ensureSpace(true) | ||||||
|  | 		c.recentEvict.Remove(key) | ||||||
|  | 		c.frequent.Add(key, value) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Add to the recently seen list | ||||||
|  | 	c.ensureSpace(false) | ||||||
|  | 	c.recent.Add(key, value) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ensureSpace is used to ensure we have space in the cache | ||||||
|  | func (c *TwoQueueCache) ensureSpace(recentEvict bool) { | ||||||
|  | 	// If we have space, nothing to do | ||||||
|  | 	recentLen := c.recent.Len() | ||||||
|  | 	freqLen := c.frequent.Len() | ||||||
|  | 	if recentLen+freqLen < c.size { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// If the recent buffer is larger than | ||||||
|  | 	// the target, evict from there | ||||||
|  | 	if recentLen > 0 && (recentLen > c.recentSize || (recentLen == c.recentSize && !recentEvict)) { | ||||||
|  | 		k, _, _ := c.recent.RemoveOldest() | ||||||
|  | 		c.recentEvict.Add(k, nil) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Remove from the frequent list otherwise | ||||||
|  | 	c.frequent.RemoveOldest() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TwoQueueCache) Len() int { | ||||||
|  | 	c.lock.RLock() | ||||||
|  | 	defer c.lock.RUnlock() | ||||||
|  | 	return c.recent.Len() + c.frequent.Len() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TwoQueueCache) Keys() []interface{} { | ||||||
|  | 	c.lock.RLock() | ||||||
|  | 	defer c.lock.RUnlock() | ||||||
|  | 	k1 := c.frequent.Keys() | ||||||
|  | 	k2 := c.recent.Keys() | ||||||
|  | 	return append(k1, k2...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TwoQueueCache) Remove(key interface{}) { | ||||||
|  | 	c.lock.Lock() | ||||||
|  | 	defer c.lock.Unlock() | ||||||
|  | 	if c.frequent.Remove(key) { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if c.recent.Remove(key) { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if c.recentEvict.Remove(key) { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TwoQueueCache) Purge() { | ||||||
|  | 	c.lock.Lock() | ||||||
|  | 	defer c.lock.Unlock() | ||||||
|  | 	c.recent.Purge() | ||||||
|  | 	c.frequent.Purge() | ||||||
|  | 	c.recentEvict.Purge() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TwoQueueCache) Contains(key interface{}) bool { | ||||||
|  | 	c.lock.RLock() | ||||||
|  | 	defer c.lock.RUnlock() | ||||||
|  | 	return c.frequent.Contains(key) || c.recent.Contains(key) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TwoQueueCache) Peek(key interface{}) (interface{}, bool) { | ||||||
|  | 	c.lock.RLock() | ||||||
|  | 	defer c.lock.RUnlock() | ||||||
|  | 	if val, ok := c.frequent.Peek(key); ok { | ||||||
|  | 		return val, ok | ||||||
|  | 	} | ||||||
|  | 	return c.recent.Peek(key) | ||||||
|  | } | ||||||
							
								
								
									
										362
									
								
								vendor/github.com/hashicorp/golang-lru/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										362
									
								
								vendor/github.com/hashicorp/golang-lru/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,362 @@ | |||||||
|  | Mozilla Public License, version 2.0 | ||||||
|  |  | ||||||
|  | 1. Definitions | ||||||
|  |  | ||||||
|  | 1.1. "Contributor" | ||||||
|  |  | ||||||
|  |      means each individual or legal entity that creates, contributes to the | ||||||
|  |      creation of, or owns Covered Software. | ||||||
|  |  | ||||||
|  | 1.2. "Contributor Version" | ||||||
|  |  | ||||||
|  |      means the combination of the Contributions of others (if any) used by a | ||||||
|  |      Contributor and that particular Contributor's Contribution. | ||||||
|  |  | ||||||
|  | 1.3. "Contribution" | ||||||
|  |  | ||||||
|  |      means Covered Software of a particular Contributor. | ||||||
|  |  | ||||||
|  | 1.4. "Covered Software" | ||||||
|  |  | ||||||
|  |      means Source Code Form to which the initial Contributor has attached the | ||||||
|  |      notice in Exhibit A, the Executable Form of such Source Code Form, and | ||||||
|  |      Modifications of such Source Code Form, in each case including portions | ||||||
|  |      thereof. | ||||||
|  |  | ||||||
|  | 1.5. "Incompatible With Secondary Licenses" | ||||||
|  |      means | ||||||
|  |  | ||||||
|  |      a. that the initial Contributor has attached the notice described in | ||||||
|  |         Exhibit B to the Covered Software; or | ||||||
|  |  | ||||||
|  |      b. that the Covered Software was made available under the terms of | ||||||
|  |         version 1.1 or earlier of the License, but not also under the terms of | ||||||
|  |         a Secondary License. | ||||||
|  |  | ||||||
|  | 1.6. "Executable Form" | ||||||
|  |  | ||||||
|  |      means any form of the work other than Source Code Form. | ||||||
|  |  | ||||||
|  | 1.7. "Larger Work" | ||||||
|  |  | ||||||
|  |      means a work that combines Covered Software with other material, in a | ||||||
|  |      separate file or files, that is not Covered Software. | ||||||
|  |  | ||||||
|  | 1.8. "License" | ||||||
|  |  | ||||||
|  |      means this document. | ||||||
|  |  | ||||||
|  | 1.9. "Licensable" | ||||||
|  |  | ||||||
|  |      means having the right to grant, to the maximum extent possible, whether | ||||||
|  |      at the time of the initial grant or subsequently, any and all of the | ||||||
|  |      rights conveyed by this License. | ||||||
|  |  | ||||||
|  | 1.10. "Modifications" | ||||||
|  |  | ||||||
|  |      means any of the following: | ||||||
|  |  | ||||||
|  |      a. any file in Source Code Form that results from an addition to, | ||||||
|  |         deletion from, or modification of the contents of Covered Software; or | ||||||
|  |  | ||||||
|  |      b. any new file in Source Code Form that contains any Covered Software. | ||||||
|  |  | ||||||
|  | 1.11. "Patent Claims" of a Contributor | ||||||
|  |  | ||||||
|  |       means any patent claim(s), including without limitation, method, | ||||||
|  |       process, and apparatus claims, in any patent Licensable by such | ||||||
|  |       Contributor that would be infringed, but for the grant of the License, | ||||||
|  |       by the making, using, selling, offering for sale, having made, import, | ||||||
|  |       or transfer of either its Contributions or its Contributor Version. | ||||||
|  |  | ||||||
|  | 1.12. "Secondary License" | ||||||
|  |  | ||||||
|  |       means either the GNU General Public License, Version 2.0, the GNU Lesser | ||||||
|  |       General Public License, Version 2.1, the GNU Affero General Public | ||||||
|  |       License, Version 3.0, or any later versions of those licenses. | ||||||
|  |  | ||||||
|  | 1.13. "Source Code Form" | ||||||
|  |  | ||||||
|  |       means the form of the work preferred for making modifications. | ||||||
|  |  | ||||||
|  | 1.14. "You" (or "Your") | ||||||
|  |  | ||||||
|  |       means an individual or a legal entity exercising rights under this | ||||||
|  |       License. For legal entities, "You" includes any entity that controls, is | ||||||
|  |       controlled by, or is under common control with You. For purposes of this | ||||||
|  |       definition, "control" means (a) the power, direct or indirect, to cause | ||||||
|  |       the direction or management of such entity, whether by contract or | ||||||
|  |       otherwise, or (b) ownership of more than fifty percent (50%) of the | ||||||
|  |       outstanding shares or beneficial ownership of such entity. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 2. License Grants and Conditions | ||||||
|  |  | ||||||
|  | 2.1. Grants | ||||||
|  |  | ||||||
|  |      Each Contributor hereby grants You a world-wide, royalty-free, | ||||||
|  |      non-exclusive license: | ||||||
|  |  | ||||||
|  |      a. under intellectual property rights (other than patent or trademark) | ||||||
|  |         Licensable by such Contributor to use, reproduce, make available, | ||||||
|  |         modify, display, perform, distribute, and otherwise exploit its | ||||||
|  |         Contributions, either on an unmodified basis, with Modifications, or | ||||||
|  |         as part of a Larger Work; and | ||||||
|  |  | ||||||
|  |      b. under Patent Claims of such Contributor to make, use, sell, offer for | ||||||
|  |         sale, have made, import, and otherwise transfer either its | ||||||
|  |         Contributions or its Contributor Version. | ||||||
|  |  | ||||||
|  | 2.2. Effective Date | ||||||
|  |  | ||||||
|  |      The licenses granted in Section 2.1 with respect to any Contribution | ||||||
|  |      become effective for each Contribution on the date the Contributor first | ||||||
|  |      distributes such Contribution. | ||||||
|  |  | ||||||
|  | 2.3. Limitations on Grant Scope | ||||||
|  |  | ||||||
|  |      The licenses granted in this Section 2 are the only rights granted under | ||||||
|  |      this License. No additional rights or licenses will be implied from the | ||||||
|  |      distribution or licensing of Covered Software under this License. | ||||||
|  |      Notwithstanding Section 2.1(b) above, no patent license is granted by a | ||||||
|  |      Contributor: | ||||||
|  |  | ||||||
|  |      a. for any code that a Contributor has removed from Covered Software; or | ||||||
|  |  | ||||||
|  |      b. for infringements caused by: (i) Your and any other third party's | ||||||
|  |         modifications of Covered Software, or (ii) the combination of its | ||||||
|  |         Contributions with other software (except as part of its Contributor | ||||||
|  |         Version); or | ||||||
|  |  | ||||||
|  |      c. under Patent Claims infringed by Covered Software in the absence of | ||||||
|  |         its Contributions. | ||||||
|  |  | ||||||
|  |      This License does not grant any rights in the trademarks, service marks, | ||||||
|  |      or logos of any Contributor (except as may be necessary to comply with | ||||||
|  |      the notice requirements in Section 3.4). | ||||||
|  |  | ||||||
|  | 2.4. Subsequent Licenses | ||||||
|  |  | ||||||
|  |      No Contributor makes additional grants as a result of Your choice to | ||||||
|  |      distribute the Covered Software under a subsequent version of this | ||||||
|  |      License (see Section 10.2) or under the terms of a Secondary License (if | ||||||
|  |      permitted under the terms of Section 3.3). | ||||||
|  |  | ||||||
|  | 2.5. Representation | ||||||
|  |  | ||||||
|  |      Each Contributor represents that the Contributor believes its | ||||||
|  |      Contributions are its original creation(s) or it has sufficient rights to | ||||||
|  |      grant the rights to its Contributions conveyed by this License. | ||||||
|  |  | ||||||
|  | 2.6. Fair Use | ||||||
|  |  | ||||||
|  |      This License is not intended to limit any rights You have under | ||||||
|  |      applicable copyright doctrines of fair use, fair dealing, or other | ||||||
|  |      equivalents. | ||||||
|  |  | ||||||
|  | 2.7. Conditions | ||||||
|  |  | ||||||
|  |      Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in | ||||||
|  |      Section 2.1. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 3. Responsibilities | ||||||
|  |  | ||||||
|  | 3.1. Distribution of Source Form | ||||||
|  |  | ||||||
|  |      All distribution of Covered Software in Source Code Form, including any | ||||||
|  |      Modifications that You create or to which You contribute, must be under | ||||||
|  |      the terms of this License. You must inform recipients that the Source | ||||||
|  |      Code Form of the Covered Software is governed by the terms of this | ||||||
|  |      License, and how they can obtain a copy of this License. You may not | ||||||
|  |      attempt to alter or restrict the recipients' rights in the Source Code | ||||||
|  |      Form. | ||||||
|  |  | ||||||
|  | 3.2. Distribution of Executable Form | ||||||
|  |  | ||||||
|  |      If You distribute Covered Software in Executable Form then: | ||||||
|  |  | ||||||
|  |      a. such Covered Software must also be made available in Source Code Form, | ||||||
|  |         as described in Section 3.1, and You must inform recipients of the | ||||||
|  |         Executable Form how they can obtain a copy of such Source Code Form by | ||||||
|  |         reasonable means in a timely manner, at a charge no more than the cost | ||||||
|  |         of distribution to the recipient; and | ||||||
|  |  | ||||||
|  |      b. You may distribute such Executable Form under the terms of this | ||||||
|  |         License, or sublicense it under different terms, provided that the | ||||||
|  |         license for the Executable Form does not attempt to limit or alter the | ||||||
|  |         recipients' rights in the Source Code Form under this License. | ||||||
|  |  | ||||||
|  | 3.3. Distribution of a Larger Work | ||||||
|  |  | ||||||
|  |      You may create and distribute a Larger Work under terms of Your choice, | ||||||
|  |      provided that You also comply with the requirements of this License for | ||||||
|  |      the Covered Software. If the Larger Work is a combination of Covered | ||||||
|  |      Software with a work governed by one or more Secondary Licenses, and the | ||||||
|  |      Covered Software is not Incompatible With Secondary Licenses, this | ||||||
|  |      License permits You to additionally distribute such Covered Software | ||||||
|  |      under the terms of such Secondary License(s), so that the recipient of | ||||||
|  |      the Larger Work may, at their option, further distribute the Covered | ||||||
|  |      Software under the terms of either this License or such Secondary | ||||||
|  |      License(s). | ||||||
|  |  | ||||||
|  | 3.4. Notices | ||||||
|  |  | ||||||
|  |      You may not remove or alter the substance of any license notices | ||||||
|  |      (including copyright notices, patent notices, disclaimers of warranty, or | ||||||
|  |      limitations of liability) contained within the Source Code Form of the | ||||||
|  |      Covered Software, except that You may alter any license notices to the | ||||||
|  |      extent required to remedy known factual inaccuracies. | ||||||
|  |  | ||||||
|  | 3.5. Application of Additional Terms | ||||||
|  |  | ||||||
|  |      You may choose to offer, and to charge a fee for, warranty, support, | ||||||
|  |      indemnity or liability obligations to one or more recipients of Covered | ||||||
|  |      Software. However, You may do so only on Your own behalf, and not on | ||||||
|  |      behalf of any Contributor. You must make it absolutely clear that any | ||||||
|  |      such warranty, support, indemnity, or liability obligation is offered by | ||||||
|  |      You alone, and You hereby agree to indemnify every Contributor for any | ||||||
|  |      liability incurred by such Contributor as a result of warranty, support, | ||||||
|  |      indemnity or liability terms You offer. You may include additional | ||||||
|  |      disclaimers of warranty and limitations of liability specific to any | ||||||
|  |      jurisdiction. | ||||||
|  |  | ||||||
|  | 4. Inability to Comply Due to Statute or Regulation | ||||||
|  |  | ||||||
|  |    If it is impossible for You to comply with any of the terms of this License | ||||||
|  |    with respect to some or all of the Covered Software due to statute, | ||||||
|  |    judicial order, or regulation then You must: (a) comply with the terms of | ||||||
|  |    this License to the maximum extent possible; and (b) describe the | ||||||
|  |    limitations and the code they affect. Such description must be placed in a | ||||||
|  |    text file included with all distributions of the Covered Software under | ||||||
|  |    this License. Except to the extent prohibited by statute or regulation, | ||||||
|  |    such description must be sufficiently detailed for a recipient of ordinary | ||||||
|  |    skill to be able to understand it. | ||||||
|  |  | ||||||
|  | 5. Termination | ||||||
|  |  | ||||||
|  | 5.1. The rights granted under this License will terminate automatically if You | ||||||
|  |      fail to comply with any of its terms. However, if You become compliant, | ||||||
|  |      then the rights granted under this License from a particular Contributor | ||||||
|  |      are reinstated (a) provisionally, unless and until such Contributor | ||||||
|  |      explicitly and finally terminates Your grants, and (b) on an ongoing | ||||||
|  |      basis, if such Contributor fails to notify You of the non-compliance by | ||||||
|  |      some reasonable means prior to 60 days after You have come back into | ||||||
|  |      compliance. Moreover, Your grants from a particular Contributor are | ||||||
|  |      reinstated on an ongoing basis if such Contributor notifies You of the | ||||||
|  |      non-compliance by some reasonable means, this is the first time You have | ||||||
|  |      received notice of non-compliance with this License from such | ||||||
|  |      Contributor, and You become compliant prior to 30 days after Your receipt | ||||||
|  |      of the notice. | ||||||
|  |  | ||||||
|  | 5.2. If You initiate litigation against any entity by asserting a patent | ||||||
|  |      infringement claim (excluding declaratory judgment actions, | ||||||
|  |      counter-claims, and cross-claims) alleging that a Contributor Version | ||||||
|  |      directly or indirectly infringes any patent, then the rights granted to | ||||||
|  |      You by any and all Contributors for the Covered Software under Section | ||||||
|  |      2.1 of this License shall terminate. | ||||||
|  |  | ||||||
|  | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user | ||||||
|  |      license agreements (excluding distributors and resellers) which have been | ||||||
|  |      validly granted by You or Your distributors under this License prior to | ||||||
|  |      termination shall survive termination. | ||||||
|  |  | ||||||
|  | 6. Disclaimer of Warranty | ||||||
|  |  | ||||||
|  |    Covered Software is provided under this License on an "as is" basis, | ||||||
|  |    without warranty of any kind, either expressed, implied, or statutory, | ||||||
|  |    including, without limitation, warranties that the Covered Software is free | ||||||
|  |    of defects, merchantable, fit for a particular purpose or non-infringing. | ||||||
|  |    The entire risk as to the quality and performance of the Covered Software | ||||||
|  |    is with You. Should any Covered Software prove defective in any respect, | ||||||
|  |    You (not any Contributor) assume the cost of any necessary servicing, | ||||||
|  |    repair, or correction. This disclaimer of warranty constitutes an essential | ||||||
|  |    part of this License. No use of  any Covered Software is authorized under | ||||||
|  |    this License except under this disclaimer. | ||||||
|  |  | ||||||
|  | 7. Limitation of Liability | ||||||
|  |  | ||||||
|  |    Under no circumstances and under no legal theory, whether tort (including | ||||||
|  |    negligence), contract, or otherwise, shall any Contributor, or anyone who | ||||||
|  |    distributes Covered Software as permitted above, be liable to You for any | ||||||
|  |    direct, indirect, special, incidental, or consequential damages of any | ||||||
|  |    character including, without limitation, damages for lost profits, loss of | ||||||
|  |    goodwill, work stoppage, computer failure or malfunction, or any and all | ||||||
|  |    other commercial damages or losses, even if such party shall have been | ||||||
|  |    informed of the possibility of such damages. This limitation of liability | ||||||
|  |    shall not apply to liability for death or personal injury resulting from | ||||||
|  |    such party's negligence to the extent applicable law prohibits such | ||||||
|  |    limitation. Some jurisdictions do not allow the exclusion or limitation of | ||||||
|  |    incidental or consequential damages, so this exclusion and limitation may | ||||||
|  |    not apply to You. | ||||||
|  |  | ||||||
|  | 8. Litigation | ||||||
|  |  | ||||||
|  |    Any litigation relating to this License may be brought only in the courts | ||||||
|  |    of a jurisdiction where the defendant maintains its principal place of | ||||||
|  |    business and such litigation shall be governed by laws of that | ||||||
|  |    jurisdiction, without reference to its conflict-of-law provisions. Nothing | ||||||
|  |    in this Section shall prevent a party's ability to bring cross-claims or | ||||||
|  |    counter-claims. | ||||||
|  |  | ||||||
|  | 9. Miscellaneous | ||||||
|  |  | ||||||
|  |    This License represents the complete agreement concerning the subject | ||||||
|  |    matter hereof. If any provision of this License is held to be | ||||||
|  |    unenforceable, such provision shall be reformed only to the extent | ||||||
|  |    necessary to make it enforceable. Any law or regulation which provides that | ||||||
|  |    the language of a contract shall be construed against the drafter shall not | ||||||
|  |    be used to construe this License against a Contributor. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 10. Versions of the License | ||||||
|  |  | ||||||
|  | 10.1. New Versions | ||||||
|  |  | ||||||
|  |       Mozilla Foundation is the license steward. Except as provided in Section | ||||||
|  |       10.3, no one other than the license steward has the right to modify or | ||||||
|  |       publish new versions of this License. Each version will be given a | ||||||
|  |       distinguishing version number. | ||||||
|  |  | ||||||
|  | 10.2. Effect of New Versions | ||||||
|  |  | ||||||
|  |       You may distribute the Covered Software under the terms of the version | ||||||
|  |       of the License under which You originally received the Covered Software, | ||||||
|  |       or under the terms of any subsequent version published by the license | ||||||
|  |       steward. | ||||||
|  |  | ||||||
|  | 10.3. Modified Versions | ||||||
|  |  | ||||||
|  |       If you create software not governed by this License, and you want to | ||||||
|  |       create a new license for such software, you may create and use a | ||||||
|  |       modified version of this License if you rename the license and remove | ||||||
|  |       any references to the name of the license steward (except to note that | ||||||
|  |       such modified license differs from this License). | ||||||
|  |  | ||||||
|  | 10.4. Distributing Source Code Form that is Incompatible With Secondary | ||||||
|  |       Licenses If You choose to distribute Source Code Form that is | ||||||
|  |       Incompatible With Secondary Licenses under the terms of this version of | ||||||
|  |       the License, the notice described in Exhibit B of this License must be | ||||||
|  |       attached. | ||||||
|  |  | ||||||
|  | Exhibit A - Source Code Form License Notice | ||||||
|  |  | ||||||
|  |       This Source Code Form is subject to the | ||||||
|  |       terms of the Mozilla Public License, v. | ||||||
|  |       2.0. If a copy of the MPL was not | ||||||
|  |       distributed with this file, You can | ||||||
|  |       obtain one at | ||||||
|  |       http://mozilla.org/MPL/2.0/. | ||||||
|  |  | ||||||
|  | If it is not possible or desirable to put the notice in a particular file, | ||||||
|  | then You may include the notice in a location (such as a LICENSE file in a | ||||||
|  | relevant directory) where a recipient would be likely to look for such a | ||||||
|  | notice. | ||||||
|  |  | ||||||
|  | You may add additional accurate notices of copyright ownership. | ||||||
|  |  | ||||||
|  | Exhibit B - "Incompatible With Secondary Licenses" Notice | ||||||
|  |  | ||||||
|  |       This Source Code Form is "Incompatible | ||||||
|  |       With Secondary Licenses", as defined by | ||||||
|  |       the Mozilla Public License, v. 2.0. | ||||||
							
								
								
									
										257
									
								
								vendor/github.com/hashicorp/golang-lru/arc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										257
									
								
								vendor/github.com/hashicorp/golang-lru/arc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,257 @@ | |||||||
|  | package lru | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"sync" | ||||||
|  |  | ||||||
|  | 	"github.com/hashicorp/golang-lru/simplelru" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // ARCCache is a thread-safe fixed size Adaptive Replacement Cache (ARC). | ||||||
|  | // ARC is an enhancement over the standard LRU cache in that tracks both | ||||||
|  | // frequency and recency of use. This avoids a burst in access to new | ||||||
|  | // entries from evicting the frequently used older entries. It adds some | ||||||
|  | // additional tracking overhead to a standard LRU cache, computationally | ||||||
|  | // it is roughly 2x the cost, and the extra memory overhead is linear | ||||||
|  | // with the size of the cache. ARC has been patented by IBM, but is | ||||||
|  | // similar to the TwoQueueCache (2Q) which requires setting parameters. | ||||||
|  | type ARCCache struct { | ||||||
|  | 	size int // Size is the total capacity of the cache | ||||||
|  | 	p    int // P is the dynamic preference towards T1 or T2 | ||||||
|  |  | ||||||
|  | 	t1 *simplelru.LRU // T1 is the LRU for recently accessed items | ||||||
|  | 	b1 *simplelru.LRU // B1 is the LRU for evictions from t1 | ||||||
|  |  | ||||||
|  | 	t2 *simplelru.LRU // T2 is the LRU for frequently accessed items | ||||||
|  | 	b2 *simplelru.LRU // B2 is the LRU for evictions from t2 | ||||||
|  |  | ||||||
|  | 	lock sync.RWMutex | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewARC creates an ARC of the given size | ||||||
|  | func NewARC(size int) (*ARCCache, error) { | ||||||
|  | 	// Create the sub LRUs | ||||||
|  | 	b1, err := simplelru.NewLRU(size, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	b2, err := simplelru.NewLRU(size, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	t1, err := simplelru.NewLRU(size, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	t2, err := simplelru.NewLRU(size, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Initialize the ARC | ||||||
|  | 	c := &ARCCache{ | ||||||
|  | 		size: size, | ||||||
|  | 		p:    0, | ||||||
|  | 		t1:   t1, | ||||||
|  | 		b1:   b1, | ||||||
|  | 		t2:   t2, | ||||||
|  | 		b2:   b2, | ||||||
|  | 	} | ||||||
|  | 	return c, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Get looks up a key's value from the cache. | ||||||
|  | func (c *ARCCache) Get(key interface{}) (interface{}, bool) { | ||||||
|  | 	c.lock.Lock() | ||||||
|  | 	defer c.lock.Unlock() | ||||||
|  |  | ||||||
|  | 	// Ff the value is contained in T1 (recent), then | ||||||
|  | 	// promote it to T2 (frequent) | ||||||
|  | 	if val, ok := c.t1.Peek(key); ok { | ||||||
|  | 		c.t1.Remove(key) | ||||||
|  | 		c.t2.Add(key, val) | ||||||
|  | 		return val, ok | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Check if the value is contained in T2 (frequent) | ||||||
|  | 	if val, ok := c.t2.Get(key); ok { | ||||||
|  | 		return val, ok | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// No hit | ||||||
|  | 	return nil, false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Add adds a value to the cache. | ||||||
|  | func (c *ARCCache) Add(key, value interface{}) { | ||||||
|  | 	c.lock.Lock() | ||||||
|  | 	defer c.lock.Unlock() | ||||||
|  |  | ||||||
|  | 	// Check if the value is contained in T1 (recent), and potentially | ||||||
|  | 	// promote it to frequent T2 | ||||||
|  | 	if c.t1.Contains(key) { | ||||||
|  | 		c.t1.Remove(key) | ||||||
|  | 		c.t2.Add(key, value) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Check if the value is already in T2 (frequent) and update it | ||||||
|  | 	if c.t2.Contains(key) { | ||||||
|  | 		c.t2.Add(key, value) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Check if this value was recently evicted as part of the | ||||||
|  | 	// recently used list | ||||||
|  | 	if c.b1.Contains(key) { | ||||||
|  | 		// T1 set is too small, increase P appropriately | ||||||
|  | 		delta := 1 | ||||||
|  | 		b1Len := c.b1.Len() | ||||||
|  | 		b2Len := c.b2.Len() | ||||||
|  | 		if b2Len > b1Len { | ||||||
|  | 			delta = b2Len / b1Len | ||||||
|  | 		} | ||||||
|  | 		if c.p+delta >= c.size { | ||||||
|  | 			c.p = c.size | ||||||
|  | 		} else { | ||||||
|  | 			c.p += delta | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Potentially need to make room in the cache | ||||||
|  | 		if c.t1.Len()+c.t2.Len() >= c.size { | ||||||
|  | 			c.replace(false) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Remove from B1 | ||||||
|  | 		c.b1.Remove(key) | ||||||
|  |  | ||||||
|  | 		// Add the key to the frequently used list | ||||||
|  | 		c.t2.Add(key, value) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Check if this value was recently evicted as part of the | ||||||
|  | 	// frequently used list | ||||||
|  | 	if c.b2.Contains(key) { | ||||||
|  | 		// T2 set is too small, decrease P appropriately | ||||||
|  | 		delta := 1 | ||||||
|  | 		b1Len := c.b1.Len() | ||||||
|  | 		b2Len := c.b2.Len() | ||||||
|  | 		if b1Len > b2Len { | ||||||
|  | 			delta = b1Len / b2Len | ||||||
|  | 		} | ||||||
|  | 		if delta >= c.p { | ||||||
|  | 			c.p = 0 | ||||||
|  | 		} else { | ||||||
|  | 			c.p -= delta | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Potentially need to make room in the cache | ||||||
|  | 		if c.t1.Len()+c.t2.Len() >= c.size { | ||||||
|  | 			c.replace(true) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Remove from B2 | ||||||
|  | 		c.b2.Remove(key) | ||||||
|  |  | ||||||
|  | 		// Add the key to the frequntly used list | ||||||
|  | 		c.t2.Add(key, value) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Potentially need to make room in the cache | ||||||
|  | 	if c.t1.Len()+c.t2.Len() >= c.size { | ||||||
|  | 		c.replace(false) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Keep the size of the ghost buffers trim | ||||||
|  | 	if c.b1.Len() > c.size-c.p { | ||||||
|  | 		c.b1.RemoveOldest() | ||||||
|  | 	} | ||||||
|  | 	if c.b2.Len() > c.p { | ||||||
|  | 		c.b2.RemoveOldest() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Add to the recently seen list | ||||||
|  | 	c.t1.Add(key, value) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // replace is used to adaptively evict from either T1 or T2 | ||||||
|  | // based on the current learned value of P | ||||||
|  | func (c *ARCCache) replace(b2ContainsKey bool) { | ||||||
|  | 	t1Len := c.t1.Len() | ||||||
|  | 	if t1Len > 0 && (t1Len > c.p || (t1Len == c.p && b2ContainsKey)) { | ||||||
|  | 		k, _, ok := c.t1.RemoveOldest() | ||||||
|  | 		if ok { | ||||||
|  | 			c.b1.Add(k, nil) | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		k, _, ok := c.t2.RemoveOldest() | ||||||
|  | 		if ok { | ||||||
|  | 			c.b2.Add(k, nil) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Len returns the number of cached entries | ||||||
|  | func (c *ARCCache) Len() int { | ||||||
|  | 	c.lock.RLock() | ||||||
|  | 	defer c.lock.RUnlock() | ||||||
|  | 	return c.t1.Len() + c.t2.Len() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Keys returns all the cached keys | ||||||
|  | func (c *ARCCache) Keys() []interface{} { | ||||||
|  | 	c.lock.RLock() | ||||||
|  | 	defer c.lock.RUnlock() | ||||||
|  | 	k1 := c.t1.Keys() | ||||||
|  | 	k2 := c.t2.Keys() | ||||||
|  | 	return append(k1, k2...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Remove is used to purge a key from the cache | ||||||
|  | func (c *ARCCache) Remove(key interface{}) { | ||||||
|  | 	c.lock.Lock() | ||||||
|  | 	defer c.lock.Unlock() | ||||||
|  | 	if c.t1.Remove(key) { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if c.t2.Remove(key) { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if c.b1.Remove(key) { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if c.b2.Remove(key) { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Purge is used to clear the cache | ||||||
|  | func (c *ARCCache) Purge() { | ||||||
|  | 	c.lock.Lock() | ||||||
|  | 	defer c.lock.Unlock() | ||||||
|  | 	c.t1.Purge() | ||||||
|  | 	c.t2.Purge() | ||||||
|  | 	c.b1.Purge() | ||||||
|  | 	c.b2.Purge() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Contains is used to check if the cache contains a key | ||||||
|  | // without updating recency or frequency. | ||||||
|  | func (c *ARCCache) Contains(key interface{}) bool { | ||||||
|  | 	c.lock.RLock() | ||||||
|  | 	defer c.lock.RUnlock() | ||||||
|  | 	return c.t1.Contains(key) || c.t2.Contains(key) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Peek is used to inspect the cache value of a key | ||||||
|  | // without updating recency or frequency. | ||||||
|  | func (c *ARCCache) Peek(key interface{}) (interface{}, bool) { | ||||||
|  | 	c.lock.RLock() | ||||||
|  | 	defer c.lock.RUnlock() | ||||||
|  | 	if val, ok := c.t1.Peek(key); ok { | ||||||
|  | 		return val, ok | ||||||
|  | 	} | ||||||
|  | 	return c.t2.Peek(key) | ||||||
|  | } | ||||||
							
								
								
									
										114
									
								
								vendor/github.com/hashicorp/golang-lru/lru.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								vendor/github.com/hashicorp/golang-lru/lru.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,114 @@ | |||||||
|  | // This package provides a simple LRU cache. It is based on the | ||||||
|  | // LRU implementation in groupcache: | ||||||
|  | // https://github.com/golang/groupcache/tree/master/lru | ||||||
|  | package lru | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"sync" | ||||||
|  |  | ||||||
|  | 	"github.com/hashicorp/golang-lru/simplelru" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Cache is a thread-safe fixed size LRU cache. | ||||||
|  | type Cache struct { | ||||||
|  | 	lru  *simplelru.LRU | ||||||
|  | 	lock sync.RWMutex | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // New creates an LRU of the given size | ||||||
|  | func New(size int) (*Cache, error) { | ||||||
|  | 	return NewWithEvict(size, nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewWithEvict constructs a fixed size cache with the given eviction | ||||||
|  | // callback. | ||||||
|  | func NewWithEvict(size int, onEvicted func(key interface{}, value interface{})) (*Cache, error) { | ||||||
|  | 	lru, err := simplelru.NewLRU(size, simplelru.EvictCallback(onEvicted)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	c := &Cache{ | ||||||
|  | 		lru: lru, | ||||||
|  | 	} | ||||||
|  | 	return c, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Purge is used to completely clear the cache | ||||||
|  | func (c *Cache) Purge() { | ||||||
|  | 	c.lock.Lock() | ||||||
|  | 	c.lru.Purge() | ||||||
|  | 	c.lock.Unlock() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Add adds a value to the cache.  Returns true if an eviction occurred. | ||||||
|  | func (c *Cache) Add(key, value interface{}) bool { | ||||||
|  | 	c.lock.Lock() | ||||||
|  | 	defer c.lock.Unlock() | ||||||
|  | 	return c.lru.Add(key, value) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Get looks up a key's value from the cache. | ||||||
|  | func (c *Cache) Get(key interface{}) (interface{}, bool) { | ||||||
|  | 	c.lock.Lock() | ||||||
|  | 	defer c.lock.Unlock() | ||||||
|  | 	return c.lru.Get(key) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Check if a key is in the cache, without updating the recent-ness | ||||||
|  | // or deleting it for being stale. | ||||||
|  | func (c *Cache) Contains(key interface{}) bool { | ||||||
|  | 	c.lock.RLock() | ||||||
|  | 	defer c.lock.RUnlock() | ||||||
|  | 	return c.lru.Contains(key) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Returns the key value (or undefined if not found) without updating | ||||||
|  | // the "recently used"-ness of the key. | ||||||
|  | func (c *Cache) Peek(key interface{}) (interface{}, bool) { | ||||||
|  | 	c.lock.RLock() | ||||||
|  | 	defer c.lock.RUnlock() | ||||||
|  | 	return c.lru.Peek(key) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ContainsOrAdd checks if a key is in the cache  without updating the | ||||||
|  | // recent-ness or deleting it for being stale,  and if not, adds the value. | ||||||
|  | // Returns whether found and whether an eviction occurred. | ||||||
|  | func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evict bool) { | ||||||
|  | 	c.lock.Lock() | ||||||
|  | 	defer c.lock.Unlock() | ||||||
|  |  | ||||||
|  | 	if c.lru.Contains(key) { | ||||||
|  | 		return true, false | ||||||
|  | 	} else { | ||||||
|  | 		evict := c.lru.Add(key, value) | ||||||
|  | 		return false, evict | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Remove removes the provided key from the cache. | ||||||
|  | func (c *Cache) Remove(key interface{}) { | ||||||
|  | 	c.lock.Lock() | ||||||
|  | 	c.lru.Remove(key) | ||||||
|  | 	c.lock.Unlock() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RemoveOldest removes the oldest item from the cache. | ||||||
|  | func (c *Cache) RemoveOldest() { | ||||||
|  | 	c.lock.Lock() | ||||||
|  | 	c.lru.RemoveOldest() | ||||||
|  | 	c.lock.Unlock() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Keys returns a slice of the keys in the cache, from oldest to newest. | ||||||
|  | func (c *Cache) Keys() []interface{} { | ||||||
|  | 	c.lock.RLock() | ||||||
|  | 	defer c.lock.RUnlock() | ||||||
|  | 	return c.lru.Keys() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Len returns the number of items in the cache. | ||||||
|  | func (c *Cache) Len() int { | ||||||
|  | 	c.lock.RLock() | ||||||
|  | 	defer c.lock.RUnlock() | ||||||
|  | 	return c.lru.Len() | ||||||
|  | } | ||||||
							
								
								
									
										160
									
								
								vendor/github.com/hashicorp/golang-lru/simplelru/lru.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								vendor/github.com/hashicorp/golang-lru/simplelru/lru.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,160 @@ | |||||||
|  | package simplelru | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"container/list" | ||||||
|  | 	"errors" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // EvictCallback is used to get a callback when a cache entry is evicted | ||||||
|  | type EvictCallback func(key interface{}, value interface{}) | ||||||
|  |  | ||||||
|  | // LRU implements a non-thread safe fixed size LRU cache | ||||||
|  | type LRU struct { | ||||||
|  | 	size      int | ||||||
|  | 	evictList *list.List | ||||||
|  | 	items     map[interface{}]*list.Element | ||||||
|  | 	onEvict   EvictCallback | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // entry is used to hold a value in the evictList | ||||||
|  | type entry struct { | ||||||
|  | 	key   interface{} | ||||||
|  | 	value interface{} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewLRU constructs an LRU of the given size | ||||||
|  | func NewLRU(size int, onEvict EvictCallback) (*LRU, error) { | ||||||
|  | 	if size <= 0 { | ||||||
|  | 		return nil, errors.New("Must provide a positive size") | ||||||
|  | 	} | ||||||
|  | 	c := &LRU{ | ||||||
|  | 		size:      size, | ||||||
|  | 		evictList: list.New(), | ||||||
|  | 		items:     make(map[interface{}]*list.Element), | ||||||
|  | 		onEvict:   onEvict, | ||||||
|  | 	} | ||||||
|  | 	return c, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Purge is used to completely clear the cache | ||||||
|  | func (c *LRU) Purge() { | ||||||
|  | 	for k, v := range c.items { | ||||||
|  | 		if c.onEvict != nil { | ||||||
|  | 			c.onEvict(k, v.Value.(*entry).value) | ||||||
|  | 		} | ||||||
|  | 		delete(c.items, k) | ||||||
|  | 	} | ||||||
|  | 	c.evictList.Init() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Add adds a value to the cache.  Returns true if an eviction occurred. | ||||||
|  | func (c *LRU) Add(key, value interface{}) bool { | ||||||
|  | 	// Check for existing item | ||||||
|  | 	if ent, ok := c.items[key]; ok { | ||||||
|  | 		c.evictList.MoveToFront(ent) | ||||||
|  | 		ent.Value.(*entry).value = value | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Add new item | ||||||
|  | 	ent := &entry{key, value} | ||||||
|  | 	entry := c.evictList.PushFront(ent) | ||||||
|  | 	c.items[key] = entry | ||||||
|  |  | ||||||
|  | 	evict := c.evictList.Len() > c.size | ||||||
|  | 	// Verify size not exceeded | ||||||
|  | 	if evict { | ||||||
|  | 		c.removeOldest() | ||||||
|  | 	} | ||||||
|  | 	return evict | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Get looks up a key's value from the cache. | ||||||
|  | func (c *LRU) Get(key interface{}) (value interface{}, ok bool) { | ||||||
|  | 	if ent, ok := c.items[key]; ok { | ||||||
|  | 		c.evictList.MoveToFront(ent) | ||||||
|  | 		return ent.Value.(*entry).value, true | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Check if a key is in the cache, without updating the recent-ness | ||||||
|  | // or deleting it for being stale. | ||||||
|  | func (c *LRU) Contains(key interface{}) (ok bool) { | ||||||
|  | 	_, ok = c.items[key] | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Returns the key value (or undefined if not found) without updating | ||||||
|  | // the "recently used"-ness of the key. | ||||||
|  | func (c *LRU) Peek(key interface{}) (value interface{}, ok bool) { | ||||||
|  | 	if ent, ok := c.items[key]; ok { | ||||||
|  | 		return ent.Value.(*entry).value, true | ||||||
|  | 	} | ||||||
|  | 	return nil, ok | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Remove removes the provided key from the cache, returning if the | ||||||
|  | // key was contained. | ||||||
|  | func (c *LRU) Remove(key interface{}) bool { | ||||||
|  | 	if ent, ok := c.items[key]; ok { | ||||||
|  | 		c.removeElement(ent) | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RemoveOldest removes the oldest item from the cache. | ||||||
|  | func (c *LRU) RemoveOldest() (interface{}, interface{}, bool) { | ||||||
|  | 	ent := c.evictList.Back() | ||||||
|  | 	if ent != nil { | ||||||
|  | 		c.removeElement(ent) | ||||||
|  | 		kv := ent.Value.(*entry) | ||||||
|  | 		return kv.key, kv.value, true | ||||||
|  | 	} | ||||||
|  | 	return nil, nil, false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetOldest returns the oldest entry | ||||||
|  | func (c *LRU) GetOldest() (interface{}, interface{}, bool) { | ||||||
|  | 	ent := c.evictList.Back() | ||||||
|  | 	if ent != nil { | ||||||
|  | 		kv := ent.Value.(*entry) | ||||||
|  | 		return kv.key, kv.value, true | ||||||
|  | 	} | ||||||
|  | 	return nil, nil, false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Keys returns a slice of the keys in the cache, from oldest to newest. | ||||||
|  | func (c *LRU) Keys() []interface{} { | ||||||
|  | 	keys := make([]interface{}, len(c.items)) | ||||||
|  | 	i := 0 | ||||||
|  | 	for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() { | ||||||
|  | 		keys[i] = ent.Value.(*entry).key | ||||||
|  | 		i++ | ||||||
|  | 	} | ||||||
|  | 	return keys | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Len returns the number of items in the cache. | ||||||
|  | func (c *LRU) Len() int { | ||||||
|  | 	return c.evictList.Len() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // removeOldest removes the oldest item from the cache. | ||||||
|  | func (c *LRU) removeOldest() { | ||||||
|  | 	ent := c.evictList.Back() | ||||||
|  | 	if ent != nil { | ||||||
|  | 		c.removeElement(ent) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // removeElement is used to remove a given list element from the cache | ||||||
|  | func (c *LRU) removeElement(e *list.Element) { | ||||||
|  | 	c.evictList.Remove(e) | ||||||
|  | 	kv := e.Value.(*entry) | ||||||
|  | 	delete(c.items, kv.key) | ||||||
|  | 	if c.onEvict != nil { | ||||||
|  | 		c.onEvict(kv.key, kv.value) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								vendor/github.com/stretchr/testify/assert/LICENCE.txt
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/stretchr/testify/assert/LICENCE.txt
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell | ||||||
|  |  | ||||||
|  | Please consider promoting this project if you find it useful. | ||||||
|  |  | ||||||
|  | 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. | ||||||
							
								
								
									
										22
									
								
								vendor/github.com/stretchr/testify/assert/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/stretchr/testify/assert/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell | ||||||
|  |  | ||||||
|  | Please consider promoting this project if you find it useful. | ||||||
|  |  | ||||||
|  | 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. | ||||||
							
								
								
									
										379
									
								
								vendor/github.com/stretchr/testify/assert/assertion_format.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										379
									
								
								vendor/github.com/stretchr/testify/assert/assertion_format.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,379 @@ | |||||||
|  | /* | ||||||
|  | * CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen | ||||||
|  | * THIS FILE MUST NOT BE EDITED BY HAND | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package assert | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	http "net/http" | ||||||
|  | 	url "net/url" | ||||||
|  | 	time "time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Conditionf uses a Comparison to assert a complex condition. | ||||||
|  | func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bool { | ||||||
|  | 	return Condition(t, comp, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Containsf asserts that the specified string, list(array, slice...) or map contains the | ||||||
|  | // specified substring or element. | ||||||
|  | // | ||||||
|  | //    assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") | ||||||
|  | //    assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") | ||||||
|  | //    assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return Contains(t, s, contains, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Emptyf asserts that the specified object is empty.  I.e. nil, "", false, 0 or either | ||||||
|  | // a slice or a channel with len == 0. | ||||||
|  | // | ||||||
|  | //  assert.Emptyf(t, obj, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return Empty(t, object, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Equalf asserts that two objects are equal. | ||||||
|  | // | ||||||
|  | //    assert.Equalf(t, 123, 123, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | // | ||||||
|  | // Pointer variable equality is determined based on the equality of the | ||||||
|  | // 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 { | ||||||
|  | 	return Equal(t, expected, actual, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // EqualErrorf asserts that a function returned an error (i.e. not `nil`) | ||||||
|  | // and that it is equal to the provided error. | ||||||
|  | // | ||||||
|  | //   actualObj, err := SomeFunction() | ||||||
|  | //   assert.EqualErrorf(t, err,  expectedErrorString, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool { | ||||||
|  | 	return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // EqualValuesf asserts that two objects are equal or convertable to the same types | ||||||
|  | // and equal. | ||||||
|  | // | ||||||
|  | //    assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123)) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return EqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Errorf asserts that a function returned an error (i.e. not `nil`). | ||||||
|  | // | ||||||
|  | //   actualObj, err := SomeFunction() | ||||||
|  | //   if assert.Errorf(t, err, "error message %s", "formatted") { | ||||||
|  | // 	   assert.Equal(t, expectedErrorf, err) | ||||||
|  | //   } | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { | ||||||
|  | 	return Error(t, err, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Exactlyf asserts that two objects are equal is value and type. | ||||||
|  | // | ||||||
|  | //    assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123)) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	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 { | ||||||
|  | 	return Fail(t, failureMessage, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // FailNowf fails test | ||||||
|  | func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) bool { | ||||||
|  | 	return FailNow(t, failureMessage, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Falsef asserts that the specified value is false. | ||||||
|  | // | ||||||
|  | //    assert.Falsef(t, myBool, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool { | ||||||
|  | 	return False(t, value, 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") | ||||||
|  | // | ||||||
|  | // 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{}) bool { | ||||||
|  | 	return HTTPBodyContains(t, handler, method, url, values, str) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 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") | ||||||
|  | // | ||||||
|  | // 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{}) bool { | ||||||
|  | 	return HTTPBodyNotContains(t, handler, method, url, values, str) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // HTTPErrorf asserts that a specified handler returns an error status code. | ||||||
|  | // | ||||||
|  | //  assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||||
|  | // | ||||||
|  | // 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) bool { | ||||||
|  | 	return HTTPError(t, handler, method, url, values) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // HTTPRedirectf asserts that a specified handler returns a redirect status code. | ||||||
|  | // | ||||||
|  | //  assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||||
|  | // | ||||||
|  | // 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) bool { | ||||||
|  | 	return HTTPRedirect(t, handler, method, url, values) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // HTTPSuccessf asserts that a specified handler returns a success status code. | ||||||
|  | // | ||||||
|  | //  assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values) bool { | ||||||
|  | 	return HTTPSuccess(t, handler, method, url, values) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Implementsf asserts that an object is implemented by the specified interface. | ||||||
|  | // | ||||||
|  | //    assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) | ||||||
|  | func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return Implements(t, interfaceObject, object, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // InDeltaf asserts that the two numerals are within delta of each other. | ||||||
|  | // | ||||||
|  | // 	 assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { | ||||||
|  | 	return InDelta(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 { | ||||||
|  | 	return InDeltaSlice(t, expected, actual, delta, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // InEpsilonf asserts that expected and actual have a relative error less than epsilon | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { | ||||||
|  | 	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 { | ||||||
|  | 	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 { | ||||||
|  | 	return IsType(t, expectedType, object, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // JSONEqf asserts that two JSON strings are equivalent. | ||||||
|  | // | ||||||
|  | //  assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { | ||||||
|  | 	return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Lenf asserts that the specified object has specific length. | ||||||
|  | // Lenf also fails if the object has a type that len() not accept. | ||||||
|  | // | ||||||
|  | //    assert.Lenf(t, mySlice, 3, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool { | ||||||
|  | 	return Len(t, object, length, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Nilf asserts that the specified object is nil. | ||||||
|  | // | ||||||
|  | //    assert.Nilf(t, err, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return Nil(t, object, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NoErrorf asserts that a function returned no error (i.e. `nil`). | ||||||
|  | // | ||||||
|  | //   actualObj, err := SomeFunction() | ||||||
|  | //   if assert.NoErrorf(t, err, "error message %s", "formatted") { | ||||||
|  | // 	   assert.Equal(t, expectedObj, actualObj) | ||||||
|  | //   } | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool { | ||||||
|  | 	return NoError(t, err, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the | ||||||
|  | // specified substring or element. | ||||||
|  | // | ||||||
|  | //    assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") | ||||||
|  | //    assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") | ||||||
|  | //    assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return NotContains(t, s, contains, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotEmptyf asserts that the specified object is NOT empty.  I.e. not nil, "", false, 0 or either | ||||||
|  | // a slice or a channel with len == 0. | ||||||
|  | // | ||||||
|  | //  if assert.NotEmptyf(t, obj, "error message %s", "formatted") { | ||||||
|  | //    assert.Equal(t, "two", obj[1]) | ||||||
|  | //  } | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return NotEmpty(t, object, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotEqualf asserts that the specified values are NOT equal. | ||||||
|  | // | ||||||
|  | //    assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | // | ||||||
|  | // 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 { | ||||||
|  | 	return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotNilf asserts that the specified object is not nil. | ||||||
|  | // | ||||||
|  | //    assert.NotNilf(t, err, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return NotNil(t, object, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. | ||||||
|  | // | ||||||
|  | //   assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { | ||||||
|  | 	return NotPanics(t, f, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotRegexpf asserts that a specified regexp does not match a string. | ||||||
|  | // | ||||||
|  | //  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") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotSubsetf asserts that the specified list(array, slice...) contains not all | ||||||
|  | // elements given in the specified subset(array, slice...). | ||||||
|  | // | ||||||
|  | //    assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return NotSubset(t, list, subset, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotZerof asserts that i is not the zero value for its type and returns the truth. | ||||||
|  | func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return NotZero(t, i, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Panicsf asserts that the code inside the specified PanicTestFunc panics. | ||||||
|  | // | ||||||
|  | //   assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { | ||||||
|  | 	return Panics(t, f, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that | ||||||
|  | // the recovered panic value equals the expected panic value. | ||||||
|  | // | ||||||
|  | //   assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { | ||||||
|  | 	return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Regexpf asserts that a specified regexp matches a string. | ||||||
|  | // | ||||||
|  | //  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") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return Regexp(t, rx, str, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Subsetf asserts that the specified list(array, slice...) contains all | ||||||
|  | // elements given in the specified subset(array, slice...). | ||||||
|  | // | ||||||
|  | //    assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return Subset(t, list, subset, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Truef asserts that the specified value is true. | ||||||
|  | // | ||||||
|  | //    assert.Truef(t, myBool, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func Truef(t TestingT, value bool, msg string, args ...interface{}) bool { | ||||||
|  | 	return True(t, value, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WithinDurationf asserts that the two times are within duration delta of each other. | ||||||
|  | // | ||||||
|  | //   assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { | ||||||
|  | 	return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Zerof asserts that i is the zero value for its type and returns the truth. | ||||||
|  | func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return Zero(t, i, append([]interface{}{msg}, args...)...) | ||||||
|  | } | ||||||
							
								
								
									
										746
									
								
								vendor/github.com/stretchr/testify/assert/assertion_forward.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										746
									
								
								vendor/github.com/stretchr/testify/assert/assertion_forward.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,746 @@ | |||||||
|  | /* | ||||||
|  | * CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen | ||||||
|  | * THIS FILE MUST NOT BE EDITED BY HAND | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package assert | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	http "net/http" | ||||||
|  | 	url "net/url" | ||||||
|  | 	time "time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Condition uses a Comparison to assert a complex condition. | ||||||
|  | func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool { | ||||||
|  | 	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 { | ||||||
|  | 	return Conditionf(a.t, comp, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Contains asserts that the specified string, list(array, slice...) or map contains the | ||||||
|  | // specified substring or element. | ||||||
|  | // | ||||||
|  | //    a.Contains("Hello World", "World") | ||||||
|  | //    a.Contains(["Hello", "World"], "World") | ||||||
|  | //    a.Contains({"Hello": "World"}, "Hello") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return Contains(a.t, s, contains, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Containsf asserts that the specified string, list(array, slice...) or map contains the | ||||||
|  | // specified substring or element. | ||||||
|  | // | ||||||
|  | //    a.Containsf("Hello World", "World", "error message %s", "formatted") | ||||||
|  | //    a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") | ||||||
|  | //    a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return Containsf(a.t, s, contains, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Empty asserts that the specified object is empty.  I.e. nil, "", false, 0 or either | ||||||
|  | // a slice or a channel with len == 0. | ||||||
|  | // | ||||||
|  | //  a.Empty(obj) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return Empty(a.t, object, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Emptyf asserts that the specified object is empty.  I.e. nil, "", false, 0 or either | ||||||
|  | // a slice or a channel with len == 0. | ||||||
|  | // | ||||||
|  | //  a.Emptyf(obj, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return Emptyf(a.t, object, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Equal asserts that two objects are equal. | ||||||
|  | // | ||||||
|  | //    a.Equal(123, 123) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | // | ||||||
|  | // Pointer variable equality is determined based on the equality of the | ||||||
|  | // 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 { | ||||||
|  | 	return Equal(a.t, expected, actual, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // EqualError asserts that a function returned an error (i.e. not `nil`) | ||||||
|  | // and that it is equal to the provided error. | ||||||
|  | // | ||||||
|  | //   actualObj, err := SomeFunction() | ||||||
|  | //   a.EqualError(err,  expectedErrorString) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return EqualError(a.t, theError, errString, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // EqualErrorf asserts that a function returned an error (i.e. not `nil`) | ||||||
|  | // and that it is equal to the provided error. | ||||||
|  | // | ||||||
|  | //   actualObj, err := SomeFunction() | ||||||
|  | //   a.EqualErrorf(err,  expectedErrorString, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) bool { | ||||||
|  | 	return EqualErrorf(a.t, theError, errString, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // EqualValues asserts that two objects are equal or convertable to the same types | ||||||
|  | // and equal. | ||||||
|  | // | ||||||
|  | //    a.EqualValues(uint32(123), int32(123)) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return EqualValues(a.t, expected, actual, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // EqualValuesf asserts that two objects are equal or convertable to the same types | ||||||
|  | // and equal. | ||||||
|  | // | ||||||
|  | //    a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123)) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return EqualValuesf(a.t, expected, actual, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Equalf asserts that two objects are equal. | ||||||
|  | // | ||||||
|  | //    a.Equalf(123, 123, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | // | ||||||
|  | // Pointer variable equality is determined based on the equality of the | ||||||
|  | // 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 { | ||||||
|  | 	return Equalf(a.t, expected, actual, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Error asserts that a function returned an error (i.e. not `nil`). | ||||||
|  | // | ||||||
|  | //   actualObj, err := SomeFunction() | ||||||
|  | //   if a.Error(err) { | ||||||
|  | // 	   assert.Equal(t, expectedError, err) | ||||||
|  | //   } | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return Error(a.t, err, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Errorf asserts that a function returned an error (i.e. not `nil`). | ||||||
|  | // | ||||||
|  | //   actualObj, err := SomeFunction() | ||||||
|  | //   if a.Errorf(err, "error message %s", "formatted") { | ||||||
|  | // 	   assert.Equal(t, expectedErrorf, err) | ||||||
|  | //   } | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { | ||||||
|  | 	return Errorf(a.t, err, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Exactly asserts that two objects are equal is value and type. | ||||||
|  | // | ||||||
|  | //    a.Exactly(int32(123), int64(123)) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return Exactly(a.t, expected, actual, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Exactlyf asserts that two objects are equal is value and type. | ||||||
|  | // | ||||||
|  | //    a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123)) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return Exactlyf(a.t, expected, actual, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Fail reports a failure through | ||||||
|  | func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return Fail(a.t, failureMessage, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // FailNow fails test | ||||||
|  | func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return FailNow(a.t, failureMessage, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // FailNowf fails test | ||||||
|  | func (a *Assertions) FailNowf(failureMessage string, msg string, args ...interface{}) bool { | ||||||
|  | 	return FailNowf(a.t, failureMessage, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Failf reports a failure through | ||||||
|  | func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{}) bool { | ||||||
|  | 	return Failf(a.t, failureMessage, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // False asserts that the specified value is false. | ||||||
|  | // | ||||||
|  | //    a.False(myBool) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return False(a.t, value, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Falsef asserts that the specified value is false. | ||||||
|  | // | ||||||
|  | //    a.Falsef(myBool, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool { | ||||||
|  | 	return Falsef(a.t, value, 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") | ||||||
|  | // | ||||||
|  | // 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{}) bool { | ||||||
|  | 	return HTTPBodyContains(a.t, handler, method, url, values, str) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 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") | ||||||
|  | // | ||||||
|  | // 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{}) bool { | ||||||
|  | 	return HTTPBodyContainsf(a.t, handler, method, url, values, str) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 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") | ||||||
|  | // | ||||||
|  | // 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{}) bool { | ||||||
|  | 	return HTTPBodyNotContains(a.t, handler, method, url, values, str) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 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") | ||||||
|  | // | ||||||
|  | // 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{}) bool { | ||||||
|  | 	return HTTPBodyNotContainsf(a.t, handler, method, url, values, str) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // HTTPError asserts that a specified handler returns an error status code. | ||||||
|  | // | ||||||
|  | //  a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values) bool { | ||||||
|  | 	return HTTPError(a.t, handler, method, url, values) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // HTTPErrorf asserts that a specified handler returns an error status code. | ||||||
|  | // | ||||||
|  | //  a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||||
|  | // | ||||||
|  | // 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) bool { | ||||||
|  | 	return HTTPErrorf(a.t, handler, method, url, values) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // HTTPRedirect asserts that a specified handler returns a redirect status code. | ||||||
|  | // | ||||||
|  | //  a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values) bool { | ||||||
|  | 	return HTTPRedirect(a.t, handler, method, url, values) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // HTTPRedirectf asserts that a specified handler returns a redirect status code. | ||||||
|  | // | ||||||
|  | //  a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||||
|  | // | ||||||
|  | // 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) bool { | ||||||
|  | 	return HTTPRedirectf(a.t, handler, method, url, values) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // HTTPSuccess asserts that a specified handler returns a success status code. | ||||||
|  | // | ||||||
|  | //  a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values) bool { | ||||||
|  | 	return HTTPSuccess(a.t, handler, method, url, values) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // HTTPSuccessf asserts that a specified handler returns a success status code. | ||||||
|  | // | ||||||
|  | //  a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values) bool { | ||||||
|  | 	return HTTPSuccessf(a.t, handler, method, url, values) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Implements asserts that an object is implemented by the specified interface. | ||||||
|  | // | ||||||
|  | //    a.Implements((*MyInterface)(nil), new(MyObject)) | ||||||
|  | func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return Implements(a.t, interfaceObject, object, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Implementsf asserts that an object is implemented by the specified interface. | ||||||
|  | // | ||||||
|  | //    a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) | ||||||
|  | func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return Implementsf(a.t, interfaceObject, object, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // InDelta asserts that the two numerals are within delta of each other. | ||||||
|  | // | ||||||
|  | // 	 a.InDelta(math.Pi, (22 / 7.0), 0.01) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return InDelta(a.t, expected, actual, delta, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // InDeltaSlice is the same as InDelta, except it compares two slices. | ||||||
|  | func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { | ||||||
|  | 	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 { | ||||||
|  | 	return InDeltaSlicef(a.t, expected, actual, delta, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // InDeltaf asserts that the two numerals are within delta of each other. | ||||||
|  | // | ||||||
|  | // 	 a.InDeltaf(math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { | ||||||
|  | 	return InDeltaf(a.t, expected, actual, delta, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // InEpsilon asserts that expected and actual have a relative error less than epsilon | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { | ||||||
|  | 	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 { | ||||||
|  | 	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 { | ||||||
|  | 	return InEpsilonSlicef(a.t, expected, actual, epsilon, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // InEpsilonf asserts that expected and actual have a relative error less than epsilon | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { | ||||||
|  | 	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 { | ||||||
|  | 	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 { | ||||||
|  | 	return IsTypef(a.t, expectedType, object, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // JSONEq asserts that two JSON strings are equivalent. | ||||||
|  | // | ||||||
|  | //  a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return JSONEq(a.t, expected, actual, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // JSONEqf asserts that two JSON strings are equivalent. | ||||||
|  | // | ||||||
|  | //  a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) bool { | ||||||
|  | 	return JSONEqf(a.t, expected, actual, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Len asserts that the specified object has specific length. | ||||||
|  | // Len also fails if the object has a type that len() not accept. | ||||||
|  | // | ||||||
|  | //    a.Len(mySlice, 3) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return Len(a.t, object, length, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Lenf asserts that the specified object has specific length. | ||||||
|  | // Lenf also fails if the object has a type that len() not accept. | ||||||
|  | // | ||||||
|  | //    a.Lenf(mySlice, 3, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) bool { | ||||||
|  | 	return Lenf(a.t, object, length, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Nil asserts that the specified object is nil. | ||||||
|  | // | ||||||
|  | //    a.Nil(err) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return Nil(a.t, object, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Nilf asserts that the specified object is nil. | ||||||
|  | // | ||||||
|  | //    a.Nilf(err, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return Nilf(a.t, object, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NoError asserts that a function returned no error (i.e. `nil`). | ||||||
|  | // | ||||||
|  | //   actualObj, err := SomeFunction() | ||||||
|  | //   if a.NoError(err) { | ||||||
|  | // 	   assert.Equal(t, expectedObj, actualObj) | ||||||
|  | //   } | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return NoError(a.t, err, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NoErrorf asserts that a function returned no error (i.e. `nil`). | ||||||
|  | // | ||||||
|  | //   actualObj, err := SomeFunction() | ||||||
|  | //   if a.NoErrorf(err, "error message %s", "formatted") { | ||||||
|  | // 	   assert.Equal(t, expectedObj, actualObj) | ||||||
|  | //   } | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool { | ||||||
|  | 	return NoErrorf(a.t, err, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the | ||||||
|  | // specified substring or element. | ||||||
|  | // | ||||||
|  | //    a.NotContains("Hello World", "Earth") | ||||||
|  | //    a.NotContains(["Hello", "World"], "Earth") | ||||||
|  | //    a.NotContains({"Hello": "World"}, "Earth") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return NotContains(a.t, s, contains, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the | ||||||
|  | // specified substring or element. | ||||||
|  | // | ||||||
|  | //    a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") | ||||||
|  | //    a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") | ||||||
|  | //    a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return NotContainsf(a.t, s, contains, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotEmpty asserts that the specified object is NOT empty.  I.e. not nil, "", false, 0 or either | ||||||
|  | // a slice or a channel with len == 0. | ||||||
|  | // | ||||||
|  | //  if a.NotEmpty(obj) { | ||||||
|  | //    assert.Equal(t, "two", obj[1]) | ||||||
|  | //  } | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return NotEmpty(a.t, object, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotEmptyf asserts that the specified object is NOT empty.  I.e. not nil, "", false, 0 or either | ||||||
|  | // a slice or a channel with len == 0. | ||||||
|  | // | ||||||
|  | //  if a.NotEmptyf(obj, "error message %s", "formatted") { | ||||||
|  | //    assert.Equal(t, "two", obj[1]) | ||||||
|  | //  } | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return NotEmptyf(a.t, object, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotEqual asserts that the specified values are NOT equal. | ||||||
|  | // | ||||||
|  | //    a.NotEqual(obj1, obj2) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | // | ||||||
|  | // 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 { | ||||||
|  | 	return NotEqual(a.t, expected, actual, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotEqualf asserts that the specified values are NOT equal. | ||||||
|  | // | ||||||
|  | //    a.NotEqualf(obj1, obj2, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | // | ||||||
|  | // 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 { | ||||||
|  | 	return NotEqualf(a.t, expected, actual, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotNil asserts that the specified object is not nil. | ||||||
|  | // | ||||||
|  | //    a.NotNil(err) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return NotNil(a.t, object, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotNilf asserts that the specified object is not nil. | ||||||
|  | // | ||||||
|  | //    a.NotNilf(err, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return NotNilf(a.t, object, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. | ||||||
|  | // | ||||||
|  | //   a.NotPanics(func(){ RemainCalm() }) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return NotPanics(a.t, f, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. | ||||||
|  | // | ||||||
|  | //   a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{}) bool { | ||||||
|  | 	return NotPanicsf(a.t, f, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotRegexp asserts that a specified regexp does not match a string. | ||||||
|  | // | ||||||
|  | //  a.NotRegexp(regexp.MustCompile("starts"), "it's starting") | ||||||
|  | //  a.NotRegexp("^start", "it's not starting") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return NotRegexp(a.t, rx, str, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotRegexpf asserts that a specified regexp does not match a string. | ||||||
|  | // | ||||||
|  | //  a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") | ||||||
|  | //  a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return NotRegexpf(a.t, rx, str, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotSubset asserts that the specified list(array, slice...) contains not all | ||||||
|  | // elements given in the specified subset(array, slice...). | ||||||
|  | // | ||||||
|  | //    a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return NotSubset(a.t, list, subset, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotSubsetf asserts that the specified list(array, slice...) contains not all | ||||||
|  | // elements given in the specified subset(array, slice...). | ||||||
|  | // | ||||||
|  | //    a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return NotSubsetf(a.t, list, subset, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotZero asserts that i is not the zero value for its type and returns the truth. | ||||||
|  | func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return NotZero(a.t, i, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotZerof asserts that i is not the zero value for its type and returns the truth. | ||||||
|  | func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return NotZerof(a.t, i, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Panics asserts that the code inside the specified PanicTestFunc panics. | ||||||
|  | // | ||||||
|  | //   a.Panics(func(){ GoCrazy() }) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return Panics(a.t, f, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that | ||||||
|  | // the recovered panic value equals the expected panic value. | ||||||
|  | // | ||||||
|  | //   a.PanicsWithValue("crazy error", func(){ GoCrazy() }) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return PanicsWithValue(a.t, expected, f, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that | ||||||
|  | // the recovered panic value equals the expected panic value. | ||||||
|  | // | ||||||
|  | //   a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { | ||||||
|  | 	return PanicsWithValuef(a.t, expected, f, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Panicsf asserts that the code inside the specified PanicTestFunc panics. | ||||||
|  | // | ||||||
|  | //   a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) bool { | ||||||
|  | 	return Panicsf(a.t, f, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Regexp asserts that a specified regexp matches a string. | ||||||
|  | // | ||||||
|  | //  a.Regexp(regexp.MustCompile("start"), "it's starting") | ||||||
|  | //  a.Regexp("start...$", "it's not starting") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return Regexp(a.t, rx, str, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Regexpf asserts that a specified regexp matches a string. | ||||||
|  | // | ||||||
|  | //  a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") | ||||||
|  | //  a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return Regexpf(a.t, rx, str, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Subset asserts that the specified list(array, slice...) contains all | ||||||
|  | // elements given in the specified subset(array, slice...). | ||||||
|  | // | ||||||
|  | //    a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return Subset(a.t, list, subset, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Subsetf asserts that the specified list(array, slice...) contains all | ||||||
|  | // elements given in the specified subset(array, slice...). | ||||||
|  | // | ||||||
|  | //    a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return Subsetf(a.t, list, subset, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // True asserts that the specified value is true. | ||||||
|  | // | ||||||
|  | //    a.True(myBool) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return True(a.t, value, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Truef asserts that the specified value is true. | ||||||
|  | // | ||||||
|  | //    a.Truef(myBool, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool { | ||||||
|  | 	return Truef(a.t, value, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WithinDuration asserts that the two times are within duration delta of each other. | ||||||
|  | // | ||||||
|  | //   a.WithinDuration(time.Now(), time.Now(), 10*time.Second) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return WithinDuration(a.t, expected, actual, delta, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WithinDurationf asserts that the two times are within duration delta of each other. | ||||||
|  | // | ||||||
|  | //   a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { | ||||||
|  | 	return WithinDurationf(a.t, expected, actual, delta, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Zero asserts that i is the zero value for its type and returns the truth. | ||||||
|  | func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool { | ||||||
|  | 	return Zero(a.t, i, msgAndArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Zerof asserts that i is the zero value for its type and returns the truth. | ||||||
|  | func (a *Assertions) Zerof(i interface{}, msg string, args ...interface{}) bool { | ||||||
|  | 	return Zerof(a.t, i, msg, args...) | ||||||
|  | } | ||||||
							
								
								
									
										1210
									
								
								vendor/github.com/stretchr/testify/assert/assertions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1210
									
								
								vendor/github.com/stretchr/testify/assert/assertions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										45
									
								
								vendor/github.com/stretchr/testify/assert/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								vendor/github.com/stretchr/testify/assert/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. | ||||||
|  | // | ||||||
|  | // Example Usage | ||||||
|  | // | ||||||
|  | // The following is a complete example using assert in a standard test function: | ||||||
|  | //    import ( | ||||||
|  | //      "testing" | ||||||
|  | //      "github.com/stretchr/testify/assert" | ||||||
|  | //    ) | ||||||
|  | // | ||||||
|  | //    func TestSomething(t *testing.T) { | ||||||
|  | // | ||||||
|  | //      var a string = "Hello" | ||||||
|  | //      var b string = "Hello" | ||||||
|  | // | ||||||
|  | //      assert.Equal(t, a, b, "The two words should be the same.") | ||||||
|  | // | ||||||
|  | //    } | ||||||
|  | // | ||||||
|  | // if you assert many times, use the format below: | ||||||
|  | // | ||||||
|  | //    import ( | ||||||
|  | //      "testing" | ||||||
|  | //      "github.com/stretchr/testify/assert" | ||||||
|  | //    ) | ||||||
|  | // | ||||||
|  | //    func TestSomething(t *testing.T) { | ||||||
|  | //      assert := assert.New(t) | ||||||
|  | // | ||||||
|  | //      var a string = "Hello" | ||||||
|  | //      var b string = "Hello" | ||||||
|  | // | ||||||
|  | //      assert.Equal(a, b, "The two words should be the same.") | ||||||
|  | //    } | ||||||
|  | // | ||||||
|  | // Assertions | ||||||
|  | // | ||||||
|  | // Assertions allow you to easily write test code, and are global funcs in the `assert` package. | ||||||
|  | // All assertion functions take, as the first argument, the `*testing.T` object provided by the | ||||||
|  | // testing framework. This allows the assertion funcs to write the failings and other details to | ||||||
|  | // the correct place. | ||||||
|  | // | ||||||
|  | // Every assertion function also takes an optional string message as the final argument, | ||||||
|  | // allowing custom error messages to be appended to the message the assertion method outputs. | ||||||
|  | package assert | ||||||
							
								
								
									
										10
									
								
								vendor/github.com/stretchr/testify/assert/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/stretchr/testify/assert/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | package assert | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // AnError is an error instance useful for testing.  If the code does not care | ||||||
|  | // about error specifics, and only needs to return the error for example, this | ||||||
|  | // error should be used to make the test code more readable. | ||||||
|  | var AnError = errors.New("assert.AnError general error for testing") | ||||||
							
								
								
									
										16
									
								
								vendor/github.com/stretchr/testify/assert/forward_assertions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								vendor/github.com/stretchr/testify/assert/forward_assertions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | package assert | ||||||
|  |  | ||||||
|  | // Assertions provides assertion methods around the | ||||||
|  | // TestingT interface. | ||||||
|  | type Assertions struct { | ||||||
|  | 	t TestingT | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // New makes a new Assertions object for the specified TestingT. | ||||||
|  | func New(t TestingT) *Assertions { | ||||||
|  | 	return &Assertions{ | ||||||
|  | 		t: t, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs | ||||||
							
								
								
									
										127
									
								
								vendor/github.com/stretchr/testify/assert/http_assertions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								vendor/github.com/stretchr/testify/assert/http_assertions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | |||||||
|  | package assert | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/http/httptest" | ||||||
|  | 	"net/url" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // httpCode is a helper that returns HTTP code of the response. It returns -1 and | ||||||
|  | // 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) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return -1, err | ||||||
|  | 	} | ||||||
|  | 	handler(w, req) | ||||||
|  | 	return w.Code, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // HTTPSuccess asserts that a specified handler returns a success status code. | ||||||
|  | // | ||||||
|  | //  assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { | ||||||
|  | 	code, err := httpCode(handler, method, url, values) | ||||||
|  | 	if err != nil { | ||||||
|  | 		Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent | ||||||
|  | 	if !isSuccessCode { | ||||||
|  | 		Fail(t, fmt.Sprintf("Expected HTTP success status code for %q but received %d", url+"?"+values.Encode(), code)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return isSuccessCode | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // HTTPRedirect asserts that a specified handler returns a redirect status code. | ||||||
|  | // | ||||||
|  | //  assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { | ||||||
|  | 	code, err := httpCode(handler, method, url, values) | ||||||
|  | 	if err != nil { | ||||||
|  | 		Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect | ||||||
|  | 	if !isRedirectCode { | ||||||
|  | 		Fail(t, fmt.Sprintf("Expected HTTP redirect status code for %q but received %d", url+"?"+values.Encode(), code)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return isRedirectCode | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // HTTPError asserts that a specified handler returns an error status code. | ||||||
|  | // | ||||||
|  | //  assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||||
|  | // | ||||||
|  | // Returns whether the assertion was successful (true) or not (false). | ||||||
|  | func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { | ||||||
|  | 	code, err := httpCode(handler, method, url, values) | ||||||
|  | 	if err != nil { | ||||||
|  | 		Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	isErrorCode := code >= http.StatusBadRequest | ||||||
|  | 	if !isErrorCode { | ||||||
|  | 		Fail(t, fmt.Sprintf("Expected HTTP error status code for %q but received %d", url+"?"+values.Encode(), code)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return isErrorCode | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // HTTPBody is a helper that returns HTTP body of the response. It returns | ||||||
|  | // empty string if building a new request fails. | ||||||
|  | func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { | ||||||
|  | 	w := httptest.NewRecorder() | ||||||
|  | 	req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  | 	handler(w, req) | ||||||
|  | 	return w.Body.String() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 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") | ||||||
|  | // | ||||||
|  | // 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{}) bool { | ||||||
|  | 	body := HTTPBody(handler, method, url, values) | ||||||
|  |  | ||||||
|  | 	contains := strings.Contains(body, fmt.Sprint(str)) | ||||||
|  | 	if !contains { | ||||||
|  | 		Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return contains | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 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") | ||||||
|  | // | ||||||
|  | // 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{}) bool { | ||||||
|  | 	body := HTTPBody(handler, method, url, values) | ||||||
|  |  | ||||||
|  | 	contains := strings.Contains(body, fmt.Sprint(str)) | ||||||
|  | 	if contains { | ||||||
|  | 		Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return !contains | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/LICENCE.txt
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/LICENCE.txt
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell | ||||||
|  |  | ||||||
|  | Please consider promoting this project if you find it useful. | ||||||
|  |  | ||||||
|  | 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. | ||||||
							
								
								
									
										22
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell | ||||||
|  |  | ||||||
|  | Please consider promoting this project if you find it useful. | ||||||
|  |  | ||||||
|  | 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. | ||||||
							
								
								
									
										152
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/bypass.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/bypass.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | |||||||
|  | // Copyright (c) 2015 Dave Collins <dave@davec.name> | ||||||
|  | // | ||||||
|  | // Permission to use, copy, modify, and 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. | ||||||
|  | // | ||||||
|  | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||||
|  | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||||
|  | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||||
|  | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||||
|  | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||||
|  | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||||
|  | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||||
|  |  | ||||||
|  | // NOTE: Due to the following build constraints, this file will only be compiled | ||||||
|  | // 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 | ||||||
|  |  | ||||||
|  | package spew | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"reflect" | ||||||
|  | 	"unsafe" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	// UnsafeDisabled is a build-time constant which specifies whether or | ||||||
|  | 	// not access to the unsafe package is available. | ||||||
|  | 	UnsafeDisabled = false | ||||||
|  |  | ||||||
|  | 	// ptrSize is the size of a pointer on the current arch. | ||||||
|  | 	ptrSize = unsafe.Sizeof((*byte)(nil)) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | 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) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 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<<flagKindWidth - 1) << flagKindShift) | ||||||
|  | 	if (upfv&flagKindMask)>>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 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // unsafeReflectValue converts the passed reflect.Value into a one that bypasses | ||||||
|  | // the typical safety restrictions preventing access to unaddressable and | ||||||
|  | // unexported data.  It works by digging the raw pointer to the underlying | ||||||
|  | // value out of the protected value and generating a new unprotected (unsafe) | ||||||
|  | // reflect.Value to it. | ||||||
|  | // | ||||||
|  | // 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) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pv := reflect.NewAt(vt, upv) | ||||||
|  | 	rv = pv | ||||||
|  | 	for i := 0; i < indirects; i++ { | ||||||
|  | 		rv = rv.Elem() | ||||||
|  | 	} | ||||||
|  | 	return rv | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | // Copyright (c) 2015 Dave Collins <dave@davec.name> | ||||||
|  | // | ||||||
|  | // Permission to use, copy, modify, and 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. | ||||||
|  | // | ||||||
|  | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||||
|  | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||||
|  | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||||
|  | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||||
|  | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||||
|  | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||||
|  | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||||
|  |  | ||||||
|  | // NOTE: Due to the following build constraints, this file will only be compiled | ||||||
|  | // 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 | ||||||
|  |  | ||||||
|  | package spew | ||||||
|  |  | ||||||
|  | import "reflect" | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	// UnsafeDisabled is a build-time constant which specifies whether or | ||||||
|  | 	// not access to the unsafe package is available. | ||||||
|  | 	UnsafeDisabled = true | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // unsafeReflectValue typically converts the passed reflect.Value into a one | ||||||
|  | // that bypasses the typical safety restrictions preventing access to | ||||||
|  | // unaddressable and unexported data.  However, doing this relies on access to | ||||||
|  | // the unsafe package.  This is a stub version which simply returns the passed | ||||||
|  | // reflect.Value when the unsafe package is not available. | ||||||
|  | func unsafeReflectValue(v reflect.Value) reflect.Value { | ||||||
|  | 	return v | ||||||
|  | } | ||||||
							
								
								
									
										341
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/common.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										341
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/common.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,341 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2013 Dave Collins <dave@davec.name> | ||||||
|  |  * | ||||||
|  |  * Permission to use, copy, modify, and 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. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||||
|  |  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||||
|  |  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||||
|  |  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||||
|  |  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||||
|  |  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||||
|  |  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package spew | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"reflect" | ||||||
|  | 	"sort" | ||||||
|  | 	"strconv" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Some constants in the form of bytes to avoid string overhead.  This mirrors | ||||||
|  | // the technique used in the fmt package. | ||||||
|  | var ( | ||||||
|  | 	panicBytes            = []byte("(PANIC=") | ||||||
|  | 	plusBytes             = []byte("+") | ||||||
|  | 	iBytes                = []byte("i") | ||||||
|  | 	trueBytes             = []byte("true") | ||||||
|  | 	falseBytes            = []byte("false") | ||||||
|  | 	interfaceBytes        = []byte("(interface {})") | ||||||
|  | 	commaNewlineBytes     = []byte(",\n") | ||||||
|  | 	newlineBytes          = []byte("\n") | ||||||
|  | 	openBraceBytes        = []byte("{") | ||||||
|  | 	openBraceNewlineBytes = []byte("{\n") | ||||||
|  | 	closeBraceBytes       = []byte("}") | ||||||
|  | 	asteriskBytes         = []byte("*") | ||||||
|  | 	colonBytes            = []byte(":") | ||||||
|  | 	colonSpaceBytes       = []byte(": ") | ||||||
|  | 	openParenBytes        = []byte("(") | ||||||
|  | 	closeParenBytes       = []byte(")") | ||||||
|  | 	spaceBytes            = []byte(" ") | ||||||
|  | 	pointerChainBytes     = []byte("->") | ||||||
|  | 	nilAngleBytes         = []byte("<nil>") | ||||||
|  | 	maxNewlineBytes       = []byte("<max depth reached>\n") | ||||||
|  | 	maxShortBytes         = []byte("<max>") | ||||||
|  | 	circularBytes         = []byte("<already shown>") | ||||||
|  | 	circularShortBytes    = []byte("<shown>") | ||||||
|  | 	invalidAngleBytes     = []byte("<invalid>") | ||||||
|  | 	openBracketBytes      = []byte("[") | ||||||
|  | 	closeBracketBytes     = []byte("]") | ||||||
|  | 	percentBytes          = []byte("%") | ||||||
|  | 	precisionBytes        = []byte(".") | ||||||
|  | 	openAngleBytes        = []byte("<") | ||||||
|  | 	closeAngleBytes       = []byte(">") | ||||||
|  | 	openMapBytes          = []byte("map[") | ||||||
|  | 	closeMapBytes         = []byte("]") | ||||||
|  | 	lenEqualsBytes        = []byte("len=") | ||||||
|  | 	capEqualsBytes        = []byte("cap=") | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // hexDigits is used to map a decimal value to a hex digit. | ||||||
|  | var hexDigits = "0123456789abcdef" | ||||||
|  |  | ||||||
|  | // catchPanic handles any panics that might occur during the handleMethods | ||||||
|  | // calls. | ||||||
|  | func catchPanic(w io.Writer, v reflect.Value) { | ||||||
|  | 	if err := recover(); err != nil { | ||||||
|  | 		w.Write(panicBytes) | ||||||
|  | 		fmt.Fprintf(w, "%v", err) | ||||||
|  | 		w.Write(closeParenBytes) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // handleMethods attempts to call the Error and String methods on the underlying | ||||||
|  | // type the passed reflect.Value represents and outputes the result to Writer w. | ||||||
|  | // | ||||||
|  | // It handles panics in any called methods by catching and displaying the error | ||||||
|  | // as the formatted value. | ||||||
|  | func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) { | ||||||
|  | 	// We need an interface to check if the type implements the error or | ||||||
|  | 	// Stringer interface.  However, the reflect package won't give us an | ||||||
|  | 	// interface on certain things like unexported struct fields in order | ||||||
|  | 	// to enforce visibility rules.  We use unsafe, when it's available, | ||||||
|  | 	// to bypass these restrictions since this package does not mutate the | ||||||
|  | 	// values. | ||||||
|  | 	if !v.CanInterface() { | ||||||
|  | 		if UnsafeDisabled { | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		v = unsafeReflectValue(v) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Choose whether or not to do error and Stringer interface lookups against | ||||||
|  | 	// the base type or a pointer to the base type depending on settings. | ||||||
|  | 	// Technically calling one of these methods with a pointer receiver can | ||||||
|  | 	// mutate the value, however, types which choose to satisify an error or | ||||||
|  | 	// Stringer interface with a pointer receiver should not be mutating their | ||||||
|  | 	// state inside these interface methods. | ||||||
|  | 	if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() { | ||||||
|  | 		v = unsafeReflectValue(v) | ||||||
|  | 	} | ||||||
|  | 	if v.CanAddr() { | ||||||
|  | 		v = v.Addr() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Is it an error or Stringer? | ||||||
|  | 	switch iface := v.Interface().(type) { | ||||||
|  | 	case error: | ||||||
|  | 		defer catchPanic(w, v) | ||||||
|  | 		if cs.ContinueOnMethod { | ||||||
|  | 			w.Write(openParenBytes) | ||||||
|  | 			w.Write([]byte(iface.Error())) | ||||||
|  | 			w.Write(closeParenBytes) | ||||||
|  | 			w.Write(spaceBytes) | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		w.Write([]byte(iface.Error())) | ||||||
|  | 		return true | ||||||
|  |  | ||||||
|  | 	case fmt.Stringer: | ||||||
|  | 		defer catchPanic(w, v) | ||||||
|  | 		if cs.ContinueOnMethod { | ||||||
|  | 			w.Write(openParenBytes) | ||||||
|  | 			w.Write([]byte(iface.String())) | ||||||
|  | 			w.Write(closeParenBytes) | ||||||
|  | 			w.Write(spaceBytes) | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 		w.Write([]byte(iface.String())) | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // printBool outputs a boolean value as true or false to Writer w. | ||||||
|  | func printBool(w io.Writer, val bool) { | ||||||
|  | 	if val { | ||||||
|  | 		w.Write(trueBytes) | ||||||
|  | 	} else { | ||||||
|  | 		w.Write(falseBytes) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // printInt outputs a signed integer value to Writer w. | ||||||
|  | func printInt(w io.Writer, val int64, base int) { | ||||||
|  | 	w.Write([]byte(strconv.FormatInt(val, base))) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // printUint outputs an unsigned integer value to Writer w. | ||||||
|  | func printUint(w io.Writer, val uint64, base int) { | ||||||
|  | 	w.Write([]byte(strconv.FormatUint(val, base))) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // printFloat outputs a floating point value using the specified precision, | ||||||
|  | // which is expected to be 32 or 64bit, to Writer w. | ||||||
|  | func printFloat(w io.Writer, val float64, precision int) { | ||||||
|  | 	w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision))) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // printComplex outputs a complex value using the specified float precision | ||||||
|  | // for the real and imaginary parts to Writer w. | ||||||
|  | func printComplex(w io.Writer, c complex128, floatPrecision int) { | ||||||
|  | 	r := real(c) | ||||||
|  | 	w.Write(openParenBytes) | ||||||
|  | 	w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision))) | ||||||
|  | 	i := imag(c) | ||||||
|  | 	if i >= 0 { | ||||||
|  | 		w.Write(plusBytes) | ||||||
|  | 	} | ||||||
|  | 	w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision))) | ||||||
|  | 	w.Write(iBytes) | ||||||
|  | 	w.Write(closeParenBytes) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x' | ||||||
|  | // prefix to Writer w. | ||||||
|  | func printHexPtr(w io.Writer, p uintptr) { | ||||||
|  | 	// Null pointer. | ||||||
|  | 	num := uint64(p) | ||||||
|  | 	if num == 0 { | ||||||
|  | 		w.Write(nilAngleBytes) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix | ||||||
|  | 	buf := make([]byte, 18) | ||||||
|  |  | ||||||
|  | 	// It's simpler to construct the hex string right to left. | ||||||
|  | 	base := uint64(16) | ||||||
|  | 	i := len(buf) - 1 | ||||||
|  | 	for num >= base { | ||||||
|  | 		buf[i] = hexDigits[num%base] | ||||||
|  | 		num /= base | ||||||
|  | 		i-- | ||||||
|  | 	} | ||||||
|  | 	buf[i] = hexDigits[num] | ||||||
|  |  | ||||||
|  | 	// Add '0x' prefix. | ||||||
|  | 	i-- | ||||||
|  | 	buf[i] = 'x' | ||||||
|  | 	i-- | ||||||
|  | 	buf[i] = '0' | ||||||
|  |  | ||||||
|  | 	// Strip unused leading bytes. | ||||||
|  | 	buf = buf[i:] | ||||||
|  | 	w.Write(buf) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // valuesSorter implements sort.Interface to allow a slice of reflect.Value | ||||||
|  | // elements to be sorted. | ||||||
|  | type valuesSorter struct { | ||||||
|  | 	values  []reflect.Value | ||||||
|  | 	strings []string // either nil or same len and values | ||||||
|  | 	cs      *ConfigState | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // newValuesSorter initializes a valuesSorter instance, which holds a set of | ||||||
|  | // surrogate keys on which the data should be sorted.  It uses flags in | ||||||
|  | // ConfigState to decide if and how to populate those surrogate keys. | ||||||
|  | func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface { | ||||||
|  | 	vs := &valuesSorter{values: values, cs: cs} | ||||||
|  | 	if canSortSimply(vs.values[0].Kind()) { | ||||||
|  | 		return vs | ||||||
|  | 	} | ||||||
|  | 	if !cs.DisableMethods { | ||||||
|  | 		vs.strings = make([]string, len(values)) | ||||||
|  | 		for i := range vs.values { | ||||||
|  | 			b := bytes.Buffer{} | ||||||
|  | 			if !handleMethods(cs, &b, vs.values[i]) { | ||||||
|  | 				vs.strings = nil | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 			vs.strings[i] = b.String() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if vs.strings == nil && cs.SpewKeys { | ||||||
|  | 		vs.strings = make([]string, len(values)) | ||||||
|  | 		for i := range vs.values { | ||||||
|  | 			vs.strings[i] = Sprintf("%#v", vs.values[i].Interface()) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return vs | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // canSortSimply tests whether a reflect.Kind is a primitive that can be sorted | ||||||
|  | // directly, or whether it should be considered for sorting by surrogate keys | ||||||
|  | // (if the ConfigState allows it). | ||||||
|  | func canSortSimply(kind reflect.Kind) bool { | ||||||
|  | 	// This switch parallels valueSortLess, except for the default case. | ||||||
|  | 	switch kind { | ||||||
|  | 	case reflect.Bool: | ||||||
|  | 		return true | ||||||
|  | 	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: | ||||||
|  | 		return true | ||||||
|  | 	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: | ||||||
|  | 		return true | ||||||
|  | 	case reflect.Float32, reflect.Float64: | ||||||
|  | 		return true | ||||||
|  | 	case reflect.String: | ||||||
|  | 		return true | ||||||
|  | 	case reflect.Uintptr: | ||||||
|  | 		return true | ||||||
|  | 	case reflect.Array: | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Len returns the number of values in the slice.  It is part of the | ||||||
|  | // sort.Interface implementation. | ||||||
|  | func (s *valuesSorter) Len() int { | ||||||
|  | 	return len(s.values) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Swap swaps the values at the passed indices.  It is part of the | ||||||
|  | // sort.Interface implementation. | ||||||
|  | func (s *valuesSorter) Swap(i, j int) { | ||||||
|  | 	s.values[i], s.values[j] = s.values[j], s.values[i] | ||||||
|  | 	if s.strings != nil { | ||||||
|  | 		s.strings[i], s.strings[j] = s.strings[j], s.strings[i] | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // valueSortLess returns whether the first value should sort before the second | ||||||
|  | // value.  It is used by valueSorter.Less as part of the sort.Interface | ||||||
|  | // implementation. | ||||||
|  | func valueSortLess(a, b reflect.Value) bool { | ||||||
|  | 	switch a.Kind() { | ||||||
|  | 	case reflect.Bool: | ||||||
|  | 		return !a.Bool() && b.Bool() | ||||||
|  | 	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: | ||||||
|  | 		return a.Int() < b.Int() | ||||||
|  | 	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: | ||||||
|  | 		return a.Uint() < b.Uint() | ||||||
|  | 	case reflect.Float32, reflect.Float64: | ||||||
|  | 		return a.Float() < b.Float() | ||||||
|  | 	case reflect.String: | ||||||
|  | 		return a.String() < b.String() | ||||||
|  | 	case reflect.Uintptr: | ||||||
|  | 		return a.Uint() < b.Uint() | ||||||
|  | 	case reflect.Array: | ||||||
|  | 		// Compare the contents of both arrays. | ||||||
|  | 		l := a.Len() | ||||||
|  | 		for i := 0; i < l; i++ { | ||||||
|  | 			av := a.Index(i) | ||||||
|  | 			bv := b.Index(i) | ||||||
|  | 			if av.Interface() == bv.Interface() { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			return valueSortLess(av, bv) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return a.String() < b.String() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Less returns whether the value at index i should sort before the | ||||||
|  | // value at index j.  It is part of the sort.Interface implementation. | ||||||
|  | func (s *valuesSorter) Less(i, j int) bool { | ||||||
|  | 	if s.strings == nil { | ||||||
|  | 		return valueSortLess(s.values[i], s.values[j]) | ||||||
|  | 	} | ||||||
|  | 	return s.strings[i] < s.strings[j] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // sortValues is a sort function that handles both native types and any type that | ||||||
|  | // can be converted to error or Stringer.  Other inputs are sorted according to | ||||||
|  | // their Value.String() value to ensure display stability. | ||||||
|  | func sortValues(values []reflect.Value, cs *ConfigState) { | ||||||
|  | 	if len(values) == 0 { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	sort.Sort(newValuesSorter(values, cs)) | ||||||
|  | } | ||||||
							
								
								
									
										306
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/config.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										306
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/config.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,306 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2013 Dave Collins <dave@davec.name> | ||||||
|  |  * | ||||||
|  |  * Permission to use, copy, modify, and 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. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||||
|  |  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||||
|  |  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||||
|  |  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||||
|  |  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||||
|  |  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||||
|  |  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package spew | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"os" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // ConfigState houses the configuration options used by spew to format and | ||||||
|  | // display values.  There is a global instance, Config, that is used to control | ||||||
|  | // all top-level Formatter and Dump functionality.  Each ConfigState instance | ||||||
|  | // provides methods equivalent to the top-level functions. | ||||||
|  | // | ||||||
|  | // The zero value for ConfigState provides no indentation.  You would typically | ||||||
|  | // want to set it to a space or a tab. | ||||||
|  | // | ||||||
|  | // Alternatively, you can use NewDefaultConfig to get a ConfigState instance | ||||||
|  | // with default settings.  See the documentation of NewDefaultConfig for default | ||||||
|  | // values. | ||||||
|  | type ConfigState struct { | ||||||
|  | 	// Indent specifies the string to use for each indentation level.  The | ||||||
|  | 	// global config instance that all top-level functions use set this to a | ||||||
|  | 	// single space by default.  If you would like more indentation, you might | ||||||
|  | 	// set this to a tab with "\t" or perhaps two spaces with "  ". | ||||||
|  | 	Indent string | ||||||
|  |  | ||||||
|  | 	// MaxDepth controls the maximum number of levels to descend into nested | ||||||
|  | 	// data structures.  The default, 0, means there is no limit. | ||||||
|  | 	// | ||||||
|  | 	// NOTE: Circular data structures are properly detected, so it is not | ||||||
|  | 	// necessary to set this value unless you specifically want to limit deeply | ||||||
|  | 	// nested data structures. | ||||||
|  | 	MaxDepth int | ||||||
|  |  | ||||||
|  | 	// DisableMethods specifies whether or not error and Stringer interfaces are | ||||||
|  | 	// invoked for types that implement them. | ||||||
|  | 	DisableMethods bool | ||||||
|  |  | ||||||
|  | 	// DisablePointerMethods specifies whether or not to check for and invoke | ||||||
|  | 	// error and Stringer interfaces on types which only accept a pointer | ||||||
|  | 	// receiver when the current type is not a pointer. | ||||||
|  | 	// | ||||||
|  | 	// NOTE: This might be an unsafe action since calling one of these methods | ||||||
|  | 	// with a pointer receiver could technically mutate the value, however, | ||||||
|  | 	// in practice, types which choose to satisify an error or Stringer | ||||||
|  | 	// interface with a pointer receiver should not be mutating their state | ||||||
|  | 	// inside these interface methods.  As a result, this option relies on | ||||||
|  | 	// access to the unsafe package, so it will not have any effect when | ||||||
|  | 	// running in environments without access to the unsafe package such as | ||||||
|  | 	// Google App Engine or with the "safe" build tag specified. | ||||||
|  | 	DisablePointerMethods bool | ||||||
|  |  | ||||||
|  | 	// DisablePointerAddresses specifies whether to disable the printing of | ||||||
|  | 	// pointer addresses. This is useful when diffing data structures in tests. | ||||||
|  | 	DisablePointerAddresses bool | ||||||
|  |  | ||||||
|  | 	// DisableCapacities specifies whether to disable the printing of capacities | ||||||
|  | 	// for arrays, slices, maps and channels. This is useful when diffing | ||||||
|  | 	// data structures in tests. | ||||||
|  | 	DisableCapacities bool | ||||||
|  |  | ||||||
|  | 	// ContinueOnMethod specifies whether or not recursion should continue once | ||||||
|  | 	// a custom error or Stringer interface is invoked.  The default, false, | ||||||
|  | 	// means it will print the results of invoking the custom error or Stringer | ||||||
|  | 	// interface and return immediately instead of continuing to recurse into | ||||||
|  | 	// the internals of the data type. | ||||||
|  | 	// | ||||||
|  | 	// NOTE: This flag does not have any effect if method invocation is disabled | ||||||
|  | 	// via the DisableMethods or DisablePointerMethods options. | ||||||
|  | 	ContinueOnMethod bool | ||||||
|  |  | ||||||
|  | 	// SortKeys specifies map keys should be sorted before being printed. Use | ||||||
|  | 	// this to have a more deterministic, diffable output.  Note that only | ||||||
|  | 	// native types (bool, int, uint, floats, uintptr and string) and types | ||||||
|  | 	// that support the error or Stringer interfaces (if methods are | ||||||
|  | 	// enabled) are supported, with other types sorted according to the | ||||||
|  | 	// reflect.Value.String() output which guarantees display stability. | ||||||
|  | 	SortKeys bool | ||||||
|  |  | ||||||
|  | 	// SpewKeys specifies that, as a last resort attempt, map keys should | ||||||
|  | 	// be spewed to strings and sorted by those strings.  This is only | ||||||
|  | 	// considered if SortKeys is true. | ||||||
|  | 	SpewKeys bool | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Config is the active configuration of the top-level functions. | ||||||
|  | // The configuration can be changed by modifying the contents of spew.Config. | ||||||
|  | var Config = ConfigState{Indent: " "} | ||||||
|  |  | ||||||
|  | // Errorf is a wrapper for fmt.Errorf that treats each argument as if it were | ||||||
|  | // passed with a Formatter interface returned by c.NewFormatter.  It returns | ||||||
|  | // the formatted string as a value that satisfies error.  See NewFormatter | ||||||
|  | // for formatting details. | ||||||
|  | // | ||||||
|  | // This function is shorthand for the following syntax: | ||||||
|  | // | ||||||
|  | //	fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b)) | ||||||
|  | func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) { | ||||||
|  | 	return fmt.Errorf(format, c.convertArgs(a)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Fprint is a wrapper for fmt.Fprint that treats each argument as if it were | ||||||
|  | // passed with a Formatter interface returned by c.NewFormatter.  It returns | ||||||
|  | // the number of bytes written and any write error encountered.  See | ||||||
|  | // NewFormatter for formatting details. | ||||||
|  | // | ||||||
|  | // This function is shorthand for the following syntax: | ||||||
|  | // | ||||||
|  | //	fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b)) | ||||||
|  | func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) { | ||||||
|  | 	return fmt.Fprint(w, c.convertArgs(a)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were | ||||||
|  | // passed with a Formatter interface returned by c.NewFormatter.  It returns | ||||||
|  | // the number of bytes written and any write error encountered.  See | ||||||
|  | // NewFormatter for formatting details. | ||||||
|  | // | ||||||
|  | // This function is shorthand for the following syntax: | ||||||
|  | // | ||||||
|  | //	fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b)) | ||||||
|  | func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { | ||||||
|  | 	return fmt.Fprintf(w, format, c.convertArgs(a)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it | ||||||
|  | // passed with a Formatter interface returned by c.NewFormatter.  See | ||||||
|  | // NewFormatter for formatting details. | ||||||
|  | // | ||||||
|  | // This function is shorthand for the following syntax: | ||||||
|  | // | ||||||
|  | //	fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b)) | ||||||
|  | func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { | ||||||
|  | 	return fmt.Fprintln(w, c.convertArgs(a)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Print is a wrapper for fmt.Print that treats each argument as if it were | ||||||
|  | // passed with a Formatter interface returned by c.NewFormatter.  It returns | ||||||
|  | // the number of bytes written and any write error encountered.  See | ||||||
|  | // NewFormatter for formatting details. | ||||||
|  | // | ||||||
|  | // This function is shorthand for the following syntax: | ||||||
|  | // | ||||||
|  | //	fmt.Print(c.NewFormatter(a), c.NewFormatter(b)) | ||||||
|  | func (c *ConfigState) Print(a ...interface{}) (n int, err error) { | ||||||
|  | 	return fmt.Print(c.convertArgs(a)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Printf is a wrapper for fmt.Printf that treats each argument as if it were | ||||||
|  | // passed with a Formatter interface returned by c.NewFormatter.  It returns | ||||||
|  | // the number of bytes written and any write error encountered.  See | ||||||
|  | // NewFormatter for formatting details. | ||||||
|  | // | ||||||
|  | // This function is shorthand for the following syntax: | ||||||
|  | // | ||||||
|  | //	fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b)) | ||||||
|  | func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) { | ||||||
|  | 	return fmt.Printf(format, c.convertArgs(a)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Println is a wrapper for fmt.Println that treats each argument as if it were | ||||||
|  | // passed with a Formatter interface returned by c.NewFormatter.  It returns | ||||||
|  | // the number of bytes written and any write error encountered.  See | ||||||
|  | // NewFormatter for formatting details. | ||||||
|  | // | ||||||
|  | // This function is shorthand for the following syntax: | ||||||
|  | // | ||||||
|  | //	fmt.Println(c.NewFormatter(a), c.NewFormatter(b)) | ||||||
|  | func (c *ConfigState) Println(a ...interface{}) (n int, err error) { | ||||||
|  | 	return fmt.Println(c.convertArgs(a)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Sprint is a wrapper for fmt.Sprint that treats each argument as if it were | ||||||
|  | // passed with a Formatter interface returned by c.NewFormatter.  It returns | ||||||
|  | // the resulting string.  See NewFormatter for formatting details. | ||||||
|  | // | ||||||
|  | // This function is shorthand for the following syntax: | ||||||
|  | // | ||||||
|  | //	fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b)) | ||||||
|  | func (c *ConfigState) Sprint(a ...interface{}) string { | ||||||
|  | 	return fmt.Sprint(c.convertArgs(a)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were | ||||||
|  | // passed with a Formatter interface returned by c.NewFormatter.  It returns | ||||||
|  | // the resulting string.  See NewFormatter for formatting details. | ||||||
|  | // | ||||||
|  | // This function is shorthand for the following syntax: | ||||||
|  | // | ||||||
|  | //	fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b)) | ||||||
|  | func (c *ConfigState) Sprintf(format string, a ...interface{}) string { | ||||||
|  | 	return fmt.Sprintf(format, c.convertArgs(a)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it | ||||||
|  | // were passed with a Formatter interface returned by c.NewFormatter.  It | ||||||
|  | // returns the resulting string.  See NewFormatter for formatting details. | ||||||
|  | // | ||||||
|  | // This function is shorthand for the following syntax: | ||||||
|  | // | ||||||
|  | //	fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b)) | ||||||
|  | func (c *ConfigState) Sprintln(a ...interface{}) string { | ||||||
|  | 	return fmt.Sprintln(c.convertArgs(a)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | NewFormatter returns a custom formatter that satisfies the fmt.Formatter | ||||||
|  | interface.  As a result, it integrates cleanly with standard fmt package | ||||||
|  | printing functions.  The formatter is useful for inline printing of smaller data | ||||||
|  | types similar to the standard %v format specifier. | ||||||
|  |  | ||||||
|  | The custom formatter only responds to the %v (most compact), %+v (adds pointer | ||||||
|  | addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb | ||||||
|  | combinations.  Any other verbs such as %x and %q will be sent to the the | ||||||
|  | standard fmt package for formatting.  In addition, the custom formatter ignores | ||||||
|  | the width and precision arguments (however they will still work on the format | ||||||
|  | specifiers not handled by the custom formatter). | ||||||
|  |  | ||||||
|  | Typically this function shouldn't be called directly.  It is much easier to make | ||||||
|  | use of the custom formatter by calling one of the convenience functions such as | ||||||
|  | c.Printf, c.Println, or c.Printf. | ||||||
|  | */ | ||||||
|  | func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter { | ||||||
|  | 	return newFormatter(c, v) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Fdump formats and displays the passed arguments to io.Writer w.  It formats | ||||||
|  | // exactly the same as Dump. | ||||||
|  | func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) { | ||||||
|  | 	fdump(c, w, a...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | Dump displays the passed parameters to standard out with newlines, customizable | ||||||
|  | indentation, and additional debug information such as complete types and all | ||||||
|  | pointer addresses used to indirect to the final value.  It provides the | ||||||
|  | following features over the built-in printing facilities provided by the fmt | ||||||
|  | package: | ||||||
|  |  | ||||||
|  | 	* Pointers are dereferenced and followed | ||||||
|  | 	* Circular data structures are detected and handled properly | ||||||
|  | 	* Custom Stringer/error interfaces are optionally invoked, including | ||||||
|  | 	  on unexported types | ||||||
|  | 	* Custom types which only implement the Stringer/error interfaces via | ||||||
|  | 	  a pointer receiver are optionally invoked when passing non-pointer | ||||||
|  | 	  variables | ||||||
|  | 	* Byte arrays and slices are dumped like the hexdump -C command which | ||||||
|  | 	  includes offsets, byte values in hex, and ASCII output | ||||||
|  |  | ||||||
|  | The configuration options are controlled by modifying the public members | ||||||
|  | of c.  See ConfigState for options documentation. | ||||||
|  |  | ||||||
|  | See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to | ||||||
|  | get the formatted result as a string. | ||||||
|  | */ | ||||||
|  | func (c *ConfigState) Dump(a ...interface{}) { | ||||||
|  | 	fdump(c, os.Stdout, a...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Sdump returns a string with the passed arguments formatted exactly the same | ||||||
|  | // as Dump. | ||||||
|  | func (c *ConfigState) Sdump(a ...interface{}) string { | ||||||
|  | 	var buf bytes.Buffer | ||||||
|  | 	fdump(c, &buf, a...) | ||||||
|  | 	return buf.String() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // convertArgs accepts a slice of arguments and returns a slice of the same | ||||||
|  | // length with each argument converted to a spew Formatter interface using | ||||||
|  | // the ConfigState associated with s. | ||||||
|  | func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) { | ||||||
|  | 	formatters = make([]interface{}, len(args)) | ||||||
|  | 	for index, arg := range args { | ||||||
|  | 		formatters[index] = newFormatter(c, arg) | ||||||
|  | 	} | ||||||
|  | 	return formatters | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewDefaultConfig returns a ConfigState with the following default settings. | ||||||
|  | // | ||||||
|  | // 	Indent: " " | ||||||
|  | // 	MaxDepth: 0 | ||||||
|  | // 	DisableMethods: false | ||||||
|  | // 	DisablePointerMethods: false | ||||||
|  | // 	ContinueOnMethod: false | ||||||
|  | // 	SortKeys: false | ||||||
|  | func NewDefaultConfig() *ConfigState { | ||||||
|  | 	return &ConfigState{Indent: " "} | ||||||
|  | } | ||||||
							
								
								
									
										202
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,202 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2013 Dave Collins <dave@davec.name> | ||||||
|  |  * | ||||||
|  |  * Permission to use, copy, modify, and 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. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||||
|  |  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||||
|  |  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||||
|  |  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||||
|  |  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||||
|  |  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||||
|  |  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | Package spew implements a deep pretty printer for Go data structures to aid in | ||||||
|  | debugging. | ||||||
|  |  | ||||||
|  | A quick overview of the additional features spew provides over the built-in | ||||||
|  | printing facilities for Go data types are as follows: | ||||||
|  |  | ||||||
|  | 	* Pointers are dereferenced and followed | ||||||
|  | 	* Circular data structures are detected and handled properly | ||||||
|  | 	* Custom Stringer/error interfaces are optionally invoked, including | ||||||
|  | 	  on unexported types | ||||||
|  | 	* Custom types which only implement the Stringer/error interfaces via | ||||||
|  | 	  a pointer receiver are optionally invoked when passing non-pointer | ||||||
|  | 	  variables | ||||||
|  | 	* Byte arrays and slices are dumped like the hexdump -C command which | ||||||
|  | 	  includes offsets, byte values in hex, and ASCII output (only when using | ||||||
|  | 	  Dump style) | ||||||
|  |  | ||||||
|  | There are two different approaches spew allows for dumping Go data structures: | ||||||
|  |  | ||||||
|  | 	* Dump style which prints with newlines, customizable indentation, | ||||||
|  | 	  and additional debug information such as types and all pointer addresses | ||||||
|  | 	  used to indirect to the final value | ||||||
|  | 	* A custom Formatter interface that integrates cleanly with the standard fmt | ||||||
|  | 	  package and replaces %v, %+v, %#v, and %#+v to provide inline printing | ||||||
|  | 	  similar to the default %v while providing the additional functionality | ||||||
|  | 	  outlined above and passing unsupported format verbs such as %x and %q | ||||||
|  | 	  along to fmt | ||||||
|  |  | ||||||
|  | Quick Start | ||||||
|  |  | ||||||
|  | This section demonstrates how to quickly get started with spew.  See the | ||||||
|  | sections below for further details on formatting and configuration options. | ||||||
|  |  | ||||||
|  | To dump a variable with full newlines, indentation, type, and pointer | ||||||
|  | information use Dump, Fdump, or Sdump: | ||||||
|  | 	spew.Dump(myVar1, myVar2, ...) | ||||||
|  | 	spew.Fdump(someWriter, myVar1, myVar2, ...) | ||||||
|  | 	str := spew.Sdump(myVar1, myVar2, ...) | ||||||
|  |  | ||||||
|  | Alternatively, if you would prefer to use format strings with a compacted inline | ||||||
|  | printing style, use the convenience wrappers Printf, Fprintf, etc with | ||||||
|  | %v (most compact), %+v (adds pointer addresses), %#v (adds types), or | ||||||
|  | %#+v (adds types and pointer addresses): | ||||||
|  | 	spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) | ||||||
|  | 	spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) | ||||||
|  | 	spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) | ||||||
|  | 	spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) | ||||||
|  |  | ||||||
|  | Configuration Options | ||||||
|  |  | ||||||
|  | Configuration of spew is handled by fields in the ConfigState type.  For | ||||||
|  | convenience, all of the top-level functions use a global state available | ||||||
|  | via the spew.Config global. | ||||||
|  |  | ||||||
|  | It is also possible to create a ConfigState instance that provides methods | ||||||
|  | equivalent to the top-level functions.  This allows concurrent configuration | ||||||
|  | options.  See the ConfigState documentation for more details. | ||||||
|  |  | ||||||
|  | The following configuration options are available: | ||||||
|  | 	* Indent | ||||||
|  | 		String to use for each indentation level for Dump functions. | ||||||
|  | 		It is a single space by default.  A popular alternative is "\t". | ||||||
|  |  | ||||||
|  | 	* MaxDepth | ||||||
|  | 		Maximum number of levels to descend into nested data structures. | ||||||
|  | 		There is no limit by default. | ||||||
|  |  | ||||||
|  | 	* DisableMethods | ||||||
|  | 		Disables invocation of error and Stringer interface methods. | ||||||
|  | 		Method invocation is enabled by default. | ||||||
|  |  | ||||||
|  | 	* DisablePointerMethods | ||||||
|  | 		Disables invocation of error and Stringer interface methods on types | ||||||
|  | 		which only accept pointer receivers from non-pointer variables. | ||||||
|  | 		Pointer method invocation is enabled by default. | ||||||
|  |  | ||||||
|  | 	* ContinueOnMethod | ||||||
|  | 		Enables recursion into types after invoking error and Stringer interface | ||||||
|  | 		methods. Recursion after method invocation is disabled by default. | ||||||
|  |  | ||||||
|  | 	* SortKeys | ||||||
|  | 		Specifies map keys should be sorted before being printed. Use | ||||||
|  | 		this to have a more deterministic, diffable output.  Note that | ||||||
|  | 		only native types (bool, int, uint, floats, uintptr and string) | ||||||
|  | 		and types which implement error or Stringer interfaces are | ||||||
|  | 		supported with other types sorted according to the | ||||||
|  | 		reflect.Value.String() output which guarantees display | ||||||
|  | 		stability.  Natural map order is used by default. | ||||||
|  |  | ||||||
|  | 	* SpewKeys | ||||||
|  | 		Specifies that, as a last resort attempt, map keys should be | ||||||
|  | 		spewed to strings and sorted by those strings.  This is only | ||||||
|  | 		considered if SortKeys is true. | ||||||
|  |  | ||||||
|  | Dump Usage | ||||||
|  |  | ||||||
|  | Simply call spew.Dump with a list of variables you want to dump: | ||||||
|  |  | ||||||
|  | 	spew.Dump(myVar1, myVar2, ...) | ||||||
|  |  | ||||||
|  | You may also call spew.Fdump if you would prefer to output to an arbitrary | ||||||
|  | io.Writer.  For example, to dump to standard error: | ||||||
|  |  | ||||||
|  | 	spew.Fdump(os.Stderr, myVar1, myVar2, ...) | ||||||
|  |  | ||||||
|  | A third option is to call spew.Sdump to get the formatted output as a string: | ||||||
|  |  | ||||||
|  | 	str := spew.Sdump(myVar1, myVar2, ...) | ||||||
|  |  | ||||||
|  | Sample Dump Output | ||||||
|  |  | ||||||
|  | See the Dump example for details on the setup of the types and variables being | ||||||
|  | shown here. | ||||||
|  |  | ||||||
|  | 	(main.Foo) { | ||||||
|  | 	 unexportedField: (*main.Bar)(0xf84002e210)({ | ||||||
|  | 	  flag: (main.Flag) flagTwo, | ||||||
|  | 	  data: (uintptr) <nil> | ||||||
|  | 	 }), | ||||||
|  | 	 ExportedField: (map[interface {}]interface {}) (len=1) { | ||||||
|  | 	  (string) (len=3) "one": (bool) true | ||||||
|  | 	 } | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C | ||||||
|  | command as shown. | ||||||
|  | 	([]uint8) (len=32 cap=32) { | ||||||
|  | 	 00000000  11 12 13 14 15 16 17 18  19 1a 1b 1c 1d 1e 1f 20  |............... | | ||||||
|  | 	 00000010  21 22 23 24 25 26 27 28  29 2a 2b 2c 2d 2e 2f 30  |!"#$%&'()*+,-./0| | ||||||
|  | 	 00000020  31 32                                             |12| | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | Custom Formatter | ||||||
|  |  | ||||||
|  | Spew provides a custom formatter that implements the fmt.Formatter interface | ||||||
|  | so that it integrates cleanly with standard fmt package printing functions. The | ||||||
|  | formatter is useful for inline printing of smaller data types similar to the | ||||||
|  | standard %v format specifier. | ||||||
|  |  | ||||||
|  | The custom formatter only responds to the %v (most compact), %+v (adds pointer | ||||||
|  | addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb | ||||||
|  | combinations.  Any other verbs such as %x and %q will be sent to the the | ||||||
|  | standard fmt package for formatting.  In addition, the custom formatter ignores | ||||||
|  | the width and precision arguments (however they will still work on the format | ||||||
|  | specifiers not handled by the custom formatter). | ||||||
|  |  | ||||||
|  | Custom Formatter Usage | ||||||
|  |  | ||||||
|  | The simplest way to make use of the spew custom formatter is to call one of the | ||||||
|  | convenience functions such as spew.Printf, spew.Println, or spew.Printf.  The | ||||||
|  | functions have syntax you are most likely already familiar with: | ||||||
|  |  | ||||||
|  | 	spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) | ||||||
|  | 	spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) | ||||||
|  | 	spew.Println(myVar, myVar2) | ||||||
|  | 	spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) | ||||||
|  | 	spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) | ||||||
|  |  | ||||||
|  | See the Index for the full list convenience functions. | ||||||
|  |  | ||||||
|  | Sample Formatter Output | ||||||
|  |  | ||||||
|  | Double pointer to a uint8: | ||||||
|  | 	  %v: <**>5 | ||||||
|  | 	 %+v: <**>(0xf8400420d0->0xf8400420c8)5 | ||||||
|  | 	 %#v: (**uint8)5 | ||||||
|  | 	%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 | ||||||
|  |  | ||||||
|  | Pointer to circular struct with a uint8 field and a pointer to itself: | ||||||
|  | 	  %v: <*>{1 <*><shown>} | ||||||
|  | 	 %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>} | ||||||
|  | 	 %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>} | ||||||
|  | 	%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>} | ||||||
|  |  | ||||||
|  | See the Printf example for details on the setup of variables being shown | ||||||
|  | here. | ||||||
|  |  | ||||||
|  | Errors | ||||||
|  |  | ||||||
|  | Since it is possible for custom Stringer/error interfaces to panic, spew | ||||||
|  | detects them and handles them internally by printing the panic information | ||||||
|  | inline with the output.  Since spew is intended to provide deep pretty printing | ||||||
|  | capabilities on structures, it intentionally does not return any errors. | ||||||
|  | */ | ||||||
|  | package spew | ||||||
							
								
								
									
										509
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/dump.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										509
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/dump.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,509 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2013 Dave Collins <dave@davec.name> | ||||||
|  |  * | ||||||
|  |  * Permission to use, copy, modify, and 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. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||||
|  |  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||||
|  |  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||||
|  |  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||||
|  |  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||||
|  |  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||||
|  |  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package spew | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"encoding/hex" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"os" | ||||||
|  | 	"reflect" | ||||||
|  | 	"regexp" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	// uint8Type is a reflect.Type representing a uint8.  It is used to | ||||||
|  | 	// convert cgo types to uint8 slices for hexdumping. | ||||||
|  | 	uint8Type = reflect.TypeOf(uint8(0)) | ||||||
|  |  | ||||||
|  | 	// 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$") | ||||||
|  |  | ||||||
|  | 	// 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$") | ||||||
|  |  | ||||||
|  | 	// 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$") | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // dumpState contains information about the state of a dump operation. | ||||||
|  | type dumpState struct { | ||||||
|  | 	w                io.Writer | ||||||
|  | 	depth            int | ||||||
|  | 	pointers         map[uintptr]int | ||||||
|  | 	ignoreNextType   bool | ||||||
|  | 	ignoreNextIndent bool | ||||||
|  | 	cs               *ConfigState | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // indent performs indentation according to the depth level and cs.Indent | ||||||
|  | // option. | ||||||
|  | func (d *dumpState) indent() { | ||||||
|  | 	if d.ignoreNextIndent { | ||||||
|  | 		d.ignoreNextIndent = false | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // unpackValue returns values inside of non-nil interfaces when possible. | ||||||
|  | // This is useful for data types like structs, arrays, slices, and maps which | ||||||
|  | // can contain varying types packed inside an interface. | ||||||
|  | func (d *dumpState) unpackValue(v reflect.Value) reflect.Value { | ||||||
|  | 	if v.Kind() == reflect.Interface && !v.IsNil() { | ||||||
|  | 		v = v.Elem() | ||||||
|  | 	} | ||||||
|  | 	return v | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // dumpPtr handles formatting of pointers by indirecting them as necessary. | ||||||
|  | func (d *dumpState) dumpPtr(v reflect.Value) { | ||||||
|  | 	// Remove pointers at or below the current depth from map used to detect | ||||||
|  | 	// circular refs. | ||||||
|  | 	for k, depth := range d.pointers { | ||||||
|  | 		if depth >= d.depth { | ||||||
|  | 			delete(d.pointers, k) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Keep list of all dereferenced pointers to show later. | ||||||
|  | 	pointerChain := make([]uintptr, 0) | ||||||
|  |  | ||||||
|  | 	// Figure out how many levels of indirection there are by dereferencing | ||||||
|  | 	// pointers and unpacking interfaces down the chain while detecting circular | ||||||
|  | 	// references. | ||||||
|  | 	nilFound := false | ||||||
|  | 	cycleFound := false | ||||||
|  | 	indirects := 0 | ||||||
|  | 	ve := v | ||||||
|  | 	for ve.Kind() == reflect.Ptr { | ||||||
|  | 		if ve.IsNil() { | ||||||
|  | 			nilFound = true | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		indirects++ | ||||||
|  | 		addr := ve.Pointer() | ||||||
|  | 		pointerChain = append(pointerChain, addr) | ||||||
|  | 		if pd, ok := d.pointers[addr]; ok && pd < d.depth { | ||||||
|  | 			cycleFound = true | ||||||
|  | 			indirects-- | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		d.pointers[addr] = d.depth | ||||||
|  |  | ||||||
|  | 		ve = ve.Elem() | ||||||
|  | 		if ve.Kind() == reflect.Interface { | ||||||
|  | 			if ve.IsNil() { | ||||||
|  | 				nilFound = true | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 			ve = ve.Elem() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Display type information. | ||||||
|  | 	d.w.Write(openParenBytes) | ||||||
|  | 	d.w.Write(bytes.Repeat(asteriskBytes, indirects)) | ||||||
|  | 	d.w.Write([]byte(ve.Type().String())) | ||||||
|  | 	d.w.Write(closeParenBytes) | ||||||
|  |  | ||||||
|  | 	// Display pointer information. | ||||||
|  | 	if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 { | ||||||
|  | 		d.w.Write(openParenBytes) | ||||||
|  | 		for i, addr := range pointerChain { | ||||||
|  | 			if i > 0 { | ||||||
|  | 				d.w.Write(pointerChainBytes) | ||||||
|  | 			} | ||||||
|  | 			printHexPtr(d.w, addr) | ||||||
|  | 		} | ||||||
|  | 		d.w.Write(closeParenBytes) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Display dereferenced value. | ||||||
|  | 	d.w.Write(openParenBytes) | ||||||
|  | 	switch { | ||||||
|  | 	case nilFound == true: | ||||||
|  | 		d.w.Write(nilAngleBytes) | ||||||
|  |  | ||||||
|  | 	case cycleFound == true: | ||||||
|  | 		d.w.Write(circularBytes) | ||||||
|  |  | ||||||
|  | 	default: | ||||||
|  | 		d.ignoreNextType = true | ||||||
|  | 		d.dump(ve) | ||||||
|  | 	} | ||||||
|  | 	d.w.Write(closeParenBytes) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // dumpSlice handles formatting of arrays and slices.  Byte (uint8 under | ||||||
|  | // reflection) arrays and slices are dumped in hexdump -C fashion. | ||||||
|  | func (d *dumpState) dumpSlice(v reflect.Value) { | ||||||
|  | 	// Determine whether this type should be hex dumped or not.  Also, | ||||||
|  | 	// for types which should be hexdumped, try to use the underlying data | ||||||
|  | 	// first, then fall back to trying to convert them to a uint8 slice. | ||||||
|  | 	var buf []uint8 | ||||||
|  | 	doConvert := false | ||||||
|  | 	doHexDump := false | ||||||
|  | 	numEntries := v.Len() | ||||||
|  | 	if numEntries > 0 { | ||||||
|  | 		vt := v.Index(0).Type() | ||||||
|  | 		vts := vt.String() | ||||||
|  | 		switch { | ||||||
|  | 		// C types that need to be converted. | ||||||
|  | 		case cCharRE.MatchString(vts): | ||||||
|  | 			fallthrough | ||||||
|  | 		case cUnsignedCharRE.MatchString(vts): | ||||||
|  | 			fallthrough | ||||||
|  | 		case cUint8tCharRE.MatchString(vts): | ||||||
|  | 			doConvert = true | ||||||
|  |  | ||||||
|  | 		// Try to use existing uint8 slices and fall back to converting | ||||||
|  | 		// and copying if that fails. | ||||||
|  | 		case vt.Kind() == reflect.Uint8: | ||||||
|  | 			// We need an addressable interface to convert the type | ||||||
|  | 			// to a byte slice.  However, the reflect package won't | ||||||
|  | 			// give us an interface on certain things like | ||||||
|  | 			// unexported struct fields in order to enforce | ||||||
|  | 			// visibility rules.  We use unsafe, when available, to | ||||||
|  | 			// bypass these restrictions since this package does not | ||||||
|  | 			// mutate the values. | ||||||
|  | 			vs := v | ||||||
|  | 			if !vs.CanInterface() || !vs.CanAddr() { | ||||||
|  | 				vs = unsafeReflectValue(vs) | ||||||
|  | 			} | ||||||
|  | 			if !UnsafeDisabled { | ||||||
|  | 				vs = vs.Slice(0, numEntries) | ||||||
|  |  | ||||||
|  | 				// Use the existing uint8 slice if it can be | ||||||
|  | 				// type asserted. | ||||||
|  | 				iface := vs.Interface() | ||||||
|  | 				if slice, ok := iface.([]uint8); ok { | ||||||
|  | 					buf = slice | ||||||
|  | 					doHexDump = true | ||||||
|  | 					break | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// The underlying data needs to be converted if it can't | ||||||
|  | 			// be type asserted to a uint8 slice. | ||||||
|  | 			doConvert = true | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Copy and convert the underlying type if needed. | ||||||
|  | 		if doConvert && vt.ConvertibleTo(uint8Type) { | ||||||
|  | 			// Convert and copy each element into a uint8 byte | ||||||
|  | 			// slice. | ||||||
|  | 			buf = make([]uint8, numEntries) | ||||||
|  | 			for i := 0; i < numEntries; i++ { | ||||||
|  | 				vv := v.Index(i) | ||||||
|  | 				buf[i] = uint8(vv.Convert(uint8Type).Uint()) | ||||||
|  | 			} | ||||||
|  | 			doHexDump = true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Hexdump the entire slice as needed. | ||||||
|  | 	if doHexDump { | ||||||
|  | 		indent := strings.Repeat(d.cs.Indent, d.depth) | ||||||
|  | 		str := indent + hex.Dump(buf) | ||||||
|  | 		str = strings.Replace(str, "\n", "\n"+indent, -1) | ||||||
|  | 		str = strings.TrimRight(str, d.cs.Indent) | ||||||
|  | 		d.w.Write([]byte(str)) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Recursively call dump for each item. | ||||||
|  | 	for i := 0; i < numEntries; i++ { | ||||||
|  | 		d.dump(d.unpackValue(v.Index(i))) | ||||||
|  | 		if i < (numEntries - 1) { | ||||||
|  | 			d.w.Write(commaNewlineBytes) | ||||||
|  | 		} else { | ||||||
|  | 			d.w.Write(newlineBytes) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // dump is the main workhorse for dumping a value.  It uses the passed reflect | ||||||
|  | // value to figure out what kind of object we are dealing with and formats it | ||||||
|  | // appropriately.  It is a recursive function, however circular data structures | ||||||
|  | // are detected and handled properly. | ||||||
|  | func (d *dumpState) dump(v reflect.Value) { | ||||||
|  | 	// Handle invalid reflect values immediately. | ||||||
|  | 	kind := v.Kind() | ||||||
|  | 	if kind == reflect.Invalid { | ||||||
|  | 		d.w.Write(invalidAngleBytes) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Handle pointers specially. | ||||||
|  | 	if kind == reflect.Ptr { | ||||||
|  | 		d.indent() | ||||||
|  | 		d.dumpPtr(v) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Print type information unless already handled elsewhere. | ||||||
|  | 	if !d.ignoreNextType { | ||||||
|  | 		d.indent() | ||||||
|  | 		d.w.Write(openParenBytes) | ||||||
|  | 		d.w.Write([]byte(v.Type().String())) | ||||||
|  | 		d.w.Write(closeParenBytes) | ||||||
|  | 		d.w.Write(spaceBytes) | ||||||
|  | 	} | ||||||
|  | 	d.ignoreNextType = false | ||||||
|  |  | ||||||
|  | 	// Display length and capacity if the built-in len and cap functions | ||||||
|  | 	// work with the value's kind and the len/cap itself is non-zero. | ||||||
|  | 	valueLen, valueCap := 0, 0 | ||||||
|  | 	switch v.Kind() { | ||||||
|  | 	case reflect.Array, reflect.Slice, reflect.Chan: | ||||||
|  | 		valueLen, valueCap = v.Len(), v.Cap() | ||||||
|  | 	case reflect.Map, reflect.String: | ||||||
|  | 		valueLen = v.Len() | ||||||
|  | 	} | ||||||
|  | 	if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 { | ||||||
|  | 		d.w.Write(openParenBytes) | ||||||
|  | 		if valueLen != 0 { | ||||||
|  | 			d.w.Write(lenEqualsBytes) | ||||||
|  | 			printInt(d.w, int64(valueLen), 10) | ||||||
|  | 		} | ||||||
|  | 		if !d.cs.DisableCapacities && valueCap != 0 { | ||||||
|  | 			if valueLen != 0 { | ||||||
|  | 				d.w.Write(spaceBytes) | ||||||
|  | 			} | ||||||
|  | 			d.w.Write(capEqualsBytes) | ||||||
|  | 			printInt(d.w, int64(valueCap), 10) | ||||||
|  | 		} | ||||||
|  | 		d.w.Write(closeParenBytes) | ||||||
|  | 		d.w.Write(spaceBytes) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Call Stringer/error interfaces if they exist and the handle methods flag | ||||||
|  | 	// is enabled | ||||||
|  | 	if !d.cs.DisableMethods { | ||||||
|  | 		if (kind != reflect.Invalid) && (kind != reflect.Interface) { | ||||||
|  | 			if handled := handleMethods(d.cs, d.w, v); handled { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch kind { | ||||||
|  | 	case reflect.Invalid: | ||||||
|  | 		// Do nothing.  We should never get here since invalid has already | ||||||
|  | 		// been handled above. | ||||||
|  |  | ||||||
|  | 	case reflect.Bool: | ||||||
|  | 		printBool(d.w, v.Bool()) | ||||||
|  |  | ||||||
|  | 	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: | ||||||
|  | 		printInt(d.w, v.Int(), 10) | ||||||
|  |  | ||||||
|  | 	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: | ||||||
|  | 		printUint(d.w, v.Uint(), 10) | ||||||
|  |  | ||||||
|  | 	case reflect.Float32: | ||||||
|  | 		printFloat(d.w, v.Float(), 32) | ||||||
|  |  | ||||||
|  | 	case reflect.Float64: | ||||||
|  | 		printFloat(d.w, v.Float(), 64) | ||||||
|  |  | ||||||
|  | 	case reflect.Complex64: | ||||||
|  | 		printComplex(d.w, v.Complex(), 32) | ||||||
|  |  | ||||||
|  | 	case reflect.Complex128: | ||||||
|  | 		printComplex(d.w, v.Complex(), 64) | ||||||
|  |  | ||||||
|  | 	case reflect.Slice: | ||||||
|  | 		if v.IsNil() { | ||||||
|  | 			d.w.Write(nilAngleBytes) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		fallthrough | ||||||
|  |  | ||||||
|  | 	case reflect.Array: | ||||||
|  | 		d.w.Write(openBraceNewlineBytes) | ||||||
|  | 		d.depth++ | ||||||
|  | 		if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { | ||||||
|  | 			d.indent() | ||||||
|  | 			d.w.Write(maxNewlineBytes) | ||||||
|  | 		} else { | ||||||
|  | 			d.dumpSlice(v) | ||||||
|  | 		} | ||||||
|  | 		d.depth-- | ||||||
|  | 		d.indent() | ||||||
|  | 		d.w.Write(closeBraceBytes) | ||||||
|  |  | ||||||
|  | 	case reflect.String: | ||||||
|  | 		d.w.Write([]byte(strconv.Quote(v.String()))) | ||||||
|  |  | ||||||
|  | 	case reflect.Interface: | ||||||
|  | 		// The only time we should get here is for nil interfaces due to | ||||||
|  | 		// unpackValue calls. | ||||||
|  | 		if v.IsNil() { | ||||||
|  | 			d.w.Write(nilAngleBytes) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	case reflect.Ptr: | ||||||
|  | 		// Do nothing.  We should never get here since pointers have already | ||||||
|  | 		// been handled above. | ||||||
|  |  | ||||||
|  | 	case reflect.Map: | ||||||
|  | 		// nil maps should be indicated as different than empty maps | ||||||
|  | 		if v.IsNil() { | ||||||
|  | 			d.w.Write(nilAngleBytes) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		d.w.Write(openBraceNewlineBytes) | ||||||
|  | 		d.depth++ | ||||||
|  | 		if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { | ||||||
|  | 			d.indent() | ||||||
|  | 			d.w.Write(maxNewlineBytes) | ||||||
|  | 		} else { | ||||||
|  | 			numEntries := v.Len() | ||||||
|  | 			keys := v.MapKeys() | ||||||
|  | 			if d.cs.SortKeys { | ||||||
|  | 				sortValues(keys, d.cs) | ||||||
|  | 			} | ||||||
|  | 			for i, key := range keys { | ||||||
|  | 				d.dump(d.unpackValue(key)) | ||||||
|  | 				d.w.Write(colonSpaceBytes) | ||||||
|  | 				d.ignoreNextIndent = true | ||||||
|  | 				d.dump(d.unpackValue(v.MapIndex(key))) | ||||||
|  | 				if i < (numEntries - 1) { | ||||||
|  | 					d.w.Write(commaNewlineBytes) | ||||||
|  | 				} else { | ||||||
|  | 					d.w.Write(newlineBytes) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		d.depth-- | ||||||
|  | 		d.indent() | ||||||
|  | 		d.w.Write(closeBraceBytes) | ||||||
|  |  | ||||||
|  | 	case reflect.Struct: | ||||||
|  | 		d.w.Write(openBraceNewlineBytes) | ||||||
|  | 		d.depth++ | ||||||
|  | 		if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { | ||||||
|  | 			d.indent() | ||||||
|  | 			d.w.Write(maxNewlineBytes) | ||||||
|  | 		} else { | ||||||
|  | 			vt := v.Type() | ||||||
|  | 			numFields := v.NumField() | ||||||
|  | 			for i := 0; i < numFields; i++ { | ||||||
|  | 				d.indent() | ||||||
|  | 				vtf := vt.Field(i) | ||||||
|  | 				d.w.Write([]byte(vtf.Name)) | ||||||
|  | 				d.w.Write(colonSpaceBytes) | ||||||
|  | 				d.ignoreNextIndent = true | ||||||
|  | 				d.dump(d.unpackValue(v.Field(i))) | ||||||
|  | 				if i < (numFields - 1) { | ||||||
|  | 					d.w.Write(commaNewlineBytes) | ||||||
|  | 				} else { | ||||||
|  | 					d.w.Write(newlineBytes) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		d.depth-- | ||||||
|  | 		d.indent() | ||||||
|  | 		d.w.Write(closeBraceBytes) | ||||||
|  |  | ||||||
|  | 	case reflect.Uintptr: | ||||||
|  | 		printHexPtr(d.w, uintptr(v.Uint())) | ||||||
|  |  | ||||||
|  | 	case reflect.UnsafePointer, reflect.Chan, reflect.Func: | ||||||
|  | 		printHexPtr(d.w, v.Pointer()) | ||||||
|  |  | ||||||
|  | 	// There were not any other types at the time this code was written, but | ||||||
|  | 	// fall back to letting the default fmt package handle it in case any new | ||||||
|  | 	// types are added. | ||||||
|  | 	default: | ||||||
|  | 		if v.CanInterface() { | ||||||
|  | 			fmt.Fprintf(d.w, "%v", v.Interface()) | ||||||
|  | 		} else { | ||||||
|  | 			fmt.Fprintf(d.w, "%v", v.String()) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // fdump is a helper function to consolidate the logic from the various public | ||||||
|  | // methods which take varying writers and config states. | ||||||
|  | func fdump(cs *ConfigState, w io.Writer, a ...interface{}) { | ||||||
|  | 	for _, arg := range a { | ||||||
|  | 		if arg == nil { | ||||||
|  | 			w.Write(interfaceBytes) | ||||||
|  | 			w.Write(spaceBytes) | ||||||
|  | 			w.Write(nilAngleBytes) | ||||||
|  | 			w.Write(newlineBytes) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		d := dumpState{w: w, cs: cs} | ||||||
|  | 		d.pointers = make(map[uintptr]int) | ||||||
|  | 		d.dump(reflect.ValueOf(arg)) | ||||||
|  | 		d.w.Write(newlineBytes) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Fdump formats and displays the passed arguments to io.Writer w.  It formats | ||||||
|  | // exactly the same as Dump. | ||||||
|  | func Fdump(w io.Writer, a ...interface{}) { | ||||||
|  | 	fdump(&Config, w, a...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Sdump returns a string with the passed arguments formatted exactly the same | ||||||
|  | // as Dump. | ||||||
|  | func Sdump(a ...interface{}) string { | ||||||
|  | 	var buf bytes.Buffer | ||||||
|  | 	fdump(&Config, &buf, a...) | ||||||
|  | 	return buf.String() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | Dump displays the passed parameters to standard out with newlines, customizable | ||||||
|  | indentation, and additional debug information such as complete types and all | ||||||
|  | pointer addresses used to indirect to the final value.  It provides the | ||||||
|  | following features over the built-in printing facilities provided by the fmt | ||||||
|  | package: | ||||||
|  |  | ||||||
|  | 	* Pointers are dereferenced and followed | ||||||
|  | 	* Circular data structures are detected and handled properly | ||||||
|  | 	* Custom Stringer/error interfaces are optionally invoked, including | ||||||
|  | 	  on unexported types | ||||||
|  | 	* Custom types which only implement the Stringer/error interfaces via | ||||||
|  | 	  a pointer receiver are optionally invoked when passing non-pointer | ||||||
|  | 	  variables | ||||||
|  | 	* Byte arrays and slices are dumped like the hexdump -C command which | ||||||
|  | 	  includes offsets, byte values in hex, and ASCII output | ||||||
|  |  | ||||||
|  | The configuration options are controlled by an exported package global, | ||||||
|  | spew.Config.  See ConfigState for options documentation. | ||||||
|  |  | ||||||
|  | See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to | ||||||
|  | get the formatted result as a string. | ||||||
|  | */ | ||||||
|  | func Dump(a ...interface{}) { | ||||||
|  | 	fdump(&Config, os.Stdout, a...) | ||||||
|  | } | ||||||
							
								
								
									
										419
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/format.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										419
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/format.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,419 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2013 Dave Collins <dave@davec.name> | ||||||
|  |  * | ||||||
|  |  * Permission to use, copy, modify, and 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. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||||
|  |  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||||
|  |  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||||
|  |  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||||
|  |  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||||
|  |  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||||
|  |  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package spew | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"fmt" | ||||||
|  | 	"reflect" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // supportedFlags is a list of all the character flags supported by fmt package. | ||||||
|  | const supportedFlags = "0-+# " | ||||||
|  |  | ||||||
|  | // formatState implements the fmt.Formatter interface and contains information | ||||||
|  | // about the state of a formatting operation.  The NewFormatter function can | ||||||
|  | // be used to get a new Formatter which can be used directly as arguments | ||||||
|  | // in standard fmt package printing calls. | ||||||
|  | type formatState struct { | ||||||
|  | 	value          interface{} | ||||||
|  | 	fs             fmt.State | ||||||
|  | 	depth          int | ||||||
|  | 	pointers       map[uintptr]int | ||||||
|  | 	ignoreNextType bool | ||||||
|  | 	cs             *ConfigState | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // buildDefaultFormat recreates the original format string without precision | ||||||
|  | // and width information to pass in to fmt.Sprintf in the case of an | ||||||
|  | // unrecognized type.  Unless new types are added to the language, this | ||||||
|  | // function won't ever be called. | ||||||
|  | func (f *formatState) buildDefaultFormat() (format string) { | ||||||
|  | 	buf := bytes.NewBuffer(percentBytes) | ||||||
|  |  | ||||||
|  | 	for _, flag := range supportedFlags { | ||||||
|  | 		if f.fs.Flag(int(flag)) { | ||||||
|  | 			buf.WriteRune(flag) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	buf.WriteRune('v') | ||||||
|  |  | ||||||
|  | 	format = buf.String() | ||||||
|  | 	return format | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // constructOrigFormat recreates the original format string including precision | ||||||
|  | // and width information to pass along to the standard fmt package.  This allows | ||||||
|  | // automatic deferral of all format strings this package doesn't support. | ||||||
|  | func (f *formatState) constructOrigFormat(verb rune) (format string) { | ||||||
|  | 	buf := bytes.NewBuffer(percentBytes) | ||||||
|  |  | ||||||
|  | 	for _, flag := range supportedFlags { | ||||||
|  | 		if f.fs.Flag(int(flag)) { | ||||||
|  | 			buf.WriteRune(flag) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if width, ok := f.fs.Width(); ok { | ||||||
|  | 		buf.WriteString(strconv.Itoa(width)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if precision, ok := f.fs.Precision(); ok { | ||||||
|  | 		buf.Write(precisionBytes) | ||||||
|  | 		buf.WriteString(strconv.Itoa(precision)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	buf.WriteRune(verb) | ||||||
|  |  | ||||||
|  | 	format = buf.String() | ||||||
|  | 	return format | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // unpackValue returns values inside of non-nil interfaces when possible and | ||||||
|  | // ensures that types for values which have been unpacked from an interface | ||||||
|  | // are displayed when the show types flag is also set. | ||||||
|  | // This is useful for data types like structs, arrays, slices, and maps which | ||||||
|  | // can contain varying types packed inside an interface. | ||||||
|  | func (f *formatState) unpackValue(v reflect.Value) reflect.Value { | ||||||
|  | 	if v.Kind() == reflect.Interface { | ||||||
|  | 		f.ignoreNextType = false | ||||||
|  | 		if !v.IsNil() { | ||||||
|  | 			v = v.Elem() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return v | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // formatPtr handles formatting of pointers by indirecting them as necessary. | ||||||
|  | func (f *formatState) formatPtr(v reflect.Value) { | ||||||
|  | 	// Display nil if top level pointer is nil. | ||||||
|  | 	showTypes := f.fs.Flag('#') | ||||||
|  | 	if v.IsNil() && (!showTypes || f.ignoreNextType) { | ||||||
|  | 		f.fs.Write(nilAngleBytes) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Remove pointers at or below the current depth from map used to detect | ||||||
|  | 	// circular refs. | ||||||
|  | 	for k, depth := range f.pointers { | ||||||
|  | 		if depth >= f.depth { | ||||||
|  | 			delete(f.pointers, k) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Keep list of all dereferenced pointers to possibly show later. | ||||||
|  | 	pointerChain := make([]uintptr, 0) | ||||||
|  |  | ||||||
|  | 	// Figure out how many levels of indirection there are by derferencing | ||||||
|  | 	// pointers and unpacking interfaces down the chain while detecting circular | ||||||
|  | 	// references. | ||||||
|  | 	nilFound := false | ||||||
|  | 	cycleFound := false | ||||||
|  | 	indirects := 0 | ||||||
|  | 	ve := v | ||||||
|  | 	for ve.Kind() == reflect.Ptr { | ||||||
|  | 		if ve.IsNil() { | ||||||
|  | 			nilFound = true | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		indirects++ | ||||||
|  | 		addr := ve.Pointer() | ||||||
|  | 		pointerChain = append(pointerChain, addr) | ||||||
|  | 		if pd, ok := f.pointers[addr]; ok && pd < f.depth { | ||||||
|  | 			cycleFound = true | ||||||
|  | 			indirects-- | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		f.pointers[addr] = f.depth | ||||||
|  |  | ||||||
|  | 		ve = ve.Elem() | ||||||
|  | 		if ve.Kind() == reflect.Interface { | ||||||
|  | 			if ve.IsNil() { | ||||||
|  | 				nilFound = true | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 			ve = ve.Elem() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Display type or indirection level depending on flags. | ||||||
|  | 	if showTypes && !f.ignoreNextType { | ||||||
|  | 		f.fs.Write(openParenBytes) | ||||||
|  | 		f.fs.Write(bytes.Repeat(asteriskBytes, indirects)) | ||||||
|  | 		f.fs.Write([]byte(ve.Type().String())) | ||||||
|  | 		f.fs.Write(closeParenBytes) | ||||||
|  | 	} else { | ||||||
|  | 		if nilFound || cycleFound { | ||||||
|  | 			indirects += strings.Count(ve.Type().String(), "*") | ||||||
|  | 		} | ||||||
|  | 		f.fs.Write(openAngleBytes) | ||||||
|  | 		f.fs.Write([]byte(strings.Repeat("*", indirects))) | ||||||
|  | 		f.fs.Write(closeAngleBytes) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Display pointer information depending on flags. | ||||||
|  | 	if f.fs.Flag('+') && (len(pointerChain) > 0) { | ||||||
|  | 		f.fs.Write(openParenBytes) | ||||||
|  | 		for i, addr := range pointerChain { | ||||||
|  | 			if i > 0 { | ||||||
|  | 				f.fs.Write(pointerChainBytes) | ||||||
|  | 			} | ||||||
|  | 			printHexPtr(f.fs, addr) | ||||||
|  | 		} | ||||||
|  | 		f.fs.Write(closeParenBytes) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Display dereferenced value. | ||||||
|  | 	switch { | ||||||
|  | 	case nilFound == true: | ||||||
|  | 		f.fs.Write(nilAngleBytes) | ||||||
|  |  | ||||||
|  | 	case cycleFound == true: | ||||||
|  | 		f.fs.Write(circularShortBytes) | ||||||
|  |  | ||||||
|  | 	default: | ||||||
|  | 		f.ignoreNextType = true | ||||||
|  | 		f.format(ve) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // format is the main workhorse for providing the Formatter interface.  It | ||||||
|  | // uses the passed reflect value to figure out what kind of object we are | ||||||
|  | // dealing with and formats it appropriately.  It is a recursive function, | ||||||
|  | // however circular data structures are detected and handled properly. | ||||||
|  | func (f *formatState) format(v reflect.Value) { | ||||||
|  | 	// Handle invalid reflect values immediately. | ||||||
|  | 	kind := v.Kind() | ||||||
|  | 	if kind == reflect.Invalid { | ||||||
|  | 		f.fs.Write(invalidAngleBytes) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Handle pointers specially. | ||||||
|  | 	if kind == reflect.Ptr { | ||||||
|  | 		f.formatPtr(v) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Print type information unless already handled elsewhere. | ||||||
|  | 	if !f.ignoreNextType && f.fs.Flag('#') { | ||||||
|  | 		f.fs.Write(openParenBytes) | ||||||
|  | 		f.fs.Write([]byte(v.Type().String())) | ||||||
|  | 		f.fs.Write(closeParenBytes) | ||||||
|  | 	} | ||||||
|  | 	f.ignoreNextType = false | ||||||
|  |  | ||||||
|  | 	// Call Stringer/error interfaces if they exist and the handle methods | ||||||
|  | 	// flag is enabled. | ||||||
|  | 	if !f.cs.DisableMethods { | ||||||
|  | 		if (kind != reflect.Invalid) && (kind != reflect.Interface) { | ||||||
|  | 			if handled := handleMethods(f.cs, f.fs, v); handled { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch kind { | ||||||
|  | 	case reflect.Invalid: | ||||||
|  | 		// Do nothing.  We should never get here since invalid has already | ||||||
|  | 		// been handled above. | ||||||
|  |  | ||||||
|  | 	case reflect.Bool: | ||||||
|  | 		printBool(f.fs, v.Bool()) | ||||||
|  |  | ||||||
|  | 	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: | ||||||
|  | 		printInt(f.fs, v.Int(), 10) | ||||||
|  |  | ||||||
|  | 	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: | ||||||
|  | 		printUint(f.fs, v.Uint(), 10) | ||||||
|  |  | ||||||
|  | 	case reflect.Float32: | ||||||
|  | 		printFloat(f.fs, v.Float(), 32) | ||||||
|  |  | ||||||
|  | 	case reflect.Float64: | ||||||
|  | 		printFloat(f.fs, v.Float(), 64) | ||||||
|  |  | ||||||
|  | 	case reflect.Complex64: | ||||||
|  | 		printComplex(f.fs, v.Complex(), 32) | ||||||
|  |  | ||||||
|  | 	case reflect.Complex128: | ||||||
|  | 		printComplex(f.fs, v.Complex(), 64) | ||||||
|  |  | ||||||
|  | 	case reflect.Slice: | ||||||
|  | 		if v.IsNil() { | ||||||
|  | 			f.fs.Write(nilAngleBytes) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		fallthrough | ||||||
|  |  | ||||||
|  | 	case reflect.Array: | ||||||
|  | 		f.fs.Write(openBracketBytes) | ||||||
|  | 		f.depth++ | ||||||
|  | 		if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { | ||||||
|  | 			f.fs.Write(maxShortBytes) | ||||||
|  | 		} else { | ||||||
|  | 			numEntries := v.Len() | ||||||
|  | 			for i := 0; i < numEntries; i++ { | ||||||
|  | 				if i > 0 { | ||||||
|  | 					f.fs.Write(spaceBytes) | ||||||
|  | 				} | ||||||
|  | 				f.ignoreNextType = true | ||||||
|  | 				f.format(f.unpackValue(v.Index(i))) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		f.depth-- | ||||||
|  | 		f.fs.Write(closeBracketBytes) | ||||||
|  |  | ||||||
|  | 	case reflect.String: | ||||||
|  | 		f.fs.Write([]byte(v.String())) | ||||||
|  |  | ||||||
|  | 	case reflect.Interface: | ||||||
|  | 		// The only time we should get here is for nil interfaces due to | ||||||
|  | 		// unpackValue calls. | ||||||
|  | 		if v.IsNil() { | ||||||
|  | 			f.fs.Write(nilAngleBytes) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	case reflect.Ptr: | ||||||
|  | 		// Do nothing.  We should never get here since pointers have already | ||||||
|  | 		// been handled above. | ||||||
|  |  | ||||||
|  | 	case reflect.Map: | ||||||
|  | 		// nil maps should be indicated as different than empty maps | ||||||
|  | 		if v.IsNil() { | ||||||
|  | 			f.fs.Write(nilAngleBytes) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		f.fs.Write(openMapBytes) | ||||||
|  | 		f.depth++ | ||||||
|  | 		if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { | ||||||
|  | 			f.fs.Write(maxShortBytes) | ||||||
|  | 		} else { | ||||||
|  | 			keys := v.MapKeys() | ||||||
|  | 			if f.cs.SortKeys { | ||||||
|  | 				sortValues(keys, f.cs) | ||||||
|  | 			} | ||||||
|  | 			for i, key := range keys { | ||||||
|  | 				if i > 0 { | ||||||
|  | 					f.fs.Write(spaceBytes) | ||||||
|  | 				} | ||||||
|  | 				f.ignoreNextType = true | ||||||
|  | 				f.format(f.unpackValue(key)) | ||||||
|  | 				f.fs.Write(colonBytes) | ||||||
|  | 				f.ignoreNextType = true | ||||||
|  | 				f.format(f.unpackValue(v.MapIndex(key))) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		f.depth-- | ||||||
|  | 		f.fs.Write(closeMapBytes) | ||||||
|  |  | ||||||
|  | 	case reflect.Struct: | ||||||
|  | 		numFields := v.NumField() | ||||||
|  | 		f.fs.Write(openBraceBytes) | ||||||
|  | 		f.depth++ | ||||||
|  | 		if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { | ||||||
|  | 			f.fs.Write(maxShortBytes) | ||||||
|  | 		} else { | ||||||
|  | 			vt := v.Type() | ||||||
|  | 			for i := 0; i < numFields; i++ { | ||||||
|  | 				if i > 0 { | ||||||
|  | 					f.fs.Write(spaceBytes) | ||||||
|  | 				} | ||||||
|  | 				vtf := vt.Field(i) | ||||||
|  | 				if f.fs.Flag('+') || f.fs.Flag('#') { | ||||||
|  | 					f.fs.Write([]byte(vtf.Name)) | ||||||
|  | 					f.fs.Write(colonBytes) | ||||||
|  | 				} | ||||||
|  | 				f.format(f.unpackValue(v.Field(i))) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		f.depth-- | ||||||
|  | 		f.fs.Write(closeBraceBytes) | ||||||
|  |  | ||||||
|  | 	case reflect.Uintptr: | ||||||
|  | 		printHexPtr(f.fs, uintptr(v.Uint())) | ||||||
|  |  | ||||||
|  | 	case reflect.UnsafePointer, reflect.Chan, reflect.Func: | ||||||
|  | 		printHexPtr(f.fs, v.Pointer()) | ||||||
|  |  | ||||||
|  | 	// There were not any other types at the time this code was written, but | ||||||
|  | 	// fall back to letting the default fmt package handle it if any get added. | ||||||
|  | 	default: | ||||||
|  | 		format := f.buildDefaultFormat() | ||||||
|  | 		if v.CanInterface() { | ||||||
|  | 			fmt.Fprintf(f.fs, format, v.Interface()) | ||||||
|  | 		} else { | ||||||
|  | 			fmt.Fprintf(f.fs, format, v.String()) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Format satisfies the fmt.Formatter interface. See NewFormatter for usage | ||||||
|  | // details. | ||||||
|  | func (f *formatState) Format(fs fmt.State, verb rune) { | ||||||
|  | 	f.fs = fs | ||||||
|  |  | ||||||
|  | 	// Use standard formatting for verbs that are not v. | ||||||
|  | 	if verb != 'v' { | ||||||
|  | 		format := f.constructOrigFormat(verb) | ||||||
|  | 		fmt.Fprintf(fs, format, f.value) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if f.value == nil { | ||||||
|  | 		if fs.Flag('#') { | ||||||
|  | 			fs.Write(interfaceBytes) | ||||||
|  | 		} | ||||||
|  | 		fs.Write(nilAngleBytes) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	f.format(reflect.ValueOf(f.value)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // newFormatter is a helper function to consolidate the logic from the various | ||||||
|  | // public methods which take varying config states. | ||||||
|  | func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter { | ||||||
|  | 	fs := &formatState{value: v, cs: cs} | ||||||
|  | 	fs.pointers = make(map[uintptr]int) | ||||||
|  | 	return fs | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | NewFormatter returns a custom formatter that satisfies the fmt.Formatter | ||||||
|  | interface.  As a result, it integrates cleanly with standard fmt package | ||||||
|  | printing functions.  The formatter is useful for inline printing of smaller data | ||||||
|  | types similar to the standard %v format specifier. | ||||||
|  |  | ||||||
|  | The custom formatter only responds to the %v (most compact), %+v (adds pointer | ||||||
|  | addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb | ||||||
|  | combinations.  Any other verbs such as %x and %q will be sent to the the | ||||||
|  | standard fmt package for formatting.  In addition, the custom formatter ignores | ||||||
|  | the width and precision arguments (however they will still work on the format | ||||||
|  | specifiers not handled by the custom formatter). | ||||||
|  |  | ||||||
|  | Typically this function shouldn't be called directly.  It is much easier to make | ||||||
|  | use of the custom formatter by calling one of the convenience functions such as | ||||||
|  | Printf, Println, or Fprintf. | ||||||
|  | */ | ||||||
|  | func NewFormatter(v interface{}) fmt.Formatter { | ||||||
|  | 	return newFormatter(&Config, v) | ||||||
|  | } | ||||||
							
								
								
									
										148
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/spew.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/spew.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2013 Dave Collins <dave@davec.name> | ||||||
|  |  * | ||||||
|  |  * Permission to use, copy, modify, and 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. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||||
|  |  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||||
|  |  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||||
|  |  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||||
|  |  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||||
|  |  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||||
|  |  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package spew | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Errorf is a wrapper for fmt.Errorf that treats each argument as if it were | ||||||
|  | // passed with a default Formatter interface returned by NewFormatter.  It | ||||||
|  | // returns the formatted string as a value that satisfies error.  See | ||||||
|  | // NewFormatter for formatting details. | ||||||
|  | // | ||||||
|  | // This function is shorthand for the following syntax: | ||||||
|  | // | ||||||
|  | //	fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b)) | ||||||
|  | func Errorf(format string, a ...interface{}) (err error) { | ||||||
|  | 	return fmt.Errorf(format, convertArgs(a)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Fprint is a wrapper for fmt.Fprint that treats each argument as if it were | ||||||
|  | // passed with a default Formatter interface returned by NewFormatter.  It | ||||||
|  | // returns the number of bytes written and any write error encountered.  See | ||||||
|  | // NewFormatter for formatting details. | ||||||
|  | // | ||||||
|  | // This function is shorthand for the following syntax: | ||||||
|  | // | ||||||
|  | //	fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b)) | ||||||
|  | func Fprint(w io.Writer, a ...interface{}) (n int, err error) { | ||||||
|  | 	return fmt.Fprint(w, convertArgs(a)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were | ||||||
|  | // passed with a default Formatter interface returned by NewFormatter.  It | ||||||
|  | // returns the number of bytes written and any write error encountered.  See | ||||||
|  | // NewFormatter for formatting details. | ||||||
|  | // | ||||||
|  | // This function is shorthand for the following syntax: | ||||||
|  | // | ||||||
|  | //	fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b)) | ||||||
|  | func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { | ||||||
|  | 	return fmt.Fprintf(w, format, convertArgs(a)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it | ||||||
|  | // passed with a default Formatter interface returned by NewFormatter.  See | ||||||
|  | // NewFormatter for formatting details. | ||||||
|  | // | ||||||
|  | // This function is shorthand for the following syntax: | ||||||
|  | // | ||||||
|  | //	fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b)) | ||||||
|  | func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { | ||||||
|  | 	return fmt.Fprintln(w, convertArgs(a)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Print is a wrapper for fmt.Print that treats each argument as if it were | ||||||
|  | // passed with a default Formatter interface returned by NewFormatter.  It | ||||||
|  | // returns the number of bytes written and any write error encountered.  See | ||||||
|  | // NewFormatter for formatting details. | ||||||
|  | // | ||||||
|  | // This function is shorthand for the following syntax: | ||||||
|  | // | ||||||
|  | //	fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b)) | ||||||
|  | func Print(a ...interface{}) (n int, err error) { | ||||||
|  | 	return fmt.Print(convertArgs(a)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Printf is a wrapper for fmt.Printf that treats each argument as if it were | ||||||
|  | // passed with a default Formatter interface returned by NewFormatter.  It | ||||||
|  | // returns the number of bytes written and any write error encountered.  See | ||||||
|  | // NewFormatter for formatting details. | ||||||
|  | // | ||||||
|  | // This function is shorthand for the following syntax: | ||||||
|  | // | ||||||
|  | //	fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b)) | ||||||
|  | func Printf(format string, a ...interface{}) (n int, err error) { | ||||||
|  | 	return fmt.Printf(format, convertArgs(a)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Println is a wrapper for fmt.Println that treats each argument as if it were | ||||||
|  | // passed with a default Formatter interface returned by NewFormatter.  It | ||||||
|  | // returns the number of bytes written and any write error encountered.  See | ||||||
|  | // NewFormatter for formatting details. | ||||||
|  | // | ||||||
|  | // This function is shorthand for the following syntax: | ||||||
|  | // | ||||||
|  | //	fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b)) | ||||||
|  | func Println(a ...interface{}) (n int, err error) { | ||||||
|  | 	return fmt.Println(convertArgs(a)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Sprint is a wrapper for fmt.Sprint that treats each argument as if it were | ||||||
|  | // passed with a default Formatter interface returned by NewFormatter.  It | ||||||
|  | // returns the resulting string.  See NewFormatter for formatting details. | ||||||
|  | // | ||||||
|  | // This function is shorthand for the following syntax: | ||||||
|  | // | ||||||
|  | //	fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b)) | ||||||
|  | func Sprint(a ...interface{}) string { | ||||||
|  | 	return fmt.Sprint(convertArgs(a)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were | ||||||
|  | // passed with a default Formatter interface returned by NewFormatter.  It | ||||||
|  | // returns the resulting string.  See NewFormatter for formatting details. | ||||||
|  | // | ||||||
|  | // This function is shorthand for the following syntax: | ||||||
|  | // | ||||||
|  | //	fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b)) | ||||||
|  | func Sprintf(format string, a ...interface{}) string { | ||||||
|  | 	return fmt.Sprintf(format, convertArgs(a)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it | ||||||
|  | // were passed with a default Formatter interface returned by NewFormatter.  It | ||||||
|  | // returns the resulting string.  See NewFormatter for formatting details. | ||||||
|  | // | ||||||
|  | // This function is shorthand for the following syntax: | ||||||
|  | // | ||||||
|  | //	fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b)) | ||||||
|  | func Sprintln(a ...interface{}) string { | ||||||
|  | 	return fmt.Sprintln(convertArgs(a)...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // convertArgs accepts a slice of arguments and returns a slice of the same | ||||||
|  | // length with each argument converted to a default spew Formatter interface. | ||||||
|  | func convertArgs(args []interface{}) (formatters []interface{}) { | ||||||
|  | 	formatters = make([]interface{}, len(args)) | ||||||
|  | 	for index, arg := range args { | ||||||
|  | 		formatters[index] = NewFormatter(arg) | ||||||
|  | 	} | ||||||
|  | 	return formatters | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/difflib/LICENCE.txt
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/difflib/LICENCE.txt
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell | ||||||
|  |  | ||||||
|  | Please consider promoting this project if you find it useful. | ||||||
|  |  | ||||||
|  | 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. | ||||||
							
								
								
									
										22
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/difflib/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/difflib/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell | ||||||
|  |  | ||||||
|  | Please consider promoting this project if you find it useful. | ||||||
|  |  | ||||||
|  | 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. | ||||||
							
								
								
									
										758
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/difflib/difflib.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										758
									
								
								vendor/github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/difflib/difflib.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,758 @@ | |||||||
|  | // Package difflib is a partial port of Python difflib module. | ||||||
|  | // | ||||||
|  | // It provides tools to compare sequences of strings and generate textual diffs. | ||||||
|  | // | ||||||
|  | // The following class and functions have been ported: | ||||||
|  | // | ||||||
|  | // - SequenceMatcher | ||||||
|  | // | ||||||
|  | // - unified_diff | ||||||
|  | // | ||||||
|  | // - context_diff | ||||||
|  | // | ||||||
|  | // Getting unified diffs was the main goal of the port. Keep in mind this code | ||||||
|  | // is mostly suitable to output text differences in a human friendly way, there | ||||||
|  | // are no guarantees generated diffs are consumable by patch(1). | ||||||
|  | package difflib | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bufio" | ||||||
|  | 	"bytes" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func min(a, b int) int { | ||||||
|  | 	if a < b { | ||||||
|  | 		return a | ||||||
|  | 	} | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func max(a, b int) int { | ||||||
|  | 	if a > b { | ||||||
|  | 		return a | ||||||
|  | 	} | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func calculateRatio(matches, length int) float64 { | ||||||
|  | 	if length > 0 { | ||||||
|  | 		return 2.0 * float64(matches) / float64(length) | ||||||
|  | 	} | ||||||
|  | 	return 1.0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Match struct { | ||||||
|  | 	A    int | ||||||
|  | 	B    int | ||||||
|  | 	Size int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type OpCode struct { | ||||||
|  | 	Tag byte | ||||||
|  | 	I1  int | ||||||
|  | 	I2  int | ||||||
|  | 	J1  int | ||||||
|  | 	J2  int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SequenceMatcher compares sequence of strings. The basic | ||||||
|  | // algorithm predates, and is a little fancier than, an algorithm | ||||||
|  | // published in the late 1980's by Ratcliff and Obershelp under the | ||||||
|  | // hyperbolic name "gestalt pattern matching".  The basic idea is to find | ||||||
|  | // the longest contiguous matching subsequence that contains no "junk" | ||||||
|  | // elements (R-O doesn't address junk).  The same idea is then applied | ||||||
|  | // recursively to the pieces of the sequences to the left and to the right | ||||||
|  | // of the matching subsequence.  This does not yield minimal edit | ||||||
|  | // sequences, but does tend to yield matches that "look right" to people. | ||||||
|  | // | ||||||
|  | // SequenceMatcher tries to compute a "human-friendly diff" between two | ||||||
|  | // sequences.  Unlike e.g. UNIX(tm) diff, the fundamental notion is the | ||||||
|  | // longest *contiguous* & junk-free matching subsequence.  That's what | ||||||
|  | // catches peoples' eyes.  The Windows(tm) windiff has another interesting | ||||||
|  | // notion, pairing up elements that appear uniquely in each sequence. | ||||||
|  | // That, and the method here, appear to yield more intuitive difference | ||||||
|  | // reports than does diff.  This method appears to be the least vulnerable | ||||||
|  | // to synching up on blocks of "junk lines", though (like blank lines in | ||||||
|  | // ordinary text files, or maybe "<P>" lines in HTML files).  That may be | ||||||
|  | // because this is the only method of the 3 that has a *concept* of | ||||||
|  | // "junk" <wink>. | ||||||
|  | // | ||||||
|  | // Timing:  Basic R-O is cubic time worst case and quadratic time expected | ||||||
|  | // case.  SequenceMatcher is quadratic time for the worst case and has | ||||||
|  | // expected-case behavior dependent in a complicated way on how many | ||||||
|  | // elements the sequences have in common; best case time is linear. | ||||||
|  | type SequenceMatcher struct { | ||||||
|  | 	a              []string | ||||||
|  | 	b              []string | ||||||
|  | 	b2j            map[string][]int | ||||||
|  | 	IsJunk         func(string) bool | ||||||
|  | 	autoJunk       bool | ||||||
|  | 	bJunk          map[string]struct{} | ||||||
|  | 	matchingBlocks []Match | ||||||
|  | 	fullBCount     map[string]int | ||||||
|  | 	bPopular       map[string]struct{} | ||||||
|  | 	opCodes        []OpCode | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewMatcher(a, b []string) *SequenceMatcher { | ||||||
|  | 	m := SequenceMatcher{autoJunk: true} | ||||||
|  | 	m.SetSeqs(a, b) | ||||||
|  | 	return &m | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewMatcherWithJunk(a, b []string, autoJunk bool, | ||||||
|  | 	isJunk func(string) bool) *SequenceMatcher { | ||||||
|  |  | ||||||
|  | 	m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk} | ||||||
|  | 	m.SetSeqs(a, b) | ||||||
|  | 	return &m | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Set two sequences to be compared. | ||||||
|  | func (m *SequenceMatcher) SetSeqs(a, b []string) { | ||||||
|  | 	m.SetSeq1(a) | ||||||
|  | 	m.SetSeq2(b) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Set the first sequence to be compared. The second sequence to be compared is | ||||||
|  | // not changed. | ||||||
|  | // | ||||||
|  | // SequenceMatcher computes and caches detailed information about the second | ||||||
|  | // sequence, so if you want to compare one sequence S against many sequences, | ||||||
|  | // use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other | ||||||
|  | // sequences. | ||||||
|  | // | ||||||
|  | // See also SetSeqs() and SetSeq2(). | ||||||
|  | func (m *SequenceMatcher) SetSeq1(a []string) { | ||||||
|  | 	if &a == &m.a { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	m.a = a | ||||||
|  | 	m.matchingBlocks = nil | ||||||
|  | 	m.opCodes = nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Set the second sequence to be compared. The first sequence to be compared is | ||||||
|  | // not changed. | ||||||
|  | func (m *SequenceMatcher) SetSeq2(b []string) { | ||||||
|  | 	if &b == &m.b { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	m.b = b | ||||||
|  | 	m.matchingBlocks = nil | ||||||
|  | 	m.opCodes = nil | ||||||
|  | 	m.fullBCount = nil | ||||||
|  | 	m.chainB() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *SequenceMatcher) chainB() { | ||||||
|  | 	// Populate line -> index mapping | ||||||
|  | 	b2j := map[string][]int{} | ||||||
|  | 	for i, s := range m.b { | ||||||
|  | 		indices := b2j[s] | ||||||
|  | 		indices = append(indices, i) | ||||||
|  | 		b2j[s] = indices | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Purge junk elements | ||||||
|  | 	m.bJunk = map[string]struct{}{} | ||||||
|  | 	if m.IsJunk != nil { | ||||||
|  | 		junk := m.bJunk | ||||||
|  | 		for s, _ := range b2j { | ||||||
|  | 			if m.IsJunk(s) { | ||||||
|  | 				junk[s] = struct{}{} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		for s, _ := range junk { | ||||||
|  | 			delete(b2j, s) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Purge remaining popular elements | ||||||
|  | 	popular := map[string]struct{}{} | ||||||
|  | 	n := len(m.b) | ||||||
|  | 	if m.autoJunk && n >= 200 { | ||||||
|  | 		ntest := n/100 + 1 | ||||||
|  | 		for s, indices := range b2j { | ||||||
|  | 			if len(indices) > ntest { | ||||||
|  | 				popular[s] = struct{}{} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		for s, _ := range popular { | ||||||
|  | 			delete(b2j, s) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	m.bPopular = popular | ||||||
|  | 	m.b2j = b2j | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *SequenceMatcher) isBJunk(s string) bool { | ||||||
|  | 	_, ok := m.bJunk[s] | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Find longest matching block in a[alo:ahi] and b[blo:bhi]. | ||||||
|  | // | ||||||
|  | // If IsJunk is not defined: | ||||||
|  | // | ||||||
|  | // Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where | ||||||
|  | //     alo <= i <= i+k <= ahi | ||||||
|  | //     blo <= j <= j+k <= bhi | ||||||
|  | // and for all (i',j',k') meeting those conditions, | ||||||
|  | //     k >= k' | ||||||
|  | //     i <= i' | ||||||
|  | //     and if i == i', j <= j' | ||||||
|  | // | ||||||
|  | // In other words, of all maximal matching blocks, return one that | ||||||
|  | // starts earliest in a, and of all those maximal matching blocks that | ||||||
|  | // start earliest in a, return the one that starts earliest in b. | ||||||
|  | // | ||||||
|  | // If IsJunk is defined, first the longest matching block is | ||||||
|  | // determined as above, but with the additional restriction that no | ||||||
|  | // junk element appears in the block.  Then that block is extended as | ||||||
|  | // far as possible by matching (only) junk elements on both sides.  So | ||||||
|  | // the resulting block never matches on junk except as identical junk | ||||||
|  | // happens to be adjacent to an "interesting" match. | ||||||
|  | // | ||||||
|  | // If no blocks match, return (alo, blo, 0). | ||||||
|  | func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match { | ||||||
|  | 	// CAUTION:  stripping common prefix or suffix would be incorrect. | ||||||
|  | 	// E.g., | ||||||
|  | 	//    ab | ||||||
|  | 	//    acab | ||||||
|  | 	// Longest matching block is "ab", but if common prefix is | ||||||
|  | 	// stripped, it's "a" (tied with "b").  UNIX(tm) diff does so | ||||||
|  | 	// strip, so ends up claiming that ab is changed to acab by | ||||||
|  | 	// inserting "ca" in the middle.  That's minimal but unintuitive: | ||||||
|  | 	// "it's obvious" that someone inserted "ac" at the front. | ||||||
|  | 	// Windiff ends up at the same place as diff, but by pairing up | ||||||
|  | 	// the unique 'b's and then matching the first two 'a's. | ||||||
|  | 	besti, bestj, bestsize := alo, blo, 0 | ||||||
|  |  | ||||||
|  | 	// find longest junk-free match | ||||||
|  | 	// during an iteration of the loop, j2len[j] = length of longest | ||||||
|  | 	// junk-free match ending with a[i-1] and b[j] | ||||||
|  | 	j2len := map[int]int{} | ||||||
|  | 	for i := alo; i != ahi; i++ { | ||||||
|  | 		// look at all instances of a[i] in b; note that because | ||||||
|  | 		// b2j has no junk keys, the loop is skipped if a[i] is junk | ||||||
|  | 		newj2len := map[int]int{} | ||||||
|  | 		for _, j := range m.b2j[m.a[i]] { | ||||||
|  | 			// a[i] matches b[j] | ||||||
|  | 			if j < blo { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			if j >= bhi { | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 			k := j2len[j-1] + 1 | ||||||
|  | 			newj2len[j] = k | ||||||
|  | 			if k > bestsize { | ||||||
|  | 				besti, bestj, bestsize = i-k+1, j-k+1, k | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		j2len = newj2len | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Extend the best by non-junk elements on each end.  In particular, | ||||||
|  | 	// "popular" non-junk elements aren't in b2j, which greatly speeds | ||||||
|  | 	// the inner loop above, but also means "the best" match so far | ||||||
|  | 	// doesn't contain any junk *or* popular non-junk elements. | ||||||
|  | 	for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) && | ||||||
|  | 		m.a[besti-1] == m.b[bestj-1] { | ||||||
|  | 		besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 | ||||||
|  | 	} | ||||||
|  | 	for besti+bestsize < ahi && bestj+bestsize < bhi && | ||||||
|  | 		!m.isBJunk(m.b[bestj+bestsize]) && | ||||||
|  | 		m.a[besti+bestsize] == m.b[bestj+bestsize] { | ||||||
|  | 		bestsize += 1 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Now that we have a wholly interesting match (albeit possibly | ||||||
|  | 	// empty!), we may as well suck up the matching junk on each | ||||||
|  | 	// side of it too.  Can't think of a good reason not to, and it | ||||||
|  | 	// saves post-processing the (possibly considerable) expense of | ||||||
|  | 	// figuring out what to do with it.  In the case of an empty | ||||||
|  | 	// interesting match, this is clearly the right thing to do, | ||||||
|  | 	// because no other kind of match is possible in the regions. | ||||||
|  | 	for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) && | ||||||
|  | 		m.a[besti-1] == m.b[bestj-1] { | ||||||
|  | 		besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 | ||||||
|  | 	} | ||||||
|  | 	for besti+bestsize < ahi && bestj+bestsize < bhi && | ||||||
|  | 		m.isBJunk(m.b[bestj+bestsize]) && | ||||||
|  | 		m.a[besti+bestsize] == m.b[bestj+bestsize] { | ||||||
|  | 		bestsize += 1 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return Match{A: besti, B: bestj, Size: bestsize} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Return list of triples describing matching subsequences. | ||||||
|  | // | ||||||
|  | // Each triple is of the form (i, j, n), and means that | ||||||
|  | // a[i:i+n] == b[j:j+n].  The triples are monotonically increasing in | ||||||
|  | // i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are | ||||||
|  | // adjacent triples in the list, and the second is not the last triple in the | ||||||
|  | // list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe | ||||||
|  | // adjacent equal blocks. | ||||||
|  | // | ||||||
|  | // The last triple is a dummy, (len(a), len(b), 0), and is the only | ||||||
|  | // triple with n==0. | ||||||
|  | func (m *SequenceMatcher) GetMatchingBlocks() []Match { | ||||||
|  | 	if m.matchingBlocks != nil { | ||||||
|  | 		return m.matchingBlocks | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match | ||||||
|  | 	matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match { | ||||||
|  | 		match := m.findLongestMatch(alo, ahi, blo, bhi) | ||||||
|  | 		i, j, k := match.A, match.B, match.Size | ||||||
|  | 		if match.Size > 0 { | ||||||
|  | 			if alo < i && blo < j { | ||||||
|  | 				matched = matchBlocks(alo, i, blo, j, matched) | ||||||
|  | 			} | ||||||
|  | 			matched = append(matched, match) | ||||||
|  | 			if i+k < ahi && j+k < bhi { | ||||||
|  | 				matched = matchBlocks(i+k, ahi, j+k, bhi, matched) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		return matched | ||||||
|  | 	} | ||||||
|  | 	matched := matchBlocks(0, len(m.a), 0, len(m.b), nil) | ||||||
|  |  | ||||||
|  | 	// It's possible that we have adjacent equal blocks in the | ||||||
|  | 	// matching_blocks list now. | ||||||
|  | 	nonAdjacent := []Match{} | ||||||
|  | 	i1, j1, k1 := 0, 0, 0 | ||||||
|  | 	for _, b := range matched { | ||||||
|  | 		// Is this block adjacent to i1, j1, k1? | ||||||
|  | 		i2, j2, k2 := b.A, b.B, b.Size | ||||||
|  | 		if i1+k1 == i2 && j1+k1 == j2 { | ||||||
|  | 			// Yes, so collapse them -- this just increases the length of | ||||||
|  | 			// the first block by the length of the second, and the first | ||||||
|  | 			// block so lengthened remains the block to compare against. | ||||||
|  | 			k1 += k2 | ||||||
|  | 		} else { | ||||||
|  | 			// Not adjacent.  Remember the first block (k1==0 means it's | ||||||
|  | 			// the dummy we started with), and make the second block the | ||||||
|  | 			// new block to compare against. | ||||||
|  | 			if k1 > 0 { | ||||||
|  | 				nonAdjacent = append(nonAdjacent, Match{i1, j1, k1}) | ||||||
|  | 			} | ||||||
|  | 			i1, j1, k1 = i2, j2, k2 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if k1 > 0 { | ||||||
|  | 		nonAdjacent = append(nonAdjacent, Match{i1, j1, k1}) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0}) | ||||||
|  | 	m.matchingBlocks = nonAdjacent | ||||||
|  | 	return m.matchingBlocks | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Return list of 5-tuples describing how to turn a into b. | ||||||
|  | // | ||||||
|  | // Each tuple is of the form (tag, i1, i2, j1, j2).  The first tuple | ||||||
|  | // has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the | ||||||
|  | // tuple preceding it, and likewise for j1 == the previous j2. | ||||||
|  | // | ||||||
|  | // The tags are characters, with these meanings: | ||||||
|  | // | ||||||
|  | // 'r' (replace):  a[i1:i2] should be replaced by b[j1:j2] | ||||||
|  | // | ||||||
|  | // 'd' (delete):   a[i1:i2] should be deleted, j1==j2 in this case. | ||||||
|  | // | ||||||
|  | // 'i' (insert):   b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case. | ||||||
|  | // | ||||||
|  | // 'e' (equal):    a[i1:i2] == b[j1:j2] | ||||||
|  | func (m *SequenceMatcher) GetOpCodes() []OpCode { | ||||||
|  | 	if m.opCodes != nil { | ||||||
|  | 		return m.opCodes | ||||||
|  | 	} | ||||||
|  | 	i, j := 0, 0 | ||||||
|  | 	matching := m.GetMatchingBlocks() | ||||||
|  | 	opCodes := make([]OpCode, 0, len(matching)) | ||||||
|  | 	for _, m := range matching { | ||||||
|  | 		//  invariant:  we've pumped out correct diffs to change | ||||||
|  | 		//  a[:i] into b[:j], and the next matching block is | ||||||
|  | 		//  a[ai:ai+size] == b[bj:bj+size]. So we need to pump | ||||||
|  | 		//  out a diff to change a[i:ai] into b[j:bj], pump out | ||||||
|  | 		//  the matching block, and move (i,j) beyond the match | ||||||
|  | 		ai, bj, size := m.A, m.B, m.Size | ||||||
|  | 		tag := byte(0) | ||||||
|  | 		if i < ai && j < bj { | ||||||
|  | 			tag = 'r' | ||||||
|  | 		} else if i < ai { | ||||||
|  | 			tag = 'd' | ||||||
|  | 		} else if j < bj { | ||||||
|  | 			tag = 'i' | ||||||
|  | 		} | ||||||
|  | 		if tag > 0 { | ||||||
|  | 			opCodes = append(opCodes, OpCode{tag, i, ai, j, bj}) | ||||||
|  | 		} | ||||||
|  | 		i, j = ai+size, bj+size | ||||||
|  | 		// the list of matching blocks is terminated by a | ||||||
|  | 		// sentinel with size 0 | ||||||
|  | 		if size > 0 { | ||||||
|  | 			opCodes = append(opCodes, OpCode{'e', ai, i, bj, j}) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	m.opCodes = opCodes | ||||||
|  | 	return m.opCodes | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Isolate change clusters by eliminating ranges with no changes. | ||||||
|  | // | ||||||
|  | // Return a generator of groups with up to n lines of context. | ||||||
|  | // Each group is in the same format as returned by GetOpCodes(). | ||||||
|  | func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode { | ||||||
|  | 	if n < 0 { | ||||||
|  | 		n = 3 | ||||||
|  | 	} | ||||||
|  | 	codes := m.GetOpCodes() | ||||||
|  | 	if len(codes) == 0 { | ||||||
|  | 		codes = []OpCode{OpCode{'e', 0, 1, 0, 1}} | ||||||
|  | 	} | ||||||
|  | 	// Fixup leading and trailing groups if they show no changes. | ||||||
|  | 	if codes[0].Tag == 'e' { | ||||||
|  | 		c := codes[0] | ||||||
|  | 		i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 | ||||||
|  | 		codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2} | ||||||
|  | 	} | ||||||
|  | 	if codes[len(codes)-1].Tag == 'e' { | ||||||
|  | 		c := codes[len(codes)-1] | ||||||
|  | 		i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 | ||||||
|  | 		codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)} | ||||||
|  | 	} | ||||||
|  | 	nn := n + n | ||||||
|  | 	groups := [][]OpCode{} | ||||||
|  | 	group := []OpCode{} | ||||||
|  | 	for _, c := range codes { | ||||||
|  | 		i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 | ||||||
|  | 		// End the current group and start a new one whenever | ||||||
|  | 		// there is a large range with no changes. | ||||||
|  | 		if c.Tag == 'e' && i2-i1 > nn { | ||||||
|  | 			group = append(group, OpCode{c.Tag, i1, min(i2, i1+n), | ||||||
|  | 				j1, min(j2, j1+n)}) | ||||||
|  | 			groups = append(groups, group) | ||||||
|  | 			group = []OpCode{} | ||||||
|  | 			i1, j1 = max(i1, i2-n), max(j1, j2-n) | ||||||
|  | 		} | ||||||
|  | 		group = append(group, OpCode{c.Tag, i1, i2, j1, j2}) | ||||||
|  | 	} | ||||||
|  | 	if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') { | ||||||
|  | 		groups = append(groups, group) | ||||||
|  | 	} | ||||||
|  | 	return groups | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Return a measure of the sequences' similarity (float in [0,1]). | ||||||
|  | // | ||||||
|  | // Where T is the total number of elements in both sequences, and | ||||||
|  | // M is the number of matches, this is 2.0*M / T. | ||||||
|  | // Note that this is 1 if the sequences are identical, and 0 if | ||||||
|  | // they have nothing in common. | ||||||
|  | // | ||||||
|  | // .Ratio() is expensive to compute if you haven't already computed | ||||||
|  | // .GetMatchingBlocks() or .GetOpCodes(), in which case you may | ||||||
|  | // want to try .QuickRatio() or .RealQuickRation() first to get an | ||||||
|  | // upper bound. | ||||||
|  | func (m *SequenceMatcher) Ratio() float64 { | ||||||
|  | 	matches := 0 | ||||||
|  | 	for _, m := range m.GetMatchingBlocks() { | ||||||
|  | 		matches += m.Size | ||||||
|  | 	} | ||||||
|  | 	return calculateRatio(matches, len(m.a)+len(m.b)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Return an upper bound on ratio() relatively quickly. | ||||||
|  | // | ||||||
|  | // This isn't defined beyond that it is an upper bound on .Ratio(), and | ||||||
|  | // is faster to compute. | ||||||
|  | func (m *SequenceMatcher) QuickRatio() float64 { | ||||||
|  | 	// viewing a and b as multisets, set matches to the cardinality | ||||||
|  | 	// of their intersection; this counts the number of matches | ||||||
|  | 	// without regard to order, so is clearly an upper bound | ||||||
|  | 	if m.fullBCount == nil { | ||||||
|  | 		m.fullBCount = map[string]int{} | ||||||
|  | 		for _, s := range m.b { | ||||||
|  | 			m.fullBCount[s] = m.fullBCount[s] + 1 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// avail[x] is the number of times x appears in 'b' less the | ||||||
|  | 	// number of times we've seen it in 'a' so far ... kinda | ||||||
|  | 	avail := map[string]int{} | ||||||
|  | 	matches := 0 | ||||||
|  | 	for _, s := range m.a { | ||||||
|  | 		n, ok := avail[s] | ||||||
|  | 		if !ok { | ||||||
|  | 			n = m.fullBCount[s] | ||||||
|  | 		} | ||||||
|  | 		avail[s] = n - 1 | ||||||
|  | 		if n > 0 { | ||||||
|  | 			matches += 1 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return calculateRatio(matches, len(m.a)+len(m.b)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Return an upper bound on ratio() very quickly. | ||||||
|  | // | ||||||
|  | // This isn't defined beyond that it is an upper bound on .Ratio(), and | ||||||
|  | // is faster to compute than either .Ratio() or .QuickRatio(). | ||||||
|  | func (m *SequenceMatcher) RealQuickRatio() float64 { | ||||||
|  | 	la, lb := len(m.a), len(m.b) | ||||||
|  | 	return calculateRatio(min(la, lb), la+lb) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Convert range to the "ed" format | ||||||
|  | func formatRangeUnified(start, stop int) string { | ||||||
|  | 	// Per the diff spec at http://www.unix.org/single_unix_specification/ | ||||||
|  | 	beginning := start + 1 // lines start numbering with one | ||||||
|  | 	length := stop - start | ||||||
|  | 	if length == 1 { | ||||||
|  | 		return fmt.Sprintf("%d", beginning) | ||||||
|  | 	} | ||||||
|  | 	if length == 0 { | ||||||
|  | 		beginning -= 1 // empty ranges begin at line just before the range | ||||||
|  | 	} | ||||||
|  | 	return fmt.Sprintf("%d,%d", beginning, length) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Unified diff parameters | ||||||
|  | type UnifiedDiff struct { | ||||||
|  | 	A        []string // First sequence lines | ||||||
|  | 	FromFile string   // First file name | ||||||
|  | 	FromDate string   // First file time | ||||||
|  | 	B        []string // Second sequence lines | ||||||
|  | 	ToFile   string   // Second file name | ||||||
|  | 	ToDate   string   // Second file time | ||||||
|  | 	Eol      string   // Headers end of line, defaults to LF | ||||||
|  | 	Context  int      // Number of context lines | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Compare two sequences of lines; generate the delta as a unified diff. | ||||||
|  | // | ||||||
|  | // Unified diffs are a compact way of showing line changes and a few | ||||||
|  | // lines of context.  The number of context lines is set by 'n' which | ||||||
|  | // defaults to three. | ||||||
|  | // | ||||||
|  | // By default, the diff control lines (those with ---, +++, or @@) are | ||||||
|  | // created with a trailing newline.  This is helpful so that inputs | ||||||
|  | // created from file.readlines() result in diffs that are suitable for | ||||||
|  | // file.writelines() since both the inputs and outputs have trailing | ||||||
|  | // newlines. | ||||||
|  | // | ||||||
|  | // For inputs that do not have trailing newlines, set the lineterm | ||||||
|  | // argument to "" so that the output will be uniformly newline free. | ||||||
|  | // | ||||||
|  | // The unidiff format normally has a header for filenames and modification | ||||||
|  | // times.  Any or all of these may be specified using strings for | ||||||
|  | // 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'. | ||||||
|  | // The modification times are normally expressed in the ISO 8601 format. | ||||||
|  | func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error { | ||||||
|  | 	buf := bufio.NewWriter(writer) | ||||||
|  | 	defer buf.Flush() | ||||||
|  | 	w := func(format string, args ...interface{}) error { | ||||||
|  | 		_, err := buf.WriteString(fmt.Sprintf(format, args...)) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(diff.Eol) == 0 { | ||||||
|  | 		diff.Eol = "\n" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	started := false | ||||||
|  | 	m := NewMatcher(diff.A, diff.B) | ||||||
|  | 	for _, g := range m.GetGroupedOpCodes(diff.Context) { | ||||||
|  | 		if !started { | ||||||
|  | 			started = true | ||||||
|  | 			fromDate := "" | ||||||
|  | 			if len(diff.FromDate) > 0 { | ||||||
|  | 				fromDate = "\t" + diff.FromDate | ||||||
|  | 			} | ||||||
|  | 			toDate := "" | ||||||
|  | 			if len(diff.ToDate) > 0 { | ||||||
|  | 				toDate = "\t" + diff.ToDate | ||||||
|  | 			} | ||||||
|  | 			err := w("--- %s%s%s", diff.FromFile, fromDate, diff.Eol) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			err = w("+++ %s%s%s", diff.ToFile, toDate, diff.Eol) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		first, last := g[0], g[len(g)-1] | ||||||
|  | 		range1 := formatRangeUnified(first.I1, last.I2) | ||||||
|  | 		range2 := formatRangeUnified(first.J1, last.J2) | ||||||
|  | 		if err := w("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		for _, c := range g { | ||||||
|  | 			i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 | ||||||
|  | 			if c.Tag == 'e' { | ||||||
|  | 				for _, line := range diff.A[i1:i2] { | ||||||
|  | 					if err := w(" " + line); err != nil { | ||||||
|  | 						return err | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			if c.Tag == 'r' || c.Tag == 'd' { | ||||||
|  | 				for _, line := range diff.A[i1:i2] { | ||||||
|  | 					if err := w("-" + line); err != nil { | ||||||
|  | 						return err | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if c.Tag == 'r' || c.Tag == 'i' { | ||||||
|  | 				for _, line := range diff.B[j1:j2] { | ||||||
|  | 					if err := w("+" + line); err != nil { | ||||||
|  | 						return err | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Like WriteUnifiedDiff but returns the diff a string. | ||||||
|  | func GetUnifiedDiffString(diff UnifiedDiff) (string, error) { | ||||||
|  | 	w := &bytes.Buffer{} | ||||||
|  | 	err := WriteUnifiedDiff(w, diff) | ||||||
|  | 	return string(w.Bytes()), err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Convert range to the "ed" format. | ||||||
|  | func formatRangeContext(start, stop int) string { | ||||||
|  | 	// Per the diff spec at http://www.unix.org/single_unix_specification/ | ||||||
|  | 	beginning := start + 1 // lines start numbering with one | ||||||
|  | 	length := stop - start | ||||||
|  | 	if length == 0 { | ||||||
|  | 		beginning -= 1 // empty ranges begin at line just before the range | ||||||
|  | 	} | ||||||
|  | 	if length <= 1 { | ||||||
|  | 		return fmt.Sprintf("%d", beginning) | ||||||
|  | 	} | ||||||
|  | 	return fmt.Sprintf("%d,%d", beginning, beginning+length-1) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ContextDiff UnifiedDiff | ||||||
|  |  | ||||||
|  | // Compare two sequences of lines; generate the delta as a context diff. | ||||||
|  | // | ||||||
|  | // Context diffs are a compact way of showing line changes and a few | ||||||
|  | // lines of context. The number of context lines is set by diff.Context | ||||||
|  | // which defaults to three. | ||||||
|  | // | ||||||
|  | // By default, the diff control lines (those with *** or ---) are | ||||||
|  | // created with a trailing newline. | ||||||
|  | // | ||||||
|  | // For inputs that do not have trailing newlines, set the diff.Eol | ||||||
|  | // argument to "" so that the output will be uniformly newline free. | ||||||
|  | // | ||||||
|  | // The context diff format normally has a header for filenames and | ||||||
|  | // modification times.  Any or all of these may be specified using | ||||||
|  | // strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate. | ||||||
|  | // The modification times are normally expressed in the ISO 8601 format. | ||||||
|  | // If not specified, the strings default to blanks. | ||||||
|  | func WriteContextDiff(writer io.Writer, diff ContextDiff) error { | ||||||
|  | 	buf := bufio.NewWriter(writer) | ||||||
|  | 	defer buf.Flush() | ||||||
|  | 	var diffErr error | ||||||
|  | 	w := func(format string, args ...interface{}) { | ||||||
|  | 		_, err := buf.WriteString(fmt.Sprintf(format, args...)) | ||||||
|  | 		if diffErr == nil && err != nil { | ||||||
|  | 			diffErr = err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(diff.Eol) == 0 { | ||||||
|  | 		diff.Eol = "\n" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	prefix := map[byte]string{ | ||||||
|  | 		'i': "+ ", | ||||||
|  | 		'd': "- ", | ||||||
|  | 		'r': "! ", | ||||||
|  | 		'e': "  ", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	started := false | ||||||
|  | 	m := NewMatcher(diff.A, diff.B) | ||||||
|  | 	for _, g := range m.GetGroupedOpCodes(diff.Context) { | ||||||
|  | 		if !started { | ||||||
|  | 			started = true | ||||||
|  | 			fromDate := "" | ||||||
|  | 			if len(diff.FromDate) > 0 { | ||||||
|  | 				fromDate = "\t" + diff.FromDate | ||||||
|  | 			} | ||||||
|  | 			toDate := "" | ||||||
|  | 			if len(diff.ToDate) > 0 { | ||||||
|  | 				toDate = "\t" + diff.ToDate | ||||||
|  | 			} | ||||||
|  | 			w("*** %s%s%s", diff.FromFile, fromDate, diff.Eol) | ||||||
|  | 			w("--- %s%s%s", diff.ToFile, toDate, diff.Eol) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		first, last := g[0], g[len(g)-1] | ||||||
|  | 		w("***************" + diff.Eol) | ||||||
|  |  | ||||||
|  | 		range1 := formatRangeContext(first.I1, last.I2) | ||||||
|  | 		w("*** %s ****%s", range1, diff.Eol) | ||||||
|  | 		for _, c := range g { | ||||||
|  | 			if c.Tag == 'r' || c.Tag == 'd' { | ||||||
|  | 				for _, cc := range g { | ||||||
|  | 					if cc.Tag == 'i' { | ||||||
|  | 						continue | ||||||
|  | 					} | ||||||
|  | 					for _, line := range diff.A[cc.I1:cc.I2] { | ||||||
|  | 						w(prefix[cc.Tag] + line) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		range2 := formatRangeContext(first.J1, last.J2) | ||||||
|  | 		w("--- %s ----%s", range2, diff.Eol) | ||||||
|  | 		for _, c := range g { | ||||||
|  | 			if c.Tag == 'r' || c.Tag == 'i' { | ||||||
|  | 				for _, cc := range g { | ||||||
|  | 					if cc.Tag == 'd' { | ||||||
|  | 						continue | ||||||
|  | 					} | ||||||
|  | 					for _, line := range diff.B[cc.J1:cc.J2] { | ||||||
|  | 						w(prefix[cc.Tag] + line) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return diffErr | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Like WriteContextDiff but returns the diff a string. | ||||||
|  | func GetContextDiffString(diff ContextDiff) (string, error) { | ||||||
|  | 	w := &bytes.Buffer{} | ||||||
|  | 	err := WriteContextDiff(w, diff) | ||||||
|  | 	return string(w.Bytes()), err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Split a string on "\n" while preserving them. The output can be used | ||||||
|  | // as input for UnifiedDiff and ContextDiff structures. | ||||||
|  | func SplitLines(s string) []string { | ||||||
|  | 	lines := strings.SplitAfter(s, "\n") | ||||||
|  | 	lines[len(lines)-1] += "\n" | ||||||
|  | 	return lines | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								vendor/github.com/thoj/go-ircevent/examples/simple/simple.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/thoj/go-ircevent/examples/simple/simple.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,27 +0,0 @@ | |||||||
| package main |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"github.com/thoj/go-ircevent" |  | ||||||
| 	"crypto/tls" |  | ||||||
| 	"fmt" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| const channel = "#go-eventirc-test"; |  | ||||||
| const serverssl = "irc.freenode.net:7000" |  | ||||||
|  |  | ||||||
| func main() { |  | ||||||
|         ircnick1 := "blatiblat" |  | ||||||
|         irccon := irc.IRC(ircnick1, "IRCTestSSL") |  | ||||||
|         irccon.VerboseCallbackHandler = true |  | ||||||
|         irccon.Debug = true |  | ||||||
|         irccon.UseTLS = true |  | ||||||
|         irccon.TLSConfig = &tls.Config{InsecureSkipVerify: true} |  | ||||||
|         irccon.AddCallback("001", func(e *irc.Event) { irccon.Join(channel) }) |  | ||||||
|         irccon.AddCallback("366", func(e *irc.Event) {  }) |  | ||||||
|         err := irccon.Connect(serverssl) |  | ||||||
| 	if err != nil { |  | ||||||
| 		fmt.Printf("Err %s", err ) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|         irccon.Loop() |  | ||||||
| } |  | ||||||
							
								
								
									
										51
									
								
								vendor/manifest
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										51
									
								
								vendor/manifest
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,14 @@ | |||||||
| { | { | ||||||
| 	"version": 0, | 	"version": 0, | ||||||
| 	"dependencies": [ | 	"dependencies": [ | ||||||
|  | 		{ | ||||||
|  | 			"importpath": "github.com/42wim/go-ircevent", | ||||||
|  | 			"repository": "https://github.com/42wim/go-ircevent", | ||||||
|  | 			"vcs": "git", | ||||||
|  | 			"revision": "d3aec637ae2f2a4f9ff95df55091894d80fa3112", | ||||||
|  | 			"branch": "ircv3", | ||||||
|  | 			"notests": true | ||||||
|  | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"importpath": "github.com/BurntSushi/toml", | 			"importpath": "github.com/BurntSushi/toml", | ||||||
| 			"repository": "https://github.com/BurntSushi/toml", | 			"repository": "https://github.com/BurntSushi/toml", | ||||||
| @@ -194,6 +202,14 @@ | |||||||
| 			"branch": "master", | 			"branch": "master", | ||||||
| 			"notests": true | 			"notests": true | ||||||
| 		}, | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"importpath": "github.com/hashicorp/golang-lru", | ||||||
|  | 			"repository": "https://github.com/hashicorp/golang-lru", | ||||||
|  | 			"vcs": "git", | ||||||
|  | 			"revision": "0a025b7e63adc15a622f29b0b2c4c3848243bbf6", | ||||||
|  | 			"branch": "master", | ||||||
|  | 			"notests": true | ||||||
|  | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"importpath": "github.com/jpillora/backoff", | 			"importpath": "github.com/jpillora/backoff", | ||||||
| 			"repository": "https://github.com/jpillora/backoff", | 			"repository": "https://github.com/jpillora/backoff", | ||||||
| @@ -456,6 +472,33 @@ | |||||||
| 			"branch": "master", | 			"branch": "master", | ||||||
| 			"notests": true | 			"notests": true | ||||||
| 		}, | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"importpath": "github.com/stretchr/testify/assert", | ||||||
|  | 			"repository": "https://github.com/stretchr/testify", | ||||||
|  | 			"vcs": "git", | ||||||
|  | 			"revision": "05e8a0eda380579888eb53c394909df027f06991", | ||||||
|  | 			"branch": "master", | ||||||
|  | 			"path": "/assert", | ||||||
|  | 			"notests": true | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"importpath": "github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew", | ||||||
|  | 			"repository": "https://github.com/stretchr/testify", | ||||||
|  | 			"vcs": "git", | ||||||
|  | 			"revision": "05e8a0eda380579888eb53c394909df027f06991", | ||||||
|  | 			"branch": "master", | ||||||
|  | 			"path": "vendor/github.com/davecgh/go-spew/spew", | ||||||
|  | 			"notests": true | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"importpath": "github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/difflib", | ||||||
|  | 			"repository": "https://github.com/stretchr/testify", | ||||||
|  | 			"vcs": "git", | ||||||
|  | 			"revision": "05e8a0eda380579888eb53c394909df027f06991", | ||||||
|  | 			"branch": "master", | ||||||
|  | 			"path": "vendor/github.com/pmezard/go-difflib/difflib", | ||||||
|  | 			"notests": true | ||||||
|  | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"importpath": "github.com/technoweenie/multipartstreamer", | 			"importpath": "github.com/technoweenie/multipartstreamer", | ||||||
| 			"repository": "https://github.com/technoweenie/multipartstreamer", | 			"repository": "https://github.com/technoweenie/multipartstreamer", | ||||||
| @@ -464,14 +507,6 @@ | |||||||
| 			"branch": "master", | 			"branch": "master", | ||||||
| 			"notests": true | 			"notests": true | ||||||
| 		}, | 		}, | ||||||
| 		{ |  | ||||||
| 			"importpath": "github.com/thoj/go-ircevent", |  | ||||||
| 			"repository": "https://github.com/thoj/go-ircevent", |  | ||||||
| 			"vcs": "git", |  | ||||||
| 			"revision": "1b0acb5f2f1b615cfbd4b9f91abb14cb39a18769", |  | ||||||
| 			"branch": "master", |  | ||||||
| 			"notests": true |  | ||||||
| 		}, |  | ||||||
| 		{ | 		{ | ||||||
| 			"importpath": "github.com/tylerb/graceful", | 			"importpath": "github.com/tylerb/graceful", | ||||||
| 			"repository": "https://github.com/tylerb/graceful", | 			"repository": "https://github.com/tylerb/graceful", | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user