<commit_before>package aws

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/rds"
	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

// Constants not currently provided by the AWS Go SDK
const (
	rdsDbInstanceRoleStatusActive  = "ACTIVE"
	rdsDbInstanceRoleStatusDeleted = "DELETED"
	rdsDbInstanceRoleStatusPending = "PENDING"
)

func resourceAwsDbInstanceRoleAssociation() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsDbInstanceRoleAssociationCreate,
		Read:   resourceAwsDbInstanceRoleAssociationRead,
		Delete: resourceAwsDbInstanceRoleAssociationDelete,

		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"db_instance_identifier": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"feature_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"role_arn": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateArn,
			},
		},
	}
}

func resourceAwsDbInstanceRoleAssociationCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).rdsconn

	dbInstanceIdentifier := d.Get("db_instance_identifier").(string)
	roleArn := d.Get("role_arn").(string)

	input := &rds.AddRoleToDBInstanceInput{
		DBInstanceIdentifier: aws.String(dbInstanceIdentifier),
		FeatureName:          aws.String(d.Get("feature_name").(string)),
		RoleArn:              aws.String(roleArn),
	}

	log.Printf("[DEBUG] RDS DB Instance (%s) IAM Role associating: %s", dbInstanceIdentifier, roleArn)
	_, err := conn.AddRoleToDBInstance(input)

	if err != nil {
		return fmt.Errorf("error associating RDS DB Instance (%s) IAM Role (%s): %s", dbInstanceIdentifier, roleArn, err)
	}

	d.SetId(fmt.Sprintf("%s,%s", dbInstanceIdentifier, roleArn))

	if err := waitForRdsDbInstanceRoleAssociation(conn, dbInstanceIdentifier, roleArn); err != nil {
		return fmt.Errorf("error waiting for RDS DB Instance (%s) IAM Role (%s) association: %s", dbInstanceIdentifier, roleArn, err)
	}

	return resourceAwsDbInstanceRoleAssociationRead(d, meta)
}

func resourceAwsDbInstanceRoleAssociationRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).rdsconn

	dbInstanceIdentifier, roleArn, err := resourceAwsDbInstanceRoleAssociationDecodeId(d.Id())

	if err != nil {
		return fmt.Errorf("error reading resource ID: %s", err)
	}

	dbInstanceRole, err := rdsDescribeDbInstanceRole(conn, dbInstanceIdentifier, roleArn)

	if isAWSErr(err, rds.ErrCodeDBInstanceNotFoundFault, "") {
		log.Printf("[WARN] RDS DB Instance (%s) not found, removing from state", dbInstanceIdentifier)
		d.SetId("")
		return nil
	}

	if err != nil {
		return fmt.Errorf("error reading RDS DB Instance (%s) IAM Role (%s) association: %s", dbInstanceIdentifier, roleArn, err)
	}

	if dbInstanceRole == nil {
		log.Printf("[WARN] RDS DB Instance (%s) IAM Role (%s) association not found, removing from state", dbInstanceIdentifier, roleArn)
		d.SetId("")
		return nil
	}

	d.Set("db_instance_identifier", dbInstanceIdentifier)
	d.Set("feature_name", dbInstanceRole.FeatureName)
	d.Set("role_arn", dbInstanceRole.RoleArn)

	return nil
}

func resourceAwsDbInstanceRoleAssociationDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).rdsconn

	dbInstanceIdentifier, roleArn, err := resourceAwsDbInstanceRoleAssociationDecodeId(d.Id())

	if err != nil {
		return fmt.Errorf("error reading resource ID: %s", err)
	}

	input := &rds.RemoveRoleFromDBInstanceInput{
		DBInstanceIdentifier: aws.String(dbInstanceIdentifier),
		FeatureName:          aws.String(d.Get("feature_name").(string)),
		RoleArn:              aws.String(roleArn),
	}

	log.Printf("[DEBUG] RDS DB Instance (%s) IAM Role disassociating: %s", dbInstanceIdentifier, roleArn)
	_, err = conn.RemoveRoleFromDBInstance(input)

	if isAWSErr(err, rds.ErrCodeDBInstanceNotFoundFault, "") {
		return nil
	}

	if isAWSErr(err, rds.ErrCodeDBInstanceRoleNotFoundFault, "") {
		return nil
	}

	if err != nil {
		return fmt.Errorf("error disassociating RDS DB Instance (%s) IAM Role (%s): %s", dbInstanceIdentifier, roleArn, err)
	}

	if err := waitForRdsDbInstanceRoleDisassociation(conn, dbInstanceIdentifier, roleArn); err != nil {
		return fmt.Errorf("error waiting for RDS DB Instance (%s) IAM Role (%s) disassociation: %s", dbInstanceIdentifier, roleArn, err)
	}

	return nil
}

func resourceAwsDbInstanceRoleAssociationDecodeId(id string) (string, string, error) {
	parts := strings.SplitN(id, ",", 2)

	if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
		return "", "", fmt.Errorf("unexpected format of ID (%s), expected DB-INSTANCE-ID,ROLE-ARN", id)
	}

	return parts[0], parts[1], nil
}

func rdsDescribeDbInstanceRole(conn *rds.RDS, dbInstanceIdentifier, roleArn string) (*rds.DBInstanceRole, error) {
	input := &rds.DescribeDBInstancesInput{
		DBInstanceIdentifier: aws.String(dbInstanceIdentifier),
	}

	log.Printf("[DEBUG] Describing RDS DB Instance: %s", input)
	output, err := conn.DescribeDBInstances(input)

	if err != nil {
		return nil, err
	}

	var dbInstance *rds.DBInstance

	for _, outputDbInstance := range output.DBInstances {
		if aws.StringValue(outputDbInstance.DBInstanceIdentifier) == dbInstanceIdentifier {
			dbInstance = outputDbInstance
			break
		}
	}

	if dbInstance == nil {
		return nil, nil
	}

	var dbInstanceRole *rds.DBInstanceRole

	for _, associatedRole := range dbInstance.AssociatedRoles {
		if aws.StringValue(associatedRole.RoleArn) == roleArn {
			dbInstanceRole = associatedRole
			break
		}
	}

	return dbInstanceRole, nil
}

func waitForRdsDbInstanceRoleAssociation(conn *rds.RDS, dbInstanceIdentifier, roleArn string) error {
	stateConf := &resource.StateChangeConf{
		Pending: []string{rdsDbInstanceRoleStatusPending},
		Target:  []string{rdsDbInstanceRoleStatusActive},
		Refresh: func() (interface{}, string, error) {
			dbInstanceRole, err := rdsDescribeDbInstanceRole(conn, dbInstanceIdentifier, roleArn)

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

			return dbInstanceRole, aws.StringValue(dbInstanceRole.Status), nil
		},
		Timeout: 5 * time.Minute,
		Delay:   5 * time.Second,
	}

	log.Printf("[DEBUG] Waiting for RDS DB Instance (%s) IAM Role association: %s", dbInstanceIdentifier, roleArn)
	_, err := stateConf.WaitForState()

	return err
}

func waitForRdsDbInstanceRoleDisassociation(conn *rds.RDS, dbInstanceIdentifier, roleArn string) error {
	stateConf := &resource.StateChangeConf{
		Pending: []string{
			rdsDbInstanceRoleStatusActive,
			rdsDbInstanceRoleStatusPending,
		},
		Target: []string{rdsDbInstanceRoleStatusDeleted},
		Refresh: func() (interface{}, string, error) {
			dbInstanceRole, err := rdsDescribeDbInstanceRole(conn, dbInstanceIdentifier, roleArn)

			if isAWSErr(err, rds.ErrCodeDBInstanceNotFoundFault, "") {
				return &rds.DBInstanceRole{}, rdsDbInstanceRoleStatusDeleted, nil
			}

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

			if dbInstanceRole != nil {
				return dbInstanceRole, aws.StringValue(dbInstanceRole.Status), nil
			}

			return &rds.DBInstanceRole{}, rdsDbInstanceRoleStatusDeleted, nil
		},
		Timeout: 5 * time.Minute,
		Delay:   5 * time.Second,
	}

	log.Printf("[DEBUG] Waiting for RDS DB Instance (%s) IAM Role disassociation: %s", dbInstanceIdentifier, roleArn)
	_, err := stateConf.WaitForState()

	return err
}
<commit_msg>resource/aws_db_instance_role_association: Prevent immediate read after creation panic (#13927)<commit_after>package aws

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/rds"
	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

// Constants not currently provided by the AWS Go SDK
const (
	rdsDbInstanceRoleStatusActive  = "ACTIVE"
	rdsDbInstanceRoleStatusDeleted = "DELETED"
	rdsDbInstanceRoleStatusPending = "PENDING"
)

func resourceAwsDbInstanceRoleAssociation() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsDbInstanceRoleAssociationCreate,
		Read:   resourceAwsDbInstanceRoleAssociationRead,
		Delete: resourceAwsDbInstanceRoleAssociationDelete,

		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"db_instance_identifier": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"feature_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"role_arn": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateArn,
			},
		},
	}
}

func resourceAwsDbInstanceRoleAssociationCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).rdsconn

	dbInstanceIdentifier := d.Get("db_instance_identifier").(string)
	roleArn := d.Get("role_arn").(string)

	input := &rds.AddRoleToDBInstanceInput{
		DBInstanceIdentifier: aws.String(dbInstanceIdentifier),
		FeatureName:          aws.String(d.Get("feature_name").(string)),
		RoleArn:              aws.String(roleArn),
	}

	log.Printf("[DEBUG] RDS DB Instance (%s) IAM Role associating: %s", dbInstanceIdentifier, roleArn)
	_, err := conn.AddRoleToDBInstance(input)

	if err != nil {
		return fmt.Errorf("error associating RDS DB Instance (%s) IAM Role (%s): %s", dbInstanceIdentifier, roleArn, err)
	}

	d.SetId(fmt.Sprintf("%s,%s", dbInstanceIdentifier, roleArn))

	if err := waitForRdsDbInstanceRoleAssociation(conn, dbInstanceIdentifier, roleArn); err != nil {
		return fmt.Errorf("error waiting for RDS DB Instance (%s) IAM Role (%s) association: %s", dbInstanceIdentifier, roleArn, err)
	}

	return resourceAwsDbInstanceRoleAssociationRead(d, meta)
}

func resourceAwsDbInstanceRoleAssociationRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).rdsconn

	dbInstanceIdentifier, roleArn, err := resourceAwsDbInstanceRoleAssociationDecodeId(d.Id())

	if err != nil {
		return fmt.Errorf("error reading resource ID: %s", err)
	}

	dbInstanceRole, err := rdsDescribeDbInstanceRole(conn, dbInstanceIdentifier, roleArn)

	if isAWSErr(err, rds.ErrCodeDBInstanceNotFoundFault, "") {
		log.Printf("[WARN] RDS DB Instance (%s) not found, removing from state", dbInstanceIdentifier)
		d.SetId("")
		return nil
	}

	if err != nil {
		return fmt.Errorf("error reading RDS DB Instance (%s) IAM Role (%s) association: %s", dbInstanceIdentifier, roleArn, err)
	}

	if dbInstanceRole == nil {
		log.Printf("[WARN] RDS DB Instance (%s) IAM Role (%s) association not found, removing from state", dbInstanceIdentifier, roleArn)
		d.SetId("")
		return nil
	}

	d.Set("db_instance_identifier", dbInstanceIdentifier)
	d.Set("feature_name", dbInstanceRole.FeatureName)
	d.Set("role_arn", dbInstanceRole.RoleArn)

	return nil
}

func resourceAwsDbInstanceRoleAssociationDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).rdsconn

	dbInstanceIdentifier, roleArn, err := resourceAwsDbInstanceRoleAssociationDecodeId(d.Id())

	if err != nil {
		return fmt.Errorf("error reading resource ID: %s", err)
	}

	input := &rds.RemoveRoleFromDBInstanceInput{
		DBInstanceIdentifier: aws.String(dbInstanceIdentifier),
		FeatureName:          aws.String(d.Get("feature_name").(string)),
		RoleArn:              aws.String(roleArn),
	}

	log.Printf("[DEBUG] RDS DB Instance (%s) IAM Role disassociating: %s", dbInstanceIdentifier, roleArn)
	_, err = conn.RemoveRoleFromDBInstance(input)

	if isAWSErr(err, rds.ErrCodeDBInstanceNotFoundFault, "") {
		return nil
	}

	if isAWSErr(err, rds.ErrCodeDBInstanceRoleNotFoundFault, "") {
		return nil
	}

	if err != nil {
		return fmt.Errorf("error disassociating RDS DB Instance (%s) IAM Role (%s): %s", dbInstanceIdentifier, roleArn, err)
	}

	if err := waitForRdsDbInstanceRoleDisassociation(conn, dbInstanceIdentifier, roleArn); err != nil {
		return fmt.Errorf("error waiting for RDS DB Instance (%s) IAM Role (%s) disassociation: %s", dbInstanceIdentifier, roleArn, err)
	}

	return nil
}

