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

package main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
	"time"
	"unicode/utf8"
)

func TestExactly16Bytes(t *testing.T) {
	var tests = []string{
		"",
		"a",
		"日本語",
		"1234567890123456",
		"12345678901234567890",
		"1234567890123本語4567890",
		"12345678901234日本語567890",
		"123456789012345日本語67890",
		"1234567890123456日本語7890",
		"1234567890123456日本語7日本語890",
	}
	for _, str := range tests {
		got := exactly16Bytes(str)
		if len(got) != 16 {
			t.Errorf("exactly16Bytes(%q) is %q, length %d", str, got, len(got))
		}
		// Make sure it is full runes.
		for _, c := range got {
			if c == utf8.RuneError {
				t.Errorf("exactly16Bytes(%q) is %q, has partial rune", str, got)
			}
		}
	}
}

// tmpDir creates a temporary directory and returns its name.
func tmpDir(t *testing.T) string {
	name, err := ioutil.TempDir("", "pack")
	if err != nil {
		t.Fatal(err)
	}
	return name
}

// Test that we can create an archive, write to it, and get the same contents back.
// Tests the rv and then the pv command on a new archive.
func TestCreate(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	name := filepath.Join(dir, "pack.a")
	ar := archive(name, os.O_RDWR, nil)
	// Add an entry by hand.
	ar.addFile(helloFile.Reset())
	ar.fd.Close()
	// Now check it.
	ar = archive(name, os.O_RDONLY, []string{helloFile.name})
	var buf bytes.Buffer
	stdout = &buf
	verbose = true
	defer func() {
		stdout = os.Stdout
		verbose = false
	}()
	ar.scan(ar.printContents)
	ar.fd.Close()
	result := buf.String()
	// Expect verbose output plus file contents.
	expect := fmt.Sprintf("%s\n%s", helloFile.name, helloFile.contents)
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
}

// Test that we can create an archive, put some files in it, and get back a correct listing.
// Tests the tv command.
func TestTableOfContents(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	name := filepath.Join(dir, "pack.a")
	ar := archive(name, os.O_RDWR, nil)
	// Add some entries by hand.
	ar.addFile(helloFile.Reset())
	ar.addFile(goodbyeFile.Reset())
	ar.fd.Close()
	// Now print it.
	ar = archive(name, os.O_RDONLY, nil)
	var buf bytes.Buffer
	stdout = &buf
	verbose = true
	defer func() {
		stdout = os.Stdout
		verbose = false
	}()
	ar.scan(ar.tableOfContents)
	ar.fd.Close()
	result := buf.String()
	// Expect verbose listing.
	expect := fmt.Sprintf("%s\n%s\n", helloFile.Entry(), goodbyeFile.Entry())
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
	// Do it again without verbose.
	verbose = false
	buf.Reset()
	ar = archive(name, os.O_RDONLY, nil)
	ar.scan(ar.tableOfContents)
	ar.fd.Close()
	result = buf.String()
	// Expect non-verbose listing.
	expect = fmt.Sprintf("%s\n%s\n", helloFile.name, goodbyeFile.name)
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
}

// Test that we can create an archive, put some files in it, and get back a file.
// Tests the x command.
func TestExtract(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	name := filepath.Join(dir, "pack.a")
	ar := archive(name, os.O_RDWR, nil)
	// Add some entries by hand.
	ar.addFile(helloFile.Reset())
	ar.addFile(goodbyeFile.Reset())
	ar.fd.Close()
	// Now extract one file. We chdir to the directory of the archive for simplicity.
	pwd, err := os.Getwd()
	if err != nil {
		t.Fatal("os.Getwd: ", err)
	}
	err = os.Chdir(dir)
	if err != nil {
		t.Fatal("os.Chdir: ", err)
	}
	defer func() {
		err := os.Chdir(pwd)
		if err != nil {
			t.Fatal("os.Chdir: ", err)
		}
	}()
	ar = archive(name, os.O_RDONLY, []string{goodbyeFile.name})
	ar.scan(ar.extractContents)
	ar.fd.Close()
	data, err := ioutil.ReadFile(goodbyeFile.name)
	if err != nil {
		t.Fatal(err)
	}
	// Expect contents of file.
	result := string(data)
	expect := goodbyeFile.contents
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
}

// Test that pack-created archives can be understood by the tools.
func TestHello(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	hello := filepath.Join(dir, "hello.go")
	prog := `
		package main
		func main() {
			println("hello world")
		}
	`
	err := ioutil.WriteFile(hello, []byte(prog), 0666)
	if err != nil {
		t.Fatal(err)
	}

	run := func(args ...string) string {
		cmd := exec.Command(args[0], args[1:]...)
		cmd.Dir = dir
		out, err := cmd.CombinedOutput()
		if err != nil {
			t.Fatalf("%v: %v\n%s", args, err, string(out))
		}
		return string(out)
	}

	out := run("go", "env")
	i := strings.Index(out, "GOCHAR=\"")
	if i < 0 {
		t.Fatal("cannot find GOCHAR in 'go env' output")
	}
	char := out[i+8 : i+9]
	run("go", "build", "cmd/pack") // writes pack binary to dir
	run("go", "tool", char+"g", "hello.go")
	run("./pack", "grc", "hello.a", "hello."+char)
	run("go", "tool", char+"l", "-o", "a.out", "hello.a")
	out = run("./a.out")
	if out != "hello world\n" {
		t.Fatal("incorrect output: %q, want %q", out, "hello world\n")
	}
}

// Fake implementation of files.

var helloFile = &FakeFile{
	name:     "hello",
	contents: "hello world", // 11 bytes, an odd number.
	mode:     0644,
}

var goodbyeFile = &FakeFile{
	name:     "goodbye",
	contents: "Sayonara, Jim", // 13 bytes, another odd number.
	mode:     0644,
}

// FakeFile implements FileLike and also os.FileInfo.
type FakeFile struct {
	name     string
	contents string
	mode     os.FileMode
	offset   int
}

// Reset prepares a FakeFile for reuse.
func (f *FakeFile) Reset() *FakeFile {
	f.offset = 0
	return f
}

// FileLike methods.

func (f *FakeFile) Name() string {
	// A bit of a cheat: we only have a basename, so that's also ok for FileInfo.
	return f.name
}

func (f *FakeFile) Stat() (os.FileInfo, error) {
	return f, nil
}

func (f *FakeFile) Read(p []byte) (int, error) {
	if f.offset >= len(f.contents) {
		return 0, io.EOF
	}
	n := copy(p, f.contents[f.offset:])
	f.offset += n
	return n, nil
}

func (f *FakeFile) Close() error {
	return nil
}

// os.FileInfo methods.

func (f *FakeFile) Size() int64 {
	return int64(len(f.contents))
}

func (f *FakeFile) Mode() os.FileMode {
	return f.mode
}

func (f *FakeFile) ModTime() time.Time {
	return time.Time{}
}

func (f *FakeFile) IsDir() bool {
	return false
}

func (f *FakeFile) Sys() interface{} {
	return nil
}

// Special helpers.

func (f *FakeFile) Entry() *Entry {
	return &Entry{
		name:  f.name,
		mtime: 0, // Defined to be zero.
		uid:   0, // Ditto.
		gid:   0, // Ditto.
		mode:  f.mode,
		size:  int64(len(f.contents)),
	}
}
<commit_msg>cmd/pack: dump output of command of "go env" command in test Get more information to help understand build failure on Plan 9. Also Windows. (TestHello is failing because GOCHAR does not appear in output. What does?)<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
	"time"
	"unicode/utf8"
)

func TestExactly16Bytes(t *testing.T) {
	var tests = []string{
		"",
		"a",
		"日本語",
		"1234567890123456",
		"12345678901234567890",
		"1234567890123本語4567890",
		"12345678901234日本語567890",
		"123456789012345日本語67890",
		"1234567890123456日本語7890",
		"1234567890123456日本語7日本語890",
	}
	for _, str := range tests {
		got := exactly16Bytes(str)
		if len(got) != 16 {
			t.Errorf("exactly16Bytes(%q) is %q, length %d", str, got, len(got))
		}
		// Make sure it is full runes.
		for _, c := range got {
			if c == utf8.RuneError {
				t.Errorf("exactly16Bytes(%q) is %q, has partial rune", str, got)
			}
		}
	}
}

// tmpDir creates a temporary directory and returns its name.
func tmpDir(t *testing.T) string {
	name, err := ioutil.TempDir("", "pack")
	if err != nil {
		t.Fatal(err)
	}
	return name
}

// Test that we can create an archive, write to it, and get the same contents back.
// Tests the rv and then the pv command on a new archive.
func TestCreate(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	name := filepath.Join(dir, "pack.a")
	ar := archive(name, os.O_RDWR, nil)
	// Add an entry by hand.
	ar.addFile(helloFile.Reset())
	ar.fd.Close()
	// Now check it.
	ar = archive(name, os.O_RDONLY, []string{helloFile.name})
	var buf bytes.Buffer
	stdout = &buf
	verbose = true
	defer func() {
		stdout = os.Stdout
		verbose = false
	}()
	ar.scan(ar.printContents)
	ar.fd.Close()
	result := buf.String()
	// Expect verbose output plus file contents.
	expect := fmt.Sprintf("%s\n%s", helloFile.name, helloFile.contents)
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
}

// Test that we can create an archive, put some files in it, and get back a correct listing.
// Tests the tv command.
func TestTableOfContents(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	name := filepath.Join(dir, "pack.a")
	ar := archive(name, os.O_RDWR, nil)
	// Add some entries by hand.
	ar.addFile(helloFile.Reset())
	ar.addFile(goodbyeFile.Reset())
	ar.fd.Close()
	// Now print it.
	ar = archive(name, os.O_RDONLY, nil)
	var buf bytes.Buffer
	stdout = &buf
	verbose = true
	defer func() {
		stdout = os.Stdout
		verbose = false
	}()
	ar.scan(ar.tableOfContents)
	ar.fd.Close()
	result := buf.String()
	// Expect verbose listing.
	expect := fmt.Sprintf("%s\n%s\n", helloFile.Entry(), goodbyeFile.Entry())
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
	// Do it again without verbose.
	verbose = false
	buf.Reset()
	ar = archive(name, os.O_RDONLY, nil)
	ar.scan(ar.tableOfContents)
	ar.fd.Close()
	result = buf.String()
	// Expect non-verbose listing.
	expect = fmt.Sprintf("%s\n%s\n", helloFile.name, goodbyeFile.name)
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
}

// Test that we can create an archive, put some files in it, and get back a file.
// Tests the x command.
func TestExtract(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	name := filepath.Join(dir, "pack.a")
	ar := archive(name, os.O_RDWR, nil)
	// Add some entries by hand.
	ar.addFile(helloFile.Reset())
	ar.addFile(goodbyeFile.Reset())
	ar.fd.Close()
	// Now extract one file. We chdir to the directory of the archive for simplicity.
	pwd, err := os.Getwd()
	if err != nil {
		t.Fatal("os.Getwd: ", err)
	}
	err = os.Chdir(dir)
	if err != nil {
		t.Fatal("os.Chdir: ", err)
	}
	defer func() {
		err := os.Chdir(pwd)
		if err != nil {
			t.Fatal("os.Chdir: ", err)
		}
	}()
	ar = archive(name, os.O_RDONLY, []string{goodbyeFile.name})
	ar.scan(ar.extractContents)
	ar.fd.Close()
	data, err := ioutil.ReadFile(goodbyeFile.name)
	if err != nil {
		t.Fatal(err)
	}
	// Expect contents of file.
	result := string(data)
	expect := goodbyeFile.contents
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
}

