<commit_before>package gopush

import (
	"fmt"
	"reflect"
	"strings"
)

func newCodeStack(interpreter *Interpreter) *Stack {
	s := &Stack{
		Functions: make(map[string]Instruction),
	}

	s.Functions["="] = func() {
		if !interpreter.stackOK("code", 2) || !interpreter.stackOK("boolean", 0) {
			return
		}

		c1 := interpreter.Stacks["code"].Pop().(Code)
		c2 := interpreter.Stacks["code"].Pop().(Code)

		if reflect.DeepEqual(c1, c2) {
			interpreter.Stacks["boolean"].Push(true)
		} else {
			interpreter.Stacks["boolean"].Push(false)
		}
	}

	s.Functions["append"] = func() {
		if !interpreter.stackOK("code", 2) {
			return
		}

		c1 := interpreter.Stacks["code"].Pop().(Code)
		c2 := interpreter.Stacks["code"].Pop().(Code)

		if c1.Literal != "" {
			c1 = Code{Length: c1.Length, List: []Code{c1}}
		}

		if c2.Literal != "" {
			c2 = Code{Length: c2.Length, List: []Code{c2}}
		}

		combined := Code{Length: c1.Length + c2.Length, List: append(c2.List, c1.List...)}

		if combined.Length <= interpreter.Options.MaxPointsInProgram {
			interpreter.Stacks["code"].Push(combined)
		}
	}

	s.Functions["atom"] = func() {
		if !interpreter.stackOK("code", 1) || !interpreter.stackOK("boolean", 0) {
			return
		}

		c := interpreter.Stacks["code"].Pop().(Code)

		if c.Literal != "" {
			interpreter.Stacks["boolean"].Push(true)
		} else {
			interpreter.Stacks["boolean"].Push(false)
		}
	}

	s.Functions["car"] = func() {
		if !interpreter.stackOK("code", 1) {
			return
		}

		c := interpreter.Stacks["code"].Pop().(Code)

		if len(c.List) == 0 {
			return
		}

		interpreter.Stacks["code"].Push(c.List[0])
	}

	s.Functions["cdr"] = func() {
		if !interpreter.stackOK("code", 1) {
			return
		}

		c := interpreter.Stacks["code"].Pop().(Code)

		if len(c.List) == 0 {
			interpreter.Stacks["code"].Push(Code{})
		} else {
			cdr := Code{
				Length: c.Length - c.List[0].Length,
				List:   c.List[1:],
			}
			interpreter.Stacks["code"].Push(cdr)
		}
	}

	s.Functions["cons"] = func() {
		if !interpreter.stackOK("code", 2) {
			return
		}

		c1 := interpreter.Stacks["code"].Pop().(Code)
		c2 := interpreter.Stacks["code"].Pop().(Code)

		if c1.Literal != "" {
			c1 = Code{Length: 1, List: []Code{c1}}
		}

		if c2.Literal != "" {
			c2 = Code{Length: 1, List: []Code{c2}}
		}

		c := Code{
			Length: c1.Length + c2.Length,
			List:   append(c2.List, c1.List...),
		}

		interpreter.Stacks["code"].Push(c)
	}

	s.Functions["container"] = func() {
		if !interpreter.stackOK("code", 2) {
			return
		}

		c1 := interpreter.Stacks["code"].Pop().(Code)
		c2 := interpreter.Stacks["code"].Pop().(Code)

		c := c1.Container(c2)
		interpreter.Stacks["code"].Push(c)
	}

	s.Functions["contains"] = func() {
		if !interpreter.stackOK("code", 2) || !interpreter.stackOK("boolean", 0) {
			return
		}

		c1 := interpreter.Stacks["code"].Pop().(Code)
		c2 := interpreter.Stacks["code"].Pop().(Code)

		interpreter.Stacks["boolean"].Push(c2.Contains(c1))
	}

	s.Functions["define"] = func() {
		if !interpreter.stackOK("name", 1) || !interpreter.stackOK("code", 1) {
			return
		}

		n := interpreter.Stacks["name"].Pop().(string)
		c := interpreter.Stacks["code"].Pop().(Code)

		interpreter.define(n, c)
	}

	s.Functions["definition"] = func() {
		if !interpreter.stackOK("name", 1) {
			return
		}

		n := interpreter.Stacks["name"].Pop().(string)

		if c, ok := interpreter.Definitions[n]; ok {
			interpreter.Stacks["code"].Push(c)
		}
	}

	s.Functions["discrepancy"] = func() {
		if !interpreter.stackOK("code", 2) || !interpreter.stackOK("integer", 0) {
			return
		}

		c1 := interpreter.Stacks["code"].Pop().(Code)
		c2 := interpreter.Stacks["code"].Pop().(Code)

		u1 := c1.UniqueItems()
		u2 := c2.UniqueItems()

		keys := make(map[string]struct{}, 0)

		for k := range u1 {
			keys[k] = struct{}{}
		}

		for k := range u2 {
			keys[k] = struct{}{}
		}

		discrepancy := int64(0)
		for k := range keys {
			if u1[k] > u2[k] {
				discrepancy += u1[k] - u2[k]
			} else {
				discrepancy += u2[k] - u1[k]
			}
		}

		interpreter.Stacks["integer"].Push(discrepancy)
	}

	s.Functions["do"] = func() {
		if !interpreter.stackOK("code", 1) {
			return
		}

		c := interpreter.Stacks["code"].Pop().(Code)

		err := interpreter.runCode(c)
		if err != nil {
			panic(err)
		}

		interpreter.Stacks["code"].Pop()
	}

	s.Functions["do*"] = func() {
		if !interpreter.stackOK("code", 1) {
			return
		}

		c := interpreter.Stacks["code"].Pop().(Code)
		interpreter.Stacks["code"].Pop()

		err := interpreter.runCode(c)
		if err != nil {
			panic(err)
		}
	}

	s.Functions["do*count"] = func() {
		// TODO
	}

	s.Functions["do*range"] = func() {
		if !interpreter.stackOK("code", 1) || !interpreter.stackOK("integer", 2) {
			return
		}

		c := interpreter.Stacks["code"].Pop().(Code)
		dst := interpreter.Stacks["integer"].Pop().(int64)
		cur := interpreter.Stacks["integer"].Pop().(int64)

		if cur == dst {
			interpreter.Stacks["integer"].Push(cur)
			interpreter.Stacks["exec"].Push(c)
		} else {
			interpreter.Stacks["integer"].Push(cur)

			if dst < cur {
				cur--
			} else {
				cur++
			}

			interpreter.Stacks["code"].Push(c)
			interpreter.Stacks["exec"].Push(c)
			interpreter.Stacks["exec"].Push(Code{Length: 1, Literal: "CODE.DO*RANGE"})
			interpreter.Stacks["integer"].Push(cur)
			interpreter.Stacks["integer"].Push(dst)
		}
	}

	s.Functions["do*times"] = func() {
		// TODO
	}

	s.Functions["dup"] = func() {
		interpreter.Stacks["code"].Dup()
	}

	s.Functions["extract"] = func() {
		// TODO
	}

	s.Functions["flush"] = func() {
		interpreter.Stacks["code"].Flush()
	}

	s.Functions["fromboolean"] = func() {
		if !interpreter.stackOK("boolean", 1) {
			return
		}

		b := interpreter.Stacks["boolean"].Pop().(bool)
		interpreter.Stacks["code"].Push(Code{Length: 1, Literal: fmt.Sprint(b)})
	}

	s.Functions["fromfloat"] = func() {
		if !interpreter.stackOK("float", 1) {
			return
		}

		f := interpreter.Stacks["float"].Pop().(float64)
		l := fmt.Sprint(f)
		if !strings.Contains(l, ".") {
			l += ".0"
		}
		interpreter.Stacks["code"].Push(Code{Length: 1, Literal: l})
	}

	s.Functions["frominteger"] = func() {
		if !interpreter.stackOK("integer", 1) {
			return
		}

		i := interpreter.Stacks["integer"].Pop().(int64)
		interpreter.Stacks["code"].Push(Code{Length: 1, Literal: fmt.Sprint(i)})
	}

	s.Functions["fromname"] = func() {
		if !interpreter.stackOK("name", 1) {
			return
		}

		n := interpreter.Stacks["name"].Pop().(string)
		interpreter.Stacks["code"].Push(Code{Length: 1, Literal: n})
	}

	s.Functions["if"] = func() {
		if !interpreter.stackOK("code", 2) || !interpreter.stackOK("boolean", 1) {
			return
		}

		b := interpreter.Stacks["boolean"].Pop().(bool)
		c1 := interpreter.Stacks["code"].Pop().(Code)
		c2 := interpreter.Stacks["code"].Pop().(Code)

		if b {
			interpreter.Stacks["exec"].Push(c2)
		} else {
			interpreter.Stacks["exec"].Push(c1)
		}
	}

	s.Functions["insert"] = func() {
		// TODO
	}

	s.Functions["instructions"] = func() {
		// TODO
	}

	s.Functions["length"] = func() {
		if !interpreter.stackOK("code", 1) || !interpreter.stackOK("integer", 0) {
			return
		}

		c := interpreter.Stacks["code"].Peek().(Code)
		if c.Literal != "" {
			interpreter.Stacks["integer"].Push(int64(1))
		} else {
			interpreter.Stacks["integer"].Push(int64(len(c.List)))
		}
	}

	s.Functions["list"] = func() {
		if !interpreter.stackOK("code", 2) {
			return
		}

		c1 := interpreter.Stacks["code"].Pop().(Code)
		c2 := interpreter.Stacks["code"].Pop().(Code)

		c := Code{
			Length: c1.Length + c2.Length,
			List:   []Code{c1, c2},
		}

		interpreter.Stacks["code"].Push(c)
	}

	s.Functions["member"] = func() {
		// TODO
	}

	s.Functions["noop"] = func() {
		// Does nothing
	}

	s.Functions["nth"] = func() {
		// TODO
	}

	s.Functions["nthcdr"] = func() {
		// TODO
	}

	s.Functions["null"] = func() {
		if !interpreter.stackOK("code", 1) || !interpreter.stackOK("boolean", 0) {
			return
		}

		c := interpreter.Stacks["code"].Peek().(Code)
		interpreter.Stacks["boolean"].Push(c.Literal == "" && len(c.List) == 0)
	}

	s.Functions["pop"] = func() {
		// TODO
	}

	s.Functions["position"] = func() {
		// TODO
	}

	s.Functions["quote"] = func() {
		if !interpreter.stackOK("exec", 1) {
			return
		}

		c := interpreter.Stacks["exec"].Pop().(Code)
		interpreter.Stacks["code"].Push(c)
	}

	s.Functions["rand"] = func() {
		if !interpreter.stackOK("integer", 1) {
			return
		}

		maxPoints := interpreter.Stacks["integer"].Pop().(int64)
		if maxPoints < 0 {
			maxPoints *= -1
		}

		if maxPoints > interpreter.Options.MaxPointsInRandomExpression {
			maxPoints = interpreter.Options.MaxPointsInRandomExpression
		}

		c := interpreter.RandomCode(maxPoints)
		interpreter.Stacks["code"].Push(c)
	}

	s.Functions["rot"] = func() {
		interpreter.Stacks["code"].Rot()
	}

	s.Functions["shove"] = func() {
		if !interpreter.stackOK("code", 1) || !interpreter.stackOK("integer", 1) {
			return
		}

		idx := interpreter.Stacks["integer"].Pop().(int64)
		c := interpreter.Stacks["code"].Peek().(Code)
		interpreter.Stacks["code"].Shove(c, idx)
		interpreter.Stacks["code"].Pop()
	}

	s.Functions["size"] = func() {
		if !interpreter.stackOK("code", 1) || !interpreter.stackOK("integer", 0) {
			return
		}

		c := interpreter.Stacks["code"].Peek().(Code)
		interpreter.Stacks["integer"].Push(c.Length)
	}

	s.Functions["stackdepth"] = func() {
		if !interpreter.stackOK("integer", 0) {
			return
		}

		interpreter.Stacks["integer"].Push(interpreter.Stacks["code"].Len())
	}

	s.Functions["subst"] = func() {
		// TODO
	}

	s.Functions["swap"] = func() {
		interpreter.Stacks["code"].Swap()
	}

	s.Functions["yank"] = func() {
		if !interpreter.stackOK("integer", 1) || !interpreter.stackOK("code", 1) {
			return
		}

		idx := interpreter.Stacks["integer"].Pop().(int64)
		interpreter.Stacks["code"].Yank(idx)
	}

	s.Functions["yankdup"] = func() {
		if !interpreter.stackOK("integer", 1) || !interpreter.stackOK("code", 1) {
			return
		}

		idx := interpreter.Stacks["integer"].Pop().(int64)
		interpreter.Stacks["code"].YankDup(idx)
	}

	return s
}
<commit_msg>Implement CODE.INSTRUCTIONS<commit_after>package gopush

import (
	"fmt"
	"reflect"
	"strings"
)