func resourceAwsDbInstanceRoleAssociationDecodeId(id string) (string, string, error) {
	parts := strings.SplitN(id, ",", 2)

	if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
		return "", "", fmt.Errorf("unexpected format of ID (%s), expected DB-INSTANCE-ID,ROLE-ARN", id)
	}

	return parts[0], parts[1], nil
}

func rdsDescribeDbInstanceRole(conn *rds.RDS, dbInstanceIdentifier, roleArn string) (*rds.DBInstanceRole, error) {
	input := &rds.DescribeDBInstancesInput{
		DBInstanceIdentifier: aws.String(dbInstanceIdentifier),
	}

	log.Printf("[DEBUG] Describing RDS DB Instance: %s", input)
	output, err := conn.DescribeDBInstances(input)

	if err != nil {
		return nil, err
	}

	var dbInstance *rds.DBInstance

	for _, outputDbInstance := range output.DBInstances {
		if aws.StringValue(outputDbInstance.DBInstanceIdentifier) == dbInstanceIdentifier {
			dbInstance = outputDbInstance
			break
		}
	}

	if dbInstance == nil {
		return nil, nil
	}

	var dbInstanceRole *rds.DBInstanceRole

	for _, associatedRole := range dbInstance.AssociatedRoles {
		if aws.StringValue(associatedRole.RoleArn) == roleArn {
			dbInstanceRole = associatedRole
			break
		}
	}

	return dbInstanceRole, nil
}

func waitForRdsDbInstanceRoleAssociation(conn *rds.RDS, dbInstanceIdentifier, roleArn string) error {
	stateConf := &resource.StateChangeConf{
		Pending: []string{rdsDbInstanceRoleStatusPending},
		Target:  []string{rdsDbInstanceRoleStatusActive},
		Refresh: func() (interface{}, string, error) {
			dbInstanceRole, err := rdsDescribeDbInstanceRole(conn, dbInstanceIdentifier, roleArn)

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

			if dbInstanceRole == nil {
				return nil, rdsDbInstanceRoleStatusPending, nil
			}

			return dbInstanceRole, aws.StringValue(dbInstanceRole.Status), nil
		},
		Timeout: 5 * time.Minute,
		Delay:   5 * time.Second,
	}

	log.Printf("[DEBUG] Waiting for RDS DB Instance (%s) IAM Role association: %s", dbInstanceIdentifier, roleArn)
	_, err := stateConf.WaitForState()

	return err
}

func waitForRdsDbInstanceRoleDisassociation(conn *rds.RDS, dbInstanceIdentifier, roleArn string) error {
	stateConf := &resource.StateChangeConf{
		Pending: []string{
			rdsDbInstanceRoleStatusActive,
			rdsDbInstanceRoleStatusPending,
		},
		Target: []string{rdsDbInstanceRoleStatusDeleted},
		Refresh: func() (interface{}, string, error) {
			dbInstanceRole, err := rdsDescribeDbInstanceRole(conn, dbInstanceIdentifier, roleArn)

			if isAWSErr(err, rds.ErrCodeDBInstanceNotFoundFault, "") {
				return &rds.DBInstanceRole{}, rdsDbInstanceRoleStatusDeleted, nil
			}

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

			if dbInstanceRole != nil {
				return dbInstanceRole, aws.StringValue(dbInstanceRole.Status), nil
			}

			return &rds.DBInstanceRole{}, rdsDbInstanceRoleStatusDeleted, nil
		},
		Timeout: 5 * time.Minute,
		Delay:   5 * time.Second,
	}

	log.Printf("[DEBUG] Waiting for RDS DB Instance (%s) IAM Role disassociation: %s", dbInstanceIdentifier, roleArn)
	_, err := stateConf.WaitForState()

	return err
}
<|endoftext|>


<commit_before>package tlj

import (
//	"net"
	"reflect"
//	"errors"
	"encoding/json"
	"encoding/binary"
	"testing"
)

type Thingy struct {
	Name	string
	ID		int
}

func BuildThingy(data []byte) interface{} {
		thing := &Thingy{}
		err := json.Unmarshal(data, &thing)
		if err != nil { return nil }
		return thing
}

func TestTypeStoreIsCorrectType(t *testing.T) {
	type_store := NewTypeStore()
	if reflect.TypeOf(type_store) != reflect.TypeOf(TypeStore{}) {
		t.Errorf("return value of NewTypeStore() != tlj.TypeStore")
	}
}

func TestTypeStoreHasCapsuleBuilder(t *testing.T) {
	type_store := NewTypeStore()
	cap := Capsule {
		RequestID:	1,
		Type:		1,
		Data:		"test",
	}
	cap_bytes, _ := json.Marshal(cap)
	iface := type_store.BuildType(0, cap_bytes)
	if restored, ok := iface.(*Capsule); ok {
		if restored.RequestID != cap.RequestID {
			t.Errorf("capsule builder did not restore RequestID")
		}
		if restored.Type != cap.Type {
			t.Errorf("capsule builder did not restore Type")
		}
		if restored.Data != cap.Data {
			t.Errorf("capsule builder did not restore Data")
		}
	} else {
		t.Errorf("could not assert *Capsule type on restored interface")
	}
}

func TestTypeStoreCanAddType(t *testing.T) {
	type_store := NewTypeStore()
	thingy_type := reflect.TypeOf(Thingy{})
	type_store.AddType(thingy_type, BuildThingy)
	if type_store.TypeCodes[thingy_type] != 1 {
		t.Errorf("call to AddType on new TypeStore did not create type_id of 1")
	}
}

func TestTypeStoreCanLookupCode(t *testing.T) {
	type_store := NewTypeStore()
	code, present := type_store.LookupCode(reflect.TypeOf(Capsule{}))
	if code != 0 || !present {
		t.Errorf("unable to lookup type_code for Capsule")
	}
}

func TestTypeStoreWontLookupBadCode(t *testing.T) {
	type_store := NewTypeStore()
	_, present := type_store.LookupCode(reflect.TypeOf(Thingy{}))
	if present {
		t.Errorf("nonexistent type returns a code")
	}
}

func TestTypeStoreCanBuildType(t *testing.T) {
	type_store := NewTypeStore()
	thingy_type := reflect.TypeOf(Thingy{})
	type_store.AddType(thingy_type, BuildThingy)
	if type_store.TypeCodes[thingy_type] != 1 {
		t.Errorf("call to AddType on new TypeStore did not create type_id of 1")
	}
	thingy := Thingy {
		Name:	"test",
		ID:		1,
	}
	marshalled, err := json.Marshal(thingy)
	if err != nil {
		t.Errorf("marshalling thingy returned an error")
	}
	iface := type_store.BuildType(1, marshalled)
	if restored, ok := iface.(*Thingy); ok {
		if restored.Name != thingy.Name {
			t.Errorf("string not presevered when building from marshalled struct")
		}
		if restored.ID != thingy.ID {
			t.Errorf("int not presevered when building from marshalled struct")
		}
	} else {
		t.Errorf("could not assert *Thingy type on restored interface")
	}
}

func TestTypeStoreWontBuildBadType(t *testing.T) {
	type_store := NewTypeStore()
	iface := type_store.BuildType(1, make([]byte, 0))
	if iface != nil {
		t.Errorf("type_store built something with a nonexistent id")
	}
}

func TestTypeStoreWontBuildUnformattedData(t *testing.T) {
	type_store := NewTypeStore()
	iface := type_store.BuildType(0, []byte("notjson"))
	if iface != nil {
		t.Errorf("type_store built something when bad data was supplied")
	}
}

func TestFormat(t *testing.T) {
	type_store := NewTypeStore()
	type_store.AddType(reflect.TypeOf(Thingy{}), BuildThingy)
	thing := Thingy {
		Name:	"test",
		ID:		1,
	}
	bytes, err := format(thing, &type_store)
	if err != nil {
		t.Errorf("error formatting valid struct: %s", err)
	}
	type_bytes := bytes[:2]
	size_bytes := bytes[2:6]
	json_data := bytes[6:]
	type_int := binary.LittleEndian.Uint16(type_bytes)
	size_int := binary.LittleEndian.Uint32(size_bytes)
	if type_int != 1 {
		t.Errorf("format didn't use the correct type ID")
	}
	if int(size_int) != len(json_data) {
		t.Errorf("format didn't set the correct length")
	}
	restored_thing := &Thingy{}
	err = json.Unmarshal(json_data, &restored_thing)
	if err != nil {
		t.Errorf("error unmarahalling format data: %s", err)
	}
}

func TestCantFormatUnknownType(t *testing.T) {
	type_store := NewTypeStore()
	thing := Thingy {
		Name:	"test",
		ID:		1,
	}
	_, err := format(thing, &type_store)
	if err == nil {
		t.Errorf("format didn't return error when unknown type was passed in")
	}
}

/*
func TestFormatCapsule(t *testing.T) {

}

func TestCantFormatCapsuleWithUnknownType(t *testing.T) {

}

func TestNextStruct(t *testing.T) {

}

func TestNextStructErrorWithBrokenSocket(t *testing.T) {

}

func TestNextStructNilWhenMissing(t *testing.T) {

}

func TestNextStructNilWhenHeaderTooSmall(t *testing.T) {

}
*/
<commit_msg>formatCapsule tests<commit_after>package tlj

import (
//	"net"
	"reflect"
//	"errors"
	"encoding/json"
	"encoding/binary"
	"testing"
)

type Thingy struct {
	Name	string
	ID		int
}

func BuildThingy(data []byte) interface{} {
		thing := &Thingy{}
		err := json.Unmarshal(data, &thing)
		if err != nil { return nil }
		return thing
}

func TestTypeStoreIsCorrectType(t *testing.T) {
	type_store := NewTypeStore()
	if reflect.TypeOf(type_store) != reflect.TypeOf(TypeStore{}) {
		t.Errorf("return value of NewTypeStore() != tlj.TypeStore")
	}
}

func TestTypeStoreHasCapsuleBuilder(t *testing.T) {
	type_store := NewTypeStore()
	cap := Capsule {
		RequestID:	1,
		Type:		1,
		Data:		"test",
	}
	cap_bytes, _ := json.Marshal(cap)
	iface := type_store.BuildType(0, cap_bytes)
	if restored, ok := iface.(*Capsule); ok {
		if restored.RequestID != cap.RequestID {
			t.Errorf("capsule builder did not restore RequestID")
		}
		if restored.Type != cap.Type {
			t.Errorf("capsule builder did not restore Type")
		}
		if restored.Data != cap.Data {
			t.Errorf("capsule builder did not restore Data")
		}
	} else {
		t.Errorf("could not assert *Capsule type on restored interface")
	}
}

func TestTypeStoreCanAddType(t *testing.T) {
	type_store := NewTypeStore()
	thingy_type := reflect.TypeOf(Thingy{})
	type_store.AddType(thingy_type, BuildThingy)
	if type_store.TypeCodes[thingy_type] != 1 {
		t.Errorf("call to AddType on new TypeStore did not create type_id of 1")
	}
}

func TestTypeStoreCanLookupCode(t *testing.T) {
	type_store := NewTypeStore()
	code, present := type_store.LookupCode(reflect.TypeOf(Capsule{}))
	if code != 0 || !present {
		t.Errorf("unable to lookup type_code for Capsule")
	}
}

func TestTypeStoreWontLookupBadCode(t *testing.T) {
	type_store := NewTypeStore()
	_, present := type_store.LookupCode(reflect.TypeOf(Thingy{}))
	if present {
		t.Errorf("nonexistent type returns a code")
	}
}

