<commit_before>// Package band provides band specific defaults and configuration for
// downlink communication with end-nodes.
package band

import (
	"errors"
	"fmt"
	"time"

	"github.com/brocaar/lorawan"
)

// Name defines the band-name type.
type Name string

// Available ISM bands.
const (
	AS_923     Name = "AS_923"
	AU_915_928 Name = "AU_915_928"
	CN_470_510 Name = "CN_470_510"
	CN_779_787 Name = "CN_779_787"
	EU_433     Name = "EU_433"
	EU_863_870 Name = "EU_863_870"
	KR_920_923 Name = "KR_920_923"
	RU_864_869 Name = "RU_864_869"
	US_902_928 Name = "US_902_928"
)

// Modulation defines the modulation type.
type Modulation string

// Possible modulation types.
const (
	LoRaModulation Modulation = "LORA"
	FSKModulation  Modulation = "FSK"
)

// DataRate defines a data rate
type DataRate struct {
	Modulation   Modulation `json:"modulation"`
	SpreadFactor int        `json:"spreadFactor,omitempty"` // used for LoRa
	Bandwidth    int        `json:"bandwidth,omitempty"`    // in kHz, used for LoRa
	BitRate      int        `json:"bitRate,omitempty"`      // bits per second, used for FSK
}

// MaxPayloadSize defines the max payload size
type MaxPayloadSize struct {
	M int // The maximum MACPayload size length
	N int // The maximum application payload length in the absence of the optional FOpt control field
}

// Channel defines the channel structure
type Channel struct {
	Frequency      int   // frequency in Hz
	DataRates      []int // each int mapping to an index in DataRateConfiguration
	userConfigured bool  // user-configured channel
	deactivated    bool  // used to deactivate on or multiple channels (e.g. for US ISM band)
}

// Band defines an region specific ISM band implementation for LoRa.
type Band struct {
	// dwellTime defines if dwell time limitation should be taken into account
	dwellTime lorawan.DwellTime

	// rx1DataRate defines the RX1 data-rate given the uplink data-rate
	// and a RX1DROffset value. These values are retrievable by using
	// the GetRX1DataRate method.
	rx1DataRate [][]int

	// DefaultTXPower defines the default radiated transmit output power
	DefaultTXPower int

	// ImplementsCFlist defines if the band implements the optional channel
	// frequency list.
	ImplementsCFlist bool

	// RX2Frequency defines the fixed frequency for the RX2 receive window
	RX2Frequency int

	// RX2DataRate defines the fixed data-rate for the RX2 receive window
	RX2DataRate int

	// MaxFcntGap defines the MAC_FCNT_GAP default value.
	MaxFCntGap uint32

	// ADRACKLimit defines the ADR_ACK_LIMIT default value.
	ADRACKLimit int

	// ADRACKDelay defines the ADR_ACK_DELAY default value.
	ADRACKDelay int

	// ReceiveDelay1 defines the RECEIVE_DELAY1 default value.
	ReceiveDelay1 time.Duration

	// ReceiveDelay2 defines the RECEIVE_DELAY2 default value.
	ReceiveDelay2 time.Duration

	// JoinAcceptDelay1 defines the JOIN_ACCEPT_DELAY1 default value.
	JoinAcceptDelay1 time.Duration

	// JoinAcceptDelay2 defines the JOIN_ACCEPT_DELAY2 default value.
	JoinAcceptDelay2 time.Duration

	// ACKTimeoutMin defines the ACK_TIMEOUT min. default value.
	ACKTimeoutMin time.Duration

	// ACKTimeoutMax defines the ACK_TIMEOUT max. default value.
	ACKTimeoutMax time.Duration

	// DataRates defines the available data rates.
	DataRates []DataRate

	// MaxPayloadSize defines the maximum payload size, per data-rate.
	MaxPayloadSize []MaxPayloadSize

	// TXPower defines the TX power configuration.
	TXPower []int

	// UplinkChannels defines the list of (default) configured uplink channels.
	UplinkChannels []Channel

	// DownlinkChannels defines the list of (default) configured downlink
	// channels.
	DownlinkChannels []Channel

	// getRX1ChannelFunc implements a function which returns the RX1 channel
	// based on the uplink / TX channel.
	getRX1ChannelFunc func(txChannel int) int

	// getRX1FrequencyFunc implements a function which returns the RX1 frequency
	// given the uplink frequency.
	getRX1FrequencyFunc func(band *Band, txFrequency int) (int, error)

	// getRX1DataRateFunc implements a function which returns the RX1 data-rate
	// given the uplink data-rate and data-rate offset.
	getRX1DataRateFunc func(band *Band, uplinkDR, rx1DROffset int) (int, error)
}

// GetRX1Channel returns the channel to use for RX1 given the channel used
// for uplink.
func (b *Band) GetRX1Channel(txChannel int) int {
	return b.getRX1ChannelFunc(txChannel)
}

// GetRX1Frequency returns the frequency to use for RX1 given the uplink
// frequency.
func (b *Band) GetRX1Frequency(txFrequency int) (int, error) {
	return b.getRX1FrequencyFunc(b, txFrequency)
}

// GetRX1DataRate returns the RX1 data-rate given the uplink data-rate and
// RX1 data-rate offset.
func (b *Band) GetRX1DataRate(uplinkDR, rx1DROffset int) (int, error) {
	// use the lookup table when no function has been defined
	if b.getRX1DataRateFunc == nil {
		if uplinkDR > len(b.rx1DataRate)-1 {
			return 0, errors.New("lorawan/band: invalid data-rate")
		}
		if rx1DROffset > len(b.rx1DataRate[uplinkDR])-1 {
			return 0, errors.New("lorawan/band: invalid data-rate offset")
		}
		return b.rx1DataRate[uplinkDR][rx1DROffset], nil
	}
	return b.getRX1DataRateFunc(b, uplinkDR, rx1DROffset)
}

// GetUplinkChannelNumber returns the channel number given a frequency.
func (b *Band) GetUplinkChannelNumber(frequency int) (int, error) {
	for chanNum, channel := range b.UplinkChannels {
		if frequency == channel.Frequency {
			return chanNum, nil
		}
	}

	return 0, fmt.Errorf("lorawan/band: unknown channel for frequency: %d", frequency)
}

// GetDataRate returns the index of the given DataRate.
func (b *Band) GetDataRate(dr DataRate) (int, error) {
	for i, d := range b.DataRates {
		if d == dr {
			return i, nil
		}
	}
	return 0, errors.New("lorawan/band: the given data-rate does not exist")
}

// AddChannel adds an extra (user-configured) channel to the channels.
// The DataRates wil be set to DR 0-5.
// Note: this is only allowed when the band supports a CFList.
func (b *Band) AddChannel(freq int) error {
	if !b.ImplementsCFlist {
		return errors.New("lorawan/band: band does not implement CFList")
	}

	c := Channel{
		Frequency:      freq,
		DataRates:      []int{0, 1, 2, 3, 4, 5},
		userConfigured: true,
	}

	b.UplinkChannels = append(b.UplinkChannels, c)
	b.DownlinkChannels = append(b.DownlinkChannels, c)

	return nil
}

// GetCFList returns the CFList used for OTAA activation, or returns nil if
// the band does not implement the CFList or when there are no extra channels.
// Note that this only returns the first 5 extra channels.
func (b *Band) GetCFList() *lorawan.CFList {
	if !b.ImplementsCFlist {
		return nil
	}

	var cFList lorawan.CFList
	var i int
	for _, c := range b.UplinkChannels {
		if c.userConfigured && i < len(cFList) {
			cFList[i] = uint32(c.Frequency)
			i++
		}
	}

	if cFList[0] == 0 {
		return nil
	}
	return &cFList
}

// DisableUplinkChannel disables the given uplink channel.
func (b *Band) DisableUplinkChannel(i int) error {
	if i > len(b.UplinkChannels)-1 {
		return ErrChannelDoesNotExist
	}

	b.UplinkChannels[i].deactivated = true
	return nil
}

// EnableUplinkChannel enables the given uplink channel.
func (b *Band) EnableUplinkChannel(i int) error {
	if i > len(b.UplinkChannels)-1 {
		return ErrChannelDoesNotExist
	}

	b.UplinkChannels[i].deactivated = false
	return nil
}

// GetEnabledUplinkChannels returns the enabled uplink channels.
func (b *Band) GetEnabledUplinkChannels() []int {
	var out []int
	for i, c := range b.UplinkChannels {
		if !c.deactivated {
			out = append(out, i)
		}
	}
	return out
}

// GetDisabledUplinkChannels returns the disabled uplink channels.
func (b *Band) GetDisabledUplinkChannels() []int {
	var out []int
	for i, c := range b.UplinkChannels {
		if c.deactivated {
			out = append(out, i)
		}
	}
	return out
}

// GetConfig returns the band configuration for the given band.
// Please refer to the LoRaWAN specification for more details about the effect
// of the repeater and dwell time arguments.
func GetConfig(name Name, repeaterCompatible bool, dt lorawan.DwellTime) (Band, error) {
	switch name {
	case AS_923:
		return newAS923Band(repeaterCompatible, dt)
	case AU_915_928:
		return newAU915Band(repeaterCompatible)
	case CN_470_510:
		return newCN470Band()
	case CN_779_787:
		return newCN779Band(repeaterCompatible)
	case EU_433:
		return newEU433Band(repeaterCompatible)
	case EU_863_870:
		return newEU863Band(repeaterCompatible)
	case KR_920_923:
		return newKR920Band()
	case RU_864_869:
		return newRU864Band(repeaterCompatible)
	case US_902_928:
		return newUS902Band(repeaterCompatible)
	default:
		return Band{}, fmt.Errorf("lorawan/band: band %s is undefined", name)
	}
}
<commit_msg>Disable channel when freq == 0.<commit_after>// Package band provides band specific defaults and configuration for
// downlink communication with end-nodes.
package band

import (
	"errors"
	"fmt"
	"time"

	"github.com/brocaar/lorawan"
)

// Name defines the band-name type.
type Name string

// Available ISM bands.
const (
	AS_923     Name = "AS_923"
	AU_915_928 Name = "AU_915_928"
	CN_470_510 Name = "CN_470_510"
	CN_779_787 Name = "CN_779_787"
	EU_433     Name = "EU_433"
	EU_863_870 Name = "EU_863_870"
	KR_920_923 Name = "KR_920_923"
	RU_864_869 Name = "RU_864_869"
	US_902_928 Name = "US_902_928"
)

// Modulation defines the modulation type.
type Modulation string

// Possible modulation types.
const (
	LoRaModulation Modulation = "LORA"
	FSKModulation  Modulation = "FSK"
)

// DataRate defines a data rate
type DataRate struct {
	Modulation   Modulation `json:"modulation"`
	SpreadFactor int        `json:"spreadFactor,omitempty"` // used for LoRa
	Bandwidth    int        `json:"bandwidth,omitempty"`    // in kHz, used for LoRa
	BitRate      int        `json:"bitRate,omitempty"`      // bits per second, used for FSK
}

// MaxPayloadSize defines the max payload size
type MaxPayloadSize struct {
	M int // The maximum MACPayload size length
	N int // The maximum application payload length in the absence of the optional FOpt control field
}

// Channel defines the channel structure
type Channel struct {
	Frequency      int   // frequency in Hz
	DataRates      []int // each int mapping to an index in DataRateConfiguration
	userConfigured bool  // user-configured channel
	deactivated    bool  // used to deactivate on or multiple channels (e.g. for US ISM band)
}

// Band defines an region specific ISM band implementation for LoRa.
type Band struct {
	// dwellTime defines if dwell time limitation should be taken into account
	dwellTime lorawan.DwellTime

	// rx1DataRate defines the RX1 data-rate given the uplink data-rate
	// and a RX1DROffset value. These values are retrievable by using
	// the GetRX1DataRate method.
	rx1DataRate [][]int

	// DefaultTXPower defines the default radiated transmit output power
	DefaultTXPower int

	// ImplementsCFlist defines if the band implements the optional channel
	// frequency list.
	ImplementsCFlist bool

	// RX2Frequency defines the fixed frequency for the RX2 receive window
	RX2Frequency int

	// RX2DataRate defines the fixed data-rate for the RX2 receive window
	RX2DataRate int

	// MaxFcntGap defines the MAC_FCNT_GAP default value.
	MaxFCntGap uint32

	// ADRACKLimit defines the ADR_ACK_LIMIT default value.
	ADRACKLimit int

	// ADRACKDelay defines the ADR_ACK_DELAY default value.
	ADRACKDelay int

	// ReceiveDelay1 defines the RECEIVE_DELAY1 default value.
	ReceiveDelay1 time.Duration

	// ReceiveDelay2 defines the RECEIVE_DELAY2 default value.
	ReceiveDelay2 time.Duration

	// JoinAcceptDelay1 defines the JOIN_ACCEPT_DELAY1 default value.
	JoinAcceptDelay1 time.Duration

	// JoinAcceptDelay2 defines the JOIN_ACCEPT_DELAY2 default value.
	JoinAcceptDelay2 time.Duration

	// ACKTimeoutMin defines the ACK_TIMEOUT min. default value.
	ACKTimeoutMin time.Duration

	// ACKTimeoutMax defines the ACK_TIMEOUT max. default value.
	ACKTimeoutMax time.Duration

	// DataRates defines the available data rates.
	DataRates []DataRate

	// MaxPayloadSize defines the maximum payload size, per data-rate.
	MaxPayloadSize []MaxPayloadSize

	// TXPower defines the TX power configuration.
	TXPower []int

	// UplinkChannels defines the list of (default) configured uplink channels.
	UplinkChannels []Channel

	// DownlinkChannels defines the list of (default) configured downlink
	// channels.
	DownlinkChannels []Channel

	// getRX1ChannelFunc implements a function which returns the RX1 channel
	// based on the uplink / TX channel.
	getRX1ChannelFunc func(txChannel int) int

	// getRX1FrequencyFunc implements a function which returns the RX1 frequency
	// given the uplink frequency.
	getRX1FrequencyFunc func(band *Band, txFrequency int) (int, error)

	// getRX1DataRateFunc implements a function which returns the RX1 data-rate
	// given the uplink data-rate and data-rate offset.
	getRX1DataRateFunc func(band *Band, uplinkDR, rx1DROffset int) (int, error)
}

