<commit_before>package testutil

import (
	"bytes"
	"fmt"
	"math"
	"os"
	"reflect"
	"runtime"
	"strings"
	"testing"
)

// Ceq is a custom equals check; the given function returns true if its arguments are equal
func Ceq(t *testing.T, expected, actual interface{}, eq func(a, b interface{}) bool, context ...interface{}) bool {
	return ceq(getCaller(), t, expected, actual, eq, context)
}

func ceq(caller string, t *testing.T, expected, actual interface{}, eq func(a, b interface{}) bool, context []interface{}) bool {
	e := eq(expected, actual)
	require(caller, t, e, mergeContext(context, "Expecting %v (%v), got %v (%v)", expected, reflect.TypeOf(expected), actual, reflect.TypeOf(actual)))
	return e
}

// Cneq is a custom not-equals check; the given function returns true if its arguments are equal
func Cneq(t *testing.T, unexpected, actual interface{}, eq func(a, b interface{}) bool, context ...interface{}) bool {
	return cneq(getCaller(), t, unexpected, actual, eq, context)
}

func cneq(caller string, t *testing.T, unexpected, actual interface{}, eq func(a, b interface{}) bool, context []interface{}) bool {
	ne := !eq(unexpected, actual)
	require(caller, t, ne, mergeContext(context, "Value should not be %v (%v)", unexpected, reflect.TypeOf(unexpected)))
	return ne
}

// Require is an assertion that logs a failure if its given argument is not true
func Require(t *testing.T, condition bool, context ...interface{}) {
	require(getCaller(), t, condition, context)
}

func require(caller string, t *testing.T, condition bool, context []interface{}) {
	if !condition {
		if len(context) == 0 {
			t.Fatalf("%s: Assertion failed", caller)
		} else {
			msg := context[0].(string)
			// if any args were deferred (e.g. a function instead of a value), get those args now
			args := make([]interface{}, len(context)-1)
			for i, a := range context[1:] {
				rv := reflect.ValueOf(a)
				if rv.Kind() == reflect.Func {
					a = rv.Call([]reflect.Value{})[0].Interface()
				}
				args[i] = a
			}
			t.Fatalf("%s: %s", caller, fmt.Sprintf(msg, args...))
		}
	}
}

func mergeContext(context []interface{}, msg string, msgArgs ...interface{}) []interface{} {
	if len(context) == 0 {
		ret := make([]interface{}, len(msgArgs)+1)
		ret[0] = msg
		for i, a := range msgArgs {
			ret[i+1] = a
		}
		return ret
	} else {
		ret := make([]interface{}, len(msgArgs)+2)
		ret[0] = msg + ": %s"
		for i, a := range msgArgs {
			ret[i+1] = a
		}
		ret[len(ret)-1] = func() string {
			f := context[0].(string)
			return fmt.Sprintf(f, context[1:]...)
		}
		return ret
	}
}

func getCaller() string {
	pc, file, line, ok := runtime.Caller(2)
	if !ok {
		return "?"
	}
	fn := runtime.FuncForPC(pc)
	var fnName string
	if fn == nil {
		fnName = "?"
	} else {
		fnName = fn.Name()
	}
	return fmt.Sprintf("%s(%s:%d)", lastComponents(fnName, 1), lastComponents(file, 2), line)
}

const pathSep = string(os.PathSeparator)

func lastComponents(s string, count int) string {
	ss := s
	var i int
	for count > 0 {
		i = strings.LastIndex(ss, pathSep)
		if i < 0 {
			return s
		}
		count--
		ss = ss[:i]
	}
	return s[i+1:]
}

// Ok asserts that the given error is nil
func Ok(t *testing.T, err error, context ...interface{}) {
	require(getCaller(), t, err == nil, mergeContext(context, "Unexpected error: %s", func() interface{} { return err.Error() }))
}

// Nok asserts that the given error is not nil
func Nok(t *testing.T, err error, context ...interface{}) {
	require(getCaller(), t, err != nil, mergeContext(context, "Expected error but got none"))
}

// Eq asserts that the given two values are equal
func Eq(t *testing.T, expected, actual interface{}, context ...interface{}) bool {
	return ceq(getCaller(), t, expected, actual, eqany, context)
}

// Neq asserts that the given two values are not equal
func Neq(t *testing.T, unexpected, actual interface{}, context ...interface{}) bool {
	return cneq(getCaller(), t, unexpected, actual, eqany, context)
}

// default equality test and helpers

func eqany(expected, actual interface{}) bool {
	if expected == nil && actual == nil {
		return true
	}
	if expected == nil || actual == nil {
		return false
	}

	// We don't want reflect.DeepEquals because of its recursive nature. So we need
	// a custom compare for slices and maps. Two slices are equal if they have the
	// same number of elements and the elements at the same index are equal to each
	// other. Two maps are equal if their key sets are the same and the corresponding
	// values are equal. (The relationship is not recursive,  slices or maps that
	// contain other slices or maps can't be tested.)
	et := reflect.TypeOf(expected)

	if et.Kind() == reflect.Slice {
		return eqslice(reflect.ValueOf(expected), reflect.ValueOf(actual))
	} else if et.Kind() == reflect.Map {
		return eqmap(reflect.ValueOf(expected), reflect.ValueOf(actual))
	} else {
		return eqscalar(expected, actual)
	}
}

func eqscalar(expected, actual interface{}) bool {
	// special-case simple equality for []byte (since slices aren't directly comparable)
	if e, ok := expected.([]byte); ok {
		a, ok := actual.([]byte)
		return ok && bytes.Equal(e, a)
	}
	// and special-cases to handle NaN
	if e, ok := expected.(float32); ok && math.IsNaN(float64(e)) {
		a, ok := actual.(float32)
		return ok && math.IsNaN(float64(a))
	}
	if e, ok := expected.(float64); ok && math.IsNaN(e) {
		a, ok := actual.(float64)
		return ok && math.IsNaN(a)
	}
	// simple logic for everything else
	return expected == actual
}

func eqslice(expected, actual reflect.Value) bool {
	if expected.Len() != actual.Len() {
		return false
	}
	for i := 0; i < expected.Len(); i++ {
		e := expected.Index(i).Interface()
		a := actual.Index(i).Interface()
		if !eqscalar(e, a) {
			return false
		}
	}
	return true
}

