<commit_before>package jman

import (
	"bytes"
	"errors"
	"io/ioutil"
	"strconv"
	"strings"
	"sync"
)

var (
	// ErrSpecificNode is for when retrieving a node does not return a specific key/value, but perhaps a map
	ErrSpecificNode = errors.New("Could not find a specific node that matched the given path")
)

// JSONFile represents a JSON file and contains the filename and root node
type JSONFile struct {
	filename string
	rootnode *Node
	rw       *sync.RWMutex
}

// NewFile will read the given filename and return a JSONFile struct
func NewFile(filename string) (*JSONFile, error) {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	js, err := New(data)
	if err != nil {
		return nil, err
	}
	rw := &sync.RWMutex{}
	return &JSONFile{filename, js, rw}, nil
}

// Recursively look up a given JSON path
func jsonpath(js *Node, JSONpath string) (*Node, string, error) {
	if JSONpath == "" {
		// Could not find end node
		return js, JSONpath, ErrSpecificNode
	}
	firstpart := JSONpath
	secondpart := ""
	if strings.Contains(JSONpath, ".") {
		fields := strings.SplitN(JSONpath, ".", 2)
		firstpart = fields[0]
		secondpart = fields[1]
	}
	if firstpart == "x" {
		return jsonpath(js, secondpart)
	} else if strings.Contains(firstpart, "[") && strings.Contains(firstpart, "]") {
		fields := strings.SplitN(firstpart, "[", 2)
		name := fields[0]
		if name != "x" {
			js = js.Get(name)
		}
		fields = strings.SplitN(fields[1], "]", 2)
		index, err := strconv.Atoi(fields[0])
		if err != nil {
			return js, JSONpath, errors.New("Invalid index: " + fields[0] + " (" + err.Error() + ")")
		}
		node, ok := js.GetIndex(index)
		if !ok {
			return js, JSONpath, errors.New("Could not find index: " + fields[0])
		}
		return jsonpath(node, secondpart)
	}
	name := firstpart
	if secondpart != "" {
		return js, JSONpath, errors.New("JSON path left unparsed: " + secondpart)
	}
	return js.Get(name), "", nil
}

// GetNode will find the JSON node that corresponds to the given JSON path
func (jf *JSONFile) GetNode(JSONpath string) (*Node, error) {
	foundnode, leftoverpath, err := jsonpath(jf.rootnode, JSONpath)
	if err != nil {
		return nil, err
	}
	if leftoverpath != "" {
		return nil, errors.New("JSON path left unparsed: " + leftoverpath)
	}
	if foundnode == nil {
		return nil, errors.New("Could not lookup: " + JSONpath)
	}
	return foundnode, nil
}

// GetString will find the string that corresponds to the given JSON Path
func (jf *JSONFile) GetString(JSONpath string) (string, error) {
	node, err := jf.GetNode(JSONpath)
	if err != nil {
		return "", err
	}
	return node.String(), nil
}

func (jf *JSONFile) SetString(JSONpath, value string) error {
	firstpart := ""
	lastpart := JSONpath
	if strings.Contains(JSONpath, ".") {
		pos := strings.LastIndex(JSONpath, ".")
		firstpart = JSONpath[:pos]
		lastpart = JSONpath[pos+1:]
	}

	node, _, err := jsonpath(jf.rootnode, firstpart)
	if (err != nil) && (err != ErrSpecificNode) {
		return err
	}

	_, hasNode := node.CheckGet(lastpart)
	if !hasNode {
		return errors.New("Index out of range? Could not set value.")
	}

	// It's weird that simplejson Set does not return an error value
	node.Set(lastpart, value)

	newdata, err := jf.rootnode.EncodePretty()
	if err != nil {
		return err
	}

	return jf.Write(newdata)
}

// Write writes the current JSON data to the file
func (jf *JSONFile) Write(data []byte) error {
	jf.rw.Lock()
	defer jf.rw.Unlock()
	// TODO: Add newline as well?
	return ioutil.WriteFile(jf.filename, data, 0666)
}

// AddJSON adds JSON data at the given JSON path
func (jf *JSONFile) AddJSON(JSONpath, JSONdata string) error {
	firstpart := ""
	lastpart := JSONpath
	if strings.Contains(JSONpath, ".") {
		pos := strings.LastIndex(JSONpath, ".")
		firstpart = JSONpath[:pos]
		lastpart = JSONpath[pos+1:]
	}

	node, _, err := jsonpath(jf.rootnode, firstpart)
	if (err != nil) && (err != ErrSpecificNode) {
		return err
	}

	_, hasNode := node.CheckGet(lastpart)
	if hasNode {
		return errors.New("The JSON path should not point to a single key when adding JSON data.")
	}

	listJSON, err := node.Encode()
	if err != nil {
		return err
	}

	fullJSON, err := jf.rootnode.Encode()
	if err != nil {
		return err
	}

	// TODO: Fork simplejson for a better way of adding data!
	newFullJSON := bytes.Replace(fullJSON, listJSON, badd(listJSON[:len(listJSON)-1], []byte(","+JSONdata+"]")), 1)

	js, err := New(newFullJSON)
	if err != nil {
		return err
	}

	newFullJSON, err = js.EncodePretty()
	if err != nil {
		return err
	}

	return jf.Write(newFullJSON)
}

// SetString sets a value to the given JSON file at the given JSON path
func SetString(filename, JSONpath, value string) error {
	jf, err := NewFile(filename)
	if err != nil {
		return err
	}
	return jf.SetString(JSONpath, value)
}

// AddJSON adds JSON data to the given JSON file at the given JSON path
func AddJSON(filename, JSONpath, JSONdata string) error {
	jf, err := NewFile(filename)
	if err != nil {
		return err
	}
	return jf.AddJSON(JSONpath, JSONdata)
}

// JSONString will find the string that corresponds to the given JSON Path,
// given a filename and a simple JSON path expression.
func GetString(filename, JSONpath string) (string, error) {
	jf, err := NewFile(filename)
	if err != nil {
		return "", err
	}
	return jf.GetString(JSONpath)
}
<commit_msg>golint has no complaints<commit_after>package jman

import (
	"bytes"
	"errors"
	"io/ioutil"
	"strconv"
	"strings"
	"sync"
)

var (
	// ErrSpecificNode is for when retrieving a node does not return a specific key/value, but perhaps a map
	ErrSpecificNode = errors.New("Could not find a specific node that matched the given path")
)

// JSONFile represents a JSON file and contains the filename and root node
type JSONFile struct {
	filename string
	rootnode *Node
	rw       *sync.RWMutex
}

// NewFile will read the given filename and return a JSONFile struct
func NewFile(filename string) (*JSONFile, error) {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	js, err := New(data)
	if err != nil {
		return nil, err
	}
	rw := &sync.RWMutex{}
	return &JSONFile{filename, js, rw}, nil
}

// Recursively look up a given JSON path
func jsonpath(js *Node, JSONpath string) (*Node, string, error) {
	if JSONpath == "" {
		// Could not find end node
		return js, JSONpath, ErrSpecificNode
	}
	firstpart := JSONpath
	secondpart := ""
	if strings.Contains(JSONpath, ".") {
		fields := strings.SplitN(JSONpath, ".", 2)
		firstpart = fields[0]
		secondpart = fields[1]
	}
	if firstpart == "x" {
		return jsonpath(js, secondpart)
	} else if strings.Contains(firstpart, "[") && strings.Contains(firstpart, "]") {
		fields := strings.SplitN(firstpart, "[", 2)
		name := fields[0]
		if name != "x" {
			js = js.Get(name)
		}
		fields = strings.SplitN(fields[1], "]", 2)
		index, err := strconv.Atoi(fields[0])
		if err != nil {
			return js, JSONpath, errors.New("Invalid index: " + fields[0] + " (" + err.Error() + ")")
		}
		node, ok := js.GetIndex(index)
		if !ok {
			return js, JSONpath, errors.New("Could not find index: " + fields[0])
		}
		return jsonpath(node, secondpart)
	}
	name := firstpart
	if secondpart != "" {
		return js, JSONpath, errors.New("JSON path left unparsed: " + secondpart)
	}
	return js.Get(name), "", nil
}

// GetNode will find the JSON node that corresponds to the given JSON path
func (jf *JSONFile) GetNode(JSONpath string) (*Node, error) {
	foundnode, leftoverpath, err := jsonpath(jf.rootnode, JSONpath)
	if err != nil {
		return nil, err
	}
	if leftoverpath != "" {
		return nil, errors.New("JSON path left unparsed: " + leftoverpath)
	}
	if foundnode == nil {
		return nil, errors.New("Could not lookup: " + JSONpath)
	}
	return foundnode, nil
}

// GetString will find the string that corresponds to the given JSON path
func (jf *JSONFile) GetString(JSONpath string) (string, error) {
	node, err := jf.GetNode(JSONpath)
	if err != nil {
		return "", err
	}
	return node.String(), nil
}

// SetString will change the value of the key that the given JSON path points to
func (jf *JSONFile) SetString(JSONpath, value string) error {
	firstpart := ""
	lastpart := JSONpath
	if strings.Contains(JSONpath, ".") {
		pos := strings.LastIndex(JSONpath, ".")
		firstpart = JSONpath[:pos]
		lastpart = JSONpath[pos+1:]
	}

	node, _, err := jsonpath(jf.rootnode, firstpart)
	if (err != nil) && (err != ErrSpecificNode) {
		return err
	}

	_, hasNode := node.CheckGet(lastpart)
	if !hasNode {
		return errors.New("Index out of range? Could not set value.")
	}

	// It's weird that simplejson Set does not return an error value
	node.Set(lastpart, value)

	newdata, err := jf.rootnode.EncodePretty()
	if err != nil {
		return err
	}

	return jf.Write(newdata)
}

// Write writes the current JSON data to the file
func (jf *JSONFile) Write(data []byte) error {
	jf.rw.Lock()
	defer jf.rw.Unlock()
	// TODO: Add newline as well?
	return ioutil.WriteFile(jf.filename, data, 0666)
}

// AddJSON adds JSON data at the given JSON path
func (jf *JSONFile) AddJSON(JSONpath, JSONdata string) error {
	firstpart := ""
	lastpart := JSONpath
	if strings.Contains(JSONpath, ".") {
		pos := strings.LastIndex(JSONpath, ".")
		firstpart = JSONpath[:pos]
		lastpart = JSONpath[pos+1:]
	}

	node, _, err := jsonpath(jf.rootnode, firstpart)
	if (err != nil) && (err != ErrSpecificNode) {
		return err
	}

	_, hasNode := node.CheckGet(lastpart)
	if hasNode {
		return errors.New("The JSON path should not point to a single key when adding JSON data.")
	}

	listJSON, err := node.Encode()
	if err != nil {
		return err
	}

	fullJSON, err := jf.rootnode.Encode()
	if err != nil {
		return err
	}

	// TODO: Fork simplejson for a better way of adding data!
	newFullJSON := bytes.Replace(fullJSON, listJSON, badd(listJSON[:len(listJSON)-1], []byte(","+JSONdata+"]")), 1)

	js, err := New(newFullJSON)
	if err != nil {
		return err
	}

	newFullJSON, err = js.EncodePretty()
	if err != nil {
		return err
	}

	return jf.Write(newFullJSON)
}

// SetString sets a value to the given JSON file at the given JSON path
func SetString(filename, JSONpath, value string) error {
	jf, err := NewFile(filename)
	if err != nil {
		return err
	}
	return jf.SetString(JSONpath, value)
}

// AddJSON adds JSON data to the given JSON file at the given JSON path
func AddJSON(filename, JSONpath, JSONdata string) error {
	jf, err := NewFile(filename)
	if err != nil {
		return err
	}
	return jf.AddJSON(JSONpath, JSONdata)
}

// GetString will find the string that corresponds to the given JSON Path,
// given a filename and a simple JSON path expression.
func GetString(filename, JSONpath string) (string, error) {
	jf, err := NewFile(filename)
	if err != nil {
		return "", err
	}
	return jf.GetString(JSONpath)
}
<|endoftext|>


<commit_before>package zdns

import (
	"bufio"
	"encoding/json"
	"fmt"
	"github.com/miekg/dns"
	_ "io"
	"log"
	"os"
	"strconv"
	"strings"
	"sync"
)

func parseAlexa(line string) (string, int) {
	s := strings.SplitN(line, ",", 1)
	rank, err := strconv.Atoi(s[0])
	if err != nil {
		log.Fatal("Malformed Alexa Top Million file")
	}
	return s[1], rank
}

func makeName(name string, prefix string) (string, bool) {
	if prefix == "" {
		return name, false
	} else {
		return strings.Join([]string{prefix, name}, ""), true
	}
}