// GetRX1Channel returns the channel to use for RX1 given the channel used
// for uplink.
func (b *Band) GetRX1Channel(txChannel int) int {
	return b.getRX1ChannelFunc(txChannel)
}

// GetRX1Frequency returns the frequency to use for RX1 given the uplink
// frequency.
func (b *Band) GetRX1Frequency(txFrequency int) (int, error) {
	return b.getRX1FrequencyFunc(b, txFrequency)
}

// GetRX1DataRate returns the RX1 data-rate given the uplink data-rate and
// RX1 data-rate offset.
func (b *Band) GetRX1DataRate(uplinkDR, rx1DROffset int) (int, error) {
	// use the lookup table when no function has been defined
	if b.getRX1DataRateFunc == nil {
		if uplinkDR > len(b.rx1DataRate)-1 {
			return 0, errors.New("lorawan/band: invalid data-rate")
		}
		if rx1DROffset > len(b.rx1DataRate[uplinkDR])-1 {
			return 0, errors.New("lorawan/band: invalid data-rate offset")
		}
		return b.rx1DataRate[uplinkDR][rx1DROffset], nil
	}
	return b.getRX1DataRateFunc(b, uplinkDR, rx1DROffset)
}

// GetUplinkChannelNumber returns the channel number given a frequency.
func (b *Band) GetUplinkChannelNumber(frequency int) (int, error) {
	for chanNum, channel := range b.UplinkChannels {
		if frequency == channel.Frequency {
			return chanNum, nil
		}
	}

	return 0, fmt.Errorf("lorawan/band: unknown channel for frequency: %d", frequency)
}

// GetDataRate returns the index of the given DataRate.
func (b *Band) GetDataRate(dr DataRate) (int, error) {
	for i, d := range b.DataRates {
		if d == dr {
			return i, nil
		}
	}
	return 0, errors.New("lorawan/band: the given data-rate does not exist")
}

// AddChannel adds an extra (user-configured) channel to the channels.
// The DataRates wil be set to DR 0-5.
// Note: this is only allowed when the band supports a CFList.
func (b *Band) AddChannel(freq int) error {
	if !b.ImplementsCFlist {
		return errors.New("lorawan/band: band does not implement CFList")
	}

	c := Channel{
		Frequency:      freq,
		DataRates:      []int{0, 1, 2, 3, 4, 5},
		userConfigured: true,
		deactivated:    freq == 0,
	}

	b.UplinkChannels = append(b.UplinkChannels, c)
	b.DownlinkChannels = append(b.DownlinkChannels, c)

	return nil
}

// GetCFList returns the CFList used for OTAA activation, or returns nil if
// the band does not implement the CFList or when there are no extra channels.
// Note that this only returns the first 5 extra channels.
func (b *Band) GetCFList() *lorawan.CFList {
	if !b.ImplementsCFlist {
		return nil
	}

	var cFList lorawan.CFList
	var i int
	for _, c := range b.UplinkChannels {
		if c.userConfigured && i < len(cFList) {
			cFList[i] = uint32(c.Frequency)
			i++
		}
	}

	if cFList[0] == 0 {
		return nil
	}
	return &cFList
}

// DisableUplinkChannel disables the given uplink channel.
func (b *Band) DisableUplinkChannel(i int) error {
	if i > len(b.UplinkChannels)-1 {
		return ErrChannelDoesNotExist
	}

	b.UplinkChannels[i].deactivated = true
	return nil
}

// EnableUplinkChannel enables the given uplink channel.
func (b *Band) EnableUplinkChannel(i int) error {
	if i > len(b.UplinkChannels)-1 {
		return ErrChannelDoesNotExist
	}

	b.UplinkChannels[i].deactivated = false
	return nil
}

// GetEnabledUplinkChannels returns the enabled uplink channels.
func (b *Band) GetEnabledUplinkChannels() []int {
	var out []int
	for i, c := range b.UplinkChannels {
		if !c.deactivated {
			out = append(out, i)
		}
	}
	return out
}

// GetDisabledUplinkChannels returns the disabled uplink channels.
func (b *Band) GetDisabledUplinkChannels() []int {
	var out []int
	for i, c := range b.UplinkChannels {
		if c.deactivated {
			out = append(out, i)
		}
	}
	return out
}

// GetConfig returns the band configuration for the given band.
// Please refer to the LoRaWAN specification for more details about the effect
// of the repeater and dwell time arguments.
func GetConfig(name Name, repeaterCompatible bool, dt lorawan.DwellTime) (Band, error) {
	switch name {
	case AS_923:
		return newAS923Band(repeaterCompatible, dt)
	case AU_915_928:
		return newAU915Band(repeaterCompatible)
	case CN_470_510:
		return newCN470Band()
	case CN_779_787:
		return newCN779Band(repeaterCompatible)
	case EU_433:
		return newEU433Band(repeaterCompatible)
	case EU_863_870:
		return newEU863Band(repeaterCompatible)
	case KR_920_923:
		return newKR920Band()
	case RU_864_869:
		return newRU864Band(repeaterCompatible)
	case US_902_928:
		return newUS902Band(repeaterCompatible)
	default:
		return Band{}, fmt.Errorf("lorawan/band: band %s is undefined", name)
	}
}
<|endoftext|>


<commit_before>package reform_test

import (
	"database/sql"
	"fmt"
	"log"
	"os"
	"testing"
	"time"

	_ "github.com/denisenkom/go-mssqldb"
	_ "github.com/go-sql-driver/mysql"
	_ "github.com/lib/pq"
	_ "github.com/mattn/go-sqlite3"

	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"

	"gopkg.in/reform.v1"
	"gopkg.in/reform.v1/dialects/mssql"
	"gopkg.in/reform.v1/dialects/mysql"
	"gopkg.in/reform.v1/dialects/postgresql"
	"gopkg.in/reform.v1/dialects/sqlite3"
	. "gopkg.in/reform.v1/internal/test/models"
)

var (
	DB *reform.DB
)

func TestMain(m *testing.M) {
	driver := os.Getenv("REFORM_DRIVER")
	source := os.Getenv("REFORM_TEST_SOURCE")
	log.Printf("driver = %q, source = %q", driver, source)
	if driver == "" || source == "" {
		log.Fatal("no driver or source, set REFORM_DRIVER and REFORM_TEST_SOURCE")
	}

	db, err := sql.Open(driver, source)
	if err != nil {
		log.Fatal(err)
	}

	// Use single connection so various session-related variables work.
	// For example: "PRAGMA foreign_keys" for SQLite3, "SET IDENTITY_INSERT" for MS SQL, etc.
	db.SetMaxIdleConns(1)
	db.SetMaxOpenConns(1)
	db.SetConnMaxLifetime(-1)

	err = db.Ping()
	if err != nil {
		log.Fatal(err)
	}

	// print useful information for debugging
	now := time.Now()
	log.Printf("time.Now()       = %s", now)
	log.Printf("time.Now().UTC() = %s", now.UTC())

	// select dialect for driver
	var dialect reform.Dialect
	switch driver {
	case "mysql":
		dialect = mysql.Dialect

		var mode, autocommit, tz string
		err = db.QueryRow("SELECT @@sql_mode, @@autocommit, @@time_zone").Scan(&mode, &autocommit, &tz)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("MySQL sql_mode   = %q", mode)
		log.Printf("MySQL autocommit = %q", autocommit)
		log.Printf("MySQL time_zone  = %q", tz)

	case "postgres":
		dialect = postgresql.Dialect

		var tz string
		err = db.QueryRow("SHOW TimeZone").Scan(&tz)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("PostgreSQL TimeZone = %q", tz)

	case "sqlite3":
		dialect = sqlite3.Dialect

		_, err = db.Exec("PRAGMA foreign_keys = ON")
		if err != nil {
			log.Fatal(err)
		}

	case "mssql":
		dialect = mssql.Dialect

		var options uint16
		err = db.QueryRow("SELECT @@OPTIONS").Scan(&options)
		if err != nil {
			log.Fatal(err)
		}
		xact := "ON"
		if options&0x4000 == 0 {
			xact = "OFF"
		}
		log.Printf("MS SQL OPTIONS = %#4x (XACT_ABORT %s)", options, xact)

	default:
		log.Fatal("reform: no dialect for driver " + driver)
	}

	DB = reform.NewDB(db, dialect, nil)

	os.Exit(m.Run())
}

// checkForeignKeys checks that foreign keys are still enforced for sqlite3.
func checkForeignKeys(t *testing.T, q *reform.Querier) {
	if q.Dialect != sqlite3.Dialect {
		return
	}

	var enabled bool
	err := q.QueryRow("PRAGMA foreign_keys").Scan(&enabled)
	require.NoError(t, err)
	require.True(t, enabled)
}

// setIdentityInsert allows or disallows insertions of rows with set primary keys for MS SQL.
func setIdentityInsert(t *testing.T, q *reform.Querier, table string, allow bool) {
	if q.Dialect != mssql.Dialect {
		return
	}

	allowString := "OFF"
	if allow {
		allowString = "ON"
	}
	sql := fmt.Sprintf("SET IDENTITY_INSERT %s %s", q.QuoteIdentifier(table), allowString)
	_, err := q.Exec(sql)
	require.NoError(t, err)
}

type ReformSuite struct {
	suite.Suite
	tx *reform.TX
	q  *reform.Querier
}

func TestReformSuite(t *testing.T) {
	suite.Run(t, new(ReformSuite))
}

func (s *ReformSuite) SetupTest() {
	pl := reform.NewPrintfLogger(s.T().Logf)
	pl.LogTypes = true
	DB.Logger = pl

	var err error
	s.tx, err = DB.Begin()
	s.Require().NoError(err)

	s.q = s.tx.WithTag("test")

	setIdentityInsert(s.T(), s.q, "people", false)
}

func (s *ReformSuite) TearDownTest() {
	// some transactional tests rollback and nilify q
	if s.q != nil {
		checkForeignKeys(s.T(), s.q)

		err := s.tx.Rollback()
		s.Require().NoError(err)
	}

	checkForeignKeys(s.T(), DB.Querier)
}

func (s *ReformSuite) RestartTransaction() {
	s.TearDownTest()
	s.SetupTest()
}

func (s *ReformSuite) TestStringer() {
	person, err := s.q.FindByPrimaryKeyFrom(PersonTable, 1)
	s.NoError(err)
	expected := "ID: 1 (int32), GroupID: 65534 (*int32), Name: `Denis Mills` (string), Email: <nil> (*string), CreatedAt: 2009-11-10 23:00:00 +0000 UTC (time.Time), UpdatedAt: <nil> (*time.Time)"
	s.Equal(expected, person.String())

	project, err := s.q.FindByPrimaryKeyFrom(ProjectTable, "baron")
	s.NoError(err)
	expected = "Name: `Vicious Baron` (string), ID: `baron` (string), Start: 2014-06-01 00:00:00 +0000 UTC (time.Time), End: 2016-02-21 00:00:00 +0000 UTC (*time.Time)"
	s.Equal(expected, project.String())
}

func (s *ReformSuite) TestNeverNil() {
	project := new(Project)

	for i, v := range project.Values() {
		if v == nil {
			s.Fail("Value is nil", "%s %#v", ProjectTable.Columns()[i], v)
		}
	}

	for i, v := range project.Pointers() {
		if v == nil {
			s.Fail("Pointer is nil", "%s %#v", ProjectTable.Columns()[i], v)
		}
	}

	v := project.PKValue()
	if v == nil {
		s.Fail("PKValue is nil")
	}

	v = project.PKPointer()
	if v == nil {
		s.Fail("PKPointer is nil")
	}
}

