<commit_before>package gcexporter

import (
	"code.google.com/p/go.tools/go/exact"
	"code.google.com/p/go.tools/go/types"
	"fmt"
	"io"
	"strconv"
	"strings"
)

type exporter struct {
	pkg      *types.Package
	imports  map[*types.Package]bool
	toExport []types.Object
	out      io.Writer
}

func Write(pkg *types.Package, out io.Writer, sizes types.Sizes) {
	fmt.Fprintf(out, "package %s\n", pkg.Name())

	e := &exporter{pkg: pkg, imports: make(map[*types.Package]bool), out: out}

	for _, imp := range pkg.Imports() {
		e.addImport(imp)
	}

	for _, name := range pkg.Scope().Names() {
		obj := pkg.Scope().Lookup(name)

		_, isTypeName := obj.(*types.TypeName)
		if obj.IsExported() || isTypeName {
			e.toExport = append(e.toExport, obj)
		}
	}

	for i := 0; i < len(e.toExport); i++ {
		switch o := e.toExport[i].(type) {
		case *types.TypeName:
			fmt.Fprintf(out, "type %s %s\n", e.makeName(o), e.makeType(o.Type().Underlying()))
			if _, isInterface := o.Type().Underlying().(*types.Interface); !isInterface {
				writeMethods := func(methods *types.MethodSet) {
					for i := 0; i < methods.Len(); i++ {
						m := methods.At(i)
						if len(m.Index()) > 1 {
							continue // method of embedded field
						}
						out.Write([]byte("func (? " + e.makeType(m.Recv()) + ") " + e.makeName(m.Obj()) + e.makeSignature(m.Type()) + "\n"))
					}
				}
				writeMethods(o.Type().MethodSet())
				writeMethods(types.NewPointer(o.Type()).MethodSet())
			}
		case *types.Func:
			out.Write([]byte("func " + e.makeName(o) + e.makeSignature(o.Type()) + "\n"))
		case *types.Const:
			optType := ""
			basic, isBasic := o.Type().(*types.Basic)
			if !isBasic || basic.Info()&types.IsUntyped == 0 {
				optType = " " + e.makeType(o.Type())
			}

			basic = o.Type().Underlying().(*types.Basic)
			var val string
			switch {
			case basic.Info()&types.IsBoolean != 0:
				val = strconv.FormatBool(exact.BoolVal(o.Val()))
			case basic.Info()&types.IsInteger != 0:
				if basic.Kind() == types.Uint64 {
					d, _ := exact.Uint64Val(o.Val())
					val = strconv.FormatUint(d, 10)
					break
				}
				d, _ := exact.Int64Val(o.Val())
				val = strconv.FormatInt(d, 10)
			case basic.Info()&types.IsFloat != 0:
				f, _ := exact.Float64Val(o.Val())
				val = strconv.FormatFloat(f, 'b', -1, 64)
			case basic.Info()&types.IsComplex != 0:
				r, _ := exact.Float64Val(exact.Real(o.Val()))
				i, _ := exact.Float64Val(exact.Imag(o.Val()))
				val = fmt.Sprintf("(%s+%si)", strconv.FormatFloat(r, 'b', -1, 64), strconv.FormatFloat(i, 'b', -1, 64))
			case basic.Info()&types.IsString != 0:
				val = fmt.Sprintf("%#v", exact.StringVal(o.Val()))
			default:
				panic("Unhandled constant type: " + basic.String())
			}
			out.Write([]byte("const " + e.makeName(o) + optType + " = " + val + "\n"))
		case *types.Var:
			out.Write([]byte("var " + e.makeName(o) + " " + e.makeType(o.Type()) + "\n"))
		default:
			panic(fmt.Sprintf("Unhandled object: %T\n", o))
		}
	}

	fmt.Fprintf(out, "$$\n")
}

func (e *exporter) addImport(pkg *types.Package) {
	if _, found := e.imports[pkg]; found {
		return
	}
	fmt.Fprintf(e.out, "import %s \"%s\"\n", pkg.Name(), pkg.Path())
	e.imports[pkg] = true
}

func (e *exporter) makeName(o types.Object) string {
	if o.Name() == "" || o.Name() == "_" {
		return "?"
	}
	if o.Pkg() == nil || o.Pkg() == e.pkg {
		return `@"".` + o.Name()
	}
	e.addImport(o.Pkg())
	return `@"` + o.Pkg().Path() + `".` + o.Name()
}

func (e *exporter) makeType(ty types.Type) string {
	switch t := ty.(type) {
	case *types.Basic:
		if t.Kind() == types.UnsafePointer {
			return `@"unsafe".Pointer`
		}
		return t.Name()
	case *types.Array:
		return "[" + strconv.FormatInt(t.Len(), 10) + "]" + e.makeType(t.Elem())
	case *types.Slice:
		return "[]" + e.makeType(t.Elem())
	case *types.Map:
		return "map[" + e.makeType(t.Key()) + "]" + e.makeType(t.Elem())
	case *types.Pointer:
		return "*" + e.makeType(t.Elem())
	case *types.Struct:
		fields := make([]string, t.NumFields())
		for i := range fields {
			field := t.Field(i)
			name := "?"
			if !field.Anonymous() {
				name = e.makeName(field)
			}
			tag := ""
			if t.Tag(i) != "" {
				tag = fmt.Sprintf(" %#v", t.Tag(i))
			}
			fields[i] = name + " " + e.makeType(field.Type()) + tag
		}
		return "struct { " + strings.Join(fields, "; ") + " }"
	case *types.Interface:
		methods := make([]string, t.NumMethods())
		for i := range methods {
			m := t.Method(i)
			methods[i] = e.makeName(m) + e.makeSignature(m.Type())
		}
		return "interface { " + strings.Join(methods, "; ") + " }"
	case *types.Signature:
		return "func " + e.makeSignature(t)
	case *types.Chan:
		switch t.Dir() {
		case types.SendRecv:
			return "chan " + e.makeType(t.Elem())
		case types.SendOnly:
			return "chan<- " + e.makeType(t.Elem())
		case types.RecvOnly:
			return "<-chan " + e.makeType(t.Elem())
		default:
			panic("invalid channel direction")
		}
	case *types.Named:
		if t.Obj().Pkg() == nil {
			return t.Obj().Name()
		}
		found := false
		for _, o := range e.toExport {
			if o == t.Obj() {
				found = true
				break
			}
		}
		if !found {
			e.toExport = append(e.toExport, t.Obj())
		}
		return e.makeName(t.Obj())
	default:
		panic(fmt.Sprintf("Unhandled type: %T\n", t))
	}
}

func (e *exporter) makeSignature(t types.Type) string {
	sig := t.(*types.Signature)
	return "(" + e.makeParameters(sig.Params(), sig.IsVariadic()) + ") (" + e.makeParameters(sig.Results(), false) + ")"
}

func (e *exporter) makeParameters(tuple *types.Tuple, isVariadic bool) string {
	params := make([]string, tuple.Len())
	for i := range params {
		param := tuple.At(i)
		paramType := param.Type()
		dots := ""
		if isVariadic && i == tuple.Len()-1 {
			dots = "..."
			paramType = paramType.(*types.Slice).Elem()
		}
		params[i] = e.makeName(param) + " " + dots + e.makeType(paramType)
	}
	return strings.Join(params, ", ")
}
<commit_msg>fixed gcexporter for rune constants<commit_after>package gcexporter

import (
	"code.google.com/p/go.tools/go/exact"
	"code.google.com/p/go.tools/go/types"
	"fmt"
	"io"
	"strconv"
	"strings"
	"unicode"
)

type exporter struct {
	pkg      *types.Package
	imports  map[*types.Package]bool
	toExport []types.Object
	out      io.Writer
}

func Write(pkg *types.Package, out io.Writer, sizes types.Sizes) {
	fmt.Fprintf(out, "package %s\n", pkg.Name())

	e := &exporter{pkg: pkg, imports: make(map[*types.Package]bool), out: out}

	for _, imp := range pkg.Imports() {
		e.addImport(imp)
	}

	for _, name := range pkg.Scope().Names() {
		obj := pkg.Scope().Lookup(name)

		_, isTypeName := obj.(*types.TypeName)
		if obj.IsExported() || isTypeName {
			e.toExport = append(e.toExport, obj)
		}
	}

	for i := 0; i < len(e.toExport); i++ {
		switch o := e.toExport[i].(type) {
		case *types.TypeName:
			fmt.Fprintf(out, "type %s %s\n", e.makeName(o), e.makeType(o.Type().Underlying()))
			if _, isInterface := o.Type().Underlying().(*types.Interface); !isInterface {
				writeMethods := func(methods *types.MethodSet) {
					for i := 0; i < methods.Len(); i++ {
						m := methods.At(i)
						if len(m.Index()) > 1 {
							continue // method of embedded field
						}
						out.Write([]byte("func (? " + e.makeType(m.Recv()) + ") " + e.makeName(m.Obj()) + e.makeSignature(m.Type()) + "\n"))
					}
				}
				writeMethods(o.Type().MethodSet())
				writeMethods(types.NewPointer(o.Type()).MethodSet())
			}
		case *types.Func:
			out.Write([]byte("func " + e.makeName(o) + e.makeSignature(o.Type()) + "\n"))
		case *types.Const:
			optType := ""
			basic, isBasic := o.Type().(*types.Basic)
			if !isBasic || basic.Info()&types.IsUntyped == 0 {
				optType = " " + e.makeType(o.Type())
			}

			basic = o.Type().Underlying().(*types.Basic)
			var val string
			switch {
			case basic.Info()&types.IsBoolean != 0:
				val = strconv.FormatBool(exact.BoolVal(o.Val()))
			case basic.Info()&types.IsInteger != 0:
				if basic.Kind() == types.Uint64 {
					d, _ := exact.Uint64Val(o.Val())
					val = fmt.Sprintf("%#x", d)
					break
				}
				d, _ := exact.Int64Val(o.Val())
				if basic.Kind() == types.UntypedRune {
					if d < 0 || d > unicode.MaxRune {
						val = fmt.Sprintf("('\\x00' + %d)", d)
						break
					}
					val = fmt.Sprintf("%q", rune(d))
					break
				}
				val = fmt.Sprintf("%#x", d)
			case basic.Info()&types.IsFloat != 0:
				f, _ := exact.Float64Val(o.Val())
				val = strconv.FormatFloat(f, 'b', -1, 64)
			case basic.Info()&types.IsComplex != 0:
				r, _ := exact.Float64Val(exact.Real(o.Val()))
				i, _ := exact.Float64Val(exact.Imag(o.Val()))
				val = fmt.Sprintf("(%s+%si)", strconv.FormatFloat(r, 'b', -1, 64), strconv.FormatFloat(i, 'b', -1, 64))
			case basic.Info()&types.IsString != 0:
				val = fmt.Sprintf("%#v", exact.StringVal(o.Val()))
			default:
				panic("Unhandled constant type: " + basic.String())
			}
			out.Write([]byte("const " + e.makeName(o) + optType + " = " + val + "\n"))
		case *types.Var:
			out.Write([]byte("var " + e.makeName(o) + " " + e.makeType(o.Type()) + "\n"))
		default:
			panic(fmt.Sprintf("Unhandled object: %T\n", o))
		}
	}

	fmt.Fprintf(out, "$$\n")
}

func (e *exporter) addImport(pkg *types.Package) {
	if _, found := e.imports[pkg]; found {
		return
	}
	fmt.Fprintf(e.out, "import %s \"%s\"\n", pkg.Name(), pkg.Path())
	e.imports[pkg] = true
}

func (e *exporter) makeName(o types.Object) string {
	if o.Name() == "" || o.Name() == "_" {
		return "?"
	}
	if o.Pkg() == nil || o.Pkg() == e.pkg {
		return `@"".` + o.Name()
	}
	e.addImport(o.Pkg())
	return `@"` + o.Pkg().Path() + `".` + o.Name()
}