func TestTypeStoreCanBuildType(t *testing.T) {
	type_store := NewTypeStore()
	thingy_type := reflect.TypeOf(Thingy{})
	type_store.AddType(thingy_type, BuildThingy)
	if type_store.TypeCodes[thingy_type] != 1 {
		t.Errorf("call to AddType on new TypeStore did not create type_id of 1")
	}
	thingy := Thingy {
		Name:	"test",
		ID:		1,
	}
	marshalled, err := json.Marshal(thingy)
	if err != nil {
		t.Errorf("marshalling thingy returned an error")
	}
	iface := type_store.BuildType(1, marshalled)
	if restored, ok := iface.(*Thingy); ok {
		if restored.Name != thingy.Name {
			t.Errorf("string not presevered when building from marshalled struct")
		}
		if restored.ID != thingy.ID {
			t.Errorf("int not presevered when building from marshalled struct")
		}
	} else {
		t.Errorf("could not assert *Thingy type on restored interface")
	}
}

func TestTypeStoreWontBuildBadType(t *testing.T) {
	type_store := NewTypeStore()
	iface := type_store.BuildType(1, make([]byte, 0))
	if iface != nil {
		t.Errorf("type_store built something with a nonexistent id")
	}
}

func TestTypeStoreWontBuildUnformattedData(t *testing.T) {
	type_store := NewTypeStore()
	iface := type_store.BuildType(0, []byte("notjson"))
	if iface != nil {
		t.Errorf("type_store built something when bad data was supplied")
	}
}

func TestFormat(t *testing.T) {
	type_store := NewTypeStore()
	type_store.AddType(reflect.TypeOf(Thingy{}), BuildThingy)
	thing := Thingy {
		Name:	"test",
		ID:		1,
	}
	bytes, err := format(thing, &type_store)
	if err != nil {
		t.Errorf("error formatting valid struct: %s", err)
	}
	type_bytes := bytes[:2]
	size_bytes := bytes[2:6]
	json_data := bytes[6:]
	type_int := binary.LittleEndian.Uint16(type_bytes)
	size_int := binary.LittleEndian.Uint32(size_bytes)
	if type_int != 1 {
		t.Errorf("format didn't use the correct type ID")
	}
	if int(size_int) != len(json_data) {
		t.Errorf("format didn't set the correct length")
	}
	restored_thing := &Thingy{}
	err = json.Unmarshal(json_data, &restored_thing)
	if err != nil {
		t.Errorf("error unmarahalling format data: %s", err)
	}
}

func TestCantFormatUnknownType(t *testing.T) {
	type_store := NewTypeStore()
	thing := Thingy {
		Name:	"test",
		ID:		1,
	}
	_, err := format(thing, &type_store)
	if err == nil {
		t.Errorf("format didn't return error when unknown type was passed in")
	}
}

func TestFormatCapsule(t *testing.T) {
	type_store := NewTypeStore()
	type_store.AddType(reflect.TypeOf(Thingy{}), BuildThingy)
	thing := Thingy {
		Name:	"test",
		ID:		1,
	}
	bytes, err := formatCapsule(thing, &type_store, 1)
	if err != nil {
		t.Errorf("error formatting capsule with a thingy: %s", err)
	}
	type_bytes := bytes[:2]
	size_bytes := bytes[2:6]
	capsule_data := bytes[6:]
	type_int := binary.LittleEndian.Uint16(type_bytes)
	size_int := binary.LittleEndian.Uint32(size_bytes)
	if type_int != 0 {
		t.Errorf("formatCapsule didn't use the correct type ID")
	}
	if int(size_int) != len(capsule_data) {
		t.Errorf("formatCapsule didn't set the correct length")
	}
	restored_capsule := &Capsule{}
	err = json.Unmarshal(capsule_data, &restored_capsule)
	if err != nil {
		t.Errorf("could not unmarshal formatted capsule")
	}
	if restored_capsule.RequestID != 1 {
		t.Errorf("formatCapsule didn't set the correct RequestID")
	}
	if restored_capsule.Type != 1 {
		t.Errorf("formatCapsule didn't set the correct Type")
	}
	restored_thing := &Thingy{}
	err = json.Unmarshal([]byte(restored_capsule.Data), &restored_thing)
	if err != nil {
		t.Errorf("could not unmarshal capsule data")
	}
	if restored_thing.Name != "test" {
		t.Errorf("capsuled thingy didn't come back with same name")
	}
	if restored_thing.ID != 1 {
		t.Errorf("capsuled thingy didn't come back with same ID")
	}
}

func TestCantFormatCapsuleWithUnknownType(t *testing.T) {
	type_store := NewTypeStore()
	thing := Thingy {
		Name:	"test",
		ID:		1,
	}
	_, err := formatCapsule(thing, &type_store, 1)
	if err == nil {
		t.Errorf("formatCapsule didn't error when it recieved a bad type")
	}
}

/*
func TestNextStruct(t *testing.T) {

}

func TestNextStructErrorWithBrokenSocket(t *testing.T) {

}

func TestNextStructNilWhenMissing(t *testing.T) {

}

func TestNextStructNilWhenHeaderTooSmall(t *testing.T) {

}
*/
<|endoftext|>


<commit_before>// This file contains code taken from the Go source code.
// Copyright from original file included here:
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the GO_LICENSE file.

package log5go

// These tests are too simple.

import (
	"bytes"
	"os"
	"regexp"
	"testing"
)

const (
	Rdate         = `[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]`
	Rtime         = `[0-9][0-9]:[0-9][0-9]:[0-9][0-9]`
	Rmicroseconds = `\.[0-9][0-9][0-9][0-9][0-9][0-9]`
	Rline         = `(54|56):` // must update if the calls to l.Printf / l.Print below move
	Rlongfile     = `.*/[A-Za-z0-9_\-]+\.go:` + Rline
	Rshortfile    = `[A-Za-z0-9_\-]+\.go:` + Rline
)

type tester struct {
	flag    int
	prefix  string
	pattern string // regexp that log output must match; we add ^ and expected_text$ always
}

var tests = []tester{
	// individual pieces:
	{0, "", ""},
	{0, "XXX", "XXX"},
	{Ldate, "", Rdate + " "},
	{Ltime, "", Rtime + " "},
	{Ltime | Lmicroseconds, "", Rtime + Rmicroseconds + " "},
	{Lmicroseconds, "", Rtime + Rmicroseconds + " "}, // microsec implies time
	{Llongfile, "", Rlongfile + " "},
	{Lshortfile, "", Rshortfile + " "},
	{Llongfile | Lshortfile, "", Rshortfile + " "}, // shortfile overrides longfile
	// everything at once:
	{Ldate | Ltime | Lmicroseconds | Llongfile, "XXX", "XXX" + Rdate + " " + Rtime + Rmicroseconds + " " + Rlongfile + " "},
	{Ldate | Ltime | Lmicroseconds | Lshortfile, "XXX", "XXX" + Rdate + " " + Rtime + Rmicroseconds + " " + Rshortfile + " "},
}

// Test using Println("hello", 23, "world") or using Printf("hello %d world", 23)
func testPrint(t *testing.T, flag int, prefix string, pattern string, useFormat bool) {
	buf := new(bytes.Buffer)
	SetOutput(buf)
	SetFlags(flag)
	SetPrefix(prefix)
	if useFormat {
		Printf("hello %d world", 23)
	} else {
		Println("hello", 23, "world")
	}
	line := buf.String()
	line = line[0 : len(line)-1]
	pattern = "^" + pattern + "hello 23 world$"
	matched, err4 := regexp.MatchString(pattern, line)
	if err4 != nil {
		t.Fatal("pattern did not compile:", err4)
	}
	if !matched {
		t.Errorf("log output should match %q is %q", pattern, line)
	}
	SetOutput(os.Stderr)
}

func TestAll(t *testing.T) {
	for _, testcase := range tests {
		testPrint(t, testcase.flag, testcase.prefix, testcase.pattern, false)
		testPrint(t, testcase.flag, testcase.prefix, testcase.pattern, true)
	}
}

func TestOutput(t *testing.T) {
	const testString = "test"
	var b bytes.Buffer
	l := New(&b, "", 0)
	l.Println(testString)
	if expect := testString + "\n"; b.String() != expect {
		t.Errorf("log output should match %q is %q", expect, b.String())
	}
}

func TestFlagAndPrefixSetting(t *testing.T) {
	var b bytes.Buffer
	l := New(&b, "Test:", LstdFlags)
	f := l.Flags()
	if f != LstdFlags {
		t.Errorf("Flags 1: expected %x got %x", LstdFlags, f)
	}
	l.SetFlags(f | Lmicroseconds)
	f = l.Flags()
	if f != LstdFlags|Lmicroseconds {
		t.Errorf("Flags 2: expected %x got %x", LstdFlags|Lmicroseconds, f)
	}
	p := l.Prefix()
	if p != "Test:" {
		t.Errorf(`Prefix: expected "Test:" got %q`, p)
	}
	l.SetPrefix("Reality:")
	p = l.Prefix()
	if p != "Reality:" {
		t.Errorf(`Prefix: expected "Reality:" got %q`, p)
	}
	// Verify a log message looks right, with our prefix and microseconds present.
	l.Print("hello")
	pattern := "^Reality:" + Rdate + " " + Rtime + Rmicroseconds + " hello\n"
	matched, err := regexp.Match(pattern, b.Bytes())
	if err != nil {
		t.Fatalf("pattern %q did not compile: %s", pattern, err)
	}
	if !matched {
		t.Error("message did not match pattern")
	}
}
<commit_msg>fix for source line-dependent test<commit_after>// This file contains code taken from the Go source code.
// Copyright from original file included here:
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the GO_LICENSE file.

package log5go

// These tests are too simple.

import (
	"bytes"
	"os"
	"regexp"
	"testing"
)

const (
	Rdate         = `[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]`
	Rtime         = `[0-9][0-9]:[0-9][0-9]:[0-9][0-9]`
	Rmicroseconds = `\.[0-9][0-9][0-9][0-9][0-9][0-9]`
	Rline         = `(56|58):` // must update if the calls to l.Printf / l.Print below move
	Rlongfile     = `.*/[A-Za-z0-9_\-]+\.go:` + Rline
	Rshortfile    = `[A-Za-z0-9_\-]+\.go:` + Rline
)

type tester struct {
	flag    int
	prefix  string
	pattern string // regexp that log output must match; we add ^ and expected_text$ always
}

var tests = []tester{
	// individual pieces:
	{0, "", ""},
	{0, "XXX", "XXX"},
	{Ldate, "", Rdate + " "},
	{Ltime, "", Rtime + " "},
	{Ltime | Lmicroseconds, "", Rtime + Rmicroseconds + " "},
	{Lmicroseconds, "", Rtime + Rmicroseconds + " "}, // microsec implies time
	{Llongfile, "", Rlongfile + " "},
	{Lshortfile, "", Rshortfile + " "},
	{Llongfile | Lshortfile, "", Rshortfile + " "}, // shortfile overrides longfile
	// everything at once:
	{Ldate | Ltime | Lmicroseconds | Llongfile, "XXX", "XXX" + Rdate + " " + Rtime + Rmicroseconds + " " + Rlongfile + " "},
	{Ldate | Ltime | Lmicroseconds | Lshortfile, "XXX", "XXX" + Rdate + " " + Rtime + Rmicroseconds + " " + Rshortfile + " "},
}

// Test using Println("hello", 23, "world") or using Printf("hello %d world", 23)
func testPrint(t *testing.T, flag int, prefix string, pattern string, useFormat bool) {
	buf := new(bytes.Buffer)
	SetOutput(buf)
	SetFlags(flag)
	SetPrefix(prefix)
	if useFormat {
		Printf("hello %d world", 23)
	} else {
		Println("hello", 23, "world")
	}
	line := buf.String()
	line = line[0 : len(line)-1]
	pattern = "^" + pattern + "hello 23 world$"
	matched, err4 := regexp.MatchString(pattern, line)
	if err4 != nil {
		t.Fatal("pattern did not compile:", err4)
	}
	if !matched {
		t.Errorf("log output should match %q is %q", pattern, line)
	}
	SetOutput(os.Stderr)
}

func TestAll(t *testing.T) {
	for _, testcase := range tests {
		testPrint(t, testcase.flag, testcase.prefix, testcase.pattern, false)
		testPrint(t, testcase.flag, testcase.prefix, testcase.pattern, true)
	}
}

