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

package firestorm

import (
	"encoding/json"
	"fmt"
	"sync"
	"sync/atomic"
	"time"

	"github.com/blevesearch/bleve/document"
	"github.com/blevesearch/bleve/index"
	"github.com/blevesearch/bleve/index/store"
	"github.com/blevesearch/bleve/registry"
)

const Name = "firestorm"

var UnsafeBatchUseDetected = fmt.Errorf("bleve.Batch is NOT thread-safe, modification after execution detected")

type Firestorm struct {
	storeName        string
	storeConfig      map[string]interface{}
	store            store.KVStore
	compensator      *Compensator
	analysisQueue    *index.AnalysisQueue
	fieldCache       *index.FieldCache
	highDocNumber    uint64
	docCount         *uint64
	garbageCollector *GarbageCollector
	lookuper         *Lookuper
	dictUpdater      *DictUpdater
	stats            *indexStat
}

func NewFirestorm(storeName string, storeConfig map[string]interface{}, analysisQueue *index.AnalysisQueue) (index.Index, error) {
	initialCount := uint64(0)
	rv := Firestorm{
		storeName:     storeName,
		storeConfig:   storeConfig,
		compensator:   NewCompensator(),
		analysisQueue: analysisQueue,
		fieldCache:    index.NewFieldCache(),
		docCount:      &initialCount,
		highDocNumber: 0,
		stats:         &indexStat{},
	}
	rv.stats.f = &rv
	rv.garbageCollector = NewGarbageCollector(&rv)
	rv.lookuper = NewLookuper(&rv)
	rv.dictUpdater = NewDictUpdater(&rv)
	return &rv, nil
}

func (f *Firestorm) Open() (err error) {

	// open the kv store
	storeConstructor := registry.KVStoreConstructorByName(f.storeName)
	if storeConstructor == nil {
		err = index.ErrorUnknownStorageType
		return
	}

	// now open the store
	f.store, err = storeConstructor(&mergeOperator, f.storeConfig)
	if err != nil {
		return
	}

	// start a reader
	var kvreader store.KVReader
	kvreader, err = f.store.Reader()
	if err != nil {
		return
	}

	// assert correct version, and find out if this is new index
	var newIndex bool
	newIndex, err = f.checkVersion(kvreader)
	if err != nil {
		return
	}

	if !newIndex {
		// process existing index before opening
		err = f.warmup(kvreader)
		if err != nil {
			return
		}
	}

	err = kvreader.Close()
	if err != nil {
		return
	}

	if newIndex {
		// prepare a new index
		err = f.bootstrap()
		if err != nil {
			return
		}
	}

	// start the garbage collector
	f.garbageCollector.Start()

	// start the lookuper
	f.lookuper.Start()

	// start the dict updater
	f.dictUpdater.Start()

	return
}

func (f *Firestorm) Close() error {
	f.garbageCollector.Stop()
	f.lookuper.Stop()
	f.dictUpdater.Stop()
	return f.store.Close()
}

func (f *Firestorm) DocCount() (uint64, error) {
	count := atomic.LoadUint64(f.docCount)
	return count, nil

}

func (f *Firestorm) Update(doc *document.Document) (err error) {

	// assign this document a number
	doc.Number = atomic.AddUint64(&f.highDocNumber, 1)

	// do analysis before acquiring write lock
	analysisStart := time.Now()
	resultChan := make(chan *index.AnalysisResult)
	aw := index.NewAnalysisWork(f, doc, resultChan)

	// put the work on the queue
	go f.analysisQueue.Queue(aw)

	// wait for the result
	result := <-resultChan
	close(resultChan)
	atomic.AddUint64(&f.stats.analysisTime, uint64(time.Since(analysisStart)))

	// start a writer for this update
	indexStart := time.Now()
	var kvwriter store.KVWriter
	kvwriter, err = f.store.Writer()
	if err != nil {
		return
	}
	defer func() {
		if cerr := kvwriter.Close(); err == nil && cerr != nil {
			err = cerr
		}
	}()

	var dictionaryDeltas map[string]int64
	dictionaryDeltas, err = f.batchRows(kvwriter, result.Rows, nil)
	if err != nil {
		_ = kvwriter.Close()
		atomic.AddUint64(&f.stats.errors, 1)
		return
	}

	f.compensator.Mutate([]byte(doc.ID), doc.Number)
	f.lookuper.Notify(doc.Number, []byte(doc.ID))
	f.dictUpdater.NotifyBatch(dictionaryDeltas)

	atomic.AddUint64(&f.stats.indexTime, uint64(time.Since(indexStart)))
	return
}

func (f *Firestorm) Delete(id string) error {
	indexStart := time.Now()
	f.compensator.Mutate([]byte(id), 0)
	f.lookuper.Notify(0, []byte(id))
	atomic.AddUint64(&f.stats.indexTime, uint64(time.Since(indexStart)))
	return nil
}

func (f *Firestorm) batchRows(writer store.KVWriter, rows []index.IndexRow, deleteKeys [][]byte) (map[string]int64, error) {

	// prepare batch
	wb := writer.NewBatch()

	var kbuf []byte
	var vbuf []byte

	prepareBuf := func(buf []byte, sizeNeeded int) []byte {
		if cap(buf) < sizeNeeded {
			return make([]byte, sizeNeeded, sizeNeeded+128)
		}
		return buf[0:sizeNeeded]
	}

	dictionaryDeltas := make(map[string]int64)
	for _, row := range rows {
		tfr, ok := row.(*TermFreqRow)
		if ok {
			if tfr.Field() != 0 {
				kbuf = prepareBuf(kbuf, tfr.DictionaryRowKeySize())
				klen, err := tfr.DictionaryRowKeyTo(kbuf)
				if err != nil {
					return nil, err
				}

				dictionaryDeltas[string(kbuf[0:klen])] += 1
			}
		}

		kbuf = prepareBuf(kbuf, row.KeySize())
		klen, err := row.KeyTo(kbuf)
		if err != nil {
			return nil, err
		}

		vbuf = prepareBuf(vbuf, row.ValueSize())
		vlen, err := row.ValueTo(vbuf)
		if err != nil {
			return nil, err
		}

		wb.Set(kbuf[0:klen], vbuf[0:vlen])
	}

	for _, dk := range deleteKeys {
		wb.Delete(dk)
	}

	// write out the batch
	err := writer.ExecuteBatch(wb)
	if err != nil {
		return nil, err
	}
	return dictionaryDeltas, nil
}

func (f *Firestorm) Batch(batch *index.Batch) (err error) {

	// acquire enough doc numbers for all updates in the batch
	// FIXME we actually waste doc numbers because deletes are in the
	// same map and we don't need numbers for them
	lastDocNumber := atomic.AddUint64(&f.highDocNumber, uint64(len(batch.IndexOps)))
	firstDocNumber := lastDocNumber - uint64(len(batch.IndexOps)) + 1

	analysisStart := time.Now()
	resultChan := make(chan *index.AnalysisResult)

	var numUpdates uint64
	var docsDeleted uint64
	for _, doc := range batch.IndexOps {
		if doc != nil {
			doc.Number = firstDocNumber // actually assign doc numbers here
			firstDocNumber++
			numUpdates++
		} else {
			docsDeleted++
		}
	}

	var detectedUnsafeMutex sync.RWMutex
	detectedUnsafe := false

	go func() {
		sofar := uint64(0)
		for _, doc := range batch.IndexOps {
			if doc != nil {
				sofar++
				if sofar > numUpdates {
					detectedUnsafeMutex.Lock()
					detectedUnsafe = true
					detectedUnsafeMutex.Unlock()
					return
				}
				aw := index.NewAnalysisWork(f, doc, resultChan)
				// put the work on the queue
				f.analysisQueue.Queue(aw)
			}
		}
	}()

	allRows := make([]index.IndexRow, 0, 1000)
	// wait for the result
	var itemsDeQueued uint64
	for itemsDeQueued < numUpdates {
		result := <-resultChan
		allRows = append(allRows, result.Rows...)
		itemsDeQueued++
	}
	close(resultChan)

	detectedUnsafeMutex.RLock()
	defer detectedUnsafeMutex.RUnlock()
	if detectedUnsafe {
		return UnsafeBatchUseDetected
	}

	atomic.AddUint64(&f.stats.analysisTime, uint64(time.Since(analysisStart)))

	deleteKeys := make([][]byte, 0)
	// add the internal ops
	for internalKey, internalValue := range batch.InternalOps {
		if internalValue == nil {
			// delete
			deleteInternalRow := NewInternalRow([]byte(internalKey), nil)
			deleteKeys = append(deleteKeys, deleteInternalRow.Key())
		} else {
			updateInternalRow := NewInternalRow([]byte(internalKey), internalValue)
			allRows = append(allRows, updateInternalRow)
		}
	}

	indexStart := time.Now()
	// start a writer for this batch
	var kvwriter store.KVWriter
	kvwriter, err = f.store.Writer()
	if err != nil {
		return
	}

	var dictionaryDeltas map[string]int64
	dictionaryDeltas, err = f.batchRows(kvwriter, allRows, deleteKeys)
	if err != nil {
		_ = kvwriter.Close()
		atomic.AddUint64(&f.stats.errors, 1)
		return
	}

	f.compensator.MutateBatch(batch.IndexOps, lastDocNumber)
	for docID, doc := range batch.IndexOps {
		if doc != nil {
			f.lookuper.Notify(doc.Number, []byte(doc.ID))
		} else {
			f.lookuper.Notify(0, []byte(docID))
		}
	}
	f.dictUpdater.NotifyBatch(dictionaryDeltas)

	err = kvwriter.Close()
	atomic.AddUint64(&f.stats.indexTime, uint64(time.Since(indexStart)))

	if err == nil {
		atomic.AddUint64(&f.stats.updates, numUpdates)
		atomic.AddUint64(&f.stats.deletes, docsDeleted)
		atomic.AddUint64(&f.stats.batches, 1)
	} else {
		atomic.AddUint64(&f.stats.errors, 1)
	}

	return
}

func (f *Firestorm) SetInternal(key, val []byte) (err error) {
	internalRow := NewInternalRow(key, val)
	var writer store.KVWriter
	writer, err = f.store.Writer()
	if err != nil {
		return
	}
	defer func() {
		if cerr := writer.Close(); err == nil && cerr != nil {
			err = cerr
		}
	}()

	wb := writer.NewBatch()
	wb.Set(internalRow.Key(), internalRow.Value())

	return writer.ExecuteBatch(wb)
}

func (f *Firestorm) DeleteInternal(key []byte) (err error) {
	internalRow := NewInternalRow(key, nil)
	var writer store.KVWriter
	writer, err = f.store.Writer()
	if err != nil {
		return
	}
	defer func() {
		if cerr := writer.Close(); err == nil && cerr != nil {
			err = cerr
		}
	}()

	wb := writer.NewBatch()
	wb.Delete(internalRow.Key())

	return writer.ExecuteBatch(wb)
}

func (f *Firestorm) DumpAll() chan interface{} {
	rv := make(chan interface{})
	go func() {
		defer close(rv)

		// start an isolated reader for use during the dump
		kvreader, err := f.store.Reader()
		if err != nil {
			rv <- err
			return
		}
		defer func() {
			cerr := kvreader.Close()
			if cerr != nil {
				rv <- cerr
			}
		}()

		err = f.dumpPrefix(kvreader, rv, nil)
		if err != nil {
			rv <- err
			return
		}
	}()
	return rv
}

func (f *Firestorm) DumpDoc(docID string) chan interface{} {
	rv := make(chan interface{})
	go func() {
		defer close(rv)

		// start an isolated reader for use during the dump
		kvreader, err := f.store.Reader()
		if err != nil {
			rv <- err
			return
		}
		defer func() {
			cerr := kvreader.Close()
			if cerr != nil {
				rv <- cerr
			}
		}()

		err = f.dumpDoc(kvreader, rv, []byte(docID))
		if err != nil {
			rv <- err
			return
		}
	}()
	return rv
}

func (f *Firestorm) DumpFields() chan interface{} {
	rv := make(chan interface{})
	go func() {
		defer close(rv)

		// start an isolated reader for use during the dump
		kvreader, err := f.store.Reader()
		if err != nil {
			rv <- err
			return
		}
		defer func() {
			cerr := kvreader.Close()
			if cerr != nil {
				rv <- cerr
			}
		}()

		err = f.dumpPrefix(kvreader, rv, FieldKeyPrefix)
		if err != nil {
			rv <- err
			return
		}
	}()
	return rv
}

func (f *Firestorm) Reader() (index.IndexReader, error) {
	return newFirestormReader(f)
}

func (f *Firestorm) Stats() json.Marshaler {
	return f.stats

}

func (f *Firestorm) Wait(timeout time.Duration) error {
	return f.dictUpdater.waitTasksDone(timeout)
}

func init() {
	registry.RegisterIndexType(Name, NewFirestorm)
}
<commit_msg>renamed var to docsUpdated to match docsDeleted naming<commit_after>//  Copyright (c) 2015 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
//  except in compliance with the License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing, software distributed under the
//  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
//  either express or implied. See the License for the specific language governing permissions
//  and limitations under the License.

package firestorm

import (
	"encoding/json"
	"fmt"
	"sync"
	"sync/atomic"
	"time"

	"github.com/blevesearch/bleve/document"
	"github.com/blevesearch/bleve/index"
	"github.com/blevesearch/bleve/index/store"
	"github.com/blevesearch/bleve/registry"
)

const Name = "firestorm"

