<commit_before>package dynago_test

import (
	"os"
	"testing"

	"github.com/stretchr/testify/assert"

	dynago "gopkg.in/underarmour/dynago.v1"
	"gopkg.in/underarmour/dynago.v1/schema"
)

type functional struct {
	client *dynago.Client
}

func (f *functional) setUp(t *testing.T) (*assert.Assertions, *dynago.Client) {
	if testing.Short() {
		t.SkipNow()
	}
	dynago.DebugFunc = t.Logf

	if f.client == nil {
		ge := func(key string, d string) string {
			r := os.Getenv("DYNAGO_TEST_" + key)
			if r == "" {
				r = d
			}
			return r
		}
		endpoint := os.Getenv("DYNAGO_TEST_ENDPOINT")
		if endpoint == "" {
			t.SkipNow()
		}
		executor := dynago.NewAwsExecutor(endpoint, ge("REGION", "us-east-1"), ge("ACCESS_KEY", "AKIAEXAMPLE"), ge("SECRET_KEY", "SECRETEXAMPLE"), "")
		f.client = dynago.NewClient(executor)
		makeTables(t, f.client)

		// Add some posts
		writer := f.client.BatchWrite()
		for i := 100; i < 118; i++ {
			writer = writer.Put("Posts", dynago.Document{"UserId": 42, "Dated": i})
		}
		_, err := writer.Execute()
		assert.NoError(t, err)
	}
	return assert.New(t), f.client
}

var funcTest functional

func makeTables(t *testing.T, client *dynago.Client) {
	complexIndexed := complexIndexedSchema()
	hashTable := schema.NewCreateRequest("Person").HashKey("Id", schema.Number)
	hashRange := schema.NewCreateRequest("Posts").
		HashKey("UserId", schema.Number).
		RangeKey("Dated", schema.Number)

	tables := []*schema.CreateRequest{hashTable, hashRange, complexIndexed}
	for _, table := range tables {
		_, err := client.CreateTable(table)
		if err != nil {
			if e, ok := err.(*dynago.Error); ok && e.Type == dynago.ErrorResourceInUse {
				continue
			}
			panic(err)
		}
	}
}

func complexIndexedSchema() *schema.CreateRequest {
	return &schema.CreateRequest{
		TableName: "Indexed",
		AttributeDefinitions: []schema.AttributeDefinition{
			{"Id", schema.String},
			{"UserId", schema.String},
			{"Dated", schema.Number},
		},
		KeySchema: []schema.KeySchema{
			{"UserId", schema.HashKey},
			{"Id", schema.RangeKey},
		},
		ProvisionedThroughput: schema.NewThroughput(1, 1),
		GlobalSecondaryIndexes: []schema.SecondaryIndex{
			{
				IndexName:  "index1",
				Projection: schema.NewProjection(schema.ProjectAll),
				KeySchema: []schema.KeySchema{
					{"Id", schema.HashKey},
				},
				ProvisionedThroughput: schema.NewThroughput(1, 1),
			},
		},
		LocalSecondaryIndexes: []schema.SecondaryIndex{
			{
				IndexName:  "index2",
				Projection: schema.NewProjection(schema.ProjectInclude, "Foo", "Bar"),
				KeySchema: []schema.KeySchema{
					{"UserId", schema.HashKey},
					{"Dated", schema.RangeKey},
				},
				ProvisionedThroughput: schema.NewThroughput(1, 1),
			},
		},
		StreamSpecification: &schema.StreamSpecification{
			StreamEnabled:  true,
			StreamViewType: "NEW_AND_OLD_IMAGES",
		},
	}
}

func TestBatchGet(t *testing.T) {
	assert, client := funcTest.setUp(t)
	k := func(dated int) dynago.Document {
		return dynago.HashRangeKey("UserId", 42, "Dated", dated)
	}
	g := client.BatchGet().
		Get("Posts", k(100), k(101), k(102)).
		ConsistentRead("Posts", true)

	result, err := g.Execute()
	assert.NoError(err)
	assert.NotNil(result)
	assert.Equal(1, len(result.Responses))
	assert.Equal(3, len(result.Responses["Posts"]))
}

func TestDeleteItem_functional(t *testing.T) {
	assert, client := funcTest.setUp(t)
	_, err := client.PutItem("Person", person(47, "Mary")).Execute()
	assert.NoError(err)

	key := dynago.HashKey("Id", 47)
	di := client.DeleteItem("Person", key).
		ConditionExpression("#n <> :name", dynago.P("#n", "Name"), dynago.P(":name", "Mary")).
		ReturnValues(dynago.ReturnAllOld)
	result, err := di.Execute()
	assert.Nil(result)
	assert.NotNil(err)
	e := err.(*dynago.Error)
	assert.Equal(dynago.ErrorConditionFailed, e.Type)

	result, err = di.ConditionExpression("#n <> :name", dynago.P(":name", "Albert")).Execute()
	assert.NoError(err)
	assert.NotNil(result)
	doc := dynago.Document{"Name": "Mary", "IncVal": dynago.Number("1"), "Id": dynago.Number("47")}
	assert.Equal(doc, result.Attributes)

	result, err = di.ReturnValues(dynago.ReturnNone).Execute()
	assert.Nil(err)
	assert.Nil(result)
}

func TestGet(t *testing.T) {
	assert, client := funcTest.setUp(t)
	putResp, err := client.PutItem("Person", person(42, "Bob")).Execute()
	assert.NoError(err)
	assert.Nil(putResp)

	response, err := client.GetItem("Person", dynago.HashKey("Id", 42)).Execute()
	assert.Equal("Bob", response.Item["Name"])
	assert.IsType(dynago.Number("1"), response.Item["Id"])
	assert.Equal(dynago.Number("42"), response.Item["Id"])
}

func TestPutItemConditional(t *testing.T) {
	assert, client := funcTest.setUp(t)
	doc := person(45, "Joe")
	doc["Count"] = 94
	client.PutItem("Person", doc).Execute()

	doc["Count"] = 45

	basePut := client.PutItem("Person", doc).
		ConditionExpression("#c > :val").
		Param("#c", "Count")

	_, err := basePut.Param(":val", 100).Execute()

	e := err.(*dynago.Error)
	assert.Equal(dynago.ErrorConditionFailed, e.Type)

	_, err = basePut.Param(":val", 50).Execute()
	assert.NoError(err)
}

func TestBatchWrite(t *testing.T) {
	assert, client := funcTest.setUp(t)
	_, err := client.PutItem("Person", person(4, "ToDelete")).Execute()
	assert.NoError(err)

	p1 := person(1, "Joe")
	p2 := person(2, "Mary")
	p3 := person(3, "Amy")
	_, err = client.BatchWrite().
		Put("Person", p1, p2, p3).
		Delete("Person", dynago.HashKey("Id", 4)).
		Execute()

	assert.NoError(err)

	response, err := client.GetItem("Person", dynago.HashKey("Id", 2)).Execute()
	assert.Equal("Mary", response.Item["Name"])
}

func TestUpdateItemConditional(t *testing.T) {
	assert, client := funcTest.setUp(t)
	_, err := client.PutItem("Person", person(5, "ToUpdate")).Execute()
	assert.NoError(err)
	update := client.UpdateItem("Person", dynago.HashKey("Id", 5)).
		UpdateExpression("SET #n = :name").
		ConditionExpression("#n = :orig").
		Param("#n", "Name").Param(":name", "Bob").
		ReturnValues(dynago.ReturnUpdatedNew)

	result, err := update.Param(":orig", "NotValue").Execute()
	assert.Error(err)
	assert.IsType(&dynago.Error{}, err)
	e := err.(*dynago.Error)
	assert.Equal(dynago.ErrorConditionFailed, e.Type)

	result, err = update.Param(":orig", "ToUpdate").Execute()
	assert.NoError(err)
	assert.NotNil(result)
	assert.NotNil(result.Attributes)
	assert.Equal("Bob", result.Attributes["Name"])
}

func TestUpdateItemSimple(t *testing.T) {
	assert, client := funcTest.setUp(t)
	_, err := client.PutItem("Person", person(5, "ToUpdate")).Execute()
	assert.NoError(err)
	result, err := client.UpdateItem("Person", dynago.HashKey("Id", 5)).
		UpdateExpression("SET #n = :name").
		Param("#n", "Name").Param(":name", "Bob").
		Execute()
	assert.NoError(err)
	assert.Nil(result)
}

func TestTableActions(t *testing.T) {
	tables := []string{"abc", "def", "ghi"}
	assert, client := funcTest.setUp(t)
	for _, name := range tables {
		_, err := client.CreateTable(schema.NewCreateRequest(name).HashKey("Id", schema.Number))
		if e, ok := err.(*dynago.Error); !ok || e.Type != dynago.ErrorResourceInUse {
			assert.NoError(err)
		}
	}
	list, err := client.ListTables().Limit(10).Execute()
	assert.NoError(err)
	assert.NotNil(list)
	assert.True(len(list.TableNames) > len(tables))
	assert.Nil(list.Next())

	// Pagination of tables should work
	list1, err := client.ListTables().Limit(2).Execute()
	assert.NoError(err)
	assert.NotNil(list1.Next())
	assert.Equal(2, len(list1.TableNames))
	assert.Equal(list.TableNames[:2], list1.TableNames[:2])
	list2, err := list1.Next().Execute()
	assert.Equal(list.TableNames[2:4], list2.TableNames[:2])

	for _, name := range tables {
		_, err := client.DeleteTable(name)
		assert.NoError(err)
	}

}

func TestDescribeTable(t *testing.T) {
	assert, client := funcTest.setUp(t)
	response, err := client.DescribeTable("Bogus")
	assert.Error(err)

	response, err = client.DescribeTable("Posts")
	assert.NoError(err)
	assert.Equal("Posts", response.Table.TableName)
	assert.Equal(2, len(response.Table.AttributeDefinitions))
	assert.Equal(0, len(response.Table.GlobalSecondaryIndexes))

	response, err = client.DescribeTable("Indexed")
	assert.NoError(err)
	assert.Equal(1, len(response.Table.GlobalSecondaryIndexes))
	assert.Equal(1, len(response.Table.LocalSecondaryIndexes))
	lsi := response.Table.LocalSecondaryIndexes[0]
	assert.Equal("index2", lsi.IndexName)
	assert.Equal(2, len(lsi.KeySchema))
	assert.Equal(schema.ProjectInclude, lsi.Projection.ProjectionType)
	assert.Equal([]string{"Foo", "Bar"}, lsi.Projection.NonKeyAttributes)
}

func TestPutItemReturnValues(t *testing.T) {
	assert, client := funcTest.setUp(t)
	doc := dynago.Document{
		"UserId": 50, "Dated": 2, "Title": "abc",
		"List": dynago.List{"abc", nil, "def"},
	}
	response, err := client.PutItem("Posts", doc).Execute()
	assert.Nil(response)
	assert.NoError(err)

	// Now test return values
	doc["Title"] = "def"
	response, err = client.PutItem("Posts", doc).
		ReturnValues(dynago.ReturnAllOld).
		Execute()
	assert.NoError(err)
	assert.NotNil(response)
	assert.Equal("abc", response.Attributes["Title"])
	assert.Equal(dynago.List{"abc", nil, "def"}, response.Attributes["List"])
}

