<commit_before>package printer

import (
	"bytes"
	"fmt"
	"go/parser"
	"go/printer"
	"go/token"
	"text/template"

	"github.com/davyxu/tabtoy/exportorv2/i18n"
	"github.com/davyxu/tabtoy/exportorv2/model"
)

const goTemplate = `// Generated by github.com/davyxu/tabtoy
// Version: {{.ToolVersion}}
// DO NOT EDIT!!
package {{.Package}}

import(
	{{if .HasAnyIndex}}"fmt"{{end}}
	"encoding/json"
	"io/ioutil"
)
{{range $a, $en := .Enums}}
// Defined in table: {{$en.DefinedTable}}
type {{$en.Name}} int32
const (
{{range .GoFields}}
	{{.Comment}}
	{{$en.Name}}_{{.Name}} {{$en.Name}} = {{.Number}}
{{end}}
)
{{end}}

{{range $a, $strus := .Structs}}
// Defined in table: {{$strus.DefinedTable}}
type {{$strus.Name}} struct{
	{{range $b, $fd := $strus.GoFields}}
	{{.Comment}}
	{{$fd.Name}} {{$fd.TypeString}} {{$fd.StructTag}}
	{{end}}
}
{{end}}

// {{$.Name}} 访问接口
type {{$.Name}}Table struct{

	// 表格原始数据
	{{$.Name}}

	// 索引函数表
	indexFuncByName map[string][]func(*{{$.Name}}Table)

	// 清空函数表
	clearFuncByName map[string][]func(*{{$.Name}}Table)

	{{range $a, $strus := .IndexedStructs}} {{range .Indexes}}
	{{$strus.Name}}By{{.Name}} map[{{.KeyType}}]*{{$strus.TypeName}}
	{{end}} {{end}}
}

{{range .VerticalFields}}
{{.Comment}}
func (self *{{$.Name}}Table) Get{{.Name}}( ) {{.ElementTypeString}} {
	return self.{{.Name}}[0]
}
{{end}}

// 从json文件加载
func (self *{{$.Name}}Table) Load(filename string) error {

	data, err := ioutil.ReadFile(filename)

	if err != nil {
		return err
	}

	// 清除
	for _, list := range self.clearFuncByName {
		for _, v := range list {
			v(self)
		}
	}

	err = json.Unmarshal(data, &self.{{$.Name}})
	if err != nil {
		return err
	}

	// 生成索引
	for _, list := range self.indexFuncByName {
		for _, v := range list {
			v(self)
		}
	}

	return nil
}

// 注册外部索引入口, 索引回调, 清空回调
func (self *{{$.Name}}Table) RegisterIndexEntry(name string, indexCallback func(*{{$.Name}}Table), clearCallback func(*{{$.Name}}Table)) {

	indexList, _ := self.indexFuncByName[name]
	clearList, _ := self.clearFuncByName[name]

	if indexCallback != nil {
		indexList = append(indexList, indexCallback)
	}

	if clearCallback != nil {
		clearList = append(clearList, clearCallback)
	}

	self.indexFuncByName[name] = indexList
	self.clearFuncByName[name] = clearList
}

// 创建一个{{$.Name}}表读取实例
func New{{$.Name}}Table() *{{$.Name}}Table {
	return &{{$.Name}}Table{

		indexFuncByName: map[string][]func(*{{$.Name}}Table){

		{{range $a, $strus := .IndexedStructs}}
			"{{$strus.Name}}": {func(tab *{{$.Name}}Table) {

				// {{$strus.Name}}
				for _, def := range tab.{{$strus.Name}} {
					{{range .Indexes}}
					if _, ok := tab.{{$strus.Name}}By{{.Name}}[def.{{.Name}}]; ok {
						panic(fmt.Sprintf("duplicate index in {{$strus.Name}}By{{.Name}}: %v", def.{{.Name}}))
					}
					{{end}}
					{{range .Indexes}}
					tab.{{$strus.Name}}By{{.Name}}[def.{{.Name}}] = def{{end}}

				}
			}},
		{{end}}

		},

		clearFuncByName: map[string][]func(*{{$.Name}}Table){

		{{range $a, $strus := .IndexedStructs}}
			"{{$strus.Name}}": {func(tab *{{$.Name}}Table) {

				// {{$strus.Name}}

				{{range .Indexes}}
				tab.{{$strus.Name}}By{{.Name}} = make(map[{{.KeyType}}]*{{$strus.TypeName}}){{end}}
			}},
		{{end}}

		},

		{{range $a, $strus := .IndexedStructs}} {{range .Indexes}}
		{{$strus.Name}}By{{.Name}} : make(map[{{.KeyType}}]*{{$strus.TypeName}}),
		{{end}} {{end}}

	}
}

`

// 每个带有MakeIndex的列
type goFieldModel struct {
	*model.FieldDescriptor
	Number int
}

func (self goFieldModel) Alias() string {
	return self.FieldDescriptor.Meta.GetString("Alias")
}

func (self goFieldModel) Comment() string {

	var out string

	if self.FieldDescriptor.Meta.GetString("Alias") != "" {
		out += "// "
		out += self.FieldDescriptor.Meta.GetString("Alias")
	}

	if self.FieldDescriptor.Comment != "" {
		if out == "" {
			out += "//"
		}
		out += self.FieldDescriptor.Comment
	}

	return out

}

func (self *goFieldModel) KeyType() string {

	// 修复: 当枚举做索引时, 多出包名
	if self.Type == model.FieldType_Enum {
		return self.Complex.Name
	}

	return model.FieldTypeToString(self.Type)
}

func (self *goFieldModel) StructTag() string {

	var buf bytes.Buffer

	buf.WriteString("`")

	var userTagCount int

	self.Meta.VisitUserMeta(func(k string, v interface{}) bool {

		if userTagCount > 0 {
			buf.WriteString(" ")
		}

		buf.WriteString(fmt.Sprintf("%s:\"%s\"", k, v))

		userTagCount++

		return true
	})

	buf.WriteString("`")

	if userTagCount == 0 {
		return ""
	}

	return buf.String()
}

func (self *goFieldModel) ElementTypeString() string {

	switch self.FieldDescriptor.Type {
	case model.FieldType_Float:
		return "float32"
	case model.FieldType_Struct:
		return "*" + self.FieldDescriptor.TypeString()
	default:
		return self.FieldDescriptor.TypeString()
	}
}

func (self *goFieldModel) TypeString() string {

	var prefix string
	if self.IsRepeated {
		prefix = "[]"
	}

	return prefix + self.ElementTypeString()

}

// 对应每个电子表格文件
type goIndexStructModel struct {
	*model.FieldDescriptor

	Indexes []*goFieldModel
}

func (self *goIndexStructModel) TypeName() string {
	return self.Complex.Name
}

type goStructModel struct {
	*model.Descriptor

	GoFields []*goFieldModel
}

func (self *goStructModel) DefinedTable() string {
	return self.File.Name
}

// 整个导出文件
type goFileModel struct {
	*model.FileDescriptor
	ToolVersion    string
	IndexedStructs []*goIndexStructModel
	Structs        []*goStructModel
	Enums          []*goStructModel
	IndexCount     int

	// 配置的字段
	VerticalFields []*goFieldModel
}

func (self *goFileModel) HasAnyIndex() bool {
	return self.IndexCount > 0
}

func (self *goFileModel) HasAnyStruct() bool {
	return len(self.Structs) > 0
}

func (self *goFileModel) Package() string {
	return self.FileDescriptor.Pragma.GetString("Package")
}

type goPrinter struct {
}

func collectIndexInfo(g *Globals, fm *goFileModel) {

	// 遍历需要导出的合并字段
	for _, fd := range g.CombineStruct.Fields {

		// fd --> 对应每个文件的Row定义, 也就是XXDefine, 在CombineStruct上, 只是一个字段

		// 对CombineStruct的XXDefine对应的字段
		if g.CombineStruct.Usage != model.DescriptorUsage_CombineStruct {
			continue
		}

		//非结构体不输出(其实根本不会击中)
		if fd.Complex == nil {
			continue
		}

		// 这个字段被限制输出
		if !fd.Complex.File.MatchTag(".go") {
			continue
		}

		// 这个结构有索引才创建
		if len(fd.Complex.Indexes) == 0 {
			continue
		}

		rm := goIndexStructModel{FieldDescriptor: fd}

		// 索引字段
		for _, key := range fd.Complex.Indexes {

			rm.Indexes = append(rm.Indexes, &goFieldModel{
				FieldDescriptor: key,
			})
			fm.IndexCount++
		}

		fm.IndexedStructs = append(fm.IndexedStructs, &rm)

	}
}

func collectAllStructInfo(g *Globals, fm *goFileModel) {

	// 遍历所有类型
	for _, d := range g.FileDescriptor.Descriptors {

		// 这给被限制输出
		if !d.File.MatchTag(".go") {
			log.Infof("%s: %s", i18n.String(i18n.Printer_IgnoredByOutputTag), d.Name)
			continue
		}

		structM := &goStructModel{Descriptor: d}

		// 遍历字段
		for index, fd := range d.Fields {

			// 对CombineStruct的XXDefine对应的字段
			if d.Usage == model.DescriptorUsage_CombineStruct {

				// 这个字段被限制输出
				if fd.Complex != nil && !fd.Complex.File.MatchTag(".go") {
					continue
				}

				if fd.Complex != nil && fd.Complex.File.Pragma.GetBool("Vertical") {
					fm.VerticalFields = append(fm.VerticalFields, &goFieldModel{FieldDescriptor: fd})
				}
			}

			field := &goFieldModel{FieldDescriptor: fd}

			switch d.Kind {
			case model.DescriptorKind_Struct:
				field.Number = index + 1
			case model.DescriptorKind_Enum:
				field.Number = int(fd.EnumValue)
			}

			structM.GoFields = append(structM.GoFields, field)

		}

		switch d.Kind {
		case model.DescriptorKind_Struct:
			fm.Structs = append(fm.Structs, structM)
		case model.DescriptorKind_Enum:
			fm.Enums = append(fm.Enums, structM)
		}

	}
}

func (self *goPrinter) Run(g *Globals) *Stream {

	tpl, err := template.New("golang").Parse(goTemplate)
	if err != nil {
		log.Errorln(err)
		return nil
	}

	var fm goFileModel
	fm.ToolVersion = g.Version
	fm.FileDescriptor = g.FileDescriptor

	collectIndexInfo(g, &fm)
	collectAllStructInfo(g, &fm)

	bf := NewStream()

	err = tpl.Execute(bf.Buffer(), &fm)
	if err != nil {
		log.Errorln(err)
		return nil
	}

	if err := formatCode(bf.Buffer()); err != nil {
		log.Errorln("format golang code err", err)
		log.Errorln(bf.Buffer().String())
		return nil
	}

	return bf
}

func formatCode(bf *bytes.Buffer) error {

	fset := token.NewFileSet()

	ast, err := parser.ParseFile(fset, "", bf, parser.ParseComments)
	if err != nil {
		return err
	}

	bf.Reset()

	err = (&printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}).Fprint(bf, fset, ast)
	if err != nil {
		return err
	}

	return nil
}

func init() {

	RegisterPrinter("go", &goPrinter{})

}
<commit_msg>修复: 热更新时, 数据没有完全复位<commit_after>package printer

import (
	"bytes"
	"fmt"
	"go/parser"
	"go/printer"
	"go/token"
	"text/template"

	"github.com/davyxu/tabtoy/exportorv2/i18n"
	"github.com/davyxu/tabtoy/exportorv2/model"
)