func TestOutput(t *testing.T) {
	const testString = "test"
	var b bytes.Buffer
	l := New(&b, "", 0)
	l.Println(testString)
	if expect := testString + "\n"; b.String() != expect {
		t.Errorf("log output should match %q is %q", expect, b.String())
	}
}

func TestFlagAndPrefixSetting(t *testing.T) {
	var b bytes.Buffer
	l := New(&b, "Test:", LstdFlags)
	f := l.Flags()
	if f != LstdFlags {
		t.Errorf("Flags 1: expected %x got %x", LstdFlags, f)
	}
	l.SetFlags(f | Lmicroseconds)
	f = l.Flags()
	if f != LstdFlags|Lmicroseconds {
		t.Errorf("Flags 2: expected %x got %x", LstdFlags|Lmicroseconds, f)
	}
	p := l.Prefix()
	if p != "Test:" {
		t.Errorf(`Prefix: expected "Test:" got %q`, p)
	}
	l.SetPrefix("Reality:")
	p = l.Prefix()
	if p != "Reality:" {
		t.Errorf(`Prefix: expected "Reality:" got %q`, p)
	}
	// Verify a log message looks right, with our prefix and microseconds present.
	l.Print("hello")
	pattern := "^Reality:" + Rdate + " " + Rtime + Rmicroseconds + " hello\n"
	matched, err := regexp.Match(pattern, b.Bytes())
	if err != nil {
		t.Fatalf("pattern %q did not compile: %s", pattern, err)
	}
	if !matched {
		t.Error("message did not match pattern")
	}
}
<|endoftext|>


<commit_before>package promise

import (
	"fmt"
	"sync"
)

type CompletablePromise struct {
	completed    bool
	rejected     bool
	cause        error
	value        interface{}
	mutex        sync.Mutex
	waitGroup    sync.WaitGroup
	compute      func(interface{}) interface{}
	handle       func(error)
	dependencies []Completable
}

func completable(compute func(interface{}) interface{}, handle func(error)) *CompletablePromise {
	completable := new(CompletablePromise)

	completable.compute = compute
	completable.handle = handle
	completable.completed = false
	completable.rejected = false
	completable.dependencies = make([]Completable, 0)
	completable.waitGroup.Add(1)

	return completable
}

// Generate a new completable promise. This provides an implementation of the
// `promise.Completable` interface which is threadsafe.
func Promise() Completable {
	return completable(func(x interface{}) interface{} { return x }, nil)
}

// Determine if the promise has been resolved.
func (promise *CompletablePromise) Resolved() bool {
	return promise.completed && !promise.rejected
}

func (promise *CompletablePromise) Rejected() bool {
	return promise.rejected
}

// Return the value of the promise, if it was resolved successfully, or return
// the cause of failure if it was not. Block until the promise is either
// completed or rejected.
func (promise *CompletablePromise) Get() (interface{}, error) {
	if !promise.completed {
		promise.waitGroup.Wait()
	}

	return promise.value, promise.cause
}

func (promise *CompletablePromise) depend(compute func(interface{}) interface{}) Thenable {
	andThen := completable(compute, nil)

	promise.dependencies = append(promise.dependencies, andThen)

	return andThen
}

// The private version of this is used for `Combine` to call, so that it won't
// attempt to acquire the mutex twice.
func (promise *CompletablePromise) then(compute func(interface{}) interface{}) Thenable {
	if !promise.completed {
		return promise.depend(compute)
	} else if promise.rejected {
		return Rejected(promise.cause)
	} else {
		return Completed(compute(promise.value))
	}
}

// Compose this promise into one which is complete when the following code has
// executed.
func (promise *CompletablePromise) Then(compute func(interface{}) interface{}) Thenable {
	if !promise.completed {
		promise.mutex.Lock()

		defer promise.mutex.Unlock()

		return promise.then(compute)
	} else if promise.rejected {
		return Rejected(promise.cause)
	} else {
		return Completed(compute(promise.value))
	}
}

// Compose this promise into another one which handles an upstream error with
// the given handler.
func (promise *CompletablePromise) Catch(handle func(error)) Thenable {
	if !promise.completed {
		promise.mutex.Lock()

		defer promise.mutex.Unlock()

		// Double check now that we have the lock that this is still true.
		if !promise.completed {
			rejectable := completable(nil, handle)

			promise.dependencies = append(promise.dependencies, rejectable)

			return rejectable
		}
	}

	if promise.rejected {
		handle(promise.cause)

		return Rejected(promise.cause)
	}

	return promise
}

// Error due to an illegal second state transition, after figuring out what
// caused the previous state transition.
func panicStateComplete(rejected bool) {
	var method string

	if rejected {
		method = "Reject()"
	} else {
		method = "Complete()"
	}

	panic(fmt.Sprintf("%s was already called on this promise", method))
}

// Complete this promise with a given value.
// It is considered a programming error to complete a promise multiple times.
// The promise is to be completed once, and not thereafter.
func (promise *CompletablePromise) Complete(value interface{}) {
	// This should rarely actually be blocking, there's a separate mutex for
	// each completable promise and the mutex is only acquired during assembly
	// and completion.
	promise.mutex.Lock()

	defer promise.mutex.Unlock()

	if promise.completed {
		panicStateComplete(promise.rejected)
	}

	composed := value

	if promise.compute != nil {
		composed = promise.compute(value)
	}

	if composed != nil {
		promise.value = composed
	}

	promise.waitGroup.Done()

	for _, dependency := range promise.dependencies {
		dependency.Complete(composed)
	}

	promise.completed = true
}

// Reject this promise and all of its dependencies.
// Reject this promise, and along with it all promises which were derived from
// it.
func (promise *CompletablePromise) Reject(cause error) {
	if cause == nil {
		panic(fmt.Sprintf("Reject() requires a non-nil cause"))
	}

	promise.mutex.Lock()

	defer promise.mutex.Unlock()

	if promise.completed {
		panicStateComplete(promise.rejected)
	}

	if promise.handle != nil {
		promise.handle(cause)
	}

	promise.waitGroup.Done()

	for _, dependency := range promise.dependencies {
		dependency.Reject(cause)
	}

	// Due to the fact that this code is a little racey (specifically,
	// completed is used as a guard), the order of these assignments is
	// important — specifically, the *completed* flag must be *last*.
	promise.cause = cause
	promise.rejected = true
	promise.completed = true
}

// Combine this promise with another by applying the combinator `create` to the
// value once it is available. `create` must return an instance of a
// `Thenable`. The instance *may* be `Completable`. Returns a new completable
// promise which is completed when the returned promise, and this promise, are
// completed...but no sooner.
func (promise *CompletablePromise) Combine(create func(interface{}) Thenable) Thenable {
	if !promise.completed {
		promise.mutex.Lock()

		defer promise.mutex.Unlock()

		if !promise.completed {
			// So, this may seem a little whacky, but what is happening here is
			// that seeing as there is presently no value from which to generate
			// the new promise, a callback is registered using Then() which
			// executes the supplied transform function, and when the promise that
			// was returned by *that* transform produces a result, it is copied
			// over to the placeholder thus satisfying the request.
			placeholder := Promise()

			// It's important that the internal then() is used here, because the
			// external one allocates a mutex lock. sync.Mutex is not a reentrant lock
			// type, unfortunately.
			promise.depend(func(awaited interface{}) interface{} {
				create(awaited).Then(func(composed interface{}) interface{} {
					placeholder.Complete(composed)

					return nil
				}).Catch(func(err error) {
					placeholder.Reject(err)
				})

				return nil
			}).Catch(func(err error) {
				placeholder.Reject(err)
			})

			return placeholder
		}
	}

	if promise.rejected {
		return Rejected(promise.cause)
	} else {
		return create(promise.value)
	}
}
<commit_msg>Converted truth-table to enumeration of state<commit_after>package promise

import (
	"fmt"
	"sync"
)

type State uint8

const (
	PENDING State = iota
	FULFILLED
	REJECTED
)

type CompletablePromise struct {
	state        State
	cause        error
	value        interface{}
	mutex        sync.Mutex
	waitGroup    sync.WaitGroup
	compute      func(interface{}) interface{}
	handle       func(error)
	dependencies []Completable
}

func completable(compute func(interface{}) interface{}, handle func(error)) *CompletablePromise {
	completable := new(CompletablePromise)

	completable.compute = compute
	completable.handle = handle
	completable.state = PENDING
	completable.dependencies = make([]Completable, 0)
	completable.waitGroup.Add(1)

	return completable
}

// Generate a new completable promise. This provides an implementation of the
// `promise.Completable` interface which is threadsafe.
func Promise() Completable {
	return completable(func(x interface{}) interface{} { return x }, nil)
}

// Determine if the promise has been resolved.
func (promise *CompletablePromise) Resolved() bool {
	return promise.state == FULFILLED
}

func (promise *CompletablePromise) Rejected() bool {
	return promise.state == REJECTED
}

// Return the value of the promise, if it was resolved successfully, or return
// the cause of failure if it was not. Block until the promise is either
// completed or rejected.
func (promise *CompletablePromise) Get() (interface{}, error) {
	if promise.state == PENDING {
		promise.waitGroup.Wait()
	}

	return promise.value, promise.cause
}

func (promise *CompletablePromise) depend(compute func(interface{}) interface{}) Thenable {
	andThen := completable(compute, nil)

	promise.dependencies = append(promise.dependencies, andThen)

	return andThen
}

// The private version of this is used for `Combine` to call, so that it won't
// attempt to acquire the mutex twice.
func (promise *CompletablePromise) then(compute func(interface{}) interface{}) Thenable {
	switch promise.state {
	case PENDING:
		return promise.depend(compute)
	case REJECTED:
		return Rejected(promise.cause)
	case FULFILLED:
		return Completed(compute(promise.value))
	}

	panic("Invalid state")
}

// Compose this promise into one which is complete when the following code has
// executed.
func (promise *CompletablePromise) Then(compute func(interface{}) interface{}) Thenable {
	switch promise.state {
	case PENDING:
		promise.mutex.Lock()

		defer promise.mutex.Unlock()

		return promise.then(compute)
	case REJECTED:
		return Rejected(promise.cause)
	case FULFILLED:
		return Completed(compute(promise.value))
	}

	panic("Invalid state")
}

// Compose this promise into another one which handles an upstream error with
// the given handler.
func (promise *CompletablePromise) Catch(handle func(error)) Thenable {
	if promise.state == PENDING {
		promise.mutex.Lock()

		defer promise.mutex.Unlock()

		// Double check now that we have the lock that this is still true.
		if promise.state == PENDING {
			rejectable := completable(nil, handle)

			promise.dependencies = append(promise.dependencies, rejectable)

			return rejectable
		}
	}

	if promise.state == REJECTED {
		handle(promise.cause)

		return Rejected(promise.cause)
	}

	return promise
}

// Error due to an illegal second state transition, after figuring out what
// caused the previous state transition.
func panicStateComplete(rejected bool) {
	var method string

	if rejected {
		method = "Reject()"
	} else {
		method = "Complete()"
	}

	panic(fmt.Sprintf("%s was already called on this promise", method))
}

// Complete this promise with a given value.
// It is considered a programming error to complete a promise multiple times.
// The promise is to be completed once, and not thereafter.
func (promise *CompletablePromise) Complete(value interface{}) {
	// This should rarely actually be blocking, there's a separate mutex for
	// each completable promise and the mutex is only acquired during assembly
	// and completion.
	promise.mutex.Lock()

	defer promise.mutex.Unlock()

	if promise.state != PENDING {
		panicStateComplete(promise.state == REJECTED)
	}

	composed := value

	if promise.compute != nil {
		composed = promise.compute(value)
	}

	if composed != nil {
		promise.value = composed
	}

	promise.waitGroup.Done()

	for _, dependency := range promise.dependencies {
		dependency.Complete(composed)
	}

	promise.state = FULFILLED
}

