<commit_before>package processors_test

import (
	"fmt"
	"os"
	"strings"

	"github.com/dailyburn/ratchet"
	"github.com/dailyburn/ratchet/data"
	"github.com/dailyburn/ratchet/logger"
	"github.com/dailyburn/ratchet/processors"
)

func ExampleGetRequest() {
	logger.LogLevel = logger.LevelSilent

	getGoogle, err := processors.NewHTTPRequest("GET", "http://www.google.com", nil)
	if err != nil {
		panic(err)
	}
	// this is just a really basic checking function so we can have
	// determinable example output.
	checkHTML := processors.NewFuncTransformer(func(d data.JSON) data.JSON {
		output := "Got HTML?\n"
		if strings.Contains(strings.ToLower(string(d)), "html") {
			output += "YES\n"
		} else {
			output += "NO\n"
		}
		output += "HTML contains Search Google?\n"
		if strings.Contains(string(d), "Google Search") {
			output += "YES\n"
		} else {
			output += "NO\n"
		}
		return data.JSON(output)
	})
	stdout := processors.NewIoWriter(os.Stdout)
	pipeline := ratchet.NewPipeline(getGoogle, checkHTML, stdout)

	err = <-pipeline.Run()

	if err != nil {
		fmt.Println("An error occurred in the ratchet pipeline:", err.Error())
	}

	// Output:
	// Got HTML?
	// YES
	// HTML contains Search Google?
	// YES
}
<commit_msg>Fix typo in http_request_test<commit_after>package processors_test

import (
	"fmt"
	"os"
	"strings"

	"github.com/dailyburn/ratchet"
	"github.com/dailyburn/ratchet/data"
	"github.com/dailyburn/ratchet/logger"
	"github.com/dailyburn/ratchet/processors"
)

func ExampleGetRequest() {
	logger.LogLevel = logger.LevelSilent

	getGoogle, err := processors.NewHTTPRequest("GET", "http://www.google.com", nil)
	if err != nil {
		panic(err)
	}
	// this is just a really basic checking function so we can have
	// determinable example output.
	checkHTML := processors.NewFuncTransformer(func(d data.JSON) data.JSON {
		output := "Got HTML?\n"
		if strings.Contains(strings.ToLower(string(d)), "html") {
			output += "YES\n"
		} else {
			output += "NO\n"
		}
		output += "HTML contains Google Search?\n"
		if strings.Contains(string(d), "Google Search") {
			output += "YES\n"
		} else {
			output += "NO\n"
		}
		return data.JSON(output)
	})
	stdout := processors.NewIoWriter(os.Stdout)
	pipeline := ratchet.NewPipeline(getGoogle, checkHTML, stdout)

	err = <-pipeline.Run()

	if err != nil {
		fmt.Println("An error occurred in the ratchet pipeline:", err.Error())
	}

	// Output:
	// Got HTML?
	// YES
	// HTML contains Google Search?
	// YES
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// Package state manages the meta-data required by consensus for an avalanche
// dag.
package state

import (
	"errors"

	"github.com/ava-labs/avalanchego/cache"
	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/database/versiondb"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/snow"
	"github.com/ava-labs/avalanchego/snow/choices"
	"github.com/ava-labs/avalanchego/snow/consensus/avalanche"
	"github.com/ava-labs/avalanchego/snow/consensus/snowstorm"
	"github.com/ava-labs/avalanchego/snow/engine/avalanche/vertex"
	"github.com/ava-labs/avalanchego/utils/hashing"
	"github.com/ava-labs/avalanchego/utils/math"
)

const (
	dbCacheSize = 10000
	idCacheSize = 1000
)

var (
	errUnknownVertex = errors.New("unknown vertex")
	errWrongChainID  = errors.New("wrong ChainID in vertex")
)

// Serializer manages the state of multiple vertices
type Serializer struct {
	ctx   *snow.Context
	vm    vertex.DAGVM
	state *prefixedState
	db    *versiondb.Database
	edge  ids.Set
}

// Initialize implements the avalanche.State interface
func (s *Serializer) Initialize(ctx *snow.Context, vm vertex.DAGVM, db database.Database) {
	s.ctx = ctx
	s.vm = vm

	vdb := versiondb.New(db)
	dbCache := &cache.LRU{Size: dbCacheSize}
	rawState := &state{
		serializer: s,
		dbCache:    dbCache,
		db:         vdb,
	}
	s.state = newPrefixedState(rawState, idCacheSize)
	s.db = vdb

	s.edge.Add(s.state.Edge()...)
}

// ParseVertex implements the avalanche.State interface
func (s *Serializer) ParseVertex(b []byte) (avalanche.Vertex, error) {
	vtx, err := s.parseVertex(b)
	if err != nil {
		return nil, err
	}
	if err := vtx.Verify(); err != nil {
		return nil, err
	}
	uVtx := &uniqueVertex{
		serializer: s,
		vtxID:      vtx.ID(),
	}
	if uVtx.Status() == choices.Unknown {
		if err := uVtx.setVertex(vtx); err != nil {
			return nil, err
		}
	}

	return uVtx, s.db.Commit()
}

// BuildVertex implements the avalanche.State interface
func (s *Serializer) BuildVertex(parentSet ids.Set, txs []snowstorm.Tx) (avalanche.Vertex, error) {
	if len(txs) == 0 {
		return nil, errNoTxs
	}

	parentIDs := parentSet.List()
	ids.SortIDs(parentIDs)
	sortTxs(txs)

	height := uint64(0)
	for _, parentID := range parentIDs {
		parent, err := s.getVertex(parentID)
		if err != nil {
			return nil, err
		}
		height = math.Max64(height, parent.v.vtx.height)
	}

	vtx := &innerVertex{
		chainID:   s.ctx.ChainID,
		height:    height + 1,
		parentIDs: parentIDs,
		txs:       txs,
	}

	bytes, err := vtx.Marshal()
	if err != nil {
		return nil, err
	}
	vtx.bytes = bytes
	vtx.id = ids.NewID(hashing.ComputeHash256Array(vtx.bytes))

	uVtx := &uniqueVertex{
		serializer: s,
		vtxID:      vtx.ID(),
	}
	// It is possible this vertex already exists in the database, even though we
	// just made it.
	if uVtx.Status() == choices.Unknown {
		if err := uVtx.setVertex(vtx); err != nil {
			return nil, err
		}
	}

	return uVtx, s.db.Commit()
}

// GetVertex implements the avalanche.State interface
func (s *Serializer) GetVertex(vtxID ids.ID) (avalanche.Vertex, error) { return s.getVertex(vtxID) }

// Edge implements the avalanche.State interface
func (s *Serializer) Edge() []ids.ID { return s.edge.List() }

func (s *Serializer) parseVertex(b []byte) (*innerVertex, error) {
	vtx := &innerVertex{}
	if err := vtx.Unmarshal(b, s.vm); err != nil {
		return nil, err
	} else if !vtx.chainID.Equals(s.ctx.ChainID) {
		return nil, errWrongChainID
	}
	return vtx, nil
}

func (s *Serializer) getVertex(vtxID ids.ID) (*uniqueVertex, error) {
	vtx := &uniqueVertex{
		serializer: s,
		vtxID:      vtxID,
	}
	if vtx.Status() == choices.Unknown {
		return nil, errUnknownVertex
	}
	return vtx, nil
}
<commit_msg>enforce max txs and max parents in BuildVertex<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// Package state manages the meta-data required by consensus for an avalanche
// dag.
package state

import (
	"errors"
	"fmt"

	"github.com/ava-labs/avalanchego/cache"
	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/database/versiondb"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/snow"
	"github.com/ava-labs/avalanchego/snow/choices"
	"github.com/ava-labs/avalanchego/snow/consensus/avalanche"
	"github.com/ava-labs/avalanchego/snow/consensus/snowstorm"
	"github.com/ava-labs/avalanchego/snow/engine/avalanche/vertex"
	"github.com/ava-labs/avalanchego/utils/hashing"
	"github.com/ava-labs/avalanchego/utils/math"
)

const (
	dbCacheSize = 10000
	idCacheSize = 1000
)

var (
	errUnknownVertex = errors.New("unknown vertex")
	errWrongChainID  = errors.New("wrong ChainID in vertex")
)

// Serializer manages the state of multiple vertices
type Serializer struct {
	ctx   *snow.Context
	vm    vertex.DAGVM
	state *prefixedState
	db    *versiondb.Database
	edge  ids.Set
}

// Initialize implements the avalanche.State interface
func (s *Serializer) Initialize(ctx *snow.Context, vm vertex.DAGVM, db database.Database) {
	s.ctx = ctx
	s.vm = vm

	vdb := versiondb.New(db)
	dbCache := &cache.LRU{Size: dbCacheSize}
	rawState := &state{
		serializer: s,
		dbCache:    dbCache,
		db:         vdb,
	}
	s.state = newPrefixedState(rawState, idCacheSize)
	s.db = vdb

	s.edge.Add(s.state.Edge()...)
}

// ParseVertex implements the avalanche.State interface
func (s *Serializer) ParseVertex(b []byte) (avalanche.Vertex, error) {
	vtx, err := s.parseVertex(b)
	if err != nil {
		return nil, err
	}
	if err := vtx.Verify(); err != nil {
		return nil, err
	}
	uVtx := &uniqueVertex{
		serializer: s,
		vtxID:      vtx.ID(),
	}
	if uVtx.Status() == choices.Unknown {
		if err := uVtx.setVertex(vtx); err != nil {
			return nil, err
		}
	}

	return uVtx, s.db.Commit()
}

// BuildVertex implements the avalanche.State interface
func (s *Serializer) BuildVertex(parentSet ids.Set, txs []snowstorm.Tx) (avalanche.Vertex, error) {
	if len(txs) == 0 {
		return nil, errNoTxs
	} else if l := len(txs); l > maxTxsPerVtx {
		return nil, fmt.Errorf("number of txs (%d) exceeds max (%d)", l, maxTxsPerVtx)
	} else if l := parentSet.Len(); l > maxNumParents {
		return nil, fmt.Errorf("number of parents (%d) exceeds max (%d)", l, maxNumParents)
	}

	parentIDs := parentSet.List()
	ids.SortIDs(parentIDs)
	sortTxs(txs)

	height := uint64(0)
	for _, parentID := range parentIDs {
		parent, err := s.getVertex(parentID)
		if err != nil {
			return nil, err
		}
		height = math.Max64(height, parent.v.vtx.height)
	}

	vtx := &innerVertex{
		chainID:   s.ctx.ChainID,
		height:    height + 1,
		parentIDs: parentIDs,
		txs:       txs,
	}

	bytes, err := vtx.Marshal()
	if err != nil {
		return nil, err
	}
	vtx.bytes = bytes
	vtx.id = ids.NewID(hashing.ComputeHash256Array(vtx.bytes))

	uVtx := &uniqueVertex{
		serializer: s,
		vtxID:      vtx.ID(),
	}
	// It is possible this vertex already exists in the database, even though we
	// just made it.
	if uVtx.Status() == choices.Unknown {
		if err := uVtx.setVertex(vtx); err != nil {
			return nil, err
		}
	}

	return uVtx, s.db.Commit()
}

// GetVertex implements the avalanche.State interface
func (s *Serializer) GetVertex(vtxID ids.ID) (avalanche.Vertex, error) { return s.getVertex(vtxID) }

// Edge implements the avalanche.State interface
func (s *Serializer) Edge() []ids.ID { return s.edge.List() }

func (s *Serializer) parseVertex(b []byte) (*innerVertex, error) {
	vtx := &innerVertex{}
	if err := vtx.Unmarshal(b, s.vm); err != nil {
		return nil, err
	} else if !vtx.chainID.Equals(s.ctx.ChainID) {
		return nil, errWrongChainID
	}
	return vtx, nil
}

func (s *Serializer) getVertex(vtxID ids.ID) (*uniqueVertex, error) {
	vtx := &uniqueVertex{
		serializer: s,
		vtxID:      vtxID,
	}
	if vtx.Status() == choices.Unknown {
		return nil, errUnknownVertex
	}
	return vtx, nil
}
<|endoftext|>


<commit_before>package softlayer

import (
	"fmt"
	"log"
	"net"
	"strconv"
	"strings"
	"time"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/softlayer/softlayer-go/datatypes"
	"github.com/softlayer/softlayer-go/filter"
	"github.com/softlayer/softlayer-go/helpers/product"
	"github.com/softlayer/softlayer-go/services"
	"github.com/softlayer/softlayer-go/session"
	"github.com/softlayer/softlayer-go/sl"
)

const (
	AdditionalServicesGlobalIpAddressesPackageType = "ADDITIONAL_SERVICES_GLOBAL_IP_ADDRESSES"

	GlobalIpMask = "id,ipAddress[ipAddress],destinationIpAddress[ipAddress]"
)

func resourceSoftLayerGlobalIp() *schema.Resource {
	return &schema.Resource{
		Create:   resourceSoftLayerGlobalIpCreate,
		Read:     resourceSoftLayerGlobalIpRead,
		Update:   resourceSoftLayerGlobalIpUpdate,
		Delete:   resourceSoftLayerGlobalIpDelete,
		Exists:   resourceSoftLayerGlobalIpExists,
		Importer: &schema.ResourceImporter{},

		Schema: map[string]*schema.Schema{
			"id": &schema.Schema{
				Type:     schema.TypeInt,
				Computed: true,
			},

			"ip_address": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"routes_to": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
					address := v.(string)
					if net.ParseIP(address) == nil {
						errors = append(errors, fmt.Errorf("Invalid IP format: %s", address))
					}
					return
				},
				DiffSuppressFunc: func(k, o, n string, d *schema.ResourceData) bool {
					return net.ParseIP(o).String() == net.ParseIP(n).String()
				},
			},
		},
	}
}