const goTemplate = `// Generated by github.com/davyxu/tabtoy
// Version: {{.ToolVersion}}
// DO NOT EDIT!!
package {{.Package}}

import(
	{{if .HasAnyIndex}}"fmt"{{end}}
	"encoding/json"
	"io/ioutil"
)
{{range $a, $en := .Enums}}
// Defined in table: {{$en.DefinedTable}}
type {{$en.Name}} int32
const (
{{range .GoFields}}
	{{.Comment}}
	{{$en.Name}}_{{.Name}} {{$en.Name}} = {{.Number}}
{{end}}
)
{{end}}

{{range $a, $strus := .Structs}}
// Defined in table: {{$strus.DefinedTable}}
type {{$strus.Name}} struct{
	{{range $b, $fd := $strus.GoFields}}
	{{.Comment}}
	{{$fd.Name}} {{$fd.TypeString}} {{$fd.StructTag}}
	{{end}}
}
{{end}}

// {{$.Name}} 访问接口
type {{$.Name}}Table struct{

	// 表格原始数据
	{{$.Name}}

	// 索引函数表
	indexFuncByName map[string][]func(*{{$.Name}}Table)

	// 清空函数表
	clearFuncByName map[string][]func(*{{$.Name}}Table)

	{{range $a, $strus := .IndexedStructs}} {{range .Indexes}}
	{{$strus.Name}}By{{.Name}} map[{{.KeyType}}]*{{$strus.TypeName}}
	{{end}} {{end}}
}

{{range .VerticalFields}}
{{.Comment}}
func (self *{{$.Name}}Table) Get{{.Name}}( ) {{.ElementTypeString}} {
	return self.{{.Name}}[0]
}
{{end}}

// 从json文件加载
func (self *{{$.Name}}Table) Load(filename string) error {

	data, err := ioutil.ReadFile(filename)

	if err != nil {
		return err
	}

	// 清除前通知
	for _, list := range self.clearFuncByName {
		for _, v := range list {
			v(self)
		}
	}

	// 复位数据
	self.{{$.Name}} = {{$.Name}}{}

	// 读取
	err = json.Unmarshal(data, &self.{{$.Name}})
	if err != nil {
		return err
	}

	// 生成索引
	for _, list := range self.indexFuncByName {
		for _, v := range list {
			v(self)
		}
	}

	return nil
}

// 注册外部索引入口, 索引回调, 清空回调
func (self *{{$.Name}}Table) RegisterIndexEntry(name string, indexCallback func(*{{$.Name}}Table), clearCallback func(*{{$.Name}}Table)) {

	indexList, _ := self.indexFuncByName[name]
	clearList, _ := self.clearFuncByName[name]

	if indexCallback != nil {
		indexList = append(indexList, indexCallback)
	}

	if clearCallback != nil {
		clearList = append(clearList, clearCallback)
	}

	self.indexFuncByName[name] = indexList
	self.clearFuncByName[name] = clearList
}

// 创建一个{{$.Name}}表读取实例
func New{{$.Name}}Table() *{{$.Name}}Table {
	return &{{$.Name}}Table{

		indexFuncByName: map[string][]func(*{{$.Name}}Table){

		{{range $a, $strus := .IndexedStructs}}
			"{{$strus.Name}}": {func(tab *{{$.Name}}Table) {

				// {{$strus.Name}}
				for _, def := range tab.{{$strus.Name}} {
					{{range .Indexes}}
					if _, ok := tab.{{$strus.Name}}By{{.Name}}[def.{{.Name}}]; ok {
						panic(fmt.Sprintf("duplicate index in {{$strus.Name}}By{{.Name}}: %v", def.{{.Name}}))
					}
					{{end}}
					{{range .Indexes}}
					tab.{{$strus.Name}}By{{.Name}}[def.{{.Name}}] = def{{end}}

				}
			}},
		{{end}}

		},

		clearFuncByName: map[string][]func(*{{$.Name}}Table){

		{{range $a, $strus := .IndexedStructs}}
			"{{$strus.Name}}": {func(tab *{{$.Name}}Table) {

				// {{$strus.Name}}

				{{range .Indexes}}
				tab.{{$strus.Name}}By{{.Name}} = make(map[{{.KeyType}}]*{{$strus.TypeName}}){{end}}
			}},
		{{end}}

		},

		{{range $a, $strus := .IndexedStructs}} {{range .Indexes}}
		{{$strus.Name}}By{{.Name}} : make(map[{{.KeyType}}]*{{$strus.TypeName}}),
		{{end}} {{end}}

	}
}

`

// 每个带有MakeIndex的列
type goFieldModel struct {
	*model.FieldDescriptor
	Number int
}

func (self goFieldModel) Alias() string {
	return self.FieldDescriptor.Meta.GetString("Alias")
}

func (self goFieldModel) Comment() string {

	var out string

	if self.FieldDescriptor.Meta.GetString("Alias") != "" {
		out += "// "
		out += self.FieldDescriptor.Meta.GetString("Alias")
	}

	if self.FieldDescriptor.Comment != "" {
		if out == "" {
			out += "//"
		}
		out += self.FieldDescriptor.Comment
	}

	return out

}

func (self *goFieldModel) KeyType() string {

	// 修复: 当枚举做索引时, 多出包名
	if self.Type == model.FieldType_Enum {
		return self.Complex.Name
	}

	return model.FieldTypeToString(self.Type)
}

func (self *goFieldModel) StructTag() string {

	var buf bytes.Buffer

	buf.WriteString("`")

	var userTagCount int

	self.Meta.VisitUserMeta(func(k string, v interface{}) bool {

		if userTagCount > 0 {
			buf.WriteString(" ")
		}

		buf.WriteString(fmt.Sprintf("%s:\"%s\"", k, v))

		userTagCount++

		return true
	})

	buf.WriteString("`")

	if userTagCount == 0 {
		return ""
	}

	return buf.String()
}

func (self *goFieldModel) ElementTypeString() string {

	switch self.FieldDescriptor.Type {
	case model.FieldType_Float:
		return "float32"
	case model.FieldType_Struct:
		return "*" + self.FieldDescriptor.TypeString()
	default:
		return self.FieldDescriptor.TypeString()
	}
}

func (self *goFieldModel) TypeString() string {

	var prefix string
	if self.IsRepeated {
		prefix = "[]"
	}

	return prefix + self.ElementTypeString()

}

// 对应每个电子表格文件
type goIndexStructModel struct {
	*model.FieldDescriptor

	Indexes []*goFieldModel
}

func (self *goIndexStructModel) TypeName() string {
	return self.Complex.Name
}

type goStructModel struct {
	*model.Descriptor

	GoFields []*goFieldModel
}

func (self *goStructModel) DefinedTable() string {
	return self.File.Name
}

// 整个导出文件
type goFileModel struct {
	*model.FileDescriptor
	ToolVersion    string
	IndexedStructs []*goIndexStructModel
	Structs        []*goStructModel
	Enums          []*goStructModel
	IndexCount     int

	// 配置的字段
	VerticalFields []*goFieldModel
}

func (self *goFileModel) HasAnyIndex() bool {
	return self.IndexCount > 0
}

func (self *goFileModel) HasAnyStruct() bool {
	return len(self.Structs) > 0
}

func (self *goFileModel) Package() string {
	return self.FileDescriptor.Pragma.GetString("Package")
}

type goPrinter struct {
}

func collectIndexInfo(g *Globals, fm *goFileModel) {

	// 遍历需要导出的合并字段
	for _, fd := range g.CombineStruct.Fields {

		// fd --> 对应每个文件的Row定义, 也就是XXDefine, 在CombineStruct上, 只是一个字段

		// 对CombineStruct的XXDefine对应的字段
		if g.CombineStruct.Usage != model.DescriptorUsage_CombineStruct {
			continue
		}

		//非结构体不输出(其实根本不会击中)
		if fd.Complex == nil {
			continue
		}

		// 这个字段被限制输出
		if !fd.Complex.File.MatchTag(".go") {
			continue
		}

		// 这个结构有索引才创建
		if len(fd.Complex.Indexes) == 0 {
			continue
		}

		rm := goIndexStructModel{FieldDescriptor: fd}

		// 索引字段
		for _, key := range fd.Complex.Indexes {

			rm.Indexes = append(rm.Indexes, &goFieldModel{
				FieldDescriptor: key,
			})
			fm.IndexCount++
		}

		fm.IndexedStructs = append(fm.IndexedStructs, &rm)

	}
}

func collectAllStructInfo(g *Globals, fm *goFileModel) {

	// 遍历所有类型
	for _, d := range g.FileDescriptor.Descriptors {

		// 这给被限制输出
		if !d.File.MatchTag(".go") {
			log.Infof("%s: %s", i18n.String(i18n.Printer_IgnoredByOutputTag), d.Name)
			continue
		}

		structM := &goStructModel{Descriptor: d}

		// 遍历字段
		for index, fd := range d.Fields {

			// 对CombineStruct的XXDefine对应的字段
			if d.Usage == model.DescriptorUsage_CombineStruct {

				// 这个字段被限制输出
				if fd.Complex != nil && !fd.Complex.File.MatchTag(".go") {
					continue
				}

				if fd.Complex != nil && fd.Complex.File.Pragma.GetBool("Vertical") {
					fm.VerticalFields = append(fm.VerticalFields, &goFieldModel{FieldDescriptor: fd})
				}
			}

			field := &goFieldModel{FieldDescriptor: fd}

			switch d.Kind {
			case model.DescriptorKind_Struct:
				field.Number = index + 1
			case model.DescriptorKind_Enum:
				field.Number = int(fd.EnumValue)
			}

			structM.GoFields = append(structM.GoFields, field)

		}

		switch d.Kind {
		case model.DescriptorKind_Struct:
			fm.Structs = append(fm.Structs, structM)
		case model.DescriptorKind_Enum:
			fm.Enums = append(fm.Enums, structM)
		}

	}
}

func (self *goPrinter) Run(g *Globals) *Stream {

	tpl, err := template.New("golang").Parse(goTemplate)
	if err != nil {
		log.Errorln(err)
		return nil
	}

	var fm goFileModel
	fm.ToolVersion = g.Version
	fm.FileDescriptor = g.FileDescriptor

	collectIndexInfo(g, &fm)
	collectAllStructInfo(g, &fm)

	bf := NewStream()

	err = tpl.Execute(bf.Buffer(), &fm)
	if err != nil {
		log.Errorln(err)
		return nil
	}

	if err := formatCode(bf.Buffer()); err != nil {
		log.Errorln("format golang code err", err)
		log.Errorln(bf.Buffer().String())
		return nil
	}

	return bf
}

func formatCode(bf *bytes.Buffer) error {

	fset := token.NewFileSet()

	ast, err := parser.ParseFile(fset, "", bf, parser.ParseComments)
	if err != nil {
		return err
	}

	bf.Reset()

	err = (&printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}).Fprint(bf, fset, ast)
	if err != nil {
		return err
	}

	return nil
}

func init() {

	RegisterPrinter("go", &goPrinter{})

}
<|endoftext|>


<commit_before>package logging

import (
	"encoding/json"
	"fmt"
	"time"
)

const (
	StandardTimeFormat = "2006-01-02 15:04:05.000Z07:00"
	tolerance          = 25 * time.Millisecond
)

// Formatter is interface for defining new custom log messages formats.
type Formatter interface {
	// Should return string that represents log message based on provided Record.
	Format(record *Record) string
}

// StandardFormatter adds time of logging with message to be logged.
type StandardFormatter struct {
	TimeFormat string
}

// Format constructs string for logging. Time of logging is added to log message.
// Also, if time of logging is more then 25 miliseconds in the passt, both
// times will be added to message (time when application sent log and time when
// message was processed). Otherwise, only time of processing will be written.
func (formatter *StandardFormatter) Format(record *Record) string {
	var message string
	now := time.Now()
	if now.Sub(record.Time) <= tolerance {
		message = record.Message
	} else {
		message = fmt.Sprintf("[%v] %v", record.Time.Format(formatter.TimeFormat), record.Message)
	}
	return fmt.Sprintf("[%v] %v %v", now.Format(formatter.TimeFormat), record.Level, message)
}

// JsonFormatter creates JSON struct with provided record.
type JsonFormatter struct{}

// Format creates JSON struct from provided record and returns it.
func (formatter *JsonFormatter) Format(record *Record) string {
	data, _ := json.Marshal(record)
	return string(data)
}

// MessageFormatter logs only Message from the Record.
type MessageFormatter struct{}

// Format returns only the Record Message
func (formatter *MessageFormatter) Format(record *Record) string {
	return record.Message
}

<commit_msg>Rename JsonFormatter to JSONFormatter<commit_after>package logging

import (
	"encoding/json"
	"fmt"
	"time"
)

const (
	// StandardTimeFormat defines a representaion of timestamps in log lines.
	StandardTimeFormat = "2006-01-02 15:04:05.000Z07:00"
	tolerance          = 25 * time.Millisecond
)