// Test that pack-created archives can be understood by the tools.
func TestHello(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	hello := filepath.Join(dir, "hello.go")
	prog := `
		package main
		func main() {
			println("hello world")
		}
	`
	err := ioutil.WriteFile(hello, []byte(prog), 0666)
	if err != nil {
		t.Fatal(err)
	}

	run := func(args ...string) string {
		cmd := exec.Command(args[0], args[1:]...)
		cmd.Dir = dir
		out, err := cmd.CombinedOutput()
		if err != nil {
			t.Fatalf("%v: %v\n%s", args, err, string(out))
		}
		return string(out)
	}

	out := run("go", "env")
	i := strings.Index(out, "GOCHAR=\"")
	if i < 0 {
		t.Fatal("cannot find GOCHAR in 'go env' output:\n", out)
	}
	char := out[i+8 : i+9]
	run("go", "build", "cmd/pack") // writes pack binary to dir
	run("go", "tool", char+"g", "hello.go")
	run("./pack", "grc", "hello.a", "hello."+char)
	run("go", "tool", char+"l", "-o", "a.out", "hello.a")
	out = run("./a.out")
	if out != "hello world\n" {
		t.Fatal("incorrect output: %q, want %q", out, "hello world\n")
	}
}

// Fake implementation of files.

var helloFile = &FakeFile{
	name:     "hello",
	contents: "hello world", // 11 bytes, an odd number.
	mode:     0644,
}

var goodbyeFile = &FakeFile{
	name:     "goodbye",
	contents: "Sayonara, Jim", // 13 bytes, another odd number.
	mode:     0644,
}

// FakeFile implements FileLike and also os.FileInfo.
type FakeFile struct {
	name     string
	contents string
	mode     os.FileMode
	offset   int
}

// Reset prepares a FakeFile for reuse.
func (f *FakeFile) Reset() *FakeFile {
	f.offset = 0
	return f
}

// FileLike methods.

func (f *FakeFile) Name() string {
	// A bit of a cheat: we only have a basename, so that's also ok for FileInfo.
	return f.name
}

func (f *FakeFile) Stat() (os.FileInfo, error) {
	return f, nil
}

func (f *FakeFile) Read(p []byte) (int, error) {
	if f.offset >= len(f.contents) {
		return 0, io.EOF
	}
	n := copy(p, f.contents[f.offset:])
	f.offset += n
	return n, nil
}

func (f *FakeFile) Close() error {
	return nil
}

// os.FileInfo methods.

func (f *FakeFile) Size() int64 {
	return int64(len(f.contents))
}

func (f *FakeFile) Mode() os.FileMode {
	return f.mode
}

func (f *FakeFile) ModTime() time.Time {
	return time.Time{}
}

func (f *FakeFile) IsDir() bool {
	return false
}

func (f *FakeFile) Sys() interface{} {
	return nil
}

// Special helpers.

func (f *FakeFile) Entry() *Entry {
	return &Entry{
		name:  f.name,
		mtime: 0, // Defined to be zero.
		uid:   0, // Ditto.
		gid:   0, // Ditto.
		mode:  f.mode,
		size:  int64(len(f.contents)),
	}
}
<|endoftext|>


<commit_before>package pool

import (
	"errors"
	"fmt"
	"net"
	"sync"
)

// channelPool implements the Pool interface based on buffered channels.
type channelPool struct {
	// storage for our net.Conn connections
	mu    sync.Mutex
	conns chan net.Conn

	// net.Conn generator
	factory Factory
}

// Factory is a function to create new connections.
type Factory func() (net.Conn, error)

// NewChannelPool returns a new pool based on buffered channels with an initial
// capacity and maximum capacity. Factory is used when initial capacity is
// greater than zero to fill the pool. A zero initialCap doesn't fill the Pool
// until a new Get() is called. During a Get(), If there is no new connection
// available in the pool, a new connection will be created via the Factory()
// method.
func NewChannelPool(initialCap, maxCap int, factory Factory) (Pool, error) {
	if initialCap < 0 || maxCap <= 0 || initialCap > maxCap {
		return nil, errors.New("invalid capacity settings")
	}

	c := &channelPool{
		conns:   make(chan net.Conn, maxCap),
		factory: factory,
	}

	// create initial connections, if something goes wrong,
	// just close the pool error out.
	for i := 0; i < initialCap; i++ {
		conn, err := factory()
		if err != nil {
			c.Close()
			return nil, fmt.Errorf("factory is not able to fill the pool: %s", err)
		}
		c.conns <- conn
	}

	return c, nil
}

func (c *channelPool) getConnsAndFactory() (chan net.Conn, Factory) {
	c.mu.Lock()
	conns := c.conns
	factory := c.factory
	c.mu.Unlock()
	return conns, factory
}

// Get implements the Pool interfaces Get() method. If there is no new
// connection available in the pool, a new connection will be created via the
// Factory() method.
func (c *channelPool) Get() (net.Conn, error) {
	conns, factory := c.getConnsAndFactory()
	if conns == nil {
		return nil, ErrClosed
	}

	// wrap our connections with out custom net.Conn implementation (wrapConn
	// method) that puts the connection back to the pool if it's closed.
	select {
	case conn := <-conns:
		if conn == nil {
			return nil, ErrClosed
		}

		return c.wrapConn(conn), nil
	default:
		conn, err := factory()
		if err != nil {
			return nil, err
		}

		return c.wrapConn(conn), nil
	}
}

// put puts the connection back to the pool. If the pool is full or closed,
// conn is simply closed. A nil conn will be rejected.
func (c *channelPool) put(conn net.Conn) error {
	if conn == nil {
		return errors.New("connection is nil. rejecting")
	}

	c.mu.Lock()
	defer c.mu.Unlock()

	if c.conns == nil {
		// pool is closed, close passed connection
		return conn.Close()
	}

	// put the resource back into the pool. If the pool is full, this will
	// block and the default case will be executed.
	select {
	case c.conns <- conn:
		return nil
	default:
		// pool is full, close passed connection
		return conn.Close()
	}
}

func (c *channelPool) Close() {
	c.mu.Lock()
	conns := c.conns
	c.conns = nil
	c.factory = nil
	c.mu.Unlock()

	if conns == nil {
		return
	}

	close(conns)
	for conn := range conns {
		conn.Close()
	}
}

func (c *channelPool) Len() int {
	conns, _ := c.getConnsAndFactory()
	return len(conns)
}
<commit_msg>using sync.RWMutex instead of sync.Mutex in channelPool.<commit_after>package pool

import (
	"errors"
	"fmt"
	"net"
	"sync"
)

// channelPool implements the Pool interface based on buffered channels.
type channelPool struct {
	// storage for our net.Conn connections
	mu    sync.RWMutex
	conns chan net.Conn

	// net.Conn generator
	factory Factory
}

// Factory is a function to create new connections.
type Factory func() (net.Conn, error)

// NewChannelPool returns a new pool based on buffered channels with an initial
// capacity and maximum capacity. Factory is used when initial capacity is
// greater than zero to fill the pool. A zero initialCap doesn't fill the Pool
// until a new Get() is called. During a Get(), If there is no new connection
// available in the pool, a new connection will be created via the Factory()
// method.
func NewChannelPool(initialCap, maxCap int, factory Factory) (Pool, error) {
	if initialCap < 0 || maxCap <= 0 || initialCap > maxCap {
		return nil, errors.New("invalid capacity settings")
	}

	c := &channelPool{
		conns:   make(chan net.Conn, maxCap),
		factory: factory,
	}

	// create initial connections, if something goes wrong,
	// just close the pool error out.
	for i := 0; i < initialCap; i++ {
		conn, err := factory()
		if err != nil {
			c.Close()
			return nil, fmt.Errorf("factory is not able to fill the pool: %s", err)
		}
		c.conns <- conn
	}

	return c, nil
}

func (c *channelPool) getConnsAndFactory() (chan net.Conn, Factory) {
	c.mu.RLock()
	conns := c.conns
	factory := c.factory
	c.mu.RUnlock()
	return conns, factory
}

// Get implements the Pool interfaces Get() method. If there is no new
// connection available in the pool, a new connection will be created via the
// Factory() method.
func (c *channelPool) Get() (net.Conn, error) {
	conns, factory := c.getConnsAndFactory()
	if conns == nil {
		return nil, ErrClosed
	}

	// wrap our connections with out custom net.Conn implementation (wrapConn
	// method) that puts the connection back to the pool if it's closed.
	select {
	case conn := <-conns:
		if conn == nil {
			return nil, ErrClosed
		}

		return c.wrapConn(conn), nil
	default:
		conn, err := factory()
		if err != nil {
			return nil, err
		}

		return c.wrapConn(conn), nil
	}
}

// put puts the connection back to the pool. If the pool is full or closed,
// conn is simply closed. A nil conn will be rejected.
func (c *channelPool) put(conn net.Conn) error {
	if conn == nil {
		return errors.New("connection is nil. rejecting")
	}

	c.mu.RLock()
	defer c.mu.RUnlock()

	if c.conns == nil {
		// pool is closed, close passed connection
		return conn.Close()
	}

	// put the resource back into the pool. If the pool is full, this will
	// block and the default case will be executed.
	select {
	case c.conns <- conn:
		return nil
	default:
		// pool is full, close passed connection
		return conn.Close()
	}
}

func (c *channelPool) Close() {
	c.mu.Lock()
	conns := c.conns
	c.conns = nil
	c.factory = nil
	c.mu.Unlock()

	if conns == nil {
		return
	}

	close(conns)
	for conn := range conns {
		conn.Close()
	}
}

func (c *channelPool) Len() int {
	conns, _ := c.getConnsAndFactory()
	return len(conns)
}
<|endoftext|>


<commit_before>// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
// All rights reserved. Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"os"
	"regexp"
	"strconv"
	"strings"
	"text/template"
)

var output string

type field struct {
	Name      string
	IsBasic   bool
	IsSlice   bool
	IsMap     bool
	FieldType string
	KeyType   string
	Encoder   string
	Convert   string
	Max       int
}

var headerTpl = template.Must(template.New("header").Parse(`package {{.Package}}

import (
	"bytes"
	"io"

	"github.com/calmh/syncthing/xdr"
)
`))

var encodeTpl = template.Must(template.New("encoder").Parse(`
func (o {{.TypeName}}) EncodeXDR(w io.Writer) (int, error) {
	var xw = xdr.NewWriter(w)
	return o.encodeXDR(xw)
}//+n

func (o {{.TypeName}}) MarshalXDR() []byte {
	return o.AppendXDR(make([]byte, 0, 128))
}//+n

func (o {{.TypeName}}) AppendXDR(bs []byte) []byte {
	var aw = xdr.AppendWriter(bs)
	var xw = xdr.NewWriter(&aw)
	o.encodeXDR(xw)
	return []byte(aw)
}//+n

func (o {{.TypeName}}) encodeXDR(xw *xdr.Writer) (int, error) {
	{{range $field := .Fields}}
	{{if not $field.IsSlice}}
		{{if ne $field.Convert ""}}
		xw.Write{{$field.Encoder}}({{$field.Convert}}(o.{{$field.Name}}))
		{{else if $field.IsBasic}}
		{{if ge $field.Max 1}}
		if len(o.{{$field.Name}}) > {{$field.Max}} {
			return xw.Tot(), xdr.ErrElementSizeExceeded
		}
		{{end}}
		xw.Write{{$field.Encoder}}(o.{{$field.Name}})
		{{else}}
		o.{{$field.Name}}.encodeXDR(xw)
		{{end}}
	{{else}}
	{{if ge $field.Max 1}}
	if len(o.{{$field.Name}}) > {{$field.Max}} {
		return xw.Tot(), xdr.ErrElementSizeExceeded
	}
	{{end}}
	xw.WriteUint32(uint32(len(o.{{$field.Name}})))
	for i := range o.{{$field.Name}} {
		{{if ne $field.Convert ""}}
		xw.Write{{$field.Encoder}}({{$field.Convert}}(o.{{$field.Name}}[i]))
		{{else if $field.IsBasic}}
		xw.Write{{$field.Encoder}}(o.{{$field.Name}}[i])
		{{else}}
		o.{{$field.Name}}[i].encodeXDR(xw)
		{{end}}
	}
	{{end}}
	{{end}}
	return xw.Tot(), xw.Error()
}//+n

func (o *{{.TypeName}}) DecodeXDR(r io.Reader) error {
	xr := xdr.NewReader(r)
	return o.decodeXDR(xr)
}//+n

func (o *{{.TypeName}}) UnmarshalXDR(bs []byte) error {
	var br = bytes.NewReader(bs)
	var xr = xdr.NewReader(br)
	return o.decodeXDR(xr)
}//+n

func (o *{{.TypeName}}) decodeXDR(xr *xdr.Reader) error {
	{{range $field := .Fields}}
	{{if not $field.IsSlice}}
		{{if ne $field.Convert ""}}
		o.{{$field.Name}} = {{$field.FieldType}}(xr.Read{{$field.Encoder}}())
		{{else if $field.IsBasic}}
		{{if ge $field.Max 1}}
		o.{{$field.Name}} = xr.Read{{$field.Encoder}}Max({{$field.Max}})
		{{else}}
		o.{{$field.Name}} = xr.Read{{$field.Encoder}}()
		{{end}}
		{{else}}
		(&o.{{$field.Name}}).decodeXDR(xr)
		{{end}}
	{{else}}
	_{{$field.Name}}Size := int(xr.ReadUint32())
	{{if ge $field.Max 1}}
	if _{{$field.Name}}Size > {{$field.Max}} {
		return xdr.ErrElementSizeExceeded
	}
	{{end}}
	o.{{$field.Name}} = make([]{{$field.FieldType}}, _{{$field.Name}}Size)
	for i := range o.{{$field.Name}} {
		{{if ne $field.Convert ""}}
		o.{{$field.Name}}[i] = {{$field.FieldType}}(xr.Read{{$field.Encoder}}())
		{{else if $field.IsBasic}}
		o.{{$field.Name}}[i] = xr.Read{{$field.Encoder}}()
		{{else}}
		(&o.{{$field.Name}}[i]).decodeXDR(xr)
		{{end}}
	}
	{{end}}
	{{end}}
	return xr.Error()
}`))

