<commit_before>package dbo

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"

	"github.com/byuoitav/authmiddleware/bearertoken"
	"github.com/byuoitav/configuration-database-microservice/accessors"
)

// GetData will run a get on the url, and attempt to fill the interface provided from the returned JSON.
func GetData(url string, structToFill interface{}) error {
	log.Printf("Getting data from URL: %s...", url)
	// Make an HTTP client so we can add custom headers (currently used for adding in the Bearer token for inter-microservice communication)

	client := &http.Client{}
	req, _ := http.NewRequest("GET", url, nil)

	if len(os.Getenv("LOCAL_ENVIRONMENT")) == 0 {

		log.Printf("Adding the bearer token for inter-service communication")

		token, err := bearertoken.GetToken()
		if err != nil {
			return err
		}

		req.Header.Set("Authorization", "Bearer "+token.Token)
	}

	resp, err := client.Do(req)
	if err != nil {
		return err
	}

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(b, structToFill)
	if err != nil {
		return err
	}
	log.Printf("Done.")
	return nil
}

// GetAllRawCommands retrieves all the commands
func GetAllRawCommands() (commands []accessors.RawCommand, err error) {
	log.Printf("Getting all commands.")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/commands"
	err = GetData(url, &commands)

	if err != nil {
		log.Printf("Error: %s", err.Error())
		return
	}

	log.Printf("Done.")
	return
}

// GetRoomByInfo simply retrieves a device's information from the databse.
func GetRoomByInfo(buildingName string, roomName string) (toReturn accessors.Room, err error) {
	log.Printf("Getting room %s in building %s...", roomName, buildingName)
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+buildingName+"/rooms/"+roomName, &toReturn)
	return
}

// GetDeviceByName simply retrieves a device's information from the databse.
func GetDeviceByName(buildingName string, roomName string, deviceName string) (toReturn accessors.Device, err error) {
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+buildingName+"/rooms/"+roomName+"/devices/"+deviceName, &toReturn)
	return
}

// GetDevicesByRoom will jut get the devices based on the room.
func GetDevicesByRoom(buildingName string, roomName string) (toReturn []accessors.Device, err error) {
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+buildingName+"/rooms/"+roomName+"/devices", &toReturn)
	return
}

// GetDevicesByBuildingAndRoomAndRole will get the devices with the given role from the DB
func GetDevicesByBuildingAndRoomAndRole(building string, room string, roleName string) (toReturn []accessors.Device, err error) {
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+building+"/rooms/"+room+"/devices/roles/"+roleName, &toReturn)
	return
}

// SetAudioInDB will set the audio levels in the database
func SetAudioInDB(building string, room string, device accessors.Device) error {
	log.Printf("Updating audio levels in DB.")

	if device.Volume != nil {
		url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + building + "/rooms/" + room + "/devices/" + device.Name + "/attributes/volume/" + strconv.Itoa(*device.Volume)
		request, err := http.NewRequest("PUT", url, nil)
		client := &http.Client{}
		_, err = client.Do(request)

		if err != nil {
			return err
		}
	}

	if device.Muted != nil {
		url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + building + "/rooms/" + room + "/devices/" + device.Name + "/attributes/muted/" + strconv.FormatBool(*device.Muted)
		request, err := http.NewRequest("PUT", url, nil)
		client := &http.Client{}
		_, err = client.Do(request)

		if err != nil {
			return err
		}
	}

	return nil
}

// getBuildings will return all buildings
func GetBuildings() ([]accessors.Building, error) {
	log.Printf("getting all buildings")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings"
	var buildings []accessors.Building
	err := GetData(url, &buildings)

	return buildings, err
}
<commit_msg>added function to get rooms by building to dbo package<commit_after>package dbo

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"

	"github.com/byuoitav/authmiddleware/bearertoken"
	"github.com/byuoitav/configuration-database-microservice/accessors"
)

// GetData will run a get on the url, and attempt to fill the interface provided from the returned JSON.
func GetData(url string, structToFill interface{}) error {
	log.Printf("Getting data from URL: %s...", url)
	// Make an HTTP client so we can add custom headers (currently used for adding in the Bearer token for inter-microservice communication)

	client := &http.Client{}
	req, _ := http.NewRequest("GET", url, nil)

	if len(os.Getenv("LOCAL_ENVIRONMENT")) == 0 {

		log.Printf("Adding the bearer token for inter-service communication")

		token, err := bearertoken.GetToken()
		if err != nil {
			return err
		}

		req.Header.Set("Authorization", "Bearer "+token.Token)
	}

	resp, err := client.Do(req)
	if err != nil {
		return err
	}

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(b, structToFill)
	if err != nil {
		return err
	}
	log.Printf("Done.")
	return nil
}

// GetAllRawCommands retrieves all the commands
func GetAllRawCommands() (commands []accessors.RawCommand, err error) {
	log.Printf("Getting all commands.")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/commands"
	err = GetData(url, &commands)

	if err != nil {
		log.Printf("Error: %s", err.Error())
		return
	}

	log.Printf("Done.")
	return
}

// GetRoomByInfo simply retrieves a device's information from the databse.
func GetRoomByInfo(buildingName string, roomName string) (toReturn accessors.Room, err error) {
	log.Printf("Getting room %s in building %s...", roomName, buildingName)
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+buildingName+"/rooms/"+roomName, &toReturn)
	return
}

// GetDeviceByName simply retrieves a device's information from the databse.
func GetDeviceByName(buildingName string, roomName string, deviceName string) (toReturn accessors.Device, err error) {
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+buildingName+"/rooms/"+roomName+"/devices/"+deviceName, &toReturn)
	return
}

// GetDevicesByRoom will jut get the devices based on the room.
func GetDevicesByRoom(buildingName string, roomName string) (toReturn []accessors.Device, err error) {
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+buildingName+"/rooms/"+roomName+"/devices", &toReturn)
	return
}

// GetDevicesByBuildingAndRoomAndRole will get the devices with the given role from the DB
func GetDevicesByBuildingAndRoomAndRole(building string, room string, roleName string) (toReturn []accessors.Device, err error) {
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+building+"/rooms/"+room+"/devices/roles/"+roleName, &toReturn)
	return
}

// SetAudioInDB will set the audio levels in the database
func SetAudioInDB(building string, room string, device accessors.Device) error {
	log.Printf("Updating audio levels in DB.")

	if device.Volume != nil {
		url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + building + "/rooms/" + room + "/devices/" + device.Name + "/attributes/volume/" + strconv.Itoa(*device.Volume)
		request, err := http.NewRequest("PUT", url, nil)
		client := &http.Client{}
		_, err = client.Do(request)

		if err != nil {
			return err
		}
	}

	if device.Muted != nil {
		url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + building + "/rooms/" + room + "/devices/" + device.Name + "/attributes/muted/" + strconv.FormatBool(*device.Muted)
		request, err := http.NewRequest("PUT", url, nil)
		client := &http.Client{}
		_, err = client.Do(request)

		if err != nil {
			return err
		}
	}

	return nil
}

// GetBuildings will return all buildings
func GetBuildings() ([]accessors.Building, error) {
	log.Printf("getting all buildings...")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings"
	var buildings []accessors.Building
	err := GetData(url, &buildings)

	return buildings, err
}

// GetRooms returns all the rooms in a given building
func GetRoomsByBuilding(building string) ([]accessors.Room, error) {
	log.Printf("getting all rooms from %v ...", building)
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + building + "/rooms"
	var rooms []accessors.Room
	err := GetData(url, &rooms)
	return rooms, err
}
<|endoftext|>


<commit_before>package models

import (
	"time"
)

type Recruit struct {
	EncodedKey     string    `datastore:"-" json:"key,omitempty"`
	Name           string    `datastore:"name" json:"name,omitempty"`
	Email          string    `datastore:"email" json:"email,omitempty"`
	Year           int       `datastore:"year" json:"year,omitempty"`
	Major          string    `datastore:"major" json:"major,omitempty"` //Possibly make this a dropdown with enumerated majors
	DateRegistered time.Time `datastore:"date_registered" json:"date_registered,omitempty"`
	// interest_level  int
}

func (r *Recruit) DatastoreKind() string {
	return "recruit"
}
<commit_msg>Add gender support<commit_after>package models

import (
	"time"
)

type Recruit struct {
	EncodedKey     string    `datastore:"-" json:"key,omitempty"`
	Name           string    `datastore:"name" json:"name,omitempty"`
	Email          string    `datastore:"email" json:"email,omitempty"`
	Year           int       `datastore:"year" json:"year,omitempty"`
	Major          string    `datastore:"major" json:"major,omitempty"` //Possibly make this a dropdown with enumerated majors
	IsMale         bool      `datastore:"male" json:"male,omitempty"`
	DateRegistered time.Time `datastore:"date_registered" json:"date_registered,omitempty"`
	// interest_level  int
}

func (r *Recruit) DatastoreKind() string {
	return "recruit"
}
<|endoftext|>


<commit_before>// Copyright 2014 The Azul3D 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 wav

import (
	"encoding/binary"
	"errors"
	"io"
	"sync"
	"unsafe"

	"azul3d.org/audio.v1"
)

const (
	// Data format codes

	// PCM
	wave_FORMAT_PCM = 0x0001

	// IEEE float
	wave_FORMAT_IEEE_FLOAT = 0x0003

	// 8-bit ITU-T G.711 A-law
	wave_FORMAT_ALAW = 0x0006

	// 8-bit ITU-T G.711 µ-law
	wave_FORMAT_MULAW = 0x0007

	// Determined by SubFormat
	wave_FORMAT_EXTENSIBLE = 0xFFFE
)

type decoder struct {
	access sync.RWMutex

	format, bitsPerSample   uint16
	chunkSize, currentCount uint32
	dataChunkBegin          int32

	r      interface{}
	rd     io.Reader
	config *audio.Config
}

func (d *decoder) bRead(data interface{}, sz uintptr) error {
	d.currentCount += uint32(sz)
	if d.chunkSize > 0 {
		if d.currentCount > d.chunkSize {
			return audio.EOS
		}
	} else {
		d.dataChunkBegin += int32(sz)
	}
	return binary.Read(d.rd, binary.LittleEndian, data)
}

// Reads and returns the next RIFF chunk, note that always len(ident) == 4
// E.g.
//
//  "fmt " (notice space).
//
// Length is length of chunk data.
//
// Returns any read errors.
func (d *decoder) nextChunk() (ident string, length uint32, err error) {
	// Read chunk identity, like "RIFF" or "fmt "
	var chunkIdent [4]byte
	err = d.bRead(&chunkIdent, unsafe.Sizeof(chunkIdent))
	if err != nil {
		return "", 0, err
	}
	ident = string(chunkIdent[:])

	// Read chunk length
	err = d.bRead(&length, unsafe.Sizeof(length))
	if err != nil {
		return "", 0, err
	}
	return
}

func (d *decoder) Seek(sample uint64) error {
	rs, ok := d.r.(io.ReadSeeker)
	if ok {
		offset := int64(sample * (uint64(d.bitsPerSample) / 8))
		_, err := rs.Seek(int64(d.dataChunkBegin)+offset, 0)
		if err != nil {
			return err
		}
	}
	return nil
}

func (d *decoder) readPCM8(b audio.Slice) (read int, err error) {
	bb, bbOk := b.(audio.PCM8Samples)

	for read = 0; read < b.Len(); read++ {
		// Pull one sample from the data stream
		var sample audio.PCM8

		err = d.bRead(&sample, unsafe.Sizeof(sample))
		if err != nil {
			return
		}

		if bbOk {
			bb[read] = sample
		} else {
			f64 := audio.PCM8ToF64(sample)
			b.Set(read, f64)
		}
	}

	return
}

func (d *decoder) readPCM16(b audio.Slice) (read int, err error) {
	bb, bbOk := b.(audio.PCM16Samples)

	for read = 0; read < b.Len(); read++ {
		// Pull one sample from the data stream
		var sample audio.PCM16

		err = d.bRead(&sample, unsafe.Sizeof(sample))
		if err != nil {
			return
		}

		if bbOk {
			bb[read] = sample
		} else {
			f64 := audio.PCM16ToF64(sample)
			b.Set(read, f64)
		}
	}

	return
}

func (d *decoder) readPCM24(b audio.Slice) (read int, err error) {
	bb, bbOk := b.(audio.PCM32Samples)

	for read = 0; read < b.Len(); read++ {
		// Pull one sample from the data stream
		var sample [3]uint8

		err = d.bRead(&sample, unsafe.Sizeof(sample))
		if err != nil {
			return
		}

		var ss audio.PCM32
		ss = audio.PCM32(sample[0]) | audio.PCM32(sample[1])<<8 | audio.PCM32(sample[2])<<16
		if (ss & 0x800000) > 0 {
			ss |= ^0xffffff
		}

		if bbOk {
			bb[read] = ss
		} else {
			f64 := audio.PCM32ToF64(ss)
			b.Set(read, f64)
		}
	}

	return
}