// Formatter is interface for defining new custom log messages formats.
type Formatter interface {
	// Should return string that represents log message based on provided Record.
	Format(record *Record) string
}

// StandardFormatter adds time of logging with message to be logged.
type StandardFormatter struct {
	TimeFormat string
}

// Format constructs string for logging. Time of logging is added to log message.
// Also, if time of logging is more then 25 miliseconds in the passt, both
// times will be added to message (time when application sent log and time when
// message was processed). Otherwise, only time of processing will be written.
func (formatter *StandardFormatter) Format(record *Record) string {
	var message string
	now := time.Now()
	if now.Sub(record.Time) <= tolerance {
		message = record.Message
	} else {
		message = fmt.Sprintf("[%v] %v", record.Time.Format(formatter.TimeFormat), record.Message)
	}
	return fmt.Sprintf("[%v] %v %v", now.Format(formatter.TimeFormat), record.Level, message)
}

// JSONFormatter creates JSON struct with provided record.
type JSONFormatter struct{}

// Format creates JSON struct from provided record and returns it.
func (formatter *JSONFormatter) Format(record *Record) string {
	data, _ := json.Marshal(record)
	return string(data)
}

// MessageFormatter logs only Message from the Record.
type MessageFormatter struct{}

// Format returns only the Record Message
func (formatter *MessageFormatter) Format(record *Record) string {
	return record.Message
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes 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 testing

import (
	"math/rand"
	"testing"

	admissionv1beta1 "k8s.io/api/admission/v1beta1"
	admissionregv1beta1 "k8s.io/api/admissionregistration/v1beta1"
	appsv1 "k8s.io/api/apps/v1"
	appsv1beta1 "k8s.io/api/apps/v1beta1"
	appsv1beta2 "k8s.io/api/apps/v1beta2"
	authenticationv1 "k8s.io/api/authentication/v1"
	authenticationv1beta1 "k8s.io/api/authentication/v1beta1"
	authorizationv1 "k8s.io/api/authorization/v1"
	authorizationv1beta1 "k8s.io/api/authorization/v1beta1"
	autoscalingv1 "k8s.io/api/autoscaling/v1"
	autoscalingv2beta1 "k8s.io/api/autoscaling/v2beta1"
	batchv1 "k8s.io/api/batch/v1"
	batchv1beta1 "k8s.io/api/batch/v1beta1"
	batchv2alpha1 "k8s.io/api/batch/v2alpha1"
	certificatesv1beta1 "k8s.io/api/certificates/v1beta1"
	corev1 "k8s.io/api/core/v1"
	eventsv1beta1 "k8s.io/api/events/v1beta1"
	extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
	imagepolicyv1alpha1 "k8s.io/api/imagepolicy/v1alpha1"
	networkingv1 "k8s.io/api/networking/v1"
	policyv1beta1 "k8s.io/api/policy/v1beta1"
	rbacv1 "k8s.io/api/rbac/v1"
	rbacv1alpha1 "k8s.io/api/rbac/v1alpha1"
	rbacv1beta1 "k8s.io/api/rbac/v1beta1"
	schedulingv1 "k8s.io/api/scheduling/v1"
	schedulingv1alpha1 "k8s.io/api/scheduling/v1alpha1"
	schedulingv1beta1 "k8s.io/api/scheduling/v1beta1"
	settingsv1alpha1 "k8s.io/api/settings/v1alpha1"
	storagev1 "k8s.io/api/storage/v1"
	storagev1alpha1 "k8s.io/api/storage/v1alpha1"
	storagev1beta1 "k8s.io/api/storage/v1beta1"

	"github.com/stretchr/testify/require"
	"k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
	"k8s.io/apimachinery/pkg/api/apitesting/roundtrip"
	genericfuzzer "k8s.io/apimachinery/pkg/apis/meta/fuzzer"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/runtime/serializer"
)

var groups = []runtime.SchemeBuilder{
	admissionv1beta1.SchemeBuilder,
	admissionregv1beta1.SchemeBuilder,
	appsv1beta1.SchemeBuilder,
	appsv1beta2.SchemeBuilder,
	appsv1.SchemeBuilder,
	authenticationv1beta1.SchemeBuilder,
	authenticationv1.SchemeBuilder,
	authorizationv1beta1.SchemeBuilder,
	authorizationv1.SchemeBuilder,
	autoscalingv1.SchemeBuilder,
	autoscalingv2beta1.SchemeBuilder,
	batchv2alpha1.SchemeBuilder,
	batchv1beta1.SchemeBuilder,
	batchv1.SchemeBuilder,
	certificatesv1beta1.SchemeBuilder,
	corev1.SchemeBuilder,
	eventsv1beta1.SchemeBuilder,
	extensionsv1beta1.SchemeBuilder,
	imagepolicyv1alpha1.SchemeBuilder,
	networkingv1.SchemeBuilder,
	policyv1beta1.SchemeBuilder,
	rbacv1alpha1.SchemeBuilder,
	rbacv1beta1.SchemeBuilder,
	rbacv1.SchemeBuilder,
	schedulingv1alpha1.SchemeBuilder,
	schedulingv1beta1.SchemeBuilder,
	schedulingv1.SchemeBuilder,
	settingsv1alpha1.SchemeBuilder,
	storagev1alpha1.SchemeBuilder,
	storagev1beta1.SchemeBuilder,
	storagev1.SchemeBuilder,
}

func TestRoundTripExternalTypes(t *testing.T) {
	for _, builder := range groups {
		scheme := runtime.NewScheme()
		codecs := serializer.NewCodecFactory(scheme)

		require.NoError(t, builder.AddToScheme(scheme))
		seed := rand.Int63()
		// I'm only using the generic fuzzer funcs, but at some point in time we might need to
		// switch to specialized. For now we're happy with the current serialization test.
		fuzzer := fuzzer.FuzzerFor(genericfuzzer.Funcs, rand.NewSource(seed), codecs)

		roundtrip.RoundTripExternalTypes(t, scheme, codecs, fuzzer, nil)
	}
}

func TestFailRoundTrip(t *testing.T) {
	scheme := runtime.NewScheme()
	codecs := serializer.NewCodecFactory(scheme)
	groupVersion := schema.GroupVersion{Group: "broken", Version: "v1"}
	builder := runtime.NewSchemeBuilder(func(scheme *runtime.Scheme) error {
		scheme.AddKnownTypes(groupVersion, &BrokenType{})
		metav1.AddToGroupVersion(scheme, groupVersion)
		return nil
	})
	require.NoError(t, builder.AddToScheme(scheme))
	seed := rand.Int63()
	fuzzer := fuzzer.FuzzerFor(genericfuzzer.Funcs, rand.NewSource(seed), codecs)
	tmpT := new(testing.T)
	roundtrip.RoundTripExternalTypes(tmpT, scheme, codecs, fuzzer, nil)
	// It's very hacky way of making sure the DeepCopy is actually invoked inside RoundTripExternalTypes
	// used in the other test. If for some reason this tests starts passing we need to fail b/c we're not testing
	// the DeepCopy in the other method which we care so much about.
	if !tmpT.Failed() {
		t.Log("RoundTrip should've failed on DeepCopy but it did not!")
		t.FailNow()
	}
}

type BrokenType struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Field1 string `json:"field1,omitempty"`
	Field2 string `json:"field2,omitempty"`
}

func (in *BrokenType) DeepCopy() *BrokenType {
	return new(BrokenType)
}

