<commit_before>package mongo

import (
	"context"
	"testing"
	"time"

	"github.com/rs/rest-layer/resource"
	"github.com/rs/rest-layer/schema"
	"github.com/stretchr/testify/assert"
	"gopkg.in/mgo.v2"
)

// Mongo doesn't support nanoseconds
var now = time.Now().Round(time.Millisecond)

// cleanup deletes a database immediately and on defer when call as:
//
//   defer cleanup(c, "database")()
func cleanup(s *mgo.Session, db string) func() {
	s.DB(db).DropDatabase()
	return func() {
		s.DB(db).DropDatabase()
	}
}

func TestInsert(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}
	s, err := mgo.Dial("")
	if !assert.NoError(t, err) {
		return
	}
	defer cleanup(s, "testinsert")()
	h := NewHandler(s, "testinsert", "test")
	items := []*resource.Item{
		{
			ID:      "1234",
			ETag:    "etag",
			Updated: now,
			Payload: map[string]interface{}{
				"id":  "1234",
				"foo": "bar",
			},
		},
	}
	err = h.Insert(context.Background(), items)
	assert.NoError(t, err)
	d := map[string]interface{}{}
	err = s.DB("testinsert").C("test").FindId("1234").One(&d)
	if !assert.NoError(t, err) {
		return
	}
	assert.Equal(t, map[string]interface{}{"foo": "bar", "_id": "1234", "_etag": "etag", "_updated": now}, d)

	// Inserting same item twice should return a conflict error
	err = h.Insert(context.Background(), items)
	assert.Equal(t, resource.ErrConflict, err)
}

func TestUpdate(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}
	s, err := mgo.Dial("")
	if !assert.NoError(t, err) {
		return
	}
	defer cleanup(s, "testupdate")()
	h := NewHandler(s, "testupdate", "test")
	oldItem := &resource.Item{
		ID:      "1234",
		ETag:    "etag1",
		Updated: now,
		Payload: map[string]interface{}{
			"id":  "1234",
			"foo": "bar",
		},
	}
	newItem := &resource.Item{
		ID:      "1234",
		ETag:    "etag2",
		Updated: now,
		Payload: map[string]interface{}{
			"id":  "1234",
			"foo": "baz",
		},
	}

	// Can't update a non existing item
	err = h.Update(context.Background(), newItem, oldItem)
	assert.Equal(t, resource.ErrNotFound, err)

	err = h.Insert(context.Background(), []*resource.Item{oldItem})
	assert.NoError(t, err)
	err = h.Update(context.Background(), newItem, oldItem)
	assert.NoError(t, err)

	// Update refused if original item's etag doesn't match stored one
	err = h.Update(context.Background(), newItem, oldItem)
	assert.Equal(t, resource.ErrConflict, err)
}

func TestDelete(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}
	s, err := mgo.Dial("")
	if !assert.NoError(t, err) {
		return
	}
	defer cleanup(s, "testupdate")()
	h := NewHandler(s, "testupdate", "test")
	item := &resource.Item{
		ID:      "1234",
		ETag:    "etag1",
		Updated: now,
		Payload: map[string]interface{}{
			"id":  "1234",
			"foo": "bar",
		},
	}

	// Can't delete a non existing item
	err = h.Delete(context.Background(), item)
	assert.Equal(t, resource.ErrNotFound, err)

	err = h.Insert(context.Background(), []*resource.Item{item})
	assert.NoError(t, err)
	err = h.Delete(context.Background(), item)
	assert.NoError(t, err)

	// Update refused if original item's etag doesn't match stored one
	err = h.Insert(context.Background(), []*resource.Item{item})
	assert.NoError(t, err)
	item.ETag = "etag2"
	err = h.Delete(context.Background(), item)
	assert.Equal(t, resource.ErrConflict, err)
}

func TestClear(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}
	s, err := mgo.Dial("")
	if !assert.NoError(t, err) {
		return
	}
	defer cleanup(s, "testupdate")()
	h := NewHandler(s, "testupdate", "test")
	items := []*resource.Item{
		{ID: "1", Payload: map[string]interface{}{"id": "1", "name": "a"}},
		{ID: "2", Payload: map[string]interface{}{"id": "2", "name": "b"}},
		{ID: "3", Payload: map[string]interface{}{"id": "3", "name": "c"}},
		{ID: "4", Payload: map[string]interface{}{"id": "4", "name": "d"}},
	}

	err = h.Insert(context.Background(), items)
	assert.NoError(t, err)

	lookup := resource.NewLookupWithQuery(schema.Query{
		schema.In{Field: "name", Values: []schema.Value{"c", "d"}},
	})
	deleted, err := h.Clear(context.Background(), lookup)
	assert.NoError(t, err)
	assert.Equal(t, 2, deleted)

	lookup = resource.NewLookupWithQuery(schema.Query{
		schema.Equal{Field: "id", Value: "2"},
	})
	deleted, err = h.Clear(context.Background(), lookup)
	assert.NoError(t, err)
	assert.Equal(t, 1, deleted)
}

func TestFind(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}
	s, err := mgo.Dial("")
	if !assert.NoError(t, err) {
		return
	}
	defer cleanup(s, "testfind")()
	h := NewHandler(s, "testfind", "test")
	h2 := NewHandler(s, "testfind", "test2")
	items := []*resource.Item{
		{ID: "1", Payload: map[string]interface{}{"id": "1", "name": "a", "age": 1}},
		{ID: "2", Payload: map[string]interface{}{"id": "2", "name": "b", "age": 2}},
		{ID: "3", Payload: map[string]interface{}{"id": "3", "name": "c", "age": 3}},
		{ID: "4", Payload: map[string]interface{}{"id": "4", "name": "d", "age": 4}},
	}
	ctx := context.Background()
	assert.NoError(t, h.Insert(ctx, items))
	assert.NoError(t, h2.Insert(ctx, items))

	lookup := resource.NewLookup()
	l, err := h.Find(ctx, lookup, 1, -1)
	if assert.NoError(t, err) {
		assert.Equal(t, 1, l.Page)
		assert.Equal(t, -1, l.Total) // Mongo doesn't support total counting
		assert.Len(t, l.Items, 4)
		// Do not check result's content as its order is unpredictable
	}

	lookup = resource.NewLookupWithQuery(schema.Query{
		schema.Equal{Field: "name", Value: "c"},
	})
	l, err = h.Find(ctx, lookup, 1, 100)
	if assert.NoError(t, err) {
		assert.Equal(t, 1, l.Page)
		assert.Equal(t, -1, l.Total) // Mongo doesn't support total counting
		if assert.Len(t, l.Items, 1) {
			item := l.Items[0]
			assert.Equal(t, "3", item.ID)
			assert.Equal(t, map[string]interface{}{"id": "3", "name": "c", "age": 3}, item.Payload)
		}
	}

	lookup = resource.NewLookupWithQuery(schema.Query{
		schema.In{Field: "name", Values: []schema.Value{"c", "d"}},
	})
	lookup.SetSorts([]string{"name"})
	l, err = h.Find(ctx, lookup, 1, 100)
	if assert.NoError(t, err) {
		assert.Equal(t, 1, l.Page)
		assert.Equal(t, -1, l.Total) // Mongo doesn't support total counting
		if assert.Len(t, l.Items, 2) {
			item := l.Items[0]
			assert.Equal(t, "3", item.ID)
			assert.Equal(t, map[string]interface{}{"id": "3", "name": "c", "age": 3}, item.Payload)
			item = l.Items[1]
			assert.Equal(t, "4", item.ID)
			assert.Equal(t, map[string]interface{}{"id": "4", "name": "d", "age": 4}, item.Payload)
		}
	}

	lookup = resource.NewLookupWithQuery(schema.Query{
		schema.Equal{Field: "id", Value: "3"},
	})
	l, err = h.Find(ctx, lookup, 1, 1)
	if assert.NoError(t, err) {
		assert.Equal(t, 1, l.Page)
		assert.Equal(t, -1, l.Total) // Mongo doesn't support total counting
		if assert.Len(t, l.Items, 1) {
			item := l.Items[0]
			assert.Equal(t, "3", item.ID)
			assert.Equal(t, map[string]interface{}{"id": "3", "name": "c", "age": 3}, item.Payload)
		}
	}

	lookup = resource.NewLookupWithQuery(schema.Query{
		schema.Equal{Field: "id", Value: "10"},
	})
	l, err = h.Find(ctx, lookup, 1, 1)
	if assert.NoError(t, err) {
		assert.Equal(t, 1, l.Page)
		assert.Equal(t, -1, l.Total) // Mongo doesn't support total counting
		assert.Len(t, l.Items, 0)
	}

	lookup = resource.NewLookupWithQuery(schema.Query{
		schema.In{Field: "id", Values: []schema.Value{"3", "4", "10"}},
	})
	l, err = h.Find(ctx, lookup, 1, -1)
	if assert.NoError(t, err) {
		assert.Equal(t, 1, l.Page)
		assert.Equal(t, -1, l.Total) // Mongo doesn't support total counting
		assert.Len(t, l.Items, 2)
	}
}
<commit_msg>Fix demo and unit test<commit_after>package mongo

import (
	"context"
	"testing"
	"time"

	"github.com/rs/rest-layer/resource"
	"github.com/rs/rest-layer/schema"
	"github.com/stretchr/testify/assert"
	"gopkg.in/mgo.v2"
)

// Mongo doesn't support nanoseconds
var now = time.Now().Round(time.Millisecond)

// cleanup deletes a database immediately and on defer when call as:
//
//   defer cleanup(c, "database")()
func cleanup(s *mgo.Session, db string) func() {
	s.DB(db).DropDatabase()
	return func() {
		s.DB(db).DropDatabase()
	}
}

func TestInsert(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}
	s, err := mgo.Dial("")
	if !assert.NoError(t, err) {
		return
	}
	defer cleanup(s, "testinsert")()
	h := NewHandler(s, "testinsert", "test")
	items := []*resource.Item{
		{
			ID:      "1234",
			ETag:    "etag",
			Updated: now,
			Payload: map[string]interface{}{
				"id":  "1234",
				"foo": "bar",
			},
		},
	}
	err = h.Insert(context.Background(), items)
	assert.NoError(t, err)
	d := map[string]interface{}{}
	err = s.DB("testinsert").C("test").FindId("1234").One(&d)
	if !assert.NoError(t, err) {
		return
	}
	assert.Equal(t, map[string]interface{}{"foo": "bar", "_id": "1234", "_etag": "etag", "_updated": now}, d)

	// Inserting same item twice should return a conflict error
	err = h.Insert(context.Background(), items)
	assert.Equal(t, resource.ErrConflict, err)
}

func TestUpdate(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}
	s, err := mgo.Dial("")
	if !assert.NoError(t, err) {
		return
	}
	defer cleanup(s, "testupdate")()
	h := NewHandler(s, "testupdate", "test")
	oldItem := &resource.Item{
		ID:      "1234",
		ETag:    "etag1",
		Updated: now,
		Payload: map[string]interface{}{
			"id":  "1234",
			"foo": "bar",
		},
	}
	newItem := &resource.Item{
		ID:      "1234",
		ETag:    "etag2",
		Updated: now,
		Payload: map[string]interface{}{
			"id":  "1234",
			"foo": "baz",
		},
	}

	// Can't update a non existing item
	err = h.Update(context.Background(), newItem, oldItem)
	assert.Equal(t, resource.ErrNotFound, err)

	err = h.Insert(context.Background(), []*resource.Item{oldItem})
	assert.NoError(t, err)
	err = h.Update(context.Background(), newItem, oldItem)
	assert.NoError(t, err)

	// Update refused if original item's etag doesn't match stored one
	err = h.Update(context.Background(), newItem, oldItem)
	assert.Equal(t, resource.ErrConflict, err)
}

func TestDelete(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}
	s, err := mgo.Dial("")
	if !assert.NoError(t, err) {
		return
	}
	defer cleanup(s, "testupdate")()
	h := NewHandler(s, "testupdate", "test")
	item := &resource.Item{
		ID:      "1234",
		ETag:    "etag1",
		Updated: now,
		Payload: map[string]interface{}{
			"id":  "1234",
			"foo": "bar",
		},
	}

	// Can't delete a non existing item
	err = h.Delete(context.Background(), item)
	assert.Equal(t, resource.ErrNotFound, err)

	err = h.Insert(context.Background(), []*resource.Item{item})
	assert.NoError(t, err)
	err = h.Delete(context.Background(), item)
	assert.NoError(t, err)

	// Update refused if original item's etag doesn't match stored one
	err = h.Insert(context.Background(), []*resource.Item{item})
	assert.NoError(t, err)
	item.ETag = "etag2"
	err = h.Delete(context.Background(), item)
	assert.Equal(t, resource.ErrConflict, err)
}

