<commit_before>package azurerm

import (
	"fmt"
	"log"
	"time"

	"github.com/Azure/azure-sdk-for-go/arm/network"
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/jen20/riviera/azure"
)

func resourceArmLoadBalancer() *schema.Resource {
	return &schema.Resource{
		Create: resourceArmLoadBalancerCreate,
		Read:   resourecArmLoadBalancerRead,
		Update: resourceArmLoadBalancerCreate,
		Delete: resourceArmLoadBalancerDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"location": locationSchema(),

			"resource_group_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"frontend_ip_configuration": {
				Type:     schema.TypeList,
				Optional: true,
				MinItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"name": {
							Type:     schema.TypeString,
							Required: true,
						},

						"subnet_id": {
							Type:     schema.TypeString,
							Optional: true,
							Computed: true,
						},

						"private_ip_address": {
							Type:     schema.TypeString,
							Optional: true,
							Computed: true,
						},

						"public_ip_address_id": {
							Type:     schema.TypeString,
							Optional: true,
							Computed: true,
						},

						"private_ip_address_allocation": {
							Type:             schema.TypeString,
							Optional:         true,
							Computed:         true,
							ValidateFunc:     validateLoadBalancerPrivateIpAddressAllocation,
							StateFunc:        ignoreCaseStateFunc,
							DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
						},

						"load_balancer_rules": {
							Type:     schema.TypeSet,
							Computed: true,
							Elem:     &schema.Schema{Type: schema.TypeString},
							Set:      schema.HashString,
						},

						"inbound_nat_rules": {
							Type:     schema.TypeSet,
							Computed: true,
							Elem:     &schema.Schema{Type: schema.TypeString},
							Set:      schema.HashString,
						},
					},
				},
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceArmLoadBalancerCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient)
	loadBalancerClient := client.loadBalancerClient

	log.Printf("[INFO] preparing arguments for Azure ARM LoadBalancer creation.")

	name := d.Get("name").(string)
	location := d.Get("location").(string)
	resGroup := d.Get("resource_group_name").(string)
	tags := d.Get("tags").(map[string]interface{})
	expandedTags := expandTags(tags)

	properties := network.LoadBalancerPropertiesFormat{}

	if _, ok := d.GetOk("frontend_ip_configuration"); ok {
		properties.FrontendIPConfigurations = expandAzureRmLoadBalancerFrontendIpConfigurations(d)
	}

	loadbalancer := network.LoadBalancer{
		Name:     azure.String(name),
		Location: azure.String(location),
		Tags:     expandedTags,
		LoadBalancerPropertiesFormat: &properties,
	}

	_, err := loadBalancerClient.CreateOrUpdate(resGroup, name, loadbalancer, make(chan struct{}))
	if err != nil {
		return errwrap.Wrapf("Error Creating/Updating LoadBalancer {{err}}", err)
	}

	read, err := loadBalancerClient.Get(resGroup, name, "")
	if err != nil {
		return errwrap.Wrapf("Error Getting LoadBalancer {{err}", err)
	}
	if read.ID == nil {
		return fmt.Errorf("Cannot read LoadBalancer %s (resource group %s) ID", name, resGroup)
	}

	d.SetId(*read.ID)

	log.Printf("[DEBUG] Waiting for LoadBalancer (%s) to become available", name)
	stateConf := &resource.StateChangeConf{
		Pending: []string{"Accepted", "Updating"},
		Target:  []string{"Succeeded"},
		Refresh: loadbalancerStateRefreshFunc(client, resGroup, name),
		Timeout: 10 * time.Minute,
	}
	if _, err := stateConf.WaitForState(); err != nil {
		return fmt.Errorf("Error waiting for LoadBalancer (%s) to become available: %s", name, err)
	}

	return resourecArmLoadBalancerRead(d, meta)
}

func resourecArmLoadBalancerRead(d *schema.ResourceData, meta interface{}) error {
	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}

	loadBalancer, exists, err := retrieveLoadBalancerById(d.Id(), meta)
	if err != nil {
		return errwrap.Wrapf("Error Getting LoadBalancer By ID {{err}}", err)
	}
	if !exists {
		d.SetId("")
		log.Printf("[INFO] LoadBalancer %q not found. Removing from state", d.Get("name").(string))
		return nil
	}

	d.Set("name", loadBalancer.Name)
	d.Set("location", loadBalancer.Location)
	d.Set("resource_group_name", id.ResourceGroup)

	if loadBalancer.LoadBalancerPropertiesFormat != nil && loadBalancer.LoadBalancerPropertiesFormat.FrontendIPConfigurations != nil {
		d.Set("frontend_ip_configuration", flattenLoadBalancerFrontendIpConfiguration(loadBalancer.LoadBalancerPropertiesFormat.FrontendIPConfigurations))
	}

	flattenAndSetTags(d, loadBalancer.Tags)

	return nil
}

func resourceArmLoadBalancerDelete(d *schema.ResourceData, meta interface{}) error {
	loadBalancerClient := meta.(*ArmClient).loadBalancerClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return errwrap.Wrapf("Error Parsing Azure Resource ID {{err}}", err)
	}
	resGroup := id.ResourceGroup
	name := id.Path["loadBalancers"]

	_, err = loadBalancerClient.Delete(resGroup, name, make(chan struct{}))
	if err != nil {
		return errwrap.Wrapf("Error Deleting LoadBalancer {{err}}", err)
	}

	d.SetId("")
	return nil
}

func expandAzureRmLoadBalancerFrontendIpConfigurations(d *schema.ResourceData) *[]network.FrontendIPConfiguration {
	configs := d.Get("frontend_ip_configuration").([]interface{})
	frontEndConfigs := make([]network.FrontendIPConfiguration, 0, len(configs))

	for _, configRaw := range configs {
		data := configRaw.(map[string]interface{})

		private_ip_allocation_method := data["private_ip_address_allocation"].(string)
		properties := network.FrontendIPConfigurationPropertiesFormat{
			PrivateIPAllocationMethod: network.IPAllocationMethod(private_ip_allocation_method),
		}

		if v := data["private_ip_address"].(string); v != "" {
			properties.PrivateIPAddress = &v
		}

		if v := data["public_ip_address_id"].(string); v != "" {
			properties.PublicIPAddress = &network.PublicIPAddress{
				ID: &v,
			}
		}

		if v := data["subnet_id"].(string); v != "" {
			properties.Subnet = &network.Subnet{
				ID: &v,
			}
		}

		name := data["name"].(string)
		frontEndConfig := network.FrontendIPConfiguration{
			Name: &name,
			FrontendIPConfigurationPropertiesFormat: &properties,
		}

		frontEndConfigs = append(frontEndConfigs, frontEndConfig)
	}

	return &frontEndConfigs
}

func flattenLoadBalancerFrontendIpConfiguration(ipConfigs *[]network.FrontendIPConfiguration) []interface{} {
	result := make([]interface{}, 0, len(*ipConfigs))
	for _, config := range *ipConfigs {
		ipConfig := make(map[string]interface{})
		ipConfig["name"] = *config.Name
		ipConfig["private_ip_address_allocation"] = config.FrontendIPConfigurationPropertiesFormat.PrivateIPAllocationMethod

		if config.FrontendIPConfigurationPropertiesFormat.Subnet != nil {
			ipConfig["subnet_id"] = *config.FrontendIPConfigurationPropertiesFormat.Subnet.ID
		}

		if config.FrontendIPConfigurationPropertiesFormat.PrivateIPAddress != nil {
			ipConfig["private_ip_address"] = *config.FrontendIPConfigurationPropertiesFormat.PrivateIPAddress
		}

		if config.FrontendIPConfigurationPropertiesFormat.PublicIPAddress != nil {
			ipConfig["public_ip_address_id"] = *config.FrontendIPConfigurationPropertiesFormat.PublicIPAddress.ID
		}

		if config.FrontendIPConfigurationPropertiesFormat.LoadBalancingRules != nil {
			load_balancing_rules := make([]string, 0, len(*config.FrontendIPConfigurationPropertiesFormat.LoadBalancingRules))
			for _, rule := range *config.FrontendIPConfigurationPropertiesFormat.LoadBalancingRules {
				load_balancing_rules = append(load_balancing_rules, *rule.ID)
			}

			ipConfig["load_balancer_rules"] = load_balancing_rules

		}

		if config.FrontendIPConfigurationPropertiesFormat.InboundNatRules != nil {
			inbound_nat_rules := make([]string, 0, len(*config.FrontendIPConfigurationPropertiesFormat.InboundNatRules))
			for _, rule := range *config.FrontendIPConfigurationPropertiesFormat.InboundNatRules {
				inbound_nat_rules = append(inbound_nat_rules, *rule.ID)
			}

			ipConfig["inbound_nat_rules"] = inbound_nat_rules

		}

		result = append(result, ipConfig)
	}
	return result
}
<commit_msg>Expose any dynamically-assigned private IP address<commit_after>package azurerm

import (
	"fmt"
	"log"
	"time"

	"github.com/Azure/azure-sdk-for-go/arm/network"
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/jen20/riviera/azure"
)

func resourceArmLoadBalancer() *schema.Resource {
	return &schema.Resource{
		Create: resourceArmLoadBalancerCreate,
		Read:   resourecArmLoadBalancerRead,
		Update: resourceArmLoadBalancerCreate,
		Delete: resourceArmLoadBalancerDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"location": locationSchema(),

			"resource_group_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"frontend_ip_configuration": {
				Type:     schema.TypeList,
				Optional: true,
				MinItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"name": {
							Type:     schema.TypeString,
							Required: true,
						},

						"subnet_id": {
							Type:     schema.TypeString,
							Optional: true,
							Computed: true,
						},

						"private_ip_address": {
							Type:     schema.TypeString,
							Optional: true,
							Computed: true,
						},

						"public_ip_address_id": {
							Type:     schema.TypeString,
							Optional: true,
							Computed: true,
						},

						"private_ip_address_allocation": {
							Type:             schema.TypeString,
							Optional:         true,
							Computed:         true,
							ValidateFunc:     validateLoadBalancerPrivateIpAddressAllocation,
							StateFunc:        ignoreCaseStateFunc,
							DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
						},

						"load_balancer_rules": {
							Type:     schema.TypeSet,
							Computed: true,
							Elem:     &schema.Schema{Type: schema.TypeString},
							Set:      schema.HashString,
						},

						"inbound_nat_rules": {
							Type:     schema.TypeSet,
							Computed: true,
							Elem:     &schema.Schema{Type: schema.TypeString},
							Set:      schema.HashString,
						},
					},
				},
			},

			"private_ip_address": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceArmLoadBalancerCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient)
	loadBalancerClient := client.loadBalancerClient

	log.Printf("[INFO] preparing arguments for Azure ARM LoadBalancer creation.")

	name := d.Get("name").(string)
	location := d.Get("location").(string)
	resGroup := d.Get("resource_group_name").(string)
	tags := d.Get("tags").(map[string]interface{})
	expandedTags := expandTags(tags)

	properties := network.LoadBalancerPropertiesFormat{}

	if _, ok := d.GetOk("frontend_ip_configuration"); ok {
		properties.FrontendIPConfigurations = expandAzureRmLoadBalancerFrontendIpConfigurations(d)
	}

	loadbalancer := network.LoadBalancer{
		Name:     azure.String(name),
		Location: azure.String(location),
		Tags:     expandedTags,
		LoadBalancerPropertiesFormat: &properties,
	}

	_, err := loadBalancerClient.CreateOrUpdate(resGroup, name, loadbalancer, make(chan struct{}))
	if err != nil {
		return errwrap.Wrapf("Error Creating/Updating LoadBalancer {{err}}", err)
	}

	read, err := loadBalancerClient.Get(resGroup, name, "")
	if err != nil {
		return errwrap.Wrapf("Error Getting LoadBalancer {{err}", err)
	}
	if read.ID == nil {
		return fmt.Errorf("Cannot read LoadBalancer %s (resource group %s) ID", name, resGroup)
	}

	d.SetId(*read.ID)

	log.Printf("[DEBUG] Waiting for LoadBalancer (%s) to become available", name)
	stateConf := &resource.StateChangeConf{
		Pending: []string{"Accepted", "Updating"},
		Target:  []string{"Succeeded"},
		Refresh: loadbalancerStateRefreshFunc(client, resGroup, name),
		Timeout: 10 * time.Minute,
	}
	if _, err := stateConf.WaitForState(); err != nil {
		return fmt.Errorf("Error waiting for LoadBalancer (%s) to become available: %s", name, err)
	}

	return resourecArmLoadBalancerRead(d, meta)
}

func resourecArmLoadBalancerRead(d *schema.ResourceData, meta interface{}) error {
	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}

	loadBalancer, exists, err := retrieveLoadBalancerById(d.Id(), meta)
	if err != nil {
		return errwrap.Wrapf("Error Getting LoadBalancer By ID {{err}}", err)
	}
	if !exists {
		d.SetId("")
		log.Printf("[INFO] LoadBalancer %q not found. Removing from state", d.Get("name").(string))
		return nil
	}

	d.Set("name", loadBalancer.Name)
	d.Set("location", loadBalancer.Location)
	d.Set("resource_group_name", id.ResourceGroup)

	if loadBalancer.LoadBalancerPropertiesFormat != nil && loadBalancer.LoadBalancerPropertiesFormat.FrontendIPConfigurations != nil {
		ipconfig := flattenLoadBalancerFrontendIpConfiguration(loadBalancer.LoadBalancerPropertiesFormat.FrontendIPConfigurations)
		d.Set("frontend_ip_configuration", ipconfig)

		for _, config := range ipconfig {
			cfg := config.(map[string]interface{})
			if priv_ip, ok := cfg["private_ip_address"]; ok {
				d.Set("private_ip_address", priv_ip)

				// set the private IP address at most once
				break
			}
		}
	}

	flattenAndSetTags(d, loadBalancer.Tags)

	return nil
}

