<commit_before>package features

import (
	"fmt"
	"math"
	"os"
	"testing"

	"github.com/cilium/ebpf"
	"github.com/cilium/ebpf/internal/testutils"
)

var progTypeMinVersion = map[ebpf.ProgramType]string{
	ebpf.SocketFilter:          "3.19",
	ebpf.Kprobe:                "4.1",
	ebpf.SchedCLS:              "4.1",
	ebpf.SchedACT:              "4.1",
	ebpf.TracePoint:            "4.7",
	ebpf.XDP:                   "4.8",
	ebpf.PerfEvent:             "4.9",
	ebpf.CGroupSKB:             "4.10",
	ebpf.CGroupSock:            "4.10",
	ebpf.LWTIn:                 "4.10",
	ebpf.LWTOut:                "4.10",
	ebpf.LWTXmit:               "4.10",
	ebpf.SockOps:               "4.13",
	ebpf.SkSKB:                 "4.14",
	ebpf.CGroupDevice:          "4.15",
	ebpf.SkMsg:                 "4.17",
	ebpf.RawTracepoint:         "4.17",
	ebpf.CGroupSockAddr:        "4.17",
	ebpf.LWTSeg6Local:          "4.18",
	ebpf.LircMode2:             "4.18",
	ebpf.SkReuseport:           "4.19",
	ebpf.FlowDissector:         "4.20",
	ebpf.CGroupSysctl:          "5.2",
	ebpf.RawTracepointWritable: "5.2",
	ebpf.CGroupSockopt:         "5.3",
	ebpf.Tracing:               "5.5",
	ebpf.StructOps:             "5.6",
	ebpf.Extension:             "5.6",
	ebpf.LSM:                   "5.7",
	ebpf.SkLookup:              "5.9",
}

func TestHaveProgType(t *testing.T) {
	for progType := ebpf.UnspecifiedProgram + 1; progType <= progType.Max(); progType++ {
		// Need inner loop copy to make use of t.Parallel()
		pt := progType

		minVersion, ok := progTypeMinVersion[pt]
		if !ok {
			// In cases where a new prog type wasn't added to progTypeMinVersion
			// we should make sure the test runs anyway and fails on old kernels
			minVersion = "0.0"
		}

		feature := fmt.Sprintf("program type %s", pt.String())

		t.Run(pt.String(), func(t *testing.T) {
			t.Parallel()

			if progLoadProbeNotImplemented(pt) {
				t.Skipf("Test for prog type %s requires working probe", pt.String())
			}
			testutils.SkipOnOldKernel(t, minVersion, feature)

			if err := HaveProgType(pt); err != nil {
				if pt == ebpf.LircMode2 {
					// The probe for LircMode2 technically works but needs to be skipped if unsuccessful
					// because the current testing kernels don't support it.
					testutils.SkipIfNotSupported(t, err)
				}

				t.Fatalf("Program type %s isn't supported even though kernel is at least %s: %v", pt.String(), minVersion, err)
			}
		})

	}
}

func TestHaveProgTypeUnsupported(t *testing.T) {
	if err := haveProgType(ebpf.ProgramType(math.MaxUint32)); err != ebpf.ErrNotSupported {
		t.Fatalf("Expected ebpf.ErrNotSupported but was: %v", err)
	}
}

func TestHaveProgTypeInvalid(t *testing.T) {
	if err := HaveProgType(ebpf.ProgramType(math.MaxUint32)); err != os.ErrInvalid {
		t.Fatalf("Expected os.ErrInvalid but was: %v", err)
	}
}
<commit_msg>features: prog_test - enable LircMode2 test<commit_after>package features

import (
	"fmt"
	"math"
	"os"
	"testing"

	"github.com/cilium/ebpf"
	"github.com/cilium/ebpf/internal/testutils"
)

var progTypeMinVersion = map[ebpf.ProgramType]string{
	ebpf.SocketFilter:          "3.19",
	ebpf.Kprobe:                "4.1",
	ebpf.SchedCLS:              "4.1",
	ebpf.SchedACT:              "4.1",
	ebpf.TracePoint:            "4.7",
	ebpf.XDP:                   "4.8",
	ebpf.PerfEvent:             "4.9",
	ebpf.CGroupSKB:             "4.10",
	ebpf.CGroupSock:            "4.10",
	ebpf.LWTIn:                 "4.10",
	ebpf.LWTOut:                "4.10",
	ebpf.LWTXmit:               "4.10",
	ebpf.SockOps:               "4.13",
	ebpf.SkSKB:                 "4.14",
	ebpf.CGroupDevice:          "4.15",
	ebpf.SkMsg:                 "4.17",
	ebpf.RawTracepoint:         "4.17",
	ebpf.CGroupSockAddr:        "4.17",
	ebpf.LWTSeg6Local:          "4.18",
	ebpf.LircMode2:             "4.18",
	ebpf.SkReuseport:           "4.19",
	ebpf.FlowDissector:         "4.20",
	ebpf.CGroupSysctl:          "5.2",
	ebpf.RawTracepointWritable: "5.2",
	ebpf.CGroupSockopt:         "5.3",
	ebpf.Tracing:               "5.5",
	ebpf.StructOps:             "5.6",
	ebpf.Extension:             "5.6",
	ebpf.LSM:                   "5.7",
	ebpf.SkLookup:              "5.9",
}

func TestHaveProgType(t *testing.T) {
	for progType := ebpf.UnspecifiedProgram + 1; progType <= progType.Max(); progType++ {
		// Need inner loop copy to make use of t.Parallel()
		pt := progType

		minVersion, ok := progTypeMinVersion[pt]
		if !ok {
			// In cases where a new prog type wasn't added to progTypeMinVersion
			// we should make sure the test runs anyway and fails on old kernels
			minVersion = "0.0"
		}

		feature := fmt.Sprintf("program type %s", pt.String())

		t.Run(pt.String(), func(t *testing.T) {
			t.Parallel()

			if progLoadProbeNotImplemented(pt) {
				t.Skipf("Test for prog type %s requires working probe", pt.String())
			}
			testutils.SkipOnOldKernel(t, minVersion, feature)

			if err := HaveProgType(pt); err != nil {
				t.Fatalf("Program type %s isn't supported even though kernel is at least %s: %v", pt.String(), minVersion, err)
			}
		})

	}
}

func TestHaveProgTypeUnsupported(t *testing.T) {
	if err := haveProgType(ebpf.ProgramType(math.MaxUint32)); err != ebpf.ErrNotSupported {
		t.Fatalf("Expected ebpf.ErrNotSupported but was: %v", err)
	}
}

func TestHaveProgTypeInvalid(t *testing.T) {
	if err := HaveProgType(ebpf.ProgramType(math.MaxUint32)); err != os.ErrInvalid {
		t.Fatalf("Expected os.ErrInvalid but was: %v", err)
	}
}
<|endoftext|>


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

package controller