var maxRe = regexp.MustCompile(`\Wmax:(\d+)`)

type typeSet struct {
	Type    string
	Encoder string
}

var xdrEncoders = map[string]typeSet{
	"int16":  typeSet{"uint16", "Uint16"},
	"uint16": typeSet{"", "Uint16"},
	"int32":  typeSet{"uint32", "Uint32"},
	"uint32": typeSet{"", "Uint32"},
	"int64":  typeSet{"uint64", "Uint64"},
	"uint64": typeSet{"", "Uint64"},
	"int":    typeSet{"uint64", "Uint64"},
	"string": typeSet{"", "String"},
	"[]byte": typeSet{"", "Bytes"},
	"bool":   typeSet{"", "Bool"},
}

func handleStruct(name string, t *ast.StructType) {
	var fs []field
	for _, sf := range t.Fields.List {
		if len(sf.Names) == 0 {
			// We don't handle anonymous fields
			continue
		}

		fn := sf.Names[0].Name
		var max = 0
		if sf.Comment != nil {
			c := sf.Comment.List[0].Text
			if m := maxRe.FindStringSubmatch(c); m != nil {
				max, _ = strconv.Atoi(m[1])
			}
			if strings.Contains(c, "noencode") {
				continue
			}
		}

		var f field
		switch ft := sf.Type.(type) {
		case *ast.Ident:
			tn := ft.Name
			if enc, ok := xdrEncoders[tn]; ok {
				f = field{
					Name:      fn,
					IsBasic:   true,
					FieldType: tn,
					Encoder:   enc.Encoder,
					Convert:   enc.Type,
					Max:       max,
				}
			} else {
				f = field{
					Name:      fn,
					IsBasic:   false,
					FieldType: tn,
					Max:       max,
				}
			}

		case *ast.ArrayType:
			if ft.Len != nil {
				// We don't handle arrays
				continue
			}

			tn := ft.Elt.(*ast.Ident).Name
			if enc, ok := xdrEncoders["[]"+tn]; ok {
				f = field{
					Name:      fn,
					IsBasic:   true,
					FieldType: tn,
					Encoder:   enc.Encoder,
					Convert:   enc.Type,
					Max:       max,
				}
			} else if enc, ok := xdrEncoders[tn]; ok {
				f = field{
					Name:      fn,
					IsBasic:   true,
					IsSlice:   true,
					FieldType: tn,
					Encoder:   enc.Encoder,
					Convert:   enc.Type,
					Max:       max,
				}
			} else {
				f = field{
					Name:      fn,
					IsBasic:   false,
					IsSlice:   true,
					FieldType: tn,
					Max:       max,
				}
			}
		}

		fs = append(fs, f)
	}

	switch output {
	case "code":
		generateCode(name, fs)
	case "diagram":
		generateDiagram(name, fs)
	case "xdr":
		generateXdr(name, fs)
	}
}

func generateCode(name string, fs []field) {
	var buf bytes.Buffer
	err := encodeTpl.Execute(&buf, map[string]interface{}{"TypeName": name, "Fields": fs})
	if err != nil {
		panic(err)
	}

	bs := regexp.MustCompile(`(\s*\n)+`).ReplaceAll(buf.Bytes(), []byte("\n"))
	bs = bytes.Replace(bs, []byte("//+n"), []byte("\n"), -1)

	bs, err = format.Source(bs)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(bs))
}

func generateDiagram(sn string, fs []field) {
	fmt.Println(sn + " Structure:")
	fmt.Println()
	fmt.Println(" 0                   1                   2                   3")
	fmt.Println(" 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1")
	line := "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+"
	fmt.Println(line)

	for _, f := range fs {
		tn := f.FieldType
		sl := f.IsSlice

		if sl {
			fmt.Printf("| %s |\n", center("Number of "+f.Name, 61))
			fmt.Println(line)
		}
		switch tn {
		case "uint16":
			fmt.Printf("| %s | %s |\n", center(f.Name, 29), center("0x0000", 29))
			fmt.Println(line)
		case "uint32":
			fmt.Printf("| %s |\n", center(f.Name, 61))
			fmt.Println(line)
		case "int64", "uint64":
			fmt.Printf("| %-61s |\n", "")
			fmt.Printf("+ %s +\n", center(f.Name+" (64 bits)", 61))
			fmt.Printf("| %-61s |\n", "")
			fmt.Println(line)
		case "string", "byte": // XXX We assume slice of byte!
			fmt.Printf("| %s |\n", center("Length of "+f.Name, 61))
			fmt.Println(line)
			fmt.Printf("/ %61s /\n", "")
			fmt.Printf("\\ %s \\\n", center(f.Name+" (variable length)", 61))
			fmt.Printf("/ %61s /\n", "")
			fmt.Println(line)
		default:
			if sl {
				tn = "Zero or more " + tn + " Structures"
				fmt.Printf("/ %s /\n", center("", 61))
				fmt.Printf("\\ %s \\\n", center(tn, 61))
				fmt.Printf("/ %s /\n", center("", 61))
			} else {
				fmt.Printf("| %s |\n", center(tn, 61))
			}
			fmt.Println(line)
		}
	}
	fmt.Println()
	fmt.Println()
}

func generateXdr(sn string, fs []field) {
	fmt.Printf("struct %s {\n", sn)

	for _, f := range fs {
		tn := f.FieldType
		fn := f.Name
		suf := ""
		if f.IsSlice {
			suf = "<>"
		}

		switch tn {
		case "uint16":
			fmt.Printf("\tunsigned short %s%s;\n", fn, suf)
		case "uint32":
			fmt.Printf("\tunsigned int %s%s;\n", fn, suf)
		case "int64":
			fmt.Printf("\thyper %s%s;\n", fn, suf)
		case "uint64":
			fmt.Printf("\tunsigned hyper %s%s;\n", fn, suf)
		case "string":
			fmt.Printf("\tstring %s<>;\n", fn)
		case "byte":
			fmt.Printf("\topaque %s<>;\n", fn)
		default:
			fmt.Printf("\t%s %s%s;\n", tn, fn, suf)
		}
	}
	fmt.Println("}")
	fmt.Println()
}

func center(s string, w int) string {
	w -= len(s)
	l := w / 2
	r := l
	if l+r < w {
		r++
	}
	return strings.Repeat(" ", l) + s + strings.Repeat(" ", r)
}

func inspector(fset *token.FileSet) func(ast.Node) bool {
	return func(n ast.Node) bool {
		switch n := n.(type) {
		case *ast.TypeSpec:
			switch t := n.Type.(type) {
			case *ast.StructType:
				name := n.Name.Name
				handleStruct(name, t)
			}
			return false
		default:
			return true
		}
	}
}

func main() {
	flag.StringVar(&output, "output", "code", "code,xdr,diagram")
	flag.Parse()
	fname := flag.Arg(0)

	// Create the AST by parsing src.
	fset := token.NewFileSet() // positions are relative to fset
	f, err := parser.ParseFile(fset, fname, nil, parser.ParseComments)
	if err != nil {
		panic(err)
	}

	//ast.Print(fset, f)

	if output == "code" {
		headerTpl.Execute(os.Stdout, map[string]string{"Package": f.Name.Name})
	}

	i := inspector(fset)
	ast.Inspect(f, i)
}
<commit_msg>Slightly clean up XDR generator<commit_after>// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
// All rights reserved. Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"os"
	"regexp"
	"strconv"
	"strings"
	"text/template"
)

type fieldInfo struct {
	Name      string
	IsBasic   bool   // handled by one the native Read/WriteUint64 etc functions
	IsSlice   bool   // field is a slice of FieldType
	FieldType string // original type of field, i.e. "int"
	Encoder   string // the encoder name, i.e. "Uint64" for Read/WriteUint64
	Convert   string // what to convert to when encoding, i.e. "uint64"
	Max       int    // max size for slices and strings
}

type structInfo struct {
	Name   string
	Fields []fieldInfo
}

var headerTpl = template.Must(template.New("header").Parse(`// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
// All rights reserved. Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

// ************************************************************
// This file is automatically generated by genxdr. Do not edit.
// ************************************************************

package {{.Package}}

import (
	"bytes"
	"io"

	"github.com/calmh/syncthing/xdr"
)
`))