func (s *ReformSuite) TestHasPK() {
	person := new(Person)
	project := new(Project)
	s.False(person.HasPK())
	s.False(project.HasPK())

	person.ID = 1
	project.ID = "id"
	s.True(person.HasPK())
	s.True(project.HasPK())
}

func (s *ReformSuite) TestPlaceholders() {
	if s.q.Dialect != postgresql.Dialect {
		s.T().Skip("PostgreSQL-specific test")
	}

	s.Equal([]string{"$1", "$2", "$3", "$4", "$5"}, s.q.Placeholders(1, 5))
	s.Equal([]string{"$2", "$3", "$4", "$5", "$6"}, s.q.Placeholders(2, 5))
}

func (s *ReformSuite) TestTimezones() {
	setIdentityInsert(s.T(), s.q, "people", true)

	t1 := time.Now()
	t2 := t1.UTC()
	vlat, err := time.LoadLocation("Asia/Vladivostok")
	s.NoError(err)
	tVLAT := t1.In(vlat)
	hst, err := time.LoadLocation("US/Hawaii")
	s.NoError(err)
	tHST := t1.In(hst)

	{
		q := fmt.Sprintf(`INSERT INTO people (id, name, created_at) VALUES `+
			`(11, '11', %s), (12, '12', %s), (13, '13', %s), (14, '14', %s)`,
			s.q.Placeholder(1), s.q.Placeholder(2), s.q.Placeholder(3), s.q.Placeholder(4))
		_, err := s.q.Exec(q, t1, t2, tVLAT, tHST)
		s.NoError(err)

		q = `SELECT created_at, created_at FROM people WHERE id IN (11, 12, 13, 14) ORDER BY id`
		rows, err := s.q.Query(q)
		s.NoError(err)

		for _, t := range []time.Time{t1, t2, tVLAT, tHST} {
			var createdS string
			var createdT time.Time
			rows.Next()
			err = rows.Scan(&createdS, &createdT)
			s.NoError(err)
			log.Printf("%s read from database as %q and %s", t, createdS, createdT)
		}

		err = rows.Close()
		s.NoError(err)
	}

	{
		q := fmt.Sprintf(`INSERT INTO projects (id, name, start) VALUES `+
			`('11', '11', %s), ('12', '12', %s), ('13', '13', %s), ('14', '14', %s)`,
			s.q.Placeholder(1), s.q.Placeholder(2), s.q.Placeholder(3), s.q.Placeholder(4))
		_, err := s.q.Exec(q, t1, t2, tVLAT, tHST)
		s.NoError(err)

		q = `SELECT start, start FROM projects WHERE id IN ('11', '12', '13', '14') ORDER BY id`
		rows, err := s.q.Query(q)
		s.NoError(err)
		defer rows.Close()

		for _, t := range []time.Time{t1, t2, tVLAT, tHST} {
			var startS string
			var startT time.Time
			rows.Next()
			err = rows.Scan(&startS, &startT)
			s.NoError(err)
			log.Printf("%s read from database as %q and %s", t, startS, startT)
		}

		err = rows.Close()
		s.NoError(err)
	}
}

// database/sql.(*Rows).Columns() is not currently used, but may be useful in the future.
// Test is in place to track drivers supporting it.
func (s *ReformSuite) TestColumns() {
	rows, err := s.q.SelectRows(PersonTable, "WHERE name = "+s.q.Placeholder(1)+" ORDER BY id", "Elfrieda Abbott")
	s.NoError(err)
	s.Require().NotNil(rows)
	defer rows.Close()

	columns, err := rows.Columns()
	s.NoError(err)
	s.Equal(PersonTable.Columns(), columns)
}
<commit_msg>Log server version in tests.<commit_after>package reform_test

import (
	"database/sql"
	"fmt"
	"log"
	"os"
	"testing"
	"time"

	_ "github.com/denisenkom/go-mssqldb"
	_ "github.com/go-sql-driver/mysql"
	_ "github.com/lib/pq"
	_ "github.com/mattn/go-sqlite3"

	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"

	"gopkg.in/reform.v1"
	"gopkg.in/reform.v1/dialects/mssql"
	"gopkg.in/reform.v1/dialects/mysql"
	"gopkg.in/reform.v1/dialects/postgresql"
	"gopkg.in/reform.v1/dialects/sqlite3"
	. "gopkg.in/reform.v1/internal/test/models"
)

var (
	DB *reform.DB
)

func TestMain(m *testing.M) {
	driver := os.Getenv("REFORM_DRIVER")
	source := os.Getenv("REFORM_TEST_SOURCE")
	log.Printf("driver = %q, source = %q", driver, source)
	if driver == "" || source == "" {
		log.Fatal("no driver or source, set REFORM_DRIVER and REFORM_TEST_SOURCE")
	}

	db, err := sql.Open(driver, source)
	if err != nil {
		log.Fatal(err)
	}

	// Use single connection so various session-related variables work.
	// For example: "PRAGMA foreign_keys" for SQLite3, "SET IDENTITY_INSERT" for MS SQL, etc.
	db.SetMaxIdleConns(1)
	db.SetMaxOpenConns(1)
	db.SetConnMaxLifetime(-1)

	err = db.Ping()
	if err != nil {
		log.Fatal(err)
	}

	// print useful information for debugging
	now := time.Now()
	log.Printf("time.Now()       = %s", now)
	log.Printf("time.Now().UTC() = %s", now.UTC())

	// select dialect for driver
	var dialect reform.Dialect
	switch driver {
	case "mysql":
		dialect = mysql.Dialect

		var version, mode, autocommit, tz string
		err = db.QueryRow("SELECT @@version, @@sql_mode, @@autocommit, @@time_zone").Scan(&version, &mode, &autocommit, &tz)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("MySQL version    = %q", version)
		log.Printf("MySQL sql_mode   = %q", mode)
		log.Printf("MySQL autocommit = %q", autocommit)
		log.Printf("MySQL time_zone  = %q", tz)

	case "postgres":
		dialect = postgresql.Dialect

		var version, tz string
		err = db.QueryRow("SHOW server_version").Scan(&version)
		if err != nil {
			log.Fatal(err)
		}
		err = db.QueryRow("SHOW TimeZone").Scan(&tz)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("PostgreSQL version  = %q", version)
		log.Printf("PostgreSQL TimeZone = %q", tz)

	case "sqlite3":
		dialect = sqlite3.Dialect

		var version, source string
		err = db.QueryRow("SELECT sqlite_version(), sqlite_source_id()").Scan(&version, &source)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("SQLite3 version = %q", version)
		log.Printf("SQLite3 source  = %q", source)

		_, err = db.Exec("PRAGMA foreign_keys = ON")
		if err != nil {
			log.Fatal(err)
		}

	case "mssql":
		dialect = mssql.Dialect

		var version string
		var options uint16
		err = db.QueryRow("SELECT @@VERSION, @@OPTIONS").Scan(&version, &options)
		if err != nil {
			log.Fatal(err)
		}
		xact := "ON"
		if options&0x4000 == 0 {
			xact = "OFF"
		}
		log.Printf("MS SQL version = %s", version)
		log.Printf("MS SQL OPTIONS = %#4x (XACT_ABORT %s)", options, xact)

	default:
		log.Fatal("reform: no dialect for driver " + driver)
	}

	DB = reform.NewDB(db, dialect, nil)

	os.Exit(m.Run())
}

// checkForeignKeys checks that foreign keys are still enforced for sqlite3.
func checkForeignKeys(t *testing.T, q *reform.Querier) {
	if q.Dialect != sqlite3.Dialect {
		return
	}

	var enabled bool
	err := q.QueryRow("PRAGMA foreign_keys").Scan(&enabled)
	require.NoError(t, err)
	require.True(t, enabled)
}

// setIdentityInsert allows or disallows insertions of rows with set primary keys for MS SQL.
func setIdentityInsert(t *testing.T, q *reform.Querier, table string, allow bool) {
	if q.Dialect != mssql.Dialect {
		return
	}

	allowString := "OFF"
	if allow {
		allowString = "ON"
	}
	sql := fmt.Sprintf("SET IDENTITY_INSERT %s %s", q.QuoteIdentifier(table), allowString)
	_, err := q.Exec(sql)
	require.NoError(t, err)
}

type ReformSuite struct {
	suite.Suite
	tx *reform.TX
	q  *reform.Querier
}

func TestReformSuite(t *testing.T) {
	suite.Run(t, new(ReformSuite))
}

func (s *ReformSuite) SetupTest() {
	pl := reform.NewPrintfLogger(s.T().Logf)
	pl.LogTypes = true
	DB.Logger = pl

	var err error
	s.tx, err = DB.Begin()
	s.Require().NoError(err)

	s.q = s.tx.WithTag("test")

	setIdentityInsert(s.T(), s.q, "people", false)
}

func (s *ReformSuite) TearDownTest() {
	// some transactional tests rollback and nilify q
	if s.q != nil {
		checkForeignKeys(s.T(), s.q)

		err := s.tx.Rollback()
		s.Require().NoError(err)
	}

	checkForeignKeys(s.T(), DB.Querier)
}

func (s *ReformSuite) RestartTransaction() {
	s.TearDownTest()
	s.SetupTest()
}

func (s *ReformSuite) TestStringer() {
	person, err := s.q.FindByPrimaryKeyFrom(PersonTable, 1)
	s.NoError(err)
	expected := "ID: 1 (int32), GroupID: 65534 (*int32), Name: `Denis Mills` (string), Email: <nil> (*string), CreatedAt: 2009-11-10 23:00:00 +0000 UTC (time.Time), UpdatedAt: <nil> (*time.Time)"
	s.Equal(expected, person.String())

	project, err := s.q.FindByPrimaryKeyFrom(ProjectTable, "baron")
	s.NoError(err)
	expected = "Name: `Vicious Baron` (string), ID: `baron` (string), Start: 2014-06-01 00:00:00 +0000 UTC (time.Time), End: 2016-02-21 00:00:00 +0000 UTC (*time.Time)"
	s.Equal(expected, project.String())
}

func (s *ReformSuite) TestNeverNil() {
	project := new(Project)

	for i, v := range project.Values() {
		if v == nil {
			s.Fail("Value is nil", "%s %#v", ProjectTable.Columns()[i], v)
		}
	}

	for i, v := range project.Pointers() {
		if v == nil {
			s.Fail("Pointer is nil", "%s %#v", ProjectTable.Columns()[i], v)
		}
	}

	v := project.PKValue()
	if v == nil {
		s.Fail("PKValue is nil")
	}

	v = project.PKPointer()
	if v == nil {
		s.Fail("PKPointer is nil")
	}
}

func (s *ReformSuite) TestHasPK() {
	person := new(Person)
	project := new(Project)
	s.False(person.HasPK())
	s.False(project.HasPK())

	person.ID = 1
	project.ID = "id"
	s.True(person.HasPK())
	s.True(project.HasPK())
}

func (s *ReformSuite) TestPlaceholders() {
	if s.q.Dialect != postgresql.Dialect {
		s.T().Skip("PostgreSQL-specific test")
	}

	s.Equal([]string{"$1", "$2", "$3", "$4", "$5"}, s.q.Placeholders(1, 5))
	s.Equal([]string{"$2", "$3", "$4", "$5", "$6"}, s.q.Placeholders(2, 5))
}

func (s *ReformSuite) TestTimezones() {
	setIdentityInsert(s.T(), s.q, "people", true)

	t1 := time.Now()
	t2 := t1.UTC()
	vlat, err := time.LoadLocation("Asia/Vladivostok")
	s.NoError(err)
	tVLAT := t1.In(vlat)
	hst, err := time.LoadLocation("US/Hawaii")
	s.NoError(err)
	tHST := t1.In(hst)

	{
		q := fmt.Sprintf(`INSERT INTO people (id, name, created_at) VALUES `+
			`(11, '11', %s), (12, '12', %s), (13, '13', %s), (14, '14', %s)`,
			s.q.Placeholder(1), s.q.Placeholder(2), s.q.Placeholder(3), s.q.Placeholder(4))
		_, err := s.q.Exec(q, t1, t2, tVLAT, tHST)
		s.NoError(err)

		q = `SELECT created_at, created_at FROM people WHERE id IN (11, 12, 13, 14) ORDER BY id`
		rows, err := s.q.Query(q)
		s.NoError(err)

		for _, t := range []time.Time{t1, t2, tVLAT, tHST} {
			var createdS string
			var createdT time.Time
			rows.Next()
			err = rows.Scan(&createdS, &createdT)
			s.NoError(err)
			log.Printf("%s read from database as %q and %s", t, createdS, createdT)
		}

		err = rows.Close()
		s.NoError(err)
	}

	{
		q := fmt.Sprintf(`INSERT INTO projects (id, name, start) VALUES `+
			`('11', '11', %s), ('12', '12', %s), ('13', '13', %s), ('14', '14', %s)`,
			s.q.Placeholder(1), s.q.Placeholder(2), s.q.Placeholder(3), s.q.Placeholder(4))
		_, err := s.q.Exec(q, t1, t2, tVLAT, tHST)
		s.NoError(err)

		q = `SELECT start, start FROM projects WHERE id IN ('11', '12', '13', '14') ORDER BY id`
		rows, err := s.q.Query(q)
		s.NoError(err)
		defer rows.Close()

		for _, t := range []time.Time{t1, t2, tVLAT, tHST} {
			var startS string
			var startT time.Time
			rows.Next()
			err = rows.Scan(&startS, &startT)
			s.NoError(err)
			log.Printf("%s read from database as %q and %s", t, startS, startT)
		}

		err = rows.Close()
		s.NoError(err)
	}
}