import (
	"testing"

	"github.com/google/shenzhen-go/model"
	"github.com/google/shenzhen-go/model/parts"
	"github.com/google/shenzhen-go/model/pin"
	pb "github.com/google/shenzhen-go/proto"
	"golang.org/x/net/context"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

func code(err error) codes.Code {
	if err == nil {
		return codes.OK
	}
	if st, ok := status.FromError(err); ok {
		return st.Code()
	}
	return codes.Unknown
}

func TestLookupGraph(t *testing.T) {
	foo := &model.Graph{Name: "foo"}
	bar := &model.Graph{Name: "bar"}
	c := &controller{
		loadedGraphs: map[string]*model.Graph{
			"foo": foo,
			"bar": bar,
		},
	}
	tests := []struct {
		key  string
		g    *model.Graph
		code codes.Code
	}{
		{"foo", foo, codes.OK},
		{"bar", bar, codes.OK},
		{"baz", nil, codes.NotFound},
	}
	for _, test := range tests {
		g, err := c.lookupGraph(test.key)
		if got, want := g, test.g; got != want {
			t.Errorf("c.lookupGraph(%q) = %v, want %v", test.key, got, want)
		}
		if got, want := code(err), test.code; got != want {
			t.Errorf("c.lookupGraph(%q) = %v, want %v", test.key, got, want)
		}
	}
}

func TestLookupNode(t *testing.T) {
	bar := &model.Node{Name: "bar"}
	foo := &model.Graph{
		Name:  "foo",
		Nodes: map[string]*model.Node{"bar": bar},
	}
	c := &controller{
		loadedGraphs: map[string]*model.Graph{"foo": foo},
	}
	tests := []struct {
		gk, nk string
		g      *model.Graph
		n      *model.Node
		code   codes.Code
	}{
		{"foo", "bar", foo, bar, codes.OK},
		{"foo", "baz", nil, nil, codes.NotFound},
		{"baz", "bar", nil, nil, codes.NotFound},
	}
	for _, test := range tests {
		g, n, err := c.lookupNode(test.gk, test.nk)
		if got, want := g, test.g; got != want {
			t.Errorf("c.lookupNode(%q, %q) = graph %v, want %v", test.gk, test.nk, got, want)
		}
		if got, want := n, test.n; got != want {
			t.Errorf("c.lookupNode(%q, %q) = node %v, want %v", test.gk, test.nk, got, want)
		}
		if got, want := code(err), test.code; got != want {
			t.Errorf("c.lookupNode(%q, %q) = code %v, want %v", test.gk, test.nk, got, want)
		}
	}
}

func TestLookupChannel(t *testing.T) {
	bar := &model.Channel{Name: "bar"}
	foo := &model.Graph{
		Name:     "foo",
		Channels: map[string]*model.Channel{"bar": bar},
	}
	c := &controller{
		loadedGraphs: map[string]*model.Graph{"foo": foo},
	}
	tests := []struct {
		gk, ck string
		g      *model.Graph
		ch     *model.Channel
		code   codes.Code
	}{
		{"foo", "bar", foo, bar, codes.OK},
		{"foo", "baz", nil, nil, codes.NotFound},
		{"baz", "bar", nil, nil, codes.NotFound},
	}
	for _, test := range tests {
		g, ch, err := c.lookupChannel(test.gk, test.ck)
		if got, want := g, test.g; got != want {
			t.Errorf("c.lookupChannel(%q, %q) = graph %v, want %v", test.gk, test.ck, got, want)
		}
		if got, want := ch, test.ch; got != want {
			t.Errorf("c.lookupChannel(%q, %q) = node %v, want %v", test.gk, test.ck, got, want)
		}
		if got, want := code(err), test.code; got != want {
			t.Errorf("c.lookupChannel(%q, %q) = code %v, want %v", test.gk, test.ck, got, want)
		}
	}
}

func TestCreateChannel(t *testing.T) {
	bar := &model.Channel{Name: "bar"}
	foo := &model.Graph{
		Name:     "foo",
		Channels: map[string]*model.Channel{"bar": bar},
	}
	c := &controller{
		loadedGraphs: map[string]*model.Graph{"foo": foo},
	}
	tests := []struct {
		req  *pb.CreateChannelRequest
		code codes.Code
	}{
		{
			req: &pb.CreateChannelRequest{
				Graph: "nope",
				Name:  "baz",
			},
			code: codes.NotFound,
		},
		{
			req: &pb.CreateChannelRequest{
				Graph: "foo",
				Name:  "baz",
			},
			code: codes.OK,
		},
		{
			req: &pb.CreateChannelRequest{
				Graph: "foo",
				Name:  "bar",
			},
			code: codes.FailedPrecondition,
		},
	}
	for _, test := range tests {
		_, err := c.CreateChannel(context.Background(), test.req)
		if got, want := code(err), test.code; got != want {
			t.Errorf("c.CreateChannel(%v) = code %v, want %v", test.req, got, want)
		}
	}
	_, got := foo.Channels["baz"]
	if want := true; got != want {
		t.Errorf("foo.Channels[baz] is missing, want present")
	}
}

func TestConnectPin(t *testing.T) {
	baz := &model.Node{
		Name: "baz",
		Part: parts.NewCode(nil, "", "", "", pin.Map{
			"qux": &pin.Definition{
				Type: "int",
			},
		}),
		Connections: make(map[string]string),
	}
	bar := &model.Channel{Name: "bar", Type: "int"}
	tuz := &model.Channel{Name: "tuz", Type: "string"}
	foo := &model.Graph{
		Name:     "foo",
		Channels: map[string]*model.Channel{"bar": bar, "tuz": tuz},
		Nodes:    map[string]*model.Node{"baz": baz},
	}
	c := &controller{
		loadedGraphs: map[string]*model.Graph{"foo": foo},
	}
	tests := []struct {
		req  *pb.ConnectPinRequest
		code codes.Code
	}{
		{
			// no such graph
			req: &pb.ConnectPinRequest{
				Graph:   "nope",
				Node:    "baz",
				Channel: "bar",
				Pin:     "qux",
			},
			code: codes.NotFound,
		},
		{
			// no such node
			req: &pb.ConnectPinRequest{
				Graph:   "foo",
				Node:    "barz",
				Channel: "bar",
				Pin:     "qux",
			},
			code: codes.NotFound,
		},
		{
			// no such channel
			req: &pb.ConnectPinRequest{
				Graph:   "foo",
				Node:    "baz",
				Channel: "buz",
				Pin:     "qux",
			},
			code: codes.NotFound,
		},
		{
			// No such pin
			req: &pb.ConnectPinRequest{
				Graph:   "foo",
				Node:    "baz",
				Channel: "bar",
				Pin:     "wux",
			},
			code: codes.NotFound,
		},
		{
			// type mismatch
			req: &pb.ConnectPinRequest{
				Graph:   "foo",
				Node:    "baz",
				Channel: "tuz",
				Pin:     "qux",
			},
			code: codes.FailedPrecondition,
		},
		{
			// It works
			req: &pb.ConnectPinRequest{
				Graph:   "foo",
				Node:    "baz",
				Channel: "bar",
				Pin:     "qux",
			},
			code: codes.OK,
		},
	}
	for _, test := range tests {
		_, err := c.ConnectPin(context.Background(), test.req)
		if got, want := code(err), test.code; got != want {
			t.Errorf("c.ConnectPin(%v) = code %v, want %v", test.req, got, want)
		}
	}
	if got, want := baz.Connections["qux"], "bar"; got != want {
		t.Errorf("baz.Connections[qux] = %q, want %q", got, want)
	}
}

func TestDeleteChannel(t *testing.T) {
	// TODO
}

func TestDisconnectPin(t *testing.T) {
	// TODO
}

func TestSave(t *testing.T) {
	// TODO
}

func TestSetGraphProperties(t *testing.T) {
	// TODO
}

func TestSetNodeProperties(t *testing.T) {
	// TODO
}

func TestSetPosition(t *testing.T) {
	// TODO
}
<commit_msg>Add DeleteChannel test<commit_after>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package controller

import (
	"testing"

	"github.com/google/shenzhen-go/model"
	"github.com/google/shenzhen-go/model/parts"
	"github.com/google/shenzhen-go/model/pin"
	pb "github.com/google/shenzhen-go/proto"
	"golang.org/x/net/context"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

func code(err error) codes.Code {
	if err == nil {
		return codes.OK
	}
	if st, ok := status.FromError(err); ok {
		return st.Code()
	}
	return codes.Unknown
}

func TestLookupGraph(t *testing.T) {
	foo := &model.Graph{Name: "foo"}
	bar := &model.Graph{Name: "bar"}
	c := &controller{
		loadedGraphs: map[string]*model.Graph{
			"foo": foo,
			"bar": bar,
		},
	}
	tests := []struct {
		key  string
		g    *model.Graph
		code codes.Code
	}{
		{"foo", foo, codes.OK},
		{"bar", bar, codes.OK},
		{"baz", nil, codes.NotFound},
	}
	for _, test := range tests {
		g, err := c.lookupGraph(test.key)
		if got, want := g, test.g; got != want {
			t.Errorf("c.lookupGraph(%q) = %v, want %v", test.key, got, want)
		}
		if got, want := code(err), test.code; got != want {
			t.Errorf("c.lookupGraph(%q) = %v, want %v", test.key, got, want)
		}
	}
}

func TestLookupNode(t *testing.T) {
	bar := &model.Node{Name: "bar"}
	foo := &model.Graph{
		Name:  "foo",
		Nodes: map[string]*model.Node{"bar": bar},
	}
	c := &controller{
		loadedGraphs: map[string]*model.Graph{"foo": foo},
	}
	tests := []struct {
		gk, nk string
		g      *model.Graph
		n      *model.Node
		code   codes.Code
	}{
		{"foo", "bar", foo, bar, codes.OK},
		{"foo", "baz", nil, nil, codes.NotFound},
		{"baz", "bar", nil, nil, codes.NotFound},
	}
	for _, test := range tests {
		g, n, err := c.lookupNode(test.gk, test.nk)
		if got, want := g, test.g; got != want {
			t.Errorf("c.lookupNode(%q, %q) = graph %v, want %v", test.gk, test.nk, got, want)
		}
		if got, want := n, test.n; got != want {
			t.Errorf("c.lookupNode(%q, %q) = node %v, want %v", test.gk, test.nk, got, want)
		}
		if got, want := code(err), test.code; got != want {
			t.Errorf("c.lookupNode(%q, %q) = code %v, want %v", test.gk, test.nk, got, want)
		}
	}
}

func TestLookupChannel(t *testing.T) {
	bar := &model.Channel{Name: "bar"}
	foo := &model.Graph{
		Name:     "foo",
		Channels: map[string]*model.Channel{"bar": bar},
	}
	c := &controller{
		loadedGraphs: map[string]*model.Graph{"foo": foo},
	}
	tests := []struct {
		gk, ck string
		g      *model.Graph
		ch     *model.Channel
		code   codes.Code
	}{
		{"foo", "bar", foo, bar, codes.OK},
		{"foo", "baz", nil, nil, codes.NotFound},
		{"baz", "bar", nil, nil, codes.NotFound},
	}
	for _, test := range tests {
		g, ch, err := c.lookupChannel(test.gk, test.ck)
		if got, want := g, test.g; got != want {
			t.Errorf("c.lookupChannel(%q, %q) = graph %v, want %v", test.gk, test.ck, got, want)
		}
		if got, want := ch, test.ch; got != want {
			t.Errorf("c.lookupChannel(%q, %q) = node %v, want %v", test.gk, test.ck, got, want)
		}
		if got, want := code(err), test.code; got != want {
			t.Errorf("c.lookupChannel(%q, %q) = code %v, want %v", test.gk, test.ck, got, want)
		}
	}
}

func TestCreateChannel(t *testing.T) {
	bar := &model.Channel{Name: "bar"}
	foo := &model.Graph{
		Name:     "foo",
		Channels: map[string]*model.Channel{"bar": bar},
	}
	c := &controller{
		loadedGraphs: map[string]*model.Graph{"foo": foo},
	}
	tests := []struct {
		req  *pb.CreateChannelRequest
		code codes.Code
	}{
		{
			req: &pb.CreateChannelRequest{
				Graph: "nope",
				Name:  "baz",
			},
			code: codes.NotFound,
		},
		{
			req: &pb.CreateChannelRequest{
				Graph: "foo",
				Name:  "baz",
			},
			code: codes.OK,
		},
		{
			req: &pb.CreateChannelRequest{
				Graph: "foo",
				Name:  "bar",
			},
			code: codes.FailedPrecondition,
		},
	}
	for _, test := range tests {
		_, err := c.CreateChannel(context.Background(), test.req)
		if got, want := code(err), test.code; got != want {
			t.Errorf("c.CreateChannel(%v) = code %v, want %v", test.req, got, want)
		}
	}
	_, got := foo.Channels["baz"]
	if want := true; got != want {
		t.Errorf("foo.Channels[baz] is missing, want present")
	}
}

func TestConnectPin(t *testing.T) {
	baz := &model.Node{
		Name: "baz",
		Part: parts.NewCode(nil, "", "", "", pin.Map{
			"qux": &pin.Definition{
				Type: "int",
			},
		}),
		Connections: make(map[string]string),
	}
	bar := &model.Channel{Name: "bar", Type: "int"}
	tuz := &model.Channel{Name: "tuz", Type: "string"}
	foo := &model.Graph{
		Name:     "foo",
		Channels: map[string]*model.Channel{"bar": bar, "tuz": tuz},
		Nodes:    map[string]*model.Node{"baz": baz},
	}
	c := &controller{
		loadedGraphs: map[string]*model.Graph{"foo": foo},
	}
	tests := []struct {
		req  *pb.ConnectPinRequest
		code codes.Code
	}{
		{
			// no such graph
			req: &pb.ConnectPinRequest{
				Graph:   "nope",
				Node:    "baz",
				Channel: "bar",
				Pin:     "qux",
			},
			code: codes.NotFound,
		},
		{
			// no such node
			req: &pb.ConnectPinRequest{
				Graph:   "foo",
				Node:    "barz",
				Channel: "bar",
				Pin:     "qux",
			},
			code: codes.NotFound,
		},
		{
			// no such channel
			req: &pb.ConnectPinRequest{
				Graph:   "foo",
				Node:    "baz",
				Channel: "buz",
				Pin:     "qux",
			},
			code: codes.NotFound,
		},
		{
			// No such pin
			req: &pb.ConnectPinRequest{
				Graph:   "foo",
				Node:    "baz",
				Channel: "bar",
				Pin:     "wux",
			},
			code: codes.NotFound,
		},
		{
			// type mismatch
			req: &pb.ConnectPinRequest{
				Graph:   "foo",
				Node:    "baz",
				Channel: "tuz",
				Pin:     "qux",
			},
			code: codes.FailedPrecondition,
		},
		{
			// It works
			req: &pb.ConnectPinRequest{
				Graph:   "foo",
				Node:    "baz",
				Channel: "bar",
				Pin:     "qux",
			},
			code: codes.OK,
		},
	}
	for _, test := range tests {
		_, err := c.ConnectPin(context.Background(), test.req)
		if got, want := code(err), test.code; got != want {
			t.Errorf("c.ConnectPin(%v) = code %v, want %v", test.req, got, want)
		}
	}
	if got, want := baz.Connections["qux"], "bar"; got != want {
		t.Errorf("baz.Connections[qux] = %q, want %q", got, want)
	}
}

func TestDeleteChannel(t *testing.T) {
	baz := &model.Node{
		Name: "baz",
		Part: parts.NewCode(nil, "", "", "", pin.Map{
			"qux": &pin.Definition{
				Type: "int",
			},
		}),
		Connections: map[string]string{
			"qux": "bar",
		},
	}
	bar := &model.Channel{Name: "bar", Type: "int"}
	foo := &model.Graph{
		Name:     "foo",
		Channels: map[string]*model.Channel{"bar": bar},
		Nodes:    map[string]*model.Node{"baz": baz},
	}
	c := &controller{
		loadedGraphs: map[string]*model.Graph{"foo": foo},
	}
	tests := []struct {
		req  *pb.DeleteChannelRequest
		code codes.Code
	}{
		{ // No such graph
			req: &pb.DeleteChannelRequest{
				Graph:   "nope",
				Channel: "bar",
			},
			code: codes.NotFound,
		},
		{ // No such channel
			req: &pb.DeleteChannelRequest{
				Graph:   "foo",
				Channel: "baz",
			},
			code: codes.NotFound,
		},
		{ // Ok
			req: &pb.DeleteChannelRequest{
				Graph:   "foo",
				Channel: "bar",
			},
			code: codes.OK,
		},
	}
	for _, test := range tests {
		_, err := c.DeleteChannel(context.Background(), test.req)
		if got, want := code(err), test.code; got != want {
			t.Errorf("c.DeleteChannel(%v) = code %v, want %v", test.req, got, want)
		}
	}
	// Channel should be gone
	if _, found := foo.Channels["bar"]; found {
		t.Error("channel 'bar' still exists in graph")
	}
	// Reference from node should be gone
	if got, want := baz.Connections["qux"], "nil"; got != want {
		t.Errorf("baz.Connections[qux] = %q, want %q", got, want)
	}
}

func TestDisconnectPin(t *testing.T) {
	// TODO
}

func TestSave(t *testing.T) {
	// TODO
}

func TestSetGraphProperties(t *testing.T) {
	// TODO
}

func TestSetNodeProperties(t *testing.T) {
	// TODO
}

func TestSetPosition(t *testing.T) {
	// TODO
}
<|endoftext|>


<commit_before><commit_msg>	new file:   main.go<commit_after><|endoftext|>


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

package gosnmp

import (
	"fmt"
	"reflect"
	"testing"
)

var _ = fmt.Sprintf("dummy") // dummy

// Tests in alphabetical order of function being tested

// -----------------------------------------------------------------------------

var testsMarshalLength = []struct {
	length   int
	expected []byte
}{
	{1, []byte{0x01}},
	{129, []byte{0x81, 0x81}},
}

func TestMarshalLength(t *testing.T) {
	for i, test := range testsMarshalLength {
		testBytes, err := marshalLength(test.length)
		if err != nil {
			t.Errorf("%d: length %d got err %v", i, test.length, err)
		}
		if !reflect.DeepEqual(testBytes, test.expected) {
			t.Errorf("%d: length %d got |%x| expected |%x|",
				i, test.length, testBytes, test.expected)
		}
	}
}

// -----------------------------------------------------------------------------

var testsPartition = []struct {
	currentPosition int
	partitionSize   int
	sliceLength      int
	ok               bool
}{
	{-1, 3, 8, false}, // test out of range
	{8, 3, 8, false},  // test out of range
	{0, 3, 8, false},  // test 0-7/3 per doco
	{1, 3, 8, false},
	{2, 3, 8, true},
	{3, 3, 8, false},
	{4, 3, 8, false},
	{5, 3, 8, true},
	{6, 3, 8, false},
	{7, 3, 8, true},
	{-1, 1, 3, false}, // partition size of one
	{0, 1, 3, true},
	{1, 1, 3, true},
	{2, 1, 3, true},
	{3, 1, 3, false},
}

func TestPartition(t *testing.T) {
	for i, test := range testsPartition {
		ok := Partition(test.currentPosition, test.partitionSize, test.sliceLength)
		if ok != test.ok {
			t.Errorf("#%d: Bad result: %v (expected %v)", i, ok, test.ok)
		}
	}
}

// ---------------------------------------------------------------------

var testsSnmpVersionString = []struct {
	in  SnmpVersion
	out string
}{
	{Version1, "1"},
	{Version2c, "2c"},
}

func TestSnmpVersionString(t *testing.T) {
	for i, test := range testsSnmpVersionString {
		result := test.in.String()
		if result != test.out {
			t.Errorf("#%d, got %v expected %v", i, result, test.out)
		}
	}
}

// ---------------------------------------------------------------------
<commit_msg>go fmt misc_test.go<commit_after>// Copyright 2012-2014 The GoSNMP Authors. All rights reserved.  Use of this
// source code is governed by a BSD-style license that can be found in the
// LICENSE file.

package gosnmp

import (
	"fmt"
	"reflect"
	"testing"
)

var _ = fmt.Sprintf("dummy") // dummy

// Tests in alphabetical order of function being tested

// -----------------------------------------------------------------------------

var testsMarshalLength = []struct {
	length   int
	expected []byte
}{
	{1, []byte{0x01}},
	{129, []byte{0x81, 0x81}},
}

func TestMarshalLength(t *testing.T) {
	for i, test := range testsMarshalLength {
		testBytes, err := marshalLength(test.length)
		if err != nil {
			t.Errorf("%d: length %d got err %v", i, test.length, err)
		}
		if !reflect.DeepEqual(testBytes, test.expected) {
			t.Errorf("%d: length %d got |%x| expected |%x|",
				i, test.length, testBytes, test.expected)
		}
	}
}

// -----------------------------------------------------------------------------

var testsPartition = []struct {
	currentPosition int
	partitionSize   int
	sliceLength     int
	ok              bool
}{
	{-1, 3, 8, false}, // test out of range
	{8, 3, 8, false},  // test out of range
	{0, 3, 8, false},  // test 0-7/3 per doco
	{1, 3, 8, false},
	{2, 3, 8, true},
	{3, 3, 8, false},
	{4, 3, 8, false},
	{5, 3, 8, true},
	{6, 3, 8, false},
	{7, 3, 8, true},
	{-1, 1, 3, false}, // partition size of one
	{0, 1, 3, true},
	{1, 1, 3, true},
	{2, 1, 3, true},
	{3, 1, 3, false},
}

func TestPartition(t *testing.T) {
	for i, test := range testsPartition {
		ok := Partition(test.currentPosition, test.partitionSize, test.sliceLength)
		if ok != test.ok {
			t.Errorf("#%d: Bad result: %v (expected %v)", i, ok, test.ok)
		}
	}
}

// ---------------------------------------------------------------------

var testsSnmpVersionString = []struct {
	in  SnmpVersion
	out string
}{
	{Version1, "1"},
	{Version2c, "2c"},
}

func TestSnmpVersionString(t *testing.T) {
	for i, test := range testsSnmpVersionString {
		result := test.in.String()
		if result != test.out {
			t.Errorf("#%d, got %v expected %v", i, result, test.out)
		}
	}
}

// ---------------------------------------------------------------------
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"regexp"
)

func GetLazarusPath() string {
	envFile := szPath + "/etc/lazarus/environmentoptions.xml"
	if checkFileExists(envFile) {
		bs, err := ioutil.ReadFile(envFile)
		if err == nil {
			reg := regexp.MustCompile(`\<LazarusDirectory Value\=\"(.+?)"\>`)
			matchs := reg.FindSubmatch(bs)
			if len(matchs) >= 2 {
				lazarusDir = string(matchs[1])
			}
		}
	}
	return ""
}
<commit_msg>fix LazarusPatch/lz_linux.go<commit_after>package main

import (
	"io/ioutil"
	"regexp"
)

func GetLazarusPath() string {
	envFile := "/etc/lazarus/environmentoptions.xml"
	if checkFileExists(envFile) {
		bs, err := ioutil.ReadFile(envFile)
		if err == nil {
			reg := regexp.MustCompile(`\<LazarusDirectory Value\=\"(.+?)"\>`)
			matchs := reg.FindSubmatch(bs)
			if len(matchs) >= 2 {
				return string(matchs[1])
			}
		}
	}
	return ""
}
<|endoftext|>


<commit_before>package stacksmith

import (
	"net/http"

	"github.com/dghubble/sling"
)

// DiscoveryService ...
type DiscoveryService struct {
	sling *sling.Sling
}

func newDiscoveryService(sling *sling.Sling) *DiscoveryService {
	return &DiscoveryService{
		sling: sling,
	}
}

// ListItem ...
type ListItem struct {
	Items []Item `json:"items"`
}

// Item ...
type Item struct {
	ID       string `json:"id"`
	Name     string `json:"name"`
	Category string `json:"category"`
	Versions []struct {
		Version     string `json:"version"`
		Revision    int    `json:"revision"`
		Branch      string `json:"branch"`
		Checksum    string `json:"checksum"`
		PublishedAt string `json:"publised_at"`
	} `json:"versions"`
	Prebuilt      bool `json:"prebuilt"`
	ReleaseSeries []struct {
		Version string `json:"version"`
		Payload string `json:"payload"`
	} `json:"release_series"`
	DependenciesURL string `json:"dependencies_url"`
}

// Query ...
type Query struct {
	Query string `url:"query,omitempty"`
}

// ComponentsList ...
func (s *DiscoveryService) ComponentsList(query string) (*ListItem, *http.Response, error) {
	return getDiscovery(s, "components", query)
}

// ServicesList ...
func (s *DiscoveryService) ServicesList(query string) (*ListItem, *http.Response, error) {
	return getDiscovery(s, "services", query)
}

// RuntimesList ...
func (s *DiscoveryService) RuntimesList(query string) (*ListItem, *http.Response, error) {
	return getDiscovery(s, "runtimes", query)
}

// OsesList ...
func (s *DiscoveryService) OsesList(query string) (*ListItem, *http.Response, error) {
	return getDiscovery(s, "oses", query)
}

func getDiscovery(s *DiscoveryService, path string, query string) (*ListItem, *http.Response, error) {
	componentList := new(ListItem)
	apiError := new(APIError)
	resp, err := s.sling.New().Get(path).QueryStruct(Query{Query: query}).Receive(componentList, apiError)
	return componentList, resp, relevantError(err, *apiError)
}
<commit_msg>Add all the methods that lacked in the Discovery section<commit_after>package stacksmith

import (
	"fmt"
	"net/http"

	"github.com/dghubble/sling"
)

// DiscoveryService ...
type DiscoveryService struct {
	sling *sling.Sling
}

func newDiscoveryService(sling *sling.Sling) *DiscoveryService {
	return &DiscoveryService{
		sling: sling,
	}
}

// ListItem ...
type ListItem struct {
	Items []Item `json:"items"`
}

// Item ...
type Item struct {
	ID       string `json:"id"`
	Name     string `json:"name"`
	Category string `json:"category"`
	Versions []struct {
		Version     string `json:"version"`
		Revision    int    `json:"revision"`
		Branch      string `json:"branch"`
		Checksum    string `json:"checksum"`
		PublishedAt string `json:"publised_at"`
	} `json:"versions"`
	Prebuilt      bool `json:"prebuilt"`
	ReleaseSeries []struct {
		Version string `json:"version"`
		Payload string `json:"payload"`
	} `json:"release_series"`
	DependenciesURL string `json:"dependencies_url"`
}

// Kinds ...
type Kinds struct {
	TotalEntries int `json:"total_entries"`
	TotalPages   int `json:"total_pages"`
	Items        []struct {
		ID           string `json:"id"`
		Name         string `json:"name"`
		Description  string `json:"description"`
		Default      bool   `json:"default"`
		ComponentURL string `json:"component_url"`
	} `json:"items"`
}

// Changelog ...
type Changelog struct {
	TotalEntries int `json:"total_entries"`
	TotalPAges   int `json:"total_pages"`
	Items        []struct {
		Version         string `json:"version"`
		Revision        int    `json:"revision"`
		Branch          string `json:"branch"`
		Checksum        string `json:"checksum"`
		PublishedAt     string `json:"published_at"`
		ReleaseNotes    string `json:"release_notes"`
		ReleaseNotesURL string `json:"release_notes_url"`
	} `json:"items"`
}

// Dependencies ...
type Dependencies struct {
	TotalEntries int      `json:"total_entries"`
	TotalPages   int      `json:"total_pages"`
	Items        []string `json:"items"`
}

// Query ...
type Query struct {
	Query string `url:"query,omitempty"`
}

// RangeParams ...
type RangeParams struct {
	From string `url:"from,omitempty"`
	To   string `url:"to,omitempty"`
}

// ComponentsList ...
func (s *DiscoveryService) ComponentsList(query string) (*ListItem, *http.Response, error) {
	return getDiscovery(s, "components", query)
}

// GetComponent ...
func (s *DiscoveryService) GetComponent(componentName string) (*Item, *http.Response, error) {
	component := new(Item)
	apiError := new(APIError)
	path := fmt.Sprintf("components/%s", componentName)
	resp, err := s.sling.New().Get(path).Receive(component, apiError)
	return component, resp, relevantError(err, *apiError)
}

// GetChangelogFrom ...
func (s *DiscoveryService) GetChangelogFrom(componentName string,
	rangeParam *RangeParams, pageParam *PaginationParams) (*Changelog, *http.Response, error) {
	changelog := new(Changelog)
	apiError := new(APIError)
	path := fmt.Sprintf("components/%s/changelog", componentName)
	resp, err := s.sling.New().Get(path).QueryStruct(rangeParam).QueryStruct(pageParam).Receive(changelog, apiError)
	return changelog, resp, relevantError(err, *apiError)
}

// GetDependenciesFrom ...
func (s *DiscoveryService) GetDependenciesFrom(componentName string) (*Dependencies, *http.Response, error) {
	dependencies := new(Dependencies)
	apiError := new(APIError)
	path := fmt.Sprintf("components/%s/dependencies", componentName)
	resp, err := s.sling.New().Get(path).Receive(dependencies, apiError)
	return dependencies, resp, relevantError(err, *apiError)
}

// ServicesList ...
func (s *DiscoveryService) ServicesList(query string) (*ListItem, *http.Response, error) {
	return getDiscovery(s, "services", query)
}

// RuntimesList ...
func (s *DiscoveryService) RuntimesList(query string) (*ListItem, *http.Response, error) {
	return getDiscovery(s, "runtimes", query)
}

// OsesList ...
func (s *DiscoveryService) OsesList(query string) (*ListItem, *http.Response, error) {
	return getDiscovery(s, "oses", query)
}

// KindsList ...
func (s *DiscoveryService) KindsList(pageParams *PaginationParams) (*Kinds, *http.Response, error) {
	kinds := new(Kinds)
	apiError := new(APIError)
	resp, err := s.sling.New().Get("kinds").QueryStruct(pageParams).Receive(kinds, apiError)
	return kinds, resp, relevantError(err, *apiError)
}

// GetKindsFrom ...
func (s *DiscoveryService) GetKindsFrom(componentName string,
	pageParams *PaginationParams) (*Kinds, *http.Response, error) {
	kinds := new(Kinds)
	apiError := new(APIError)
	path := fmt.Sprintf("components/%s/kinds", componentName)
	resp, err := s.sling.New().Get(path).Receive(kinds, apiError)
	return kinds, resp, relevantError(err, *apiError)
}

func getDiscovery(s *DiscoveryService, path string, query string) (*ListItem, *http.Response, error) {
	componentList := new(ListItem)
	apiError := new(APIError)
	resp, err := s.sling.New().Get(path).QueryStruct(Query{Query: query}).Receive(componentList, apiError)
	return componentList, resp, relevantError(err, *apiError)
}
<|endoftext|>


<commit_before>package pongo2

import (
	"errors"
	"fmt"
	"strings"
	"unicode/utf8"
)

const (
	TokenError = iota
	EOF

	TokenHTML

	TokenKeyword
	TokenIdentifier
	TokenString
	TokenNumber
	TokenSymbol
)

var (
	tokenSpaceChars      = " \n\r\t"
	tokenIdentifierChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
	tokenDigits          = "0123456789"

	// Available symbols in pongo2 (within filters/tag)
	TokenSymbols = []string{
		// 3-Char symbols

		// 2-Char symbols
		"==", ">=", "<=", "&&", "||", "{{", "}}", "{%", "%}", "!=", "<>",

		// 1-Char symbol
		"(", ")", "+", "-", "*", "<", ">", "/", "^", ",", ".", "!", "|", ":", "=", "%",
	}

	// Available keywords in pongo2
	TokenKeywords = []string{"in", "and", "or", "not", "true", "false", "as"}
)

type TokenType int
type Token struct {
	Filename string
	Typ      TokenType
	Val      string
	Line     int
	Col      int
}

type lexerStateFn func() lexerStateFn
type lexer struct {
	name      string
	input     string
	start     int // start pos of the item
	pos       int // current pos
	width     int // width of last rune
	tokens    []*Token
	errored   bool
	startline int
	startcol  int
	line      int
	col       int

	in_verbatim   bool
	verbatim_name string
}

func (t *Token) String() string {
	val := t.Val
	if len(val) > 1000 {
		val = fmt.Sprintf("%s...%s", val[:10], val[len(val)-5:len(val)])
	}

	typ := ""
	switch t.Typ {
	case TokenHTML:
		typ = "HTML"
	case TokenError:
		typ = "Error"
	case TokenIdentifier:
		typ = "Identifier"
	case TokenKeyword:
		typ = "Keyword"
	case TokenNumber:
		typ = "Number"
	case TokenString:
		typ = "String"
	case TokenSymbol:
		typ = "Symbol"
	default:
		typ = "Unknown"
	}

	return fmt.Sprintf("<Token Typ=%s (%d) Val='%s' Line=%d Col=%d>",
		typ, t.Typ, val, t.Line, t.Col)
}

func lex(name string, input string) ([]*Token, error) {
	l := &lexer{
		name:      name,
		input:     input,
		tokens:    make([]*Token, 0, 100),
		line:      1,
		col:       1,
		startline: 1,
		startcol:  1,
	}
	l.run()
	if l.errored {
		errtoken := l.tokens[len(l.tokens)-1]
		return nil, errors.New(fmt.Sprintf("[Lexer Error in %s (Line %d Col %d)]: %s",
			name, errtoken.Line, errtoken.Col, errtoken.Val))
	}
	return l.tokens, nil
}

func (l *lexer) value() string {
	return l.input[l.start:l.pos]
}

func (l *lexer) length() int {
	return l.pos - l.start
}

func (l *lexer) emit(t TokenType) {
	tok := &Token{
		Filename: l.name,
		Typ:      t,
		Val:      l.value(),
		Line:     l.startline,
		Col:      l.startcol,
	}
	l.tokens = append(l.tokens, tok)
	l.start = l.pos
	l.startline = l.line
	l.startcol = l.col
}

func (l *lexer) next() rune {
	if l.pos >= len(l.input) {
		l.width = 0
		return EOF
	}
	r, w := utf8.DecodeRuneInString(l.input[l.pos:])
	l.width = w
	l.pos += l.width
	l.col += l.width
	return r
}

func (l *lexer) backup() {
	l.pos -= l.width
	l.col -= l.width
}

func (l *lexer) peek() rune {
	r := l.next()
	l.backup()
	return r
}

func (l *lexer) ignore() {
	l.start = l.pos
	l.startline = l.line
	l.startcol = l.col
}

func (l *lexer) accept(what string) bool {
	if strings.IndexRune(what, l.next()) >= 0 {
		return true
	}
	l.backup()
	return false
}

func (l *lexer) acceptRun(what string) {
	for strings.IndexRune(what, l.next()) >= 0 {
	}
	l.backup()
}

func (l *lexer) errorf(format string, args ...interface{}) lexerStateFn {
	t := &Token{
		Filename: l.name,
		Typ:      TokenError,
		Val:      fmt.Sprintf(format, args...),
		Line:     l.startline,
		Col:      l.startcol,
	}
	l.tokens = append(l.tokens, t)
	l.errored = true
	l.startline = l.line
	l.startcol = l.col
	return nil
}

func (l *lexer) eof() bool {
	return l.start >= len(l.input)-1
}

func (l *lexer) run() {
	for {
		// TODO: Support verbatim tag names
		// https://docs.djangoproject.com/en/dev/ref/templates/builtins/#verbatim
		if l.in_verbatim {
			name := l.verbatim_name
			if name != "" {
				name += " "
			}
			if strings.HasPrefix(l.input[l.pos:], fmt.Sprintf("{%% endverbatim %s%%}", name)) { // end verbatim
				if l.pos > l.start {
					l.emit(TokenHTML)
				}
				w := len("{% endverbatim %}")
				l.pos += w
				l.col += w
				l.ignore()
				l.in_verbatim = false
			}
		} else if strings.HasPrefix(l.input[l.pos:], "{% verbatim %}") { // tag
			if l.pos > l.start {
				l.emit(TokenHTML)
			}
			l.in_verbatim = true
			w := len("{% verbatim %}")
			l.pos += w
			l.col += w
			l.ignore()
		}

		if !l.in_verbatim {
			// Ignore single-line comments {# ... #}
			if strings.HasPrefix(l.input[l.pos:], "{#") {
				if l.pos > l.start {
					l.emit(TokenHTML)
				}

				l.pos += 2 // pass '{#'
				l.col += 2

				for {
					switch l.peek() {
					case EOF:
						l.errorf("Single-line comment not closed.")
						return
					case '\n':
						l.errorf("Newline not permitted in a single-line comment.")
						return
					}

					if strings.HasPrefix(l.input[l.pos:], "#}") {
						l.pos += 2 // pass '#}'
						l.col += 2
						break
					}

					l.next()
				}
				l.ignore() // ignore whole comment

				// Comment skipped
				continue // next token
			}

			if strings.HasPrefix(l.input[l.pos:], "{{") || // variable
				strings.HasPrefix(l.input[l.pos:], "{%") { // tag
				if l.pos > l.start {
					l.emit(TokenHTML)
				}
				l.tokenize()
				if l.errored {
					return
				}
				continue
			}
		}

		switch l.peek() {
		case '\n':
			l.line++
			l.col = 0
		}
		if l.next() == EOF {
			break
		}
	}

	if l.pos > l.start {
		l.emit(TokenHTML)
	}

	if l.in_verbatim {
		l.errorf("verbatim-tag not closed, got EOF.")
	}
}

func (l *lexer) tokenize() {
	for state := l.stateCode; state != nil; {
		state = state()
	}
}

func (l *lexer) stateCode() lexerStateFn {
outer_loop:
	for {
		switch {
		case l.accept(tokenSpaceChars):
			if l.value() == "\n" {
				return l.errorf("Newline not allowed within tag/variable.")
			}
			l.ignore()
			continue
		case l.accept(tokenIdentifierChars):
			return l.stateIdentifier
		case l.accept(tokenDigits):
			return l.stateNumber
		case l.accept(`"`):
			return l.stateString
		}

		// Check for symbol
		for _, sym := range TokenSymbols {
			if strings.HasPrefix(l.input[l.start:], sym) {
				l.pos += len(sym)
				l.col += l.length()
				l.emit(TokenSymbol)

				if sym == "%}" || sym == "}}" {
					// Tag/variable end, return after emit
					return nil
				}

				continue outer_loop
			}
		}

		if l.pos < len(l.input) {
			return l.errorf("Unknown character: %q (%d)", l.peek(), l.peek())
		}

		break
	}

	// Normal shut down
	return nil
}

func (l *lexer) stateIdentifier() lexerStateFn {
	l.acceptRun(tokenIdentifierChars)
	l.acceptRun(tokenDigits)
	for _, kw := range TokenKeywords {
		if kw == l.value() {
			l.emit(TokenKeyword)
			return l.stateCode
		}
	}
	l.emit(TokenIdentifier)
	return l.stateCode
}

func (l *lexer) stateNumber() lexerStateFn {
	l.acceptRun(tokenDigits)
	/*
		Maybe context-sensitive number lexing?
		* comments.0.Text // first comment
		* usercomments.1.0 // second user, first comment
		* if (score >= 8.5) // 8.5 as a number

		if l.peek() == '.' {
			l.accept(".")
			if !l.accept(tokenDigits) {
				return l.errorf("Malformed number.")
			}
			l.acceptRun(tokenDigits)
		}
	*/
	l.emit(TokenNumber)
	return l.stateCode
}

func (l *lexer) stateString() lexerStateFn {
	l.ignore()
	l.startcol -= 1 // we're starting the position at the first "
	for !l.accept(`"`) {
		switch l.next() {
		case EOF:
			return l.errorf("Unexpected EOF, string not closed.")
		case '\n':
			return l.errorf("Newline in string is not allowed.")
		}
	}
	l.backup()
	l.emit(TokenString)

	l.next()
	l.ignore()

	return l.stateCode
}
<commit_msg>Allow identifiers with digits in it.<commit_after>package pongo2

import (
	"errors"
	"fmt"
	"strings"
	"unicode/utf8"
)

const (
	TokenError = iota
	EOF

	TokenHTML

	TokenKeyword
	TokenIdentifier
	TokenString
	TokenNumber
	TokenSymbol
)

var (
	tokenSpaceChars                = " \n\r\t"
	tokenIdentifierChars           = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
	tokenIdentifierCharsWithDigits = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789"
	tokenDigits                    = "0123456789"

	// Available symbols in pongo2 (within filters/tag)
	TokenSymbols = []string{
		// 3-Char symbols

		// 2-Char symbols
		"==", ">=", "<=", "&&", "||", "{{", "}}", "{%", "%}", "!=", "<>",

		// 1-Char symbol
		"(", ")", "+", "-", "*", "<", ">", "/", "^", ",", ".", "!", "|", ":", "=", "%",
	}

	// Available keywords in pongo2
	TokenKeywords = []string{"in", "and", "or", "not", "true", "false", "as"}
)

type TokenType int
type Token struct {
	Filename string
	Typ      TokenType
	Val      string
	Line     int
	Col      int
}

type lexerStateFn func() lexerStateFn
type lexer struct {
	name      string
	input     string
	start     int // start pos of the item
	pos       int // current pos
	width     int // width of last rune
	tokens    []*Token
	errored   bool
	startline int
	startcol  int
	line      int
	col       int

	in_verbatim   bool
	verbatim_name string
}

func (t *Token) String() string {
	val := t.Val
	if len(val) > 1000 {
		val = fmt.Sprintf("%s...%s", val[:10], val[len(val)-5:len(val)])
	}

	typ := ""
	switch t.Typ {
	case TokenHTML:
		typ = "HTML"
	case TokenError:
		typ = "Error"
	case TokenIdentifier:
		typ = "Identifier"
	case TokenKeyword:
		typ = "Keyword"
	case TokenNumber:
		typ = "Number"
	case TokenString:
		typ = "String"
	case TokenSymbol:
		typ = "Symbol"
	default:
		typ = "Unknown"
	}

	return fmt.Sprintf("<Token Typ=%s (%d) Val='%s' Line=%d Col=%d>",
		typ, t.Typ, val, t.Line, t.Col)
}

func lex(name string, input string) ([]*Token, error) {
	l := &lexer{
		name:      name,
		input:     input,
		tokens:    make([]*Token, 0, 100),
		line:      1,
		col:       1,
		startline: 1,
		startcol:  1,
	}
	l.run()
	if l.errored {
		errtoken := l.tokens[len(l.tokens)-1]
		return nil, errors.New(fmt.Sprintf("[Lexer Error in %s (Line %d Col %d)]: %s",
			name, errtoken.Line, errtoken.Col, errtoken.Val))
	}
	return l.tokens, nil
}

func (l *lexer) value() string {
	return l.input[l.start:l.pos]
}

func (l *lexer) length() int {
	return l.pos - l.start
}

func (l *lexer) emit(t TokenType) {
	tok := &Token{
		Filename: l.name,
		Typ:      t,
		Val:      l.value(),
		Line:     l.startline,
		Col:      l.startcol,
	}
	l.tokens = append(l.tokens, tok)
	l.start = l.pos
	l.startline = l.line
	l.startcol = l.col
}

func (l *lexer) next() rune {
	if l.pos >= len(l.input) {
		l.width = 0
		return EOF
	}
	r, w := utf8.DecodeRuneInString(l.input[l.pos:])
	l.width = w
	l.pos += l.width
	l.col += l.width
	return r
}

func (l *lexer) backup() {
	l.pos -= l.width
	l.col -= l.width
}

func (l *lexer) peek() rune {
	r := l.next()
	l.backup()
	return r
}

func (l *lexer) ignore() {
	l.start = l.pos
	l.startline = l.line
	l.startcol = l.col
}

func (l *lexer) accept(what string) bool {
	if strings.IndexRune(what, l.next()) >= 0 {
		return true
	}
	l.backup()
	return false
}

func (l *lexer) acceptRun(what string) {
	for strings.IndexRune(what, l.next()) >= 0 {
	}
	l.backup()
}

func (l *lexer) errorf(format string, args ...interface{}) lexerStateFn {
	t := &Token{
		Filename: l.name,
		Typ:      TokenError,
		Val:      fmt.Sprintf(format, args...),
		Line:     l.startline,
		Col:      l.startcol,
	}
	l.tokens = append(l.tokens, t)
	l.errored = true
	l.startline = l.line
	l.startcol = l.col
	return nil
}

func (l *lexer) eof() bool {
	return l.start >= len(l.input)-1
}

func (l *lexer) run() {
	for {
		// TODO: Support verbatim tag names
		// https://docs.djangoproject.com/en/dev/ref/templates/builtins/#verbatim
		if l.in_verbatim {
			name := l.verbatim_name
			if name != "" {
				name += " "
			}
			if strings.HasPrefix(l.input[l.pos:], fmt.Sprintf("{%% endverbatim %s%%}", name)) { // end verbatim
				if l.pos > l.start {
					l.emit(TokenHTML)
				}
				w := len("{% endverbatim %}")
				l.pos += w
				l.col += w
				l.ignore()
				l.in_verbatim = false
			}
		} else if strings.HasPrefix(l.input[l.pos:], "{% verbatim %}") { // tag
			if l.pos > l.start {
				l.emit(TokenHTML)
			}
			l.in_verbatim = true
			w := len("{% verbatim %}")
			l.pos += w
			l.col += w
			l.ignore()
		}

		if !l.in_verbatim {
			// Ignore single-line comments {# ... #}
			if strings.HasPrefix(l.input[l.pos:], "{#") {
				if l.pos > l.start {
					l.emit(TokenHTML)
				}

				l.pos += 2 // pass '{#'
				l.col += 2

				for {
					switch l.peek() {
					case EOF:
						l.errorf("Single-line comment not closed.")
						return
					case '\n':
						l.errorf("Newline not permitted in a single-line comment.")
						return
					}

					if strings.HasPrefix(l.input[l.pos:], "#}") {
						l.pos += 2 // pass '#}'
						l.col += 2
						break
					}

					l.next()
				}
				l.ignore() // ignore whole comment

				// Comment skipped
				continue // next token
			}

			if strings.HasPrefix(l.input[l.pos:], "{{") || // variable
				strings.HasPrefix(l.input[l.pos:], "{%") { // tag
				if l.pos > l.start {
					l.emit(TokenHTML)
				}
				l.tokenize()
				if l.errored {
					return
				}
				continue
			}
		}

		switch l.peek() {
		case '\n':
			l.line++
			l.col = 0
		}
		if l.next() == EOF {
			break
		}
	}

	if l.pos > l.start {
		l.emit(TokenHTML)
	}

	if l.in_verbatim {
		l.errorf("verbatim-tag not closed, got EOF.")
	}
}

func (l *lexer) tokenize() {
	for state := l.stateCode; state != nil; {
		state = state()
	}
}

func (l *lexer) stateCode() lexerStateFn {
outer_loop:
	for {
		switch {
		case l.accept(tokenSpaceChars):
			if l.value() == "\n" {
				return l.errorf("Newline not allowed within tag/variable.")
			}
			l.ignore()
			continue
		case l.accept(tokenIdentifierChars):
			return l.stateIdentifier
		case l.accept(tokenDigits):
			return l.stateNumber
		case l.accept(`"`):
			return l.stateString
		}

		// Check for symbol
		for _, sym := range TokenSymbols {
			if strings.HasPrefix(l.input[l.start:], sym) {
				l.pos += len(sym)
				l.col += l.length()
				l.emit(TokenSymbol)

				if sym == "%}" || sym == "}}" {
					// Tag/variable end, return after emit
					return nil
				}

				continue outer_loop
			}
		}

		if l.pos < len(l.input) {
			return l.errorf("Unknown character: %q (%d)", l.peek(), l.peek())
		}

		break
	}

	// Normal shut down
	return nil
}

func (l *lexer) stateIdentifier() lexerStateFn {
	l.acceptRun(tokenIdentifierChars)
	l.acceptRun(tokenIdentifierCharsWithDigits)
	for _, kw := range TokenKeywords {
		if kw == l.value() {
			l.emit(TokenKeyword)
			return l.stateCode
		}
	}
	l.emit(TokenIdentifier)
	return l.stateCode
}

func (l *lexer) stateNumber() lexerStateFn {
	l.acceptRun(tokenDigits)
	/*
		Maybe context-sensitive number lexing?
		* comments.0.Text // first comment
		* usercomments.1.0 // second user, first comment
		* if (score >= 8.5) // 8.5 as a number

		if l.peek() == '.' {
			l.accept(".")
			if !l.accept(tokenDigits) {
				return l.errorf("Malformed number.")
			}
			l.acceptRun(tokenDigits)
		}
	*/
	l.emit(TokenNumber)
	return l.stateCode
}

func (l *lexer) stateString() lexerStateFn {
	l.ignore()
	l.startcol -= 1 // we're starting the position at the first "
	for !l.accept(`"`) {
		switch l.next() {
		case EOF:
			return l.errorf("Unexpected EOF, string not closed.")
		case '\n':
			return l.errorf("Newline in string is not allowed.")
		}
	}
	l.backup()
	l.emit(TokenString)

	l.next()
	l.ignore()

	return l.stateCode
}
<|endoftext|>


<commit_before>/*
Two APIs

The Lexer type has two APIs, one is used byte StateFn types.  The other is
called by the parser. These APIs are called the scanner and the parser APIs
here.

The parser API

The only function the parser calls on the lexer is Next to retreive the next
token from the input stream.  Eventually an item with type ItemEOF is returned
at which point there are no more tokens in the stream.

The scanner API

The lexer uses Emit to construct complete lexemes to return from
future/concurrent calls to Next by the parser.  The scanner uses a combination
of methods to manipulate its position and and prepare lexemes to be emitted.
Lexer errors are emitted to the parser using the Errorf method which keeps the
scanner-parser interface uniform.

Common lexer methods used in a scanner are the Accept[Run][Range]
family of methods.  Accept* methods take a set and advance the
lexer if incoming runes are in the set. The AcceptRun* subfamily
advance the lexer as far as possible.

For scanning known sequences of bytes (e.g. keywords) the
AcceptString method avoids a lot of branching that would be
incurred using methods that match character classes.

The remaining methods provide low level functionality that can
be combined to address corner cases.
*/
package sprite_sass

import (
	"container/list"
	"errors"
	"fmt"
	"math"
	"strings"
	"unicode"
	"unicode/utf8"
)

const EOF rune = 0x04

// IsEOF returns true if n is zero.
func IsEOF(c rune, n int) bool {
	return n == 0
}

// IsInvalid returns true if c is utf8.RuneError and n is 1.
func IsInvalid(c rune, n int) bool {
	return c == utf8.RuneError && n == 1
}

// StateFn functions scan runes from the lexer's input and emit items.  A StateFn
// is responsible for emitting ItemEOF after input has been consumed.
type StateFn func(*Lexer) StateFn

// Lexer contains an input string and state associate with the lexing the
// input.
type Lexer struct {
	input string     // string being scanned
	start int        // start position for the current lexeme
	pos   int        // current position
	width int        // length of the last rune read
	last  rune       // the last rune read
	state StateFn    // the current state
	items *list.List // Buffer of lexed items
}

// Create a new lexer. Must be given a non-nil state.
func New(state StateFn, input string) *Lexer {

	if state == nil {
		panic("nil start state")
	}
	return &Lexer{
		state: state,
		input: input,
		items: list.New(),
	}
}

// Input returns the input string being lexed by the l.
func (l *Lexer) Input() string {
	return l.input
}

// Start marks the first byte of item currently being lexed.
func (l *Lexer) Start() int {
	return l.start
}

// Pos marks the next byte to be read in the input string.  The behavior of Pos
// is unspecified if an error previously occurred or if all input has been
// consumed.
func (l *Lexer) Pos() int {
	return l.pos
}

// Current returns the contents of the item currently being lexed.
func (l *Lexer) Current() string {
	return l.input[l.start:l.pos]
}

// Last return the last rune read from the input stream.
func (l *Lexer) Last() (r rune, width int) {
	return l.last, l.width
}

// Advance adds one rune of input to the current lexeme,
// increments the lexer's position, and returns the input
// rune with its size in bytes (encoded as UTF-8).  Invalid
// UTF-8 codepoints cause the current call and all subsequent
// calls to return (utf8.RuneError, 1).  If there is no input
// the returned size is zero.
func (l *Lexer) Advance() (rune, int) {
	if l.pos >= len(l.input) {
		l.width = 0
		return EOF, l.width
	}
	l.last, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
	if l.last == utf8.RuneError && l.width == 1 {
		return l.last, l.width
	}
	l.pos += l.width
	return l.last, l.width
}

// Backup removes the last rune from the current lexeme and moves l's position
// back in the input string accordingly. Backup should only be called after a
// call to Advance.
func (l *Lexer) Backup() {
	l.pos -= l.width
}

// Peek returns the next rune in the input stream without adding it to the
// current lexeme.
func (l *Lexer) Peek() (rune, int) {
	c, n := l.Advance()
	l.Backup()
	return c, n
}

// Ignore throws away the current lexeme.
func (l *Lexer) Ignore() {
	l.start = l.pos
}

// Accept advances the lexer if the next rune is in valid.
func (l *Lexer) Accept(valid string) (ok bool) {
	r, _ := l.Advance()
	ok = strings.IndexRune(valid, r) >= 0
	if !ok {
		l.Backup()
	}
	return
}

// AcceptFunc advances the lexer if fn return true for the next rune.
func (l *Lexer) AcceptFunc(fn func(rune) bool) (ok bool) {
	switch r, n := l.Advance(); {
	case IsEOF(r, n):
		return false
	case IsInvalid(r, n):
		return false
	case fn(r):
		return true
	default:
		l.Backup()
		return false
	}
}

// AcceptRange advances l's position if the current rune is in tab.
func (l *Lexer) AcceptRange(tab *unicode.RangeTable) (ok bool) {
	r, _ := l.Advance()
	ok = unicode.Is(tab, r)
	if !ok {
		l.Backup()
	}
	return
}

// AcceptRun advances l's position as long as the current rune is in valid.
func (l *Lexer) AcceptRun(valid string) (n int) {
	for l.Accept(valid) {
		n++
	}
	return
}

// AcceptRunFunc advances l's position as long as fn returns
// true for the next input rune.
func (l *Lexer) AcceptRunFunc(fn func(rune) bool) int {
	var n int
	for l.AcceptFunc(fn) {
		n++
	}
	return n
}

// AcceptRunRange advances l's possition as long as the current
// rune is in tab.
func (l *Lexer) AcceptRunRange(tab *unicode.RangeTable) (n int) {
	for l.AcceptRange(tab) {
		n++
	}
	return
}

// AcceptString advances the lexer len(s) bytes if the next
// len(s) bytes equal s. AcceptString returns true if l advanced.
func (l *Lexer) AcceptString(s string) (ok bool) {
	if strings.HasPrefix(l.input[l.pos:], s) {
		l.pos += len(s)
		return true
	}
	return false
}

// Errorf causes an error item to be emitted from l.Next().  The item's value
// (and its error message) are the result of evaluating format and vs with
// fmt.Sprintf.
func (l *Lexer) Errorf(format string, vs ...interface{}) StateFn {
	l.enqueue(&Item{
		ItemError,
		l.start,
		fmt.Sprintf(format, vs...),
		false,
	})
	return nil
}

// Emit the current value as an Item with the specified type.
func (l *Lexer) Emit(t ItemType) {

	l.enqueue(&Item{
		t,
		l.start,
		l.input[l.start:l.pos],
		false,
	})
	l.start = l.pos
}

// The method by which items are extracted from the input.
// Returns nil if the lexer has entered a nil state.
func (l *Lexer) Next() (i *Item) {
	for {
		if head := l.dequeue(); head != nil {
			return head
		}
		if l.state == nil {
			return &Item{ItemEOF, l.start, "", false}
		}
		l.state = l.state(l)
	}
}

func (l *Lexer) enqueue(i *Item) {
	l.items.PushBack(i)
}

func (l *Lexer) dequeue() *Item {
	head := l.items.Front()
	if head == nil {
		return nil
	}
	return l.items.Remove(head).(*Item)
}

// A type for all the types of items in the language being lexed.
type ItemType uint16

// Special item types.
const (
	ItemEOF ItemType = math.MaxUint16 - iota
	ItemError
	IMPORT
	EXTRA
	CMD
	VAR
	SUB
	VALUE
	FILE
	SPRITE
	NUMBER
	TEXT
	DOLLAR
	special_beg
	LPAREN
	RPAREN
	LBRACKET
	RBRACKET
	SEMIC
	special_end
)

var Tokens = [...]string{
	ItemEOF:   "eof",
	ItemError: "error",
	IMPORT:    "@import",
	EXTRA:     "extra",
	CMD:       "command",
	VAR:       "variable",
	SUB:       "sub",
	VALUE:     "value",
	FILE:      "file",
	SPRITE:    "sprite",
	NUMBER:    "number",
	TEXT:      "text",
	DOLLAR:    "$",
	LPAREN:    "(",
	RPAREN:    ")",
	LBRACKET:  "{",
	RBRACKET:  "}",
	SEMIC:     ";",
}

const (
	Symbols = `/\.*-_`
)

func IsAllowedRune(r rune) bool {
	return unicode.IsNumber(r) ||
		unicode.IsLetter(r) ||
		strings.ContainsRune(Symbols, r)
}

func (i ItemType) String() string {
	return Tokens[i]
}

// An individual scanned item (a lexeme).
type Item struct {
	Type  ItemType
	Pos   int
	Value string
	Write bool
}

func (i Item) Error() error {
	if i.Type == ItemError {
		return errors.New("Error reading input")
	}
	return nil
}

// String returns the raw lexeme of i.
func (i Item) String() string {
	switch i.Type {
	case ItemError:
		return i.Value
	case ItemEOF:
		return "EOF"
	}
	if len(i.Value) > 10 {
		return fmt.Sprintf("%s", i.Value)
	}
	return i.Value
}

func (l *Lexer) Action() StateFn {
	for {
		switch r, _ := l.Advance(); {
		case r == EOF: // || r == '\n':
			l.enqueue(&Item{
				ItemEOF,
				l.start,
				"",
				false,
			})
			return nil
		case IsSpace(r):
			l.Ignore()
		case IsSymbol(r):
			return l.Paren()
		case r == '@':
			return l.Directive()
		case r == '"' || r == '\'':
			return l.File()
		case r == '$':
			return l.Var()
		case IsAllowedRune(r):
			return l.Text()
		default:
			//l.Advance()
			//l.Emit(EXTRA)
		}
	}
}

func IsSymbol(r rune) bool {
	return strings.ContainsRune("(),;{}", r)
}

func IsSpace(r rune) bool {
	return strings.ContainsRune(" \n", r)
}

func IsPrintable(r rune) bool {
	return true
}

func (l *Lexer) Directive() StateFn {

	l.AcceptRunFunc(IsAllowedRune)

	switch l.Current() {
	case "@import":
		l.Emit(IMPORT)
		break
	}
	return l.Action()
}

func (l *Lexer) Paren() StateFn {
	switch l.Current() {
	case "(":
		l.Emit(LPAREN)
	case ")":
		l.Emit(RPAREN)
	case "{":
		l.Emit(LBRACKET)
	case "}":
		l.Emit(RBRACKET)
	case ";":
		l.Emit(SEMIC)
	}
	return l.Action()
}

// $images: sprite-map("*.png");
func (l *Lexer) Var() StateFn {
	l.Accept("$")
	l.AcceptRunFunc(IsAllowedRune)
	r, _ := l.Peek()

	if r == ':' {
		l.Emit(VAR)
	} else {
		l.Emit(SUB)
	}
	return l.Action()
}

func (l *Lexer) Text() StateFn {
	l.AcceptRunFunc(IsAllowedRune)

	switch l.Current() {
	//Primary support
	case "sprite", "sprite-file", "sprite-height",
		"sprite-map", "sprite-path", "sprite-position",
		"sprite-width", "sprite-url":
		l.Emit(CMD)
		return l.Action()
	//Tertiary support
	case "sprite-map-name", "sprite-names":
		l.Emit(CMD)
		return l.Action()
	default:
		l.Emit(TEXT)
	}
	return l.Action()
}

func (l *Lexer) File() StateFn {
	l.Ignore()
	if !l.AcceptFunc(IsAllowedRune) {
		return l.Action()
	}
	l.AcceptRunFunc(IsAllowedRune)
	l.Emit(FILE)
	return l.Action()
}
<commit_msg>Remove preamble<commit_after>/*
The lexer processes text flagging any sass extended commands
sprite* as commands
*/
package sprite_sass

import (
	"container/list"
	"errors"
	"fmt"
	"math"
	"strings"
	"unicode"
	"unicode/utf8"
)

const EOF rune = 0x04

// IsEOF returns true if n is zero.
func IsEOF(c rune, n int) bool {
	return n == 0
}

// IsInvalid returns true if c is utf8.RuneError and n is 1.
func IsInvalid(c rune, n int) bool {
	return c == utf8.RuneError && n == 1
}

// StateFn functions scan runes from the lexer's input and emit items.  A StateFn
// is responsible for emitting ItemEOF after input has been consumed.
type StateFn func(*Lexer) StateFn

// Lexer contains an input string and state associate with the lexing the
// input.
type Lexer struct {
	input string     // string being scanned
	start int        // start position for the current lexeme
	pos   int        // current position
	width int        // length of the last rune read
	last  rune       // the last rune read
	state StateFn    // the current state
	items *list.List // Buffer of lexed items
}

// Create a new lexer. Must be given a non-nil state.
func New(state StateFn, input string) *Lexer {

	if state == nil {
		panic("nil start state")
	}
	return &Lexer{
		state: state,
		input: input,
		items: list.New(),
	}
}

// Input returns the input string being lexed by the l.
func (l *Lexer) Input() string {
	return l.input
}

// Start marks the first byte of item currently being lexed.
func (l *Lexer) Start() int {
	return l.start
}

// Pos marks the next byte to be read in the input string.  The behavior of Pos
// is unspecified if an error previously occurred or if all input has been
// consumed.
func (l *Lexer) Pos() int {
	return l.pos
}

// Current returns the contents of the item currently being lexed.
func (l *Lexer) Current() string {
	return l.input[l.start:l.pos]
}

// Last return the last rune read from the input stream.
func (l *Lexer) Last() (r rune, width int) {
	return l.last, l.width
}

// Advance adds one rune of input to the current lexeme,
// increments the lexer's position, and returns the input
// rune with its size in bytes (encoded as UTF-8).  Invalid
// UTF-8 codepoints cause the current call and all subsequent
// calls to return (utf8.RuneError, 1).  If there is no input
// the returned size is zero.
func (l *Lexer) Advance() (rune, int) {
	if l.pos >= len(l.input) {
		l.width = 0
		return EOF, l.width
	}
	l.last, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
	if l.last == utf8.RuneError && l.width == 1 {
		return l.last, l.width
	}
	l.pos += l.width
	return l.last, l.width
}

// Backup removes the last rune from the current lexeme and moves l's position
// back in the input string accordingly. Backup should only be called after a
// call to Advance.
func (l *Lexer) Backup() {
	l.pos -= l.width
}

// Peek returns the next rune in the input stream without adding it to the
// current lexeme.
func (l *Lexer) Peek() (rune, int) {
	c, n := l.Advance()
	l.Backup()
	return c, n
}

// Ignore throws away the current lexeme.
func (l *Lexer) Ignore() {
	l.start = l.pos
}

// Accept advances the lexer if the next rune is in valid.
func (l *Lexer) Accept(valid string) (ok bool) {
	r, _ := l.Advance()
	ok = strings.IndexRune(valid, r) >= 0
	if !ok {
		l.Backup()
	}
	return
}

// AcceptFunc advances the lexer if fn return true for the next rune.
func (l *Lexer) AcceptFunc(fn func(rune) bool) (ok bool) {
	switch r, n := l.Advance(); {
	case IsEOF(r, n):
		return false
	case IsInvalid(r, n):
		return false
	case fn(r):
		return true
	default:
		l.Backup()
		return false
	}
}

// AcceptRange advances l's position if the current rune is in tab.
func (l *Lexer) AcceptRange(tab *unicode.RangeTable) (ok bool) {
	r, _ := l.Advance()
	ok = unicode.Is(tab, r)
	if !ok {
		l.Backup()
	}
	return
}

// AcceptRun advances l's position as long as the current rune is in valid.
func (l *Lexer) AcceptRun(valid string) (n int) {
	for l.Accept(valid) {
		n++
	}
	return
}

// AcceptRunFunc advances l's position as long as fn returns
// true for the next input rune.
func (l *Lexer) AcceptRunFunc(fn func(rune) bool) int {
	var n int
	for l.AcceptFunc(fn) {
		n++
	}
	return n
}

// AcceptRunRange advances l's possition as long as the current
// rune is in tab.
func (l *Lexer) AcceptRunRange(tab *unicode.RangeTable) (n int) {
	for l.AcceptRange(tab) {
		n++
	}
	return
}

// AcceptString advances the lexer len(s) bytes if the next
// len(s) bytes equal s. AcceptString returns true if l advanced.
func (l *Lexer) AcceptString(s string) (ok bool) {
	if strings.HasPrefix(l.input[l.pos:], s) {
		l.pos += len(s)
		return true
	}
	return false
}

// Errorf causes an error item to be emitted from l.Next().  The item's value
// (and its error message) are the result of evaluating format and vs with
// fmt.Sprintf.
func (l *Lexer) Errorf(format string, vs ...interface{}) StateFn {
	l.enqueue(&Item{
		ItemError,
		l.start,
		fmt.Sprintf(format, vs...),
		false,
	})
	return nil
}

// Emit the current value as an Item with the specified type.
func (l *Lexer) Emit(t ItemType) {

	l.enqueue(&Item{
		t,
		l.start,
		l.input[l.start:l.pos],
		false,
	})
	l.start = l.pos
}

// The method by which items are extracted from the input.
// Returns nil if the lexer has entered a nil state.
func (l *Lexer) Next() (i *Item) {
	for {
		if head := l.dequeue(); head != nil {
			return head
		}
		if l.state == nil {
			return &Item{ItemEOF, l.start, "", false}
		}
		l.state = l.state(l)
	}
}

func (l *Lexer) enqueue(i *Item) {
	l.items.PushBack(i)
}

func (l *Lexer) dequeue() *Item {
	head := l.items.Front()
	if head == nil {
		return nil
	}
	return l.items.Remove(head).(*Item)
}

// A type for all the types of items in the language being lexed.
type ItemType uint16

// Special item types.
const (
	ItemEOF ItemType = math.MaxUint16 - iota
	ItemError
	IMPORT
	EXTRA
	CMD
	VAR
	SUB
	VALUE
	FILE
	SPRITE
	NUMBER
	TEXT
	DOLLAR
	special_beg
	LPAREN
	RPAREN
	LBRACKET
	RBRACKET
	SEMIC
	special_end
)

var Tokens = [...]string{
	ItemEOF:   "eof",
	ItemError: "error",
	IMPORT:    "@import",
	EXTRA:     "extra",
	CMD:       "command",
	VAR:       "variable",
	SUB:       "sub",
	VALUE:     "value",
	FILE:      "file",
	SPRITE:    "sprite",
	NUMBER:    "number",
	TEXT:      "text",
	DOLLAR:    "$",
	LPAREN:    "(",
	RPAREN:    ")",
	LBRACKET:  "{",
	RBRACKET:  "}",
	SEMIC:     ";",
}

const (
	Symbols = `/\.*-_`
)

func IsAllowedRune(r rune) bool {
	return unicode.IsNumber(r) ||
		unicode.IsLetter(r) ||
		strings.ContainsRune(Symbols, r)
}

func (i ItemType) String() string {
	return Tokens[i]
}

// An individual scanned item (a lexeme).
type Item struct {
	Type  ItemType
	Pos   int
	Value string
	Write bool
}

func (i Item) Error() error {
	if i.Type == ItemError {
		return errors.New("Error reading input")
	}
	return nil
}

// String returns the raw lexeme of i.
func (i Item) String() string {
	switch i.Type {
	case ItemError:
		return i.Value
	case ItemEOF:
		return "EOF"
	}
	if len(i.Value) > 10 {
		return fmt.Sprintf("%s", i.Value)
	}
	return i.Value
}

func (l *Lexer) Action() StateFn {
	for {
		switch r, _ := l.Advance(); {
		case r == EOF: // || r == '\n':
			l.enqueue(&Item{
				ItemEOF,
				l.start,
				"",
				false,
			})
			return nil
		case IsSpace(r):
			l.Ignore()
		case IsSymbol(r):
			return l.Paren()
		case r == '@':
			return l.Directive()
		case r == '"' || r == '\'':
			return l.File()
		case r == '$':
			return l.Var()
		case IsAllowedRune(r):
			return l.Text()
		default:
			//l.Advance()
			//l.Emit(EXTRA)
		}
	}
}

func IsSymbol(r rune) bool {
	return strings.ContainsRune("(),;{}", r)
}

func IsSpace(r rune) bool {
	return strings.ContainsRune(" \n", r)
}

func IsPrintable(r rune) bool {
	return true
}

func (l *Lexer) Directive() StateFn {

	l.AcceptRunFunc(IsAllowedRune)

	switch l.Current() {
	case "@import":
		l.Emit(IMPORT)
		break
	}
	return l.Action()
}

func (l *Lexer) Paren() StateFn {
	switch l.Current() {
	case "(":
		l.Emit(LPAREN)
	case ")":
		l.Emit(RPAREN)
	case "{":
		l.Emit(LBRACKET)
	case "}":
		l.Emit(RBRACKET)
	case ";":
		l.Emit(SEMIC)
	}
	return l.Action()
}

// $images: sprite-map("*.png");
func (l *Lexer) Var() StateFn {
	l.Accept("$")
	l.AcceptRunFunc(IsAllowedRune)
	r, _ := l.Peek()

	if r == ':' {
		l.Emit(VAR)
	} else {
		l.Emit(SUB)
	}
	return l.Action()
}

func (l *Lexer) Text() StateFn {
	l.AcceptRunFunc(IsAllowedRune)

	switch l.Current() {
	//Primary support
	case "sprite", "sprite-file", "sprite-height",
		"sprite-map", "sprite-path", "sprite-position",
		"sprite-width", "sprite-url":
		l.Emit(CMD)
		return l.Action()
	//Tertiary support
	case "sprite-map-name", "sprite-names":
		l.Emit(CMD)
		return l.Action()
	default:
		l.Emit(TEXT)
	}
	return l.Action()
}

func (l *Lexer) File() StateFn {
	l.Ignore()
	if !l.AcceptFunc(IsAllowedRune) {
		return l.Action()
	}
	l.AcceptRunFunc(IsAllowedRune)
	l.Emit(FILE)
	return l.Action()
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"fmt"
	"github.com/Pallinder/go-randomdata"
	_ "github.com/go-sql-driver/mysql"
)

func main() {
	db, err := sql.Open("mysql", "leogsouza:@/c9?charset=UTF8")
	checkErr(err)

	// insert
	stmt, err := db.Prepare("INSERT userinfo SET username=?, departname=?, created=?")
	checkErr(err)

	res, err := stmt.Exec("leogsouza", "Project Development", "2016-10-03")
	checkErr(err)

	id, err := res.LastInsertId()
	checkErr(err)

	fmt.Println("Last inserted ID", id)

	// update
	stmt, err = db.Prepare("update userinfo set username= ? where uid=?")
	checkErr(err)

	res, err = stmt.Exec("legosuz", id)
	checkErr(err)

	affect, err := res.RowsAffected()
	checkErr(err)

	fmt.Println("Rows affected", affect)

	// query
	rows, err := db.Query("SELECT * FROM userinfo")
	checkErr(err)

	for rows.Next() {
		var uid int
		var username string
		var department string
		var created string

		err = rows.Scan(&uid, &username, &department, &created)
		checkErr(err)

		fmt.Println("ID:", uid)
		fmt.Println("Username:", username)
		fmt.Println("Department:", department)
		fmt.Println("Created:", created)
	}

	// delete
	stmt, err = db.Prepare("delete from userinfo where uid=?")
	checkErr(err)

	res, err = stmt.Exec(id)
	checkErr(err)

	affect, err = res.RowsAffected()
	checkErr(err)
	fmt.Println("Rows affected on delete", affect)

	db.Close()
}

func checkErr(err error) {
	if err != nil {
		panic(err)
	}
}
<commit_msg>Modified database example<commit_after>package main

import (
	"database/sql"
	"fmt"
	"github.com/Pallinder/go-randomdata"
	_ "github.com/go-sql-driver/mysql"
)

func main() {
	db, err := sql.Open("mysql", "leogsouza:@/c9?charset=UTF8")
	checkErr(err)

	for i := 1; i <= 1000; i++ {
		// insert
		stmt, err := db.Prepare("INSERT userinfo SET username=?, departname=?, created=?")
		checkErr(err)
		username := randomdata.SillyName()
		department := randomdata.SillyName()
		created := randomdata.FullDate()
		res, err := stmt.Exec(username, department, created)
		checkErr(err)

		id, err := res.LastInsertId()
		checkErr(err)

		fmt.Println("Last inserted ID", id)
		stmt.Close()
	}

	// update
	/*stmt, err = db.Prepare("update userinfo set username= ? where uid=?")
	checkErr(err)

	res, err = stmt.Exec("legosuz", id)
	checkErr(err)

	affect, err := res.RowsAffected()
	checkErr(err)

	fmt.Println("Rows affected", affect)*/

	// query
	rows, err := db.Query("SELECT * FROM userinfo")
	checkErr(err)

	for rows.Next() {
		var uid int
		var username string
		var department string
		var created string

		err = rows.Scan(&uid, &username, &department, &created)
		checkErr(err)

		fmt.Println("ID:", uid)
		fmt.Println("Username:", username)
		fmt.Println("Department:", department)
		fmt.Println("Created:", created)
	}

	// delete
	/*stmt, err = db.Prepare("delete from userinfo where uid=?")
	checkErr(err)

	res, err = stmt.Exec(id)
	checkErr(err)

	affect, err = res.RowsAffected()
	checkErr(err)
	fmt.Println("Rows affected on delete", affect)*/

	db.Close()
}

func checkErr(err error) {
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccDataSourceAwsSecurityGroup(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccDataSourceAwsSecurityGroupConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccDataSourceAwsSecurityGroupCheck("data.aws_security_group.by_id"),
					testAccDataSourceAwsSecurityGroupCheck("data.aws_security_group.by_tag"),
					testAccDataSourceAwsSecurityGroupCheck("data.aws_security_group.by_filter"),
					testAccDataSourceAwsSecurityGroupCheck("data.aws_security_group.by_name"),
					testAccDataSourceAwsSecurityGroupCheckDefault("data.aws_security_group.default_by_name"),
				),
			},
		},
	})
}

