<commit_before>package ginoauth2

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/golang/glog"
	"golang.org/x/oauth2"
)

var AuthInfoURL string
var Realms []string = []string{"employees", "services"}

//var Transport http.Transport = http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
var Transport http.Transport = http.Transport{}

type AccessTuple struct {
	Realm string // p.e. "employees", "services"
	Uid   string // UnixName
	Cn    string // RealName
}

type TokenContainer struct {
	Token     *oauth2.Token
	Scopes    map[string]interface{} // LDAP record vom Benutzer (cn, ..
	GrantType string                 // password, ??
	Realm     string                 // services, employees
}

func extractToken(r *http.Request) (*oauth2.Token, error) {
	hdr := r.Header.Get("Authorization")
	if hdr == "" {
		return nil, errors.New("No authorization header")
	}

	th := strings.Split(hdr, " ")
	if len(th) != 2 {
		return nil, errors.New("Incomplete authorization header")
	}

	return &oauth2.Token{AccessToken: th[1], TokenType: th[0]}, nil
}

func RequestAuthInfo(t *oauth2.Token) ([]byte, error) {
	var uv = make(url.Values)
	// uv.Set("realm", o.Realm)
	uv.Set("access_token", t.AccessToken)
	info_url := AuthInfoURL + "?" + uv.Encode()
	client := &http.Client{Transport: &Transport}
	req, err := http.NewRequest("GET", info_url, nil)
	if err != nil {
		return nil, err
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	return ioutil.ReadAll(resp.Body)
}

func ParseTokenContainer(t *oauth2.Token, data map[string]interface{}) (*TokenContainer, error) {
	tdata := make(map[string]interface{})

	ttype := data["token_type"].(string)
	gtype := data["grant_type"].(string)

	realm := data["realm"].(string)
	exp := data["expires_in"].(float64)
	tok := data["access_token"].(string)
	if ttype != t.TokenType {
		return nil, errors.New("Token type mismatch")
	}
	if tok != t.AccessToken {
		return nil, errors.New("Mismatch between verify request and answer")
	}

	scopes := data["scope"].([]interface{})
	for _, scope := range scopes {
		sscope := scope.(string)
		sval, ok := data[sscope]
		if ok {
			tdata[sscope] = sval
		}
	}

	return &TokenContainer{
		Token: &oauth2.Token{
			AccessToken: tok,
			TokenType:   ttype,
			Expiry:      time.Now().Add(time.Duration(exp) * time.Second),
		},
		Scopes:    tdata,
		Realm:     realm,
		GrantType: gtype,
	}, nil
}

func GetTokenContainer(token *oauth2.Token) (*TokenContainer, error) {
	body, err := RequestAuthInfo(token)
	if err != nil {
		glog.Errorf("RequestAuthInfo failed caused by: %s", err)
		return nil, err
	}
	// extract AuthInfo
	var data map[string]interface{}
	err = json.Unmarshal(body, &data)
	if err != nil {
		glog.Errorf("JSON.Unmarshal failed caused by: %s", err)
		return nil, err
	}
	if _, ok := data["error_description"]; ok {
		var s string
		s = data["error_description"].(string)
		glog.Errorf("RequestAuthInfo returned an error: %s", s)
		return nil, errors.New(s)
	}
	return ParseTokenContainer(token, data)
}

func getTokenContainer(ctx *gin.Context) (*TokenContainer, bool) {
	var oauth_token *oauth2.Token
	var tc *TokenContainer
	var err error

	if oauth_token, err = extractToken(ctx.Request); err != nil {
		glog.Errorf("Can not extract oauth2.Token, caused by: %s", err)
		return nil, false
	}
	if !oauth_token.Valid() {
		glog.Infof("Invalid Token - nil or expired")
		return nil, false
	}

	if tc, err = GetTokenContainer(oauth_token); err != nil {
		glog.Errorf("Can not extract TokenContainer, caused by: %s", err)
		return nil, false
	}

	return tc, true
}

//
// TokenContainer
//
// Validates that the AccessToken within TokenContainer is not expired and not empty.
func (t *TokenContainer) Valid() bool {
	if t.Token == nil {
		return false
	}
	return t.Token.Valid()
}

// Authorization function that checks UID scope
// TokenContainer must be Valid
// []AccessTuple: [{Realm:employee Uid:sszuecs Cn:Sandor Szücs} {Realm:employee Uid:njuettner Cn:Nick Jüttner}]
func UidCheck(tc *TokenContainer, access_tuple []AccessTuple) bool {
	uid := tc.Scopes["uid"].(string)
	for idx := range access_tuple {
		at := access_tuple[idx]
		if uid == at.Uid {
			glog.Infof("Grant access to %s\n", uid)
			return true
		}
	}

	return false
}

// Router middleware that can be used to get an authenticated and authorized service for the whole router group.
// Example:
//
//      var endpoints oauth2.Endpoint = oauth2.Endpoint{
//	        AuthURL:  "https://token.oauth2.corp.com/access_token",
//	        TokenURL: "https://oauth2.corp.com/corp/oauth2/tokeninfo",
//      }
//      var acl []ginoauth2.AccessTuple = []ginoauth2.AccessTuple{{"employee", 1070, "sszuecs"}, {"employee", 1114, "njuettner"}}
//      router := gin.Default()
//	private := router.Group("")
//	private.Use(ginoauth2.Auth(ginoatuh2.UidCheck, ginoauth2.endpoints, acl))
//	private.GET("/api/private", func(c *gin.Context) {
//		c.JSON(200, gin.H{"message": "Hello from private"})
//	})
//
func Auth(accessCheckFunction func(tc *TokenContainer, access_tuple []AccessTuple) bool, endpoints oauth2.Endpoint, users []AccessTuple) gin.HandlerFunc {
	// init
	glog.Infof("Register allowed users: %+v", users)
	AuthInfoURL = endpoints.TokenURL
	// middleware
	return func(ctx *gin.Context) {
		var token_container *TokenContainer
		token_container, ok := getTokenContainer(ctx)
		if !ok {
			// set LOCATION header to auth endpoint such that the user can easily get a new access-token
			ctx.Writer.Header().Set("Location", endpoints.AuthURL)
			ctx.AbortWithError(http.StatusUnauthorized, errors.New("No token in context"))
			return
		}

		if !token_container.Valid() {
			// set LOCATION header to auth endpoint such that the user can easily get a new access-token
			ctx.Writer.Header().Set("Location", endpoints.AuthURL)
			ctx.AbortWithError(http.StatusUnauthorized, errors.New("Invalid Token"))
			return
		}

		if !accessCheckFunction(token_container, users) {
			ctx.AbortWithError(http.StatusForbidden, errors.New("Access to the Resource is fobidden"))
			return
		}

		// access allowed
		ctx.Writer.Header().Set("Bearer", token_container.Token.AccessToken)
	}
}

// vim: ts=4 sw=4 noexpandtab nolist syn=go
<commit_msg>Modified middleware to support gin context<commit_after>package ginoauth2

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/golang/glog"
	"golang.org/x/oauth2"
)

var AuthInfoURL string
var Realms []string = []string{"employees", "services"}

//var Transport http.Transport = http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
var Transport http.Transport = http.Transport{}

type AccessTuple struct {
	Realm string // p.e. "employees", "services"
	Uid   string // UnixName
	Cn    string // RealName
}

type TokenContainer struct {
	Token     *oauth2.Token
	Scopes    map[string]interface{} // LDAP record vom Benutzer (cn, ..
	GrantType string                 // password, ??
	Realm     string                 // services, employees
}

func extractToken(r *http.Request) (*oauth2.Token, error) {
	hdr := r.Header.Get("Authorization")
	if hdr == "" {
		return nil, errors.New("No authorization header")
	}

	th := strings.Split(hdr, " ")
	if len(th) != 2 {
		return nil, errors.New("Incomplete authorization header")
	}

	return &oauth2.Token{AccessToken: th[1], TokenType: th[0]}, nil
}