func doLookup(g *GlobalLookupFactory, gc *GlobalConf, input <-chan string, output chan<- string, wg *sync.WaitGroup) error {
	f, err := (*g).MakeRoutineFactory()
	if err != nil {
		log.Fatal("Unable to create new routine factory", err.Error())
	}
	for line := range input {
		var res Result
		var rawName string
		var rank int
		if gc.AlexaFormat == true {
			rawName, rank = parseAlexa(line)
			res.AlexaRank = rank
		} else {
			rawName = line
		}
		lookupName, changed := makeName(rawName, gc.NamePrefix)
		res.Domain = lookupName
		if changed {
			res.OriginalDomain = rawName
		}
		l, err := f.MakeLookup()
		if err != nil {
			log.Fatal("Unable to build lookup instance", err)
		}
		innerRes, status, err := l.DoLookup(lookupName)
		res.Status = string(status)
		res.Data = innerRes
		if err != nil {
			res.Error = err.Error()
		}
		jsonRes, err := json.Marshal(res)
		if err != nil {
			log.Fatal("Unable to marshal JSON result", err)
		}
		output <- string(jsonRes)
		fmt.Print(string(jsonRes))
	}
	(*wg).Done()
	return nil
}

// write results from lookup to output file
func doOutput(out <-chan string, path string) error {
	var f *os.File
	if path == "" || path == "-" {
		f = os.Stdout
	} else {
		f, err := os.Open(path)
		if err != nil {
			log.Fatal("unable to open output file:", err.Error())
		}
		defer f.Close()
	}
	for n := range out {
		f.WriteString(n)
		f.WriteString("\n")
	}
	return nil
}

// read input file and put results into channel
func doInput(in chan<- string, path string) error {
	var f *os.File
	if path == "" || path == "-" {
		f = os.Stdin
	} else {
		var err error
		f, err = os.Open(path)
		if err != nil {
			log.Fatal("unable to open output file:", err.Error())
		}
	}
	s := bufio.NewScanner(f)
	for s.Scan() {
		in <- s.Text()
	}
	if err := s.Err(); err != nil {
		log.Fatal("input unable to read file", err)
	}
	close(in)
	return nil
}

func DoLookups(g *GlobalLookupFactory, c *GlobalConf) error {
	// doInput: take lines from input -> inChan
	//	- closes channel when done processing
	// doOutput: take serialized JSON from outChan and write
	// DoLookup:
	//	- n threads that do processing from in and place results in out
	//	- process until inChan closes, then wg.done()
	// Once we processing threads have all finished, wait until the
	// output and metadata threads have completed
	inChan := make(chan string)
	outChan := make(chan string)
	go doOutput(outChan, c.OutputFilePath)
	go doInput(inChan, c.InputFilePath)
	var wg sync.WaitGroup
	wg.Add(c.Threads)
	for i := 0; i < c.Threads; i++ {
		go doLookup(g, c, inChan, outChan, &wg)
	}
	wg.Wait()
	close(outChan)
	return nil
}

func GetDNSServers(path string) ([]string, error) {
	c, err := dns.ClientConfigFromFile(path)
	if err != nil {
		return []string{}, err
	}
	var servers []string
	for _, s := range c.Servers {
		full := strings.Join([]string{s, c.Port}, ":")
		servers = append(servers, full)
	}
	return servers, nil
}
<commit_msg>This scaffolding works<commit_after>package zdns

import (
	"bufio"
	"encoding/json"
	"github.com/miekg/dns"
	_ "io"
	"log"
	"os"
	"strconv"
	"strings"
	"sync"
)

func parseAlexa(line string) (string, int) {
	s := strings.SplitN(line, ",", 1)
	rank, err := strconv.Atoi(s[0])
	if err != nil {
		log.Fatal("Malformed Alexa Top Million file")
	}
	return s[1], rank
}

func makeName(name string, prefix string) (string, bool) {
	if prefix == "" {
		return name, false
	} else {
		return strings.Join([]string{prefix, name}, ""), true
	}
}

func doLookup(g *GlobalLookupFactory, gc *GlobalConf, input <-chan string, output chan<- string, wg *sync.WaitGroup) error {
	f, err := (*g).MakeRoutineFactory()
	if err != nil {
		log.Fatal("Unable to create new routine factory", err.Error())
	}
	for line := range input {
		var res Result
		var rawName string
		var rank int
		if gc.AlexaFormat == true {
			rawName, rank = parseAlexa(line)
			res.AlexaRank = rank
		} else {
			rawName = line
		}
		lookupName, changed := makeName(rawName, gc.NamePrefix)
		res.Domain = lookupName
		if changed {
			res.OriginalDomain = rawName
		}
		l, err := f.MakeLookup()
		if err != nil {
			log.Fatal("Unable to build lookup instance", err)
		}
		innerRes, status, err := l.DoLookup(lookupName)
		res.Status = string(status)
		res.Data = innerRes
		if err != nil {
			res.Error = err.Error()
		}
		jsonRes, err := json.Marshal(res)
		if err != nil {
			log.Fatal("Unable to marshal JSON result", err)
		}
		output <- string(jsonRes)
	}
	(*wg).Done()
	return nil
}

// write results from lookup to output file
func doOutput(out <-chan string, path string, wg *sync.WaitGroup) error {
	var f *os.File
	if path == "" || path == "-" {
		f = os.Stdout
	} else {
		f, err := os.Open(path)
		if err != nil {
			log.Fatal("unable to open output file:", err.Error())
		}
		defer f.Close()
	}
	for n := range out {
		f.WriteString(n)
		f.WriteString("\n")
	}
	(*wg).Done()
	return nil
}

// read input file and put results into channel
func doInput(in chan<- string, path string, wg *sync.WaitGroup) error {
	var f *os.File
	if path == "" || path == "-" {
		f = os.Stdin
	} else {
		var err error
		f, err = os.Open(path)
		if err != nil {
			log.Fatal("unable to open output file:", err.Error())
		}
	}
	s := bufio.NewScanner(f)
	for s.Scan() {
		in <- s.Text()
	}
	if err := s.Err(); err != nil {
		log.Fatal("input unable to read file", err)
	}
	close(in)
	(*wg).Done()
	return nil
}

func DoLookups(g *GlobalLookupFactory, c *GlobalConf) error {
	// doInput: take lines from input -> inChan
	//	- closes channel when done processing
	// doOutput: take serialized JSON from outChan and write
	// DoLookup:
	//	- n threads that do processing from in and place results in out
	//	- process until inChan closes, then wg.done()
	// Once we processing threads have all finished, wait until the
	// output and metadata threads have completed
	inChan := make(chan string)
	outChan := make(chan string)
	var routineWG sync.WaitGroup
	go doOutput(outChan, c.OutputFilePath, &routineWG)
	go doInput(inChan, c.InputFilePath, &routineWG)
	routineWG.Add(2)
	// create pool of worker goroutines
	var lookupWG sync.WaitGroup
	lookupWG.Add(c.Threads)
	for i := 0; i < c.Threads; i++ {
		go doLookup(g, c, inChan, outChan, &lookupWG)
	}
	lookupWG.Wait()
	close(outChan)
	routineWG.Wait()
	return nil
}

func GetDNSServers(path string) ([]string, error) {
	c, err := dns.ClientConfigFromFile(path)
	if err != nil {
		return []string{}, err
	}
	var servers []string
	for _, s := range c.Servers {
		full := strings.Join([]string{s, c.Port}, ":")
		servers = append(servers, full)
	}
	return servers, nil
}
<|endoftext|>


<commit_before>package crane

import (
	"fmt"
)

type UnitOfWork struct {
	targeted       []string
	containers     []string
	order          []string
	requireStarted []string
}

func NewUnitOfWork(graph DependencyGraph, targeted []string) (uow *UnitOfWork, err error) {

	uow = &UnitOfWork{
		targeted:       targeted,
		containers:     targeted,
		order:          []string{},
		requireStarted: []string{},
	}

	// select all containers which we care about
	for {
		c := uow.containers
		initialLenContainers := len(c)
		for _, name := range c {
			dependencies := graph[name]
			if dependencies == nil {
				err = fmt.Errorf("Container %s referenced, but not defined.", name)
				return
			}
			for _, dep := range dependencies.All {
				uow.ensureInContainers(dep)
				if dependencies.requireStarted(dep) {
					uow.ensureInRequireStarted(dep)
				}
			}
		}
		if len(uow.containers) == initialLenContainers {
			break
		}
	}

	// bring containers into order
	for {
		initialLenOrdered := len(uow.order)
		for _, name := range uow.containers {
			if dependencies, ok := graph[name]; ok {
				if dependencies.satisfied() {
					uow.order = append(uow.order, name)
					graph.resolve(name)
				}
			}
		}
		if len(uow.order) == initialLenOrdered {
			break
		}
	}

	if len(uow.order) < len(uow.containers) {
		err = fmt.Errorf("Dependencies for container(s) %s could not be resolved.", uow.targeted)
	} else if len(uow.containers) == 0 {
		err = fmt.Errorf("ERROR: Command cannot be applied to any container.")
	}

	return
}

func (uow *UnitOfWork) Run(cmds []string, excluded []string) {
	for _, container := range uow.Containers() {
		if includes(uow.targeted, container.Name()) {
			container.Run(cmds, excluded, cfg.Path())
		} else if includes(uow.requireStarted, container.Name()) {
			container.Start(excluded, cfg.Path())
		}
	}
}

func (uow *UnitOfWork) Lift(cmds []string, excluded []string, noCache bool) {
	for _, container := range uow.Containers() {
		if includes(uow.targeted, container.Name()) {
			container.Lift(cmds, noCache, excluded, cfg.Path())
		} else if includes(uow.requireStarted, container.Name()) {
			container.Start(excluded, cfg.Path())
		}
	}
}

func (uow *UnitOfWork) Stats() {
	args := []string{"stats"}
	for _, container := range uow.Targeted() {
		if container.Running() {
			args = append(args, container.Name())
		}
	}
	if len(args) > 1 {
		executeCommand("docker", args)
	} else {
		printErrorf("None of the targeted container is running.\n")
	}
}

func (uow *UnitOfWork) Status(noTrunc bool) {
	uow.Targeted().Status(noTrunc)
}

// Push containers.
func (uow *UnitOfWork) Push() {
	for _, container := range uow.Targeted() {
		container.Push()
	}
}

// Unpause containers.
func (uow *UnitOfWork) Unpause() {
	for _, container := range uow.Targeted() {
		container.Unpause()
	}
}

// Pause containers.
func (uow *UnitOfWork) Pause() {
	for _, container := range uow.Targeted().Reversed() {
		container.Pause()
	}
}

// Start containers.
func (uow *UnitOfWork) Start() {
	for _, container := range uow.Containers() {
		if includes(uow.targeted, container.Name()) {
			container.Start(excluded, cfg.Path())
		} else if includes(uow.requireStarted, container.Name()) {
			container.Start(excluded, cfg.Path())
		}
	}
}

// Stop containers.
func (uow *UnitOfWork) Stop() {
	for _, container := range uow.Targeted().Reversed() {
		container.Stop()
	}
}

// Kill containers.
func (uow *UnitOfWork) Kill() {
	for _, container := range uow.Targeted().Reversed() {
		container.Kill()
	}
}

// Rm containers.
func (uow *UnitOfWork) Rm(force bool) {
	for _, container := range uow.Targeted().Reversed() {
		container.Rm(force)
	}
}

// Create containers.
func (uow *UnitOfWork) Create(cmds []string, excluded []string) {
	for _, container := range uow.Containers() {
		if includes(uow.targeted, container.Name()) {
			container.Create(cmds, excluded, cfg.Path())
		} else if includes(uow.requireStarted, container.Name()) {
			container.Start(excluded, cfg.Path())
		}
	}
}

// Provision containers.
func (uow *UnitOfWork) Provision(noCache bool) {
	uow.Targeted().Provision(noCache)
}

// Pull containers.
func (uow *UnitOfWork) PullImage() {
	for _, container := range uow.Targeted() {
		if len(container.Dockerfile()) == 0 {
			container.PullImage()
		}
	}
}

// Log containers.
func (uow *UnitOfWork) Logs(follow bool, timestamps bool, tail string, colorize bool, since string) {
	uow.Targeted().Logs(follow, timestamps, tail, colorize, since)
}

func (uow *UnitOfWork) Containers() Containers {
	c := []Container{}
	for _, name := range uow.order {
		c = append(c, cfg.Container(name))
	}
	return c
}

func (uow *UnitOfWork) Targeted() Containers {
	c := []Container{}
	for _, name := range uow.order {
		if includes(uow.targeted, name) {
			c = append(c, cfg.Container(name))
		}
	}
	return c
}

func (uow *UnitOfWork) Affected() []string {
	c := []string{}
	for _, name := range uow.order {
		if !includes(uow.targeted, name) {
			c = append(c, name)
		}
	}
	return c
}

func (uow *UnitOfWork) ensureInContainers(name string) {
	if !includes(uow.containers, name) {
		uow.containers = append(uow.containers, name)
	}
}

func (uow *UnitOfWork) ensureInRequireStarted(name string) {
	if !includes(uow.requireStarted, name) {
		uow.requireStarted = append(uow.requireStarted, name)
	}
}
<commit_msg>Start non-existant containers too when required<commit_after>package crane

import (
	"fmt"
)

type UnitOfWork struct {
	targeted       []string
	containers     []string
	order          []string
	requireStarted []string
}

