<commit_before>package stripe

import (
	"encoding/json"

	"github.com/stripe/stripe-go/v72/form"
)

// SubscriptionScheduleEndBehavior describe what happens to a schedule when it ends.
type SubscriptionScheduleEndBehavior string

// List of values that SubscriptionScheduleEndBehavior can take.
const (
	SubscriptionScheduleEndBehaviorCancel  SubscriptionScheduleEndBehavior = "cancel"
	SubscriptionScheduleEndBehaviorRelease SubscriptionScheduleEndBehavior = "release"
)

// SubscriptionScheduleStatus is the list of allowed values for the schedule's status.
type SubscriptionScheduleStatus string

// List of values that SubscriptionScheduleStatus can take.
const (
	SubscriptionScheduleStatusActive    SubscriptionScheduleStatus = "active"
	SubscriptionScheduleStatusCanceled  SubscriptionScheduleStatus = "canceled"
	SubscriptionScheduleStatusCompleted SubscriptionScheduleStatus = "completed"
	SubscriptionScheduleStatusPastDue   SubscriptionScheduleStatus = "not_started"
	SubscriptionScheduleStatusTrialing  SubscriptionScheduleStatus = "released"
)

// SubscriptionSchedulePhaseBillingCycleAnchor is the list of allowed values for the
// schedule's billing_cycle_anchor.
type SubscriptionSchedulePhaseBillingCycleAnchor string

// List of values for SubscriptionSchedulePhaseBillingCycleAnchor
const (
	SubscriptionSchedulePhaseBillingCycleAnchorAutomatic  SubscriptionSchedulePhaseBillingCycleAnchor = "automatic"
	SubscriptionSchedulePhaseBillingCycleAnchorPhaseStart SubscriptionSchedulePhaseBillingCycleAnchor = "phase_start"
)

// SubscriptionScheduleInvoiceSettingsParams is a structure representing the parameters allowed to
// control invoice settings on invoices associated with a subscription schedule.
type SubscriptionScheduleInvoiceSettingsParams struct {
	DaysUntilDue *int64 `form:"days_until_due"`
}

// SubscriptionScheduleDefaultSettingsParams is the set of parameters
// representing the subscription schedule’s default settings.
type SubscriptionScheduleDefaultSettingsParams struct {
	Params                `form:"*"`
	ApplicationFeePercent *float64                                   `form:"application_fee_percent,high_precision"`
	BillingCycleAnchor    *string                                    `form:"billing_cycle_anchor"`
	BillingThresholds     *SubscriptionBillingThresholdsParams       `form:"billing_thresholds"`
	CollectionMethod      *string                                    `form:"collection_method"`
	DefaultPaymentMethod  *string                                    `form:"default_payment_method"`
	InvoiceSettings       *SubscriptionScheduleInvoiceSettingsParams `form:"invoice_settings"`
	TransferData          *SubscriptionTransferDataParams            `form:"transfer_data"`
}

// SubscriptionSchedulePhaseAddInvoiceItemPriceDataRecurringParams is a structure representing the
// parameters to create an inline recurring price for a given invoice item.
type SubscriptionSchedulePhaseAddInvoiceItemPriceDataRecurringParams struct {
	AggregateUsage  *string `form:"aggregate_usage"`
	Interval        *string `form:"interval"`
	IntervalCount   *int64  `form:"interval_count"`
	TrialPeriodDays *int64  `form:"trial_period_days"`
	UsageType       *string `form:"usage_type"`
}

// SubscriptionSchedulePhaseAddInvoiceItemPriceDataParams is a structure representing the parameters
// to create an inline price for a given invoice item.
type SubscriptionSchedulePhaseAddInvoiceItemPriceDataParams struct {
	Currency          *string                                                          `form:"currency"`
	Product           *string                                                          `form:"product"`
	Recurring         *SubscriptionSchedulePhaseAddInvoiceItemPriceDataRecurringParams `form:"recurring"`
	UnitAmount        *int64                                                           `form:"unit_amount"`
	UnitAmountDecimal *float64                                                         `form:"unit_amount_decimal,high_precision"`
}

// SubscriptionSchedulePhaseAddInvoiceItemParams is a structure representing the parameters allowed to control
// the invoice items to add at the start of a phase.
type SubscriptionSchedulePhaseAddInvoiceItemParams struct {
	Price     *string                     `form:"price"`
	PriceData *InvoiceItemPriceDataParams `form:"price_data"`
	Quantity  *int64                      `form:"quantity"`
	TaxRates  []*string                   `form:"tax_rates"`
}

// SubscriptionSchedulePhaseItemParams is a structure representing the parameters allowed to control
// a specic plan on a phase on a subscription schedule.
type SubscriptionSchedulePhaseItemParams struct {
	BillingThresholds *SubscriptionItemBillingThresholdsParams `form:"billing_thresholds"`
	Plan              *string                                  `form:"plan"`
	Price             *string                                  `form:"price"`
	PriceData         *SubscriptionItemPriceDataParams         `form:"price_data"`
	Quantity          *int64                                   `form:"quantity"`
	TaxRates          []*string                                `form:"tax_rates"`
}

// SubscriptionSchedulePhaseParams is a structure representing the parameters allowed to control
// a phase on a subscription schedule.
type SubscriptionSchedulePhaseParams struct {
	AddInvoiceItems       []*SubscriptionSchedulePhaseAddInvoiceItemParams `form:"add_invoice_items"`
	ApplicationFeePercent *float64                                         `form:"application_fee_percent"`
	BillingCycleAnchor    *string                                          `form:"billing_cycle_anchor"`
	BillingThresholds     *SubscriptionBillingThresholdsParams             `form:"billing_thresholds"`
	CollectionMethod      *string                                          `form:"collection_method"`
	Coupon                *string                                          `form:"coupon"`
	DefaultPaymentMethod  *string                                          `form:"default_payment_method"`
	DefaultTaxRates       []*string                                        `form:"default_tax_rates"`
	EndDate               *int64                                           `form:"end_date"`
	InvoiceSettings       *SubscriptionScheduleInvoiceSettingsParams       `form:"invoice_settings"`
	Iterations            *int64                                           `form:"iterations"`
	Items                 []*SubscriptionSchedulePhaseItemParams           `form:"items"`
	ProrationBehavior     *string                                          `form:"proration_behavior"`
	StartDate             *int64                                           `form:"start_date"`
	TransferData          *SubscriptionTransferDataParams                  `form:"transfer_data"`
	Trial                 *bool                                            `form:"trial"`
	TrialEnd              *int64                                           `form:"trial_end"`
}

// SubscriptionScheduleParams is the set of parameters that can be used when creating or updating a
// subscription schedule.
type SubscriptionScheduleParams struct {
	Params            `form:"*"`
	Customer          *string                                    `form:"customer"`
	DefaultSettings   *SubscriptionScheduleDefaultSettingsParams `form:"default_settings"`
	EndBehavior       *string                                    `form:"end_behavior"`
	FromSubscription  *string                                    `form:"from_subscription"`
	ProrationBehavior *string                                    `form:"proration_behavior"`
	Phases            []*SubscriptionSchedulePhaseParams         `form:"phases"`
	StartDate         *int64                                     `form:"start_date"`
	StartDateNow      *bool                                      `form:"-"` // See custom AppendTo
}

// AppendTo implements custom encoding logic for SubscriptionScheduleParams so that the special
// "now" value for start_date can be implemented (they're otherwise timestamps rather than strings).
func (p *SubscriptionScheduleParams) AppendTo(body *form.Values, keyParts []string) {
	if BoolValue(p.StartDateNow) {
		body.Add(form.FormatKey(append(keyParts, "start_date")), "now")
	}
}

// SubscriptionScheduleCancelParams is the set of parameters that can be used when canceling a
// subscription schedule.
type SubscriptionScheduleCancelParams struct {
	Params     `form:"*"`
	InvoiceNow *bool `form:"invoice_now"`
	Prorate    *bool `form:"prorate"`
}

// SubscriptionScheduleReleaseParams is the set of parameters that can be used when releasing a
// subscription schedule.
type SubscriptionScheduleReleaseParams struct {
	Params             `form:"*"`
	PreserveCancelDate *bool `form:"preserve_cancel_date"`
}

// SubscriptionScheduleListParams is the set of parameters that can be used when listing
// subscription schedules.
type SubscriptionScheduleListParams struct {
	ListParams       `form:"*"`
	CanceledAt       int64             `form:"canceled_at"`
	CanceledAtRange  *RangeQueryParams `form:"canceled_at"`
	CompletedAt      int64             `form:"completed_at"`
	CompletedAtRange *RangeQueryParams `form:"completed_at"`
	Created          int64             `form:"created"`
	CreatedRange     *RangeQueryParams `form:"created"`
	Customer         string            `form:"customer"`
	ReleasedAt       int64             `form:"released_at"`
	ReleasedAtRange  *RangeQueryParams `form:"released_at"`
	Scheduled        *bool             `form:"scheduled"`
}

// SubscriptionScheduleCurrentPhase is a structure the current phase's period.
type SubscriptionScheduleCurrentPhase struct {
	EndDate   int64 `json:"end_date"`
	StartDate int64 `json:"start_date"`
}

// SubscriptionScheduleInvoiceSettings is a structure representing the settings applied to invoices
// associated with a subscription schedule.
type SubscriptionScheduleInvoiceSettings struct {
	DaysUntilDue int64 `json:"days_until_due"`
}

// SubscriptionScheduleDefaultSettings is a structure representing the
// subscription schedule’s default settings.
type SubscriptionScheduleDefaultSettings struct {
	ApplicationFeePercent float64                                     `json:"application_fee_percent,string"`
	BillingCycleAnchor    SubscriptionSchedulePhaseBillingCycleAnchor `json:"billing_cycle_anchor"`
	BillingThresholds     *SubscriptionBillingThresholds              `json:"billing_thresholds"`
	CollectionMethod      SubscriptionCollectionMethod                `json:"collection_method"`
	DefaultPaymentMethod  *PaymentMethod                              `json:"default_payment_method"`
	InvoiceSettings       *SubscriptionScheduleInvoiceSettings        `json:"invoice_settings"`
	TransferData          *SubscriptionTransferData                   `json:"transfer_data"`
}

// SubscriptionSchedulePhaseAddInvoiceItem represents the invoice items to add when the phase starts.
type SubscriptionSchedulePhaseAddInvoiceItem struct {
	Price    *Price     `json:"price"`
	Quantity int64      `json:"quantity"`
	TaxRates []*TaxRate `json:"tax_rates"`
}

// SubscriptionSchedulePhaseItem represents plan details for a given phase
type SubscriptionSchedulePhaseItem struct {
	BillingThresholds *SubscriptionItemBillingThresholds `json:"billing_thresholds"`
	Plan              *Plan                              `json:"plan"`
	Price             *Price                             `json:"price"`
	Quantity          int64                              `json:"quantity"`
	TaxRates          []*TaxRate                         `json:"tax_rates"`
}

// SubscriptionSchedulePhase is a structure a phase of a subscription schedule.
type SubscriptionSchedulePhase struct {
	AddInvoiceItems       []*SubscriptionSchedulePhaseAddInvoiceItem  `json:"add_invoice_items"`
	ApplicationFeePercent float64                                     `json:"application_fee_percent"`
	BillingCycleAnchor    SubscriptionSchedulePhaseBillingCycleAnchor `json:"billing_cycle_anchor"`
	BillingThresholds     *SubscriptionBillingThresholds              `json:"billing_thresholds"`
	CollectionMethod      SubscriptionCollectionMethod                `json:"collection_method"`
	Coupon                *Coupon                                     `json:"coupon"`
	DefaultPaymentMethod  *PaymentMethod                              `json:"default_payment_method"`
	DefaultTaxRates       []*TaxRate                                  `json:"default_tax_rates"`
	EndDate               int64                                       `json:"end_date"`
	InvoiceSettings       *SubscriptionScheduleInvoiceSettings        `json:"invoice_settings"`
	Items                 []*SubscriptionSchedulePhaseItem            `json:"items"`
	StartDate             int64                                       `json:"start_date"`
	TransferData          *SubscriptionTransferData                   `json:"transfer_data"`
	TrialEnd              int64                                       `json:"trial_end"`
}

// SubscriptionSchedule is the resource representing a Stripe subscription schedule.
type SubscriptionSchedule struct {
	APIResource
	CanceledAt           int64                                `json:"canceled_at"`
	CompletedAt          int64                                `json:"completed_at"`
	Created              int64                                `json:"created"`
	CurrentPhase         *SubscriptionScheduleCurrentPhase    `json:"current_phase"`
	Customer             *Customer                            `json:"customer"`
	DefaultSettings      *SubscriptionScheduleDefaultSettings `json:"default_settings"`
	EndBehavior          SubscriptionScheduleEndBehavior      `json:"end_behavior"`
	ID                   string                               `json:"id"`
	Livemode             bool                                 `json:"livemode"`
	Metadata             map[string]string                    `json:"metadata"`
	Object               string                               `json:"object"`
	Phases               []*SubscriptionSchedulePhase         `json:"phases"`
	ReleasedSubscription *Subscription                        `json:"released_subscription"`
	Status               SubscriptionScheduleStatus           `json:"status"`
	Subscription         *Subscription                        `json:"subscription"`
}