func RequestAuthInfo(t *oauth2.Token) ([]byte, error) {
	var uv = make(url.Values)
	// uv.Set("realm", o.Realm)
	uv.Set("access_token", t.AccessToken)
	info_url := AuthInfoURL + "?" + uv.Encode()
	client := &http.Client{Transport: &Transport}
	req, err := http.NewRequest("GET", info_url, nil)
	if err != nil {
		return nil, err
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	return ioutil.ReadAll(resp.Body)
}

func ParseTokenContainer(t *oauth2.Token, data map[string]interface{}) (*TokenContainer, error) {
	tdata := make(map[string]interface{})

	ttype := data["token_type"].(string)
	gtype := data["grant_type"].(string)

	realm := data["realm"].(string)
	exp := data["expires_in"].(float64)
	tok := data["access_token"].(string)
	if ttype != t.TokenType {
		return nil, errors.New("Token type mismatch")
	}
	if tok != t.AccessToken {
		return nil, errors.New("Mismatch between verify request and answer")
	}

	scopes := data["scope"].([]interface{})
	for _, scope := range scopes {
		sscope := scope.(string)
		sval, ok := data[sscope]
		if ok {
			tdata[sscope] = sval
		}
	}

	return &TokenContainer{
		Token: &oauth2.Token{
			AccessToken: tok,
			TokenType:   ttype,
			Expiry:      time.Now().Add(time.Duration(exp) * time.Second),
		},
		Scopes:    tdata,
		Realm:     realm,
		GrantType: gtype,
	}, nil
}

func GetTokenContainer(token *oauth2.Token) (*TokenContainer, error) {
	body, err := RequestAuthInfo(token)
	if err != nil {
		glog.Errorf("RequestAuthInfo failed caused by: %s", err)
		return nil, err
	}
	// extract AuthInfo
	var data map[string]interface{}
	err = json.Unmarshal(body, &data)
	if err != nil {
		glog.Errorf("JSON.Unmarshal failed caused by: %s", err)
		return nil, err
	}
	if _, ok := data["error_description"]; ok {
		var s string
		s = data["error_description"].(string)
		glog.Errorf("RequestAuthInfo returned an error: %s", s)
		return nil, errors.New(s)
	}
	return ParseTokenContainer(token, data)
}

func getTokenContainer(ctx *gin.Context) (*TokenContainer, bool) {
	var oauth_token *oauth2.Token
	var tc *TokenContainer
	var err error

	if oauth_token, err = extractToken(ctx.Request); err != nil {
		glog.Errorf("Can not extract oauth2.Token, caused by: %s", err)
		return nil, false
	}
	if !oauth_token.Valid() {
		glog.Infof("Invalid Token - nil or expired")
		return nil, false
	}

	if tc, err = GetTokenContainer(oauth_token); err != nil {
		glog.Errorf("Can not extract TokenContainer, caused by: %s", err)
		return nil, false
	}

	return tc, true
}

//
// TokenContainer
//
// Validates that the AccessToken within TokenContainer is not expired and not empty.
func (t *TokenContainer) Valid() bool {
	if t.Token == nil {
		return false
	}
	return t.Token.Valid()
}

// Authorization function that checks UID scope
// TokenContainer must be Valid
// []AccessTuple: [{Realm:employee Uid:sszuecs Cn:Sandor Szücs} {Realm:employee Uid:njuettner Cn:Nick Jüttner}]
func UidCheck(tc *TokenContainer, access_tuple []AccessTuple, ctx *gin.Context) bool {
	uid := tc.Scopes["uid"].(string)
	for idx := range access_tuple {
		at := access_tuple[idx]
		if uid == at.Uid {
			ctx.Set("uid", uid) //in this way I can set the authorized uid
			glog.Infof("Grant access to %s\n", uid)
			return true
		}
	}

	return false
}

// Router middleware that can be used to get an authenticated and authorized service for the whole router group.
// Example:
//
//      var endpoints oauth2.Endpoint = oauth2.Endpoint{
//	        AuthURL:  "https://token.oauth2.corp.com/access_token",
//	        TokenURL: "https://oauth2.corp.com/corp/oauth2/tokeninfo",
//      }
//      var acl []ginoauth2.AccessTuple = []ginoauth2.AccessTuple{{"employee", 1070, "sszuecs"}, {"employee", 1114, "njuettner"}}
//      router := gin.Default()
//	private := router.Group("")
//	private.Use(ginoauth2.Auth(ginoatuh2.UidCheck, ginoauth2.endpoints, acl))
//	private.GET("/api/private", func(c *gin.Context) {
//		c.JSON(200, gin.H{"message": "Hello from private"})
//	})
//
func Auth(accessCheckFunction func(tc *TokenContainer, access_tuple []AccessTuple, ctx *gin.Context) bool, endpoints oauth2.Endpoint, users []AccessTuple) gin.HandlerFunc {
	// init
	glog.Infof("Register allowed users: %+v", users)
	AuthInfoURL = endpoints.TokenURL
	// middleware
	return func(ctx *gin.Context) {
		var token_container *TokenContainer
		token_container, ok := getTokenContainer(ctx)
		if !ok {
			// set LOCATION header to auth endpoint such that the user can easily get a new access-token
			ctx.Writer.Header().Set("Location", endpoints.AuthURL)
			ctx.AbortWithError(http.StatusUnauthorized, errors.New("No token in context"))
			return
		}

		if !token_container.Valid() {
			// set LOCATION header to auth endpoint such that the user can easily get a new access-token
			ctx.Writer.Header().Set("Location", endpoints.AuthURL)
			ctx.AbortWithError(http.StatusUnauthorized, errors.New("Invalid Token"))
			return
		}

		if !accessCheckFunction(token_container, users, ctx) {
			ctx.AbortWithError(http.StatusForbidden, errors.New("Access to the Resource is fobidden"))
			return
		}

		// access allowed
		ctx.Writer.Header().Set("Bearer", token_container.Token.AccessToken)
	}
}

// vim: ts=4 sw=4 noexpandtab nolist syn=go
<|endoftext|>


<commit_before>package emotechief

import (
	"context"
	"errors"
	"fmt"
	"math/rand"
	"net/http"
	"regexp"
	"time"

	"github.com/carlmjohnson/requests"
	"github.com/gempir/gempbot/internal/channelpoint"
	"github.com/gempir/gempbot/internal/dto"
	"github.com/gempir/gempbot/internal/log"
	"github.com/gempir/gempbot/internal/store"
	"github.com/nicklaw5/helix/v2"
)

func (e *EmoteChief) VerifySetBttvEmote(channelUserID, emoteId, channel string, slots int) (addedEmote *bttvEmoteResponse, emoteAddType dto.EmoteChangeType, bttvUserId string, removalTargetEmoteId string, err error) {
	if e.db.IsEmoteBlocked(channelUserID, emoteId, dto.REWARD_BTTV) {
		return nil, dto.EMOTE_ADD_ADD, "", "", errors.New("Emote is blocked")
	}

	addedEmote, err = getBttvEmote(emoteId)
	if err != nil {
		return
	}

	if !addedEmote.Sharing {
		err = errors.New("Emote is not shared")
		return
	}

	// first figure out the bttvUserId for the channel, might cache this later on
	var userResp bttvUserResponse
	err = requests.
		URL(BTTV_API).
		Pathf("/3/cached/users/twitch/%s", channelUserID).
		ToJSON(&userResp).
		Fetch(context.Background())
	if err != nil {
		return
	}

	bttvUserId = userResp.ID

	// figure out the limit for the channel, might also chache this later on with expiry
	var dashboards dashboardsResponse
	err = requests.
		URL(BTTV_API).
		Pathf("/3/account/dashboards").
		Bearer(e.cfg.BttvToken).
		ToJSON(&dashboards).
		Fetch(context.Background())
	if err != nil {
		return
	}

	var dbCfg dashboardCfg
	for _, db := range dashboards {
		if db.ID == bttvUserId {
			dbCfg = db
		}
	}
	if dbCfg.ID == "" {
		err = errors.New("No permission to moderate, add gempbot as BetterTTV editor")
		return
	}
	sharedEmotesLimit := dbCfg.Limits.Sharedemotes

	// figure currently added emotes
	var dashboard bttvDashboardResponse
	err = requests.
		URL(BTTV_API).
		Pathf("/3/users/%s", bttvUserId).
		Param("limited", "false").
		Param("personal", "false").
		ToJSON(&dashboard).
		Fetch(context.Background())
	if err != nil {
		return
	}

	for _, emote := range dashboard.Sharedemotes {
		if emote.ID == emoteId {
			err = errors.New("Emote already added")
			return
		}
		if emote.Code == addedEmote.Code {
			err = fmt.Errorf("Emote code \"%s\" already added", addedEmote.Code)
			return
		}
	}

	for _, emote := range dashboard.Channelemotes {
		if emote.ID == emoteId {
			err = fmt.Errorf("Emote \"%s\" already a channel emote", emote.Code)
			return
		}
		if emote.Code == addedEmote.Code {
			err = fmt.Errorf("Emote code \"%s\" already a channel emote", addedEmote.Code)
			return
		}
	}
	log.Infof("Current shared emotes: %d/%d", len(dashboard.Sharedemotes), sharedEmotesLimit)

	emotesAdded := e.db.GetEmoteAdded(channelUserID, dto.REWARD_BTTV, slots)
	log.Infof("Total Previous emotes %d in %s", len(emotesAdded), channelUserID)

	if len(emotesAdded) > 0 {
		oldestEmote := emotesAdded[len(emotesAdded)-1]
		if !oldestEmote.Blocked {
			for _, sharedEmote := range dashboard.Sharedemotes {
				if oldestEmote.EmoteID == sharedEmote.ID {
					removalTargetEmoteId = oldestEmote.EmoteID
					log.Infof("Found removal target %s in %s", removalTargetEmoteId, channelUserID)
				}
			}
		} else {
			log.Infof("Removal target %s is already blocked, so already removed, skipping removal", oldestEmote.EmoteID)
		}
	}

	emoteAddType = dto.EMOTE_ADD_REMOVED_PREVIOUS
	if removalTargetEmoteId == "" && len(dashboard.Sharedemotes) >= sharedEmotesLimit {
		if len(dashboard.Sharedemotes) == 0 {
			return nil, dto.EMOTE_ADD_ADD, "", "", errors.New("emotes limit reached and can't find amount of emotes added to choose random")
		}

		emoteAddType = dto.EMOTE_ADD_REMOVED_RANDOM
		log.Infof("Didn't find previous emote history of %d emotes and limit reached, choosing random in %s", slots, channelUserID)
		removalTargetEmoteId = dashboard.Sharedemotes[rand.Intn(len(dashboard.Sharedemotes))].ID
	}

	return
}

func (e *EmoteChief) RemoveBttvEmote(channelUserID, emoteID string) (*bttvEmoteResponse, error) {
	var userResp bttvUserResponse
	err := requests.
		URL(BTTV_API).
		Pathf("/3/cached/users/twitch/%s", channelUserID).
		ToJSON(&userResp).
		Fetch(context.Background())
	if err != nil {
		return nil, err
	}

	bttvUserId := userResp.ID

	err = requests.
		URL(BTTV_API).
		Pathf("/3/emotes/%s/shared/%s", emoteID, bttvUserId).
		Bearer(e.cfg.BttvToken).
		Method(http.MethodDelete).
		Fetch(context.Background())
	if err != nil {
		return nil, err
	}

	e.db.CreateEmoteAdd(channelUserID, dto.REWARD_BTTV, emoteID, dto.EMOTE_ADD_REMOVED_BLOCKED)
	log.Infof("Blocked channelId: %s emoteId: %s", channelUserID, emoteID)

	return getBttvEmote(emoteID)
}

func (e *EmoteChief) SetBttvEmote(channelUserID, emoteId, channel string, slots int) (addedEmote *bttvEmoteResponse, removedEmote *bttvEmoteResponse, err error) {
	addedEmote, emoteAddType, bttvUserId, removalTargetEmoteId, err := e.VerifySetBttvEmote(channelUserID, emoteId, channel, slots)
	if err != nil {
		return nil, nil, err
	}

	// do we need to remove the emote?
	if removalTargetEmoteId != "" {
		err = requests.
			URL(BTTV_API).
			Pathf("/3/emotes/%s/shared/%s", removalTargetEmoteId, bttvUserId).
			Bearer(e.cfg.BttvToken).
			Method(http.MethodDelete).
			Fetch(context.Background())
		if err != nil {
			return
		}

		e.db.CreateEmoteAdd(channelUserID, dto.REWARD_BTTV, removalTargetEmoteId, emoteAddType)
		log.Infof("Deleted channelId: %s emoteId: %s", channelUserID, removalTargetEmoteId)

		removedEmote, _ = getBttvEmote(removalTargetEmoteId)
	}

	// Add new emote
	err = requests.
		URL(BTTV_API).
		Pathf("/3/emotes/%s/shared/%s", emoteId, bttvUserId).
		Bearer(e.cfg.BttvToken).
		Method(http.MethodPut).
		Fetch(context.Background())
	if err != nil {
		return
	}

	log.Infof("Added channelId: %s emoteId: %s", channelUserID, emoteId)
	e.db.CreateEmoteAdd(channelUserID, dto.REWARD_BTTV, emoteId, dto.EMOTE_ADD_ADD)

	return
}

func getBttvEmote(emoteID string) (*bttvEmoteResponse, error) {
	if emoteID == "" {
		return nil, nil
	}

	var emoteResp bttvEmoteResponse
	err := requests.
		URL(BTTV_API).
		Pathf("/3/emotes/%s", emoteID).
		ToJSON(&emoteResp).
		Fetch(context.Background())

	return &emoteResp, err
}

var bttvRegex = regexp.MustCompile(`https?:\/\/betterttv.com\/emotes\/(\w*)`)

func (ec *EmoteChief) VerifyBttvRedemption(reward store.ChannelPointReward, redemption helix.EventSubChannelPointsCustomRewardRedemptionEvent) bool {
	opts := channelpoint.UnmarshallBttvAdditionalOptions(reward.AdditionalOptions)

	emoteID, err := GetBttvEmoteId(redemption.UserInput)
	if err == nil {
		_, _, _, _, err := ec.VerifySetBttvEmote(redemption.BroadcasterUserID, emoteID, redemption.BroadcasterUserLogin, opts.Slots)
		if err != nil {
			log.Warnf("Bttv error %s %s", redemption.BroadcasterUserLogin, err)
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("⚠ Failed to add bttv emote from @%s error: %s", redemption.UserName, err.Error()))
			return false
		}

		return true
	}

	ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("⚠ Failed to add bttv emote from @%s error: %s", redemption.UserName, err.Error()))
	return false
}

