<commit_before>package ipfs

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	gohttp "net/http"
	"time"

	ipfs "github.com/ipfs/go-ipfs-api"
	"github.com/johnny-morrice/godless/crdt"
	"github.com/johnny-morrice/godless/internal/http"
	"github.com/johnny-morrice/godless/log"
	"github.com/pkg/errors"
)

type IPFSRecord struct {
	Namespace crdt.Namespace
}

func makeIpfsRecord(namespace crdt.Namespace) *IPFSRecord {
	return &IPFSRecord{
		Namespace: namespace,
	}
}

func (record *IPFSRecord) encode(w io.Writer) error {
	return crdt.EncodeNamespace(record.Namespace, w)
}

func (record *IPFSRecord) decode(r io.Reader) error {
	ns, err := crdt.DecodeNamespace(r)

	if err != nil {
		return err
	}

	record.Namespace = ns
	return nil
}

type encoder interface {
	encode(io.Writer) error
}

type decoder interface {
	decode(io.Reader) error
}

type IPFSIndex struct {
	Index crdt.Index
}

func makeIpfsIndex(index crdt.Index) *IPFSIndex {
	return &IPFSIndex{
		Index: index,
	}
}

func (index *IPFSIndex) encode(w io.Writer) error {
	return crdt.EncodeIndex(index.Index, w)
}

func (index *IPFSIndex) decode(r io.Reader) error {
	dx, err := crdt.DecodeIndex(r)

	if err != nil {
		return err
	}

	index.Index = dx
	return nil
}

// TODO Don't use Shell directly - invent an interface.  This would enable mocking.
type IPFSPeer struct {
	Url         string
	Client      *gohttp.Client
	Shell       *ipfs.Shell
	PingTimeout time.Duration
	pinger      *ipfs.Shell
}

func (peer *IPFSPeer) Connect() error {
	if peer.PingTimeout == 0 {
		peer.PingTimeout = __DEFAULT_PING_TIMEOUT
	}

	if peer.Client == nil {
		peer.Client = http.DefaultBackendClient()
	}

	log.Info("Connecting to IPFS API...")
	pingClient := http.DefaultBackendClient()
	pingClient.Timeout = peer.PingTimeout
	peer.Shell = ipfs.NewShellWithClient(peer.Url, peer.Client)
	peer.pinger = ipfs.NewShellWithClient(peer.Url, pingClient)
	err := peer.validateConnection()

	if err == nil {
		log.Info("IPFS API Connection OK")
	}

	return err
}

func (peer *IPFSPeer) Disconnect() error {
	// Nothing to do.
	return nil
}

func (peer *IPFSPeer) validateShell() error {
	if peer.Shell == nil {
		return peer.Connect()
	}

	return peer.validateConnection()
}

func (peer *IPFSPeer) validateConnection() error {
	if !peer.pinger.IsUp() {
		return fmt.Errorf("IPFSPeer is not up at '%v'", peer.Url)
	}

	return nil
}

func (peer *IPFSPeer) PublishAddr(addr crdt.IPFSPath, topics []crdt.IPFSPath) error {
	const failMsg = "IPFSPeer.PublishAddr failed"

	if verr := peer.validateShell(); verr != nil {
		return verr
	}

	publishValue := string(addr)

	for _, t := range topics {
		topicText := string(t)
		err := peer.Shell.PubSubPublish(topicText, publishValue)

		if err != nil {
			return errors.Wrap(err, failMsg)
		}
	}

	return nil
}

func (peer *IPFSPeer) SubscribeAddrStream(topic crdt.IPFSPath) (<-chan crdt.IPFSPath, <-chan error) {
	stream := make(chan crdt.IPFSPath)
	errch := make(chan error)

	tidy := func() {
		close(stream)
		close(errch)
	}

	if verr := peer.validateShell(); verr != nil {
		go func() {
			errch <- verr
			defer tidy()
		}()

		return stream, errch
	}

	go func() {
		defer tidy()

		topicText := string(topic)
		subscription, launchErr := peer.Shell.PubSubSubscribe(topicText)

		if launchErr != nil {
			errch <- launchErr
			return
		}

		for {
			record, recordErr := subscription.Next()

			if recordErr != nil {
				errch <- recordErr
				continue
			}

			pubsubPeer := record.From()
			bs := record.Data()
			addr := crdt.IPFSPath(string(bs))

			stream <- addr
			log.Info("Subscription update: '%v' from '%v'", addr, pubsubPeer)
		}

	}()

	return stream, errch
}

func (peer *IPFSPeer) AddIndex(index crdt.Index) (crdt.IPFSPath, error) {
	const failMsg = "IPFSPeer.AddIndex failed"

	log.Info("Adding index to IPFS...")

	if verr := peer.validateShell(); verr != nil {
		return crdt.NIL_PATH, verr
	}

	chunk := makeIpfsIndex(index)

	path, addErr := peer.add(chunk)

	if addErr != nil {
		return crdt.NIL_PATH, errors.Wrap(addErr, failMsg)
	}

	log.Info("Added index")

	return path, nil
}

func (peer *IPFSPeer) CatIndex(addr crdt.IPFSPath) (crdt.Index, error) {
	log.Info("Catting index from IPFS...")

	if verr := peer.validateShell(); verr != nil {
		return crdt.EmptyIndex(), verr
	}

	chunk := &IPFSIndex{}
	caterr := peer.cat(addr, chunk)

	if caterr != nil {
		return crdt.EmptyIndex(), errors.Wrap(caterr, "IPFSPeer.CatNamespace failed")
	}

	log.Info("Catted index")

	return chunk.Index, nil
}

func (peer *IPFSPeer) AddNamespace(namespace crdt.Namespace) (crdt.IPFSPath, error) {
	log.Info("Adding Namespace to IPFS...")

	if verr := peer.validateShell(); verr != nil {
		return crdt.NIL_PATH, verr
	}

	chunk := makeIpfsRecord(namespace)

	path, err := peer.add(chunk)

	if err != nil {
		return crdt.NIL_PATH, errors.Wrap(err, "IPFSPeer.AddNamespace failed")
	}

	log.Info("Added namespace")

	return path, nil
}

func (peer *IPFSPeer) CatNamespace(addr crdt.IPFSPath) (crdt.Namespace, error) {
	log.Info("Catting namespace from IPFS...")

	if verr := peer.validateShell(); verr != nil {
		return crdt.EmptyNamespace(), verr
	}

	chunk := &IPFSRecord{}
	caterr := peer.cat(addr, chunk)

	if caterr != nil {
		return crdt.EmptyNamespace(), errors.Wrap(caterr, "IPFSPeer.CatNamespace failed")
	}

	log.Info("Catted namespace")

	return chunk.Namespace, nil
}

func (peer *IPFSPeer) add(chunk encoder) (crdt.IPFSPath, error) {
	const failMsg = "IPFSPeer.add failed"
	buff := &bytes.Buffer{}
	err := chunk.encode(buff)

	if err != nil {
		return crdt.NIL_PATH, errors.Wrap(err, failMsg)
	}

	path, sherr := peer.Shell.Add(buff)

	if sherr != nil {
		return crdt.NIL_PATH, errors.Wrap(err, failMsg)
	}

	return crdt.IPFSPath(path), nil
}

func (peer *IPFSPeer) cat(path crdt.IPFSPath, out decoder) error {
	const failMsg = "IPFSPeer.cat failed"
	reader, err := peer.Shell.Cat(string(path))

	if err != nil {
		return errors.Wrap(err, failMsg)
	}

	defer reader.Close()

	err = out.decode(reader)

	if err != nil {
		return errors.Wrap(err, failMsg)
	}

	// According to IPFS binding docs we must drain the reader.
	remainder, drainerr := ioutil.ReadAll(reader)

	if drainerr != nil {
		log.Warn("error draining reader: %v", drainerr)
	}

	if len(remainder) != 0 {
		log.Warn("remaining bits after gob: %v", remainder)
	}

	return nil
}

const __DEFAULT_PING_TIMEOUT = time.Second * 5
<commit_msg>Fix wonky subscriptions<commit_after>package ipfs

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	gohttp "net/http"
	"time"

	ipfs "github.com/ipfs/go-ipfs-api"
	"github.com/johnny-morrice/godless/crdt"
	"github.com/johnny-morrice/godless/internal/http"
	"github.com/johnny-morrice/godless/log"
	"github.com/pkg/errors"
)

type IPFSRecord struct {
	Namespace crdt.Namespace
}

func makeIpfsRecord(namespace crdt.Namespace) *IPFSRecord {
	return &IPFSRecord{
		Namespace: namespace,
	}
}

func (record *IPFSRecord) encode(w io.Writer) error {
	return crdt.EncodeNamespace(record.Namespace, w)
}

func (record *IPFSRecord) decode(r io.Reader) error {
	ns, err := crdt.DecodeNamespace(r)

	if err != nil {
		return err
	}

	record.Namespace = ns
	return nil
}

type encoder interface {
	encode(io.Writer) error
}

type decoder interface {
	decode(io.Reader) error
}

type IPFSIndex struct {
	Index crdt.Index
}

func makeIpfsIndex(index crdt.Index) *IPFSIndex {
	return &IPFSIndex{
		Index: index,
	}
}

func (index *IPFSIndex) encode(w io.Writer) error {
	return crdt.EncodeIndex(index.Index, w)
}

func (index *IPFSIndex) decode(r io.Reader) error {
	dx, err := crdt.DecodeIndex(r)

	if err != nil {
		return err
	}

	index.Index = dx
	return nil
}

// TODO Don't use Shell directly - invent an interface.  This would enable mocking.
type IPFSPeer struct {
	Url         string
	Client      *gohttp.Client
	Shell       *ipfs.Shell
	PingTimeout time.Duration
	pinger      *ipfs.Shell
}

func (peer *IPFSPeer) Connect() error {
	if peer.PingTimeout == 0 {
		peer.PingTimeout = __DEFAULT_PING_TIMEOUT
	}

	if peer.Client == nil {
		peer.Client = http.DefaultBackendClient()
	}

	log.Info("Connecting to IPFS API...")
	pingClient := http.DefaultBackendClient()
	pingClient.Timeout = peer.PingTimeout
	peer.Shell = ipfs.NewShellWithClient(peer.Url, peer.Client)
	peer.pinger = ipfs.NewShellWithClient(peer.Url, pingClient)
	err := peer.validateConnection()

	if err == nil {
		log.Info("IPFS API Connection OK")
	}

	return err
}

func (peer *IPFSPeer) Disconnect() error {
	// Nothing to do.
	return nil
}

func (peer *IPFSPeer) validateShell() error {
	if peer.Shell == nil {
		return peer.Connect()
	}

	return peer.validateConnection()
}

func (peer *IPFSPeer) validateConnection() error {
	if !peer.pinger.IsUp() {
		return fmt.Errorf("IPFSPeer is not up at '%v'", peer.Url)
	}

	return nil
}

func (peer *IPFSPeer) PublishAddr(addr crdt.IPFSPath, topics []crdt.IPFSPath) error {
	const failMsg = "IPFSPeer.PublishAddr failed"

	if verr := peer.validateShell(); verr != nil {
		return verr
	}

	publishValue := string(addr)

	for _, t := range topics {
		topicText := string(t)
		err := peer.Shell.PubSubPublish(topicText, publishValue)

		if err != nil {
			return errors.Wrap(err, failMsg)
		}
	}

	return nil
}

func (peer *IPFSPeer) SubscribeAddrStream(topic crdt.IPFSPath) (<-chan crdt.IPFSPath, <-chan error) {
	stream := make(chan crdt.IPFSPath)
	errch := make(chan error)

	tidy := func() {
		close(stream)
		close(errch)
	}

	if verr := peer.validateShell(); verr != nil {
		go func() {
			errch <- verr
			defer tidy()
		}()

		return stream, errch
	}

	go func() {
		defer tidy()

		topicText := string(topic)

		var subscription *ipfs.PubSubSubscription

	RESTART:
		for subscription == nil {
			var launchErr error
			log.Info("(Re)starting subscription")
			subscription, launchErr = peer.Shell.PubSubSubscribe(topicText)

			if launchErr != nil {
				log.Error("Subcription launch failed, retrying: %v", launchErr)
			}

			for {
				log.Info("Fetching next subscription message...")
				record, recordErr := subscription.Next()

				if recordErr != nil {
					log.Error("Subscription read failed, continuing: %v", recordErr)
					continue RESTART
				}

				pubsubPeer := record.From()
				bs := record.Data()
				addr := crdt.IPFSPath(string(bs))

				stream <- addr
				log.Info("Subscription update: '%v' from '%v'", addr, pubsubPeer)
			}
		}

	}()

	return stream, errch
}

func (peer *IPFSPeer) AddIndex(index crdt.Index) (crdt.IPFSPath, error) {
	const failMsg = "IPFSPeer.AddIndex failed"

	log.Info("Adding index to IPFS...")

	if verr := peer.validateShell(); verr != nil {
		return crdt.NIL_PATH, verr
	}

	chunk := makeIpfsIndex(index)

	path, addErr := peer.add(chunk)

	if addErr != nil {
		return crdt.NIL_PATH, errors.Wrap(addErr, failMsg)
	}

	log.Info("Added index")

	return path, nil
}

func (peer *IPFSPeer) CatIndex(addr crdt.IPFSPath) (crdt.Index, error) {
	log.Info("Catting index from IPFS...")

	if verr := peer.validateShell(); verr != nil {
		return crdt.EmptyIndex(), verr
	}

	chunk := &IPFSIndex{}
	caterr := peer.cat(addr, chunk)

	if caterr != nil {
		return crdt.EmptyIndex(), errors.Wrap(caterr, "IPFSPeer.CatNamespace failed")
	}

	log.Info("Catted index")

	return chunk.Index, nil
}

func (peer *IPFSPeer) AddNamespace(namespace crdt.Namespace) (crdt.IPFSPath, error) {
	log.Info("Adding Namespace to IPFS...")

	if verr := peer.validateShell(); verr != nil {
		return crdt.NIL_PATH, verr
	}

	chunk := makeIpfsRecord(namespace)

	path, err := peer.add(chunk)

	if err != nil {
		return crdt.NIL_PATH, errors.Wrap(err, "IPFSPeer.AddNamespace failed")
	}

	log.Info("Added namespace")

	return path, nil
}

