<commit_before>package aws

import (
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSGroupMembership_basic(t *testing.T) {
	var group iam.GetGroupOutput

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSGroupMembershipDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSGroupMemberConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSGroupMembershipExists("aws_iam_group_membership.team", &group),
					testAccCheckAWSGroupMembershipAttributes(&group, []string{"test-user"}),
				),
			},

			resource.TestStep{
				Config: testAccAWSGroupMemberConfigUpdate,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSGroupMembershipExists("aws_iam_group_membership.team", &group),
					testAccCheckAWSGroupMembershipAttributes(&group, []string{"test-user-two", "test-user-three"}),
				),
			},

			resource.TestStep{
				Config: testAccAWSGroupMemberConfigUpdateDown,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSGroupMembershipExists("aws_iam_group_membership.team", &group),
					testAccCheckAWSGroupMembershipAttributes(&group, []string{"test-user-three"}),
				),
			},
		},
	})
}

func testAccCheckAWSGroupMembershipDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).iamconn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_iam_group_membership" {
			continue
		}

		group := rs.Primary.Attributes["group"]

		resp, err := conn.GetGroup(&iam.GetGroupInput{
			GroupName: aws.String(group),
		})
		if err != nil {
			// might error here
			return err
		}

		users := []string{"test-user", "test-user-two", "test-user-three"}
		for _, u := range resp.Users {
			for _, i := range users {
				if i == *u.UserName {
					return fmt.Errorf("Error: User (%s) still a member of Group (%s)", i, *resp.Group.GroupName)
				}
			}
		}

	}

	return nil
}

func testAccCheckAWSGroupMembershipExists(n string, g *iam.GetGroupOutput) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No User name is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).iamconn
		gn := rs.Primary.Attributes["group"]

		resp, err := conn.GetGroup(&iam.GetGroupInput{
			GroupName: aws.String(gn),
		})

		if err != nil {
			return fmt.Errorf("Error: Group (%s) not found", gn)
		}

		*g = *resp

		return nil
	}
}

func testAccCheckAWSGroupMembershipAttributes(group *iam.GetGroupOutput, users []string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		if *group.Group.GroupName != "test-group" {
			return fmt.Errorf("Bad group membership: expected %s, got %s", "test-group", *group.Group.GroupName)
		}

		uc := len(users)
		for _, u := range users {
			for _, gu := range group.Users {
				if u == *gu.UserName {
					uc--
				}
			}
		}

		if uc > 0 {
			return fmt.Errorf("Bad group membership count, expected (%d), but only (%d) found", len(users), uc)
		}
		return nil
	}
}

const testAccAWSGroupMemberConfig = `
resource "aws_iam_group" "group" {
	name = "test-group"
	path = "/"
}

resource "aws_iam_user" "user" {
	name = "test-user"
	path = "/"
}

resource "aws_iam_group_membership" "team" {
	name = "tf-testing-group-membership"
	users = ["${aws_iam_user.user.name}"]
	group = "${aws_iam_group.group.name}"
}
`

const testAccAWSGroupMemberConfigUpdate = `
resource "aws_iam_group" "group" {
	name = "test-group"
	path = "/"
}

resource "aws_iam_user" "user" {
	name = "test-user"
	path = "/"
}

resource "aws_iam_user" "user_two" {
	name = "test-user-two"
	path = "/"
}

resource "aws_iam_user" "user_three" {
	name = "test-user-three"
	path = "/"
}

resource "aws_iam_group_membership" "team" {
	name = "tf-testing-group-membership"
	users = [
		"${aws_iam_user.user_two.name}",
		"${aws_iam_user.user_three.name}",
	]
	group = "${aws_iam_group.group.name}"
}
`

const testAccAWSGroupMemberConfigUpdateDown = `
resource "aws_iam_group" "group" {
	name = "test-group"
	path = "/"
}

resource "aws_iam_user" "user_three" {
	name = "test-user-three"
	path = "/"
}

resource "aws_iam_group_membership" "team" {
	name = "tf-testing-group-membership"
	users = [
		"${aws_iam_user.user_three.name}",
	]
	group = "${aws_iam_group.group.name}"
}
`
<commit_msg>provider/aws: fix CheckDestroy for group_membership test<commit_after>package aws

import (
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSGroupMembership_basic(t *testing.T) {
	var group iam.GetGroupOutput

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSGroupMembershipDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSGroupMemberConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSGroupMembershipExists("aws_iam_group_membership.team", &group),
					testAccCheckAWSGroupMembershipAttributes(&group, []string{"test-user"}),
				),
			},

			resource.TestStep{
				Config: testAccAWSGroupMemberConfigUpdate,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSGroupMembershipExists("aws_iam_group_membership.team", &group),
					testAccCheckAWSGroupMembershipAttributes(&group, []string{"test-user-two", "test-user-three"}),
				),
			},

			resource.TestStep{
				Config: testAccAWSGroupMemberConfigUpdateDown,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSGroupMembershipExists("aws_iam_group_membership.team", &group),
					testAccCheckAWSGroupMembershipAttributes(&group, []string{"test-user-three"}),
				),
			},
		},
	})
}

func testAccCheckAWSGroupMembershipDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).iamconn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_iam_group_membership" {
			continue
		}

		group := rs.Primary.Attributes["group"]

		_, err := conn.GetGroup(&iam.GetGroupInput{
			GroupName: aws.String(group),
		})
		if err != nil {
			// Verify the error is what we want
			if ae, ok := err.(awserr.Error); ok && ae.Code() == "NoSuchEntity" {
				continue
			}
			return err
		}

		return fmt.Errorf("still exists")
	}

	return nil
}

func testAccCheckAWSGroupMembershipExists(n string, g *iam.GetGroupOutput) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No User name is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).iamconn
		gn := rs.Primary.Attributes["group"]

		resp, err := conn.GetGroup(&iam.GetGroupInput{
			GroupName: aws.String(gn),
		})

		if err != nil {
			return fmt.Errorf("Error: Group (%s) not found", gn)
		}

		*g = *resp

		return nil
	}
}

func testAccCheckAWSGroupMembershipAttributes(group *iam.GetGroupOutput, users []string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		if *group.Group.GroupName != "test-group" {
			return fmt.Errorf("Bad group membership: expected %s, got %s", "test-group", *group.Group.GroupName)
		}

		uc := len(users)
		for _, u := range users {
			for _, gu := range group.Users {
				if u == *gu.UserName {
					uc--
				}
			}
		}

		if uc > 0 {
			return fmt.Errorf("Bad group membership count, expected (%d), but only (%d) found", len(users), uc)
		}
		return nil
	}
}

const testAccAWSGroupMemberConfig = `
resource "aws_iam_group" "group" {
	name = "test-group"
	path = "/"
}

resource "aws_iam_user" "user" {
	name = "test-user"
	path = "/"
}

resource "aws_iam_group_membership" "team" {
	name = "tf-testing-group-membership"
	users = ["${aws_iam_user.user.name}"]
	group = "${aws_iam_group.group.name}"
}
`

const testAccAWSGroupMemberConfigUpdate = `
resource "aws_iam_group" "group" {
	name = "test-group"
	path = "/"
}

resource "aws_iam_user" "user" {
	name = "test-user"
	path = "/"
}

resource "aws_iam_user" "user_two" {
	name = "test-user-two"
	path = "/"
}

resource "aws_iam_user" "user_three" {
	name = "test-user-three"
	path = "/"
}

resource "aws_iam_group_membership" "team" {
	name = "tf-testing-group-membership"
	users = [
		"${aws_iam_user.user_two.name}",
		"${aws_iam_user.user_three.name}",
	]
	group = "${aws_iam_group.group.name}"
}
`

const testAccAWSGroupMemberConfigUpdateDown = `
resource "aws_iam_group" "group" {
	name = "test-group"
	path = "/"
}

resource "aws_iam_user" "user_three" {
	name = "test-user-three"
	path = "/"
}

resource "aws_iam_group_membership" "team" {
	name = "tf-testing-group-membership"
	users = [
		"${aws_iam_user.user_three.name}",
	]
	group = "${aws_iam_group.group.name}"
}
`
<|endoftext|>


<commit_before>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      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 localfilereader provides utility functions for reading files and permissions from the local filesystem.
package localfilereader

import (
	"context"
	"io"
	"os"
	"os/user"
	"strconv"
	"strings"
	"syscall"

	apb "github.com/google/localtoast/scannerlib/proto/api_go_proto"
)

// Flags for special UNIX permission bits.
const (
	SetuidFlag = 04000
	SetgidFlag = 02000
	StickyFlag = 01000
)

var (
	userIDLookup = newCachedIDLookup(func(id int) (string, error) {
		usr, err := user.LookupId(strconv.Itoa(id))
		if err != nil {
			return "", err
		}
		return usr.Username, nil
	})
	groupIDLookup = newCachedIDLookup(func(id int) (string, error) {
		grp, err := user.LookupGroupId(strconv.Itoa(id))
		if err != nil {
			return "", err
		}
		return grp.Name, nil
	})
)

type cachedIDLookup struct {
	lookupFunc func(int) (string, error)
	valueCache map[int]string
	errorCache map[int]error
}

func (l *cachedIDLookup) Lookup(id int) (string, error) {
	if val, ok := l.valueCache[id]; ok {
		return val, nil
	}
	if err, ok := l.errorCache[id]; ok {
		return "", err
	}
	val, err := l.lookupFunc(id)
	if err == nil {
		l.valueCache[id] = val
	} else {
		l.errorCache[id] = err
	}
	return val, err
}

func newCachedIDLookup(lookupFunc func(int) (string, error)) cachedIDLookup {
	return cachedIDLookup{
		lookupFunc: lookupFunc,
		valueCache: make(map[int]string),
		errorCache: make(map[int]error),
	}
}

// OpenFile opens the specified file for reading.
func OpenFile(ctx context.Context, path string) (io.ReadCloser, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	return file, nil
}

// FilesInDir lists the contents of the specified directory.
func FilesInDir(ctx context.Context, path string) ([]*apb.DirContent, error) {
	files, err := os.ReadDir(path)
	if err != nil {
		return nil, err
	}
	contents := make([]*apb.DirContent, 0, len(files))
	for _, f := range files {
		if f.IsDir() || f.Type().IsRegular() || f.Type()&os.ModeSymlink == os.ModeSymlink {
			contents = append(contents, &apb.DirContent{
				Name:      f.Name(),
				IsDir:     f.IsDir(),
				IsSymlink: f.Type()&os.ModeSymlink == os.ModeSymlink,
			})
		}
	}
	return contents, nil
}

// FilePermissions returns unix permission-related data for the specified file or directory.
func FilePermissions(ctx context.Context, path string) (*apb.PosixPermissions, error) {
	fi, err := os.Lstat(path)
	if err != nil {
		return nil, err
	}
	sys := fi.Sys()
	uid := int(sys.(*syscall.Stat_t).Uid)
	gid := int(sys.(*syscall.Stat_t).Gid)

	username, err := userIDLookup.Lookup(uid)
	if err != nil {
		// "unknown userid" means the file is unowned (uid not found
		// in /etc/group, possibly because the user got deleted). Leave
		// the username empty to signal this.
		if !strings.Contains(err.Error(), "unknown userid") {
			return nil, err
		}
	}

	groupname, err := groupIDLookup.Lookup(gid)
	if err != nil {
		// "unknown groupid" means the file is ungrouped (gid not found
		// in /etc/group, possibly because the group got deleted). Leave
		// the groupname empty to signal this.
		if !strings.Contains(err.Error(), "unknown groupid") {
			return nil, err
		}
	}

	perms := int32(fi.Mode().Perm())
	// Mode().Perm() only contains the regular permission bits, so add the
	// special flag bits separately.
	if fi.Mode()&os.ModeSetuid != 0 {
		perms |= SetuidFlag
	}
	if fi.Mode()&os.ModeSetgid != 0 {
		perms |= SetgidFlag
	}
	if fi.Mode()&os.ModeSticky != 0 {
		perms |= StickyFlag
	}
	return &apb.PosixPermissions{
		PermissionNum: perms,
		Uid:           int32(uid),
		User:          username,
		Gid:           int32(gid),
		Group:         groupname,
	}, nil
}
<commit_msg>Start using fs.Mode instead of os.Mode again.<commit_after>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      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 localfilereader provides utility functions for reading files and permissions from the local filesystem.
package localfilereader

