<commit_before>/*
Copyright 2020 gRPC authors.

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.
*/

// Configure is an executable that generates a defaults file for the manager.
// It accepts a template file and replaces placeholders with data that may
// change based on where the manager and container images will live and run.
//
// This tool uses Go's text/template package for templating, see
// https://pkg.go.dev/text/template for a description of the syntax.

package main

import (
	"flag"
	"fmt"
	"log"
	"os/exec"
	"strings"
)

func main() {
	var imagePrefix string
	var tagOfImagesToDelete string

	flag.StringVar(&imagePrefix, "p", "", "set the root repository for search")
	flag.StringVar(&tagOfImagesToDelete, "t", "", "images with this tag will be deleted")

	flag.Parse()

	getRepository := exec.Command("gcloud", "container", "images", "list", fmt.Sprintf("--repository=%s", imagePrefix))
	getRepositoryOutput, err := getRepository.CombinedOutput()
	if err != nil {
		log.Printf("failed getting repositories within %s: %s\n", imagePrefix, string(getRepositoryOutput))
	}

	log.Printf("all image repositories within specified registry: %s\n", imagePrefix)
	log.Println(string(getRepositoryOutput))

	allRepositories := strings.Split(string(getRepositoryOutput), "\n")
	for i, curRepository := range allRepositories {
		if i == 0 || curRepository == "" {
			continue
		}
		log.Printf("processing image repository: %s\n", curRepository)

		curImageToProcess := fmt.Sprintf("%s:%s", curRepository, tagOfImagesToDelete)

		getImageHaveTheTag := exec.Command("gcloud", "container", "images", "list-tags", curRepository, fmt.Sprintf("--filter=%s", tagOfImagesToDelete))
		getImageHaveTheTagOutput, err := getImageHaveTheTag.CombinedOutput()
		if err != nil {
			log.Printf("failed getting image: %s with tag %s: %s\n", curRepository, tagOfImagesToDelete, string(getImageHaveTheTagOutput))
		}

		imageFullLine := strings.Split(string(getImageHaveTheTagOutput), "\n")
		if len(imageFullLine) <= 2 {
			log.Printf("tag: %s is not presented.\n", tagOfImagesToDelete)
			continue
		}

		numbersOfTagsOfCurrentImage := len(strings.Split(strings.Fields(imageFullLine[1])[1], ","))

		if numbersOfTagsOfCurrentImage > 1 {
			log.Printf("image have multiple tags, including %s, untag the image with tag %s instead of deleting image\n", tagOfImagesToDelete, tagOfImagesToDelete)
			untagImages := exec.Command("gcloud", "-q", "container", "images", "untag", curImageToProcess)
			unTagImageOutput, err := untagImages.CombinedOutput()
			if err != nil {
				log.Printf("failed untagging %s: %s\n", curImageToProcess, string(unTagImageOutput))
			}
			log.Printf("succeeded untagging %s:%s\n", curRepository, tagOfImagesToDelete)
		} else {
			deleteImage := exec.Command("gcloud", "-q", "container", "images", "delete", curImageToProcess)
			deleteImageOutput, err := deleteImage.CombinedOutput()
			if err != nil {
				log.Printf("failed deleting image %s : %s\n", curImageToProcess, string(deleteImageOutput))
			}
			log.Printf("succeeded deleting delete %s\n", curImageToProcess)
		}
	}
	log.Printf("all images with tag: %s within container registry: %s are processed.\n", tagOfImagesToDelete, imagePrefix)
}
<commit_msg>Cleanup delete_prebuilt_workers.go (#146)<commit_after>/*
Copyright 2020 gRPC authors.

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.
*/

// Configure is an executable that generates a defaults file for the manager.
// It accepts a template file and replaces placeholders with data that may
// change based on where the manager and container images will live and run.
//
// This tool uses Go's text/template package for templating, see
// https://pkg.go.dev/text/template for a description of the syntax.

package main

import (
	"flag"
	"fmt"
	"log"
	"os/exec"
	"strings"
)

func main() {
	var imagePrefix string
	var tagOfImagesToDelete string

	flag.StringVar(&imagePrefix, "p", "", "set the root repository for search")
	flag.StringVar(&tagOfImagesToDelete, "t", "", "images with this tag will be deleted")

	flag.Parse()

	if len(imagePrefix) == 0 {
		log.Fatalln("no root repository is provided")
	}

	if len(tagOfImagesToDelete) == 0 {
		log.Fatalln("no image tag is provided")
	}

	log.Printf("start to process all images within %s having tag: %s", imagePrefix, tagOfImagesToDelete)

	getRepository := exec.Command("gcloud", "container", "images", "list", fmt.Sprintf("--repository=%s", imagePrefix))
	getRepositoryOutput, err := getRepository.CombinedOutput()
	if err != nil {
		log.Printf("failed getting repositories within %s: %s\n", imagePrefix, string(getRepositoryOutput))
	}

	log.Printf("all image repositories within specified registry: %s\n", imagePrefix)
	log.Println(string(getRepositoryOutput))

	allRepositories := strings.Split(string(getRepositoryOutput), "\n")
	for i, curRepository := range allRepositories {
		if i == 0 || curRepository == "" {
			continue
		}
		log.Printf("processing image repository: %s\n", curRepository)

		curImageToProcess := fmt.Sprintf("%s:%s", curRepository, tagOfImagesToDelete)

		getImageHaveTheTag := exec.Command("gcloud", "container", "images", "list-tags", curRepository, fmt.Sprintf("--filter=%s", tagOfImagesToDelete))
		getImageHaveTheTagOutput, err := getImageHaveTheTag.CombinedOutput()
		if err != nil {
			log.Printf("failed getting image: %s with tag %s: %s\n", curRepository, tagOfImagesToDelete, string(getImageHaveTheTagOutput))
		}

		imageFullLine := strings.Split(string(getImageHaveTheTagOutput), "\n")
		if len(imageFullLine) <= 2 {
			log.Printf("tag: %s is not presented.\n", tagOfImagesToDelete)
			continue
		}

		numbersOfTagsOfCurrentImage := len(strings.Split(strings.Fields(imageFullLine[1])[1], ","))

		if numbersOfTagsOfCurrentImage > 1 {
			log.Printf("image have multiple tags, including %s, untag the image with tag %s instead of deleting image\n", tagOfImagesToDelete, tagOfImagesToDelete)
			untagImages := exec.Command("gcloud", "-q", "container", "images", "untag", curImageToProcess)
			unTagImageOutput, err := untagImages.CombinedOutput()
			if err != nil {
				log.Printf("failed untagging %s: %s\n", curImageToProcess, string(unTagImageOutput))
			}
			log.Printf("succeeded untagging %s:%s\n", curRepository, tagOfImagesToDelete)
		} else {
			deleteImage := exec.Command("gcloud", "-q", "container", "images", "delete", curImageToProcess)
			deleteImageOutput, err := deleteImage.CombinedOutput()
			if err != nil {
				log.Printf("failed deleting image %s : %s\n", curImageToProcess, string(deleteImageOutput))
			}
			log.Printf("succeeded deleting  %s\n", curImageToProcess)
		}
	}
	log.Printf("all images with tag: %s within container registry: %s are processed.\n", tagOfImagesToDelete, imagePrefix)
}
<|endoftext|>


<commit_before>package adoc

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"
)

// This part contains apis for the images listed in
// https://docs.docker.com/reference/api/docker_remote_api_v1.17/#22-images

type Image struct {
	Created     int64
	Id          string
	Labels      map[string]string
	ParentId    string
	RepoTags    []string
	Size        int64
	VirtualSize int64
	RepoDigests []string // v1.18
}

type ImageDetail struct {
	Architecture    string
	Author          string
	Comment         string
	Container       string
	ContainerConfig ContainerConfig
	Created         time.Time
	DockerVersion   string
	Id              string
	Os              string
	Parent          string
	Size            int64
	VirtualSize     int64
	//Config          ContainerConfig // don't know what this is for
}

func (client *DockerClient) ListImages(showAll bool, filters ...string) ([]Image, error) {
	v := url.Values{}
	v.Set("all", formatBoolToIntString(showAll))
	if len(filters) > 0 && filters[0] != "" {
		v.Set("filters", filters[0])
	}
	uri := fmt.Sprintf("images/json?%s", v.Encode())
	if data, err := client.sendRequest("GET", uri, nil, nil); err != nil {
		return nil, err
	} else {
		var images []Image
		err := json.Unmarshal(data, &images)
		return images, err
	}
}

func (client *DockerClient) InspectImage(name string) (ImageDetail, error) {
	var ret ImageDetail
	uri := fmt.Sprintf("images/%s/json", name)
	if data, err := client.sendRequest("GET", uri, nil, nil); err != nil {
		return ret, err
	} else {
		err := json.Unmarshal(data, &ret)
		return ret, err
	}
}

func (client *DockerClient) PullImage(name string, tag string, authConfig ...AuthConfig) error {
	v := url.Values{}
	v.Set("fromImage", name)
	v.Set("tag", tag)
	uri := fmt.Sprintf("images/create?%s", v.Encode())
	header := make(map[string]string)
	if len(authConfig) > 0 {
		header["X-Registry-Auth"] = authConfig[0].Encode()
	}
	err := client.sendRequestCallback("POST", uri, nil, header, func(resp *http.Response) error {
		var status map[string]interface{}
		var cbErr error
		decoder := json.NewDecoder(resp.Body)
		for ; cbErr == nil; cbErr = decoder.Decode(&status) {
		}
		if cbErr != io.EOF {
			return cbErr
		}
		if errMsg, ok := status["error"]; ok {
			return fmt.Errorf("Pull image error: %s", errMsg)
		}
		return nil
	}, true)
	return err
}

func (client *DockerClient) RemoveImage(name string, force, noprune bool) error {
	v := url.Values{}
	v.Set("force", formatBoolToIntString(force))
	v.Set("noprune", formatBoolToIntString(noprune))
	uri := fmt.Sprintf("images/%s?%s", name, v.Encode())
	_, err := client.sendRequest("DELETE", uri, nil, nil)
	return err
}

// Missing apis for
// build: Build image from a Dockerfile
// images/(name)/history
// images/(name)/push
// images/(name)/tag
// images/search
<commit_msg>add image push & image tag<commit_after>package adoc

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"
)

// This part contains apis for the images listed in
// https://docs.docker.com/reference/api/docker_remote_api_v1.17/#22-images

type Image struct {
	Created     int64
	Id          string
	Labels      map[string]string
	ParentId    string
	RepoTags    []string
	Size        int64
	VirtualSize int64
	RepoDigests []string // v1.18
}

type ImageDetail struct {
	Architecture    string
	Author          string
	Comment         string
	Container       string
	ContainerConfig ContainerConfig
	Created         time.Time
	DockerVersion   string
	Id              string
	Os              string
	Parent          string
	Size            int64
	VirtualSize     int64
	//Config          ContainerConfig // don't know what this is for
}

func (client *DockerClient) ListImages(showAll bool, filters ...string) ([]Image, error) {
	v := url.Values{}
	v.Set("all", formatBoolToIntString(showAll))
	if len(filters) > 0 && filters[0] != "" {
		v.Set("filters", filters[0])
	}
	uri := fmt.Sprintf("images/json?%s", v.Encode())
	if data, err := client.sendRequest("GET", uri, nil, nil); err != nil {
		return nil, err
	} else {
		var images []Image
		err := json.Unmarshal(data, &images)
		return images, err
	}
}

func (client *DockerClient) InspectImage(name string) (ImageDetail, error) {
	var ret ImageDetail
	uri := fmt.Sprintf("images/%s/json", name)
	if data, err := client.sendRequest("GET", uri, nil, nil); err != nil {
		return ret, err
	} else {
		err := json.Unmarshal(data, &ret)
		return ret, err
	}
}

func (client *DockerClient) PullImage(name string, tag string, authConfig ...AuthConfig) error {
	v := url.Values{}
	v.Set("fromImage", name)
	v.Set("tag", tag)
	uri := fmt.Sprintf("images/create?%s", v.Encode())
	header := make(map[string]string)
	if len(authConfig) > 0 {
		header["X-Registry-Auth"] = authConfig[0].Encode()
	}
	err := client.sendRequestCallback("POST", uri, nil, header, func(resp *http.Response) error {
		var status map[string]interface{}
		var cbErr error
		decoder := json.NewDecoder(resp.Body)
		for ; cbErr == nil; cbErr = decoder.Decode(&status) {
		}
		if cbErr != io.EOF {
			return cbErr
		}
		if errMsg, ok := status["error"]; ok {
			return fmt.Errorf("Pull image error: %s", errMsg)
		}
		return nil
	}, true)
	return err
}

func (client *DockerClient) RemoveImage(name string, force, noprune bool) error {
	v := url.Values{}
	v.Set("force", formatBoolToIntString(force))
	v.Set("noprune", formatBoolToIntString(noprune))
	uri := fmt.Sprintf("images/%s?%s", name, v.Encode())
	_, err := client.sendRequest("DELETE", uri, nil, nil)
	return err
}