var encodeTpl = template.Must(template.New("encoder").Parse(`
func (o {{.TypeName}}) EncodeXDR(w io.Writer) (int, error) {
	var xw = xdr.NewWriter(w)
	return o.encodeXDR(xw)
}//+n

func (o {{.TypeName}}) MarshalXDR() []byte {
	return o.AppendXDR(make([]byte, 0, 128))
}//+n

func (o {{.TypeName}}) AppendXDR(bs []byte) []byte {
	var aw = xdr.AppendWriter(bs)
	var xw = xdr.NewWriter(&aw)
	o.encodeXDR(xw)
	return []byte(aw)
}//+n

func (o {{.TypeName}}) encodeXDR(xw *xdr.Writer) (int, error) {
	{{range $fieldInfo := .Fields}}
		{{if not $fieldInfo.IsSlice}}
			{{if ne $fieldInfo.Convert ""}}
				xw.Write{{$fieldInfo.Encoder}}({{$fieldInfo.Convert}}(o.{{$fieldInfo.Name}}))
			{{else if $fieldInfo.IsBasic}}
				{{if ge $fieldInfo.Max 1}}
					if len(o.{{$fieldInfo.Name}}) > {{$fieldInfo.Max}} {
						return xw.Tot(), xdr.ErrElementSizeExceeded
					}
				{{end}}
				xw.Write{{$fieldInfo.Encoder}}(o.{{$fieldInfo.Name}})
			{{else}}
				o.{{$fieldInfo.Name}}.encodeXDR(xw)
			{{end}}
		{{else}}
			{{if ge $fieldInfo.Max 1}}
				if len(o.{{$fieldInfo.Name}}) > {{$fieldInfo.Max}} {
					return xw.Tot(), xdr.ErrElementSizeExceeded
				}
			{{end}}
			xw.WriteUint32(uint32(len(o.{{$fieldInfo.Name}})))
			for i := range o.{{$fieldInfo.Name}} {
			{{if ne $fieldInfo.Convert ""}}
				xw.Write{{$fieldInfo.Encoder}}({{$fieldInfo.Convert}}(o.{{$fieldInfo.Name}}[i]))
			{{else if $fieldInfo.IsBasic}}
				xw.Write{{$fieldInfo.Encoder}}(o.{{$fieldInfo.Name}}[i])
			{{else}}
				o.{{$fieldInfo.Name}}[i].encodeXDR(xw)
			{{end}}
			}
		{{end}}
	{{end}}
	return xw.Tot(), xw.Error()
}//+n

func (o *{{.TypeName}}) DecodeXDR(r io.Reader) error {
	xr := xdr.NewReader(r)
	return o.decodeXDR(xr)
}//+n

func (o *{{.TypeName}}) UnmarshalXDR(bs []byte) error {
	var br = bytes.NewReader(bs)
	var xr = xdr.NewReader(br)
	return o.decodeXDR(xr)
}//+n

func (o *{{.TypeName}}) decodeXDR(xr *xdr.Reader) error {
	{{range $fieldInfo := .Fields}}
		{{if not $fieldInfo.IsSlice}}
			{{if ne $fieldInfo.Convert ""}}
				o.{{$fieldInfo.Name}} = {{$fieldInfo.FieldType}}(xr.Read{{$fieldInfo.Encoder}}())
			{{else if $fieldInfo.IsBasic}}
				{{if ge $fieldInfo.Max 1}}
					o.{{$fieldInfo.Name}} = xr.Read{{$fieldInfo.Encoder}}Max({{$fieldInfo.Max}})
				{{else}}
					o.{{$fieldInfo.Name}} = xr.Read{{$fieldInfo.Encoder}}()
				{{end}}
			{{else}}
				(&o.{{$fieldInfo.Name}}).decodeXDR(xr)
			{{end}}
		{{else}}
			_{{$fieldInfo.Name}}Size := int(xr.ReadUint32())
			{{if ge $fieldInfo.Max 1}}
				if _{{$fieldInfo.Name}}Size > {{$fieldInfo.Max}} {
					return xdr.ErrElementSizeExceeded
				}
			{{end}}
			o.{{$fieldInfo.Name}} = make([]{{$fieldInfo.FieldType}}, _{{$fieldInfo.Name}}Size)
			for i := range o.{{$fieldInfo.Name}} {
				{{if ne $fieldInfo.Convert ""}}
					o.{{$fieldInfo.Name}}[i] = {{$fieldInfo.FieldType}}(xr.Read{{$fieldInfo.Encoder}}())
				{{else if $fieldInfo.IsBasic}}
					o.{{$fieldInfo.Name}}[i] = xr.Read{{$fieldInfo.Encoder}}()
				{{else}}
					(&o.{{$fieldInfo.Name}}[i]).decodeXDR(xr)
				{{end}}
			}
		{{end}}
	{{end}}
	return xr.Error()
}`))

var maxRe = regexp.MustCompile(`\Wmax:(\d+)`)

type typeSet struct {
	Type    string
	Encoder string
}

var xdrEncoders = map[string]typeSet{
	"int16":  typeSet{"uint16", "Uint16"},
	"uint16": typeSet{"", "Uint16"},
	"int32":  typeSet{"uint32", "Uint32"},
	"uint32": typeSet{"", "Uint32"},
	"int64":  typeSet{"uint64", "Uint64"},
	"uint64": typeSet{"", "Uint64"},
	"int":    typeSet{"uint64", "Uint64"},
	"string": typeSet{"", "String"},
	"[]byte": typeSet{"", "Bytes"},
	"bool":   typeSet{"", "Bool"},
}

func handleStruct(t *ast.StructType) []fieldInfo {
	var fs []fieldInfo

	for _, sf := range t.Fields.List {
		if len(sf.Names) == 0 {
			// We don't handle anonymous fields
			continue
		}

		fn := sf.Names[0].Name
		var max = 0
		if sf.Comment != nil {
			c := sf.Comment.List[0].Text
			if m := maxRe.FindStringSubmatch(c); m != nil {
				max, _ = strconv.Atoi(m[1])
			}
			if strings.Contains(c, "noencode") {
				continue
			}
		}

		var f fieldInfo
		switch ft := sf.Type.(type) {
		case *ast.Ident:
			tn := ft.Name
			if enc, ok := xdrEncoders[tn]; ok {
				f = fieldInfo{
					Name:      fn,
					IsBasic:   true,
					FieldType: tn,
					Encoder:   enc.Encoder,
					Convert:   enc.Type,
					Max:       max,
				}
			} else {
				f = fieldInfo{
					Name:      fn,
					IsBasic:   false,
					FieldType: tn,
					Max:       max,
				}
			}

		case *ast.ArrayType:
			if ft.Len != nil {
				// We don't handle arrays
				continue
			}

			tn := ft.Elt.(*ast.Ident).Name
			if enc, ok := xdrEncoders["[]"+tn]; ok {
				f = fieldInfo{
					Name:      fn,
					IsBasic:   true,
					FieldType: tn,
					Encoder:   enc.Encoder,
					Convert:   enc.Type,
					Max:       max,
				}
			} else if enc, ok := xdrEncoders[tn]; ok {
				f = fieldInfo{
					Name:      fn,
					IsBasic:   true,
					IsSlice:   true,
					FieldType: tn,
					Encoder:   enc.Encoder,
					Convert:   enc.Type,
					Max:       max,
				}
			} else {
				f = fieldInfo{
					Name:      fn,
					IsBasic:   false,
					IsSlice:   true,
					FieldType: tn,
					Max:       max,
				}
			}
		}

		fs = append(fs, f)
	}

	return fs
}

func generateCode(s structInfo) {
	name := s.Name
	fs := s.Fields

	var buf bytes.Buffer
	err := encodeTpl.Execute(&buf, map[string]interface{}{"TypeName": name, "Fields": fs})
	if err != nil {
		panic(err)
	}

	bs := regexp.MustCompile(`(\s*\n)+`).ReplaceAll(buf.Bytes(), []byte("\n"))
	bs = bytes.Replace(bs, []byte("//+n"), []byte("\n"), -1)

	bs, err = format.Source(bs)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(bs))
}

func uncamelize(s string) string {
	return regexp.MustCompile("[a-z][A-Z]").ReplaceAllStringFunc(s, func(camel string) string {
		return camel[:1] + " " + camel[1:]
	})
}

func generateDiagram(s structInfo) {
	sn := s.Name
	fs := s.Fields

	fmt.Println(sn + " Structure:")
	fmt.Println()
	fmt.Println(" 0                   1                   2                   3")
	fmt.Println(" 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1")
	line := "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+"
	fmt.Println(line)

	for _, f := range fs {
		tn := f.FieldType
		sl := f.IsSlice
		name := uncamelize(f.Name)

		if sl {
			fmt.Printf("| %s |\n", center("Number of "+name, 61))
			fmt.Println(line)
		}
		switch tn {
		case "bool":
			fmt.Printf("| %s |V|\n", center(name+" (V=0 or 1)", 59))
			fmt.Println(line)
		case "uint16":
			fmt.Printf("| %s | %s |\n", center("0x0000", 29), center(name, 29))
			fmt.Println(line)
		case "uint32":
			fmt.Printf("| %s |\n", center(name, 61))
			fmt.Println(line)
		case "int64", "uint64":
			fmt.Printf("| %-61s |\n", "")
			fmt.Printf("+ %s +\n", center(name+" (64 bits)", 61))
			fmt.Printf("| %-61s |\n", "")
			fmt.Println(line)
		case "string", "byte": // XXX We assume slice of byte!
			fmt.Printf("| %s |\n", center("Length of "+name, 61))
			fmt.Println(line)
			fmt.Printf("/ %61s /\n", "")
			fmt.Printf("\\ %s \\\n", center(name+" (variable length)", 61))
			fmt.Printf("/ %61s /\n", "")
			fmt.Println(line)
		default:
			if sl {
				tn = "Zero or more " + tn + " Structures"
				fmt.Printf("/ %s /\n", center("", 61))
				fmt.Printf("\\ %s \\\n", center(tn, 61))
				fmt.Printf("/ %s /\n", center("", 61))
			} else {
				fmt.Printf("| %s |\n", center(tn, 61))
			}
			fmt.Println(line)
		}
	}
	fmt.Println()
	fmt.Println()
}

func generateXdr(s structInfo) {
	sn := s.Name
	fs := s.Fields

	fmt.Printf("struct %s {\n", sn)

	for _, f := range fs {
		tn := f.FieldType
		fn := f.Name
		suf := ""
		l := ""
		if f.Max > 0 {
			l = strconv.Itoa(f.Max)
		}
		if f.IsSlice {
			suf = "<" + l + ">"
		}

		switch tn {
		case "uint16", "uint32":
			fmt.Printf("\tunsigned int %s%s;\n", fn, suf)
		case "int64":
			fmt.Printf("\thyper %s%s;\n", fn, suf)
		case "uint64":
			fmt.Printf("\tunsigned hyper %s%s;\n", fn, suf)
		case "string":
			fmt.Printf("\tstring %s<%s>;\n", fn, l)
		case "byte":
			fmt.Printf("\topaque %s<%s>;\n", fn, l)
		default:
			fmt.Printf("\t%s %s%s;\n", tn, fn, suf)
		}
	}
	fmt.Println("}")
	fmt.Println()
}

func center(s string, w int) string {
	w -= len(s)
	l := w / 2
	r := l
	if l+r < w {
		r++
	}
	return strings.Repeat(" ", l) + s + strings.Repeat(" ", r)
}

func inspector(structs *[]structInfo) func(ast.Node) bool {
	return func(n ast.Node) bool {
		switch n := n.(type) {
		case *ast.TypeSpec:
			switch t := n.Type.(type) {
			case *ast.StructType:
				name := n.Name.Name
				fs := handleStruct(t)
				*structs = append(*structs, structInfo{name, fs})
			}
			return false
		default:
			return true
		}
	}
}

func main() {
	flag.Parse()
	fname := flag.Arg(0)

	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, fname, nil, parser.ParseComments)
	if err != nil {
		panic(err)
	}

	var structs []structInfo
	i := inspector(&structs)
	ast.Inspect(f, i)

	headerTpl.Execute(os.Stdout, map[string]string{"Package": f.Name.Name})
	for _, s := range structs {
		fmt.Printf("\n/*\n\n")
		generateDiagram(s)
		generateXdr(s)
		fmt.Printf("*/\n")
		generateCode(s)
	}
}
<|endoftext|>


<commit_before>package HumorChecker // "cirello.io/HumorChecker"

import (
	"bufio"
	"regexp"
	"strings"
)

type sign int

const (
	positive sign = +1
	negative sign = -1
)

//Score is the result of sentiment calculation
type Score struct {
	// Score is the sum of the sentiment points of the analyzed text.
	// Negativity will render negative points only, and vice-versa.
	Score float64

	// Comparative establishes a ratio of sentiment per word
	Comparative float64

	// List of words for a given sentiment.
	Words []string
}

//FullScore is the difference between positive and negative sentiment
type FullScore struct {
	// Score is the difference between positive and negative sentiment
	// scores.
	Score float64

	// Comparative is the difference between positive and negative sentiment
	// comparative scores.
	Comparative float64

	// Positive score object
	Positive Score

	// Negative score object
	Negative Score
}

var lettersAndSpaceOnly = regexp.MustCompile(`[^a-zA-Z ]+`)

func calculateScore(phrase string, calcSign sign) Score {
	var hits float64
	var words []string
	var count int

	scanner := bufio.NewScanner(strings.NewReader(strings.ToLower(lettersAndSpaceOnly.ReplaceAllString(phrase, " "))))
	scanner.Split(bufio.ScanWords)
	for scanner.Scan() {
		count++
		word := scanner.Text()
		if v, ok := afinn[word]; ok {
			if (calcSign == positive && v > 0) || (calcSign == negative && v < 0) {
				hits += v * float64(calcSign)
				words = append(words, word)
			}
		}
	}

	return Score{
		Score:       hits,
		Comparative: hits / float64(count),
		Words:       words,
	}
}