import (
	"context"
	"io"
	"io/fs"
	"os"
	"os/user"
	"strconv"
	"strings"
	"syscall"

	apb "github.com/google/localtoast/scannerlib/proto/api_go_proto"
)

// Flags for special UNIX permission bits.
const (
	SetuidFlag = 04000
	SetgidFlag = 02000
	StickyFlag = 01000
)

var (
	userIDLookup = newCachedIDLookup(func(id int) (string, error) {
		usr, err := user.LookupId(strconv.Itoa(id))
		if err != nil {
			return "", err
		}
		return usr.Username, nil
	})
	groupIDLookup = newCachedIDLookup(func(id int) (string, error) {
		grp, err := user.LookupGroupId(strconv.Itoa(id))
		if err != nil {
			return "", err
		}
		return grp.Name, nil
	})
)

type cachedIDLookup struct {
	lookupFunc func(int) (string, error)
	valueCache map[int]string
	errorCache map[int]error
}

func (l *cachedIDLookup) Lookup(id int) (string, error) {
	if val, ok := l.valueCache[id]; ok {
		return val, nil
	}
	if err, ok := l.errorCache[id]; ok {
		return "", err
	}
	val, err := l.lookupFunc(id)
	if err == nil {
		l.valueCache[id] = val
	} else {
		l.errorCache[id] = err
	}
	return val, err
}

func newCachedIDLookup(lookupFunc func(int) (string, error)) cachedIDLookup {
	return cachedIDLookup{
		lookupFunc: lookupFunc,
		valueCache: make(map[int]string),
		errorCache: make(map[int]error),
	}
}

// OpenFile opens the specified file for reading.
func OpenFile(ctx context.Context, path string) (io.ReadCloser, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	return file, nil
}

// FilesInDir lists the contents of the specified directory.
func FilesInDir(ctx context.Context, path string) ([]*apb.DirContent, error) {
	files, err := os.ReadDir(path)
	if err != nil {
		return nil, err
	}
	contents := make([]*apb.DirContent, 0, len(files))
	for _, f := range files {
		if f.IsDir() || f.Type().IsRegular() || f.Type()&fs.ModeSymlink == fs.ModeSymlink {
			contents = append(contents, &apb.DirContent{
				Name:      f.Name(),
				IsDir:     f.IsDir(),
				IsSymlink: f.Type()&fs.ModeSymlink == fs.ModeSymlink,
			})
		}
	}
	return contents, nil
}

// FilePermissions returns unix permission-related data for the specified file or directory.
func FilePermissions(ctx context.Context, path string) (*apb.PosixPermissions, error) {
	fi, err := os.Lstat(path)
	if err != nil {
		return nil, err
	}
	sys := fi.Sys()
	uid := int(sys.(*syscall.Stat_t).Uid)
	gid := int(sys.(*syscall.Stat_t).Gid)

	username, err := userIDLookup.Lookup(uid)
	if err != nil {
		// "unknown userid" means the file is unowned (uid not found
		// in /etc/group, possibly because the user got deleted). Leave
		// the username empty to signal this.
		if !strings.Contains(err.Error(), "unknown userid") {
			return nil, err
		}
	}

	groupname, err := groupIDLookup.Lookup(gid)
	if err != nil {
		// "unknown groupid" means the file is ungrouped (gid not found
		// in /etc/group, possibly because the group got deleted). Leave
		// the groupname empty to signal this.
		if !strings.Contains(err.Error(), "unknown groupid") {
			return nil, err
		}
	}

	perms := int32(fi.Mode().Perm())
	// Mode().Perm() only contains the regular permission bits, so add the
	// special flag bits separately.
	if fi.Mode()&fs.ModeSetuid != 0 {
		perms |= SetuidFlag
	}
	if fi.Mode()&fs.ModeSetgid != 0 {
		perms |= SetgidFlag
	}
	if fi.Mode()&fs.ModeSticky != 0 {
		perms |= StickyFlag
	}
	return &apb.PosixPermissions{
		PermissionNum: perms,
		Uid:           int32(uid),
		User:          username,
		Gid:           int32(gid),
		Group:         groupname,
	}, nil
}
<|endoftext|>


<commit_before>package types

import (
	"fmt"
)

//
// Recipe Creation Types
//

type Recipe struct {
	Title        string       `json:"title" validate:"recipetitle"`
	Notes        string       `json:"notes" validate:"recipenotes"`
	Ingredients  []Ingredient `json:"ingredients"`
	CookTime     int          `json:"cooktime" validate:"time"`
	CookTimeUnit string       `json:"cooktimeunit" validate:"timeunit"`
	PrepTime     int          `json:"preptime" validate:"time"`
	PrepTimeUnit string       `json:"prepunit" validate:"timeunit"`
	Steps        []Step       `json:"steps"`
	Tags         []Tag        `json:"tags"`
	Public       bool         `json:"public"`
}

//
// Recipe Component Types
//

type Ingredient struct {
	Name       string
	Amount     int
	AmountUnit string
	URL        string
}

type Step struct {
	Instruction string
	Time        int
	TimeUnit    string
}

//
// Recipe Tag Types
//

type Tag struct {
	Name string
}

func (v RicettaValidator) validateTimeUnit(i interface{}) error {
	timeUnit := i.(string)
	if timeUnit == "" {
		return fmt.Errorf("Required field")
	} else if !v.Constants.TIME_UNIT_REGEX.MatchString(timeUnit) {
		return fmt.Errorf(timeUnit + " is not a valid unit of time (secs-weeks)")
	} else {
		return nil
	}
}
<commit_msg>More JSON annotations for recipes<commit_after>package types

import (
	"fmt"
)

//
// Recipe Creation Types
//

type Recipe struct {
	Title        string       `json:"title" validate:"recipetitle"`
	Notes        string       `json:"notes" validate:"recipenotes"`
	Ingredients  []Ingredient `json:"ingredients"`
	CookTime     int          `json:"cooktime" validate:"time"`
	CookTimeUnit string       `json:"cooktimeunit" validate:"timeunit"`
	PrepTime     int          `json:"preptime" validate:"time"`
	PrepTimeUnit string       `json:"prepunit" validate:"timeunit"`
	Steps        []Step       `json:"steps"`
	Tags         []Tag        `json:"tags"`
	Public       bool         `json:"public"`
}

//
// Recipe Component Types
//

type Ingredient struct {
	Name       string `json:"name"`
	Amount     int    `json:"amount"`
	AmountUnit string `json:"amountunit"`
	URL        string `json:"url"`
}

type Step struct {
	Instruction string `json:"instruction"`
	Time        int    `json:"time"`
	TimeUnit    string `json:"timeunit" validate:"timeunit"`
}

//
// Recipe Tag Types
//

type Tag struct {
	Name string `json:"name"`
}

func (v RicettaValidator) validateTimeUnit(i interface{}) error {
	timeUnit := i.(string)
	if timeUnit == "" {
		return fmt.Errorf("Required field")
	} else if !v.Constants.TIME_UNIT_REGEX.MatchString(timeUnit) {
		return fmt.Errorf(timeUnit + " is not a valid unit of time (secs-weeks)")
	} else {
		return nil
	}
}
<|endoftext|>


<commit_before>package rest

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

	"github.com/codegangsta/negroni"
	"github.com/julienschmidt/httprouter"

	"github.com/intelsdi-x/pulse/core"
	cschedule "github.com/intelsdi-x/pulse/pkg/schedule"
	"github.com/intelsdi-x/pulse/scheduler/wmap"
)

type managesMetrics interface {
	MetricCatalog() ([]core.Metric, error)
	Load(string) error
	PluginCatalog() core.PluginCatalog
	AvailablePlugins() []core.AvailablePlugin
}

//todo remove this interface
type managesTasks interface {
	CreateTask(cschedule.Schedule, *wmap.WorkflowMap, ...core.TaskOption) (core.Task, core.TaskErrors)
	GetTasks() map[uint64]core.Task
	StartTask(id uint64) error
}

type Server struct {
	mm managesMetrics
	mt managesTasks
	n  *negroni.Negroni
	r  *httprouter.Router
}

func New() *Server {
	n := negroni.New(
		&negroni.Recovery{Logger: log.New(os.Stdout, "[pulse-rest] ", 0), PrintStack: true},
		&negroni.Logger{log.New(os.Stdout, "[pulse-rest] ", 0)},
	)
	return &Server{
		r: httprouter.New(),
		n: n,
	}

}

func (s *Server) Start(addrString string) {
	go s.start(addrString)
}

func (s *Server) run(addrString string) {
	log.Printf("[pulse-rest] listening on %s\n", addrString)
	http.ListenAndServe(addrString, s.n)
}

func (s *Server) BindMetricManager(m managesMetrics) {
	s.mm = m
}

func (s *Server) BindTaskManager(t managesTasks) {
	s.mt = t
}

func (s *Server) start(addrString string) {
	// plugin routes
	s.r.GET("/v1/plugins", s.getPlugins)
	s.r.GET("/v1/plugins/:name", s.getPluginsByName)
	s.r.GET("/v1/plugins/:name/:version", s.getPlugin)
	s.r.POST("/v1/plugins", s.loadPlugin)

	// metric routes
	s.r.GET("/v1/metrics", s.getMetrics)
	s.r.GET("/v1/metrics/*namespace", s.getMetricsFromTree)

	// task routes
	s.r.GET("/v1/tasks", s.getTasks)
	s.r.POST("/v1/tasks", s.addTask)
	s.r.PUT("/v1/tasks/:id/start", s.startTask)

	// set negroni router to the server's router (httprouter)
	s.n.UseHandler(s.r)
	// start http handling
	s.run(addrString)
}

type response struct {
	Meta *responseMeta          `json:"meta"`
	Data map[string]interface{} `json:"data"`
}

type responseMeta struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

func replyError(code int, w http.ResponseWriter, err error) {
	w.WriteHeader(code)
	resp := &response{
		Meta: &responseMeta{
			Code:    code,
			Message: err.Error(),
		},
	}
	jerr, _ := json.MarshalIndent(resp, "", "  ")
	fmt.Fprint(w, string(jerr))
}

func replySuccess(code int, w http.ResponseWriter, data map[string]interface{}) {
	w.WriteHeader(code)
	resp := &response{
		Meta: &responseMeta{
			Code: code,
		},
		Data: data,
	}
	j, err := json.MarshalIndent(resp, "", "  ")
	if err != nil {
		replyError(500, w, err)
		return
	}
	fmt.Fprint(w, string(j))
}

func marshalBody(in interface{}, body io.ReadCloser) (int, error) {
	b, err := ioutil.ReadAll(body)
	if err != nil {
		return 500, err
	}
	err = json.Unmarshal(b, in)
	if err != nil {
		return 400, err
	}
	return 0, nil
}

func parseNamespace(ns string) []string {
	if strings.Index(ns, "/") == 0 {
		return strings.Split(ns[1:], "/")
	}
	return strings.Split(ns, "/")
}

func joinNamespace(ns []string) string {
	return "/" + strings.Join(ns, "/")
}
<commit_msg>Fixes go vet error on rest<commit_after>package rest

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

	"github.com/codegangsta/negroni"
	"github.com/julienschmidt/httprouter"

	"github.com/intelsdi-x/pulse/core"
	cschedule "github.com/intelsdi-x/pulse/pkg/schedule"
	"github.com/intelsdi-x/pulse/scheduler/wmap"
)

type managesMetrics interface {
	MetricCatalog() ([]core.Metric, error)
	Load(string) error
	PluginCatalog() core.PluginCatalog
	AvailablePlugins() []core.AvailablePlugin
}

//todo remove this interface
type managesTasks interface {
	CreateTask(cschedule.Schedule, *wmap.WorkflowMap, ...core.TaskOption) (core.Task, core.TaskErrors)
	GetTasks() map[uint64]core.Task
	StartTask(id uint64) error
}

type Server struct {
	mm managesMetrics
	mt managesTasks
	n  *negroni.Negroni
	r  *httprouter.Router
}

func New() *Server {

	n := negroni.New(
		&negroni.Recovery{Logger: log.New(os.Stdout, "[pulse-rest] ", 0), PrintStack: true},
		&negroni.Logger{Logger: log.New(os.Stdout, "[pulse-rest] ", 0)},
	)
	return &Server{
		r: httprouter.New(),
		n: n,
	}

}