func newCodeStack(interpreter *Interpreter) *Stack {
	s := &Stack{
		Functions: make(map[string]Instruction),
	}

	s.Functions["="] = func() {
		if !interpreter.stackOK("code", 2) || !interpreter.stackOK("boolean", 0) {
			return
		}

		c1 := interpreter.Stacks["code"].Pop().(Code)
		c2 := interpreter.Stacks["code"].Pop().(Code)

		if reflect.DeepEqual(c1, c2) {
			interpreter.Stacks["boolean"].Push(true)
		} else {
			interpreter.Stacks["boolean"].Push(false)
		}
	}

	s.Functions["append"] = func() {
		if !interpreter.stackOK("code", 2) {
			return
		}

		c1 := interpreter.Stacks["code"].Pop().(Code)
		c2 := interpreter.Stacks["code"].Pop().(Code)

		if c1.Literal != "" {
			c1 = Code{Length: c1.Length, List: []Code{c1}}
		}

		if c2.Literal != "" {
			c2 = Code{Length: c2.Length, List: []Code{c2}}
		}

		combined := Code{Length: c1.Length + c2.Length, List: append(c2.List, c1.List...)}

		if combined.Length <= interpreter.Options.MaxPointsInProgram {
			interpreter.Stacks["code"].Push(combined)
		}
	}

	s.Functions["atom"] = func() {
		if !interpreter.stackOK("code", 1) || !interpreter.stackOK("boolean", 0) {
			return
		}

		c := interpreter.Stacks["code"].Pop().(Code)

		if c.Literal != "" {
			interpreter.Stacks["boolean"].Push(true)
		} else {
			interpreter.Stacks["boolean"].Push(false)
		}
	}

	s.Functions["car"] = func() {
		if !interpreter.stackOK("code", 1) {
			return
		}

		c := interpreter.Stacks["code"].Pop().(Code)

		if len(c.List) == 0 {
			return
		}

		interpreter.Stacks["code"].Push(c.List[0])
	}

	s.Functions["cdr"] = func() {
		if !interpreter.stackOK("code", 1) {
			return
		}

		c := interpreter.Stacks["code"].Pop().(Code)

		if len(c.List) == 0 {
			interpreter.Stacks["code"].Push(Code{})
		} else {
			cdr := Code{
				Length: c.Length - c.List[0].Length,
				List:   c.List[1:],
			}
			interpreter.Stacks["code"].Push(cdr)
		}
	}

	s.Functions["cons"] = func() {
		if !interpreter.stackOK("code", 2) {
			return
		}

		c1 := interpreter.Stacks["code"].Pop().(Code)
		c2 := interpreter.Stacks["code"].Pop().(Code)

		if c1.Literal != "" {
			c1 = Code{Length: 1, List: []Code{c1}}
		}

		if c2.Literal != "" {
			c2 = Code{Length: 1, List: []Code{c2}}
		}

		c := Code{
			Length: c1.Length + c2.Length,
			List:   append(c2.List, c1.List...),
		}

		interpreter.Stacks["code"].Push(c)
	}

	s.Functions["container"] = func() {
		if !interpreter.stackOK("code", 2) {
			return
		}

		c1 := interpreter.Stacks["code"].Pop().(Code)
		c2 := interpreter.Stacks["code"].Pop().(Code)

		c := c1.Container(c2)
		interpreter.Stacks["code"].Push(c)
	}

	s.Functions["contains"] = func() {
		if !interpreter.stackOK("code", 2) || !interpreter.stackOK("boolean", 0) {
			return
		}

		c1 := interpreter.Stacks["code"].Pop().(Code)
		c2 := interpreter.Stacks["code"].Pop().(Code)

		interpreter.Stacks["boolean"].Push(c2.Contains(c1))
	}

	s.Functions["define"] = func() {
		if !interpreter.stackOK("name", 1) || !interpreter.stackOK("code", 1) {
			return
		}

		n := interpreter.Stacks["name"].Pop().(string)
		c := interpreter.Stacks["code"].Pop().(Code)

		interpreter.define(n, c)
	}

	s.Functions["definition"] = func() {
		if !interpreter.stackOK("name", 1) {
			return
		}

		n := interpreter.Stacks["name"].Pop().(string)

		if c, ok := interpreter.Definitions[n]; ok {
			interpreter.Stacks["code"].Push(c)
		}
	}

	s.Functions["discrepancy"] = func() {
		if !interpreter.stackOK("code", 2) || !interpreter.stackOK("integer", 0) {
			return
		}

		c1 := interpreter.Stacks["code"].Pop().(Code)
		c2 := interpreter.Stacks["code"].Pop().(Code)

		u1 := c1.UniqueItems()
		u2 := c2.UniqueItems()

		keys := make(map[string]struct{}, 0)

		for k := range u1 {
			keys[k] = struct{}{}
		}

		for k := range u2 {
			keys[k] = struct{}{}
		}

		discrepancy := int64(0)
		for k := range keys {
			if u1[k] > u2[k] {
				discrepancy += u1[k] - u2[k]
			} else {
				discrepancy += u2[k] - u1[k]
			}
		}

		interpreter.Stacks["integer"].Push(discrepancy)
	}

	s.Functions["do"] = func() {
		if !interpreter.stackOK("code", 1) {
			return
		}

		c := interpreter.Stacks["code"].Pop().(Code)

		err := interpreter.runCode(c)
		if err != nil {
			panic(err)
		}

		interpreter.Stacks["code"].Pop()
	}

	s.Functions["do*"] = func() {
		if !interpreter.stackOK("code", 1) {
			return
		}

		c := interpreter.Stacks["code"].Pop().(Code)
		interpreter.Stacks["code"].Pop()

		err := interpreter.runCode(c)
		if err != nil {
			panic(err)
		}
	}

	s.Functions["do*count"] = func() {
		// TODO
	}

	s.Functions["do*range"] = func() {
		if !interpreter.stackOK("code", 1) || !interpreter.stackOK("integer", 2) {
			return
		}

		c := interpreter.Stacks["code"].Pop().(Code)
		dst := interpreter.Stacks["integer"].Pop().(int64)
		cur := interpreter.Stacks["integer"].Pop().(int64)

		if cur == dst {
			interpreter.Stacks["integer"].Push(cur)
			interpreter.Stacks["exec"].Push(c)
		} else {
			interpreter.Stacks["integer"].Push(cur)

			if dst < cur {
				cur--
			} else {
				cur++
			}

			interpreter.Stacks["code"].Push(c)
			interpreter.Stacks["exec"].Push(c)
			interpreter.Stacks["exec"].Push(Code{Length: 1, Literal: "CODE.DO*RANGE"})
			interpreter.Stacks["integer"].Push(cur)
			interpreter.Stacks["integer"].Push(dst)
		}
	}

	s.Functions["do*times"] = func() {
		// TODO
	}

	s.Functions["dup"] = func() {
		interpreter.Stacks["code"].Dup()
	}

	s.Functions["extract"] = func() {
		// TODO
	}

	s.Functions["flush"] = func() {
		interpreter.Stacks["code"].Flush()
	}

	s.Functions["fromboolean"] = func() {
		if !interpreter.stackOK("boolean", 1) {
			return
		}

		b := interpreter.Stacks["boolean"].Pop().(bool)
		interpreter.Stacks["code"].Push(Code{Length: 1, Literal: fmt.Sprint(b)})
	}

	s.Functions["fromfloat"] = func() {
		if !interpreter.stackOK("float", 1) {
			return
		}

		f := interpreter.Stacks["float"].Pop().(float64)
		l := fmt.Sprint(f)
		if !strings.Contains(l, ".") {
			l += ".0"
		}
		interpreter.Stacks["code"].Push(Code{Length: 1, Literal: l})
	}

	s.Functions["frominteger"] = func() {
		if !interpreter.stackOK("integer", 1) {
			return
		}

		i := interpreter.Stacks["integer"].Pop().(int64)
		interpreter.Stacks["code"].Push(Code{Length: 1, Literal: fmt.Sprint(i)})
	}

	s.Functions["fromname"] = func() {
		if !interpreter.stackOK("name", 1) {
			return
		}

		n := interpreter.Stacks["name"].Pop().(string)
		interpreter.Stacks["code"].Push(Code{Length: 1, Literal: n})
	}

	s.Functions["if"] = func() {
		if !interpreter.stackOK("code", 2) || !interpreter.stackOK("boolean", 1) {
			return
		}

		b := interpreter.Stacks["boolean"].Pop().(bool)
		c1 := interpreter.Stacks["code"].Pop().(Code)
		c2 := interpreter.Stacks["code"].Pop().(Code)

		if b {
			interpreter.Stacks["exec"].Push(c2)
		} else {
			interpreter.Stacks["exec"].Push(c1)
		}
	}

	s.Functions["insert"] = func() {
		// TODO
	}

	s.Functions["instructions"] = func() {
		c := Code{List: make([]Code, 0, len(interpreter.listOfInstructions))}

		for _, instr := range interpreter.listOfInstructions {
			if instr == "NAME-ERC" || instr == "FLOAT-ERC" || instr == "INTEGER-ERC" {
				continue
			}

			c.Length++
			c.List = append(c.List, Code{Length: 1, Literal: instr})
		}

		interpreter.Stacks["code"].Push(c)
	}

	s.Functions["length"] = func() {
		if !interpreter.stackOK("code", 1) || !interpreter.stackOK("integer", 0) {
			return
		}

		c := interpreter.Stacks["code"].Peek().(Code)
		if c.Literal != "" {
			interpreter.Stacks["integer"].Push(int64(1))
		} else {
			interpreter.Stacks["integer"].Push(int64(len(c.List)))
		}
	}

	s.Functions["list"] = func() {
		if !interpreter.stackOK("code", 2) {
			return
		}

		c1 := interpreter.Stacks["code"].Pop().(Code)
		c2 := interpreter.Stacks["code"].Pop().(Code)

		c := Code{
			Length: c1.Length + c2.Length,
			List:   []Code{c1, c2},
		}

		interpreter.Stacks["code"].Push(c)
	}

	s.Functions["member"] = func() {
		// TODO
	}

	s.Functions["noop"] = func() {
		// Does nothing
	}

	s.Functions["nth"] = func() {
		// TODO
	}

	s.Functions["nthcdr"] = func() {
		// TODO
	}

	s.Functions["null"] = func() {
		if !interpreter.stackOK("code", 1) || !interpreter.stackOK("boolean", 0) {
			return
		}

		c := interpreter.Stacks["code"].Peek().(Code)
		interpreter.Stacks["boolean"].Push(c.Literal == "" && len(c.List) == 0)
	}

	s.Functions["pop"] = func() {
		// TODO
	}

	s.Functions["position"] = func() {
		// TODO
	}

	s.Functions["quote"] = func() {
		if !interpreter.stackOK("exec", 1) {
			return
		}

		c := interpreter.Stacks["exec"].Pop().(Code)
		interpreter.Stacks["code"].Push(c)
	}

	s.Functions["rand"] = func() {
		if !interpreter.stackOK("integer", 1) {
			return
		}

		maxPoints := interpreter.Stacks["integer"].Pop().(int64)
		if maxPoints < 0 {
			maxPoints *= -1
		}

		if maxPoints > interpreter.Options.MaxPointsInRandomExpression {
			maxPoints = interpreter.Options.MaxPointsInRandomExpression
		}

		c := interpreter.RandomCode(maxPoints)
		interpreter.Stacks["code"].Push(c)
	}

	s.Functions["rot"] = func() {
		interpreter.Stacks["code"].Rot()
	}

	s.Functions["shove"] = func() {
		if !interpreter.stackOK("code", 1) || !interpreter.stackOK("integer", 1) {
			return
		}

		idx := interpreter.Stacks["integer"].Pop().(int64)
		c := interpreter.Stacks["code"].Peek().(Code)
		interpreter.Stacks["code"].Shove(c, idx)
		interpreter.Stacks["code"].Pop()
	}

	s.Functions["size"] = func() {
		if !interpreter.stackOK("code", 1) || !interpreter.stackOK("integer", 0) {
			return
		}

		c := interpreter.Stacks["code"].Peek().(Code)
		interpreter.Stacks["integer"].Push(c.Length)
	}

	s.Functions["stackdepth"] = func() {
		if !interpreter.stackOK("integer", 0) {
			return
		}

		interpreter.Stacks["integer"].Push(interpreter.Stacks["code"].Len())
	}

	s.Functions["subst"] = func() {
		// TODO
	}

	s.Functions["swap"] = func() {
		interpreter.Stacks["code"].Swap()
	}

	s.Functions["yank"] = func() {
		if !interpreter.stackOK("integer", 1) || !interpreter.stackOK("code", 1) {
			return
		}

		idx := interpreter.Stacks["integer"].Pop().(int64)
		interpreter.Stacks["code"].Yank(idx)
	}

	s.Functions["yankdup"] = func() {
		if !interpreter.stackOK("integer", 1) || !interpreter.stackOK("code", 1) {
			return
		}

		idx := interpreter.Stacks["integer"].Pop().(int64)
		interpreter.Stacks["code"].YankDup(idx)
	}

	return s
}
<|endoftext|>


<commit_before>// Copyright 2017 tsuru 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 (
	"log"
	"time"

	"strconv"

	"github.com/prometheus/client_golang/prometheus"
)