func resourceArmLoadBalancerDelete(d *schema.ResourceData, meta interface{}) error {
	loadBalancerClient := meta.(*ArmClient).loadBalancerClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return errwrap.Wrapf("Error Parsing Azure Resource ID {{err}}", err)
	}
	resGroup := id.ResourceGroup
	name := id.Path["loadBalancers"]

	_, err = loadBalancerClient.Delete(resGroup, name, make(chan struct{}))
	if err != nil {
		return errwrap.Wrapf("Error Deleting LoadBalancer {{err}}", err)
	}

	d.SetId("")
	return nil
}

func expandAzureRmLoadBalancerFrontendIpConfigurations(d *schema.ResourceData) *[]network.FrontendIPConfiguration {
	configs := d.Get("frontend_ip_configuration").([]interface{})
	frontEndConfigs := make([]network.FrontendIPConfiguration, 0, len(configs))

	for _, configRaw := range configs {
		data := configRaw.(map[string]interface{})

		private_ip_allocation_method := data["private_ip_address_allocation"].(string)
		properties := network.FrontendIPConfigurationPropertiesFormat{
			PrivateIPAllocationMethod: network.IPAllocationMethod(private_ip_allocation_method),
		}

		if v := data["private_ip_address"].(string); v != "" {
			properties.PrivateIPAddress = &v
		}

		if v := data["public_ip_address_id"].(string); v != "" {
			properties.PublicIPAddress = &network.PublicIPAddress{
				ID: &v,
			}
		}

		if v := data["subnet_id"].(string); v != "" {
			properties.Subnet = &network.Subnet{
				ID: &v,
			}
		}

		name := data["name"].(string)
		frontEndConfig := network.FrontendIPConfiguration{
			Name: &name,
			FrontendIPConfigurationPropertiesFormat: &properties,
		}

		frontEndConfigs = append(frontEndConfigs, frontEndConfig)
	}

	return &frontEndConfigs
}

func flattenLoadBalancerFrontendIpConfiguration(ipConfigs *[]network.FrontendIPConfiguration) []interface{} {
	result := make([]interface{}, 0, len(*ipConfigs))
	for _, config := range *ipConfigs {
		ipConfig := make(map[string]interface{})
		ipConfig["name"] = *config.Name
		ipConfig["private_ip_address_allocation"] = config.FrontendIPConfigurationPropertiesFormat.PrivateIPAllocationMethod

		if config.FrontendIPConfigurationPropertiesFormat.Subnet != nil {
			ipConfig["subnet_id"] = *config.FrontendIPConfigurationPropertiesFormat.Subnet.ID
		}

		if config.FrontendIPConfigurationPropertiesFormat.PrivateIPAddress != nil {
			ipConfig["private_ip_address"] = *config.FrontendIPConfigurationPropertiesFormat.PrivateIPAddress
		}

		if config.FrontendIPConfigurationPropertiesFormat.PublicIPAddress != nil {
			ipConfig["public_ip_address_id"] = *config.FrontendIPConfigurationPropertiesFormat.PublicIPAddress.ID
		}

		if config.FrontendIPConfigurationPropertiesFormat.LoadBalancingRules != nil {
			load_balancing_rules := make([]string, 0, len(*config.FrontendIPConfigurationPropertiesFormat.LoadBalancingRules))
			for _, rule := range *config.FrontendIPConfigurationPropertiesFormat.LoadBalancingRules {
				load_balancing_rules = append(load_balancing_rules, *rule.ID)
			}

			ipConfig["load_balancer_rules"] = load_balancing_rules

		}

		if config.FrontendIPConfigurationPropertiesFormat.InboundNatRules != nil {
			inbound_nat_rules := make([]string, 0, len(*config.FrontendIPConfigurationPropertiesFormat.InboundNatRules))
			for _, rule := range *config.FrontendIPConfigurationPropertiesFormat.InboundNatRules {
				inbound_nat_rules = append(inbound_nat_rules, *rule.ID)
			}

			ipConfig["inbound_nat_rules"] = inbound_nat_rules

		}

		result = append(result, ipConfig)
	}
	return result
}
<|endoftext|>


<commit_before>package sandra

import (
	"errors"
	"fmt"
	"time"

	"github.com/gocql/gocql"
	"github.com/mailgun/log"
)

type Cassandra interface {
	ExecuteQuery(string, ...interface{}) error
	ExecuteBatch(gocql.BatchType, []string, [][]interface{}) error
	ExecuteUnloggedBatch([]string, [][]interface{}) error
	ScanQuery(string, []interface{}, ...interface{}) error
	ScanCASQuery(string, []interface{}, ...interface{}) (bool, error)
	IterQuery(string, []interface{}, ...interface{}) func() (int, bool, error)
	Close() error
}

type cassandra struct {
	session *gocql.Session
	config  CassandraConfig
}

// CassandraConfig is a json and yaml friendly configuration struct
type CassandraConfig struct {
	Nodes       []string      // addresses for the initial connections
	Timeout     time.Duration // connection timeout (default: 600ms)
	Keyspace    string        // initial keyspace (optional)
	NumConns    int           // number of connections per host (default: 2)
	NumStreams  int           // number of streams per connection (default: 128)
	Consistency string        // consistency to use, default quorum
	Port        int           // port to connect to, default: 9042
	KeepAlive   string        // The keepalive period to use default: 0

	// TestMode affects whether a keyspace creation will be attempted on Cassandra initialization.
	TestMode bool `config:"optional"`
}

func (c CassandraConfig) String() string {
	return fmt.Sprintf("CassandraConfig(Nodes=%v, Keyspace=%v, Consistency=%v, TestMode=%v)",
		c.Nodes, c.Keyspace, c.Consistency, c.TestMode)
}

var NotFound = errors.New("Not found")

func NewCassandra(config CassandraConfig) (Cassandra, error) {
	log.Infof("Connecting to Cassandra with config: %v", config)

	cluster, err := setDefaults(config)
	if err != nil {
		return nil, err
	}

	// in test mode, create a keyspace if necessary
	if config.TestMode == true {
		session, err := cluster.CreateSession()
		if err != nil {
			return nil, err
		}

		query := session.Query(
			fmt.Sprintf(
				`create keyspace if not exists %v
                   with replication = {'class': 'SimpleStrategy', 'replication_factor': 1}`,
				config.Keyspace))

		if err := query.Exec(); err != nil {
			log.Errorf("Error creating keyspace: %v", err)
		}

		session.Close()
	}

	// switch the keyspace
	cluster.Keyspace = config.Keyspace

	session, err := cluster.CreateSession()
	if err != nil {
		return nil, err
	}

	return &cassandra{session: session}, nil
}

func (c *cassandra) Close() error {
	c.session.Close()
	return nil
}

func (c *cassandra) ExecuteQuery(queryString string, queryParams ...interface{}) error {
	return c.session.Query(queryString, queryParams...).Exec()
}

func (c *cassandra) ExecuteBatch(batchType gocql.BatchType, queries []string, params [][]interface{}) error {
	count := len(queries)

	// quick sanity check
	if count != len(params) {
		return errors.New("Amount of queries and params does not match")
	}

	batch := gocql.NewBatch(batchType)
	for idx := 0; idx < count; idx++ {
		batch.Query(queries[idx], params[idx]...)
	}

	return c.session.ExecuteBatch(batch)
}

func (c *cassandra) ExecuteUnloggedBatch(queries []string, params [][]interface{}) error {
	return c.ExecuteBatch(gocql.UnloggedBatch, queries, params)
}

func (c *cassandra) ScanQuery(queryString string, queryParams []interface{}, outParams ...interface{}) error {
	if err := c.session.Query(queryString, queryParams...).Scan(outParams...); err != nil {
		if err == gocql.ErrNotFound {
			return NotFound
		}
		return err
	}
	return nil
}

// Execute a lightweight transaction (an UPDATE or INSERT statement containing an IF clause)
func (c *cassandra) ScanCASQuery(queryString string, queryParams []interface{}, outParams ...interface{}) (bool, error) {
	return c.session.Query(queryString, queryParams...).ScanCAS(outParams...)
}

func (c *cassandra) IterQuery(queryString string, queryParams []interface{}, outParams ...interface{}) func() (int, bool, error) {
	iter := c.session.Query(queryString, queryParams...).Iter()
	idx := -1
	return func() (int, bool, error) {
		idx++
		if iter.Scan(outParams...) {
			return idx, true, nil
		}
		if err := iter.Close(); err != nil {
			return idx, true, err
		}
		return idx, false, nil
	}
}

// Return appropriate gocql.Consistency based on the provided consistency level name.
func translateConsistency(consistencyName string) (gocql.Consistency, error) {
	for index, name := range gocql.ConsistencyNames {
		if name == consistencyName {
			return gocql.Consistency(index), nil
		}
	}
	return gocql.One, fmt.Errorf("unknown consistency: %v", consistencyName)
}

func translateKeepAlive(k string) (time.Duration, error) {
	if k == "" {
		return 0, nil
	}
	return time.ParseDuration(k)
}

func setDefaults(cfg CassandraConfig) (*gocql.ClusterConfig, error) {
	// convert consistency name into appropriate gocql.Consistency value
	consistency, err := translateConsistency(cfg.Consistency)
	if err != nil {
		return nil, err
	}

	keepAlive, err := translateKeepAlive(cfg.KeepAlive)
	if err != nil {
		return nil, err
	}

	if cfg.Timeout == 0 {
		cfg.Timeout = 600 * time.Millisecond
	}
	if cfg.Port == 0 {
		cfg.Port = 9042
	}
	if cfg.NumConns == 0 {
		cfg.NumConns = 2
	}
	if cfg.NumStreams == 0 {
		cfg.NumStreams = 128
	}
	cluster := gocql.NewCluster(cfg.Nodes...)
	cluster.ProtoVersion = 2
	cluster.CQLVersion = "3.0.0"
	cluster.Timeout = cfg.Timeout
	cluster.NumConns = cfg.NumConns
	cluster.Consistency = consistency
	cluster.SocketKeepalive = keepAlive
	return cluster, nil
}
<commit_msg>Timeout to string<commit_after>package sandra

import (
	"errors"
	"fmt"
	"time"

	"github.com/gocql/gocql"
	"github.com/mailgun/log"
)

type Cassandra interface {
	ExecuteQuery(string, ...interface{}) error
	ExecuteBatch(gocql.BatchType, []string, [][]interface{}) error
	ExecuteUnloggedBatch([]string, [][]interface{}) error
	ScanQuery(string, []interface{}, ...interface{}) error
	ScanCASQuery(string, []interface{}, ...interface{}) (bool, error)
	IterQuery(string, []interface{}, ...interface{}) func() (int, bool, error)
	Close() error
}

type cassandra struct {
	session *gocql.Session
	config  CassandraConfig
}

// CassandraConfig is a json and yaml friendly configuration struct
type CassandraConfig struct {
	Nodes       []string // addresses for the initial connections
	Timeout     string   // connection timeout (default: 600ms)
	Keyspace    string   // initial keyspace (optional)
	NumConns    int      // number of connections per host (default: 2)
	NumStreams  int      // number of streams per connection (default: 128)
	Consistency string   // consistency to use, default quorum
	Port        int      // port to connect to, default: 9042
	KeepAlive   string   // The keepalive period to use default: 0

	// TestMode affects whether a keyspace creation will be attempted on Cassandra initialization.
	TestMode bool `config:"optional"`
}

func (c CassandraConfig) String() string {
	return fmt.Sprintf("CassandraConfig(Nodes=%v, Keyspace=%v, Consistency=%v, TestMode=%v)",
		c.Nodes, c.Keyspace, c.Consistency, c.TestMode)
}

var NotFound = errors.New("Not found")

func NewCassandra(config CassandraConfig) (Cassandra, error) {
	log.Infof("Connecting to Cassandra with config: %v", config)

	cluster, err := setDefaults(config)
	if err != nil {
		return nil, err
	}

	// in test mode, create a keyspace if necessary
	if config.TestMode == true {
		session, err := cluster.CreateSession()
		if err != nil {
			return nil, err
		}

		query := session.Query(
			fmt.Sprintf(
				`create keyspace if not exists %v
                   with replication = {'class': 'SimpleStrategy', 'replication_factor': 1}`,
				config.Keyspace))

		if err := query.Exec(); err != nil {
			log.Errorf("Error creating keyspace: %v", err)
		}

		session.Close()
	}

	// switch the keyspace
	cluster.Keyspace = config.Keyspace

	session, err := cluster.CreateSession()
	if err != nil {
		return nil, err
	}

	return &cassandra{session: session}, nil
}

func (c *cassandra) Close() error {
	c.session.Close()
	return nil
}

func (c *cassandra) ExecuteQuery(queryString string, queryParams ...interface{}) error {
	return c.session.Query(queryString, queryParams...).Exec()
}

func (c *cassandra) ExecuteBatch(batchType gocql.BatchType, queries []string, params [][]interface{}) error {
	count := len(queries)

	// quick sanity check
	if count != len(params) {
		return errors.New("Amount of queries and params does not match")
	}

	batch := gocql.NewBatch(batchType)
	for idx := 0; idx < count; idx++ {
		batch.Query(queries[idx], params[idx]...)
	}

	return c.session.ExecuteBatch(batch)
}

func (c *cassandra) ExecuteUnloggedBatch(queries []string, params [][]interface{}) error {
	return c.ExecuteBatch(gocql.UnloggedBatch, queries, params)
}

func (c *cassandra) ScanQuery(queryString string, queryParams []interface{}, outParams ...interface{}) error {
	if err := c.session.Query(queryString, queryParams...).Scan(outParams...); err != nil {
		if err == gocql.ErrNotFound {
			return NotFound
		}
		return err
	}
	return nil
}