func TestQueryPagination(t *testing.T) {
	assert, client := funcTest.setUp(t)
	assert.NoError(batchAddPosts(client, 42, 100, 118))

	// Paginate the posts
	q := client.Query("Posts").
		KeyConditionExpression("UserId = :uid", dynago.P(":uid", 42)).
		FilterExpression("Dated <> :d", dynago.P(":d", 101)).
		Limit(10)
	results, err := q.Execute()
	assert.NoError(err)
	assert.Equal(9, len(results.Items))
	assert.Equal(dynago.Number("42"), results.Items[0]["UserId"])
	assert.Equal(dynago.Number("100"), results.Items[0]["Dated"])

	// Check that we skipped something.
	assert.Equal(10, results.ScannedCount)
	assert.Equal(9, results.Count)

	assert.NotNil(results.LastEvaluatedKey)
	assert.Equal(2, len(results.LastEvaluatedKey))
	assert.NotNil(results.Next())

	// page 2, also use ProjectionExpression
	results, err = results.Next().ProjectionExpression("Dated").Execute()
	assert.NoError(err)
	assert.Equal(8, len(results.Items))
	assert.Equal(1, len(results.Items[0]))
	assert.Nil(results.LastEvaluatedKey)
	assert.Nil(results.Next())
}

func TestScanBasic(t *testing.T) {
	assert, client := funcTest.setUp(t)
	assert.NoError(batchAddPosts(client, 50, 100, 120))
	scan := client.Scan("Posts").Limit(20)
	result, err := scan.Execute()
	assert.NoError(err)
	assert.NotEqual(0, len(result.Items))
	assert.NotNil(result.LastEvaluatedKey)
	assert.NotNil(result.Next())
	result2, err := scan.ExclusiveStartKey(result.LastEvaluatedKey).Execute()
	assert.NotEqual(result.Items, result2.Items)
	assert.Nil(result2.Next())

	// ensure Next method matches manual ExclusiveStartKey
	result2a, err := result.Next().Execute()
	assert.NoError(err)
	assert.Equal(result2a.Items, result2.Items)
}

func person(id int, name string) dynago.Document {
	return dynago.Document{"Id": id, "Name": name, "IncVal": 1}
}

func post(uid int, dated int) dynago.Document {
	return dynago.Document{"UserId": uid, "Dated": dated}
}

func batchAddPosts(client *dynago.Client, userId, start, end int) error {
	// Add some posts
	writer := client.BatchWrite()
	for i := start; i < end; i++ {
		writer = writer.Put("Posts", dynago.Document{"UserId": userId, "Dated": i})
	}
	_, err := writer.Execute()
	return err
}
<commit_msg>Fix test<commit_after>package dynago_test

import (
	"os"
	"testing"

	"github.com/stretchr/testify/assert"

	dynago "gopkg.in/underarmour/dynago.v1"
	"gopkg.in/underarmour/dynago.v1/schema"
)

type functional struct {
	client *dynago.Client
}

func (f *functional) setUp(t *testing.T) (*assert.Assertions, *dynago.Client) {
	if testing.Short() {
		t.SkipNow()
	}
	dynago.DebugFunc = t.Logf

	if f.client == nil {
		ge := func(key string, d string) string {
			r := os.Getenv("DYNAGO_TEST_" + key)
			if r == "" {
				r = d
			}
			return r
		}
		endpoint := os.Getenv("DYNAGO_TEST_ENDPOINT")
		if endpoint == "" {
			t.SkipNow()
		}
		executor := dynago.NewAwsExecutor(endpoint, ge("REGION", "us-east-1"), ge("ACCESS_KEY", "AKIAEXAMPLE"), ge("SECRET_KEY", "SECRETEXAMPLE"), "")
		f.client = dynago.NewClient(executor)
		makeTables(t, f.client)

		// Add some posts
		writer := f.client.BatchWrite()
		for i := 100; i < 118; i++ {
			writer = writer.Put("Posts", dynago.Document{"UserId": 42, "Dated": i, "NonPrimary": i})
		}
		_, err := writer.Execute()
		assert.NoError(t, err)
	}
	return assert.New(t), f.client
}

var funcTest functional

func makeTables(t *testing.T, client *dynago.Client) {
	complexIndexed := complexIndexedSchema()
	hashTable := schema.NewCreateRequest("Person").HashKey("Id", schema.Number)
	hashRange := schema.NewCreateRequest("Posts").
		HashKey("UserId", schema.Number).
		RangeKey("Dated", schema.Number)

	tables := []*schema.CreateRequest{hashTable, hashRange, complexIndexed}
	for _, table := range tables {
		_, err := client.CreateTable(table)
		if err != nil {
			if e, ok := err.(*dynago.Error); ok && e.Type == dynago.ErrorResourceInUse {
				continue
			}
			panic(err)
		}
	}
}

func complexIndexedSchema() *schema.CreateRequest {
	return &schema.CreateRequest{
		TableName: "Indexed",
		AttributeDefinitions: []schema.AttributeDefinition{
			{"Id", schema.String},
			{"UserId", schema.String},
			{"Dated", schema.Number},
		},
		KeySchema: []schema.KeySchema{
			{"UserId", schema.HashKey},
			{"Id", schema.RangeKey},
		},
		ProvisionedThroughput: schema.NewThroughput(1, 1),
		GlobalSecondaryIndexes: []schema.SecondaryIndex{
			{
				IndexName:  "index1",
				Projection: schema.NewProjection(schema.ProjectAll),
				KeySchema: []schema.KeySchema{
					{"Id", schema.HashKey},
				},
				ProvisionedThroughput: schema.NewThroughput(1, 1),
			},
		},
		LocalSecondaryIndexes: []schema.SecondaryIndex{
			{
				IndexName:  "index2",
				Projection: schema.NewProjection(schema.ProjectInclude, "Foo", "Bar"),
				KeySchema: []schema.KeySchema{
					{"UserId", schema.HashKey},
					{"Dated", schema.RangeKey},
				},
				ProvisionedThroughput: schema.NewThroughput(1, 1),
			},
		},
		StreamSpecification: &schema.StreamSpecification{
			StreamEnabled:  true,
			StreamViewType: "NEW_AND_OLD_IMAGES",
		},
	}
}

func TestBatchGet(t *testing.T) {
	assert, client := funcTest.setUp(t)
	k := func(dated int) dynago.Document {
		return dynago.HashRangeKey("UserId", 42, "Dated", dated)
	}
	g := client.BatchGet().
		Get("Posts", k(100), k(101), k(102)).
		ConsistentRead("Posts", true)

	result, err := g.Execute()
	assert.NoError(err)
	assert.NotNil(result)
	assert.Equal(1, len(result.Responses))
	assert.Equal(3, len(result.Responses["Posts"]))
}

func TestDeleteItem_functional(t *testing.T) {
	assert, client := funcTest.setUp(t)
	_, err := client.PutItem("Person", person(47, "Mary")).Execute()
	assert.NoError(err)

	key := dynago.HashKey("Id", 47)
	di := client.DeleteItem("Person", key).
		ConditionExpression("#n <> :name", dynago.P("#n", "Name"), dynago.P(":name", "Mary")).
		ReturnValues(dynago.ReturnAllOld)
	result, err := di.Execute()
	assert.Nil(result)
	assert.NotNil(err)
	e := err.(*dynago.Error)
	assert.Equal(dynago.ErrorConditionFailed, e.Type)

	result, err = di.ConditionExpression("#n <> :name", dynago.P(":name", "Albert")).Execute()
	assert.NoError(err)
	assert.NotNil(result)
	doc := dynago.Document{"Name": "Mary", "IncVal": dynago.Number("1"), "Id": dynago.Number("47")}
	assert.Equal(doc, result.Attributes)

	result, err = di.ReturnValues(dynago.ReturnNone).Execute()
	assert.Nil(err)
	assert.Nil(result)
}

func TestGet(t *testing.T) {
	assert, client := funcTest.setUp(t)
	putResp, err := client.PutItem("Person", person(42, "Bob")).Execute()
	assert.NoError(err)
	assert.Nil(putResp)

	response, err := client.GetItem("Person", dynago.HashKey("Id", 42)).Execute()
	assert.Equal("Bob", response.Item["Name"])
	assert.IsType(dynago.Number("1"), response.Item["Id"])
	assert.Equal(dynago.Number("42"), response.Item["Id"])
}

func TestPutItemConditional(t *testing.T) {
	assert, client := funcTest.setUp(t)
	doc := person(45, "Joe")
	doc["Count"] = 94
	client.PutItem("Person", doc).Execute()

	doc["Count"] = 45

	basePut := client.PutItem("Person", doc).
		ConditionExpression("#c > :val").
		Param("#c", "Count")

	_, err := basePut.Param(":val", 100).Execute()

	e := err.(*dynago.Error)
	assert.Equal(dynago.ErrorConditionFailed, e.Type)

	_, err = basePut.Param(":val", 50).Execute()
	assert.NoError(err)
}

func TestBatchWrite(t *testing.T) {
	assert, client := funcTest.setUp(t)
	_, err := client.PutItem("Person", person(4, "ToDelete")).Execute()
	assert.NoError(err)

	p1 := person(1, "Joe")
	p2 := person(2, "Mary")
	p3 := person(3, "Amy")
	_, err = client.BatchWrite().
		Put("Person", p1, p2, p3).
		Delete("Person", dynago.HashKey("Id", 4)).
		Execute()

	assert.NoError(err)

	response, err := client.GetItem("Person", dynago.HashKey("Id", 2)).Execute()
	assert.Equal("Mary", response.Item["Name"])
}

func TestUpdateItemConditional(t *testing.T) {
	assert, client := funcTest.setUp(t)
	_, err := client.PutItem("Person", person(5, "ToUpdate")).Execute()
	assert.NoError(err)
	update := client.UpdateItem("Person", dynago.HashKey("Id", 5)).
		UpdateExpression("SET #n = :name").
		ConditionExpression("#n = :orig").
		Param("#n", "Name").Param(":name", "Bob").
		ReturnValues(dynago.ReturnUpdatedNew)

	result, err := update.Param(":orig", "NotValue").Execute()
	assert.Error(err)
	assert.IsType(&dynago.Error{}, err)
	e := err.(*dynago.Error)
	assert.Equal(dynago.ErrorConditionFailed, e.Type)

	result, err = update.Param(":orig", "ToUpdate").Execute()
	assert.NoError(err)
	assert.NotNil(result)
	assert.NotNil(result.Attributes)
	assert.Equal("Bob", result.Attributes["Name"])
}

func TestUpdateItemSimple(t *testing.T) {
	assert, client := funcTest.setUp(t)
	_, err := client.PutItem("Person", person(5, "ToUpdate")).Execute()
	assert.NoError(err)
	result, err := client.UpdateItem("Person", dynago.HashKey("Id", 5)).
		UpdateExpression("SET #n = :name").
		Param("#n", "Name").Param(":name", "Bob").
		Execute()
	assert.NoError(err)
	assert.Nil(result)
}

func TestTableActions(t *testing.T) {
	tables := []string{"abc", "def", "ghi"}
	assert, client := funcTest.setUp(t)
	for _, name := range tables {
		_, err := client.CreateTable(schema.NewCreateRequest(name).HashKey("Id", schema.Number))
		if e, ok := err.(*dynago.Error); !ok || e.Type != dynago.ErrorResourceInUse {
			assert.NoError(err)
		}
	}
	list, err := client.ListTables().Limit(10).Execute()
	assert.NoError(err)
	assert.NotNil(list)
	assert.True(len(list.TableNames) > len(tables))
	assert.Nil(list.Next())

	// Pagination of tables should work
	list1, err := client.ListTables().Limit(2).Execute()
	assert.NoError(err)
	assert.NotNil(list1.Next())
	assert.Equal(2, len(list1.TableNames))
	assert.Equal(list.TableNames[:2], list1.TableNames[:2])
	list2, err := list1.Next().Execute()
	assert.Equal(list.TableNames[2:4], list2.TableNames[:2])

	for _, name := range tables {
		_, err := client.DeleteTable(name)
		assert.NoError(err)
	}

}