// SubscriptionScheduleList is a list object for subscription schedules.
type SubscriptionScheduleList struct {
	APIResource
	ListMeta
	Data []*SubscriptionSchedule `json:"data"`
}

// UnmarshalJSON handles deserialization of a SubscriptionSchedule.
// This custom unmarshaling is needed because the resulting
// property may be an id or the full struct if it was expanded.
func (s *SubscriptionSchedule) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		s.ID = id
		return nil
	}

	type schedule SubscriptionSchedule
	var v schedule
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	*s = SubscriptionSchedule(v)
	return nil
}
<commit_msg>Fix names of subscription schedule status constants (#1274)<commit_after>package stripe

import (
	"encoding/json"

	"github.com/stripe/stripe-go/v72/form"
)

// SubscriptionScheduleEndBehavior describe what happens to a schedule when it ends.
type SubscriptionScheduleEndBehavior string

// List of values that SubscriptionScheduleEndBehavior can take.
const (
	SubscriptionScheduleEndBehaviorCancel  SubscriptionScheduleEndBehavior = "cancel"
	SubscriptionScheduleEndBehaviorRelease SubscriptionScheduleEndBehavior = "release"
)

// SubscriptionScheduleStatus is the list of allowed values for the schedule's status.
type SubscriptionScheduleStatus string

// List of values that SubscriptionScheduleStatus can take.
const (
	SubscriptionScheduleStatusActive     SubscriptionScheduleStatus = "active"
	SubscriptionScheduleStatusCanceled   SubscriptionScheduleStatus = "canceled"
	SubscriptionScheduleStatusCompleted  SubscriptionScheduleStatus = "completed"
	SubscriptionScheduleStatusNotStarted SubscriptionScheduleStatus = "not_started"
	SubscriptionScheduleStatusReleased   SubscriptionScheduleStatus = "released"
)

// SubscriptionSchedulePhaseBillingCycleAnchor is the list of allowed values for the
// schedule's billing_cycle_anchor.
type SubscriptionSchedulePhaseBillingCycleAnchor string

// List of values for SubscriptionSchedulePhaseBillingCycleAnchor
const (
	SubscriptionSchedulePhaseBillingCycleAnchorAutomatic  SubscriptionSchedulePhaseBillingCycleAnchor = "automatic"
	SubscriptionSchedulePhaseBillingCycleAnchorPhaseStart SubscriptionSchedulePhaseBillingCycleAnchor = "phase_start"
)

// SubscriptionScheduleInvoiceSettingsParams is a structure representing the parameters allowed to
// control invoice settings on invoices associated with a subscription schedule.
type SubscriptionScheduleInvoiceSettingsParams struct {
	DaysUntilDue *int64 `form:"days_until_due"`
}

// SubscriptionScheduleDefaultSettingsParams is the set of parameters
// representing the subscription schedule’s default settings.
type SubscriptionScheduleDefaultSettingsParams struct {
	Params                `form:"*"`
	ApplicationFeePercent *float64                                   `form:"application_fee_percent,high_precision"`
	BillingCycleAnchor    *string                                    `form:"billing_cycle_anchor"`
	BillingThresholds     *SubscriptionBillingThresholdsParams       `form:"billing_thresholds"`
	CollectionMethod      *string                                    `form:"collection_method"`
	DefaultPaymentMethod  *string                                    `form:"default_payment_method"`
	InvoiceSettings       *SubscriptionScheduleInvoiceSettingsParams `form:"invoice_settings"`
	TransferData          *SubscriptionTransferDataParams            `form:"transfer_data"`
}

// SubscriptionSchedulePhaseAddInvoiceItemPriceDataRecurringParams is a structure representing the
// parameters to create an inline recurring price for a given invoice item.
type SubscriptionSchedulePhaseAddInvoiceItemPriceDataRecurringParams struct {
	AggregateUsage  *string `form:"aggregate_usage"`
	Interval        *string `form:"interval"`
	IntervalCount   *int64  `form:"interval_count"`
	TrialPeriodDays *int64  `form:"trial_period_days"`
	UsageType       *string `form:"usage_type"`
}

// SubscriptionSchedulePhaseAddInvoiceItemPriceDataParams is a structure representing the parameters
// to create an inline price for a given invoice item.
type SubscriptionSchedulePhaseAddInvoiceItemPriceDataParams struct {
	Currency          *string                                                          `form:"currency"`
	Product           *string                                                          `form:"product"`
	Recurring         *SubscriptionSchedulePhaseAddInvoiceItemPriceDataRecurringParams `form:"recurring"`
	UnitAmount        *int64                                                           `form:"unit_amount"`
	UnitAmountDecimal *float64                                                         `form:"unit_amount_decimal,high_precision"`
}

// SubscriptionSchedulePhaseAddInvoiceItemParams is a structure representing the parameters allowed to control
// the invoice items to add at the start of a phase.
type SubscriptionSchedulePhaseAddInvoiceItemParams struct {
	Price     *string                     `form:"price"`
	PriceData *InvoiceItemPriceDataParams `form:"price_data"`
	Quantity  *int64                      `form:"quantity"`
	TaxRates  []*string                   `form:"tax_rates"`
}

// SubscriptionSchedulePhaseItemParams is a structure representing the parameters allowed to control
// a specic plan on a phase on a subscription schedule.
type SubscriptionSchedulePhaseItemParams struct {
	BillingThresholds *SubscriptionItemBillingThresholdsParams `form:"billing_thresholds"`
	Plan              *string                                  `form:"plan"`
	Price             *string                                  `form:"price"`
	PriceData         *SubscriptionItemPriceDataParams         `form:"price_data"`
	Quantity          *int64                                   `form:"quantity"`
	TaxRates          []*string                                `form:"tax_rates"`
}

// SubscriptionSchedulePhaseParams is a structure representing the parameters allowed to control
// a phase on a subscription schedule.
type SubscriptionSchedulePhaseParams struct {
	AddInvoiceItems       []*SubscriptionSchedulePhaseAddInvoiceItemParams `form:"add_invoice_items"`
	ApplicationFeePercent *float64                                         `form:"application_fee_percent"`
	BillingCycleAnchor    *string                                          `form:"billing_cycle_anchor"`
	BillingThresholds     *SubscriptionBillingThresholdsParams             `form:"billing_thresholds"`
	CollectionMethod      *string                                          `form:"collection_method"`
	Coupon                *string                                          `form:"coupon"`
	DefaultPaymentMethod  *string                                          `form:"default_payment_method"`
	DefaultTaxRates       []*string                                        `form:"default_tax_rates"`
	EndDate               *int64                                           `form:"end_date"`
	InvoiceSettings       *SubscriptionScheduleInvoiceSettingsParams       `form:"invoice_settings"`
	Iterations            *int64                                           `form:"iterations"`
	Items                 []*SubscriptionSchedulePhaseItemParams           `form:"items"`
	ProrationBehavior     *string                                          `form:"proration_behavior"`
	StartDate             *int64                                           `form:"start_date"`
	TransferData          *SubscriptionTransferDataParams                  `form:"transfer_data"`
	Trial                 *bool                                            `form:"trial"`
	TrialEnd              *int64                                           `form:"trial_end"`
}

// SubscriptionScheduleParams is the set of parameters that can be used when creating or updating a
// subscription schedule.
type SubscriptionScheduleParams struct {
	Params            `form:"*"`
	Customer          *string                                    `form:"customer"`
	DefaultSettings   *SubscriptionScheduleDefaultSettingsParams `form:"default_settings"`
	EndBehavior       *string                                    `form:"end_behavior"`
	FromSubscription  *string                                    `form:"from_subscription"`
	ProrationBehavior *string                                    `form:"proration_behavior"`
	Phases            []*SubscriptionSchedulePhaseParams         `form:"phases"`
	StartDate         *int64                                     `form:"start_date"`
	StartDateNow      *bool                                      `form:"-"` // See custom AppendTo
}

// AppendTo implements custom encoding logic for SubscriptionScheduleParams so that the special
// "now" value for start_date can be implemented (they're otherwise timestamps rather than strings).
func (p *SubscriptionScheduleParams) AppendTo(body *form.Values, keyParts []string) {
	if BoolValue(p.StartDateNow) {
		body.Add(form.FormatKey(append(keyParts, "start_date")), "now")
	}
}

// SubscriptionScheduleCancelParams is the set of parameters that can be used when canceling a
// subscription schedule.
type SubscriptionScheduleCancelParams struct {
	Params     `form:"*"`
	InvoiceNow *bool `form:"invoice_now"`
	Prorate    *bool `form:"prorate"`
}

// SubscriptionScheduleReleaseParams is the set of parameters that can be used when releasing a
// subscription schedule.
type SubscriptionScheduleReleaseParams struct {
	Params             `form:"*"`
	PreserveCancelDate *bool `form:"preserve_cancel_date"`
}

// SubscriptionScheduleListParams is the set of parameters that can be used when listing
// subscription schedules.
type SubscriptionScheduleListParams struct {
	ListParams       `form:"*"`
	CanceledAt       int64             `form:"canceled_at"`
	CanceledAtRange  *RangeQueryParams `form:"canceled_at"`
	CompletedAt      int64             `form:"completed_at"`
	CompletedAtRange *RangeQueryParams `form:"completed_at"`
	Created          int64             `form:"created"`
	CreatedRange     *RangeQueryParams `form:"created"`
	Customer         string            `form:"customer"`
	ReleasedAt       int64             `form:"released_at"`
	ReleasedAtRange  *RangeQueryParams `form:"released_at"`
	Scheduled        *bool             `form:"scheduled"`
}

// SubscriptionScheduleCurrentPhase is a structure the current phase's period.
type SubscriptionScheduleCurrentPhase struct {
	EndDate   int64 `json:"end_date"`
	StartDate int64 `json:"start_date"`
}

// SubscriptionScheduleInvoiceSettings is a structure representing the settings applied to invoices
// associated with a subscription schedule.
type SubscriptionScheduleInvoiceSettings struct {
	DaysUntilDue int64 `json:"days_until_due"`
}

// SubscriptionScheduleDefaultSettings is a structure representing the
// subscription schedule’s default settings.
type SubscriptionScheduleDefaultSettings struct {
	ApplicationFeePercent float64                                     `json:"application_fee_percent,string"`
	BillingCycleAnchor    SubscriptionSchedulePhaseBillingCycleAnchor `json:"billing_cycle_anchor"`
	BillingThresholds     *SubscriptionBillingThresholds              `json:"billing_thresholds"`
	CollectionMethod      SubscriptionCollectionMethod                `json:"collection_method"`
	DefaultPaymentMethod  *PaymentMethod                              `json:"default_payment_method"`
	InvoiceSettings       *SubscriptionScheduleInvoiceSettings        `json:"invoice_settings"`
	TransferData          *SubscriptionTransferData                   `json:"transfer_data"`
}

// SubscriptionSchedulePhaseAddInvoiceItem represents the invoice items to add when the phase starts.
type SubscriptionSchedulePhaseAddInvoiceItem struct {
	Price    *Price     `json:"price"`
	Quantity int64      `json:"quantity"`
	TaxRates []*TaxRate `json:"tax_rates"`
}

// SubscriptionSchedulePhaseItem represents plan details for a given phase
type SubscriptionSchedulePhaseItem struct {
	BillingThresholds *SubscriptionItemBillingThresholds `json:"billing_thresholds"`
	Plan              *Plan                              `json:"plan"`
	Price             *Price                             `json:"price"`
	Quantity          int64                              `json:"quantity"`
	TaxRates          []*TaxRate                         `json:"tax_rates"`
}

// SubscriptionSchedulePhase is a structure a phase of a subscription schedule.
type SubscriptionSchedulePhase struct {
	AddInvoiceItems       []*SubscriptionSchedulePhaseAddInvoiceItem  `json:"add_invoice_items"`
	ApplicationFeePercent float64                                     `json:"application_fee_percent"`
	BillingCycleAnchor    SubscriptionSchedulePhaseBillingCycleAnchor `json:"billing_cycle_anchor"`
	BillingThresholds     *SubscriptionBillingThresholds              `json:"billing_thresholds"`
	CollectionMethod      SubscriptionCollectionMethod                `json:"collection_method"`
	Coupon                *Coupon                                     `json:"coupon"`
	DefaultPaymentMethod  *PaymentMethod                              `json:"default_payment_method"`
	DefaultTaxRates       []*TaxRate                                  `json:"default_tax_rates"`
	EndDate               int64                                       `json:"end_date"`
	InvoiceSettings       *SubscriptionScheduleInvoiceSettings        `json:"invoice_settings"`
	Items                 []*SubscriptionSchedulePhaseItem            `json:"items"`
	StartDate             int64                                       `json:"start_date"`
	TransferData          *SubscriptionTransferData                   `json:"transfer_data"`
	TrialEnd              int64                                       `json:"trial_end"`
}

// SubscriptionSchedule is the resource representing a Stripe subscription schedule.
type SubscriptionSchedule struct {
	APIResource
	CanceledAt           int64                                `json:"canceled_at"`
	CompletedAt          int64                                `json:"completed_at"`
	Created              int64                                `json:"created"`
	CurrentPhase         *SubscriptionScheduleCurrentPhase    `json:"current_phase"`
	Customer             *Customer                            `json:"customer"`
	DefaultSettings      *SubscriptionScheduleDefaultSettings `json:"default_settings"`
	EndBehavior          SubscriptionScheduleEndBehavior      `json:"end_behavior"`
	ID                   string                               `json:"id"`
	Livemode             bool                                 `json:"livemode"`
	Metadata             map[string]string                    `json:"metadata"`
	Object               string                               `json:"object"`
	Phases               []*SubscriptionSchedulePhase         `json:"phases"`
	ReleasedSubscription *Subscription                        `json:"released_subscription"`
	Status               SubscriptionScheduleStatus           `json:"status"`
	Subscription         *Subscription                        `json:"subscription"`
}

// SubscriptionScheduleList is a list object for subscription schedules.
type SubscriptionScheduleList struct {
	APIResource
	ListMeta
	Data []*SubscriptionSchedule `json:"data"`
}

// UnmarshalJSON handles deserialization of a SubscriptionSchedule.
// This custom unmarshaling is needed because the resulting
// property may be an id or the full struct if it was expanded.
func (s *SubscriptionSchedule) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		s.ID = id
		return nil
	}

	type schedule SubscriptionSchedule
	var v schedule
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	*s = SubscriptionSchedule(v)
	return nil
}
<|endoftext|>