func eqmap(expected, actual reflect.Value) bool {
	if expected.Len() != actual.Len() {
		return false
	}
	for _, k := range expected.MapKeys() {
		e := expected.MapIndex(k)
		a := actual.MapIndex(k)
		if !a.IsValid() {
			return false
		}
		if !eqscalar(e.Interface(), a.Interface()) {
			return false
		}
	}
	return true
}
<commit_msg>greatly simplify testutils package by using t.Helper (#381)<commit_after>package testutil

import (
	"bytes"
	"math"
	"reflect"
	"testing"
)

// Ceq is a custom equals check; the given function returns true if its arguments are equal
func Ceq(t *testing.T, expected, actual interface{}, eq func(a, b interface{}) bool, context ...interface{}) bool {
	t.Helper()
	e := eq(expected, actual)
	Require(t, e, mergeContext(context, "Expecting %v (%v), got %v (%v)", expected, reflect.TypeOf(expected), actual, reflect.TypeOf(actual))...)
	return e
}

// Cneq is a custom not-equals check; the given function returns true if its arguments are equal
func Cneq(t *testing.T, unexpected, actual interface{}, eq func(a, b interface{}) bool, context ...interface{}) bool {
	t.Helper()
	ne := !eq(unexpected, actual)
	Require(t, ne, mergeContext(context, "Value should not be %v (%v)", unexpected, reflect.TypeOf(unexpected))...)
	return ne
}

// Require is an assertion that logs a failure if its given argument is not true
func Require(t *testing.T, condition bool, context ...interface{}) {
	t.Helper()
	if !condition {
		if len(context) == 0 {
			t.Fatalf("Assertion failed")
		} else {
			msg := context[0].(string)
			// if any args were deferred (e.g. a function instead of a value), get those args now
			args := make([]interface{}, len(context)-1)
			for i, a := range context[1:] {
				rv := reflect.ValueOf(a)
				if rv.Kind() == reflect.Func {
					a = rv.Call([]reflect.Value{})[0].Interface()
				}
				args[i] = a
			}
			t.Fatalf(msg, args...)
		}
	}
}

func mergeContext(context []interface{}, msg string, msgArgs ...interface{}) []interface{} {
	if len(context) == 0 {
		ret := make([]interface{}, 0, len(msgArgs)+1)
		ret = append(ret, msg)
		ret = append(ret, msgArgs...)
		return ret
	} else {
		ret := make([]interface{}, 0, len(context)+len(msgArgs))
		ret = append(ret, msg+": "+context[0].(string))
		ret = append(ret, msgArgs...)
		ret = append(ret, context[1:]...)
		return ret
	}
}

// Ok asserts that the given error is nil
func Ok(t *testing.T, err error, context ...interface{}) {
	t.Helper()
	Require(t, err == nil, mergeContext(context, "Unexpected error: %s", func() interface{} { return err.Error() })...)
}

// Nok asserts that the given error is not nil
func Nok(t *testing.T, err error, context ...interface{}) {
	t.Helper()
	Require(t, err != nil, mergeContext(context, "Expected error but got none")...)
}

// Eq asserts that the given two values are equal
func Eq(t *testing.T, expected, actual interface{}, context ...interface{}) bool {
	t.Helper()
	return Ceq(t, expected, actual, eqany, context...)
}

// Neq asserts that the given two values are not equal
func Neq(t *testing.T, unexpected, actual interface{}, context ...interface{}) bool {
	t.Helper()
	return Cneq(t, unexpected, actual, eqany, context...)
}

// default equality test and helpers

func eqany(expected, actual interface{}) bool {
	if expected == nil && actual == nil {
		return true
	}
	if expected == nil || actual == nil {
		return false
	}

	// We don't want reflect.DeepEquals because of its recursive nature. So we need
	// a custom compare for slices and maps. Two slices are equal if they have the
	// same number of elements and the elements at the same index are equal to each
	// other. Two maps are equal if their key sets are the same and the corresponding
	// values are equal. (The relationship is not recursive,  slices or maps that
	// contain other slices or maps can't be tested.)
	et := reflect.TypeOf(expected)

	if et.Kind() == reflect.Slice {
		return eqslice(reflect.ValueOf(expected), reflect.ValueOf(actual))
	} else if et.Kind() == reflect.Map {
		return eqmap(reflect.ValueOf(expected), reflect.ValueOf(actual))
	} else {
		return eqscalar(expected, actual)
	}
}

func eqscalar(expected, actual interface{}) bool {
	// special-case simple equality for []byte (since slices aren't directly comparable)
	if e, ok := expected.([]byte); ok {
		a, ok := actual.([]byte)
		return ok && bytes.Equal(e, a)
	}
	// and special-cases to handle NaN
	if e, ok := expected.(float32); ok && math.IsNaN(float64(e)) {
		a, ok := actual.(float32)
		return ok && math.IsNaN(float64(a))
	}
	if e, ok := expected.(float64); ok && math.IsNaN(e) {
		a, ok := actual.(float64)
		return ok && math.IsNaN(a)
	}
	// simple logic for everything else
	return expected == actual
}

func eqslice(expected, actual reflect.Value) bool {
	if expected.Len() != actual.Len() {
		return false
	}
	for i := 0; i < expected.Len(); i++ {
		e := expected.Index(i).Interface()
		a := actual.Index(i).Interface()
		if !eqscalar(e, a) {
			return false
		}
	}
	return true
}

func eqmap(expected, actual reflect.Value) bool {
	if expected.Len() != actual.Len() {
		return false
	}
	for _, k := range expected.MapKeys() {
		e := expected.MapIndex(k)
		a := actual.MapIndex(k)
		if !a.IsValid() {
			return false
		}
		if !eqscalar(e.Interface(), a.Interface()) {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before><commit_msg>Add support for all sizes of blob<commit_after><|endoftext|>


<commit_before>// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// vespa query command
// author: bratseth

package cmd

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

	"github.com/spf13/cobra"
	"github.com/vespa-engine/vespa/util"
)

func init() {
	rootCmd.AddCommand(queryCmd)
}

var queryCmd = &cobra.Command{
	Use:     "query <query-parameters>",
	Short:   "Issue a query to Vespa",
	Example: `$ vespa query "yql=select from sources * where title contains 'foo';" hits=5`,
	Long: `Issue a query to Vespa.

Any parameter from https://docs.vespa.ai/en/reference/query-api-reference.html
can be set by the syntax [parameter-name]=[value].`,
	// TODO: Support referencing a query json file
	Args: cobra.MinimumNArgs(1),
	Run: func(cmd *cobra.Command, args []string) {
		query(args)
	},
}

func query(arguments []string) {
	url, _ := url.Parse(queryTarget() + "/search/")
	urlQuery := url.Query()
	for i := 0; i < len(arguments); i++ {
		key, value := splitArg(arguments[i])
		urlQuery.Set(key, value)
	}
	url.RawQuery = urlQuery.Encode()

	// TODO: Make this work for Vespa Cloud (pass certificate)
	response, err := util.HttpDo(&http.Request{URL: url}, time.Second*10, "Container")
	if err != nil {
		log.Print(color.Red("Error: "), "Request failed: ", err)
		return
	}
	defer response.Body.Close()

	if response.StatusCode == 200 {
		log.Print(util.ReaderToJSON(response.Body))
	} else if response.StatusCode/100 == 4 {
		log.Print(color.Red("Error: "), "Invalid query: ", response.Status, "\n")
		log.Print(util.ReaderToJSON(response.Body))
	} else {
		log.Print(color.Red("Error: "), response.Status, " from container at ", color.Cyan(url.Host), "\n")
		log.Print(util.ReaderToJSON(response.Body))
	}
}

func splitArg(argument string) (string, string) {
	equalsIndex := strings.Index(argument, "=")
	if equalsIndex < 1 {
		return "yql", argument
	} else {
		return argument[0:equalsIndex], argument[equalsIndex+1:]
	}
}

func queryTarget() string {
	return getService("query").BaseURL
}
<commit_msg>Support query in cloud<commit_after>// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// vespa query command
// author: bratseth

package cmd

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

	"github.com/spf13/cobra"
	"github.com/vespa-engine/vespa/util"
)

func init() {
	rootCmd.AddCommand(queryCmd)
}

var queryCmd = &cobra.Command{
	Use:     "query <query-parameters>",
	Short:   "Issue a query to Vespa",
	Example: `$ vespa query "yql=select from sources * where title contains 'foo';" hits=5`,
	Long: `Issue a query to Vespa.

Any parameter from https://docs.vespa.ai/en/reference/query-api-reference.html
can be set by the syntax [parameter-name]=[value].`,
	// TODO: Support referencing a query json file
	Args: cobra.MinimumNArgs(1),
	Run: func(cmd *cobra.Command, args []string) {
		query(args)
	},
}

func query(arguments []string) {
	service := getService("query")
	url, _ := url.Parse(service.BaseURL + "/search/")
	urlQuery := url.Query()
	for i := 0; i < len(arguments); i++ {
		key, value := splitArg(arguments[i])
		urlQuery.Set(key, value)
	}
	url.RawQuery = urlQuery.Encode()

	response, err := service.Do(&http.Request{URL: url}, time.Second*10)
	if err != nil {
		log.Print(color.Red("Error: "), "Request failed: ", err)
		return
	}
	defer response.Body.Close()

	if response.StatusCode == 200 {
		log.Print(util.ReaderToJSON(response.Body))
	} else if response.StatusCode/100 == 4 {
		log.Print(color.Red("Error: "), "Invalid query: ", response.Status, "\n")
		log.Print(util.ReaderToJSON(response.Body))
	} else {
		log.Print(color.Red("Error: "), response.Status, " from container at ", color.Cyan(url.Host), "\n")
		log.Print(util.ReaderToJSON(response.Body))
	}
}

func splitArg(argument string) (string, string) {
	equalsIndex := strings.Index(argument, "=")
	if equalsIndex < 1 {
		return "yql", argument
	} else {
		return argument[0:equalsIndex], argument[equalsIndex+1:]
	}
}
<|endoftext|>


<commit_before>package booklist

import (
	"fmt"
	"image"
	"image/jpeg"
	"log"
	"math"
	"os"
	"path/filepath"
	"runtime/debug"
	"sort"
	"strings"

	"github.com/bamiaux/rez"
	"github.com/geek1011/BookBrowser/formats"
	"github.com/geek1011/BookBrowser/models"
	"github.com/geek1011/BookBrowser/modules/util"
	zglob "github.com/mattn/go-zglob"
)

// BookList represents a list of Books
type BookList []*models.Book

// IndexerError represents a indexer error.
type IndexerError struct {
	Filename string
	Error    error
}

// NewBookListFromDir creates a BookList from a directory of books.
func NewBookListFromDir(dir, coverOutDir string, verbose, nocovers bool) (*BookList, []*IndexerError) {
	errors := []*IndexerError{}
	books := BookList{}

	filenames := map[string][]string{}
	for _, format := range formats.Formats {
		matches, err := zglob.Glob(filepath.Join(dir, format.Glob))
		if err != nil {
			errors = append(errors, &IndexerError{
				Filename: format.Glob,
				Error:    fmt.Errorf("error getting list of matched filenames for format %s: %v", format.Extension, err),
			})

			if verbose {
				log.Printf("Error getting matches for %s: %v", format.Glob, err)
			}

			continue
		}

		filenames[format.Extension] = matches
	}

	total := 0
	for _, i := range filenames {
		total += len(i)
	}

	current := 0
	for _, format := range formats.Formats {
		for _, filename := range filenames[format.Extension] {
			current++

			if verbose {
				log.Printf("[%v/%v] Indexing %s", current, total, filename)
			}

			book, cover, err := format.Indexer(filename)
			if err != nil {
				errors = append(errors, &IndexerError{
					Filename: filename,
					Error:    err,
				})

				if verbose {
					log.Printf("[%v/%v] Error indexing %s: %v", current, total, filename, err)
				}

				continue
			}

			if !nocovers && book.HasCover && cover != nil {
				coverPath := filepath.Join(coverOutDir, book.ID+".jpg")
				thumbPath := filepath.Join(coverOutDir, book.ID+"_thumb.jpg")

				if !(util.Exists(coverPath) && util.Exists(thumbPath)) {
					coverFile, err := os.Create(coverPath)
					if err != nil {
						continue
					}
					defer coverFile.Close()

					err = jpeg.Encode(coverFile, cover, nil)
					if err != nil {
						continue
					}

					coverBounds := cover.Bounds()
					coverWidth := coverBounds.Dx()
					coverHeight := coverBounds.Dy()

					if coverWidth <= 200 {
						continue
					}

					// Scale to fit in 200x900
					scale := math.Min(float64(200.0/float64(coverWidth)), float64(900.0/float64(coverHeight)))

					// Scale and round down
					coverWidth = int(float64(coverWidth) * scale)
					coverHeight = int(float64(coverHeight) * scale)

					r := image.Rect(0, 0, coverWidth, coverHeight)
					var thumb image.Image
					switch t := cover.(type) {
					case *image.YCbCr:
						thumb = image.NewYCbCr(r, t.SubsampleRatio)
					case *image.RGBA:
						thumb = image.NewRGBA(r)
					case *image.NRGBA:
						thumb = image.NewNRGBA(r)
					case *image.Gray:
						thumb = image.NewGray(r)
					default:
						continue
					}

					// rez.NewLanczos(2.0) is faster, but slower
					err = rez.Convert(thumb, cover, rez.NewBicubicFilter())
					if err != nil {
						fmt.Println(coverWidth, coverHeight, scale, err)
						continue
					}

					thumbFile, err := os.Create(thumbPath)
					if err != nil {
						continue
					}
					defer thumbFile.Close()

					err = jpeg.Encode(thumbFile, thumb, nil)
					if err != nil {
						continue
					}
				}

				book.HasCover = true
			}

			if nocovers {
				book.HasCover = false
			}

			books = append(books, book)
		}
	}

	debug.FreeOSMemory()
	return &books, errors
}

// Sorted returns a copy of the BookList sorted by the function
func (l *BookList) Sorted(sorter func(a, b *models.Book) bool) BookList {
	// Make a copy
	sorted := make(BookList, len(*l))
	copy(sorted, *l)
	// Sort the copy
	sort.Slice(sorted, func(i, j int) bool {
		return sorter(sorted[i], sorted[j])
	})
	return sorted
}

// Filtered returns a copy of the BookList filtered by the function
func (l *BookList) Filtered(filterer func(a *models.Book) bool) *BookList {
	filtered := BookList{}
	for _, a := range *l {
		if filterer(a) {
			filtered = append(filtered, a)
		}
	}

	return &filtered
}

// AuthorList is a list of authors
type AuthorList []*models.Author

// SeriesList is a list of series
type SeriesList []*models.Series

// GetAuthors gets the authors in a BookList
func (l *BookList) GetAuthors() *AuthorList {
	authors := AuthorList{}
	done := map[string]bool{}
	for _, b := range *l {
		if b.Author == nil {
			continue
		}

		if done[b.Author.ID] {
			continue
		}
		authors = append(authors, b.Author)
		done[b.Author.ID] = true
	}
	return &authors
}

// Sorted returns a copy of the AuthorList sorted by the function
func (l *AuthorList) Sorted(sorter func(a, b *models.Author) bool) *AuthorList {
	// Make a copy
	sorted := make(AuthorList, len(*l))
	copy(sorted, *l)
	// Sort the copy
	sort.Slice(sorted, func(i, j int) bool {
		return sorter(sorted[i], sorted[j])
	})
	return &sorted
}

// Filtered returns a copy of the AuthorList filtered by the function
func (l *AuthorList) Filtered(filterer func(a *models.Author) bool) *AuthorList {
	filtered := AuthorList{}
	for _, a := range *l {
		if filterer(a) {
			filtered = append(filtered, a)
		}
	}

	return &filtered
}

// GetSeries gets the series in a BookList
func (l *BookList) GetSeries() *SeriesList {
	series := SeriesList{}
	done := map[string]bool{}
	for _, b := range *l {
		if b.Series == nil {
			continue
		}

		if done[b.Series.ID] {
			continue
		}
		series = append(series, b.Series)
		done[b.Series.ID] = true
	}

	return &series
}

// Sorted returns a copy of the SeriesList sorted by the function
func (l *SeriesList) Sorted(sorter func(a, b *models.Series) bool) *SeriesList {
	// Make a copy
	sorted := make(SeriesList, len(*l))
	copy(sorted, *l)
	// Sort the copy
	sort.Slice(sorted, func(i, j int) bool {
		return sorter(sorted[i], sorted[j])
	})
	return &sorted
}

// Filtered returns a copy of the SeriesList filtered by the function
func (l *SeriesList) Filtered(filterer func(a *models.Series) bool) *SeriesList {
	filtered := SeriesList{}
	for _, a := range *l {
		if filterer(a) {
			filtered = append(filtered, a)
		}
	}

	return &filtered
}

// HasBook checks whether a book with an id exists
func (l *BookList) HasBook(id string) bool {
	exists := false
	for _, b := range *l {
		if b.ID == id {
			exists = true
		}
	}
	return exists
}

// HasAuthor checks whether an author with an id exists
func (l *BookList) HasAuthor(id string) bool {
	exists := false
	for _, b := range *l {
		if b.Author == nil {
			continue
		}

		if b.Author.ID == id {
			exists = true
		}
	}
	return exists
}

// HasSeries checks whether a series with an id exists
func (l *BookList) HasSeries(id string) bool {
	exists := false
	for _, b := range *l {
		if b.Series == nil {
			continue
		}
		if b.Series.ID == id {
			exists = true
		}
	}
	return exists
}

// SortBy sorts by sort, and returns a sorted copy. If sorter is invalid, it returns the original list.
//
// sort can be:
// - author-asc
// - author-desc
// - title-asc
// - title-desc
// - series-asc
// - series-desc
// - seriesindex-asc
// - seriesindex-desc
// - modified-desc
func (l *BookList) SortBy(sort string) (nl *BookList, sorted bool) {
	sort = strings.ToLower(sort)

	nb := *l

	switch sort {
	case "author-asc":
		nb = nb.Sorted(func(a, b *models.Book) bool {
			if a.Author != nil && b.Author != nil {
				return a.Author.Name < b.Author.Name
			}
			return false
		})
		break
	case "author-desc":
		nb = nb.Sorted(func(a, b *models.Book) bool {
			if a.Author != nil && b.Author != nil {
				return a.Author.Name > b.Author.Name
			}
			return false
		})
		break
	case "title-asc":
		nb = nb.Sorted(func(a, b *models.Book) bool {
			return a.Title < b.Title
		})
		break
	case "title-desc":
		nb = nb.Sorted(func(a, b *models.Book) bool {
			return a.Title > b.Title
		})
		break
	case "series-asc":
		nb = nb.Sorted(func(a, b *models.Book) bool {
			if a.Series != nil && b.Series != nil {
				return a.Series.Name < b.Series.Name
			}
			return false
		})
		break
	case "series-desc":
		nb = nb.Sorted(func(a, b *models.Book) bool {
			if a.Series != nil && b.Series != nil {
				return a.Series.Name > b.Series.Name
			}
			return false
		})
		break
	case "seriesindex-asc":
		nb = nb.Sorted(func(a, b *models.Book) bool {
			if a.Series != nil && b.Series != nil {
				return a.Series.Index < b.Series.Index
			}
			return false
		})
		break
	case "seriesindex-desc":
		nb = nb.Sorted(func(a, b *models.Book) bool {
			if a.Series != nil && b.Series != nil {
				return a.Series.Index > b.Series.Index
			}
			return false
		})
		break
	case "modified-desc":
		nb = nb.Sorted(func(a, b *models.Book) bool {
			return a.ModTime.Unix() > b.ModTime.Unix()
		})
		break
	default:
		return &nb, false
	}

	return &nb, true
}
<commit_msg>Move thumbnail generation into a function. (fixes #44)<commit_after>package booklist

import (
	"fmt"
	"image"
	"image/jpeg"
	"log"
	"math"
	"os"
	"path/filepath"
	"runtime/debug"
	"sort"
	"strings"

	"github.com/bamiaux/rez"
	"github.com/geek1011/BookBrowser/formats"
	"github.com/geek1011/BookBrowser/models"
	"github.com/geek1011/BookBrowser/modules/util"
	zglob "github.com/mattn/go-zglob"
)

// BookList represents a list of Books
type BookList []*models.Book

// IndexerError represents a indexer error.
type IndexerError struct {
	Filename string
	Error    error
}

// NewBookListFromDir creates a BookList from a directory of books.
func NewBookListFromDir(dir, coverOutDir string, verbose, nocovers bool) (*BookList, []*IndexerError) {
	errors := []*IndexerError{}
	books := BookList{}

	filenames := map[string][]string{}
	for _, format := range formats.Formats {
		matches, err := zglob.Glob(filepath.Join(dir, format.Glob))
		if err != nil {
			errors = append(errors, &IndexerError{
				Filename: format.Glob,
				Error:    fmt.Errorf("error getting list of matched filenames for format %s: %v", format.Extension, err),
			})

			if verbose {
				log.Printf("Error getting matches for %s: %v", format.Glob, err)
			}

			continue
		}

		filenames[format.Extension] = matches
	}

	total := 0
	for _, i := range filenames {
		total += len(i)
	}

	current := 0
	for _, format := range formats.Formats {
		for _, filename := range filenames[format.Extension] {
			current++

			if verbose {
				log.Printf("[%v/%v] Indexing %s", current, total, filename)
			}

			book, cover, err := format.Indexer(filename)
			if err != nil {
				errors = append(errors, &IndexerError{
					Filename: filename,
					Error:    err,
				})

				if verbose {
					log.Printf("[%v/%v] Error indexing %s: %v", current, total, filename, err)
				}

				continue
			}

			if !nocovers && book.HasCover && cover != nil {
				coverPath := filepath.Join(coverOutDir, book.ID+".jpg")
				thumbPath := filepath.Join(coverOutDir, book.ID+"_thumb.jpg")

				if !(util.Exists(coverPath) && util.Exists(thumbPath)) {
          err = makeThumbs(coverPath, thumbPath, cover);
          if err != nil {
            continue
          }
				}

				book.HasCover = true
			}

			if nocovers {
				book.HasCover = false
			}

			books = append(books, book)
		}
	}

	debug.FreeOSMemory()
	return &books, errors
}

func makeThumbs(coverPath string, thumbPath string, cover image.Image) error {
    coverFile, err := os.Create(coverPath)
    if err != nil {
      return nil
    }
    defer coverFile.Close()

    err = jpeg.Encode(coverFile, cover, nil)
    if err != nil {
      return nil
    }

    coverBounds := cover.Bounds()
    coverWidth := coverBounds.Dx()
    coverHeight := coverBounds.Dy()

    if coverWidth <= 200 {
      return nil
    }

    // Scale to fit in 200x900
    scale := math.Min(float64(200.0/float64(coverWidth)), float64(900.0/float64(coverHeight)))

    // Scale and round down
    coverWidth = int(float64(coverWidth) * scale)
    coverHeight = int(float64(coverHeight) * scale)

    r := image.Rect(0, 0, coverWidth, coverHeight)
    var thumb image.Image
    switch t := cover.(type) {
    case *image.YCbCr:
      thumb = image.NewYCbCr(r, t.SubsampleRatio)
    case *image.RGBA:
      thumb = image.NewRGBA(r)
    case *image.NRGBA:
      thumb = image.NewNRGBA(r)
    case *image.Gray:
      thumb = image.NewGray(r)
    default:
      return nil
    }

    // rez.NewLanczos(2.0) is faster, but slower
    err = rez.Convert(thumb, cover, rez.NewBicubicFilter())
    if err != nil {
      fmt.Println(coverWidth, coverHeight, scale, err)
      return err
    }

    thumbFile, err := os.Create(thumbPath)
    if err != nil {
      return err
    }
    defer thumbFile.Close()

    err = jpeg.Encode(thumbFile, thumb, nil)
    if err != nil {
      return err
    }

    return nil
}

// Sorted returns a copy of the BookList sorted by the function
func (l *BookList) Sorted(sorter func(a, b *models.Book) bool) BookList {
	// Make a copy
	sorted := make(BookList, len(*l))
	copy(sorted, *l)
	// Sort the copy
	sort.Slice(sorted, func(i, j int) bool {
		return sorter(sorted[i], sorted[j])
	})
	return sorted
}

// Filtered returns a copy of the BookList filtered by the function
func (l *BookList) Filtered(filterer func(a *models.Book) bool) *BookList {
	filtered := BookList{}
	for _, a := range *l {
		if filterer(a) {
			filtered = append(filtered, a)
		}
	}

	return &filtered
}

// AuthorList is a list of authors
type AuthorList []*models.Author

// SeriesList is a list of series
type SeriesList []*models.Series

// GetAuthors gets the authors in a BookList
func (l *BookList) GetAuthors() *AuthorList {
	authors := AuthorList{}
	done := map[string]bool{}
	for _, b := range *l {
		if b.Author == nil {
			continue
		}

		if done[b.Author.ID] {
			continue
		}
		authors = append(authors, b.Author)
		done[b.Author.ID] = true
	}
	return &authors
}

// Sorted returns a copy of the AuthorList sorted by the function
func (l *AuthorList) Sorted(sorter func(a, b *models.Author) bool) *AuthorList {
	// Make a copy
	sorted := make(AuthorList, len(*l))
	copy(sorted, *l)
	// Sort the copy
	sort.Slice(sorted, func(i, j int) bool {
		return sorter(sorted[i], sorted[j])
	})
	return &sorted
}

// Filtered returns a copy of the AuthorList filtered by the function
func (l *AuthorList) Filtered(filterer func(a *models.Author) bool) *AuthorList {
	filtered := AuthorList{}
	for _, a := range *l {
		if filterer(a) {
			filtered = append(filtered, a)
		}
	}

	return &filtered
}

// GetSeries gets the series in a BookList
func (l *BookList) GetSeries() *SeriesList {
	series := SeriesList{}
	done := map[string]bool{}
	for _, b := range *l {
		if b.Series == nil {
			continue
		}

		if done[b.Series.ID] {
			continue
		}
		series = append(series, b.Series)
		done[b.Series.ID] = true
	}

	return &series
}

// Sorted returns a copy of the SeriesList sorted by the function
func (l *SeriesList) Sorted(sorter func(a, b *models.Series) bool) *SeriesList {
	// Make a copy
	sorted := make(SeriesList, len(*l))
	copy(sorted, *l)
	// Sort the copy
	sort.Slice(sorted, func(i, j int) bool {
		return sorter(sorted[i], sorted[j])
	})
	return &sorted
}

// Filtered returns a copy of the SeriesList filtered by the function
func (l *SeriesList) Filtered(filterer func(a *models.Series) bool) *SeriesList {
	filtered := SeriesList{}
	for _, a := range *l {
		if filterer(a) {
			filtered = append(filtered, a)
		}
	}

	return &filtered
}

// HasBook checks whether a book with an id exists
func (l *BookList) HasBook(id string) bool {
	exists := false
	for _, b := range *l {
		if b.ID == id {
			exists = true
		}
	}
	return exists
}

// HasAuthor checks whether an author with an id exists
func (l *BookList) HasAuthor(id string) bool {
	exists := false
	for _, b := range *l {
		if b.Author == nil {
			continue
		}

		if b.Author.ID == id {
			exists = true
		}
	}
	return exists
}

// HasSeries checks whether a series with an id exists
func (l *BookList) HasSeries(id string) bool {
	exists := false
	for _, b := range *l {
		if b.Series == nil {
			continue
		}
		if b.Series.ID == id {
			exists = true
		}
	}
	return exists
}

// SortBy sorts by sort, and returns a sorted copy. If sorter is invalid, it returns the original list.
//
// sort can be:
// - author-asc
// - author-desc
// - title-asc
// - title-desc
// - series-asc
// - series-desc
// - seriesindex-asc
// - seriesindex-desc
// - modified-desc
func (l *BookList) SortBy(sort string) (nl *BookList, sorted bool) {
	sort = strings.ToLower(sort)

	nb := *l

	switch sort {
	case "author-asc":
		nb = nb.Sorted(func(a, b *models.Book) bool {
			if a.Author != nil && b.Author != nil {
				return a.Author.Name < b.Author.Name
			}
			return false
		})
		break
	case "author-desc":
		nb = nb.Sorted(func(a, b *models.Book) bool {
			if a.Author != nil && b.Author != nil {
				return a.Author.Name > b.Author.Name
			}
			return false
		})
		break
	case "title-asc":
		nb = nb.Sorted(func(a, b *models.Book) bool {
			return a.Title < b.Title
		})
		break
	case "title-desc":
		nb = nb.Sorted(func(a, b *models.Book) bool {
			return a.Title > b.Title
		})
		break
	case "series-asc":
		nb = nb.Sorted(func(a, b *models.Book) bool {
			if a.Series != nil && b.Series != nil {
				return a.Series.Name < b.Series.Name
			}
			return false
		})
		break
	case "series-desc":
		nb = nb.Sorted(func(a, b *models.Book) bool {
			if a.Series != nil && b.Series != nil {
				return a.Series.Name > b.Series.Name
			}
			return false
		})
		break
	case "seriesindex-asc":
		nb = nb.Sorted(func(a, b *models.Book) bool {
			if a.Series != nil && b.Series != nil {
				return a.Series.Index < b.Series.Index
			}
			return false
		})
		break
	case "seriesindex-desc":
		nb = nb.Sorted(func(a, b *models.Book) bool {
			if a.Series != nil && b.Series != nil {
				return a.Series.Index > b.Series.Index
			}
			return false
		})
		break
	case "modified-desc":
		nb = nb.Sorted(func(a, b *models.Book) bool {
			return a.ModTime.Unix() > b.ModTime.Unix()
		})
		break
	default:
		return &nb, false
	}

	return &nb, true
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 fuse

import (
	"fmt"
	"syscall"
	"unsafe"

	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/internal/buffer"
	"github.com/jacobsa/fuse/internal/fusekernel"
)

// Return the response that should be sent to the kernel. If the op requires no
// response, return a nil response.
func kernelResponse(
	fuseID uint64,
	op interface{},
	opErr error,
	protocol fusekernel.Protocol) (msg []byte) {
	// If the user replied with an error, create room enough just for the result
	// header and fill it in with an error. Otherwise create an appropriate
	// response.
	var b buffer.OutMessage
	if opErr != nil {
		b = buffer.NewOutMessage(0)
		if errno, ok := opErr.(syscall.Errno); ok {
			b.OutHeader().Error = -int32(errno)
		} else {
			b.OutHeader().Error = -int32(syscall.EIO)
		}
	} else {
		b = kernelResponseForOp(op, protocol)
	}

	msg = b.Bytes()

	// Fill in the rest of the header.
	h := b.OutHeader()
	h.Unique = fuseID
	h.Len = uint32(len(msg))

	return
}

// Like kernelResponse, but assumes the user replied with a nil error to the
// op.
func kernelResponseForOp(
	op interface{},
	protocol fusekernel.Protocol) (b buffer.OutMessage) {
	// Create the appropriate output message
	switch o := op.(type) {
	case *fuseops.LookUpInodeOp:
		size := fusekernel.EntryOutSize(protocol)
		b = buffer.NewOutMessage(size)
		out := (*fusekernel.EntryOut)(b.Grow(size))
		convertChildInodeEntry(&o.Entry, out)

	case *fuseops.GetInodeAttributesOp:
		size := fusekernel.AttrOutSize(protocol)
		b = buffer.NewOutMessage(size)
		out := (*fusekernel.AttrOut)(b.Grow(size))
		out.AttrValid, out.AttrValidNsec = convertExpirationTime(o.AttributesExpiration)
		convertAttributes(o.Inode, &o.Attributes, &out.Attr)

	case *fuseops.SetInodeAttributesOp:
		size := fusekernel.AttrOutSize(protocol)
		b = buffer.NewOutMessage(size)
		out := (*fusekernel.AttrOut)(b.Grow(size))
		out.AttrValid, out.AttrValidNsec = convertExpirationTime(o.AttributesExpiration)
		convertAttributes(o.Inode, &o.Attributes, &out.Attr)

	case *fuseops.ForgetInodeOp:
		// No response.

	case *fuseops.MkDirOp:
		size := fusekernel.EntryOutSize(protocol)
		b = buffer.NewOutMessage(size)
		out := (*fusekernel.EntryOut)(b.Grow(size))
		convertChildInodeEntry(&o.Entry, out)

	case *fuseops.CreateFileOp:
		eSize := fusekernel.EntryOutSize(protocol)
		b = buffer.NewOutMessage(eSize + unsafe.Sizeof(fusekernel.OpenOut{}))

		e := (*fusekernel.EntryOut)(b.Grow(eSize))
		convertChildInodeEntry(&o.Entry, e)

		oo := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
		oo.Fh = uint64(o.Handle)

	case *fuseops.CreateSymlinkOp:
		size := fusekernel.EntryOutSize(protocol)
		b = buffer.NewOutMessage(size)
		out := (*fusekernel.EntryOut)(b.Grow(size))
		convertChildInodeEntry(&o.Entry, out)

	case *fuseops.RenameOp:
		b = buffer.NewOutMessage(0)

	case *fuseops.RmDirOp:
		b = buffer.NewOutMessage(0)

	case *fuseops.UnlinkOp:
		b = buffer.NewOutMessage(0)

	case *fuseops.OpenDirOp:
		b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.OpenOut{}))
		out := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
		out.Fh = uint64(o.Handle)

	case *fuseops.ReadDirOp:
		b = buffer.NewOutMessage(uintptr(len(o.Data)))
		b.Append(o.Data)

	case *fuseops.ReleaseDirHandleOp:
		b = buffer.NewOutMessage(0)

	case *fuseops.OpenFileOp:
		b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.OpenOut{}))
		out := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
		out.Fh = uint64(o.Handle)

	case *fuseops.ReadFileOp:
		b = buffer.NewOutMessage(uintptr(len(o.Data)))
		b.Append(o.Data)

	case *fuseops.WriteFileOp:
		b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.WriteOut{}))
		out := (*fusekernel.WriteOut)(b.Grow(unsafe.Sizeof(fusekernel.WriteOut{})))
		out.Size = uint32(len(o.Data))

	case *fuseops.SyncFileOp:
		b = buffer.NewOutMessage(0)

	case *fuseops.FlushFileOp:
		b = buffer.NewOutMessage(0)

	case *fuseops.ReleaseFileHandleOp:
		b = buffer.NewOutMessage(0)

	case *fuseops.ReadSymlinkOp:
		b = buffer.NewOutMessage(uintptr(len(o.Target)))
		b.AppendString(o.Target)

	case *internalStatFSOp:
		b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.StatfsOut{}))
		b.Grow(unsafe.Sizeof(fusekernel.StatfsOut{}))

	case *internalInterruptOp:
		// No response.

	case *internalInitOp:
		b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.InitOut{}))
		out := (*fusekernel.InitOut)(b.Grow(unsafe.Sizeof(fusekernel.InitOut{})))

		out.Major = o.Library.Major
		out.Minor = o.Library.Minor
		out.MaxReadahead = o.MaxReadahead
		out.Flags = uint32(o.Flags)
		out.MaxWrite = o.MaxWrite

	default:
		panic(fmt.Sprintf("Unknown op: %#v", op))
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Internal
////////////////////////////////////////////////////////////////////////

