<commit_before>package market

import (
	"bytes"
	"context"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/greater-commons/paypal-marketplace/orders"
)

const (
	createOrderRoute           = "/v1/checkout/orders"
	getTransactionContextRoute = "/v1/risk/transaction-contexts"
	disbursePaymentsRoute      = "/v1/payments/referenced-payouts-items"
)

func (c *Client) CreateOrder(ctx context.Context, trackingID string, params *orders.CreateOrderParams) (*orders.CreateOrderResponse, error) {
	d, err := json.Marshal(params)
	if err != nil {
		return nil, err
	}
	r := &request{
		client:   c,
		method:   http.MethodPost,
		endpoint: createOrderRoute,
		body:     bytes.NewReader(d),
		headers: map[string][]string{
			"Paypal-Client-Metadata-Id": []string{trackingID},
		},
	}
	res, err := r.do(ctx)
	if err != nil {
		return nil, err
	}
	if res.status == http.StatusCreated {
		r := &orders.CreateOrderResponse{}
		err = json.NewDecoder(res.body).Decode(r)
		if err != nil {
			return nil, err
		}
		return r, nil
	}
	errorData, err := ioutil.ReadAll(res.body)
	if err != nil {
		return nil, err
	}
	return nil, &BadResponse{
		Status: res.status,
		Body:   string(errorData),
	}
}

func (c *Client) CancelOrder(ctx context.Context, orderID string) error {
	r := &request{
		client:   c,
		method:   http.MethodDelete,
		endpoint: createOrderRoute + "/" + url.PathEscape(orderID),
	}
	res, err := r.do(ctx)
	if err != nil {
		return err
	}
	if res.status == http.StatusNoContent {
		return nil
	}
	errorData, err := ioutil.ReadAll(res.body)
	if err != nil {
		return err
	}
	return &BadResponse{
		Status: res.status,
		Body:   string(errorData),
	}
}

func (c *Client) GetOrderDetails(ctx context.Context, orderID string) (*orders.CreateOrderResponse, error) {
	r := &request{
		client:   c,
		method:   http.MethodGet,
		endpoint: createOrderRoute + "/" + url.PathEscape(orderID),
	}
	res, err := r.do(ctx)
	if err != nil {
		return nil, err
	}
	if res.status == http.StatusOK {
		r := &orders.CreateOrderResponse{}
		err := json.NewDecoder(res.body).Decode(r)
		if err != nil {
			return nil, err
		}
		return r, nil
	}
	errorData, err := ioutil.ReadAll(res.body)
	if err != nil {
		return nil, err
	}
	return nil, &BadResponse{
		Status: res.status,
		Body:   string(errorData),
	}
}

func (c *Client) SaveTransactionContext(ctx context.Context, merchantID, trackingID string, additionalData []orders.KeyValuePair) error {
	body := struct {
		AdditionalData []orders.KeyValuePair `json:"additional_data,omitempty"`
	}{
		AdditionalData: additionalData,
	}
	d, err := json.Marshal(&body)
	if err != nil {
		return err
	}
	r := &request{
		client:   c,
		method:   http.MethodPut,
		endpoint: getTransactionContextRoute + "/" + url.PathEscape(merchantID) + "/" + url.PathEscape(trackingID),
		body:     bytes.NewReader(d),
	}
	res, err := r.do(ctx)
	if err != nil {
		return err
	}
	if res.status == http.StatusOK {
		return nil
	}
	errorData, err := ioutil.ReadAll(res.body)
	if err != nil {
		return err
	}
	return &BadResponse{
		Status: res.status,
		Body:   string(errorData),
	}
}

func (c *Client) PayOrder(ctx context.Context, orderID string, disbursementMode orders.DisbursementModeData) (*orders.PayOrderResponse, error) {
	data := struct {
		DisbursementMode orders.DisbursementModeData `json:"disbursement_mode"`
	}{
		DisbursementMode: disbursementMode,
	}
	d, err := json.Marshal(&data)
	if err != nil {
		return nil, err
	}
	r := &request{
		client:   c,
		method:   http.MethodPost,
		endpoint: createOrderRoute + "/" + url.PathEscape(orderID) + "/pay",
		body:     bytes.NewReader(d),
	}
	res, err := r.do(ctx)
	if err != nil {
		return nil, err
	}
	if res.status == http.StatusOK {
		r := &orders.PayOrderResponse{}
		err = json.NewDecoder(res.body).Decode(r)
		if err != nil {
			return nil, err
		}
		return r, nil
	}

	errorData, err := ioutil.ReadAll(res.body)
	if err != nil {
		return nil, err
	}
	return nil, &BadResponse{
		Status: res.status,
		Body:   string(errorData),
	}
}

func (c *Client) FinalizeDisbursement(ctx context.Context, responsePreference orders.ResponsePreferenceData, transactionID string) (*orders.FinalizeDisbursementResponse, error) {
	data := struct {
		ReferenceID   string `json:"reference_id"`
		ReferenceType string `json:"reference_type"`
	}{
		ReferenceID:   transactionID,
		ReferenceType: "TRANSACTION_ID",
	}
	d, err := json.Marshal(&data)
	if err != nil {
		return nil, err
	}
	r := &request{
		client:   c,
		method:   http.MethodPost,
		endpoint: disbursePaymentsRoute,
		body:     bytes.NewReader(d),
		headers: map[string][]string{
			"Prefer": []string{string(responsePreference)},
		},
	}
	res, err := r.do(ctx)
	if err != nil {
		return nil, err
	}
	if res.status == http.StatusOK {
		r := &orders.FinalizeDisbursementResponse{}
		err = json.NewDecoder(res.body).Decode(r)
		if err != nil {
			return nil, err
		}
		return r, nil
	}

	errorData, err := ioutil.ReadAll(res.body)
	if err != nil {
		return nil, err
	}
	return nil, &BadResponse{
		Status: res.status,
		Body:   string(errorData),
	}
}
<commit_msg>Generalize response for paying for an order<commit_after>package market

import (
	"bytes"
	"context"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/greater-commons/paypal-marketplace/orders"
)

const (
	createOrderRoute           = "/v1/checkout/orders"
	getTransactionContextRoute = "/v1/risk/transaction-contexts"
	disbursePaymentsRoute      = "/v1/payments/referenced-payouts-items"
)

func (c *Client) CreateOrder(ctx context.Context, trackingID string, params *orders.CreateOrderParams) (*orders.CreateOrderResponse, error) {
	d, err := json.Marshal(params)
	if err != nil {
		return nil, err
	}
	r := &request{
		client:   c,
		method:   http.MethodPost,
		endpoint: createOrderRoute,
		body:     bytes.NewReader(d),
		headers: map[string][]string{
			"Paypal-Client-Metadata-Id": []string{trackingID},
		},
	}
	res, err := r.do(ctx)
	if err != nil {
		return nil, err
	}
	if res.status == http.StatusCreated {
		r := &orders.CreateOrderResponse{}
		err = json.NewDecoder(res.body).Decode(r)
		if err != nil {
			return nil, err
		}
		return r, nil
	}
	errorData, err := ioutil.ReadAll(res.body)
	if err != nil {
		return nil, err
	}
	return nil, &BadResponse{
		Status: res.status,
		Body:   string(errorData),
	}
}

func (c *Client) CancelOrder(ctx context.Context, orderID string) error {
	r := &request{
		client:   c,
		method:   http.MethodDelete,
		endpoint: createOrderRoute + "/" + url.PathEscape(orderID),
	}
	res, err := r.do(ctx)
	if err != nil {
		return err
	}
	if res.status == http.StatusNoContent {
		return nil
	}
	errorData, err := ioutil.ReadAll(res.body)
	if err != nil {
		return err
	}
	return &BadResponse{
		Status: res.status,
		Body:   string(errorData),
	}
}

func (c *Client) GetOrderDetails(ctx context.Context, orderID string) (*orders.CreateOrderResponse, error) {
	r := &request{
		client:   c,
		method:   http.MethodGet,
		endpoint: createOrderRoute + "/" + url.PathEscape(orderID),
	}
	res, err := r.do(ctx)
	if err != nil {
		return nil, err
	}
	if res.status == http.StatusOK {
		r := &orders.CreateOrderResponse{}
		err := json.NewDecoder(res.body).Decode(r)
		if err != nil {
			return nil, err
		}
		return r, nil
	}
	errorData, err := ioutil.ReadAll(res.body)
	if err != nil {
		return nil, err
	}
	return nil, &BadResponse{
		Status: res.status,
		Body:   string(errorData),
	}
}

func (c *Client) SaveTransactionContext(ctx context.Context, merchantID, trackingID string, additionalData []orders.KeyValuePair) error {
	body := struct {
		AdditionalData []orders.KeyValuePair `json:"additional_data,omitempty"`
	}{
		AdditionalData: additionalData,
	}
	d, err := json.Marshal(&body)
	if err != nil {
		return err
	}
	r := &request{
		client:   c,
		method:   http.MethodPut,
		endpoint: getTransactionContextRoute + "/" + url.PathEscape(merchantID) + "/" + url.PathEscape(trackingID),
		body:     bytes.NewReader(d),
	}
	res, err := r.do(ctx)
	if err != nil {
		return err
	}
	if res.status == http.StatusOK {
		return nil
	}
	errorData, err := ioutil.ReadAll(res.body)
	if err != nil {
		return err
	}
	return &BadResponse{
		Status: res.status,
		Body:   string(errorData),
	}
}

func (c *Client) PayOrder(ctx context.Context, orderID string, disbursementMode orders.DisbursementModeData) (*orders.PayOrderResponse, error) {
	data := struct {
		DisbursementMode orders.DisbursementModeData `json:"disbursement_mode"`
	}{
		DisbursementMode: disbursementMode,
	}
	d, err := json.Marshal(&data)
	if err != nil {
		return nil, err
	}
	r := &request{
		client:   c,
		method:   http.MethodPost,
		endpoint: createOrderRoute + "/" + url.PathEscape(orderID) + "/pay",
		body:     bytes.NewReader(d),
	}
	res, err := r.do(ctx)
	if err != nil {
		return nil, err
	}
	if res.status >= 200 && res.status < 300 {
		r := &orders.PayOrderResponse{}
		err = json.NewDecoder(res.body).Decode(r)
		if err != nil {
			return nil, err
		}
		return r, nil
	}

	errorData, err := ioutil.ReadAll(res.body)
	if err != nil {
		return nil, err
	}
	return nil, &BadResponse{
		Status: res.status,
		Body:   string(errorData),
	}
}

func (c *Client) FinalizeDisbursement(ctx context.Context, responsePreference orders.ResponsePreferenceData, transactionID string) (*orders.FinalizeDisbursementResponse, error) {
	data := struct {
		ReferenceID   string `json:"reference_id"`
		ReferenceType string `json:"reference_type"`
	}{
		ReferenceID:   transactionID,
		ReferenceType: "TRANSACTION_ID",
	}
	d, err := json.Marshal(&data)
	if err != nil {
		return nil, err
	}
	r := &request{
		client:   c,
		method:   http.MethodPost,
		endpoint: disbursePaymentsRoute,
		body:     bytes.NewReader(d),
		headers: map[string][]string{
			"Prefer": []string{string(responsePreference)},
		},
	}
	res, err := r.do(ctx)
	if err != nil {
		return nil, err
	}
	if res.status == http.StatusOK {
		r := &orders.FinalizeDisbursementResponse{}
		err = json.NewDecoder(res.body).Decode(r)
		if err != nil {
			return nil, err
		}
		return r, nil
	}

	errorData, err := ioutil.ReadAll(res.body)
	if err != nil {
		return nil, err
	}
	return nil, &BadResponse{
		Status: res.status,
		Body:   string(errorData),
	}
}
<|endoftext|>


<commit_before>package orm

import (
	"database/sql"
	"errors"
	"fmt"
	"reflect"

	c "github.com/d0ngw/go/common"
)

// OpTxFunc 在事务中处理的函数
type OpTxFunc func(tx *sql.Tx) (interface{}, error)

// OpCreator Op
type OpCreator interface {
	//NewOp create a new Op
	NewOp() (*Op, error)
}

// Op 数据库操作接口,与sql.DB对应,封装了事务等
type Op struct {
	pool           *Pool          //数据连接
	tx             *sql.Tx        //事务
	txDone         bool           //事务是否结束
	rollbackOnly   bool           //是否只回滚
	transDepth     int            //调用的深度
	sharDBSerevcie ShardDBService //分片服务
}

// DB sql.DB
func (p *Op) DB() *sql.DB {
	return p.pool.db
}

// Pool pool
func (p *Op) Pool() *Pool {
	return p.pool
}

// PoolName name of pool
func (p *Op) PoolName() string {
	return p.pool.name
}

// SetupTableShard use op pool setup entity table shard
func (p *Op) SetupTableShard(entity Entity, ruleName string) error {
	if p.sharDBSerevcie == nil {
		return errors.New("no shard db service")
	}
	poolName, err := p.sharDBSerevcie.setupTableShard(entity, ruleName)
	if err != nil {
		return err
	}
	if poolName != p.PoolName() {
		return fmt.Errorf("op.PoolName %s != entity.PoolName %s", p.PoolName(), poolName)
	}
	return nil
}

func (p *Op) close() {
	p.tx = nil
	p.rollbackOnly = false
	p.transDepth = 0
}

//检查事务的状态
func (p *Op) checkTransStatus() error {
	if p.txDone {
		return sql.ErrTxDone
	}
	if p.tx == nil {
		return NewDBError(nil, "Not begin transaction")
	}
	return nil
}

func (p *Op) incrTransDepth() {
	p.transDepth = p.transDepth + 1
	c.Debugf("p.tranDepth:%v", p.transDepth)
}

func (p *Op) decrTransDepth() error {
	p.transDepth = p.transDepth - 1
	c.Debugf("p.tranDepth:%v", p.transDepth)
	if p.transDepth < 0 {
		return NewDBError(nil, "Too many invoke commit or rollback")
	}
	return nil
}

//结束事务
func (p *Op) finishTrans() error {
	if err := p.checkTransStatus(); err != nil {
		return err
	}
	if err := p.decrTransDepth(); err != nil {
		return err
	}
	if p.transDepth > 0 {
		return nil
	}
	defer p.close()
	p.txDone = true
	if p.rollbackOnly {
		c.Debugf("Rollback")
		return p.tx.Rollback()
	}
	c.Debugf("Commit")
	return p.tx.Commit()
}

// BeginTx 开始事务,支持简单的嵌套调用,如果已经开始了事务,则直接返回成功
func (p *Op) BeginTx() (err error) {
	p.incrTransDepth()
	if p.tx != nil {
		return nil //事务已经开启
	}
	if tx, err := p.DB().Begin(); err == nil {
		p.tx = tx
		return nil
	}
	return err
}

