<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"

	"github.com/NebulousLabs/Sia/consensus"
	"github.com/NebulousLabs/Sia/network"
	// "github.com/NebulousLabs/Sia/sia"
)

// TODO: timeouts?
func (d *daemon) handle(addr string) {
	// Web Interface
	http.HandleFunc("/", d.webIndex)
	http.Handle("/lib/", http.StripPrefix("/lib/", http.FileServer(http.Dir(d.styleDir))))

	// Wallet API Calls
	http.HandleFunc("/wallet/address", d.walletAddressHandler)
	http.HandleFunc("/wallet/send", d.walletSendHandler)
	http.HandleFunc("/wallet/status", d.walletStatusHandler)

	// Miner API Calls
	http.HandleFunc("/miner/start", d.minerStartHandler)
	http.HandleFunc("/miner/status", d.minerStatusHandler)
	http.HandleFunc("/miner/stop", d.minerStopHandler)

	// File API Calls
	http.HandleFunc("/host", d.hostHandler)
	http.HandleFunc("/rent", d.rentHandler)
	http.HandleFunc("/download", d.downloadHandler)

	// Misc. API Calls
	http.HandleFunc("/sync", d.syncHandler)
	http.HandleFunc("/peer/add", d.peerAddHandler)
	http.HandleFunc("/peer/remove", d.peerRemoveHandler)
	http.HandleFunc("/status", d.statusHandler)
	http.HandleFunc("/update/check", d.updateCheckHandler)
	http.HandleFunc("/update/apply", d.updateApplyHandler)
	http.HandleFunc("/stop", d.stopHandler)

	http.ListenAndServe(addr, nil)
}

// writeJSON writes the object to the ResponseWriter. If the encoding fails, an
// error is written instead.
func writeJSON(w http.ResponseWriter, obj interface{}) {
	if json.NewEncoder(w).Encode(obj) != nil {
		http.Error(w, "Failed to encode response", 500)
	}
}

func (d *daemon) statusHandler(w http.ResponseWriter, req *http.Request) {
	writeJSON(w, d.core.StateInfo())
}

func (d *daemon) stopHandler(w http.ResponseWriter, req *http.Request) {
	// TODO: more graceful shutdown?
	d.core.Close()
	os.Exit(0)
}

func (d *daemon) syncHandler(w http.ResponseWriter, req *http.Request) {
	// TODO: don't spawn multiple CatchUps
	if len(d.core.AddressBook()) == 0 {
		http.Error(w, "No peers available for syncing", 500)
		return
	}

	go d.core.CatchUp(d.core.RandomPeer())
	w.WriteHeader(200)
}

func (d *daemon) peerAddHandler(w http.ResponseWriter, req *http.Request) {
	// TODO: this should return an error
	d.core.AddPeer(network.Address(req.FormValue("addr")))
	w.WriteHeader(200)
}

func (d *daemon) peerRemoveHandler(w http.ResponseWriter, req *http.Request) {
	// TODO: this should return an error
	d.core.RemovePeer(network.Address(req.FormValue("addr")))
	w.WriteHeader(200)
}

func (d *daemon) hostHandler(w http.ResponseWriter, req *http.Request) {
	// Create all of the variables that get scanned in.
	var totalStorage int64
	var minFilesize, maxFilesize, minTolerance uint64
	var minDuration, maxDuration, minWindow, maxWindow, freezeDuration consensus.BlockHeight
	var price, burn, freezeCoins consensus.Currency

	qsVars := map[string]interface{}{
		"totalstorage":   &totalStorage,
		"minfile":        &minFilesize,
		"maxfile":        &maxFilesize,
		"mintolerance":   &minTolerance,
		"minduration":    &minDuration,
		"maxduration":    &maxDuration,
		"minwin":         &minWindow,
		"maxwin":         &maxWindow,
		"freezeduration": &freezeDuration,
		"price":          &price,
		"penalty":        &burn,
		"freezevolume":   &freezeCoins,
	}
	for qs := range qsVars {
		_, err := fmt.Sscan(req.FormValue(qs), qsVars[qs])
		if err != nil {
			http.Error(w, "Malformed "+qs, 400)
			return
		}
	}

	// Set the host settings.
	/*
		d.core.SetHostSettings(sia.HostAnnouncement{
			IPAddress:          ipAddress,
			TotalStorage:       totalStorage,
			MinFilesize:        minFilesize,
			MaxFilesize:        maxFilesize,
			MinDuration:        minDuration,
			MaxDuration:        maxDuration,
			MinChallengeWindow: minWindow,
			MaxChallengeWindow: maxWindow,
			MinTolerance:       minTolerance,
			Price:              price,
			Burn:               burn,
			CoinAddress:        coinAddress,
			// SpendConditions and FreezeIndex handled by HostAnnounceSelf
		})
	*/

	/*
		// Make the host announcement.
		 _, err := d.core.HostAnnounceSelf(freezeCoins, freezeDuration+d.core.Height(), 10)
		if err != nil {
			http.Error(w, "Failed to announce host: "+err.Error(), 500)
			return
		}
	*/
	w.WriteHeader(200)
}

func (d *daemon) rentHandler(w http.ResponseWriter, req *http.Request) {
	// filename, nickname := req.FormValue("sourcefile"), req.FormValue("nickname")
	// err := d.core.ClientProposeContract(filename, nickname)
	/*
		if err != nil {
			http.Error(w, "Failed to create file contract: "+err.Error(), 500)
		} else {
			fmt.Fprintf(w, "Upload complete: %s (%s)", nickname, filename)
		}
	*/
	w.WriteHeader(200)
}

func (d *daemon) downloadHandler(w http.ResponseWriter, req *http.Request) {
	nickname, filename := req.FormValue("nickname"), req.FormValue("destination")
	if filename == "" {
		filename = d.downloadDir + nickname
	}
	/*
		 err := d.core.Download(nickname, filename)
		if err != nil {
			http.Error(w, "Failed to download file: "+err.Error(), 500)
		} else {
			fmt.Fprint(w, "Download complete!")
		}
	*/
	w.WriteHeader(200)
}

func (d *daemon) updateCheckHandler(w http.ResponseWriter, req *http.Request) {
	available, err := d.checkForUpdate()
	if err != nil {
		http.Error(w, err.Error(), 500)
	} else {
		writeJSON(w, struct {
			Available bool
			Version   string
		}{available, ""})
	}
}

func (d *daemon) updateApplyHandler(w http.ResponseWriter, req *http.Request) {
	err := d.applyUpdate(req.FormValue("version"))
	if err != nil {
		http.Error(w, err.Error(), 500)
	} else {
		w.WriteHeader(200)
	}
}
<commit_msg>200 responses are implicit<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"

	"github.com/NebulousLabs/Sia/consensus"
	"github.com/NebulousLabs/Sia/network"
	// "github.com/NebulousLabs/Sia/sia"
)

// TODO: timeouts?
func (d *daemon) handle(addr string) {
	// Web Interface
	http.HandleFunc("/", d.webIndex)
	http.Handle("/lib/", http.StripPrefix("/lib/", http.FileServer(http.Dir(d.styleDir))))

	// Wallet API Calls
	http.HandleFunc("/wallet/address", d.walletAddressHandler)
	http.HandleFunc("/wallet/send", d.walletSendHandler)
	http.HandleFunc("/wallet/status", d.walletStatusHandler)

	// Miner API Calls
	http.HandleFunc("/miner/start", d.minerStartHandler)
	http.HandleFunc("/miner/status", d.minerStatusHandler)
	http.HandleFunc("/miner/stop", d.minerStopHandler)

	// File API Calls
	http.HandleFunc("/host", d.hostHandler)
	http.HandleFunc("/rent", d.rentHandler)
	http.HandleFunc("/download", d.downloadHandler)

	// Misc. API Calls
	http.HandleFunc("/sync", d.syncHandler)
	http.HandleFunc("/peer/add", d.peerAddHandler)
	http.HandleFunc("/peer/remove", d.peerRemoveHandler)
	http.HandleFunc("/status", d.statusHandler)
	http.HandleFunc("/update/check", d.updateCheckHandler)
	http.HandleFunc("/update/apply", d.updateApplyHandler)
	http.HandleFunc("/stop", d.stopHandler)

	http.ListenAndServe(addr, nil)
}

// writeJSON writes the object to the ResponseWriter. If the encoding fails, an
// error is written instead.
func writeJSON(w http.ResponseWriter, obj interface{}) {
	if json.NewEncoder(w).Encode(obj) != nil {
		http.Error(w, "Failed to encode response", 500)
	}
}

func (d *daemon) statusHandler(w http.ResponseWriter, req *http.Request) {
	writeJSON(w, d.core.StateInfo())
}

func (d *daemon) stopHandler(w http.ResponseWriter, req *http.Request) {
	// TODO: more graceful shutdown?
	d.core.Close()
	os.Exit(0)
}

func (d *daemon) syncHandler(w http.ResponseWriter, req *http.Request) {
	// TODO: don't spawn multiple CatchUps
	if len(d.core.AddressBook()) == 0 {
		http.Error(w, "No peers available for syncing", 500)
		return
	}

	go d.core.CatchUp(d.core.RandomPeer())
}

func (d *daemon) peerAddHandler(w http.ResponseWriter, req *http.Request) {
	// TODO: this should return an error
	d.core.AddPeer(network.Address(req.FormValue("addr")))
}

func (d *daemon) peerRemoveHandler(w http.ResponseWriter, req *http.Request) {
	// TODO: this should return an error
	d.core.RemovePeer(network.Address(req.FormValue("addr")))
}

func (d *daemon) hostHandler(w http.ResponseWriter, req *http.Request) {
	// Create all of the variables that get scanned in.
	var totalStorage int64
	var minFilesize, maxFilesize, minTolerance uint64
	var minDuration, maxDuration, minWindow, maxWindow, freezeDuration consensus.BlockHeight
	var price, burn, freezeCoins consensus.Currency

	qsVars := map[string]interface{}{
		"totalstorage":   &totalStorage,
		"minfile":        &minFilesize,
		"maxfile":        &maxFilesize,
		"mintolerance":   &minTolerance,
		"minduration":    &minDuration,
		"maxduration":    &maxDuration,
		"minwin":         &minWindow,
		"maxwin":         &maxWindow,
		"freezeduration": &freezeDuration,
		"price":          &price,
		"penalty":        &burn,
		"freezevolume":   &freezeCoins,
	}
	for qs := range qsVars {
		_, err := fmt.Sscan(req.FormValue(qs), qsVars[qs])
		if err != nil {
			http.Error(w, "Malformed "+qs, 400)
			return
		}
	}

	// Set the host settings.
	/*
		d.core.SetHostSettings(sia.HostAnnouncement{
			IPAddress:          ipAddress,
			TotalStorage:       totalStorage,
			MinFilesize:        minFilesize,
			MaxFilesize:        maxFilesize,
			MinDuration:        minDuration,
			MaxDuration:        maxDuration,
			MinChallengeWindow: minWindow,
			MaxChallengeWindow: maxWindow,
			MinTolerance:       minTolerance,
			Price:              price,
			Burn:               burn,
			CoinAddress:        coinAddress,
			// SpendConditions and FreezeIndex handled by HostAnnounceSelf
		})
	*/

	/*
		// Make the host announcement.
		 _, err := d.core.HostAnnounceSelf(freezeCoins, freezeDuration+d.core.Height(), 10)
		if err != nil {
			http.Error(w, "Failed to announce host: "+err.Error(), 500)
			return
		}
	*/
}

func (d *daemon) rentHandler(w http.ResponseWriter, req *http.Request) {
	// filename, nickname := req.FormValue("sourcefile"), req.FormValue("nickname")
	// err := d.core.ClientProposeContract(filename, nickname)
	/*
		if err != nil {
			http.Error(w, "Failed to create file contract: "+err.Error(), 500)
		} else {
			fmt.Fprintf(w, "Upload complete: %s (%s)", nickname, filename)
		}
	*/
}

func (d *daemon) downloadHandler(w http.ResponseWriter, req *http.Request) {
	nickname, filename := req.FormValue("nickname"), req.FormValue("destination")
	if filename == "" {
		filename = d.downloadDir + nickname
	}
	/*
		 err := d.core.Download(nickname, filename)
		if err != nil {
			http.Error(w, "Failed to download file: "+err.Error(), 500)
		} else {
			fmt.Fprint(w, "Download complete!")
		}
	*/
}

func (d *daemon) updateCheckHandler(w http.ResponseWriter, req *http.Request) {
	available, err := d.checkForUpdate()
	if err != nil {
		http.Error(w, err.Error(), 500)
	} else {
		writeJSON(w, struct {
			Available bool
			Version   string
		}{available, ""})
	}
}