func TestClear(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}
	s, err := mgo.Dial("")
	if !assert.NoError(t, err) {
		return
	}
	defer cleanup(s, "testupdate")()
	h := NewHandler(s, "testupdate", "test")
	items := []*resource.Item{
		{ID: "1", Payload: map[string]interface{}{"id": "1", "name": "a"}},
		{ID: "2", Payload: map[string]interface{}{"id": "2", "name": "b"}},
		{ID: "3", Payload: map[string]interface{}{"id": "3", "name": "c"}},
		{ID: "4", Payload: map[string]interface{}{"id": "4", "name": "d"}},
	}

	err = h.Insert(context.Background(), items)
	assert.NoError(t, err)

	lookup := resource.NewLookupWithQuery(schema.Query{
		schema.In{Field: "name", Values: []schema.Value{"c", "d"}},
	})
	deleted, err := h.Clear(context.Background(), lookup)
	assert.NoError(t, err)
	assert.Equal(t, 2, deleted)

	lookup = resource.NewLookupWithQuery(schema.Query{
		schema.Equal{Field: "id", Value: "2"},
	})
	deleted, err = h.Clear(context.Background(), lookup)
	assert.NoError(t, err)
	assert.Equal(t, 1, deleted)
}

func TestFind(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}
	s, err := mgo.Dial("")
	if !assert.NoError(t, err) {
		return
	}
	defer cleanup(s, "testfind")()
	h := NewHandler(s, "testfind", "test")
	h2 := NewHandler(s, "testfind", "test2")
	items := []*resource.Item{
		{ID: "1", Payload: map[string]interface{}{"id": "1", "name": "a", "age": 1}},
		{ID: "2", Payload: map[string]interface{}{"id": "2", "name": "b", "age": 2}},
		{ID: "3", Payload: map[string]interface{}{"id": "3", "name": "c", "age": 3}},
		{ID: "4", Payload: map[string]interface{}{"id": "4", "name": "d", "age": 4}},
	}
	ctx := context.Background()
	assert.NoError(t, h.Insert(ctx, items))
	assert.NoError(t, h2.Insert(ctx, items))

	lookup := resource.NewLookup()
	l, err := h.Find(ctx, lookup, 0, -1)
	if assert.NoError(t, err) {
		assert.Equal(t, -1, l.Total) // Mongo doesn't support total counting
		assert.Len(t, l.Items, 4)
		// Do not check result's content as its order is unpredictable
	}

	lookup = resource.NewLookupWithQuery(schema.Query{
		schema.Equal{Field: "name", Value: "c"},
	})
	l, err = h.Find(ctx, lookup, 0, 100)
	if assert.NoError(t, err) {
		assert.Equal(t, -1, l.Total) // Mongo doesn't support total counting
		if assert.Len(t, l.Items, 1) {
			item := l.Items[0]
			assert.Equal(t, "3", item.ID)
			assert.Equal(t, map[string]interface{}{"id": "3", "name": "c", "age": 3}, item.Payload)
		}
	}

	lookup = resource.NewLookupWithQuery(schema.Query{
		schema.In{Field: "name", Values: []schema.Value{"c", "d"}},
	})
	lookup.SetSorts([]string{"name"})
	l, err = h.Find(ctx, lookup, 0, 100)
	if assert.NoError(t, err) {
		assert.Equal(t, -1, l.Total) // Mongo doesn't support total counting
		if assert.Len(t, l.Items, 2) {
			item := l.Items[0]
			assert.Equal(t, "3", item.ID)
			assert.Equal(t, map[string]interface{}{"id": "3", "name": "c", "age": 3}, item.Payload)
			item = l.Items[1]
			assert.Equal(t, "4", item.ID)
			assert.Equal(t, map[string]interface{}{"id": "4", "name": "d", "age": 4}, item.Payload)
		}
	}

	lookup = resource.NewLookupWithQuery(schema.Query{
		schema.Equal{Field: "id", Value: "3"},
	})
	l, err = h.Find(ctx, lookup, 0, 1)
	if assert.NoError(t, err) {
		assert.Equal(t, -1, l.Total) // Mongo doesn't support total counting
		if assert.Len(t, l.Items, 1) {
			item := l.Items[0]
			assert.Equal(t, "3", item.ID)
			assert.Equal(t, map[string]interface{}{"id": "3", "name": "c", "age": 3}, item.Payload)
		}
	}

	lookup = resource.NewLookupWithQuery(schema.Query{
		schema.Equal{Field: "id", Value: "10"},
	})
	l, err = h.Find(ctx, lookup, 0, 1)
	if assert.NoError(t, err) {
		assert.Equal(t, -1, l.Total) // Mongo doesn't support total counting
		assert.Len(t, l.Items, 0)
	}

	lookup = resource.NewLookupWithQuery(schema.Query{
		schema.In{Field: "id", Values: []schema.Value{"3", "4", "10"}},
	})
	l, err = h.Find(ctx, lookup, 0, -1)
	if assert.NoError(t, err) {
		assert.Equal(t, -1, l.Total) // Mongo doesn't support total counting
		assert.Len(t, l.Items, 2)
	}
}
<|endoftext|>


<commit_before>package mpdb

// A pair consisting of a meal identifier and a corresponding score
type MealScore struct {
	MealID uint64
	Score float32
}

const SCORE_BUFFER_SIZE = 32

func (db DB) GenerateSuggestions(date time.Time) (suggs []*mpdata.Meal, err error) {
	err = db.createScoreTable()
	if err != nil {
		return nil, err
	}
	defer db.dropScoreTable()

	meals, err := db.ListMeals(false)
	if err != nil {
		return nil, err
	}

	csdStmt, err := db.prepareCsdStmt()
	if err != nil {
		return nil, err
	}
	defer csdStmt.Close()

	ntdStmt, err := db.prepareNtdStmt()
	if err != nil {
		return nil, err
	}
	defer ntdStmt.Close()

	scorePairs := make([]MealScore, 0, SCORE_BUFFER_SIZE)

	for _, meal := range meals {
		dist, err := db.findClosestServingDistance(csdStmt, meal.ID, date)
		if err != nil {
			return nil, err
		}

		tds, err := db.findNearbyTagDists(ntdStmt, date)
		if err != nil {
			return nil, err
		}

		score := mpdata.CalculateScore(meal.Favourite, dist, tds)
		scorePair := MealScore{meal.ID, score}
		scorePairs = append(scorePairs, scorePair)

		if len(scorePairs) == cap(scorePairs) {
			err = db.insertScores(scorePairs)
			if err != nil {
				return nil, err
			}

			scorePairs = scorePairs[:0]
		}
	}

	if len(scorePairs) > 0 {
		err = db.insertScores(scorePairs)
		if err != nil {
			return nil, err
		}
	}

	suggs, err = db.listMealsByScore()
	if err != nil {
		return nil, err
	}

	return suggs, nil
}
<commit_msg>Add minimal documentation in mpdb/suggs.go<commit_after>package mpdb

// A pair consisting of a meal identifier and a corresponding score
type MealScore struct {
	MealID uint64
	Score float32
}

const SUGGESTION_BATCH_SIZE = 10

func (db DB) GenerateSuggestions(date time.Time) (suggs []*mpdata.Meal, err error) {
	// Create temporary table
	err = db.createScoreTable()
	if err != nil {
		return nil, err
	}

	// Defer dropping the temporary table until this function exits
	defer db.dropScoreTable()

	// Get a list of all meals
	meals, err := db.ListMeals(false)
	if err != nil {
		return nil, err
	}

	// Prepare findClosestServingDistance query for repeated use
	csdStmt, err := db.prepareCsdStmt()
	if err != nil {
		return nil, err
	}
	defer csdStmt.Close() // Defer cleanup of the prepared statement

	// Prepare findNearbyTagDists query for repeated use
	ntdStmt, err := db.prepareNtdStmt()
	if err != nil {
		return nil, err
	}
	defer ntdStmt.Close() // Defer cleanup of the prepared statement

	// Create buffer to hold current batch in
	scorePairs := make([]MealScore, 0, SUGGESTION_BATCH_SIZE)

	for _, meal := range meals {
		// Find closest serving distance
		dist, err := db.findClosestServingDistance(csdStmt, meal.ID, date)
		if err != nil {
			return nil, err
		}

		// Find nearby tag distances
		tds, err := db.findNearbyTagDists(ntdStmt, date)
		if err != nil {
			return nil, err
		}

		// Calculate score and add to current batch
		score := mpdata.CalculateScore(meal.Favourite, dist, tds)
		scorePair := MealScore{meal.ID, score}
		scorePairs = append(scorePairs, scorePair)

		// If batch is full,
		if len(scorePairs) == cap(scorePairs) {
			// Insert batch into score table
			err = db.insertScores(scorePairs)
			if err != nil {
				return nil, err
			}

			// Truncate batch buffer to be empty
			scorePairs = scorePairs[:0]
		}
	}

	// If there are scores not yet inserted,
	if len(scorePairs) > 0 {
		// Insert them
		err = db.insertScores(scorePairs)
		if err != nil {
			return nil, err
		}
	}

	// List all meals, but sorted by score
	suggs, err = db.listMealsByScore()
	if err != nil {
		return nil, err
	}

	return suggs, nil
}
<|endoftext|>


<commit_before>package kit

// InstrumentingTemplate
var InstrumentingTemplate = `
{{$schema := .Schema}}
{{$title := ToUpperFirst .Schema.Title}}

package {{ToLower $title}}

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/go-kit/kit/endpoint"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/metrics"
	"golang.org/x/net/context"
)

func DefaultMiddlewares(method string, requestCount metrics.Counter, requestLatency metrics.TimeHistogram, logger log.Logger) endpoint.Middleware {
	return endpoint.Chain(
		RequestLatencyMiddleware(method, requestLatency),
		RequestCountMiddleware(method, requestCount),
		RequestLoggingMiddleware(method, logger),
	)
}

func RequestCountMiddleware(method string, requestCount metrics.Counter) endpoint.Middleware {
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			defer func() {
				methodField := metrics.Field{Key: "method", Value: method}
				errorField := metrics.Field{Key: "error", Value: fmt.Sprintf("%v", err)}
				requestCount.With(methodField).With(errorField).Add(1)
			}()

			response, err = next(ctx, request)
			return
		}
	}
}

func RequestLatencyMiddleware(method string, requestLatency metrics.TimeHistogram) endpoint.Middleware {
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			defer func(begin time.Time) {
				methodField := metrics.Field{Key: "method", Value: method}
				errorField := metrics.Field{Key: "error", Value: fmt.Sprintf("%v", err)}
				requestLatency.With(methodField).With(errorField).Observe(time.Since(begin))
			}(time.Now())

			response, err = next(ctx, request)
			return
		}
	}
}

func RequestLoggingMiddleware(method string, logger log.Logger) endpoint.Middleware {
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			defer func(begin time.Time) {
				input, _ := json.Marshal(request)
				output, _ := json.Marshal(response)
				_ = logger.Log(
					"method", method,
					"input", string(input),
					"output", string(output),
					"err", err,
					"took", time.Since(begin),
				)
			}(time.Now())
			response, err = next(ctx, request)
			return
		}
	}
}
`

// InterfaceTemplate
var InterfaceTemplate = `
{{$schema := .Schema}}
{{$title := ToUpperFirst .Schema.Title}}

package {{ToLower $title}}

type {{$title}}Service interface {
{{range $funcKey, $funcValue := $schema.Functions}}
{{$funcKey}}(ctx context.Context, req *{{Argumentize $funcValue.Properties.incoming}}) (res *{{Argumentize $funcValue.Properties.outgoing}}, err error){{end}}
}
`

// TransportHTTPServerTemplate
var TransportHTTPServerTemplate = `
{{$schema := .Schema}}
{{$title := ToUpperFirst .Schema.Title}}

package {{ToLower $title}}

import (
	"encoding/json"
	"net/http"

	"golang.org/x/net/context"

	"github.com/go-kit/kit/endpoint"
	httptransport "github.com/go-kit/kit/transport/http"
)

// Handler functions

{{range $funcKey, $funcValue := $schema.Functions}}
func New{{$funcKey}}Handler(ctx context.Context, svc {{$title}}Service, middleware endpoint.Middleware, options ...httptransport.ServerOption) *httptransport.Server {
	return httptransport.NewServer(
		ctx,
		middleware(make{{$funcKey}}Endpoint(svc)),
		decode{{$funcKey}}Request,
		encodeResponse,
		options...,
	)
}
{{end}}

// Endpoint functions

{{range $funcKey, $funcValue := $schema.Functions}}
func make{{$funcKey}}Endpoint(svc {{$title}}Service) endpoint.Endpoint {
	return func(ctx context.Context, request interface{}) (interface{}, error) {
		req := request.(*{{Argumentize $funcValue.Properties.incoming}})
		return svc.{{$funcKey}}(ctx, req)
	}
}
{{end}}
`