// Negativity calculates the negative sentiment of a sentence
func Negativity(phrase string) Score {
	return calculateScore(phrase, negative)
}

// Positivity calculates the positive sentiment of a sentence
func Positivity(phrase string) Score {
	return calculateScore(phrase, positive)
}

// Analyze calculates overall sentiment
func Analyze(phrase string) FullScore {
	pos := Positivity(phrase)
	neg := Negativity(phrase)

	return FullScore{
		Score:       pos.Score - neg.Score,
		Comparative: pos.Comparative - neg.Comparative,
		Positive:    pos,
		Negative:    neg,
	}
}
<commit_msg>Code Grooming<commit_after>package HumorChecker // "cirello.io/HumorChecker"

import (
	"bufio"
	"regexp"
	"strings"
)

type sign int

const (
	positive sign = +1
	negative sign = -1
)

// Score is the result of sentiment calculation
type Score struct {
	// Score is the sum of the sentiment points of the analyzed text.
	// Negativity will render negative points only, and vice-versa.
	Score float64

	// Comparative establishes a ratio of sentiment per word
	Comparative float64

	// List of words for a given sentiment.
	Words []string
}

// FullScore is the difference between positive and negative sentiment
type FullScore struct {
	// Score is the difference between positive and negative sentiment
	// scores.
	Score float64

	// Comparative is the difference between positive and negative sentiment
	// comparative scores.
	Comparative float64

	// Positive score object
	Positive Score

	// Negative score object
	Negative Score
}

var lettersAndSpaceOnly = regexp.MustCompile(`[^a-zA-Z ]+`)

func calculateScore(phrase string, calcSign sign) Score {
	var hits float64
	var words []string
	var count int

	scanner := bufio.NewScanner(strings.NewReader(strings.ToLower(lettersAndSpaceOnly.ReplaceAllString(phrase, " "))))
	scanner.Split(bufio.ScanWords)
	for scanner.Scan() {
		count++
		word := scanner.Text()
		if v, ok := afinn[word]; ok {
			if (calcSign == positive && v > 0) || (calcSign == negative && v < 0) {
				hits += v * float64(calcSign)
				words = append(words, word)
			}
		}
	}

	return Score{
		Score:       hits,
		Comparative: hits / float64(count),
		Words:       words,
	}
}

// Negativity calculates the negative sentiment of a sentence
func Negativity(phrase string) Score {
	return calculateScore(phrase, negative)
}

// Positivity calculates the positive sentiment of a sentence
func Positivity(phrase string) Score {
	return calculateScore(phrase, positive)
}

// Analyze calculates overall sentiment
func Analyze(phrase string) FullScore {
	pos := Positivity(phrase)
	neg := Negativity(phrase)

	return FullScore{
		Score:       pos.Score - neg.Score,
		Comparative: pos.Comparative - neg.Comparative,
		Positive:    pos,
		Negative:    neg,
	}
}
<|endoftext|>


<commit_before>package provisioner

import (
	"errors"
	"github.com/Sirupsen/logrus"
	"github.com/powerman/rpc-codec/jsonrpc2"
	//"github.com/kubernetes-incubator/external-storage/iscsi/targetd/provisioner/jsonrpc2"
	"github.com/kubernetes-incubator/external-storage/lib/controller"
	"github.com/spf13/viper"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/pkg/api/v1"
	//"net/rpc"
	//"net/rpc/jsonrpc"
	"sort"
	"strings"
)

var log = logrus.New()

type vol_createArgs struct {
	Pool string `json:"pool"`
	Name string `json:"name"`
	Size int64  `json:"size"`
}

type vol_destroyArgs struct {
	Pool string `json:"pool"`
	Name string `json:"name"`
}

type export_createArgs struct {
	Pool          string `json:"pool"`
	Vol           string `json:"vol"`
	Initiator_wwn string `json:"initiator_wwn"`
	Lun           int32  `json:"lun"`
}

type export_destroyArgs struct {
	Pool          string `json:"pool"`
	Vol           string `json:"vol"`
	Initiator_wwn string `json:"initiator_wwn"`
}

type iscsiProvisioner struct {
	targetdURL string
}

type export struct {
	Initiator_wwn string `json:"initiator_wwn"`
	Lun           int32  `json:"lun"`
	Vol_name      string `json:"vol_name"`
	Vol_size      int    `json:"vol_size"`
	Vol_uuid      string `json:"vol_uuid"`
	Pool          string `json:"pool"`
}

type exportList []export

type result int

func NewiscsiProvisioner(url string) controller.Provisioner {

	initLog()

	return &iscsiProvisioner{
		targetdURL: url,
	}
}

// Provision creates a storage asset and returns a PV object representing it.
func (p *iscsiProvisioner) Provision(options controller.VolumeOptions) (*v1.PersistentVolume, error) {
	log.Debugln("new provision request received for pvc: ", options.PVName)
	vol, lun, pool, err := p.createVolume(options)
	if err != nil {
		log.Warnln(err)
		return nil, err
	}
	log.Debugln("volume created with vol and lun: ", vol, lun)

	annotations := make(map[string]string)
	annotations["volume_name"] = vol
	annotations["pool"] = pool
	annotations["initiators"] = options.Parameters["initiators"]
	//	annotations[annExportBlock] = exportBlock
	//	annotations[annExportID] = strconv.FormatUint(uint64(exportID), 10)
	//	annotations[annProjectBlock] = projectBlock
	//	annotations[annProjectID] = strconv.FormatUint(uint64(projectID), 10)
	//	if supGroup != 0 {
	//		annotations[VolumeGidAnnotationKey] = strconv.FormatUint(supGroup, 10)
	//	}
	//	annotations[annProvisionerID] = string(p.identity)

	pv := &v1.PersistentVolume{
		ObjectMeta: metav1.ObjectMeta{
			Name:        options.PVName,
			Labels:      map[string]string{},
			Annotations: annotations,
		},
		Spec: v1.PersistentVolumeSpec{
			PersistentVolumeReclaimPolicy: options.PersistentVolumeReclaimPolicy,
			AccessModes:                   options.PVC.Spec.AccessModes,
			Capacity: v1.ResourceList{
				v1.ResourceName(v1.ResourceStorage): options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)],
			},
			PersistentVolumeSource: v1.PersistentVolumeSource{
				ISCSI: &v1.ISCSIVolumeSource{
					TargetPortal:   options.Parameters["targetPortal"],
					IQN:            options.Parameters["iqn"],
					ISCSIInterface: options.Parameters["iscsiInterface"],
					Lun:            lun,
					ReadOnly:       false,
					FSType:         "xfs",
				},
			},
		},
	}
	return pv, nil
}

// Delete removes the storage asset that was created by Provision represented
// by the given PV.
func (p *iscsiProvisioner) Delete(volume *v1.PersistentVolume) error {
	//vol from the annotation
	log.Debugln("volume deletion request received: ", volume.GetName())
	for _, initiator := range strings.Split(volume.Annotations["initiators"], ",") {
		log.Debugln("removing iscsi export: ", volume.Annotations["volume_name"], volume.Annotations["pool"], initiator)
		err := p.export_destroy(volume.Annotations["volume_name"], volume.Annotations["pool"], initiator)
		if err != nil {
			log.Warnln(err)
			return err
		}
		log.Debugln("iscsi export removed: ", volume.Annotations["volume_name"], volume.Annotations["pool"], initiator)
	}
	log.Debugln("removing logical volume : ", volume.Annotations["volume_name"], volume.Annotations["pool"])
	err := p.vol_destroy(volume.Annotations["volume_name"], volume.Annotations["pool"])
	if err != nil {
		log.Warnln(err)
		return err
	}
	log.Debugln("logical volume removed: ", volume.Annotations["volume_name"], volume.Annotations["pool"])
	log.Debugln("volume deletion request completed: ", volume.GetName())
	return nil
}

func initLog() {
	var err error
	log.Level, err = logrus.ParseLevel(viper.GetString("log-level"))
	if err != nil {
		log.Fatalln(err)
	}
}

func (p *iscsiProvisioner) createVolume(options controller.VolumeOptions) (vol string, lun int32, pool string, err error) {
	size := getSize(options)
	vol = p.getVolumeName(options)
	lun, err = p.getFirstAvailableLun()
	pool = p.getVolumeGroup(options)
	initiators := p.getInitiators(options)
	if err != nil {
		log.Warnln(err)
		return "", 0, "", err
	}
	log.Debugln("creating volume name, size, pool: ", vol, size, pool)
	err = p.vol_create(vol, size, pool)
	if err != nil {
		log.Warnln(err)
		return "", 0, "", err
	}
	log.Debugln("created volume name, size, pool: ", vol, size, pool)
	for _, initiator := range initiators {
		log.Debugln("exporting volume name, lun, pool, initiatir: ", vol, lun, pool, initiator)
		err = p.export_create(vol, lun, pool, initiator)
		if err != nil {
			log.Warnln(err)
			return "", 0, "", err
		}
		log.Debugln("exported volume name, lun, pool, initiator ", vol, lun, pool, initiator)
	}
	return vol, lun, pool, nil
}

func getSize(options controller.VolumeOptions) int64 {
	q := options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
	return q.Value()
}

func (p *iscsiProvisioner) getVolumeName(options controller.VolumeOptions) string {
	return options.PVName
}

func (p *iscsiProvisioner) getVolumeGroup(options controller.VolumeOptions) string {
	if options.Parameters["volumeGroup"] == "" {
		return "vg-targetd"
	}
	return options.Parameters["volumeGroup"]
}

func (p *iscsiProvisioner) getInitiators(options controller.VolumeOptions) []string {
	return strings.Split(options.Parameters["initiators"], ",")
}

func (p *iscsiProvisioner) getFirstAvailableLun() (int32, error) {
	log.Debugln("calling export_list")
	exportList, err := p.export_list()
	if err != nil {
		log.Warnln(err)
		return -1, err
	}
	log.Debugln("export_list called")
	if len(exportList) == 255 {
		return -1, errors.New("255 luns allocated no more luns available")
	}
	lun := int32(-1)
	sort.Sort(exportList)
	for i, export := range exportList {
		if i < int(export.Lun) {
			lun = int32(i)
			break
		}
	}
	if lun == -1 {
		lun = int32(len(exportList))
	}
	return lun, nil
	//return 0, nil
}

////// json rpc operations ////
func (p *iscsiProvisioner) vol_destroy(vol string, pool string) error {
	client, err := p.getConnection()
	defer client.Close()
	if err != nil {
		log.Warnln(err)
		return err
	}

	//make arguments object
	args := vol_destroyArgs{
		Pool: pool,
		Name: vol,
	}
	//this will store returned result
	var result result
	//call remote procedure with args
	err = client.Call("vol_destroy", args, &result)
	return err
}

func (p *iscsiProvisioner) export_destroy(vol string, pool string, initiator string) error {

	client, err := p.getConnection()
	defer client.Close()
	if err != nil {
		log.Warnln(err)
		return err
	}

	//make arguments object
	args := export_destroyArgs{
		Pool:          pool,
		Vol:           vol,
		Initiator_wwn: initiator,
	}
	//this will store returned result
	var result result
	//call remote procedure with args
	err = client.Call("export_destroy", args, &result)
	return err
}

func (p *iscsiProvisioner) vol_create(name string, size int64, pool string) error {

	client, err := p.getConnection()
	defer client.Close()
	if err != nil {
		log.Warnln(err)
		return err
	}

	//make arguments object
	args := vol_createArgs{
		Pool: pool,
		Name: name,
		Size: size,
	}
	//this will store returned result
	var result result
	//call remote procedure with args
	err = client.Call("vol_create", args, &result)
	return err
}

func (p *iscsiProvisioner) export_create(vol string, lun int32, pool string, initiator string) error {

	client, err := p.getConnection()
	defer client.Close()
	if err != nil {
		log.Warnln(err)
		return err
	}

	//make arguments object
	args := export_createArgs{
		Pool:          pool,
		Vol:           vol,
		Initiator_wwn: initiator,
		Lun:           lun,
	}
	//this will store returned result
	var result result
	//call remote procedure with args
	err = client.Call("export_create", args, &result)
	return err
}