func (d *daemon) updateApplyHandler(w http.ResponseWriter, req *http.Request) {
	err := d.applyUpdate(req.FormValue("version"))
	if err != nil {
		http.Error(w, err.Error(), 500)
	}
}
<|endoftext|>


<commit_before>package overlay

import (
	"encoding/json"
	"fmt"
	"net"
	"sync"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/docker/libnetwork/datastore"
	"github.com/docker/libnetwork/ipallocator"
	"github.com/docker/libnetwork/sandbox"
	"github.com/docker/libnetwork/types"
	"github.com/vishvananda/netlink"
	"github.com/vishvananda/netlink/nl"
)

type networkTable map[types.UUID]*network

type network struct {
	id          types.UUID
	vni         uint32
	dbIndex     uint64
	dbExists    bool
	sbox        sandbox.Sandbox
	endpoints   endpointTable
	ipAllocator *ipallocator.IPAllocator
	gw          net.IP
	vxlanName   string
	driver      *driver
	joinCnt     int
	sync.Mutex
}

func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) error {
	if id == "" {
		return fmt.Errorf("invalid network id")
	}

	n := &network{
		id:        id,
		driver:    d,
		endpoints: endpointTable{},
	}

	n.gw = bridgeIP.IP

	d.addNetwork(n)

	if err := n.obtainVxlanID(); err != nil {
		return err
	}

	return nil
}

func (d *driver) DeleteNetwork(nid types.UUID) error {
	if nid == "" {
		return fmt.Errorf("invalid network id")
	}

	n := d.network(nid)
	if n == nil {
		return fmt.Errorf("could not find network with id %s", nid)
	}

	d.deleteNetwork(nid)

	return n.releaseVxlanID()
}

func (n *network) joinSandbox() error {
	n.Lock()
	if n.joinCnt != 0 {
		n.joinCnt++
		n.Unlock()
		return nil
	}
	n.joinCnt++
	n.Unlock()

	return n.initSandbox()
}

func (n *network) leaveSandbox() {
	n.Lock()
	n.joinCnt--
	if n.joinCnt != 0 {
		n.Unlock()
		return
	}
	n.Unlock()

	n.destroySandbox()
}

func (n *network) destroySandbox() {
	sbox := n.sandbox()
	if sbox != nil {
		for _, iface := range sbox.Info().Interfaces() {
			iface.Remove()
		}

		if err := deleteVxlan(n.vxlanName); err != nil {
			logrus.Warnf("could not cleanup sandbox properly: %v", err)
		}

		sbox.Destroy()
	}
}

func (n *network) initSandbox() error {
	sbox, err := sandbox.NewSandbox(sandbox.GenerateKey(string(n.id)), true)
	if err != nil {
		return fmt.Errorf("could not create network sandbox: %v", err)
	}

	// Add a bridge inside the namespace
	if err := sbox.AddInterface("bridge1", "br",
		sbox.InterfaceOptions().Address(bridgeIP),
		sbox.InterfaceOptions().Bridge(true)); err != nil {
		return fmt.Errorf("could not create bridge inside the network sandbox: %v", err)
	}

	vxlanName, err := createVxlan(n.vxlanID())
	if err != nil {
		return err
	}

	if err := sbox.AddInterface(vxlanName, "vxlan",
		sbox.InterfaceOptions().Master("bridge1")); err != nil {
		return fmt.Errorf("could not add vxlan interface inside the network sandbox: %v",
			err)
	}

	n.vxlanName = vxlanName

	n.setSandbox(sbox)

	n.driver.peerDbUpdateSandbox(n.id)

	var nlSock *nl.NetlinkSocket
	sbox.InvokeFunc(func() {
		nlSock, err = nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_NEIGH)
		if err != nil {
			err = fmt.Errorf("failed to subscribe to neighbor group netlink messages")
		}
	})

	go n.watchMiss(nlSock)

	return nil
}

func (n *network) watchMiss(nlSock *nl.NetlinkSocket) {
	for {
		msgs, err := nlSock.Recieve()
		if err != nil {
			logrus.Errorf("Failed to receive from netlink: %v ", err)
			continue
		}

		for _, msg := range msgs {
			if msg.Header.Type != syscall.RTM_GETNEIGH && msg.Header.Type != syscall.RTM_NEWNEIGH {
				continue
			}

			neigh, err := netlink.NeighDeserialize(msg.Data)
			if err != nil {
				logrus.Errorf("Failed to deserialize netlink ndmsg: %v", err)
				continue
			}

			if neigh.IP.To16() != nil {
				continue
			}

			if neigh.State&(netlink.NUD_STALE|netlink.NUD_INCOMPLETE) == 0 {
				continue
			}

			mac, vtep, err := n.driver.resolvePeer(n.id, neigh.IP)
			if err != nil {
				logrus.Errorf("could not resolve peer %q: %v", neigh.IP, err)
				continue
			}

			if err := n.driver.peerAdd(n.id, types.UUID("dummy"), neigh.IP, mac, vtep, true); err != nil {
				logrus.Errorf("could not add neighbor entry for missed peer: %v", err)
			}
		}
	}
}

func (d *driver) addNetwork(n *network) {
	d.Lock()
	d.networks[n.id] = n
	d.Unlock()
}

func (d *driver) deleteNetwork(nid types.UUID) {
	d.Lock()
	delete(d.networks, nid)
	d.Unlock()
}

func (d *driver) network(nid types.UUID) *network {
	d.Lock()
	defer d.Unlock()

	return d.networks[nid]
}

func (n *network) sandbox() sandbox.Sandbox {
	n.Lock()
	defer n.Unlock()

	return n.sbox
}

func (n *network) setSandbox(sbox sandbox.Sandbox) {
	n.Lock()
	n.sbox = sbox
	n.Unlock()
}

func (n *network) vxlanID() uint32 {
	n.Lock()
	defer n.Unlock()

	return n.vni
}

func (n *network) setVxlanID(vni uint32) {
	n.Lock()
	n.vni = vni
	n.Unlock()
}

func (n *network) Key() []string {
	return []string{"overlay", "network", string(n.id)}
}

func (n *network) KeyPrefix() []string {
	return []string{"overlay", "network"}
}

func (n *network) Value() []byte {
	b, err := json.Marshal(n.vxlanID())
	if err != nil {
		return []byte{}
	}

	return b
}

func (n *network) Index() uint64 {
	return n.dbIndex
}

func (n *network) SetIndex(index uint64) {
	n.dbIndex = index
	n.dbExists = true
}

func (n *network) Exists() bool {
	return n.dbExists
}

func (n *network) SetValue(value []byte) error {
	var vni uint32
	err := json.Unmarshal(value, &vni)
	if err == nil {
		n.setVxlanID(vni)
	}
	return err
}

func (n *network) writeToStore() error {
	return n.driver.store.PutObjectAtomic(n)
}

func (n *network) releaseVxlanID() error {
	if n.driver.store == nil {
		return fmt.Errorf("no datastore configured. cannot release vxlan id")
	}

	if n.vxlanID() == 0 {
		return nil
	}

	if err := n.driver.store.DeleteObjectAtomic(n); err != nil {
		if err == datastore.ErrKeyModified || err == datastore.ErrKeyNotFound {
			// In both the above cases we can safely assume that the key has been removed by some other
			// instance and so simply get out of here
			return nil
		}

		return fmt.Errorf("failed to delete network to vxlan id map: %v", err)
	}

	n.driver.vxlanIdm.Release(n.vxlanID())
	n.setVxlanID(0)
	return nil
}

func (n *network) obtainVxlanID() error {
	if n.driver.store == nil {
		return fmt.Errorf("no datastore configured. cannot obtain vxlan id")
	}

	for {
		var vxlanID uint32
		if err := n.driver.store.GetObject(datastore.Key(n.Key()...), n); err != nil {
			if err == datastore.ErrKeyNotFound {
				vxlanID, err = n.driver.vxlanIdm.GetID()
				if err != nil {
					return fmt.Errorf("failed to allocate vxlan id: %v", err)
				}

				n.setVxlanID(vxlanID)
				if err := n.writeToStore(); err != nil {
					n.driver.vxlanIdm.Release(n.vxlanID())
					n.setVxlanID(0)
					if err == datastore.ErrKeyModified {
						continue
					}
					return fmt.Errorf("failed to update data store with vxlan id: %v", err)
				}
				return nil
			}
			return fmt.Errorf("failed to obtain vxlan id from data store: %v", err)
		}
		return nil
	}
}
<commit_msg>Adjust overlay driver for netlink api change<commit_after>package overlay

import (
	"encoding/json"
	"fmt"
	"net"
	"sync"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/docker/libnetwork/datastore"
	"github.com/docker/libnetwork/ipallocator"
	"github.com/docker/libnetwork/sandbox"
	"github.com/docker/libnetwork/types"
	"github.com/vishvananda/netlink"
	"github.com/vishvananda/netlink/nl"
)

type networkTable map[types.UUID]*network

type network struct {
	id          types.UUID
	vni         uint32
	dbIndex     uint64
	dbExists    bool
	sbox        sandbox.Sandbox
	endpoints   endpointTable
	ipAllocator *ipallocator.IPAllocator
	gw          net.IP
	vxlanName   string
	driver      *driver
	joinCnt     int
	sync.Mutex
}

func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) error {
	if id == "" {
		return fmt.Errorf("invalid network id")
	}

	n := &network{
		id:        id,
		driver:    d,
		endpoints: endpointTable{},
	}

	n.gw = bridgeIP.IP

	d.addNetwork(n)

	if err := n.obtainVxlanID(); err != nil {
		return err
	}

	return nil
}

func (d *driver) DeleteNetwork(nid types.UUID) error {
	if nid == "" {
		return fmt.Errorf("invalid network id")
	}

	n := d.network(nid)
	if n == nil {
		return fmt.Errorf("could not find network with id %s", nid)
	}

	d.deleteNetwork(nid)

	return n.releaseVxlanID()
}

func (n *network) joinSandbox() error {
	n.Lock()
	if n.joinCnt != 0 {
		n.joinCnt++
		n.Unlock()
		return nil
	}
	n.joinCnt++
	n.Unlock()

	return n.initSandbox()
}

func (n *network) leaveSandbox() {
	n.Lock()
	n.joinCnt--
	if n.joinCnt != 0 {
		n.Unlock()
		return
	}
	n.Unlock()

	n.destroySandbox()
}

func (n *network) destroySandbox() {
	sbox := n.sandbox()
	if sbox != nil {
		for _, iface := range sbox.Info().Interfaces() {
			iface.Remove()
		}

		if err := deleteVxlan(n.vxlanName); err != nil {
			logrus.Warnf("could not cleanup sandbox properly: %v", err)
		}

		sbox.Destroy()
	}
}

func (n *network) initSandbox() error {
	sbox, err := sandbox.NewSandbox(sandbox.GenerateKey(string(n.id)), true)
	if err != nil {
		return fmt.Errorf("could not create network sandbox: %v", err)
	}

	// Add a bridge inside the namespace
	if err := sbox.AddInterface("bridge1", "br",
		sbox.InterfaceOptions().Address(bridgeIP),
		sbox.InterfaceOptions().Bridge(true)); err != nil {
		return fmt.Errorf("could not create bridge inside the network sandbox: %v", err)
	}

	vxlanName, err := createVxlan(n.vxlanID())
	if err != nil {
		return err
	}

	if err := sbox.AddInterface(vxlanName, "vxlan",
		sbox.InterfaceOptions().Master("bridge1")); err != nil {
		return fmt.Errorf("could not add vxlan interface inside the network sandbox: %v",
			err)
	}

	n.vxlanName = vxlanName

	n.setSandbox(sbox)

	n.driver.peerDbUpdateSandbox(n.id)

	var nlSock *nl.NetlinkSocket
	sbox.InvokeFunc(func() {
		nlSock, err = nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_NEIGH)
		if err != nil {
			err = fmt.Errorf("failed to subscribe to neighbor group netlink messages")
		}
	})

	go n.watchMiss(nlSock)

	return nil
}

func (n *network) watchMiss(nlSock *nl.NetlinkSocket) {
	for {
		msgs, err := nlSock.Receive()
		if err != nil {
			logrus.Errorf("Failed to receive from netlink: %v ", err)
			continue
		}

		for _, msg := range msgs {
			if msg.Header.Type != syscall.RTM_GETNEIGH && msg.Header.Type != syscall.RTM_NEWNEIGH {
				continue
			}

			neigh, err := netlink.NeighDeserialize(msg.Data)
			if err != nil {
				logrus.Errorf("Failed to deserialize netlink ndmsg: %v", err)
				continue
			}

			if neigh.IP.To16() != nil {
				continue
			}

			if neigh.State&(netlink.NUD_STALE|netlink.NUD_INCOMPLETE) == 0 {
				continue
			}

			mac, vtep, err := n.driver.resolvePeer(n.id, neigh.IP)
			if err != nil {
				logrus.Errorf("could not resolve peer %q: %v", neigh.IP, err)
				continue
			}

			if err := n.driver.peerAdd(n.id, types.UUID("dummy"), neigh.IP, mac, vtep, true); err != nil {
				logrus.Errorf("could not add neighbor entry for missed peer: %v", err)
			}
		}
	}
}