// A sentinel used for unknown ops. The user is expected to respond with a
// non-nil error.
type unknownOp struct {
	opCode uint32
	inode  fuseops.InodeID
}

func (o *unknownOp) ShortDesc() (desc string) {
	desc = fmt.Sprintf("<opcode %d>(inode=%v)", o.opCode, o.inode)
	return
}

func (o *unknownOp) DebugString() string {
	return o.ShortDesc()
}

// Common implementation for our "internal" ops that don't need to be pretty.
type internalOp struct {
}

func (o *internalOp) ShortDesc() string   { return "<internalOp>" }
func (o *internalOp) DebugString() string { return "<internalOp>" }

type internalStatFSOp struct {
	internalOp
}

func (o *internalStatFSOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.StatfsOut{}))
	b.Grow(unsafe.Sizeof(fusekernel.StatfsOut{}))

	return
}

type internalInterruptOp struct {
	internalOp
	FuseID uint64
}

func (o *internalInterruptOp) kernelResponse() (b buffer.OutMessage) {
	panic("Shouldn't get here.")
}

type internalInitOp struct {
	internalOp

	// In
	Kernel fusekernel.Protocol

	// Out
	Library      fusekernel.Protocol
	MaxReadahead uint32
	Flags        fusekernel.InitFlags
	MaxWrite     uint32
}

