<commit_before>package fields

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestRadiobuttonsUnmarshalValue(t *testing.T) {
	form := RadiobuttonsForm{}
	data := `"foo"`
	_, err := form.UnmarshalValue([]byte(data))
	assert.NoError(t, err)

	data = `[]`
	_, err = form.UnmarshalValue([]byte(data))
	assert.Error(t, err)

	data = ``
	_, err = form.UnmarshalValue([]byte(data))
	assert.NoError(t, err)

	data = `{}`
	_, err = form.UnmarshalValue([]byte(data))
	assert.Error(t, err)

	data = `{"value": "foo"}`
	_, err = form.UnmarshalValue([]byte(data))
	assert.Error(t, err)
}

func TestRadiobuttonsValidate(t *testing.T) {
	form := &RadiobuttonsForm{}
	assert.Error(t, form.Validate())

	form = &RadiobuttonsForm{"foo", "bar", "foo"}
	assert.Error(t, form.Validate())

	form = nil
	assert.Error(t, form.Validate())

	form = &RadiobuttonsForm{"foo", "bar"}
	assert.NoError(t, form.Validate())

	form = &RadiobuttonsForm{"foo"}
	assert.NoError(t, form.Validate())
}

func TestRadiobuttonsValidateValue(t *testing.T) {
	form := RadiobuttonsForm{"foo", "bar"}
	var value RadiobuttonsValue
	ptr := &value

	value = "foo"
	assert.NoError(t, form.ValidateValue(ptr))

	value = "bar"
	assert.NoError(t, form.ValidateValue(ptr))

	value = "baz"
	assert.Error(t, form.ValidateValue(ptr))

	ptr = nil
	assert.NoError(t, form.ValidateValue(ptr))

	assert.Error(t, form.ValidateValue(nil))
}

// func TestRadiobuttonsIsComplete(t *testing.T) {
// 	value := &RadiobuttonsValue{}
// 	assert.True(t, value.IsComplete())
// 	value = &RadiobuttonsValue{true, false}
// 	assert.True(t, value.IsComplete())
// 	value = nil
// 	assert.False(t, value.IsComplete())
// }
<commit_msg>Add test for Radiobutton IsComplete function<commit_after>package fields

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestRadiobuttonsUnmarshalValue(t *testing.T) {
	form := RadiobuttonsForm{}
	data := `"foo"`
	_, err := form.UnmarshalValue([]byte(data))
	assert.NoError(t, err)

	data = `[]`
	_, err = form.UnmarshalValue([]byte(data))
	assert.Error(t, err)

	data = ``
	_, err = form.UnmarshalValue([]byte(data))
	assert.NoError(t, err)

	data = `{}`
	_, err = form.UnmarshalValue([]byte(data))
	assert.Error(t, err)

	data = `{"value": "foo"}`
	_, err = form.UnmarshalValue([]byte(data))
	assert.Error(t, err)
}

func TestRadiobuttonsValidate(t *testing.T) {
	form := &RadiobuttonsForm{}
	assert.Error(t, form.Validate())

	form = &RadiobuttonsForm{"foo", "bar", "foo"}
	assert.Error(t, form.Validate())

	form = nil
	assert.Error(t, form.Validate())

	form = &RadiobuttonsForm{"foo", "bar"}
	assert.NoError(t, form.Validate())

	form = &RadiobuttonsForm{"foo"}
	assert.NoError(t, form.Validate())
}

func TestRadiobuttonsValidateValue(t *testing.T) {
	form := RadiobuttonsForm{"foo", "bar"}
	var value RadiobuttonsValue
	ptr := &value

	value = "foo"
	assert.NoError(t, form.ValidateValue(ptr))

	value = "bar"
	assert.NoError(t, form.ValidateValue(ptr))

	value = "baz"
	assert.Error(t, form.ValidateValue(ptr))

	ptr = nil
	assert.NoError(t, form.ValidateValue(ptr))

	assert.Error(t, form.ValidateValue(nil))
}

func TestRadiobuttonsIsComplete(t *testing.T) {
	var value RadiobuttonsValue = ""
	ptr := &value
	assert.True(t, ptr.IsComplete())
	ptr = nil
	assert.False(t, ptr.IsComplete())
}
<|endoftext|>


<commit_before>package instancecommands

import (
	"github.com/codegangsta/cli"
	"github.com/jrperritt/rack/handler"
	"github.com/jrperritt/rack/util"
	osServers "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
	"github.com/rackspace/gophercloud/pagination"
	"github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
)

var list = cli.Command{
	Name:        "list",
	Usage:       util.Usage(commandPrefix, "list", ""),
	Description: "Lists existing servers",
	Action:      actionList,
	Flags:       util.CommandFlags(flagsList, keysList),
	BashComplete: func(c *cli.Context) {
		util.CompleteFlags(util.CommandFlags(flagsList, keysList))
	},
}

func flagsList() []cli.Flag {
	return []cli.Flag{
		cli.BoolFlag{
			Name:  "all-pages",
			Usage: "[optional] Return all servers. Default is to paginate.",
		},
		cli.StringFlag{
			Name:  "name",
			Usage: "Only list servers with this name.",
		},
		cli.StringFlag{
			Name:  "changes-since",
			Usage: "Only list servers that have been changed since this time/date stamp.",
		},
		cli.StringFlag{
			Name:  "image",
			Usage: "Only list servers that have this image ID.",
		},
		cli.StringFlag{
			Name:  "flavor",
			Usage: "Only list servers that have this flavor ID.",
		},
		cli.StringFlag{
			Name:  "status",
			Usage: "Only list servers that have this status.",
		},
		cli.StringFlag{
			Name:  "marker",
			Usage: "Start listing servers at this server ID.",
		},
		cli.IntFlag{
			Name:  "limit",
			Usage: "[optional] Only return this many servers at most.",
		},
	}
}

var keysList = []string{"ID", "Name", "Status", "Public IPv4", "Private IPv4", "Image", "Flavor"}

type paramsList struct {
	opts     *osServers.ListOpts
	allPages bool
}

type commandList handler.Command

func actionList(c *cli.Context) {
	command := &commandList{
		Ctx: &handler.Context{
			CLIContext: c,
		},
	}
	handler.Handle(command)
}

func (command *commandList) Context() *handler.Context {
	return command.Ctx
}

func (command *commandList) Keys() []string {
	return keysList
}

func (command *commandList) ServiceClientType() string {
	return serviceClientType
}

func (command *commandList) HandleFlags(resource *handler.Resource) error {
	c := command.Ctx.CLIContext
	opts := &osServers.ListOpts{
		ChangesSince: c.String("changes-since"),
		Image:        c.String("image"),
		Flavor:       c.String("flavor"),
		Name:         c.String("name"),
		Status:       c.String("status"),
		Marker:       c.String("marker"),
		Limit:        c.Int("limit"),
	}
	resource.Params = &paramsList{
		opts:     opts,
		allPages: c.Bool("all-pages"),
	}
	return nil
}

func (command *commandList) HandleSingle(resource *handler.Resource) error {
	return nil
}

func (command *commandList) Execute(resource *handler.Resource) {
	opts := resource.Params.(*paramsList).opts
	allPages := resource.Params.(*paramsList).allPages
	pager := servers.List(command.Ctx.ServiceClient, opts)
	if allPages {
		pages, err := pager.AllPages()
		if err != nil {
			resource.Err = err
			return
		}
		info, err := servers.ExtractServers(pages)
		if err != nil {
			resource.Err = err
			return
		}
		result := make([]map[string]interface{}, len(info))
		for j, server := range info {
			result[j] = serverSingle(&server)
		}
		resource.Result = result
	} else {
		limit := opts.Limit
		err := pager.EachPage(func(page pagination.Page) (bool, error) {
			info, err := servers.ExtractServers(page)
			if err != nil {
				return false, err
			}
			result := make([]map[string]interface{}, len(info))
			for j, server := range info {
				result[j] = serverSingle(&server)
			}
			resource.Result = result
			if len(info) >= limit {
				limit -= len(info)
				return false, nil
			}
			command.Ctx.WaitGroup.Add(1)
			command.Ctx.Results <- resource
			return true, nil
		})
		if err != nil {
			resource.Err = err
			return
		}
	}
}
<commit_msg>marke flag usage msgs consistent for 'instance list'<commit_after>package instancecommands

import (
	"github.com/codegangsta/cli"
	"github.com/jrperritt/rack/handler"
	"github.com/jrperritt/rack/util"
	osServers "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
	"github.com/rackspace/gophercloud/pagination"
	"github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
)

var list = cli.Command{
	Name:        "list",
	Usage:       util.Usage(commandPrefix, "list", ""),
	Description: "Lists existing servers",
	Action:      actionList,
	Flags:       util.CommandFlags(flagsList, keysList),
	BashComplete: func(c *cli.Context) {
		util.CompleteFlags(util.CommandFlags(flagsList, keysList))
	},
}

func flagsList() []cli.Flag {
	return []cli.Flag{
		cli.BoolFlag{
			Name:  "all-pages",
			Usage: "[optional] Return all servers. Default is to paginate.",
		},
		cli.StringFlag{
			Name:  "name",
			Usage: "[optional] Only list servers with this name.",
		},
		cli.StringFlag{
			Name:  "changes-since",
			Usage: "[optional] Only list servers that have been changed since this time/date stamp.",
		},
		cli.StringFlag{
			Name:  "image",
			Usage: "[optional] Only list servers that have this image ID.",
		},
		cli.StringFlag{
			Name:  "flavor",
			Usage: "[optional] Only list servers that have this flavor ID.",
		},
		cli.StringFlag{
			Name:  "status",
			Usage: "[optional] Only list servers that have this status.",
		},
		cli.StringFlag{
			Name:  "marker",
			Usage: "[optional] Start listing servers at this server ID.",
		},
		cli.IntFlag{
			Name:  "limit",
			Usage: "[optional] Only return this many servers at most.",
		},
	}
}

var keysList = []string{"ID", "Name", "Status", "Public IPv4", "Private IPv4", "Image", "Flavor"}

type paramsList struct {
	opts     *osServers.ListOpts
	allPages bool
}

type commandList handler.Command

func actionList(c *cli.Context) {
	command := &commandList{
		Ctx: &handler.Context{
			CLIContext: c,
		},
	}
	handler.Handle(command)
}

func (command *commandList) Context() *handler.Context {
	return command.Ctx
}

func (command *commandList) Keys() []string {
	return keysList
}

func (command *commandList) ServiceClientType() string {
	return serviceClientType
}

func (command *commandList) HandleFlags(resource *handler.Resource) error {
	c := command.Ctx.CLIContext
	opts := &osServers.ListOpts{
		ChangesSince: c.String("changes-since"),
		Image:        c.String("image"),
		Flavor:       c.String("flavor"),
		Name:         c.String("name"),
		Status:       c.String("status"),
		Marker:       c.String("marker"),
		Limit:        c.Int("limit"),
	}
	resource.Params = &paramsList{
		opts:     opts,
		allPages: c.Bool("all-pages"),
	}
	return nil
}

func (command *commandList) HandleSingle(resource *handler.Resource) error {
	return nil
}