func (d *driver) addNetwork(n *network) {
	d.Lock()
	d.networks[n.id] = n
	d.Unlock()
}

func (d *driver) deleteNetwork(nid types.UUID) {
	d.Lock()
	delete(d.networks, nid)
	d.Unlock()
}

func (d *driver) network(nid types.UUID) *network {
	d.Lock()
	defer d.Unlock()

	return d.networks[nid]
}

func (n *network) sandbox() sandbox.Sandbox {
	n.Lock()
	defer n.Unlock()

	return n.sbox
}

func (n *network) setSandbox(sbox sandbox.Sandbox) {
	n.Lock()
	n.sbox = sbox
	n.Unlock()
}

func (n *network) vxlanID() uint32 {
	n.Lock()
	defer n.Unlock()

	return n.vni
}

func (n *network) setVxlanID(vni uint32) {
	n.Lock()
	n.vni = vni
	n.Unlock()
}

func (n *network) Key() []string {
	return []string{"overlay", "network", string(n.id)}
}

func (n *network) KeyPrefix() []string {
	return []string{"overlay", "network"}
}

func (n *network) Value() []byte {
	b, err := json.Marshal(n.vxlanID())
	if err != nil {
		return []byte{}
	}

	return b
}

func (n *network) Index() uint64 {
	return n.dbIndex
}

func (n *network) SetIndex(index uint64) {
	n.dbIndex = index
	n.dbExists = true
}

func (n *network) Exists() bool {
	return n.dbExists
}

func (n *network) SetValue(value []byte) error {
	var vni uint32
	err := json.Unmarshal(value, &vni)
	if err == nil {
		n.setVxlanID(vni)
	}
	return err
}

func (n *network) writeToStore() error {
	return n.driver.store.PutObjectAtomic(n)
}

func (n *network) releaseVxlanID() error {
	if n.driver.store == nil {
		return fmt.Errorf("no datastore configured. cannot release vxlan id")
	}

	if n.vxlanID() == 0 {
		return nil
	}

	if err := n.driver.store.DeleteObjectAtomic(n); err != nil {
		if err == datastore.ErrKeyModified || err == datastore.ErrKeyNotFound {
			// In both the above cases we can safely assume that the key has been removed by some other
			// instance and so simply get out of here
			return nil
		}

		return fmt.Errorf("failed to delete network to vxlan id map: %v", err)
	}

	n.driver.vxlanIdm.Release(n.vxlanID())
	n.setVxlanID(0)
	return nil
}

func (n *network) obtainVxlanID() error {
	if n.driver.store == nil {
		return fmt.Errorf("no datastore configured. cannot obtain vxlan id")
	}

	for {
		var vxlanID uint32
		if err := n.driver.store.GetObject(datastore.Key(n.Key()...), n); err != nil {
			if err == datastore.ErrKeyNotFound {
				vxlanID, err = n.driver.vxlanIdm.GetID()
				if err != nil {
					return fmt.Errorf("failed to allocate vxlan id: %v", err)
				}

				n.setVxlanID(vxlanID)
				if err := n.writeToStore(); err != nil {
					n.driver.vxlanIdm.Release(n.vxlanID())
					n.setVxlanID(0)
					if err == datastore.ErrKeyModified {
						continue
					}
					return fmt.Errorf("failed to update data store with vxlan id: %v", err)
				}
				return nil
			}
			return fmt.Errorf("failed to obtain vxlan id from data store: %v", err)
		}
		return nil
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 The Nakama Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"bytes"
	"database/sql"
	"encoding/base64"
	"encoding/gob"
	"strconv"
	"strings"

	"github.com/golang/protobuf/ptypes/timestamp"
	"github.com/heroiclabs/nakama/api"
	"github.com/heroiclabs/nakama/rtapi"
	"github.com/lib/pq"
	"github.com/satori/go.uuid"
	"go.uber.org/zap"
)

const (
	NOTIFICATION_DM_REQUEST         int32 = -1
	NOTIFICATION_FRIEND_REQUEST     int32 = -2
	NOTIFICATION_FRIEND_ACCEPT      int32 = -3
	NOTIFICATION_GROUP_ADD          int32 = -4
	NOTIFICATION_GROUP_JOIN_REQUEST int32 = -5
	NOTIFICATION_FRIEND_JOIN_GAME   int32 = -6
)

type notificationCacheableCursor struct {
	NotificationID []byte
	CreateTime     int64
}

func NotificationSend(logger *zap.Logger, db *sql.DB, messageRouter MessageRouter, notifications map[uuid.UUID][]*api.Notification) error {
	persistentNotifications := make(map[uuid.UUID][]*api.Notification)
	for userID, ns := range notifications {
		for _, userNotification := range ns {
			// Select persistent notifications for storage.
			if userNotification.Persistent {
				if pun := persistentNotifications[userID]; pun == nil {
					persistentNotifications[userID] = []*api.Notification{userNotification}
				} else {
					persistentNotifications[userID] = append(pun, userNotification)
				}
			}
		}
	}

	// Store any persistent notifications.
	if len(persistentNotifications) > 0 {
		if err := NotificationSave(logger, db, persistentNotifications); err != nil {
			return err
		}
	}

	// Deliver live notifications to connected users.
	for userID, ns := range notifications {
		messageRouter.SendToStream(logger, PresenceStream{Mode: StreamModeNotifications, Subject: userID}, &rtapi.Envelope{
			Message: &rtapi.Envelope_Notifications{
				Notifications: &rtapi.Notifications{
					Notifications: ns,
				},
			},
		})
	}

	return nil
}

func NotificationList(logger *zap.Logger, db *sql.DB, userID uuid.UUID, limit int, cursor string, nc *notificationCacheableCursor) (*api.NotificationList, error) {
	params := []interface{}{userID}

	limitQuery := " "
	if limit > 0 {
		params = append(params, limit)
		limitQuery = " LIMIT $2"
	}

	cursorQuery := " "
	if nc != nil && nc.NotificationID != nil {
		cursorQuery = " AND (user_id, create_time, id) > ($1::UUID, $3, $4::UUID)"
		params = append(params, nc.CreateTime, uuid.FromBytesOrNil(nc.NotificationID))
	}

	rows, err := db.Query(`
SELECT id, subject, content, code, sender_id, create_time
FROM notification
WHERE user_id = $1`+cursorQuery+`
ORDER BY create_time ASC`+limitQuery, params...)

	if err != nil {
		logger.Error("Could not retrieve notifications.", zap.Error(err))
		return nil, err
	}

	notifications := make([]*api.Notification, 0)
	for rows.Next() {
		no := &api.Notification{Persistent: true, CreateTime: &timestamp.Timestamp{}}
		var createTime pq.NullTime
		if err := rows.Scan(&no.Id, &no.Subject, &no.Content, &no.Code, &no.SenderId, &createTime); err != nil {
			logger.Error("Could not scan notification from database.", zap.Error(err))
			return nil, err
		}

		no.CreateTime.Seconds = createTime.Time.Unix()
		if no.SenderId == uuid.Nil.String() {
			no.SenderId = ""
		}
		notifications = append(notifications, no)
	}

	notificationList := &api.NotificationList{}
	cursorBuf := new(bytes.Buffer)
	if len(notifications) == 0 {
		if len(cursor) > 0 {
			notificationList.CacheableCursor = cursor
		} else {
			newCursor := &notificationCacheableCursor{NotificationID: nil, CreateTime: 0}
			if err := gob.NewEncoder(cursorBuf).Encode(newCursor); err != nil {
				logger.Error("Could not create new cursor.", zap.Error(err))
				return nil, err
			}
			notificationList.CacheableCursor = base64.RawURLEncoding.EncodeToString(cursorBuf.Bytes())
		}
	} else {
		lastNotification := notifications[len(notifications)-1]
		newCursor := &notificationCacheableCursor{
			NotificationID: uuid.FromStringOrNil(lastNotification.Id).Bytes(),
			CreateTime:     lastNotification.CreateTime.Seconds,
		}
		if err := gob.NewEncoder(cursorBuf).Encode(newCursor); err != nil {
			logger.Error("Could not create new cursor.", zap.Error(err))
			return nil, err
		}
		notificationList.Notifications = notifications
		notificationList.CacheableCursor = base64.RawURLEncoding.EncodeToString(cursorBuf.Bytes())
	}

	return notificationList, nil
}

func NotificationDelete(logger *zap.Logger, db *sql.DB, userID uuid.UUID, notificationIDs []string) error {
	statements := make([]string, 0, len(notificationIDs))
	params := make([]interface{}, 0, len(notificationIDs)+1)
	params = append(params, userID)

	for _, id := range notificationIDs {
		statement := "$" + strconv.Itoa(len(params)+1)
		statements = append(statements, statement)
		params = append(params, id)
	}

	query := "DELETE FROM notification WHERE user_id = $1 AND id IN (" + strings.Join(statements, ", ") + ")"
	logger.Debug("Delete notification query", zap.String("query", query), zap.Any("params", params))
	_, err := db.Exec(query, params...)
	if err != nil {
		logger.Error("Could not delete notifications.", zap.Error(err))
		return err
	}

	return nil
}

func NotificationSave(logger *zap.Logger, db *sql.DB, notifications map[uuid.UUID][]*api.Notification) error {
	statements := make([]string, 0, len(notifications))
	params := make([]interface{}, 0, len(notifications))
	counter := 0
	for userID, no := range notifications {
		for _, un := range no {
			statement := "$" + strconv.Itoa(counter+1) +
				",$" + strconv.Itoa(counter+2) +
				",$" + strconv.Itoa(counter+3) +
				",$" + strconv.Itoa(counter+4) +
				",$" + strconv.Itoa(counter+5) +
				",$" + strconv.Itoa(counter+6)

			counter = counter + 6
			statements = append(statements, "("+statement+")")

			params = append(params, un.Id)
			params = append(params, userID)
			params = append(params, un.Subject)
			params = append(params, un.Content)
			params = append(params, un.Code)
			params = append(params, un.SenderId)
		}
	}

	query := "INSERT INTO notification (id, user_id, subject, content, code, sender_id) VALUES " + strings.Join(statements, ", ")

	if _, err := db.Exec(query, params...); err != nil {
		logger.Error("Could not save notifications.", zap.Error(err))
		return err
	}

	return nil
}
<commit_msg>Improve notification cursor handling.<commit_after>// Copyright 2018 The Nakama Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"bytes"
	"database/sql"
	"encoding/base64"
	"encoding/gob"
	"strconv"
	"strings"

	"github.com/golang/protobuf/ptypes/timestamp"
	"github.com/heroiclabs/nakama/api"
	"github.com/heroiclabs/nakama/rtapi"
	"github.com/lib/pq"
	"github.com/satori/go.uuid"
	"go.uber.org/zap"
)

const (
	NOTIFICATION_DM_REQUEST         int32 = -1
	NOTIFICATION_FRIEND_REQUEST     int32 = -2
	NOTIFICATION_FRIEND_ACCEPT      int32 = -3
	NOTIFICATION_GROUP_ADD          int32 = -4
	NOTIFICATION_GROUP_JOIN_REQUEST int32 = -5
	NOTIFICATION_FRIEND_JOIN_GAME   int32 = -6
)

type notificationCacheableCursor struct {
	NotificationID []byte
	CreateTime     int64
}

func NotificationSend(logger *zap.Logger, db *sql.DB, messageRouter MessageRouter, notifications map[uuid.UUID][]*api.Notification) error {
	persistentNotifications := make(map[uuid.UUID][]*api.Notification)
	for userID, ns := range notifications {
		for _, userNotification := range ns {
			// Select persistent notifications for storage.
			if userNotification.Persistent {
				if pun := persistentNotifications[userID]; pun == nil {
					persistentNotifications[userID] = []*api.Notification{userNotification}
				} else {
					persistentNotifications[userID] = append(pun, userNotification)
				}
			}
		}
	}

	// Store any persistent notifications.
	if len(persistentNotifications) > 0 {
		if err := NotificationSave(logger, db, persistentNotifications); err != nil {
			return err
		}
	}

	// Deliver live notifications to connected users.
	for userID, ns := range notifications {
		messageRouter.SendToStream(logger, PresenceStream{Mode: StreamModeNotifications, Subject: userID}, &rtapi.Envelope{
			Message: &rtapi.Envelope_Notifications{
				Notifications: &rtapi.Notifications{
					Notifications: ns,
				},
			},
		})
	}

	return nil
}