func NewUnitOfWork(graph DependencyGraph, targeted []string) (uow *UnitOfWork, err error) {

	uow = &UnitOfWork{
		targeted:       targeted,
		containers:     targeted,
		order:          []string{},
		requireStarted: []string{},
	}

	// select all containers which we care about
	for {
		c := uow.containers
		initialLenContainers := len(c)
		for _, name := range c {
			dependencies := graph[name]
			if dependencies == nil {
				err = fmt.Errorf("Container %s referenced, but not defined.", name)
				return
			}
			for _, dep := range dependencies.All {
				uow.ensureInContainers(dep)
				if dependencies.requireStarted(dep) {
					uow.ensureInRequireStarted(dep)
				}
			}
		}
		if len(uow.containers) == initialLenContainers {
			break
		}
	}

	// bring containers into order
	for {
		initialLenOrdered := len(uow.order)
		for _, name := range uow.containers {
			if dependencies, ok := graph[name]; ok {
				if dependencies.satisfied() {
					uow.order = append(uow.order, name)
					graph.resolve(name)
				}
			}
		}
		if len(uow.order) == initialLenOrdered {
			break
		}
	}

	if len(uow.order) < len(uow.containers) {
		err = fmt.Errorf("Dependencies for container(s) %s could not be resolved.", uow.targeted)
	} else if len(uow.containers) == 0 {
		err = fmt.Errorf("ERROR: Command cannot be applied to any container.")
	}

	return
}

func (uow *UnitOfWork) Run(cmds []string, excluded []string) {
	for _, container := range uow.Containers() {
		if includes(uow.targeted, container.Name()) {
			container.Run(cmds, excluded, cfg.Path())
		} else if includes(uow.requireStarted, container.Name()) || !container.Exists() {
			container.Start(excluded, cfg.Path())
		}
	}
}

func (uow *UnitOfWork) Lift(cmds []string, excluded []string, noCache bool) {
	for _, container := range uow.Containers() {
		if includes(uow.targeted, container.Name()) {
			container.Lift(cmds, noCache, excluded, cfg.Path())
		} else if includes(uow.requireStarted, container.Name()) || !container.Exists() {
			container.Start(excluded, cfg.Path())
		}
	}
}

func (uow *UnitOfWork) Stats() {
	args := []string{"stats"}
	for _, container := range uow.Targeted() {
		if container.Running() {
			args = append(args, container.Name())
		}
	}
	if len(args) > 1 {
		executeCommand("docker", args)
	} else {
		printErrorf("None of the targeted container is running.\n")
	}
}

func (uow *UnitOfWork) Status(noTrunc bool) {
	uow.Targeted().Status(noTrunc)
}

// Push containers.
func (uow *UnitOfWork) Push() {
	for _, container := range uow.Targeted() {
		container.Push()
	}
}

// Unpause containers.
func (uow *UnitOfWork) Unpause() {
	for _, container := range uow.Targeted() {
		container.Unpause()
	}
}

// Pause containers.
func (uow *UnitOfWork) Pause() {
	for _, container := range uow.Targeted().Reversed() {
		container.Pause()
	}
}

// Start containers.
func (uow *UnitOfWork) Start() {
	for _, container := range uow.Containers() {
		if includes(uow.targeted, container.Name()) {
			container.Start(excluded, cfg.Path())
		} else if includes(uow.requireStarted, container.Name()) || !container.Exists() {
			container.Start(excluded, cfg.Path())
		}
	}
}

// Stop containers.
func (uow *UnitOfWork) Stop() {
	for _, container := range uow.Targeted().Reversed() {
		container.Stop()
	}
}

// Kill containers.
func (uow *UnitOfWork) Kill() {
	for _, container := range uow.Targeted().Reversed() {
		container.Kill()
	}
}

// Rm containers.
func (uow *UnitOfWork) Rm(force bool) {
	for _, container := range uow.Targeted().Reversed() {
		container.Rm(force)
	}
}

// Create containers.
func (uow *UnitOfWork) Create(cmds []string, excluded []string) {
	for _, container := range uow.Containers() {
		if includes(uow.targeted, container.Name()) {
			container.Create(cmds, excluded, cfg.Path())
		} else if includes(uow.requireStarted, container.Name()) || !container.Exists() {
			container.Start(excluded, cfg.Path())
		}
	}
}

// Provision containers.
func (uow *UnitOfWork) Provision(noCache bool) {
	uow.Targeted().Provision(noCache)
}

// Pull containers.
func (uow *UnitOfWork) PullImage() {
	for _, container := range uow.Targeted() {
		if len(container.Dockerfile()) == 0 {
			container.PullImage()
		}
	}
}

// Log containers.
func (uow *UnitOfWork) Logs(follow bool, timestamps bool, tail string, colorize bool, since string) {
	uow.Targeted().Logs(follow, timestamps, tail, colorize, since)
}

func (uow *UnitOfWork) Containers() Containers {
	c := []Container{}
	for _, name := range uow.order {
		c = append(c, cfg.Container(name))
	}
	return c
}

func (uow *UnitOfWork) Targeted() Containers {
	c := []Container{}
	for _, name := range uow.order {
		if includes(uow.targeted, name) {
			c = append(c, cfg.Container(name))
		}
	}
	return c
}

func (uow *UnitOfWork) Affected() []string {
	c := []string{}
	for _, name := range uow.order {
		if !includes(uow.targeted, name) {
			c = append(c, name)
		}
	}
	return c
}

func (uow *UnitOfWork) ensureInContainers(name string) {
	if !includes(uow.containers, name) {
		uow.containers = append(uow.containers, name)
	}
}

func (uow *UnitOfWork) ensureInRequireStarted(name string) {
	if !includes(uow.requireStarted, name) {
		uow.requireStarted = append(uow.requireStarted, name)
	}
}
<|endoftext|>


<commit_before>package criteria

import (
	"github.com/viant/assertly"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/data"
	"fmt"
)

//Criterion represent evaluation criterion
type Criterion struct {
	*Predicate
	LeftOperand  interface{}
	Operator     string
	RightOperand interface{}
}

func (c *Criterion) expandOperand(opperand interface{}, state data.Map) interface{} {
	if opperand == nil {
		return nil
	}
	return state.Expand(opperand)
}

//Apply evaluates criterion with supplied context and state map . Dolar prefixed $expression will be expanded before evaluation.
func (c *Criterion) Apply(state data.Map) (bool, error) {

	if c.Predicate != nil && len(c.Predicate.Criteria) > 0 {
		return c.Predicate.Apply(state)
	}

	leftOperand := c.expandOperand(c.LeftOperand, state)
	rightOperand := c.expandOperand(c.RightOperand, state)
	var err error
	var leftNumber, rightNumber float64
	var rootPath = assertly.NewDataPath("/")
	var context = assertly.NewDefaultContext()

	switch c.Operator {
	case "=", ":":
		validation, err := assertly.AssertWithContext(rightOperand, leftOperand, rootPath, context)
		if err != nil {
			return false, err
		}
		fmt.Printf("validation: %v\n", validation.Report())
		return validation.FailedCount == 0, nil
	case "!=", "":
		if _, ok := leftOperand.(string); ok && rightOperand == nil {
			rightOperand = ""
		}
		validation, err := assertly.AssertWithContext(leftOperand, rightOperand, rootPath, context)
		if err != nil {
			return false, err
		}
		return validation.FailedCount > 0, nil
	case ">=":
		if leftNumber, err = toolbox.ToFloat(leftOperand); err == nil {
			if rightNumber, err = toolbox.ToFloat(rightOperand); err == nil {
				return leftNumber >= rightNumber, nil
			}
		}
	case "<=":
		if leftNumber, err = toolbox.ToFloat(leftOperand); err == nil {
			if rightNumber, err = toolbox.ToFloat(rightOperand); err == nil {
				return leftNumber <= rightNumber, nil
			}
		}

	case ">":
		if leftNumber, err = toolbox.ToFloat(leftOperand); err == nil {
			if rightNumber, err = toolbox.ToFloat(rightOperand); err == nil {
				return leftNumber > rightNumber, nil
			}
		}
	case "<":
		if leftNumber, err = toolbox.ToFloat(leftOperand); err == nil {
			if rightNumber, err = toolbox.ToFloat(rightOperand); err == nil {
				return leftNumber < rightNumber, nil
			}
		}
	}
	return false, err
}

//NewCriterion creates a new criterion
func NewCriterion(leftOperand interface{}, operator string, rightOperand interface{}) *Criterion {
	return &Criterion{
		LeftOperand:  leftOperand,
		Operator:     operator,
		RightOperand: rightOperand,
	}
}
<commit_msg>patched evaluator with predicate<commit_after>package criteria

import (
	"github.com/viant/assertly"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/data"
	"fmt"
)

//Criterion represent evaluation criterion
type Criterion struct {
	*Predicate
	LeftOperand  interface{}
	Operator     string
	RightOperand interface{}
}

func (c *Criterion) expandOperand(opperand interface{}, state data.Map) interface{} {
	if opperand == nil {
		return nil
	}
	return state.Expand(opperand)
}

//Apply evaluates criterion with supplied context and state map . Dolar prefixed $expression will be expanded before evaluation.
func (c *Criterion) Apply(state data.Map) (bool, error) {

	if c.Predicate != nil && len(c.Predicate.Criteria) > 0 {
		return c.Predicate.Apply(state)
	}

	leftOperand := c.expandOperand(c.LeftOperand, state)
	rightOperand := c.expandOperand(c.RightOperand, state)
	var err error
	var leftNumber, rightNumber float64
	var rootPath = assertly.NewDataPath("/")
	var context = assertly.NewDefaultContext()

	switch c.Operator {
	case "=", ":":
		validation, err := assertly.AssertWithContext(rightOperand, leftOperand, rootPath, context)
		if err != nil {
			return false, err
		}
		return validation.FailedCount == 0, nil
	case "!=", "":
		if _, ok := leftOperand.(string); ok && rightOperand == nil {
			rightOperand = ""
		}
		validation, err := assertly.AssertWithContext(leftOperand, rightOperand, rootPath, context)
		if err != nil {
			return false, err
		}
		return validation.FailedCount > 0, nil
	case ">=":
		if leftNumber, err = toolbox.ToFloat(leftOperand); err == nil {
			if rightNumber, err = toolbox.ToFloat(rightOperand); err == nil {
				return leftNumber >= rightNumber, nil
			}
		}
	case "<=":
		if leftNumber, err = toolbox.ToFloat(leftOperand); err == nil {
			if rightNumber, err = toolbox.ToFloat(rightOperand); err == nil {
				return leftNumber <= rightNumber, nil
			}
		}

	case ">":
		if leftNumber, err = toolbox.ToFloat(leftOperand); err == nil {
			if rightNumber, err = toolbox.ToFloat(rightOperand); err == nil {
				return leftNumber > rightNumber, nil
			}
		}
	case "<":
		if leftNumber, err = toolbox.ToFloat(leftOperand); err == nil {
			if rightNumber, err = toolbox.ToFloat(rightOperand); err == nil {
				return leftNumber < rightNumber, nil
			}
		}
	}
	return false, err
}

//NewCriterion creates a new criterion
func NewCriterion(leftOperand interface{}, operator string, rightOperand interface{}) *Criterion {
	return &Criterion{
		LeftOperand:  leftOperand,
		Operator:     operator,
		RightOperand: rightOperand,
	}
}
<|endoftext|>


<commit_before>package api

import (
	"errors"
	"reflect"
	"strings"

	"github.com/godbus/dbus"
	"github.com/muka/go-bluetooth/bluez"
	"github.com/muka/go-bluetooth/bluez/profile"
	"github.com/muka/go-bluetooth/emitter"
	"github.com/muka/go-bluetooth/util"
	"github.com/tj/go-debug"
)

var dbgDevice = debug.Debug("bluez:api:Device")

var deviceRegistry = make(map[string]*Device)

// NewDevice creates a new Device
func NewDevice(path string) *Device {

	if _, ok := deviceRegistry[path]; ok {
		// dbgDevice("Reusing cache instance %s", path)
		return deviceRegistry[path]
	}

	d := new(Device)
	d.Path = path
	d.client = profile.NewDevice1(path)

	d.client.GetProperties()
	d.Properties = d.client.Properties
	d.chars = make(map[dbus.ObjectPath]*profile.GattCharacteristic1, 0)

	deviceRegistry[path] = d

	// d.watchProperties()

	return d
}

//ClearDevice free a device struct
func ClearDevice(d *Device) {

	d.Disconnect()
	d.unwatchProperties()
	c, err := d.GetClient()
	if err == nil {
		c.Close()
	}

	if _, ok := deviceRegistry[d.Path]; ok {
		delete(deviceRegistry, d.Path)
	}

}

// ParseDevice parse a Device from a ObjectManager map
func ParseDevice(path dbus.ObjectPath, propsMap map[string]dbus.Variant) (*Device, error) {

	d := new(Device)
	d.Path = string(path)
	d.client = profile.NewDevice1(d.Path)

	props := new(profile.Device1Properties)
	util.MapToStruct(props, propsMap)
	c, err := d.GetClient()
	if err != nil {
		return nil, err
	}
	c.Properties = props

	return d, nil
}