func TestDescribeTable(t *testing.T) {
	assert, client := funcTest.setUp(t)
	response, err := client.DescribeTable("Bogus")
	assert.Error(err)

	response, err = client.DescribeTable("Posts")
	assert.NoError(err)
	assert.Equal("Posts", response.Table.TableName)
	assert.Equal(2, len(response.Table.AttributeDefinitions))
	assert.Equal(0, len(response.Table.GlobalSecondaryIndexes))

	response, err = client.DescribeTable("Indexed")
	assert.NoError(err)
	assert.Equal(1, len(response.Table.GlobalSecondaryIndexes))
	assert.Equal(1, len(response.Table.LocalSecondaryIndexes))
	lsi := response.Table.LocalSecondaryIndexes[0]
	assert.Equal("index2", lsi.IndexName)
	assert.Equal(2, len(lsi.KeySchema))
	assert.Equal(schema.ProjectInclude, lsi.Projection.ProjectionType)
	assert.Equal([]string{"Foo", "Bar"}, lsi.Projection.NonKeyAttributes)
}

func TestPutItemReturnValues(t *testing.T) {
	assert, client := funcTest.setUp(t)
	doc := dynago.Document{
		"UserId": 50, "Dated": 2, "Title": "abc",
		"List": dynago.List{"abc", nil, "def"},
	}
	response, err := client.PutItem("Posts", doc).Execute()
	assert.Nil(response)
	assert.NoError(err)

	// Now test return values
	doc["Title"] = "def"
	response, err = client.PutItem("Posts", doc).
		ReturnValues(dynago.ReturnAllOld).
		Execute()
	assert.NoError(err)
	assert.NotNil(response)
	assert.Equal("abc", response.Attributes["Title"])
	assert.Equal(dynago.List{"abc", nil, "def"}, response.Attributes["List"])
}

func TestQueryPagination(t *testing.T) {
	assert, client := funcTest.setUp(t)
	assert.NoError(batchAddPosts(client, 42, 100, 118))

	// Paginate the posts
	q := client.Query("Posts").
		KeyConditionExpression("UserId = :uid", dynago.P(":uid", 42)).
		FilterExpression("NonPrimary <> :d", dynago.P(":d", 101)).
		Limit(10)
	results, err := q.Execute()
	assert.NoError(err)
	assert.Equal(10, len(results.Items))
	assert.Equal(dynago.Number("42"), results.Items[0]["UserId"])
	assert.Equal(dynago.Number("100"), results.Items[0]["Dated"])

	// Check that we skipped something.
	assert.Equal(10, results.ScannedCount)
	assert.Equal(10, results.Count)

	assert.NotNil(results.LastEvaluatedKey)
	assert.Equal(2, len(results.LastEvaluatedKey))
	assert.NotNil(results.Next())

	// page 2, also use ProjectionExpression
	results, err = results.Next().ProjectionExpression("Dated").Execute()
	assert.NoError(err)
	assert.Equal(8, len(results.Items))
	assert.Equal(1, len(results.Items[0]))
	assert.Nil(results.LastEvaluatedKey)
	assert.Nil(results.Next())
}

func TestScanBasic(t *testing.T) {
	assert, client := funcTest.setUp(t)
	assert.NoError(batchAddPosts(client, 50, 100, 120))
	scan := client.Scan("Posts").Limit(20)
	result, err := scan.Execute()
	assert.NoError(err)
	assert.NotEqual(0, len(result.Items))
	assert.NotNil(result.LastEvaluatedKey)
	assert.NotNil(result.Next())
	result2, err := scan.ExclusiveStartKey(result.LastEvaluatedKey).Execute()
	assert.NotEqual(result.Items, result2.Items)
	assert.Nil(result2.Next())

	// ensure Next method matches manual ExclusiveStartKey
	result2a, err := result.Next().Execute()
	assert.NoError(err)
	assert.Equal(result2a.Items, result2.Items)
}

func person(id int, name string) dynago.Document {
	return dynago.Document{"Id": id, "Name": name, "IncVal": 1}
}

func post(uid int, dated int) dynago.Document {
	return dynago.Document{"UserId": uid, "Dated": dated}
}

func batchAddPosts(client *dynago.Client, userId, start, end int) error {
	// Add some posts
	writer := client.BatchWrite()
	for i := start; i < end; i++ {
		writer = writer.Put("Posts", dynago.Document{"UserId": userId, "Dated": i})
	}
	_, err := writer.Execute()
	return err
}
<|endoftext|>


<commit_before>// Copyright 2017 Jayson Grace. All rights reserved
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package main

import (
	"fmt"
	"os/exec"
	"regexp"
	"strings"
	"sync"
)

// exeCmd executes an input command.
// Once the command have successfully ben run, it will
// return a string with the output result of the command.
// TODO: Add concurrent operations to speed things up
func exeCmd(cmd string, wg *sync.WaitGroup) string {
	fmt.Println("Running: ", cmd)
	parts := strings.Fields(cmd)
	head := parts[0]
	parts = parts[1:]

	out, err := exec.Command(head, parts...).Output()
	if err != nil {
		errmsg("%s", err)
	}
	warn("%s", out)
	wg.Done()
	return string(out)
}

// strToSlice takes a string and a delimiter in the
// form of a regex. It will use this to split a string
// into a slice, and return it.
func strToSlice(s string, delimiter string) []string {
	r := regexp.MustCompile(delimiter)
	slice := r.FindAllString(s, -1)
	return slice
}
<commit_msg>Rewrote func documentation<commit_after>// Copyright 2017 Jayson Grace. All rights reserved
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package main

import (
	"fmt"
	"os/exec"
	"regexp"
	"strings"
	"sync"
)

// exeCmd executes an input command.
// Return a string with the output result of the command if
// it has run successfully.
// TODO: Add concurrent operations to speed things up
func exeCmd(cmd string, wg *sync.WaitGroup) string {
	fmt.Println("Running: ", cmd)
	parts := strings.Fields(cmd)
	head := parts[0]
	parts = parts[1:]

	out, err := exec.Command(head, parts...).Output()
	if err != nil {
		errmsg("%s", err)
	}
	warn("%s", out)
	wg.Done()
	return string(out)
}

// strToSlice takes a string and a delimiter in the
// form of a regex. It will use this to split a string
// into a slice, and return it.
func strToSlice(s string, delimiter string) []string {
	r := regexp.MustCompile(delimiter)
	slice := r.FindAllString(s, -1)
	return slice
}
<|endoftext|>


<commit_before>// package waddell implements a low-latency signaling server that allows peers
// to exchange small messages (up to around 64kB) over TCP.  It is named after
// William B. Waddell, one of the founders of the Pony Express.
//
// Peers are identified by randomly assigned peer ids (type 4 UUIDs), which are
// used to address messages to the peers.  For the scheme to work, peers must
// have some out-of-band mechanism by which they can exchange peer ids.  Note
// that as soon as one peer contacts another via waddell, the 2nd peer will have
// the 1st peer's address and be able to reply using it.
//
// Peers can obtain new ids simply by reconnecting to waddell, and depending on
// security requirements it may be a good idea to do so periodically.
//
//
// Here is an example exchange between two peers:
//
//   peer 1 -> waddell server : connect
//   waddell server -> peer 1 : send newly assigned peer id
//   peer 2 -> waddell server : connect
//   waddell server -> peer 2 : send newly assigned peer id
//   (out of band) peer 1 lets peer 2 know about its id
//   peer 2 -> waddell server : send message to peer 1
//   waddell server -> peer 1 : deliver message from peer 2 (includes peer 2's id)
//   peer 1 -> waddell server : send message to peer 2
//   etc ..
//
//
// Message structure on the wire (bits):
//
//   0-16    Frame Length - waddell uses github.com/getlantern/framed to frame
//                          messages. framed uses the first 16 bits of the
//                          message to indicate the length of the frame (Little
//                          Endian).
//
//   16-144  Sender/Recipient Address - Two 64-bit integers encoded in Little
//                                      Endian byte order.
//
//   144+    Message Body - whatever data the client sent
//
package waddell

import (
	"encoding/binary"
	"fmt"

	"code.google.com/p/go-uuid/uuid"
)

const (
	PEER_ID_LENGTH   = 16
	WADDELL_OVERHEAD = 18 // bytes of overhead imposed by waddell
)

var endianness = binary.LittleEndian

// PeerId is an identifier for a waddell peer, composed of two 64 bit integers
// representing a type 4 UUID.
type PeerId struct {
	part1 uint64
	part2 uint64
}

// PeerIdFromString constructs a PeerId from the string-encoded version of a
// uuid.UUID.
func PeerIdFromString(s string) (PeerId, error) {
	return readPeerId(uuid.Parse(s))
}

func (id PeerId) String() string {
	b := uuid.UUID(make([]byte, 16))
	id.write([]byte(b))
	return b.String()
}

func readPeerId(b []byte) (PeerId, error) {
	if len(b) < PEER_ID_LENGTH {
		return PeerId{}, fmt.Errorf("Insufficient data to read peer id, message may be truncated")
	}
	return PeerId{
		endianness.Uint64(b[0:8]),
		endianness.Uint64(b[8:]),
	}, nil
}

func randomPeerId() PeerId {
	id, err := readPeerId(uuid.NewRandom())
	if err != nil {
		panic(fmt.Sprintf("Unable to generate random peer id: %s", err))
	}
	return id
}

func (id PeerId) write(b []byte) error {
	if len(b) < PEER_ID_LENGTH {
		return fmt.Errorf("Insufficient room to write peer id")
	}
	endianness.PutUint64(b, id.part1)
	endianness.PutUint64(b[8:], id.part2)
	return nil
}

func (id PeerId) toBytes() []byte {
	b := make([]byte, PEER_ID_LENGTH)
	id.write(b)
	return b
}
<commit_msg>Godoc updates<commit_after>// package waddell implements a low-latency signaling server that allows peers
// to exchange small messages (up to around 64kB) over TCP.  It is named after
// William B. Waddell, one of the founders of the Pony Express.
//
// Peers are identified by randomly assigned peer ids (type 4 UUIDs), which are
// used to address messages to the peers.  For the scheme to work, peers must
// have some out-of-band mechanism by which they can exchange peer ids.  Note
// that as soon as one peer contacts another via waddell, the 2nd peer will have
// the 1st peer's address and be able to reply using it.
//
// Peers can obtain new ids simply by reconnecting to waddell, and depending on
// security requirements it may be a good idea to do so periodically.
//
//
// Here is an example exchange between two peers:
//
//   peer 1 -> waddell server : connect
//   waddell server -> peer 1 : send newly assigned peer id
//   peer 2 -> waddell server : connect
//   waddell server -> peer 2 : send newly assigned peer id
//   (out of band) peer 1 lets peer 2 know about its id
//   peer 2 -> waddell server : send message to peer 1
//   waddell server -> peer 1 : deliver message from peer 2 (includes peer 2's id)
//   peer 1 -> waddell server : send message to peer 2
//   etc ..
//
//
// Message structure on the wire (bits):
//
//   0-15    Frame Length       - waddell uses github.com/getlantern/framed to
//                               frame messages. framed uses the first 16 bits
//                               of the message to indicate the length of the
//                               frame (Little Endian).
//   16-79   Sender Address     - 64-bit integer in Little Endian byte order
//   80-143  Recipient Address  - 64-bit integer in Little Endian byte order
//   144+    Message Body       - whatever data the client sent
//
package waddell