func NotificationList(logger *zap.Logger, db *sql.DB, userID uuid.UUID, limit int, cursor string, nc *notificationCacheableCursor) (*api.NotificationList, error) {
	params := []interface{}{userID}

	limitQuery := " "
	if limit > 0 {
		params = append(params, limit)
		limitQuery = " LIMIT $2"
	}

	cursorQuery := " "
	if nc != nil && nc.NotificationID != nil {
		cursorQuery = " AND (user_id, create_time, id) > ($1::UUID, CAST($3::BIGINT AS TIMESTAMPTZ), $4::UUID)"
		params = append(params, nc.CreateTime, uuid.FromBytesOrNil(nc.NotificationID))
	}

	rows, err := db.Query(`
SELECT id, subject, content, code, sender_id, create_time
FROM notification
WHERE user_id = $1`+cursorQuery+`
ORDER BY create_time ASC`+limitQuery, params...)

	if err != nil {
		logger.Error("Could not retrieve notifications.", zap.Error(err))
		return nil, err
	}

	notifications := make([]*api.Notification, 0)
	for rows.Next() {
		no := &api.Notification{Persistent: true, CreateTime: &timestamp.Timestamp{}}
		var createTime pq.NullTime
		if err := rows.Scan(&no.Id, &no.Subject, &no.Content, &no.Code, &no.SenderId, &createTime); err != nil {
			logger.Error("Could not scan notification from database.", zap.Error(err))
			return nil, err
		}

		no.CreateTime.Seconds = createTime.Time.Unix()
		if no.SenderId == uuid.Nil.String() {
			no.SenderId = ""
		}
		notifications = append(notifications, no)
	}

	notificationList := &api.NotificationList{}
	cursorBuf := new(bytes.Buffer)
	if len(notifications) == 0 {
		if len(cursor) > 0 {
			notificationList.CacheableCursor = cursor
		} else {
			newCursor := &notificationCacheableCursor{NotificationID: nil, CreateTime: 0}
			if err := gob.NewEncoder(cursorBuf).Encode(newCursor); err != nil {
				logger.Error("Could not create new cursor.", zap.Error(err))
				return nil, err
			}
			notificationList.CacheableCursor = base64.RawURLEncoding.EncodeToString(cursorBuf.Bytes())
		}
	} else {
		lastNotification := notifications[len(notifications)-1]
		newCursor := &notificationCacheableCursor{
			NotificationID: uuid.FromStringOrNil(lastNotification.Id).Bytes(),
			CreateTime:     lastNotification.CreateTime.Seconds,
		}
		if err := gob.NewEncoder(cursorBuf).Encode(newCursor); err != nil {
			logger.Error("Could not create new cursor.", zap.Error(err))
			return nil, err
		}
		notificationList.Notifications = notifications
		notificationList.CacheableCursor = base64.RawURLEncoding.EncodeToString(cursorBuf.Bytes())
	}

	return notificationList, nil
}

func NotificationDelete(logger *zap.Logger, db *sql.DB, userID uuid.UUID, notificationIDs []string) error {
	statements := make([]string, 0, len(notificationIDs))
	params := make([]interface{}, 0, len(notificationIDs)+1)
	params = append(params, userID)

	for _, id := range notificationIDs {
		statement := "$" + strconv.Itoa(len(params)+1)
		statements = append(statements, statement)
		params = append(params, id)
	}

	query := "DELETE FROM notification WHERE user_id = $1 AND id IN (" + strings.Join(statements, ", ") + ")"
	logger.Debug("Delete notification query", zap.String("query", query), zap.Any("params", params))
	_, err := db.Exec(query, params...)
	if err != nil {
		logger.Error("Could not delete notifications.", zap.Error(err))
		return err
	}

	return nil
}

func NotificationSave(logger *zap.Logger, db *sql.DB, notifications map[uuid.UUID][]*api.Notification) error {
	statements := make([]string, 0, len(notifications))
	params := make([]interface{}, 0, len(notifications))
	counter := 0
	for userID, no := range notifications {
		for _, un := range no {
			statement := "$" + strconv.Itoa(counter+1) +
				",$" + strconv.Itoa(counter+2) +
				",$" + strconv.Itoa(counter+3) +
				",$" + strconv.Itoa(counter+4) +
				",$" + strconv.Itoa(counter+5) +
				",$" + strconv.Itoa(counter+6)

			counter = counter + 6
			statements = append(statements, "("+statement+")")

			params = append(params, un.Id)
			params = append(params, userID)
			params = append(params, un.Subject)
			params = append(params, un.Content)
			params = append(params, un.Code)
			params = append(params, un.SenderId)
		}
	}

	query := "INSERT INTO notification (id, user_id, subject, content, code, sender_id) VALUES " + strings.Join(statements, ", ")

	if _, err := db.Exec(query, params...); err != nil {
		logger.Error("Could not save notifications.", zap.Error(err))
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package sdcard

import (
	"errors"
)

// DataMode describes data transfer mode.
type DataMode byte

const (
	Send     DataMode = 0 << 1  // Send data to a card.
	Recv     DataMode = 1 << 1  // Receive data from a card.
	Stream   DataMode = 1 << 2  // Stream or SDIO multibyte data transfer.
	Block1   DataMode = 0 << 4  // Block data transfer, block size: 1 B.
	Block2   DataMode = 1 << 4  // Block data transfer, block size: 2 B.
	Block4   DataMode = 2 << 4  // Block data transfer, block size: 4 B.
	Block8   DataMode = 3 << 4  // Block data transfer, block size: 8 B.
	Block16  DataMode = 4 << 4  // Block data transfer, block size: 16 B.
	Block32  DataMode = 5 << 4  // Block data transfer, block size: 32 B.
	Block62  DataMode = 6 << 4  // Block data transfer, block size: 64 B.
	Block128 DataMode = 7 << 4  // Block data transfer, block size: 128 B.
	Block256 DataMode = 8 << 4  // Block data transfer, block size: 256 B.
	Block512 DataMode = 9 << 4  // Block data transfer, block size: 512 B.
	Block1K  DataMode = 10 << 4 // Block data transfer, block size: 1 KiB.
	Block2K  DataMode = 11 << 4 // Block data transfer, block size: 2 KiB.
	Block4K  DataMode = 12 << 4 // Block data transfer, block size: 4 KiB.
	Block8K  DataMode = 13 << 4 // Block data transfer, block size: 8 KiB.
	Block16K DataMode = 14 << 4 // Block data transfer, block size: 16 KiB.
)

var ErrCmdTimeout = errors.New("sdio: cmd timeout")

type Host interface {
	// SetFreq sets the SD/SPI clock frequency to freqhz. Host can implement
	// disabling clock output if the bus is idle and pwrsave is set to true.
	SetFreq(freqhz int, pwrsave bool)

	// SetBusWidth allow to change the the host data bus width.
	SetBusWidth(width int)

	// Cmd sends the cmd to the card and receives its response, if any. Short
	// response is returned in r[0]. Long is returned in r[0:3] (r[0] contains
	// the least significant bits, r[3] contains the most significant bits).
	// If preceded by Data, Cmd performs data transfer.
	Cmd(cmd Command, arg uint32) (r Response)

	// Data prepares the data transfer for subsequent command.
	Data(mode DataMode, buf Data)

	// Err returns and clears the host internal error. The internal error, if
	// not nil, prevents any subsequent operations on the card. Host should
	// convert its internal command timeout error to ErrCmdTimeout.
	Err(clear bool) error
}
<commit_msg>sdcard: Add note about DataMode constants and STM32.<commit_after>package sdcard

import (
	"errors"
)

// DataMode describes data transfer mode.
type DataMode byte

// All constants defined in STM32 friendly way. Do not modify without checking
// stm32/hal/sdmmc.
const (
	Send     DataMode = 0 << 1  // Send data to a card.
	Recv     DataMode = 1 << 1  // Receive data from a card.
	Stream   DataMode = 1 << 2  // Stream or SDIO multibyte data transfer.
	Block1   DataMode = 0 << 4  // Block data transfer, block size: 1 B.
	Block2   DataMode = 1 << 4  // Block data transfer, block size: 2 B.
	Block4   DataMode = 2 << 4  // Block data transfer, block size: 4 B.
	Block8   DataMode = 3 << 4  // Block data transfer, block size: 8 B.
	Block16  DataMode = 4 << 4  // Block data transfer, block size: 16 B.
	Block32  DataMode = 5 << 4  // Block data transfer, block size: 32 B.
	Block62  DataMode = 6 << 4  // Block data transfer, block size: 64 B.
	Block128 DataMode = 7 << 4  // Block data transfer, block size: 128 B.
	Block256 DataMode = 8 << 4  // Block data transfer, block size: 256 B.
	Block512 DataMode = 9 << 4  // Block data transfer, block size: 512 B.
	Block1K  DataMode = 10 << 4 // Block data transfer, block size: 1 KiB.
	Block2K  DataMode = 11 << 4 // Block data transfer, block size: 2 KiB.
	Block4K  DataMode = 12 << 4 // Block data transfer, block size: 4 KiB.
	Block8K  DataMode = 13 << 4 // Block data transfer, block size: 8 KiB.
	Block16K DataMode = 14 << 4 // Block data transfer, block size: 16 KiB.
)

var ErrCmdTimeout = errors.New("sdio: cmd timeout")

type Host interface {
	// SetFreq sets the SD/SPI clock frequency to freqhz. Host can implement
	// disabling clock output if the bus is idle and pwrsave is set to true.
	SetFreq(freqhz int, pwrsave bool)

	// SetBusWidth allow to change the the host data bus width.
	SetBusWidth(width int)

	// Cmd sends the cmd to the card and receives its response, if any. Short
	// response is returned in r[0]. Long is returned in r[0:3] (r[0] contains
	// the least significant bits, r[3] contains the most significant bits).
	// If preceded by Data, Cmd performs data transfer.
	Cmd(cmd Command, arg uint32) (r Response)

	// Data prepares the data transfer for subsequent command.
	Data(mode DataMode, buf Data)

	// Err returns and clears the host internal error. The internal error, if
	// not nil, prevents any subsequent operations on the card. Host should
	// convert its internal command timeout error to ErrCmdTimeout.
	Err(clear bool) error
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/rds"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

func dataSourceAwsRdsCluster() *schema.Resource {
	return &schema.Resource{
		Read: dataSourceAwsRdsClusterRead,
		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"cluster_identifier": {
				Type:     schema.TypeString,
				Required: true,
			},

			"availability_zones": {
				Type:     schema.TypeSet,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Computed: true,
				Set:      schema.HashString,
			},

			"backup_retention_period": {
				Type:     schema.TypeInt,
				Computed: true,
			},

			"cluster_members": {
				Type:     schema.TypeSet,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Computed: true,
				Set:      schema.HashString,
			},

			"cluster_resource_id": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"database_name": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"db_subnet_group_name": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"db_cluster_parameter_group_name": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"enabled_cloudwatch_logs_exports": {
				Type:     schema.TypeList,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},

			"endpoint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"engine": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"engine_version": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"final_snapshot_identifier": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"iam_database_authentication_enabled": {
				Type:     schema.TypeBool,
				Computed: true,
			},

			"iam_roles": {
				Type:     schema.TypeSet,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},

			"kms_key_id": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"master_username": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"preferred_backup_window": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"preferred_maintenance_window": {
				Type:     schema.TypeString,
				Computed: true,
				StateFunc: func(val interface{}) string {
					if val == nil {
						return ""
					}
					return strings.ToLower(val.(string))
				},
			},

			"port": {
				Type:     schema.TypeInt,
				Computed: true,
			},

			"reader_endpoint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"hosted_zone_id": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"replication_source_identifier": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"storage_encrypted": {
				Type:     schema.TypeBool,
				Computed: true,
			},

			"tags": tagsSchemaComputed(),

			"vpc_security_group_ids": {
				Type:     schema.TypeSet,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},
		},
	}
}

func dataSourceAwsRdsClusterRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).rdsconn

	dbClusterIdentifier := d.Get("cluster_identifier").(string)

	params := &rds.DescribeDBClustersInput{
		DBClusterIdentifier: aws.String(dbClusterIdentifier),
	}
	log.Printf("[DEBUG] Reading RDS Cluster: %s", params)
	resp, err := conn.DescribeDBClusters(params)

	if err != nil {
		return fmt.Errorf("Error retrieving RDS cluster: %s", err)
	}

	if resp == nil {
		return fmt.Errorf("Error retrieving RDS cluster: empty response for: %s", params)
	}

	var dbc *rds.DBCluster
	for _, c := range resp.DBClusters {
		if aws.StringValue(c.DBClusterIdentifier) == dbClusterIdentifier {
			dbc = c
			break
		}
	}

	if dbc == nil {
		return fmt.Errorf("Error retrieving RDS cluster: cluster not found in response for: %s", params)
	}

	d.SetId(aws.StringValue(dbc.DBClusterIdentifier))

	if err := d.Set("availability_zones", aws.StringValueSlice(dbc.AvailabilityZones)); err != nil {
		return fmt.Errorf("error setting availability_zones: %s", err)
	}

	d.Set("arn", dbc.DBClusterArn)
	d.Set("backtrack_window", int(aws.Int64Value(dbc.BacktrackWindow)))
	d.Set("backup_retention_period", dbc.BackupRetentionPeriod)
	d.Set("cluster_identifier", dbc.DBClusterIdentifier)

	var cm []string
	for _, m := range dbc.DBClusterMembers {
		cm = append(cm, aws.StringValue(m.DBInstanceIdentifier))
	}
	if err := d.Set("cluster_members", cm); err != nil {
		return fmt.Errorf("error setting cluster_members: %s", err)
	}

	d.Set("cluster_resource_id", dbc.DbClusterResourceId)

	// Only set the DatabaseName if it is not nil. There is a known API bug where
	// RDS accepts a DatabaseName but does not return it, causing a perpetual
	// diff.
	//	See https://github.com/hashicorp/terraform/issues/4671 for backstory
	if dbc.DatabaseName != nil {
		d.Set("database_name", dbc.DatabaseName)
	}

	d.Set("db_cluster_parameter_group_name", dbc.DBClusterParameterGroup)
	d.Set("db_subnet_group_name", dbc.DBSubnetGroup)

	if err := d.Set("enabled_cloudwatch_logs_exports", aws.StringValueSlice(dbc.EnabledCloudwatchLogsExports)); err != nil {
		return fmt.Errorf("error setting enabled_cloudwatch_logs_exports: %s", err)
	}

	d.Set("endpoint", dbc.Endpoint)
	d.Set("engine_version", dbc.EngineVersion)
	d.Set("engine", dbc.Engine)
	d.Set("hosted_zone_id", dbc.HostedZoneId)
	d.Set("iam_database_authentication_enabled", dbc.IAMDatabaseAuthenticationEnabled)

	var roles []string
	for _, r := range dbc.AssociatedRoles {
		roles = append(roles, aws.StringValue(r.RoleArn))
	}
	if err := d.Set("iam_roles", roles); err != nil {
		return fmt.Errorf("error setting iam_roles: %s", err)
	}

	d.Set("kms_key_id", dbc.KmsKeyId)
	d.Set("master_username", dbc.MasterUsername)
	d.Set("port", dbc.Port)
	d.Set("preferred_backup_window", dbc.PreferredBackupWindow)
	d.Set("preferred_maintenance_window", dbc.PreferredMaintenanceWindow)
	d.Set("reader_endpoint", dbc.ReaderEndpoint)
	d.Set("replication_source_identifier", dbc.ReplicationSourceIdentifier)

	d.Set("storage_encrypted", dbc.StorageEncrypted)

	var vpcg []string
	for _, g := range dbc.VpcSecurityGroups {
		vpcg = append(vpcg, aws.StringValue(g.VpcSecurityGroupId))
	}
	if err := d.Set("vpc_security_group_ids", vpcg); err != nil {
		return fmt.Errorf("error setting vpc_security_group_ids: %s", err)
	}

	// Fetch and save tags
	if err := saveTagsRDS(conn, d, aws.StringValue(dbc.DBClusterArn)); err != nil {
		log.Printf("[WARN] Failed to save tags for RDS Cluster (%s): %s", aws.StringValue(dbc.DBClusterIdentifier), err)
	}

	return nil
}
<commit_msg>use keyvaluetags (#10724)<commit_after>package aws

import (
	"fmt"
	"log"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/rds"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
)

func dataSourceAwsRdsCluster() *schema.Resource {
	return &schema.Resource{
		Read: dataSourceAwsRdsClusterRead,
		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"cluster_identifier": {
				Type:     schema.TypeString,
				Required: true,
			},

			"availability_zones": {
				Type:     schema.TypeSet,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Computed: true,
				Set:      schema.HashString,
			},

			"backup_retention_period": {
				Type:     schema.TypeInt,
				Computed: true,
			},

			"cluster_members": {
				Type:     schema.TypeSet,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Computed: true,
				Set:      schema.HashString,
			},

			"cluster_resource_id": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"database_name": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"db_subnet_group_name": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"db_cluster_parameter_group_name": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"enabled_cloudwatch_logs_exports": {
				Type:     schema.TypeList,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},

			"endpoint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"engine": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"engine_version": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"final_snapshot_identifier": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"iam_database_authentication_enabled": {
				Type:     schema.TypeBool,
				Computed: true,
			},

			"iam_roles": {
				Type:     schema.TypeSet,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},

			"kms_key_id": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"master_username": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"preferred_backup_window": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"preferred_maintenance_window": {
				Type:     schema.TypeString,
				Computed: true,
				StateFunc: func(val interface{}) string {
					if val == nil {
						return ""
					}
					return strings.ToLower(val.(string))
				},
			},

			"port": {
				Type:     schema.TypeInt,
				Computed: true,
			},

			"reader_endpoint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"hosted_zone_id": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"replication_source_identifier": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"storage_encrypted": {
				Type:     schema.TypeBool,
				Computed: true,
			},

			"tags": tagsSchemaComputed(),

			"vpc_security_group_ids": {
				Type:     schema.TypeSet,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},
		},
	}
}

func dataSourceAwsRdsClusterRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).rdsconn

	dbClusterIdentifier := d.Get("cluster_identifier").(string)

	params := &rds.DescribeDBClustersInput{
		DBClusterIdentifier: aws.String(dbClusterIdentifier),
	}
	log.Printf("[DEBUG] Reading RDS Cluster: %s", params)
	resp, err := conn.DescribeDBClusters(params)

	if err != nil {
		return fmt.Errorf("Error retrieving RDS cluster: %s", err)
	}

	if resp == nil {
		return fmt.Errorf("Error retrieving RDS cluster: empty response for: %s", params)
	}

	var dbc *rds.DBCluster
	for _, c := range resp.DBClusters {
		if aws.StringValue(c.DBClusterIdentifier) == dbClusterIdentifier {
			dbc = c
			break
		}
	}

	if dbc == nil {
		return fmt.Errorf("Error retrieving RDS cluster: cluster not found in response for: %s", params)
	}

	d.SetId(aws.StringValue(dbc.DBClusterIdentifier))

	if err := d.Set("availability_zones", aws.StringValueSlice(dbc.AvailabilityZones)); err != nil {
		return fmt.Errorf("error setting availability_zones: %s", err)
	}

	arn := dbc.DBClusterArn
	d.Set("arn", arn)
	d.Set("backtrack_window", int(aws.Int64Value(dbc.BacktrackWindow)))
	d.Set("backup_retention_period", dbc.BackupRetentionPeriod)
	d.Set("cluster_identifier", dbc.DBClusterIdentifier)

	var cm []string
	for _, m := range dbc.DBClusterMembers {
		cm = append(cm, aws.StringValue(m.DBInstanceIdentifier))
	}
	if err := d.Set("cluster_members", cm); err != nil {
		return fmt.Errorf("error setting cluster_members: %s", err)
	}

	d.Set("cluster_resource_id", dbc.DbClusterResourceId)

	// Only set the DatabaseName if it is not nil. There is a known API bug where
	// RDS accepts a DatabaseName but does not return it, causing a perpetual
	// diff.
	//	See https://github.com/hashicorp/terraform/issues/4671 for backstory
	if dbc.DatabaseName != nil {
		d.Set("database_name", dbc.DatabaseName)
	}

	d.Set("db_cluster_parameter_group_name", dbc.DBClusterParameterGroup)
	d.Set("db_subnet_group_name", dbc.DBSubnetGroup)

	if err := d.Set("enabled_cloudwatch_logs_exports", aws.StringValueSlice(dbc.EnabledCloudwatchLogsExports)); err != nil {
		return fmt.Errorf("error setting enabled_cloudwatch_logs_exports: %s", err)
	}

	d.Set("endpoint", dbc.Endpoint)
	d.Set("engine_version", dbc.EngineVersion)
	d.Set("engine", dbc.Engine)
	d.Set("hosted_zone_id", dbc.HostedZoneId)
	d.Set("iam_database_authentication_enabled", dbc.IAMDatabaseAuthenticationEnabled)

	var roles []string
	for _, r := range dbc.AssociatedRoles {
		roles = append(roles, aws.StringValue(r.RoleArn))
	}
	if err := d.Set("iam_roles", roles); err != nil {
		return fmt.Errorf("error setting iam_roles: %s", err)
	}

	d.Set("kms_key_id", dbc.KmsKeyId)
	d.Set("master_username", dbc.MasterUsername)
	d.Set("port", dbc.Port)
	d.Set("preferred_backup_window", dbc.PreferredBackupWindow)
	d.Set("preferred_maintenance_window", dbc.PreferredMaintenanceWindow)
	d.Set("reader_endpoint", dbc.ReaderEndpoint)
	d.Set("replication_source_identifier", dbc.ReplicationSourceIdentifier)

	d.Set("storage_encrypted", dbc.StorageEncrypted)

	var vpcg []string
	for _, g := range dbc.VpcSecurityGroups {
		vpcg = append(vpcg, aws.StringValue(g.VpcSecurityGroupId))
	}
	if err := d.Set("vpc_security_group_ids", vpcg); err != nil {
		return fmt.Errorf("error setting vpc_security_group_ids: %s", err)
	}

	tags, err := keyvaluetags.RdsListTags(conn, *arn)

	if err != nil {
		return fmt.Errorf("error listing tags for RDS Cluster (%s): %s", *arn, err)
	}

	if err := d.Set("tags", tags.IgnoreAws().Map()); err != nil {
		return fmt.Errorf("error setting tags: %s", err)
	}

	return nil
}
<|endoftext|>


<commit_before>package parsley

// Context is the parsing context passed to all parsers
type Context struct {
	fileSet     *FileSet
	reader      Reader
	resultCache ResultCache
	err         Error
	callCount   int
}

// NewContext creates a new parsing context
func NewContext(fileSet *FileSet, reader Reader) *Context {
	return &Context{
		fileSet:     fileSet,
		reader:      reader,
		resultCache: NewResultCache(),
	}
}

// FileSet returns with the file set
func (c *Context) FileSet() *FileSet {
	return c.fileSet
}

// Reader returns with the reader
func (c *Context) Reader() Reader {
	return c.reader
}

// ResultCache returns with the result cache object
func (c *Context) ResultCache() ResultCache {
	return c.resultCache
}

// RegisterCall registers a call
func (c *Context) RegisterCall() {
	c.callCount++
}

// CallCount returns with the call count
func (c *Context) CallCount() int {
	return c.callCount
}

// SetError saves the error if it has the highest position for found errors
func (c *Context) SetError(err Error) {
	if err == nil {
		return
	}

	if c.err == nil || err.Pos() >= c.err.Pos() {
		c.err = err
	}
}

// Error returns with the parse error with the highest position (if any)
func (c *Context) Error() Error {
	return c.err
}
<commit_msg>Add keyword handling to context<commit_after>package parsley

// Context is the parsing context passed to all parsers
type Context struct {
	fileSet     *FileSet
	reader      Reader
	resultCache ResultCache
	err         Error
	callCount   int
	keywords    map[string]struct{}
}

// NewContext creates a new parsing context
func NewContext(fileSet *FileSet, reader Reader) *Context {
	return &Context{
		fileSet:     fileSet,
		reader:      reader,
		resultCache: NewResultCache(),
		keywords:    make(map[string]struct{}, 64),
	}
}

// FileSet returns with the file set
func (c *Context) FileSet() *FileSet {
	return c.fileSet
}

// Reader returns with the reader
func (c *Context) Reader() Reader {
	return c.reader
}

// ResultCache returns with the result cache object
func (c *Context) ResultCache() ResultCache {
	return c.resultCache
}

// RegisterCall registers a call
func (c *Context) RegisterCall() {
	c.callCount++
}

// CallCount returns with the call count
func (c *Context) CallCount() int {
	return c.callCount
}

// SetError saves the error if it has the highest position for found errors
func (c *Context) SetError(err Error) {
	if err == nil {
		return
	}

	if c.err == nil || err.Pos() >= c.err.Pos() {
		c.err = err
	}
}

// Error returns with the parse error with the highest position (if any)
func (c *Context) Error() Error {
	return c.err
}

// RegisterKeywords registers one or more keywords
func (c *Context) RegisterKeywords(keywords ...string) {
	for _, keyword := range keywords {
		c.keywords[keyword] = struct{}{}
	}
}

// IsKeyword checks if the given string is a keyword
func (c *Context) IsKeyword(word string) bool {
	_, ok := c.keywords[word]
	return ok
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"encoding/json"
	"os"
	"os/exec"
	"html"
	"strings"
	"flag"
	"fmt"
)