func (d *Device) watchProperties() error {

	dbgDevice("watch-prop: watching properties")

	channel, err := d.client.Register()
	if err != nil {
		return err
	}

	go (func() {
		for {

			if channel == nil {
				dbgDevice("watch-prop: nil channel, exit")
				break
			}

			dbgDevice("watch-prop: waiting updates")
			sig := <-channel

			if sig == nil {
				dbgDevice("watch-prop: nil sig, exit")
				return
			}

			dbgDevice("watch-prop: signal name %s", sig.Name)
			if sig.Name != bluez.PropertiesChanged {
				dbgDevice("Skipped %s vs %s\n", sig.Name, bluez.PropertiesInterface)
				continue
			}

			dbgDevice("Device property changed")
			for i := 0; i < len(sig.Body); i++ {
				dbgDevice("%s -> %s", reflect.TypeOf(sig.Body[i]), sig.Body[i])
			}

			iface := sig.Body[0].(string)
			changes := sig.Body[1].(map[string]dbus.Variant)
			for field, val := range changes {

				// updates [*]Properties struct
				props := d.Properties

				s := reflect.ValueOf(props).Elem()
				// exported field
				f := s.FieldByName(field)
				if f.IsValid() {
					// A Value can be changed only if it is
					// addressable and was not obtained by
					// the use of unexported struct fields.
					if f.CanSet() {
						x := reflect.ValueOf(val.Value())
						f.Set(x)
						dbgDevice("Set props value: %s = %s\n", field, x.Interface())
					}
				}

				dbgDevice("Emit change for %s = %v\n", field, val.Value())
				propChanged := PropertyChangedEvent{string(iface), field, val.Value(), props, d}
				d.Emit("changed", propChanged)
			}
		}
	})()

	return nil
}

//Device return an API to interact with a DBus device
type Device struct {
	Path       string
	Properties *profile.Device1Properties
	client     *profile.Device1
	chars      map[dbus.ObjectPath]*profile.GattCharacteristic1
}

func (d *Device) unwatchProperties() error {
	return d.client.Unregister()
}

//GetClient return a DBus Device1 interface client
func (d *Device) GetClient() (*profile.Device1, error) {
	if d.client == nil {
		return nil, errors.New("Client not available")
	}
	return d.client, nil
}

//GetProperties return the properties for the device
func (d *Device) GetProperties() (*profile.Device1Properties, error) {

	if d == nil {
		return nil, errors.New("Empty device pointer")
	}

	c, err := d.GetClient()
	if err != nil {
		return nil, err
	}

	props, err := c.GetProperties()

	if err != nil {
		return nil, err
	}

	d.Properties = props
	return d.Properties, err
}

//GetProperty return a property value
func (d *Device) GetProperty(name string) (data interface{}, err error) {
	c, err := d.GetClient()
	if err != nil {
		return nil, err
	}
	val, err := c.GetProperty(name)
	if err != nil {
		return nil, err
	}
	return val.Value(), nil
}

//On register callback for event
func (d *Device) On(name string, fn *emitter.Callback) {
	switch name {
	case "changed":
		d.watchProperties()
		break
	}
	emitter.On(d.Path+"."+name, fn)
}

//Off unregister callback for event
func (d *Device) Off(name string, cb *emitter.Callback) {
	switch name {
	case "changed":
		d.unwatchProperties()
		break
	}

	pattern := d.Path + "." + name
	if name != "*" {
		emitter.Off(pattern, cb)
	} else {
		emitter.RemoveListeners(pattern, nil)
	}
}

//Emit an event
func (d *Device) Emit(name string, data interface{}) {
	emitter.Emit(d.Path+"."+name, data)
}

//GetService return a GattService
func (d *Device) GetService(path string) *profile.GattService1 {
	return profile.NewGattService1(path, "org.bluez")
}

//GetChar return a GattService
func (d *Device) GetChar(path string) *profile.GattCharacteristic1 {
	return profile.NewGattCharacteristic1(path)
}

//GetCharByUUID return a GattService by its uuid, return nil if not found
func (d *Device) GetCharByUUID(uuid string) (*profile.GattCharacteristic1, error) {

	uuid = strings.ToUpper(uuid)

	list := d.GetCharsList()

	dbgDevice("Find by uuid, char list %d, cached list %d", len(list), len(d.chars))

	var deviceFound *profile.GattCharacteristic1

	for _, path := range list {

		// use cache
		_, ok := d.chars[path]
		if !ok {
			d.chars[path] = profile.NewGattCharacteristic1(string(path))
		}

		props := d.chars[path].Properties
		cuuid := strings.ToUpper(props.UUID)

		if cuuid == uuid {
			dbgDevice("Found char %s", uuid)
			deviceFound = d.chars[path]
		}
	}

	if deviceFound == nil {
		dbgDevice("Characteristic not Found: %s ", uuid)
	}

	return deviceFound, nil
}

//GetCharsList return a device characteristics
func (d *Device) GetCharsList() []dbus.ObjectPath {

	var chars []dbus.ObjectPath

	if len(d.chars) != 0 {

		for objpath := range d.chars {
			chars = append(chars, objpath)
		}

		dbgDevice("Cached %d chars", len(chars))
		return chars
	}

	dbgDevice("Scanning chars by ObjectPath")
	list := GetManager().GetObjects()
	for objpath := range *list {
		path := string(objpath)
		if !strings.HasPrefix(path, d.Path) {
			continue
		}
		charPos := strings.Index(path, "char")
		if charPos == -1 {
			continue
		}
		if strings.Index(path[charPos:], "desc") != -1 {
			continue
		}

		chars = append(chars, objpath)
	}

	dbgDevice("Found %d chars", len(chars))
	return chars
}

//IsConnected check if connected to the device
func (d *Device) IsConnected() bool {

	props, _ := d.GetProperties()

	if props == nil {
		return false
	}

	return props.Connected
}

//Connect to device
func (d *Device) Connect() error {

	c, err := d.GetClient()
	if err != nil {
		return err
	}

	err = c.Connect()
	if err != nil {
		return err
	}
	return nil
}

//Disconnect from a device
func (d *Device) Disconnect() error {
	c, err := d.GetClient()
	if err != nil {
		return err
	}
	c.Disconnect()
	return nil
}

//Pair a device
func (d *Device) Pair() error {
	c, err := d.GetClient()
	if err != nil {
		return err
	}
	c.Pair()
	return nil
}
<commit_msg>Fix nil map panic<commit_after>package api

import (
	"errors"
	"reflect"
	"strings"

	"github.com/godbus/dbus"
	"github.com/muka/go-bluetooth/bluez"
	"github.com/muka/go-bluetooth/bluez/profile"
	"github.com/muka/go-bluetooth/emitter"
	"github.com/muka/go-bluetooth/util"
	"github.com/tj/go-debug"
)

var dbgDevice = debug.Debug("bluez:api:Device")

var deviceRegistry = make(map[string]*Device)

// NewDevice creates a new Device
func NewDevice(path string) *Device {

	if _, ok := deviceRegistry[path]; ok {
		// dbgDevice("Reusing cache instance %s", path)
		return deviceRegistry[path]
	}

	d := new(Device)
	d.Path = path
	d.client = profile.NewDevice1(path)

	d.client.GetProperties()
	d.Properties = d.client.Properties
	d.chars = make(map[dbus.ObjectPath]*profile.GattCharacteristic1, 0)

	deviceRegistry[path] = d

	// d.watchProperties()

	return d
}

//ClearDevice free a device struct
func ClearDevice(d *Device) {

	d.Disconnect()
	d.unwatchProperties()
	c, err := d.GetClient()
	if err == nil {
		c.Close()
	}

	if _, ok := deviceRegistry[d.Path]; ok {
		delete(deviceRegistry, d.Path)
	}

}

// ParseDevice parse a Device from a ObjectManager map
func ParseDevice(path dbus.ObjectPath, propsMap map[string]dbus.Variant) (*Device, error) {

	d := new(Device)
	d.Path = string(path)
	d.client = profile.NewDevice1(d.Path)

	props := new(profile.Device1Properties)
	util.MapToStruct(props, propsMap)
	c, err := d.GetClient()
	if err != nil {
		return nil, err
	}
	c.Properties = props
	d.chars = make(map[dbus.ObjectPath]*profile.GattCharacteristic1, 0)

	return d, nil
}

func (d *Device) watchProperties() error {

	dbgDevice("watch-prop: watching properties")

	channel, err := d.client.Register()
	if err != nil {
		return err
	}

	go (func() {
		for {

			if channel == nil {
				dbgDevice("watch-prop: nil channel, exit")
				break
			}

			dbgDevice("watch-prop: waiting updates")
			sig := <-channel

			if sig == nil {
				dbgDevice("watch-prop: nil sig, exit")
				return
			}

			dbgDevice("watch-prop: signal name %s", sig.Name)
			if sig.Name != bluez.PropertiesChanged {
				dbgDevice("Skipped %s vs %s\n", sig.Name, bluez.PropertiesInterface)
				continue
			}

			dbgDevice("Device property changed")
			for i := 0; i < len(sig.Body); i++ {
				dbgDevice("%s -> %s", reflect.TypeOf(sig.Body[i]), sig.Body[i])
			}

			iface := sig.Body[0].(string)
			changes := sig.Body[1].(map[string]dbus.Variant)
			for field, val := range changes {

				// updates [*]Properties struct
				props := d.Properties

				s := reflect.ValueOf(props).Elem()
				// exported field
				f := s.FieldByName(field)
				if f.IsValid() {
					// A Value can be changed only if it is
					// addressable and was not obtained by
					// the use of unexported struct fields.
					if f.CanSet() {
						x := reflect.ValueOf(val.Value())
						f.Set(x)
						dbgDevice("Set props value: %s = %s\n", field, x.Interface())
					}
				}

				dbgDevice("Emit change for %s = %v\n", field, val.Value())
				propChanged := PropertyChangedEvent{string(iface), field, val.Value(), props, d}
				d.Emit("changed", propChanged)
			}
		}
	})()

	return nil
}

//Device return an API to interact with a DBus device
type Device struct {
	Path       string
	Properties *profile.Device1Properties
	client     *profile.Device1
	chars      map[dbus.ObjectPath]*profile.GattCharacteristic1
}

func (d *Device) unwatchProperties() error {
	return d.client.Unregister()
}

//GetClient return a DBus Device1 interface client
func (d *Device) GetClient() (*profile.Device1, error) {
	if d.client == nil {
		return nil, errors.New("Client not available")
	}
	return d.client, nil
}

//GetProperties return the properties for the device
func (d *Device) GetProperties() (*profile.Device1Properties, error) {

	if d == nil {
		return nil, errors.New("Empty device pointer")
	}

	c, err := d.GetClient()
	if err != nil {
		return nil, err
	}

	props, err := c.GetProperties()

	if err != nil {
		return nil, err
	}

	d.Properties = props
	return d.Properties, err
}

//GetProperty return a property value
func (d *Device) GetProperty(name string) (data interface{}, err error) {
	c, err := d.GetClient()
	if err != nil {
		return nil, err
	}
	val, err := c.GetProperty(name)
	if err != nil {
		return nil, err
	}
	return val.Value(), nil
}

//On register callback for event
func (d *Device) On(name string, fn *emitter.Callback) {
	switch name {
	case "changed":
		d.watchProperties()
		break
	}
	emitter.On(d.Path+"."+name, fn)
}

//Off unregister callback for event
func (d *Device) Off(name string, cb *emitter.Callback) {
	switch name {
	case "changed":
		d.unwatchProperties()
		break
	}

	pattern := d.Path + "." + name
	if name != "*" {
		emitter.Off(pattern, cb)
	} else {
		emitter.RemoveListeners(pattern, nil)
	}
}

//Emit an event
func (d *Device) Emit(name string, data interface{}) {
	emitter.Emit(d.Path+"."+name, data)
}

//GetService return a GattService
func (d *Device) GetService(path string) *profile.GattService1 {
	return profile.NewGattService1(path, "org.bluez")
}

//GetChar return a GattService
func (d *Device) GetChar(path string) *profile.GattCharacteristic1 {
	return profile.NewGattCharacteristic1(path)
}

//GetCharByUUID return a GattService by its uuid, return nil if not found
func (d *Device) GetCharByUUID(uuid string) (*profile.GattCharacteristic1, error) {

	uuid = strings.ToUpper(uuid)

	list := d.GetCharsList()

	dbgDevice("Find by uuid, char list %d, cached list %d", len(list), len(d.chars))

	var deviceFound *profile.GattCharacteristic1

	for _, path := range list {

		// use cache
		_, ok := d.chars[path]
		if !ok {
			d.chars[path] = profile.NewGattCharacteristic1(string(path))
		}

		props := d.chars[path].Properties
		cuuid := strings.ToUpper(props.UUID)

		if cuuid == uuid {
			dbgDevice("Found char %s", uuid)
			deviceFound = d.chars[path]
		}
	}

	if deviceFound == nil {
		dbgDevice("Characteristic not Found: %s ", uuid)
	}

	return deviceFound, nil
}