func (client *DockerClient) TagImage(name string, repo string, tag string, force bool) error {
	v := url.Values{}
	v.Set("force", formatBoolToIntString(force))
	v.Set("repo", repo)
	v.Set("tag", tag)
	uri := fmt.Sprintf("images/%s/tag?%s", name, v.Encode())
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) PushImage(name string, repo string, tag string, authConfig ...AuthConfig) error {
	v := url.Values{}
	v.Set("tag", tag)
	uri := fmt.Sprintf("images/%s/%s/push?%s", repo, name, v.Encode())
	header := make(map[string]string)
	if len(authConfig) > 0 {
		header["X-Registry-Auth"] = authConfig[0].Encode()
	}
	err := client.sendRequestCallback("POST", uri, nil, header, func(resp *http.Response) error {
		var status map[string]interface{}
		var cbErr error
		decoder := json.NewDecoder(resp.Body)
		for ; cbErr == nil; cbErr = decoder.Decode(&status) {
		}
		if cbErr != io.EOF {
			return cbErr
		}
		if errMsg, ok := status["error"]; ok {
			return fmt.Errorf("Push image error: %s", errMsg)
		}
		return nil
	}, true)
	return err
}

// Missing apis for
// build: Build image from a Dockerfile
// images/(name)/history
// images/search
<|endoftext|>


<commit_before>package discordgo

import (
	"github.com/jonas747/gojay"
	"github.com/pkg/errors"
)

// This file contains all the possible structs that can be
// handled by AddHandler/EventHandler.
// DO NOT ADD ANYTHING BUT EVENT HANDLER STRUCTS TO THIS FILE.
//go:generate go run tools/cmd/eventhandlers/main.go

// Connect is the data for a Connect event.
// This is a sythetic event and is not dispatched by Discord.
type Connect struct{}

// Disconnect is the data for a Disconnect event.
// This is a sythetic event and is not dispatched by Discord.
type Disconnect struct{}

// RateLimit is the data for a RateLimit event.
// This is a sythetic event and is not dispatched by Discord.
type RateLimit struct {
	*TooManyRequests
	URL string
}

// Event provides a basic initial struct for all websocket events.
type Event struct {
	Operation GatewayOP          `json:"op"`
	Sequence  int64              `json:"s"`
	Type      string             `json:"t"`
	RawData   gojay.EmbeddedJSON `json:"d"`
	// Struct contains one of the other types in this file.
	Struct interface{} `json:"-"`
}

// implement gojay.UnmarshalerJSONObject
func (evt *Event) UnmarshalJSONObject(dec *gojay.Decoder, key string) error {
	switch key {
	case "op":
		return dec.Int((*int)(&evt.Operation))
	case "s":
		return dec.Int64(&evt.Sequence)
	case "t":
		return dec.String(&evt.Type)
	case "d":
		if cap(evt.RawData) > 1000000 && len(evt.RawData) < 1000000 {
			evt.RawData = nil
		} else if evt.RawData != nil {
			evt.RawData = evt.RawData[:0]
		}

		return dec.AddEmbeddedJSON(&evt.RawData)
	}

	return nil
}

func (evt *Event) NKeys() int {
	return 0
}

// A Ready stores all data for the websocket READY event.
type Ready struct {
	Version         int          `json:"v"`
	SessionID       string       `json:"session_id"`
	User            *SelfUser    `json:"user"`
	ReadState       []*ReadState `json:"read_state"`
	PrivateChannels []*Channel   `json:"private_channels"`
	Guilds          []*Guild     `json:"guilds"`

	// Undocumented fields
	Settings          *Settings            `json:"user_settings"`
	UserGuildSettings []*UserGuildSettings `json:"user_guild_settings"`
	Relationships     []*Relationship      `json:"relationships"`
	Presences         []*Presence          `json:"presences"`
	Notes             map[string]string    `json:"notes"`
}

// ChannelCreate is the data for a ChannelCreate event.
type ChannelCreate struct {
	*Channel
}

// ChannelUpdate is the data for a ChannelUpdate event.
type ChannelUpdate struct {
	*Channel
}

// ChannelDelete is the data for a ChannelDelete event.
type ChannelDelete struct {
	*Channel
}

// ChannelPinsUpdate stores data for a ChannelPinsUpdate event.
type ChannelPinsUpdate struct {
	LastPinTimestamp string `json:"last_pin_timestamp"`
	ChannelID        int64  `json:"channel_id,string"`
	GuildID          int64  `json:"guild_id,string,omitempty"`
}

func (cp *ChannelPinsUpdate) GetGuildID() int64 {
	return cp.GuildID
}

func (cp *ChannelPinsUpdate) GetChannelID() int64 {
	return cp.ChannelID
}

// GuildCreate is the data for a GuildCreate event.
type GuildCreate struct {
	*Guild
}

// GuildUpdate is the data for a GuildUpdate event.
type GuildUpdate struct {
	*Guild
}

// GuildDelete is the data for a GuildDelete event.
type GuildDelete struct {
	*Guild
}

// GuildBanAdd is the data for a GuildBanAdd event.
type GuildBanAdd struct {
	User    *User `json:"user"`
	GuildID int64 `json:"guild_id,string"`
}

func (gba *GuildBanAdd) GetGuildID() int64 {
	return gba.GuildID
}

// GuildBanRemove is the data for a GuildBanRemove event.
type GuildBanRemove struct {
	User    *User `json:"user"`
	GuildID int64 `json:"guild_id,string"`
}

func (e *GuildBanRemove) GetGuildID() int64 {
	return e.GuildID
}

// GuildMemberAdd is the data for a GuildMemberAdd event.
type GuildMemberAdd struct {
	*Member
}

// GuildMemberUpdate is the data for a GuildMemberUpdate event.
type GuildMemberUpdate struct {
	*Member
}

// GuildMemberRemove is the data for a GuildMemberRemove event.
type GuildMemberRemove struct {
	*Member
}

// GuildRoleCreate is the data for a GuildRoleCreate event.
type GuildRoleCreate struct {
	*GuildRole
}

// GuildRoleUpdate is the data for a GuildRoleUpdate event.
type GuildRoleUpdate struct {
	*GuildRole
}

// A GuildRoleDelete is the data for a GuildRoleDelete event.
type GuildRoleDelete struct {
	RoleID  int64 `json:"role_id,string"`
	GuildID int64 `json:"guild_id,string"`
}

func (e *GuildRoleDelete) GetGuildID() int64 {
	return e.GuildID
}

// A GuildEmojisUpdate is the data for a guild emoji update event.
type GuildEmojisUpdate struct {
	GuildID int64    `json:"guild_id,string"`
	Emojis  []*Emoji `json:"emojis"`
}

func (e *GuildEmojisUpdate) GetGuildID() int64 {
	return e.GuildID
}

// A GuildMembersChunk is the data for a GuildMembersChunk event.
type GuildMembersChunk struct {
	GuildID    int64     `json:"guild_id,string"`
	Members    []*Member `json:"members"`
	ChunkIndex int       `json:"chunk_index"`
	ChunkCount int       `json:"chunk_count"`
	Nonce      string    `json:"nonce"`
}

func (e *GuildMembersChunk) GetGuildID() int64 {
	return e.GuildID
}

// GuildIntegrationsUpdate is the data for a GuildIntegrationsUpdate event.
type GuildIntegrationsUpdate struct {
	GuildID int64 `json:"guild_id,string"`
}

func (e *GuildIntegrationsUpdate) GetGuildID() int64 {
	return e.GuildID
}

// MessageAck is the data for a MessageAck event.
type MessageAck struct {
	MessageID int64 `json:"message_id,string"`
	ChannelID int64 `json:"channel_id,string"`
}

// MessageCreate is the data for a MessageCreate event.
type MessageCreate struct {
	*Message
}

// MessageUpdate is the data for a MessageUpdate event.
type MessageUpdate struct {
	*Message
}

// MessageDelete is the data for a MessageDelete event.
type MessageDelete struct {
	*Message
}

// MessageReactionAdd is the data for a MessageReactionAdd event.
type MessageReactionAdd struct {
	*MessageReaction
}

// MessageReactionRemove is the data for a MessageReactionRemove event.
type MessageReactionRemove struct {
	*MessageReaction
}

// MessageReactionRemoveAll is the data for a MessageReactionRemoveAll event.
type MessageReactionRemoveAll struct {
	*MessageReaction
}

// PresencesReplace is the data for a PresencesReplace event.
type PresencesReplace []*Presence

// PresenceUpdate is the data for a PresenceUpdate event.
//easyjson:json
type PresenceUpdate struct {
	Presence
	GuildID int64 `json:"guild_id,string"`
}

func (e *PresenceUpdate) GetGuildID() int64 {
	return e.GuildID
}

// implement gojay.UnmarshalerJSONObject
func (p *PresenceUpdate) UnmarshalJSONObject(dec *gojay.Decoder, key string) error {
	switch key {
	case "guild_id":
		return errors.Wrap(DecodeSnowflake(&p.GuildID, dec), key)
	default:
		return p.Presence.UnmarshalJSONObject(dec, key)
	}
}

func (p *PresenceUpdate) NKeys() int {
	return 0
}

// Resumed is the data for a Resumed event.
type Resumed struct {
	Trace []string `json:"_trace"`
}

// RelationshipAdd is the data for a RelationshipAdd event.
type RelationshipAdd struct {
	*Relationship
}

// RelationshipRemove is the data for a RelationshipRemove event.
type RelationshipRemove struct {
	*Relationship
}

var _ gojay.UnmarshalerJSONObject = (*TypingStart)(nil)

// TypingStart is the data for a TypingStart event.
type TypingStart struct {
	UserID    int64 `json:"user_id,string"`
	ChannelID int64 `json:"channel_id,string"`
	Timestamp int   `json:"timestamp"`
	GuildID   int64 `json:"guild_id,string,omitempty"`
}

// implement gojay.UnmarshalerJSONObject
func (ts *TypingStart) UnmarshalJSONObject(dec *gojay.Decoder, key string) error {
	switch key {
	case "user_id":
		return DecodeSnowflake(&ts.UserID, dec)
	case "channel_id":
		return DecodeSnowflake(&ts.ChannelID, dec)
	case "guild_id":
		return DecodeSnowflake(&ts.GuildID, dec)
	case "timestamp":
		return dec.Int(&ts.Timestamp)
	}

	return nil
}

func (ts *TypingStart) NKeys() int {
	return 0
}

func (e *TypingStart) GetGuildID() int64 {
	return e.GuildID
}

func (e *TypingStart) GetChannelID() int64 {
	return e.ChannelID
}

// UserUpdate is the data for a UserUpdate event.
type UserUpdate struct {
	*User
}

// UserSettingsUpdate is the data for a UserSettingsUpdate event.
type UserSettingsUpdate map[string]interface{}

// UserGuildSettingsUpdate is the data for a UserGuildSettingsUpdate event.
type UserGuildSettingsUpdate struct {
	*UserGuildSettings
}

// UserNoteUpdate is the data for a UserNoteUpdate event.
type UserNoteUpdate struct {
	ID   int64  `json:"id,string"`
	Note string `json:"note"`
}

// VoiceServerUpdate is the data for a VoiceServerUpdate event.
type VoiceServerUpdate struct {
	Token    string `json:"token"`
	GuildID  int64  `json:"guild_id,string"`
	Endpoint string `json:"endpoint"`
}

func (e *VoiceServerUpdate) GetGuildID() int64 {
	return e.GuildID
}

// VoiceStateUpdate is the data for a VoiceStateUpdate event.
type VoiceStateUpdate struct {
	*VoiceState
}

// MessageDeleteBulk is the data for a MessageDeleteBulk event
type MessageDeleteBulk struct {
	Messages  IDSlice `json:"ids,string"`
	ChannelID int64   `json:"channel_id,string"`
	GuildID   int64   `json:"guild_id,string"`
}

func (e *MessageDeleteBulk) GetGuildID() int64 {
	return e.GuildID
}

func (e *MessageDeleteBulk) GetChannelID() int64 {
	return e.ChannelID
}

// WebhooksUpdate is the data for a WebhooksUpdate event
type WebhooksUpdate struct {
	GuildID   int64 `json:"guild_id,string"`
	ChannelID int64 `json:"channel_id,string"`
}

func (e *WebhooksUpdate) GetGuildID() int64 {
	return e.GuildID
}

func (e *WebhooksUpdate) GetChannelID() int64 {
	return e.ChannelID
}

// InviteCreate is the data for the InviteCreate event
type InviteCreate struct {
	GuildID   int64 `json:"guild_id,string"`
	ChannelID int64 `json:"channel_id,string"`

	Code      string    `json:"code"`
	CreatedAt Timestamp `json:"created_at"`

	MaxAge    int  `json:"max_age"`
	MaxUses   int  `json:"max_uses"`
	Temporary bool `json:"temporary"`
	Uses      int  `json:"uses"`

	Inviter *InviteUser `json:"inviter"`
}

// InviteDelete is the data for the InviteDelete event
type InviteDelete struct {
	GuildID   int64  `json:"guild_id,string"`
	ChannelID int64  `json:"channel_id,string"`
	Code      string `json:"code"`
}
<commit_msg>fix user update event<commit_after>package discordgo

import (
	"github.com/jonas747/gojay"
	"github.com/pkg/errors"
)

// This file contains all the possible structs that can be
// handled by AddHandler/EventHandler.
// DO NOT ADD ANYTHING BUT EVENT HANDLER STRUCTS TO THIS FILE.
//go:generate go run tools/cmd/eventhandlers/main.go

