<commit_before>package lfs

import (
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/git-lfs/git-lfs/config"
	"github.com/git-lfs/git-lfs/errors"
	"github.com/git-lfs/git-lfs/tools"
	"github.com/git-lfs/git-lfs/tools/humanize"
	"github.com/git-lfs/git-lfs/tq"
	"github.com/rubyist/tracerx"
)

func (f *GitFilter) SmudgeToFile(filename string, ptr *Pointer, download bool, manifest *tq.Manifest, cb tools.CopyCallback) error {
	tools.MkdirAll(filepath.Dir(filename), f.cfg)

	if stat, _ := os.Stat(filename); stat != nil && stat.Mode()&0200 == 0 {
		if err := os.Chmod(filename, stat.Mode()|0200); err != nil {
			return errors.Wrap(err,
				"Could not restore write permission")
		}

		// When we're done, return the file back to its normal
		// permission bits.
		defer os.Chmod(filename, stat.Mode())
	}

	abs, err := filepath.Abs(filename)
	if err != nil {
		return fmt.Errorf("could not produce absolute path for %q", filename)
	}

	file, err := os.Create(abs)
	if err != nil {
		return fmt.Errorf("could not create working directory file: %v", err)
	}
	defer file.Close()
	if _, err := f.Smudge(file, ptr, filename, download, manifest, cb); err != nil {
		if errors.IsDownloadDeclinedError(err) {
			// write placeholder data instead
			file.Seek(0, io.SeekStart)
			ptr.Encode(file)
			return err
		} else {
			return fmt.Errorf("could not write working directory file: %v", err)
		}
	}
	return nil
}

func (f *GitFilter) Smudge(writer io.Writer, ptr *Pointer, workingfile string, download bool, manifest *tq.Manifest, cb tools.CopyCallback) (int64, error) {
	mediafile, err := f.ObjectPath(ptr.Oid)
	if err != nil {
		return 0, err
	}

	LinkOrCopyFromReference(f.cfg, ptr.Oid, ptr.Size)

	stat, statErr := os.Stat(mediafile)
	if statErr == nil && stat != nil {
		fileSize := stat.Size()
		if fileSize != ptr.Size {
			tracerx.Printf("Removing %s, size %d is invalid", mediafile, fileSize)
			os.RemoveAll(mediafile)
			stat = nil
		}
	}

	var n int64

	if statErr != nil || stat == nil {
		if download {
			n, err = f.downloadFile(writer, ptr, workingfile, mediafile, manifest, cb)
		} else {
			return 0, errors.NewDownloadDeclinedError(statErr, "smudge")
		}
	} else {
		n, err = f.readLocalFile(writer, ptr, mediafile, workingfile, cb)
	}

	if err != nil {
		return 0, errors.NewSmudgeError(err, ptr.Oid, mediafile)
	}

	return n, nil
}

func (f *GitFilter) downloadFile(writer io.Writer, ptr *Pointer, workingfile, mediafile string, manifest *tq.Manifest, cb tools.CopyCallback) (int64, error) {
	fmt.Fprintf(os.Stderr, "Downloading %s (%s)\n", workingfile, humanize.FormatBytes(uint64(ptr.Size)))

	// NOTE: if given, "cb" is a tools.CopyCallback which writes updates
	// to the logpath specified by GIT_LFS_PROGRESS.
	//
	// Either way, forward it into the *tq.TransferQueue so that updates are
	// sent over correctly.

	q := tq.NewTransferQueue(tq.Download, manifest, f.cfg.Remote(),
		tq.WithProgressCallback(cb),
		tq.RemoteRef(f.RemoteRef()),
	)
	q.Add(filepath.Base(workingfile), mediafile, ptr.Oid, ptr.Size, false, nil)
	q.Wait()

	if errs := q.Errors(); len(errs) > 0 {
		var multiErr error
		for _, e := range errs {
			if multiErr != nil {
				multiErr = fmt.Errorf("%v\n%v", multiErr, e)
			} else {
				multiErr = e
			}
			return 0, errors.Wrapf(multiErr, "Error downloading %s (%s)", workingfile, ptr.Oid)
		}
	}

	return f.readLocalFile(writer, ptr, mediafile, workingfile, nil)
}

func (f *GitFilter) readLocalFile(writer io.Writer, ptr *Pointer, mediafile string, workingfile string, cb tools.CopyCallback) (int64, error) {
	reader, err := tools.RobustOpen(mediafile)
	if err != nil {
		return 0, errors.Wrapf(err, "error opening media file")
	}
	defer reader.Close()

	if ptr.Size == 0 {
		if stat, _ := os.Stat(mediafile); stat != nil {
			ptr.Size = stat.Size()
		}
	}

	if len(ptr.Extensions) > 0 {
		registeredExts := f.cfg.Extensions()
		extensions := make(map[string]config.Extension)
		for _, ptrExt := range ptr.Extensions {
			ext, ok := registeredExts[ptrExt.Name]
			if !ok {
				err := fmt.Errorf("extension '%s' is not configured", ptrExt.Name)
				return 0, errors.Wrap(err, "smudge")
			}
			ext.Priority = ptrExt.Priority
			extensions[ext.Name] = ext
		}
		exts, err := config.SortExtensions(extensions)
		if err != nil {
			return 0, errors.Wrap(err, "smudge")
		}

		// pipe extensions in reverse order
		var extsR []config.Extension
		for i := range exts {
			ext := exts[len(exts)-1-i]
			extsR = append(extsR, ext)
		}

		request := &pipeRequest{"smudge", reader, workingfile, extsR}

		response, err := pipeExtensions(f.cfg, request)
		if err != nil {
			return 0, errors.Wrap(err, "smudge")
		}

		actualExts := make(map[string]*pipeExtResult)
		for _, result := range response.results {
			actualExts[result.name] = result
		}

		// verify name, order, and oids
		oid := response.results[0].oidIn
		if ptr.Oid != oid {
			err = fmt.Errorf("actual oid %s during smudge does not match expected %s", oid, ptr.Oid)
			return 0, errors.Wrap(err, "smudge")
		}

		for _, expected := range ptr.Extensions {
			actual := actualExts[expected.Name]
			if actual.name != expected.Name {
				err = fmt.Errorf("actual extension name '%s' does not match expected '%s'", actual.name, expected.Name)
				return 0, errors.Wrap(err, "smudge")
			}
			if actual.oidOut != expected.Oid {
				err = fmt.Errorf("actual oid %s for extension '%s' does not match expected %s", actual.oidOut, expected.Name, expected.Oid)
				return 0, errors.Wrap(err, "smudge")
			}
		}

		// setup reader
		reader, err = os.Open(response.file.Name())
		if err != nil {
			return 0, errors.Wrapf(err, "Error opening smudged file: %s", err)
		}
		defer reader.Close()
	}

	n, err := tools.CopyWithCallback(writer, reader, ptr.Size, cb)
	if err != nil {
		return n, errors.Wrapf(err, "Error reading from media file: %s", err)
	}

	return n, nil
}
<commit_msg>Fix downloadFile in gitfilter_smudge.go to actually propagate all errors<commit_after>package lfs

import (
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/git-lfs/git-lfs/config"
	"github.com/git-lfs/git-lfs/errors"
	"github.com/git-lfs/git-lfs/tools"
	"github.com/git-lfs/git-lfs/tools/humanize"
	"github.com/git-lfs/git-lfs/tq"
	"github.com/rubyist/tracerx"
)

func (f *GitFilter) SmudgeToFile(filename string, ptr *Pointer, download bool, manifest *tq.Manifest, cb tools.CopyCallback) error {
	tools.MkdirAll(filepath.Dir(filename), f.cfg)

	if stat, _ := os.Stat(filename); stat != nil && stat.Mode()&0200 == 0 {
		if err := os.Chmod(filename, stat.Mode()|0200); err != nil {
			return errors.Wrap(err,
				"Could not restore write permission")
		}

		// When we're done, return the file back to its normal
		// permission bits.
		defer os.Chmod(filename, stat.Mode())
	}

	abs, err := filepath.Abs(filename)
	if err != nil {
		return fmt.Errorf("could not produce absolute path for %q", filename)
	}

	file, err := os.Create(abs)
	if err != nil {
		return fmt.Errorf("could not create working directory file: %v", err)
	}
	defer file.Close()
	if _, err := f.Smudge(file, ptr, filename, download, manifest, cb); err != nil {
		if errors.IsDownloadDeclinedError(err) {
			// write placeholder data instead
			file.Seek(0, io.SeekStart)
			ptr.Encode(file)
			return err
		} else {
			return fmt.Errorf("could not write working directory file: %v", err)
		}
	}
	return nil
}

func (f *GitFilter) Smudge(writer io.Writer, ptr *Pointer, workingfile string, download bool, manifest *tq.Manifest, cb tools.CopyCallback) (int64, error) {
	mediafile, err := f.ObjectPath(ptr.Oid)
	if err != nil {
		return 0, err
	}

	LinkOrCopyFromReference(f.cfg, ptr.Oid, ptr.Size)

	stat, statErr := os.Stat(mediafile)
	if statErr == nil && stat != nil {
		fileSize := stat.Size()
		if fileSize != ptr.Size {
			tracerx.Printf("Removing %s, size %d is invalid", mediafile, fileSize)
			os.RemoveAll(mediafile)
			stat = nil
		}
	}

	var n int64

	if statErr != nil || stat == nil {
		if download {
			n, err = f.downloadFile(writer, ptr, workingfile, mediafile, manifest, cb)
		} else {
			return 0, errors.NewDownloadDeclinedError(statErr, "smudge")
		}
	} else {
		n, err = f.readLocalFile(writer, ptr, mediafile, workingfile, cb)
	}

	if err != nil {
		return 0, errors.NewSmudgeError(err, ptr.Oid, mediafile)
	}

	return n, nil
}

func (f *GitFilter) downloadFile(writer io.Writer, ptr *Pointer, workingfile, mediafile string, manifest *tq.Manifest, cb tools.CopyCallback) (int64, error) {
	fmt.Fprintf(os.Stderr, "Downloading %s (%s)\n", workingfile, humanize.FormatBytes(uint64(ptr.Size)))

	// NOTE: if given, "cb" is a tools.CopyCallback which writes updates
	// to the logpath specified by GIT_LFS_PROGRESS.
	//
	// Either way, forward it into the *tq.TransferQueue so that updates are
	// sent over correctly.

	q := tq.NewTransferQueue(tq.Download, manifest, f.cfg.Remote(),
		tq.WithProgressCallback(cb),
		tq.RemoteRef(f.RemoteRef()),
	)
	q.Add(filepath.Base(workingfile), mediafile, ptr.Oid, ptr.Size, false, nil)
	q.Wait()

	if errs := q.Errors(); len(errs) > 0 {
		var multiErr error
		for _, e := range errs {
			if multiErr != nil {
				multiErr = fmt.Errorf("%v\n%v", multiErr, e)
			} else {
				multiErr = e
			}
		}

		return 0, errors.Wrapf(multiErr, "Error downloading %s (%s)", workingfile, ptr.Oid)
	}

	return f.readLocalFile(writer, ptr, mediafile, workingfile, nil)
}