func (s *Server) Start(addrString string) {
	go s.start(addrString)
}

func (s *Server) run(addrString string) {
	log.Printf("[pulse-rest] listening on %s\n", addrString)
	http.ListenAndServe(addrString, s.n)
}

func (s *Server) BindMetricManager(m managesMetrics) {
	s.mm = m
}

func (s *Server) BindTaskManager(t managesTasks) {
	s.mt = t
}

func (s *Server) start(addrString string) {
	// plugin routes
	s.r.GET("/v1/plugins", s.getPlugins)
	s.r.GET("/v1/plugins/:name", s.getPluginsByName)
	s.r.GET("/v1/plugins/:name/:version", s.getPlugin)
	s.r.POST("/v1/plugins", s.loadPlugin)

	// metric routes
	s.r.GET("/v1/metrics", s.getMetrics)
	s.r.GET("/v1/metrics/*namespace", s.getMetricsFromTree)

	// task routes
	s.r.GET("/v1/tasks", s.getTasks)
	s.r.POST("/v1/tasks", s.addTask)
	s.r.PUT("/v1/tasks/:id/start", s.startTask)

	// set negroni router to the server's router (httprouter)
	s.n.UseHandler(s.r)
	// start http handling
	s.run(addrString)
}

type response struct {
	Meta *responseMeta          `json:"meta"`
	Data map[string]interface{} `json:"data"`
}

type responseMeta struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

func replyError(code int, w http.ResponseWriter, err error) {
	w.WriteHeader(code)
	resp := &response{
		Meta: &responseMeta{
			Code:    code,
			Message: err.Error(),
		},
	}
	jerr, _ := json.MarshalIndent(resp, "", "  ")
	fmt.Fprint(w, string(jerr))
}

func replySuccess(code int, w http.ResponseWriter, data map[string]interface{}) {
	w.WriteHeader(code)
	resp := &response{
		Meta: &responseMeta{
			Code: code,
		},
		Data: data,
	}
	j, err := json.MarshalIndent(resp, "", "  ")
	if err != nil {
		replyError(500, w, err)
		return
	}
	fmt.Fprint(w, string(j))
}

func marshalBody(in interface{}, body io.ReadCloser) (int, error) {
	b, err := ioutil.ReadAll(body)
	if err != nil {
		return 500, err
	}
	err = json.Unmarshal(b, in)
	if err != nil {
		return 400, err
	}
	return 0, nil
}

func parseNamespace(ns string) []string {
	if strings.Index(ns, "/") == 0 {
		return strings.Split(ns[1:], "/")
	}
	return strings.Split(ns, "/")
}

func joinNamespace(ns []string) string {
	return "/" + strings.Join(ns, "/")
}
<|endoftext|>


<commit_before>package valider

import (
	"reflect"
	"time"
)

type Map struct {
	raw    interface{}
	field  string
	errors *Errors

	value reflect.Value
}

func (v *Validator) Map(value interface{}, field string) *Map {
	return &Map{raw: value, field: field, errors: v.Errors}
}

func (ma *Map) Required() *Map {
	ma.value = reflect.ValueOf(ma.raw)
	if ma.value.Kind() == reflect.Ptr {
		ma.value = ma.value.Elem()
	}
	switch ma.value.Kind() {
	case reflect.Map:
		if ma.value.Len() == 0 {
			(*ma.errors)[ma.field] = append((*ma.errors)[ma.field], Error{ErrRequired, CodeRequired})
		}
	default:
		(*ma.errors)[ma.field] = append((*ma.errors)[ma.field], Error{ErrUnsupported, CodeUnsupported})
	}
	return ma
}

func (ma *Map) Keys(keys ...string) *Map {
	ma.value = reflect.ValueOf(ma.raw)
	if ma.value.Kind() == reflect.Ptr {
		ma.value = ma.value.Elem()
	}
	if ma.value.Len() != 0 {
		switch ma.value.Kind() {
		case reflect.Map:
			for _, key := range keys {
				if !ma.value.MapIndex(reflect.ValueOf(key)).IsValid() {
					(*ma.errors)[ma.field] = append((*ma.errors)[ma.field], Error{ErrNotFound, CodeNotFound})
				}
			}
		default:
			(*ma.errors)[ma.field] = append((*ma.errors)[ma.field], Error{ErrUnsupported, CodeUnsupported})
		}
	}
	return ma
}

func (ma *Map) Range(min, max int) *Map {
	ma.value = reflect.ValueOf(ma.raw)
	if ma.value.Kind() == reflect.Ptr {
		ma.value = ma.value.Elem()
	}
	if ma.value.Len() != 0 {
		switch ma.value.Kind() {
		case reflect.Map:
			len := ma.value.Len()
			if len < min || len > max {
				(*ma.errors)[ma.field] = append((*ma.errors)[ma.field], Error{ErrOutRange, CodeOutRange})
			}
		default:
			(*ma.errors)[ma.field] = append((*ma.errors)[ma.field], Error{ErrUnsupported, CodeUnsupported})
		}
	}
	return ma
}

func (ma *Map) In(values interface{}) *Map {
	return ma
}

func (ma *Map) Date(layout string) *Map {
	ma.value = reflect.ValueOf(ma.raw)
	if ma.value.Kind() == reflect.Ptr {
		ma.value = ma.value.Elem()
	}
	if ma.value.Len() != 0 {
		switch ma.value.Kind() {
		case reflect.Map:
			for _, key := range ma.value.MapKeys() {
				ma.value = ma.value.MapIndex(key)
				ma.date(layout)
			}
		default:
			(*ma.errors)[ma.field] = append((*ma.errors)[ma.field], Error{ErrUnsupported, CodeUnsupported})
		}
	}
	return ma
}

func (ma *Map) date(layout string) *Map {
	switch ma.value.Kind() {
	case reflect.Slice, reflect.Array:
		len := ma.value.Len()
		for i := 0; i < len; i++ {
			ma.value = ma.value.Index(i)
			ma.date(layout)
		}
	case reflect.String:
		if _, err := time.Parse(layout, ma.value.String()); err != nil {
			(*ma.errors)[ma.field] = append((*ma.errors)[ma.field], Error{ErrDate, CodeDate})
		}
	default:
		(*ma.errors)[ma.field] = append((*ma.errors)[ma.field], Error{ErrUnsupported, CodeUnsupported})
	}
	return ma
}
<commit_msg>add new functionality for map types<commit_after>package valider

import (
	"reflect"
	"time"
)

type Map struct {
	raw    interface{}
	field  string
	errors *Errors

	value reflect.Value
}

func (v *Validator) Map(value interface{}, field string) *Map {
	return &Map{raw: value, field: field, errors: v.Errors}
}

func (ma *Map) Required() *Map {
	ma.value = reflect.ValueOf(ma.raw)
	if ma.value.Kind() == reflect.Ptr {
		ma.value = ma.value.Elem()
	}
	switch ma.value.Kind() {
	case reflect.Map:
		if ma.value.Len() == 0 {
			(*ma.errors)[ma.field] = append((*ma.errors)[ma.field], Error{ErrRequired, CodeRequired})
		}
	default:
		(*ma.errors)[ma.field] = append((*ma.errors)[ma.field], Error{ErrUnsupported, CodeUnsupported})
	}
	return ma
}

func (ma *Map) SameKeys(keys ...string) *Map {
	ma.value = reflect.ValueOf(ma.raw)
	if ma.value.Kind() == reflect.Ptr {
		ma.value = ma.value.Elem()
	}
	if ma.value.Len() != 0 {
		switch ma.value.Kind() {
		case reflect.Map:
		for _, key := range keys {
			if !ma.value.MapIndex(reflect.ValueOf(key)).IsValid() {
				(*ma.errors)[ma.field+"."+key] = append((*ma.errors)[ma.field+"."+key], Error{ErrNotFound, CodeNotFound})
			}
		}
		default:
			(*ma.errors)[ma.field] = append((*ma.errors)[ma.field], Error{ErrUnsupported, CodeUnsupported})
		}
	}
	return ma
}

func (ma *Map) Keys(keys ...string) *Map {
	ma.value = reflect.ValueOf(ma.raw)
	if ma.value.Kind() == reflect.Ptr {
		ma.value = ma.value.Elem()
	}
	if ma.value.Len() != 0 {
		switch ma.value.Kind() {
		case reflect.Map:
			k := ma.value.MapKeys()
			for _, v := range k {
				found := false
				for _, key := range keys {
					if v.String() == key {
						found = true
					}
				}
				if !found {
					(*ma.errors)[ma.field] = append((*ma.errors)[ma.field], Error{ErrNotFound, CodeNotFound})
				}
			}
		default:
			(*ma.errors)[ma.field] = append((*ma.errors)[ma.field], Error{ErrUnsupported, CodeUnsupported})
		}
	}
	return ma
}

func (ma *Map) Range(min, max int) *Map {
	ma.value = reflect.ValueOf(ma.raw)
	if ma.value.Kind() == reflect.Ptr {
		ma.value = ma.value.Elem()
	}
	if ma.value.Len() != 0 {
		switch ma.value.Kind() {
		case reflect.Map:
			len := ma.value.Len()
			if len < min || len > max {
				(*ma.errors)[ma.field] = append((*ma.errors)[ma.field], Error{ErrOutRange, CodeOutRange})
			}
		default:
			(*ma.errors)[ma.field] = append((*ma.errors)[ma.field], Error{ErrUnsupported, CodeUnsupported})
		}
	}
	return ma
}

func (ma *Map) In(values interface{}) *Map {
	return ma
}

func (ma *Map) Date(layout string) *Map {
	ma.value = reflect.ValueOf(ma.raw)
	if ma.value.Kind() == reflect.Ptr {
		ma.value = ma.value.Elem()
	}
	if ma.value.Len() != 0 {
		switch ma.value.Kind() {
		case reflect.Map:
			for _, key := range ma.value.MapKeys() {
				ma.value = ma.value.MapIndex(key)
				ma.date(layout)
			}
		default:
			(*ma.errors)[ma.field] = append((*ma.errors)[ma.field], Error{ErrUnsupported, CodeUnsupported})
		}
	}
	return ma
}

func (ma *Map) date(layout string) *Map {
	switch ma.value.Kind() {
	case reflect.Slice, reflect.Array:
		len := ma.value.Len()
		for i := 0; i < len; i++ {
			ma.value = ma.value.Index(i)
			ma.date(layout)
		}
	case reflect.String:
		if _, err := time.Parse(layout, ma.value.String()); err != nil {
			(*ma.errors)[ma.field] = append((*ma.errors)[ma.field], Error{ErrDate, CodeDate})
		}
	default:
		(*ma.errors)[ma.field] = append((*ma.errors)[ma.field], Error{ErrUnsupported, CodeUnsupported})
	}
	return ma
}
<|endoftext|>


<commit_before>package statistics

import (
  "math"
)

func Max(slice []float64) (float64, error) {
  if len(slice) == 0 {
    return math.NaN(), EmptySlice
  }
  max := slice[0]
  for i := 1; i < len(slice); i++ {
    if slice[i] > max {
      max = slice[i]
    }
  }
  return max, nil
}
<commit_msg>Change the package name<commit_after>package gostats

import (
  "math"
)

func Max(slice []float64) (float64, error) {
  if len(slice) == 0 {
    return math.NaN(), EmptySlice
  }
  max := slice[0]
  for i := 1; i < len(slice); i++ {
    if slice[i] > max {
      max = slice[i]
    }
  }
  return max, nil
}
<|endoftext|>


<commit_before>package httpbase

import (
	"encoding/json"
	"github.com/reflexionhealth/vanilla/router"
)

const (
	unauthorizedMsg = "Access is denied due to invalid credentials"
	forbiddenMsg    = "You don't have permissions for this operation"
	notFoundMsg     = "No route for requested path"
	noMethodMsg     = "Requested path doesn't support that HTTP method"
)

// If HeaderRequestErrors is set, errors will additionally be sent in that header
var HeaderRequestErrors = "Request-Errors"