// database/sql.(*Rows).Columns() is not currently used, but may be useful in the future.
// Test is in place to track drivers supporting it.
func (s *ReformSuite) TestColumns() {
	rows, err := s.q.SelectRows(PersonTable, "WHERE name = "+s.q.Placeholder(1)+" ORDER BY id", "Elfrieda Abbott")
	s.NoError(err)
	s.Require().NotNil(rows)
	defer rows.Close()

	columns, err := rows.Columns()
	s.NoError(err)
	s.Equal(PersonTable.Columns(), columns)
}
<|endoftext|>


<commit_before>package main

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

// See https://confluence.atlassian.com/display/BITBUCKET/POST+hook+management
type BitbucketNotification struct {
	Canon_url  string
	User       string
	Repository BitbucketRepository
	Commits    []BitbucketCommit
}

type BitbucketRepository struct {
	Absolute_url string
	Fork         bool
	Is_private   bool
	Name         string
	Owner        string
	Scm          string
	Slug         string
	Website      string
}

type BitbucketCommit struct {
	Author       string
	Branches     []string
	Branch       string
	Files        []BitbucketFile
	Message      string
	Node         string
	Parents      []string
	Raw_author   string
	Raw_node     string
	Revision     int
	Size         int
	Timestamp    string
	Utctimestamp string
}

type BitbucketFile struct {
	File string
	Type string
}

func BitbucketParse(r *http.Request) (n Notification, err error) {
	r.ParseForm()
	payload := r.Form.Get("payload")

	log.Printf("Received payload %s", payload)

	bytes := []byte(payload)

	return parseBytes(bytes)
}

func parseBytes(bytes []byte) (n BitbucketNotification, err error) {
	err = json.Unmarshal(bytes, &n)
	return
}

func (n BitbucketNotification) RepositoryUrl() (repositoryUrl string) {
	repositoryUrl = n.Canon_url + n.Repository.Absolute_url
	if repositoryUrl[len(repositoryUrl)-1] == '/' {
		repositoryUrl = repositoryUrl[:len(repositoryUrl)-1]
	}
	return
}

func (n BitbucketNotification) Branches() (branches map[string]bool) {
	branches = make(map[string]bool)
	for _, commit := range n.Commits {
		if commit.Branch != "" {
			branches[commit.Branch] = true
		}
		for _, branch := range commit.Branches {
			branches[branch] = true
		}
	}
	return
}
<commit_msg>Add undocumented Truncted property to BitbucketNotification<commit_after>package main

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

// See https://confluence.atlassian.com/display/BITBUCKET/POST+hook+management
type BitbucketNotification struct {
	Canon_url  string
	User       string
	Repository BitbucketRepository
	Truncated  bool
	Commits    []BitbucketCommit
}

type BitbucketRepository struct {
	Absolute_url string
	Fork         bool
	Is_private   bool
	Name         string
	Owner        string
	Scm          string
	Slug         string
	Website      string
}

type BitbucketCommit struct {
	Author       string
	Branches     []string
	Branch       string
	Files        []BitbucketFile
	Message      string
	Node         string
	Parents      []string
	Raw_author   string
	Raw_node     string
	Revision     int
	Size         int
	Timestamp    string
	Utctimestamp string
}

type BitbucketFile struct {
	File string
	Type string
}

func BitbucketParse(r *http.Request) (n Notification, err error) {
	r.ParseForm()
	payload := r.Form.Get("payload")

	log.Printf("Received payload %s", payload)

	bytes := []byte(payload)

	return parseBytes(bytes)
}

func parseBytes(bytes []byte) (n BitbucketNotification, err error) {
	err = json.Unmarshal(bytes, &n)
	return
}

func (n BitbucketNotification) RepositoryUrl() (repositoryUrl string) {
	repositoryUrl = n.Canon_url + n.Repository.Absolute_url
	if repositoryUrl[len(repositoryUrl)-1] == '/' {
		repositoryUrl = repositoryUrl[:len(repositoryUrl)-1]
	}
	return
}

func (n BitbucketNotification) Branches() (branches map[string]bool) {
	branches = make(map[string]bool)
	for _, commit := range n.Commits {
		if commit.Branch != "" {
			branches[commit.Branch] = true
		}
		for _, branch := range commit.Branches {
			branches[branch] = true
		}
	}
	return
}
<|endoftext|>


<commit_before>//可以参考python flask的blueprint功能,大致的功能是这样的
// backend := &Blueprint{BasePath:"/backend", Name:"backend"}
// backend.Before(HandlerFunc)  这里可以做验证，log等等操作
// backend.Get("/home", HandlerFunc) Url处理
// backend.After(HandlerFunc)   之后的操作????
// globalApp.register(backend)
// globalApp.Run(":8080")

package illusion

import "regexp"

//Blueprint应该实现的接口
//供参考
type BluePrinter interface {
	//进入handler之前的操作
	Before(HandlerFunc)

	//进入handler之后的操作,?
	After(HandlerFunc)

	//返回自身，构成链式调用
	Handle(string, string, HandlerFunc) BluePrinter

	//匹配任意的http方法
	Any(string, HandlerFunc) BluePrinter

	//如下为标准的http方法
	Get(string, HandlerFunc) BluePrinter
	Post(string, HandlerFunc) BluePrinter
	Delete(string, HandlerFunc) BluePrinter
	Patch(string, HandlerFunc) BluePrinter
	Put(string, HandlerFunc) BluePrinter
	Options(string, HandlerFunc) BluePrinter
	Head(string, HandlerFunc) BluePrinter

	//这怎么用呢？
	//等我了解一下
	StaticFile(string, string) BluePrinter
	Static(string, string) BluePrinter
	StaticFs(string, string) BluePrinter
}

type Blueprint struct {
	//对应此Blueprint的基础路径
	BasePath string

	//名称
	//名称应该唯一
	Name string

	//处理链, 可以一次性存储，
	//毕竟，你不会放太多的函数在调用链中,right? :)
	//Handlers HandlerChain

	//handler之前的处理链
	BeforeChain HandlerChain

	//handler之后的处理链
	AfterChain HandlerChain

	//错误信息
	Err error

	//必须要持有的核心路由
	//不显示持有也可以，但不明显，最好持有
	//核心路由在全局是个单例
	illusion *Illusion
}

//将handler合并进一个数组中
//func (it *Blueprint)extendBeforeChain(handler HandlerFunc){
//	return
//}

//func (it *Blueprint)extendAfterChain(Handler HandlerFunc){
///return
//}

//结合beforeChain + Handler + afterChain形成一个调用链
func (it *Blueprint) fullChain(handler HandlerFunc) HandlerChain {
	return nil
}

//结合blueprint.BasePath + relativePath形成绝对Url
func (it *Blueprint) TruePath(relativePath string) string {
	return CleanPath(it.BasePath + "/" + relativePath)
}

//如下是Blueprint需要实现的接口
//func NewBlueprint() *Blueprint {
//	return Blueprint("/", "home")
//}

func Blueprint(path, name string) *Blueprint {
	return &Blueprint{
		BasePath:    path,
		Name:        name,
		BeforeChain: make(HandlerChain, 0, 5), //最大的beforeChain个数
		AfterChain:  make(HandlerChain, 0, 5), //最大的AfterChain个数
		Err:         nil,
		illusion:    globalIllusion(),
	}
}

func (it *Blueprint) Before(handler HandlerFunc) BluePrinter {
	it.BeforeChain = append(it.BeforeChain, handler)
	return it
}

func (it *Blueprint) After(handler HandlerFunc) BluePrinter {
	it.AfterChain = append(it.AfterChain, handler)
	return it
}

func (it *Blueprint) Handle(httpMethod, basePath string, handler HandlerFunc) BluePrinter {
	if matches, err := regexp.MatchString("^[A-Z]+$", httpMethod); !matches || err != nil {
		it.Err = err
		panic("http method " + httpMethod + " is not valid")
	}
	return
}
<commit_msg>构想illusion怎么写呢<commit_after>//可以参考python flask的blueprint功能,大致的功能是这样的
// backend := &Blueprint{BasePath:"/backend", Name:"backend"}
// backend.Before(HandlerFunc)  这里可以做验证，log等等操作
// backend.Get("/home", HandlerFunc) Url处理
// backend.After(HandlerFunc)   之后的操作????
// globalApp.register(backend)
// globalApp.Run(":8080")

package illusion

import "regexp"

//Blueprint应该实现的接口
//供参考
type BluePrinter interface {
	//进入handler之前的操作
	Before(HandlerFunc)

	//进入handler之后的操作,?
	After(HandlerFunc)

	//返回自身，构成链式调用
	Handle(string, string, HandlerFunc) BluePrinter

	//匹配任意的http方法
	Any(string, HandlerFunc) BluePrinter

	//如下为标准的http方法
	Get(string, HandlerFunc) BluePrinter
	Post(string, HandlerFunc) BluePrinter
	Delete(string, HandlerFunc) BluePrinter
	Patch(string, HandlerFunc) BluePrinter
	Put(string, HandlerFunc) BluePrinter
	Options(string, HandlerFunc) BluePrinter
	Head(string, HandlerFunc) BluePrinter

	//这怎么用呢？
	//等我了解一下
	StaticFile(string, string) BluePrinter
	Static(string, string) BluePrinter
	StaticFs(string, string) BluePrinter
}

type Blueprint struct {
	//对应此Blueprint的基础路径
	BasePath string

	//名称
	//名称应该唯一
	Name string

	//处理链, 可以一次性存储，
	//毕竟，你不会放太多的函数在调用链中,right? :)
	//Handlers HandlerChain

	//handler之前的处理链
	BeforeChain HandlerChain

	//handler之后的处理链
	AfterChain HandlerChain

	//错误信息
	Err error

	//必须要持有的核心路由
	//不显示持有也可以，但不明显，最好持有
	//核心路由在全局是个单例
	illusion *Illusion
}

//将handler合并进一个数组中
//func (it *Blueprint)extendBeforeChain(handler HandlerFunc){
//	return
//}

//func (it *Blueprint)extendAfterChain(Handler HandlerFunc){
///return
//}

//如下是Blueprint需要实现的接口
//func NewBlueprint() *Blueprint {
//	return Blueprint("/", "home")
//}

func Blueprint(path, name string) *Blueprint {
	return &Blueprint{
		BasePath:    path,
		Name:        name,
		BeforeChain: make(HandlerChain, 0, 5), //最大的beforeChain个数
		AfterChain:  make(HandlerChain, 0, 5), //最大的AfterChain个数
		Err:         nil,
		illusion:    globalIllusion(),
	}
}

//结合beforeChain + Handler + afterChain形成一个调用链
func (it *Blueprint) fullChain(handler HandlerFunc) HandlerChain {
	return nil
}

//结合blueprint.BasePath + relativePath形成绝对Url
func (it *Blueprint) TruePath(relativePath string) string {
	return CleanPath(it.BasePath + "/" + relativePath)
}

func (it *Blueprint) Before(handler HandlerFunc) BluePrinter {
	it.BeforeChain = append(it.BeforeChain, handler)
	return it
}

func (it *Blueprint) After(handler HandlerFunc) BluePrinter {
	it.AfterChain = append(it.AfterChain, handler)
	return it
}

func (it *Blueprint) Handle(httpMethod, relativePath string, handler HandlerFunc) BluePrinter {
	if matches, err := regexp.MatchString("^[A-Z]+$", httpMethod); !matches || err != nil {
		it.Err = err
		panic("http method " + httpMethod + " is not valid")
	}
	handlers := it.fullChain(handler)
	finalPath := it.TruePath(relativePath)
	//这是illusion所缺少的啦
	it.illusion.addRoute(httpMethod, finalPath, handlers)
	return it
}

<|endoftext|>


<commit_before>// Package bulk has functions to load and save image metadata to a CSV file as
// needed by the 'Bulk Add CSV' Shimmie2 extension. The CSV file is assumed to
// have the following format:
//
//  "/path/to/image.jpg","spaced tags","source","rating s/q/e","/path/thumbnail.jpg"
//
// The last record (thumbnail) is left empty as thumbnails can easily be
// generated by the server.
//
// The package assumes that all images and the CSV file are under a certain
// directory path that is used as input in many package functions.
package bulk