import (
	"encoding/binary"
	"fmt"

	"code.google.com/p/go-uuid/uuid"
)

const (
	PEER_ID_LENGTH   = 16
	WADDELL_OVERHEAD = 18 // bytes of overhead imposed by waddell
)

var endianness = binary.LittleEndian

// PeerId is an identifier for a waddell peer, composed of two 64 bit integers
// representing a type 4 UUID.
type PeerId struct {
	part1 uint64
	part2 uint64
}

// PeerIdFromString constructs a PeerId from the string-encoded version of a
// uuid.UUID.
func PeerIdFromString(s string) (PeerId, error) {
	return readPeerId(uuid.Parse(s))
}

func (id PeerId) String() string {
	b := uuid.UUID(make([]byte, 16))
	id.write([]byte(b))
	return b.String()
}

func readPeerId(b []byte) (PeerId, error) {
	if len(b) < PEER_ID_LENGTH {
		return PeerId{}, fmt.Errorf("Insufficient data to read peer id, message may be truncated")
	}
	return PeerId{
		endianness.Uint64(b[0:8]),
		endianness.Uint64(b[8:]),
	}, nil
}

func randomPeerId() PeerId {
	id, err := readPeerId(uuid.NewRandom())
	if err != nil {
		panic(fmt.Sprintf("Unable to generate random peer id: %s", err))
	}
	return id
}

func (id PeerId) write(b []byte) error {
	if len(b) < PEER_ID_LENGTH {
		return fmt.Errorf("Insufficient room to write peer id")
	}
	endianness.PutUint64(b, id.part1)
	endianness.PutUint64(b[8:], id.part2)
	return nil
}

func (id PeerId) toBytes() []byte {
	b := make([]byte, PEER_ID_LENGTH)
	id.write(b)
	return b
}
<|endoftext|>


<commit_before>package QesyGo

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"time"
)

type RandWeiht struct {
	Name   string
	Weight int
}

var randSeek int64 = 0

type RandWeihtArr []RandWeiht

func Substr(str string, start int, end int) string {
	var endNum int
	s := []byte(str)
	if end > 0 {
		endNum = start + end
	} else {
		endNum = len(str) + end
	}
	return string(s[start:endNum])
}

func Rand(Min int, Max int) int {
	tempNum := Max - Min
	if randSeek > 9 {
		randSeek = 0
	} else {
		randSeek++
	}
	Println(randSeek)
	rand.Seed(time.Now().UnixNano() + randSeek)
	return Min + rand.Intn(tempNum)
}

func Rate(num int) bool {
	rand := rand.Intn(100) + 1
	if rand <= num {
		return true
	} else {
		return false
	}
}

/*
* RandWeihtArr := &lib.RandWeihtArr{{"user1",8}, {"user2",1},{"user3",1}}
* who := RandWeihtArr.RandWeight()
 */
func (arr *RandWeihtArr) RandWeight() string {
	var all int
	for _, v := range *arr {
		all += v.Weight
	}
	plusNum := 0
	tempArr := make(map[string][2]int)
	for _, v := range *arr {
		plusNum += v.Weight
		tempArr[v.Name] = [2]int{plusNum - v.Weight, plusNum}
	}
	randNum := Rand(0, all) + 1
	var ret string
	for k, v := range tempArr {
		if randNum > v[0] && randNum <= v[1] {
			ret = k
			break
		}
	}
	return ret
}

func ReadFile(str string) ([]byte, error) {
	return ioutil.ReadFile(str)
}

func JsonEncode(arr interface{}) ([]byte, error) {
	return json.Marshal(arr)
}

func JsonDecode(str []byte, jsonArr interface{}) error {
	strNew := string(str)
	if strNew == "null" || strNew == "" {
		return nil
	}
	err := json.Unmarshal(str, jsonArr)
	return err

}

func Printf(format string, a ...interface{}) {
	fmt.Printf(format, a)
}

func Fprintf(w http.ResponseWriter, str string) {
	fmt.Fprintf(w, str)
}

func Die(v interface{}) {
	log.Fatal(v)
}

func Implode(arr []string, sep string) string {
	return strings.Join(arr, sep)
}

func Explode(str string, sep string) []string {
	if str == "" {
		return []string{}
	}
	return strings.Split(str, sep)
}

func Err(str string) error {
	return errors.New(str)
}

func Println(str ...interface{}) {
	fmt.Println(str)
}

func Time(str string) int64 {
	now := time.Now()
	t := now.UnixNano()
	switch str {
	case "Millisecond":
		t = now.UnixNano() / 1000
	case "Microsecond":
		t = now.UnixNano() / 1000000
	case "Second":
		t = now.UnixNano() / 1000000000
	}
	return t
}

func TimeStr(str string) string {
	t := Time(str)
	return strconv.FormatInt(t, 10)
}

func TimeInt(str string) int {
	t := Time(str)
	ret, _ := Int64ToInt(t)
	return ret
}

//-- format : "2006-01-02 03:04:05 PM" --
/*
月份 1,01,Jan,January
日　 2,02,_2
时　 3,03,15,PM,pm,AM,am
分　 4,04
秒　 5,05
年　 06,2006
周几 Mon,Monday
时区时差表示 -07,-0700,Z0700,Z07:00,-07:00,MST
时区字母缩写 MST
*/
func Date(timestamp int64, format string) string {
	tm := time.Unix(timestamp, 0)
	return tm.Format(format)
}

//-- "01/02/2006", "02/08/2015" --
func StrToTime(format string, input string) int64 {
	tm2, _ := time.Parse(format, input)
	return tm2.Unix()
}

func Int64ToInt(num int64) (int, error) {
	str := strconv.FormatInt(num, 10)
	return strconv.Atoi(str)
}

func Unset(arr []string, str string) []string {
	newArr := []string{}
	for _, v := range arr {
		if v != str && v != "" {
			newArr = append(newArr, v)
		}
	}
	return newArr
}

//-- kind 0:纯数字，1：小写，2：大写，3：数字+大小写字幕 --
func Krand(size int, kind int) []byte {
	ikind, kinds, result := kind, [][]int{[]int{10, 48}, []int{26, 97}, []int{26, 65}}, make([]byte, size)
	is_all := kind > 2 || kind < 0
	rand.Seed(time.Now().UnixNano())
	for i := 0; i < size; i++ {
		if is_all { // random ikind
			ikind = rand.Intn(3)
		}
		scope, base := kinds[ikind][0], kinds[ikind][1]
		result[i] = uint8(base + rand.Intn(scope))
	}
	return result
}
<commit_msg>update rand<commit_after>package QesyGo

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"time"
)

type RandWeiht struct {
	Name   string
	Weight int
}

var randSeek int64 = 0

type RandWeihtArr []RandWeiht

func Substr(str string, start int, end int) string {
	var endNum int
	s := []byte(str)
	if end > 0 {
		endNum = start + end
	} else {
		endNum = len(str) + end
	}
	return string(s[start:endNum])
}

func Rand(Min int, Max int) int {
	tempNum := Max - Min
	if randSeek > 9999999999 {
		randSeek = 0
	} else {
		randSeek++
	}
	rand.Seed(time.Now().UnixNano() + randSeek)
	return Min + rand.Intn(tempNum)
}

func Rate(num int) bool {
	rand := Rand(1, 100)
	if rand <= num {
		return true
	} else {
		return false
	}
}

/*
* RandWeihtArr := &lib.RandWeihtArr{{"user1",8}, {"user2",1},{"user3",1}}
* who := RandWeihtArr.RandWeight()
 */
func (arr *RandWeihtArr) RandWeight() string {
	var all int
	for _, v := range *arr {
		all += v.Weight
	}
	plusNum := 0
	tempArr := make(map[string][2]int)
	for _, v := range *arr {
		plusNum += v.Weight
		tempArr[v.Name] = [2]int{plusNum - v.Weight, plusNum}
	}
	randNum := Rand(0, all) + 1
	var ret string
	for k, v := range tempArr {
		if randNum > v[0] && randNum <= v[1] {
			ret = k
			break
		}
	}
	return ret
}

func ReadFile(str string) ([]byte, error) {
	return ioutil.ReadFile(str)
}

func JsonEncode(arr interface{}) ([]byte, error) {
	return json.Marshal(arr)
}

func JsonDecode(str []byte, jsonArr interface{}) error {
	strNew := string(str)
	if strNew == "null" || strNew == "" {
		return nil
	}
	err := json.Unmarshal(str, jsonArr)
	return err

}

func Printf(format string, a ...interface{}) {
	fmt.Printf(format, a)
}

func Fprintf(w http.ResponseWriter, str string) {
	fmt.Fprintf(w, str)
}

func Die(v interface{}) {
	log.Fatal(v)
}

func Implode(arr []string, sep string) string {
	return strings.Join(arr, sep)
}

func Explode(str string, sep string) []string {
	if str == "" {
		return []string{}
	}
	return strings.Split(str, sep)
}

func Err(str string) error {
	return errors.New(str)
}

func Println(str ...interface{}) {
	fmt.Println(str)
}

func Time(str string) int64 {
	now := time.Now()
	t := now.UnixNano()
	switch str {
	case "Millisecond":
		t = now.UnixNano() / 1000
	case "Microsecond":
		t = now.UnixNano() / 1000000
	case "Second":
		t = now.UnixNano() / 1000000000
	}
	return t
}

func TimeStr(str string) string {
	t := Time(str)
	return strconv.FormatInt(t, 10)
}

func TimeInt(str string) int {
	t := Time(str)
	ret, _ := Int64ToInt(t)
	return ret
}

//-- format : "2006-01-02 03:04:05 PM" --
/*
月份 1,01,Jan,January
日　 2,02,_2
时　 3,03,15,PM,pm,AM,am
分　 4,04
秒　 5,05
年　 06,2006
周几 Mon,Monday
时区时差表示 -07,-0700,Z0700,Z07:00,-07:00,MST
时区字母缩写 MST
*/
func Date(timestamp int64, format string) string {
	tm := time.Unix(timestamp, 0)
	return tm.Format(format)
}

//-- "01/02/2006", "02/08/2015" --
func StrToTime(format string, input string) int64 {
	tm2, _ := time.Parse(format, input)
	return tm2.Unix()
}

func Int64ToInt(num int64) (int, error) {
	str := strconv.FormatInt(num, 10)
	return strconv.Atoi(str)
}

func Unset(arr []string, str string) []string {
	newArr := []string{}
	for _, v := range arr {
		if v != str && v != "" {
			newArr = append(newArr, v)
		}
	}
	return newArr
}

//-- kind 0:纯数字，1：小写，2：大写，3：数字+大小写字幕 --
func Krand(size int, kind int) []byte {
	ikind, kinds, result := kind, [][]int{[]int{10, 48}, []int{26, 97}, []int{26, 65}}, make([]byte, size)
	is_all := kind > 2 || kind < 0
	rand.Seed(time.Now().UnixNano())
	for i := 0; i < size; i++ {
		if is_all { // random ikind
			ikind = Rand(1, 3)
		}
		scope, base := kinds[ikind][0], kinds[ikind][1]
		result[i] = uint8(base + Rand(1, scope))
	}
	return result
}
<|endoftext|>