func (in *BrokenType) DeepCopyObject() runtime.Object {
	return in.DeepCopy()
}
<commit_msg>Remove broken round trip test<commit_after>/*
Copyright 2018 The Kubernetes 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 testing

import (
	"math/rand"
	"testing"

	admissionv1beta1 "k8s.io/api/admission/v1beta1"
	admissionregv1beta1 "k8s.io/api/admissionregistration/v1beta1"
	appsv1 "k8s.io/api/apps/v1"
	appsv1beta1 "k8s.io/api/apps/v1beta1"
	appsv1beta2 "k8s.io/api/apps/v1beta2"
	authenticationv1 "k8s.io/api/authentication/v1"
	authenticationv1beta1 "k8s.io/api/authentication/v1beta1"
	authorizationv1 "k8s.io/api/authorization/v1"
	authorizationv1beta1 "k8s.io/api/authorization/v1beta1"
	autoscalingv1 "k8s.io/api/autoscaling/v1"
	autoscalingv2beta1 "k8s.io/api/autoscaling/v2beta1"
	batchv1 "k8s.io/api/batch/v1"
	batchv1beta1 "k8s.io/api/batch/v1beta1"
	batchv2alpha1 "k8s.io/api/batch/v2alpha1"
	certificatesv1beta1 "k8s.io/api/certificates/v1beta1"
	corev1 "k8s.io/api/core/v1"
	eventsv1beta1 "k8s.io/api/events/v1beta1"
	extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
	imagepolicyv1alpha1 "k8s.io/api/imagepolicy/v1alpha1"
	networkingv1 "k8s.io/api/networking/v1"
	policyv1beta1 "k8s.io/api/policy/v1beta1"
	rbacv1 "k8s.io/api/rbac/v1"
	rbacv1alpha1 "k8s.io/api/rbac/v1alpha1"
	rbacv1beta1 "k8s.io/api/rbac/v1beta1"
	schedulingv1 "k8s.io/api/scheduling/v1"
	schedulingv1alpha1 "k8s.io/api/scheduling/v1alpha1"
	schedulingv1beta1 "k8s.io/api/scheduling/v1beta1"
	settingsv1alpha1 "k8s.io/api/settings/v1alpha1"
	storagev1 "k8s.io/api/storage/v1"
	storagev1alpha1 "k8s.io/api/storage/v1alpha1"
	storagev1beta1 "k8s.io/api/storage/v1beta1"

	"github.com/stretchr/testify/require"
	"k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
	"k8s.io/apimachinery/pkg/api/apitesting/roundtrip"
	genericfuzzer "k8s.io/apimachinery/pkg/apis/meta/fuzzer"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/serializer"
)

var groups = []runtime.SchemeBuilder{
	admissionv1beta1.SchemeBuilder,
	admissionregv1beta1.SchemeBuilder,
	appsv1beta1.SchemeBuilder,
	appsv1beta2.SchemeBuilder,
	appsv1.SchemeBuilder,
	authenticationv1beta1.SchemeBuilder,
	authenticationv1.SchemeBuilder,
	authorizationv1beta1.SchemeBuilder,
	authorizationv1.SchemeBuilder,
	autoscalingv1.SchemeBuilder,
	autoscalingv2beta1.SchemeBuilder,
	batchv2alpha1.SchemeBuilder,
	batchv1beta1.SchemeBuilder,
	batchv1.SchemeBuilder,
	certificatesv1beta1.SchemeBuilder,
	corev1.SchemeBuilder,
	eventsv1beta1.SchemeBuilder,
	extensionsv1beta1.SchemeBuilder,
	imagepolicyv1alpha1.SchemeBuilder,
	networkingv1.SchemeBuilder,
	policyv1beta1.SchemeBuilder,
	rbacv1alpha1.SchemeBuilder,
	rbacv1beta1.SchemeBuilder,
	rbacv1.SchemeBuilder,
	schedulingv1alpha1.SchemeBuilder,
	schedulingv1beta1.SchemeBuilder,
	schedulingv1.SchemeBuilder,
	settingsv1alpha1.SchemeBuilder,
	storagev1alpha1.SchemeBuilder,
	storagev1beta1.SchemeBuilder,
	storagev1.SchemeBuilder,
}

func TestRoundTripExternalTypes(t *testing.T) {
	for _, builder := range groups {
		scheme := runtime.NewScheme()
		codecs := serializer.NewCodecFactory(scheme)

		require.NoError(t, builder.AddToScheme(scheme))
		seed := rand.Int63()
		// I'm only using the generic fuzzer funcs, but at some point in time we might need to
		// switch to specialized. For now we're happy with the current serialization test.
		fuzzer := fuzzer.FuzzerFor(genericfuzzer.Funcs, rand.NewSource(seed), codecs)

		roundtrip.RoundTripExternalTypes(t, scheme, codecs, fuzzer, nil)
	}
}
<|endoftext|>


<commit_before>package v_3_0_0

import "github.com/giantswarm/apiextensions/pkg/apis/provider/v1alpha1"

const (
	defaultHyperkubeApiserverBindAddress = "${DEFAULT_IPV4}"
)

type Params struct {
	Cluster v1alpha1.Cluster
	// DisableCalico flag when set removes all calico related Kubernetes
	// manifests from the cloud config together with their initialization.
	DisableCalico bool
	// Hyperkube allows to pass extra `docker run` and `command` arguments
	// to hyperkube image commands. This allows to e.g. add cloud provider
	// extensions.
	Hyperkube Hyperkube
	Extension Extension
	// ExtraManifests allows to specify extra Kubernetes manifests in
	// /opt/k8s-addons script. The manifests are applied after calico is
	// ready.
	//
	// The general use-case is to create a manifest file with Extension and
	// then apply the manifest by adding it to ExtraManifests.
	ExtraManifests []string
	// MasterAPIDomain is a value of domain passed to various Kubernetes
	// services. When MasterAPIDomain is empty value of
	// Cluster.Kubernetes.API.Domain is passed.
	//
	// NOTE This is a work around limitation of Azure load balancers.
	// Hopefully Load Balancer Standard SKU will allow to get rid of that.
	//
	// azure-operator sets that to 127.0.0.1. Other operators leave it empty.
	MasterAPIDomain string
	Node            v1alpha1.ClusterNode
}

type Hyperkube struct {
	Apiserver         HyperkubeApiserver
	ControllerManager HyperkubeDocker
	Kubelet           HyperkubeDocker
}

type HyperkubeApiserver struct {
	HyperkubeDocker

	// BindAddress is a value of the --bind-address flag passed to the
	// hyperkube apiserver. When BindAddress is empty value of
	// `${DEFAULT_IPV4}` will be passed.
	//
	// NOTE This is a work around limitation of Azure load balancers.
	// Hopefully Load Balancer Standard SKU will allow to get rid of that.
	//
	// azure-operator sets that to 0.0.0.0. Other operators leave it empty.
	BindAddress string
}

type HyperkubeDocker struct {
	RunExtraArgs     []string
	CommandExtraArgs []string
}

type FileMetadata struct {
	AssetContent string
	Path         string
	Owner        string
	Encoding     string
	Permissions  int
}

type FileAsset struct {
	Metadata FileMetadata
	Content  []string
}

type UnitMetadata struct {
	AssetContent string
	Name         string
	Enable       bool
	Command      string
}

type UnitAsset struct {
	Metadata UnitMetadata
	Content  []string
}

// VerbatimSection is a blob of YAML we want to add to the
// CloudConfig, with no variable interpolation.
type VerbatimSection struct {
	Name    string
	Content string
}

type Extension interface {
	Files() ([]FileAsset, error)
	Units() ([]UnitAsset, error)
	VerbatimSections() []VerbatimSection
}
<commit_msg>v_3_0_0: add fine hyperkube types (#271)<commit_after>package v_3_0_0

import "github.com/giantswarm/apiextensions/pkg/apis/provider/v1alpha1"

const (
	defaultHyperkubeApiserverBindAddress = "${DEFAULT_IPV4}"
)

type Params struct {
	Cluster v1alpha1.Cluster
	// DisableCalico flag when set removes all calico related Kubernetes
	// manifests from the cloud config together with their initialization.
	DisableCalico bool
	// Hyperkube allows to pass extra `docker run` and `command` arguments
	// to hyperkube image commands. This allows to e.g. add cloud provider
	// extensions.
	Hyperkube Hyperkube
	Extension Extension
	// ExtraManifests allows to specify extra Kubernetes manifests in
	// /opt/k8s-addons script. The manifests are applied after calico is
	// ready.
	//
	// The general use-case is to create a manifest file with Extension and
	// then apply the manifest by adding it to ExtraManifests.
	ExtraManifests []string
	// MasterAPIDomain is a value of domain passed to various Kubernetes
	// services. When MasterAPIDomain is empty value of
	// Cluster.Kubernetes.API.Domain is passed.
	//
	// NOTE This is a work around limitation of Azure load balancers.
	// Hopefully Load Balancer Standard SKU will allow to get rid of that.
	//
	// azure-operator sets that to 127.0.0.1. Other operators leave it empty.
	MasterAPIDomain string
	Node            v1alpha1.ClusterNode
}

type Hyperkube struct {
	Apiserver         HyperkubeApiserver
	ControllerManager HyperkubeControllerManager
	Kubelet           HyperkubeKubelet
}

type HyperkubeApiserver struct {
	// BindAddress is a value of the --bind-address flag passed to the
	// hyperkube apiserver. When BindAddress is empty value of
	// `${DEFAULT_IPV4}` will be passed.
	//
	// NOTE This is a work around limitation of Azure load balancers.
	// Hopefully Load Balancer Standard SKU will allow to get rid of that.
	//
	// azure-operator sets that to 0.0.0.0. Other operators leave it empty.
	BindAddress string
	Docker      HyperkubeDocker
}

type HyperkubeControllerManager struct {
	Docker HyperkubeDocker
}

type HyperkubeKubelet struct {
	Docker HyperkubeDocker
}

type HyperkubeDocker struct {
	RunExtraArgs     []string
	CommandExtraArgs []string
}

type FileMetadata struct {
	AssetContent string
	Path         string
	Owner        string
	Encoding     string
	Permissions  int
}

type FileAsset struct {
	Metadata FileMetadata
	Content  []string
}

type UnitMetadata struct {
	AssetContent string
	Name         string
	Enable       bool
	Command      string
}

type UnitAsset struct {
	Metadata UnitMetadata
	Content  []string
}

// VerbatimSection is a blob of YAML we want to add to the
// CloudConfig, with no variable interpolation.
type VerbatimSection struct {
	Name    string
	Content string
}

type Extension interface {
	Files() ([]FileAsset, error)
	Units() ([]UnitAsset, error)
	VerbatimSections() []VerbatimSection
}
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package main

import (
	"context"
	"errors"
	"time"

	"google.golang.org/api/iterator"

	"cloud.google.com/go/firestore"
)

func addDocAsMap(ctx context.Context, client *firestore.Client) error {
	// [START fs_add_simple_doc_as_map]
	_, err := client.Collection("cities").Doc("LA").Set(ctx, map[string]interface{}{
		"name":    "Los Angeles",
		"state":   "CA",
		"country": "USA",
	})
	if err != nil {
		return err
	}
	// [END fs_add_simple_doc_as_map]
	return nil
}

func addDocDataTypes(ctx context.Context, client *firestore.Client) error {
	// [START fs_add_doc_data_types]
	doc := make(map[string]interface{})
	doc["stringExample"] = "Hello world!"
	doc["booleanExample"] = true
	doc["numberExample"] = 3.14159265
	doc["dateExample"] = time.Now()
	doc["arrayExample"] = []interface{}{5, true, "hello"}
	doc["nullExample"] = nil
	doc["objectExample"] = map[string]interface{}{
		"a": 5,
		"b": true,
	}

	_, err := client.Collection("data").Doc("one").Set(ctx, doc)
	if err != nil {
		return err
	}
	// [END fs_add_doc_data_types]
	return nil
}

func addDocWithID(ctx context.Context, client *firestore.Client) error {
	var data = make(map[string]interface{})
	// [START fs_add_doc_with_id]
	_, err := client.Collection("cities").Doc("new-city-id").Set(ctx, data)
	if err != nil {
		return err
	}
	// [END fs_add_doc_with_id]
	return nil
}

func addDocWithoutID(ctx context.Context, client *firestore.Client) error {
	// [START fs_add_doc_auto_id]
	_, _, err := client.Collection("cities").Add(ctx, map[string]interface{}{
		"name":    "Tokyo",
		"country": "Japan",
	})
	if err != nil {
		return err
	}
	// [END fs_add_doc_auto_id]
	return nil
}

func addDocAsEntity(ctx context.Context, client *firestore.Client) error {
	// [START fs_add_simple_doc_as_entity]
	city := City{
		Name:    "Los Angeles",
		Country: "USA",
	}
	_, err := client.Collection("cities").Doc("LA").Set(ctx, city)
	if err != nil {
		return err
	}
	// [END fs_add_simple_doc_as_entity]
	return nil
}

func addDocAfterAutoGeneratedID(ctx context.Context, client *firestore.Client) error {
	data := City{
		Name:    "Sydney",
		Country: "Australia",
	}

	// [START fs_add_doc_data_after_auto_id]
	ref := client.Collection("cities").NewDoc()

	// later...
	_, err := ref.Set(ctx, data)
	if err != nil {
		return err
	}
	// [END fs_add_doc_data_after_auto_id]
	return nil
}

func updateDoc(ctx context.Context, client *firestore.Client) error {
	// [START fs_update_doc]
	_, err := client.Collection("cities").Doc("DC").Set(ctx, map[string]interface{}{
		"capital": true,
	}, firestore.MergeAll)
	if err != nil {
		return err
	}
	// [END fs_update_doc]
	return nil
}

func updateDocCreateIfMissing(ctx context.Context, client *firestore.Client) error {
	// [START fs_update_create_if_missing]
	_, err := client.Collection("cities").Doc("BJ").Set(ctx, map[string]interface{}{
		"capital": true,
	}, firestore.MergeAll)

	if err != nil {
		return err
	}
	// [END fs_update_create_if_missing]
	return nil
}

func updateDocMultiple(ctx context.Context, client *firestore.Client) error {
	_, err := client.Collection("cities").Doc("Delhi").Set(ctx, City{Name: "Delhi"})
	if err != nil {
		return err
	}

	// [START fs_update_multiple_fields]
	_, err = client.Collection("cities").Doc("Delhi").Set(ctx, map[string]interface{}{
		"capital":           true,
		"country":           "India",
		"population":        16787941,
		"areaInSquareMiles": 573.0,
	}, firestore.MergeAll)
	if err != nil {
		return err
	}
	// [END fs_update_multiple_fields]
	return nil
}

func updateDocNested(ctx context.Context, client *firestore.Client) error {
	// [START fs_update_nested_fields]
	initialData := map[string]interface{}{
		"name": "Frank",
		"age":  12,
		"favorites": map[string]interface{}{
			"food":    "Pizza",
			"color":   "Blue",
			"subject": "recess",
		},
	}

	// [START_EXCLUDE]
	_, err := client.Collection("users").Doc("frank").Set(ctx, initialData)
	if err != nil {
		return err
	}
	// [END_EXCLUDE]

	_, err = client.Collection("users").Doc("frank").Set(ctx, map[string]interface{}{
		"age": 13,
		"favorites": map[string]interface{}{
			"color": "Red",
		},
	}, firestore.MergeAll)
	if err != nil {
		return err
	}
	// [END fs_update_nested_fields]
	return nil
}

func updateDocServerTimestamp(ctx context.Context, client *firestore.Client) error {
	_, preErr := client.Collection("objects").Doc("some-id").Set(ctx, map[string]interface{}{
		"timestamp": 0,
	})
	if preErr != nil {
		return preErr
	}

	// [START fs_update_server_timestamp]
	_, err := client.Collection("objects").Doc("some-id").Set(ctx, map[string]interface{}{
		"timestamp": firestore.ServerTimestamp,
	}, firestore.MergeAll)
	if err != nil {
		return err
	}
	// [END fs_update_server_timestamp]
	return nil
}

func deleteDoc(ctx context.Context, client *firestore.Client) error {
	// [START fs_delete_doc]
	_, err := client.Collection("cities").Doc("DC").Delete(ctx)
	if err != nil {
		return err
	}
	// [END fs_delete_doc]
	return nil
}

func deleteField(ctx context.Context, client *firestore.Client) error {
	// [START fs_delete_field]
	_, err := client.Collection("cities").Doc("BJ").Update(ctx, []firestore.Update{
		{
			Path:  "capital",
			Value: firestore.Delete,
		},
	})
	if err != nil {
		return err
	}
	// [END fs_delete_field]

	// Use Set once this feature is implemented:
	// https://github.com/GoogleCloudPlatform/google-cloud-go/issues/832
	// Set(ctx, map[string]interface{}{
	//	"capital": firestore.Delete,
	//})
	return nil
}

// [START fs_delete_collection]
func deleteCollection(ctx context.Context, client *firestore.Client,
	ref *firestore.CollectionRef, batchSize int) error {

	for {
		// Get a batch of documents
		iter := ref.Limit(batchSize).Documents(ctx)
		numDeleted := 0

		// Iterate through the documents, adding
		// a delete operation for each one to a
		// WriteBatch.
		batch := client.Batch()
		for {
			doc, err := iter.Next()
			if err == iterator.Done {
				break
			}
			if err != nil {
				return err
			}

			batch.Delete(doc.Ref)
			numDeleted++
		}

		// If there are no documents to delete,
		// the process is over.
		if numDeleted == 0 {
			return nil
		}

		_, err := batch.Commit(ctx)
		if err != nil {
			return err
		}
	}
}

// [END fs_delete_collection]

func runSimpleTransaction(ctx context.Context, client *firestore.Client) error {
	_, preErr := client.Collection("cities").Doc("SF").Set(ctx, map[string]interface{}{
		"population": 860000,
	})

	if preErr != nil {
		return preErr
	}

	// [START fs_run_simple_transaction]
	ref := client.Collection("cities").Doc("SF")
	err := client.RunTransaction(ctx, func(ctx context.Context, tx *firestore.Transaction) error {
		doc, err := tx.Get(ref) // tx.Get, NOT ref.Get!
		if err != nil {
			return err
		}
		pop, err := doc.DataAt("population")
		if err != nil {
			return err
		}
		return tx.Set(ref, map[string]interface{}{
			"population": pop.(int64) + 1,
		}, firestore.MergeAll)
	})
	if err != nil {
		return err
	}
	// [END fs_run_simple_transaction]
	return nil
}

func infoTransaction(ctx context.Context, client *firestore.Client) error {
	// [START fs_return_info_transaction]
	ref := client.Collection("cities").Doc("SF")
	err := client.RunTransaction(ctx, func(ctx context.Context, tx *firestore.Transaction) error {
		doc, err := tx.Get(ref)
		if err != nil {
			return err
		}
		pop, err := doc.DataAt("population")
		if err != nil {
			return err
		}
		newpop := pop.(int64) + 1
		if newpop <= 1000000 {
			return tx.Set(ref, map[string]interface{}{
				"population": pop.(int64) + 1,
			}, firestore.MergeAll)
		}
		return errors.New("population is too big")
	})
	if err != nil {
		return err
	}
	// [END fs_return_info_transaction]
	return nil
}

func batchWrite(ctx context.Context, client *firestore.Client) error {
	// [START fs_batch_write]
	// Get a new write batch.
	batch := client.Batch()

	// Set the value of "NYC".
	nycRef := client.Collection("cities").Doc("NYC")
	batch.Set(nycRef, map[string]interface{}{
		"name": "New York City",
	})

	// Update the population of "SF".
	sfRef := client.Collection("cities").Doc("SF")
	batch.Set(sfRef, map[string]interface{}{
		"population": 1000000,
	}, firestore.MergeAll)

	// Delete the city "LA".
	laRef := client.Collection("cities").Doc("LA")
	batch.Delete(laRef)

	// Commit the batch.
	_, err := batch.Commit(ctx)
	if err != nil {
		return err
	}
	// [END fs_batch_write]
	return nil
}
<commit_msg>firestore: update error handling (#624)<commit_after>// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package main

import (
	"context"
	"errors"
	"log"
	"time"

	"google.golang.org/api/iterator"

	"cloud.google.com/go/firestore"
)

func addDocAsMap(ctx context.Context, client *firestore.Client) error {
	// [START fs_add_simple_doc_as_map]
	_, err := client.Collection("cities").Doc("LA").Set(ctx, map[string]interface{}{
		"name":    "Los Angeles",
		"state":   "CA",
		"country": "USA",
	})
	if err != nil {
		// Handle any errors in an appropriate way, such as returning them.
		log.Printf("An error has occurred: %s", err)
	}
	// [END fs_add_simple_doc_as_map]
	return err
}

func addDocDataTypes(ctx context.Context, client *firestore.Client) error {
	// [START fs_add_doc_data_types]
	doc := make(map[string]interface{})
	doc["stringExample"] = "Hello world!"
	doc["booleanExample"] = true
	doc["numberExample"] = 3.14159265
	doc["dateExample"] = time.Now()
	doc["arrayExample"] = []interface{}{5, true, "hello"}
	doc["nullExample"] = nil
	doc["objectExample"] = map[string]interface{}{
		"a": 5,
		"b": true,
	}

	_, err := client.Collection("data").Doc("one").Set(ctx, doc)
	if err != nil {
		// Handle any errors in an appropriate way, such as returning them.
		log.Printf("An error has occurred: %s", err)
	}
	// [END fs_add_doc_data_types]
	return err
}

func addDocWithID(ctx context.Context, client *firestore.Client) error {
	var data = make(map[string]interface{})
	// [START fs_add_doc_with_id]
	_, err := client.Collection("cities").Doc("new-city-id").Set(ctx, data)
	if err != nil {
		// Handle any errors in an appropriate way, such as returning them.
		log.Printf("An error has occurred: %s", err)
	}
	// [END fs_add_doc_with_id]
	return err
}

func addDocWithoutID(ctx context.Context, client *firestore.Client) error {
	// [START fs_add_doc_auto_id]
	_, _, err := client.Collection("cities").Add(ctx, map[string]interface{}{
		"name":    "Tokyo",
		"country": "Japan",
	})
	if err != nil {
		// Handle any errors in an appropriate way, such as returning them.
		log.Printf("An error has occurred: %s", err)
	}
	// [END fs_add_doc_auto_id]
	return err
}

func addDocAsEntity(ctx context.Context, client *firestore.Client) error {
	// [START fs_add_simple_doc_as_entity]
	city := City{
		Name:    "Los Angeles",
		Country: "USA",
	}
	_, err := client.Collection("cities").Doc("LA").Set(ctx, city)
	if err != nil {
		// Handle any errors in an appropriate way, such as returning them.
		log.Printf("An error has occurred: %s", err)
	}
	// [END fs_add_simple_doc_as_entity]
	return err
}

func addDocAfterAutoGeneratedID(ctx context.Context, client *firestore.Client) error {
	data := City{
		Name:    "Sydney",
		Country: "Australia",
	}

	// [START fs_add_doc_data_after_auto_id]
	ref := client.Collection("cities").NewDoc()

	// later...
	_, err := ref.Set(ctx, data)
	if err != nil {
		// Handle any errors in an appropriate way, such as returning them.
		log.Printf("An error has occurred: %s", err)
	}
	// [END fs_add_doc_data_after_auto_id]
	return err
}

func updateDoc(ctx context.Context, client *firestore.Client) error {
	// [START fs_update_doc]
	_, err := client.Collection("cities").Doc("DC").Set(ctx, map[string]interface{}{
		"capital": true,
	}, firestore.MergeAll)
	if err != nil {
		// Handle any errors in an appropriate way, such as returning them.
		log.Printf("An error has occurred: %s", err)
	}
	// [END fs_update_doc]
	return err
}

func updateDocCreateIfMissing(ctx context.Context, client *firestore.Client) error {
	// [START fs_update_create_if_missing]
	_, err := client.Collection("cities").Doc("BJ").Set(ctx, map[string]interface{}{
		"capital": true,
	}, firestore.MergeAll)

	if err != nil {
		// Handle any errors in an appropriate way, such as returning them.
		log.Printf("An error has occurred: %s", err)
	}
	// [END fs_update_create_if_missing]
	return err
}

func updateDocMultiple(ctx context.Context, client *firestore.Client) error {
	_, err := client.Collection("cities").Doc("Delhi").Set(ctx, City{Name: "Delhi"})
	if err != nil {
		return err
	}

	// [START fs_update_multiple_fields]
	_, err = client.Collection("cities").Doc("Delhi").Set(ctx, map[string]interface{}{
		"capital":           true,
		"country":           "India",
		"population":        16787941,
		"areaInSquareMiles": 573.0,
	}, firestore.MergeAll)
	if err != nil {
		// Handle any errors in an appropriate way, such as returning them.
		log.Printf("An error has occurred: %s", err)
	}
	// [END fs_update_multiple_fields]
	return err
}

func updateDocNested(ctx context.Context, client *firestore.Client) error {
	// [START fs_update_nested_fields]
	initialData := map[string]interface{}{
		"name": "Frank",
		"age":  12,
		"favorites": map[string]interface{}{
			"food":    "Pizza",
			"color":   "Blue",
			"subject": "recess",
		},
	}

	// [START_EXCLUDE]

	if _, err := client.Collection("users").Doc("frank").Set(ctx, initialData); err != nil {
		return err
	}
	// [END_EXCLUDE]

	_, err := client.Collection("users").Doc("frank").Set(ctx, map[string]interface{}{
		"age": 13,
		"favorites": map[string]interface{}{
			"color": "Red",
		},
	}, firestore.MergeAll)
	if err != nil {
		// Handle any errors in an appropriate way, such as returning them.
		log.Printf("An error has occurred: %s", err)
	}
	// [END fs_update_nested_fields]
	return err
}

func updateDocServerTimestamp(ctx context.Context, client *firestore.Client) error {
	_, preErr := client.Collection("objects").Doc("some-id").Set(ctx, map[string]interface{}{
		"timestamp": 0,
	})
	if preErr != nil {
		return preErr
	}

	// [START fs_update_server_timestamp]
	_, err := client.Collection("objects").Doc("some-id").Set(ctx, map[string]interface{}{
		"timestamp": firestore.ServerTimestamp,
	}, firestore.MergeAll)
	if err != nil {
		// Handle any errors in an appropriate way, such as returning them.
		log.Printf("An error has occurred: %s", err)
	}
	// [END fs_update_server_timestamp]
	return err
}

func deleteDoc(ctx context.Context, client *firestore.Client) error {
	// [START fs_delete_doc]
	_, err := client.Collection("cities").Doc("DC").Delete(ctx)
	if err != nil {
		// Handle any errors in an appropriate way, such as returning them.
		log.Printf("An error has occurred: %s", err)
	}
	// [END fs_delete_doc]
	return err
}

func deleteField(ctx context.Context, client *firestore.Client) error {
	// [START fs_delete_field]
	_, err := client.Collection("cities").Doc("BJ").Update(ctx, []firestore.Update{
		{
			Path:  "capital",
			Value: firestore.Delete,
		},
	})
	if err != nil {
		// Handle any errors in an appropriate way, such as returning them.
		log.Printf("An error has occurred: %s", err)
	}
	// [END fs_delete_field]

	// Use Set once this feature is implemented:
	// https://github.com/GoogleCloudPlatform/google-cloud-go/issues/832
	// Set(ctx, map[string]interface{}{
	//	"capital": firestore.Delete,
	//})
	return err
}

// [START fs_delete_collection]
func deleteCollection(ctx context.Context, client *firestore.Client,
	ref *firestore.CollectionRef, batchSize int) error {

	for {
		// Get a batch of documents
		iter := ref.Limit(batchSize).Documents(ctx)
		numDeleted := 0

		// Iterate through the documents, adding
		// a delete operation for each one to a
		// WriteBatch.
		batch := client.Batch()
		for {
			doc, err := iter.Next()
			if err == iterator.Done {
				break
			}
			if err != nil {
				return err
			}

			batch.Delete(doc.Ref)
			numDeleted++
		}

		// If there are no documents to delete,
		// the process is over.
		if numDeleted == 0 {
			return nil
		}

		_, err := batch.Commit(ctx)
		if err != nil {
			return err
		}
	}
}

// [END fs_delete_collection]

func runSimpleTransaction(ctx context.Context, client *firestore.Client) error {
	_, preErr := client.Collection("cities").Doc("SF").Set(ctx, map[string]interface{}{
		"population": 860000,
	})

	if preErr != nil {
		return preErr
	}

	// [START fs_run_simple_transaction]
	ref := client.Collection("cities").Doc("SF")
	err := client.RunTransaction(ctx, func(ctx context.Context, tx *firestore.Transaction) error {
		doc, err := tx.Get(ref) // tx.Get, NOT ref.Get!
		if err != nil {
			return err
		}
		pop, err := doc.DataAt("population")
		if err != nil {
			return err
		}
		return tx.Set(ref, map[string]interface{}{
			"population": pop.(int64) + 1,
		}, firestore.MergeAll)
	})
	if err != nil {
		// Handle any errors appropriately in this section.
		log.Printf("An error has occurred: %s", err)
	}
	// [END fs_run_simple_transaction]
	return err
}

func infoTransaction(ctx context.Context, client *firestore.Client) error {
	// [START fs_return_info_transaction]
	ref := client.Collection("cities").Doc("SF")
	err := client.RunTransaction(ctx, func(ctx context.Context, tx *firestore.Transaction) error {
		doc, err := tx.Get(ref)
		if err != nil {
			return err
		}
		pop, err := doc.DataAt("population")
		if err != nil {
			return err
		}
		newpop := pop.(int64) + 1
		if newpop <= 1000000 {
			return tx.Set(ref, map[string]interface{}{
				"population": pop.(int64) + 1,
			}, firestore.MergeAll)
		}
		return errors.New("population is too big")
	})
	if err != nil {
		// Handle any errors in an appropriate way, such as returning them.
		log.Printf("An error has occurred: %s", err)
	}
	// [END fs_return_info_transaction]
	return err
}

func batchWrite(ctx context.Context, client *firestore.Client) error {
	// [START fs_batch_write]
	// Get a new write batch.
	batch := client.Batch()

	// Set the value of "NYC".
	nycRef := client.Collection("cities").Doc("NYC")
	batch.Set(nycRef, map[string]interface{}{
		"name": "New York City",
	})

	// Update the population of "SF".
	sfRef := client.Collection("cities").Doc("SF")
	batch.Set(sfRef, map[string]interface{}{
		"population": 1000000,
	}, firestore.MergeAll)

	// Delete the city "LA".
	laRef := client.Collection("cities").Doc("LA")
	batch.Delete(laRef)

	// Commit the batch.
	_, err := batch.Commit(ctx)
	if err != nil {
		// Handle any errors in an appropriate way, such as returning them.
		log.Printf("An error has occurred: %s", err)
	}
	// [END fs_batch_write]
	return err
}
<|endoftext|>


<commit_before>package main

/*
1)将长网址md5生成32位签名串,分为4段, 每段8个字节;
2)对这四段循环处理, 取8个字节, 将他看成16进制串与0x3fffffff(30位1)与操作, 即超过30位的忽略处理;
3)这30位分成6段, 每5位的数字作为字母表的索引取得特定字符, 依次进行获得6位字符串;
4)总的md5串可以获得4个6位串; 取里面的任意一个就可作为这个长url的短url地址;
这种算法,虽然会生成4个,但是仍然存在重复几率,下面的算法一和三,就是这种的实现.
*/
import (
	// "bytes"
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
	"strconv"
	// "strings"
)