func GetBttvEmoteId(message string) (string, error) {
	matches := bttvRegex.FindAllStringSubmatch(message, -1)

	if len(matches) == 1 && len(matches[0]) == 2 {
		return matches[0][1], nil
	}

	return "", errors.New("no bttv emote link found")
}

func (ec *EmoteChief) HandleBttvRedemption(reward store.ChannelPointReward, redemption helix.EventSubChannelPointsCustomRewardRedemptionEvent, updateStatus bool) {
	opts := channelpoint.UnmarshallBttvAdditionalOptions(reward.AdditionalOptions)
	success := false

	emoteID, err := GetBttvEmoteId(redemption.UserInput)
	if err == nil {
		emoteAdded, emoteRemoved, err := ec.SetBttvEmote(redemption.BroadcasterUserID, emoteID, redemption.BroadcasterUserLogin, opts.Slots)
		if err != nil {
			log.Warnf("Bttv error %s %s", redemption.BroadcasterUserLogin, err)
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("⚠ Failed to add bttv emote from @%s error: %s", redemption.UserName, err.Error()))
		} else if emoteAdded != nil && emoteRemoved != nil {
			success = true
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("✅ Added new bttv emote %s redeemed by @%s removed: %s", emoteAdded.Code, redemption.UserName, emoteRemoved.Code))
		} else if emoteAdded != nil {
			success = true
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("✅ Added new bttv emote %s redeemed by @%s", emoteAdded.Code, redemption.UserName))
		} else {
			success = true
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("✅ Added new bttv emote [unknown] redeemed by @%s", redemption.UserName))
		}
	} else {
		ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("⚠ Failed to add bttv emote from @%s error: %s", redemption.UserName, err.Error()))
	}

	if redemption.UserID == dto.GEMPIR_USER_ID {
		return
	}

	if updateStatus {
		err := ec.helixClient.UpdateRedemptionStatus(redemption.BroadcasterUserID, redemption.Reward.ID, redemption.ID, success)
		if err != nil {
			log.Errorf("Failed to update redemption status %s", err.Error())
			return
		}
	}
}

const BTTV_API = "https://api.betterttv.net"

type bttvUserResponse struct {
	ID string `json:"id"`
}

type bttvDashboardResponse struct {
	ID            string   `json:"id"`
	Name          string   `json:"name"`
	Displayname   string   `json:"displayName"`
	Providerid    string   `json:"providerId"`
	Bots          []string `json:"bots"`
	Channelemotes []struct {
		ID             string    `json:"id"`
		Code           string    `json:"code"`
		Imagetype      string    `json:"imageType"`
		Userid         string    `json:"userId"`
		Createdat      time.Time `json:"createdAt"`
		Updatedat      time.Time `json:"updatedAt"`
		Global         bool      `json:"global"`
		Live           bool      `json:"live"`
		Sharing        bool      `json:"sharing"`
		Approvalstatus string    `json:"approvalStatus"`
	} `json:"channelEmotes"`
	Sharedemotes []struct {
		ID             string    `json:"id"`
		Code           string    `json:"code"`
		Imagetype      string    `json:"imageType"`
		Createdat      time.Time `json:"createdAt"`
		Updatedat      time.Time `json:"updatedAt"`
		Global         bool      `json:"global"`
		Live           bool      `json:"live"`
		Sharing        bool      `json:"sharing"`
		Approvalstatus string    `json:"approvalStatus"`
		User           struct {
			ID          string `json:"id"`
			Name        string `json:"name"`
			Displayname string `json:"displayName"`
			Providerid  string `json:"providerId"`
		} `json:"user"`
	} `json:"sharedEmotes"`
}

type bttvEmoteResponse struct {
	ID             string    `json:"id"`
	Code           string    `json:"code"`
	Imagetype      string    `json:"imageType"`
	Createdat      time.Time `json:"createdAt"`
	Updatedat      time.Time `json:"updatedAt"`
	Global         bool      `json:"global"`
	Live           bool      `json:"live"`
	Sharing        bool      `json:"sharing"`
	Approvalstatus string    `json:"approvalStatus"`
	User           struct {
		ID          string `json:"id"`
		Name        string `json:"name"`
		Displayname string `json:"displayName"`
		Providerid  string `json:"providerId"`
	} `json:"user"`
}

type dashboardsResponse []dashboardCfg

type dashboardCfg struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	Displayname string `json:"displayName"`
	Providerid  string `json:"providerId"`
	Avatar      string `json:"avatar"`
	Limits      struct {
		Channelemotes  int `json:"channelEmotes"`
		Sharedemotes   int `json:"sharedEmotes"`
		Personalemotes int `json:"personalEmotes"`
	} `json:"limits"`
}
<commit_msg>change api key<commit_after>package emotechief

import (
	"context"
	"errors"
	"fmt"
	"math/rand"
	"net/http"
	"regexp"
	"time"

	"github.com/carlmjohnson/requests"
	"github.com/gempir/gempbot/internal/channelpoint"
	"github.com/gempir/gempbot/internal/dto"
	"github.com/gempir/gempbot/internal/log"
	"github.com/gempir/gempbot/internal/store"
	"github.com/nicklaw5/helix/v2"
)

func (e *EmoteChief) VerifySetBttvEmote(channelUserID, emoteId, channel string, slots int) (addedEmote *bttvEmoteResponse, emoteAddType dto.EmoteChangeType, bttvUserId string, removalTargetEmoteId string, err error) {
	if e.db.IsEmoteBlocked(channelUserID, emoteId, dto.REWARD_BTTV) {
		return nil, dto.EMOTE_ADD_ADD, "", "", errors.New("Emote is blocked")
	}

	addedEmote, err = getBttvEmote(emoteId)
	if err != nil {
		return
	}

	if !addedEmote.Sharing {
		err = errors.New("Emote is not shared")
		return
	}

	// first figure out the bttvUserId for the channel, might cache this later on
	var userResp bttvUserResponse
	err = requests.
		URL(BTTV_API).
		Pathf("/3/cached/users/twitch/%s", channelUserID).
		ToJSON(&userResp).
		Fetch(context.Background())
	if err != nil {
		return
	}

	bttvUserId = userResp.ID

	// figure out the limit for the channel, might also chache this later on with expiry
	var dashboards dashboardsResponse
	err = requests.
		URL(BTTV_API).
		Pathf("/3/account/dashboards").
		Bearer(e.cfg.BttvToken).
		ToJSON(&dashboards).
		Fetch(context.Background())
	if err != nil {
		return
	}

	var dbCfg dashboardCfg
	for _, db := range dashboards {
		if db.ID == bttvUserId {
			dbCfg = db
		}
	}
	if dbCfg.ID == "" {
		err = errors.New("No permission to moderate, add gempbot as BetterTTV editor")
		return
	}
	sharedEmotesLimit := dbCfg.Limits.Sharedemotes

	// figure currently added emotes
	var dashboard bttvDashboardResponse
	err = requests.
		URL(BTTV_API).
		Pathf("/3/users/%s", bttvUserId).
		Param("limited", "false").
		Param("personal", "false").
		ToJSON(&dashboard).
		Fetch(context.Background())
	if err != nil {
		return
	}

	for _, emote := range dashboard.Sharedemotes {
		if emote.ID == emoteId {
			err = errors.New("Emote already added")
			return
		}
		if emote.Code == addedEmote.Code {
			err = fmt.Errorf("Emote code \"%s\" already added", addedEmote.Code)
			return
		}
	}

	for _, emote := range dashboard.Channelemotes {
		if emote.ID == emoteId {
			err = fmt.Errorf("Emote \"%s\" already a channel emote", emote.Code)
			return
		}
		if emote.Code == addedEmote.Code {
			err = fmt.Errorf("Emote code \"%s\" already a channel emote", addedEmote.Code)
			return
		}
	}
	log.Infof("Current shared emotes: %d/%d", len(dashboard.Sharedemotes), sharedEmotesLimit)

	emotesAdded := e.db.GetEmoteAdded(channelUserID, dto.REWARD_BTTV, slots)
	log.Infof("Total Previous emotes %d in %s", len(emotesAdded), channelUserID)

	if len(emotesAdded) > 0 {
		oldestEmote := emotesAdded[len(emotesAdded)-1]
		if !oldestEmote.Blocked {
			for _, sharedEmote := range dashboard.Sharedemotes {
				if oldestEmote.EmoteID == sharedEmote.ID {
					removalTargetEmoteId = oldestEmote.EmoteID
					log.Infof("Found removal target %s in %s", removalTargetEmoteId, channelUserID)
				}
			}
		} else {
			log.Infof("Removal target %s is already blocked, so already removed, skipping removal", oldestEmote.EmoteID)
		}
	}

	emoteAddType = dto.EMOTE_ADD_REMOVED_PREVIOUS
	if removalTargetEmoteId == "" && len(dashboard.Sharedemotes) >= sharedEmotesLimit {
		if len(dashboard.Sharedemotes) == 0 {
			return nil, dto.EMOTE_ADD_ADD, "", "", errors.New("emotes limit reached and can't find amount of emotes added to choose random")
		}

		emoteAddType = dto.EMOTE_ADD_REMOVED_RANDOM
		log.Infof("Didn't find previous emote history of %d emotes and limit reached, choosing random in %s", slots, channelUserID)
		removalTargetEmoteId = dashboard.Sharedemotes[rand.Intn(len(dashboard.Sharedemotes))].ID
	}

	return
}

func (e *EmoteChief) RemoveBttvEmote(channelUserID, emoteID string) (*bttvEmoteResponse, error) {
	var userResp bttvUserResponse
	err := requests.
		URL(BTTV_API).
		Pathf("/3/cached/users/twitch/%s", channelUserID).
		ToJSON(&userResp).
		Fetch(context.Background())
	if err != nil {
		return nil, err
	}

	bttvUserId := userResp.ID

	err = requests.
		URL(BTTV_API).
		Pathf("/3/emotes/%s/shared/%s", emoteID, bttvUserId).
		Bearer(e.cfg.BttvToken).
		Method(http.MethodDelete).
		Fetch(context.Background())
	if err != nil {
		return nil, err
	}

	e.db.CreateEmoteAdd(channelUserID, dto.REWARD_BTTV, emoteID, dto.EMOTE_ADD_REMOVED_BLOCKED)
	log.Infof("Blocked channelId: %s emoteId: %s", channelUserID, emoteID)

	return getBttvEmote(emoteID)
}