<commit_before>package searchends

import (
	"fmt"
	"github.com/blevesearch/bleve"
	"github.com/blevesearch/bleve/mapping"
	wyc "github.com/voidshard/wysteria/common"
	"os"
	"strings"
)

// Wrapper struct around bleve search indexes
type bleveSearchbase struct {
	collections bleve.Index
	items       bleve.Index
	versions    bleve.Index
	resources   bleve.Index
	links       bleve.Index
}

// Return a new bleve index given a path on disk, either by creating if it doesn't exist or opening it if it does.
func newBleveIndex(name string, documentMapping *mapping.IndexMappingImpl) (bleve.Index, error) {
	_, err := os.Stat(name)
	if err == nil {
		// Open existing index
		return bleve.Open(name)
	}
	// Create new index
	return bleve.New(name, documentMapping)
}

// 'connect' to bleve by opening/creating all of our indexes
func bleveConnect(settings *Settings) (Searchbase, error) {
	sb := &bleveSearchbase{}
	imapping := bleve.NewIndexMapping()

	idx, err := newBleveIndex(settings.Database+tableCollection, imapping)
	if err != nil {
		return nil, err
	}
	sb.collections = idx

	idx, err = newBleveIndex(settings.Database+tableItem, imapping)
	if err != nil {
		return nil, err
	}
	sb.items = idx

	idx, err = newBleveIndex(settings.Database+tableVersion, imapping)
	if err != nil {
		return nil, err
	}
	sb.versions = idx

	idx, err = newBleveIndex(settings.Database+tableResource, imapping)
	if err != nil {
		return nil, err
	}
	sb.resources = idx

	idx, err = newBleveIndex(settings.Database+tableLink, imapping)
	if err != nil {
		return nil, err
	}
	sb.links = idx

	return sb, nil
}

// close bleve connections (or more accurately, close open filehandlers I guess)
func (b *bleveSearchbase) Close() error {
	b.collections.Close()
	b.items.Close()
	b.versions.Close()
	b.resources.Close()
	b.links.Close()
	return nil
}

// Insert collection using the given id
func (b *bleveSearchbase) InsertCollection(id string, doc *wyc.Collection) error {
	in := copyCollection(doc)    // create copy so we don't modify the original
	in.Name = b64encode(in.Name) // encode name so we aren't tripped up by spaces / bleve control chars
	return b.collections.Index(id, in)
}

// Insert collection using the given id
func (b *bleveSearchbase) InsertItem(id string, doc *wyc.Item) error {
	in := copyItem(doc) // create copy so we don't modify the original

	// mutate values of our copy so we don't have to worry about weird chars
	in.ItemType = b64encode(in.ItemType)
	in.Variant = b64encode(in.Variant)
	for k, v := range doc.Facets {
		in.Facets[b64encode(k)] = b64encode(v)
	}

	return b.items.Index(id, in)
}

// Insert version using the given id
func (b *bleveSearchbase) InsertVersion(id string, doc *wyc.Version) error {
	in := copyVersion(doc) // create copy so we don't modify the original

	// mutate values of our copy so we don't have to worry about weird chars
	for k, v := range doc.Facets {
		in.Facets[b64encode(k)] = b64encode(v)
	}
	return b.versions.Index(id, in)
}

// Insert resource using the given id
func (b *bleveSearchbase) InsertResource(id string, doc *wyc.Resource) error {
	in := copyResource(doc)
	in.Name = b64encode(in.Name)
	in.ResourceType = b64encode(in.ResourceType)
	in.Location = b64encode(in.Location)
	return b.resources.Index(id, in)
}

// Insert link using the given id
func (b *bleveSearchbase) InsertLink(id string, doc *wyc.Link) error {
	in := copyLink(doc)
	in.Name = b64encode(in.Name)
	return b.links.Index(id, in)
}

// Update item with given id
func (b *bleveSearchbase) UpdateItem(id string, in *wyc.Item) error {
	// For bleve, updating and inserting are the same thing
	return b.InsertItem(id, in)
}

// Update item with given id
func (b *bleveSearchbase) UpdateVersion(id string, in *wyc.Version) error {
	// For bleve, updating and inserting are the same thing
	return b.InsertVersion(id, in)
}

// Iterate over given IDs and delete from given index
func genericDelete(index bleve.Index, ids ...string) error {
	for _, id := range ids {
		err := index.Delete(id)
		if err != nil {
			return err
		}
	}
	return nil
}

// Delete collections by ID(s)
func (b *bleveSearchbase) DeleteCollection(ids ...string) error {
	return genericDelete(b.collections, ids...)
}

// Delete items by ID(s)
func (b *bleveSearchbase) DeleteItem(ids ...string) error {
	return genericDelete(b.items, ids...)
}

// Delete versions by ID(s)
func (b *bleveSearchbase) DeleteVersion(ids ...string) error {
	return genericDelete(b.versions, ids...)
}

// Delete resources by ID(s)
func (b *bleveSearchbase) DeleteResource(ids ...string) error {
	return genericDelete(b.resources, ids...)
}

// Delete links by ID(s)
func (b *bleveSearchbase) DeleteLink(ids ...string) error {
	return genericDelete(b.links, ids...)
}

// Transform a QueryDesc into a bleve compatible search string for a collection
func toCollectionQueryString(desc *wyc.QueryDesc) string {
	sq := []string{}
	if desc.Id != "" {
		sq = append(sq, fmt.Sprintf("+Id:%s", desc.Id))
	}
	if desc.Name != "" {
		sq = append(sq, fmt.Sprintf("+Name:%s", b64encode(desc.Name)))
	}
	return strings.Join(sq, " ")
}

// Transform a QueryDesc into a bleve compatible search string for a item
func toItemQueryString(desc *wyc.QueryDesc) string {
	sq := []string{}
	if desc.Id != "" {
		sq = append(sq, fmt.Sprintf("+Id:%s", desc.Id))
	}
	if desc.ItemType != "" {
		sq = append(sq, fmt.Sprintf("+ItemType:%s", b64encode(desc.ItemType)))
	}
	if desc.Variant != "" {
		sq = append(sq, fmt.Sprintf("+Variant:%s", b64encode(desc.Variant)))
	}
	if desc.Parent != "" {
		sq = append(sq, fmt.Sprintf("+Parent:%s", desc.Parent))
	}
	for k, v := range desc.Facets {
		sq = append(sq, fmt.Sprintf("+Facets.%s:%s", b64encode(k), b64encode(v)))
	}
	return strings.Join(sq, " ")
}

// Transform a QueryDesc into a bleve compatible search string for a version
func toVersionQueryString(desc *wyc.QueryDesc) string {
	sq := []string{}
	if desc.Id != "" {
		sq = append(sq, fmt.Sprintf("+Id:%s", desc.Id))
	}
	if desc.Parent != "" {
		sq = append(sq, fmt.Sprintf("+Parent:%s", desc.Parent))
	}
	for k, v := range desc.Facets {
		sq = append(sq, fmt.Sprintf("+Facets.%s:%s", b64encode(k), b64encode(v)))
	}
	if desc.VersionNumber > 0 {
		sq = append(sq, fmt.Sprintf("+Number:%d", desc.VersionNumber))
	}
	return strings.Join(sq, " ")
}

// Transform a QueryDesc into a bleve compatible search string for a resource
func toResourceQueryString(desc *wyc.QueryDesc) string {
	sq := []string{}
	if desc.Id != "" {
		sq = append(sq, fmt.Sprintf("+Id:%s", desc.Id))
	}
	if desc.Parent != "" {
		sq = append(sq, fmt.Sprintf("+Parent:%s", desc.Parent))
	}
	if desc.ResourceType != "" {
		sq = append(sq, fmt.Sprintf("+ResourceType:%s", b64encode(desc.ResourceType)))
	}
	if desc.Name != "" {
		sq = append(sq, fmt.Sprintf("+Name:%s", b64encode(desc.Name)))
	}
	if desc.Location != "" {
		sq = append(sq, fmt.Sprintf("+Location:%s", b64encode(desc.Location)))
	}
	return strings.Join(sq, " ")
}

// Transform a QueryDesc into a bleve compatible search string for a link
func toLinkQueryString(desc *wyc.QueryDesc) string {
	sq := []string{}
	if desc.Id != "" {
		sq = append(sq, fmt.Sprintf("+Id:%s", desc.Id))
	}
	if desc.Name != "" {
		sq = append(sq, fmt.Sprintf("+Name:%s", b64encode(desc.Name)))
	}
	if desc.LinkSrc != "" {
		sq = append(sq, fmt.Sprintf("+Src:%s", desc.LinkSrc))
	}
	if desc.LinkDst != "" {
		sq = append(sq, fmt.Sprintf("+Dst:%s", desc.LinkDst))
	}
	return strings.Join(sq, " ")
}

// A generic query function that handles grabbing IDs from a bleve index given
//  - limit: max number of entries to return
//  - from: return only entries after this number
//  - index: a bleve index to search
//  - convert: a function to take a QueryDesc and turn it into a bleve query string
//  - queries: the search QueryDesc objects
func genericQuery(limit, from int, index bleve.Index, convert func(desc *wyc.QueryDesc) string, queries ...*wyc.QueryDesc) ([]string, error) {
	// ToDo: There is probably a smarter way to do this as a single query with limit / page

	if len(queries) < 1 {
		return nil, nil
	}

	var result *bleve.SearchResult
	for _, query := range queries {
		search_query := bleve.NewQueryStringQuery(convert(query))
		search := bleve.NewSearchRequest(search_query)
		res, err := index.Search(search)

		if err != nil {
			return nil, err
		}
		if result == nil {
			result = res
		} else {
			if res.Hits.Len() > 0 {
				result.Merge(res)
			}
		}
	}

	ids := []string{}
	for _, doc := range result.Hits {
		ids = append(ids, doc.ID)
	}

	if limit < 1 {
		// there is no limit, return them all
		return ids, nil
	}

	if limit >= len(ids) {
		// we found less results than our limit, return them all
		return ids, nil
	}

	if limit+from >= len(ids) {
		// we've been asked for the last segment of the values
		return ids[from:], nil
	}

	// We've been asked for a page of results somewhere in the middle
	return ids[from : limit+from], nil
}

// Search for collections matching the given query descriptions
func (b *bleveSearchbase) QueryCollection(limit, from int, query ...*wyc.QueryDesc) ([]string, error) {
	return genericQuery(limit, from, b.collections, toCollectionQueryString, query...)
}

// Search for items matching the given query descriptions
func (b *bleveSearchbase) QueryItem(limit, from int, query ...*wyc.QueryDesc) ([]string, error) {
	return genericQuery(limit, from, b.items, toItemQueryString, query...)
}

// Search for versions matching the given query descriptions
func (b *bleveSearchbase) QueryVersion(limit, from int, query ...*wyc.QueryDesc) ([]string, error) {
	return genericQuery(limit, from, b.versions, toVersionQueryString, query...)
}

// Search for resources matching the given query descriptions
func (b *bleveSearchbase) QueryResource(limit, from int, query ...*wyc.QueryDesc) ([]string, error) {
	return genericQuery(limit, from, b.resources, toResourceQueryString, query...)
}