// Execute a lightweight transaction (an UPDATE or INSERT statement containing an IF clause)
func (c *cassandra) ScanCASQuery(queryString string, queryParams []interface{}, outParams ...interface{}) (bool, error) {
	return c.session.Query(queryString, queryParams...).ScanCAS(outParams...)
}

func (c *cassandra) IterQuery(queryString string, queryParams []interface{}, outParams ...interface{}) func() (int, bool, error) {
	iter := c.session.Query(queryString, queryParams...).Iter()
	idx := -1
	return func() (int, bool, error) {
		idx++
		if iter.Scan(outParams...) {
			return idx, true, nil
		}
		if err := iter.Close(); err != nil {
			return idx, true, err
		}
		return idx, false, nil
	}
}

// Return appropriate gocql.Consistency based on the provided consistency level name.
func translateConsistency(consistencyName string) (gocql.Consistency, error) {
	for index, name := range gocql.ConsistencyNames {
		if name == consistencyName {
			return gocql.Consistency(index), nil
		}
	}
	return gocql.One, fmt.Errorf("unknown consistency: %v", consistencyName)
}

func translateDuration(k string, df time.Duration) (time.Duration, error) {
	if k == "" {
		return df, nil
	}
	return time.ParseDuration(k)
}

func setDefaults(cfg CassandraConfig) (*gocql.ClusterConfig, error) {
	// convert consistency name into appropriate gocql.Consistency value
	consistency, err := translateConsistency(cfg.Consistency)
	if err != nil {
		return nil, err
	}

	keepAlive, err := translateDuration(cfg.KeepAlive, 0)
	if err != nil {
		return nil, err
	}
	timeout, err := translateDuration(cfg.Timeout, 600*time.Millisecond)
	if err != nil {
		return nil, err
	}
	if cfg.Port == 0 {
		cfg.Port = 9042
	}
	if cfg.NumConns == 0 {
		cfg.NumConns = 2
	}
	if cfg.NumStreams == 0 {
		cfg.NumStreams = 128
	}
	cluster := gocql.NewCluster(cfg.Nodes...)
	cluster.ProtoVersion = 2
	cluster.CQLVersion = "3.0.0"
	cluster.Timeout = timeout
	cluster.NumConns = cfg.NumConns
	cluster.Consistency = consistency
	cluster.SocketKeepalive = keepAlive
	return cluster, nil
}
<|endoftext|>


<commit_before>package report

import (
	"bytes"
	"os"
	"runtime"
	"text/template"

	"github.com/git-time-metric/gtm/note"
	"github.com/git-time-metric/gtm/util"
	isatty "github.com/mattn/go-isatty"
)

var funcMap = template.FuncMap{
	"FormatDuration": util.FormatDuration,
	"RightPad2Len":   util.RightPad2Len,
	"LeftPad2Len":    util.LeftPad2Len,
	"Percent":        util.Percent,
}

type ProjectCommits struct {
	Path    string
	Commits []string
}

const (
	commitsTpl string = `
{{ $headerFormat := .HeaderFormat }}
{{- range $note := .Notes }}
	{{- $total := .Note.Total }}
	{{- printf $headerFormat $note.Hash }} {{ printf $headerFormat $note.Subject }}{{- printf "\n" }}
	{{- $note.Date }} {{ $note.Project }} {{ $note.Author }}{{- printf "\n" }}
	{{- range $i, $f := .Note.Files }}
		{{- if $f.IsTerminal }}
			{{- FormatDuration $f.TimeSpent | printf "\n%14s" }} {{ Percent $f.TimeSpent $total | printf "%5.2f"}}%% [{{ $f.Status }}] Terminal
		{{- else }}
			{{- FormatDuration $f.TimeSpent | printf "\n%14s" }} {{ Percent $f.TimeSpent $total | printf "%5.2f"}}%% [{{ $f.Status }}] {{$f.SourceFile}}
		{{- end }}
	{{- end }}
	{{- if len .Note.Files }}
		{{- FormatDuration $total | printf "\n%14s\n\n" }}
	{{- else }}
		{{- printf "\n" }}
	{{- end }}
{{- end -}}`
	statusTpl string = `
{{ range $i, $f := .Note.Files }}
	{{- if $f.IsTerminal }}
		{{- FormatDuration $f.TimeSpent | printf "%14s" }}  [{{ $f.Status }}] Terminal
	{{- else }}
		{{- FormatDuration $f.TimeSpent | printf "%14s" }}  [{{ $f.Status }}] {{$f.SourceFile}}
	{{- end }}
{{ end }}
{{- if len .Note.Files }}
	{{- FormatDuration .Note.Total | printf "%14s" }}
{{ end }}`
	commitTotalsTpl string = `
{{ $headerFormat := .HeaderFormat }}
{{- $total := .Notes.Total }}
{{- range $_, $note := .Notes }}
	{{- printf $headerFormat $note.Hash }} {{ printf $headerFormat $note.Subject }}{{- printf "\n" }}
	{{- $note.Date }} {{ $note.Project }} {{ $note.Author }} {{if len .Note.Files }}{{ Percent $note.Note.Total $total | printf "%.2f"}}%% {{ FormatDuration .Note.Total }}{{ end }}
	{{- print "\n" }}
{{ end }}`
	timelineTpl string = `
           0123456789012345678901234
{{ range $_, $entry := .Timeline }}
	{{- $entry.Day }} {{ RightPad2Len $entry.Bars " " 24 }} {{ LeftPad2Len $entry.Duration " " 13 }}
{{ end }}
{{- if len .Timeline }}
	{{- LeftPad2Len .Timeline.Duration " " 49 }}
{{ end }}`
	filesTpl string = `
{{- $total := .Files.Total }}
{{ range $i, $f := .Files }}
	{{- if $f.IsTerminal }}
		{{- $f.Duration | printf "%14s" }} {{ Percent $f.Seconds $total | printf "%5.2f"}}%%  Terminal
	{{- else }}
		{{- $f.Duration | printf "%14s" }} {{ Percent $f.Seconds $total | printf "%5.2f"}}%%  {{ $f.Filename }}
	{{- end }}
{{ end }}
{{- if len .Files }}
	{{- .Files.Duration | printf "%14s" }}
{{ end }}`
)

// Status returns the status report
func Status(n note.CommitNote, totalOnly bool) (string, error) {
	if totalOnly {
		return util.DurationStr(n.Total()), nil
	}
	b := new(bytes.Buffer)
	t := template.Must(template.New("Status").Funcs(funcMap).Parse(statusTpl))

	err := t.Execute(b, commitNoteDetail{Note: n})
	if err != nil {
		return "", err
	}
	return b.String(), nil
}

// Commits returns the commits report
func Commits(projects []ProjectCommits, totalOnly bool, limit int) (string, error) {
	notes := retrieveNotes(projects)
	b := new(bytes.Buffer)
	var t *template.Template
	if totalOnly {
		t = template.Must(template.New("Commit Totals").Funcs(funcMap).Parse(commitTotalsTpl))
	} else {
		t = template.Must(template.New("Commits").Funcs(funcMap).Parse(commitsTpl))
	}
	headerFormat := "%s"
	if isatty.IsTerminal(os.Stdout.Fd()) && runtime.GOOS != "windows" {
		headerFormat = "\x1b[1m%s\x1b[0m"
	}

	if limit > 0 && len(notes) > limit {
		notes = notes[0:limit]
	}

	err := t.Execute(
		b,
		struct {
			Notes        commitNoteDetails
			HeaderFormat string
		}{
			notes,
			headerFormat})
	if err != nil {
		return "", err
	}
	return b.String(), nil
}

// Timeline returns the timeline report
func Timeline(projects []ProjectCommits, limit int) (string, error) {
	notes := retrieveNotes(projects)
	b := new(bytes.Buffer)
	t := template.Must(template.New("Timeline").Funcs(funcMap).Parse(timelineTpl))

	if limit > 0 && len(notes) > limit {
		notes = notes[0:limit]
	}

	err := t.Execute(
		b,
		struct {
			Timeline timelineEntries
		}{
			notes.timeline(),
		})
	if err != nil {
		return "", err
	}
	return b.String(), nil
}

// Files returns the files report
func Files(projects []ProjectCommits, limit int) (string, error) {
	notes := retrieveNotes(projects)
	b := new(bytes.Buffer)
	t := template.Must(template.New("Files").Funcs(funcMap).Parse(filesTpl))

	if limit > 0 && len(notes) > limit {
		notes = notes[0:limit]
	}

	err := t.Execute(
		b,
		struct {
			Files fileEntries
		}{
			notes.files(),
		})
	if err != nil {
		return "", err
	}
	return b.String(), nil

}
<commit_msg>Improve report formats<commit_after>package report

import (
	"bytes"
	"os"
	"runtime"
	"text/template"

	"github.com/git-time-metric/gtm/note"
	"github.com/git-time-metric/gtm/util"
	isatty "github.com/mattn/go-isatty"
)

var funcMap = template.FuncMap{
	"FormatDuration": util.FormatDuration,
	"RightPad2Len":   util.RightPad2Len,
	"LeftPad2Len":    util.LeftPad2Len,
	"Percent":        util.Percent,
}

type ProjectCommits struct {
	Path    string
	Commits []string
}

const (
	commitsTpl string = `
{{ $headerFormat := .HeaderFormat }}
{{- range $note := .Notes }}
	{{- $total := .Note.Total }}
	{{- printf $headerFormat $note.Hash }} {{ printf $headerFormat $note.Subject }}{{- printf "\n" }}
	{{- $note.Date }} {{ printf $headerFormat $note.Project }} {{ $note.Author }}{{- printf "\n" }}
	{{- range $i, $f := .Note.Files }}
		{{- if $f.IsTerminal }}
			{{- FormatDuration $f.TimeSpent | printf "\n%14s" }} {{ Percent $f.TimeSpent $total | printf "%5.2f"}}%% [{{ $f.Status }}] Terminal
		{{- else }}
			{{- FormatDuration $f.TimeSpent | printf "\n%14s" }} {{ Percent $f.TimeSpent $total | printf "%5.2f"}}%% [{{ $f.Status }}] {{$f.SourceFile}}
		{{- end }}
	{{- end }}
	{{- if len .Note.Files }}
		{{- FormatDuration $total | printf "\n%14s\n\n" }}
	{{- else }}
		{{- printf "\n" }}
	{{- end }}
{{- end -}}`
	statusTpl string = `
{{ range $i, $f := .Note.Files }}
	{{- if $f.IsTerminal }}
		{{- FormatDuration $f.TimeSpent | printf "%14s" }}  [{{ $f.Status }}] Terminal
	{{- else }}
		{{- FormatDuration $f.TimeSpent | printf "%14s" }}  [{{ $f.Status }}] {{$f.SourceFile}}
	{{- end }}
{{ end }}
{{- if len .Note.Files }}
	{{- FormatDuration .Note.Total | printf "%14s" }}
{{ end }}`
	commitTotalsTpl string = `
{{ $headerFormat := .HeaderFormat }}
{{- $total := .Notes.Total }}
{{- range $_, $note := .Notes }}
	{{- print "\n" }}
	{{- printf $headerFormat $note.Hash }} {{ printf $headerFormat $note.Subject }}{{- printf "\n" }}
	{{- $note.Date }} {{ printf $headerFormat $note.Project }} {{ $note.Author }}{{- printf "\n" }}
	{{- if len .Note.Files }}
		{{- printf "\n" }}  {{ FormatDuration .Note.Total | printf "%14s" }}   {{ Percent $note.Note.Total $total | printf "%.2f"}}%%{{- print "\n" }}
	{{- end }}
{{- end }}
  {{ FormatDuration $total | printf "%14s" }} 100.00%% {{ printf "\n" }}
`
	timelineTpl string = `
           0123456789012345678901234
{{ range $_, $entry := .Timeline }}
	{{- $entry.Day }} {{ RightPad2Len $entry.Bars " " 24 }} {{ LeftPad2Len $entry.Duration " " 13 }}
{{ end }}
{{- if len .Timeline }}
	{{- LeftPad2Len .Timeline.Duration " " 49 }}
{{ end }}`
	filesTpl string = `
{{- $total := .Files.Total }}
{{ range $i, $f := .Files }}
	{{- if $f.IsTerminal }}
		{{- $f.Duration | printf "%14s" }} {{ Percent $f.Seconds $total | printf "%5.2f"}}%%  Terminal
	{{- else }}
		{{- $f.Duration | printf "%14s" }} {{ Percent $f.Seconds $total | printf "%5.2f"}}%%  {{ $f.Filename }}
	{{- end }}
{{ end }}
{{- if len .Files }}
	{{- .Files.Duration | printf "%14s" }}
{{ end }}`
)

// Status returns the status report
func Status(n note.CommitNote, totalOnly bool) (string, error) {
	if totalOnly {
		return util.DurationStr(n.Total()), nil
	}
	b := new(bytes.Buffer)
	t := template.Must(template.New("Status").Funcs(funcMap).Parse(statusTpl))

	err := t.Execute(b, commitNoteDetail{Note: n})
	if err != nil {
		return "", err
	}
	return b.String(), nil
}

// Commits returns the commits report
func Commits(projects []ProjectCommits, totalOnly bool, limit int) (string, error) {
	notes := retrieveNotes(projects)
	b := new(bytes.Buffer)
	var t *template.Template
	if totalOnly {
		t = template.Must(template.New("Commit Totals").Funcs(funcMap).Parse(commitTotalsTpl))
	} else {
		t = template.Must(template.New("Commits").Funcs(funcMap).Parse(commitsTpl))
	}
	headerFormat := "%s"
	if isatty.IsTerminal(os.Stdout.Fd()) && runtime.GOOS != "windows" {
		headerFormat = "\x1b[1m%s\x1b[0m"
	}

	if limit > 0 && len(notes) > limit {
		notes = notes[0:limit]
	}

	err := t.Execute(
		b,
		struct {
			Notes        commitNoteDetails
			HeaderFormat string
		}{
			notes,
			headerFormat})
	if err != nil {
		return "", err
	}
	return b.String(), nil
}

