<commit_before>package aws

import (
	"fmt"
	"log"

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

func resourceAwsPinpointApp() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsPinpointAppCreate,
		Read:   resourceAwsPinpointAppRead,
		Update: resourceAwsPinpointAppUpdate,
		Delete: resourceAwsPinpointAppDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:          schema.TypeString,
				Optional:      true,
				Computed:      true,
				ForceNew:      true,
				ConflictsWith: []string{"name_prefix"},
			},
			"name_prefix": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},
			"application_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			//"cloudwatch_metrics_enabled": {
			//	Type:     schema.TypeBool,
			//	Optional: true,
			//	Default:  false,
			//},
			"campaign_hook": {
				Type:     schema.TypeList,
				Optional: true,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"lambda_function_name": {
							Type:     schema.TypeString,
							Optional: true,
						},
						"mode": {
							Type:     schema.TypeString,
							Optional: true,
							ValidateFunc: validation.StringInSlice([]string{
								pinpoint.ModeDelivery,
								pinpoint.ModeFilter,
							}, false),
						},
						"web_url": {
							Type:     schema.TypeString,
							Optional: true,
						},
					},
				},
			},
			"limits": {
				Type:     schema.TypeList,
				Optional: true,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"daily": {
							Type:     schema.TypeInt,
							Optional: true,
						},
						"maximum_duration": {
							Type:     schema.TypeInt,
							Optional: true,
						},
						"messages_per_second": {
							Type:     schema.TypeInt,
							Optional: true,
						},
						"total": {
							Type:     schema.TypeInt,
							Optional: true,
						},
					},
				},
			},
			"quiet_time": {
				Type:     schema.TypeList,
				Optional: true,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"end": {
							Type:     schema.TypeString,
							Optional: true,
						},
						"start": {
							Type:     schema.TypeString,
							Optional: true,
						},
					},
				},
			},
		},
	}
}

func resourceAwsPinpointAppCreate(d *schema.ResourceData, meta interface{}) error {
	pinpointconn := meta.(*AWSClient).pinpointconn

	var name string
	if v, ok := d.GetOk("name"); ok {
		name = v.(string)
	} else if v, ok := d.GetOk("name_prefix"); ok {
		name = resource.PrefixedUniqueId(v.(string))
	} else {
		name = resource.UniqueId()
	}

	log.Printf("[DEBUG] Pinpoint create app: %s", name)

	req := &pinpoint.CreateAppInput{
		CreateApplicationRequest: &pinpoint.CreateApplicationRequest{
			Name: aws.String(name),
		},
	}

	output, err := pinpointconn.CreateApp(req)
	if err != nil {
		return fmt.Errorf("error creating Pinpoint app: %s", err)
	}

	d.SetId(*output.ApplicationResponse.Id)

	return resourceAwsPinpointAppUpdate(d, meta)
}

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

	appSettings := &pinpoint.WriteApplicationSettingsRequest{}

	//if d.HasChange("cloudwatch_metrics_enabled") {
	//	appSettings.CloudWatchMetricsEnabled = aws.Bool(d.Get("cloudwatch_metrics_enabled").(bool));
	//}

	if d.HasChange("campaign_hook") {
		appSettings.CampaignHook = expandPinpointCampaignHook(d.Get("campaign_hook").([]interface{}))
	}

	if d.HasChange("limits") {
		appSettings.Limits = expandPinpointCampaignLimits(d.Get("limits").([]interface{}))
	}

	if d.HasChange("quiet_time") {
		appSettings.QuietTime = expandPinpointQuietTime(d.Get("quiet_time").([]interface{}))
	}

	req := pinpoint.UpdateApplicationSettingsInput{
		ApplicationId:                   aws.String(d.Id()),
		WriteApplicationSettingsRequest: appSettings,
	}

	_, err := conn.UpdateApplicationSettings(&req)
	if err != nil {
		return err
	}

	return resourceAwsPinpointAppRead(d, meta)
}

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

	log.Printf("[INFO] Reading Pinpoint App Attributes for %s", d.Id())

	app, err := conn.GetApp(&pinpoint.GetAppInput{
		ApplicationId: aws.String(d.Id()),
	})
	if err != nil {
		if isAWSErr(err, pinpoint.ErrCodeNotFoundException, "") {
			log.Printf("[WARN] Pinpoint App (%s) not found, error code (404)", d.Id())
			d.SetId("")
			return nil
		}

		return err
	}

	settings, err := conn.GetApplicationSettings(&pinpoint.GetApplicationSettingsInput{
		ApplicationId: aws.String(d.Id()),
	})
	if err != nil {
		if isAWSErr(err, pinpoint.ErrCodeNotFoundException, "") {
			log.Printf("[WARN] Pinpoint App (%s) not found, error code (404)", d.Id())
			d.SetId("")
			return nil
		}

		return err
	}

	d.Set("name", app.ApplicationResponse.Name)
	d.Set("application_id", app.ApplicationResponse.Id)

	if err := d.Set("campaign_hook", flattenPinpointCampaignHook(settings.ApplicationSettingsResource.CampaignHook)); err != nil {
		return fmt.Errorf("error setting campaign_hook: %s", err)
	}
	if err := d.Set("limits", flattenPinpointCampaignLimits(settings.ApplicationSettingsResource.Limits)); err != nil {
		return fmt.Errorf("error setting limits: %s", err)
	}
	if err := d.Set("quiet_time", flattenPinpointQuietTime(settings.ApplicationSettingsResource.QuietTime)); err != nil {
		return fmt.Errorf("error setting quiet_time: %s", err)
	}

	return nil
}

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

	log.Printf("[DEBUG] Pinpoint Delete App: %s", d.Id())
	_, err := conn.DeleteApp(&pinpoint.DeleteAppInput{
		ApplicationId: aws.String(d.Id()),
	})

	if isAWSErr(err, pinpoint.ErrCodeNotFoundException, "") {
		return nil
	}

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

func expandPinpointCampaignHook(configs []interface{}) *pinpoint.CampaignHook {
	if configs == nil || len(configs) == 0 {
		return nil
	}

	m := configs[0].(map[string]interface{})

	ch := &pinpoint.CampaignHook{}

	if m["lambda_function_name"] != nil {
		ch.LambdaFunctionName = aws.String(m["lambda_function_name"].(string))
	}

	if m["mode"] != nil {
		ch.Mode = aws.String(m["mode"].(string))
	}

	if m["web_url"] != nil {
		ch.WebUrl = aws.String(m["web_url"].(string))
	}

	return ch
}

func flattenPinpointCampaignHook(ch *pinpoint.CampaignHook) []interface{} {
	l := make([]interface{}, 0)

	m := map[string]interface{}{}

	if ch.LambdaFunctionName != nil {
		m["lambda_function_name"] = *ch.LambdaFunctionName
	}

	if ch.Mode != nil && *ch.Mode != "" {
		m["mode"] = *ch.Mode
	}

	if ch.WebUrl != nil {
		m["web_url"] = *ch.WebUrl
	}

	if len(m) <= 0 {
		return nil
	}

	l = append(l, m)

	return l
}

func expandPinpointCampaignLimits(configs []interface{}) *pinpoint.CampaignLimits {
	if configs == nil || len(configs) == 0 {
		return nil
	}

	m := configs[0].(map[string]interface{})

	cl := pinpoint.CampaignLimits{}

	if m["daily"] != nil {
		cl.Daily = aws.Int64(int64(m["daily"].(int)))
	}

	if m["maximum_duration"] != nil {
		cl.MaximumDuration = aws.Int64(int64(m["maximum_duration"].(int)))
	}

	if m["messages_per_second"] != nil {
		cl.MessagesPerSecond = aws.Int64(int64(m["messages_per_second"].(int)))
	}
	if m["total"] != nil {
		cl.Total = aws.Int64(int64(m["total"].(int)))
	}

	return &cl
}

func flattenPinpointCampaignLimits(cl *pinpoint.CampaignLimits) []interface{} {
	l := make([]interface{}, 0)

	m := map[string]interface{}{}

	if cl.Daily != nil {
		m["daily"] = *cl.Daily
	}
	if cl.MaximumDuration != nil {
		m["maximum_duration"] = *cl.MaximumDuration
	}
	if cl.MessagesPerSecond != nil {
		m["messages_per_second"] = *cl.MessagesPerSecond
	}

	if cl.Total != nil {
		m["total"] = *cl.Total
	}

	if len(m) <= 0 {
		return nil
	}

	l = append(l, m)

	return l
}

func expandPinpointQuietTime(configs []interface{}) *pinpoint.QuietTime {
	if configs == nil || len(configs) == 0 {
		return nil
	}

	m := configs[0].(map[string]interface{})

	qt := pinpoint.QuietTime{}

	if m["end"] != nil {
		qt.End = aws.String(m["end"].(string))
	}

	if m["start"] != nil {
		qt.Start = aws.String(m["start"].(string))
	}

	return &qt
}

func flattenPinpointQuietTime(qt *pinpoint.QuietTime) []interface{} {
	l := make([]interface{}, 0)

	m := map[string]interface{}{}

	if qt.End != nil {
		m["end"] = *qt.End
	}
	if qt.Start != nil {
		m["start"] = *qt.Start
	}

	if len(m) <= 0 {
		return nil
	}

	l = append(l, m)

	return l
}
<commit_msg>Simplify expand methods<commit_after>package aws

import (
	"fmt"
	"log"

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

func resourceAwsPinpointApp() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsPinpointAppCreate,
		Read:   resourceAwsPinpointAppRead,
		Update: resourceAwsPinpointAppUpdate,
		Delete: resourceAwsPinpointAppDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:          schema.TypeString,
				Optional:      true,
				Computed:      true,
				ForceNew:      true,
				ConflictsWith: []string{"name_prefix"},
			},
			"name_prefix": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},
			"application_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			//"cloudwatch_metrics_enabled": {
			//	Type:     schema.TypeBool,
			//	Optional: true,
			//	Default:  false,
			//},
			"campaign_hook": {
				Type:     schema.TypeList,
				Optional: true,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"lambda_function_name": {
							Type:     schema.TypeString,
							Optional: true,
						},
						"mode": {
							Type:     schema.TypeString,
							Optional: true,
							ValidateFunc: validation.StringInSlice([]string{
								pinpoint.ModeDelivery,
								pinpoint.ModeFilter,
							}, false),
						},
						"web_url": {
							Type:     schema.TypeString,
							Optional: true,
						},
					},
				},
			},
			"limits": {
				Type:     schema.TypeList,
				Optional: true,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"daily": {
							Type:     schema.TypeInt,
							Optional: true,
						},
						"maximum_duration": {
							Type:     schema.TypeInt,
							Optional: true,
						},
						"messages_per_second": {
							Type:     schema.TypeInt,
							Optional: true,
						},
						"total": {
							Type:     schema.TypeInt,
							Optional: true,
						},
					},
				},
			},
			"quiet_time": {
				Type:     schema.TypeList,
				Optional: true,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"end": {
							Type:     schema.TypeString,
							Optional: true,
						},
						"start": {
							Type:     schema.TypeString,
							Optional: true,
						},
					},
				},
			},
		},
	}
}