import (
	"encoding/csv"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

// Image holds the metadata of each image from the CSV file.
type Image struct {
	ID     int
	Name   string
	Tags   []string
	Source string
	Rating string
}

var supportedExt = []string{"gif", "jpeg", "jpg", "png", "swf"}

func isSupportedType(name string) bool {
	fname := strings.ToLower(name)
	for _, ext := range supportedExt {
		// The only possible returned error is ErrBadPattern, when pattern is
		// malformed. Patterns like *.jpg are never malformed so we ignore the
		// error.
		matches, _ := filepath.Match("*."+ext, fname)
		if matches {
			return true
		}
	}
	return false
}

// LoadImages expects a slice of directory entries (os.FileInfo) which is the
// result of a read directory like ioutil.ReadDir. It loops through the slice,
// ignoring any directory and keeps only the files with one of the following
// extensions: "gif", "jpeg", "jpg", "png", "swf"
//
// It returns a slice of images without metadata, using the filename as Name
// and the order the files were found as an increasing ID starting from 0.
//
// In case of a CSV file, the image metadata should be read using LoadCSV and
// then combined with the images (discovered by LoadImages) using Combine.
func LoadImages(files []os.FileInfo) []Image {
	images := []Image{}

	id := 0
	for _, f := range files {
		if !f.IsDir() {
			if isSupportedType(f.Name()) {
				img := Image{ID: id, Name: f.Name()}
				images = append(images, img)
				id++
			}
		}
	}
	return images
}

// LoadCSV loads the image metadata from a CSV file that is open for reading.
// The metadata are returned as slice of images and should be combined with the
// slice of images discovered by LoadImages by calling Combine.
func LoadCSV(file io.Reader) ([]Image, error) {
	images := []Image{}

	r := csv.NewReader(file)
	for {
		record, err := r.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}

		if len(record) != 5 {
			return nil, fmt.Errorf("invalid csv file format")
		}
		// Image filepath (first column) should exist otherwise we cannot match
		// the metadata with the images found under the directory.
		if record[0] != "" {
			img := Image{
				Name:   filepath.Base(record[0]),
				Tags:   strings.Split(record[1], " "),
				Source: record[2],
				Rating: record[3],
			}
			images = append(images, img)
		}
	}
	return images, nil
}

// Combine takes the metadata of imagesWithInfo and copies them to images
// returning the combined result.
func Combine(images, imagesWithInfo []Image) []Image {
	for _, info := range imagesWithInfo {
		if info.Name == "" {
			continue
		}
		img := findByName(images, info.Name)
		if img != nil {
			img.Source = info.Source
			img.Rating = info.Rating
			img.Tags = info.Tags
		}
	}
	return images
}

type byName []Image

func (img byName) Len() int           { return len(img) }
func (img byName) Swap(i, j int)      { img[i], img[j] = img[j], img[i] }
func (img byName) Less(i, j int) bool { return img[i].Name < img[j].Name }

func findByName(image []Image, name string) *Image {
	sort.Sort(byName(image))
	i := sort.Search(len(image), func(i int) bool { return image[i].Name >= name })
	if i < len(image) && image[i].Name == name {
		return &image[i]
	}
	return nil
}

type byID []Image

func (img byID) Len() int           { return len(img) }
func (img byID) Swap(i, j int)      { img[i], img[j] = img[j], img[i] }
func (img byID) Less(i, j int) bool { return img[i].ID < img[j].ID }

// FindByID takes a slice of images, sorts them by ID and then finds then one
// with ID id.
func FindByID(image []Image, id int) *Image {
	sort.Sort(byID(image))
	i := sort.Search(len(image), func(i int) bool { return image[i].ID >= id })
	if i < len(image) && image[i].ID == id {
		return &image[i]
	}
	return nil
}

// CurrentPrefix reads from an open CSV file, reads the first line and uses the
// base directory of the provided workingDir path to find the path prefix used
// in the CSV file.
//
// As an example if the provided workingDir path is '/localpath/pics' and the
// first line has '/serverpath/pics/pic1' then the returned current prefix will
// be '/serverpath'.
func CurrentPrefix(workingDir string, file io.Reader) (string, error) {
	r := csv.NewReader(file)
	firstLine, err := r.Read()
	if err == io.EOF {
		return "", nil
	}
	if err != nil {
		return "", err
	}

	serverDir := firstLine[0]
	picFolder := filepath.Base(workingDir)
	sep := fmt.Sprintf("%c", filepath.Separator)
	if !strings.Contains(serverDir, picFolder) {
		return sep, nil
	}
	for {
		if filepath.Base(serverDir) == picFolder || serverDir == "" || serverDir == "." {
			break
		} else {
			serverDir, _ = filepath.Split(serverDir)
			serverDir = filepath.Dir(serverDir)
		}
	}
	return filepath.Dir(serverDir), nil
}

func sortTags(tags []string) []string {
	seen := make(map[string]struct{}, len(tags))
	sort.Strings(tags)
	var (
		all       = make([]string, 0)
		series    = make([]string, 0)
		character = make([]string, 0)
		artist    = make([]string, 0)
		tk        = make([]string, 0)
		normal    = make([]string, 0)
	)
	for _, t := range tags {
		if _, ok := seen[t]; ok {
			continue
		}
		seen[t] = struct{}{}
		switch {
		case strings.HasPrefix(t, "series:"):
			series = append(series, t)
		case strings.HasPrefix(t, "character:"):
			character = append(character, t)
		case strings.HasPrefix(t, "artist:"):
			artist = append(artist, t)
		case strings.HasPrefix(t, "tk:"):
			tk = append(tk, t)
		default:
			normal = append(normal, t)
		}
	}
	all = append(all, series...)
	all = append(all, character...)
	all = append(all, artist...)
	all = append(all, tk...)
	all = append(all, normal...)
	return all
}

// Save will write the image metadata to an open for writing file. It will
// keep the base of the dir path and replace the prefix with the provided one.
func Save(file io.Writer, images []Image, dir, prefix string, useLinuxSep bool) error {
	// Sort each image's tags.
	sorted := make([]Image, 0, len(images))
	for _, img := range images {
		img.Tags = sortTags(img.Tags)
		sorted = append(sorted, img)
	}
	// Save to file.
	w := csv.NewWriter(file)
	w.WriteAll(toRecords(sorted, dir, prefix, useLinuxSep))

	if err := w.Error(); err != nil {
		return fmt.Errorf("error writing csv: %v", err)
	}
	return nil
}

func toRecords(images []Image, dir, prefix string, useLinuxSep bool) [][]string {
	var records [][]string
	for _, img := range images {
		record := toRecord(img, dir, prefix, useLinuxSep)
		records = append(records, record)
	}
	return records
}

func toRecord(img Image, dir, prefix string, useLinuxSep bool) []string {
	var record []string
	p := filepath.Join(prefix, filepath.Base(dir), img.Name)
	if useLinuxSep {
		p = strings.Replace(p, "\\", "/", -1)
	}
	record = append(record, p)
	record = append(record, strings.Join(img.Tags, " "))
	record = append(record, img.Source)
	record = append(record, img.Rating)
	record = append(record, "")
	return record
}
<commit_msg>Use filepath.ToSlash instead of manual Replace<commit_after>// Package bulk has functions to load and save image metadata to a CSV file as
// needed by the 'Bulk Add CSV' Shimmie2 extension. The CSV file is assumed to
// have the following format:
//
//  "/path/to/image.jpg","spaced tags","source","rating s/q/e","/path/thumbnail.jpg"
//
// The last record (thumbnail) is left empty as thumbnails can easily be
// generated by the server.
//
// The package assumes that all images and the CSV file are under a certain
// directory path that is used as input in many package functions.
package bulk

import (
	"encoding/csv"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

// Image holds the metadata of each image from the CSV file.
type Image struct {
	ID     int
	Name   string
	Tags   []string
	Source string
	Rating string
}

var supportedExt = []string{"gif", "jpeg", "jpg", "png", "swf"}

func isSupportedType(name string) bool {
	fname := strings.ToLower(name)
	for _, ext := range supportedExt {
		// The only possible returned error is ErrBadPattern, when pattern is
		// malformed. Patterns like *.jpg are never malformed so we ignore the
		// error.
		matches, _ := filepath.Match("*."+ext, fname)
		if matches {
			return true
		}
	}
	return false
}

// LoadImages expects a slice of directory entries (os.FileInfo) which is the
// result of a read directory like ioutil.ReadDir. It loops through the slice,
// ignoring any directory and keeps only the files with one of the following
// extensions: "gif", "jpeg", "jpg", "png", "swf"
//
// It returns a slice of images without metadata, using the filename as Name
// and the order the files were found as an increasing ID starting from 0.
//
// In case of a CSV file, the image metadata should be read using LoadCSV and
// then combined with the images (discovered by LoadImages) using Combine.
func LoadImages(files []os.FileInfo) []Image {
	images := []Image{}

	id := 0
	for _, f := range files {
		if !f.IsDir() {
			if isSupportedType(f.Name()) {
				img := Image{ID: id, Name: f.Name()}
				images = append(images, img)
				id++
			}
		}
	}
	return images
}

// LoadCSV loads the image metadata from a CSV file that is open for reading.
// The metadata are returned as slice of images and should be combined with the
// slice of images discovered by LoadImages by calling Combine.
func LoadCSV(file io.Reader) ([]Image, error) {
	images := []Image{}

	r := csv.NewReader(file)
	for {
		record, err := r.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}

		if len(record) != 5 {
			return nil, fmt.Errorf("invalid csv file format")
		}
		// Image filepath (first column) should exist otherwise we cannot match
		// the metadata with the images found under the directory.
		if record[0] != "" {
			img := Image{
				Name:   filepath.Base(record[0]),
				Tags:   strings.Split(record[1], " "),
				Source: record[2],
				Rating: record[3],
			}
			images = append(images, img)
		}
	}
	return images, nil
}

// Combine takes the metadata of imagesWithInfo and copies them to images
// returning the combined result.
func Combine(images, imagesWithInfo []Image) []Image {
	for _, info := range imagesWithInfo {
		if info.Name == "" {
			continue
		}
		img := findByName(images, info.Name)
		if img != nil {
			img.Source = info.Source
			img.Rating = info.Rating
			img.Tags = info.Tags
		}
	}
	return images
}

type byName []Image

func (img byName) Len() int           { return len(img) }
func (img byName) Swap(i, j int)      { img[i], img[j] = img[j], img[i] }
func (img byName) Less(i, j int) bool { return img[i].Name < img[j].Name }

func findByName(image []Image, name string) *Image {
	sort.Sort(byName(image))
	i := sort.Search(len(image), func(i int) bool { return image[i].Name >= name })
	if i < len(image) && image[i].Name == name {
		return &image[i]
	}
	return nil
}

type byID []Image

func (img byID) Len() int           { return len(img) }
func (img byID) Swap(i, j int)      { img[i], img[j] = img[j], img[i] }
func (img byID) Less(i, j int) bool { return img[i].ID < img[j].ID }

// FindByID takes a slice of images, sorts them by ID and then finds then one
// with ID id.
func FindByID(image []Image, id int) *Image {
	sort.Sort(byID(image))
	i := sort.Search(len(image), func(i int) bool { return image[i].ID >= id })
	if i < len(image) && image[i].ID == id {
		return &image[i]
	}
	return nil
}

// CurrentPrefix reads from an open CSV file, reads the first line and uses the
// base directory of the provided workingDir path to find the path prefix used
// in the CSV file.
//
// As an example if the provided workingDir path is '/localpath/pics' and the
// first line has '/serverpath/pics/pic1' then the returned current prefix will
// be '/serverpath'.
func CurrentPrefix(workingDir string, file io.Reader) (string, error) {
	r := csv.NewReader(file)
	firstLine, err := r.Read()
	if err == io.EOF {
		return "", nil
	}
	if err != nil {
		return "", err
	}

	serverDir := firstLine[0]
	picFolder := filepath.Base(workingDir)
	sep := fmt.Sprintf("%c", filepath.Separator)
	if !strings.Contains(serverDir, picFolder) {
		return sep, nil
	}
	for {
		if filepath.Base(serverDir) == picFolder || serverDir == "" || serverDir == "." {
			break
		} else {
			serverDir, _ = filepath.Split(serverDir)
			serverDir = filepath.Dir(serverDir)
		}
	}
	return filepath.Dir(serverDir), nil
}

func sortTags(tags []string) []string {
	seen := make(map[string]struct{}, len(tags))
	sort.Strings(tags)
	var (
		all       = make([]string, 0)
		series    = make([]string, 0)
		character = make([]string, 0)
		artist    = make([]string, 0)
		tk        = make([]string, 0)
		normal    = make([]string, 0)
	)
	for _, t := range tags {
		if _, ok := seen[t]; ok {
			continue
		}
		seen[t] = struct{}{}
		switch {
		case strings.HasPrefix(t, "series:"):
			series = append(series, t)
		case strings.HasPrefix(t, "character:"):
			character = append(character, t)
		case strings.HasPrefix(t, "artist:"):
			artist = append(artist, t)
		case strings.HasPrefix(t, "tk:"):
			tk = append(tk, t)
		default:
			normal = append(normal, t)
		}
	}
	all = append(all, series...)
	all = append(all, character...)
	all = append(all, artist...)
	all = append(all, tk...)
	all = append(all, normal...)
	return all
}