//GetCharsList return a device characteristics
func (d *Device) GetCharsList() []dbus.ObjectPath {

	var chars []dbus.ObjectPath

	if len(d.chars) != 0 {

		for objpath := range d.chars {
			chars = append(chars, objpath)
		}

		dbgDevice("Cached %d chars", len(chars))
		return chars
	}

	dbgDevice("Scanning chars by ObjectPath")
	list := GetManager().GetObjects()
	for objpath := range *list {
		path := string(objpath)
		if !strings.HasPrefix(path, d.Path) {
			continue
		}
		charPos := strings.Index(path, "char")
		if charPos == -1 {
			continue
		}
		if strings.Index(path[charPos:], "desc") != -1 {
			continue
		}

		chars = append(chars, objpath)
	}

	dbgDevice("Found %d chars", len(chars))
	return chars
}

//IsConnected check if connected to the device
func (d *Device) IsConnected() bool {

	props, _ := d.GetProperties()

	if props == nil {
		return false
	}

	return props.Connected
}

//Connect to device
func (d *Device) Connect() error {

	c, err := d.GetClient()
	if err != nil {
		return err
	}

	err = c.Connect()
	if err != nil {
		return err
	}
	return nil
}

//Disconnect from a device
func (d *Device) Disconnect() error {
	c, err := d.GetClient()
	if err != nil {
		return err
	}
	c.Disconnect()
	return nil
}