func resourceAwsPinpointAppCreate(d *schema.ResourceData, meta interface{}) error {
	pinpointconn := meta.(*AWSClient).pinpointconn

	var name string
	if v, ok := d.GetOk("name"); ok {
		name = v.(string)
	} else if v, ok := d.GetOk("name_prefix"); ok {
		name = resource.PrefixedUniqueId(v.(string))
	} else {
		name = resource.UniqueId()
	}

	log.Printf("[DEBUG] Pinpoint create app: %s", name)

	req := &pinpoint.CreateAppInput{
		CreateApplicationRequest: &pinpoint.CreateApplicationRequest{
			Name: aws.String(name),
		},
	}

	output, err := pinpointconn.CreateApp(req)
	if err != nil {
		return fmt.Errorf("error creating Pinpoint app: %s", err)
	}

	d.SetId(*output.ApplicationResponse.Id)

	return resourceAwsPinpointAppUpdate(d, meta)
}

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

	appSettings := &pinpoint.WriteApplicationSettingsRequest{}

	//if d.HasChange("cloudwatch_metrics_enabled") {
	//	appSettings.CloudWatchMetricsEnabled = aws.Bool(d.Get("cloudwatch_metrics_enabled").(bool));
	//}

	if d.HasChange("campaign_hook") {
		appSettings.CampaignHook = expandPinpointCampaignHook(d.Get("campaign_hook").([]interface{}))
	}

	if d.HasChange("limits") {
		appSettings.Limits = expandPinpointCampaignLimits(d.Get("limits").([]interface{}))
	}

	if d.HasChange("quiet_time") {
		appSettings.QuietTime = expandPinpointQuietTime(d.Get("quiet_time").([]interface{}))
	}

	req := pinpoint.UpdateApplicationSettingsInput{
		ApplicationId:                   aws.String(d.Id()),
		WriteApplicationSettingsRequest: appSettings,
	}

	_, err := conn.UpdateApplicationSettings(&req)
	if err != nil {
		return err
	}

	return resourceAwsPinpointAppRead(d, meta)
}

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

	log.Printf("[INFO] Reading Pinpoint App Attributes for %s", d.Id())

	app, err := conn.GetApp(&pinpoint.GetAppInput{
		ApplicationId: aws.String(d.Id()),
	})
	if err != nil {
		if isAWSErr(err, pinpoint.ErrCodeNotFoundException, "") {
			log.Printf("[WARN] Pinpoint App (%s) not found, error code (404)", d.Id())
			d.SetId("")
			return nil
		}

		return err
	}

	settings, err := conn.GetApplicationSettings(&pinpoint.GetApplicationSettingsInput{
		ApplicationId: aws.String(d.Id()),
	})
	if err != nil {
		if isAWSErr(err, pinpoint.ErrCodeNotFoundException, "") {
			log.Printf("[WARN] Pinpoint App (%s) not found, error code (404)", d.Id())
			d.SetId("")
			return nil
		}

		return err
	}

	d.Set("name", app.ApplicationResponse.Name)
	d.Set("application_id", app.ApplicationResponse.Id)

	if err := d.Set("campaign_hook", flattenPinpointCampaignHook(settings.ApplicationSettingsResource.CampaignHook)); err != nil {
		return fmt.Errorf("error setting campaign_hook: %s", err)
	}
	if err := d.Set("limits", flattenPinpointCampaignLimits(settings.ApplicationSettingsResource.Limits)); err != nil {
		return fmt.Errorf("error setting limits: %s", err)
	}
	if err := d.Set("quiet_time", flattenPinpointQuietTime(settings.ApplicationSettingsResource.QuietTime)); err != nil {
		return fmt.Errorf("error setting quiet_time: %s", err)
	}

	return nil
}

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

	log.Printf("[DEBUG] Pinpoint Delete App: %s", d.Id())
	_, err := conn.DeleteApp(&pinpoint.DeleteAppInput{
		ApplicationId: aws.String(d.Id()),
	})

	if isAWSErr(err, pinpoint.ErrCodeNotFoundException, "") {
		return nil
	}

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

func expandPinpointCampaignHook(configs []interface{}) *pinpoint.CampaignHook {
	if configs == nil || len(configs) == 0 {
		return nil
	}

	m := configs[0].(map[string]interface{})

	ch := &pinpoint.CampaignHook{}

	if v, ok := m["lambda_function_name"]; ok {
		ch.LambdaFunctionName = aws.String(v.(string))
	}

	if v, ok := m["mode"]; ok {
		ch.Mode = aws.String(v.(string))
	}

	if v, ok := m["web_url"]; ok {
		ch.WebUrl = aws.String(v.(string))
	}

	return ch
}

func flattenPinpointCampaignHook(ch *pinpoint.CampaignHook) []interface{} {
	l := make([]interface{}, 0)

	m := map[string]interface{}{}

	if ch.LambdaFunctionName != nil {
		m["lambda_function_name"] = *ch.LambdaFunctionName
	}

	if ch.Mode != nil && *ch.Mode != "" {
		m["mode"] = *ch.Mode
	}

	if ch.WebUrl != nil {
		m["web_url"] = *ch.WebUrl
	}

	if len(m) <= 0 {
		return nil
	}

	l = append(l, m)

	return l
}

func expandPinpointCampaignLimits(configs []interface{}) *pinpoint.CampaignLimits {
	if configs == nil || len(configs) == 0 {
		return nil
	}

	m := configs[0].(map[string]interface{})

	cl := pinpoint.CampaignLimits{}

	if v, ok := m["daily"]; ok {
		cl.Daily = aws.Int64(int64(v.(int)))
	}

	if v, ok := m["maximum_duration"]; ok {
		cl.MaximumDuration = aws.Int64(int64(v.(int)))
	}

	if v, ok := m["messages_per_second"]; ok {
		cl.MessagesPerSecond = aws.Int64(int64(v.(int)))
	}

	if v, ok := m["total"]; ok {
		cl.Total = aws.Int64(int64(v.(int)))
	}

	return &cl
}

func flattenPinpointCampaignLimits(cl *pinpoint.CampaignLimits) []interface{} {
	l := make([]interface{}, 0)

	m := map[string]interface{}{}

	if cl.Daily != nil {
		m["daily"] = *cl.Daily
	}
	if cl.MaximumDuration != nil {
		m["maximum_duration"] = *cl.MaximumDuration
	}
	if cl.MessagesPerSecond != nil {
		m["messages_per_second"] = *cl.MessagesPerSecond
	}

	if cl.Total != nil {
		m["total"] = *cl.Total
	}

	if len(m) <= 0 {
		return nil
	}

	l = append(l, m)

	return l
}

func expandPinpointQuietTime(configs []interface{}) *pinpoint.QuietTime {
	if configs == nil || len(configs) == 0 {
		return nil
	}

	m := configs[0].(map[string]interface{})

	qt := pinpoint.QuietTime{}

	if v, ok := m["end"]; ok {
		qt.End = aws.String(v.(string))
	}

	if v, ok := m["start"]; ok {
		qt.Start = aws.String(v.(string))
	}

	return &qt
}

func flattenPinpointQuietTime(qt *pinpoint.QuietTime) []interface{} {
	l := make([]interface{}, 0)

	m := map[string]interface{}{}

	if qt.End != nil {
		m["end"] = *qt.End
	}
	if qt.Start != nil {
		m["start"] = *qt.Start
	}

	if len(m) <= 0 {
		return nil
	}

	l = append(l, m)

	return l
}
<|endoftext|>


<commit_before>package deepcopier

import (
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/guregu/null"
	"github.com/lib/pq"
	"github.com/oleiade/reflections"
)

const (
	// TagName is struct field tag name.
	TagName = "deepcopier"

	// FieldOptionName is the from field option name for struct tag.
	FieldOptionName = "field"

	// ContextOptionName is the context option name for struct tag.
	ContextOptionName = "context"

	// SkipOptionName is the skip option name for struct tag.
	SkipOptionName = "skip"
)

// DeepCopier deep copies a struct to/from a struct.
type DeepCopier struct {
	Source      interface{}
	Destination interface{}
	Tagged      interface{}
	Context     map[string]interface{}
	Reversed    bool
}

// FieldOptions contains options passed to SetField method.
type FieldOptions struct {
	SourceField      string
	DestinationField string
	WithContext      bool
	Skip             bool
}

// Copy sets the source.
func Copy(source interface{}) *DeepCopier {
	return &DeepCopier{
		Source:   source,
		Reversed: false,
	}
}

// To sets the given tagged struct as destination struct.
// Source -> Destination
func (dc *DeepCopier) To(tagged interface{}) error {
	dc.Destination = tagged
	dc.Tagged = tagged
	return dc.ProcessCopy()
}

// From sets the given tagged struct as source and the current source as destination.
// Source <- Destination
func (dc *DeepCopier) From(tagged interface{}) error {
	dc.Destination = dc.Source
	dc.Source = tagged
	dc.Tagged = tagged
	dc.Reversed = true
	return dc.ProcessCopy()
}

// ProcessCopy processes copy.
func (dc *DeepCopier) ProcessCopy() error {
	fields := []string{}

	val := reflect.ValueOf(dc.Tagged).Elem()

	for i := 0; i < val.NumField(); i++ {
		typ := val.Type().Field(i)

		// Embedded struct
		if typ.Anonymous {
			f, _ := reflections.Fields(val.Field(i).Interface())
			fields = append(fields, f...)
		} else {
			fields = append(fields, typ.Name)
		}
	}

	for _, field := range fields {
		fieldOptions := &FieldOptions{
			SourceField:      field,
			DestinationField: field,
			WithContext:      false,
			Skip:             false,
		}

		tagOptions, _ := reflections.GetFieldTag(dc.Tagged, field, TagName)

		if tagOptions != "" {
			opts := dc.GetTagOptions(tagOptions)
			if _, ok := opts[FieldOptionName]; ok {
				fieldName := opts[FieldOptionName]
				if !dc.Reversed {
					fieldOptions.SourceField = fieldName
				} else {
					fieldOptions.DestinationField = fieldName
				}
			}
			if _, ok := opts[ContextOptionName]; ok {
				fieldOptions.WithContext = true
			}
			if _, ok := opts[SkipOptionName]; ok {
				fieldOptions.Skip = true
			}
		}

		if err := dc.SetField(fieldOptions); err != nil {
			return err
		}
	}
	return nil
}

// -----------------------------------------------------------------------------
// Options
// -----------------------------------------------------------------------------

// WithContext injects the given context into the builder instance.
func (dc *DeepCopier) WithContext(context map[string]interface{}) *DeepCopier {
	dc.Context = context
	return dc
}

// -----------------------------------------------------------------------------
// Struct tags
// -----------------------------------------------------------------------------

// GetTagOptions parses deepcopier tag field and returns options.
func (dc *DeepCopier) GetTagOptions(value string) map[string]string {
	options := map[string]string{}
	for _, opt := range strings.Split(value, ";") {
		o := strings.Split(opt, ":")
		// deepcopier:"keyword; without; value;"
		if len(o) == 1 {
			k := o[0]
			options[k] = ""
		}
		// deepcopier:"key:value; anotherkey:anothervalue"
		if len(o) == 2 {
			k, v := o[0], o[1]
			k = strings.TrimSpace(k)
			v = strings.TrimSpace(v)
			options[k] = v
		}
	}
	return options
}

// -----------------------------------------------------------------------------
// Field Setters
// -----------------------------------------------------------------------------

// SetField sets the value of the given field.
func (dc *DeepCopier) SetField(options *FieldOptions) error {
	if options.Skip {
		return nil
	}

	if dc.Reversed {
		has, _ := reflections.HasField(dc.Destination, options.DestinationField)
		if !has {
			return nil
		}
	}

	has, _ := reflections.HasField(dc.Source, options.SourceField)
	if !has {
		err := dc.HandleMethod(options)
		if err != nil {
			has, _ = reflections.HasField(dc.Destination, options.DestinationField)
			if has {
				return nil
			}
		}
		return nil
	}

	kind, _ := reflections.GetFieldKind(dc.Source, options.SourceField)
	if kind == reflect.Struct {
		if err := dc.HandleStructField(options); err != nil {
			return err
		}
		return nil
	}

	if err := dc.HandleField(options); err != nil {
		return err
	}

	return nil
}

// SetFieldValue Sets the given value to the given field.
func (dc *DeepCopier) SetFieldValue(entity interface{}, name string, value reflect.Value) error {
	kind := value.Kind()

	if kind == reflect.Ptr {
		value = value.Elem()
		kind = value.Kind()
	}

	// Maps
	if kind == reflect.Map {
		switch v := value.Interface().(type) {
		case map[string]interface{}, map[string]string, map[string]map[string]string, map[string]map[string]map[string]string:
			if err := reflections.SetField(entity, name, v); err != nil {
				return err
			}
			return nil
		}
	}

	// Structs
	if kind == reflect.Struct {
		switch v := value.Interface().(type) {
		case time.Time, pq.NullTime, null.String:
			if err := reflections.SetField(entity, name, v); err != nil {
				return err
			}
			return nil
		}
	}

	// Slices
	if kind == reflect.Slice {
		switch v := value.Interface().(type) {
		case []int8, []int16, []int32, []int64, []int, []uint8, []uint16, []uint32, []uint64, []uint, []float32, []float64, []string, []bool:
			if err := reflections.SetField(entity, name, v); err != nil {
				return err
			}
			return nil
		}
	}

	// Reflect
	switch kind {
	case reflect.Int8:
		if err := reflections.SetField(entity, name, int8(value.Int())); err != nil {
			return err
		}
		return nil
	case reflect.Int16:
		if err := reflections.SetField(entity, name, int16(value.Int())); err != nil {
			return err
		}
		return nil
	case reflect.Int32:
		if err := reflections.SetField(entity, name, int32(value.Int())); err != nil {
			return err
		}
		return nil
	case reflect.Int64:
		if err := reflections.SetField(entity, name, value.Int()); err != nil {
			return err
		}
		return nil
	case reflect.Int:
		if err := reflections.SetField(entity, name, int(value.Int())); err != nil {
			return err
		}
		return nil
	case reflect.Uint8:
		if err := reflections.SetField(entity, name, uint8(value.Uint())); err != nil {
			return err
		}
		return nil
	case reflect.Uint16:
		if err := reflections.SetField(entity, name, uint16(value.Uint())); err != nil {
			return err
		}
		return nil
	case reflect.Uint32:
		if err := reflections.SetField(entity, name, uint32(value.Uint())); err != nil {
			return err
		}
		return nil
	case reflect.Uint64:
		if err := reflections.SetField(entity, name, value.Uint()); err != nil {
			return err
		}
		return nil
	case reflect.Uint:
		if err := reflections.SetField(entity, name, uint(value.Uint())); err != nil {
			return err
		}
		return nil
	case reflect.Float32:
		if err := reflections.SetField(entity, name, float32(value.Float())); err != nil {
			return err
		}
		return nil
	case reflect.Float64:
		if err := reflections.SetField(entity, name, value.Float()); err != nil {
			return err
		}
		return nil
	case reflect.String:
		if err := reflections.SetField(entity, name, value.String()); err != nil {
			return err
		}
		return nil
	case reflect.Bool:
		if err := reflections.SetField(entity, name, value.Bool()); err != nil {
			return err
		}
		return nil
	}

	return nil
}