const (
	signSalt = "Ak47"
	base32   = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"
)

func main() {
	url := "www.google.com"
	h := md5.New()
	io.WriteString(h, url+signSalt)
	hx := h.Sum(nil)
	var result [4]string
	s := hex.EncodeToString(hx)
	length := len(s) / 8
	for i := 0; i < length; i++ {
		subhex := s[i*8 : i*8+8]
		// fmt.Println(strconv.QuoteToASCII("0x" + subhex))
		//hexstr := strconv.QuoteToASCII("0x" + subhex)
		bit, _ := strconv.ParseInt(subhex, 16, 64)
		// fmt.Println(bit & 0x3FFFFFFF)
		var chHex = 0x3FFFFFFF & bit
		var outchars = ""
		for i := 0; i < 6; i++ {
			val := 0x0000003D & chHex
			outchars += base32[val : val+1]
			chHex = chHex >> 5
		}
		result[i] = outchars
	}
	fmt.Println(result)
	// fmt.Println(hex.EncodeToString(hx))
	// fmt.Println(strings.ToUpper(base32))

}
<commit_msg>更新base32值，修改位与运算的值<commit_after>package main

/*
1)将长网址md5生成32位签名串,分为4段, 每段8个字节;
2)对这四段循环处理, 取8个字节, 将他看成16进制串与0x3fffffff(30位1)与操作, 即超过30位的忽略处理;
3)这30位分成6段, 每5位的数字作为字母表的索引取得特定字符, 依次进行获得6位字符串;
4)总的md5串可以获得4个6位串; 取里面的任意一个就可作为这个长url的短url地址;
这种算法,虽然会生成4个,但是仍然存在重复几率,下面的算法一和三,就是这种的实现.
*/
import (
	// "bytes"
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
	"strconv"
	// "strings"
)