var (
	unauthorizedHeader = mustMakeErrorHeader(unauthorizedMsg)
	unauthorizedBody   = mustMakeErrorBody(unauthorizedMsg)
	forbiddenHeader    = mustMakeErrorHeader(forbiddenMsg)
	forbiddenBody      = mustMakeErrorBody(forbiddenMsg)
	notFoundHeader     = mustMakeErrorHeader(notFoundMsg)
	notFoundBody       = mustMakeErrorBody(notFoundMsg)
	noMethodHeader     = mustMakeErrorHeader(noMethodMsg)
	noMethodBody       = mustMakeErrorBody(noMethodMsg)
)

type RequestErrors struct {
	Errors []string `json:"errors"`
}

func mustMakeErrorHeader(errmsg string) string {
	headerBytes, err := json.Marshal([]string{errmsg})
	if err != nil {
		panic("unable to make error header")
	}

	return string(headerBytes)
}

func mustMakeErrorBody(errmsg string) string {
	bodyBytes, err := json.Marshal(RequestErrors{[]string{errmsg}})
	if err != nil {
		panic("unable to make error body")
	}

	return string(bodyBytes)
}

// Error sets Reflexion-Request-Errors and renders the errors json body
func Error(r *router.Response, status int, errmsg string) {
	if len(HeaderRequestErrors) > 0 {
		header := mustMakeErrorBody(errmsg)
		r.Header().Set(HeaderRequestErrors, body)
	}

	body := mustMakeErrorHeader(errmsg)
	r.JSON(status, header)
}

func StaticError(r *router.Response, status int, header string, body string) {
	if len(HeaderRequestErrors) > 0 {
		r.Header().Set(HeaderRequestErrors, header)
	}

	r.JSON(status, body)
}

func Unauthorized(r *router.Response) {
	StaticError(r, 401, unauthorizedHeader, unauthorizedBody)
}

func Forbidden(r *router.Response) {
	StaticError(r, 403, forbiddenHeader, forbiddenBody)
}

func NotFound(r *router.Response) {
	StaticError(r, 404, notFoundHeader, notFoundBody)
}

func NoMethod(r *router.Response) {
	StaticError(r, 405, noMethodHeader, noMethodBody)
}
<commit_msg>httpbase: fix typo in error renderer<commit_after>package httpbase

import (
	"encoding/json"
	"github.com/reflexionhealth/vanilla/router"
)

const (
	unauthorizedMsg = "Access is denied due to invalid credentials"
	forbiddenMsg    = "You don't have permissions for this operation"
	notFoundMsg     = "No route for requested path"
	noMethodMsg     = "Requested path doesn't support that HTTP method"
)

// If HeaderRequestErrors is set, errors will additionally be sent in that header
var HeaderRequestErrors = "Request-Errors"

var (
	unauthorizedHeader = mustMakeErrorHeader(unauthorizedMsg)
	unauthorizedBody   = mustMakeErrorBody(unauthorizedMsg)
	forbiddenHeader    = mustMakeErrorHeader(forbiddenMsg)
	forbiddenBody      = mustMakeErrorBody(forbiddenMsg)
	notFoundHeader     = mustMakeErrorHeader(notFoundMsg)
	notFoundBody       = mustMakeErrorBody(notFoundMsg)
	noMethodHeader     = mustMakeErrorHeader(noMethodMsg)
	noMethodBody       = mustMakeErrorBody(noMethodMsg)
)

type RequestErrors struct {
	Errors []string `json:"errors"`
}

func mustMakeErrorHeader(errmsg string) string {
	headerBytes, err := json.Marshal([]string{errmsg})
	if err != nil {
		panic("unable to make error header")
	}

	return string(headerBytes)
}

func mustMakeErrorBody(errmsg string) string {
	bodyBytes, err := json.Marshal(RequestErrors{[]string{errmsg}})
	if err != nil {
		panic("unable to make error body")
	}

	return string(bodyBytes)
}

// Error sets Reflexion-Request-Errors and renders the errors json body
func Error(r *router.Response, status int, errmsg string) {
	if len(HeaderRequestErrors) > 0 {
		header := mustMakeErrorHeader(errmsg)
		r.Header().Set(HeaderRequestErrors, header)
	}

	body := mustMakeErrorBody(errmsg)
	r.JSON(status, body)
}

func StaticError(r *router.Response, status int, header string, body string) {
	if len(HeaderRequestErrors) > 0 {
		r.Header().Set(HeaderRequestErrors, header)
	}

	r.JSON(status, body)
}

func Unauthorized(r *router.Response) {
	StaticError(r, 401, unauthorizedHeader, unauthorizedBody)
}

func Forbidden(r *router.Response) {
	StaticError(r, 403, forbiddenHeader, forbiddenBody)
}

func NotFound(r *router.Response) {
	StaticError(r, 404, notFoundHeader, notFoundBody)
}