// Reject this promise and all of its dependencies.
// Reject this promise, and along with it all promises which were derived from
// it.
func (promise *CompletablePromise) Reject(cause error) {
	if cause == nil {
		panic(fmt.Sprintf("Reject() requires a non-nil cause"))
	}

	promise.mutex.Lock()

	defer promise.mutex.Unlock()

	if promise.state != PENDING {
		panicStateComplete(promise.state == REJECTED)
	}

	if promise.handle != nil {
		promise.handle(cause)
	}

	promise.waitGroup.Done()

	for _, dependency := range promise.dependencies {
		dependency.Reject(cause)
	}

	// Due to the fact that this code is a little racey (specifically,
	// completed is used as a guard), the order of these assignments is
	// important — specifically, the *completed* flag must be *last*.
	promise.cause = cause
	promise.state = FULFILLED
}

// Combine this promise with another by applying the combinator `create` to the
// value once it is available. `create` must return an instance of a
// `Thenable`. The instance *may* be `Completable`. Returns a new completable
// promise which is completed when the returned promise, and this promise, are
// completed...but no sooner.
func (promise *CompletablePromise) Combine(create func(interface{}) Thenable) Thenable {
	if promise.state == PENDING {
		promise.mutex.Lock()

		defer promise.mutex.Unlock()

		if promise.state == PENDING {
			// So, this may seem a little whacky, but what is happening here is
			// that seeing as there is presently no value from which to generate
			// the new promise, a callback is registered using Then() which
			// executes the supplied transform function, and when the promise that
			// was returned by *that* transform produces a result, it is copied
			// over to the placeholder thus satisfying the request.
			placeholder := Promise()

			// It's important that the internal then() is used here, because the
			// external one allocates a mutex lock. sync.Mutex is not a reentrant lock
			// type, unfortunately.
			promise.depend(func(awaited interface{}) interface{} {
				create(awaited).Then(func(composed interface{}) interface{} {
					placeholder.Complete(composed)

					return nil
				}).Catch(func(err error) {
					placeholder.Reject(err)
				})

				return nil
			}).Catch(func(err error) {
				placeholder.Reject(err)
			})

			return placeholder
		}
	}

	if promise.state == REJECTED {
		return Rejected(promise.cause)
	} else {
		return create(promise.value)
	}
}
<|endoftext|>


<commit_before>package reverseproxy

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"sync"

	"github.com/gorilla/websocket"
	"github.com/koding/kite"
	"github.com/koding/kite/config"
	"github.com/koding/websocketproxy"
)

const (
	Version = "0.0.1"
	Name    = "proxy"
)

type Proxy struct {
	Kite *kite.Kite

	listener  net.Listener
	TLSConfig *tls.Config

	readyC chan bool // To signal when kite is ready to accept connections
	closeC chan bool // To signal when kite is closed with Close()

	// Holds registered kites. Keys are kite IDs.
	kites   map[string]*url.URL
	kitesMu sync.Mutex

	// muxer for proxy
	mux            *http.ServeMux
	websocketProxy http.Handler
	httpProxy      http.Handler

	// Proxy properties used to give urls and bind the listener
	Scheme     string
	PublicHost string // If given it must match the domain in certificate.
	Port       int
}

func New(conf *config.Config) *Proxy {
	k := kite.New(Name, Version)
	k.Config = conf

	// Listen on 3999 by default

	p := &Proxy{
		Kite:   k,
		kites:  make(map[string]*url.URL),
		readyC: make(chan bool),
		closeC: make(chan bool),
		mux:    http.NewServeMux(),
		Port:   k.Config.Port,
	}

	// third part kites are going to use this to register themself to
	// proxy-kite and get a proxy url, which they use for register to kontrol.
	p.Kite.HandleFunc("register", p.handleRegister)

	// create our websocketproxy http.handler

	p.websocketProxy = &websocketproxy.WebsocketProxy{
		Backend: p.backend,
		Upgrader: &websocket.Upgrader{
			ReadBufferSize:  4096,
			WriteBufferSize: 4096,
			CheckOrigin: func(r *http.Request) bool {
				// TODO: change this to publicdomain and also kites should add them to
				return true
			},
		},
	}

	p.mux.Handle("/", k)
	p.mux.Handle("/proxy/", p)

	// OnDisconnect is called whenever a kite is disconnected from us.
	k.OnDisconnect(func(r *kite.Client) {
		k.Log.Info("Removing kite Id '%s' from proxy. It's disconnected", r.Kite.ID)
		delete(p.kites, r.Kite.ID)
	})

	return p
}

func (p *Proxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	p.websocketProxy.ServeHTTP(rw, req)
}

func (p *Proxy) CloseNotify() chan bool {
	return p.closeC
}

func (p *Proxy) ReadyNotify() chan bool {
	return p.readyC
}

func (p *Proxy) handleRegister(r *kite.Request) (interface{}, error) {
	kiteUrl, err := url.Parse(r.Args.One().MustString())
	if err != nil {
		return nil, err
	}

	p.kites[r.Client.ID] = kiteUrl

	proxyURL := url.URL{
		Scheme:   p.Scheme,
		Host:     p.PublicHost + ":" + strconv.Itoa(p.Port),
		Path:     "proxy",
		RawQuery: "kiteId=" + r.Client.ID,
	}

	s := proxyURL.String()
	p.Kite.Log.Info("Registering kite with url: '%s'. Can be reached now with: '%s'", kiteUrl, s)

	return s, nil
}

func (p *Proxy) backend(req *http.Request) *url.URL {
	kiteId := req.URL.Query().Get("kiteId")

	p.kitesMu.Lock()
	defer p.kitesMu.Unlock()

	backendURL, ok := p.kites[kiteId]
	if !ok {
		p.Kite.Log.Error("kite for id '%s' is not found: %s", kiteId, req.URL.String())
		return nil
	}

	// change "http" with "ws" because websocket procol expects a ws or wss as
	// scheme
	if err := replaceSchemeWithWS(backendURL); err != nil {
		return nil
	}

	// change now the path for the backend kite. Kite register itself with
	// something like "localhost:7777/kite" however we are going to
	// dial/connect to a sockjs server and there is no sessionId/serverId in
	// the path. This causes problem because the SockJS serve can't parse it.
	// Therefore we as an intermediate client are getting the path as (ommited the query):
	// "/proxy/795/kite-fba0954a-07c7-4d34-4215-6a88733cf65c-OjLnvABL/websocket"
	// which will be converted to
	// "localhost:7777/kite/795/kite-fba0954a-07c7-4d34-4215-6a88733cf65c-OjLnvABL/websocket"
	backendURL.Path += strings.TrimPrefix(req.URL.Path, "/proxy")

	// also change the Origin to the client's host name, like as if someone
	// with the same backendUrl is trying to connect to the kite. Otherwise
	// will get an "Origin not allowed"
	req.Header.Set("Origin", "http://"+backendURL.Host)

	p.Kite.Log.Info("Returning backend url: '%s' for kiteId: %s", backendURL.String(), kiteId)
	return backendURL
}

// TODO: put this into a util package, is used by others too
func replaceSchemeWithWS(u *url.URL) error {
	switch u.Scheme {
	case "http":
		u.Scheme = "ws"
	case "https":
		u.Scheme = "wss"
	default:
		return fmt.Errorf("invalid scheme in url: %s", u.Scheme)
	}
	return nil
}

// ListenAndServe listens on the TCP network address addr and then calls Serve
// with handler to handle requests on incoming connections.
func (p *Proxy) ListenAndServe() error {
	var err error
	p.listener, err = net.Listen("tcp4",
		net.JoinHostPort(p.Kite.Config.IP, strconv.Itoa(p.Kite.Config.Port)))
	if err != nil {
		return err
	}
	p.Kite.Log.Info("Listening on: %s", p.listener.Addr().String())

	close(p.readyC)

	server := http.Server{
		Handler: p.mux,
	}

	defer close(p.closeC)
	return server.Serve(p.listener)
}

func (p *Proxy) ListenAndServeTLS(certFile, keyFile string) error {
	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		p.Kite.Log.Fatal("Could not load cert/key files: %s", err.Error())
	}

	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{cert},
	}

	p.listener, err = net.Listen("tcp",
		net.JoinHostPort(p.Kite.Config.IP, strconv.Itoa(p.Kite.Config.Port)))
	if err != nil {
		p.Kite.Log.Fatal(err.Error())
	}
	p.Kite.Log.Info("Listening on: %s", p.listener.Addr().String())

	// now we are ready
	close(p.readyC)

	p.listener = tls.NewListener(p.listener, tlsConfig)

	server := &http.Server{
		Handler:   p.mux,
		TLSConfig: tlsConfig,
	}

	defer close(p.closeC)
	return server.Serve(p.listener)
}

func (p *Proxy) Run() {
	p.ListenAndServe()
}
<commit_msg>reverseproxy: only proxy websocket if it's a websocket upgrade<commit_after>package reverseproxy

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"sync"

	"github.com/gorilla/websocket"
	"github.com/koding/kite"
	"github.com/koding/kite/config"
	"github.com/koding/websocketproxy"
)

const (
	Version = "0.0.1"
	Name    = "proxy"
)

type Proxy struct {
	Kite *kite.Kite

	listener  net.Listener
	TLSConfig *tls.Config

	readyC chan bool // To signal when kite is ready to accept connections
	closeC chan bool // To signal when kite is closed with Close()

	// Holds registered kites. Keys are kite IDs.
	kites   map[string]*url.URL
	kitesMu sync.Mutex

	// muxer for proxy
	mux            *http.ServeMux
	websocketProxy http.Handler
	httpProxy      http.Handler

	// Proxy properties used to give urls and bind the listener
	Scheme     string
	PublicHost string // If given it must match the domain in certificate.
	Port       int
}

func New(conf *config.Config) *Proxy {
	k := kite.New(Name, Version)
	k.Config = conf

	// Listen on 3999 by default

	p := &Proxy{
		Kite:   k,
		kites:  make(map[string]*url.URL),
		readyC: make(chan bool),
		closeC: make(chan bool),
		mux:    http.NewServeMux(),
		Port:   k.Config.Port,
	}

	// third part kites are going to use this to register themself to
	// proxy-kite and get a proxy url, which they use for register to kontrol.
	p.Kite.HandleFunc("register", p.handleRegister)

	// create our websocketproxy http.handler

	p.websocketProxy = &websocketproxy.WebsocketProxy{
		Backend: p.backend,
		Upgrader: &websocket.Upgrader{
			ReadBufferSize:  4096,
			WriteBufferSize: 4096,
			CheckOrigin: func(r *http.Request) bool {
				// TODO: change this to publicdomain and also kites should add them to
				return true
			},
		},
	}

	p.mux.Handle("/", k)
	p.mux.Handle("/proxy/", p)

	// OnDisconnect is called whenever a kite is disconnected from us.
	k.OnDisconnect(func(r *kite.Client) {
		k.Log.Info("Removing kite Id '%s' from proxy. It's disconnected", r.Kite.ID)
		delete(p.kites, r.Kite.ID)
	})

	return p
}

func (p *Proxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	if isWebsocket(req) {
		p.websocketProxy.ServeHTTP(rw, req)
	}
}

// isWebsocket checks wether the incoming request is a part of websocket
// handshake
func isWebsocket(req *http.Request) bool {
	if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
		!strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") {
		return false
	}
	return true
}

func (p *Proxy) CloseNotify() chan bool {
	return p.closeC
}

func (p *Proxy) ReadyNotify() chan bool {
	return p.readyC
}

func (p *Proxy) handleRegister(r *kite.Request) (interface{}, error) {
	kiteUrl, err := url.Parse(r.Args.One().MustString())
	if err != nil {
		return nil, err
	}

	p.kites[r.Client.ID] = kiteUrl

	proxyURL := url.URL{
		Scheme:   p.Scheme,
		Host:     p.PublicHost + ":" + strconv.Itoa(p.Port),
		Path:     "proxy",
		RawQuery: "kiteId=" + r.Client.ID,
	}

	s := proxyURL.String()
	p.Kite.Log.Info("Registering kite with url: '%s'. Can be reached now with: '%s'", kiteUrl, s)

	return s, nil
}