// TransportHTTPClientTemplate
var TransportHTTPClientTemplate = `
{{$schema := .Schema}}
{{$title := ToUpperFirst .Schema.Title}}

package {{ToLower $title}}

import (
	jujuratelimit "github.com/juju/ratelimit"
	"github.com/sony/gobreaker"
	"golang.org/x/net/context"

	"github.com/go-kit/kit/circuitbreaker"
	"github.com/go-kit/kit/endpoint"
	"github.com/go-kit/kit/loadbalancer"
	"github.com/go-kit/kit/loadbalancer/static"
	"github.com/go-kit/kit/log"
	kitratelimit "github.com/go-kit/kit/ratelimit"
	httptransport "github.com/go-kit/kit/transport/http"
)

// {{$title}}Client holds remote endpoint functions
// Satisfies {{$title}}Service interface
type {{$title}}Client struct {
{{range $funcKey, $funcValue := $schema.Functions}}// {{$funcKey}}Endpoint provides remote call to {{ToLower $funcKey}} endpoint
	{{$funcKey}}Endpoint endpoint.Endpoint

{{end}}}

// New{{$title}}Client creates a new client for {{$title}}Service
func  New{{$title}}Client(proxies []string, ctx context.Context, maxAttempt int, maxTime time.Duration, qps int, logger log.Logger) *{{$title}}Client {
return &{{$title}}Client{
{{range $funcKey, $funcValue := $schema.Functions}}
{{$funcKey}}Endpoint : new{{$funcKey}}ClientEndpoint(proxies, ctx, maxAttempt, maxTime, qps, logger),{{end}}
}
}

{{range $funcKey, $funcValue := $schema.Functions}}
{{AsComment $funcValue.Description}}func ({{Pointerize $title}} *{{$title}}Client) {{$funcKey}}(ctx context.Context, req *{{Argumentize $funcValue.Properties.incoming}}) (*{{Argumentize $funcValue.Properties.outgoing}}, error) {
	res, err := {{Pointerize $title}}.{{$funcKey}}Endpoint(ctx, req)
	if err != nil {
		return nil, err
	}

	return res.(*{{Argumentize $funcValue.Properties.outgoing}}), nil
}
{{end}}

// Client Endpoint functions
{{range $funcKey, $funcValue := $schema.Functions}}
func new{{$funcKey}}ClientEndpoint(proxies []string, ctx context.Context, maxAttempt int, maxTime time.Duration, qps int, logger log.Logger) endpoint.Endpoint {
	factory := createFactory(ctx, qps, make{{$funcKey}}Proxy)
	return defaultClientEndpointCreator(proxies, maxAttempt, maxTime, logger, factory)
}
{{end}}

{{range $funcKey, $funcValue := $schema.Functions}}
func make{{$funcKey}}Proxy(ctx context.Context, instance string) endpoint.Endpoint {
	return httptransport.NewClient(
		"POST",
		createProxyURL(instance, "{{ToLower $funcKey}}"),
		encodeRequest,
		decode{{$funcKey}}Response,
	).Endpoint()
}
{{end}}

// Proxy functions

func createProxyURL(instance, endpoint string) *url.URL {
	if !strings.HasPrefix(instance, "http") {
		instance = "http://" + instance
	}
	u, err := url.Parse(instance)
	if err != nil {
		panic(err)
	}
	if u.Path == "" {
		u.Path = endpoint
	}

	return u
}

type proxyFunc func(context.Context, string) endpoint.Endpoint

func createFactory(ctx context.Context, qps int, pf proxyFunc) loadbalancer.Factory {
	return func(instance string) (endpoint.Endpoint, io.Closer, error) {
		var e endpoint.Endpoint
		e = pf(ctx, instance)
		e = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{}))(e)
		e = kitratelimit.NewTokenBucketLimiter(jujuratelimit.NewBucketWithRate(float64(qps), int64(qps)))(e)
		return e, nil, nil
	}
}

func defaultClientEndpointCreator(
	proxies []string,
	maxAttempts int,
	maxTime time.Duration,
	logger log.Logger,
	factory loadbalancer.Factory,
) endpoint.Endpoint {

	publisher := static.NewPublisher(
		proxies,
		factory,
		logger,
	)

	lb := loadbalancer.NewRoundRobin(publisher)
	return loadbalancer.Retry(maxAttempts, maxTime, lb)
}

`

// TransportHTTPSemioticsTemplate
var TransportHTTPSemioticsTemplate = `
{{$schema := .Schema}}
{{$title := ToUpperFirst .Schema.Title}}

package {{ToLower $title}}

import (
	"encoding/json"
	"net/http"

	"golang.org/x/net/context"

	"github.com/go-kit/kit/endpoint"
	httptransport "github.com/go-kit/kit/transport/http"
)

// Decode Request functions

{{range $funcKey, $funcValue := $schema.Functions}}
func decode{{$funcKey}}Request(r *http.Request) (interface{}, error) {
	var req {{Argumentize $funcValue.Properties.incoming}}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		return nil, err
	}
	return &req, nil
}
{{end}}

// Decode Response functions

{{range $funcKey, $funcValue := $schema.Functions}}
func decode{{$funcKey}}Response(r *http.Response) (interface{}, error) {
	var res {{Argumentize $funcValue.Properties.incoming}}
	if err := json.NewDecoder(r.Body).Decode(&res); err != nil {
		return nil, err
	}
	return &res, nil
}
{{end}}

// Encode request function

func encodeRequest(r *http.Request, request interface{}) error {
	var buf bytes.Buffer
	if err := json.NewEncoder(&buf).Encode(request); err != nil {
		return err
	}
	r.Body = ioutil.NopCloser(&buf)
	return nil
}

// Encode response function

func encodeResponse(rw http.ResponseWriter, response interface{}) error {
	return json.NewEncoder(rw).Encode(response)
}
`
<commit_msg>Gene: added better documentation for instrumenting middlewares<commit_after>package kit

// InstrumentingTemplate
var InstrumentingTemplate = `
{{$schema := .Schema}}
{{$title := ToUpperFirst .Schema.Title}}

package {{ToLower $title}}

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/go-kit/kit/endpoint"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/metrics"
	"golang.org/x/net/context"
)

// DefaultMiddlewares provides bare bones for default middlewares with
// requestLatency, requestCount and requestLogging
func DefaultMiddlewares(method string, requestCount metrics.Counter, requestLatency metrics.TimeHistogram, logger log.Logger) endpoint.Middleware {
	return endpoint.Chain(
		RequestLatencyMiddleware(method, requestLatency),
		RequestCountMiddleware(method, requestCount),
		RequestLoggingMiddleware(method, logger),
	)
}

// RequestCountMiddleware prepares a request counter endpoint.Middleware for
// package wide usage
func RequestCountMiddleware(method string, requestCount metrics.Counter) endpoint.Middleware {
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			defer func() {
				methodField := metrics.Field{Key: "method", Value: method}
				errorField := metrics.Field{Key: "error", Value: fmt.Sprintf("%v", err)}
				requestCount.With(methodField).With(errorField).Add(1)
			}()

			response, err = next(ctx, request)
			return
		}
	}
}

// RequestLatencyMiddleware prepares a request latency calculator
// endpoint.Middleware for package wide usage
func RequestLatencyMiddleware(method string, requestLatency metrics.TimeHistogram) endpoint.Middleware {
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			defer func(begin time.Time) {
				methodField := metrics.Field{Key: "method", Value: method}
				errorField := metrics.Field{Key: "error", Value: fmt.Sprintf("%v", err)}
				requestLatency.With(methodField).With(errorField).Observe(time.Since(begin))
			}(time.Now())

			response, err = next(ctx, request)
			return
		}
	}
}

// RequestLoggingMiddleware prepares a request logger endpoint.Middleware for
// package wide usage
func RequestLoggingMiddleware(method string, logger log.Logger) endpoint.Middleware {
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			defer func(begin time.Time) {
				input, _ := json.Marshal(request)
				output, _ := json.Marshal(response)
				_ = logger.Log(
					"method", method,
					"input", string(input),
					"output", string(output),
					"err", err,
					"took", time.Since(begin),
				)
			}(time.Now())
			response, err = next(ctx, request)
			return
		}
	}
}
`

// InterfaceTemplate
var InterfaceTemplate = `
{{$schema := .Schema}}
{{$title := ToUpperFirst .Schema.Title}}

package {{ToLower $title}}

type {{$title}}Service interface {
{{range $funcKey, $funcValue := $schema.Functions}}
{{$funcKey}}(ctx context.Context, req *{{Argumentize $funcValue.Properties.incoming}}) (res *{{Argumentize $funcValue.Properties.outgoing}}, err error){{end}}
}
`

// TransportHTTPServerTemplate
var TransportHTTPServerTemplate = `
{{$schema := .Schema}}
{{$title := ToUpperFirst .Schema.Title}}

package {{ToLower $title}}

import (
	"encoding/json"
	"net/http"

	"golang.org/x/net/context"

	"github.com/go-kit/kit/endpoint"
	httptransport "github.com/go-kit/kit/transport/http"
)

// Handler functions

{{range $funcKey, $funcValue := $schema.Functions}}
func New{{$funcKey}}Handler(ctx context.Context, svc {{$title}}Service, middleware endpoint.Middleware, options ...httptransport.ServerOption) *httptransport.Server {
	return httptransport.NewServer(
		ctx,
		middleware(make{{$funcKey}}Endpoint(svc)),
		decode{{$funcKey}}Request,
		encodeResponse,
		options...,
	)
}
{{end}}

// Endpoint functions

{{range $funcKey, $funcValue := $schema.Functions}}
func make{{$funcKey}}Endpoint(svc {{$title}}Service) endpoint.Endpoint {
	return func(ctx context.Context, request interface{}) (interface{}, error) {
		req := request.(*{{Argumentize $funcValue.Properties.incoming}})
		return svc.{{$funcKey}}(ctx, req)
	}
}
{{end}}
`

// TransportHTTPClientTemplate
var TransportHTTPClientTemplate = `
{{$schema := .Schema}}
{{$title := ToUpperFirst .Schema.Title}}

package {{ToLower $title}}

import (
	jujuratelimit "github.com/juju/ratelimit"
	"github.com/sony/gobreaker"
	"golang.org/x/net/context"

	"github.com/go-kit/kit/circuitbreaker"
	"github.com/go-kit/kit/endpoint"
	"github.com/go-kit/kit/loadbalancer"
	"github.com/go-kit/kit/loadbalancer/static"
	"github.com/go-kit/kit/log"
	kitratelimit "github.com/go-kit/kit/ratelimit"
	httptransport "github.com/go-kit/kit/transport/http"
)

// {{$title}}Client holds remote endpoint functions
// Satisfies {{$title}}Service interface
type {{$title}}Client struct {
{{range $funcKey, $funcValue := $schema.Functions}}// {{$funcKey}}Endpoint provides remote call to {{ToLower $funcKey}} endpoint
	{{$funcKey}}Endpoint endpoint.Endpoint

{{end}}}

// New{{$title}}Client creates a new client for {{$title}}Service
func  New{{$title}}Client(proxies []string, ctx context.Context, maxAttempt int, maxTime time.Duration, qps int, logger log.Logger) *{{$title}}Client {
return &{{$title}}Client{
{{range $funcKey, $funcValue := $schema.Functions}}
{{$funcKey}}Endpoint : new{{$funcKey}}ClientEndpoint(proxies, ctx, maxAttempt, maxTime, qps, logger),{{end}}
}
}

{{range $funcKey, $funcValue := $schema.Functions}}
{{AsComment $funcValue.Description}}func ({{Pointerize $title}} *{{$title}}Client) {{$funcKey}}(ctx context.Context, req *{{Argumentize $funcValue.Properties.incoming}}) (*{{Argumentize $funcValue.Properties.outgoing}}, error) {
	res, err := {{Pointerize $title}}.{{$funcKey}}Endpoint(ctx, req)
	if err != nil {
		return nil, err
	}

	return res.(*{{Argumentize $funcValue.Properties.outgoing}}), nil
}
{{end}}

// Client Endpoint functions
{{range $funcKey, $funcValue := $schema.Functions}}
func new{{$funcKey}}ClientEndpoint(proxies []string, ctx context.Context, maxAttempt int, maxTime time.Duration, qps int, logger log.Logger) endpoint.Endpoint {
	factory := createFactory(ctx, qps, make{{$funcKey}}Proxy)
	return defaultClientEndpointCreator(proxies, maxAttempt, maxTime, logger, factory)
}
{{end}}

{{range $funcKey, $funcValue := $schema.Functions}}
func make{{$funcKey}}Proxy(ctx context.Context, instance string) endpoint.Endpoint {
	return httptransport.NewClient(
		"POST",
		createProxyURL(instance, "{{ToLower $funcKey}}"),
		encodeRequest,
		decode{{$funcKey}}Response,
	).Endpoint()
}
{{end}}

// Proxy functions

func createProxyURL(instance, endpoint string) *url.URL {
	if !strings.HasPrefix(instance, "http") {
		instance = "http://" + instance
	}
	u, err := url.Parse(instance)
	if err != nil {
		panic(err)
	}
	if u.Path == "" {
		u.Path = endpoint
	}

	return u
}

type proxyFunc func(context.Context, string) endpoint.Endpoint

func createFactory(ctx context.Context, qps int, pf proxyFunc) loadbalancer.Factory {
	return func(instance string) (endpoint.Endpoint, io.Closer, error) {
		var e endpoint.Endpoint
		e = pf(ctx, instance)
		e = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{}))(e)
		e = kitratelimit.NewTokenBucketLimiter(jujuratelimit.NewBucketWithRate(float64(qps), int64(qps)))(e)
		return e, nil, nil
	}
}

func defaultClientEndpointCreator(
	proxies []string,
	maxAttempts int,
	maxTime time.Duration,
	logger log.Logger,
	factory loadbalancer.Factory,
) endpoint.Endpoint {

	publisher := static.NewPublisher(
		proxies,
		factory,
		logger,
	)

	lb := loadbalancer.NewRoundRobin(publisher)
	return loadbalancer.Retry(maxAttempts, maxTime, lb)
}

`