// Connect is the data for a Connect event.
// This is a sythetic event and is not dispatched by Discord.
type Connect struct{}

// Disconnect is the data for a Disconnect event.
// This is a sythetic event and is not dispatched by Discord.
type Disconnect struct{}

// RateLimit is the data for a RateLimit event.
// This is a sythetic event and is not dispatched by Discord.
type RateLimit struct {
	*TooManyRequests
	URL string
}

// Event provides a basic initial struct for all websocket events.
type Event struct {
	Operation GatewayOP          `json:"op"`
	Sequence  int64              `json:"s"`
	Type      string             `json:"t"`
	RawData   gojay.EmbeddedJSON `json:"d"`
	// Struct contains one of the other types in this file.
	Struct interface{} `json:"-"`
}

// implement gojay.UnmarshalerJSONObject
func (evt *Event) UnmarshalJSONObject(dec *gojay.Decoder, key string) error {
	switch key {
	case "op":
		return dec.Int((*int)(&evt.Operation))
	case "s":
		return dec.Int64(&evt.Sequence)
	case "t":
		return dec.String(&evt.Type)
	case "d":
		if cap(evt.RawData) > 1000000 && len(evt.RawData) < 1000000 {
			evt.RawData = nil
		} else if evt.RawData != nil {
			evt.RawData = evt.RawData[:0]
		}

		return dec.AddEmbeddedJSON(&evt.RawData)
	}

	return nil
}

func (evt *Event) NKeys() int {
	return 0
}

// A Ready stores all data for the websocket READY event.
type Ready struct {
	Version         int          `json:"v"`
	SessionID       string       `json:"session_id"`
	User            *SelfUser    `json:"user"`
	ReadState       []*ReadState `json:"read_state"`
	PrivateChannels []*Channel   `json:"private_channels"`
	Guilds          []*Guild     `json:"guilds"`

	// Undocumented fields
	Settings          *Settings            `json:"user_settings"`
	UserGuildSettings []*UserGuildSettings `json:"user_guild_settings"`
	Relationships     []*Relationship      `json:"relationships"`
	Presences         []*Presence          `json:"presences"`
	Notes             map[string]string    `json:"notes"`
}

// ChannelCreate is the data for a ChannelCreate event.
type ChannelCreate struct {
	*Channel
}

// ChannelUpdate is the data for a ChannelUpdate event.
type ChannelUpdate struct {
	*Channel
}

// ChannelDelete is the data for a ChannelDelete event.
type ChannelDelete struct {
	*Channel
}

// ChannelPinsUpdate stores data for a ChannelPinsUpdate event.
type ChannelPinsUpdate struct {
	LastPinTimestamp string `json:"last_pin_timestamp"`
	ChannelID        int64  `json:"channel_id,string"`
	GuildID          int64  `json:"guild_id,string,omitempty"`
}

func (cp *ChannelPinsUpdate) GetGuildID() int64 {
	return cp.GuildID
}

func (cp *ChannelPinsUpdate) GetChannelID() int64 {
	return cp.ChannelID
}

// GuildCreate is the data for a GuildCreate event.
type GuildCreate struct {
	*Guild
}

// GuildUpdate is the data for a GuildUpdate event.
type GuildUpdate struct {
	*Guild
}

// GuildDelete is the data for a GuildDelete event.
type GuildDelete struct {
	*Guild
}

// GuildBanAdd is the data for a GuildBanAdd event.
type GuildBanAdd struct {
	User    *User `json:"user"`
	GuildID int64 `json:"guild_id,string"`
}

func (gba *GuildBanAdd) GetGuildID() int64 {
	return gba.GuildID
}

// GuildBanRemove is the data for a GuildBanRemove event.
type GuildBanRemove struct {
	User    *User `json:"user"`
	GuildID int64 `json:"guild_id,string"`
}

func (e *GuildBanRemove) GetGuildID() int64 {
	return e.GuildID
}

// GuildMemberAdd is the data for a GuildMemberAdd event.
type GuildMemberAdd struct {
	*Member
}

// GuildMemberUpdate is the data for a GuildMemberUpdate event.
type GuildMemberUpdate struct {
	*Member
}

// GuildMemberRemove is the data for a GuildMemberRemove event.
type GuildMemberRemove struct {
	*Member
}

// GuildRoleCreate is the data for a GuildRoleCreate event.
type GuildRoleCreate struct {
	*GuildRole
}

// GuildRoleUpdate is the data for a GuildRoleUpdate event.
type GuildRoleUpdate struct {
	*GuildRole
}

// A GuildRoleDelete is the data for a GuildRoleDelete event.
type GuildRoleDelete struct {
	RoleID  int64 `json:"role_id,string"`
	GuildID int64 `json:"guild_id,string"`
}

func (e *GuildRoleDelete) GetGuildID() int64 {
	return e.GuildID
}

// A GuildEmojisUpdate is the data for a guild emoji update event.
type GuildEmojisUpdate struct {
	GuildID int64    `json:"guild_id,string"`
	Emojis  []*Emoji `json:"emojis"`
}

func (e *GuildEmojisUpdate) GetGuildID() int64 {
	return e.GuildID
}

// A GuildMembersChunk is the data for a GuildMembersChunk event.
type GuildMembersChunk struct {
	GuildID    int64     `json:"guild_id,string"`
	Members    []*Member `json:"members"`
	ChunkIndex int       `json:"chunk_index"`
	ChunkCount int       `json:"chunk_count"`
	Nonce      string    `json:"nonce"`
}

func (e *GuildMembersChunk) GetGuildID() int64 {
	return e.GuildID
}

// GuildIntegrationsUpdate is the data for a GuildIntegrationsUpdate event.
type GuildIntegrationsUpdate struct {
	GuildID int64 `json:"guild_id,string"`
}

func (e *GuildIntegrationsUpdate) GetGuildID() int64 {
	return e.GuildID
}

// MessageAck is the data for a MessageAck event.
type MessageAck struct {
	MessageID int64 `json:"message_id,string"`
	ChannelID int64 `json:"channel_id,string"`
}

// MessageCreate is the data for a MessageCreate event.
type MessageCreate struct {
	*Message
}

// MessageUpdate is the data for a MessageUpdate event.
type MessageUpdate struct {
	*Message
}

// MessageDelete is the data for a MessageDelete event.
type MessageDelete struct {
	*Message
}

// MessageReactionAdd is the data for a MessageReactionAdd event.
type MessageReactionAdd struct {
	*MessageReaction
}

// MessageReactionRemove is the data for a MessageReactionRemove event.
type MessageReactionRemove struct {
	*MessageReaction
}

// MessageReactionRemoveAll is the data for a MessageReactionRemoveAll event.
type MessageReactionRemoveAll struct {
	*MessageReaction
}

// PresencesReplace is the data for a PresencesReplace event.
type PresencesReplace []*Presence

// PresenceUpdate is the data for a PresenceUpdate event.
//easyjson:json
type PresenceUpdate struct {
	Presence
	GuildID int64 `json:"guild_id,string"`
}

func (e *PresenceUpdate) GetGuildID() int64 {
	return e.GuildID
}

// implement gojay.UnmarshalerJSONObject
func (p *PresenceUpdate) UnmarshalJSONObject(dec *gojay.Decoder, key string) error {
	switch key {
	case "guild_id":
		return errors.Wrap(DecodeSnowflake(&p.GuildID, dec), key)
	default:
		return p.Presence.UnmarshalJSONObject(dec, key)
	}
}

func (p *PresenceUpdate) NKeys() int {
	return 0
}

// Resumed is the data for a Resumed event.
type Resumed struct {
	Trace []string `json:"_trace"`
}

// RelationshipAdd is the data for a RelationshipAdd event.
type RelationshipAdd struct {
	*Relationship
}

// RelationshipRemove is the data for a RelationshipRemove event.
type RelationshipRemove struct {
	*Relationship
}

var _ gojay.UnmarshalerJSONObject = (*TypingStart)(nil)

// TypingStart is the data for a TypingStart event.
type TypingStart struct {
	UserID    int64 `json:"user_id,string"`
	ChannelID int64 `json:"channel_id,string"`
	Timestamp int   `json:"timestamp"`
	GuildID   int64 `json:"guild_id,string,omitempty"`
}

// implement gojay.UnmarshalerJSONObject
func (ts *TypingStart) UnmarshalJSONObject(dec *gojay.Decoder, key string) error {
	switch key {
	case "user_id":
		return DecodeSnowflake(&ts.UserID, dec)
	case "channel_id":
		return DecodeSnowflake(&ts.ChannelID, dec)
	case "guild_id":
		return DecodeSnowflake(&ts.GuildID, dec)
	case "timestamp":
		return dec.Int(&ts.Timestamp)
	}

	return nil
}

func (ts *TypingStart) NKeys() int {
	return 0
}

func (e *TypingStart) GetGuildID() int64 {
	return e.GuildID
}

func (e *TypingStart) GetChannelID() int64 {
	return e.ChannelID
}

// UserUpdate is the data for a UserUpdate event.
type UserUpdate struct {
	*User
}

// implement gojay.UnmarshalerJSONObject
func (u *UserUpdate) UnmarshalJSONObject(dec *gojay.Decoder, key string) error {
	u.User = &User{}
	return u.User.UnmarshalJSONObject(dec, key)
}

func (u *UserUpdate) NKeys() int {
	return 0
}

// UserSettingsUpdate is the data for a UserSettingsUpdate event.
type UserSettingsUpdate map[string]interface{}

// UserGuildSettingsUpdate is the data for a UserGuildSettingsUpdate event.
type UserGuildSettingsUpdate struct {
	*UserGuildSettings
}

// UserNoteUpdate is the data for a UserNoteUpdate event.
type UserNoteUpdate struct {
	ID   int64  `json:"id,string"`
	Note string `json:"note"`
}

// VoiceServerUpdate is the data for a VoiceServerUpdate event.
type VoiceServerUpdate struct {
	Token    string `json:"token"`
	GuildID  int64  `json:"guild_id,string"`
	Endpoint string `json:"endpoint"`
}

func (e *VoiceServerUpdate) GetGuildID() int64 {
	return e.GuildID
}

// VoiceStateUpdate is the data for a VoiceStateUpdate event.
type VoiceStateUpdate struct {
	*VoiceState
}

// MessageDeleteBulk is the data for a MessageDeleteBulk event
type MessageDeleteBulk struct {
	Messages  IDSlice `json:"ids,string"`
	ChannelID int64   `json:"channel_id,string"`
	GuildID   int64   `json:"guild_id,string"`
}

func (e *MessageDeleteBulk) GetGuildID() int64 {
	return e.GuildID
}

func (e *MessageDeleteBulk) GetChannelID() int64 {
	return e.ChannelID
}

// WebhooksUpdate is the data for a WebhooksUpdate event
type WebhooksUpdate struct {
	GuildID   int64 `json:"guild_id,string"`
	ChannelID int64 `json:"channel_id,string"`
}

func (e *WebhooksUpdate) GetGuildID() int64 {
	return e.GuildID
}

func (e *WebhooksUpdate) GetChannelID() int64 {
	return e.ChannelID
}

// InviteCreate is the data for the InviteCreate event
type InviteCreate struct {
	GuildID   int64 `json:"guild_id,string"`
	ChannelID int64 `json:"channel_id,string"`

	Code      string    `json:"code"`
	CreatedAt Timestamp `json:"created_at"`

	MaxAge    int  `json:"max_age"`
	MaxUses   int  `json:"max_uses"`
	Temporary bool `json:"temporary"`
	Uses      int  `json:"uses"`

	Inviter *InviteUser `json:"inviter"`
}