func (p *Proxy) backend(req *http.Request) *url.URL {
	kiteId := req.URL.Query().Get("kiteId")

	p.kitesMu.Lock()
	defer p.kitesMu.Unlock()

	backendURL, ok := p.kites[kiteId]
	if !ok {
		p.Kite.Log.Error("kite for id '%s' is not found: %s", kiteId, req.URL.String())
		return nil
	}

	// change "http" with "ws" because websocket procol expects a ws or wss as
	// scheme
	if err := replaceSchemeWithWS(backendURL); err != nil {
		return nil
	}

	// change now the path for the backend kite. Kite register itself with
	// something like "localhost:7777/kite" however we are going to
	// dial/connect to a sockjs server and there is no sessionId/serverId in
	// the path. This causes problem because the SockJS serve can't parse it.
	// Therefore we as an intermediate client are getting the path as (ommited the query):
	// "/proxy/795/kite-fba0954a-07c7-4d34-4215-6a88733cf65c-OjLnvABL/websocket"
	// which will be converted to
	// "localhost:7777/kite/795/kite-fba0954a-07c7-4d34-4215-6a88733cf65c-OjLnvABL/websocket"
	backendURL.Path += strings.TrimPrefix(req.URL.Path, "/proxy")

	// also change the Origin to the client's host name, like as if someone
	// with the same backendUrl is trying to connect to the kite. Otherwise
	// will get an "Origin not allowed"
	req.Header.Set("Origin", "http://"+backendURL.Host)

	p.Kite.Log.Info("Returning backend url: '%s' for kiteId: %s", backendURL.String(), kiteId)
	return backendURL
}

// TODO: put this into a util package, is used by others too
func replaceSchemeWithWS(u *url.URL) error {
	switch u.Scheme {
	case "http":
		u.Scheme = "ws"
	case "https":
		u.Scheme = "wss"
	default:
		return fmt.Errorf("invalid scheme in url: %s", u.Scheme)
	}
	return nil
}

// ListenAndServe listens on the TCP network address addr and then calls Serve
// with handler to handle requests on incoming connections.
func (p *Proxy) ListenAndServe() error {
	var err error
	p.listener, err = net.Listen("tcp4",
		net.JoinHostPort(p.Kite.Config.IP, strconv.Itoa(p.Kite.Config.Port)))
	if err != nil {
		return err
	}
	p.Kite.Log.Info("Listening on: %s", p.listener.Addr().String())

	close(p.readyC)

	server := http.Server{
		Handler: p.mux,
	}

	defer close(p.closeC)
	return server.Serve(p.listener)
}

func (p *Proxy) ListenAndServeTLS(certFile, keyFile string) error {
	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		p.Kite.Log.Fatal("Could not load cert/key files: %s", err.Error())
	}

	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{cert},
	}

	p.listener, err = net.Listen("tcp",
		net.JoinHostPort(p.Kite.Config.IP, strconv.Itoa(p.Kite.Config.Port)))
	if err != nil {
		p.Kite.Log.Fatal(err.Error())
	}
	p.Kite.Log.Info("Listening on: %s", p.listener.Addr().String())

	// now we are ready
	close(p.readyC)

	p.listener = tls.NewListener(p.listener, tlsConfig)

	server := &http.Server{
		Handler:   p.mux,
		TLSConfig: tlsConfig,
	}

	defer close(p.closeC)
	return server.Serve(p.listener)
}

func (p *Proxy) Run() {
	p.ListenAndServe()
}
<|endoftext|>


<commit_before>package echo

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"net/http"
	"reflect"
	"strconv"
	"strings"
)

type (
	// Binder is the interface that wraps the Bind method.
	Binder interface {
		Bind(i interface{}, c Context) error
	}

	// DefaultBinder is the default implementation of the Binder interface.
	DefaultBinder struct{}

	// BindUnmarshaler is the interface used to wrap the UnmarshalParam method.
	BindUnmarshaler interface {
		// UnmarshalParam decodes and assigns a value from an form or query param.
		UnmarshalParam(param string) error
	}
)

// Bind implements the `Binder#Bind` function.
func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) {
	req := c.Request()
	if req.ContentLength == 0 {
		if req.Method == GET {
			if err = b.bindData(i, c.QueryParams(), "query"); err != nil {
				return NewHTTPError(http.StatusBadRequest, err.Error())
			}
			return
		}
		return NewHTTPError(http.StatusBadRequest, "Request body can't be empty")
	}
	ctype := req.Header.Get(HeaderContentType)
	switch {
	case strings.HasPrefix(ctype, MIMEApplicationJSON):
		if err = json.NewDecoder(req.Body).Decode(i); err != nil {
			if ute, ok := err.(*json.UnmarshalTypeError); ok {
				return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unmarshal type error: expected=%v, got=%v, offset=%v", ute.Type, ute.Value, ute.Offset))
			} else if se, ok := err.(*json.SyntaxError); ok {
				return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: offset=%v, error=%v", se.Offset, se.Error()))
			} else {
				return NewHTTPError(http.StatusBadRequest, err.Error())
			}
		}
	case strings.HasPrefix(ctype, MIMEApplicationXML), strings.HasPrefix(ctype, MIMETextXML):
		if err = xml.NewDecoder(req.Body).Decode(i); err != nil {
			if ute, ok := err.(*xml.UnsupportedTypeError); ok {
				return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unsupported type error: type=%v, error=%v", ute.Type, ute.Error()))
			} else if se, ok := err.(*xml.SyntaxError); ok {
				return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: line=%v, error=%v", se.Line, se.Error()))
			} else {
				return NewHTTPError(http.StatusBadRequest, err.Error())
			}
		}
	case strings.HasPrefix(ctype, MIMEApplicationForm), strings.HasPrefix(ctype, MIMEMultipartForm):
		params, err := c.FormParams()
		if err != nil {
			return NewHTTPError(http.StatusBadRequest, err.Error())
		}
		if err = b.bindData(i, params, "form"); err != nil {
			return NewHTTPError(http.StatusBadRequest, err.Error())
		}
	default:
		return ErrUnsupportedMediaType
	}
	return
}

func (b *DefaultBinder) bindData(ptr interface{}, data map[string][]string, tag string) error {
	typ := reflect.TypeOf(ptr).Elem()
	val := reflect.ValueOf(ptr).Elem()

	if typ.Kind() != reflect.Struct {
		return errors.New("Binding element must be a struct")
	}

	for i := 0; i < typ.NumField(); i++ {
		typeField := typ.Field(i)
		structField := val.Field(i)
		if !structField.CanSet() {
			continue
		}
		structFieldKind := structField.Kind()
		inputFieldName := typeField.Tag.Get(tag)

		if inputFieldName == "" {
			inputFieldName = typeField.Name
			// If tag is nil, we inspect if the field is a struct.
			if _, ok := bindUnmarshaler(structField); !ok && structFieldKind == reflect.Struct {
				err := b.bindData(structField.Addr().Interface(), data, tag)
				if err != nil {
					return err
				}
				continue
			}
		}
		inputValue, exists := data[inputFieldName]
		if !exists {
			continue
		}

		// Call this first, in case we're dealing with an alias to an array type
		if ok, err := unmarshalField(typeField.Type.Kind(), inputValue[0], structField); ok {
			if err != nil {
				return err
			}
			continue
		}

		numElems := len(inputValue)
		if structFieldKind == reflect.Slice && numElems > 0 {
			sliceOf := structField.Type().Elem().Kind()
			slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
			for j := 0; j < numElems; j++ {
				if err := setWithProperType(sliceOf, inputValue[j], slice.Index(j)); err != nil {
					return err
				}
			}
			val.Field(i).Set(slice)
		} else {
			if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
				return err
			}
		}
	}
	return nil
}

func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error {
	// But also call it here, in case we're dealing with an array of BindUnmarshalers
	if ok, err := unmarshalField(valueKind, val, structField); ok {
		return err
	}

	switch valueKind {
	case reflect.Int:
		return setIntField(val, 0, structField)
	case reflect.Int8:
		return setIntField(val, 8, structField)
	case reflect.Int16:
		return setIntField(val, 16, structField)
	case reflect.Int32:
		return setIntField(val, 32, structField)
	case reflect.Int64:
		return setIntField(val, 64, structField)
	case reflect.Uint:
		return setUintField(val, 0, structField)
	case reflect.Uint8:
		return setUintField(val, 8, structField)
	case reflect.Uint16:
		return setUintField(val, 16, structField)
	case reflect.Uint32:
		return setUintField(val, 32, structField)
	case reflect.Uint64:
		return setUintField(val, 64, structField)
	case reflect.Bool:
		return setBoolField(val, structField)
	case reflect.Float32:
		return setFloatField(val, 32, structField)
	case reflect.Float64:
		return setFloatField(val, 64, structField)
	case reflect.String:
		structField.SetString(val)
	default:
		return errors.New("unknown type")
	}
	return nil
}

func unmarshalField(valueKind reflect.Kind, val string, field reflect.Value) (bool, error) {
	switch valueKind {
	case reflect.Ptr:
		return unmarshalFieldPtr(val, field)
	default:
		return unmarshalFieldNonPtr(val, field)
	}
}

// bindUnmarshaler attempts to unmarshal a reflect.Value into a BindUnmarshaler
func bindUnmarshaler(field reflect.Value) (BindUnmarshaler, bool) {
	ptr := reflect.New(field.Type())
	if ptr.CanInterface() {
		iface := ptr.Interface()
		if unmarshaler, ok := iface.(BindUnmarshaler); ok {
			return unmarshaler, ok
		}
	}
	return nil, false
}

func unmarshalFieldNonPtr(value string, field reflect.Value) (bool, error) {
	if unmarshaler, ok := bindUnmarshaler(field); ok {
		err := unmarshaler.UnmarshalParam(value)
		field.Set(reflect.ValueOf(unmarshaler).Elem())
		return true, err
	}
	return false, nil
}

func unmarshalFieldPtr(value string, field reflect.Value) (bool, error) {
	if field.IsNil() {
		// Initialize the pointer to a nil value
		field.Set(reflect.New(field.Type().Elem()))
	}
	return unmarshalFieldNonPtr(value, field.Elem())
}

func setIntField(value string, bitSize int, field reflect.Value) error {
	if value == "" {
		value = "0"
	}
	intVal, err := strconv.ParseInt(value, 10, bitSize)
	if err == nil {
		field.SetInt(intVal)
	}
	return err
}

func setUintField(value string, bitSize int, field reflect.Value) error {
	if value == "" {
		value = "0"
	}
	uintVal, err := strconv.ParseUint(value, 10, bitSize)
	if err == nil {
		field.SetUint(uintVal)
	}
	return err
}

func setBoolField(value string, field reflect.Value) error {
	if value == "" {
		value = "false"
	}
	boolVal, err := strconv.ParseBool(value)
	if err == nil {
		field.SetBool(boolVal)
	}
	return err
}

func setFloatField(value string, bitSize int, field reflect.Value) error {
	if value == "" {
		value = "0.0"
	}
	floatVal, err := strconv.ParseFloat(value, bitSize)
	if err == nil {
		field.SetFloat(floatVal)
	}
	return err
}
<commit_msg>Bind DELETE params too<commit_after>package echo

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"net/http"
	"reflect"
	"strconv"
	"strings"
)

type (
	// Binder is the interface that wraps the Bind method.
	Binder interface {
		Bind(i interface{}, c Context) error
	}

	// DefaultBinder is the default implementation of the Binder interface.
	DefaultBinder struct{}

	// BindUnmarshaler is the interface used to wrap the UnmarshalParam method.
	BindUnmarshaler interface {
		// UnmarshalParam decodes and assigns a value from an form or query param.
		UnmarshalParam(param string) error
	}
)

// Bind implements the `Binder#Bind` function.
func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) {
	req := c.Request()
	if req.ContentLength == 0 {
		if req.Method == GET || req.Method == DELETE {
			if err = b.bindData(i, c.QueryParams(), "query"); err != nil {
				return NewHTTPError(http.StatusBadRequest, err.Error())
			}
			return
		}
		return NewHTTPError(http.StatusBadRequest, "Request body can't be empty")
	}
	ctype := req.Header.Get(HeaderContentType)
	switch {
	case strings.HasPrefix(ctype, MIMEApplicationJSON):
		if err = json.NewDecoder(req.Body).Decode(i); err != nil {
			if ute, ok := err.(*json.UnmarshalTypeError); ok {
				return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unmarshal type error: expected=%v, got=%v, offset=%v", ute.Type, ute.Value, ute.Offset))
			} else if se, ok := err.(*json.SyntaxError); ok {
				return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: offset=%v, error=%v", se.Offset, se.Error()))
			} else {
				return NewHTTPError(http.StatusBadRequest, err.Error())
			}
		}
	case strings.HasPrefix(ctype, MIMEApplicationXML), strings.HasPrefix(ctype, MIMETextXML):
		if err = xml.NewDecoder(req.Body).Decode(i); err != nil {
			if ute, ok := err.(*xml.UnsupportedTypeError); ok {
				return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unsupported type error: type=%v, error=%v", ute.Type, ute.Error()))
			} else if se, ok := err.(*xml.SyntaxError); ok {
				return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: line=%v, error=%v", se.Line, se.Error()))
			} else {
				return NewHTTPError(http.StatusBadRequest, err.Error())
			}
		}
	case strings.HasPrefix(ctype, MIMEApplicationForm), strings.HasPrefix(ctype, MIMEMultipartForm):
		params, err := c.FormParams()
		if err != nil {
			return NewHTTPError(http.StatusBadRequest, err.Error())
		}
		if err = b.bindData(i, params, "form"); err != nil {
			return NewHTTPError(http.StatusBadRequest, err.Error())
		}
	default:
		return ErrUnsupportedMediaType
	}
	return
}