func testAccDataSourceAwsSecurityGroupCheck(name string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[name]
		if !ok {
			return fmt.Errorf("root module has no resource called %s", name)
		}

		SGRs, ok := s.RootModule().Resources["aws_security_group.test"]
		if !ok {
			return fmt.Errorf("can't find aws_security_group.test in state")
		}
		vpcRs, ok := s.RootModule().Resources["aws_vpc.test"]
		if !ok {
			return fmt.Errorf("can't find aws_vpc.test in state")
		}
		attr := rs.Primary.Attributes

		if attr["id"] != SGRs.Primary.Attributes["id"] {
			return fmt.Errorf(
				"id is %s; want %s",
				attr["id"],
				SGRs.Primary.Attributes["id"],
			)
		}

		if attr["vpc_id"] != vpcRs.Primary.Attributes["id"] {
			return fmt.Errorf(
				"vpc_id is %s; want %s",
				attr["vpc_id"],
				vpcRs.Primary.Attributes["id"],
			)
		}

		if attr["tags.Name"] != "terraform-testacc-security-group-data-source" {
			return fmt.Errorf("bad Name tag %s", attr["tags.Name"])
		}

		return nil
	}
}

func testAccDataSourceAwsSecurityGroupCheckDefault(name string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[name]
		if !ok {
			return fmt.Errorf("root module has no resource called %s", name)
		}

		vpcRs, ok := s.RootModule().Resources["aws_vpc.test"]
		if !ok {
			return fmt.Errorf("can't find aws_vpc.test in state")
		}
		attr := rs.Primary.Attributes

		if attr["id"] != vpcRs.Primary.Attributes["default_security_group_id"] {
			return fmt.Errorf(
				"id is %s; want %s",
				attr["id"],
				vpcRs.Primary.Attributes["default_security_group_id"],
			)
		}

		return nil
	}
}

