<commit_before>// Package sqldb helps create tables from Go structures.
//
// Field tag format: `db:"key[:value] key[:value]..."`
// Keys:
// table: table name
// col: column name, col:- to skip.
// type: char, text and Go builtin types: string/bool/int/uint/int8...
// precision: for string and char type, it's the 'length', such as precision:100,
//            for float and double it's 'precision, exact', such as precision: 32,5.
// dbtype: the final database type, it will override type and precision key
// pk: primary key
// autoincr: auto increament
// notnull: not null
// default: default value, '-' to disable default
// unique: unique constraint name or empty
// fk: foreign key: TABLE.COLUMN
package sqldb

import (
	"bytes"
	"database/sql"
	"fmt"
	"reflect"
	"strings"
)

type Column struct {
	Name         string
	Type         string
	Precision    string
	DBType       string
	Primary      bool
	AutoIncr     bool
	Notnull      bool
	Default      bool
	DefaultVal   string
	Unique       bool
	UniqueName   string
	ForeignTable string
	ForeignCol   string
}

type Table struct {
	Name string
	Cols []Column
}

type Config struct {
	DBDialect  DBDialect
	FieldTag   string
	Default    bool
	Notnull    bool
	NameMapper NameMapper
}

func (c *Config) initDefault() {
	if c.DBDialect == nil {
		c.DBDialect = Postgres{}
	}
	if c.FieldTag == "" {
		c.FieldTag = "db"
	}
	if c.NameMapper == nil {
		c.NameMapper = strings.ToLower
	}
}

func (c *Config) CreateTables(db *sql.DB, models ...interface{}) error {
	for _, mod := range models {
		table, err := c.StructTable(mod)
		if err != nil {
			return err
		}
		s, err := c.SQLCreate(table)
		if err != nil {
			return fmt.Errorf("%s: %s", table.Name, err.Error())
		}
		_, err = db.Exec(s)
		if err != nil {
			return fmt.Errorf("%s: %s", table.Name, err.Error())
		}
	}
	return nil
}

func (c *Config) SQLCreate(table Table) (string, error) {
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "CREATE TABLE IF NOT EXISTS %s (\n", table.Name)
	var (
		uniques   map[string][]string
		primaries []string
		foreigns  []int
		lastQuite string
	)
	for i, col := range table.Cols {
		dbTyp, defaultVal, err := c.DBDialect.Type(col.Type, col.Precision, col.DefaultVal)
		if err != nil {
			return "", err
		}
		if col.Primary {
			primaries = append(primaries, col.Name)
		}
		if col.ForeignTable != "" {
			foreigns = append(foreigns, i)
		}
		if col.DBType != "" {
			dbTyp = col.DBType
		}
		var constraints string
		if col.Unique {
			if col.UniqueName == "" {
				constraints += " UNIQUE"
			} else {
				if uniques == nil {
					uniques = make(map[string][]string)
				}
				uniques[col.UniqueName] = append(uniques[col.UniqueName], col.Name)
			}
		}
		if col.AutoIncr {
			constraints += " AUTO INCREAMENT"
		}
		if !col.Notnull {
			constraints += " NOT NULL"
		}
		if col.Default {
			constraints += " DEFAULT " + defaultVal
		}
		lastQuite = ""
		if i != len(table.Cols)-1 || len(primaries) != 0 || len(uniques) != 0 || len(foreigns) != 0 {
			lastQuite = ","
		}
		fmt.Fprintf(&buf, "    %s %s %s%s\n", col.Name, dbTyp, constraints, lastQuite)
	}
	if len(primaries) > 0 {
		lastQuite = ""
		if len(uniques) != 0 || len(foreigns) != 0 {
			lastQuite = ","
		}
		fmt.Fprintf(&buf, "    PRIMARY KEY (%s)%s\n", strings.Join(primaries, ","), lastQuite)
	}
	for name, keys := range uniques {
		lastQuite = ""
		if len(foreigns) != 0 || len(uniques) != 1 {
			lastQuite = ","
		}
		fmt.Fprintf(&buf, "    CONSTRAINT %s UNIQUE (%s)%s\n", name, strings.Join(keys, ","), lastQuite)
		delete(uniques, name)
	}
	for i, index := range foreigns {
		col := table.Cols[index]
		lastQuite = ""
		if i != len(foreigns)-1 {
			lastQuite = ","
		}
		fmt.Fprintf(&buf, "    FOREIGN KEY(%s) REFERENCES %s(%s)%s\n", col.Name, col.ForeignTable, col.ForeignCol, lastQuite)
	}
	fmt.Fprintf(&buf, ");\n")
	return buf.String(), nil
}

func (c *Config) parseColumn(t *Table, f *reflect.StructField) (Column, error) {
	col := Column{
		Name:    c.NameMapper(f.Name),
		Type:    f.Type.Kind().String(),
		Default: c.Default,
		Notnull: !c.Notnull,
	}

	var conds []string
	tag := strings.TrimSpace(f.Tag.Get(c.FieldTag))
	if tag != "" {
		conds = strings.Split(tag, " ")
	}
	for _, sec := range conds {
		sec = strings.TrimSpace(sec)
		var (
			keyCond  = strings.SplitN(sec, ":", 2)
			condName = keyCond[0]
			condVal  string
		)
		if len(keyCond) > 1 {
			condVal = keyCond[1]
		}

		switch condName {
		case "table":
			t.Name = condVal
		case "col":
			col.Name = condVal
			if condVal == "" {
				return col, fmt.Errorf("invalid column name")
			}
			if condVal == "-" {
				col.Name = ""
				return col, nil
			}
			col.Name = condVal
		case "type":
			if condVal == "" {
				return col, fmt.Errorf("invalid column type: %s", col.Name)
			}
			col.Type = condVal
		case "precision":
			if condVal == "" {
				return col, fmt.Errorf("invalid column precision: %s", col.Name)
			}
			col.Precision = condVal
		case "dbtype":
			if condVal == "" {
				return col, fmt.Errorf("invalid column db type: %s", col.Name)
			}
			col.DBType = condVal
		case "pk":
			col.Primary = condVal == "" || condVal == "true"
		case "autoincr":
			col.AutoIncr = condVal == "" || condVal == "true"
		case "notnull":
			col.Notnull = condVal == "" || condVal == "true"
		case "default":
			col.Default = condVal != "-"
			if c.Default {
				col.DefaultVal = condVal
			}
		case "unique":
			col.Unique = true
			col.UniqueName = condVal
		case "fk":
			fkConds := strings.SplitN(condVal, ".", 2)
			if len(fkConds) != 2 || fkConds[0] == "" || fkConds[1] == "" {
				return col, fmt.Errorf("invalid foreign key: %s", condVal)
			}
			col.ForeignTable = fkConds[0]
			col.ForeignCol = fkConds[1]
		default:
			return col, fmt.Errorf("unsupported tag: %s", condName)
		}
	}
	return col, nil
}

func (c *Config) StructTable(v interface{}) (Table, error) {
	c.initDefault()

	refv := reflect.ValueOf(v)
	if refv.Kind() == reflect.Ptr {
		refv = refv.Elem()
	}
	if refv.Kind() != reflect.Struct {
		return Table{}, fmt.Errorf("invalid artument type, expect (pointer of) structure")
	}
	reft := refv.Type()

	t := Table{
		Name: c.NameMapper(reft.Name()),
	}
	n := reft.NumField()
	for i := 0; i < n; i++ {
		f := reft.Field(i)
		col, err := c.parseColumn(&t, &f)
		if err != nil {
			return t, err
		}
		if col.Name != "" {
			t.Cols = append(t.Cols, col)
		}
	}
	return t, nil
}
<commit_msg>update doc<commit_after>// Package sqldb helps create tables from Go structures.
//
// Field tag format: `db:"key[:value] key[:value]..."`. Available keys:
//   table: table name
//   col: column name, col:- to skip.
//   type: char, text and Go builtin types: string/bool/int/uint/int8...
//   precision: for string and char type, it's the 'length', such as precision:100,
//              for float and double it's 'precision, exact', such as precision: 32,5.
//   dbtype: the final database type, it will override type and precision key
//   pk: primary key
//   autoincr: auto increament
//   notnull: not null
//   default: default value, '-' to disable default
//   unique: unique constraint name or empty
//   fk: foreign key: TABLE.COLUMN
package sqldb

import (
	"bytes"
	"database/sql"
	"fmt"
	"reflect"
	"strings"
)

type Column struct {
	Name         string
	Type         string
	Precision    string
	DBType       string
	Primary      bool
	AutoIncr     bool
	Notnull      bool
	Default      bool
	DefaultVal   string
	Unique       bool
	UniqueName   string
	ForeignTable string
	ForeignCol   string
}

type Table struct {
	Name string
	Cols []Column
}

type Config struct {
	DBDialect  DBDialect
	FieldTag   string
	Default    bool
	Notnull    bool
	NameMapper NameMapper
}

func (c *Config) initDefault() {
	if c.DBDialect == nil {
		c.DBDialect = Postgres{}
	}
	if c.FieldTag == "" {
		c.FieldTag = "db"
	}
	if c.NameMapper == nil {
		c.NameMapper = strings.ToLower
	}
}

func (c *Config) CreateTables(db *sql.DB, models ...interface{}) error {
	for _, mod := range models {
		table, err := c.StructTable(mod)
		if err != nil {
			return err
		}
		s, err := c.SQLCreate(table)
		if err != nil {
			return fmt.Errorf("%s: %s", table.Name, err.Error())
		}
		_, err = db.Exec(s)
		if err != nil {
			return fmt.Errorf("%s: %s", table.Name, err.Error())
		}
	}
	return nil
}

func (c *Config) SQLCreate(table Table) (string, error) {
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "CREATE TABLE IF NOT EXISTS %s (\n", table.Name)
	var (
		uniques   map[string][]string
		primaries []string
		foreigns  []int
		lastQuite string
	)
	for i, col := range table.Cols {
		dbTyp, defaultVal, err := c.DBDialect.Type(col.Type, col.Precision, col.DefaultVal)
		if err != nil {
			return "", err
		}
		if col.Primary {
			primaries = append(primaries, col.Name)
		}
		if col.ForeignTable != "" {
			foreigns = append(foreigns, i)
		}
		if col.DBType != "" {
			dbTyp = col.DBType
		}
		var constraints string
		if col.Unique {
			if col.UniqueName == "" {
				constraints += " UNIQUE"
			} else {
				if uniques == nil {
					uniques = make(map[string][]string)
				}
				uniques[col.UniqueName] = append(uniques[col.UniqueName], col.Name)
			}
		}
		if col.AutoIncr {
			constraints += " AUTO INCREAMENT"
		}
		if !col.Notnull {
			constraints += " NOT NULL"
		}
		if col.Default {
			constraints += " DEFAULT " + defaultVal
		}
		lastQuite = ""
		if i != len(table.Cols)-1 || len(primaries) != 0 || len(uniques) != 0 || len(foreigns) != 0 {
			lastQuite = ","
		}
		fmt.Fprintf(&buf, "    %s %s %s%s\n", col.Name, dbTyp, constraints, lastQuite)
	}
	if len(primaries) > 0 {
		lastQuite = ""
		if len(uniques) != 0 || len(foreigns) != 0 {
			lastQuite = ","
		}
		fmt.Fprintf(&buf, "    PRIMARY KEY (%s)%s\n", strings.Join(primaries, ","), lastQuite)
	}
	for name, keys := range uniques {
		lastQuite = ""
		if len(foreigns) != 0 || len(uniques) != 1 {
			lastQuite = ","
		}
		fmt.Fprintf(&buf, "    CONSTRAINT %s UNIQUE (%s)%s\n", name, strings.Join(keys, ","), lastQuite)
		delete(uniques, name)
	}
	for i, index := range foreigns {
		col := table.Cols[index]
		lastQuite = ""
		if i != len(foreigns)-1 {
			lastQuite = ","
		}
		fmt.Fprintf(&buf, "    FOREIGN KEY(%s) REFERENCES %s(%s)%s\n", col.Name, col.ForeignTable, col.ForeignCol, lastQuite)
	}
	fmt.Fprintf(&buf, ");\n")
	return buf.String(), nil
}

func (c *Config) parseColumn(t *Table, f *reflect.StructField) (Column, error) {
	col := Column{
		Name:    c.NameMapper(f.Name),
		Type:    f.Type.Kind().String(),
		Default: c.Default,
		Notnull: !c.Notnull,
	}

	var conds []string
	tag := strings.TrimSpace(f.Tag.Get(c.FieldTag))
	if tag != "" {
		conds = strings.Split(tag, " ")
	}
	for _, sec := range conds {
		sec = strings.TrimSpace(sec)
		var (
			keyCond  = strings.SplitN(sec, ":", 2)
			condName = keyCond[0]
			condVal  string
		)
		if len(keyCond) > 1 {
			condVal = keyCond[1]
		}

		switch condName {
		case "table":
			t.Name = condVal
		case "col":
			col.Name = condVal
			if condVal == "" {
				return col, fmt.Errorf("invalid column name")
			}
			if condVal == "-" {
				col.Name = ""
				return col, nil
			}
			col.Name = condVal
		case "type":
			if condVal == "" {
				return col, fmt.Errorf("invalid column type: %s", col.Name)
			}
			col.Type = condVal
		case "precision":
			if condVal == "" {
				return col, fmt.Errorf("invalid column precision: %s", col.Name)
			}
			col.Precision = condVal
		case "dbtype":
			if condVal == "" {
				return col, fmt.Errorf("invalid column db type: %s", col.Name)
			}
			col.DBType = condVal
		case "pk":
			col.Primary = condVal == "" || condVal == "true"
		case "autoincr":
			col.AutoIncr = condVal == "" || condVal == "true"
		case "notnull":
			col.Notnull = condVal == "" || condVal == "true"
		case "default":
			col.Default = condVal != "-"
			if c.Default {
				col.DefaultVal = condVal
			}
		case "unique":
			col.Unique = true
			col.UniqueName = condVal
		case "fk":
			fkConds := strings.SplitN(condVal, ".", 2)
			if len(fkConds) != 2 || fkConds[0] == "" || fkConds[1] == "" {
				return col, fmt.Errorf("invalid foreign key: %s", condVal)
			}
			col.ForeignTable = fkConds[0]
			col.ForeignCol = fkConds[1]
		default:
			return col, fmt.Errorf("unsupported tag: %s", condName)
		}
	}
	return col, nil
}