// Search for links matching the given query descriptions
func (b *bleveSearchbase) QueryLink(limit, from int, query ...*wyc.QueryDesc) ([]string, error) {
	return genericQuery(limit, from, b.links, toLinkQueryString, query...)
}
<commit_msg>Use bleves NewSearchRequestOptions to set limit & offset<commit_after>package searchends

import (
	"fmt"
	"github.com/blevesearch/bleve"
	"github.com/blevesearch/bleve/mapping"
	wyc "github.com/voidshard/wysteria/common"
	"os"
	"strings"
)

// Wrapper struct around bleve search indexes
type bleveSearchbase struct {
	collections bleve.Index
	items       bleve.Index
	versions    bleve.Index
	resources   bleve.Index
	links       bleve.Index
}

// Return a new bleve index given a path on disk, either by creating if it doesn't exist or opening it if it does.
func newBleveIndex(name string, documentMapping *mapping.IndexMappingImpl) (bleve.Index, error) {
	_, err := os.Stat(name)
	if err == nil {
		// Open existing index
		return bleve.Open(name)
	}
	// Create new index
	return bleve.New(name, documentMapping)
}

// 'connect' to bleve by opening/creating all of our indexes
func bleveConnect(settings *Settings) (Searchbase, error) {
	sb := &bleveSearchbase{}
	imapping := bleve.NewIndexMapping()

	idx, err := newBleveIndex(settings.Database+tableCollection, imapping)
	if err != nil {
		return nil, err
	}
	sb.collections = idx

	idx, err = newBleveIndex(settings.Database+tableItem, imapping)
	if err != nil {
		return nil, err
	}
	sb.items = idx

	idx, err = newBleveIndex(settings.Database+tableVersion, imapping)
	if err != nil {
		return nil, err
	}
	sb.versions = idx

	idx, err = newBleveIndex(settings.Database+tableResource, imapping)
	if err != nil {
		return nil, err
	}
	sb.resources = idx

	idx, err = newBleveIndex(settings.Database+tableLink, imapping)
	if err != nil {
		return nil, err
	}
	sb.links = idx

	return sb, nil
}

// close bleve connections (or more accurately, close open filehandlers I guess)
func (b *bleveSearchbase) Close() error {
	b.collections.Close()
	b.items.Close()
	b.versions.Close()
	b.resources.Close()
	b.links.Close()
	return nil
}

// Insert collection using the given id
func (b *bleveSearchbase) InsertCollection(id string, doc *wyc.Collection) error {
	in := copyCollection(doc)    // create copy so we don't modify the original
	in.Name = b64encode(in.Name) // encode name so we aren't tripped up by spaces / bleve control chars
	return b.collections.Index(id, in)
}

// Insert collection using the given id
func (b *bleveSearchbase) InsertItem(id string, doc *wyc.Item) error {
	in := copyItem(doc) // create copy so we don't modify the original

	// mutate values of our copy so we don't have to worry about weird chars
	in.ItemType = b64encode(in.ItemType)
	in.Variant = b64encode(in.Variant)
	for k, v := range doc.Facets {
		in.Facets[b64encode(k)] = b64encode(v)
	}

	return b.items.Index(id, in)
}

// Insert version using the given id
func (b *bleveSearchbase) InsertVersion(id string, doc *wyc.Version) error {
	in := copyVersion(doc) // create copy so we don't modify the original

	// mutate values of our copy so we don't have to worry about weird chars
	for k, v := range doc.Facets {
		in.Facets[b64encode(k)] = b64encode(v)
	}
	return b.versions.Index(id, in)
}

// Insert resource using the given id
func (b *bleveSearchbase) InsertResource(id string, doc *wyc.Resource) error {
	in := copyResource(doc)
	in.Name = b64encode(in.Name)
	in.ResourceType = b64encode(in.ResourceType)
	in.Location = b64encode(in.Location)
	return b.resources.Index(id, in)
}

// Insert link using the given id
func (b *bleveSearchbase) InsertLink(id string, doc *wyc.Link) error {
	in := copyLink(doc)
	in.Name = b64encode(in.Name)
	return b.links.Index(id, in)
}

// Update item with given id
func (b *bleveSearchbase) UpdateItem(id string, in *wyc.Item) error {
	// For bleve, updating and inserting are the same thing
	return b.InsertItem(id, in)
}

// Update item with given id
func (b *bleveSearchbase) UpdateVersion(id string, in *wyc.Version) error {
	// For bleve, updating and inserting are the same thing
	return b.InsertVersion(id, in)
}

// Iterate over given IDs and delete from given index
func genericDelete(index bleve.Index, ids ...string) error {
	for _, id := range ids {
		err := index.Delete(id)
		if err != nil {
			return err
		}
	}
	return nil
}

// Delete collections by ID(s)
func (b *bleveSearchbase) DeleteCollection(ids ...string) error {
	return genericDelete(b.collections, ids...)
}

// Delete items by ID(s)
func (b *bleveSearchbase) DeleteItem(ids ...string) error {
	return genericDelete(b.items, ids...)
}

// Delete versions by ID(s)
func (b *bleveSearchbase) DeleteVersion(ids ...string) error {
	return genericDelete(b.versions, ids...)
}

// Delete resources by ID(s)
func (b *bleveSearchbase) DeleteResource(ids ...string) error {
	return genericDelete(b.resources, ids...)
}

// Delete links by ID(s)
func (b *bleveSearchbase) DeleteLink(ids ...string) error {
	return genericDelete(b.links, ids...)
}

// Transform a QueryDesc into a bleve compatible search string for a collection
func toCollectionQueryString(desc *wyc.QueryDesc) string {
	sq := []string{}
	if desc.Id != "" {
		sq = append(sq, fmt.Sprintf("+Id:%s", desc.Id))
	}
	if desc.Name != "" {
		sq = append(sq, fmt.Sprintf("+Name:%s", b64encode(desc.Name)))
	}
	return strings.Join(sq, " ")
}

// Transform a QueryDesc into a bleve compatible search string for a item
func toItemQueryString(desc *wyc.QueryDesc) string {
	sq := []string{}
	if desc.Id != "" {
		sq = append(sq, fmt.Sprintf("+Id:%s", desc.Id))
	}
	if desc.ItemType != "" {
		sq = append(sq, fmt.Sprintf("+ItemType:%s", b64encode(desc.ItemType)))
	}
	if desc.Variant != "" {
		sq = append(sq, fmt.Sprintf("+Variant:%s", b64encode(desc.Variant)))
	}
	if desc.Parent != "" {
		sq = append(sq, fmt.Sprintf("+Parent:%s", desc.Parent))
	}
	for k, v := range desc.Facets {
		sq = append(sq, fmt.Sprintf("+Facets.%s:%s", b64encode(k), b64encode(v)))
	}
	return strings.Join(sq, " ")
}

// Transform a QueryDesc into a bleve compatible search string for a version
func toVersionQueryString(desc *wyc.QueryDesc) string {
	sq := []string{}
	if desc.Id != "" {
		sq = append(sq, fmt.Sprintf("+Id:%s", desc.Id))
	}
	if desc.Parent != "" {
		sq = append(sq, fmt.Sprintf("+Parent:%s", desc.Parent))
	}
	for k, v := range desc.Facets {
		sq = append(sq, fmt.Sprintf("+Facets.%s:%s", b64encode(k), b64encode(v)))
	}
	if desc.VersionNumber > 0 {
		sq = append(sq, fmt.Sprintf("+Number:%d", desc.VersionNumber))
	}
	return strings.Join(sq, " ")
}

// Transform a QueryDesc into a bleve compatible search string for a resource
func toResourceQueryString(desc *wyc.QueryDesc) string {
	sq := []string{}
	if desc.Id != "" {
		sq = append(sq, fmt.Sprintf("+Id:%s", desc.Id))
	}
	if desc.Parent != "" {
		sq = append(sq, fmt.Sprintf("+Parent:%s", desc.Parent))
	}
	if desc.ResourceType != "" {
		sq = append(sq, fmt.Sprintf("+ResourceType:%s", b64encode(desc.ResourceType)))
	}
	if desc.Name != "" {
		sq = append(sq, fmt.Sprintf("+Name:%s", b64encode(desc.Name)))
	}
	if desc.Location != "" {
		sq = append(sq, fmt.Sprintf("+Location:%s", b64encode(desc.Location)))
	}
	return strings.Join(sq, " ")
}

// Transform a QueryDesc into a bleve compatible search string for a link
func toLinkQueryString(desc *wyc.QueryDesc) string {
	sq := []string{}
	if desc.Id != "" {
		sq = append(sq, fmt.Sprintf("+Id:%s", desc.Id))
	}
	if desc.Name != "" {
		sq = append(sq, fmt.Sprintf("+Name:%s", b64encode(desc.Name)))
	}
	if desc.LinkSrc != "" {
		sq = append(sq, fmt.Sprintf("+Src:%s", desc.LinkSrc))
	}
	if desc.LinkDst != "" {
		sq = append(sq, fmt.Sprintf("+Dst:%s", desc.LinkDst))
	}
	return strings.Join(sq, " ")
}

// A generic query function that handles grabbing IDs from a bleve index given
//  - limit: max number of entries to return
//  - from: return only entries after this number
//  - index: a bleve index to search
//  - convert: a function to take a QueryDesc and turn it into a bleve query string
//  - queries: the search QueryDesc objects
func genericQuery(limit, from int, index bleve.Index, convert func(desc *wyc.QueryDesc) string, queries ...*wyc.QueryDesc) ([]string, error) {
	// ToDo: There is probably a smarter way to do this as a single query with limit / page

	if len(queries) < 1 {
		return nil, nil
	}

	var result *bleve.SearchResult
	for _, query := range queries {
		search_query := bleve.NewQueryStringQuery(convert(query))

		search := bleve.NewSearchRequestOptions(search_query, limit, from, false)
		res, err := index.Search(search)

		if err != nil {
			return nil, err
		}
		if result == nil {
			result = res
		} else {
			if res.Hits.Len() > 0 {
				result.Merge(res)
			}
		}
	}

	ids := []string{}
	for _, doc := range result.Hits {
		ids = append(ids, doc.ID)
	}

	return ids, nil
}

// Search for collections matching the given query descriptions
func (b *bleveSearchbase) QueryCollection(limit, from int, query ...*wyc.QueryDesc) ([]string, error) {
	return genericQuery(limit, from, b.collections, toCollectionQueryString, query...)
}

// Search for items matching the given query descriptions
func (b *bleveSearchbase) QueryItem(limit, from int, query ...*wyc.QueryDesc) ([]string, error) {
	return genericQuery(limit, from, b.items, toItemQueryString, query...)
}

// Search for versions matching the given query descriptions
func (b *bleveSearchbase) QueryVersion(limit, from int, query ...*wyc.QueryDesc) ([]string, error) {
	return genericQuery(limit, from, b.versions, toVersionQueryString, query...)
}

// Search for resources matching the given query descriptions
func (b *bleveSearchbase) QueryResource(limit, from int, query ...*wyc.QueryDesc) ([]string, error) {
	return genericQuery(limit, from, b.resources, toResourceQueryString, query...)
}

// Search for links matching the given query descriptions
func (b *bleveSearchbase) QueryLink(limit, from int, query ...*wyc.QueryDesc) ([]string, error) {
	return genericQuery(limit, from, b.links, toLinkQueryString, query...)
}
<|endoftext|>


<commit_before>// Copyright (c) 2016, Janoš Guljaš <janos@resenje.org>
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package server

import (
	"bytes"
	"fmt"
	"html/template"
	"net"
	"net/http"
)

func renderToResponse(w http.ResponseWriter, tmpl *template.Template, name string, status int, data interface{}, contentType string) (err error) {
	if name == "" {
		name = "base"
	}
	buf := bytes.Buffer{}
	if err = tmpl.ExecuteTemplate(&buf, name, data); err != nil {
		return
	}
	if contentType != "" {
		w.Header().Set("Content-Type", contentType)
	}
	w.WriteHeader(status)
	_, err = buf.WriteTo(w)
	return
}

func renderToString(tmpl *template.Template, name string, data interface{}) (string, error) {
	if name == "" {
		name = "base"
	}
	buf := bytes.Buffer{}
	if err := tmpl.ExecuteTemplate(&buf, name, data); err != nil {
		return "", err
	}
	return buf.String(), nil
}

func respondText(w http.ResponseWriter, tmpl *template.Template, data interface{}) {
	if err := renderToResponse(w, tmpl, "", http.StatusOK, data, "text/plain; charset=utf-8"); err != nil {
		panic(fmt.Sprintf("respond text: %s", err))
	}
}

func respond(w http.ResponseWriter, templateName string, data interface{}) {
	if err := renderToResponse(w, srv.templates[templateName], "", http.StatusOK, data, "text/html; charset=utf-8"); err != nil {
		panic(fmt.Sprintf("respond: %s", err))
	}
}