func (e *EmoteChief) SetBttvEmote(channelUserID, emoteId, channel string, slots int) (addedEmote *bttvEmoteResponse, removedEmote *bttvEmoteResponse, err error) {
	addedEmote, emoteAddType, bttvUserId, removalTargetEmoteId, err := e.VerifySetBttvEmote(channelUserID, emoteId, channel, slots)
	if err != nil {
		return nil, nil, err
	}

	// do we need to remove the emote?
	if removalTargetEmoteId != "" {
		err = requests.
			URL(BTTV_API).
			Pathf("/3/emotes/%s/shared/%s", removalTargetEmoteId, bttvUserId).
			Bearer(e.cfg.BttvToken).
			Method(http.MethodDelete).
			Fetch(context.Background())
		if err != nil {
			return
		}

		e.db.CreateEmoteAdd(channelUserID, dto.REWARD_BTTV, removalTargetEmoteId, emoteAddType)
		log.Infof("Deleted channelId: %s emoteId: %s", channelUserID, removalTargetEmoteId)

		removedEmote, _ = getBttvEmote(removalTargetEmoteId)
	}

	// Add new emote
	err = requests.
		URL(BTTV_API).
		Pathf("/3/emotes/%s/shared/%s", emoteId, bttvUserId).
		Bearer(e.cfg.BttvToken).
		Method(http.MethodPut).
		Fetch(context.Background())
	if err != nil {
		return
	}

	log.Infof("Added channelId: %s emoteId: %s", channelUserID, emoteId)
	e.db.CreateEmoteAdd(channelUserID, dto.REWARD_BTTV, emoteId, dto.EMOTE_ADD_ADD)

	return
}

func getBttvEmote(emoteID string) (*bttvEmoteResponse, error) {
	if emoteID == "" {
		return nil, nil
	}

	var emoteResp bttvEmoteResponse
	err := requests.
		URL(BTTV_API).
		Pathf("/3/emotes/%s", emoteID).
		ToJSON(&emoteResp).
		Fetch(context.Background())

	return &emoteResp, err
}

var bttvRegex = regexp.MustCompile(`https?:\/\/betterttv.com\/emotes\/(\w*)`)

func (ec *EmoteChief) VerifyBttvRedemption(reward store.ChannelPointReward, redemption helix.EventSubChannelPointsCustomRewardRedemptionEvent) bool {
	opts := channelpoint.UnmarshallBttvAdditionalOptions(reward.AdditionalOptions)

	emoteID, err := GetBttvEmoteId(redemption.UserInput)
	if err == nil {
		_, _, _, _, err := ec.VerifySetBttvEmote(redemption.BroadcasterUserID, emoteID, redemption.BroadcasterUserLogin, opts.Slots)
		if err != nil {
			log.Warnf("Bttv error %s %s", redemption.BroadcasterUserLogin, err)
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("⚠ Failed to add bttv emote from @%s error: %s", redemption.UserName, err.Error()))
			return false
		}

		return true
	}

	ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("⚠ Failed to add bttv emote from @%s error: %s", redemption.UserName, err.Error()))
	return false
}

func GetBttvEmoteId(message string) (string, error) {
	matches := bttvRegex.FindAllStringSubmatch(message, -1)

	if len(matches) == 1 && len(matches[0]) == 2 {
		return matches[0][1], nil
	}

	return "", errors.New("no bttv emote link found")
}

func (ec *EmoteChief) HandleBttvRedemption(reward store.ChannelPointReward, redemption helix.EventSubChannelPointsCustomRewardRedemptionEvent, updateStatus bool) {
	opts := channelpoint.UnmarshallBttvAdditionalOptions(reward.AdditionalOptions)
	success := false

	emoteID, err := GetBttvEmoteId(redemption.UserInput)
	if err == nil {
		emoteAdded, emoteRemoved, err := ec.SetBttvEmote(redemption.BroadcasterUserID, emoteID, redemption.BroadcasterUserLogin, opts.Slots)
		if err != nil {
			log.Warnf("Bttv error %s %s", redemption.BroadcasterUserLogin, err)
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("⚠ Failed to add bttv emote from @%s error: %s", redemption.UserName, err.Error()))
		} else if emoteAdded != nil && emoteRemoved != nil {
			success = true
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("✅ Added new bttv emote %s redeemed by @%s removed: %s", emoteAdded.Code, redemption.UserName, emoteRemoved.Code))
		} else if emoteAdded != nil {
			success = true
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("✅ Added new bttv emote %s redeemed by @%s", emoteAdded.Code, redemption.UserName))
		} else {
			success = true
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("✅ Added new bttv emote [unknown] redeemed by @%s", redemption.UserName))
		}
	} else {
		ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("⚠ Failed to add bttv emote from @%s error: %s", redemption.UserName, err.Error()))
	}

	if redemption.UserID == dto.GEMPIR_USER_ID {
		return
	}

	if updateStatus {
		err := ec.helixClient.UpdateRedemptionStatus(redemption.BroadcasterUserID, redemption.Reward.ID, redemption.ID, success)
		if err != nil {
			log.Errorf("Failed to update redemption status %s", err.Error())
			return
		}
	}
}

const BTTV_API = "https://api.betterttv.net"

type bttvUserResponse struct {
	ID string `json:"id"`
}

type bttvDashboardResponse struct {
	ID            string   `json:"id"`
	Name          string   `json:"name"`
	Displayname   string   `json:"displayName"`
	Providerid    string   `json:"providerId"`
	Bots          []string `json:"bots"`
	Channelemotes []struct {
		ID             string    `json:"id"`
		Code           string    `json:"code"`
		Imagetype      string    `json:"imageType"`
		Userid         string    `json:"userId"`
		Createdat      time.Time `json:"createdAt"`
		Updatedat      time.Time `json:"updatedAt"`
		Global         bool      `json:"global"`
		Live           bool      `json:"live"`
		Sharing        bool      `json:"sharing"`
		Approvalstatus string    `json:"approvalStatus"`
	} `json:"liveEmotes"`
	Sharedemotes []struct {
		ID             string    `json:"id"`
		Code           string    `json:"code"`
		Imagetype      string    `json:"imageType"`
		Createdat      time.Time `json:"createdAt"`
		Updatedat      time.Time `json:"updatedAt"`
		Global         bool      `json:"global"`
		Live           bool      `json:"live"`
		Sharing        bool      `json:"sharing"`
		Approvalstatus string    `json:"approvalStatus"`
		User           struct {
			ID          string `json:"id"`
			Name        string `json:"name"`
			Displayname string `json:"displayName"`
			Providerid  string `json:"providerId"`
		} `json:"user"`
	} `json:"sharedEmotes"`
}

type bttvEmoteResponse struct {
	ID             string    `json:"id"`
	Code           string    `json:"code"`
	Imagetype      string    `json:"imageType"`
	Createdat      time.Time `json:"createdAt"`
	Updatedat      time.Time `json:"updatedAt"`
	Global         bool      `json:"global"`
	Live           bool      `json:"live"`
	Sharing        bool      `json:"sharing"`
	Approvalstatus string    `json:"approvalStatus"`
	User           struct {
		ID          string `json:"id"`
		Name        string `json:"name"`
		Displayname string `json:"displayName"`
		Providerid  string `json:"providerId"`
	} `json:"user"`
}

type dashboardsResponse []dashboardCfg

type dashboardCfg struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	Displayname string `json:"displayName"`
	Providerid  string `json:"providerId"`
	Avatar      string `json:"avatar"`
	Limits      struct {
		Channelemotes  int `json:"channelEmotes"`
		Sharedemotes   int `json:"sharedEmotes"`
		Personalemotes int `json:"personalEmotes"`
	} `json:"limits"`
}
<|endoftext|>


<commit_before>package genutil

import (
	"bytes"
	"fmt"
	"go/format"
	"io"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/lestrrat/go-jsschema"
	"github.com/lestrrat/go-jsval"
	"github.com/lestrrat/go-jsval/builder"
)

var rxif = regexp.MustCompile(`\s*interface\s*{\s*}\s*`)

func LooksLikeContainer(s string) bool {
	return strings.HasPrefix(s, "[]") || strings.HasPrefix(s, "map[")
}

func LooksLikeStruct(s string) bool {
	if rxif.MatchString(s) {
		return false
	}
	return !LooksLikeContainer(s)
}

var wsrx = regexp.MustCompile(`\s+`)

func TitleToName(s string) string {
	buf := bytes.Buffer{}
	for _, p := range wsrx.Split(s, -1) {
		buf.WriteString(strings.ToUpper(p[:1]))
		buf.WriteString(p[1:])
	}
	return buf.String()
}

func MakeValidator(s *schema.Schema, ctx interface{}) (*jsval.JSVal, error) {
	b := builder.New()
	v, err := b.BuildWithCtx(s, ctx)
	if err != nil {
		return nil, err
	}

	return v, nil
}

func WriteImports(out io.Writer, stdlibs, extlibs []string) error {
	if len(stdlibs) == 0 && len(extlibs) == 0 {
		return nil
	}

	fmt.Fprint(out, "import (\n")
	for _, pname := range stdlibs {
		fmt.Fprint(out, "\t"+`"`+pname+`"`+"\n")
	}
	if len(extlibs) > 0 {
		if len(stdlibs) > 0 {
			fmt.Fprint(out, "\n")
		}
		for _, pname := range extlibs {
			fmt.Fprint(out, "\t"+`"`+pname+`"`+"\n")
		}
	}
	fmt.Fprint(out, ")\n\n")
	return nil
}

func CreateFile(fn string) (*os.File, error) {
	dir := filepath.Dir(fn)
	if _, err := os.Stat(dir); err != nil {
		if err := os.MkdirAll(dir, 0755); err != nil {
			return nil, err
		}
	}
	f, err := os.Create(fn)
	if err != nil {
		return nil, err
	}
	return f, nil
}

func WriteFmtCode(out io.Writer, buf *bytes.Buffer) error {
	fsrc, err := format.Source(buf.Bytes())
	if err != nil {
		log.Printf("Failed to cleanup Go code (probably a syntax error). Generating file anyway")
		if _, err := buf.WriteTo(out); err != nil {
			return err
		}
		return nil
	}

	if _, err := out.Write(fsrc); err != nil {
		return err
	}
	return nil
}

func WriteDoNotEdit(out io.Writer) {
	fmt.Fprintf(out, "// DO NOT EDIT. Automatically generated by hsup at %s\n", time.Now().Format(time.RFC1123))
}
<commit_msg>Don't include timestamp<commit_after>package genutil

import (
	"bytes"
	"fmt"
	"go/format"
	"io"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/lestrrat/go-jsschema"
	"github.com/lestrrat/go-jsval"
	"github.com/lestrrat/go-jsval/builder"
)

var rxif = regexp.MustCompile(`\s*interface\s*{\s*}\s*`)

func LooksLikeContainer(s string) bool {
	return strings.HasPrefix(s, "[]") || strings.HasPrefix(s, "map[")
}

func LooksLikeStruct(s string) bool {
	if rxif.MatchString(s) {
		return false
	}
	return !LooksLikeContainer(s)
}

var wsrx = regexp.MustCompile(`\s+`)

func TitleToName(s string) string {
	buf := bytes.Buffer{}
	for _, p := range wsrx.Split(s, -1) {
		buf.WriteString(strings.ToUpper(p[:1]))
		buf.WriteString(p[1:])
	}
	return buf.String()
}

func MakeValidator(s *schema.Schema, ctx interface{}) (*jsval.JSVal, error) {
	b := builder.New()
	v, err := b.BuildWithCtx(s, ctx)
	if err != nil {
		return nil, err
	}

	return v, nil
}