const testAccDataSourceAwsSecurityGroupConfig = `
provider "aws" {
  region = "eu-west-1"
}
resource "aws_vpc" "test" {
  cidr_block = "172.16.0.0/16"

  tags {
    Name = "terraform-testacc-subnet-data-source"
  }
}

resource "aws_security_group" "test" {
  vpc_id = "${aws_vpc.test.id}"
  name = "security-groupe-name-test"
  tags {
    Name = "terraform-testacc-security-group-data-source"
  }
}

data "aws_security_group" "by_id" {
  id = "${aws_security_group.test.id}"
}

data "aws_security_group" "by_name" {
  name = "${aws_security_group.test.name}"
}

data "aws_security_group" "default_by_name" {
	vpc_id = "${aws_vpc.test.id}"
  name = "default"
}

data "aws_security_group" "by_tag" {
  tags {
    Name = "${aws_security_group.test.tags["Name"]}"
  }
}

data "aws_security_group" "by_filter" {
  filter {
    name = "group-name"
    values = ["${aws_security_group.test.name}"]
  }
}
`
<commit_msg>provider/aws: Fix sg data source acctests (#11994)<commit_after>package aws

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccDataSourceAwsSecurityGroup(t *testing.T) {
	rInt := acctest.RandInt()
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccDataSourceAwsSecurityGroupConfig(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccDataSourceAwsSecurityGroupCheck("data.aws_security_group.by_id"),
					testAccDataSourceAwsSecurityGroupCheck("data.aws_security_group.by_tag"),
					testAccDataSourceAwsSecurityGroupCheck("data.aws_security_group.by_filter"),
					testAccDataSourceAwsSecurityGroupCheck("data.aws_security_group.by_name"),
					testAccDataSourceAwsSecurityGroupCheckDefault("data.aws_security_group.default_by_name"),
				),
			},
		},
	})
}