var errorTemplates = map[int][]string{
	http.StatusBadRequest:            {"BadRequest", "BadRequestPrivate"},
	http.StatusUnauthorized:          {"Unauthorized", "UnauthorizedPrivate"},
	http.StatusForbidden:             {"Forbidden", "ForbiddenPrivate"},
	http.StatusNotFound:              {"NotFound", "NotFoundPrivate"},
	http.StatusRequestEntityTooLarge: {"RequestEntityTooLarge", "RequestEntityTooLargePrivate"},
	http.StatusInternalServerError:   {"InternalServerError", "InternalServerErrorPrivate"},
	http.StatusServiceUnavailable:    {"ServiceUnavailable", "ServiceUnavailablePrivate"},
}

func respondError(w http.ResponseWriter, r *http.Request, c int) {
	var ctx map[string]interface{}
	u, r, err := getRequestUser(r)
	if err != nil {
		srv.Logger.Errorf("get user: %s", err)
		if _, ok := err.(net.Error); ok {
			if err := renderToResponse(w, srv.templates["ServiceUnavailable"], "", http.StatusServiceUnavailable, nil, "text/html; charset=utf-8"); err != nil {
				srv.Logger.Errorf("render service unavailable response: %s", err)
			}
			return
		}
		if err := renderToResponse(w, srv.templates["InternalServerError"], "", http.StatusServiceUnavailable, nil, "text/html; charset=utf-8"); err != nil {
			srv.Logger.Errorf("render internal server error response: %s", err)
		}
		return
	}
	var tpl *template.Template
	if u != nil {
		tpl = srv.templates[errorTemplates[c][1]]
		ctx = map[string]interface{}{
			"User": u,
		}
	} else {
		tpl = srv.templates[errorTemplates[c][0]]
	}
	if err := renderToResponse(w, tpl, "", c, ctx, "text/html; charset=utf-8"); err != nil {
		srv.Logger.Errorf("render http code %v response: %s", c, err)
	}
}

func htmlBadRequestHandler(w http.ResponseWriter, r *http.Request) {
	respondError(w, r, http.StatusBadRequest)
}

func htmlUnauthorizedHandler(w http.ResponseWriter, r *http.Request) {
	respondError(w, r, http.StatusUnauthorized)
}

func htmlForbiddenHandler(w http.ResponseWriter, r *http.Request) {
	respondError(w, r, http.StatusForbidden)
}

func htmlNotFoundHandler(w http.ResponseWriter, r *http.Request) {
	respondError(w, r, http.StatusNotFound)
}

func htmlRequestEntityTooLargeHandler(w http.ResponseWriter, r *http.Request) {
	respondError(w, r, http.StatusRequestEntityTooLarge)
}

func htmlInternalServerErrorHandler(w http.ResponseWriter, r *http.Request) {
	respondError(w, r, http.StatusInternalServerError)
}

func htmlServiceUnavailableHandler(w http.ResponseWriter, r *http.Request) {
	respondError(w, r, http.StatusServiceUnavailable)
}
<commit_msg>Improve template render error handling<commit_after>// Copyright (c) 2016, Janoš Guljaš <janos@resenje.org>
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package server

import (
	"bytes"
	"html/template"
	"net"
	"net/http"
)

func renderToResponse(w http.ResponseWriter, tmpl *template.Template, name string, status int, data interface{}, contentType string) {
	if name == "" {
		name = "base"
	}
	buf := bytes.Buffer{}
	if err := tmpl.ExecuteTemplate(&buf, name, data); err != nil {
		panic(err)
	}
	if contentType != "" {
		w.Header().Set("Content-Type", contentType)
	}
	w.WriteHeader(status)
	if _, err := buf.WriteTo(w); err != nil {
		srv.Logger.Errorf("render to response: %v", err)
	}
	return
}

func renderToString(tmpl *template.Template, name string, data interface{}) (string, error) {
	if name == "" {
		name = "base"
	}
	buf := bytes.Buffer{}
	if err := tmpl.ExecuteTemplate(&buf, name, data); err != nil {
		return "", err
	}
	return buf.String(), nil
}

func respondText(w http.ResponseWriter, tmpl *template.Template, data interface{}) {
	renderToResponse(w, tmpl, "", http.StatusOK, data, "text/plain; charset=utf-8")
}

func respond(w http.ResponseWriter, templateName string, data interface{}) {
	renderToResponse(w, srv.templates[templateName], "", http.StatusOK, data, "text/html; charset=utf-8")
}

var errorTemplates = map[int][]string{
	http.StatusBadRequest:            {"BadRequest", "BadRequestPrivate"},
	http.StatusUnauthorized:          {"Unauthorized", "UnauthorizedPrivate"},
	http.StatusForbidden:             {"Forbidden", "ForbiddenPrivate"},
	http.StatusNotFound:              {"NotFound", "NotFoundPrivate"},
	http.StatusRequestEntityTooLarge: {"RequestEntityTooLarge", "RequestEntityTooLargePrivate"},
	http.StatusInternalServerError:   {"InternalServerError", "InternalServerErrorPrivate"},
	http.StatusServiceUnavailable:    {"ServiceUnavailable", "ServiceUnavailablePrivate"},
}

func respondError(w http.ResponseWriter, r *http.Request, c int) {
	var ctx map[string]interface{}
	u, r, err := getRequestUser(r)
	if err != nil {
		srv.Logger.Errorf("get user: %s", err)
		if _, ok := err.(net.Error); ok {
			renderToResponse(w, srv.templates["ServiceUnavailable"], "", http.StatusServiceUnavailable, nil, "text/html; charset=utf-8")
			return
		}
		renderToResponse(w, srv.templates["InternalServerError"], "", http.StatusServiceUnavailable, nil, "text/html; charset=utf-8")
		return
	}
	var tpl *template.Template
	if u != nil {
		tpl = srv.templates[errorTemplates[c][1]]
		ctx = map[string]interface{}{
			"User": u,
		}
	} else {
		tpl = srv.templates[errorTemplates[c][0]]
	}
	renderToResponse(w, tpl, "", c, ctx, "text/html; charset=utf-8")
}

func htmlBadRequestHandler(w http.ResponseWriter, r *http.Request) {
	respondError(w, r, http.StatusBadRequest)
}

func htmlUnauthorizedHandler(w http.ResponseWriter, r *http.Request) {
	respondError(w, r, http.StatusUnauthorized)
}

func htmlForbiddenHandler(w http.ResponseWriter, r *http.Request) {
	respondError(w, r, http.StatusForbidden)
}

func htmlNotFoundHandler(w http.ResponseWriter, r *http.Request) {
	respondError(w, r, http.StatusNotFound)
}

func htmlRequestEntityTooLargeHandler(w http.ResponseWriter, r *http.Request) {
	respondError(w, r, http.StatusRequestEntityTooLarge)
}

func htmlInternalServerErrorHandler(w http.ResponseWriter, r *http.Request) {
	respondError(w, r, http.StatusInternalServerError)
}

func htmlServiceUnavailableHandler(w http.ResponseWriter, r *http.Request) {
	respondError(w, r, http.StatusServiceUnavailable)
}
<|endoftext|>


<commit_before>//go:build linux && cgo
// +build linux,cgo

package device

import (
	"fmt"
	"strings"

	udev "github.com/farjump/go-libudev"

	deviceConfig "github.com/lxc/lxd/lxd/device/config"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/validate"
)

// unixHotplugIsOurDevice indicates whether the unixHotplug device event qualifies as part of our device.
// This function is not defined against the unixHotplug struct type so that it can be used in event
// callbacks without needing to keep a reference to the unixHotplug device struct.
func unixHotplugIsOurDevice(config deviceConfig.Device, unixHotplug *UnixHotplugEvent) bool {
	// Check if event matches criteria for this device, if not return.
	if (config["vendorid"] != "" && config["vendorid"] != unixHotplug.Vendor) || (config["productid"] != "" && config["productid"] != unixHotplug.Product) {
		return false
	}

	return true
}

type unixHotplug struct {
	deviceCommon
}

// isRequired indicates whether the device config requires this device to start OK.
func (d *unixHotplug) isRequired() bool {
	// Defaults to not required.
	if shared.IsTrue(d.config["required"]) {
		return true
	}

	return false
}

// validateConfig checks the supplied config for correctness.
func (d *unixHotplug) validateConfig(instConf instance.ConfigReader) error {
	if !instanceSupported(instConf.Type(), instancetype.Container) {
		return ErrUnsupportedDevType
	}

	rules := map[string]func(string) error{
		"vendorid":  validate.Optional(validate.IsDeviceID),
		"productid": validate.Optional(validate.IsDeviceID),
		"uid":       unixValidUserID,
		"gid":       unixValidUserID,
		"mode":      unixValidOctalFileMode,
		"required":  validate.Optional(validate.IsBool),
	}

	err := d.config.Validate(rules)
	if err != nil {
		return err
	}

	if d.config["vendorid"] == "" && d.config["productid"] == "" {
		return fmt.Errorf("Unix hotplug devices require a vendorid or a productid")
	}

	return nil
}

// Register is run after the device is started or when LXD starts.
func (d *unixHotplug) Register() error {
	// Extract variables needed to run the event hook so that the reference to this device
	// struct is not needed to be kept in memory.
	devicesPath := d.inst.DevicesPath()
	devConfig := d.config
	deviceName := d.name
	state := d.state

	// Handler for when a UnixHotplug event occurs.
	f := func(e UnixHotplugEvent) (*deviceConfig.RunConfig, error) {
		runConf := deviceConfig.RunConfig{}

		if e.Action == "add" {
			if !unixHotplugIsOurDevice(devConfig, &e) {
				return nil, nil
			}

			if e.Subsystem == "block" {
				err := unixDeviceSetupBlockNum(state, devicesPath, "unix", deviceName, devConfig, e.Major, e.Minor, e.Path, false, &runConf)
				if err != nil {
					return nil, err
				}
			} else {
				err := unixDeviceSetupCharNum(state, devicesPath, "unix", deviceName, devConfig, e.Major, e.Minor, e.Path, false, &runConf)
				if err != nil {
					return nil, err
				}
			}
		} else if e.Action == "remove" {
			relativeTargetPath := strings.TrimPrefix(e.Path, "/")
			err := unixDeviceRemove(devicesPath, "unix", deviceName, relativeTargetPath, &runConf)
			if err != nil {
				return nil, err
			}

			// Add a post hook function to remove the specific unix hotplug device file after unmount.
			runConf.PostHooks = []func() error{func() error {
				err := unixDeviceDeleteFiles(state, devicesPath, "unix", deviceName, relativeTargetPath)
				if err != nil {
					return fmt.Errorf("Failed to delete files for device '%s': %v", deviceName, err)
				}

				return nil
			}}
		}

		runConf.Uevents = append(runConf.Uevents, e.UeventParts)

		return &runConf, nil
	}

	unixHotplugRegisterHandler(d.inst, d.name, f)

	return nil
}

// Start is run when the device is added to the instance
func (d *unixHotplug) Start() (*deviceConfig.RunConfig, error) {
	runConf := deviceConfig.RunConfig{}
	runConf.PostHooks = []func() error{d.Register}

	device := d.loadUnixDevice()
	if d.isRequired() && device == nil {
		return nil, fmt.Errorf("Required Unix Hotplug device not found")
	}
	if device == nil {
		return &runConf, nil
	}

	devnum := device.Devnum()
	major := uint32(devnum.Major())
	minor := uint32(devnum.Minor())

	// setup device
	var err error
	if device.Subsystem() == "block" {
		err = unixDeviceSetupBlockNum(d.state, d.inst.DevicesPath(), "unix", d.name, d.config, major, minor, device.Devnode(), false, &runConf)
	} else {
		err = unixDeviceSetupCharNum(d.state, d.inst.DevicesPath(), "unix", d.name, d.config, major, minor, device.Devnode(), false, &runConf)
	}

	if err != nil {
		return nil, err
	}

	return &runConf, nil
}

// Stop is run when the device is removed from the instance
func (d *unixHotplug) Stop() (*deviceConfig.RunConfig, error) {
	unixHotplugUnregisterHandler(d.inst, d.name)

	runConf := deviceConfig.RunConfig{
		PostHooks: []func() error{d.postStop},
	}

	err := unixDeviceRemove(d.inst.DevicesPath(), "unix", d.name, "", &runConf)
	if err != nil {
		return nil, err
	}

	return &runConf, nil
}

// postStop is run after the device is removed from the instance
func (d *unixHotplug) postStop() error {
	err := unixDeviceDeleteFiles(d.state, d.inst.DevicesPath(), "unix", d.name, "")
	if err != nil {
		return fmt.Errorf("Failed to delete files for device '%s': %v", d.name, err)
	}

	return nil
}