func (p *iscsiProvisioner) export_list() (exportList, error) {

	client, err := p.getConnection()
	defer client.Close()
	if err != nil {
		log.Warnln(err)
		return nil, err
	}

	//this will store returned result
	var result exportList
	//call remote procedure with args
	err = client.Call("export_list", nil, &result)
	return result, err
}

func (slice exportList) Len() int {
	return len(slice)
}

func (slice exportList) Less(i, j int) bool {
	return slice[i].Lun < slice[j].Lun
}

func (slice exportList) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

func (p *iscsiProvisioner) getConnection() (*jsonrpc2.Client, error) {
	log.Debugln("opening connection to targetd: ", p.targetdURL)

	client := jsonrpc2.NewHTTPClient(p.targetdURL)

	if client == nil {
		log.Warnln("error creating the connection to targetd", p.targetdURL)
		return nil, errors.New("error creating the connection to targetd")
	}
	log.Debugln("targetd client created")
	return client, nil
}

//func (p *iscsiProvisioner) getConnection2() (*rpc.Client, error) {
//	log.Debugln("opening connection to targetd: ", p.targetdURL)
//
//	client, err := jsonrpc.Dial("tcp", p.targetdURL)
//
//	if err != nil {
//		log.Warnln(err)
//		return nil, err
//	}
//	log.Debugln("targetd client created")
//	return client, nil
//}
<commit_msg>fixed bug on skipping luns<commit_after>package provisioner

import (
	"errors"
	"github.com/Sirupsen/logrus"
	"github.com/powerman/rpc-codec/jsonrpc2"
	//"github.com/kubernetes-incubator/external-storage/iscsi/targetd/provisioner/jsonrpc2"
	"github.com/kubernetes-incubator/external-storage/lib/controller"
	"github.com/spf13/viper"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/pkg/api/v1"
	//"net/rpc"
	//"net/rpc/jsonrpc"
	"sort"
	"strings"
)

var log = logrus.New()

type vol_createArgs struct {
	Pool string `json:"pool"`
	Name string `json:"name"`
	Size int64  `json:"size"`
}

type vol_destroyArgs struct {
	Pool string `json:"pool"`
	Name string `json:"name"`
}

type export_createArgs struct {
	Pool          string `json:"pool"`
	Vol           string `json:"vol"`
	Initiator_wwn string `json:"initiator_wwn"`
	Lun           int32  `json:"lun"`
}

type export_destroyArgs struct {
	Pool          string `json:"pool"`
	Vol           string `json:"vol"`
	Initiator_wwn string `json:"initiator_wwn"`
}

type iscsiProvisioner struct {
	targetdURL string
}

type export struct {
	Initiator_wwn string `json:"initiator_wwn"`
	Lun           int32  `json:"lun"`
	Vol_name      string `json:"vol_name"`
	Vol_size      int    `json:"vol_size"`
	Vol_uuid      string `json:"vol_uuid"`
	Pool          string `json:"pool"`
}

type exportList []export

type result int

func NewiscsiProvisioner(url string) controller.Provisioner {

	initLog()

	return &iscsiProvisioner{
		targetdURL: url,
	}
}

// Provision creates a storage asset and returns a PV object representing it.
func (p *iscsiProvisioner) Provision(options controller.VolumeOptions) (*v1.PersistentVolume, error) {
	log.Debugln("new provision request received for pvc: ", options.PVName)
	vol, lun, pool, err := p.createVolume(options)
	if err != nil {
		log.Warnln(err)
		return nil, err
	}
	log.Debugln("volume created with vol and lun: ", vol, lun)

	annotations := make(map[string]string)
	annotations["volume_name"] = vol
	annotations["pool"] = pool
	annotations["initiators"] = options.Parameters["initiators"]
	//	annotations[annExportBlock] = exportBlock
	//	annotations[annExportID] = strconv.FormatUint(uint64(exportID), 10)
	//	annotations[annProjectBlock] = projectBlock
	//	annotations[annProjectID] = strconv.FormatUint(uint64(projectID), 10)
	//	if supGroup != 0 {
	//		annotations[VolumeGidAnnotationKey] = strconv.FormatUint(supGroup, 10)
	//	}
	//	annotations[annProvisionerID] = string(p.identity)

	pv := &v1.PersistentVolume{
		ObjectMeta: metav1.ObjectMeta{
			Name:        options.PVName,
			Labels:      map[string]string{},
			Annotations: annotations,
		},
		Spec: v1.PersistentVolumeSpec{
			PersistentVolumeReclaimPolicy: options.PersistentVolumeReclaimPolicy,
			AccessModes:                   options.PVC.Spec.AccessModes,
			Capacity: v1.ResourceList{
				v1.ResourceName(v1.ResourceStorage): options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)],
			},
			PersistentVolumeSource: v1.PersistentVolumeSource{
				ISCSI: &v1.ISCSIVolumeSource{
					TargetPortal:   options.Parameters["targetPortal"],
					IQN:            options.Parameters["iqn"],
					ISCSIInterface: options.Parameters["iscsiInterface"],
					Lun:            lun,
					ReadOnly:       false,
					FSType:         "xfs",
				},
			},
		},
	}
	return pv, nil
}

// Delete removes the storage asset that was created by Provision represented
// by the given PV.
func (p *iscsiProvisioner) Delete(volume *v1.PersistentVolume) error {
	//vol from the annotation
	log.Debugln("volume deletion request received: ", volume.GetName())
	for _, initiator := range strings.Split(volume.Annotations["initiators"], ",") {
		log.Debugln("removing iscsi export: ", volume.Annotations["volume_name"], volume.Annotations["pool"], initiator)
		err := p.export_destroy(volume.Annotations["volume_name"], volume.Annotations["pool"], initiator)
		if err != nil {
			log.Warnln(err)
			return err
		}
		log.Debugln("iscsi export removed: ", volume.Annotations["volume_name"], volume.Annotations["pool"], initiator)
	}
	log.Debugln("removing logical volume : ", volume.Annotations["volume_name"], volume.Annotations["pool"])
	err := p.vol_destroy(volume.Annotations["volume_name"], volume.Annotations["pool"])
	if err != nil {
		log.Warnln(err)
		return err
	}
	log.Debugln("logical volume removed: ", volume.Annotations["volume_name"], volume.Annotations["pool"])
	log.Debugln("volume deletion request completed: ", volume.GetName())
	return nil
}

func initLog() {
	var err error
	log.Level, err = logrus.ParseLevel(viper.GetString("log-level"))
	if err != nil {
		log.Fatalln(err)
	}
}

func (p *iscsiProvisioner) createVolume(options controller.VolumeOptions) (vol string, lun int32, pool string, err error) {
	size := getSize(options)
	vol = p.getVolumeName(options)
	lun, err = p.getFirstAvailableLun()
	pool = p.getVolumeGroup(options)
	initiators := p.getInitiators(options)
	if err != nil {
		log.Warnln(err)
		return "", 0, "", err
	}
	log.Debugln("creating volume name, size, pool: ", vol, size, pool)
	err = p.vol_create(vol, size, pool)
	if err != nil {
		log.Warnln(err)
		return "", 0, "", err
	}
	log.Debugln("created volume name, size, pool: ", vol, size, pool)
	for _, initiator := range initiators {
		log.Debugln("exporting volume name, lun, pool, initiatir: ", vol, lun, pool, initiator)
		err = p.export_create(vol, lun, pool, initiator)
		if err != nil {
			log.Warnln(err)
			return "", 0, "", err
		}
		log.Debugln("exported volume name, lun, pool, initiator ", vol, lun, pool, initiator)
	}
	return vol, lun, pool, nil
}

func getSize(options controller.VolumeOptions) int64 {
	q := options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
	return q.Value()
}

func (p *iscsiProvisioner) getVolumeName(options controller.VolumeOptions) string {
	return options.PVName
}

func (p *iscsiProvisioner) getVolumeGroup(options controller.VolumeOptions) string {
	if options.Parameters["volumeGroup"] == "" {
		return "vg-targetd"
	}
	return options.Parameters["volumeGroup"]
}

func (p *iscsiProvisioner) getInitiators(options controller.VolumeOptions) []string {
	return strings.Split(options.Parameters["initiators"], ",")
}

func (p *iscsiProvisioner) getFirstAvailableLun() (int32, error) {
	log.Debugln("calling export_list")
	exportList, err := p.export_list()
	if err != nil {
		log.Warnln(err)
		return -1, err
	}
	log.Debugln("export_list called")
	if len(exportList) == 255 {
		return -1, errors.New("255 luns allocated no more luns available")
	}
	sort.Sort(exportList)
	//this is sloppy way to remove duplicates
	unique_export := make(map[int32]export)
	for _, export := range exportList {
		unique_export[export.Lun] = export
	}
	//this is a sloppy way to get the list of luns
	luns := make([]int32, len(unique_export), len(unique_export))
	i := 0
	for _, export := range unique_export {
		luns[i] = export.Lun
		i++
	}
	lun := int32(-1)
	for i, clun := range luns {
		if i < int(clun) {
			lun = int32(i)
			break
		}
	}
	if lun == -1 {
		lun = int32(len(exportList))
	}
	return lun, nil
	//return 0, nil
}

////// json rpc operations ////
func (p *iscsiProvisioner) vol_destroy(vol string, pool string) error {
	client, err := p.getConnection()
	defer client.Close()
	if err != nil {
		log.Warnln(err)
		return err
	}

	//make arguments object
	args := vol_destroyArgs{
		Pool: pool,
		Name: vol,
	}
	//this will store returned result
	var result result
	//call remote procedure with args
	err = client.Call("vol_destroy", args, &result)
	return err
}

func (p *iscsiProvisioner) export_destroy(vol string, pool string, initiator string) error {

	client, err := p.getConnection()
	defer client.Close()
	if err != nil {
		log.Warnln(err)
		return err
	}

	//make arguments object
	args := export_destroyArgs{
		Pool:          pool,
		Vol:           vol,
		Initiator_wwn: initiator,
	}
	//this will store returned result
	var result result
	//call remote procedure with args
	err = client.Call("export_destroy", args, &result)
	return err
}

func (p *iscsiProvisioner) vol_create(name string, size int64, pool string) error {

	client, err := p.getConnection()
	defer client.Close()
	if err != nil {
		log.Warnln(err)
		return err
	}

	//make arguments object
	args := vol_createArgs{
		Pool: pool,
		Name: name,
		Size: size,
	}
	//this will store returned result
	var result result
	//call remote procedure with args
	err = client.Call("vol_create", args, &result)
	return err
}

func (p *iscsiProvisioner) export_create(vol string, lun int32, pool string, initiator string) error {

	client, err := p.getConnection()
	defer client.Close()
	if err != nil {
		log.Warnln(err)
		return err
	}

	//make arguments object
	args := export_createArgs{
		Pool:          pool,
		Vol:           vol,
		Initiator_wwn: initiator,
		Lun:           lun,
	}
	//this will store returned result
	var result result
	//call remote procedure with args
	err = client.Call("export_create", args, &result)
	return err
}

func (p *iscsiProvisioner) export_list() (exportList, error) {

	client, err := p.getConnection()
	defer client.Close()
	if err != nil {
		log.Warnln(err)
		return nil, err
	}

	//this will store returned result
	var result exportList
	//call remote procedure with args
	err = client.Call("export_list", nil, &result)
	return result, err
}

func (slice exportList) Len() int {
	return len(slice)
}

func (slice exportList) Less(i, j int) bool {
	return slice[i].Lun < slice[j].Lun
}

func (slice exportList) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

func (p *iscsiProvisioner) getConnection() (*jsonrpc2.Client, error) {
	log.Debugln("opening connection to targetd: ", p.targetdURL)

	client := jsonrpc2.NewHTTPClient(p.targetdURL)

	if client == nil {
		log.Warnln("error creating the connection to targetd", p.targetdURL)
		return nil, errors.New("error creating the connection to targetd")
	}
	log.Debugln("targetd client created")
	return client, nil
}