// TransportHTTPSemioticsTemplate
var TransportHTTPSemioticsTemplate = `
{{$schema := .Schema}}
{{$title := ToUpperFirst .Schema.Title}}

package {{ToLower $title}}

import (
	"encoding/json"
	"net/http"

	"golang.org/x/net/context"

	"github.com/go-kit/kit/endpoint"
	httptransport "github.com/go-kit/kit/transport/http"
)

// Decode Request functions

{{range $funcKey, $funcValue := $schema.Functions}}
func decode{{$funcKey}}Request(r *http.Request) (interface{}, error) {
	var req {{Argumentize $funcValue.Properties.incoming}}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		return nil, err
	}
	return &req, nil
}
{{end}}

// Decode Response functions

{{range $funcKey, $funcValue := $schema.Functions}}
func decode{{$funcKey}}Response(r *http.Response) (interface{}, error) {
	var res {{Argumentize $funcValue.Properties.incoming}}
	if err := json.NewDecoder(r.Body).Decode(&res); err != nil {
		return nil, err
	}
	return &res, nil
}
{{end}}

// Encode request function

func encodeRequest(r *http.Request, request interface{}) error {
	var buf bytes.Buffer
	if err := json.NewEncoder(&buf).Encode(request); err != nil {
		return err
	}
	r.Body = ioutil.NopCloser(&buf)
	return nil
}

// Encode response function

func encodeResponse(rw http.ResponseWriter, response interface{}) error {
	return json.NewEncoder(rw).Encode(response)
}
`
<|endoftext|>


<commit_before>package main

import (
  "flag"
  "fmt"
  "io"
  "log"
  "os"
  "path/filepath"
  "sort"
  "strings"
  "time"
)

var (
  output_name string
)

func init() {
  flag.StringVar(&output_name, "output", output_name, "Output filename (- for STDOUT, do not specify for default, multifasta_output_TIMESTAMP.fasta)")
  flag.Parse()
}

func defaultOutput(inputPath string) string {
  dir := filepath.Dir(inputPath)
  t := time.Now()
  outfile := fmt.Sprintf("multifasta_output_%d%02d%02d-%02d%02d.fasta",
    t.Year(),
    t.Month(),
    t.Day(),
    t.Hour(),
    t.Minute())
  return filepath.Join(dir, outfile)
}

func openOutput(outputPath string) *os.File {
  switch outputPath {
  case "-":
    return os.Stdout
  default:
    out, err := os.Create(outputPath)
    if nil != err {
      log.Fatal(err)
    }
    return out
  }
}

func main() {
  if len(flag.Args()) <= 0 {
    fmt.Fprintf(os.Stderr, "Missing one or more input file(s)\n")
    flag.Usage()
    os.Exit(1)
  }
  input_paths := flag.Args()
  sort.Strings(input_paths)
  if "" == output_name {
    output_name = defaultOutput(input_paths[0])
  }
  out := openOutput(output_name)

  defer out.Close()

  for index, infile := range input_paths {
    basefile := filepath.Base(infile)
    extension := filepath.Ext(basefile)
    underscore := strings.Index(basefile,"_")
    if underscore < 0 {
      underscore = 0
    } else {
      underscore += 1
    }
    fmt.Fprintf(out, ">%s exported from %s\r\n", basefile[underscore:len(basefile)-len(extension)], basefile)
    in, err := os.Open(infile)
    if err != nil {
      log.Fatal(err)
    }
    io.Copy(out, in)
    in.Close()
    if index+1 < len(input_paths) {
      fmt.Fprintf(out, "\r\n")
    }
  }
}
<commit_msg>Adds seconds to default file output<commit_after>package main

import (
  "flag"
  "fmt"
  "io"
  "log"
  "os"
  "path/filepath"
  "sort"
  "strings"
  "time"
)

var (
  output_name string
)

func init() {
  flag.StringVar(&output_name, "output", output_name, "Output filename (- for STDOUT, do not specify for default, multifasta_output_TIMESTAMP.fasta)")
  flag.Parse()
}

func defaultOutput(inputPath string) string {
  dir := filepath.Dir(inputPath)
  t := time.Now()
  outfile := fmt.Sprintf("multifasta_output_%d%02d%02d-%02d%02d%02d.fasta",
    t.Year(),
    t.Month(),
    t.Day(),
    t.Hour(),
    t.Minute(),
    t.Second())
  return filepath.Join(dir, outfile)
}

func openOutput(outputPath string) *os.File {
  switch outputPath {
  case "-":
    return os.Stdout
  default:
    out, err := os.Create(outputPath)
    if nil != err {
      log.Fatal(err)
    }
    return out
  }
}

func main() {
  if len(flag.Args()) <= 0 {
    fmt.Fprintf(os.Stderr, "Missing one or more input file(s)\n")
    flag.Usage()
    os.Exit(1)
  }
  input_paths := flag.Args()
  sort.Strings(input_paths)
  if "" == output_name {
    output_name = defaultOutput(input_paths[0])
  }
  out := openOutput(output_name)

  defer out.Close()

  for index, infile := range input_paths {
    basefile := filepath.Base(infile)
    extension := filepath.Ext(basefile)
    underscore := strings.Index(basefile,"_")
    if underscore < 0 {
      underscore = 0
    } else {
      underscore += 1
    }
    fmt.Fprintf(out, ">%s exported from %s\r\n", basefile[underscore:len(basefile)-len(extension)], basefile)
    in, err := os.Open(infile)
    if err != nil {
      log.Fatal(err)
    }
    io.Copy(out, in)
    in.Close()
    if index+1 < len(input_paths) {
      fmt.Fprintf(out, "\r\n")
    }
  }
}
<|endoftext|>


<commit_before>package gatekeeper

import (
	"crypto/x509"
	"encoding/pem"
	"io/ioutil"
	"os"
	"path/filepath"
)

// Loads the certificate from given path and creates a certificate pool from it.
func LoadCACert(path string) (*x509.CertPool, error) {
	certs, err := loadCertFromPEM(path)
	if err != nil {
		return nil, err
	}

	result := x509.NewCertPool()
	for _, cert := range certs {
		result.AddCert(cert)
	}

	return result, nil
}

// Loads the certificates present in the given directory and creates a
// certificate pool from it.
func LoadCAPath(path string) (*x509.CertPool, error) {
	result := x509.NewCertPool()
	fn := func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		certs, err := loadCertFromPEM(path)
		if err != nil {
			return err
		}

		for _, cert := range certs {
			result.AddCert(cert)
		}
		return nil
	}

	return result, filepath.Walk(path, fn)
}

// Creates a certificate from the given path
func loadCertFromPEM(path string) ([]*x509.Certificate, error) {
	pemCerts, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	certs := make([]*x509.Certificate, 0, 5)
	for len(pemCerts) > 0 {
		var block *pem.Block
		block, pemCerts = pem.Decode(pemCerts)
		if block == nil {
			break
		}
		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
			continue
		}

		cert, err := x509.ParseCertificate(block.Bytes)
		if err != nil {
			return nil, err
		}

		certs = append(certs, cert)
	}

	return certs, nil
}
<commit_msg>Simplify implementation of gatekeeper client cert loading code while keeping the existing behaviour<commit_after>package gatekeeper

import (
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
)

// Loads the certificate from given path and creates a certificate pool from it.
func LoadCACert(path string) (*x509.CertPool, error) {
	result := x509.NewCertPool()
	if err := appendPEMCertsFromPath(result, path); err != nil {
		return nil, err
	}

	return result, nil
}

// Loads the certificates present in the given directory or file and creates a
// certificate pool from it. Assumes that _only_ PEM formatted cert files
// are present in the given directory. The presence of other files will
// cause this to fail.
func LoadCAPath(path string) (*x509.CertPool, error) {
	result := x509.NewCertPool()

	// filepath.WalkFunc to traverse the directory structure, starting from input path
	// attempting to load certs into result in the process
	appendCerts := func(path string, info os.FileInfo, err error) error {
		// cascade errors to the end of the traversal,
		//  we fail early if there are files other than certs under path
		if err != nil {
			return err
		}

		// ignore dirs
		if info.IsDir() {
			return nil
		}

		// try and append certs from files
		if err := appendPEMCertsFromPath(result, path); err != nil {
			// fail if we can't append PEM certs or get no PEM certs from a file
			return err
		}

		return nil
	}

	// if we error while walking with appendCerts, don't return any certs
	if err := filepath.Walk(path, appendCerts); err != nil {
		return nil, err
	}

	return result, nil
}

func appendPEMCertsFromPath(certPool *x509.CertPool, path string) error {
	pemCerts, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}

	if ok := certPool.AppendCertsFromPEM(pemCerts); !ok {
		return fmt.Errorf("No PEM certs could be parsed from %s", path)
	}

	return nil
}
<|endoftext|>


<commit_before>package gateway

import (
	"fmt"
	"github.com/42wim/matterbridge/bridge"
	"github.com/42wim/matterbridge/bridge/config"
	log "github.com/Sirupsen/logrus"
	"strings"
	"time"
)

type Gateway struct {
	*config.Config
	MyConfig        *config.Gateway
	Bridges         map[string]*bridge.Bridge
	Channels        map[string]*config.ChannelInfo
	ChannelOptions  map[string]config.ChannelOptions
	Names           map[string]bool
	Name            string
	Message         chan config.Message
	DestChannelFunc func(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo
}

func New(cfg *config.Config) *Gateway {
	gw := &Gateway{}
	gw.Config = cfg
	gw.Channels = make(map[string]*config.ChannelInfo)
	gw.Message = make(chan config.Message)
	gw.Bridges = make(map[string]*bridge.Bridge)
	gw.Names = make(map[string]bool)
	gw.DestChannelFunc = gw.getDestChannel
	return gw
}

func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
	for _, br := range gw.Bridges {
		if br.Account == cfg.Account {
			gw.mapChannelsToBridge(br)
			err := br.JoinChannels()
			if err != nil {
				return fmt.Errorf("Bridge %s failed to join channel: %v", br.Account, err)
			}
			return nil
		}
	}
	log.Infof("Starting bridge: %s ", cfg.Account)
	br := bridge.New(gw.Config, cfg, gw.Message)
	gw.mapChannelsToBridge(br)
	gw.Bridges[cfg.Account] = br
	err := br.Connect()
	if err != nil {
		return fmt.Errorf("Bridge %s failed to start: %v", br.Account, err)
	}
	err = br.JoinChannels()
	if err != nil {
		return fmt.Errorf("Bridge %s failed to join channel: %v", br.Account, err)
	}
	return nil
}