// cmdline argument
var portNumber = flag.Int("port", 8085, "Port number listening on")

type Command struct {
	Cmd string `json:"cmd"`
	Env string `json:"env"`
}

func handler(responseWriter http.ResponseWriter, request *http.Request) {
	defer request.Body.Close()

	cmdIn := &Command{}
	if err := json.NewDecoder(request.Body).Decode(&cmdIn); err != nil {
		writeResponse(responseWriter, http.StatusBadRequest, err.Error())
		return
	}

	cmdElements := strings.Split(cmdIn.Cmd, " ")
	cmd := exec.Command(cmdElements[0], cmdElements[1:]...)

	if len(cmdIn.Env) > 0 {
		env := os.Environ()
		env = append(env, cmdIn.Env)
		cmd.Env = env
	}

	path := html.EscapeString(request.URL.Path)
	if strings.HasSuffix(path, "/sync") {
		output, error := cmd.CombinedOutput()

		if error != nil {
			writeResponse(responseWriter, http.StatusBadRequest, error.Error())
			return
		}

		responseWriter.Header().Set("Content-Type", "application/json")
		writeResponse(responseWriter, http.StatusAccepted, string(output[:]))

	} else if strings.HasSuffix(path, "/async") {
		error := cmd.Start()

		if error != nil {
			writeResponse(responseWriter, http.StatusBadRequest, error.Error())
			return
		}

		writeResponse(responseWriter, http.StatusAccepted, "started")

	} else {
		writeResponse(responseWriter, http.StatusBadRequest, "use /sync or /async")

	}
}

func writeResponse(responseWriter http.ResponseWriter, code int, message string) {
	responseWriter.WriteHeader(code)
	data := struct {
			Code    int    `json:"code"`
			Message string `json:"message"`
    }{
		code,
		message,
	}
	responseWriter.Header().Set("Content-Type", "application/json")
	json.NewEncoder(responseWriter).Encode(&data)
}

func main() {
	flag.Parse()
	listenOn := fmt.Sprintf(":%v", *portNumber)

    http.HandleFunc("/", handler)
    http.ListenAndServe(listenOn, nil)
}<commit_msg>refomratting go code<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"html"
	"net/http"
	"os"
	"os/exec"
	"strings"
)

// cmdline argument
var portNumber = flag.Int("port", 8085, "Port number listening on")

type Command struct {
	Cmd string `json:"cmd"`
	Env string `json:"env"`
}

func handler(responseWriter http.ResponseWriter, request *http.Request) {
	defer request.Body.Close()

	cmdIn := &Command{}
	if err := json.NewDecoder(request.Body).Decode(&cmdIn); err != nil {
		writeResponse(responseWriter, http.StatusBadRequest, err.Error())
		return
	}

	cmdElements := strings.Split(cmdIn.Cmd, " ")
	cmd := exec.Command(cmdElements[0], cmdElements[1:]...)

	if len(cmdIn.Env) > 0 {
		env := os.Environ()
		env = append(env, cmdIn.Env)
		cmd.Env = env
	}

	path := html.EscapeString(request.URL.Path)
	if strings.HasSuffix(path, "/sync") {
		output, error := cmd.CombinedOutput()

		if error != nil {
			writeResponse(responseWriter, http.StatusBadRequest, error.Error())
			return
		}

		responseWriter.Header().Set("Content-Type", "application/json")
		writeResponse(responseWriter, http.StatusAccepted, string(output[:]))

	} else if strings.HasSuffix(path, "/async") {
		error := cmd.Start()

		if error != nil {
			writeResponse(responseWriter, http.StatusBadRequest, error.Error())
			return
		}

		writeResponse(responseWriter, http.StatusAccepted, "started")

	} else {
		writeResponse(responseWriter, http.StatusBadRequest, "use /sync or /async")

	}
}

func writeResponse(responseWriter http.ResponseWriter, code int, message string) {
	responseWriter.WriteHeader(code)
	data := struct {
		Code    int    `json:"code"`
		Message string `json:"message"`
	}{
		code,
		message,
	}
	responseWriter.Header().Set("Content-Type", "application/json")
	json.NewEncoder(responseWriter).Encode(&data)
}

func main() {
	flag.Parse()
	listenOn := fmt.Sprintf(":%v", *portNumber)

	http.HandleFunc("/", handler)
	http.ListenAndServe(listenOn, nil)
}
<|endoftext|>


<commit_before>package todolist