func (d *decoder) readPCM32(b audio.Slice) (read int, err error) {
	bb, bbOk := b.(audio.PCM32Samples)

	for read = 0; read < b.Len(); read++ {
		// Pull one sample from the data stream
		var sample audio.PCM32

		err = d.bRead(&sample, unsafe.Sizeof(sample))
		if err != nil {
			return
		}

		if bbOk {
			bb[read] = sample
		} else {
			f64 := audio.PCM32ToF64(sample)
			b.Set(read, f64)
		}
	}

	return
}

func (d *decoder) readF32(b audio.Slice) (read int, err error) {
	bb, bbOk := b.(audio.F32Samples)

	for read = 0; read < b.Len(); read++ {
		// Pull one sample from the data stream
		var sample audio.F32

		err = d.bRead(&sample, unsafe.Sizeof(sample))
		if err != nil {
			return
		}

		if bbOk {
			bb[read] = sample
		} else {
			b.Set(read, audio.F64(sample))
		}
	}

	return
}

func (d *decoder) readF64(b audio.Slice) (read int, err error) {
	for read = 0; read < b.Len(); read++ {
		// Pull one sample from the data stream
		var sample audio.F64

		err = d.bRead(&sample, unsafe.Sizeof(sample))
		if err != nil {
			return
		}

		b.Set(read, sample)
	}

	return
}

func (d *decoder) readMuLaw(b audio.Slice) (read int, err error) {
	bb, bbOk := b.(audio.MuLawSamples)

	for read = 0; read < b.Len(); read++ {
		// Pull one sample from the data stream
		var sample audio.MuLaw

		err = d.bRead(&sample, unsafe.Sizeof(sample))
		if err != nil {
			return
		}

		if bbOk {
			bb[read] = sample
		} else {
			p16 := audio.MuLawToPCM16(sample)
			b.Set(read, audio.PCM16ToF64(p16))
		}
	}

	return
}

func (d *decoder) readALaw(b audio.Slice) (read int, err error) {
	bb, bbOk := b.(audio.ALawSamples)

	for read = 0; read < b.Len(); read++ {
		// Pull one sample from the data stream
		var sample audio.ALaw

		err = d.bRead(&sample, unsafe.Sizeof(sample))
		if err != nil {
			return
		}

		if bbOk {
			bb[read] = sample
		} else {
			p16 := audio.ALawToPCM16(sample)
			b.Set(read, audio.PCM16ToF64(p16))
		}
	}

	return
}

func (d *decoder) Read(b audio.Slice) (read int, err error) {
	if b.Len() == 0 {
		return
	}

	d.access.Lock()
	defer d.access.Unlock()

	switch d.format {
	case wave_FORMAT_PCM:
		switch d.bitsPerSample {
		case 8:
			return d.readPCM8(b)
		case 16:
			return d.readPCM16(b)
		case 24:
			return d.readPCM24(b)
		case 32:
			return d.readPCM32(b)
		}

	case wave_FORMAT_IEEE_FLOAT:
		switch d.bitsPerSample {
		case 32:
			return d.readF32(b)
		case 64:
			return d.readF64(b)
		}

	case wave_FORMAT_MULAW:
		return d.readMuLaw(b)

	case wave_FORMAT_ALAW:
		return d.readALaw(b)
	}
	return
}

func (d *decoder) Config() audio.Config {
	d.access.RLock()
	defer d.access.RUnlock()

	return *d.config
}

// ErrUnsupported defines an error for decoding wav data that is valid (by the
// wave specification) but not supported by the decoder in this package.
//
// This error only happens for audio files containing extensible wav data.
var ErrUnsupported = errors.New("wav: data format is valid but not supported by decoder")

func to32(b []byte) uint32 {
	return binary.LittleEndian.Uint32(b)
}

func to16(b []byte) uint16 {
	return binary.LittleEndian.Uint16(b)
}

// NewDecoder returns a new initialized audio decoder for the io.Reader or
// io.ReadSeeker, r.
func newDecoder(r interface{}) (audio.Decoder, error) {
	d := new(decoder)
	d.r = r

	switch t := r.(type) {
	case io.Reader:
		d.rd = t
	case io.ReadSeeker:
		d.rd = io.Reader(t)
	default:
		panic("NewDecoder(): Invalid reader type; must be io.Reader or io.ReadSeeker!")
	}

	var (
		complete bool

		c16 fmtChunk16
		c18 fmtChunk18
		c40 fmtChunk40
	)
	for !complete {
		ident, length, err := d.nextChunk()
		if err != nil {
			return nil, err
		}

		switch ident {
		case "RIFF":
			var format [4]byte
			err = d.bRead(&format, unsafe.Sizeof(format))
			if string(format[:]) != "WAVE" {
				return nil, audio.ErrInvalidData
			}

		case "fmt ":
			// Always contains the 16-byte chunk
			err = d.bRead(&c16, unsafe.Sizeof(c16))
			if err != nil {
				return nil, err
			}
			d.bitsPerSample = to16(c16.BitsPerSample[:])

			// Sometimes contains extensive 18/40 total byte chunks
			switch length {
			case 18:
				err = d.bRead(&c18, unsafe.Sizeof(c18))
				if err != nil {
					return nil, err
				}
			case 40:
				err = d.bRead(&c40, unsafe.Sizeof(c40))
				if err != nil {
					return nil, err
				}
			}

			// Verify format tag
			ft := to16(c16.FormatTag[:])
			switch {
			case ft == wave_FORMAT_PCM && (d.bitsPerSample == 8 || d.bitsPerSample == 16 || d.bitsPerSample == 24 || d.bitsPerSample == 32):
				break
			case ft == wave_FORMAT_IEEE_FLOAT && (d.bitsPerSample == 32 || d.bitsPerSample == 64):
				break
			case ft == wave_FORMAT_ALAW && d.bitsPerSample == 8:
				break
			case ft == wave_FORMAT_MULAW && d.bitsPerSample == 8:
				break
			// We don't support extensible wav files
			//case wave_FORMAT_EXTENSIBLE:
			//	break
			default:
				return nil, ErrUnsupported
			}

			// Assign format tag for later (See Read() method)
			d.format = to16(c16.FormatTag[:])

			// We now have enough information to build the audio configuration
			d.config = &audio.Config{
				Channels:   int(to16(c16.Channels[:])),
				SampleRate: int(to32(c16.SamplesPerSec[:])),
			}

		case "fact":
			// We need to scan fact chunk first.
			var fact factChunk
			err = d.bRead(&fact, unsafe.Sizeof(fact))
			if err != nil {
				return nil, err
			}

		case "data":
			// Read the data chunk header now
			d.chunkSize = length
			complete = true
		}
	}

	return d, nil
}

func init() {
	audio.RegisterFormat("wav", "RIFF", newDecoder)
}
<commit_msg>Use binary.Size instead of unsafe.Sizeof; Fixes #4<commit_after>// Copyright 2014 The Azul3D 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 wav

import (
	"encoding/binary"
	"errors"
	"io"
	"sync"

	"azul3d.org/audio.v1"
)

const (
	// Data format codes

	// PCM
	wave_FORMAT_PCM = 0x0001

	// IEEE float
	wave_FORMAT_IEEE_FLOAT = 0x0003

	// 8-bit ITU-T G.711 A-law
	wave_FORMAT_ALAW = 0x0006

	// 8-bit ITU-T G.711 µ-law
	wave_FORMAT_MULAW = 0x0007

	// Determined by SubFormat
	wave_FORMAT_EXTENSIBLE = 0xFFFE
)

type decoder struct {
	access sync.RWMutex

	format, bitsPerSample   uint16
	chunkSize, currentCount uint32
	dataChunkBegin          int32

	r      interface{}
	rd     io.Reader
	config *audio.Config
}

func (d *decoder) bRead(data interface{}, sz int) error {
	d.currentCount += uint32(sz)
	if d.chunkSize > 0 {
		if d.currentCount > d.chunkSize {
			return audio.EOS
		}
	} else {
		d.dataChunkBegin += int32(sz)
	}
	return binary.Read(d.rd, binary.LittleEndian, data)
}

// Reads and returns the next RIFF chunk, note that always len(ident) == 4
// E.g.
//
//  "fmt " (notice space).
//
// Length is length of chunk data.
//
// Returns any read errors.
func (d *decoder) nextChunk() (ident string, length uint32, err error) {
	// Read chunk identity, like "RIFF" or "fmt "
	var chunkIdent [4]byte
	err = d.bRead(&chunkIdent, binary.Size(chunkIdent))
	if err != nil {
		return "", 0, err
	}
	ident = string(chunkIdent[:])

	// Read chunk length
	err = d.bRead(&length, binary.Size(length))
	if err != nil {
		return "", 0, err
	}
	return
}

func (d *decoder) Seek(sample uint64) error {
	rs, ok := d.r.(io.ReadSeeker)
	if ok {
		offset := int64(sample * (uint64(d.bitsPerSample) / 8))
		_, err := rs.Seek(int64(d.dataChunkBegin)+offset, 0)
		if err != nil {
			return err
		}
	}
	return nil
}

func (d *decoder) readPCM8(b audio.Slice) (read int, err error) {
	bb, bbOk := b.(audio.PCM8Samples)

	for read = 0; read < b.Len(); read++ {
		// Pull one sample from the data stream
		var sample audio.PCM8

		err = d.bRead(&sample, binary.Size(sample))
		if err != nil {
			return
		}

		if bbOk {
			bb[read] = sample
		} else {
			f64 := audio.PCM8ToF64(sample)
			b.Set(read, f64)
		}
	}

	return
}

func (d *decoder) readPCM16(b audio.Slice) (read int, err error) {
	bb, bbOk := b.(audio.PCM16Samples)

	for read = 0; read < b.Len(); read++ {
		// Pull one sample from the data stream
		var sample audio.PCM16

		err = d.bRead(&sample, binary.Size(sample))
		if err != nil {
			return
		}

		if bbOk {
			bb[read] = sample
		} else {
			f64 := audio.PCM16ToF64(sample)
			b.Set(read, f64)
		}
	}

	return
}

func (d *decoder) readPCM24(b audio.Slice) (read int, err error) {
	bb, bbOk := b.(audio.PCM32Samples)

	for read = 0; read < b.Len(); read++ {
		// Pull one sample from the data stream
		var sample [3]uint8

		err = d.bRead(&sample, binary.Size(sample))
		if err != nil {
			return
		}

		var ss audio.PCM32
		ss = audio.PCM32(sample[0]) | audio.PCM32(sample[1])<<8 | audio.PCM32(sample[2])<<16
		if (ss & 0x800000) > 0 {
			ss |= ^0xffffff
		}

		if bbOk {
			bb[read] = ss
		} else {
			f64 := audio.PCM32ToF64(ss)
			b.Set(read, f64)
		}
	}

	return
}

func (d *decoder) readPCM32(b audio.Slice) (read int, err error) {
	bb, bbOk := b.(audio.PCM32Samples)

	for read = 0; read < b.Len(); read++ {
		// Pull one sample from the data stream
		var sample audio.PCM32

		err = d.bRead(&sample, binary.Size(sample))
		if err != nil {
			return
		}

		if bbOk {
			bb[read] = sample
		} else {
			f64 := audio.PCM32ToF64(sample)
			b.Set(read, f64)
		}
	}

	return
}

func (d *decoder) readF32(b audio.Slice) (read int, err error) {
	bb, bbOk := b.(audio.F32Samples)

	for read = 0; read < b.Len(); read++ {
		// Pull one sample from the data stream
		var sample audio.F32

		err = d.bRead(&sample, binary.Size(sample))
		if err != nil {
			return
		}

		if bbOk {
			bb[read] = sample
		} else {
			b.Set(read, audio.F64(sample))
		}
	}

	return
}

func (d *decoder) readF64(b audio.Slice) (read int, err error) {
	for read = 0; read < b.Len(); read++ {
		// Pull one sample from the data stream
		var sample audio.F64

		err = d.bRead(&sample, binary.Size(sample))
		if err != nil {
			return
		}

		b.Set(read, sample)
	}

	return
}