func (f *GitFilter) readLocalFile(writer io.Writer, ptr *Pointer, mediafile string, workingfile string, cb tools.CopyCallback) (int64, error) {
	reader, err := tools.RobustOpen(mediafile)
	if err != nil {
		return 0, errors.Wrapf(err, "error opening media file")
	}
	defer reader.Close()

	if ptr.Size == 0 {
		if stat, _ := os.Stat(mediafile); stat != nil {
			ptr.Size = stat.Size()
		}
	}

	if len(ptr.Extensions) > 0 {
		registeredExts := f.cfg.Extensions()
		extensions := make(map[string]config.Extension)
		for _, ptrExt := range ptr.Extensions {
			ext, ok := registeredExts[ptrExt.Name]
			if !ok {
				err := fmt.Errorf("extension '%s' is not configured", ptrExt.Name)
				return 0, errors.Wrap(err, "smudge")
			}
			ext.Priority = ptrExt.Priority
			extensions[ext.Name] = ext
		}
		exts, err := config.SortExtensions(extensions)
		if err != nil {
			return 0, errors.Wrap(err, "smudge")
		}

		// pipe extensions in reverse order
		var extsR []config.Extension
		for i := range exts {
			ext := exts[len(exts)-1-i]
			extsR = append(extsR, ext)
		}

		request := &pipeRequest{"smudge", reader, workingfile, extsR}

		response, err := pipeExtensions(f.cfg, request)
		if err != nil {
			return 0, errors.Wrap(err, "smudge")
		}

		actualExts := make(map[string]*pipeExtResult)
		for _, result := range response.results {
			actualExts[result.name] = result
		}

		// verify name, order, and oids
		oid := response.results[0].oidIn
		if ptr.Oid != oid {
			err = fmt.Errorf("actual oid %s during smudge does not match expected %s", oid, ptr.Oid)
			return 0, errors.Wrap(err, "smudge")
		}

		for _, expected := range ptr.Extensions {
			actual := actualExts[expected.Name]
			if actual.name != expected.Name {
				err = fmt.Errorf("actual extension name '%s' does not match expected '%s'", actual.name, expected.Name)
				return 0, errors.Wrap(err, "smudge")
			}
			if actual.oidOut != expected.Oid {
				err = fmt.Errorf("actual oid %s for extension '%s' does not match expected %s", actual.oidOut, expected.Name, expected.Oid)
				return 0, errors.Wrap(err, "smudge")
			}
		}

		// setup reader
		reader, err = os.Open(response.file.Name())
		if err != nil {
			return 0, errors.Wrapf(err, "Error opening smudged file: %s", err)
		}
		defer reader.Close()
	}

	n, err := tools.CopyWithCallback(writer, reader, ptr.Size, cb)
	if err != nil {
		return n, errors.Wrapf(err, "Error reading from media file: %s", err)
	}

	return n, nil
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// 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
//
// 	https://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 xsrf_test

import (
	"github.com/google/go-safeweb/safehttp_test"
	"testing"
)

func TestTokenValidation(t *testing.T) {

}

func TestFetchMetadataPolicy(t *testing.T) {

}
<commit_msg>Added tests for xsrf skeleton<commit_after>// Copyright 2020 Google LLC
//
// 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
//
// 	https://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 xsrf_test

import (
	"github.com/google/go-safeweb/safehttp"
	"github.com/google/go-safeweb/safehttp/plugins/xsrf"
	"github.com/google/safehtml"
	"github.com/google/safehtml/template"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

type testDispatcher struct{}

func (testDispatcher) Write(rw http.ResponseWriter, resp safehttp.Response) error {
	switch x := resp.(type) {
	case safehtml.HTML:
		_, err := rw.Write([]byte(x.String()))
		return err
	default:
		panic("not a safe response type")
	}
}

func (testDispatcher) ExecuteTemplate(rw http.ResponseWriter, t safehttp.Template, data interface{}) error {
	switch x := t.(type) {
	case *template.Template:
		return x.Execute(rw, data)
	default:
		panic("not a safe response type")
	}
}

type responseRecorder struct {
	header http.Header
	writer io.Writer
	status int
}

func newResponseRecorder(w io.Writer) *responseRecorder {
	return &responseRecorder{
		header: http.Header{},
		writer: w,
		status: http.StatusOK,
	}
}

func (r *responseRecorder) Header() http.Header {
	return r.header
}

func (r *responseRecorder) WriteHeader(statusCode int) {
	r.status = statusCode
}

func (r *responseRecorder) Write(data []byte) (int, error) {
	return r.writer.Write(data)
}

type testStorageService struct{}

func (s testStorageService) GetUserID() (string, error) {
	return "potato", nil
}

func TestXSRFToken(t *testing.T) {
	tests := []struct {
		name       string
		target     string
		host       string
		path       string
		wantStatus int
	}{
		{
			name:       "Valid token",
			target:     "http://foo.com/pizza",
			host:       "foo.com",
			path:       "/pizza",
			wantStatus: 200,
		},
		{
			name:       "Invalid host in token generation",
			target:     "http://foo.com/pizza",
			host:       "bar.com",
			path:       "/pizza",
			wantStatus: 403,
		},
		{
			name:       "Invalid path in token generation",
			target:     "http://foo.com/pizza",
			host:       "foo.com",
			path:       "/spaghetti",
			wantStatus: 403,
		},
	}
	for _, test := range tests {
		p := xsrf.NewPlugin("1234", testStorageService{})
		tok, err := p.GenerateToken(test.host, test.path)
		if err != nil {
			t.Fatalf("p.GenerateToken: got %v, want nil", err)
		}
		b := strings.NewReader(xsrf.TokenKey + "=" + tok)
		req := httptest.NewRequest("POST", test.target, b)
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		m := safehttp.NewMachinery(p.Before, &testDispatcher{})
		rec := newResponseRecorder(&strings.Builder{})
		m.HandleRequest(rec, req)
		if want := test.wantStatus; rec.status != want {
			t.Errorf("response status: got %v, want %v", rec.status, want)
		}

	}
}
<|endoftext|>


<commit_before>/* Copyright 2017 Google Inc.
 * https://github.com/NeilFraser/CodeCity
 *
 * 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 object

import (
	"fmt"
	"math"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf16"
)

// Booleans, numbers and strings are represented as immediate data -
// i.e., the Value interface data contains the value itself rather
// than a pointer to it, as it would in the case of a plain object.
// (null and undefined are similarly represented with empty structs.)
//
//
// tl;dr: do NOT take the address of a primitive.

// NewFromRaw takes a raw JavaScript literal (as a string as it
// appears in the source code, and as found in an ast.Literal.Raw
// property) and returns a primitive Value object representing the
// value of that literal.
func NewFromRaw(raw string) Value {
	if raw == "true" {
		return Boolean(true)
	} else if raw == "false" {
		return Boolean(false)
	} else if raw == "undefined" {
		return Undefined{}
	} else if raw == "null" {
		return Null{}
	} else if raw[0] == '"' {
		s, err := strconv.Unquote(raw)
		if err != nil {
			panic(err)
		}
		return String(s)
	} else if raw[0] == '\'' {
		// BUG(cpcallen): single-quoted string literals not implemented.
		panic(fmt.Errorf("Single-quoted string literals not implemented"))
	} else if unicode.IsDigit(rune(raw[0])) {
		// BUG(cpcallen): numeric literals probably not handled
		// completely in accordance with ES5.1 spec; it is implemented
		// using String.ToNumber which may be unduly tolerant and
		// handle certain edge cases differently.
		return String(raw).ToNumber()
	} else if raw[0] == '/' {
		// BUG(cpcallen): regular expresion literals not implemented.
		panic(fmt.Errorf("Regular Expression literals not implemented"))
	} else {
		panic(fmt.Errorf("Unrecognized raw literal %v", raw))
	}
}

/********************************************************************/

// Boolean represents a JS boolean value.
type Boolean bool

// Boolean must satisfy Value.
var _ Value = Boolean(false)

// Type always returns "boolean" for Booleans.
func (Boolean) Type() string {
	return "boolean"
}

// IsPrimitive alwasy returns true for Booleans.
func (Boolean) IsPrimitive() bool {
	return true
}

// Proto returns BooleanProto for all Booleans.
func (Boolean) Proto() Value {
	return BooleanProto
}

// GetProperty on Boolean just passes to its prototype:
func (b Boolean) GetProperty(name string) (Value, *ErrorMsg) {
	return b.Proto().GetProperty(name)
}

// SetProperty on Boolean always succeeds but has no effect.
func (Boolean) SetProperty(name string, value Value) *ErrorMsg {
	return nil
}

func (Boolean) propNames() []string { return nil }

// HasOwnProperty always returns false for Boolean values.
func (Boolean) HasOwnProperty(string) bool { return false }

// DeleteProperty always succeeds on Boolean.
func (Boolean) DeleteProperty(name string) *ErrorMsg {
	return nil
}

// ToBoolean on a Boolean just returns itself.
func (b Boolean) ToBoolean() Boolean {
	return b
}

// ToNumber converts a boolean to 1 (if true) or 0 (if false).
func (b Boolean) ToNumber() Number {
	if b {
		return 1
	} else {
		return 0
	}
}

// ToString converts a boolean to "true" or "false" as appropriate.
func (b Boolean) ToString() String {
	if b {
		return "true"
	} else {
		return "false"
	}
}

// ToPrimitive on a primitive just returns itself.
func (b Boolean) ToPrimitive() Value {
	return b
}

/********************************************************************/

// Number represents a JS numeric value.
type Number float64

// Number must satisfy Value.
var _ Value = Number(0)

// Type always returns "number" for numbers.
func (Number) Type() string {
	return "number"
}

// IsPrimitive alwasy returns true for Numbers.
func (Number) IsPrimitive() bool {
	return true
}

// Proto returns NumberProto for all Numbers.
func (Number) Proto() Value {
	return NumberProto
}

// GetProperty on Number just passes to its prototype:
func (n Number) GetProperty(name string) (Value, *ErrorMsg) {
	return n.Proto().GetProperty(name)
}

// SetProperty on Number always succeeds but has no effect.
func (Number) SetProperty(name string, value Value) *ErrorMsg {
	return nil
}

func (Number) propNames() []string { return nil }

// HasOwnProperty always returns false for Number values.
func (Number) HasOwnProperty(string) bool { return false }

// DeleteProperty always succeeds on Number.
func (Number) DeleteProperty(name string) *ErrorMsg {
	return nil
}

// ToBoolean on a number returns true if the number is not 0 or NaN.
func (n Number) ToBoolean() Boolean {
	return Boolean(!(float64(n) == 0 || math.IsNaN(float64(n))))
}

// ToNumber on a Number just returns itself.
func (n Number) ToNumber() Number {
	return n
}

// ToString on a number returns "Infinity" for +Inf, "-Infinity" for
// -Inf, "NaN" for NaN, and a decimal or exponential representation
// for regular numeric values.
//
// BUG(cpcallen): This implementation is probably not strictly
// compatible with the ES5.1 spec.  In particular, transtion from
// decimal to exponential representation is not guaranteed to be
// compliant.
//
// FIXME: Should we return "-0" for negative zero?  Do we?
func (n Number) ToString() String {
	switch float64(n) {
	case math.Inf(+1):
		return "Infinity"
	case math.Inf(-1):
		return "-Infinity"
	default:
		return String(fmt.Sprintf("%g", n))
	}
}

// ToPrimitive on a primitive just returns itself.
func (n Number) ToPrimitive() Value {
	return n
}

/********************************************************************/

// String represents a JS string value.
type String string

// String must satisfy Value.
var _ Value = String("")

// Type always returns "string" for strings.
func (String) Type() string {
	return "string"
}

// IsPrimitive alwasy returns true for Strings.
func (String) IsPrimitive() bool {
	return true
}

// Proto returns StringProto for all Strings.
func (String) Proto() Value {
	return StringProto
}

// GetProperty on String implements a magic .length property itself,
// and passes any other property lookups to its prototype:
func (s String) GetProperty(name string) (Value, *ErrorMsg) {
	if name != "length" {
		return s.Proto().GetProperty(name)
	}
	return Number(len(utf16.Encode([]rune(string(s))))), nil
}

// SetProperty on String always succeeds but has no effect (even on
// length).
func (String) SetProperty(name string, value Value) *ErrorMsg {
	return nil
}

func (String) propNames() []string { return []string{"length"} }

// HasOwnProperty always returns true for "length" and false for all
// other inputs for Strings.
//
// FIXME: should return true for numeric inputs 0 <= n < length!
func (String) HasOwnProperty(s string) bool {
	if s == "length" {
		return true
	}
	return false
}

// DeleteProperty always succeeds on String unless name is "length".
func (s String) DeleteProperty(name string) *ErrorMsg {
	if name != "length" {
		return nil
	}
	return &ErrorMsg{"TypeError",
		fmt.Sprintf("Cannot delete property 'length' of %s", s.ToString())}
}

// ToBoolean on String returns true iff the string is non-empty.
func (s String) ToBoolean() Boolean {
	return len(string(s)) != 0
}

// ToNumber returns the numeric value of the string, or NaN if it does
// not look like a number.
//
// BUG(cpcallen): String.ToNumber() is probably not strictly compliant
// with the ES5.1 spec.
func (s String) ToNumber() Number {
	str := strings.TrimSpace(string(s))
	if len(str) == 0 { // Empty string == 0
		return 0
	}
	if len(str) > 2 { // Hex?  (Octal not supported in use strict!)
		pfx := str[0:2]
		if pfx == "0x" || pfx == "0X" {
			n, err := strconv.ParseInt(str[2:], 16, 64)
			if err != nil {
				if err.(*strconv.NumError).Err == strconv.ErrSyntax {
					return Number(math.NaN())
				} else if err.(*strconv.NumError).Err == strconv.ErrRange {
					if n > 0 {
						return Number(math.Inf(+1))
					} else {
						return Number(math.Inf(-1))
					}
				} else {
					panic(err)
				}
			}
			return Number(float64(n))
		}
	}
	n, err := strconv.ParseFloat(str, 64)
	if err != nil {
		// Malformed number?
		if err.(*strconv.NumError).Err == strconv.ErrSyntax {
			return Number(math.NaN())
		} else if err.(*strconv.NumError).Err != strconv.ErrRange {
			panic(err)
		}
	}
	return Number(n)
}

// ToString on a string just returns itself.
func (s String) ToString() String {
	return s
}

// ToPrimitive on a primitive just returns itself.
func (s String) ToPrimitive() Value {
	return s
}

/********************************************************************/

// Null represents a JS null value.
type Null struct{}

// Null must satisfy Value.
var _ Value = Null{}

// Type (surprisingly) returns "object" for null values.
func (Null) Type() string {
	return "object"
}

// IsPrimitive alwasy returns true for Null.
func (Null) IsPrimitive() bool {
	return true
}

// Proto on Undefined and Null values should not be callable from
// user code, but is used in various places internally (e.g.,
// PropIter.Next()); we return nil to signal that there is no prototype.
// (Previously we returned Undefined{} or Null{}, but this just forces
// us to write additional code elsewhere to avoid infinite loops, and
// violates the rule that there should be no prototype chain loops.)
func (Null) Proto() Value {
	return nil
}

// GetProperty on Null always returns an error.
func (Null) GetProperty(name string) (Value, *ErrorMsg) {
	return nil, &ErrorMsg{
		Name:    "TypeError",
		Message: fmt.Sprintf("Cannot read property '%s' of null", name),
	}
}

// SetProperty on Null always fails.
func (Null) SetProperty(name string, value Value) *ErrorMsg {
	return &ErrorMsg{
		Name:    "TypeError",
		Message: fmt.Sprintf("Cannot set property '%s' of null", name),
	}
}

func (Null) propNames() []string { return nil }

// HasOwnProperty always returns false for Null values
// FIXME: this should throw.
func (Null) HasOwnProperty(string) bool { return false }

// DeleteProperty should never be called on Null
func (Null) DeleteProperty(name string) *ErrorMsg {
	panic("Null.DeleteProperty() not callable")
}

// ToBoolean on Null always return false.
func (Null) ToBoolean() Boolean {
	return false
}

// ToNumber on Null always returns 0.
func (Null) ToNumber() Number {
	return 0
}

// ToString on Null always returns "null".
func (Null) ToString() String {
	return "null"
}

// ToPrimitive on a primitive just returns itself.
func (Null) ToPrimitive() Value {
	return Null{}
}

/********************************************************************/

// Undefined represents a JS undefined value.
type Undefined struct{}

// Undefined must satisfy Value.
var _ Value = Undefined{}

// Type always returns "undefined" for undefined.
func (Undefined) Type() string {
	return "undefined"
}

// IsPrimitive always returns true for Undefined.
func (Undefined) IsPrimitive() bool {
	return true
}

// Proto on Undefined returns nil; see not on Null.Proto() for why.
func (Undefined) Proto() Value {
	return nil
}

// GetProperty on Undefined always returns an error.
func (Undefined) GetProperty(name string) (Value, *ErrorMsg) {
	return nil, &ErrorMsg{
		Name:    "TypeError",
		Message: fmt.Sprintf("Cannot read property '%s' of undefined", name),
	}
}

// SetProperty on Undefined always fails.
func (Undefined) SetProperty(name string, value Value) *ErrorMsg {
	return &ErrorMsg{
		Name:    "TypeError",
		Message: fmt.Sprintf("Cannot set property '%s' of undefined", name),
	}
}

func (Undefined) propNames() []string { return nil }

// HasOwnProperty always returns false for Undefined values
// FIXME: this should throw.
func (Undefined) HasOwnProperty(string) bool { return false }

// DeleteProperty should never be called on Undeined.
func (Undefined) DeleteProperty(name string) *ErrorMsg {
	panic("Null.DeleteProperty() not callable")
}

// ToBoolean on Undefined always returns false.
func (Undefined) ToBoolean() Boolean {
	return false
}

// ToNumber on Undefined always returns NaN.
func (Undefined) ToNumber() Number {
	return Number(math.NaN())
}

// ToString on Undefined always returns "undefined".
func (Undefined) ToString() String {
	return "undefined"
}

// ToPrimitive on a primitive just returns itself.
func (Undefined) ToPrimitive() Value {
	return Undefined{}
}

/********************************************************************/

// BooleanProto is the the (plain) JavaScript object that is the
// prototype for all Boolean primitives.  (It would usually be
// accessed in JavaScript as Boolean.prototype.)
var BooleanProto = New(nil, ObjectProto)

// NumberProto is the the (plain) JavaScript object that is the
// prototype for all Number primitives.  (It would usually be
// accessed in JavaScript as Number.prototype.)
var NumberProto = New(nil, ObjectProto)

// StringProto is the the (plain) JavaScript object that is the
// prototype for all String primitives.  (It would usually be
// accessed in JavaScript as String.prototype.)
var StringProto = New(nil, ObjectProto)
<commit_msg>Improve spec-compliance of Number.ToString()<commit_after>/* Copyright 2017 Google Inc.
 * https://github.com/NeilFraser/CodeCity
 *
 * 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 object

import (
	"fmt"
	"math"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf16"
)

// Booleans, numbers and strings are represented as immediate data -
// i.e., the Value interface data contains the value itself rather
// than a pointer to it, as it would in the case of a plain object.
// (null and undefined are similarly represented with empty structs.)
//
//
// tl;dr: do NOT take the address of a primitive.

// NewFromRaw takes a raw JavaScript literal (as a string as it
// appears in the source code, and as found in an ast.Literal.Raw
// property) and returns a primitive Value object representing the
// value of that literal.
func NewFromRaw(raw string) Value {
	if raw == "true" {
		return Boolean(true)
	} else if raw == "false" {
		return Boolean(false)
	} else if raw == "undefined" {
		return Undefined{}
	} else if raw == "null" {
		return Null{}
	} else if raw[0] == '"' {
		s, err := strconv.Unquote(raw)
		if err != nil {
			panic(err)
		}
		return String(s)
	} else if raw[0] == '\'' {
		// BUG(cpcallen): single-quoted string literals not implemented.
		panic(fmt.Errorf("Single-quoted string literals not implemented"))
	} else if unicode.IsDigit(rune(raw[0])) {
		// BUG(cpcallen): numeric literals probably not handled
		// completely in accordance with ES5.1 spec; it is implemented
		// using String.ToNumber which may be unduly tolerant and
		// handle certain edge cases differently.
		return String(raw).ToNumber()
	} else if raw[0] == '/' {
		// BUG(cpcallen): regular expresion literals not implemented.
		panic(fmt.Errorf("Regular Expression literals not implemented"))
	} else {
		panic(fmt.Errorf("Unrecognized raw literal %v", raw))
	}
}

/********************************************************************/

// Boolean represents a JS boolean value.
type Boolean bool

// Boolean must satisfy Value.
var _ Value = Boolean(false)

// Type always returns "boolean" for Booleans.
func (Boolean) Type() string {
	return "boolean"
}

// IsPrimitive alwasy returns true for Booleans.
func (Boolean) IsPrimitive() bool {
	return true
}

// Proto returns BooleanProto for all Booleans.
func (Boolean) Proto() Value {
	return BooleanProto
}

// GetProperty on Boolean just passes to its prototype:
func (b Boolean) GetProperty(name string) (Value, *ErrorMsg) {
	return b.Proto().GetProperty(name)
}

// SetProperty on Boolean always succeeds but has no effect.
func (Boolean) SetProperty(name string, value Value) *ErrorMsg {
	return nil
}

func (Boolean) propNames() []string { return nil }

// HasOwnProperty always returns false for Boolean values.
func (Boolean) HasOwnProperty(string) bool { return false }

// DeleteProperty always succeeds on Boolean.
func (Boolean) DeleteProperty(name string) *ErrorMsg {
	return nil
}

// ToBoolean on a Boolean just returns itself.
func (b Boolean) ToBoolean() Boolean {
	return b
}

// ToNumber converts a boolean to 1 (if true) or 0 (if false).
func (b Boolean) ToNumber() Number {
	if b {
		return 1
	} else {
		return 0
	}
}

// ToString converts a boolean to "true" or "false" as appropriate.
func (b Boolean) ToString() String {
	if b {
		return "true"
	} else {
		return "false"
	}
}

// ToPrimitive on a primitive just returns itself.
func (b Boolean) ToPrimitive() Value {
	return b
}

/********************************************************************/

// Number represents a JS numeric value.
type Number float64

// Number must satisfy Value.
var _ Value = Number(0)

// Type always returns "number" for numbers.
func (Number) Type() string {
	return "number"
}

// IsPrimitive alwasy returns true for Numbers.
func (Number) IsPrimitive() bool {
	return true
}

// Proto returns NumberProto for all Numbers.
func (Number) Proto() Value {
	return NumberProto
}

// GetProperty on Number just passes to its prototype:
func (n Number) GetProperty(name string) (Value, *ErrorMsg) {
	return n.Proto().GetProperty(name)
}

// SetProperty on Number always succeeds but has no effect.
func (Number) SetProperty(name string, value Value) *ErrorMsg {
	return nil
}

func (Number) propNames() []string { return nil }

// HasOwnProperty always returns false for Number values.
func (Number) HasOwnProperty(string) bool { return false }

// DeleteProperty always succeeds on Number.
func (Number) DeleteProperty(name string) *ErrorMsg {
	return nil
}

// ToBoolean on a number returns true if the number is not 0 or NaN.
func (n Number) ToBoolean() Boolean {
	return Boolean(!(float64(n) == 0 || math.IsNaN(float64(n))))
}

// ToNumber on a Number just returns itself.
func (n Number) ToNumber() Number {
	return n
}

// ToString on a number returns "Infinity" for +Inf, "-Infinity" for
// -Inf, "NaN" for NaN, and a decimal or exponential representation
// for regular numeric values.
//
// BUG(cpcallen): This implementation may not be strictly compatible
// with the ES5.1 spec, although transtion from decimal to exponential
// representation should be correct.
//
// FIXME: Should we return "-0" for negative zero?  Do we?
func (n Number) ToString() String {
	switch float64(n) {
	case math.Inf(+1):
		return "Infinity"
	case math.Inf(-1):
		return "-Infinity"
	case 0:
		if math.Signbit(float64(n)) {
			return "-0"
		}
		return "0"
	default:
		exp := math.Log10(math.Abs(float64(n)))
		if exp >= 21 || exp < -6 {
			return String(strconv.FormatFloat(float64(n), 'e', -1, 64))
		}
		return String(strconv.FormatFloat(float64(n), 'f', -1, 64))
	}
}

// ToPrimitive on a primitive just returns itself.
func (n Number) ToPrimitive() Value {
	return n
}

/********************************************************************/

// String represents a JS string value.
type String string

// String must satisfy Value.
var _ Value = String("")

// Type always returns "string" for strings.
func (String) Type() string {
	return "string"
}

// IsPrimitive alwasy returns true for Strings.
func (String) IsPrimitive() bool {
	return true
}

// Proto returns StringProto for all Strings.
func (String) Proto() Value {
	return StringProto
}

// GetProperty on String implements a magic .length property itself,
// and passes any other property lookups to its prototype:
func (s String) GetProperty(name string) (Value, *ErrorMsg) {
	if name != "length" {
		return s.Proto().GetProperty(name)
	}
	return Number(len(utf16.Encode([]rune(string(s))))), nil
}

// SetProperty on String always succeeds but has no effect (even on
// length).
func (String) SetProperty(name string, value Value) *ErrorMsg {
	return nil
}

func (String) propNames() []string { return []string{"length"} }

// HasOwnProperty always returns true for "length" and false for all
// other inputs for Strings.
//
// FIXME: should return true for numeric inputs 0 <= n < length!
func (String) HasOwnProperty(s string) bool {
	if s == "length" {
		return true
	}
	return false
}

// DeleteProperty always succeeds on String unless name is "length".
func (s String) DeleteProperty(name string) *ErrorMsg {
	if name != "length" {
		return nil
	}
	return &ErrorMsg{"TypeError",
		fmt.Sprintf("Cannot delete property 'length' of %s", s.ToString())}
}

// ToBoolean on String returns true iff the string is non-empty.
func (s String) ToBoolean() Boolean {
	return len(string(s)) != 0
}

// ToNumber returns the numeric value of the string, or NaN if it does
// not look like a number.
//
// BUG(cpcallen): String.ToNumber() is probably not strictly compliant
// with the ES5.1 spec.
func (s String) ToNumber() Number {
	str := strings.TrimSpace(string(s))
	if len(str) == 0 { // Empty string == 0
		return 0
	}
	if len(str) > 2 { // Hex?  (Octal not supported in use strict!)
		pfx := str[0:2]
		if pfx == "0x" || pfx == "0X" {
			n, err := strconv.ParseInt(str[2:], 16, 64)
			if err != nil {
				if err.(*strconv.NumError).Err == strconv.ErrSyntax {
					return Number(math.NaN())
				} else if err.(*strconv.NumError).Err == strconv.ErrRange {
					if n > 0 {
						return Number(math.Inf(+1))
					} else {
						return Number(math.Inf(-1))
					}
				} else {
					panic(err)
				}
			}
			return Number(float64(n))
		}
	}
	n, err := strconv.ParseFloat(str, 64)
	if err != nil {
		// Malformed number?
		if err.(*strconv.NumError).Err == strconv.ErrSyntax {
			return Number(math.NaN())
		} else if err.(*strconv.NumError).Err != strconv.ErrRange {
			panic(err)
		}
	}
	return Number(n)
}

// ToString on a string just returns itself.
func (s String) ToString() String {
	return s
}

// ToPrimitive on a primitive just returns itself.
func (s String) ToPrimitive() Value {
	return s
}

/********************************************************************/

// Null represents a JS null value.
type Null struct{}

// Null must satisfy Value.
var _ Value = Null{}

// Type (surprisingly) returns "object" for null values.
func (Null) Type() string {
	return "object"
}

// IsPrimitive alwasy returns true for Null.
func (Null) IsPrimitive() bool {
	return true
}

// Proto on Undefined and Null values should not be callable from
// user code, but is used in various places internally (e.g.,
// PropIter.Next()); we return nil to signal that there is no prototype.
// (Previously we returned Undefined{} or Null{}, but this just forces
// us to write additional code elsewhere to avoid infinite loops, and
// violates the rule that there should be no prototype chain loops.)
func (Null) Proto() Value {
	return nil
}

// GetProperty on Null always returns an error.
func (Null) GetProperty(name string) (Value, *ErrorMsg) {
	return nil, &ErrorMsg{
		Name:    "TypeError",
		Message: fmt.Sprintf("Cannot read property '%s' of null", name),
	}
}

// SetProperty on Null always fails.
func (Null) SetProperty(name string, value Value) *ErrorMsg {
	return &ErrorMsg{
		Name:    "TypeError",
		Message: fmt.Sprintf("Cannot set property '%s' of null", name),
	}
}

func (Null) propNames() []string { return nil }

// HasOwnProperty always returns false for Null values
// FIXME: this should throw.
func (Null) HasOwnProperty(string) bool { return false }

// DeleteProperty should never be called on Null
func (Null) DeleteProperty(name string) *ErrorMsg {
	panic("Null.DeleteProperty() not callable")
}

// ToBoolean on Null always return false.
func (Null) ToBoolean() Boolean {
	return false
}

// ToNumber on Null always returns 0.
func (Null) ToNumber() Number {
	return 0
}

// ToString on Null always returns "null".
func (Null) ToString() String {
	return "null"
}

// ToPrimitive on a primitive just returns itself.
func (Null) ToPrimitive() Value {
	return Null{}
}

/********************************************************************/

// Undefined represents a JS undefined value.
type Undefined struct{}

// Undefined must satisfy Value.
var _ Value = Undefined{}

// Type always returns "undefined" for undefined.
func (Undefined) Type() string {
	return "undefined"
}

// IsPrimitive always returns true for Undefined.
func (Undefined) IsPrimitive() bool {
	return true
}

// Proto on Undefined returns nil; see not on Null.Proto() for why.
func (Undefined) Proto() Value {
	return nil
}

// GetProperty on Undefined always returns an error.
func (Undefined) GetProperty(name string) (Value, *ErrorMsg) {
	return nil, &ErrorMsg{
		Name:    "TypeError",
		Message: fmt.Sprintf("Cannot read property '%s' of undefined", name),
	}
}

// SetProperty on Undefined always fails.
func (Undefined) SetProperty(name string, value Value) *ErrorMsg {
	return &ErrorMsg{
		Name:    "TypeError",
		Message: fmt.Sprintf("Cannot set property '%s' of undefined", name),
	}
}

func (Undefined) propNames() []string { return nil }

// HasOwnProperty always returns false for Undefined values
// FIXME: this should throw.
func (Undefined) HasOwnProperty(string) bool { return false }

// DeleteProperty should never be called on Undeined.
func (Undefined) DeleteProperty(name string) *ErrorMsg {
	panic("Null.DeleteProperty() not callable")
}

// ToBoolean on Undefined always returns false.
func (Undefined) ToBoolean() Boolean {
	return false
}

// ToNumber on Undefined always returns NaN.
func (Undefined) ToNumber() Number {
	return Number(math.NaN())
}

// ToString on Undefined always returns "undefined".
func (Undefined) ToString() String {
	return "undefined"
}

// ToPrimitive on a primitive just returns itself.
func (Undefined) ToPrimitive() Value {
	return Undefined{}
}

/********************************************************************/

// BooleanProto is the the (plain) JavaScript object that is the
// prototype for all Boolean primitives.  (It would usually be
// accessed in JavaScript as Boolean.prototype.)
var BooleanProto = New(nil, ObjectProto)

// NumberProto is the the (plain) JavaScript object that is the
// prototype for all Number primitives.  (It would usually be
// accessed in JavaScript as Number.prototype.)
var NumberProto = New(nil, ObjectProto)

// StringProto is the the (plain) JavaScript object that is the
// prototype for all String primitives.  (It would usually be
// accessed in JavaScript as String.prototype.)
var StringProto = New(nil, ObjectProto)
<|endoftext|>


<commit_before>// Copyright 2015 Hajime Hoshi
//
// 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.

// +build js

package ui

import (
	"github.com/gopherjs/gopherjs/js"
	"github.com/gopherjs/webgl"
	"github.com/hajimehoshi/ebiten/internal/opengl"
	"strconv"
)

var canvas js.Object
var context *opengl.Context

func shown() bool {
	return !js.Global.Get("document").Get("hidden").Bool()
}

func Use(f func(*opengl.Context)) {
	f(context)
}

func vsync() {
	ch := make(chan struct{})
	js.Global.Get("window").Call("requestAnimationFrame", func() {
		close(ch)
	})
	<-ch
}

func DoEvents() {
	vsync()
	for !shown() {
		vsync()
	}
}

func Terminate() {
	// Do nothing.
}

func IsClosed() bool {
	return false
}

func SwapBuffers() {
	// Do nothing.
}

func init() {
	// TODO: Implement this with node-webgl mainly for testing.

	doc := js.Global.Get("document")
	if doc.Get("body") == nil {
		ch := make(chan struct{})
		js.Global.Get("window").Call("addEventListener", "load", func() {
			close(ch)
		})
		<-ch
	}
	doc.Call("addEventListener", "keydown", func(e js.Object) bool {
		code := e.Get("keyCode").Int()
		// Backspace
		if code == 8 {
			return false
		}
		// Functions
		if 112 <= code && code <= 123 {
			return false
		}
		// Alt and arrows
		if code == 37 && code == 39 {
			// Don't need to check Alt.
			return false
		}
		return true
	})

	canvas = doc.Call("createElement", "canvas")
	canvas.Set("width", 16)
	canvas.Set("height", 16)
	doc.Get("body").Call("appendChild", canvas)

	htmlStyle := doc.Get("documentElement").Get("style")
	htmlStyle.Set("height", "100%")
	htmlStyle.Set("margin", "0")
	htmlStyle.Set("padding", "0")

	bodyStyle := doc.Get("body").Get("style")
	bodyStyle.Set("backgroundColor", "#000")
	bodyStyle.Set("position", "relative")
	bodyStyle.Set("height", "100%")
	bodyStyle.Set("margin", "0")
	bodyStyle.Set("padding", "0")
	doc.Get("body").Call("addEventListener", "click", func() {
		canvas.Call("focus")
	})

	canvasStyle := canvas.Get("style")
	canvasStyle.Set("position", "absolute")

	webglContext, err := webgl.NewContext(canvas, &webgl.ContextAttributes{
		Alpha:              true,
		PremultipliedAlpha: true,
	})
	if err != nil {
		panic(err)
	}
	context = opengl.NewContext(webglContext)

	// Make the canvas focusable.
	canvas.Call("setAttribute", "tabindex", 1)
	canvas.Get("style").Set("outline", "none")

	canvas.Call("addEventListener", "keydown", func(e js.Object) bool {
		code := e.Get("keyCode").Int()
		currentInput.keyDown(code)
		return false
	})
	canvas.Call("addEventListener", "keyup", func(e js.Object) bool {
		code := e.Get("keyCode").Int()
		currentInput.keyUp(code)
		return false
	})
	canvas.Call("addEventListener", "mousedown", func(e js.Object) bool {
		button := e.Get("button").Int()
		currentInput.mouseDown(button)
		return false
	})
	canvas.Call("addEventListener", "mouseup", func(e js.Object) bool {
		button := e.Get("button").Int()
		currentInput.mouseUp(button)
		return false
	})
	canvas.Call("addEventListener", "contextmenu", func(e js.Object) bool {
		return false
	})
}

func devicePixelRatio() int {
	// TODO: What if ratio is not an integer but a float?
	ratio := js.Global.Get("window").Get("devicePixelRatio").Int()
	if ratio == 0 {
		ratio = 1
	}
	return ratio
}

func Start(width, height, scale int, title string) (actualScale int, err error) {
	doc := js.Global.Get("document")
	doc.Set("title", title)
	// for retina
	actualScale = scale * devicePixelRatio()
	canvas.Set("width", width*actualScale)
	canvas.Set("height", height*actualScale)
	canvasStyle := canvas.Get("style")

	cssWidth := width * scale
	cssHeight := height * scale
	canvasStyle.Set("width", strconv.Itoa(cssWidth)+"px")
	canvasStyle.Set("height", strconv.Itoa(cssHeight)+"px")
	canvasStyle.Set("left", "calc(50% - "+strconv.Itoa(cssWidth/2)+"px)")
	canvasStyle.Set("top", "calc(50% - "+strconv.Itoa(cssHeight/2)+"px)")

	canvas.Call("addEventListener", "mousemove", func(e js.Object) {
		rect := canvas.Call("getBoundingClientRect")
		x, y := e.Get("clientX").Int(), e.Get("clientY").Int()
		x -= rect.Get("left").Int()
		y -= rect.Get("top").Int()
		currentInput.mouseMove(x/scale, y/scale)
	})
	canvas.Call("focus")

	return actualScale, nil
}
<commit_msg>Modify comments<commit_after>// Copyright 2015 Hajime Hoshi
//
// 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.

// +build js

package ui

import (
	"github.com/gopherjs/gopherjs/js"
	"github.com/gopherjs/webgl"
	"github.com/hajimehoshi/ebiten/internal/opengl"
	"strconv"
)

var canvas js.Object
var context *opengl.Context

func shown() bool {
	return !js.Global.Get("document").Get("hidden").Bool()
}

func Use(f func(*opengl.Context)) {
	f(context)
}

func vsync() {
	ch := make(chan struct{})
	js.Global.Get("window").Call("requestAnimationFrame", func() {
		close(ch)
	})
	<-ch
}

func DoEvents() {
	vsync()
	for !shown() {
		vsync()
	}
}

func Terminate() {
	// Do nothing.
}

func IsClosed() bool {
	return false
}

func SwapBuffers() {
	// Do nothing.
}

func init() {
	// TODO: Implement this with node-webgl mainly for testing.

	doc := js.Global.Get("document")
	if doc.Get("body") == nil {
		ch := make(chan struct{})
		js.Global.Get("window").Call("addEventListener", "load", func() {
			close(ch)
		})
		<-ch
	}
	doc.Call("addEventListener", "keydown", func(e js.Object) bool {
		code := e.Get("keyCode").Int()
		// Backspace
		if code == 8 {
			return false
		}
		// Functions
		if 112 <= code && code <= 123 {
			return false
		}
		// Alt and arrows
		if code == 37 && code == 39 {
			// Don't need to check Alt.
			return false
		}
		return true
	})

	canvas = doc.Call("createElement", "canvas")
	canvas.Set("width", 16)
	canvas.Set("height", 16)
	doc.Get("body").Call("appendChild", canvas)

	htmlStyle := doc.Get("documentElement").Get("style")
	htmlStyle.Set("height", "100%")
	htmlStyle.Set("margin", "0")
	htmlStyle.Set("padding", "0")

	bodyStyle := doc.Get("body").Get("style")
	bodyStyle.Set("backgroundColor", "#000")
	bodyStyle.Set("position", "relative")
	bodyStyle.Set("height", "100%")
	bodyStyle.Set("margin", "0")
	bodyStyle.Set("padding", "0")
	doc.Get("body").Call("addEventListener", "click", func() {
		canvas.Call("focus")
	})

	canvasStyle := canvas.Get("style")
	canvasStyle.Set("position", "absolute")

	webglContext, err := webgl.NewContext(canvas, &webgl.ContextAttributes{
		Alpha:              true,
		PremultipliedAlpha: true,
	})
	if err != nil {
		panic(err)
	}
	context = opengl.NewContext(webglContext)

	// Make the canvas focusable.
	canvas.Call("setAttribute", "tabindex", 1)
	canvas.Get("style").Set("outline", "none")

	canvas.Call("addEventListener", "keydown", func(e js.Object) bool {
		code := e.Get("keyCode").Int()
		currentInput.keyDown(code)
		return false
	})
	canvas.Call("addEventListener", "keyup", func(e js.Object) bool {
		code := e.Get("keyCode").Int()
		currentInput.keyUp(code)
		return false
	})
	canvas.Call("addEventListener", "mousedown", func(e js.Object) bool {
		button := e.Get("button").Int()
		currentInput.mouseDown(button)
		return false
	})
	canvas.Call("addEventListener", "mouseup", func(e js.Object) bool {
		button := e.Get("button").Int()
		currentInput.mouseUp(button)
		return false
	})
	canvas.Call("addEventListener", "contextmenu", func(e js.Object) bool {
		return false
	})
}

func devicePixelRatio() int {
	// TODO: What if ratio is not an integer but a float?
	ratio := js.Global.Get("window").Get("devicePixelRatio").Int()
	if ratio == 0 {
		ratio = 1
	}
	return ratio
}

func Start(width, height, scale int, title string) (actualScale int, err error) {
	doc := js.Global.Get("document")
	doc.Set("title", title)
	actualScale = scale * devicePixelRatio()
	canvas.Set("width", width*actualScale)
	canvas.Set("height", height*actualScale)
	canvasStyle := canvas.Get("style")

	cssWidth := width * scale
	cssHeight := height * scale
	canvasStyle.Set("width", strconv.Itoa(cssWidth)+"px")
	canvasStyle.Set("height", strconv.Itoa(cssHeight)+"px")
	// CSS calc requires space chars.
	canvasStyle.Set("left", "calc(50% - "+strconv.Itoa(cssWidth/2)+"px)")
	canvasStyle.Set("top", "calc(50% - "+strconv.Itoa(cssHeight/2)+"px)")

	canvas.Call("addEventListener", "mousemove", func(e js.Object) {
		rect := canvas.Call("getBoundingClientRect")
		x, y := e.Get("clientX").Int(), e.Get("clientY").Int()
		x -= rect.Get("left").Int()
		y -= rect.Get("top").Int()
		currentInput.mouseMove(x/scale, y/scale)
	})
	canvas.Call("focus")

	return actualScale, nil
}
<|endoftext|>


<commit_before>package vm

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/grubby/grubby/ast"
	"github.com/grubby/grubby/interpreter/vm/builtins"
	"github.com/grubby/grubby/parser"
)

type vm struct {
	currentFilename string
	ObjectSpace     map[string]builtins.Value
	CurrentGlobals  map[string]builtins.Value
	CurrentSymbols  map[string]builtins.Value
	CurrentClasses  map[string]builtins.Class

	stack *CallStack
}

type VM interface {
	Run(string) (builtins.Value, error)
	Get(string) (builtins.Value, error)
	MustGet(string) builtins.Value

	Set(string, builtins.Value)

	Symbols() map[string]builtins.Value
	Globals() map[string]builtins.Value
	Classes() map[string]builtins.Class
}

func NewVM(name string) VM {
	vm := &vm{
		currentFilename: name,
		stack:           NewCallStack(),
		CurrentGlobals:  make(map[string]builtins.Value),
		ObjectSpace:     make(map[string]builtins.Value),
		CurrentSymbols:  make(map[string]builtins.Value),
	}
	vm.registerClasses()

	loadPath := vm.CurrentClasses["Array"].New()
	vm.CurrentGlobals["LOAD_PATH"] = loadPath
	vm.CurrentGlobals[":"] = loadPath

	objectClass := vm.CurrentClasses["Object"]
	vm.ObjectSpace["Object"] = objectClass
	vm.ObjectSpace["Kernel"] = builtins.NewGlobalKernelClass()
	vm.ObjectSpace["File"] = builtins.NewFileClass()
	vm.ObjectSpace["ARGV"] = vm.CurrentClasses["Array"].New()

	main := objectClass.New()
	main.AddMethod(builtins.NewMethod("to_s", func(args ...builtins.Value) (builtins.Value, error) {
		return builtins.NewString("main"), nil
	}))
	main.AddMethod(builtins.NewMethod("require", func(args ...builtins.Value) (builtins.Value, error) {
		fileName := args[0].(*builtins.StringValue).String()
		if fileName == "rubygems" {
			// don't "require 'rubygems'"
			return vm.CurrentClasses["False"].New(), nil
		}

		for _, pathStr := range loadPath.(*builtins.Array).Members() {
			path := pathStr.(*builtins.StringValue)
			fullPath := filepath.Join(path.String(), fileName+".rb")
			file, err := os.Open(fullPath)
			if err != nil {
				continue
			}

			contents, err := ioutil.ReadAll(file)

			if err == nil {
				originalName := vm.currentFilename
				defer func() {
					vm.currentFilename = originalName
				}()

				vm.currentFilename = file.Name()
				_, rubyErr := vm.Run(string(contents))
				return vm.CurrentClasses["True"].New(), rubyErr
			}
		}

		errorMessage := fmt.Sprintf("LoadError: cannot load such file -- %s", fileName)
		return nil, builtins.NewLoadError(errorMessage, vm.stack.String())
	}))
	main.AddMethod(builtins.NewMethod("puts", func(args ...builtins.Value) (builtins.Value, error) {
		for _, arg := range args {
			os.Stdout.Write([]byte(arg.String() + "\n"))
		}

		return nil, nil
	}))

	vm.ObjectSpace["main"] = main

	return vm
}

func (vm *vm) registerClasses() {
	vm.CurrentClasses = map[string]builtins.Class{
		"Array":   builtins.NewArrayClass().(builtins.Class),
		"Object":  builtins.NewGlobalObjectClass(),
		"Process": builtins.NewProcessClass(),
		"True":    builtins.NewTrueClass(),
		"False":   builtins.NewFalseClass(),
		"Nil":     builtins.NewNilClass(),
	}
}

func (vm *vm) MustGet(key string) builtins.Value {
	val, ok := vm.ObjectSpace[key]
	if ok {
		return val
	}

	val, ok = vm.CurrentGlobals[key]
	if ok {
		return val
	}

	return nil
}

func (vm *vm) Get(key string) (builtins.Value, error) {
	val, ok := vm.ObjectSpace[key]
	if ok {
		return val, nil
	}

	val, ok = vm.CurrentGlobals[key]
	if ok {
		return val, nil
	}

	return nil, errors.New(fmt.Sprintf("'%s' is undefined", key))
}

func (vm *vm) Set(key string, value builtins.Value) {
	vm.ObjectSpace[key] = value
}

func (vm *vm) Symbols() map[string]builtins.Value {
	return vm.CurrentSymbols
}

func (vm *vm) Globals() map[string]builtins.Value {
	return vm.CurrentGlobals
}

func (vm *vm) Classes() map[string]builtins.Class {
	return vm.CurrentClasses
}

type ParseError struct {
	Filename string
}

func NewParseError(filename string) *ParseError {
	return &ParseError{Filename: filename}
}

func (err *ParseError) Error() string {
	return "parse error"
}

func (vm *vm) Run(input string) (builtins.Value, error) {
	lexer := parser.NewLexer(input)
	result := parser.RubyParse(lexer)
	if result != 0 {
		return nil, NewParseError(vm.currentFilename)
	}

	main := vm.ObjectSpace["main"]
	vm.stack.Unshift("main")
	defer vm.stack.Shift()

	return vm.executeWithContext(parser.Statements, main)
}

func (vm *vm) executeWithContext(statements []ast.Node, context builtins.Value) (builtins.Value, error) {
	var (
		returnValue builtins.Value
		returnErr   error
	)
	for _, statement := range statements {
		switch statement.(type) {
		case ast.IfBlock:
			truthy := false
			ifBlock := statement.(ast.IfBlock)
			switch ifBlock.Condition.(type) {
			case ast.Boolean:
				truthy = ifBlock.Condition.(ast.Boolean).Value
			case ast.BareReference:
				truthy = ifBlock.Condition.(ast.BareReference).Name == "nil"
			default:
				truthy = true
			}

			if truthy {
				returnValue, returnErr = vm.executeWithContext(ifBlock.Body, context)
			} else {
				returnValue, returnErr = vm.executeWithContext(ifBlock.Else, context)
			}
		case ast.FuncDecl:
			// FIXME: assumes for now this will only ever be at the top level
			// it seems like this should be replaced with context, but that's really context for calling methods, not
			// necessarily for defining new methods
			funcNode := statement.(ast.FuncDecl)
			method := builtins.NewMethod(funcNode.Name.Name, func(args ...builtins.Value) (builtins.Value, error) {
				return nil, nil
			})
			returnValue = method
			vm.ObjectSpace["Kernel"].AddPrivateMethod(method)
		case ast.SimpleString:
			returnValue = builtins.NewString(statement.(ast.SimpleString).Value)
		case ast.InterpolatedString:
			returnValue = builtins.NewString(statement.(ast.InterpolatedString).Value)
		case ast.Boolean:
			if statement.(ast.Boolean).Value {
				returnValue = vm.CurrentClasses["True"].New()
			} else {
				returnValue = vm.CurrentClasses["False"].New()
			}
		case ast.GlobalVariable:
			returnValue = vm.CurrentGlobals[statement.(ast.GlobalVariable).Name]
		case ast.ConstantInt:
			returnValue = builtins.NewInt(statement.(ast.ConstantInt).Value)
		case ast.ConstantFloat:
			returnValue = builtins.NewFloat(statement.(ast.ConstantFloat).Value)
		case ast.Symbol:
			name := statement.(ast.Symbol).Name
			maybe, ok := vm.CurrentSymbols[name]
			if !ok {
				returnValue = builtins.NewSymbol(name)
				vm.CurrentSymbols[name] = returnValue
			} else {
				returnValue = maybe
			}
		case ast.BareReference:
			name := statement.(ast.BareReference).Name
			maybe, ok := vm.ObjectSpace[name]
			if ok {
				returnValue = maybe
			} else {
				returnErr = builtins.NewNameError(name, context.String(), context.Class().String(), vm.stack.String())
			}
		case ast.CallExpression:
			var method builtins.Method
			callExpr := statement.(ast.CallExpression)

			var target builtins.Value
			if callExpr.Target != nil {
				target, returnErr = vm.executeWithContext(ast.Nodes{callExpr.Target}, context)
				if returnErr != nil {
					return nil, returnErr
				}
			} else {
				target = context
			}

			if target == nil {
				nilValue := vm.CurrentClasses["Nil"].New()
				return nil, builtins.NewNoMethodError(callExpr.Func.Name, nilValue.String(), nilValue.Class().String(), vm.stack.String())
			}
			method, err := target.Method(callExpr.Func.Name)

			if err != nil {
				fmt.Printf("name error with target %#v\n", target)
				err := builtins.NewNameError(callExpr.Func.Name, target.String(), target.Class().String(), vm.stack.String())
				return nil, err
			}

			args := []builtins.Value{}
			for _, astArgument := range callExpr.Args {
				arg, err := vm.executeWithContext(ast.Nodes{astArgument}, context)
				if err != nil {
					return nil, err
				}

				args = append(args, arg)
			}

			vm.stack.Unshift(method.Name())
			defer vm.stack.Shift()

			returnValue, returnErr = method.Execute(args...)
			if returnErr != nil {
				return returnValue, returnErr
			}

		case ast.Assignment:
			assignment := statement.(ast.Assignment)
			returnValue, err := vm.executeWithContext([]ast.Node{assignment.RHS}, context)
			if err != nil {
				return nil, err
			}

			switch assignment.LHS.(type) {
			case ast.BareReference:
				ref := assignment.LHS.(ast.BareReference)
				vm.ObjectSpace[ref.Name] = returnValue
			case ast.GlobalVariable:
				globalVar := assignment.LHS.(ast.GlobalVariable)
				vm.CurrentGlobals[globalVar.Name] = returnValue
			default:
				panic(fmt.Sprintf("unimplemented assignment failure: %#v", assignment.LHS))
			}

		case ast.FileNameConstReference:
			returnValue = builtins.NewString(vm.currentFilename)
		case ast.Begin:
			begin := statement.(ast.Begin)
			_, err := vm.executeWithContext(begin.Body, context)

			if err != nil {
				rubyErr := err.(builtins.Value)
				for _, rescue := range begin.Rescue {
					r := rescue.(ast.Rescue)
					if r.Exception.Class.Name == rubyErr.String() {
						_, err = vm.executeWithContext(r.Body, context)
						if err == nil {
							break
						}
					}
				}
			}

			if err != nil {
				returnErr = err
			}
		default:
			panic(fmt.Sprintf("handled unknown statement type: %T:\n\t\n => %#v\n", statement, statement))
		}
	}

	return returnValue, returnErr
}
<commit_msg>Always zero out the parser Statements in Run<commit_after>package vm

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/grubby/grubby/ast"
	"github.com/grubby/grubby/interpreter/vm/builtins"
	"github.com/grubby/grubby/parser"
)