func WriteImports(out io.Writer, stdlibs, extlibs []string) error {
	if len(stdlibs) == 0 && len(extlibs) == 0 {
		return nil
	}

	fmt.Fprint(out, "import (\n")
	for _, pname := range stdlibs {
		fmt.Fprint(out, "\t"+`"`+pname+`"`+"\n")
	}
	if len(extlibs) > 0 {
		if len(stdlibs) > 0 {
			fmt.Fprint(out, "\n")
		}
		for _, pname := range extlibs {
			fmt.Fprint(out, "\t"+`"`+pname+`"`+"\n")
		}
	}
	fmt.Fprint(out, ")\n\n")
	return nil
}

func CreateFile(fn string) (*os.File, error) {
	dir := filepath.Dir(fn)
	if _, err := os.Stat(dir); err != nil {
		if err := os.MkdirAll(dir, 0755); err != nil {
			return nil, err
		}
	}
	f, err := os.Create(fn)
	if err != nil {
		return nil, err
	}
	return f, nil
}

func WriteFmtCode(out io.Writer, buf *bytes.Buffer) error {
	fsrc, err := format.Source(buf.Bytes())
	if err != nil {
		log.Printf("Failed to cleanup Go code (probably a syntax error). Generating file anyway")
		if _, err := buf.WriteTo(out); err != nil {
			return err
		}
		return nil
	}

	if _, err := out.Write(fsrc); err != nil {
		return err
	}
	return nil
}

func WriteDoNotEdit(out io.Writer) {
	fmt.Fprintf(out, "// DO NOT EDIT. Automatically generated by hsup\n")
}
<|endoftext|>


<commit_before>package gmws

import (
	"os"

	"github.com/svvu/gomws/mwsHttps"
)

// MwsConfig is configuraton to create the gomws base.
// AccessKey and SecretKey are optional, bette to set them in evn variables.
type MwsConfig struct {
	SellerId  string
	AuthToken string
	Region    string
	AccessKey string
	SecretKey string
}

// MwsClient the interface for API clients.
type MwsClient interface {
	Version() string
	Name() string
	NewClient(config MwsConfig) (MwsClient, error)
	GetServiceStatus() (mwsHttps.Response, error)
}

const (
	envAccessKey = "AWS_ACCESS_KEY"
	envSecretKey = "AWS_SECRET_KEY"
)

// Credential the credential to access the API.
type Credential struct {
	AccessKey string
	SecretKey string
}

// GetCredential get the credential from evn variables.
func GetCredential() Credential {
	credential := Credential{}
	credential.AccessKey = os.Getenv(envAccessKey)
	credential.SecretKey = os.Getenv(envSecretKey)

	return credential
}
<commit_msg>Add pretty lib to inspect struct<commit_after>package gmws

import (
	"fmt"
	"os"

	"github.com/kr/pretty"
	"github.com/svvu/gomws/mwsHttps"
)

// MwsConfig is configuraton to create the gomws base.
// AccessKey and SecretKey are optional, bette to set them in evn variables.
type MwsConfig struct {
	SellerId  string
	AuthToken string
	Region    string
	AccessKey string
	SecretKey string
}

// MwsClient the interface for API clients.
type MwsClient interface {
	Version() string
	Name() string
	NewClient(config MwsConfig) (MwsClient, error)
	GetServiceStatus() (mwsHttps.Response, error)
}

const (
	envAccessKey = "AWS_ACCESS_KEY"
	envSecretKey = "AWS_SECRET_KEY"
)

// Credential the credential to access the API.
type Credential struct {
	AccessKey string
	SecretKey string
}

// GetCredential get the credential from evn variables.
func GetCredential() Credential {
	credential := Credential{}
	credential.AccessKey = os.Getenv(envAccessKey)
	credential.SecretKey = os.Getenv(envSecretKey)

	return credential
}

// Inspect print out the value in a user friendly way.
func Inspect(value interface{}) {
	fmt.Printf("%# v", pretty.Formatter(value))
}
<|endoftext|>


<commit_before>// go gen-con - Go Generic Containers
// Copyright 2016 Aurélien Rainone. All rights reserved.
// Use of this source code is governed by a MIT license
// that can be found in the LICENSE file.

package main

import (
	"bytes"
	"flag"
	"fmt"
	"github.com/aurelien-rainone/go-gencon/containers"
	"go/format"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"text/template"
	"unicode"
	"unicode/utf8"
)

var (
	container = flag.String("cont", "", "generic container type name; must be set")
	containee = flag.String("type", "", "containee type name (user type or builtin); must be set")
	name      = flag.String("name", "", "override generated container name; default 'ContaineeContainer' or 'containeeContainer' if containee is exported")
	output    = flag.String("output", "", "output file name; default srcdir/containee_container.go")
	pkg       = flag.String("pkg", "", "package name of the generated file; default to 'main' for cli usage or the containee package if go-gencon is called by `go generate`")
)

// Usage is a replacement usage function for the flags package.
func Usage() {
	fmt.Fprintf(os.Stderr, "Usage of go-gencon:\n")
	fmt.Fprintf(os.Stderr, "    go-gencon [flags] -type containee -cont container\n")
	fmt.Fprintf(os.Stderr, "\n")
	fmt.Fprintf(os.Stderr, "Available generic containers:\n")
	fmt.Fprintf(os.Stderr, " - BoundedStack\n")
	fmt.Fprintf(os.Stderr, "\n")
	fmt.Fprintf(os.Stderr, "For more information, see:\n")
	fmt.Fprintf(os.Stderr, "\thttp://github.com/aurelien-rainone/go-gencon\n")
	fmt.Fprintf(os.Stderr, "Flags:\n")
	flag.PrintDefaults()
}

func main() {

	log.SetFlags(0)
	log.SetPrefix("go-gencon: ")
	flag.Usage = Usage
	flag.Parse()

	if os.Getenv("GOPACKAGE") != "" {
		*pkg = os.Getenv("GOPACKAGE")
	}

	if *container == "" || *container == "" || *pkg == "" {
		flag.Usage()
		os.Exit(2)
	}

	var (
		g    Generator // generator instance
		cter string    // type name of generate container
	)
	cter = *containee + *container
	if len(*name) > 0 {
		cter = *name
	}

	g.nfo = GeneratorInfo{
		Containee: *containee,
		Container: cter,
		Package:   *pkg,
	}
	g.nfo.Exported, g.nfo.Builtin = typeInfoFromString(*containee)
	g.contType = *container

	// Print the header and package clause.
	g.Printf("// This file has been generated by \"go-gencon\"; DO NOT EDIT\n")
	g.Printf("// command: \"go-gencon %s\"\n", strings.Join(os.Args[1:], " "))
	g.Printf("// Go Generic Containers\n")
	g.Printf("// For more information see http://github.com/aurelien-rainone/go-gencon\n")
	g.Printf("\n")

	// Format the output.
	src := g.format()

	if *output == "" {
		// generate output filename
		baseName := fmt.Sprintf("%s_%s.go", *containee, *container)
		*output = filepath.Join(".", strings.ToLower(baseName))
	}
	err := ioutil.WriteFile(*output, src, 0644)
	if err != nil {
		log.Fatalf("writing output: %s", err)
	}

}

func typeInfoFromString(typename string) (isExported, isBuiltin bool) {
	isBuiltin = false
	switch typename {
	case "uint8", "uint16", "uint32", "uint64":
	case "int8", "int16", "int32", "int64":
	case "float32", "float64", "complex64", "complex128":
	case "byte", "rune":
	case "uint", "int", "uintptr":
		isBuiltin = true
	}
	isExported = isFirstUpper(typename)
	return
}

func isFirstUpper(s string) bool {
	r, n := utf8.DecodeRuneInString(s)
	if n == 0 {
		// should never get there
		panic("Can't get first letter of an empty string...")
	}
	if r == utf8.RuneError {
		// should never get there either
		panic("Invalid encoding")
	}
	return unicode.IsUpper(r)
}

// Generator holds the state of the analysis. Primarily used to buffer
// the output for format.Source.
type Generator struct {
	buf      bytes.Buffer  // Accumulated output.
	nfo      GeneratorInfo // Info needed to generate the file
	contType string        // container to generate
}

type GeneratorInfo struct {
	Containee, Container, Package string
	Exported                      bool // containee is an exported type
	Builtin                       bool // containee is a builtin type
}

func (g *Generator) Printf(format string, args ...interface{}) {
	fmt.Fprintf(&g.buf, format, args...)
}

// format returns the gofmt-ed contents of the Generator's buffer.
func (g *Generator) format() []byte {
	// list supported containers
	containers := map[string]string{
		"boundedstack": containers.BoundedStack,
	}

	// create container template
	var (
		tmpl  *template.Template
		str   string
		err   error
		found bool
	)
	if str, found = containers[strings.ToLower(g.contType)]; !found {
		log.Fatalf("No template for container '%s'", g.contType)
	}
	if tmpl, err = template.New(g.contType).Parse(str); err != nil {
		panic(err)
	}
	err = tmpl.Execute(&g.buf, g.nfo)
	if err != nil {
		panic(err)
	}

	src, err := format.Source(g.buf.Bytes())
	if err != nil {
		// Should never happen, but can arise when developing this code.
		// The user can compile the output to see the error.
		log.Printf("warning: internal error: invalid Go generated: %s", err)
		log.Printf("warning: compile the package to analyze the error")
		return g.buf.Bytes()
	}
	return src
}
<commit_msg>Accepts dir or file for output flag<commit_after>// go gen-con - Go Generic Containers
// Copyright 2016 Aurélien Rainone. All rights reserved.
// Use of this source code is governed by a MIT license
// that can be found in the LICENSE file.

package main

import (
	"bytes"
	"flag"
	"fmt"
	"github.com/aurelien-rainone/go-gencon/containers"
	"go/format"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"text/template"
	"unicode"
	"unicode/utf8"
)

var (
	container = flag.String("cont", "", "generic container type name; must be set")
	containee = flag.String("type", "", "containee type name (user type or builtin); must be set")
	name      = flag.String("name", "", "override generated container name; default 'ContaineeContainer' or 'containeeContainer' if containee is exported")
	output    = flag.String("output", "", "output file name; default srcdir/containee_container.go")
	pkg       = flag.String("pkg", "", "package name of the generated file; default to 'main' for cli usage or the containee package if go-gencon is called by `go generate`")
)

// Usage is a replacement usage function for the flags package.
func Usage() {
	fmt.Fprintf(os.Stderr, "Usage of go-gencon:\n")
	fmt.Fprintf(os.Stderr, "    go-gencon [flags] -type containee -cont container\n")
	fmt.Fprintf(os.Stderr, "\n")
	fmt.Fprintf(os.Stderr, "Available generic containers:\n")
	fmt.Fprintf(os.Stderr, " - BoundedStack\n")
	fmt.Fprintf(os.Stderr, "\n")
	fmt.Fprintf(os.Stderr, "For more information, see:\n")
	fmt.Fprintf(os.Stderr, "\thttp://github.com/aurelien-rainone/go-gencon\n")
	fmt.Fprintf(os.Stderr, "Flags:\n")
	flag.PrintDefaults()
}