// Timeline returns the timeline report
func Timeline(projects []ProjectCommits, limit int) (string, error) {
	notes := retrieveNotes(projects)
	b := new(bytes.Buffer)
	t := template.Must(template.New("Timeline").Funcs(funcMap).Parse(timelineTpl))

	if limit > 0 && len(notes) > limit {
		notes = notes[0:limit]
	}

	err := t.Execute(
		b,
		struct {
			Timeline timelineEntries
		}{
			notes.timeline(),
		})
	if err != nil {
		return "", err
	}
	return b.String(), nil
}

// Files returns the files report
func Files(projects []ProjectCommits, limit int) (string, error) {
	notes := retrieveNotes(projects)
	b := new(bytes.Buffer)
	t := template.Must(template.New("Files").Funcs(funcMap).Parse(filesTpl))

	if limit > 0 && len(notes) > limit {
		notes = notes[0:limit]
	}

	err := t.Execute(
		b,
		struct {
			Files fileEntries
		}{
			notes.files(),
		})
	if err != nil {
		return "", err
	}
	return b.String(), nil

}
<|endoftext|>


<commit_before>// TODO: Break tests into multiple files.
// TODO: Change from Goconvey to standard tests.
package requests

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"mime"
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"
	"time"

	"github.com/jochasinga/gtime"
	"github.com/jochasinga/relay"
	//. "github.com/smartystreets/goconvey/convey"
)

func unexpectErr(result, expected interface{}) error {
	err := fmt.Errorf("[Expect] >> %v <<\n[Result] >> %v <<\n", expected, result)
	return err
}

var (
	// Functional options used in the tests.
	fn1 = func(r *Request) {
		r.Header.Set("content-type", "application/json")
	}
	fn2 = func(r *Request) {
		r.Timeout = time.Duration(3) * time.Second
	}
	fn3 = func(r *Request) {
		r.SetBasicAuth("user", "pass")
	}
	fn4 = func(r *Request) {
		r.Params.Add("foo", "bar")
	}
	fn5 = func(r *Request) {
		r.Params.Add("name", "Ava")
	}

	// Handler functions used in the test servers.
	helloHandler = func(w http.ResponseWriter, r *http.Request) {
		io.WriteString(w, "Hello world!")
	}
	jsonHandler = func(w http.ResponseWriter, r *http.Request) {
		data := []byte(`{"foo": ["bar", "baz"]}`)
		w.Header().Set("Content-Type", "application/json")
		w.Write(data)
	}
	jsonWithTypeParamHandler = func(w http.ResponseWriter, r *http.Request) {
		data := []byte(`{"foo": ["bar", "baz"]}`)
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.Write(data)
	}
	htmlHandler = func(w http.ResponseWriter, r *http.Request) {
		html := "
Blanca!
"
		w.Header().Set("Content-Type", "text/html")
		io.WriteString(w, html)
	}
	multTypeHandler = func(w http.ResponseWriter, r *http.Request) {
		data := []byte(`{"foo": ["bar", "baz"]}`)
		w.Header().Set("Content-Type", "text/html")
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.Write(data)
	}
	contentTypeHandler = func(w http.ResponseWriter, r *http.Request) {
		io.WriteString(w, r.Header.Get("Content-Type"))
	}
	fooHandler = func(w http.ResponseWriter, r *http.Request) {
		io.WriteString(w, r.FormValue("foo"))
	}
	nameHandler = func(w http.ResponseWriter, r *http.Request) {
		io.WriteString(w, r.FormValue("name"))
	}
	basicAuthHandler = func(w http.ResponseWriter, r *http.Request) {
		user, password, ok := r.BasicAuth()
		if !ok {
			log.Panicln("Error getting Basic Auth.")
		}
		io.WriteString(w, user+" : "+password)
	}

	// Test tables and array of functional options to use in tests.
	optList = [...][]func(*Request){
		{fn1},
		{fn1, fn2},
		{fn1, fn2, fn3},
		{fn1, fn2, fn3, fn4},
		{fn1, fn2, fn3, fn4, fn5},
	}
	getFuncTestTable = []struct {
		fn       func(*Request)
		handler  func(http.ResponseWriter, *http.Request)
		expected string
	}{
		{fn1, contentTypeHandler, "application/json"},
		{fn3, basicAuthHandler, "user : pass"},
		{fn4, fooHandler, "bar"},
		{fn5, nameHandler, "Ava"},
	}
	postFuncTestTable = []struct {
		bodyType string
		body     *bytes.Buffer
		opts     []func(*Request)
	}{
		{
			"application/json",
			bytes.NewBufferString(`{"foo":"bar"}`),
			[]func(*Request){fn1, fn2, fn3, fn4, fn5},
		},
	}
	getFuncSyncTestTable = []struct {
		delay    int
		expected int
	}{
		{1, 2},
		{2, 4},
		{3, 6},
		{4, 8},
	}
	getFuncTimeoutTestTable = []struct {
		delay    int
		timeout  float64
		expected float64
	}{
		{1, 0.5, 0.5},
		{2, 0.5, 0.5},
		{2, 1.0, 1.0},
		{3, 1.0, 1.0},
	}
	jsonFuncTestTable = []struct {
		fn       func(http.ResponseWriter, *http.Request)
		expected []byte
	}{
		{jsonHandler, []byte(`{"foo": ["bar", "baz"]}`)},
		{jsonWithTypeParamHandler, []byte(`{"foo": ["bar", "baz"]}`)},
		{multTypeHandler, []byte(`{"foo": ["bar", "baz"]}`)},
		{htmlHandler, []byte{}},
	}
	badURLs = []string{
		"://maggot.#&",
		"crap://bs.com",
		"htp://f#as3",
	}
)

/******************** POST *************************/
func TestPostResponseType(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	for _, tt := range postFuncTestTable {
		resp, err := Post(ts.URL, tt.bodyType, tt.body, tt.opts...)
		if err != nil {
			t.Error(err)
		}
		resultType := reflect.TypeOf(resp)
		expectedType := reflect.TypeOf(&Response{})
		if resultType != expectedType {
			t.Error(unexpectErr(resultType, expectedType))
		}
	}
}

/******************** POST *************************/

// Test that the returned type is always *Response.
func TestGetResponseType(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	for _, opts := range optList {
		resp, err := Get(ts.URL, opts...)
		if err != nil {
			t.Error(err)
		}
		resultType := reflect.TypeOf(resp)
		expectedType := reflect.TypeOf(&Response{})
		if resultType != expectedType {
			t.Error(unexpectErr(resultType, expectedType))
		}
	}
}

// Test that the request has the appropriate options.
func TestGetResponseOptions(t *testing.T) {
	for _, tt := range getFuncTestTable {
		ts := httptest.NewServer(http.HandlerFunc(tt.handler))
		defer ts.Close()
		resp, err := Get(ts.URL, tt.fn)
		if err != nil {
			t.Error(err)
		}
		if resp.String() != tt.expected {
			t.Error(err)
		}
	}
}

// Get should wait for the response and return
func TestGetResponseTime(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	for _, tt := range getFuncSyncTestTable {
		delay := time.Duration(tt.delay) * time.Second
		expected := time.Duration(tt.expected) * time.Second
		p := relay.NewProxy(delay, ts)
		start := time.Now()
		_, _ = Get(p.URL)
		elapsed := time.Since(start)
		if elapsed <= expected {
			t.Error(unexpectErr(elapsed, expected))
		}
	}
}

// Get should wait fo the response until timed out.
func TestGetResponseOnTimeout(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	for _, tt := range getFuncTimeoutTestTable {
		delay := time.Duration(tt.delay) * time.Second
		p := relay.NewProxy(delay, ts)
		start := time.Now()
		_, err := Get(p.URL, func(r *Request) {
			r.Timeout = gtime.Ftos(tt.timeout)
		})
		if err == nil {
			t.Error(err)
		}
		elapsed := time.Since(start).Seconds()
		deviation := gtime.FloatTime.Seconds()
		if !(elapsed >= tt.expected-deviation || elapsed <= tt.expected+deviation) {
			t.Error(unexpectErr(elapsed, tt.expected))
		}
	}
}

func TestResponseLen(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	resp, err := Get(ts.URL)
	if err != nil {
		t.Error(err)
	}
	length := resp.Len()
	expected := len("Hello world!")
	if length != len("Hello world!") {
		t.Error(unexpectErr(length, expected))
	}
}

func TestResponseAsBytes(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	resp, err := Get(ts.URL)
	if err != nil {
		t.Error(err)
	}
	result := resp.Bytes()
	expected := []byte("Hello world!")
	if bytes.Compare(result, expected) != 0 {
		t.Error(unexpectErr(result, expected))
	}
}

func TestResponseAsJSON(t *testing.T) {
	for _, tt := range jsonFuncTestTable {
		ts := httptest.NewServer(http.HandlerFunc(tt.fn))
		defer ts.Close()
		resp, err := Get(ts.URL)
		if err != nil {
			t.Error(err)
		}
		result := resp.JSON()
		if bytes.Compare(result, tt.expected) != 0 {
			t.Error(unexpectErr(result, tt.expected))
		}
	}
}

func TestResponseAsString(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	resp, err := Get(ts.URL)
	if err != nil {
		t.Error(err)
	}
	result := resp.String()
	if result != "Hello world!" {
		t.Error(unexpectErr(result, "Hello world!"))
	}
}

func TestResponseContentType(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(jsonWithTypeParamHandler))
	defer ts.Close()
	resp, err := Get(ts.URL)
	if err != nil {
		t.Error(err)
	}
	result, params, err := resp.ContentType()
	if err != nil {
		t.Error(err)
	}
	resultParam := params["charset"]
	expectedParam := "utf-8"
	if resultParam != expectedParam {
		t.Error(unexpectErr(resultParam, expectedParam))
	}
	expected, _, err := mime.ParseMediaType("application/json")
	if err != nil {
		t.Error(err)
	}
	if result != expected {
		t.Error(unexpectErr(result, expected))
	}
}

func TestGetWithBadURLs(t *testing.T) {
	for _, url := range badURLs {
		_, err := Get(url)
		if err == nil {
			t.Error(err)
		}
	}
}

func TestGetAsyncResponseType(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	rc, err := GetAsync(ts.URL)
	if err != nil {
		t.Error(err)
	}
	expectedType := reflect.TypeOf((<-chan *Response)(nil))
	resultType := reflect.TypeOf(rc)
	if resultType != expectedType {
		t.Error(unexpectErr(resultType, expectedType))
	}
}

// GetAsync should return immediately.
func TestGetFuncAsyncResponseTimes(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	deviation := time.Duration(10) * time.Millisecond
	for _, tt := range getFuncSyncTestTable {
		delay := time.Duration(tt.delay) * time.Second
		expected := time.Duration(tt.expected)*time.Second + deviation
		p := relay.NewProxy(delay, ts)
		start := time.Now()
		_, _ = GetAsync(p.URL)
		elapsed := time.Since(start)
		if elapsed >= expected {
			t.Error(unexpectErr(elapsed, expected))
		}
	}
}

// GetAsync should return immediately.
func TestGetFuncAsyncResponse(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	delay := time.Duration(1) * time.Second
	p := relay.NewProxy(delay, ts)
	rc, err := GetAsync(p.URL)
	if err != nil {
		t.Error(err)
	}
	resp := <-rc
	if resp.Error != nil {
		t.Error(resp.Error)
	}
	result := resp.String()
	if result != "Hello world!" {
		t.Error(unexpectErr(result, "Hello world!"))
	}
}
<commit_msg>Test type of PostJSON's response<commit_after>// TODO: Break tests into multiple files.
// TODO: Change from Goconvey to standard tests.
package requests

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"mime"
	"net/http"
	"net/http/httptest"
	"reflect"
	"strings"
	"testing"
	"time"

	"github.com/jochasinga/gtime"
	"github.com/jochasinga/relay"
)

func unexpectErr(result, expected interface{}) error {
	err := fmt.Errorf("[Expect] >> %v <<\n[Result] >> %v <<\n", expected, result)
	return err
}