type vm struct {
	currentFilename string
	ObjectSpace     map[string]builtins.Value
	CurrentGlobals  map[string]builtins.Value
	CurrentSymbols  map[string]builtins.Value
	CurrentClasses  map[string]builtins.Class

	stack *CallStack
}

type VM interface {
	Run(string) (builtins.Value, error)
	Get(string) (builtins.Value, error)
	MustGet(string) builtins.Value

	Set(string, builtins.Value)

	Symbols() map[string]builtins.Value
	Globals() map[string]builtins.Value
	Classes() map[string]builtins.Class
}

func NewVM(name string) VM {
	vm := &vm{
		currentFilename: name,
		stack:           NewCallStack(),
		CurrentGlobals:  make(map[string]builtins.Value),
		ObjectSpace:     make(map[string]builtins.Value),
		CurrentSymbols:  make(map[string]builtins.Value),
	}
	vm.registerClasses()

	loadPath := vm.CurrentClasses["Array"].New()
	vm.CurrentGlobals["LOAD_PATH"] = loadPath
	vm.CurrentGlobals[":"] = loadPath

	objectClass := vm.CurrentClasses["Object"]
	vm.ObjectSpace["Object"] = objectClass
	vm.ObjectSpace["Kernel"] = builtins.NewGlobalKernelClass()
	vm.ObjectSpace["File"] = builtins.NewFileClass()
	vm.ObjectSpace["ARGV"] = vm.CurrentClasses["Array"].New()

	main := objectClass.New()
	main.AddMethod(builtins.NewMethod("to_s", func(args ...builtins.Value) (builtins.Value, error) {
		return builtins.NewString("main"), nil
	}))
	main.AddMethod(builtins.NewMethod("require", func(args ...builtins.Value) (builtins.Value, error) {
		fileName := args[0].(*builtins.StringValue).String()
		if fileName == "rubygems" {
			// don't "require 'rubygems'"
			return vm.CurrentClasses["False"].New(), nil
		}

		for _, pathStr := range loadPath.(*builtins.Array).Members() {
			path := pathStr.(*builtins.StringValue)
			fullPath := filepath.Join(path.String(), fileName+".rb")
			file, err := os.Open(fullPath)
			if err != nil {
				continue
			}

			contents, err := ioutil.ReadAll(file)

			if err == nil {
				originalName := vm.currentFilename
				defer func() {
					vm.currentFilename = originalName
				}()

				vm.currentFilename = file.Name()
				_, rubyErr := vm.Run(string(contents))
				return vm.CurrentClasses["True"].New(), rubyErr
			}
		}

		errorMessage := fmt.Sprintf("LoadError: cannot load such file -- %s", fileName)
		return nil, builtins.NewLoadError(errorMessage, vm.stack.String())
	}))
	main.AddMethod(builtins.NewMethod("puts", func(args ...builtins.Value) (builtins.Value, error) {
		for _, arg := range args {
			os.Stdout.Write([]byte(arg.String() + "\n"))
		}

		return nil, nil
	}))

	vm.ObjectSpace["main"] = main

	return vm
}