func testAccDataSourceAwsSecurityGroupCheck(name string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[name]
		if !ok {
			return fmt.Errorf("root module has no resource called %s", name)
		}

		SGRs, ok := s.RootModule().Resources["aws_security_group.test"]
		if !ok {
			return fmt.Errorf("can't find aws_security_group.test in state")
		}
		vpcRs, ok := s.RootModule().Resources["aws_vpc.test"]
		if !ok {
			return fmt.Errorf("can't find aws_vpc.test in state")
		}
		attr := rs.Primary.Attributes

		if attr["id"] != SGRs.Primary.Attributes["id"] {
			return fmt.Errorf(
				"id is %s; want %s",
				attr["id"],
				SGRs.Primary.Attributes["id"],
			)
		}

		if attr["vpc_id"] != vpcRs.Primary.Attributes["id"] {
			return fmt.Errorf(
				"vpc_id is %s; want %s",
				attr["vpc_id"],
				vpcRs.Primary.Attributes["id"],
			)
		}

		if attr["tags.Name"] != "tf-acctest" {
			return fmt.Errorf("bad Name tag %s", attr["tags.Name"])
		}

		return nil
	}
}

func testAccDataSourceAwsSecurityGroupCheckDefault(name string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[name]
		if !ok {
			return fmt.Errorf("root module has no resource called %s", name)
		}

		vpcRs, ok := s.RootModule().Resources["aws_vpc.test"]
		if !ok {
			return fmt.Errorf("can't find aws_vpc.test in state")
		}
		attr := rs.Primary.Attributes

		if attr["id"] != vpcRs.Primary.Attributes["default_security_group_id"] {
			return fmt.Errorf(
				"id is %s; want %s",
				attr["id"],
				vpcRs.Primary.Attributes["default_security_group_id"],
			)
		}

		return nil
	}
}