func NoMethod(r *router.Response) {
	StaticError(r, 405, noMethodHeader, noMethodBody)
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 Liu Dong <ddliuhb@gmail.com>.
// Licensed under the MIT license.

// Test httpclient with httpbin(http://httpbin.org)
package httpclient

import (
    "testing"
    "fmt"
    "strings"
    "io/ioutil"
    "net/http"
    "encoding/json"
)

// common response format on httpbin.org
type ResponseInfo struct {
    Gzipped bool `json:"gzipped"`
    Method string `json:"method"`
    Origin string `json:"origin"`
    Useragent string `json:"user-agent"` // http://httpbin.org/user-agent
    Form map[string]string `json:"form"`
    Files map[string]string `json:"files"`
    Headers map[string]string  `json:"headers"`
    Cookies map[string]string  `json:"cookies"`
}

func TestRequest(t *testing.T) {
    // get
    res, err := NewHttpClient().
        Get("http://httpbin.org/get", nil)

    if err != nil {
        t.Error("get failed", err)
    }

    if res.StatusCode != 200 {
        t.Error("Status Code not 200")
    }

    // post
    res, err = NewHttpClient().
        Post("http://httpbin.org/post", map[string]string {
            "username": "dong",
            "password": "******",
        })

    if err != nil {
        t.Error("post failed", err)
    }

    if res.StatusCode != 200 {
        t.Error("Status Code not 200")
    }

    body, err := res.ReadAll()

    if err != nil {
        t.Error(err)
    }

    var info ResponseInfo

    err = json.Unmarshal(body, &info)

    if err != nil {
        t.Error(err)
    }

    if username, ok := info.Form["username"]; !ok || username != "dong" {
        t.Error("form data is not set properly")
    }

    // post, multipart
    res, err = NewHttpClient().
        Post("http://httpbin.org/post", map[string]string {
            "message": "Hello world!",
            "@image": "README.md",
        })

    if err != nil {
        t.Error(err)
    }

    if res.StatusCode != 200 {
        t.Error("Status Code is not 200")
    }

    body, err = res.ReadAll()

    if err != nil {
        t.Error(err)
    }

    err = json.Unmarshal(body, &info)

    if err != nil {
        t.Error(err)
    }

    image, ok := info.Files["image"]
    if !ok {
        t.Error("file not uploaded")
    }

    imageContent, err := ioutil.ReadFile("README.md")
    if err != nil {
        t.Error(err)
    }

    if string(imageContent) != image {
        t.Error("file is not uploaded properly")
    }
}

func TestResponse(t *testing.T) {
    c := NewHttpClient()
    res, err := c.
        Get("http://httpbin.org/user-agent", nil)

    if err != nil {
        t.Error(err)
    }

    // read with ioutil
    defer res.Body.Close()
    body1, err := ioutil.ReadAll(res.Body)

    if err != nil {
        t.Error(err)
    }

    res, err = c.
        Get("http://httpbin.org/user-agent", nil)

    if err != nil {
        t.Error(err)
    }

    body2, err := res.ReadAll()

    res, err = c.
        Get("http://httpbin.org/user-agent", nil)

    if err != nil {
        t.Error(err)
    }

    body3, err := res.ToString()

    if err != nil {
        t.Error(err)
    }
    if string(body1) != string(body2) || string(body1) != body3 {
        t.Error("Error response body")
    }
}

func TestHeaders(t *testing.T) {
    // set referer in options
    res, err := NewHttpClient().
        WithHeader("header1", "value1").
        WithOption(OPT_REFERER, "http://google.com").
        Get("http://httpbin.org/get", nil)

    if err != nil {
        t.Error(err)
    }

    var info ResponseInfo

    body, err := res.ReadAll()

    if err != nil {
        t.Error(err)
    }

    err = json.Unmarshal(body, &info)

    if err != nil {
        t.Error(err)
    }

    referer, ok := info.Headers["Referer"]
    if !ok || referer != "http://google.com" {
        t.Error("referer is not set properly")
    }

    useragent, ok := info.Headers["User-Agent"]
    if !ok  || useragent != USERAGENT {
        t.Error("useragent is not set properly")
    }

    value, ok := info.Headers["Header1"]
    if !ok || value != "value1" {
        t.Error("custom header is not set properly")
    }
}

func _TestProxy(t *testing.T) {
    proxy := "127.0.0.1:1080"

    res, err := NewHttpClient().
        WithOption(OPT_PROXY, proxy).
        Get("http://httpbin.org/get", nil)

    if err != nil {
        t.Error(err)
    }

    if res.StatusCode != 200 {
        t.Error("StatusCode is not 200")
    }

    res, err = NewHttpClient().
        WithOption(OPT_PROXY_FUNC, func(*http.Request) (int, string, error) {
            return PROXY_HTTP, proxy, nil
        }).
        Get("http://httpbin.org/get", nil)

    if err != nil {
        t.Error(err)
    }

    if res.StatusCode != 200 {
        t.Error("StatusCode is not 200")
    }
}

func TestTimeout(t *testing.T) {
    // connect timeout
    res, err := NewHttpClient().
        WithOption(OPT_CONNECTTIMEOUT_MS, 1).
        Get("http://httpbin.org/get", nil)

    if err == nil {
        t.Error("OPT_CONNECTTIMEOUT_MS does not work")
    }

    if !IsTimeoutError(err) {
        t.Error("Maybe it's not a timeout error?", err)
    }

    // timeout
    res, err = NewHttpClient().
        WithOption(OPT_TIMEOUT, 3).
        Get("http://httpbin.org/delay/3", nil)

    if err == nil {
        t.Error("OPT_TIMEOUT does not work")
    }

    if !strings.Contains(err.Error(), "timeout") {
        t.Error("Maybe it's not a timeout error?", err)
    }

    // no timeout
    res, err = NewHttpClient().
        WithOption(OPT_TIMEOUT, 100).
        Get("http://httpbin.org/delay/3", nil)

    if err != nil {
        t.Error("OPT_TIMEOUT does not work properly")
    }

    if res.StatusCode != 200 {
        t.Error("StatusCode is not 200")
    }
}

func TestRedirect(t *testing.T) {
    c := NewHttpClient().Defaults(Map {
        OPT_USERAGENT: "test redirect",
    })
    // follow locatioin
    res, err := c.
        WithOptions(Map {
            OPT_FOLLOWLOCATION: true,
            OPT_MAXREDIRS: 10,
        }).
        Get("http://httpbin.org/redirect/3", nil)

    if err != nil {
        t.Error(err)
    }

    if res.StatusCode != 200 || res.Request.URL.String() != "http://httpbin.org/get" {
        t.Error("Redirect failed")
    }

    // should keep useragent
    var info ResponseInfo

    body, err := res.ReadAll()

    if err != nil {
        t.Error(err)
    }

    err = json.Unmarshal(body, &info)

    if err != nil {
        t.Error(err)
    }

    if useragent, ok := info.Headers["User-Agent"]; !ok || useragent != "test redirect" {
        t.Error("Useragent is not passed through")
    }

    // no follow
    res, err = c.
        WithOption(OPT_FOLLOWLOCATION, false).
        Get("http://httpbin.org/relative-redirect/3", nil)

    if err == nil {
        t.Error("Must not follow location")
    }

    if !strings.Contains(err.Error(), "redirect not allowed") {
        t.Error(err)
    }

    if res.StatusCode != 302 || res.Header.Get("Location") != "/relative-redirect/2" {
        t.Error("Redirect failed: ", res.StatusCode, res.Header.Get("Location"))
    }

    // maxredirs
    res, err = c.
        WithOption(OPT_MAXREDIRS, 2).
        Get("http://httpbin.org/relative-redirect/3", nil)

    if err == nil {
        t.Error("Must not follow through")
    }

    if !IsRedirectError(err) {
        t.Error("Not a redirect error", err)
    }

    if !strings.Contains(err.Error(), "stopped after 2 redirects") {
        t.Error(err)
    }

    if res.StatusCode != 302 || res.Header.Get("Location") != "/relative-redirect/1" {
        t.Error("OPT_MAXREDIRS does not work properly")
    }

    // custom redirect policy
    res, err = c.
        WithOption(OPT_REDIRECT_POLICY, func(req *http.Request, via []*http.Request) error {
            if req.URL.String() == "http://httpbin.org/relative-redirect/1" {
                return fmt.Errorf("should stop here")
            }

            return nil
        }).
        Get("http://httpbin.org/relative-redirect/3", nil)

    if err == nil {
        t.Error("Must not follow through")
    }

    if !strings.Contains(err.Error(), "should stop here") {
        t.Error(err)
    }

    if res.StatusCode != 302 || res.Header.Get("Location") != "/relative-redirect/1" {
        t.Error("OPT_REDIRECT_POLICY does not work properly")
    }
}

func TestCookie(t *testing.T) {
    c := NewHttpClient()

    res, err := c.
        WithCookie(&http.Cookie {
            Name: "username",
            Value: "dong",
        }).
        Get("http://httpbin.org/cookies", nil)

    if err != nil {
        t.Error(err)
    }

    body, err := res.ReadAll()

    if err != nil {
        t.Error(err)
    }

    var info ResponseInfo

    err = json.Unmarshal(body, &info)

    if err != nil {
        t.Error(err)
    }

    if username, ok := info.Cookies["username"]; !ok || username != "dong" {
        t.Error("cookie is not set properly")
    }

    if c.CookieValue("http://httpbin.org/cookies", "username") != "dong" {
        t.Error("cookie is not set properly")
    }

    // get old cookie
    res, err = c.
        Get("http://httpbin.org/cookies", nil)

    if err != nil {
        t.Error(err)
    }

    body, err = res.ReadAll()

    if err != nil {
        t.Error(err)
    }

    err = json.Unmarshal(body, &info)

    if err != nil {
        t.Error(err)
    }

    if username, ok := info.Cookies["username"]; !ok || username != "dong" {
        t.Error("cookie lost")
    }

    if c.CookieValue("http://httpbin.org/cookies", "username") != "dong" {
        t.Error("cookie lost")
    }

    // update cookie
    res, err = c.
        WithCookie(&http.Cookie {
            Name: "username",
            Value: "octcat",
        }).
        Get("http://httpbin.org/cookies", nil)

    if err != nil {
        t.Error(err)
    }

    body, err = res.ReadAll()

    if err != nil {
        t.Error(err)
    }

    err = json.Unmarshal(body, &info)

    if err != nil {
        t.Error(err)
    }

    if username, ok := info.Cookies["username"]; !ok || username != "octcat" {
        t.Error("cookie update failed")
    }

    if c.CookieValue("http://httpbin.org/cookies", "username") != "octcat" {
        t.Error("cookie update failed")
    }
}

func TestGzip(t *testing.T) {
    c := NewHttpClient()
    res, err := c.
        WithHeader("Accept-Encoding", "gzip, deflate").
        Get("http://httpbin.org/gzip", nil)

    if err != nil {
        t.Error(err)
    }

    body, err := res.ReadAll()

    if err != nil {
        t.Error(err)
    }

    var info ResponseInfo

    err = json.Unmarshal(body, &info)

    if err != nil {
        t.Error(err)
    }

    if !info.Gzipped {
        t.Error("Parse gzip failed")
    }
}

func _TestCurrentUA(ch chan bool, t *testing.T, c *HttpClient, ua string) {
    res, err := c.
        Begin().
        WithOption(OPT_USERAGENT, ua).
        Get("http://httpbin.org/headers", nil)

    if err != nil {
        t.Error(err)
    }

    body, err := res.ReadAll()

    if err != nil {
        t.Error(err)
    }

    var info ResponseInfo
    err = json.Unmarshal(body, &info)

    if err != nil {
        t.Error(err)
    }

    if resUA, ok := info.Headers["User-Agent"]; !ok || resUA != ua {
        t.Error("TestCurrentUA failed")
    }

    ch <- true
}

func TestConcurrent(t *testing.T) {
    total := 100
    chs := make([]chan bool, total)
    c := NewHttpClient()
    for i := 0; i < total; i++ {
        chs[i] = make(chan bool)
        go _TestCurrentUA(chs[i], t, c, fmt.Sprint("go-httpclient UA-", i))
    }

    for _, ch := range chs {
        <- ch
    }
}<commit_msg>test issue 10<commit_after>// Copyright 2014-2015 Liu Dong <ddliuhb@gmail.com>.
// Licensed under the MIT license.

// Test httpclient with httpbin(http://httpbin.org)
package httpclient

import (
    "testing"
    "fmt"
    "strings"
    "io/ioutil"
    "net/http"
    "encoding/json"
)

// common response format on httpbin.org
type ResponseInfo struct {
    Gzipped bool `json:"gzipped"`
    Method string `json:"method"`
    Origin string `json:"origin"`
    Useragent string `json:"user-agent"` // http://httpbin.org/user-agent
    Form map[string]string `json:"form"`
    Files map[string]string `json:"files"`
    Headers map[string]string  `json:"headers"`
    Cookies map[string]string  `json:"cookies"`
}

func TestRequest(t *testing.T) {
    // get
    res, err := NewHttpClient().
        Get("http://httpbin.org/get", nil)

    if err != nil {
        t.Error("get failed", err)
    }

    if res.StatusCode != 200 {
        t.Error("Status Code not 200")
    }

    // post
    res, err = NewHttpClient().
        Post("http://httpbin.org/post", map[string]string {
            "username": "dong",
            "password": "******",
        })

    if err != nil {
        t.Error("post failed", err)
    }

    if res.StatusCode != 200 {
        t.Error("Status Code not 200")
    }

    body, err := res.ReadAll()

    if err != nil {
        t.Error(err)
    }

    var info ResponseInfo

    err = json.Unmarshal(body, &info)

    if err != nil {
        t.Error(err)
    }

    if username, ok := info.Form["username"]; !ok || username != "dong" {
        t.Error("form data is not set properly")
    }

    // post, multipart
    res, err = NewHttpClient().
        Post("http://httpbin.org/post", map[string]string {
            "message": "Hello world!",
            "@image": "README.md",
        })

    if err != nil {
        t.Error(err)
    }

    if res.StatusCode != 200 {
        t.Error("Status Code is not 200")
    }

    body, err = res.ReadAll()

    if err != nil {
        t.Error(err)
    }

    err = json.Unmarshal(body, &info)

    if err != nil {
        t.Error(err)
    }

    image, ok := info.Files["image"]
    if !ok {
        t.Error("file not uploaded")
    }

    imageContent, err := ioutil.ReadFile("README.md")
    if err != nil {
        t.Error(err)
    }

    if string(imageContent) != image {
        t.Error("file is not uploaded properly")
    }
}

func TestResponse(t *testing.T) {
    c := NewHttpClient()
    res, err := c.
        Get("http://httpbin.org/user-agent", nil)

    if err != nil {
        t.Error(err)
    }

    // read with ioutil
    defer res.Body.Close()
    body1, err := ioutil.ReadAll(res.Body)

    if err != nil {
        t.Error(err)
    }

    res, err = c.
        Get("http://httpbin.org/user-agent", nil)

    if err != nil {
        t.Error(err)
    }

    body2, err := res.ReadAll()

    res, err = c.
        Get("http://httpbin.org/user-agent", nil)

    if err != nil {
        t.Error(err)
    }

    body3, err := res.ToString()

    if err != nil {
        t.Error(err)
    }
    if string(body1) != string(body2) || string(body1) != body3 {
        t.Error("Error response body")
    }
}

func TestHeaders(t *testing.T) {
    // set referer in options
    res, err := NewHttpClient().
        WithHeader("header1", "value1").
        WithOption(OPT_REFERER, "http://google.com").
        Get("http://httpbin.org/get", nil)

    if err != nil {
        t.Error(err)
    }

    var info ResponseInfo

    body, err := res.ReadAll()

    if err != nil {
        t.Error(err)
    }

    err = json.Unmarshal(body, &info)

    if err != nil {
        t.Error(err)
    }

    referer, ok := info.Headers["Referer"]
    if !ok || referer != "http://google.com" {
        t.Error("referer is not set properly")
    }

    useragent, ok := info.Headers["User-Agent"]
    if !ok  || useragent != USERAGENT {
        t.Error("useragent is not set properly")
    }

    value, ok := info.Headers["Header1"]
    if !ok || value != "value1" {
        t.Error("custom header is not set properly")
    }
}

func _TestProxy(t *testing.T) {
    proxy := "127.0.0.1:1080"

    res, err := NewHttpClient().
        WithOption(OPT_PROXY, proxy).
        Get("http://httpbin.org/get", nil)

    if err != nil {
        t.Error(err)
    }

    if res.StatusCode != 200 {
        t.Error("StatusCode is not 200")
    }

    res, err = NewHttpClient().
        WithOption(OPT_PROXY_FUNC, func(*http.Request) (int, string, error) {
            return PROXY_HTTP, proxy, nil
        }).
        Get("http://httpbin.org/get", nil)

    if err != nil {
        t.Error(err)
    }

    if res.StatusCode != 200 {
        t.Error("StatusCode is not 200")
    }
}

func TestTimeout(t *testing.T) {
    // connect timeout
    res, err := NewHttpClient().
        WithOption(OPT_CONNECTTIMEOUT_MS, 1).
        Get("http://httpbin.org/get", nil)

    if err == nil {
        t.Error("OPT_CONNECTTIMEOUT_MS does not work")
    }

    if !IsTimeoutError(err) {
        t.Error("Maybe it's not a timeout error?", err)
    }

    // timeout
    res, err = NewHttpClient().
        WithOption(OPT_TIMEOUT, 3).
        Get("http://httpbin.org/delay/3", nil)

    if err == nil {
        t.Error("OPT_TIMEOUT does not work")
    }

    if !strings.Contains(err.Error(), "timeout") {
        t.Error("Maybe it's not a timeout error?", err)
    }

    // no timeout
    res, err = NewHttpClient().
        WithOption(OPT_TIMEOUT, 100).
        Get("http://httpbin.org/delay/3", nil)

    if err != nil {
        t.Error("OPT_TIMEOUT does not work properly")
    }

    if res.StatusCode != 200 {
        t.Error("StatusCode is not 200")
    }
}

func TestRedirect(t *testing.T) {
    c := NewHttpClient().Defaults(Map {
        OPT_USERAGENT: "test redirect",
    })
    // follow locatioin
    res, err := c.
        WithOptions(Map {
            OPT_FOLLOWLOCATION: true,
            OPT_MAXREDIRS: 10,
        }).
        Get("http://httpbin.org/redirect/3", nil)

    if err != nil {
        t.Error(err)
    }

    if res.StatusCode != 200 || res.Request.URL.String() != "http://httpbin.org/get" {
        t.Error("Redirect failed")
    }

    // should keep useragent
    var info ResponseInfo

    body, err := res.ReadAll()

    if err != nil {
        t.Error(err)
    }

    err = json.Unmarshal(body, &info)

    if err != nil {
        t.Error(err)
    }

    if useragent, ok := info.Headers["User-Agent"]; !ok || useragent != "test redirect" {
        t.Error("Useragent is not passed through")
    }

    // no follow
    res, err = c.
        WithOption(OPT_FOLLOWLOCATION, false).
        Get("http://httpbin.org/relative-redirect/3", nil)

    if err == nil {
        t.Error("Must not follow location")
    }

    if !strings.Contains(err.Error(), "redirect not allowed") {
        t.Error(err)
    }

    if res.StatusCode != 302 || res.Header.Get("Location") != "/relative-redirect/2" {
        t.Error("Redirect failed: ", res.StatusCode, res.Header.Get("Location"))
    }

    // maxredirs
    res, err = c.
        WithOption(OPT_MAXREDIRS, 2).
        Get("http://httpbin.org/relative-redirect/3", nil)

    if err == nil {
        t.Error("Must not follow through")
    }

    if !IsRedirectError(err) {
        t.Error("Not a redirect error", err)
    }

    if !strings.Contains(err.Error(), "stopped after 2 redirects") {
        t.Error(err)
    }

    if res.StatusCode != 302 || res.Header.Get("Location") != "/relative-redirect/1" {
        t.Error("OPT_MAXREDIRS does not work properly")
    }

    // custom redirect policy
    res, err = c.
        WithOption(OPT_REDIRECT_POLICY, func(req *http.Request, via []*http.Request) error {
            if req.URL.String() == "http://httpbin.org/relative-redirect/1" {
                return fmt.Errorf("should stop here")
            }

            return nil
        }).
        Get("http://httpbin.org/relative-redirect/3", nil)

    if err == nil {
        t.Error("Must not follow through")
    }

    if !strings.Contains(err.Error(), "should stop here") {
        t.Error(err)
    }

    if res.StatusCode != 302 || res.Header.Get("Location") != "/relative-redirect/1" {
        t.Error("OPT_REDIRECT_POLICY does not work properly")
    }
}

func TestCookie(t *testing.T) {
    c := NewHttpClient()

    res, err := c.
        WithCookie(&http.Cookie {
            Name: "username",
            Value: "dong",
        }).
        Get("http://httpbin.org/cookies", nil)

    if err != nil {
        t.Error(err)
    }

    body, err := res.ReadAll()

    if err != nil {
        t.Error(err)
    }

    var info ResponseInfo

    err = json.Unmarshal(body, &info)

    if err != nil {
        t.Error(err)
    }

    if username, ok := info.Cookies["username"]; !ok || username != "dong" {
        t.Error("cookie is not set properly")
    }

    if c.CookieValue("http://httpbin.org/cookies", "username") != "dong" {
        t.Error("cookie is not set properly")
    }

    // get old cookie
    res, err = c.
        Get("http://httpbin.org/cookies", nil)

    if err != nil {
        t.Error(err)
    }

    body, err = res.ReadAll()

    if err != nil {
        t.Error(err)
    }

    err = json.Unmarshal(body, &info)

    if err != nil {
        t.Error(err)
    }

    if username, ok := info.Cookies["username"]; !ok || username != "dong" {
        t.Error("cookie lost")
    }

    if c.CookieValue("http://httpbin.org/cookies", "username") != "dong" {
        t.Error("cookie lost")
    }

    // update cookie
    res, err = c.
        WithCookie(&http.Cookie {
            Name: "username",
            Value: "octcat",
        }).
        Get("http://httpbin.org/cookies", nil)

    if err != nil {
        t.Error(err)
    }

    body, err = res.ReadAll()

    if err != nil {
        t.Error(err)
    }

    err = json.Unmarshal(body, &info)

    if err != nil {
        t.Error(err)
    }

    if username, ok := info.Cookies["username"]; !ok || username != "octcat" {
        t.Error("cookie update failed")
    }

    if c.CookieValue("http://httpbin.org/cookies", "username") != "octcat" {
        t.Error("cookie update failed")
    }
}

func TestGzip(t *testing.T) {
    c := NewHttpClient()
    res, err := c.
        WithHeader("Accept-Encoding", "gzip, deflate").
        Get("http://httpbin.org/gzip", nil)

    if err != nil {
        t.Error(err)
    }

    body, err := res.ReadAll()

    if err != nil {
        t.Error(err)
    }

    var info ResponseInfo

    err = json.Unmarshal(body, &info)

    if err != nil {
        t.Error(err)
    }

    if !info.Gzipped {
        t.Error("Parse gzip failed")
    }
}

func _TestCurrentUA(ch chan bool, t *testing.T, c *HttpClient, ua string) {
    res, err := c.
        Begin().
        WithOption(OPT_USERAGENT, ua).
        Get("http://httpbin.org/headers", nil)

    if err != nil {
        t.Error(err)
    }

    body, err := res.ReadAll()

    if err != nil {
        t.Error(err)
    }

    var info ResponseInfo
    err = json.Unmarshal(body, &info)

    if err != nil {
        t.Error(err)
    }

    if resUA, ok := info.Headers["User-Agent"]; !ok || resUA != ua {
        t.Error("TestCurrentUA failed")
    }

    ch <- true
}

func TestConcurrent(t *testing.T) {
    total := 100
    chs := make([]chan bool, total)
    c := NewHttpClient()
    for i := 0; i < total; i++ {
        chs[i] = make(chan bool)
        go _TestCurrentUA(chs[i], t, c, fmt.Sprint("go-httpclient UA-", i))
    }

    for _, ch := range chs {
        <- ch
    }
}

func TestIssue10(t *testing.T) {
    var testString = "gpThzrynEC1MdenWgAILwvL2CYuNGO9RwtbH1NZJ1GE31ywFOCY%2BLCctUl86jBi8TccpdPI5ppZ%2Bgss%2BNjqGHg=="
    c := NewHttpClient()
    res, err := c.Post("http://httpbin.org/post", map[string]string {
        "a": "a",
        "b": "b",
        "c": testString,
        "d": "d",
    })

    if err != nil {
        t.Error(err)
    }

    body, err := res.ReadAll()

    if err != nil {
        t.Error(err)
    }

    var info ResponseInfo

    err = json.Unmarshal(body, &info)

    if err != nil {
        t.Error(err)
    }

    if info.Form["c"] != testString {
        t.Error("error")
    }
}<|endoftext|>


<commit_before>
package httputil

import (
    "net/http"
    "regexp"
    "strings"
    "fmt"
    "sync"
    "github.com/princeofdatamining/golib/strutil"
)

const (
    MATCH_HOST_ANY = ".*$"
)

var bottle_rule_syntax = regexp.MustCompile(
    `(\\*)`+                                // [1]
    "(?:"+
        "(?:"+
            ":"+
            "([a-zA-Z_][a-zA-Z_0-9]*)?"+    // [2] name
            "()"+                           // [3] filter
            "(?:"+
                "#" + "(.*?)" + "#"+        // [4] conf
            ")"+
        ")"+
        "|"+
        "(?:"+
            "<"+
            "([a-zA-Z_][a-zA-Z_0-9]*)?"+    // [5] name
            "(?:"+
                ":"+
                "([a-zA-Z_]*)"+             // [6] filter
                "(?:"+
                    ":"+
                    "("+                    // [7] conf
                        "(?:"+
                            `\\.`+
                            "|"+
                            `[^\\>]+`+
                        ")+"+
                    ")?"+                   // [7]
                ")?"+
            ")?"+
            ">"+
        ")"+
    ")",
)

func ParseBottleRule(rule string) (parts [][]string) {
    var (
        offset, start, end  int
        prefix, submatch    string
        name, filter, conf  string
    )
    for _, match := range bottle_rule_syntax.FindAllStringSubmatchIndex(rule, -1) {
        // fmt.Printf("% d\n", match)
        m := strutil.SubmatchIndex(match)
        submatch, start, end, _ = m.GetSubmatch(rule, 0)
        prefix += rule[offset:start]
        // fmt.Printf("prefix +=> %q\n", prefix)
        offset = end
        //
        start, end, _ = m.GetIndexPairs(1)
        if (end - start) % 2 != 0 {
            prefix += submatch[end-start:]
            // fmt.Printf("prefix +[\\:]=> %q\n", prefix)
            continue
        }
        if prefix != "" {
            parts = append(parts, []string{prefix, "", "", ""})
            // fmt.Printf("prefix => %q\n", prefix)
            prefix = ""
        }
        //
        if _, _, _, exists := m.GetSubmatch(rule, 3); exists {
            name   = m.GetSubstring(rule, 2)
            filter = m.GetSubstring(rule, 3)
            conf   = m.GetSubstring(rule, 4)
        } else {
            name   = m.GetSubstring(rule, 5)
            filter = m.GetSubstring(rule, 6)
            conf   = m.GetSubstring(rule, 7)
        }
        // fmt.Printf("name: %q, filter: %q, conf: %q\n", name, filter, conf)
        parts = append(parts, []string{"", name, filter, conf})
    }
    if offset < len(rule) || prefix != "" {
        prefix += rule[offset:]
        // fmt.Printf("prefix ::=> %q\n", prefix)
        parts = append(parts, []string{prefix, "", "", ""})
    }
    return
}

//

type WrapperFunc func (http.Handler) (http.Handler)

//

func ErrorHandler(error string, code int) (http.Handler) { return &errorHandler{error, code} }
type errorHandler struct {
    error   string
    code    int
}
func (this *errorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) () { http.Error(w, this.error, this.code) }

//

func FileHandler(fn string) (http.Handler) { return &fileHandler{fn} }
type fileHandler struct {
    fn    string
}
func (this *fileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) () { http.ServeFile(w, r, this.fn) }

//

var _ http.Handler = NewMultiHostRouter()

func NewMultiHostRouter() (*MultiHostRouter) {
    return &MultiHostRouter{
        cached: make(map[string]*Router),
        DefaultSchema: "http",
    }
}
type MultiHostRouter struct {
    sync.RWMutex
    hosts           []*Router
    defaults        *Router
    cached          map[string]*Router
    //
    wrapFunc        WrapperFunc
    DefaultSchema   string
    DefaultHost     string
    UseDefaultHost  bool
}
func (this *MultiHostRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) () {
    h := this.Handler(r)
    h.ServeHTTP(w, r)
}
func (this *MultiHostRouter) Handler(r *http.Request) (h http.Handler) {
    this.RLock()
    defer this.RUnlock()

    routers, found := this.findAllRouters(r.Host)
    if !found {
        if this.UseDefaultHost && this.DefaultHost != r.Host {
            url := *r.URL
            url.Scheme = this.DefaultSchema
            url.Host = this.DefaultHost
            return this.wrapped(http.RedirectHandler(url.String(), http.StatusTemporaryRedirect))
        } else {
            return this.wrapped(ErrorHandler("406 Unknown Host", http.StatusNotAcceptable))
        }
    }
    for _, router := range routers {
        if route, _, _ := router.match(r.URL.Path); route != nil {
            return route.handler
        }
    }
    return this.wrapped(http.NotFoundHandler())
}
func (this *MultiHostRouter) findAllRouters(host string) (routers []*Router, found bool) {
    routers, found = this.findRouters(host)
    if found || !this.UseDefaultHost {
        return
    }
    return this.findRouters(this.DefaultHost)
}
func (this *MultiHostRouter) findRouters(host string) (routers []*Router, found bool) {
    host = strings.Split(host, ":")[0]
    for _, router := range this.hosts {
        if router.matchHost(host) {
            routers, found = append(routers, router), true
        }
    }
    if router := this.defaults; !found && router != nil {
        if router.matchHost(host) {
            routers, found = append(routers, router), true
        }
    }
    return
}
func (this *MultiHostRouter) WrapFunc(f WrapperFunc) () { this.wrapFunc = f }
func (this *MultiHostRouter) wrapped(h http.Handler) (http.Handler) {
    if this.wrapFunc == nil {
        return h
    }
    return this.wrapFunc(h)
}
func (this *MultiHostRouter) Handle(host_pattern, path_pattern string, h http.Handler) () { this.AddRouter(host_pattern).Handle(path_pattern, h) }
func (this *MultiHostRouter) cacheRouter(host_pattern string) (router *Router, found bool) {
    if this.cached == nil {
        this.cached = make(map[string]*Router)
    }
    router, found = this.cached[host_pattern]
    return
}
func (this *MultiHostRouter) AddRouter(host_pattern string) (router *Router) {
    this.Lock()
    if router, found := this.cacheRouter(host_pattern); found {
        this.Unlock()
        return router
    }
    defer this.Unlock()

    router = newRouter(this, host_pattern)
    //
    if host_pattern == MATCH_HOST_ANY {
        this.defaults = router
    } else {
        this.hosts = append(this.hosts, router)
    }
    this.cached[host_pattern] = router
    return
}

//

func NewRouter() (*Router) {
    return &Router{
        rules: make(map[string]*Route),
        builder: make(map[string][]*builderPart),
        static: make(map[string]*Route),
    }
}
type Router struct {
    sync.RWMutex
    rules       map[string]*Route
    builder     map[string][]*builderPart
    static      map[string]*Route
    //
    parent      *MultiHostRouter
    host_re     *regexp.Regexp
    wrapFunc    WrapperFunc
}
type builderPart struct {
    key     string
    static  bool
}
func newRouter(p *MultiHostRouter, host_pattern string) (*Router) {
    this := NewRouter()
    this.parent = p
    this.host_re = regexp.MustCompile(host_pattern)
    this.WrapFunc(p.wrapFunc)
    return this
}
func (this *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) () { this.Handler(r).ServeHTTP(w, r) }
func (this *Router) Handler(r *http.Request) (http.Handler) {
    if route, _, _ := this.match(r.URL.Path); route != nil {
        return route.handler
    }
    return this.wrapped(http.NotFoundHandler())
}
func (this *Router) matchHost(host string) (bool) { return this.host_re.MatchString(host) }
func (this *Router) match(path string) (*Route, []string, map[string]string) {
    this.RLock()
    defer this.RUnlock()

    if rule, found := this.static[path]; found {
        return rule, nil, nil
    }
    return nil, nil, nil
}
func (this *Router) WrapFunc(f WrapperFunc) () { this.wrapFunc = f }
func (this *Router) wrapped(h http.Handler) (http.Handler) {
    if this.wrapFunc == nil {
        return h
    }
    return this.wrapFunc(h)
}
func (this *Router) Handle(rule string, h http.Handler) () {
    this.Lock()
    defer this.Unlock()

    if _, exists := this.rules[rule]; exists {
        return
    }

    route := &Route{
        handler: this.wrapped(h),
    }
    this.rules[rule] = route

    pattern := ""
    builder := []*builderPart{}
    static := true
    anons := 0
    for _, parts := range ParseBottleRule(rule) {
        prefix, key, filter, conf := parts[0], parts[1], parts[2], parts[3]
        fmt.Printf("\t%q %q %q %q\n", prefix, key, filter, conf)
        if prefix != "" {
            pattern += regexp.QuoteMeta(prefix)
            builder = append(builder, &builderPart{ prefix, true })
            continue
        }
        static = false
        if key == "" {
            // key = fmt.Sprintf("anon%d", anons)
            pattern += fmt.Sprintf("(%s)", mask)
            anons++
        } else {
            pattern += fmt.Sprintf("(?P<%s>%s)", key, mask)
        }
        builder = append(builder, &builderPart{ key, false })
    }
    this.builder[rule] = builder

    if static {
        path := this.build(rule)
        fmt.Printf("\tstatic %q\n", path)
        this.static[path] = route
        return
    }

    reMatch := regexp.MustCompile(fmt.Spritnf("^%s$", pattern))
    subNames := reMatch.SubexpNames()
    getargs := func (path string) (args []string, kwargs map[string]string) {
        subs := reMatch.FindStringSubmatch(path)
        for i, subName := range subNames {
            if i == 0 { continue }
            if subName == "" {
                args = append(args, subs[i])
            } else {
                if kwargs == nil {
                    kwargs = make(map[string]string)
                }
                kwargs[subName] = subs[i]
            }
        }
        return args, kwargs
    }
}
func (this *Router) build(rule string) (url string) {
    builder := this.builder[rule]
    for _, part := range builder {
        if part.static {
            url += part.key
        }
    }
    return
}

//

type Route struct {
    handler     http.Handler
}

//

type FilterFunc func (conf string) (string)

var filters = map[string]FilterFunc{
    "re": func (conf string) (string) {
        if conf != "" { return conf }
        return ""
    },
    "int": func (conf string) (string) {
        return `-?\d+`
    },
    "float": func (conf string) (string) {
        return `-?[\d.]+`
    },
}
<commit_msg>* httputil/router.go<commit_after>
package httputil

import (
    "net/http"
    "regexp"
    "strings"
    "fmt"
    "sync"
    "github.com/princeofdatamining/golib/strutil"
)

/*
    <name>
    <name:filter>
    <name:re:pattern>
//*/
var bottle_rule_syntax = regexp.MustCompile(
    `(\\*)`+                                // [1]
    "(?:"+
        "(?:"+
            ":"+
            "([a-zA-Z_][a-zA-Z_0-9]*)?"+    // [2] name
            "()"+                           // [3] filter
            "(?:"+
                "#" + "(.*?)" + "#"+        // [4] conf
            ")"+
        ")"+
        "|"+
        "(?:"+
            "<"+
            "([a-zA-Z_][a-zA-Z_0-9]*)?"+    // [5] name
            "(?:"+
                ":"+
                "([a-zA-Z_]*)"+             // [6] filter
                "(?:"+
                    ":"+
                    "("+                    // [7] conf
                        "(?:"+
                            `\\.`+
                            "|"+
                            `[^\\>]+`+
                        ")+"+
                    ")?"+                   // [7]
                ")?"+
            ")?"+
            ">"+
        ")"+
    ")",
)

func ParseBottleRule(rule string) (parts [][]string) {
    var (
        offset, start, end  int
        prefix, submatch    string
        name, filter, conf  string
    )
    for _, match := range bottle_rule_syntax.FindAllStringSubmatchIndex(rule, -1) {
        // fmt.Printf("% d\n", match)
        m := strutil.SubmatchIndex(match)
        submatch, start, end, _ = m.GetSubmatch(rule, 0)
        prefix += rule[offset:start]
        // fmt.Printf("prefix +=> %q\n", prefix)
        offset = end
        //
        start, end, _ = m.GetIndexPairs(1)
        if (end - start) % 2 != 0 {
            prefix += submatch[end-start:]
            // fmt.Printf("prefix +[\\:]=> %q\n", prefix)
            continue
        }
        if prefix != "" {
            parts = append(parts, []string{prefix, "", "", ""})
            // fmt.Printf("prefix => %q\n", prefix)
            prefix = ""
        }
        //
        if _, _, _, exists := m.GetSubmatch(rule, 3); exists {
            name   = m.GetSubstring(rule, 2)
            filter = m.GetSubstring(rule, 3)
            conf   = m.GetSubstring(rule, 4)
        } else {
            name   = m.GetSubstring(rule, 5)
            filter = m.GetSubstring(rule, 6)
            conf   = m.GetSubstring(rule, 7)
        }
        // fmt.Printf("name: %q, filter: %q, conf: %q\n", name, filter, conf)
        parts = append(parts, []string{"", name, filter, conf})
    }
    if offset < len(rule) || prefix != "" {
        prefix += rule[offset:]
        // fmt.Printf("prefix ::=> %q\n", prefix)
        parts = append(parts, []string{prefix, "", "", ""})
    }
    return
}

type FilterFunc func (conf string) (string)

var filters = map[string]FilterFunc{
    "re": func (conf string) (string) {
        if conf != "" { return conf }
        return `[^/]+`
    },
    "int": func (conf string) (string) {
        return `-?\d+`
    },
    "float": func (conf string) (string) {
        return `-?[\d.]+`
    },
    "path": func (conf string) (string) {
        return `.+`
    },
}

//

type (
    WrapperFunc func (http.Handler) (http.Handler)
    HandlerArgs func (http.ResponseWriter, *http.Request, []string, map[string]string) ()
)

//

func ErrorHandler(error string, code int) (http.Handler) { return &errorHandler{error, code} }
type errorHandler struct {
    error   string
    code    int
}
func (this *errorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) () { http.Error(w, this.error, this.code) }

//

func FileHandler(fn string) (http.Handler) { return &fileHandler{fn} }
type fileHandler struct {
    fn    string
}
func (this *fileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) () { http.ServeFile(w, r, this.fn) }

//

const (
    MATCH_HOST_ANY = ".*$"
)

var _ http.Handler = NewMultiHostRouter()

func NewMultiHostRouter() (*MultiHostRouter) {
    return &MultiHostRouter{
        cached: make(map[string]*Router),
        DefaultSchema: "http",
    }
}
type MultiHostRouter struct {
    sync.RWMutex
    hosts           []*Router
    defaults        *Router
    cached          map[string]*Router
    //
    wrapFunc        WrapperFunc
    DefaultSchema   string
    DefaultHost     string
    UseDefaultHost  bool
}
func (this *MultiHostRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) () {
    h := this.Handler(r)
    h.ServeHTTP(w, r)
}
func (this *MultiHostRouter) Handler(r *http.Request) (h http.Handler) {
    this.RLock()
    defer this.RUnlock()

    routers, found := this.findAllRouters(r.Host)
    if !found {
        if this.UseDefaultHost && this.DefaultHost != r.Host {
            url := *r.URL
            url.Scheme = this.DefaultSchema
            url.Host = this.DefaultHost
            return this.wrapped(http.RedirectHandler(url.String(), http.StatusTemporaryRedirect))
        } else {
            return this.wrapped(ErrorHandler("406 Unknown Host", http.StatusNotAcceptable))
        }
    }
    for _, router := range routers {
        if route, _, _ := router.match(r.URL.Path); route != nil {
            return route.handler
        }
    }
    return this.wrapped(http.NotFoundHandler())
}
func (this *MultiHostRouter) findAllRouters(host string) (routers []*Router, found bool) {
    routers, found = this.findRouters(host)
    if found || !this.UseDefaultHost {
        return
    }
    return this.findRouters(this.DefaultHost)
}
func (this *MultiHostRouter) findRouters(host string) (routers []*Router, found bool) {
    host = strings.Split(host, ":")[0]
    for _, router := range this.hosts {
        if router.matchHost(host) {
            routers, found = append(routers, router), true
        }
    }
    if router := this.defaults; !found && router != nil {
        if router.matchHost(host) {
            routers, found = append(routers, router), true
        }
    }
    return
}
func (this *MultiHostRouter) WrapFunc(f WrapperFunc) () { this.wrapFunc = f }
func (this *MultiHostRouter) wrapped(h http.Handler) (http.Handler) {
    if this.wrapFunc == nil {
        return h
    }
    return this.wrapFunc(h)
}
func (this *MultiHostRouter) Handle(host_pattern, path_pattern string, h http.Handler) () { this.AddRouter(host_pattern).Handle(path_pattern, h) }
func (this *MultiHostRouter) HandleFunc(host_pattern, path_pattern string, f func (http.ResponseWriter, *http.Request) ()) () {
    this.Handle(host_pattern, path_pattern, http.HandlerFunc(f))
}
func (this *MultiHostRouter) cacheRouter(host_pattern string) (router *Router, found bool) {
    if this.cached == nil {
        this.cached = make(map[string]*Router)
    }
    router, found = this.cached[host_pattern]
    return
}
func (this *MultiHostRouter) AddRouter(host_pattern string) (router *Router) {
    this.Lock()
    if router, found := this.cacheRouter(host_pattern); found {
        this.Unlock()
        return router
    }
    defer this.Unlock()

    router = newRouter(this, host_pattern)
    //
    if host_pattern == MATCH_HOST_ANY {
        this.defaults = router
    } else {
        this.hosts = append(this.hosts, router)
    }
    this.cached[host_pattern] = router
    return
}

//

func NewRouter() (*Router) {
    return &Router{
        rules: make(map[string]*Route),
        builder: make(map[string][]*builderPart),
        static: make(map[string]*Route),
    }
}
type Router struct {
    sync.RWMutex
    rules       map[string]*Route
    builder     map[string][]*builderPart
    static      map[string]*Route
    dynamic     []*dynamicPart
    //
    parent      *MultiHostRouter
    host_re     *regexp.Regexp
    wrapFunc    WrapperFunc
}
type builderPart struct {
    key     string
    static  bool
}
type dynamicPart struct {
    pattern     string
    rexp        *regexp.Regexp
    pairs       []*pairGetargsRule
}
type pairGetargsRule struct {
    getargs     func (string) ([]string, map[string]string)
    route       *Route
}
func newRouter(p *MultiHostRouter, host_pattern string) (*Router) {
    this := NewRouter()
    this.parent = p
    this.host_re = regexp.MustCompile(host_pattern)
    this.WrapFunc(p.wrapFunc)
    return this
}
func (this *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) () { this.Handler(r).ServeHTTP(w, r) }
func (this *Router) Handler(r *http.Request) (http.Handler) {
    if route, _, _ := this.match(r.URL.Path); route != nil {
        return route.handler
    }
    return this.wrapped(http.NotFoundHandler())
}
func (this *Router) matchHost(host string) (bool) { return this.host_re.MatchString(host) }
func (this *Router) match(path string) (*Route, []string, map[string]string) {
    this.RLock()
    defer this.RUnlock()

    // fmt.Printf("match static rule...\n")
    if route, found := this.static[path]; found {
        return route, nil, nil
    }

    // fmt.Printf("match dynamic rule...\n")
    for _, d := range this.dynamic {
        // fmt.Printf("dynamic %q\n", d.pattern)
        subindex := d.rexp.FindStringSubmatchIndex(path)
        // fmt.Printf("\t% d\n", subindex)
        i := indexCombined(subindex, len(d.pairs))-1
        if i < 0 {
            continue
        }
        getargs, route := d.pairs[i].getargs, d.pairs[i].route
        args, kwargs := getargs(path)
        // fmt.Printf("\tmatched args: % q; kwargs: %+v\n", args, kwargs)
        return route, args, kwargs
    }

    return nil, nil, nil
}
func indexCombined(subs []int, n int) (i int) {
    for i < n {
        i++
        if subs[i*2+1] >= 0 {
            return i
        }
    }
    return 0
}
func (this *Router) WrapFunc(f WrapperFunc) () { this.wrapFunc = f }
func (this *Router) wrapped(h http.Handler) (http.Handler) {
    if this.wrapFunc == nil {
        return h
    }
    return this.wrapFunc(h)
}
func (this *Router) Handle(rule string, h http.Handler) () {
    this.Lock()
    defer this.Unlock()

    if _, exists := this.rules[rule]; exists {
        return
    }

    route := &Route{
        handler: this.wrapped(h),
    }
    this.rules[rule] = route

    pattern := ""
    flat_pattern := ""
    builder := []*builderPart{}
    static := true
    anons := 0
    for _, parts := range ParseBottleRule(rule) {
        var se string
        prefix, key, filter, conf := parts[0], parts[1], parts[2], parts[3]
        // fmt.Printf("\t%q %q %q %q\n", prefix, key, filter, conf)
        if prefix != "" {
            se = regexp.QuoteMeta(prefix)
            pattern += se
            flat_pattern += se
            builder = append(builder, &builderPart{ prefix, true })
            continue
        }
        static = false
        mask := filters[filter](conf)
        if key == "" {
            // key = fmt.Sprintf("anon%d", anons)
            pattern += fmt.Sprintf("(%s)", mask)
            anons++
        } else {
            pattern += fmt.Sprintf("(?P<%s>%s)", key, mask)
        }
        flat_pattern += fmt.Sprintf("(?:%s)", mask)
        builder = append(builder, &builderPart{ key, false })
    }
    this.builder[rule] = builder

    if static {
        path := this.build(rule)
        // fmt.Printf("\tstatic %q\n", path)
        this.static[path] = route
        return
    }

    reMatch := regexp.MustCompile(fmt.Sprintf("^%s$", pattern))
    subNames := reMatch.SubexpNames()
    getargs := func (path string) (args []string, kwargs map[string]string) {
        subs := reMatch.FindStringSubmatch(path)
        for i, subName := range subNames {
            if i == 0 { continue }
            if subName == "" {
                args = append(args, subs[i])
            } else {
                if kwargs == nil {
                    kwargs = make(map[string]string)
                }
                kwargs[subName] = subs[i]
            }
        }
        return args, kwargs
    }

    var (
        e error
        last *dynamicPart
    )
    flat_pattern = fmt.Sprintf("(^%s$)", flat_pattern)
    if N := len(this.dynamic); N <= 0 {
        e = fmt.Errorf("")
    } else {
        last = this.dynamic[N-1]
        combined := last.pattern + "|" + flat_pattern
        if exp, err := regexp.Compile(combined); err == nil {
            last.pattern = combined
            last.rexp = exp
        } else {
            e = err
        }
    }
    if e != nil {
        last = &dynamicPart{
            pattern: flat_pattern,
        }
        this.dynamic = append(this.dynamic, last)
    }
    last.pairs = append(last.pairs, &pairGetargsRule{
        getargs: getargs,
        route: route,
    })
}
func (this *Router) HandleFunc(rule string, f func (http.ResponseWriter, *http.Request) ()) () {
    this.Handle(rule, http.HandlerFunc(f))
}
func (this *Router) build(rule string) (url string) {
    builder := this.builder[rule]
    for _, part := range builder {
        if part.static {
            url += part.key
        }
    }
    return
}

//

type Route struct {
    handler     http.Handler
}

//
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
)