func (vm *vm) registerClasses() {
	vm.CurrentClasses = map[string]builtins.Class{
		"Array":   builtins.NewArrayClass().(builtins.Class),
		"Object":  builtins.NewGlobalObjectClass(),
		"Process": builtins.NewProcessClass(),
		"True":    builtins.NewTrueClass(),
		"False":   builtins.NewFalseClass(),
		"Nil":     builtins.NewNilClass(),
	}
}

func (vm *vm) MustGet(key string) builtins.Value {
	val, ok := vm.ObjectSpace[key]
	if ok {
		return val
	}

	val, ok = vm.CurrentGlobals[key]
	if ok {
		return val
	}

	return nil
}

func (vm *vm) Get(key string) (builtins.Value, error) {
	val, ok := vm.ObjectSpace[key]
	if ok {
		return val, nil
	}

	val, ok = vm.CurrentGlobals[key]
	if ok {
		return val, nil
	}

	return nil, errors.New(fmt.Sprintf("'%s' is undefined", key))
}

func (vm *vm) Set(key string, value builtins.Value) {
	vm.ObjectSpace[key] = value
}

func (vm *vm) Symbols() map[string]builtins.Value {
	return vm.CurrentSymbols
}

func (vm *vm) Globals() map[string]builtins.Value {
	return vm.CurrentGlobals
}

