mirror of
https://github.com/pierre-emmanuelJ/iptv-proxy.git
synced 2026-03-11 21:54:19 +01:00
Add iptv smarter android app AND xtream server api compatibility (#2)
Signed-off-by: Pierre-Emmanuel Jacquier <pierre-emmanuel.jacquier@epitech.eu>
This commit is contained in:
committed by
GitHub
parent
2561702e5c
commit
93430a69ee
24
cmd/root.go
24
cmd/root.go
@@ -3,14 +3,15 @@ package cmd
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/jamesnetherton/m3u"
|
||||
"github.com/pierre-emmanuelJ/iptv-proxy/pkg/config"
|
||||
|
||||
"github.com/pierre-emmanuelJ/iptv-proxy/pkg/routes"
|
||||
|
||||
"github.com/jamesnetherton/m3u"
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
@@ -23,7 +24,13 @@ var rootCmd = &cobra.Command{
|
||||
Use: "iptv-proxy",
|
||||
Short: "A brief description of your application",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
playlist, err := m3u.Parse(viper.GetString("m3u-url"))
|
||||
m3uURL := viper.GetString("m3u-url")
|
||||
playlist, err := m3u.Parse(m3uURL)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
remoteHostURL, err := url.Parse(m3uURL)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -34,8 +41,12 @@ var rootCmd = &cobra.Command{
|
||||
Hostname: viper.GetString("hostname"),
|
||||
Port: viper.GetInt64("port"),
|
||||
},
|
||||
User: viper.GetString("user"),
|
||||
Password: viper.GetString("password"),
|
||||
RemoteURL: remoteHostURL,
|
||||
XtreamUser: viper.GetString("xtream-user"),
|
||||
XtreamPassword: viper.GetString("xtream-password"),
|
||||
XtreamBaseURL: viper.GetString("xtream-base-url"),
|
||||
User: viper.GetString("user"),
|
||||
Password: viper.GetString("password"),
|
||||
}
|
||||
|
||||
if e := routes.Serve(conf); e != nil {
|
||||
@@ -64,7 +75,10 @@ func init() {
|
||||
rootCmd.Flags().Int64("port", 8080, "Port to expose the IPTVs endpoints")
|
||||
rootCmd.Flags().String("hostname", "", "Hostname or IP to expose the IPTVs endpoints")
|
||||
rootCmd.Flags().String("user", "usertest", "user UNSAFE(temp auth to access proxy)")
|
||||
rootCmd.Flags().String("password", "passwordtest", "password UNSAFE(temp auth to access proxy)")
|
||||
rootCmd.Flags().String("password", "passwordtest", "password UNSAFE(auth to access m3u proxy and xtream proxy)")
|
||||
rootCmd.Flags().String("xtream-user", "xtream_user", "Xtream-code user login")
|
||||
rootCmd.Flags().String("xtream-password", "xtream_password", "Xtream-code password login")
|
||||
rootCmd.Flags().String("xtream-base-url", "http://expample.tv:8080", "Xtream-code base url")
|
||||
|
||||
if e := viper.BindPFlags(rootCmd.Flags()); e != nil {
|
||||
log.Fatal("error binding PFlags to viper")
|
||||
|
||||
@@ -22,4 +22,12 @@ services:
|
||||
# Hostname or IP to expose the IPTVs endpoints (for machine not for docker)
|
||||
HOSTNAME: localhost
|
||||
GIN_MODE: release
|
||||
## Xtream-code proxy configuration
|
||||
XTREAM_USER: xtream_user
|
||||
XTREAM_PASSWORD: xtream_password
|
||||
XTREAM_BASE_URL: "http://example.tv:8080"
|
||||
##### UNSAFE AUTH TODO ADD REAL AUTH
|
||||
#will be used for m3u and xtream auth poxy
|
||||
USER: test
|
||||
PASSWORD: testpassword
|
||||
|
||||
|
||||
2
go.mod
2
go.mod
@@ -1,7 +1,6 @@
|
||||
module github.com/pierre-emmanuelJ/iptv-proxy
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/gin-contrib/cors v0.0.0-20190226021855-50921afdc5c1
|
||||
github.com/gin-gonic/gin v1.3.0
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
@@ -9,4 +8,5 @@ require (
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/spf13/cobra v0.0.3
|
||||
github.com/spf13/viper v1.3.1
|
||||
github.com/tellytv/go.xtream-codes v0.0.0-20190114013623-9b74dcb500e4
|
||||
)
|
||||
|
||||
2
go.sum
2
go.sum
@@ -52,6 +52,8 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
|
||||
github.com/spf13/viper v1.3.1 h1:5+8j8FTpnFV4nEImW/ofkzEt8VoOiLXxdYIDsB73T38=
|
||||
github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/tellytv/go.xtream-codes v0.0.0-20190114013623-9b74dcb500e4 h1:V5xNxrc8ApVSY2uHeVXDkByVB2iAgoOcmWOOu01oATM=
|
||||
github.com/tellytv/go.xtream-codes v0.0.0-20190114013623-9b74dcb500e4/go.mod h1:gWtQ2uZJ49dBh4cWiFuz7Tb5ALxLB9hY1GFoz34lsGs=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2 h1:EICbibRW4JNKMcY+LsWmuwob+CRS1BmdRdjphAm9mH4=
|
||||
github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package config
|
||||
|
||||
import "github.com/jamesnetherton/m3u"
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/jamesnetherton/m3u"
|
||||
)
|
||||
|
||||
// HostConfiguration containt host infos
|
||||
type HostConfiguration struct {
|
||||
@@ -10,8 +14,12 @@ type HostConfiguration struct {
|
||||
|
||||
// ProxyConfig Contain original m3u playlist and HostConfiguration
|
||||
type ProxyConfig struct {
|
||||
Playlist *m3u.Playlist
|
||||
HostConfig *HostConfiguration
|
||||
Playlist *m3u.Playlist
|
||||
HostConfig *HostConfiguration
|
||||
XtreamUser string
|
||||
XtreamPassword string
|
||||
XtreamBaseURL string
|
||||
RemoteURL *url.URL
|
||||
//XXX Very unsafe
|
||||
User, Password string
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ func ReplaceURL(proxyConfig *config.ProxyConfig) (*m3u.Playlist, error) {
|
||||
}
|
||||
config := proxyConfig.HostConfig
|
||||
uri := fmt.Sprintf(
|
||||
"http://%s:%d%s?user=%s&password=%s",
|
||||
"http://%s:%d%s?username=%s&password=%s",
|
||||
config.Hostname,
|
||||
config.Port,
|
||||
oriURL.RequestURI(),
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jamesnetherton/m3u"
|
||||
|
||||
@@ -46,10 +49,16 @@ func Routes(proxyConfig *config.ProxyConfig, r *gin.RouterGroup, newM3U []byte)
|
||||
}
|
||||
|
||||
r.GET("/iptv.m3u", p.authenticate, p.getM3U)
|
||||
|
||||
// XXX Private need for external Android app
|
||||
r.POST("/iptv.m3u", p.authenticate, p.getM3U)
|
||||
|
||||
//Xtream, iptv Smarter android app compatibility
|
||||
r.POST("/player_api.php", p.appAuthenticate, p.xtreamPlayerAPI)
|
||||
r.GET("/xmltv.php", p.authenticate, p.xtreamXMLTV)
|
||||
r.GET(fmt.Sprintf("/%s/%s/:id", proxyConfig.User, proxyConfig.Password), p.xtreamStream)
|
||||
r.GET(fmt.Sprintf("/movie/%s/%s/:id", proxyConfig.User, proxyConfig.Password), p.xtreamStreamMovie)
|
||||
r.GET(fmt.Sprintf("/series/%s/%s/:id", proxyConfig.User, proxyConfig.Password), p.xtreamStreamSeries)
|
||||
|
||||
for i, track := range proxyConfig.Playlist.Tracks {
|
||||
oriURL, err := url.Parse(track.URI)
|
||||
if err != nil {
|
||||
@@ -64,15 +73,25 @@ func Routes(proxyConfig *config.ProxyConfig, r *gin.RouterGroup, newM3U []byte)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *proxy) getM3U(c *gin.Context) {
|
||||
c.Header("Content-Disposition", "attachment; filename=\"iptv.m3u\"")
|
||||
c.Data(http.StatusOK, "application/octet-stream", p.newM3U)
|
||||
}
|
||||
|
||||
func (p *proxy) reverseProxy(c *gin.Context) {
|
||||
rpURL, err := url.Parse(p.Track.URI)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
resp, err := http.Get(rpURL.String())
|
||||
stream(c, rpURL)
|
||||
}
|
||||
|
||||
func stream(c *gin.Context, oriURL *url.URL) {
|
||||
resp, err := http.Get(oriURL.String())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
@@ -90,14 +109,9 @@ func copyHTTPHeader(c *gin.Context, header http.Header) {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *proxy) getM3U(c *gin.Context) {
|
||||
c.Header("Content-Disposition", "attachment; filename=\"iptv.m3u\"")
|
||||
c.Data(http.StatusOK, "application/octet-stream", p.newM3U)
|
||||
}
|
||||
|
||||
// AuthRequest handle auth credentials
|
||||
type AuthRequest struct {
|
||||
User string `form:"user" binding:"required"`
|
||||
User string `form:"username" binding:"required"`
|
||||
Password string `form:"password" binding:"required"`
|
||||
} // XXX very unsafe
|
||||
|
||||
@@ -113,6 +127,31 @@ func (p *proxy) authenticate(ctx *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *proxy) appAuthenticate(c *gin.Context) {
|
||||
contents, err := ioutil.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
q, err := url.ParseQuery(string(contents))
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
if len(q["username"]) == 0 || len(q["password"]) == 0 {
|
||||
c.AbortWithError(http.StatusBadRequest, fmt.Errorf("bad body url query parameters"))
|
||||
return
|
||||
}
|
||||
log.Printf("[iptv-proxy] %v | %s |App Auth\n", time.Now().Format("2006/01/02 - 15:04:05"), c.ClientIP())
|
||||
//XXX very unsafe
|
||||
if p.ProxyConfig.User != q["username"][0] || p.ProxyConfig.Password != q["password"][0] {
|
||||
c.AbortWithStatus(http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
c.Request.Body = ioutil.NopCloser(bytes.NewReader(contents))
|
||||
}
|
||||
|
||||
func initm3u(p *config.ProxyConfig) ([]byte, error) {
|
||||
playlist, err := proxyM3U.ReplaceURL(p)
|
||||
if err != nil {
|
||||
|
||||
133
pkg/routes/xtream.go
Normal file
133
pkg/routes/xtream.go
Normal file
@@ -0,0 +1,133 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
xtreamapi "github.com/pierre-emmanuelJ/iptv-proxy/pkg/xtream-proxy"
|
||||
)
|
||||
|
||||
func (p *proxy) xtreamPlayerAPI(c *gin.Context) {
|
||||
contents, err := ioutil.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println(string(contents))
|
||||
|
||||
q, err := url.ParseQuery(string(contents))
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
if len(q["username"]) == 0 || len(q["password"]) == 0 {
|
||||
c.AbortWithError(http.StatusBadRequest, fmt.Errorf(`bad body url query parameters: missing "username" and "password"`))
|
||||
return
|
||||
}
|
||||
|
||||
var action string
|
||||
if len(q["action"]) > 0 {
|
||||
action = q["action"][0]
|
||||
}
|
||||
|
||||
client, err := xtreamapi.New(p.XtreamUser, p.XtreamPassword, p.XtreamBaseURL)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
var respBody interface{}
|
||||
|
||||
switch action {
|
||||
case xtreamapi.GetLiveCategories:
|
||||
respBody, err = client.GetLiveCategories()
|
||||
case xtreamapi.GetLiveStreams:
|
||||
respBody, err = client.GetLiveStreams("")
|
||||
case xtreamapi.GetVodCategories:
|
||||
respBody, err = client.GetVideoOnDemandCategories()
|
||||
case xtreamapi.GetVodStreams:
|
||||
respBody, err = client.GetVideoOnDemandStreams("")
|
||||
case xtreamapi.GetVodInfo:
|
||||
if len(q["vod_id"]) < 1 {
|
||||
c.AbortWithError(http.StatusBadRequest, fmt.Errorf(`bad body url query parameters: missing "vod_id"`))
|
||||
return
|
||||
}
|
||||
respBody, err = client.GetVideoOnDemandInfo(q["vod_id"][0])
|
||||
case xtreamapi.GetSeriesCategories:
|
||||
respBody, err = client.GetSeriesCategories()
|
||||
case xtreamapi.GetSeries:
|
||||
respBody, err = client.GetSeries("")
|
||||
case xtreamapi.GetSerieInfo:
|
||||
if len(q["series_id"]) < 1 {
|
||||
c.AbortWithError(http.StatusBadRequest, fmt.Errorf(`bad body url query parameters: missing "series_id"`))
|
||||
return
|
||||
}
|
||||
respBody, err = client.GetSeriesInfo(q["series_id"][0])
|
||||
default:
|
||||
respBody, err = client.Login(p.User, p.Password, "http://"+p.HostConfig.Hostname, int(p.HostConfig.Port))
|
||||
}
|
||||
|
||||
log.Printf("[iptv-proxy] %v | %s |Action\t%s\n", time.Now().Format("2006/01/02 - 15:04:05"), c.ClientIP(), action)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, respBody)
|
||||
}
|
||||
|
||||
func (p *proxy) xtreamXMLTV(c *gin.Context) {
|
||||
client, err := xtreamapi.New(p.XtreamUser, p.XtreamPassword, p.XtreamBaseURL)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := client.GetXMLTV()
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.Data(http.StatusOK, "application/xml", resp)
|
||||
}
|
||||
|
||||
func (p *proxy) xtreamStream(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
rpURL, err := url.Parse(fmt.Sprintf("%s/%s/%s/%s", p.XtreamBaseURL, p.XtreamUser, p.XtreamPassword, id))
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
stream(c, rpURL)
|
||||
}
|
||||
|
||||
func (p *proxy) xtreamStreamMovie(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
rpURL, err := url.Parse(fmt.Sprintf("%s/movie/%s/%s/%s", p.XtreamBaseURL, p.XtreamUser, p.XtreamPassword, id))
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
stream(c, rpURL)
|
||||
}
|
||||
|
||||
func (p *proxy) xtreamStreamSeries(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
rpURL, err := url.Parse(fmt.Sprintf("%s/series/%s/%s/%s", p.XtreamBaseURL, p.XtreamUser, p.XtreamPassword, id))
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
stream(c, rpURL)
|
||||
}
|
||||
64
pkg/xtream-proxy/xtream-proxy.go
Normal file
64
pkg/xtream-proxy/xtream-proxy.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package xtreamproxy
|
||||
|
||||
import (
|
||||
xtream "github.com/tellytv/go.xtream-codes"
|
||||
)
|
||||
|
||||
const (
|
||||
GetLiveCategories = "get_live_categories"
|
||||
GetLiveStreams = "get_live_streams"
|
||||
GetVodCategories = "get_vod_categories"
|
||||
GetVodStreams = "get_vod_streams"
|
||||
GetVodInfo = "get_vod_info"
|
||||
GetSeriesCategories = "get_series_categories"
|
||||
GetSeries = "get_series"
|
||||
GetSerieInfo = "get_series_info"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
*xtream.XtreamClient
|
||||
}
|
||||
|
||||
func New(user, password, baseURL string) (*Client, error) {
|
||||
cli, err := xtream.NewClient(user, password, baseURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Client{cli}, nil
|
||||
}
|
||||
|
||||
type Login struct {
|
||||
UserInfo xtream.UserInfo `json:"user_info"`
|
||||
ServerInfo xtream.ServerInfo `json:"server_info"`
|
||||
}
|
||||
|
||||
func (c *Client) Login(proxyUser, proxyPassword, proxyURL string, proxyPort int) (Login, error) {
|
||||
req := Login{
|
||||
UserInfo: xtream.UserInfo{
|
||||
Username: proxyUser,
|
||||
Password: proxyPassword,
|
||||
Message: c.UserInfo.Message,
|
||||
Auth: c.UserInfo.Auth,
|
||||
Status: c.UserInfo.Status,
|
||||
ExpDate: c.UserInfo.ExpDate,
|
||||
IsTrial: c.UserInfo.IsTrial,
|
||||
ActiveConnections: c.UserInfo.ActiveConnections,
|
||||
CreatedAt: c.UserInfo.CreatedAt,
|
||||
MaxConnections: c.UserInfo.MaxConnections,
|
||||
AllowedOutputFormats: c.UserInfo.AllowedOutputFormats,
|
||||
},
|
||||
ServerInfo: xtream.ServerInfo{
|
||||
URL: proxyURL,
|
||||
Port: proxyPort,
|
||||
HTTPSPort: proxyPort,
|
||||
Protocol: c.ServerInfo.Protocol,
|
||||
RTMPPort: proxyPort,
|
||||
Timezone: c.ServerInfo.Timezone,
|
||||
TimestampNow: c.ServerInfo.TimestampNow,
|
||||
TimeNow: c.ServerInfo.TimeNow,
|
||||
},
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
15
vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
15
vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
@@ -1,15 +0,0 @@
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
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.
|
||||
145
vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
145
vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
@@ -1,145 +0,0 @@
|
||||
// Copyright (c) 2015-2016 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.
|
||||
// Go versions prior to 1.4 are disabled because they use a different layout
|
||||
// for interfaces which make the implementation of unsafeReflectValue more complex.
|
||||
// +build !js,!appengine,!safe,!disableunsafe,go1.4
|
||||
|
||||
package spew
|
||||
|
||||
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))
|
||||
)
|
||||
|
||||
type flag uintptr
|
||||
|
||||
var (
|
||||
// flagRO indicates whether the value field of a reflect.Value
|
||||
// is read-only.
|
||||
flagRO flag
|
||||
|
||||
// flagAddr indicates whether the address of the reflect.Value's
|
||||
// value may be taken.
|
||||
flagAddr flag
|
||||
)
|
||||
|
||||
// flagKindMask holds the bits that make up the kind
|
||||
// part of the flags field. In all the supported versions,
|
||||
// it is in the lower 5 bits.
|
||||
const flagKindMask = flag(0x1f)
|
||||
|
||||
// Different versions of Go have used different
|
||||
// bit layouts for the flags type. This table
|
||||
// records the known combinations.
|
||||
var okFlags = []struct {
|
||||
ro, addr flag
|
||||
}{{
|
||||
// From Go 1.4 to 1.5
|
||||
ro: 1 << 5,
|
||||
addr: 1 << 7,
|
||||
}, {
|
||||
// Up to Go tip.
|
||||
ro: 1<<5 | 1<<6,
|
||||
addr: 1 << 8,
|
||||
}}
|
||||
|
||||
var flagValOffset = func() uintptr {
|
||||
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
|
||||
if !ok {
|
||||
panic("reflect.Value has no flag field")
|
||||
}
|
||||
return field.Offset
|
||||
}()
|
||||
|
||||
// flagField returns a pointer to the flag field of a reflect.Value.
|
||||
func flagField(v *reflect.Value) *flag {
|
||||
return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
|
||||
}
|
||||
|
||||
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
|
||||
// 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) reflect.Value {
|
||||
if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
|
||||
return v
|
||||
}
|
||||
flagFieldPtr := flagField(&v)
|
||||
*flagFieldPtr &^= flagRO
|
||||
*flagFieldPtr |= flagAddr
|
||||
return v
|
||||
}
|
||||
|
||||
// Sanity checks against future reflect package changes
|
||||
// to the type or semantics of the Value.flag field.
|
||||
func init() {
|
||||
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
|
||||
if !ok {
|
||||
panic("reflect.Value has no flag field")
|
||||
}
|
||||
if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
|
||||
panic("reflect.Value flag field has changed kind")
|
||||
}
|
||||
type t0 int
|
||||
var t struct {
|
||||
A t0
|
||||
// t0 will have flagEmbedRO set.
|
||||
t0
|
||||
// a will have flagStickyRO set
|
||||
a t0
|
||||
}
|
||||
vA := reflect.ValueOf(t).FieldByName("A")
|
||||
va := reflect.ValueOf(t).FieldByName("a")
|
||||
vt0 := reflect.ValueOf(t).FieldByName("t0")
|
||||
|
||||
// Infer flagRO from the difference between the flags
|
||||
// for the (otherwise identical) fields in t.
|
||||
flagPublic := *flagField(&vA)
|
||||
flagWithRO := *flagField(&va) | *flagField(&vt0)
|
||||
flagRO = flagPublic ^ flagWithRO
|
||||
|
||||
// Infer flagAddr from the difference between a value
|
||||
// taken from a pointer and not.
|
||||
vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
|
||||
flagNoPtr := *flagField(&vA)
|
||||
flagPtr := *flagField(&vPtrA)
|
||||
flagAddr = flagNoPtr ^ flagPtr
|
||||
|
||||
// Check that the inferred flags tally with one of the known versions.
|
||||
for _, f := range okFlags {
|
||||
if flagRO == f.ro && flagAddr == f.addr {
|
||||
return
|
||||
}
|
||||
}
|
||||
panic("reflect.Value read-only flag has changed semantics")
|
||||
}
|
||||
38
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
38
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
@@ -1,38 +0,0 @@
|
||||
// Copyright (c) 2015-2016 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 !go1.4
|
||||
|
||||
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/davecgh/go-spew/spew/common.go
generated
vendored
341
vendor/github.com/davecgh/go-spew/spew/common.go
generated
vendored
@@ -1,341 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 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 hexadecimal 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/davecgh/go-spew/spew/config.go
generated
vendored
306
vendor/github.com/davecgh/go-spew/spew/config.go
generated
vendored
@@ -1,306 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 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: " "}
|
||||
}
|
||||
211
vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
211
vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
@@ -1,211 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 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.
|
||||
|
||||
* DisablePointerAddresses
|
||||
DisablePointerAddresses specifies whether to disable the printing of
|
||||
pointer addresses. This is useful when diffing data structures in tests.
|
||||
|
||||
* DisableCapacities
|
||||
DisableCapacities specifies whether to disable the printing of
|
||||
capacities for arrays, slices, maps and channels. This is useful when
|
||||
diffing data structures in tests.
|
||||
|
||||
* 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/davecgh/go-spew/spew/dump.go
generated
vendored
509
vendor/github.com/davecgh/go-spew/spew/dump.go
generated
vendored
@@ -1,509 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 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:
|
||||
d.w.Write(nilAngleBytes)
|
||||
|
||||
case cycleFound:
|
||||
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/davecgh/go-spew/spew/format.go
generated
vendored
419
vendor/github.com/davecgh/go-spew/spew/format.go
generated
vendored
@@ -1,419 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 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:
|
||||
f.fs.Write(nilAngleBytes)
|
||||
|
||||
case cycleFound:
|
||||
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/davecgh/go-spew/spew/spew.go
generated
vendored
148
vendor/github.com/davecgh/go-spew/spew/spew.go
generated
vendored
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 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
|
||||
}
|
||||
12
vendor/github.com/tellytv/go.xtream-codes/.gitignore
generated
vendored
Normal file
12
vendor/github.com/tellytv/go.xtream-codes/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
33
vendor/github.com/tellytv/go.xtream-codes/.gometalinter.json
generated
vendored
Normal file
33
vendor/github.com/tellytv/go.xtream-codes/.gometalinter.json
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"Enable": [
|
||||
"deadcode",
|
||||
"errcheck",
|
||||
"gochecknoinits",
|
||||
"goconst",
|
||||
"gofmt",
|
||||
"goimports",
|
||||
"golint",
|
||||
"gosec",
|
||||
"gotype",
|
||||
"gotypex",
|
||||
"ineffassign",
|
||||
"interfacer",
|
||||
"megacheck",
|
||||
"misspell",
|
||||
"nakedret",
|
||||
"safesql",
|
||||
"structcheck",
|
||||
"test",
|
||||
"testify",
|
||||
"unconvert",
|
||||
"unparam",
|
||||
"varcheck",
|
||||
"vet",
|
||||
"vetshadow"
|
||||
],
|
||||
"Deadline": "5m",
|
||||
"Sort": [
|
||||
"path",
|
||||
"linter"
|
||||
]
|
||||
}
|
||||
21
vendor/github.com/tellytv/go.xtream-codes/LICENSE
generated
vendored
Normal file
21
vendor/github.com/tellytv/go.xtream-codes/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Telly
|
||||
|
||||
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.
|
||||
2
vendor/github.com/tellytv/go.xtream-codes/README.md
generated
vendored
Normal file
2
vendor/github.com/tellytv/go.xtream-codes/README.md
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# go.xtream-codes
|
||||
A Go library for accessing Xtream-Codes servers
|
||||
58
vendor/github.com/tellytv/go.xtream-codes/base64.go
generated
vendored
Normal file
58
vendor/github.com/tellytv/go.xtream-codes/base64.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
package xtreamcodes
|
||||
|
||||
// Package base64 provides a byte slice type that marshals into json as
|
||||
// a raw (no padding) base64url value.
|
||||
// Originally from https://github.com/manifoldco/go-base64
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Base64Value is a base64url encoded json object,
|
||||
type Base64Value []byte
|
||||
|
||||
// New returns a pointer to a Base64Value, cast from the given byte slice.
|
||||
// This is a convenience function, handling the address creation that a direct
|
||||
// cast would not allow.
|
||||
func New(b []byte) *Base64Value {
|
||||
v := Base64Value(b)
|
||||
return &v
|
||||
}
|
||||
|
||||
// NewFromString returns a Base64Value containing the decoded data in encoded.
|
||||
func NewFromString(encoded string) (*Base64Value, error) {
|
||||
out, err := base64.RawURLEncoding.DecodeString(encoded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return New(out), nil
|
||||
}
|
||||
|
||||
// MarshalJSON returns the ba64url encoding of bv for JSON representation.
|
||||
func (bv *Base64Value) MarshalJSON() ([]byte, error) {
|
||||
return []byte("\"" + base64.RawURLEncoding.EncodeToString(*bv) + "\""), nil
|
||||
}
|
||||
|
||||
func (bv *Base64Value) String() string {
|
||||
return base64.RawURLEncoding.EncodeToString(*bv)
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets bv to the bytes represented in the base64url encoding b.
|
||||
func (bv *Base64Value) UnmarshalJSON(b []byte) error {
|
||||
if len(b) < 2 || b[0] != byte('"') || b[len(b)-1] != byte('"') {
|
||||
return errors.New("value is not a string")
|
||||
}
|
||||
|
||||
out := make([]byte, base64.RawURLEncoding.DecodedLen(len(b)-2))
|
||||
n, err := base64.RawURLEncoding.Decode(out, b[1:len(b)-1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(bv).Elem()
|
||||
v.SetBytes(out[:n])
|
||||
return nil
|
||||
}
|
||||
181
vendor/github.com/tellytv/go.xtream-codes/ffmpeg.go
generated
vendored
Normal file
181
vendor/github.com/tellytv/go.xtream-codes/ffmpeg.go
generated
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
// Copyright 2015 Dave Gradwell
|
||||
// Under BSD-style license (see LICENSE file)
|
||||
// Originally from https://github.com/xdave/ff
|
||||
|
||||
package xtreamcodes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// abs is a quick integer function (math package doesn't use ints).
|
||||
func abs(num int) int {
|
||||
if num >= 0 {
|
||||
return num
|
||||
}
|
||||
return -num
|
||||
}
|
||||
|
||||
// Tags represents either Format or FFMPEGStreamInfo tags embedded in the file.
|
||||
type Tags map[string]interface{}
|
||||
|
||||
// Format describes container format info.
|
||||
type Format struct {
|
||||
BitRate string `json:"bit_rate"`
|
||||
Duration string `json:"duration"`
|
||||
Filename string `json:"filename"`
|
||||
LongName string `json:"format_long_name"`
|
||||
Name string `json:"format_name"`
|
||||
NumPrograms int `json:"nb_programs"`
|
||||
NumStreams int `json:"nb_streams"`
|
||||
ProbeScore int `json:"probe_score"`
|
||||
Size string `json:"size"`
|
||||
StartTime string `json:"start_time"`
|
||||
Tags Tags `json:"tags"`
|
||||
}
|
||||
|
||||
// Disposition of FFMPEGStreamInfo
|
||||
type Disposition struct {
|
||||
AttachedPic int `json:"attached_pic"`
|
||||
CleanEffects int `json:"clean_effects"`
|
||||
Comment int `json:"comment"`
|
||||
Default int `json:"default"`
|
||||
Dub int `json:"dub"`
|
||||
Forced int `json:"forced"`
|
||||
HearingImpaired int `json:"hearing_impaired"`
|
||||
Karaoke int `json:"karaoke"`
|
||||
Lyrics int `json:"lyrics"`
|
||||
Original int `json:"original"`
|
||||
VisualImpaired int `json:"visual_impaired"`
|
||||
}
|
||||
|
||||
// SideData describes metadata. More fields may be added later as needed.
|
||||
type SideData struct {
|
||||
Displaymatrix string `json:"displaymatrix"`
|
||||
Rotation int `json:"rotation"`
|
||||
Size int `json:"side_data_size"`
|
||||
Type string `json:"side_data_type"`
|
||||
}
|
||||
|
||||
// StreamType is used for enumerating the CodecType field in FFMPEGStreamInfo.
|
||||
type StreamType string
|
||||
|
||||
const (
|
||||
// VideoStream is the constant for video streams.
|
||||
VideoStream StreamType = "video"
|
||||
// AudioStream is the constant for audio streams.
|
||||
AudioStream = "audio"
|
||||
)
|
||||
|
||||
// FFMPEGStreamInfo represents any kind of stream (Audio, Video, etc)
|
||||
type FFMPEGStreamInfo struct {
|
||||
AvgFrameRate string `json:"avg_frame_rate"`
|
||||
BitRate string `json:"bit_rate"`
|
||||
BitsPerRawSample string `json:"bits_per_raw_sample"`
|
||||
BitsPerSample int `json:"bits_per_sample"`
|
||||
ChannelLayout string `json:"channel_layout"`
|
||||
Channels int `json:"channels"`
|
||||
ChromaLocation string `json:"chroma_location"`
|
||||
CodecLongName string `json:"codec_long_name"`
|
||||
CodecName string `json:"codec_name"`
|
||||
CodecTag string `json:"codec_tag"`
|
||||
CodecTagString string `json:"codec_tag_string"`
|
||||
CodecTimeBase string `json:"codec_time_base"`
|
||||
CodecType StreamType `json:"codec_type"`
|
||||
CodedHeight int `json:"coded_height"`
|
||||
CodedWidth int `json:"coded_width"`
|
||||
ColorPrimaries string `json:"color_primaries"`
|
||||
ColorRange string `json:"color_range"`
|
||||
ColorSpace string `json:"color_space"`
|
||||
ColorTransfer string `json:"color_transfer"`
|
||||
DisplayAspectRatio string `json:"display_aspect_ratio"`
|
||||
DivxPacked string `json:"divx_packed"`
|
||||
DmixMode string `json:"dmix_mode"`
|
||||
Duration string `json:"duration"`
|
||||
DurationTs int `json:"duration_ts"`
|
||||
HasBFrames int `json:"has_b_frames"`
|
||||
Height int `json:"height"`
|
||||
ID string `json:"id"`
|
||||
Index int `json:"index"`
|
||||
IsAvc string `json:"is_avc"`
|
||||
Level int `json:"level"`
|
||||
LoroCmixlev string `json:"loro_cmixlev"`
|
||||
LoroSurmixlev string `json:"loro_surmixlev"`
|
||||
LtrtCmixlev string `json:"ltrt_cmixlev"`
|
||||
LtrtSurmixlev string `json:"ltrt_surmixlev"`
|
||||
MaxBitRate string `json:"max_bit_rate"`
|
||||
NalLengthSize string `json:"nal_length_size"`
|
||||
NumFrames string `json:"nb_frames"`
|
||||
PixFmt string `json:"pix_fmt"`
|
||||
Profile string `json:"profile"`
|
||||
QuarterSample string `json:"quarter_sample"`
|
||||
FrameRate string `json:"r_frame_rate"`
|
||||
Refs int `json:"refs"`
|
||||
SampleAspectRatio string `json:"sample_aspect_ratio"`
|
||||
SampleFmt string `json:"sample_fmt"`
|
||||
SampleRate string `json:"sample_rate"`
|
||||
StartPts int `json:"start_pts"`
|
||||
StartTime string `json:"start_time"`
|
||||
TimeBase string `json:"time_base"`
|
||||
Timecode string `json:"timecode"`
|
||||
Width int `json:"width"`
|
||||
Tags Tags `json:"tags"`
|
||||
Disposition Disposition `json:"disposition"`
|
||||
SideDataList []SideData `json:"side_data_list"`
|
||||
}
|
||||
|
||||
// IsRotated returns true if the stream is rotated.
|
||||
func (s FFMPEGStreamInfo) IsRotated() (bool, error) {
|
||||
rot, err := s.Rotation()
|
||||
return rot != 0, err
|
||||
}
|
||||
|
||||
// Rotation gets the rotation value of video stream, either from stream side-data, or tags.
|
||||
// Returns 0 if it's not rotated, or if we can't figure it out.
|
||||
func (s FFMPEGStreamInfo) Rotation() (int, error) {
|
||||
for _, sdata := range s.SideDataList {
|
||||
return abs(sdata.Rotation), nil
|
||||
}
|
||||
rotationTag, ok := s.Tags["rotate"]
|
||||
if ok {
|
||||
rotation, ok := rotationTag.(string)
|
||||
if ok {
|
||||
val, valErr := strconv.Atoi(rotation)
|
||||
if valErr != nil {
|
||||
return 0, valErr
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// ProbeInfo is just a nice structure that represents the JSON data returned by ffprobe.
|
||||
// Mostly auto-generated with http://mholt.github.io/json-to-go
|
||||
type ProbeInfo struct {
|
||||
Format Format `json:"format"`
|
||||
Streams []FFMPEGStreamInfo `json:"streams"`
|
||||
}
|
||||
|
||||
// NewInfo returns a new ProbeInfo structure from the input JSON
|
||||
func NewInfo(jsonData string) (*ProbeInfo, error) {
|
||||
info := new(ProbeInfo)
|
||||
err := json.Unmarshal([]byte(jsonData), info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// FilterStreams filters out streams of the given StreamType.
|
||||
// Returns a []FFMPEGStreamInfo slice, even if it's empty
|
||||
func (info ProbeInfo) FilterStreams(t StreamType) []FFMPEGStreamInfo {
|
||||
streams := []FFMPEGStreamInfo{}
|
||||
for _, stream := range info.Streams {
|
||||
if stream.CodecType == t {
|
||||
streams = append(streams, stream)
|
||||
}
|
||||
}
|
||||
return streams
|
||||
}
|
||||
306
vendor/github.com/tellytv/go.xtream-codes/structs.go
generated
vendored
Normal file
306
vendor/github.com/tellytv/go.xtream-codes/structs.go
generated
vendored
Normal file
@@ -0,0 +1,306 @@
|
||||
package xtreamcodes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Timestamp is a helper struct to convert unix timestamp ints and strings to time.Time.
|
||||
type Timestamp struct {
|
||||
time.Time
|
||||
quoted bool
|
||||
}
|
||||
|
||||
// MarshalJSON returns the Unix timestamp as a string.
|
||||
func (t Timestamp) MarshalJSON() ([]byte, error) {
|
||||
if t.quoted {
|
||||
return []byte(`"` + strconv.FormatInt(t.Time.Unix(), 10) + `"`), nil
|
||||
}
|
||||
return []byte(strconv.FormatInt(t.Time.Unix(), 10)), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON converts the int or string to a Unix timestamp.
|
||||
func (t *Timestamp) UnmarshalJSON(b []byte) error {
|
||||
// Timestamps are sometimes quoted, sometimes not, lets just always remove quotes just in case...
|
||||
t.quoted = strings.Contains(string(b), `"`)
|
||||
ts, err := strconv.Atoi(strings.Replace(string(b), `"`, "", -1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.Time = time.Unix(int64(ts), 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConvertibleBoolean is a helper type to allow JSON documents using 0/1 or "true" and "false" be converted to bool.
|
||||
type ConvertibleBoolean struct {
|
||||
bool
|
||||
quoted bool
|
||||
}
|
||||
|
||||
// MarshalJSON returns a 0 or 1 depending on bool state.
|
||||
func (bit ConvertibleBoolean) MarshalJSON() ([]byte, error) {
|
||||
var bitSetVar int8
|
||||
if bit.bool {
|
||||
bitSetVar = 1
|
||||
}
|
||||
|
||||
if bit.quoted {
|
||||
return json.Marshal(fmt.Sprint(bitSetVar))
|
||||
}
|
||||
|
||||
return json.Marshal(bitSetVar)
|
||||
}
|
||||
|
||||
// UnmarshalJSON converts a 0, 1, true or false into a bool
|
||||
func (bit *ConvertibleBoolean) UnmarshalJSON(data []byte) error {
|
||||
bit.quoted = strings.Contains(string(data), `"`)
|
||||
// Bools as ints are sometimes quoted, sometimes not, lets just always remove quotes just in case...
|
||||
asString := strings.Replace(string(data), `"`, "", -1)
|
||||
if asString == "1" || asString == "true" {
|
||||
bit.bool = true
|
||||
} else if asString == "0" || asString == "false" {
|
||||
bit.bool = false
|
||||
} else {
|
||||
return fmt.Errorf("Boolean unmarshal error: invalid input %s", asString)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// jsonInt is a int64 which unmarshals from JSON
|
||||
// as either unquoted or quoted (with any amount
|
||||
// of internal leading/trailing whitespace).
|
||||
// Originally found at https://bit.ly/2NkJ0SK and
|
||||
// https://play.golang.org/p/KNPxDL1yqL
|
||||
type jsonInt int64
|
||||
|
||||
func (f jsonInt) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(int64(f))
|
||||
}
|
||||
|
||||
func (f *jsonInt) UnmarshalJSON(data []byte) error {
|
||||
var v int64
|
||||
|
||||
data = bytes.Trim(data, `" `)
|
||||
|
||||
err := json.Unmarshal(data, &v)
|
||||
*f = jsonInt(v)
|
||||
return err
|
||||
}
|
||||
|
||||
// ServerInfo describes the state of the Xtream-Codes server.
|
||||
type ServerInfo struct {
|
||||
HTTPSPort int `json:"https_port,string"`
|
||||
Port int `json:"port,string"`
|
||||
Process bool `json:"process"`
|
||||
RTMPPort int `json:"rtmp_port,string"`
|
||||
Protocol string `json:"server_protocol"`
|
||||
TimeNow string `json:"time_now"`
|
||||
TimestampNow Timestamp `json:"timestamp_now,string"`
|
||||
Timezone string `json:"timezone"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// UserInfo is the current state of the user as it relates to the Xtream-Codes server.
|
||||
type UserInfo struct {
|
||||
ActiveConnections int `json:"active_cons,string"`
|
||||
AllowedOutputFormats []string `json:"allowed_output_formats"`
|
||||
Auth ConvertibleBoolean `json:"auth"`
|
||||
CreatedAt Timestamp `json:"created_at"`
|
||||
ExpDate *Timestamp `json:"exp_date"`
|
||||
IsTrial ConvertibleBoolean `json:"is_trial,string"`
|
||||
MaxConnections int `json:"max_connections,string"`
|
||||
Message string `json:"message"`
|
||||
Password string `json:"password"`
|
||||
Status string `json:"status"`
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
// AuthenticationResponse is a container for what the server returns after the initial authentication.
|
||||
type AuthenticationResponse struct {
|
||||
ServerInfo ServerInfo `json:"server_info"`
|
||||
UserInfo UserInfo `json:"user_info"`
|
||||
}
|
||||
|
||||
// Category describes a grouping of Stream.
|
||||
type Category struct {
|
||||
ID int `json:"category_id,string"`
|
||||
Name string `json:"category_name"`
|
||||
Parent int `json:"parent_id"`
|
||||
|
||||
// Set by us, not Xtream.
|
||||
Type string `json:"-"`
|
||||
}
|
||||
|
||||
// Stream is a streamble video source.
|
||||
type Stream struct {
|
||||
Added *Timestamp `json:"added"`
|
||||
CategoryID int `json:"category_id,string"`
|
||||
ContainerExtension string `json:"container_extension"`
|
||||
CustomSid string `json:"custom_sid"`
|
||||
DirectSource string `json:"direct_source,omitempty"`
|
||||
EPGChannelID string `json:"epg_channel_id"`
|
||||
Icon string `json:"stream_icon"`
|
||||
ID int `json:"stream_id"`
|
||||
Name string `json:"name"`
|
||||
Number int `json:"num"`
|
||||
Rating FlexFloat `json:"rating"`
|
||||
Rating5based float64 `json:"rating_5based"`
|
||||
TVArchive int `json:"tv_archive"`
|
||||
TVArchiveDuration *jsonInt `json:"tv_archive_duration"`
|
||||
Type string `json:"stream_type"`
|
||||
}
|
||||
|
||||
type FlexFloat float64
|
||||
|
||||
func (ff *FlexFloat) UnmarshalJSON(b []byte) error {
|
||||
if b[0] != '"' {
|
||||
return json.Unmarshal(b, (*float64)(ff))
|
||||
}
|
||||
|
||||
var s string
|
||||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(s) == 0 {
|
||||
s = "0"
|
||||
}
|
||||
|
||||
f, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
f = 0
|
||||
}
|
||||
*ff = FlexFloat(f)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SeriesInfo contains information about a TV series.
|
||||
type SeriesInfo struct {
|
||||
BackdropPath *JSONStringSlice `json:"backdrop_path,omitempty"`
|
||||
Cast string `json:"cast"`
|
||||
CategoryID *int `json:"category_id,string"`
|
||||
Cover string `json:"cover"`
|
||||
Director string `json:"director"`
|
||||
EpisodeRunTime string `json:"episode_run_time"`
|
||||
Genre string `json:"genre"`
|
||||
LastModified *Timestamp `json:"last_modified,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Num int `json:"num"`
|
||||
Plot string `json:"plot"`
|
||||
Rating int `json:"rating,string"`
|
||||
Rating5 float64 `json:"rating_5based"`
|
||||
ReleaseDate string `json:"releaseDate"`
|
||||
SeriesID int `json:"series_id"`
|
||||
StreamType string `json:"stream_type"`
|
||||
YoutubeTrailer string `json:"youtube_trailer"`
|
||||
}
|
||||
|
||||
type SeriesEpisode struct {
|
||||
Added string `json:"added"`
|
||||
ContainerExtension string `json:"container_extension"`
|
||||
CustomSid string `json:"custom_sid"`
|
||||
DirectSource string `json:"direct_source"`
|
||||
EpisodeNum int `json:"episode_num"`
|
||||
ID string `json:"id"`
|
||||
Info struct {
|
||||
Audio FFMPEGStreamInfo `json:"audio"`
|
||||
Bitrate int `json:"bitrate"`
|
||||
Duration string `json:"duration"`
|
||||
DurationSecs int `json:"duration_secs"`
|
||||
MovieImage string `json:"movie_image"`
|
||||
Name string `json:"name"`
|
||||
Plot string `json:"plot"`
|
||||
Rating FlexFloat `json:"rating"`
|
||||
ReleaseDate string `json:"releasedate"`
|
||||
Video FFMPEGStreamInfo `json:"video"`
|
||||
} `json:"info"`
|
||||
Season int `json:"season"`
|
||||
Title string `json:"title"`
|
||||
}
|
||||
|
||||
type Series struct {
|
||||
Episodes map[string][]SeriesEpisode `json:"episodes"`
|
||||
Info SeriesInfo `json:"info"`
|
||||
Seasons []interface{} `json:"seasons"`
|
||||
}
|
||||
|
||||
// VideoOnDemandInfo contains information about a video on demand stream.
|
||||
type VideoOnDemandInfo struct {
|
||||
Info struct {
|
||||
Audio FFMPEGStreamInfo `json:"audio"`
|
||||
BackdropPath []string `json:"backdrop_path"`
|
||||
Bitrate int `json:"bitrate"`
|
||||
Cast string `json:"cast"`
|
||||
Director string `json:"director"`
|
||||
Duration string `json:"duration"`
|
||||
DurationSecs int `json:"duration_secs"`
|
||||
Genre string `json:"genre"`
|
||||
MovieImage string `json:"movie_image"`
|
||||
Plot string `json:"plot"`
|
||||
Rating string `json:"rating"`
|
||||
ReleaseDate string `json:"releasedate"`
|
||||
TmdbID string `json:"tmdb_id"`
|
||||
Video FFMPEGStreamInfo `json:"video"`
|
||||
YoutubeTrailer string `json:"youtube_trailer"`
|
||||
} `json:"info"`
|
||||
MovieData struct {
|
||||
Added Timestamp `json:"added"`
|
||||
CategoryID int `json:"category_id,string"`
|
||||
ContainerExtension string `json:"container_extension"`
|
||||
CustomSid string `json:"custom_sid"`
|
||||
DirectSource string `json:"direct_source"`
|
||||
Name string `json:"name"`
|
||||
StreamID int `json:"stream_id"`
|
||||
} `json:"movie_data"`
|
||||
}
|
||||
|
||||
type epgContainer struct {
|
||||
EPGListings []EPGInfo `json:"epg_listings"`
|
||||
}
|
||||
|
||||
// EPGInfo describes electronic programming guide information of a stream.
|
||||
type EPGInfo struct {
|
||||
ChannelID string `json:"channel_id"`
|
||||
Description Base64Value `json:"description"`
|
||||
End string `json:"end"`
|
||||
EPGID int `json:"epg_id,string"`
|
||||
HasArchive ConvertibleBoolean `json:"has_archive"`
|
||||
ID int `json:"id,string"`
|
||||
Lang string `json:"lang"`
|
||||
NowPlaying ConvertibleBoolean `json:"now_playing"`
|
||||
Start string `json:"start"`
|
||||
StartTimestamp Timestamp `json:"start_timestamp"`
|
||||
StopTimestamp Timestamp `json:"stop_timestamp"`
|
||||
Title Base64Value `json:"title"`
|
||||
}
|
||||
|
||||
// JSONStringSlice is a struct containing a slice of strings.
|
||||
// It is needed for cases in which we may get an array or may get
|
||||
// a single string in a JSON response.
|
||||
type JSONStringSlice struct {
|
||||
Slice []string `json:"-"`
|
||||
SingleString bool `json:"-"`
|
||||
}
|
||||
|
||||
// MarshalJSON returns b as the JSON encoding of b.
|
||||
func (b JSONStringSlice) MarshalJSON() ([]byte, error) {
|
||||
if !b.SingleString {
|
||||
return json.Marshal(b.Slice)
|
||||
}
|
||||
return json.Marshal(b.Slice[0])
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *b to a copy of data.
|
||||
func (b *JSONStringSlice) UnmarshalJSON(data []byte) error {
|
||||
if data[0] == '"' {
|
||||
data = append([]byte(`[`), data...)
|
||||
data = append(data, []byte(`]`)...)
|
||||
b.SingleString = true
|
||||
}
|
||||
|
||||
return json.Unmarshal(data, &b.Slice)
|
||||
}
|
||||
316
vendor/github.com/tellytv/go.xtream-codes/xtream-codes.go
generated
vendored
Normal file
316
vendor/github.com/tellytv/go.xtream-codes/xtream-codes.go
generated
vendored
Normal file
@@ -0,0 +1,316 @@
|
||||
// Package xtreamcodes provides a Golang interface to the Xtream-Codes IPTV Server API.
|
||||
package xtreamcodes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var userAgent = "go.xstream-codes (Go-http-client/1.1)"
|
||||
|
||||
// XtreamClient is the client used to communicate with a Xtream-Codes server.
|
||||
type XtreamClient struct {
|
||||
Username string
|
||||
Password string
|
||||
BaseURL string
|
||||
|
||||
ServerInfo ServerInfo
|
||||
UserInfo UserInfo
|
||||
|
||||
// Our HTTP client to communicate with Xtream
|
||||
HTTP *http.Client
|
||||
|
||||
// We store an internal map of Streams for use with GetStreamURL
|
||||
streams map[int]Stream
|
||||
}
|
||||
|
||||
// NewClient returns an initialized XtreamClient with the given values.
|
||||
func NewClient(username, password, baseURL string) (*XtreamClient, error) {
|
||||
|
||||
_, parseURLErr := url.Parse(baseURL)
|
||||
if parseURLErr != nil {
|
||||
return nil, fmt.Errorf("error parsing url: %s", parseURLErr.Error())
|
||||
}
|
||||
|
||||
client := &XtreamClient{
|
||||
Username: username,
|
||||
Password: password,
|
||||
BaseURL: baseURL,
|
||||
|
||||
HTTP: http.DefaultClient,
|
||||
|
||||
streams: make(map[int]Stream),
|
||||
}
|
||||
|
||||
authData, authErr := client.sendRequest("", nil)
|
||||
if authErr != nil {
|
||||
return nil, fmt.Errorf("error sending authentication request: %s", authErr.Error())
|
||||
}
|
||||
|
||||
a := &AuthenticationResponse{}
|
||||
|
||||
if jsonErr := json.Unmarshal(authData, &a); jsonErr != nil {
|
||||
return nil, fmt.Errorf("error unmarshaling json: %s", jsonErr.Error())
|
||||
}
|
||||
|
||||
client.ServerInfo = a.ServerInfo
|
||||
client.UserInfo = a.UserInfo
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// GetStreamURL will return a stream URL string for the given streamID and wantedFormat.
|
||||
func (c *XtreamClient) GetStreamURL(streamID int, wantedFormat string) (string, error) {
|
||||
|
||||
// For Live Streams the main format is
|
||||
// http(s)://domain:port/live/username/password/streamID.ext ( In allowed_output_formats element you have the available ext )
|
||||
// For VOD Streams the format is:
|
||||
// http(s)://domain:port/movie/username/password/streamID.ext ( In target_container element you have the available ext )
|
||||
// For Series Streams the format is
|
||||
// http(s)://domain:port/series/username/password/streamID.ext ( In target_container element you have the available ext )
|
||||
|
||||
validFormat := false
|
||||
|
||||
for _, allowedFormat := range c.UserInfo.AllowedOutputFormats {
|
||||
if wantedFormat == allowedFormat {
|
||||
validFormat = true
|
||||
}
|
||||
}
|
||||
|
||||
if !validFormat {
|
||||
return "", fmt.Errorf("%s is not an allowed output format", wantedFormat)
|
||||
}
|
||||
|
||||
if _, ok := c.streams[streamID]; !ok {
|
||||
return "", fmt.Errorf("%d is not a valid stream id", streamID)
|
||||
}
|
||||
|
||||
stream := c.streams[streamID]
|
||||
|
||||
return fmt.Sprintf("%s/%s/%s/%s/%d.%s", c.BaseURL, stream.Type, c.Username, c.Password, stream.ID, wantedFormat), nil
|
||||
}
|
||||
|
||||
// GetLiveCategories will return a slice of categories for live streams.
|
||||
func (c *XtreamClient) GetLiveCategories() ([]Category, error) {
|
||||
return c.GetCategories("live")
|
||||
}
|
||||
|
||||
// GetVideoOnDemandCategories will return a slice of categories for VOD streams.
|
||||
func (c *XtreamClient) GetVideoOnDemandCategories() ([]Category, error) {
|
||||
return c.GetCategories("vod")
|
||||
}
|
||||
|
||||
// GetSeriesCategories will return a slice of categories for series streams.
|
||||
func (c *XtreamClient) GetSeriesCategories() ([]Category, error) {
|
||||
return c.GetCategories("series")
|
||||
}
|
||||
|
||||
// GetCategories is a helper function used by GetLiveCategories, GetVideoOnDemandCategories and
|
||||
// GetSeriesCategories to reduce duplicate code.
|
||||
func (c *XtreamClient) GetCategories(catType string) ([]Category, error) {
|
||||
catData, catErr := c.sendRequest(fmt.Sprintf("get_%s_categories", catType), nil)
|
||||
if catErr != nil {
|
||||
return nil, catErr
|
||||
}
|
||||
|
||||
cats := make([]Category, 0)
|
||||
|
||||
jsonErr := json.Unmarshal(catData, &cats)
|
||||
|
||||
for idx := range cats {
|
||||
cats[idx].Type = catType
|
||||
}
|
||||
|
||||
return cats, jsonErr
|
||||
}
|
||||
|
||||
// GetLiveStreams will return a slice of live streams.
|
||||
// You can also optionally provide a categoryID to limit the output to members of that category.
|
||||
func (c *XtreamClient) GetLiveStreams(categoryID string) ([]Stream, error) {
|
||||
return c.GetStreams("live", categoryID)
|
||||
}
|
||||
|
||||
// GetVideoOnDemandStreams will return a slice of VOD streams.
|
||||
// You can also optionally provide a categoryID to limit the output to members of that category.
|
||||
func (c *XtreamClient) GetVideoOnDemandStreams(categoryID string) ([]Stream, error) {
|
||||
return c.GetStreams("vod", categoryID)
|
||||
}
|
||||
|
||||
// GetStreams is a helper function used by GetLiveStreams and GetVideoOnDemandStreams
|
||||
// to reduce duplicate code.
|
||||
func (c *XtreamClient) GetStreams(streamAction, categoryID string) ([]Stream, error) {
|
||||
var params url.Values
|
||||
if categoryID != "" {
|
||||
params = url.Values{}
|
||||
params.Add("category_id", categoryID)
|
||||
}
|
||||
|
||||
// For whatever reason, unlike live and vod, series streams action doesn't have "_streams".
|
||||
if streamAction != "series" {
|
||||
streamAction = fmt.Sprintf("%s_streams", streamAction)
|
||||
}
|
||||
|
||||
streamData, streamErr := c.sendRequest(fmt.Sprintf("get_%s", streamAction), params)
|
||||
if streamErr != nil {
|
||||
return nil, streamErr
|
||||
}
|
||||
|
||||
streams := make([]Stream, 0)
|
||||
|
||||
if jsonErr := json.Unmarshal(streamData, &streams); jsonErr != nil {
|
||||
return nil, jsonErr
|
||||
}
|
||||
|
||||
for _, stream := range streams {
|
||||
c.streams[stream.ID] = stream
|
||||
}
|
||||
|
||||
return streams, nil
|
||||
}
|
||||
|
||||
// GetSeries will return a slice of all available Series.
|
||||
// You can also optionally provide a categoryID to limit the output to members of that category.
|
||||
func (c *XtreamClient) GetSeries(categoryID string) ([]SeriesInfo, error) {
|
||||
var params url.Values
|
||||
if categoryID != "" {
|
||||
params = url.Values{}
|
||||
params.Add("category_id", categoryID)
|
||||
}
|
||||
|
||||
seriesData, seriesErr := c.sendRequest("get_series", params)
|
||||
if seriesErr != nil {
|
||||
return nil, seriesErr
|
||||
}
|
||||
|
||||
seriesInfos := make([]SeriesInfo, 0)
|
||||
|
||||
if jsonErr := json.Unmarshal(seriesData, &seriesInfos); jsonErr != nil {
|
||||
return nil, jsonErr
|
||||
}
|
||||
|
||||
return seriesInfos, nil
|
||||
}
|
||||
|
||||
// GetSeriesInfo will return a series info for the given seriesID.
|
||||
func (c *XtreamClient) GetSeriesInfo(seriesID string) (*Series, error) {
|
||||
if seriesID == "" {
|
||||
return nil, fmt.Errorf("series ID can not be empty")
|
||||
}
|
||||
|
||||
seriesData, seriesErr := c.sendRequest("get_series_info", url.Values{"series_id": []string{seriesID}})
|
||||
if seriesErr != nil {
|
||||
return nil, seriesErr
|
||||
}
|
||||
|
||||
seriesInfo := &Series{}
|
||||
|
||||
jsonErr := json.Unmarshal(seriesData, &seriesInfo)
|
||||
|
||||
return seriesInfo, jsonErr
|
||||
}
|
||||
|
||||
// GetVideoOnDemandInfo will return VOD info for the given vodID.
|
||||
func (c *XtreamClient) GetVideoOnDemandInfo(vodID string) (*VideoOnDemandInfo, error) {
|
||||
if vodID == "" {
|
||||
return nil, fmt.Errorf("vod ID can not be empty")
|
||||
}
|
||||
|
||||
vodData, vodErr := c.sendRequest("get_vod_info", url.Values{"vod_id": []string{vodID}})
|
||||
if vodErr != nil {
|
||||
return nil, vodErr
|
||||
}
|
||||
|
||||
vodInfo := &VideoOnDemandInfo{}
|
||||
|
||||
jsonErr := json.Unmarshal(vodData, &vodInfo)
|
||||
|
||||
return vodInfo, jsonErr
|
||||
}
|
||||
|
||||
// GetShortEPG returns a short version of the EPG for the given streamID. If no limit is provided, the next 4 items in the EPG will be returned.
|
||||
func (c *XtreamClient) GetShortEPG(streamID string, limit int) ([]EPGInfo, error) {
|
||||
return c.getEPG("get_short_epg", streamID, limit)
|
||||
}
|
||||
|
||||
// GetEPG returns the full EPG for the given streamID.
|
||||
func (c *XtreamClient) GetEPG(streamID string) ([]EPGInfo, error) {
|
||||
return c.getEPG("get_simple_data_table", streamID, 0)
|
||||
}
|
||||
|
||||
// GetXMLTV will return a slice of bytes for the XMLTV EPG file available from the provider.
|
||||
func (c *XtreamClient) GetXMLTV() ([]byte, error) {
|
||||
xmlTVData, xmlTVErr := c.sendRequest("xmltv.php", nil)
|
||||
if xmlTVErr != nil {
|
||||
return nil, xmlTVErr
|
||||
}
|
||||
|
||||
return xmlTVData, xmlTVErr
|
||||
}
|
||||
|
||||
func (c *XtreamClient) getEPG(action, streamID string, limit int) ([]EPGInfo, error) {
|
||||
if streamID == "" {
|
||||
return nil, fmt.Errorf("stream ID can not be empty")
|
||||
}
|
||||
|
||||
params := url.Values{"stream_id": []string{streamID}}
|
||||
if limit > 0 {
|
||||
params.Add("limit", strconv.Itoa(limit))
|
||||
}
|
||||
|
||||
epgData, epgErr := c.sendRequest(action, params)
|
||||
if epgErr != nil {
|
||||
return nil, epgErr
|
||||
}
|
||||
|
||||
epgContainer := &epgContainer{}
|
||||
|
||||
jsonErr := json.Unmarshal(epgData, &epgContainer)
|
||||
|
||||
return epgContainer.EPGListings, jsonErr
|
||||
}
|
||||
|
||||
func (c *XtreamClient) sendRequest(action string, parameters url.Values) ([]byte, error) {
|
||||
file := "player_api.php"
|
||||
if action == "xmltv.php" {
|
||||
file = action
|
||||
}
|
||||
url := fmt.Sprintf("%s/%s?username=%s&password=%s", c.BaseURL, file, c.Username, c.Password)
|
||||
if action != "" {
|
||||
url = fmt.Sprintf("%s&action=%s", url, action)
|
||||
}
|
||||
if parameters != nil {
|
||||
url = fmt.Sprintf("%s&%s", url, parameters.Encode())
|
||||
}
|
||||
|
||||
request, httpErr := http.NewRequest("GET", url, nil)
|
||||
if httpErr != nil {
|
||||
return nil, httpErr
|
||||
}
|
||||
|
||||
request.Header.Set("User-Agent", userAgent)
|
||||
|
||||
response, httpErr := c.HTTP.Do(request)
|
||||
if httpErr != nil {
|
||||
return nil, fmt.Errorf("cannot reach server. %v", httpErr)
|
||||
}
|
||||
|
||||
if response.StatusCode > 399 {
|
||||
return nil, fmt.Errorf("status code was %d, expected 2XX-3XX", response.StatusCode)
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
if _, copyErr := io.Copy(buf, response.Body); copyErr != nil {
|
||||
return nil, copyErr
|
||||
}
|
||||
|
||||
if closeErr := response.Body.Close(); closeErr != nil {
|
||||
return nil, fmt.Errorf("cannot read response. %v", closeErr)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
@@ -1,5 +1,3 @@
|
||||
# github.com/davecgh/go-spew v1.1.1
|
||||
github.com/davecgh/go-spew/spew
|
||||
# github.com/fsnotify/fsnotify v1.4.7
|
||||
github.com/fsnotify/fsnotify
|
||||
# github.com/gin-contrib/cors v0.0.0-20190226021855-50921afdc5c1
|
||||
@@ -57,6 +55,8 @@ github.com/spf13/jwalterweatherman
|
||||
github.com/spf13/pflag
|
||||
# github.com/spf13/viper v1.3.1
|
||||
github.com/spf13/viper
|
||||
# github.com/tellytv/go.xtream-codes v0.0.0-20190114013623-9b74dcb500e4
|
||||
github.com/tellytv/go.xtream-codes
|
||||
# github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2
|
||||
github.com/ugorji/go/codec
|
||||
# golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb
|
||||
|
||||
Reference in New Issue
Block a user