// InviteDelete is the data for the InviteDelete event
type InviteDelete struct {
	GuildID   int64  `json:"guild_id,string"`
	ChannelID int64  `json:"channel_id,string"`
	Code      string `json:"code"`
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package xml

import (
	"bytes"
	"io"
	"os"
	"reflect"
	"strings"
	"unicode"
)

// BUG(rsc): Mapping between XML elements and data structures is inherently flawed:
// an XML element is an order-dependent collection of anonymous
// values, while a data structure is an order-independent collection
// of named values.
// See package json for a textual representation more suitable
// to data structures.

// Unmarshal parses an XML element from r and uses the
// reflect library to fill in an arbitrary struct, slice, or string
// pointed at by val.  Well-formed data that does not fit
// into val is discarded.
//
// For example, given these definitions:
//
//	type Email struct {
//		Where string "attr";
//		Addr string;
//	}
//
//	type Result struct {
//		XMLName xml.Name "result";
//		Name string;
//		Phone string;
//		Email []Email;
//	}
//
//	var result = Result{ "name", "phone", nil }
//
// unmarshalling the XML input
//
//	<result>
//		<email where="home">
//			<addr>gre@example.com</addr>
//		</email>
//		<email where='work'>
//			<addr>gre@work.com</addr>
//		</email>
//		<name>Grace R. Emlin</name>
//		<address>123 Main Street</address>
//	</result>
//
// via Unmarshal(r, &result) is equivalent to assigning
//
//	r = Result{
//		xml.Name{"", "result"},
//		"Grace R. Emlin",	// name
//		"phone",	// no phone given
//		[]Email{
//			Email{ "home", "gre@example.com" },
//			Email{ "work", "gre@work.com" }
//		}
//	}
//
// Note that the field r.Phone has not been modified and
// that the XML <address> element was discarded.
//
// Because Unmarshal uses the reflect package, it can only
// assign to upper case fields.  Unmarshal uses a case-insensitive
// comparison to match XML element names to struct field names.
//
// Unmarshal maps an XML element to a struct using the following rules:
//
//   * If the struct has a field named XMLName of type xml.Name,
//      Unmarshal records the element name in that field.
//
//   * If the XMLName field has an associated tag string of the form
//      "tag" or "namespace-URL tag", the XML element must have
//      the given tag (and, optionally, name space) or else Unmarshal
//      returns an error.
//
//   * If the XML element has an attribute whose name matches a
//      struct field of type string with tag "attr", Unmarshal records
//      the attribute value in that field.
//
//   * If the XML element contains character data, that data is
//      accumulated in the first struct field that has tag "chardata".
//      The struct field may have type []byte or string.
//      If there is no such field, the character data is discarded.
//
//   * If the XML element contains a sub-element whose name
//      matches a struct field whose tag is neither "attr" nor "chardata",
//      Unmarshal maps the sub-element to that struct field.
//      Otherwise, if the struct has a field named Any, unmarshal
//      maps the sub-element to that struct field.
//
// Unmarshal maps an XML element to a string or []byte by saving the
// concatenation of that elements character data in the string or []byte.
//
// Unmarshal maps an XML element to a slice by extending the length
// of the slice and mapping the element to the newly created value.
//
// Unmarshal maps an XML element to a bool by setting the bool to true.
//
// Unmarshal maps an XML element to an xml.Name by recording the
// element name.
//
// Unmarshal maps an XML element to a pointer by setting the pointer
// to a freshly allocated value and then mapping the element to that value.
//
func Unmarshal(r io.Reader, val interface{}) os.Error {
	v, ok := reflect.NewValue(val).(*reflect.PtrValue)
	if !ok {
		return os.NewError("non-pointer passed to Unmarshal")
	}
	p := NewParser(r)
	elem := v.Elem()
	err := p.unmarshal(elem, nil)
	if err != nil {
		return err
	}
	return nil
}

// An UnmarshalError represents an error in the unmarshalling process.
type UnmarshalError string

func (e UnmarshalError) String() string { return string(e) }

// The Parser's Unmarshal method is like xml.Unmarshal
// except that it can be passed a pointer to the initial start element,
// useful when a client reads some raw XML tokens itself
// but also defers to Unmarshal for some elements.
// Passing a nil start element indicates that Unmarshal should
// read the token stream to find the start element.
func (p *Parser) Unmarshal(val interface{}, start *StartElement) os.Error {
	v, ok := reflect.NewValue(val).(*reflect.PtrValue)
	if !ok {
		return os.NewError("non-pointer passed to Unmarshal")
	}
	return p.unmarshal(v.Elem(), start)
}

// fieldName strips invalid characters from an XML name
// to create a valid Go struct name.  It also converts the
// name to lower case letters.
func fieldName(original string) string {
	return strings.Map(
		func(x int) int {
			if unicode.IsDigit(x) || unicode.IsLetter(x) {
				return unicode.ToLower(x)
			}
			return -1
		},
		original)
}

// Unmarshal a single XML element into val.
func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
	// Find start element if we need it.
	if start == nil {
		for {
			tok, err := p.Token()
			if err != nil {
				return err
			}
			if t, ok := tok.(StartElement); ok {
				start = &t
				break
			}
		}
	}

	if pv, ok := val.(*reflect.PtrValue); ok {
		if pv.Get() == 0 {
			zv := reflect.MakeZero(pv.Type().(*reflect.PtrType).Elem())
			pv.PointTo(zv)
			val = zv
		} else {
			val = pv.Elem()
		}
	}

	var (
		data        []byte
		saveData    reflect.Value
		comment     []byte
		saveComment reflect.Value
		sv          *reflect.StructValue
		styp        *reflect.StructType
	)
	switch v := val.(type) {
	case *reflect.BoolValue:
		v.Set(true)

	case *reflect.SliceValue:
		typ := v.Type().(*reflect.SliceType)
		if _, ok := typ.Elem().(*reflect.Uint8Type); ok {
			// []byte
			saveData = v
			break
		}

		// Slice of element values.
		// Grow slice.
		n := v.Len()
		if n >= v.Cap() {
			ncap := 2 * n
			if ncap < 4 {
				ncap = 4
			}
			new := reflect.MakeSlice(typ, n, ncap)
			reflect.ArrayCopy(new, v)
			v.Set(new)
		}
		v.SetLen(n + 1)

		// Recur to read element into slice.
		if err := p.unmarshal(v.Elem(n), start); err != nil {
			v.SetLen(n)
			return err
		}
		return nil

	case *reflect.StringValue:
		saveData = v

	case *reflect.StructValue:
		if _, ok := v.Interface().(Name); ok {
			v.Set(reflect.NewValue(start.Name).(*reflect.StructValue))
			break
		}

		sv = v
		typ := sv.Type().(*reflect.StructType)
		styp = typ
		// Assign name.
		if f, ok := typ.FieldByName("XMLName"); ok {
			// Validate element name.
			if f.Tag != "" {
				tag := f.Tag
				ns := ""
				i := strings.LastIndex(tag, " ")
				if i >= 0 {
					ns, tag = tag[0:i], tag[i+1:]
				}
				if tag != start.Name.Local {
					return UnmarshalError("expected element type <" + tag + "> but have <" + start.Name.Local + ">")
				}
				if ns != "" && ns != start.Name.Space {
					e := "expected element <" + tag + "> in name space " + ns + " but have "
					if start.Name.Space == "" {
						e += "no name space"
					} else {
						e += start.Name.Space
					}
					return UnmarshalError(e)
				}
			}

			// Save
			v := sv.FieldByIndex(f.Index)
			if _, ok := v.Interface().(Name); !ok {
				return UnmarshalError(sv.Type().String() + " field XMLName does not have type xml.Name")
			}
			v.(*reflect.StructValue).Set(reflect.NewValue(start.Name).(*reflect.StructValue))
		}

		// Assign attributes.
		// Also, determine whether we need to save character data or comments.
		for i, n := 0, typ.NumField(); i < n; i++ {
			f := typ.Field(i)
			switch f.Tag {
			case "attr":
				strv, ok := sv.FieldByIndex(f.Index).(*reflect.StringValue)
				if !ok {
					return UnmarshalError(sv.Type().String() + " field " + f.Name + " has attr tag but is not type string")
				}
				// Look for attribute.
				val := ""
				k := strings.ToLower(f.Name)
				for _, a := range start.Attr {
					if fieldName(a.Name.Local) == k {
						val = a.Value
						break
					}
				}
				strv.Set(val)

			case "comment":
				if saveComment == nil {
					saveComment = sv.FieldByIndex(f.Index)
				}

			case "chardata":
				if saveData == nil {
					saveData = sv.FieldByIndex(f.Index)
				}
			}
		}
	}

	// Find end element.
	// Process sub-elements along the way.
Loop:
	for {
		tok, err := p.Token()
		if err != nil {
			return err
		}
		switch t := tok.(type) {
		case StartElement:
			// Sub-element.
			// Look up by tag name.
			// If that fails, fall back to mop-up field named "Any".
			if sv != nil {
				k := fieldName(t.Name.Local)
				any := -1
				for i, n := 0, styp.NumField(); i < n; i++ {
					f := styp.Field(i)
					if strings.ToLower(f.Name) == k {
						if err := p.unmarshal(sv.FieldByIndex(f.Index), &t); err != nil {
							return err
						}
						continue Loop
					}
					if any < 0 && f.Name == "Any" {
						any = i
					}
				}
				if any >= 0 {
					if err := p.unmarshal(sv.FieldByIndex(styp.Field(any).Index), &t); err != nil {
						return err
					}
					continue Loop
				}
			}
			// Not saving sub-element but still have to skip over it.
			if err := p.Skip(); err != nil {
				return err
			}

		case EndElement:
			break Loop

		case CharData:
			if saveData != nil {
				data = bytes.Add(data, t)
			}

		case Comment:
			if saveComment != nil {
				comment = bytes.Add(comment, t)
			}
		}
	}

	// Save accumulated character data and comments
	switch t := saveData.(type) {
	case *reflect.StringValue:
		t.Set(string(data))
	case *reflect.SliceValue:
		t.Set(reflect.NewValue(data).(*reflect.SliceValue))
	}

	switch t := saveComment.(type) {
	case *reflect.StringValue:
		t.Set(string(comment))
	case *reflect.SliceValue:
		t.Set(reflect.NewValue(comment).(*reflect.SliceValue))
	}

	return nil
}

// Have already read a start element.
// Read tokens until we find the end element.
// Token is taking care of making sure the
// end element matches the start element we saw.
func (p *Parser) Skip() os.Error {
	for {
		tok, err := p.Token()
		if err != nil {
			return err
		}
		switch t := tok.(type) {
		case StartElement:
			if err := p.Skip(); err != nil {
				return err
			}
		case EndElement:
			return nil
		}
	}
	panic("unreachable")
}
<commit_msg>xml: Fix comment so that example code compiles<commit_after>// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package xml

import (
	"bytes"
	"io"
	"os"
	"reflect"
	"strings"
	"unicode"
)

// BUG(rsc): Mapping between XML elements and data structures is inherently flawed:
// an XML element is an order-dependent collection of anonymous
// values, while a data structure is an order-independent collection
// of named values.
// See package json for a textual representation more suitable
// to data structures.

// Unmarshal parses an XML element from r and uses the
// reflect library to fill in an arbitrary struct, slice, or string
// pointed at by val.  Well-formed data that does not fit
// into val is discarded.
//
// For example, given these definitions:
//
//	type Email struct {
//		Where string "attr";
//		Addr string;
//	}
//
//	type Result struct {
//		XMLName xml.Name "result";
//		Name string;
//		Phone string;
//		Email []Email;
//	}
//
//	result := Result{ Name: "name", Phone: "phone", Email: nil }
//
// unmarshalling the XML input
//
//	<result>
//		<email where="home">
//			<addr>gre@example.com</addr>
//		</email>
//		<email where='work'>
//			<addr>gre@work.com</addr>
//		</email>
//		<name>Grace R. Emlin</name>
//		<address>123 Main Street</address>
//	</result>
//
// via Unmarshal(r, &result) is equivalent to assigning
//
//	r = Result{
//		xml.Name{"", "result"},
//		"Grace R. Emlin",	// name
//		"phone",	// no phone given
//		[]Email{
//			Email{ "home", "gre@example.com" },
//			Email{ "work", "gre@work.com" }
//		}
//	}
//
// Note that the field r.Phone has not been modified and
// that the XML <address> element was discarded.
//
// Because Unmarshal uses the reflect package, it can only
// assign to upper case fields.  Unmarshal uses a case-insensitive
// comparison to match XML element names to struct field names.
//
// Unmarshal maps an XML element to a struct using the following rules:
//
//   * If the struct has a field named XMLName of type xml.Name,
//      Unmarshal records the element name in that field.
//
//   * If the XMLName field has an associated tag string of the form
//      "tag" or "namespace-URL tag", the XML element must have
//      the given tag (and, optionally, name space) or else Unmarshal
//      returns an error.
//
//   * If the XML element has an attribute whose name matches a
//      struct field of type string with tag "attr", Unmarshal records
//      the attribute value in that field.
//
//   * If the XML element contains character data, that data is
//      accumulated in the first struct field that has tag "chardata".
//      The struct field may have type []byte or string.
//      If there is no such field, the character data is discarded.
//
//   * If the XML element contains a sub-element whose name
//      matches a struct field whose tag is neither "attr" nor "chardata",
//      Unmarshal maps the sub-element to that struct field.
//      Otherwise, if the struct has a field named Any, unmarshal
//      maps the sub-element to that struct field.
//
// Unmarshal maps an XML element to a string or []byte by saving the
// concatenation of that elements character data in the string or []byte.
//
// Unmarshal maps an XML element to a slice by extending the length
// of the slice and mapping the element to the newly created value.
//
// Unmarshal maps an XML element to a bool by setting the bool to true.
//
// Unmarshal maps an XML element to an xml.Name by recording the
// element name.
//
// Unmarshal maps an XML element to a pointer by setting the pointer
// to a freshly allocated value and then mapping the element to that value.
//
func Unmarshal(r io.Reader, val interface{}) os.Error {
	v, ok := reflect.NewValue(val).(*reflect.PtrValue)
	if !ok {
		return os.NewError("non-pointer passed to Unmarshal")
	}
	p := NewParser(r)
	elem := v.Elem()
	err := p.unmarshal(elem, nil)
	if err != nil {
		return err
	}
	return nil
}

// An UnmarshalError represents an error in the unmarshalling process.
type UnmarshalError string

func (e UnmarshalError) String() string { return string(e) }

// The Parser's Unmarshal method is like xml.Unmarshal
// except that it can be passed a pointer to the initial start element,
// useful when a client reads some raw XML tokens itself
// but also defers to Unmarshal for some elements.
// Passing a nil start element indicates that Unmarshal should
// read the token stream to find the start element.
func (p *Parser) Unmarshal(val interface{}, start *StartElement) os.Error {
	v, ok := reflect.NewValue(val).(*reflect.PtrValue)
	if !ok {
		return os.NewError("non-pointer passed to Unmarshal")
	}
	return p.unmarshal(v.Elem(), start)
}