func (o *internalInitOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.InitOut{}))
	out := (*fusekernel.InitOut)(b.Grow(unsafe.Sizeof(fusekernel.InitOut{})))

	out.Major = o.Library.Major
	out.Minor = o.Library.Minor
	out.MaxReadahead = o.MaxReadahead
	out.Flags = uint32(o.Flags)
	out.MaxWrite = o.MaxWrite

	return
}
<commit_msg>Fixed a bug.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 fuse

import (
	"fmt"
	"syscall"
	"unsafe"

	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/internal/buffer"
	"github.com/jacobsa/fuse/internal/fusekernel"
)

// Return the response that should be sent to the kernel. If the op requires no
// response, return a nil response.
func kernelResponse(
	fuseID uint64,
	op interface{},
	opErr error,
	protocol fusekernel.Protocol) (msg []byte) {
	// If the user replied with an error, create room enough just for the result
	// header and fill it in with an error. Otherwise create an appropriate
	// response.
	var b buffer.OutMessage
	if opErr != nil {
		b = buffer.NewOutMessage(0)
		if errno, ok := opErr.(syscall.Errno); ok {
			b.OutHeader().Error = -int32(errno)
		} else {
			b.OutHeader().Error = -int32(syscall.EIO)
		}
	} else {
		b = kernelResponseForOp(op, protocol)
	}

	msg = b.Bytes()

	// Fill in the rest of the header, if a response is required.
	if msg != nil {
		h := b.OutHeader()
		h.Unique = fuseID
		h.Len = uint32(len(msg))
	}

	return
}

// Like kernelResponse, but assumes the user replied with a nil error to the
// op. Returns a nil response if no response is required.
func kernelResponseForOp(
	op interface{},
	protocol fusekernel.Protocol) (b buffer.OutMessage) {
	// Create the appropriate output message
	switch o := op.(type) {
	case *fuseops.LookUpInodeOp:
		size := fusekernel.EntryOutSize(protocol)
		b = buffer.NewOutMessage(size)
		out := (*fusekernel.EntryOut)(b.Grow(size))
		convertChildInodeEntry(&o.Entry, out)

	case *fuseops.GetInodeAttributesOp:
		size := fusekernel.AttrOutSize(protocol)
		b = buffer.NewOutMessage(size)
		out := (*fusekernel.AttrOut)(b.Grow(size))
		out.AttrValid, out.AttrValidNsec = convertExpirationTime(o.AttributesExpiration)
		convertAttributes(o.Inode, &o.Attributes, &out.Attr)

	case *fuseops.SetInodeAttributesOp:
		size := fusekernel.AttrOutSize(protocol)
		b = buffer.NewOutMessage(size)
		out := (*fusekernel.AttrOut)(b.Grow(size))
		out.AttrValid, out.AttrValidNsec = convertExpirationTime(o.AttributesExpiration)
		convertAttributes(o.Inode, &o.Attributes, &out.Attr)

	case *fuseops.ForgetInodeOp:
		// No response.

	case *fuseops.MkDirOp:
		size := fusekernel.EntryOutSize(protocol)
		b = buffer.NewOutMessage(size)
		out := (*fusekernel.EntryOut)(b.Grow(size))
		convertChildInodeEntry(&o.Entry, out)

	case *fuseops.CreateFileOp:
		eSize := fusekernel.EntryOutSize(protocol)
		b = buffer.NewOutMessage(eSize + unsafe.Sizeof(fusekernel.OpenOut{}))

		e := (*fusekernel.EntryOut)(b.Grow(eSize))
		convertChildInodeEntry(&o.Entry, e)

		oo := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
		oo.Fh = uint64(o.Handle)

	case *fuseops.CreateSymlinkOp:
		size := fusekernel.EntryOutSize(protocol)
		b = buffer.NewOutMessage(size)
		out := (*fusekernel.EntryOut)(b.Grow(size))
		convertChildInodeEntry(&o.Entry, out)

	case *fuseops.RenameOp:
		b = buffer.NewOutMessage(0)

	case *fuseops.RmDirOp:
		b = buffer.NewOutMessage(0)

	case *fuseops.UnlinkOp:
		b = buffer.NewOutMessage(0)

	case *fuseops.OpenDirOp:
		b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.OpenOut{}))
		out := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
		out.Fh = uint64(o.Handle)

	case *fuseops.ReadDirOp:
		b = buffer.NewOutMessage(uintptr(len(o.Data)))
		b.Append(o.Data)

	case *fuseops.ReleaseDirHandleOp:
		b = buffer.NewOutMessage(0)

	case *fuseops.OpenFileOp:
		b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.OpenOut{}))
		out := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
		out.Fh = uint64(o.Handle)

	case *fuseops.ReadFileOp:
		b = buffer.NewOutMessage(uintptr(len(o.Data)))
		b.Append(o.Data)

	case *fuseops.WriteFileOp:
		b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.WriteOut{}))
		out := (*fusekernel.WriteOut)(b.Grow(unsafe.Sizeof(fusekernel.WriteOut{})))
		out.Size = uint32(len(o.Data))

	case *fuseops.SyncFileOp:
		b = buffer.NewOutMessage(0)

	case *fuseops.FlushFileOp:
		b = buffer.NewOutMessage(0)

	case *fuseops.ReleaseFileHandleOp:
		b = buffer.NewOutMessage(0)

	case *fuseops.ReadSymlinkOp:
		b = buffer.NewOutMessage(uintptr(len(o.Target)))
		b.AppendString(o.Target)

	case *internalStatFSOp:
		b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.StatfsOut{}))
		b.Grow(unsafe.Sizeof(fusekernel.StatfsOut{}))

	case *internalInterruptOp:
		// No response.

	case *internalInitOp:
		b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.InitOut{}))
		out := (*fusekernel.InitOut)(b.Grow(unsafe.Sizeof(fusekernel.InitOut{})))

		out.Major = o.Library.Major
		out.Minor = o.Library.Minor
		out.MaxReadahead = o.MaxReadahead
		out.Flags = uint32(o.Flags)
		out.MaxWrite = o.MaxWrite

	default:
		panic(fmt.Sprintf("Unknown op: %#v", op))
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Internal
////////////////////////////////////////////////////////////////////////

// A sentinel used for unknown ops. The user is expected to respond with a
// non-nil error.
type unknownOp struct {
	opCode uint32
	inode  fuseops.InodeID
}

func (o *unknownOp) ShortDesc() (desc string) {
	desc = fmt.Sprintf("<opcode %d>(inode=%v)", o.opCode, o.inode)
	return
}

func (o *unknownOp) DebugString() string {
	return o.ShortDesc()
}

// Common implementation for our "internal" ops that don't need to be pretty.
type internalOp struct {
}

func (o *internalOp) ShortDesc() string   { return "<internalOp>" }
func (o *internalOp) DebugString() string { return "<internalOp>" }

type internalStatFSOp struct {
	internalOp
}