// -----------------------------------------------------------------------------
// Field Type Handlers
// -----------------------------------------------------------------------------

// HandleStructField sets the value for the given supported struct field.
func (dc *DeepCopier) HandleStructField(options *FieldOptions) error {
	f, err := reflections.GetField(dc.Source, options.SourceField)
	if err != nil {
		return err
	}

	switch v := f.(type) {
	case pq.NullTime:
		if v.Valid {
			if err := reflections.SetField(dc.Destination, options.DestinationField, &v.Time); err != nil {
				return err
			}
		}
	case time.Time:
		if err := reflections.SetField(dc.Destination, options.DestinationField, v); err != nil {
			return err
		}
	}

	return nil
}

// HandleField sets value for the given field.
func (dc *DeepCopier) HandleField(options *FieldOptions) error {
	v, err := reflections.GetField(dc.Source, options.SourceField)
	if err != nil {
		return err
	}

	value := reflect.ValueOf(v)
	if err := dc.SetFieldValue(dc.Destination, options.DestinationField, value); err != nil {
		return err
	}

	return nil
}

// HandleMethod tries to call method on model and sets result in resource field.
func (dc *DeepCopier) HandleMethod(options *FieldOptions) error {
	if dc.Reversed {
		return nil
	}

	method := reflect.ValueOf(dc.Source).MethodByName(options.SourceField)
	if !method.IsValid() {
		return fmt.Errorf("Method %s does not exist", options.SourceField)
	}

	var results []reflect.Value
	if options.WithContext {
		results = method.Call([]reflect.Value{reflect.ValueOf(dc.Context)})
	} else {
		results = method.Call([]reflect.Value{})
	}

	if err := dc.SetFieldValue(dc.Destination, options.DestinationField, results[0]); err != nil {
		return err
	}

	return nil
}
<commit_msg>Ptr nil check<commit_after>package deepcopier

import (
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/guregu/null"
	"github.com/lib/pq"
	"github.com/oleiade/reflections"
)

const (
	// TagName is struct field tag name.
	TagName = "deepcopier"

	// FieldOptionName is the from field option name for struct tag.
	FieldOptionName = "field"

	// ContextOptionName is the context option name for struct tag.
	ContextOptionName = "context"

	// SkipOptionName is the skip option name for struct tag.
	SkipOptionName = "skip"
)

// DeepCopier deep copies a struct to/from a struct.
type DeepCopier struct {
	Source      interface{}
	Destination interface{}
	Tagged      interface{}
	Context     map[string]interface{}
	Reversed    bool
}

// FieldOptions contains options passed to SetField method.
type FieldOptions struct {
	SourceField      string
	DestinationField string
	WithContext      bool
	Skip             bool
}

// Copy sets the source.
func Copy(source interface{}) *DeepCopier {
	return &DeepCopier{
		Source:   source,
		Reversed: false,
	}
}

// To sets the given tagged struct as destination struct.
// Source -> Destination
func (dc *DeepCopier) To(tagged interface{}) error {
	dc.Destination = tagged
	dc.Tagged = tagged
	return dc.ProcessCopy()
}

// From sets the given tagged struct as source and the current source as destination.
// Source <- Destination
func (dc *DeepCopier) From(tagged interface{}) error {
	dc.Destination = dc.Source
	dc.Source = tagged
	dc.Tagged = tagged
	dc.Reversed = true
	return dc.ProcessCopy()
}

// ProcessCopy processes copy.
func (dc *DeepCopier) ProcessCopy() error {
	fields := []string{}

	val := reflect.ValueOf(dc.Tagged).Elem()

	for i := 0; i < val.NumField(); i++ {
		typ := val.Type().Field(i)

		// Embedded struct
		if typ.Anonymous {
			f, _ := reflections.Fields(val.Field(i).Interface())
			fields = append(fields, f...)
		} else {
			fields = append(fields, typ.Name)
		}
	}

	for _, field := range fields {
		fieldOptions := &FieldOptions{
			SourceField:      field,
			DestinationField: field,
			WithContext:      false,
			Skip:             false,
		}

		tagOptions, _ := reflections.GetFieldTag(dc.Tagged, field, TagName)

		if tagOptions != "" {
			opts := dc.GetTagOptions(tagOptions)
			if _, ok := opts[FieldOptionName]; ok {
				fieldName := opts[FieldOptionName]
				if !dc.Reversed {
					fieldOptions.SourceField = fieldName
				} else {
					fieldOptions.DestinationField = fieldName
				}
			}
			if _, ok := opts[ContextOptionName]; ok {
				fieldOptions.WithContext = true
			}
			if _, ok := opts[SkipOptionName]; ok {
				fieldOptions.Skip = true
			}
		}

		if err := dc.SetField(fieldOptions); err != nil {
			return err
		}
	}
	return nil
}

// -----------------------------------------------------------------------------
// Options
// -----------------------------------------------------------------------------

// WithContext injects the given context into the builder instance.
func (dc *DeepCopier) WithContext(context map[string]interface{}) *DeepCopier {
	dc.Context = context
	return dc
}

// -----------------------------------------------------------------------------
// Struct tags
// -----------------------------------------------------------------------------

// GetTagOptions parses deepcopier tag field and returns options.
func (dc *DeepCopier) GetTagOptions(value string) map[string]string {
	options := map[string]string{}
	for _, opt := range strings.Split(value, ";") {
		o := strings.Split(opt, ":")
		// deepcopier:"keyword; without; value;"
		if len(o) == 1 {
			k := o[0]
			options[k] = ""
		}
		// deepcopier:"key:value; anotherkey:anothervalue"
		if len(o) == 2 {
			k, v := o[0], o[1]
			k = strings.TrimSpace(k)
			v = strings.TrimSpace(v)
			options[k] = v
		}
	}
	return options
}

// -----------------------------------------------------------------------------
// Field Setters
// -----------------------------------------------------------------------------

// SetField sets the value of the given field.
func (dc *DeepCopier) SetField(options *FieldOptions) error {
	if options.Skip {
		return nil
	}

	if dc.Reversed {
		has, _ := reflections.HasField(dc.Destination, options.DestinationField)
		if !has {
			return nil
		}
	}

	has, _ := reflections.HasField(dc.Source, options.SourceField)
	if !has {
		err := dc.HandleMethod(options)
		if err != nil {
			has, _ = reflections.HasField(dc.Destination, options.DestinationField)
			if has {
				return nil
			}
		}
		return nil
	}

	kind, _ := reflections.GetFieldKind(dc.Source, options.SourceField)
	if kind == reflect.Struct {
		if err := dc.HandleStructField(options); err != nil {
			return err
		}
		return nil
	}

	if err := dc.HandleField(options); err != nil {
		return err
	}

	return nil
}

// SetFieldValue Sets the given value to the given field.
func (dc *DeepCopier) SetFieldValue(entity interface{}, name string, value reflect.Value) error {
	kind := value.Kind()

	if kind == reflect.Ptr {
		if value.IsNil() {
			return nil
		}
		value = value.Elem()
		kind = value.Kind()
	}

	// Maps
	if kind == reflect.Map {
		switch v := value.Interface().(type) {
		case map[string]interface{}, map[string]string, map[string]map[string]string, map[string]map[string]map[string]string:
			if err := reflections.SetField(entity, name, v); err != nil {
				return err
			}
			return nil
		}
	}

	// Structs
	if kind == reflect.Struct {
		switch v := value.Interface().(type) {
		case time.Time, pq.NullTime, null.String:
			if err := reflections.SetField(entity, name, v); err != nil {
				return err
			}
			return nil
		}
	}

	// Slices
	if kind == reflect.Slice {
		switch v := value.Interface().(type) {
		case []int8, []int16, []int32, []int64, []int, []uint8, []uint16, []uint32, []uint64, []uint, []float32, []float64, []string, []bool:
			if err := reflections.SetField(entity, name, v); err != nil {
				return err
			}
			return nil
		}
	}

	// Reflect
	switch kind {
	case reflect.Int8:
		if err := reflections.SetField(entity, name, int8(value.Int())); err != nil {
			return err
		}
		return nil
	case reflect.Int16:
		if err := reflections.SetField(entity, name, int16(value.Int())); err != nil {
			return err
		}
		return nil
	case reflect.Int32:
		if err := reflections.SetField(entity, name, int32(value.Int())); err != nil {
			return err
		}
		return nil
	case reflect.Int64:
		if err := reflections.SetField(entity, name, value.Int()); err != nil {
			return err
		}
		return nil
	case reflect.Int:
		if err := reflections.SetField(entity, name, int(value.Int())); err != nil {
			return err
		}
		return nil
	case reflect.Uint8:
		if err := reflections.SetField(entity, name, uint8(value.Uint())); err != nil {
			return err
		}
		return nil
	case reflect.Uint16:
		if err := reflections.SetField(entity, name, uint16(value.Uint())); err != nil {
			return err
		}
		return nil
	case reflect.Uint32:
		if err := reflections.SetField(entity, name, uint32(value.Uint())); err != nil {
			return err
		}
		return nil
	case reflect.Uint64:
		if err := reflections.SetField(entity, name, value.Uint()); err != nil {
			return err
		}
		return nil
	case reflect.Uint:
		if err := reflections.SetField(entity, name, uint(value.Uint())); err != nil {
			return err
		}
		return nil
	case reflect.Float32:
		if err := reflections.SetField(entity, name, float32(value.Float())); err != nil {
			return err
		}
		return nil
	case reflect.Float64:
		if err := reflections.SetField(entity, name, value.Float()); err != nil {
			return err
		}
		return nil
	case reflect.String:
		if err := reflections.SetField(entity, name, value.String()); err != nil {
			return err
		}
		return nil
	case reflect.Bool:
		if err := reflections.SetField(entity, name, value.Bool()); err != nil {
			return err
		}
		return nil
	}

	return nil
}

// -----------------------------------------------------------------------------
// Field Type Handlers
// -----------------------------------------------------------------------------

// HandleStructField sets the value for the given supported struct field.
func (dc *DeepCopier) HandleStructField(options *FieldOptions) error {
	f, err := reflections.GetField(dc.Source, options.SourceField)
	if err != nil {
		return err
	}

	switch v := f.(type) {
	case pq.NullTime:
		if v.Valid {
			if err := reflections.SetField(dc.Destination, options.DestinationField, &v.Time); err != nil {
				return err
			}
		}
	case time.Time:
		if err := reflections.SetField(dc.Destination, options.DestinationField, v); err != nil {
			return err
		}
	}

	return nil
}

// HandleField sets value for the given field.
func (dc *DeepCopier) HandleField(options *FieldOptions) error {
	v, err := reflections.GetField(dc.Source, options.SourceField)
	if err != nil {
		return err
	}

	value := reflect.ValueOf(v)
	if err := dc.SetFieldValue(dc.Destination, options.DestinationField, value); err != nil {
		return err
	}

	return nil
}