var (
	// Functional options used in the tests.
	fn1 = func(r *Request) {
		r.Header.Set("content-type", "application/json")
	}
	fn2 = func(r *Request) {
		r.Timeout = time.Duration(3) * time.Second
	}
	fn3 = func(r *Request) {
		r.SetBasicAuth("user", "pass")
	}
	fn4 = func(r *Request) {
		r.Params.Add("foo", "bar")
	}
	fn5 = func(r *Request) {
		r.Params.Add("name", "Ava")
	}

	// Handler functions used in the test servers.
	helloHandler = func(w http.ResponseWriter, r *http.Request) {
		io.WriteString(w, "Hello world!")
	}
	jsonHandler = func(w http.ResponseWriter, r *http.Request) {
		data := []byte(`{"foo": ["bar", "baz"]}`)
		w.Header().Set("Content-Type", "application/json")
		w.Write(data)
	}
	jsonWithTypeParamHandler = func(w http.ResponseWriter, r *http.Request) {
		data := []byte(`{"foo": ["bar", "baz"]}`)
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.Write(data)
	}
	htmlHandler = func(w http.ResponseWriter, r *http.Request) {
		html := "
Blanca!
"
		w.Header().Set("Content-Type", "text/html")
		io.WriteString(w, html)
	}
	multTypeHandler = func(w http.ResponseWriter, r *http.Request) {
		data := []byte(`{"foo": ["bar", "baz"]}`)
		w.Header().Set("Content-Type", "text/html")
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.Write(data)
	}
	contentTypeHandler = func(w http.ResponseWriter, r *http.Request) {
		io.WriteString(w, r.Header.Get("Content-Type"))
	}
	fooHandler = func(w http.ResponseWriter, r *http.Request) {
		io.WriteString(w, r.FormValue("foo"))
	}
	nameHandler = func(w http.ResponseWriter, r *http.Request) {
		io.WriteString(w, r.FormValue("name"))
	}
	basicAuthHandler = func(w http.ResponseWriter, r *http.Request) {
		user, password, ok := r.BasicAuth()
		if !ok {
			log.Panicln("Error getting Basic Auth.")
		}
		io.WriteString(w, user+" : "+password)
	}

	// Test tables and array of functional options to use in tests.
	optList = [...][]func(*Request){
		{fn1},
		{fn1, fn2},
		{fn1, fn2, fn3},
		{fn1, fn2, fn3, fn4},
		{fn1, fn2, fn3, fn4, fn5},
	}
	getFuncTestTable = []struct {
		fn       func(*Request)
		handler  func(http.ResponseWriter, *http.Request)
		expected string
	}{
		{fn1, contentTypeHandler, "application/json"},
		{fn3, basicAuthHandler, "user : pass"},
		{fn4, fooHandler, "bar"},
		{fn5, nameHandler, "Ava"},
	}
	postFuncTestTable = []struct {
		bodyType string
		body     io.Reader
		opts     []func(*Request)
	}{
		{
			"application/json",
			bytes.NewBufferString(`{"foo":"bar"}`),
			[]func(*Request){fn1, fn2, fn3, fn4, fn5},
		},
		{
			"text/xml",
			strings.NewReader(`<foo>bar</foo>`),
			[]func(*Request){fn1, fn2, fn3, fn4, fn5},
		},
	}

	// Data for testing PostJSON
	bodyMap    = map[string][]string{"foo": []string{"bar", "baz"}}
	bodyStruct = struct {
		Foo []string `json:"foo"`
	}{[]string{"bar", "baz"}}
	bodyHybridMap = map[string][]interface{}{
		"duplica": {bodyMap, bodyStruct},
	}
	postJSONArgs = [...]interface{}{
		bodyMap, bodyStruct, bodyHybridMap,
	}

	// For testing timeouts
	getFuncSyncTestTable = []struct {
		delay    int
		expected int
	}{
		{1, 2},
		{2, 4},
		{3, 6},
		{4, 8},
	}
	getFuncTimeoutTestTable = []struct {
		delay    int
		timeout  float64
		expected float64
	}{
		{1, 0.5, 0.5},
		{2, 0.5, 0.5},
		{2, 1.0, 1.0},
		{3, 1.0, 1.0},
	}
	jsonFuncTestTable = []struct {
		fn       func(http.ResponseWriter, *http.Request)
		expected []byte
	}{
		{jsonHandler, []byte(`{"foo": ["bar", "baz"]}`)},
		{jsonWithTypeParamHandler, []byte(`{"foo": ["bar", "baz"]}`)},
		{multTypeHandler, []byte(`{"foo": ["bar", "baz"]}`)},
		{htmlHandler, []byte{}},
	}
	badURLs = []string{
		"://maggot.#&",
		"crap://bs.com",
		"htp://f#as3",
	}
)

/******************** POST *************************/
func TestPostResponseType(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	for _, tt := range postFuncTestTable {
		resp, err := Post(ts.URL, tt.bodyType, tt.body, tt.opts...)
		if err != nil {
			t.Error(err)
		}
		resultType := reflect.TypeOf(resp)
		expectedType := reflect.TypeOf(&Response{})
		if resultType != expectedType {
			t.Error(unexpectErr(resultType, expectedType))
		}
	}
}

func TestPostJSONResponseType(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	for _, arg := range postJSONArgs {
		resp, err := PostJSON(ts.URL, arg)
		if err != nil {
			t.Error(err)
		}
		resultType := reflect.TypeOf(resp)
		expectedType := reflect.TypeOf(&Response{})
		if resultType != expectedType {
			t.Error(unexpectErr(resultType, expectedType))
		}
	}
}

/******************** POST *************************/

// Test that the returned type is always *Response.
func TestGetResponseType(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	for _, opts := range optList {
		resp, err := Get(ts.URL, opts...)
		if err != nil {
			t.Error(err)
		}
		resultType := reflect.TypeOf(resp)
		expectedType := reflect.TypeOf(&Response{})
		if resultType != expectedType {
			t.Error(unexpectErr(resultType, expectedType))
		}
	}
}

// Test that the request has the appropriate options.
func TestGetResponseOptions(t *testing.T) {
	for _, tt := range getFuncTestTable {
		ts := httptest.NewServer(http.HandlerFunc(tt.handler))
		defer ts.Close()
		resp, err := Get(ts.URL, tt.fn)
		if err != nil {
			t.Error(err)
		}
		if resp.String() != tt.expected {
			t.Error(err)
		}
	}
}

// Get should wait for the response and return
func TestGetResponseTime(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	for _, tt := range getFuncSyncTestTable {
		delay := time.Duration(tt.delay) * time.Second
		expected := time.Duration(tt.expected) * time.Second
		p := relay.NewProxy(delay, ts)
		start := time.Now()
		_, _ = Get(p.URL)
		elapsed := time.Since(start)
		if elapsed <= expected {
			t.Error(unexpectErr(elapsed, expected))
		}
	}
}

// Get should wait fo the response until timed out.
func TestGetResponseOnTimeout(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	for _, tt := range getFuncTimeoutTestTable {
		delay := time.Duration(tt.delay) * time.Second
		p := relay.NewProxy(delay, ts)
		start := time.Now()
		_, err := Get(p.URL, func(r *Request) {
			r.Timeout = gtime.Ftos(tt.timeout)
		})
		if err == nil {
			t.Error(err)
		}
		elapsed := time.Since(start).Seconds()
		deviation := gtime.FloatTime.Seconds()
		if !(elapsed >= tt.expected-deviation || elapsed <= tt.expected+deviation) {
			t.Error(unexpectErr(elapsed, tt.expected))
		}
	}
}

func TestResponseLen(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	resp, err := Get(ts.URL)
	if err != nil {
		t.Error(err)
	}
	length := resp.Len()
	expected := len("Hello world!")
	if length != len("Hello world!") {
		t.Error(unexpectErr(length, expected))
	}
}

func TestResponseAsBytes(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	resp, err := Get(ts.URL)
	if err != nil {
		t.Error(err)
	}
	result := resp.Bytes()
	expected := []byte("Hello world!")
	if bytes.Compare(result, expected) != 0 {
		t.Error(unexpectErr(result, expected))
	}
}

func TestResponseAsJSON(t *testing.T) {
	for _, tt := range jsonFuncTestTable {
		ts := httptest.NewServer(http.HandlerFunc(tt.fn))
		defer ts.Close()
		resp, err := Get(ts.URL)
		if err != nil {
			t.Error(err)
		}
		result := resp.JSON()
		if bytes.Compare(result, tt.expected) != 0 {
			t.Error(unexpectErr(result, tt.expected))
		}
	}
}

func TestResponseAsString(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	resp, err := Get(ts.URL)
	if err != nil {
		t.Error(err)
	}
	result := resp.String()
	if result != "Hello world!" {
		t.Error(unexpectErr(result, "Hello world!"))
	}
}

func TestResponseContentType(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(jsonWithTypeParamHandler))
	defer ts.Close()
	resp, err := Get(ts.URL)
	if err != nil {
		t.Error(err)
	}
	result, params, err := resp.ContentType()
	if err != nil {
		t.Error(err)
	}
	resultParam := params["charset"]
	expectedParam := "utf-8"
	if resultParam != expectedParam {
		t.Error(unexpectErr(resultParam, expectedParam))
	}
	expected, _, err := mime.ParseMediaType("application/json")
	if err != nil {
		t.Error(err)
	}
	if result != expected {
		t.Error(unexpectErr(result, expected))
	}
}

func TestGetWithBadURLs(t *testing.T) {
	for _, url := range badURLs {
		_, err := Get(url)
		if err == nil {
			t.Error(err)
		}
	}
}

func TestGetAsyncResponseType(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	rc, err := GetAsync(ts.URL)
	if err != nil {
		t.Error(err)
	}
	expectedType := reflect.TypeOf((<-chan *Response)(nil))
	resultType := reflect.TypeOf(rc)
	if resultType != expectedType {
		t.Error(unexpectErr(resultType, expectedType))
	}
}

// GetAsync should return immediately.
func TestGetAsyncResponseTimes(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	deviation := time.Duration(10) * time.Millisecond
	for _, tt := range getFuncSyncTestTable {
		delay := time.Duration(tt.delay) * time.Second
		expected := time.Duration(tt.expected)*time.Second + deviation
		p := relay.NewProxy(delay, ts)
		start := time.Now()
		_, _ = GetAsync(p.URL)
		elapsed := time.Since(start)
		if elapsed >= expected {
			t.Error(unexpectErr(elapsed, expected))
		}
	}
}

// GetAsync should return immediately.
func TestGetAsyncResponse(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(helloHandler))
	defer ts.Close()
	delay := time.Duration(1) * time.Second
	p := relay.NewProxy(delay, ts)
	rc, err := GetAsync(p.URL)
	if err != nil {
		t.Error(err)
	}
	resp := <-rc
	if resp.Error != nil {
		t.Error(resp.Error)
	}
	result := resp.String()
	if result != "Hello world!" {
		t.Error(unexpectErr(result, "Hello world!"))
	}
}
<|endoftext|>


<commit_before>package required_test

import (
	"github.com/tiaguinho/required"
	"reflect"
	"testing"
)

var (
	i = required.Message{
		Field:  "I",
		ErrMsg: "where is the value?",
	}

	s = required.Message{
		Field:  "default",
		ErrMsg: "this field is required",
	}
)

type T struct {
	I int    `required:"where is the number?"`
	S string `json:"default" required:"-"`
	A []*A   `json:"array"`
	N int    `json:"do_not_check"`
}

type A struct {
	I int    `required:"-"`
	S string `json:"s" required:"don't left this field blank!'"`
}

func TestValidate(t *testing.T) {
	v := T{}

	err := required.Validate(v)
	if err == nil {
		t.Error("error expected! returned nil.")
	}

	sm := make([]required.Message, 2)
	sm[0] = i
	sm[1] = s

	e := required.New(sm...)
	if e == err {
		t.Errorf("\n expected: \n %s \n got: \n %s", e, err)
	}

	v.A = make([]*A, 1)
	v.A[0] = &A{
		I: 50,
	}

	err = required.Validate(v)
	if err == nil {
		t.Errorf("error expected! returned nil.%s", err)
	}

	v.I = 100
	v.S = "ok"
	v.A[0].S = "sub message"

	err = required.Validate(v)
	if err != nil {
		t.Errorf("\n no error message expected \n got: \n %s", err)
	}
}

func TestValidateWithMessage(t *testing.T) {
	v := T{}

	msg, err := required.ValidateWithMessage(v)
	if err != nil {
		sm := make([]required.Message, 2)
		sm[0] = i
		sm[1] = s

		if reflect.DeepEqual(sm, msg) {
			t.Errorf("\n expected: \n %s \n got: \n %s", sm, msg)
		}
	}

	v.A = make([]*A, 1)
	v.A[0] = &A{
		I: 50,
	}

	err = required.Validate(v)
	if err == nil {
		t.Error("error expected! returned nil.")
	}

	v.I = 100
	v.S = "ok"
	v.A[0].S = "sub message"

	msg, err = required.ValidateWithMessage(v)
	if err != nil || len(msg) > 0 {
		t.Errorf("\n no error message expected \n got: \n %s \n %+v", err, msg)
	}
}
<commit_msg>add more tests<commit_after>package required_test

import (
	"fmt"
	"github.com/tiaguinho/required"
	"reflect"
	"testing"
)

var (
	i = required.Message{
		Field:  "I",
		ErrMsg: "where is the value?",
	}

	s = required.Message{
		Field:  "default",
		ErrMsg: "this field is required",
	}
)

type T struct {
	I int    `required:"where is the number?"`
	S string `json:"default" required:"-"`
	A []*A   `json:"array"`
	N int    `json:"do_not_check"`
}

type A struct {
	I int    `required:"-"`
	S string `json:"s" required:"don't left this field blank!'"`
}

func TestError(t *testing.T) {
	e := required.New(required.Message{Index: 0, Field: "test", ErrMsg: "test message"})
	if e.Error() != fmt.Sprintf("[%s]: %s \n", "test", "test message") {
		t.Errorf("got: %s", e)
	}

}

func TestValidate(t *testing.T) {
	err := required.Validate("")
	if err == nil {
		t.Errorf("error expected! returned nil.%s", err)
	}

	v := T{}

	err = required.Validate(v)
	if err == nil {
		t.Error("error expected! returned nil.")
	}

	sm := make([]required.Message, 2)
	sm[0] = i
	sm[1] = s

	e := required.New(sm...)
	if e == err {
		t.Errorf("\n expected: \n %s \n got: \n %s", e, err)
	}

	v.A = make([]*A, 1)
	v.A[0] = &A{
		I: 50,
	}

	err = required.Validate(v)
	if err == nil {
		t.Errorf("error expected! returned nil.%s", err)
	}

	v.I = 100
	v.S = "ok"
	v.A[0].S = "sub message"

	err = required.Validate(v)
	if err != nil {
		t.Errorf("\n no error message expected \n got: \n %s", err)
	}
}