func (b *DefaultBinder) bindData(ptr interface{}, data map[string][]string, tag string) error {
	typ := reflect.TypeOf(ptr).Elem()
	val := reflect.ValueOf(ptr).Elem()

	if typ.Kind() != reflect.Struct {
		return errors.New("Binding element must be a struct")
	}

	for i := 0; i < typ.NumField(); i++ {
		typeField := typ.Field(i)
		structField := val.Field(i)
		if !structField.CanSet() {
			continue
		}
		structFieldKind := structField.Kind()
		inputFieldName := typeField.Tag.Get(tag)

		if inputFieldName == "" {
			inputFieldName = typeField.Name
			// If tag is nil, we inspect if the field is a struct.
			if _, ok := bindUnmarshaler(structField); !ok && structFieldKind == reflect.Struct {
				err := b.bindData(structField.Addr().Interface(), data, tag)
				if err != nil {
					return err
				}
				continue
			}
		}
		inputValue, exists := data[inputFieldName]
		if !exists {
			continue
		}

		// Call this first, in case we're dealing with an alias to an array type
		if ok, err := unmarshalField(typeField.Type.Kind(), inputValue[0], structField); ok {
			if err != nil {
				return err
			}
			continue
		}

		numElems := len(inputValue)
		if structFieldKind == reflect.Slice && numElems > 0 {
			sliceOf := structField.Type().Elem().Kind()
			slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
			for j := 0; j < numElems; j++ {
				if err := setWithProperType(sliceOf, inputValue[j], slice.Index(j)); err != nil {
					return err
				}
			}
			val.Field(i).Set(slice)
		} else {
			if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
				return err
			}
		}
	}
	return nil
}

func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error {
	// But also call it here, in case we're dealing with an array of BindUnmarshalers
	if ok, err := unmarshalField(valueKind, val, structField); ok {
		return err
	}

	switch valueKind {
	case reflect.Int:
		return setIntField(val, 0, structField)
	case reflect.Int8:
		return setIntField(val, 8, structField)
	case reflect.Int16:
		return setIntField(val, 16, structField)
	case reflect.Int32:
		return setIntField(val, 32, structField)
	case reflect.Int64:
		return setIntField(val, 64, structField)
	case reflect.Uint:
		return setUintField(val, 0, structField)
	case reflect.Uint8:
		return setUintField(val, 8, structField)
	case reflect.Uint16:
		return setUintField(val, 16, structField)
	case reflect.Uint32:
		return setUintField(val, 32, structField)
	case reflect.Uint64:
		return setUintField(val, 64, structField)
	case reflect.Bool:
		return setBoolField(val, structField)
	case reflect.Float32:
		return setFloatField(val, 32, structField)
	case reflect.Float64:
		return setFloatField(val, 64, structField)
	case reflect.String:
		structField.SetString(val)
	default:
		return errors.New("unknown type")
	}
	return nil
}

func unmarshalField(valueKind reflect.Kind, val string, field reflect.Value) (bool, error) {
	switch valueKind {
	case reflect.Ptr:
		return unmarshalFieldPtr(val, field)
	default:
		return unmarshalFieldNonPtr(val, field)
	}
}

// bindUnmarshaler attempts to unmarshal a reflect.Value into a BindUnmarshaler
func bindUnmarshaler(field reflect.Value) (BindUnmarshaler, bool) {
	ptr := reflect.New(field.Type())
	if ptr.CanInterface() {
		iface := ptr.Interface()
		if unmarshaler, ok := iface.(BindUnmarshaler); ok {
			return unmarshaler, ok
		}
	}
	return nil, false
}

func unmarshalFieldNonPtr(value string, field reflect.Value) (bool, error) {
	if unmarshaler, ok := bindUnmarshaler(field); ok {
		err := unmarshaler.UnmarshalParam(value)
		field.Set(reflect.ValueOf(unmarshaler).Elem())
		return true, err
	}
	return false, nil
}

func unmarshalFieldPtr(value string, field reflect.Value) (bool, error) {
	if field.IsNil() {
		// Initialize the pointer to a nil value
		field.Set(reflect.New(field.Type().Elem()))
	}
	return unmarshalFieldNonPtr(value, field.Elem())
}

func setIntField(value string, bitSize int, field reflect.Value) error {
	if value == "" {
		value = "0"
	}
	intVal, err := strconv.ParseInt(value, 10, bitSize)
	if err == nil {
		field.SetInt(intVal)
	}
	return err
}

func setUintField(value string, bitSize int, field reflect.Value) error {
	if value == "" {
		value = "0"
	}
	uintVal, err := strconv.ParseUint(value, 10, bitSize)
	if err == nil {
		field.SetUint(uintVal)
	}
	return err
}

func setBoolField(value string, field reflect.Value) error {
	if value == "" {
		value = "false"
	}
	boolVal, err := strconv.ParseBool(value)
	if err == nil {
		field.SetBool(boolVal)
	}
	return err
}

func setFloatField(value string, bitSize int, field reflect.Value) error {
	if value == "" {
		value = "0.0"
	}
	floatVal, err := strconv.ParseFloat(value, bitSize)
	if err == nil {
		field.SetFloat(floatVal)
	}
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"strconv"
	"strings"
)

// Speech represents the speech that will be performed for the agenda.
type speech struct {
	manualCode string
	manualName string
	speechDetails
}

// Factory method to create a speech using a manual and the speech number in that manual.
func (speech) new(manualCode string, number int) speech {
	manualCode = strings.ToLower(manualCode)
	sp := speech{manualCode: manualCode}

	manual := manualMap[manualCode]
	sp.manualName = manual.manualName
	sp.number = number

	// Do not fail with invalid input, return with the default values set.
	if number < 1 || number > len(manual.speeches) {
		log.Print("Speech number is invalid for the manual.  Manual code: "+manualCode+" speech number:", number)
		return sp
	}

	sp.name = manual.speeches[number-1].name
	sp.min = manual.speeches[number-1].min
	sp.max = manual.speeches[number-1].max

	return sp
}

// Info create a string that represents a speech ex: "#1 Ice Breaker (4-6 mins)."
func (s speech) info() string {
	return "#" + strconv.Itoa(s.number) + " " + s.name +
		" " + "(" + strconv.Itoa(s.min) + "-" + strconv.Itoa(s.max) + " mins)"
}

// Each Toastmaster manual contains 5-10 ordered and named speeches.
// The fields min and max are the minimum and maximum speaking times for a speech.
type speechDetails struct {
	number int
	name   string
	min    int
	max    int
}

// There are 16 Toastmaster manuals a speaker can use at a meeting.
type manual struct {
	manualName string
	speeches   []speechDetails
}

// Representation of the Toasmaters speeches with their code as the key.
var manualMap = map[string]manual{
	"cc": {
		manualName: "Competent Communicator",
		speeches: []speechDetails{
			{number: 1, name: "Ice Breaker", min: 4, max: 6},
			{number: 2, name: "Organize Your Speech", min: 5, max: 7},
			{number: 3, name: "Get to the Point", min: 5, max: 7},
			{number: 4, name: "How to Say It", min: 5, max: 7},
			{number: 5, name: "Your Body Speaks", min: 5, max: 7},
			{number: 6, name: "Vocal Variety", min: 5, max: 7},
			{number: 7, name: "Research Your Topic", min: 5, max: 7},
			{number: 8, name: "Visual Aids", min: 5, max: 7},
			{number: 9, name: "Persuade with Power", min: 5, max: 7},
			{number: 10, name: "Inspire Your Audience", min: 8, max: 10},
		},
	},
	"inform": {
		manualName: "Speaking to Inform",
		speeches: []speechDetails{
			{number: 1, name: "The Speech to Inform", min: 5, max: 7},
			{number: 2, name: "Resources for Informing", min: 5, max: 7},
			{number: 3, name: "The Demonstration Talk", min: 5, max: 7},
			{number: 4, name: "A Fact Finding Report", min: 5, max: 7},
			{number: 5, name: "The Abstract Concept", min: 6, max: 8},
		},
	},
	"interpretive": {
		manualName: "Interpretive Reading",
		speeches: []speechDetails{
			{number: 1, name: "Read a Story", min: 8, max: 10},
			{number: 2, name: "Interpretive Poetry", min: 6, max: 8},
			{number: 3, name: "The Monodrama", min: 5, max: 7},
			{number: 4, name: "The Play", min: 12, max: 15},
			{number: 5, name: "The Oratorical Speech", min: 8, max: 10},
		},
	},
	"mgt": {
		manualName: "Speeches By Management",
		speeches: []speechDetails{
			{number: 1, name: "The Briefing", min: 8, max: 10},
			{number: 2, name: "The Technical Speech", min: 8, max: 10},
			{number: 3, name: "Manage and Motivate", min: 10, max: 12},
			{number: 4, name: "The Status Report", min: 10, max: 12},
			{number: 5, name: "Confrontation", min: 5, max: 15},
		},
	},
	"technical": {
		manualName: "Technical Presentations",
		speeches: []speechDetails{
			{number: 1, name: "Technical Briefing", min: 8, max: 10},
			{number: 2, name: "The Proposal", min: 8, max: 10},
			{number: 3, name: "The Nontechnical Audience", min: 10, max: 12},
			{number: 4, name: "Presenting a Technical Paper", min: 10, max: 12},
			{number: 5, name: "The Team Technical Presentation", min: 20, max: 30},
		},
	},
	"storytelling": {
		manualName: "Storytelling",
		speeches: []speechDetails{
			{number: 1, name: "The Folk Tale", min: 7, max: 9},
			{number: 2, name: "Let's Get Personal", min: 6, max: 8},
			{number: 3, name: "The Moral of the Story", min: 4, max: 6},
			{number: 4, name: "The Touching Story", min: 6, max: 8},
			{number: 5, name: "Bringing History to Life", min: 7, max: 9},
		},
	},
}
<commit_msg>added HPL and Specialty manuals.<commit_after>package main

import (
	"log"
	"strconv"
	"strings"
)

// Speech represents the speech that will be performed for the agenda.
type speech struct {
	manualCode string
	manualName string
	speechDetails
}

// Factory method to create a speech using a manual and the speech number in that manual.
func (speech) new(manualCode string, number int) speech {
	manualCode = strings.ToLower(manualCode)
	sp := speech{manualCode: manualCode}

	manual := manualMap[manualCode]
	sp.manualName = manual.manualName
	sp.number = number

	// Do not fail with invalid input, return with the default values set.
	if number < 1 || number > len(manual.speeches) {
		log.Print("Speech number is invalid for the manual.  Manual code: "+manualCode+" speech number:", number)
		return sp
	}

	sp.name = manual.speeches[number-1].name
	sp.min = manual.speeches[number-1].min
	sp.max = manual.speeches[number-1].max

	return sp
}

// Info create a string that represents a speech ex: "#1 Ice Breaker (4-6 mins)."
func (s speech) info() string {
	return "#" + strconv.Itoa(s.number) + " " + s.name +
		" " + "(" + strconv.Itoa(s.min) + "-" + strconv.Itoa(s.max) + " mins)"
}

// Each Toastmaster manual contains 5-10 ordered and named speeches.
// The fields min and max are the minimum and maximum speaking times for a speech.
type speechDetails struct {
	number int
	name   string
	min    int
	max    int
}