// HandleMethod tries to call method on model and sets result in resource field.
func (dc *DeepCopier) HandleMethod(options *FieldOptions) error {
	if dc.Reversed {
		return nil
	}

	method := reflect.ValueOf(dc.Source).MethodByName(options.SourceField)
	if !method.IsValid() {
		return fmt.Errorf("Method %s does not exist", options.SourceField)
	}

	var results []reflect.Value
	if options.WithContext {
		results = method.Call([]reflect.Value{reflect.ValueOf(dc.Context)})
	} else {
		results = method.Call([]reflect.Value{})
	}

	if err := dc.SetFieldValue(dc.Destination, options.DestinationField, results[0]); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package route_services

import (
	"encoding/json"
	"fmt"
	"net/url"

	. "code.cloudfoundry.org/cf-routing-test-helpers/helpers"
	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/cloudfoundry-incubator/cf-test-helpers/helpers"
	"github.com/cloudfoundry-incubator/cf-test-helpers/workflowhelpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/assets"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/random_name"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = RouteServicesDescribe("Route Services", func() {
	Context("when a route binds to a service", func() {
		Context("when service broker returns a route service url", func() {
			var (
				serviceInstanceName      string
				brokerName               string
				appName                  string
				routeServiceName         string
				golangAsset              = assets.NewAssets().Golang
				loggingRouteServiceAsset = assets.NewAssets().LoggingRouteService
			)

			BeforeEach(func() {
				if config.Backend != "diego" {
					Skip(`Skipping this test because config.Backend is not set to 'diego'
NOTE: Ensure your platform is running Diego before enabling this test`)
				}
				routeServiceName = random_name.CATSRandomName("APP")
				brokerName = random_name.CATSRandomName("BROKER")
				serviceInstanceName = random_name.CATSRandomName("SVCINS")
				appName = random_name.CATSRandomName("APP")

				serviceName := random_name.CATSRandomName("SVC")
				brokerAppName := random_name.CATSRandomName("APP")

				createServiceBroker(brokerName, brokerAppName, serviceName)
				createServiceInstance(serviceInstanceName, serviceName)

				PushAppNoStart(appName, golangAsset, config.GoBuildpackName, config.AppsDomain, CF_PUSH_TIMEOUT)
				EnableDiego(appName, DEFAULT_TIMEOUT)
				StartApp(appName, CF_PUSH_TIMEOUT)

				PushApp(routeServiceName, loggingRouteServiceAsset, config.GoBuildpackName, config.AppsDomain, CF_PUSH_TIMEOUT)
				configureBroker(brokerAppName, routeServiceName)

				bindRouteToService(appName, serviceInstanceName)
			})

			AfterEach(func() {
				AppReport(appName, DEFAULT_TIMEOUT)
				AppReport(routeServiceName, DEFAULT_TIMEOUT)

				unbindRouteFromService(appName, serviceInstanceName)
				deleteServiceInstance(serviceInstanceName)
				deleteServiceBroker(brokerName)
				DeleteApp(appName, DEFAULT_TIMEOUT)
				DeleteApp(routeServiceName, DEFAULT_TIMEOUT)
			})

			It("a request to the app is routed through the route service", func() {
				Eventually(func() *Session {
					helpers.CurlAppRoot(appName)
					logs := cf.Cf("logs", "--recent", routeServiceName)
					Expect(logs.Wait(DEFAULT_TIMEOUT)).To(Exit(0))
					return logs
				}, DEFAULT_TIMEOUT).Should(Say("Response Body: go, world"))
			})
		})

		Context("when service broker does not return a route service url", func() {
			var (
				serviceInstanceName string
				brokerName          string
				appName             string
				golangAsset         = assets.NewAssets().Golang
			)

			BeforeEach(func() {
				appName = random_name.CATSRandomName("APP")
				brokerName = random_name.CATSRandomName("BROKER")
				serviceInstanceName = random_name.CATSRandomName("SVCINS")

				brokerAppName := random_name.CATSRandomName("APP")
				serviceName := random_name.CATSRandomName("SVC")

				createServiceBroker(brokerName, brokerAppName, serviceName)
				createServiceInstance(serviceInstanceName, serviceName)

				PushAppNoStart(appName, golangAsset, config.GoBuildpackName, config.AppsDomain, CF_PUSH_TIMEOUT)
				EnableDiego(appName, DEFAULT_TIMEOUT)
				StartApp(appName, CF_PUSH_TIMEOUT)

				configureBroker(brokerAppName, "")

				bindRouteToService(appName, serviceInstanceName)
			})

			AfterEach(func() {
				AppReport(appName, DEFAULT_TIMEOUT)

				unbindRouteFromService(appName, serviceInstanceName)
				deleteServiceInstance(serviceInstanceName)
				deleteServiceBroker(brokerName)
				DeleteApp(appName, DEFAULT_TIMEOUT)
			})

			It("routes to an app", func() {
				Eventually(func() string {
					return helpers.CurlAppRoot(appName)
				}, DEFAULT_TIMEOUT).Should(ContainSubstring("go, world"))
			})
		})

		Context("when arbitrary parameters are sent", func() {
			var (
				serviceInstanceName string
				brokerName          string
				brokerAppName       string
				hostname            string
			)

			BeforeEach(func() {
				hostname = random_name.CATSRandomName("ROUTE")
				brokerAppName = random_name.CATSRandomName("APP")
				serviceInstanceName = random_name.CATSRandomName("SVCINS")
				brokerName = random_name.CATSRandomName("BROKER")

				serviceName := random_name.CATSRandomName("SVC")

				createServiceBroker(brokerName, brokerAppName, serviceName)
				createServiceInstance(serviceInstanceName, serviceName)

				CreateRoute(hostname, "", context.RegularUserContext().Space, config.AppsDomain, DEFAULT_TIMEOUT)

				configureBroker(brokerAppName, "")
			})

			AfterEach(func() {
				unbindRouteFromService(hostname, serviceInstanceName)
				deleteServiceInstance(serviceInstanceName)
				deleteServiceBroker(brokerName)
				DeleteRoute(hostname, "", config.AppsDomain, DEFAULT_TIMEOUT)
			})

			It("passes them to the service broker", func() {
				bindRouteToServiceWithParams(hostname, serviceInstanceName, "{\"key1\":[\"value1\",\"irynaparam\"],\"key2\":\"value3\"}")

				Eventually(func() *Session {
					logs := cf.Cf("logs", "--recent", brokerAppName)
					Expect(logs.Wait(DEFAULT_TIMEOUT)).To(Exit(0))
					return logs
				}, DEFAULT_TIMEOUT).Should(Say("irynaparam"))
			})
		})
	})
})

type customMap map[string]interface{}

func (c customMap) key(key string) customMap {
	return c[key].(map[string]interface{})
}

func bindRouteToService(hostname, serviceInstanceName string) {
	routeGuid := GetRouteGuid(hostname, "", DEFAULT_TIMEOUT)

	Expect(cf.Cf("bind-route-service", config.AppsDomain, serviceInstanceName,
		"-f",
		"--hostname", hostname,
	).Wait(DEFAULT_TIMEOUT)).To(Exit(0))

	Eventually(func() string {
		response := cf.Cf("curl", fmt.Sprintf("/v2/routes/%s", routeGuid))
		Expect(response.Wait(DEFAULT_TIMEOUT)).To(Exit(0))

		return string(response.Out.Contents())
	}, DEFAULT_TIMEOUT, "1s").ShouldNot(ContainSubstring(`"service_instance_guid": null`))
}

func bindRouteToServiceWithParams(hostname, serviceInstanceName string, params string) {
	routeGuid := GetRouteGuid(hostname, "", DEFAULT_TIMEOUT)
	Expect(cf.Cf("bind-route-service", config.AppsDomain, serviceInstanceName,
		"-f",
		"--hostname", hostname,
		"-c", fmt.Sprintf("{\"parameters\": %s}", params),
	).Wait(DEFAULT_TIMEOUT)).To(Exit(0))

	Eventually(func() string {
		response := cf.Cf("curl", fmt.Sprintf("/v2/routes/%s", routeGuid))
		Expect(response.Wait(DEFAULT_TIMEOUT)).To(Exit(0))

		return string(response.Out.Contents())
	}, DEFAULT_TIMEOUT, "1s").ShouldNot(ContainSubstring(`"service_instance_guid": null`))
}

func unbindRouteFromService(hostname, serviceInstanceName string) {
	routeGuid := GetRouteGuid(hostname, "", DEFAULT_TIMEOUT)
	Expect(cf.Cf("unbind-route-service", config.AppsDomain, serviceInstanceName,
		"-f",
		"--hostname", hostname,
	).Wait(DEFAULT_TIMEOUT)).To(Exit(0))

	Eventually(func() string {
		response := cf.Cf("curl", fmt.Sprintf("/v2/routes/%s", routeGuid))
		Expect(response.Wait(DEFAULT_TIMEOUT)).To(Exit(0))

		return string(response.Out.Contents())
	}, DEFAULT_TIMEOUT, "1s").Should(ContainSubstring(`"service_instance_guid": null`))
}

func createServiceInstance(serviceInstanceName, serviceName string) {
	Expect(cf.Cf("create-service", serviceName, "fake-plan", serviceInstanceName).Wait(DEFAULT_TIMEOUT)).To(Exit(0))
}

func deleteServiceInstance(serviceInstanceName string) {
	Expect(cf.Cf("delete-service", serviceInstanceName, "-f").Wait(DEFAULT_TIMEOUT)).To(Exit(0))
}

func configureBroker(serviceBrokerAppName, routeServiceName string) {
	brokerConfigJson := helpers.CurlApp(serviceBrokerAppName, "/config")

	var brokerConfigMap customMap

	err := json.Unmarshal([]byte(brokerConfigJson), &brokerConfigMap)
	Expect(err).NotTo(HaveOccurred())

	if routeServiceName != "" {
		routeServiceUrl := helpers.AppUri(routeServiceName, "/")
		url, err := url.Parse(routeServiceUrl)
		Expect(err).NotTo(HaveOccurred())
		url.Scheme = "https"
		routeServiceUrl = url.String()

		brokerConfigMap.key("behaviors").key("bind").key("default").key("body")["route_service_url"] = routeServiceUrl
	} else {
		body := brokerConfigMap.key("behaviors").key("bind").key("default").key("body")
		delete(body, "route_service_url")
	}
	changedJson, err := json.Marshal(brokerConfigMap)
	Expect(err).NotTo(HaveOccurred())

	helpers.CurlApp(serviceBrokerAppName, "/config", "-X", "POST", "-d", string(changedJson))
}

func createServiceBroker(brokerName, brokerAppName, serviceName string) {
	serviceBrokerAsset := assets.NewAssets().ServiceBroker
	PushApp(brokerAppName, serviceBrokerAsset, config.RubyBuildpackName, config.AppsDomain, CF_PUSH_TIMEOUT)

	initiateBrokerConfig(serviceName, brokerAppName)

	brokerUrl := helpers.AppUri(brokerAppName, "")

	config = helpers.LoadConfig()
	context := workflowhelpers.NewContext(config)
	workflowhelpers.AsUser(context.AdminUserContext(), context.ShortTimeout(), func() {
		session := cf.Cf("create-service-broker", brokerName, "user", "password", brokerUrl)
		Expect(session.Wait(DEFAULT_TIMEOUT)).To(Exit(0))

		session = cf.Cf("enable-service-access", serviceName)
		Expect(session.Wait(DEFAULT_TIMEOUT)).To(Exit(0))

	})
}

func deleteServiceBroker(brokerName string) {
	context := workflowhelpers.NewContext(config)
	workflowhelpers.AsUser(context.AdminUserContext(), context.ShortTimeout(), func() {
		responseBuffer := cf.Cf("delete-service-broker", brokerName, "-f")
		Expect(responseBuffer.Wait(DEFAULT_TIMEOUT)).To(Exit(0))
	})
}

func initiateBrokerConfig(serviceName, serviceBrokerAppName string) {
	brokerConfigJson := helpers.CurlApp(serviceBrokerAppName, "/config")

	var brokerConfigMap customMap

	err := json.Unmarshal([]byte(brokerConfigJson), &brokerConfigMap)
	Expect(err).NotTo(HaveOccurred())

	dashboardClientId := random_name.CATSRandomName("DASHBOARD-ID")
	serviceId := random_name.CATSRandomName("SVC-ID")

	services := brokerConfigMap.key("behaviors").key("catalog").key("body")["services"].([]interface{})
	service := services[0].(map[string]interface{})
	service["dashboard_client"].(map[string]interface{})["id"] = dashboardClientId
	service["name"] = serviceName
	service["id"] = serviceId

	plans := service["plans"].([]interface{})

	for i, plan := range plans {
		servicePlanId := random_name.CATSRandomName(fmt.Sprintf("SVC-PLAN-ID-%d-", i))
		plan.(map[string]interface{})["id"] = servicePlanId
	}

	changedJson, err := json.Marshal(brokerConfigMap)
	Expect(err).NotTo(HaveOccurred())

	helpers.CurlApp(serviceBrokerAppName, "/config", "-X", "POST", "-d", string(changedJson))
}
<commit_msg>Move skip logic to a top-level context<commit_after>package route_services