//Pair a device
func (d *Device) Pair() error {
	c, err := d.GetClient()
	if err != nil {
		return err
	}
	c.Pair()
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package libkbfs

import (
	"fmt"
	"sync"
	"sync/atomic"

	lru "github.com/hashicorp/golang-lru"
	"github.com/keybase/kbfs/kbfsblock"
	"github.com/keybase/kbfs/kbfshash"
	"github.com/keybase/kbfs/tlf"
)

type idCacheKey struct {
	tlf           tlf.ID
	plaintextHash kbfshash.RawDefaultHash
}

// BlockCacheStandard implements the BlockCache interface by storing
// blocks in an in-memory LRU cache.  Clean blocks are identified
// internally by just their block ID (since blocks are immutable and
// content-addressable).
type BlockCacheStandard struct {
	cleanBytesCapacity uint64

	ids *lru.Cache

	cleanTransient *lru.Cache

	cleanLock      sync.RWMutex
	cleanPermanent map[kbfsblock.ID]Block

	bytesLock       sync.Mutex
	cleanTotalBytes uint64
}

// NewBlockCacheStandard constructs a new BlockCacheStandard instance
// with the given transient capacity (in number of entries) and the
// clean bytes capacity, which is the total of number of bytes allowed
// between the transient and permanent clean caches.  If putting a
// block will exceed this bytes capacity, transient entries are
// evicted until the block will fit in capacity.
func NewBlockCacheStandard(transientCapacity int,
	cleanBytesCapacity uint64) *BlockCacheStandard {
	b := &BlockCacheStandard{
		cleanBytesCapacity: cleanBytesCapacity,
		cleanPermanent:     make(map[kbfsblock.ID]Block),
	}

	if transientCapacity > 0 {
		var err error
		// TODO: Plumb error up.
		b.ids, err = lru.New(transientCapacity)
		if err != nil {
			return nil
		}

		b.cleanTransient, err = lru.NewWithEvict(transientCapacity, b.onEvict)
		if err != nil {
			return nil
		}
	}
	return b
}

// GetWithPrefetch implements the BlockCache interface for BlockCacheStandard.
func (b *BlockCacheStandard) GetWithPrefetch(ptr BlockPointer) (Block, bool, error) {
	if b.cleanTransient != nil {
		if tmp, ok := b.cleanTransient.Get(ptr.ID); ok {
			block, ok := tmp.(Block)
			if !ok {
				return nil, false, BadDataError{ptr.ID}
			}
			return block, true, nil
		}
	}

	block := func() Block {
		b.cleanLock.RLock()
		defer b.cleanLock.RUnlock()
		return b.cleanPermanent[ptr.ID]
	}()
	if block != nil {
		return block, true, nil
	}

	return nil, false, NoSuchBlockError{ptr.ID}
}

// Get implements the BlockCache interface for BlockCacheStandard.
func (b *BlockCacheStandard) Get(ptr BlockPointer) (Block, error) {
	block, _, err := b.GetWithPrefetch(ptr)
	return block, err
}

func getCachedBlockSize(block Block) uint32 {
	// Get the size of the block.  For direct file blocks, use the
	// length of the plaintext contents.  For everything else, just
	// approximate the plaintext size using the encoding size.
	switch b := block.(type) {
	case *FileBlock:
		if b.IsInd {
			return b.GetEncodedSize()
		}
		return uint32(len(b.Contents))
	default:
		return block.GetEncodedSize()
	}
}

func (b *BlockCacheStandard) onEvict(key interface{}, value interface{}) {
	block, ok := value.(Block)
	if !ok {
		return
	}

	b.bytesLock.Lock()
	defer b.bytesLock.Unlock()
	b.cleanTotalBytes -= uint64(getCachedBlockSize(block))
}

// CheckForKnownPtr implements the BlockCache interface for BlockCacheStandard.
func (b *BlockCacheStandard) CheckForKnownPtr(tlf tlf.ID, block *FileBlock) (
	BlockPointer, error) {
	if block.IsInd {
		return BlockPointer{}, NotDirectFileBlockError{}
	}

	if b.ids == nil {
		return BlockPointer{}, nil
	}

	key := idCacheKey{tlf, block.UpdateHash()}
	tmp, ok := b.ids.Get(key)
	if !ok {
		return BlockPointer{}, nil
	}

	ptr, ok := tmp.(BlockPointer)
	if !ok {
		return BlockPointer{}, fmt.Errorf("Unexpected cached id: %v", tmp)
	}
	return ptr, nil
}

// SetCleanBytesCapacity implements the BlockCache interface for
// BlockCacheStandard.
func (b *BlockCacheStandard) SetCleanBytesCapacity(capacity uint64) {
	atomic.StoreUint64(&b.cleanBytesCapacity, capacity)
}

// GetCleanBytesCapacity implements the BlockCache interface for
// BlockCacheStandard.
func (b *BlockCacheStandard) GetCleanBytesCapacity() (capacity uint64) {
	return atomic.LoadUint64(&b.cleanBytesCapacity)
}

func (b *BlockCacheStandard) makeRoomForSize(size uint64) bool {
	if b.cleanTransient == nil {
		return false
	}

	oldLen := b.cleanTransient.Len() + 1
	doUnlock := true
	b.bytesLock.Lock()
	defer func() {
		if doUnlock {
			b.bytesLock.Unlock()
		}
	}()

	cleanBytesCapacity := b.GetCleanBytesCapacity()

	// Evict items from the cache until the bytes capacity is lower
	// than the total capacity (or until no items are removed).
	for b.cleanTotalBytes+size > cleanBytesCapacity {
		// Unlock while removing, since onEvict needs the lock and
		// cleanTransient.Len() takes the LRU mutex (which could lead
		// to a deadlock with onEvict).  TODO: either change
		// `cleanTransient` into an `lru.SimpleLRU` and protect it
		// with our own lock, or build our own LRU that can evict
		// based on total bytes.  See #250 and KBFS-1404 for a longer
		// discussion.
		b.bytesLock.Unlock()
		doUnlock = false
		if oldLen == b.cleanTransient.Len() {
			break
		}
		oldLen = b.cleanTransient.Len()
		b.cleanTransient.RemoveOldest()
		doUnlock = true
		b.bytesLock.Lock()
	}
	if b.cleanTotalBytes+size > cleanBytesCapacity {
		// There must be too many permanent clean blocks, so we
		// couldn't make room.
		return false
	}
	// Only count clean bytes if we actually have a transient cache.
	b.cleanTotalBytes += size
	return true
}

// PutWithPrefetch implements the BlockCache interface for BlockCacheStandard.
func (b *BlockCacheStandard) PutWithPrefetch(
	ptr BlockPointer, tlf tlf.ID, block Block, lifetime BlockCacheLifetime,
	hasPrefetched bool) error {

	madeRoom := false

	switch lifetime {
	case NoCacheEntry:
		return nil

	case TransientEntry:
		// If it's the right type of block, store the hash -> ID mapping.
		if fBlock, isFileBlock := block.(*FileBlock); b.ids != nil && isFileBlock && !fBlock.IsInd {

			key := idCacheKey{tlf, fBlock.GetHash()}
			// zero out the refnonce, it doesn't matter
			ptr.RefNonce = kbfsblock.ZeroRefNonce
			b.ids.Add(key, ptr)
		}
		if b.cleanTransient == nil {
			return nil
		}
		// Cache it later, once we know there's room
		defer func() {
			if madeRoom {
				b.cleanTransient.Add(ptr.ID, block)
			}
		}()

	case PermanentEntry:
		func() {
			b.cleanLock.Lock()
			defer b.cleanLock.Unlock()
			b.cleanPermanent[ptr.ID] = block
		}()

	default:
		return fmt.Errorf("Unknown lifetime %v", lifetime)
	}

	// We must make room whether the cache is transient or permanent
	size := uint64(getCachedBlockSize(block))
	madeRoom = b.makeRoomForSize(size)

	return nil
}

// Put implements the BlockCache interface for BlockCacheStandard.
func (b *BlockCacheStandard) Put(
	ptr BlockPointer, tlf tlf.ID, block Block, lifetime BlockCacheLifetime) error {
	// Default should be to assume that a prefetch has happened, and thus it
	// won't trigger prefetches in the future.
	return b.PutWithPrefetch(ptr, tlf, block, lifetime, true)
}

// DeletePermanent implements the BlockCache interface for
// BlockCacheStandard.
func (b *BlockCacheStandard) DeletePermanent(id kbfsblock.ID) error {
	b.cleanLock.Lock()
	defer b.cleanLock.Unlock()
	block, ok := b.cleanPermanent[id]
	if ok {
		delete(b.cleanPermanent, id)
		b.bytesLock.Lock()
		defer b.bytesLock.Unlock()
		b.cleanTotalBytes -= uint64(getCachedBlockSize(block))
	}
	return nil
}

// DeleteTransient implements the BlockCache interface for BlockCacheStandard.
func (b *BlockCacheStandard) DeleteTransient(
	ptr BlockPointer, tlf tlf.ID) error {
	if b.cleanTransient == nil {
		return nil
	}

	// If the block is cached and a file block, delete the known
	// pointer as well.
	if tmp, ok := b.cleanTransient.Get(ptr.ID); ok {
		block, ok := tmp.(Block)
		if !ok {
			return BadDataError{ptr.ID}
		}

		// Remove the key if it exists
		if fBlock, ok := block.(*FileBlock); b.ids != nil && ok &&
			!fBlock.IsInd {
			_, hash := kbfshash.DoRawDefaultHash(fBlock.Contents)
			key := idCacheKey{tlf, hash}
			b.ids.Remove(key)
		}

		b.cleanTransient.Remove(ptr.ID)
	}
	return nil
}

// DeleteKnownPtr implements the BlockCache interface for BlockCacheStandard.
func (b *BlockCacheStandard) DeleteKnownPtr(tlf tlf.ID, block *FileBlock) error {
	if block.IsInd {
		return NotDirectFileBlockError{}
	}

	if b.ids == nil {
		return nil
	}

	_, hash := kbfshash.DoRawDefaultHash(block.Contents)
	key := idCacheKey{tlf, hash}
	b.ids.Remove(key)
	return nil
}
<commit_msg>bcache: add hasPrefetched boolean to cache<commit_after>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package libkbfs

import (
	"fmt"
	"sync"
	"sync/atomic"

	lru "github.com/hashicorp/golang-lru"
	"github.com/keybase/kbfs/kbfsblock"
	"github.com/keybase/kbfs/kbfshash"
	"github.com/keybase/kbfs/tlf"
)

type blockContainer struct {
	block         Block
	hasPrefetched bool
}

type idCacheKey struct {
	tlf           tlf.ID
	plaintextHash kbfshash.RawDefaultHash
}

// BlockCacheStandard implements the BlockCache interface by storing
// blocks in an in-memory LRU cache.  Clean blocks are identified
// internally by just their block ID (since blocks are immutable and
// content-addressable).
type BlockCacheStandard struct {
	cleanBytesCapacity uint64

	ids *lru.Cache

	cleanTransient *lru.Cache

	cleanLock      sync.RWMutex
	cleanPermanent map[kbfsblock.ID]Block

	bytesLock       sync.Mutex
	cleanTotalBytes uint64
}

// NewBlockCacheStandard constructs a new BlockCacheStandard instance
// with the given transient capacity (in number of entries) and the
// clean bytes capacity, which is the total of number of bytes allowed
// between the transient and permanent clean caches.  If putting a
// block will exceed this bytes capacity, transient entries are
// evicted until the block will fit in capacity.
func NewBlockCacheStandard(transientCapacity int,
	cleanBytesCapacity uint64) *BlockCacheStandard {
	b := &BlockCacheStandard{
		cleanBytesCapacity: cleanBytesCapacity,
		cleanPermanent:     make(map[kbfsblock.ID]Block),
	}

	if transientCapacity > 0 {
		var err error
		// TODO: Plumb error up.
		b.ids, err = lru.New(transientCapacity)
		if err != nil {
			return nil
		}

		b.cleanTransient, err = lru.NewWithEvict(transientCapacity, b.onEvict)
		if err != nil {
			return nil
		}
	}
	return b
}

// GetWithPrefetch implements the BlockCache interface for BlockCacheStandard.
func (b *BlockCacheStandard) GetWithPrefetch(ptr BlockPointer) (Block, bool, error) {
	if b.cleanTransient != nil {
		if tmp, ok := b.cleanTransient.Get(ptr.ID); ok {
			bc, ok := tmp.(blockContainer)
			if !ok {
				return nil, false, BadDataError{ptr.ID}
			}
			return bc.block, bc.hasPrefetched, nil
		}
	}

	block := func() Block {
		b.cleanLock.RLock()
		defer b.cleanLock.RUnlock()
		return b.cleanPermanent[ptr.ID]
	}()
	if block != nil {
		return block, true, nil
	}

	return nil, false, NoSuchBlockError{ptr.ID}
}

// Get implements the BlockCache interface for BlockCacheStandard.
func (b *BlockCacheStandard) Get(ptr BlockPointer) (Block, error) {
	block, _, err := b.GetWithPrefetch(ptr)
	return block, err
}

func getCachedBlockSize(block Block) uint32 {
	// Get the size of the block.  For direct file blocks, use the
	// length of the plaintext contents.  For everything else, just
	// approximate the plaintext size using the encoding size.
	switch b := block.(type) {
	case *FileBlock:
		if b.IsInd {
			return b.GetEncodedSize()
		}
		return uint32(len(b.Contents))
	default:
		return block.GetEncodedSize()
	}
}

func (b *BlockCacheStandard) onEvict(key interface{}, value interface{}) {
	bc, ok := value.(blockContainer)
	if !ok {
		return
	}
	block := bc.block

	b.bytesLock.Lock()
	defer b.bytesLock.Unlock()
	b.cleanTotalBytes -= uint64(getCachedBlockSize(block))
}

// CheckForKnownPtr implements the BlockCache interface for BlockCacheStandard.
func (b *BlockCacheStandard) CheckForKnownPtr(tlf tlf.ID, block *FileBlock) (
	BlockPointer, error) {
	if block.IsInd {
		return BlockPointer{}, NotDirectFileBlockError{}
	}

	if b.ids == nil {
		return BlockPointer{}, nil
	}

	key := idCacheKey{tlf, block.UpdateHash()}
	tmp, ok := b.ids.Get(key)
	if !ok {
		return BlockPointer{}, nil
	}

	ptr, ok := tmp.(BlockPointer)
	if !ok {
		return BlockPointer{}, fmt.Errorf("Unexpected cached id: %v", tmp)
	}
	return ptr, nil
}

// SetCleanBytesCapacity implements the BlockCache interface for
// BlockCacheStandard.
func (b *BlockCacheStandard) SetCleanBytesCapacity(capacity uint64) {
	atomic.StoreUint64(&b.cleanBytesCapacity, capacity)
}

// GetCleanBytesCapacity implements the BlockCache interface for
// BlockCacheStandard.
func (b *BlockCacheStandard) GetCleanBytesCapacity() (capacity uint64) {
	return atomic.LoadUint64(&b.cleanBytesCapacity)
}

func (b *BlockCacheStandard) makeRoomForSize(size uint64) bool {
	if b.cleanTransient == nil {
		return false
	}

	oldLen := b.cleanTransient.Len() + 1
	doUnlock := true
	b.bytesLock.Lock()
	defer func() {
		if doUnlock {
			b.bytesLock.Unlock()
		}
	}()

	cleanBytesCapacity := b.GetCleanBytesCapacity()

	// Evict items from the cache until the bytes capacity is lower
	// than the total capacity (or until no items are removed).
	for b.cleanTotalBytes+size > cleanBytesCapacity {
		// Unlock while removing, since onEvict needs the lock and
		// cleanTransient.Len() takes the LRU mutex (which could lead
		// to a deadlock with onEvict).  TODO: either change
		// `cleanTransient` into an `lru.SimpleLRU` and protect it
		// with our own lock, or build our own LRU that can evict
		// based on total bytes.  See #250 and KBFS-1404 for a longer
		// discussion.
		b.bytesLock.Unlock()
		doUnlock = false
		if oldLen == b.cleanTransient.Len() {
			break
		}
		oldLen = b.cleanTransient.Len()
		b.cleanTransient.RemoveOldest()
		doUnlock = true
		b.bytesLock.Lock()
	}
	if b.cleanTotalBytes+size > cleanBytesCapacity {
		// There must be too many permanent clean blocks, so we
		// couldn't make room.
		return false
	}
	// Only count clean bytes if we actually have a transient cache.
	b.cleanTotalBytes += size
	return true
}

// PutWithPrefetch implements the BlockCache interface for BlockCacheStandard.
func (b *BlockCacheStandard) PutWithPrefetch(
	ptr BlockPointer, tlf tlf.ID, block Block, lifetime BlockCacheLifetime,
	hasPrefetched bool) error {

	madeRoom := false

	switch lifetime {
	case NoCacheEntry:
		return nil

	case TransientEntry:
		// If it's the right type of block, store the hash -> ID mapping.
		if fBlock, isFileBlock := block.(*FileBlock); b.ids != nil && isFileBlock && !fBlock.IsInd {

			key := idCacheKey{tlf, fBlock.GetHash()}
			// zero out the refnonce, it doesn't matter
			ptr.RefNonce = kbfsblock.ZeroRefNonce
			b.ids.Add(key, ptr)
		}
		if b.cleanTransient == nil {
			return nil
		}
		// Cache it later, once we know there's room
		defer func() {
			if madeRoom {
				b.cleanTransient.Add(ptr.ID, blockContainer{block, hasPrefetched})
			}
		}()

	case PermanentEntry:
		func() {
			b.cleanLock.Lock()
			defer b.cleanLock.Unlock()
			b.cleanPermanent[ptr.ID] = block
		}()

	default:
		return fmt.Errorf("Unknown lifetime %v", lifetime)
	}

	// We must make room whether the cache is transient or permanent
	size := uint64(getCachedBlockSize(block))
	madeRoom = b.makeRoomForSize(size)

	return nil
}

// Put implements the BlockCache interface for BlockCacheStandard.
func (b *BlockCacheStandard) Put(
	ptr BlockPointer, tlf tlf.ID, block Block, lifetime BlockCacheLifetime) error {
	// Default should be to assume that a prefetch has happened, and thus it
	// won't trigger prefetches in the future.
	return b.PutWithPrefetch(ptr, tlf, block, lifetime, true)
}

// DeletePermanent implements the BlockCache interface for
// BlockCacheStandard.
func (b *BlockCacheStandard) DeletePermanent(id kbfsblock.ID) error {
	b.cleanLock.Lock()
	defer b.cleanLock.Unlock()
	block, ok := b.cleanPermanent[id]
	if ok {
		delete(b.cleanPermanent, id)
		b.bytesLock.Lock()
		defer b.bytesLock.Unlock()
		b.cleanTotalBytes -= uint64(getCachedBlockSize(block))
	}
	return nil
}

// DeleteTransient implements the BlockCache interface for BlockCacheStandard.
func (b *BlockCacheStandard) DeleteTransient(
	ptr BlockPointer, tlf tlf.ID) error {
	if b.cleanTransient == nil {
		return nil
	}

	// If the block is cached and a file block, delete the known
	// pointer as well.
	if tmp, ok := b.cleanTransient.Get(ptr.ID); ok {
		bc, ok := tmp.(blockContainer)
		if !ok {
			return BadDataError{ptr.ID}
		}
		block := bc.block

		// Remove the key if it exists
		if fBlock, ok := block.(*FileBlock); b.ids != nil && ok &&
			!fBlock.IsInd {
			_, hash := kbfshash.DoRawDefaultHash(fBlock.Contents)
			key := idCacheKey{tlf, hash}
			b.ids.Remove(key)
		}

		b.cleanTransient.Remove(ptr.ID)
	}
	return nil
}

// DeleteKnownPtr implements the BlockCache interface for BlockCacheStandard.
func (b *BlockCacheStandard) DeleteKnownPtr(tlf tlf.ID, block *FileBlock) error {
	if block.IsInd {
		return NotDirectFileBlockError{}
	}

	if b.ids == nil {
		return nil
	}

	_, hash := kbfshash.DoRawDefaultHash(block.Contents)
	key := idCacheKey{tlf, hash}
	b.ids.Remove(key)
	return nil
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"sync"
	"time"

	"github.com/docker/swarm/cluster"
)

// EventsHandler broadcasts events to multiple client listeners.
type eventsHandler struct {
	sync.RWMutex
	ws map[string]io.Writer
	cs map[string]chan struct{}
}

// NewEventsHandler creates a new EventsHandler for a cluster.
// The new eventsHandler is initialized with no writers or channels.
func newEventsHandler() *eventsHandler {
	return &eventsHandler{
		ws: make(map[string]io.Writer),
		cs: make(map[string]chan struct{}),
	}
}

// Add adds the writer and a new channel for the remote address.
func (eh *eventsHandler) Add(remoteAddr string, w io.Writer) {
	eh.Lock()
	eh.ws[remoteAddr] = w
	eh.cs[remoteAddr] = make(chan struct{})
	eh.Unlock()
}

// Wait waits on a signal from the remote address.
func (eh *eventsHandler) Wait(remoteAddr string, until int64) {

	timer := time.NewTimer(0)
	timer.Stop()
	if until > 0 {
		dur := time.Unix(until, 0).Sub(time.Now())
		timer = time.NewTimer(dur)
	}

	// subscribe to http client close event
	w := eh.ws[remoteAddr]
	var closeNotify <-chan bool
	if closeNotifier, ok := w.(http.CloseNotifier); ok {
		closeNotify = closeNotifier.CloseNotify()
	}

	select {
	case <-eh.cs[remoteAddr]:
	case <-closeNotify:
	case <-timer.C: // `--until` timeout
		close(eh.cs[remoteAddr])
	}
	eh.cleanupHandler(remoteAddr)
}

func (eh *eventsHandler) cleanupHandler(remoteAddr string) {
	eh.Lock()
	// the maps are expected to have the same keys
	delete(eh.cs, remoteAddr)
	delete(eh.ws, remoteAddr)
	eh.Unlock()

}

// Handle writes information about a cluster event to each remote address in the cluster that has been added to the events handler.
// After an unsuccessful write to a remote address, the associated channel is closed and the address is removed from the events handler.
func (eh *eventsHandler) Handle(e *cluster.Event) error {
	eh.RLock()

	// remove this hack once 1.10 is broadly adopted
	from := e.From
	e.From = e.From + " node:" + e.Engine.Name

	// Attributes will be nil if the event was sent by engine < 1.10
	if e.Actor.Attributes == nil {
		e.Actor.Attributes = make(map[string]string)
	}
	e.Actor.Attributes["node.name"] = e.Engine.Name
	e.Actor.Attributes["node.id"] = e.Engine.ID
	e.Actor.Attributes["node.addr"] = e.Engine.Addr
	e.Actor.Attributes["node.ip"] = e.Engine.IP

	data, err := json.Marshal(e)
	e.From = from
	if err != nil {
		return err
	}

	// remove the node field once 1.10 is broadly adopted & interlock stop relying on it
	node := fmt.Sprintf(",%q:{%q:%q,%q:%q,%q:%q,%q:%q}}",
		"node",
		"Name", e.Engine.Name,
		"Id", e.Engine.ID,
		"Addr", e.Engine.Addr,
		"Ip", e.Engine.IP,
	)

	// insert Node field
	data = data[:len(data)-1]
	data = append(data, []byte(node)...)

	var failed []string

	for key, w := range eh.ws {
		if _, err := fmt.Fprintf(w, string(data)); err != nil {
			// collect them to handle later under Lock
			failed = append(failed, key)
			continue
		}

		if f, ok := w.(http.Flusher); ok {
			f.Flush()
		}
	}
	eh.RUnlock()
	eh.Lock()
	if len(failed) > 0 {
		for _, key := range failed {
			if ch, ok := eh.cs[key]; ok {
				close(ch)
			}
			delete(eh.cs, key)
		}
	}

	eh.Unlock()

	return nil
}

// Size returns the number of remote addresses that the events handler currently contains.
func (eh *eventsHandler) Size() int {
	eh.RLock()
	defer eh.RUnlock()
	return len(eh.ws)
}
<commit_msg>add read lock for eventsHandler when only it is necessary.<commit_after>package api

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"sync"
	"time"

	"github.com/docker/swarm/cluster"
)

// EventsHandler broadcasts events to multiple client listeners.
type eventsHandler struct {
	sync.RWMutex
	ws map[string]io.Writer
	cs map[string]chan struct{}
}

// NewEventsHandler creates a new EventsHandler for a cluster.
// The new eventsHandler is initialized with no writers or channels.
func newEventsHandler() *eventsHandler {
	return &eventsHandler{
		ws: make(map[string]io.Writer),
		cs: make(map[string]chan struct{}),
	}
}