func main() {

	log.SetFlags(0)
	log.SetPrefix("go-gencon: ")
	flag.Usage = Usage
	flag.Parse()

	if os.Getenv("GOPACKAGE") != "" {
		*pkg = os.Getenv("GOPACKAGE")
	} else if *pkg == "" {
		*pkg = "main"
	}

	if *container == "" || *container == "" {
		flag.Usage()
		os.Exit(2)
	}

	var (
		g    Generator // generator instance
		cter string    // type name of generate container
	)
	cter = *containee + *container
	if len(*name) > 0 {
		cter = *name
	}

	g.nfo = GeneratorInfo{
		Containee: *containee,
		Container: cter,
		Package:   *pkg,
	}
	g.nfo.Exported, g.nfo.Builtin = typeInfoFromString(*containee)
	g.contType = *container

	// Print the header and package clause.
	g.Printf("// This file has been generated by \"go-gencon\"; DO NOT EDIT\n")
	g.Printf("// command: \"go-gencon %s\"\n", strings.Join(os.Args[1:], " "))
	g.Printf("// Go Generic Containers\n")
	g.Printf("// For more information see http://github.com/aurelien-rainone/go-gencon\n")
	g.Printf("\n")

	// Format the output.
	src := g.format()

	// generate output filename
	err := ioutil.WriteFile(
		generateOutputFileName(*containee, *container, *output),
		src, 0644)
	if err != nil {
		log.Fatalf("writing output: %s", err)
	}
}

func generateOutputFileName(containee, container, outputFlag string) (fileName string) {
	baseName := strings.ToLower(fmt.Sprintf("%s_%s.go", containee, container))
	if outputFlag == "" {
		fileName = filepath.Join(".", baseName)
	} else {
		if info, err := os.Stat(outputFlag); err == nil && info.IsDir() {
			// -output is a directory
			fileName = filepath.Join(outputFlag, baseName)
		} else {
			// -output is just a filename
			fileName = filepath.Join(".", outputFlag)
		}
	}
	return
}

func typeInfoFromString(typename string) (isExported, isBuiltin bool) {
	isBuiltin = false
	switch typename {
	case "uint8", "uint16", "uint32", "uint64":
	case "int8", "int16", "int32", "int64":
	case "float32", "float64", "complex64", "complex128":
	case "byte", "rune":
	case "uint", "int", "uintptr":
		isBuiltin = true
	}
	isExported = isFirstUpper(typename)
	return
}

func isFirstUpper(s string) bool {
	r, n := utf8.DecodeRuneInString(s)
	if n == 0 {
		// should never get there
		panic("Can't get first letter of an empty string...")
	}
	if r == utf8.RuneError {
		// should never get there either
		panic("Invalid encoding")
	}
	return unicode.IsUpper(r)
}

// Generator holds the state of the analysis. Primarily used to buffer
// the output for format.Source.
type Generator struct {
	buf      bytes.Buffer  // Accumulated output.
	nfo      GeneratorInfo // Info needed to generate the file
	contType string        // container to generate
}

type GeneratorInfo struct {
	Containee, Container, Package string
	Exported                      bool // containee is an exported type
	Builtin                       bool // containee is a builtin type
}

func (g *Generator) Printf(format string, args ...interface{}) {
	fmt.Fprintf(&g.buf, format, args...)
}

// format returns the gofmt-ed contents of the Generator's buffer.
func (g *Generator) format() []byte {
	// list supported containers
	containers := map[string]string{
		"boundedstack": containers.BoundedStack,
	}

	// create container template
	var (
		tmpl  *template.Template
		str   string
		err   error
		found bool
	)
	if str, found = containers[strings.ToLower(g.contType)]; !found {
		log.Fatalf("No template for container '%s'", g.contType)
	}
	if tmpl, err = template.New(g.contType).Parse(str); err != nil {
		panic(err)
	}
	err = tmpl.Execute(&g.buf, g.nfo)
	if err != nil {
		panic(err)
	}

	src, err := format.Source(g.buf.Bytes())
	if err != nil {
		// Should never happen, but can arise when developing this code.
		// The user can compile the output to see the error.
		log.Printf("warning: internal error: invalid Go generated: %s", err)
		log.Printf("warning: compile the package to analyze the error")
		return g.buf.Bytes()
	}
	return src
}
<|endoftext|>


<commit_before>package msgpack

import (
    "io"
    "os"
    "unsafe"
    "reflect"
)

func readByte(reader io.Reader) (v uint8, err os.Error) {
    data := [1]byte{}
    _, e := reader.Read(data[0:])
    if e != nil { return 0, e }
    return data[0], nil
}

func readUint16(reader io.Reader) (v uint16, n int, err os.Error) {
    data := [2]byte{}
    n, e := reader.Read(data[0:])
    if e != nil { return 0, n, e }
    return (uint16(data[0]) << 8) | uint16(data[1]), n, nil
}

func readUint32(reader io.Reader) (v uint32, n int, err os.Error) {
    data := [4]byte{}
    n, e := reader.Read(data[0:])
    if e != nil { return 0, n, e }
    return (uint32(data[0]) << 24) | (uint32(data[1]) << 16) | (uint32(data[2]) << 8) | uint32(data[3]), n, nil
}

func readUint64(reader io.Reader) (v uint64, n int, err os.Error) {
    data := [8]byte{}
    n, e := reader.Read(data[0:])
    if e != nil { return 0, n, e }
    return (uint64(data[0]) << 56) | (uint64(data[1]) << 48) | (uint64(data[2]) << 40) | (uint64(data[3]) << 32) | (uint64(data[4]) << 24) | (uint64(data[5]) << 16) | (uint64(data[6]) << 8) | uint64(data[7]), n, nil
}

func readInt16(reader io.Reader) (v int16, n int, err os.Error) {
    data := [2]byte{}
    n, e := reader.Read(data[0:])
    if e != nil { return 0, n, e }
    return (int16(data[0]) << 8) | int16(data[1]), n, nil
}

func readInt32(reader io.Reader) (v int32, n int, err os.Error) {
    data := [4]byte{}
    n, e := reader.Read(data[0:])
    if e != nil { return 0, n, e }
    return (int32(data[0]) << 24) | (int32(data[1]) << 16) | (int32(data[2]) << 8) | int32(data[3]), n, nil
}

func readInt64(reader io.Reader) (v int64, n int, err os.Error) {
    data := [8]byte{}
    n, e := reader.Read(data[0:])
    if e != nil { return 0, n, e }
    return (int64(data[0]) << 56) | (int64(data[1]) << 48) | (int64(data[2]) << 40) | (int64(data[3]) << 32) | (int64(data[4]) << 24) | (int64(data[5]) << 16) | (int64(data[6]) << 8) | int64(data[7]), n, nil
}

func unpackArray(reader io.Reader, nelems uint) (v reflect.Value, n int, err os.Error) {
    retval := make([]interface{}, nelems)
    nbytesread := 0
    var i uint
    for i = 0; i < nelems; i++ {
        v, n, e := Unpack(reader)
        nbytesread += n
        if e != nil { return nil, nbytesread, e }
        retval[i] = v.Interface()
    }
    return reflect.NewValue(retval), nbytesread, nil
}

func unpackMap(reader io.Reader, nelems uint) (v reflect.Value, n int, err os.Error) {
    retval := make(map [interface{}] interface{})
    nbytesread := 0
    var i uint
    for i = 0; i < nelems; i++ {
        k, n, e := Unpack(reader)
        nbytesread += n
        if e != nil { return nil, nbytesread, e }
        v, n, e := Unpack(reader)
        nbytesread += n
        if e != nil { return nil, nbytesread, e }
        retval[k.Interface()] = v.Interface()
    }
    return reflect.NewValue(retval), nbytesread, nil
}