var UnsafeBatchUseDetected = fmt.Errorf("bleve.Batch is NOT thread-safe, modification after execution detected")

type Firestorm struct {
	storeName        string
	storeConfig      map[string]interface{}
	store            store.KVStore
	compensator      *Compensator
	analysisQueue    *index.AnalysisQueue
	fieldCache       *index.FieldCache
	highDocNumber    uint64
	docCount         *uint64
	garbageCollector *GarbageCollector
	lookuper         *Lookuper
	dictUpdater      *DictUpdater
	stats            *indexStat
}

func NewFirestorm(storeName string, storeConfig map[string]interface{}, analysisQueue *index.AnalysisQueue) (index.Index, error) {
	initialCount := uint64(0)
	rv := Firestorm{
		storeName:     storeName,
		storeConfig:   storeConfig,
		compensator:   NewCompensator(),
		analysisQueue: analysisQueue,
		fieldCache:    index.NewFieldCache(),
		docCount:      &initialCount,
		highDocNumber: 0,
		stats:         &indexStat{},
	}
	rv.stats.f = &rv
	rv.garbageCollector = NewGarbageCollector(&rv)
	rv.lookuper = NewLookuper(&rv)
	rv.dictUpdater = NewDictUpdater(&rv)
	return &rv, nil
}

func (f *Firestorm) Open() (err error) {

	// open the kv store
	storeConstructor := registry.KVStoreConstructorByName(f.storeName)
	if storeConstructor == nil {
		err = index.ErrorUnknownStorageType
		return
	}

	// now open the store
	f.store, err = storeConstructor(&mergeOperator, f.storeConfig)
	if err != nil {
		return
	}

	// start a reader
	var kvreader store.KVReader
	kvreader, err = f.store.Reader()
	if err != nil {
		return
	}

	// assert correct version, and find out if this is new index
	var newIndex bool
	newIndex, err = f.checkVersion(kvreader)
	if err != nil {
		return
	}

	if !newIndex {
		// process existing index before opening
		err = f.warmup(kvreader)
		if err != nil {
			return
		}
	}

	err = kvreader.Close()
	if err != nil {
		return
	}

	if newIndex {
		// prepare a new index
		err = f.bootstrap()
		if err != nil {
			return
		}
	}

	// start the garbage collector
	f.garbageCollector.Start()

	// start the lookuper
	f.lookuper.Start()

	// start the dict updater
	f.dictUpdater.Start()

	return
}

func (f *Firestorm) Close() error {
	f.garbageCollector.Stop()
	f.lookuper.Stop()
	f.dictUpdater.Stop()
	return f.store.Close()
}

func (f *Firestorm) DocCount() (uint64, error) {
	count := atomic.LoadUint64(f.docCount)
	return count, nil

}

func (f *Firestorm) Update(doc *document.Document) (err error) {

	// assign this document a number
	doc.Number = atomic.AddUint64(&f.highDocNumber, 1)

	// do analysis before acquiring write lock
	analysisStart := time.Now()
	resultChan := make(chan *index.AnalysisResult)
	aw := index.NewAnalysisWork(f, doc, resultChan)

	// put the work on the queue
	go f.analysisQueue.Queue(aw)

	// wait for the result
	result := <-resultChan
	close(resultChan)
	atomic.AddUint64(&f.stats.analysisTime, uint64(time.Since(analysisStart)))

	// start a writer for this update
	indexStart := time.Now()
	var kvwriter store.KVWriter
	kvwriter, err = f.store.Writer()
	if err != nil {
		return
	}
	defer func() {
		if cerr := kvwriter.Close(); err == nil && cerr != nil {
			err = cerr
		}
	}()

	var dictionaryDeltas map[string]int64
	dictionaryDeltas, err = f.batchRows(kvwriter, result.Rows, nil)
	if err != nil {
		_ = kvwriter.Close()
		atomic.AddUint64(&f.stats.errors, 1)
		return
	}

	f.compensator.Mutate([]byte(doc.ID), doc.Number)
	f.lookuper.Notify(doc.Number, []byte(doc.ID))
	f.dictUpdater.NotifyBatch(dictionaryDeltas)

	atomic.AddUint64(&f.stats.indexTime, uint64(time.Since(indexStart)))
	return
}

func (f *Firestorm) Delete(id string) error {
	indexStart := time.Now()
	f.compensator.Mutate([]byte(id), 0)
	f.lookuper.Notify(0, []byte(id))
	atomic.AddUint64(&f.stats.indexTime, uint64(time.Since(indexStart)))
	return nil
}

func (f *Firestorm) batchRows(writer store.KVWriter, rows []index.IndexRow, deleteKeys [][]byte) (map[string]int64, error) {

	// prepare batch
	wb := writer.NewBatch()

	var kbuf []byte
	var vbuf []byte

	prepareBuf := func(buf []byte, sizeNeeded int) []byte {
		if cap(buf) < sizeNeeded {
			return make([]byte, sizeNeeded, sizeNeeded+128)
		}
		return buf[0:sizeNeeded]
	}

	dictionaryDeltas := make(map[string]int64)
	for _, row := range rows {
		tfr, ok := row.(*TermFreqRow)
		if ok {
			if tfr.Field() != 0 {
				kbuf = prepareBuf(kbuf, tfr.DictionaryRowKeySize())
				klen, err := tfr.DictionaryRowKeyTo(kbuf)
				if err != nil {
					return nil, err
				}

				dictionaryDeltas[string(kbuf[0:klen])] += 1
			}
		}

		kbuf = prepareBuf(kbuf, row.KeySize())
		klen, err := row.KeyTo(kbuf)
		if err != nil {
			return nil, err
		}

		vbuf = prepareBuf(vbuf, row.ValueSize())
		vlen, err := row.ValueTo(vbuf)
		if err != nil {
			return nil, err
		}

		wb.Set(kbuf[0:klen], vbuf[0:vlen])
	}

	for _, dk := range deleteKeys {
		wb.Delete(dk)
	}

	// write out the batch
	err := writer.ExecuteBatch(wb)
	if err != nil {
		return nil, err
	}
	return dictionaryDeltas, nil
}

func (f *Firestorm) Batch(batch *index.Batch) (err error) {

	// acquire enough doc numbers for all updates in the batch
	// FIXME we actually waste doc numbers because deletes are in the
	// same map and we don't need numbers for them
	lastDocNumber := atomic.AddUint64(&f.highDocNumber, uint64(len(batch.IndexOps)))
	firstDocNumber := lastDocNumber - uint64(len(batch.IndexOps)) + 1

	analysisStart := time.Now()
	resultChan := make(chan *index.AnalysisResult)

	var docsUpdated uint64
	var docsDeleted uint64
	for _, doc := range batch.IndexOps {
		if doc != nil {
			doc.Number = firstDocNumber // actually assign doc numbers here
			firstDocNumber++
			docsUpdated++
		} else {
			docsDeleted++
		}
	}

	var detectedUnsafeMutex sync.RWMutex
	detectedUnsafe := false

	go func() {
		sofar := uint64(0)
		for _, doc := range batch.IndexOps {
			if doc != nil {
				sofar++
				if sofar > docsUpdated {
					detectedUnsafeMutex.Lock()
					detectedUnsafe = true
					detectedUnsafeMutex.Unlock()
					return
				}
				aw := index.NewAnalysisWork(f, doc, resultChan)
				// put the work on the queue
				f.analysisQueue.Queue(aw)
			}
		}
	}()

	allRows := make([]index.IndexRow, 0, 1000)
	// wait for the result
	var itemsDeQueued uint64
	for itemsDeQueued < docsUpdated {
		result := <-resultChan
		allRows = append(allRows, result.Rows...)
		itemsDeQueued++
	}
	close(resultChan)

	detectedUnsafeMutex.RLock()
	defer detectedUnsafeMutex.RUnlock()
	if detectedUnsafe {
		return UnsafeBatchUseDetected
	}

	atomic.AddUint64(&f.stats.analysisTime, uint64(time.Since(analysisStart)))

	deleteKeys := make([][]byte, 0)
	// add the internal ops
	for internalKey, internalValue := range batch.InternalOps {
		if internalValue == nil {
			// delete
			deleteInternalRow := NewInternalRow([]byte(internalKey), nil)
			deleteKeys = append(deleteKeys, deleteInternalRow.Key())
		} else {
			updateInternalRow := NewInternalRow([]byte(internalKey), internalValue)
			allRows = append(allRows, updateInternalRow)
		}
	}

	indexStart := time.Now()
	// start a writer for this batch
	var kvwriter store.KVWriter
	kvwriter, err = f.store.Writer()
	if err != nil {
		return
	}

	var dictionaryDeltas map[string]int64
	dictionaryDeltas, err = f.batchRows(kvwriter, allRows, deleteKeys)
	if err != nil {
		_ = kvwriter.Close()
		atomic.AddUint64(&f.stats.errors, 1)
		return
	}

	f.compensator.MutateBatch(batch.IndexOps, lastDocNumber)
	for docID, doc := range batch.IndexOps {
		if doc != nil {
			f.lookuper.Notify(doc.Number, []byte(doc.ID))
		} else {
			f.lookuper.Notify(0, []byte(docID))
		}
	}
	f.dictUpdater.NotifyBatch(dictionaryDeltas)

	err = kvwriter.Close()
	atomic.AddUint64(&f.stats.indexTime, uint64(time.Since(indexStart)))

	if err == nil {
		atomic.AddUint64(&f.stats.updates, docsUpdated)
		atomic.AddUint64(&f.stats.deletes, docsDeleted)
		atomic.AddUint64(&f.stats.batches, 1)
	} else {
		atomic.AddUint64(&f.stats.errors, 1)
	}

	return
}

func (f *Firestorm) SetInternal(key, val []byte) (err error) {
	internalRow := NewInternalRow(key, val)
	var writer store.KVWriter
	writer, err = f.store.Writer()
	if err != nil {
		return
	}
	defer func() {
		if cerr := writer.Close(); err == nil && cerr != nil {
			err = cerr
		}
	}()

	wb := writer.NewBatch()
	wb.Set(internalRow.Key(), internalRow.Value())

	return writer.ExecuteBatch(wb)
}

func (f *Firestorm) DeleteInternal(key []byte) (err error) {
	internalRow := NewInternalRow(key, nil)
	var writer store.KVWriter
	writer, err = f.store.Writer()
	if err != nil {
		return
	}
	defer func() {
		if cerr := writer.Close(); err == nil && cerr != nil {
			err = cerr
		}
	}()

	wb := writer.NewBatch()
	wb.Delete(internalRow.Key())

	return writer.ExecuteBatch(wb)
}

func (f *Firestorm) DumpAll() chan interface{} {
	rv := make(chan interface{})
	go func() {
		defer close(rv)

		// start an isolated reader for use during the dump
		kvreader, err := f.store.Reader()
		if err != nil {
			rv <- err
			return
		}
		defer func() {
			cerr := kvreader.Close()
			if cerr != nil {
				rv <- cerr
			}
		}()

		err = f.dumpPrefix(kvreader, rv, nil)
		if err != nil {
			rv <- err
			return
		}
	}()
	return rv
}

func (f *Firestorm) DumpDoc(docID string) chan interface{} {
	rv := make(chan interface{})
	go func() {
		defer close(rv)

		// start an isolated reader for use during the dump
		kvreader, err := f.store.Reader()
		if err != nil {
			rv <- err
			return
		}
		defer func() {
			cerr := kvreader.Close()
			if cerr != nil {
				rv <- cerr
			}
		}()

		err = f.dumpDoc(kvreader, rv, []byte(docID))
		if err != nil {
			rv <- err
			return
		}
	}()
	return rv
}

func (f *Firestorm) DumpFields() chan interface{} {
	rv := make(chan interface{})
	go func() {
		defer close(rv)

		// start an isolated reader for use during the dump
		kvreader, err := f.store.Reader()
		if err != nil {
			rv <- err
			return
		}
		defer func() {
			cerr := kvreader.Close()
			if cerr != nil {
				rv <- cerr
			}
		}()

		err = f.dumpPrefix(kvreader, rv, FieldKeyPrefix)
		if err != nil {
			rv <- err
			return
		}
	}()
	return rv
}

func (f *Firestorm) Reader() (index.IndexReader, error) {
	return newFirestormReader(f)
}

func (f *Firestorm) Stats() json.Marshaler {
	return f.stats

}

func (f *Firestorm) Wait(timeout time.Duration) error {
	return f.dictUpdater.waitTasksDone(timeout)
}

func init() {
	registry.RegisterIndexType(Name, NewFirestorm)
}
<|endoftext|>


<commit_before>package utils

import (
	"testing"

	"gopkg.in/yaml.v2"

	envmanModels "github.com/bitrise-io/envman/models"
	stepmanModels "github.com/bitrise-io/stepman/models"
)