// fieldName strips invalid characters from an XML name
// to create a valid Go struct name.  It also converts the
// name to lower case letters.
func fieldName(original string) string {
	return strings.Map(
		func(x int) int {
			if unicode.IsDigit(x) || unicode.IsLetter(x) {
				return unicode.ToLower(x)
			}
			return -1
		},
		original)
}

// Unmarshal a single XML element into val.
func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
	// Find start element if we need it.
	if start == nil {
		for {
			tok, err := p.Token()
			if err != nil {
				return err
			}
			if t, ok := tok.(StartElement); ok {
				start = &t
				break
			}
		}
	}

	if pv, ok := val.(*reflect.PtrValue); ok {
		if pv.Get() == 0 {
			zv := reflect.MakeZero(pv.Type().(*reflect.PtrType).Elem())
			pv.PointTo(zv)
			val = zv
		} else {
			val = pv.Elem()
		}
	}

	var (
		data        []byte
		saveData    reflect.Value
		comment     []byte
		saveComment reflect.Value
		sv          *reflect.StructValue
		styp        *reflect.StructType
	)
	switch v := val.(type) {
	case *reflect.BoolValue:
		v.Set(true)

	case *reflect.SliceValue:
		typ := v.Type().(*reflect.SliceType)
		if _, ok := typ.Elem().(*reflect.Uint8Type); ok {
			// []byte
			saveData = v
			break
		}

		// Slice of element values.
		// Grow slice.
		n := v.Len()
		if n >= v.Cap() {
			ncap := 2 * n
			if ncap < 4 {
				ncap = 4
			}
			new := reflect.MakeSlice(typ, n, ncap)
			reflect.ArrayCopy(new, v)
			v.Set(new)
		}
		v.SetLen(n + 1)

		// Recur to read element into slice.
		if err := p.unmarshal(v.Elem(n), start); err != nil {
			v.SetLen(n)
			return err
		}
		return nil

	case *reflect.StringValue:
		saveData = v

	case *reflect.StructValue:
		if _, ok := v.Interface().(Name); ok {
			v.Set(reflect.NewValue(start.Name).(*reflect.StructValue))
			break
		}

		sv = v
		typ := sv.Type().(*reflect.StructType)
		styp = typ
		// Assign name.
		if f, ok := typ.FieldByName("XMLName"); ok {
			// Validate element name.
			if f.Tag != "" {
				tag := f.Tag
				ns := ""
				i := strings.LastIndex(tag, " ")
				if i >= 0 {
					ns, tag = tag[0:i], tag[i+1:]
				}
				if tag != start.Name.Local {
					return UnmarshalError("expected element type <" + tag + "> but have <" + start.Name.Local + ">")
				}
				if ns != "" && ns != start.Name.Space {
					e := "expected element <" + tag + "> in name space " + ns + " but have "
					if start.Name.Space == "" {
						e += "no name space"
					} else {
						e += start.Name.Space
					}
					return UnmarshalError(e)
				}
			}

			// Save
			v := sv.FieldByIndex(f.Index)
			if _, ok := v.Interface().(Name); !ok {
				return UnmarshalError(sv.Type().String() + " field XMLName does not have type xml.Name")
			}
			v.(*reflect.StructValue).Set(reflect.NewValue(start.Name).(*reflect.StructValue))
		}

		// Assign attributes.
		// Also, determine whether we need to save character data or comments.
		for i, n := 0, typ.NumField(); i < n; i++ {
			f := typ.Field(i)
			switch f.Tag {
			case "attr":
				strv, ok := sv.FieldByIndex(f.Index).(*reflect.StringValue)
				if !ok {
					return UnmarshalError(sv.Type().String() + " field " + f.Name + " has attr tag but is not type string")
				}
				// Look for attribute.
				val := ""
				k := strings.ToLower(f.Name)
				for _, a := range start.Attr {
					if fieldName(a.Name.Local) == k {
						val = a.Value
						break
					}
				}
				strv.Set(val)

			case "comment":
				if saveComment == nil {
					saveComment = sv.FieldByIndex(f.Index)
				}

			case "chardata":
				if saveData == nil {
					saveData = sv.FieldByIndex(f.Index)
				}
			}
		}
	}

	// Find end element.
	// Process sub-elements along the way.
Loop:
	for {
		tok, err := p.Token()
		if err != nil {
			return err
		}
		switch t := tok.(type) {
		case StartElement:
			// Sub-element.
			// Look up by tag name.
			// If that fails, fall back to mop-up field named "Any".
			if sv != nil {
				k := fieldName(t.Name.Local)
				any := -1
				for i, n := 0, styp.NumField(); i < n; i++ {
					f := styp.Field(i)
					if strings.ToLower(f.Name) == k {
						if err := p.unmarshal(sv.FieldByIndex(f.Index), &t); err != nil {
							return err
						}
						continue Loop
					}
					if any < 0 && f.Name == "Any" {
						any = i
					}
				}
				if any >= 0 {
					if err := p.unmarshal(sv.FieldByIndex(styp.Field(any).Index), &t); err != nil {
						return err
					}
					continue Loop
				}
			}
			// Not saving sub-element but still have to skip over it.
			if err := p.Skip(); err != nil {
				return err
			}

		case EndElement:
			break Loop

		case CharData:
			if saveData != nil {
				data = bytes.Add(data, t)
			}

		case Comment:
			if saveComment != nil {
				comment = bytes.Add(comment, t)
			}
		}
	}

	// Save accumulated character data and comments
	switch t := saveData.(type) {
	case *reflect.StringValue:
		t.Set(string(data))
	case *reflect.SliceValue:
		t.Set(reflect.NewValue(data).(*reflect.SliceValue))
	}

	switch t := saveComment.(type) {
	case *reflect.StringValue:
		t.Set(string(comment))
	case *reflect.SliceValue:
		t.Set(reflect.NewValue(comment).(*reflect.SliceValue))
	}

	return nil
}

// Have already read a start element.
// Read tokens until we find the end element.
// Token is taking care of making sure the
// end element matches the start element we saw.
func (p *Parser) Skip() os.Error {
	for {
		tok, err := p.Token()
		if err != nil {
			return err
		}
		switch t := tok.(type) {
		case StartElement:
			if err := p.Skip(); err != nil {
				return err
			}
		case EndElement:
			return nil
		}
	}
	panic("unreachable")
}
<|endoftext|>


<commit_before>package bot

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	log "github.com/Sirupsen/logrus"
	discord "github.com/bwmarrin/discordgo"
)

type DiscordConfig struct {
	Token        string `json:"token"`
	UseNicknames bool   `json:"use_nicknames"`
}

var (
	dBotID      string
	dSession    *discord.Session
	dGuilds     = map[string]string{}
	dGuildChans = map[string]map[string]string{}
)

func dInit() {
	d, err := discord.New(fmt.Sprintf("Bot %s", conf.Discord.Token))
	dSession = d
	if err != nil {
		log.Fatalf("Failed to initialise Discord session: %s", err)
	}

	u, err := dSession.User("@me")
	if err != nil {
		log.Fatalf("Failed to get own Discord user: %s", err)
	}

	dBotID = u.ID

	guilds, err := dSession.UserGuilds()
	if err != nil {
		log.Fatalf("Failed to get guilds: %s", err)
	}

	for _, g := range guilds {
		chans, err := dSession.GuildChannels(g.ID)
		if err != nil {
			log.Fatalf("Failed to get channels for %s: %s", g.Name, err)
		}

		dGuilds[g.Name] = g.ID
		dGuildChans[g.Name] = map[string]string{}
		for _, c := range chans {
			if c.Type == "text" {
				dGuildChans[g.Name][c.Name] = c.ID
			}
		}
	}

	dSession.AddHandler(dMessageCreate)

	err = dSession.Open()
	if err != nil {
		log.Fatalf("Failed to connect to Discord: %s", err)
	}

	log.Infof("Connected to Discord")
}

func dMessageCreate(s *discord.Session, m *discord.MessageCreate) {
	if m.Author.ID == dBotID {
		return
	}

	c, err := s.Channel(m.ChannelID)
	if err != nil {
		log.Errorf("Failed to get channel for incoming message with CID %s: %s", m.ChannelID, err)
		return
	}

	guildID := c.GuildID

	g, err := s.Guild(guildID)
	if err != nil {
		log.Errorf("Failed to get guild with ID %s: %s", guildID, err)
		return
	}

	channel := fmt.Sprintf("%s#%s", g.Name, c.Name)
	authorName := getDisplayNameForUser(m.Author, g.Members)

	if m.Content != "" {
		message := m.Content

		// Channels
		for _, c := range g.Channels {
			if c.Type != "text" {
				continue
			}
			find := fmt.Sprintf("<#%s>", c.ID)
			replace := fmt.Sprintf("#%s", c.Name)
			message = strings.Replace(message, find, replace, -1)
		}

		// Users
		for _, u := range g.Members {
			find := fmt.Sprintf("<@%s>", u.User.ID)
			find2 := fmt.Sprintf("<@!%s>", u.User.ID)
			replace := fmt.Sprintf("@%s", getDisplayNameForMember(u))
			message = strings.Replace(message, find, replace, -1)
			message = strings.Replace(message, find2, replace, -1)
		}

		// Roles
		for _, r := range g.Roles {
			find := fmt.Sprintf("<@&%s>", r.ID)
			replace := fmt.Sprintf("@%s", r.Name)
			message = strings.Replace(message, find, replace, -1)
		}

		// Multiline
		lines := strings.Split(message, "\n")
		if len(lines) > 3 {
			url := uploadToPtpb(message)

			for _, line := range lines[:2] {
				incomingDiscord(authorName, channel, line)
			}
			incomingDiscord("[SYSTEM]", channel, fmt.Sprintf("full message from %s: %s", iAddAntiPing(authorName), url))
		} else {
			for _, line := range lines {
				incomingDiscord(authorName, channel, line)
			}
		}
	}
	for _, a := range m.Attachments {
		incomingDiscord(authorName, channel, a.ProxyURL)
	}
}

func uploadToPtpb(s string) string {
	resp, err := http.PostForm("https://ptpb.pw/",
		url.Values{"c": {s}, "p": {"1"}})

	if err != nil {
		log.Errorf("Failed to upload to PTPB: %s", err)
		return "Failed to upload to PTPB"
	}
	defer resp.Body.Close()
	if resp.StatusCode == http.StatusOK {
		return resp.Header.Get("Location")
	}

	log.Errorf("Failed to upload to PTPB: HTTP %d", resp.StatusCode)
	ret, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Errorf("Failed to read body: %s", err)
	} else {
		log.Errorf(string(ret))
	}
	return fmt.Sprintf("Failed to upload to PTPB: HTTP %d", resp.StatusCode)

}

func dOutgoing(nick, channel, message string) {
	chanParts := strings.Split(channel, "#")
	guildID := dGuilds[chanParts[0]]
	chanID := dGuildChans[chanParts[0]][chanParts[1]]

	g, err := dSession.Guild(guildID)
	if err != nil {
		log.Errorf("Failed to get guild with ID %s: %s", guildID, err)
		return
	}

	// Channels
	for _, c := range g.Channels {
		if c.Type != "text" {
			continue
		}
		find := fmt.Sprintf("#%s", c.Name)
		replace := fmt.Sprintf("<#%s>", c.ID)
		message = strings.Replace(message, find, replace, -1)
	}

	// Users
	for _, u := range g.Members {
		find := fmt.Sprintf("@%s", getDisplayNameForMember(u))
		replace := fmt.Sprintf("<@%s>", u.User.ID)
		message = strings.Replace(message, find, replace, -1)
	}

	// Roles
	for _, r := range g.Roles {
		find := fmt.Sprintf("@%s", r.Name)
		replace := fmt.Sprintf("<@&%s>", r.ID)
		message = strings.Replace(message, find, replace, -1)
	}

	dSession.ChannelMessageSend(chanID, fmt.Sprintf("**<%s>** %s", nick, message))
}

func getDisplayNameForMember(member *discord.Member) string {
	if conf.Discord.UseNicknames && member.Nick != "" {
		return member.Nick
	}

	return member.User.Username
}

func getDisplayNameForUser(user *discord.User, members []*discord.Member) string {
	if conf.Discord.UseNicknames {
		for _, m := range members {
			if m.User.ID == user.ID {
				return getDisplayNameForMember(m)
			}
		}
	}

	return user.Username
}
<commit_msg>Run Discord messages in their own goroutine<commit_after>package bot

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	log "github.com/Sirupsen/logrus"
	discord "github.com/bwmarrin/discordgo"
)

type DiscordConfig struct {
	Token        string `json:"token"`
	UseNicknames bool   `json:"use_nicknames"`
}

var (
	dBotID      string
	dSession    *discord.Session
	dGuilds     = map[string]string{}
	dGuildChans = map[string]map[string]string{}

	dMsgQueue = make(chan func())
)

