forked from lug/matterbridge
		
	Compare commits
	
		
			23 Commits
		
	
	
		
			v0.16.2
			...
			v1.0.0-rc1
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					c28b87641e | ||
| 
						 | 
					f8e6a69d6e | ||
| 
						 | 
					54216cec4b | ||
| 
						 | 
					12989bbd99 | ||
| 
						 | 
					38d09dba2e | ||
| 
						 | 
					fafd0c68e9 | ||
| 
						 | 
					41195c8e48 | ||
| 
						 | 
					a97804548e | ||
| 
						 | 
					ba653c0841 | ||
| 
						 | 
					5b191f78a0 | ||
| 
						 | 
					83ef61287e | ||
| 
						 | 
					3527e09bc5 | ||
| 
						 | 
					ddc5b3268f | ||
| 
						 | 
					22307b1934 | ||
| 
						 | 
					bd97357f8d | ||
| 
						 | 
					10dab1366e | ||
| 
						 | 
					52fc94c1fe | ||
| 
						 | 
					c1c7961dd6 | ||
| 
						 | 
					d3eef051b1 | ||
| 
						 | 
					57654df81e | ||
| 
						 | 
					0f791d7a9a | ||
| 
						 | 
					58779e0d65 | ||
| 
						 | 
					4ac361b5fd | 
@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										115
									
								
								README-0.6.md
									
									
									
									
									
								
							
							
						
						
									
										115
									
								
								README-0.6.md
									
									
									
									
									
								
							@@ -1,115 +0,0 @@
 | 
				
			|||||||
# matterbridge
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Simple bridge between mattermost, IRC, XMPP, Gitter and Slack
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* Relays public channel messages between mattermost, IRC, XMPP, Gitter and Slack. Pick and mix.
 | 
					 | 
				
			||||||
* Supports multiple channels.
 | 
					 | 
				
			||||||