func (c *Config) StructTable(v interface{}) (Table, error) {
	c.initDefault()

	refv := reflect.ValueOf(v)
	if refv.Kind() == reflect.Ptr {
		refv = refv.Elem()
	}
	if refv.Kind() != reflect.Struct {
		return Table{}, fmt.Errorf("invalid artument type, expect (pointer of) structure")
	}
	reft := refv.Type()

	t := Table{
		Name: c.NameMapper(reft.Name()),
	}
	n := reft.NumField()
	for i := 0; i < n; i++ {
		f := reft.Field(i)
		col, err := c.parseColumn(&t, &f)
		if err != nil {
			return t, err
		}
		if col.Name != "" {
			t.Cols = append(t.Cols, col)
		}
	}
	return t, nil
}
<|endoftext|>


<commit_before>package crud

import "strconv"
import "fmt"
import "strings"
import "errors"

var (
	errInsertRepeat = errors.New("重复插入")
	errSQLSyncPanic = errors.New("SQL语句异常")
	errInsertData   = errors.New("插入数据库异常")
	errNoUpdateKey  = errors.New("没有更新主键")
)

// Table 是对CRUD进一层的封装
type Table struct {
	*CRUD
	tableName string
}

// Name 返回名称
func (t *Table) Name() string {
	return t.tableName
}

// All 返回这张表所有数据
func (t *Table) All() []map[string]string {
	return t.Query("SELECT * FROM " + t.tableName).RawsMap()
}

// Count 返回表有多少条数据
func (t *Table) Count() (count int) {
	t.Query("SELECT COUNT(*) FROM " + t.tableName).Scan(&count)
	return
}

// UpdateTime 查找表的更新时间
func (t *Table) UpdateTime() (updateTime string) {
	t.Query("SELECT `UPDATE_TIME` FROM  INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA =(select database()) AND TABLE_NAME = '" + t.tableName + "';").Scan(&updateTime)
	return
}

// AutoIncrement 查找表的自增ID的值
func (t *Table) AutoIncrement() (id int) {
	t.Query("SELECT `AUTO_INCREMENT` FROM  INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA =(select database()) AND TABLE_NAME = '" + t.tableName + "';").Scan(&id)
	return
}

// SetAutoIncrement 设置自动增长ID
func (t *Table) SetAutoIncrement(id int) error {
	_, err := t.Exec("ALTER TABLE `" + t.tableName + "` AUTO_INCREMENT = " + strconv.Itoa(id)).RowsAffected()
	return err
}

// MaxID 查找表的最大ID，如果为NULL的话则为0
func (t *Table) MaxID() (maxid int) {
	t.Query("SELECT IFNULL(MAX(id), 0) as id FROM `" + t.tableName + "`").Scan(&maxid)
	return
}

// IDIn 查找多个ID对应的列
func (t *Table) IDIn(ids ...interface{}) *SQLRows {
	if len(ids) == 0 {
		return &SQLRows{}
	}
	return t.Query(fmt.Sprintf("SELECT * FROM %s WHERE id in (%s)", t.tableName, argslice(len(ids))), ids...)
}

// Create 创建
// check 如果有，则会判断表里面以这几个字段为唯一的话，数据库是否存在此条数据，如果有就不插入了。
//
func (t *Table) Create(m map[string]interface{}, checks ...string) error {
	//INSERT INTO `feedback` (`task_id`, `template_question_id`, `question_options_id`, `suggestion`, `member_id`) VALUES ('1', '1', '1', '1', '1')
	if len(checks) > 0 {
		names := []string{}
		values := []interface{}{}
		for _, check := range checks {
			names = append(names, "`"+check+"`"+" = ? ")
			values = append(values, m[check])
		}
		// SELECT COUNT(*) FROM `feedback` WHERE `task_id` = ? AND `member_id` = ?
		if t.Query(fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE %s", t.tableName, strings.Join(names, "AND")), values...).Int() > 0 {
			return errInsertRepeat
		}
	}
	ks, vs := ksvs(m)
	e, err := t.Exec(fmt.Sprintf("INSERT INTO `%s` (%s) VALUES (%s)", t.tableName, strings.Join(ks, ","), argslice(len(ks))), vs...).RowsAffected()
	if err != nil {
		return errors.New("SQL语句异常")
	}
	if e <= 0 {
		return errors.New("插入数据库异常")
	}
	return nil
}

//Reads 查找
func (t *Table) Reads(m map[string]interface{}) []map[string]string {
	//SELECT * FROM address WHERE id = 1 AND uid = 27
	ks, vs := ksvs(m, " = ? ")
	return t.Query(fmt.Sprintf("SELECT * FROM %s WHERE %s", t.tableName, strings.Join(ks, "AND")), vs...).RawsMap()
}

func (t *Table) Read(m map[string]interface{}) map[string]string {
	rs := t.Reads(m)
	if len(rs) > 0 {
		return rs[0]
	}
	return map[string]string{}
}

// Update 更新
// 如果map里面有id的话会自动删除id，然后使用id来作为更新的条件。
func (t *Table) Update(m map[string]interface{}, keys ...string) error {
	if len(keys) == 0 {
		keys = append(keys, "id")
	}
	keysValue := []interface{}{}
	whereks := []string{}
	for _, key := range keys {
		val, ok := m[key]
		if !ok {
			return errors.New("没有更新主键")
		}
		keysValue = append(keysValue, val)
		delete(m, key)
		whereks = append(whereks, "`"+key+"` = ? ")
	}
	//因为在更新的时候最好不要更新ID，而有时候又会将ID传入进来，所以id每次都会被删除，如果要更新id的话使用Exec()
	delete(m, "id")
	ks, vs := ksvs(m, " = ? ")
	for _, val := range keysValue {
		vs = append(vs, val)
	}
	_, err := t.Exec(fmt.Sprintf("UPDATE `%s` SET %s WHERE %s LIMIT 1", t.tableName, strings.Join(ks, ","), strings.Join(whereks, "AND")), vs...).RowsAffected()
	if err != nil {
		return errors.New("SQL语句异常")
	}
	return nil
}

//CreateOrUpdate 创建或者更新
func (t *Table) CreateOrUpdate(m map[string]interface{}, keys ...string) error {
	err := t.Create(m, keys...)
	if err != nil {
		if err == errInsertRepeat {
			return t.Update(m, keys...)
		}
		return err
	}
	return nil
}

// Delete 删除
func (t *Table) Delete(m map[string]interface{}) (int64, error) {
	ks, vs := ksvs(m, " = ? ")
	return t.Exec(fmt.Sprintf("DELETE FROM %s WHERE %s", t.tableName, strings.Join(ks, "AND")), vs...).RowsAffected()
}
<commit_msg>create 返回ID<commit_after>package crud

import "strconv"
import "fmt"
import "strings"
import "errors"

var (
	errInsertRepeat = errors.New("重复插入")
	errSQLSyncPanic = errors.New("SQL语句异常")
	errInsertData   = errors.New("插入数据库异常")
	errNoUpdateKey  = errors.New("没有更新主键")
)

// Table 是对CRUD进一层的封装
type Table struct {
	*CRUD
	tableName string
}

// Name 返回名称
func (t *Table) Name() string {
	return t.tableName
}

// All 返回这张表所有数据
func (t *Table) All() []map[string]string {
	return t.Query("SELECT * FROM " + t.tableName).RawsMap()
}

// Count 返回表有多少条数据
func (t *Table) Count() (count int) {
	t.Query("SELECT COUNT(*) FROM " + t.tableName).Scan(&count)
	return
}

// UpdateTime 查找表的更新时间
func (t *Table) UpdateTime() (updateTime string) {
	t.Query("SELECT `UPDATE_TIME` FROM  INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA =(select database()) AND TABLE_NAME = '" + t.tableName + "';").Scan(&updateTime)
	return
}

// AutoIncrement 查找表的自增ID的值
func (t *Table) AutoIncrement() (id int) {
	t.Query("SELECT `AUTO_INCREMENT` FROM  INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA =(select database()) AND TABLE_NAME = '" + t.tableName + "';").Scan(&id)
	return
}

// SetAutoIncrement 设置自动增长ID
func (t *Table) SetAutoIncrement(id int) error {
	_, err := t.Exec("ALTER TABLE `" + t.tableName + "` AUTO_INCREMENT = " + strconv.Itoa(id)).RowsAffected()
	return err
}

// MaxID 查找表的最大ID，如果为NULL的话则为0
func (t *Table) MaxID() (maxid int) {
	t.Query("SELECT IFNULL(MAX(id), 0) as id FROM `" + t.tableName + "`").Scan(&maxid)
	return
}

// IDIn 查找多个ID对应的列
func (t *Table) IDIn(ids ...interface{}) *SQLRows {
	if len(ids) == 0 {
		return &SQLRows{}
	}
	return t.Query(fmt.Sprintf("SELECT * FROM %s WHERE id in (%s)", t.tableName, argslice(len(ids))), ids...)
}

// Create 创建
// check 如果有，则会判断表里面以这几个字段为唯一的话，数据库是否存在此条数据，如果有就不插入了。
//
func (t *Table) Create(m map[string]interface{}, checks ...string) (int64, error) {
	//INSERT INTO `feedback` (`task_id`, `template_question_id`, `question_options_id`, `suggestion`, `member_id`) VALUES ('1', '1', '1', '1', '1')
	if len(checks) > 0 {
		names := []string{}
		values := []interface{}{}
		for _, check := range checks {
			names = append(names, "`"+check+"`"+" = ? ")
			values = append(values, m[check])
		}
		// SELECT COUNT(*) FROM `feedback` WHERE `task_id` = ? AND `member_id` = ?
		if t.Query(fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE %s", t.tableName, strings.Join(names, "AND")), values...).Int() > 0 {
			return 0, errInsertRepeat
		}
	}
	ks, vs := ksvs(m)
	id, err := t.Exec(fmt.Sprintf("INSERT INTO `%s` (%s) VALUES (%s)", t.tableName, strings.Join(ks, ","), argslice(len(ks))), vs...).LastInsertId()
	if err != nil {
		return 0, errors.New("SQL语句异常")
	}
	if id <= 0 {
		return 0, errors.New("插入数据库异常")
	}
	return id, nil
}

//Reads 查找
func (t *Table) Reads(m map[string]interface{}) []map[string]string {
	//SELECT * FROM address WHERE id = 1 AND uid = 27
	ks, vs := ksvs(m, " = ? ")
	return t.Query(fmt.Sprintf("SELECT * FROM %s WHERE %s", t.tableName, strings.Join(ks, "AND")), vs...).RawsMap()
}

func (t *Table) Read(m map[string]interface{}) map[string]string {
	rs := t.Reads(m)
	if len(rs) > 0 {
		return rs[0]
	}
	return map[string]string{}
}

// Update 更新
// 如果map里面有id的话会自动删除id，然后使用id来作为更新的条件。
func (t *Table) Update(m map[string]interface{}, keys ...string) error {
	if len(keys) == 0 {
		keys = append(keys, "id")
	}
	keysValue := []interface{}{}
	whereks := []string{}
	for _, key := range keys {
		val, ok := m[key]
		if !ok {
			return errors.New("没有更新主键")
		}
		keysValue = append(keysValue, val)
		delete(m, key)
		whereks = append(whereks, "`"+key+"` = ? ")
	}
	//因为在更新的时候最好不要更新ID，而有时候又会将ID传入进来，所以id每次都会被删除，如果要更新id的话使用Exec()
	delete(m, "id")
	ks, vs := ksvs(m, " = ? ")
	for _, val := range keysValue {
		vs = append(vs, val)
	}
	_, err := t.Exec(fmt.Sprintf("UPDATE `%s` SET %s WHERE %s LIMIT 1", t.tableName, strings.Join(ks, ","), strings.Join(whereks, "AND")), vs...).RowsAffected()
	if err != nil {
		return errors.New("SQL语句异常")
	}
	return nil
}

//CreateOrUpdate 创建或者更新
func (t *Table) CreateOrUpdate(m map[string]interface{}, keys ...string) error {
	_, err := t.Create(m, keys...)
	if err != nil {
		if err == errInsertRepeat {
			return t.Update(m, keys...)
		}
		return err
	}
	return nil
}

// Delete 删除
func (t *Table) Delete(m map[string]interface{}) (int64, error) {
	ks, vs := ksvs(m, " = ? ")
	return t.Exec(fmt.Sprintf("DELETE FROM %s WHERE %s", t.tableName, strings.Join(ks, "AND")), vs...).RowsAffected()
}
<|endoftext|>


<commit_before>// Copyright 2017 Tamás Gulácsi
//
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.

package main

import (
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"encoding/xml"
	"fmt"
	"io"
	"log"
	"path"
	"strings"
	"sync"
	"text/template"
	"time"
	"unicode"

	"github.com/golang/protobuf/protoc-gen-go/descriptor"
	protoc "github.com/golang/protobuf/protoc-gen-go/plugin"
	"golang.org/x/sync/errgroup"
)