const (
	signSalt = "Ak47"
	//	base32   = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	//base62 = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	base32 = "bcdefghjkmnpqrstuvwxyz0123456789"
)

func main() {
	url := "www.google.com"
	h := md5.New()
	io.WriteString(h, url+signSalt)
	hx := h.Sum(nil)
	var result [4]string
	s := hex.EncodeToString(hx)
	length := len(s) / 8
	for i := 0; i < length; i++ {
		subhex := s[i*8 : i*8+8]
		// fmt.Println(strconv.QuoteToASCII("0x" + subhex))
		//hexstr := strconv.QuoteToASCII("0x" + subhex)
		bit, _ := strconv.ParseInt(subhex, 16, 64)
		// fmt.Println(bit & 0x3FFFFFFF)
		var chHex = 0x3FFFFFFF & bit
		var outchars = ""
		for i := 0; i < 6; i++ {
			//base 62 3D base32 1F
			//比base值少2
			val := 0x0000001F & chHex
			outchars += base32[val : val+1]
			chHex = chHex >> 5
		}
		result[i] = outchars
	}
	fmt.Println(result)
	// fmt.Println(hex.EncodeToString(hx))
	// fmt.Println(strings.ToUpper(base32))

}
<|endoftext|>


<commit_before>// Copyright 2016 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package flags defines command-line flags to make them consistent between binaries.
// Not all flags make sense for all binaries.
package flags // import "upspin.io/flags"

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"upspin.io/config"
	"upspin.io/log"
)

// flagVar represents a flag in this package.
type flagVar struct {
	set  func()        // Set the value at parse time.
	arg  func() string // Return the argument to set the flag.
	arg2 func() string // Return the argument to set the second flag; usually nil.
}

const (
	defaultBlockSize  = 1024 * 1024 // Keep in sync with upspin.BlockSize.]
	defaultHTTPAddr   = ":80"
	defaultHTTPSAddr  = ":443"
	defaultLog        = "info"
	defaultServerKind = "inprocess"
)

var (
	defaultCacheDir         = upspinDir("")
	defaultLetsEncryptCache = upspinDir("letsencrypt")
	defaultConfig           = upspinDir("config")
)

func upspinDir(subdir string) string {
	home, err := config.Homedir()
	if err != nil {
		log.Error.Printf("flags: could not locate home directory: %v", err)
		home = "."
	}
	return filepath.Join(home, "upspin", subdir)
}

// None is the set of no flags. It is rarely needed as most programs
// use either the Server or Client set.
var None = []string{}

// Server is the set of flags most useful in servers. It can be passed as the
// argument to Parse to set up the package for a server.
var Server = []string{
	"config", "log", "http", "https", "letscache", "tls", "addr", "insecure",
}

// Client is the set of flags most useful in clients. It can be passed as the
// argument to Parse to set up the package for a client.
var Client = []string{
	"config", "log", "blocksize", "prudent",
}

// The Parse and Register functions bind these variables to their respective
// command-line flags.
var (
	// BlockSize ("blocksize") is the block size used when writing large files.
	// The default is 1MB.
	BlockSize = defaultBlockSize

	// CacheDir ("cachedir") specifies the directory for the various file
	// caches.
	CacheDir = defaultCacheDir

	// Config ("config") names the Upspin configuration file to use.
	Config = defaultConfig

	// HTTPAddr ("http") is the network address on which to listen for
	// incoming insecure network connections.
	HTTPAddr = defaultHTTPAddr

	// HTTPSAddr ("https") is the network address on which to listen for
	// incoming secure network connections.
	HTTPSAddr = defaultHTTPSAddr

	// InsecureHTTP ("insecure") specifies whether to serve insecure HTTP
	// on HTTPAddr, instead of serving HTTPS (secured by TLS) on HTTPSAddr.
	InsecureHTTP = false

	// LetsEncryptCache ("letscache") is the location of a file in which
	// the Let's Encrypt certificates are stored. The containing directory
	// should be owner-accessible only (chmod 0700).
	LetsEncryptCache = defaultLetsEncryptCache

	// Log ("log") sets the level of logging (implements flag.Value).
	Log logFlag

	// NetAddr ("addr") is the publicly accessible network address of this
	// server.
	NetAddr = ""

	// ServerConfig ("serverconfig") specifies configuration options for
	// servers in "key=value" pairs.
	ServerConfig []string

	// ServerKind ("kind") is the implementation kind of this server.
	ServerKind = defaultServerKind

	// Prudent ("prudent") sets an extra security mode in the client to
	// check for malicious or buggy servers, at possible cost in
	// performance or convenience. Specifically, one check is that the
	// writer listed in a directory entry is either the owner or a user
	// currently with write permission. This protects against a forged
	// directory entry at the cost of potentially blocking a legitimate
	// file written by a user who no longer has write permission.
	Prudent = false

	// TLSCertFile and TLSKeyFile ("tls") specify the location of a TLS
	// certificate/key pair used for serving TLS (HTTPS).
	TLSCertFile = ""
	TLSKeyFile  = ""
)

// flags is a map of flag registration functions keyed by flag name,
// used by Parse to register specific (or all) flags.
var flags = map[string]*flagVar{
	"addr": strVar(&NetAddr, "addr", NetAddr, "publicly accessible network address (`host:port`)"),
	"blocksize": &flagVar{
		set: func() {
			flag.IntVar(&BlockSize, "blocksize", BlockSize, "`size` of blocks when writing large files")
		},
		arg: func() string {
			if BlockSize == defaultBlockSize {
				return ""
			}
			return fmt.Sprintf("-blocksize=%d", BlockSize)
		},
	},
	"cachedir": strVar(&CacheDir, "cachedir", CacheDir, "`directory` containing all file caches"),
	"config":   strVar(&Config, "config", Config, "user's configuration `file`"),
	"http":     strVar(&HTTPAddr, "http", HTTPAddr, "`address` for incoming insecure network connections"),
	"https":    strVar(&HTTPSAddr, "https", HTTPSAddr, "`address` for incoming secure network connections"),
	"insecure": &flagVar{
		set: func() {
			flag.BoolVar(&InsecureHTTP, "insecure", false, "whether to serve insecure HTTP instead of HTTPS")
		},
		arg: func() string {
			if InsecureHTTP {
				return "-insecure"
			}
			return ""
		},
	},
	"kind":      strVar(&ServerKind, "kind", ServerKind, "server implementation `kind` (inprocess, gcp)"),
	"letscache": strVar(&LetsEncryptCache, "letscache", defaultLetsEncryptCache, "Let's Encrypt cache `directory`"),
	"log": &flagVar{
		set: func() {
			Log.Set("info")
			flag.Var(&Log, "log", "`level` of logging: debug, info, error, disabled")
		},
		arg: func() string { return strArg("log", Log.String(), defaultLog) },
	},
	"serverconfig": &flagVar{
		set: func() {
			flag.Var(configFlag{&ServerConfig}, "serverconfig", "comma-separated list of configuration options (key=value) for this server")
		},
		arg: func() string { return strArg("serverconfig", configFlag{&ServerConfig}.String(), "") },
	},
	"prudent": &flagVar{
		set: func() {
			flag.BoolVar(&Prudent, "prudent", false, "protect against malicious directory server")
		},
		arg: func() string {
			if !Prudent {
				return ""
			}
			return "-prudent"
		},
	},
	"tls": &flagVar{
		set: func() {
			flag.StringVar(&TLSCertFile, "tls_cert", "", "TLS Certificate `file` in PEM format")
			flag.StringVar(&TLSKeyFile, "tls_key", "", "TLS Key `file` in PEM format")
		},
		arg:  func() string { return strArg("tls_cert", TLSCertFile, "") },
		arg2: func() string { return strArg("tls_key", TLSKeyFile, "") },
	},
}