var (
	unitsDesc    = prometheus.NewDesc("tsuru_usage_units", "The current number of started/errored units", []string{"app", "pool", "plan", "team"}, nil)
	nodesDesc    = prometheus.NewDesc("tsuru_usage_nodes", "The current number of nodes", []string{"pool"}, nil)
	servicesDesc = prometheus.NewDesc("tsuru_usage_services", "The current number of service instances", []string{"service", "instance", "team", "plan"}, nil)
	collectErr   = prometheus.NewCounterVec(prometheus.CounterOpts{Name: "tsuru_usage_collector_errors", Help: "The error count while fetching metrics"}, []string{"op"})
	collectHist  = prometheus.NewHistogram(prometheus.HistogramOpts{Name: "tsuru_usage_collector_duration_seconds", Help: "The duration of collector runs"})
)

func init() {
	prometheus.MustRegister(collectErr)
	prometheus.MustRegister(collectHist)
}

type TsuruCollector struct {
	client   *tsuruClient
	services []string
}

func (c *TsuruCollector) Describe(ch chan<- *prometheus.Desc) {
	ch <- unitsDesc
	ch <- nodesDesc
}

func (c *TsuruCollector) Collect(ch chan<- prometheus.Metric) {
	now := time.Now()
	defer func() {
		collectHist.Observe(time.Since(now).Seconds())
	}()
	unitsCounts, err := c.client.fetchUnitsCount()
	if err != nil {
		log.Printf("failed to fetch units metrics: %s", err)
		collectErr.WithLabelValues("units").Inc()
	}
	for _, u := range unitsCounts {
		ch <- prometheus.MustNewConstMetric(unitsDesc, prometheus.GaugeValue, float64(u.count), u.app, u.pool, u.plan, u.team)
	}
	nodesCounts, err := c.client.fetchNodesCount()
	if err != nil {
		log.Printf("failed to fetch nodes metrics: %s", err)
		collectErr.WithLabelValues("nodes").Inc()
	}
	for p, c := range nodesCounts {
		ch <- prometheus.MustNewConstMetric(nodesDesc, prometheus.GaugeValue, float64(c), p)
	}
	instances, err := c.client.fetchServicesInstances(c.services)
	if err != nil {
		log.Printf("failed to fetch services metrics: %s", err)
		collectErr.WithLabelValues("services").Inc()
	}
	for _, i := range instances {
		count := 1
		if str := i.Info["Instances"]; str != "" {
			if v, err := strconv.Atoi(str); err == nil {
				count = v
			}
		}
		ch <- prometheus.MustNewConstMetric(servicesDesc, prometheus.GaugeValue, float64(count), i.Service, i.Name, i.TeamOwner, i.PlanName)
	}
}
<commit_msg>concurrent collector<commit_after>// Copyright 2017 tsuru 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 (
	"log"
	"sync"
	"time"

	"strconv"

	"github.com/prometheus/client_golang/prometheus"
)

var (
	unitsDesc    = prometheus.NewDesc("tsuru_usage_units", "The current number of started/errored units", []string{"app", "pool", "plan", "team"}, nil)
	nodesDesc    = prometheus.NewDesc("tsuru_usage_nodes", "The current number of nodes", []string{"pool"}, nil)
	servicesDesc = prometheus.NewDesc("tsuru_usage_services", "The current number of service instances", []string{"service", "instance", "team", "plan"}, nil)
	collectErr   = prometheus.NewCounterVec(prometheus.CounterOpts{Name: "tsuru_usage_collector_errors", Help: "The error count while fetching metrics"}, []string{"op"})
	collectHist  = prometheus.NewHistogram(prometheus.HistogramOpts{Name: "tsuru_usage_collector_duration_seconds", Help: "The duration of collector runs"})
)

func init() {
	prometheus.MustRegister(collectErr)
	prometheus.MustRegister(collectHist)
}

type TsuruCollector struct {
	client   *tsuruClient
	services []string
}

func (c *TsuruCollector) Describe(ch chan<- *prometheus.Desc) {
	ch <- unitsDesc
	ch <- nodesDesc
}

func (c *TsuruCollector) Collect(ch chan<- prometheus.Metric) {
	now := time.Now()
	defer func() {
		collectHist.Observe(time.Since(now).Seconds())
	}()
	wg := sync.WaitGroup{}
	collects := []func(chan<- prometheus.Metric){c.collectUnits, c.collectNodes, c.collectInstances}
	wg.Add(len(collects))
	for _, collect := range collects {
		go func(f func(chan<- prometheus.Metric)) {
			f(ch)
			wg.Done()
		}(collect)
	}
	wg.Wait()
}

func (c *TsuruCollector) collectUnits(ch chan<- prometheus.Metric) {
	unitsCounts, err := c.client.fetchUnitsCount()
	if err != nil {
		log.Printf("failed to fetch units metrics: %s", err)
		collectErr.WithLabelValues("units").Inc()
	}
	for _, u := range unitsCounts {
		ch <- prometheus.MustNewConstMetric(unitsDesc, prometheus.GaugeValue, float64(u.count), u.app, u.pool, u.plan, u.team)
	}
}

func (c *TsuruCollector) collectNodes(ch chan<- prometheus.Metric) {
	nodesCounts, err := c.client.fetchNodesCount()
	if err != nil {
		log.Printf("failed to fetch nodes metrics: %s", err)
		collectErr.WithLabelValues("nodes").Inc()
	}
	for p, c := range nodesCounts {
		ch <- prometheus.MustNewConstMetric(nodesDesc, prometheus.GaugeValue, float64(c), p)
	}
}

func (c *TsuruCollector) collectInstances(ch chan<- prometheus.Metric) {
	instances, err := c.client.fetchServicesInstances(c.services)
	if err != nil {
		log.Printf("failed to fetch services metrics: %s", err)
		collectErr.WithLabelValues("services").Inc()
	}
	for _, i := range instances {
		count := 1
		if str := i.Info["Instances"]; str != "" {
			if v, err := strconv.Atoi(str); err == nil {
				count = v
			}
		}
		ch <- prometheus.MustNewConstMetric(servicesDesc, prometheus.GaugeValue, float64(count), i.Service, i.Name, i.TeamOwner, i.PlanName)
	}
}
<|endoftext|>


<commit_before>package sms // import "github.com/mndrix/sms-over-xmpp"

import (
	"crypto/rand"
	"encoding/base32"
	"encoding/xml"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	xco "github.com/mndrix/go-xco"
	"github.com/pkg/errors"
)

// ErrIgnoreMessage should be returned to indicate that a message
// should be ignored; as if it never happened.
var ErrIgnoreMessage = errors.New("ignore this message")

// Component represents an SMS-over-XMPP component.
type Component struct {
	config Config

	// xmpp is the XMPP component which handles all interactions
	// with an XMPP server.
	xmpp *xco.Component

	// xmppMutex serializes access to the XMPP component to avoid
	// collisions while talking to the XMPP server.
	xmppMutex sync.Mutex
}

// Main runs a component using the given configuration.  It's the main
// entrypoint for launching your own component if you don't want to
// use the sms-over-xmpp command.
func Main(config Config) {
	sc := &Component{config: config}

	// start goroutine for handling XMPP and HTTP
	xmppDead := sc.runXmppComponent()
	httpDead := sc.runHttpServer()

	for {
		select {
		case _ = <-httpDead:
			log.Printf("HTTP died. Restarting")
			httpDead = sc.runHttpServer()
		case _ = <-xmppDead:
			log.Printf("XMPP died. Restarting")
			time.Sleep(1 * time.Second) // don't hammer server
			xmppDead = sc.runXmppComponent()
		}
	}
}

// runHttpServer creates a goroutine for receiving HTTP requests.
// it returns a channel for monitoring the goroutine's health.
// if that channel closes, the HTTP goroutine has died.
func (sc *Component) runHttpServer() <-chan struct{} {
	config := sc.config
	addr := fmt.Sprintf("%s:%d", config.HttpHost(), config.HttpPort())
	healthCh := make(chan struct{})
	go func() {
		defer func() { close(healthCh) }()
		err := http.ListenAndServe(addr, sc)
		log.Printf("HTTP server error: %s", err)
	}()
	return healthCh
}

// runXmppComponent creates a goroutine for sending and receiving XMPP
// stanzas.  it returns a channel for monitoring the goroutine's health.
// if that channel closes, the XMPP goroutine has died.
func (sc *Component) runXmppComponent() <-chan struct{} {
	config := sc.config
	opts := xco.Options{
		Name:         config.ComponentName(),
		SharedSecret: config.SharedSecret(),
		Address:      fmt.Sprintf("%s:%d", config.XmppHost(), config.XmppPort()),
		Logger:       log.New(os.Stderr, "", log.LstdFlags),
	}

	healthCh := make(chan struct{})
	go func() {
		defer func() { close(healthCh) }()

		c, err := xco.NewComponent(opts)
		if err != nil {
			log.Printf("can't create internal XMPP component: %s", err)
			return
		}

		c.MessageHandler = sc.onMessage
		c.PresenceHandler = sc.onPresence
		c.IqHandler = sc.onIq
		c.UnknownHandler = sc.onUnknown
		sc.setXmpp(c)

		err = c.Run()
		log.Printf("lost XMPP connection: %s", err)
	}()
	return healthCh
}

func (sc *Component) setXmpp(c *xco.Component) {
	sc.xmppMutex.Lock()
	defer func() { sc.xmppMutex.Unlock() }()

	sc.xmpp = c
}

func (sc *Component) onMessage(c *xco.Component, m *xco.Message) error {
	log.Printf("Message: %+v", m)
	if m.Body == "" {
		log.Printf("  ignoring message with empty body")
		return nil
	}

	// convert recipient address into a phone number
	toPhone, err := sc.config.AddressToPhone(m.To)
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		return nil
	default:
		return errors.Wrap(err, "converting 'to' address to phone")
	}

	// convert author's address into a phone number
	fromPhone, err := sc.config.AddressToPhone(m.From)
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		return nil
	default:
		return errors.Wrap(err, "converting 'from' address to phone")
	}

	// choose an SMS provider
	provider, err := sc.config.SmsProvider()
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		return nil
	default:
		return errors.Wrap(err, "choosing an SMS provider")
	}

	// send the message
	err = provider.SendSms(fromPhone, toPhone, m.Body)
	return errors.Wrap(err, "sending SMS")
}

func (sc *Component) onPresence(c *xco.Component, p *xco.Presence) error {
	log.Printf("Presence: %+v", p)
	return nil
}

func (sc *Component) onIq(c *xco.Component, iq *xco.Iq) error {
	log.Printf("Iq: %+v", iq)
	return nil
}

func (sc *Component) onUnknown(c *xco.Component, x *xml.StartElement) error {
	log.Printf("Unknown: %+v", x)
	return nil
}

func (sc *Component) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	msgSid := r.FormValue("MessageSid")
	log.Printf("%s %s (%s)", r.Method, r.URL.Path, msgSid)

	// verify HTTP authentication
	if !sc.isHttpAuthenticated(r) {
		w.Header().Set("WWW-Authenticate", "Basic realm=\"sms-over-xmpp\"")
		w.WriteHeader(http.StatusUnauthorized)
		fmt.Fprintln(w, "Not authorized")
		return
	}

	// which SMS provider is applicable?
	provider, err := sc.config.SmsProvider()
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		msg := "ignored during provider selection"
		log.Println(msg)
		return
	default:
		msg := fmt.Sprintf("ERROR: choosing an SMS provider: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, msg)
		log.Println(msg)
		return
	}

	fromPhone, toPhone, body, err := provider.ReceiveSms(r)

	// convert author's phone number into XMPP address
	from, err := sc.config.PhoneToAddress(fromPhone)
	switch err {
	case nil:
		// all is well. proceed
	case ErrIgnoreMessage:
		msg := "ignored based on From address"
		log.Println(msg)
		return
	default:
		msg := fmt.Sprintf("ERROR: From address %s: %s", fromPhone, err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, msg)
		log.Println(msg)
		return
	}

	// convert recipient's phone number into XMPP address
	to, err := sc.config.PhoneToAddress(toPhone)
	switch err {
	case nil:
		// all is well. proceed
	case ErrIgnoreMessage:
		msg := "ignored based on To address"
		log.Println(msg)
		return
	default:
		msg := fmt.Sprintf("ERROR: To address %s: %s", toPhone, err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, msg)
		log.Println(msg)
		return
	}

	// deliver message over XMPP
	msg := &xco.Message{
		XMLName: xml.Name{
			Local: "message",
			Space: "jabber:component:accept",
		},

		Header: xco.Header{
			From: from,
			To:   to,
			ID:   NewId(),
		},
		Type: "chat",
		Body: body,
	}
	err = sc.xmppSend(msg)
	if err != nil {
		log.Printf("ERROR: can't send message: %s", err)
	}
}

func (sc *Component) isHttpAuthenticated(r *http.Request) bool {
	// config without any HTTP auth allows everything
	conf, ok := sc.config.(CanHttpAuth)
	if !ok {
		return true
	}
	wantUser := conf.HttpUsername()
	wantPass := conf.HttpPassword()
	if wantUser == "" && wantPass == "" {
		return true
	}

	// now we know that HTTP authentication is mandatory
	gotUser, gotPass, ok := r.BasicAuth()
	if !ok {
		return false
	}

	return gotUser == wantUser && gotPass == wantPass
}