func (peer *IPFSPeer) CatNamespace(addr crdt.IPFSPath) (crdt.Namespace, error) {
	log.Info("Catting namespace from IPFS...")

	if verr := peer.validateShell(); verr != nil {
		return crdt.EmptyNamespace(), verr
	}

	chunk := &IPFSRecord{}
	caterr := peer.cat(addr, chunk)

	if caterr != nil {
		return crdt.EmptyNamespace(), errors.Wrap(caterr, "IPFSPeer.CatNamespace failed")
	}

	log.Info("Catted namespace")

	return chunk.Namespace, nil
}

func (peer *IPFSPeer) add(chunk encoder) (crdt.IPFSPath, error) {
	const failMsg = "IPFSPeer.add failed"
	buff := &bytes.Buffer{}
	err := chunk.encode(buff)

	if err != nil {
		return crdt.NIL_PATH, errors.Wrap(err, failMsg)
	}

	path, sherr := peer.Shell.Add(buff)

	if sherr != nil {
		return crdt.NIL_PATH, errors.Wrap(err, failMsg)
	}

	return crdt.IPFSPath(path), nil
}

func (peer *IPFSPeer) cat(path crdt.IPFSPath, out decoder) error {
	const failMsg = "IPFSPeer.cat failed"
	reader, err := peer.Shell.Cat(string(path))

	if err != nil {
		return errors.Wrap(err, failMsg)
	}

	defer reader.Close()

	err = out.decode(reader)

	if err != nil {
		return errors.Wrap(err, failMsg)
	}

	// According to IPFS binding docs we must drain the reader.
	remainder, drainerr := ioutil.ReadAll(reader)

	if drainerr != nil {
		log.Warn("error draining reader: %v", drainerr)
	}

	if len(remainder) != 0 {
		log.Warn("remaining bits after gob: %v", remainder)
	}

	return nil
}

const __DEFAULT_PING_TIMEOUT = time.Second * 5
<|endoftext|>


<commit_before>// 6 january 2016
package ui

// #include <CoreFoundation/CoreFoundation.h>
// #include <CoreServices/CoreServices.h>
// #include "ui.h"
// static inline CFDictionaryRef searchOptionsDictionary(void)
// {
// 	CFMutableDictionaryRef dict;
//
// 	dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
// 	if (dict == NULL) /* TODO */;
// 	/* TODO this doesn't actually work? */
// 	CFDictionaryAddValue(dict, kSKProximityIndexing, kCFBooleanTrue);
// 	return dict;
// }
// static inline CFStringRef toCFString(char *s)
// {
// 	CFStringRef cs;
//
// 	cs = CFStringCreateWithCString(NULL, s, kCFStringEncodingUTF8);
// 	if (cs == NULL) /* TODO */;
// 	free(s);
// 	return cs;
// }
// static inline SKDocumentID *mkResults(void)
// {
// 	SKDocumentID *d;
//
// 	d = (SKDocumentID *) malloc(1 * sizeof (SKDocumentID));
// 	if (d == NULL) /* TODO */;
// 	return d;
// }
// static inline void freeResults(SKDocumentID *d)
// {
// 	free(d);
// }
// static inline CFIndex *mkCount(void)
// {
// 	CFIndex *c;
//
// 	c = (CFIndex *) malloc(1 * sizeof (CFIndex));
// 	if (c == NULL) /* TODO */;
// 	return c;
// }
// static inline void freeCount(CFIndex *c)
// {
// 	free(c);
// }
// static inline void releaseSearch(SKSearchRef sr)
// {
// 	CFRelease(sr);
// }
// static inline char *fromCFString(CFStringRef cfstr)
// {
// 	char *s;
// 	CFIndex n;
//
// 	/* see http://opensource.apple.com/source/DirectoryService/DirectoryService-514.25/PlugIns/LDAPv3/CLDAPBindData.cpp?txt */
// 	n = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr), kCFStringEncodingUTF8) + 1;
// 	s = (char *) malloc(n * sizeof (char));
// 	if (s == NULL) /* TODO */;
// 	if (CFStringGetCString(cfstr, s, n, kCFStringEncodingUTF8) == false) /* TODO */;
// 	return s;
// }
// static inline void freestr(char *s)
// {
// 	free(s);
// }
import "C"

type SearchIndex struct {
	data		C.CFMutableDataRef
	si		C.SKIndexRef
}

func NewSearchIndex() *SearchIndex {
	s := new(SearchIndex)
	s.data = C.CFDataCreateMutable(nil, 0)
	if s.data == nil {
		panic("out of memory in NewSearchIndex()")
	}
	s.si = C.SKIndexCreateWithMutableData(s.data, nil, C.kSKIndexInverted, C.searchOptionsDictionary())
	// TODO release the search options dictionary?
	return s
}

// TODO destroy

func (s *SearchIndex) Add(key string, text string) {
	// scroll down for URL format image reference at https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/SearchKitConcepts/searchKit_concepts/searchKit_concepts.html#//apple_ref/doc/uid/TP40002844-BABDBJGC
	// TODO urlencode keys
	ckey := C.toCFString(C.CString("data://" + key))
	keyurl := C.CFURLCreateWithString(nil, ckey, nil)
	ctext := C.toCFString(C.CString(text))
	doc := C.SKDocumentCreateWithURL(keyurl)
	if doc == nil {
		panic("error creating document key for adding entry to search index")
	}
	res := C.SKIndexAddDocumentWithText(s.si, doc, ctext, C.false)
	if res == C.false {
		panic("error adding entry to search index")
	}
	// TODO release ckey, keyurl, doc, and ctext?
}

type SearchResults struct {
	sr		C.SKSearchRef
	s		*SearchIndex
	d		*C.SKDocumentID
	c		*C.CFIndex
	done		bool
}

// TODO break searchFor apart for substring matches
func (s *SearchIndex) Search(searchFor string) *SearchResults {
	// must flush before searching
	if C.SKIndexFlush(s.si) == C.false {
		panic("error flushing search index before searching")
	}

	r := new(SearchResults)
	r.s = s
	r.d = C.mkResults()
	r.c = C.mkCount()

	cfor := C.toCFString(C.CString(searchFor))
	// TODO kSKSearchOptionFindSimilar?
	r.sr = C.SKSearchCreate(s.si, cfor, C.kSKSearchOptionDefault)
	// TODO release cfor?
	return r
}

func (r *SearchResults) Dismiss() {
	if !r.done {
		C.SKSearchCancel(r.sr)
	}
	C.releaseSearch(r.sr)
	C.freeCount(r.c)
	C.freeResults(r.d)
}

func (r *SearchResults) Next() bool {
	if r.done {
		return false
	}
	for {
		res := C.SKSearchFindMatches(r.sr, 1, r.d, nil, 0, r.c)
		if res == C.false {				// no more
			r.done = true
			return false
		}
		if *(r.c) == 1 {					// got result
			return true
		}
		// otherwise try again
	}
	panic("unreachable")
}

func (r *SearchResults) Result() string {
	if r.done {
		panic("attempt to call SearchResults.Result() after finished")
	}
	doc := C.SKIndexCopyDocumentForDocumentID(r.s.si, *(r.d))
	// scroll down for URL format image reference at https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/SearchKitConcepts/searchKit_concepts/searchKit_concepts.html#//apple_ref/doc/uid/TP40002844-BABDBJGC and above as well
	cfstr := C.SKDocumentGetName(doc)
	if cfstr == nil {
		panic("error getting key out of search result")
	}
	cstr := C.fromCFString(cfstr)
	str := C.GoString(cstr)
	C.freestr(cstr)
	// TODO release doc and cfstr?
	return str
}
<commit_msg>Implemented substring match.<commit_after>// 6 january 2016
package ui

import (
	"strings"
	"unicode"
)

// #include <CoreFoundation/CoreFoundation.h>
// #include <CoreServices/CoreServices.h>
// #include "ui.h"
// static inline CFDictionaryRef searchOptionsDictionary(void)
// {
// 	CFMutableDictionaryRef dict;
//
// 	dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
// 	if (dict == NULL) /* TODO */;
// 	/* TODO this doesn't actually work? */
// 	CFDictionaryAddValue(dict, kSKProximityIndexing, kCFBooleanTrue);
// 	return dict;
// }
// static inline CFStringRef toCFString(char *s)
// {
// 	CFStringRef cs;
//
// 	cs = CFStringCreateWithCString(NULL, s, kCFStringEncodingUTF8);
// 	if (cs == NULL) /* TODO */;
// 	free(s);
// 	return cs;
// }
// static inline SKDocumentID *mkResults(void)
// {
// 	SKDocumentID *d;
//
// 	d = (SKDocumentID *) malloc(1 * sizeof (SKDocumentID));
// 	if (d == NULL) /* TODO */;
// 	return d;
// }
// static inline void freeResults(SKDocumentID *d)
// {
// 	free(d);
// }
// static inline CFIndex *mkCount(void)
// {
// 	CFIndex *c;
//
// 	c = (CFIndex *) malloc(1 * sizeof (CFIndex));
// 	if (c == NULL) /* TODO */;
// 	return c;
// }
// static inline void freeCount(CFIndex *c)
// {
// 	free(c);
// }
// static inline void releaseSearch(SKSearchRef sr)
// {
// 	CFRelease(sr);
// }
// static inline char *fromCFString(CFStringRef cfstr)
// {
// 	char *s;
// 	CFIndex n;
//
// 	/* see http://opensource.apple.com/source/DirectoryService/DirectoryService-514.25/PlugIns/LDAPv3/CLDAPBindData.cpp?txt */
// 	n = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr), kCFStringEncodingUTF8) + 1;
// 	s = (char *) malloc(n * sizeof (char));
// 	if (s == NULL) /* TODO */;
// 	if (CFStringGetCString(cfstr, s, n, kCFStringEncodingUTF8) == false) /* TODO */;
// 	return s;
// }
// static inline void freestr(char *s)
// {
// 	free(s);
// }
import "C"

type SearchIndex struct {
	data		C.CFMutableDataRef
	si		C.SKIndexRef
}

func NewSearchIndex() *SearchIndex {
	s := new(SearchIndex)
	s.data = C.CFDataCreateMutable(nil, 0)
	if s.data == nil {
		panic("out of memory in NewSearchIndex()")
	}
	s.si = C.SKIndexCreateWithMutableData(s.data, nil, C.kSKIndexInverted, C.searchOptionsDictionary())
	// TODO release the search options dictionary?
	return s
}

// TODO destroy

func (s *SearchIndex) Add(key string, text string) {
	// scroll down for URL format image reference at https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/SearchKitConcepts/searchKit_concepts/searchKit_concepts.html#//apple_ref/doc/uid/TP40002844-BABDBJGC
	// TODO urlencode keys
	ckey := C.toCFString(C.CString("data://" + key))
	keyurl := C.CFURLCreateWithString(nil, ckey, nil)
	ctext := C.toCFString(C.CString(text))
	doc := C.SKDocumentCreateWithURL(keyurl)
	if doc == nil {
		panic("error creating document key for adding entry to search index")
	}
	res := C.SKIndexAddDocumentWithText(s.si, doc, ctext, C.false)
	if res == C.false {
		panic("error adding entry to search index")
	}
	// TODO release ckey, keyurl, doc, and ctext?
}

type SearchResults struct {
	sr		C.SKSearchRef
	s		*SearchIndex
	d		*C.SKDocumentID
	c		*C.CFIndex
	done		bool
}

func (s *SearchIndex) Search(searchFor string) *SearchResults {
	// SearchKit doesn't do substring matches
	// the glossary of the programming guide says to fake it ourselves
	fields := strings.FieldsFunc(searchFor, func(c rune) bool {
		return !unicode.IsLetter(c) && !unicode.IsNumber(c)
	})
	for i, _ := range fields {
		fields[i] = "*" + fields[i] + "*"
	}
	searchFor = strings.Join(fields, " ")

	// must flush before searching
	if C.SKIndexFlush(s.si) == C.false {
		panic("error flushing search index before searching")
	}

	r := new(SearchResults)
	r.s = s
	r.d = C.mkResults()
	r.c = C.mkCount()

	cfor := C.toCFString(C.CString(searchFor))
	// TODO kSKSearchOptionFindSimilar?
	r.sr = C.SKSearchCreate(s.si, cfor, C.kSKSearchOptionDefault)
	// TODO release cfor?
	return r
}

func (r *SearchResults) Dismiss() {
	if !r.done {
		C.SKSearchCancel(r.sr)
	}
	C.releaseSearch(r.sr)
	C.freeCount(r.c)
	C.freeResults(r.d)
}

func (r *SearchResults) Next() bool {
	if r.done {
		return false
	}
	for {
		res := C.SKSearchFindMatches(r.sr, 1, r.d, nil, 0, r.c)
		if res == C.false {				// no more
			r.done = true
			return false
		}
		if *(r.c) == 1 {					// got result
			return true
		}
		// otherwise try again
	}
	panic("unreachable")
}

func (r *SearchResults) Result() string {
	if r.done {
		panic("attempt to call SearchResults.Result() after finished")
	}
	doc := C.SKIndexCopyDocumentForDocumentID(r.s.si, *(r.d))
	// scroll down for URL format image reference at https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/SearchKitConcepts/searchKit_concepts/searchKit_concepts.html#//apple_ref/doc/uid/TP40002844-BABDBJGC and above as well
	cfstr := C.SKDocumentGetName(doc)
	if cfstr == nil {
		panic("error getting key out of search result")
	}
	cstr := C.fromCFString(cfstr)
	str := C.GoString(cstr)
	C.freestr(cstr)
	// TODO release doc and cfstr?
	return str
}
<|endoftext|>


<commit_before>package prototest

import (
	"io"

	"github.com/ghthor/filu"
	"github.com/ghthor/filu/actor"
	"github.com/ghthor/filu/auth"
	"github.com/ghthor/filu/net"
	"github.com/ghthor/filu/net/client"

	"github.com/ghthor/gospec"
	. "github.com/ghthor/gospec"
)

type mockConn struct {
	pr [2]*io.PipeReader
	pw [2]*io.PipeWriter

	server, client net.Conn
}

type readWriter struct {
	io.Reader
	io.Writer
}