func TestValidateWithMessage(t *testing.T) {
	msg, err := required.ValidateWithMessage("")
	if err == nil {
		t.Errorf("error expected! returned nil.%s", err)
	}

	v := T{}

	msg, err = required.ValidateWithMessage(v)
	if err != nil {
		sm := make([]required.Message, 2)
		sm[0] = i
		sm[1] = s

		if reflect.DeepEqual(sm, msg) {
			t.Errorf("\n expected: \n %s \n got: \n %s", sm, msg)
		}
	}

	v.A = make([]*A, 1)
	v.A[0] = &A{
		I: 50,
	}

	err = required.Validate(v)
	if err == nil {
		t.Error("error expected! returned nil.")
	}

	v.I = 100
	v.S = "ok"
	v.A[0].S = "sub message"

	msg, err = required.ValidateWithMessage(v)
	if err != nil || len(msg) > 0 {
		t.Errorf("\n no error message expected \n got: \n %s \n %+v", err, msg)
	}
}
<|endoftext|>


<commit_before>package main

import (
    "bufio"
    "encoding/csv"
    "flag"
    "fmt"
    "io"
    //"io/ioutil"
    "os"
    "strconv"
    "strings"
    "unicode/utf8"
)

var inSep = flag.String("in-sep", ",", "Single character field separator used by your input")
var outSep = flag.String("out-sep", ",", "Single-character field separator to use when printing multiple columns in your output. Only valid if outputting something meant to be passed to cut/awk, and not a properly-formatted, quoted CSV file.")

var filename = flag.String("filename", "", "File to read from. If not specified, program reads from stdin.")

var fieldNumsRaw = flag.String("fieldNums", "", "Comma-separated list of field indexes (starting at 0) to print to the command line")
var noRFC = flag.Bool("no-rfc", false, "Program defaults to printing RFC 4180-compliant, quoted, well-formatted CSV. If this flag is supplied, output is returned as a string naively joined by --out-sep. --no-rfc is assumed to imply you want to pass the output to naive tools like cut or awk, and in that case, it is recommended that you select an --out-sep that is unlikely to be in youc content, such as a pipe or a backtick.")

func main() {
    flag.Parse()

    var fieldNums []int

    // Identify the numbers you want to print out
    if *fieldNumsRaw != "" {
        for _, numStr := range strings.Split(*fieldNumsRaw, ",") {
            numStr := strings.TrimSpace(numStr)
            numInt, err := strconv.Atoi(numStr)
            if err != nil {
                panic(err)
            }
            fieldNums = append(fieldNums, numInt)
        }
    }

    csvWriter := csv.NewWriter(os.Stdout)
    csvWriter.Comma = getSeparator(*outSep)

    var reader *bufio.Reader
    if *filename == "" {
        reader = bufio.NewReader(os.Stdin)
    } else {
        f, err := os.Open(*filename)
        if err != nil {
            panic(err)
        }

        reader = bufio.NewReader(f)
    }
    for {
        line, err := reader.ReadString('\n')
        if err != nil {
            if err == io.EOF {
                break
            }
            panic(err)
        }

        fields, err := processLine(line)
        if err != nil {
            if err == io.EOF {
                break
            } else {
                panic(err)
            }
        }

        var toPrint []string
        if *fieldNumsRaw == "" {
            for i, _ := range fields {
                toPrint = append(toPrint, fields[i])
            }
        } else {
            for _, num := range fieldNums {
                toPrint = append(toPrint, fields[num])
            }
        }

        if *noRFC == false {
            csvWriter.Write(toPrint)
        } else {
            fmt.Println(strings.Join(toPrint, *outSep))
        }
    }
    if *noRFC == false {
        csvWriter.Flush()
    }
}

func processLine(line string) ([]string, error) {
    strReader := strings.NewReader(line)
    csvReader := csv.NewReader(strReader)
    csvReader.LazyQuotes = true

    sepString := *inSep
    _ = utf8.DecodeRuneInString
    _ = sepString

    csvReader.Comma = getSeparator(sepString)

    fields, err := csvReader.Read()
    if err != nil {
        if err == io.EOF {
            return nil, io.EOF
        } else {
            fmt.Println("Error in the following line:")
            fmt.Println(line)
            panic(err)
        }
    }

    return fields, nil
}

func getSeparator(sepString string) (sepRune rune) {
    sepString = `'` + sepString + `'`
    sepRunes, err := strconv.Unquote(sepString)
    if err != nil {
        if err.Error() == "invalid syntax" {  // Single quote was used as separator. No idea why someone would want this, but it doesn't hurt to support it
            sepString = `"` + sepString + `"`
            sepRunes, err = strconv.Unquote(sepString)
            if err != nil {
                panic(err)
            }

        } else {
            panic(err)
        }
    }
    sepRune = ([]rune(sepRunes))[0]

    return sepRune
}
<commit_msg>Removed some unnecessary stuff<commit_after>package main

import (
    "bufio"
    "encoding/csv"
    "flag"
    "fmt"
    "io"
    "os"
    "strconv"
    "strings"
)

var inSep = flag.String("in-sep", ",", "Single character field separator used by your input")
var outSep = flag.String("out-sep", ",", "Single-character field separator to use when printing multiple columns in your output. Only valid if outputting something meant to be passed to cut/awk, and not a properly-formatted, quoted CSV file.")

var filename = flag.String("filename", "", "File to read from. If not specified, program reads from stdin.")

var fieldNumsRaw = flag.String("fieldNums", "", "Comma-separated list of field indexes (starting at 0) to print to the command line")
var noRFC = flag.Bool("no-rfc", false, "Program defaults to printing RFC 4180-compliant, quoted, well-formatted CSV. If this flag is supplied, output is returned as a string naively joined by --out-sep. --no-rfc is assumed to imply you want to pass the output to naive tools like cut or awk, and in that case, it is recommended that you select an --out-sep that is unlikely to be in youc content, such as a pipe or a backtick.")

func main() {
    flag.Parse()

    var fieldNums []int

    // Identify the numbers you want to print out
    if *fieldNumsRaw != "" {
        for _, numStr := range strings.Split(*fieldNumsRaw, ",") {
            numStr := strings.TrimSpace(numStr)
            numInt, err := strconv.Atoi(numStr)
            if err != nil {
                panic(err)
            }
            fieldNums = append(fieldNums, numInt)
        }
    }

    csvWriter := csv.NewWriter(os.Stdout)
    csvWriter.Comma = getSeparator(*outSep)

    var reader *bufio.Reader
    if *filename == "" {
        reader = bufio.NewReader(os.Stdin)
    } else {
        f, err := os.Open(*filename)
        if err != nil {
            panic(err)
        }

        reader = bufio.NewReader(f)
    }
    for {
        line, err := reader.ReadString('\n')
        if err != nil {
            if err == io.EOF {
                break
            }
            panic(err)
        }

        fields, err := processLine(line)
        if err != nil {
            if err == io.EOF {
                break
            } else {
                panic(err)
            }
        }

        var toPrint []string
        if *fieldNumsRaw == "" {
            for i, _ := range fields {
                toPrint = append(toPrint, fields[i])
            }
        } else {
            for _, num := range fieldNums {
                toPrint = append(toPrint, fields[num])
            }
        }

        if *noRFC == false {
            csvWriter.Write(toPrint)
        } else {
            fmt.Println(strings.Join(toPrint, *outSep))
        }
    }
    if *noRFC == false {
        csvWriter.Flush()
    }
}

func processLine(line string) ([]string, error) {
    strReader := strings.NewReader(line)
    csvReader := csv.NewReader(strReader)
    csvReader.LazyQuotes = true

    sepString := *inSep

    csvReader.Comma = getSeparator(sepString)

    fields, err := csvReader.Read()
    if err != nil {
        if err == io.EOF {
            return nil, io.EOF
        } else {
            fmt.Println("Error in the following line:")
            fmt.Println(line)
            panic(err)
        }
    }

    return fields, nil
}

func getSeparator(sepString string) (sepRune rune) {
    sepString = `'` + sepString + `'`
    sepRunes, err := strconv.Unquote(sepString)
    if err != nil {
        if err.Error() == "invalid syntax" {  // Single quote was used as separator. No idea why someone would want this, but it doesn't hurt to support it
            sepString = `"` + sepString + `"`
            sepRunes, err = strconv.Unquote(sepString)
            if err != nil {
                panic(err)
            }

        } else {
            panic(err)
        }
    }
    sepRune = ([]rune(sepRunes))[0]

    return sepRune
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"image"
	image_draw "image/draw"
	_ "image/png"
	"io/ioutil"

	"golang.org/x/mobile/asset"
	"golang.org/x/mobile/exp/gl/glutil"
	"golang.org/x/mobile/gl"
)

// EncodeObject converts float32 vertices into a LittleEndian byte array.
func EncodeObject(vertices ...[]float32) []byte {
	buf := bytes.Buffer{}
	for _, v := range vertices {
		err := binary.Write(&buf, binary.LittleEndian, v)
		if err != nil {
			panic(fmt.Sprintln("binary.Write failed:", err))
		}
	}

	return buf.Bytes()
}

func loadAsset(name string) ([]byte, error) {
	f, err := asset.Open(name)
	if err != nil {
		return nil, err
	}
	return ioutil.ReadAll(f)
}

// LoadProgram reads shader sources from the asset repository, compiles, and
// links them into a program.
func LoadProgram(vertexAsset, fragmentAsset string) (p gl.Program, err error) {
	vertexSrc, err := loadAsset(vertexAsset)
	if err != nil {
		return
	}

	fragmentSrc, err := loadAsset(fragmentAsset)
	if err != nil {
		return
	}

	p, err = glutil.CreateProgram(string(vertexSrc), string(fragmentSrc))
	return
}

// LoadTexture reads and decodes an image from the asset repository and creates
// a texture object based on the full dimensions of the image.
func LoadTexture(name string) (tex gl.Texture, err error) {
	imgFile, err := asset.Open(name)
	if err != nil {
		return
	}
	img, _, err := image.Decode(imgFile)
	if err != nil {
		return
	}

	rgba := image.NewRGBA(img.Bounds())
	image_draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, image_draw.Src)

	tex = gl.CreateTexture()
	gl.ActiveTexture(gl.TEXTURE0)
	gl.BindTexture(gl.TEXTURE_2D, tex)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
	gl.TexImage2D(
		gl.TEXTURE_2D,
		0,
		rgba.Rect.Size().X,
		rgba.Rect.Size().Y,
		gl.RGBA,
		gl.UNSIGNED_BYTE,
		rgba.Pix)

	return
}
<commit_msg>Update util.go<commit_after>package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"image"
	image_draw "image/draw"
	_ "image/png"
	"io/ioutil"

	"golang.org/x/mobile/asset"
	"golang.org/x/mobile/exp/gl/glutil"
	"golang.org/x/mobile/gl"
)

// EncodeObject converts float32 vertices into a LittleEndian byte array.
func EncodeObject(vertices ...[]float32) []byte {
	buf := bytes.Buffer{}
	for _, v := range vertices {
		err := binary.Write(&buf, binary.LittleEndian, v)
		if err != nil {
			panic(fmt.Sprintln("binary.Write failed:", err))
		}
	}

	return buf.Bytes()
}

func loadAsset(name string) ([]byte, error) {
	f, err := asset.Open(name)
	if err != nil {
		return nil, err
	}
	defer a.Close()
	return ioutil.ReadAll(f)
}

// LoadProgram reads shader sources from the asset repository, compiles, and
// links them into a program.
func LoadProgram(vertexAsset, fragmentAsset string) (p gl.Program, err error) {
	vertexSrc, err := loadAsset(vertexAsset)
	if err != nil {
		return
	}

	fragmentSrc, err := loadAsset(fragmentAsset)
	if err != nil {
		return
	}

	p, err = glutil.CreateProgram(string(vertexSrc), string(fragmentSrc))
	return
}

// LoadTexture reads and decodes an image from the asset repository and creates
// a texture object based on the full dimensions of the image.
func LoadTexture(name string) (tex gl.Texture, err error) {
	imgFile, err := asset.Open(name)
	if err != nil {
		return
	}
	img, _, err := image.Decode(imgFile)
	if err != nil {
		return
	}

	rgba := image.NewRGBA(img.Bounds())
	image_draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, image_draw.Src)

	tex = gl.CreateTexture()
	gl.ActiveTexture(gl.TEXTURE0)
	gl.BindTexture(gl.TEXTURE_2D, tex)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
	gl.TexImage2D(
		gl.TEXTURE_2D,
		0,
		rgba.Rect.Size().X,
		rgba.Rect.Size().Y,
		gl.RGBA,
		gl.UNSIGNED_BYTE,
		rgba.Pix)

	return
}
<|endoftext|>


<commit_before>/*
Copyright 2015 Google Inc. All rights reserved.

Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file or at
https://developers.google.com/open-source/licenses/bsd
*/
package cups

/*
#cgo LDFLAGS: -lcups
#include <cups/cups.h>
#include <stddef.h> // size_t
#include <stdlib.h> // free, malloc
#include <time.h>   // time_t

const char
    *POST_RESOURCE        = "/",
    *REQUESTED_ATTRIBUTES = "requested-attributes",
		*JOB_URI_ATTRIBUTE    = "job-uri",
		*IPP                  = "ipp";
*/
import "C"
import (
	"cups-connector/lib"
	"errors"
	"fmt"
	"os"
	"runtime"
	"syscall"
	"time"
	"unsafe"

	"github.com/golang/glog"
)

const (
	// jobURIFormat is the string format required by the CUPS API
	// to do things like query the state of a job.
	jobURIFormat = "/jobs/%d"
)

// cupsCore handles CUPS API interaction and connection management.
type cupsCore struct {
	host       *C.char
	port       C.int
	encryption C.http_encryption_t
	// connectionSemaphore limits the quantity of open CUPS connections.
	connectionSemaphore *lib.Semaphore
	// connectionPool allows a connection to be reused instead of closed.
	connectionPool chan *C.http_t
}