func resourceSoftLayerGlobalIpCreate(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(ProviderConfig).SoftLayerSession()

	// Find price items with AdditionalServicesGlobalIpAddresses
	productOrderContainer, err := buildGlobalIpProductOrderContainer(d, sess, AdditionalServicesGlobalIpAddressesPackageType)
	if err != nil {
		// Find price items with AdditionalServices
		productOrderContainer, err = buildGlobalIpProductOrderContainer(d, sess, AdditionalServicesPackageType)
		if err != nil {
			return fmt.Errorf("Error creating global ip: %s", err)
		}
	}

	log.Println("[INFO] Creating global ip")

	receipt, err := services.GetProductOrderService(sess).
		PlaceOrder(productOrderContainer, sl.Bool(false))
	if err != nil {
		return fmt.Errorf("Error during creation of global ip: %s", err)
	}

	globalIp, err := findGlobalIpByOrderId(sess, *receipt.OrderId)
	if err != nil {
		return fmt.Errorf("Error during creation of global ip: %s", err)
	}

	d.SetId(fmt.Sprintf("%d", *globalIp.Id))
	d.Set("ip_address", *globalIp.IpAddress.IpAddress)

	return resourceSoftLayerGlobalIpUpdate(d, meta)
}

func resourceSoftLayerGlobalIpRead(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(ProviderConfig).SoftLayerSession()
	service := services.GetNetworkSubnetIpAddressGlobalService(sess)

	globalIpId, err := strconv.Atoi(d.Id())
	if err != nil {
		return fmt.Errorf("Not a valid global ip ID, must be an integer: %s", err)
	}

	globalIp, err := service.Id(globalIpId).Mask(GlobalIpMask).GetObject()
	if err != nil {
		return fmt.Errorf("Error retrieving Global Ip: %s", err)
	}

	d.Set("id", *globalIp.Id)
	d.Set("ip_address", *globalIp.IpAddress.IpAddress)
	if globalIp.DestinationIpAddress != nil {
		d.Set("routes_to", *globalIp.DestinationIpAddress.IpAddress)
	}
	return nil
}