const wsdlTmpl = xml.Header + `<wsdl:definitions
    name="{{.Package}}"
    targetNamespace="{{.TargetNS}}"
    xmlns:tns="{{.TargetNS}}"
    xmlns:types="{{.TypesNS}}"
    xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <wsdl:documentation>
    Service: {{.Package}}
    Version: {{.Version}}
    Generated: {{.GeneratedAt}}
    Owner: {{.Owner}}
  </wsdl:documentation>
  <wsdl:types>
    <xsd:schema elementFormDefault="qualified" targetNamespace="{{.TargetNS}}">
      <xsd:element name="exception">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="type" nillable="true" type="xsd:string"/>
            <xsd:element name="message" nillable="true" type="xsd:string"/>
            <xsd:element name="traceback" nillable="true" type="xsd:string"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
      {{range $k, $m := .Types}}
        {{mkType $k $m}}
      {{end}}
    </xsd:schema>
  </wsdl:types>
  <wsdl:message name="error">
    <wsdl:part element="types:exception" name="error"/>
  </wsdl:message>
  {{range .GetMethod}}
  <wsdl:message name="{{mkTypeName .GetInputType}}" />
  <wsdl:message name="{{mkTypeName .GetOutputType}}" />
  {{end}}
  <wsdl:portType name="{{.Package}}">
    {{range .GetMethod}}
    <wsdl:operation name="{{.GetName}}">
      <wsdl:input message="tns:{{mkTypeName .GetInputType}}"/>
      <wsdl:output message="tns:{{mkTypeName .GetOutputType}}"/>
      <wsdl:fault message="tns:error" name="error"/>
    </wsdl:operation>
    {{end}}
  </wsdl:portType>
  <wsdl:binding name="{{.Package}}_soap" type="tns:{{.Package}}">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
    {{range .GetMethod}}<wsdl:operation name="{{.Name}}">
      <soap:operation soapAction="{{$.TargetNS}}{{.GetName}}" style="document"/>
      <input><soap:body use="literal"/></input>
      <output><soap:body use="literal"/></output>
      <fault name="error"><soap:fault name="error" use="literal"/></fault>
    </wsdl:operation>{{end}}
  </wsdl:binding>
  <wsdl:service name="{{.Package}}__service">
    <wsdl:port binding="tns:{{.Package}}_soap" name="web">
      <soap:address location="http://localhost:12469/portal/"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>
`

func Generate(resp *protoc.CodeGeneratorResponse, req protoc.CodeGeneratorRequest) error {
	destPkg := req.GetParameter()
	if destPkg == "" {
		destPkg = "main"
	}
	// Find roots.
	rootNames := req.GetFileToGenerate()
	files := req.GetProtoFile()
	roots := make(map[string]*descriptor.FileDescriptorProto, len(rootNames))
	allTypes := make(map[string]*descriptor.DescriptorProto, 1024)
	var found int
	for i := len(files) - 1; i >= 0; i-- {
		f := files[i]
		for _, m := range f.GetMessageType() {
			allTypes["."+f.GetPackage()+"."+m.GetName()] = m
		}
		if found == len(rootNames) {
			continue
		}
		for _, root := range rootNames {
			if f.GetName() == root {
				roots[root] = files[i]
				found++
				break
			}
		}
	}

	msgTypes := make(map[string]*descriptor.DescriptorProto, len(allTypes))
	for _, root := range roots {
		//k := "." + root.GetName() + "."
		var k string
		for _, svc := range root.GetService() {
			for _, m := range svc.GetMethod() {
				if kk := k + m.GetInputType(); len(kk) > len(k) {
					msgTypes[kk] = allTypes[kk]
				}
				if kk := k + m.GetOutputType(); len(kk) > len(k) {
					msgTypes[kk] = allTypes[kk]
				}
			}
		}
	}

	// delete not needed message types
	wsdlTemplate := template.Must(template.
		New("wsdl").
		Funcs(template.FuncMap{
			"mkTypeName": mkTypeName,
			"mkType":     typer{Types: allTypes}.mkType,
		}).
		Parse(wsdlTmpl))

	// Service, bind and message types from roots.
	// All other types are from imported dependencies, recursively.
	type whole struct {
		*descriptor.ServiceDescriptorProto
		Package           string
		TargetNS, TypesNS string
		Types             map[string]*descriptor.DescriptorProto
		GeneratedAt       time.Time
		Version, Owner    string
	}

	now := time.Now()
	var grp errgroup.Group
	var mu sync.Mutex
	resp.File = make([]*protoc.CodeGeneratorResponse_File, 0, len(roots))
	for _, root := range roots {
		root := root
		pkg := root.GetName()
		for _, svc := range root.GetService() {
			grp.Go(func() error {
				data := whole{
					Package:  svc.GetName(),
					TargetNS: "http://" + pkg + "/" + svc.GetName() + "/",
					TypesNS:  "http://" + pkg + "/" + svc.GetName() + "_types/",
					Types:    msgTypes,

					ServiceDescriptorProto: svc,
					GeneratedAt:            now,
				}
				destFn := strings.TrimSuffix(path.Base(pkg), ".proto") + ".wsdl"
				buf := bufPool.Get().(*bytes.Buffer)
				buf.Reset()
				defer func() {
					buf.Reset()
					bufPool.Put(buf)
				}()
				err := wsdlTemplate.Execute(buf, data)
				content := buf.String()
				mu.Lock()
				if err != nil {
					errS := err.Error()
					resp.Error = &errS
				}
				resp.File = append(resp.File,
					&protoc.CodeGeneratorResponse_File{
						Name:    &destFn,
						Content: &content,
					})
				// also, embed the wsdl
				goFn := destFn + ".go"
				goContent := `package ` + destPkg + `

// WSDLgzb64 contains the WSDL, gzipped and base64-encoded.
// You can easily read it with soapproxy.Ungzb64.
const WSDLgzb64 = ` + "`" + gzb64(content) + "`\n"
				resp.File = append(resp.File,
					&protoc.CodeGeneratorResponse_File{
						Name:    &goFn,
						Content: &goContent,
					})
				mu.Unlock()
				return nil
			})
		}
	}
	return grp.Wait()
}

var bufPool = sync.Pool{New: func() interface{} { return bytes.NewBuffer(make([]byte, 0, 4096)) }}

var typeTemplate = template.Must(
	template.New("type").
		Funcs(template.FuncMap{
			"mkXSDElement": mkXSDElement,
		}).
		Parse(`
<xsd:element name="{{.Name}}">
  <xsd:complexType>
    <xsd:all>
	{{range .Fields}}{{mkXSDElement .}}
	{{end}}
    </xsd:all>
  </xsd:complexType>
</xsd:element>
`))

type typer struct {
	Types map[string]*descriptor.DescriptorProto
}

func (t typer) mkType(fullName string, m *descriptor.DescriptorProto) string {
	buf := bufPool.Get().(*bytes.Buffer)
	buf.Reset()
	defer func() {
		buf.Reset()
		bufPool.Put(buf)
	}()

	subTypes := make(map[string][]*descriptor.FieldDescriptorProto)
	for _, f := range m.GetField() {
		tn := mkTypeName(f.GetTypeName())
		if tn == "" {
			continue
		}
		subTypes[tn] = append(subTypes[tn],
			t.Types[f.GetTypeName()].GetField()...)
	}
	type Fields struct {
		Name   string
		Fields []*descriptor.FieldDescriptorProto
	}
	for k, vv := range subTypes {
		if err := typeTemplate.Execute(buf,
			Fields{Name: k, Fields: vv},
		); err != nil {
			panic(err)
		}
	}
	if fullName == "" {
		fullName = m.GetName()
	}
	typName := mkTypeName(fullName)
	if err := typeTemplate.Execute(buf,
		Fields{Name: typName, Fields: m.GetField()},
	); err != nil {
		panic(err)
	}
	return buf.String()
}

func mkXSDElement(f *descriptor.FieldDescriptorProto) string {
	maxOccurs := 1
	if f.GetLabel() == descriptor.FieldDescriptorProto_LABEL_REPEATED {
		maxOccurs = 999
	}
	name := CamelCase(f.GetName())
	typ := mkTypeName(f.GetTypeName())
	if typ == "" {
		typ = xsdType(f.GetType())
		if typ == "" {
			log.Printf("no type name for %s (%s)", f.GetTypeName(), f)
		}
	} else {
		typ = "tns:" + typ
	}
	return fmt.Sprintf(
		`<xsd:element minOccurs="0" nillable="true" maxOccurs="%d" name="%s" type="%s"/>`,
		maxOccurs, name, typ,
	)
}

func mkTypeName(s string) string {
	s = strings.TrimPrefix(s, ".")
	if s == "" {
		return s
	}
	if i := strings.IndexByte(s, '.'); i >= 0 {
		return CamelCase(s[:i]) + "_" + (s[i+1:])
	}
	if 'A' <= s[0] && s[0] <= 'Z' {
		return (s)
	}
	return s
}

func xsdType(t descriptor.FieldDescriptorProto_Type) string {
	switch t {
	case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
		return "xsd:double"
	case descriptor.FieldDescriptorProto_TYPE_FLOAT:
		return "xsd:float"
	case descriptor.FieldDescriptorProto_TYPE_INT64,
		descriptor.FieldDescriptorProto_TYPE_FIXED64,
		descriptor.FieldDescriptorProto_TYPE_SFIXED64,
		descriptor.FieldDescriptorProto_TYPE_SINT64:
		return "xsd:long"
	case descriptor.FieldDescriptorProto_TYPE_UINT64:
		return "xsd:unsignedLong"
	case descriptor.FieldDescriptorProto_TYPE_INT32,
		descriptor.FieldDescriptorProto_TYPE_FIXED32,
		descriptor.FieldDescriptorProto_TYPE_SFIXED32,
		descriptor.FieldDescriptorProto_TYPE_SINT32:
		return "xsd:int"
	case descriptor.FieldDescriptorProto_TYPE_BOOL:
		return "xsd:bool"
	case descriptor.FieldDescriptorProto_TYPE_STRING:
		return "xsd:string"
	case descriptor.FieldDescriptorProto_TYPE_GROUP:
		return "?grp?"
	case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
		return "?msg?"
	case descriptor.FieldDescriptorProto_TYPE_BYTES:
		return "xsd:base64Binary"
	case descriptor.FieldDescriptorProto_TYPE_UINT32:
		return "xsd:unsignedInt"
	case descriptor.FieldDescriptorProto_TYPE_ENUM:
		return "?enum?"
	}
	return "???"
}

var digitUnder = strings.NewReplacer(
	"_0", "__0",
	"_1", "__1",
	"_2", "__2",
	"_3", "__3",
	"_4", "__4",
	"_5", "__5",
	"_6", "__6",
	"_7", "__7",
	"_8", "__8",
	"_9", "__9",
)

func CamelCase(text string) string {
	if text == "" {
		return text
	}
	var prefix string
	if text[0] == '*' {
		prefix, text = "*", text[1:]
	}

	text = digitUnder.Replace(text)
	var last rune
	return prefix + strings.Map(func(r rune) rune {
		defer func() { last = r }()
		if r == '_' {
			if last != '_' {
				return -1
			}
			return '_'
		}
		if last == 0 || last == '_' || '0' <= last && last <= '9' {
			return unicode.ToUpper(r)
		}
		return unicode.ToLower(r)
	},
		text,
	)
}

// UnCamelCase reverses CamelCase
func UnCamelCase(s string) string {
	v := make([]rune, 0, len(s))
	for i, r := range s {
		if i == 0 {
			v = append(v, unicode.ToLower(r))
			continue
		}
		if unicode.IsUpper(r) {
			v = append(v, '_', unicode.ToLower(r))
			continue
		}
		v = append(v, r)
	}
	return string(v)
}

func gzb64(s string) string {
	buf := bufPool.Get().(*bytes.Buffer)
	defer func() {
		buf.Reset()
		bufPool.Put(buf)
	}()
	buf.Reset()
	bw := base64.NewEncoder(base64.StdEncoding, buf)
	gw := gzip.NewWriter(bw)
	io.WriteString(gw, s)
	if err := gw.Close(); err != nil {
		panic(err)
	}
	if err := bw.Close(); err != nil {
		panic(err)
	}
	return buf.String()
}

// vim: set fileencoding=utf-8 noet:
<commit_msg>xsd:sequence to allow maxOccurs>1<commit_after>// Copyright 2017 Tamás Gulácsi
//
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.

package main

import (
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"encoding/xml"
	"fmt"
	"io"
	"log"
	"path"
	"strings"
	"sync"
	"text/template"
	"time"
	"unicode"

	"github.com/golang/protobuf/protoc-gen-go/descriptor"
	protoc "github.com/golang/protobuf/protoc-gen-go/plugin"
	"golang.org/x/sync/errgroup"
)

const wsdlTmpl = xml.Header + `<definitions
    name="{{.Package}}"
    targetNamespace="{{.TargetNS}}"
    xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:tns="{{.TargetNS}}"
    xmlns:types="{{.TypesNS}}"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <documentation>
    Service: {{.Package}}
    Version: {{.Version}}
    Generated: {{.GeneratedAt}}
    Owner: {{.Owner}}
  </documentation>
  <types>
    <xsd:schema elementFormDefault="qualified" targetNamespace="{{.TypesNS}}">
      <xsd:element name="exception">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="type" nillable="true" type="xsd:string"/>
            <xsd:element name="message" nillable="true" type="xsd:string"/>
            <xsd:element name="traceback" nillable="true" type="xsd:string"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
      {{range $k, $m := .Types}}
        {{mkType $k $m}}
      {{end}}
    </xsd:schema>
  </types>
  <message name="error">
    <part element="types:exception" name="error"/>
  </message>
  {{range .GetMethod}}
  {{$input := mkTypeName .GetInputType}}
  {{$output := mkTypeName .GetOutputType}}
  <message name="{{$input}}">
    <part element="types:{{$input}}" name="{{$input}}" />
  </message>
  <message name="{{$output}}">
    <part element="types:{{$output}}" name="{{$output}}" />
  </message>
  {{end}}
  <portType name="{{.Package}}">
    {{range .GetMethod}}
    <operation name="{{.GetName}}">
      <input message="tns:{{mkTypeName .GetInputType}}"/>
      <output message="tns:{{mkTypeName .GetOutputType}}"/>
      <fault message="tns:error" name="error"/>
    </operation>{{end}}
  </portType>
  <binding name="{{.Package}}_soap" type="tns:{{.Package}}">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
    {{range .GetMethod}}
    <operation name="{{.Name}}">
      <wsdl:documentation></wsdl:documentation>
      <soap:operation soapAction="{{$.TargetNS}}{{.GetName}}" style="document" />
      <input><soap:body use="literal"/></input>
      <output><soap:body use="literal"/></output>
      <fault name="error"><soap:fault name="error" use="literal"/></fault>
    </operation>{{end}}
  </binding>
  <service name="{{.Package}}__service">
    <port binding="tns:{{.Package}}_soap" name="{{.Package}}">
      {{range .Locations}}
      <soap:address location="{{.}}"/>
      {{end}}
    </port>
  </service>
</definitions>
`