// Commit 提交事务
func (p *Op) Commit() error {
	return p.finishTrans()
}

// Rollback 回滚事务
func (p *Op) Rollback() error {
	p.SetRollbackOnly(true)
	return p.finishTrans()
}

// SetRollbackOnly 设置只回滚
func (p *Op) SetRollbackOnly(rollback bool) {
	p.rollbackOnly = rollback
}

// IsRollbackOnly 是否只回滚
func (p *Op) IsRollbackOnly() bool {
	return p.rollbackOnly
}

// DoInTrans 在事务中执行
func (p *Op) DoInTrans(peration OpTxFunc) (rt interface{}, err error) {
	if err := p.BeginTx(); err != nil {
		return nil, err
	}
	var succ = false
	//结束事务
	defer func() {
		if !succ {
			p.SetRollbackOnly(true)
		}
		transErr := p.finishTrans()
		if transErr != nil {
			c.Errorf("Finish transaction err:%v", transErr)
			rt = nil
			err = transErr
		}
	}()
	rt, err = peration(p.tx)
	if err != nil {
		c.Errorf("Operation fail:%v", err)
		succ = false
	} else {
		succ = true
	}
	return
}

//查找实体对应的模型元
func findEntityMeta(entity Entity) *meta {
	_, _, typ := extract(entity)
	modelMeta := findMeta(typ)
	if modelMeta == nil {
		panic(NewDBErrorf(nil, "Can't find modelMeta for:%v ", typ))
	}
	return modelMeta
}

// Add 添加实体
func Add(op *Op, entity Entity) error {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		return modelMeta.insertFunc(op.tx, entity)
	}
	return modelMeta.insertFunc(op.DB(), entity)
}

// Update 更新实体
func Update(op *Op, entity Entity) (bool, error) {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		bvalue, err := modelMeta.updateFunc(op.tx, entity)
		if err != nil {
			return false, err
		}
		return reflect.ValueOf(bvalue).Bool(), nil
	}
	return modelMeta.updateFunc(op.DB(), entity)
}

// UpdateExcludeColumns 更新除columns之外的字段
func UpdateExcludeColumns(op *Op, entity Entity, columns ...string) (bool, error) {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		bvalue, err := modelMeta.updateExcludeColumnsFunc(op.tx, entity, columns...)
		if err != nil {
			return false, err
		}
		return reflect.ValueOf(bvalue).Bool(), nil
	}
	return modelMeta.updateExcludeColumnsFunc(op.DB(), entity, columns...)
}

// UpdateColumns 更新列
func UpdateColumns(op *Op, entity Entity, columns string, condition string, params ...interface{}) (int64, error) {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		return modelMeta.updateColumnsFunc(op.tx, entity, columns, condition, params)
	}
	return modelMeta.updateColumnsFunc(op.DB(), entity, columns, condition, params)
}

// Get 根据ID查询实体
func Get(op *Op, entity Entity, id interface{}) (Entity, error) {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		e, err := modelMeta.getFunc(op.tx, entity, id)
		if e == nil || err != nil {
			return nil, err
		}
		return e.(Entity), nil
	}
	return modelMeta.getFunc(op.DB(), entity, id)
}

// Query 根据条件查询实体
func Query(op *Op, entity Entity, condition string, params ...interface{}) ([]Entity, error) {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		return modelMeta.entityQueryFunc(op.tx, entity, condition, params)
	}
	return modelMeta.entityQueryFunc(op.DB(), entity, condition, params)
}

// QueryColumns 根据条件查询columns指定的字段
func QueryColumns(op *Op, entity Entity, columns []string, condition string, params ...interface{}) ([]Entity, error) {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		return modelMeta.entityQueryColumnFunc(op.tx, entity, columns, condition, params)
	}
	return modelMeta.entityQueryColumnFunc(op.DB(), entity, columns, condition, params)
}

type count struct {
	Count int64
}

// QueryCount 根据条件查询条数
func QueryCount(op *Op, entity Entity, column string, condition string, params ...interface{}) (num int64, err error) {
	modelMeta := findEntityMeta(entity)
	columns := []string{"count(" + column + ")"}
	var counts []*count
	if op.tx != nil {
		err = modelMeta.clumnsQueryFunc(op.tx, entity, &counts, columns, condition, params)
	} else {
		err = modelMeta.clumnsQueryFunc(op.DB(), entity, &counts, columns, condition, params)
	}
	if err != nil {
		return
	}
	if len(counts) > 0 {
		num = counts[0].Count
	}
	return
}

// QueryColumnsForDestSlice 根据条件查询数据,结果保存到destSlicePtr
func QueryColumnsForDestSlice(op *Op, entity Entity, destSlicePtr interface{}, columns []string, condition string, params ...interface{}) (err error) {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		err = modelMeta.clumnsQueryFunc(op.tx, entity, destSlicePtr, columns, condition, params)
	} else {
		err = modelMeta.clumnsQueryFunc(op.DB(), entity, destSlicePtr, columns, condition, params)
	}
	if err != nil {
		return
	}
	return
}

// Del 根据ID删除实体
func Del(op *Op, entity Entity, id interface{}) (bool, error) {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		return modelMeta.delEFunc(op.tx, entity, id)
	}
	return modelMeta.delEFunc(op.DB(), entity, id)
}

// DelByCondition 根据条件删除
func DelByCondition(op *Op, entity Entity, condition string, params ...interface{}) (int64, error) {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		return modelMeta.delFunc(op.tx, entity, condition, params)
	}
	return modelMeta.delFunc(op.DB(), entity, condition, params)
}

// AddOrUpdate 添加或者更新实体(如果id已经存在),只支持MySql
func AddOrUpdate(op *Op, entity Entity) (int64, error) {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		return modelMeta.insertOrUpdateFunc(op.tx, entity)
	}
	return modelMeta.insertOrUpdateFunc(op.DB(), entity)
}
<commit_msg>fix transaction<commit_after>package orm

import (
	"database/sql"
	"errors"
	"fmt"
	"reflect"

	c "github.com/d0ngw/go/common"
)

// OpTxFunc 在事务中处理的函数
type OpTxFunc func(tx *sql.Tx) (interface{}, error)

// OpCreator Op
type OpCreator interface {
	//NewOp create a new Op
	NewOp() (*Op, error)
}

// Op 数据库操作接口,与sql.DB对应,封装了事务等
type Op struct {
	pool           *Pool          //数据连接
	tx             *sql.Tx        //事务
	txDone         bool           //事务是否结束
	rollbackOnly   bool           //是否只回滚
	transDepth     int            //调用的深度
	sharDBSerevcie ShardDBService //分片服务
}

// DB sql.DB
func (p *Op) DB() *sql.DB {
	return p.pool.db
}

// Pool pool
func (p *Op) Pool() *Pool {
	return p.pool
}

// PoolName name of pool
func (p *Op) PoolName() string {
	return p.pool.name
}

// SetupTableShard use op pool setup entity table shard
func (p *Op) SetupTableShard(entity Entity, ruleName string) error {
	if p.sharDBSerevcie == nil {
		return errors.New("no shard db service")
	}
	poolName, err := p.sharDBSerevcie.setupTableShard(entity, ruleName)
	if err != nil {
		return err
	}
	if poolName != p.PoolName() {
		return fmt.Errorf("op.PoolName %s != entity.PoolName %s", p.PoolName(), poolName)
	}
	return nil
}

func (p *Op) close() {
	p.tx = nil
	p.rollbackOnly = false
	p.transDepth = 0
}

//检查事务的状态
func (p *Op) checkTransStatus() error {
	if p.txDone {
		return sql.ErrTxDone
	}
	if p.tx == nil {
		return NewDBError(nil, "Not begin transaction")
	}
	return nil
}

func (p *Op) incrTransDepth() {
	p.transDepth = p.transDepth + 1
	c.Debugf("p.tranDepth:%v", p.transDepth)
}

func (p *Op) decrTransDepth() error {
	p.transDepth = p.transDepth - 1
	c.Debugf("p.tranDepth:%v", p.transDepth)
	if p.transDepth < 0 {
		return NewDBError(nil, "Too many invoke commit or rollback")
	}
	return nil
}

//结束事务
func (p *Op) finishTrans() error {
	if err := p.checkTransStatus(); err != nil {
		return err
	}
	if err := p.decrTransDepth(); err != nil {
		return err
	}
	if p.transDepth > 0 {
		return nil
	}
	defer p.close()
	p.txDone = true
	if p.rollbackOnly {
		c.Debugf("Rollback")
		return p.tx.Rollback()
	}
	c.Debugf("Commit")
	return p.tx.Commit()
}

// BeginTx 开始事务,支持简单的嵌套调用,如果已经开始了事务,则直接返回成功
func (p *Op) BeginTx() (err error) {
	p.incrTransDepth()
	if p.tx != nil {
		return nil //事务已经开启
	}
	if tx, err := p.DB().Begin(); err == nil {
		p.tx = tx
		p.txDone = false
		return nil
	}
	return err
}

// Commit 提交事务
func (p *Op) Commit() error {
	return p.finishTrans()
}

// Rollback 回滚事务
func (p *Op) Rollback() error {
	p.SetRollbackOnly(true)
	return p.finishTrans()
}

// SetRollbackOnly 设置只回滚
func (p *Op) SetRollbackOnly(rollback bool) {
	p.rollbackOnly = rollback
}

// IsRollbackOnly 是否只回滚
func (p *Op) IsRollbackOnly() bool {
	return p.rollbackOnly
}

// DoInTrans 在事务中执行
func (p *Op) DoInTrans(peration OpTxFunc) (rt interface{}, err error) {
	if err := p.BeginTx(); err != nil {
		return nil, err
	}
	var succ = false
	//结束事务
	defer func() {
		if !succ {
			p.SetRollbackOnly(true)
		}
		transErr := p.finishTrans()
		if transErr != nil {
			c.Errorf("Finish transaction err:%v", transErr)
			rt = nil
			err = transErr
		}
	}()
	rt, err = peration(p.tx)
	if err != nil {
		c.Errorf("Operation fail:%v", err)
		succ = false
	} else {
		succ = true
	}
	return
}

//查找实体对应的模型元
func findEntityMeta(entity Entity) *meta {
	_, _, typ := extract(entity)
	modelMeta := findMeta(typ)
	if modelMeta == nil {
		panic(NewDBErrorf(nil, "Can't find modelMeta for:%v ", typ))
	}
	return modelMeta
}

// Add 添加实体
func Add(op *Op, entity Entity) error {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		return modelMeta.insertFunc(op.tx, entity)
	}
	return modelMeta.insertFunc(op.DB(), entity)
}

// Update 更新实体
func Update(op *Op, entity Entity) (bool, error) {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		bvalue, err := modelMeta.updateFunc(op.tx, entity)
		if err != nil {
			return false, err
		}
		return reflect.ValueOf(bvalue).Bool(), nil
	}
	return modelMeta.updateFunc(op.DB(), entity)
}

// UpdateExcludeColumns 更新除columns之外的字段
func UpdateExcludeColumns(op *Op, entity Entity, columns ...string) (bool, error) {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		bvalue, err := modelMeta.updateExcludeColumnsFunc(op.tx, entity, columns...)
		if err != nil {
			return false, err
		}
		return reflect.ValueOf(bvalue).Bool(), nil
	}
	return modelMeta.updateExcludeColumnsFunc(op.DB(), entity, columns...)
}

// UpdateColumns 更新列
func UpdateColumns(op *Op, entity Entity, columns string, condition string, params ...interface{}) (int64, error) {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		return modelMeta.updateColumnsFunc(op.tx, entity, columns, condition, params)
	}
	return modelMeta.updateColumnsFunc(op.DB(), entity, columns, condition, params)
}

// Get 根据ID查询实体
func Get(op *Op, entity Entity, id interface{}) (Entity, error) {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		e, err := modelMeta.getFunc(op.tx, entity, id)
		if e == nil || err != nil {
			return nil, err
		}
		return e.(Entity), nil
	}
	return modelMeta.getFunc(op.DB(), entity, id)
}

// Query 根据条件查询实体
func Query(op *Op, entity Entity, condition string, params ...interface{}) ([]Entity, error) {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		return modelMeta.entityQueryFunc(op.tx, entity, condition, params)
	}
	return modelMeta.entityQueryFunc(op.DB(), entity, condition, params)
}

// QueryColumns 根据条件查询columns指定的字段
func QueryColumns(op *Op, entity Entity, columns []string, condition string, params ...interface{}) ([]Entity, error) {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		return modelMeta.entityQueryColumnFunc(op.tx, entity, columns, condition, params)
	}
	return modelMeta.entityQueryColumnFunc(op.DB(), entity, columns, condition, params)
}

type count struct {
	Count int64
}

// QueryCount 根据条件查询条数
func QueryCount(op *Op, entity Entity, column string, condition string, params ...interface{}) (num int64, err error) {
	modelMeta := findEntityMeta(entity)
	columns := []string{"count(" + column + ")"}
	var counts []*count
	if op.tx != nil {
		err = modelMeta.clumnsQueryFunc(op.tx, entity, &counts, columns, condition, params)
	} else {
		err = modelMeta.clumnsQueryFunc(op.DB(), entity, &counts, columns, condition, params)
	}
	if err != nil {
		return
	}
	if len(counts) > 0 {
		num = counts[0].Count
	}
	return
}

// QueryColumnsForDestSlice 根据条件查询数据,结果保存到destSlicePtr
func QueryColumnsForDestSlice(op *Op, entity Entity, destSlicePtr interface{}, columns []string, condition string, params ...interface{}) (err error) {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		err = modelMeta.clumnsQueryFunc(op.tx, entity, destSlicePtr, columns, condition, params)
	} else {
		err = modelMeta.clumnsQueryFunc(op.DB(), entity, destSlicePtr, columns, condition, params)
	}
	if err != nil {
		return
	}
	return
}

// Del 根据ID删除实体
func Del(op *Op, entity Entity, id interface{}) (bool, error) {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		return modelMeta.delEFunc(op.tx, entity, id)
	}
	return modelMeta.delEFunc(op.DB(), entity, id)
}

// DelByCondition 根据条件删除
func DelByCondition(op *Op, entity Entity, condition string, params ...interface{}) (int64, error) {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		return modelMeta.delFunc(op.tx, entity, condition, params)
	}
	return modelMeta.delFunc(op.DB(), entity, condition, params)
}