import (
	"encoding/json"
	"fmt"
	"net/url"

	. "code.cloudfoundry.org/cf-routing-test-helpers/helpers"
	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/cloudfoundry-incubator/cf-test-helpers/helpers"
	"github.com/cloudfoundry-incubator/cf-test-helpers/workflowhelpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/assets"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/random_name"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = RouteServicesDescribe("Route Services", func() {
	BeforeEach(func() {
		if config.Backend != "diego" {
			Skip(`Skipping this test because config.Backend is not set to 'diego'
NOTE: Ensure your platform is running Diego before enabling this test`)
		}
	})

	Context("when a route binds to a service", func() {
		Context("when service broker returns a route service url", func() {
			var (
				serviceInstanceName      string
				brokerName               string
				appName                  string
				routeServiceName         string
				golangAsset              = assets.NewAssets().Golang
				loggingRouteServiceAsset = assets.NewAssets().LoggingRouteService
			)

			BeforeEach(func() {
				routeServiceName = random_name.CATSRandomName("APP")
				brokerName = random_name.CATSRandomName("BROKER")
				serviceInstanceName = random_name.CATSRandomName("SVCINS")
				appName = random_name.CATSRandomName("APP")

				serviceName := random_name.CATSRandomName("SVC")
				brokerAppName := random_name.CATSRandomName("APP")

				createServiceBroker(brokerName, brokerAppName, serviceName)
				createServiceInstance(serviceInstanceName, serviceName)

				PushAppNoStart(appName, golangAsset, config.GoBuildpackName, config.AppsDomain, CF_PUSH_TIMEOUT)
				EnableDiego(appName, DEFAULT_TIMEOUT)
				StartApp(appName, CF_PUSH_TIMEOUT)

				PushApp(routeServiceName, loggingRouteServiceAsset, config.GoBuildpackName, config.AppsDomain, CF_PUSH_TIMEOUT)
				configureBroker(brokerAppName, routeServiceName)

				bindRouteToService(appName, serviceInstanceName)
			})

			AfterEach(func() {
				AppReport(appName, DEFAULT_TIMEOUT)
				AppReport(routeServiceName, DEFAULT_TIMEOUT)

				unbindRouteFromService(appName, serviceInstanceName)
				deleteServiceInstance(serviceInstanceName)
				deleteServiceBroker(brokerName)
				DeleteApp(appName, DEFAULT_TIMEOUT)
				DeleteApp(routeServiceName, DEFAULT_TIMEOUT)
			})

			It("a request to the app is routed through the route service", func() {
				Eventually(func() *Session {
					helpers.CurlAppRoot(appName)
					logs := cf.Cf("logs", "--recent", routeServiceName)
					Expect(logs.Wait(DEFAULT_TIMEOUT)).To(Exit(0))
					return logs
				}, DEFAULT_TIMEOUT).Should(Say("Response Body: go, world"))
			})
		})

		Context("when service broker does not return a route service url", func() {
			var (
				serviceInstanceName string
				brokerName          string
				appName             string
				golangAsset         = assets.NewAssets().Golang
			)

			BeforeEach(func() {
				appName = random_name.CATSRandomName("APP")
				brokerName = random_name.CATSRandomName("BROKER")
				serviceInstanceName = random_name.CATSRandomName("SVCINS")

				brokerAppName := random_name.CATSRandomName("APP")
				serviceName := random_name.CATSRandomName("SVC")

				createServiceBroker(brokerName, brokerAppName, serviceName)
				createServiceInstance(serviceInstanceName, serviceName)

				PushAppNoStart(appName, golangAsset, config.GoBuildpackName, config.AppsDomain, CF_PUSH_TIMEOUT)
				EnableDiego(appName, DEFAULT_TIMEOUT)
				StartApp(appName, CF_PUSH_TIMEOUT)

				configureBroker(brokerAppName, "")

				bindRouteToService(appName, serviceInstanceName)
			})

			AfterEach(func() {
				AppReport(appName, DEFAULT_TIMEOUT)

				unbindRouteFromService(appName, serviceInstanceName)
				deleteServiceInstance(serviceInstanceName)
				deleteServiceBroker(brokerName)
				DeleteApp(appName, DEFAULT_TIMEOUT)
			})

			It("routes to an app", func() {
				Eventually(func() string {
					return helpers.CurlAppRoot(appName)
				}, DEFAULT_TIMEOUT).Should(ContainSubstring("go, world"))
			})
		})

		Context("when arbitrary parameters are sent", func() {
			var (
				serviceInstanceName string
				brokerName          string
				brokerAppName       string
				hostname            string
			)

			BeforeEach(func() {
				hostname = random_name.CATSRandomName("ROUTE")
				brokerAppName = random_name.CATSRandomName("APP")
				serviceInstanceName = random_name.CATSRandomName("SVCINS")
				brokerName = random_name.CATSRandomName("BROKER")

				serviceName := random_name.CATSRandomName("SVC")

				createServiceBroker(brokerName, brokerAppName, serviceName)
				createServiceInstance(serviceInstanceName, serviceName)

				CreateRoute(hostname, "", context.RegularUserContext().Space, config.AppsDomain, DEFAULT_TIMEOUT)

				configureBroker(brokerAppName, "")
			})

			AfterEach(func() {
				unbindRouteFromService(hostname, serviceInstanceName)
				deleteServiceInstance(serviceInstanceName)
				deleteServiceBroker(brokerName)
				DeleteRoute(hostname, "", config.AppsDomain, DEFAULT_TIMEOUT)
			})

			It("passes them to the service broker", func() {
				bindRouteToServiceWithParams(hostname, serviceInstanceName, "{\"key1\":[\"value1\",\"irynaparam\"],\"key2\":\"value3\"}")

				Eventually(func() *Session {
					logs := cf.Cf("logs", "--recent", brokerAppName)
					Expect(logs.Wait(DEFAULT_TIMEOUT)).To(Exit(0))
					return logs
				}, DEFAULT_TIMEOUT).Should(Say("irynaparam"))
			})
		})
	})
})

type customMap map[string]interface{}

func (c customMap) key(key string) customMap {
	return c[key].(map[string]interface{})
}

func bindRouteToService(hostname, serviceInstanceName string) {
	routeGuid := GetRouteGuid(hostname, "", DEFAULT_TIMEOUT)

	Expect(cf.Cf("bind-route-service", config.AppsDomain, serviceInstanceName,
		"-f",
		"--hostname", hostname,
	).Wait(DEFAULT_TIMEOUT)).To(Exit(0))

	Eventually(func() string {
		response := cf.Cf("curl", fmt.Sprintf("/v2/routes/%s", routeGuid))
		Expect(response.Wait(DEFAULT_TIMEOUT)).To(Exit(0))

		return string(response.Out.Contents())
	}, DEFAULT_TIMEOUT, "1s").ShouldNot(ContainSubstring(`"service_instance_guid": null`))
}

func bindRouteToServiceWithParams(hostname, serviceInstanceName string, params string) {
	routeGuid := GetRouteGuid(hostname, "", DEFAULT_TIMEOUT)
	Expect(cf.Cf("bind-route-service", config.AppsDomain, serviceInstanceName,
		"-f",
		"--hostname", hostname,
		"-c", fmt.Sprintf("{\"parameters\": %s}", params),
	).Wait(DEFAULT_TIMEOUT)).To(Exit(0))

	Eventually(func() string {
		response := cf.Cf("curl", fmt.Sprintf("/v2/routes/%s", routeGuid))
		Expect(response.Wait(DEFAULT_TIMEOUT)).To(Exit(0))

		return string(response.Out.Contents())
	}, DEFAULT_TIMEOUT, "1s").ShouldNot(ContainSubstring(`"service_instance_guid": null`))
}

func unbindRouteFromService(hostname, serviceInstanceName string) {
	routeGuid := GetRouteGuid(hostname, "", DEFAULT_TIMEOUT)
	Expect(cf.Cf("unbind-route-service", config.AppsDomain, serviceInstanceName,
		"-f",
		"--hostname", hostname,
	).Wait(DEFAULT_TIMEOUT)).To(Exit(0))

	Eventually(func() string {
		response := cf.Cf("curl", fmt.Sprintf("/v2/routes/%s", routeGuid))
		Expect(response.Wait(DEFAULT_TIMEOUT)).To(Exit(0))

		return string(response.Out.Contents())
	}, DEFAULT_TIMEOUT, "1s").Should(ContainSubstring(`"service_instance_guid": null`))
}

func createServiceInstance(serviceInstanceName, serviceName string) {
	Expect(cf.Cf("create-service", serviceName, "fake-plan", serviceInstanceName).Wait(DEFAULT_TIMEOUT)).To(Exit(0))
}

func deleteServiceInstance(serviceInstanceName string) {
	Expect(cf.Cf("delete-service", serviceInstanceName, "-f").Wait(DEFAULT_TIMEOUT)).To(Exit(0))
}

func configureBroker(serviceBrokerAppName, routeServiceName string) {
	brokerConfigJson := helpers.CurlApp(serviceBrokerAppName, "/config")

	var brokerConfigMap customMap

	err := json.Unmarshal([]byte(brokerConfigJson), &brokerConfigMap)
	Expect(err).NotTo(HaveOccurred())

	if routeServiceName != "" {
		routeServiceUrl := helpers.AppUri(routeServiceName, "/")
		url, err := url.Parse(routeServiceUrl)
		Expect(err).NotTo(HaveOccurred())
		url.Scheme = "https"
		routeServiceUrl = url.String()

		brokerConfigMap.key("behaviors").key("bind").key("default").key("body")["route_service_url"] = routeServiceUrl
	} else {
		body := brokerConfigMap.key("behaviors").key("bind").key("default").key("body")
		delete(body, "route_service_url")
	}
	changedJson, err := json.Marshal(brokerConfigMap)
	Expect(err).NotTo(HaveOccurred())

	helpers.CurlApp(serviceBrokerAppName, "/config", "-X", "POST", "-d", string(changedJson))
}

func createServiceBroker(brokerName, brokerAppName, serviceName string) {
	serviceBrokerAsset := assets.NewAssets().ServiceBroker
	PushApp(brokerAppName, serviceBrokerAsset, config.RubyBuildpackName, config.AppsDomain, CF_PUSH_TIMEOUT)

	initiateBrokerConfig(serviceName, brokerAppName)

	brokerUrl := helpers.AppUri(brokerAppName, "")

	config = helpers.LoadConfig()
	context := workflowhelpers.NewContext(config)
	workflowhelpers.AsUser(context.AdminUserContext(), context.ShortTimeout(), func() {
		session := cf.Cf("create-service-broker", brokerName, "user", "password", brokerUrl)
		Expect(session.Wait(DEFAULT_TIMEOUT)).To(Exit(0))

		session = cf.Cf("enable-service-access", serviceName)
		Expect(session.Wait(DEFAULT_TIMEOUT)).To(Exit(0))

	})
}

func deleteServiceBroker(brokerName string) {
	context := workflowhelpers.NewContext(config)
	workflowhelpers.AsUser(context.AdminUserContext(), context.ShortTimeout(), func() {
		responseBuffer := cf.Cf("delete-service-broker", brokerName, "-f")
		Expect(responseBuffer.Wait(DEFAULT_TIMEOUT)).To(Exit(0))
	})
}

func initiateBrokerConfig(serviceName, serviceBrokerAppName string) {
	brokerConfigJson := helpers.CurlApp(serviceBrokerAppName, "/config")

	var brokerConfigMap customMap

	err := json.Unmarshal([]byte(brokerConfigJson), &brokerConfigMap)
	Expect(err).NotTo(HaveOccurred())

	dashboardClientId := random_name.CATSRandomName("DASHBOARD-ID")
	serviceId := random_name.CATSRandomName("SVC-ID")

	services := brokerConfigMap.key("behaviors").key("catalog").key("body")["services"].([]interface{})
	service := services[0].(map[string]interface{})
	service["dashboard_client"].(map[string]interface{})["id"] = dashboardClientId
	service["name"] = serviceName
	service["id"] = serviceId

	plans := service["plans"].([]interface{})

	for i, plan := range plans {
		servicePlanId := random_name.CATSRandomName(fmt.Sprintf("SVC-PLAN-ID-%d-", i))
		plan.(map[string]interface{})["id"] = servicePlanId
	}

	changedJson, err := json.Marshal(brokerConfigMap)
	Expect(err).NotTo(HaveOccurred())

	helpers.CurlApp(serviceBrokerAppName, "/config", "-X", "POST", "-d", string(changedJson))
}
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package builder