// loadUnixDevice scans the host machine for unix devices with matching product/vendor ids
// and returns the first matching device with the subsystem type char or block
func (d *unixHotplug) loadUnixDevice() *udev.Device {
	// Find device if exists
	u := udev.Udev{}
	e := u.NewEnumerate()

	if d.config["vendorid"] != "" {
		e.AddMatchProperty("ID_VENDOR_ID", d.config["vendorid"])
	}
	if d.config["productid"] != "" {
		e.AddMatchProperty("ID_MODEL_ID", d.config["productid"])
	}
	e.AddMatchIsInitialized()
	devices, _ := e.Devices()
	var device *udev.Device
	for i := range devices {
		device = devices[i]

		devnum := device.Devnum()
		if devnum.Major() == 0 || devnum.Minor() == 0 {
			continue
		}

		if device.Devnode() == "" {
			continue
		}

		if !strings.HasPrefix(device.Subsystem(), "usb") {
			return device
		}
	}

	return nil
}
<commit_msg>lxd/device: Switch to github.com/jochenvg/go-udev<commit_after>//go:build linux && cgo
// +build linux,cgo

package device

import (
	"fmt"
	"strings"

	udev "github.com/jochenvg/go-udev"

	deviceConfig "github.com/lxc/lxd/lxd/device/config"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/validate"
)

// unixHotplugIsOurDevice indicates whether the unixHotplug device event qualifies as part of our device.
// This function is not defined against the unixHotplug struct type so that it can be used in event
// callbacks without needing to keep a reference to the unixHotplug device struct.
func unixHotplugIsOurDevice(config deviceConfig.Device, unixHotplug *UnixHotplugEvent) bool {
	// Check if event matches criteria for this device, if not return.
	if (config["vendorid"] != "" && config["vendorid"] != unixHotplug.Vendor) || (config["productid"] != "" && config["productid"] != unixHotplug.Product) {
		return false
	}

	return true
}

type unixHotplug struct {
	deviceCommon
}

// isRequired indicates whether the device config requires this device to start OK.
func (d *unixHotplug) isRequired() bool {
	// Defaults to not required.
	if shared.IsTrue(d.config["required"]) {
		return true
	}

	return false
}

// validateConfig checks the supplied config for correctness.
func (d *unixHotplug) validateConfig(instConf instance.ConfigReader) error {
	if !instanceSupported(instConf.Type(), instancetype.Container) {
		return ErrUnsupportedDevType
	}

	rules := map[string]func(string) error{
		"vendorid":  validate.Optional(validate.IsDeviceID),
		"productid": validate.Optional(validate.IsDeviceID),
		"uid":       unixValidUserID,
		"gid":       unixValidUserID,
		"mode":      unixValidOctalFileMode,
		"required":  validate.Optional(validate.IsBool),
	}

	err := d.config.Validate(rules)
	if err != nil {
		return err
	}

	if d.config["vendorid"] == "" && d.config["productid"] == "" {
		return fmt.Errorf("Unix hotplug devices require a vendorid or a productid")
	}

	return nil
}

// Register is run after the device is started or when LXD starts.
func (d *unixHotplug) Register() error {
	// Extract variables needed to run the event hook so that the reference to this device
	// struct is not needed to be kept in memory.
	devicesPath := d.inst.DevicesPath()
	devConfig := d.config
	deviceName := d.name
	state := d.state

	// Handler for when a UnixHotplug event occurs.
	f := func(e UnixHotplugEvent) (*deviceConfig.RunConfig, error) {
		runConf := deviceConfig.RunConfig{}

		if e.Action == "add" {
			if !unixHotplugIsOurDevice(devConfig, &e) {
				return nil, nil
			}

			if e.Subsystem == "block" {
				err := unixDeviceSetupBlockNum(state, devicesPath, "unix", deviceName, devConfig, e.Major, e.Minor, e.Path, false, &runConf)
				if err != nil {
					return nil, err
				}
			} else {
				err := unixDeviceSetupCharNum(state, devicesPath, "unix", deviceName, devConfig, e.Major, e.Minor, e.Path, false, &runConf)
				if err != nil {
					return nil, err
				}
			}
		} else if e.Action == "remove" {
			relativeTargetPath := strings.TrimPrefix(e.Path, "/")
			err := unixDeviceRemove(devicesPath, "unix", deviceName, relativeTargetPath, &runConf)
			if err != nil {
				return nil, err
			}

			// Add a post hook function to remove the specific unix hotplug device file after unmount.
			runConf.PostHooks = []func() error{func() error {
				err := unixDeviceDeleteFiles(state, devicesPath, "unix", deviceName, relativeTargetPath)
				if err != nil {
					return fmt.Errorf("Failed to delete files for device '%s': %v", deviceName, err)
				}

				return nil
			}}
		}

		runConf.Uevents = append(runConf.Uevents, e.UeventParts)

		return &runConf, nil
	}

	unixHotplugRegisterHandler(d.inst, d.name, f)

	return nil
}

// Start is run when the device is added to the instance
func (d *unixHotplug) Start() (*deviceConfig.RunConfig, error) {
	runConf := deviceConfig.RunConfig{}
	runConf.PostHooks = []func() error{d.Register}

	device := d.loadUnixDevice()
	if d.isRequired() && device == nil {
		return nil, fmt.Errorf("Required Unix Hotplug device not found")
	}
	if device == nil {
		return &runConf, nil
	}

	devnum := device.Devnum()
	major := uint32(devnum.Major())
	minor := uint32(devnum.Minor())

	// setup device
	var err error
	if device.Subsystem() == "block" {
		err = unixDeviceSetupBlockNum(d.state, d.inst.DevicesPath(), "unix", d.name, d.config, major, minor, device.Devnode(), false, &runConf)
	} else {
		err = unixDeviceSetupCharNum(d.state, d.inst.DevicesPath(), "unix", d.name, d.config, major, minor, device.Devnode(), false, &runConf)
	}

	if err != nil {
		return nil, err
	}

	return &runConf, nil
}

// Stop is run when the device is removed from the instance
func (d *unixHotplug) Stop() (*deviceConfig.RunConfig, error) {
	unixHotplugUnregisterHandler(d.inst, d.name)

	runConf := deviceConfig.RunConfig{
		PostHooks: []func() error{d.postStop},
	}

	err := unixDeviceRemove(d.inst.DevicesPath(), "unix", d.name, "", &runConf)
	if err != nil {
		return nil, err
	}

	return &runConf, nil
}

// postStop is run after the device is removed from the instance
func (d *unixHotplug) postStop() error {
	err := unixDeviceDeleteFiles(d.state, d.inst.DevicesPath(), "unix", d.name, "")
	if err != nil {
		return fmt.Errorf("Failed to delete files for device '%s': %v", d.name, err)
	}

	return nil
}