func newCUPSCore(maxConnections uint) (*cupsCore, error) {
	host := C.cupsServer()
	port := C.ippPort()
	encryption := C.cupsEncryption()

	var e string
	switch encryption {
	case C.HTTP_ENCRYPTION_ALWAYS:
		e = "encrypting ALWAYS"
	case C.HTTP_ENCRYPTION_IF_REQUESTED:
		e = "encrypting IF REQUESTED"
	case C.HTTP_ENCRYPTION_NEVER:
		e = "encrypting NEVER"
	case C.HTTP_ENCRYPTION_REQUIRED:
		e = "encryption REQUIRED"
	default:
		encryption = C.HTTP_ENCRYPTION_REQUIRED
		e = "encrypting REQUIRED"
	}

	cs := lib.NewSemaphore(maxConnections)
	cp := make(chan *C.http_t)

	cc := &cupsCore{host, port, encryption, cs, cp}

	// This connection isn't used, just checks that a connection is possible
	// before returning from the constructor.
	http, err := cc.connect()
	if err != nil {
		return nil, err
	}
	cc.disconnect(http)

	glog.Infof("connected to CUPS server %s:%d %s\n", C.GoString(host), int(port), e)

	return cc, nil
}

// printFile prints by calling C.cupsPrintFile2().
// Returns the CUPS job ID, which is 0 (and meaningless) when err
// is not nil.
func (cc *cupsCore) printFile(user, printername, filename, title *C.char, numOptions C.int, options *C.cups_option_t) (C.int, error) {
	http, err := cc.connect()
	if err != nil {
		return 0, err
	}
	defer cc.disconnect(http)

	C.cupsSetUser(user)
	jobID := C.cupsPrintFile2(http, printername, filename, title, numOptions, options)
	if jobID == 0 {
		return 0, fmt.Errorf("Failed to call cupsPrintFile2(): %d %s",
			int(C.cupsLastError()), C.GoString(C.cupsLastErrorString()))
	}

	return jobID, nil
}

// getPrinters gets the current list and state of printers by calling
// C.doRequest (IPP_OP_CUPS_GET_PRINTERS).
//
// The caller is responsible to C.ippDelete the returned *C.ipp_t response.
func (cc *cupsCore) getPrinters(attributes **C.char, attrSize C.int) (*C.ipp_t, error) {
	// ippNewRequest() returns ipp_t pointer which does not need explicit free.
	request := C.ippNewRequest(C.IPP_OP_CUPS_GET_PRINTERS)
	C.ippAddStrings(request, C.IPP_TAG_OPERATION, C.IPP_TAG_KEYWORD, C.REQUESTED_ATTRIBUTES,
		attrSize, nil, attributes)

	response, err := cc.doRequestWithRetry(request,
		[]C.ipp_status_t{C.IPP_STATUS_OK, C.IPP_STATUS_ERROR_NOT_FOUND})
	if err != nil {
		err = fmt.Errorf("Failed to call cupsDoRequest() [IPP_OP_CUPS_GET_PRINTERS]: %s", err)
		return nil, err
	}

	return response, nil
}

// getPPD gets the filename of the PPD for a printer by calling
// C.cupsGetPPD3. If the PPD hasn't changed since the time indicated
// by modtime, then the returned filename is a nil pointer.
//
// Note that modtime is a pointer whose value is changed by this
// function.
//
// The caller is responsible to C.free the returned *C.char filename
// if the returned filename is not nil.
func (cc *cupsCore) getPPD(printername *C.char, modtime *C.time_t) (*C.char, error) {
	bufsize := C.size_t(syscall.PathMax)
	buffer := (*C.char)(C.malloc(bufsize))
	if buffer == nil {
		return nil, errors.New("Failed to malloc; out of memory?")
	}
	C.memset(unsafe.Pointer(buffer), 0, bufsize)

	http, err := cc.connect()
	if err != nil {
		return nil, err
	}
	defer cc.disconnect(http)

	httpStatus := C.cupsGetPPD3(http, printername, modtime, buffer, bufsize)

	switch httpStatus {
	case C.HTTP_STATUS_NOT_MODIFIED:
		// Cache hit.
		if len(C.GoString(buffer)) > 0 {
			os.Remove(C.GoString(buffer))
		}
		C.free(unsafe.Pointer(buffer))
		return nil, nil

	case C.HTTP_STATUS_OK:
		// Cache miss.
		return buffer, nil

	default:
		if len(C.GoString(buffer)) > 0 {
			os.Remove(C.GoString(buffer))
		}
		C.free(unsafe.Pointer(buffer))
		cupsLastError := C.cupsLastError()
		if cupsLastError != C.IPP_STATUS_OK {
			return nil, fmt.Errorf("Failed to call cupsGetPPD3(): %d %s",
				int(cupsLastError), C.GoString(C.cupsLastErrorString()))
		}

		return nil, fmt.Errorf("Failed to call cupsGetPPD3(); HTTP status: %d", int(httpStatus))
	}
}

// getJobAttributes gets the requested attributes for a job by calling
// C.doRequest (IPP_OP_GET_JOB_ATTRIBUTES).
//
// The caller is responsible to C.ippDelete the returned *C.ipp_t response.
func (cc *cupsCore) getJobAttributes(jobID C.int, attributes **C.char) (*C.ipp_t, error) {
	uri, err := createJobURI(jobID)
	if err != nil {
		return nil, err
	}
	defer C.free(unsafe.Pointer(uri))

	// ippNewRequest() returns ipp_t pointer does not need explicit free.
	request := C.ippNewRequest(C.IPP_OP_GET_JOB_ATTRIBUTES)

	C.ippAddString(request, C.IPP_TAG_OPERATION, C.IPP_TAG_URI, C.JOB_URI_ATTRIBUTE, nil, uri)
	C.ippAddStrings(request, C.IPP_TAG_OPERATION, C.IPP_TAG_KEYWORD, C.REQUESTED_ATTRIBUTES,
		C.int(0), nil, attributes)

	response, err := cc.doRequestWithRetry(request, []C.ipp_status_t{C.IPP_STATUS_OK})
	if err != nil {
		err = fmt.Errorf("Failed to call cupsDoRequest() [IPP_OP_GET_JOB_ATTRIBUTES]: %s", err)
		return nil, err
	}

	return response, nil
}

// createJobURI creates a uri string for the job-uri attribute, used to get the
// status of a CUPS job.
func createJobURI(jobID C.int) (*C.char, error) {
	length := C.size_t(urlMaxLength)
	uri := (*C.char)(C.malloc(length))
	if uri == nil {
		return nil, errors.New("Failed to malloc; out of memory?")
	}

	resource := C.CString(fmt.Sprintf(jobURIFormat, uint32(jobID)))
	defer C.free(unsafe.Pointer(resource))
	C.httpAssembleURI(C.HTTP_URI_CODING_ALL,
		uri, C.int(length), C.IPP, nil, C.cupsServer(), C.ippPort(), resource)

	return uri, nil
}

// doRequestWithRetry calls doRequest and retries once on failure.
func (cc *cupsCore) doRequestWithRetry(request *C.ipp_t, acceptableStatusCodes []C.ipp_status_t) (*C.ipp_t, error) {
	response, err := cc.doRequest(request, acceptableStatusCodes)
	if err == nil {
		return response, err
	}

	return cc.doRequest(request, acceptableStatusCodes)
}

// doRequest calls cupsDoRequest().
func (cc *cupsCore) doRequest(request *C.ipp_t, acceptableStatusCodes []C.ipp_status_t) (*C.ipp_t, error) {
	http, err := cc.connect()
	if err != nil {
		return nil, err
	}
	defer cc.disconnect(http)

	response := C.cupsDoRequest(http, request, C.POST_RESOURCE)
	if response == nil {
		return nil, fmt.Errorf("cupsDoRequest failed: %d %s", int(C.cupsLastError()), C.GoString(C.cupsLastErrorString()))
	}
	statusCode := C.ippGetStatusCode(response)
	for _, sc := range acceptableStatusCodes {
		if statusCode == sc {
			return response, nil
		}
	}

	return nil, fmt.Errorf("IPP status code %d", int(statusCode))
}

// connect calls C.httpConnectEncrypt to create a new, open
// connection to the CUPS server specified by environment variables,
// client.conf, etc.
//
// connect also acquires the connection semaphore and locks the OS
// thread to allow the CUPS API to use thread-local storage cleanly.
//
// The caller is responsible to close the connection when finished
// using cupsCore.disconnect.
func (cc *cupsCore) connect() (*C.http_t, error) {
	cc.connectionSemaphore.Acquire()

	// Lock the OS thread so that thread-local storage is available to
	// cupsLastError() and cupsLastErrorString().
	runtime.LockOSThread()

	var http *C.http_t

	select {
	case h := <-cc.connectionPool:
		// Reuse another connection.
		http = h
	default:
		// No connection available for reuse; create a new one.
		http = C.httpConnectEncrypt(cc.host, cc.port, cc.encryption)
		if http == nil {
			defer cc.disconnect(http)
			return nil, fmt.Errorf("Failed to connect to CUPS server %s:%d because %d %s",
				C.GoString(cc.host), int(cc.port), int(C.cupsLastError()), C.GoString(C.cupsLastErrorString()))
		}
	}

	return http, nil
}

// disconnect calls C.httpClose to close an open CUPS connection, then
// unlocks the OS thread and the connection semaphore.
//
// The http argument may be nil; the OS thread and semaphore are still
// treated the same as described above.
func (cc *cupsCore) disconnect(http *C.http_t) {
	go func() {
		select {
		case cc.connectionPool <- http:
			// Hand this connection to the next guy who needs it.
		case <-time.After(time.Second):
			// Don't wait very long; stale connections are no fun.
			C.httpClose(http)
		}
	}()
	runtime.UnlockOSThread()
	cc.connectionSemaphore.Release()
}

func (cc *cupsCore) connQtyOpen() uint {
	return cc.connectionSemaphore.Count()
}

func (cc *cupsCore) connQtyMax() uint {
	return cc.connectionSemaphore.Size()
}
<commit_msg>Check for invalid IPP requests before executing them.<commit_after>/*
Copyright 2015 Google Inc. All rights reserved.

Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file or at
https://developers.google.com/open-source/licenses/bsd
*/
package cups

/*
#cgo LDFLAGS: -lcups
#include <cups/cups.h>
#include <stddef.h> // size_t
#include <stdlib.h> // free, malloc
#include <time.h>   // time_t

const char
    *POST_RESOURCE        = "/",
    *REQUESTED_ATTRIBUTES = "requested-attributes",
		*JOB_URI_ATTRIBUTE    = "job-uri",
		*IPP                  = "ipp";
*/
import "C"
import (
	"cups-connector/lib"
	"errors"
	"fmt"
	"os"
	"runtime"
	"syscall"
	"time"
	"unsafe"

	"github.com/golang/glog"
)

const (
	// jobURIFormat is the string format required by the CUPS API
	// to do things like query the state of a job.
	jobURIFormat = "/jobs/%d"
)

// cupsCore handles CUPS API interaction and connection management.
type cupsCore struct {
	host       *C.char
	port       C.int
	encryption C.http_encryption_t
	// connectionSemaphore limits the quantity of open CUPS connections.
	connectionSemaphore *lib.Semaphore
	// connectionPool allows a connection to be reused instead of closed.
	connectionPool chan *C.http_t
}

func newCUPSCore(maxConnections uint) (*cupsCore, error) {
	host := C.cupsServer()
	port := C.ippPort()
	encryption := C.cupsEncryption()

	var e string
	switch encryption {
	case C.HTTP_ENCRYPTION_ALWAYS:
		e = "encrypting ALWAYS"
	case C.HTTP_ENCRYPTION_IF_REQUESTED:
		e = "encrypting IF REQUESTED"
	case C.HTTP_ENCRYPTION_NEVER:
		e = "encrypting NEVER"
	case C.HTTP_ENCRYPTION_REQUIRED:
		e = "encryption REQUIRED"
	default:
		encryption = C.HTTP_ENCRYPTION_REQUIRED
		e = "encrypting REQUIRED"
	}

	cs := lib.NewSemaphore(maxConnections)
	cp := make(chan *C.http_t)

	cc := &cupsCore{host, port, encryption, cs, cp}

	// This connection isn't used, just checks that a connection is possible
	// before returning from the constructor.
	http, err := cc.connect()
	if err != nil {
		return nil, err
	}
	cc.disconnect(http)

	glog.Infof("connected to CUPS server %s:%d %s\n", C.GoString(host), int(port), e)

	return cc, nil
}

// printFile prints by calling C.cupsPrintFile2().
// Returns the CUPS job ID, which is 0 (and meaningless) when err
// is not nil.
func (cc *cupsCore) printFile(user, printername, filename, title *C.char, numOptions C.int, options *C.cups_option_t) (C.int, error) {
	http, err := cc.connect()
	if err != nil {
		return 0, err
	}
	defer cc.disconnect(http)

	C.cupsSetUser(user)
	jobID := C.cupsPrintFile2(http, printername, filename, title, numOptions, options)
	if jobID == 0 {
		return 0, fmt.Errorf("Failed to call cupsPrintFile2(): %d %s",
			int(C.cupsLastError()), C.GoString(C.cupsLastErrorString()))
	}

	return jobID, nil
}