//Licenselist holds short and long names of licenses
type Licenselist []struct {
	Short string `json:"short"`
	Long  string `json:"long"`
}

//Licenseinfo holds information of licenses
type Licenseinfo []struct {
	Name        string `json:"name"`
	Title       string `json:"title"`
	Category    string `json:"category"`
	Source      string `json:"source"`
	Description string `json:"description"`
	Tags        struct {
		Required  []string `json:"required"`
		Permitted []string `json:"permitted"`
		Forbidden []string `json:"forbidden"`
	} `json:"tags"`
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func getfilepath(filename string) string {
	gopath := os.Getenv("GOPATH")
	licensethispath := gopath + "/src/github.com/hasit/licensethis/"
	filepath := licensethispath + filename

	return filepath
}

func help() {
	helptext := `licensethis - Choose an OSS license for your project with ease.

Usage:
licensethis help
licensethis list
licensethis info <license-name>
licensethis generate <license-name>

Commands:
help		Show this help text.
list		Show a list of all available OSS licenses.
info		Show more information for a license.
generate	Generate LICENSE.txt file in current folder after asking for author name.

Examples:
licensethis help		Show this help text.
licensethis info mit		Show more information for MIT license.
licensethis generate mit	Geneate MIT license text file in current directory.`

	fmt.Printf("%v\n", helptext)
}

func list() {
	listfilepath := getfilepath("licenselist.json")
	file, err1 := ioutil.ReadFile(listfilepath)
	check(err1)

	var licenselist Licenselist
	err2 := json.Unmarshal(file, &licenselist)
	check(err2)

	fmt.Println("List of all available OSS licenses:")
	for i := range licenselist {
		fmt.Printf("%d. %v - %v\n", i, licenselist[i].Short, licenselist[i].Long)
	}
}

func info(licensename string) {
	infofilepath := getfilepath("licenseinfo.json")
	file, err1 := ioutil.ReadFile(infofilepath)
	check(err1)

	var licenseinfo Licenseinfo
	err2 := json.Unmarshal(file, &licenseinfo)
	check(err2)

	for i := range licenseinfo {
		if licenseinfo[i].Name == licensename {
			fmt.Printf("Title: %v\n", licenseinfo[i].Title)
			fmt.Printf("Category: %v\n", licenseinfo[i].Category)
			fmt.Printf("Source: %v\n", licenseinfo[i].Source)
			fmt.Printf("Description: %v\n", licenseinfo[i].Description)
			fmt.Println("Tags: ")
			fmt.Println("  Required: ")
			for r := range licenseinfo[i].Tags.Required {
				fmt.Printf("    -%v\n", licenseinfo[i].Tags.Required[r])
			}
			fmt.Println("  Permitted: ")
			for r := range licenseinfo[i].Tags.Permitted {
				fmt.Printf("    -%v\n", licenseinfo[i].Tags.Permitted[r])
			}
			fmt.Println("  Forbidden: ")
			for r := range licenseinfo[i].Tags.Forbidden {
				fmt.Printf("    -%v\n", licenseinfo[i].Tags.Forbidden[r])
			}
		} else {
			fmt.Println("No such license found!")
			fmt.Println("Try `licensethis list` and use the exact short name.")
			break
		}
	}
}

func generate(licensename string) {
	fmt.Println(licensename)
}

// !!---FUTURE---!!
//might have to look into a way to accept more than one license name for `info`.
func parseArgs(args []string) {
	if len(args) == 1 {
		switch args[0] {
		case "help":
			help()
		case "list":
			list()
		default:
			fmt.Println("Incorrect usage!")
			fmt.Println("Type `licensethis help` for help on proper usage.")
		}
	} else if len(args) == 2 {
		switch args[0] {
		case "info":
			info(args[1])
		case "generate":
			generate(args[1])
		default:
			fmt.Println("Incorrect usage!")
			fmt.Println("Type `licensethis help` for help on proper usage.")
		}
	} else {
		fmt.Println("Incorrect usage!")
		fmt.Println("Type `licensethis help` for help on proper usage.")
	}
}

func main() {
	args := os.Args[1:]
	parseArgs(args)
}
<commit_msg>more FUTURE comment added<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
)