func newMockConn() mockConn {
	c := mockConn{}
	c.pr[0], c.pw[0] = io.Pipe()
	c.pr[1], c.pw[1] = io.Pipe()

	c.server = net.NewGobConn(readWriter{c.pr[0], c.pw[1]})
	c.client = net.NewGobConn(readWriter{c.pr[1], c.pw[0]})

	return c
}

func (c mockConn) close() {
	for _, r := range c.pr {
		r.Close()
	}

	for _, w := range c.pw {
		w.Close()
	}
}

type mockActorDB struct {
	Get    chan<- actor.GetActorsRequest
	Select chan<- actor.SelectionRequest
}

func (db mockActorDB) close() {
	close(db.Get)
	close(db.Select)
}

func newMockActorDB(actors map[string][]string) *mockActorDB {
	getCh := make(chan actor.GetActorsRequest)
	selCh := make(chan actor.SelectionRequest)
	db := &mockActorDB{
		Get:    getCh,
		Select: selCh,
	}

	getProc := actor.NewGetActorsRequestProcessor()

	actor.SelectionRequestSource(selCh).
		WriteToProcessor(actor.NewSelectionProcessor()).
		WriteTo(getProc).
		End()

	actor.GetActorsRequestSource(getCh).
		WriteToProcessor(getProc).
		End()

	for username, names := range actors {
		for _, name := range names {
			db.createActor(username, name)
		}
	}

	return db
}

func (db mockActorDB) createActor(username, actorname string) actor.CreatedActor {
	r := actor.NewSelectionRequest(filu.Actor{
		Username: username,
		Name:     actorname,
	})
	db.Select <- r
	return <-r.CreatedActor
}

func DescribeClientServerProtocol(c gospec.Context) {
	authDB := auth.NewStream(nil, nil, nil)

	createUser := func(conn mockConn, username, password string) (net.AuthenticatedUser, client.CreatedUser) {
		trip := client.NewUnauthenticatedConn(conn.client).AttemptLogin(username, password)
		user, err := net.AuthenticateFrom(conn.server, authDB)
		c.Assume(err, IsNil)

		err = nil
		var loginFailure net.UserLoginFailure
		var loginSuccess client.LoggedInUser
		var createdUser client.CreatedUser

		select {
		case err = <-trip.Error:
		case loginFailure = <-trip.LoginFailure:
		case loginSuccess = <-trip.LoginSuccess:
		case createdUser = <-trip.CreateSuccess:
		}

		c.Assume(err, IsNil)
		c.Assume(loginFailure, Equals, net.UserLoginFailure{})
		c.Assume(loginSuccess, Equals, client.LoggedInUser{})

		return user, createdUser
	}

	conn := newMockConn()
	defer conn.close()

	c.Specify("an unauthenticated connection", func() {
		c.Specify("can create a new user", func() {
			authUser, createdUser := createUser(conn, "newUser", "password")
			c.Expect(authUser.Username, Equals, createdUser.Name)

			c.Specify("unless the user already exists", func() {
				trip := client.NewUnauthenticatedConn(conn.client).AttemptLogin("newUser", "some other password")
				_, err := net.AuthenticateFrom(conn.server, authDB)
				c.Expect(err, Equals, net.ErrInvalidLoginCredentials)

				err = nil
				var loginFailure net.UserLoginFailure
				var loginSuccess client.LoggedInUser
				var createdUser client.CreatedUser

				select {
				case err = <-trip.Error:
				case loginFailure = <-trip.LoginFailure:
				case loginSuccess = <-trip.LoginSuccess:
				case createdUser = <-trip.CreateSuccess:
				}

				c.Assume(err, IsNil)
				c.Assume(loginSuccess, Equals, client.LoggedInUser{})
				c.Assume(createdUser, Equals, client.CreatedUser{})
				c.Expect(loginFailure.Name, Equals, "newUser")
			})
		})

		c.Specify("can log a user in", func() {
			createUser(conn, "username", "password")
			trip := client.NewUnauthenticatedConn(conn.client).AttemptLogin("username", "password")
			authedUser, err := net.AuthenticateFrom(conn.server, authDB)
			c.Assume(err, IsNil)

			err = nil
			var loginFailure net.UserLoginFailure
			var loginSuccess client.LoggedInUser
			var createdUser client.CreatedUser

			select {
			case err = <-trip.Error:
			case loginFailure = <-trip.LoginFailure:
			case loginSuccess = <-trip.LoginSuccess:
			case createdUser = <-trip.CreateSuccess:
			}

			c.Assume(err, IsNil)
			c.Assume(loginFailure, Equals, net.UserLoginFailure{})
			c.Assume(createdUser, Equals, client.CreatedUser{})
			c.Expect(loginSuccess.Name, Equals, authedUser.Username)

			c.Specify("unless the password is invalid", func() {
				trip := client.NewUnauthenticatedConn(conn.client).AttemptLogin("username", "invalid")
				_, err := net.AuthenticateFrom(conn.server, authDB)
				c.Expect(err, Equals, net.ErrInvalidLoginCredentials)

				err = nil
				var loginFailure net.UserLoginFailure
				var loginSuccess client.LoggedInUser
				var createdUser client.CreatedUser

				select {
				case err = <-trip.Error:
				case loginFailure = <-trip.LoginFailure:
				case loginSuccess = <-trip.LoginSuccess:
				case createdUser = <-trip.CreateSuccess:
				}

				c.Assume(err, IsNil)
				c.Assume(loginSuccess, Equals, client.LoggedInUser{})
				c.Assume(createdUser, Equals, client.CreatedUser{})
				c.Expect(loginFailure.Name, Equals, "username")
			})
		})
	})

	authenticatedUser, createdUser := createUser(conn, "jim", "jimisthebest11!")

	actorDB := newMockActorDB(map[string][]string{
		"jim": {
			"jim the slayer",
			"jim the destroyer",
			"jimmy shrimp steamer",
		},
	})
	defer actorDB.close()

	c.Specify("an authenticated connection", func() {
		c.Specify("receives a list of actors", func() {
			trip := createdUser.GetActors()
			c.Expect(net.SendActors(conn.server, actorDB.Get, authenticatedUser), IsNil)

			var err error = nil
			var selectConn client.SelectActorConn
			select {
			case err = <-trip.Error:
			case selectConn = <-trip.SelectActorConn:
			}

			c.Assume(err, IsNil)
			c.Expect(selectConn.Actors(), ContainsAll, []string{
				"jim the slayer",
				"jim the destroyer",
				"jimmy shrimp steamer",
			})
		})

		trip := createdUser.GetActors()
		c.Assume(net.SendActors(conn.server, actorDB.Get, authenticatedUser), IsNil)

		var err error = nil
		var selectConn client.SelectActorConn
		select {
		case err = <-trip.Error:
		case selectConn = <-trip.SelectActorConn:
		}
		c.Assume(err, IsNil)

		c.Specify("can create a new actor", func() {
			trip := selectConn.SelectActor("jay")

			actor, err := net.SelectActorFrom(conn.server, actorDB.Select, authenticatedUser)
			c.Assume(err, IsNil)

			var selectedActor client.SelectedActorConn
			var createdActor client.SelectedActorConn
			select {
			case err = <-trip.Error:
			case selectedActor = <-trip.SelectedActor:
			case createdActor = <-trip.CreatedActor:
			}
			c.Assume(err, IsNil)
			c.Assume(selectedActor, IsNil)
			c.Assume(createdActor, Not(IsNil))

			expectedActor := filu.Actor{
				Username: "jim",
				Name:     "jay",
			}
			c.Expect(actor, Equals, expectedActor)
			c.Expect(createdActor.Actor(), Equals, expectedActor)
		})

		c.Specify("can select an actor", func() {
			trip := selectConn.SelectActor("jim the slayer")

			actor, err := net.SelectActorFrom(conn.server, actorDB.Select, authenticatedUser)
			c.Assume(err, IsNil)

			var selectedActor client.SelectedActorConn
			var createdActor client.SelectedActorConn
			select {
			case err = <-trip.Error:
			case selectedActor = <-trip.SelectedActor:
			case createdActor = <-trip.CreatedActor:
			}
			c.Assume(err, IsNil)
			c.Assume(selectedActor, Not(IsNil))
			c.Assume(createdActor, IsNil)

			expectedActor := filu.Actor{
				Username: "jim",
				Name:     "jim the slayer",
			}
			c.Expect(actor, Equals, expectedActor)
			c.Expect(selectedActor.Actor(), Equals, expectedActor)
		})
	})
}
<commit_msg>[filu/net/prototest] factor out boilerplate testing client logins<commit_after>package prototest

import (
	"io"

	"github.com/ghthor/filu"
	"github.com/ghthor/filu/actor"
	"github.com/ghthor/filu/auth"
	"github.com/ghthor/filu/net"
	"github.com/ghthor/filu/net/client"

	"github.com/ghthor/gospec"
	. "github.com/ghthor/gospec"
)

type mockConn struct {
	pr [2]*io.PipeReader
	pw [2]*io.PipeWriter

	server, client net.Conn
}

type readWriter struct {
	io.Reader
	io.Writer
}

func newMockConn() mockConn {
	c := mockConn{}
	c.pr[0], c.pw[0] = io.Pipe()
	c.pr[1], c.pw[1] = io.Pipe()

	c.server = net.NewGobConn(readWriter{c.pr[0], c.pw[1]})
	c.client = net.NewGobConn(readWriter{c.pr[1], c.pw[0]})

	return c
}

func (c mockConn) close() {
	for _, r := range c.pr {
		r.Close()
	}

	for _, w := range c.pw {
		w.Close()
	}
}

type mockActorDB struct {
	Get    chan<- actor.GetActorsRequest
	Select chan<- actor.SelectionRequest
}

func (db mockActorDB) close() {
	close(db.Get)
	close(db.Select)
}

func newMockActorDB(actors map[string][]string) *mockActorDB {
	getCh := make(chan actor.GetActorsRequest)
	selCh := make(chan actor.SelectionRequest)
	db := &mockActorDB{
		Get:    getCh,
		Select: selCh,
	}

	getProc := actor.NewGetActorsRequestProcessor()

	actor.SelectionRequestSource(selCh).
		WriteToProcessor(actor.NewSelectionProcessor()).
		WriteTo(getProc).
		End()

	actor.GetActorsRequestSource(getCh).
		WriteToProcessor(getProc).
		End()

	for username, names := range actors {
		for _, name := range names {
			db.createActor(username, name)
		}
	}

	return db
}

func (db mockActorDB) createActor(username, actorname string) actor.CreatedActor {
	r := actor.NewSelectionRequest(filu.Actor{
		Username: username,
		Name:     actorname,
	})
	db.Select <- r
	return <-r.CreatedActor
}

type loginTripResult struct {
	err          error
	failure      net.UserLoginFailure
	loggedInUser client.LoggedInUser
	createdUser  client.CreatedUser
}

func NewLoginResult(trip client.LoginRoundTrip) loginTripResult {
	var result loginTripResult

	select {
	case result.err = <-trip.Error:
	case result.failure = <-trip.LoginFailure:
	case result.loggedInUser = <-trip.LoginSuccess:
	case result.createdUser = <-trip.CreateSuccess:
	}

	return result
}

func DescribeClientServerProtocol(c gospec.Context) {
	authDB := auth.NewStream(nil, nil, nil)

	createUser := func(conn mockConn, username, password string) (net.AuthenticatedUser, client.CreatedUser) {
		trip := client.NewUnauthenticatedConn(conn.client).AttemptLogin(username, password)
		user, err := net.AuthenticateFrom(conn.server, authDB)
		c.Assume(err, IsNil)

		result := NewLoginResult(trip)

		c.Assume(result.err, IsNil)
		c.Assume(result.failure, Equals, net.UserLoginFailure{})
		c.Assume(result.loggedInUser, Equals, client.LoggedInUser{})

		return user, result.createdUser
	}

	conn := newMockConn()
	defer conn.close()

	c.Specify("an unauthenticated connection", func() {
		c.Specify("can create a new user", func() {
			authUser, createdUser := createUser(conn, "newUser", "password")
			c.Expect(authUser.Username, Equals, createdUser.Name)

			c.Specify("unless the user already exists", func() {
				trip := client.NewUnauthenticatedConn(conn.client).AttemptLogin("newUser", "some other password")
				_, err := net.AuthenticateFrom(conn.server, authDB)
				c.Expect(err, Equals, net.ErrInvalidLoginCredentials)

				result := NewLoginResult(trip)
				c.Assume(result.err, IsNil)
				c.Assume(result.loggedInUser, Equals, client.LoggedInUser{})
				c.Assume(result.createdUser, Equals, client.CreatedUser{})
				c.Expect(result.failure.Name, Equals, "newUser")
			})
		})

		c.Specify("can log a user in", func() {
			createUser(conn, "username", "password")
			trip := client.NewUnauthenticatedConn(conn.client).AttemptLogin("username", "password")
			authedUser, err := net.AuthenticateFrom(conn.server, authDB)
			c.Assume(err, IsNil)

			result := NewLoginResult(trip)
			c.Assume(result.err, IsNil)
			c.Assume(result.failure, Equals, net.UserLoginFailure{})
			c.Assume(result.createdUser, Equals, client.CreatedUser{})
			c.Expect(result.loggedInUser.Name, Equals, authedUser.Username)

			c.Specify("unless the password is invalid", func() {
				trip := client.NewUnauthenticatedConn(conn.client).AttemptLogin("username", "invalid")
				_, err := net.AuthenticateFrom(conn.server, authDB)
				c.Expect(err, Equals, net.ErrInvalidLoginCredentials)

				result := NewLoginResult(trip)
				c.Assume(result.err, IsNil)
				c.Assume(result.loggedInUser, Equals, client.LoggedInUser{})
				c.Assume(result.createdUser, Equals, client.CreatedUser{})
				c.Expect(result.failure.Name, Equals, "username")
			})
		})
	})

	authenticatedUser, createdUser := createUser(conn, "jim", "jimisthebest11!")

	actorDB := newMockActorDB(map[string][]string{
		"jim": {
			"jim the slayer",
			"jim the destroyer",
			"jimmy shrimp steamer",
		},
	})
	defer actorDB.close()

	c.Specify("an authenticated connection", func() {
		c.Specify("receives a list of actors", func() {
			trip := createdUser.GetActors()
			c.Expect(net.SendActors(conn.server, actorDB.Get, authenticatedUser), IsNil)

			var err error = nil
			var selectConn client.SelectActorConn
			select {
			case err = <-trip.Error:
			case selectConn = <-trip.SelectActorConn:
			}

			c.Assume(err, IsNil)
			c.Expect(selectConn.Actors(), ContainsAll, []string{
				"jim the slayer",
				"jim the destroyer",
				"jimmy shrimp steamer",
			})
		})

		trip := createdUser.GetActors()
		c.Assume(net.SendActors(conn.server, actorDB.Get, authenticatedUser), IsNil)

		var err error = nil
		var selectConn client.SelectActorConn
		select {
		case err = <-trip.Error:
		case selectConn = <-trip.SelectActorConn:
		}
		c.Assume(err, IsNil)

		c.Specify("can create a new actor", func() {
			trip := selectConn.SelectActor("jay")

			actor, err := net.SelectActorFrom(conn.server, actorDB.Select, authenticatedUser)
			c.Assume(err, IsNil)

			var selectedActor client.SelectedActorConn
			var createdActor client.SelectedActorConn
			select {
			case err = <-trip.Error:
			case selectedActor = <-trip.SelectedActor:
			case createdActor = <-trip.CreatedActor:
			}
			c.Assume(err, IsNil)
			c.Assume(selectedActor, IsNil)
			c.Assume(createdActor, Not(IsNil))

			expectedActor := filu.Actor{
				Username: "jim",
				Name:     "jay",
			}
			c.Expect(actor, Equals, expectedActor)
			c.Expect(createdActor.Actor(), Equals, expectedActor)
		})

		c.Specify("can select an actor", func() {
			trip := selectConn.SelectActor("jim the slayer")

			actor, err := net.SelectActorFrom(conn.server, actorDB.Select, authenticatedUser)
			c.Assume(err, IsNil)

			var selectedActor client.SelectedActorConn
			var createdActor client.SelectedActorConn
			select {
			case err = <-trip.Error:
			case selectedActor = <-trip.SelectedActor:
			case createdActor = <-trip.CreatedActor:
			}
			c.Assume(err, IsNil)
			c.Assume(selectedActor, Not(IsNil))
			c.Assume(createdActor, IsNil)

			expectedActor := filu.Actor{
				Username: "jim",
				Name:     "jim the slayer",
			}
			c.Expect(actor, Equals, expectedActor)
			c.Expect(selectedActor.Actor(), Equals, expectedActor)
		})
	})
}
<|endoftext|>