// loadUnixDevice scans the host machine for unix devices with matching product/vendor ids
// and returns the first matching device with the subsystem type char or block
func (d *unixHotplug) loadUnixDevice() *udev.Device {
	// Find device if exists
	u := udev.Udev{}
	e := u.NewEnumerate()

	if d.config["vendorid"] != "" {
		e.AddMatchProperty("ID_VENDOR_ID", d.config["vendorid"])
	}
	if d.config["productid"] != "" {
		e.AddMatchProperty("ID_MODEL_ID", d.config["productid"])
	}
	e.AddMatchIsInitialized()
	devices, _ := e.Devices()
	var device *udev.Device
	for i := range devices {
		device = devices[i]

		devnum := device.Devnum()
		if devnum.Major() == 0 || devnum.Minor() == 0 {
			continue
		}

		if device.Devnode() == "" {
			continue
		}

		if !strings.HasPrefix(device.Subsystem(), "usb") {
			return device
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package response551

import (
	"encoding/json"
	"fmt"
	"github.com/go51/string551"
	"html/template"
	"net/http"
)

type RedirectType struct {
	uri  string
	code int
	text string
}

func Redirect(uri string, code int) RedirectType {
	return RedirectType{
		uri:  uri,
		code: code,
		text: http.StatusText(code),
	}
}

func (r RedirectType) Code() int {
	return r.code
}

func (r RedirectType) Text() string {
	return r.text
}

func (r RedirectType) Uri() string {
	return r.uri
}

type ErrorType struct {
	code    int
	text    string
	message string
}

func Error(code int, message string) ErrorType {
	return ErrorType{
		code:    code,
		text:    http.StatusText(code),
		message: message,
	}
}

func (e ErrorType) Code() int {
	return e.code
}

func (e ErrorType) Text() string {
	return e.text
}

func (e ErrorType) Message() string {
	return e.message
}

func (e ErrorType) String() string {
	return e.message
}

func Response(w http.ResponseWriter, r *http.Request, data interface{}, packageName, routeName string) {
	if redirectType, ok := interface{}(data).(RedirectType); ok {
		// Redirect Type
		http.Redirect(w, r, redirectType.uri, redirectType.code)
		return
	} else if errorType, ok := interface{}(data).(ErrorType); ok {
		// Error Type
		http.Error(w, errorType.message, errorType.code)
		return
	} else if _, ok := interface{}(data).(map[string]interface{}); ok {
		if isJSON(r) {
			jsonOutput(w, data)
		} else {
			// View template rendering
			htmlOutput(w, data, packageName, routeName)
		}
		return
	}

	fmt.Fprintf(w, "%v", data)

}

func isJSON(r *http.Request) bool {
	format := string551.Lower(r.FormValue("format"))

	if format == "json" {
		return true
	} else {
		return false
	}
}

func htmlOutput(w http.ResponseWriter, data interface{}, packageName, routeName string) {

	templates := []string{
		"view/template/base.html",
		"view/" + packageName + "/" + routeName + ".html",
	}

	tmpl, err := template.New(packageName + routeName).Funcs(funcMap()).ParseFiles(templates...)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	err = tmpl.ExecuteTemplate(w, "base", data)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

}

func jsonOutput(w http.ResponseWriter, data interface{}) {
	jsonData, err := json.Marshal(data)
	if err != nil {
		panic(err)
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write(jsonData)
}

func funcMap() template.FuncMap {
	funcMap := template.FuncMap{}

	funcMap["raw"] = func(text string) template.HTML { return template.HTML(text) }

	return funcMap

}
<commit_msg>refs #13 html 出力の際に共通項目としてログインユーザーの情報を自動で追加する<commit_after>package response551

import (
	"encoding/json"
	"fmt"
	"github.com/go51/string551"
	"html/template"
	"net/http"
)

type RedirectType struct {
	uri  string
	code int
	text string
}

func Redirect(uri string, code int) RedirectType {
	return RedirectType{
		uri:  uri,
		code: code,
		text: http.StatusText(code),
	}
}

func (r RedirectType) Code() int {
	return r.code
}

func (r RedirectType) Text() string {
	return r.text
}

func (r RedirectType) Uri() string {
	return r.uri
}

type ErrorType struct {
	code    int
	text    string
	message string
}

func Error(code int, message string) ErrorType {
	return ErrorType{
		code:    code,
		text:    http.StatusText(code),
		message: message,
	}
}

func (e ErrorType) Code() int {
	return e.code
}

func (e ErrorType) Text() string {
	return e.text
}

func (e ErrorType) Message() string {
	return e.message
}

func (e ErrorType) String() string {
	return e.message
}

func Response(w http.ResponseWriter, r *http.Request, data interface{}, packageName, routeName string, user interface{}) {
	if redirectType, ok := interface{}(data).(RedirectType); ok {
		// Redirect Type
		http.Redirect(w, r, redirectType.uri, redirectType.code)
		return
	} else if errorType, ok := interface{}(data).(ErrorType); ok {
		// Error Type
		http.Error(w, errorType.message, errorType.code)
		return
	} else if param, ok := interface{}(data).(map[string]interface{}); ok {
		if isJSON(r) {
			jsonOutput(w, param)
		} else {
			// View template rendering
			param["user"] = user
			htmlOutput(w, param, packageName, routeName)
		}
		return
	}

	fmt.Fprintf(w, "%v", data)

}

func isJSON(r *http.Request) bool {
	format := string551.Lower(r.FormValue("format"))

	if format == "json" {
		return true
	} else {
		return false
	}
}

func htmlOutput(w http.ResponseWriter, data interface{}, packageName, routeName string) {

	templates := []string{
		"view/template/base.html",
		"view/" + packageName + "/" + routeName + ".html",
	}

	tmpl, err := template.New(packageName + routeName).Funcs(funcMap()).ParseFiles(templates...)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	err = tmpl.ExecuteTemplate(w, "base", data)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

}

func jsonOutput(w http.ResponseWriter, data interface{}) {
	jsonData, err := json.Marshal(data)
	if err != nil {
		panic(err)
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write(jsonData)
}

func funcMap() template.FuncMap {
	funcMap := template.FuncMap{}

	funcMap["raw"] = func(text string) template.HTML {
		return template.HTML(text)
	}

	return funcMap

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

func TestRetool(t *testing.T) {
	// These integration tests require more than most go tests: they require a go compiler to build
	// retool, a working version of git to perform retool's operations, and network access to do the
	// git fetches.
	retool, err := buildRetool()
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		_ = os.RemoveAll(filepath.Dir(retool))
	}()

	t.Run("cache pollution", func(t *testing.T) {
		dir, err := ioutil.TempDir("", "")
		if err != nil {
			t.Fatalf("unable to make temp dir: %s", err)
		}
		defer func() {
			_ = os.RemoveAll(dir)
		}()

		// This should fail because this version of mockery has an import line that points to uber's
		// internal repo, which can't be reached:
		cmd := exec.Command(retool, "-base-dir", dir, "add",
			"github.com/vektra/mockery/cmd/mockery", "d895b9fcc32730719faaccd7840ad7277c94c2d0",
		)
		cmd.Dir = dir
		_, err = cmd.Output()
		if err == nil {
			t.Fatal("expected error when adding mockery at broken commit d895b9, but got no error")
		}

		// Now, without cleaning the cache, try again on a healthy commit. In
		// ff9a1fda7478ede6250ee3c7e4ce32dc30096236 of retool and earlier, this would still fail because
		// the cache would be polluted with a bad source tree.
		cmd = exec.Command(retool, "-base-dir", dir, "add",
			"github.com/vektra/mockery/cmd/mockery", "origin/master",
		)
		cmd.Dir = dir
		_, err = cmd.Output()
		if err != nil {
			if exitErr, ok := err.(*exec.ExitError); ok {
				t.Fatalf("expected no error when adding mockery at broken commit d895b9, but got this:\n%s", string(exitErr.Stderr))
			} else {
				t.Fatalf("unexpected err when running %q: %q", strings.Join(cmd.Args, " "), err)
			}
		}
	})

	t.Run("version", func(t *testing.T) {
		dir, err := ioutil.TempDir("", "")
		if err != nil {
			t.Fatalf("unable to make temp dir: %s", err)
		}
		defer func() {
			_ = os.RemoveAll(dir)
		}()

		// Should work even in a directory without tools.json
		cmd := exec.Command(retool, "version")
		cmd.Dir = dir
		out, err := cmd.Output()
		if err != nil {
			t.Fatalf("expected no errors when using retool version, have this:\n%s", string(out))
		}
		if want := fmt.Sprintf("retool %s", version); string(out) != want {
			t.Errorf("have=%q, want=%q", string(out), want)
		}
	})

	t.Run("build", func(t *testing.T) {
		dir, err := ioutil.TempDir("", "")
		if err != nil {
			t.Fatalf("unable to make temp dir: %s", err)
		}
		defer func() {
			_ = os.RemoveAll(dir)
		}()

		cmd := exec.Command(retool, "-base-dir", dir, "add",
			"github.com/twitchtv/retool", "origin/master",
		)
		cmd.Dir = dir
		_, err = cmd.Output()
		if err != nil {
			if exitErr, ok := err.(*exec.ExitError); ok {
				t.Fatalf("expected no errors when using retool add, have this:\n%s", string(exitErr.Stderr))
			} else {
				t.Fatalf("unexpected err when running %q: %q", strings.Join(cmd.Args, " "), err)
			}
		}

		// Suppose we only have _tools/src available. Does `retool build` work?
		_ = os.RemoveAll(filepath.Join(dir, "_tools", "bin"))
		_ = os.RemoveAll(filepath.Join(dir, "_tools", "pkg"))
		_ = os.RemoveAll(filepath.Join(dir, "_tools", "manifest.json"))

		cmd = exec.Command(retool, "-base-dir", dir, "build")
		cmd.Dir = dir
		_, err = cmd.Output()

		if err != nil {
			if exitErr, ok := err.(*exec.ExitError); ok {
				t.Fatalf("expected no errors when using retool build, have this:\n%s", string(exitErr.Stderr))
			} else {
				t.Fatalf("unexpected err when running %q: %q", strings.Join(cmd.Args, " "), err)
			}
		}

		// Now the binary should be installed
		assertBinInstalled(t, dir, "retool")

		// Legal files should be kept around
		_, err = os.Stat(filepath.Join(dir, "_tools", "src", "github.com", "twitchtv", "retool", "LICENSE"))
		if err != nil {
			t.Error("missing license file")
		}
	})

	t.Run("dep_added", func(t *testing.T) {
		dir, err := ioutil.TempDir("", "")
		if err != nil {
			t.Fatalf("unable to make temp dir: %s", err)
		}
		defer func() {
			_ = os.RemoveAll(dir)
		}()

		// Use a package which used to have a dependency (in this case, one on
		// github.com/spenczar/retool_test_lib), but doesn't have that dependency for HEAD of
		// origin/master today.
		cmd := exec.Command(retool, "-base-dir", dir, "add",
			"github.com/spenczar/retool_test_app", "origin/has_dep",
		)
		cmd.Dir = dir
		_, err = cmd.Output()
		if err != nil {
			if exitErr, ok := err.(*exec.ExitError); ok {
				t.Fatalf("expected no errors when using retool add, have this:\n%s", string(exitErr.Stderr))
			} else {
				t.Fatalf("unexpected err when running %q: %q", strings.Join(cmd.Args, " "), err)
			}
		}
	})

	t.Run("do", func(t *testing.T) {
		dir, err := ioutil.TempDir("", "")
		if err != nil {
			t.Fatalf("unable to make temp dir: %s", err)
		}
		defer func() {
			_ = os.RemoveAll(dir)
		}()

		cmd := exec.Command(retool, "-base-dir", dir, "add",
			"github.com/twitchtv/retool", "v1.0.1",
		)
		cmd.Dir = dir
		_, err = cmd.Output()
		if err != nil {
			if exitErr, ok := err.(*exec.ExitError); ok {
				t.Fatalf("expected no errors when using retool add, have this:\n%s", string(exitErr.Stderr))
			} else {
				t.Fatalf("unexpected err when running %q: %q", strings.Join(cmd.Args, " "), err)
			}
		}

		cmd = exec.Command(retool, "do", "retool", "version")
		cmd.Dir = dir
		out, err := cmd.Output()
		if err != nil {
			if exitErr, ok := err.(*exec.ExitError); ok {
				t.Fatalf("expected no errors when using retool do, have this:\n%s", string(exitErr.Stderr))
			} else {
				t.Fatalf("unexpected err when running %q: %q", strings.Join(cmd.Args, " "), err)
			}
		}
		if want := "retool v1.0.1"; string(out) != want {
			t.Errorf("have=%q, want=%q", string(out), want)
		}
	})
}
<commit_msg>Reduce repetition in test command invocations (#13)<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

func TestRetool(t *testing.T) {
	// These integration tests require more than most go tests: they require a go compiler to build
	// retool, a working version of git to perform retool's operations, and network access to do the
	// git fetches.
	retool, err := buildRetool()
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		_ = os.RemoveAll(filepath.Dir(retool))
	}()

	t.Run("cache pollution", func(t *testing.T) {
		dir, err := ioutil.TempDir("", "")
		if err != nil {
			t.Fatalf("unable to make temp dir: %s", err)
		}
		defer func() {
			_ = os.RemoveAll(dir)
		}()

		// This should fail because this version of mockery has an import line that points to uber's
		// internal repo, which can't be reached:
		cmd := exec.Command(retool, "-base-dir", dir, "add",
			"github.com/vektra/mockery/cmd/mockery", "d895b9fcc32730719faaccd7840ad7277c94c2d0",
		)
		cmd.Dir = dir
		_, err = cmd.Output()
		if err == nil {
			t.Fatal("expected error when adding mockery at broken commit d895b9, but got no error")
		}

		// Now, without cleaning the cache, try again on a healthy commit. In
		// ff9a1fda7478ede6250ee3c7e4ce32dc30096236 of retool and earlier, this would still fail because
		// the cache would be polluted with a bad source tree.
		runRetoolCmd(t, dir, retool, "add", "github.com/vektra/mockery/cmd/mockery", "origin/master")
	})

	t.Run("version", func(t *testing.T) {
		dir, err := ioutil.TempDir("", "")
		if err != nil {
			t.Fatalf("unable to make temp dir: %s", err)
		}
		defer func() {
			_ = os.RemoveAll(dir)
		}()

		// Should work even in a directory without tools.json
		cmd := exec.Command(retool, "version")
		cmd.Dir = dir
		out, err := cmd.Output()
		if err != nil {
			t.Fatalf("expected no errors when using retool version, have this:\n%s", string(out))
		}
		if want := fmt.Sprintf("retool %s", version); string(out) != want {
			t.Errorf("have=%q, want=%q", string(out), want)
		}
	})

	t.Run("build", func(t *testing.T) {
		dir, err := ioutil.TempDir("", "")
		if err != nil {
			t.Fatalf("unable to make temp dir: %s", err)
		}
		defer func() {
			_ = os.RemoveAll(dir)
		}()
		runRetoolCmd(t, dir, retool, "add", "github.com/twitchtv/retool", "origin/master")

		// Suppose we only have _tools/src available. Does `retool build` work?
		_ = os.RemoveAll(filepath.Join(dir, "_tools", "bin"))
		_ = os.RemoveAll(filepath.Join(dir, "_tools", "pkg"))
		_ = os.RemoveAll(filepath.Join(dir, "_tools", "manifest.json"))

		runRetoolCmd(t, dir, retool, "build")

		// Now the binary should be installed
		assertBinInstalled(t, dir, "retool")

		// Legal files should be kept around
		_, err = os.Stat(filepath.Join(dir, "_tools", "src", "github.com", "twitchtv", "retool", "LICENSE"))
		if err != nil {
			t.Error("missing license file")
		}
	})

	t.Run("dep_added", func(t *testing.T) {
		dir, err := ioutil.TempDir("", "")
		if err != nil {
			t.Fatalf("unable to make temp dir: %s", err)
		}
		defer func() {
			_ = os.RemoveAll(dir)
		}()

		// Use a package which used to have a dependency (in this case, one on
		// github.com/spenczar/retool_test_lib), but doesn't have that dependency for HEAD of
		// origin/master today.
		runRetoolCmd(t, dir, retool, "add", "github.com/spenczar/retool_test_app", "origin/has_dep")
	})

	t.Run("do", func(t *testing.T) {
		dir, err := ioutil.TempDir("", "")
		if err != nil {
			t.Fatalf("unable to make temp dir: %s", err)
		}
		defer func() {
			_ = os.RemoveAll(dir)
		}()

		runRetoolCmd(t, dir, retool, "add", "github.com/twitchtv/retool", "v1.0.1")
		output := runRetoolCmd(t, dir, retool, "do", "retool", "version")
		if want := "retool v1.0.1"; output != want {
			t.Errorf("have=%q, want=%q", output, want)
		}
	})
}

func runRetoolCmd(t *testing.T, dir, retool string, args ...string) (output string) {
	args = append([]string{"-base-dir", dir}, args...)
	cmd := exec.Command(retool, args...)
	cmd.Dir = dir
	out, err := cmd.Output()
	if err != nil {
		if exitErr, ok := err.(*exec.ExitError); ok {
			t.Fatalf("command %q failed: %s", "retool "+strings.Join(cmd.Args[1:], " "), string(exitErr.Stderr))
		} else {
			t.Fatalf("unexpected err when running %q: %q", strings.Join(cmd.Args, " "), err)
		}
	}
	return string(out)
}
<|endoftext|>


<commit_before>package rhymer

import (
	"fmt"
	"math/rand"
	"strings"
	"testing"

	markov "github.com/high-moctane/go-markov_chain_Japanese"
)

var sampleInput = strings.Split(`ある日の暮方の事である。一人の下人が、羅生門の下で雨やみを待っていた。広い門の下には、この男のほかに誰もいない。ただ、所々丹塗の剥げた、大きな円柱に、蟋蟀が一匹とまっている。羅生門が、朱雀大路にある以上は、この男のほかにも、雨やみをする市女笠や揉烏帽子が、もう二三人はありそうなものである。それが、この男のほかには誰もいない。何故かと云うと、この二三年、京都には、地震とか辻風とか火事とか饑饉とか云う災がつづいて起った。そこで洛中のさびれ方は一通りではない。旧記によると、仏像や仏具を打砕いて、その丹がついたり、金銀の箔がついたりした木を、路ばたにつみ重ねて、薪の料に売っていたと云う事である。洛中がその始末であるから、羅生門の修理などは、元より誰も捨てて顧る者がなかった。するとその荒れ果てたのをよい事にして、狐狸が棲すむ。盗人が棲む。とうとうしまいには、引取り手のない死人を、この門へ持って来て、棄てて行くと云う習慣さえ出来た。そこで、日の目が見えなくなると、誰でも気味を悪るがって、この門の近所へは足ぶみをしない事になってしまったのである。その代りまた鴉がどこからか、たくさん集って来た。昼間見ると、その鴉が何羽となく輪を描いて、高い鴟尾のまわりを啼きながら、飛びまわっている。ことに門の上の空が、夕焼けであかくなる時には、それが胡麻をまいたようにはっきり見えた。鴉は、勿論、門の上にある死人の肉を、啄みに来るのである。――もっとも今日は、刻限が遅いせいか、一羽も見えない。ただ、所々、崩れかかった、そうしてその崩れ目に長い草のはえた石段の上に、鴉の糞が、点々と白くこびりついているのが見える。下人は七段ある石段の一番上の段に、洗いざらした紺の襖の尻を据えて、右の頬に出来た、大きな面皰を気にしながら、ぼんやり、雨のふるのを眺めていた。`, "。")

func init() {
	rand.Seed(0)
}

func TestGenerate(t *testing.T) {
	ma, _ := markov.New(1, map[string]string{})
	defer ma.Destroy()
	for _, v := range sampleInput {
		ma.Add(v)
	}
	weight := NewMoraWeight([]MoraWeightCell{{1.0, 10.0}, {2.0, 20.0}, {3.0, 30.0}})
	rh := New(ma, &weight, 7, 1000, 0.8)
	ph := rh.Generate()
	expected := []string{"広い門の上の空がその丹", "羅生門の近所へは一通り"}
	for i, v := range ph {
		if v.OriginalForm() != expected[i] {
			t.Errorf("expected %v, but %v", expected, ph)
		}
	}
}

func TestStream(t *testing.T) {
	ma, _ := markov.New(1, map[string]string{})
	defer ma.Destroy()
	for _, v := range sampleInput {
		ma.Add(v)
	}
	weight := NewMoraWeight([]MoraWeightCell{{1.0, 10.0}, {2.0, 20.0}, {3.0, 30.0}})
	rh := New(ma, &weight, 7, 100, 0.8)
	ch := rh.Stream()
	for i := 0; i < 5; i++ {
		for _, v := range <-ch {
			fmt.Println(v.OriginalForm())
		}
		fmt.Println("")
	}
}
<commit_msg>not works<commit_after>package rhymer

import (
	"math/rand"
	"strings"
	"testing"

	markov "github.com/high-moctane/go-markov_chain_Japanese"
)

var sampleInput = strings.Split(`ある日の暮方の事である。一人の下人が、羅生門の下で雨やみを待っていた。広い門の下には、この男のほかに誰もいない。ただ、所々丹塗の剥げた、大きな円柱に、蟋蟀が一匹とまっている。羅生門が、朱雀大路にある以上は、この男のほかにも、雨やみをする市女笠や揉烏帽子が、もう二三人はありそうなものである。それが、この男のほかには誰もいない。何故かと云うと、この二三年、京都には、地震とか辻風とか火事とか饑饉とか云う災がつづいて起った。そこで洛中のさびれ方は一通りではない。旧記によると、仏像や仏具を打砕いて、その丹がついたり、金銀の箔がついたりした木を、路ばたにつみ重ねて、薪の料に売っていたと云う事である。洛中がその始末であるから、羅生門の修理などは、元より誰も捨てて顧る者がなかった。するとその荒れ果てたのをよい事にして、狐狸が棲すむ。盗人が棲む。とうとうしまいには、引取り手のない死人を、この門へ持って来て、棄てて行くと云う習慣さえ出来た。そこで、日の目が見えなくなると、誰でも気味を悪るがって、この門の近所へは足ぶみをしない事になってしまったのである。その代りまた鴉がどこからか、たくさん集って来た。昼間見ると、その鴉が何羽となく輪を描いて、高い鴟尾のまわりを啼きながら、飛びまわっている。ことに門の上の空が、夕焼けであかくなる時には、それが胡麻をまいたようにはっきり見えた。鴉は、勿論、門の上にある死人の肉を、啄みに来るのである。――もっとも今日は、刻限が遅いせいか、一羽も見えない。ただ、所々、崩れかかった、そうしてその崩れ目に長い草のはえた石段の上に、鴉の糞が、点々と白くこびりついているのが見える。下人は七段ある石段の一番上の段に、洗いざらした紺の襖の尻を据えて、右の頬に出来た、大きな面皰を気にしながら、ぼんやり、雨のふるのを眺めていた`, "。")

func init() {
	rand.Seed(0)
}

func TestGenerate(t *testing.T) {
	ma, _ := markov.New(1, map[string]string{})
	defer ma.Destroy()
	for _, v := range sampleInput {
		ma.Add(v)
	}
	weight := NewMoraWeight([]MoraWeightCell{{1.0, 10.0}, {2.0, 20.0}, {3.0, 30.0}})
	rh := New(ma, &weight, 0.8, 6)

	ph := rh.Generate(3)
	expected := []string{"鴉の下人は七段", "洛中の上にある石段", "一通りでは七段"}
	for i, v := range ph {
		if len(ph) != 3 || v.OriginalForm() != expected[i] {
			t.Errorf("expected %v, but %v", expected, ph)
		}
	}
}

func TestGenerateFromKana(t *testing.T) {
	ma, _ := markov.New(1, map[string]string{})
	defer ma.Destroy()
	for _, v := range sampleInput {
		ma.Add(v)
	}
	weight := NewMoraWeight([]MoraWeightCell{{1.0, 10.0}, {2.0, 20.0}, {3.0, 30.0}})
	rh := New(ma, &weight, 0.8, 6)

	ph := rh.GenerateFromKana(3, "アンガイ")
	expected := []string{"その荒れ果てたと云う災", "ある日の暮方のまわり", "するとその丹がその代り"}
	for i, v := range ph {
		if len(ph) != 3 || v.OriginalForm() != expected[i] {
			t.Errorf("expected %v, but %v", expected, ph)
		}
	}
}

func TestStream(t *testing.T) {
	ma, _ := markov.New(1, map[string]string{})
	defer ma.Destroy()
	for _, v := range sampleInput {
		ma.Add(v)
	}
	weight := NewMoraWeight([]MoraWeightCell{{1.0, 10.0}, {2.0, 20.0}, {3.0, 30.0}})
	rh := New(ma, &weight, 0.8, 6)

	ch, kill := rh.Stream(3)
	defer func() { kill <- true }()

	expected := [][]string{
		{"下人は七段ある石段", "盗人が一人の段", "するとその鴉は七段"},
		{"下人は七段ある石段", "そこで洛中の襖の一番", "羅生門の下にある石段"},
	}

	for i := 0; i < 2; i++ {
		for j, v := range <-ch {
			if v.OriginalForm() != expected[i][j] {
				t.Errorf("expected %v, but %v", expected[i][j], v.OriginalForm())
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io"
	"sort"
	"strings"
)

type Identifier string

type Table struct {
	data  map[string][]Identifier
	nrows int
	ncols int
	// tables by field and mapped by the same field
	sorted map[string]*SortedTable
}

func NewTable(r io.Reader) *Table {
	t := new(Table)
	scanner := bufio.NewScanner(r)
	if scanner.Scan() {
		headers := strings.Split(scanner.Text(), fieldDelim)
		t.data = make(map[string][]Identifier)
		t.ncols = len(headers)
		m := 0
		for scanner.Scan() {
			tokens := strings.Split(scanner.Text(), fieldDelim)
			for i, h := range headers {
				t.data[h] = append(t.data[h], Identifier(tokens[i]))
			}
			m++
		}
		t.nrows = m
	}
	t.sorted = make(map[string]*SortedTable)
	return t
}

func (t *Table) Print() {
	for h, _ := range t.data {
		fmt.Printf("%s: ", h)
		for _, v := range t.data[h] {
			fmt.Printf("%s ", v)
		}
		fmt.Println()
	}
}

// Return a SortedTable sorted on field
func (t *Table) Sorted(field string) (s *SortedTable) {
	s = t.sorted[field]
	if s == nil {
		s = NewSortedTable(t, field)
		// cache SortedTable
		t.sorted[field] = s
	}
	return s
}

// SortedTable is sorted upon initialization and maintains the sorted order by
// its rows index
type SortedTable struct {
	table *Table
	rows  []int
	field string
}

func NewSortedTable(t *Table, field string) *SortedTable {
	rows := make([]int, t.nrows)
	for i, _ := range t.data[field] {
		rows[i] = i
	}
	s := &SortedTable{
		table: t,
		rows:  rows,
		field: field,
	}
	sort.Sort(s)

	return s
}

func (s *SortedTable) FieldExists(f string) bool {
	_, exists := s.table.data[f]
	return exists
}

func (s *SortedTable) At(i int, field string) Identifier {
	return s.table.data[field][s.rows[i]]
}

func (s *SortedTable) Slice(field string) []Identifier {
	unsorted := s.table.data[field]
	sorted := make([]Identifier, len(s.rows))
	for i, r := range s.rows {
		sorted[i] = unsorted[r]
	}
	return sorted
}

// x of xs must be in the field on which s has been sorted.
func (s *SortedTable) Map(xs []Identifier, dest string) []Identifier {
	y := make([]Identifier, len(xs))
	if s.FieldExists(dest) {
		srcColumn := s.Slice(s.field)
		destColumn := s.Slice(dest)
		for i, x := range xs {
			j := sort.Search(
				len(srcColumn),
				func(k int) bool {
					return srcColumn[k] >= x
				},
			)
			// find all matches
			var id Identifier
			for ; j < len(srcColumn) && srcColumn[j] == x; j++ {
				if len(destColumn[j]) > 0 {
					if len(id) > 0 {
						id += idDelim + destColumn[j]
					} else {
						id = destColumn[j]
					}
				}
			}
			y[i] = id
		}
	}
	return y
}

func (s *SortedTable) Len() int {
	return len(s.rows)
}

func (s *SortedTable) Swap(i, j int) {
	s.rows[i], s.rows[j] = s.rows[j], s.rows[i]
}

func (s *SortedTable) Less(i, j int) bool {
	return s.table.data[s.field][s.rows[i]] < s.table.data[s.field][s.rows[j]]
}
<commit_msg>Replaced Print() with String() for Table.<commit_after>package main

import (
	"bufio"
	"fmt"
	"io"
	"sort"
	"strings"
)

type Identifier string

type Table struct {
	data  map[string][]Identifier
	nrows int
	ncols int
	// tables by field and mapped by the same field
	sorted map[string]*SortedTable
}

func NewTable(r io.Reader) *Table {
	t := new(Table)
	scanner := bufio.NewScanner(r)
	if scanner.Scan() {
		headers := strings.Split(scanner.Text(), fieldDelim)
		t.data = make(map[string][]Identifier)
		t.ncols = len(headers)
		m := 0
		for scanner.Scan() {
			tokens := strings.Split(scanner.Text(), fieldDelim)
			for i, h := range headers {
				t.data[h] = append(t.data[h], Identifier(tokens[i]))
			}
			m++
		}
		t.nrows = m
	}
	t.sorted = make(map[string]*SortedTable)
	return t
}

func (t *Table) String() (s string) {
	for h, _ := range t.data {
		s += fmt.Sprintf("%s: ", h)
		for _, v := range t.data[h] {
			s += fmt.Sprintf("%s ", v)
		}
		s += fmt.Sprintln()
	}
	return
}

// Return a SortedTable sorted on field
func (t *Table) Sorted(field string) (s *SortedTable) {
	s = t.sorted[field]
	if s == nil {
		s = NewSortedTable(t, field)
		// cache SortedTable
		t.sorted[field] = s
	}
	return s
}

// SortedTable is sorted upon initialization and maintains the sorted order by
// its rows index
type SortedTable struct {
	table *Table
	rows  []int
	field string
}

func NewSortedTable(t *Table, field string) *SortedTable {
	rows := make([]int, t.nrows)
	for i, _ := range t.data[field] {
		rows[i] = i
	}
	s := &SortedTable{
		table: t,
		rows:  rows,
		field: field,
	}
	sort.Sort(s)

	return s
}

func (s *SortedTable) FieldExists(f string) bool {
	_, exists := s.table.data[f]
	return exists
}

func (s *SortedTable) At(i int, field string) Identifier {
	return s.table.data[field][s.rows[i]]
}

func (s *SortedTable) Slice(field string) []Identifier {
	unsorted := s.table.data[field]
	sorted := make([]Identifier, len(s.rows))
	for i, r := range s.rows {
		sorted[i] = unsorted[r]
	}
	return sorted
}

// x of xs must be in the field on which s has been sorted.
func (s *SortedTable) Map(xs []Identifier, dest string) []Identifier {
	y := make([]Identifier, len(xs))
	if s.FieldExists(dest) {
		srcColumn := s.Slice(s.field)
		destColumn := s.Slice(dest)
		for i, x := range xs {
			j := sort.Search(
				len(srcColumn),
				func(k int) bool {
					return srcColumn[k] >= x
				},
			)
			// find all matches
			var id Identifier
			for ; j < len(srcColumn) && srcColumn[j] == x; j++ {
				if len(destColumn[j]) > 0 {
					if len(id) > 0 {
						id += idDelim + destColumn[j]
					} else {
						id = destColumn[j]
					}
				}
			}
			y[i] = id
		}
	}
	return y
}

func (s *SortedTable) Len() int {
	return len(s.rows)
}

func (s *SortedTable) Swap(i, j int) {
	s.rows[i], s.rows[j] = s.rows[j], s.rows[i]
}

func (s *SortedTable) Less(i, j int) bool {
	return s.table.data[s.field][s.rows[i]] < s.table.data[s.field][s.rows[j]]
}
<|endoftext|>