func Generate(resp *protoc.CodeGeneratorResponse, req protoc.CodeGeneratorRequest) error {
	destPkg := req.GetParameter()
	if destPkg == "" {
		destPkg = "main"
	}
	// Find roots.
	rootNames := req.GetFileToGenerate()
	files := req.GetProtoFile()
	roots := make(map[string]*descriptor.FileDescriptorProto, len(rootNames))
	allTypes := make(map[string]*descriptor.DescriptorProto, 1024)
	var found int
	for i := len(files) - 1; i >= 0; i-- {
		f := files[i]
		for _, m := range f.GetMessageType() {
			allTypes["."+f.GetPackage()+"."+m.GetName()] = m
		}
		if found == len(rootNames) {
			continue
		}
		for _, root := range rootNames {
			if f.GetName() == root {
				roots[root] = files[i]
				found++
				break
			}
		}
	}

	msgTypes := make(map[string]*descriptor.DescriptorProto, len(allTypes))
	for _, root := range roots {
		//k := "." + root.GetName() + "."
		var k string
		for _, svc := range root.GetService() {
			for _, m := range svc.GetMethod() {
				if kk := k + m.GetInputType(); len(kk) > len(k) {
					msgTypes[kk] = allTypes[kk]
				}
				if kk := k + m.GetOutputType(); len(kk) > len(k) {
					msgTypes[kk] = allTypes[kk]
				}
			}
		}
	}

	// delete not needed message types
	wsdlTemplate := template.Must(template.
		New("wsdl").
		Funcs(template.FuncMap{
			"mkTypeName": mkTypeName,
			"mkType":     typer{Types: allTypes}.mkType,
		}).
		Parse(wsdlTmpl))

	// Service, bind and message types from roots.
	// All other types are from imported dependencies, recursively.
	type whole struct {
		*descriptor.ServiceDescriptorProto
		Package           string
		TargetNS, TypesNS string
		Types             map[string]*descriptor.DescriptorProto
		GeneratedAt       time.Time
		Version, Owner    string
		Locations         []string
	}

	now := time.Now()
	var grp errgroup.Group
	var mu sync.Mutex
	resp.File = make([]*protoc.CodeGeneratorResponse_File, 0, len(roots))
	for _, root := range roots {
		root := root
		pkg := root.GetName()
		for _, svc := range root.GetService() {
			grp.Go(func() error {
				data := whole{
					Package:  svc.GetName(),
					TargetNS: "http://" + pkg + "/" + svc.GetName() + "/",
					TypesNS:  "http://" + pkg + "/" + svc.GetName() + "_types/",
					Types:    msgTypes,

					ServiceDescriptorProto: svc,
					GeneratedAt:            now,
				}
				destFn := strings.TrimSuffix(path.Base(pkg), ".proto") + ".wsdl"
				buf := bufPool.Get().(*bytes.Buffer)
				buf.Reset()
				defer func() {
					buf.Reset()
					bufPool.Put(buf)
				}()
				err := wsdlTemplate.Execute(buf, data)
				content := buf.String()
				mu.Lock()
				if err != nil {
					errS := err.Error()
					resp.Error = &errS
				}
				resp.File = append(resp.File,
					&protoc.CodeGeneratorResponse_File{
						Name:    &destFn,
						Content: &content,
					})
				// also, embed the wsdl
				goFn := destFn + ".go"
				goContent := `package ` + destPkg + `

// WSDLgzb64 contains the WSDL, gzipped and base64-encoded.
// You can easily read it with soapproxy.Ungzb64.
const WSDLgzb64 = ` + "`" + gzb64(content) + "`\n"
				resp.File = append(resp.File,
					&protoc.CodeGeneratorResponse_File{
						Name:    &goFn,
						Content: &goContent,
					})
				mu.Unlock()
				return nil
			})
		}
	}
	return grp.Wait()
}

var bufPool = sync.Pool{New: func() interface{} { return bytes.NewBuffer(make([]byte, 0, 4096)) }}

var elementTypeTemplate = template.Must(
	template.New("type").
		Funcs(template.FuncMap{
			"mkXSDElement": mkXSDElement,
		}).
		Parse(`<xsd:element name="{{.Name}}">
  <xsd:complexType>
    <xsd:sequence>
	{{range .Fields}}{{mkXSDElement .}}
	{{end}}
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>
`))

var xsdTypeTemplate = template.Must(
	template.New("type").
		Funcs(template.FuncMap{
			"mkXSDElement": mkXSDElement,
		}).
		Parse(`<xsd:complexType name="{{.Name}}">
  <xsd:sequence>
  {{range .Fields}}{{mkXSDElement .}}
  {{end}}
  </xsd:sequence>
</xsd:complexType>
`))

type typer struct {
	Types map[string]*descriptor.DescriptorProto
}

func (t typer) mkType(fullName string, m *descriptor.DescriptorProto) string {
	buf := bufPool.Get().(*bytes.Buffer)
	buf.Reset()
	defer func() {
		buf.Reset()
		bufPool.Put(buf)
	}()

	// <BrunoLevelek_Output><PLevelek><SzerzAzon>1</SzerzAzon><Tipus></Tipus><Url></Url><Datum></Datum></PLevelek><PLevelek><SzerzAzon>2</SzerzAzon><Tipus>f</Tipus><Url></Url><Datum></Datum></PLevelek><PHibaKod>0</PHibaKod><PHibaSzov></PHibaSzov></BrunoLevelek_Output>Hello, playground
	subTypes := make(map[string][]*descriptor.FieldDescriptorProto)
	for _, f := range m.GetField() {
		tn := mkTypeName(f.GetTypeName())
		if tn == "" {
			continue
		}
		subTypes[tn] = append(subTypes[tn],
			t.Types[f.GetTypeName()].GetField()...)
	}
	type Fields struct {
		Name   string
		Fields []*descriptor.FieldDescriptorProto
	}
	if fullName == "" {
		fullName = m.GetName()
	}
	typName := mkTypeName(fullName)
	if err := elementTypeTemplate.Execute(buf,
		Fields{Name: typName, Fields: m.GetField()},
	); err != nil {
		panic(err)
	}
	for k, vv := range subTypes {
		if err := xsdTypeTemplate.Execute(buf,
			Fields{Name: k, Fields: vv},
		); err != nil {
			panic(err)
		}
	}
	return buf.String()
}

func mkXSDElement(f *descriptor.FieldDescriptorProto) string {
	maxOccurs := 1
	if f.GetLabel() == descriptor.FieldDescriptorProto_LABEL_REPEATED {
		maxOccurs = 999
	}
	name := CamelCase(f.GetName())
	typ := mkTypeName(f.GetTypeName())
	if typ == "" {
		typ = xsdType(f.GetType())
		if typ == "" {
			log.Printf("no type name for %s (%s)", f.GetTypeName(), f)
		}
	} else {
		typ = "types:" + typ
	}
	return fmt.Sprintf(
		`<xsd:element minOccurs="0" nillable="true" maxOccurs="%d" name="%s" type="%s"/>`,
		maxOccurs, name, typ,
	)
}

func mkTypeName(s string) string {
	s = strings.TrimPrefix(s, ".")
	if s == "" {
		return s
	}
	i := strings.IndexByte(s, '.')
	if i < 0 {
		return s
	}
	s = CamelCase(s[:i]) + "_" + (s[i+1:])
	i = strings.IndexByte(s, '_')
	if strings.HasPrefix(s[i+1:], s[:i+1]) {
		s = s[i+1:]
	}
	return s
}

func xsdType(t descriptor.FieldDescriptorProto_Type) string {
	switch t {
	case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
		return "xsd:double"
	case descriptor.FieldDescriptorProto_TYPE_FLOAT:
		return "xsd:float"
	case descriptor.FieldDescriptorProto_TYPE_INT64,
		descriptor.FieldDescriptorProto_TYPE_FIXED64,
		descriptor.FieldDescriptorProto_TYPE_SFIXED64,
		descriptor.FieldDescriptorProto_TYPE_SINT64:
		return "xsd:long"
	case descriptor.FieldDescriptorProto_TYPE_UINT64:
		return "xsd:unsignedLong"
	case descriptor.FieldDescriptorProto_TYPE_INT32,
		descriptor.FieldDescriptorProto_TYPE_FIXED32,
		descriptor.FieldDescriptorProto_TYPE_SFIXED32,
		descriptor.FieldDescriptorProto_TYPE_SINT32:
		return "xsd:int"
	case descriptor.FieldDescriptorProto_TYPE_BOOL:
		return "xsd:bool"
	case descriptor.FieldDescriptorProto_TYPE_STRING:
		return "xsd:string"
	case descriptor.FieldDescriptorProto_TYPE_GROUP:
		return "?grp?"
	case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
		return "?msg?"
	case descriptor.FieldDescriptorProto_TYPE_BYTES:
		return "xsd:base64Binary"
	case descriptor.FieldDescriptorProto_TYPE_UINT32:
		return "xsd:unsignedInt"
	case descriptor.FieldDescriptorProto_TYPE_ENUM:
		return "?enum?"
	}
	return "???"
}

var digitUnder = strings.NewReplacer(
	"_0", "__0",
	"_1", "__1",
	"_2", "__2",
	"_3", "__3",
	"_4", "__4",
	"_5", "__5",
	"_6", "__6",
	"_7", "__7",
	"_8", "__8",
	"_9", "__9",
)

func CamelCase(text string) string {
	if text == "" {
		return text
	}
	var prefix string
	if text[0] == '*' {
		prefix, text = "*", text[1:]
	}

	text = digitUnder.Replace(text)
	var last rune
	return prefix + strings.Map(func(r rune) rune {
		defer func() { last = r }()
		if r == '_' {
			if last != '_' {
				return -1
			}
			return '_'
		}
		if last == 0 || last == '_' || '0' <= last && last <= '9' {
			return unicode.ToUpper(r)
		}
		return unicode.ToLower(r)
	},
		text,
	)
}

// UnCamelCase reverses CamelCase
func UnCamelCase(s string) string {
	v := make([]rune, 0, len(s))
	for i, r := range s {
		if i == 0 {
			v = append(v, unicode.ToLower(r))
			continue
		}
		if unicode.IsUpper(r) {
			v = append(v, '_', unicode.ToLower(r))
			continue
		}
		v = append(v, r)
	}
	return string(v)
}

func gzb64(s string) string {
	buf := bufPool.Get().(*bytes.Buffer)
	defer func() {
		buf.Reset()
		bufPool.Put(buf)
	}()
	buf.Reset()
	bw := base64.NewEncoder(base64.StdEncoding, buf)
	gw := gzip.NewWriter(bw)
	io.WriteString(gw, s)
	if err := gw.Close(); err != nil {
		panic(err)
	}
	if err := bw.Close(); err != nil {
		panic(err)
	}
	return buf.String()
}

// vim: set fileencoding=utf-8 noet:
<|endoftext|>


<commit_before>package protocol

import (
	"encoding/binary"
	"io"
	"net"
	"strconv"
	"testing"
)

// FakeKafkaServer is a mock helper for testing the Broker and other higher-level APIs.
// It takes a testing.T as provided by the test framework and a channel of responses to use.
// It spawns a TCP server on a kernel-selected localhost port, then spawns a goroutine that reads Kafka requests
// from that port and returns each provided response in order (if a response is nil, nothing is sent).
// When the server is successfully created, it returns the port on which it is listening and a 'done' channel
// which it will close when it exits. Otherwise it will return an error (if an error occurs *in* the goroutine it
// is simply logged to the testing.T and the goroutine exits). There is also a StopFakeServer helper that leads to
// this recommended pattern in tests:
//
//	port, done, err := FakeKafkaServer(t, responses)
//	if err != nil {
//      	t.Fatal(err)
//      }
//      defer StopFakeServer(responses, done)
//
// When running tests like this, it is strongly recommended to specify a -timeout to `go test` so that if the test hangs
// waiting for a response, it automatically panics.
//
// It is not necessary to prefix message length or correlation ID to your response bytes, the server does that
// automatically as a convenience.
func FakeKafkaServer(t *testing.T, responses <-chan []byte) (int32, <-chan bool, error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return 0, nil, err
	}
	_, portStr, err := net.SplitHostPort(ln.Addr().String())
	if err != nil {
		return 0, nil, err
	}
	tmp, err := strconv.ParseInt(portStr, 10, 32)
	if err != nil {
		return 0, nil, err
	}
	port := int32(tmp)
	done := make(chan bool)
	go func() {
		defer close(done)
		conn, err := ln.Accept()
		if err != nil {
			t.Error(err)
			conn.Close()
			ln.Close()
			return
		}
		reqHeader := make([]byte, 4)
		resHeader := make([]byte, 8)
		for response := range responses {
			_, err := io.ReadFull(conn, reqHeader)
			if err != nil {
				t.Error(err)
				conn.Close()
				ln.Close()
				return
			}
			body := make([]byte, binary.BigEndian.Uint32(reqHeader))
			if len(body) < 10 {
				t.Error("Kafka request too short.")
				conn.Close()
				ln.Close()
				return
			}
			_, err = io.ReadFull(conn, body)
			if err != nil {
				t.Error(err)
				conn.Close()
				ln.Close()
				return
			}
			if response == nil {
				continue
			}
			binary.BigEndian.PutUint32(resHeader, uint32(len(response)))
			binary.BigEndian.PutUint32(resHeader[4:], binary.BigEndian.Uint32(body[4:]))
			_, err = conn.Write(resHeader)
			if err != nil {
				t.Error(err)
				conn.Close()
				ln.Close()
				return
			}
			_, err = conn.Write(response)
			if err != nil {
				t.Error(err)
				conn.Close()
				ln.Close()
				return
			}
		}
		err = conn.Close()
		if err != nil {
			t.Error(err)
			ln.Close()
			return
		}
		err = ln.Close()
		if err != nil {
			t.Error(err)
			return
		}
	}()
	return port, done, nil
}