// AddOrUpdate 添加或者更新实体(如果id已经存在),只支持MySql
func AddOrUpdate(op *Op, entity Entity) (int64, error) {
	modelMeta := findEntityMeta(entity)
	if op.tx != nil {
		return modelMeta.insertOrUpdateFunc(op.tx, entity)
	}
	return modelMeta.insertOrUpdateFunc(op.DB(), entity)
}
<|endoftext|>


<commit_before>package ui

import (
	"fmt"
	"strings"

	"github.com/elves/elvish/parse"
	"github.com/elves/elvish/util"
	"github.com/xiaq/persistent/hash"
	"github.com/xiaq/persistent/vector"
)

// Styled is a piece of text with style.
type Styled struct {
	Text   string
	Styles Styles
}

var styleTranslationTable = map[string]string{
	"bold":       "1",
	"dim":        "2",
	"italic":     "3",
	"underlined": "4",
	"blink":      "5",
	"inverse":    "7",

	"black":        "30",
	"red":          "31",
	"green":        "32",
	"yellow":       "33",
	"blue":         "34",
	"magenta":      "35",
	"cyan":         "36",
	"lightgray":    "37",
	"gray":         "90",
	"lightred":     "91",
	"lightgreen":   "92",
	"lightyellow":  "93",
	"lightblue":    "94",
	"lightmagenta": "95",
	"lightcyan":    "96",
	"white":        "97",

	"bg-default":      "49",
	"bg-black":        "40",
	"bg-red":          "41",
	"bg-green":        "42",
	"bg-yellow":       "43",
	"bg-blue":         "44",
	"bg-magenta":      "45",
	"bg-cyan":         "46",
	"bg-lightgray":    "47",
	"bg-gray":         "100",
	"bg-lightred":     "101",
	"bg-lightgreen":   "102",
	"bg-lightyellow":  "103",
	"bg-lightblue":    "104",
	"bg-lightmagenta": "105",
	"bg-lightcyan":    "106",
	"bg-white":        "107",
}

func Unstyled(s string) Styled {
	return Styled{s, Styles{}}
}

func (s *Styled) Kind() string {
	return "styled"
}

func (s *Styled) Equal(a interface{}) bool {
	rhs, ok := a.(*Styled)
	if !ok {
		return false
	}
	return s.Text == rhs.Text && s.Styles.Eq(rhs.Styles)
}

func (s *Styled) Hash() uint32 {
	h := hash.DJBInit
	h = hash.DJBCombine(h, hash.String(s.Text))
	h = hash.DJBCombine(h, s.Styles.Hash())
	return h
}

func (s *Styled) String() string {
	return "\033[" + s.Styles.String() + "m" + s.Text + "\033[m"
}

func (s *Styled) Repr(indent int) string {
	quotedStyles := make([]string, len(s.Styles))
	for i, st := range s.Styles {
		quotedStyles[i] = parse.Quote(st)
	}
	return fmt.Sprintf("(edit:styled %s [%s]",
		parse.Quote(s.Text), strings.Join(quotedStyles, " "))
}

func (s *Styled) Index(k interface{}) (interface{}, bool) {
	switch k {
	case "text":
		return s.Text, true
	case "styles":
		li := vector.Empty
		for _, st := range s.Styles {
			li = li.Cons(st)
		}
		return li, true
	default:
		return nil, false
	}
}

func (s *Styled) IterateKeys(f func(interface{}) bool) {
	util.Feed(f, "text", "styles")
}

type Styles []string

func (ss Styles) Eq(rhs Styles) bool {
	if len(ss) != len(rhs) {
		return false
	}
	for i, s := range ss {
		if s != rhs[i] {
			return false
		}
	}
	return true
}

func (ss Styles) Hash() uint32 {
	h := hash.DJBInit
	for _, s := range ss {
		h = hash.DJBCombine(h, hash.String(s))
	}
	return h
}

func JoinStyles(so Styles, st ...Styles) Styles {
	for _, v := range st {
		so = append(so, v...)
	}

	return so
}

func TranslateStyle(s string) string {
	v, ok := styleTranslationTable[s]
	if ok {
		return v
	}
	return s
}

func StylesFromString(s string) Styles {
	var st Styles
	for _, v := range strings.Split(s, ";") {
		st = append(st, v)
	}

	return st
}

func (s Styles) String() string {
	var o string
	for i, v := range s {
		if len(v) > 0 {
			if i > 0 {
				o += ";"
			}
			o += TranslateStyle(v)
		}
	}

	return o
}
<commit_msg>edit/ui: Document that ui.Styled is deprecated.<commit_after>package ui

import (
	"fmt"
	"strings"

	"github.com/elves/elvish/parse"
	"github.com/elves/elvish/util"
	"github.com/xiaq/persistent/hash"
	"github.com/xiaq/persistent/vector"
)

// Styled is a piece of text with style. This type is deprecated in favor of
// styled.Segment and styled.Text, and existing code that still uses it should
// eventually be migrated to use the newer types.
type Styled struct {
	Text   string
	Styles Styles
}

var styleTranslationTable = map[string]string{
	"bold":       "1",
	"dim":        "2",
	"italic":     "3",
	"underlined": "4",
	"blink":      "5",
	"inverse":    "7",

	"black":        "30",
	"red":          "31",
	"green":        "32",
	"yellow":       "33",
	"blue":         "34",
	"magenta":      "35",
	"cyan":         "36",
	"lightgray":    "37",
	"gray":         "90",
	"lightred":     "91",
	"lightgreen":   "92",
	"lightyellow":  "93",
	"lightblue":    "94",
	"lightmagenta": "95",
	"lightcyan":    "96",
	"white":        "97",

	"bg-default":      "49",
	"bg-black":        "40",
	"bg-red":          "41",
	"bg-green":        "42",
	"bg-yellow":       "43",
	"bg-blue":         "44",
	"bg-magenta":      "45",
	"bg-cyan":         "46",
	"bg-lightgray":    "47",
	"bg-gray":         "100",
	"bg-lightred":     "101",
	"bg-lightgreen":   "102",
	"bg-lightyellow":  "103",
	"bg-lightblue":    "104",
	"bg-lightmagenta": "105",
	"bg-lightcyan":    "106",
	"bg-white":        "107",
}

func Unstyled(s string) Styled {
	return Styled{s, Styles{}}
}

func (s *Styled) Kind() string {
	return "styled"
}

func (s *Styled) Equal(a interface{}) bool {
	rhs, ok := a.(*Styled)
	if !ok {
		return false
	}
	return s.Text == rhs.Text && s.Styles.Eq(rhs.Styles)
}

func (s *Styled) Hash() uint32 {
	h := hash.DJBInit
	h = hash.DJBCombine(h, hash.String(s.Text))
	h = hash.DJBCombine(h, s.Styles.Hash())
	return h
}

func (s *Styled) String() string {
	return "\033[" + s.Styles.String() + "m" + s.Text + "\033[m"
}

func (s *Styled) Repr(indent int) string {
	quotedStyles := make([]string, len(s.Styles))
	for i, st := range s.Styles {
		quotedStyles[i] = parse.Quote(st)
	}
	return fmt.Sprintf("(edit:styled %s [%s]",
		parse.Quote(s.Text), strings.Join(quotedStyles, " "))
}

func (s *Styled) Index(k interface{}) (interface{}, bool) {
	switch k {
	case "text":
		return s.Text, true
	case "styles":
		li := vector.Empty
		for _, st := range s.Styles {
			li = li.Cons(st)
		}
		return li, true
	default:
		return nil, false
	}
}

func (s *Styled) IterateKeys(f func(interface{}) bool) {
	util.Feed(f, "text", "styles")
}

type Styles []string

func (ss Styles) Eq(rhs Styles) bool {
	if len(ss) != len(rhs) {
		return false
	}
	for i, s := range ss {
		if s != rhs[i] {
			return false
		}
	}
	return true
}

func (ss Styles) Hash() uint32 {
	h := hash.DJBInit
	for _, s := range ss {
		h = hash.DJBCombine(h, hash.String(s))
	}
	return h
}

func JoinStyles(so Styles, st ...Styles) Styles {
	for _, v := range st {
		so = append(so, v...)
	}

	return so
}

func TranslateStyle(s string) string {
	v, ok := styleTranslationTable[s]
	if ok {
		return v
	}
	return s
}

func StylesFromString(s string) Styles {
	var st Styles
	for _, v := range strings.Split(s, ";") {
		st = append(st, v)
	}

	return st
}

func (s Styles) String() string {
	var o string
	for i, v := range s {
		if len(v) > 0 {
			if i > 0 {
				o += ";"
			}
			o += TranslateStyle(v)
		}
	}

	return o
}
<|endoftext|>


<commit_before>package s3

import (
	"bytes"
	"io"
	"net/http"
	"path"
	"restic"
	"strings"

	"restic/backend"
	"restic/errors"

	"github.com/minio/minio-go"

	"restic/debug"
)

const connLimit = 40

// s3 is a backend which stores the data on an S3 endpoint.
type s3 struct {
	client     *minio.Client
	connChan   chan struct{}
	bucketname string
	prefix     string
}

// Open opens the S3 backend at bucket and region. The bucket is created if it
// does not exist yet.
func Open(cfg Config) (restic.Backend, error) {
	debug.Log("open, config %#v", cfg)

	client, err := minio.New(cfg.Endpoint, cfg.KeyID, cfg.Secret, !cfg.UseHTTP)
	if err != nil {
		return nil, errors.Wrap(err, "minio.New")
	}

	be := &s3{client: client, bucketname: cfg.Bucket, prefix: cfg.Prefix}

	tr := &http.Transport{MaxIdleConnsPerHost: connLimit}
	client.SetCustomTransport(tr)

	be.createConnections()

	found, err := client.BucketExists(cfg.Bucket)
	if err != nil {
		debug.Log("BucketExists(%v) returned err %v", cfg.Bucket, err)
		return nil, errors.Wrap(err, "client.BucketExists")
	}

	if !found {
		// create new bucket with default ACL in default region
		err = client.MakeBucket(cfg.Bucket, "")
		if err != nil {
			return nil, errors.Wrap(err, "client.MakeBucket")
		}
	}

	return be, nil
}

func (be *s3) s3path(h restic.Handle) string {
	if h.Type == restic.ConfigFile {
		return path.Join(be.prefix, string(h.Type))
	}
	return path.Join(be.prefix, string(h.Type), h.Name)
}

func (be *s3) createConnections() {
	be.connChan = make(chan struct{}, connLimit)
	for i := 0; i < connLimit; i++ {
		be.connChan <- struct{}{}
	}
}

// Location returns this backend's location (the bucket name).
func (be *s3) Location() string {
	return be.bucketname
}

// Save stores data in the backend at the handle.
func (be *s3) Save(h restic.Handle, rd io.Reader) (err error) {
	if err := h.Valid(); err != nil {
		return err
	}

	debug.Log("Save %v", h)

	objName := be.s3path(h)

	// Check key does not already exist
	_, err = be.client.StatObject(be.bucketname, objName)
	if err == nil {
		debug.Log("%v already exists", h)
		return errors.New("key already exists")
	}

	<-be.connChan
	defer func() {
		be.connChan <- struct{}{}
	}()

	debug.Log("PutObject(%v, %v)",
		be.bucketname, objName)
	n, err := be.client.PutObject(be.bucketname, objName, rd, "binary/octet-stream")
	debug.Log("%v -> %v bytes, err %#v", objName, n, err)

	return errors.Wrap(err, "client.PutObject")
}

// wrapReader wraps an io.ReadCloser to run an additional function on Close.
type wrapReader struct {
	io.ReadCloser
	f func()
}

func (wr wrapReader) Close() error {
	err := wr.ReadCloser.Close()
	wr.f()
	return err
}

// Load returns a reader that yields the contents of the file at h at the
// given offset. If length is nonzero, only a portion of the file is
// returned. rd must be closed after use.
func (be *s3) Load(h restic.Handle, length int, offset int64) (io.ReadCloser, error) {
	debug.Log("Load %v, length %v, offset %v", h, length, offset)
	if err := h.Valid(); err != nil {
		return nil, err
	}

	if offset < 0 {
		return nil, errors.New("offset is negative")
	}

	if length < 0 {
		return nil, errors.Errorf("invalid length %d", length)
	}

	var obj *minio.Object

	objName := be.s3path(h)

	// get token for connection
	<-be.connChan

	obj, err := be.client.GetObject(be.bucketname, objName)
	if err != nil {
		debug.Log("  err %v", err)

		// return token
		be.connChan <- struct{}{}

		return nil, errors.Wrap(err, "client.GetObject")
	}

	// if we're going to read the whole object, just pass it on.
	if length == 0 {
		debug.Log("Load %v: pass on object", h)

		_, err = obj.Seek(offset, 0)
		if err != nil {
			_ = obj.Close()

			// return token
			be.connChan <- struct{}{}

			return nil, errors.Wrap(err, "obj.Seek")
		}

		rd := wrapReader{
			ReadCloser: obj,
			f: func() {
				debug.Log("Close()")
				// return token
				be.connChan <- struct{}{}
			},
		}
		return rd, nil
	}

	defer func() {
		// return token
		be.connChan <- struct{}{}
	}()

	// otherwise use a buffer with ReadAt
	info, err := obj.Stat()
	if err != nil {
		_ = obj.Close()
		return nil, errors.Wrap(err, "obj.Stat")
	}

	if offset > info.Size {
		_ = obj.Close()
		return nil, errors.New("offset larger than file size")
	}

	l := int64(length)
	if offset+l > info.Size {
		l = info.Size - offset
	}

	buf := make([]byte, l)
	n, err := obj.ReadAt(buf, offset)
	debug.Log("Load %v: use buffer with ReadAt: %v, %v", h, n, err)
	if err == io.EOF {
		debug.Log("Load %v: shorten buffer %v -> %v", h, len(buf), n)
		buf = buf[:n]
		err = nil
	}

	if err != nil {
		_ = obj.Close()
		return nil, errors.Wrap(err, "obj.ReadAt")
	}

	return backend.Closer{Reader: bytes.NewReader(buf)}, nil
}

// Stat returns information about a blob.
func (be *s3) Stat(h restic.Handle) (bi restic.FileInfo, err error) {
	debug.Log("%v", h)

	objName := be.s3path(h)
	var obj *minio.Object

	obj, err = be.client.GetObject(be.bucketname, objName)
	if err != nil {
		debug.Log("GetObject() err %v", err)
		return restic.FileInfo{}, errors.Wrap(err, "client.GetObject")
	}

	// make sure that the object is closed properly.
	defer func() {
		e := obj.Close()
		if err == nil {
			err = errors.Wrap(e, "Close")
		}
	}()

	fi, err := obj.Stat()
	if err != nil {
		debug.Log("Stat() err %v", err)
		return restic.FileInfo{}, errors.Wrap(err, "Stat")
	}

	return restic.FileInfo{Size: fi.Size}, nil
}