import (
	"os"
	"path/filepath"

	"mynewt.apache.org/newt/newt/pkg"
	"mynewt.apache.org/newt/newt/target"
	"mynewt.apache.org/newt/newt/toolchain"
	"mynewt.apache.org/newt/util"
)

type Builder struct {
	Packages map[*pkg.LocalPackage]*BuildPackage
	features map[string]bool
	apis     map[string]*BuildPackage

	appPkg       *BuildPackage
	Bsp          *pkg.BspPackage
	compilerPkg  *pkg.LocalPackage
	compilerInfo *toolchain.CompilerInfo

	target *target.Target
}

func NewBuilder(target *target.Target) (*Builder, error) {
	b := &Builder{}

	if err := b.Init(target); err != nil {
		return nil, err
	}

	return b, nil
}

func (b *Builder) Init(target *target.Target) error {
	b.target = target

	b.Packages = map[*pkg.LocalPackage]*BuildPackage{}
	b.features = map[string]bool{}
	b.apis = map[string]*BuildPackage{}

	return nil
}

func (b *Builder) Features() map[string]bool {
	return b.features
}

func (b *Builder) AddFeature(feature string) {
	b.features[feature] = true
}

func (b *Builder) AddPackage(npkg *pkg.LocalPackage) *BuildPackage {
	// Don't allow nil entries to the map
	if npkg == nil {
		panic("Cannot add nil package builder map")
	}

	bpkg := b.Packages[npkg]
	if bpkg == nil {
		bpkg = NewBuildPackage(npkg)
		b.Packages[npkg] = bpkg
	}

	return bpkg
}

// @return bool                 true if this is a new API.
func (b *Builder) AddApi(apiString string, bpkg *BuildPackage) bool {
	curBpkg := b.apis[apiString]
	if curBpkg == nil {
		b.apis[apiString] = bpkg
		return true
	} else {
		if curBpkg != bpkg {
			util.StatusMessage(util.VERBOSITY_QUIET,
				"Warning: API conflict: %s (%s <-> %s)\n", apiString,
				curBpkg.Name(), bpkg.Name())
		}
		return false
	}
}

func (b *Builder) loadDeps() error {
	// Circularly resolve dependencies, identities, APIs, and required APIs
	// until no new ones exist.
	for {
		reprocess := false
		for _, bpkg := range b.Packages {
			newDeps, newFeatures, err := bpkg.Resolve(b)
			if err != nil {
				return err
			}

			if newFeatures {
				// A new supported feature was discovered.  It is impossible to
				// determine what new dependency and API requirements are
				// generated as a result.  All packages need to be reprocessed.
				for _, bpkg := range b.Packages {
					bpkg.depsResolved = false
					bpkg.apisSatisfied = false
				}
				reprocess = true
				break
			}
			if newDeps {
				// The new dependencies need to be processed.  Iterate again
				// after this iteration completes.
				reprocess = true
			}
		}

		if !reprocess {
			break
		}
	}

	return nil
}

// Recursively compiles all the .c and .s files in the specified directory.
// Architecture-specific files are also compiled.
func buildDir(srcDir string, c *toolchain.Compiler, arch string,
	ignDirs []string) error {

	// Quietly succeed if the source directory doesn't exist.
	if util.NodeNotExist(srcDir) {
		return nil
	}

	util.StatusMessage(util.VERBOSITY_VERBOSE,
		"Compiling src in base directory: %s\n", srcDir)

	// Start from the source directory.
	if err := os.Chdir(srcDir); err != nil {
		return util.NewNewtError(err.Error())
	}

	// Ignore architecture-specific source files for now.  Use a temporary
	// string slice here so that the "arch" directory is not ignored in the
	// subsequent architecture-specific compile phase.
	if err := c.RecursiveCompile(toolchain.COMPILER_TYPE_C,
		append(ignDirs, "arch")); err != nil {

		return err
	}

	archDir := srcDir + "/arch/" + arch + "/"
	if util.NodeExist(archDir) {
		util.StatusMessage(util.VERBOSITY_VERBOSE,
			"Compiling architecture specific src pkgs in directory: %s\n",
			archDir)
		if err := os.Chdir(archDir); err != nil {
			return util.NewNewtError(err.Error())
		}

		// Compile C source.
		if err := c.RecursiveCompile(toolchain.COMPILER_TYPE_C,
			ignDirs); err != nil {

			return err
		}

		// Compile assembly source (only architecture-specific).
		if err := c.RecursiveCompile(toolchain.COMPILER_TYPE_ASM,
			ignDirs); err != nil {

			return err
		}
	}

	return nil
}

func (b *Builder) newCompiler(bpkg *BuildPackage,
	dstDir string) (*toolchain.Compiler, error) {

	c, err := toolchain.NewCompiler(b.compilerPkg.BasePath(), dstDir,
		b.target.BuildProfile)
	if err != nil {
		return nil, err
	}
	c.AddInfo(b.compilerInfo)

	if bpkg != nil {
		ci, err := bpkg.CompilerInfo(b)
		if err != nil {
			return nil, err
		}
		c.AddInfo(ci)
	}

	// Specify all the source yml files as dependencies.  If a yml file has
	// changed, a full rebuild is required.
	for _, bp := range b.Packages {
		c.AddDeps(bp.CfgFilenames()...)
	}

	return c, nil
}

// Compiles and archives a package.
func (b *Builder) buildPackage(bpkg *BuildPackage) error {
	srcDir := bpkg.BasePath() + "/src"
	if util.NodeNotExist(srcDir) {
		// Nothing to compile.
		return nil
	}

	c, err := b.newCompiler(bpkg, b.PkgBinDir(bpkg.Name()))
	if err != nil {
		return err
	}

	// Build the package source in two phases:
	// 1. Non-test code.
	// 2. Test code (if the "test" feature is enabled).
	//
	// This is done in two passes because the structure of
	// architecture-specific directories is different for normal code and test
	// code, and not easy to generalize into a single operation:
	//     * src/arch/<target-arch>
	//     * src/test/arch/<target-arch>
	if err = buildDir(srcDir, c, b.Bsp.Arch, []string{"test"}); err != nil {
		return err
	}
	if b.features["TEST"] {
		testSrcDir := srcDir + "/test"
		if err = buildDir(testSrcDir, c, b.Bsp.Arch, nil); err != nil {
			return err
		}
	}

	// Create a static library ("archive").
	if err := os.Chdir(bpkg.BasePath() + "/"); err != nil {
		return util.NewNewtError(err.Error())
	}
	archiveFile := b.ArchivePath(bpkg.Name())
	if err = c.CompileArchive(archiveFile); err != nil {
		return err
	}

	return nil
}

func (b *Builder) link(elfName string) error {
	c, err := b.newCompiler(b.appPkg, b.PkgBinDir(elfName))
	if err != nil {
		return err
	}

	pkgNames := []string{}
	for _, bpkg := range b.Packages {
		archivePath := b.ArchivePath(bpkg.Name())
		if util.NodeExist(archivePath) {
			pkgNames = append(pkgNames, archivePath)
		}
	}

	if b.Bsp.LinkerScript != "" {
		c.LinkerScript = b.Bsp.BasePath() + b.Bsp.LinkerScript
	}
	err = c.CompileElf(elfName, pkgNames)
	if err != nil {
		return err
	}

	return nil
}

// Populates the builder with all the packages that need to be built and
// configures each package's build settings.  After this function executes,
// packages are ready to be built.
func (b *Builder) PrepBuild() error {
	if b.Bsp != nil {
		// Already prepped
		return nil
	}

	// Collect the seed packages.
	bspPkg := b.target.Bsp()
	if bspPkg == nil {
		if b.target.BspName == "" {
			return util.NewNewtError("BSP package not specified by target")
		} else {
			return util.NewNewtError("BSP package not found: " +
				b.target.BspName)
		}
	}

	b.Bsp = pkg.NewBspPackage(bspPkg)
	compilerPkg := b.resolveCompiler()
	if compilerPkg == nil {
		if b.Bsp.CompilerName == "" {
			return util.NewNewtError("Compiler package not specified by BSP")
		} else {
			return util.NewNewtError("Compiler package not found: " +
				b.Bsp.CompilerName)
		}
	}

	// An app package is not required (e.g., unit tests).
	appPkg := b.target.App()

	// Seed the builder with the app (if present), bsp, and target packages.

	var appBpkg *BuildPackage
	if appPkg != nil {
		appBpkg = b.Packages[appPkg]
		if appBpkg == nil {
			appBpkg = b.AddPackage(appPkg)
		}
		b.appPkg = appBpkg
	}

	bspBpkg := b.Packages[bspPkg]
	if bspBpkg == nil {
		bspBpkg = b.AddPackage(bspPkg)
	}

	targetBpkg := b.AddPackage(b.target.Package())

	// Populate the full set of packages to be built and resolve the feature
	// set.
	if err := b.loadDeps(); err != nil {
		return err
	}

	b.logDepInfo()

	// Terminate if any package has an unmet API requirement.
	if err := b.verifyApisSatisfied(); err != nil {
		return err
	}

	// Populate the base set of compiler flags.  Flags from the following
	// packages get applied to every source file:
	//     * app (if present)
	//     * bsp
	//     * compiler (not added here)
	//     * target

	baseCi := toolchain.NewCompilerInfo()

	// App flags.
	if appBpkg != nil {
		appCi, err := appBpkg.CompilerInfo(b)
		if err != nil {
			return err
		}
		baseCi.AddCompilerInfo(appCi)
	}

	// Bsp flags.
	bspCi, err := bspBpkg.CompilerInfo(b)
	if err != nil {
		return err
	}
	// Define a cpp symbol indicating the BSP architecture.
	bspCi.Cflags = append(bspCi.Cflags, "-DARCH_"+b.Bsp.Arch)
	baseCi.AddCompilerInfo(bspCi)

	// Target flags.
	targetCi, err := targetBpkg.CompilerInfo(b)
	if err != nil {
		return err
	}

	baseCi.AddCompilerInfo(targetCi)

	// Note: Compiler flags get added when compiler is created.

	// Read the BSP configuration.  These settings are necessary for the link
	// step.
	if err := b.Bsp.Reload(b.Features()); err != nil {
		return err
	}

	b.compilerPkg = compilerPkg
	b.compilerInfo = baseCi

	return nil
}

func (b *Builder) Build() error {
	if err := b.target.Validate(true); err != nil {
		return err
	}

	// Populate the package and feature sets and calculate the base compiler
	// flags.
	if err := b.PrepBuild(); err != nil {
		return err
	}

	// XXX: If any yml files have changed, a full rebuild is required.  We
	// don't currently check this.

	for _, bpkg := range b.Packages {
		if err := b.buildPackage(bpkg); err != nil {
			return err
		}
	}

	if err := b.link(b.AppElfPath()); err != nil {
		return err
	}

	return nil
}

func (b *Builder) Test(p *pkg.LocalPackage) error {
	if err := b.target.Validate(false); err != nil {
		return err
	}

	// Seed the builder with the package under test.
	testBpkg := b.AddPackage(p)

	// A few features are automatically supported when the test command is
	// used:
	//     * TEST:      ensures that the test code gets compiled.
	//     * SELFTEST:  indicates that there is no app.
	b.AddFeature("TEST")
	b.AddFeature("SELFTEST")

	// Populate the package and feature sets and calculate the base compiler
	// flags.
	err := b.PrepBuild()
	if err != nil {
		return err
	}

	// Define the PKG_TEST symbol while the package under test is being
	// compiled.  This symbol enables the appropriate main function that
	// usually comes from an app.
	testPkgCi, err := testBpkg.CompilerInfo(b)
	if err != nil {
		return err
	}
	testPkgCi.Cflags = append(testPkgCi.Cflags, "-DMYNEWT_SELFTEST")

	// XXX: If any yml files have changed, a full rebuild is required.  We
	// don't currently check this.

	for _, bpkg := range b.Packages {
		err = b.buildPackage(bpkg)
		if err != nil {
			return err
		}
	}

	testFilename := b.TestExePath(p.Name())
	err = b.link(testFilename)
	if err != nil {
		return err
	}

	// Run the tests.
	if err := os.Chdir(filepath.Dir(testFilename)); err != nil {
		return err
	}

	util.StatusMessage(util.VERBOSITY_DEFAULT, "Executing test: %s\n",
		testFilename)
	if _, err := util.ShellCommand(testFilename); err != nil {
		return util.NewNewtError("Test failure: " + err.Error())
	}

	return nil
}