<commit_before>// common.go
//
// This file implements the configuration part for when you need the API
// key to modify things in the Atlas configuration and manage measurements.

package atlas

import (
    "regexp"
    "errors"
	"github.com/sendgrid/rest"
	"fmt"
	"encoding/json"
)

const (
	apiEndpoint = "https://atlas.ripe.net/api/v2"

	ourVersion = "0.9"
)

// APIKey is the API key
var APIKey string

// ErrInvalidMeasurementType is a new error
var ErrInvalidMeasurementType = errors.New("invalid measurement type")

// ErrInvalidAPIKey is returned when the key is invalid
var ErrInvalidAPIKey = errors.New("invalid API key")

// SetAuth stores the credentials for later use
func SetAuth(key string) {
	APIKey = key
}

// HasAPIKey returns whether an API key is stored
func HasAPIKey() (string, bool) {
	if APIKey == "" {
		return "", false
	}
	return APIKey, true
}

// getPageNum returns the value of the page= parameter
func getPageNum(url string) (page string) {
	re := regexp.MustCompile(`page=(\d+)`)
	if m := re.FindStringSubmatch(url); len(m) >= 1 {
		return m[1]
	}
	return ""
}

// prepareRequest insert all pre-defined stuff
func prepareRequest(what string) (req rest.Request) {
	endPoint := apiEndpoint + fmt.Sprintf("/%s/", what)
	key, ok := HasAPIKey()

	// Add at least one option, the APIkey if present
	hdrs := make(map[string]string)
	opts := make(map[string]string)

	// Insert our sig
	hdrs["User-Agent"] = fmt.Sprintf("ripe-atlas/%s", ourVersion)

	// Insert key
	if ok {
		opts["key"] = key
	}

	req = rest.Request{
		BaseURL: endPoint,
		Headers: hdrs,
		QueryParams: opts,
	}
	return
}

// handleAPIResponse check status code & errors from the API
func handleAPIResponse(r *rest.Response) (err error) {
	if r.StatusCode != 200 || r.StatusCode != 0 {
		var aerr APIError

		err = json.Unmarshal([]byte(r.Body), &aerr)
		err = fmt.Errorf("status: %d code: %d - r:%v\n",
			aerr.Error.Status,
			aerr.Error.Code,
			aerr.Error.Detail)
	}
	return
}
<commit_msg>Simplify always-true expression.<commit_after>// common.go
//
// This file implements the configuration part for when you need the API
// key to modify things in the Atlas configuration and manage measurements.

package atlas

import (
    "regexp"
    "errors"
	"github.com/sendgrid/rest"
	"fmt"
	"encoding/json"
)

const (
	apiEndpoint = "https://atlas.ripe.net/api/v2"

	ourVersion = "0.9"
)

// APIKey is the API key
var APIKey string

// ErrInvalidMeasurementType is a new error
var ErrInvalidMeasurementType = errors.New("invalid measurement type")

// ErrInvalidAPIKey is returned when the key is invalid
var ErrInvalidAPIKey = errors.New("invalid API key")

// SetAuth stores the credentials for later use
func SetAuth(key string) {
	APIKey = key
}

// HasAPIKey returns whether an API key is stored
func HasAPIKey() (string, bool) {
	if APIKey == "" {
		return "", false
	}
	return APIKey, true
}

// getPageNum returns the value of the page= parameter
func getPageNum(url string) (page string) {
	re := regexp.MustCompile(`page=(\d+)`)
	if m := re.FindStringSubmatch(url); len(m) >= 1 {
		return m[1]
	}
	return ""
}

// prepareRequest insert all pre-defined stuff
func prepareRequest(what string) (req rest.Request) {
	endPoint := apiEndpoint + fmt.Sprintf("/%s/", what)
	key, ok := HasAPIKey()

	// Add at least one option, the APIkey if present
	hdrs := make(map[string]string)
	opts := make(map[string]string)

	// Insert our sig
	hdrs["User-Agent"] = fmt.Sprintf("ripe-atlas/%s", ourVersion)

	// Insert key
	if ok {
		opts["key"] = key
	}

	req = rest.Request{
		BaseURL: endPoint,
		Headers: hdrs,
		QueryParams: opts,
	}
	return
}

// handleAPIResponse check status code & errors from the API
func handleAPIResponse(r *rest.Response) (err error) {
	if r.StatusCode != 200 {
		if r.StatusCode != 0 {
			var aerr APIError

			err = json.Unmarshal([]byte(r.Body), &aerr)
			err = fmt.Errorf("status: %d code: %d - r:%v\n",
				aerr.Error.Status,
				aerr.Error.Code,
				aerr.Error.Detail)
		}
	}
	return
}
<|endoftext|>


<commit_before>// Copyright 2015 Robert S. Gerus. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package config

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"os"
	"sort"
	"sync"
	"time"
)

type cacheEntry struct {
	modTime  time.Time
	contents interface{}
}

type config struct {
	cache         map[string]cacheEntry
	buildFileList func(map[string]string) []string
	l             sync.Mutex
}

var c config

func init() {
	log.Println("Initializing configuration cache")
	c.cache = make(map[string]cacheEntry)
}

func lookupvar(key, path string) interface{} {
	var f interface{}
	i, err := os.Stat(path)
	_, ok := c.cache[path]
	if os.IsNotExist(err) {
		log.Println("Config does not exist", path)
		if ok {
			log.Println("Purging", path, "from cache")
			delete(c.cache, path)
		}
	}
	if err != nil {
		return nil
	}

	if c.cache[path].modTime.Before(i.ModTime()) || !ok {
		log.Println("Stale cache for", path)
		data, err := ioutil.ReadFile(path)
		if err != nil {
			return nil
		}

		err = json.Unmarshal(data, &f)
		if err != nil {
			log.Println("Cannot parse", path)
			return nil
		}

		log.Println("Updating cache for", path)
		c.cache[path] = cacheEntry{
			modTime:  i.ModTime(),
			contents: f,
		}

		return f.(map[string]interface{})[key]
	}

	log.Println("Found cache for", path)
	return c.cache[path].contents.(map[string]interface{})[key]
}

// SetFileListBuilder registers file list builder function.
//
// Registered function takes context (key-value map[string]string) as the only
// argument and return a slice of strings - file paths.
func SetFileListBuilder(f func(map[string]string) []string) {
	c.buildFileList = f
}

// Lookup searches for requested configuration key in file list built using
// context.
func Lookup(context map[string]string, key string) interface{} {
	var value interface{}

	c.l.Lock()
	defer c.l.Unlock()

	for _, fpath := range c.buildFileList(context) {
		log.Println("Context:", context, "Looking up", key, "in", fpath)
		value = lookupvar(key, fpath)
		if value != nil {
			log.Println("Context:", context, "Found", key, value)
			return value
		}
	}

	log.Println("Context:", context, "Key", key, "not found")
	return nil
}

// LookupString is analogous to Lookup(), but does the cast to string.
func LookupString(context map[string]string, key string) string {
	var value interface{}

	c.l.Lock()
	defer c.l.Unlock()

	for _, fpath := range c.buildFileList(context) {
		log.Println("Context:", context, "Looking up", key, "in", fpath)
		value = lookupvar(key, fpath)
		if value != nil {
			log.Println("Context:", context, "Found", key, value)
			return value.(string)
		}
	}

	log.Println("Context:", context, "Key", key, "not found")
	return ""
}

// LookupStringSlice is analogous to Lookup(), but does the cast to []string
func LookupStringSlice(context map[string]string, key string) (retval []string) {
	var value interface{}

	c.l.Lock()
	defer c.l.Unlock()

	for _, fpath := range c.buildFileList(context) {
		log.Println("Context:", context, "Looking up", key, "in", fpath)
		value = lookupvar(key, fpath)
		if value != nil {
			log.Println("Context:", context, "Found", key, value)
			for _, s := range value.([]interface{}) {
				retval = append(retval, s.(string))
			}
			sort.Strings(retval)
			return retval
		}
	}

	log.Println("Context:", context, "Key", key, "not found")
	return []string{""}
}

// LookupStringMap is analogous to Lookup(), but does the cast to
// map[string]bool for optimised lookups.
func LookupStringMap(context map[string]string, key string) (retval map[string]bool) {
	var value interface{}
	retval = make(map[string]bool)

	c.l.Lock()
	defer c.l.Unlock()

	for _, fpath := range c.buildFileList(context) {
		log.Println("Context:", context, "Looking up", key, "in", fpath)
		value = lookupvar(key, fpath)
		if value != nil {
			log.Println("Context:", context, "Found", key, value)
			for _, s := range value.([]interface{}) {
				retval[s.(string)] = true
			}
			return retval
		}
	}

	log.Println("Context:", context, "Key", key, "not found")
	return retval
}
<commit_msg>Lock configuration until file list builder is set.<commit_after>// Copyright 2015 Robert S. Gerus. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package config

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"os"
	"sort"
	"sync"
	"time"
)

type cacheEntry struct {
	modTime  time.Time
	contents interface{}
}

type config struct {
	cache         map[string]cacheEntry
	buildFileList func(map[string]string) []string
	l             sync.Mutex
}

var c config

func init() {
	log.Println("Initializing configuration cache")
	c.cache = make(map[string]cacheEntry)
	c.l.Lock() // Lock until we have a proper file list builder
}

func lookupvar(key, path string) interface{} {
	var f interface{}
	i, err := os.Stat(path)
	_, ok := c.cache[path]
	if os.IsNotExist(err) {
		log.Println("Config does not exist", path)
		if ok {
			log.Println("Purging", path, "from cache")
			delete(c.cache, path)
		}
	}
	if err != nil {
		return nil
	}

	if c.cache[path].modTime.Before(i.ModTime()) || !ok {
		log.Println("Stale cache for", path)
		data, err := ioutil.ReadFile(path)
		if err != nil {
			return nil
		}

		err = json.Unmarshal(data, &f)
		if err != nil {
			log.Println("Cannot parse", path)
			return nil
		}

		log.Println("Updating cache for", path)
		c.cache[path] = cacheEntry{
			modTime:  i.ModTime(),
			contents: f,
		}

		return f.(map[string]interface{})[key]
	}

	log.Println("Found cache for", path)
	return c.cache[path].contents.(map[string]interface{})[key]
}

// SetFileListBuilder registers file list builder function.
//
// Registered function takes context (key-value map[string]string) as the only
// argument and return a slice of strings - file paths.
func SetFileListBuilder(f func(map[string]string) []string) {
	c.buildFileList = f
	c.l.Unlock()
}

// Lookup searches for requested configuration key in file list built using
// context.
func Lookup(context map[string]string, key string) interface{} {
	var value interface{}

	c.l.Lock()
	defer c.l.Unlock()

	for _, fpath := range c.buildFileList(context) {
		log.Println("Context:", context, "Looking up", key, "in", fpath)
		value = lookupvar(key, fpath)
		if value != nil {
			log.Println("Context:", context, "Found", key, value)
			return value
		}
	}

	log.Println("Context:", context, "Key", key, "not found")
	return nil
}

// LookupString is analogous to Lookup(), but does the cast to string.
func LookupString(context map[string]string, key string) string {
	var value interface{}

	c.l.Lock()
	defer c.l.Unlock()

	for _, fpath := range c.buildFileList(context) {
		log.Println("Context:", context, "Looking up", key, "in", fpath)
		value = lookupvar(key, fpath)
		if value != nil {
			log.Println("Context:", context, "Found", key, value)
			return value.(string)
		}
	}

	log.Println("Context:", context, "Key", key, "not found")
	return ""
}