import (
	"fmt"
	"strconv"
	"testing"
	"time"

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

func TestAddTodo(t *testing.T) {
	assert := assert.New(t)
	app := &App{TodoList: &TodoList{}, TodoStore: &MemoryStore{}}
	year := strconv.Itoa(time.Now().Year())

	app.AddTodo("a do some stuff due may 23")

	todo := app.TodoList.FindById(1)
	assert.Equal("do some stuff", todo.Subject)
	assert.Equal(fmt.Sprintf("%s-05-23", year), todo.Due)
	assert.Equal(false, todo.Completed)
	assert.Equal(false, todo.Archived)
	assert.Equal(false, todo.IsPriority)
	assert.Equal("", todo.CompletedDate)
	assert.Equal([]string{}, todo.Projects)
	assert.Equal([]string{}, todo.Contexts)
}

func TestAddDoneTodo(t *testing.T) {
	assert := assert.New(t)
	app := &App{TodoList: &TodoList{}, TodoStore: &MemoryStore{}}

	app.AddDoneTodo("Groked how to do done todos @pop")

	todo := app.TodoList.FindById(1)
	assert.Equal("Groked how to do done todos @pop", todo.Subject)
	assert.Equal(true, todo.Completed)
	assert.Equal(false, todo.Archived)
	assert.Equal(false, todo.IsPriority)
	assert.Equal([]string{}, todo.Projects)
	assert.Equal(1, len(todo.Contexts))
	assert.Equal("pop", todo.Contexts[0])
}

func TestAddTodoWithEuropeanDates(t *testing.T) {
	assert := assert.New(t)
	app := &App{TodoList: &TodoList{}, TodoStore: &MemoryStore{}}

	app.AddTodo("a do some stuff due 23 may")

	todo := app.TodoList.FindById(1)
	assert.Equal("do some stuff", todo.Subject)
	assert.Equal("2017-05-23", todo.Due)
	assert.Equal(false, todo.Completed)
	assert.Equal(false, todo.Archived)
	assert.Equal(false, todo.IsPriority)
	assert.Equal("", todo.CompletedDate)
	assert.Equal([]string{}, todo.Projects)
	assert.Equal([]string{}, todo.Contexts)
}

func TestAddEmptyTodo(t *testing.T) {
	assert := assert.New(t)
	app := &App{TodoList: &TodoList{}, TodoStore: &MemoryStore{}}

	app.AddTodo("a")
	app.AddTodo("a      ")
	app.AddTodo("a\t\t\t\t")
	app.AddTodo("a\t \t  \t   \t")

	assert.Equal(len(app.TodoList.Data), 0)
}

func TestListbyProject(t *testing.T) {
	assert := assert.New(t)
	app := &App{TodoList: &TodoList{}, TodoStore: &MemoryStore{}}
	app.Load()

	// create three todos w/wo a project
	app.AddTodo("this is a test +testme")
	app.AddTodo("this is a test +testmetoo @work")
	app.AddTodo("this is a test with no projects")
	app.CompleteTodo("c 1")

	// simulate listTodos
	input := "l by p"
	filtered := NewFilter(app.TodoList.Todos()).Filter(input)
	grouped := app.getGroups(input, filtered)

	assert.Equal(3, len(grouped.Groups))

	// testme project has 1 todo and its completed
	assert.Equal(1, len(grouped.Groups["testme"]))
	assert.Equal(true, grouped.Groups["testme"][0].Completed)

	// testmetoo project has 1 todo and it has a context
	assert.Equal(1, len(grouped.Groups["testmetoo"]))
	assert.Equal(1, len(grouped.Groups["testmetoo"][0].Contexts))
	assert.Equal("work", grouped.Groups["testmetoo"][0].Contexts[0])
}

func TestListbyContext(t *testing.T) {
	assert := assert.New(t)
	app := &App{TodoList: &TodoList{}, TodoStore: &MemoryStore{}}
	app.Load()

	// create three todos w/wo a context
	app.AddTodo("this is a test +testme")
	app.AddTodo("this is a test +testmetoo @work")
	app.AddTodo("this is a test with no projects")
	app.CompleteTodo("c 1")

	// simulate listTodos
	input := "l by c"
	filtered := NewFilter(app.TodoList.Todos()).Filter(input)
	grouped := app.getGroups(input, filtered)

	assert.Equal(2, len(grouped.Groups))

	// work context has 1 todo and it has a project of testmetoo
	assert.Equal(1, len(grouped.Groups["work"]))
	assert.Equal(1, len(grouped.Groups["work"][0].Projects))
	assert.Equal("testmetoo", grouped.Groups["work"][0].Projects[0])

	// There are two todos with no context
	assert.Equal(2, len(grouped.Groups["No contexts"]))

	// check to see if the a todos with no context contain a
	// completed todo
	var hasACompletedTodo bool
	for _, todo := range grouped.Groups["No contexts"] {
		if todo.Completed {
			hasACompletedTodo = true
		}
	}
	assert.Equal(true, hasACompletedTodo)
}

func TestGetId(t *testing.T) {
	assert := assert.New(t)
	app := &App{TodoList: &TodoList{}, TodoStore: &MemoryStore{}}
	// not a valid id
	assert.Equal(-1, app.getId("p"))
	// a single digit id
	assert.Equal(6, app.getId("6"))
	// a double digit id
	assert.Equal(66, app.getId("66"))
}

func TestGetIds(t *testing.T) {
	assert := assert.New(t)
	app := &App{TodoList: &TodoList{}, TodoStore: &MemoryStore{}}
	// no valid id here
	assert.Equal(0, len(app.getIds("p")))
	// one valid value here
	assert.Equal([]int{6}, app.getIds("6"))
	// lots of single post numbers
	assert.Equal([]int{6, 10, 8, 4}, app.getIds("6,10,8,4"))
	// a correct range
	assert.Equal([]int{6, 7, 8}, app.getIds("6-8"))
	// some incorrect ranges
	assert.Equal(0, len(app.getIds("6-6")))
	assert.Equal(0, len(app.getIds("8-6")))
	// some compsite ranges
	assert.Equal([]int{5, 6, 7, 8, 10, 11, 9}, app.getIds("5,6-8,10-11,9"))
}
<commit_msg>fix test failing due to new year<commit_after>package todolist

import (
	"fmt"
	"strconv"
	"testing"
	"time"

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

func TestAddTodo(t *testing.T) {
	assert := assert.New(t)
	app := &App{TodoList: &TodoList{}, TodoStore: &MemoryStore{}}
	year := strconv.Itoa(time.Now().Year())

	app.AddTodo("a do some stuff due may 23")

	todo := app.TodoList.FindById(1)
	assert.Equal("do some stuff", todo.Subject)
	assert.Equal(fmt.Sprintf("%s-05-23", year), todo.Due)
	assert.Equal(false, todo.Completed)
	assert.Equal(false, todo.Archived)
	assert.Equal(false, todo.IsPriority)
	assert.Equal("", todo.CompletedDate)
	assert.Equal([]string{}, todo.Projects)
	assert.Equal([]string{}, todo.Contexts)
}

func TestAddDoneTodo(t *testing.T) {
	assert := assert.New(t)
	app := &App{TodoList: &TodoList{}, TodoStore: &MemoryStore{}}

	app.AddDoneTodo("Groked how to do done todos @pop")

	todo := app.TodoList.FindById(1)
	assert.Equal("Groked how to do done todos @pop", todo.Subject)
	assert.Equal(true, todo.Completed)
	assert.Equal(false, todo.Archived)
	assert.Equal(false, todo.IsPriority)
	assert.Equal([]string{}, todo.Projects)
	assert.Equal(1, len(todo.Contexts))
	assert.Equal("pop", todo.Contexts[0])
}

func TestAddTodoWithEuropeanDates(t *testing.T) {
	assert := assert.New(t)
	app := &App{TodoList: &TodoList{}, TodoStore: &MemoryStore{}}
	year := strconv.Itoa(time.Now().Year())

	app.AddTodo("a do some stuff due 23 may")

	todo := app.TodoList.FindById(1)
	assert.Equal("do some stuff", todo.Subject)
	assert.Equal(fmt.Sprintf("%s-05-23", year), todo.Due)
	assert.Equal(false, todo.Completed)
	assert.Equal(false, todo.Archived)
	assert.Equal(false, todo.IsPriority)
	assert.Equal("", todo.CompletedDate)
	assert.Equal([]string{}, todo.Projects)
	assert.Equal([]string{}, todo.Contexts)
}

func TestAddEmptyTodo(t *testing.T) {
	assert := assert.New(t)
	app := &App{TodoList: &TodoList{}, TodoStore: &MemoryStore{}}

	app.AddTodo("a")
	app.AddTodo("a      ")
	app.AddTodo("a\t\t\t\t")
	app.AddTodo("a\t \t  \t   \t")

	assert.Equal(len(app.TodoList.Data), 0)
}

func TestListbyProject(t *testing.T) {
	assert := assert.New(t)
	app := &App{TodoList: &TodoList{}, TodoStore: &MemoryStore{}}
	app.Load()

	// create three todos w/wo a project
	app.AddTodo("this is a test +testme")
	app.AddTodo("this is a test +testmetoo @work")
	app.AddTodo("this is a test with no projects")
	app.CompleteTodo("c 1")

	// simulate listTodos
	input := "l by p"
	filtered := NewFilter(app.TodoList.Todos()).Filter(input)
	grouped := app.getGroups(input, filtered)

	assert.Equal(3, len(grouped.Groups))

	// testme project has 1 todo and its completed
	assert.Equal(1, len(grouped.Groups["testme"]))
	assert.Equal(true, grouped.Groups["testme"][0].Completed)

	// testmetoo project has 1 todo and it has a context
	assert.Equal(1, len(grouped.Groups["testmetoo"]))
	assert.Equal(1, len(grouped.Groups["testmetoo"][0].Contexts))
	assert.Equal("work", grouped.Groups["testmetoo"][0].Contexts[0])
}

func TestListbyContext(t *testing.T) {
	assert := assert.New(t)
	app := &App{TodoList: &TodoList{}, TodoStore: &MemoryStore{}}
	app.Load()

	// create three todos w/wo a context
	app.AddTodo("this is a test +testme")
	app.AddTodo("this is a test +testmetoo @work")
	app.AddTodo("this is a test with no projects")
	app.CompleteTodo("c 1")

	// simulate listTodos
	input := "l by c"
	filtered := NewFilter(app.TodoList.Todos()).Filter(input)
	grouped := app.getGroups(input, filtered)

	assert.Equal(2, len(grouped.Groups))

	// work context has 1 todo and it has a project of testmetoo
	assert.Equal(1, len(grouped.Groups["work"]))
	assert.Equal(1, len(grouped.Groups["work"][0].Projects))
	assert.Equal("testmetoo", grouped.Groups["work"][0].Projects[0])

	// There are two todos with no context
	assert.Equal(2, len(grouped.Groups["No contexts"]))

	// check to see if the a todos with no context contain a
	// completed todo
	var hasACompletedTodo bool
	for _, todo := range grouped.Groups["No contexts"] {
		if todo.Completed {
			hasACompletedTodo = true
		}
	}
	assert.Equal(true, hasACompletedTodo)
}

func TestGetId(t *testing.T) {
	assert := assert.New(t)
	app := &App{TodoList: &TodoList{}, TodoStore: &MemoryStore{}}
	// not a valid id
	assert.Equal(-1, app.getId("p"))
	// a single digit id
	assert.Equal(6, app.getId("6"))
	// a double digit id
	assert.Equal(66, app.getId("66"))
}

func TestGetIds(t *testing.T) {
	assert := assert.New(t)
	app := &App{TodoList: &TodoList{}, TodoStore: &MemoryStore{}}
	// no valid id here
	assert.Equal(0, len(app.getIds("p")))
	// one valid value here
	assert.Equal([]int{6}, app.getIds("6"))
	// lots of single post numbers
	assert.Equal([]int{6, 10, 8, 4}, app.getIds("6,10,8,4"))
	// a correct range
	assert.Equal([]int{6, 7, 8}, app.getIds("6-8"))
	// some incorrect ranges
	assert.Equal(0, len(app.getIds("6-6")))
	assert.Equal(0, len(app.getIds("8-6")))
	// some compsite ranges
	assert.Equal([]int{5, 6, 7, 8, 10, 11, 9}, app.getIds("5,6-8,10-11,9"))
}
<|endoftext|>


<commit_before><commit_msg>blockmanager: move db read condition last<commit_after><|endoftext|>


<commit_before>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package listener

import (
	"github.com/webx-top/com"
	"github.com/webx-top/db"
	"github.com/webx-top/db/lib/factory"

	"github.com/admpub/nging/application/dbschema"
	modelFile "github.com/admpub/nging/application/model/file"
)

var DBI = func() *factory.DBI {
	return dbschema.DBI
}

// New 实例化监听器具
func New(cb Callback, embedded bool, seperators ...string) *FileRelation {
	var seperator string
	if len(seperators) > 0 {
		seperator = seperators[0]
	}
	return &FileRelation{Embedded: embedded, Seperator: seperator, callback: cb}
}

type Callback func(m factory.Model) (tableID string, content string, property *Property)

type Property struct {
	Embedded  bool   // 是否为嵌入图片
	Seperator string // 文件字段中多个文件路径之间的分隔符，空字符串代表为单个文件
	Update    func(event string, content string) error
	Exit      bool
}

func NewProperty() *Property {
	return &Property{}
}

func NewProUP(m factory.Model, cond db.Compound) *Property {
	return NewProperty().GenUpdater(m, cond)
}

func (pro *Property) GenUpdater(m factory.Model, cond db.Compound) *Property {
	pro.Update = func(field string, content string) error {
		err := m.EventOFF().SetField(nil, field, content, cond)
		m.EventON()
		return err
	}
	return pro
}

// FileRelation 文件关联数据监听
// FileRelation.SetTable(`table`,`field`).ListenDefault()
type FileRelation struct {
	TableName  string      // 数据表名称
	FieldName  string      // 数据表字段名
	UpdateCond interface{} // 主键字段名称/或
	Embedded   bool        // 是否为嵌入图片
	Seperator  string      // 文件字段中多个文件路径之间的分隔符，空字符串代表为单个文件
	callback   Callback    //根据模型获取表行ID和内容
	dbi        *factory.DBI
}

func (f *FileRelation) SetSeperator(seperator string) *FileRelation {
	f.Seperator = seperator
	return f
}

func (f *FileRelation) Callback() Callback {
	return f.callback
}

func (f *FileRelation) SetTable(table string, field string) *FileRelation {
	f.TableName = table
	f.FieldName = field
	return f
}

func (f *FileRelation) SetDBI(dbi *factory.DBI) *FileRelation {
	f.dbi = dbi
	return f
}

func (f *FileRelation) SetEmbedded(embedded bool) *FileRelation {
	f.Embedded = embedded
	return f
}

func (f *FileRelation) ListenDefault() *FileRelation {
	return f.Listen(`created`, `updated`, `deleted`)
}

func (f *FileRelation) attachUpdateEvent(event string) func(m factory.Model, editColumns ...string) error {
	seperator := f.Seperator
	embedded := f.Embedded
	callback := f.callback
	return func(m factory.Model, editColumns ...string) error {
		if len(editColumns) > 0 && !com.InSlice(f.FieldName, editColumns) {
			return nil
		}
		fileM := modelFile.NewEmbedded(m.Context())
		tableID, content, property := callback(m)
		var update func(string, string) error
		if property != nil {
			if property.Exit {
				return nil
			}
			seperator = property.Seperator
			embedded = property.Embedded
			update = property.Update
		}
		//println(event+`=========`, f.TableName, f.FieldName, tableID, content)
		if update == nil {
			return fileM.Updater(f.TableName, f.FieldName, tableID).SetSeperator(seperator).Handle(event, &content, embedded)
		}
		rawContent := content
		err := fileM.Updater(f.TableName, f.FieldName, tableID).SetSeperator(seperator).Handle(event, &content, embedded)
		if err != nil {
			return err
		}
		if rawContent != content {
			err = update(f.FieldName, content)
		}
		return err
	}
}

func (f *FileRelation) attachEvent(event string) func(m factory.Model, editColumns ...string) error {
	seperator := f.Seperator
	embedded := f.Embedded
	callback := f.callback
	return func(m factory.Model, _ ...string) error {
		fileM := modelFile.NewEmbedded(m.Context())
		tableID, content, property := callback(m)
		var update func(string, string) error
		if property != nil {
			if property.Exit {
				return nil
			}
			seperator = property.Seperator
			embedded = property.Embedded
			update = property.Update
		}
		if update == nil {
			return fileM.Updater(f.TableName, f.FieldName, tableID).SetSeperator(seperator).Handle(event, &content, embedded)
		}
		rawContent := content
		err := fileM.Updater(f.TableName, f.FieldName, tableID).SetSeperator(seperator).Handle(event, &content, embedded)
		if err != nil {
			return err
		}
		if rawContent != content {
			err = update(f.FieldName, content)
		}
		return err
	}
}

func (f *FileRelation) attachDeleteEvent(event string) func(m factory.Model, editColumns ...string) error {
	seperator := f.Seperator
	embedded := f.Embedded
	callback := f.callback
	return func(m factory.Model, _ ...string) error {
		fileM := modelFile.NewEmbedded(m.Context())
		tableID, content, property := callback(m)
		if property != nil {
			if property.Exit {
				return nil
			}
			seperator = property.Seperator
			embedded = property.Embedded
		}
		return fileM.Updater(f.TableName, f.FieldName, tableID).SetSeperator(seperator).Handle(event, &content, embedded)
	}
}

func (f *FileRelation) DBI() *factory.DBI {
	dbi := f.dbi
	if dbi == nil {
		dbi = DBI()
	}
	return dbi
}

func (f *FileRelation) Listen(events ...string) *FileRelation {
	dbi := f.DBI()
	for _, event := range events {
		switch event {
		case `updating`, `updated`:
			dbi.On(event, f.attachUpdateEvent(event), f.TableName)
		case `deleting`, `deleted`:
			dbi.On(event, f.attachDeleteEvent(event), f.TableName)
		default:
			dbi.On(event, f.attachEvent(event), f.TableName)
		}
	}
	return f
}

func (f *FileRelation) On(event string, h factory.EventHandler) *FileRelation {
	f.DBI().On(event, h, f.TableName)
	return f
}
<commit_msg>update<commit_after>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package listener

import (
	"github.com/webx-top/com"
	"github.com/webx-top/db"
	"github.com/webx-top/db/lib/factory"

	"github.com/admpub/nging/application/dbschema"
	modelFile "github.com/admpub/nging/application/model/file"
)

var DBI = func() *factory.DBI {
	return dbschema.DBI
}

// New 实例化监听器具
func New(cb Callback, embedded bool, seperators ...string) *FileRelation {
	var seperator string
	if len(seperators) > 0 {
		seperator = seperators[0]
	}
	return &FileRelation{Embedded: embedded, Seperator: seperator, callback: cb}
}

type Callback func(m factory.Model) (tableID string, content string, property *Property)

type Property struct {
	Embedded  bool   // 是否为嵌入图片
	Seperator string // 文件字段中多个文件路径之间的分隔符，空字符串代表为单个文件
	Update    func(event string, content string) error
	Exit      bool
}

func NewProperty() *Property {
	return &Property{}
}

func NewProUP(m factory.Model, cond db.Compound) *Property {
	return NewProperty().GenUpdater(m, cond)
}

func (pro *Property) GenUpdater(m factory.Model, cond db.Compound) *Property {
	pro.Update = func(field string, content string) error {
		err := m.EventOFF().SetField(nil, field, content, cond)
		m.EventON()
		return err
	}
	return pro
}

// FileRelation 文件关联数据监听
// FileRelation.SetTable(`table`,`field`).ListenDefault()
type FileRelation struct {
	TableName string   // 数据表名称
	FieldName string   // 数据表字段名
	Embedded  bool     // 是否为嵌入图片
	Seperator string   // 文件字段中多个文件路径之间的分隔符，空字符串代表为单个文件
	callback  Callback //根据模型获取表行ID和内容
	dbi       *factory.DBI
}

func (f *FileRelation) SetSeperator(seperator string) *FileRelation {
	f.Seperator = seperator
	return f
}

func (f *FileRelation) Callback() Callback {
	return f.callback
}

func (f *FileRelation) SetTable(table string, field string) *FileRelation {
	f.TableName = table
	f.FieldName = field
	return f
}

func (f *FileRelation) SetDBI(dbi *factory.DBI) *FileRelation {
	f.dbi = dbi
	return f
}

func (f *FileRelation) SetEmbedded(embedded bool) *FileRelation {
	f.Embedded = embedded
	return f
}

func (f *FileRelation) ListenDefault() *FileRelation {
	return f.Listen(`created`, `updated`, `deleted`)
}

func (f *FileRelation) attachUpdateEvent(event string) func(m factory.Model, editColumns ...string) error {
	seperator := f.Seperator
	embedded := f.Embedded
	callback := f.callback
	return func(m factory.Model, editColumns ...string) error {
		if len(editColumns) > 0 && !com.InSlice(f.FieldName, editColumns) {
			return nil
		}
		fileM := modelFile.NewEmbedded(m.Context())
		tableID, content, property := callback(m)
		var update func(string, string) error
		if property != nil {
			if property.Exit {
				return nil
			}
			seperator = property.Seperator
			embedded = property.Embedded
			update = property.Update
		}
		//println(event+`=========`, f.TableName, f.FieldName, tableID, content)
		if update == nil {
			return fileM.Updater(f.TableName, f.FieldName, tableID).SetSeperator(seperator).Handle(event, &content, embedded)
		}
		rawContent := content
		err := fileM.Updater(f.TableName, f.FieldName, tableID).SetSeperator(seperator).Handle(event, &content, embedded)
		if err != nil {
			return err
		}
		if rawContent != content {
			err = update(f.FieldName, content)
		}
		return err
	}
}

func (f *FileRelation) attachEvent(event string) func(m factory.Model, editColumns ...string) error {
	seperator := f.Seperator
	embedded := f.Embedded
	callback := f.callback
	return func(m factory.Model, _ ...string) error {
		fileM := modelFile.NewEmbedded(m.Context())
		tableID, content, property := callback(m)
		var update func(string, string) error
		if property != nil {
			if property.Exit {
				return nil
			}
			seperator = property.Seperator
			embedded = property.Embedded
			update = property.Update
		}
		if update == nil {
			return fileM.Updater(f.TableName, f.FieldName, tableID).SetSeperator(seperator).Handle(event, &content, embedded)
		}
		rawContent := content
		err := fileM.Updater(f.TableName, f.FieldName, tableID).SetSeperator(seperator).Handle(event, &content, embedded)
		if err != nil {
			return err
		}
		if rawContent != content {
			err = update(f.FieldName, content)
		}
		return err
	}
}

func (f *FileRelation) attachDeleteEvent(event string) func(m factory.Model, editColumns ...string) error {
	seperator := f.Seperator
	embedded := f.Embedded
	callback := f.callback
	return func(m factory.Model, _ ...string) error {
		fileM := modelFile.NewEmbedded(m.Context())
		tableID, content, property := callback(m)
		if property != nil {
			if property.Exit {
				return nil
			}
			seperator = property.Seperator
			embedded = property.Embedded
		}
		return fileM.Updater(f.TableName, f.FieldName, tableID).SetSeperator(seperator).Handle(event, &content, embedded)
	}
}

func (f *FileRelation) DBI() *factory.DBI {
	dbi := f.dbi
	if dbi == nil {
		dbi = DBI()
	}
	return dbi
}

func (f *FileRelation) Listen(events ...string) *FileRelation {
	dbi := f.DBI()
	for _, event := range events {
		switch event {
		case `updating`, `updated`:
			dbi.On(event, f.attachUpdateEvent(event), f.TableName)
		case `deleting`, `deleted`:
			dbi.On(event, f.attachDeleteEvent(event), f.TableName)
		default:
			dbi.On(event, f.attachEvent(event), f.TableName)
		}
	}
	return f
}

func (f *FileRelation) On(event string, h factory.EventHandler) *FileRelation {
	f.DBI().On(event, h, f.TableName)
	return f
}
<|endoftext|>


<commit_before>package errors_test

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/adrg/errors"
)

func ExampleNew() {
	err := errors.New("something bad happened")

	fmt.Printf("%s\n", err)  // Print error message only.
	fmt.Printf("%v\n", err)  // Print error chain without stack details.
	fmt.Printf("%+v\n", err) // Print error chain with stack details.
}

func ExampleErrorf() {
	err := errors.Errorf("invalid user ID: %d", 0)

	fmt.Printf("%s\n", err)  // Print error message only.
	fmt.Printf("%v\n", err)  // Print error chain without stack details.
	fmt.Printf("%+v\n", err) // Print error chain with stack details.
}

func ExampleAnnotate() {
	_, err := strconv.Atoi("this will fail")

	// No need to check if the original error is nil.
	// If err is nil, Annotate returns nil.
	err = errors.Annotate(err, "conversion failed")

	fmt.Printf("%s\n", err)  // Print error message only.
	fmt.Printf("%v\n", err)  // Print error chain without stack details.
	fmt.Printf("%+v\n", err) // Print error chain with stack details.
}

func ExampleAnnotatef() {
	var err error

	email := "alice @example.com"
	if strings.Contains(email, " ") {
		err = errors.New("email address cannot contain spaces")
	}

	// No need to check if the original error is nil.
	// If err is nil, Annotatef returns nil.
	err = errors.Annotatef(err, "invalid email adddress %s", email)

	fmt.Printf("%s\n", err)  // Print error message only.
	fmt.Printf("%v\n", err)  // Print error chain without stack details.
	fmt.Printf("%+v\n", err) // Print error chain with stack details.
}

func ExampleWrap() {
	errInvalid := errors.New("invalid data")

	_, err := strconv.Atoi("this will fail")
	if err != nil {
		err = errors.Wrap(errInvalid, err)
	}

	fmt.Printf("%s\n", err)  // Print error message only.
	fmt.Printf("%v\n", err)  // Print error chain without stack details.
	fmt.Printf("%+v\n", err) // Print error chain with stack details.
}

func ExampleUnwrap() {
	err := errors.Annotate(errors.New("first error"), "second error")
	if next := errors.Unwrap(err); next != nil {
		fmt.Printf("%s\n", next)  // Print error message only.
		fmt.Printf("%v\n", next)  // Print error chain without stack details.
		fmt.Printf("%+v\n", next) // Print error chain with stack details.
	}
}

func ExampleIs() {
	errInvalid := errors.New("invalid data")

	err := errors.Annotate(errInvalid, "validation failed")
	if errors.Is(err, errInvalid) {
		fmt.Println("err is invalidErr")
	}
}

func ExampleAs() {
	_, err := strconv.Atoi("this will fail")

	// No need to check if the original error is nil.
	// If err is nil, Annotate returns nil.
	err = errors.Annotate(err, "conversion failed")

	nErr := &strconv.NumError{}
	if ok := errors.As(err, &nErr); ok {
		fmt.Printf("error %s: parsing `%s`: %s\n", nErr.Func, nErr.Num, nErr.Err)
	}
}

func ExampleAs_interface() {
	err := errors.NewHTTP(nil, 500, "something bad happened")
	err = errors.Annotate(err, "annotated HTTP error interface")

	var nErr errors.HTTP
	if ok := errors.As(err, &nErr); ok {
		fmt.Printf("%s\n", err)        // Print error message only.
		fmt.Printf("%v\n", err)        // Print error chain without stack details.
		fmt.Printf("%+v\n", err)       // Print error chain with stack details.
		fmt.Println(errors.Code(nErr)) // Print error code
	}
}

func ExampleCode() {
	err := errors.HTTPf(nil, http.StatusNotFound, "invalid endpoint")

	// Print error code.
	fmt.Println(errors.Code(err))
}

func ExampleNewHTTP() {
	_, err := strconv.Atoi("this will fail")
	if err != nil {
		err = errors.NewHTTP(err, http.StatusBadRequest, "invalid data")

		fmt.Printf("%s\n", err)       // Print error message only.
		fmt.Printf("%v\n", err)       // Print error chain without stack details.
		fmt.Printf("%+v\n", err)      // Print error chain with stack details.
		fmt.Println(errors.Code(err)) // Print error code.
	}
}

func ExampleHTTPf() {
	validateEmail := func(email string) error {
		if strings.Contains(email, " ") {
			return errors.New("email address cannot contain spaces")
		}

		return nil
	}

	email := "alice @example.com"
	if err := validateEmail(email); err != nil {
		err = errors.HTTPf(err, http.StatusBadRequest, "invalid email address %s", email)

		fmt.Printf("%s\n", err)       // Print error message only.
		fmt.Printf("%v\n", err)       // Print error chain without stack details.
		fmt.Printf("%+v\n", err)      // Print error chain with stack details.
		fmt.Println(errors.Code(err)) // Print error code.
	}
}
<commit_msg>Update function example<commit_after>package errors_test

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/adrg/errors"
)