// Save will write the image metadata to an open for writing file. It will
// keep the base of the dir path and replace the prefix with the provided one.
func Save(file io.Writer, images []Image, dir, prefix string, useLinuxSep bool) error {
	// Sort each image's tags.
	sorted := make([]Image, 0, len(images))
	for _, img := range images {
		img.Tags = sortTags(img.Tags)
		sorted = append(sorted, img)
	}
	// Save to file.
	w := csv.NewWriter(file)
	w.WriteAll(toRecords(sorted, dir, prefix, useLinuxSep))

	if err := w.Error(); err != nil {
		return fmt.Errorf("error writing csv: %v", err)
	}
	return nil
}

func toRecords(images []Image, dir, prefix string, useLinuxSep bool) [][]string {
	var records [][]string
	for _, img := range images {
		record := toRecord(img, dir, prefix, useLinuxSep)
		records = append(records, record)
	}
	return records
}

func toRecord(img Image, dir, prefix string, useLinuxSep bool) []string {
	var record []string
	p := filepath.Join(prefix, filepath.Base(dir), img.Name)
	if useLinuxSep {
		p = filepath.ToSlash(p)
	}
	record = append(record, p)
	record = append(record, strings.Join(img.Tags, " "))
	record = append(record, img.Source)
	record = append(record, img.Rating)
	record = append(record, "")
	return record
}
<|endoftext|>


<commit_before>package numeric

import (
	"errors"

	"github.com/ready-steady/linear/matrix"
)

// Compute calculates the temperature profile corresponding to a power profile
// and a timeline. The power profile is specified by a function func(time
// float64, power []float64) evaluating the power dissipation at an arbitrary
// time moment. The timeline should be an increasing sequence that contains at
// least two elements with the first one being the initial time.
func (t *Temperature) Compute(power func(float64, []float64),
	time []float64) ([]float64, error) {

	cc, nc := t.Cores, t.Nodes

	sc := uint(len(time))
	if sc < 2 {
		return nil, errors.New("the timeline should have at least two points")
	}

	A, B := t.system.A, t.system.B

	P := make([]float64, cc)

	derivative := func(time float64, S, dS []float64) {
		matrix.Multiply(A, S, dS, nc, nc, 1)
		power(time, P)
		for i := uint(0); i < cc; i++ {
			dS[i] += B[i] * P[i]
		}
	}

	S, _, err := t.integrator.Compute(derivative, time, make([]float64, nc))
	if err != nil {
		return nil, err
	}

	Q := make([]float64, sc*cc)
	for i := uint(0); i < cc; i++ {
		for j := uint(0); j < sc; j++ {
			Q[j*cc+i] = S[j*nc+i] + t.system.Qamb
		}
	}

	return Q, nil
}
<commit_msg>Updated temperature/numeric wrt to the changes in ode<commit_after>package numeric

import (
	"errors"

	"github.com/ready-steady/linear/matrix"
)

// Compute calculates the temperature profile corresponding to a power profile
// and a timeline. The power profile is specified by a function func(time
// float64, power []float64) evaluating the power dissipation at an arbitrary
// time moment. The timeline should be an increasing sequence that contains at
// least two elements with the first one being the initial time.
func (t *Temperature) Compute(power func(float64, []float64),
	time []float64) ([]float64, error) {

	cc, nc := t.Cores, t.Nodes

	sc := uint(len(time))
	if sc < 2 {
		return nil, errors.New("the timeline should have at least two points")
	}

	A, B := t.system.A, t.system.B

	P := make([]float64, cc)

	derivative := func(time float64, S, dS []float64) {
		matrix.Multiply(A, S, dS, nc, nc, 1)
		power(time, P)
		for i := uint(0); i < cc; i++ {
			dS[i] += B[i] * P[i]
		}
	}

	S, _, _, err := t.integrator.Compute(derivative, time, make([]float64, nc))
	if err != nil {
		return nil, err
	}

	Q := make([]float64, sc*cc)
	for i := uint(0); i < cc; i++ {
		for j := uint(0); j < sc; j++ {
			Q[j*cc+i] = S[j*nc+i] + t.system.Qamb
		}
	}

	return Q, nil
}
<|endoftext|>


<commit_before>package bfchroma

import (
	"bytes"
	"fmt"
	"testing"

	"github.com/alecthomas/chroma"
	"github.com/alecthomas/chroma/formatters/html"
	"github.com/alecthomas/chroma/styles"
	"github.com/stretchr/testify/assert"
	bf "gopkg.in/russross/blackfriday.v2"
)

func TestExtend(t *testing.T) {
	var b bf.Renderer
	var r *Renderer
	b = bf.NewHTMLRenderer(bf.HTMLRendererParameters{
		Flags: bf.CommonHTMLFlags,
	})
	r = NewRenderer(Extend(b))
	assert.Equal(t, r.Base, b, "should be the same renderer")
}

func ExampleExtend() {
	md := "```go\npackage main\n\nfunc main() {\n}\n```"

	b := bf.NewHTMLRenderer(bf.HTMLRendererParameters{
		Flags: bf.CommonHTMLFlags,
	})
	r := NewRenderer(Extend(b))

	h := bf.Run([]byte(md), bf.WithRenderer(r))
	fmt.Println(string(h))
}

func TestStyle(t *testing.T) {
	var r *Renderer
	for k, v := range styles.Registry {
		r = NewRenderer(Style(k))
		assert.Equal(t, r.Style, v, "Style should match")
	}
	for _, v := range []string{"♥", "inexistent", "fallback!"} {
		r = NewRenderer(Style(v))
		assert.Equal(t, r.Style, styles.Fallback)
	}
}

func ExampleStyle() {
	md := "```go\npackage main\n\nfunc main() {\n}\n```"

	r := NewRenderer(Style("github"))

	h := bf.Run([]byte(md), bf.WithRenderer(r))
	fmt.Println(string(h))
}

func TestChromaStyle(t *testing.T) {
	var r *Renderer
	for _, v := range styles.Registry {
		r = NewRenderer(ChromaStyle(v))
		assert.Equal(t, r.Style, v, "Style should match")
	}
}

func ExampleChromaStyle() {
	md := "```go\npackage main\n\nfunc main() {\n}\n```"

	r := NewRenderer(ChromaStyle(styles.GitHub))

	h := bf.Run([]byte(md), bf.WithRenderer(r))
	fmt.Println(string(h))
}

func TestWithoutAutodetect(t *testing.T) {
	r := NewRenderer(WithoutAutodetect())
	assert.False(t, r.Autodetect, "Should set Autodetect to false")
	r = NewRenderer()
	assert.True(t, r.Autodetect, "Not using option should leave Autodetect to true")
}

func ExampleWithoutAutodetect() {
	md := "```\npackage main\n\nfunc main() {\n}\n```"

	r := NewRenderer(WithoutAutodetect())

	h := bf.Run([]byte(md), bf.WithRenderer(r))
	fmt.Println(string(h))
}

func TestChromaOptions(t *testing.T) {
	NewRenderer(ChromaOptions(html.WithClasses()))
}

func ExampleChromaOptions() {
	md := "```go\npackage main\n\nfunc main() {\n}\n```"

	r := NewRenderer(ChromaOptions(html.WithLineNumbers()))

	h := bf.Run([]byte(md), bf.WithRenderer(r))
	fmt.Println(string(h))
}