// LookupStringSlice is analogous to Lookup(), but does the cast to []string
func LookupStringSlice(context map[string]string, key string) (retval []string) {
	var value interface{}

	c.l.Lock()
	defer c.l.Unlock()

	for _, fpath := range c.buildFileList(context) {
		log.Println("Context:", context, "Looking up", key, "in", fpath)
		value = lookupvar(key, fpath)
		if value != nil {
			log.Println("Context:", context, "Found", key, value)
			for _, s := range value.([]interface{}) {
				retval = append(retval, s.(string))
			}
			sort.Strings(retval)
			return retval
		}
	}

	log.Println("Context:", context, "Key", key, "not found")
	return []string{""}
}

// LookupStringMap is analogous to Lookup(), but does the cast to
// map[string]bool for optimised lookups.
func LookupStringMap(context map[string]string, key string) (retval map[string]bool) {
	var value interface{}
	retval = make(map[string]bool)

	c.l.Lock()
	defer c.l.Unlock()

	for _, fpath := range c.buildFileList(context) {
		log.Println("Context:", context, "Looking up", key, "in", fpath)
		value = lookupvar(key, fpath)
		if value != nil {
			log.Println("Context:", context, "Found", key, value)
			for _, s := range value.([]interface{}) {
				retval[s.(string)] = true
			}
			return retval
		}
	}

	log.Println("Context:", context, "Key", key, "not found")
	return retval
}
<|endoftext|>


<commit_before>//Package goconfig uses a struct as input and populates the
//fields of this struct with parameters fom command
//line, environment variables and configuration file.
package goconfig

import (
	"errors"
	"fmt"
	"path"

	"path/filepath"

	"github.com/crgimenes/goconfig/goenv"
	"github.com/crgimenes/goconfig/goflags"
	"github.com/crgimenes/goconfig/validate"
)

// Tag to set main name of field
var Tag = "cfg"

// TagDefault to set default value
var TagDefault = "cfgDefault"

// Path sets default config path
var Path string

// File name of default config file
var File string

// FileRequired config file required
var FileRequired bool

// HelpString temporarily saves help
var HelpString string

// PrefixFlag is a string that would be placed at the beginning of the generated Flag tags.
var PrefixFlag string

// PrefixEnv is a string that would be placed at the beginning of the generated Event tags.
var PrefixEnv string

// ErrFileFormatNotDefined Is the error that is returned when there is no defined configuration file format.
var ErrFileFormatNotDefined = errors.New("file format not defined")

//Usage is a function to show the help, can be replaced by your own version.
var Usage func()

// Fileformat struct holds the functions to Load the file containing the settings
type Fileformat struct {
	Extension   string
	Load        func(config interface{}) (err error)
	PrepareHelp func(config interface{}) (help string, err error)
}

// Formats is the list of registered formats.
var Formats []Fileformat

func findFileFormat(extension string) (format Fileformat, err error) {
	format = Fileformat{}
	for _, f := range Formats {
		if f.Extension == extension {
			format = f
			return
		}
	}
	err = ErrFileFormatNotDefined
	return
}

func init() {
	Usage = DefaultUsage
	Path = "./"
	File = ""
	FileRequired = false
}

// Parse configuration
func Parse(config interface{}) (err error) {
	ext := path.Ext(File)
	if ext != "" {
		var format Fileformat
		format, err = findFileFormat(ext)
		if err != nil {
			return
		}
		err = format.Load(config)
		if err != nil {
			return
		}
		HelpString, err = format.PrepareHelp(config)
		if err != nil {
			return
		}
	}

	goenv.Prefix = PrefixEnv
	goenv.Setup(Tag, TagDefault)
	err = goenv.Parse(config)
	if err != nil {
		return
	}

	goflags.Prefix = PrefixFlag
	goflags.Setup(Tag, TagDefault)
	goflags.Usage = Usage
	goflags.Preserve = true
	err = goflags.Parse(config)
	if err != nil {
		return
	}

	validate.Prefix = PrefixFlag
	validate.Setup(Tag, TagDefault)
	err = validate.Parse(config)

	return
}

// PrintDefaults print the default help
func PrintDefaults() {
	if File != "" {
		fmt.Printf("Config file %q:\n", filepath.Join(Path, File))
		fmt.Println(HelpString)
	}
}

// DefaultUsage is assigned for Usage function by default
func DefaultUsage() {
	fmt.Println("Usage")
	goflags.PrintDefaults()
	goenv.PrintDefaults()
	PrintDefaults()
}
<commit_msg>implement TagUsage<commit_after>//Package goconfig uses a struct as input and populates the
//fields of this struct with parameters fom command
//line, environment variables and configuration file.
package goconfig

import (
	"errors"
	"fmt"
	"path"

	"path/filepath"

	"github.com/crgimenes/goconfig/goenv"
	"github.com/crgimenes/goconfig/goflags"
	"github.com/crgimenes/goconfig/validate"
)

// Tag to set main name of field
var Tag = "cfg"

// TagDefault to set default value
var TagDefault = "cfgDefault"

// TagUsage to set usage help line
var TagUsage = "cfgUsage"

// Path sets default config path
var Path string

// File name of default config file
var File string

// FileRequired config file required
var FileRequired bool

// HelpString temporarily saves help
var HelpString string

// PrefixFlag is a string that would be placed at the beginning of the generated Flag tags.
var PrefixFlag string

// PrefixEnv is a string that would be placed at the beginning of the generated Event tags.
var PrefixEnv string

// ErrFileFormatNotDefined Is the error that is returned when there is no defined configuration file format.
var ErrFileFormatNotDefined = errors.New("file format not defined")

//Usage is a function to show the help, can be replaced by your own version.
var Usage func()

// Fileformat struct holds the functions to Load the file containing the settings
type Fileformat struct {
	Extension   string
	Load        func(config interface{}) (err error)
	PrepareHelp func(config interface{}) (help string, err error)
}

// Formats is the list of registered formats.
var Formats []Fileformat

func findFileFormat(extension string) (format Fileformat, err error) {
	format = Fileformat{}
	for _, f := range Formats {
		if f.Extension == extension {
			format = f
			return
		}
	}
	err = ErrFileFormatNotDefined
	return
}

func init() {
	Usage = DefaultUsage
	Path = "./"
	File = ""
	FileRequired = false
}

// Parse configuration
func Parse(config interface{}) (err error) {
	ext := path.Ext(File)
	if ext != "" {
		var format Fileformat
		format, err = findFileFormat(ext)
		if err != nil {
			return
		}
		err = format.Load(config)
		if err != nil {
			return
		}
		HelpString, err = format.PrepareHelp(config)
		if err != nil {
			return
		}
	}

	goenv.Prefix = PrefixEnv
	goenv.Setup(Tag, TagDefault)
	err = goenv.Parse(config)
	if err != nil {
		return
	}

	goflags.Prefix = PrefixFlag
	goflags.Setup(Tag, TagDefault, TagUsage)
	goflags.Usage = Usage
	goflags.Preserve = true
	err = goflags.Parse(config)
	if err != nil {
		return
	}

	validate.Prefix = PrefixFlag
	validate.Setup(Tag, TagDefault)
	err = validate.Parse(config)

	return
}

// PrintDefaults print the default help
func PrintDefaults() {
	if File != "" {
		fmt.Printf("Config file %q:\n", filepath.Join(Path, File))
		fmt.Println(HelpString)
	}
}

// DefaultUsage is assigned for Usage function by default
func DefaultUsage() {
	fmt.Println("Usage")
	goflags.PrintDefaults()
	goenv.PrintDefaults()
	PrintDefaults()
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/gcfg"
	"flag"
	"fmt"
	"os"
	"path"
	"path/filepath"
)

type Config struct {
	Files struct {
		Pubring  string
		Mlist2   string
		Pubkey   string
		Secring  string
		Adminkey string
		Help     string
		Pooldir  string
		Maildir  string
		IDlog    string
		ChunkDB  string
		Exedir   string
	}
	Urls struct {
		Fetch   bool
		Pubring string
		Mlist2  string
	}
	Mail struct {
		Sendmail       bool
		Pipe           string
		Outfile        bool
		UseTLS         bool
		SMTPRelay      string
		SMTPPort       int
		MXRelay        bool
		EnvelopeSender string
		Username       string
		Password       string
		OutboundName   string
		OutboundAddy   string
		CustomFrom     bool
	}
	Stats struct {
		Minlat    int
		Maxlat    int
		Minrel    float32
		Relfinal  float32
		Chain     string
		Numcopies int
		Distance  int
		StaleHrs  int
	}
	Pool struct {
		Size int
		Rate int
		Loop int
	}
	Remailer struct {
		Name        string
		Address     string
		Exit        bool
		MaxSize     int
		IDexp       int
		ChunkExpire int
		Keylife     int
		Keygrace    int
		Loglevel    string
		Daemon      bool
	}
}