func (gw *Gateway) AddConfig(cfg *config.Gateway) error {
	if gw.Names[cfg.Name] {
		return fmt.Errorf("Gateway with name %s already exists", cfg.Name)
	}
	if cfg.Name == "" {
		return fmt.Errorf("%s", "Gateway without name found")
	}
	log.Infof("Starting gateway: %s", cfg.Name)
	gw.Names[cfg.Name] = true
	gw.Name = cfg.Name
	gw.MyConfig = cfg
	gw.mapChannels()
	for _, br := range append(gw.MyConfig.In, append(gw.MyConfig.InOut, gw.MyConfig.Out...)...) {
		err := gw.AddBridge(&br)
		if err != nil {
			return err
		}
	}
	return nil
}

func (gw *Gateway) mapChannelsToBridge(br *bridge.Bridge) {
	for ID, channel := range gw.Channels {
		if br.Account == channel.Account {
			br.Channels[ID] = *channel
		}
	}
}

func (gw *Gateway) Start() error {
	go gw.handleReceive()
	return nil
}

func (gw *Gateway) handleReceive() {
	for {
		select {
		case msg := <-gw.Message:
			if msg.Event == config.EVENT_FAILURE {
				for _, br := range gw.Bridges {
					if msg.Account == br.Account {
						go gw.reconnectBridge(br)
					}
				}
			}
			if msg.Event == config.EVENT_REJOIN_CHANNELS {
				for _, br := range gw.Bridges {
					if msg.Account == br.Account {
						br.Joined = make(map[string]bool)
						br.JoinChannels()
					}
				}
				continue
			}
			if !gw.ignoreMessage(&msg) {
				msg.Timestamp = time.Now()
				for _, br := range gw.Bridges {
					gw.handleMessage(msg, br)
				}
			}
		}
	}
}

func (gw *Gateway) reconnectBridge(br *bridge.Bridge) {
	br.Disconnect()
	time.Sleep(time.Second * 5)
RECONNECT:
	log.Infof("Reconnecting %s", br.Account)
	err := br.Connect()
	if err != nil {
		log.Errorf("Reconnection failed: %s. Trying again in 60 seconds", err)
		time.Sleep(time.Second * 60)
		goto RECONNECT
	}
	br.Joined = make(map[string]bool)
	br.JoinChannels()
}

func (gw *Gateway) mapChannels() error {
	for _, br := range append(gw.MyConfig.Out, gw.MyConfig.InOut...) {
		ID := br.Channel + br.Account
		_, ok := gw.Channels[ID]
		if !ok {
			channel := &config.ChannelInfo{Name: br.Channel, Direction: "out", ID: ID, Options: br.Options, Account: br.Account,
				GID: make(map[string]bool), SameChannel: make(map[string]bool)}
			channel.GID[gw.Name] = true
			channel.SameChannel[gw.Name] = br.SameChannel
			gw.Channels[channel.ID] = channel
		}
		gw.Channels[ID].GID[gw.Name] = true
		gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel
	}

	for _, br := range append(gw.MyConfig.In, gw.MyConfig.InOut...) {
		ID := br.Channel + br.Account
		_, ok := gw.Channels[ID]
		if !ok {
			channel := &config.ChannelInfo{Name: br.Channel, Direction: "in", ID: ID, Options: br.Options, Account: br.Account,
				GID: make(map[string]bool), SameChannel: make(map[string]bool)}
			channel.GID[gw.Name] = true
			channel.SameChannel[gw.Name] = br.SameChannel
			gw.Channels[channel.ID] = channel
		}
		gw.Channels[ID].GID[gw.Name] = true
		gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel
	}
	return nil
}

func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo {
	var channels []config.ChannelInfo
	for _, channel := range gw.Channels {
		if _, ok := gw.Channels[getChannelID(*msg)]; !ok {
			continue
		}
		if channel.Direction == "out" && channel.Account == dest.Account && gw.validGatewayDest(msg, channel) {
			channels = append(channels, *channel)
		}
	}
	return channels
}

func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) {
	// only relay join/part when configged
	if msg.Event == config.EVENT_JOIN_LEAVE && !gw.Bridges[dest.Account].Config.ShowJoinPart {
		return
	}
	// broadcast to every out channel (irc QUIT)
	if msg.Channel == "" && msg.Event != config.EVENT_JOIN_LEAVE {
		log.Debug("empty channel")
		return
	}
	originchannel := msg.Channel
	origmsg := msg
	for _, channel := range gw.DestChannelFunc(&msg, *dest) {
		// do not send to ourself
		if channel.ID == getChannelID(origmsg) {
			continue
		}
		log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name)
		msg.Channel = channel.Name
		gw.modifyAvatar(&msg, dest)
		gw.modifyUsername(&msg, dest)
		// for api we need originchannel as channel
		if dest.Protocol == "api" {
			msg.Channel = originchannel
		}
		err := dest.Send(msg)
		if err != nil {
			fmt.Println(err)
		}
	}
}

func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
	if msg.Text == "" {
		log.Debugf("ignoring empty message %#v from %s", msg, msg.Account)
		return true
	}
	for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreNicks) {
		if msg.Username == entry {
			log.Debugf("ignoring %s from %s", msg.Username, msg.Account)
			return true
		}
	}
	return false
}

func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) {
	br := gw.Bridges[msg.Account]
	msg.Protocol = br.Protocol
	nick := gw.Config.General.RemoteNickFormat
	if nick == "" {
		nick = dest.Config.RemoteNickFormat
	}
	if len(msg.Username) > 0 {
		// fix utf-8 issue #193
		i := 0
		for index := range msg.Username {
			if i == 1 {
				i = index
				break
			}
			i++
		}
		nick = strings.Replace(nick, "{NOPINGNICK}", msg.Username[:i]+""+msg.Username[i:], -1)
	}
	nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
	nick = strings.Replace(nick, "{BRIDGE}", br.Name, -1)
	nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1)
	msg.Username = nick
}

func (gw *Gateway) modifyAvatar(msg *config.Message, dest *bridge.Bridge) {
	iconurl := gw.Config.General.IconURL
	if iconurl == "" {
		iconurl = dest.Config.IconURL
	}
	iconurl = strings.Replace(iconurl, "{NICK}", msg.Username, -1)
	if msg.Avatar == "" {
		msg.Avatar = iconurl
	}
}

func getChannelID(msg config.Message) string {
	return msg.Channel + msg.Account
}

func (gw *Gateway) validGatewayDest(msg *config.Message, channel *config.ChannelInfo) bool {
	GIDmap := gw.Channels[getChannelID(*msg)].GID

	// gateway is specified in message (probably from api)
	if msg.Gateway != "" {
		return channel.GID[msg.Gateway]
	}

	// check if we are running a samechannelgateway.
	// if it is and the channel name matches it's ok, otherwise we shouldn't use this channel.
	for k, _ := range GIDmap {
		if channel.SameChannel[k] == true {
			if msg.Channel == channel.Name {
				// add the gateway to our message
				msg.Gateway = k
				return true
			} else {
				return false
			}
		}
	}
	// check if we are in the correct gateway
	for k, _ := range GIDmap {
		if channel.GID[k] == true {
			// add the gateway to our message
			msg.Gateway = k
			return true
		}
	}
	return false
}
<commit_msg>Remove need for channel when using api. Closes #195<commit_after>package gateway

import (
	"fmt"
	"github.com/42wim/matterbridge/bridge"
	"github.com/42wim/matterbridge/bridge/config"
	log "github.com/Sirupsen/logrus"
	"strings"
	"time"
)

type Gateway struct {
	*config.Config
	MyConfig        *config.Gateway
	Bridges         map[string]*bridge.Bridge
	Channels        map[string]*config.ChannelInfo
	ChannelOptions  map[string]config.ChannelOptions
	Names           map[string]bool
	Name            string
	Message         chan config.Message
	DestChannelFunc func(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo
}

func New(cfg *config.Config) *Gateway {
	gw := &Gateway{}
	gw.Config = cfg
	gw.Channels = make(map[string]*config.ChannelInfo)
	gw.Message = make(chan config.Message)
	gw.Bridges = make(map[string]*bridge.Bridge)
	gw.Names = make(map[string]bool)
	gw.DestChannelFunc = gw.getDestChannel
	return gw
}

func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
	for _, br := range gw.Bridges {
		if br.Account == cfg.Account {
			gw.mapChannelsToBridge(br)
			err := br.JoinChannels()
			if err != nil {
				return fmt.Errorf("Bridge %s failed to join channel: %v", br.Account, err)
			}
			return nil
		}
	}
	log.Infof("Starting bridge: %s ", cfg.Account)
	br := bridge.New(gw.Config, cfg, gw.Message)
	gw.mapChannelsToBridge(br)
	gw.Bridges[cfg.Account] = br
	err := br.Connect()
	if err != nil {
		return fmt.Errorf("Bridge %s failed to start: %v", br.Account, err)
	}
	err = br.JoinChannels()
	if err != nil {
		return fmt.Errorf("Bridge %s failed to join channel: %v", br.Account, err)
	}
	return nil
}

func (gw *Gateway) AddConfig(cfg *config.Gateway) error {
	if gw.Names[cfg.Name] {
		return fmt.Errorf("Gateway with name %s already exists", cfg.Name)
	}
	if cfg.Name == "" {
		return fmt.Errorf("%s", "Gateway without name found")
	}
	log.Infof("Starting gateway: %s", cfg.Name)
	gw.Names[cfg.Name] = true
	gw.Name = cfg.Name
	gw.MyConfig = cfg
	gw.mapChannels()
	for _, br := range append(gw.MyConfig.In, append(gw.MyConfig.InOut, gw.MyConfig.Out...)...) {
		err := gw.AddBridge(&br)
		if err != nil {
			return err
		}
	}
	return nil
}

func (gw *Gateway) mapChannelsToBridge(br *bridge.Bridge) {
	for ID, channel := range gw.Channels {
		if br.Account == channel.Account {
			br.Channels[ID] = *channel
		}
	}
}

func (gw *Gateway) Start() error {
	go gw.handleReceive()
	return nil
}

func (gw *Gateway) handleReceive() {
	for {
		select {
		case msg := <-gw.Message:
			if msg.Event == config.EVENT_FAILURE {
				for _, br := range gw.Bridges {
					if msg.Account == br.Account {
						go gw.reconnectBridge(br)
					}
				}
			}
			if msg.Event == config.EVENT_REJOIN_CHANNELS {
				for _, br := range gw.Bridges {
					if msg.Account == br.Account {
						br.Joined = make(map[string]bool)
						br.JoinChannels()
					}
				}
				continue
			}
			if !gw.ignoreMessage(&msg) {
				msg.Timestamp = time.Now()
				for _, br := range gw.Bridges {
					gw.handleMessage(msg, br)
				}
			}
		}
	}
}

func (gw *Gateway) reconnectBridge(br *bridge.Bridge) {
	br.Disconnect()
	time.Sleep(time.Second * 5)
RECONNECT:
	log.Infof("Reconnecting %s", br.Account)
	err := br.Connect()
	if err != nil {
		log.Errorf("Reconnection failed: %s. Trying again in 60 seconds", err)
		time.Sleep(time.Second * 60)
		goto RECONNECT
	}
	br.Joined = make(map[string]bool)
	br.JoinChannels()
}

func (gw *Gateway) mapChannels() error {
	for _, br := range append(gw.MyConfig.Out, gw.MyConfig.InOut...) {
		if isApi(br.Account) {
			br.Channel = "api"
		}
		ID := br.Channel + br.Account
		_, ok := gw.Channels[ID]
		if !ok {
			channel := &config.ChannelInfo{Name: br.Channel, Direction: "out", ID: ID, Options: br.Options, Account: br.Account,
				GID: make(map[string]bool), SameChannel: make(map[string]bool)}
			channel.GID[gw.Name] = true
			channel.SameChannel[gw.Name] = br.SameChannel
			gw.Channels[channel.ID] = channel
		}
		gw.Channels[ID].GID[gw.Name] = true
		gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel
	}

	for _, br := range append(gw.MyConfig.In, gw.MyConfig.InOut...) {
		if isApi(br.Account) {
			br.Channel = "api"
		}
		ID := br.Channel + br.Account
		_, ok := gw.Channels[ID]
		if !ok {
			channel := &config.ChannelInfo{Name: br.Channel, Direction: "in", ID: ID, Options: br.Options, Account: br.Account,
				GID: make(map[string]bool), SameChannel: make(map[string]bool)}
			channel.GID[gw.Name] = true
			channel.SameChannel[gw.Name] = br.SameChannel
			gw.Channels[channel.ID] = channel
		}
		gw.Channels[ID].GID[gw.Name] = true
		gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel
	}
	return nil
}