func (o *internalStatFSOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.StatfsOut{}))
	b.Grow(unsafe.Sizeof(fusekernel.StatfsOut{}))

	return
}

type internalInterruptOp struct {
	internalOp
	FuseID uint64
}

func (o *internalInterruptOp) kernelResponse() (b buffer.OutMessage) {
	panic("Shouldn't get here.")
}

type internalInitOp struct {
	internalOp

	// In
	Kernel fusekernel.Protocol

	// Out
	Library      fusekernel.Protocol
	MaxReadahead uint32
	Flags        fusekernel.InitFlags
	MaxWrite     uint32
}

func (o *internalInitOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.InitOut{}))
	out := (*fusekernel.InitOut)(b.Grow(unsafe.Sizeof(fusekernel.InitOut{})))

	out.Major = o.Library.Major
	out.Minor = o.Library.Minor
	out.MaxReadahead = o.MaxReadahead
	out.Flags = uint32(o.Flags)
	out.MaxWrite = o.MaxWrite

	return
}
<|endoftext|>


<commit_before>package config

import "fmt"

type optType int

func (ot optType) String() string {
	return optTypeMap[ot]
}

const (
	noneType optType = iota
	boolType
	stringType
	intType
	int8Type
	int16Type
	int32Type
	int64Type
	uintType
	uint8Type
	uint16Type
	uint32Type
	uint64Type
	float32Type
	float64Type

	stringsType
	intsType
	int64sType
	uintsType
	uint64sType
)

var optTypeMap = map[optType]string{
	noneType:    "none",
	boolType:    "bool",
	stringType:  "string",
	intType:     "int",
	int8Type:    "int8",
	int16Type:   "int16",
	int32Type:   "int32",
	int64Type:   "int64",
	uintType:    "uint",
	uint8Type:   "uint8",
	uint16Type:  "uint16",
	uint32Type:  "uint32",
	uint64Type:  "uint64",
	float32Type: "float32",
	float64Type: "float64",

	stringsType: "[]string",
	intsType:    "[]int",
	int64sType:  "[]int64",
	uintsType:   "[]uint",
	uint64sType: "[]uint64",
}

type baseOpt struct {
	name     string
	help     string
	short    string
	_default interface{}

	_type      optType
	validators []Validator
}

var _ ValidatorChainOpt = baseOpt{}

func newBaseOpt(short, name string, _default interface{}, help string,
	optType optType) baseOpt {
	o := baseOpt{
		short:    short,
		name:     name,
		help:     help,
		_default: _default,
		_type:    optType,
	}
	o.Default()
	return o
}

// SetValidators sets the validator chain
func (o baseOpt) SetValidators(vs []Validator) ValidatorChainOpt {
	o.validators = vs
	return o
}

// GetValidators returns the validator chain
func (o baseOpt) GetValidators() []Validator {
	return o.validators
}

// GetName returns the name of the option.
func (o baseOpt) Name() string {
	return o.name
}

// GetShort returns the shorthand name of the option.
func (o baseOpt) Short() string {
	return o.short
}

func (o baseOpt) IsBool() bool {
	if o._type == boolType {
		return true
	}
	return false
}

// GetHelp returns the help doc of the option.
func (o baseOpt) Help() string {
	return o.help
}

// GetDefault returns the default value of the option.
func (o baseOpt) Default() interface{} {
	if o._default == nil {
		return nil
	}

	switch o._type {
	case boolType:
		return o._default.(bool)
	case stringType:
		return o._default.(string)
	case intType:
		return o._default.(int)
	case int8Type:
		return o._default.(int8)
	case int16Type:
		return o._default.(int16)
	case int32Type:
		return o._default.(int32)
	case int64Type:
		return o._default.(int64)
	case uintType:
		return o._default.(uint)
	case uint8Type:
		return o._default.(uint8)
	case uint16Type:
		return o._default.(uint16)
	case uint32Type:
		return o._default.(uint32)
	case uint64Type:
		return o._default.(uint64)
	case float32Type:
		return o._default.(float32)
	case float64Type:
		return o._default.(float64)
	default:
		panic(fmt.Errorf("don't support the type %s", o._type))
	}
}

// Parse parses the value of the option to a certain type.
func (o baseOpt) Parse(data interface{}) (v interface{}, err error) {
	switch o._type {
	case boolType:
		return ToBool(data)
	case stringType:
		return ToString(data)
	case intType, int8Type, int16Type, int32Type, int64Type:
		v, err = ToInt64(data)
	case uintType, uint8Type, uint16Type, uint32Type, uint64Type:
		v, err = ToUint64(data)
	case float32Type, float64Type:
		v, err = ToFloat64(data)
	default:
		panic(fmt.Errorf("don't support the type %s", o._type))
	}

	if err != nil {
		return
	}

	switch o._type {
	// case uint64Type:
	// case int64Type:
	// case float64Type:
	case intType:
		v = int(v.(int64))
	case int8Type:
		v = int8(v.(int64))
	case int16Type:
		v = int16(v.(int64))
	case int32Type:
		v = int32(v.(int64))
	case uintType:
		v = uint(v.(uint64))
	case uint8Type:
		v = uint8(v.(uint64))
	case uint16Type:
		v = uint16(v.(uint64))
	case uint32Type:
		v = uint32(v.(uint64))
	case float32Type:
		v = float32(v.(float64))
	}
	return
}

// BoolOpt return a new bool option.
func BoolOpt(short, name string, _default bool, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, boolType)
}

// StrOpt return a new string option.
func StrOpt(short, name string, _default string, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, stringType)
}

// IntOpt return a new int option.
func IntOpt(short, name string, _default int, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, intType)
}

// Int8Opt return a new int8 option.
func Int8Opt(short, name string, _default int8, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, int8Type)
}

// Int16Opt return a new int16 option.
func Int16Opt(short, name string, _default int16, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, int16Type)
}

// Int32Opt return a new int32 option.
func Int32Opt(short, name string, _default int32, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, int32Type)
}

// Int64Opt return a new int64 option.
func Int64Opt(short, name string, _default int64, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, int64Type)
}

// UintOpt return a new uint option.
func UintOpt(short, name string, _default uint, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, uintType)
}

// Uint8Opt return a new uint8 option.
func Uint8Opt(short, name string, _default uint8, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, uint8Type)
}

// Uint16Opt return a new uint16 option.
func Uint16Opt(short, name string, _default uint16, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, uint16Type)
}

// Uint32Opt return a new uint32 option.
func Uint32Opt(short, name string, _default uint32, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, uint32Type)
}

// Uint64Opt return a new uint64 option.
func Uint64Opt(short, name string, _default uint64, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, uint64Type)
}

// Float32Opt return a new float32 option.
func Float32Opt(short, name string, _default float32, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, float32Type)
}

// Float64Opt return a new float64 option.
func Float64Opt(short, name string, _default float64, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, float64Type)
}

// Bool return a new bool option.
func Bool(name string, _default bool, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, boolType)
}

// Str return a new string option.
func Str(name string, _default string, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, stringType)
}

// Int return a new int option.
func Int(name string, _default int, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, intType)
}

// Int8 return a new int8 option.
func Int8(name string, _default int8, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, int8Type)
}

// Int16 return a new int16 option.
func Int16(name string, _default int16, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, int16Type)
}

// Int32 return a new int32 option.
func Int32(name string, _default int32, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, int32Type)
}

// Int64 return a new int64 option.
func Int64(name string, _default int64, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, int64Type)
}

// Uint return a new uint option.
func Uint(name string, _default uint, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, uintType)
}

// Uint8 return a new uint8 option.
func Uint8(name string, _default uint8, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, uint8Type)
}

// Uint16 return a new uint16 option.
func Uint16(name string, _default uint16, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, uint16Type)
}

// Uint32 return a new uint32 option.
func Uint32(name string, _default uint32, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, uint32Type)
}

// Uint64 return a new uint64 option.
func Uint64(name string, _default uint64, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, uint64Type)
}

// Float32 return a new float32 option.
func Float32(name string, _default float32, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, float32Type)
}

// Float64 return a new float64 option.
func Float64(name string, _default float64, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, float64Type)
}
<commit_msg>Update doc of the options.<commit_after>package config

import "fmt"

type optType int

func (ot optType) String() string {
	return optTypeMap[ot]
}

const (
	noneType optType = iota
	boolType
	stringType
	intType
	int8Type
	int16Type
	int32Type
	int64Type
	uintType
	uint8Type
	uint16Type
	uint32Type
	uint64Type
	float32Type
	float64Type

	stringsType
	intsType
	int64sType
	uintsType
	uint64sType
)

var optTypeMap = map[optType]string{
	noneType:    "none",
	boolType:    "bool",
	stringType:  "string",
	intType:     "int",
	int8Type:    "int8",
	int16Type:   "int16",
	int32Type:   "int32",
	int64Type:   "int64",
	uintType:    "uint",
	uint8Type:   "uint8",
	uint16Type:  "uint16",
	uint32Type:  "uint32",
	uint64Type:  "uint64",
	float32Type: "float32",
	float64Type: "float64",

	stringsType: "[]string",
	intsType:    "[]int",
	int64sType:  "[]int64",
	uintsType:   "[]uint",
	uint64sType: "[]uint64",
}

type baseOpt struct {
	name     string
	help     string
	short    string
	_default interface{}

	_type      optType
	validators []Validator
}

var _ ValidatorChainOpt = baseOpt{}

func newBaseOpt(short, name string, _default interface{}, help string,
	optType optType) baseOpt {
	o := baseOpt{
		short:    short,
		name:     name,
		help:     help,
		_default: _default,
		_type:    optType,
	}
	o.Default()
	return o
}

// SetValidators sets the validator chain
func (o baseOpt) SetValidators(vs []Validator) ValidatorChainOpt {
	o.validators = vs
	return o
}

// GetValidators returns the validator chain
func (o baseOpt) GetValidators() []Validator {
	return o.validators
}

// GetName returns the name of the option.
func (o baseOpt) Name() string {
	return o.name
}

// GetShort returns the shorthand name of the option.
func (o baseOpt) Short() string {
	return o.short
}

func (o baseOpt) IsBool() bool {
	if o._type == boolType {
		return true
	}
	return false
}

// GetHelp returns the help doc of the option.
func (o baseOpt) Help() string {
	return o.help
}

// GetDefault returns the default value of the option.
func (o baseOpt) Default() interface{} {
	if o._default == nil {
		return nil
	}

	switch o._type {
	case boolType:
		return o._default.(bool)
	case stringType:
		return o._default.(string)
	case intType:
		return o._default.(int)
	case int8Type:
		return o._default.(int8)
	case int16Type:
		return o._default.(int16)
	case int32Type:
		return o._default.(int32)
	case int64Type:
		return o._default.(int64)
	case uintType:
		return o._default.(uint)
	case uint8Type:
		return o._default.(uint8)
	case uint16Type:
		return o._default.(uint16)
	case uint32Type:
		return o._default.(uint32)
	case uint64Type:
		return o._default.(uint64)
	case float32Type:
		return o._default.(float32)
	case float64Type:
		return o._default.(float64)
	default:
		panic(fmt.Errorf("don't support the type %s", o._type))
	}
}

// Parse parses the value of the option to a certain type.
func (o baseOpt) Parse(data interface{}) (v interface{}, err error) {
	switch o._type {
	case boolType:
		return ToBool(data)
	case stringType:
		return ToString(data)
	case intType, int8Type, int16Type, int32Type, int64Type:
		v, err = ToInt64(data)
	case uintType, uint8Type, uint16Type, uint32Type, uint64Type:
		v, err = ToUint64(data)
	case float32Type, float64Type:
		v, err = ToFloat64(data)
	default:
		panic(fmt.Errorf("don't support the type %s", o._type))
	}

	if err != nil {
		return
	}

	switch o._type {
	// case uint64Type:
	// case int64Type:
	// case float64Type:
	case intType:
		v = int(v.(int64))
	case int8Type:
		v = int8(v.(int64))
	case int16Type:
		v = int16(v.(int64))
	case int32Type:
		v = int32(v.(int64))
	case uintType:
		v = uint(v.(uint64))
	case uint8Type:
		v = uint8(v.(uint64))
	case uint16Type:
		v = uint16(v.(uint64))
	case uint32Type:
		v = uint32(v.(uint64))
	case float32Type:
		v = float32(v.(float64))
	}
	return
}

// BoolOpt return a new bool option.
func BoolOpt(short, name string, _default bool, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, boolType)
}

// StrOpt return a new string option.
func StrOpt(short, name string, _default string, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, stringType)
}