// Parse registers the command-line flags for the given default flags list, plus
// any extra flag names, and calls flag.Parse. Passing no flag names in either
// list registers all flags. Passing an unknown name triggers a panic.
// The Server and Client variables contain useful default sets.
//
// Examples:
// 	flags.Parse(flags.Client) // Register all client flags.
//	flags.Parse(flags.Server, "cachedir") // Register all server flags plus cachedir.
// 	flags.Parse(nil) // Register all flags.
// 	flags.Parse(flags.None, "config", "endpoint") // Register only config and endpoint.
func Parse(defaultList []string, extras ...string) {
	ParseArgs(os.Args[1:], defaultList, extras...)
}

// ParseArgs is the same as Parse but uses the provided argument list
// instead of those provided on the command line. For ParseArgs, the
// initial command name should not be provided.
func ParseArgs(args, defaultList []string, extras ...string) {
	if len(defaultList) == 0 && len(extras) == 0 {
		Register()
	} else {
		if len(defaultList) > 0 {
			Register(defaultList...)
		}
		if len(extras) > 0 {
			Register(extras...)
		}
	}
	flag.CommandLine.Parse(args)
}

// Register registers the command-line flags for the given flag names.
// Unlike Parse, it may be called multiple times.
// Passing zero names install all flags.
// Passing an unknown name triggers a panic.
//
// For example:
// 	flags.Register("config", "endpoint") // Register Config and Endpoint.
// or
// 	flags.Register() // Register all flags.
func Register(names ...string) {
	if len(names) == 0 {
		// Register all flags if no names provided.
		for _, flag := range flags {
			flag.set()
		}
	} else {
		for _, n := range names {
			flag, ok := flags[n]
			if !ok {
				panic(fmt.Sprintf("unknown flag %q", n))
			}
			flag.set()
		}
	}
}

// Args returns a slice of -flag=value strings that will recreate
// the state of the flags. Flags set to their default value are elided.
func Args() []string {
	var args []string
	for _, flag := range flags {
		arg := flag.arg()
		if arg == "" {
			continue
		}
		args = append(args, arg)
		if flag.arg2 != nil {
			args = append(args, flag.arg2())
		}
	}
	return args
}

// strVar returns a flagVar for the given string flag.
func strVar(value *string, name, _default, usage string) *flagVar {
	return &flagVar{
		set: func() {
			flag.StringVar(value, name, _default, usage)
		},
		arg: func() string {
			return strArg(name, *value, _default)
		},
	}
}

// strArg returns a command-line argument that will recreate the flag,
// or the empty string if the value is the default.
func strArg(name, value, _default string) string {
	if value == _default {
		return ""
	}
	return "-" + name + "=" + value
}

type logFlag string

// String implements flag.Value.
func (f logFlag) String() string {
	return string(f)
}

// Set implements flag.Value.
func (f *logFlag) Set(level string) error {
	err := log.SetLevel(level)
	if err != nil {
		return err
	}
	*f = logFlag(log.GetLevel())
	return nil
}

// Get implements flag.Getter.
func (logFlag) Get() interface{} {
	return log.GetLevel()
}

type configFlag struct {
	s *[]string
}

// String implements flag.Value.
func (f configFlag) String() string {
	if f.s == nil {
		return ""
	}
	return strings.Join(*f.s, ",")
}

// Set implements flag.Value.
func (f configFlag) Set(s string) error {
	ss := strings.Split(strings.TrimSpace(s), ",")
	// Drop empty elements.
	for i := 0; i < len(ss); i++ {
		if ss[i] == "" {
			ss = append(ss[:i], ss[i+1:]...)
		}
	}
	*f.s = ss
	return nil
}

// Get implements flag.Getter.
func (f configFlag) Get() interface{} {
	if f.s == nil {
		return ""
	}
	return *f.s
}
<commit_msg>flags: allow flags to be set in a separate FlagSet<commit_after>// Copyright 2016 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package flags defines command-line flags to make them consistent between binaries.
// Not all flags make sense for all binaries.
package flags // import "upspin.io/flags"

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"upspin.io/config"
	"upspin.io/log"
)

// flagVar represents a flag in this package.
type flagVar struct {
	set  func(fs *flag.FlagSet) // Set the value at parse time.
	arg  func() string          // Return the argument to set the flag.
	arg2 func() string          // Return the argument to set the second flag; usually nil.
}

const (
	defaultBlockSize  = 1024 * 1024 // Keep in sync with upspin.BlockSize.]
	defaultHTTPAddr   = ":80"
	defaultHTTPSAddr  = ":443"
	defaultLog        = "info"
	defaultServerKind = "inprocess"
)

var (
	defaultCacheDir         = upspinDir("")
	defaultLetsEncryptCache = upspinDir("letsencrypt")
	defaultConfig           = upspinDir("config")
)

func upspinDir(subdir string) string {
	home, err := config.Homedir()
	if err != nil {
		log.Error.Printf("flags: could not locate home directory: %v", err)
		home = "."
	}
	return filepath.Join(home, "upspin", subdir)
}

// None is the set of no flags. It is rarely needed as most programs
// use either the Server or Client set.
var None = []string{}

// Server is the set of flags most useful in servers. It can be passed as the
// argument to Parse to set up the package for a server.
var Server = []string{
	"config", "log", "http", "https", "letscache", "tls", "addr", "insecure",
}

// Client is the set of flags most useful in clients. It can be passed as the
// argument to Parse to set up the package for a client.
var Client = []string{
	"config", "log", "blocksize", "prudent",
}

// The Parse and Register functions bind these variables to their respective
// command-line flags.
var (
	// BlockSize ("blocksize") is the block size used when writing large files.
	// The default is 1MB.
	BlockSize = defaultBlockSize

	// CacheDir ("cachedir") specifies the directory for the various file
	// caches.
	CacheDir = defaultCacheDir

	// Config ("config") names the Upspin configuration file to use.
	Config = defaultConfig

	// HTTPAddr ("http") is the network address on which to listen for
	// incoming insecure network connections.
	HTTPAddr = defaultHTTPAddr

	// HTTPSAddr ("https") is the network address on which to listen for
	// incoming secure network connections.
	HTTPSAddr = defaultHTTPSAddr

	// InsecureHTTP ("insecure") specifies whether to serve insecure HTTP
	// on HTTPAddr, instead of serving HTTPS (secured by TLS) on HTTPSAddr.
	InsecureHTTP = false

	// LetsEncryptCache ("letscache") is the location of a file in which
	// the Let's Encrypt certificates are stored. The containing directory
	// should be owner-accessible only (chmod 0700).
	LetsEncryptCache = defaultLetsEncryptCache

	// Log ("log") sets the level of logging (implements flag.Value).
	Log logFlag

	// NetAddr ("addr") is the publicly accessible network address of this
	// server.
	NetAddr = ""

	// ServerConfig ("serverconfig") specifies configuration options for
	// servers in "key=value" pairs.
	ServerConfig []string

	// ServerKind ("kind") is the implementation kind of this server.
	ServerKind = defaultServerKind

	// Prudent ("prudent") sets an extra security mode in the client to
	// check for malicious or buggy servers, at possible cost in
	// performance or convenience. Specifically, one check is that the
	// writer listed in a directory entry is either the owner or a user
	// currently with write permission. This protects against a forged
	// directory entry at the cost of potentially blocking a legitimate
	// file written by a user who no longer has write permission.
	Prudent = false

	// TLSCertFile and TLSKeyFile ("tls") specify the location of a TLS
	// certificate/key pair used for serving TLS (HTTPS).
	TLSCertFile = ""
	TLSKeyFile  = ""
)

// flags is a map of flag registration functions keyed by flag name,
// used by Parse to register specific (or all) flags.
var flags = map[string]*flagVar{
	"addr": strVar(&NetAddr, "addr", NetAddr, "publicly accessible network address (`host:port`)"),
	"blocksize": &flagVar{
		set: func(fs *flag.FlagSet) {
			fs.IntVar(&BlockSize, "blocksize", BlockSize, "`size` of blocks when writing large files")
		},
		arg: func() string {
			if BlockSize == defaultBlockSize {
				return ""
			}
			return fmt.Sprintf("-blocksize=%d", BlockSize)
		},
	},
	"cachedir": strVar(&CacheDir, "cachedir", CacheDir, "`directory` containing all file caches"),
	"config":   strVar(&Config, "config", Config, "user's configuration `file`"),
	"http":     strVar(&HTTPAddr, "http", HTTPAddr, "`address` for incoming insecure network connections"),
	"https":    strVar(&HTTPSAddr, "https", HTTPSAddr, "`address` for incoming secure network connections"),
	"insecure": &flagVar{
		set: func(fs *flag.FlagSet) {
			fs.BoolVar(&InsecureHTTP, "insecure", false, "whether to serve insecure HTTP instead of HTTPS")
		},
		arg: func() string {
			if InsecureHTTP {
				return "-insecure"
			}
			return ""
		},
	},
	"kind":      strVar(&ServerKind, "kind", ServerKind, "server implementation `kind` (inprocess, gcp)"),
	"letscache": strVar(&LetsEncryptCache, "letscache", defaultLetsEncryptCache, "Let's Encrypt cache `directory`"),
	"log": &flagVar{
		set: func(fs *flag.FlagSet) {
			Log.Set("info")
			fs.Var(&Log, "log", "`level` of logging: debug, info, error, disabled")
		},
		arg: func() string { return strArg("log", Log.String(), defaultLog) },
	},
	"serverconfig": &flagVar{
		set: func(fs *flag.FlagSet) {
			fs.Var(configFlag{&ServerConfig}, "serverconfig", "comma-separated list of configuration options (key=value) for this server")
		},
		arg: func() string { return strArg("serverconfig", configFlag{&ServerConfig}.String(), "") },
	},
	"prudent": &flagVar{
		set: func(fs *flag.FlagSet) {
			fs.BoolVar(&Prudent, "prudent", false, "protect against malicious directory server")
		},
		arg: func() string {
			if !Prudent {
				return ""
			}
			return "-prudent"
		},
	},
	"tls": &flagVar{
		set: func(fs *flag.FlagSet) {
			fs.StringVar(&TLSCertFile, "tls_cert", "", "TLS Certificate `file` in PEM format")
			fs.StringVar(&TLSKeyFile, "tls_key", "", "TLS Key `file` in PEM format")
		},
		arg:  func() string { return strArg("tls_cert", TLSCertFile, "") },
		arg2: func() string { return strArg("tls_key", TLSKeyFile, "") },
	},
}

// Parse registers the command-line flags for the given default flags list, plus
// any extra flag names, and calls flag.Parse. Passing no flag names in either
// list registers all flags. Passing an unknown name triggers a panic.
// The Server and Client variables contain useful default sets.
//
// Examples:
// 	flags.Parse(flags.Client) // Register all client flags.
//	flags.Parse(flags.Server, "cachedir") // Register all server flags plus cachedir.
// 	flags.Parse(nil) // Register all flags.
// 	flags.Parse(flags.None, "config", "endpoint") // Register only config and endpoint.
func Parse(defaultList []string, extras ...string) {
	ParseArgsInto(flag.CommandLine, os.Args[1:], defaultList, extras...)
}

// ParseInto is the same as Parse but accepts a FlagSet argument instead of
// using the default flag.CommandLine FlagSet.
func ParseInto(fs *flag.FlagSet, defaultList []string, extras ...string) {
	ParseArgsInto(fs, os.Args[1:], defaultList, extras...)
}

// ParseArgs is the same as Parse but uses the provided argument list
// instead of those provided on the command line. For ParseArgs, the
// initial command name should not be provided.
func ParseArgs(args, defaultList []string, extras ...string) {
	ParseArgsInto(flag.CommandLine, args, defaultList, extras...)
}

// ParseArgsInto is the same as ParseArgs but accepts a FlagSet argument instead of
// using the default flag.CommandLine FlagSet.
func ParseArgsInto(fs *flag.FlagSet, args, defaultList []string, extras ...string) {
	if len(defaultList) == 0 && len(extras) == 0 {
		RegisterInto(fs)
	} else {
		if len(defaultList) > 0 {
			RegisterInto(fs, defaultList...)
		}
		if len(extras) > 0 {
			RegisterInto(fs, extras...)
		}
	}
	fs.Parse(args)
}