func (command *commandList) Execute(resource *handler.Resource) {
	opts := resource.Params.(*paramsList).opts
	allPages := resource.Params.(*paramsList).allPages
	pager := servers.List(command.Ctx.ServiceClient, opts)
	if allPages {
		pages, err := pager.AllPages()
		if err != nil {
			resource.Err = err
			return
		}
		info, err := servers.ExtractServers(pages)
		if err != nil {
			resource.Err = err
			return
		}
		result := make([]map[string]interface{}, len(info))
		for j, server := range info {
			result[j] = serverSingle(&server)
		}
		resource.Result = result
	} else {
		limit := opts.Limit
		err := pager.EachPage(func(page pagination.Page) (bool, error) {
			info, err := servers.ExtractServers(page)
			if err != nil {
				return false, err
			}
			result := make([]map[string]interface{}, len(info))
			for j, server := range info {
				result[j] = serverSingle(&server)
			}
			resource.Result = result
			if len(info) >= limit {
				limit -= len(info)
				return false, nil
			}
			command.Ctx.WaitGroup.Add(1)
			command.Ctx.Results <- resource
			return true, nil
		})
		if err != nil {
			resource.Err = err
			return
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"path/filepath"
	"github.com/golang/glog"
	"github.com/immutablet/k8s-kms-plugin/plugin"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"net/http"
	"os"
	k8spb "github.com/immutablet/k8s-kms-plugin/v1beta1"
	"golang.org/x/net/context"
	"time"
)

var (
	metricsPort = flag.String("metrics-addr", ":8081", "Address at which to publish metrics")
	metricsPath = flag.String("metrics-path", "/metrics", "Path at which to publish metrics")

	healthzPort = flag.String("healthz-addr", ":8082", "Address at which to publish healthz")
	healthzPath = flag.String("healthz-path", "/healthz", "Path at which to publish healthz")

	projectID  = flag.String("project-id", "", "Cloud project where KMS key-ring is hosted")
	locationID = flag.String("location-id", "global", "Location of the key-ring")
	keyRingID  = flag.String("key-ring-id", "", "ID of the key-ring where keys are stored")
	keyID      = flag.String("key-id", "", "Id of the key use for crypto operations")

	pathToUnixSocket = flag.String("path-to-unix-socket", "/tmp/kms-plugin.socket", "Full path to Unix socket that is used for communicating with KubeAPI Server")
)

func main() {
	flag.Parse()

	glog.Infof("Starting cloud KMS gRPC Plugin.")

	socketDir := filepath.Dir(*pathToUnixSocket)
	_, err := os.Stat(socketDir)
	glog.Infof("Unix Socket directory is %s", socketDir)
	if err != nil &&  os.IsNotExist(err) {
		glog.Fatalf(" Directory %s portion of path-to-unix-socket flag:%s does not exist.", socketDir, *pathToUnixSocket)
	}
	glog.Infof("Communicating with KUBE API via %s", *pathToUnixSocket)

	go func() {
		http.Handle(*metricsPath, promhttp.Handler())
		glog.Fatal(http.ListenAndServe(*metricsPort, nil))
	}()

	kmsPlugin, err := plugin.New(*projectID, *locationID, *keyRingID, *keyID, *pathToUnixSocket)
	if err != nil {
		glog.Fatalf("failed to instantiate kmsPlugin, %v", err)
	}
	mustPingKMS(kmsPlugin)

	err = kmsPlugin.SetupRPCServer()
	if err != nil {
		glog.Fatalf("failed to setup gRPC Server, %v", err)
	}

	glog.Infof("Pinging KMS gRPC in 10ms.")
	go func () {
		time.Sleep(10 * time.Millisecond)
		mustPingRPC(kmsPlugin)

		// Now we can declare healthz OK.
		http.HandleFunc(*healthzPath, handleHealthz)
		glog.Fatal(http.ListenAndServe(*healthzPort, nil))
	}()

	glog.Infof("About to server gRPC")

	err = kmsPlugin.Serve(kmsPlugin.Listener)
	if err != nil {
		glog.Fatalf("failed to serve gRPC, %v", err)
	}
}

func mustPingKMS(kms *plugin.Plugin) {
	plainText := []byte("secret")

	glog.Infof("Pinging KMS.")

	encryptRequest := k8spb.EncryptRequest{Version: plugin.APIVersion, Plain: []byte(plainText)}
	encryptResponse, err := kms.Encrypt(context.Background(), &encryptRequest)

	if err != nil {
		glog.Fatalf("failed to ping KMS: %v", err)
	}

	decryptRequest := k8spb.DecryptRequest{Version: plugin.APIVersion, Cipher: []byte(encryptResponse.Cipher)}
	decryptResponse, err := kms.Decrypt(context.Background(), &decryptRequest)
	if err != nil {
		glog.Fatalf("failed to ping KMS: %v", err)
	}

	if string(decryptResponse.Plain) != string(plainText) {
		glog.Fatalf("failed to ping kms, expected secret, but got %s", string(decryptResponse.Plain))
	}

	glog.Infof("Successfully pinged KMS.")
}

func mustPingRPC(kms *plugin.Plugin) {
	glog.Infof("Pinging KMS gRPC.")

	connection, err := kms.NewUnixSocketConnection()
	if err != nil {
		glog.Fatalf("failed to open unix socket, %v", err)
	}
	client := k8spb.NewKMSServiceClient(connection)

	plainText := []byte("secret")

	encryptRequest := k8spb.EncryptRequest{Version: plugin.APIVersion, Plain: []byte(plainText)}
	encryptResponse, err := client.Encrypt(context.Background(), &encryptRequest)

	if err != nil {
		glog.Fatalf("failed to ping KMS: %v", err)
	}

	decryptRequest := k8spb.DecryptRequest{Version: plugin.APIVersion, Cipher: []byte(encryptResponse.Cipher)}
	decryptResponse, err := client.Decrypt(context.Background(), &decryptRequest)
	if err != nil {
		glog.Fatalf("failed to ping KMS gRPC: %v", err)
	}

	if string(decryptResponse.Plain) != string(plainText) {
		glog.Fatalf("failed to ping KMS gRPC, expected secret, but got %s", string(decryptResponse.Plain))
	}

	glog.Infof("Successfully pinged gRPC KMS.")
}

func handleHealthz(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(200)
	w.Write([]byte("ok"))
}
<commit_msg>Added license header to main.<commit_after>/*
Copyright 2018 Google LLC

 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

 https://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

package main

import (
	"flag"
	"path/filepath"
	"github.com/golang/glog"
	"github.com/immutablet/k8s-kms-plugin/plugin"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"net/http"
	"os"
	k8spb "github.com/immutablet/k8s-kms-plugin/v1beta1"
	"golang.org/x/net/context"
	"time"
)

var (
	metricsPort = flag.String("metrics-addr", ":8081", "Address at which to publish metrics")
	metricsPath = flag.String("metrics-path", "/metrics", "Path at which to publish metrics")

	healthzPort = flag.String("healthz-addr", ":8082", "Address at which to publish healthz")
	healthzPath = flag.String("healthz-path", "/healthz", "Path at which to publish healthz")

	projectID  = flag.String("project-id", "", "Cloud project where KMS key-ring is hosted")
	locationID = flag.String("location-id", "global", "Location of the key-ring")
	keyRingID  = flag.String("key-ring-id", "", "ID of the key-ring where keys are stored")
	keyID      = flag.String("key-id", "", "Id of the key use for crypto operations")

	pathToUnixSocket = flag.String("path-to-unix-socket", "/tmp/kms-plugin.socket", "Full path to Unix socket that is used for communicating with KubeAPI Server")
)

func main() {
	flag.Parse()

	glog.Infof("Starting cloud KMS gRPC Plugin.")

	socketDir := filepath.Dir(*pathToUnixSocket)
	_, err := os.Stat(socketDir)
	glog.Infof("Unix Socket directory is %s", socketDir)
	if err != nil &&  os.IsNotExist(err) {
		glog.Fatalf(" Directory %s portion of path-to-unix-socket flag:%s does not exist.", socketDir, *pathToUnixSocket)
	}
	glog.Infof("Communicating with KUBE API via %s", *pathToUnixSocket)

	go func() {
		http.Handle(*metricsPath, promhttp.Handler())
		glog.Fatal(http.ListenAndServe(*metricsPort, nil))
	}()

	kmsPlugin, err := plugin.New(*projectID, *locationID, *keyRingID, *keyID, *pathToUnixSocket)
	if err != nil {
		glog.Fatalf("failed to instantiate kmsPlugin, %v", err)
	}
	mustPingKMS(kmsPlugin)

	err = kmsPlugin.SetupRPCServer()
	if err != nil {
		glog.Fatalf("failed to setup gRPC Server, %v", err)
	}

	glog.Infof("Pinging KMS gRPC in 10ms.")
	go func () {
		time.Sleep(10 * time.Millisecond)
		mustPingRPC(kmsPlugin)

		// Now we can declare healthz OK.
		http.HandleFunc(*healthzPath, handleHealthz)
		glog.Fatal(http.ListenAndServe(*healthzPort, nil))
	}()

	glog.Infof("About to server gRPC")

	err = kmsPlugin.Serve(kmsPlugin.Listener)
	if err != nil {
		glog.Fatalf("failed to serve gRPC, %v", err)
	}
}

func mustPingKMS(kms *plugin.Plugin) {
	plainText := []byte("secret")

	glog.Infof("Pinging KMS.")

	encryptRequest := k8spb.EncryptRequest{Version: plugin.APIVersion, Plain: []byte(plainText)}
	encryptResponse, err := kms.Encrypt(context.Background(), &encryptRequest)

	if err != nil {
		glog.Fatalf("failed to ping KMS: %v", err)
	}

	decryptRequest := k8spb.DecryptRequest{Version: plugin.APIVersion, Cipher: []byte(encryptResponse.Cipher)}
	decryptResponse, err := kms.Decrypt(context.Background(), &decryptRequest)
	if err != nil {
		glog.Fatalf("failed to ping KMS: %v", err)
	}

	if string(decryptResponse.Plain) != string(plainText) {
		glog.Fatalf("failed to ping kms, expected secret, but got %s", string(decryptResponse.Plain))
	}

	glog.Infof("Successfully pinged KMS.")
}

func mustPingRPC(kms *plugin.Plugin) {
	glog.Infof("Pinging KMS gRPC.")

	connection, err := kms.NewUnixSocketConnection()
	if err != nil {
		glog.Fatalf("failed to open unix socket, %v", err)
	}
	client := k8spb.NewKMSServiceClient(connection)

	plainText := []byte("secret")

	encryptRequest := k8spb.EncryptRequest{Version: plugin.APIVersion, Plain: []byte(plainText)}
	encryptResponse, err := client.Encrypt(context.Background(), &encryptRequest)

	if err != nil {
		glog.Fatalf("failed to ping KMS: %v", err)
	}

	decryptRequest := k8spb.DecryptRequest{Version: plugin.APIVersion, Cipher: []byte(encryptResponse.Cipher)}
	decryptResponse, err := client.Decrypt(context.Background(), &decryptRequest)
	if err != nil {
		glog.Fatalf("failed to ping KMS gRPC: %v", err)
	}

	if string(decryptResponse.Plain) != string(plainText) {
		glog.Fatalf("failed to ping KMS gRPC, expected secret, but got %s", string(decryptResponse.Plain))
	}

	glog.Infof("Successfully pinged gRPC KMS.")
}

func handleHealthz(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(200)
	w.Write([]byte("ok"))
}
<|endoftext|>


<commit_before>package blockexplorer

import (
	"errors"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
	"github.com/boltdb/bolt"
)

// addHashType adds an entry in the Hashes bucket for identifing that hash
func addHashType(tx *bolt.Tx, hash crypto.Hash, hashType int) error {
	b := tx.Bucket([]byte("Hashes"))
	if b == nil {
		return errors.New("bucket Hashes does not exist")
	}

	return b.Put(encoding.Marshal(hash), encoding.Marshal(hashType))
}

// addAddress either creates a new list of transactions for the given
// address, or adds the txid to the list if such a list already exists
func addAddress(tx *bolt.Tx, addr types.UnlockHash, txid crypto.Hash) error {
	err := addHashType(tx, crypto.Hash(addr), hashUnlockHash)
	if err != nil {
		return err
	}

	b := tx.Bucket([]byte("Addresses"))
	if b == nil {
		return errors.New("Addresses bucket does not exist")
	}

	txBytes := b.Get(encoding.Marshal(addr))
	if txBytes == nil {
		err := b.Put(encoding.Marshal(addr), encoding.Marshal([]crypto.Hash{txid}))
		if err != nil {
			return err
		}
		return nil
	}

	var txns []crypto.Hash
	err = encoding.Unmarshal(txBytes, &txns)
	if err != nil {
		return err
	}

	txns = append(txns, txid)

	return b.Put(encoding.Marshal(addr), encoding.Marshal(txns))
}

// addSiacoinInput changes an existing outputTransactions struct to
// point to the place where that output was used
func addSiacoinInput(tx *bolt.Tx, outputID types.SiacoinOutputID, txid crypto.Hash) error {
	b := tx.Bucket([]byte("SiacoinOutputs"))
	if b == nil {
		return errors.New("bucket SiacoinOutputs does not exist")
	}

	outputBytes := b.Get(encoding.Marshal(outputID))
	if outputBytes == nil {
		return errors.New("output for id does not exist")
	}

	var ot outputTransactions
	err := encoding.Unmarshal(outputBytes, &ot)
	if err != nil {
		return err
	}

	ot.InputTx = txid

	return b.Put(encoding.Marshal(outputID), encoding.Marshal(ot))
}

// addSiafundInpt does the same thing as addSiacoinInput except with siafunds
func addSiafundInput(tx *bolt.Tx, outputID types.SiafundOutputID, txid crypto.Hash) error {
	b := tx.Bucket([]byte("SiafundOutputs"))
	if b == nil {
		return errors.New("bucket SaifundOutputs does not exist")
	}

	outputBytes := b.Get(encoding.Marshal(outputID))
	if outputBytes == nil {
		return errors.New("output for id does not exist")
	}

	var ot outputTransactions
	err := encoding.Unmarshal(outputBytes, &ot)
	if err != nil {
		return err
	}

	ot.InputTx = txid

	return b.Put(encoding.Marshal(outputID), encoding.Marshal(ot))
}

// addFcRevision changes an existing fcInfo struct to contain the txid
// of the contract revision
func addFcRevision(tx *bolt.Tx, fcid types.FileContractID, txid crypto.Hash) error {
	b := tx.Bucket([]byte("FileContracts"))
	if b == nil {
		return errors.New("bucket FileContracts does not exist")
	}

	fiBytes := b.Get(encoding.Marshal(fcid))
	if fiBytes == nil {
		return errors.New("filecontract does not exist in database")
	}

	var fi fcInfo
	err := encoding.Unmarshal(fiBytes, &fi)
	if err != nil {
		return err
	}

	fi.Revisions = append(fi.Revisions, txid)

	return b.Put(encoding.Marshal(fcid), encoding.Marshal(fi))
}

// addFcProof changes an existing fcInfo struct in the database to
// contain the txid of its storage proof
func addFcProof(tx *bolt.Tx, fcid types.FileContractID, txid crypto.Hash) error {
	b := tx.Bucket([]byte("FileContracts"))
	if b == nil {
		return errors.New("bucket FileContracts does not exist")
	}

	fiBytes := b.Get(encoding.Marshal(fcid))
	if fiBytes == nil {
		return errors.New("filecontract does not exist in database")
	}

	var fi fcInfo
	err := encoding.Unmarshal(fiBytes, &fi)
	if err != nil {
		return err
	}

	fi.Proof = txid

	return b.Put(encoding.Marshal(fcid), encoding.Marshal(fi))
}

func addNewHash(tx *bolt.Tx, bucketName string, t int, hash crypto.Hash, value interface{}) error {
	err := addHashType(tx, hash, t)
	if err != nil {
		return err
	}

	b := tx.Bucket([]byte(bucketName))
	if b == nil {
		return errors.New("bucket does not exist: " + bucketName)
	}
	return b.Put(encoding.Marshal(hash), encoding.Marshal(value))
}

// addNewOutput creats a new outputTransactions struct and adds it to the database
func addNewOutput(tx *bolt.Tx, outputID types.SiacoinOutputID, txid crypto.Hash) error {
	otx := outputTransactions{txid, crypto.Hash{}}
	return addNewHash(tx, "SiacoinOutputs", hashCoinOutputID, crypto.Hash(outputID), otx)
}

// addNewSFOutput does the same thing as addNewOutput does, except for siafunds
func addNewSFOutput(tx *bolt.Tx, outputID types.SiafundOutputID, txid crypto.Hash) error {
	otx := outputTransactions{txid, crypto.Hash{}}
	return addNewHash(tx, "SiafundOutputs", hashFundOutputID, crypto.Hash(outputID), otx)
}

// addHeight adds a block summary (modules.ExplorerBlockData) to the
// database with a height as the key
func addHeight(tx *bolt.Tx, height types.BlockHeight, bs modules.ExplorerBlockData) error {
	b := tx.Bucket([]byte("Heights"))
	if b == nil {
		return errors.New("bucket Blocks does not exist")
	}

	return b.Put(encoding.Marshal(height), encoding.Marshal(bs))
}

// addBlockDB parses a block and adds it to the database
func (be *BlockExplorer) addBlockDB(b types.Block) error {
	// Special case for the genesis block, which does not have a
	// valid parent, and for testing, as tests will not always use
	// blocks in consensus
	var blocktarget types.Target
	if b.ID() == be.genesisBlockID {
		blocktarget = types.RootDepth
	} else {
		var exists bool
		blocktarget, exists = be.cs.ChildTarget(b.ParentID)
		if build.DEBUG {
			if build.Release == "testing" {
				blocktarget = types.RootDepth
			}
			if !exists {
				panic("Applied block not in consensus")
			}

		}
	}

	tx, err := be.db.Begin(true)
	if err != nil {
		return err
	}
	defer tx.Rollback()

	// Construct the struct that will be inside the database
	blockStruct := blockData{
		Block:  b,
		Height: be.blockchainHeight,
	}

	err = addNewHash(tx, "Blocks", hashBlock, crypto.Hash(b.ID()), blockStruct)
	if err != nil {
		return err
	}

	bSum := modules.ExplorerBlockData{
		ID:        b.ID(),
		Timestamp: b.Timestamp,
		Target:    blocktarget,
		Size:      uint64(len(encoding.Marshal(b))),
	}

	err = addHeight(tx, be.blockchainHeight, bSum)
	if err != nil {
		return err
	}
	err = addHashType(tx, crypto.Hash(b.ID()), hashBlock)
	if err != nil {
		return err
	}

	// Insert the miner payouts as new outputs
	for i, payout := range b.MinerPayouts {
		err = addAddress(tx, payout.UnlockHash, crypto.Hash(b.ID()))
		if err != nil {
			return err
		}
		err = addNewOutput(tx, b.MinerPayoutID(i), crypto.Hash(b.ID()))
		if err != nil {
			return err
		}
	}

	// Insert each transaction
	for i, txn := range b.Transactions {
		err = addNewHash(tx, "Transactions", hashTransaction, txn.ID(), txInfo{b.ID(), i})
		if err != nil {
			return err
		}
		err = be.addTransaction(tx, txn)
		if err != nil {
			return err
		}
	}

	return tx.Commit()
}

// addTransaction is called from addBlockDB, and delegates the adding
// of information to the database to the functions defined above
func (be *BlockExplorer) addTransaction(btx *bolt.Tx, tx types.Transaction) error {
	// Store this for quick lookup
	txid := tx.ID()

	// Append each input to the list of modifications
	for _, input := range tx.SiacoinInputs {
		err := addSiacoinInput(btx, input.ParentID, txid)
		if err != nil {
			return err
		}
	}

	// Handle all the transaction outputs
	for i, output := range tx.SiacoinOutputs {
		err := addAddress(btx, output.UnlockHash, txid)
		if err != nil {
			return err
		}
		err = addNewOutput(btx, tx.SiacoinOutputID(i), txid)
		if err != nil {
			return err
		}
	}

	// Handle each file contract individually
	for i, contract := range tx.FileContracts {
		fcid := tx.FileContractID(i)
		err := addNewHash(btx, "FileContracts", hashFilecontract, crypto.Hash(fcid), fcInfo{
			Contract: txid,
		})
		if err != nil {
			return err
		}

		for j, output := range contract.ValidProofOutputs {
			err = addAddress(btx, output.UnlockHash, txid)
			if err != nil {
				return err
			}
			err = addNewOutput(btx, fcid.StorageProofOutputID(true, j), txid)
			if err != nil {
				return err
			}
		}
		for j, output := range contract.MissedProofOutputs {
			err = addAddress(btx, output.UnlockHash, txid)
			if err != nil {
				return err
			}
			err = addNewOutput(btx, fcid.StorageProofOutputID(false, j), txid)
			if err != nil {
				return err
			}
		}

		err = addAddress(btx, contract.UnlockHash, txid)
		if err != nil {
			return err
		}
	}

	// Update the list of revisions
	for _, revision := range tx.FileContractRevisions {
		err := addFcRevision(btx, revision.ParentID, txid)
		if err != nil {
			return err
		}

		// Note the old outputs will still be there in the
		// database. This is to provide information to the
		// people who may just need it.
		for i, output := range revision.NewValidProofOutputs {
			err = addAddress(btx, output.UnlockHash, txid)
			if err != nil {
				return err
			}
			err = addNewOutput(btx, revision.ParentID.StorageProofOutputID(true, i), txid)
			if err != nil {
				return err
			}
		}
		for i, output := range revision.NewMissedProofOutputs {
			err = addAddress(btx, output.UnlockHash, txid)
			if err != nil {
				return err
			}
			err = addNewOutput(btx, revision.ParentID.StorageProofOutputID(false, i), txid)
			if err != nil {
				return err
			}
		}

		addAddress(btx, revision.NewUnlockHash, txid)
	}

	// Update the list of storage proofs
	for _, proof := range tx.StorageProofs {
		err := addFcProof(btx, proof.ParentID, txid)
		if err != nil {
			return err
		}
	}

	// Append all the siafund inputs to the modification list
	for _, input := range tx.SiafundInputs {
		err := addSiafundInput(btx, input.ParentID, txid)
		if err != nil {
			return err
		}
	}

	// Handle all the siafund outputs
	for i, output := range tx.SiafundOutputs {
		err := addAddress(btx, output.UnlockHash, txid)
		if err != nil {
			return err
		}
		err = addNewSFOutput(btx, tx.SiafundOutputID(i), txid)
		if err != nil {
			return err
		}

	}

	return addHashType(btx, txid, hashTransaction)
}
<commit_msg>clean up addblock with helper fns<commit_after>package blockexplorer

import (
	"errors"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"

	"github.com/boltdb/bolt"
)

var (
	ErrNilEntry = errors.New("entry does not exist")
)

func getObject(b *bolt.Bucket, key, obj interface{}) error {
	objBytes := b.Get(encoding.Marshal(key))
	if objBytes == nil {
		return ErrNilEntry
	}
	return encoding.Unmarshal(objBytes, obj)
}

func putObject(b *bolt.Bucket, key, val interface{}) error {
	return b.Put(encoding.Marshal(key), encoding.Marshal(val))
}

// addHashType adds an entry in the Hashes bucket for identifing that hash
func addHashType(tx *bolt.Tx, hash crypto.Hash, hashType int) error {
	b := tx.Bucket([]byte("Hashes"))
	if b == nil {
		return errors.New("bucket Hashes does not exist")
	}

	return putObject(b, hash, hashType)
}

// addAddress either creates a new list of transactions for the given
// address, or adds the txid to the list if such a list already exists
func addAddress(tx *bolt.Tx, addr types.UnlockHash, txid crypto.Hash) error {
	err := addHashType(tx, crypto.Hash(addr), hashUnlockHash)
	if err != nil {
		return err
	}

	b := tx.Bucket([]byte("Addresses"))
	if b == nil {
		return errors.New("Addresses bucket does not exist")
	}

	var txns []crypto.Hash
	err = getObject(b, addr, &txns)
	if err != ErrNilEntry {
		return err
	}
	txns = append(txns, txid)

	return putObject(b, addr, txns)
}

// addSiacoinInput changes an existing outputTransactions struct to
// point to the place where that output was used
func addSiacoinInput(tx *bolt.Tx, outputID types.SiacoinOutputID, txid crypto.Hash) error {
	b := tx.Bucket([]byte("SiacoinOutputs"))
	if b == nil {
		return errors.New("bucket SiacoinOutputs does not exist")
	}

	var ot outputTransactions
	err := getObject(b, outputID, &ot)
	if err != nil {
		return err
	}

	ot.InputTx = txid

	return putObject(b, outputID, ot)
}

// addSiafundInpt does the same thing as addSiacoinInput except with siafunds
func addSiafundInput(tx *bolt.Tx, outputID types.SiafundOutputID, txid crypto.Hash) error {
	b := tx.Bucket([]byte("SiafundOutputs"))
	if b == nil {
		return errors.New("bucket SiafundOutputs does not exist")
	}

	var ot outputTransactions
	err := getObject(b, outputID, &ot)
	if err != nil {
		return err
	}

	ot.InputTx = txid

	return putObject(b, outputID, ot)
}

// addFcRevision changes an existing fcInfo struct to contain the txid
// of the contract revision
func addFcRevision(tx *bolt.Tx, fcid types.FileContractID, txid crypto.Hash) error {
	b := tx.Bucket([]byte("FileContracts"))
	if b == nil {
		return errors.New("bucket FileContracts does not exist")
	}

	var fi fcInfo
	err := getObject(b, fcid, &fi)
	if err != nil {
		return err
	}

	fi.Revisions = append(fi.Revisions, txid)

	return putObject(b, fcid, fi)
}

// addFcProof changes an existing fcInfo struct in the database to
// contain the txid of its storage proof
func addFcProof(tx *bolt.Tx, fcid types.FileContractID, txid crypto.Hash) error {
	b := tx.Bucket([]byte("FileContracts"))
	if b == nil {
		return errors.New("bucket FileContracts does not exist")
	}

	var fi fcInfo
	err := getObject(b, fcid, &fi)
	if err != nil {
		return err
	}

	fi.Proof = txid

	return putObject(b, fcid, fi)
}

func addNewHash(tx *bolt.Tx, bucketName string, t int, hash crypto.Hash, value interface{}) error {
	err := addHashType(tx, hash, t)
	if err != nil {
		return err
	}

	b := tx.Bucket([]byte(bucketName))
	if b == nil {
		return errors.New("bucket does not exist: " + bucketName)
	}
	return putObject(b, hash, value)
}

// addNewOutput creats a new outputTransactions struct and adds it to the database
func addNewOutput(tx *bolt.Tx, outputID types.SiacoinOutputID, txid crypto.Hash) error {
	otx := outputTransactions{txid, crypto.Hash{}}
	return addNewHash(tx, "SiacoinOutputs", hashCoinOutputID, crypto.Hash(outputID), otx)
}

// addNewSFOutput does the same thing as addNewOutput does, except for siafunds
func addNewSFOutput(tx *bolt.Tx, outputID types.SiafundOutputID, txid crypto.Hash) error {
	otx := outputTransactions{txid, crypto.Hash{}}
	return addNewHash(tx, "SiafundOutputs", hashFundOutputID, crypto.Hash(outputID), otx)
}

// addHeight adds a block summary (modules.ExplorerBlockData) to the
// database with a height as the key
func addHeight(tx *bolt.Tx, height types.BlockHeight, bs modules.ExplorerBlockData) error {
	b := tx.Bucket([]byte("Heights"))
	if b == nil {
		return errors.New("bucket Blocks does not exist")
	}

	return putObject(b, height, bs)
}

// addBlockDB parses a block and adds it to the database
func (be *BlockExplorer) addBlockDB(b types.Block) error {
	// Special case for the genesis block, which does not have a
	// valid parent, and for testing, as tests will not always use
	// blocks in consensus
	var blocktarget types.Target
	if b.ID() == be.genesisBlockID {
		blocktarget = types.RootDepth
	} else {
		var exists bool
		blocktarget, exists = be.cs.ChildTarget(b.ParentID)
		if build.DEBUG {
			if build.Release == "testing" {
				blocktarget = types.RootDepth
			}
			if !exists {
				panic("Applied block not in consensus")
			}

		}
	}

	tx, err := be.db.Begin(true)
	if err != nil {
		return err
	}
	defer tx.Rollback()

	// Construct the struct that will be inside the database
	blockStruct := blockData{
		Block:  b,
		Height: be.blockchainHeight,
	}

	err = addNewHash(tx, "Blocks", hashBlock, crypto.Hash(b.ID()), blockStruct)
	if err != nil {
		return err
	}

	bSum := modules.ExplorerBlockData{
		ID:        b.ID(),
		Timestamp: b.Timestamp,
		Target:    blocktarget,
		Size:      uint64(len(encoding.Marshal(b))),
	}

	err = addHeight(tx, be.blockchainHeight, bSum)
	if err != nil {
		return err
	}
	err = addHashType(tx, crypto.Hash(b.ID()), hashBlock)
	if err != nil {
		return err
	}

	// Insert the miner payouts as new outputs
	for i, payout := range b.MinerPayouts {
		err = addAddress(tx, payout.UnlockHash, crypto.Hash(b.ID()))
		if err != nil {
			return err
		}
		err = addNewOutput(tx, b.MinerPayoutID(i), crypto.Hash(b.ID()))
		if err != nil {
			return err
		}
	}

	// Insert each transaction
	for i, txn := range b.Transactions {
		err = addNewHash(tx, "Transactions", hashTransaction, txn.ID(), txInfo{b.ID(), i})
		if err != nil {
			return err
		}
		err = be.addTransaction(tx, txn)
		if err != nil {
			return err
		}
	}

	return tx.Commit()
}

// addTransaction is called from addBlockDB, and delegates the adding
// of information to the database to the functions defined above
func (be *BlockExplorer) addTransaction(btx *bolt.Tx, tx types.Transaction) error {
	// Store this for quick lookup
	txid := tx.ID()

	// Append each input to the list of modifications
	for _, input := range tx.SiacoinInputs {
		err := addSiacoinInput(btx, input.ParentID, txid)
		if err != nil {
			return err
		}
	}

	// Handle all the transaction outputs
	for i, output := range tx.SiacoinOutputs {
		err := addAddress(btx, output.UnlockHash, txid)
		if err != nil {
			return err
		}
		err = addNewOutput(btx, tx.SiacoinOutputID(i), txid)
		if err != nil {
			return err
		}
	}

	// Handle each file contract individually
	for i, contract := range tx.FileContracts {
		fcid := tx.FileContractID(i)
		err := addNewHash(btx, "FileContracts", hashFilecontract, crypto.Hash(fcid), fcInfo{
			Contract: txid,
		})
		if err != nil {
			return err
		}

		for j, output := range contract.ValidProofOutputs {
			err = addAddress(btx, output.UnlockHash, txid)
			if err != nil {
				return err
			}
			err = addNewOutput(btx, fcid.StorageProofOutputID(true, j), txid)
			if err != nil {
				return err
			}
		}
		for j, output := range contract.MissedProofOutputs {
			err = addAddress(btx, output.UnlockHash, txid)
			if err != nil {
				return err
			}
			err = addNewOutput(btx, fcid.StorageProofOutputID(false, j), txid)
			if err != nil {
				return err
			}
		}

		err = addAddress(btx, contract.UnlockHash, txid)
		if err != nil {
			return err
		}
	}

	// Update the list of revisions
	for _, revision := range tx.FileContractRevisions {
		err := addFcRevision(btx, revision.ParentID, txid)
		if err != nil {
			return err
		}

		// Note the old outputs will still be there in the
		// database. This is to provide information to the
		// people who may just need it.
		for i, output := range revision.NewValidProofOutputs {
			err = addAddress(btx, output.UnlockHash, txid)
			if err != nil {
				return err
			}
			err = addNewOutput(btx, revision.ParentID.StorageProofOutputID(true, i), txid)
			if err != nil {
				return err
			}
		}
		for i, output := range revision.NewMissedProofOutputs {
			err = addAddress(btx, output.UnlockHash, txid)
			if err != nil {
				return err
			}
			err = addNewOutput(btx, revision.ParentID.StorageProofOutputID(false, i), txid)
			if err != nil {
				return err
			}
		}

		addAddress(btx, revision.NewUnlockHash, txid)
	}

	// Update the list of storage proofs
	for _, proof := range tx.StorageProofs {
		err := addFcProof(btx, proof.ParentID, txid)
		if err != nil {
			return err
		}
	}

	// Append all the siafund inputs to the modification list
	for _, input := range tx.SiafundInputs {
		err := addSiafundInput(btx, input.ParentID, txid)
		if err != nil {
			return err
		}
	}

	// Handle all the siafund outputs
	for i, output := range tx.SiafundOutputs {
		err := addAddress(btx, output.UnlockHash, txid)
		if err != nil {
			return err
		}
		err = addNewSFOutput(btx, tx.SiafundOutputID(i), txid)
		if err != nil {
			return err
		}

	}

	return addHashType(btx, txid, hashTransaction)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
)

// #cgo CFLAGS: -I/usr/include/chewing
// #cgo LDFLAGS: -lchewing
// #include <chewing.h>
import "C"

type ChewingBenchmarkContext struct {
	accuracy []Accuracy
	ctx      *[0]byte
}

var BOPOMOFO_START = map[rune]uint8{
	'ㄅ': '1',
	'ㄆ': 'q',
	'ㄇ': 'a',
	'ㄈ': 'z',
	'ㄉ': '2',
	'ㄊ': 'w',
	'ㄋ': 's',
	'ㄌ': 'x',
	'ㄍ': 'e',
	'ㄎ': 'd',
	'ㄏ': 'c',
	'ㄐ': 'r',
	'ㄑ': 'f',
	'ㄒ': 'v',
	'ㄓ': '5',
	'ㄔ': 't',
	'ㄕ': 'g',
	'ㄖ': 'b',
	'ㄗ': 'y',
	'ㄘ': 'h',
	'ㄙ': 'n',
}

var BOPOMOFO_END = map[rune]uint8{
	'ㄧ': 'u',
	'ㄨ': 'j',
	'ㄩ': 'm',

	'ㄚ': '8',
	'ㄛ': 'i',
	'ㄜ': 'k',
	'ㄝ': ',',
	'ㄞ': '9',
	'ㄟ': 'o',
	'ㄠ': 'l',
	'ㄡ': '.',
	'ㄢ': '0',
	'ㄣ': 'p',
	'ㄤ': ';',
	'ㄥ': '/',
	'ㄦ': '-',
}

var BOPOMOFO_TONE = map[rune]uint8{
	'˙': '7',
	'ˊ': '6',
	'ˇ': '3',
	'ˋ': '4',
}

func newChewingBenchmarkItem() *ChewingBenchmarkContext {
	ctx := new(ChewingBenchmarkContext)

	ctx.ctx = C.chewing_new2(nil, nil, nil, nil)
	if ctx.ctx == nil {
		panic("chewing_new2 returns NULL")
	}

	return ctx
}

func (ctx *ChewingBenchmarkContext) deinit() {
	if ctx.ctx == nil {
		return
	}

	C.chewing_delete(ctx.ctx)
	ctx.ctx = nil
}

func (ctx *ChewingBenchmarkContext) getName() string {
	return "chewing"
}

func (ctx *ChewingBenchmarkContext) enterBenchmarkInput(input *BenchmarkInput) {
	if ctx.ctx == nil {
		return
	}

	ctx.enterBopomofo(input)
	ctx.computeAccuracy(input)
	ctx.selectCandidate(input)
}

func (ctx *ChewingBenchmarkContext) enterBopomofo(input *BenchmarkInput) {
	C.chewing_clean_bopomofo_buf(ctx.ctx)
	C.chewing_clean_preedit_buf(ctx.ctx)

	for _, key := range bopomofoToKey(input.inputBopomofo) {
		C.chewing_handle_Default(ctx.ctx, C.int(key))
	}
}

func (ctx *ChewingBenchmarkContext) computeAccuracy(input *BenchmarkInput) {
	var accuracy Accuracy

	result := C.GoString(C.chewing_buffer_String_static(ctx.ctx))

	if len(result) != len(input.inputString) {
		panic("len(result) != len(input.inputString)")
	}

	for i := range result {
		if result[i] == input.inputString[i] {
			accuracy.correctCount++
		}
		accuracy.wordCount++
	}

	ctx.accuracy = append(ctx.accuracy, accuracy)
}

func (ctx *ChewingBenchmarkContext) selectCandidate(input *BenchmarkInput) {
	var ret C.int

	ret = C.chewing_handle_Home(ctx.ctx)
	if ret == -1 {
		panic(fmt.Sprintf("C.chewing_handle_Home(ctx.ctx) = %d", ret))
	}

	for _, word := range input.inputString {

		ret = C.chewing_cand_open(ctx.ctx)
		if ret != 0 {
			panic(fmt.Sprintf("C.chewing_cand_open(ctx.ctx) = %d", ret))
		}

		ret = C.chewing_cand_list_last(ctx.ctx)
		if ret != 0 {
			panic(fmt.Sprintf("C.chewing_cand_list_last(ctx.ctx) = %d", ret))
		}

		total := C.chewing_cand_TotalChoice(ctx.ctx)
		for index := C.int(0); index < total; index++ {
			cand := []rune(C.GoString(C.chewing_cand_string_by_index_static(ctx.ctx, index)))

			if len(cand) != 1 {
				panic("C.chewing_cand_string_by_index_static(ctx.ctx, index) does not return single word")
			}

			if cand[0] == word {
				C.chewing_cand_choose_by_index(ctx.ctx, index)
				break
			}
		}

		ret = C.chewing_cand_close(ctx.ctx)
		if ret != 0 {
			panic(fmt.Sprintf("C.chewing_cand_close(ctx.ctx) = %d", ret))
		}

		ret = C.chewing_handle_Right(ctx.ctx)
		if ret != 0 {
			panic(fmt.Sprintf("C.chewing_handle_Right(ctx.ctx) = %d", ret))
		}
	}

	if C.GoString(C.chewing_buffer_String_static(ctx.ctx)) != input.inputString {
		panic("Cannot select correct word")
	}

	ret = C.chewing_commit_preedit_buf(ctx.ctx)
	if ret != 0 {
		panic(fmt.Sprintf("C.chewing_commit_preedit_buf(ctx.ctx) = %d", ret))
	}
}

func (ctx *ChewingBenchmarkContext) getAccuracy() []Accuracy {
	return ctx.accuracy
}

func bopomofoToKey(bopomofo string) (keySequence []uint8) {
	terminated := true
	var key uint8
	var ok bool

	for _, runeValue := range bopomofo {
		key, ok = BOPOMOFO_START[runeValue]
		if ok {
			if !terminated {
				keySequence = append(keySequence, ' ')
				terminated = true
			}
			keySequence = append(keySequence, key)
			continue
		}

		key, ok = BOPOMOFO_END[runeValue]
		if ok {
			terminated = false
			keySequence = append(keySequence, key)
			continue
		}

		key, ok = BOPOMOFO_TONE[runeValue]
		if ok {
			terminated = true
			keySequence = append(keySequence, key)
			continue
		}

		panic(fmt.Sprintf("Unknown bopomofo: %c", runeValue))
	}

	if !terminated {
		keySequence = append(keySequence, ' ')
	}

	return keySequence
}
<commit_msg>Remove unnecessary chewing_cand_close()<commit_after>package main

import (
	"fmt"
)

// #cgo CFLAGS: -I/usr/include/chewing
// #cgo LDFLAGS: -lchewing
// #include <chewing.h>
import "C"

type ChewingBenchmarkContext struct {
	accuracy []Accuracy
	ctx      *[0]byte
}

var BOPOMOFO_START = map[rune]uint8{
	'ㄅ': '1',
	'ㄆ': 'q',
	'ㄇ': 'a',
	'ㄈ': 'z',
	'ㄉ': '2',
	'ㄊ': 'w',
	'ㄋ': 's',
	'ㄌ': 'x',
	'ㄍ': 'e',
	'ㄎ': 'd',
	'ㄏ': 'c',
	'ㄐ': 'r',
	'ㄑ': 'f',
	'ㄒ': 'v',
	'ㄓ': '5',
	'ㄔ': 't',
	'ㄕ': 'g',
	'ㄖ': 'b',
	'ㄗ': 'y',
	'ㄘ': 'h',
	'ㄙ': 'n',
}

var BOPOMOFO_END = map[rune]uint8{
	'ㄧ': 'u',
	'ㄨ': 'j',
	'ㄩ': 'm',

	'ㄚ': '8',
	'ㄛ': 'i',
	'ㄜ': 'k',
	'ㄝ': ',',
	'ㄞ': '9',
	'ㄟ': 'o',
	'ㄠ': 'l',
	'ㄡ': '.',
	'ㄢ': '0',
	'ㄣ': 'p',
	'ㄤ': ';',
	'ㄥ': '/',
	'ㄦ': '-',
}

var BOPOMOFO_TONE = map[rune]uint8{
	'˙': '7',
	'ˊ': '6',
	'ˇ': '3',
	'ˋ': '4',
}

func newChewingBenchmarkItem() *ChewingBenchmarkContext {
	ctx := new(ChewingBenchmarkContext)

	ctx.ctx = C.chewing_new2(nil, nil, nil, nil)
	if ctx.ctx == nil {
		panic("chewing_new2 returns NULL")
	}

	return ctx
}

func (ctx *ChewingBenchmarkContext) deinit() {
	if ctx.ctx == nil {
		return
	}

	C.chewing_delete(ctx.ctx)
	ctx.ctx = nil
}

func (ctx *ChewingBenchmarkContext) getName() string {
	return "chewing"
}

func (ctx *ChewingBenchmarkContext) enterBenchmarkInput(input *BenchmarkInput) {
	if ctx.ctx == nil {
		return
	}

	ctx.enterBopomofo(input)
	ctx.computeAccuracy(input)
	ctx.selectCandidate(input)
}

func (ctx *ChewingBenchmarkContext) enterBopomofo(input *BenchmarkInput) {
	C.chewing_clean_bopomofo_buf(ctx.ctx)
	C.chewing_clean_preedit_buf(ctx.ctx)

	for _, key := range bopomofoToKey(input.inputBopomofo) {
		C.chewing_handle_Default(ctx.ctx, C.int(key))
	}
}

func (ctx *ChewingBenchmarkContext) computeAccuracy(input *BenchmarkInput) {
	var accuracy Accuracy

	result := C.GoString(C.chewing_buffer_String_static(ctx.ctx))

	if len(result) != len(input.inputString) {
		panic("len(result) != len(input.inputString)")
	}

	for i := range result {
		if result[i] == input.inputString[i] {
			accuracy.correctCount++
		}
		accuracy.wordCount++
	}

	ctx.accuracy = append(ctx.accuracy, accuracy)
}

func (ctx *ChewingBenchmarkContext) selectCandidate(input *BenchmarkInput) {
	var ret C.int

	ret = C.chewing_handle_Home(ctx.ctx)
	if ret == -1 {
		panic(fmt.Sprintf("C.chewing_handle_Home(ctx.ctx) = %d", ret))
	}

	for _, word := range input.inputString {

		ret = C.chewing_cand_open(ctx.ctx)
		if ret != 0 {
			panic(fmt.Sprintf("C.chewing_cand_open(ctx.ctx) = %d", ret))
		}

		ret = C.chewing_cand_list_last(ctx.ctx)
		if ret != 0 {
			panic(fmt.Sprintf("C.chewing_cand_list_last(ctx.ctx) = %d", ret))
		}

		total := C.chewing_cand_TotalChoice(ctx.ctx)
		for index := C.int(0); index < total; index++ {
			cand := []rune(C.GoString(C.chewing_cand_string_by_index_static(ctx.ctx, index)))

			if len(cand) != 1 {
				panic("C.chewing_cand_string_by_index_static(ctx.ctx, index) does not return single word")
			}

			if cand[0] == word {
				C.chewing_cand_choose_by_index(ctx.ctx, index)
				break
			}
		}

		ret = C.chewing_handle_Right(ctx.ctx)
		if ret != 0 {
			panic(fmt.Sprintf("C.chewing_handle_Right(ctx.ctx) = %d", ret))
		}
	}

	if C.GoString(C.chewing_buffer_String_static(ctx.ctx)) != input.inputString {
		panic("Cannot select correct word")
	}

	ret = C.chewing_commit_preedit_buf(ctx.ctx)
	if ret != 0 {
		panic(fmt.Sprintf("C.chewing_commit_preedit_buf(ctx.ctx) = %d", ret))
	}
}

func (ctx *ChewingBenchmarkContext) getAccuracy() []Accuracy {
	return ctx.accuracy
}

func bopomofoToKey(bopomofo string) (keySequence []uint8) {
	terminated := true
	var key uint8
	var ok bool

	for _, runeValue := range bopomofo {
		key, ok = BOPOMOFO_START[runeValue]
		if ok {
			if !terminated {
				keySequence = append(keySequence, ' ')
				terminated = true
			}
			keySequence = append(keySequence, key)
			continue
		}

		key, ok = BOPOMOFO_END[runeValue]
		if ok {
			terminated = false
			keySequence = append(keySequence, key)
			continue
		}

		key, ok = BOPOMOFO_TONE[runeValue]
		if ok {
			terminated = true
			keySequence = append(keySequence, key)
			continue
		}

		panic(fmt.Sprintf("Unknown bopomofo: %c", runeValue))
	}

	if !terminated {
		keySequence = append(keySequence, ' ')
	}

	return keySequence
}
<|endoftext|>


<commit_before>package yelp

import (
	"errors"
	"fmt"

	"github.com/guregu/null"
)

// CoordinateOptions are used with complex searches for locations.
// The geographic coordinate format is defined as:
// ll=latitude,longitude,accuracy,altitude,altitude_accuracy
type CoordinateOptions struct {
	Latitude         null.Float // Latitude of geo-point to search near (required)
	Longitude        null.Float // Longitude of geo-point to search near (required)
	Accuracy         null.Float // Accuracy of latitude, longitude (optional)
	Altitude         null.Float // Altitude (optional)
	AltitudeAccuracy null.Float // Accuracy of altitude (optional)
}

// getParameters will reflect over the values of the given
// struct, and provide a type appropriate set of querystring parameters
// that match the defined values.
func (o CoordinateOptions) getParameters() (params map[string]string, err error) {
	// coordinate requires at least a latitude and longitude - others are option
	if !o.Latitude.Valid || !o.Longitude.Valid {
		return nil, errors.New("latitude and longitude are required fields for a coordinate based search")
	}

	ll := fmt.Sprintf("%v,%v", o.Latitude.Float64, o.Longitude.Float64)
	if o.Accuracy.Valid {
		ll += fmt.Sprintf(",%v", o.Accuracy.Float64)
	}
	if o.Altitude.Valid {
		ll += fmt.Sprintf(",%v", o.Altitude.Float64)
	}
	if o.AltitudeAccuracy.Valid {
		ll += fmt.Sprintf(",%v", o.AltitudeAccuracy.Float64)
	}

	return map[string]string{
		"ll": ll,
	}, nil
}
<commit_msg>update the new API of yelp<commit_after>package yelp

import (
	"errors"
	"fmt"

	"github.com/guregu/null"
)

// CoordinateOptions are used with complex searches for locations.
// The geographic coordinate format is defined as:
// ll=latitude,longitude,accuracy,altitude,altitude_accuracy
type CoordinateOptions struct {
	Latitude         null.Float // Latitude of geo-point to search near (required)
	Longitude        null.Float // Longitude of geo-point to search near (required)
	Accuracy         null.Float // Accuracy of latitude, longitude (optional)
	Altitude         null.Float // Altitude (optional)
	AltitudeAccuracy null.Float // Accuracy of altitude (optional)
}

// getParameters will reflect over the values of the given
// struct, and provide a type appropriate set of querystring parameters
// that match the defined values.
func (o CoordinateOptions) getParameters() (params map[string]string, err error) {
	// coordinate requires at least a latitude and longitude - others are option
	if !o.Latitude.Valid || !o.Longitude.Valid {
		return nil, errors.New("latitude and longitude are required fields for a coordinate based search")
	}
	if o.cc != "" {
		params["latitude"] = fmt.Sprintf("%v", o.Latitude.Float64)
	}
	if o.lang != "" {
		params["longitude"] = fmt.Sprintf("%v", o.Longitude.Float64)
	}

	ll := fmt.Sprintf("%v,%v", o.Latitude.Float64, o.Longitude.Float64)
	if o.Accuracy.Valid {
		ll += fmt.Sprintf(",%v", o.Accuracy.Float64)
	}
	if o.Altitude.Valid {
		ll += fmt.Sprintf(",%v", o.Altitude.Float64)
	}
	if o.AltitudeAccuracy.Valid {
		ll += fmt.Sprintf(",%v", o.AltitudeAccuracy.Float64)
	}

//	return map[string]string{
//		"ll": ll,
//	}, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Knative 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.
*/

package metrics

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path"
	"strconv"
	"strings"
	"time"

	"go.opencensus.io/stats"
	corev1 "k8s.io/api/core/v1"
	"knative.dev/pkg/metrics/metricskey"
)

// metricsBackend specifies the backend to use for metrics
type metricsBackend string

const (
	// BackendDestinationKey points to the config map entry key for metrics backend destination.
	BackendDestinationKey = "metrics.backend-destination"
	// DomainEnv points to the metrics domain env var.
	DomainEnv = "METRICS_DOMAIN"

	// The following keys are used to configure metrics reporting.
	// See https://github.com/knative/serving/blob/master/config/config-observability.yaml
	// for details.
	allowStackdriverCustomMetricsKey = "metrics.allow-stackdriver-custom-metrics"
	collectorAddressKey              = "metrics.opencensus-address"
	collectorSecureKey               = "metrics.opencensus-require-tls"
	reportingPeriodKey               = "metrics.reporting-period-seconds"

	// Stackdriver client configuration keys
	stackdriverClusterNameKey           = "metrics.stackdriver-cluster-name"
	stackdriverCustomMetricSubDomainKey = "metrics.stackdriver-custom-metrics-subdomain"
	stackdriverGCPLocationKey           = "metrics.stackdriver-gcp-location"
	stackdriverProjectIDKey             = "metrics.stackdriver-project-id"
	stackdriverUseSecretKey             = "metrics.stackdriver-use-secret"

	defaultBackendEnvName = "DEFAULT_METRICS_BACKEND"
	defaultPrometheusPort = 9090
	maxPrometheusPort     = 65535
	minPrometheusPort     = 1024
	prometheusPortEnvName = "METRICS_PROMETHEUS_PORT"
)

// Metrics backend "enum".
const (
	// stackdriver is used for Stackdriver backend
	stackdriver metricsBackend = "stackdriver"
	// prometheus is used for Prometheus backend
	prometheus metricsBackend = "prometheus"
	// openCensus is used to export to the OpenCensus Agent / Collector,
	// which can send to many other services.
	openCensus metricsBackend = "opencensus"
	// none is used to export, well, nothing.
	none metricsBackend = "none"
)

type metricsConfig struct {
	// The metrics domain. e.g. "serving.knative.dev" or "build.knative.dev".
	domain string
	// The component that emits the metrics. e.g. "activator", "autoscaler".
	component string
	// The metrics backend destination.
	backendDestination metricsBackend
	// reportingPeriod specifies the interval between reporting aggregated views.
	// If duration is less than or equal to zero, it enables the default behavior.
	reportingPeriod time.Duration

	// recorder provides a hook for performing custom transformations before
	// writing the metrics to the stats.RecordWithOptions interface.
	recorder func(context.Context, []stats.Measurement, ...stats.Options) error

	// secret contains credentials for an exporter to use for authentication.
	secret *corev1.Secret

	// ---- OpenCensus specific below ----
	// collectorAddress is the address of the collector, if not `localhost:55678`
	collectorAddress string
	// Require mutual TLS. Defaults to "false" because mutual TLS is hard to set up.
	requireSecure bool

	// ---- Prometheus specific below ----
	// prometheusPort is the port where metrics are exposed in Prometheus
	// format. It defaults to 9090.
	prometheusPort int

	// ---- Stackdriver specific below ----
	// True if backendDestination equals to "stackdriver". Store this in a variable
	// to reduce string comparison operations.
	isStackdriverBackend bool
	// stackdriverMetricTypePrefix is the metric domain joins component, e.g.
	// "knative.dev/serving/activator". Store this in a variable to reduce string
	// join operations.
	stackdriverMetricTypePrefix string
	// stackdriverCustomMetricTypePrefix is "custom.googleapis.com" joined with the subdomain and component.
	// E.g., "custom.googleapis.com/<subdomain>/<component>".
	// Store this in a variable to reduce string join operations.
	stackdriverCustomMetricTypePrefix string
	// stackdriverClientConfig is the metadata to configure the metrics exporter's Stackdriver client.
	stackdriverClientConfig StackdriverClientConfig
}

// StackdriverClientConfig encapsulates the metadata required to configure a Stackdriver client.
type StackdriverClientConfig struct {
	// ProjectID is the stackdriver project ID to which data is uploaded.
	// This is not necessarily the GCP project ID where the Kubernetes cluster is hosted.
	// Required when the Kubernetes cluster is not hosted on GCE.
	ProjectID string
	// GCPLocation is the GCP region or zone to which data is uploaded.
	// This is not necessarily the GCP location where the Kubernetes cluster is hosted.
	// Required when the Kubernetes cluster is not hosted on GCE.
	GCPLocation string
	// ClusterName is the cluster name with which the data will be associated in Stackdriver.
	// Required when the Kubernetes cluster is not hosted on GCE.
	ClusterName string
	// UseSecret is whether the credentials stored in a Kubernetes Secret should be used to
	// authenticate with Stackdriver. The Secret name and namespace can be specified by calling
	// metrics.SetStackdriverSecretLocation.
	// If UseSecret is false, Google Application Default Credentials
	// will be used (https://cloud.google.com/docs/authentication/production).
	UseSecret bool
}

// NewStackdriverClientConfigFromMap creates a stackdriverClientConfig from the given map
func NewStackdriverClientConfigFromMap(config map[string]string) *StackdriverClientConfig {
	return &StackdriverClientConfig{
		ProjectID:   config[stackdriverProjectIDKey],
		GCPLocation: config[stackdriverGCPLocationKey],
		ClusterName: config[stackdriverClusterNameKey],
		UseSecret:   strings.EqualFold(config[stackdriverUseSecretKey], "true"),
	}
}

// record applies the `ros` Options to each measurement in `mss` and then records the resulting
// measurements in the metricsConfig's designated backend.
func (mc *metricsConfig) record(ctx context.Context, mss []stats.Measurement, ros ...stats.Options) error {
	if mc == nil || mc.backendDestination == none {
		// Don't record data points if the metric config is not initialized yet or if
		// the defined backend is "none" explicitly.
		return nil
	}

	if mc.recorder == nil {
		opt, err := optionForResource(metricskey.GetResource(ctx))
		if err != nil {
			return err
		}
		ros = append(ros, opt)

		return stats.RecordWithOptions(ctx, append(ros, stats.WithMeasurements(mss...))...)
	}
	return mc.recorder(ctx, mss, ros...)
}

func createMetricsConfig(ctx context.Context, ops ExporterOptions) (*metricsConfig, error) {
	var mc metricsConfig

	if ops.Domain == "" {
		return nil, errors.New("metrics domain cannot be empty")
	}
	mc.domain = ops.Domain

	if ops.Component == "" {
		return nil, errors.New("metrics component name cannot be empty")
	}
	mc.component = ops.Component

	if ops.ConfigMap == nil {
		return nil, errors.New("metrics config map cannot be empty")
	}
	m := ops.ConfigMap
	// Read backend setting from environment variable first
	backend := os.Getenv(defaultBackendEnvName)
	if backend == "" {
		// Use Prometheus if DEFAULT_METRICS_BACKEND does not exist or is empty
		backend = string(prometheus)
	}
	// Override backend if it is set in the config map.
	if backendFromConfig, ok := m[BackendDestinationKey]; ok {
		backend = backendFromConfig
	}

	switch lb := metricsBackend(strings.ToLower(backend)); lb {
	case stackdriver, prometheus, openCensus:
		mc.backendDestination = lb
	default:
		return nil, fmt.Errorf("unsupported metrics backend value %q", backend)
	}

	switch mc.backendDestination {
	case openCensus:
		mc.collectorAddress = ops.ConfigMap[collectorAddressKey]
		if isSecure := ops.ConfigMap[collectorSecureKey]; isSecure != "" {
			var err error
			if mc.requireSecure, err = strconv.ParseBool(isSecure); err != nil {
				return nil, fmt.Errorf("invalid %s value %q", collectorSecureKey, isSecure)
			}

			if mc.requireSecure {
				mc.secret, err = getOpenCensusSecret(ops.Component, ops.Secrets)
				if err != nil {
					return nil, err
				}
			}
		}
	case prometheus:
		pp := ops.PrometheusPort
		if pp == 0 {
			var err error
			pp, err = prometheusPort()
			if err != nil {
				return nil, fmt.Errorf("failed to determine Prometheus port: %w", err)
			}
		}

		if pp < minPrometheusPort || pp > maxPrometheusPort {
			return nil, fmt.Errorf("invalid port %d, should be between %d and %d",
				pp, minPrometheusPort, maxPrometheusPort)
		}

		mc.prometheusPort = pp
	case stackdriver:
		// If stackdriverClientConfig is not provided for stackdriver backend destination, OpenCensus will try to
		// use the application default credentials. If that is not available, Opencensus would fail to create the
		// metrics exporter.
		scc := NewStackdriverClientConfigFromMap(m)
		mc.stackdriverClientConfig = *scc
		mc.isStackdriverBackend = true
		var allowCustomMetrics bool
		var err error
		mc.stackdriverMetricTypePrefix = path.Join(mc.domain, mc.component)

		customMetricsSubDomain := m[stackdriverCustomMetricSubDomainKey]
		if customMetricsSubDomain == "" {
			customMetricsSubDomain = defaultCustomMetricSubDomain
		}
		mc.stackdriverCustomMetricTypePrefix = path.Join(customMetricTypePrefix, customMetricsSubDomain, mc.component)
		if ascmStr := m[allowStackdriverCustomMetricsKey]; ascmStr != "" {
			allowCustomMetrics, err = strconv.ParseBool(ascmStr)
			if err != nil {
				return nil, fmt.Errorf("invalid %s value %q", allowStackdriverCustomMetricsKey, ascmStr)
			}
		}

		mc.recorder = sdCustomMetricsRecorder(mc, allowCustomMetrics)

		if scc.UseSecret {
			secret, err := getStackdriverSecret(ctx, ops.Secrets)
			if err != nil {
				return nil, err
			}

			mc.secret = secret
		}
	}

	// If reporting period is specified, use the value from the configuration.
	// If not, set a default value based on the selected backend.
	// Each exporter makes different promises about what the lowest supported
	// reporting period is. For Stackdriver, this value is 1 minute.
	// For Prometheus, we will use a lower value since the exporter doesn't
	// push anything but just responds to pull requests, and shorter durations
	// do not really hurt the performance and we rely on the scraping configuration.
	if repStr := m[reportingPeriodKey]; repStr != "" {
		repInt, err := strconv.Atoi(repStr)
		if err != nil {
			return nil, fmt.Errorf("invalid %s value %q", reportingPeriodKey, repStr)
		}
		mc.reportingPeriod = time.Duration(repInt) * time.Second
	} else {
		switch mc.backendDestination {
		case stackdriver, openCensus:
			mc.reportingPeriod = time.Minute
		case prometheus:
			mc.reportingPeriod = 5 * time.Second
		}
	}
	return &mc, nil
}

// Domain holds the metrics domain to use for surfacing metrics.
func Domain() string {
	if domain := os.Getenv(DomainEnv); domain != "" {
		return domain
	}

	panic(fmt.Sprintf(`The environment variable %q is not set

If this is a process running on Kubernetes, then it should be specifying
this via:

  env:
  - name: %s
    value: knative.dev/some-repository

If this is a Go unit test consuming metric.Domain() then it should add the
following import:

import (
	_ "knative.dev/pkg/metrics/testing"
)`, DomainEnv, DomainEnv))
}

// prometheusPort returns the TCP port number configured via the environment
// for the Prometheus metrics exporter if it's set, a default value otherwise.
// No validation is performed on the port value, other than ensuring that value
// is a valid port number (16-bit unsigned integer).
func prometheusPort() (int, error) {
	ppStr := os.Getenv(prometheusPortEnvName)
	if ppStr == "" {
		return defaultPrometheusPort, nil
	}

	pp, err := strconv.ParseUint(ppStr, 10, 16)
	if err != nil {
		return -1, fmt.Errorf("the environment variable %q could not be parsed as a port number: %w",
			prometheusPortEnvName, err)
	}

	return int(pp), nil
}

// JsonToMetricsOptions converts a JSON string to an ExporterOptions object.
// TODO(vagababov): remove after updating deps.
// Deprecated: Use JSONToOptions instead.
func JsonToMetricsOptions(jsonOpts string) (*ExporterOptions, error) { //nolint // No rename due to backwards incompatibility. {
	return JSONToOptions(jsonOpts)
}

// JSONToOptions converts a json string to ExporterOptions.
func JSONToOptions(jsonOpts string) (*ExporterOptions, error) {
	var opts ExporterOptions
	if jsonOpts == "" {
		return nil, errors.New("json options string is empty")
	}

	if err := json.Unmarshal([]byte(jsonOpts), &opts); err != nil {
		return nil, err
	}

	return &opts, nil
}

// MetricsOptionsToJson converts an ExporterOptions object to a JSON string.
// TODO(vagababov): remove after updating deps.
// Deprecated: Use OptionsToJSON instead.
func MetricsOptionsToJson(opts *ExporterOptions) (string, error) { //nolint // No rename due to backwards incompatibility.
	return OptionsToJSON(opts)
}

// OptionsToJSON converts an ExporterOptions object to a JSON string.
func OptionsToJSON(opts *ExporterOptions) (string, error) {
	if opts == nil {
		return "", nil
	}

	jsonOpts, err := json.Marshal(opts)
	return string(jsonOpts), err
}
<commit_msg>Remove deprecated methods now that downstream has been updated (#1847)<commit_after>/*
Copyright 2018 The Knative 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.
*/

package metrics

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path"
	"strconv"
	"strings"
	"time"

	"go.opencensus.io/stats"
	corev1 "k8s.io/api/core/v1"
	"knative.dev/pkg/metrics/metricskey"
)

// metricsBackend specifies the backend to use for metrics
type metricsBackend string

const (
	// BackendDestinationKey points to the config map entry key for metrics backend destination.
	BackendDestinationKey = "metrics.backend-destination"
	// DomainEnv points to the metrics domain env var.
	DomainEnv = "METRICS_DOMAIN"

	// The following keys are used to configure metrics reporting.
	// See https://github.com/knative/serving/blob/master/config/config-observability.yaml
	// for details.
	allowStackdriverCustomMetricsKey = "metrics.allow-stackdriver-custom-metrics"
	collectorAddressKey              = "metrics.opencensus-address"
	collectorSecureKey               = "metrics.opencensus-require-tls"
	reportingPeriodKey               = "metrics.reporting-period-seconds"

	// Stackdriver client configuration keys
	stackdriverClusterNameKey           = "metrics.stackdriver-cluster-name"
	stackdriverCustomMetricSubDomainKey = "metrics.stackdriver-custom-metrics-subdomain"
	stackdriverGCPLocationKey           = "metrics.stackdriver-gcp-location"
	stackdriverProjectIDKey             = "metrics.stackdriver-project-id"
	stackdriverUseSecretKey             = "metrics.stackdriver-use-secret"

	defaultBackendEnvName = "DEFAULT_METRICS_BACKEND"
	defaultPrometheusPort = 9090
	maxPrometheusPort     = 65535
	minPrometheusPort     = 1024
	prometheusPortEnvName = "METRICS_PROMETHEUS_PORT"
)

// Metrics backend "enum".
const (
	// stackdriver is used for Stackdriver backend
	stackdriver metricsBackend = "stackdriver"
	// prometheus is used for Prometheus backend
	prometheus metricsBackend = "prometheus"
	// openCensus is used to export to the OpenCensus Agent / Collector,
	// which can send to many other services.
	openCensus metricsBackend = "opencensus"
	// none is used to export, well, nothing.
	none metricsBackend = "none"
)

type metricsConfig struct {
	// The metrics domain. e.g. "serving.knative.dev" or "build.knative.dev".
	domain string
	// The component that emits the metrics. e.g. "activator", "autoscaler".
	component string
	// The metrics backend destination.
	backendDestination metricsBackend
	// reportingPeriod specifies the interval between reporting aggregated views.
	// If duration is less than or equal to zero, it enables the default behavior.
	reportingPeriod time.Duration

	// recorder provides a hook for performing custom transformations before
	// writing the metrics to the stats.RecordWithOptions interface.
	recorder func(context.Context, []stats.Measurement, ...stats.Options) error

	// secret contains credentials for an exporter to use for authentication.
	secret *corev1.Secret

	// ---- OpenCensus specific below ----
	// collectorAddress is the address of the collector, if not `localhost:55678`
	collectorAddress string
	// Require mutual TLS. Defaults to "false" because mutual TLS is hard to set up.
	requireSecure bool

	// ---- Prometheus specific below ----
	// prometheusPort is the port where metrics are exposed in Prometheus
	// format. It defaults to 9090.
	prometheusPort int

	// ---- Stackdriver specific below ----
	// True if backendDestination equals to "stackdriver". Store this in a variable
	// to reduce string comparison operations.
	isStackdriverBackend bool
	// stackdriverMetricTypePrefix is the metric domain joins component, e.g.
	// "knative.dev/serving/activator". Store this in a variable to reduce string
	// join operations.
	stackdriverMetricTypePrefix string
	// stackdriverCustomMetricTypePrefix is "custom.googleapis.com" joined with the subdomain and component.
	// E.g., "custom.googleapis.com/<subdomain>/<component>".
	// Store this in a variable to reduce string join operations.
	stackdriverCustomMetricTypePrefix string
	// stackdriverClientConfig is the metadata to configure the metrics exporter's Stackdriver client.
	stackdriverClientConfig StackdriverClientConfig
}

// StackdriverClientConfig encapsulates the metadata required to configure a Stackdriver client.
type StackdriverClientConfig struct {
	// ProjectID is the stackdriver project ID to which data is uploaded.
	// This is not necessarily the GCP project ID where the Kubernetes cluster is hosted.
	// Required when the Kubernetes cluster is not hosted on GCE.
	ProjectID string
	// GCPLocation is the GCP region or zone to which data is uploaded.
	// This is not necessarily the GCP location where the Kubernetes cluster is hosted.
	// Required when the Kubernetes cluster is not hosted on GCE.
	GCPLocation string
	// ClusterName is the cluster name with which the data will be associated in Stackdriver.
	// Required when the Kubernetes cluster is not hosted on GCE.
	ClusterName string
	// UseSecret is whether the credentials stored in a Kubernetes Secret should be used to
	// authenticate with Stackdriver. The Secret name and namespace can be specified by calling
	// metrics.SetStackdriverSecretLocation.
	// If UseSecret is false, Google Application Default Credentials
	// will be used (https://cloud.google.com/docs/authentication/production).
	UseSecret bool
}

// NewStackdriverClientConfigFromMap creates a stackdriverClientConfig from the given map
func NewStackdriverClientConfigFromMap(config map[string]string) *StackdriverClientConfig {
	return &StackdriverClientConfig{
		ProjectID:   config[stackdriverProjectIDKey],
		GCPLocation: config[stackdriverGCPLocationKey],
		ClusterName: config[stackdriverClusterNameKey],
		UseSecret:   strings.EqualFold(config[stackdriverUseSecretKey], "true"),
	}
}

// record applies the `ros` Options to each measurement in `mss` and then records the resulting
// measurements in the metricsConfig's designated backend.
func (mc *metricsConfig) record(ctx context.Context, mss []stats.Measurement, ros ...stats.Options) error {
	if mc == nil || mc.backendDestination == none {
		// Don't record data points if the metric config is not initialized yet or if
		// the defined backend is "none" explicitly.
		return nil
	}

	if mc.recorder == nil {
		opt, err := optionForResource(metricskey.GetResource(ctx))
		if err != nil {
			return err
		}
		ros = append(ros, opt)

		return stats.RecordWithOptions(ctx, append(ros, stats.WithMeasurements(mss...))...)
	}
	return mc.recorder(ctx, mss, ros...)
}

func createMetricsConfig(ctx context.Context, ops ExporterOptions) (*metricsConfig, error) {
	var mc metricsConfig

	if ops.Domain == "" {
		return nil, errors.New("metrics domain cannot be empty")
	}
	mc.domain = ops.Domain

	if ops.Component == "" {
		return nil, errors.New("metrics component name cannot be empty")
	}
	mc.component = ops.Component

	if ops.ConfigMap == nil {
		return nil, errors.New("metrics config map cannot be empty")
	}
	m := ops.ConfigMap
	// Read backend setting from environment variable first
	backend := os.Getenv(defaultBackendEnvName)
	if backend == "" {
		// Use Prometheus if DEFAULT_METRICS_BACKEND does not exist or is empty
		backend = string(prometheus)
	}
	// Override backend if it is set in the config map.
	if backendFromConfig, ok := m[BackendDestinationKey]; ok {
		backend = backendFromConfig
	}

	switch lb := metricsBackend(strings.ToLower(backend)); lb {
	case stackdriver, prometheus, openCensus:
		mc.backendDestination = lb
	default:
		return nil, fmt.Errorf("unsupported metrics backend value %q", backend)
	}

	switch mc.backendDestination {
	case openCensus:
		mc.collectorAddress = ops.ConfigMap[collectorAddressKey]
		if isSecure := ops.ConfigMap[collectorSecureKey]; isSecure != "" {
			var err error
			if mc.requireSecure, err = strconv.ParseBool(isSecure); err != nil {
				return nil, fmt.Errorf("invalid %s value %q", collectorSecureKey, isSecure)
			}

			if mc.requireSecure {
				mc.secret, err = getOpenCensusSecret(ops.Component, ops.Secrets)
				if err != nil {
					return nil, err
				}
			}
		}
	case prometheus:
		pp := ops.PrometheusPort
		if pp == 0 {
			var err error
			pp, err = prometheusPort()
			if err != nil {
				return nil, fmt.Errorf("failed to determine Prometheus port: %w", err)
			}
		}

		if pp < minPrometheusPort || pp > maxPrometheusPort {
			return nil, fmt.Errorf("invalid port %d, should be between %d and %d",
				pp, minPrometheusPort, maxPrometheusPort)
		}

		mc.prometheusPort = pp
	case stackdriver:
		// If stackdriverClientConfig is not provided for stackdriver backend destination, OpenCensus will try to
		// use the application default credentials. If that is not available, Opencensus would fail to create the
		// metrics exporter.
		scc := NewStackdriverClientConfigFromMap(m)
		mc.stackdriverClientConfig = *scc
		mc.isStackdriverBackend = true
		var allowCustomMetrics bool
		var err error
		mc.stackdriverMetricTypePrefix = path.Join(mc.domain, mc.component)

		customMetricsSubDomain := m[stackdriverCustomMetricSubDomainKey]
		if customMetricsSubDomain == "" {
			customMetricsSubDomain = defaultCustomMetricSubDomain
		}
		mc.stackdriverCustomMetricTypePrefix = path.Join(customMetricTypePrefix, customMetricsSubDomain, mc.component)
		if ascmStr := m[allowStackdriverCustomMetricsKey]; ascmStr != "" {
			allowCustomMetrics, err = strconv.ParseBool(ascmStr)
			if err != nil {
				return nil, fmt.Errorf("invalid %s value %q", allowStackdriverCustomMetricsKey, ascmStr)
			}
		}

		mc.recorder = sdCustomMetricsRecorder(mc, allowCustomMetrics)

		if scc.UseSecret {
			secret, err := getStackdriverSecret(ctx, ops.Secrets)
			if err != nil {
				return nil, err
			}

			mc.secret = secret
		}
	}

	// If reporting period is specified, use the value from the configuration.
	// If not, set a default value based on the selected backend.
	// Each exporter makes different promises about what the lowest supported
	// reporting period is. For Stackdriver, this value is 1 minute.
	// For Prometheus, we will use a lower value since the exporter doesn't
	// push anything but just responds to pull requests, and shorter durations
	// do not really hurt the performance and we rely on the scraping configuration.
	if repStr := m[reportingPeriodKey]; repStr != "" {
		repInt, err := strconv.Atoi(repStr)
		if err != nil {
			return nil, fmt.Errorf("invalid %s value %q", reportingPeriodKey, repStr)
		}
		mc.reportingPeriod = time.Duration(repInt) * time.Second
	} else {
		switch mc.backendDestination {
		case stackdriver, openCensus:
			mc.reportingPeriod = time.Minute
		case prometheus:
			mc.reportingPeriod = 5 * time.Second
		}
	}
	return &mc, nil
}

// Domain holds the metrics domain to use for surfacing metrics.
func Domain() string {
	if domain := os.Getenv(DomainEnv); domain != "" {
		return domain
	}

	panic(fmt.Sprintf(`The environment variable %q is not set

If this is a process running on Kubernetes, then it should be specifying
this via:

  env:
  - name: %s
    value: knative.dev/some-repository

If this is a Go unit test consuming metric.Domain() then it should add the
following import:

import (
	_ "knative.dev/pkg/metrics/testing"
)`, DomainEnv, DomainEnv))
}

// prometheusPort returns the TCP port number configured via the environment
// for the Prometheus metrics exporter if it's set, a default value otherwise.
// No validation is performed on the port value, other than ensuring that value
// is a valid port number (16-bit unsigned integer).
func prometheusPort() (int, error) {
	ppStr := os.Getenv(prometheusPortEnvName)
	if ppStr == "" {
		return defaultPrometheusPort, nil
	}

	pp, err := strconv.ParseUint(ppStr, 10, 16)
	if err != nil {
		return -1, fmt.Errorf("the environment variable %q could not be parsed as a port number: %w",
			prometheusPortEnvName, err)
	}

	return int(pp), nil
}

// JSONToOptions converts a json string to ExporterOptions.
func JSONToOptions(jsonOpts string) (*ExporterOptions, error) {
	var opts ExporterOptions
	if jsonOpts == "" {
		return nil, errors.New("json options string is empty")
	}

	if err := json.Unmarshal([]byte(jsonOpts), &opts); err != nil {
		return nil, err
	}

	return &opts, nil
}

// OptionsToJSON converts an ExporterOptions object to a JSON string.
func OptionsToJSON(opts *ExporterOptions) (string, error) {
	if opts == nil {
		return "", nil
	}

	jsonOpts, err := json.Marshal(opts)
	return string(jsonOpts), err
}
<|endoftext|>


<commit_before>// Copyright 2011 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

// Package metrics provides storage for metrics being recorded by mtail
// programs.
package metrics

import (
	"encoding/json"
	"fmt"
	"sync"
	"time"

	"github.com/google/mtail/metrics/datum"
	"github.com/pkg/errors"
)

// Kind enumerates the types of metrics supported.
type Kind int

const (
	_ Kind = iota

	// Counter is a monotonically nondecreasing metric.
	Counter

	// Gauge is a Kind that can take on any value, and may be set
	// discontinuously from its previous value.
	Gauge

	// Timer is a specialisation of Gauge that can be used to store time
	// intervals, such as latency and durations.  It enables certain behaviour
	// in exporters that handle time intervals such as StatsD.
	Timer

	// Text is a special metric type for free text, usually for operating as a 'hidden' metric, as often these values cannot be exported.
	Text
)

const (
	// Int indicates this metric is an integer metric type.
	Int = datum.Int
	// Float indicates this metric is a floating-point metric type.
	Float = datum.Float
	// String indicates this metric contains string values
	String = datum.String
)

func (m Kind) String() string {
	switch m {
	case Counter:
		return "Counter"
	case Gauge:
		return "Gauge"
	case Timer:
		return "Timer"
	case Text:
		return "Text"
	}
	return "Unknown"
}

// LabelValue is an object that names a Datum value with a list of label
// strings.
type LabelValue struct {
	Labels []string `json:",omitempty"`
	Value  datum.Datum
	Expiry time.Duration // After this time of inactivity, the LabelValue is removed from the metric.
}

func (lv *LabelValue) String() string {
	return fmt.Sprintf("LabelValue: %s %s", lv.Labels, lv.Value)
}

// Metric is an object that describes a metric, with its name, the creator and
// owner program name, its Kind, a sequence of Keys that may be used to
// add dimension to the metric, and a list of LabelValues that contain data for
// labels in each dimension of the Keys.
type Metric struct {
	sync.RWMutex
	Name        string // Name
	Program     string // Instantiating program
	Kind        Kind
	Type        datum.Type
	Hidden      bool          `json:",omitempty"`
	Keys        []string      `json:",omitempty"`
	LabelValues []*LabelValue `json:",omitempty"`
	Source      string        `json:"-"`
}

// NewMetric returns a new empty metric of dimension len(keys).
func NewMetric(name string, prog string, kind Kind, typ datum.Type, keys ...string) *Metric {
	m := newMetric(len(keys))
	m.Name = name
	m.Program = prog
	m.Kind = kind
	m.Type = typ
	copy(m.Keys, keys)
	return m
}

// newMetric returns a new empty Metric
func newMetric(len int) *Metric {
	return &Metric{Keys: make([]string, len),
		LabelValues: make([]*LabelValue, 0)}
}

func (m *Metric) findLabelValueOrNil(labelvalues []string) *LabelValue {
Loop:
	for i, lv := range m.LabelValues {
		for j := 0; j < len(lv.Labels); j++ {
			if lv.Labels[j] != labelvalues[j] {
				continue Loop
			}
		}
		return m.LabelValues[i]
	}
	return nil
}

// GetDatum returns the datum named by a sequence of string label values from a
// Metric.  If the sequence of label values does not yet exist, it is created.
func (m *Metric) GetDatum(labelvalues ...string) (d datum.Datum, err error) {
	if len(labelvalues) != len(m.Keys) {
		return nil, errors.Errorf("Label values requested (%q) not same length as keys for metric %q", labelvalues, m)
	}
	m.Lock()
	defer m.Unlock()
	if lv := m.findLabelValueOrNil(labelvalues); lv != nil {
		d = lv.Value
	} else {
		switch m.Type {
		case datum.Int:
			d = datum.NewInt()
		case datum.Float:
			d = datum.NewFloat()
		case datum.String:
			d = datum.NewString()
		}
		m.LabelValues = append(m.LabelValues, &LabelValue{Labels: labelvalues, Value: d})
	}
	return d, nil
}

// RemoveDatum removes the Datum described by labelvalues from the Metric m.
func (m *Metric) RemoveDatum(labelvalues ...string) error {
	if len(labelvalues) != len(m.Keys) {
		return errors.Errorf("Label values requested (%q) not same length as keys for metric %q", labelvalues, m)
	}
	m.Lock()
	defer m.Unlock()
Loop:
	for i, lv := range m.LabelValues {
		for j := 0; j < len(lv.Labels); j++ {
			if lv.Labels[j] != labelvalues[j] {
				continue Loop
			}
		}
		// remove from the slice
		m.LabelValues = append(m.LabelValues[:i], m.LabelValues[i+1:]...)
	}
	return nil
}

// LabelSet is an object that maps the keys of a Metric to the labels naming a
// Datum, for use when enumerating Datums from a Metric.
type LabelSet struct {
	Labels map[string]string
	Datum  datum.Datum
}

func zip(keys []string, values []string) map[string]string {
	r := make(map[string]string)
	for i, v := range values {
		r[keys[i]] = v
	}
	return r
}

// EmitLabelSets enumerates the LabelSets corresponding to the LabelValues of a
// Metric.  It emits them onto the provided channel, then closes the channel to
// signal completion.
func (m *Metric) EmitLabelSets(c chan *LabelSet) {
	for _, lv := range m.LabelValues {
		ls := &LabelSet{zip(m.Keys, lv.Labels), lv.Value}
		c <- ls
	}
	close(c)
}

// UnmarshalJSON converts a JSON byte string into a LabelValue
func (lv *LabelValue) UnmarshalJSON(b []byte) error {
	var obj map[string]*json.RawMessage
	err := json.Unmarshal(b, &obj)
	if err != nil {
		return err
	}

	labels := make([]string, 0)
	if _, ok := obj["Labels"]; ok {
		err = json.Unmarshal(*obj["Labels"], &labels)
		if err != nil {
			return err
		}
	}
	lv.Labels = labels

	var valObj map[string]*json.RawMessage
	err = json.Unmarshal(*obj["Value"], &valObj)
	if err != nil {
		return err
	}
	var t int64
	err = json.Unmarshal(*valObj["Time"], &t)
	if err != nil {
		return err
	}
	var i int64
	err = json.Unmarshal(*valObj["Value"], &i)
	if err != nil {
		return err
	}
	lv.Value = datum.MakeInt(i, time.Unix(t/1e9, t%1e9))
	return nil
}

func (m *Metric) String() string {
	m.RLock()
	defer m.RUnlock()
	return fmt.Sprintf("Metric: name=%s program=%s kind=%s type=%s hidden=%v keys=%v labelvalues=%v source=%s", m.Name, m.Program, m.Kind, m.Type, m.Hidden, m.Keys, m.LabelValues, m.Source)
}

// SetSource sets the source of a metric, describing where in user programmes it was defined.
func (m *Metric) SetSource(source string) {
	m.Lock()
	defer m.Unlock()
	m.Source = source
}
<commit_msg>Don't emit Expiry in JSON.<commit_after>// Copyright 2011 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

// Package metrics provides storage for metrics being recorded by mtail
// programs.
package metrics

import (
	"encoding/json"
	"fmt"
	"sync"
	"time"

	"github.com/google/mtail/metrics/datum"
	"github.com/pkg/errors"
)

// Kind enumerates the types of metrics supported.
type Kind int

const (
	_ Kind = iota

	// Counter is a monotonically nondecreasing metric.
	Counter

	// Gauge is a Kind that can take on any value, and may be set
	// discontinuously from its previous value.
	Gauge

	// Timer is a specialisation of Gauge that can be used to store time
	// intervals, such as latency and durations.  It enables certain behaviour
	// in exporters that handle time intervals such as StatsD.
	Timer

	// Text is a special metric type for free text, usually for operating as a 'hidden' metric, as often these values cannot be exported.
	Text
)

const (
	// Int indicates this metric is an integer metric type.
	Int = datum.Int
	// Float indicates this metric is a floating-point metric type.
	Float = datum.Float
	// String indicates this metric contains string values
	String = datum.String
)

func (m Kind) String() string {
	switch m {
	case Counter:
		return "Counter"
	case Gauge:
		return "Gauge"
	case Timer:
		return "Timer"
	case Text:
		return "Text"
	}
	return "Unknown"
}

// LabelValue is an object that names a Datum value with a list of label
// strings.
type LabelValue struct {
	Labels []string `json:",omitempty"`
	Value  datum.Datum
	// After this time of inactivity, the LabelValue is removed from the metric.
	Expiry time.Duration `json:",omitempty"`
}

func (lv *LabelValue) String() string {
	return fmt.Sprintf("LabelValue: %s %s", lv.Labels, lv.Value)
}

// Metric is an object that describes a metric, with its name, the creator and
// owner program name, its Kind, a sequence of Keys that may be used to
// add dimension to the metric, and a list of LabelValues that contain data for
// labels in each dimension of the Keys.
type Metric struct {
	sync.RWMutex
	Name        string // Name
	Program     string // Instantiating program
	Kind        Kind
	Type        datum.Type
	Hidden      bool          `json:",omitempty"`
	Keys        []string      `json:",omitempty"`
	LabelValues []*LabelValue `json:",omitempty"`
	Source      string        `json:"-"`
}

// NewMetric returns a new empty metric of dimension len(keys).
func NewMetric(name string, prog string, kind Kind, typ datum.Type, keys ...string) *Metric {
	m := newMetric(len(keys))
	m.Name = name
	m.Program = prog
	m.Kind = kind
	m.Type = typ
	copy(m.Keys, keys)
	return m
}

// newMetric returns a new empty Metric
func newMetric(len int) *Metric {
	return &Metric{Keys: make([]string, len),
		LabelValues: make([]*LabelValue, 0)}
}

func (m *Metric) findLabelValueOrNil(labelvalues []string) *LabelValue {
Loop:
	for i, lv := range m.LabelValues {
		for j := 0; j < len(lv.Labels); j++ {
			if lv.Labels[j] != labelvalues[j] {
				continue Loop
			}
		}
		return m.LabelValues[i]
	}
	return nil
}

// GetDatum returns the datum named by a sequence of string label values from a
// Metric.  If the sequence of label values does not yet exist, it is created.
func (m *Metric) GetDatum(labelvalues ...string) (d datum.Datum, err error) {
	if len(labelvalues) != len(m.Keys) {
		return nil, errors.Errorf("Label values requested (%q) not same length as keys for metric %q", labelvalues, m)
	}
	m.Lock()
	defer m.Unlock()
	if lv := m.findLabelValueOrNil(labelvalues); lv != nil {
		d = lv.Value
	} else {
		switch m.Type {
		case datum.Int:
			d = datum.NewInt()
		case datum.Float:
			d = datum.NewFloat()
		case datum.String:
			d = datum.NewString()
		}
		m.LabelValues = append(m.LabelValues, &LabelValue{Labels: labelvalues, Value: d})
	}
	return d, nil
}

// RemoveDatum removes the Datum described by labelvalues from the Metric m.
func (m *Metric) RemoveDatum(labelvalues ...string) error {
	if len(labelvalues) != len(m.Keys) {
		return errors.Errorf("Label values requested (%q) not same length as keys for metric %q", labelvalues, m)
	}
	m.Lock()
	defer m.Unlock()
Loop:
	for i, lv := range m.LabelValues {
		for j := 0; j < len(lv.Labels); j++ {
			if lv.Labels[j] != labelvalues[j] {
				continue Loop
			}
		}
		// remove from the slice
		m.LabelValues = append(m.LabelValues[:i], m.LabelValues[i+1:]...)
	}
	return nil
}

// LabelSet is an object that maps the keys of a Metric to the labels naming a
// Datum, for use when enumerating Datums from a Metric.
type LabelSet struct {
	Labels map[string]string
	Datum  datum.Datum
}

func zip(keys []string, values []string) map[string]string {
	r := make(map[string]string)
	for i, v := range values {
		r[keys[i]] = v
	}
	return r
}

// EmitLabelSets enumerates the LabelSets corresponding to the LabelValues of a
// Metric.  It emits them onto the provided channel, then closes the channel to
// signal completion.
func (m *Metric) EmitLabelSets(c chan *LabelSet) {
	for _, lv := range m.LabelValues {
		ls := &LabelSet{zip(m.Keys, lv.Labels), lv.Value}
		c <- ls
	}
	close(c)
}

// UnmarshalJSON converts a JSON byte string into a LabelValue
func (lv *LabelValue) UnmarshalJSON(b []byte) error {
	var obj map[string]*json.RawMessage
	err := json.Unmarshal(b, &obj)
	if err != nil {
		return err
	}

	labels := make([]string, 0)
	if _, ok := obj["Labels"]; ok {
		err = json.Unmarshal(*obj["Labels"], &labels)
		if err != nil {
			return err
		}
	}
	lv.Labels = labels

	var valObj map[string]*json.RawMessage
	err = json.Unmarshal(*obj["Value"], &valObj)
	if err != nil {
		return err
	}
	var t int64
	err = json.Unmarshal(*valObj["Time"], &t)
	if err != nil {
		return err
	}
	var i int64
	err = json.Unmarshal(*valObj["Value"], &i)
	if err != nil {
		return err
	}
	lv.Value = datum.MakeInt(i, time.Unix(t/1e9, t%1e9))
	return nil
}

func (m *Metric) String() string {
	m.RLock()
	defer m.RUnlock()
	return fmt.Sprintf("Metric: name=%s program=%s kind=%s type=%s hidden=%v keys=%v labelvalues=%v source=%s", m.Name, m.Program, m.Kind, m.Type, m.Hidden, m.Keys, m.LabelValues, m.Source)
}

// SetSource sets the source of a metric, describing where in user programmes it was defined.
func (m *Metric) SetSource(source string) {
	m.Lock()
	defer m.Unlock()
	m.Source = source
}
<|endoftext|>


<commit_before>package microview

import (
	"bytes"
	"testing"
)

func TestClear(t *testing.T) {
	for _, tt := range []struct {
		name string
		mode uint8
		want []byte
	}{
		{"ALL", ALL, []byte("0,1")},
		{"PAGE", PAGE, []byte("0,0")},
	} {
		t.Run(tt.name, func(t *testing.T) {
			if got := Clear(tt.mode); !bytes.Equal(got, tt.want) {
				t.Fatalf("Clear(%s) = %q, want %q", tt.name, got, tt.want)
			}
		})
	}
}
<commit_msg>Test NewMicroView<commit_after>package microview

import (
	"bytes"
	"image"
	"io"
	"io/ioutil"
	"testing"
)

func TestNewMicroView(t *testing.T) {
	buf := bytes.NewBufferString("")

	mv := NewMicroView(struct {
		io.ReadWriter
		io.Closer
	}{buf, ioutil.NopCloser(nil)}, Delay(0))
	defer mv.Close()

	if got, want := mv.Bounds(), image.Rect(0, 0, 64, 48); !got.Eq(want) {
		t.Fatalf("m.Bounds() = %v, want %v", got, want)
	}

	mv.Run(Rect(5, 10, 15, 20))

	if got, want := buf.String(), "9,5,10,15,20"; got != want {
		t.Fatalf("buf.String() = %q, want %q", got, want)
	}
}

func TestClear(t *testing.T) {
	for _, tt := range []struct {
		name string
		mode uint8
		want []byte
	}{
		{"ALL", ALL, []byte("0,1")},
		{"PAGE", PAGE, []byte("0,0")},
	} {
		t.Run(tt.name, func(t *testing.T) {
			if got := Clear(tt.mode); !bytes.Equal(got, tt.want) {
				t.Fatalf("Clear(%s) = %q, want %q", tt.name, got, tt.want)
			}
		})
	}
}
<|endoftext|>