func TestConvertStepsInputs(t *testing.T) {
	originalInputsStr := `
envs:
- KEY1: value1
  opts:
    title: title
    description: description
    summary: summary
    value_options:
    - option1
    is_required: false
    is_expand: false
    is_dont_change_value: false
- KEY3: value3
- KEY4: value4
`

	diffInputsStr := `
envs:
- KEY2:
  opts:
    title: title2
    description: description2
    summary: summary2
    value_options:
    - option3
    is_required: true
    is_expand: true
    is_dont_change_value: true
`

	conversionMap := map[string]string{
		"KEY1": "KEY2",
	}

	originalEnvs := envmanModels.EnvsYMLModel{}
	if err := yaml.Unmarshal([]byte(originalInputsStr), &originalEnvs); err != nil {
		t.Fatal("Failed to yaml.Unmarshal, err:", err)
	}

	diffEnvs := envmanModels.EnvsYMLModel{}
	if err := yaml.Unmarshal([]byte(diffInputsStr), &diffEnvs); err != nil {
		t.Fatal("Failed to yaml.Unmarshal, err:", err)
	}

	convertedEnvs, err := convertStepsInputs(originalEnvs.Envs, diffEnvs.Envs, conversionMap)
	if err != nil {
		t.Fatal("Failed to convert envs, err:", err)
	}

	// Test length
	if len(convertedEnvs) != 3 {
		t.Fatal("Failed to convert envs")
	}

	convertedEnv := convertedEnvs[0]

	// Test key-value conversion
	key, value, err := convertedEnv.GetKeyValuePair()
	if err != nil {
		t.Fatal("Failed to get env key-value pair, err:", err)
	}

	if key != "KEY1" {
		t.Fatal("Failed to convert env key")
	}
	if value != "value1" {
		t.Fatal("Failed to convert env value")
	}

	// Test options conversion
	convertedOptions, err := convertedEnv.GetOptions()
	if err != nil {
		t.Fatal("Failed to get env options, err:", err)
	}

	if *convertedOptions.Title != "title" {
		t.Fatal("Failed to convert Title")
	}
	if *convertedOptions.Description != "description" {
		t.Fatal("Failed to convert Description")
	}
	if *convertedOptions.Summary != "summary" {
		t.Fatal("Failed to convert Summary")
	}
	if convertedOptions.ValueOptions[0] != "option1" {
		t.Fatal("Failed to convert ValueOptions")
	}
	if *convertedOptions.IsRequired != false {
		t.Fatal("Failed to convert IsRequired")
	}
	if *convertedOptions.IsExpand != true {
		t.Fatal("Failed to convert IsExpand")
	}
	if *convertedOptions.IsDontChangeValue != false {
		t.Fatal("Failed to convert IsDontChangeValue")
	}
}

func TestConvertStep(t *testing.T) {
	diffStepStr := `
title: title
is_always_run: true
`

	diffStep := stepmanModels.StepModel{}
	if err := yaml.Unmarshal([]byte(diffStepStr), &diffStep); err != nil {
		t.Fatal("Failed to yaml.Unmarshal, err:", err)
	}

	convertedStep, err := ConvertStep(diffStep, "script", map[string]string{})
	if err != nil {
		t.Fatal("Failed to convert step, err:", err)
	}

	if *convertedStep.Title != "title" {
		t.Fatal("Failed to convert Title")
	}
	if *convertedStep.IsAlwaysRun != true {
		t.Fatal("Failed to convert IsAlwaysRun")
	}
}
<commit_msg>PR fix<commit_after>package utils

import (
	"testing"

	"gopkg.in/yaml.v2"

	envmanModels "github.com/bitrise-io/envman/models"
	stepmanModels "github.com/bitrise-io/stepman/models"
)

func TestConvertStepsInputs(t *testing.T) {
	originalInputsStr := `
envs:
- KEY1: value1
  opts:
    title: title
    description: description
    summary: summary
    value_options:
    - option1
    is_required: false
    is_expand: false
    is_dont_change_value: false
- KEY3: value3
- KEY4: value4
`

	diffInputsStr := `
envs:
- KEY2:
  opts:
    title: title2
    description: description2
    summary: summary2
    value_options:
    - option3
    is_required: true
    is_expand: true
    is_dont_change_value: true
`

	conversionMap := map[string]string{
		"KEY1": "KEY2",
	}

	originalEnvs := envmanModels.EnvsYMLModel{}
	if err := yaml.Unmarshal([]byte(originalInputsStr), &originalEnvs); err != nil {
		t.Fatal("Failed to yaml.Unmarshal, err:", err)
	}

	diffEnvs := envmanModels.EnvsYMLModel{}
	if err := yaml.Unmarshal([]byte(diffInputsStr), &diffEnvs); err != nil {
		t.Fatal("Failed to yaml.Unmarshal, err:", err)
	}

	convertedEnvs, err := convertStepsInputs(originalEnvs.Envs, diffEnvs.Envs, conversionMap)
	if err != nil {
		t.Fatal("Failed to convert envs, err:", err)
	}

	// Test length
	if len(convertedEnvs) != 3 {
		t.Fatal("Failed to convert envs")
	}

	convertedEnv := convertedEnvs[0]

	// Test key-value conversion
	key, value, err := convertedEnv.GetKeyValuePair()
	if err != nil {
		t.Fatal("Failed to get env key-value pair, err:", err)
	}

	if key != "KEY1" {
		t.Fatal("Failed to convert env key")
	}
	if value != "value1" {
		t.Fatal("Failed to convert env value")
	}

	// Test options conversion
	convertedOptions, err := convertedEnv.GetOptions()
	if err != nil {
		t.Fatal("Failed to get env options, err:", err)
	}

	if *convertedOptions.Title != "title" {
		t.Fatal("Failed to convert Title")
	}
	if *convertedOptions.Description != "description" {
		t.Fatal("Failed to convert Description")
	}
	if *convertedOptions.Summary != "summary" {
		t.Fatal("Failed to convert Summary")
	}
	if convertedOptions.ValueOptions[0] != "option1" {
		t.Fatal("Failed to convert ValueOptions")
	}
	if *convertedOptions.IsRequired != false {
		t.Fatal("Failed to convert IsRequired")
	}
	if *convertedOptions.IsExpand != true {
		t.Fatal("Failed to convert IsExpand")
	}
	if *convertedOptions.IsDontChangeValue != false {
		t.Fatal("Failed to convert IsDontChangeValue")
	}
}

func TestConvertStep(t *testing.T) {
	diffStepStr := `
title: title
is_always_run: true
`

	diffStep := stepmanModels.StepModel{}
	if err := yaml.Unmarshal([]byte(diffStepStr), &diffStep); err != nil {
		t.Fatal("Failed to yaml.Unmarshal, err:", err)
	}

	convertedStep, _, err := ConvertStep(diffStep, "script", map[string]string{})
	if err != nil {
		t.Fatal("Failed to convert step, err:", err)
	}

	if *convertedStep.Title != "title" {
		t.Fatal("Failed to convert Title")
	}
	if *convertedStep.IsAlwaysRun != true {
		t.Fatal("Failed to convert IsAlwaysRun")
	}
}
<|endoftext|>


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

package github

import (
	"encoding/json"
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func TestAuthorizationsService_List(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/authorizations", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testFormValues(t, r, values{"page": "1", "per_page": "2"})
		fmt.Fprint(w, `[{"id":1}]`)
	})

	opt := &ListOptions{Page: 1, PerPage: 2}
	got, _, err := client.Authorizations.List(opt)
	if err != nil {
		t.Errorf("Authorizations.List returned error: %v", err)
	}

	want := []*Authorization{{ID: Int(1)}}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Authorizations.List returned %+v, want %+v", *got[0].ID, *want[0].ID)
	}
}

func TestAuthorizationsService_Get(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `{"id":1}`)
	})

	got, _, err := client.Authorizations.Get(1)
	if err != nil {
		t.Errorf("Authorizations.Get returned error: %v", err)
	}

	want := &Authorization{ID: Int(1)}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Authorizations.Get returned auth %+v, want %+v", got, want)
	}
}

func TestAuthorizationsService_Create(t *testing.T) {
	setup()
	defer teardown()

	input := &AuthorizationRequest{
		Note: String("test"),
	}

	mux.HandleFunc("/authorizations", func(w http.ResponseWriter, r *http.Request) {
		v := new(AuthorizationRequest)
		json.NewDecoder(r.Body).Decode(v)

		testMethod(t, r, "POST")
		if !reflect.DeepEqual(v, input) {
			t.Errorf("Request body = %+v, want %+v", v, input)
		}

		fmt.Fprint(w, `{"ID":1}`)
	})

	got, _, err := client.Authorizations.Create(input)
	if err != nil {
		t.Errorf("Authorizations.Create returned error: %v", err)
	}

	want := &Authorization{ID: Int(1)}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Authorization.Create returned %+v, want %+v", got, want)
	}
}

func TestAuthorizationsService_GetOrCreateForApp(t *testing.T) {
	setup()
	defer teardown()

	input := &AuthorizationRequest{
		Note: String("test"),
	}

	mux.HandleFunc("/authorizations/clients/id", func(w http.ResponseWriter, r *http.Request) {
		v := new(AuthorizationRequest)
		json.NewDecoder(r.Body).Decode(v)

		testMethod(t, r, "PUT")
		if !reflect.DeepEqual(v, input) {
			t.Errorf("Request body = %+v, want %+v", v, input)
		}

		fmt.Fprint(w, `{"ID":1}`)
	})

	got, _, err := client.Authorizations.GetOrCreateForApp("id", input)
	if err != nil {
		t.Errorf("Authorizations.GetOrCreateForApp returned error: %v", err)
	}

	want := &Authorization{ID: Int(1)}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Authorization.GetOrCreateForApp returned %+v, want %+v", got, want)
	}
}

func TestAuthorizationsService_GetOrCreateForApp_Fingerprint(t *testing.T) {
	setup()
	defer teardown()

	input := &AuthorizationRequest{
		Note:        String("test"),
		Fingerprint: String("fp"),
	}

	mux.HandleFunc("/authorizations/clients/id/fp", func(w http.ResponseWriter, r *http.Request) {
		v := new(AuthorizationRequest)
		json.NewDecoder(r.Body).Decode(v)

		testMethod(t, r, "PUT")
		if !reflect.DeepEqual(v, input) {
			t.Errorf("Request body = %+v, want %+v", v, input)
		}

		fmt.Fprint(w, `{"ID":1}`)
	})

	got, _, err := client.Authorizations.GetOrCreateForApp("id", input)
	if err != nil {
		t.Errorf("Authorizations.GetOrCreateForApp returned error: %v", err)
	}

	want := &Authorization{ID: Int(1)}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Authorization.GetOrCreateForApp returned %+v, want %+v", got, want)
	}
}

func TestAuthorizationsService_Edit(t *testing.T) {
	setup()
	defer teardown()

	input := &AuthorizationUpdateRequest{
		Note: String("test"),
	}

	mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) {
		v := new(AuthorizationUpdateRequest)
		json.NewDecoder(r.Body).Decode(v)

		testMethod(t, r, "PATCH")
		if !reflect.DeepEqual(v, input) {
			t.Errorf("Request body = %+v, want %+v", v, input)
		}

		fmt.Fprint(w, `{"ID":1}`)
	})

	got, _, err := client.Authorizations.Edit(1, input)
	if err != nil {
		t.Errorf("Authorizations.Edit returned error: %v", err)
	}

	want := &Authorization{ID: Int(1)}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Authorization.Update returned %+v, want %+v", got, want)
	}
}

func TestAuthorizationsService_Delete(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "DELETE")
		w.WriteHeader(http.StatusNoContent)
	})

	_, err := client.Authorizations.Delete(1)
	if err != nil {
		t.Errorf("Authorizations.Delete returned error: %v", err)
	}
}

func TestAuthorizationsService_Check(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `{"id":1}`)
	})

	got, _, err := client.Authorizations.Check("id", "t")
	if err != nil {
		t.Errorf("Authorizations.Check returned error: %v", err)
	}

	want := &Authorization{ID: Int(1)}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Authorizations.Check returned auth %+v, want %+v", got, want)
	}
}

func TestAuthorizationsService_Reset(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "POST")
		fmt.Fprint(w, `{"ID":1}`)
	})

	got, _, err := client.Authorizations.Reset("id", "t")
	if err != nil {
		t.Errorf("Authorizations.Reset returned error: %v", err)
	}

	want := &Authorization{ID: Int(1)}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Authorizations.Reset returned auth %+v, want %+v", got, want)
	}
}

func TestAuthorizationsService_Revoke(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "DELETE")
		w.WriteHeader(http.StatusNoContent)
	})

	_, err := client.Authorizations.Revoke("id", "t")
	if err != nil {
		t.Errorf("Authorizations.Revoke returned error: %v", err)
	}
}

func TestListGrants(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/applications/grants", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testHeader(t, r, "Accept", mediaTypeOAuthGrantAuthorizationsPreview)
		fmt.Fprint(w, `[{"id": 1}]`)
	})

	got, _, err := client.Authorizations.ListGrants()
	if err != nil {
		t.Errorf("OAuthAuthorizations.ListGrants returned error: %v", err)
	}

	want := []*Grant{{ID: Int(1)}}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("OAuthAuthorizations.ListGrants = %+v, want %+v", got, want)
	}
}

func TestGetGrant(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/applications/grants/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testHeader(t, r, "Accept", mediaTypeOAuthGrantAuthorizationsPreview)
		fmt.Fprint(w, `{"id": 1}`)
	})

	got, _, err := client.Authorizations.GetGrant(1)
	if err != nil {
		t.Errorf("OAuthAuthorizations.GetGrant returned error: %v", err)
	}

	want := &Grant{ID: Int(1)}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("OAuthAuthorizations.GetGrant = %+v, want %+v", got, want)
	}
}

func TestDeleteGrant(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/applications/grants/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "DELETE")
		testHeader(t, r, "Accept", mediaTypeOAuthGrantAuthorizationsPreview)
	})

	_, err := client.Authorizations.DeleteGrant(1)
	if err != nil {
		t.Errorf("OAuthAuthorizations.DeleteGrant returned error: %v", err)
	}
}
<commit_msg>add unit tests for #342 - CreateImpersonation and DeleteImpersonation<commit_after>// Copyright 2015 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package github