// IntOpt return a new int option.
func IntOpt(short, name string, _default int, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, intType)
}

// Int8Opt return a new int8 option.
func Int8Opt(short, name string, _default int8, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, int8Type)
}

// Int16Opt return a new int16 option.
func Int16Opt(short, name string, _default int16, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, int16Type)
}

// Int32Opt return a new int32 option.
func Int32Opt(short, name string, _default int32, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, int32Type)
}

// Int64Opt return a new int64 option.
func Int64Opt(short, name string, _default int64, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, int64Type)
}

// UintOpt return a new uint option.
func UintOpt(short, name string, _default uint, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, uintType)
}

// Uint8Opt return a new uint8 option.
func Uint8Opt(short, name string, _default uint8, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, uint8Type)
}

// Uint16Opt return a new uint16 option.
func Uint16Opt(short, name string, _default uint16, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, uint16Type)
}

// Uint32Opt return a new uint32 option.
func Uint32Opt(short, name string, _default uint32, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, uint32Type)
}

// Uint64Opt return a new uint64 option.
func Uint64Opt(short, name string, _default uint64, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, uint64Type)
}

// Float32Opt return a new float32 option.
func Float32Opt(short, name string, _default float32, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, float32Type)
}

// Float64Opt return a new float64 option.
func Float64Opt(short, name string, _default float64, help string) ValidatorChainOpt {
	return newBaseOpt(short, name, _default, help, float64Type)
}

// Bool is equal to BoolOpt("", name, _default, help).
func Bool(name string, _default bool, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, boolType)
}

// Str is equal to StrOpt("", name, _default, help).
func Str(name string, _default string, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, stringType)
}

// Int is equal to IntOpt("", name, _default, help).
func Int(name string, _default int, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, intType)
}

// Int8 is equal to Int8Opt("", name, _default, help).
func Int8(name string, _default int8, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, int8Type)
}

// Int16 is equal to Int16Opt("", name, _default, help).
func Int16(name string, _default int16, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, int16Type)
}

// Int32 is equal to Int32Opt("", name, _default, help).
func Int32(name string, _default int32, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, int32Type)
}

// Int64 is equal to Int64Opt("", name, _default, help).
func Int64(name string, _default int64, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, int64Type)
}

// Uint is equal to UintOpt("", name, _default, help).
func Uint(name string, _default uint, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, uintType)
}

// Uint8 is equal to Uint8Opt("", name, _default, help).
func Uint8(name string, _default uint8, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, uint8Type)
}

// Uint16 is equal to Uint16Opt("", name, _default, help).
func Uint16(name string, _default uint16, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, uint16Type)
}

// Uint32 is equal to Uint32Opt("", name, _default, help).
func Uint32(name string, _default uint32, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, uint32Type)
}

// Uint64 is equal to Uint64Opt("", name, _default, help).
func Uint64(name string, _default uint64, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, uint64Type)
}

// Float32 is equal to Float32Opt("", name, _default, help).
func Float32(name string, _default float32, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, float32Type)
}

// Float64 is equal to Float64Opt("", name, _default, help).
func Float64(name string, _default float64, help string) ValidatorChainOpt {
	return newBaseOpt("", name, _default, help, float64Type)
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package spaces_test

import (
	"github.com/juju/errors"
	"github.com/juju/names"
	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"

	"github.com/juju/juju/apiserver/common"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/apiserver/spaces"
	apiservertesting "github.com/juju/juju/apiserver/testing"
	coretesting "github.com/juju/juju/testing"
)

type SpacesSuite struct {
	coretesting.BaseSuite
	apiservertesting.StubNetwork

	resources  *common.Resources
	authorizer apiservertesting.FakeAuthorizer
	facade     spaces.API
}

var _ = gc.Suite(&SpacesSuite{})

func (s *SpacesSuite) SetUpSuite(c *gc.C) {
	s.StubNetwork.SetUpSuite(c)
	s.BaseSuite.SetUpSuite(c)
}

func (s *SpacesSuite) TearDownSuite(c *gc.C) {
	s.BaseSuite.TearDownSuite(c)
}

func (s *SpacesSuite) SetUpTest(c *gc.C) {
	s.BaseSuite.SetUpTest(c)
	apiservertesting.BackingInstance.SetUp(c, apiservertesting.StubZonedEnvironName, apiservertesting.WithZones, apiservertesting.WithSpaces, apiservertesting.WithSubnets)

	s.resources = common.NewResources()
	s.authorizer = apiservertesting.FakeAuthorizer{
		Tag:            names.NewUserTag("admin"),
		EnvironManager: false,
	}

	var err error
	s.facade, err = spaces.NewAPI(apiservertesting.BackingInstance, s.resources, s.authorizer)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(s.facade, gc.NotNil)
}

func (s *SpacesSuite) TearDownTest(c *gc.C) {
	if s.resources != nil {
		s.resources.StopAll()
	}
	s.BaseSuite.TearDownTest(c)
}

func (s *SpacesSuite) TestNewAPI(c *gc.C) {
	// Clients are allowed.
	facade, err := spaces.NewAPI(apiservertesting.BackingInstance, s.resources, s.authorizer)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(facade, gc.NotNil)
	// No calls so far.
	apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub)

	// Agents are not allowed
	agentAuthorizer := s.authorizer
	agentAuthorizer.Tag = names.NewMachineTag("42")
	facade, err = spaces.NewAPI(apiservertesting.BackingInstance, s.resources, agentAuthorizer)
	c.Assert(err, jc.DeepEquals, common.ErrPerm)
	c.Assert(facade, gc.IsNil)
	// No calls so far.
	apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub)
}

type checkAddSpacesParams struct {
	Name      string
	Subnets   []string
	Error     string
	MakesCall bool
	Public    bool
}

func (s *SpacesSuite) checkAddSpaces(c *gc.C, p checkAddSpacesParams) {
	args := params.CreateSpaceParams{}
	if p.Name != "" {
		args.SpaceTag = "space-" + p.Name
	}
	if len(p.Subnets) > 0 {
		for _, cidr := range p.Subnets {
			args.SubnetTags = append(args.SubnetTags, "subnet-"+cidr)
		}
	}
	args.Public = p.Public

	spaces := params.CreateSpacesParams{}
	spaces.Spaces = append(spaces.Spaces, args)
	results, err := s.facade.CreateSpaces(spaces)

	c.Assert(len(results.Results), gc.Equals, 1)
	c.Assert(err, gc.IsNil)
	if p.Error == "" {
		c.Assert(results.Results[0].Error, gc.IsNil)
	} else {
		c.Assert(results.Results[0].Error, gc.NotNil)
		c.Assert(results.Results[0].Error, gc.ErrorMatches, p.Error)
	}

	if p.Error == "" || p.MakesCall {
		apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub,
			apiservertesting.BackingCall("AddSpace", p.Name, p.Subnets, p.Public),
		)
	} else {
		apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub)
	}

}

func (s *SpacesSuite) TestAddSpacesOneSubnet(c *gc.C) {
	p := checkAddSpacesParams{
		Name:    "foo",
		Subnets: []string{"10.0.0.0/24"}}
	s.checkAddSpaces(c, p)
}

func (s *SpacesSuite) TestAddSpacesTwoSubnets(c *gc.C) {
	p := checkAddSpacesParams{
		Name:    "foo",
		Subnets: []string{"10.0.0.0/24", "10.0.1.0/24"}}
	s.checkAddSpaces(c, p)
}

func (s *SpacesSuite) TestAddSpacesManySubnets(c *gc.C) {
	p := checkAddSpacesParams{
		Name: "foo",
		Subnets: []string{"10.0.0.0/24", "10.0.1.0/24", "10.0.2.0/24",
			"10.0.3.0/24", "10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24"}}
	s.checkAddSpaces(c, p)
}

func (s *SpacesSuite) TestAddSpacesAPIError(c *gc.C) {
	apiservertesting.SharedStub.SetErrors(errors.AlreadyExistsf("space-foo"))
	p := checkAddSpacesParams{
		Name:      "foo",
		Subnets:   []string{"10.0.0.0/24"},
		MakesCall: true,
		Error:     "cannot create space: space-foo already exists"}
	s.checkAddSpaces(c, p)
}

func (s *SpacesSuite) TestCreateInvalidSpace(c *gc.C) {
	p := checkAddSpacesParams{
		Name:    "-",
		Subnets: []string{"10.0.0.0/24"},
		Error:   "given SpaceTag is invalid: \"space--\" is not a valid space tag"}
	s.checkAddSpaces(c, p)
}

func (s *SpacesSuite) TestCreateInvalidSubnet(c *gc.C) {
	p := checkAddSpacesParams{
		Name:    "foo",
		Subnets: []string{"bar"},
		Error:   "given SubnetTag is invalid: \"subnet-bar\" is not a valid subnet tag"}
	s.checkAddSpaces(c, p)
}

func (s *SpacesSuite) TestPublic(c *gc.C) {
	p := checkAddSpacesParams{
		Name:    "foo",
		Subnets: []string{"10.0.0.0/24"},
		Public:  true}
	s.checkAddSpaces(c, p)
}

func (s *SpacesSuite) TestEmptySpaceName(c *gc.C) {
	p := checkAddSpacesParams{
		Subnets: []string{"10.0.0.0/24"},
		Error:   "given SpaceTag is invalid: \"\" is not a valid tag"}
	s.checkAddSpaces(c, p)
}

func (s *SpacesSuite) TestNoSubnets(c *gc.C) {
	p := checkAddSpacesParams{
		Name:  "foo",
		Error: "calling CreateSpaces with zero subnets is not valid"}
	s.checkAddSpaces(c, p)
}

func (s *SpacesSuite) TestListSpacesDefault(c *gc.C) {
	spaces, err := s.facade.ListSpaces()
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(spaces.Error, gc.IsNil)
	expected := []params.SpaceListResult{
		params.SpaceListResult{
			Name: "default",
			Subnets: []params.Subnet{
				params.Subnet{
					CIDR:       "192.168.0.0/24",
					ProviderId: "provider-192.168.0.0/24",
					Zones:      []string{"foo"},
					Status:     "in-use",
				},
				params.Subnet{
					CIDR:       "192.168.3.0/24",
					ProviderId: "provider-192.168.3.0/24",
					VLANTag:    23,
					Zones:      []string{"bar", "bam"},
				},
			},
		},
		params.SpaceListResult{
			Name: "dmz",
			Subnets: []params.Subnet{
				params.Subnet{
					CIDR:       "192.168.1.0/24",
					ProviderId: "provider-192.168.1.0/24",
					VLANTag:    23,
					Zones:      []string{"bar", "bam"},
				},
			},
		},
		params.SpaceListResult{
			Name: "private",
			Subnets: []params.Subnet{
				params.Subnet{
					CIDR:       "192.168.2.0/24",
					ProviderId: "provider-192.168.2.0/24",
					Zones:      []string{"foo"},
					Status:     "in-use",
				},
			},
		},
	}
	c.Assert(spaces.Results, jc.DeepEquals, expected)
}
<commit_msg>Test first failure mode<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package spaces_test

import (
	"github.com/juju/errors"
	"github.com/juju/names"
	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"

	"github.com/juju/juju/apiserver/common"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/apiserver/spaces"
	apiservertesting "github.com/juju/juju/apiserver/testing"
	coretesting "github.com/juju/juju/testing"
)

type SpacesSuite struct {
	coretesting.BaseSuite
	apiservertesting.StubNetwork

	resources  *common.Resources
	authorizer apiservertesting.FakeAuthorizer
	facade     spaces.API
}

var _ = gc.Suite(&SpacesSuite{})

func (s *SpacesSuite) SetUpSuite(c *gc.C) {
	s.StubNetwork.SetUpSuite(c)
	s.BaseSuite.SetUpSuite(c)
}

func (s *SpacesSuite) TearDownSuite(c *gc.C) {
	s.BaseSuite.TearDownSuite(c)
}

func (s *SpacesSuite) SetUpTest(c *gc.C) {
	s.BaseSuite.SetUpTest(c)
	apiservertesting.BackingInstance.SetUp(c, apiservertesting.StubZonedEnvironName, apiservertesting.WithZones, apiservertesting.WithSpaces, apiservertesting.WithSubnets)

	s.resources = common.NewResources()
	s.authorizer = apiservertesting.FakeAuthorizer{
		Tag:            names.NewUserTag("admin"),
		EnvironManager: false,
	}

	var err error
	s.facade, err = spaces.NewAPI(apiservertesting.BackingInstance, s.resources, s.authorizer)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(s.facade, gc.NotNil)
}