func (vm *vm) Classes() map[string]builtins.Class {
	return vm.CurrentClasses
}

type ParseError struct {
	Filename string
}

func NewParseError(filename string) *ParseError {
	return &ParseError{Filename: filename}
}

func (err *ParseError) Error() string {
	return "parse error"
}

func (vm *vm) Run(input string) (builtins.Value, error) {
	parser.Statements = []ast.Node{}
	lexer := parser.NewLexer(input)
	result := parser.RubyParse(lexer)
	if result != 0 {
		return nil, NewParseError(vm.currentFilename)
	}

	main := vm.ObjectSpace["main"]
	vm.stack.Unshift("main")
	defer vm.stack.Shift()

	return vm.executeWithContext(parser.Statements, main)
}

func (vm *vm) executeWithContext(statements []ast.Node, context builtins.Value) (builtins.Value, error) {
	var (
		returnValue builtins.Value
		returnErr   error
	)
	for _, statement := range statements {
		switch statement.(type) {
		case ast.IfBlock:
			truthy := false
			ifBlock := statement.(ast.IfBlock)
			switch ifBlock.Condition.(type) {
			case ast.Boolean:
				truthy = ifBlock.Condition.(ast.Boolean).Value
			case ast.BareReference:
				truthy = ifBlock.Condition.(ast.BareReference).Name == "nil"
			default:
				truthy = true
			}

			if truthy {
				returnValue, returnErr = vm.executeWithContext(ifBlock.Body, context)
			} else {
				returnValue, returnErr = vm.executeWithContext(ifBlock.Else, context)
			}
		case ast.FuncDecl:
			// FIXME: assumes for now this will only ever be at the top level
			// it seems like this should be replaced with context, but that's really context for calling methods, not
			// necessarily for defining new methods
			funcNode := statement.(ast.FuncDecl)
			method := builtins.NewMethod(funcNode.Name.Name, func(args ...builtins.Value) (builtins.Value, error) {
				return nil, nil
			})
			returnValue = method
			vm.ObjectSpace["Kernel"].AddPrivateMethod(method)
		case ast.SimpleString:
			returnValue = builtins.NewString(statement.(ast.SimpleString).Value)
		case ast.InterpolatedString:
			returnValue = builtins.NewString(statement.(ast.InterpolatedString).Value)
		case ast.Boolean:
			if statement.(ast.Boolean).Value {
				returnValue = vm.CurrentClasses["True"].New()
			} else {
				returnValue = vm.CurrentClasses["False"].New()
			}
		case ast.GlobalVariable:
			returnValue = vm.CurrentGlobals[statement.(ast.GlobalVariable).Name]
		case ast.ConstantInt:
			returnValue = builtins.NewInt(statement.(ast.ConstantInt).Value)
		case ast.ConstantFloat:
			returnValue = builtins.NewFloat(statement.(ast.ConstantFloat).Value)
		case ast.Symbol:
			name := statement.(ast.Symbol).Name
			maybe, ok := vm.CurrentSymbols[name]
			if !ok {
				returnValue = builtins.NewSymbol(name)
				vm.CurrentSymbols[name] = returnValue
			} else {
				returnValue = maybe
			}
		case ast.BareReference:
			name := statement.(ast.BareReference).Name
			maybe, ok := vm.ObjectSpace[name]
			if ok {
				returnValue = maybe
			} else {
				returnErr = builtins.NewNameError(name, context.String(), context.Class().String(), vm.stack.String())
			}
		case ast.CallExpression:
			var method builtins.Method
			callExpr := statement.(ast.CallExpression)

			var target builtins.Value
			if callExpr.Target != nil {
				target, returnErr = vm.executeWithContext(ast.Nodes{callExpr.Target}, context)
				if returnErr != nil {
					return nil, returnErr
				}
			} else {
				target = context
			}

			if target == nil {
				nilValue := vm.CurrentClasses["Nil"].New()
				return nil, builtins.NewNoMethodError(callExpr.Func.Name, nilValue.String(), nilValue.Class().String(), vm.stack.String())
			}
			method, err := target.Method(callExpr.Func.Name)

			if err != nil {
				fmt.Printf("name error with target %#v\n", target)
				err := builtins.NewNameError(callExpr.Func.Name, target.String(), target.Class().String(), vm.stack.String())
				return nil, err
			}

			args := []builtins.Value{}
			for _, astArgument := range callExpr.Args {
				arg, err := vm.executeWithContext(ast.Nodes{astArgument}, context)
				if err != nil {
					return nil, err
				}

				args = append(args, arg)
			}

			vm.stack.Unshift(method.Name())
			defer vm.stack.Shift()

			returnValue, returnErr = method.Execute(args...)
			if returnErr != nil {
				return returnValue, returnErr
			}

		case ast.Assignment:
			assignment := statement.(ast.Assignment)
			returnValue, err := vm.executeWithContext([]ast.Node{assignment.RHS}, context)
			if err != nil {
				return nil, err
			}

			switch assignment.LHS.(type) {
			case ast.BareReference:
				ref := assignment.LHS.(ast.BareReference)
				vm.ObjectSpace[ref.Name] = returnValue
			case ast.GlobalVariable:
				globalVar := assignment.LHS.(ast.GlobalVariable)
				vm.CurrentGlobals[globalVar.Name] = returnValue
			default:
				panic(fmt.Sprintf("unimplemented assignment failure: %#v", assignment.LHS))
			}

		case ast.FileNameConstReference:
			returnValue = builtins.NewString(vm.currentFilename)
		case ast.Begin:
			begin := statement.(ast.Begin)
			_, err := vm.executeWithContext(begin.Body, context)

			if err != nil {
				rubyErr := err.(builtins.Value)
				for _, rescue := range begin.Rescue {
					r := rescue.(ast.Rescue)
					if r.Exception.Class.Name == rubyErr.String() {
						_, err = vm.executeWithContext(r.Body, context)
						if err == nil {
							break
						}
					}
				}
			}

			if err != nil {
				returnErr = err
			}
		default:
			panic(fmt.Sprintf("handled unknown statement type: %T:\n\t\n => %#v\n", statement, statement))
		}
	}

	return returnValue, returnErr
}
<|endoftext|>