func TestRenderWithChroma(t *testing.T) {
	var err error
	var b *bytes.Buffer
	r := NewRenderer()
	tests := []struct {
		in  []byte
		cbd bf.CodeBlockData
		out string
	}{
		{[]byte{0}, bf.CodeBlockData{}, "
\x00
\n"},
		{[]byte{0, 1, 2}, bf.CodeBlockData{}, "
\x00\x01\x02
\n"},
		{[]byte("Hello World"), bf.CodeBlockData{}, "
Hello World
\n"},
	}
	for _, test := range tests {
		b = new(bytes.Buffer)
		err = r.RenderWithChroma(b, test.in, test.cbd)
		assert.NoError(t, err, "Should not fail")
		assert.Equal(t, b.String(), test.out)
	}
}

func TestRender(t *testing.T) {
	md := "```go\npackage main\n\nfunc main() {\n}\n```"
	r := NewRenderer()
	bg := r.Style.Get(chroma.Background).Background.String()

	h := bf.Run([]byte(md), bf.WithRenderer(r))
	assert.Contains(t, string(h), r.Style.Get(chroma.NameFunction).Colour.String())
	assert.Contains(t, string(h), bg)
	assert.Contains(t, string(h), "<pre")

	// Check if auto-detection works on Go example
	md = "```\npackage main\n\nfunc main() {\n}\n```"
	h = bf.Run([]byte(md), bf.WithRenderer(r))
	assert.Contains(t, string(h), r.Style.Get(chroma.NameFunction).Colour.String())
	assert.Contains(t, string(h), bg)
	assert.Contains(t, string(h), "<pre")

	// Check if disabling the Autodetect feature works
	r = NewRenderer(WithoutAutodetect())
	h = bf.Run([]byte(md), bf.WithRenderer(r))
	assert.NotContains(t, string(h), r.Style.Get(chroma.NameFunction).Colour.String())
	assert.Contains(t, string(h), bg)
	assert.Contains(t, string(h), "<pre")
}

func ExampleNewRenderer() {
	// Complex example on how to initialize the renderer
	md := "```go\npackage main\n\nfunc main() {\n}\n```"

	r := NewRenderer(
		Extend(bf.NewHTMLRenderer(bf.HTMLRendererParameters{
			Flags: bf.CommonHTMLFlags,
		})),
		WithoutAutodetect(),
		ChromaStyle(styles.GitHub),
		ChromaOptions(html.WithLineNumbers()),
	)

	h := bf.Run([]byte(md), bf.WithRenderer(r))
	fmt.Println(string(h))
}
<commit_msg>Fixing tests<commit_after>package bfchroma

import (
	"bytes"
	"fmt"
	"testing"

	"github.com/alecthomas/chroma"
	"github.com/alecthomas/chroma/formatters/html"
	"github.com/alecthomas/chroma/styles"
	"github.com/stretchr/testify/assert"
	bf "gopkg.in/russross/blackfriday.v2"
)

func TestExtend(t *testing.T) {
	var b bf.Renderer
	var r *Renderer
	b = bf.NewHTMLRenderer(bf.HTMLRendererParameters{
		Flags: bf.CommonHTMLFlags,
	})
	r = NewRenderer(Extend(b))
	assert.Equal(t, r.Base, b, "should be the same renderer")
}

func ExampleExtend() {
	md := "```go\npackage main\n\nfunc main() {\n}\n```"

	b := bf.NewHTMLRenderer(bf.HTMLRendererParameters{
		Flags: bf.CommonHTMLFlags,
	})
	r := NewRenderer(Extend(b))

	h := bf.Run([]byte(md), bf.WithRenderer(r))
	fmt.Println(string(h))
}

func TestStyle(t *testing.T) {
	var r *Renderer
	for k, v := range styles.Registry {
		r = NewRenderer(Style(k))
		assert.Equal(t, r.Style, v, "Style should match")
	}
	for _, v := range []string{"♥", "inexistent", "fallback!"} {
		r = NewRenderer(Style(v))
		assert.Equal(t, r.Style, styles.Fallback)
	}
}

func ExampleStyle() {
	md := "```go\npackage main\n\nfunc main() {\n}\n```"

	r := NewRenderer(Style("github"))

	h := bf.Run([]byte(md), bf.WithRenderer(r))
	fmt.Println(string(h))
}

func TestChromaStyle(t *testing.T) {
	var r *Renderer
	for _, v := range styles.Registry {
		r = NewRenderer(ChromaStyle(v))
		assert.Equal(t, r.Style, v, "Style should match")
	}
}

func ExampleChromaStyle() {
	md := "```go\npackage main\n\nfunc main() {\n}\n```"

	r := NewRenderer(ChromaStyle(styles.GitHub))

	h := bf.Run([]byte(md), bf.WithRenderer(r))
	fmt.Println(string(h))
}

func TestWithoutAutodetect(t *testing.T) {
	r := NewRenderer(WithoutAutodetect())
	assert.False(t, r.Autodetect, "Should set Autodetect to false")
	r = NewRenderer()
	assert.True(t, r.Autodetect, "Not using option should leave Autodetect to true")
}

func ExampleWithoutAutodetect() {
	md := "```\npackage main\n\nfunc main() {\n}\n```"

	r := NewRenderer(WithoutAutodetect())

	h := bf.Run([]byte(md), bf.WithRenderer(r))
	fmt.Println(string(h))
}

func TestChromaOptions(t *testing.T) {
	NewRenderer(ChromaOptions(html.WithClasses()))
}

func ExampleChromaOptions() {
	md := "```go\npackage main\n\nfunc main() {\n}\n```"

	r := NewRenderer(ChromaOptions(html.WithLineNumbers()))

	h := bf.Run([]byte(md), bf.WithRenderer(r))
	fmt.Println(string(h))
}

func TestRenderWithChroma(t *testing.T) {
	var err error
	var b *bytes.Buffer
	r := NewRenderer()
	tests := []struct {
		in  []byte
		cbd bf.CodeBlockData
		out string
	}{
		{[]byte{0}, bf.CodeBlockData{}, "
\x00
"},
		{[]byte{0, 1, 2}, bf.CodeBlockData{}, "
\x00\x01\x02
"},
		{[]byte("Hello World"), bf.CodeBlockData{}, "
Hello World
"},
	}
	for _, test := range tests {
		b = new(bytes.Buffer)
		err = r.RenderWithChroma(b, test.in, test.cbd)
		assert.NoError(t, err, "Should not fail")
		assert.Equal(t, test.out, b.String())
	}
}

func TestRender(t *testing.T) {
	md := "```go\npackage main\n\nfunc main() {\n}\n```"
	r := NewRenderer()
	bg := r.Style.Get(chroma.Background).Background.String()

	h := bf.Run([]byte(md), bf.WithRenderer(r))
	assert.Contains(t, string(h), r.Style.Get(chroma.NameFunction).Colour.String())
	assert.Contains(t, string(h), bg)
	assert.Contains(t, string(h), "<pre")

	// Check if auto-detection works on Go example
	md = "```\npackage main\n\nfunc main() {\n}\n```"
	h = bf.Run([]byte(md), bf.WithRenderer(r))
	assert.Contains(t, string(h), r.Style.Get(chroma.NameFunction).Colour.String())
	assert.Contains(t, string(h), bg)
	assert.Contains(t, string(h), "<pre")

	// Check if disabling the Autodetect feature works
	r = NewRenderer(WithoutAutodetect())
	h = bf.Run([]byte(md), bf.WithRenderer(r))
	assert.NotContains(t, string(h), r.Style.Get(chroma.NameFunction).Colour.String())
	assert.Contains(t, string(h), bg)
	assert.Contains(t, string(h), "<pre")
}

func ExampleNewRenderer() {
	// Complex example on how to initialize the renderer
	md := "```go\npackage main\n\nfunc main() {\n}\n```"

	r := NewRenderer(
		Extend(bf.NewHTMLRenderer(bf.HTMLRendererParameters{
			Flags: bf.CommonHTMLFlags,
		})),
		WithoutAutodetect(),
		ChromaStyle(styles.GitHub),
		ChromaOptions(html.WithLineNumbers()),
	)

	h := bf.Run([]byte(md), bf.WithRenderer(r))
	fmt.Println(string(h))
}
<|endoftext|>


<commit_before>package resource

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/qor/qor"
	"github.com/qor/qor/utils"
	"github.com/qor/roles"
	"github.com/qor/validations"
)

// Metaor interface
type Metaor interface {
	GetName() string
	GetFieldName() string
	GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	GetFormattedValuer() func(interface{}, *qor.Context) interface{}
	GetValuer() func(interface{}, *qor.Context) interface{}
	GetResource() Resourcer
	GetMetas() []Metaor
	HasPermission(roles.PermissionMode, *qor.Context) bool
}

// ConfigureMetaBeforeInitializeInterface if a struct's field's type implemented this interface, it will be called when initializing a meta
type ConfigureMetaBeforeInitializeInterface interface {
	ConfigureQorMetaBeforeInitialize(Metaor)
}

// ConfigureMetaInterface if a struct's field's type implemented this interface, it will be called after configed
type ConfigureMetaInterface interface {
	ConfigureQorMeta(Metaor)
}

// MetaConfigInterface meta configuration interface
type MetaConfigInterface interface {
	ConfigureMetaInterface
}

// MetaConfig base meta config struct
type MetaConfig struct {
}

// ConfigureQorMeta implement the MetaConfigInterface
func (MetaConfig) ConfigureQorMeta(Metaor) {
}

// Meta meta struct definition
type Meta struct {
	Name            string
	FieldName       string
	FieldStruct     *gorm.StructField
	Setter          func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	Valuer          func(interface{}, *qor.Context) interface{}
	FormattedValuer func(interface{}, *qor.Context) interface{}
	Config          MetaConfigInterface
	Resource        Resourcer
	Permission      *roles.Permission
}

// GetBaseResource get base resource from meta
func (meta Meta) GetBaseResource() Resourcer {
	return meta.Resource
}

// GetName get meta's name
func (meta Meta) GetName() string {
	return meta.Name
}

// GetFieldName get meta's field name
func (meta Meta) GetFieldName() string {
	return meta.FieldName
}

// SetFieldName set meta's field name
func (meta *Meta) SetFieldName(name string) {
	meta.FieldName = name
}

// GetSetter get setter from meta
func (meta Meta) GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
	return meta.Setter
}

// SetSetter set setter to meta
func (meta *Meta) SetSetter(fc func(resource interface{}, metaValue *MetaValue, context *qor.Context)) {
	meta.Setter = fc
}

// GetValuer get valuer from meta
func (meta Meta) GetValuer() func(interface{}, *qor.Context) interface{} {
	return meta.Valuer
}

// SetValuer set valuer for meta
func (meta *Meta) SetValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.Valuer = fc
}

// GetFormattedValuer get formatted valuer from meta
func (meta *Meta) GetFormattedValuer() func(interface{}, *qor.Context) interface{} {
	if meta.FormattedValuer != nil {
		return meta.FormattedValuer
	}
	return meta.Valuer
}

// SetFormattedValuer set formatted valuer for meta
func (meta *Meta) SetFormattedValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.FormattedValuer = fc
}

// HasPermission check has permission or not
func (meta Meta) HasPermission(mode roles.PermissionMode, context *qor.Context) bool {
	if meta.Permission == nil {
		return true
	}
	return meta.Permission.HasPermission(mode, context.Roles...)
}

// SetPermission set permission for meta
func (meta *Meta) SetPermission(permission *roles.Permission) {
	meta.Permission = permission
}

// PreInitialize when will be run before initialize, used to fill some basic necessary information
func (meta *Meta) PreInitialize() error {
	if meta.Name == "" {
		utils.ExitWithMsg("Meta should have name: %v", reflect.TypeOf(meta))
	} else if meta.FieldName == "" {
		meta.FieldName = meta.Name
	}

	// parseNestedField used to handle case like Profile.Name
	var parseNestedField = func(value reflect.Value, name string) (reflect.Value, string) {
		fields := strings.Split(name, ".")
		value = reflect.Indirect(value)
		for _, field := range fields[:len(fields)-1] {
			value = value.FieldByName(field)
		}

		return value, fields[len(fields)-1]
	}

	var getField = func(fields []*gorm.StructField, name string) *gorm.StructField {
		for _, field := range fields {
			if field.Name == name || field.DBName == name {
				return field
			}
		}
		return nil
	}

	var nestedField = strings.Contains(meta.FieldName, ".")
	var scope = &gorm.Scope{Value: meta.Resource.GetResource().Value}
	if nestedField {
		subModel, name := parseNestedField(reflect.ValueOf(meta.Resource.GetResource().Value), meta.FieldName)
		meta.FieldStruct = getField(scope.New(subModel.Interface()).GetStructFields(), name)
	} else {
		meta.FieldStruct = getField(scope.GetStructFields(), meta.FieldName)
	}
	return nil
}

// Initialize initialize meta, will set valuer, setter if haven't configure it
func (meta *Meta) Initialize() error {
	var (
		nestedField = strings.Contains(meta.FieldName, ".")
		field       = meta.FieldStruct
		hasColumn   = meta.FieldStruct != nil
	)

	var fieldType reflect.Type
	if hasColumn {
		fieldType = field.Struct.Type
		for fieldType.Kind() == reflect.Ptr {
			fieldType = fieldType.Elem()
		}
	}

	// Set Meta Valuer
	if meta.Valuer == nil {
		if hasColumn {
			meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
				scope := context.GetDB().NewScope(value)
				fieldName := meta.FieldName
				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				if f, ok := scope.FieldByName(fieldName); ok {
					if relationship := f.Relationship; relationship != nil && f.Field.CanAddr() && !scope.PrimaryKeyZero() {
						if ((relationship.Kind == "has_many" || relationship.Kind == "many_to_many") && f.Field.Len() == 0) ||
							((relationship.Kind == "has_one" || relationship.Kind == "belongs_to") && context.GetDB().NewScope(f.Field.Interface()).PrimaryKeyZero()) {
							context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.FieldName)
						}
					}

					return f.Field.Interface()
				}

				return ""
			}
		} else {
			utils.ExitWithMsg("Meta %v is not supported for resource %v, no `Valuer` configured for it", meta.FieldName, reflect.TypeOf(meta.Resource.GetResource().Value))
		}
	}

	if meta.Setter == nil && hasColumn {
		if relationship := field.Relationship; relationship != nil {
			if relationship.Kind == "belongs_to" || relationship.Kind == "many_to_many" {
				meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
					scope := &gorm.Scope{Value: resource}
					reflectValue := reflect.Indirect(reflect.ValueOf(resource))
					field := reflectValue.FieldByName(meta.FieldName)

					if field.Kind() == reflect.Ptr {
						if field.IsNil() {
							field.Set(utils.NewValue(field.Type()).Elem())
						}

						for field.Kind() == reflect.Ptr {
							field = field.Elem()
						}
					}

					primaryKeys := utils.ToArray(metaValue.Value)
					// associations not changed for belongs to
					if relationship.Kind == "belongs_to" && len(relationship.ForeignFieldNames) == 1 {
						oldPrimaryKeys := utils.ToArray(reflectValue.FieldByName(relationship.ForeignFieldNames[0]).Interface())
						// if not changed
						if fmt.Sprint(primaryKeys) == fmt.Sprint(oldPrimaryKeys) {
							return
						}

						// if removed
						if len(primaryKeys) == 0 {
							field := reflectValue.FieldByName(relationship.ForeignFieldNames[0])
							field.Set(reflect.Zero(field.Type()))
						}
					}

					if len(primaryKeys) > 0 {
						// set current field value to blank and replace it with new value
						field.Set(reflect.Zero(field.Type()))
						context.GetDB().Where(primaryKeys).Find(field.Addr().Interface())
					}

					// Replace many 2 many relations
					if relationship.Kind == "many_to_many" {
						if !scope.PrimaryKeyZero() {
							context.GetDB().Model(resource).Association(meta.FieldName).Replace(field.Interface())
							field.Set(reflect.Zero(field.Type()))
						}
					}
				}
			}
		} else {
			meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
				if metaValue == nil {
					return
				}

				var (
					value     = metaValue.Value
					fieldName = meta.FieldName
				)

				defer func() {
					if r := recover(); r != nil {
						context.AddError(validations.NewError(resource, meta.Name, fmt.Sprintf("Can't set value %v", value)))
					}
				}()

				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				field := reflect.Indirect(reflect.ValueOf(resource)).FieldByName(fieldName)
				if field.Kind() == reflect.Ptr {
					if field.IsNil() && utils.ToString(value) != "" {
						field.Set(utils.NewValue(field.Type()).Elem())
					}

					if utils.ToString(value) == "" {
						field.Set(reflect.Zero(field.Type()))
						return
					}

					for field.Kind() == reflect.Ptr {
						field = field.Elem()
					}
				}

				if field.IsValid() && field.CanAddr() {
					switch field.Kind() {
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
						field.SetInt(utils.ToInt(value))
					case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
						field.SetUint(utils.ToUint(value))
					case reflect.Float32, reflect.Float64:
						field.SetFloat(utils.ToFloat(value))
					case reflect.Bool:
						// TODO: add test
						if utils.ToString(value) == "true" {
							field.SetBool(true)
						} else {
							field.SetBool(false)
						}
					default:
						if scanner, ok := field.Addr().Interface().(sql.Scanner); ok {
							if value == nil && len(metaValue.MetaValues.Values) > 0 {
								decodeMetaValuesToField(meta.Resource, field, metaValue, context)
								return
							}

							if scanner.Scan(value) != nil {
								scanner.Scan(utils.ToString(value))
							}
						} else if reflect.TypeOf("").ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToString(value)).Convert(field.Type()))
						} else if reflect.TypeOf([]string{}).ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToArray(value)).Convert(field.Type()))
						} else if rvalue := reflect.ValueOf(value); reflect.TypeOf(rvalue.Type()).ConvertibleTo(field.Type()) {
							field.Set(rvalue.Convert(field.Type()))
						} else if _, ok := field.Addr().Interface().(*time.Time); ok {
							if str := utils.ToString(value); str != "" {
								if newTime, err := utils.ParseTime(str, context); err == nil {
									field.Set(reflect.ValueOf(newTime))
								}
							} else {
								field.Set(reflect.Zero(field.Type()))
							}
						} else {
							var buf = bytes.NewBufferString("")
							json.NewEncoder(buf).Encode(value)
							if err := json.NewDecoder(strings.NewReader(buf.String())).Decode(field.Addr().Interface()); err != nil {
								utils.ExitWithMsg("Can't set value %v to %v [meta %v]", reflect.TypeOf(value), field.Type(), meta)
							}
						}
					}
				}
			}
		}
	}

	if nestedField {
		oldvalue := meta.Valuer
		meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
			return oldvalue(getNestedModel(value, meta.FieldName, context), context)
		}
		oldSetter := meta.Setter
		meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
			oldSetter(getNestedModel(resource, meta.FieldName, context), metaValue, context)
		}
	}
	return nil
}