func (d *decoder) readMuLaw(b audio.Slice) (read int, err error) {
	bb, bbOk := b.(audio.MuLawSamples)

	for read = 0; read < b.Len(); read++ {
		// Pull one sample from the data stream
		var sample audio.MuLaw

		err = d.bRead(&sample, binary.Size(sample))
		if err != nil {
			return
		}

		if bbOk {
			bb[read] = sample
		} else {
			p16 := audio.MuLawToPCM16(sample)
			b.Set(read, audio.PCM16ToF64(p16))
		}
	}

	return
}

func (d *decoder) readALaw(b audio.Slice) (read int, err error) {
	bb, bbOk := b.(audio.ALawSamples)

	for read = 0; read < b.Len(); read++ {
		// Pull one sample from the data stream
		var sample audio.ALaw

		err = d.bRead(&sample, binary.Size(sample))
		if err != nil {
			return
		}

		if bbOk {
			bb[read] = sample
		} else {
			p16 := audio.ALawToPCM16(sample)
			b.Set(read, audio.PCM16ToF64(p16))
		}
	}

	return
}

func (d *decoder) Read(b audio.Slice) (read int, err error) {
	if b.Len() == 0 {
		return
	}

	d.access.Lock()
	defer d.access.Unlock()

	switch d.format {
	case wave_FORMAT_PCM:
		switch d.bitsPerSample {
		case 8:
			return d.readPCM8(b)
		case 16:
			return d.readPCM16(b)
		case 24:
			return d.readPCM24(b)
		case 32:
			return d.readPCM32(b)
		}

	case wave_FORMAT_IEEE_FLOAT:
		switch d.bitsPerSample {
		case 32:
			return d.readF32(b)
		case 64:
			return d.readF64(b)
		}

	case wave_FORMAT_MULAW:
		return d.readMuLaw(b)

	case wave_FORMAT_ALAW:
		return d.readALaw(b)
	}
	return
}

func (d *decoder) Config() audio.Config {
	d.access.RLock()
	defer d.access.RUnlock()

	return *d.config
}

// ErrUnsupported defines an error for decoding wav data that is valid (by the
// wave specification) but not supported by the decoder in this package.
//
// This error only happens for audio files containing extensible wav data.
var ErrUnsupported = errors.New("wav: data format is valid but not supported by decoder")

func to32(b []byte) uint32 {
	return binary.LittleEndian.Uint32(b)
}

func to16(b []byte) uint16 {
	return binary.LittleEndian.Uint16(b)
}

// NewDecoder returns a new initialized audio decoder for the io.Reader or
// io.ReadSeeker, r.
func newDecoder(r interface{}) (audio.Decoder, error) {
	d := new(decoder)
	d.r = r

	switch t := r.(type) {
	case io.Reader:
		d.rd = t
	case io.ReadSeeker:
		d.rd = io.Reader(t)
	default:
		panic("NewDecoder(): Invalid reader type; must be io.Reader or io.ReadSeeker!")
	}

	var (
		complete bool

		c16 fmtChunk16
		c18 fmtChunk18
		c40 fmtChunk40
	)
	for !complete {
		ident, length, err := d.nextChunk()
		if err != nil {
			return nil, err
		}

		switch ident {
		case "RIFF":
			var format [4]byte
			err = d.bRead(&format, binary.Size(format))
			if string(format[:]) != "WAVE" {
				return nil, audio.ErrInvalidData
			}

		case "fmt ":
			// Always contains the 16-byte chunk
			err = d.bRead(&c16, binary.Size(c16))
			if err != nil {
				return nil, err
			}
			d.bitsPerSample = to16(c16.BitsPerSample[:])

			// Sometimes contains extensive 18/40 total byte chunks
			switch length {
			case 18:
				err = d.bRead(&c18, binary.Size(c18))
				if err != nil {
					return nil, err
				}
			case 40:
				err = d.bRead(&c40, binary.Size(c40))
				if err != nil {
					return nil, err
				}
			}

			// Verify format tag
			ft := to16(c16.FormatTag[:])
			switch {
			case ft == wave_FORMAT_PCM && (d.bitsPerSample == 8 || d.bitsPerSample == 16 || d.bitsPerSample == 24 || d.bitsPerSample == 32):
				break
			case ft == wave_FORMAT_IEEE_FLOAT && (d.bitsPerSample == 32 || d.bitsPerSample == 64):
				break
			case ft == wave_FORMAT_ALAW && d.bitsPerSample == 8:
				break
			case ft == wave_FORMAT_MULAW && d.bitsPerSample == 8:
				break
			// We don't support extensible wav files
			//case wave_FORMAT_EXTENSIBLE:
			//	break
			default:
				return nil, ErrUnsupported
			}

			// Assign format tag for later (See Read() method)
			d.format = to16(c16.FormatTag[:])

			// We now have enough information to build the audio configuration
			d.config = &audio.Config{
				Channels:   int(to16(c16.Channels[:])),
				SampleRate: int(to32(c16.SamplesPerSec[:])),
			}

		case "fact":
			// We need to scan fact chunk first.
			var fact factChunk
			err = d.bRead(&fact, binary.Size(fact))
			if err != nil {
				return nil, err
			}

		case "data":
			// Read the data chunk header now
			d.chunkSize = length
			complete = true
		}
	}

	return d, nil
}

func init() {
	audio.RegisterFormat("wav", "RIFF", newDecoder)
}
<|endoftext|>


<commit_before>package gokeepasslib

import (
	"bytes"
	"compress/gzip"
	"encoding/xml"
	"errors"
	"io"
	"io/ioutil"
	"reflect"
)

// Decoder stores a reader which is expected to be in kdbx format
type Decoder struct {
	r io.Reader
}

func (d *Decoder) Decode(db *Database) (err error) {
	db.Signature = new(FileSignature)
	if err = db.Signature.ReadFrom(d.r); err != nil {
		return  err
	}

	db.Headers = new(FileHeaders)
	err = db.Headers.ReadFrom(d.r)
	if err != nil {
		return err
	}

	if err := d.readData(db); err != nil {
		return err
	}

	return nil
}

func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{r: r}
}

func (d *Decoder) readData(db *Database) error {
	in, err := ioutil.ReadAll(d.r)
	if err != nil {
		return err
	}

	mode,err := db.Decrypter()
	if err != nil {
		return err
	}
	decrypted := make([]byte, len(in))
	mode.CryptBlocks(decrypted, in)

	startBytes := db.Headers.StreamStartBytes
	if !reflect.DeepEqual(decrypted[0:len(startBytes)], startBytes) {
		return errors.New("Database integrity check failed")
	}
	decrypted = decrypted[len(startBytes):]

	var xmlDecoder *xml.Decoder
	if db.Headers.CompressionFlags == GzipCompressionFlag { //Unzip if the header compression flag is 1 for gzip
		zippedBody, err := DecodeBlocks(decrypted)
		if err != nil {
			return err
		}

		b := bytes.NewBuffer(zippedBody)
		r, err := gzip.NewReader(b)
		if err != nil {
			return err
		}
		defer r.Close()
		xmlDecoder = xml.NewDecoder(r)
	} else { //Otherwise assume it not compressed
		xmlDecoder = xml.NewDecoder(bytes.NewReader(decrypted))
	}

	db.Content = &DBContent{}
	err = xmlDecoder.Decode(db.Content)
	return err
}
<commit_msg>wrote simpler<commit_after>package gokeepasslib

import (
	"bytes"
	"compress/gzip"
	"encoding/xml"
	"errors"
	"io"
	"io/ioutil"
	"reflect"
)

// Decoder stores a reader which is expected to be in kdbx format
type Decoder struct {
	r io.Reader
}

func (d *Decoder) Decode(db *Database) (err error) {
	db.Signature = new(FileSignature)
	if err = db.Signature.ReadFrom(d.r); err != nil {
		return  err
	}

	db.Headers = new(FileHeaders)
	if err = db.Headers.ReadFrom(d.r); err != nil {
		return err
	}

	if err := d.readData(db); err != nil {
		return err
	}

	return nil
}

func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{r: r}
}

func (d *Decoder) readData(db *Database) error {
	in, err := ioutil.ReadAll(d.r)
	if err != nil {
		return err
	}

	mode,err := db.Decrypter()
	if err != nil {
		return err
	}
	decrypted := make([]byte, len(in))
	mode.CryptBlocks(decrypted, in)

	startBytes := db.Headers.StreamStartBytes
	if !reflect.DeepEqual(decrypted[0:len(startBytes)], startBytes) {
		return errors.New("Database integrity check failed")
	}
	decrypted = decrypted[len(startBytes):]

	var xmlDecoder *xml.Decoder
	if db.Headers.CompressionFlags == GzipCompressionFlag { //Unzip if the header compression flag is 1 for gzip
		zippedBody, err := DecodeBlocks(decrypted)
		if err != nil {
			return err
		}

		b := bytes.NewBuffer(zippedBody)
		r, err := gzip.NewReader(b)
		if err != nil {
			return err
		}
		defer r.Close()
		xmlDecoder = xml.NewDecoder(r)
	} else { //Otherwise assume it not compressed
		xmlDecoder = xml.NewDecoder(bytes.NewReader(decrypted))
	}

	db.Content = &DBContent{}
	err = xmlDecoder.Decode(db.Content)
	return err
}
<|endoftext|>