func (s *SpacesSuite) TearDownTest(c *gc.C) {
	if s.resources != nil {
		s.resources.StopAll()
	}
	s.BaseSuite.TearDownTest(c)
}

func (s *SpacesSuite) TestNewAPI(c *gc.C) {
	// Clients are allowed.
	facade, err := spaces.NewAPI(apiservertesting.BackingInstance, s.resources, s.authorizer)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(facade, gc.NotNil)
	// No calls so far.
	apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub)

	// Agents are not allowed
	agentAuthorizer := s.authorizer
	agentAuthorizer.Tag = names.NewMachineTag("42")
	facade, err = spaces.NewAPI(apiservertesting.BackingInstance, s.resources, agentAuthorizer)
	c.Assert(err, jc.DeepEquals, common.ErrPerm)
	c.Assert(facade, gc.IsNil)
	// No calls so far.
	apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub)
}

type checkAddSpacesParams struct {
	Name      string
	Subnets   []string
	Error     string
	MakesCall bool
	Public    bool
}

func (s *SpacesSuite) checkAddSpaces(c *gc.C, p checkAddSpacesParams) {
	args := params.CreateSpaceParams{}
	if p.Name != "" {
		args.SpaceTag = "space-" + p.Name
	}
	if len(p.Subnets) > 0 {
		for _, cidr := range p.Subnets {
			args.SubnetTags = append(args.SubnetTags, "subnet-"+cidr)
		}
	}
	args.Public = p.Public

	spaces := params.CreateSpacesParams{}
	spaces.Spaces = append(spaces.Spaces, args)
	results, err := s.facade.CreateSpaces(spaces)

	c.Assert(len(results.Results), gc.Equals, 1)
	c.Assert(err, gc.IsNil)
	if p.Error == "" {
		c.Assert(results.Results[0].Error, gc.IsNil)
	} else {
		c.Assert(results.Results[0].Error, gc.NotNil)
		c.Assert(results.Results[0].Error, gc.ErrorMatches, p.Error)
	}

	if p.Error == "" || p.MakesCall {
		apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub,
			apiservertesting.BackingCall("AddSpace", p.Name, p.Subnets, p.Public),
		)
	} else {
		apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub)
	}

}

func (s *SpacesSuite) TestAddSpacesOneSubnet(c *gc.C) {
	p := checkAddSpacesParams{
		Name:    "foo",
		Subnets: []string{"10.0.0.0/24"}}
	s.checkAddSpaces(c, p)
}

func (s *SpacesSuite) TestAddSpacesTwoSubnets(c *gc.C) {
	p := checkAddSpacesParams{
		Name:    "foo",
		Subnets: []string{"10.0.0.0/24", "10.0.1.0/24"}}
	s.checkAddSpaces(c, p)
}

func (s *SpacesSuite) TestAddSpacesManySubnets(c *gc.C) {
	p := checkAddSpacesParams{
		Name: "foo",
		Subnets: []string{"10.0.0.0/24", "10.0.1.0/24", "10.0.2.0/24",
			"10.0.3.0/24", "10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24"}}
	s.checkAddSpaces(c, p)
}

func (s *SpacesSuite) TestAddSpacesAPIError(c *gc.C) {
	apiservertesting.SharedStub.SetErrors(errors.AlreadyExistsf("space-foo"))
	p := checkAddSpacesParams{
		Name:      "foo",
		Subnets:   []string{"10.0.0.0/24"},
		MakesCall: true,
		Error:     "cannot create space: space-foo already exists"}
	s.checkAddSpaces(c, p)
}

func (s *SpacesSuite) TestCreateInvalidSpace(c *gc.C) {
	p := checkAddSpacesParams{
		Name:    "-",
		Subnets: []string{"10.0.0.0/24"},
		Error:   "given SpaceTag is invalid: \"space--\" is not a valid space tag"}
	s.checkAddSpaces(c, p)
}

func (s *SpacesSuite) TestCreateInvalidSubnet(c *gc.C) {
	p := checkAddSpacesParams{
		Name:    "foo",
		Subnets: []string{"bar"},
		Error:   "given SubnetTag is invalid: \"subnet-bar\" is not a valid subnet tag"}
	s.checkAddSpaces(c, p)
}

func (s *SpacesSuite) TestPublic(c *gc.C) {
	p := checkAddSpacesParams{
		Name:    "foo",
		Subnets: []string{"10.0.0.0/24"},
		Public:  true}
	s.checkAddSpaces(c, p)
}

func (s *SpacesSuite) TestEmptySpaceName(c *gc.C) {
	p := checkAddSpacesParams{
		Subnets: []string{"10.0.0.0/24"},
		Error:   "given SpaceTag is invalid: \"\" is not a valid tag"}
	s.checkAddSpaces(c, p)
}

func (s *SpacesSuite) TestNoSubnets(c *gc.C) {
	p := checkAddSpacesParams{
		Name:  "foo",
		Error: "calling CreateSpaces with zero subnets is not valid"}
	s.checkAddSpaces(c, p)
}

func (s *SpacesSuite) TestListSpacesDefault(c *gc.C) {
	spaces, err := s.facade.ListSpaces()
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(spaces.Error, gc.IsNil)
	expected := []params.SpaceListResult{
		params.SpaceListResult{
			Name: "default",
			Subnets: []params.Subnet{
				params.Subnet{
					CIDR:       "192.168.0.0/24",
					ProviderId: "provider-192.168.0.0/24",
					Zones:      []string{"foo"},
					Status:     "in-use",
				},
				params.Subnet{
					CIDR:       "192.168.3.0/24",
					ProviderId: "provider-192.168.3.0/24",
					VLANTag:    23,
					Zones:      []string{"bar", "bam"},
				},
			},
		},
		params.SpaceListResult{
			Name: "dmz",
			Subnets: []params.Subnet{
				params.Subnet{
					CIDR:       "192.168.1.0/24",
					ProviderId: "provider-192.168.1.0/24",
					VLANTag:    23,
					Zones:      []string{"bar", "bam"},
				},
			},
		},
		params.SpaceListResult{
			Name: "private",
			Subnets: []params.Subnet{
				params.Subnet{
					CIDR:       "192.168.2.0/24",
					ProviderId: "provider-192.168.2.0/24",
					Zones:      []string{"foo"},
					Status:     "in-use",
				},
			},
		},
	}
	c.Assert(spaces.Results, jc.DeepEquals, expected)
}

func (s *SpacesSuite) TestListSpacesAllSpacesError(c *gc.C) {
	boom := errors.New("boom")
	apiservertesting.BackingInstance.SetErrors(boom)
	spaces, err := s.facade.ListSpaces()
	c.Assert(err, gc.ErrorMatches, "cannot list spaces: boom")
	c.Assert(spaces.Error.Error(), gc.Equals, "boom")
}
<|endoftext|>


<commit_before>// Copyright 2014 Vasco Costa. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package owm implements a wrapper for the OpenWeatherMap API.
//
// Read more about the OpenWeatherMap API here: http://openweathermap.org/api.
package owm

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
)

type weatherLine struct {
	Id          int
	Main        string
	Description string
	Icon        string
}

// Weather represents the current weather at a specific location.
//
// It is returned by the WeatherBy* methods of Client.
type Weather struct {
	Coord struct {
		Lat float64
		Lon float64
	}
	Sys struct {
		Type    int
		Id      int
		Country string
		Sunrise int
		Sunset  int
	}
	Weather []weatherLine
	Base    string
	Main    struct {
		Temp      float64
		Humidity  int
		TempMin   float64 `json:"temp_min"`
		TempMax   float64 `json:"temp_max"`
		Pressure  float64
		SeaLevel  float64 `json:"sea_level"`
		GrndLevel float64 `json:"grnd_level"`
	}
	Wind struct {
		Speed float64
		Deg   float64
		Gust  float64
	}
	Clouds struct {
		All int
	}
	Dt   int
	Id   int
	Name string
	Cod  int
}

type weatherSet struct {
	Cnt     int
	Weather []Weather `json:"list"`
}

// Forecast represents the weather forecast for a specific location.
//
// It is returned by the ForecastBy* methods of Client.
type Forecast struct {
	Cod  string
	City struct {
		Id    int
		Name  string
		Coord struct {
			Lon float64
			Lat float64
		}
		Country string
	}
	Cnt     int
	Weather []Weather `json:"list"`
}

// Client represents an OpenWeatherMap API client.
type Client struct {
	key     string
	baseURL string
}

// NewClient returns a new Client given an API key.
//
// Pass an empty string as the key argument to use the client without an API key.
func NewClient(key string) *Client {
	return &Client{key, "http://api.openweathermap.org/data/2.5/"}
}

func (c *Client) data(url string) (data []byte, err error) {
	res, err := http.Get(url)
	if err != nil {
		err = errors.New("owm: error while fetching weather data")
		return
	}
	data, err = ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		err = errors.New("owm: error while fetching weather data")
		return
	}
	return
}

func (c *Client) weather(url string) (w Weather, err error) {
	if c.key != "" {
		url += "&APPID=" + c.key
	}
	data, err := c.data(url)
	if err != nil || strings.Contains(string(data), `"cod":"404"`) {
		err = errors.New("owm: error while fetching weather data")
		return
	}
	err = json.Unmarshal(data, &w)
	if err != nil {
		err = errors.New("owm: error while decoding weather data")
		return
	}
	return
}

func (c *Client) weatherSet(url string) (ws weatherSet, err error) {
	if c.key != "" {
		url += "&APPID=" + c.key
	}
	data, err := c.data(url)
	if err != nil || strings.Contains(string(data), `"cod":"404"`) {
		err = errors.New("owm: error while fetching weather data")
		return
	}
	err = json.Unmarshal(data, &ws)
	if err != nil {
		err = errors.New("owm: error while decoding weather data")
		return
	}
	return
}

func (c *Client) forecast(url string) (f Forecast, err error) {
	if c.key != "" {
		url += "&APPID=" + c.key
	}
	data, err := c.data(url)
	if err != nil || strings.Contains(string(data), `"cod":"404"`) {
		err = errors.New("owm: error while fetching forecast data")
		return
	}
	err = json.Unmarshal(data, &f)
	if err != nil {
		err = errors.New("owm: error while decoding forecast data")
		return
	}
	return
}

// WeatherByName decodes the current weather given the city name and the units.
func (c *Client) WeatherByName(name string, units string) (w Weather, err error) {
	w, err = c.weather(c.baseURL +
		"weather" +
		"?q=" + name +
		"&units=" + units)
	return
}

// WeatherById decodes the current weather given the city id and the units.
func (c *Client) WeatherById(id int, units string) (w Weather, err error) {
	w, err = c.weather(c.baseURL +
		"weather" +
		"?id=" + strconv.Itoa(id) +
		"&units=" + units)
	return
}

// WeatherByCoord decodes the current weather given the city coordinates and the units.
func (c *Client) WeatherByCoord(lat, lon float64, units string) (w Weather, err error) {
	w, err = c.weather(c.baseURL +
		"weather" +
		"?lat=" + strconv.FormatFloat(lat, 'f', 2, 64) +
		"&lon=" + strconv.FormatFloat(lon, 'f', 2, 64) +
		"&units=" + units)
	return
}

// WeatherByZone decodes a slice of current weathers given the zone coordinates, map zoom and the units.
func (c *Client) WeatherByZone(lat1, lon1, lat2, lon2 float64, zoom int, units string) (w []Weather, err error) {
	ws, err := c.weatherSet(c.baseURL +
		"box/city" +
		"?bbox=" +
		strconv.FormatFloat(lat1, 'f', 2, 64) + "," +
		strconv.FormatFloat(lon1, 'f', 2, 64) + "," +
		strconv.FormatFloat(lat2, 'f', 2, 64) + "," +
		strconv.FormatFloat(lon2, 'f', 2, 64) + "," +
		strconv.Itoa(zoom) +
		"&units=" + units)
	w = ws.Weather
	return
}

// WeatherByRadius decodes a slice of current weathers given the center coordinates, radius and the units.
func (c *Client) WeatherByRadius(lat, lon, radius float64, units string) (w []Weather, err error) {
	ws, err := c.weatherSet(c.baseURL +
		"find" +
		"?lat=" + strconv.FormatFloat(lat, 'f', 2, 64) + "," +
		"&lon=" + strconv.FormatFloat(lon, 'f', 2, 64) + "," +
		"&cnt=" + strconv.FormatFloat(radius, 'f', 2, 64) +
		"&units=" + units)
	w = ws.Weather
	return
}