func getNestedModel(value interface{}, fieldName string, context *qor.Context) interface{} {
	model := reflect.Indirect(reflect.ValueOf(value))
	fields := strings.Split(fieldName, ".")
	for _, field := range fields[:len(fields)-1] {
		if model.CanAddr() {
			submodel := model.FieldByName(field)
			if context.GetDB().NewRecord(submodel.Interface()) && !context.GetDB().NewRecord(model.Addr().Interface()) {
				if submodel.CanAddr() {
					context.GetDB().Model(model.Addr().Interface()).Association(field).Find(submodel.Addr().Interface())
					model = submodel
				} else {
					break
				}
			} else {
				model = submodel
			}
		}
	}

	if model.CanAddr() {
		return model.Addr().Interface()
	}
	return nil
}
<commit_msg>Support use pointer of struct as belongs_to, has_one relationship<commit_after>package resource

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/qor/qor"
	"github.com/qor/qor/utils"
	"github.com/qor/roles"
	"github.com/qor/validations"
)

// Metaor interface
type Metaor interface {
	GetName() string
	GetFieldName() string
	GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	GetFormattedValuer() func(interface{}, *qor.Context) interface{}
	GetValuer() func(interface{}, *qor.Context) interface{}
	GetResource() Resourcer
	GetMetas() []Metaor
	HasPermission(roles.PermissionMode, *qor.Context) bool
}

// ConfigureMetaBeforeInitializeInterface if a struct's field's type implemented this interface, it will be called when initializing a meta
type ConfigureMetaBeforeInitializeInterface interface {
	ConfigureQorMetaBeforeInitialize(Metaor)
}

// ConfigureMetaInterface if a struct's field's type implemented this interface, it will be called after configed
type ConfigureMetaInterface interface {
	ConfigureQorMeta(Metaor)
}

// MetaConfigInterface meta configuration interface
type MetaConfigInterface interface {
	ConfigureMetaInterface
}

// MetaConfig base meta config struct
type MetaConfig struct {
}

// ConfigureQorMeta implement the MetaConfigInterface
func (MetaConfig) ConfigureQorMeta(Metaor) {
}

// Meta meta struct definition
type Meta struct {
	Name            string
	FieldName       string
	FieldStruct     *gorm.StructField
	Setter          func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	Valuer          func(interface{}, *qor.Context) interface{}
	FormattedValuer func(interface{}, *qor.Context) interface{}
	Config          MetaConfigInterface
	Resource        Resourcer
	Permission      *roles.Permission
}

// GetBaseResource get base resource from meta
func (meta Meta) GetBaseResource() Resourcer {
	return meta.Resource
}

// GetName get meta's name
func (meta Meta) GetName() string {
	return meta.Name
}

// GetFieldName get meta's field name
func (meta Meta) GetFieldName() string {
	return meta.FieldName
}

// SetFieldName set meta's field name
func (meta *Meta) SetFieldName(name string) {
	meta.FieldName = name
}

// GetSetter get setter from meta
func (meta Meta) GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
	return meta.Setter
}

// SetSetter set setter to meta
func (meta *Meta) SetSetter(fc func(resource interface{}, metaValue *MetaValue, context *qor.Context)) {
	meta.Setter = fc
}

// GetValuer get valuer from meta
func (meta Meta) GetValuer() func(interface{}, *qor.Context) interface{} {
	return meta.Valuer
}

// SetValuer set valuer for meta
func (meta *Meta) SetValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.Valuer = fc
}

// GetFormattedValuer get formatted valuer from meta
func (meta *Meta) GetFormattedValuer() func(interface{}, *qor.Context) interface{} {
	if meta.FormattedValuer != nil {
		return meta.FormattedValuer
	}
	return meta.Valuer
}

// SetFormattedValuer set formatted valuer for meta
func (meta *Meta) SetFormattedValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.FormattedValuer = fc
}

// HasPermission check has permission or not
func (meta Meta) HasPermission(mode roles.PermissionMode, context *qor.Context) bool {
	if meta.Permission == nil {
		return true
	}
	return meta.Permission.HasPermission(mode, context.Roles...)
}

// SetPermission set permission for meta
func (meta *Meta) SetPermission(permission *roles.Permission) {
	meta.Permission = permission
}

// PreInitialize when will be run before initialize, used to fill some basic necessary information
func (meta *Meta) PreInitialize() error {
	if meta.Name == "" {
		utils.ExitWithMsg("Meta should have name: %v", reflect.TypeOf(meta))
	} else if meta.FieldName == "" {
		meta.FieldName = meta.Name
	}

	// parseNestedField used to handle case like Profile.Name
	var parseNestedField = func(value reflect.Value, name string) (reflect.Value, string) {
		fields := strings.Split(name, ".")
		value = reflect.Indirect(value)
		for _, field := range fields[:len(fields)-1] {
			value = value.FieldByName(field)
		}

		return value, fields[len(fields)-1]
	}

	var getField = func(fields []*gorm.StructField, name string) *gorm.StructField {
		for _, field := range fields {
			if field.Name == name || field.DBName == name {
				return field
			}
		}
		return nil
	}

	var nestedField = strings.Contains(meta.FieldName, ".")
	var scope = &gorm.Scope{Value: meta.Resource.GetResource().Value}
	if nestedField {
		subModel, name := parseNestedField(reflect.ValueOf(meta.Resource.GetResource().Value), meta.FieldName)
		meta.FieldStruct = getField(scope.New(subModel.Interface()).GetStructFields(), name)
	} else {
		meta.FieldStruct = getField(scope.GetStructFields(), meta.FieldName)
	}
	return nil
}

// Initialize initialize meta, will set valuer, setter if haven't configure it
func (meta *Meta) Initialize() error {
	var (
		nestedField = strings.Contains(meta.FieldName, ".")
		field       = meta.FieldStruct
		hasColumn   = meta.FieldStruct != nil
	)

	var fieldType reflect.Type
	if hasColumn {
		fieldType = field.Struct.Type
		for fieldType.Kind() == reflect.Ptr {
			fieldType = fieldType.Elem()
		}
	}

	// Set Meta Valuer
	if meta.Valuer == nil {
		if hasColumn {
			meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
				scope := context.GetDB().NewScope(value)
				fieldName := meta.FieldName
				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				if f, ok := scope.FieldByName(fieldName); ok {
					if relationship := f.Relationship; relationship != nil && f.Field.CanAddr() && !scope.PrimaryKeyZero() {
						if (relationship.Kind == "has_many" || relationship.Kind == "many_to_many") && f.Field.Len() == 0 {
							context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.FieldName)
						} else if (relationship.Kind == "has_one" || relationship.Kind == "belongs_to") && context.GetDB().NewScope(f.Field.Interface()).PrimaryKeyZero() {
							if f.Field.Kind() == reflect.Ptr && f.Field.IsNil() {
								f.Field.Set(reflect.New(f.Field.Type().Elem()))
							}

							context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.FieldName)
						}
					}

					return f.Field.Interface()
				}

				return ""
			}
		} else {
			utils.ExitWithMsg("Meta %v is not supported for resource %v, no `Valuer` configured for it", meta.FieldName, reflect.TypeOf(meta.Resource.GetResource().Value))
		}
	}

	if meta.Setter == nil && hasColumn {
		if relationship := field.Relationship; relationship != nil {
			if relationship.Kind == "belongs_to" || relationship.Kind == "many_to_many" {
				meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
					scope := &gorm.Scope{Value: resource}
					reflectValue := reflect.Indirect(reflect.ValueOf(resource))
					field := reflectValue.FieldByName(meta.FieldName)

					if field.Kind() == reflect.Ptr {
						if field.IsNil() {
							field.Set(utils.NewValue(field.Type()).Elem())
						}

						for field.Kind() == reflect.Ptr {
							field = field.Elem()
						}
					}

					primaryKeys := utils.ToArray(metaValue.Value)
					// associations not changed for belongs to
					if relationship.Kind == "belongs_to" && len(relationship.ForeignFieldNames) == 1 {
						oldPrimaryKeys := utils.ToArray(reflectValue.FieldByName(relationship.ForeignFieldNames[0]).Interface())
						// if not changed
						if fmt.Sprint(primaryKeys) == fmt.Sprint(oldPrimaryKeys) {
							return
						}

						// if removed
						if len(primaryKeys) == 0 {
							field := reflectValue.FieldByName(relationship.ForeignFieldNames[0])
							field.Set(reflect.Zero(field.Type()))
						}
					}

					if len(primaryKeys) > 0 {
						// set current field value to blank and replace it with new value
						field.Set(reflect.Zero(field.Type()))
						context.GetDB().Where(primaryKeys).Find(field.Addr().Interface())
					}

					// Replace many 2 many relations
					if relationship.Kind == "many_to_many" {
						if !scope.PrimaryKeyZero() {
							context.GetDB().Model(resource).Association(meta.FieldName).Replace(field.Interface())
							field.Set(reflect.Zero(field.Type()))
						}
					}
				}
			}
		} else {
			meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
				if metaValue == nil {
					return
				}

				var (
					value     = metaValue.Value
					fieldName = meta.FieldName
				)

				defer func() {
					if r := recover(); r != nil {
						context.AddError(validations.NewError(resource, meta.Name, fmt.Sprintf("Can't set value %v", value)))
					}
				}()

				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				field := reflect.Indirect(reflect.ValueOf(resource)).FieldByName(fieldName)
				if field.Kind() == reflect.Ptr {
					if field.IsNil() && utils.ToString(value) != "" {
						field.Set(utils.NewValue(field.Type()).Elem())
					}

					if utils.ToString(value) == "" {
						field.Set(reflect.Zero(field.Type()))
						return
					}

					for field.Kind() == reflect.Ptr {
						field = field.Elem()
					}
				}

				if field.IsValid() && field.CanAddr() {
					switch field.Kind() {
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
						field.SetInt(utils.ToInt(value))
					case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
						field.SetUint(utils.ToUint(value))
					case reflect.Float32, reflect.Float64:
						field.SetFloat(utils.ToFloat(value))
					case reflect.Bool:
						// TODO: add test
						if utils.ToString(value) == "true" {
							field.SetBool(true)
						} else {
							field.SetBool(false)
						}
					default:
						if scanner, ok := field.Addr().Interface().(sql.Scanner); ok {
							if value == nil && len(metaValue.MetaValues.Values) > 0 {
								decodeMetaValuesToField(meta.Resource, field, metaValue, context)
								return
							}

							if scanner.Scan(value) != nil {
								scanner.Scan(utils.ToString(value))
							}
						} else if reflect.TypeOf("").ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToString(value)).Convert(field.Type()))
						} else if reflect.TypeOf([]string{}).ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToArray(value)).Convert(field.Type()))
						} else if rvalue := reflect.ValueOf(value); reflect.TypeOf(rvalue.Type()).ConvertibleTo(field.Type()) {
							field.Set(rvalue.Convert(field.Type()))
						} else if _, ok := field.Addr().Interface().(*time.Time); ok {
							if str := utils.ToString(value); str != "" {
								if newTime, err := utils.ParseTime(str, context); err == nil {
									field.Set(reflect.ValueOf(newTime))
								}
							} else {
								field.Set(reflect.Zero(field.Type()))
							}
						} else {
							var buf = bytes.NewBufferString("")
							json.NewEncoder(buf).Encode(value)
							if err := json.NewDecoder(strings.NewReader(buf.String())).Decode(field.Addr().Interface()); err != nil {
								utils.ExitWithMsg("Can't set value %v to %v [meta %v]", reflect.TypeOf(value), field.Type(), meta)
							}
						}
					}
				}
			}
		}
	}

	if nestedField {
		oldvalue := meta.Valuer
		meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
			return oldvalue(getNestedModel(value, meta.FieldName, context), context)
		}
		oldSetter := meta.Setter
		meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
			oldSetter(getNestedModel(resource, meta.FieldName, context), metaValue, context)
		}
	}
	return nil
}

func getNestedModel(value interface{}, fieldName string, context *qor.Context) interface{} {
	model := reflect.Indirect(reflect.ValueOf(value))
	fields := strings.Split(fieldName, ".")
	for _, field := range fields[:len(fields)-1] {
		if model.CanAddr() {
			submodel := model.FieldByName(field)
			if context.GetDB().NewRecord(submodel.Interface()) && !context.GetDB().NewRecord(model.Addr().Interface()) {
				if submodel.CanAddr() {
					context.GetDB().Model(model.Addr().Interface()).Association(field).Find(submodel.Addr().Interface())
					model = submodel
				} else {
					break
				}
			} else {
				model = submodel
			}
		}
	}

	if model.CanAddr() {
		return model.Addr().Interface()
	}
	return nil
}
<|endoftext|>