// Test returns true if a blob of the given type and name exists in the backend.
func (be *s3) Test(h restic.Handle) (bool, error) {
	found := false
	objName := be.s3path(h)
	_, err := be.client.StatObject(be.bucketname, objName)
	if err == nil {
		found = true
	}

	// If error, then not found
	return found, nil
}

// Remove removes the blob with the given name and type.
func (be *s3) Remove(h restic.Handle) error {
	objName := be.s3path(h)
	err := be.client.RemoveObject(be.bucketname, objName)
	debug.Log("Remove(%v) -> err %v", h, err)
	return errors.Wrap(err, "client.RemoveObject")
}

// List returns a channel that yields all names of blobs of type t. A
// goroutine is started for this. If the channel done is closed, sending
// stops.
func (be *s3) List(t restic.FileType, done <-chan struct{}) <-chan string {
	debug.Log("listing %v", t)
	ch := make(chan string)

	prefix := be.s3path(restic.Handle{Type: t}) + "/"

	listresp := be.client.ListObjects(be.bucketname, prefix, true, done)

	go func() {
		defer close(ch)
		for obj := range listresp {
			m := strings.TrimPrefix(obj.Key, prefix)
			if m == "" {
				continue
			}

			select {
			case ch <- m:
			case <-done:
				return
			}
		}
	}()

	return ch
}

// Remove keys for a specified backend type.
func (be *s3) removeKeys(t restic.FileType) error {
	done := make(chan struct{})
	defer close(done)
	for key := range be.List(restic.DataFile, done) {
		err := be.Remove(restic.Handle{Type: restic.DataFile, Name: key})
		if err != nil {
			return err
		}
	}

	return nil
}

// Delete removes all restic keys in the bucket. It will not remove the bucket itself.
func (be *s3) Delete() error {
	alltypes := []restic.FileType{
		restic.DataFile,
		restic.KeyFile,
		restic.LockFile,
		restic.SnapshotFile,
		restic.IndexFile}

	for _, t := range alltypes {
		err := be.removeKeys(t)
		if err != nil {
			return nil
		}
	}

	return be.Remove(restic.Handle{Type: restic.ConfigFile})
}

// Close does nothing
func (be *s3) Close() error { return nil }
<commit_msg>Cache size of last ReadAt on S3 for performance<commit_after>package s3

import (
	"bytes"
	"io"
	"net/http"
	"path"
	"restic"
	"strings"

	"restic/backend"
	"restic/errors"

	"github.com/minio/minio-go"

	"restic/debug"
)

const connLimit = 40

// s3 is a backend which stores the data on an S3 endpoint.
type s3 struct {
	client       *minio.Client
	connChan     chan struct{}
	bucketname   string
	prefix       string
	cacheObjName string
	cacheSize    int64
}

// Open opens the S3 backend at bucket and region. The bucket is created if it
// does not exist yet.
func Open(cfg Config) (restic.Backend, error) {
	debug.Log("open, config %#v", cfg)

	client, err := minio.New(cfg.Endpoint, cfg.KeyID, cfg.Secret, !cfg.UseHTTP)
	if err != nil {
		return nil, errors.Wrap(err, "minio.New")
	}

	be := &s3{client: client, bucketname: cfg.Bucket, prefix: cfg.Prefix}

	tr := &http.Transport{MaxIdleConnsPerHost: connLimit}
	client.SetCustomTransport(tr)

	be.createConnections()

	found, err := client.BucketExists(cfg.Bucket)
	if err != nil {
		debug.Log("BucketExists(%v) returned err %v", cfg.Bucket, err)
		return nil, errors.Wrap(err, "client.BucketExists")
	}

	if !found {
		// create new bucket with default ACL in default region
		err = client.MakeBucket(cfg.Bucket, "")
		if err != nil {
			return nil, errors.Wrap(err, "client.MakeBucket")
		}
	}

	return be, nil
}

func (be *s3) s3path(h restic.Handle) string {
	if h.Type == restic.ConfigFile {
		return path.Join(be.prefix, string(h.Type))
	}
	return path.Join(be.prefix, string(h.Type), h.Name)
}

func (be *s3) createConnections() {
	be.connChan = make(chan struct{}, connLimit)
	for i := 0; i < connLimit; i++ {
		be.connChan <- struct{}{}
	}
}

// Location returns this backend's location (the bucket name).
func (be *s3) Location() string {
	return be.bucketname
}

// Save stores data in the backend at the handle.
func (be *s3) Save(h restic.Handle, rd io.Reader) (err error) {
	if err := h.Valid(); err != nil {
		return err
	}

	debug.Log("Save %v", h)

	objName := be.s3path(h)

	// Check key does not already exist
	_, err = be.client.StatObject(be.bucketname, objName)
	if err == nil {
		debug.Log("%v already exists", h)
		return errors.New("key already exists")
	}

	<-be.connChan
	defer func() {
		be.connChan <- struct{}{}
	}()

	debug.Log("PutObject(%v, %v)",
		be.bucketname, objName)
	n, err := be.client.PutObject(be.bucketname, objName, rd, "binary/octet-stream")
	debug.Log("%v -> %v bytes, err %#v", objName, n, err)

	return errors.Wrap(err, "client.PutObject")
}

// wrapReader wraps an io.ReadCloser to run an additional function on Close.
type wrapReader struct {
	io.ReadCloser
	f func()
}

func (wr wrapReader) Close() error {
	err := wr.ReadCloser.Close()
	wr.f()
	return err
}

// Load returns a reader that yields the contents of the file at h at the
// given offset. If length is nonzero, only a portion of the file is
// returned. rd must be closed after use.
func (be *s3) Load(h restic.Handle, length int, offset int64) (io.ReadCloser, error) {
	debug.Log("Load %v, length %v, offset %v", h, length, offset)
	if err := h.Valid(); err != nil {
		return nil, err
	}

	if offset < 0 {
		return nil, errors.New("offset is negative")
	}

	if length < 0 {
		return nil, errors.Errorf("invalid length %d", length)
	}

	var obj *minio.Object
	var size int64

	objName := be.s3path(h)

	// get token for connection
	<-be.connChan

	obj, err := be.client.GetObject(be.bucketname, objName)
	if err != nil {
		debug.Log("  err %v", err)

		// return token
		be.connChan <- struct{}{}

		return nil, errors.Wrap(err, "client.GetObject")
	}

	// if we're going to read the whole object, just pass it on.
	if length == 0 {
		debug.Log("Load %v: pass on object", h)

		_, err = obj.Seek(offset, 0)
		if err != nil {
			_ = obj.Close()

			// return token
			be.connChan <- struct{}{}

			return nil, errors.Wrap(err, "obj.Seek")
		}

		rd := wrapReader{
			ReadCloser: obj,
			f: func() {
				debug.Log("Close()")
				// return token
				be.connChan <- struct{}{}
			},
		}
		return rd, nil
	}

	defer func() {
		// return token
		be.connChan <- struct{}{}
	}()

	// otherwise use a buffer with ReadAt
	if be.cacheObjName == objName {
		size = be.cacheSize
	} else {
		info, err := obj.Stat()
		if err != nil {
			_ = obj.Close()
			return nil, errors.Wrap(err, "obj.Stat")
		}
		size = info.Size
		be.cacheObjName = objName
		be.cacheSize = size
	}

	if offset > size {
		_ = obj.Close()
		return nil, errors.New("offset larger than file size")
	}

	l := int64(length)
	if offset+l > size {
		l = size - offset
	}

	buf := make([]byte, l)
	n, err := obj.ReadAt(buf, offset)
	debug.Log("Load %v: use buffer with ReadAt: %v, %v", h, n, err)
	if err == io.EOF {
		debug.Log("Load %v: shorten buffer %v -> %v", h, len(buf), n)
		buf = buf[:n]
		err = nil
	}

	if err != nil {
		_ = obj.Close()
		return nil, errors.Wrap(err, "obj.ReadAt")
	}

	return backend.Closer{Reader: bytes.NewReader(buf)}, nil
}

// Stat returns information about a blob.
func (be *s3) Stat(h restic.Handle) (bi restic.FileInfo, err error) {
	debug.Log("%v", h)

	objName := be.s3path(h)
	var obj *minio.Object

	obj, err = be.client.GetObject(be.bucketname, objName)
	if err != nil {
		debug.Log("GetObject() err %v", err)
		return restic.FileInfo{}, errors.Wrap(err, "client.GetObject")
	}

	// make sure that the object is closed properly.
	defer func() {
		e := obj.Close()
		if err == nil {
			err = errors.Wrap(e, "Close")
		}
	}()

	fi, err := obj.Stat()
	if err != nil {
		debug.Log("Stat() err %v", err)
		return restic.FileInfo{}, errors.Wrap(err, "Stat")
	}

	return restic.FileInfo{Size: fi.Size}, nil
}

// Test returns true if a blob of the given type and name exists in the backend.
func (be *s3) Test(h restic.Handle) (bool, error) {
	found := false
	objName := be.s3path(h)
	_, err := be.client.StatObject(be.bucketname, objName)
	if err == nil {
		found = true
	}

	// If error, then not found
	return found, nil
}

// Remove removes the blob with the given name and type.
func (be *s3) Remove(h restic.Handle) error {
	objName := be.s3path(h)
	err := be.client.RemoveObject(be.bucketname, objName)
	debug.Log("Remove(%v) -> err %v", h, err)
	return errors.Wrap(err, "client.RemoveObject")
}

// List returns a channel that yields all names of blobs of type t. A
// goroutine is started for this. If the channel done is closed, sending
// stops.
func (be *s3) List(t restic.FileType, done <-chan struct{}) <-chan string {
	debug.Log("listing %v", t)
	ch := make(chan string)

	prefix := be.s3path(restic.Handle{Type: t}) + "/"

	listresp := be.client.ListObjects(be.bucketname, prefix, true, done)

	go func() {
		defer close(ch)
		for obj := range listresp {
			m := strings.TrimPrefix(obj.Key, prefix)
			if m == "" {
				continue
			}

			select {
			case ch <- m:
			case <-done:
				return
			}
		}
	}()

	return ch
}

// Remove keys for a specified backend type.
func (be *s3) removeKeys(t restic.FileType) error {
	done := make(chan struct{})
	defer close(done)
	for key := range be.List(restic.DataFile, done) {
		err := be.Remove(restic.Handle{Type: restic.DataFile, Name: key})
		if err != nil {
			return err
		}
	}

	return nil
}

// Delete removes all restic keys in the bucket. It will not remove the bucket itself.
func (be *s3) Delete() error {
	alltypes := []restic.FileType{
		restic.DataFile,
		restic.KeyFile,
		restic.LockFile,
		restic.SnapshotFile,
		restic.IndexFile}

	for _, t := range alltypes {
		err := be.removeKeys(t)
		if err != nil {
			return nil
		}
	}

	return be.Remove(restic.Handle{Type: restic.ConfigFile})
}

// Close does nothing
func (be *s3) Close() error { return nil }
<|endoftext|>


<commit_before>package pcap

import (
	"encoding/binary"
	"fmt"
	"reflect"
	"strings"
	"time"
)

type PacketTime struct {
	Sec  int32
	Usec int32
}

// Packet is a single packet parsed from a pcap file.
type Packet struct {
	// porting from 'pcap_pkthdr' struct
	Time   time.Time // packet send/receive time
	Caplen uint32    // bytes stored in the file (caplen <= len)
	Len    uint32    // bytes sent/received

	Data []byte // packet data

	Type    int // protocol type, see LINKTYPE_*
	DestMac uint64
	SrcMac  uint64

	Headers []interface{} // decoded headers, in order
	Payload []byte        // remaining non-header bytes
}

// Decode decodes the headers of a Packet.
func (p *Packet) Decode() {

	p.Type = int(binary.BigEndian.Uint16(p.Data[12:14]))
	p.DestMac = decodemac(p.Data[0:6])
	p.SrcMac = decodemac(p.Data[6:12])
	p.Payload = p.Data[14:]

	switch p.Type {
	case TYPE_IP:
		p.decodeIp()
	case TYPE_IP6:
		p.decodeIp6()
	case TYPE_ARP:
		p.decodeArp()
	}
}

func (p *Packet) headerString(headers []interface{}) string {
	// If there's just one header, return that.
	if len(headers) == 1 {
		if hdr, ok := headers[0].(fmt.Stringer); ok {
			return hdr.String()
		}
	}
	// If there are two headers (IPv4/IPv6 -> TCP/UDP/IP..)
	if len(headers) == 2 {
		// Commonly the first header is an address.
		if addr, ok := p.Headers[0].(addrHdr); ok {
			if hdr, ok := p.Headers[1].(addrStringer); ok {
				return fmt.Sprintf("%s %s", p.Time, hdr.String(addr))
			}
		}
	}
	// For IP in IP, we do a recursive call.
	if len(headers) >= 2 {
		if addr, ok := headers[0].(addrHdr); ok {
			if _, ok := headers[1].(addrHdr); ok {
				return fmt.Sprintf("%s > %s IP in IP: ",
					addr.SrcAddr(), addr.DestAddr(), p.headerString(headers[1:]))
			}
		}
	}

	var typeNames []string
	for _, hdr := range headers {
		typeNames = append(typeNames, reflect.TypeOf(hdr).String())
	}

	return fmt.Sprintf("unknown [%s]", strings.Join(typeNames, ","))
}

// String prints a one-line representation of the packet header.
// The output is suitable for use in a tcpdump program.
func (p *Packet) String() string {
	// If there are no headers, print "unsupported protocol".
	if len(p.Headers) == 0 {
		return fmt.Sprintf("%s unsupported protocol %d", p.Time, int(p.Type))
	}
	return fmt.Sprintf("%s %s", p.Time, p.headerString(p.Headers))
}

func (p *Packet) decodeArp() {
	pkt := p.Payload
	arp := new(Arphdr)
	arp.Addrtype = binary.BigEndian.Uint16(pkt[0:2])
	arp.Protocol = binary.BigEndian.Uint16(pkt[2:4])
	arp.HwAddressSize = pkt[4]
	arp.ProtAddressSize = pkt[5]
	arp.Operation = binary.BigEndian.Uint16(pkt[6:8])
	arp.SourceHwAddress = pkt[8 : 8+arp.HwAddressSize]
	arp.SourceProtAddress = pkt[8+arp.HwAddressSize : 8+arp.HwAddressSize+arp.ProtAddressSize]
	arp.DestHwAddress = pkt[8+arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+arp.ProtAddressSize]
	arp.DestProtAddress = pkt[8+2*arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+2*arp.ProtAddressSize]

	p.Headers = append(p.Headers, arp)
	p.Payload = p.Payload[8+2*arp.HwAddressSize+2*arp.ProtAddressSize:]
}