func testAccDataSourceAwsSecurityGroupConfig(rInt int) string {
	return fmt.Sprintf(`
	provider "aws" {
		region = "eu-west-1"
	}
	resource "aws_vpc" "test" {
		cidr_block = "172.16.0.0/16"

		tags {
			Name = "terraform-testacc-subnet-data-source"
		}
	}

	resource "aws_security_group" "test" {
		vpc_id = "${aws_vpc.test.id}"
		name = "test-%d"
		tags {
			Name = "tf-acctest"
			Seed = "%d"
		}
	}

	data "aws_security_group" "by_id" {
		id = "${aws_security_group.test.id}"
	}

	data "aws_security_group" "by_name" {
		name = "${aws_security_group.test.name}"
	}

	data "aws_security_group" "default_by_name" {
		vpc_id = "${aws_vpc.test.id}"
		name = "default"
	}

	data "aws_security_group" "by_tag" {
		tags {
			Seed = "${aws_security_group.test.tags["Seed"]}"
		}
	}

	data "aws_security_group" "by_filter" {
		filter {
			name = "group-name"
			values = ["${aws_security_group.test.name}"]
		}
	}`, rInt, rInt)
}
<|endoftext|>


<commit_before>package controllers

import (
	"encoding/base64"
	"encoding/json"
	"strconv"
	"strings"

	"github.com/astaxie/beego"
	"github.com/lfq7413/tomato/config"
	"github.com/lfq7413/tomato/errs"
	"github.com/lfq7413/tomato/rest"
	"github.com/lfq7413/tomato/types"
	"github.com/lfq7413/tomato/utils"
)