import (
	"encoding/json"
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func TestAuthorizationsService_List(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/authorizations", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testFormValues(t, r, values{"page": "1", "per_page": "2"})
		fmt.Fprint(w, `[{"id":1}]`)
	})

	opt := &ListOptions{Page: 1, PerPage: 2}
	got, _, err := client.Authorizations.List(opt)
	if err != nil {
		t.Errorf("Authorizations.List returned error: %v", err)
	}

	want := []*Authorization{{ID: Int(1)}}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Authorizations.List returned %+v, want %+v", *got[0].ID, *want[0].ID)
	}
}

func TestAuthorizationsService_Get(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `{"id":1}`)
	})

	got, _, err := client.Authorizations.Get(1)
	if err != nil {
		t.Errorf("Authorizations.Get returned error: %v", err)
	}

	want := &Authorization{ID: Int(1)}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Authorizations.Get returned auth %+v, want %+v", got, want)
	}
}

func TestAuthorizationsService_Create(t *testing.T) {
	setup()
	defer teardown()

	input := &AuthorizationRequest{
		Note: String("test"),
	}

	mux.HandleFunc("/authorizations", func(w http.ResponseWriter, r *http.Request) {
		v := new(AuthorizationRequest)
		json.NewDecoder(r.Body).Decode(v)

		testMethod(t, r, "POST")
		if !reflect.DeepEqual(v, input) {
			t.Errorf("Request body = %+v, want %+v", v, input)
		}

		fmt.Fprint(w, `{"ID":1}`)
	})

	got, _, err := client.Authorizations.Create(input)
	if err != nil {
		t.Errorf("Authorizations.Create returned error: %v", err)
	}

	want := &Authorization{ID: Int(1)}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Authorization.Create returned %+v, want %+v", got, want)
	}
}

func TestAuthorizationsService_GetOrCreateForApp(t *testing.T) {
	setup()
	defer teardown()

	input := &AuthorizationRequest{
		Note: String("test"),
	}

	mux.HandleFunc("/authorizations/clients/id", func(w http.ResponseWriter, r *http.Request) {
		v := new(AuthorizationRequest)
		json.NewDecoder(r.Body).Decode(v)

		testMethod(t, r, "PUT")
		if !reflect.DeepEqual(v, input) {
			t.Errorf("Request body = %+v, want %+v", v, input)
		}

		fmt.Fprint(w, `{"ID":1}`)
	})

	got, _, err := client.Authorizations.GetOrCreateForApp("id", input)
	if err != nil {
		t.Errorf("Authorizations.GetOrCreateForApp returned error: %v", err)
	}

	want := &Authorization{ID: Int(1)}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Authorization.GetOrCreateForApp returned %+v, want %+v", got, want)
	}
}

func TestAuthorizationsService_GetOrCreateForApp_Fingerprint(t *testing.T) {
	setup()
	defer teardown()

	input := &AuthorizationRequest{
		Note:        String("test"),
		Fingerprint: String("fp"),
	}

	mux.HandleFunc("/authorizations/clients/id/fp", func(w http.ResponseWriter, r *http.Request) {
		v := new(AuthorizationRequest)
		json.NewDecoder(r.Body).Decode(v)

		testMethod(t, r, "PUT")
		if !reflect.DeepEqual(v, input) {
			t.Errorf("Request body = %+v, want %+v", v, input)
		}

		fmt.Fprint(w, `{"ID":1}`)
	})

	got, _, err := client.Authorizations.GetOrCreateForApp("id", input)
	if err != nil {
		t.Errorf("Authorizations.GetOrCreateForApp returned error: %v", err)
	}

	want := &Authorization{ID: Int(1)}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Authorization.GetOrCreateForApp returned %+v, want %+v", got, want)
	}
}

func TestAuthorizationsService_Edit(t *testing.T) {
	setup()
	defer teardown()

	input := &AuthorizationUpdateRequest{
		Note: String("test"),
	}

	mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) {
		v := new(AuthorizationUpdateRequest)
		json.NewDecoder(r.Body).Decode(v)

		testMethod(t, r, "PATCH")
		if !reflect.DeepEqual(v, input) {
			t.Errorf("Request body = %+v, want %+v", v, input)
		}

		fmt.Fprint(w, `{"ID":1}`)
	})

	got, _, err := client.Authorizations.Edit(1, input)
	if err != nil {
		t.Errorf("Authorizations.Edit returned error: %v", err)
	}

	want := &Authorization{ID: Int(1)}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Authorization.Update returned %+v, want %+v", got, want)
	}
}

func TestAuthorizationsService_Delete(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "DELETE")
		w.WriteHeader(http.StatusNoContent)
	})

	_, err := client.Authorizations.Delete(1)
	if err != nil {
		t.Errorf("Authorizations.Delete returned error: %v", err)
	}
}

func TestAuthorizationsService_Check(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `{"id":1}`)
	})

	got, _, err := client.Authorizations.Check("id", "t")
	if err != nil {
		t.Errorf("Authorizations.Check returned error: %v", err)
	}

	want := &Authorization{ID: Int(1)}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Authorizations.Check returned auth %+v, want %+v", got, want)
	}
}

func TestAuthorizationsService_Reset(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "POST")
		fmt.Fprint(w, `{"ID":1}`)
	})

	got, _, err := client.Authorizations.Reset("id", "t")
	if err != nil {
		t.Errorf("Authorizations.Reset returned error: %v", err)
	}

	want := &Authorization{ID: Int(1)}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Authorizations.Reset returned auth %+v, want %+v", got, want)
	}
}

func TestAuthorizationsService_Revoke(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "DELETE")
		w.WriteHeader(http.StatusNoContent)
	})

	_, err := client.Authorizations.Revoke("id", "t")
	if err != nil {
		t.Errorf("Authorizations.Revoke returned error: %v", err)
	}
}

func TestListGrants(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/applications/grants", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testHeader(t, r, "Accept", mediaTypeOAuthGrantAuthorizationsPreview)
		fmt.Fprint(w, `[{"id": 1}]`)
	})

	got, _, err := client.Authorizations.ListGrants()
	if err != nil {
		t.Errorf("OAuthAuthorizations.ListGrants returned error: %v", err)
	}

	want := []*Grant{{ID: Int(1)}}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("OAuthAuthorizations.ListGrants = %+v, want %+v", got, want)
	}
}

func TestGetGrant(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/applications/grants/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testHeader(t, r, "Accept", mediaTypeOAuthGrantAuthorizationsPreview)
		fmt.Fprint(w, `{"id": 1}`)
	})

	got, _, err := client.Authorizations.GetGrant(1)
	if err != nil {
		t.Errorf("OAuthAuthorizations.GetGrant returned error: %v", err)
	}

	want := &Grant{ID: Int(1)}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("OAuthAuthorizations.GetGrant = %+v, want %+v", got, want)
	}
}

func TestDeleteGrant(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/applications/grants/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "DELETE")
		testHeader(t, r, "Accept", mediaTypeOAuthGrantAuthorizationsPreview)
	})

	_, err := client.Authorizations.DeleteGrant(1)
	if err != nil {
		t.Errorf("OAuthAuthorizations.DeleteGrant returned error: %v", err)
	}
}

func TestAuthorizationsService_CreateImpersonation(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/admin/users/u/authorizations", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "POST")
		fmt.Fprint(w, `{"id":1}`)
	})

	req := &AuthorizationRequest{Scopes: []Scope{ScopePublicRepo}}
	got, _, err := client.Authorizations.CreateImpersonation("u", req)
	if err != nil {
		t.Errorf("Authorizations.CreateImpersonation returned error: %+v", err)
	}

	want := &Authorization{ID: Int(1)}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Authorizations.CreateImpersonation returned %+v, want %+v", *got.ID, *want.ID)
	}
}

func TestAuthorizationsService_DeleteImpersonation(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/admin/users/u/authorizations", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "DELETE")
	})

	_, err := client.Authorizations.DeleteImpersonation("u")
	if err != nil {
		t.Errorf("Authorizations.DeleteImpersonation returned error: %+v", err)
	}
}
<|endoftext|>


<commit_before>package client

import (
	"encoding/json"
	"errors"
	"io"
	"strings"

	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
	"golang.org/x/net/context"
)

type teamAPIHandler struct {
	libkb.Contextified
	cli    keybase1.TeamsClient
	indent bool
}

func newTeamAPIHandler(g *libkb.GlobalContext, indentOutput bool) *teamAPIHandler {
	return &teamAPIHandler{Contextified: libkb.NewContextified(g), indent: indentOutput}
}

func (t *teamAPIHandler) handle(ctx context.Context, c Call, w io.Writer) error {
	switch c.Params.Version {
	case 0, 1:
		return t.handleV1(ctx, c, w)
	default:
		return ErrInvalidVersion{version: c.Params.Version}
	}
}

func (t *teamAPIHandler) handleV1(ctx context.Context, c Call, w io.Writer) error {
	if err := t.requireOptionsV1(c); err != nil {
		return err
	}

	cli, err := GetTeamsClient(t.G())
	if err != nil {
		return err
	}
	t.cli = cli

	switch c.Method {
	case "add-members":
		return t.addMembers(ctx, c, w)
	case "create-team":
		return t.createTeam(ctx, c, w)
	case "edit-member":
		return t.editMember(ctx, c, w)
	case "leave-team":
		return t.leaveTeam(ctx, c, w)
	case "list-self-memberships":
		return t.listSelfMemberships(ctx, c, w)
	case "list-team-memberships":
		return t.listTeamMemberships(ctx, c, w)
	case "list-user-memberships":
		return t.listUserMemberships(ctx, c, w)
	case "remove-member":
		return t.removeMember(ctx, c, w)
	case "rename-subteam":
		return t.renameSubteam(ctx, c, w)
	default:
		return ErrInvalidMethod{name: c.Method, version: 1}
	}
}

type memberEmail struct {
	Email string `json:"email"`
	Role  string `json:"role"`
}

type memberUsername struct {
	Username string `json:"username"`
	Role     string `json:"role"`
}

type addMembersOptions struct {
	Team      string           `json:"team"`
	Emails    []memberEmail    `json:"emails"`
	Usernames []memberUsername `json:"usernames"`
}

func (a *addMembersOptions) Check() error {
	if len(a.Team) == 0 {
		return errors.New("`team` field required")
	}

	if len(a.Emails) == 0 && len(a.Usernames) == 0 {
		return errors.New("no emails or usernames specified")
	}

	for _, e := range a.Emails {
		if len(e.Email) == 0 {
			return errors.New("empty email address")
		}
		if len(e.Role) == 0 {
			return errors.New("empty role")
		}
		if _, err := mapRole(e.Role); err != nil {
			return err
		}
	}

	for _, u := range a.Usernames {
		if len(u.Username) == 0 {
			return errors.New("empty username")
		}
		if len(u.Role) == 0 {
			return errors.New("empty role")
		}
		if _, err := mapRole(u.Role); err != nil {
			return err
		}
	}

	return nil
}

func (t *teamAPIHandler) addMembers(ctx context.Context, c Call, w io.Writer) error {
	var opts addMembersOptions
	if err := t.unmarshalOptions(c, &opts); err != nil {
		return t.encodeErr(c, err, w)
	}

	// currently service endpoint can only handle one at a time
	// can improve this when CORE-6172 is complete
	var args []keybase1.TeamAddMemberArg
	for _, e := range opts.Emails {
		role, err := mapRole(e.Role)
		if err != nil {
			return t.encodeErr(c, err, w)
		}
		arg := keybase1.TeamAddMemberArg{
			Name:  opts.Team,
			Email: e.Email,
			Role:  role,
		}
		args = append(args, arg)
	}
	for _, u := range opts.Usernames {
		role, err := mapRole(u.Role)
		if err != nil {
			return t.encodeErr(c, err, w)
		}
		arg := keybase1.TeamAddMemberArg{
			Name:     opts.Team,
			Username: u.Username,
			Role:     role,
		}
		args = append(args, arg)
	}

	var all []keybase1.TeamAddMemberResult
	for _, arg := range args {
		res, err := t.cli.TeamAddMember(ctx, arg)
		if err != nil {
			return t.encodeErr(c, err, w)
		}
		all = append(all, res)
	}

	return t.encodeResult(c, all, w)
}

type createTeamOptions struct {
	Team string `json:"team"`
}

func (c *createTeamOptions) Check() error {
	_, err := keybase1.TeamNameFromString(c.Team)
	return err
}

func (t *teamAPIHandler) createTeam(ctx context.Context, c Call, w io.Writer) error {
	var opts createTeamOptions
	if err := t.unmarshalOptions(c, &opts); err != nil {
		return t.encodeErr(c, err, w)
	}

	name, err := keybase1.TeamNameFromString(opts.Team)
	if err != nil {
		return t.encodeErr(c, err, w)
	}
	sendChatNotification := name.IsRootTeam()

	createRes, err := t.cli.TeamCreate(context.TODO(), keybase1.TeamCreateArg{
		Name:                 name.String(),
		SendChatNotification: sendChatNotification,
	})
	if err != nil {
		return t.encodeErr(c, err, w)
	}

	return t.encodeResult(c, createRes, w)
}

func (t *teamAPIHandler) editMember(ctx context.Context, c Call, w io.Writer) error {
	return nil
}

type leaveTeamOptions struct {
	Team      string `json:"team"`
	Permanent bool   `json:"permanent"`
}

func (c *leaveTeamOptions) Check() error {
	_, err := keybase1.TeamNameFromString(c.Team)
	return err
}