func (p *Packet) decodeIp() {
	if len(p.Payload) < 20 {
		return
	}
	pkt := p.Payload
	ip := new(Iphdr)

	ip.Version = uint8(pkt[0]) >> 4
	ip.Ihl = uint8(pkt[0]) & 0x0F
	ip.Tos = pkt[1]
	ip.Length = binary.BigEndian.Uint16(pkt[2:4])
	ip.Id = binary.BigEndian.Uint16(pkt[4:6])
	flagsfrags := binary.BigEndian.Uint16(pkt[6:8])
	ip.Flags = uint8(flagsfrags >> 13)
	ip.FragOffset = flagsfrags & 0x1FFF
	ip.Ttl = pkt[8]
	ip.Protocol = pkt[9]
	ip.Checksum = binary.BigEndian.Uint16(pkt[10:12])
	ip.SrcIp = pkt[12:16]
	ip.DestIp = pkt[16:20]
	pEnd := int(ip.Length)
	if pEnd > len(pkt) {
		pEnd = len(pkt)
	}
	pIhl := int(ip.Ihl) * 4
	if pIhl > pEnd {
		pIhl = pEnd
	}
	p.Payload = pkt[pIhl:pEnd]
	p.Headers = append(p.Headers, ip)

	switch ip.Protocol {
	case IP_TCP:
		p.decodeTcp()
	case IP_UDP:
		p.decodeUdp()
	case IP_ICMP:
		p.decodeIcmp()
	case IP_INIP:
		p.decodeIp()
	}
}

func (p *Packet) decodeTcp() {
	pLenPayload := len(p.Payload)
	if pLenPayload < 20 {
		return
	}
	pkt := p.Payload
	tcp := new(Tcphdr)
	tcp.SrcPort = binary.BigEndian.Uint16(pkt[0:2])
	tcp.DestPort = binary.BigEndian.Uint16(pkt[2:4])
	tcp.Seq = binary.BigEndian.Uint32(pkt[4:8])
	tcp.Ack = binary.BigEndian.Uint32(pkt[8:12])
	tcp.DataOffset = (pkt[12] & 0xF0) >> 4
	tcp.Flags = binary.BigEndian.Uint16(pkt[12:14]) & 0x1FF
	tcp.Window = binary.BigEndian.Uint16(pkt[14:16])
	tcp.Checksum = binary.BigEndian.Uint16(pkt[16:18])
	tcp.Urgent = binary.BigEndian.Uint16(pkt[18:20])
	pDataOffset := int(tcp.DataOffset * 4)
	if pDataOffset > pLenPayload {
		pDataOffset = pLenPayload
	}
	p.Payload = pkt[pDataOffset:]
	p.Headers = append(p.Headers, tcp)
}

func (p *Packet) decodeUdp() {
	if len(p.Payload) < 8 {
		return
	}
	pkt := p.Payload
	udp := new(Udphdr)
	udp.SrcPort = binary.BigEndian.Uint16(pkt[0:2])
	udp.DestPort = binary.BigEndian.Uint16(pkt[2:4])
	udp.Length = binary.BigEndian.Uint16(pkt[4:6])
	udp.Checksum = binary.BigEndian.Uint16(pkt[6:8])
	p.Headers = append(p.Headers, udp)
	p.Payload = pkt[8:]
}

func (p *Packet) decodeIcmp() *Icmphdr {
	if len(p.Payload) < 8 {
		return nil
	}
	pkt := p.Payload
	icmp := new(Icmphdr)
	icmp.Type = pkt[0]
	icmp.Code = pkt[1]
	icmp.Checksum = binary.BigEndian.Uint16(pkt[2:4])
	icmp.Id = binary.BigEndian.Uint16(pkt[4:6])
	icmp.Seq = binary.BigEndian.Uint16(pkt[6:8])
	p.Payload = pkt[8:]
	p.Headers = append(p.Headers, icmp)
	return icmp
}

func (p *Packet) decodeIp6() {
	if len(p.Payload) < 40 {
		return
	}
	pkt := p.Payload
	ip6 := new(Ip6hdr)
	ip6.Version = uint8(pkt[0]) >> 4
	ip6.TrafficClass = uint8((binary.BigEndian.Uint16(pkt[0:2]) >> 4) & 0x00FF)
	ip6.FlowLabel = binary.BigEndian.Uint32(pkt[0:4]) & 0x000FFFFF
	ip6.Length = binary.BigEndian.Uint16(pkt[4:6])
	ip6.NextHeader = pkt[6]
	ip6.HopLimit = pkt[7]
	ip6.SrcIp = pkt[8:24]
	ip6.DestIp = pkt[24:40]
	p.Payload = pkt[40:]
	p.Headers = append(p.Headers, ip6)

	switch ip6.NextHeader {
	case IP_TCP:
		p.decodeTcp()
	case IP_UDP:
		p.decodeUdp()
	case IP_ICMP:
		p.decodeIcmp()
	case IP_INIP:
		p.decodeIp()
	}
}
<commit_msg>Make sure IPinIP has a max recursive limit of 3.<commit_after>package pcap

import (
	"encoding/binary"
	"fmt"
	"reflect"
	"strings"
	"time"
)

type PacketTime struct {
	Sec  int32
	Usec int32
}

// Packet is a single packet parsed from a pcap file.
type Packet struct {
	// porting from 'pcap_pkthdr' struct
	Time   time.Time // packet send/receive time
	Caplen uint32    // bytes stored in the file (caplen <= len)
	Len    uint32    // bytes sent/received

	Data []byte // packet data

	Type    int // protocol type, see LINKTYPE_*
	DestMac uint64
	SrcMac  uint64

	Headers []interface{} // decoded headers, in order
	Payload []byte        // remaining non-header bytes
}

// Decode decodes the headers of a Packet.
func (p *Packet) Decode() {

	p.Type = int(binary.BigEndian.Uint16(p.Data[12:14]))
	p.DestMac = decodemac(p.Data[0:6])
	p.SrcMac = decodemac(p.Data[6:12])
	p.Payload = p.Data[14:]

	switch p.Type {
	case TYPE_IP:
		p.decodeIp(0)
	case TYPE_IP6:
		p.decodeIp6(0)
	case TYPE_ARP:
		p.decodeArp()
	}
}

func (p *Packet) headerString(headers []interface{}) string {
	// If there's just one header, return that.
	if len(headers) == 1 {
		if hdr, ok := headers[0].(fmt.Stringer); ok {
			return hdr.String()
		}
	}
	// If there are two headers (IPv4/IPv6 -> TCP/UDP/IP..)
	if len(headers) == 2 {
		// Commonly the first header is an address.
		if addr, ok := p.Headers[0].(addrHdr); ok {
			if hdr, ok := p.Headers[1].(addrStringer); ok {
				return fmt.Sprintf("%s %s", p.Time, hdr.String(addr))
			}
		}
	}
	// For IP in IP, we do a recursive call.
	if len(headers) >= 2 {
		if addr, ok := headers[0].(addrHdr); ok {
			if _, ok := headers[1].(addrHdr); ok {
				return fmt.Sprintf("%s > %s IP in IP: ",
					addr.SrcAddr(), addr.DestAddr(), p.headerString(headers[1:]))
			}
		}
	}

	var typeNames []string
	for _, hdr := range headers {
		typeNames = append(typeNames, reflect.TypeOf(hdr).String())
	}

	return fmt.Sprintf("unknown [%s]", strings.Join(typeNames, ","))
}

// String prints a one-line representation of the packet header.
// The output is suitable for use in a tcpdump program.
func (p *Packet) String() string {
	// If there are no headers, print "unsupported protocol".
	if len(p.Headers) == 0 {
		return fmt.Sprintf("%s unsupported protocol %d", p.Time, int(p.Type))
	}
	return fmt.Sprintf("%s %s", p.Time, p.headerString(p.Headers))
}

func (p *Packet) decodeArp() {
	pkt := p.Payload
	arp := new(Arphdr)
	arp.Addrtype = binary.BigEndian.Uint16(pkt[0:2])
	arp.Protocol = binary.BigEndian.Uint16(pkt[2:4])
	arp.HwAddressSize = pkt[4]
	arp.ProtAddressSize = pkt[5]
	arp.Operation = binary.BigEndian.Uint16(pkt[6:8])
	arp.SourceHwAddress = pkt[8 : 8+arp.HwAddressSize]
	arp.SourceProtAddress = pkt[8+arp.HwAddressSize : 8+arp.HwAddressSize+arp.ProtAddressSize]
	arp.DestHwAddress = pkt[8+arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+arp.ProtAddressSize]
	arp.DestProtAddress = pkt[8+2*arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+2*arp.ProtAddressSize]

	p.Headers = append(p.Headers, arp)
	p.Payload = p.Payload[8+2*arp.HwAddressSize+2*arp.ProtAddressSize:]
}

func (p *Packet) decodeIp(recur int) {
	if len(p.Payload) < 20 {
		return
	}
	if recur > 3 {
		return
	}
	pkt := p.Payload
	ip := new(Iphdr)

	ip.Version = uint8(pkt[0]) >> 4
	ip.Ihl = uint8(pkt[0]) & 0x0F
	ip.Tos = pkt[1]
	ip.Length = binary.BigEndian.Uint16(pkt[2:4])
	ip.Id = binary.BigEndian.Uint16(pkt[4:6])
	flagsfrags := binary.BigEndian.Uint16(pkt[6:8])
	ip.Flags = uint8(flagsfrags >> 13)
	ip.FragOffset = flagsfrags & 0x1FFF
	ip.Ttl = pkt[8]
	ip.Protocol = pkt[9]
	ip.Checksum = binary.BigEndian.Uint16(pkt[10:12])
	ip.SrcIp = pkt[12:16]
	ip.DestIp = pkt[16:20]
	pEnd := int(ip.Length)
	if pEnd > len(pkt) {
		pEnd = len(pkt)
	}
	pIhl := int(ip.Ihl) * 4
	if pIhl > pEnd {
		pIhl = pEnd
	}
	p.Payload = pkt[pIhl:pEnd]
	p.Headers = append(p.Headers, ip)

	switch ip.Protocol {
	case IP_TCP:
		p.decodeTcp()
	case IP_UDP:
		p.decodeUdp()
	case IP_ICMP:
		p.decodeIcmp()
	case IP_INIP:
		p.decodeIp(recur+1)
	}
}

func (p *Packet) decodeTcp() {
	pLenPayload := len(p.Payload)
	if pLenPayload < 20 {
		return
	}
	pkt := p.Payload
	tcp := new(Tcphdr)
	tcp.SrcPort = binary.BigEndian.Uint16(pkt[0:2])
	tcp.DestPort = binary.BigEndian.Uint16(pkt[2:4])
	tcp.Seq = binary.BigEndian.Uint32(pkt[4:8])
	tcp.Ack = binary.BigEndian.Uint32(pkt[8:12])
	tcp.DataOffset = (pkt[12] & 0xF0) >> 4
	tcp.Flags = binary.BigEndian.Uint16(pkt[12:14]) & 0x1FF
	tcp.Window = binary.BigEndian.Uint16(pkt[14:16])
	tcp.Checksum = binary.BigEndian.Uint16(pkt[16:18])
	tcp.Urgent = binary.BigEndian.Uint16(pkt[18:20])
	pDataOffset := int(tcp.DataOffset * 4)
	if pDataOffset > pLenPayload {
		pDataOffset = pLenPayload
	}
	p.Payload = pkt[pDataOffset:]
	p.Headers = append(p.Headers, tcp)
}

func (p *Packet) decodeUdp() {
	if len(p.Payload) < 8 {
		return
	}
	pkt := p.Payload
	udp := new(Udphdr)
	udp.SrcPort = binary.BigEndian.Uint16(pkt[0:2])
	udp.DestPort = binary.BigEndian.Uint16(pkt[2:4])
	udp.Length = binary.BigEndian.Uint16(pkt[4:6])
	udp.Checksum = binary.BigEndian.Uint16(pkt[6:8])
	p.Headers = append(p.Headers, udp)
	p.Payload = pkt[8:]
}

func (p *Packet) decodeIcmp() *Icmphdr {
	if len(p.Payload) < 8 {
		return nil
	}
	pkt := p.Payload
	icmp := new(Icmphdr)
	icmp.Type = pkt[0]
	icmp.Code = pkt[1]
	icmp.Checksum = binary.BigEndian.Uint16(pkt[2:4])
	icmp.Id = binary.BigEndian.Uint16(pkt[4:6])
	icmp.Seq = binary.BigEndian.Uint16(pkt[6:8])
	p.Payload = pkt[8:]
	p.Headers = append(p.Headers, icmp)
	return icmp
}

func (p *Packet) decodeIp6(recur int) {
	if len(p.Payload) < 40 {
		return
	}
	pkt := p.Payload
	ip6 := new(Ip6hdr)
	ip6.Version = uint8(pkt[0]) >> 4
	ip6.TrafficClass = uint8((binary.BigEndian.Uint16(pkt[0:2]) >> 4) & 0x00FF)
	ip6.FlowLabel = binary.BigEndian.Uint32(pkt[0:4]) & 0x000FFFFF
	ip6.Length = binary.BigEndian.Uint16(pkt[4:6])
	ip6.NextHeader = pkt[6]
	ip6.HopLimit = pkt[7]
	ip6.SrcIp = pkt[8:24]
	ip6.DestIp = pkt[24:40]
	p.Payload = pkt[40:]
	p.Headers = append(p.Headers, ip6)

	switch ip6.NextHeader {
	case IP_TCP:
		p.decodeTcp()
	case IP_UDP:
		p.decodeUdp()
	case IP_ICMP:
		p.decodeIcmp()
	case IP_INIP:
		p.decodeIp(recur+1)
	}
}
<|endoftext|>


<commit_before>package main

import "github.com/roasbeef/btcd/chaincfg"

// activeNetParams is a pointer to the parameters specific to the currently
// active bitcoin network.
var activeNetParams = testNetParams

// netParams couples the p2p parameters of a network with the corresponding RPC
// port of a daemon running on the particular network.
type netParams struct {
	*chaincfg.Params
	rpcPort string
}

// testNetParams contains parameters specific to the 3rd version of the test network.
var testNetParams = netParams{
	Params:  &chaincfg.TestNet3Params,
	rpcPort: "18334",
}

// simNetParams contains parameters specific to the simulation test network.
var simNetParams = netParams{
	Params:  &chaincfg.SimNetParams,
	rpcPort: "18556",
}
<commit_msg>config: add litecoin testnet4 parameters to the set of RPC parameters<commit_after>package main