func resourceSoftLayerGlobalIpUpdate(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(ProviderConfig).SoftLayerSession()
	service := services.GetNetworkSubnetIpAddressGlobalService(sess)

	globalIpId, err := strconv.Atoi(d.Id())
	if err != nil {
		return fmt.Errorf("Not a valid global ip ID, must be an integer: %s", err)
	}

	routes_to := d.Get("routes_to").(string)
	if strings.Contains(routes_to, ":") && len(routes_to) != 39 {
		parts := strings.Split(routes_to, ":")
		for x, s := range parts {
			if s == "" {
				zeroes := 9 - len(parts)
				parts[x] = strings.Repeat("0000:", zeroes)[:(zeroes*4)+(zeroes-1)]
			} else {
				parts[x] = fmt.Sprintf("%04s", s)
			}
		}

		routes_to = strings.Join(parts, ":")
		d.Set("routes_to", routes_to)
	}

	_, err = service.Id(globalIpId).Route(sl.String(routes_to))
	if err != nil {
		return fmt.Errorf("Error editing Global Ip: %s", err)
	}
	stateConf := &resource.StateChangeConf{
		Pending: []string{"pending"},
		Target:  []string{"complete"},
		Refresh: func() (interface{}, string, error) {
			transaction, err := service.Id(globalIpId).GetActiveTransaction()
			if err != nil {
				return datatypes.Network_Subnet_IpAddress_Global{}, "pending", err
			}
			if transaction.Id == nil {
				return datatypes.Network_Subnet_IpAddress_Global{}, "complete", nil
			}
			return datatypes.Network_Subnet_IpAddress_Global{}, "pending", nil
		},
		Timeout:    10 * time.Minute,
		Delay:      5 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	pendingResult, err := stateConf.WaitForState()

	if err != nil {
		return fmt.Errorf("Error waiting for global ip destination ip address to become active: %s", err)
	}

	if _, ok := pendingResult.(datatypes.Network_Subnet_IpAddress_Global); ok {
		return nil
	}

	return nil
}

func resourceSoftLayerGlobalIpDelete(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(ProviderConfig).SoftLayerSession()
	service := services.GetNetworkSubnetIpAddressGlobalService(sess)

	globalIpId, err := strconv.Atoi(d.Id())
	if err != nil {
		return fmt.Errorf("Not a valid global ip ID, must be an integer: %s", err)
	}

	billingItem, err := service.Id(globalIpId).GetBillingItem()
	if err != nil {
		return fmt.Errorf("Error deleting global ip: %s", err)
	}

	if billingItem.Id == nil {
		return nil
	}

	_, err = services.GetBillingItemService(sess).Id(*billingItem.Id).CancelService()

	return err
}

func resourceSoftLayerGlobalIpExists(d *schema.ResourceData, meta interface{}) (bool, error) {
	sess := meta.(ProviderConfig).SoftLayerSession()
	service := services.GetNetworkSubnetIpAddressGlobalService(sess)

	globalIpId, err := strconv.Atoi(d.Id())
	if err != nil {
		return false, fmt.Errorf("Not a valid ID, must be an integer: %s", err)
	}

	result, err := service.Id(globalIpId).GetObject()
	if err != nil {
		if apiErr, ok := err.(sl.Error); ok && apiErr.StatusCode == 404 {
			return false, nil
		}
		return false, fmt.Errorf("Error retrieving global ip: %s", err)
	}
	return result.Id != nil && *result.Id == globalIpId, nil
}

func findGlobalIpByOrderId(sess *session.Session, orderId int) (datatypes.Network_Subnet_IpAddress_Global, error) {
	stateConf := &resource.StateChangeConf{
		Pending: []string{"pending"},
		Target:  []string{"complete"},
		Refresh: func() (interface{}, string, error) {
			globalIps, err := services.GetAccountService(sess).
				Filter(filter.Path("globalIpRecords.billingItem.orderItem.order.id").
					Eq(strconv.Itoa(orderId)).Build()).
				Mask("id,ipAddress[ipAddress]").
				GetGlobalIpRecords()
			if err != nil {
				return datatypes.Network_Subnet_IpAddress_Global{}, "", err
			}

			if len(globalIps) == 1 && globalIps[0].IpAddress != nil {
				return globalIps[0], "complete", nil
			} else if len(globalIps) == 0 || len(globalIps) == 1 {
				return nil, "pending", nil
			} else {
				return nil, "", fmt.Errorf("Expected one global ip: %s", err)
			}
		},
		Timeout:    10 * time.Minute,
		Delay:      5 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	pendingResult, err := stateConf.WaitForState()

	if err != nil {
		return datatypes.Network_Subnet_IpAddress_Global{}, err
	}

	if result, ok := pendingResult.(datatypes.Network_Subnet_IpAddress_Global); ok {
		return result, nil
	}

	return datatypes.Network_Subnet_IpAddress_Global{},
		fmt.Errorf("Cannot find global ip with order id '%d'", orderId)
}

func buildGlobalIpProductOrderContainer(d *schema.ResourceData, sess *session.Session, packageType string) (
	*datatypes.Container_Product_Order_Network_Subnet, error) {

	// 1. Get a package
	pkg, err := product.GetPackageByType(sess, packageType)
	if err != nil {
		return &datatypes.Container_Product_Order_Network_Subnet{}, err
	}

	// 2. Get all prices for the package
	productItems, err := product.GetPackageProducts(sess, *pkg.Id)
	if err != nil {
		return &datatypes.Container_Product_Order_Network_Subnet{}, err
	}

	// 3. Find global ip prices
	globalIpKeyname := "GLOBAL_IPV4"
	if strings.Contains(d.Get("routes_to").(string), ":") {
		globalIpKeyname = "GLOBAL_IPV6"
	}

	// 4. Select items with a matching keyname
	globalIpItems := []datatypes.Product_Item{}
	for _, item := range productItems {
		if *item.KeyName == globalIpKeyname {
			globalIpItems = append(globalIpItems, item)
		}
	}

	if len(globalIpItems) == 0 {
		return &datatypes.Container_Product_Order_Network_Subnet{},
			fmt.Errorf("No product items matching %s could be found", globalIpKeyname)
	}

	productOrderContainer := datatypes.Container_Product_Order_Network_Subnet{
		Container_Product_Order: datatypes.Container_Product_Order{
			PackageId: pkg.Id,
			Prices: []datatypes.Product_Item_Price{
				{
					Id: globalIpItems[0].Prices[0].Id,
				},
			},
			Quantity: sl.Int(1),
		},
	}

	return &productOrderContainer, nil
}
<commit_msg>Check if n has the appropriate IPv6 format in DiffSuppressFunc of globalip. (#114)<commit_after>package softlayer

import (
	"fmt"
	"log"
	"net"
	"strconv"
	"strings"
	"time"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/softlayer/softlayer-go/datatypes"
	"github.com/softlayer/softlayer-go/filter"
	"github.com/softlayer/softlayer-go/helpers/product"
	"github.com/softlayer/softlayer-go/services"
	"github.com/softlayer/softlayer-go/session"
	"github.com/softlayer/softlayer-go/sl"
)

const (
	AdditionalServicesGlobalIpAddressesPackageType = "ADDITIONAL_SERVICES_GLOBAL_IP_ADDRESSES"

	GlobalIpMask = "id,ipAddress[ipAddress],destinationIpAddress[ipAddress]"
)

func resourceSoftLayerGlobalIp() *schema.Resource {
	return &schema.Resource{
		Create:   resourceSoftLayerGlobalIpCreate,
		Read:     resourceSoftLayerGlobalIpRead,
		Update:   resourceSoftLayerGlobalIpUpdate,
		Delete:   resourceSoftLayerGlobalIpDelete,
		Exists:   resourceSoftLayerGlobalIpExists,
		Importer: &schema.ResourceImporter{},

		Schema: map[string]*schema.Schema{
			"id": &schema.Schema{
				Type:     schema.TypeInt,
				Computed: true,
			},

			"ip_address": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"routes_to": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
					address := v.(string)
					if net.ParseIP(address) == nil {
						errors = append(errors, fmt.Errorf("Invalid IP format: %s", address))
					}
					return
				},
				DiffSuppressFunc: func(k, o, n string, d *schema.ResourceData) bool {
					newRoutesTo := net.ParseIP(n)
					// Return true when n has the appropriate IPv6 format and
					// the compressed value of n equals the compressed value of o.
					return newRoutesTo != nil && (newRoutesTo.String() == net.ParseIP(o).String())
				},
			},
		},
	}
}

func resourceSoftLayerGlobalIpCreate(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(ProviderConfig).SoftLayerSession()

	// Find price items with AdditionalServicesGlobalIpAddresses
	productOrderContainer, err := buildGlobalIpProductOrderContainer(d, sess, AdditionalServicesGlobalIpAddressesPackageType)
	if err != nil {
		// Find price items with AdditionalServices
		productOrderContainer, err = buildGlobalIpProductOrderContainer(d, sess, AdditionalServicesPackageType)
		if err != nil {
			return fmt.Errorf("Error creating global ip: %s", err)
		}
	}

	log.Println("[INFO] Creating global ip")

	receipt, err := services.GetProductOrderService(sess).
		PlaceOrder(productOrderContainer, sl.Bool(false))
	if err != nil {
		return fmt.Errorf("Error during creation of global ip: %s", err)
	}

	globalIp, err := findGlobalIpByOrderId(sess, *receipt.OrderId)
	if err != nil {
		return fmt.Errorf("Error during creation of global ip: %s", err)
	}

	d.SetId(fmt.Sprintf("%d", *globalIp.Id))
	d.Set("ip_address", *globalIp.IpAddress.IpAddress)

	return resourceSoftLayerGlobalIpUpdate(d, meta)
}

func resourceSoftLayerGlobalIpRead(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(ProviderConfig).SoftLayerSession()
	service := services.GetNetworkSubnetIpAddressGlobalService(sess)

	globalIpId, err := strconv.Atoi(d.Id())
	if err != nil {
		return fmt.Errorf("Not a valid global ip ID, must be an integer: %s", err)
	}

	globalIp, err := service.Id(globalIpId).Mask(GlobalIpMask).GetObject()
	if err != nil {
		return fmt.Errorf("Error retrieving Global Ip: %s", err)
	}

	d.Set("id", *globalIp.Id)
	d.Set("ip_address", *globalIp.IpAddress.IpAddress)
	if globalIp.DestinationIpAddress != nil {
		d.Set("routes_to", *globalIp.DestinationIpAddress.IpAddress)
	}
	return nil
}

func resourceSoftLayerGlobalIpUpdate(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(ProviderConfig).SoftLayerSession()
	service := services.GetNetworkSubnetIpAddressGlobalService(sess)

	globalIpId, err := strconv.Atoi(d.Id())
	if err != nil {
		return fmt.Errorf("Not a valid global ip ID, must be an integer: %s", err)
	}

	routes_to := d.Get("routes_to").(string)
	if strings.Contains(routes_to, ":") && len(routes_to) != 39 {
		parts := strings.Split(routes_to, ":")
		for x, s := range parts {
			if s == "" {
				zeroes := 9 - len(parts)
				parts[x] = strings.Repeat("0000:", zeroes)[:(zeroes*4)+(zeroes-1)]
			} else {
				parts[x] = fmt.Sprintf("%04s", s)
			}
		}

		routes_to = strings.Join(parts, ":")
		d.Set("routes_to", routes_to)
	}

	_, err = service.Id(globalIpId).Route(sl.String(routes_to))
	if err != nil {
		return fmt.Errorf("Error editing Global Ip: %s", err)
	}
	stateConf := &resource.StateChangeConf{
		Pending: []string{"pending"},
		Target:  []string{"complete"},
		Refresh: func() (interface{}, string, error) {
			transaction, err := service.Id(globalIpId).GetActiveTransaction()
			if err != nil {
				return datatypes.Network_Subnet_IpAddress_Global{}, "pending", err
			}
			if transaction.Id == nil {
				return datatypes.Network_Subnet_IpAddress_Global{}, "complete", nil
			}
			return datatypes.Network_Subnet_IpAddress_Global{}, "pending", nil
		},
		Timeout:    10 * time.Minute,
		Delay:      5 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	pendingResult, err := stateConf.WaitForState()

	if err != nil {
		return fmt.Errorf("Error waiting for global ip destination ip address to become active: %s", err)
	}

	if _, ok := pendingResult.(datatypes.Network_Subnet_IpAddress_Global); ok {
		return nil
	}

	return nil
}

func resourceSoftLayerGlobalIpDelete(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(ProviderConfig).SoftLayerSession()
	service := services.GetNetworkSubnetIpAddressGlobalService(sess)

	globalIpId, err := strconv.Atoi(d.Id())
	if err != nil {
		return fmt.Errorf("Not a valid global ip ID, must be an integer: %s", err)
	}

	billingItem, err := service.Id(globalIpId).GetBillingItem()
	if err != nil {
		return fmt.Errorf("Error deleting global ip: %s", err)
	}

	if billingItem.Id == nil {
		return nil
	}

	_, err = services.GetBillingItemService(sess).Id(*billingItem.Id).CancelService()

	return err
}

func resourceSoftLayerGlobalIpExists(d *schema.ResourceData, meta interface{}) (bool, error) {
	sess := meta.(ProviderConfig).SoftLayerSession()
	service := services.GetNetworkSubnetIpAddressGlobalService(sess)

	globalIpId, err := strconv.Atoi(d.Id())
	if err != nil {
		return false, fmt.Errorf("Not a valid ID, must be an integer: %s", err)
	}

	result, err := service.Id(globalIpId).GetObject()
	if err != nil {
		if apiErr, ok := err.(sl.Error); ok && apiErr.StatusCode == 404 {
			return false, nil
		}
		return false, fmt.Errorf("Error retrieving global ip: %s", err)
	}
	return result.Id != nil && *result.Id == globalIpId, nil
}

func findGlobalIpByOrderId(sess *session.Session, orderId int) (datatypes.Network_Subnet_IpAddress_Global, error) {
	stateConf := &resource.StateChangeConf{
		Pending: []string{"pending"},
		Target:  []string{"complete"},
		Refresh: func() (interface{}, string, error) {
			globalIps, err := services.GetAccountService(sess).
				Filter(filter.Path("globalIpRecords.billingItem.orderItem.order.id").
					Eq(strconv.Itoa(orderId)).Build()).
				Mask("id,ipAddress[ipAddress]").
				GetGlobalIpRecords()
			if err != nil {
				return datatypes.Network_Subnet_IpAddress_Global{}, "", err
			}

			if len(globalIps) == 1 && globalIps[0].IpAddress != nil {
				return globalIps[0], "complete", nil
			} else if len(globalIps) == 0 || len(globalIps) == 1 {
				return nil, "pending", nil
			} else {
				return nil, "", fmt.Errorf("Expected one global ip: %s", err)
			}
		},
		Timeout:    10 * time.Minute,
		Delay:      5 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	pendingResult, err := stateConf.WaitForState()

	if err != nil {
		return datatypes.Network_Subnet_IpAddress_Global{}, err
	}

	if result, ok := pendingResult.(datatypes.Network_Subnet_IpAddress_Global); ok {
		return result, nil
	}

	return datatypes.Network_Subnet_IpAddress_Global{},
		fmt.Errorf("Cannot find global ip with order id '%d'", orderId)
}

func buildGlobalIpProductOrderContainer(d *schema.ResourceData, sess *session.Session, packageType string) (
	*datatypes.Container_Product_Order_Network_Subnet, error) {

	// 1. Get a package
	pkg, err := product.GetPackageByType(sess, packageType)
	if err != nil {
		return &datatypes.Container_Product_Order_Network_Subnet{}, err
	}

	// 2. Get all prices for the package
	productItems, err := product.GetPackageProducts(sess, *pkg.Id)
	if err != nil {
		return &datatypes.Container_Product_Order_Network_Subnet{}, err
	}

	// 3. Find global ip prices
	globalIpKeyname := "GLOBAL_IPV4"
	if strings.Contains(d.Get("routes_to").(string), ":") {
		globalIpKeyname = "GLOBAL_IPV6"
	}

	// 4. Select items with a matching keyname
	globalIpItems := []datatypes.Product_Item{}
	for _, item := range productItems {
		if *item.KeyName == globalIpKeyname {
			globalIpItems = append(globalIpItems, item)
		}
	}

	if len(globalIpItems) == 0 {
		return &datatypes.Container_Product_Order_Network_Subnet{},
			fmt.Errorf("No product items matching %s could be found", globalIpKeyname)
	}

	productOrderContainer := datatypes.Container_Product_Order_Network_Subnet{
		Container_Product_Order: datatypes.Container_Product_Order{
			PackageId: pkg.Id,
			Prices: []datatypes.Product_Item_Price{
				{
					Id: globalIpItems[0].Prices[0].Id,
				},
			},
			Quantity: sl.Int(1),
		},
	}

	return &productOrderContainer, nil
}
<|endoftext|>


<commit_before>package ingestor

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

	"code.cloudfoundry.org/lager"
	"github.com/google/go-github/github"
)

type handler struct {
	logger    lager.Logger
	secretKey []byte
	ingestor  Ingestor
}

func NewHandler(logger lager.Logger, ingestor Ingestor, secretKey string) *handler {
	return &handler{
		logger:    logger.Session("webhook-handler"),
		secretKey: []byte(secretKey),
		ingestor:  ingestor,
	}
}

func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.logger.Debug("starting")

	payload, err := github.ValidatePayload(r, h.secretKey)
	if err != nil {
		h.logger.Error("invalid-payload", err)
		w.WriteHeader(http.StatusForbidden)
		return
	}

	var event github.PushEvent
	if err := json.Unmarshal(payload, &event); err != nil {
		h.logger.Error("unmarshal-failed", err)
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	scan, valid := extractPushScanFromEvent(event)
	if !valid {
		h.logger.Info("invalid-event-dropped")
		w.WriteHeader(http.StatusOK)
		return
	}

	h.logger.Info("handling-webhook-payload", lager.Data{
		"before":  scan.From,
		"after":   scan.To,
		"owner":   scan.Owner,
		"repo":    scan.Repository,
		"private": scan.Private,
	})

	err = h.ingestor.IngestPushScan(h.logger, scan, r.Header.Get("X-GitHub-Delivery"))
	if err != nil {
		h.logger.Error("ingest-push-scan-failed", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	h.logger.Debug("done")
	w.WriteHeader(http.StatusOK)
}
<commit_msg>ingestor.NewHandler returns http.Handler<commit_after>package ingestor

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

	"code.cloudfoundry.org/lager"
	"github.com/google/go-github/github"
)

type handler struct {
	logger    lager.Logger
	secretKey []byte
	ingestor  Ingestor
}

func NewHandler(logger lager.Logger, ingestor Ingestor, secretKey string) http.Handler {
	return &handler{
		logger:    logger.Session("webhook-handler"),
		secretKey: []byte(secretKey),
		ingestor:  ingestor,
	}
}

func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.logger.Debug("starting")

	payload, err := github.ValidatePayload(r, h.secretKey)
	if err != nil {
		h.logger.Error("invalid-payload", err)
		w.WriteHeader(http.StatusForbidden)
		return
	}

	var event github.PushEvent
	if err := json.Unmarshal(payload, &event); err != nil {
		h.logger.Error("unmarshal-failed", err)
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	scan, valid := extractPushScanFromEvent(event)
	if !valid {
		h.logger.Info("invalid-event-dropped")
		w.WriteHeader(http.StatusOK)
		return
	}

	h.logger.Info("handling-webhook-payload", lager.Data{
		"before":  scan.From,
		"after":   scan.To,
		"owner":   scan.Owner,
		"repo":    scan.Repository,
		"private": scan.Private,
	})

	err = h.ingestor.IngestPushScan(h.logger, scan, r.Header.Get("X-GitHub-Delivery"))
	if err != nil {
		h.logger.Error("ingest-push-scan-failed", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	h.logger.Debug("done")
	w.WriteHeader(http.StatusOK)
}
<|endoftext|>


<commit_before>package admin

import (
	"diskette/util"
	"errors"
	"net/http"
	"time"

	"github.com/labstack/echo"
	"labix.org/v2/mgo/bson"
)

// http POST localhost:5025/admin/remove-users userIds:='["56bf19d65a1d18b704000001", "56be731d5a1d18accd000001"]' X-Diskette-Session-Token:<session_token>
func (service *serviceImpl) SignoutUsers(c *echo.Context) error {
	var request struct {
		UserIds []string `json:"userIds"`
	}
	c.Bind(&request)

	if request.UserIds == nil {
		return c.JSON(http.StatusBadRequest, util.CreateErrResponse(errors.New("Missing parameter 'userIds'")))
	}

	objectIds := []bson.ObjectId{}
	for _, userId := range request.UserIds {
		objectIds = append(objectIds, bson.ObjectIdHex(userId))
	}

	info, err := service.userCollection.UpdateAll(
		bson.M{
			"_id": bson.M{
				"$in": objectIds,
			},
		},
		bson.M{
			"$set": bson.M{
				"signedOutAt": time.Now(),
			},
		},
	)

	if err != nil {
		return c.JSON(http.StatusInternalServerError, util.CreateErrResponse(err))
	}

	return c.JSON(http.StatusOK, util.CreateOkResponse(info.Updated))
}
<commit_msg>update<commit_after>package admin

import (
	"diskette/util"
	"errors"
	"net/http"
	"time"

	"github.com/labstack/echo"
	"labix.org/v2/mgo/bson"
)

// http POST localhost:5025/admin/signout-users userIds:='["56bf19d65a1d18b704000001", "56be731d5a1d18accd000001"]' X-Diskette-Session-Token:<session_token>
func (service *serviceImpl) SignoutUsers(c *echo.Context) error {
	var request struct {
		UserIds []string `json:"userIds"`
	}
	c.Bind(&request)

	if request.UserIds == nil {
		return c.JSON(http.StatusBadRequest, util.CreateErrResponse(errors.New("Missing parameter 'userIds'")))
	}

	objectIds := []bson.ObjectId{}
	for _, userId := range request.UserIds {
		objectIds = append(objectIds, bson.ObjectIdHex(userId))
	}

	info, err := service.userCollection.UpdateAll(
		bson.M{
			"_id": bson.M{
				"$in": objectIds,
			},
		},
		bson.M{
			"$set": bson.M{
				"signedOutAt": time.Now(),
			},
		},
	)

	if err != nil {
		return c.JSON(http.StatusInternalServerError, util.CreateErrResponse(err))
	}

	return c.JSON(http.StatusOK, util.CreateOkResponse(info.Updated))
}
<|endoftext|>


<commit_before>package match

import (
	"fmt"

	"github.com/tisp-lang/tisp/src/lib/ast"
	"github.com/tisp-lang/tisp/src/lib/debug"
	"github.com/tisp-lang/tisp/src/lib/gensym"
	"github.com/tisp-lang/tisp/src/lib/scalar"
)

type desugarer struct {
	letBoundNames, lets []interface{}
}

func newDesugarer() *desugarer {
	return &desugarer{nil, nil}
}

func (d *desugarer) desugar(x interface{}) interface{} {
	switch x := x.(type) {
	case ast.App:
		return ast.NewApp(
			d.desugar(x.Function()),
			d.desugar(x.Arguments()).(ast.Arguments),
			x.DebugInfo())
	case ast.Arguments:
		ps := make([]ast.PositionalArgument, 0, len(x.Positionals()))

		for _, p := range x.Positionals() {
			ps = append(ps, d.desugar(p).(ast.PositionalArgument))
		}

		ks := make([]ast.KeywordArgument, 0, len(x.Keywords()))

		for _, k := range x.Keywords() {
			ks = append(ks, d.desugar(k).(ast.KeywordArgument))
		}

		ds := make([]interface{}, 0, len(x.ExpandedDicts()))

		for _, dict := range x.ExpandedDicts() {
			ds = append(ds, d.desugar(dict))
		}

		return ast.NewArguments(ps, ks, ds)
	case ast.KeywordArgument:
		return ast.NewKeywordArgument(x.Name(), d.desugar(x.Value()))
	case ast.LetFunction:
		ls := make([]interface{}, 0, len(x.Lets()))

		for _, l := range x.Lets() {
			l := d.desugar(l)
			ls = append(ls, append(d.takeLets(), l)...)
		}

		b := d.desugar(x.Body())

		return ast.NewLetFunction(
			x.Name(),
			x.Signature(),
			append(ls, d.takeLets()...),
			b,
			x.DebugInfo())
	case ast.LetVar:
		return ast.NewLetVar(x.Name(), d.desugar(x.Expr()))
	case ast.Match:
		cs := make([]ast.MatchCase, 0, len(x.Cases()))

		for _, c := range x.Cases() {
			cs = append(cs, renameBoundNamesInCase(ast.NewMatchCase(c.Pattern(), d.desugar(c.Value()))))
		}

		return d.resultApp(d.createMatchFunction(cs), d.desugar(x.Value()))
	case ast.MutualRecursion:
		fs := make([]ast.LetFunction, 0, len(x.LetFunctions()))

		for _, f := range x.LetFunctions() {
			fs = append(fs, d.desugar(f).(ast.LetFunction))
		}

		return ast.NewMutualRecursion(fs, x.DebugInfo())
	case ast.Output:
		return ast.NewOutput(d.desugar(x.Expr()), x.Expanded())
	case ast.PositionalArgument:
		return ast.NewPositionalArgument(d.desugar(x.Value()), x.Expanded())
	default:
		return x
	}
}

func (d *desugarer) takeLets() []interface{} {
	ls := append(d.letBoundNames, d.lets...)
	d.letBoundNames = nil
	d.lets = nil
	return ls
}

func (d *desugarer) letTempVar(v interface{}) string {
	s := gensym.GenSym("match", "tmp")
	d.lets = append(d.lets, ast.NewLetVar(s, v))
	return s
}

func (d *desugarer) bindName(p interface{}, v interface{}) string {
	s := generalNamePatternToName(p)
	d.letBoundNames = append(d.letBoundNames, ast.NewLetVar(s, v))
	return s
}

// matchedApp applies a function to arguments and creates a matched value of
// match expression.
func (d *desugarer) matchedApp(f interface{}, args ...interface{}) string {
	return d.bindName(gensym.GenSym("match", "app"), app(f, args...))
}

// resultApp applies a function to arguments and creates a result value of match
// expression.
func (d *desugarer) resultApp(f interface{}, args ...interface{}) string {
	return d.letTempVar(app(f, args...))
}

func (d *desugarer) createMatchFunction(cs []ast.MatchCase) interface{} {
	arg := gensym.GenSym("match", "argument")
	body := d.desugarCases(arg, cs, "$matchError")

	f := ast.NewLetFunction(
		gensym.GenSym("match", "function"),
		ast.NewSignature([]string{arg}, nil, "", nil, nil, ""),
		d.takeLets(),
		body,
		debug.NewGoInfo(0))

	d.lets = append(d.lets, f)

	return f.Name()
}

func (d *desugarer) desugarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	css := groupCases(cs)

	if cs, ok := css[namePattern]; ok {
		c := cs[0]
		d.bindName(c.Pattern().(string), v)
		dc = c.Value()
	}

	ks := []ast.SwitchCase{}

	if cs, ok := css[listPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"list\"", d.desugarListCases(v, cs, dc)))
	}

	if cs, ok := css[dictPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"dict\"", d.desugarDictCases(v, cs, dc)))
	}

	if cs, ok := css[scalarPattern]; ok {
		dc = d.desugarScalarCases(v, cs, dc)
	}

	return newSwitch(d.resultApp("$typeOf", v), ks, dc)
}