func StopFakeServer(responses chan []byte, done <-chan bool) {
	close(responses)
	<-done
}

func TestBrokerEquals(t *testing.T) {
	var b1, b2 *Broker

	b1 = nil
	b2 = nil

	if !b1.Equals(b2) {
		t.Error("Two nil brokers didn't compare equal.")
	}

	b1 = NewBroker("abc", 123)

	if b1.Equals(b2) {
		t.Error("Non-nil and nil brokers compared equal.")
	}
	if b2.Equals(b1) {
		t.Error("Nil and non-nil brokers compared equal.")
	}

	b2 = NewBroker("abc", 1234)
	if b1.Equals(b2) || b2.Equals(b1) {
		t.Error("Brokers with different ports compared equal.")
	}

	b2 = NewBroker("abcd", 123)
	if b1.Equals(b2) || b2.Equals(b1) {
		t.Error("Brokers with different hosts compared equal.")
	}

	b2 = NewBroker("abc", 123)
	b2.id = -2
	if b1.Equals(b2) || b2.Equals(b1) {
		t.Error("Brokers with different ids compared equal.")
	}

	b2.id = -1
	if !b1.Equals(b2) || !b2.Equals(b1) {
		t.Error("Similar brokers did not compare equal.")
	}
}

func TestBrokerID(t *testing.T) {

	broker := NewBroker("abc", 123)

	if broker.ID() != -1 {
		t.Error("New broker didn't have an ID of -1.")
	}

	broker.id = 34
	if broker.ID() != 34 {
		t.Error("Manually setting broker ID did not take effect.")
	}
}

func TestBrokerConnectClose(t *testing.T) {
	responses := make(chan []byte)
	port, done, err := FakeKafkaServer(t, responses)
	if err != nil {
		t.Fatal(err)
	}
	defer StopFakeServer(responses, done)

	broker := NewBroker("localhost", port)
	err = broker.Connect()
	if err != nil {
		t.Fatal(err)
	}
	err = broker.Close()
	if err != nil {
		t.Error(err)
	}
}
<commit_msg>Table-driven broker tests<commit_after>package protocol

import (
	"encoding/binary"
	"io"
	"net"
	"sarama/types"
	"strconv"
	"testing"
)

// FakeKafkaServer is a mock helper for testing the Broker and other higher-level APIs.
// It takes a testing.T as provided by the test framework and a channel of responses to use.
// It spawns a TCP server on a kernel-selected localhost port, then spawns a goroutine that reads Kafka requests
// from that port and returns each provided response in order (if a response is nil, nothing is sent).
// When the server is successfully created, it returns the port on which it is listening and a 'done' channel
// which it will close when it exits. Otherwise it will return an error (if an error occurs *in* the goroutine it
// is simply logged to the testing.T and the goroutine exits). There is also a StopFakeServer helper that leads to
// this recommended pattern in tests:
//
//	port, done, err := FakeKafkaServer(t, responses)
//	if err != nil {
//      	t.Fatal(err)
//      }
//      defer StopFakeServer(responses, done)
//
// When running tests like this, it is strongly recommended to specify a -timeout to `go test` so that if the test hangs
// waiting for a response, it automatically panics.
//
// It is not necessary to prefix message length or correlation ID to your response bytes, the server does that
// automatically as a convenience.
func FakeKafkaServer(t *testing.T, responses <-chan []byte) (int32, <-chan bool, error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return 0, nil, err
	}
	_, portStr, err := net.SplitHostPort(ln.Addr().String())
	if err != nil {
		return 0, nil, err
	}
	tmp, err := strconv.ParseInt(portStr, 10, 32)
	if err != nil {
		return 0, nil, err
	}
	port := int32(tmp)
	done := make(chan bool)
	go func() {
		defer close(done)
		conn, err := ln.Accept()
		if err != nil {
			t.Error(err)
			conn.Close()
			ln.Close()
			return
		}
		reqHeader := make([]byte, 4)
		resHeader := make([]byte, 8)
		for response := range responses {
			_, err := io.ReadFull(conn, reqHeader)
			if err != nil {
				t.Error(err)
				conn.Close()
				ln.Close()
				return
			}
			body := make([]byte, binary.BigEndian.Uint32(reqHeader))
			if len(body) < 10 {
				t.Error("Kafka request too short.")
				conn.Close()
				ln.Close()
				return
			}
			_, err = io.ReadFull(conn, body)
			if err != nil {
				t.Error(err)
				conn.Close()
				ln.Close()
				return
			}
			if response == nil {
				continue
			}
			binary.BigEndian.PutUint32(resHeader, uint32(len(response)+4))
			binary.BigEndian.PutUint32(resHeader[4:], binary.BigEndian.Uint32(body[4:]))
			_, err = conn.Write(resHeader)
			if err != nil {
				t.Error(err)
				conn.Close()
				ln.Close()
				return
			}
			_, err = conn.Write(response)
			if err != nil {
				t.Error(err)
				conn.Close()
				ln.Close()
				return
			}
		}
		err = conn.Close()
		if err != nil {
			t.Error(err)
			ln.Close()
			return
		}
		err = ln.Close()
		if err != nil {
			t.Error(err)
			return
		}
	}()
	return port, done, nil
}

func StopFakeServer(responses chan []byte, done <-chan bool) {
	close(responses)
	<-done
}

func TestBrokerEquals(t *testing.T) {
	var b1, b2 *Broker

	b1 = nil
	b2 = nil

	if !b1.Equals(b2) {
		t.Error("Two nil brokers didn't compare equal.")
	}

	b1 = NewBroker("abc", 123)

	if b1.Equals(b2) {
		t.Error("Non-nil and nil brokers compared equal.")
	}
	if b2.Equals(b1) {
		t.Error("Nil and non-nil brokers compared equal.")
	}

	b2 = NewBroker("abc", 1234)
	if b1.Equals(b2) || b2.Equals(b1) {
		t.Error("Brokers with different ports compared equal.")
	}

	b2 = NewBroker("abcd", 123)
	if b1.Equals(b2) || b2.Equals(b1) {
		t.Error("Brokers with different hosts compared equal.")
	}

	b2 = NewBroker("abc", 123)
	b2.id = -2
	if b1.Equals(b2) || b2.Equals(b1) {
		t.Error("Brokers with different ids compared equal.")
	}

	b2.id = -1
	if !b1.Equals(b2) || !b2.Equals(b1) {
		t.Error("Similar brokers did not compare equal.")
	}
}

func TestBrokerID(t *testing.T) {

	broker := NewBroker("abc", 123)

	if broker.ID() != -1 {
		t.Error("New broker didn't have an ID of -1.")
	}

	broker.id = 34
	if broker.ID() != 34 {
		t.Error("Manually setting broker ID did not take effect.")
	}
}

func TestSimpleBrokerCommunication(t *testing.T) {
	responses := make(chan []byte)
	port, done, err := FakeKafkaServer(t, responses)
	if err != nil {
		t.Fatal(err)
	}
	defer StopFakeServer(responses, done)

	broker := NewBroker("localhost", port)
	err = broker.Connect()
	if err != nil {
		t.Fatal(err)
	}

	for _, tt := range brokerTestTable {
		go func() {
			responses <- tt.response
		}()
		tt.runner(t, broker)
	}

	err = broker.Close()
	if err != nil {
		t.Error(err)
	}
}