<commit_before>/**
 * Copyright 2014 @ S1N1 Team.
 * name :
 * author : jarryliu
 * date : 2014-02-05 21:53
 * description :
 * history :
 */
package partner

import (
	"github.com/atnet/gof"
	"github.com/atnet/gof/web"
	"go2o/src/cache"
	"go2o/src/core/domain/interface/enum"
	"go2o/src/core/domain/interface/shopping"
	"go2o/src/core/service/dps"
	"html/template"
	"strconv"
)

func (this *orderC) setShop(ctx *web.Context,
	partnerId int, order *shopping.ValueOrder) {
	shopDr := cache.GetShopDropList(partnerId, -1)

	isNoShop := len(shopDr) == 0

	ctx.App.Template().Execute(ctx.Response,
		gof.TemplateDataMap{
			"shopDr":  template.HTML(shopDr),
			"noShop":  isNoShop,
			"orderNo": order.OrderNo,
		}, "views/partner/order/order_setup_setshop.html")
}

func (this *orderC) SetShop_post(ctx *web.Context) {
	partnerId := this.GetPartnerId(ctx)
	r, w := ctx.Request, ctx.Response
	r.ParseForm()

	shopId, err := strconv.Atoi(r.FormValue("shopId"))
	if err == nil {
		err = dps.ShoppingService.SetDeliverShop(partnerId,
			r.FormValue("order_no"), shopId)
	}
	if err != nil {
		w.Write([]byte("{result:false,message:'" + err.Error() + "'}"))
	} else {
		w.Write([]byte("{result:true}"))
	}
}