func dInit() {
	d, err := discord.New(fmt.Sprintf("Bot %s", conf.Discord.Token))
	dSession = d
	if err != nil {
		log.Fatalf("Failed to initialise Discord session: %s", err)
	}

	u, err := dSession.User("@me")
	if err != nil {
		log.Fatalf("Failed to get own Discord user: %s", err)
	}

	dBotID = u.ID

	guilds, err := dSession.UserGuilds()
	if err != nil {
		log.Fatalf("Failed to get guilds: %s", err)
	}

	for _, g := range guilds {
		chans, err := dSession.GuildChannels(g.ID)
		if err != nil {
			log.Fatalf("Failed to get channels for %s: %s", g.Name, err)
		}

		dGuilds[g.Name] = g.ID
		dGuildChans[g.Name] = map[string]string{}
		for _, c := range chans {
			if c.Type == "text" {
				dGuildChans[g.Name][c.Name] = c.ID
			}
		}
	}

	dSession.AddHandler(dMessageCreate)

	err = dSession.Open()
	if err != nil {
		log.Fatalf("Failed to connect to Discord: %s", err)
	}

	go func() {
		for f := range dMsgQueue {
			f()
		}
	}()

	log.Infof("Connected to Discord")
}

func dMessageCreate(s *discord.Session, m *discord.MessageCreate) {
	if m.Author.ID == dBotID {
		return
	}

	c, err := s.Channel(m.ChannelID)
	if err != nil {
		log.Errorf("Failed to get channel for incoming message with CID %s: %s", m.ChannelID, err)
		return
	}

	guildID := c.GuildID

	g, err := s.Guild(guildID)
	if err != nil {
		log.Errorf("Failed to get guild with ID %s: %s", guildID, err)
		return
	}

	channel := fmt.Sprintf("%s#%s", g.Name, c.Name)
	authorName := getDisplayNameForUser(m.Author, g.Members)

	if m.Content != "" {
		message := m.Content

		// Channels
		for _, c := range g.Channels {
			if c.Type != "text" {
				continue
			}
			find := fmt.Sprintf("<#%s>", c.ID)
			replace := fmt.Sprintf("#%s", c.Name)
			message = strings.Replace(message, find, replace, -1)
		}

		// Users
		for _, u := range g.Members {
			find := fmt.Sprintf("<@%s>", u.User.ID)
			find2 := fmt.Sprintf("<@!%s>", u.User.ID)
			replace := fmt.Sprintf("@%s", getDisplayNameForMember(u))
			message = strings.Replace(message, find, replace, -1)
			message = strings.Replace(message, find2, replace, -1)
		}

		// Roles
		for _, r := range g.Roles {
			find := fmt.Sprintf("<@&%s>", r.ID)
			replace := fmt.Sprintf("@%s", r.Name)
			message = strings.Replace(message, find, replace, -1)
		}

		// Multiline
		lines := strings.Split(message, "\n")
		if len(lines) > 3 {
			url := uploadToPtpb(message)

			for _, line := range lines[:2] {
				incomingDiscord(authorName, channel, line)
			}
			incomingDiscord("[SYSTEM]", channel, fmt.Sprintf("full message from %s: %s", iAddAntiPing(authorName), url))
		} else {
			for _, line := range lines {
				incomingDiscord(authorName, channel, line)
			}
		}
	}
	for _, a := range m.Attachments {
		incomingDiscord(authorName, channel, a.ProxyURL)
	}
}

func uploadToPtpb(s string) string {
	resp, err := http.PostForm("https://ptpb.pw/",
		url.Values{"c": {s}, "p": {"1"}})

	if err != nil {
		log.Errorf("Failed to upload to PTPB: %s", err)
		return "Failed to upload to PTPB"
	}
	defer resp.Body.Close()
	if resp.StatusCode == http.StatusOK {
		return resp.Header.Get("Location")
	}

	log.Errorf("Failed to upload to PTPB: HTTP %d", resp.StatusCode)
	ret, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Errorf("Failed to read body: %s", err)
	} else {
		log.Errorf(string(ret))
	}
	return fmt.Sprintf("Failed to upload to PTPB: HTTP %d", resp.StatusCode)

}

func dOutgoing(nick, channel, message string) {
	chanParts := strings.Split(channel, "#")
	guildID := dGuilds[chanParts[0]]
	chanID := dGuildChans[chanParts[0]][chanParts[1]]

	g, err := dSession.Guild(guildID)
	if err != nil {
		log.Errorf("Failed to get guild with ID %s: %s", guildID, err)
		return
	}

	// Channels
	for _, c := range g.Channels {
		if c.Type != "text" {
			continue
		}
		find := fmt.Sprintf("#%s", c.Name)
		replace := fmt.Sprintf("<#%s>", c.ID)
		message = strings.Replace(message, find, replace, -1)
	}

	// Users
	for _, u := range g.Members {
		find := fmt.Sprintf("@%s", getDisplayNameForMember(u))
		replace := fmt.Sprintf("<@%s>", u.User.ID)
		message = strings.Replace(message, find, replace, -1)
	}

	// Roles
	for _, r := range g.Roles {
		find := fmt.Sprintf("@%s", r.Name)
		replace := fmt.Sprintf("<@&%s>", r.ID)
		message = strings.Replace(message, find, replace, -1)
	}

	dMsgQueue <- func() {
		dSession.ChannelMessageSend(chanID, fmt.Sprintf("**<%s>** %s", nick, message))
	}
}

func getDisplayNameForMember(member *discord.Member) string {
	if conf.Discord.UseNicknames && member.Nick != "" {
		return member.Nick
	}

	return member.User.Username
}

func getDisplayNameForUser(user *discord.User, members []*discord.Member) string {
	if conf.Discord.UseNicknames {
		for _, m := range members {
			if m.User.ID == user.ID {
				return getDisplayNameForMember(m)
			}
		}
	}

	return user.Username
}
<|endoftext|>


<commit_before>// Copyright 2015 mparaiso<mparaiso@online.fr>. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package expect

import (
	"regexp"
	"strings"
	"testing"
)

// Expectation is an expectation to be tested
type Expectation struct {
	value interface{}
	test  *testing.T
}

// NegativeExpectation is a negative expectation to be tested
type NegativeExpectation struct {
	value interface{}
	test  *testing.T
}

type expectationBuilder struct {
	test *testing.T
}

// New returns a new expectationBuilder
// usefull if test suits contains many assertions
func New(t *testing.T) *expectationBuilder {
	return &expectationBuilder{t}
}

// Expect returns a new Expectation
func (e *expectationBuilder) Expect(val interface{}) *Expectation {
	return Expect(val, e.test)
}

// ToEqual expects 2 values to be equal
func (e *Expectation) ToEqual(val interface{}) {
	if e.value != val {
		e.test.Errorf("%+v should be equal to %+v", e.value, val)
	}
}

// ToPanic expects a function to panic when executed
func (e *Expectation) ToPanic() {
	defer func() {
		if err := recover(); err == nil {
			e.test.Errorf("%v should panic", e.value)
		}
	}()
	e.value.(func())()
}

// ToBe expects 2 values to be equal
func (e *Expectation) ToBe(val interface{}) {
	e.ToEqual(val)
}

// ToMatch expects a value to match a regular expression
func (e *Expectation) ToMatch(val string) {
	if match, err := regexp.MatchString(val, e.value.(string)); err != nil {
		e.test.Error(err)
	} else if match == false {
		e.test.Errorf("%+v should match to %+v", e.value, val)
	}
}

// ToBeNil expects a value to be nil
func (e *Expectation) ToBeNil() {
	if e.value != nil {
		e.test.Errorf("%+v should be nil", e.value)
	}
}

// ToBeTrue expects a value to be true
func (e *Expectation) ToBeTrue() {
	if e.value.(bool) != true {
		e.test.Errorf("%+v should be true", e.value)
	}
}

// ToBeFalse expects a value to be false
func (e *Expectation) ToBeFalse() {
	if e.value.(bool) != false {
		e.test.Errorf("%+v should be false", e.value)
	}
}

// ToContain expects a string to be a substring of value
func (e *Expectation) ToContain(word string) {

	if strings.Contains(e.value.(string), word) == false {
		e.test.Errorf("%+v should contain %+v", e.value, word)
	}
}

// toBeLessThan expects value to be less than  number
func (e *Expectation) toBeLessThan(number interface{}) {
	if toFloat64(e.value) >= toFloat64(number) {
		e.test.Errorf("%+v should be less then %+v", e.value, number)
	}
}

// ToBeGreaterThan expects value to be greater than number
func (e *Expectation) ToBeGreaterThan(number interface{}) {
	if toFloat64(e.value) <= toFloat64(number) {
		e.test.Errorf("%+v should greater than %+v", e.value, number)
	}
}

// Not reverse expectations
func (e *Expectation) Not() *NegativeExpectation {
	return &NegativeExpectation{e.value, e.test}
}

func (e *NegativeExpectation) ToEqual(val interface{}) {
	if e.value == val {
		e.test.Errorf("%+v should not be equal to %+v", e.value, val)
	}
}
func (e *NegativeExpectation) ToBe(val interface{}) {
	e.ToEqual(val)
}

func (e *NegativeExpectation) ToMatch(val string) {
	if match, err := regexp.MatchString(val, e.value.(string)); err != nil {
		e.test.Error(err)
	} else if match == true {
		e.test.Errorf("%+v should not match to %+v", e.value, val)
	}
}

func (e *NegativeExpectation) ToBeNil() {
	if e.value == nil {
		e.test.Errorf("%+v should not be nil", e.value)
	}
}

func (e *NegativeExpectation) ToBeTrue() {
	if e.value.(bool) == true {
		e.test.Errorf("%+v should not be true", e.value)
	}
}

func (e *NegativeExpectation) ToBeFalse() {
	if e.value.(bool) == false {
		e.test.Errorf("%+v should not be false", e.value)
	}
}

func (e *NegativeExpectation) ToContain(word string) {

	if strings.Contains(e.value.(string), word) == true {
		e.test.Errorf("%+v should not contain %+v", e.value, word)
	}
}

func (e *NegativeExpectation) toBeLessThan(number float64) {
	if toFloat64(e.value) < toFloat64(number) {
		e.test.Errorf("%+v should not be less than %+v", e.value, number)
	}
}

func (e *NegativeExpectation) ToBeGreaterThan(number interface{}) {
	if toFloat64(e.value) > toFloat64(number) {
		e.test.Errorf("%+v should not be greater than %+v", e.value, number)
	}
}

// ToPanic expects a function not to panic when executed
func (e *NegativeExpectation) ToPanic() {
	defer func() {
		if err := recover(); err != nil {
			e.test.Errorf("%+v should not panic", e.value)
		}
	}()
	e.value.(func())()
}

// Expect returns a new Expectation,
// usefull if a test suit contains only one assertion
func Expect(val interface{}, t *testing.T) *Expectation {
	return &Expectation{val, t}
}
<commit_msg>Added Equal<commit_after>// Copyright 2015 mparaiso<mparaiso@online.fr>. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package expect

import (
	"regexp"
	"strings"
	"testing"
)

// Expectation is an expectation to be tested
type Expectation struct {
	value interface{}
	test  *testing.T
}

// NegativeExpectation is a negative expectation to be tested
type NegativeExpectation struct {
	value interface{}
	test  *testing.T
}

type expectationBuilder struct {
	test *testing.T
}

// New returns a new expectationBuilder
// usefull if test suits contains many assertions
func New(t *testing.T) *expectationBuilder {
	return &expectationBuilder{t}
}

// Expect returns a new Expectation
func (e *expectationBuilder) Expect(val interface{}) *Expectation {
	return Expect(val, e.test)
}

// ToEqual expects 2 values to be equal
func (e *Expectation) ToEqual(val interface{}) {
	if e.value != val {
		e.test.Errorf("%+v should be equal to %+v", e.value, val)
	}
}

// ToPanic expects a function to panic when executed
func (e *Expectation) ToPanic() {
	defer func() {
		if err := recover(); err == nil {
			e.test.Errorf("%v should panic", e.value)
		}
	}()
	e.value.(func())()
}

// ToBe expects 2 values to be equal
func (e *Expectation) ToBe(val interface{}) {
	e.ToEqual(val)
}

// ToMatch expects a value to match a regular expression
func (e *Expectation) ToMatch(val string) {
	if match, err := regexp.MatchString(val, e.value.(string)); err != nil {
		e.test.Error(err)
	} else if match == false {
		e.test.Errorf("%+v should match to %+v", e.value, val)
	}
}

// ToBeNil expects a value to be nil
func (e *Expectation) ToBeNil() {
	if e.value != nil {
		e.test.Errorf("%+v should be nil", e.value)
	}
}

// ToBeTrue expects a value to be true
func (e *Expectation) ToBeTrue() {
	if e.value.(bool) != true {
		e.test.Errorf("%+v should be true", e.value)
	}
}

// ToBeFalse expects a value to be false
func (e *Expectation) ToBeFalse() {
	if e.value.(bool) != false {
		e.test.Errorf("%+v should be false", e.value)
	}
}

// ToContain expects a string to be a substring of value
func (e *Expectation) ToContain(word string) {

	if strings.Contains(e.value.(string), word) == false {
		e.test.Errorf("%+v should contain %+v", e.value, word)
	}
}

// toBeLessThan expects value to be less than  number
func (e *Expectation) toBeLessThan(number interface{}) {
	if toFloat64(e.value) >= toFloat64(number) {
		e.test.Errorf("%+v should be less then %+v", e.value, number)
	}
}