func init() {
	var err error
	// Function as a client
	flag.BoolVar(&flag_client, "mail", false, "Function as a client")
	flag.BoolVar(&flag_client, "m", false, "Function as a client")
	// Send (from pool)
	flag.BoolVar(&flag_send, "send", false, "Force pool send")
	flag.BoolVar(&flag_send, "S", false, "Force pool send")
	// Perform remailer actions
	flag.BoolVar(&flag_remailer, "remailer", false,
		"Perform routine remailer actions")
	flag.BoolVar(&flag_remailer, "M", false,
		"Perform routine remailer actions")
	// Start remailer as a daemon
	flag.BoolVar(&flag_daemon, "daemon", false,
		"Start remailer as a daemon. (Requires -M")
	flag.BoolVar(&flag_daemon, "D", false,
		"Start remailer as a daemon. (Requires -M")
	// Remailer chain
	flag.StringVar(&flag_chain, "chain", "", "Remailer chain")
	flag.StringVar(&flag_chain, "l", "", "Remailer chain")
	// Recipient address
	flag.StringVar(&flag_to, "to", "", "Recipient email address")
	flag.StringVar(&flag_to, "t", "", "Recipient email address")
	// Subject header
	flag.StringVar(&flag_subject, "subject", "", "Subject header")
	flag.StringVar(&flag_subject, "s", "", "Subject header")
	// Number of copies
	flag.IntVar(&flag_copies, "copies", 0, "Number of copies")
	flag.IntVar(&flag_copies, "c", 0, "Number of copies")
	// Config file
	flag.StringVar(&flag_config, "config", "", "Config file")
	// Read STDIN
	flag.BoolVar(&flag_stdin, "read-mail", false, "Read a message from stdin")
	flag.BoolVar(&flag_stdin, "R", false, "Read a message from stdin")
	// Write to STDOUT
	flag.BoolVar(&flag_stdout, "stdout", false, "Write message to stdout")
	// Inject dummy
	flag.BoolVar(&flag_dummy, "dummy", false, "Inject a dummy message")
	flag.BoolVar(&flag_dummy, "d", false, "Inject a dummy message")
	// Disable dummy messaging
	flag.BoolVar(&flag_nodummy, "nodummy", false, "Don't send dummies")
	// Print Version
	flag.BoolVar(&flag_version, "version", false, "Print version string")
	flag.BoolVar(&flag_version, "V", false, "Print version string")
	// Memory usage
	flag.BoolVar(&flag_meminfo, "meminfo", false, "Print memory info")

	// Figure out the dir of the yamn binary
	var dir string
	dir, err = filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		panic(err)
	}

	// Set defaults and read config file
	cfg.Files.Pubkey = path.Join(dir, "key.txt")
	cfg.Files.Pubring = path.Join(dir, "pubring.mix")
	cfg.Files.Secring = path.Join(dir, "secring.mix")
	cfg.Files.Mlist2 = path.Join(dir, "mlist2.txt")
	cfg.Files.Adminkey = path.Join(dir, "adminkey.txt")
	cfg.Files.Help = path.Join(dir, "help.txt")
	cfg.Files.Pooldir = path.Join(dir, "pool")
	cfg.Files.Maildir = path.Join(dir, "Maildir")
	cfg.Files.IDlog = path.Join(dir, "idlog")
	cfg.Files.ChunkDB = path.Join(dir, "chunkdb")
	cfg.Files.Exedir = dir
	cfg.Urls.Fetch = true
	cfg.Urls.Pubring = "http://www.mixmin.net/yamn/pubring.mix"
	cfg.Urls.Mlist2 = "http://www.mixmin.net/yamn/mlist2.txt"
	cfg.Mail.Sendmail = false
	cfg.Mail.Outfile = false
	cfg.Mail.SMTPRelay = "snorky.mixmin.net"
	cfg.Mail.SMTPPort = 587
	cfg.Mail.UseTLS = true
	cfg.Mail.MXRelay = true
	cfg.Mail.EnvelopeSender = "nobody@nowhere.invalid"
	cfg.Mail.Username = ""
	cfg.Mail.Password = ""
	cfg.Mail.OutboundName = "Anonymous Remailer"
	cfg.Mail.OutboundAddy = "remailer@domain.invalid"
	cfg.Mail.CustomFrom = false
	cfg.Stats.Minrel = 98.0
	cfg.Stats.Relfinal = 99.0
	cfg.Stats.Minlat = 2
	cfg.Stats.Maxlat = 60
	cfg.Stats.Chain = "yamn4,*,*"
	cfg.Stats.Numcopies = 1
	cfg.Stats.Distance = 2
	cfg.Stats.StaleHrs = 24
	cfg.Pool.Size = 45
	cfg.Pool.Rate = 65
	cfg.Pool.Loop = 300
	cfg.Remailer.Name = "anon"
	cfg.Remailer.Address = "mix@nowhere.invalid"
	cfg.Remailer.Exit = false
	cfg.Remailer.MaxSize = 12
	cfg.Remailer.IDexp = 14
	cfg.Remailer.ChunkExpire = 60
	cfg.Remailer.Keylife = 60
	cfg.Remailer.Keygrace = 28
	cfg.Remailer.Loglevel = "info"
	cfg.Remailer.Daemon = false
}

func flags() {
	var err error
	flag.Parse()
	flag_args = flag.Args()
	if flag_version {
		fmt.Println(version)
		os.Exit(0)
	} else if flag_config != "" {
		err = gcfg.ReadFileInto(&cfg, flag_config)
		if err != nil {
			fmt.Fprintf(
				os.Stderr, "Unable to read %s\n", flag_config)
			os.Exit(1)
		}
	} else if os.Getenv("YAMNCFG") != "" {
		err = gcfg.ReadFileInto(&cfg, os.Getenv("YAMNCFG"))
		if err != nil {
			fmt.Fprintf(
				os.Stderr, "Unable to read %s\n", flag_config)
			os.Exit(1)
		}
	} else {
		fn := path.Join(cfg.Files.Exedir, "yamn.cfg")
		err = gcfg.ReadFileInto(&cfg, fn)
		if err != nil {
			if !flag_client {
				fmt.Println(err)
			}
			fmt.Println("Using internal, default config.")
		}
	}
}

var flag_client bool
var flag_send bool
var flag_remailer bool
var flag_daemon bool
var flag_chain string
var flag_to string
var flag_subject string
var flag_args []string
var flag_config string
var flag_copies int
var flag_stdin bool
var flag_stdout bool
var flag_dummy bool
var flag_nodummy bool
var flag_version bool
var flag_meminfo bool
var cfg Config
<commit_msg>Add a dir flag to define basedir<commit_after>package main

import (
	"code.google.com/p/gcfg"
	"flag"
	"fmt"
	"os"
	"path"
	"path/filepath"
)

type Config struct {
	Files struct {
		Pubring  string
		Mlist2   string
		Pubkey   string
		Secring  string
		Adminkey string
		Help     string
		Pooldir  string
		Maildir  string
		IDlog    string
		ChunkDB  string
	}
	Urls struct {
		Fetch   bool
		Pubring string
		Mlist2  string
	}
	Mail struct {
		Sendmail       bool
		Pipe           string
		Outfile        bool
		UseTLS         bool
		SMTPRelay      string
		SMTPPort       int
		MXRelay        bool
		EnvelopeSender string
		Username       string
		Password       string
		OutboundName   string
		OutboundAddy   string
		CustomFrom     bool
	}
	Stats struct {
		Minlat    int
		Maxlat    int
		Minrel    float32
		Relfinal  float32
		Chain     string
		Numcopies int
		Distance  int
		StaleHrs  int
	}
	Pool struct {
		Size int
		Rate int
		Loop int
	}
	Remailer struct {
		Name        string
		Address     string
		Exit        bool
		MaxSize     int
		IDexp       int
		ChunkExpire int
		Keylife     int
		Keygrace    int
		Loglevel    string
		Daemon      bool
	}
}

func init() {
	var err error
	// Function as a client
	flag.BoolVar(&flag_client, "mail", false, "Function as a client")
	flag.BoolVar(&flag_client, "m", false, "Function as a client")
	// Send (from pool)
	flag.BoolVar(&flag_send, "send", false, "Force pool send")
	flag.BoolVar(&flag_send, "S", false, "Force pool send")
	// Perform remailer actions
	flag.BoolVar(&flag_remailer, "remailer", false,
		"Perform routine remailer actions")
	flag.BoolVar(&flag_remailer, "M", false,
		"Perform routine remailer actions")
	// Start remailer as a daemon
	flag.BoolVar(&flag_daemon, "daemon", false,
		"Start remailer as a daemon. (Requires -M")
	flag.BoolVar(&flag_daemon, "D", false,
		"Start remailer as a daemon. (Requires -M")
	// Remailer chain
	flag.StringVar(&flag_chain, "chain", "", "Remailer chain")
	flag.StringVar(&flag_chain, "l", "", "Remailer chain")
	// Recipient address
	flag.StringVar(&flag_to, "to", "", "Recipient email address")
	flag.StringVar(&flag_to, "t", "", "Recipient email address")
	// Subject header
	flag.StringVar(&flag_subject, "subject", "", "Subject header")
	flag.StringVar(&flag_subject, "s", "", "Subject header")
	// Number of copies
	flag.IntVar(&flag_copies, "copies", 0, "Number of copies")
	flag.IntVar(&flag_copies, "c", 0, "Number of copies")
	// Config file
	flag.StringVar(&flag_config, "config", "", "Config file")
	// Read STDIN
	flag.BoolVar(&flag_stdin, "read-mail", false, "Read a message from stdin")
	flag.BoolVar(&flag_stdin, "R", false, "Read a message from stdin")
	// Write to STDOUT
	flag.BoolVar(&flag_stdout, "stdout", false, "Write message to stdout")
	// Inject dummy
	flag.BoolVar(&flag_dummy, "dummy", false, "Inject a dummy message")
	flag.BoolVar(&flag_dummy, "d", false, "Inject a dummy message")
	// Disable dummy messaging
	flag.BoolVar(&flag_nodummy, "nodummy", false, "Don't send dummies")
	// Print Version
	flag.BoolVar(&flag_version, "version", false, "Print version string")
	flag.BoolVar(&flag_version, "V", false, "Print version string")
	// Memory usage
	flag.BoolVar(&flag_meminfo, "meminfo", false, "Print memory info")

	// Figure out the dir of the yamn binary
	var dir string
	dir, err = filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		panic(err)
	}
	flag.StringVar(&flag_basedir, "dir", dir, "Base directory")

	// Set defaults and read config file
	cfg.Files.Pubkey = path.Join(dir, "key.txt")
	cfg.Files.Pubring = path.Join(dir, "pubring.mix")
	cfg.Files.Secring = path.Join(dir, "secring.mix")
	cfg.Files.Mlist2 = path.Join(dir, "mlist2.txt")
	cfg.Files.Adminkey = path.Join(dir, "adminkey.txt")
	cfg.Files.Help = path.Join(dir, "help.txt")
	cfg.Files.Pooldir = path.Join(dir, "pool")
	cfg.Files.Maildir = path.Join(dir, "Maildir")
	cfg.Files.IDlog = path.Join(dir, "idlog")
	cfg.Files.ChunkDB = path.Join(dir, "chunkdb")
	cfg.Urls.Fetch = true
	cfg.Urls.Pubring = "http://www.mixmin.net/yamn/pubring.mix"
	cfg.Urls.Mlist2 = "http://www.mixmin.net/yamn/mlist2.txt"
	cfg.Mail.Sendmail = false
	cfg.Mail.Outfile = false
	cfg.Mail.SMTPRelay = "snorky.mixmin.net"
	cfg.Mail.SMTPPort = 587
	cfg.Mail.UseTLS = true
	cfg.Mail.MXRelay = true
	cfg.Mail.EnvelopeSender = "nobody@nowhere.invalid"
	cfg.Mail.Username = ""
	cfg.Mail.Password = ""
	cfg.Mail.OutboundName = "Anonymous Remailer"
	cfg.Mail.OutboundAddy = "remailer@domain.invalid"
	cfg.Mail.CustomFrom = false
	cfg.Stats.Minrel = 98.0
	cfg.Stats.Relfinal = 99.0
	cfg.Stats.Minlat = 2
	cfg.Stats.Maxlat = 60
	cfg.Stats.Chain = "yamn4,*,*"
	cfg.Stats.Numcopies = 1
	cfg.Stats.Distance = 2
	cfg.Stats.StaleHrs = 24
	cfg.Pool.Size = 45
	cfg.Pool.Rate = 65
	cfg.Pool.Loop = 300
	cfg.Remailer.Name = "anon"
	cfg.Remailer.Address = "mix@nowhere.invalid"
	cfg.Remailer.Exit = false
	cfg.Remailer.MaxSize = 12
	cfg.Remailer.IDexp = 14
	cfg.Remailer.ChunkExpire = 60
	cfg.Remailer.Keylife = 60
	cfg.Remailer.Keygrace = 28
	cfg.Remailer.Loglevel = "info"
	cfg.Remailer.Daemon = false
}

func flags() {
	var err error
	flag.Parse()
	flag_args = flag.Args()
	if flag_version {
		fmt.Println(version)
		os.Exit(0)
	} else if flag_config != "" {
		err = gcfg.ReadFileInto(&cfg, flag_config)
		if err != nil {
			fmt.Fprintf(
				os.Stderr, "Unable to read %s\n", flag_config)
			os.Exit(1)
		}
	} else if os.Getenv("YAMNCFG") != "" {
		err = gcfg.ReadFileInto(&cfg, os.Getenv("YAMNCFG"))
		if err != nil {
			fmt.Fprintf(
				os.Stderr, "Unable to read %s\n", flag_config)
			os.Exit(1)
		}
	} else {
		fn := path.Join(flag_basedir, "yamn.cfg")
		err = gcfg.ReadFileInto(&cfg, fn)
		if err != nil {
			if !flag_client {
				fmt.Println(err)
			}
			fmt.Println("Using internal, default config.")
		}
	}
}