// xmppSend sends a single XML stanza over the XMPP connection.  It
// serializes concurrent access to avoid collisions on the wire.
func (sc *Component) xmppSend(msg interface{}) error {
	sc.xmppMutex.Lock()
	defer func() { sc.xmppMutex.Unlock() }()

	return sc.xmpp.Send(msg)
}

// NewId generates a random string which is suitable as an XMPP stanza
// ID.  The string contains enough entropy to be universally unique.
func NewId() string {
	// generate 128 random bits (6 more than standard UUID)
	bytes := make([]byte, 16)
	_, err := rand.Read(bytes)
	if err != nil {
		panic(err)
	}

	// convert them to base 32 encoding
	s := base32.StdEncoding.EncodeToString(bytes)
	return strings.ToLower(strings.TrimRight(s, "="))
}
<commit_msg>Support XEP-0030 service discovery info queries<commit_after>package sms // import "github.com/mndrix/sms-over-xmpp"

import (
	"crypto/rand"
	"encoding/base32"
	"encoding/xml"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	xco "github.com/mndrix/go-xco"
	"github.com/pkg/errors"
)

// ErrIgnoreMessage should be returned to indicate that a message
// should be ignored; as if it never happened.
var ErrIgnoreMessage = errors.New("ignore this message")

// Component represents an SMS-over-XMPP component.
type Component struct {
	config Config

	// xmpp is the XMPP component which handles all interactions
	// with an XMPP server.
	xmpp *xco.Component

	// xmppMutex serializes access to the XMPP component to avoid
	// collisions while talking to the XMPP server.
	xmppMutex sync.Mutex
}

// Main runs a component using the given configuration.  It's the main
// entrypoint for launching your own component if you don't want to
// use the sms-over-xmpp command.
func Main(config Config) {
	sc := &Component{config: config}

	// start goroutine for handling XMPP and HTTP
	xmppDead := sc.runXmppComponent()
	httpDead := sc.runHttpServer()

	for {
		select {
		case _ = <-httpDead:
			log.Printf("HTTP died. Restarting")
			httpDead = sc.runHttpServer()
		case _ = <-xmppDead:
			log.Printf("XMPP died. Restarting")
			time.Sleep(1 * time.Second) // don't hammer server
			xmppDead = sc.runXmppComponent()
		}
	}
}

// runHttpServer creates a goroutine for receiving HTTP requests.
// it returns a channel for monitoring the goroutine's health.
// if that channel closes, the HTTP goroutine has died.
func (sc *Component) runHttpServer() <-chan struct{} {
	config := sc.config
	addr := fmt.Sprintf("%s:%d", config.HttpHost(), config.HttpPort())
	healthCh := make(chan struct{})
	go func() {
		defer func() { close(healthCh) }()
		err := http.ListenAndServe(addr, sc)
		log.Printf("HTTP server error: %s", err)
	}()
	return healthCh
}

// runXmppComponent creates a goroutine for sending and receiving XMPP
// stanzas.  it returns a channel for monitoring the goroutine's health.
// if that channel closes, the XMPP goroutine has died.
func (sc *Component) runXmppComponent() <-chan struct{} {
	config := sc.config
	opts := xco.Options{
		Name:         config.ComponentName(),
		SharedSecret: config.SharedSecret(),
		Address:      fmt.Sprintf("%s:%d", config.XmppHost(), config.XmppPort()),
		Logger:       log.New(os.Stderr, "", log.LstdFlags),
	}

	healthCh := make(chan struct{})
	go func() {
		defer func() { close(healthCh) }()

		c, err := xco.NewComponent(opts)
		if err != nil {
			log.Printf("can't create internal XMPP component: %s", err)
			return
		}

		c.MessageHandler = sc.onMessage
		c.DiscoInfoHandler = sc.onDiscoInfo
		c.PresenceHandler = sc.onPresence
		c.IqHandler = sc.onIq
		c.UnknownHandler = sc.onUnknown
		sc.setXmpp(c)

		err = c.Run()
		log.Printf("lost XMPP connection: %s", err)
	}()
	return healthCh
}

func (sc *Component) setXmpp(c *xco.Component) {
	sc.xmppMutex.Lock()
	defer func() { sc.xmppMutex.Unlock() }()

	sc.xmpp = c
}

func (sc *Component) onMessage(c *xco.Component, m *xco.Message) error {
	log.Printf("Message: %+v", m)
	if m.Body == "" {
		log.Printf("  ignoring message with empty body")
		return nil
	}

	// convert recipient address into a phone number
	toPhone, err := sc.config.AddressToPhone(m.To)
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		return nil
	default:
		return errors.Wrap(err, "converting 'to' address to phone")
	}

	// convert author's address into a phone number
	fromPhone, err := sc.config.AddressToPhone(m.From)
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		return nil
	default:
		return errors.Wrap(err, "converting 'from' address to phone")
	}

	// choose an SMS provider
	provider, err := sc.config.SmsProvider()
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		return nil
	default:
		return errors.Wrap(err, "choosing an SMS provider")
	}

	// send the message
	err = provider.SendSms(fromPhone, toPhone, m.Body)
	return errors.Wrap(err, "sending SMS")
}

func (sc *Component) onDiscoInfo(c *xco.Component, iq *xco.Iq) ([]xco.DiscoIdentity, []xco.DiscoFeature, error) {
	log.Printf("Disco: %+v", iq)
	ids := []xco.DiscoIdentity{
		{
			Category: "gateway",
			Type:     "sms",
			Name:     "SMS over XMPP",
		},
	}
	return ids, nil, nil
}

func (sc *Component) onPresence(c *xco.Component, p *xco.Presence) error {
	log.Printf("Presence: %+v", p)
	return nil
}

func (sc *Component) onIq(c *xco.Component, iq *xco.Iq) error {
	log.Printf("Iq: %+v", iq)
	return nil
}

func (sc *Component) onUnknown(c *xco.Component, x *xml.StartElement) error {
	log.Printf("Unknown: %+v", x)
	return nil
}

func (sc *Component) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	msgSid := r.FormValue("MessageSid")
	log.Printf("%s %s (%s)", r.Method, r.URL.Path, msgSid)

	// verify HTTP authentication
	if !sc.isHttpAuthenticated(r) {
		w.Header().Set("WWW-Authenticate", "Basic realm=\"sms-over-xmpp\"")
		w.WriteHeader(http.StatusUnauthorized)
		fmt.Fprintln(w, "Not authorized")
		return
	}

	// which SMS provider is applicable?
	provider, err := sc.config.SmsProvider()
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		msg := "ignored during provider selection"
		log.Println(msg)
		return
	default:
		msg := fmt.Sprintf("ERROR: choosing an SMS provider: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, msg)
		log.Println(msg)
		return
	}

	fromPhone, toPhone, body, err := provider.ReceiveSms(r)

	// convert author's phone number into XMPP address
	from, err := sc.config.PhoneToAddress(fromPhone)
	switch err {
	case nil:
		// all is well. proceed
	case ErrIgnoreMessage:
		msg := "ignored based on From address"
		log.Println(msg)
		return
	default:
		msg := fmt.Sprintf("ERROR: From address %s: %s", fromPhone, err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, msg)
		log.Println(msg)
		return
	}

	// convert recipient's phone number into XMPP address
	to, err := sc.config.PhoneToAddress(toPhone)
	switch err {
	case nil:
		// all is well. proceed
	case ErrIgnoreMessage:
		msg := "ignored based on To address"
		log.Println(msg)
		return
	default:
		msg := fmt.Sprintf("ERROR: To address %s: %s", toPhone, err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, msg)
		log.Println(msg)
		return
	}

	// deliver message over XMPP
	msg := &xco.Message{
		XMLName: xml.Name{
			Local: "message",
			Space: "jabber:component:accept",
		},

		Header: xco.Header{
			From: from,
			To:   to,
			ID:   NewId(),
		},
		Type: "chat",
		Body: body,
	}
	err = sc.xmppSend(msg)
	if err != nil {
		log.Printf("ERROR: can't send message: %s", err)
	}
}

func (sc *Component) isHttpAuthenticated(r *http.Request) bool {
	// config without any HTTP auth allows everything
	conf, ok := sc.config.(CanHttpAuth)
	if !ok {
		return true
	}
	wantUser := conf.HttpUsername()
	wantPass := conf.HttpPassword()
	if wantUser == "" && wantPass == "" {
		return true
	}

	// now we know that HTTP authentication is mandatory
	gotUser, gotPass, ok := r.BasicAuth()
	if !ok {
		return false
	}

	return gotUser == wantUser && gotPass == wantPass
}

// xmppSend sends a single XML stanza over the XMPP connection.  It
// serializes concurrent access to avoid collisions on the wire.
func (sc *Component) xmppSend(msg interface{}) error {
	sc.xmppMutex.Lock()
	defer func() { sc.xmppMutex.Unlock() }()

	return sc.xmpp.Send(msg)
}

// NewId generates a random string which is suitable as an XMPP stanza
// ID.  The string contains enough entropy to be universally unique.
func NewId() string {
	// generate 128 random bits (6 more than standard UUID)
	bytes := make([]byte, 16)
	_, err := rand.Read(bytes)
	if err != nil {
		panic(err)
	}

	// convert them to base 32 encoding
	s := base32.StdEncoding.EncodeToString(bytes)
	return strings.ToLower(strings.TrimRight(s, "="))
}
<|endoftext|>


<commit_before>// Copyright (c) nano Author. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package nano

import (
	"log"

	"github.com/lonnng/nano/component"
)

var (
	comps = make([]component.Component, 0)
)

func startupComponents() {
	// component initialize hooks
	for _, c := range comps {
		c.Init()
	}

	// component after initialize hooks
	for _, c := range comps {
		c.AfterInit()
	}

	// register all components
	for _, c := range comps {
		if err := handler.register(c); err != nil {
			log.Println(err.Error())
		}
	}

	handler.DumpServices()
}

func shutdownComponents() {
	// reverse call `BeforeShutdown` hooks
	length := len(comps)
	for i := length - 1; i >= 0; i++ {
		comps[i].BeforeShutdown()
	}

	// reverse call `Shutdown` hooks
	for i := length - 1; i >= 0; i++ {
		comps[i].Shutdown()
	}
}
<commit_msg>fix index overflow & go fmt<commit_after>// Copyright (c) nano Author. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package nano

import (
	"github.com/lonnng/nano/component"
	"log"
)

var (
	comps = make([]component.Component, 0)
)

func startupComponents() {
	// component initialize hooks
	for _, c := range comps {
		c.Init()
	}

	// component after initialize hooks
	for _, c := range comps {
		c.AfterInit()
	}

	// register all components
	for _, c := range comps {
		if err := handler.register(c); err != nil {
			log.Println(err.Error())
		}
	}

	handler.DumpServices()
}

func shutdownComponents() {
	// reverse call `BeforeShutdown` hooks
	length := len(comps)
	for i := length - 1; i >= 0; i-- {
		comps[i].BeforeShutdown()
	}

	// reverse call `Shutdown` hooks
	for i := length - 1; i >= 0; i-- {
		comps[i].Shutdown()
	}
}
<|endoftext|>


<commit_before>package redisc