// There are 16 Toastmaster manuals a speaker can use at a meeting.
type manual struct {
	manualName string
	speeches   []speechDetails
}

// Representation of the Toasmaters speeches with their code as the key.
var manualMap = map[string]manual{
	"cc": {
		manualName: "Competent Communicator",
		speeches: []speechDetails{
			{number: 1, name: "Ice Breaker", min: 4, max: 6},
			{number: 2, name: "Organize Your Speech", min: 5, max: 7},
			{number: 3, name: "Get to the Point", min: 5, max: 7},
			{number: 4, name: "How to Say It", min: 5, max: 7},
			{number: 5, name: "Your Body Speaks", min: 5, max: 7},
			{number: 6, name: "Vocal Variety", min: 5, max: 7},
			{number: 7, name: "Research Your Topic", min: 5, max: 7},
			{number: 8, name: "Visual Aids", min: 5, max: 7},
			{number: 9, name: "Persuade with Power", min: 5, max: 7},
			{number: 10, name: "Inspire Your Audience", min: 8, max: 10},
		},
	},
	"inform": {
		manualName: "Speaking to Inform",
		speeches: []speechDetails{
			{number: 1, name: "The Speech to Inform", min: 5, max: 7},
			{number: 2, name: "Resources for Informing", min: 5, max: 7},
			{number: 3, name: "The Demonstration Talk", min: 5, max: 7},
			{number: 4, name: "A Fact Finding Report", min: 5, max: 7},
			{number: 5, name: "The Abstract Concept", min: 6, max: 8},
		},
	},
	"interpretive": {
		manualName: "Interpretive Reading",
		speeches: []speechDetails{
			{number: 1, name: "Read a Story", min: 8, max: 10},
			{number: 2, name: "Interpretive Poetry", min: 6, max: 8},
			{number: 3, name: "The Monodrama", min: 5, max: 7},
			{number: 4, name: "The Play", min: 12, max: 15},
			{number: 5, name: "The Oratorical Speech", min: 8, max: 10},
		},
	},
	"mgt": {
		manualName: "Speeches By Management",
		speeches: []speechDetails{
			{number: 1, name: "The Briefing", min: 8, max: 10},
			{number: 2, name: "The Technical Speech", min: 8, max: 10},
			{number: 3, name: "Manage and Motivate", min: 10, max: 12},
			{number: 4, name: "The Status Report", min: 10, max: 12},
			{number: 5, name: "Confrontation", min: 5, max: 15},
		},
	},
	"technical": {
		manualName: "Technical Presentations",
		speeches: []speechDetails{
			{number: 1, name: "Technical Briefing", min: 8, max: 10},
			{number: 2, name: "The Proposal", min: 8, max: 10},
			{number: 3, name: "The Nontechnical Audience", min: 10, max: 12},
			{number: 4, name: "Presenting a Technical Paper", min: 10, max: 12},
			{number: 5, name: "The Team Technical Presentation", min: 20, max: 30},
		},
	},
	"storytelling": {
		manualName: "Storytelling",
		speeches: []speechDetails{
			{number: 1, name: "The Folk Tale", min: 7, max: 9},
			{number: 2, name: "Let's Get Personal", min: 6, max: 8},
			{number: 3, name: "The Moral of the Story", min: 4, max: 6},
			{number: 4, name: "The Touching Story", min: 6, max: 8},
			{number: 5, name: "Bringing History to Life", min: 7, max: 9},
		},
	},
	"specialty": {
		manualName: "Specialty Speeches",
		speeches: []speechDetails{
			{number: 1, name: "Impromptu Speaking", min: 5, max: 7},
			{number: 2, name: "Uplift the Spirit", min: 8, max: 10},
			{number: 3, name: "Sell a product", min: 10, max: 12},
			{number: 4, name: "Read Out Loud", min: 12, max: 15},
			{number: 5, name: "Introduce the speaker", min: 0, max: 0},
		},
	},
	"hpl": {
		manualName: "High Performance Leadership",
		speeches: []speechDetails{
			{number: 1, name: "Share Your Vision", min: 5, max: 6},
			{number: 2, name: "Presenting The Results", min: 5, max: 7},
		},
	},

}
<|endoftext|>


<commit_before>package git

import ()

type Blob struct {
	*TreeEntry

	data   []byte
	dataed bool

	size  int64
	sized bool
}

func (b *Blob) Size() int64 {
	if b.sized {
		return b.size
	}

	size, err := b.ptree.repo.objectSize(b.Id)
	if err != nil {
		return 0
	}

	b.sized = true
	b.size = size
	return b.size
}

func (b *Blob) Data() ([]byte, error) {
	if b.dataed {
		return b.data, nil
	}
	_, _, data, err := b.ptree.repo.getRawObject(b.Id)
	if err != nil {
		return nil, err
	}
	b.data = data
	b.dataed = true
	return b.data, nil
}
<commit_msg>add write blob<commit_after>package git

import (
	"bytes"
	"compress/zlib"
	sha "crypto/sha1"
	"fmt"
	"io"
)

type Blob struct {
	*TreeEntry

	data   []byte
	dataed bool
}

func (b *Blob) Data() ([]byte, error) {
	if b.dataed {
		return b.data, nil
	}
	_, _, data, err := b.ptree.repo.getRawObject(b.Id)
	if err != nil {
		return nil, err
	}
	b.data = data
	b.dataed = true
	return b.data, nil
}

func (b *Blob) Save(w io.Writer, compress bool) (string, error) {
	var data []byte
	buf := bytes.NewBuffer(data)
	header := fmt.Sprintf("blob %d\\0", len(b.data))
	_, err := buf.Write([]byte(header))
	if err != nil {
		return "", err
	}
	_, err = buf.Write(b.data)
	if err != nil {
		return "", err
	}
	if compress {
		var b bytes.Buffer
		c := zlib.NewWriter(&b)
		_, err = io.Copy(c, buf)
		if err != nil {
			return "", err
		}
		_, err = io.Copy(w, &b)
	} else {
		_, err = io.Copy(w, buf)
	}
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("%x", sha.Sum(data)), nil
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"strconv"

	// "golang.org/x/net/context"

	pubsub "google.golang.org/api/pubsub/v1"
)

type (
	Publisher interface {
		Publish(topic string, msg *pubsub.PubsubMessage) (*pubsub.PublishResponse, error)
	}

	pubsubPublisher struct {
		topicsService *pubsub.ProjectsTopicsService
	}
)

func (pp *pubsubPublisher) Publish(topic string, msg *pubsub.PubsubMessage) (*pubsub.PublishResponse, error) {
	req := &pubsub.PublishRequest{
		Messages: []*pubsub.PubsubMessage{msg},
	}
	return pp.topicsService.Publish(topic, req).Do()
}

type (
	ProgressConfig struct {
		Topic string
	}

	ProgressNotification struct {
		config    *ProgressConfig
		publisher Publisher
	}
)

const (
	PROCESSING     = 1
	DOWNLOADING    = 2
	DOWNLOAD_OK    = 3
	DOWNLOAD_ERROR = 4
	EXECUTING      = 5
	EXECUTE_OK     = 6
	EXECUTE_ERROR  = 7
	UPLOADING      = 8
	UPLOAD_OK      = 9
	UPLOAD_ERROR   = 10
	ACKSENDING     = 11
	ACKSEND_OK     = 12
	ACKSEND_ERROR  = 13
	CLEANUP        = 14

	COMPLETED = ACKSEND_OK
	TOTAL     = CLEANUP
)

var PROGRESS_MESSAFGES = map[int]string{
	PROCESSING:     "PROCESSING",
	DOWNLOADING:    "DOWNLOADING",
	DOWNLOAD_OK:    "DOWNLOAD_OK",
	DOWNLOAD_ERROR: "DOWNLOAD_ERROR",
	EXECUTING:      "EXECUTING",
	EXECUTE_OK:     "EXECUTE_OK",
	EXECUTE_ERROR:  "EXECUTE_ERROR",
	UPLOADING:      "UPLOADING",
	UPLOAD_OK:      "UPLOAD_OK",
	UPLOAD_ERROR:   "UPLOAD_ERROR",
	ACKSENDING:     "ACKSENDING",
	ACKSEND_OK:     "ACKSEND_OK",
	ACKSEND_ERROR:  "ACKSEND_ERROR",
	CLEANUP:        "CLEANUP",
}

func (pn *ProgressNotification) notify(progress int, job_msg_id, level string) error {
	msg := PROGRESS_MESSAFGES[progress]
	log.Printf("Notify %v/%v %v\n", progress, TOTAL, msg)
	opts := map[string]string{
		"progress":       strconv.Itoa(progress),
		"total":          strconv.Itoa(TOTAL),
		"completed":      strconv.FormatBool(progress == COMPLETED),
		"job_message_id": job_msg_id,
		"level":          level,
	}
	m := &pubsub.PubsubMessage{Data: msg, Attributes: opts}
	_, err := pn.publisher.Publish(pn.config.Topic, m)
	if err != nil {
		log.Printf("Error to publish notification to %v msg: %v cause of %v\n", pn.config.Topic, m, err)
		return err
	}
	return nil
}
<commit_msg>:bug: Encode published message data by base64<commit_after>package main

import (
	"encoding/base64"
	"log"
	"strconv"

	// "golang.org/x/net/context"

	pubsub "google.golang.org/api/pubsub/v1"
)

type (
	Publisher interface {
		Publish(topic string, msg *pubsub.PubsubMessage) (*pubsub.PublishResponse, error)
	}

	pubsubPublisher struct {
		topicsService *pubsub.ProjectsTopicsService
	}
)

func (pp *pubsubPublisher) Publish(topic string, msg *pubsub.PubsubMessage) (*pubsub.PublishResponse, error) {
	req := &pubsub.PublishRequest{
		Messages: []*pubsub.PubsubMessage{msg},
	}
	return pp.topicsService.Publish(topic, req).Do()
}

type (
	ProgressConfig struct {
		Topic string
	}

	ProgressNotification struct {
		config    *ProgressConfig
		publisher Publisher
	}
)

const (
	PROCESSING     = 1
	DOWNLOADING    = 2
	DOWNLOAD_OK    = 3
	DOWNLOAD_ERROR = 4
	EXECUTING      = 5
	EXECUTE_OK     = 6
	EXECUTE_ERROR  = 7
	UPLOADING      = 8
	UPLOAD_OK      = 9
	UPLOAD_ERROR   = 10
	ACKSENDING     = 11
	ACKSEND_OK     = 12
	ACKSEND_ERROR  = 13
	CLEANUP        = 14

	COMPLETED = ACKSEND_OK
	TOTAL     = CLEANUP
)

var PROGRESS_MESSAFGES = map[int]string{
	PROCESSING:     "PROCESSING",
	DOWNLOADING:    "DOWNLOADING",
	DOWNLOAD_OK:    "DOWNLOAD_OK",
	DOWNLOAD_ERROR: "DOWNLOAD_ERROR",
	EXECUTING:      "EXECUTING",
	EXECUTE_OK:     "EXECUTE_OK",
	EXECUTE_ERROR:  "EXECUTE_ERROR",
	UPLOADING:      "UPLOADING",
	UPLOAD_OK:      "UPLOAD_OK",
	UPLOAD_ERROR:   "UPLOAD_ERROR",
	ACKSENDING:     "ACKSENDING",
	ACKSEND_OK:     "ACKSEND_OK",
	ACKSEND_ERROR:  "ACKSEND_ERROR",
	CLEANUP:        "CLEANUP",
}

func (pn *ProgressNotification) notify(progress int, job_msg_id, level string) error {
	msg := PROGRESS_MESSAFGES[progress]
	log.Printf("Notify %v/%v %v\n", progress, TOTAL, msg)
	opts := map[string]string{
		"progress":       strconv.Itoa(progress),
		"total":          strconv.Itoa(TOTAL),
		"completed":      strconv.FormatBool(progress == COMPLETED),
		"job_message_id": job_msg_id,
		"level":          level,
	}
	m := &pubsub.PubsubMessage{Data: base64.StdEncoding.EncodeToString([]byte(msg)), Attributes: opts}
	_, err := pn.publisher.Publish(pn.config.Topic, m)
	if err != nil {
		log.Printf("Error to publish notification to %v msg: %v cause of %v\n", pn.config.Topic, m, err)
		return err
	}
	return nil
}
<|endoftext|>