<commit_before>// this file was generated by gomacro command: import "log/syslog"
// DO NOT EDIT! Any change will be lost when the file is re-generated

package imports

import (
	. "reflect"
	"log/syslog"
)

func init() {
	Packages["log/syslog"] = Package{
	Binds: map[string]Value{
		"Dial":	ValueOf(syslog.Dial),
		"LOG_ALERT":	ValueOf(syslog.LOG_ALERT),
		"LOG_AUTH":	ValueOf(syslog.LOG_AUTH),
		"LOG_AUTHPRIV":	ValueOf(syslog.LOG_AUTHPRIV),
		"LOG_CRIT":	ValueOf(syslog.LOG_CRIT),
		"LOG_CRON":	ValueOf(syslog.LOG_CRON),
		"LOG_DAEMON":	ValueOf(syslog.LOG_DAEMON),
		"LOG_DEBUG":	ValueOf(syslog.LOG_DEBUG),
		"LOG_EMERG":	ValueOf(syslog.LOG_EMERG),
		"LOG_ERR":	ValueOf(syslog.LOG_ERR),
		"LOG_FTP":	ValueOf(syslog.LOG_FTP),
		"LOG_INFO":	ValueOf(syslog.LOG_INFO),
		"LOG_KERN":	ValueOf(syslog.LOG_KERN),
		"LOG_LOCAL0":	ValueOf(syslog.LOG_LOCAL0),
		"LOG_LOCAL1":	ValueOf(syslog.LOG_LOCAL1),
		"LOG_LOCAL2":	ValueOf(syslog.LOG_LOCAL2),
		"LOG_LOCAL3":	ValueOf(syslog.LOG_LOCAL3),
		"LOG_LOCAL4":	ValueOf(syslog.LOG_LOCAL4),
		"LOG_LOCAL5":	ValueOf(syslog.LOG_LOCAL5),
		"LOG_LOCAL6":	ValueOf(syslog.LOG_LOCAL6),
		"LOG_LOCAL7":	ValueOf(syslog.LOG_LOCAL7),
		"LOG_LPR":	ValueOf(syslog.LOG_LPR),
		"LOG_MAIL":	ValueOf(syslog.LOG_MAIL),
		"LOG_NEWS":	ValueOf(syslog.LOG_NEWS),
		"LOG_NOTICE":	ValueOf(syslog.LOG_NOTICE),
		"LOG_SYSLOG":	ValueOf(syslog.LOG_SYSLOG),
		"LOG_USER":	ValueOf(syslog.LOG_USER),
		"LOG_UUCP":	ValueOf(syslog.LOG_UUCP),
		"LOG_WARNING":	ValueOf(syslog.LOG_WARNING),
		"New":	ValueOf(syslog.New),
		"NewLogger":	ValueOf(syslog.NewLogger),
	},
	Types: map[string]Type{
		"Priority":	TypeOf((*syslog.Priority)(nil)).Elem(),
		"Writer":	TypeOf((*syslog.Writer)(nil)).Elem(),
	},
	Proxies: map[string]Type{
	} }
}
<commit_msg>disabled imports/log_syslog.go on plan9 and windows<commit_after>// this file was generated by gomacro command: import "log/syslog"
// DO NOT EDIT! Any change will be lost when the file is re-generated

// +build !plan9
// +build !windows

package imports

import (
	. "reflect"
	"log/syslog"
)

func init() {
	Packages["log/syslog"] = Package{
	Binds: map[string]Value{
		"Dial":	ValueOf(syslog.Dial),
		"LOG_ALERT":	ValueOf(syslog.LOG_ALERT),
		"LOG_AUTH":	ValueOf(syslog.LOG_AUTH),
		"LOG_AUTHPRIV":	ValueOf(syslog.LOG_AUTHPRIV),
		"LOG_CRIT":	ValueOf(syslog.LOG_CRIT),
		"LOG_CRON":	ValueOf(syslog.LOG_CRON),
		"LOG_DAEMON":	ValueOf(syslog.LOG_DAEMON),
		"LOG_DEBUG":	ValueOf(syslog.LOG_DEBUG),
		"LOG_EMERG":	ValueOf(syslog.LOG_EMERG),
		"LOG_ERR":	ValueOf(syslog.LOG_ERR),
		"LOG_FTP":	ValueOf(syslog.LOG_FTP),
		"LOG_INFO":	ValueOf(syslog.LOG_INFO),
		"LOG_KERN":	ValueOf(syslog.LOG_KERN),
		"LOG_LOCAL0":	ValueOf(syslog.LOG_LOCAL0),
		"LOG_LOCAL1":	ValueOf(syslog.LOG_LOCAL1),
		"LOG_LOCAL2":	ValueOf(syslog.LOG_LOCAL2),
		"LOG_LOCAL3":	ValueOf(syslog.LOG_LOCAL3),
		"LOG_LOCAL4":	ValueOf(syslog.LOG_LOCAL4),
		"LOG_LOCAL5":	ValueOf(syslog.LOG_LOCAL5),
		"LOG_LOCAL6":	ValueOf(syslog.LOG_LOCAL6),
		"LOG_LOCAL7":	ValueOf(syslog.LOG_LOCAL7),
		"LOG_LPR":	ValueOf(syslog.LOG_LPR),
		"LOG_MAIL":	ValueOf(syslog.LOG_MAIL),
		"LOG_NEWS":	ValueOf(syslog.LOG_NEWS),
		"LOG_NOTICE":	ValueOf(syslog.LOG_NOTICE),
		"LOG_SYSLOG":	ValueOf(syslog.LOG_SYSLOG),
		"LOG_USER":	ValueOf(syslog.LOG_USER),
		"LOG_UUCP":	ValueOf(syslog.LOG_UUCP),
		"LOG_WARNING":	ValueOf(syslog.LOG_WARNING),
		"New":	ValueOf(syslog.New),
		"NewLogger":	ValueOf(syslog.NewLogger),
	},
	Types: map[string]Type{
		"Priority":	TypeOf((*syslog.Priority)(nil)).Elem(),
		"Writer":	TypeOf((*syslog.Writer)(nil)).Elem(),
	},
	Proxies: map[string]Type{
	} }
}
<|endoftext|>


<commit_before>package netflow9

import (
	"github.com/tehmaze/go-netflow/common/session"
	"github.com/tehmaze/go-netflow/common/translate"
)

type TranslatedField struct {
	Name  string
	Type  uint16
	Value interface{}
	Bytes []byte
}

type Translate struct {
	*translate.Translate
}

func NewTranslate(s session.Session) *Translate {
	return &Translate{translate.NewTranslate(s)}
}

func (t *Translate) Record(dr *DataRecord) error {
	if t.Session == nil {
		if debug {
			debugLog.Println("no session, can't translate field")
		}
		return nil
	}
	var (
		tm session.Template
		tr TemplateRecord
		ok bool
	)
	if tm, ok = t.Session.GetTemplate(dr.TemplateID); !ok {
		if debug {
			debugLog.Printf("no template for id=%d, can't translate field\n", dr.TemplateID)
		}
		return nil
	}
	if tr, ok = tm.(TemplateRecord); !ok {
		return nil
	}
	if tr.Fields == nil {
		if debug {
			debugLog.Printf("no fields in template id=%d, can't translate\n", dr.TemplateID)
		}
		return nil
	}

	if debug {
		debugLog.Printf("translating %d/%d fields\n", len(dr.Fields), len(tr.Fields))
	}

	for i, field := range tr.Fields {
		if i > len(dr.Fields) {
			break
		}
		f := &dr.Fields[i]
		f.Translated = &TranslatedField{}
		f.Translated.Type = field.Type

		if element, ok := t.Translate.Key(translate.Key{0, field.Type}); ok {
			f.Translated.Name = element.Name
			f.Translated.Value = translate.Bytes(dr.Fields[i].Bytes, element.Type)
		} else if debug {
			debugLog.Printf("no translator element for {0, %d}\n", field.Type)
		}
	}

	return nil
}
<commit_msg>Fixed slice overrun<commit_after>package netflow9

import (
	"github.com/tehmaze/go-netflow/common/session"
	"github.com/tehmaze/go-netflow/common/translate"
)

type TranslatedField struct {
	Name  string
	Type  uint16
	Value interface{}
	Bytes []byte
}

type Translate struct {
	*translate.Translate
}

func NewTranslate(s session.Session) *Translate {
	return &Translate{translate.NewTranslate(s)}
}

func (t *Translate) Record(dr *DataRecord) error {
	if t.Session == nil {
		if debug {
			debugLog.Println("no session, can't translate field")
		}
		return nil
	}
	var (
		tm session.Template
		tr TemplateRecord
		ok bool
	)
	if tm, ok = t.Session.GetTemplate(dr.TemplateID); !ok {
		if debug {
			debugLog.Printf("no template for id=%d, can't translate field\n", dr.TemplateID)
		}
		return nil
	}
	if tr, ok = tm.(TemplateRecord); !ok {
		return nil
	}
	if tr.Fields == nil {
		if debug {
			debugLog.Printf("no fields in template id=%d, can't translate\n", dr.TemplateID)
		}
		return nil
	}

	if debug {
		debugLog.Printf("translating %d/%d fields\n", len(dr.Fields), len(tr.Fields))
	}

	for i, field := range tr.Fields {
		if i >= len(dr.Fields) {
			break
		}
		f := &dr.Fields[i]
		f.Translated = &TranslatedField{}
		f.Translated.Type = field.Type

		if element, ok := t.Translate.Key(translate.Key{0, field.Type}); ok {
			f.Translated.Name = element.Name
			f.Translated.Value = translate.Bytes(dr.Fields[i].Bytes, element.Type)
		} else if debug {
			debugLog.Printf("no translator element for {0, %d}\n", field.Type)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"net/url"
	"strings"
	"time"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/sns"
)

const awsSNSPendingConfirmationMessage = "pending confirmation"
const awsSNSPendingConfirmationMessageWithoutSpaces = "pendingconfirmation"
const awsSNSPasswordObfuscationPattern = "****"

var SNSSubscriptionAttributeMap = map[string]string{
	"topic_arn":            "TopicArn",
	"endpoint":             "Endpoint",
	"protocol":             "Protocol",
	"raw_message_delivery": "RawMessageDelivery",
}

func resourceAwsSnsTopicSubscription() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSnsTopicSubscriptionCreate,
		Read:   resourceAwsSnsTopicSubscriptionRead,
		Update: resourceAwsSnsTopicSubscriptionUpdate,
		Delete: resourceAwsSnsTopicSubscriptionDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"protocol": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     false,
				ValidateFunc: validateSNSSubscriptionProtocol,
			},
			"endpoint": {
				Type:     schema.TypeString,
				Required: true,
			},
			"endpoint_auto_confirms": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},
			"confirmation_timeout_in_minutes": {
				Type:     schema.TypeInt,
				Optional: true,
				Default:  1,
			},
			"topic_arn": {
				Type:     schema.TypeString,
				Required: true,
			},
			"delivery_policy": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"raw_message_delivery": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceAwsSnsTopicSubscriptionCreate(d *schema.ResourceData, meta interface{}) error {
	snsconn := meta.(*AWSClient).snsconn

	output, err := subscribeToSNSTopic(d, snsconn)

	if err != nil {
		return err
	}

	if subscriptionHasPendingConfirmation(output.SubscriptionArn) {
		log.Printf("[WARN] Invalid SNS Subscription, received a \"%s\" ARN", awsSNSPendingConfirmationMessage)
		return nil
	}

	log.Printf("New subscription ARN: %s", *output.SubscriptionArn)
	d.SetId(*output.SubscriptionArn)

	// Write the ARN to the 'arn' field for export
	d.Set("arn", *output.SubscriptionArn)

	return resourceAwsSnsTopicSubscriptionUpdate(d, meta)
}