var brokerTestTable = []struct {
	response []byte
	runner   func(*testing.T, *Broker)
}{
	{[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
		func(t *testing.T, broker *Broker) {
			request := MetadataRequest{}
			_, err := broker.GetMetadata("clientID", &request)
			if err != nil {
				t.Error(err)
			}
		}},

	{nil,
		func(t *testing.T, broker *Broker) {
			request := ProduceRequest{}
			request.RequiredAcks = types.NO_RESPONSE
			response, err := broker.Produce("clientID", &request)
			if err != nil {
				t.Error(err)
			}
			if response != nil {
				t.Error("Produce request with NO_RESPONSE got a response!")
			}
		}},
}
<|endoftext|>


<commit_before>// Copyright 2019 The Terraformer Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ibm

import (
	"context"
	"fmt"
	"os"

	"github.com/GoogleCloudPlatform/terraformer/terraformutils"
	"github.com/IBM-Cloud/bluemix-go"
	"github.com/IBM-Cloud/bluemix-go/api/resource/resourcev1/catalog"
	"github.com/IBM-Cloud/bluemix-go/api/resource/resourcev2/controllerv2"
	"github.com/IBM-Cloud/bluemix-go/session"
	kp "github.com/IBM/keyprotect-go-client"
)

type KPGenerator struct {
	IBMService
}

func (g KPGenerator) loadKP() func(kpID, kpName string) terraformutils.Resource {
	names := make(map[string]struct{})
	random := true
	return func(kpID, kpName string) terraformutils.Resource {
		names, random = getRandom(names, kpName, random)
		resource := terraformutils.NewSimpleResource(
			kpID,
			normalizeResourceName(kpName, random),
			"ibm_resource_instance",
			"ibm",
			[]string{})
		return resource
	}
}

func (g KPGenerator) loadkPKeys() func(kpKeyCRN, kpKeyName string, dependsOn []string) terraformutils.Resource {
	names := make(map[string]struct{})
	random := true
	return func(kpKeyCRN, kpKeyName string, dependsOn []string) terraformutils.Resource {
		names, random = getRandom(names, kpKeyName, random)
		resource := terraformutils.NewResource(
			kpKeyCRN,
			normalizeResourceName(kpKeyName, random),
			"ibm_kms_key",
			"ibm",
			map[string]string{},
			[]string{},
			map[string]interface{}{
				"depends_on": dependsOn,
			})
		return resource
	}
}

func (g *KPGenerator) InitResources() error {
	region := g.Args["region"].(string)
	bmxConfig := &bluemix.Config{
		BluemixAPIKey: os.Getenv("IC_API_KEY"),
	}
	sess, err := session.New(bmxConfig)
	if err != nil {
		return err
	}

	catalogClient, err := catalog.New(sess)
	if err != nil {
		return err
	}

	controllerClient, err := controllerv2.New(sess)
	if err != nil {
		return err
	}

	serviceID, err := catalogClient.ResourceCatalog().FindByName("kms", true)
	if err != nil {
		return err
	}
	query := controllerv2.ServiceInstanceQuery{
		ServiceID: serviceID[0].ID,
	}
	kpInstances, err := controllerClient.ResourceServiceInstanceV2().ListInstances(query)
	if err != nil {
		return err
	}
	kpurl := fmt.Sprintf("https://%s.kms.cloud.ibm.com", region)
	options := kp.ClientConfig{
		BaseURL: envFallBack([]string{"IBMCLOUD_KP_API_ENDPOINT"}, kpurl),
		APIKey:  os.Getenv("IC_API_KEY"),
		Verbose: kp.VerboseFailOnly,
	}

	client, err := kp.New(options, kp.DefaultTransport())
	if err != nil {
		return err
	}
	fnObjt := g.loadKP()
	for _, kpInstance := range kpInstances {
		g.Resources = append(g.Resources, fnObjt(kpInstance.ID, kpInstance.Name))
		resourceName := g.Resources[len(g.Resources)-1:][0].ResourceName
		client.Config.InstanceID = kpInstance.Guid

		output, err := client.GetKeys(context.Background(), 100, 0)
		if err != nil {
			return err
		}
		fnObjt := g.loadkPKeys()
		for _, key := range output.Keys {
			var dependsOn []string
			dependsOn = append(dependsOn,
				"ibm_resource_instance."+resourceName)
			g.Resources = append(g.Resources, fnObjt(key.CRN, key.Name, dependsOn))
		}

	}

	return nil
}
<commit_msg>Add kms alias and kms policy (#1537)<commit_after>// Copyright 2019 The Terraformer Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ibm

import (
	"context"
	"fmt"
	"os"

	"github.com/GoogleCloudPlatform/terraformer/terraformutils"
	"github.com/IBM-Cloud/bluemix-go"
	"github.com/IBM-Cloud/bluemix-go/api/resource/resourcev1/catalog"
	"github.com/IBM-Cloud/bluemix-go/api/resource/resourcev2/controllerv2"
	"github.com/IBM-Cloud/bluemix-go/session"
	kp "github.com/IBM/keyprotect-go-client"
)

type KPGenerator struct {
	IBMService
}

func (g KPGenerator) loadKP() func(kpID, kpName string) terraformutils.Resource {
	names := make(map[string]struct{})
	random := true
	return func(kpID, kpName string) terraformutils.Resource {
		names, random = getRandom(names, kpName, random)
		resource := terraformutils.NewSimpleResource(
			kpID,
			normalizeResourceName(kpName, random),
			"ibm_resource_instance",
			"ibm",
			[]string{})
		return resource
	}
}

func (g KPGenerator) loadkPKeys() func(kpKeyCRN, kpKeyName string, dependsOn []string) terraformutils.Resource {
	names := make(map[string]struct{})
	random := true
	return func(kpKeyCRN, kpKeyName string, dependsOn []string) terraformutils.Resource {
		names, random = getRandom(names, kpKeyName, random)
		resource := terraformutils.NewResource(
			kpKeyCRN,
			normalizeResourceName(kpKeyName, random),
			"ibm_kms_key",
			"ibm",
			map[string]string{},
			[]string{},
			map[string]interface{}{
				"depends_on": dependsOn,
			})
		return resource
	}
}

func (g KPGenerator) loadkPKeyAliases() func(kpKeyCRN, kpKeyAlias string, dependsOn []string) terraformutils.Resource {
	return func(kpKeyCRN, kpKeyAlias string, dependsOn []string) terraformutils.Resource {
		resource := terraformutils.NewResource(
			fmt.Sprintf("%s:alias:%s", kpKeyAlias, kpKeyCRN),
			normalizeResourceName(kpKeyAlias, true),
			"ibm_kms_key_alias",
			"ibm",
			map[string]string{},
			[]string{},
			map[string]interface{}{
				"depends_on": dependsOn,
			})
		return resource
	}
}

func (g KPGenerator) loadKpKeyPolicies() func(kpKeyCRN string, dependsOn []string) terraformutils.Resource {
	return func(kpKeyCRN string, dependsOn []string) terraformutils.Resource {
		resource := terraformutils.NewResource(
			kpKeyCRN,
			normalizeResourceName("kp_policies", true),
			"ibm_kms_key_policies",
			"ibm",
			map[string]string{},
			[]string{},
			map[string]interface{}{
				"depends_on": dependsOn,
			})
		return resource
	}
}

func (g *KPGenerator) InitResources() error {
	region := g.Args["region"].(string)
	bmxConfig := &bluemix.Config{
		BluemixAPIKey: os.Getenv("IC_API_KEY"),
	}
	sess, err := session.New(bmxConfig)
	if err != nil {
		return err
	}

	catalogClient, err := catalog.New(sess)
	if err != nil {
		return err
	}

	controllerClient, err := controllerv2.New(sess)
	if err != nil {
		return err
	}

	serviceID, err := catalogClient.ResourceCatalog().FindByName("kms", true)
	if err != nil {
		return err
	}
	query := controllerv2.ServiceInstanceQuery{
		ServiceID: serviceID[0].ID,
	}
	kpInstances, err := controllerClient.ResourceServiceInstanceV2().ListInstances(query)
	if err != nil {
		return err
	}
	kpurl := fmt.Sprintf("https://%s.kms.cloud.ibm.com", region)
	options := kp.ClientConfig{
		BaseURL: envFallBack([]string{"IBMCLOUD_KP_API_ENDPOINT"}, kpurl),
		APIKey:  os.Getenv("IC_API_KEY"),
		Verbose: kp.VerboseFailOnly,
	}

	client, err := kp.New(options, kp.DefaultTransport())
	if err != nil {
		return err
	}
	fnObjt := g.loadKP()
	for _, kpInstance := range kpInstances {
		g.Resources = append(g.Resources, fnObjt(kpInstance.ID, kpInstance.Name))
		resourceName := g.Resources[len(g.Resources)-1:][0].ResourceName
		client.Config.InstanceID = kpInstance.Guid

		output, err := client.GetKeys(context.Background(), 100, 0)
		if err != nil {
			return err
		}

		fnObjt := g.loadkPKeys()
		for _, key := range output.Keys {
			var dependsOn []string
			dependsOn = append(dependsOn,
				"ibm_resource_instance."+resourceName)
			g.Resources = append(g.Resources, fnObjt(key.CRN, key.Name, dependsOn))
			resourceName := g.Resources[len(g.Resources)-1:][0].ResourceName

			fnObjt := g.loadkPKeyAliases()
			dependsOn = append(dependsOn,
				"ibm_kms_key."+resourceName)
			for _, alias := range key.Aliases {
				g.Resources = append(g.Resources, fnObjt(key.CRN, alias, dependsOn))
			}

			policies, _ := client.GetPolicies(context.Background(), key.ID)
			funObjt := g.loadKpKeyPolicies()
			for range policies {
				g.Resources = append(g.Resources, funObjt(key.CRN, dependsOn))
			}
		}
	}
	return nil
}

func (g *KPGenerator) PostConvertHook() error {
	for i, rk := range g.Resources {
		if rk.InstanceInfo.Type != "ibm_kms_key" {
			continue
		}

		for _, ri := range g.Resources {
			if ri.InstanceInfo.Type != "ibm_resource_instance" {
				continue
			}

			if rk.InstanceState.Attributes["instance_id"] == ri.InstanceState.Attributes["guid"] {
				g.Resources[i].Item["instance_id"] = "${ibm_resource_instance." + ri.ResourceName + ".guid}"
			}
		}
	}

	for i, ra := range g.Resources {
		if ra.InstanceInfo.Type != "ibm_kms_key_alias" {
			continue
		}
		for _, rk := range g.Resources {
			if rk.InstanceInfo.Type != "ibm_kms_key" {
				continue
			}
			if ra.InstanceState.Attributes["instance_id"] == rk.InstanceState.Attributes["instance_id"] && ra.InstanceState.Attributes["key_id"] == rk.InstanceState.Attributes["key_id"] {
				g.Resources[i].Item["instance_id"] = "${ibm_kms_key." + rk.ResourceName + ".instance_id}"
				g.Resources[i].Item["key_id"] = "${ibm_kms_key." + rk.ResourceName + ".key_id}"
			}
		}
	}

	for i, rp := range g.Resources {
		if rp.InstanceInfo.Type != "ibm_kms_key_policies" {
			continue
		}
		for _, rk := range g.Resources {
			if rk.InstanceInfo.Type != "ibm_kms_key" {
				continue
			}
			if rp.InstanceState.Attributes["instance_id"] == rk.InstanceState.Attributes["instance_id"] && rp.InstanceState.Attributes["key_id"] == rk.InstanceState.Attributes["key_id"] {
				g.Resources[i].Item["instance_id"] = "${ibm_kms_key." + rk.ResourceName + ".instance_id}"
				g.Resources[i].Item["key_id"] = "${ibm_kms_key." + rk.ResourceName + ".key_id}"
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// flickr.test API methods
package test

import (
	"encoding/xml"

	"github.com/masci/flickr.go/flickr"
	flickErr "github.com/masci/flickr.go/flickr/error"
)

// Response type used by Login function
type LoginResponse struct {
	flickr.FlickrResponse
	// the user who provided authentication infos
	User struct {
		XMLName xml.Name `xml:"user"`
		// Flickr ID
		ID string `xml:"id,attr"`
		// Flickr Username
		Username string `xml:"username"`
	}
}

// Response type used by Echo function
type EchoResponse struct {
	flickr.FlickrResponse
	// API method name, dotted notation
	Method string `xml:"method"`
	// API Key provided
	ApiKey string `xml:"api_key"`
	// API data exchange format (ex. rest)
	Format string `xml:"format"`
}

// A testing method which checks if the caller is logged in then returns their username.
// This method requires authentication with 'read' permission.
func Login(client *flickr.FlickrClient) (*LoginResponse, error) {
	client.EndpointUrl = flickr.API_ENDPOINT // TODO move to SetDefaultArgs?
	client.SetDefaultArgs()
	client.Args.Set("method", "flickr.test.login")
	client.Args.Set("oauth_token", client.OAuthToken)
	client.Args.Set("oauth_consumer_key", client.ApiKey)
	client.Sign(client.OAuthTokenSecret)

	loginResponse := &LoginResponse{}
	err := flickr.GetResponse(client, loginResponse)

	if err != nil {
		return nil, err
	}

	if loginResponse.HasErrors() {
		return loginResponse, flickErr.NewError(10)
	}

	return loginResponse, nil
}

// Noop method
// This method requires authentication with 'read' permission.
func Null(client *flickr.FlickrClient) (*flickr.FlickrResponse, error) {
	client.EndpointUrl = flickr.API_ENDPOINT
	client.SetDefaultArgs()
	client.Args.Set("method", "flickr.test.null")
	client.Args.Set("oauth_token", client.OAuthToken)
	client.Args.Set("oauth_consumer_key", client.ApiKey)
	client.Sign(client.OAuthTokenSecret)

	response := &flickr.FlickrResponse{}
	err := flickr.GetResponse(client, response)

	if err != nil {
		return nil, err
	}

	if response.HasErrors() {
		return response, flickErr.NewError(10)
	}

	return response, nil
}

// A testing method which echo's all parameters back in the response.
// This method does not require authentication.
func Echo(client *flickr.FlickrClient) (*EchoResponse, error) {
	client.EndpointUrl = flickr.API_ENDPOINT
	client.Args.Set("method", "flickr.test.echo")
	client.Args.Set("oauth_consumer_key", client.ApiKey)

	response := &EchoResponse{}
	err := flickr.GetResponse(client, response)

	if err != nil {
		return nil, err
	}

	if response.HasErrors() {
		return response, flickErr.NewError(10)
	}

	return response, nil
}
<commit_msg>removed TODO<commit_after>// flickr.test API methods
package test

import (
	"encoding/xml"

	"github.com/masci/flickr.go/flickr"
	flickErr "github.com/masci/flickr.go/flickr/error"
)

// Response type used by Login function
type LoginResponse struct {
	flickr.FlickrResponse
	// the user who provided authentication infos
	User struct {
		XMLName xml.Name `xml:"user"`
		// Flickr ID
		ID string `xml:"id,attr"`
		// Flickr Username
		Username string `xml:"username"`
	}
}

// Response type used by Echo function
type EchoResponse struct {
	flickr.FlickrResponse
	// API method name, dotted notation
	Method string `xml:"method"`
	// API Key provided
	ApiKey string `xml:"api_key"`
	// API data exchange format (ex. rest)
	Format string `xml:"format"`
}

// A testing method which checks if the caller is logged in then returns their username.
// This method requires authentication with 'read' permission.
func Login(client *flickr.FlickrClient) (*LoginResponse, error) {
	client.EndpointUrl = flickr.API_ENDPOINT
	client.SetDefaultArgs()
	client.Args.Set("method", "flickr.test.login")
	client.Args.Set("oauth_token", client.OAuthToken)
	client.Args.Set("oauth_consumer_key", client.ApiKey)
	client.Sign(client.OAuthTokenSecret)

	loginResponse := &LoginResponse{}
	err := flickr.GetResponse(client, loginResponse)

	if err != nil {
		return nil, err
	}

	if loginResponse.HasErrors() {
		return loginResponse, flickErr.NewError(10)
	}

	return loginResponse, nil
}

// Noop method
// This method requires authentication with 'read' permission.
func Null(client *flickr.FlickrClient) (*flickr.FlickrResponse, error) {
	client.EndpointUrl = flickr.API_ENDPOINT
	client.SetDefaultArgs()
	client.Args.Set("method", "flickr.test.null")
	client.Args.Set("oauth_token", client.OAuthToken)
	client.Args.Set("oauth_consumer_key", client.ApiKey)
	client.Sign(client.OAuthTokenSecret)

	response := &flickr.FlickrResponse{}
	err := flickr.GetResponse(client, response)

	if err != nil {
		return nil, err
	}

	if response.HasErrors() {
		return response, flickErr.NewError(10)
	}

	return response, nil
}

// A testing method which echo's all parameters back in the response.
// This method does not require authentication.
func Echo(client *flickr.FlickrClient) (*EchoResponse, error) {
	client.EndpointUrl = flickr.API_ENDPOINT
	client.Args.Set("method", "flickr.test.echo")
	client.Args.Set("oauth_consumer_key", client.ApiKey)

	response := &EchoResponse{}
	err := flickr.GetResponse(client, response)

	if err != nil {
		return nil, err
	}

	if response.HasErrors() {
		return response, flickErr.NewError(10)
	}

	return response, nil
}
<|endoftext|>


<commit_before>// Copyright 2012-2019 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"time"
)

// Command is a signal used to control a running nats-server process.
type Command string

// Valid Command values.
const (
	CommandStop   = Command("stop")
	CommandQuit   = Command("quit")
	CommandReopen = Command("reopen")
	CommandReload = Command("reload")

	// private for now
	commandLDMode = Command("ldm")
)

var (
	// gitCommit injected at build
	gitCommit string
	// trustedKeys is a whitespace separated array of trusted operator's public nkeys.
	trustedKeys string
)

const (
	// VERSION is the current version for the server.
	VERSION = "2.0.4"

	// PROTO is the currently supported protocol.
	// 0 was the original
	// 1 maintains proto 0, adds echo abilities for CONNECT from the client. Clients
	// should not send echo unless proto in INFO is >= 1.
	PROTO = 1

	// DEFAULT_PORT is the default port for client connections.
	DEFAULT_PORT = 4222

	// RANDOM_PORT is the value for port that, when supplied, will cause the
	// server to listen on a randomly-chosen available port. The resolved port
	// is available via the Addr() method.
	RANDOM_PORT = -1

	// DEFAULT_HOST defaults to all interfaces.
	DEFAULT_HOST = "0.0.0.0"

	// MAX_CONTROL_LINE_SIZE is the maximum allowed protocol control line size.
	// 4k should be plenty since payloads sans connect/info string are separate.
	MAX_CONTROL_LINE_SIZE = 4096

	// MAX_PAYLOAD_SIZE is the maximum allowed payload size. Should be using
	// something different if > 1MB payloads are needed.
	MAX_PAYLOAD_SIZE = (1024 * 1024)

	// MAX_PENDING_SIZE is the maximum outbound pending bytes per client.
	MAX_PENDING_SIZE = (64 * 1024 * 1024)

	// DEFAULT_MAX_CONNECTIONS is the default maximum connections allowed.
	DEFAULT_MAX_CONNECTIONS = (64 * 1024)

	// TLS_TIMEOUT is the TLS wait time.
	TLS_TIMEOUT = 500 * time.Millisecond

	// AUTH_TIMEOUT is the authorization wait time.
	AUTH_TIMEOUT = 2 * TLS_TIMEOUT

	// DEFAULT_PING_INTERVAL is how often pings are sent to clients and routes.
	DEFAULT_PING_INTERVAL = 2 * time.Minute

	// DEFAULT_PING_MAX_OUT is maximum allowed pings outstanding before disconnect.
	DEFAULT_PING_MAX_OUT = 2

	// CR_LF string
	CR_LF = "\r\n"

	// LEN_CR_LF hold onto the computed size.
	LEN_CR_LF = len(CR_LF)

	// DEFAULT_FLUSH_DEADLINE is the write/flush deadlines.
	DEFAULT_FLUSH_DEADLINE = 2 * time.Second

	// DEFAULT_HTTP_PORT is the default monitoring port.
	DEFAULT_HTTP_PORT = 8222

	// ACCEPT_MIN_SLEEP is the minimum acceptable sleep times on temporary errors.
	ACCEPT_MIN_SLEEP = 10 * time.Millisecond

	// ACCEPT_MAX_SLEEP is the maximum acceptable sleep times on temporary errors
	ACCEPT_MAX_SLEEP = 1 * time.Second

	// DEFAULT_ROUTE_CONNECT Route solicitation intervals.
	DEFAULT_ROUTE_CONNECT = 1 * time.Second

	// DEFAULT_ROUTE_RECONNECT Route reconnect intervals.
	DEFAULT_ROUTE_RECONNECT = 1 * time.Second

	// DEFAULT_ROUTE_DIAL Route dial timeout.
	DEFAULT_ROUTE_DIAL = 1 * time.Second

	// DEFAULT_LEAF_NODE_RECONNECT LeafNode reconnect interval.
	DEFAULT_LEAF_NODE_RECONNECT = time.Second

	// DEFAULT_LEAF_TLS_TIMEOUT TLS timeout for LeafNodes
	DEFAULT_LEAF_TLS_TIMEOUT = 2 * time.Second

	// PROTO_SNIPPET_SIZE is the default size of proto to print on parse errors.
	PROTO_SNIPPET_SIZE = 32

	// MAX_MSG_ARGS Maximum possible number of arguments from MSG proto.
	MAX_MSG_ARGS = 4

	// MAX_PUB_ARGS Maximum possible number of arguments from PUB proto.
	MAX_PUB_ARGS = 3

	// DEFAULT_MAX_CLOSED_CLIENTS is the maximum number of closed connections we hold onto.
	DEFAULT_MAX_CLOSED_CLIENTS = 10000

	// DEFAULT_MAX_ACCOUNT_AE_RESPONSE_MAPS is for auto-expire response maps for imports.
	DEFAULT_MAX_ACCOUNT_AE_RESPONSE_MAPS = 100000

	// DEFAULT_MAX_ACCOUNT_INTERNAL_RESPONSE_MAPS is for non auto-expire response maps for imports.
	// These are present for non-singleton response types.
	DEFAULT_MAX_ACCOUNT_INTERNAL_RESPONSE_MAPS = 100000

	// DEFAULT_TTL_AE_RESPONSE_MAP is the default time to expire auto-response map entries.
	DEFAULT_TTL_AE_RESPONSE_MAP = 10 * time.Minute

	// DEFAULT_LAME_DUCK_DURATION is the time in which the server spreads
	// the closing of clients when signaled to go in lame duck mode.
	DEFAULT_LAME_DUCK_DURATION = 2 * time.Minute

	// DEFAULT_LEAFNODE_INFO_WAIT Route dial timeout.
	DEFAULT_LEAFNODE_INFO_WAIT = 1 * time.Second

	// DEFAULT_LEAFNODE_PORT is the default port for remote leafnode connections.
	DEFAULT_LEAFNODE_PORT = 7422

	// DEFAULT_CONNECT_ERROR_REPORTS is the number of attempts at which a
	// repeated failed route, gateway or leaf node connection is reported.
	// This is used for initial connection, that is, when the server has
	// never had a connection to the given endpoint. Once connected, and
	// if a disconnect occurs, DEFAULT_RECONNECT_ERROR_REPORTS is used
	// instead.
	// The default is to report every 3600 attempts (roughly every hour).
	DEFAULT_CONNECT_ERROR_REPORTS = 3600

	// DEFAULT_RECONNECT_ERROR_REPORTS is the default number of failed
	// attempt to reconnect a route, gateway or leaf node connection.
	// The default is to report every attempt.
	DEFAULT_RECONNECT_ERROR_REPORTS = 1

	// DEFAULT_RTT_MEASUREMENT_INTERVAL is how often we want to measure RTT from
	// this server to clients, routes, gateways or leafnode connections.
	DEFAULT_RTT_MEASUREMENT_INTERVAL = time.Hour

	// DEFAULT_ALLOW_RESPONSE_MAX_MSGS is the default number of responses allowed
	// for a reply subject.
	DEFAULT_ALLOW_RESPONSE_MAX_MSGS = 1

	// DEFAULT_ALLOW_RESPONSE_EXPIRATION is the default time allowed for a given
	// dynamic response permission.
	DEFAULT_ALLOW_RESPONSE_EXPIRATION = 2 * time.Minute

	// DEFAULT_SERVICE_LATENCY_SAMPLING is the default sampling rate for service
	// latency metrics
	DEFAULT_SERVICE_LATENCY_SAMPLING = 100
)
<commit_msg>Bump version to 2.1.0-RC1<commit_after>// Copyright 2012-2019 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"time"
)

// Command is a signal used to control a running nats-server process.
type Command string

// Valid Command values.
const (
	CommandStop   = Command("stop")
	CommandQuit   = Command("quit")
	CommandReopen = Command("reopen")
	CommandReload = Command("reload")

	// private for now
	commandLDMode = Command("ldm")
)

var (
	// gitCommit injected at build
	gitCommit string
	// trustedKeys is a whitespace separated array of trusted operator's public nkeys.
	trustedKeys string
)

const (
	// VERSION is the current version for the server.
	VERSION = "2.1.0-RC1"

	// PROTO is the currently supported protocol.
	// 0 was the original
	// 1 maintains proto 0, adds echo abilities for CONNECT from the client. Clients
	// should not send echo unless proto in INFO is >= 1.
	PROTO = 1

	// DEFAULT_PORT is the default port for client connections.
	DEFAULT_PORT = 4222

	// RANDOM_PORT is the value for port that, when supplied, will cause the
	// server to listen on a randomly-chosen available port. The resolved port
	// is available via the Addr() method.
	RANDOM_PORT = -1

	// DEFAULT_HOST defaults to all interfaces.
	DEFAULT_HOST = "0.0.0.0"

	// MAX_CONTROL_LINE_SIZE is the maximum allowed protocol control line size.
	// 4k should be plenty since payloads sans connect/info string are separate.
	MAX_CONTROL_LINE_SIZE = 4096

	// MAX_PAYLOAD_SIZE is the maximum allowed payload size. Should be using
	// something different if > 1MB payloads are needed.
	MAX_PAYLOAD_SIZE = (1024 * 1024)

	// MAX_PENDING_SIZE is the maximum outbound pending bytes per client.
	MAX_PENDING_SIZE = (64 * 1024 * 1024)

	// DEFAULT_MAX_CONNECTIONS is the default maximum connections allowed.
	DEFAULT_MAX_CONNECTIONS = (64 * 1024)

	// TLS_TIMEOUT is the TLS wait time.
	TLS_TIMEOUT = 500 * time.Millisecond

	// AUTH_TIMEOUT is the authorization wait time.
	AUTH_TIMEOUT = 2 * TLS_TIMEOUT

	// DEFAULT_PING_INTERVAL is how often pings are sent to clients and routes.
	DEFAULT_PING_INTERVAL = 2 * time.Minute

	// DEFAULT_PING_MAX_OUT is maximum allowed pings outstanding before disconnect.
	DEFAULT_PING_MAX_OUT = 2

	// CR_LF string
	CR_LF = "\r\n"

	// LEN_CR_LF hold onto the computed size.
	LEN_CR_LF = len(CR_LF)

	// DEFAULT_FLUSH_DEADLINE is the write/flush deadlines.
	DEFAULT_FLUSH_DEADLINE = 2 * time.Second

	// DEFAULT_HTTP_PORT is the default monitoring port.
	DEFAULT_HTTP_PORT = 8222

	// ACCEPT_MIN_SLEEP is the minimum acceptable sleep times on temporary errors.
	ACCEPT_MIN_SLEEP = 10 * time.Millisecond

	// ACCEPT_MAX_SLEEP is the maximum acceptable sleep times on temporary errors
	ACCEPT_MAX_SLEEP = 1 * time.Second

	// DEFAULT_ROUTE_CONNECT Route solicitation intervals.
	DEFAULT_ROUTE_CONNECT = 1 * time.Second

	// DEFAULT_ROUTE_RECONNECT Route reconnect intervals.
	DEFAULT_ROUTE_RECONNECT = 1 * time.Second

	// DEFAULT_ROUTE_DIAL Route dial timeout.
	DEFAULT_ROUTE_DIAL = 1 * time.Second

	// DEFAULT_LEAF_NODE_RECONNECT LeafNode reconnect interval.
	DEFAULT_LEAF_NODE_RECONNECT = time.Second

	// DEFAULT_LEAF_TLS_TIMEOUT TLS timeout for LeafNodes
	DEFAULT_LEAF_TLS_TIMEOUT = 2 * time.Second

	// PROTO_SNIPPET_SIZE is the default size of proto to print on parse errors.
	PROTO_SNIPPET_SIZE = 32

	// MAX_MSG_ARGS Maximum possible number of arguments from MSG proto.
	MAX_MSG_ARGS = 4

	// MAX_PUB_ARGS Maximum possible number of arguments from PUB proto.
	MAX_PUB_ARGS = 3

	// DEFAULT_MAX_CLOSED_CLIENTS is the maximum number of closed connections we hold onto.
	DEFAULT_MAX_CLOSED_CLIENTS = 10000

	// DEFAULT_MAX_ACCOUNT_AE_RESPONSE_MAPS is for auto-expire response maps for imports.
	DEFAULT_MAX_ACCOUNT_AE_RESPONSE_MAPS = 100000

	// DEFAULT_MAX_ACCOUNT_INTERNAL_RESPONSE_MAPS is for non auto-expire response maps for imports.
	// These are present for non-singleton response types.
	DEFAULT_MAX_ACCOUNT_INTERNAL_RESPONSE_MAPS = 100000

	// DEFAULT_TTL_AE_RESPONSE_MAP is the default time to expire auto-response map entries.
	DEFAULT_TTL_AE_RESPONSE_MAP = 10 * time.Minute

	// DEFAULT_LAME_DUCK_DURATION is the time in which the server spreads
	// the closing of clients when signaled to go in lame duck mode.
	DEFAULT_LAME_DUCK_DURATION = 2 * time.Minute

	// DEFAULT_LEAFNODE_INFO_WAIT Route dial timeout.
	DEFAULT_LEAFNODE_INFO_WAIT = 1 * time.Second

	// DEFAULT_LEAFNODE_PORT is the default port for remote leafnode connections.
	DEFAULT_LEAFNODE_PORT = 7422

	// DEFAULT_CONNECT_ERROR_REPORTS is the number of attempts at which a
	// repeated failed route, gateway or leaf node connection is reported.
	// This is used for initial connection, that is, when the server has
	// never had a connection to the given endpoint. Once connected, and
	// if a disconnect occurs, DEFAULT_RECONNECT_ERROR_REPORTS is used
	// instead.
	// The default is to report every 3600 attempts (roughly every hour).
	DEFAULT_CONNECT_ERROR_REPORTS = 3600

	// DEFAULT_RECONNECT_ERROR_REPORTS is the default number of failed
	// attempt to reconnect a route, gateway or leaf node connection.
	// The default is to report every attempt.
	DEFAULT_RECONNECT_ERROR_REPORTS = 1

	// DEFAULT_RTT_MEASUREMENT_INTERVAL is how often we want to measure RTT from
	// this server to clients, routes, gateways or leafnode connections.
	DEFAULT_RTT_MEASUREMENT_INTERVAL = time.Hour

	// DEFAULT_ALLOW_RESPONSE_MAX_MSGS is the default number of responses allowed
	// for a reply subject.
	DEFAULT_ALLOW_RESPONSE_MAX_MSGS = 1

	// DEFAULT_ALLOW_RESPONSE_EXPIRATION is the default time allowed for a given
	// dynamic response permission.
	DEFAULT_ALLOW_RESPONSE_EXPIRATION = 2 * time.Minute

	// DEFAULT_SERVICE_LATENCY_SAMPLING is the default sampling rate for service
	// latency metrics
	DEFAULT_SERVICE_LATENCY_SAMPLING = 100
)
<|endoftext|>


<commit_before>package torrent

import (
	"strings"

	"github.com/anacrolix/torrent/metainfo"
)

// Provides access to regions of torrent data that correspond to its files.
type File struct {
	t      Torrent
	path   string
	offset int64
	length int64
	fi     metainfo.FileInfo
}

// Data for this file begins this far into the torrent.
func (f *File) Offset() int64 {
	return f.offset
}

func (f File) FileInfo() metainfo.FileInfo {
	return f.fi
}

func (f File) Path() string {
	return f.path
}

func (f *File) Length() int64 {
	return f.length
}

// The relative file path for a multi-file torrent, and the torrent name for a
// single-file torrent.
func (f *File) DisplayPath() string {
	fip := f.FileInfo().Path
	if len(fip) == 0 {
		return f.t.Info().Name
	}
	return strings.Join(fip, "/")

}

type FilePieceState struct {
	Bytes int64 // Bytes within the piece that are part of this File.
	PieceState
}

// Returns the state of pieces in this file.
func (f *File) State() (ret []FilePieceState) {
	pieceSize := int64(f.t.usualPieceSize())
	off := f.offset % pieceSize
	remaining := f.length
	for i := int(f.offset / pieceSize); ; i++ {
		if remaining == 0 {
			break
		}
		len1 := pieceSize - off
		if len1 > remaining {
			len1 = remaining
		}
		f.t.cl.mu.RLock()
		ps := f.t.pieceState(i)
		f.t.cl.mu.RUnlock()
		ret = append(ret, FilePieceState{len1, ps})
		off = 0
		remaining -= len1
	}
	return
}

// Marks pieces in the region of the file for download. This is a helper
// wrapping Torrent.SetRegionPriority.
func (f *File) PrioritizeRegion(off, len int64) {
	if off < 0 || off >= f.length {
		return
	}
	if off+len > f.length {
		len = f.length - off
	}
	off += f.offset
	f.t.SetRegionPriority(off, len)
}
<commit_msg>Add method File.Torrent<commit_after>package torrent

import (
	"strings"

	"github.com/anacrolix/torrent/metainfo"
)

// Provides access to regions of torrent data that correspond to its files.
type File struct {
	t      Torrent
	path   string
	offset int64
	length int64
	fi     metainfo.FileInfo
}

func (f *File) Torrent() Torrent {
	return f.t
}

// Data for this file begins this far into the torrent.
func (f *File) Offset() int64 {
	return f.offset
}

func (f File) FileInfo() metainfo.FileInfo {
	return f.fi
}

func (f File) Path() string {
	return f.path
}

func (f *File) Length() int64 {
	return f.length
}

// The relative file path for a multi-file torrent, and the torrent name for a
// single-file torrent.
func (f *File) DisplayPath() string {
	fip := f.FileInfo().Path
	if len(fip) == 0 {
		return f.t.Info().Name
	}
	return strings.Join(fip, "/")

}

type FilePieceState struct {
	Bytes int64 // Bytes within the piece that are part of this File.
	PieceState
}

// Returns the state of pieces in this file.
func (f *File) State() (ret []FilePieceState) {
	pieceSize := int64(f.t.usualPieceSize())
	off := f.offset % pieceSize
	remaining := f.length
	for i := int(f.offset / pieceSize); ; i++ {
		if remaining == 0 {
			break
		}
		len1 := pieceSize - off
		if len1 > remaining {
			len1 = remaining
		}
		f.t.cl.mu.RLock()
		ps := f.t.pieceState(i)
		f.t.cl.mu.RUnlock()
		ret = append(ret, FilePieceState{len1, ps})
		off = 0
		remaining -= len1
	}
	return
}

// Marks pieces in the region of the file for download. This is a helper
// wrapping Torrent.SetRegionPriority.
func (f *File) PrioritizeRegion(off, len int64) {
	if off < 0 || off >= f.length {
		return
	}
	if off+len > f.length {
		len = f.length - off
	}
	off += f.offset
	f.t.SetRegionPriority(off, len)
}
<|endoftext|>


<commit_before>package main

import (
   "github.com/dgnorton/dmapi"
   "encoding/json"
   "fmt"
   "io"
   "os"
   "path"
   "path/filepath"
   "text/tabwriter"
)

var cmdFind = &Command {
   UsageLine:  "find [-s start date] [-e end date] [-p regex] [-f output template file]",
   Short:      "search local data store for specified entries",
   Long: `
Searchs the local data for an entries matching the specified
parameters.  If only a start date is given, all entries from that
time forward will be shown.  If no start or end dates are given,
all entries will be considered. The pattern is a regex pattern
with the exception that giving only "*" as the pattern will
match anything.`,
}

func init() {
   cmdFind.Run = runFind
   addFindFlags(cmdFind)
}

// Add command line flags specific to the find command here
   var findStart string    // -s start date
   var findEnd string      // -e end date
   var findPattern string  // -p regex pattern or *
   var findFormat string   // -f

func addFindFlags(cmd *Command) {
   cmd.Flag.StringVar(&findStart, "s", "", "")
   cmd.Flag.StringVar(&findEnd, "e", "", "")
   cmd.Flag.StringVar(&findPattern, "p", "", "")
   cmd.Flag.StringVar(&findFormat, "f", "", "")
}

func runFind(cfg *config, cmd *Command, args []string) {
   user := cfg.User
   usrDir, err := userDir(user)
   if err != nil {
      fatalf("%s [find.go - runFind - userDir]", err)
   }

   userFile := path.Join(usrDir, "entries.json")

   haveUserFile, err := isFile(userFile)
   if err != nil {
      fatalf("%s [find.go - runFind - isFile]", err)
   }

   if haveUserFile == false {
      fatalf("No entries to search.  Need to run 'dm sync'?")
   }

   entries, err := dmapi.LoadEntries(userFile)
   if err != nil {
      fatalf("%s [find.go - runFind - dmapi.LoadEntries]", err)
   }

   if len(entries.Entries) == 0 {
      fatalf("No entries to search.  Need to run 'dm sync'?")
   }

   matches, err := entries.Find(findStart, findEnd, findPattern)
   if err != nil {
      fatalf("%s [find.go - runFind - dmapi.Find]", err)
   }

   if findFormat != "" {
      var wr io.Writer = os.Stdout

      if filepath.Ext(findFormat) == "tsv" {
         wr = tabwriter.NewWriter(wr, 0, 8, 1, '\t', 0)
      }

      if filepath.Ext(findFormat) == "html" {
         err = fprintHTML(wr, matches, findFormat)
      } else {
         err = fprintText(wr, matches, findFormat)
      }

      if err != nil {
         fatalf("%s [find.go - runFind - fprintText]", err)
      }
   } else {
       bytes, err := json.Marshal(matches)
       if err != nil {
         fatalf("%s [find.go - runFind - json.Marshal]", err)
       }
       fmt.Fprintf(os.Stdout, "%s", string(bytes))
   }
}

<commit_msg>Added sketchy support for natural-language date range support in the find command.<commit_after>package main

import (
   "github.com/dgnorton/dmapi"
   "github.com/dgnorton/nltime"
   "encoding/json"
   "fmt"
   "io"
   "os"
   "path"
   "path/filepath"
   "strings"
   "text/tabwriter"
   "time"
)

var cmdFind = &Command {
   UsageLine:  "find [-s start date] [-e end date] [-p regex] [-f output template file]",
   Short:      "search local data store for specified entries",
   Long: `
Searchs the local data for an entries matching the specified
parameters.  If only a start date is given, all entries from that
time forward will be shown.  If no start or end dates are given,
all entries will be considered. The pattern is a regex pattern
with the exception that giving only "*" as the pattern will
match anything.`,
}

func init() {
   cmdFind.Run = runFind
   addFindFlags(cmdFind)
}

// Add command line flags specific to the find command here
   var findStart string    // -s start date
   var findEnd string      // -e end date
   var findPattern string  // -p regex pattern or *
   var findFormat string   // -f

func addFindFlags(cmd *Command) {
   cmd.Flag.StringVar(&findStart, "s", "", "")
   cmd.Flag.StringVar(&findEnd, "e", "", "")
   cmd.Flag.StringVar(&findPattern, "p", "", "")
   cmd.Flag.StringVar(&findFormat, "f", "", "")
}

func runFind(cfg *config, cmd *Command, args []string) {
   user := cfg.User
   usrDir, err := userDir(user)
   if err != nil {
      fatalf("%s [find.go - runFind - userDir]", err)
   }

   userFile := path.Join(usrDir, "entries.json")

   haveUserFile, err := isFile(userFile)
   if err != nil {
      fatalf("%s [find.go - runFind - isFile]", err)
   }

   if haveUserFile == false {
      fatalf("No entries to search.  Need to run 'dm sync'?")
   }

   entries, err := dmapi.LoadEntries(userFile)
   if err != nil {
      fatalf("%s [find.go - runFind - dmapi.LoadEntries]", err)
   }

   if len(entries.Entries) == 0 {
      fatalf("No entries to search.  Need to run 'dm sync'?")
   }

   if len(args) > 0 {
      args, params, err := parseNaturalLangArgs(args)
      if err != nil {
         fatalf("%s [find.go - runFind - parseNaturalLangArgs]", err)
      }

      cmd.Flag.Parse(args)
      findStart = params.StartDate
      findEnd = params.EndDate
   }

   matches, err := entries.Find(findStart, findEnd, findPattern)
   if err != nil {
      fatalf("%s [find.go - runFind - dmapi.Find]", err)
   }

   if findFormat != "" {
      var wr io.Writer = os.Stdout

      if filepath.Ext(findFormat) == "tsv" {
         wr = tabwriter.NewWriter(wr, 0, 8, 1, '\t', 0)
      }

      if filepath.Ext(findFormat) == "html" {
         err = fprintHTML(wr, matches, findFormat)
      } else {
         err = fprintText(wr, matches, findFormat)
      }

      if err != nil {
         fatalf("%s [find.go - runFind - fprintText]", err)
      }
   } else {
       bytes, err := json.Marshal(matches)
       if err != nil {
         fatalf("%s [find.go - runFind - json.Marshal]", err)
       }
       fmt.Fprintf(os.Stdout, "%s", string(bytes))
   }
}

type findParams struct {
  StartDate, EndDate, Pattern string
}

func parseNaturalLangArgs(args []string) ([]string, findParams, error) {
   var nlargs, otherArgs []string
   for i, arg := range args {
      if arg[0] == '-' {
         otherArgs = args[i:]
         break
      }
      nlargs = append(nlargs, arg)
   }
   d, err := nltime.ParseRange(strings.Join(nlargs, " "), time.Monday)
   if err != nil {
      return nil, findParams{}, err
   }
   return otherArgs, findParams{d[0].Format("06/1/2"), d[1].Format("06/1/2"), ""}, nil
}
<|endoftext|>


<commit_before>package challenges

import (
	"strconv"
	"strings"
)

func init() {
	Challenges[11] = Challenge{
		Title:    "Summation of primes",
		Correct:  true,
		Callback: C011,
		ID:       11,
	}
}

var c011GridString = strings.TrimSpace(`
08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08
49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00
81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65
52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91
22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80
24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50
32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70
67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21
24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72
21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95
78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92
16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57
86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58
19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40
04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66
88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69
04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36
20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16
20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54
01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48
`)

var c011Grid [][]int

func init() {
	lines := strings.Split(c011GridString, "\n")
	c011Grid = make([][]int, len(lines))
	for lineIdx, line := range lines {
		cols := strings.Split(line, " ")
		c011Grid[lineIdx] = make([]int, len(cols))
		for colIdx, col := range cols {
			c011Grid[lineIdx][colIdx], _ = strconv.Atoi(col)
		}
	}
}

func C011() (interface{}, error) {
	maxProduct := 0
	length := 4

	width := len(c011Grid[0])
	height := len(c011Grid)

	// Horizontal
	for y := 0; y < height; y++ {
		for x := 0; x < width-length; x++ {
			product := 1
			for i := 0; i < length; i++ {
				product *= c011Grid[y][x+i]
			}
			if product > maxProduct {
				maxProduct = product
			}
		}
	}

	// Vertical
	for y := 0; y < height-length; y++ {
		for x := 0; x < width; x++ {
			product := 1
			for i := 0; i < length; i++ {
				product *= c011Grid[y+i][x]
			}
			if product > maxProduct {
				maxProduct = product
			}
		}
	}

	// Diagonal NW->SE
	for y := 0; y < height-length; y++ {
		for x := 0; x < width-length; x++ {
			product := 1
			for i := 0; i < length; i++ {
				product *= c011Grid[y+i][x+i]
			}
			if product > maxProduct {
				maxProduct = product
			}
		}
	}

	// Diagonal NE->SW
	for y := 0; y < height-length; y++ {
		for x := length; x < width; x++ {
			product := 1
			for i := 0; i < length; i++ {
				product *= c011Grid[y+i][x-i]
			}
			if product > maxProduct {
				maxProduct = product
			}
		}
	}
	return maxProduct, nil
}
<commit_msg>Fix bad copy-paste<commit_after>package challenges

import (
	"strconv"
	"strings"
)

func init() {
	Challenges[11] = Challenge{
		Title:    "Largest product in a grid",
		Correct:  true,
		Callback: C011,
		ID:       11,
	}
}

var c011GridString = strings.TrimSpace(`
08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08
49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00
81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65
52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91
22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80
24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50
32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70
67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21
24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72
21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95
78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92
16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57
86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58
19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40
04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66
88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69
04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36
20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16
20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54
01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48
`)

var c011Grid [][]int

func init() {
	lines := strings.Split(c011GridString, "\n")
	c011Grid = make([][]int, len(lines))
	for lineIdx, line := range lines {
		cols := strings.Split(line, " ")
		c011Grid[lineIdx] = make([]int, len(cols))
		for colIdx, col := range cols {
			c011Grid[lineIdx][colIdx], _ = strconv.Atoi(col)
		}
	}
}

func C011() (interface{}, error) {
	maxProduct := 0
	length := 4

	width := len(c011Grid[0])
	height := len(c011Grid)

	// Horizontal
	for y := 0; y < height; y++ {
		for x := 0; x < width-length; x++ {
			product := 1
			for i := 0; i < length; i++ {
				product *= c011Grid[y][x+i]
			}
			if product > maxProduct {
				maxProduct = product
			}
		}
	}

	// Vertical
	for y := 0; y < height-length; y++ {
		for x := 0; x < width; x++ {
			product := 1
			for i := 0; i < length; i++ {
				product *= c011Grid[y+i][x]
			}
			if product > maxProduct {
				maxProduct = product
			}
		}
	}

	// Diagonal NW->SE
	for y := 0; y < height-length; y++ {
		for x := 0; x < width-length; x++ {
			product := 1
			for i := 0; i < length; i++ {
				product *= c011Grid[y+i][x+i]
			}
			if product > maxProduct {
				maxProduct = product
			}
		}
	}

	// Diagonal NE->SW
	for y := 0; y < height-length; y++ {
		for x := length; x < width; x++ {
			product := 1
			for i := 0; i < length; i++ {
				product *= c011Grid[y+i][x-i]
			}
			if product > maxProduct {
				maxProduct = product
			}
		}
	}
	return maxProduct, nil
}
<|endoftext|>