func ExampleNew() {
	err := errors.New("something bad happened")

	fmt.Printf("%s\n", err)  // Print error message only.
	fmt.Printf("%v\n", err)  // Print error chain without stack details.
	fmt.Printf("%+v\n", err) // Print error chain with stack details.
}

func ExampleErrorf() {
	err := errors.Errorf("invalid user ID: %d", 0)

	fmt.Printf("%s\n", err)  // Print error message only.
	fmt.Printf("%v\n", err)  // Print error chain without stack details.
	fmt.Printf("%+v\n", err) // Print error chain with stack details.
}

func ExampleAnnotate() {
	_, err := strconv.Atoi("this will fail")

	// No need to check if the original error is nil.
	// If err is nil, Annotate returns nil.
	err = errors.Annotate(err, "conversion failed")

	fmt.Printf("%s\n", err)  // Print error message only.
	fmt.Printf("%v\n", err)  // Print error chain without stack details.
	fmt.Printf("%+v\n", err) // Print error chain with stack details.
}

func ExampleAnnotatef() {
	var err error

	email := "alice @example.com"
	if strings.Contains(email, " ") {
		err = errors.New("email address cannot contain spaces")
	}

	// No need to check if the original error is nil.
	// If err is nil, Annotatef returns nil.
	err = errors.Annotatef(err, "invalid email adddress %s", email)

	fmt.Printf("%s\n", err)  // Print error message only.
	fmt.Printf("%v\n", err)  // Print error chain without stack details.
	fmt.Printf("%+v\n", err) // Print error chain with stack details.
}

func ExampleWrap() {
	errInvalid := errors.New("invalid data")

	_, err := strconv.Atoi("this will fail")
	if err != nil {
		err = errors.Wrap(errInvalid, err)
	}

	fmt.Printf("%s\n", err)  // Print error message only.
	fmt.Printf("%v\n", err)  // Print error chain without stack details.
	fmt.Printf("%+v\n", err) // Print error chain with stack details.
}

func ExampleUnwrap() {
	err := errors.Annotate(errors.New("first error"), "second error")
	if next := errors.Unwrap(err); next != nil {
		fmt.Printf("%s\n", next)  // Print error message only.
		fmt.Printf("%v\n", next)  // Print error chain without stack details.
		fmt.Printf("%+v\n", next) // Print error chain with stack details.
	}
}

func ExampleIs() {
	errInvalid := errors.New("invalid data")

	err := errors.Annotate(errInvalid, "validation failed")
	if errors.Is(err, errInvalid) {
		fmt.Println("err is invalidErr")
	}
}

func ExampleAs() {
	_, err := strconv.Atoi("this will fail")

	// No need to check if the original error is nil.
	// If err is nil, Annotate returns nil.
	err = errors.Annotate(err, "conversion failed")

	nErr := &strconv.NumError{}
	if ok := errors.As(err, &nErr); ok {
		fmt.Printf("error %s: parsing `%s`: %s\n", nErr.Func, nErr.Num, nErr.Err)
	}
}

func ExampleAs_interface() {
	err := errors.NewHTTP(nil, 500, "something bad happened")
	err = errors.Annotate(err, "annotated HTTP error interface")

	var nErr errors.HTTP
	if ok := errors.As(err, &nErr); ok {
		fmt.Printf("%s\n", err)        // Print error message only.
		fmt.Printf("%v\n", err)        // Print error chain without stack details.
		fmt.Printf("%+v\n", err)       // Print error chain with stack details.
		fmt.Println(errors.Code(nErr)) // Print error code
	}
}

func ExampleCode() {
	err := errors.HTTPf(nil, http.StatusNotFound, "invalid endpoint")

	// Print error code.
	fmt.Println(errors.Code(err))
}

func ExampleNewHTTP() {
	_, err := strconv.Atoi("this will fail")
	if err != nil {
		err = errors.NewHTTP(err, http.StatusBadRequest, "invalid data")

		fmt.Printf("%s\n", err)       // Print error message only.
		fmt.Printf("%v\n", err)       // Print error chain without stack details.
		fmt.Printf("%+v\n", err)      // Print error chain with stack details.
		fmt.Println(errors.Code(err)) // Print error code.
	}
}

func ExampleHTTPf() {
	validateEmail := func(email string) error {
		if strings.Contains(email, " ") {
			return errors.New("email address cannot contain spaces")
		}

		return nil
	}

	email := "alice @example.com"
	if err := validateEmail(email); err != nil {
		err = errors.HTTPf(err, http.StatusBadRequest, "invalid email %s", email)

		fmt.Printf("%s\n", err)       // Print error message only.
		fmt.Printf("%v\n", err)       // Print error chain without stack details.
		fmt.Printf("%+v\n", err)      // Print error chain with stack details.
		fmt.Println(errors.Code(err)) // Print error code.
	}
}
<|endoftext|>