// ToBeGreaterThan expects value to be greater than number
func (e *Expectation) ToBeGreaterThan(number interface{}) {
	if toFloat64(e.value) <= toFloat64(number) {
		e.test.Errorf("%+v should greater than %+v", e.value, number)
	}
}

// Not reverse expectations
func (e *Expectation) Not() *NegativeExpectation {
	return &NegativeExpectation{e.value, e.test}
}

func (e *NegativeExpectation) ToEqual(val interface{}) {
	if e.value == val {
		e.test.Errorf("%+v should not be equal to %+v", e.value, val)
	}
}
func (e *NegativeExpectation) ToBe(val interface{}) {
	e.ToEqual(val)
}

func (e *NegativeExpectation) ToMatch(val string) {
	if match, err := regexp.MatchString(val, e.value.(string)); err != nil {
		e.test.Error(err)
	} else if match == true {
		e.test.Errorf("%+v should not match to %+v", e.value, val)
	}
}

func (e *NegativeExpectation) ToBeNil() {
	if e.value == nil {
		e.test.Errorf("%+v should not be nil", e.value)
	}
}

func (e *NegativeExpectation) ToBeTrue() {
	if e.value.(bool) == true {
		e.test.Errorf("%+v should not be true", e.value)
	}
}

func (e *NegativeExpectation) ToBeFalse() {
	if e.value.(bool) == false {
		e.test.Errorf("%+v should not be false", e.value)
	}
}

func (e *NegativeExpectation) ToContain(word string) {

	if strings.Contains(e.value.(string), word) == true {
		e.test.Errorf("%+v should not contain %+v", e.value, word)
	}
}

func (e *NegativeExpectation) toBeLessThan(number float64) {
	if toFloat64(e.value) < toFloat64(number) {
		e.test.Errorf("%+v should not be less than %+v", e.value, number)
	}
}

func (e *NegativeExpectation) ToBeGreaterThan(number interface{}) {
	if toFloat64(e.value) > toFloat64(number) {
		e.test.Errorf("%+v should not be greater than %+v", e.value, number)
	}
}

// ToPanic expects a function not to panic when executed
func (e *NegativeExpectation) ToPanic() {
	defer func() {
		if err := recover(); err != nil {
			e.test.Errorf("%+v should not panic", e.value)
		}
	}()
	e.value.(func())()
}

// Expect returns a new Expectation,
// usefull if a test suit contains only one assertion
func Expect(val interface{}, t *testing.T) *Expectation {
	return &Expectation{val, t}
}

// Equal is a helper used to reduce the boilerplate during test
func Equal(t *testing.T, got, want interface{}, comments ...string) {
	var comment string
	if want != got {
		if len(comments) > 0 {
			comment = comments[0]

		} else {
			comment = "Expect"
		}
		_, file, line, _ := runtime.Caller(1)
		t.Fatalf(fmt.Sprintf("Expect\r%s:%d:\r\t%s : %s", filepath.Base(file), line, comment, "want '%v' got '%v'."), want, got)
	}
}
<|endoftext|>


<commit_before>package rethink

import (
	r "github.com/dancannon/gorethink"
	"fmt"
)

type DB struct {
	Session *r.Session
}

func NewDB(session *r.Session) *DB {
	return &DB{
		Session: session,
	}
}

var emptyMap map[string]interface{}

func (db *DB) Get(table, id string) (map[string]interface{}, error) {
	result, err := r.Table(table).Get(id).RunRow(db.Session)
	switch {
	case err != nil:
		return emptyMap, err
	case result.IsNil():
		return emptyMap, fmt.Errorf("No such id: %s", id)
	default:
		var response map[string]interface{}
		result.Scan(&response)
		return response, nil
	}
}

func (db *DB) GetAll(query r.RqlTerm) ([]map[string]interface{}, error) {
	var results []map[string]interface{}
	rows, err := query.Run(db.Session)
	if err != nil {
		return results, err
	}

	for rows.Next() {
		var response map[string]interface{}
		rows.Scan(&response)
		results = append(results, response)
	}

	return results, nil
}
<commit_msg>Run go fmt.<commit_after>package rethink

import (
	"fmt"
	r "github.com/dancannon/gorethink"
)

type DB struct {
	Session *r.Session
}

func NewDB(session *r.Session) *DB {
	return &DB{
		Session: session,
	}
}

var emptyMap map[string]interface{}

func (db *DB) Get(table, id string) (map[string]interface{}, error) {
	result, err := r.Table(table).Get(id).RunRow(db.Session)
	switch {
	case err != nil:
		return emptyMap, err
	case result.IsNil():
		return emptyMap, fmt.Errorf("No such id: %s", id)
	default:
		var response map[string]interface{}
		result.Scan(&response)
		return response, nil
	}
}

func (db *DB) GetAll(query r.RqlTerm) ([]map[string]interface{}, error) {
	var results []map[string]interface{}
	rows, err := query.Run(db.Session)
	if err != nil {
		return results, err
	}

	for rows.Next() {
		var response map[string]interface{}
		rows.Scan(&response)
		results = append(results, response)
	}

	return results, nil
}
<|endoftext|>


<commit_before>// Copyright © 2017 Aqua Security Software Ltd. <info@aquasec.com>
//
// 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 check

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/golang/glog"
	yaml "gopkg.in/yaml.v2"
	"k8s.io/client-go/util/jsonpath"
)

// test:
// flag: OPTION
// set: (true|false)
// compare:
//   op: (eq|gt|gte|lt|lte|has)
//   value: val

type binOp string

const (
	and                   binOp = "and"
	or                          = "or"
	defaultArraySeparator       = ","
)

type testItem struct {
	Flag    string
	Path    string
	Output  string
	Value   string
	Set     bool
	Compare compare
}

type compare struct {
	Op    string
	Value string
}

type testOutput struct {
	testResult     bool
	actualResult   string
	ExpectedResult string
}

func failTestItem(s string) *testOutput {
	return &testOutput{testResult: false, actualResult: s}
}

func (t *testItem) execute(s string, isMultipleOutput bool) *testOutput {
	result := &testOutput{}
	s = strings.TrimRight(s, " \n")

	// If the test has output that should be evaluated for each row
	if isMultipleOutput {
		output := strings.Split(s, "\n")
		for _, op := range output {
			result = t.evaluate(op)
			// If the test failed for the current row, no need to keep testing for this output
			if !result.testResult {
				break
			}
		}
	} else {
		result = t.evaluate(s)
	}

	return result
}

func (t *testItem) evaluate(s string) *testOutput {
	result := &testOutput{}
	var match bool
	var flagVal string

	if t.Flag != "" {
		// Flag comparison: check if the flag is present in the input
		match = strings.Contains(s, t.Flag)
	} else {
		// Path != "" - we don't know whether it's YAML or JSON but
		// we can just try one then the other
		var jsonInterface interface{}

		if t.Path != "" {
			err := unmarshal(s, &jsonInterface)
			if err != nil {
				fmt.Fprintf(os.Stderr, "failed to load YAML or JSON from provided input \"%s\": %v\n", s, err)
				return failTestItem("failed to load YAML or JSON")
			}

		}

		jsonpathResult, err := executeJSONPath(t.Path, &jsonInterface)
		if err != nil {
			fmt.Fprintf(os.Stderr, "unable to parse path expression \"%s\": %v\n", t.Path, err)
			return failTestItem("error executing path expression")
		}
		match = (jsonpathResult != "")
		flagVal = jsonpathResult
	}

	if t.Set {
		isset := match

		if isset && t.Compare.Op != "" {
			if t.Flag != "" {
				// Expects flags in the form;
				// --flag=somevalue
				// flag: somevalue
				// --flag
				// somevalue
				pttn := `(` + t.Flag + `)(=|: *)*([^\s]*) *`
				flagRe := regexp.MustCompile(pttn)
				vals := flagRe.FindStringSubmatch(s)

				if len(vals) > 0 {
					if vals[3] != "" {
						flagVal = vals[3]
					} else {
						// --bool-flag
						if strings.HasPrefix(t.Flag, "--") {
							flagVal = "true"
						} else {
							flagVal = vals[1]
						}
					}
				} else {
					glog.V(1).Infof(fmt.Sprintf("invalid flag in testitem definition"))
					return failTestItem("error invalid flag in testitem definition")
				}
			}

			result.ExpectedResult, result.testResult = compareOp(t.Compare.Op, flagVal, t.Compare.Value)
		} else {
			result.ExpectedResult = fmt.Sprintf("'%s' is present", t.Flag)
			result.testResult = isset
		}
	} else {
		result.ExpectedResult = fmt.Sprintf("'%s' is not present", t.Flag)
		notset := !match
		result.testResult = notset
	}
	return result
}

func compareOp(tCompareOp string, flagVal string, tCompareValue string) (string, bool) {

	expectedResultPattern := ""
	testResult := false

	switch tCompareOp {
	case "eq":
		expectedResultPattern = "'%s' is equal to '%s'"
		value := strings.ToLower(flagVal)
		// Do case insensitive comparaison for booleans ...
		if value == "false" || value == "true" {
			testResult = value == tCompareValue
		} else {
			testResult = flagVal == tCompareValue
		}

	case "noteq":
		expectedResultPattern = "'%s' is not equal to '%s'"
		value := strings.ToLower(flagVal)
		// Do case insensitive comparaison for booleans ...
		if value == "false" || value == "true" {
			testResult = !(value == tCompareValue)
		} else {
			testResult = !(flagVal == tCompareValue)
		}

	case "gt", "gte", "lt", "lte":
		a, b, err := toNumeric(flagVal, tCompareValue)
		if err != nil {
			glog.V(1).Infof(fmt.Sprintf("Not numeric value - flag: %q - compareValue: %q %v\n", flagVal, tCompareValue, err))
			return "Invalid Number(s) used for comparison", false
		}
		switch tCompareOp {
		case "gt":
			expectedResultPattern = "%s is greater than %s"
			testResult = a > b

		case "gte":
			expectedResultPattern = "%s is greater or equal to %s"
			testResult = a >= b

		case "lt":
			expectedResultPattern = "%s is lower than %s"
			testResult = a < b

		case "lte":
			expectedResultPattern = "%s is lower or equal to %s"
			testResult = a <= b
		}

	case "has":
		expectedResultPattern = "'%s' has '%s'"
		testResult = strings.Contains(flagVal, tCompareValue)

	case "nothave":
		expectedResultPattern = " '%s' not have '%s'"
		testResult = !strings.Contains(flagVal, tCompareValue)

	case "regex":
		expectedResultPattern = " '%s' matched by '%s'"
		opRe := regexp.MustCompile(tCompareValue)
		testResult = opRe.MatchString(flagVal)

	case "valid_elements":
		expectedResultPattern = "'%s' contains valid elements from '%s'"
		s := splitAndRemoveLastSeparator(flagVal, defaultArraySeparator)
		target := splitAndRemoveLastSeparator(tCompareValue, defaultArraySeparator)
		testResult = allElementsValid(s, target)

	case "bitmask":
		expectedResultPattern = "bitmask '%s' AND '%s'"
		requested, err := strconv.ParseInt(flagVal, 8, 64)
		if err != nil {
			glog.V(1).Infof(fmt.Sprintf("Not numeric value - flag: %q - compareValue: %q %v\n", flagVal, tCompareValue, err))
			return fmt.Sprintf("Not numeric value - flag: %s", flagVal), false
		}
		max, err := strconv.ParseInt(tCompareValue, 8, 64)
		if err != nil {
			glog.V(1).Infof(fmt.Sprintf("Not numeric value - flag: %q - compareValue: %q %v\n", flagVal, tCompareValue, err))
			return fmt.Sprintf("Not numeric value - flag: %s", tCompareValue), false
		}
		testResult = (max & requested) == requested
	}
	if expectedResultPattern == "" {
		return expectedResultPattern, testResult
	}

	return fmt.Sprintf(expectedResultPattern, flagVal, tCompareValue), testResult
}

func unmarshal(s string, jsonInterface *interface{}) error {
	data := []byte(s)
	err := json.Unmarshal(data, jsonInterface)
	if err != nil {
		err := yaml.Unmarshal(data, jsonInterface)
		if err != nil {
			return err
		}
	}
	return nil
}

func executeJSONPath(path string, jsonInterface interface{}) (string, error) {
	j := jsonpath.New("jsonpath")
	j.AllowMissingKeys(true)
	err := j.Parse(path)
	if err != nil {
		return "", err
	}

	buf := new(bytes.Buffer)
	err = j.Execute(buf, jsonInterface)
	if err != nil {
		return "", err
	}
	jsonpathResult := buf.String()
	return jsonpathResult, nil
}

func allElementsValid(s, t []string) bool {
	sourceEmpty := len(s) == 0
	targetEmpty := len(t) == 0

	if sourceEmpty && targetEmpty {
		return true
	}

	// XOR comparison -
	//     if either value is empty and the other is not empty,
	//     not all elements are valid
	if (sourceEmpty || targetEmpty) && !(sourceEmpty && targetEmpty) {
		return false
	}

	for _, sv := range s {
		found := false
		for _, tv := range t {
			if sv == tv {
				found = true
				break
			}
		}
		if !found {
			return false
		}
	}
	return true
}

func splitAndRemoveLastSeparator(s, sep string) []string {
	cleanS := strings.TrimRight(strings.TrimSpace(s), sep)
	if len(cleanS) == 0 {
		return []string{}
	}

	ts := strings.Split(cleanS, sep)
	for i := range ts {
		ts[i] = strings.TrimSpace(ts[i])
	}

	return ts
}