func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo {
	var channels []config.ChannelInfo
	for _, channel := range gw.Channels {
		if _, ok := gw.Channels[getChannelID(*msg)]; !ok {
			continue
		}
		if channel.Direction == "out" && channel.Account == dest.Account && gw.validGatewayDest(msg, channel) {
			channels = append(channels, *channel)
		}
	}
	return channels
}

func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) {
	// only relay join/part when configged
	if msg.Event == config.EVENT_JOIN_LEAVE && !gw.Bridges[dest.Account].Config.ShowJoinPart {
		return
	}
	// broadcast to every out channel (irc QUIT)
	if msg.Channel == "" && msg.Event != config.EVENT_JOIN_LEAVE {
		log.Debug("empty channel")
		return
	}
	originchannel := msg.Channel
	origmsg := msg
	for _, channel := range gw.DestChannelFunc(&msg, *dest) {
		// do not send to ourself
		if channel.ID == getChannelID(origmsg) {
			continue
		}
		log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name)
		msg.Channel = channel.Name
		gw.modifyAvatar(&msg, dest)
		gw.modifyUsername(&msg, dest)
		// for api we need originchannel as channel
		if dest.Protocol == "api" {
			msg.Channel = originchannel
		}
		err := dest.Send(msg)
		if err != nil {
			fmt.Println(err)
		}
	}
}

func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
	if msg.Text == "" {
		log.Debugf("ignoring empty message %#v from %s", msg, msg.Account)
		return true
	}
	for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreNicks) {
		if msg.Username == entry {
			log.Debugf("ignoring %s from %s", msg.Username, msg.Account)
			return true
		}
	}
	return false
}

func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) {
	br := gw.Bridges[msg.Account]
	msg.Protocol = br.Protocol
	nick := gw.Config.General.RemoteNickFormat
	if nick == "" {
		nick = dest.Config.RemoteNickFormat
	}
	if len(msg.Username) > 0 {
		// fix utf-8 issue #193
		i := 0
		for index := range msg.Username {
			if i == 1 {
				i = index
				break
			}
			i++
		}
		nick = strings.Replace(nick, "{NOPINGNICK}", msg.Username[:i]+""+msg.Username[i:], -1)
	}
	nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
	nick = strings.Replace(nick, "{BRIDGE}", br.Name, -1)
	nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1)
	msg.Username = nick
}

func (gw *Gateway) modifyAvatar(msg *config.Message, dest *bridge.Bridge) {
	iconurl := gw.Config.General.IconURL
	if iconurl == "" {
		iconurl = dest.Config.IconURL
	}
	iconurl = strings.Replace(iconurl, "{NICK}", msg.Username, -1)
	if msg.Avatar == "" {
		msg.Avatar = iconurl
	}
}

func getChannelID(msg config.Message) string {
	return msg.Channel + msg.Account
}

func (gw *Gateway) validGatewayDest(msg *config.Message, channel *config.ChannelInfo) bool {
	GIDmap := gw.Channels[getChannelID(*msg)].GID

	// gateway is specified in message (probably from api)
	if msg.Gateway != "" {
		return channel.GID[msg.Gateway]
	}

	// check if we are running a samechannelgateway.
	// if it is and the channel name matches it's ok, otherwise we shouldn't use this channel.
	for k, _ := range GIDmap {
		if channel.SameChannel[k] == true {
			if msg.Channel == channel.Name {
				// add the gateway to our message
				msg.Gateway = k
				return true
			} else {
				return false
			}
		}
	}
	// check if we are in the correct gateway
	for k, _ := range GIDmap {
		if channel.GID[k] == true {
			// add the gateway to our message
			msg.Gateway = k
			return true
		}
	}
	return false
}

func isApi(account string) bool {
	if strings.HasPrefix(account, "api.") {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package gateway

import (
	"fmt"
	"github.com/42wim/matterbridge/bridge"
	"github.com/42wim/matterbridge/bridge/config"
	log "github.com/Sirupsen/logrus"
	"github.com/peterhellberg/emojilib"
	//	"github.com/davecgh/go-spew/spew"
	"regexp"
	"strings"
	"time"
)

type Gateway struct {
	*config.Config
	MyConfig        *config.Gateway
	Bridges         map[string]*bridge.Bridge
	Channels        map[string]*config.ChannelInfo
	ChannelOptions  map[string]config.ChannelOptions
	Names           map[string]bool
	Name            string
	Message         chan config.Message
	DestChannelFunc func(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo
}

func New(cfg *config.Config) *Gateway {
	gw := &Gateway{}
	gw.Config = cfg
	gw.Channels = make(map[string]*config.ChannelInfo)
	gw.Message = make(chan config.Message)
	gw.Bridges = make(map[string]*bridge.Bridge)
	gw.Names = make(map[string]bool)
	gw.DestChannelFunc = gw.getDestChannel
	return gw
}

func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
	for _, br := range gw.Bridges {
		if br.Account == cfg.Account {
			gw.mapChannelsToBridge(br)
			err := br.JoinChannels()
			if err != nil {
				return fmt.Errorf("Bridge %s failed to join channel: %v", br.Account, err)
			}
			return nil
		}
	}
	log.Infof("Starting bridge: %s ", cfg.Account)
	br := bridge.New(gw.Config, cfg, gw.Message)
	gw.mapChannelsToBridge(br)
	gw.Bridges[cfg.Account] = br
	err := br.Connect()
	if err != nil {
		return fmt.Errorf("Bridge %s failed to start: %v", br.Account, err)
	}
	err = br.JoinChannels()
	if err != nil {
		return fmt.Errorf("Bridge %s failed to join channel: %v", br.Account, err)
	}
	return nil
}

func (gw *Gateway) AddConfig(cfg *config.Gateway) error {
	if gw.Names[cfg.Name] {
		return fmt.Errorf("Gateway with name %s already exists", cfg.Name)
	}
	if cfg.Name == "" {
		return fmt.Errorf("%s", "Gateway without name found")
	}
	log.Infof("Starting gateway: %s", cfg.Name)
	gw.Names[cfg.Name] = true
	gw.Name = cfg.Name
	gw.MyConfig = cfg
	gw.mapChannels()
	for _, br := range append(gw.MyConfig.In, append(gw.MyConfig.InOut, gw.MyConfig.Out...)...) {
		err := gw.AddBridge(&br)
		if err != nil {
			return err
		}
	}
	return nil
}

func (gw *Gateway) mapChannelsToBridge(br *bridge.Bridge) {
	for ID, channel := range gw.Channels {
		if br.Account == channel.Account {
			br.Channels[ID] = *channel
		}
	}
}

func (gw *Gateway) Start() error {
	go gw.handleReceive()
	return nil
}

func (gw *Gateway) handleReceive() {
	for msg := range gw.Message {
		if msg.Event == config.EVENT_FAILURE {
			for _, br := range gw.Bridges {
				if msg.Account == br.Account {
					go gw.reconnectBridge(br)
				}
			}
		}
		if msg.Event == config.EVENT_REJOIN_CHANNELS {
			for _, br := range gw.Bridges {
				if msg.Account == br.Account {
					br.Joined = make(map[string]bool)
					br.JoinChannels()
				}
			}
			continue
		}
		if !gw.ignoreMessage(&msg) {
			msg.Timestamp = time.Now()
			gw.modifyMessage(&msg)
			for _, br := range gw.Bridges {
				gw.handleMessage(msg, br)
			}
		}
	}
}

func (gw *Gateway) reconnectBridge(br *bridge.Bridge) {
	br.Disconnect()
	time.Sleep(time.Second * 5)
RECONNECT:
	log.Infof("Reconnecting %s", br.Account)
	err := br.Connect()
	if err != nil {
		log.Errorf("Reconnection failed: %s. Trying again in 60 seconds", err)
		time.Sleep(time.Second * 60)
		goto RECONNECT
	}
	br.Joined = make(map[string]bool)
	br.JoinChannels()
}

func (gw *Gateway) mapChannels() error {
	for _, br := range append(gw.MyConfig.Out, gw.MyConfig.InOut...) {
		if isApi(br.Account) {
			br.Channel = "api"
		}
		ID := br.Channel + br.Account
		_, ok := gw.Channels[ID]
		if !ok {
			channel := &config.ChannelInfo{Name: br.Channel, Direction: "out", ID: ID, Options: br.Options, Account: br.Account,
				GID: make(map[string]bool), SameChannel: make(map[string]bool)}
			channel.GID[gw.Name] = true
			channel.SameChannel[gw.Name] = br.SameChannel
			gw.Channels[channel.ID] = channel
		}
		gw.Channels[ID].GID[gw.Name] = true
		gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel
	}

	for _, br := range append(gw.MyConfig.In, gw.MyConfig.InOut...) {
		if isApi(br.Account) {
			br.Channel = "api"
		}
		ID := br.Channel + br.Account
		_, ok := gw.Channels[ID]
		if !ok {
			channel := &config.ChannelInfo{Name: br.Channel, Direction: "in", ID: ID, Options: br.Options, Account: br.Account,
				GID: make(map[string]bool), SameChannel: make(map[string]bool)}
			channel.GID[gw.Name] = true
			channel.SameChannel[gw.Name] = br.SameChannel
			gw.Channels[channel.ID] = channel
		}
		gw.Channels[ID].GID[gw.Name] = true
		gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel
	}
	return nil
}

func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo {
	var channels []config.ChannelInfo
	for _, channel := range gw.Channels {
		if _, ok := gw.Channels[getChannelID(*msg)]; !ok {
			continue
		}
		// add gateway to message
		gw.validGatewayDest(msg, channel)

		// do samechannelgateway logic
		if channel.SameChannel[msg.Gateway] {
			if msg.Channel == channel.Name && msg.Account != dest.Account {
				channels = append(channels, *channel)
			}
			continue
		}

		if channel.Direction == "out" && channel.Account == dest.Account && gw.validGatewayDest(msg, channel) {
			channels = append(channels, *channel)
		}
	}
	return channels
}

func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) {
	// only relay join/part when configged
	if msg.Event == config.EVENT_JOIN_LEAVE && !gw.Bridges[dest.Account].Config.ShowJoinPart {
		return
	}
	// broadcast to every out channel (irc QUIT)
	if msg.Channel == "" && msg.Event != config.EVENT_JOIN_LEAVE {
		log.Debug("empty channel")
		return
	}
	originchannel := msg.Channel
	origmsg := msg
	for _, channel := range gw.DestChannelFunc(&msg, *dest) {
		// do not send to ourself
		if channel.ID == getChannelID(origmsg) {
			continue
		}
		log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name)
		msg.Channel = channel.Name
		gw.modifyAvatar(&msg, dest)
		gw.modifyUsername(&msg, dest)
		// for api we need originchannel as channel
		if dest.Protocol == "api" {
			msg.Channel = originchannel
		}
		err := dest.Send(msg)
		if err != nil {
			fmt.Println(err)
		}
	}
}

func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
	if msg.Text == "" {
		log.Debugf("ignoring empty message %#v from %s", msg, msg.Account)
		return true
	}
	for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreNicks) {
		if msg.Username == entry {
			log.Debugf("ignoring %s from %s", msg.Username, msg.Account)
			return true
		}
	}
	// TODO do not compile regexps everytime
	for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreMessages) {
		if entry != "" {
			re, err := regexp.Compile(entry)
			if err != nil {
				log.Errorf("incorrect regexp %s for %s", entry, msg.Account)
				continue
			}
			if re.MatchString(msg.Text) {
				log.Debugf("matching %s. ignoring %s from %s", entry, msg.Text, msg.Account)
				return true
			}
		}
	}
	return false
}

func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) {
	br := gw.Bridges[msg.Account]
	msg.Protocol = br.Protocol
	nick := gw.Config.General.RemoteNickFormat
	if nick == "" {
		nick = dest.Config.RemoteNickFormat
	}
	if len(msg.Username) > 0 {
		// fix utf-8 issue #193
		i := 0
		for index := range msg.Username {
			if i == 1 {
				i = index
				break
			}
			i++
		}
		nick = strings.Replace(nick, "{NOPINGNICK}", msg.Username[:i]+""+msg.Username[i:], -1)
	}
	nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
	nick = strings.Replace(nick, "{BRIDGE}", br.Name, -1)
	nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1)
	msg.Username = nick
}