func (t *teamAPIHandler) leaveTeam(ctx context.Context, c Call, w io.Writer) error {
	var opts leaveTeamOptions
	if err := t.unmarshalOptions(c, &opts); err != nil {
		return t.encodeErr(c, err, w)
	}

	arg := keybase1.TeamLeaveArg{
		Name:      opts.Team,
		Permanent: opts.Permanent,
	}
	if err := t.cli.TeamLeave(ctx, arg); err != nil {
		return t.encodeErr(c, err, w)
	}
	return t.encodeResult(c, nil, w)
}

func (t *teamAPIHandler) listSelfMemberships(ctx context.Context, c Call, w io.Writer) error {
	arg := keybase1.TeamListArg{
		All: true,
	}
	list, err := t.cli.TeamList(ctx, arg)
	if err != nil {
		return t.encodeErr(c, err, w)
	}
	return t.encodeResult(c, list, w)
}

type listTeamOptions struct {
	Team      string `json:"team"`
	ForcePoll bool   `json:"force-poll"`
}

func (c *listTeamOptions) Check() error {
	_, err := keybase1.TeamNameFromString(c.Team)
	return err
}

func (t *teamAPIHandler) listTeamMemberships(ctx context.Context, c Call, w io.Writer) error {
	var opts listTeamOptions
	if err := t.unmarshalOptions(c, &opts); err != nil {
		return t.encodeErr(c, err, w)
	}

	arg := keybase1.TeamGetArg{
		Name:        opts.Team,
		ForceRepoll: opts.ForcePoll,
	}
	details, err := t.cli.TeamGet(ctx, arg)
	if err != nil {
		return t.encodeErr(c, err, w)
	}

	return t.encodeResult(c, details, w)
}

type listUserOptions struct {
	UserAssertion        string `json:"user"`
	IncludeImplicitTeams bool   `json:"include-implicit-teams"`
}

func (o *listUserOptions) Check() error {
	if len(o.UserAssertion) == 0 {
		return errors.New("list-user-memberships: \"user\" required")
	}

	return nil
}

func (t *teamAPIHandler) listUserMemberships(ctx context.Context, c Call, w io.Writer) error {
	var opts listUserOptions
	if err := t.unmarshalOptions(c, &opts); err != nil {
		return t.encodeErr(c, err, w)
	}

	arg := keybase1.TeamListArg{
		UserAssertion:        opts.UserAssertion,
		IncludeImplicitTeams: opts.IncludeImplicitTeams,
	}
	list, err := t.cli.TeamList(ctx, arg)
	if err != nil {
		return t.encodeErr(c, err, w)
	}

	return t.encodeResult(c, list, w)
}

type removeMemberOptions struct {
	Team     string `json:"team"`
	Username string `json:"username"`
}

func (c *removeMemberOptions) Check() error {
	if _, err := keybase1.TeamNameFromString(c.Team); err != nil {
		return err
	}
	if len(c.Username) == 0 {
		return errors.New("remove-member: specify username to remove")
	}

	return nil
}

// XXX implement Email option after CORE-6223 merged
func (t *teamAPIHandler) removeMember(ctx context.Context, c Call, w io.Writer) error {
	var opts removeMemberOptions
	if err := t.unmarshalOptions(c, &opts); err != nil {
		return t.encodeErr(c, err, w)
	}

	arg := keybase1.TeamRemoveMemberArg{
		Name:     opts.Team,
		Username: opts.Username,
	}
	if err := t.cli.TeamRemoveMember(ctx, arg); err != nil {
		return t.encodeErr(c, err, w)
	}
	return t.encodeResult(c, nil, w)
}

type renameOptions struct {
	Team        string `json:"team"`
	NewTeamName string `json:"new-team-name"`
}

func (c *renameOptions) Check() error {
	if err := checkSubteam(c.Team); err != nil {
		return err
	}
	return checkSubteam(c.NewTeamName)
}

func (t *teamAPIHandler) renameSubteam(ctx context.Context, c Call, w io.Writer) error {
	var opts renameOptions
	if err := t.unmarshalOptions(c, &opts); err != nil {
		return t.encodeErr(c, err, w)
	}

	name, err := keybase1.TeamNameFromString(opts.Team)
	if err != nil {
		return t.encodeErr(c, err, w)
	}
	newName, err := keybase1.TeamNameFromString(opts.NewTeamName)
	if err != nil {
		return t.encodeErr(c, err, w)
	}

	arg := keybase1.TeamRenameArg{
		PrevName: name,
		NewName:  newName,
	}
	if err := t.cli.TeamRename(ctx, arg); err != nil {
		return t.encodeErr(c, err, w)
	}
	return t.encodeResult(c, nil, w)
}

func (t *teamAPIHandler) requireOptionsV1(c Call) error {
	if len(c.Params.Options) == 0 {
		if c.Method != "list-self-memberships" {
			return ErrInvalidOptions{version: 1, method: c.Method, err: errors.New("empty options")}
		}
	}
	return nil

}

func (t *teamAPIHandler) encodeResult(call Call, result interface{}, w io.Writer) error {
	reply := Reply{
		Result: result,
	}
	return t.encodeReply(call, reply, w)
}

func (t *teamAPIHandler) encodeErr(call Call, err error, w io.Writer) error {
	reply := Reply{Error: &CallError{Message: err.Error()}}
	return t.encodeReply(call, reply, w)
}

func (t *teamAPIHandler) encodeReply(call Call, reply Reply, w io.Writer) error {
	reply.Jsonrpc = call.Jsonrpc
	reply.ID = call.ID

	enc := json.NewEncoder(w)
	if t.indent {
		enc.SetIndent("", "    ")
	}
	return enc.Encode(reply)
}

func (t *teamAPIHandler) unmarshalOptions(c Call, opts Checker) error {
	if len(c.Params.Options) == 0 {
		return nil
	}
	if err := json.Unmarshal(c.Params.Options, opts); err != nil {
		return err
	}
	return opts.Check()
}

func mapRole(srole string) (keybase1.TeamRole, error) {
	role, ok := keybase1.TeamRoleMap[strings.ToUpper(srole)]
	if !ok {
		return 0, errors.New("invalid team role, please use owner, admin, writer, or reader")
	}

	return role, nil

}

type Checker interface {
	Check() error
}

func checkSubteam(name string) error {
	n, err := keybase1.TeamNameFromString(name)
	if err != nil {
		return err
	}
	if n.IsRootTeam() {
		return errors.New("can only rename subteams")
	}
	return nil
}
<commit_msg>Add edit-member<commit_after>package client

import (
	"encoding/json"
	"errors"
	"io"
	"strings"

	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
	"golang.org/x/net/context"
)

type teamAPIHandler struct {
	libkb.Contextified
	cli    keybase1.TeamsClient
	indent bool
}

func newTeamAPIHandler(g *libkb.GlobalContext, indentOutput bool) *teamAPIHandler {
	return &teamAPIHandler{Contextified: libkb.NewContextified(g), indent: indentOutput}
}

func (t *teamAPIHandler) handle(ctx context.Context, c Call, w io.Writer) error {
	switch c.Params.Version {
	case 0, 1:
		return t.handleV1(ctx, c, w)
	default:
		return ErrInvalidVersion{version: c.Params.Version}
	}
}

func (t *teamAPIHandler) handleV1(ctx context.Context, c Call, w io.Writer) error {
	if err := t.requireOptionsV1(c); err != nil {
		return err
	}

	cli, err := GetTeamsClient(t.G())
	if err != nil {
		return err
	}
	t.cli = cli

	switch c.Method {
	case "add-members":
		return t.addMembers(ctx, c, w)
	case "create-team":
		return t.createTeam(ctx, c, w)
	case "edit-member":
		return t.editMember(ctx, c, w)
	case "leave-team":
		return t.leaveTeam(ctx, c, w)
	case "list-self-memberships":
		return t.listSelfMemberships(ctx, c, w)
	case "list-team-memberships":
		return t.listTeamMemberships(ctx, c, w)
	case "list-user-memberships":
		return t.listUserMemberships(ctx, c, w)
	case "remove-member":
		return t.removeMember(ctx, c, w)
	case "rename-subteam":
		return t.renameSubteam(ctx, c, w)
	default:
		return ErrInvalidMethod{name: c.Method, version: 1}
	}
}

type memberEmail struct {
	Email string `json:"email"`
	Role  string `json:"role"`
}

type memberUsername struct {
	Username string `json:"username"`
	Role     string `json:"role"`
}

type addMembersOptions struct {
	Team      string           `json:"team"`
	Emails    []memberEmail    `json:"emails"`
	Usernames []memberUsername `json:"usernames"`
}

func (a *addMembersOptions) Check() error {
	if len(a.Team) == 0 {
		return errors.New("`team` field required")
	}

	if len(a.Emails) == 0 && len(a.Usernames) == 0 {
		return errors.New("no emails or usernames specified")
	}

	for _, e := range a.Emails {
		if len(e.Email) == 0 {
			return errors.New("empty email address")
		}
		if len(e.Role) == 0 {
			return errors.New("empty role")
		}
		if _, err := mapRole(e.Role); err != nil {
			return err
		}
	}

	for _, u := range a.Usernames {
		if len(u.Username) == 0 {
			return errors.New("empty username")
		}
		if len(u.Role) == 0 {
			return errors.New("empty role")
		}
		if _, err := mapRole(u.Role); err != nil {
			return err
		}
	}

	return nil
}

func (t *teamAPIHandler) addMembers(ctx context.Context, c Call, w io.Writer) error {
	var opts addMembersOptions
	if err := t.unmarshalOptions(c, &opts); err != nil {
		return t.encodeErr(c, err, w)
	}

	// currently service endpoint can only handle one at a time
	// can improve this when CORE-6172 is complete
	var args []keybase1.TeamAddMemberArg
	for _, e := range opts.Emails {
		role, err := mapRole(e.Role)
		if err != nil {
			return t.encodeErr(c, err, w)
		}
		arg := keybase1.TeamAddMemberArg{
			Name:  opts.Team,
			Email: e.Email,
			Role:  role,
		}
		args = append(args, arg)
	}
	for _, u := range opts.Usernames {
		role, err := mapRole(u.Role)
		if err != nil {
			return t.encodeErr(c, err, w)
		}
		arg := keybase1.TeamAddMemberArg{
			Name:     opts.Team,
			Username: u.Username,
			Role:     role,
		}
		args = append(args, arg)
	}

	var all []keybase1.TeamAddMemberResult
	for _, arg := range args {
		res, err := t.cli.TeamAddMember(ctx, arg)
		if err != nil {
			return t.encodeErr(c, err, w)
		}
		all = append(all, res)
	}

	return t.encodeResult(c, all, w)
}

type createTeamOptions struct {
	Team string `json:"team"`
}

func (c *createTeamOptions) Check() error {
	_, err := keybase1.TeamNameFromString(c.Team)
	return err
}

func (t *teamAPIHandler) createTeam(ctx context.Context, c Call, w io.Writer) error {
	var opts createTeamOptions
	if err := t.unmarshalOptions(c, &opts); err != nil {
		return t.encodeErr(c, err, w)
	}

	name, err := keybase1.TeamNameFromString(opts.Team)
	if err != nil {
		return t.encodeErr(c, err, w)
	}
	sendChatNotification := name.IsRootTeam()

	createRes, err := t.cli.TeamCreate(context.TODO(), keybase1.TeamCreateArg{
		Name:                 name.String(),
		SendChatNotification: sendChatNotification,
	})
	if err != nil {
		return t.encodeErr(c, err, w)
	}

	return t.encodeResult(c, createRes, w)
}

type editMemberOptions struct {
	Team     string `json:"team"`
	Username string `json:"username"`
	Role     string `json:"role"`
}

func (e *editMemberOptions) Check() error {
	_, err := keybase1.TeamNameFromString(e.Team)
	if err != nil {
		return err
	}
	if len(e.Username) == 0 {
		return errors.New("empty username")
	}
	if len(e.Role) == 0 {
		return errors.New("empty role")
	}
	if _, err := mapRole(e.Role); err != nil {
		return err
	}

	return nil
}

func (t *teamAPIHandler) editMember(ctx context.Context, c Call, w io.Writer) error {
	var opts editMemberOptions
	if err := t.unmarshalOptions(c, &opts); err != nil {
		return t.encodeErr(c, err, w)
	}

	role, err := mapRole(opts.Role)
	if err != nil {
		return t.encodeErr(c, err, w)
	}
	arg := keybase1.TeamEditMemberArg{
		Name:     opts.Team,
		Username: opts.Username,
		Role:     role,
	}
	if err := t.cli.TeamEditMember(ctx, arg); err != nil {
		return t.encodeErr(c, err, w)
	}

	return t.encodeResult(c, nil, w)
}

type leaveTeamOptions struct {
	Team      string `json:"team"`
	Permanent bool   `json:"permanent"`
}

func (c *leaveTeamOptions) Check() error {
	_, err := keybase1.TeamNameFromString(c.Team)
	return err
}

func (t *teamAPIHandler) leaveTeam(ctx context.Context, c Call, w io.Writer) error {
	var opts leaveTeamOptions
	if err := t.unmarshalOptions(c, &opts); err != nil {
		return t.encodeErr(c, err, w)
	}

	arg := keybase1.TeamLeaveArg{
		Name:      opts.Team,
		Permanent: opts.Permanent,
	}
	if err := t.cli.TeamLeave(ctx, arg); err != nil {
		return t.encodeErr(c, err, w)
	}
	return t.encodeResult(c, nil, w)
}