func (this *orderC) setState(ctx *web.Context,
	partnerId int, order *shopping.ValueOrder) {

	var descript string
	var button string

	switch order.Status {
	case enum.ORDER_COMPLETED:
		descript = `<span style="color:green">订单已经完成！</span>`
	case enum.ORDER_CANCEL:
		descript = `<span style="color:red">订单已经作废！</span>`
	case enum.ORDER_WAIT_CONFIRM:
		descript = "确认订单无误后，点击按钮进行下一步.."
		button = `<input class="btn" type="button" id="btn2" value="确认订单"/>`
	case enum.ORDER_CONFIRMED:
		button = `<input class="btn" type="button" id="btn2" value="开始处理订单"/>`
	case enum.ORDER_WAIT_DELIVERY:
		button = `<input class="btn" type="button" id="btn2" value="开始配送"/>`
	case enum.ORDER_WAIT_RECEIVE:
		button = `<input class="btn" type="button" id="btn2" value="确认收货"/>`
	case enum.ORDER_RECEIVED:
		if order.IsPaid == 0 {
			descript = `<span style="color:red">订单尚未付款,如果已经付款，请人工手动付款！</span>`
		} else {
			descript = "如果已收货，点击按钮完成订单"
			button = `<input class="btn" type="button" id="btn2" value="完成订单"/>`
		}
	}

	ctx.App.Template().Execute(ctx.Response,
		gof.TemplateDataMap{
			"button":   template.HTML(button),
			"descript": template.HTML(descript),
			"order_no": order.OrderNo,
		}, "views/partner/order/order_setup_setstate.html")
}
<commit_msg>refactor<commit_after>/**
 * Copyright 2014 @ S1N1 Team.
 * name :
 * author : jarryliu
 * date : 2014-02-05 21:53
 * description :
 * history :
 */
package partner

import (
	"github.com/atnet/gof"
	"github.com/atnet/gof/web"
	"go2o/src/cache"
	"go2o/src/core/domain/interface/enum"
	"go2o/src/core/domain/interface/shopping"
	"go2o/src/core/service/dps"
	"html/template"
	"strconv"
)

func (this *orderC) setShop(ctx *web.Context,
	partnerId int, order *shopping.ValueOrder) {
	shopDr := cache.GetShopDropList(partnerId, -1)

	isNoShop := len(shopDr) == 0

	ctx.App.Template().Execute(ctx.Response,
		gof.TemplateDataMap{
			"shopDr":  template.HTML(shopDr),
			"noShop":  isNoShop,
			"orderNo": order.OrderNo,
		}, "views/partner/order/order_setup_setshop.html")
}

func (this *orderC) SetShop_post(ctx *web.Context) {
	partnerId := this.GetPartnerId(ctx)
	r, w := ctx.Request, ctx.Response
	r.ParseForm()

	shopId, err := strconv.Atoi(r.FormValue("shopId"))
	if err == nil {
		err = dps.ShoppingService.SetDeliverShop(partnerId,
			r.FormValue("order_no"), shopId)
	}
	if err != nil {
		w.Write([]byte("{result:false,message:'" + err.Error() + "'}"))
	} else {
		w.Write([]byte("{result:true}"))
	}
}

func (this *orderC) setState(ctx *web.Context,
	partnerId int, order *shopping.ValueOrder) {

	var descript string
	var button string

	switch order.Status {
	case enum.ORDER_COMPLETED:
		descript = `<span style="color:green">订单已经完成！</span>`
	case enum.ORDER_CANCEL:
		descript = `<span style="color:red">订单已经作废！</span>`
	case enum.ORDER_WAIT_CONFIRM:
		descript = "确认订单无误后，点击按钮进行下一步.."
		button = `<input class="btn" type="button" id="btn2" value="确认订单"/>`
	case enum.ORDER_WAIT_DELIVERY:
		button = `<input class="btn" type="button" id="btn2" value="开始配送"/>`
	case enum.ORDER_WAIT_RECEIVE:
		button = `<input class="btn" type="button" id="btn2" value="确认收货"/>`
	case enum.ORDER_RECEIVED:
		if order.IsPaid == 0 {
			descript = `<span style="color:red">订单尚未付款,如果已经付款，请人工手动付款！</span>`
		} else {
			descript = "如果已收货，点击按钮完成订单"
			button = `<input class="btn" type="button" id="btn2" value="完成订单"/>`
		}
	}

	ctx.App.Template().Execute(ctx.Response,
		gof.TemplateDataMap{
			"button":   template.HTML(button),
			"descript": template.HTML(descript),
			"order_no": order.OrderNo,
		}, "views/partner/order/order_setup_setstate.html")
}
<|endoftext|>


<commit_before>package application

import (
	"fmt"
	"github.com/stretchr/testify/assert"
	"github.com/thoas/imaging"
	"github.com/thoas/picfit/dummy"
	"github.com/thoas/picfit/engines"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"path"
	"testing"
)

type Dimension struct {
	Width  int
	Height int
}

type TestRequest struct {
	URL        string
	Dimensions *Dimension
}

func newDummyApplication() *Application {
	app := NewApplication()
	app.SourceStorage = &dummy.DummyStorage{}
	app.DestStorage = &dummy.DummyStorage{}
	app.KVStore = &dummy.DummyKVStore{}
	app.Engine = &engines.GoImageEngine{}

	return app
}

func newHTTPServer() *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		if r.Method == "GET" {
			f, err := os.Open(path.Join("testdata", r.URL.Path))
			defer f.Close()

			if err != nil {
				w.WriteHeader(500)
			} else {
				bytes, _ := ioutil.ReadAll(f)

				w.Header().Set("Content-Length", fmt.Sprintf("%d\n\n%v", len(bytes), bytes))
				w.Write(bytes)
				w.WriteHeader(200)
			}
		}
	}))
}

func TestDummyApplication(t *testing.T) {
	ts := newHTTPServer()
	defer ts.Close()

	app := newDummyApplication()

	u, _ := url.Parse(ts.URL + "/avatar.png")

	tests := []*TestRequest{
		&TestRequest{
			URL: fmt.Sprintf("http://example.com/display?url=%s&w=50&h=50&op=resize", u.String()),
			Dimensions: &Dimension{
				Width:  50,
				Height: 50,
			},
		},
		&TestRequest{
			URL: fmt.Sprintf("http://example.com/display?url=%s&w=100&h=0&op=resize", u.String()),
			Dimensions: &Dimension{
				Width:  100,
				Height: 100,
			},
		},
		&TestRequest{
			URL: fmt.Sprintf("http://example.com/display?url=%s&w=50&h=50&op=thumbnail", u.String()),
			Dimensions: &Dimension{
				Width:  50,
				Height: 50,
			},
		},
	}

	for _, test := range tests {
		request, _ := http.NewRequest("GET", test.URL, nil)

		res := httptest.NewRecorder()

		handler := app.ServeHTTP(ImageHandler)

		handler.ServeHTTP(res, request)

		img, err := imaging.Decode(res.Body)

		assert.Nil(t, err)
		assert.Equal(t, img.Bounds().Max.X, test.Dimensions.Width)
		assert.Equal(t, img.Bounds().Max.Y, test.Dimensions.Height)
	}
}
<commit_msg>Fix unittests<commit_after>package application

import (
	"fmt"
	"github.com/stretchr/testify/assert"
	"github.com/thoas/imaging"
	"github.com/thoas/picfit/dummy"
	"github.com/thoas/picfit/engines"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"path"
	"testing"
)

type Dimension struct {
	Width  int
	Height int
}

type TestRequest struct {
	URL        string
	Dimensions *Dimension
}

func newDummyApplication() *Application {
	app := NewApplication()
	app.SourceStorage = &dummy.DummyStorage{}
	app.DestStorage = &dummy.DummyStorage{}
	app.KVStore = &dummy.DummyKVStore{}
	app.Engine = &engines.GoImageEngine{}

	return app
}

func newHTTPServer() *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		if r.Method == "GET" {
			f, err := os.Open(path.Join("testdata", r.URL.Path))
			defer f.Close()

			if err != nil {
				w.WriteHeader(500)
			} else {
				bytes, _ := ioutil.ReadAll(f)

				w.Header().Set("Content-Length", fmt.Sprintf("%d\n\n%v", len(bytes), bytes))
				w.Write(bytes)
				w.WriteHeader(200)
			}
		}
	}))
}

func TestDummyApplication(t *testing.T) {
	ts := newHTTPServer()
	defer ts.Close()

	app := newDummyApplication()

	filenames := []string{"avatar.png", "schwarzy.jpg"}

	for _, filename := range filenames {
		u, _ := url.Parse(ts.URL + "/" + filename)

		tests := []*TestRequest{
			&TestRequest{
				URL: fmt.Sprintf("http://example.com/display?url=%s&w=50&h=50&op=resize", u.String()),
				Dimensions: &Dimension{
					Width:  50,
					Height: 50,
				},
			},
			&TestRequest{
				URL: fmt.Sprintf("http://example.com/display?url=%s&w=100&h=30&op=resize", u.String()),
				Dimensions: &Dimension{
					Width:  100,
					Height: 30,
				},
			},
			&TestRequest{
				URL: fmt.Sprintf("http://example.com/display?url=%s&w=50&h=50&op=thumbnail", u.String()),
				Dimensions: &Dimension{
					Width:  50,
					Height: 50,
				},
			},
		}

		for _, test := range tests {
			request, _ := http.NewRequest("GET", test.URL, nil)

			res := httptest.NewRecorder()

			handler := app.ServeHTTP(ImageHandler)

			handler.ServeHTTP(res, request)

			img, err := imaging.Decode(res.Body)

			assert.Nil(t, err)

			if img.Bounds().Max.X != test.Dimensions.Width {
				t.Fatalf("Invalid width for %s: %d != %d", filename, img.Bounds().Max.X, test.Dimensions.Width)
			}

			if img.Bounds().Max.Y != test.Dimensions.Height {
				t.Fatalf("Invalid width for %s: %d != %d", filename, img.Bounds().Max.Y, test.Dimensions.Height)
			}
		}
	}

}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/APTrust/exchange/context"
	"github.com/APTrust/exchange/dpn/network"
	"github.com/APTrust/exchange/models"
	"github.com/APTrust/exchange/util"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"strings"
)