// getPrinters gets the current list and state of printers by calling
// C.doRequest (IPP_OP_CUPS_GET_PRINTERS).
//
// The caller is responsible to C.ippDelete the returned *C.ipp_t response.
func (cc *cupsCore) getPrinters(attributes **C.char, attrSize C.int) (*C.ipp_t, error) {
	// ippNewRequest() returns ipp_t pointer which does not need explicit free.
	request := C.ippNewRequest(C.IPP_OP_CUPS_GET_PRINTERS)
	C.ippAddStrings(request, C.IPP_TAG_OPERATION, C.IPP_TAG_KEYWORD, C.REQUESTED_ATTRIBUTES,
		attrSize, nil, attributes)

	response, err := cc.doRequestWithRetry(request,
		[]C.ipp_status_t{C.IPP_STATUS_OK, C.IPP_STATUS_ERROR_NOT_FOUND})
	if err != nil {
		err = fmt.Errorf("Failed to call cupsDoRequest() [IPP_OP_CUPS_GET_PRINTERS]: %s", err)
		return nil, err
	}

	return response, nil
}

// getPPD gets the filename of the PPD for a printer by calling
// C.cupsGetPPD3. If the PPD hasn't changed since the time indicated
// by modtime, then the returned filename is a nil pointer.
//
// Note that modtime is a pointer whose value is changed by this
// function.
//
// The caller is responsible to C.free the returned *C.char filename
// if the returned filename is not nil.
func (cc *cupsCore) getPPD(printername *C.char, modtime *C.time_t) (*C.char, error) {
	bufsize := C.size_t(syscall.PathMax)
	buffer := (*C.char)(C.malloc(bufsize))
	if buffer == nil {
		return nil, errors.New("Failed to malloc; out of memory?")
	}
	C.memset(unsafe.Pointer(buffer), 0, bufsize)

	http, err := cc.connect()
	if err != nil {
		return nil, err
	}
	defer cc.disconnect(http)

	httpStatus := C.cupsGetPPD3(http, printername, modtime, buffer, bufsize)

	switch httpStatus {
	case C.HTTP_STATUS_NOT_MODIFIED:
		// Cache hit.
		if len(C.GoString(buffer)) > 0 {
			os.Remove(C.GoString(buffer))
		}
		C.free(unsafe.Pointer(buffer))
		return nil, nil

	case C.HTTP_STATUS_OK:
		// Cache miss.
		return buffer, nil

	default:
		if len(C.GoString(buffer)) > 0 {
			os.Remove(C.GoString(buffer))
		}
		C.free(unsafe.Pointer(buffer))
		cupsLastError := C.cupsLastError()
		if cupsLastError != C.IPP_STATUS_OK {
			return nil, fmt.Errorf("Failed to call cupsGetPPD3(): %d %s",
				int(cupsLastError), C.GoString(C.cupsLastErrorString()))
		}

		return nil, fmt.Errorf("Failed to call cupsGetPPD3(); HTTP status: %d", int(httpStatus))
	}
}

// getJobAttributes gets the requested attributes for a job by calling
// C.doRequest (IPP_OP_GET_JOB_ATTRIBUTES).
//
// The caller is responsible to C.ippDelete the returned *C.ipp_t response.
func (cc *cupsCore) getJobAttributes(jobID C.int, attributes **C.char) (*C.ipp_t, error) {
	uri, err := createJobURI(jobID)
	if err != nil {
		return nil, err
	}
	defer C.free(unsafe.Pointer(uri))

	// ippNewRequest() returns ipp_t pointer does not need explicit free.
	request := C.ippNewRequest(C.IPP_OP_GET_JOB_ATTRIBUTES)

	C.ippAddString(request, C.IPP_TAG_OPERATION, C.IPP_TAG_URI, C.JOB_URI_ATTRIBUTE, nil, uri)
	C.ippAddStrings(request, C.IPP_TAG_OPERATION, C.IPP_TAG_KEYWORD, C.REQUESTED_ATTRIBUTES,
		C.int(0), nil, attributes)

	response, err := cc.doRequestWithRetry(request, []C.ipp_status_t{C.IPP_STATUS_OK})
	if err != nil {
		err = fmt.Errorf("Failed to call cupsDoRequest() [IPP_OP_GET_JOB_ATTRIBUTES]: %s", err)
		return nil, err
	}

	return response, nil
}

// createJobURI creates a uri string for the job-uri attribute, used to get the
// status of a CUPS job.
func createJobURI(jobID C.int) (*C.char, error) {
	length := C.size_t(urlMaxLength)
	uri := (*C.char)(C.malloc(length))
	if uri == nil {
		return nil, errors.New("Failed to malloc; out of memory?")
	}

	resource := C.CString(fmt.Sprintf(jobURIFormat, uint32(jobID)))
	defer C.free(unsafe.Pointer(resource))
	C.httpAssembleURI(C.HTTP_URI_CODING_ALL,
		uri, C.int(length), C.IPP, nil, C.cupsServer(), C.ippPort(), resource)

	return uri, nil
}

// doRequestWithRetry calls doRequest and retries once on failure.
func (cc *cupsCore) doRequestWithRetry(request *C.ipp_t, acceptableStatusCodes []C.ipp_status_t) (*C.ipp_t, error) {
	response, err := cc.doRequest(request, acceptableStatusCodes)
	if err == nil {
		return response, err
	}

	return cc.doRequest(request, acceptableStatusCodes)
}

// doRequest calls cupsDoRequest().
func (cc *cupsCore) doRequest(request *C.ipp_t, acceptableStatusCodes []C.ipp_status_t) (*C.ipp_t, error) {
	http, err := cc.connect()
	if err != nil {
		return nil, err
	}
	defer cc.disconnect(http)

	if C.ippValidateAttributes(request) != 1 {
		return nil, fmt.Errorf("Bad IPP request: %s", C.GoString(C.cupsLastErrorString()))
	}

	response := C.cupsDoRequest(http, request, C.POST_RESOURCE)
	if response == nil {
		return nil, fmt.Errorf("cupsDoRequest failed: %d %s", int(C.cupsLastError()), C.GoString(C.cupsLastErrorString()))
	}
	statusCode := C.ippGetStatusCode(response)
	for _, sc := range acceptableStatusCodes {
		if statusCode == sc {
			return response, nil
		}
	}

	return nil, fmt.Errorf("IPP status code %d", int(statusCode))
}

// connect calls C.httpConnectEncrypt to create a new, open
// connection to the CUPS server specified by environment variables,
// client.conf, etc.
//
// connect also acquires the connection semaphore and locks the OS
// thread to allow the CUPS API to use thread-local storage cleanly.
//
// The caller is responsible to close the connection when finished
// using cupsCore.disconnect.
func (cc *cupsCore) connect() (*C.http_t, error) {
	cc.connectionSemaphore.Acquire()

	// Lock the OS thread so that thread-local storage is available to
	// cupsLastError() and cupsLastErrorString().
	runtime.LockOSThread()

	var http *C.http_t

	select {
	case h := <-cc.connectionPool:
		// Reuse another connection.
		http = h
	default:
		// No connection available for reuse; create a new one.
		http = C.httpConnectEncrypt(cc.host, cc.port, cc.encryption)
		if http == nil {
			defer cc.disconnect(http)
			return nil, fmt.Errorf("Failed to connect to CUPS server %s:%d because %d %s",
				C.GoString(cc.host), int(cc.port), int(C.cupsLastError()), C.GoString(C.cupsLastErrorString()))
		}
	}

	return http, nil
}

// disconnect calls C.httpClose to close an open CUPS connection, then
// unlocks the OS thread and the connection semaphore.
//
// The http argument may be nil; the OS thread and semaphore are still
// treated the same as described above.
func (cc *cupsCore) disconnect(http *C.http_t) {
	go func() {
		select {
		case cc.connectionPool <- http:
			// Hand this connection to the next guy who needs it.
		case <-time.After(time.Second):
			// Don't wait very long; stale connections are no fun.
			C.httpClose(http)
		}
	}()
	runtime.UnlockOSThread()
	cc.connectionSemaphore.Release()
}

func (cc *cupsCore) connQtyOpen() uint {
	return cc.connectionSemaphore.Count()
}

func (cc *cupsCore) connQtyMax() uint {
	return cc.connectionSemaphore.Size()
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Townsourced Inc.

// Package data handles all the data and application structures handling for Lex Library
package data

import (
	"database/sql"
	"log"
	"strings"
	"time"

	_ "github.com/mattn/go-sqlite3" // register sqlite3
	"github.com/pkg/errors"
)

// Database Types
const (
	sqlite      = iota // github.com/mattn/go-sqlite3
	postgres           // github.com/lib/pq
	mysql              // github.com/go-sql-driver/mysql/
	cockroachdb        // github.com/lib/pq
	tidb               // github.com/go-sql-driver/mysql/
)

const databaseName = "lexLibrary"

var db *sql.DB
var dbType int

// Config is the data layer configuration used to determine how to initialize the data layer
type Config struct {
	DatabaseFile string
	SearchFile   string

	DatabaseType string
	DatabaseURL  string

	SSLCert     string
	SSLKey      string
	SSLRootCert string

	MaxIdleConnections    int
	MaxOpenConnections    int
	MaxConnectionLifetime string

	AllowSchemaRollback bool
}

//TODO: Timeout setting across all databases

// DefaultConfig returns the default configuration for the data layer
func DefaultConfig() Config {
	return Config{
		DatabaseFile: "./lexLibrary.db",
		SearchFile:   "./lexLibrary.search",
	}
}

// Init initializes the data layer based on the passed in configuration.
// Initialization includes things like setting up the database and the connections to it.
func Init(cfg Config) error {
	err := initSearch(cfg)
	if err != nil {
		return err
	}

	switch strings.ToLower(cfg.DatabaseType) {
	case "postgres":
		dbType = postgres
		err = initPostgres(cfg)
		if err != nil {
			return err
		}
	default:
		dbType = sqlite

		err = initSQLite(cfg)
		if err != nil {
			return err
		}
	}

	if cfg.MaxConnectionLifetime != "" {
		lifetime, err := time.ParseDuration(cfg.MaxConnectionLifetime)
		if err == nil {
			db.SetConnMaxLifetime(lifetime)
		} else {
			log.Printf("Invalid MaxConnectionLifetime duration format (%s), using default", cfg.MaxConnectionLifetime)
		}
	}
	db.SetMaxIdleConns(cfg.MaxIdleConnections)
	db.SetMaxOpenConns(cfg.MaxOpenConnections)

	err = ensureSchema(cfg.AllowSchemaRollback)
	if err != nil {
		return err
	}

	return nil
}

func initSQLite(cfg Config) error {
	url := ""
	if cfg.DatabaseFile == "" && cfg.DatabaseURL == "" {
		cfg.DatabaseFile = DefaultConfig().DatabaseFile
	}

	if cfg.DatabaseURL == "" {
		url = cfg.DatabaseFile
	}

	var err error
	db, err = sql.Open("sqlite3", url)
	if err != nil {
		return err
	}

	return nil
}

func initPostgres(cfg Config) error {
	// test if lexLibrary database exists and create if it doesn't
	return errors.Errorf("TODO")
}
<commit_msg>Not much<commit_after>// Copyright (c) 2017 Townsourced Inc.

// Package data handles all the data and application structures handling for Lex Library
package data

import (
	"database/sql"
	"log"
	"strings"
	"time"

	_ "github.com/mattn/go-sqlite3" // register sqlite3
	"github.com/pkg/errors"
)

// Database Types
const (
	sqlite      = iota // github.com/mattn/go-sqlite3
	postgres           // github.com/lib/pq
	mysql              // github.com/go-sql-driver/mysql/
	cockroachdb        // github.com/lib/pq
	tidb               // github.com/go-sql-driver/mysql/
)

const databaseName = "lexLibrary"

var db *sql.DB
var dbType int

// Config is the data layer configuration used to determine how to initialize the data layer
type Config struct {
	DatabaseFile string
	SearchFile   string

	DatabaseType string
	DatabaseURL  string

	SSLCert     string
	SSLKey      string
	SSLRootCert string

	MaxIdleConnections    int
	MaxOpenConnections    int
	MaxConnectionLifetime string

	AllowSchemaRollback bool
}

// DefaultConfig returns the default configuration for the data layer
func DefaultConfig() Config {
	return Config{
		DatabaseFile: "./lexLibrary.db",
		SearchFile:   "./lexLibrary.search",
	}
}

// Init initializes the data layer based on the passed in configuration.
// Initialization includes things like setting up the database and the connections to it.
func Init(cfg Config) error {
	err := initSearch(cfg)
	if err != nil {
		return err
	}

	switch strings.ToLower(cfg.DatabaseType) {
	case "postgres":
		dbType = postgres
		err = initPostgres(cfg)
		if err != nil {
			return err
		}
		dbType = sqlite

	case "sqlite":
		err = initSQLite(cfg)
		if err != nil {
			return err
		}
	default:
		return errors.New("Invalid database type")
	}

	if cfg.MaxConnectionLifetime != "" {
		lifetime, err := time.ParseDuration(cfg.MaxConnectionLifetime)
		if err == nil {
			db.SetConnMaxLifetime(lifetime)
		} else {
			log.Printf("Invalid MaxConnectionLifetime duration format (%s), using default", cfg.MaxConnectionLifetime)
		}
	}
	db.SetMaxIdleConns(cfg.MaxIdleConnections)
	db.SetMaxOpenConns(cfg.MaxOpenConnections)

	err = ensureSchema(cfg.AllowSchemaRollback)
	if err != nil {
		return err
	}

	return nil
}

func initSQLite(cfg Config) error {
	url := ""
	if cfg.DatabaseFile == "" && cfg.DatabaseURL == "" {
		cfg.DatabaseFile = DefaultConfig().DatabaseFile
	}

	if cfg.DatabaseURL == "" {
		url = cfg.DatabaseFile
	}

	var err error
	db, err = sql.Open("sqlite3", url)
	if err != nil {
		return err
	}

	return nil
}

func initPostgres(cfg Config) error {
	// test if lexLibrary database exists and create if it doesn't
	return errors.Errorf("TODO")
}
<|endoftext|>