func (b *Builder) Clean() error {
	path := b.BinDir()
	util.StatusMessage(util.VERBOSITY_VERBOSE, "Cleaning directory %s\n", path)
	err := os.RemoveAll(path)
	return err
}
<commit_msg>Remove obsolete comment.<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package builder

import (
	"os"
	"path/filepath"

	"mynewt.apache.org/newt/newt/pkg"
	"mynewt.apache.org/newt/newt/target"
	"mynewt.apache.org/newt/newt/toolchain"
	"mynewt.apache.org/newt/util"
)

type Builder struct {
	Packages map[*pkg.LocalPackage]*BuildPackage
	features map[string]bool
	apis     map[string]*BuildPackage

	appPkg       *BuildPackage
	Bsp          *pkg.BspPackage
	compilerPkg  *pkg.LocalPackage
	compilerInfo *toolchain.CompilerInfo

	target *target.Target
}

func NewBuilder(target *target.Target) (*Builder, error) {
	b := &Builder{}

	if err := b.Init(target); err != nil {
		return nil, err
	}

	return b, nil
}

func (b *Builder) Init(target *target.Target) error {
	b.target = target

	b.Packages = map[*pkg.LocalPackage]*BuildPackage{}
	b.features = map[string]bool{}
	b.apis = map[string]*BuildPackage{}

	return nil
}

func (b *Builder) Features() map[string]bool {
	return b.features
}

func (b *Builder) AddFeature(feature string) {
	b.features[feature] = true
}

func (b *Builder) AddPackage(npkg *pkg.LocalPackage) *BuildPackage {
	// Don't allow nil entries to the map
	if npkg == nil {
		panic("Cannot add nil package builder map")
	}

	bpkg := b.Packages[npkg]
	if bpkg == nil {
		bpkg = NewBuildPackage(npkg)
		b.Packages[npkg] = bpkg
	}

	return bpkg
}

// @return bool                 true if this is a new API.
func (b *Builder) AddApi(apiString string, bpkg *BuildPackage) bool {
	curBpkg := b.apis[apiString]
	if curBpkg == nil {
		b.apis[apiString] = bpkg
		return true
	} else {
		if curBpkg != bpkg {
			util.StatusMessage(util.VERBOSITY_QUIET,
				"Warning: API conflict: %s (%s <-> %s)\n", apiString,
				curBpkg.Name(), bpkg.Name())
		}
		return false
	}
}

func (b *Builder) loadDeps() error {
	// Circularly resolve dependencies, identities, APIs, and required APIs
	// until no new ones exist.
	for {
		reprocess := false
		for _, bpkg := range b.Packages {
			newDeps, newFeatures, err := bpkg.Resolve(b)
			if err != nil {
				return err
			}

			if newFeatures {
				// A new supported feature was discovered.  It is impossible to
				// determine what new dependency and API requirements are
				// generated as a result.  All packages need to be reprocessed.
				for _, bpkg := range b.Packages {
					bpkg.depsResolved = false
					bpkg.apisSatisfied = false
				}
				reprocess = true
				break
			}
			if newDeps {
				// The new dependencies need to be processed.  Iterate again
				// after this iteration completes.
				reprocess = true
			}
		}

		if !reprocess {
			break
		}
	}

	return nil
}

// Recursively compiles all the .c and .s files in the specified directory.
// Architecture-specific files are also compiled.
func buildDir(srcDir string, c *toolchain.Compiler, arch string,
	ignDirs []string) error {

	// Quietly succeed if the source directory doesn't exist.
	if util.NodeNotExist(srcDir) {
		return nil
	}

	util.StatusMessage(util.VERBOSITY_VERBOSE,
		"Compiling src in base directory: %s\n", srcDir)

	// Start from the source directory.
	if err := os.Chdir(srcDir); err != nil {
		return util.NewNewtError(err.Error())
	}

	// Ignore architecture-specific source files for now.  Use a temporary
	// string slice here so that the "arch" directory is not ignored in the
	// subsequent architecture-specific compile phase.
	if err := c.RecursiveCompile(toolchain.COMPILER_TYPE_C,
		append(ignDirs, "arch")); err != nil {

		return err
	}

	archDir := srcDir + "/arch/" + arch + "/"
	if util.NodeExist(archDir) {
		util.StatusMessage(util.VERBOSITY_VERBOSE,
			"Compiling architecture specific src pkgs in directory: %s\n",
			archDir)
		if err := os.Chdir(archDir); err != nil {
			return util.NewNewtError(err.Error())
		}

		// Compile C source.
		if err := c.RecursiveCompile(toolchain.COMPILER_TYPE_C,
			ignDirs); err != nil {

			return err
		}

		// Compile assembly source (only architecture-specific).
		if err := c.RecursiveCompile(toolchain.COMPILER_TYPE_ASM,
			ignDirs); err != nil {

			return err
		}
	}

	return nil
}

func (b *Builder) newCompiler(bpkg *BuildPackage,
	dstDir string) (*toolchain.Compiler, error) {

	c, err := toolchain.NewCompiler(b.compilerPkg.BasePath(), dstDir,
		b.target.BuildProfile)
	if err != nil {
		return nil, err
	}
	c.AddInfo(b.compilerInfo)

	if bpkg != nil {
		ci, err := bpkg.CompilerInfo(b)
		if err != nil {
			return nil, err
		}
		c.AddInfo(ci)
	}

	// Specify all the source yml files as dependencies.  If a yml file has
	// changed, a full rebuild is required.
	for _, bp := range b.Packages {
		c.AddDeps(bp.CfgFilenames()...)
	}

	return c, nil
}

// Compiles and archives a package.
func (b *Builder) buildPackage(bpkg *BuildPackage) error {
	srcDir := bpkg.BasePath() + "/src"
	if util.NodeNotExist(srcDir) {
		// Nothing to compile.
		return nil
	}

	c, err := b.newCompiler(bpkg, b.PkgBinDir(bpkg.Name()))
	if err != nil {
		return err
	}

	// Build the package source in two phases:
	// 1. Non-test code.
	// 2. Test code (if the "test" feature is enabled).
	//
	// This is done in two passes because the structure of
	// architecture-specific directories is different for normal code and test
	// code, and not easy to generalize into a single operation:
	//     * src/arch/<target-arch>
	//     * src/test/arch/<target-arch>
	if err = buildDir(srcDir, c, b.Bsp.Arch, []string{"test"}); err != nil {
		return err
	}
	if b.features["TEST"] {
		testSrcDir := srcDir + "/test"
		if err = buildDir(testSrcDir, c, b.Bsp.Arch, nil); err != nil {
			return err
		}
	}

	// Create a static library ("archive").
	if err := os.Chdir(bpkg.BasePath() + "/"); err != nil {
		return util.NewNewtError(err.Error())
	}
	archiveFile := b.ArchivePath(bpkg.Name())
	if err = c.CompileArchive(archiveFile); err != nil {
		return err
	}

	return nil
}

func (b *Builder) link(elfName string) error {
	c, err := b.newCompiler(b.appPkg, b.PkgBinDir(elfName))
	if err != nil {
		return err
	}

	pkgNames := []string{}
	for _, bpkg := range b.Packages {
		archivePath := b.ArchivePath(bpkg.Name())
		if util.NodeExist(archivePath) {
			pkgNames = append(pkgNames, archivePath)
		}
	}

	if b.Bsp.LinkerScript != "" {
		c.LinkerScript = b.Bsp.BasePath() + b.Bsp.LinkerScript
	}
	err = c.CompileElf(elfName, pkgNames)
	if err != nil {
		return err
	}

	return nil
}

// Populates the builder with all the packages that need to be built and
// configures each package's build settings.  After this function executes,
// packages are ready to be built.
func (b *Builder) PrepBuild() error {
	if b.Bsp != nil {
		// Already prepped
		return nil
	}

	// Collect the seed packages.
	bspPkg := b.target.Bsp()
	if bspPkg == nil {
		if b.target.BspName == "" {
			return util.NewNewtError("BSP package not specified by target")
		} else {
			return util.NewNewtError("BSP package not found: " +
				b.target.BspName)
		}
	}

	b.Bsp = pkg.NewBspPackage(bspPkg)
	compilerPkg := b.resolveCompiler()
	if compilerPkg == nil {
		if b.Bsp.CompilerName == "" {
			return util.NewNewtError("Compiler package not specified by BSP")
		} else {
			return util.NewNewtError("Compiler package not found: " +
				b.Bsp.CompilerName)
		}
	}

	// An app package is not required (e.g., unit tests).
	appPkg := b.target.App()

	// Seed the builder with the app (if present), bsp, and target packages.

	var appBpkg *BuildPackage
	if appPkg != nil {
		appBpkg = b.Packages[appPkg]
		if appBpkg == nil {
			appBpkg = b.AddPackage(appPkg)
		}
		b.appPkg = appBpkg
	}

	bspBpkg := b.Packages[bspPkg]
	if bspBpkg == nil {
		bspBpkg = b.AddPackage(bspPkg)
	}

	targetBpkg := b.AddPackage(b.target.Package())

	// Populate the full set of packages to be built and resolve the feature
	// set.
	if err := b.loadDeps(); err != nil {
		return err
	}

	b.logDepInfo()

	// Terminate if any package has an unmet API requirement.
	if err := b.verifyApisSatisfied(); err != nil {
		return err
	}

	// Populate the base set of compiler flags.  Flags from the following
	// packages get applied to every source file:
	//     * app (if present)
	//     * bsp
	//     * compiler (not added here)
	//     * target

	baseCi := toolchain.NewCompilerInfo()

	// App flags.
	if appBpkg != nil {
		appCi, err := appBpkg.CompilerInfo(b)
		if err != nil {
			return err
		}
		baseCi.AddCompilerInfo(appCi)
	}

	// Bsp flags.
	bspCi, err := bspBpkg.CompilerInfo(b)
	if err != nil {
		return err
	}
	// Define a cpp symbol indicating the BSP architecture.
	bspCi.Cflags = append(bspCi.Cflags, "-DARCH_"+b.Bsp.Arch)
	baseCi.AddCompilerInfo(bspCi)

	// Target flags.
	targetCi, err := targetBpkg.CompilerInfo(b)
	if err != nil {
		return err
	}

	baseCi.AddCompilerInfo(targetCi)

	// Note: Compiler flags get added when compiler is created.

	// Read the BSP configuration.  These settings are necessary for the link
	// step.
	if err := b.Bsp.Reload(b.Features()); err != nil {
		return err
	}

	b.compilerPkg = compilerPkg
	b.compilerInfo = baseCi

	return nil
}

func (b *Builder) Build() error {
	if err := b.target.Validate(true); err != nil {
		return err
	}

	// Populate the package and feature sets and calculate the base compiler
	// flags.
	if err := b.PrepBuild(); err != nil {
		return err
	}

	for _, bpkg := range b.Packages {
		if err := b.buildPackage(bpkg); err != nil {
			return err
		}
	}

	if err := b.link(b.AppElfPath()); err != nil {
		return err
	}

	return nil
}

func (b *Builder) Test(p *pkg.LocalPackage) error {
	if err := b.target.Validate(false); err != nil {
		return err
	}

	// Seed the builder with the package under test.
	testBpkg := b.AddPackage(p)

	// A few features are automatically supported when the test command is
	// used:
	//     * TEST:      ensures that the test code gets compiled.
	//     * SELFTEST:  indicates that there is no app.
	b.AddFeature("TEST")
	b.AddFeature("SELFTEST")

	// Populate the package and feature sets and calculate the base compiler
	// flags.
	err := b.PrepBuild()
	if err != nil {
		return err
	}

	// Define the PKG_TEST symbol while the package under test is being
	// compiled.  This symbol enables the appropriate main function that
	// usually comes from an app.
	testPkgCi, err := testBpkg.CompilerInfo(b)
	if err != nil {
		return err
	}
	testPkgCi.Cflags = append(testPkgCi.Cflags, "-DMYNEWT_SELFTEST")

	for _, bpkg := range b.Packages {
		err = b.buildPackage(bpkg)
		if err != nil {
			return err
		}
	}

	testFilename := b.TestExePath(p.Name())
	err = b.link(testFilename)
	if err != nil {
		return err
	}

	// Run the tests.
	if err := os.Chdir(filepath.Dir(testFilename)); err != nil {
		return err
	}

	util.StatusMessage(util.VERBOSITY_DEFAULT, "Executing test: %s\n",
		testFilename)
	if _, err := util.ShellCommand(testFilename); err != nil {
		return util.NewNewtError("Test failure: " + err.Error())
	}

	return nil
}

func (b *Builder) Clean() error {
	path := b.BinDir()
	util.StatusMessage(util.VERBOSITY_VERBOSE, "Cleaning directory %s\n", path)
	err := os.RemoveAll(path)
	return err
}
<|endoftext|>