//Licenselist holds short and long names of licenses
type Licenselist []struct {
	Short string `json:"short"`
	Long  string `json:"long"`
}

//Licenseinfo holds information of licenses
type Licenseinfo []struct {
	Name        string `json:"name"`
	Title       string `json:"title"`
	Category    string `json:"category"`
	Source      string `json:"source"`
	Description string `json:"description"`
	Tags        struct {
		Required  []string `json:"required"`
		Permitted []string `json:"permitted"`
		Forbidden []string `json:"forbidden"`
	} `json:"tags"`
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func getfilepath(filename string) string {
	gopath := os.Getenv("GOPATH")
	licensethispath := gopath + "/src/github.com/hasit/licensethis/"
	filepath := licensethispath + filename

	return filepath
}

func help() {
	helptext := `licensethis - Choose an OSS license for your project with ease.

Usage:
licensethis help
licensethis list
licensethis info <license-name>
licensethis generate <license-name>

Commands:
help		Show this help text.
list		Show a list of all available OSS licenses.
info		Show more information for a license.
generate	Generate LICENSE.txt file in current folder after asking for author name.

Examples:
licensethis help		Show this help text.
licensethis info mit		Show more information for MIT license.
licensethis generate mit	Geneate MIT license text file in current directory.`

	fmt.Printf("%v\n", helptext)
}

func list() {
	listfilepath := getfilepath("licenselist.json")
	file, err1 := ioutil.ReadFile(listfilepath)
	check(err1)

	var licenselist Licenselist
	err2 := json.Unmarshal(file, &licenselist)
	check(err2)

	fmt.Println("List of all available OSS licenses:")
	for i := range licenselist {
		fmt.Printf("%d. %v - %v\n", i, licenselist[i].Short, licenselist[i].Long)
	}
}

// !!---FUTURE---!!
// alow substrings of license names as arguments too
func info(licensename string) {
	infofilepath := getfilepath("licenseinfo.json")
	file, err1 := ioutil.ReadFile(infofilepath)
	check(err1)

	var licenseinfo Licenseinfo
	err2 := json.Unmarshal(file, &licenseinfo)
	check(err2)

	for i := range licenseinfo {
		if licenseinfo[i].Name == licensename {
			fmt.Printf("Title: %v\n", licenseinfo[i].Title)
			fmt.Printf("Category: %v\n", licenseinfo[i].Category)
			fmt.Printf("Source: %v\n", licenseinfo[i].Source)
			fmt.Printf("Description: %v\n", licenseinfo[i].Description)
			fmt.Println("Tags: ")
			fmt.Println("  Required: ")
			for r := range licenseinfo[i].Tags.Required {
				fmt.Printf("    -%v\n", licenseinfo[i].Tags.Required[r])
			}
			fmt.Println("  Permitted: ")
			for r := range licenseinfo[i].Tags.Permitted {
				fmt.Printf("    -%v\n", licenseinfo[i].Tags.Permitted[r])
			}
			fmt.Println("  Forbidden: ")
			for r := range licenseinfo[i].Tags.Forbidden {
				fmt.Printf("    -%v\n", licenseinfo[i].Tags.Forbidden[r])
			}
		} else {
			fmt.Println("No such license found!")
			fmt.Println("Try `licensethis list` and use the exact short name.")
			break
		}
	}
}

func generate(licensename string) {
	fmt.Println(licensename)
}

// !!---FUTURE---!!
//might have to look into a way to accept more than one license name for `info`.
func parseArgs(args []string) {
	if len(args) == 1 {
		switch args[0] {
		case "help":
			help()
		case "list":
			list()
		default:
			fmt.Println("Incorrect usage!")
			fmt.Println("Type `licensethis help` for help on proper usage.")
		}
	} else if len(args) == 2 {
		switch args[0] {
		case "info":
			info(args[1])
		case "generate":
			generate(args[1])
		default:
			fmt.Println("Incorrect usage!")
			fmt.Println("Type `licensethis help` for help on proper usage.")
		}
	} else {
		fmt.Println("Incorrect usage!")
		fmt.Println("Type `licensethis help` for help on proper usage.")
	}
}

func main() {
	args := os.Args[1:]
	parseArgs(args)
}
<|endoftext|>