// Register registers the command-line flags for the given flag names.
// Unlike Parse, it may be called multiple times.
// Passing zero names install all flags.
// Passing an unknown name triggers a panic.
//
// For example:
// 	flags.Register("config", "endpoint") // Register Config and Endpoint.
// or
// 	flags.Register() // Register all flags.
func Register(names ...string) {
	RegisterInto(flag.CommandLine, names...)
}

// RegisterInto  is the same as Register but accepts a FlagSet argument instead of
// using the default flag.CommandLine FlagSet.
func RegisterInto(fs *flag.FlagSet, names ...string) {
	if len(names) == 0 {
		// Register all flags if no names provided.
		for _, f := range flags {
			f.set(fs)
		}
	} else {
		for _, n := range names {
			f, ok := flags[n]
			if !ok {
				panic(fmt.Sprintf("unknown flag %q", n))
			}
			f.set(fs)
		}
	}
}

// Args returns a slice of -flag=value strings that will recreate
// the state of the flags. Flags set to their default value are elided.
func Args() []string {
	var args []string
	for _, f := range flags {
		arg := f.arg()
		if arg == "" {
			continue
		}
		args = append(args, arg)
		if f.arg2 != nil {
			args = append(args, f.arg2())
		}
	}
	return args
}

// strVar returns a flagVar for the given string flag.
func strVar(value *string, name, _default, usage string) *flagVar {
	return &flagVar{
		set: func(fs *flag.FlagSet) {
			fs.StringVar(value, name, _default, usage)
		},
		arg: func() string {
			return strArg(name, *value, _default)
		},
	}
}

// strArg returns a command-line argument that will recreate the flag,
// or the empty string if the value is the default.
func strArg(name, value, _default string) string {
	if value == _default {
		return ""
	}
	return "-" + name + "=" + value
}

type logFlag string

// String implements flag.Value.
func (f logFlag) String() string {
	return string(f)
}

// Set implements flag.Value.
func (f *logFlag) Set(level string) error {
	err := log.SetLevel(level)
	if err != nil {
		return err
	}
	*f = logFlag(log.GetLevel())
	return nil
}

// Get implements flag.Getter.
func (logFlag) Get() interface{} {
	return log.GetLevel()
}

type configFlag struct {
	s *[]string
}

// String implements flag.Value.
func (f configFlag) String() string {
	if f.s == nil {
		return ""
	}
	return strings.Join(*f.s, ",")
}

// Set implements flag.Value.
func (f configFlag) Set(s string) error {
	ss := strings.Split(strings.TrimSpace(s), ",")
	// Drop empty elements.
	for i := 0; i < len(ss); i++ {
		if ss[i] == "" {
			ss = append(ss[:i], ss[i+1:]...)
		}
	}
	*f.s = ss
	return nil
}

// Get implements flag.Getter.
func (f configFlag) Get() interface{} {
	if f.s == nil {
		return ""
	}
	return *f.s
}
<|endoftext|>


<commit_before>package upstream

import (
	"context"
	"log"
	"math/rand"
	"net"
	"net/url"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/pkg/errors"
	"go.uber.org/zap"
)

// Upstream struct
type Upstream struct {
	scheme string
	port   string
	host   string
	ipwcs  []*IPwc
	csum   string
	v      uint64
	logger *zap.Logger
	mu     *sync.RWMutex
}

// IPwc : IP with counter
type IPwc struct {
	ip string
	c  int64
	v  uint64
}

// New :
func New(upstream string, logger *zap.Logger) (*Upstream, error) {
	var h string
	var p string
	var err error
	u := new(url.URL)

	if upstream != "" {
		u, err = url.Parse(upstream)
		if err != nil {
			return nil, errors.Wrap(err, "upsteam url is invalid")
		}
		if u.Scheme != "http" && u.Scheme != "https" {
			return nil, errors.New("upsteam url is invalid: upsteam url scheme should be http or https")
		}
		if u.Host == "" {
			return nil, errors.New("upsteam url is invalid: no hostname")
		}

		hostPortSplit := strings.Split(u.Host, ":")
		h = hostPortSplit[0]
		p = ""
		if len(hostPortSplit) > 1 {
			p = hostPortSplit[1]
		}
	}

	um := &Upstream{
		scheme: u.Scheme,
		host:   h,
		port:   p,
		v:      0,
		logger: logger,
		mu:     new(sync.RWMutex),
	}

	if um.Enabled() {
		ctx := context.Background()
		ipwcs, err := um.RefreshIP(ctx)
		if err != nil {
			return nil, errors.Wrap(err, "failed initial resolv hostname")
		}
		if len(ipwcs) < 1 {
			return nil, errors.New("Could not resolv hostname")
		}
		go um.Run(ctx)
	}
	return um, nil
}

// Enabled : upstream is enabled
func (u *Upstream) Enabled() bool {
	return u.scheme != ""
}

// GetScheme : get upstream's scheme
func (u *Upstream) GetScheme() string {
	return u.scheme
}

// RefreshIP : resolve hostname
func (u *Upstream) RefreshIP(ctx context.Context) ([]*IPwc, error) {
	u.v++
	ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
	addrs, err := net.DefaultResolver.LookupIPAddr(ctx, u.host)
	cancel()
	if err != nil {
		return nil, err
	}

	sort.Slice(addrs, func(i, j int) bool {
		return addrs[i].IP.String() > addrs[j].IP.String()
	})

	ips := make([]string, len(addrs))
	ipwcs := make([]*IPwc, len(addrs))
	for i, ia := range addrs {
		ips[i] = ia.IP.String()
		ipwcs[i] = &IPwc{
			ip: ia.IP.String(),
			v:  u.v,
			c:  0,
		}
	}
	csum := strings.Join(ips, ",")
	u.mu.Lock()
	defer u.mu.Unlock()
	if csum != u.csum {
		u.csum = csum
		u.ipwcs = ipwcs
	}

	return ipwcs, nil
}

// Run : resolv hostname in background
func (u *Upstream) Run(ctx context.Context) {
	ticker := time.NewTicker(3 * time.Second)
	defer ticker.Stop()
	for {
		select {
		case <-ctx.Done():
			return
		case _ = <-ticker.C:
			for _, ipwc := range u.ipwcs {
				log.Printf("%v", ipwc)
			}
			_, err := u.RefreshIP(ctx)
			if err != nil {
				u.logger.Error("failed refresh ip", zap.Error(err))
			}
		}
	}
}

// Get : wild
func (u *Upstream) Get() (string, *IPwc, error) {
	u.mu.Lock()
	defer u.mu.Unlock()

	sort.Slice(u.ipwcs, func(i, j int) bool {
		if u.ipwcs[i].c == u.ipwcs[j].c {
			return rand.Intn(1) == 0
		}
		return u.ipwcs[i].c < u.ipwcs[j].c
	})

	u.ipwcs[0].c++
	h := u.ipwcs[0].ip
	if u.port != "" {
		h = h + ":" + u.port
	}
	ipwc := &IPwc{
		ip: u.ipwcs[0].ip,
		v:  u.ipwcs[0].v,
		c:  0,
	}
	return h, ipwc, nil
}

// Release : decrement counter
func (u *Upstream) Release(o *IPwc) {
	u.mu.Lock()
	defer u.mu.Unlock()
	for i, ipwc := range u.ipwcs {
		if ipwc.ip == o.ip && ipwc.v == o.v {
			u.ipwcs[i].c = u.ipwcs[i].c - 1
		}
	}
}
<commit_msg>error when no upstream hosts<commit_after>package upstream

import (
	"context"
	"log"
	"math/rand"
	"net"
	"net/url"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/pkg/errors"
	"go.uber.org/zap"
)

// Upstream struct
type Upstream struct {
	scheme string
	port   string
	host   string
	ipwcs  []*IPwc
	csum   string
	v      uint64
	logger *zap.Logger
	mu     *sync.RWMutex
}

// IPwc : IP with counter
type IPwc struct {
	ip string
	c  int64
	v  uint64
}

// New :
func New(upstream string, logger *zap.Logger) (*Upstream, error) {
	var h string
	var p string
	var err error
	u := new(url.URL)

	if upstream != "" {
		u, err = url.Parse(upstream)
		if err != nil {
			return nil, errors.Wrap(err, "upsteam url is invalid")
		}
		if u.Scheme != "http" && u.Scheme != "https" {
			return nil, errors.New("upsteam url is invalid: upsteam url scheme should be http or https")
		}
		if u.Host == "" {
			return nil, errors.New("upsteam url is invalid: no hostname")
		}

		hostPortSplit := strings.Split(u.Host, ":")
		h = hostPortSplit[0]
		p = ""
		if len(hostPortSplit) > 1 {
			p = hostPortSplit[1]
		}
	}

	um := &Upstream{
		scheme: u.Scheme,
		host:   h,
		port:   p,
		v:      0,
		logger: logger,
		mu:     new(sync.RWMutex),
	}

	if um.Enabled() {
		ctx := context.Background()
		ipwcs, err := um.RefreshIP(ctx)
		if err != nil {
			return nil, errors.Wrap(err, "failed initial resolv hostname")
		}
		if len(ipwcs) < 1 {
			return nil, errors.New("Could not resolv hostname")
		}
		go um.Run(ctx)
	}
	return um, nil
}

// Enabled : upstream is enabled
func (u *Upstream) Enabled() bool {
	return u.scheme != ""
}

// GetScheme : get upstream's scheme
func (u *Upstream) GetScheme() string {
	return u.scheme
}

// RefreshIP : resolve hostname
func (u *Upstream) RefreshIP(ctx context.Context) ([]*IPwc, error) {
	u.v++
	ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
	addrs, err := net.DefaultResolver.LookupIPAddr(ctx, u.host)
	cancel()
	if err != nil {
		return nil, err
	}

	sort.Slice(addrs, func(i, j int) bool {
		return addrs[i].IP.String() > addrs[j].IP.String()
	})

	ips := make([]string, len(addrs))
	ipwcs := make([]*IPwc, len(addrs))
	for i, ia := range addrs {
		ips[i] = ia.IP.String()
		ipwcs[i] = &IPwc{
			ip: ia.IP.String(),
			v:  u.v,
			c:  0,
		}
	}
	csum := strings.Join(ips, ",")
	u.mu.Lock()
	defer u.mu.Unlock()
	if csum != u.csum {
		u.csum = csum
		u.ipwcs = ipwcs
	}

	return ipwcs, nil
}

// Run : resolv hostname in background
func (u *Upstream) Run(ctx context.Context) {
	ticker := time.NewTicker(3 * time.Second)
	defer ticker.Stop()
	for {
		select {
		case <-ctx.Done():
			return
		case _ = <-ticker.C:
			for _, ipwc := range u.ipwcs {
				log.Printf("%v", ipwc)
			}
			_, err := u.RefreshIP(ctx)
			if err != nil {
				u.logger.Error("failed refresh ip", zap.Error(err))
			}
		}
	}
}

// Get : wild
func (u *Upstream) Get() (string, *IPwc, error) {
	u.mu.Lock()
	defer u.mu.Unlock()

	if len(u.ipwcs) < 1 {
		return "", &IPwc{}, errors.New("No upstream hosts")
	}

	sort.Slice(u.ipwcs, func(i, j int) bool {
		if u.ipwcs[i].c == u.ipwcs[j].c {
			return rand.Intn(1) == 0
		}
		return u.ipwcs[i].c < u.ipwcs[j].c
	})

	u.ipwcs[0].c++
	h := u.ipwcs[0].ip
	if u.port != "" {
		h = h + ":" + u.port
	}
	ipwc := &IPwc{
		ip: u.ipwcs[0].ip,
		v:  u.ipwcs[0].v,
		c:  0,
	}
	return h, ipwc, nil
}

// Release : decrement counter
func (u *Upstream) Release(o *IPwc) {
	u.mu.Lock()
	defer u.mu.Unlock()
	for i, ipwc := range u.ipwcs {
		if ipwc.ip == o.ip && ipwc.v == o.v {
			u.ipwcs[i].c = u.ipwcs[i].c - 1
		}
	}
}
<|endoftext|>