func groupCases(cs []ast.MatchCase) map[patternType][]ast.MatchCase {
	css := map[patternType][]ast.MatchCase{}

	for i, c := range cs {
		t := getPatternType(c.Pattern())

		if t == namePattern && i < len(cs)-1 {
			panic("A wildcard pattern is found while some patterns are left")
		}

		css[t] = append(css[t], c)
	}

	return css
}

func getPatternType(p interface{}) patternType {
	switch x := p.(type) {
	case string:
		if scalar.Defined(x) {
			return scalarPattern
		}

		return namePattern
	case ast.App:
		switch x.Function().(string) {
		case "$list":
			return listPattern
		case "$dict":
			return dictPattern
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}

func isGeneralNamePattern(p interface{}) bool {
	switch x := p.(type) {
	case string:
		if scalar.Defined(x) {
			return false
		}

		return true
	case ast.App:
		ps := x.Arguments().Positionals()
		ok := len(ps) == 1 && ps[0].Expanded()

		switch x.Function().(string) {
		case "$list":
			return ok
		case "$dict":
			return ok
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}

func (d *desugarer) desugarListCases(list interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		first interface{}
		cases []ast.MatchCase
	}

	gs := []group{}
	first := d.matchedApp("$first", list)
	rest := d.matchedApp("$rest", list)

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = d.resultApp("$if", app("$=", list, "$emptyList"), c.Value(), dc)
			continue
		}

		v := ps[0].Value()

		if ps[0].Expanded() {
			d.bindName(v.(string), list)
			dc = c.Value()
			break
		}

		c = ast.NewMatchCase(
			ast.NewApp("$list", ast.NewArguments(ps[1:], nil, nil), debug.NewGoInfo(0)),
			c.Value())

		if isGeneralNamePattern(v) {
			d.bindName(v, first)

			if ks := cs[i+1:]; len(ks) > 0 {
				dc = d.desugarListCases(list, ks, dc)
			}

			next := d.desugarCases(rest, []ast.MatchCase{c}, dc)

			switch getPatternType(v) {
			case namePattern:
				dc = next
			case listPattern:
				dc = d.ifType(first, "list", next, dc)
			case dictPattern:
				dc = d.ifType(first, "dict", next, dc)
			default:
				panic("Unreachable")
			}

			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(v, g.first) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{v, []ast.MatchCase{c}})
		}
	}

	ks := make([]ast.MatchCase, 0, len(gs))
	dc = d.letTempVar(dc)

	for _, g := range gs {
		ks = append(ks, ast.NewMatchCase(g.first, d.desugarCases(rest, g.cases, dc)))
	}

	return d.desugarCases(first, ks, dc)
}