func resourceAwsSnsTopicSubscriptionUpdate(d *schema.ResourceData, meta interface{}) error {
	snsconn := meta.(*AWSClient).snsconn

	// If any changes happened, un-subscribe and re-subscribe
	if d.HasChange("protocol") || d.HasChange("endpoint") || d.HasChange("topic_arn") {
		log.Printf("[DEBUG] Updating subscription %s", d.Id())
		// Unsubscribe
		_, err := snsconn.Unsubscribe(&sns.UnsubscribeInput{
			SubscriptionArn: aws.String(d.Id()),
		})

		if err != nil {
			return fmt.Errorf("Error unsubscribing from SNS topic: %s", err)
		}

		// Re-subscribe and set id
		output, err := subscribeToSNSTopic(d, snsconn)
		d.SetId(*output.SubscriptionArn)
		d.Set("arn", *output.SubscriptionArn)
	}

	if d.HasChange("raw_message_delivery") {
		_, n := d.GetChange("raw_message_delivery")

		attrValue := "false"

		if n.(bool) {
			attrValue = "true"
		}

		req := &sns.SetSubscriptionAttributesInput{
			SubscriptionArn: aws.String(d.Id()),
			AttributeName:   aws.String("RawMessageDelivery"),
			AttributeValue:  aws.String(attrValue),
		}
		_, err := snsconn.SetSubscriptionAttributes(req)

		if err != nil {
			return fmt.Errorf("Unable to set raw message delivery attribute on subscription")
		}
	}

	return resourceAwsSnsTopicSubscriptionRead(d, meta)
}

func resourceAwsSnsTopicSubscriptionRead(d *schema.ResourceData, meta interface{}) error {
	snsconn := meta.(*AWSClient).snsconn

	log.Printf("[DEBUG] Loading subscription %s", d.Id())

	attributeOutput, err := snsconn.GetSubscriptionAttributes(&sns.GetSubscriptionAttributesInput{
		SubscriptionArn: aws.String(d.Id()),
	})
	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NotFound" {
			log.Printf("[WARN] SNS Topic Subscription (%s) not found, error code (404)", d.Id())
			d.SetId("")
			return nil
		}

		return err
	}

	if attributeOutput.Attributes != nil && len(attributeOutput.Attributes) > 0 {
		attrHash := attributeOutput.Attributes
		resource := *resourceAwsSnsTopicSubscription()

		for iKey, oKey := range SNSSubscriptionAttributeMap {
			log.Printf("[DEBUG] Reading %s => %s", iKey, oKey)

			if attrHash[oKey] != nil {
				if resource.Schema[iKey] != nil {
					var value string
					value = *attrHash[oKey]
					log.Printf("[DEBUG] Reading %s => %s -> %s", iKey, oKey, value)
					d.Set(iKey, value)
				}
			}
		}
	}

	return nil
}

func resourceAwsSnsTopicSubscriptionDelete(d *schema.ResourceData, meta interface{}) error {
	snsconn := meta.(*AWSClient).snsconn

	log.Printf("[DEBUG] SNS delete topic subscription: %s", d.Id())
	_, err := snsconn.Unsubscribe(&sns.UnsubscribeInput{
		SubscriptionArn: aws.String(d.Id()),
	})
	if err != nil {
		return err
	}
	return nil
}

func subscribeToSNSTopic(d *schema.ResourceData, snsconn *sns.SNS) (output *sns.SubscribeOutput, err error) {
	protocol := d.Get("protocol").(string)
	endpoint := d.Get("endpoint").(string)
	topic_arn := d.Get("topic_arn").(string)
	endpoint_auto_confirms := d.Get("endpoint_auto_confirms").(bool)
	confirmation_timeout_in_minutes := d.Get("confirmation_timeout_in_minutes").(int)

	if strings.Contains(protocol, "http") && !endpoint_auto_confirms {
		return nil, fmt.Errorf("Protocol http/https is only supported for endpoints which auto confirms!")
	}

	log.Printf("[DEBUG] SNS create topic subscription: %s (%s) @ '%s'", endpoint, protocol, topic_arn)

	req := &sns.SubscribeInput{
		Protocol: aws.String(protocol),
		Endpoint: aws.String(endpoint),
		TopicArn: aws.String(topic_arn),
	}

	output, err = snsconn.Subscribe(req)
	if err != nil {
		return nil, fmt.Errorf("Error creating SNS topic: %s", err)
	}

	log.Printf("[DEBUG] Finished subscribing to topic %s with subscription arn %s", topic_arn, *output.SubscriptionArn)

	if strings.Contains(protocol, "http") && subscriptionHasPendingConfirmation(output.SubscriptionArn) {

		log.Printf("[DEBUG] SNS create topic subscription is pending so fetching the subscription list for topic : %s (%s) @ '%s'", endpoint, protocol, topic_arn)

		err = resource.Retry(time.Duration(confirmation_timeout_in_minutes)*time.Minute, func() *resource.RetryError {

			subscription, err := findSubscriptionByNonID(d, snsconn)

			if subscription != nil {
				output.SubscriptionArn = subscription.SubscriptionArn
				return nil
			}

			if err != nil {
				return resource.RetryableError(
					fmt.Errorf("Error fetching subscriptions for SNS topic %s: %s", topic_arn, err))
			}

			return resource.RetryableError(
				fmt.Errorf("Endpoint (%s) did not autoconfirm the subscription for topic %s", endpoint, topic_arn))
		})

		if err != nil {
			return nil, err
		}
	}

	log.Printf("[DEBUG] Created new subscription! %s", *output.SubscriptionArn)
	return output, nil
}

// finds a subscription using protocol, endpoint and topic_arn (which is a key in sns subscription)
func findSubscriptionByNonID(d *schema.ResourceData, snsconn *sns.SNS) (*sns.Subscription, error) {
	protocol := d.Get("protocol").(string)
	endpoint := d.Get("endpoint").(string)
	topic_arn := d.Get("topic_arn").(string)
	obfuscatedEndpoint := obfuscateEndpoint(endpoint)

	req := &sns.ListSubscriptionsByTopicInput{
		TopicArn: aws.String(topic_arn),
	}

	for {
		res, err := snsconn.ListSubscriptionsByTopic(req)

		if err != nil {
			return nil, fmt.Errorf("Error fetching subscriptions for topic %s : %s", topic_arn, err)
		}

		for _, subscription := range res.Subscriptions {
			log.Printf("[DEBUG] check subscription with Subscription EndPoint %s (local: %s), Protocol %s, topicARN %s and SubscriptionARN %s", *subscription.Endpoint, obfuscatedEndpoint, *subscription.Protocol, *subscription.TopicArn, *subscription.SubscriptionArn)
			if *subscription.Endpoint == obfuscatedEndpoint && *subscription.Protocol == protocol && *subscription.TopicArn == topic_arn && !subscriptionHasPendingConfirmation(subscription.SubscriptionArn) {
				return subscription, nil
			}
		}

		// if there are more than 100 subscriptions then go to the next 100 otherwise return an error
		if res.NextToken != nil {
			req.NextToken = res.NextToken
		} else {
			return nil, fmt.Errorf("Error finding subscription for topic %s with endpoint %s and protocol %s", topic_arn, endpoint, protocol)
		}
	}
}

// returns true if arn is nil or has both pending and confirmation words in the arn
func subscriptionHasPendingConfirmation(arn *string) bool {
	if arn != nil && !strings.Contains(strings.Replace(strings.ToLower(*arn), " ", "", -1), awsSNSPendingConfirmationMessageWithoutSpaces) {
		return false
	}

	return true
}

// returns the endpoint with obfuscated password, if any
func obfuscateEndpoint(endpoint string) string {
	res, err := url.Parse(endpoint)
	if err != nil {
		fmt.Println(err)
	}

	var obfuscatedEndpoint = res.String()

	// If the user is defined, we try to get the username and password, if defined.
	// Then, we update the user with the obfuscated version.
	if res.User != nil {
		if password, ok := res.User.Password(); ok {
			obfuscatedEndpoint = strings.Replace(obfuscatedEndpoint, password, awsSNSPasswordObfuscationPattern, 1)
		}
	}
	return obfuscatedEndpoint
}
<commit_msg>Fix SNS subscription sub/unsub/sub bug.<commit_after>package aws

import (
	"fmt"
	"log"
	"net/url"
	"strings"
	"time"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/sns"
)

const awsSNSPendingConfirmationMessage = "pending confirmation"
const awsSNSPendingConfirmationMessageWithoutSpaces = "pendingconfirmation"
const awsSNSPasswordObfuscationPattern = "****"

var SNSSubscriptionAttributeMap = map[string]string{
	"topic_arn":            "TopicArn",
	"endpoint":             "Endpoint",
	"protocol":             "Protocol",
	"raw_message_delivery": "RawMessageDelivery",
}

func resourceAwsSnsTopicSubscription() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSnsTopicSubscriptionCreate,
		Read:   resourceAwsSnsTopicSubscriptionRead,
		Update: resourceAwsSnsTopicSubscriptionUpdate,
		Delete: resourceAwsSnsTopicSubscriptionDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"protocol": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     false,
				ValidateFunc: validateSNSSubscriptionProtocol,
			},
			"endpoint": {
				Type:     schema.TypeString,
				Required: true,
			},
			"endpoint_auto_confirms": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},
			"confirmation_timeout_in_minutes": {
				Type:     schema.TypeInt,
				Optional: true,
				Default:  1,
			},
			"topic_arn": {
				Type:     schema.TypeString,
				Required: true,
			},
			"delivery_policy": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"raw_message_delivery": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceAwsSnsTopicSubscriptionCreate(d *schema.ResourceData, meta interface{}) error {
	snsconn := meta.(*AWSClient).snsconn

	output, err := subscribeToSNSTopic(d, snsconn)

	if err != nil {
		return err
	}

	if subscriptionHasPendingConfirmation(output.SubscriptionArn) {
		log.Printf("[WARN] Invalid SNS Subscription, received a \"%s\" ARN", awsSNSPendingConfirmationMessage)
		return nil
	}

	log.Printf("New subscription ARN: %s", *output.SubscriptionArn)
	d.SetId(*output.SubscriptionArn)

	// Write the ARN to the 'arn' field for export
	d.Set("arn", *output.SubscriptionArn)

	return resourceAwsSnsTopicSubscriptionUpdate(d, meta)
}

func resourceAwsSnsTopicSubscriptionUpdate(d *schema.ResourceData, meta interface{}) error {
	snsconn := meta.(*AWSClient).snsconn

	// If any changes happened, un-subscribe and re-subscribe
	if !d.IsNewResource() && (d.HasChange("protocol") || d.HasChange("endpoint") || d.HasChange("topic_arn")) {
		log.Printf("[DEBUG] Updating subscription %s", d.Id())
		// Unsubscribe
		_, err := snsconn.Unsubscribe(&sns.UnsubscribeInput{
			SubscriptionArn: aws.String(d.Id()),
		})

		if err != nil {
			return fmt.Errorf("Error unsubscribing from SNS topic: %s", err)
		}

		// Re-subscribe and set id
		output, err := subscribeToSNSTopic(d, snsconn)
		d.SetId(*output.SubscriptionArn)
		d.Set("arn", *output.SubscriptionArn)
	}

	if d.HasChange("raw_message_delivery") {
		_, n := d.GetChange("raw_message_delivery")

		attrValue := "false"

		if n.(bool) {
			attrValue = "true"
		}

		req := &sns.SetSubscriptionAttributesInput{
			SubscriptionArn: aws.String(d.Id()),
			AttributeName:   aws.String("RawMessageDelivery"),
			AttributeValue:  aws.String(attrValue),
		}
		_, err := snsconn.SetSubscriptionAttributes(req)

		if err != nil {
			return fmt.Errorf("Unable to set raw message delivery attribute on subscription")
		}
	}

	return resourceAwsSnsTopicSubscriptionRead(d, meta)
}

func resourceAwsSnsTopicSubscriptionRead(d *schema.ResourceData, meta interface{}) error {
	snsconn := meta.(*AWSClient).snsconn

	log.Printf("[DEBUG] Loading subscription %s", d.Id())

	attributeOutput, err := snsconn.GetSubscriptionAttributes(&sns.GetSubscriptionAttributesInput{
		SubscriptionArn: aws.String(d.Id()),
	})
	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NotFound" {
			log.Printf("[WARN] SNS Topic Subscription (%s) not found, error code (404)", d.Id())
			d.SetId("")
			return nil
		}

		return err
	}

	if attributeOutput.Attributes != nil && len(attributeOutput.Attributes) > 0 {
		attrHash := attributeOutput.Attributes
		resource := *resourceAwsSnsTopicSubscription()

		for iKey, oKey := range SNSSubscriptionAttributeMap {
			log.Printf("[DEBUG] Reading %s => %s", iKey, oKey)

			if attrHash[oKey] != nil {
				if resource.Schema[iKey] != nil {
					var value string
					value = *attrHash[oKey]
					log.Printf("[DEBUG] Reading %s => %s -> %s", iKey, oKey, value)
					d.Set(iKey, value)
				}
			}
		}
	}

	return nil
}

func resourceAwsSnsTopicSubscriptionDelete(d *schema.ResourceData, meta interface{}) error {
	snsconn := meta.(*AWSClient).snsconn

	log.Printf("[DEBUG] SNS delete topic subscription: %s", d.Id())
	_, err := snsconn.Unsubscribe(&sns.UnsubscribeInput{
		SubscriptionArn: aws.String(d.Id()),
	})
	if err != nil {
		return err
	}
	return nil
}