func (gw *Gateway) modifyAvatar(msg *config.Message, dest *bridge.Bridge) {
	iconurl := gw.Config.General.IconURL
	if iconurl == "" {
		iconurl = dest.Config.IconURL
	}
	iconurl = strings.Replace(iconurl, "{NICK}", msg.Username, -1)
	if msg.Avatar == "" {
		msg.Avatar = iconurl
	}
}

func (gw *Gateway) modifyMessage(msg *config.Message) {
	// replace :emoji: to unicode
	msg.Text = emojilib.Replace(msg.Text)
}

func getChannelID(msg config.Message) string {
	return msg.Channel + msg.Account
}

func (gw *Gateway) validGatewayDest(msg *config.Message, channel *config.ChannelInfo) bool {
	GIDmap := gw.Channels[getChannelID(*msg)].GID

	// gateway is specified in message (probably from api)
	if msg.Gateway != "" {
		return channel.GID[msg.Gateway]
	}

	// check if we are running a samechannelgateway.
	// if it is and the channel name matches it's ok, otherwise we shouldn't use this channel.
	for k := range GIDmap {
		if channel.SameChannel[k] {
			if msg.Channel == channel.Name {
				// add the gateway to our message
				msg.Gateway = k
				return true
			} else {
				return false
			}
		}
	}
	// check if we are in the correct gateway
	for k := range GIDmap {
		if channel.GID[k] {
			// add the gateway to our message
			msg.Gateway = k
			return true
		}
	}
	return false
}

func isApi(account string) bool {
	return strings.HasPrefix(account, "api.")
}
<commit_msg>Fix message modification<commit_after>package gateway

import (
	"fmt"
	"github.com/42wim/matterbridge/bridge"
	"github.com/42wim/matterbridge/bridge/config"
	log "github.com/Sirupsen/logrus"
	"github.com/peterhellberg/emojilib"
	//	"github.com/davecgh/go-spew/spew"
	"regexp"
	"strings"
	"time"
)

type Gateway struct {
	*config.Config
	MyConfig        *config.Gateway
	Bridges         map[string]*bridge.Bridge
	Channels        map[string]*config.ChannelInfo
	ChannelOptions  map[string]config.ChannelOptions
	Names           map[string]bool
	Name            string
	Message         chan config.Message
	DestChannelFunc func(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo
}

func New(cfg *config.Config) *Gateway {
	gw := &Gateway{}
	gw.Config = cfg
	gw.Channels = make(map[string]*config.ChannelInfo)
	gw.Message = make(chan config.Message)
	gw.Bridges = make(map[string]*bridge.Bridge)
	gw.Names = make(map[string]bool)
	gw.DestChannelFunc = gw.getDestChannel
	return gw
}

func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
	for _, br := range gw.Bridges {
		if br.Account == cfg.Account {
			gw.mapChannelsToBridge(br)
			err := br.JoinChannels()
			if err != nil {
				return fmt.Errorf("Bridge %s failed to join channel: %v", br.Account, err)
			}
			return nil
		}
	}
	log.Infof("Starting bridge: %s ", cfg.Account)
	br := bridge.New(gw.Config, cfg, gw.Message)
	gw.mapChannelsToBridge(br)
	gw.Bridges[cfg.Account] = br
	err := br.Connect()
	if err != nil {
		return fmt.Errorf("Bridge %s failed to start: %v", br.Account, err)
	}
	err = br.JoinChannels()
	if err != nil {
		return fmt.Errorf("Bridge %s failed to join channel: %v", br.Account, err)
	}
	return nil
}

func (gw *Gateway) AddConfig(cfg *config.Gateway) error {
	if gw.Names[cfg.Name] {
		return fmt.Errorf("Gateway with name %s already exists", cfg.Name)
	}
	if cfg.Name == "" {
		return fmt.Errorf("%s", "Gateway without name found")
	}
	log.Infof("Starting gateway: %s", cfg.Name)
	gw.Names[cfg.Name] = true
	gw.Name = cfg.Name
	gw.MyConfig = cfg
	gw.mapChannels()
	for _, br := range append(gw.MyConfig.In, append(gw.MyConfig.InOut, gw.MyConfig.Out...)...) {
		err := gw.AddBridge(&br)
		if err != nil {
			return err
		}
	}
	return nil
}

func (gw *Gateway) mapChannelsToBridge(br *bridge.Bridge) {
	for ID, channel := range gw.Channels {
		if br.Account == channel.Account {
			br.Channels[ID] = *channel
		}
	}
}

func (gw *Gateway) Start() error {
	go gw.handleReceive()
	return nil
}

func (gw *Gateway) handleReceive() {
	for msg := range gw.Message {
		if msg.Event == config.EVENT_FAILURE {
			for _, br := range gw.Bridges {
				if msg.Account == br.Account {
					go gw.reconnectBridge(br)
				}
			}
		}
		if msg.Event == config.EVENT_REJOIN_CHANNELS {
			for _, br := range gw.Bridges {
				if msg.Account == br.Account {
					br.Joined = make(map[string]bool)
					br.JoinChannels()
				}
			}
			continue
		}
		if !gw.ignoreMessage(&msg) {
			msg.Timestamp = time.Now()
			gw.modifyMessage(&msg)
			for _, br := range gw.Bridges {
				gw.handleMessage(msg, br)
			}
		}
	}
}

func (gw *Gateway) reconnectBridge(br *bridge.Bridge) {
	br.Disconnect()
	time.Sleep(time.Second * 5)
RECONNECT:
	log.Infof("Reconnecting %s", br.Account)
	err := br.Connect()
	if err != nil {
		log.Errorf("Reconnection failed: %s. Trying again in 60 seconds", err)
		time.Sleep(time.Second * 60)
		goto RECONNECT
	}
	br.Joined = make(map[string]bool)
	br.JoinChannels()
}

func (gw *Gateway) mapChannels() error {
	for _, br := range append(gw.MyConfig.Out, gw.MyConfig.InOut...) {
		if isApi(br.Account) {
			br.Channel = "api"
		}
		ID := br.Channel + br.Account
		_, ok := gw.Channels[ID]
		if !ok {
			channel := &config.ChannelInfo{Name: br.Channel, Direction: "out", ID: ID, Options: br.Options, Account: br.Account,
				GID: make(map[string]bool), SameChannel: make(map[string]bool)}
			channel.GID[gw.Name] = true
			channel.SameChannel[gw.Name] = br.SameChannel
			gw.Channels[channel.ID] = channel
		}
		gw.Channels[ID].GID[gw.Name] = true
		gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel
	}

	for _, br := range append(gw.MyConfig.In, gw.MyConfig.InOut...) {
		if isApi(br.Account) {
			br.Channel = "api"
		}
		ID := br.Channel + br.Account
		_, ok := gw.Channels[ID]
		if !ok {
			channel := &config.ChannelInfo{Name: br.Channel, Direction: "in", ID: ID, Options: br.Options, Account: br.Account,
				GID: make(map[string]bool), SameChannel: make(map[string]bool)}
			channel.GID[gw.Name] = true
			channel.SameChannel[gw.Name] = br.SameChannel
			gw.Channels[channel.ID] = channel
		}
		gw.Channels[ID].GID[gw.Name] = true
		gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel
	}
	return nil
}

func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo {
	var channels []config.ChannelInfo
	for _, channel := range gw.Channels {
		if _, ok := gw.Channels[getChannelID(*msg)]; !ok {
			continue
		}
		// add gateway to message
		gw.validGatewayDest(msg, channel)

		// do samechannelgateway logic
		if channel.SameChannel[msg.Gateway] {
			if msg.Channel == channel.Name && msg.Account != dest.Account {
				channels = append(channels, *channel)
			}
			continue
		}

		if channel.Direction == "out" && channel.Account == dest.Account && gw.validGatewayDest(msg, channel) {
			channels = append(channels, *channel)
		}
	}
	return channels
}

func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) {
	// only relay join/part when configged
	if msg.Event == config.EVENT_JOIN_LEAVE && !gw.Bridges[dest.Account].Config.ShowJoinPart {
		return
	}
	// broadcast to every out channel (irc QUIT)
	if msg.Channel == "" && msg.Event != config.EVENT_JOIN_LEAVE {
		log.Debug("empty channel")
		return
	}
	originchannel := msg.Channel
	origmsg := msg
	for _, channel := range gw.DestChannelFunc(&msg, *dest) {
		// do not send to ourself
		if channel.ID == getChannelID(origmsg) {
			continue
		}
		log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name)
		msg.Channel = channel.Name
		msg.Avatar = gw.modifyAvatar(origmsg, dest)
		msg.Username = gw.modifyUsername(origmsg, dest)
		// for api we need originchannel as channel
		if dest.Protocol == "api" {
			msg.Channel = originchannel
		}
		err := dest.Send(msg)
		if err != nil {
			fmt.Println(err)
		}
	}
}

func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
	if msg.Text == "" {
		log.Debugf("ignoring empty message %#v from %s", msg, msg.Account)
		return true
	}
	for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreNicks) {
		if msg.Username == entry {
			log.Debugf("ignoring %s from %s", msg.Username, msg.Account)
			return true
		}
	}
	// TODO do not compile regexps everytime
	for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreMessages) {
		if entry != "" {
			re, err := regexp.Compile(entry)
			if err != nil {
				log.Errorf("incorrect regexp %s for %s", entry, msg.Account)
				continue
			}
			if re.MatchString(msg.Text) {
				log.Debugf("matching %s. ignoring %s from %s", entry, msg.Text, msg.Account)
				return true
			}
		}
	}
	return false
}

func (gw *Gateway) modifyUsername(msg config.Message, dest *bridge.Bridge) string {
	br := gw.Bridges[msg.Account]
	msg.Protocol = br.Protocol
	nick := gw.Config.General.RemoteNickFormat
	if nick == "" {
		nick = dest.Config.RemoteNickFormat
	}
	if len(msg.Username) > 0 {
		// fix utf-8 issue #193
		i := 0
		for index := range msg.Username {
			if i == 1 {
				i = index
				break
			}
			i++
		}
		nick = strings.Replace(nick, "{NOPINGNICK}", msg.Username[:i]+""+msg.Username[i:], -1)
	}
	nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
	nick = strings.Replace(nick, "{BRIDGE}", br.Name, -1)
	nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1)
	return nick
}

func (gw *Gateway) modifyAvatar(msg config.Message, dest *bridge.Bridge) string {
	iconurl := gw.Config.General.IconURL
	if iconurl == "" {
		iconurl = dest.Config.IconURL
	}
	iconurl = strings.Replace(iconurl, "{NICK}", msg.Username, -1)
	if msg.Avatar == "" {
		msg.Avatar = iconurl
	}
	return msg.Avatar
}

func (gw *Gateway) modifyMessage(msg *config.Message) {
	// replace :emoji: to unicode
	msg.Text = emojilib.Replace(msg.Text)
}

func getChannelID(msg config.Message) string {
	return msg.Channel + msg.Account
}

func (gw *Gateway) validGatewayDest(msg *config.Message, channel *config.ChannelInfo) bool {
	GIDmap := gw.Channels[getChannelID(*msg)].GID

	// gateway is specified in message (probably from api)
	if msg.Gateway != "" {
		return channel.GID[msg.Gateway]
	}

	// check if we are running a samechannelgateway.
	// if it is and the channel name matches it's ok, otherwise we shouldn't use this channel.
	for k := range GIDmap {
		if channel.SameChannel[k] {
			if msg.Channel == channel.Name {
				// add the gateway to our message
				msg.Gateway = k
				return true
			} else {
				return false
			}
		}
	}
	// check if we are in the correct gateway
	for k := range GIDmap {
		if channel.GID[k] {
			// add the gateway to our message
			msg.Gateway = k
			return true
		}
	}
	return false
}

func isApi(account string) bool {
	return strings.HasPrefix(account, "api.")
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"fmt"
	"os"

	"github.com/codegangsta/cli"
	"github.com/drone/drone-go/drone"
	"github.com/jackspirou/syscerts"
)

type handlerFunc func(*cli.Context, drone.Client) error

// handle wraps the command function handlers and
// sets up the environment.
func handle(c *cli.Context, fn handlerFunc) {
	var token = c.GlobalString("token")
	var server = c.GlobalString("server")

	// if no server url is provided we can default
	// to the hosted Drone service.
	if len(server) == 0 {
		fmt.Println("Error: you must provide the Drone server address.")
		os.Exit(1)
	}
	if len(token) == 0 {
		fmt.Println("Error: you must provide your Drone access token.")
		os.Exit(1)
	}

	// attempt to find system CA certs
	certs := syscerts.SystemRootsPool()
	tlsConfig := &tls.Config{RootCAs: certs}

	// create the drone client with TLS options
	client := drone.NewClientTokenTLS(server, token, tlsConfig)

	// handle the function
	if err := fn(c, client); err != nil {
		println(err.Error())
		os.Exit(1)
	}
}
<commit_msg>Trim slash suffix from the server URL<commit_after>package main