func (t *teamAPIHandler) listSelfMemberships(ctx context.Context, c Call, w io.Writer) error {
	arg := keybase1.TeamListArg{
		All: true,
	}
	list, err := t.cli.TeamList(ctx, arg)
	if err != nil {
		return t.encodeErr(c, err, w)
	}
	return t.encodeResult(c, list, w)
}

type listTeamOptions struct {
	Team      string `json:"team"`
	ForcePoll bool   `json:"force-poll"`
}

func (c *listTeamOptions) Check() error {
	_, err := keybase1.TeamNameFromString(c.Team)
	return err
}

func (t *teamAPIHandler) listTeamMemberships(ctx context.Context, c Call, w io.Writer) error {
	var opts listTeamOptions
	if err := t.unmarshalOptions(c, &opts); err != nil {
		return t.encodeErr(c, err, w)
	}

	arg := keybase1.TeamGetArg{
		Name:        opts.Team,
		ForceRepoll: opts.ForcePoll,
	}
	details, err := t.cli.TeamGet(ctx, arg)
	if err != nil {
		return t.encodeErr(c, err, w)
	}

	return t.encodeResult(c, details, w)
}

type listUserOptions struct {
	UserAssertion        string `json:"username"`
	IncludeImplicitTeams bool   `json:"include-implicit-teams"`
}

func (o *listUserOptions) Check() error {
	if len(o.UserAssertion) == 0 {
		return errors.New("list-user-memberships: \"user\" required")
	}

	return nil
}

func (t *teamAPIHandler) listUserMemberships(ctx context.Context, c Call, w io.Writer) error {
	var opts listUserOptions
	if err := t.unmarshalOptions(c, &opts); err != nil {
		return t.encodeErr(c, err, w)
	}

	arg := keybase1.TeamListArg{
		UserAssertion:        opts.UserAssertion,
		IncludeImplicitTeams: opts.IncludeImplicitTeams,
	}
	list, err := t.cli.TeamList(ctx, arg)
	if err != nil {
		return t.encodeErr(c, err, w)
	}

	return t.encodeResult(c, list, w)
}

type removeMemberOptions struct {
	Team     string `json:"team"`
	Username string `json:"username"`
}

func (c *removeMemberOptions) Check() error {
	if _, err := keybase1.TeamNameFromString(c.Team); err != nil {
		return err
	}
	if len(c.Username) == 0 {
		return errors.New("remove-member: specify username to remove")
	}

	return nil
}

// XXX implement Email option after CORE-6223 merged
func (t *teamAPIHandler) removeMember(ctx context.Context, c Call, w io.Writer) error {
	var opts removeMemberOptions
	if err := t.unmarshalOptions(c, &opts); err != nil {
		return t.encodeErr(c, err, w)
	}

	arg := keybase1.TeamRemoveMemberArg{
		Name:     opts.Team,
		Username: opts.Username,
	}
	if err := t.cli.TeamRemoveMember(ctx, arg); err != nil {
		return t.encodeErr(c, err, w)
	}
	return t.encodeResult(c, nil, w)
}

type renameOptions struct {
	Team        string `json:"team"`
	NewTeamName string `json:"new-team-name"`
}

func (c *renameOptions) Check() error {
	if err := checkSubteam(c.Team); err != nil {
		return err
	}
	return checkSubteam(c.NewTeamName)
}

func (t *teamAPIHandler) renameSubteam(ctx context.Context, c Call, w io.Writer) error {
	var opts renameOptions
	if err := t.unmarshalOptions(c, &opts); err != nil {
		return t.encodeErr(c, err, w)
	}

	name, err := keybase1.TeamNameFromString(opts.Team)
	if err != nil {
		return t.encodeErr(c, err, w)
	}
	newName, err := keybase1.TeamNameFromString(opts.NewTeamName)
	if err != nil {
		return t.encodeErr(c, err, w)
	}

	arg := keybase1.TeamRenameArg{
		PrevName: name,
		NewName:  newName,
	}
	if err := t.cli.TeamRename(ctx, arg); err != nil {
		return t.encodeErr(c, err, w)
	}
	return t.encodeResult(c, nil, w)
}

func (t *teamAPIHandler) requireOptionsV1(c Call) error {
	if len(c.Params.Options) == 0 {
		if c.Method != "list-self-memberships" {
			return ErrInvalidOptions{version: 1, method: c.Method, err: errors.New("empty options")}
		}
	}
	return nil

}

func (t *teamAPIHandler) encodeResult(call Call, result interface{}, w io.Writer) error {
	reply := Reply{
		Result: result,
	}
	return t.encodeReply(call, reply, w)
}

func (t *teamAPIHandler) encodeErr(call Call, err error, w io.Writer) error {
	reply := Reply{Error: &CallError{Message: err.Error()}}
	return t.encodeReply(call, reply, w)
}

func (t *teamAPIHandler) encodeReply(call Call, reply Reply, w io.Writer) error {
	reply.Jsonrpc = call.Jsonrpc
	reply.ID = call.ID

	enc := json.NewEncoder(w)
	if t.indent {
		enc.SetIndent("", "    ")
	}
	return enc.Encode(reply)
}

func (t *teamAPIHandler) unmarshalOptions(c Call, opts Checker) error {
	if len(c.Params.Options) == 0 {
		return nil
	}
	if err := json.Unmarshal(c.Params.Options, opts); err != nil {
		return err
	}
	return opts.Check()
}

func mapRole(srole string) (keybase1.TeamRole, error) {
	role, ok := keybase1.TeamRoleMap[strings.ToUpper(srole)]
	if !ok {
		return 0, errors.New("invalid team role, please use owner, admin, writer, or reader")
	}

	return role, nil

}

type Checker interface {
	Check() error
}

func checkSubteam(name string) error {
	n, err := keybase1.TeamNameFromString(name)
	if err != nil {
		return err
	}
	if n.IsRootTeam() {
		return errors.New("can only rename subteams")
	}
	return nil
}
<|endoftext|>


<commit_before>// Package archive packs files into a gzipped tarball with maximum
// compression. It will only pack regular files and directories.
package archive

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
)

// Verbose determines whether to print paths as they are packed.
var Verbose = true

// PackFiles walks the specified paths and archives them; the resulting
// archive is gzipped with maximum compression.
func PackFiles(paths []string) ([]byte, error) {
	// Create a buffer to write our archive to.
	buf := new(bytes.Buffer)

	zbuf, err := gzip.NewWriterLevel(buf, gzip.BestCompression)
	if err != nil {
		return nil, err
	}

	// Create a new tar archive.
	tw := tar.NewWriter(zbuf)

	for _, walkPath := range paths {
		walker := func(path string, info os.FileInfo, err error) error {
			if info == nil {
				return fmt.Errorf("filecrypt: %s could not be read", path)
			}

			if !info.Mode().IsDir() && !info.Mode().IsRegular() {
				return errors.New("filecrypt: failed to compress " + path)
			}

			if Verbose {
				fmt.Println("Pack file", path)
			}

			filePath := filepath.Clean(path)
			hdr, err := tar.FileInfoHeader(info, filePath)
			if err != nil {
				return err
			}
			hdr.Name = filePath

			if err = tw.WriteHeader(hdr); err != nil {
				return err
			}

			if info.Mode().IsRegular() {
				file, err := os.Open(path)
				if err != nil {
					return err
				}

				_, err = io.Copy(tw, file)
				file.Close()
				return err
			}
			return nil

		}
		err := filepath.Walk(walkPath, walker)
		if err != nil {
			return nil, err
		}

	}

	tw.Close()
	zbuf.Close()

	return buf.Bytes(), nil
}

// UnpackFiles decompresses and unarchives the gzipped tarball passed in
// as data and uncompresses it to the top-level directory given.
func UnpackFiles(in []byte, top string) error {
	buf := bytes.NewBuffer(in)
	zbuf, err := gzip.NewReader(buf)
	if err != nil {
		return err
	}
	defer zbuf.Close()

	tr := tar.NewReader(zbuf)

	for {
		hdr, err := tr.Next()
		if err == io.EOF {
			return nil
		} else if err != nil {
			return err
		}

		if Verbose {
			fmt.Println(hdr.Name)
		}

		filePath := filepath.Clean(filepath.Join(top, hdr.Name))
		switch hdr.Typeflag {
		case tar.TypeReg, tar.TypeRegA:
			file, err := os.Create(filePath)
			if err != nil {
				return err
			}

			_, err = io.Copy(file, tr)
			if err != nil {
				return err
			}

			err = file.Chmod(os.FileMode(hdr.Mode))
			if err != nil {
				return err
			}
		case tar.TypeDir:
			err = os.MkdirAll(filePath, os.FileMode(hdr.Mode))
			if err != nil {
				return err
			}
		}
	}
}
<commit_msg>Switch to sbuf.<commit_after>// Package archive packs files into a gzipped tarball with maximum
// compression. It will only pack regular files and directories.
package archive

import (
	"archive/tar"
	"compress/gzip"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/kisom/sbuf"
)

// Verbose determines whether to print paths as they are packed.
var Verbose = true

// PackFiles walks the specified paths and archives them; the resulting
// archive is gzipped with maximum compression.
func PackFiles(paths []string) ([]byte, error) {
	// Create a buffer to write our archive to.
	buf := sbuf.NewBuffer(0)

	zbuf, err := gzip.NewWriterLevel(buf, gzip.BestCompression)
	if err != nil {
		return nil, err
	}

	// Create a new tar archive.
	tw := tar.NewWriter(zbuf)

	for _, walkPath := range paths {
		walker := func(path string, info os.FileInfo, err error) error {
			if info == nil {
				return fmt.Errorf("filecrypt: %s could not be read", path)
			}

			if !info.Mode().IsDir() && !info.Mode().IsRegular() {
				return errors.New("filecrypt: failed to compress " + path)
			}

			if Verbose {
				fmt.Println("Pack file", path)
			}

			filePath := filepath.Clean(path)
			hdr, err := tar.FileInfoHeader(info, filePath)
			if err != nil {
				return err
			}
			hdr.Name = filePath

			if err = tw.WriteHeader(hdr); err != nil {
				return err
			}

			if info.Mode().IsRegular() {
				file, err := os.Open(path)
				if err != nil {
					return err
				}

				_, err = io.Copy(tw, file)
				file.Close()
				return err
			}
			return nil

		}
		err := filepath.Walk(walkPath, walker)
		if err != nil {
			return nil, err
		}

	}

	tw.Close()
	zbuf.Close()

	return buf.Bytes(), nil
}

// UnpackFiles decompresses and unarchives the gzipped tarball passed in
// as data and uncompresses it to the top-level directory given.
func UnpackFiles(in []byte, top string) error {
	buf := sbuf.NewBufferFrom(in)
	zbuf, err := gzip.NewReader(buf)
	if err != nil {
		return err
	}
	defer zbuf.Close()

	tr := tar.NewReader(zbuf)

	for {
		hdr, err := tr.Next()
		if err == io.EOF {
			return nil
		} else if err != nil {
			return err
		}

		if Verbose {
			fmt.Println(hdr.Name)
		}

		filePath := filepath.Clean(filepath.Join(top, hdr.Name))
		switch hdr.Typeflag {
		case tar.TypeReg, tar.TypeRegA:
			file, err := os.Create(filePath)
			if err != nil {
				return err
			}

			_, err = io.Copy(file, tr)
			if err != nil {
				return err
			}

			err = file.Chmod(os.FileMode(hdr.Mode))
			if err != nil {
				return err
			}
		case tar.TypeDir:
			err = os.MkdirAll(filePath, os.FileMode(hdr.Mode))
			if err != nil {
				return err
			}
		}
	}
}
<|endoftext|>


<commit_before>package enmime