func (e *exporter) makeType(ty types.Type) string {
	switch t := ty.(type) {
	case *types.Basic:
		if t.Kind() == types.UnsafePointer {
			return `@"unsafe".Pointer`
		}
		return t.Name()
	case *types.Array:
		return "[" + strconv.FormatInt(t.Len(), 10) + "]" + e.makeType(t.Elem())
	case *types.Slice:
		return "[]" + e.makeType(t.Elem())
	case *types.Map:
		return "map[" + e.makeType(t.Key()) + "]" + e.makeType(t.Elem())
	case *types.Pointer:
		return "*" + e.makeType(t.Elem())
	case *types.Struct:
		fields := make([]string, t.NumFields())
		for i := range fields {
			field := t.Field(i)
			name := "?"
			if !field.Anonymous() {
				name = e.makeName(field)
			}
			tag := ""
			if t.Tag(i) != "" {
				tag = fmt.Sprintf(" %#v", t.Tag(i))
			}
			fields[i] = name + " " + e.makeType(field.Type()) + tag
		}
		return "struct { " + strings.Join(fields, "; ") + " }"
	case *types.Interface:
		methods := make([]string, t.NumMethods())
		for i := range methods {
			m := t.Method(i)
			methods[i] = e.makeName(m) + e.makeSignature(m.Type())
		}
		return "interface { " + strings.Join(methods, "; ") + " }"
	case *types.Signature:
		return "func " + e.makeSignature(t)
	case *types.Chan:
		switch t.Dir() {
		case types.SendRecv:
			return "chan " + e.makeType(t.Elem())
		case types.SendOnly:
			return "chan<- " + e.makeType(t.Elem())
		case types.RecvOnly:
			return "<-chan " + e.makeType(t.Elem())
		default:
			panic("invalid channel direction")
		}
	case *types.Named:
		if t.Obj().Pkg() == nil {
			return t.Obj().Name()
		}
		found := false
		for _, o := range e.toExport {
			if o == t.Obj() {
				found = true
				break
			}
		}
		if !found {
			e.toExport = append(e.toExport, t.Obj())
		}
		return e.makeName(t.Obj())
	default:
		panic(fmt.Sprintf("Unhandled type: %T\n", t))
	}
}

func (e *exporter) makeSignature(t types.Type) string {
	sig := t.(*types.Signature)
	return "(" + e.makeParameters(sig.Params(), sig.IsVariadic()) + ") (" + e.makeParameters(sig.Results(), false) + ")"
}

func (e *exporter) makeParameters(tuple *types.Tuple, isVariadic bool) string {
	params := make([]string, tuple.Len())
	for i := range params {
		param := tuple.At(i)
		paramType := param.Type()
		dots := ""
		if isVariadic && i == tuple.Len()-1 {
			dots = "..."
			paramType = paramType.(*types.Slice).Elem()
		}
		params[i] = e.makeName(param) + " " + dots + e.makeType(paramType)
	}
	return strings.Join(params, ", ")
}
<|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 gcsproxy

import (
	"fmt"
	"io/ioutil"
	"math"
	"os"

	"github.com/jacobsa/gcloud/gcs"
	"golang.org/x/net/context"
	"google.golang.org/cloud/storage"
)

// A view on a particular generation of an object in GCS that allows random
// access reads and writes.
//
// Reads may involve reading from a local cache. Writes are buffered locally
// until the Sync method is called, at which time a new generation of the
// object is created.
//
// This type is not safe for concurrent access. The user must provide external
// synchronization.
type ObjectProxy struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	bucket gcs.Bucket

	/////////////////////////
	// Constant data
	/////////////////////////

	// The name of the GCS object for which we are a proxy. Might not currently
	// exist in the bucket.
	name string

	/////////////////////////
	// Mutable state
	/////////////////////////

	// The specific generation of the object from which our local state is
	// branched. If we have no local state, the contents of this object are
	// exactly our contents. May be zero if our source is a "doesn't exist"
	// generation.
	srcGeneration uint64

	// The size of the object from which our local state is branched. If
	// srcGeneration is non-zero, this is the size of that generation in GCS.
	//
	// INVARIANT: If srcGeneration == 0, srcSize == 0
	srcSize uint64

	// A local temporary file containing our current contents. When non-nil, this
	// is the authority on our contents. When nil, our contents are defined by
	// the generation identified by srcGeneration.
	localFile *os.File

	// false if localFile is present but its contents may be different from the
	// contents of our source generation. Sync needs to do work iff this is true.
	//
	// INVARIANT: If srcGeneration == 0, then dirty
	// INVARIANT: If dirty, then localFile != nil
	dirty bool
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

// Create a view on the given GCS object generation which is assumed to have
// the given size, or zero if branching from a non-existent object (in which
// case the initial contents are empty).
//
// REQUIRES: If srcGeneration == 0, then srcSize == 0
func NewObjectProxy(
	ctx context.Context,
	bucket gcs.Bucket,
	name string,
	srcGeneration uint64,
	srcSize uint64) (op *ObjectProxy, err error) {
	// Set up the basic struct.
	op = &ObjectProxy{
		bucket:        bucket,
		name:          name,
		srcGeneration: srcGeneration,
		srcSize:       srcSize,
	}

	// For "doesn't exist" source generations, we must establish an empty local
	// file and mark the proxy dirty.
	if srcGeneration == 0 {
		if err = op.ensureLocalFile(ctx); err != nil {
			return
		}

		op.dirty = true
	}

	return
}

// Return the name of the proxied object. This may or may not be an object that
// currently exists in the bucket.
func (op *ObjectProxy) Name() string {
	return op.name
}

// Panic if any internal invariants are violated. Careful users can call this
// at appropriate times to help debug weirdness. Consider using
// syncutil.InvariantMutex to automate the process.
func (op *ObjectProxy) CheckInvariants() {
	// INVARIANT: If srcGeneration == 0, srcSize == 0
	if op.srcGeneration == 0 && op.srcSize != 0 {
		panic("Expected zero source size.")
	}

	// INVARIANT: If srcGeneration == 0, then dirty
	if op.srcGeneration == 0 && !op.dirty {
		panic("Expected dirty.")
	}

	// INVARIANT: If dirty, then localFile != nil
	if op.dirty && op.localFile == nil {
		panic("Expected non-nil localFile.")
	}
}

// Destroy any local file caches, putting the proxy into an indeterminate
// state. Should be used before dropping the final reference to the proxy.
func (op *ObjectProxy) Destroy() (err error) {
	// Make sure that when we exit no invariants are violated.
	defer func() {
		op.srcGeneration = 1
		op.localFile = nil
		op.dirty = false
	}()

	// If we have no local file, there's nothing to do.
	if op.localFile == nil {
		return
	}

	// Close the local file.
	if err = op.localFile.Close(); err != nil {
		err = fmt.Errorf("Close: %v", err)
		return
	}

	return
}

// Return the current size in bytes of the content and an indication of whether
// the proxied object has changed out from under us (in which case Sync will
// fail).
func (op *ObjectProxy) Stat(
	ctx context.Context) (size uint64, clobbered bool, err error) {
	panic("TODO")
}

// Make a random access read into our view of the content. May block for
// network access.
//
// Guarantees that err != nil if n < len(buf)
func (op *ObjectProxy) ReadAt(
	ctx context.Context,
	buf []byte,
	offset int64) (n int, err error) {
	// Make sure we have a local file.
	if err = op.ensureLocalFile(ctx); err != nil {
		err = fmt.Errorf("ensureLocalFile: %v", err)
		return
	}

	// Serve the read from the file.
	n, err = op.localFile.ReadAt(buf, offset)

	return
}

// Make a random access write into our view of the content. May block for
// network access. Not guaranteed to be reflected remotely until after Sync is
// called successfully.
//
// Guarantees that err != nil if n < len(buf)
func (op *ObjectProxy) WriteAt(
	ctx context.Context,
	buf []byte,
	offset int64) (n int, err error) {
	// Make sure we have a local file.
	if err = op.ensureLocalFile(ctx); err != nil {
		err = fmt.Errorf("ensureLocalFile: %v", err)
		return
	}

	// TODO(jacobsa): Make sure the dirty flag modification below is tested by
	// removing it and looking for a failure.
	op.dirty = true
	n, err = op.localFile.WriteAt(buf, offset)

	return
}

// Truncate our view of the content to the given number of bytes, extending if
// n is greater than the current size. May block for network access. Not
// guaranteed to be reflected remotely until after Sync is called successfully.
func (op *ObjectProxy) Truncate(ctx context.Context, n uint64) (err error) {
	// Make sure we have a local file.
	if err = op.ensureLocalFile(ctx); err != nil {
		err = fmt.Errorf("ensureLocalFile: %v", err)
		return
	}

	// Convert to signed, which is what os.File wants.
	if n > math.MaxInt64 {
		err = fmt.Errorf("Illegal offset: %v", n)
		return
	}

	// TODO(jacobsa): Make sure the dirty flag modification below is tested by
	// removing it and looking for a failure.
	op.dirty = true
	err = op.localFile.Truncate(int64(n))

	return
}