import (
	"crypto/tls"
	"fmt"
	"os"
	"strings"

	"github.com/codegangsta/cli"
	"github.com/drone/drone-go/drone"
	"github.com/jackspirou/syscerts"
)

type handlerFunc func(*cli.Context, drone.Client) error

// handle wraps the command function handlers and
// sets up the environment.
func handle(c *cli.Context, fn handlerFunc) {
	var token = c.GlobalString("token")
	var server = strings.TrimSuffix(c.GlobalString("server"), "/")

	// if no server url is provided we can default
	// to the hosted Drone service.
	if len(server) == 0 {
		fmt.Println("Error: you must provide the Drone server address.")
		os.Exit(1)
	}

	if len(token) == 0 {
		fmt.Println("Error: you must provide your Drone access token.")
		os.Exit(1)
	}

	// attempt to find system CA certs
	certs := syscerts.SystemRootsPool()
	tlsConfig := &tls.Config{RootCAs: certs}

	// create the drone client with TLS options
	client := drone.NewClientTokenTLS(server, token, tlsConfig)

	// handle the function
	if err := fn(c, client); err != nil {
		println(err.Error())
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package rx

import (
	"sync"
	"sync/atomic"
)

//jig:template Merge<Foo>
//jig:needs Observable<Foo> Merge

// MergeFoo combines multiple Observables into one by merging their emissions.
// An error from any of the observables will terminate the merged observables.
func MergeFoo(observables ...ObservableFoo) ObservableFoo {
	if len(observables) == 0 {
		return EmptyFoo()
	}
	return observables[0].MergeWith(observables[1:]...)
}

//jig:template Observable<Foo> MergeWith

// MergeWith combines multiple Observables into one by merging their emissions.
// An error from any of the observables will terminate the merged observables.
func (o ObservableFoo) MergeWith(other ...ObservableFoo) ObservableFoo {
	if len(other) == 0 {
		return o
	}
	observable := func(observe FooObserver, subscribeOn Scheduler, subscriber Subscriber) {
		var observers struct {
			sync.Mutex
			done bool
			len  int
		}
		observer := func(next foo, err error, done bool) {
			observers.Lock()
			defer observers.Unlock()
			if !observers.done {
				switch {
				case !done:
					observe(next, nil, false)
				case err != nil:
					observers.done = true
					var zero foo
					observe(zero, err, true)
				default:
					if observers.len--; observers.len == 0 {
						var zero foo
						observe(zero, nil, true)
					}
				}
			}
		}
		subscribeOn.Schedule(func() {
			if subscriber.Subscribed() {
				observers.len = 1 + len(other)
				o(observer, subscribeOn, subscriber)
				for _, o := range other {
					if !subscriber.Subscribed() {
						return
					}
					o(observer, subscribeOn, subscriber)
				}
			}
		})
	}
	return observable
}

//jig:template Observable<Foo> MergeMap<Bar>

// MergeMapBar transforms the items emitted by an ObservableFoo by applying a
// function to each item an returning an ObservableBar. The stream of ObservableBar
// items is then merged into a single stream of Bar items using the MergeAll operator.
func (o ObservableFoo) MergeMapBar(project func(foo) ObservableBar) ObservableBar {
	return o.MapObservableBar(project).MergeAll()
}

//jig:template Observable<Foo> MergeMapTo<Bar>

// MergeMapToBar maps every entry emitted by the ObservableFoo into a single
// ObservableBar. The stream of ObservableBar items is then merged into a
// single stream of Bar items using the MergeAll operator.
func (o ObservableFoo) MergeMapToBar(inner ObservableBar) ObservableBar {
	project := func(foo) ObservableBar { return inner }
	return o.MapObservableBar(project).MergeAll()
}

//jig:template ObservableObservable<Foo> MergeAll

// MergeAll flattens a higher order observable by merging the observables it emits.
func (o ObservableObservableFoo) MergeAll() ObservableFoo {
	observable := func(observe FooObserver, subscribeOn Scheduler, subscriber Subscriber) {
		var observers struct {
			sync.Mutex
			done bool
			len  int32
		}
		observer := func(next foo, err error, done bool) {
			observers.Lock()
			defer observers.Unlock()
			if !observers.done {
				switch {
				case !done:
					observe(next, nil, false)
				case err != nil:
					observers.done = true
					var zero foo
					observe(zero, err, true)
				default:
					if atomic.AddInt32(&observers.len, -1) == 0 {
						var zero foo
						observe(zero, nil, true)
					}
				}
			}
		}
		merger := func(next ObservableFoo, err error, done bool) {
			if !done {
				atomic.AddInt32(&observers.len, 1)
				next(observer, subscribeOn, subscriber)
			} else {
				var zero foo
				observer(zero, err, true)
			}
		}
		subscribeOn.Schedule(func() {
			if subscriber.Subscribed() {
				observers.len = 1
				o(merger, subscribeOn, subscriber)
			}
		})
	}
	return observable
}

//jig:template MergeDelayError<Foo>
//jig:needs Observable<Foo> MergeDelayError

// MergeDelayErrorFoo combines multiple Observables into one by merging their emissions.
// Any error will be deferred until all observables terminate.
func MergeDelayErrorFoo(observables ...ObservableFoo) ObservableFoo {
	if len(observables) == 0 {
		return EmptyFoo()
	}
	return observables[0].MergeDelayErrorWith(observables[1:]...)
}

//jig:template Observable<Foo> MergeDelayErrorWith

// MergeDelayError combines multiple Observables into one by merging their emissions.
// Any error will be deferred until all observables terminate.
func (o ObservableFoo) MergeDelayErrorWith(other ...ObservableFoo) ObservableFoo {
	if len(other) == 0 {
		return o
	}
	observable := func(observe FooObserver, subscribeOn Scheduler, subscriber Subscriber) {
		var observers struct {
			sync.Mutex
			len int
			err error
		}
		observer := func(next foo, err error, done bool) {
			observers.Lock()
			defer observers.Unlock()
			if !done {
				observe(next, nil, false)
			} else {
				if err != nil {
					observers.err = err
				}
				if observers.len--; observers.len == 0 {
					var zero foo
					observe(zero, observers.err, true)
				}
			}
		}
		subscribeOn.Schedule(func() {
			if subscriber.Subscribed() {
				observers.len = 1 + len(other)
				o(observer, subscribeOn, subscriber)
				for _, o := range other {
					if !subscriber.Subscribed() {
						return
					}
					o(observer, subscribeOn, subscriber)
				}
			}
		})
	}
	return observable
}
<commit_msg>Add MergeMapAs for type names that start with "To"<commit_after>package rx

import (
	"sync"
	"sync/atomic"
)

//jig:template Merge<Foo>
//jig:needs Observable<Foo> Merge

// MergeFoo combines multiple Observables into one by merging their emissions.
// An error from any of the observables will terminate the merged observables.
func MergeFoo(observables ...ObservableFoo) ObservableFoo {
	if len(observables) == 0 {
		return EmptyFoo()
	}
	return observables[0].MergeWith(observables[1:]...)
}

//jig:template Observable<Foo> MergeWith

// MergeWith combines multiple Observables into one by merging their emissions.
// An error from any of the observables will terminate the merged observables.
func (o ObservableFoo) MergeWith(other ...ObservableFoo) ObservableFoo {
	if len(other) == 0 {
		return o
	}
	observable := func(observe FooObserver, subscribeOn Scheduler, subscriber Subscriber) {
		var observers struct {
			sync.Mutex
			done bool
			len  int
		}
		observer := func(next foo, err error, done bool) {
			observers.Lock()
			defer observers.Unlock()
			if !observers.done {
				switch {
				case !done:
					observe(next, nil, false)
				case err != nil:
					observers.done = true
					var zero foo
					observe(zero, err, true)
				default:
					if observers.len--; observers.len == 0 {
						var zero foo
						observe(zero, nil, true)
					}
				}
			}
		}
		subscribeOn.Schedule(func() {
			if subscriber.Subscribed() {
				observers.len = 1 + len(other)
				o(observer, subscribeOn, subscriber)
				for _, o := range other {
					if !subscriber.Subscribed() {
						return
					}
					o(observer, subscribeOn, subscriber)
				}
			}
		})
	}
	return observable
}

//jig:template Observable<Foo> MergeMap<Bar>

// MergeMapBar transforms the items emitted by an ObservableFoo by applying a
// function to each item an returning an ObservableBar. The stream of ObservableBar
// items is then merged into a single stream of Bar items using the MergeAll operator.
func (o ObservableFoo) MergeMapBar(project func(foo) ObservableBar) ObservableBar {
	return o.MapObservableBar(project).MergeAll()
}

//jig:template Observable<Foo> MergeMapAs<Bar>

// MergeMapAsBar transforms the items emitted by an ObservableFoo by applying a
// function to each item an returning an ObservableBar. The stream of ObservableBar
// items is then merged into a single stream of Bar items using the MergeAll operator.
func (o ObservableFoo) MergeMapAsBar(project func(foo) ObservableBar) ObservableBar {
	return o.MapObservableBar(project).MergeAll()
}

//jig:template Observable<Foo> MergeMapTo<Bar>

// MergeMapToBar maps every entry emitted by the ObservableFoo into a single
// ObservableBar. The stream of ObservableBar items is then merged into a
// single stream of Bar items using the MergeAll operator.
func (o ObservableFoo) MergeMapToBar(inner ObservableBar) ObservableBar {
	project := func(foo) ObservableBar { return inner }
	return o.MapObservableBar(project).MergeAll()
}

//jig:template ObservableObservable<Foo> MergeAll

// MergeAll flattens a higher order observable by merging the observables it emits.
func (o ObservableObservableFoo) MergeAll() ObservableFoo {
	observable := func(observe FooObserver, subscribeOn Scheduler, subscriber Subscriber) {
		var observers struct {
			sync.Mutex
			done bool
			len  int32
		}
		observer := func(next foo, err error, done bool) {
			observers.Lock()
			defer observers.Unlock()
			if !observers.done {
				switch {
				case !done:
					observe(next, nil, false)
				case err != nil:
					observers.done = true
					var zero foo
					observe(zero, err, true)
				default:
					if atomic.AddInt32(&observers.len, -1) == 0 {
						var zero foo
						observe(zero, nil, true)
					}
				}
			}
		}
		merger := func(next ObservableFoo, err error, done bool) {
			if !done {
				atomic.AddInt32(&observers.len, 1)
				next(observer, subscribeOn, subscriber)
			} else {
				var zero foo
				observer(zero, err, true)
			}
		}
		subscribeOn.Schedule(func() {
			if subscriber.Subscribed() {
				observers.len = 1
				o(merger, subscribeOn, subscriber)
			}
		})
	}
	return observable
}

//jig:template MergeDelayError<Foo>
//jig:needs Observable<Foo> MergeDelayError

// MergeDelayErrorFoo combines multiple Observables into one by merging their emissions.
// Any error will be deferred until all observables terminate.
func MergeDelayErrorFoo(observables ...ObservableFoo) ObservableFoo {
	if len(observables) == 0 {
		return EmptyFoo()
	}
	return observables[0].MergeDelayErrorWith(observables[1:]...)
}

//jig:template Observable<Foo> MergeDelayErrorWith

// MergeDelayError combines multiple Observables into one by merging their emissions.
// Any error will be deferred until all observables terminate.
func (o ObservableFoo) MergeDelayErrorWith(other ...ObservableFoo) ObservableFoo {
	if len(other) == 0 {
		return o
	}
	observable := func(observe FooObserver, subscribeOn Scheduler, subscriber Subscriber) {
		var observers struct {
			sync.Mutex
			len int
			err error
		}
		observer := func(next foo, err error, done bool) {
			observers.Lock()
			defer observers.Unlock()
			if !done {
				observe(next, nil, false)
			} else {
				if err != nil {
					observers.err = err
				}
				if observers.len--; observers.len == 0 {
					var zero foo
					observe(zero, observers.err, true)
				}
			}
		}
		subscribeOn.Schedule(func() {
			if subscriber.Subscribed() {
				observers.len = 1 + len(other)
				o(observer, subscribeOn, subscriber)
				for _, o := range other {
					if !subscriber.Subscribed() {
						return
					}
					o(observer, subscribeOn, subscriber)
				}
			}
		})
	}
	return observable
}
<|endoftext|>