// WeatherByIds decodes a slice of current weathers given a slice of city ids and the units.
func (c *Client) WeatherByIds(id []int, units string) (w []Weather, err error) {
	var ids string
	for i := range id {
		ids += strconv.Itoa(id[i]) + ","
	}
	ws, err := c.weatherSet(c.baseURL +
		"group" +
		"?id=" + ids[:len(ids)-1] +
		"&units=" + units)
	w = ws.Weather
	return
}

// ForecastByName decodes the current forecast given the city name and the units.
func (c *Client) ForecastByName(name string, units string) (f Forecast, err error) {
	f, err = c.forecast(c.baseURL +
		"forecast" +
		"?q=" + name +
		"&units=" + units)
	return
}

// ForecastById decodes the current forecast given the city id and the units.
func (c *Client) ForecastById(id int, units string) (f Forecast, err error) {
	f, err = c.forecast(c.baseURL +
		"forecast" +
		"?id=" + strconv.Itoa(id) +
		"&units=" + units)
	return
}

// ForecastByCoord decodes the current forecast given the city coordinates and the units.
func (c *Client) ForecastByCoord(lat, lon float64, units string) (f Forecast, err error) {
	f, err = c.forecast(c.baseURL +
		"forecast" +
		"?lat=" + strconv.FormatFloat(lat, 'f', 2, 64) +
		"&lon=" + strconv.FormatFloat(lon, 'f', 2, 64) +
		"&units=" + units)
	return
}

// DailyForecastByName decodes the daily forecast given the city name, number of days  and units.
func (c *Client) DailyForecastByName(name string, days int, units string) (f Forecast, err error) {
	f, err = c.forecast(c.baseURL +
		"forecast/daily" +
		"?q=" + name +
		"&cnt=" + strconv.Itoa(days) +
		"&units=" + units)
	return
}

// DailyForecastById decodes the daily forecast given the city id, number of days and units.
func (c *Client) DailyForecastById(id, days int, units string) (f Forecast, err error) {
	f, err = c.forecast(c.baseURL +
		"forecast/daily" +
		"?id=" + strconv.Itoa(id) +
		"&cnt=" + strconv.Itoa(days) +
		"&units=" + units)
	return
}

// DailyForecastByCoord decodes the daily forecast given the city coordinates, number of days and units.
func (c *Client) DailyForecastByCoord(lat, lon float64, days int, units string) (f Forecast, err error) {
	f, err = c.forecast(c.baseURL +
		"forecast/daily" +
		"?lat=" + strconv.FormatFloat(lat, 'f', 2, 64) +
		"&lon=" + strconv.FormatFloat(lon, 'f', 2, 64) +
		"&cnt=" + strconv.Itoa(days) +
		"&units=" + units)
	return
}
<commit_msg>fixed comments<commit_after>// Copyright 2014 Vasco Costa. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package owm implements a wrapper for the OpenWeatherMap API.
//
// Read more about the OpenWeatherMap API here: http://openweathermap.org/api.
package owm

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
)

type weatherLine struct {
	Id          int
	Main        string
	Description string
	Icon        string
}

// Weather represents the current weather at a specific location.
//
// It is returned by the WeatherBy* methods of Client.
type Weather struct {
	Coord struct {
		Lat float64
		Lon float64
	}
	Sys struct {
		Type    int
		Id      int
		Country string
		Sunrise int
		Sunset  int
	}
	Weather []weatherLine
	Base    string
	Main    struct {
		Temp      float64
		Humidity  int
		TempMin   float64 `json:"temp_min"`
		TempMax   float64 `json:"temp_max"`
		Pressure  float64
		SeaLevel  float64 `json:"sea_level"`
		GrndLevel float64 `json:"grnd_level"`
	}
	Wind struct {
		Speed float64
		Deg   float64
		Gust  float64
	}
	Clouds struct {
		All int
	}
	Dt   int
	Id   int
	Name string
	Cod  int
}

type weatherSet struct {
	Cnt     int
	Weather []Weather `json:"list"`
}

// Forecast represents the weather forecast for a specific location.
//
// It is returned by the ForecastBy* methods of Client.
type Forecast struct {
	Cod  string
	City struct {
		Id    int
		Name  string
		Coord struct {
			Lon float64
			Lat float64
		}
		Country string
	}
	Cnt     int
	Weather []Weather `json:"list"`
}

// Client represents an OpenWeatherMap API client.
type Client struct {
	key     string
	baseURL string
}

// NewClient returns a new Client given an API key.
//
// Pass an empty string as the key argument to use the client without an API key.
func NewClient(key string) *Client {
	return &Client{key, "http://api.openweathermap.org/data/2.5/"}
}

func (c *Client) data(url string) (data []byte, err error) {
	res, err := http.Get(url)
	if err != nil {
		err = errors.New("owm: error while fetching weather data")
		return
	}
	data, err = ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		err = errors.New("owm: error while fetching weather data")
		return
	}
	return
}

func (c *Client) weather(url string) (w Weather, err error) {
	if c.key != "" {
		url += "&APPID=" + c.key
	}
	data, err := c.data(url)
	if err != nil || strings.Contains(string(data), `"cod":"404"`) {
		err = errors.New("owm: error while fetching weather data")
		return
	}
	err = json.Unmarshal(data, &w)
	if err != nil {
		err = errors.New("owm: error while decoding weather data")
		return
	}
	return
}

func (c *Client) weatherSet(url string) (ws weatherSet, err error) {
	if c.key != "" {
		url += "&APPID=" + c.key
	}
	data, err := c.data(url)
	if err != nil || strings.Contains(string(data), `"cod":"404"`) {
		err = errors.New("owm: error while fetching weather data")
		return
	}
	err = json.Unmarshal(data, &ws)
	if err != nil {
		err = errors.New("owm: error while decoding weather data")
		return
	}
	return
}

func (c *Client) forecast(url string) (f Forecast, err error) {
	if c.key != "" {
		url += "&APPID=" + c.key
	}
	data, err := c.data(url)
	if err != nil || strings.Contains(string(data), `"cod":"404"`) {
		err = errors.New("owm: error while fetching forecast data")
		return
	}
	err = json.Unmarshal(data, &f)
	if err != nil {
		err = errors.New("owm: error while decoding forecast data")
		return
	}
	return
}

// WeatherByName decodes the current weather of a location given the city name
// and units. It uses the corresponding web API URL to fetch JSON encoded data
// and returns a Weather with as much fields decoded as those available.
//
// An error is returned if there is a problem while fetching weather data from
// the web API or decoding the weather data.
func (c *Client) WeatherByName(name string, units string) (w Weather, err error) {
	w, err = c.weather(c.baseURL +
		"weather" +
		"?q=" + name +
		"&units=" + units)
	return
}

// WeatherById decodes the current weather of a location given the city id and
// units. It uses the corresponding web API URL to fetch JSON encoded data and
// returns a Weather with as much fields decoded as those available.
//
// An error is returned if there is a problem while fetching weather data from
// the web API or decoding the weather data.
func (c *Client) WeatherById(id int, units string) (w Weather, err error) {
	w, err = c.weather(c.baseURL +
		"weather" +
		"?id=" + strconv.Itoa(id) +
		"&units=" + units)
	return
}

// WeatherByCoord decodes the current weather of a location given the city
// coordinates and units. It uses the corresponding web API URL to fetch JSON
// encoded data and returns a Weather with as much fields decoded as those
// available.
//
// An error is returned if there is a problem while fetching weather data from
// the web API or decoding the weather data.
func (c *Client) WeatherByCoord(lat, lon float64, units string) (w Weather, err error) {
	w, err = c.weather(c.baseURL +
		"weather" +
		"?lat=" + strconv.FormatFloat(lat, 'f', 2, 64) +
		"&lon=" + strconv.FormatFloat(lon, 'f', 2, 64) +
		"&units=" + units)
	return
}

// WeatherByZone decodes the current weather of multiple locations given the
// zone coordinates, map zoom and units. It uses the corresponding web API URL
// to fetch JSON encoded data and returns a []Weather with as much fields
// decoded as those available.
//
// An error is returned if there is a problem while fetching weather data from
// the web API or decoding the weather data.
func (c *Client) WeatherByZone(lat1, lon1, lat2, lon2 float64, zoom int, units string) (w []Weather, err error) {
	ws, err := c.weatherSet(c.baseURL +
		"box/city" +
		"?bbox=" +
		strconv.FormatFloat(lat1, 'f', 2, 64) + "," +
		strconv.FormatFloat(lon1, 'f', 2, 64) + "," +
		strconv.FormatFloat(lat2, 'f', 2, 64) + "," +
		strconv.FormatFloat(lon2, 'f', 2, 64) + "," +
		strconv.Itoa(zoom) +
		"&units=" + units)
	w = ws.Weather
	return
}

// WeatherByRadius decodes the current weather of multiple locations given the
// center coordinates, radius and units. It uses the corresponding web API URL
// to fetch JSON encoded data and returns a []Weather with as much fields
// decoded as those available.
//
// An error is returned if there is a problem while fetching weather data from
// the web API or decoding the weather data.
func (c *Client) WeatherByRadius(lat, lon, radius float64, units string) (w []Weather, err error) {
	ws, err := c.weatherSet(c.baseURL +
		"find" +
		"?lat=" + strconv.FormatFloat(lat, 'f', 2, 64) + "," +
		"&lon=" + strconv.FormatFloat(lon, 'f', 2, 64) + "," +
		"&cnt=" + strconv.FormatFloat(radius, 'f', 2, 64) +
		"&units=" + units)
	w = ws.Weather
	return
}

// WeatherByIds decodes the current weather of multiple locations given the a
// slice of city ids and units. It uses the corresponding web API URL to fetch
// JSON encoded data and returns a []Weather with as much fields decoded fields
// as those available.
//
// An error is returned if there is a problem while fetching weather data from
// the web API or decoding the weather data.
func (c *Client) WeatherByIds(id []int, units string) (w []Weather, err error) {
	var ids string
	for i := range id {
		ids += strconv.Itoa(id[i]) + ","
	}
	ws, err := c.weatherSet(c.baseURL +
		"group" +
		"?id=" + ids[:len(ids)-1] +
		"&units=" + units)
	w = ws.Weather
	return
}

// ForecastByName decodes the current forecast given the city name and the units.
func (c *Client) ForecastByName(name string, units string) (f Forecast, err error) {
	f, err = c.forecast(c.baseURL +
		"forecast" +
		"?q=" + name +
		"&units=" + units)
	return
}

// ForecastById decodes the current forecast given the city id and the units.
func (c *Client) ForecastById(id int, units string) (f Forecast, err error) {
	f, err = c.forecast(c.baseURL +
		"forecast" +
		"?id=" + strconv.Itoa(id) +
		"&units=" + units)
	return
}

// ForecastByCoord decodes the current forecast given the city coordinates and the units.
func (c *Client) ForecastByCoord(lat, lon float64, units string) (f Forecast, err error) {
	f, err = c.forecast(c.baseURL +
		"forecast" +
		"?lat=" + strconv.FormatFloat(lat, 'f', 2, 64) +
		"&lon=" + strconv.FormatFloat(lon, 'f', 2, 64) +
		"&units=" + units)
	return
}

// DailyForecastByName decodes the daily forecast given the city name, number of days  and units.
func (c *Client) DailyForecastByName(name string, days int, units string) (f Forecast, err error) {
	f, err = c.forecast(c.baseURL +
		"forecast/daily" +
		"?q=" + name +
		"&cnt=" + strconv.Itoa(days) +
		"&units=" + units)
	return
}

// DailyForecastById decodes the daily forecast given the city id, number of days and units.
func (c *Client) DailyForecastById(id, days int, units string) (f Forecast, err error) {
	f, err = c.forecast(c.baseURL +
		"forecast/daily" +
		"?id=" + strconv.Itoa(id) +
		"&cnt=" + strconv.Itoa(days) +
		"&units=" + units)
	return
}

// DailyForecastByCoord decodes the daily forecast given the city coordinates, number of days and units.
func (c *Client) DailyForecastByCoord(lat, lon float64, days int, units string) (f Forecast, err error) {
	f, err = c.forecast(c.baseURL +
		"forecast/daily" +
		"?lat=" + strconv.FormatFloat(lat, 'f', 2, 64) +
		"&lon=" + strconv.FormatFloat(lon, 'f', 2, 64) +
		"&cnt=" + strconv.Itoa(days) +
		"&units=" + units)
	return
}
<|endoftext|>