// Add adds the writer and a new channel for the remote address.
func (eh *eventsHandler) Add(remoteAddr string, w io.Writer) {
	eh.Lock()
	eh.ws[remoteAddr] = w
	eh.cs[remoteAddr] = make(chan struct{})
	eh.Unlock()
}

// Wait waits on a signal from the remote address.
func (eh *eventsHandler) Wait(remoteAddr string, until int64) {

	timer := time.NewTimer(0)
	timer.Stop()
	if until > 0 {
		dur := time.Unix(until, 0).Sub(time.Now())
		timer = time.NewTimer(dur)
	}

	// subscribe to http client close event
	w := eh.ws[remoteAddr]
	var closeNotify <-chan bool
	if closeNotifier, ok := w.(http.CloseNotifier); ok {
		closeNotify = closeNotifier.CloseNotify()
	}

	select {
	case <-eh.cs[remoteAddr]:
	case <-closeNotify:
	case <-timer.C: // `--until` timeout
		close(eh.cs[remoteAddr])
	}
	eh.cleanupHandler(remoteAddr)
}

func (eh *eventsHandler) cleanupHandler(remoteAddr string) {
	eh.Lock()
	// the maps are expected to have the same keys
	delete(eh.cs, remoteAddr)
	delete(eh.ws, remoteAddr)
	eh.Unlock()

}

// Handle writes information about a cluster event to each remote address in the cluster that has been added to the events handler.
// After an unsuccessful write to a remote address, the associated channel is closed and the address is removed from the events handler.
func (eh *eventsHandler) Handle(e *cluster.Event) error {
	// remove this hack once 1.10 is broadly adopted
	from := e.From
	e.From = e.From + " node:" + e.Engine.Name

	// Attributes will be nil if the event was sent by engine < 1.10
	if e.Actor.Attributes == nil {
		e.Actor.Attributes = make(map[string]string)
	}
	e.Actor.Attributes["node.name"] = e.Engine.Name
	e.Actor.Attributes["node.id"] = e.Engine.ID
	e.Actor.Attributes["node.addr"] = e.Engine.Addr
	e.Actor.Attributes["node.ip"] = e.Engine.IP

	data, err := json.Marshal(e)
	e.From = from
	if err != nil {
		return err
	}

	// remove the node field once 1.10 is broadly adopted & interlock stop relying on it
	node := fmt.Sprintf(",%q:{%q:%q,%q:%q,%q:%q,%q:%q}}",
		"node",
		"Name", e.Engine.Name,
		"Id", e.Engine.ID,
		"Addr", e.Engine.Addr,
		"Ip", e.Engine.IP,
	)

	// insert Node field
	data = data[:len(data)-1]
	data = append(data, []byte(node)...)

	var failed []string

	eh.RLock()

	for key, w := range eh.ws {
		if _, err := fmt.Fprintf(w, string(data)); err != nil {
			// collect them to handle later under Lock
			failed = append(failed, key)
			continue
		}

		if f, ok := w.(http.Flusher); ok {
			f.Flush()
		}
	}
	eh.RUnlock()
	eh.Lock()
	if len(failed) > 0 {
		for _, key := range failed {
			if ch, ok := eh.cs[key]; ok {
				close(ch)
			}
			delete(eh.cs, key)
		}
	}

	eh.Unlock()

	return nil
}

// Size returns the number of remote addresses that the events handler currently contains.
func (eh *eventsHandler) Size() int {
	eh.RLock()
	defer eh.RUnlock()
	return len(eh.ws)
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"strings"
)

const (
	// HealthAny is special, and is used as a wild card,
	// not as a specific state.
	HealthAny      = "any"
	HealthPassing  = "passing"
	HealthWarning  = "warning"
	HealthCritical = "critical"
	HealthMaint    = "maintenance"
)

const (
	// NodeMaint is the special key set by a node in maintenance mode.
	NodeMaint = "_node_maintenance"

	// ServiceMaintPrefix is the prefix for a service in maintenance mode.
	ServiceMaintPrefix = "_service_maintenance:"
)

// HealthCheck is used to represent a single check
type HealthCheck struct {
	Node        string
	CheckID     string
	Name        string
	Status      string
	Notes       string
	Output      string
	ServiceID   string
	ServiceName string
}

// HealthChecks is a collection of HealthCheck structs.
type HealthChecks []*HealthCheck

// AggregatedStatus returns the "best" status for the list of health checks.
// Because a given entry may have many service and node-level health checks
// attached, this function determines the best representative of the status as
// as single string using the following heuristic:
//
//  maintenance > critical > warning > passing
//
func (c HealthChecks) AggregatedStatus() string {
	var passing, warning, critical, maintenance bool
	for _, check := range c {
		id := string(check.CheckID)
		if id == NodeMaint || strings.HasPrefix(id, ServiceMaintPrefix) {
			maintenance = true
			continue
		}

		switch check.Status {
		case HealthPassing:
			passing = true
		case HealthWarning:
			warning = true
		case HealthCritical:
			critical = true
		default:
			return ""
		}
	}

	switch {
	case maintenance:
		return HealthMaint
	case critical:
		return HealthCritical
	case warning:
		return HealthWarning
	case passing:
		return HealthPassing
	default:
		return HealthPassing
	}
}

// ServiceEntry is used for the health service endpoint
type ServiceEntry struct {
	Node    *Node
	Service *AgentService
	Checks  []*HealthCheck
}

// Health can be used to query the Health endpoints
type Health struct {
	c *Client
}

// Health returns a handle to the health endpoints
func (c *Client) Health() *Health {
	return &Health{c}
}

// Node is used to query for checks belonging to a given node
func (h *Health) Node(node string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) {
	r := h.c.newRequest("GET", "/v1/health/node/"+node)
	r.setQueryOptions(q)
	rtt, resp, err := requireOK(h.c.doRequest(r))
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	qm := &QueryMeta{}
	parseQueryMeta(resp, qm)
	qm.RequestTime = rtt

	var out []*HealthCheck
	if err := decodeBody(resp, &out); err != nil {
		return nil, nil, err
	}
	return out, qm, nil
}

// Checks is used to return the checks associated with a service
func (h *Health) Checks(service string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) {
	r := h.c.newRequest("GET", "/v1/health/checks/"+service)
	r.setQueryOptions(q)
	rtt, resp, err := requireOK(h.c.doRequest(r))
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	qm := &QueryMeta{}
	parseQueryMeta(resp, qm)
	qm.RequestTime = rtt

	var out []*HealthCheck
	if err := decodeBody(resp, &out); err != nil {
		return nil, nil, err
	}
	return out, qm, nil
}

// Service is used to query health information along with service info
// for a given service. It can optionally do server-side filtering on a tag
// or nodes with passing health checks only.
func (h *Health) Service(service, tag string, passingOnly bool, q *QueryOptions) ([]*ServiceEntry, *QueryMeta, error) {
	r := h.c.newRequest("GET", "/v1/health/service/"+service)
	r.setQueryOptions(q)
	if tag != "" {
		r.params.Set("tag", tag)
	}
	if passingOnly {
		r.params.Set(HealthPassing, "1")
	}
	rtt, resp, err := requireOK(h.c.doRequest(r))
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	qm := &QueryMeta{}
	parseQueryMeta(resp, qm)
	qm.RequestTime = rtt

	var out []*ServiceEntry
	if err := decodeBody(resp, &out); err != nil {
		return nil, nil, err
	}
	return out, qm, nil
}

// State is used to retrieve all the checks in a given state.
// The wildcard "any" state can also be used for all checks.
func (h *Health) State(state string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) {
	switch state {
	case HealthAny:
	case HealthWarning:
	case HealthCritical:
	case HealthPassing:
	default:
		return nil, nil, fmt.Errorf("Unsupported state: %v", state)
	}
	r := h.c.newRequest("GET", "/v1/health/state/"+state)
	r.setQueryOptions(q)
	rtt, resp, err := requireOK(h.c.doRequest(r))
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	qm := &QueryMeta{}
	parseQueryMeta(resp, qm)
	qm.RequestTime = rtt

	var out []*HealthCheck
	if err := decodeBody(resp, &out); err != nil {
		return nil, nil, err
	}
	return out, qm, nil
}
<commit_msg>Return the correct type<commit_after>package api

import (
	"fmt"
	"strings"
)

const (
	// HealthAny is special, and is used as a wild card,
	// not as a specific state.
	HealthAny      = "any"
	HealthPassing  = "passing"
	HealthWarning  = "warning"
	HealthCritical = "critical"
	HealthMaint    = "maintenance"
)

const (
	// NodeMaint is the special key set by a node in maintenance mode.
	NodeMaint = "_node_maintenance"

	// ServiceMaintPrefix is the prefix for a service in maintenance mode.
	ServiceMaintPrefix = "_service_maintenance:"
)

// HealthCheck is used to represent a single check
type HealthCheck struct {
	Node        string
	CheckID     string
	Name        string
	Status      string
	Notes       string
	Output      string
	ServiceID   string
	ServiceName string
}

// HealthChecks is a collection of HealthCheck structs.
type HealthChecks []*HealthCheck

// AggregatedStatus returns the "best" status for the list of health checks.
// Because a given entry may have many service and node-level health checks
// attached, this function determines the best representative of the status as
// as single string using the following heuristic:
//
//  maintenance > critical > warning > passing
//
func (c HealthChecks) AggregatedStatus() string {
	var passing, warning, critical, maintenance bool
	for _, check := range c {
		id := string(check.CheckID)
		if id == NodeMaint || strings.HasPrefix(id, ServiceMaintPrefix) {
			maintenance = true
			continue
		}

		switch check.Status {
		case HealthPassing:
			passing = true
		case HealthWarning:
			warning = true
		case HealthCritical:
			critical = true
		default:
			return ""
		}
	}

	switch {
	case maintenance:
		return HealthMaint
	case critical:
		return HealthCritical
	case warning:
		return HealthWarning
	case passing:
		return HealthPassing
	default:
		return HealthPassing
	}
}

// ServiceEntry is used for the health service endpoint
type ServiceEntry struct {
	Node    *Node
	Service *AgentService
	Checks  HealthChecks
}

// Health can be used to query the Health endpoints
type Health struct {
	c *Client
}

// Health returns a handle to the health endpoints
func (c *Client) Health() *Health {
	return &Health{c}
}

// Node is used to query for checks belonging to a given node
func (h *Health) Node(node string, q *QueryOptions) (HealthChecks, *QueryMeta, error) {
	r := h.c.newRequest("GET", "/v1/health/node/"+node)
	r.setQueryOptions(q)
	rtt, resp, err := requireOK(h.c.doRequest(r))
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	qm := &QueryMeta{}
	parseQueryMeta(resp, qm)
	qm.RequestTime = rtt

	var out HealthChecks
	if err := decodeBody(resp, &out); err != nil {
		return nil, nil, err
	}
	return out, qm, nil
}

// Checks is used to return the checks associated with a service
func (h *Health) Checks(service string, q *QueryOptions) (HealthChecks, *QueryMeta, error) {
	r := h.c.newRequest("GET", "/v1/health/checks/"+service)
	r.setQueryOptions(q)
	rtt, resp, err := requireOK(h.c.doRequest(r))
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	qm := &QueryMeta{}
	parseQueryMeta(resp, qm)
	qm.RequestTime = rtt

	var out HealthChecks
	if err := decodeBody(resp, &out); err != nil {
		return nil, nil, err
	}
	return out, qm, nil
}

// Service is used to query health information along with service info
// for a given service. It can optionally do server-side filtering on a tag
// or nodes with passing health checks only.
func (h *Health) Service(service, tag string, passingOnly bool, q *QueryOptions) ([]*ServiceEntry, *QueryMeta, error) {
	r := h.c.newRequest("GET", "/v1/health/service/"+service)
	r.setQueryOptions(q)
	if tag != "" {
		r.params.Set("tag", tag)
	}
	if passingOnly {
		r.params.Set(HealthPassing, "1")
	}
	rtt, resp, err := requireOK(h.c.doRequest(r))
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	qm := &QueryMeta{}
	parseQueryMeta(resp, qm)
	qm.RequestTime = rtt

	var out []*ServiceEntry
	if err := decodeBody(resp, &out); err != nil {
		return nil, nil, err
	}
	return out, qm, nil
}

// State is used to retrieve all the checks in a given state.
// The wildcard "any" state can also be used for all checks.
func (h *Health) State(state string, q *QueryOptions) (HealthChecks, *QueryMeta, error) {
	switch state {
	case HealthAny:
	case HealthWarning:
	case HealthCritical:
	case HealthPassing:
	default:
		return nil, nil, fmt.Errorf("Unsupported state: %v", state)
	}
	r := h.c.newRequest("GET", "/v1/health/state/"+state)
	r.setQueryOptions(q)
	rtt, resp, err := requireOK(h.c.doRequest(r))
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	qm := &QueryMeta{}
	parseQueryMeta(resp, qm)
	qm.RequestTime = rtt

	var out HealthChecks
	if err := decodeBody(resp, &out); err != nil {
		return nil, nil, err
	}
	return out, qm, nil
}
<|endoftext|>


<commit_before>package jet

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"time"
)