import (
	"testing"

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

// Put the quoted-printable decoder through its paces
func TestQuotedPrintableDecoder(t *testing.T) {
	// Test table data
	var testTable = []struct {
		input, expect string
	}{
		{"", ""},
		{"Hello", "Hello"},
		{"Hello_World", "Hello World"},
		{"Hello=20World", "Hello World"},
		{"Hello=3f", "Hello?"},
		{"Hello=3F", "Hello?"},
	}

	for _, tt := range testTable {
		result, err := decodeQuotedPrintable([]byte(tt.input))
		assert.Nil(t, err)
		assert.Equal(t, tt.expect, string(result),
			"Expected %q, got %q for input %q", tt.expect, result, tt.input)
	}

	// Check malformed quoted chars
	input := []byte("Hello=")
	_, err := decodeQuotedPrintable(input)
	assert.NotNil(t, err)

	input = []byte("Hello=3")
	_, err = decodeQuotedPrintable(input)
	assert.NotNil(t, err)
}

// Check the base64 decoder
func TestBase64Decoder(t *testing.T) {
	input := []byte("SGVsbG8gV29ybGQ=")
	expect := []byte("Hello World")
	result, err := decodeBase64(input)
	assert.Nil(t, err)
	assert.Equal(t, string(expect), string(result))
}

// Ensure that a single plain text token passes unharmed
func TestPlainSingleToken(t *testing.T) {
	input := "Test"
	expect := input
	result := DecodeHeader(input)
	assert.Equal(t, expect, result)
}

// Ensure that a string of plain text tokens do not get mangled
func TestPlainTokens(t *testing.T) {
	input := "Testing One two 3 4"
	expect := input
	result := DecodeHeader(input)
	assert.Equal(t, expect, result)
}

// Test control character detection & abort
func TestCharsetControlDetect(t *testing.T) {
	input := "=?US\nASCII?Q?Keith_Moore?="
	expect := input
	result := DecodeHeader(input)
	assert.Equal(t, expect, result)
}

// Test control character detection & abort
func TestEncodingControlDetect(t *testing.T) {
	input := "=?US-ASCII?\r?Keith_Moore?="
	expect := input
	result := DecodeHeader(input)
	assert.Equal(t, expect, result)
}

// Test control character detection & abort
func TestEncTextControlDetect(t *testing.T) {
	input := "=?US-ASCII?Q?Keith\tMoore?="
	expect := input
	result := DecodeHeader(input)
	assert.Equal(t, expect, result)
}

// Test mangled termination
func TestInvalidTermination(t *testing.T) {
	input := "=?US-ASCII?Q?Keith_Moore?!"
	expect := input
	result := DecodeHeader(input)
	assert.Equal(t, expect, result)
}

// Try decoding a simple ASCII quoted-printable encoded word
func TestAsciiQ(t *testing.T) {
	input := "=?US-ASCII?Q?Keith_Moore?="
	expect := "Keith Moore"
	result := DecodeHeader(input)
	assert.Equal(t, expect, result)
}

// Try decoding a simple ASCII quoted-printable encoded word
func TestAsciiB64(t *testing.T) {
	input := "=?US-ASCII?B?SGVsbG8gV29ybGQ=?="
	expect := "Hello World"
	result := DecodeHeader(input)
	assert.Equal(t, expect, result)
}

// Try decoding an embedded ASCII quoted-printable encoded word
func TestEmbeddedAsciiQ(t *testing.T) {
	// This is not legal, so we expect it to fail
	input := "ab=?US-ASCII?Q?Keith_Moore?=CD"
	expect := input
	result := DecodeHeader(input)
	assert.Equal(t, expect, result)

	// Abutting a MIME header comment is legal
	input = "(=?US-ASCII?Q?Keith_Moore?=)"
	expect = "(Keith Moore)"
	result = DecodeHeader(input)
	assert.Equal(t, expect, result)
}

// Spacing rules from RFC 2047
func TestSpacing(t *testing.T) {
	var testTable = []struct {
		input, expect string
	}{
		{"(=?ISO-8859-1?Q?a?=)", "(a)"},
		{"(=?ISO-8859-1?Q?a?= b)", "(a b)"},
		{"(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)", "(ab)"},
		{"(=?ISO-8859-1?Q?a?=  =?ISO-8859-1?Q?b?=)", "(ab)"},
		{"(=?ISO-8859-1?Q?a?=\r\n  =?ISO-8859-1?Q?b?=)", "(ab)"},
		{"(=?ISO-8859-1?Q?a_b?=)", "(a b)"},
		{"(=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=)", "(a b)"},
	}

	for _, tt := range testTable {
		result := DecodeHeader(tt.input)
		assert.Equal(t, tt.expect, result,
			"Expected %q, got %q for input %q", tt.expect, result, tt.input)
	}
}

// Test some different character sets
func TestCharsets(t *testing.T) {
	var testTable = []struct {
		input, expect string
	}{
		{"=?utf-8?q?abcABC_=24_=c2=a2_=e2=82=ac?=", "abcABC $ \u00a2 \u20ac"},
		{"=?iso-8859-1?q?#=a3_c=a9_r=ae_u=b5?=", "#\u00a3 c\u00a9 r\u00ae u\u00b5"},
		{"=?big5?q?=a1=5d_=a1=61_=a1=71?=", "\uff08 \uff5b \u3008"},
	}

	for _, tt := range testTable {
		result := DecodeHeader(tt.input)
		assert.Equal(t, tt.expect, result,
			"Expected %q, got %q for input %q", tt.expect, result, tt.input)
	}
}
<commit_msg>Add test for DecodeToUTF8Base64Header<commit_after>package enmime

import (
	"testing"

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

// Put the quoted-printable decoder through its paces
func TestQuotedPrintableDecoder(t *testing.T) {
	// Test table data
	var testTable = []struct {
		input, expect string
	}{
		{"", ""},
		{"Hello", "Hello"},
		{"Hello_World", "Hello World"},
		{"Hello=20World", "Hello World"},
		{"Hello=3f", "Hello?"},
		{"Hello=3F", "Hello?"},
	}

	for _, tt := range testTable {
		result, err := decodeQuotedPrintable([]byte(tt.input))
		assert.Nil(t, err)
		assert.Equal(t, tt.expect, string(result),
			"Expected %q, got %q for input %q", tt.expect, result, tt.input)
	}

	// Check malformed quoted chars
	input := []byte("Hello=")
	_, err := decodeQuotedPrintable(input)
	assert.NotNil(t, err)

	input = []byte("Hello=3")
	_, err = decodeQuotedPrintable(input)
	assert.NotNil(t, err)
}

// Check the base64 decoder
func TestBase64Decoder(t *testing.T) {
	input := []byte("SGVsbG8gV29ybGQ=")
	expect := []byte("Hello World")
	result, err := decodeBase64(input)
	assert.Nil(t, err)
	assert.Equal(t, string(expect), string(result))
}

// Ensure that a single plain text token passes unharmed
func TestPlainSingleToken(t *testing.T) {
	input := "Test"
	expect := input
	result := DecodeHeader(input)
	assert.Equal(t, expect, result)
}

// Ensure that a string of plain text tokens do not get mangled
func TestPlainTokens(t *testing.T) {
	input := "Testing One two 3 4"
	expect := input
	result := DecodeHeader(input)
	assert.Equal(t, expect, result)
}

// Test control character detection & abort
func TestCharsetControlDetect(t *testing.T) {
	input := "=?US\nASCII?Q?Keith_Moore?="
	expect := input
	result := DecodeHeader(input)
	assert.Equal(t, expect, result)
}

// Test control character detection & abort
func TestEncodingControlDetect(t *testing.T) {
	input := "=?US-ASCII?\r?Keith_Moore?="
	expect := input
	result := DecodeHeader(input)
	assert.Equal(t, expect, result)
}

// Test control character detection & abort
func TestEncTextControlDetect(t *testing.T) {
	input := "=?US-ASCII?Q?Keith\tMoore?="
	expect := input
	result := DecodeHeader(input)
	assert.Equal(t, expect, result)
}

// Test mangled termination
func TestInvalidTermination(t *testing.T) {
	input := "=?US-ASCII?Q?Keith_Moore?!"
	expect := input
	result := DecodeHeader(input)
	assert.Equal(t, expect, result)
}

// Try decoding a simple ASCII quoted-printable encoded word
func TestAsciiQ(t *testing.T) {
	input := "=?US-ASCII?Q?Keith_Moore?="
	expect := "Keith Moore"
	result := DecodeHeader(input)
	assert.Equal(t, expect, result)
}

// Try decoding a simple ASCII quoted-printable encoded word
func TestAsciiB64(t *testing.T) {
	input := "=?US-ASCII?B?SGVsbG8gV29ybGQ=?="
	expect := "Hello World"
	result := DecodeHeader(input)
	assert.Equal(t, expect, result)
}

// Try decoding an embedded ASCII quoted-printable encoded word
func TestEmbeddedAsciiQ(t *testing.T) {
	// This is not legal, so we expect it to fail
	input := "ab=?US-ASCII?Q?Keith_Moore?=CD"
	expect := input
	result := DecodeHeader(input)
	assert.Equal(t, expect, result)

	// Abutting a MIME header comment is legal
	input = "(=?US-ASCII?Q?Keith_Moore?=)"
	expect = "(Keith Moore)"
	result = DecodeHeader(input)
	assert.Equal(t, expect, result)
}

// Spacing rules from RFC 2047
func TestSpacing(t *testing.T) {
	var testTable = []struct {
		input, expect string
	}{
		{"(=?ISO-8859-1?Q?a?=)", "(a)"},
		{"(=?ISO-8859-1?Q?a?= b)", "(a b)"},
		{"(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)", "(ab)"},
		{"(=?ISO-8859-1?Q?a?=  =?ISO-8859-1?Q?b?=)", "(ab)"},
		{"(=?ISO-8859-1?Q?a?=\r\n  =?ISO-8859-1?Q?b?=)", "(ab)"},
		{"(=?ISO-8859-1?Q?a_b?=)", "(a b)"},
		{"(=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=)", "(a b)"},
	}

	for _, tt := range testTable {
		result := DecodeHeader(tt.input)
		assert.Equal(t, tt.expect, result,
			"Expected %q, got %q for input %q", tt.expect, result, tt.input)
	}
}

// Test some different character sets
func TestCharsets(t *testing.T) {
	var testTable = []struct {
		input, expect string
	}{
		{"=?utf-8?q?abcABC_=24_=c2=a2_=e2=82=ac?=", "abcABC $ \u00a2 \u20ac"},
		{"=?iso-8859-1?q?#=a3_c=a9_r=ae_u=b5?=", "#\u00a3 c\u00a9 r\u00ae u\u00b5"},
		{"=?big5?q?=a1=5d_=a1=61_=a1=71?=", "\uff08 \uff5b \u3008"},
	}

	for _, tt := range testTable {
		result := DecodeHeader(tt.input)
		assert.Equal(t, tt.expect, result,
			"Expected %q, got %q for input %q", tt.expect, result, tt.input)
	}
}

// Test re-encoding to base64
func TestDecodeToUTF8Base64Header(t *testing.T) {
	var testTable = []struct {
		input, expect string
	}{
		{"no encoding", "no encoding"},
		{"=?utf-8?q?abcABC_=24_=c2=a2_=e2=82=ac?=", "=?UTF-8?B?YWJjQUJDICQgwqIg4oKs?="},
		{"=?iso-8859-1?q?#=a3_c=a9_r=ae_u=b5?=", "=?UTF-8?B?I8KjIGPCqSBywq4gdcK1?="},
		{"=?big5?q?=a1=5d_=a1=61_=a1=71?=", "=?UTF-8?B?77yIIO+9myDjgIg=?="},
	}

	for _, tt := range testTable {
		result := DecodeToUTF8Base64Header(tt.input)
		assert.Equal(t, tt.expect, result,
			"Expected %q, got %q for input %q", tt.expect, result, tt.input)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/abdulkadiryaman/go-exp/newmath"
)
func main() {
	fmt.Printf("Hello, world.  Sqrt(2) = %v\n", newmath.Sqrt(2))
}

<commit_msg>minor changes and indentation fix<commit_after>package main

import (
	"fmt"

	"github.com/abdulkadiryaman/go-exp/newmath"
)

	func main() {
		fmt.Printf("Sqrt(2) = %v\n", newmath.Sqrt(2))
	}

<|endoftext|>


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

package wiringpi

import (
	"fmt"
	"os"
)

var (
	board2pin = []int{
		-1,
		-1,
		-1,
		8,
		-1,
		9,
		-1,
		7,
		15,
		-1,
		16,
		0,
		1,
		2,
		-1,
		-1,
		4,
		-1,
		5,
		12,
		-1,
		13,
		6,
		14,
		10,
		-1,
		11,
	}
	gpio2pin = []int{
		8,
		9,
		-1,
		-1,
		7,
		-1,
		-1,
		11,
		10,
		13,
		12,
		14,
		-1,
		-1,
		15,
		16,
		-1,
		0,
		1,
		-1,
		-1,
		2,
		3,
		4,
		5,
		6,
		-1,
		-1,
		17,
		18,
		19,
		20,
	}

	gpioModes = []string{"IN", "OUT", "ALT5", "ALT4", "ALT0", "ALT1", "ALT2", "ALT3"}
)

//noinspection GoUnusedConst
const (
	PIN_GPIO_0  = 0
	PIN_GPIO_1  = 1
	PIN_GPIO_2  = 2
	PIN_GPIO_3  = 3
	PIN_GPIO_4  = 4
	PIN_GPIO_5  = 5
	PIN_GPIO_6  = 6
	PIN_GPIO_7  = 7
	PIN_SDA     = 8
	PIN_SCL     = 9
	PIN_CE0     = 10
	PIN_CE1     = 11
	PIN_MOSI    = 12
	PIN_MOSO    = 13
	PIN_SCLK    = 14
	PIN_TXD     = 15
	PIN_RXD     = 16
	PIN_GPIO_8  = 17
	PIN_GPIO_9  = 18
	PIN_GPIO_10 = 19
	PIN_GPIO_11 = 20

	MODE_IN   = 0
	MODE_OUT  = 1
	MODE_ALT5 = 2
	MODE_ALT4 = 3
	MODE_ALT0 = 4
	MODE_ALT1 = 5
	MODE_ALT2 = 6
	MODE_ALT3 = 7
)

//use RPi.GPIO's BOARD numbering
//noinspection GoUnusedExportedFunction
func BoardToPin(pin int) int {
	if pin < 1 || pin >= len(board2pin) {
		panic(fmt.Sprintf("Invalid board pin number: %d", pin))
	}
	return board2pin[pin]
}

//noinspection GoUnusedExportedFunction
func GpioToPin(pin int) int {
	if pin < 0 || pin >= len(gpio2pin) {
		panic(fmt.Sprintf("Invalid bcm gpio number: %d", pin))
	}
	return gpio2pin[pin]
}

//noinspection GoUnusedExportedFunction
func PinToGpio(pin int) int {
	return internalPinToGpio(pin)
}

// This initialises wiringPi and assumes that the calling program is going to be using the wiringPi pin numbering scheme.
// This is a simplified numbering scheme which provides a mapping from virtual pin numbers 0 through 16 to the real
// underlying Broadcom GPIO pin numbers. See the pins page for a table which maps the wiringPi pin number to the
// Broadcom GPIO pin number to the physical location on the edge connector.
//
// This function needs to be called with root privileges.
//
//noinspection GoUnusedExportedFunction
func Setup() int {
	return internalSetup()
}

//This is identical to above, however it allows the calling programs to use the Broadcom GPIO pin numbers
// directly with no re-mapping.
//
// As above, this function needs to be called with root privileges, and note that some pins are different
// from revision 1 to revision 2 boards.
//
//noinspection GoUnusedExportedFunction
func SetupGpio() int {
	return internalSetupGpio()
}

// Identical to above, however it allows the calling programs to use the physical pin numbers on the P1 connector only.
//
// As above, this function needs to be called with root privileges.
//
//noinspection GoUnusedExportedFunction
func SetupPhys() int {
	return internalSetupPhys()
}

// This initialises wiringPi but uses the /sys/class/gpio interface rather than accessing the hardware directly.
// This can be called as a non-root user provided the GPIO pins have been exported before-hand using the gpio program.
// Pin numbering in this mode is the native Broadcom GPIO numbers – the same as wiringPiSetupGpio() above, so be
// aware of the differences between Rev 1 and Rev 2 boards.
//
// Note: In this mode you can only use the pins which have been exported via the /sys/class/gpio interface
// before you run your program. You can do this in a separate shell-script, or by using the system() function
// from inside your program to call the gpio program.
//
//Also note that some functions have no effect when using this mode as they’re not currently possible to action unless called with root privileges. (although you can use system() to call gpio to set/change modes if needed)
//
//noinspection GoUnusedExportedFunction
func SetupSys() int {
	return internalSetupSys()
}

// This sets the mode of a pin to either INPUT, OUTPUT, PWM_OUTPUT or GPIO_CLOCK. Note that only wiringPi pin 1
// (BCM_GPIO 18) supports PWM output and only wiringPi pin 7 (BCM_GPIO 4) supports CLOCK output modes.
//
// This function has no effect when in Sys mode. If you need to change the pin mode, then you can do it with the
// gpio program in a script before you start your program.
//
//noinspection GoUnusedExportedFunction
func PinMode(pin int, mode int) {
	internalPinMode(pin, mode)
}

// This sets the pull-up or pull-down resistor mode on the given pin, which should be set as an input. Unlike the
// Arduino, the BCM2835 has both pull-up an down internal resistors. The parameter pud should be; PUD_OFF, (no pull up/down), PUD_DOWN (pull to ground) or PUD_UP (pull to 3.3v) The internal pull up/down resistors have a value of approximately 50KΩ on the Raspberry Pi.
//
// This function has no effect on the Raspberry Pi’s GPIO pins when in Sys mode. If you need to activate a
// pull-up/pull-down, then you can do it with the gpio program in a script before you start your program.
//
//noinspection GoUnusedExportedFunction
func PullUpDnControl(pin int, pud int) {
	internalPullUpDnControl(pin, pud)
}

//Writes the value HIGH or LOW (1 or 0) to the given pin which must have been previously set as an output.
//
//WiringPi treats any non-zero number as HIGH, however 0 is the only representation of LOW.
//
//noinspection GoUnusedExportedFunction
func DigitalWrite(pin int, mode int) {
	internalDigitalWrite(pin, mode)
}

// Writes the value to the PWM register for the given pin. The Raspberry Pi has one on-board PWM pin, pin 1
// (BMC_GPIO 18, Phys 12) and the range is 0-1024. Other PWM devices may have other PWM ranges.
//
// This function is not able to control the Pi’s on-board PWM when in Sys mode.
//
//noinspection GoUnusedExportedFunction
func PwmWrite(pin int, value int) {
	internalPwmWrite(pin, value)
}

//noinspection GoUnusedExportedFunction
func DigitalRead(pin int) int {
	return internalDigitalRead(pin)
}

//noinspection GoUnusedExportedFunction
func DigitalReadStr(pin int) string {
	if internalDigitalRead(pin) == LOW {
		return "LOW"
	}
	return "HIGH"
}

func GetMode(pin int) int {
	return internalGetMode(pin)
}

//noinspection GoUnusedExportedFunction
func GetModeStr(pin int) string {
	var mode = internalGetMode(pin)

	if mode > len(gpioModes) {
		return "INVALID"
	}

	return gpioModes[GetMode(pin)]
}

//noinspection GoUnusedExportedFunction
func Delay(ms int) {
	internalDelay(ms)
}

//noinspection GoUnusedExportedFunction
func DelayMicroseconds(microSec int) {
	internalDelayMicroseconds(microSec)
}

//noinspection GoUnusedExportedFunction
func WiringISR(pin int, mode int) chan int {
	return internalWiringISR(pin, mode)
}

//noinspection GoUnusedExportedFunction
func IsRaspberryPi() bool {
	_, err := os.Stat("/opt/vc/include/bcm_host.h")
	return !os.IsNotExist(err)
}

//noinspection GoUnusedExportedFunction
func SetupI2C(devId int) int {
	return internalSetupI2C(devId)
}

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

package wiringpi

/*
#include <time.h>
#include <stdint.h>

uint64_t as_nanoseconds(struct timespec* ts) {
    return ts->tv_sec * (uint64_t)1000000000L + ts->tv_nsec;
}

uint64_t monotonic_time() {
    struct timespec last_t;
    clock_gettime(CLOCK_MONOTONIC, &last_t);
    return as_nanoseconds(&last_t);
}
*/
import "C"
import (
	"fmt"
	"os"
)

var (
	board2pin = []int{
		-1,
		-1,
		-1,
		8,
		-1,
		9,
		-1,
		7,
		15,
		-1,
		16,
		0,
		1,
		2,
		-1,
		-1,
		4,
		-1,
		5,
		12,
		-1,
		13,
		6,
		14,
		10,
		-1,
		11,
	}
	gpio2pin = []int{
		8,
		9,
		-1,
		-1,
		7,
		-1,
		-1,
		11,
		10,
		13,
		12,
		14,
		-1,
		-1,
		15,
		16,
		-1,
		0,
		1,
		-1,
		-1,
		2,
		3,
		4,
		5,
		6,
		-1,
		-1,
		17,
		18,
		19,
		20,
	}

	gpioModes = []string{"IN", "OUT", "ALT5", "ALT4", "ALT0", "ALT1", "ALT2", "ALT3"}
)

//noinspection GoUnusedConst
const (
	PIN_GPIO_0  = 0
	PIN_GPIO_1  = 1
	PIN_GPIO_2  = 2
	PIN_GPIO_3  = 3
	PIN_GPIO_4  = 4
	PIN_GPIO_5  = 5
	PIN_GPIO_6  = 6
	PIN_GPIO_7  = 7
	PIN_SDA     = 8
	PIN_SCL     = 9
	PIN_CE0     = 10
	PIN_CE1     = 11
	PIN_MOSI    = 12
	PIN_MOSO    = 13
	PIN_SCLK    = 14
	PIN_TXD     = 15
	PIN_RXD     = 16
	PIN_GPIO_8  = 17
	PIN_GPIO_9  = 18
	PIN_GPIO_10 = 19
	PIN_GPIO_11 = 20

	MODE_IN   = 0
	MODE_OUT  = 1
	MODE_ALT5 = 2
	MODE_ALT4 = 3
	MODE_ALT0 = 4
	MODE_ALT1 = 5
	MODE_ALT2 = 6
	MODE_ALT3 = 7
)

//use RPi.GPIO's BOARD numbering
//noinspection GoUnusedExportedFunction
func BoardToPin(pin int) int {
	if pin < 1 || pin >= len(board2pin) {
		panic(fmt.Sprintf("Invalid board pin number: %d", pin))
	}
	return board2pin[pin]
}

//noinspection GoUnusedExportedFunction
func GpioToPin(pin int) int {
	if pin < 0 || pin >= len(gpio2pin) {
		panic(fmt.Sprintf("Invalid bcm gpio number: %d", pin))
	}
	return gpio2pin[pin]
}

//noinspection GoUnusedExportedFunction
func PinToGpio(pin int) int {
	return internalPinToGpio(pin)
}

// This initialises wiringPi and assumes that the calling program is going to be using the wiringPi pin numbering scheme.
// This is a simplified numbering scheme which provides a mapping from virtual pin numbers 0 through 16 to the real
// underlying Broadcom GPIO pin numbers. See the pins page for a table which maps the wiringPi pin number to the
// Broadcom GPIO pin number to the physical location on the edge connector.
//
// This function needs to be called with root privileges.
//
//noinspection GoUnusedExportedFunction
func Setup() int {
	return internalSetup()
}

//This is identical to above, however it allows the calling programs to use the Broadcom GPIO pin numbers
// directly with no re-mapping.
//
// As above, this function needs to be called with root privileges, and note that some pins are different
// from revision 1 to revision 2 boards.
//
//noinspection GoUnusedExportedFunction
func SetupGpio() int {
	return internalSetupGpio()
}

// Identical to above, however it allows the calling programs to use the physical pin numbers on the P1 connector only.
//
// As above, this function needs to be called with root privileges.
//
//noinspection GoUnusedExportedFunction
func SetupPhys() int {
	return internalSetupPhys()
}

// This initialises wiringPi but uses the /sys/class/gpio interface rather than accessing the hardware directly.
// This can be called as a non-root user provided the GPIO pins have been exported before-hand using the gpio program.
// Pin numbering in this mode is the native Broadcom GPIO numbers – the same as wiringPiSetupGpio() above, so be
// aware of the differences between Rev 1 and Rev 2 boards.
//
// Note: In this mode you can only use the pins which have been exported via the /sys/class/gpio interface
// before you run your program. You can do this in a separate shell-script, or by using the system() function
// from inside your program to call the gpio program.
//
//Also note that some functions have no effect when using this mode as they’re not currently possible to action unless called with root privileges. (although you can use system() to call gpio to set/change modes if needed)
//
//noinspection GoUnusedExportedFunction
func SetupSys() int {
	return internalSetupSys()
}

// This sets the mode of a pin to either INPUT, OUTPUT, PWM_OUTPUT or GPIO_CLOCK. Note that only wiringPi pin 1
// (BCM_GPIO 18) supports PWM output and only wiringPi pin 7 (BCM_GPIO 4) supports CLOCK output modes.
//
// This function has no effect when in Sys mode. If you need to change the pin mode, then you can do it with the
// gpio program in a script before you start your program.
//
//noinspection GoUnusedExportedFunction
func PinMode(pin int, mode int) {
	internalPinMode(pin, mode)
}

// This sets the pull-up or pull-down resistor mode on the given pin, which should be set as an input. Unlike the
// Arduino, the BCM2835 has both pull-up an down internal resistors. The parameter pud should be; PUD_OFF, (no pull up/down), PUD_DOWN (pull to ground) or PUD_UP (pull to 3.3v) The internal pull up/down resistors have a value of approximately 50KΩ on the Raspberry Pi.
//
// This function has no effect on the Raspberry Pi’s GPIO pins when in Sys mode. If you need to activate a
// pull-up/pull-down, then you can do it with the gpio program in a script before you start your program.
//
//noinspection GoUnusedExportedFunction
func PullUpDnControl(pin int, pud int) {
	internalPullUpDnControl(pin, pud)
}

//Writes the value HIGH or LOW (1 or 0) to the given pin which must have been previously set as an output.
//
//WiringPi treats any non-zero number as HIGH, however 0 is the only representation of LOW.
//
//noinspection GoUnusedExportedFunction
func DigitalWrite(pin int, mode int) {
	internalDigitalWrite(pin, mode)
}

// Writes the value to the PWM register for the given pin. The Raspberry Pi has one on-board PWM pin, pin 1
// (BMC_GPIO 18, Phys 12) and the range is 0-1024. Other PWM devices may have other PWM ranges.
//
// This function is not able to control the Pi’s on-board PWM when in Sys mode.
//
//noinspection GoUnusedExportedFunction
func PwmWrite(pin int, value int) {
	internalPwmWrite(pin, value)
}

//noinspection GoUnusedExportedFunction
func DigitalRead(pin int) int {
	return internalDigitalRead(pin)
}

//noinspection GoUnusedExportedFunction
func DigitalReadStr(pin int) string {
	if internalDigitalRead(pin) == LOW {
		return "LOW"
	}
	return "HIGH"
}

func GetMode(pin int) int {
	return internalGetMode(pin)
}

//noinspection GoUnusedExportedFunction
func GetModeStr(pin int) string {
	var mode = internalGetMode(pin)

	if mode > len(gpioModes) {
		return "INVALID"
	}

	return gpioModes[GetMode(pin)]
}

//noinspection GoUnusedExportedFunction
func Delay(ms int) {
	internalDelay(ms)
}

//noinspection GoUnusedExportedFunction
func DelayMicroseconds(microSec int) {
	internalDelayMicroseconds(microSec)
}

//noinspection GoUnusedExportedFunction
func WiringISR(pin int, mode int) chan int {
	return internalWiringISR(pin, mode)
}

//noinspection GoUnusedExportedFunction
func IsRaspberryPi() bool {
	_, err := os.Stat("/opt/vc/include/bcm_host.h")
	return !os.IsNotExist(err)
}

//noinspection GoUnusedExportedFunction
func SetupI2C(devId int) int {
	return internalSetupI2C(devId)
}

//noinspection GoUnusedExportedFunction
func I2cRead(fd int) int {
	return internalI2CRead(fd)
}

//noinspection GoUnusedExportedFunction
func MonotonicTime() uint64 {
	return C.monotonic_time()
}
<|endoftext|>