// If the proxy is dirty due to having been written to or due to having a nil
// source, save its current contents to GCS and return a generation number for
// a generation with exactly those contents. Do so with a precondition such
// that the creation will fail if the source generation is not current. In that
// case, return an error of type *gcs.PreconditionError.
func (op *ObjectProxy) Sync(ctx context.Context) (gen uint64, err error) {
	// Do we need to do anything?
	if !op.dirty {
		gen = op.srcGeneration
		return
	}

	// Seek the file to the start so that it can be used as a reader for its full
	// contents below.
	_, err = op.localFile.Seek(0, 0)
	if err != nil {
		err = fmt.Errorf("Seek: %v", err)
		return
	}

	// Write a new generation of the object with the appropriate contents, using
	// an appropriate precondition.
	signedSrcGeneration := int64(op.srcGeneration)
	req := &gcs.CreateObjectRequest{
		Attrs: storage.ObjectAttrs{
			Name: op.name,
		},
		Contents:               op.localFile,
		GenerationPrecondition: &signedSrcGeneration,
	}

	o, err := op.bucket.CreateObject(ctx, req)

	// Special case: handle precondition errors.
	if _, ok := err.(*gcs.PreconditionError); ok {
		err = &gcs.PreconditionError{
			Err: fmt.Errorf("CreateObject: %v", err),
		}

		return
	}

	// Propagate other errors more directly.
	if err != nil {
		err = fmt.Errorf("CreateObject: %v", err)
		return
	}

	// Make sure the server didn't return a silly generation number.
	//
	// TODO(jacobsa): Push unsigned generation numbers and a guarantee on zero
	// into package gcs, including checking results from the server, and remove
	// this.
	if o.Generation <= 0 {
		err = fmt.Errorf(
			"CreateObject returned invalid generation number: %v",
			o.Generation)

		return
	}

	gen = uint64(o.Generation)

	// Update our state.
	op.srcGeneration = gen
	op.dirty = false

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// Set up an unlinked local temporary file for the given generation of the
// given object. Special case: generation == 0 means an empty file.
func makeLocalFile(
	ctx context.Context,
	bucket gcs.Bucket,
	name string,
	generation uint64) (f *os.File, err error) {
	// Create the file.
	f, err = ioutil.TempFile("", "object_proxy")
	if err != nil {
		err = fmt.Errorf("TempFile: %v", err)
		return
	}

	// Unlink the file so that its inode will be garbage collected when the file
	// is closed.
	if err = os.Remove(f.Name()); err != nil {
		f.Close()
		err = fmt.Errorf("Remove: %v", err)
		return
	}

	// Fetch the object's contents if necessary.
	if generation != 0 {
		panic("TODO")
	}

	return
}

// Ensure that op.localFile is non-nil with an authoritative view of op's
// contents.
func (op *ObjectProxy) ensureLocalFile(ctx context.Context) (err error) {
	// Is there anything to do?
	if op.localFile != nil {
		return
	}

	// Set up the file.
	f, err := makeLocalFile(ctx, op.bucket, op.name, op.srcGeneration)
	if err != nil {
		err = fmt.Errorf("makeLocalFile: %v", err)
		return
	}

	op.localFile = f
	return
}
<commit_msg>Implemented ObjectProxy.Stat.<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 gcsproxy

import (
	"fmt"
	"io/ioutil"
	"math"
	"os"

	"github.com/jacobsa/gcloud/gcs"
	"golang.org/x/net/context"
	"google.golang.org/cloud/storage"
)

// A view on a particular generation of an object in GCS that allows random
// access reads and writes.
//
// Reads may involve reading from a local cache. Writes are buffered locally
// until the Sync method is called, at which time a new generation of the
// object is created.
//
// This type is not safe for concurrent access. The user must provide external
// synchronization.
type ObjectProxy struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	bucket gcs.Bucket

	/////////////////////////
	// Constant data
	/////////////////////////

	// The name of the GCS object for which we are a proxy. Might not currently
	// exist in the bucket.
	name string

	/////////////////////////
	// Mutable state
	/////////////////////////

	// The specific generation of the object from which our local state is
	// branched. If we have no local state, the contents of this object are
	// exactly our contents. May be zero if our source is a "doesn't exist"
	// generation.
	srcGeneration uint64

	// The size of the object from which our local state is branched. If
	// srcGeneration is non-zero, this is the size of that generation in GCS.
	//
	// INVARIANT: If srcGeneration == 0, srcSize == 0
	srcSize uint64

	// A local temporary file containing our current contents. When non-nil, this
	// is the authority on our contents. When nil, our contents are defined by
	// the generation identified by srcGeneration.
	localFile *os.File

	// false if localFile is present but its contents may be different from the
	// contents of our source generation. Sync needs to do work iff this is true.
	//
	// INVARIANT: If srcGeneration == 0, then dirty
	// INVARIANT: If dirty, then localFile != nil
	dirty bool
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

// Create a view on the given GCS object generation which is assumed to have
// the given size, or zero if branching from a non-existent object (in which
// case the initial contents are empty).
//
// REQUIRES: If srcGeneration == 0, then srcSize == 0
func NewObjectProxy(
	ctx context.Context,
	bucket gcs.Bucket,
	name string,
	srcGeneration uint64,
	srcSize uint64) (op *ObjectProxy, err error) {
	// Set up the basic struct.
	op = &ObjectProxy{
		bucket:        bucket,
		name:          name,
		srcGeneration: srcGeneration,
		srcSize:       srcSize,
	}

	// For "doesn't exist" source generations, we must establish an empty local
	// file and mark the proxy dirty.
	if srcGeneration == 0 {
		if err = op.ensureLocalFile(ctx); err != nil {
			return
		}

		op.dirty = true
	}

	return
}

// Return the name of the proxied object. This may or may not be an object that
// currently exists in the bucket.
func (op *ObjectProxy) Name() string {
	return op.name
}

// Panic if any internal invariants are violated. Careful users can call this
// at appropriate times to help debug weirdness. Consider using
// syncutil.InvariantMutex to automate the process.
func (op *ObjectProxy) CheckInvariants() {
	// INVARIANT: If srcGeneration == 0, srcSize == 0
	if op.srcGeneration == 0 && op.srcSize != 0 {
		panic("Expected zero source size.")
	}

	// INVARIANT: If srcGeneration == 0, then dirty
	if op.srcGeneration == 0 && !op.dirty {
		panic("Expected dirty.")
	}

	// INVARIANT: If dirty, then localFile != nil
	if op.dirty && op.localFile == nil {
		panic("Expected non-nil localFile.")
	}
}

// Destroy any local file caches, putting the proxy into an indeterminate
// state. Should be used before dropping the final reference to the proxy.
func (op *ObjectProxy) Destroy() (err error) {
	// Make sure that when we exit no invariants are violated.
	defer func() {
		op.srcGeneration = 1
		op.localFile = nil
		op.dirty = false
	}()

	// If we have no local file, there's nothing to do.
	if op.localFile == nil {
		return
	}

	// Close the local file.
	if err = op.localFile.Close(); err != nil {
		err = fmt.Errorf("Close: %v", err)
		return
	}

	return
}

// Return the current size in bytes of the content and an indication of whether
// the proxied object has changed out from under us (in which case Sync will
// fail).
func (op *ObjectProxy) Stat(
	ctx context.Context) (size uint64, clobbered bool, err error) {
	// Stat the object in GCS.
	req := &gcs.StatObjectRequest{Name: op.name}
	o, bucketErr := op.bucket.StatObject(ctx, req)

	// Propagate errors. Special case: suppress ErrNotFound, treating it as a
	// zero generation below.
	if bucketErr != nil && bucketErr != gcs.ErrNotFound {
		err = fmt.Errorf("StatObject: %v", bucketErr)
		return
	}

	// Find the generation number, or zero if not found.
	var currentGen uint64
	if bucketErr == nil {
		currentGen = o.Generation
	}

	// We are clobbered iff the generation doesn't match our source generation.
	clobbered = (currentGen != op.srcGeneration)

	// If we have a file, it is authoritative for our size. Otherwise our source
	// size is authoritative.
	if op.localFile != nil {
		var fi os.FileInfo
		if fi, err = op.localFile.Stat(); err != nil {
			err = fmt.Errorf("Stat: %v", err)
			return
		}

		size = uint64(fi.Size())
	} else {
		size = op.srcSize
	}

	return
}

// Make a random access read into our view of the content. May block for
// network access.
//
// Guarantees that err != nil if n < len(buf)
func (op *ObjectProxy) ReadAt(
	ctx context.Context,
	buf []byte,
	offset int64) (n int, err error) {
	// Make sure we have a local file.
	if err = op.ensureLocalFile(ctx); err != nil {
		err = fmt.Errorf("ensureLocalFile: %v", err)
		return
	}

	// Serve the read from the file.
	n, err = op.localFile.ReadAt(buf, offset)

	return
}

// Make a random access write into our view of the content. May block for
// network access. Not guaranteed to be reflected remotely until after Sync is
// called successfully.
//
// Guarantees that err != nil if n < len(buf)
func (op *ObjectProxy) WriteAt(
	ctx context.Context,
	buf []byte,
	offset int64) (n int, err error) {
	// Make sure we have a local file.
	if err = op.ensureLocalFile(ctx); err != nil {
		err = fmt.Errorf("ensureLocalFile: %v", err)
		return
	}

	// TODO(jacobsa): Make sure the dirty flag modification below is tested by
	// removing it and looking for a failure.
	op.dirty = true
	n, err = op.localFile.WriteAt(buf, offset)

	return
}

// Truncate our view of the content to the given number of bytes, extending if
// n is greater than the current size. May block for network access. Not
// guaranteed to be reflected remotely until after Sync is called successfully.
func (op *ObjectProxy) Truncate(ctx context.Context, n uint64) (err error) {
	// Make sure we have a local file.
	if err = op.ensureLocalFile(ctx); err != nil {
		err = fmt.Errorf("ensureLocalFile: %v", err)
		return
	}

	// Convert to signed, which is what os.File wants.
	if n > math.MaxInt64 {
		err = fmt.Errorf("Illegal offset: %v", n)
		return
	}

	// TODO(jacobsa): Make sure the dirty flag modification below is tested by
	// removing it and looking for a failure.
	op.dirty = true
	err = op.localFile.Truncate(int64(n))

	return
}

// If the proxy is dirty due to having been written to or due to having a nil
// source, save its current contents to GCS and return a generation number for
// a generation with exactly those contents. Do so with a precondition such
// that the creation will fail if the source generation is not current. In that
// case, return an error of type *gcs.PreconditionError.
func (op *ObjectProxy) Sync(ctx context.Context) (gen uint64, err error) {
	// Do we need to do anything?
	if !op.dirty {
		gen = op.srcGeneration
		return
	}

	// Seek the file to the start so that it can be used as a reader for its full
	// contents below.
	_, err = op.localFile.Seek(0, 0)
	if err != nil {
		err = fmt.Errorf("Seek: %v", err)
		return
	}

	// Write a new generation of the object with the appropriate contents, using
	// an appropriate precondition.
	signedSrcGeneration := int64(op.srcGeneration)
	req := &gcs.CreateObjectRequest{
		Attrs: storage.ObjectAttrs{
			Name: op.name,
		},
		Contents:               op.localFile,
		GenerationPrecondition: &signedSrcGeneration,
	}

	o, err := op.bucket.CreateObject(ctx, req)

	// Special case: handle precondition errors.
	if _, ok := err.(*gcs.PreconditionError); ok {
		err = &gcs.PreconditionError{
			Err: fmt.Errorf("CreateObject: %v", err),
		}

		return
	}

	// Propagate other errors more directly.
	if err != nil {
		err = fmt.Errorf("CreateObject: %v", err)
		return
	}

	// Make sure the server didn't return a silly generation number.
	//
	// TODO(jacobsa): Push unsigned generation numbers and a guarantee on zero
	// into package gcs, including checking results from the server, and remove
	// this.
	if o.Generation <= 0 {
		err = fmt.Errorf(
			"CreateObject returned invalid generation number: %v",
			o.Generation)

		return
	}

	gen = uint64(o.Generation)

	// Update our state.
	op.srcGeneration = gen
	op.dirty = false

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// Set up an unlinked local temporary file for the given generation of the
// given object. Special case: generation == 0 means an empty file.
func makeLocalFile(
	ctx context.Context,
	bucket gcs.Bucket,
	name string,
	generation uint64) (f *os.File, err error) {
	// Create the file.
	f, err = ioutil.TempFile("", "object_proxy")
	if err != nil {
		err = fmt.Errorf("TempFile: %v", err)
		return
	}

	// Unlink the file so that its inode will be garbage collected when the file
	// is closed.
	if err = os.Remove(f.Name()); err != nil {
		f.Close()
		err = fmt.Errorf("Remove: %v", err)
		return
	}

	// Fetch the object's contents if necessary.
	if generation != 0 {
		panic("TODO")
	}

	return
}

// Ensure that op.localFile is non-nil with an authoritative view of op's
// contents.
func (op *ObjectProxy) ensureLocalFile(ctx context.Context) (err error) {
	// Is there anything to do?
	if op.localFile != nil {
		return
	}

	// Set up the file.
	f, err := makeLocalFile(ctx, op.bucket, op.name, op.srcGeneration)
	if err != nil {
		err = fmt.Errorf("makeLocalFile: %v", err)
		return
	}

	op.localFile = f
	return
}
<|endoftext|>


<commit_before>// The version package provides a location to set the release versions for all
// packages to consume, without creating import cycles.
//
// This package should not import any other terraform packages.
package version

import (
	"fmt"

	version "github.com/hashicorp/go-version"
)

// The main version number that is being run at the moment.
var Version = "0.15.0"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
var Prerelease = "dev"

// SemVer is an instance of version.Version. This has the secondary
// benefit of verifying during tests and init time that our version is a
// proper semantic version, which should always be the case.
var SemVer *version.Version

func init() {
	SemVer = version.Must(version.NewVersion(Version))
}

// Header is the header name used to send the current terraform version
// in http requests.
const Header = "Terraform-Version"

// String returns the complete version string, including prerelease
func String() string {
	if Prerelease != "" {
		return fmt.Sprintf("%s-%s", Version, Prerelease)
	}
	return Version
}
<commit_msg>Release v0.15.0-alpha20210210<commit_after>// The version package provides a location to set the release versions for all
// packages to consume, without creating import cycles.
//
// This package should not import any other terraform packages.
package version

import (
	"fmt"

	version "github.com/hashicorp/go-version"
)

// The main version number that is being run at the moment.
var Version = "0.15.0"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
var Prerelease = "alpha20210210"

// SemVer is an instance of version.Version. This has the secondary
// benefit of verifying during tests and init time that our version is a
// proper semantic version, which should always be the case.
var SemVer *version.Version

func init() {
	SemVer = version.Must(version.NewVersion(Version))
}

// Header is the header name used to send the current terraform version
// in http requests.
const Header = "Terraform-Version"

// String returns the complete version string, including prerelease
func String() string {
	if Prerelease != "" {
		return fmt.Sprintf("%s-%s", Version, Prerelease)
	}
	return Version
}
<|endoftext|>


<commit_before>package version

import (
	"fmt"

	"github.com/github/hub/git"
)

var Version = "2.8.4"

func FullVersion() (string, error) {
	gitVersion, err := git.Version()
	if err != nil {
		gitVersion = "git version (unavailable)"
	}
	return fmt.Sprintf("%s\nhub version %s", gitVersion, Version), err
}
<commit_msg>hub 2.9.0<commit_after>package version

import (
	"fmt"

	"github.com/github/hub/git"
)

var Version = "2.9.0"

func FullVersion() (string, error) {
	gitVersion, err := git.Version()
	if err != nil {
		gitVersion = "git version (unavailable)"
	}
	return fmt.Sprintf("%s\nhub version %s", gitVersion, Version), err
}
<|endoftext|>


<commit_before>package version

import (
	"github.com/coreos/fleet/third_party/github.com/coreos/go-semver/semver"
)

const Version = "0.3.2+git"

var SemVersion semver.Version

func init() {
	sv, err := semver.NewVersion(Version)
	if err != nil {
		panic("bad version string!")
	}
	SemVersion = *sv
}
<commit_msg>chore(release): Bump version to v0.4.0+git<commit_after>package version

import (
	"github.com/coreos/fleet/third_party/github.com/coreos/go-semver/semver"
)

const Version = "0.4.0+git"

var SemVersion semver.Version

func init() {
	sv, err := semver.NewVersion(Version)
	if err != nil {
		panic("bad version string!")
	}
	SemVersion = *sv
}
<|endoftext|>


<commit_before>package blobstore

import (
	"fmt"
	"gnd.la/blobstore/driver"
	"gnd.la/config"
	"io"
	"reflect"
)

var (
	imports = map[string]string{
		"file":   "gnd.la/blobstore/driver/file",
		"gridfs": "gnd.la/blobstore/driver/gridfs",
		"s3":     "gnd.la/blobstore/driver/s3",
	}
)

type Store struct {
	drv driver.Driver
}

func New(url *config.URL) (*Store, error) {
	if url == nil {
		return nil, fmt.Errorf("blobstore is not configured")
	}
	opener := driver.Get(url.Scheme)
	if opener == nil {
		if imp := imports[url.Scheme]; imp != "" {
			return nil, fmt.Errorf("please import %q to use the blobstore driver %q", imp, url.Scheme)
		}
		return nil, fmt.Errorf("unknown blobstore driver %q. Perhaps you forgot an import?", url.Scheme)
	}
	drv, err := opener(url.Value, url.Options)
	if err != nil {
		return nil, fmt.Errorf("error opening blobstore driver %q: %s", url.Scheme, err)
	}
	return &Store{
		drv: drv,
	}, nil
}

func (s *Store) Create(meta interface{}) (*WFile, error) {
	return s.CreateId(newId(), meta)
}

func (s *Store) CreateId(id string, meta interface{}) (wfile *WFile, err error) {
	var w driver.WFile
	w, err = s.drv.Create(id)
	if err != nil {
		panic(err)
		return
	}
	defer func() {
		if err != nil {
			w.Close()
			s.drv.Remove(id)
		}
	}()
	// Write version number
	if err = bwrite(w, uint8(1)); err != nil {
		return
	}
	// Write flags
	if err = bwrite(w, uint64(0)); err != nil {
		return
	}
	metadataLength := uint64(0)
	if meta != nil && !isNil(meta) {
		var d []byte
		d, err = marshal(meta)
		if err != nil {
			return
		}
		metadataLength = uint64(len(d))
		if err = bwrite(w, metadataLength); err != nil {
			return
		}
		h := newHash()
		h.Write(d)
		if err = bwrite(w, h.Sum64()); err != nil {
			return
		}
		if _, err = w.Write(d); err != nil {
			return
		}
	} else {
		// No metadata. Write 0 for the length and the hash
		if err = bwrite(w, uint64(0)); err != nil {
			return
		}
		if err = bwrite(w, uint64(0)); err != nil {
			return
		}
	}
	seeker, ok := w.(io.Seeker)
	if ok {
		// Reserve 16 bytes for data header
		if err = bwrite(w, uint64(0)); err != nil {
			return
		}
		if err = bwrite(w, uint64(0)); err != nil {
			return
		}
	}
	// File is ready for writing. Hand it to the user.
	return &WFile{
		id:             id,
		metadataLength: metadataLength,
		dataHash:       newHash(),
		wfile:          w,
		seeker:         seeker,
	}, nil
}

func (s *Store) Open(id string) (rfile *RFile, err error) {
	var r driver.RFile
	r, err = s.drv.Open(id)
	if err != nil {
		return
	}
	defer func() {
		if err != nil {
			rfile = nil
			r.Close()
		}
	}()
	var version uint8
	if err = bread(r, &version); err != nil {
		return
	}
	if version != 1 {
		err = fmt.Errorf("can't read files with version %d", version)
		return
	}
	// Skip over the flags for now
	var flags uint64
	if err = bread(r, &flags); err != nil {
		return
	}
	rfile = &RFile{
		id:    id,
		rfile: r,
	}
	var metadataLength uint64
	if err = bread(r, &metadataLength); err != nil {
		return
	}
	if err = bread(r, &rfile.metadataHash); err != nil {
		return
	}
	if metadataLength > 0 {
		rfile.metadataData = make([]byte, int(metadataLength))
		if _, err = r.Read(rfile.metadataData); err != nil {
			return
		}
	}
	if err = bread(r, &rfile.dataLength); err != nil {
		return
	}
	if err = bread(r, &rfile.dataHash); err != nil {
		return
	}
	return
}

func (s *Store) Store(b []byte, meta interface{}) (string, error) {
	return s.StoreId(newId(), b, meta)
}

func (s *Store) StoreId(id string, b []byte, meta interface{}) (string, error) {
	f, err := s.CreateId(id, meta)
	if err != nil {
		return "", err
	}
	if _, err := f.Write(b); err != nil {
		return "", err
	}
	if err := f.Close(); err != nil {
		return "", err
	}
	return f.Id(), nil
}

func (s *Store) Remove(id string) error {
	return s.drv.Remove(id)
}

func (s *Store) Close() error {
	return s.drv.Close()
}

func isNil(v interface{}) bool {
	val := reflect.ValueOf(v)
	if val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface {
		return val.IsNil()
	}
	return false
}
<commit_msg>Add ReadAll() method<commit_after>package blobstore

import (
	"fmt"
	"gnd.la/blobstore/driver"
	"gnd.la/config"
	"io"
	"reflect"
)

var (
	imports = map[string]string{
		"file":   "gnd.la/blobstore/driver/file",
		"gridfs": "gnd.la/blobstore/driver/gridfs",
		"s3":     "gnd.la/blobstore/driver/s3",
	}
)

type Store struct {
	drv driver.Driver
}

func New(url *config.URL) (*Store, error) {
	if url == nil {
		return nil, fmt.Errorf("blobstore is not configured")
	}
	opener := driver.Get(url.Scheme)
	if opener == nil {
		if imp := imports[url.Scheme]; imp != "" {
			return nil, fmt.Errorf("please import %q to use the blobstore driver %q", imp, url.Scheme)
		}
		return nil, fmt.Errorf("unknown blobstore driver %q. Perhaps you forgot an import?", url.Scheme)
	}
	drv, err := opener(url.Value, url.Options)
	if err != nil {
		return nil, fmt.Errorf("error opening blobstore driver %q: %s", url.Scheme, err)
	}
	return &Store{
		drv: drv,
	}, nil
}

func (s *Store) Create(meta interface{}) (*WFile, error) {
	return s.CreateId(newId(), meta)
}

func (s *Store) CreateId(id string, meta interface{}) (wfile *WFile, err error) {
	var w driver.WFile
	w, err = s.drv.Create(id)
	if err != nil {
		panic(err)
		return
	}
	defer func() {
		if err != nil {
			w.Close()
			s.drv.Remove(id)
		}
	}()
	// Write version number
	if err = bwrite(w, uint8(1)); err != nil {
		return
	}
	// Write flags
	if err = bwrite(w, uint64(0)); err != nil {
		return
	}
	metadataLength := uint64(0)
	if meta != nil && !isNil(meta) {
		var d []byte
		d, err = marshal(meta)
		if err != nil {
			return
		}
		metadataLength = uint64(len(d))
		if err = bwrite(w, metadataLength); err != nil {
			return
		}
		h := newHash()
		h.Write(d)
		if err = bwrite(w, h.Sum64()); err != nil {
			return
		}
		if _, err = w.Write(d); err != nil {
			return
		}
	} else {
		// No metadata. Write 0 for the length and the hash
		if err = bwrite(w, uint64(0)); err != nil {
			return
		}
		if err = bwrite(w, uint64(0)); err != nil {
			return
		}
	}
	seeker, ok := w.(io.Seeker)
	if ok {
		// Reserve 16 bytes for data header
		if err = bwrite(w, uint64(0)); err != nil {
			return
		}
		if err = bwrite(w, uint64(0)); err != nil {
			return
		}
	}
	// File is ready for writing. Hand it to the user.
	return &WFile{
		id:             id,
		metadataLength: metadataLength,
		dataHash:       newHash(),
		wfile:          w,
		seeker:         seeker,
	}, nil
}

func (s *Store) Open(id string) (rfile *RFile, err error) {
	var r driver.RFile
	r, err = s.drv.Open(id)
	if err != nil {
		return
	}
	defer func() {
		if err != nil {
			rfile = nil
			r.Close()
		}
	}()
	var version uint8
	if err = bread(r, &version); err != nil {
		return
	}
	if version != 1 {
		err = fmt.Errorf("can't read files with version %d", version)
		return
	}
	// Skip over the flags for now
	var flags uint64
	if err = bread(r, &flags); err != nil {
		return
	}
	rfile = &RFile{
		id:    id,
		rfile: r,
	}
	var metadataLength uint64
	if err = bread(r, &metadataLength); err != nil {
		return
	}
	if err = bread(r, &rfile.metadataHash); err != nil {
		return
	}
	if metadataLength > 0 {
		rfile.metadataData = make([]byte, int(metadataLength))
		if _, err = r.Read(rfile.metadataData); err != nil {
			return
		}
	}
	if err = bread(r, &rfile.dataLength); err != nil {
		return
	}
	if err = bread(r, &rfile.dataHash); err != nil {
		return
	}
	return
}

// ReadAll is a shorthand for Open(f).ReadAll()
func (s *Store) ReadAll(id string) (data []byte, err error) {
	f, err := s.Open(id)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return f.ReadAll()
}

func (s *Store) Store(b []byte, meta interface{}) (string, error) {
	return s.StoreId(newId(), b, meta)
}

func (s *Store) StoreId(id string, b []byte, meta interface{}) (string, error) {
	f, err := s.CreateId(id, meta)
	if err != nil {
		return "", err
	}
	if _, err := f.Write(b); err != nil {
		return "", err
	}
	if err := f.Close(); err != nil {
		return "", err
	}
	return f.Id(), nil
}

func (s *Store) Remove(id string) error {
	return s.drv.Remove(id)
}

func (s *Store) Close() error {
	return s.drv.Close()
}

func isNil(v interface{}) bool {
	val := reflect.ValueOf(v)
	if val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface {
		return val.IsNil()
	}
	return false
}
<|endoftext|>


<commit_before>package booklitcmd

import (
	"fmt"
	"os"

	flags "github.com/jessevdk/go-flags"
	"github.com/vito/booklit"
)

func Main() {
	cmd := &Command{}
	cmd.Version = func() {
		fmt.Println(booklit.Version)
		os.Exit(0)
	}

	parser := flags.NewParser(cmd, flags.Default)
	parser.NamespaceDelimiter = "-"

	args, err := parser.Parse()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	err = cmd.Execute(args)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<commit_msg>fix double-error output, --help exit status<commit_after>package booklitcmd

import (
	"fmt"
	"os"

	flags "github.com/jessevdk/go-flags"
	"github.com/vito/booklit"
)

func Main() {
	cmd := &Command{}
	cmd.Version = func() {
		fmt.Println(booklit.Version)
		os.Exit(0)
	}

	parser := flags.NewParser(cmd, flags.Default)
	parser.NamespaceDelimiter = "-"

	args, err := parser.Parse()
	if err != nil {
		if flagsErr, ok := err.(*flags.Error); ok && flagsErr.Type == flags.ErrHelp {
			fmt.Println(err)
			os.Exit(0)
		} else {
			os.Exit(1)
		}
	}

	err = cmd.Execute(args)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<|endoftext|>


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

/*  Filename:    table.go
 *  Author:      Bryan Matsuo <bmatsuo@soe.ucsc.edu>
 *  Created:     Thu Dec  8 10:10:58 PST 2011
 *  Description: Main source file in go-table
 */

/*
Package table provides a simple framework for executing table driven tests.
A table is a set (usually a slice) of tests. A table test element is usually a
simple struct that describes a singular test. In the table package, table test
elements implement their own test(s) as a method Test which returns an os.Error.
Non-nil errors returned by a table test's Test method cause errors to be logged
with the "testing" package.

For general information about table driven testing in Go, see

	http://code.google.com/p/go-wiki/wiki/TableDrivenTests
*/
package table

import (
	"testing"
	"reflect"
	"os"
)

func value(src string, index, v reflect.Value, zero reflect.Value) reflect.Value {
	switch {
	case v.IsNil():
		return zero
	case !v.IsValid():
		panic(errorf("invalid value in %s index %v", src, index.Interface()))
	}
	return v
}

// Iterate over a range of values, issuing a callback for each one. The callback
// fn is expected to take two arguments (index/key, value pair) and return an
// os.Error.
func doRange(v reflect.Value, fn interface{}) os.Error {
	fnval := reflect.ValueOf(fn)
	fntyp := fnval.Type()
	if numin := fntyp.NumIn(); numin != 2 {
		panic(errorf("doRange function of %d arguments %v", numin, fn))
	}
	if numout := fntyp.NumOut(); numout != 2 {
		panic(errorf("doRange function of %d return values %v", numout, fn))
	}
	zero := reflect.Zero(fnval.Type().In(1))
	var out reflect.Value
	switch k := v.Kind(); k {
	case reflect.Slice:
		for i, n := 0, v.Len(); i < n; i++ {
			ival, vval := reflect.ValueOf(i), v.Index(i)
			arg := value("slice", ival, vval, zero)
			out = fnval.Call([]reflect.Value{ival, arg})[0]
			if !out.IsNil() {
				return out.Interface().(os.Error)
			}

		}
	case reflect.Map:
		for _, kval := range v.MapKeys() {
			vval := v.MapIndex(kval)
			arg := value("map", kval, vval, zero)
			out = fnval.Call([]reflect.Value{kval, arg})[0]
			if !out.IsNil() {
				return out.Interface().(os.Error)
			}
		}
	case reflect.Chan:
		var vval reflect.Value
		var ok bool
		for i := 0; true; i++ {
			ival := reflect.ValueOf(i)
			if vval, ok = v.Recv(); !ok {
				break
			}
			arg := value("chan", ival, vval, zero)
			out = fnval.Call([]reflect.Value{ival, arg})[0]
			if !out.IsNil() {
				return out.Interface().(os.Error)
			}
		}
	default:
		panic(errorf("unacceptable type for range %v", v.Type()))
	}
	return nil
}

// Test each value in a slice table.
func testSlice(t *testing.T, v reflect.Value) (err os.Error) {
	err = doRange(v, func(i int, elem interface{}) (err os.Error) {
		var e T
		prefix := sprintf("%v %d", reflect.TypeOf(elem), i)
		if e, err = mustT(t, prefix, elem); err != nil {
			if err == ErrSkip {
				err = nil
				if Verbose {
					t.Logf("%s skipped", prefix, i)
				}
			} else {
				t.Fatal(err)
			}
			return
		}
		if !error(t, prefix, tTest(e)) && Verbose {
			t.Logf("%s passed", prefix, i)
		}
		return
	})
	return
}

// Detect a value's reflect.Kind. Return the reflect.Value as well for good measure.
func kind(x interface{}) (reflect.Value, reflect.Kind) { v := reflect.ValueOf(x); return v, v.Kind() }

// A table driven test. The table must be a slice of values all implementing T.
// But, not all elements need be of the same type. And furthermore, the slice's
// element type does not need to satisfy T. For example, a slice v of type
// []interface{} can be a valid table if all its elements satisfy T.
//
// A feasible future enhancement would be to allow map tables. Possibly chan
// tables.
func Test(t *testing.T, table interface{}) {
	prefix := "table.Test" // name for internal errors.

	// A table must be a slice type.
	val, k := kind(table)
	switch k {
	case reflect.Invalid:
		fatal(t, prefix, errorf("table is invalid"))
	case reflect.Slice: // Allow chan/map?
		break
	default:
		fatal(t, prefix, errorf("table %s is not a slice", val.Type().String()))
	}

	// A table can't be empty.
	if val.Len() == 0 && k != reflect.Chan {
		fatal(t, prefix, errorf("empty table"))
	}

	// Execute table tests.
	switch k {
	case reflect.Slice:
		if testSlice(t, val) != nil {
			return
		}
	default:
		fatal(t, prefix, errorf("unexpected error"))
	}
}
<commit_msg>Add map based tables.<commit_after>// Copyright 2011, Bryan Matsuo. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*  Filename:    table.go
 *  Author:      Bryan Matsuo <bmatsuo@soe.ucsc.edu>
 *  Created:     Thu Dec  8 10:10:58 PST 2011
 *  Description: Main source file in go-table
 */

/*
Package table provides a simple framework for executing table driven tests.
A table is a set (usually a slice) of tests. A table test element is usually a
simple struct that describes a singular test. In the table package, table test
elements implement their own test(s) as a method Test which returns an os.Error.
Non-nil errors returned by a table test's Test method cause errors to be logged
with the "testing" package.

For general information about table driven testing in Go, see

	http://code.google.com/p/go-wiki/wiki/TableDrivenTests
*/
package table

import (
	"testing"
	"reflect"
	"os"
)

func value(src string, index, v reflect.Value, zero reflect.Value) reflect.Value {
	switch {
	case v.Interface() == nil:
		return zero
	case !v.IsValid():
		panic(errorf("invalid value in %s index %v", src, index.Interface()))
	}
	return v
}

// Iterate over a range of values, issuing a callback for each one. The callback
// fn is expected to take two arguments (index/key, value pair) and return an
// os.Error.
func doRange(v reflect.Value, fn interface{}) os.Error {
	fnval := reflect.ValueOf(fn)
	fntyp := fnval.Type()
	if numin := fntyp.NumIn(); numin != 2 {
		panic(errorf("doRange function of %d arguments %v", numin, fn))
	}
	if numout := fntyp.NumOut(); numout != 1 {
		panic(errorf("doRange function of %d return values %v", numout, fn))
	}
	zero := reflect.Zero(fnval.Type().In(1))
	var out reflect.Value
	switch k := v.Kind(); k {
	case reflect.Slice:
		for i, n := 0, v.Len(); i < n; i++ {
			ival, vval := reflect.ValueOf(i), v.Index(i)
			arg := value("slice", ival, vval, zero)
			out = fnval.Call([]reflect.Value{ival, arg})[0]
			if !out.IsNil() {
				return out.Interface().(os.Error)
			}

		}
	case reflect.Map:
		for _, kval := range v.MapKeys() {
			vval := v.MapIndex(kval)
			arg := value("map", kval, vval, zero)
			out = fnval.Call([]reflect.Value{kval, arg})[0]
			if !out.IsNil() {
				return out.Interface().(os.Error)
			}
		}
	case reflect.Chan:
		var vval reflect.Value
		var ok bool
		for i := 0; true; i++ {
			ival := reflect.ValueOf(i)
			if vval, ok = v.Recv(); !ok {
				break
			}
			arg := value("chan", ival, vval, zero)
			out = fnval.Call([]reflect.Value{ival, arg})[0]
			if !out.IsNil() {
				return out.Interface().(os.Error)
			}
		}
	default:
		panic(errorf("unacceptable type for range %v", v.Type()))
	}
	return nil
}

type stringer interface {
	String() string
}

func testCastT(t *testing.T, prefix string, v interface{}) (test T, err os.Error) {
	if test, err = mustT(t, prefix, v); err != nil {
		if err == ErrSkip {
			if Verbose {
				t.Logf("%s skipped", prefix)
			}
			err = nil
		} else {
			t.Fatal(err)
		}
		return
	}
	return
}

func testExecute(t *testing.T, prefix string, test T) {
	if !error(t, prefix, tTest(test)) && Verbose {
		t.Logf("%s passed", prefix)
	}
}

func testMap(t *testing.T, v reflect.Value) os.Error {
	i := new(int)
	return doRange(v, func(k, v interface{}) (err os.Error) {
		var prefix string
		switch k.(type) {
		case string:
			prefix = k.(string)
		case stringer:
			prefix = k.(stringer).String()
		default:
			prefix = sprintf("%v %d", reflect.TypeOf(k).String(), *i)
		}
		var test T
		test, err = testCastT(t, prefix, v)
		testExecute(t, prefix, test)
		(*i)++
		return
	})
}

// Test each value in a slice table.
func testSlice(t *testing.T, v reflect.Value) os.Error {
	return doRange(v, func(i int, elem interface{}) (err os.Error) {
		prefix := sprintf("%v %d", reflect.TypeOf(elem), i)
		var test T
		test, err = testCastT(t, prefix, elem)
		testExecute(t, prefix, test)
		return
	})
}

// Detect a value's reflect.Kind. Return the reflect.Value as well for good measure.
func kind(x interface{}) (reflect.Value, reflect.Kind) { v := reflect.ValueOf(x); return v, v.Kind() }

// A table driven test. The table must be a slice of values all implementing T.
// But, not all elements need be of the same type. And furthermore, the slice's
// element type does not need to satisfy T. For example, a slice v of type
// []interface{} can be a valid table if all its elements satisfy T.
//
// A feasible future enhancement would be to allow map tables. Possibly chan
// tables.
func Test(t *testing.T, table interface{}) {
	prefix := "table.Test" // name for internal errors.

	// A table must be a slice type.
	val, k := kind(table)
	switch k {
	case reflect.Invalid:
		fatal(t, prefix, errorf("table is invalid"))
	case reflect.Slice, reflect.Map: // Allow chan?
		break
	default:
		fatal(t, prefix, errorf("table %s is not a slice", val.Type().String()))
	}

	// A table can't be empty.
	if val.Len() == 0 && k != reflect.Chan {
		fatal(t, prefix, errorf("empty table"))
	}

	// Execute table tests.
	switch k {
	case reflect.Slice:
		if testSlice(t, val) != nil {
			return
		}
	case reflect.Map:
		if testMap(t, val) != nil {
			return
		}
	default:
		fatal(t, prefix, errorf("unexpected error"))
	}
}
<|endoftext|>


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

// Package format implements standard formatting of Go source.
//
// Note that formatting of Go source code changes over time, so tools relying on
// consistent formatting should execute a specific version of the gofmt binary
// instead of using this package. That way, the formatting will be stable, and
// the tools won't need to be recompiled each time gofmt changes.
//
// For example, pre-submit checks that use this package directly would behave
// differently depending on what Go version each developer uses, causing the
// check to be inherently fragile.
package format

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/parser"
	"go/printer"
	"go/token"
	"io"
)

// Keep these in sync with cmd/gofmt/gofmt.go.
const (
	tabWidth    = 8
	printerMode = printer.UseSpaces | printer.TabIndent | printerNormalizeNumbers

	// printerNormalizeNumbers means to canonicalize number literal prefixes
	// and exponents while printing. See https://golang.org/doc/go1.13#gofmt.
	//
	// This value is defined in go/printer specifically for go/format and cmd/gofmt.
	printerNormalizeNumbers = 1 << 30
)

var config = printer.Config{Mode: printerMode, Tabwidth: tabWidth}

const parserMode = parser.ParseComments

// Node formats node in canonical gofmt style and writes the result to dst.
//
// The node type must be *ast.File, *printer.CommentedNode, []ast.Decl,
// []ast.Stmt, or assignment-compatible to ast.Expr, ast.Decl, ast.Spec,
// or ast.Stmt. Node does not modify node. Imports are not sorted for
// nodes representing partial source files (for instance, if the node is
// not an *ast.File or a *printer.CommentedNode not wrapping an *ast.File).
//
// The function may return early (before the entire result is written)
// and return a formatting error, for instance due to an incorrect AST.
func Node(dst io.Writer, fset *token.FileSet, node any) error {
	// Determine if we have a complete source file (file != nil).
	var file *ast.File
	var cnode *printer.CommentedNode
	switch n := node.(type) {
	case *ast.File:
		file = n
	case *printer.CommentedNode:
		if f, ok := n.Node.(*ast.File); ok {
			file = f
			cnode = n
		}
	}

	// Sort imports if necessary.
	if file != nil && hasUnsortedImports(file) {
		// Make a copy of the AST because ast.SortImports is destructive.
		// TODO(gri) Do this more efficiently.
		var buf bytes.Buffer
		err := config.Fprint(&buf, fset, file)
		if err != nil {
			return err
		}
		file, err = parser.ParseFile(fset, "", buf.Bytes(), parserMode)
		if err != nil {
			// We should never get here. If we do, provide good diagnostic.
			return fmt.Errorf("format.Node internal error (%s)", err)
		}
		ast.SortImports(fset, file)

		// Use new file with sorted imports.
		node = file
		if cnode != nil {
			node = &printer.CommentedNode{Node: file, Comments: cnode.Comments}
		}
	}

	return config.Fprint(dst, fset, node)
}

// Source formats src in canonical gofmt style and returns the result
// or an (I/O or syntax) error. src is expected to be a syntactically
// correct Go source file, or a list of Go declarations or statements.
//
// If src is a partial source file, the leading and trailing space of src
// is applied to the result (such that it has the same leading and trailing
// space as src), and the result is indented by the same amount as the first
// line of src containing code. Imports are not sorted for partial source files.
func Source(src []byte) ([]byte, error) {
	fset := token.NewFileSet()
	file, sourceAdj, indentAdj, err := parse(fset, "", src, true)
	if err != nil {
		return nil, err
	}

	if sourceAdj == nil {
		// Complete source file.
		// TODO(gri) consider doing this always.
		ast.SortImports(fset, file)
	}

	return format(fset, file, sourceAdj, indentAdj, src, config)
}

func hasUnsortedImports(file *ast.File) bool {
	for _, d := range file.Decls {
		d, ok := d.(*ast.GenDecl)
		if !ok || d.Tok != token.IMPORT {
			// Not an import declaration, so we're done.
			// Imports are always first.
			return false
		}
		if d.Lparen.IsValid() {
			// For now assume all grouped imports are unsorted.
			// TODO(gri) Should check if they are sorted already.
			return true
		}
		// Ungrouped imports are sorted by default.
	}
	return false
}
<commit_msg>go/format: skip go/ast's object resolution<commit_after>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package format implements standard formatting of Go source.
//
// Note that formatting of Go source code changes over time, so tools relying on
// consistent formatting should execute a specific version of the gofmt binary
// instead of using this package. That way, the formatting will be stable, and
// the tools won't need to be recompiled each time gofmt changes.
//
// For example, pre-submit checks that use this package directly would behave
// differently depending on what Go version each developer uses, causing the
// check to be inherently fragile.
package format

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/parser"
	"go/printer"
	"go/token"
	"io"
)

// Keep these in sync with cmd/gofmt/gofmt.go.
const (
	tabWidth    = 8
	printerMode = printer.UseSpaces | printer.TabIndent | printerNormalizeNumbers

	// printerNormalizeNumbers means to canonicalize number literal prefixes
	// and exponents while printing. See https://golang.org/doc/go1.13#gofmt.
	//
	// This value is defined in go/printer specifically for go/format and cmd/gofmt.
	printerNormalizeNumbers = 1 << 30
)

var config = printer.Config{Mode: printerMode, Tabwidth: tabWidth}

const parserMode = parser.ParseComments | parser.SkipObjectResolution

// Node formats node in canonical gofmt style and writes the result to dst.
//
// The node type must be *ast.File, *printer.CommentedNode, []ast.Decl,
// []ast.Stmt, or assignment-compatible to ast.Expr, ast.Decl, ast.Spec,
// or ast.Stmt. Node does not modify node. Imports are not sorted for
// nodes representing partial source files (for instance, if the node is
// not an *ast.File or a *printer.CommentedNode not wrapping an *ast.File).
//
// The function may return early (before the entire result is written)
// and return a formatting error, for instance due to an incorrect AST.
func Node(dst io.Writer, fset *token.FileSet, node any) error {
	// Determine if we have a complete source file (file != nil).
	var file *ast.File
	var cnode *printer.CommentedNode
	switch n := node.(type) {
	case *ast.File:
		file = n
	case *printer.CommentedNode:
		if f, ok := n.Node.(*ast.File); ok {
			file = f
			cnode = n
		}
	}

	// Sort imports if necessary.
	if file != nil && hasUnsortedImports(file) {
		// Make a copy of the AST because ast.SortImports is destructive.
		// TODO(gri) Do this more efficiently.
		var buf bytes.Buffer
		err := config.Fprint(&buf, fset, file)
		if err != nil {
			return err
		}
		file, err = parser.ParseFile(fset, "", buf.Bytes(), parserMode)
		if err != nil {
			// We should never get here. If we do, provide good diagnostic.
			return fmt.Errorf("format.Node internal error (%s)", err)
		}
		ast.SortImports(fset, file)

		// Use new file with sorted imports.
		node = file
		if cnode != nil {
			node = &printer.CommentedNode{Node: file, Comments: cnode.Comments}
		}
	}

	return config.Fprint(dst, fset, node)
}

// Source formats src in canonical gofmt style and returns the result
// or an (I/O or syntax) error. src is expected to be a syntactically
// correct Go source file, or a list of Go declarations or statements.
//
// If src is a partial source file, the leading and trailing space of src
// is applied to the result (such that it has the same leading and trailing
// space as src), and the result is indented by the same amount as the first
// line of src containing code. Imports are not sorted for partial source files.
func Source(src []byte) ([]byte, error) {
	fset := token.NewFileSet()
	file, sourceAdj, indentAdj, err := parse(fset, "", src, true)
	if err != nil {
		return nil, err
	}

	if sourceAdj == nil {
		// Complete source file.
		// TODO(gri) consider doing this always.
		ast.SortImports(fset, file)
	}

	return format(fset, file, sourceAdj, indentAdj, src, config)
}

func hasUnsortedImports(file *ast.File) bool {
	for _, d := range file.Decls {
		d, ok := d.(*ast.GenDecl)
		if !ok || d.Tok != token.IMPORT {
			// Not an import declaration, so we're done.
			// Imports are always first.
			return false
		}
		if d.Lparen.IsValid() {
			// For now assume all grouped imports are unsorted.
			// TODO(gri) Should check if they are sorted already.
			return true
		}
		// Ungrouped imports are sorted by default.
	}
	return false
}
<|endoftext|>


<commit_before>package goat

import (
	"io"
	"net/http"
)

func ConnHandler() {

}

func announce(res http.ResponseWriter, req *http.Request) {
	io.WriteString(res, "announce successful")
}

// Tracker scrape handling
func scrape(res http.ResponseWriter, req *http.Request) {
	io.WriteString(res, "scrape successful")
}

// Tracker statistics output
func statistics(res http.ResponseWriter, req *http.Request) {
	io.WriteString(res, "statistics")
}
<commit_msg>added interface ConnHandler<commit_after>package goat

import (
	"io"
	"net/http"
)

type ConnHandler interface {
	Listen()
}

func announce(res http.ResponseWriter, req *http.Request) {
	io.WriteString(res, "announce successful")
}

// Tracker scrape handling
func scrape(res http.ResponseWriter, req *http.Request) {
	io.WriteString(res, "scrape successful")
}

// Tracker statistics output
func statistics(res http.ResponseWriter, req *http.Request) {
	io.WriteString(res, "statistics")
}
<|endoftext|>


<commit_before>package ui

import (
	"fmt"
	"image"
	"image/color"
	"os"
	"time"

	"github.com/bugsnag/bugsnag-go"
	"github.com/ninjasphere/gestic-tools/go-gestic-sdk"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/config"
	"github.com/ninjasphere/go-ninja/model"
	owm "github.com/ninjasphere/openweathermap"
	"github.com/ninjasphere/sphere-go-led-controller/fonts/O4b03b"
	"github.com/ninjasphere/sphere-go-led-controller/fonts/clock"
	"github.com/ninjasphere/sphere-go-led-controller/util"
)

var enableWeatherPane = config.MustBool("led.weather.enabled")
var weatherUpdateInterval = config.MustDuration("led.weather.updateInterval")
var temperatureDisplayTime = config.Duration(time.Second*5, "led.weather.temperatureDisplayTime")

var globalSite *model.Site
var timezone *time.Location

type WeatherPane struct {
	siteModel   *ninja.ServiceClient
	site        *model.Site
	getWeather  *time.Timer
	tempTimeout *time.Timer
	temperature bool
	weather     *owm.ForecastWeatherData
	image       util.Image
}

func NewWeatherPane(conn *ninja.Connection) *WeatherPane {

	pane := &WeatherPane{
		siteModel: conn.GetServiceClient("$home/services/SiteModel"),
		image:     util.LoadImage(util.ResolveImagePath("weather/loading.gif")),
	}

	pane.tempTimeout = time.AfterFunc(0, func() {
		pane.temperature = false
	})

	if !enableWeatherPane {
		return pane
	}

	var err error
	pane.weather, err = owm.NewForecast("C")
	if err != nil {
		log.Warningf("Failed to load weather api:", err)
		enableWeatherPane = false
	} else {
		go pane.GetWeather()
	}

	return pane
}

func (p *WeatherPane) GetWeather() {

	enableWeatherPane = false

	for {
		site := &model.Site{}
		err := p.siteModel.Call("fetch", config.MustString("siteId"), site, time.Second*5)

		if err == nil && (site.Longitude != nil || site.Latitude != nil) {
			p.site = site
			globalSite = site

			if site.TimeZoneID != nil {
				if timezone, err = time.LoadLocation(*site.TimeZoneID); err != nil {
					log.Warningf("error while setting timezone (%s): %s", *site.TimeZoneID, err)
					timezone, _ = time.LoadLocation("Local")
				}
			}
			break
		}

		log.Infof("Failed to get site, or site has no location.")

		time.Sleep(time.Second * 2)
	}

	for {

		p.weather.DailyByCoordinates(
			&owm.Coordinates{
				Longitude: *p.site.Longitude,
				Latitude:  *p.site.Latitude,
			},
			1,
		)

		if len(p.weather.List) > 0 {

			filename := util.ResolveImagePath("weather/" + p.weather.List[0].Weather[0].Icon + ".png")

			if _, err := os.Stat(filename); os.IsNotExist(err) {
				enableWeatherPane = false
				fmt.Printf("Couldn't load image for weather: %s", filename)
				bugsnag.Notify(fmt.Errorf("Unknown weather icon: %s", filename), p.weather)
			} else {
				p.image = util.LoadImage(filename)
				enableWeatherPane = true
			}
		}

		time.Sleep(weatherUpdateInterval)

	}

}

func (p *WeatherPane) IsEnabled() bool {
	return enableWeatherPane && p.weather.Unit != ""
}

func (p *WeatherPane) Gesture(gesture *gestic.GestureMessage) {
	if gesture.Tap.Active() {
		log.Infof("Weather tap!")

		p.temperature = true
		p.tempTimeout.Reset(temperatureDisplayTime)
	}
}

func (p *WeatherPane) Render() (*image.RGBA, error) {
	if p.temperature {
		img := image.NewRGBA(image.Rect(0, 0, 16, 16))

		drawText := func(text string, col color.RGBA, top int) {
			width := clock.Font.DrawString(img, 0, 8, text, color.Black)
			start := int(16 - width - 2)

			//spew.Dump("text", text, "width", width, "start", start)

			O4b03b.Font.DrawString(img, start, top, text, col)
		}

		if p.weather.City.Country == "US" || p.weather.City.Country == "United States of America" {
			drawText(fmt.Sprintf("%dF", int(p.weather.List[0].Temp.Max*(9/5)-459.67)), color.RGBA{253, 151, 32, 255}, 1)
			drawText(fmt.Sprintf("%dF", int(p.weather.List[0].Temp.Min*(9/5)-459.67)), color.RGBA{69, 175, 249, 255}, 8)
		} else {
			drawText(fmt.Sprintf("%dC", int(p.weather.List[0].Temp.Max-273.15), color.RGBA{253, 151, 32, 255}, 1)
			drawText(fmt.Sprintf("%dC", int(p.weather.List[0].Temp.Min-273.15), color.RGBA{69, 175, 249, 255}, 8)
		}

		return img, nil
	} else {
		return p.image.GetNextFrame(), nil
	}
}

func (p *WeatherPane) IsDirty() bool {
	return true
}
<commit_msg>No closing bracket<commit_after>package ui

import (
	"fmt"
	"image"
	"image/color"
	"os"
	"time"

	"github.com/bugsnag/bugsnag-go"
	"github.com/ninjasphere/gestic-tools/go-gestic-sdk"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/config"
	"github.com/ninjasphere/go-ninja/model"
	owm "github.com/ninjasphere/openweathermap"
	"github.com/ninjasphere/sphere-go-led-controller/fonts/O4b03b"
	"github.com/ninjasphere/sphere-go-led-controller/fonts/clock"
	"github.com/ninjasphere/sphere-go-led-controller/util"
)

var enableWeatherPane = config.MustBool("led.weather.enabled")
var weatherUpdateInterval = config.MustDuration("led.weather.updateInterval")
var temperatureDisplayTime = config.Duration(time.Second*5, "led.weather.temperatureDisplayTime")

var globalSite *model.Site
var timezone *time.Location

type WeatherPane struct {
	siteModel   *ninja.ServiceClient
	site        *model.Site
	getWeather  *time.Timer
	tempTimeout *time.Timer
	temperature bool
	weather     *owm.ForecastWeatherData
	image       util.Image
}

func NewWeatherPane(conn *ninja.Connection) *WeatherPane {

	pane := &WeatherPane{
		siteModel: conn.GetServiceClient("$home/services/SiteModel"),
		image:     util.LoadImage(util.ResolveImagePath("weather/loading.gif")),
	}

	pane.tempTimeout = time.AfterFunc(0, func() {
		pane.temperature = false
	})

	if !enableWeatherPane {
		return pane
	}

	var err error
	pane.weather, err = owm.NewForecast("C")
	if err != nil {
		log.Warningf("Failed to load weather api:", err)
		enableWeatherPane = false
	} else {
		go pane.GetWeather()
	}

	return pane
}

func (p *WeatherPane) GetWeather() {

	enableWeatherPane = false

	for {
		site := &model.Site{}
		err := p.siteModel.Call("fetch", config.MustString("siteId"), site, time.Second*5)

		if err == nil && (site.Longitude != nil || site.Latitude != nil) {
			p.site = site
			globalSite = site

			if site.TimeZoneID != nil {
				if timezone, err = time.LoadLocation(*site.TimeZoneID); err != nil {
					log.Warningf("error while setting timezone (%s): %s", *site.TimeZoneID, err)
					timezone, _ = time.LoadLocation("Local")
				}
			}
			break
		}

		log.Infof("Failed to get site, or site has no location.")

		time.Sleep(time.Second * 2)
	}

	for {

		p.weather.DailyByCoordinates(
			&owm.Coordinates{
				Longitude: *p.site.Longitude,
				Latitude:  *p.site.Latitude,
			},
			1,
		)

		if len(p.weather.List) > 0 {

			filename := util.ResolveImagePath("weather/" + p.weather.List[0].Weather[0].Icon + ".png")

			if _, err := os.Stat(filename); os.IsNotExist(err) {
				enableWeatherPane = false
				fmt.Printf("Couldn't load image for weather: %s", filename)
				bugsnag.Notify(fmt.Errorf("Unknown weather icon: %s", filename), p.weather)
			} else {
				p.image = util.LoadImage(filename)
				enableWeatherPane = true
			}
		}

		time.Sleep(weatherUpdateInterval)

	}

}

func (p *WeatherPane) IsEnabled() bool {
	return enableWeatherPane && p.weather.Unit != ""
}

func (p *WeatherPane) Gesture(gesture *gestic.GestureMessage) {
	if gesture.Tap.Active() {
		log.Infof("Weather tap!")

		p.temperature = true
		p.tempTimeout.Reset(temperatureDisplayTime)
	}
}

func (p *WeatherPane) Render() (*image.RGBA, error) {
	if p.temperature {
		img := image.NewRGBA(image.Rect(0, 0, 16, 16))

		drawText := func(text string, col color.RGBA, top int) {
			width := clock.Font.DrawString(img, 0, 8, text, color.Black)
			start := int(16 - width - 2)

			//spew.Dump("text", text, "width", width, "start", start)

			O4b03b.Font.DrawString(img, start, top, text, col)
		}

		if p.weather.City.Country == "US" || p.weather.City.Country == "United States of America" {
			drawText(fmt.Sprintf("%dF", int(p.weather.List[0].Temp.Max*(9/5)-459.67)), color.RGBA{253, 151, 32, 255}, 1)
			drawText(fmt.Sprintf("%dF", int(p.weather.List[0].Temp.Min*(9/5)-459.67)), color.RGBA{69, 175, 249, 255}, 8)
		} else {
			drawText(fmt.Sprintf("%dC", int(p.weather.List[0].Temp.Max-273.15)), color.RGBA{253, 151, 32, 255}, 1)
			drawText(fmt.Sprintf("%dC", int(p.weather.List[0].Temp.Min-273.15)), color.RGBA{69, 175, 249, 255}, 8)
		}

		return img, nil
	} else {
		return p.image.GetNextFrame(), nil
	}
}

func (p *WeatherPane) IsDirty() bool {
	return true
}
<|endoftext|>


<commit_before>// 11 february 2014

package ui

import (
	"fmt"
	"runtime"
	"syscall"
	"unsafe"
)

/*
problem: messages have to be dispatched on the same thread as system calls, and we can't mux GetMessage() with select, and PeekMessage() every iteration is wasteful (and leads to lag for me (only) with the concurrent garbage collector sweep)
possible: solution: use PostThreadMessage() to send uimsgs out to the message loop, which runs on its own goroutine
(I had come up with this first but wanted to try other things before doing it (and wasn't really sure if user-defined messages were safe, not quite understanding the system); nsf came up with it independently and explained that this was really the only right way to do it, so thanks to him)

problem: if the thread isn't in its main message pump, the thread message is simply lost (see, for example, http://blogs.msdn.com/b/oldnewthing/archive/2005/04/26/412116.aspx)
this happened when scrolling Areas (as scrolling is modal; see http://blogs.msdn.com/b/oldnewthing/archive/2005/04/27/412565.aspx)

the only recourse, and the one both Microsoft (http://support.microsoft.com/kb/183116) and Raymond Chen (http://blogs.msdn.com/b/oldnewthing/archive/2008/12/23/9248851.aspx) suggest (and Treeki/Ninjifox confirmed), is to create an invisible window to dispatch messages instead.

yay.
*/

var uitask chan *uimsg

type uimsg struct {
	call *syscall.LazyProc
	p    []uintptr
	ret  chan uiret
}

type uiret struct {
	ret uintptr
	err error
}

const (
	msgRequested = _WM_APP + iota + 1 // + 1 just to be safe
	msgQuit
	msgSetAreaSize
	msgRepaintAll
)

var (
	_postMessage = user32.NewProc("PostMessageW")
)

func ui(main func()) error {
	runtime.LockOSThread()

	uitask = make(chan *uimsg)
	err := doWindowsInit()
	if err != nil {
		return fmt.Errorf("error doing general Windows initialization: %v", err)
	}

	hwnd, err := makeMessageHandler()
	if err != nil {
		return fmt.Errorf("error making invisible window for handling events: %v", err)
	}

	go func() {
		for m := range uitask {
			r1, _, err := _postMessage.Call(
				uintptr(hwnd),
				msgRequested,
				uintptr(0),
				uintptr(unsafe.Pointer(m)))
			if r1 == 0 { // failure
				panic("error sending message to message loop to call function: " + err.Error())
			}
		}
	}()

	go func() {
		main()
		r1, _, err := _postMessage.Call(
			uintptr(hwnd),
			msgQuit,
			uintptr(0),
			uintptr(0))
		if r1 == 0 { // failure
			panic("error sending quit message to message loop: " + err.Error())
		}
	}()

	msgloop()
	return nil
}

var (
	_dispatchMessage  = user32.NewProc("DispatchMessageW")
	_getMessage       = user32.NewProc("GetMessageW")
	_postQuitMessage  = user32.NewProc("PostQuitMessage")
	_sendMessage      = user32.NewProc("SendMessageW")
	_translateMessage = user32.NewProc("TranslateMessage")
)

func msgloop() {
	var msg struct {
		hwnd    _HWND
		message uint32
		wParam  _WPARAM
		lParam  _LPARAM
		time    uint32
		pt      _POINT
	}

	for {
		r1, _, err := _getMessage.Call(
			uintptr(unsafe.Pointer(&msg)),
			uintptr(_NULL),
			uintptr(0),
			uintptr(0))
		if r1 == negConst(-1) { // error
			panic("error getting message in message loop: " + err.Error())
		}
		if r1 == 0 { // WM_QUIT message
			return
		}
		_translateMessage.Call(uintptr(unsafe.Pointer(&msg)))
		_dispatchMessage.Call(uintptr(unsafe.Pointer(&msg)))
	}
}

var (
	msghandlerclass = toUTF16("gomsghandler")
	msghandlertitle = toUTF16("ui package message window")
)

func makeMessageHandler() (hwnd _HWND, err error) {
	wc := &_WNDCLASS{
		lpszClassName: utf16ToArg(msghandlerclass),
		lpfnWndProc:   syscall.NewCallback(messageHandlerWndProc),
		hInstance:     hInstance,
		hIcon:         icon,
		hCursor:       cursor,
		hbrBackground: _HBRUSH(_COLOR_BTNFACE + 1),
	}

	r1, _, err := _registerClass.Call(uintptr(unsafe.Pointer(wc)))
	if r1 == 0 { // failure
		return _HWND(_NULL), fmt.Errorf("error registering the class of the invisible window for handling events: %v", err)
	}

	r1, _, err = _createWindowEx.Call(
		uintptr(0),
		utf16ToArg(msghandlerclass),
		utf16ToArg(msghandlertitle),
		uintptr(0),
		negConst(_CW_USEDEFAULT),
		negConst(_CW_USEDEFAULT),
		negConst(_CW_USEDEFAULT),
		negConst(_CW_USEDEFAULT),
		// don't negConst() HWND_MESSAGE; windowsconstgen was given a pointer by windows.h, and pointers are unsigned, so converting it back to signed doesn't work
		uintptr(_HWND_MESSAGE),
		uintptr(_NULL),
		uintptr(hInstance),
		uintptr(_NULL))
	if r1 == 0 { // failure
		return _HWND(_NULL), fmt.Errorf("error actually creating invisible window for handling events: %v", err)
	}

	return _HWND(r1), nil
}

func messageHandlerWndProc(hwnd _HWND, uMsg uint32, wParam _WPARAM, lParam _LPARAM) _LRESULT {
	switch uMsg {
	case msgRequested:
		m := (*uimsg)(unsafe.Pointer(lParam))
		r1, _, err := m.call.Call(m.p...)
		m.ret <- uiret{
			ret: r1,
			err: err,
		}
		return 0
	case msgQuit:
		// does not return a value according to MSDN
		_postQuitMessage.Call(0)
		return 0
	}
	return defWindowProc(hwnd, uMsg, wParam, lParam)
}
<commit_msg>Added the scaffolding that will allow us to change uitask on Windows to take a func(). Right now it accepts both; this will allow me to do piecewise conversion.<commit_after>// 11 february 2014

package ui

import (
	"fmt"
	"runtime"
	"syscall"
	"unsafe"
)

/*
problem: messages have to be dispatched on the same thread as system calls, and we can't mux GetMessage() with select, and PeekMessage() every iteration is wasteful (and leads to lag for me (only) with the concurrent garbage collector sweep)
possible: solution: use PostThreadMessage() to send uimsgs out to the message loop, which runs on its own goroutine
(I had come up with this first but wanted to try other things before doing it (and wasn't really sure if user-defined messages were safe, not quite understanding the system); nsf came up with it independently and explained that this was really the only right way to do it, so thanks to him)

problem: if the thread isn't in its main message pump, the thread message is simply lost (see, for example, http://blogs.msdn.com/b/oldnewthing/archive/2005/04/26/412116.aspx)
this happened when scrolling Areas (as scrolling is modal; see http://blogs.msdn.com/b/oldnewthing/archive/2005/04/27/412565.aspx)

the only recourse, and the one both Microsoft (http://support.microsoft.com/kb/183116) and Raymond Chen (http://blogs.msdn.com/b/oldnewthing/archive/2008/12/23/9248851.aspx) suggest (and Treeki/Ninjifox confirmed), is to create an invisible window to dispatch messages instead.

yay.
*/

var uitask chan interface{}

type uimsg struct {
	call *syscall.LazyProc
	p    []uintptr
	ret  chan uiret
}

type uiret struct {
	ret uintptr
	err error
}

const (
	msgRequested = _WM_APP + iota + 1 // + 1 just to be safe
	msgQuit
	msgSetAreaSize
	msgRepaintAll
)

var (
	_postMessage = user32.NewProc("PostMessageW")
)

func ui(main func()) error {
	runtime.LockOSThread()

	uitask = make(chan interface{})
	err := doWindowsInit()
	if err != nil {
		return fmt.Errorf("error doing general Windows initialization: %v", err)
	}

	hwnd, err := makeMessageHandler()
	if err != nil {
		return fmt.Errorf("error making invisible window for handling events: %v", err)
	}

	go func() {
		for m := range uitask {
			r1, _, err := _postMessage.Call(
				uintptr(hwnd),
				msgRequested,
				uintptr(0),
				uintptr(unsafe.Pointer(&m)))
			if r1 == 0 { // failure
				panic("error sending message to message loop to call function: " + err.Error())
			}
		}
	}()

	go func() {
		main()
		r1, _, err := _postMessage.Call(
			uintptr(hwnd),
			msgQuit,
			uintptr(0),
			uintptr(0))
		if r1 == 0 { // failure
			panic("error sending quit message to message loop: " + err.Error())
		}
	}()

	msgloop()
	return nil
}

var (
	_dispatchMessage  = user32.NewProc("DispatchMessageW")
	_getMessage       = user32.NewProc("GetMessageW")
	_postQuitMessage  = user32.NewProc("PostQuitMessage")
	_sendMessage      = user32.NewProc("SendMessageW")
	_translateMessage = user32.NewProc("TranslateMessage")
)

func msgloop() {
	var msg struct {
		hwnd    _HWND
		message uint32
		wParam  _WPARAM
		lParam  _LPARAM
		time    uint32
		pt      _POINT
	}

	for {
		r1, _, err := _getMessage.Call(
			uintptr(unsafe.Pointer(&msg)),
			uintptr(_NULL),
			uintptr(0),
			uintptr(0))
		if r1 == negConst(-1) { // error
			panic("error getting message in message loop: " + err.Error())
		}
		if r1 == 0 { // WM_QUIT message
			return
		}
		_translateMessage.Call(uintptr(unsafe.Pointer(&msg)))
		_dispatchMessage.Call(uintptr(unsafe.Pointer(&msg)))
	}
}

var (
	msghandlerclass = toUTF16("gomsghandler")
	msghandlertitle = toUTF16("ui package message window")
)

func makeMessageHandler() (hwnd _HWND, err error) {
	wc := &_WNDCLASS{
		lpszClassName: utf16ToArg(msghandlerclass),
		lpfnWndProc:   syscall.NewCallback(messageHandlerWndProc),
		hInstance:     hInstance,
		hIcon:         icon,
		hCursor:       cursor,
		hbrBackground: _HBRUSH(_COLOR_BTNFACE + 1),
	}

	r1, _, err := _registerClass.Call(uintptr(unsafe.Pointer(wc)))
	if r1 == 0 { // failure
		return _HWND(_NULL), fmt.Errorf("error registering the class of the invisible window for handling events: %v", err)
	}

	r1, _, err = _createWindowEx.Call(
		uintptr(0),
		utf16ToArg(msghandlerclass),
		utf16ToArg(msghandlertitle),
		uintptr(0),
		negConst(_CW_USEDEFAULT),
		negConst(_CW_USEDEFAULT),
		negConst(_CW_USEDEFAULT),
		negConst(_CW_USEDEFAULT),
		// don't negConst() HWND_MESSAGE; windowsconstgen was given a pointer by windows.h, and pointers are unsigned, so converting it back to signed doesn't work
		uintptr(_HWND_MESSAGE),
		uintptr(_NULL),
		uintptr(hInstance),
		uintptr(_NULL))
	if r1 == 0 { // failure
		return _HWND(_NULL), fmt.Errorf("error actually creating invisible window for handling events: %v", err)
	}

	return _HWND(r1), nil
}

func messageHandlerWndProc(hwnd _HWND, uMsg uint32, wParam _WPARAM, lParam _LPARAM) _LRESULT {
	switch uMsg {
	case msgRequested:
		mt := (*interface{})(unsafe.Pointer(lParam))
		switch m := (*mt).(type) {
		case *uimsg:
			r1, _, err := m.call.Call(m.p...)
			m.ret <- uiret{
				ret: r1,
				err: err,
			}
		case func():
			m()
		}
		return 0
	case msgQuit:
		// does not return a value according to MSDN
		_postQuitMessage.Call(0)
		return 0
	}
	return defWindowProc(hwnd, uMsg, wParam, lParam)
}
<|endoftext|>


<commit_before>package unit

import "testing"
import "reflect"
import "github.com/coreos/fleet/machine"

const (
	// $ echo -n "foo" | sha1sum
	// 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33 -
	testData      = "foo"
	testShaString = "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
)

func TestUnitHash(t *testing.T) {
	u := NewUnit(testData)
	h := u.Hash()
	if h.String() != testShaString {
		t.Fatalf("Unit Hash (%s) does not match expected (%s)", h.String(), testShaString)
	}

	eh := &Hash{}
	if !eh.Empty() {
		t.Fatalf("Empty hash check failed: %v", eh.Empty())
	}
}

func TestRecognizedUnitTypes(t *testing.T) {
	tts := []struct{
		name string
		ok   bool
	}{
		{"foo.service", true},
		{"foo.socket", true},
		{"foo.path", true},
		{"foo.timer", true},
		{"foo.mount", true},
		{"foo.automount", true},
		{"foo.device", true},
		{"foo.unknown", false},
	}

	for _, tt := range tts {
		ok := RecognizedUnitType(tt.name)
		if ok != tt.ok {
			t.Errorf("Case failed: name=%s expect=%t result=%t", tt.name, tt.ok, ok)
		}
	}
}

func TestDefaultUnitType(t *testing.T) {
	tts := []struct{
		name string
		out  string
	}{
		{"foo", "foo.service"},
		{"foo.service", "foo.service.service"},
		{"foo.link", "foo.link.service"},
	}

	for _, tt := range tts {
		out := DefaultUnitType(tt.name)
		if out != tt.out {
			t.Errorf("Case failed: name=%s expect=%s result=%s", tt.name, tt.out, out)
		}
	}
}

func TestNewUnitState(t *testing.T) {
	ms := &machine.MachineState{"id", "ip", nil, "version"}
	want := &UnitState{
		LoadState:    "ls",
		ActiveState:  "as",
		SubState:     "ss",
		MachineState: ms,
	}

	got := NewUnitState("ls", "as", "ss", ms)
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("NewUnitState did not create a correct UnitState: got %s, want %s", got, want)
	}

}
<commit_msg>test(unit): Assert more units types unsupported<commit_after>package unit

import "testing"
import "reflect"
import "github.com/coreos/fleet/machine"

const (
	// $ echo -n "foo" | sha1sum
	// 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33 -
	testData      = "foo"
	testShaString = "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
)

func TestUnitHash(t *testing.T) {
	u := NewUnit(testData)
	h := u.Hash()
	if h.String() != testShaString {
		t.Fatalf("Unit Hash (%s) does not match expected (%s)", h.String(), testShaString)
	}

	eh := &Hash{}
	if !eh.Empty() {
		t.Fatalf("Empty hash check failed: %v", eh.Empty())
	}
}

func TestRecognizedUnitTypes(t *testing.T) {
	tts := []struct{
		name string
		ok   bool
	}{
		{"foo.service", true},
		{"foo.socket", true},
		{"foo.path", true},
		{"foo.timer", true},
		{"foo.mount", true},
		{"foo.automount", true},
		{"foo.device", true},
		{"foo.swap", false},
		{"foo.target", false},
		{"foo.snapshot", false},
		{"foo.network", false},
		{"foo.netdev", false},
		{"foo.link", false},
		{"foo.unknown", false},
	}

	for _, tt := range tts {
		ok := RecognizedUnitType(tt.name)
		if ok != tt.ok {
			t.Errorf("Case failed: name=%s expect=%t result=%t", tt.name, tt.ok, ok)
		}
	}
}

func TestDefaultUnitType(t *testing.T) {
	tts := []struct{
		name string
		out  string
	}{
		{"foo", "foo.service"},
		{"foo.service", "foo.service.service"},
		{"foo.link", "foo.link.service"},
	}

	for _, tt := range tts {
		out := DefaultUnitType(tt.name)
		if out != tt.out {
			t.Errorf("Case failed: name=%s expect=%s result=%s", tt.name, tt.out, out)
		}
	}
}

func TestNewUnitState(t *testing.T) {
	ms := &machine.MachineState{"id", "ip", nil, "version"}
	want := &UnitState{
		LoadState:    "ls",
		ActiveState:  "as",
		SubState:     "ss",
		MachineState: ms,
	}

	got := NewUnitState("ls", "as", "ss", ms)
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("NewUnitState did not create a correct UnitState: got %s, want %s", got, want)
	}

}
<|endoftext|>


<commit_before>package sdp

import (
	"testing"
)

const MinimalSDP = "v=0\n" +
"o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\n" +
"s=SDP Seminar\n" +
"t=2873397496 2873404696\n"

const CanonicalSDP = "v=0\n" +
"o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\n" +
"s=SDP Seminar\n" +
"i=A Seminar on the session description protocol\n" +
"u=http://www.example.com/seminars/sdp.pdf\n" +
"e=j.doe@example.com (Jane Doe)\n" +
"c=IN IP4 224.2.17.12/127\n" +
"b=X-YZ:128\n" +
"b=AS:12345\n" +
"t=2873397496 2873404696\n" +
"t=3034423619 3042462419\n" +
"r=604800 3600 0 90000\n" +
"z=2882844526 -1h 2898848070 0\n" +
"a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host\n" +
"a=candidate:1 2 UDP 2113667326 203.0.113.1 54401 typ host\n" +
"a=recvonly\n" +
"m=audio 49170 RTP/AVP 0\n" +
"m=video 51372 RTP/AVP 99\n" +
"a=rtpmap:99 h263-1998/90000\n"

func TestUnmarshalMinimal(t *testing.T) {
	sd := &SessionDescription{}
	if err := sd.Unmarshal(MinimalSDP); err != nil {
		t.Errorf("error: %v", err)
	}

	actual := sd.Marshal()
	if actual != MinimalSDP {
		t.Errorf("expected: %v\n actual: %v", MinimalSDP, actual)
	}
}

func TestUnmarshalCanonical(t *testing.T) {
	sd := &SessionDescription{}
	if err := sd.Unmarshal(CanonicalSDP); err != nil {
		t.Errorf("error: %v", err)
	}

	actual := sd.Marshal()
	if actual != CanonicalSDP {
		t.Errorf("error:\n\nEXPECTED:\n%v\nACTUAL:\n%v", CanonicalSDP, actual)
	}
}<commit_msg>Add a few more incremental unittests for improved sdp parsing<commit_after>package sdp

import (
	"testing"
)

const (
	BaseSDP = "v=0\n" +
	"o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\n" +
	"s=SDP Seminar\n"

	SessionInformationSDP = BaseSDP +
	"i=A Seminar on the session description protocol\n" +
	"t=3034423619 3042462419\n"

	UriSDP = BaseSDP +
	"u=http://www.example.com/seminars/sdp.pdf\n" +
	"t=3034423619 3042462419\n"

	TimingSDP = BaseSDP +
	"t=2873397496 2873404696\n"

	RepeatTimesSDP = TimingSDP +
	"r=604800 3600 0 90000\n"
)

const CanonicalSDP = "v=0\n" +
"o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\n" +
"s=SDP Seminar\n" +
"i=A Seminar on the session description protocol\n" +
"u=http://www.example.com/seminars/sdp.pdf\n" +
"e=j.doe@example.com (Jane Doe)\n" +
"c=IN IP4 224.2.17.12/127\n" +
"b=X-YZ:128\n" +
"b=AS:12345\n" +
"t=2873397496 2873404696\n" +
"t=3034423619 3042462419\n" +
"r=604800 3600 0 90000\n" +
"z=2882844526 -1h 2898848070 0\n" +
"a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host\n" +
"a=candidate:1 2 UDP 2113667326 203.0.113.1 54401 typ host\n" +
"a=recvonly\n" +
"m=audio 49170 RTP/AVP 0\n" +
"m=video 51372 RTP/AVP 99\n" +
"a=rtpmap:99 h263-1998/90000\n"

func TestUnmarshalSessionInformation(t *testing.T) {
	sd := &SessionDescription{}
	if err := sd.Unmarshal(SessionInformationSDP); err != nil {
		t.Errorf("error: %v", err)
	}

	actual := sd.Marshal()
	if actual != SessionInformationSDP {
		t.Errorf("error:\n\nEXPECTED:\n%v\nACTUAL:\n%v", SessionInformationSDP, actual)
	}
}

func TestUnmarshalURI(t *testing.T) {
	sd := &SessionDescription{}
	if err := sd.Unmarshal(UriSDP); err != nil {
		t.Errorf("error: %v", err)
	}

	actual := sd.Marshal()
	if actual != UriSDP {
		t.Errorf("error:\n\nEXPECTED:\n%v\nACTUAL:\n%v", UriSDP, actual)
	}
}

func TestUnmarshalRepeatTimes(t *testing.T) {
	sd := &SessionDescription{}
	if err := sd.Unmarshal(RepeatTimesSDP); err != nil {
		t.Errorf("error: %v", err)
	}

	actual := sd.Marshal()
	if actual != RepeatTimesSDP {
		t.Errorf("error:\n\nEXPECTED:\n%v\nACTUAL:\n%v", RepeatTimesSDP, actual)
	}
}

func TestUnmarshalCanonical(t *testing.T) {
	sd := &SessionDescription{}
	if err := sd.Unmarshal(CanonicalSDP); err != nil {
		t.Errorf("error: %v", err)
	}

	actual := sd.Marshal()
	if actual != CanonicalSDP {
		t.Errorf("error:\n\nEXPECTED:\n%v\nACTUAL:\n%v", CanonicalSDP, actual)
	}
}
<|endoftext|>