var flag_basedir string
var flag_client bool
var flag_send bool
var flag_remailer bool
var flag_daemon bool
var flag_chain string
var flag_to string
var flag_subject string
var flag_args []string
var flag_config string
var flag_copies int
var flag_stdin bool
var flag_stdout bool
var flag_dummy bool
var flag_nodummy bool
var flag_version bool
var flag_meminfo bool
var cfg Config
<|endoftext|>


<commit_before>package config

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os/user"
	"path/filepath"
	"reflect"
	"strings"

	"gopkg.in/yaml.v2"
)

type ConfigNamespace struct {
	Organization string // optional additional namespace for orgs.
	Namespace    string // usually project name.
}

type Config interface {
	Load(src string, dst interface{}) error
}

const UserBase string = "~/.config/"

// Load expands the provided src path using config.ExpandUser, then reads
// the file and unmarshals into dst using go-yaml.
func Load(src string, dst interface{}) (err error) {
	dstv := reflect.ValueOf(dst)

	if dstv.Kind() != reflect.Ptr {
		err = errors.New("config: not a pointer.")
	} else if dstv.IsNil() {
		err = fmt.Errorf("nil %s.", reflect.TypeOf(dstv).String())
	}
	if err != nil {
		return
	}

	path, err := ExpandUser(src)
	if err != nil {
		return
	}

	data, err := ioutil.ReadFile(path)
	if err != nil {
		return
	}

	err = yaml.Unmarshal(data, &dst)
	return
}

func ExpandUser(path string) (exPath string, err error) {
	// Acts kind of like os.path.expanduser in Python, except only supports
	// expanding "~/" or "$HOME"
	usr, err := user.Current()
	if err != nil {
		return
	}

	dir := usr.HomeDir

	if path[:2] == "~/" {
		exPath = strings.Replace(path, "~/", dir, 1)
	} else if path[:5] == "$HOME" {
		exPath = strings.Replace(path, "$HOME", dir, 1)
	} else {
		err = errors.New("No expandable path provided.")
	}

	if err != nil {
		return "", err
	}

	exPath, err = filepath.Abs(filepath.Clean(exPath))

	if err != nil {
		return "", err
	}

	return
}

func (c ConfigNamespace) Path() (path string, err error) {
	userBase, err := ExpandUser(UserBase)
	if err != nil {
		return "", err
	}

	path = filepath.Join(userBase, c.Organization, c.Namespace, "config.yaml")
	return
}

func (c ConfigNamespace) Load(dst interface{}) (err error) {
	cfgPath, err := c.Path()
	if err != nil {
		return
	}

	err = Load(cfgPath, dst)
	return
}
<commit_msg>load user or system config<commit_after>package config

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"reflect"
	"strings"

	"gopkg.in/yaml.v2"
)

type ConfigNamespace struct {
	Organization string // optional additional namespace for orgs.
	Namespace    string // usually project name.
}

type Config interface {
	Load(src string, dst interface{}) error
}

const UserBase string = "~/.config/"
const EtcDir string = "/etc/"

// Load expands the provided src path using config.ExpandUser, then reads
// the file and unmarshals into dst using go-yaml.
func Load(src string, dst interface{}) (err error) {
	dstv := reflect.ValueOf(dst)

	if dstv.Kind() != reflect.Ptr {
		err = errors.New("config: not a pointer.")
	} else if dstv.IsNil() {
		err = fmt.Errorf("nil %s.", reflect.TypeOf(dstv).String())
	}
	if err != nil {
		return
	}

	path, err := ExpandUser(src)
	if err != nil {
		return
	}

	data, err := ioutil.ReadFile(path)
	if err != nil {
		return
	}

	err = yaml.Unmarshal(data, &dst)
	return
}

func ExpandUser(path string) (exPath string, err error) {
	// Acts kind of like os.path.expanduser in Python, except only supports
	// expanding "~/" or "$HOME"
	usr, err := user.Current()
	if err != nil {
		return
	}

	dir := usr.HomeDir

	if path[:2] == "~/" {
		exPath = strings.Replace(path, "~/", dir, 1)
	} else if path[:5] == "$HOME" {
		exPath = strings.Replace(path, "$HOME", dir, 1)
	} else {
		err = errors.New("No expandable path provided.")
	}

	if err != nil {
		return "", err
	}

	exPath, err = filepath.Abs(filepath.Clean(exPath))

	if err != nil {
		return "", err
	}

	return
}

// Returns path to config, chosen by hierarchy and checked for existence:
// 1. User config (~/.config/podhub/canary/config.yaml)
// 2. System config (/etc/podhub/canary/config.yaml)
func (c ConfigNamespace) Path() (path string, err error) {
	if _, err := os.Stat(c.systemPath()); err == nil {
		path = c.systemPath()
	}

	if _, err := os.Stat(c.userPath()); err == nil {
		path = c.userPath()
	}
	return
}

func (c ConfigNamespace) systemPath() (path string, err error) {
	path = filepath.Join(EtcDir, c.Organization, c.Namespace, "config.yaml")
	return
}

func (c ConfigNamespace) userPath() (path string, err error) {
	userBase, err := ExpandUser(UserBase)
	if err != nil {
		return "", err
	}

	path = filepath.Join(userBase, c.Organization, c.Namespace, "config.yaml")
	return
}

func (c ConfigNamespace) Load(dst interface{}) (err error) {
	cfgPath, err := c.Path()
	if err != nil {
		return
	}

	err = Load(cfgPath, dst)
	return
}
<|endoftext|>


<commit_before>package tusd

import (
	"errors"
	"log"
	"net/url"
	"os"
)

// Config provides a way to configure the Handler depending on your needs.
type Config struct {
	// DataStore implementation used to store and retrieve the single uploads.
	// The usage of this field is deprecated and should be avoided in favor of
	// StoreComposer.
	DataStore DataStore
	// StoreComposer points to the store composer from which the core data store
	// and optional dependencies should be taken. May only be nil if DataStore is
	// set.
	StoreComposer *StoreComposer
	// MaxSize defines how many bytes may be stored in one single upload. If its
	// value is is 0 or smaller no limit will be enforced.
	MaxSize int64
	// BasePath defines the URL path used for handling uploads, e.g. "/files/".
	// If no trailing slash is presented it will be added. You may specify an
	// absolute URL containing a scheme, e.g. "http://tus.io"
	BasePath string
	isAbs    bool
	// NotifyCompleteUploads indicates whether sending notifications about
	// completed uploads using the CompleteUploads channel should be enabled.
	NotifyCompleteUploads bool
	// NotifyTerminatedUploads indicates whether sending notifications about
	// terminated uploads using the TerminatedUploads channel should be enabled.
	NotifyTerminatedUploads bool
	// NotifyUploadProgress indicates whether sending notifications about
	// the upload progress using the UploadProgress channel should be enabled.
	NotifyUploadProgress bool
	// NotifyCreatedUploads indicates whether sending notifications about
	// the upload having been created using the CreatedUploads channel should be enabled.
	NotifyCreatedUploads bool
	// Logger is the logger to use internally, mostly for printing requests.
	Logger *log.Logger
	// Respect the X-Forwarded-Host, X-Forwarded-Proto and Forwarded headers
	// potentially set by proxies when generating an absolute URL in the
	// response to POST requests.
	RespectForwardedHeaders bool
}

func (config *Config) validate() error {
	if config.Logger == nil {
		config.Logger = log.New(os.Stdout, "[tusd] ", 0)
	}

	base := config.BasePath
	uri, err := url.Parse(base)
	if err != nil {
		return err
	}

	// Ensure base path ends with slash to remove logic from absFileURL
	if base != "" && string(base[len(base)-1]) != "/" {
		base += "/"
	}

	// Ensure base path begins with slash if not absolute (starts with scheme)
	if !uri.IsAbs() && len(base) > 0 && string(base[0]) != "/" {
		base = "/" + base
	}
	config.BasePath = base
	config.isAbs = uri.IsAbs()

	if config.StoreComposer == nil {
		config.StoreComposer = newStoreComposerFromDataStore(config.DataStore)
		config.DataStore = nil
	} else if config.DataStore != nil {
		return errors.New("tusd: either StoreComposer or DataStore may be set in Config, but not both")
	}

	if config.StoreComposer.Core == nil {
		return errors.New("tusd: StoreComposer in Config needs to contain a non-nil core")
	}

	return nil
}
<commit_msg>core: Add timestamps to tusd log  (#276)<commit_after>package tusd

import (
	"errors"
	"log"
	"net/url"
	"os"
)

// Config provides a way to configure the Handler depending on your needs.
type Config struct {
	// DataStore implementation used to store and retrieve the single uploads.
	// The usage of this field is deprecated and should be avoided in favor of
	// StoreComposer.
	DataStore DataStore
	// StoreComposer points to the store composer from which the core data store
	// and optional dependencies should be taken. May only be nil if DataStore is
	// set.
	StoreComposer *StoreComposer
	// MaxSize defines how many bytes may be stored in one single upload. If its
	// value is is 0 or smaller no limit will be enforced.
	MaxSize int64
	// BasePath defines the URL path used for handling uploads, e.g. "/files/".
	// If no trailing slash is presented it will be added. You may specify an
	// absolute URL containing a scheme, e.g. "http://tus.io"
	BasePath string
	isAbs    bool
	// NotifyCompleteUploads indicates whether sending notifications about
	// completed uploads using the CompleteUploads channel should be enabled.
	NotifyCompleteUploads bool
	// NotifyTerminatedUploads indicates whether sending notifications about
	// terminated uploads using the TerminatedUploads channel should be enabled.
	NotifyTerminatedUploads bool
	// NotifyUploadProgress indicates whether sending notifications about
	// the upload progress using the UploadProgress channel should be enabled.
	NotifyUploadProgress bool
	// NotifyCreatedUploads indicates whether sending notifications about
	// the upload having been created using the CreatedUploads channel should be enabled.
	NotifyCreatedUploads bool
	// Logger is the logger to use internally, mostly for printing requests.
	Logger *log.Logger
	// Respect the X-Forwarded-Host, X-Forwarded-Proto and Forwarded headers
	// potentially set by proxies when generating an absolute URL in the
	// response to POST requests.
	RespectForwardedHeaders bool
}

func (config *Config) validate() error {
	if config.Logger == nil {
		config.Logger = log.New(os.Stdout, "[tusd] ", log.Ldate | log.Lmicroseconds)
	}

	base := config.BasePath
	uri, err := url.Parse(base)
	if err != nil {
		return err
	}

	// Ensure base path ends with slash to remove logic from absFileURL
	if base != "" && string(base[len(base)-1]) != "/" {
		base += "/"
	}

	// Ensure base path begins with slash if not absolute (starts with scheme)
	if !uri.IsAbs() && len(base) > 0 && string(base[0]) != "/" {
		base = "/" + base
	}
	config.BasePath = base
	config.isAbs = uri.IsAbs()

	if config.StoreComposer == nil {
		config.StoreComposer = newStoreComposerFromDataStore(config.DataStore)
		config.DataStore = nil
	} else if config.DataStore != nil {
		return errors.New("tusd: either StoreComposer or DataStore may be set in Config, but not both")
	}

	if config.StoreComposer.Core == nil {
		return errors.New("tusd: StoreComposer in Config needs to contain a non-nil core")
	}

	return nil
}
<|endoftext|>