* Matterbridge can also work with private groups on your mattermost.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Look at [matterbridge.conf.sample] (https://github.com/42wim/matterbridge/blob/master/matterbridge.conf.sample) for documentation and an example.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Changelog
 | 
					 | 
				
			||||||
Since v0.6.1 support for XMPP, Gitter and Slack is added. More details in [changelog.md] (https://github.com/42wim/matterbridge/blob/master/changelog.md)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Requirements:
 | 
					 | 
				
			||||||
Accounts to one of the supported bridges
 | 
					 | 
				
			||||||
* [Mattermost] (https://github.com/mattermost/platform/)
 | 
					 | 
				
			||||||
* [IRC] (http://www.mirc.com/servers.html)
 | 
					 | 
				
			||||||
* [XMPP] (https://jabber.org)
 | 
					 | 
				
			||||||
* [Gitter] (https://gitter.im)
 | 
					 | 
				
			||||||
* [Slack] (https://www.slack.com)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## binaries
 | 
					 | 
				
			||||||
Binaries can be found [here] (https://github.com/42wim/matterbridge/releases/)
 | 
					 | 
				
			||||||
* For use with mattermost 3.3.0+ [v0.6.1](https://github.com/42wim/matterircd/releases/tag/v0.6.1)
 | 
					 | 
				
			||||||
* For use with mattermost 3.0.0-3.2.0 [v0.5.0](https://github.com/42wim/matterircd/releases/tag/v0.5.0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Docker
 | 
					 | 
				
			||||||
Create your matterbridge.conf file locally eg in ```/tmp/matterbridge.conf```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
docker run -ti -v /tmp/matterbridge.conf:/matterbridge.conf 42wim/matterbridge:0.6.1
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Compatibility
 | 
					 | 
				
			||||||
### Mattermost 
 | 
					 | 
				
			||||||
* Matterbridge v0.6.1 works with mattermost 3.3.0 and higher [3.3.0 release](https://github.com/mattermost/platform/releases/tag/v3.3.0)
 | 
					 | 
				
			||||||
* Matterbridge v0.5.0 works with mattermost 3.0.0 - 3.2.0 [3.2.0 release](https://github.com/mattermost/platform/releases/tag/v3.2.0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#### Webhooks version
 | 
					 | 
				
			||||||
* Configured incoming/outgoing [webhooks](https://www.mattermost.org/webhooks/) on your mattermost instance.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#### Plus (API) version
 | 
					 | 
				
			||||||
* A dedicated user(bot) on your mattermost instance.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## building
 | 
					 | 
				
			||||||
Go 1.6+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed, including setting up your [GOPATH] (https://golang.org/doc/code.html#GOPATH)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
cd $GOPATH
 | 
					 | 
				
			||||||
go get github.com/42wim/matterbridge
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
You should now have matterbridge binary in the bin directory:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
$ ls bin/
 | 
					 | 
				
			||||||
matterbridge
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## running
 | 
					 | 
				
			||||||
1) Copy the matterbridge.conf.sample to matterbridge.conf in the same directory as the matterbridge binary.  
 | 
					 | 
				
			||||||
2) Edit matterbridge.conf with the settings for your environment. See below for more config information.  
 | 
					 | 
				
			||||||
3) Now you can run matterbridge. 
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
Usage of ./matterbridge:
 | 
					 | 
				
			||||||
  -conf string
 | 
					 | 
				
			||||||
        config file (default "matterbridge.conf")
 | 
					 | 
				
			||||||
  -debug
 | 
					 | 
				
			||||||
        enable debug
 | 
					 | 
				
			||||||
  -plus
 | 
					 | 
				
			||||||
        running using API instead of webhooks (deprecated, set Plus flag in [general] config)
 | 
					 | 
				
			||||||
  -version
 | 
					 | 
				
			||||||
        show version
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## config
 | 
					 | 
				
			||||||
### matterbridge
 | 
					 | 
				
			||||||
matterbridge looks for matterbridge.conf in current directory. (use -conf to specify another file)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Look at [matterbridge.conf.sample] (https://github.com/42wim/matterbridge/blob/master/matterbridge.conf.sample) for an example.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### mattermost
 | 
					 | 
				
			||||||
#### webhooks version
 | 
					 | 
				
			||||||
You'll have to configure the incoming and outgoing webhooks. 
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* incoming webhooks
 | 
					 | 
				
			||||||
Go to "account settings" - integrations - "incoming webhooks".  
 | 
					 | 
				
			||||||
Choose a channel at "Add a new incoming webhook", this will create a webhook URL right below.  
 | 
					 | 
				
			||||||
This URL should be set in the matterbridge.conf in the [mattermost] section (see above)  
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* outgoing webhooks
 | 
					 | 
				
			||||||
Go to "account settings" - integrations - "outgoing webhooks".  
 | 
					 | 
				
			||||||
Choose a channel (the same as the one from incoming webhooks) and fill in the address and port of the server matterbridge will run on.  
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
e.g. http://192.168.1.1:9999 (192.168.1.1:9999 is the BindAddress specified in [mattermost] section of matterbridge.conf)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#### plus version
 | 
					 | 
				
			||||||
You'll have to create a new dedicated user on your mattermost instance.
 | 
					 | 
				
			||||||
Specify the login and password in [mattermost] section of matterbridge.conf
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## FAQ
 | 
					 | 
				
			||||||
Please look at [matterbridge.conf.sample] (https://github.com/42wim/matterbridge/blob/master/matterbridge.conf.sample) for more information first. 
 | 
					 | 
				
			||||||
### Mattermost doesn't show the IRC nicks
 | 
					 | 
				
			||||||
If you're running the webhooks version, this can be fixed by either:
 | 
					 | 
				
			||||||
* enabling "override usernames". See [mattermost documentation](http://docs.mattermost.com/developer/webhooks-incoming.html#enabling-incoming-webhooks)
 | 
					 | 
				
			||||||
* setting ```PrefixMessagesWithNick``` to ```true``` in ```mattermost``` section of your matterbridge.conf.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
If you're running the plus version you'll need to:
 | 
					 | 
				
			||||||
* setting ```PrefixMessagesWithNick``` to ```true``` in ```mattermost``` section of your matterbridge.conf.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Also look at the ```RemoteNickFormat``` setting.
 | 
					 | 
				
			||||||
							
								
								
									
										31
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								README.md
									
									
									
									
									
								
							@@ -1,7 +1,7 @@
 | 
				
			|||||||
# matterbridge
 | 
					# matterbridge
 | 
				
			||||||
Click on one of the badges below to join the chat   
 | 
					Click on one of the badges below to join the chat   
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[](https://gitter.im/42wim/matterbridge) [](https://webchat.freenode.net/?channels=matterbridgechat) [](https://discord.gg/AkKPtrQ) [](https://riot.im/app/#/room/#matterbridge:matrix.org) [](https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA) [](https://framateam.org/signup_user_complete/?id=tfqm33ggop8x3qgu4boeieta6e)
 | 
					[](https://gitter.im/42wim/matterbridge) [](https://webchat.freenode.net/?channels=matterbridgechat) [](https://discord.gg/AkKPtrQ) [](https://riot.im/app/#/room/#matterbridge:matrix.org) [](https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA) [](https://framateam.org/signup_user_complete/?id=tfqm33ggop8x3qgu4boeieta6e) 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[](https://github.com/42wim/matterbridge/releases/latest) [](https://bintray.com/42wim/nightly/Matterbridge/_latestVersion)
 | 
					[](https://github.com/42wim/matterbridge/releases/latest) [](https://bintray.com/42wim/nightly/Matterbridge/_latestVersion)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -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.2](https://github.com/42wim/matterbridge/releases/latest)
 | 
					* Latest stable release [v1.0.0-rc1](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
 | 
				
			||||||
@@ -84,12 +88,12 @@ See [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[mattermost]
 | 
					[mattermost]
 | 
				
			||||||
    [mattermost.work]
 | 
					    [mattermost.work]
 | 
				
			||||||
    useAPI=true
 | 
					 | 
				
			||||||
    Server="yourmattermostserver.tld"
 | 
					    Server="yourmattermostserver.tld"
 | 
				
			||||||
    Team="yourteam"
 | 
					    Team="yourteam"
 | 
				
			||||||
    Login="yourlogin"
 | 
					    Login="yourlogin"
 | 
				
			||||||
    Password="yourpass"
 | 
					    Password="yourpass"
 | 
				
			||||||
    PrefixMessagesWithNick=true
 | 
					    PrefixMessagesWithNick=true
 | 
				
			||||||
 | 
					    RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[gateway]]
 | 
					[[gateway]]
 | 
				
			||||||
name="mygateway"
 | 
					name="mygateway"
 | 
				
			||||||
@@ -107,7 +111,6 @@ enable=true
 | 
				
			|||||||
```
 | 
					```
 | 
				
			||||||
[slack]
 | 
					[slack]
 | 
				
			||||||
[slack.test]
 | 
					[slack.test]
 | 
				
			||||||
useAPI=true
 | 
					 | 
				
			||||||
Token="yourslacktoken"
 | 
					Token="yourslacktoken"
 | 
				
			||||||
PrefixMessagesWithNick=true
 | 
					PrefixMessagesWithNick=true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -133,11 +136,8 @@ RemoteNickFormat="[{PROTOCOL}/{BRIDGE}] <{NICK}> "
 | 
				
			|||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Running
 | 
					# Running
 | 
				
			||||||
1) Copy the matterbridge.toml.sample to matterbridge.toml 
 | 
					 | 
				
			||||||
2) Edit matterbridge.toml with the settings for your environment. 
 | 
					 | 
				
			||||||
3) Now you can run matterbridge.  (```./matterbridge```)   
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
(Matterbridge will only look for the config file in your current directory, if it isn't there specify -conf "/path/toyour/matterbridge.toml")
 | 
					See [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
Usage of ./matterbridge:
 | 
					Usage of ./matterbridge:
 | 
				
			||||||
@@ -162,18 +162,11 @@ See [changelog.md](https://github.com/42wim/matterbridge/blob/master/changelog.m
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# FAQ
 | 
					# FAQ
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Please look at [matterbridge.toml.sample](https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.sample) for more information first.
 | 
					See [FAQ](https://github.com/42wim/matterbridge/wiki/FAQ)
 | 
				
			||||||
 | 
					 | 
				
			||||||
## Mattermost doesn't show the IRC nicks
 | 
					 | 
				
			||||||
If you're running the webhooks version, this can be fixed by either:
 | 
					 | 
				
			||||||
* enabling "override usernames". See [mattermost documentation](http://docs.mattermost.com/developer/webhooks-incoming.html#enabling-incoming-webhooks)
 | 
					 | 
				
			||||||
* setting ```PrefixMessagesWithNick``` to ```true``` in ```mattermost``` section of your matterbridge.toml.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
If you're running the API version you'll need to:
 | 
					 | 
				
			||||||
* setting ```PrefixMessagesWithNick``` to ```true``` in ```mattermost``` section of your matterbridge.toml.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Also look at the ```RemoteNickFormat``` setting.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Want to tip ? 
 | 
				
			||||||
 | 
					* eth: 0xb3f9b5387c66ad6be892bcb7bbc67862f3abc16f
 | 
				
			||||||
 | 
					* btc: 1N7cKHj5SfqBHBzDJ6kad4BzeqUBBS2zhs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Thanks
 | 
					# Thanks
 | 
				
			||||||
Matterbridge wouldn't exist without these libraries:
 | 
					Matterbridge wouldn't exist without these libraries:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -116,9 +116,16 @@ func (b *bdiscord) Send(msg config.Message) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	if b.Config.WebhookURL == "" {
 | 
						if b.Config.WebhookURL == "" {
 | 
				
			||||||
		flog.Debugf("Broadcasting using token (API)")
 | 
							flog.Debugf("Broadcasting using token (API)")
 | 
				
			||||||
 | 
							if msg.Event == config.EVENT_USER_ACTION {
 | 
				
			||||||
 | 
								msg.Username = "_" + msg.Username
 | 
				
			||||||
 | 
								msg.Text = msg.Text + "_"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		b.c.ChannelMessageSend(channelID, msg.Username+msg.Text)
 | 
							b.c.ChannelMessageSend(channelID, msg.Username+msg.Text)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		flog.Debugf("Broadcasting using Webhook")
 | 
							flog.Debugf("Broadcasting using Webhook")
 | 
				
			||||||
 | 
							if msg.Event == config.EVENT_USER_ACTION {
 | 
				
			||||||
 | 
								msg.Text = "_" + msg.Text + "_"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		b.c.WebhookExecute(
 | 
							b.c.WebhookExecute(
 | 
				
			||||||
			b.webhookID,
 | 
								b.webhookID,
 | 
				
			||||||
			b.webhookToken,
 | 
								b.webhookToken,
 | 
				
			||||||
@@ -171,11 +178,14 @@ 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
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	username := b.getNick(m.Author)
 | 
						rmsg.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 +198,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 +297,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
 | 
				
			||||||
@@ -222,12 +225,18 @@ func (b *Bslack) handleSlack() {
 | 
				
			|||||||
		if b.Config.WebhookURL == "" && b.Config.WebhookBindAddress == "" && message.Username == b.si.User.Name {
 | 
							if b.Config.WebhookURL == "" && b.Config.WebhookBindAddress == "" && message.Username == b.si.User.Name {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if message.Text == "" || message.Username == "" {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		texts := strings.Split(message.Text, "\n")
 | 
							texts := strings.Split(message.Text, "\n")
 | 
				
			||||||
		for _, text := range texts {
 | 
							for _, text := range texts {
 | 
				
			||||||
			text = b.replaceURL(text)
 | 
								text = b.replaceURL(text)
 | 
				
			||||||
			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
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -279,7 +288,8 @@ func (b *Bslack) handleSlackClient(mchan chan *MMMessage) {
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
				m.Raw = ev
 | 
									m.Raw = ev
 | 
				
			||||||
				m.Text = b.replaceMention(m.Text)
 | 
									m.Text = b.replaceMention(m.Text)
 | 
				
			||||||
				if ev.BotID != "" {
 | 
									// when using webhookURL we can't check if it's our webhook or not for now
 | 
				
			||||||
 | 
									if ev.BotID != "" && b.Config.WebhookURL == "" {
 | 
				
			||||||
					bot, err := b.rtm.GetBotInfo(ev.BotID)
 | 
										bot, err := b.rtm.GetBotInfo(ev.BotID)
 | 
				
			||||||
					if err != nil {
 | 
										if err != nil {
 | 
				
			||||||
						continue
 | 
											continue
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 := ""
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ import (
 | 
				
			|||||||
	"crypto/tls"
 | 
						"crypto/tls"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
	log "github.com/Sirupsen/logrus"
 | 
						log "github.com/Sirupsen/logrus"
 | 
				
			||||||
 | 
						"github.com/jpillora/backoff"
 | 
				
			||||||
	"github.com/mattn/go-xmpp"
 | 
						"github.com/mattn/go-xmpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
@@ -43,7 +44,29 @@ func (b *Bxmpp) Connect() error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	flog.Info("Connection succeeded")
 | 
						flog.Info("Connection succeeded")
 | 
				
			||||||
	go b.handleXmpp()
 | 
						go func() {
 | 
				
			||||||
 | 
							initial := true
 | 
				
			||||||
 | 
							bf := &backoff.Backoff{
 | 
				
			||||||
 | 
								Min:    time.Second,
 | 
				
			||||||
 | 
								Max:    5 * time.Minute,
 | 
				
			||||||
 | 
								Jitter: true,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for {
 | 
				
			||||||
 | 
								if initial {
 | 
				
			||||||
 | 
									b.handleXmpp()
 | 
				
			||||||
 | 
									initial = false
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								d := bf.Duration()
 | 
				
			||||||
 | 
								flog.Infof("Disconnected. Reconnecting in %s", d)
 | 
				
			||||||
 | 
								time.Sleep(d)
 | 
				
			||||||
 | 
								b.xc, err = b.createXMPP()
 | 
				
			||||||
 | 
								if err == nil {
 | 
				
			||||||
 | 
									b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: "", Account: b.Account, Event: config.EVENT_REJOIN_CHANNELS}
 | 
				
			||||||
 | 
									b.handleXmpp()
 | 
				
			||||||
 | 
									bf.Reset()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -96,7 +119,11 @@ func (b *Bxmpp) xmppKeepAlive() chan bool {
 | 
				
			|||||||
		for {
 | 
							for {
 | 
				
			||||||
			select {
 | 
								select {
 | 
				
			||||||
			case <-ticker.C:
 | 
								case <-ticker.C:
 | 
				
			||||||
				b.xc.PingC2S("", "")
 | 
									flog.Debugf("PING")
 | 
				
			||||||
 | 
									err := b.xc.PingC2S("", "")
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										flog.Debugf("PING failed %#v", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			case <-done:
 | 
								case <-done:
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -106,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{}
 | 
				
			||||||
@@ -127,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:
 | 
				
			||||||
@@ -136,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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										17
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								changelog.md
									
									
									
									
									
								
							@@ -1,3 +1,20 @@
 | 
				
			|||||||
 | 
					# 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
 | 
				
			||||||
 | 
					## Bugfix
 | 
				
			||||||
 | 
					* general: Fix in/out logic. Closes #224 
 | 
				
			||||||
 | 
					* general: Fix message modification
 | 
				
			||||||
 | 
					* slack: Disable message from other bots when using webhooks (slack)
 | 
				
			||||||
 | 
					* mattermost: Return better error messages on mattermost connect
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# v0.16.2
 | 
					# v0.16.2
 | 
				
			||||||
## New features
 | 
					## New features
 | 
				
			||||||
* general: binary builds against latest commit are now available on https://bintray.com/42wim/nightly/Matterbridge/_latestVersion
 | 
					* general: binary builds against latest commit are now available on https://bintray.com/42wim/nightly/Matterbridge/_latestVersion
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
				
			||||||
@@ -138,51 +76,51 @@ RECONNECT:
 | 
				
			|||||||
	br.JoinChannels()
 | 
						br.JoinChannels()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (gw *Gateway) mapChannels() error {
 | 
					func (gw *Gateway) mapChannelConfig(cfg []config.Bridge, direction string) {
 | 
				
			||||||
	for _, br := range append(gw.MyConfig.Out, gw.MyConfig.InOut...) {
 | 
						for _, br := range cfg {
 | 
				
			||||||
		if isApi(br.Account) {
 | 
							if isApi(br.Account) {
 | 
				
			||||||
			br.Channel = "api"
 | 
								br.Channel = "api"
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ID := br.Channel + br.Account
 | 
							ID := br.Channel + br.Account
 | 
				
			||||||
		_, ok := gw.Channels[ID]
 | 
							if _, ok := gw.Channels[ID]; !ok {
 | 
				
			||||||
		if !ok {
 | 
								channel := &config.ChannelInfo{Name: br.Channel, Direction: direction, ID: ID, Options: br.Options, Account: br.Account,
 | 
				
			||||||
			channel := &config.ChannelInfo{Name: br.Channel, Direction: "out", ID: ID, Options: br.Options, Account: br.Account,
 | 
									SameChannel: make(map[string]bool)}
 | 
				
			||||||
				GID: 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 {
 | 
				
			||||||
 | 
								// if we already have a key and it's not our current direction it means we have a bidirectional inout
 | 
				
			||||||
 | 
								if gw.Channels[ID].Direction != direction {
 | 
				
			||||||
 | 
									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
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, br := range append(gw.MyConfig.In, gw.MyConfig.InOut...) {
 | 
					func (gw *Gateway) mapChannels() error {
 | 
				
			||||||
		if isApi(br.Account) {
 | 
						gw.mapChannelConfig(gw.MyConfig.In, "in")
 | 
				
			||||||
			br.Channel = "api"
 | 
						gw.mapChannelConfig(gw.MyConfig.Out, "out")
 | 
				
			||||||
		}
 | 
						gw.mapChannelConfig(gw.MyConfig.InOut, "inout")
 | 
				
			||||||
		ID := br.Channel + br.Account
 | 
					 | 
				
			||||||
		_, ok := gw.Channels[ID]
 | 
					 | 
				
			||||||
		if !ok {
 | 
					 | 
				
			||||||
			channel := &config.ChannelInfo{Name: br.Channel, Direction: "in", ID: ID, Options: br.Options, Account: br.Account,
 | 
					 | 
				
			||||||
				GID: make(map[string]bool), SameChannel: make(map[string]bool)}
 | 
					 | 
				
			||||||
			channel.GID[gw.Name] = true
 | 
					 | 
				
			||||||
			channel.SameChannel[gw.Name] = br.SameChannel
 | 
					 | 
				
			||||||
			gw.Channels[channel.ID] = channel
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		gw.Channels[ID].GID[gw.Name] = true
 | 
					 | 
				
			||||||
		gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo {
 | 
					func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo {
 | 
				
			||||||
	var channels []config.ChannelInfo
 | 
						var channels []config.ChannelInfo
 | 
				
			||||||
 | 
						// if source channel is in only, do nothing
 | 
				
			||||||
 | 
						for _, channel := range gw.Channels {
 | 
				
			||||||
 | 
							// lookup the channel from the message
 | 
				
			||||||
 | 
							if channel.ID == getChannelID(*msg) {
 | 
				
			||||||
 | 
								// we only have destinations if the original message is from an "in" (sending) channel
 | 
				
			||||||
 | 
								if !strings.Contains(channel.Direction, "in") {
 | 
				
			||||||
 | 
									return channels
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	for _, channel := range gw.Channels {
 | 
						for _, channel := range gw.Channels {
 | 
				
			||||||
		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] {
 | 
				
			||||||
@@ -191,8 +129,7 @@ func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []con
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if strings.Contains(channel.Direction, "out") && channel.Account == dest.Account && gw.validGatewayDest(msg, channel) {
 | 
				
			||||||
		if channel.Direction == "out" && channel.Account == dest.Account && gw.validGatewayDest(msg, channel) {
 | 
					 | 
				
			||||||
			channels = append(channels, *channel)
 | 
								channels = append(channels, *channel)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -211,15 +148,16 @@ 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
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name)
 | 
							log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name)
 | 
				
			||||||
		msg.Channel = channel.Name
 | 
							msg.Channel = channel.Name
 | 
				
			||||||
		gw.modifyAvatar(&msg, dest)
 | 
							msg.Avatar = gw.modifyAvatar(origmsg, dest)
 | 
				
			||||||
		gw.modifyUsername(&msg, dest)
 | 
							msg.Username = gw.modifyUsername(origmsg, dest)
 | 
				
			||||||
		// for api we need originchannel as channel
 | 
							// for api we need originchannel as channel
 | 
				
			||||||
		if dest.Protocol == "api" {
 | 
							if dest.Protocol == "api" {
 | 
				
			||||||
			msg.Channel = originchannel
 | 
								msg.Channel = originchannel
 | 
				
			||||||
@@ -232,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
 | 
				
			||||||
@@ -259,7 +201,7 @@ func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
 | 
				
			|||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) {
 | 
					func (gw *Gateway) modifyUsername(msg config.Message, dest *bridge.Bridge) string {
 | 
				
			||||||
	br := gw.Bridges[msg.Account]
 | 
						br := gw.Bridges[msg.Account]
 | 
				
			||||||
	msg.Protocol = br.Protocol
 | 
						msg.Protocol = br.Protocol
 | 
				
			||||||
	nick := gw.Config.General.RemoteNickFormat
 | 
						nick := gw.Config.General.RemoteNickFormat
 | 
				
			||||||
@@ -281,10 +223,10 @@ func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) {
 | 
				
			|||||||
	nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
 | 
						nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
 | 
				
			||||||
	nick = strings.Replace(nick, "{BRIDGE}", br.Name, -1)
 | 
						nick = strings.Replace(nick, "{BRIDGE}", br.Name, -1)
 | 
				
			||||||
	nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1)
 | 
						nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1)
 | 
				
			||||||
	msg.Username = nick
 | 
						return nick
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (gw *Gateway) modifyAvatar(msg *config.Message, dest *bridge.Bridge) {
 | 
					func (gw *Gateway) modifyAvatar(msg config.Message, dest *bridge.Bridge) string {
 | 
				
			||||||
	iconurl := gw.Config.General.IconURL
 | 
						iconurl := gw.Config.General.IconURL
 | 
				
			||||||
	if iconurl == "" {
 | 
						if iconurl == "" {
 | 
				
			||||||
		iconurl = dest.Config.IconURL
 | 
							iconurl = dest.Config.IconURL
 | 
				
			||||||
@@ -293,11 +235,13 @@ func (gw *Gateway) modifyAvatar(msg *config.Message, dest *bridge.Bridge) {
 | 
				
			|||||||
	if msg.Avatar == "" {
 | 
						if msg.Avatar == "" {
 | 
				
			||||||
		msg.Avatar = iconurl
 | 
							msg.Avatar = iconurl
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return msg.Avatar
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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 {
 | 
				
			||||||
@@ -305,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,14 @@ 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"
 | 
						//"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.2"
 | 
						version = "1.0.0-rc1"
 | 
				
			||||||
	githash string
 | 
						githash string
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -43,20 +43,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)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -112,7 +116,10 @@ func (m *MMClient) Login() error {
 | 
				
			|||||||
	for {
 | 
						for {
 | 
				
			||||||
		d := b.Duration()
 | 
							d := b.Duration()
 | 
				
			||||||
		// bogus call to get the serverversion
 | 
							// bogus call to get the serverversion
 | 
				
			||||||
		m.Client.GetClientProperties()
 | 
							_, err := m.Client.GetClientProperties()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("%#v", err.Error())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if firstConnection && !supportedVersion(m.Client.ServerVersion) {
 | 
							if firstConnection && !supportedVersion(m.Client.ServerVersion) {
 | 
				
			||||||
			return fmt.Errorf("unsupported mattermost version: %s", m.Client.ServerVersion)
 | 
								return fmt.Errorf("unsupported mattermost version: %s", m.Client.ServerVersion)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -267,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
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -303,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 {
 | 
				
			||||||
@@ -857,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)))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										50
									
								
								migration.md
									
									
									
									
									
								
							
							
						
						
									
										50
									
								
								migration.md
									
									
									
									
									
								
							@@ -1,50 +0,0 @@
 | 
				
			|||||||
# Breaking changes from 0.4 to 0.5 for matterbridge (webhooks version)
 | 
					 | 
				
			||||||
## IRC section
 | 
					 | 
				
			||||||
### Server
 | 
					 | 
				
			||||||
Port removed, added to server
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
server="irc.freenode.net"
 | 
					 | 
				
			||||||
port=6667
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
changed to
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
server="irc.freenode.net:6667"
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
### Channel
 | 
					 | 
				
			||||||
Removed see Channels section below
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### UseSlackCircumfix=true
 | 
					 | 
				
			||||||
Removed, can be done by using ```RemoteNickFormat="<{NICK}> "```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Mattermost section
 | 
					 | 
				
			||||||
### BindAddress
 | 
					 | 
				
			||||||
Port removed, added to BindAddress
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
BindAddress="0.0.0.0"
 | 
					 | 
				
			||||||
port=9999
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
changed to
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
BindAddress="0.0.0.0:9999"
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Token
 | 
					 | 
				
			||||||
Removed
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Channels section
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
[Token "outgoingwebhooktoken1"] 
 | 
					 | 
				
			||||||
IRCChannel="#off-topic"
 | 
					 | 
				
			||||||
MMChannel="off-topic"
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
changed to
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
[Channel "channelnameofchoice"] 
 | 
					 | 
				
			||||||
IRC="#off-topic"
 | 
					 | 
				
			||||||
Mattermost="off-topic"
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
							
								
								
									
										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