import (
	litecoinCfg "github.com/ltcsuite/ltcd/chaincfg"
	bitcoinCfg "github.com/roasbeef/btcd/chaincfg"
	"github.com/roasbeef/btcd/wire"
)

// activeNetParams is a pointer to the parameters specific to the currently
// active bitcoin network.
var activeNetParams = bitcoinTestNetParams

// bitcoinNetParams couples the p2p parameters of a network with the
// corresponding RPC port of a daemon running on the particular network.
type bitcoinNetParams struct {
	*bitcoinCfg.Params
	rpcPort string
}

// litecoinNetParams couples the p2p parameters of a network with the
// corresponding RPC port of a daemon running on the particular network.
type litecoinNetParams struct {
	*litecoinCfg.Params
	rpcPort string
}

// bitcoinTestNetParams contains parameters specific to the 3rd version of the
// test network.
var bitcoinTestNetParams = bitcoinNetParams{
	Params:  &bitcoinCfg.TestNet3Params,
	rpcPort: "18334",
}

// bitcoinSimNetParams contains parameters specific to the simulation test
// network.
var bitcoinSimNetParams = bitcoinNetParams{
	Params:  &bitcoinCfg.SimNetParams,
	rpcPort: "18556",
}

// liteTestNetParams contains parameters specific to the 4th version of the
// test network.
var liteTestNetParams = litecoinNetParams{
	Params:  &litecoinCfg.TestNet4Params,
	rpcPort: "19334",
}

// applyLitecoinParams applies the relevant chain configuration parameters that
// differ for litecoin to the chain parameters typed for btcsuite derivation.
// This function is used in place of using something like interface{} to
// abstract over _which_ chain (or fork) the parameters are for.
func applyLitecoinParams(params *bitcoinNetParams) {
	params.Name = liteTestNetParams.Name
	params.Net = wire.BitcoinNet(liteTestNetParams.Net)
	params.DefaultPort = liteTestNetParams.DefaultPort
	params.CoinbaseMaturity = liteTestNetParams.CoinbaseMaturity

	copy(params.GenesisHash[:], liteTestNetParams.GenesisHash[:])

	// Address encoding magics
	params.PubKeyHashAddrID = liteTestNetParams.PubKeyHashAddrID
	params.ScriptHashAddrID = liteTestNetParams.ScriptHashAddrID
	params.PrivateKeyID = liteTestNetParams.PrivateKeyID
	params.WitnessPubKeyHashAddrID = liteTestNetParams.WitnessPubKeyHashAddrID
	params.WitnessScriptHashAddrID = liteTestNetParams.WitnessScriptHashAddrID

	copy(params.HDPrivateKeyID[:], liteTestNetParams.HDPrivateKeyID[:])
	copy(params.HDPublicKeyID[:], liteTestNetParams.HDPublicKeyID[:])

	params.HDCoinType = liteTestNetParams.HDCoinType

	params.rpcPort = liteTestNetParams.rpcPort
}
<|endoftext|>


<commit_before>package main

/*
 Synopsis:

 Parse("1 2 + 'Hello World' [ '!' append ] 5 times")
 //=> [[fun: 1] [fun: 2] [fun: +] [str: 'Hello World'] [stm: '!' append]
       [fun: 5] [fun: times]]
 */

import (
	"fmt"
	"strings"
)

type Tokens []Token

func (ts *Tokens) String() string {
	str := "["
	for i, t := range *ts {
		if i > 0 {
			str += ", "
		}
		str += t.String()
	}
	return str + "]"
}

type Token struct {
	key  string
	val  string
}

func (t *Token) String() string {
	return "[" + t.key + ": " + t.val + "]"
}

func NewToken(key, val string) Token {
	return Token{key, val}
}

func Parse(code string) string {
	tokens := strings.Split(code, " ")
  list := new(Tokens)

	for i := 0; i < len(tokens); i++ {
		tok := tokens[i]

		if strings.HasPrefix(tok, "'") {
			idx, found := parseString(tokens, "'", i)
			i = idx
			*list = append(*list, NewToken("str", found))

		} else if strings.HasPrefix(tok, "\"") {
			idx, found := parseString(tokens, "\"", i)
			i = idx
			*list = append(*list, NewToken("str", found))

		} else if strings.TrimSpace(tok) ==  "[" {
			i++
			idx, found := parseBlock(tokens, i)
			i = idx
			*list = append(*list, NewToken("stm", found))

		} else if strings.HasPrefix(tok, "[") {
			idx, found := parseBlock(tokens, i)
			i = idx
			*list = append(*list, NewToken("stm", found))

		} else {
			*list = append(*list, NewToken("fun", tok))
		}
	}

	fmt.Println(list)

	return "abc"
}

/*
 Parses until a token ending in +until+ is found, returning what has been found
 as a string.
 */
func parseString(tokens []string, until string, idx int) (i int, str string) {
	str = ""
	for i := idx; i < len(tokens); i++ {
		tok := tokens[i]
		str += tok + " "
		if strings.HasSuffix(tok, until) {
			return i, str
		}
	}
	return len(tokens), strings.TrimSpace(str)
}

/*
 Parses a block, some tokens surrounded by square brackets.
 */
func parseBlock(tokens []string, idx int) (i int, str string) {
	str = ""
	for i := idx; i < len(tokens); i++ {
		tok := tokens[i]

		if strings.TrimSpace(tok) == "]" {
			return i, str
		}

		str += tok + " "
		if strings.HasSuffix(tok, "]") {
			return i, str
		}
	}
	return len(tokens), strings.TrimSpace(str)
}
<commit_msg>Parse now returns the list of tokens<commit_after>package main

/*
 Synopsis:

 Parse("1 2 + 'Hello World' [ '!' append ] 5 times")
 //=> [[fun: 1] [fun: 2] [fun: +] [str: 'Hello World'] [stm: '!' append]
       [fun: 5] [fun: times]]
 */

import (
	"strings"
)

type Tokens []Token

func (ts *Tokens) String() string {
	str := "["
	for i, t := range *ts {
		if i > 0 {
			str += ", "
		}
		str += t.String()
	}
	return str + "]"
}

type Token struct {
	key  string
	val  string
}

func (t *Token) String() string {
	return "[" + t.key + ": " + t.val + "]"
}

func NewToken(key, val string) Token {
	return Token{key, val}
}

func Parse(code string) *Tokens {
	tokens := strings.Split(code, " ")
  list := new(Tokens)

	for i := 0; i < len(tokens); i++ {
		tok := tokens[i]

		if strings.HasPrefix(tok, "'") {
			idx, found := parseString(tokens, "'", i)
			i = idx
			*list = append(*list, NewToken("str", found))

		} else if strings.HasPrefix(tok, "\"") {
			idx, found := parseString(tokens, "\"", i)
			i = idx
			*list = append(*list, NewToken("str", found))

		} else if strings.TrimSpace(tok) ==  "[" {
			i++
			idx, found := parseBlock(tokens, i)
			i = idx
			*list = append(*list, NewToken("stm", found))

		} else if strings.HasPrefix(tok, "[") {
			idx, found := parseBlock(tokens, i)
			i = idx
			*list = append(*list, NewToken("stm", found))

		} else {
			*list = append(*list, NewToken("fun", tok))
		}
	}

	return list
}

/*
 Parses until a token ending in +until+ is found, returning what has been found
 as a string.
 */
func parseString(tokens []string, until string, idx int) (i int, str string) {
	str = ""
	for i := idx; i < len(tokens); i++ {
		tok := tokens[i]
		str += tok + " "
		if strings.HasSuffix(tok, until) {
			return i, str
		}
	}
	return len(tokens), strings.TrimSpace(str)
}

/*
 Parses a block, some tokens surrounded by square brackets.
 */
func parseBlock(tokens []string, idx int) (i int, str string) {
	str = ""
	for i := idx; i < len(tokens); i++ {
		tok := tokens[i]

		if strings.TrimSpace(tok) == "]" {
			return i, str
		}

		str += tok + " "
		if strings.HasSuffix(tok, "]") {
			return i, str
		}
	}
	return len(tokens), strings.TrimSpace(str)
}
<|endoftext|>


<commit_before>package main

// Synopsis:
//
//   Parse("1 2 + 'Hello World' [ '!' append ] 5 times")
//   ;=> [[fun: 1] [fun: 2] [fun: +] [str: 'Hello World'] [stm: '!' append]
//         [fun: 5] [fun: times]]

import (
	"strings"
)

type Tokens []Token

func (ts *Tokens) String() string {
	str := "["
	for i, t := range *ts {
		if i > 0 {
			str += ", "
		}
		str += t.String()
	}
	return str + "]"
}

type Token struct {
	key string
	val string
}

func (t *Token) String() string {
	return "[" + t.key + ": " + t.val + "]"
}

func NewToken(key, val string) Token {
	return Token{key, val}
}

func FullParse(code string) *Tokens {
	list := new(Tokens)

	for i := 0; i < len(code); i++ {
		temp := ""

		switch c := code[i]; c {
		case '\t', ' ':
			// Ignore whitespace

		case '\n':
			*list = append(*list, NewToken("newline", ""))

		case ';':
			i, temp = parseUntil(i, code, '\n')
			*list = append(*list, NewToken("comment",
				strings.TrimSpace(strings.TrimLeft(temp, ";"))))

		case '.':
			*list = append(*list, NewToken("stm", ""))

		case '\'':
			i++
			i, temp = parseUntil(i, code, '\'')
			*list = append(*list, NewToken("str", temp))

		case '"':
			i++
			i, temp = parseUntil(i, code, '"')
			*list = append(*list, NewToken("str", temp))

		case '[':
			i++
			i, temp = parseMatching(i, code, '[', ']')
			*list = append(*list, NewToken("stm", temp))

		case ':':
			i++
			i, temp = parseUntilWhitespace(i, code)
			*list = append(*list, NewToken("stm", temp))

		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
			i, temp = parseUntilWhitespace(i, code)
			*list = append(*list, NewToken("int", temp))

		case '-':
			i, temp = parseUntilWhitespace(i, code)
			if temp == "-" {
				*list = append(*list, NewToken("fun", temp))
			} else {
				*list = append(*list, NewToken("int", temp))
			}

		default:
			i, temp = parseUntilWhitespace(i, code)
			*list = append(*list, NewToken("fun", temp))

		}
	}

	return list
}

func Parse(code string) *Tokens {
	list := new(Tokens)

	for _, tok := range *FullParse(code) {
		if tok.key != "comment" && tok.key != "newline" {
			*list = append(*list, tok)
		}
	}

	return list
}

func parseUntil(idx int, code string, until uint8) (i int, s string) {
	return parseUntilAny(idx, code, []uint8{until})
}

func parseUntilWhitespace(idx int, code string) (i int, s string) {
	return parseUntilAny(idx, code, []uint8{' ', '\n', '\t'})
}

func parseUntilAny(idx int, code string, untils []uint8) (i int, s string) {
	str := ""

	for i := idx; i < len(code); i++ {
		c := code[i]
		for _, until := range untils {
			if c == until {
				return i, str
			}
		}
		str += string(c)
	}
	return len(code), str
}

func parseMatching(idx int, code string, op, cl uint8) (i int, s string) {
	str := ""

	for i := idx; i < len(code); i++ {
		c := code[i]
		if c == op {
			i++
			f := ""
			i, f = parseMatching(i, code, op, cl)
			str += "[" + f + "]"
		} else if c == cl {
			return i, strings.TrimSpace(str)
		} else {
			str += string(c)
		}
	}
	return len(code), strings.TrimSpace(str)
}
<commit_msg>Negative numbers now correctly parsed, really this time<commit_after>package main

// Synopsis:
//
//   Parse("1 2 + 'Hello World' [ '!' append ] 5 times")
//   ;=> [[fun: 1] [fun: 2] [fun: +] [str: 'Hello World'] [stm: '!' append]
//         [fun: 5] [fun: times]]

import (
	"strings"
	"regexp"
)

type Tokens []Token

func (ts *Tokens) String() string {
	str := "["
	for i, t := range *ts {
		if i > 0 {
			str += ", "
		}
		str += t.String()
	}
	return str + "]"
}

type Token struct {
	key string
	val string
}

func (t *Token) String() string {
	return "[" + t.key + ": " + t.val + "]"
}

func NewToken(key, val string) Token {
	return Token{key, val}
}

func FullParse(code string) *Tokens {
	list := new(Tokens)

	for i := 0; i < len(code); i++ {
		temp := ""

		switch c := code[i]; c {
		case '\t', ' ':
			// Ignore whitespace

		case '\n':
			*list = append(*list, NewToken("newline", ""))

		case ';':
			i, temp = parseUntil(i, code, '\n')
			*list = append(*list, NewToken("comment",
				strings.TrimSpace(strings.TrimLeft(temp, ";"))))

		case '.':
			*list = append(*list, NewToken("stm", ""))

		case '\'':
			i++
			i, temp = parseUntil(i, code, '\'')
			*list = append(*list, NewToken("str", temp))

		case '"':
			i++
			i, temp = parseUntil(i, code, '"')
			*list = append(*list, NewToken("str", temp))

		case '[':
			i++
			i, temp = parseMatching(i, code, '[', ']')
			*list = append(*list, NewToken("stm", temp))

		case ':':
			i++
			i, temp = parseUntilWhitespace(i, code)
			*list = append(*list, NewToken("stm", temp))

		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
			i, temp = parseUntilWhitespace(i, code)
			*list = append(*list, NewToken("int", temp))

		case '-':
			i, temp = parseUntilWhitespace(i, code)
			matcher, _ := regexp.Compile("-[0-9]+")

			if matcher.MatchString(temp) {
				*list = append(*list, NewToken("int", temp))
			} else {
				*list = append(*list, NewToken("fun", temp))
			}

		default:
			i, temp = parseUntilWhitespace(i, code)
			*list = append(*list, NewToken("fun", temp))

		}
	}

	return list
}

func Parse(code string) *Tokens {
	list := new(Tokens)

	for _, tok := range *FullParse(code) {
		if tok.key != "comment" && tok.key != "newline" {
			*list = append(*list, tok)
		}
	}

	return list
}

func parseUntil(idx int, code string, until uint8) (i int, s string) {
	return parseUntilAny(idx, code, []uint8{until})
}

func parseUntilWhitespace(idx int, code string) (i int, s string) {
	return parseUntilAny(idx, code, []uint8{' ', '\n', '\t'})
}

func parseUntilAny(idx int, code string, untils []uint8) (i int, s string) {
	str := ""

	for i := idx; i < len(code); i++ {
		c := code[i]
		for _, until := range untils {
			if c == until {
				return i, str
			}
		}
		str += string(c)
	}
	return len(code), str
}