// ObjectsController 对象操作 API 的基础结构
// 处理 /classes 接口的所有请求，处理内部类的部分请求
// Info 当前请求的权限信息
// Auth 当前请求的用户权限
// JSONBody 由 JSON 格式转换来的请求数据
// RawBody 原始请求数据
// ClassName 要操作的类名
// ObjectID 要操作的对象 id
type ObjectsController struct {
	beego.Controller
	Info      *RequestInfo
	Auth      *rest.Auth
	JSONBody  types.M
	RawBody   []byte
	ClassName string
	ObjectID  string
}

// RequestInfo http 请求的权限信息
type RequestInfo struct {
	AppID          string
	MasterKey      string
	ClientKey      string
	SessionToken   string
	InstallationID string
}

// Prepare 对请求权限进行处理
// 1. 从请求头中获取各种 key
// 2. 尝试按 json 格式转换 body
// 3. 尝试从 body 中获取各种 key
// 4. 校验请求权限
// 5. 生成用户信息
func (o *ObjectsController) Prepare() {
	info := &RequestInfo{}
	info.AppID = o.Ctx.Input.Header("X-Parse-Application-Id")
	info.MasterKey = o.Ctx.Input.Header("X-Parse-Master-Key")
	info.ClientKey = o.Ctx.Input.Header("X-Parse-Client-Key")
	info.SessionToken = o.Ctx.Input.Header("X-Parse-Session-Token")
	info.InstallationID = o.Ctx.Input.Header("X-Parse-Installation-Id")

	if o.Ctx.Input.RequestBody != nil {
		contentType := o.Ctx.Input.Header("Content-type")
		if strings.HasPrefix(contentType, "application/json") {
			// 请求数据为 json 格式，进行转换，转换出错则返回错误
			var object types.M
			err := json.Unmarshal(o.Ctx.Input.RequestBody, &object)
			if err != nil {
				o.Data["json"] = errs.ErrorMessageToMap(errs.InvalidJSON, "invalid JSON")
				o.ServeJSON()
				return
			}
			o.JSONBody = object
		} else {
			// TODO 转换 json 之前，可能需要判断一下数据大小，以确保不会去转换超大数据
			// 其他格式的请求数据，仅尝试转换，转换失败不返回错误
			var object types.M
			err := json.Unmarshal(o.Ctx.Input.RequestBody, &object)
			if err != nil {
				o.RawBody = o.Ctx.Input.RequestBody
			} else {
				o.JSONBody = object
			}
		}
	}

	if o.JSONBody != nil {
		// Unity SDK sends a _noBody key which needs to be removed.
		// Unclear at this point if action needs to be taken.
		delete(o.JSONBody, "_noBody")
	}

	if info.AppID == "" {
		// 从请求数据中获取各种 key
		if o.JSONBody != nil && o.JSONBody["_ApplicationId"] != nil {
			info.AppID = o.JSONBody["_ApplicationId"].(string)
			delete(o.JSONBody, "_ApplicationId")
			if o.JSONBody["_ClientKey"] != nil {
				info.ClientKey = o.JSONBody["_ClientKey"].(string)
				delete(o.JSONBody, "_ClientKey")
			}
			if o.JSONBody["_InstallationId"] != nil {
				info.InstallationID = o.JSONBody["_InstallationId"].(string)
				delete(o.JSONBody, "_InstallationId")
			}
			if o.JSONBody["_SessionToken"] != nil {
				info.SessionToken = o.JSONBody["_SessionToken"].(string)
				delete(o.JSONBody, "_SessionToken")
			}
			if o.JSONBody["_MasterKey"] != nil {
				info.MasterKey = o.JSONBody["_MasterKey"].(string)
				delete(o.JSONBody, "_MasterKey")
			}
		} else {
			// 请求数据中也不存在 APPID 时，返回错误
			o.Data["json"] = errs.ErrorMessageToMap(403, "unauthorized")
			o.Ctx.Output.SetStatus(403)
			o.ServeJSON()
			return
		}
	}

	if o.JSONBody != nil && o.JSONBody["base64"] != nil {
		// 请求数据中存在 base64 字段，表明为文件上传，解码并设置到 RawBody 上
		data, err := base64.StdEncoding.DecodeString(o.JSONBody["base64"].(string))
		if err == nil {
			o.RawBody = data
		}
	}

	o.Info = info

	// 校验请求权限
	if info.AppID != config.TConfig.AppID {
		o.Data["json"] = errs.ErrorMessageToMap(403, "unauthorized")
		o.Ctx.Output.SetStatus(403)
		o.ServeJSON()
		return
	}
	if info.MasterKey == config.TConfig.MasterKey {
		o.Auth = &rest.Auth{InstallationID: info.InstallationID, IsMaster: true}
		return
	}
	if info.ClientKey != config.TConfig.ClientKey {
		o.Data["json"] = errs.ErrorMessageToMap(403, "unauthorized")
		o.Ctx.Output.SetStatus(403)
		o.ServeJSON()
		return
	}
	// 生成当前会话用户权限信息
	if info.SessionToken == "" {
		o.Auth = &rest.Auth{InstallationID: info.InstallationID, IsMaster: false}
	} else {
		o.Auth = rest.GetAuthForSessionToken(info.SessionToken, info.InstallationID)
	}
}

// HandleCreate 处理对象创建请求，返回对象 id 与对象位置
// @router /:className [post]
func (o *ObjectsController) HandleCreate() {

	if o.ClassName == "" {
		o.ClassName = o.Ctx.Input.Param(":className")
	}

	if o.JSONBody == nil {
		o.Data["json"] = errs.ErrorMessageToMap(errs.InvalidJSON, "request body is empty")
		o.ServeJSON()
		return
	}

	result, err := rest.Create(o.Auth, o.ClassName, o.JSONBody)
	if err != nil {
		o.Data["json"] = errs.ErrorToMap(err)
		o.ServeJSON()
		return
	}

	o.Data["json"] = result["response"]
	o.Ctx.Output.SetStatus(201)
	o.Ctx.Output.Header("location", result["location"].(string))
	o.ServeJSON()

}

// HandleGet 处理查询指定对象请求，返回查询到的对象
// @router /:className/:objectId [get]
func (o *ObjectsController) HandleGet() {
	if o.ClassName == "" {
		o.ClassName = o.Ctx.Input.Param(":className")
	}
	if o.ObjectID == "" {
		o.ObjectID = o.Ctx.Input.Param(":objectId")
	}

	options := types.M{}
	where := types.M{"objectId": o.ObjectID}
	response, err := rest.Find(o.Auth, o.ClassName, where, options)

	if err != nil {
		o.Data["json"] = errs.ErrorToMap(err)
		o.ServeJSON()
		return
	}

	results := utils.SliceInterface(response["results"])
	if results == nil && len(results) == 0 {
		o.Data["json"] = errs.ErrorMessageToMap(errs.ObjectNotFound, "Object not found.")
		o.ServeJSON()
		return
	}

	result := utils.MapInterface(results[0])
	if o.ClassName == "_User" {
		delete(result, "sessionToken")
		if o.Auth.User != nil && result["objectId"].(string) == o.Auth.User["objectId"].(string) {
			// 重新设置 session token
			result["sessionToken"] = o.Info.SessionToken
		}
	}

	o.Data["json"] = result
	o.ServeJSON()

}

// HandleUpdate 处理更新指定对象请求
// @router /:className/:objectId [put]
func (o *ObjectsController) HandleUpdate() {

	if o.ClassName == "" {
		o.ClassName = o.Ctx.Input.Param(":className")
	}
	if o.ObjectID == "" {
		o.ObjectID = o.Ctx.Input.Param(":objectId")
	}

	if o.JSONBody == nil {
		o.Data["json"] = errs.ErrorMessageToMap(errs.InvalidJSON, "request body is empty")
		o.ServeJSON()
		return
	}

	result, err := rest.Update(o.Auth, o.ClassName, o.ObjectID, o.JSONBody)
	if err != nil {
		o.Data["json"] = errs.ErrorToMap(err)
		o.ServeJSON()
		return
	}

	o.Data["json"] = result["response"]
	o.ServeJSON()

}

// HandleFind 处理查找对象请求
// @router /:className [get]
func (o *ObjectsController) HandleFind() {
	if o.ClassName == "" {
		o.ClassName = o.Ctx.Input.Param(":className")
	}

	// 获取查询参数，并组装
	options := types.M{}
	if o.GetString("skip") != "" {
		if i, err := strconv.Atoi(o.GetString("skip")); err == nil {
			options["skip"] = i
		} else {
			o.Data["json"] = errs.ErrorMessageToMap(errs.InvalidQuery, "skip should be int")
			o.ServeJSON()
			return
		}
	}
	if o.GetString("limit") != "" {
		if i, err := strconv.Atoi(o.GetString("limit")); err == nil {
			options["limit"] = i
		} else {
			o.Data["json"] = errs.ErrorMessageToMap(errs.InvalidQuery, "limit should be int")
			o.ServeJSON()
			return
		}
	} else {
		options["limit"] = 100
	}
	if o.GetString("order") != "" {
		options["order"] = o.GetString("order")
	}
	if o.GetString("count") != "" {
		options["count"] = true
	}
	if o.GetString("keys") != "" {
		options["keys"] = o.GetString("keys")
	}
	if o.GetString("include") != "" {
		options["include"] = o.GetString("include")
	}
	if o.GetString("redirectClassNameForKey") != "" {
		options["redirectClassNameForKey"] = o.GetString("redirectClassNameForKey")
	}

	where := types.M{}
	if o.GetString("where") != "" {
		err := json.Unmarshal([]byte(o.GetString("where")), &where)
		if err != nil {
			o.Data["json"] = errs.ErrorMessageToMap(errs.InvalidJSON, "where should be valid json")
			o.ServeJSON()
			return
		}
	}

	response, err := rest.Find(o.Auth, o.ClassName, where, options)
	if err != nil {
		o.Data["json"] = errs.ErrorToMap(err)
		o.ServeJSON()
		return
	}
	if utils.HasResults(response) {
		results := utils.SliceInterface(response["results"])
		for _, v := range results {
			result := utils.MapInterface(v)
			if result["sessionToken"] != nil && o.Info.SessionToken != "" {
				result["sessionToken"] = o.Info.SessionToken
			}
		}
	}

	o.Data["json"] = response
	o.ServeJSON()
}