func (d *desugarer) ifType(v interface{}, t string, then, els interface{}) interface{} {
	return d.resultApp("$if", app("$=", app("$typeOf", v), "\""+t+"\""), then, els)
}

func (d *desugarer) desugarDictCases(dict interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		key   interface{}
		cases []ast.MatchCase
	}

	gs := []group{}

	for _, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = d.resultApp("$if", app("$=", dict, "$emptyDict"), c.Value(), dc)
			continue
		}

		k := ps[0].Value()

		if ps[0].Expanded() {
			d.bindName(k.(string), dict)
			dc = c.Value()
			break
		}

		g := group{k, []ast.MatchCase{c}}

		if len(gs) == 0 {
			gs = append(gs, g)
		} else if last := len(gs) - 1; equalPatterns(g.key, gs[last].key) {
			gs[last].cases = append(gs[last].cases, c)
		} else {
			gs = append(gs, g)
		}
	}

	for i := len(gs) - 1; i >= 0; i-- {
		g := gs[i]
		dc = d.resultApp("$if",
			app("$include", dict, g.key),
			d.desugarDictCasesOfSameKey(dict, g.cases, dc),
			dc)
	}

	return dc
}

func (d *desugarer) desugarDictCasesOfSameKey(dict interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		value interface{}
		cases []ast.MatchCase
	}

	key := cs[0].Pattern().(ast.App).Arguments().Positionals()[0].Value()
	value := d.matchedApp(dict, key)
	newDict := d.matchedApp("delete", dict, key)
	gs := []group{}

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()
		v := ps[1].Value()

		c = ast.NewMatchCase(
			ast.NewApp("$dict", ast.NewArguments(ps[2:], nil, nil), debug.NewGoInfo(0)),
			c.Value())

		if isGeneralNamePattern(v) {
			d.bindName(v, value)

			if ks := cs[i+1:]; len(ks) != 0 {
				dc = d.desugarDictCasesOfSameKey(dict, ks, dc)
			}

			next := d.desugarCases(newDict, []ast.MatchCase{c}, dc)

			switch getPatternType(v) {
			case namePattern:
				dc = next
			case listPattern:
				dc = d.ifType(value, "list", next, dc)
			case dictPattern:
				dc = d.ifType(value, "dict", next, dc)
			default:
				panic("Unreachable")
			}

			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(v, g.value) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{v, []ast.MatchCase{c}})
		}
	}

	cs = make([]ast.MatchCase, 0, len(gs))
	dc = d.letTempVar(dc)

	for _, g := range gs {
		cs = append(
			cs,
			ast.NewMatchCase(g.value, d.desugarCases(newDict, g.cases, dc)))
	}

	return d.desugarCases(value, cs, dc)
}

func (d *desugarer) desugarScalarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	ks := []ast.SwitchCase{}

	for _, c := range cs {
		ks = append(ks, ast.NewSwitchCase(c.Pattern().(string), c.Value()))
	}

	return newSwitch(v, ks, dc)
}

func renameBoundNamesInCase(c ast.MatchCase) ast.MatchCase {
	p, ns := newPatternRenamer().rename(c.Pattern())
	return ast.NewMatchCase(p, newValueRenamer(ns).rename(c.Value()))
}

func equalPatterns(p, q interface{}) bool {
	switch x := p.(type) {
	case string:
		y, ok := q.(string)

		if !ok {
			return false
		}

		return x == y
	case ast.App:
		y, ok := q.(ast.App)

		if !ok ||
			x.Function().(string) != y.Function().(string) ||
			len(x.Arguments().Positionals()) != len(y.Arguments().Positionals()) {
			return false
		}

		for i := range x.Arguments().Positionals() {
			p := x.Arguments().Positionals()[i]
			q := y.Arguments().Positionals()[i]

			if p.Expanded() != q.Expanded() || !equalPatterns(p.Value(), q.Value()) {
				return false
			}
		}

		return true
	}

	panic(fmt.Errorf("Invalid pattern: %#v, %#v", p, q))
}

func generalNamePatternToName(p interface{}) string {
	switch x := p.(type) {
	case string:
		return x
	case ast.App:
		if ps := x.Arguments().Positionals(); len(ps) == 1 && ps[0].Expanded() {
			return ps[0].Value().(string)
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}
<commit_msg>Refactor match/desugarer.go<commit_after>package match

import (
	"fmt"

	"github.com/tisp-lang/tisp/src/lib/ast"
	"github.com/tisp-lang/tisp/src/lib/debug"
	"github.com/tisp-lang/tisp/src/lib/gensym"
	"github.com/tisp-lang/tisp/src/lib/scalar"
)

type desugarer struct {
	letBoundNames, lets []interface{}
}

func newDesugarer() *desugarer {
	return &desugarer{nil, nil}
}

func (d *desugarer) desugar(x interface{}) interface{} {
	switch x := x.(type) {
	case ast.App:
		return ast.NewApp(
			d.desugar(x.Function()),
			d.desugar(x.Arguments()).(ast.Arguments),
			x.DebugInfo())
	case ast.Arguments:
		ps := make([]ast.PositionalArgument, 0, len(x.Positionals()))

		for _, p := range x.Positionals() {
			ps = append(ps, d.desugar(p).(ast.PositionalArgument))
		}

		ks := make([]ast.KeywordArgument, 0, len(x.Keywords()))

		for _, k := range x.Keywords() {
			ks = append(ks, d.desugar(k).(ast.KeywordArgument))
		}

		ds := make([]interface{}, 0, len(x.ExpandedDicts()))

		for _, dict := range x.ExpandedDicts() {
			ds = append(ds, d.desugar(dict))
		}

		return ast.NewArguments(ps, ks, ds)
	case ast.KeywordArgument:
		return ast.NewKeywordArgument(x.Name(), d.desugar(x.Value()))
	case ast.LetFunction:
		ls := make([]interface{}, 0, len(x.Lets()))

		for _, l := range x.Lets() {
			l := d.desugar(l)
			ls = append(ls, append(d.takeLets(), l)...)
		}

		b := d.desugar(x.Body())

		return ast.NewLetFunction(
			x.Name(),
			x.Signature(),
			append(ls, d.takeLets()...),
			b,
			x.DebugInfo())
	case ast.LetVar:
		return ast.NewLetVar(x.Name(), d.desugar(x.Expr()))
	case ast.Match:
		cs := make([]ast.MatchCase, 0, len(x.Cases()))

		for _, c := range x.Cases() {
			cs = append(cs, renameBoundNamesInCase(ast.NewMatchCase(c.Pattern(), d.desugar(c.Value()))))
		}

		return d.resultApp(d.createMatchFunction(cs), d.desugar(x.Value()))
	case ast.MutualRecursion:
		fs := make([]ast.LetFunction, 0, len(x.LetFunctions()))

		for _, f := range x.LetFunctions() {
			fs = append(fs, d.desugar(f).(ast.LetFunction))
		}

		return ast.NewMutualRecursion(fs, x.DebugInfo())
	case ast.Output:
		return ast.NewOutput(d.desugar(x.Expr()), x.Expanded())
	case ast.PositionalArgument:
		return ast.NewPositionalArgument(d.desugar(x.Value()), x.Expanded())
	default:
		return x
	}
}

func (d *desugarer) takeLets() []interface{} {
	ls := append(d.letBoundNames, d.lets...)
	d.letBoundNames = nil
	d.lets = nil
	return ls
}

func (d *desugarer) letTempVar(v interface{}) string {
	s := gensym.GenSym("match", "tmp")
	d.lets = append(d.lets, ast.NewLetVar(s, v))
	return s
}

func (d *desugarer) bindName(p interface{}, v interface{}) string {
	s := generalNamePatternToName(p)
	d.letBoundNames = append(d.letBoundNames, ast.NewLetVar(s, v))
	return s
}

// matchedApp applies a function to arguments and creates a matched value of
// match expression.
func (d *desugarer) matchedApp(f interface{}, args ...interface{}) string {
	return d.bindName(gensym.GenSym("match", "app"), app(f, args...))
}

// resultApp applies a function to arguments and creates a result value of match
// expression.
func (d *desugarer) resultApp(f interface{}, args ...interface{}) string {
	return d.letTempVar(app(f, args...))
}

func (d *desugarer) createMatchFunction(cs []ast.MatchCase) interface{} {
	arg := gensym.GenSym("match", "argument")
	body := d.desugarCases(arg, cs, "$matchError")

	f := ast.NewLetFunction(
		gensym.GenSym("match", "function"),
		ast.NewSignature([]string{arg}, nil, "", nil, nil, ""),
		d.takeLets(),
		body,
		debug.NewGoInfo(0))

	d.lets = append(d.lets, f)

	return f.Name()
}

func (d *desugarer) desugarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	css := groupCases(cs)

	if cs, ok := css[namePattern]; ok {
		c := cs[0]
		d.bindName(c.Pattern().(string), v)
		dc = c.Value()
	}

	ks := []ast.SwitchCase{}

	if cs, ok := css[listPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"list\"", d.desugarListCases(v, cs, dc)))
	}

	if cs, ok := css[dictPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"dict\"", d.desugarDictCases(v, cs, dc)))
	}

	if cs, ok := css[scalarPattern]; ok {
		dc = d.desugarScalarCases(v, cs, dc)
	}

	return newSwitch(d.resultApp("$typeOf", v), ks, dc)
}