func main() {
	pathToConfigFile := parseCommandLine()
	config, err := models.LoadConfigFile(pathToConfigFile)
	if err != nil {
		fmt.Fprintf(os.Stderr, err.Error())
		os.Exit(1)
	}
	_context := context.NewContext(config)
	dpnClient, err := network.NewDPNRestClient(
		_context.Config.DPN.RestClient.LocalServiceURL,
		_context.Config.DPN.DPNAPIVersion,
		_context.Config.DPN.RestClient.LocalAuthToken,
		_context.Config.DPN.LocalNode,
		_context.Config.DPN)
	if err != nil {
		fmt.Fprintf(os.Stderr, err.Error())
		os.Exit(1)
	}
	deleteReplicatedBags(_context, dpnClient)
}

func deleteReplicatedBags(_context *context.Context, dpnClient *network.DPNRestClient) {
	_context.MessageLog.Info("Deleting replicated bags in %s",
		_context.Config.DPN.StagingDirectory)
	files, err := ioutil.ReadDir(_context.Config.DPN.StagingDirectory)
	if err != nil {
		_context.MessageLog.Error(err.Error())
		return
	}
	for _, finfo := range files {
		bagUUID := strings.Replace(finfo.Name(), ".tar", "", 1)
		if !util.LooksLikeUUID(bagUUID) {
			continue // Don't delete it if it's not a DPN tar file
		}
		params := url.Values{}
		params.Set("bag", bagUUID)
		params.Set("stored", "true")
		params.Set("from_node", _context.Config.DPN.LocalNode)
		resp := dpnClient.ReplicationTransferList(params)
		if resp.Error != nil {
			_context.MessageLog.Error("Error getting replication info for bag '%s': %v",
				bagUUID, resp.Error.Error())
			continue
		}
		tarfile := filepath.Join(_context.Config.DPN.StagingDirectory, finfo.Name())
		successfulReplications := resp.ReplicationTransfers()
		if len(successfulReplications) >= _context.Config.DPN.ReplicateToNumNodes {
			_context.MessageLog.Info("Deleting %s: %d successful replications",
				tarfile, len(successfulReplications))
			removeFile(_context, tarfile)
			for _, xfer := range successfulReplications {
				symlink := fmt.Sprintf("%s/dpn.%s/outbound/%s.tar",
					_context.Config.DPN.RemoteNodeHomeDirectory, xfer.ToNode, bagUUID)
				removeFile(_context, symlink)
			}
		} else {
			_context.MessageLog.Info("Leaving %s: only %d successful replications so far",
				tarfile, len(successfulReplications))
		}
	}
}

func removeFile(_context *context.Context, pathToFile string) {
	err := os.Remove(pathToFile)
	if err != nil {
		_context.MessageLog.Info("Error deleting %s: %v", pathToFile, err)
	} else {
		_context.MessageLog.Info("Deleted %s", pathToFile)
	}
}

func parseCommandLine() (configFile string) {
	flag.StringVar(&configFile, "config", "", "Path to APTrust config file")
	flag.Parse()
	if configFile == "" {
		printUsage()
		os.Exit(1)
	}
	return configFile
}

// Tell the user about the program.
func printUsage() {
	message := `
dpn_cleanup deletes tar files from the DPN staging directory and the
symlinks to those tar files from the /home/dpn.<user>/outbound directories
once the items have been replicated to the minimum number of required
nodes. The number of required nodes is defined in the config file,
under DPN.ReplicateToNumNodes.

Usage: apt_cleanup -config=<path to config file>

Param -config is required. It can be an absolute path, or a path in the format
config/env.json, where env is dev, test, demo, integration or production.

`
	fmt.Println(message)
}
<commit_msg>Commented out symlink deletion, because we're not creating them<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/APTrust/exchange/context"
	"github.com/APTrust/exchange/dpn/network"
	"github.com/APTrust/exchange/models"
	"github.com/APTrust/exchange/util"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"strings"
)

func main() {
	pathToConfigFile := parseCommandLine()
	config, err := models.LoadConfigFile(pathToConfigFile)
	if err != nil {
		fmt.Fprintf(os.Stderr, err.Error())
		os.Exit(1)
	}
	_context := context.NewContext(config)
	dpnClient, err := network.NewDPNRestClient(
		_context.Config.DPN.RestClient.LocalServiceURL,
		_context.Config.DPN.DPNAPIVersion,
		_context.Config.DPN.RestClient.LocalAuthToken,
		_context.Config.DPN.LocalNode,
		_context.Config.DPN)
	if err != nil {
		fmt.Fprintf(os.Stderr, err.Error())
		os.Exit(1)
	}
	deleteReplicatedBags(_context, dpnClient)
}

func deleteReplicatedBags(_context *context.Context, dpnClient *network.DPNRestClient) {
	_context.MessageLog.Info("Deleting replicated bags in %s",
		_context.Config.DPN.StagingDirectory)
	files, err := ioutil.ReadDir(_context.Config.DPN.StagingDirectory)
	if err != nil {
		_context.MessageLog.Error(err.Error())
		return
	}
	for _, finfo := range files {
		bagUUID := strings.Replace(finfo.Name(), ".tar", "", 1)
		if !util.LooksLikeUUID(bagUUID) {
			continue // Don't delete it if it's not a DPN tar file
		}
		params := url.Values{}
		params.Set("bag", bagUUID)
		params.Set("stored", "true")
		params.Set("from_node", _context.Config.DPN.LocalNode)
		resp := dpnClient.ReplicationTransferList(params)
		if resp.Error != nil {
			_context.MessageLog.Error("Error getting replication info for bag '%s': %v",
				bagUUID, resp.Error.Error())
			continue
		}
		tarfile := filepath.Join(_context.Config.DPN.StagingDirectory, finfo.Name())
		successfulReplications := resp.ReplicationTransfers()
		if len(successfulReplications) >= _context.Config.DPN.ReplicateToNumNodes {
			_context.MessageLog.Info("Deleting %s: %d successful replications",
				tarfile, len(successfulReplications))
			removeFile(_context, tarfile)
			// ------------------------------------------------------------------------
			// Skip this for now. We're linking the entire /home/dpn.*/outbound dirs
			// to the DPN staging dir. If there are no problems with that, delete
			// this whole block of commented code.
			// ------------------------------------------------------------------------
			// for _, xfer := range successfulReplications {
			// 	symlink := fmt.Sprintf("%s/dpn.%s/outbound/%s.tar",
			// 		_context.Config.DPN.RemoteNodeHomeDirectory, xfer.ToNode, bagUUID)
			// 	removeFile(_context, symlink)
			// }
		} else {
			_context.MessageLog.Info("Leaving %s: only %d successful replications so far",
				tarfile, len(successfulReplications))
		}
	}
}

func removeFile(_context *context.Context, pathToFile string) {
	err := os.Remove(pathToFile)
	if err != nil {
		_context.MessageLog.Info("Error deleting %s: %v", pathToFile, err)
	} else {
		_context.MessageLog.Info("Deleted %s", pathToFile)
	}
}

func parseCommandLine() (configFile string) {
	flag.StringVar(&configFile, "config", "", "Path to APTrust config file")
	flag.Parse()
	if configFile == "" {
		printUsage()
		os.Exit(1)
	}
	return configFile
}

// Tell the user about the program.
func printUsage() {
	message := `
dpn_cleanup deletes tar files from the DPN staging directory and the
symlinks to those tar files from the /home/dpn.<user>/outbound directories
once the items have been replicated to the minimum number of required
nodes. The number of required nodes is defined in the config file,
under DPN.ReplicateToNumNodes.

Usage: apt_cleanup -config=<path to config file>

Param -config is required. It can be an absolute path, or a path in the format
config/env.json, where env is dev, test, demo, integration or production.

`
	fmt.Println(message)
}
<|endoftext|>


<commit_before>// -*- tab-width: 4; -*-

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/user"
	"strings"
)

const progname = "twet"

var homedir string
var conf Config
var configpath string

var usage = fmt.Sprintf(`%s is a client for twtxt -- https://twtxt.readthedocs.org/en/stable/

Usage:
	%s command [arguments]

Commands:
	timeline
	tweet or twet

Use "%s help [command]" for more information about a command.
`, progname, progname, progname)

func main() {
	setversion()
	log.SetPrefix(fmt.Sprintf("%s: ", progname))
	log.SetFlags(0)

	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}
	homedir = usr.HomeDir

	configpath = conf.Read()

	flag.Usage = func() {
		fmt.Printf(usage)
	}
	flag.Parse()
	switch flag.Arg(0) {
	case "timeline":
		if err := timeline_command(flag.Args()[1:]); err != nil {
			log.Fatal(err)
		}
	case "tweet", "twet":
		if err := tweet_command(flag.Args()[1:]); err != nil {
			log.Fatal(err)
		}
	case "help":
		switch flag.Arg(1) {
		case "timeline":
			timeline_command([]string{"-h"})
		case "tweet", "twet":
			tweet_command([]string{"-h"})
		case "":
			flag.Usage()
			os.Exit(2)
		default:
			fmt.Fprintf(os.Stderr, "Unknown help topic %q.\n", flag.Arg(1))
			os.Exit(2)
		}
	case "version":
		fmt.Printf("%s %s\n", progname, progversion)
		if buildtimestamp != "" {
			fmt.Printf("built: %s\n", buildtimestamp)
		}
	case "":
		flag.Usage()
		os.Exit(2)
	default:
		log.Fatal(fmt.Sprintf("%q is not a valid command.\n", flag.Arg(0)))
	}
}

func setversion() {
	if gitontag != "" {
		progversion = gitontag
	} else if gitlasttag != "" {
		progversion = gitlasttag
		if gitcommit != "" {
			progversion += "+" + gitcommit
		}
	}
	progversion = strings.TrimPrefix(progversion, "v")
}

var (
	progversion    string = "v0.1.1"
	buildtimestamp string
	gitontag       string
	gitlasttag     string
	gitcommit      string
)
<commit_msg>os/user req. cgo, so no cross-compiling. just Getenv("HOME") instead<commit_after>// -*- tab-width: 4; -*-

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strings"
)

const progname = "twet"

var homedir string
var conf Config
var configpath string

var usage = fmt.Sprintf(`%s is a client for twtxt -- https://twtxt.readthedocs.org/en/stable/

Usage:
	%s command [arguments]

Commands:
	timeline
	tweet or twet

Use "%s help [command]" for more information about a command.
`, progname, progname, progname)

func main() {
	setversion()
	log.SetPrefix(fmt.Sprintf("%s: ", progname))
	log.SetFlags(0)

	if homedir = os.Getenv("HOME"); homedir == "" {
		log.Fatal("HOME env variable empty?! can't proceeed")
	}

	configpath = conf.Read()

	flag.Usage = func() {
		fmt.Printf(usage)
	}
	flag.Parse()
	switch flag.Arg(0) {
	case "timeline":
		if err := timeline_command(flag.Args()[1:]); err != nil {
			log.Fatal(err)
		}
	case "tweet", "twet":
		if err := tweet_command(flag.Args()[1:]); err != nil {
			log.Fatal(err)
		}
	case "help":
		switch flag.Arg(1) {
		case "timeline":
			timeline_command([]string{"-h"})
		case "tweet", "twet":
			tweet_command([]string{"-h"})
		case "":
			flag.Usage()
			os.Exit(2)
		default:
			fmt.Fprintf(os.Stderr, "Unknown help topic %q.\n", flag.Arg(1))
			os.Exit(2)
		}
	case "version":
		fmt.Printf("%s %s\n", progname, progversion)
		if buildtimestamp != "" {
			fmt.Printf("built: %s\n", buildtimestamp)
		}
	case "":
		flag.Usage()
		os.Exit(2)
	default:
		log.Fatal(fmt.Sprintf("%q is not a valid command.\n", flag.Arg(0)))
	}
}

func setversion() {
	if gitontag != "" {
		progversion = gitontag
	} else if gitlasttag != "" {
		progversion = gitlasttag
		if gitcommit != "" {
			progversion += "+" + gitcommit
		}
	}
	progversion = strings.TrimPrefix(progversion, "v")
}

var (
	progversion    string = "v0.1.1"
	buildtimestamp string
	gitontag       string
	gitlasttag     string
	gitcommit      string
)
<|endoftext|>