type tests struct {
	TestItems []*testItem `yaml:"test_items"`
	BinOp     binOp       `yaml:"bin_op"`
}

func (ts *tests) execute(s string, isMultipleOutput bool) *testOutput {
	finalOutput := &testOutput{}

	// If no tests are defined return with empty finalOutput.
	// This may be the case for checks of type: "skip".
	if ts == nil {
		return finalOutput
	}

	res := make([]testOutput, len(ts.TestItems))
	if len(res) == 0 {
		return finalOutput
	}

	expectedResultArr := make([]string, len(res))

	for i, t := range ts.TestItems {
		res[i] = *(t.execute(s, isMultipleOutput))
		expectedResultArr[i] = res[i].ExpectedResult
	}

	var result bool
	// If no binary operation is specified, default to AND
	switch ts.BinOp {
	default:
		glog.V(2).Info(fmt.Sprintf("unknown binary operator for tests %s\n", ts.BinOp))
		finalOutput.actualResult = fmt.Sprintf("unknown binary operator for tests %s\n", ts.BinOp)
		return finalOutput
	case and, "":
		result = true
		for i := range res {
			result = result && res[i].testResult
		}
		// Generate an AND expected result
		finalOutput.ExpectedResult = strings.Join(expectedResultArr, " AND ")

	case or:
		result = false
		for i := range res {
			result = result || res[i].testResult
		}
		// Generate an OR expected result
		finalOutput.ExpectedResult = strings.Join(expectedResultArr, " OR ")
	}

	finalOutput.testResult = result
	finalOutput.actualResult = res[0].actualResult

	if finalOutput.actualResult == "" {
		finalOutput.actualResult = s
	}

	return finalOutput
}

func toNumeric(a, b string) (c, d int, err error) {
	c, err = strconv.Atoi(strings.TrimSpace(a))
	if err != nil {
		return -1, -1, fmt.Errorf("toNumeric - error converting %s: %s", a, err)
	}
	d, err = strconv.Atoi(strings.TrimSpace(b))
	if err != nil {
		return -1, -1, fmt.Errorf("toNumeric - error converting %s: %s", b, err)
	}

	return c, d, nil
}
<commit_msg>Refactor testitem-set (#668)<commit_after>// Copyright © 2017 Aqua Security Software Ltd. <info@aquasec.com>
//
// 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 check

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/golang/glog"
	yaml "gopkg.in/yaml.v2"
	"k8s.io/client-go/util/jsonpath"
)

// test:
// flag: OPTION
// set: (true|false)
// compare:
//   op: (eq|gt|gte|lt|lte|has)
//   value: val

type binOp string

const (
	and                   binOp = "and"
	or                          = "or"
	defaultArraySeparator       = ","
)

type testItem struct {
	Flag    string
	Path    string
	Output  string
	Value   string
	Set     bool
	Compare compare
}

type compare struct {
	Op    string
	Value string
}

type testOutput struct {
	testResult     bool
	actualResult   string
	ExpectedResult string
}

func failTestItem(s string) *testOutput {
	return &testOutput{testResult: false, actualResult: s}
}

func (t *testItem) execute(s string, isMultipleOutput bool) *testOutput {
	result := &testOutput{}
	s = strings.TrimRight(s, " \n")

	// If the test has output that should be evaluated for each row
	if isMultipleOutput {
		output := strings.Split(s, "\n")
		for _, op := range output {
			result = t.evaluate(op)
			// If the test failed for the current row, no need to keep testing for this output
			if !result.testResult {
				break
			}
		}
	} else {
		result = t.evaluate(s)
	}

	return result
}

func (t *testItem) evaluate(s string) *testOutput {
	result := &testOutput{}
	var match bool
	var flagVal string

	if t.Flag != "" {
		// Flag comparison: check if the flag is present in the input
		match = strings.Contains(s, t.Flag)
	} else {
		// Path != "" - we don't know whether it's YAML or JSON but
		// we can just try one then the other
		var jsonInterface interface{}

		if t.Path != "" {
			err := unmarshal(s, &jsonInterface)
			if err != nil {
				fmt.Fprintf(os.Stderr, "failed to load YAML or JSON from provided input \"%s\": %v\n", s, err)
				return failTestItem("failed to load YAML or JSON")
			}

		}

		jsonpathResult, err := executeJSONPath(t.Path, &jsonInterface)
		if err != nil {
			fmt.Fprintf(os.Stderr, "unable to parse path expression \"%s\": %v\n", t.Path, err)
			return failTestItem("error executing path expression")
		}
		match = (jsonpathResult != "")
		flagVal = jsonpathResult
	}

	if t.Set {
		isset := match

		if isset && t.Compare.Op != "" {
			if t.Flag != "" {
				// Expects flags in the form;
				// --flag=somevalue
				// flag: somevalue
				// --flag
				// somevalue
				pttn := `(` + t.Flag + `)(=|: *)*([^\s]*) *`
				flagRe := regexp.MustCompile(pttn)
				vals := flagRe.FindStringSubmatch(s)

				if len(vals) > 0 {
					if vals[3] != "" {
						flagVal = vals[3]
					} else {
						// --bool-flag
						if strings.HasPrefix(t.Flag, "--") {
							flagVal = "true"
						} else {
							flagVal = vals[1]
						}
					}
				} else {
					glog.V(1).Infof(fmt.Sprintf("invalid flag in testitem definition"))
					return failTestItem("error invalid flag in testitem definition")
				}
			}

			result.ExpectedResult, result.testResult = compareOp(t.Compare.Op, flagVal, t.Compare.Value)
		} else {
			result.ExpectedResult = fmt.Sprintf("'%s' is present", t.Flag)
			result.testResult = isset
		}
	} else {
		result.ExpectedResult = fmt.Sprintf("'%s' is not present", t.Flag)
		notset := !match
		result.testResult = notset
	}
	return result
}

func compareOp(tCompareOp string, flagVal string, tCompareValue string) (string, bool) {

	expectedResultPattern := ""
	testResult := false

	switch tCompareOp {
	case "eq":
		expectedResultPattern = "'%s' is equal to '%s'"
		value := strings.ToLower(flagVal)
		// Do case insensitive comparaison for booleans ...
		if value == "false" || value == "true" {
			testResult = value == tCompareValue
		} else {
			testResult = flagVal == tCompareValue
		}

	case "noteq":
		expectedResultPattern = "'%s' is not equal to '%s'"
		value := strings.ToLower(flagVal)
		// Do case insensitive comparaison for booleans ...
		if value == "false" || value == "true" {
			testResult = !(value == tCompareValue)
		} else {
			testResult = !(flagVal == tCompareValue)
		}

	case "gt", "gte", "lt", "lte":
		a, b, err := toNumeric(flagVal, tCompareValue)
		if err != nil {
			glog.V(1).Infof(fmt.Sprintf("Not numeric value - flag: %q - compareValue: %q %v\n", flagVal, tCompareValue, err))
			return "Invalid Number(s) used for comparison", false
		}
		switch tCompareOp {
		case "gt":
			expectedResultPattern = "%s is greater than %s"
			testResult = a > b

		case "gte":
			expectedResultPattern = "%s is greater or equal to %s"
			testResult = a >= b

		case "lt":
			expectedResultPattern = "%s is lower than %s"
			testResult = a < b

		case "lte":
			expectedResultPattern = "%s is lower or equal to %s"
			testResult = a <= b
		}

	case "has":
		expectedResultPattern = "'%s' has '%s'"
		testResult = strings.Contains(flagVal, tCompareValue)

	case "nothave":
		expectedResultPattern = " '%s' not have '%s'"
		testResult = !strings.Contains(flagVal, tCompareValue)

	case "regex":
		expectedResultPattern = " '%s' matched by '%s'"
		opRe := regexp.MustCompile(tCompareValue)
		testResult = opRe.MatchString(flagVal)

	case "valid_elements":
		expectedResultPattern = "'%s' contains valid elements from '%s'"
		s := splitAndRemoveLastSeparator(flagVal, defaultArraySeparator)
		target := splitAndRemoveLastSeparator(tCompareValue, defaultArraySeparator)
		testResult = allElementsValid(s, target)

	case "bitmask":
		expectedResultPattern = "bitmask '%s' AND '%s'"
		requested, err := strconv.ParseInt(flagVal, 8, 64)
		if err != nil {
			glog.V(1).Infof(fmt.Sprintf("Not numeric value - flag: %q - compareValue: %q %v\n", flagVal, tCompareValue, err))
			return fmt.Sprintf("Not numeric value - flag: %s", flagVal), false
		}
		max, err := strconv.ParseInt(tCompareValue, 8, 64)
		if err != nil {
			glog.V(1).Infof(fmt.Sprintf("Not numeric value - flag: %q - compareValue: %q %v\n", flagVal, tCompareValue, err))
			return fmt.Sprintf("Not numeric value - flag: %s", tCompareValue), false
		}
		testResult = (max & requested) == requested
	}
	if expectedResultPattern == "" {
		return expectedResultPattern, testResult
	}

	return fmt.Sprintf(expectedResultPattern, flagVal, tCompareValue), testResult
}

func unmarshal(s string, jsonInterface *interface{}) error {
	data := []byte(s)
	err := json.Unmarshal(data, jsonInterface)
	if err != nil {
		err := yaml.Unmarshal(data, jsonInterface)
		if err != nil {
			return err
		}
	}
	return nil
}

func executeJSONPath(path string, jsonInterface interface{}) (string, error) {
	j := jsonpath.New("jsonpath")
	j.AllowMissingKeys(true)
	err := j.Parse(path)
	if err != nil {
		return "", err
	}

	buf := new(bytes.Buffer)
	err = j.Execute(buf, jsonInterface)
	if err != nil {
		return "", err
	}
	jsonpathResult := buf.String()
	return jsonpathResult, nil
}

func allElementsValid(s, t []string) bool {
	sourceEmpty := len(s) == 0
	targetEmpty := len(t) == 0

	if sourceEmpty && targetEmpty {
		return true
	}

	// XOR comparison -
	//     if either value is empty and the other is not empty,
	//     not all elements are valid
	if (sourceEmpty || targetEmpty) && !(sourceEmpty && targetEmpty) {
		return false
	}

	for _, sv := range s {
		found := false
		for _, tv := range t {
			if sv == tv {
				found = true
				break
			}
		}
		if !found {
			return false
		}
	}
	return true
}

func splitAndRemoveLastSeparator(s, sep string) []string {
	cleanS := strings.TrimRight(strings.TrimSpace(s), sep)
	if len(cleanS) == 0 {
		return []string{}
	}

	ts := strings.Split(cleanS, sep)
	for i := range ts {
		ts[i] = strings.TrimSpace(ts[i])
	}

	return ts
}

type tests struct {
	TestItems []*testItem `yaml:"test_items"`
	BinOp     binOp       `yaml:"bin_op"`
}

func (ts *tests) execute(s string, isMultipleOutput bool) *testOutput {
	finalOutput := &testOutput{}

	// If no tests are defined return with empty finalOutput.
	// This may be the case for checks of type: "skip".
	if ts == nil {
		return finalOutput
	}

	res := make([]testOutput, len(ts.TestItems))
	if len(res) == 0 {
		return finalOutput
	}

	expectedResultArr := make([]string, len(res))

	for i, t := range ts.TestItems {
		res[i] = *(t.execute(s, isMultipleOutput))
		expectedResultArr[i] = res[i].ExpectedResult
	}

	var result bool
	// If no binary operation is specified, default to AND
	switch ts.BinOp {
	default:
		glog.V(2).Info(fmt.Sprintf("unknown binary operator for tests %s\n", ts.BinOp))
		finalOutput.actualResult = fmt.Sprintf("unknown binary operator for tests %s\n", ts.BinOp)
		return finalOutput
	case and, "":
		result = true
		for i := range res {
			result = result && res[i].testResult
		}
		// Generate an AND expected result
		finalOutput.ExpectedResult = strings.Join(expectedResultArr, " AND ")

	case or:
		result = false
		for i := range res {
			result = result || res[i].testResult
		}
		// Generate an OR expected result
		finalOutput.ExpectedResult = strings.Join(expectedResultArr, " OR ")
	}

	finalOutput.testResult = result
	finalOutput.actualResult = res[0].actualResult

	if finalOutput.actualResult == "" {
		finalOutput.actualResult = s
	}

	return finalOutput
}

func toNumeric(a, b string) (c, d int, err error) {
	c, err = strconv.Atoi(strings.TrimSpace(a))
	if err != nil {
		return -1, -1, fmt.Errorf("toNumeric - error converting %s: %s", a, err)
	}
	d, err = strconv.Atoi(strings.TrimSpace(b))
	if err != nil {
		return -1, -1, fmt.Errorf("toNumeric - error converting %s: %s", b, err)
	}

	return c, d, nil
}

func (t *testItem) UnmarshalYAML(unmarshal func(interface{}) error) error {
	type buildTest testItem

	// Make Set parameter to be true by default.
	newTestItem := buildTest{Set: true}
	err := unmarshal(&newTestItem)
	if err != nil {
		return err
	}
	*t = testItem(newTestItem)
	return nil
}
<|endoftext|>