func subscribeToSNSTopic(d *schema.ResourceData, snsconn *sns.SNS) (output *sns.SubscribeOutput, err error) {
	protocol := d.Get("protocol").(string)
	endpoint := d.Get("endpoint").(string)
	topic_arn := d.Get("topic_arn").(string)
	endpoint_auto_confirms := d.Get("endpoint_auto_confirms").(bool)
	confirmation_timeout_in_minutes := d.Get("confirmation_timeout_in_minutes").(int)

	if strings.Contains(protocol, "http") && !endpoint_auto_confirms {
		return nil, fmt.Errorf("Protocol http/https is only supported for endpoints which auto confirms!")
	}

	log.Printf("[DEBUG] SNS create topic subscription: %s (%s) @ '%s'", endpoint, protocol, topic_arn)

	req := &sns.SubscribeInput{
		Protocol: aws.String(protocol),
		Endpoint: aws.String(endpoint),
		TopicArn: aws.String(topic_arn),
	}

	output, err = snsconn.Subscribe(req)
	if err != nil {
		return nil, fmt.Errorf("Error creating SNS topic: %s", err)
	}

	log.Printf("[DEBUG] Finished subscribing to topic %s with subscription arn %s", topic_arn, *output.SubscriptionArn)

	if strings.Contains(protocol, "http") && subscriptionHasPendingConfirmation(output.SubscriptionArn) {

		log.Printf("[DEBUG] SNS create topic subscription is pending so fetching the subscription list for topic : %s (%s) @ '%s'", endpoint, protocol, topic_arn)

		err = resource.Retry(time.Duration(confirmation_timeout_in_minutes)*time.Minute, func() *resource.RetryError {

			subscription, err := findSubscriptionByNonID(d, snsconn)

			if subscription != nil {
				output.SubscriptionArn = subscription.SubscriptionArn
				return nil
			}

			if err != nil {
				return resource.RetryableError(
					fmt.Errorf("Error fetching subscriptions for SNS topic %s: %s", topic_arn, err))
			}

			return resource.RetryableError(
				fmt.Errorf("Endpoint (%s) did not autoconfirm the subscription for topic %s", endpoint, topic_arn))
		})

		if err != nil {
			return nil, err
		}
	}

	log.Printf("[DEBUG] Created new subscription! %s", *output.SubscriptionArn)
	return output, nil
}

// finds a subscription using protocol, endpoint and topic_arn (which is a key in sns subscription)
func findSubscriptionByNonID(d *schema.ResourceData, snsconn *sns.SNS) (*sns.Subscription, error) {
	protocol := d.Get("protocol").(string)
	endpoint := d.Get("endpoint").(string)
	topic_arn := d.Get("topic_arn").(string)
	obfuscatedEndpoint := obfuscateEndpoint(endpoint)

	req := &sns.ListSubscriptionsByTopicInput{
		TopicArn: aws.String(topic_arn),
	}

	for {
		res, err := snsconn.ListSubscriptionsByTopic(req)

		if err != nil {
			return nil, fmt.Errorf("Error fetching subscriptions for topic %s : %s", topic_arn, err)
		}

		for _, subscription := range res.Subscriptions {
			log.Printf("[DEBUG] check subscription with Subscription EndPoint %s (local: %s), Protocol %s, topicARN %s and SubscriptionARN %s", *subscription.Endpoint, obfuscatedEndpoint, *subscription.Protocol, *subscription.TopicArn, *subscription.SubscriptionArn)
			if *subscription.Endpoint == obfuscatedEndpoint && *subscription.Protocol == protocol && *subscription.TopicArn == topic_arn && !subscriptionHasPendingConfirmation(subscription.SubscriptionArn) {
				return subscription, nil
			}
		}

		// if there are more than 100 subscriptions then go to the next 100 otherwise return an error
		if res.NextToken != nil {
			req.NextToken = res.NextToken
		} else {
			return nil, fmt.Errorf("Error finding subscription for topic %s with endpoint %s and protocol %s", topic_arn, endpoint, protocol)
		}
	}
}

// returns true if arn is nil or has both pending and confirmation words in the arn
func subscriptionHasPendingConfirmation(arn *string) bool {
	if arn != nil && !strings.Contains(strings.Replace(strings.ToLower(*arn), " ", "", -1), awsSNSPendingConfirmationMessageWithoutSpaces) {
		return false
	}

	return true
}

// returns the endpoint with obfuscated password, if any
func obfuscateEndpoint(endpoint string) string {
	res, err := url.Parse(endpoint)
	if err != nil {
		fmt.Println(err)
	}

	var obfuscatedEndpoint = res.String()

	// If the user is defined, we try to get the username and password, if defined.
	// Then, we update the user with the obfuscated version.
	if res.User != nil {
		if password, ok := res.User.Password(); ok {
			obfuscatedEndpoint = strings.Replace(obfuscatedEndpoint, password, awsSNSPasswordObfuscationPattern, 1)
		}
	}
	return obfuscatedEndpoint
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package fuseutil

import (
	"errors"

	"bazil.org/fuse"
	fusefs "bazil.org/fuse/fs"
	"golang.org/x/net/context"
)

// A struct representing the status of a mount operation, with methods for
// waiting on the mount to complete, waiting for unmounting, and causing
// unmounting.
type MountedFileSystem struct {
	dir string

	// The result to return from WaitForReady. Not valid until the channel is
	// closed.
	readyStatus          error
	readyStatusAvailable chan struct{}

	// The result to return from Join. Not valid until the channel is closed.
	joinStatus          error
	joinStatusAvailable chan struct{}
}

// Return the directory on which the file system is mounted (or where we
// attempted to mount it.)
func (mfs *MountedFileSystem) Dir() string {
	return mfs.dir
}

// Wait until the mount point is ready to be used. After a successful return
// from this function, the contents of the mounted file system should be
// visible in the directory supplied to NewMountPoint. May be called multiple
// times.
func (mfs *MountedFileSystem) WaitForReady(ctx context.Context) error {
	select {
	case <-mfs.readyStatusAvailable:
		return mfs.readyStatus
	case <-ctx.Done():
		return ctx.Err()
	}
}

// Block until a mounted file system has been unmounted. The return value will
// be non-nil if anything unexpected happened while serving. May be called
// multiple times. Must not be called unless WaitForReady has returned nil.
func (mfs *MountedFileSystem) Join(ctx context.Context) error {
	select {
	case <-mfs.joinStatusAvailable:
		return mfs.joinStatus
	case <-ctx.Done():
		return ctx.Err()
	}
}

// Attempt to unmount the file system. Use Join to wait for it to actually be
// unmounted. You must first call WaitForReady to ensure there is no race with
// mounting.
func (mfs *MountedFileSystem) Unmount() error {
	return fuse.Unmount(mfs.dir)
}

// Runs in the background.
func (mfs *MountedFileSystem) mountAndServe(
	fs fusefs.FS,
	options []fuse.MountOption) {
	logger := getLogger()

	// Open a FUSE connection.
	logger.Println("Opening a FUSE connection.")
	c, err := fuse.Mount(mfs.dir, options...)
	if err != nil {
		mfs.readyStatus = errors.New("fuse.Mount: " + err.Error())
		close(mfs.readyStatusAvailable)
		return
	}

	defer c.Close()

	// Start a goroutine that will notify the MountedFileSystem object when the
	// connection says it is ready (or it fails to become ready).
	go func() {
		<-c.Ready
		mfs.readyStatus = c.MountError
		close(mfs.readyStatusAvailable)
	}()

	// Serve the connection using the file system object.
	server := &fusefs.Server{
		FS: fs,
		Debug: func(msg interface{}) {
			logger.Println(msg)
		},
	}

	if err := server.Serve(c); err != nil {
		mfs.joinStatus = errors.New("fusefs.Serve: " + err.Error())
		close(mfs.joinStatusAvailable)
		return
	}

	// Signal that everything is okay.
	close(mfs.joinStatusAvailable)
}

// Attempt to mount the supplied file system on the given directory.
// mfs.WaitForReady() must be called to find out whether the mount was
// successful.
func MountFileSystem(
	dir string,
	fs fusefs.FS,
	options ...fuse.MountOption) (mfs *MountedFileSystem) {
	// Initialize the struct.
	mfs = &MountedFileSystem{
		dir:                  dir,
		readyStatusAvailable: make(chan struct{}),
		joinStatusAvailable:  make(chan struct{}),
	}

	// Mount in the background.
	go mfs.mountAndServe(fs, options)

	return mfs
}
<commit_msg>Updated the MountedFileSystem factory signature.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package fuseutil

import (
	"errors"

	"bazil.org/fuse"
	fusefs "bazil.org/fuse/fs"
	"golang.org/x/net/context"
)

// A struct representing the status of a mount operation, with methods for
// waiting on the mount to complete, waiting for unmounting, and causing
// unmounting.
type MountedFileSystem struct {
	dir string

	// The result to return from WaitForReady. Not valid until the channel is
	// closed.
	readyStatus          error
	readyStatusAvailable chan struct{}

	// The result to return from Join. Not valid until the channel is closed.
	joinStatus          error
	joinStatusAvailable chan struct{}
}

// Return the directory on which the file system is mounted (or where we
// attempted to mount it.)
func (mfs *MountedFileSystem) Dir() string {
	return mfs.dir
}

// Wait until the mount point is ready to be used. After a successful return
// from this function, the contents of the mounted file system should be
// visible in the directory supplied to NewMountPoint. May be called multiple
// times.
func (mfs *MountedFileSystem) WaitForReady(ctx context.Context) error {
	select {
	case <-mfs.readyStatusAvailable:
		return mfs.readyStatus
	case <-ctx.Done():
		return ctx.Err()
	}
}

// Block until a mounted file system has been unmounted. The return value will
// be non-nil if anything unexpected happened while serving. May be called
// multiple times. Must not be called unless WaitForReady has returned nil.
func (mfs *MountedFileSystem) Join(ctx context.Context) error {
	select {
	case <-mfs.joinStatusAvailable:
		return mfs.joinStatus
	case <-ctx.Done():
		return ctx.Err()
	}
}

// Attempt to unmount the file system. Use Join to wait for it to actually be
// unmounted. You must first call WaitForReady to ensure there is no race with
// mounting.
func (mfs *MountedFileSystem) Unmount() error {
	return fuse.Unmount(mfs.dir)
}

// Runs in the background.
func (mfs *MountedFileSystem) mountAndServe(
	fs fusefs.FS,
	options []fuse.MountOption) {
	logger := getLogger()

	// Open a FUSE connection.
	logger.Println("Opening a FUSE connection.")
	c, err := fuse.Mount(mfs.dir, options...)
	if err != nil {
		mfs.readyStatus = errors.New("fuse.Mount: " + err.Error())
		close(mfs.readyStatusAvailable)
		return
	}

	defer c.Close()

	// Start a goroutine that will notify the MountedFileSystem object when the
	// connection says it is ready (or it fails to become ready).
	go func() {
		<-c.Ready
		mfs.readyStatus = c.MountError
		close(mfs.readyStatusAvailable)
	}()

	// Serve the connection using the file system object.
	server := &fusefs.Server{
		FS: fs,
		Debug: func(msg interface{}) {
			logger.Println(msg)
		},
	}

	if err := server.Serve(c); err != nil {
		mfs.joinStatus = errors.New("fusefs.Serve: " + err.Error())
		close(mfs.joinStatusAvailable)
		return
	}

	// Signal that everything is okay.
	close(mfs.joinStatusAvailable)
}

// Attempt to mount the supplied file system on the given directory.
// mfs.WaitForReady() must be called to find out whether the mount was
// successful.
func Mount(
	dir string,
	fs FileSystem) (mfs *MountedFileSystem)
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"errors"
	"log"
)

func NewContactQuery(page int, perPage int) *ContactQuery {
	if page < 1 {
		page = 1
	}
	if perPage < 1 {
		perPage = 1
	}

	return &ContactQuery{
		limit:      perPage,
		offset:     perPage * (page - 1),
		collection: NewContactList(perPage),
	}
}

type ContactQuery struct {
	limit      int
	offset     int
	collection ContactList
	conn       *sql.DB
}

func (cq *ContactQuery) All() []*Contact {
	if cq.conn = NewDBConn(); cq.conn != nil {
		defer cq.conn.Close()
	}

	if ok := cq.fillUsers(); !ok {
		return NewContactList(0).Items()
	}

	if err := cq.fillDependentData(); err != nil {
		log.Print(err)
	}

	return cq.collection.Items()
}

func (cq *ContactQuery) fillUsers() (ok bool) {
	var err error

	defer func() {
		if err != nil {
			log.Print(err)
		}

		if cq.collection.Any() {
			ok = true
		}
	}()

	ps, err := cq.selectUsersStmt()
	if err != nil {
		return
	}
	defer ps.Close()

	rows, err := ps.Query(cq.limit, cq.offset)
	if err != nil {
		return
	}
	defer rows.Close()

	for rows.Next() {
		contact := NewContact()
		rows.Scan(
			&contact.Id,
			&contact.Email,
			&contact.FirstName,
			&contact.LastName,
			&contact.MiddleName,
			&contact.DateOfBirth,
			&contact.Sex,
		)

		cq.collection.Append(contact)
	}

	return
}

func (cq *ContactQuery) fillDependentData() (err error) {
	ps, err := cq.selectDependentDataStmt()
	if err != nil {
		return
	}
	defer ps.Close()

	rows, err := ps.Query(cq.collection.Ids())
	if err != nil {
		return
	}
	defer rows.Close()

	var userId sql.NullInt64
	current := cq.collection.First()

	for rows.Next() {

		profile := NewProfile()
		rows.Scan(
			&profile.Id,
			&profile.Type,
			&userId,
		)

		for current.Id != userId {

			if ok := cq.collection.Next(); !ok {
				break
			}
			current = cq.collection.Current()
		}

		current.Profiles = append(current.Profiles, profile)
	}

	return
}

func (cq *ContactQuery) selectUsersStmt() (*sql.Stmt, error) {
	if cq.conn == nil {
		return nil, errors.New("Can't connect to DB")
	}

	return cq.conn.Prepare(`
		select	id,
		      	email,
		      	first_name,
		      	last_name,
		      	middle_name,
		      	date_of_birth,
		      	sex
		  from users
		  where deleted_at is null
		  order by id
		  limit $1
		  offset $2`)
}

func (cq *ContactQuery) selectDependentDataStmt() (*sql.Stmt, error) {
	if cq.conn == nil {
		return nil, errors.New("Can't connect to DB")
	}

	return cq.conn.Prepare(`
		select	id,
		      	type,
		      	user_id
		  from profiles
		  where deleted_at is null
		    and user_id = any($1::integer[])
		  order by user_id, id`)
}
<commit_msg>[kami][ContactQuery] Finalize FillDependentData method<commit_after>package main

import (
	"database/sql"
	"errors"
	"log"
)