import (
	"io"
	"strings"
	"testing"
	"time"

	"github.com/gomodule/redigo/redis"
	"github.com/mna/redisc/redistest"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// Test the conn.ReadOnly behaviour in a cluster setup with 1 replica per
// node. Runs multiple tests in the same function because setting up
// such a cluster is slow.
func TestConnReadOnlyWithReplicas(t *testing.T) {
	fn, ports := redistest.StartClusterWithReplicas(t, nil)
	defer fn()

	c := &Cluster{}
	testWithReplicaBindRandomWithoutNode(t, c)

	c = &Cluster{StartupNodes: []string{":" + ports[0]}}
	testWithReplicaBindEmptySlot(t, c)

	c = &Cluster{StartupNodes: []string{":" + ports[0]}}
	testWithReplicaClusterRefresh(t, c, ports)

	// at this point the cluster has refreshed its mapping
	testReadWriteFromReplica(t, c, ports[redistest.NumClusterNodes:])

	testReadOnlyWithRandomConn(t, c, ports[redistest.NumClusterNodes:])

	testRetryReadOnlyConn(t, c, ports[:redistest.NumClusterNodes], ports[redistest.NumClusterNodes:])
}

func testRetryReadOnlyConn(t *testing.T, c *Cluster, masters []string, replicas []string) {
	conn := c.Get().(*Conn)
	defer conn.Close()

	assert.NoError(t, ReadOnlyConn(conn), "ReadOnly")
	rc, _ := RetryConn(conn, 4, time.Second)

	// keys "a" and "b" are not in the same slot - bind to "a" and
	// then ask for "b" to force a redirect.
	assert.NoError(t, BindConn(conn, "a"), "Bind")
	addr1 := assertBoundTo(t, conn, replicas)

	if _, err := rc.Do("GET", "b"); assert.NoError(t, err, "GET b") {
		addr2 := assertBoundTo(t, conn, replicas)
		assert.NotEqual(t, addr1, addr2, "Bound to different replica")

		// conn is now bound to the node serving slot "b". Send a READWRITE
		// command and get "b" again, should re-bind to the same slot, but to
		// the master.
		_, err := rc.Do("READWRITE")
		assert.NoError(t, err, "READWRITE")
		if _, err := rc.Do("GET", "b"); assert.NoError(t, err, "GET b") {
			addr3 := assertBoundTo(t, conn, masters)
			assert.NotEqual(t, addr2, addr3, "Bound to the master")
		}
	}
}

// assert that conn is bound to one of the specified ports.
func assertBoundTo(t *testing.T, conn *Conn, ports []string) string {
	conn.mu.Lock()
	addr := conn.boundAddr
	conn.mu.Unlock()

	found := false
	for _, port := range ports {
		if strings.HasSuffix(addr, ":"+port) {
			found = true
			break
		}
	}
	assert.True(t, found, "Bound address")
	return addr
}

func testReadOnlyWithRandomConn(t *testing.T, c *Cluster, replicas []string) {
	conn := c.Get().(*Conn)
	defer conn.Close()

	assert.NoError(t, ReadOnlyConn(conn), "ReadOnlyConn")
	assert.NoError(t, BindConn(conn), "BindConn")

	// it should now be bound to a random replica
	assertBoundTo(t, conn, replicas)
}

func testReadWriteFromReplica(t *testing.T, c *Cluster, replicas []string) {
	conn1 := c.Get()
	defer conn1.Close()

	_, err := conn1.Do("SET", "k1", "a")
	assert.NoError(t, err, "SET on master")

	conn2 := c.Get().(*Conn)
	defer conn2.Close()
	ReadOnlyConn(conn2)

	// can read the key from the replica (may take a moment to replicate,
	// so retry a few times)
	var got string
	deadline := time.Now().Add(100 * time.Millisecond)
	for time.Now().Before(deadline) {
		got, err = redis.String(conn2.Do("GET", "k1"))
		if err != nil && got == "a" {
			break
		}
		time.Sleep(10 * time.Millisecond)
	}
	if assert.NoError(t, err, "GET from replica") {
		assert.Equal(t, "a", got, "expected value")
	}

	// bound address should be a replica
	assertBoundTo(t, conn2, replicas)

	// write command should fail with a MOVED
	if _, err = conn2.Do("SET", "k1", "b"); assert.Error(t, err, "SET on ReadOnly conn") {
		assert.Contains(t, err.Error(), "MOVED", "MOVED error")
	}

	// sending READWRITE switches the connection back to read from master
	_, err = conn2.Do("READWRITE")
	assert.NoError(t, err, "READWRITE")

	// now even a GET fails with a MOVED
	if _, err = conn2.Do("GET", "k1"); assert.Error(t, err, "GET on replica conn after READWRITE") {
		assert.Contains(t, err.Error(), "MOVED", "MOVED error")
	}
}

func testWithReplicaBindEmptySlot(t *testing.T, c *Cluster) {
	conn := c.Get()
	defer conn.Close()

	// key "a" is not in node at [0], so will generate a refresh and connect
	// to a random node (to node at [0]).
	assert.NoError(t, conn.(*Conn).Bind("a"), "Bind to missing slot")
	if _, err := conn.Do("GET", "a"); assert.Error(t, err, "GET") {
		assert.Contains(t, err.Error(), "MOVED", "MOVED error")
	}

	// wait for refreshing to become false again
	c.mu.Lock()
	for c.refreshing {
		c.mu.Unlock()
		time.Sleep(100 * time.Millisecond)
		c.mu.Lock()
	}
	for i, v := range c.mapping {
		if !assert.NotEmpty(t, v, "Addr for %d", i) {
			break
		}
	}
	c.mu.Unlock()
}

func testWithReplicaBindRandomWithoutNode(t *testing.T, c *Cluster) {
	conn := c.Get()
	defer conn.Close()
	if err := conn.(*Conn).Bind(); assert.Error(t, err, "Bind fails") {
		assert.Contains(t, err.Error(), "failed to get a connection", "expected message")
	}
}

func testWithReplicaClusterRefresh(t *testing.T, c *Cluster, ports []string) {
	err := c.Refresh()
	if assert.NoError(t, err, "Refresh") {
		var prev string
		pix := -1
		for ix, node := range c.mapping {
			if assert.Equal(t, 2, len(node), "Mapping for slot %d must have 2 nodes", ix) {
				if node[0] != prev || ix == len(c.mapping)-1 {
					prev = node[0]
					t.Logf("%5d: %s\n", ix, node[0])
					pix++
				}
				if assert.NotEmpty(t, node[0]) {
					split0, split1 := strings.Index(node[0], ":"), strings.Index(node[1], ":")
					assert.Contains(t, ports, node[0][split0+1:], "expected address")
					assert.Contains(t, ports, node[1][split1+1:], "expected address")
				}
			} else {
				break
			}
		}
	}
}

func TestConnReadOnly(t *testing.T) {
	fn, ports := redistest.StartCluster(t, nil)
	defer fn()

	c := &Cluster{
		StartupNodes: []string{":" + ports[0]},
	}
	require.NoError(t, c.Refresh(), "Refresh")

	conn := c.Get()
	defer conn.Close()
	cc := conn.(*Conn)
	assert.NoError(t, cc.ReadOnly(), "ReadOnly")

	// both get and set work, because the connection is on a master
	_, err := cc.Do("SET", "b", 1)
	assert.NoError(t, err, "SET")
	v, err := redis.Int(cc.Do("GET", "b"))
	if assert.NoError(t, err, "GET") {
		assert.Equal(t, 1, v, "expected result")
	}

	conn2 := c.Get()
	defer conn2.Close()
	cc2 := conn2.(*Conn)
	assert.NoError(t, cc2.Bind(), "Bind")
	assert.Error(t, cc2.ReadOnly(), "ReadOnly after Bind")
}

func TestConnBind(t *testing.T) {
	fn, ports := redistest.StartCluster(t, nil)
	defer fn()

	for i, p := range ports {
		ports[i] = ":" + p
	}
	c := &Cluster{
		StartupNodes: ports,
		DialOptions:  []redis.DialOption{redis.DialConnectTimeout(2 * time.Second)},
	}
	require.NoError(t, c.Refresh(), "Refresh")

	conn := c.Get()
	defer conn.Close()

	if err := BindConn(conn, "A", "B"); assert.Error(t, err, "Bind with different keys") {
		assert.Contains(t, err.Error(), "keys do not belong to the same slot", "expected message")
	}
	assert.NoError(t, BindConn(conn, "A"), "Bind")
	if err := BindConn(conn, "B"); assert.Error(t, err, "Bind after Bind") {
		assert.Contains(t, err.Error(), "connection already bound", "expected message")
	}

	conn2 := c.Get()
	defer conn2.Close()

	assert.NoError(t, BindConn(conn2), "Bind without key")
}

func TestConnClose(t *testing.T) {
	c := &Cluster{
		StartupNodes: []string{":6379"},
	}
	conn := c.Get()
	require.NoError(t, conn.Close(), "Close")

	_, err := conn.Do("A")
	if assert.Error(t, err, "Do after Close") {
		assert.Contains(t, err.Error(), "redisc: closed", "expected message")
	}
	if assert.Error(t, conn.Err(), "Err after Close") {
		assert.Contains(t, err.Error(), "redisc: closed", "expected message")
	}
	if assert.Error(t, conn.Close(), "Close after Close") {
		assert.Contains(t, err.Error(), "redisc: closed", "expected message")
	}
	if assert.Error(t, conn.Flush(), "Flush after Close") {
		assert.Contains(t, err.Error(), "redisc: closed", "expected message")
	}
	if assert.Error(t, conn.Send("A"), "Send after Close") {
		assert.Contains(t, err.Error(), "redisc: closed", "expected message")
	}
	_, err = conn.Receive()
	if assert.Error(t, err, "Receive after Close") {
		assert.Contains(t, err.Error(), "redisc: closed", "expected message")
	}
	cc := conn.(*Conn)
	if assert.Error(t, cc.Bind("A"), "Bind after Close") {
		assert.Contains(t, err.Error(), "redisc: closed", "expected message")
	}
	if assert.Error(t, cc.ReadOnly(), "ReadOnly after Close") {
		assert.Contains(t, err.Error(), "redisc: closed", "expected message")
	}
}

func TestIsRedisError(t *testing.T) {
	err := error(redis.Error("CROSSSLOT some message"))
	assert.True(t, IsCrossSlot(err), "CrossSlot")
	assert.False(t, IsTryAgain(err), "CrossSlot")
	err = redis.Error("TRYAGAIN some message")
	assert.False(t, IsCrossSlot(err), "TryAgain")
	assert.True(t, IsTryAgain(err), "TryAgain")
	err = io.EOF
	assert.False(t, IsCrossSlot(err), "EOF")
	assert.False(t, IsTryAgain(err), "EOF")
	err = redis.Error("ERR some error")
	assert.False(t, IsCrossSlot(err), "ERR")
	assert.False(t, IsTryAgain(err), "ERR")
}
<commit_msg>add test for ConnWithTimeout<commit_after>package redisc

import (
	"io"
	"strings"
	"testing"
	"time"

	"github.com/gomodule/redigo/redis"
	"github.com/mna/redisc/redistest"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// Test the conn.ReadOnly behaviour in a cluster setup with 1 replica per
// node. Runs multiple tests in the same function because setting up
// such a cluster is slow.
func TestConnReadOnlyWithReplicas(t *testing.T) {
	fn, ports := redistest.StartClusterWithReplicas(t, nil)
	defer fn()

	c := &Cluster{}
	testWithReplicaBindRandomWithoutNode(t, c)

	c = &Cluster{StartupNodes: []string{":" + ports[0]}}
	testWithReplicaBindEmptySlot(t, c)

	c = &Cluster{StartupNodes: []string{":" + ports[0]}}
	testWithReplicaClusterRefresh(t, c, ports)

	// at this point the cluster has refreshed its mapping
	testReadWriteFromReplica(t, c, ports[redistest.NumClusterNodes:])

	testReadOnlyWithRandomConn(t, c, ports[redistest.NumClusterNodes:])

	testRetryReadOnlyConn(t, c, ports[:redistest.NumClusterNodes], ports[redistest.NumClusterNodes:])
}

func testRetryReadOnlyConn(t *testing.T, c *Cluster, masters []string, replicas []string) {
	conn := c.Get().(*Conn)
	defer conn.Close()

	assert.NoError(t, ReadOnlyConn(conn), "ReadOnly")
	rc, _ := RetryConn(conn, 4, time.Second)

	// keys "a" and "b" are not in the same slot - bind to "a" and
	// then ask for "b" to force a redirect.
	assert.NoError(t, BindConn(conn, "a"), "Bind")
	addr1 := assertBoundTo(t, conn, replicas)

	if _, err := rc.Do("GET", "b"); assert.NoError(t, err, "GET b") {
		addr2 := assertBoundTo(t, conn, replicas)
		assert.NotEqual(t, addr1, addr2, "Bound to different replica")

		// conn is now bound to the node serving slot "b". Send a READWRITE
		// command and get "b" again, should re-bind to the same slot, but to
		// the master.
		_, err := rc.Do("READWRITE")
		assert.NoError(t, err, "READWRITE")
		if _, err := rc.Do("GET", "b"); assert.NoError(t, err, "GET b") {
			addr3 := assertBoundTo(t, conn, masters)
			assert.NotEqual(t, addr2, addr3, "Bound to the master")
		}
	}
}

// assert that conn is bound to one of the specified ports.
func assertBoundTo(t *testing.T, conn *Conn, ports []string) string {
	conn.mu.Lock()
	addr := conn.boundAddr
	conn.mu.Unlock()

	found := false
	for _, port := range ports {
		if strings.HasSuffix(addr, ":"+port) {
			found = true
			break
		}
	}
	assert.True(t, found, "Bound address")
	return addr
}

func testReadOnlyWithRandomConn(t *testing.T, c *Cluster, replicas []string) {
	conn := c.Get().(*Conn)
	defer conn.Close()

	assert.NoError(t, ReadOnlyConn(conn), "ReadOnlyConn")
	assert.NoError(t, BindConn(conn), "BindConn")

	// it should now be bound to a random replica
	assertBoundTo(t, conn, replicas)
}

func testReadWriteFromReplica(t *testing.T, c *Cluster, replicas []string) {
	conn1 := c.Get()
	defer conn1.Close()

	_, err := conn1.Do("SET", "k1", "a")
	assert.NoError(t, err, "SET on master")

	conn2 := c.Get().(*Conn)
	defer conn2.Close()
	ReadOnlyConn(conn2)

	// can read the key from the replica (may take a moment to replicate,
	// so retry a few times)
	var got string
	deadline := time.Now().Add(100 * time.Millisecond)
	for time.Now().Before(deadline) {
		got, err = redis.String(conn2.Do("GET", "k1"))
		if err != nil && got == "a" {
			break
		}
		time.Sleep(10 * time.Millisecond)
	}
	if assert.NoError(t, err, "GET from replica") {
		assert.Equal(t, "a", got, "expected value")
	}

	// bound address should be a replica
	assertBoundTo(t, conn2, replicas)

	// write command should fail with a MOVED
	if _, err = conn2.Do("SET", "k1", "b"); assert.Error(t, err, "SET on ReadOnly conn") {
		assert.Contains(t, err.Error(), "MOVED", "MOVED error")
	}

	// sending READWRITE switches the connection back to read from master
	_, err = conn2.Do("READWRITE")
	assert.NoError(t, err, "READWRITE")

	// now even a GET fails with a MOVED
	if _, err = conn2.Do("GET", "k1"); assert.Error(t, err, "GET on replica conn after READWRITE") {
		assert.Contains(t, err.Error(), "MOVED", "MOVED error")
	}
}

func testWithReplicaBindEmptySlot(t *testing.T, c *Cluster) {
	conn := c.Get()
	defer conn.Close()

	// key "a" is not in node at [0], so will generate a refresh and connect
	// to a random node (to node at [0]).
	assert.NoError(t, conn.(*Conn).Bind("a"), "Bind to missing slot")
	if _, err := conn.Do("GET", "a"); assert.Error(t, err, "GET") {
		assert.Contains(t, err.Error(), "MOVED", "MOVED error")
	}

	// wait for refreshing to become false again
	c.mu.Lock()
	for c.refreshing {
		c.mu.Unlock()
		time.Sleep(100 * time.Millisecond)
		c.mu.Lock()
	}
	for i, v := range c.mapping {
		if !assert.NotEmpty(t, v, "Addr for %d", i) {
			break
		}
	}
	c.mu.Unlock()
}

func testWithReplicaBindRandomWithoutNode(t *testing.T, c *Cluster) {
	conn := c.Get()
	defer conn.Close()
	if err := conn.(*Conn).Bind(); assert.Error(t, err, "Bind fails") {
		assert.Contains(t, err.Error(), "failed to get a connection", "expected message")
	}
}

func testWithReplicaClusterRefresh(t *testing.T, c *Cluster, ports []string) {
	err := c.Refresh()
	if assert.NoError(t, err, "Refresh") {
		var prev string
		pix := -1
		for ix, node := range c.mapping {
			if assert.Equal(t, 2, len(node), "Mapping for slot %d must have 2 nodes", ix) {
				if node[0] != prev || ix == len(c.mapping)-1 {
					prev = node[0]
					t.Logf("%5d: %s\n", ix, node[0])
					pix++
				}
				if assert.NotEmpty(t, node[0]) {
					split0, split1 := strings.Index(node[0], ":"), strings.Index(node[1], ":")
					assert.Contains(t, ports, node[0][split0+1:], "expected address")
					assert.Contains(t, ports, node[1][split1+1:], "expected address")
				}
			} else {
				break
			}
		}
	}
}

func TestConnReadOnly(t *testing.T) {
	fn, ports := redistest.StartCluster(t, nil)
	defer fn()

	c := &Cluster{
		StartupNodes: []string{":" + ports[0]},
	}
	require.NoError(t, c.Refresh(), "Refresh")

	conn := c.Get()
	defer conn.Close()
	cc := conn.(*Conn)
	assert.NoError(t, cc.ReadOnly(), "ReadOnly")

	// both get and set work, because the connection is on a master
	_, err := cc.Do("SET", "b", 1)
	assert.NoError(t, err, "SET")
	v, err := redis.Int(cc.Do("GET", "b"))
	if assert.NoError(t, err, "GET") {
		assert.Equal(t, 1, v, "expected result")
	}

	conn2 := c.Get()
	defer conn2.Close()
	cc2 := conn2.(*Conn)
	assert.NoError(t, cc2.Bind(), "Bind")
	assert.Error(t, cc2.ReadOnly(), "ReadOnly after Bind")
}

func TestConnBind(t *testing.T) {
	fn, ports := redistest.StartCluster(t, nil)
	defer fn()

	for i, p := range ports {
		ports[i] = ":" + p
	}
	c := &Cluster{
		StartupNodes: ports,
		DialOptions:  []redis.DialOption{redis.DialConnectTimeout(2 * time.Second)},
	}
	require.NoError(t, c.Refresh(), "Refresh")

	conn := c.Get()
	defer conn.Close()

	if err := BindConn(conn, "A", "B"); assert.Error(t, err, "Bind with different keys") {
		assert.Contains(t, err.Error(), "keys do not belong to the same slot", "expected message")
	}
	assert.NoError(t, BindConn(conn, "A"), "Bind")
	if err := BindConn(conn, "B"); assert.Error(t, err, "Bind after Bind") {
		assert.Contains(t, err.Error(), "connection already bound", "expected message")
	}

	conn2 := c.Get()
	defer conn2.Close()

	assert.NoError(t, BindConn(conn2), "Bind without key")
}

func TestConnWithTimeout(t *testing.T) {
	fn, ports := redistest.StartCluster(t, nil)
	defer fn()

	c := &Cluster{
		StartupNodes: []string{":" + ports[0]},
		DialOptions: []redis.DialOption{
			redis.DialReadTimeout(time.Second),
		},
	}
	require.NoError(t, c.Refresh(), "Refresh")

	conn1 := c.Get().(*Conn)
	defer conn1.Close()

	_, err1 := conn1.Do("BLPOP", "x", 2)
	assert.Error(t, err1, "Do")

	conn2 := c.Get().(*Conn)
	defer conn2.Close()

	v2, err2 := conn2.DoWithTimeout(time.Second*3, "BLPOP", "x", 2)
	assert.NoError(t, err2, "DoWithTimeout")
	assert.Equal(t, nil, v2, "expected result")

	conn3 := c.Get().(*Conn)
	defer conn3.Close()

	conn3.Send("BLPOP", "x", 2)
	conn3.Flush()
	_, err3 := conn3.Receive()
	assert.Error(t, err3, "Receive")

	conn4 := c.Get().(*Conn)
	defer conn4.Close()

	conn4.Send("BLPOP", "x", 2)
	conn4.Flush()
	v4, err4 := conn4.ReceiveWithTimeout(time.Second * 3)
	assert.NoError(t, err4, "ReceiveWithTimeout")
	assert.Equal(t, nil, v4, "expected result")
}

func TestConnClose(t *testing.T) {
	c := &Cluster{
		StartupNodes: []string{":6379"},
	}
	conn := c.Get()
	require.NoError(t, conn.Close(), "Close")

	_, err := conn.Do("A")
	if assert.Error(t, err, "Do after Close") {
		assert.Contains(t, err.Error(), "redisc: closed", "expected message")
	}
	if assert.Error(t, conn.Err(), "Err after Close") {
		assert.Contains(t, err.Error(), "redisc: closed", "expected message")
	}
	if assert.Error(t, conn.Close(), "Close after Close") {
		assert.Contains(t, err.Error(), "redisc: closed", "expected message")
	}
	if assert.Error(t, conn.Flush(), "Flush after Close") {
		assert.Contains(t, err.Error(), "redisc: closed", "expected message")
	}
	if assert.Error(t, conn.Send("A"), "Send after Close") {
		assert.Contains(t, err.Error(), "redisc: closed", "expected message")
	}
	_, err = conn.Receive()
	if assert.Error(t, err, "Receive after Close") {
		assert.Contains(t, err.Error(), "redisc: closed", "expected message")
	}
	cc := conn.(*Conn)
	if assert.Error(t, cc.Bind("A"), "Bind after Close") {
		assert.Contains(t, err.Error(), "redisc: closed", "expected message")
	}
	if assert.Error(t, cc.ReadOnly(), "ReadOnly after Close") {
		assert.Contains(t, err.Error(), "redisc: closed", "expected message")
	}
}

func TestIsRedisError(t *testing.T) {
	err := error(redis.Error("CROSSSLOT some message"))
	assert.True(t, IsCrossSlot(err), "CrossSlot")
	assert.False(t, IsTryAgain(err), "CrossSlot")
	err = redis.Error("TRYAGAIN some message")
	assert.False(t, IsCrossSlot(err), "TryAgain")
	assert.True(t, IsTryAgain(err), "TryAgain")
	err = io.EOF
	assert.False(t, IsCrossSlot(err), "EOF")
	assert.False(t, IsTryAgain(err), "EOF")
	err = redis.Error("ERR some error")
	assert.False(t, IsCrossSlot(err), "ERR")
	assert.False(t, IsTryAgain(err), "ERR")
}
<|endoftext|>


<commit_before>package effect

import (
	"math"
	"time"

	"github.com/ambientsound/wirelight/blinken/lib"
	colorful "github.com/lucasb-eyer/go-colorful"
)

var waveSine float64 = 0.0

func init() {
	Effects["wave"] = Effect{
		Name:     "Wave",
		Function: wave,
		Delay:    180 * time.Microsecond,
		Palette: Palette{
			"default": colorful.Hcl(0, 0, 0),
		},
	}
}

func wave(e Effect) Effect {
	h, c, l := e.Palette["default"].Hcl()
	bounds := e.Canvas.Bounds()
	xmax := float64(bounds.Max.X)
	xstep := 180.0 / xmax

	FillFunc(e.Canvas, func(x, y int, col colorful.Color) colorful.Color {
		lumAngle := waveSine + (float64(x) * xstep)
		sin := (1 + math.Sin(lib.Rad(lumAngle))) / 4
		val := l + sin
		return colorful.Hcl(h, c, val)
	})

	waveSine += 0.1
	if waveSine >= 180.0 {
		waveSine = -waveSine
	}

	return e
}
<commit_msg>blinken: smoother wave effect.<commit_after>package effect

import (
	"math"
	"time"

	"github.com/ambientsound/wirelight/blinken/lib"
	colorful "github.com/lucasb-eyer/go-colorful"
)

var waveSine float64 = 0.0

func init() {
	Effects["wave"] = Effect{
		Name:     "Wave",
		Function: wave,
		Delay:    400 * time.Microsecond,
		Palette: Palette{
			"default": colorful.Hcl(0, 0, 0),
		},
	}
}

func wave(e Effect) Effect {
	h, s, v := e.Palette["default"].Hsv()
	bounds := e.Canvas.Bounds()
	xstep := 180.0 / float64(bounds.Max.X) // wave length equals one strip length

	FillFunc(e.Canvas, func(x, y int, col colorful.Color) colorful.Color {
		lumAngle := waveSine + (float64(x) * xstep)
		sin := (1 + math.Sin(lib.Rad(lumAngle))) / 4
		val := v + sin
		return colorful.Hsv(h, s, val)
	})

	waveSine += 0.1
	if waveSine >= 180.0 {
		waveSine = -waveSine
	}

	return e
}
<|endoftext|>


<commit_before>// package opengraph is a library for extracting OpenGraph meta-data from an html document.
// See http://ogp.me/ for more information about the OpenGraph project.
package main

import (
	//"errors"
	"fmt"
	"io"
	"strings"

	"code.google.com/p/go.net/html"
)

type OpenGraph struct {
	Title *string
	Type  *string
	Image *string
	Url   *string
}

type ogTag struct {
	property string
	content  string
}

// Extract extracts the OpenGraph data from a html document.
// The input is assumed to be UTF-8 encoded.
func Extract(r io.Reader) (*OpenGraph, error) {

	tags, err := ogAttrs(r)

	if err != nil {
		return nil, err
	}

	fmt.Println(tags)

	return nil, nil

}

// ogAttrs extracts the OpenGraph attributes from meta tags.
func ogAttrs(r io.Reader) ([]ogTag, error) {
	var tags []ogTag
	z := html.NewTokenizer(r)

	for {
		tt := z.Next()
		if tt == html.ErrorToken {
			if z.Err() == io.EOF {
				return tags, nil
			}
			return nil, z.Err()
		}

		t := z.Token()

		if t.Type == html.SelfClosingTagToken && t.Data == "meta" {
			var prop, cont string
			for _, a := range t.Attr {
				switch a.Key {
				case "property":
					prop = a.Val
				case "content":
					cont = a.Val
				}
			}

			if strings.HasPrefix(prop, "og:") {
				tags = append(tags, ogTag{prop, cont})
			}
		}
	}

	return tags, nil
}

// test, remove later
func main() {
	//s := `<p>Links:</p><ul><li><a href="foo">Foo</a><li><a href="/bar/baz">BarBaz</a></ul>`
	s := `<meta property='' content="http://ia.media-imdb.com/images/M/MV5BNjc1NzYwODEyMV5BMl5BanBnXkFtZTcwNTcxMzU1MQ@@._V1_SY1200_CR126,0,630,1200_AL_.jpg" />
	<meta property='og:type' content="video.tv_show" />
    <meta property='fb:app_id' content='115109575169727' />
    <meta property='og:title' content="The Wire (TV Series 2002–2008)" />
    <meta property='og:site_name' content='IMDb' />`
	rdr := strings.NewReader(s)
	(Extract(rdr))
}
<commit_msg>rename'<commit_after>// package opengraph is a library for extracting OpenGraph meta-data from an html document.
// See http://ogp.me/ for more information about the OpenGraph project.
package main

import (
	//"errors"
	"fmt"
	"io"
	"strings"

	"code.google.com/p/go.net/html"
)

type OpenGraph struct {
	Title *string
	Type  *string
	Image *string
	Url   *string
}

type ogAttr struct {
	property string
	content  string
}

// Extract extracts the OpenGraph data from a html document.
// The input is assumed to be UTF-8 encoded.
func Extract(r io.Reader) (*OpenGraph, error) {

	tags, err := ogAttrs(r)

	if err != nil {
		return nil, err
	}

	fmt.Println(tags)

	return nil, nil

}

// ogAttrs extracts the OpenGraph attributes from meta tags.
func ogAttrs(r io.Reader) ([]ogAttr, error) {
	var tags []ogAttr
	z := html.NewTokenizer(r)

	for {
		tt := z.Next()
		if tt == html.ErrorToken {
			if z.Err() == io.EOF {
				return tags, nil
			}
			return nil, z.Err()
		}

		t := z.Token()

		if t.Type == html.SelfClosingTagToken && t.Data == "meta" {
			var prop, cont string
			for _, a := range t.Attr {
				switch a.Key {
				case "property":
					prop = a.Val
				case "content":
					cont = a.Val
				}
			}

			if strings.HasPrefix(prop, "og:") {
				tags = append(tags, ogAttr{prop, cont})
			}
		}
	}

	return tags, nil
}

// test, remove later
func main() {
	//s := `<p>Links:</p><ul><li><a href="foo">Foo</a><li><a href="/bar/baz">BarBaz</a></ul>`
	s := `<meta property='' content="http://ia.media-imdb.com/images/M/MV5BNjc1NzYwODEyMV5BMl5BanBnXkFtZTcwNTcxMzU1MQ@@._V1_SY1200_CR126,0,630,1200_AL_.jpg" />
	<meta property='og:type' content="video.tv_show" />
    <meta property='fb:app_id' content='115109575169727' />
    <meta property='og:title' content="The Wire (TV Series 2002–2008)" />
    <meta property='og:site_name' content='IMDb' />`
	rdr := strings.NewReader(s)
	(Extract(rdr))
}
<|endoftext|>


<commit_before>package opts

import (
	"fmt"
	"math/big"
	"net"
	"path"
	"regexp"
	"strings"

	"github.com/docker/docker/api/types/filters"
	units "github.com/docker/go-units"
)

var (
	alphaRegexp  = regexp.MustCompile(`[a-zA-Z]`)
	domainRegexp = regexp.MustCompile(`^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$`)
)

// ListOpts holds a list of values and a validation function.
type ListOpts struct {
	values    *[]string
	validator ValidatorFctType
}

// NewListOpts creates a new ListOpts with the specified validator.
func NewListOpts(validator ValidatorFctType) ListOpts {
	var values []string
	return *NewListOptsRef(&values, validator)
}

// NewListOptsRef creates a new ListOpts with the specified values and validator.
func NewListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts {
	return &ListOpts{
		values:    values,
		validator: validator,
	}
}

func (opts *ListOpts) String() string {
	return fmt.Sprintf("%v", []string((*opts.values)))
}

// Set validates if needed the input value and adds it to the
// internal slice.
func (opts *ListOpts) Set(value string) error {
	if opts.validator != nil {
		v, err := opts.validator(value)
		if err != nil {
			return err
		}
		value = v
	}
	(*opts.values) = append((*opts.values), value)
	return nil
}

// Delete removes the specified element from the slice.
func (opts *ListOpts) Delete(key string) {
	for i, k := range *opts.values {
		if k == key {
			(*opts.values) = append((*opts.values)[:i], (*opts.values)[i+1:]...)
			return
		}
	}
}

// GetMap returns the content of values in a map in order to avoid
// duplicates.
func (opts *ListOpts) GetMap() map[string]struct{} {
	ret := make(map[string]struct{})
	for _, k := range *opts.values {
		ret[k] = struct{}{}
	}
	return ret
}

// GetAll returns the values of slice.
func (opts *ListOpts) GetAll() []string {
	return (*opts.values)
}

// GetAllOrEmpty returns the values of the slice
// or an empty slice when there are no values.
func (opts *ListOpts) GetAllOrEmpty() []string {
	v := *opts.values
	if v == nil {
		return make([]string, 0)
	}
	return v
}

// Get checks the existence of the specified key.
func (opts *ListOpts) Get(key string) bool {
	for _, k := range *opts.values {
		if k == key {
			return true
		}
	}
	return false
}

// Len returns the amount of element in the slice.
func (opts *ListOpts) Len() int {
	return len((*opts.values))
}

// Type returns a string name for this Option type
func (opts *ListOpts) Type() string {
	return "list"
}

// WithValidator returns the ListOpts with validator set.
func (opts *ListOpts) WithValidator(validator ValidatorFctType) *ListOpts {
	opts.validator = validator
	return opts
}

// NamedOption is an interface that list and map options
// with names implement.
type NamedOption interface {
	Name() string
}

// NamedListOpts is a ListOpts with a configuration name.
// This struct is useful to keep reference to the assigned
// field name in the internal configuration struct.
type NamedListOpts struct {
	name string
	ListOpts
}

var _ NamedOption = &NamedListOpts{}

// NewNamedListOptsRef creates a reference to a new NamedListOpts struct.
func NewNamedListOptsRef(name string, values *[]string, validator ValidatorFctType) *NamedListOpts {
	return &NamedListOpts{
		name:     name,
		ListOpts: *NewListOptsRef(values, validator),
	}
}

// Name returns the name of the NamedListOpts in the configuration.
func (o *NamedListOpts) Name() string {
	return o.name
}

// MapOpts holds a map of values and a validation function.
type MapOpts struct {
	values    map[string]string
	validator ValidatorFctType
}

// Set validates if needed the input value and add it to the
// internal map, by splitting on '='.
func (opts *MapOpts) Set(value string) error {
	if opts.validator != nil {
		v, err := opts.validator(value)
		if err != nil {
			return err
		}
		value = v
	}
	vals := strings.SplitN(value, "=", 2)
	if len(vals) == 1 {
		(opts.values)[vals[0]] = ""
	} else {
		(opts.values)[vals[0]] = vals[1]
	}
	return nil
}

// GetAll returns the values of MapOpts as a map.
func (opts *MapOpts) GetAll() map[string]string {
	return opts.values
}

func (opts *MapOpts) String() string {
	return fmt.Sprintf("%v", map[string]string((opts.values)))
}

// Type returns a string name for this Option type
func (opts *MapOpts) Type() string {
	return "map"
}

// NewMapOpts creates a new MapOpts with the specified map of values and a validator.
func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts {
	if values == nil {
		values = make(map[string]string)
	}
	return &MapOpts{
		values:    values,
		validator: validator,
	}
}

// NamedMapOpts is a MapOpts struct with a configuration name.
// This struct is useful to keep reference to the assigned
// field name in the internal configuration struct.
type NamedMapOpts struct {
	name string
	MapOpts
}

var _ NamedOption = &NamedMapOpts{}

// NewNamedMapOpts creates a reference to a new NamedMapOpts struct.
func NewNamedMapOpts(name string, values map[string]string, validator ValidatorFctType) *NamedMapOpts {
	return &NamedMapOpts{
		name:    name,
		MapOpts: *NewMapOpts(values, validator),
	}
}

// Name returns the name of the NamedMapOpts in the configuration.
func (o *NamedMapOpts) Name() string {
	return o.name
}

// ValidatorFctType defines a validator function that returns a validated string and/or an error.
type ValidatorFctType func(val string) (string, error)

// ValidatorFctListType defines a validator function that returns a validated list of string and/or an error
type ValidatorFctListType func(val string) ([]string, error)

// ValidateIPAddress validates an Ip address.
func ValidateIPAddress(val string) (string, error) {
	var ip = net.ParseIP(strings.TrimSpace(val))
	if ip != nil {
		return ip.String(), nil
	}
	return "", fmt.Errorf("%s is not an ip address", val)
}

// ValidateMACAddress validates a MAC address.
func ValidateMACAddress(val string) (string, error) {
	_, err := net.ParseMAC(strings.TrimSpace(val))
	if err != nil {
		return "", err
	}
	return val, nil
}

// ValidateDNSSearch validates domain for resolvconf search configuration.
// A zero length domain is represented by a dot (.).
func ValidateDNSSearch(val string) (string, error) {
	if val = strings.Trim(val, " "); val == "." {
		return val, nil
	}
	return validateDomain(val)
}

func validateDomain(val string) (string, error) {
	if alphaRegexp.FindString(val) == "" {
		return "", fmt.Errorf("%s is not a valid domain", val)
	}
	ns := domainRegexp.FindSubmatch([]byte(val))
	if len(ns) > 0 && len(ns[1]) < 255 {
		return string(ns[1]), nil
	}
	return "", fmt.Errorf("%s is not a valid domain", val)
}

// ValidateLabel validates that the specified string is a valid label, and returns it.
// Labels are in the form on key=value.
func ValidateLabel(val string) (string, error) {
	if strings.Count(val, "=") < 1 {
		return "", fmt.Errorf("bad attribute format: %s", val)
	}
	return val, nil
}

// ValidateSysctl validates a sysctl and returns it.
func ValidateSysctl(val string) (string, error) {
	validSysctlMap := map[string]bool{
		"kernel.msgmax":          true,
		"kernel.msgmnb":          true,
		"kernel.msgmni":          true,
		"kernel.sem":             true,
		"kernel.shmall":          true,
		"kernel.shmmax":          true,
		"kernel.shmmni":          true,
		"kernel.shm_rmid_forced": true,
	}
	validSysctlPrefixes := []string{
		"net.",
		"fs.mqueue.",
	}
	arr := strings.Split(val, "=")
	if len(arr) < 2 {
		return "", fmt.Errorf("sysctl '%s' is not whitelisted", val)
	}
	if validSysctlMap[arr[0]] {
		return val, nil
	}

	for _, vp := range validSysctlPrefixes {
		if strings.HasPrefix(arr[0], vp) {
			return val, nil
		}
	}
	return "", fmt.Errorf("sysctl '%s' is not whitelisted", val)
}

// FilterOpt is a flag type for validating filters
type FilterOpt struct {
	filter filters.Args
}

// NewFilterOpt returns a new FilterOpt
func NewFilterOpt() FilterOpt {
	return FilterOpt{filter: filters.NewArgs()}
}

func (o *FilterOpt) String() string {
	repr, err := filters.ToParam(o.filter)
	if err != nil {
		return "invalid filters"
	}
	return repr
}

// Set sets the value of the opt by parsing the command line value
func (o *FilterOpt) Set(value string) error {
	var err error
	o.filter, err = filters.ParseFlag(value, o.filter)
	return err
}

// Type returns the option type
func (o *FilterOpt) Type() string {
	return "filter"
}

// Value returns the value of this option
func (o *FilterOpt) Value() filters.Args {
	return o.filter
}

// NanoCPUs is a type for fixed point fractional number.
type NanoCPUs int64

// String returns the string format of the number
func (c *NanoCPUs) String() string {
	return big.NewRat(c.Value(), 1e9).FloatString(3)
}

// Set sets the value of the NanoCPU by passing a string
func (c *NanoCPUs) Set(value string) error {
	cpus, err := ParseCPUs(value)
	*c = NanoCPUs(cpus)
	return err
}

// Type returns the type
func (c *NanoCPUs) Type() string {
	return "decimal"
}

// Value returns the value in int64
func (c *NanoCPUs) Value() int64 {
	return int64(*c)
}

// ParseCPUs takes a string ratio and returns an integer value of nano cpus
func ParseCPUs(value string) (int64, error) {
	cpu, ok := new(big.Rat).SetString(value)
	if !ok {
		return 0, fmt.Errorf("failed to parse %v as a rational number", value)
	}
	nano := cpu.Mul(cpu, big.NewRat(1e9, 1))
	if !nano.IsInt() {
		return 0, fmt.Errorf("value is too precise")
	}
	return nano.Num().Int64(), nil
}

// ParseLink parses and validates the specified string as a link format (name:alias)
func ParseLink(val string) (string, string, error) {
	if val == "" {
		return "", "", fmt.Errorf("empty string specified for links")
	}
	arr := strings.Split(val, ":")
	if len(arr) > 2 {
		return "", "", fmt.Errorf("bad format for links: %s", val)
	}
	if len(arr) == 1 {
		return val, val, nil
	}
	// This is kept because we can actually get a HostConfig with links
	// from an already created container and the format is not `foo:bar`
	// but `/foo:/c1/bar`
	if strings.HasPrefix(arr[0], "/") {
		_, alias := path.Split(arr[1])
		return arr[0][1:], alias, nil
	}
	return arr[0], arr[1], nil
}

// ValidateLink validates that the specified string has a valid link format (containerName:alias).
func ValidateLink(val string) (string, error) {
	_, _, err := ParseLink(val)
	return val, err
}

// MemBytes is a type for human readable memory bytes (like 128M, 2g, etc)
type MemBytes int64

// String returns the string format of the human readable memory bytes
func (m *MemBytes) String() string {
	return units.BytesSize(float64(m.Value()))
}

// Set sets the value of the MemBytes by passing a string
func (m *MemBytes) Set(value string) error {
	val, err := units.RAMInBytes(value)
	*m = MemBytes(val)
	return err
}

// Type returns the type
func (m *MemBytes) Type() string {
	return "bytes"
}

// Value returns the value in int64
func (m *MemBytes) Value() int64 {
	return int64(*m)
}

// UnmarshalJSON is the customized unmarshaler for MemBytes
func (m *MemBytes) UnmarshalJSON(s []byte) error {
	if len(s) <= 2 || s[0] != '"' || s[len(s)-1] != '"' {
		return fmt.Errorf("invalid size: %q", s)
	}
	val, err := units.RAMInBytes(string(s[1 : len(s)-1]))
	*m = MemBytes(val)
	return err
}
<commit_msg>Update opts.MemBytes to disable default, and move `docker run/create/build` to use opts.MemBytes<commit_after>package opts

import (
	"fmt"
	"math/big"
	"net"
	"path"
	"regexp"
	"strings"

	"github.com/docker/docker/api/types/filters"
	units "github.com/docker/go-units"
)

var (
	alphaRegexp  = regexp.MustCompile(`[a-zA-Z]`)
	domainRegexp = regexp.MustCompile(`^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$`)
)

// ListOpts holds a list of values and a validation function.
type ListOpts struct {
	values    *[]string
	validator ValidatorFctType
}

// NewListOpts creates a new ListOpts with the specified validator.
func NewListOpts(validator ValidatorFctType) ListOpts {
	var values []string
	return *NewListOptsRef(&values, validator)
}

// NewListOptsRef creates a new ListOpts with the specified values and validator.
func NewListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts {
	return &ListOpts{
		values:    values,
		validator: validator,
	}
}

func (opts *ListOpts) String() string {
	return fmt.Sprintf("%v", []string((*opts.values)))
}

// Set validates if needed the input value and adds it to the
// internal slice.
func (opts *ListOpts) Set(value string) error {
	if opts.validator != nil {
		v, err := opts.validator(value)
		if err != nil {
			return err
		}
		value = v
	}
	(*opts.values) = append((*opts.values), value)
	return nil
}

// Delete removes the specified element from the slice.
func (opts *ListOpts) Delete(key string) {
	for i, k := range *opts.values {
		if k == key {
			(*opts.values) = append((*opts.values)[:i], (*opts.values)[i+1:]...)
			return
		}
	}
}

// GetMap returns the content of values in a map in order to avoid
// duplicates.
func (opts *ListOpts) GetMap() map[string]struct{} {
	ret := make(map[string]struct{})
	for _, k := range *opts.values {
		ret[k] = struct{}{}
	}
	return ret
}

// GetAll returns the values of slice.
func (opts *ListOpts) GetAll() []string {
	return (*opts.values)
}

// GetAllOrEmpty returns the values of the slice
// or an empty slice when there are no values.
func (opts *ListOpts) GetAllOrEmpty() []string {
	v := *opts.values
	if v == nil {
		return make([]string, 0)
	}
	return v
}

// Get checks the existence of the specified key.
func (opts *ListOpts) Get(key string) bool {
	for _, k := range *opts.values {
		if k == key {
			return true
		}
	}
	return false
}

// Len returns the amount of element in the slice.
func (opts *ListOpts) Len() int {
	return len((*opts.values))
}

// Type returns a string name for this Option type
func (opts *ListOpts) Type() string {
	return "list"
}

// WithValidator returns the ListOpts with validator set.
func (opts *ListOpts) WithValidator(validator ValidatorFctType) *ListOpts {
	opts.validator = validator
	return opts
}

// NamedOption is an interface that list and map options
// with names implement.
type NamedOption interface {
	Name() string
}

// NamedListOpts is a ListOpts with a configuration name.
// This struct is useful to keep reference to the assigned
// field name in the internal configuration struct.
type NamedListOpts struct {
	name string
	ListOpts
}

var _ NamedOption = &NamedListOpts{}

// NewNamedListOptsRef creates a reference to a new NamedListOpts struct.
func NewNamedListOptsRef(name string, values *[]string, validator ValidatorFctType) *NamedListOpts {
	return &NamedListOpts{
		name:     name,
		ListOpts: *NewListOptsRef(values, validator),
	}
}

// Name returns the name of the NamedListOpts in the configuration.
func (o *NamedListOpts) Name() string {
	return o.name
}

// MapOpts holds a map of values and a validation function.
type MapOpts struct {
	values    map[string]string
	validator ValidatorFctType
}

// Set validates if needed the input value and add it to the
// internal map, by splitting on '='.
func (opts *MapOpts) Set(value string) error {
	if opts.validator != nil {
		v, err := opts.validator(value)
		if err != nil {
			return err
		}
		value = v
	}
	vals := strings.SplitN(value, "=", 2)
	if len(vals) == 1 {
		(opts.values)[vals[0]] = ""
	} else {
		(opts.values)[vals[0]] = vals[1]
	}
	return nil
}

// GetAll returns the values of MapOpts as a map.
func (opts *MapOpts) GetAll() map[string]string {
	return opts.values
}

func (opts *MapOpts) String() string {
	return fmt.Sprintf("%v", map[string]string((opts.values)))
}

// Type returns a string name for this Option type
func (opts *MapOpts) Type() string {
	return "map"
}

// NewMapOpts creates a new MapOpts with the specified map of values and a validator.
func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts {
	if values == nil {
		values = make(map[string]string)
	}
	return &MapOpts{
		values:    values,
		validator: validator,
	}
}

// NamedMapOpts is a MapOpts struct with a configuration name.
// This struct is useful to keep reference to the assigned
// field name in the internal configuration struct.
type NamedMapOpts struct {
	name string
	MapOpts
}

var _ NamedOption = &NamedMapOpts{}

// NewNamedMapOpts creates a reference to a new NamedMapOpts struct.
func NewNamedMapOpts(name string, values map[string]string, validator ValidatorFctType) *NamedMapOpts {
	return &NamedMapOpts{
		name:    name,
		MapOpts: *NewMapOpts(values, validator),
	}
}

// Name returns the name of the NamedMapOpts in the configuration.
func (o *NamedMapOpts) Name() string {
	return o.name
}

// ValidatorFctType defines a validator function that returns a validated string and/or an error.
type ValidatorFctType func(val string) (string, error)

// ValidatorFctListType defines a validator function that returns a validated list of string and/or an error
type ValidatorFctListType func(val string) ([]string, error)

// ValidateIPAddress validates an Ip address.
func ValidateIPAddress(val string) (string, error) {
	var ip = net.ParseIP(strings.TrimSpace(val))
	if ip != nil {
		return ip.String(), nil
	}
	return "", fmt.Errorf("%s is not an ip address", val)
}

// ValidateMACAddress validates a MAC address.
func ValidateMACAddress(val string) (string, error) {
	_, err := net.ParseMAC(strings.TrimSpace(val))
	if err != nil {
		return "", err
	}
	return val, nil
}

// ValidateDNSSearch validates domain for resolvconf search configuration.
// A zero length domain is represented by a dot (.).
func ValidateDNSSearch(val string) (string, error) {
	if val = strings.Trim(val, " "); val == "." {
		return val, nil
	}
	return validateDomain(val)
}

func validateDomain(val string) (string, error) {
	if alphaRegexp.FindString(val) == "" {
		return "", fmt.Errorf("%s is not a valid domain", val)
	}
	ns := domainRegexp.FindSubmatch([]byte(val))
	if len(ns) > 0 && len(ns[1]) < 255 {
		return string(ns[1]), nil
	}
	return "", fmt.Errorf("%s is not a valid domain", val)
}

// ValidateLabel validates that the specified string is a valid label, and returns it.
// Labels are in the form on key=value.
func ValidateLabel(val string) (string, error) {
	if strings.Count(val, "=") < 1 {
		return "", fmt.Errorf("bad attribute format: %s", val)
	}
	return val, nil
}

// ValidateSysctl validates a sysctl and returns it.
func ValidateSysctl(val string) (string, error) {
	validSysctlMap := map[string]bool{
		"kernel.msgmax":          true,
		"kernel.msgmnb":          true,
		"kernel.msgmni":          true,
		"kernel.sem":             true,
		"kernel.shmall":          true,
		"kernel.shmmax":          true,
		"kernel.shmmni":          true,
		"kernel.shm_rmid_forced": true,
	}
	validSysctlPrefixes := []string{
		"net.",
		"fs.mqueue.",
	}
	arr := strings.Split(val, "=")
	if len(arr) < 2 {
		return "", fmt.Errorf("sysctl '%s' is not whitelisted", val)
	}
	if validSysctlMap[arr[0]] {
		return val, nil
	}

	for _, vp := range validSysctlPrefixes {
		if strings.HasPrefix(arr[0], vp) {
			return val, nil
		}
	}
	return "", fmt.Errorf("sysctl '%s' is not whitelisted", val)
}

// FilterOpt is a flag type for validating filters
type FilterOpt struct {
	filter filters.Args
}

// NewFilterOpt returns a new FilterOpt
func NewFilterOpt() FilterOpt {
	return FilterOpt{filter: filters.NewArgs()}
}

func (o *FilterOpt) String() string {
	repr, err := filters.ToParam(o.filter)
	if err != nil {
		return "invalid filters"
	}
	return repr
}

// Set sets the value of the opt by parsing the command line value
func (o *FilterOpt) Set(value string) error {
	var err error
	o.filter, err = filters.ParseFlag(value, o.filter)
	return err
}

// Type returns the option type
func (o *FilterOpt) Type() string {
	return "filter"
}

// Value returns the value of this option
func (o *FilterOpt) Value() filters.Args {
	return o.filter
}

// NanoCPUs is a type for fixed point fractional number.
type NanoCPUs int64

// String returns the string format of the number
func (c *NanoCPUs) String() string {
	return big.NewRat(c.Value(), 1e9).FloatString(3)
}

// Set sets the value of the NanoCPU by passing a string
func (c *NanoCPUs) Set(value string) error {
	cpus, err := ParseCPUs(value)
	*c = NanoCPUs(cpus)
	return err
}

// Type returns the type
func (c *NanoCPUs) Type() string {
	return "decimal"
}

// Value returns the value in int64
func (c *NanoCPUs) Value() int64 {
	return int64(*c)
}

// ParseCPUs takes a string ratio and returns an integer value of nano cpus
func ParseCPUs(value string) (int64, error) {
	cpu, ok := new(big.Rat).SetString(value)
	if !ok {
		return 0, fmt.Errorf("failed to parse %v as a rational number", value)
	}
	nano := cpu.Mul(cpu, big.NewRat(1e9, 1))
	if !nano.IsInt() {
		return 0, fmt.Errorf("value is too precise")
	}
	return nano.Num().Int64(), nil
}

// ParseLink parses and validates the specified string as a link format (name:alias)
func ParseLink(val string) (string, string, error) {
	if val == "" {
		return "", "", fmt.Errorf("empty string specified for links")
	}
	arr := strings.Split(val, ":")
	if len(arr) > 2 {
		return "", "", fmt.Errorf("bad format for links: %s", val)
	}
	if len(arr) == 1 {
		return val, val, nil
	}
	// This is kept because we can actually get a HostConfig with links
	// from an already created container and the format is not `foo:bar`
	// but `/foo:/c1/bar`
	if strings.HasPrefix(arr[0], "/") {
		_, alias := path.Split(arr[1])
		return arr[0][1:], alias, nil
	}
	return arr[0], arr[1], nil
}

// ValidateLink validates that the specified string has a valid link format (containerName:alias).
func ValidateLink(val string) (string, error) {
	_, _, err := ParseLink(val)
	return val, err
}

// MemBytes is a type for human readable memory bytes (like 128M, 2g, etc)
type MemBytes int64

// String returns the string format of the human readable memory bytes
func (m *MemBytes) String() string {
	// NOTE: In spf13/pflag/flag.go, "0" is considered as "zero value" while "0 B" is not.
	// We return "0" in case value is 0 here so that the default value is hidden.
	// (Sometimes "default 0 B" is actually misleading)
	if m.Value() != 0 {
		return units.BytesSize(float64(m.Value()))
	}
	return "0"
}

// Set sets the value of the MemBytes by passing a string
func (m *MemBytes) Set(value string) error {
	val, err := units.RAMInBytes(value)
	*m = MemBytes(val)
	return err
}

// Type returns the type
func (m *MemBytes) Type() string {
	return "bytes"
}

// Value returns the value in int64
func (m *MemBytes) Value() int64 {
	return int64(*m)
}

// UnmarshalJSON is the customized unmarshaler for MemBytes
func (m *MemBytes) UnmarshalJSON(s []byte) error {
	if len(s) <= 2 || s[0] != '"' || s[len(s)-1] != '"' {
		return fmt.Errorf("invalid size: %q", s)
	}
	val, err := units.RAMInBytes(string(s[1 : len(s)-1]))
	*m = MemBytes(val)
	return err
}
<|endoftext|>