<commit_before>/*
 * Minio Client (C) 2014, 2015 Minio, 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 main

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/fatih/color"
	"github.com/minio/cli"
	"github.com/minio/mc/pkg/client"
	"github.com/minio/mc/pkg/console"
	"github.com/minio/minio-xl/pkg/probe"
)

// rm specific flags.
var (
	rmFlagForce = cli.BoolFlag{
		Name:  "force",
		Usage: "Force a dangerous remove operation.",
	}

	rmFlagIncomplete = cli.BoolFlag{
		Name:  "incomplete, I",
		Usage: "Remove an incomplete upload(s).",
	}

	rmFlagHelp = cli.BoolFlag{
		Name:  "help, h",
		Usage: "Help of rm.",
	}
)

// remove a file or folder.
var rmCmd = cli.Command{
	Name:   "rm",
	Usage:  "Remove file or bucket [WARNING: Use with care].",
	Action: mainRm,
	Flags:  []cli.Flag{rmFlagForce, rmFlagIncomplete, rmFlagHelp},
	CustomHelpTemplate: `NAME:
   mc {{.Name}} - {{.Usage}}

USAGE:
   mc {{.Name}} [FLAGS] TARGET [TARGET ...]

FLAGS:
  {{range .Flags}}{{.}}
  {{end}}
EXAMPLES:
   1. Remove an object.
     $ mc {{.Name}} https://s3.amazonaws.com/jazz-songs/louis/file01.mp3

   2. Remove a file.
      $ mc {{.Name}} 1999/old-backup.tgz

   3. Remove contents of a folder recursively.
     $ mc {{.Name}} --force https://s3.amazonaws.com/jazz-songs/louis/...

   4  Remove contents of a folder recursively and folder itself.
      $ mc {{.Name}} old/photos...

   5. Remove a bucket and all its contents recursively.
     $ mc {{.Name}} --force https://s3.amazonaws.com/jazz-songs...

   6. Remove all matching objects with this prefix.
     $ mc {{.Name}} --force https://s3.amazonaws.com/ogg/gunmetal...

   7. Cancel an incomplete upload of an object.
      $ mc {{.Name}} --incomplete https://s3.amazonaws.com/jazz-songs/louis/file01.mp3

   8. Remove all incomplete uploads recursively matching this prefix.
      $ mc {{.Name}} --incomplete --force https://s3.amazonaws.com/jazz-songs/louis/...
`,
}

// Structured message depending on the type of console.
type rmMessage struct {
	URL string `json:"url"`
}

// Colorized message for console printing.
func (r rmMessage) String() string {
	return console.Colorize("Remove", fmt.Sprintf("Removed ‘%s’.", r.URL))
}

// JSON'ified message for scripting.
func (r rmMessage) JSON() string {
	msgBytes, e := json.Marshal(r)
	fatalIf(probe.NewError(e), "Unable to marshal into JSON.")
	return string(msgBytes)
}

// Validate command line arguments.
func checkRmSyntax(ctx *cli.Context) {
	args := ctx.Args()
	isForce := ctx.Bool("force")

	if !args.Present() {
		exitCode := 1
		cli.ShowCommandHelpAndExit(ctx, "rm", exitCode)
	}

	URLs, err := args2URLs(args)
	fatalIf(err.Trace(ctx.Args()...), "Unable to parse arguments.")

	// If input validation fails then provide context sensitive help without displaying generic help message.
	// The context sensitive help is shown per argument instead of all arguments to keep the help display
	// as well as the code simple. Also most of the times there will be just one arg
	for _, url := range URLs {
		u := client.NewURL(url)
		if strings.HasSuffix(url, string(u.Separator)) {
			fatalIf(errDummy().Trace(),
				"‘"+url+"’ is a folder. To remove this folder recursively, please try ‘"+url+"...’ as argument.")
		}
		if isURLRecursive(url) && !isForce {
			fatalIf(errDummy().Trace(),
				"Recursive removal requires --force option. Please review carefully before performing this operation.")
		}
	}
}

// Remove a single object.
func rm(url string, isIncomplete bool) *probe.Error {
	clnt, err := url2Client(url)
	if err != nil {
		return err.Trace(url)
	}

	if err = clnt.Remove(isIncomplete); err != nil {
		return err.Trace(url)
	}
	return nil
}

// Remove all objects recursively.
func rmAll(url string, isIncomplete bool) {
	// Initialize new client.
	clnt, err := url2Client(url)
	if err != nil {
		errorIf(err.Trace(url), "Invalid URL ‘"+url+"’.")
		return // End of journey.
	}
	isRecursive := false // Disable recursion and only list this folder's contents.
	for entry := range clnt.List(isRecursive, isIncomplete) {
		if entry.Err != nil {
			errorIf(entry.Err.Trace(url), "Unable to list ‘"+url+"’.")
			return // End of journey.
		}

		if entry.Type.IsDir() {
			// Add separator at the end to remove all its contents.
			url := entry.URL.String()
			u := client.NewURL(url)
			url = url + string(u.Separator)

			// Recursively remove contents of this directory.
			rmAll(url, isIncomplete)
		}
		// Regular type.
		err := rm(entry.URL.String(), isIncomplete)
		if err != nil {
			errorIf(err.Trace(entry.URL.String()), "Unable to remove ‘"+entry.URL.String()+"’.")
			continue
		}
		printMsg(rmMessage{entry.URL.String()})
	}
}

// main for rm command.
func mainRm(ctx *cli.Context) {
	checkRmSyntax(ctx)

	// rm specific flags.
	isForce := ctx.Bool("force")
	isIncomplete := ctx.Bool("incomplete")

	// Set color.
	console.SetColor("Remove", color.New(color.FgGreen, color.Bold))

	// Parse args.
	URLs, err := args2URLs(ctx.Args())
	fatalIf(err.Trace(ctx.Args()...), "Unable to parse arguments.")

	// Support multiple targets.
	for _, url := range URLs {
		if isURLRecursive(url) && isForce {
			url := stripRecursiveURL(url)
			removeTopFolder := false
			// find if the URL is dir or not.
			_, content, err := url2Stat(url)
			if err != nil && !prefixExists(url) {
				fatalIf(err.Trace(url), "Unable to stat ‘"+url+"’.")
			}

			if err == nil && content.Type.IsDir() {
				/* Determine whether to remove the top folder or only its
				contents. If the URL does not end with a separator, then
				include the top folder as well, otherwise not. */
				u := client.NewURL(url)
				if !strings.HasSuffix(url, string(u.Separator)) {
					// Add separator at the end to remove all its contents.
					url = url + string(u.Separator)
					// Remember to remove the top most folder.
					removeTopFolder = true
				}
			}
			// Remove contents of this folder.
			rmAll(url, isIncomplete)
			if removeTopFolder {
				// Remove top folder as well.
				err := rm(url, isIncomplete)
				if err != nil {
					errorIf(err.Trace(url), "Unable to remove ‘"+url+"’.")
					continue
				}
				printMsg(rmMessage{url})
			}

		} else {
			err := rm(url, isIncomplete)
			if err != nil {
				errorIf(err.Trace(url), "Unable to remove ‘"+url+"’.")
				continue
			}
			printMsg(rmMessage{url})
		}
	}
}
<commit_msg>rm: Do not try to remove the '.'<commit_after>/*
 * Minio Client (C) 2014, 2015 Minio, 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 main

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/fatih/color"
	"github.com/minio/cli"
	"github.com/minio/mc/pkg/client"
	"github.com/minio/mc/pkg/console"
	"github.com/minio/minio-xl/pkg/probe"
)

// rm specific flags.
var (
	rmFlagForce = cli.BoolFlag{
		Name:  "force",
		Usage: "Force a dangerous remove operation.",
	}

	rmFlagIncomplete = cli.BoolFlag{
		Name:  "incomplete, I",
		Usage: "Remove an incomplete upload(s).",
	}

	rmFlagHelp = cli.BoolFlag{
		Name:  "help, h",
		Usage: "Help of rm.",
	}
)

// remove a file or folder.
var rmCmd = cli.Command{
	Name:   "rm",
	Usage:  "Remove file or bucket [WARNING: Use with care].",
	Action: mainRm,
	Flags:  []cli.Flag{rmFlagForce, rmFlagIncomplete, rmFlagHelp},
	CustomHelpTemplate: `NAME:
   mc {{.Name}} - {{.Usage}}

USAGE:
   mc {{.Name}} [FLAGS] TARGET [TARGET ...]

FLAGS:
  {{range .Flags}}{{.}}
  {{end}}
EXAMPLES:
   1. Remove an object.
     $ mc {{.Name}} https://s3.amazonaws.com/jazz-songs/louis/file01.mp3

   2. Remove a file.
      $ mc {{.Name}} 1999/old-backup.tgz

   3. Remove contents of a folder recursively.
     $ mc {{.Name}} --force https://s3.amazonaws.com/jazz-songs/louis/...

   4  Remove contents of a folder recursively and folder itself.
      $ mc {{.Name}} old/photos...

   5. Remove a bucket and all its contents recursively.
     $ mc {{.Name}} --force https://s3.amazonaws.com/jazz-songs...

   6. Remove all matching objects with this prefix.
     $ mc {{.Name}} --force https://s3.amazonaws.com/ogg/gunmetal...

   7. Cancel an incomplete upload of an object.
      $ mc {{.Name}} --incomplete https://s3.amazonaws.com/jazz-songs/louis/file01.mp3

   8. Remove all incomplete uploads recursively matching this prefix.
      $ mc {{.Name}} --incomplete --force https://s3.amazonaws.com/jazz-songs/louis/...
`,
}

// Structured message depending on the type of console.
type rmMessage struct {
	URL string `json:"url"`
}

// Colorized message for console printing.
func (r rmMessage) String() string {
	return console.Colorize("Remove", fmt.Sprintf("Removed ‘%s’.", r.URL))
}

// JSON'ified message for scripting.
func (r rmMessage) JSON() string {
	msgBytes, e := json.Marshal(r)
	fatalIf(probe.NewError(e), "Unable to marshal into JSON.")
	return string(msgBytes)
}

// Validate command line arguments.
func checkRmSyntax(ctx *cli.Context) {
	args := ctx.Args()
	isForce := ctx.Bool("force")

	if !args.Present() {
		exitCode := 1
		cli.ShowCommandHelpAndExit(ctx, "rm", exitCode)
	}

	URLs, err := args2URLs(args)
	fatalIf(err.Trace(ctx.Args()...), "Unable to parse arguments.")

	// If input validation fails then provide context sensitive help without displaying generic help message.
	// The context sensitive help is shown per argument instead of all arguments to keep the help display
	// as well as the code simple. Also most of the times there will be just one arg
	for _, url := range URLs {
		u := client.NewURL(url)
		if strings.HasSuffix(url, string(u.Separator)) {
			fatalIf(errDummy().Trace(),
				"‘"+url+"’ is a folder. To remove this folder recursively, please try ‘"+url+"...’ as argument.")
		}
		if isURLRecursive(url) && !isForce {
			fatalIf(errDummy().Trace(),
				"Recursive removal requires --force option. Please review carefully before performing this operation.")
		}
	}
}

// Remove a single object.
func rm(url string, isIncomplete bool) *probe.Error {
	clnt, err := url2Client(url)
	if err != nil {
		return err.Trace(url)
	}

	if err = clnt.Remove(isIncomplete); err != nil {
		return err.Trace(url)
	}
	return nil
}

// Remove all objects recursively.
func rmAll(url string, isIncomplete bool) {
	// Initialize new client.
	clnt, err := url2Client(url)
	if err != nil {
		errorIf(err.Trace(url), "Invalid URL ‘"+url+"’.")
		return // End of journey.
	}
	isRecursive := false // Disable recursion and only list this folder's contents.
	for entry := range clnt.List(isRecursive, isIncomplete) {
		if entry.Err != nil {
			errorIf(entry.Err.Trace(url), "Unable to list ‘"+url+"’.")
			return // End of journey.
		}

		if entry.Type.IsDir() {
			// Add separator at the end to remove all its contents.
			url := entry.URL
			url.Path = strings.TrimSuffix(entry.URL.Path, string(entry.URL.Separator)) + string(entry.URL.Separator)

			// Recursively remove contents of this directory.
			rmAll(url.String(), isIncomplete)
		}
		// Regular type.
		err = rm(entry.URL.String(), isIncomplete)
		if err != nil {
			errorIf(err.Trace(entry.URL.String()), "Unable to remove ‘"+entry.URL.String()+"’.")
			continue
		}
		printMsg(rmMessage{entry.URL.String()})
	}
}

// main for rm command.
func mainRm(ctx *cli.Context) {
	checkRmSyntax(ctx)

	// rm specific flags.
	isForce := ctx.Bool("force")
	isIncomplete := ctx.Bool("incomplete")

	// Set color.
	console.SetColor("Remove", color.New(color.FgGreen, color.Bold))

	// Parse args.
	URLs, err := args2URLs(ctx.Args())
	fatalIf(err.Trace(ctx.Args()...), "Unable to parse arguments.")

	// Support multiple targets.
	for _, url := range URLs {
		if isURLRecursive(url) && isForce {
			url := stripRecursiveURL(url)
			removeTopFolder := false
			// find if the URL is dir or not.
			_, content, err := url2Stat(url)
			if err != nil && !prefixExists(url) {
				fatalIf(err.Trace(url), "Unable to stat ‘"+url+"’.")
			}

			if err == nil && content.Type.IsDir() {
				// Determine whether to remove the top folder or only its
				// contents. If the URL does not end with a separator, then
				// include the top folder as well, otherwise not.
				u := client.NewURL(url)
				if !strings.HasSuffix(url, string(u.Separator)) {
					// Add separator at the end to remove all its contents.
					url = url + string(u.Separator)
					// Remember to remove the top most folder.
					removeTopFolder = true
				}
			}
			// Remove contents of this folder.
			rmAll(url, isIncomplete)
			// url will be '.' for arguments which will be just '...'
			if removeTopFolder && url != "." && url != ".." {
				// Remove top folder as well.
				err := rm(url, isIncomplete)
				if err != nil {
					errorIf(err.Trace(url), "Unable to remove ‘"+url+"’.")
					continue
				}
				printMsg(rmMessage{url})
			}

		} else {
			err := rm(url, isIncomplete)
			if err != nil {
				errorIf(err.Trace(url), "Unable to remove ‘"+url+"’.")
				continue
			}
			printMsg(rmMessage{url})
		}
	}
}
<|endoftext|>


<commit_before>package logger

import (
	"strconv"
	"time"
	"strings"
	"sync"

	"github.com/kataras/iris"
	"github.com/kataras/iris/config"
	"github.com/kataras/iris/logger"
	"github.com/reconquest/loreley"
)

type loggerMiddleware struct {
	*logger.Logger
	config Config
}

var mapLock       sync.RWMutex

// Serve serves the middleware
func (l *loggerMiddleware) Serve(ctx *iris.Context) {
	//all except latency to string
	var date, timed, latency, status, ip, method, path string
	var startTime, endTime time.Time
	path = ctx.PathString()
	method = ctx.MethodString()

	startTime = time.Now()

	ctx.Next()
	//no time.Since in order to format it well after
	endTime = time.Now()
	timed = rightPad2Len(endTime.Format("15:04:05.999999"), "0", 15)
	date = endTime.Format("02/01/2006")
	latency = endTime.Sub(startTime).String()
	parts := strings.Split(latency,".")
	if(len(parts) == 2) {
	    	if(!strings.Contains(parts[1],"ms")) {
			parts[1] = leftPad2Len(parts[1],"0",6)
		}
		latency = parts[0] + "." + parts[1]
	}

	if l.config.Status {
		status = strconv.Itoa(ctx.Response.StatusCode())
	}

	if l.config.IP {
		ip = leftPad2Len(ctx.RemoteAddr()," ",15)
	}

	if !l.config.Method {
		method = ""
	}

	if !l.config.Path {
		path = ""
	}

	getText, _ := loreley.CompileAndExecuteToString(
		`{bold}{fg 15}{bg 40} GET {from "" 0}{reset}`,
		nil,
		nil,
	)

	//finally print the logs
	if(method == "GET") {
		mapLock.RLock()
		l.printf("%s %s - %s | %v | %4v | %s | %s \n", getText, timed, date, status, latency, ip, path)
		mapLock.RUnlock()
	} else {
		mapLock.RLock()
		l.printf("%s - %s %v %4v %s %s %s \n", timed, date, status, latency, ip, method, path)
		mapLock.RUnlock()
	}

}

func rightPad2Len(s string, padStr string, overallLen int) string{
	var padCountInt int
	padCountInt = 1 + ((overallLen-len(padStr))/len(padStr))
	var retStr =  s + strings.Repeat(padStr, padCountInt)
	return retStr[:overallLen]
}

func leftPad2Len(s string, padStr string, overallLen int) string{
	var padCountInt int
	padCountInt = 1 + ((overallLen-len(padStr))/len(padStr))
	var retStr = strings.Repeat(padStr, padCountInt) + s
	return retStr[(len(retStr)-overallLen):]
}

func (l *loggerMiddleware) printf(format string, a ...interface{}) {
	if l.config.EnableColors {
		l.Logger.Otherf(format, a...)
	} else {
		l.Logger.Printf(format, a...)
	}
}

// New returns the logger middleware
// receives two parameters, both of them optionals
// first is the logger, which normally you set to the 'iris.Logger'
// if logger is nil then the middlewares makes one with the default configs.
// second is optional configs(logger.Config)
func New(theLogger *logger.Logger, cfg ...Config) iris.HandlerFunc {
	if theLogger == nil {
		theLogger = logger.New(config.DefaultLogger())
	}
	c := DefaultConfig().Merge(cfg)
	l := &loggerMiddleware{Logger: theLogger, config: c}

	return l.Serve
}
<commit_msg>Update logger.go<commit_after>package logger

import (
	"strconv"
	"time"
	"strings"
	"sync"

	"github.com/kataras/iris"
	"github.com/kataras/iris/config"
	//"github.com/kataras/iris/logger"
	"github.com/iris-contrib/middleware/logger"
	"github.com/reconquest/loreley"
)

type loggerMiddleware struct {
	*logger.Logger
	config Config
}

var mapLock       sync.RWMutex

// Serve serves the middleware
func (l *loggerMiddleware) Serve(ctx *iris.Context) {
	//all except latency to string
	var date, timed, latency, status, ip, method, path string
	var startTime, endTime time.Time
	path = ctx.PathString()
	method = ctx.MethodString()

	startTime = time.Now()

	ctx.Next()
	//no time.Since in order to format it well after
	endTime = time.Now()
	timed = rightPad2Len(endTime.Format("15:04:05.999999"), "0", 15)
	date = endTime.Format("02/01/2006")
	latency = endTime.Sub(startTime).String()
	parts := strings.Split(latency,".")
	if(len(parts) == 2) {
	    	if(!strings.Contains(parts[1],"ms")) {
			parts[1] = leftPad2Len(parts[1],"0",6)
		}
		latency = parts[0] + "." + parts[1]
	}

	if l.config.Status {
		status = strconv.Itoa(ctx.Response.StatusCode())
	}

	if l.config.IP {
		ip = leftPad2Len(ctx.RemoteAddr()," ",15)
	}

	if !l.config.Method {
		method = ""
	}

	if !l.config.Path {
		path = ""
	}

	getText, _ := loreley.CompileAndExecuteToString(
		`{bold}{fg 15}{bg 40} GET {from "" 0}{reset}`,
		nil,
		nil,
	)

	//finally print the logs
	if(method == "GET") {
		mapLock.RLock()
		l.printf("%s %s - %s | %v | %4v | %s | %s \n", getText, timed, date, status, latency, ip, path)
		mapLock.RUnlock()
	} else {
		mapLock.RLock()
		l.printf("%s - %s %v %4v %s %s %s \n", timed, date, status, latency, ip, method, path)
		mapLock.RUnlock()
	}

}

func rightPad2Len(s string, padStr string, overallLen int) string{
	var padCountInt int
	padCountInt = 1 + ((overallLen-len(padStr))/len(padStr))
	var retStr =  s + strings.Repeat(padStr, padCountInt)
	return retStr[:overallLen]
}

func leftPad2Len(s string, padStr string, overallLen int) string{
	var padCountInt int
	padCountInt = 1 + ((overallLen-len(padStr))/len(padStr))
	var retStr = strings.Repeat(padStr, padCountInt) + s
	return retStr[(len(retStr)-overallLen):]
}

func (l *loggerMiddleware) printf(format string, a ...interface{}) {
	if l.config.EnableColors {
		l.Logger.Otherf(format, a...)
	} else {
		l.Logger.Printf(format, a...)
	}
}

// New returns the logger middleware
// receives two parameters, both of them optionals
// first is the logger, which normally you set to the 'iris.Logger'
// if logger is nil then the middlewares makes one with the default configs.
// second is optional configs(logger.Config)
func New(theLogger *logger.Logger, cfg ...Config) iris.HandlerFunc {
	if theLogger == nil {
		theLogger = logger.New(config.DefaultLogger())
	}
	c := DefaultConfig().Merge(cfg)
	l := &loggerMiddleware{Logger: theLogger, config: c}

	return l.Serve
}
<|endoftext|>


<commit_before>package collector

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"strings"
	"sync"
	"time"

	"github.com/karimra/gnmic/outputs"
	"github.com/openconfig/gnmi/proto/gnmi"
	"github.com/openconfig/gnmi/proto/gnmi_ext"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"
)

type TargetError struct {
	SubscriptionName string
	Err              error
}

// Target represents a gNMI enabled box
type Target struct {
	Config        *TargetConfig
	Subscriptions []*SubscriptionConfig
	Outputs       []outputs.Output

	m                  *sync.Mutex
	Client             gnmi.GNMIClient
	SubscribeClients   map[string]gnmi.GNMI_SubscribeClient // subscription name to subscribeClient
	PollChan           chan string                          // subscription name to be polled
	SubscribeResponses chan *SubscribeResponse
	Errors             chan *TargetError
}

// TargetConfig //
type TargetConfig struct {
	Name          string        `mapstructure:"name,omitempty"`
	Address       string        `mapstructure:"address,omitempty"`
	Username      *string       `mapstructure:"username,omitempty"`
	Password      *string       `mapstructure:"password,omitempty"`
	Timeout       time.Duration `mapstructure:"timeout,omitempty"`
	Insecure      *bool         `mapstructure:"insecure,omitempty"`
	TLSCA         *string       `mapstructure:"tls-ca,omitempty"`
	TLSCert       *string       `mapstructure:"tls-cert,omitempty"`
	TLSKey        *string       `mapstructure:"tls-key,omitempty"`
	SkipVerify    *bool         `mapstructure:"skip-verify,omitempty"`
	Subscriptions []string      `mapstructure:"subscriptions,omitempty"`
	Outputs       []string      `mapstructure:"outputs,omitempty"`
	BufferSize    uint          `mapstructure:"buffer-size,omitempty"`
}

func (tc *TargetConfig) String() string {
	b, err := json.Marshal(tc)
	if err != nil {
		return ""
	}
	return string(b)
}

// NewTarget //
func NewTarget(c *TargetConfig) *Target {
	t := &Target{
		Config:             c,
		Subscriptions:      make([]*SubscriptionConfig, 0),
		Outputs:            make([]outputs.Output, 0),
		m:                  new(sync.Mutex),
		SubscribeClients:   make(map[string]gnmi.GNMI_SubscribeClient),
		PollChan:           make(chan string),
		SubscribeResponses: make(chan *SubscribeResponse, c.BufferSize),
		Errors:             make(chan *TargetError),
	}
	return t

}

// NewTLS //
func (tc *TargetConfig) newTLS() (*tls.Config, error) {
	tlsConfig := &tls.Config{
		Renegotiation:      tls.RenegotiateNever,
		InsecureSkipVerify: *tc.SkipVerify,
	}
	err := loadCerts(tlsConfig, tc)
	if err != nil {
		return nil, err
	}
	return tlsConfig, nil
}

// CreateGNMIClient //
func (t *Target) CreateGNMIClient(ctx context.Context, opts ...grpc.DialOption) error {
	if opts == nil {
		opts = []grpc.DialOption{}
	}
	if *t.Config.Insecure {
		opts = append(opts, grpc.WithInsecure())
	} else {
		tlsConfig, err := t.Config.newTLS()
		if err != nil {
			return err
		}
		opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)))
	}
	timeoutCtx, cancel := context.WithTimeout(ctx, t.Config.Timeout)
	defer cancel()
	conn, err := grpc.DialContext(timeoutCtx, t.Config.Address, opts...)
	if err != nil {
		return err
	}
	t.Client = gnmi.NewGNMIClient(conn)
	return nil
}

// Capabilities sends a gnmi.CapabilitiesRequest to the target *t and returns a gnmi.CapabilitiesResponse and an error
func (t *Target) Capabilities(ctx context.Context, ext ...*gnmi_ext.Extension) (*gnmi.CapabilityResponse, error) {
	nctx, cancel := context.WithTimeout(ctx, t.Config.Timeout)
	defer cancel()
	nctx = metadata.AppendToOutgoingContext(nctx, "username", *t.Config.Username, "password", *t.Config.Password)
	response, err := t.Client.Capabilities(nctx, &gnmi.CapabilityRequest{Extension: ext})
	if err != nil {
		return nil, fmt.Errorf("failed sending capabilities request: %v", err)
	}
	return response, nil
}

// Get sends a gnmi.GetRequest to the target *t and returns a gnmi.GetResponse and an error
func (t *Target) Get(ctx context.Context, req *gnmi.GetRequest) (*gnmi.GetResponse, error) {
	nctx, cancel := context.WithTimeout(ctx, t.Config.Timeout)
	defer cancel()
	nctx = metadata.AppendToOutgoingContext(nctx, "username", *t.Config.Username, "password", *t.Config.Password)
	response, err := t.Client.Get(nctx, req)
	if err != nil {
		return nil, fmt.Errorf("failed sending GetRequest to '%s': %v", t.Config.Address, err)
	}
	return response, nil
}

// Set sends a gnmi.SetRequest to the target *t and returns a gnmi.SetResponse and an error
func (t *Target) Set(ctx context.Context, req *gnmi.SetRequest) (*gnmi.SetResponse, error) {
	nctx, cancel := context.WithTimeout(ctx, t.Config.Timeout)
	defer cancel()
	nctx = metadata.AppendToOutgoingContext(nctx, "username", *t.Config.Username, "password", *t.Config.Password)
	response, err := t.Client.Set(nctx, req)
	if err != nil {
		return nil, fmt.Errorf("failed sending SetRequest to '%s': %v", t.Config.Address, err)
	}
	return response, nil
}

// Subscribe sends a gnmi.SubscribeRequest to the target *t, responses and error are sent to the target channels
func (t *Target) Subscribe(ctx context.Context, req *gnmi.SubscribeRequest, subscriptionName string) {
	nctx, cancel := context.WithCancel(ctx)
	defer cancel()
	nctx = metadata.AppendToOutgoingContext(nctx, "username", *t.Config.Username, "password", *t.Config.Password)
	subscribeClient, err := t.Client.Subscribe(nctx)
	if err != nil {
		t.Errors <- &TargetError{
			SubscriptionName: subscriptionName,
			Err:              fmt.Errorf("failed to create a subscribe client, target='%s': %v", t.Config.Name, err),
		}
		return
	}
	t.m.Lock()
	t.SubscribeClients[subscriptionName] = subscribeClient
	t.m.Unlock()
	err = subscribeClient.Send(req)
	if err != nil {
		t.Errors <- &TargetError{
			SubscriptionName: subscriptionName,
			Err:              fmt.Errorf("target '%s' send error: %v", t.Config.Name, err),
		}
		return
	}
	switch req.GetSubscribe().Mode {
	case gnmi.SubscriptionList_ONCE, gnmi.SubscriptionList_STREAM:
		for {
			response, err := subscribeClient.Recv()
			if err != nil {
				t.Errors <- &TargetError{
					SubscriptionName: subscriptionName,
					Err:              err,
				}
				return
			}
			t.SubscribeResponses <- &SubscribeResponse{
				SubscriptionName: subscriptionName,
				Response:         response,
			}
			if req.GetSubscribe().Mode == gnmi.SubscriptionList_ONCE {
				switch response.Response.(type) {
				case *gnmi.SubscribeResponse_SyncResponse:
					return
				}
			}
		}
	case gnmi.SubscriptionList_POLL:
		for {
			select {
			case subName := <-t.PollChan:
				err = t.SubscribeClients[subName].Send(&gnmi.SubscribeRequest{
					Request: &gnmi.SubscribeRequest_Poll{
						Poll: &gnmi.Poll{},
					},
				})
				if err != nil {
					t.Errors <- &TargetError{
						SubscriptionName: subscriptionName,
						Err:              fmt.Errorf("failed to send PollRequest: %v", err),
					}
					continue
				}
				response, err := subscribeClient.Recv()
				if err != nil {
					t.Errors <- &TargetError{
						SubscriptionName: subscriptionName,
						Err:              err,
					}
					continue
				}
				t.SubscribeResponses <- &SubscribeResponse{
					SubscriptionName: subscriptionName,
					Response:         response,
				}
			case <-nctx.Done():
				return
			}
		}
	}
}

// Export //
func (t *Target) Export(rsp *gnmi.SubscribeResponse, m outputs.Meta) {
	if rsp == nil || len(t.Outputs) == 0 {
		return
	}
	wg := new(sync.WaitGroup)
	wg.Add(len(t.Outputs))
	for _, o := range t.Outputs {
		go func(o outputs.Output) {
			defer wg.Done()
			o.Write(rsp, m)
		}(o)
	}
	wg.Wait()
}

func loadCerts(tlscfg *tls.Config, c *TargetConfig) error {
	if *c.TLSCert != "" && *c.TLSKey != "" {
		certificate, err := tls.LoadX509KeyPair(*c.TLSCert, *c.TLSKey)
		if err != nil {
			return err
		}
		tlscfg.Certificates = []tls.Certificate{certificate}
		tlscfg.BuildNameToCertificate()
	}
	if c.TLSCA != nil && *c.TLSCA != "" {
		certPool := x509.NewCertPool()
		caFile, err := ioutil.ReadFile(*c.TLSCA)
		if err != nil {
			return err
		}
		if ok := certPool.AppendCertsFromPEM(caFile); !ok {
			return errors.New("failed to append certificate")
		}
		tlscfg.RootCAs = certPool
	}
	return nil
}

func (t *Target) numberOfOnceSubscriptions() int {
	num := 0
	for _, sub := range t.Subscriptions {
		if strings.ToUpper(sub.Mode) == "ONCE" {
			num++
		}
	}
	return num
}
<commit_msg>add retry logic for a subscription<commit_after>package collector

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"strings"
	"sync"
	"time"

	"github.com/karimra/gnmic/outputs"
	"github.com/openconfig/gnmi/proto/gnmi"
	"github.com/openconfig/gnmi/proto/gnmi_ext"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"
)

const (
	defaultRetryTimer = 10 * time.Second
)

type TargetError struct {
	SubscriptionName string
	Err              error
}

// Target represents a gNMI enabled box
type Target struct {
	Config        *TargetConfig
	Subscriptions []*SubscriptionConfig
	Outputs       []outputs.Output

	m                  *sync.Mutex
	Client             gnmi.GNMIClient
	SubscribeClients   map[string]gnmi.GNMI_SubscribeClient // subscription name to subscribeClient
	PollChan           chan string                          // subscription name to be polled
	SubscribeResponses chan *SubscribeResponse
	Errors             chan *TargetError
}

// TargetConfig //
type TargetConfig struct {
	Name          string        `mapstructure:"name,omitempty"`
	Address       string        `mapstructure:"address,omitempty"`
	Username      *string       `mapstructure:"username,omitempty"`
	Password      *string       `mapstructure:"password,omitempty"`
	Timeout       time.Duration `mapstructure:"timeout,omitempty"`
	Insecure      *bool         `mapstructure:"insecure,omitempty"`
	TLSCA         *string       `mapstructure:"tls-ca,omitempty"`
	TLSCert       *string       `mapstructure:"tls-cert,omitempty"`
	TLSKey        *string       `mapstructure:"tls-key,omitempty"`
	SkipVerify    *bool         `mapstructure:"skip-verify,omitempty"`
	Subscriptions []string      `mapstructure:"subscriptions,omitempty"`
	Outputs       []string      `mapstructure:"outputs,omitempty"`
	BufferSize    uint          `mapstructure:"buffer-size,omitempty"`
}

func (tc *TargetConfig) String() string {
	b, err := json.Marshal(tc)
	if err != nil {
		return ""
	}
	return string(b)
}

// NewTarget //
func NewTarget(c *TargetConfig) *Target {
	t := &Target{
		Config:             c,
		Subscriptions:      make([]*SubscriptionConfig, 0),
		Outputs:            make([]outputs.Output, 0),
		m:                  new(sync.Mutex),
		SubscribeClients:   make(map[string]gnmi.GNMI_SubscribeClient),
		PollChan:           make(chan string),
		SubscribeResponses: make(chan *SubscribeResponse, c.BufferSize),
		Errors:             make(chan *TargetError),
	}
	return t

}

// NewTLS //
func (tc *TargetConfig) newTLS() (*tls.Config, error) {
	tlsConfig := &tls.Config{
		Renegotiation:      tls.RenegotiateNever,
		InsecureSkipVerify: *tc.SkipVerify,
	}
	err := loadCerts(tlsConfig, tc)
	if err != nil {
		return nil, err
	}
	return tlsConfig, nil
}

// CreateGNMIClient //
func (t *Target) CreateGNMIClient(ctx context.Context, opts ...grpc.DialOption) error {
	if opts == nil {
		opts = []grpc.DialOption{}
	}
	if *t.Config.Insecure {
		opts = append(opts, grpc.WithInsecure())
	} else {
		tlsConfig, err := t.Config.newTLS()
		if err != nil {
			return err
		}
		opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)))
	}
	timeoutCtx, cancel := context.WithTimeout(ctx, t.Config.Timeout)
	defer cancel()
	conn, err := grpc.DialContext(timeoutCtx, t.Config.Address, opts...)
	if err != nil {
		return err
	}
	t.Client = gnmi.NewGNMIClient(conn)
	return nil
}

// Capabilities sends a gnmi.CapabilitiesRequest to the target *t and returns a gnmi.CapabilitiesResponse and an error
func (t *Target) Capabilities(ctx context.Context, ext ...*gnmi_ext.Extension) (*gnmi.CapabilityResponse, error) {
	nctx, cancel := context.WithTimeout(ctx, t.Config.Timeout)
	defer cancel()
	nctx = metadata.AppendToOutgoingContext(nctx, "username", *t.Config.Username, "password", *t.Config.Password)
	response, err := t.Client.Capabilities(nctx, &gnmi.CapabilityRequest{Extension: ext})
	if err != nil {
		return nil, fmt.Errorf("failed sending capabilities request: %v", err)
	}
	return response, nil
}

// Get sends a gnmi.GetRequest to the target *t and returns a gnmi.GetResponse and an error
func (t *Target) Get(ctx context.Context, req *gnmi.GetRequest) (*gnmi.GetResponse, error) {
	nctx, cancel := context.WithTimeout(ctx, t.Config.Timeout)
	defer cancel()
	nctx = metadata.AppendToOutgoingContext(nctx, "username", *t.Config.Username, "password", *t.Config.Password)
	response, err := t.Client.Get(nctx, req)
	if err != nil {
		return nil, fmt.Errorf("failed sending GetRequest to '%s': %v", t.Config.Address, err)
	}
	return response, nil
}

// Set sends a gnmi.SetRequest to the target *t and returns a gnmi.SetResponse and an error
func (t *Target) Set(ctx context.Context, req *gnmi.SetRequest) (*gnmi.SetResponse, error) {
	nctx, cancel := context.WithTimeout(ctx, t.Config.Timeout)
	defer cancel()
	nctx = metadata.AppendToOutgoingContext(nctx, "username", *t.Config.Username, "password", *t.Config.Password)
	response, err := t.Client.Set(nctx, req)
	if err != nil {
		return nil, fmt.Errorf("failed sending SetRequest to '%s': %v", t.Config.Address, err)
	}
	return response, nil
}

// Subscribe sends a gnmi.SubscribeRequest to the target *t, responses and error are sent to the target channels
func (t *Target) Subscribe(ctx context.Context, req *gnmi.SubscribeRequest, subscriptionName string) {
SUBSC:
	nctx, cancel := context.WithCancel(ctx)
	defer cancel()
	nctx = metadata.AppendToOutgoingContext(nctx, "username", *t.Config.Username, "password", *t.Config.Password)
	subscribeClient, err := t.Client.Subscribe(nctx)
	if err != nil {
		t.Errors <- &TargetError{
			SubscriptionName: subscriptionName,
			Err:              fmt.Errorf("failed to create a subscribe client, target='%s', retry in %d. err=%v", t.Config.Name, defaultRetryTimer, err),
		}
		cancel()
		time.Sleep(defaultRetryTimer)
		goto SUBSC
	}
	t.m.Lock()
	t.SubscribeClients[subscriptionName] = subscribeClient
	t.m.Unlock()
	err = subscribeClient.Send(req)
	if err != nil {
		t.Errors <- &TargetError{
			SubscriptionName: subscriptionName,
			Err:              fmt.Errorf("target '%s' send error, retry in %d. err=%v", t.Config.Name, defaultRetryTimer, err),
		}
		cancel()
		time.Sleep(defaultRetryTimer)
		goto SUBSC
	}
	switch req.GetSubscribe().Mode {
	case gnmi.SubscriptionList_STREAM:
		for {
			response, err := subscribeClient.Recv()
			if err != nil {
				t.Errors <- &TargetError{
					SubscriptionName: subscriptionName,
					Err:              err,
				}
				t.Errors <- &TargetError{
					SubscriptionName: subscriptionName,
					Err:              fmt.Errorf("retrying in %d", defaultRetryTimer),
				}
				cancel()
				time.Sleep(defaultRetryTimer)
				goto SUBSC
			}
			t.SubscribeResponses <- &SubscribeResponse{
				SubscriptionName: subscriptionName,
				Response:         response,
			}
		}
	case gnmi.SubscriptionList_ONCE:
		for {
			response, err := subscribeClient.Recv()
			if err != nil {
				t.Errors <- &TargetError{
					SubscriptionName: subscriptionName,
					Err:              err,
				}
				if err == io.EOF {
					return
				} else {
					t.Errors <- &TargetError{
						SubscriptionName: subscriptionName,
						Err:              fmt.Errorf("retrying in %d", defaultRetryTimer),
					}
					cancel()
					time.Sleep(defaultRetryTimer)
					goto SUBSC
				}
			}
			t.SubscribeResponses <- &SubscribeResponse{
				SubscriptionName: subscriptionName,
				Response:         response,
			}
			switch response.Response.(type) {
			case *gnmi.SubscribeResponse_SyncResponse:
				return
			}
		}
	case gnmi.SubscriptionList_POLL:
		for {
			select {
			case subName := <-t.PollChan:
				err = t.SubscribeClients[subName].Send(&gnmi.SubscribeRequest{
					Request: &gnmi.SubscribeRequest_Poll{
						Poll: &gnmi.Poll{},
					},
				})
				if err != nil {
					t.Errors <- &TargetError{
						SubscriptionName: subscriptionName,
						Err:              fmt.Errorf("failed to send PollRequest: %v", err),
					}
					continue
				}
				response, err := subscribeClient.Recv()
				if err != nil {
					t.Errors <- &TargetError{
						SubscriptionName: subscriptionName,
						Err:              err,
					}
					continue
				}
				t.SubscribeResponses <- &SubscribeResponse{
					SubscriptionName: subscriptionName,
					Response:         response,
				}
			case <-nctx.Done():
				return
			}
		}
	}
}

// Export //
func (t *Target) Export(rsp *gnmi.SubscribeResponse, m outputs.Meta) {
	if rsp == nil || len(t.Outputs) == 0 {
		return
	}
	wg := new(sync.WaitGroup)
	wg.Add(len(t.Outputs))
	for _, o := range t.Outputs {
		go func(o outputs.Output) {
			defer wg.Done()
			o.Write(rsp, m)
		}(o)
	}
	wg.Wait()
}

func loadCerts(tlscfg *tls.Config, c *TargetConfig) error {
	if *c.TLSCert != "" && *c.TLSKey != "" {
		certificate, err := tls.LoadX509KeyPair(*c.TLSCert, *c.TLSKey)
		if err != nil {
			return err
		}
		tlscfg.Certificates = []tls.Certificate{certificate}
		tlscfg.BuildNameToCertificate()
	}
	if c.TLSCA != nil && *c.TLSCA != "" {
		certPool := x509.NewCertPool()
		caFile, err := ioutil.ReadFile(*c.TLSCA)
		if err != nil {
			return err
		}
		if ok := certPool.AppendCertsFromPEM(caFile); !ok {
			return errors.New("failed to append certificate")
		}
		tlscfg.RootCAs = certPool
	}
	return nil
}

func (t *Target) numberOfOnceSubscriptions() int {
	num := 0
	for _, sub := range t.Subscriptions {
		if strings.ToUpper(sub.Mode) == "ONCE" {
			num++
		}
	}
	return num
}
<|endoftext|>


<commit_before>package command

import (
	"encoding/json"
	"fmt"
	"path/filepath"
	"strings"

	"github.com/zclconf/go-cty/cty"

	"github.com/hashicorp/terraform/terraform"
	"github.com/hashicorp/terraform/tfdiags"
)

// ValidateCommand is a Command implementation that validates the terraform files
type ValidateCommand struct {
	Meta
}

func (c *ValidateCommand) Run(args []string) int {
	args = c.Meta.process(args)
	// TODO: The `var` and `var-file` options are not actually used, and should
	// be removed in the next major release.
	if c.Meta.variableArgs.items == nil {
		c.Meta.variableArgs = newRawFlags("-var")
	}
	varValues := c.Meta.variableArgs.Alias("-var")
	varFiles := c.Meta.variableArgs.Alias("-var-file")

	var jsonOutput bool
	cmdFlags := c.Meta.defaultFlagSet("validate")
	cmdFlags.BoolVar(&jsonOutput, "json", false, "produce JSON output")
	cmdFlags.Var(varValues, "var", "variables")
	cmdFlags.Var(varFiles, "var-file", "variable file")
	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
	if err := cmdFlags.Parse(args); err != nil {
		c.Ui.Error(fmt.Sprintf("Error parsing command-line flags: %s\n", err.Error()))
		return 1
	}

	var diags tfdiags.Diagnostics

	// If set, output a warning indicating that these values are not used.
	if !varValues.Empty() || !varFiles.Empty() {
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Warning,
			"The -var and -var-file flags are not used in validate. Setting them has no effect.",
			"These flags will be removed in a future version of Terraform.",
		))
	}

	// After this point, we must only produce JSON output if JSON mode is
	// enabled, so all errors should be accumulated into diags and we'll
	// print out a suitable result at the end, depending on the format
	// selection. All returns from this point on must be tail-calls into
	// c.showResults in order to produce the expected output.
	args = cmdFlags.Args()

	var dirPath string
	if len(args) == 1 {
		dirPath = args[0]
	} else {
		dirPath = "."
	}
	dir, err := filepath.Abs(dirPath)
	if err != nil {
		diags = diags.Append(fmt.Errorf("unable to locate module: %s", err))
		return c.showResults(diags, jsonOutput)
	}

	// Check for user-supplied plugin path
	if c.pluginPath, err = c.loadPluginPath(); err != nil {
		diags = diags.Append(fmt.Errorf("error loading plugin path: %s", err))
		return c.showResults(diags, jsonOutput)
	}

	validateDiags := c.validate(dir)
	diags = diags.Append(validateDiags)

	// Validating with dev overrides in effect means that the result might
	// not be valid for a stable release, so we'll warn about that in case
	// the user is trying to use "terraform validate" as a sort of pre-flight
	// check before submitting a change.
	diags = diags.Append(c.providerDevOverrideRuntimeWarnings())

	return c.showResults(diags, jsonOutput)
}

func (c *ValidateCommand) validate(dir string) tfdiags.Diagnostics {
	var diags tfdiags.Diagnostics

	cfg, cfgDiags := c.loadConfig(dir)
	diags = diags.Append(cfgDiags)

	if diags.HasErrors() {
		return diags
	}

	// "validate" is to check if the given module is valid regardless of
	// input values, current state, etc. Therefore we populate all of the
	// input values with unknown values of the expected type, allowing us
	// to perform a type check without assuming any particular values.
	varValues := make(terraform.InputValues)
	for name, variable := range cfg.Module.Variables {
		ty := variable.Type
		if ty == cty.NilType {
			// Can't predict the type at all, so we'll just mark it as
			// cty.DynamicVal (unknown value of cty.DynamicPseudoType).
			ty = cty.DynamicPseudoType
		}
		varValues[name] = &terraform.InputValue{
			Value:      cty.UnknownVal(ty),
			SourceType: terraform.ValueFromCLIArg,
		}
	}

	opts, err := c.contextOpts()
	if err != nil {
		diags = diags.Append(err)
		return diags
	}
	opts.Config = cfg
	opts.Variables = varValues

	tfCtx, ctxDiags := terraform.NewContext(opts)
	diags = diags.Append(ctxDiags)
	if ctxDiags.HasErrors() {
		return diags
	}

	validateDiags := tfCtx.Validate()
	diags = diags.Append(validateDiags)
	return diags
}

func (c *ValidateCommand) showResults(diags tfdiags.Diagnostics, jsonOutput bool) int {
	switch {
	case jsonOutput:
		// FIXME: Eventually we'll probably want to factor this out somewhere
		// to support machine-readable outputs for other commands too, but for
		// now it's simplest to do this inline here.
		type Pos struct {
			Line   int `json:"line"`
			Column int `json:"column"`
			Byte   int `json:"byte"`
		}
		type Range struct {
			Filename string `json:"filename"`
			Start    Pos    `json:"start"`
			End      Pos    `json:"end"`
		}
		type Diagnostic struct {
			Severity string `json:"severity,omitempty"`
			Summary  string `json:"summary,omitempty"`
			Detail   string `json:"detail,omitempty"`
			Range    *Range `json:"range,omitempty"`
		}
		type Output struct {
			// We include some summary information that is actually redundant
			// with the detailed diagnostics, but avoids the need for callers
			// to re-implement our logic for deciding these.
			Valid        bool         `json:"valid"`
			ErrorCount   int          `json:"error_count"`
			WarningCount int          `json:"warning_count"`
			Diagnostics  []Diagnostic `json:"diagnostics"`
		}

		var output Output
		output.Valid = true // until proven otherwise
		for _, diag := range diags {
			var jsonDiag Diagnostic
			switch diag.Severity() {
			case tfdiags.Error:
				jsonDiag.Severity = "error"
				output.ErrorCount++
				output.Valid = false
			case tfdiags.Warning:
				jsonDiag.Severity = "warning"
				output.WarningCount++
			}

			desc := diag.Description()
			jsonDiag.Summary = desc.Summary
			jsonDiag.Detail = desc.Detail

			ranges := diag.Source()
			if ranges.Subject != nil {
				subj := ranges.Subject
				jsonDiag.Range = &Range{
					Filename: subj.Filename,
					Start: Pos{
						Line:   subj.Start.Line,
						Column: subj.Start.Column,
						Byte:   subj.Start.Byte,
					},
					End: Pos{
						Line:   subj.End.Line,
						Column: subj.End.Column,
						Byte:   subj.End.Byte,
					},
				}
			}

			output.Diagnostics = append(output.Diagnostics, jsonDiag)
		}
		if output.Diagnostics == nil {
			// Make sure this always appears as an array in our output, since
			// this is easier to consume for dynamically-typed languages.
			output.Diagnostics = []Diagnostic{}
		}

		j, err := json.MarshalIndent(&output, "", "  ")
		if err != nil {
			// Should never happen because we fully-control the input here
			panic(err)
		}
		c.Ui.Output(string(j))

	default:
		if len(diags) == 0 {
			c.Ui.Output(c.Colorize().Color("[green][bold]Success![reset] The configuration is valid.\n"))
		} else {
			c.showDiagnostics(diags)

			if !diags.HasErrors() {
				c.Ui.Output(c.Colorize().Color("[green][bold]Success![reset] The configuration is valid, but there were some validation warnings as shown above.\n"))
			}
		}
	}

	if diags.HasErrors() {
		return 1
	}
	return 0
}

func (c *ValidateCommand) Synopsis() string {
	return "Check whether the configuration is valid"
}

func (c *ValidateCommand) Help() string {
	helpText := `
Usage: terraform [global options] validate [options] [dir]

  Validate the configuration files in a directory, referring only to the
  configuration and not accessing any remote services such as remote state,
  provider APIs, etc.

  Validate runs checks that verify whether a configuration is syntactically
  valid and internally consistent, regardless of any provided variables or
  existing state. It is thus primarily useful for general verification of
  reusable modules, including correctness of attribute names and value types.

  It is safe to run this command automatically, for example as a post-save
  check in a text editor or as a test step for a re-usable module in a CI
  system.

  Validation requires an initialized working directory with any referenced
  plugins and modules installed. To initialize a working directory for
  validation without accessing any configured remote backend, use:
      terraform init -backend=false

  If dir is not specified, then the current directory will be used.

  To verify configuration in the context of a particular run (a particular
  target workspace, input variable values, etc), use the 'terraform plan'
  command instead, which includes an implied validation check.

Options:

  -json        Produce output in a machine-readable JSON format, suitable for
               use in text editor integrations and other automated systems.
               Always disables color.

  -no-color    If specified, output won't contain any color.
`
	return strings.TrimSpace(helpText)
}
<commit_msg>cli: Remove -var/-var-file flags from validate<commit_after>package command

import (
	"encoding/json"
	"fmt"
	"path/filepath"
	"strings"

	"github.com/zclconf/go-cty/cty"

	"github.com/hashicorp/terraform/terraform"
	"github.com/hashicorp/terraform/tfdiags"
)

// ValidateCommand is a Command implementation that validates the terraform files
type ValidateCommand struct {
	Meta
}

func (c *ValidateCommand) Run(args []string) int {
	args = c.Meta.process(args)

	var jsonOutput bool
	cmdFlags := c.Meta.defaultFlagSet("validate")
	cmdFlags.BoolVar(&jsonOutput, "json", false, "produce JSON output")
	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
	if err := cmdFlags.Parse(args); err != nil {
		c.Ui.Error(fmt.Sprintf("Error parsing command-line flags: %s\n", err.Error()))
		return 1
	}

	var diags tfdiags.Diagnostics

	// After this point, we must only produce JSON output if JSON mode is
	// enabled, so all errors should be accumulated into diags and we'll
	// print out a suitable result at the end, depending on the format
	// selection. All returns from this point on must be tail-calls into
	// c.showResults in order to produce the expected output.
	args = cmdFlags.Args()

	var dirPath string
	if len(args) == 1 {
		dirPath = args[0]
	} else {
		dirPath = "."
	}
	dir, err := filepath.Abs(dirPath)
	if err != nil {
		diags = diags.Append(fmt.Errorf("unable to locate module: %s", err))
		return c.showResults(diags, jsonOutput)
	}

	// Check for user-supplied plugin path
	if c.pluginPath, err = c.loadPluginPath(); err != nil {
		diags = diags.Append(fmt.Errorf("error loading plugin path: %s", err))
		return c.showResults(diags, jsonOutput)
	}

	validateDiags := c.validate(dir)
	diags = diags.Append(validateDiags)

	// Validating with dev overrides in effect means that the result might
	// not be valid for a stable release, so we'll warn about that in case
	// the user is trying to use "terraform validate" as a sort of pre-flight
	// check before submitting a change.
	diags = diags.Append(c.providerDevOverrideRuntimeWarnings())

	return c.showResults(diags, jsonOutput)
}

func (c *ValidateCommand) validate(dir string) tfdiags.Diagnostics {
	var diags tfdiags.Diagnostics

	cfg, cfgDiags := c.loadConfig(dir)
	diags = diags.Append(cfgDiags)

	if diags.HasErrors() {
		return diags
	}

	// "validate" is to check if the given module is valid regardless of
	// input values, current state, etc. Therefore we populate all of the
	// input values with unknown values of the expected type, allowing us
	// to perform a type check without assuming any particular values.
	varValues := make(terraform.InputValues)
	for name, variable := range cfg.Module.Variables {
		ty := variable.Type
		if ty == cty.NilType {
			// Can't predict the type at all, so we'll just mark it as
			// cty.DynamicVal (unknown value of cty.DynamicPseudoType).
			ty = cty.DynamicPseudoType
		}
		varValues[name] = &terraform.InputValue{
			Value:      cty.UnknownVal(ty),
			SourceType: terraform.ValueFromCLIArg,
		}
	}

	opts, err := c.contextOpts()
	if err != nil {
		diags = diags.Append(err)
		return diags
	}
	opts.Config = cfg
	opts.Variables = varValues

	tfCtx, ctxDiags := terraform.NewContext(opts)
	diags = diags.Append(ctxDiags)
	if ctxDiags.HasErrors() {
		return diags
	}

	validateDiags := tfCtx.Validate()
	diags = diags.Append(validateDiags)
	return diags
}

func (c *ValidateCommand) showResults(diags tfdiags.Diagnostics, jsonOutput bool) int {
	switch {
	case jsonOutput:
		// FIXME: Eventually we'll probably want to factor this out somewhere
		// to support machine-readable outputs for other commands too, but for
		// now it's simplest to do this inline here.
		type Pos struct {
			Line   int `json:"line"`
			Column int `json:"column"`
			Byte   int `json:"byte"`
		}
		type Range struct {
			Filename string `json:"filename"`
			Start    Pos    `json:"start"`
			End      Pos    `json:"end"`
		}
		type Diagnostic struct {
			Severity string `json:"severity,omitempty"`
			Summary  string `json:"summary,omitempty"`
			Detail   string `json:"detail,omitempty"`
			Range    *Range `json:"range,omitempty"`
		}
		type Output struct {
			// We include some summary information that is actually redundant
			// with the detailed diagnostics, but avoids the need for callers
			// to re-implement our logic for deciding these.
			Valid        bool         `json:"valid"`
			ErrorCount   int          `json:"error_count"`
			WarningCount int          `json:"warning_count"`
			Diagnostics  []Diagnostic `json:"diagnostics"`
		}

		var output Output
		output.Valid = true // until proven otherwise
		for _, diag := range diags {
			var jsonDiag Diagnostic
			switch diag.Severity() {
			case tfdiags.Error:
				jsonDiag.Severity = "error"
				output.ErrorCount++
				output.Valid = false
			case tfdiags.Warning:
				jsonDiag.Severity = "warning"
				output.WarningCount++
			}

			desc := diag.Description()
			jsonDiag.Summary = desc.Summary
			jsonDiag.Detail = desc.Detail

			ranges := diag.Source()
			if ranges.Subject != nil {
				subj := ranges.Subject
				jsonDiag.Range = &Range{
					Filename: subj.Filename,
					Start: Pos{
						Line:   subj.Start.Line,
						Column: subj.Start.Column,
						Byte:   subj.Start.Byte,
					},
					End: Pos{
						Line:   subj.End.Line,
						Column: subj.End.Column,
						Byte:   subj.End.Byte,
					},
				}
			}

			output.Diagnostics = append(output.Diagnostics, jsonDiag)
		}
		if output.Diagnostics == nil {
			// Make sure this always appears as an array in our output, since
			// this is easier to consume for dynamically-typed languages.
			output.Diagnostics = []Diagnostic{}
		}

		j, err := json.MarshalIndent(&output, "", "  ")
		if err != nil {
			// Should never happen because we fully-control the input here
			panic(err)
		}
		c.Ui.Output(string(j))

	default:
		if len(diags) == 0 {
			c.Ui.Output(c.Colorize().Color("[green][bold]Success![reset] The configuration is valid.\n"))
		} else {
			c.showDiagnostics(diags)

			if !diags.HasErrors() {
				c.Ui.Output(c.Colorize().Color("[green][bold]Success![reset] The configuration is valid, but there were some validation warnings as shown above.\n"))
			}
		}
	}

	if diags.HasErrors() {
		return 1
	}
	return 0
}

func (c *ValidateCommand) Synopsis() string {
	return "Check whether the configuration is valid"
}

func (c *ValidateCommand) Help() string {
	helpText := `
Usage: terraform [global options] validate [options] [dir]

  Validate the configuration files in a directory, referring only to the
  configuration and not accessing any remote services such as remote state,
  provider APIs, etc.

  Validate runs checks that verify whether a configuration is syntactically
  valid and internally consistent, regardless of any provided variables or
  existing state. It is thus primarily useful for general verification of
  reusable modules, including correctness of attribute names and value types.

  It is safe to run this command automatically, for example as a post-save
  check in a text editor or as a test step for a re-usable module in a CI
  system.

  Validation requires an initialized working directory with any referenced
  plugins and modules installed. To initialize a working directory for
  validation without accessing any configured remote backend, use:
      terraform init -backend=false

  If dir is not specified, then the current directory will be used.

  To verify configuration in the context of a particular run (a particular
  target workspace, input variable values, etc), use the 'terraform plan'
  command instead, which includes an implied validation check.

Options:

  -json        Produce output in a machine-readable JSON format, suitable for
               use in text editor integrations and other automated systems.
               Always disables color.

  -no-color    If specified, output won't contain any color.
`
	return strings.TrimSpace(helpText)
}
<|endoftext|>


<commit_before>package minify // import "github.com/tdewolff/minify"

import (
	"bytes"
	"io"
	"unicode"

	"github.com/tdewolff/parse"
)

const bufSize = 1024

// Default is a default minifier used to minify an unknown media type.
// It remove all whitespace at the beginning and end of the strea.
func (m Minifier) Default(w io.Writer, r io.Reader) error {
	if fr, ok := r.(interface{ Bytes() []byte }); ok {
		b := bytes.TrimSpace(fr.Bytes())
		if _, errWrite := w.Write(b); errWrite != nil {
			return errWrite
		}
		return nil
	}

	head := true
	sb := parse.NewShiftBuffer(r)
	for {
		// cause a read
		if sb.Peek(0) == 0 && sb.Err() != nil {
			if sb.Err() == io.EOF {
				return nil
			}
			return sb.Err()
		}

		// consume whole buffer and unconsume trailing whitespace
		sb.MoveTo(sb.Len())
		trailingLen := sb.Len() - len(bytes.TrimRightFunc(sb.Buffered(), unicode.IsSpace))
		sb.Move(-trailingLen)

		b := sb.Shift()
		if head {
			b = bytes.TrimLeftFunc(b, unicode.IsSpace)
			head = len(b) == 0 // if it's all whitespace, we still need to trim leading whitespace next time
		}
		if _, errWrite := w.Write(b); errWrite != nil {
			return errWrite
		}
		sb.Move(trailingLen)
	}
}
<commit_msg>go fmt<commit_after>package minify // import "github.com/tdewolff/minify"

import (
	"bytes"
	"io"
	"unicode"

	"github.com/tdewolff/parse"
)

const bufSize = 1024

// Default is a default minifier used to minify an unknown media type.
// It remove all whitespace at the beginning and end of the strea.
func (m Minifier) Default(w io.Writer, r io.Reader) error {
	if fr, ok := r.(interface {
		Bytes() []byte
	}); ok {
		b := bytes.TrimSpace(fr.Bytes())
		if _, errWrite := w.Write(b); errWrite != nil {
			return errWrite
		}
		return nil
	}

	head := true
	sb := parse.NewShiftBuffer(r)
	for {
		// cause a read
		if sb.Peek(0) == 0 && sb.Err() != nil {
			if sb.Err() == io.EOF {
				return nil
			}
			return sb.Err()
		}

		// consume whole buffer and unconsume trailing whitespace
		sb.MoveTo(sb.Len())
		trailingLen := sb.Len() - len(bytes.TrimRightFunc(sb.Buffered(), unicode.IsSpace))
		sb.Move(-trailingLen)

		b := sb.Shift()
		if head {
			b = bytes.TrimLeftFunc(b, unicode.IsSpace)
			head = len(b) == 0 // if it's all whitespace, we still need to trim leading whitespace next time
		}
		if _, errWrite := w.Write(b); errWrite != nil {
			return errWrite
		}
		sb.Move(trailingLen)
	}
}
<|endoftext|>


<commit_before>package distributor

import (
	"errors"
	"log"

	components "github.com/LoRaWanSoFa/LoRaWanSoFa/Components"
	"github.com/LoRaWanSoFa/LoRaWanSoFa/Core/ByteConverter"
	"github.com/LoRaWanSoFa/LoRaWanSoFa/Core/restUplinkConnector"
	"github.com/LoRaWanSoFa/LoRaWanSoFa/DBC/DatabaseConnector"
)

var logFatal = log.Fatal

type Distributor interface {
	InputUplink(components.MessageUplinkI) (components.MessageUplinkI, error)
	InputDownlink(components.MessageDownLink)
}

type distributor struct {
	byteConverter       byteConverter.ByteConverter
	restUplinkConnector restUplink.RestUplinkConnector
}

func New() Distributor {
	dist := new(distributor)
	dist.byteConverter = byteConverter.New()
	config := components.GetConfiguration().Rest
	dist.restUplinkConnector = restUplink.NewRestUplinkConnector(config.Ip, config.ApiKey)
	return dist
}

func (d *distributor) InputUplink(message components.MessageUplinkI) (components.MessageUplinkI, error) {
	if d.deduplicate(message) {
		newMessage := d.convertMessage(message)
		err := DatabaseConnector.StoreMessagePayloads(newMessage)
		if err != nil {
			logFatal(err)
		}
		d.restUplinkConnector.NewData(newMessage.GetDevEUI(), newMessage)
		return newMessage, nil
	} else {
		err := errors.New("message was a duplicate")
		return nil, err
	}
}

func (d *distributor) InputDownlink(message components.MessageDownLink) {

}

//The deduplicate method should deduplicate messages that come in once from the
//TTN side of things(semi-private) as well as our own private backend and return
// true only if the message has not been received yet.
func (d *distributor) deduplicate(message components.MessageUplinkI) bool {
	// TODO: deduplicate messages that could come in checking with the database
	// or createing a small cache for it.
	return true
}

func (d *distributor) convertMessage(message components.MessageUplinkI) components.MessageUplinkI {
	bytePayloads := message.GetPayloads()
	message.RemovePayloads()
	for i := range bytePayloads {
		payload, ok := bytePayloads[i].GetPayload().([]byte)
		if ok {
			sensor := bytePayloads[i].GetSensor()
			payloadS, err := d.byteConverter.ConvertSingleValue(payload, sensor.DataType)
			if err != nil {
				logFatal(err)
			} else {
				message.AddPayloadString(payloadS, sensor)
			}
		}
	}
	return message
}
<commit_msg>added documentation<commit_after>package distributor

import (
	"errors"
	"log"

	components "github.com/LoRaWanSoFa/LoRaWanSoFa/Components"
	"github.com/LoRaWanSoFa/LoRaWanSoFa/Core/ByteConverter"
	"github.com/LoRaWanSoFa/LoRaWanSoFa/Core/restUplinkConnector"
	"github.com/LoRaWanSoFa/LoRaWanSoFa/DBC/DatabaseConnector"
)

var logFatal = log.Fatal

type Distributor interface {
	InputUplink(components.MessageUplinkI) (components.MessageUplinkI, error)
	InputDownlink(components.MessageDownLink)
}

type distributor struct {
	byteConverter       byteConverter.ByteConverter
	restUplinkConnector restUplink.RestUplinkConnector
}

// Creates a new Distributor object.
func New() Distributor {
	dist := new(distributor)
	dist.byteConverter = byteConverter.New()
	config := components.GetConfiguration().Rest
	dist.restUplinkConnector = restUplink.NewRestUplinkConnector(config.Ip, config.ApiKey)
	return dist
}

// Receives an Uplink message and distributes the message to the parts of the
// application that need to receive it.
func (d *distributor) InputUplink(message components.MessageUplinkI) (components.MessageUplinkI, error) {
	if d.deduplicate(message) {
		newMessage := d.convertMessage(message)
		err := DatabaseConnector.StoreMessagePayloads(newMessage)
		if err != nil {
			logFatal(err)
		}
		d.restUplinkConnector.NewData(newMessage.GetDevEUI(), newMessage)
		return newMessage, nil
	} else {
		err := errors.New("message was a duplicate")
		return nil, err
	}
}

// Receives a Downlink message and distributes the message to the parts of the
// application that need to receive it.
func (d *distributor) InputDownlink(message components.MessageDownLink) {

}

//The deduplicate method should deduplicate messages that come in once from the
//TTN side of things(semi-private) as well as our own private backend and return
// true only if the message has not been received yet.
func (d *distributor) deduplicate(message components.MessageUplinkI) bool {
	// TODO: deduplicate messages that could come in checking with the database
	// or createing a small cache for it.
	return true
}

// Converts a message payload from bytes to string.
func (d *distributor) convertMessage(message components.MessageUplinkI) components.MessageUplinkI {
	bytePayloads := message.GetPayloads()
	message.RemovePayloads()
	for i := range bytePayloads {
		payload, ok := bytePayloads[i].GetPayload().([]byte)
		if ok {
			sensor := bytePayloads[i].GetSensor()
			payloadS, err := d.byteConverter.ConvertSingleValue(payload, sensor.DataType)
			if err != nil {
				logFatal(err)
			} else {
				message.AddPayloadString(payloadS, sensor)
			}
		}
	}
	return message
}
<|endoftext|>