func NewContactQuery(page int, perPage int) *ContactQuery {
	if page < 1 {
		page = 1
	}
	if perPage < 1 {
		perPage = 1
	}

	return &ContactQuery{
		limit:      perPage,
		offset:     perPage * (page - 1),
		collection: NewContactList(perPage),
	}
}

type ContactQuery struct {
	limit      int
	offset     int
	collection ContactList
	conn       *sql.DB
}

func (cq *ContactQuery) All() []*Contact {
	if cq.conn = NewDBConn(); cq.conn != nil {
		defer cq.conn.Close()
	}

	if ok := cq.fillUsers(); !ok {
		return NewContactList(0).Items()
	}

	if err := cq.fillDependentData(); err != nil {
		log.Print(err)
	}

	return cq.collection.Items()
}

func (cq *ContactQuery) fillUsers() (ok bool) {
	var err error

	defer func() {
		if err != nil {
			log.Print(err)
		}

		if cq.collection.Any() {
			ok = true
		}
	}()

	ps, err := cq.selectUsersStmt()
	if err != nil {
		return
	}
	defer ps.Close()

	rows, err := ps.Query(cq.limit, cq.offset)
	if err != nil {
		return
	}
	defer rows.Close()

	for rows.Next() {
		contact := NewContact()
		rows.Scan(
			&contact.Id,
			&contact.Email,
			&contact.FirstName,
			&contact.LastName,
			&contact.MiddleName,
			&contact.DateOfBirth,
			&contact.Sex,
		)

		cq.collection.Append(contact)
	}

	return
}

func (cq *ContactQuery) fillDependentData() (err error) {
	ps, err := cq.selectDependentDataStmt()
	if err != nil {
		return
	}
	defer ps.Close()

	rows, err := ps.Query(cq.collection.Ids())
	if err != nil {
		return
	}
	defer rows.Close()

	var userId sql.NullInt64

	current := cq.collection.First()

	if current == nil {
		return errors.New("Empty collection")
	}

	for rows.Next() {

		profile := NewProfile()
		subject := NewSubject()
		rows.Scan(
			&profile.Id,
			&profile.Type,
			&userId,
			&profile.School.Id,
			&profile.School.Name,
			&profile.School.Guid,
			&profile.ClassUnit.Id,
			&profile.ClassUnit.Name,
			&profile.ClassUnit.EnlistedOn,
			&profile.ClassUnit.LeftOn,
			&subject.Id,
			&subject.Name,
		)

		for current.Id != userId {
			if cq.collection.Next() {
				current = cq.collection.Current()
			} else {
				break
			}
		}

		if current.Id != userId {
			break
		}

		if lastPr := current.LastProfile(); lastPr != nil {
			if lastPr.Id != profile.Id {
				current.Profiles = append(current.Profiles, profile)
			}
		} else {
			current.Profiles = append(current.Profiles, profile)
		}

		if subject.Id.Valid {
			current.LastProfile().Subjects = append(
				current.LastProfile().Subjects,
				subject,
			)
		}
	}

	return
}

func (cq *ContactQuery) selectUsersStmt() (*sql.Stmt, error) {
	if cq.conn == nil {
		return nil, errors.New("Can't connect to DB")
	}

	return cq.conn.Prepare(`
		select	id,
		      	email,
		      	first_name,
		      	last_name,
		      	middle_name,
		      	date_of_birth,
		      	sex
		  from users
		  where deleted_at is null
		  order by id
		  limit $1
		  offset $2`)
}

func (cq *ContactQuery) selectDependentDataStmt() (*sql.Stmt, error) {
	if cq.conn == nil {
		return nil, errors.New("Can't connect to DB")
	}

	return cq.conn.Prepare(`
		select	p.id,
		      	p.type,
		      	p.user_id,
		      	p.school_id,
		      	s.short_name,
		      	s.guid,
		      	p.class_unit_id,
		      	cu.name,
		      	p.enlisted_on,
		      	p.left_on,
		      	c.subject_id,
		      	sb.name
		  from profiles p
		  left outer join schools s
		    on s.id = p.school_id
		    and s.deleted_at is null
		  left outer join class_units cu
		    on cu.id = p.class_unit_id
		    and cu.deleted_at is null
		  left outer join competences c
		    on c.profile_id = p.id
		  left outer join subjects sb
		    on c.subject_id = sb.id
		    and cu.deleted_at is null
		  where p.deleted_at is null
		    and p.user_id = any($1::integer[])
		  order by p.user_id, p.id`)
}
<|endoftext|>


<commit_before>package operator

import (
	"github.com/beard1ess/gauss/parsing"
	"reflect"
)

func recursion(

	original parsing.Keyvalue,
	modified parsing.Keyvalue,
	path []string,
	ObjectDiff parsing.ConsumableDifference,

) parsing.ConsumableDifference {

	if reflect.DeepEqual(original, modified) {
		return ObjectDiff
	}

	if !(parsing.UnorderedKeyMatch(original, modified)) {

		for k, v := range modified {
			if parsing.IndexOf(parsing.Slicer(original), k) == -1 {
				added := parsing.AddedDifference{Path: parsing.PathFormatter(path), Key: k, Value: v}
				ObjectDiff.Added = append(ObjectDiff.Added, added)
				delete(modified, k)
			}
		}
		for k, v := range original {
			if parsing.IndexOf(parsing.Slicer(modified), k) == -1 {
				removed := parsing.RemovedDifference{Path: parsing.PathFormatter(path), Key: k, Value: v}
				ObjectDiff.Removed = append(ObjectDiff.Removed, removed)
				delete(original, k)
			}
		}

		ObjectDiff = recursion(original, modified, path, ObjectDiff)
		return ObjectDiff

	} else if len(parsing.Slicer(original)) > 1 || len(parsing.Slicer(modified)) > 1 {

		for k := range original {
			ObjectDiff = recursion(parsing.Keyvalue{k: original[k]}, parsing.Keyvalue{k: modified[k]}, path, ObjectDiff)
		}
		return ObjectDiff
	} else {

		for k := range original {
			valOrig := original[k]
			valMod := modified[k]

			if !(reflect.DeepEqual(valMod, valOrig)) {
				// Specifically handle type mismatch
				if reflect.TypeOf(valOrig) != reflect.TypeOf(valMod) {
					changed := parsing.ChangedDifference{Path: parsing.PathFormatter(path),
						Key: k, OldValue: valOrig, NewValue: valMod}
					ObjectDiff.Changed = append(ObjectDiff.Changed, changed)
					return ObjectDiff
					// Map handler
				} else if reflect.TypeOf(valOrig).Kind() == reflect.Map {
					// Update the working path
					path = append(path, k)
					ObjectDiff = recursion(parsing.Remarshal(valOrig), parsing.Remarshal(valMod), path, ObjectDiff)
					return ObjectDiff
					// Slice handler
				} else if reflect.TypeOf(valOrig).Kind() == reflect.Slice {

					// Variable setup
					valOrig, _ := valOrig.([]interface{})
					valMod, _ := valMod.([]interface{})
					// Update the working path and copy into a new var
					path = append(path, k)
					if len(valOrig) != len(valMod) {

						// If slice length mismatches we need to handle that a particular way
						if len(valOrig) > len(valMod) {

							for i := range valOrig {

							Mod:
								for ii := range valMod {
									if i == ii && reflect.DeepEqual(valOrig[i], valMod[ii]) {
										break Mod
									} else {
										if i != ii && reflect.DeepEqual(valOrig[i], valMod[ii]) {
											indexed := parsing.IndexDifference{OldIndex: i, NewIndex: ii, Value: valOrig[ii],
												Path: parsing.PathFormatter(path)}
											ObjectDiff.Indexes = append(ObjectDiff.Indexes, indexed)
											break Mod
										} else if i == ii && !(reflect.DeepEqual(valOrig[i], valMod[ii])) {

											if reflect.TypeOf(valOrig[i]).Kind() == reflect.String ||
												reflect.TypeOf(valMod[ii]).Kind() == reflect.String ||
												!(parsing.DoMapArrayKeysMatch(valOrig[i], valMod[ii])){

												changed := parsing.ChangedDifference{Path: parsing.PathFormatter(parsing.PathSlice(i, path)),
													OldValue: valOrig[i], NewValue: valMod[i]}
												ObjectDiff.Changed = append(ObjectDiff.Changed, changed)
												break Mod

											} else {
												ObjectDiff = recursion(parsing.Remarshal(valOrig[i]),
													parsing.Remarshal(valMod[i]), parsing.PathSlice(i, path), ObjectDiff)
											}
										}
									}
								}
								if i > len(valMod)-1 && !(parsing.MatchAny(valOrig[i], valMod)) {

									removed := parsing.RemovedDifference{Path: parsing.PathFormatter(parsing.PathSlice(i, path)),
										Value:                             valOrig[i]}
									ObjectDiff.Removed = append(ObjectDiff.Removed, removed)
								}
							}

						} else {
							for i := range valMod {
							Orig:
								for ii := range valOrig {
									if i == ii && reflect.DeepEqual(valOrig[ii], valMod[i]) {
										break Orig
									} else {
										if i != ii && reflect.DeepEqual(valOrig[ii], valMod[i]) {
											indexed := parsing.IndexDifference{OldIndex: ii, NewIndex: i, Value: valOrig[ii],
												Path: parsing.PathFormatter(path)}
											ObjectDiff.Indexes = append(ObjectDiff.Indexes, indexed)
											break Orig
										} else if i == ii && !(reflect.DeepEqual(valOrig[ii], valMod[i])) {
											if reflect.TypeOf(valOrig[ii]).Kind() == reflect.String || reflect.TypeOf(valMod[i]).Kind() == reflect.String {
												changed := parsing.ChangedDifference{Path: parsing.PathFormatter(parsing.PathSlice(i, path)),
													OldValue: valOrig[i], NewValue: valMod[i]}
												ObjectDiff.Changed = append(ObjectDiff.Changed, changed)
												break Orig
											} else {
												ObjectDiff = recursion(parsing.Remarshal(valOrig[i]),
													parsing.Remarshal(valMod[i]), parsing.PathSlice(i, path), ObjectDiff)
											}
										}
									}
								}
								if i > len(valOrig)-1 && !(parsing.MatchAny(valMod[i], valOrig)) {

									added := parsing.AddedDifference{Path: parsing.PathFormatter(parsing.PathSlice(i, path)),
										Value:                             valMod[i]}
									ObjectDiff.Added = append(ObjectDiff.Added, added)
								}
							}
						}
					} else {
						// If both slice lengths are equal
						for i := range valOrig {
							if !(reflect.DeepEqual(valOrig[i], valMod[i])) {
								if reflect.TypeOf(valOrig[i]).Kind() == reflect.String || reflect.TypeOf(valMod[i]).Kind() == reflect.String {

									changed := parsing.ChangedDifference{Path: parsing.PathFormatter(parsing.PathSlice(i, path)),
										OldValue: valOrig[i], NewValue: valMod[i]}
									ObjectDiff.Changed = append(ObjectDiff.Changed, changed)
								} else {

									ObjectDiff = recursion(parsing.Remarshal(valOrig[i]), parsing.Remarshal(valMod[i]),
										parsing.PathSlice(i, path), ObjectDiff)
								}
							}
						}
					}
				} else {
					changed := parsing.ChangedDifference{Path: parsing.PathFormatter(path),
						Key: k, OldValue: valOrig, NewValue: valMod}
					ObjectDiff.Changed = append(ObjectDiff.Changed, changed)

				}
			}
		}
		return ObjectDiff
	}
}

func Recursion(original parsing.Keyvalue, modified parsing.Keyvalue, path []string) parsing.ConsumableDifference {
	var ObjectDiff parsing.ConsumableDifference
	return recursion(original, modified, path, ObjectDiff)
}
<commit_msg>Using pointer because turns out theyre simple af<commit_after>package operator

import (
	"github.com/beard1ess/gauss/parsing"
	"reflect"
)