func groupCases(cs []ast.MatchCase) map[patternType][]ast.MatchCase {
	css := map[patternType][]ast.MatchCase{}

	for i, c := range cs {
		t := getPatternType(c.Pattern())

		if t == namePattern && i < len(cs)-1 {
			panic("A wildcard pattern is found while some patterns are left")
		}

		css[t] = append(css[t], c)
	}

	return css
}

func getPatternType(p interface{}) patternType {
	switch x := p.(type) {
	case string:
		if scalar.Defined(x) {
			return scalarPattern
		}

		return namePattern
	case ast.App:
		switch x.Function().(string) {
		case "$list":
			return listPattern
		case "$dict":
			return dictPattern
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}

func isGeneralNamePattern(p interface{}) bool {
	switch x := p.(type) {
	case string:
		if scalar.Defined(x) {
			return false
		}

		return true
	case ast.App:
		ps := x.Arguments().Positionals()
		ok := len(ps) == 1 && ps[0].Expanded()

		switch x.Function().(string) {
		case "$list":
			return ok
		case "$dict":
			return ok
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}

func (d *desugarer) desugarListCases(list interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		first interface{}
		cases []ast.MatchCase
	}

	gs := []group{}
	first := d.matchedApp("$first", list)
	rest := d.matchedApp("$rest", list)

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = d.resultApp("$if", app("$=", list, "$emptyList"), c.Value(), dc)
			continue
		}

		v := ps[0].Value()

		if ps[0].Expanded() {
			d.bindName(v.(string), list)
			dc = c.Value()
			break
		}

		c = ast.NewMatchCase(
			ast.NewApp("$list", ast.NewArguments(ps[1:], nil, nil), debug.NewGoInfo(0)),
			c.Value())

		if isGeneralNamePattern(v) {
			d.bindName(v, first)

			if cs := cs[i+1:]; len(cs) > 0 {
				dc = d.desugarListCases(list, cs, dc)
			}

			next := d.desugarCases(rest, []ast.MatchCase{c}, dc)

			switch getPatternType(v) {
			case namePattern:
				dc = next
			case listPattern:
				dc = d.ifType(first, "list", next, dc)
			case dictPattern:
				dc = d.ifType(first, "dict", next, dc)
			default:
				panic("Unreachable")
			}

			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(v, g.first) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{v, []ast.MatchCase{c}})
		}
	}

	cs = make([]ast.MatchCase, 0, len(gs))
	dc = d.letTempVar(dc)

	for _, g := range gs {
		cs = append(cs, ast.NewMatchCase(g.first, d.desugarCases(rest, g.cases, dc)))
	}

	return d.desugarCases(first, cs, dc)
}

func (d *desugarer) ifType(v interface{}, t string, then, els interface{}) interface{} {
	return d.resultApp("$if", app("$=", app("$typeOf", v), "\""+t+"\""), then, els)
}

func (d *desugarer) desugarDictCases(dict interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		key   interface{}
		cases []ast.MatchCase
	}

	gs := []group{}

	for _, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = d.resultApp("$if", app("$=", dict, "$emptyDict"), c.Value(), dc)
			continue
		}

		k := ps[0].Value()

		if ps[0].Expanded() {
			d.bindName(k.(string), dict)
			dc = c.Value()
			break
		}

		g := group{k, []ast.MatchCase{c}}

		if len(gs) == 0 {
			gs = append(gs, g)
		} else if last := len(gs) - 1; equalPatterns(g.key, gs[last].key) {
			gs[last].cases = append(gs[last].cases, c)
		} else {
			gs = append(gs, g)
		}
	}

	for i := len(gs) - 1; i >= 0; i-- {
		g := gs[i]
		dc = d.resultApp("$if",
			app("$include", dict, g.key),
			d.desugarDictCasesOfSameKey(dict, g.cases, dc),
			dc)
	}

	return dc
}

func (d *desugarer) desugarDictCasesOfSameKey(dict interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		value interface{}
		cases []ast.MatchCase
	}

	key := cs[0].Pattern().(ast.App).Arguments().Positionals()[0].Value()
	value := d.matchedApp(dict, key)
	newDict := d.matchedApp("delete", dict, key)
	gs := []group{}

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()
		v := ps[1].Value()

		c = ast.NewMatchCase(
			ast.NewApp("$dict", ast.NewArguments(ps[2:], nil, nil), debug.NewGoInfo(0)),
			c.Value())

		if isGeneralNamePattern(v) {
			d.bindName(v, value)

			if cs := cs[i+1:]; len(cs) != 0 {
				dc = d.desugarDictCasesOfSameKey(dict, cs, dc)
			}

			next := d.desugarCases(newDict, []ast.MatchCase{c}, dc)

			switch getPatternType(v) {
			case namePattern:
				dc = next
			case listPattern:
				dc = d.ifType(value, "list", next, dc)
			case dictPattern:
				dc = d.ifType(value, "dict", next, dc)
			default:
				panic("Unreachable")
			}

			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(v, g.value) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{v, []ast.MatchCase{c}})
		}
	}

	cs = make([]ast.MatchCase, 0, len(gs))
	dc = d.letTempVar(dc)

	for _, g := range gs {
		cs = append(
			cs,
			ast.NewMatchCase(g.value, d.desugarCases(newDict, g.cases, dc)))
	}

	return d.desugarCases(value, cs, dc)
}

func (d *desugarer) desugarScalarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	ks := []ast.SwitchCase{}

	for _, c := range cs {
		ks = append(ks, ast.NewSwitchCase(c.Pattern().(string), c.Value()))
	}

	return newSwitch(v, ks, dc)
}

func renameBoundNamesInCase(c ast.MatchCase) ast.MatchCase {
	p, ns := newPatternRenamer().rename(c.Pattern())
	return ast.NewMatchCase(p, newValueRenamer(ns).rename(c.Value()))
}

func equalPatterns(p, q interface{}) bool {
	switch x := p.(type) {
	case string:
		y, ok := q.(string)

		if !ok {
			return false
		}

		return x == y
	case ast.App:
		y, ok := q.(ast.App)

		if !ok ||
			x.Function().(string) != y.Function().(string) ||
			len(x.Arguments().Positionals()) != len(y.Arguments().Positionals()) {
			return false
		}

		for i := range x.Arguments().Positionals() {
			p := x.Arguments().Positionals()[i]
			q := y.Arguments().Positionals()[i]

			if p.Expanded() != q.Expanded() || !equalPatterns(p.Value(), q.Value()) {
				return false
			}
		}

		return true
	}

	panic(fmt.Errorf("Invalid pattern: %#v, %#v", p, q))
}