// HandleDelete 处理删除指定对象请求
// @router /:className/:objectId [delete]
func (o *ObjectsController) HandleDelete() {

	if o.ClassName == "" {
		o.ClassName = o.Ctx.Input.Param(":className")
	}
	if o.ObjectID == "" {
		o.ObjectID = o.Ctx.Input.Param(":objectId")
	}

	err := rest.Delete(o.Auth, o.ClassName, o.ObjectID)
	if err != nil {
		o.Data["json"] = errs.ErrorToMap(err)
		o.ServeJSON()
		return
	}

	o.Data["json"] = types.M{}
	o.ServeJSON()
}

// Get ...
// @router / [get]
func (o *ObjectsController) Get() {
	o.Ctx.Output.SetStatus(405)
	o.Data["json"] = errs.ErrorMessageToMap(405, "Method Not Allowed")
	o.ServeJSON()
}

// Post ...
// @router / [post]
func (o *ObjectsController) Post() {
	o.Ctx.Output.SetStatus(405)
	o.Data["json"] = errs.ErrorMessageToMap(405, "Method Not Allowed")
	o.ServeJSON()
}

// Delete ...
// @router / [delete]
func (o *ObjectsController) Delete() {
	o.Ctx.Output.SetStatus(405)
	o.Data["json"] = errs.ErrorMessageToMap(405, "Method Not Allowed")
	o.ServeJSON()
}

// Put ...
// @router / [put]
func (o *ObjectsController) Put() {
	o.Ctx.Output.SetStatus(405)
	o.Data["json"] = errs.ErrorMessageToMap(405, "Method Not Allowed")
	o.ServeJSON()
}
<commit_msg>允许在查询单个对象时应用 keys 与 include 参数<commit_after>package controllers

import (
	"encoding/base64"
	"encoding/json"
	"strconv"
	"strings"

	"github.com/astaxie/beego"
	"github.com/lfq7413/tomato/config"
	"github.com/lfq7413/tomato/errs"
	"github.com/lfq7413/tomato/rest"
	"github.com/lfq7413/tomato/types"
	"github.com/lfq7413/tomato/utils"
)

// ObjectsController 对象操作 API 的基础结构
// 处理 /classes 接口的所有请求，处理内部类的部分请求
// Info 当前请求的权限信息
// Auth 当前请求的用户权限
// JSONBody 由 JSON 格式转换来的请求数据
// RawBody 原始请求数据
// ClassName 要操作的类名
// ObjectID 要操作的对象 id
type ObjectsController struct {
	beego.Controller
	Info      *RequestInfo
	Auth      *rest.Auth
	JSONBody  types.M
	RawBody   []byte
	ClassName string
	ObjectID  string
}

// RequestInfo http 请求的权限信息
type RequestInfo struct {
	AppID          string
	MasterKey      string
	ClientKey      string
	SessionToken   string
	InstallationID string
}

// Prepare 对请求权限进行处理
// 1. 从请求头中获取各种 key
// 2. 尝试按 json 格式转换 body
// 3. 尝试从 body 中获取各种 key
// 4. 校验请求权限
// 5. 生成用户信息
func (o *ObjectsController) Prepare() {
	info := &RequestInfo{}
	info.AppID = o.Ctx.Input.Header("X-Parse-Application-Id")
	info.MasterKey = o.Ctx.Input.Header("X-Parse-Master-Key")
	info.ClientKey = o.Ctx.Input.Header("X-Parse-Client-Key")
	info.SessionToken = o.Ctx.Input.Header("X-Parse-Session-Token")
	info.InstallationID = o.Ctx.Input.Header("X-Parse-Installation-Id")

	if o.Ctx.Input.RequestBody != nil {
		contentType := o.Ctx.Input.Header("Content-type")
		if strings.HasPrefix(contentType, "application/json") {
			// 请求数据为 json 格式，进行转换，转换出错则返回错误
			var object types.M
			err := json.Unmarshal(o.Ctx.Input.RequestBody, &object)
			if err != nil {
				o.Data["json"] = errs.ErrorMessageToMap(errs.InvalidJSON, "invalid JSON")
				o.ServeJSON()
				return
			}
			o.JSONBody = object
		} else {
			// TODO 转换 json 之前，可能需要判断一下数据大小，以确保不会去转换超大数据
			// 其他格式的请求数据，仅尝试转换，转换失败不返回错误
			var object types.M
			err := json.Unmarshal(o.Ctx.Input.RequestBody, &object)
			if err != nil {
				o.RawBody = o.Ctx.Input.RequestBody
			} else {
				o.JSONBody = object
			}
		}
	}

	if o.JSONBody != nil {
		// Unity SDK sends a _noBody key which needs to be removed.
		// Unclear at this point if action needs to be taken.
		delete(o.JSONBody, "_noBody")
	}

	if info.AppID == "" {
		// 从请求数据中获取各种 key
		if o.JSONBody != nil && o.JSONBody["_ApplicationId"] != nil {
			info.AppID = o.JSONBody["_ApplicationId"].(string)
			delete(o.JSONBody, "_ApplicationId")
			if o.JSONBody["_ClientKey"] != nil {
				info.ClientKey = o.JSONBody["_ClientKey"].(string)
				delete(o.JSONBody, "_ClientKey")
			}
			if o.JSONBody["_InstallationId"] != nil {
				info.InstallationID = o.JSONBody["_InstallationId"].(string)
				delete(o.JSONBody, "_InstallationId")
			}
			if o.JSONBody["_SessionToken"] != nil {
				info.SessionToken = o.JSONBody["_SessionToken"].(string)
				delete(o.JSONBody, "_SessionToken")
			}
			if o.JSONBody["_MasterKey"] != nil {
				info.MasterKey = o.JSONBody["_MasterKey"].(string)
				delete(o.JSONBody, "_MasterKey")
			}
		} else {
			// 请求数据中也不存在 APPID 时，返回错误
			o.Data["json"] = errs.ErrorMessageToMap(403, "unauthorized")
			o.Ctx.Output.SetStatus(403)
			o.ServeJSON()
			return
		}
	}

	if o.JSONBody != nil && o.JSONBody["base64"] != nil {
		// 请求数据中存在 base64 字段，表明为文件上传，解码并设置到 RawBody 上
		data, err := base64.StdEncoding.DecodeString(o.JSONBody["base64"].(string))
		if err == nil {
			o.RawBody = data
		}
	}

	o.Info = info

	// 校验请求权限
	if info.AppID != config.TConfig.AppID {
		o.Data["json"] = errs.ErrorMessageToMap(403, "unauthorized")
		o.Ctx.Output.SetStatus(403)
		o.ServeJSON()
		return
	}
	if info.MasterKey == config.TConfig.MasterKey {
		o.Auth = &rest.Auth{InstallationID: info.InstallationID, IsMaster: true}
		return
	}
	if info.ClientKey != config.TConfig.ClientKey {
		o.Data["json"] = errs.ErrorMessageToMap(403, "unauthorized")
		o.Ctx.Output.SetStatus(403)
		o.ServeJSON()
		return
	}
	// 生成当前会话用户权限信息
	if info.SessionToken == "" {
		o.Auth = &rest.Auth{InstallationID: info.InstallationID, IsMaster: false}
	} else {
		o.Auth = rest.GetAuthForSessionToken(info.SessionToken, info.InstallationID)
	}
}

// HandleCreate 处理对象创建请求，返回对象 id 与对象位置
// @router /:className [post]
func (o *ObjectsController) HandleCreate() {

	if o.ClassName == "" {
		o.ClassName = o.Ctx.Input.Param(":className")
	}

	if o.JSONBody == nil {
		o.Data["json"] = errs.ErrorMessageToMap(errs.InvalidJSON, "request body is empty")
		o.ServeJSON()
		return
	}

	result, err := rest.Create(o.Auth, o.ClassName, o.JSONBody)
	if err != nil {
		o.Data["json"] = errs.ErrorToMap(err)
		o.ServeJSON()
		return
	}

	o.Data["json"] = result["response"]
	o.Ctx.Output.SetStatus(201)
	o.Ctx.Output.Header("location", result["location"].(string))
	o.ServeJSON()

}

// HandleGet 处理查询指定对象请求，返回查询到的对象
// @router /:className/:objectId [get]
func (o *ObjectsController) HandleGet() {
	if o.ClassName == "" {
		o.ClassName = o.Ctx.Input.Param(":className")
	}
	if o.ObjectID == "" {
		o.ObjectID = o.Ctx.Input.Param(":objectId")
	}
	options := types.M{}
	if o.GetString("keys") != "" {
		options["keys"] = o.GetString("keys")
	}
	if o.GetString("include") != "" {
		options["include"] = o.GetString("include")
	}
	where := types.M{"objectId": o.ObjectID}
	response, err := rest.Find(o.Auth, o.ClassName, where, options)

	if err != nil {
		o.Data["json"] = errs.ErrorToMap(err)
		o.ServeJSON()
		return
	}

	results := utils.SliceInterface(response["results"])
	if results == nil && len(results) == 0 {
		o.Data["json"] = errs.ErrorMessageToMap(errs.ObjectNotFound, "Object not found.")
		o.ServeJSON()
		return
	}

	result := utils.MapInterface(results[0])
	if o.ClassName == "_User" {
		delete(result, "sessionToken")
		if o.Auth.User != nil && result["objectId"].(string) == o.Auth.User["objectId"].(string) {
			// 重新设置 session token
			result["sessionToken"] = o.Info.SessionToken
		}
	}

	o.Data["json"] = result
	o.ServeJSON()

}

// HandleUpdate 处理更新指定对象请求
// @router /:className/:objectId [put]
func (o *ObjectsController) HandleUpdate() {

	if o.ClassName == "" {
		o.ClassName = o.Ctx.Input.Param(":className")
	}
	if o.ObjectID == "" {
		o.ObjectID = o.Ctx.Input.Param(":objectId")
	}

	if o.JSONBody == nil {
		o.Data["json"] = errs.ErrorMessageToMap(errs.InvalidJSON, "request body is empty")
		o.ServeJSON()
		return
	}

	result, err := rest.Update(o.Auth, o.ClassName, o.ObjectID, o.JSONBody)
	if err != nil {
		o.Data["json"] = errs.ErrorToMap(err)
		o.ServeJSON()
		return
	}

	o.Data["json"] = result["response"]
	o.ServeJSON()

}

// HandleFind 处理查找对象请求
// @router /:className [get]
func (o *ObjectsController) HandleFind() {
	if o.ClassName == "" {
		o.ClassName = o.Ctx.Input.Param(":className")
	}

	// 获取查询参数，并组装
	options := types.M{}
	if o.GetString("skip") != "" {
		if i, err := strconv.Atoi(o.GetString("skip")); err == nil {
			options["skip"] = i
		} else {
			o.Data["json"] = errs.ErrorMessageToMap(errs.InvalidQuery, "skip should be int")
			o.ServeJSON()
			return
		}
	}
	if o.GetString("limit") != "" {
		if i, err := strconv.Atoi(o.GetString("limit")); err == nil {
			options["limit"] = i
		} else {
			o.Data["json"] = errs.ErrorMessageToMap(errs.InvalidQuery, "limit should be int")
			o.ServeJSON()
			return
		}
	} else {
		options["limit"] = 100
	}
	if o.GetString("order") != "" {
		options["order"] = o.GetString("order")
	}
	if o.GetString("count") != "" {
		options["count"] = true
	}
	if o.GetString("keys") != "" {
		options["keys"] = o.GetString("keys")
	}
	if o.GetString("include") != "" {
		options["include"] = o.GetString("include")
	}
	if o.GetString("redirectClassNameForKey") != "" {
		options["redirectClassNameForKey"] = o.GetString("redirectClassNameForKey")
	}

	where := types.M{}
	if o.GetString("where") != "" {
		err := json.Unmarshal([]byte(o.GetString("where")), &where)
		if err != nil {
			o.Data["json"] = errs.ErrorMessageToMap(errs.InvalidJSON, "where should be valid json")
			o.ServeJSON()
			return
		}
	}

	response, err := rest.Find(o.Auth, o.ClassName, where, options)
	if err != nil {
		o.Data["json"] = errs.ErrorToMap(err)
		o.ServeJSON()
		return
	}
	if utils.HasResults(response) {
		results := utils.SliceInterface(response["results"])
		for _, v := range results {
			result := utils.MapInterface(v)
			if result["sessionToken"] != nil && o.Info.SessionToken != "" {
				result["sessionToken"] = o.Info.SessionToken
			}
		}
	}

	o.Data["json"] = response
	o.ServeJSON()
}

// HandleDelete 处理删除指定对象请求
// @router /:className/:objectId [delete]
func (o *ObjectsController) HandleDelete() {

	if o.ClassName == "" {
		o.ClassName = o.Ctx.Input.Param(":className")
	}
	if o.ObjectID == "" {
		o.ObjectID = o.Ctx.Input.Param(":objectId")
	}

	err := rest.Delete(o.Auth, o.ClassName, o.ObjectID)
	if err != nil {
		o.Data["json"] = errs.ErrorToMap(err)
		o.ServeJSON()
		return
	}

	o.Data["json"] = types.M{}
	o.ServeJSON()
}

// Get ...
// @router / [get]
func (o *ObjectsController) Get() {
	o.Ctx.Output.SetStatus(405)
	o.Data["json"] = errs.ErrorMessageToMap(405, "Method Not Allowed")
	o.ServeJSON()
}

// Post ...
// @router / [post]
func (o *ObjectsController) Post() {
	o.Ctx.Output.SetStatus(405)
	o.Data["json"] = errs.ErrorMessageToMap(405, "Method Not Allowed")
	o.ServeJSON()
}

// Delete ...
// @router / [delete]
func (o *ObjectsController) Delete() {
	o.Ctx.Output.SetStatus(405)
	o.Data["json"] = errs.ErrorMessageToMap(405, "Method Not Allowed")
	o.ServeJSON()
}

// Put ...
// @router / [put]
func (o *ObjectsController) Put() {
	o.Ctx.Output.SetStatus(405)
	o.Data["json"] = errs.ErrorMessageToMap(405, "Method Not Allowed")
	o.ServeJSON()
}
<|endoftext|>