type mapper struct {
	conv ColumnConverter
}

func (m *mapper) unpack(keys []string, values []interface{}, out interface{}) error {
	val := reflect.ValueOf(out)
	if val.Kind() != reflect.Ptr {
		return fmt.Errorf("cannot unpack result to non-pointer (%s)", val.Type().String())
	}
	return m.unpackValue(keys, values, val)
}

func (m *mapper) unpackValue(keys []string, values []interface{}, out reflect.Value) error {
	switch out.Interface().(type) {
	case ComplexValue:
		if out.IsNil() {
			out.Set(reflect.New(out.Type().Elem()))
		}
		plain := reflect.Indirect(reflect.ValueOf(values[0]))
		return out.Interface().(ComplexValue).Decode(plain.Interface())
	}
	if out.CanAddr() {
		switch out.Addr().Interface().(type) {
		case ComplexValue:
			return m.unpackValue(keys, values, out.Addr())
		}
	}
	switch out.Kind() {
	case reflect.Ptr:
		if out.IsNil() {
			out.Set(reflect.New(out.Type().Elem()))
		}
		return m.unpackValue(keys, values, reflect.Indirect(out))
	case reflect.Slice:
		if keys == nil {
			return m.unpackSimple(nil, values, out)
		} else {
			return m.unpackSlice(keys, values, out)
		}
	case reflect.Struct:
		return m.unpackStruct(keys, values, out)
	case reflect.Map:
		if keys == nil {
			return m.unpackSimple(nil, values, out)
		} else {
			return m.unpackMap(keys, values, out)
		}
	default:
		return m.unpackSimple(nil, values, out)
	}
	return fmt.Errorf("cannot unpack result to %T (%s)", out, out.Kind())
}

func (m *mapper) unpackSlice(keys []string, values []interface{}, out reflect.Value) error {
	elemTyp := reflect.Indirect(reflect.New(out.Type().Elem()))
	m.unpackValue(keys, values, elemTyp)
	out.Set(reflect.Append(out, elemTyp))
	return nil
}

func (m *mapper) unpackStruct(keys []string, values []interface{}, out reflect.Value) error {
	// If no keys are passed in it's probably a struct field requiring
	// a simple unpack like a time.Time struct field.
	if len(keys) == 0 {
		return m.unpackSimple(nil, values, out)
	}
	for i, k := range keys {
		var convKey string
		if m.conv == nil {
			convKey = strings.ToUpper(k[:1]) + k[1:]
		} else if m.conv != nil {
			convKey = m.conv.ColumnToFieldName(k)
		}
		field := out.FieldByName(convKey)

		// If the field is not found it can mean that we don't want it or that
		// we have special case like UserID, UUID, userUUID
		// So fix the name and try again
		if !field.IsValid() {
			convKey = strings.Replace(convKey, "Uuid", "UUID", -1)
			convKey = strings.Replace(convKey, "Id", "ID", -1)
			field = out.FieldByName(convKey)
		}

		if field.IsValid() {
			m.unpackValue(nil, values[i:i+1], field)
		}
	}
	return nil
}

func (m *mapper) unpackMap(keys []string, values []interface{}, out reflect.Value) error {
	if out.IsNil() {
		out.Set(reflect.MakeMap(out.Type()))
	}
	for i, k := range keys {
		elemTyp := reflect.Indirect(reflect.New(out.Type().Elem()))
		m.unpackValue(nil, values[i:i+1], elemTyp)
		out.SetMapIndex(reflect.ValueOf(k), elemTyp)
	}

	return nil
}

func (m *mapper) unpackSimple(keys []string, values []interface{}, out reflect.Value) error {
	if !out.IsValid() {
		panic("cannot unpack to zero value")
	}
	if len(values) != 1 {
		panic("cannot unpack to simple value, invalid values input")
	}
	setValue(reflect.Indirect(reflect.ValueOf(values[0])), out)
	return nil
}

func convertAndSet(f interface{}, to reflect.Value) {
	from := reflect.ValueOf(f)
	if from.IsValid() {
		to.Set(from.Convert(to.Type()))
	} else {
		to.Set(reflect.Zero(to.Type()))
	}
}

func setValue(from, to reflect.Value) {
	switch t := from.Interface().(type) {
	case []uint8:
		setValueFromBytes(t, to)
	case int, int8, int16, int32, int64:
		setValueFromInt(reflect.ValueOf(t).Int(), to)
	case uint, uint8, uint16, uint32, uint64:
		setValueFromUint(reflect.ValueOf(t).Uint(), to)
	case float32, float64:
		setValueFromFloat(reflect.ValueOf(t).Float(), to)
	case time.Time:
		setValueFromTime(t, to)
	default:
		convertAndSet(t, to)
	}
}

func setValueFromBytes(t []uint8, to reflect.Value) {
	switch to.Interface().(type) {
	case bool:
		n, _ := strconv.ParseInt(string(t), 10, 32)
		convertAndSet(bool(n == 1), to)
	case int, int8, int16, int32, int64:
		n, _ := strconv.ParseInt(string(t), 10, 64)
		convertAndSet(n, to)
	case uint, uint8, uint16, uint32, uint64:
		n, _ := strconv.ParseUint(string(t), 10, 64)
		convertAndSet(n, to)
	case float32:
		n, _ := strconv.ParseFloat(string(t), 32)
		convertAndSet(n, to)
	case float64:
		n, _ := strconv.ParseFloat(string(t), 64)
		convertAndSet(n, to)
	case string:
		to.SetString(string(t))
	case map[string]interface{}:
		to.Set(reflect.ValueOf(parseHstoreColumn(string(t))))
	default:
		convertAndSet(t, to)
	}
}

func setValueFromFloat(f float64, to reflect.Value) {
	switch to.Interface().(type) {
	case bool:
		convertAndSet(bool(f == 1), to)
	default:
		convertAndSet(f, to)
	}
}

func setValueFromInt(i int64, to reflect.Value) {
	switch to.Interface().(type) {
	case bool:
		convertAndSet(bool(i == 1), to)
	default:
		convertAndSet(i, to)
	}
}

func setValueFromUint(i uint64, to reflect.Value) {
	switch to.Interface().(type) {
	case bool:
		convertAndSet(bool(i == 1), to)
	default:
		convertAndSet(i, to)
	}
}

func setValueFromTime(t time.Time, to reflect.Value) {
	switch to.Interface().(type) {
	case int64:
		convertAndSet(int64(t.Unix()), to)
	case uint64:
		convertAndSet(uint64(t.Unix()), to)
	default:
		convertAndSet(t, to)
	}
}
<commit_msg>Handle IP/Ip<commit_after>package jet

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"time"
)

type mapper struct {
	conv ColumnConverter
}

func (m *mapper) unpack(keys []string, values []interface{}, out interface{}) error {
	val := reflect.ValueOf(out)
	if val.Kind() != reflect.Ptr {
		return fmt.Errorf("cannot unpack result to non-pointer (%s)", val.Type().String())
	}
	return m.unpackValue(keys, values, val)
}

func (m *mapper) unpackValue(keys []string, values []interface{}, out reflect.Value) error {
	switch out.Interface().(type) {
	case ComplexValue:
		if out.IsNil() {
			out.Set(reflect.New(out.Type().Elem()))
		}
		plain := reflect.Indirect(reflect.ValueOf(values[0]))
		return out.Interface().(ComplexValue).Decode(plain.Interface())
	}
	if out.CanAddr() {
		switch out.Addr().Interface().(type) {
		case ComplexValue:
			return m.unpackValue(keys, values, out.Addr())
		}
	}
	switch out.Kind() {
	case reflect.Ptr:
		if out.IsNil() {
			out.Set(reflect.New(out.Type().Elem()))
		}
		return m.unpackValue(keys, values, reflect.Indirect(out))
	case reflect.Slice:
		if keys == nil {
			return m.unpackSimple(nil, values, out)
		} else {
			return m.unpackSlice(keys, values, out)
		}
	case reflect.Struct:
		return m.unpackStruct(keys, values, out)
	case reflect.Map:
		if keys == nil {
			return m.unpackSimple(nil, values, out)
		} else {
			return m.unpackMap(keys, values, out)
		}
	default:
		return m.unpackSimple(nil, values, out)
	}
	return fmt.Errorf("cannot unpack result to %T (%s)", out, out.Kind())
}

func (m *mapper) unpackSlice(keys []string, values []interface{}, out reflect.Value) error {
	elemTyp := reflect.Indirect(reflect.New(out.Type().Elem()))
	m.unpackValue(keys, values, elemTyp)
	out.Set(reflect.Append(out, elemTyp))
	return nil
}

func (m *mapper) unpackStruct(keys []string, values []interface{}, out reflect.Value) error {
	// If no keys are passed in it's probably a struct field requiring
	// a simple unpack like a time.Time struct field.
	if len(keys) == 0 {
		return m.unpackSimple(nil, values, out)
	}
	for i, k := range keys {
		var convKey string
		if m.conv == nil {
			convKey = strings.ToUpper(k[:1]) + k[1:]
		} else if m.conv != nil {
			convKey = m.conv.ColumnToFieldName(k)
		}
		field := out.FieldByName(convKey)

		// If the field is not found it can mean that we don't want it or that
		// we have special case like UserID, UUID, userUUID
		// So fix the name and try again
		if !field.IsValid() {
			convKey = strings.Replace(convKey, "Uuid", "UUID", -1)
			convKey = strings.Replace(convKey, "Id", "ID", -1)
			convKey = strings.Replace(convKey, "Ip", "IP", -1)
			field = out.FieldByName(convKey)
		}

		if field.IsValid() {
			m.unpackValue(nil, values[i:i+1], field)
		}
	}
	return nil
}

func (m *mapper) unpackMap(keys []string, values []interface{}, out reflect.Value) error {
	if out.IsNil() {
		out.Set(reflect.MakeMap(out.Type()))
	}
	for i, k := range keys {
		elemTyp := reflect.Indirect(reflect.New(out.Type().Elem()))
		m.unpackValue(nil, values[i:i+1], elemTyp)
		out.SetMapIndex(reflect.ValueOf(k), elemTyp)
	}

	return nil
}

func (m *mapper) unpackSimple(keys []string, values []interface{}, out reflect.Value) error {
	if !out.IsValid() {
		panic("cannot unpack to zero value")
	}
	if len(values) != 1 {
		panic("cannot unpack to simple value, invalid values input")
	}
	setValue(reflect.Indirect(reflect.ValueOf(values[0])), out)
	return nil
}

func convertAndSet(f interface{}, to reflect.Value) {
	from := reflect.ValueOf(f)
	if from.IsValid() {
		to.Set(from.Convert(to.Type()))
	} else {
		to.Set(reflect.Zero(to.Type()))
	}
}

func setValue(from, to reflect.Value) {
	switch t := from.Interface().(type) {
	case []uint8:
		setValueFromBytes(t, to)
	case int, int8, int16, int32, int64:
		setValueFromInt(reflect.ValueOf(t).Int(), to)
	case uint, uint8, uint16, uint32, uint64:
		setValueFromUint(reflect.ValueOf(t).Uint(), to)
	case float32, float64:
		setValueFromFloat(reflect.ValueOf(t).Float(), to)
	case time.Time:
		setValueFromTime(t, to)
	default:
		convertAndSet(t, to)
	}
}

func setValueFromBytes(t []uint8, to reflect.Value) {
	switch to.Interface().(type) {
	case bool:
		n, _ := strconv.ParseInt(string(t), 10, 32)
		convertAndSet(bool(n == 1), to)
	case int, int8, int16, int32, int64:
		n, _ := strconv.ParseInt(string(t), 10, 64)
		convertAndSet(n, to)
	case uint, uint8, uint16, uint32, uint64:
		n, _ := strconv.ParseUint(string(t), 10, 64)
		convertAndSet(n, to)
	case float32:
		n, _ := strconv.ParseFloat(string(t), 32)
		convertAndSet(n, to)
	case float64:
		n, _ := strconv.ParseFloat(string(t), 64)
		convertAndSet(n, to)
	case string:
		to.SetString(string(t))
	case map[string]interface{}:
		to.Set(reflect.ValueOf(parseHstoreColumn(string(t))))
	default:
		convertAndSet(t, to)
	}
}

func setValueFromFloat(f float64, to reflect.Value) {
	switch to.Interface().(type) {
	case bool:
		convertAndSet(bool(f == 1), to)
	default:
		convertAndSet(f, to)
	}
}

func setValueFromInt(i int64, to reflect.Value) {
	switch to.Interface().(type) {
	case bool:
		convertAndSet(bool(i == 1), to)
	default:
		convertAndSet(i, to)
	}
}

func setValueFromUint(i uint64, to reflect.Value) {
	switch to.Interface().(type) {
	case bool:
		convertAndSet(bool(i == 1), to)
	default:
		convertAndSet(i, to)
	}
}

func setValueFromTime(t time.Time, to reflect.Value) {
	switch to.Interface().(type) {
	case int64:
		convertAndSet(int64(t.Unix()), to)
	case uint64:
		convertAndSet(uint64(t.Unix()), to)
	default:
		convertAndSet(t, to)
	}
}
<|endoftext|>