func generalNamePatternToName(p interface{}) string {
	switch x := p.(type) {
	case string:
		return x
	case ast.App:
		if ps := x.Arguments().Positionals(); len(ps) == 1 && ps[0].Expanded() {
			return ps[0].Value().(string)
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}
<|endoftext|>


<commit_before>package match

import (
	"fmt"

	"github.com/tisp-lang/tisp/src/lib/ast"
	"github.com/tisp-lang/tisp/src/lib/debug"
	"github.com/tisp-lang/tisp/src/lib/gensym"
	"github.com/tisp-lang/tisp/src/lib/scalar"
)

type desugarer struct {
	lets []interface{}
}

func newDesugarer() *desugarer {
	return &desugarer{nil}
}

func (d *desugarer) desugarMatchExpression(x interface{}) interface{} {
	switch x := x.(type) {
	case ast.App:
		return ast.NewApp(
			d.desugarMatchExpression(x.Function()),
			d.desugarMatchExpression(x.Arguments()).(ast.Arguments),
			x.DebugInfo())
	case ast.Arguments:
		ps := make([]ast.PositionalArgument, 0, len(x.Positionals()))

		for _, p := range x.Positionals() {
			ps = append(ps, d.desugarMatchExpression(p).(ast.PositionalArgument))
		}

		ks := make([]ast.KeywordArgument, 0, len(x.Keywords()))

		for _, k := range x.Keywords() {
			ks = append(ks, d.desugarMatchExpression(k).(ast.KeywordArgument))
		}

		dicts := make([]interface{}, 0, len(x.ExpandedDicts()))

		for _, dict := range x.ExpandedDicts() {
			dicts = append(dicts, d.desugarMatchExpression(dict))
		}

		return ast.NewArguments(ps, ks, dicts)
	case ast.KeywordArgument:
		return ast.NewKeywordArgument(x.Name(), d.desugarMatchExpression(x.Value()))
	case ast.LetFunction:
		ls := make([]interface{}, 0, len(x.Lets()))

		for _, l := range x.Lets() {
			l := d.desugarMatchExpression(l)
			ls = append(ls, append(d.lets, l)...)
			d.lets = nil
		}

		return ast.NewLetFunction(
			x.Name(),
			x.Signature(),
			ls,
			d.desugarMatchExpression(x.Body()),
			x.DebugInfo())
	case ast.LetVar:
		return ast.NewLetVar(x.Name(), d.desugarMatchExpression(x.Expr()))
	case ast.Match:
		return app(d.createMatchFunction(x.Cases()), d.desugarMatchExpression(x.Value()))
	case ast.Output:
		return ast.NewOutput(d.desugarMatchExpression(x.Expr()), x.Expanded())
	case ast.PositionalArgument:
		return ast.NewPositionalArgument(d.desugarMatchExpression(x.Value()), x.Expanded())
	default:
		return x
	}
}

func (d *desugarer) letVar(v interface{}) string {
	s := gensym.GenSym("match", "intermediate")

	d.lets = append(d.lets, ast.NewLetVar(s, v))

	return s
}

func (d *desugarer) createMatchFunction(cs []ast.Case) interface{} {
	arg := gensym.GenSym("match", "argument")
	body := d.casesToBody(arg, cs)

	f := ast.NewLetFunction(
		gensym.GenSym("match", "function"),
		ast.NewSignature([]string{arg}, nil, "", nil, nil, ""),
		d.lets,
		body,
		debug.NewGoInfo(0))

	d.lets = []interface{}{f}

	return f.Name()
}

func (d *desugarer) casesToBody(arg string, cs []ast.Case) interface{} {
	cs = renameBoundNamesInCases(cs)
	body := app("error", "MatchError", "\"Failed to match a value with patterns.\"")

	for _, cs := range groupCases(cs) {
		result, ok := d.matchCasesOfSamePatterns(arg, cs)
		body = app("if", ok, result, body)
	}

	return body
}

func renameBoundNamesInCases(cs []ast.Case) []ast.Case {
	panic("Not implemented")
}

func app(f interface{}, args ...interface{}) interface{} {
	return ast.NewPApp(f, args, debug.NewGoInfo(0))
}

func (d *desugarer) matchCasesOfSamePatterns(v string, cs []ast.Case) (interface{}, interface{}) {
	// TODO: Implement this function.

	switch getPatternType(cs[0].Pattern()) {
	case listPattern:
		panic("Not implemented")
	case dictPattern:
		panic("Not implemented")
	case scalarPattern:
		ss := make([]interface{}, 0, 2*len(cs))

		for _, c := range cs {
			ss = append(ss, c.Pattern(), c.Value())
		}

		dict := d.letVar(app("dict", ss...))

		return app(dict, v), app("include", dict, v)
	case namePattern:
		panic("Not implemented")
	}

	panic(fmt.Errorf("Invalid cases: %#v", cs))
}

func matchType(v string, typ string) interface{} {
	return app("=", app("typeOf", v), typ)
}

func groupCases(cs []ast.Case) map[patternType][]ast.Case {
	m := map[patternType][]ast.Case{}

	for _, c := range cs {
		p := getPatternType(c.Pattern())
		m[p] = append(m[p], c)
	}

	return m
}

func getPatternType(p interface{}) patternType {
	switch x := p.(type) {
	case string:
		if scalar.Defined(x) {
			return scalarPattern
		}

		return namePattern
	case ast.App:
		switch x.Function().(string) {
		case "$list":
			return listPattern
		case "$dict":
			return dictPattern
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}
<commit_msg>Add mock of renameBoundNamesInCases<commit_after>package match

import (
	"fmt"

	"github.com/tisp-lang/tisp/src/lib/ast"
	"github.com/tisp-lang/tisp/src/lib/debug"
	"github.com/tisp-lang/tisp/src/lib/gensym"
	"github.com/tisp-lang/tisp/src/lib/scalar"
)

type desugarer struct {
	lets []interface{}
}

func newDesugarer() *desugarer {
	return &desugarer{nil}
}

func (d *desugarer) desugarMatchExpression(x interface{}) interface{} {
	switch x := x.(type) {
	case ast.App:
		return ast.NewApp(
			d.desugarMatchExpression(x.Function()),
			d.desugarMatchExpression(x.Arguments()).(ast.Arguments),
			x.DebugInfo())
	case ast.Arguments:
		ps := make([]ast.PositionalArgument, 0, len(x.Positionals()))

		for _, p := range x.Positionals() {
			ps = append(ps, d.desugarMatchExpression(p).(ast.PositionalArgument))
		}

		ks := make([]ast.KeywordArgument, 0, len(x.Keywords()))

		for _, k := range x.Keywords() {
			ks = append(ks, d.desugarMatchExpression(k).(ast.KeywordArgument))
		}

		dicts := make([]interface{}, 0, len(x.ExpandedDicts()))

		for _, dict := range x.ExpandedDicts() {
			dicts = append(dicts, d.desugarMatchExpression(dict))
		}

		return ast.NewArguments(ps, ks, dicts)
	case ast.KeywordArgument:
		return ast.NewKeywordArgument(x.Name(), d.desugarMatchExpression(x.Value()))
	case ast.LetFunction:
		ls := make([]interface{}, 0, len(x.Lets()))

		for _, l := range x.Lets() {
			l := d.desugarMatchExpression(l)
			ls = append(ls, append(d.lets, l)...)
			d.lets = nil
		}

		return ast.NewLetFunction(
			x.Name(),
			x.Signature(),
			ls,
			d.desugarMatchExpression(x.Body()),
			x.DebugInfo())
	case ast.LetVar:
		return ast.NewLetVar(x.Name(), d.desugarMatchExpression(x.Expr()))
	case ast.Match:
		return app(d.createMatchFunction(x.Cases()), d.desugarMatchExpression(x.Value()))
	case ast.Output:
		return ast.NewOutput(d.desugarMatchExpression(x.Expr()), x.Expanded())
	case ast.PositionalArgument:
		return ast.NewPositionalArgument(d.desugarMatchExpression(x.Value()), x.Expanded())
	default:
		return x
	}
}

func (d *desugarer) letVar(v interface{}) string {
	s := gensym.GenSym("match", "intermediate")

	d.lets = append(d.lets, ast.NewLetVar(s, v))

	return s
}

func (d *desugarer) createMatchFunction(cs []ast.Case) interface{} {
	arg := gensym.GenSym("match", "argument")
	body := d.casesToBody(arg, cs)

	f := ast.NewLetFunction(
		gensym.GenSym("match", "function"),
		ast.NewSignature([]string{arg}, nil, "", nil, nil, ""),
		d.lets,
		body,
		debug.NewGoInfo(0))

	d.lets = []interface{}{f}

	return f.Name()
}

func (d *desugarer) casesToBody(arg string, cs []ast.Case) interface{} {
	cs = renameBoundNamesInCases(cs)
	body := app("error", "MatchError", "\"Failed to match a value with patterns.\"")

	for _, cs := range groupCases(cs) {
		result, ok := d.matchCasesOfSamePatterns(arg, cs)
		body = app("if", ok, result, body)
	}

	return body
}

func renameBoundNamesInCases(cs []ast.Case) []ast.Case {
	// TODO: Implement this function.
	return cs
}

func app(f interface{}, args ...interface{}) interface{} {
	return ast.NewPApp(f, args, debug.NewGoInfo(0))
}

func (d *desugarer) matchCasesOfSamePatterns(v string, cs []ast.Case) (interface{}, interface{}) {
	switch getPatternType(cs[0].Pattern()) {
	case listPattern:
		panic("Not implemented")
	case dictPattern:
		panic("Not implemented")
	case scalarPattern:
		ss := make([]interface{}, 0, 2*len(cs))

		for _, c := range cs {
			ss = append(ss, c.Pattern(), c.Value())
		}

		dict := d.letVar(app("dict", ss...))

		return app(dict, v), app("include", dict, v)
	case namePattern:
		panic("Not implemented")
	}

	panic(fmt.Errorf("Invalid cases: %#v", cs))
}

func matchType(v string, typ string) interface{} {
	return app("=", app("typeOf", v), typ)
}

func groupCases(cs []ast.Case) map[patternType][]ast.Case {
	m := map[patternType][]ast.Case{}

	for _, c := range cs {
		p := getPatternType(c.Pattern())
		m[p] = append(m[p], c)
	}

	return m
}

func getPatternType(p interface{}) patternType {
	switch x := p.(type) {
	case string:
		if scalar.Defined(x) {
			return scalarPattern
		}

		return namePattern
	case ast.App:
		switch x.Function().(string) {
		case "$list":
			return listPattern
		case "$dict":
			return dictPattern
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}
<|endoftext|>


<commit_before>package flooding_nr

type position struct {
	x int
	y int
}

func Flood(terrain *[][]rune, x, y int) {
	size := 0
	for _, line := range *terrain {
		size += len(line)
	}

	c := make(chan position, size)

	c <- position{x, y}

	for len(c) > 0 {
		pos := <-c

		if (*terrain)[pos.y][pos.x] == '.' {
			(*terrain)[pos.y][pos.x] = 'X'
			if validatePos(terrain, pos.x+1, pos.y) {
				c <- position{pos.x + 1, pos.y}
			}
			if validatePos(terrain, pos.x-1, pos.y) {
				c <- position{pos.x - 1, pos.y}
			}
			if validatePos(terrain, pos.x, pos.y-1) {
				c <- position{pos.x, pos.y - 1}
			}
			if validatePos(terrain, pos.x, pos.y+1) {
				c <- position{pos.x, pos.y + 1}
			}

		}
	}
}

func validatePos(terrain *[][]rune, x, y int) bool {
	if y < 0 || y >= len(*terrain) || x < 0 || x >= len((*terrain)[y]) {
		return false
	}
	return (*terrain)[y][x] == '.'
}
<commit_msg>comment<commit_after>package flooding_nr

type position struct {
	x int
	y int
}

func Flood(terrain *[][]rune, x, y int) {
	size := 0
	for _, line := range *terrain {
		size += len(line)
	}

	// TODO: use real FIFO
	// using a channel instead of a FIFO forces me to set the buffer
	// to the maximum possible number of items for it not to hang
	c := make(chan position, size)

	c <- position{x, y}

	for len(c) > 0 {
		pos := <-c

		if (*terrain)[pos.y][pos.x] == '.' {
			(*terrain)[pos.y][pos.x] = 'X'
			if validatePos(terrain, pos.x+1, pos.y) {
				c <- position{pos.x + 1, pos.y}
			}
			if validatePos(terrain, pos.x-1, pos.y) {
				c <- position{pos.x - 1, pos.y}
			}
			if validatePos(terrain, pos.x, pos.y-1) {
				c <- position{pos.x, pos.y - 1}
			}
			if validatePos(terrain, pos.x, pos.y+1) {
				c <- position{pos.x, pos.y + 1}
			}

		}
	}
}

func validatePos(terrain *[][]rune, x, y int) bool {
	if y < 0 || y >= len(*terrain) || x < 0 || x >= len((*terrain)[y]) {
		return false
	}
	return (*terrain)[y][x] == '.'
}
<|endoftext|>


<commit_before>package fritz

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"os"
)

// Config stores client configuration of your FRITZ!Box
type Config struct {
	Protocol string `json:"protocol"`
	Host     string `json:"host"`
	LoginURL string `json:"loginURL"`
	Username string `json:"username"`
	Password string `json:"password"`
}

// FromFile  creates a new Config by reading from a file.
func FromFile(filestr string) (*Config, error) {
	log.Printf("Reading config from '%s'", filestr)
	file, errOpen := os.Open(filestr)
	if errOpen != nil {
		return nil, errors.New("Cannot open configuration file '" + filestr + "'. Nested error is: " + errOpen.Error())
	}
	decoder := json.NewDecoder(file)
	conf := Config{}
	errDecode := decoder.Decode(&conf)
	if errDecode != nil {
		return nil, errors.New("Unable to parse configuration file '" + filestr + "'. Nested error is: " + errDecode.Error())
	}
	return &conf, nil
}

// GetLoginURL returns the URL that is queried for the login challenge
func (config *Config) GetLoginURL() string {
	return fmt.Sprintf("%s://%s%s", config.Protocol, config.Host, config.LoginURL)
}

// GetLoginResponseURL returns the URL that is queried for the login challenge
func (config *Config) GetLoginResponseURL(response string) string {
	return fmt.Sprintf("%s?response=%s&username=%s", config.GetLoginURL(), response, config.Username)
}
<commit_msg>use fluent one-liner for decoding<commit_after>package fritz

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"os"
)

// Config stores client configuration of your FRITZ!Box
type Config struct {
	Protocol string `json:"protocol"`
	Host     string `json:"host"`
	LoginURL string `json:"loginURL"`
	Username string `json:"username"`
	Password string `json:"password"`
}

// FromFile  creates a new Config by reading from a file.
func FromFile(filestr string) (*Config, error) {
	log.Printf("Reading config from '%s'", filestr)
	file, errOpen := os.Open(filestr)
	if errOpen != nil {
		return nil, errors.New("Cannot open configuration file '" + filestr + "'. Nested error is: " + errOpen.Error())
	}
	conf := Config{}
	errDecode := json.NewDecoder(file).Decode(&conf)
	if errDecode != nil {
		return nil, errors.New("Unable to parse configuration file '" + filestr + "'. Nested error is: " + errDecode.Error())
	}
	return &conf, nil
}

// GetLoginURL returns the URL that is queried for the login challenge
func (config *Config) GetLoginURL() string {
	return fmt.Sprintf("%s://%s%s", config.Protocol, config.Host, config.LoginURL)
}

// GetLoginResponseURL returns the URL that is queried for the login challenge
func (config *Config) GetLoginResponseURL(response string) string {
	return fmt.Sprintf("%s?response=%s&username=%s", config.GetLoginURL(), response, config.Username)
}
<|endoftext|>


<commit_before>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2016 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package influxdb

import (
	"context"
	"sync"
	"time"

	"github.com/influxdata/influxdb/client/v2"
	"github.com/loadimpact/k6/lib"
	"github.com/loadimpact/k6/stats"
	log "github.com/sirupsen/logrus"
)

const (
	pushInterval = 1 * time.Second
)

var _ lib.Collector = &Collector{}

type Collector struct {
	Client    client.Client
	Config    Config
	BatchConf client.BatchPointsConfig

	buffer     []stats.Sample
	bufferLock sync.Mutex
}

func New(conf Config) (*Collector, error) {
	cl, err := MakeClient(conf)
	if err != nil {
		return nil, err
	}
	batchConf := MakeBatchConfig(conf)
	return &Collector{
		Client:    cl,
		Config:    conf,
		BatchConf: batchConf,
	}, nil
}

func (c *Collector) Init() error {
	// Try to create the database if it doesn't exist. Failure to do so is USUALLY harmless; it
	// usually means we're either a non-admin user to an existing DB or connecting over UDP.
	_, err := c.Client.Query(client.NewQuery("CREATE DATABASE "+c.BatchConf.Database, "", ""))
	if err != nil {
		log.WithError(err).Debug("InfluxDB: Couldn't create database; most likely harmless")
	}

	return nil
}

func (c *Collector) Run(ctx context.Context) {
	log.Debug("InfluxDB: Running!")
	ticker := time.NewTicker(pushInterval)
	for {
		select {
		case <-ticker.C:
			c.commit()
		case <-ctx.Done():
			c.commit()
			return
		}
	}
}

func (c *Collector) Collect(samples []stats.Sample) {
	c.bufferLock.Lock()
	c.buffer = append(c.buffer, samples...)
	c.bufferLock.Unlock()
}

func (c *Collector) Link() string {
	return c.Config.Addr
}

func (c *Collector) commit() {
	c.bufferLock.Lock()
	samples := c.buffer
	c.buffer = nil
	c.bufferLock.Unlock()

	log.Debug("InfluxDB: Committing...")
	batch, err := client.NewBatchPoints(c.BatchConf)
	if err != nil {
		log.WithError(err).Error("InfluxDB: Couldn't make a batch")
		return
	}

	for _, sample := range samples {
		tags := sample.Tags.CloneTags() //TODO: optimize when implementing https://github.com/loadimpact/k6/issues/569
		values := c.extractFields(tags, sample.Value)
		p, err := client.NewPoint(
			sample.Metric.Name,
			tags,
			values,
			sample.Time,
		)
		if err != nil {
			log.WithError(err).Error("InfluxDB: Couldn't make point from sample!")
			return
		}
		batch.AddPoint(p)
	}

	log.WithField("points", len(batch.Points())).Debug("InfluxDB: Writing...")
	startTime := time.Now()
	if err := c.Client.Write(batch); err != nil {
		log.WithError(err).Error("InfluxDB: Couldn't write stats")
	}
	t := time.Since(startTime)
	log.WithField("t", t).Debug("InfluxDB: Batch written!")
}

func (c *Collector) extractFields(tags map[string]string, value interface{}) map[string]interface{} {
	fields := make(map[string]interface{})
	fields["value"] = value
	for _, tag := range c.Config.TagsAsFields {
		if val, ok := tags[tag]; ok {
			fields[tag] = val
			delete(tags, tag)
		}
	}
	return fields
}

// GetRequiredSystemTags returns which sample tags are needed by this collector
func (c *Collector) GetRequiredSystemTags() lib.TagSet {
	return lib.TagSet{} // There are no required tags for this collector
}
<commit_msg>Optimize the InfluxDB commit function by caching tags and values.<commit_after>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2016 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package influxdb

import (
	"context"
	"sync"
	"time"

	"github.com/influxdata/influxdb/client/v2"
	"github.com/loadimpact/k6/lib"
	"github.com/loadimpact/k6/stats"
	log "github.com/sirupsen/logrus"
)

const (
	pushInterval = 1 * time.Second
)

var _ lib.Collector = &Collector{}

type Collector struct {
	Client    client.Client
	Config    Config
	BatchConf client.BatchPointsConfig

	buffer     []stats.Sample
	bufferLock sync.Mutex
}

func New(conf Config) (*Collector, error) {
	cl, err := MakeClient(conf)
	if err != nil {
		return nil, err
	}
	batchConf := MakeBatchConfig(conf)
	return &Collector{
		Client:    cl,
		Config:    conf,
		BatchConf: batchConf,
	}, nil
}

func (c *Collector) Init() error {
	// Try to create the database if it doesn't exist. Failure to do so is USUALLY harmless; it
	// usually means we're either a non-admin user to an existing DB or connecting over UDP.
	_, err := c.Client.Query(client.NewQuery("CREATE DATABASE "+c.BatchConf.Database, "", ""))
	if err != nil {
		log.WithError(err).Debug("InfluxDB: Couldn't create database; most likely harmless")
	}

	return nil
}

func (c *Collector) Run(ctx context.Context) {
	log.Debug("InfluxDB: Running!")
	ticker := time.NewTicker(pushInterval)
	for {
		select {
		case <-ticker.C:
			c.commit()
		case <-ctx.Done():
			c.commit()
			return
		}
	}
}

func (c *Collector) Collect(samples []stats.Sample) {
	c.bufferLock.Lock()
	c.buffer = append(c.buffer, samples...)
	c.bufferLock.Unlock()
}

func (c *Collector) Link() string {
	return c.Config.Addr
}

func (c *Collector) commit() {
	c.bufferLock.Lock()
	samples := c.buffer
	c.buffer = nil
	c.bufferLock.Unlock()

	log.Debug("InfluxDB: Committing...")
	batch, err := client.NewBatchPoints(c.BatchConf)
	if err != nil {
		log.WithError(err).Error("InfluxDB: Couldn't make a batch")
		return
	}

	cache := map[*stats.SampleTags]struct {
		tags   map[string]string
		values map[string]interface{}
	}{}

	var tags map[string]string
	var values map[string]interface{}
	for _, sample := range samples {
		if cached, ok := cache[sample.Tags]; ok {
			tags = cached.tags
			values = cached.values
			values["value"] = sample.Value // Overwrite the "value" field
		} else {
			tags = sample.Tags.CloneTags()
			values = c.extractFields(tags)
			values["value"] = sample.Value // Ok since the "value" field will always be overwritten
			cache[sample.Tags] = struct {
				tags   map[string]string
				values map[string]interface{}
			}{sample.Tags.CloneTags(), values}
		}
		p, err := client.NewPoint(
			sample.Metric.Name,
			tags,
			values,
			sample.Time,
		)
		if err != nil {
			log.WithError(err).Error("InfluxDB: Couldn't make point from sample!")
			return
		}
		batch.AddPoint(p)
	}

	log.WithField("points", len(batch.Points())).Debug("InfluxDB: Writing...")
	startTime := time.Now()
	if err := c.Client.Write(batch); err != nil {
		log.WithError(err).Error("InfluxDB: Couldn't write stats")
	}
	t := time.Since(startTime)
	log.WithField("t", t).Debug("InfluxDB: Batch written!")
}

func (c *Collector) extractFields(tags map[string]string) map[string]interface{} {
	fields := make(map[string]interface{})
	for _, tag := range c.Config.TagsAsFields {
		if val, ok := tags[tag]; ok {
			fields[tag] = val
			delete(tags, tag)
		}
	}
	return fields
}

// GetRequiredSystemTags returns which sample tags are needed by this collector
func (c *Collector) GetRequiredSystemTags() lib.TagSet {
	return lib.TagSet{} // There are no required tags for this collector
}
<|endoftext|>