func Unpack(reader io.Reader) (v reflect.Value, n int, err os.Error) {
    var retval reflect.Value
    var nbytesread int = 0

    c, e := readByte(reader)
    if e != nil { return nil, 0, e }
    nbytesread += 1
    if c < 0x80 || c >= 0xe0 {
        retval = reflect.NewValue(int8(c))
    } else if c >= 0x80 && c <= 0x8f {
        retval, n, e = unpackMap(reader, uint(c & 0xf))
        nbytesread += n
        if e != nil { return nil, nbytesread, e }
        nbytesread += n
    } else if c >= 0x90 && c <= 0x9f {
        retval, n, e = unpackArray(reader, uint(c & 0xf))
        nbytesread += n
        if e != nil { return nil, nbytesread, e }
        nbytesread += n
    } else if c >= 0xa0 && c <= 0xbf {
        data := make([]byte, c & 0xf);
        n, e := reader.Read(data)
        nbytesread += n
        if e != nil { return nil, nbytesread, e }
        retval = reflect.NewValue(data)
    } else {
        switch c {
        case 0xc0: retval = reflect.NewValue(nil)
        case 0xc2: retval = reflect.NewValue(false)
        case 0xc3: retval = reflect.NewValue(true)
        case 0xca:
            data, n, e := readUint32(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(*(*float32)(unsafe.Pointer(&data)))
        case 0xcb:
            data, n, e := readUint64(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(*(*float64)(unsafe.Pointer(&data)))
        case 0xcc:
            data, e := readByte(reader)
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(uint8(data))
            nbytesread += 1
        case 0xcd:
            data, n, e := readUint16(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(data)
        case 0xce:
            data, n, e := readUint32(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(data)
        case 0xcf:
            data, n, e := readUint64(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(data)
        case 0xd0:
            data, e := readByte(reader)
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(int8(data))
            nbytesread += 1
        case 0xd1:
            data, n, e := readInt16(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(data)
        case 0xd2:
            data, n, e := readInt32(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(data)
        case 0xd3:
            data, n, e := readInt64(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(data)
        case 0xda:
            nbytestoread, n, e := readUint16(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            data := make([]byte, nbytestoread)
            n, e = reader.Read(data)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(data)
        case 0xdb:
            nbytestoread, n, e := readUint32(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            data := make([]byte, nbytestoread)
            n, e = reader.Read(data)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(data)
        case 0xdc:
            nelemstoread, n, e := readUint16(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval, n, e = unpackArray(reader, uint(nelemstoread))
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
        case 0xdd:
            nelemstoread, n, e := readUint32(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval, n, e = unpackArray(reader, uint(nelemstoread))
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
        case 0xde:
            nelemstoread, n, e := readUint16(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval, n, e = unpackMap(reader, uint(nelemstoread))
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
        case 0xdf:
            nelemstoread, n, e := readUint32(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval, n, e = unpackMap(reader, uint(nelemstoread))
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
        }
    }
    return retval, nbytesread, nil
}
<commit_msg>Add new API function UnpackReflected() that returns multiple reflect.Value for unpacked elements of arrays or maps.<commit_after>package msgpack

import (
    "io"
    "os"
    "unsafe"
    "reflect"
)

func readByte(reader io.Reader) (v uint8, err os.Error) {
    data := [1]byte{}
    _, e := reader.Read(data[0:])
    if e != nil { return 0, e }
    return data[0], nil
}

func readUint16(reader io.Reader) (v uint16, n int, err os.Error) {
    data := [2]byte{}
    n, e := reader.Read(data[0:])
    if e != nil { return 0, n, e }
    return (uint16(data[0]) << 8) | uint16(data[1]), n, nil
}

func readUint32(reader io.Reader) (v uint32, n int, err os.Error) {
    data := [4]byte{}
    n, e := reader.Read(data[0:])
    if e != nil { return 0, n, e }
    return (uint32(data[0]) << 24) | (uint32(data[1]) << 16) | (uint32(data[2]) << 8) | uint32(data[3]), n, nil
}

func readUint64(reader io.Reader) (v uint64, n int, err os.Error) {
    data := [8]byte{}
    n, e := reader.Read(data[0:])
    if e != nil { return 0, n, e }
    return (uint64(data[0]) << 56) | (uint64(data[1]) << 48) | (uint64(data[2]) << 40) | (uint64(data[3]) << 32) | (uint64(data[4]) << 24) | (uint64(data[5]) << 16) | (uint64(data[6]) << 8) | uint64(data[7]), n, nil
}

func readInt16(reader io.Reader) (v int16, n int, err os.Error) {
    data := [2]byte{}
    n, e := reader.Read(data[0:])
    if e != nil { return 0, n, e }
    return (int16(data[0]) << 8) | int16(data[1]), n, nil
}

func readInt32(reader io.Reader) (v int32, n int, err os.Error) {
    data := [4]byte{}
    n, e := reader.Read(data[0:])
    if e != nil { return 0, n, e }
    return (int32(data[0]) << 24) | (int32(data[1]) << 16) | (int32(data[2]) << 8) | int32(data[3]), n, nil
}

func readInt64(reader io.Reader) (v int64, n int, err os.Error) {
    data := [8]byte{}
    n, e := reader.Read(data[0:])
    if e != nil { return 0, n, e }
    return (int64(data[0]) << 56) | (int64(data[1]) << 48) | (int64(data[2]) << 40) | (int64(data[3]) << 32) | (int64(data[4]) << 24) | (int64(data[5]) << 16) | (int64(data[6]) << 8) | int64(data[7]), n, nil
}

func unpackArray(reader io.Reader, nelems uint) (v reflect.Value, n int, err os.Error) {
    retval := make([]interface{}, nelems)
    nbytesread := 0
    var i uint
    for i = 0; i < nelems; i++ {
        v, n, e := Unpack(reader)
        nbytesread += n
        if e != nil { return nil, nbytesread, e }
        retval[i] = v.Interface()
    }
    return reflect.NewValue(retval), nbytesread, nil
}

func unpackArrayReflected(reader io.Reader, nelems uint) (v reflect.Value, n int, err os.Error) {
    retval := make([]reflect.Value, nelems)
    nbytesread := 0
    var i uint
    for i = 0; i < nelems; i++ {
        v, n, e := UnpackReflected(reader)
        nbytesread += n
        if e != nil { return nil, nbytesread, e }
        retval[i] = v
    }
    return reflect.NewValue(retval), nbytesread, nil
}

func unpackMap(reader io.Reader, nelems uint) (v reflect.Value, n int, err os.Error) {
    retval := make(map [interface{}] interface{})
    nbytesread := 0
    var i uint
    for i = 0; i < nelems; i++ {
        k, n, e := Unpack(reader)
        nbytesread += n
        if e != nil { return nil, nbytesread, e }
        v, n, e := Unpack(reader)
        nbytesread += n
        if e != nil { return nil, nbytesread, e }
        retval[k.Interface()] = v.Interface()
    }
    return reflect.NewValue(retval), nbytesread, nil
}

func unpackMapReflected(reader io.Reader, nelems uint) (v reflect.Value, n int, err os.Error) {
    retval := make(map [reflect.Value] reflect.Value)
    nbytesread := 0
    var i uint
    for i = 0; i < nelems; i++ {
        k, n, e := UnpackReflected(reader)
        nbytesread += n
        if e != nil { return nil, nbytesread, e }
        v, n, e := UnpackReflected(reader)
        nbytesread += n
        if e != nil { return nil, nbytesread, e }
        retval[k] = v
    }
    return reflect.NewValue(retval), nbytesread, nil
}

func unpack(reader io.Reader, reflected bool) (v reflect.Value, n int, err os.Error) {
    var retval reflect.Value
    var nbytesread int = 0

    c, e := readByte(reader)
    if e != nil { return nil, 0, e }
    nbytesread += 1
    if c < 0x80 || c >= 0xe0 {
        retval = reflect.NewValue(int8(c))
    } else if c >= 0x80 && c <= 0x8f {
        if reflected {
            retval, n, e = unpackMapReflected(reader, uint(c & 0xf))
        } else {
            retval, n, e = unpackMap(reader, uint(c & 0xf))
        }
        nbytesread += n
        if e != nil { return nil, nbytesread, e }
        nbytesread += n
    } else if c >= 0x90 && c <= 0x9f {
        if reflected {
            retval, n, e = unpackArrayReflected(reader, uint(c & 0xf))
        } else {
            retval, n, e = unpackArray(reader, uint(c & 0xf))
        }
        nbytesread += n
        if e != nil { return nil, nbytesread, e }
        nbytesread += n
    } else if c >= 0xa0 && c <= 0xbf {
        data := make([]byte, c & 0xf);
        n, e := reader.Read(data)
        nbytesread += n
        if e != nil { return nil, nbytesread, e }
        retval = reflect.NewValue(data)
    } else {
        switch c {
        case 0xc0: retval = reflect.NewValue(nil)
        case 0xc2: retval = reflect.NewValue(false)
        case 0xc3: retval = reflect.NewValue(true)
        case 0xca:
            data, n, e := readUint32(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(*(*float32)(unsafe.Pointer(&data)))
        case 0xcb:
            data, n, e := readUint64(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(*(*float64)(unsafe.Pointer(&data)))
        case 0xcc:
            data, e := readByte(reader)
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(uint8(data))
            nbytesread += 1
        case 0xcd:
            data, n, e := readUint16(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(data)
        case 0xce:
            data, n, e := readUint32(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(data)
        case 0xcf:
            data, n, e := readUint64(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(data)
        case 0xd0:
            data, e := readByte(reader)
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(int8(data))
            nbytesread += 1
        case 0xd1:
            data, n, e := readInt16(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(data)
        case 0xd2:
            data, n, e := readInt32(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(data)
        case 0xd3:
            data, n, e := readInt64(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(data)
        case 0xda:
            nbytestoread, n, e := readUint16(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            data := make([]byte, nbytestoread)
            n, e = reader.Read(data)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(data)
        case 0xdb:
            nbytestoread, n, e := readUint32(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            data := make([]byte, nbytestoread)
            n, e = reader.Read(data)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            retval = reflect.NewValue(data)
        case 0xdc:
            nelemstoread, n, e := readUint16(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            if reflected {
                retval, n, e = unpackArrayReflected(reader, uint(nelemstoread))
            } else {
                retval, n, e = unpackArray(reader, uint(nelemstoread))
            }
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
        case 0xdd:
            nelemstoread, n, e := readUint32(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            if reflected {
                retval, n, e = unpackArrayReflected(reader, uint(nelemstoread))
            } else {
                retval, n, e = unpackArray(reader, uint(nelemstoread))
            }
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
        case 0xde:
            nelemstoread, n, e := readUint16(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            if reflected {
                retval, n, e = unpackMapReflected(reader, uint(nelemstoread))
            } else {
                retval, n, e = unpackMap(reader, uint(nelemstoread))
            }
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
        case 0xdf:
            nelemstoread, n, e := readUint32(reader)
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
            if reflected {
                retval, n, e = unpackMapReflected(reader, uint(nelemstoread))
            } else {
                retval, n, e = unpackMap(reader, uint(nelemstoread))
            }
            nbytesread += n
            if e != nil { return nil, nbytesread, e }
        }
    }
    return retval, nbytesread, nil
}

func Unpack(reader io.Reader) (v reflect.Value, n int, err os.Error) {
    return unpack(reader, false)
}

func UnpackReflected(reader io.Reader) (v reflect.Value, n int, err os.Error) {
    return unpack(reader, true)
}

<|endoftext|>


<commit_before>// Copyright 2015 Peter Goetz
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"go/ast"
	"go/token"
	"os/exec"
	"strings"

	"github.com/petergtz/goextract/util"
)

func ExtractFileToFile(inputFileName string, selection Selection, extractedFuncName string, outputFilename string) {
	fileSet, astFile := astFromFile(inputFileName)
	createAstFileDump(inputFileName+".ast", fileSet, astFile)
	doExtraction(fileSet, astFile, selection, extractedFuncName)
	util.WriteFileAsStringOrPanic(outputFilename, stringFrom(fileSet, astFile))
	err := exec.Command("gofmt", "-w", outputFilename).Run()
	util.PanicOnError(err)
}

func ExtractFileToString(inputFileName string, selection Selection, extractedFuncName string) string {
	fileSet, astFile := astFromFile(inputFileName)
	createAstFileDump(inputFileName+".ast", fileSet, astFile)
	doExtraction(fileSet, astFile, selection, extractedFuncName)
	return stringFrom(fileSet, astFile)
}

func ExtractStringToString(input string, selection Selection, extractedFuncName string) string {
	fileSet, astFile := astFromInput(input)
	doExtraction(fileSet, astFile, selection, extractedFuncName)
	return stringFrom(fileSet, astFile)
}

func doExtraction(fileSet *token.FileSet, astFile *ast.File, selection Selection, extractedFuncName string) {
	visitor := &astNodeVisitorForExpressions{parentNode: nil, context: &expressionVisitorContext{fset: fileSet, selection: selection}}
	ast.Walk(visitor, astFile)
	if visitor.context.exprToExtract != nil {
		extractExpression(astFile, fileSet, visitor.context.exprToExtract, visitor.context.parent, extractedFuncName)
	} else {
		v := &astNodeVisitorForMultipleStatements{parentNode: nil, context: &multipleStatementVisitorContext{fset: fileSet, selection: selection}}
		ast.Walk(v, astFile)
		if v.context.posParent != v.context.endParent {
			panic(fmt.Sprintf("Selection is not valid. posParent: %v; endParent: %v",
				v.context.posParent, v.context.endParent))
		}
		if v.context.posParent == nil {
			panic(fmt.Sprintf("Selection is not valid. posParent: %v; endParent: %v",
				v.context.posParent, v.context.endParent))
		}
		extractMultipleStatements(astFile, fileSet, v.context.nodesToExtract, v.context.posParent, extractedFuncName)
	}
}

func globalVarIdents(astFile *ast.File) map[string]*ast.Ident {
	result := make(map[string]*ast.Ident)
	ast.Inspect(astFile, func(node ast.Node) bool {
		switch typedNode := node.(type) {
		case *ast.FuncDecl:
			return false
		case *ast.GenDecl:
			if typedNode.Tok.String() == "var" {
				for _, spec := range typedNode.Specs {
					for _, name := range spec.(*ast.ValueSpec).Names {
						result[name.Name] = name
					}
				}
			}
			return true
		default:
			return true
		}
	})
	return result
}

func varIdentsDeclaredWithin(nodes []ast.Node) map[string]*ast.Ident {
	result := make(map[string]*ast.Ident)
	for _, node := range nodes {
		ast.Inspect(node, func(node ast.Node) bool {
			if assignStmt, ok := node.(*ast.AssignStmt); ok && assignStmt.Tok.String() == ":=" {
				for i := range assignStmt.Lhs {
					result[assignStmt.Lhs[i].(*ast.Ident).Name] = assignStmt.Lhs[i].(*ast.Ident)
				}
			}
			return true
		})
	}
	return result
}

func varIdentsUsedIn(nodes []ast.Node) map[string]*ast.Ident {
	result := make(map[string]*ast.Ident)
	for _, node := range nodes {
		ast.Inspect(node, func(node ast.Node) bool {
			if ident, ok := node.(*ast.Ident); ok &&
				ident.Obj != nil && ident.Obj.Kind == ast.Var {
				switch typedDecl := ident.Obj.Decl.(type) {
				case *ast.AssignStmt:
					for _, lhs := range typedDecl.Lhs {
						if lhs.(*ast.Ident).Name == ident.Name {
							result[ident.Name] = ident //deduceTypeString(typedDecl.Rhs[i].(ast.Expr))
						}
					}
				default:
					result[ident.Name] = ast.NewIdent("UnresolvedType")
				}
			}
			return true
		})
	}
	return result
}

// TODO rename to varIdentsUsedIn
func overlappingVarsIdentsUsedIn(stmts []ast.Stmt, outOf map[string]*ast.Ident) map[string]*ast.Ident {
	result := make(map[string]*ast.Ident)
	for _, stmt := range stmts {
		ast.Inspect(stmt, func(node ast.Node) bool {
			if ident, ok := node.(*ast.Ident); ok {
				if outOf[ident.Name] != nil {
					result[ident.Name] = ident
				}
			}
			return true
		})
	}
	return result
}

func namesOf(idents map[string]*ast.Ident) []string {
	result := make([]string, 0, len(idents))
	for k := range idents {
		result = append(result, k)
	}
	return result
}

func exprsFrom(idents map[string]*ast.Ident) []ast.Expr {
	result := make([]ast.Expr, 0, len(idents))
	for _, v := range idents {
		result = append(result, v)
	}
	return result
}

func callExprWith(funcName string, params map[string]*ast.Ident) *ast.CallExpr {
	return &ast.CallExpr{
		Fun:  ast.NewIdent(funcName),
		Args: exprsFrom(params),
	}
}

func fieldsFrom(params map[string]*ast.Ident) (result []*ast.Field) {
	for key, val := range params {
		result = append(result, &ast.Field{
			Names: []*ast.Ident{ast.NewIdent(key)},
			Type:  ast.NewIdent(deduceTypeString(val)),
		})
	}
	return
}

func deduceTypes(exprs []ast.Expr) []*ast.Field {
	var result []*ast.Field
	for _, expr := range exprs {
		returnTypeString := deduceTypeString(expr)
		if returnTypeString != "" {
			result = append(result, &ast.Field{Type: ast.NewIdent(returnTypeString)})
		}
	}
	return result
}

func deduceTypeString(expr ast.Expr) string {
	switch typedExpr := expr.(type) {
	case *ast.BasicLit:
		return strings.ToLower(typedExpr.Kind.String())
	case *ast.CallExpr:
		if typedExpr.Fun.(*ast.Ident).Obj.Decl.(*ast.FuncDecl).Type.Results == nil {
			return ""
		}
		result := ""
		for _, res := range typedExpr.Fun.(*ast.Ident).Obj.Decl.(*ast.FuncDecl).Type.Results.List {
			result += " " + res.Type.(*ast.Ident).Name
		}
		return result
	case *ast.Ident:
		// return typedExpr.Obj.Type.(*ast.Ident).Name
		return findTypeFor(typedExpr.Obj.Name, typedExpr.Obj.Decl.(*ast.AssignStmt))
	default:
		return fmt.Sprintf("UnresolvedType_%T", expr)
	}
}

func findTypeFor(name string, assignStmt *ast.AssignStmt) string {
	for i := range assignStmt.Lhs {
		if assignStmt.Lhs[i].(*ast.Ident).Name == name {
			return deduceTypeString(assignStmt.Rhs[i])
		}
	}
	return "UnresolvedType"
}
<commit_msg>Remove forgotten commented code<commit_after>// Copyright 2015 Peter Goetz
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"go/ast"
	"go/token"
	"os/exec"
	"strings"

	"github.com/petergtz/goextract/util"
)

func ExtractFileToFile(inputFileName string, selection Selection, extractedFuncName string, outputFilename string) {
	fileSet, astFile := astFromFile(inputFileName)
	createAstFileDump(inputFileName+".ast", fileSet, astFile)
	doExtraction(fileSet, astFile, selection, extractedFuncName)
	util.WriteFileAsStringOrPanic(outputFilename, stringFrom(fileSet, astFile))
	err := exec.Command("gofmt", "-w", outputFilename).Run()
	util.PanicOnError(err)
}

func ExtractFileToString(inputFileName string, selection Selection, extractedFuncName string) string {
	fileSet, astFile := astFromFile(inputFileName)
	createAstFileDump(inputFileName+".ast", fileSet, astFile)
	doExtraction(fileSet, astFile, selection, extractedFuncName)
	return stringFrom(fileSet, astFile)
}

func ExtractStringToString(input string, selection Selection, extractedFuncName string) string {
	fileSet, astFile := astFromInput(input)
	doExtraction(fileSet, astFile, selection, extractedFuncName)
	return stringFrom(fileSet, astFile)
}

func doExtraction(fileSet *token.FileSet, astFile *ast.File, selection Selection, extractedFuncName string) {
	visitor := &astNodeVisitorForExpressions{parentNode: nil, context: &expressionVisitorContext{fset: fileSet, selection: selection}}
	ast.Walk(visitor, astFile)
	if visitor.context.exprToExtract != nil {
		extractExpression(astFile, fileSet, visitor.context.exprToExtract, visitor.context.parent, extractedFuncName)
	} else {
		v := &astNodeVisitorForMultipleStatements{parentNode: nil, context: &multipleStatementVisitorContext{fset: fileSet, selection: selection}}
		ast.Walk(v, astFile)
		if v.context.posParent != v.context.endParent {
			panic(fmt.Sprintf("Selection is not valid. posParent: %v; endParent: %v",
				v.context.posParent, v.context.endParent))
		}
		if v.context.posParent == nil {
			panic(fmt.Sprintf("Selection is not valid. posParent: %v; endParent: %v",
				v.context.posParent, v.context.endParent))
		}
		extractMultipleStatements(astFile, fileSet, v.context.nodesToExtract, v.context.posParent, extractedFuncName)
	}
}

func globalVarIdents(astFile *ast.File) map[string]*ast.Ident {
	result := make(map[string]*ast.Ident)
	ast.Inspect(astFile, func(node ast.Node) bool {
		switch typedNode := node.(type) {
		case *ast.FuncDecl:
			return false
		case *ast.GenDecl:
			if typedNode.Tok.String() == "var" {
				for _, spec := range typedNode.Specs {
					for _, name := range spec.(*ast.ValueSpec).Names {
						result[name.Name] = name
					}
				}
			}
			return true
		default:
			return true
		}
	})
	return result
}

func varIdentsDeclaredWithin(nodes []ast.Node) map[string]*ast.Ident {
	result := make(map[string]*ast.Ident)
	for _, node := range nodes {
		ast.Inspect(node, func(node ast.Node) bool {
			if assignStmt, ok := node.(*ast.AssignStmt); ok && assignStmt.Tok.String() == ":=" {
				for i := range assignStmt.Lhs {
					result[assignStmt.Lhs[i].(*ast.Ident).Name] = assignStmt.Lhs[i].(*ast.Ident)
				}
			}
			return true
		})
	}
	return result
}

func varIdentsUsedIn(nodes []ast.Node) map[string]*ast.Ident {
	result := make(map[string]*ast.Ident)
	for _, node := range nodes {
		ast.Inspect(node, func(node ast.Node) bool {
			if ident, ok := node.(*ast.Ident); ok &&
				ident.Obj != nil && ident.Obj.Kind == ast.Var {
				switch typedDecl := ident.Obj.Decl.(type) {
				case *ast.AssignStmt:
					for _, lhs := range typedDecl.Lhs {
						if lhs.(*ast.Ident).Name == ident.Name {
							result[ident.Name] = ident
						}
					}
				default:
					result[ident.Name] = ast.NewIdent("UnresolvedType")
				}
			}
			return true
		})
	}
	return result
}

// TODO rename to varIdentsUsedIn
func overlappingVarsIdentsUsedIn(stmts []ast.Stmt, outOf map[string]*ast.Ident) map[string]*ast.Ident {
	result := make(map[string]*ast.Ident)
	for _, stmt := range stmts {
		ast.Inspect(stmt, func(node ast.Node) bool {
			if ident, ok := node.(*ast.Ident); ok {
				if outOf[ident.Name] != nil {
					result[ident.Name] = ident
				}
			}
			return true
		})
	}
	return result
}

func namesOf(idents map[string]*ast.Ident) []string {
	result := make([]string, 0, len(idents))
	for k := range idents {
		result = append(result, k)
	}
	return result
}

func exprsFrom(idents map[string]*ast.Ident) []ast.Expr {
	result := make([]ast.Expr, 0, len(idents))
	for _, v := range idents {
		result = append(result, v)
	}
	return result
}

func callExprWith(funcName string, params map[string]*ast.Ident) *ast.CallExpr {
	return &ast.CallExpr{
		Fun:  ast.NewIdent(funcName),
		Args: exprsFrom(params),
	}
}

func fieldsFrom(params map[string]*ast.Ident) (result []*ast.Field) {
	for key, val := range params {
		result = append(result, &ast.Field{
			Names: []*ast.Ident{ast.NewIdent(key)},
			Type:  ast.NewIdent(deduceTypeString(val)),
		})
	}
	return
}

func deduceTypes(exprs []ast.Expr) []*ast.Field {
	var result []*ast.Field
	for _, expr := range exprs {
		returnTypeString := deduceTypeString(expr)
		if returnTypeString != "" {
			result = append(result, &ast.Field{Type: ast.NewIdent(returnTypeString)})
		}
	}
	return result
}

func deduceTypeString(expr ast.Expr) string {
	switch typedExpr := expr.(type) {
	case *ast.BasicLit:
		return strings.ToLower(typedExpr.Kind.String())
	case *ast.CallExpr:
		if typedExpr.Fun.(*ast.Ident).Obj.Decl.(*ast.FuncDecl).Type.Results == nil {
			return ""
		}
		result := ""
		for _, res := range typedExpr.Fun.(*ast.Ident).Obj.Decl.(*ast.FuncDecl).Type.Results.List {
			result += " " + res.Type.(*ast.Ident).Name
		}
		return result
	case *ast.Ident:
		// return typedExpr.Obj.Type.(*ast.Ident).Name
		return findTypeFor(typedExpr.Obj.Name, typedExpr.Obj.Decl.(*ast.AssignStmt))
	default:
		return fmt.Sprintf("UnresolvedType_%T", expr)
	}
}

func findTypeFor(name string, assignStmt *ast.AssignStmt) string {
	for i := range assignStmt.Lhs {
		if assignStmt.Lhs[i].(*ast.Ident).Name == name {
			return deduceTypeString(assignStmt.Rhs[i])
		}
	}
	return "UnresolvedType"
}
<|endoftext|>