<commit_before>package openrtb2

import "encoding/json"

// 3.2.29 Object: UserAgent
//
// This object contains a single user identifier provided as part of extended identifiers.
// The exchange should ensure that business agreements allow for the sending of this data.
type UserAgent struct {

	// Attribute:
	//   browsers
	// Type:
	//   object array; recommended
	// Description:
	//   Each BrandVersion object (see Section 3.2.30) identifies a browser or similar
	//   software component. Implementers should send brands and versions
	//   derived from the Sec-CH-UA-Full-Version-List header.
	Browsers []BrandVersion `json:"browsers,omitempty"`

	// Attribute:
	//   platform
	// Type:
	//   object; recommended
	// Description:
	//   A BrandVersion object (see Section 3.2.30) that identifies the user agent’s
	//   execution platform / OS. Implementers should send a brand derived from the
	//   Sec-CH-UA-Platform header, and version derived from the Sec-CH-UA-
	//   Platform-Version header.
	Platform *BrandVersion `json:"platform,omitempty"`

	// Attribute:
	//   mobile
	// Type:
	//   integer
	// Description:
	//   1 if the agent prefers a “mobile” version of the content, if available, i.e.
	//   optimized for small screens or touch input. 0 if the agent prefers the “desktop”
	//   or “full” content. Implementers should derive this value from the Sec-CH-UA-
	//   Mobile header.
	Mobile *int8 `json:"mobile,omitempty"`

	// Attribute:
	//   architecture
	// Type:
	//   string
	// Description:
	//   Device’s major binary architecture, e.g. “x86” or “arm”. Implementers should
	//   retrieve this value from the Sec-CH-UA-Arch header.
	Architecture string `json:"architecture,omitempty"`

	// Attribute:
	//   bitness
	// Type:
	//   string
	// Description:
	//   Device’s bitness, e.g. “64” for 64-bit architecture. Implementers should
	//   retrieve this value from the Sec-CH-UA-Bitness header.
	Bitness string `json:"bitness,omitempty"`

	// Attribute:
	//   model
	// Type:
	//   string
	// Description:
	//   Device model. Implementers should retrieve this value from the Sec-CH-UA-
	//   Model header.
	Model string `json:"model,omitempty"`

	// Attribute:
	//   source
	// Type:
	//   integer; default 0
	// Description:
	//   The source of data used to create this object, List: User-Agent Source in
	//   AdCOM 1.0.
	Source int8 `json:"source,omitempty"`

	// Attribute:
	//   ext
	// Type:
	//   object
	// Description:
	//   Placeholder for advertising-system specific extensions to this object.
	Ext json.RawMessage `json:"ext,omitempty"`
}
<commit_msg>UserAgentSource Enum<commit_after>package openrtb2

import (
	"encoding/json"

	"github.com/mxmCherry/openrtb/v16/adcom1"
)

// 3.2.29 Object: UserAgent
//
// This object contains a single user identifier provided as part of extended identifiers.
// The exchange should ensure that business agreements allow for the sending of this data.
type UserAgent struct {

	// Attribute:
	//   browsers
	// Type:
	//   object array; recommended
	// Description:
	//   Each BrandVersion object (see Section 3.2.30) identifies a browser or similar
	//   software component. Implementers should send brands and versions
	//   derived from the Sec-CH-UA-Full-Version-List header.
	Browsers []BrandVersion `json:"browsers,omitempty"`

	// Attribute:
	//   platform
	// Type:
	//   object; recommended
	// Description:
	//   A BrandVersion object (see Section 3.2.30) that identifies the user agent’s
	//   execution platform / OS. Implementers should send a brand derived from the
	//   Sec-CH-UA-Platform header, and version derived from the Sec-CH-UA-
	//   Platform-Version header.
	Platform *BrandVersion `json:"platform,omitempty"`

	// Attribute:
	//   mobile
	// Type:
	//   integer
	// Description:
	//   1 if the agent prefers a “mobile” version of the content, if available, i.e.
	//   optimized for small screens or touch input. 0 if the agent prefers the “desktop”
	//   or “full” content. Implementers should derive this value from the Sec-CH-UA-
	//   Mobile header.
	Mobile *int8 `json:"mobile,omitempty"`

	// Attribute:
	//   architecture
	// Type:
	//   string
	// Description:
	//   Device’s major binary architecture, e.g. “x86” or “arm”. Implementers should
	//   retrieve this value from the Sec-CH-UA-Arch header.
	Architecture string `json:"architecture,omitempty"`

	// Attribute:
	//   bitness
	// Type:
	//   string
	// Description:
	//   Device’s bitness, e.g. “64” for 64-bit architecture. Implementers should
	//   retrieve this value from the Sec-CH-UA-Bitness header.
	Bitness string `json:"bitness,omitempty"`

	// Attribute:
	//   model
	// Type:
	//   string
	// Description:
	//   Device model. Implementers should retrieve this value from the Sec-CH-UA-
	//   Model header.
	Model string `json:"model,omitempty"`

	// Attribute:
	//   source
	// Type:
	//   integer; default 0
	// Description:
	//   The source of data used to create this object, List: User-Agent Source in
	//   AdCOM 1.0.
	Source adcom1.UserAgentSource `json:"source,omitempty"`

	// Attribute:
	//   ext
	// Type:
	//   object
	// Description:
	//   Placeholder for advertising-system specific extensions to this object.
	Ext json.RawMessage `json:"ext,omitempty"`
}
<|endoftext|>


<commit_before>// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package optimizer

import (
	"github.com/pingcap/tidb/ast"
	"github.com/pingcap/tidb/parser"
	"github.com/pingcap/tidb/parser/opcode"
)

// Validate checkes whether the node is valid.
func Validate(node ast.Node, inPrepare bool) error {
	v := validator{inPrepare: inPrepare}
	node.Accept(&v)
	return v.err
}

// validator is an ast.Visitor that validates
// ast Nodes parsed from parser.
type validator struct {
	err           error
	wildCardCount int
	inPrepare     bool
}

func (v *validator) Enter(in ast.Node) (out ast.Node, skipChildren bool) {
	return in, false
}

func (v *validator) Leave(in ast.Node) (out ast.Node, ok bool) {
	switch x := in.(type) {
	case *ast.IsNullExpr:
		v.checkAllOneColumn(x.Expr)
	case *ast.IsTruthExpr:
		v.checkAllOneColumn(x.Expr)
	case *ast.BetweenExpr:
		v.checkAllOneColumn(x.Expr, x.Left, x.Right)
	case *ast.BinaryOperationExpr:
		v.checkBinaryOperation(x)
	case *ast.PatternInExpr:
		v.checkSameColumns(append(x.List, x.Expr)...)
	case *ast.FieldList:
		v.checkFieldList(x)
	case *ast.ByItem:
		v.checkAllOneColumn(x.Expr)
	case *ast.ParamMarkerExpr:
		if !v.inPrepare {
			v.err = parser.ErrSyntax.Gen("syntax error, unexpected '?'")
		}
	}
	return in, v.err == nil
}

// checkAllOneColumn checks that all expressions have one column.
// Expression may have more than one column when it is a rowExpr or
// a Subquery with more than one result fields.
func (v *validator) checkAllOneColumn(exprs ...ast.ExprNode) {
	for _, expr := range exprs {
		switch x := expr.(type) {
		case *ast.RowExpr:
			v.err = ErrOneColumn
		case *ast.SubqueryExpr:
			if len(x.Query.GetResultFields()) != 1 {
				v.err = ErrOneColumn
			}
		}
	}
	return
}

func (v *validator) checkBinaryOperation(x *ast.BinaryOperationExpr) {
	// row constructor only supports comparison operation.
	switch x.Op {
	case opcode.LT, opcode.LE, opcode.GE, opcode.GT, opcode.EQ, opcode.NE, opcode.NullEQ:
		v.checkSameColumns(x.L, x.R)
	default:
		v.checkAllOneColumn(x.L, x.R)
	}
}

func columnCount(ex ast.ExprNode) int {
	switch x := ex.(type) {
	case *ast.RowExpr:
		return len(x.Values)
	case *ast.SubqueryExpr:
		return len(x.Query.GetResultFields())
	default:
		return 1
	}
}

func (v *validator) checkSameColumns(exprs ...ast.ExprNode) {
	if len(exprs) == 0 {
		return
	}
	count := columnCount(exprs[0])
	for i := 1; i < len(exprs); i++ {
		if columnCount(exprs[i]) != count {
			v.err = ErrSameColumns
			return
		}
	}
}

// checkFieldList checks there is only one '*" and each field has only one column.
func (v *validator) checkFieldList(x *ast.FieldList) {
	var hasWildCard bool
	for _, val := range x.Fields {
		if val.WildCard != nil && val.WildCard.Table.L == "" {
			if hasWildCard {
				v.err = ErrMultiWildCard
				return
			}
			hasWildCard = true
		}
		v.checkAllOneColumn(val.Expr)
		if v.err != nil {
			return
		}
	}
}
<commit_msg>optimizer: address comment.<commit_after>// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package optimizer

import (
	"github.com/pingcap/tidb/ast"
	"github.com/pingcap/tidb/parser"
	"github.com/pingcap/tidb/parser/opcode"
)

// Validate checkes whether the node is valid.
func Validate(node ast.Node, inPrepare bool) error {
	v := validator{inPrepare: inPrepare}
	node.Accept(&v)
	return v.err
}

// validator is an ast.Visitor that validates
// ast Nodes parsed from parser.
type validator struct {
	err           error
	wildCardCount int
	inPrepare     bool
}

func (v *validator) Enter(in ast.Node) (out ast.Node, skipChildren bool) {
	return in, false
}

func (v *validator) Leave(in ast.Node) (out ast.Node, ok bool) {
	switch x := in.(type) {
	case *ast.IsNullExpr:
		v.checkAllOneColumn(x.Expr)
	case *ast.IsTruthExpr:
		v.checkAllOneColumn(x.Expr)
	case *ast.BetweenExpr:
		v.checkAllOneColumn(x.Expr, x.Left, x.Right)
	case *ast.BinaryOperationExpr:
		v.checkBinaryOperation(x)
	case *ast.PatternInExpr:
		v.checkSameColumns(append(x.List, x.Expr)...)
	case *ast.FieldList:
		v.checkFieldList(x)
	case *ast.ByItem:
		v.checkAllOneColumn(x.Expr)
	case *ast.ParamMarkerExpr:
		if !v.inPrepare {
			v.err = parser.ErrSyntax.Gen("syntax error, unexpected '?'")
		}
	}
	return in, v.err == nil
}

// checkAllOneColumn checks that all expressions have one column.
// Expression may have more than one column when it is a rowExpr or
// a Subquery with more than one result fields.
func (v *validator) checkAllOneColumn(exprs ...ast.ExprNode) {
	for _, expr := range exprs {
		switch x := expr.(type) {
		case *ast.RowExpr:
			v.err = ErrOneColumn
		case *ast.SubqueryExpr:
			if len(x.Query.GetResultFields()) != 1 {
				v.err = ErrOneColumn
			}
		}
	}
	return
}

func (v *validator) checkBinaryOperation(x *ast.BinaryOperationExpr) {
	// row constructor only supports comparison operation.
	switch x.Op {
	case opcode.LT, opcode.LE, opcode.GE, opcode.GT, opcode.EQ, opcode.NE, opcode.NullEQ:
		v.checkSameColumns(x.L, x.R)
	default:
		v.checkAllOneColumn(x.L, x.R)
	}
}

func columnCount(ex ast.ExprNode) int {
	switch x := ex.(type) {
	case *ast.RowExpr:
		return len(x.Values)
	case *ast.SubqueryExpr:
		return len(x.Query.GetResultFields())
	default:
		return 1
	}
}

func (v *validator) checkSameColumns(exprs ...ast.ExprNode) {
	if len(exprs) == 0 {
		return
	}
	count := columnCount(exprs[0])
	for i := 1; i < len(exprs); i++ {
		if columnCount(exprs[i]) != count {
			v.err = ErrSameColumns
			return
		}
	}
}

// checkFieldList checks if there is only one '*' and each field has only one column.
func (v *validator) checkFieldList(x *ast.FieldList) {
	var hasWildCard bool
	for _, val := range x.Fields {
		if val.WildCard != nil && val.WildCard.Table.L == "" {
			if hasWildCard {
				v.err = ErrMultiWildCard
				return
			}
			hasWildCard = true
		}
		v.checkAllOneColumn(val.Expr)
		if v.err != nil {
			return
		}
	}
}
<|endoftext|>