func recursion(

	original parsing.Keyvalue,
	modified parsing.Keyvalue,
	path []string,
	ObjectDiff *parsing.ConsumableDifference,

)  {

	if reflect.DeepEqual(original, modified) {
		return
	}

	if !(parsing.UnorderedKeyMatch(original, modified)) {

		for k, v := range modified {
			if parsing.IndexOf(parsing.Slicer(original), k) == -1 {
				added := parsing.AddedDifference{Path: parsing.PathFormatter(path), Key: k, Value: v}
				ObjectDiff.Added = append(ObjectDiff.Added, added)
				delete(modified, k)
			}
		}
		for k, v := range original {
			if parsing.IndexOf(parsing.Slicer(modified), k) == -1 {
				removed := parsing.RemovedDifference{Path: parsing.PathFormatter(path), Key: k, Value: v}
				ObjectDiff.Removed = append(ObjectDiff.Removed, removed)
				delete(original, k)
			}
		}

		recursion(original, modified, path, ObjectDiff)
		return

	} else if len(parsing.Slicer(original)) > 1 || len(parsing.Slicer(modified)) > 1 {

		for k := range original {
			 recursion(parsing.Keyvalue{k: original[k]}, parsing.Keyvalue{k: modified[k]}, path, ObjectDiff)
		}
		return
	} else {

		for k := range original {
			valOrig := original[k]
			valMod := modified[k]

			if !(reflect.DeepEqual(valMod, valOrig)) {
				// Specifically handle type mismatch
				if reflect.TypeOf(valOrig) != reflect.TypeOf(valMod) {
					changed := parsing.ChangedDifference{Path: parsing.PathFormatter(path),
						Key: k, OldValue: valOrig, NewValue: valMod}
					ObjectDiff.Changed = append(ObjectDiff.Changed, changed)
					return
					// Map handler
				} else if reflect.TypeOf(valOrig).Kind() == reflect.Map {
					// Update the working path
					path = append(path, k)
					recursion(parsing.Remarshal(valOrig), parsing.Remarshal(valMod), path, ObjectDiff)
					return
					// Slice handler
				} else if reflect.TypeOf(valOrig).Kind() == reflect.Slice {

					// Variable setup
					valOrig, _ := valOrig.([]interface{})
					valMod, _ := valMod.([]interface{})
					// Update the working path and copy into a new var
					path = append(path, k)
					if len(valOrig) != len(valMod) {

						// If slice length mismatches we need to handle that a particular way
						if len(valOrig) > len(valMod) {

							for i := range valOrig {

							Mod:
								for ii := range valMod {
									if i == ii && reflect.DeepEqual(valOrig[i], valMod[ii]) {
										break Mod
									} else {
										if i != ii && reflect.DeepEqual(valOrig[i], valMod[ii]) {
											indexed := parsing.IndexDifference{OldIndex: i, NewIndex: ii, Value: valOrig[ii],
												Path: parsing.PathFormatter(path)}
											ObjectDiff.Indexes = append(ObjectDiff.Indexes, indexed)
											break Mod
										} else if i == ii && !(reflect.DeepEqual(valOrig[i], valMod[ii])) {

											if reflect.TypeOf(valOrig[i]).Kind() == reflect.String ||
												reflect.TypeOf(valMod[ii]).Kind() == reflect.String ||
												!(parsing.DoMapArrayKeysMatch(valOrig[i], valMod[ii])){

												changed := parsing.ChangedDifference{Path: parsing.PathFormatter(parsing.PathSlice(i, path)),
													OldValue: valOrig[i], NewValue: valMod[i]}
												ObjectDiff.Changed = append(ObjectDiff.Changed, changed)
												break Mod

											} else {
												recursion(parsing.Remarshal(valOrig[i]),
													parsing.Remarshal(valMod[i]), parsing.PathSlice(i, path), ObjectDiff)
											}
										}
									}
								}
								if i > len(valMod)-1 && !(parsing.MatchAny(valOrig[i], valMod)) {

									removed := parsing.RemovedDifference{Path: parsing.PathFormatter(parsing.PathSlice(i, path)),
										Value:                             valOrig[i]}
									ObjectDiff.Removed = append(ObjectDiff.Removed, removed)
								}
							}

						} else {
							for i := range valMod {
							Orig:
								for ii := range valOrig {
									if i == ii && reflect.DeepEqual(valOrig[ii], valMod[i]) {
										break Orig
									} else {
										if i != ii && reflect.DeepEqual(valOrig[ii], valMod[i]) {
											indexed := parsing.IndexDifference{OldIndex: ii, NewIndex: i, Value: valOrig[ii],
												Path: parsing.PathFormatter(path)}
											ObjectDiff.Indexes = append(ObjectDiff.Indexes, indexed)
											break Orig
										} else if i == ii && !(reflect.DeepEqual(valOrig[ii], valMod[i])) {
											if reflect.TypeOf(valOrig[ii]).Kind() == reflect.String || reflect.TypeOf(valMod[i]).Kind() == reflect.String {
												changed := parsing.ChangedDifference{Path: parsing.PathFormatter(parsing.PathSlice(i, path)),
													OldValue: valOrig[i], NewValue: valMod[i]}
												ObjectDiff.Changed = append(ObjectDiff.Changed, changed)
												break Orig
											} else {
												recursion(parsing.Remarshal(valOrig[i]),
													parsing.Remarshal(valMod[i]), parsing.PathSlice(i, path), ObjectDiff)
											}
										}
									}
								}
								if i > len(valOrig)-1 && !(parsing.MatchAny(valMod[i], valOrig)) {

									added := parsing.AddedDifference{Path: parsing.PathFormatter(parsing.PathSlice(i, path)),
										Value:                             valMod[i]}
									ObjectDiff.Added = append(ObjectDiff.Added, added)
								}
							}
						}
					} else {
						// If both slice lengths are equal
						for i := range valOrig {
							if !(reflect.DeepEqual(valOrig[i], valMod[i])) {
								if reflect.TypeOf(valOrig[i]).Kind() == reflect.String || reflect.TypeOf(valMod[i]).Kind() == reflect.String {

									changed := parsing.ChangedDifference{Path: parsing.PathFormatter(parsing.PathSlice(i, path)),
										OldValue: valOrig[i], NewValue: valMod[i]}
									ObjectDiff.Changed = append(ObjectDiff.Changed, changed)
								} else {

									recursion(parsing.Remarshal(valOrig[i]), parsing.Remarshal(valMod[i]),
										parsing.PathSlice(i, path), ObjectDiff)
								}
							}
						}
					}
				} else {
					changed := parsing.ChangedDifference{Path: parsing.PathFormatter(path),
						Key: k, OldValue: valOrig, NewValue: valMod}
					ObjectDiff.Changed = append(ObjectDiff.Changed, changed)

				}
			}
		}
		return
	}
}

func Recursion(original parsing.Keyvalue, modified parsing.Keyvalue, path []string) parsing.ConsumableDifference {
	var ObjectDiff parsing.ConsumableDifference
	recursion(original, modified, path, &ObjectDiff)
	return ObjectDiff
}
<|endoftext|>


<commit_before>package output

import (
	"fmt"
	"testing"
	//"bitbucket.com/cswank/gogadgets/pins"
	"bitbucket.com/cswank/gogadgets/devices"
	"bitbucket.com/cswank/gogadgets"
)

type FakeOutput struct {
	devices.OutputDevice
	on bool
}

func (f *FakeOutput) On() error {
	f.on = true
	return nil
}

func (f *FakeOutput) Off() error {
	f.on = false
	return nil
}

func (f *FakeOutput) Status() bool {
	return f.on
}

func TestStart(t *testing.T) {
	location := "lab"
	name := "led"
	g := OutputGadget{
		Location: location,
		Name: name,
		OnCommand: fmt.Sprintf("turn on %s %s", location, name),
		OffCommand: fmt.Sprintf("turn off %s %s", location, name),
		Output: &FakeOutput{},
		uid: fmt.Sprintf("%s %s", location, name),
	}
	input := make(chan gogadgets.Message)
	output := make(chan gogadgets.Message)
	go g.Start(input, output)
	msg := gogadgets.Message{
		Type: "command",
		Body: "turn on lab led",
	}
	input<- msg
	status := <-output
	if status.Locations["lab"].Output["led"].Value != true {
		t.Error("shoulda been on", status)
	}

	msg = gogadgets.Message{
		Type: "command",
		Body: "shutdown",
	}
	input<- msg
	status = <-output
	if status.Locations["lab"].Output["led"].Value != false {
		t.Error("shoulda been off", status)
	}
}

func TestStartWithTrigger(t *testing.T) {
	location := "tank"
	name := "valve"
	g := OutputGadget{
		Location: location,
		Name: name,
		OnCommand: fmt.Sprintf("fill %s", location),
		OffCommand: fmt.Sprintf("stop filling %s", location),
		Output: &FakeOutput{},
		uid: fmt.Sprintf("%s %s", location, name),
	}
	input := make(chan gogadgets.Message)
	output := make(chan gogadgets.Message)
	go g.Start(input, output)
	msg := gogadgets.Message{
		Type: "command",
		Body: "fill tank to 4.4 liters",
	}
	input<- msg
	status := <-output
	if status.Locations["tank"].Output["valve"].Value != true {
		t.Error("shoulda been on", status)
	}

	//make a message that should trigger the trigger and stop the device
	l := gogadgets.Location{
		Input: map[string]gogadgets.Device{
			"volume": gogadgets.Device{
				Units: "liters",
				Value: 4.4,
			},
		},
	}
	msg = gogadgets.Message{
		Sender: "tank volume",
		Type: gogadgets.STATUS,
		Locations: map[string]gogadgets.Location{"tank": l},
	}
	input<- msg
	status = <-output
	if status.Locations["tank"].Output["valve"].Value != false {
		t.Error("shoulda been off", status)
	}
}

func TestStartWithTimeTrigger(t *testing.T) {
	location := "lab"
	name := "led"
	g := OutputGadget{
		Location: location,
		Name: name,
		OnCommand: "turn on lab led",
		OffCommand: "turn off lab led",
		Output: &FakeOutput{},
		uid: fmt.Sprintf("%s %s", location, name),
	}
	input := make(chan gogadgets.Message)
	output := make(chan gogadgets.Message)
	go g.Start(input, output)
	msg := gogadgets.Message{
		Type: "command",
		Body: "turn on lab led for 0.1 seconds",
	}
	input<- msg
	status := <-output
	if status.Locations["lab"].Output["led"].Value != true {
		t.Error("shoulda been on", status)
	}
	//wait for a second
	status = <-output
	if status.Locations["lab"].Output["led"].Value != false {
		t.Error("shoulda been off", status)
	}
}

func TestStartWithTimeTriggerForReals(t *testing.T) {
	if !utils.FileExists("/sys/class/gpio/export") {
		return //not a beaglebone
	}
	pin := pins.Pin{Type:"gpio", Port: "9", Pin: "15"}
	gpio, err := NewGPOutput(pin)
	location := "lab"
	name := "led"
	g := OutputGadget{
		Location: location,
		Name: name,
		OnCommand: "turn on lab led",
		OffCommand: "turn off lab led",
		Output: gpio,
		uid: fmt.Sprintf("%s %s", location, name),
	}
	input := make(chan gogadgets.Message)
	output := make(chan gogadgets.Message)
	go g.Start(input, output)
	msg := gogadgets.Message{
		Type: "command",
		Body: "turn on lab led for 0.1 seconds",
	}
	input<- msg
	status := <-output
	if status.Locations["lab"].Output["led"].Value != true {
		t.Error("shoulda been on", status)
	}
	//wait for a second
	status = <-output
	if status.Locations["lab"].Output["led"].Value != false {
		t.Error("shoulda been off", status)
	}
}
<commit_msg>fixed gpio output test<commit_after>package output

import (
	"fmt"
	"testing"
	"bitbucket.com/cswank/gogadgets/pins"
	"bitbucket.com/cswank/gogadgets/utils"
	"bitbucket.com/cswank/gogadgets/devices"
	"bitbucket.com/cswank/gogadgets"
)

type FakeOutput struct {
	devices.OutputDevice
	on bool
}

func (f *FakeOutput) On() error {
	f.on = true
	return nil
}

func (f *FakeOutput) Off() error {
	f.on = false
	return nil
}

func (f *FakeOutput) Status() bool {
	return f.on
}

func TestStart(t *testing.T) {
	location := "lab"
	name := "led"
	g := OutputGadget{
		Location: location,
		Name: name,
		OnCommand: fmt.Sprintf("turn on %s %s", location, name),
		OffCommand: fmt.Sprintf("turn off %s %s", location, name),
		Output: &FakeOutput{},
		uid: fmt.Sprintf("%s %s", location, name),
	}
	input := make(chan gogadgets.Message)
	output := make(chan gogadgets.Message)
	go g.Start(input, output)
	msg := gogadgets.Message{
		Type: "command",
		Body: "turn on lab led",
	}
	input<- msg
	status := <-output
	if status.Locations["lab"].Output["led"].Value != true {
		t.Error("shoulda been on", status)
	}

	msg = gogadgets.Message{
		Type: "command",
		Body: "shutdown",
	}
	input<- msg
	status = <-output
	if status.Locations["lab"].Output["led"].Value != false {
		t.Error("shoulda been off", status)
	}
}

func TestStartWithTrigger(t *testing.T) {
	location := "tank"
	name := "valve"
	g := OutputGadget{
		Location: location,
		Name: name,
		OnCommand: fmt.Sprintf("fill %s", location),
		OffCommand: fmt.Sprintf("stop filling %s", location),
		Output: &FakeOutput{},
		uid: fmt.Sprintf("%s %s", location, name),
	}
	input := make(chan gogadgets.Message)
	output := make(chan gogadgets.Message)
	go g.Start(input, output)
	msg := gogadgets.Message{
		Type: "command",
		Body: "fill tank to 4.4 liters",
	}
	input<- msg
	status := <-output
	if status.Locations["tank"].Output["valve"].Value != true {
		t.Error("shoulda been on", status)
	}

	//make a message that should trigger the trigger and stop the device
	l := gogadgets.Location{
		Input: map[string]gogadgets.Device{
			"volume": gogadgets.Device{
				Units: "liters",
				Value: 4.4,
			},
		},
	}
	msg = gogadgets.Message{
		Sender: "tank volume",
		Type: gogadgets.STATUS,
		Locations: map[string]gogadgets.Location{"tank": l},
	}
	input<- msg
	status = <-output
	if status.Locations["tank"].Output["valve"].Value != false {
		t.Error("shoulda been off", status)
	}
}

func TestStartWithTimeTrigger(t *testing.T) {
	location := "lab"
	name := "led"
	g := OutputGadget{
		Location: location,
		Name: name,
		OnCommand: "turn on lab led",
		OffCommand: "turn off lab led",
		Output: &FakeOutput{},
		uid: fmt.Sprintf("%s %s", location, name),
	}
	input := make(chan gogadgets.Message)
	output := make(chan gogadgets.Message)
	go g.Start(input, output)
	msg := gogadgets.Message{
		Type: "command",
		Body: "turn on lab led for 0.1 seconds",
	}
	input<- msg
	status := <-output
	if status.Locations["lab"].Output["led"].Value != true {
		t.Error("shoulda been on", status)
	}
	//wait for a second
	status = <-output
	if status.Locations["lab"].Output["led"].Value != false {
		t.Error("shoulda been off", status)
	}
}

func TestStartWithTimeTriggerForReals(t *testing.T) {
	if !utils.FileExists("/sys/class/gpio/export") {
		return //not a beaglebone
	}
	pin := &pins.Pin{Type:"gpio", Port: "9", Pin: "15"}
	gpio, err := devices.NewGPOutput(pin)
	if err != nil {
		t.Fatal(err)
	}
	location := "lab"
	name := "led"
	g := OutputGadget{
		Location: location,
		Name: name,
		OnCommand: "turn on lab led",
		OffCommand: "turn off lab led",
		Output: gpio,
		uid: fmt.Sprintf("%s %s", location, name),
	}
	input := make(chan gogadgets.Message)
	output := make(chan gogadgets.Message)
	go g.Start(input, output)
	msg := gogadgets.Message{
		Type: "command",
		Body: "turn on lab led for 0.1 seconds",
	}
	input<- msg
	status := <-output
	if status.Locations["lab"].Output["led"].Value != true {
		t.Error("shoulda been on", status)
	}
	//wait for a second
	status = <-output
	if status.Locations["lab"].Output["led"].Value != false {
		t.Error("shoulda been off", status)
	}
}
<|endoftext|>