//func (p *iscsiProvisioner) getConnection2() (*rpc.Client, error) {
//	log.Debugln("opening connection to targetd: ", p.targetdURL)
//
//	client, err := jsonrpc.Dial("tcp", p.targetdURL)
//
//	if err != nil {
//		log.Warnln(err)
//		return nil, err
//	}
//	log.Debugln("targetd client created")
//	return client, nil
//}
<|endoftext|>


<commit_before>package projections

import (
	cli "github.com/jdextraze/go-gesclient/client"
	"github.com/jdextraze/go-gesclient/tasks"
	"net"
	"time"
)

type Manager struct {
	client       *client
	httpEndpoint *net.TCPAddr
}

func NewManager(
	httpEndpoint *net.TCPAddr,
	operationTimeout time.Duration,
) *Manager {
	if httpEndpoint == nil {
		panic("httpEndpoint is nil")
	}

	return &Manager{
		client:       newClient(operationTimeout),
		httpEndpoint: httpEndpoint,
	}
}

// Task.Result() returns nil
func (m *Manager) EnableAsync(name string, userCredentials *cli.UserCredentials) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}

	return m.client.Enable(m.httpEndpoint, name, userCredentials)
}

// Task.Result() returns nil
func (m *Manager) DisableAsync(name string, userCredentials *cli.UserCredentials) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}

	return m.client.Disable(m.httpEndpoint, name, userCredentials)
}

// Task.Result() returns nil
func (m *Manager) AbortAsync(name string, userCredentials *cli.UserCredentials) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}

	return m.client.Abort(m.httpEndpoint, name, userCredentials)
}

// Task.Result() returns nil
func (m *Manager) CreateOneTimeAsync(query string, userCredentials *cli.UserCredentials) *tasks.Task {
	if query == "" {
		panic("query must be present")
	}

	return m.client.CreateOneTime(m.httpEndpoint, query, userCredentials)
}

// Task.Result() returns nil
func (m *Manager) CreateTransientAsync(name string, query string, userCredentials *cli.UserCredentials) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}
	if query == "" {
		panic("query must be present")
	}

	return m.client.CreateTransient(m.httpEndpoint, name, query, userCredentials)
}

// Task.Result() returns nil
func (m *Manager) CreateContinuousAsync(
	name string,
	query string,
	trackEmittedStreams bool,
	userCredentials *cli.UserCredentials,
) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}
	if query == "" {
		panic("query must be present")
	}

	return m.client.CreateContinuous(m.httpEndpoint, name, query, trackEmittedStreams, userCredentials)
}

// Task.Result() returns []*projections.ProjectionDetails
func (m *Manager) ListAllAsync(userCredentials *cli.UserCredentials) *tasks.Task {
	return m.client.ListAll(m.httpEndpoint, userCredentials)
}

// Task.Result() returns []projections.ProjectionDetails
func (m *Manager) ListOneTimeAsync(userCredentials *cli.UserCredentials) *tasks.Task {
	return m.client.ListOneTime(m.httpEndpoint, userCredentials)
}

// Task.Result() returns []projections.ProjectionDetails
func (m *Manager) ListContinuousAsync(userCredentials *cli.UserCredentials) *tasks.Task {
	return m.client.ListContinuous(m.httpEndpoint, userCredentials)
}

// Task.Result() returns a string
func (m *Manager) GetStatusAsync(name string, userCredentials *cli.UserCredentials) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}

	return m.client.GetStatus(m.httpEndpoint, name, userCredentials)
}

// Task.Result() returns a string
func (m *Manager) GetStateAsync(name string, userCredentials *cli.UserCredentials) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}

	return m.client.GetState(m.httpEndpoint, name, userCredentials)
}

// Task.Result() returns a string
func (m *Manager) GetPartitionStateAsync(
	name string,
	partitionId string,
	userCredentials *cli.UserCredentials,
) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}
	if partitionId == "" {
		panic("partitionId must be present")
	}

	return m.client.GetPartitionStateAsync(m.httpEndpoint, name, partitionId, userCredentials)
}

// Task.Result() returns a string
func (m *Manager) GetResultAsync(name string, userCredentials *cli.UserCredentials) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}

	return m.client.GetResult(m.httpEndpoint, name, userCredentials)
}

// Task.Result() returns a string
func (m *Manager) GetPartitionResultAsync(
	name string,
	partitionId string,
	userCredentials *cli.UserCredentials,
) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}
	if partitionId == "" {
		panic("partitionId must be present")
	}

	return m.client.GetPartitionResultAsync(m.httpEndpoint, name, partitionId, userCredentials)
}

// Task.Result() returns a string
func (m *Manager) GetStatisticsAsync(name string, userCredentials *cli.UserCredentials) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}

	return m.client.GetStatistics(m.httpEndpoint, name, userCredentials)
}

// Task.Result() returns a string
func (m *Manager) GetQueryAsync(name string, userCredentials *cli.UserCredentials) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}

	return m.client.GetQuery(m.httpEndpoint, name, userCredentials)
}

// Task.Result() returns nil
func (m *Manager) UpdateQueryAsync(name string, query string, userCredentials *cli.UserCredentials) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}
	if query == "" {
		panic("query must be present")
	}

	return m.client.GetQuery(m.httpEndpoint, name, userCredentials)
}

// Task.Result() returns nil
func (m *Manager) DeleteQueryAsync(
	name string,
	deleteEmittedStreams bool,
	userCredentials *cli.UserCredentials,
) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}

	return m.client.Delete(m.httpEndpoint, name, deleteEmittedStreams, userCredentials)
}
<commit_msg>Fixed ListOneTimeAsync Task.Result() comment (#35)<commit_after>package projections

import (
	cli "github.com/jdextraze/go-gesclient/client"
	"github.com/jdextraze/go-gesclient/tasks"
	"net"
	"time"
)

type Manager struct {
	client       *client
	httpEndpoint *net.TCPAddr
}

func NewManager(
	httpEndpoint *net.TCPAddr,
	operationTimeout time.Duration,
) *Manager {
	if httpEndpoint == nil {
		panic("httpEndpoint is nil")
	}

	return &Manager{
		client:       newClient(operationTimeout),
		httpEndpoint: httpEndpoint,
	}
}

// Task.Result() returns nil
func (m *Manager) EnableAsync(name string, userCredentials *cli.UserCredentials) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}

	return m.client.Enable(m.httpEndpoint, name, userCredentials)
}

// Task.Result() returns nil
func (m *Manager) DisableAsync(name string, userCredentials *cli.UserCredentials) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}

	return m.client.Disable(m.httpEndpoint, name, userCredentials)
}

// Task.Result() returns nil
func (m *Manager) AbortAsync(name string, userCredentials *cli.UserCredentials) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}

	return m.client.Abort(m.httpEndpoint, name, userCredentials)
}

// Task.Result() returns nil
func (m *Manager) CreateOneTimeAsync(query string, userCredentials *cli.UserCredentials) *tasks.Task {
	if query == "" {
		panic("query must be present")
	}

	return m.client.CreateOneTime(m.httpEndpoint, query, userCredentials)
}

// Task.Result() returns nil
func (m *Manager) CreateTransientAsync(name string, query string, userCredentials *cli.UserCredentials) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}
	if query == "" {
		panic("query must be present")
	}

	return m.client.CreateTransient(m.httpEndpoint, name, query, userCredentials)
}

// Task.Result() returns nil
func (m *Manager) CreateContinuousAsync(
	name string,
	query string,
	trackEmittedStreams bool,
	userCredentials *cli.UserCredentials,
) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}
	if query == "" {
		panic("query must be present")
	}

	return m.client.CreateContinuous(m.httpEndpoint, name, query, trackEmittedStreams, userCredentials)
}

// Task.Result() returns []*projections.ProjectionDetails
func (m *Manager) ListAllAsync(userCredentials *cli.UserCredentials) *tasks.Task {
	return m.client.ListAll(m.httpEndpoint, userCredentials)
}

// Task.Result() returns []*projections.ProjectionDetails
func (m *Manager) ListOneTimeAsync(userCredentials *cli.UserCredentials) *tasks.Task {
	return m.client.ListOneTime(m.httpEndpoint, userCredentials)
}

// Task.Result() returns []*projections.ProjectionDetails
func (m *Manager) ListContinuousAsync(userCredentials *cli.UserCredentials) *tasks.Task {
	return m.client.ListContinuous(m.httpEndpoint, userCredentials)
}

// Task.Result() returns a string
func (m *Manager) GetStatusAsync(name string, userCredentials *cli.UserCredentials) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}

	return m.client.GetStatus(m.httpEndpoint, name, userCredentials)
}

// Task.Result() returns a string
func (m *Manager) GetStateAsync(name string, userCredentials *cli.UserCredentials) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}

	return m.client.GetState(m.httpEndpoint, name, userCredentials)
}

// Task.Result() returns a string
func (m *Manager) GetPartitionStateAsync(
	name string,
	partitionId string,
	userCredentials *cli.UserCredentials,
) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}
	if partitionId == "" {
		panic("partitionId must be present")
	}

	return m.client.GetPartitionStateAsync(m.httpEndpoint, name, partitionId, userCredentials)
}

// Task.Result() returns a string
func (m *Manager) GetResultAsync(name string, userCredentials *cli.UserCredentials) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}

	return m.client.GetResult(m.httpEndpoint, name, userCredentials)
}

// Task.Result() returns a string
func (m *Manager) GetPartitionResultAsync(
	name string,
	partitionId string,
	userCredentials *cli.UserCredentials,
) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}
	if partitionId == "" {
		panic("partitionId must be present")
	}

	return m.client.GetPartitionResultAsync(m.httpEndpoint, name, partitionId, userCredentials)
}

// Task.Result() returns a string
func (m *Manager) GetStatisticsAsync(name string, userCredentials *cli.UserCredentials) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}

	return m.client.GetStatistics(m.httpEndpoint, name, userCredentials)
}

// Task.Result() returns a string
func (m *Manager) GetQueryAsync(name string, userCredentials *cli.UserCredentials) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}

	return m.client.GetQuery(m.httpEndpoint, name, userCredentials)
}

// Task.Result() returns nil
func (m *Manager) UpdateQueryAsync(name string, query string, userCredentials *cli.UserCredentials) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}
	if query == "" {
		panic("query must be present")
	}

	return m.client.GetQuery(m.httpEndpoint, name, userCredentials)
}

// Task.Result() returns nil
func (m *Manager) DeleteQueryAsync(
	name string,
	deleteEmittedStreams bool,
	userCredentials *cli.UserCredentials,
) *tasks.Task {
	if name == "" {
		panic("name must be present")
	}

	return m.client.Delete(m.httpEndpoint, name, deleteEmittedStreams, userCredentials)
}
<|endoftext|>


<commit_before>// Copyright 2014 The Prometheus 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 prometheus

import (
	"fmt"
	"sync"
)

// MetricVec is a Collector to bundle metrics of the same name that
// differ in their label values. MetricVec is usually not used directly but as a
// building block for implementations of vectors of a given metric
// type. GaugeVec, CounterVec, SummaryVec, and UntypedVec are examples already
// provided in this package.
type MetricVec struct {
	mtx      sync.RWMutex // Protects the children.
	children map[uint64]Metric
	desc     *Desc

	newMetric func(labelValues ...string) Metric
}

// Describe implements Collector. The length of the returned slice
// is always one.
func (m *MetricVec) Describe(ch chan<- *Desc) {
	ch <- m.desc
}

// Collect implements Collector.
func (m *MetricVec) Collect(ch chan<- Metric) {
	m.mtx.RLock()
	defer m.mtx.RUnlock()

	for _, metric := range m.children {
		ch <- metric
	}
}