func parseMatching(idx int, code string, op, cl uint8) (i int, s string) {
	str := ""

	for i := idx; i < len(code); i++ {
		c := code[i]
		if c == op {
			i++
			f := ""
			i, f = parseMatching(i, code, op, cl)
			str += "[" + f + "]"
		} else if c == cl {
			return i, strings.TrimSpace(str)
		} else {
			str += string(c)
		}
	}
	return len(code), strings.TrimSpace(str)
}
<|endoftext|>


<commit_before>package irc

import (
	"bytes"
	"strings"
)

var tagDecodeSlashMap = map[rune]rune{
	':':  ';',
	's':  ' ',
	'\\': '\\',
	'r':  '\r',
	'n':  '\n',
}

var tagEncodeMap = map[rune]string{
	';':  "\\:",
	' ':  "\\s",
	'\\': "\\\\",
	'\r': "\\r",
	'\n': "\\n",
}

// TagValue represents the value of a tag.
type TagValue string

// ParseTagValue parses a TagValue from the connection. If you need to
// set a TagValue, you probably want to just set the string itself, so
// it will be encoded properly.
func ParseTagValue(v string) TagValue {
	ret := &bytes.Buffer{}

	input := bytes.NewBufferString(v)

	for {
		c, _, err := input.ReadRune()
		if err != nil {
			break
		}

		if c == '\\' {
			c2, _, err := input.ReadRune()
			if err != nil {
				ret.WriteRune(c)
				break
			}

			if replacement, ok := tagDecodeSlashMap[c2]; ok {
				ret.WriteRune(replacement)
			} else {
				ret.WriteRune(c)
				ret.WriteRune(c2)
			}
		} else {
			ret.WriteRune(c)
		}
	}

	return TagValue(ret.String())
}

// Encode converts a TagValue to the format in the connection.
func (v TagValue) Encode() string {
	ret := &bytes.Buffer{}

	for _, c := range v {
		if replacement, ok := tagEncodeMap[c]; ok {
			ret.WriteString(replacement)
		} else {
			ret.WriteRune(c)
		}
	}

	return ret.String()
}

// Tags represents the IRCv3 message tags.
type Tags map[string]TagValue

// ParseTags takes a tag string and parses it into a tag map. It will
// always return a tag map, even if there are no valid tags.
func ParseTags(line string) Tags {
	ret := Tags{}

	tags := strings.Split(line, ";")
	for _, tag := range tags {
		parts := strings.SplitN(tag, "=", 2)
		if len(parts) < 2 {
			ret[parts[0]] = ""
			continue
		}

		ret[parts[0]] = ParseTagValue(parts[1])
	}

	return ret
}

// GetTag is a convenience method to look up a tag in the map.
func (t Tags) GetTag(key string) (string, bool) {
	ret, ok := t[key]
	return string(ret), ok
}

// Copy will create a new copy of all IRC tags attached to this
// message.
func (t Tags) Copy() Tags {
	ret := Tags{}

	for k, v := range t {
		ret[k] = v
	}

	return ret
}

// String ensures this is stringable
func (t Tags) String() string {
	buf := &bytes.Buffer{}

	for k, v := range t {
		buf.WriteByte(';')
		buf.WriteString(k)
		if v != "" {
			buf.WriteByte('=')
			buf.WriteString(v.Encode())
		}
	}

	// We don't need the first byte because that's an extra ';'
	// character.
	buf.ReadByte()

	return buf.String()
}

// Prefix represents the prefix of a message, generally the user who sent it
type Prefix struct {
	// Name will contain the nick of who sent the message, the
	// server who sent the message, or a blank string
	Name string

	// User will either contain the user who sent the message or a blank string
	User string

	// Host will either contain the host of who sent the message or a blank string
	Host string
}

// ParsePrefix takes an identity string and parses it into an
// identity struct. It will always return an Prefix struct and never
// nil.
func ParsePrefix(line string) *Prefix {
	// Start by creating an Prefix with nothing but the host
	id := &Prefix{
		Name: line,
	}

	uh := strings.SplitN(id.Name, "@", 2)
	if len(uh) == 2 {
		id.Name, id.Host = uh[0], uh[1]
	}

	nu := strings.SplitN(id.Name, "!", 2)
	if len(nu) == 2 {
		id.Name, id.User = nu[0], nu[1]
	}

	return id
}

// Copy will create a new copy of an Prefix
func (p *Prefix) Copy() *Prefix {
	if p == nil {
		return nil
	}

	newPrefix := &Prefix{}

	*newPrefix = *p

	return newPrefix
}

// String ensures this is stringable
func (p *Prefix) String() string {
	buf := &bytes.Buffer{}
	buf.WriteString(p.Name)

	if p.User != "" {
		buf.WriteString("!")
		buf.WriteString(p.User)
	}

	if p.Host != "" {
		buf.WriteString("@")
		buf.WriteString(p.Host)
	}

	return buf.String()
}

// Message represents a line parsed from the server
type Message struct {
	// Each message can have IRCv3 tags
	Tags

	// Each message can have a Prefix
	*Prefix

	// Command is which command is being called.
	Command string

	// Params are all the arguments for the command.
	Params []string
}

// ParseMessage takes a message string (usually a whole line) and
// parses it into a Message struct. This will return nil in the case
// of invalid messages.
func ParseMessage(line string) *Message {
	// Trim the line and make sure we have data
	line = strings.TrimSpace(line)
	if len(line) == 0 {
		return nil
	}

	c := &Message{
		Tags:   Tags{},
		Prefix: &Prefix{},
	}

	if line[0] == '@' {
		split := strings.SplitN(line, " ", 2)
		if len(split) < 2 {
			return nil
		}

		c.Tags = ParseTags(split[0][1:])
		line = split[1]
	}

	if line[0] == ':' {
		split := strings.SplitN(line, " ", 2)
		if len(split) < 2 {
			return nil
		}

		// Parse the identity, if there was one
		c.Prefix = ParsePrefix(split[0][1:])
		line = split[1]
	}

	// Split out the trailing then the rest of the args. Because
	// we expect there to be at least one result as an arg (the
	// command) we don't need to special case the trailing arg and
	// can just attempt a split on " :"
	split := strings.SplitN(line, " :", 2)
	c.Params = strings.FieldsFunc(split[0], func(r rune) bool {
		return r == ' '
	})

	// If there are no args, we need to bail because we need at
	// least the command.
	if len(c.Params) == 0 {
		return nil
	}

	// If we had a trailing arg, append it to the other args
	if len(split) == 2 {
		c.Params = append(c.Params, split[1])
	}

	// Because of how it's parsed, the Command will show up as the
	// first arg.
	c.Command = c.Params[0]
	c.Params = c.Params[1:]

	return c
}

// Trailing returns the last argument in the Message or an empty string
// if there are no args
func (m *Message) Trailing() string {
	if len(m.Params) < 1 {
		return ""
	}

	return m.Params[len(m.Params)-1]
}

// FromChannel is mostly for PRIVMSG messages (and similar derived messages)
// It will check if the message came from a channel or a person.
func (m *Message) FromChannel() bool {
	if len(m.Params) < 1 || len(m.Params[0]) < 1 {
		return false
	}

	switch m.Params[0][0] {
	case '#', '&':
		return true
	default:
		return false
	}
}

// Copy will create a new copy of an message
func (m *Message) Copy() *Message {
	// Create a new message
	newMessage := &Message{}

	// Copy stuff from the old message
	*newMessage = *m

	// Copy any IRcv3 tags
	newMessage.Tags = m.Tags.Copy()

	// Copy the Prefix
	newMessage.Prefix = m.Prefix.Copy()

	// Copy the Params slice
	newMessage.Params = append(make([]string, 0, len(m.Params)), m.Params...)

	return newMessage
}

// String ensures this is stringable
func (m *Message) String() string {
	buf := &bytes.Buffer{}

	// Write any IRCv3 tags if they exist in the message
	if len(m.Tags) > 0 {
		buf.WriteByte('@')
		buf.WriteString(m.Tags.String())
		buf.WriteByte(' ')
	}

	// Add the prefix if we have one
	if m.Prefix != nil && m.Prefix.Name != "" {
		buf.WriteByte(':')
		buf.WriteString(m.Prefix.String())
		buf.WriteByte(' ')
	}

	// Add the command since we know we'll always have one
	buf.WriteString(m.Command)

	if len(m.Params) > 0 {
		args := m.Params[:len(m.Params)-1]
		trailing := m.Params[len(m.Params)-1]

		if len(args) > 0 {
			buf.WriteByte(' ')
			buf.WriteString(strings.Join(args, " "))
		}

		// If trailing is zero-length, contains a space or starts with
		// a : we need to actually specify that it's trailing.
		if len(trailing) == 0 || strings.ContainsRune(trailing, ' ') || trailing[0] == ':' {
			buf.WriteString(" :")
		} else {
			buf.WriteString(" ")
		}
		buf.WriteString(trailing)
	}

	return buf.String()
}
<commit_msg>Add information about FromChannel<commit_after>package irc

import (
	"bytes"
	"strings"
)

var tagDecodeSlashMap = map[rune]rune{
	':':  ';',
	's':  ' ',
	'\\': '\\',
	'r':  '\r',
	'n':  '\n',
}

var tagEncodeMap = map[rune]string{
	';':  "\\:",
	' ':  "\\s",
	'\\': "\\\\",
	'\r': "\\r",
	'\n': "\\n",
}

// TagValue represents the value of a tag.
type TagValue string

// ParseTagValue parses a TagValue from the connection. If you need to
// set a TagValue, you probably want to just set the string itself, so
// it will be encoded properly.
func ParseTagValue(v string) TagValue {
	ret := &bytes.Buffer{}

	input := bytes.NewBufferString(v)

	for {
		c, _, err := input.ReadRune()
		if err != nil {
			break
		}

		if c == '\\' {
			c2, _, err := input.ReadRune()
			if err != nil {
				ret.WriteRune(c)
				break
			}

			if replacement, ok := tagDecodeSlashMap[c2]; ok {
				ret.WriteRune(replacement)
			} else {
				ret.WriteRune(c)
				ret.WriteRune(c2)
			}
		} else {
			ret.WriteRune(c)
		}
	}

	return TagValue(ret.String())
}

// Encode converts a TagValue to the format in the connection.
func (v TagValue) Encode() string {
	ret := &bytes.Buffer{}

	for _, c := range v {
		if replacement, ok := tagEncodeMap[c]; ok {
			ret.WriteString(replacement)
		} else {
			ret.WriteRune(c)
		}
	}

	return ret.String()
}

// Tags represents the IRCv3 message tags.
type Tags map[string]TagValue

// ParseTags takes a tag string and parses it into a tag map. It will
// always return a tag map, even if there are no valid tags.
func ParseTags(line string) Tags {
	ret := Tags{}

	tags := strings.Split(line, ";")
	for _, tag := range tags {
		parts := strings.SplitN(tag, "=", 2)
		if len(parts) < 2 {
			ret[parts[0]] = ""
			continue
		}

		ret[parts[0]] = ParseTagValue(parts[1])
	}

	return ret
}

// GetTag is a convenience method to look up a tag in the map.
func (t Tags) GetTag(key string) (string, bool) {
	ret, ok := t[key]
	return string(ret), ok
}

// Copy will create a new copy of all IRC tags attached to this
// message.
func (t Tags) Copy() Tags {
	ret := Tags{}

	for k, v := range t {
		ret[k] = v
	}

	return ret
}

// String ensures this is stringable
func (t Tags) String() string {
	buf := &bytes.Buffer{}

	for k, v := range t {
		buf.WriteByte(';')
		buf.WriteString(k)
		if v != "" {
			buf.WriteByte('=')
			buf.WriteString(v.Encode())
		}
	}

	// We don't need the first byte because that's an extra ';'
	// character.
	buf.ReadByte()

	return buf.String()
}

// Prefix represents the prefix of a message, generally the user who sent it
type Prefix struct {
	// Name will contain the nick of who sent the message, the
	// server who sent the message, or a blank string
	Name string

	// User will either contain the user who sent the message or a blank string
	User string

	// Host will either contain the host of who sent the message or a blank string
	Host string
}

// ParsePrefix takes an identity string and parses it into an
// identity struct. It will always return an Prefix struct and never
// nil.
func ParsePrefix(line string) *Prefix {
	// Start by creating an Prefix with nothing but the host
	id := &Prefix{
		Name: line,
	}

	uh := strings.SplitN(id.Name, "@", 2)
	if len(uh) == 2 {
		id.Name, id.Host = uh[0], uh[1]
	}

	nu := strings.SplitN(id.Name, "!", 2)
	if len(nu) == 2 {
		id.Name, id.User = nu[0], nu[1]
	}

	return id
}

// Copy will create a new copy of an Prefix
func (p *Prefix) Copy() *Prefix {
	if p == nil {
		return nil
	}

	newPrefix := &Prefix{}

	*newPrefix = *p

	return newPrefix
}

// String ensures this is stringable
func (p *Prefix) String() string {
	buf := &bytes.Buffer{}
	buf.WriteString(p.Name)

	if p.User != "" {
		buf.WriteString("!")
		buf.WriteString(p.User)
	}

	if p.Host != "" {
		buf.WriteString("@")
		buf.WriteString(p.Host)
	}

	return buf.String()
}

// Message represents a line parsed from the server
type Message struct {
	// Each message can have IRCv3 tags
	Tags

	// Each message can have a Prefix
	*Prefix

	// Command is which command is being called.
	Command string

	// Params are all the arguments for the command.
	Params []string
}

// ParseMessage takes a message string (usually a whole line) and
// parses it into a Message struct. This will return nil in the case
// of invalid messages.
func ParseMessage(line string) *Message {
	// Trim the line and make sure we have data
	line = strings.TrimSpace(line)
	if len(line) == 0 {
		return nil
	}

	c := &Message{
		Tags:   Tags{},
		Prefix: &Prefix{},
	}

	if line[0] == '@' {
		split := strings.SplitN(line, " ", 2)
		if len(split) < 2 {
			return nil
		}

		c.Tags = ParseTags(split[0][1:])
		line = split[1]
	}

	if line[0] == ':' {
		split := strings.SplitN(line, " ", 2)
		if len(split) < 2 {
			return nil
		}

		// Parse the identity, if there was one
		c.Prefix = ParsePrefix(split[0][1:])
		line = split[1]
	}

	// Split out the trailing then the rest of the args. Because
	// we expect there to be at least one result as an arg (the
	// command) we don't need to special case the trailing arg and
	// can just attempt a split on " :"
	split := strings.SplitN(line, " :", 2)
	c.Params = strings.FieldsFunc(split[0], func(r rune) bool {
		return r == ' '
	})

	// If there are no args, we need to bail because we need at
	// least the command.
	if len(c.Params) == 0 {
		return nil
	}

	// If we had a trailing arg, append it to the other args
	if len(split) == 2 {
		c.Params = append(c.Params, split[1])
	}

	// Because of how it's parsed, the Command will show up as the
	// first arg.
	c.Command = c.Params[0]
	c.Params = c.Params[1:]

	return c
}

// Trailing returns the last argument in the Message or an empty string
// if there are no args
func (m *Message) Trailing() string {
	if len(m.Params) < 1 {
		return ""
	}

	return m.Params[len(m.Params)-1]
}

// FromChannel is mostly for PRIVMSG messages (and similar derived
// messages) It will check if the message came from a channel or a
// person. This is not always accurate, because some servers allow for
// other characters at the start of a channel.
//
// TODO: Remove in favor of *Client.FromChannel(*Message)
func (m *Message) FromChannel() bool {
	if len(m.Params) < 1 || len(m.Params[0]) < 1 {
		return false
	}

	switch m.Params[0][0] {
	case '#', '&':
		return true
	default:
		return false
	}
}

// Copy will create a new copy of an message
func (m *Message) Copy() *Message {
	// Create a new message
	newMessage := &Message{}

	// Copy stuff from the old message
	*newMessage = *m

	// Copy any IRcv3 tags
	newMessage.Tags = m.Tags.Copy()

	// Copy the Prefix
	newMessage.Prefix = m.Prefix.Copy()

	// Copy the Params slice
	newMessage.Params = append(make([]string, 0, len(m.Params)), m.Params...)

	return newMessage
}

// String ensures this is stringable
func (m *Message) String() string {
	buf := &bytes.Buffer{}

	// Write any IRCv3 tags if they exist in the message
	if len(m.Tags) > 0 {
		buf.WriteByte('@')
		buf.WriteString(m.Tags.String())
		buf.WriteByte(' ')
	}

	// Add the prefix if we have one
	if m.Prefix != nil && m.Prefix.Name != "" {
		buf.WriteByte(':')
		buf.WriteString(m.Prefix.String())
		buf.WriteByte(' ')
	}

	// Add the command since we know we'll always have one
	buf.WriteString(m.Command)

	if len(m.Params) > 0 {
		args := m.Params[:len(m.Params)-1]
		trailing := m.Params[len(m.Params)-1]

		if len(args) > 0 {
			buf.WriteByte(' ')
			buf.WriteString(strings.Join(args, " "))
		}

		// If trailing is zero-length, contains a space or starts with
		// a : we need to actually specify that it's trailing.
		if len(trailing) == 0 || strings.ContainsRune(trailing, ' ') || trailing[0] == ':' {
			buf.WriteString(" :")
		} else {
			buf.WriteString(" ")
		}
		buf.WriteString(trailing)
	}

	return buf.String()
}
<|endoftext|>


<commit_before>package nstcc

import (
	"errors"
	"fmt"
)

func (c *compiler) next() error {
	for {
		if c.parseFlags&parseFlagSpaces != 0 {
			if err := c.nextNoMacroSpace(); err != nil {
				return err
			}
		} else {
			if err := c.nextNoMacro(); err != nil {
				return err
			}
		}

		if true { // TODO: the TCC code says "if (!macro_ptr)".
			if c.tok >= tokIdent && c.parseFlags&parseFlagPreprocess != 0 {
				// TODO: if not reading from macro substituted string, then try
				// to substitute macros.
			}

		} else {
			// TODO: macro_ptr code path.
		}

		if c.tok == tokPPNum && c.parseFlags&parseFlagTokNum != 0 {
			// TODO: convert preprocessor tokens into C tokens.
		}

		return nil
	}
}

func (c *compiler) nextNoMacro() error {
	for {
		if err := c.nextNoMacroSpace(); err != nil {
			return err
		}
		if !c.tok.isSpace() {
			return nil
		}
	}
}

func (c *compiler) nextNoMacroSpace() error {
	// TODO: check what the TCC code calls macro_ptr.
	return c.nextNoMacro1()
}

func (c *compiler) nextNoMacro1() error {
redoNoStart:
	for {
		if c.s >= len(c.src) {
			c.tok = tokEOF
			return nil
		}

		switch t := c.src[c.s]; {
		default:
			return fmt.Errorf(`nstcc: unrecognized token '\x%02x'`, t)

		case t == ' ' || t == '\t':
			c.tok = token(t)
			c.s++
			return nil

		case t == '\f' || t == '\v' || t == '\r':
			c.s++
			continue redoNoStart

		case t == '\\':
			// TODO.

		case t == '\n':
			// TODO: file->line_num++
			c.tokFlags |= tokFlagBOL
			c.s++
			if c.parseFlags&parseFlagLineFeed == 0 {
				continue redoNoStart
			}
			c.tok = '\n'
			return nil

		case t == '#':
			// TODO.

		case t == 'L':
			// TODO: parse things like the wchar_t L"abc".
			fallthrough

		case isID[t]:
			s := c.s + 1
			for ; s < len(c.src); s++ {
				t = c.src[s]
				if !isIDNum[t] {
					break
				}
			}

			if t != '\\' {
				ts, err := c.idents.byStr(c.src[c.s:s])
				if err != nil {
					return err
				}
				c.tok = ts.tok
			} else {
				// TODO.
			}

			c.tokFlags = 0
			c.s = s
			return nil

		case t == '!':
			c.s++
			if c.peekc() == '=' {
				c.s++
				c.tok = tokNE
			} else {
				c.tok = '!'
			}
			c.tokFlags = 0
			return nil

		case t == '=':
			c.s++
			if c.peekc() == '=' {
				c.s++
				c.tok = tokEq
			} else {
				c.tok = '='
			}
			c.tokFlags = 0
			return nil

		case t == '*':
			c.s++
			if c.peekc() == '=' {
				c.s++
				c.tok = tokAMul
			} else {
				c.tok = '*'
			}
			c.tokFlags = 0
			return nil

		case t == '%':
			c.s++
			if c.peekc() == '=' {
				c.s++
				c.tok = tokAMod
			} else {
				c.tok = '%'
			}
			c.tokFlags = 0
			return nil

		case t == '^':
			c.s++
			if c.peekc() == '=' {
				c.s++
				c.tok = tokAXor
			} else {
				c.tok = '^'
			}
			c.tokFlags = 0
			return nil

		case t == '/':
			c.s++
			switch c.peekc() {
			case '*':
				c.tok = ' '
				c.s++
				return c.parseSlashStarComment()
			case '/':
				c.tok = ' '
				c.s++
				c.parseSlashSlashComment()
				return nil
			case '=':
				c.tok = tokADiv
				c.tokFlags = 0
				c.s++
				return nil
			default:
				c.tok = '/'
				c.tokFlags = 0
				c.s++
				return nil
			}

		case isSimpleToken[t]:
			c.tok = token(t)
			c.tokFlags = 0
			c.s++
			return nil
		}
	}
}

func (c *compiler) peekc() token {
	if c.s >= len(c.src) {
		return tokEOF
	}
	// TODO: handle c == '\\'.
	return token(c.src[c.s])
}

func (c *compiler) parseSlashStarComment() error {
	star := false
	for c.s < len(c.src) {
		switch x := c.src[c.s]; x {
		default:
			c.s++
			star = x == '*'
		case '\n':
			// TODO: file->line_num++
			c.s++
			star = false
		case '/':
			c.s++
			if star {
				return nil
			}
			star = false
			// TODO: case '\\':
		}
	}
	return errors.New("nstcc: unexpected end of file in comment")
}

func (c *compiler) parseSlashSlashComment() {
	for c.s < len(c.src) {
		switch c.src[c.s] {
		default:
			c.s++
		case '\n':
			return
			// TODO: case '\\':
		}
	}
}
<commit_msg>Parse a few more symbol tokens.<commit_after>package nstcc

import (
	"errors"
	"fmt"
)

func (c *compiler) next() error {
	for {
		if c.parseFlags&parseFlagSpaces != 0 {
			if err := c.nextNoMacroSpace(); err != nil {
				return err
			}
		} else {
			if err := c.nextNoMacro(); err != nil {
				return err
			}
		}

		if true { // TODO: the TCC code says "if (!macro_ptr)".
			if c.tok >= tokIdent && c.parseFlags&parseFlagPreprocess != 0 {
				// TODO: if not reading from macro substituted string, then try
				// to substitute macros.
			}

		} else {
			// TODO: macro_ptr code path.
		}

		if c.tok == tokPPNum && c.parseFlags&parseFlagTokNum != 0 {
			// TODO: convert preprocessor tokens into C tokens.
		}

		return nil
	}
}

func (c *compiler) nextNoMacro() error {
	for {
		if err := c.nextNoMacroSpace(); err != nil {
			return err
		}
		if !c.tok.isSpace() {
			return nil
		}
	}
}

func (c *compiler) nextNoMacroSpace() error {
	// TODO: check what the TCC code calls macro_ptr.
	return c.nextNoMacro1()
}

func (c *compiler) nextNoMacro1() error {
redoNoStart:
	for {
		if c.s >= len(c.src) {
			c.tok = tokEOF
			return nil
		}

		switch t := c.src[c.s]; {
		default:
			return fmt.Errorf(`nstcc: unrecognized token '\x%02x'`, t)

		case t == ' ' || t == '\t':
			c.tok = token(t)
			c.s++
			return nil

		case t == '\f' || t == '\v' || t == '\r':
			c.s++
			continue redoNoStart

		case t == '\\':
			// TODO.

		case t == '\n':
			// TODO: file->line_num++
			c.tokFlags |= tokFlagBOL
			c.s++
			if c.parseFlags&parseFlagLineFeed == 0 {
				continue redoNoStart
			}
			c.tok = '\n'
			return nil

		case t == '#':
			// TODO.

		case t == 'L':
			// TODO: parse things like the wchar_t L"abc".
			fallthrough

		case isID[t]:
			s := c.s + 1
			for ; s < len(c.src); s++ {
				t = c.src[s]
				if !isIDNum[t] {
					break
				}
			}

			if t != '\\' {
				ts, err := c.idents.byStr(c.src[c.s:s])
				if err != nil {
					return err
				}
				c.tok = ts.tok
			} else {
				// TODO.
			}

			c.tokFlags = 0
			c.s = s
			return nil

		case t == '<':
			c.s++
			switch c.peekc() {
			case '=':
				c.s++
				c.tok = tokLE
			case '<':
				c.s++
				if c.peekc() == '=' {
					c.s++
					c.tok = tokAShL
				} else {
					c.tok = tokShL
				}
			default:
				c.tok = tokLT
			}
			c.tokFlags = 0
			return nil

		case t == '>':
			c.s++
			switch c.peekc() {
			case '=':
				c.s++
				c.tok = tokGE
			case '>':
				c.s++
				if c.peekc() == '=' {
					c.s++
					c.tok = tokASAR
				} else {
					c.tok = tokSAR
				}
			default:
				c.tok = tokGT
			}
			c.tokFlags = 0
			return nil

		case t == '&':
			c.s++
			switch c.peekc() {
			case '&':
				c.s++
				c.tok = tokLAnd
			case '=':
				c.s++
				c.tok = tokAAnd
			default:
				c.tok = '&'
			}
			c.tokFlags = 0
			return nil

		case t == '|':
			c.s++
			switch c.peekc() {
			case '|':
				c.s++
				c.tok = tokLOr
			case '=':
				c.s++
				c.tok = tokAOr
			default:
				c.tok = '|'
			}
			c.tokFlags = 0
			return nil

		case t == '+':
			c.s++
			switch c.peekc() {
			case '+':
				c.s++
				c.tok = tokInc
			case '=':
				c.s++
				c.tok = tokAAdd
			default:
				c.tok = '+'
			}
			c.tokFlags = 0
			return nil

		case t == '-':
			c.s++
			switch c.peekc() {
			case '-':
				c.s++
				c.tok = tokDec
			case '=':
				c.s++
				c.tok = tokASub
			case '>':
				c.s++
				c.tok = tokArrow
			default:
				c.tok = '-'
			}
			c.tokFlags = 0
			return nil

		case t == '!':
			c.s++
			if c.peekc() == '=' {
				c.s++
				c.tok = tokNE
			} else {
				c.tok = '!'
			}
			c.tokFlags = 0
			return nil

		case t == '=':
			c.s++
			if c.peekc() == '=' {
				c.s++
				c.tok = tokEq
			} else {
				c.tok = '='
			}
			c.tokFlags = 0
			return nil

		case t == '*':
			c.s++
			if c.peekc() == '=' {
				c.s++
				c.tok = tokAMul
			} else {
				c.tok = '*'
			}
			c.tokFlags = 0
			return nil

		case t == '%':
			c.s++
			if c.peekc() == '=' {
				c.s++
				c.tok = tokAMod
			} else {
				c.tok = '%'
			}
			c.tokFlags = 0
			return nil

		case t == '^':
			c.s++
			if c.peekc() == '=' {
				c.s++
				c.tok = tokAXor
			} else {
				c.tok = '^'
			}
			c.tokFlags = 0
			return nil

		case t == '/':
			c.s++
			switch c.peekc() {
			case '*':
				c.tok = ' '
				c.s++
				return c.parseSlashStarComment()
			case '/':
				c.tok = ' '
				c.s++
				c.parseSlashSlashComment()
				return nil
			case '=':
				c.tok = tokADiv
				c.tokFlags = 0
				c.s++
				return nil
			default:
				c.tok = '/'
				c.tokFlags = 0
				c.s++
				return nil
			}

		case isSimpleToken[t]:
			c.tok = token(t)
			c.tokFlags = 0
			c.s++
			return nil
		}
	}
}

func (c *compiler) peekc() token {
	if c.s >= len(c.src) {
		return tokEOF
	}
	// TODO: handle c == '\\'.
	return token(c.src[c.s])
}

func (c *compiler) parseSlashStarComment() error {
	star := false
	for c.s < len(c.src) {
		switch x := c.src[c.s]; x {
		default:
			c.s++
			star = x == '*'
		case '\n':
			// TODO: file->line_num++
			c.s++
			star = false
		case '/':
			c.s++
			if star {
				return nil
			}
			star = false
			// TODO: case '\\':
		}
	}
	return errors.New("nstcc: unexpected end of file in comment")
}

func (c *compiler) parseSlashSlashComment() {
	for c.s < len(c.src) {
		switch c.src[c.s] {
		default:
			c.s++
		case '\n':
			return
			// TODO: case '\\':
		}
	}
}
<|endoftext|>