// GetMetricWithLabelValues returns the Metric for the given slice of label
// values (same order as the VariableLabels in Desc). If that combination of
// label values is accessed for the first time, a new Metric is created.
//
// It is possible to call this method without using the returned Metric to only
// create the new Metric but leave it at its start value (e.g. a Summary or
// Histogram without any observations). See also the SummaryVec example.
//
// Keeping the Metric for later use is possible (and should be considered if
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
// Delete can be used to delete the Metric from the MetricVec. In that case, the
// Metric will still exist, but it will not be exported anymore, even if a
// Metric with the same label values is created later. See also the CounterVec
// example.
//
// An error is returned if the number of label values is not the same as the
// number of VariableLabels in Desc.
//
// Note that for more than one label value, this method is prone to mistakes
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
// an alternative to avoid that type of mistake. For higher label numbers, the
// latter has a much more readable (albeit more verbose) syntax, but it comes
// with a performance overhead (for creating and processing the Labels map).
// See also the GaugeVec example.
func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) {
	m.mtx.Lock()
	defer m.mtx.Unlock()

	h, err := m.hashLabelValues(lvs)
	if err != nil {
		return nil, err
	}
	return m.getOrCreateMetric(h, lvs...), nil
}

// GetMetricWith returns the Metric for the given Labels map (the label names
// must match those of the VariableLabels in Desc). If that label map is
// accessed for the first time, a new Metric is created. Implications of
// creating a Metric without using it and keeping the Metric for later use are
// the same as for GetMetricWithLabelValues.
//
// An error is returned if the number and names of the Labels are inconsistent
// with those of the VariableLabels in Desc.
//
// This method is used for the same purpose as
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
// methods.
func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
	m.mtx.Lock()
	defer m.mtx.Unlock()

	h, err := m.hashLabels(labels)
	if err != nil {
		return nil, err
	}
	lvs := make([]string, len(labels))
	for i, label := range m.desc.variableLabels {
		lvs[i] = labels[label]
	}
	return m.getOrCreateMetric(h, lvs...), nil
}

// WithLabelValues works as GetMetricWithLabelValues, but panics if an error
// occurs. The method allows neat syntax like:
//     httpReqs.WithLabelValues("404", "POST").Inc()
func (m *MetricVec) WithLabelValues(lvs ...string) Metric {
	metric, err := m.GetMetricWithLabelValues(lvs...)
	if err != nil {
		panic(err)
	}
	return metric
}

// With works as GetMetricWith, but panics if an error occurs. The method allows
// neat syntax like:
//     httpReqs.With(Labels{"status":"404", "method":"POST"}).Inc()
func (m *MetricVec) With(labels Labels) Metric {
	metric, err := m.GetMetricWith(labels)
	if err != nil {
		panic(err)
	}
	return metric
}

// DeleteLabelValues removes the metric where the variable labels are the same
// as those passed in as labels (same order as the VariableLabels in Desc). It
// returns true if a metric was deleted.
//
// It is not an error if the number of label values is not the same as the
// number of VariableLabels in Desc.  However, such inconsistent label count can
// never match an actual Metric, so the method will always return false in that
// case.
//
// Note that for more than one label value, this method is prone to mistakes
// caused by an incorrect order of arguments. Consider Delete(Labels) as an
// alternative to avoid that type of mistake. For higher label numbers, the
// latter has a much more readable (albeit more verbose) syntax, but it comes
// with a performance overhead (for creating and processing the Labels map).
// See also the CounterVec example.
func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
	m.mtx.Lock()
	defer m.mtx.Unlock()

	h, err := m.hashLabelValues(lvs)
	if err != nil {
		return false
	}
	if _, has := m.children[h]; !has {
		return false
	}
	delete(m.children, h)
	return true
}

// Delete deletes the metric where the variable labels are the same as those
// passed in as labels. It returns true if a metric was deleted.
//
// It is not an error if the number and names of the Labels are inconsistent
// with those of the VariableLabels in the Desc of the MetricVec. However, such
// inconsistent Labels can never match an actual Metric, so the method will
// always return false in that case.
//
// This method is used for the same purpose as DeleteLabelValues(...string). See
// there for pros and cons of the two methods.
func (m *MetricVec) Delete(labels Labels) bool {
	m.mtx.Lock()
	defer m.mtx.Unlock()

	h, err := m.hashLabels(labels)
	if err != nil {
		return false
	}
	if _, has := m.children[h]; !has {
		return false
	}
	delete(m.children, h)
	return true
}

// Reset deletes all metrics in this vector.
func (m *MetricVec) Reset() {
	m.mtx.Lock()
	defer m.mtx.Unlock()

	for h := range m.children {
		delete(m.children, h)
	}
}

func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
	if len(vals) != len(m.desc.variableLabels) {
		return 0, errInconsistentCardinality
	}
	h := hashNew()
	for _, val := range vals {
		h = hashAdd(h, val)
	}
	return h, nil
}

func (m *MetricVec) hashLabels(labels Labels) (uint64, error) {
	if len(labels) != len(m.desc.variableLabels) {
		return 0, errInconsistentCardinality
	}
	h := hashNew()
	for _, label := range m.desc.variableLabels {
		val, ok := labels[label]
		if !ok {
			return 0, fmt.Errorf("label name %q missing in label map", label)
		}
		h = hashAdd(h, val)
	}
	return h, nil
}

func (m *MetricVec) getOrCreateMetric(hash uint64, labelValues ...string) Metric {
	metric, ok := m.children[hash]
	if !ok {
		// Copy labelValues. Otherwise, they would be allocated even if we don't go
		// down this code path.
		copiedLabelValues := append(make([]string, 0, len(labelValues)), labelValues...)
		metric = m.newMetric(copiedLabelValues...)
		m.children[hash] = metric
	}
	return metric
}
<commit_msg>ok not has<commit_after>// Copyright 2014 The Prometheus 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 prometheus

import (
	"fmt"
	"sync"
)

// MetricVec is a Collector to bundle metrics of the same name that
// differ in their label values. MetricVec is usually not used directly but as a
// building block for implementations of vectors of a given metric
// type. GaugeVec, CounterVec, SummaryVec, and UntypedVec are examples already
// provided in this package.
type MetricVec struct {
	mtx      sync.RWMutex // Protects the children.
	children map[uint64]Metric
	desc     *Desc

	newMetric func(labelValues ...string) Metric
}

// Describe implements Collector. The length of the returned slice
// is always one.
func (m *MetricVec) Describe(ch chan<- *Desc) {
	ch <- m.desc
}

// Collect implements Collector.
func (m *MetricVec) Collect(ch chan<- Metric) {
	m.mtx.RLock()
	defer m.mtx.RUnlock()

	for _, metric := range m.children {
		ch <- metric
	}
}

// GetMetricWithLabelValues returns the Metric for the given slice of label
// values (same order as the VariableLabels in Desc). If that combination of
// label values is accessed for the first time, a new Metric is created.
//
// It is possible to call this method without using the returned Metric to only
// create the new Metric but leave it at its start value (e.g. a Summary or
// Histogram without any observations). See also the SummaryVec example.
//
// Keeping the Metric for later use is possible (and should be considered if
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
// Delete can be used to delete the Metric from the MetricVec. In that case, the
// Metric will still exist, but it will not be exported anymore, even if a
// Metric with the same label values is created later. See also the CounterVec
// example.
//
// An error is returned if the number of label values is not the same as the
// number of VariableLabels in Desc.
//
// Note that for more than one label value, this method is prone to mistakes
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
// an alternative to avoid that type of mistake. For higher label numbers, the
// latter has a much more readable (albeit more verbose) syntax, but it comes
// with a performance overhead (for creating and processing the Labels map).
// See also the GaugeVec example.
func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) {
	m.mtx.Lock()
	defer m.mtx.Unlock()

	h, err := m.hashLabelValues(lvs)
	if err != nil {
		return nil, err
	}
	return m.getOrCreateMetric(h, lvs...), nil
}

// GetMetricWith returns the Metric for the given Labels map (the label names
// must match those of the VariableLabels in Desc). If that label map is
// accessed for the first time, a new Metric is created. Implications of
// creating a Metric without using it and keeping the Metric for later use are
// the same as for GetMetricWithLabelValues.
//
// An error is returned if the number and names of the Labels are inconsistent
// with those of the VariableLabels in Desc.
//
// This method is used for the same purpose as
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
// methods.
func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
	m.mtx.Lock()
	defer m.mtx.Unlock()

	h, err := m.hashLabels(labels)
	if err != nil {
		return nil, err
	}
	lvs := make([]string, len(labels))
	for i, label := range m.desc.variableLabels {
		lvs[i] = labels[label]
	}
	return m.getOrCreateMetric(h, lvs...), nil
}

// WithLabelValues works as GetMetricWithLabelValues, but panics if an error
// occurs. The method allows neat syntax like:
//     httpReqs.WithLabelValues("404", "POST").Inc()
func (m *MetricVec) WithLabelValues(lvs ...string) Metric {
	metric, err := m.GetMetricWithLabelValues(lvs...)
	if err != nil {
		panic(err)
	}
	return metric
}

// With works as GetMetricWith, but panics if an error occurs. The method allows
// neat syntax like:
//     httpReqs.With(Labels{"status":"404", "method":"POST"}).Inc()
func (m *MetricVec) With(labels Labels) Metric {
	metric, err := m.GetMetricWith(labels)
	if err != nil {
		panic(err)
	}
	return metric
}

// DeleteLabelValues removes the metric where the variable labels are the same
// as those passed in as labels (same order as the VariableLabels in Desc). It
// returns true if a metric was deleted.
//
// It is not an error if the number of label values is not the same as the
// number of VariableLabels in Desc.  However, such inconsistent label count can
// never match an actual Metric, so the method will always return false in that
// case.
//
// Note that for more than one label value, this method is prone to mistakes
// caused by an incorrect order of arguments. Consider Delete(Labels) as an
// alternative to avoid that type of mistake. For higher label numbers, the
// latter has a much more readable (albeit more verbose) syntax, but it comes
// with a performance overhead (for creating and processing the Labels map).
// See also the CounterVec example.
func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
	m.mtx.Lock()
	defer m.mtx.Unlock()

	h, err := m.hashLabelValues(lvs)
	if err != nil {
		return false
	}
	if _, ok := m.children[h]; !ok {
		return false
	}
	delete(m.children, h)
	return true
}

// Delete deletes the metric where the variable labels are the same as those
// passed in as labels. It returns true if a metric was deleted.
//
// It is not an error if the number and names of the Labels are inconsistent
// with those of the VariableLabels in the Desc of the MetricVec. However, such
// inconsistent Labels can never match an actual Metric, so the method will
// always return false in that case.
//
// This method is used for the same purpose as DeleteLabelValues(...string). See
// there for pros and cons of the two methods.
func (m *MetricVec) Delete(labels Labels) bool {
	m.mtx.Lock()
	defer m.mtx.Unlock()

	h, err := m.hashLabels(labels)
	if err != nil {
		return false
	}
	if _, ok := m.children[h]; !ok {
		return false
	}
	delete(m.children, h)
	return true
}

// Reset deletes all metrics in this vector.
func (m *MetricVec) Reset() {
	m.mtx.Lock()
	defer m.mtx.Unlock()

	for h := range m.children {
		delete(m.children, h)
	}
}

func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
	if len(vals) != len(m.desc.variableLabels) {
		return 0, errInconsistentCardinality
	}
	h := hashNew()
	for _, val := range vals {
		h = hashAdd(h, val)
	}
	return h, nil
}

func (m *MetricVec) hashLabels(labels Labels) (uint64, error) {
	if len(labels) != len(m.desc.variableLabels) {
		return 0, errInconsistentCardinality
	}
	h := hashNew()
	for _, label := range m.desc.variableLabels {
		val, ok := labels[label]
		if !ok {
			return 0, fmt.Errorf("label name %q missing in label map", label)
		}
		h = hashAdd(h, val)
	}
	return h, nil
}

func (m *MetricVec) getOrCreateMetric(hash uint64, labelValues ...string) Metric {
	metric, ok := m.children[hash]
	if !ok {
		// Copy labelValues. Otherwise, they would be allocated even if we don't go
		// down this code path.
		copiedLabelValues := append(make([]string, 0, len(labelValues)), labelValues...)
		metric = m.newMetric(copiedLabelValues...)
		m.children[hash] = metric
	}
	return metric
}
<|endoftext|>