<commit_before>// Copyright 2015 The Cockroach 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. See the AUTHORS file
// for names of contributors.
//
// Author: Tobias Schottdorf (tobias.schottdorf@gmail.com)

package kv

import (
	"sync"

	"github.com/cockroachdb/cockroach/proto"
	"github.com/cockroachdb/cockroach/util/cache"
)

// A leaderCache is a cache used to keep track of the leader
// replica of Raft consensus groups.
type leaderCache struct {
	mu    sync.RWMutex
	cache *cache.UnorderedCache
}

// newLeaderCache creates a new leaderCache of the given size.
// The underlying cache internally uses a hash map, so lookups
// are cheap.
func newLeaderCache(size int) *leaderCache {
	return &leaderCache{
		cache: cache.NewUnorderedCache(cache.Config{
			Policy: cache.CacheLRU,
			ShouldEvict: func(s int, key, value interface{}) bool {
				return s > size
			},
		}),
	}
}

// Lookup consults the cache for the replica cached as the leader of
// the given Raft consensus group.
func (lc *leaderCache) Lookup(group proto.RaftID) *proto.Replica {
	lc.mu.Lock()
	defer lc.mu.Unlock()
	v, ok := lc.cache.Get(group)
	if !ok {
		return nil
	}
	return v.(*proto.Replica)
}

// Update invalidates the cached leader for the given Raft group.
// If a replica is passed in, it is inserted into the cache.
// A StoreID of 0 (empty replica) means evict.
func (lc *leaderCache) Update(group proto.RaftID, r proto.Replica) {
	lc.mu.Lock()
	defer lc.mu.Unlock()
	lc.cache.Del(group)
	if r.StoreID != 0 {
		lc.cache.Add(group, &r)
	}
}
<commit_msg>changed RWMutex to Mutex<commit_after>// Copyright 2015 The Cockroach 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. See the AUTHORS file
// for names of contributors.
//
// Author: Tobias Schottdorf (tobias.schottdorf@gmail.com)

package kv

import (
	"sync"

	"github.com/cockroachdb/cockroach/proto"
	"github.com/cockroachdb/cockroach/util/cache"
)

// A leaderCache is a cache used to keep track of the leader
// replica of Raft consensus groups.
type leaderCache struct {
	mu    sync.Mutex
	cache *cache.UnorderedCache
}

// newLeaderCache creates a new leaderCache of the given size.
// The underlying cache internally uses a hash map, so lookups
// are cheap.
func newLeaderCache(size int) *leaderCache {
	return &leaderCache{
		cache: cache.NewUnorderedCache(cache.Config{
			Policy: cache.CacheLRU,
			ShouldEvict: func(s int, key, value interface{}) bool {
				return s > size
			},
		}),
	}
}

// Lookup consults the cache for the replica cached as the leader of
// the given Raft consensus group.
func (lc *leaderCache) Lookup(group proto.RaftID) *proto.Replica {
	lc.mu.Lock()
	defer lc.mu.Unlock()
	v, ok := lc.cache.Get(group)
	if !ok {
		return nil
	}
	return v.(*proto.Replica)
}

// Update invalidates the cached leader for the given Raft group.
// If a replica is passed in, it is inserted into the cache.
// A StoreID of 0 (empty replica) means evict.
func (lc *leaderCache) Update(group proto.RaftID, r proto.Replica) {
	lc.mu.Lock()
	defer lc.mu.Unlock()
	lc.cache.Del(group)
	if r.StoreID != 0 {
		lc.cache.Add(group, &r)
	}
}
<|endoftext|>


<commit_before>package message

import "encoding/xml"

// MsgType 基本消息类型
type MsgType string

// EventType 事件类型
type EventType string

// InfoType 第三方平台授权事件类型
type InfoType string

const (
	//MsgTypeText 表示文本消息
	MsgTypeText MsgType = "text"
	//MsgTypeImage 表示图片消息
	MsgTypeImage = "image"
	//MsgTypeVoice 表示语音消息
	MsgTypeVoice = "voice"
	//MsgTypeVideo 表示视频消息
	MsgTypeVideo = "video"
	//MsgTypeShortVideo 表示短视频消息[限接收]
	MsgTypeShortVideo = "shortvideo"
	//MsgTypeLocation 表示坐标消息[限接收]
	MsgTypeLocation = "location"
	//MsgTypeLink 表示链接消息[限接收]
	MsgTypeLink = "link"
	//MsgTypeMusic 表示音乐消息[限回复]
	MsgTypeMusic = "music"
	//MsgTypeNews 表示图文消息[限回复]
	MsgTypeNews = "news"
	//MsgTypeTransfer 表示消息消息转发到客服
	MsgTypeTransfer = "transfer_customer_service"
	//MsgTypeEvent 表示事件推送消息
	MsgTypeEvent = "event"
)

const (
	//EventSubscribe 订阅
	EventSubscribe EventType = "subscribe"
	//EventUnsubscribe 取消订阅
	EventUnsubscribe = "unsubscribe"
	//EventScan 用户已经关注公众号，则微信会将带场景值扫描事件推送给开发者
	EventScan = "SCAN"
	//EventLocation 上报地理位置事件
	EventLocation = "LOCATION"
	//EventClick 点击菜单拉取消息时的事件推送
	EventClick = "CLICK"
	//EventView 点击菜单跳转链接时的事件推送
	EventView = "VIEW"
	//EventScancodePush 扫码推事件的事件推送
	EventScancodePush = "scancode_push"
	//EventScancodeWaitmsg 扫码推事件且弹出“消息接收中”提示框的事件推送
	EventScancodeWaitmsg = "scancode_waitmsg"
	//EventPicSysphoto 弹出系统拍照发图的事件推送
	EventPicSysphoto = "pic_sysphoto"
	//EventPicPhotoOrAlbum 弹出拍照或者相册发图的事件推送
	EventPicPhotoOrAlbum = "pic_photo_or_album"
	//EventPicWeixin 弹出微信相册发图器的事件推送
	EventPicWeixin = "pic_weixin"
	//EventLocationSelect 弹出地理位置选择器的事件推送
	EventLocationSelect = "location_select"
	//EventTemplateSendJobFinish 发送模板消息推送通知
	EventTemplateSendJobFinish = "TEMPLATESENDJOBFINISH"
	//EventWxaMediaCheck 异步校验图片/音频是否含有违法违规内容推送事件
	EventWxaMediaCheck = "wxa_media_check"
)

const (
	// InfoTypeVerifyTicket 返回ticket
	InfoTypeVerifyTicket InfoType = "component_verify_ticket"
	// InfoTypeAuthorized 授权
	InfoTypeAuthorized = "authorized"
	// InfoTypeUnauthorized 取消授权
	InfoTypeUnauthorized = "unauthorized"
	// InfoTypeUpdateAuthorized 更新授权
	InfoTypeUpdateAuthorized = "updateauthorized"
)

//MixMessage 存放所有微信发送过来的消息和事件
type MixMessage struct {
	CommonToken

	//基本消息
	MsgID        int64   `xml:"MsgId"`
	Content      string  `xml:"Content"`
	Recognition  string  `xml:"Recognition"`
	PicURL       string  `xml:"PicUrl"`
	MediaID      string  `xml:"MediaId"`
	Format       string  `xml:"Format"`
	ThumbMediaID string  `xml:"ThumbMediaId"`
	LocationX    float64 `xml:"Location_X"`
	LocationY    float64 `xml:"Location_Y"`
	Scale        float64 `xml:"Scale"`
	Label        string  `xml:"Label"`
	Title        string  `xml:"Title"`
	Description  string  `xml:"Description"`
	URL          string  `xml:"Url"`

	//事件相关
	Event       EventType `xml:"Event"`
	EventKey    string    `xml:"EventKey"`
	Ticket      string    `xml:"Ticket"`
	Latitude    string    `xml:"Latitude"`
	Longitude   string    `xml:"Longitude"`
	Precision   string    `xml:"Precision"`
	MenuID      string    `xml:"MenuId"`
	Status      string    `xml:"Status"`
	SessionFrom string    `xml:"SessionFrom"`

	ScanCodeInfo struct {
		ScanType   string `xml:"ScanType"`
		ScanResult string `xml:"ScanResult"`
	} `xml:"ScanCodeInfo"`

	SendPicsInfo struct {
		Count   int32      `xml:"Count"`
		PicList []EventPic `xml:"PicList>item"`
	} `xml:"SendPicsInfo"`

	SendLocationInfo struct {
		LocationX float64 `xml:"Location_X"`
		LocationY float64 `xml:"Location_Y"`
		Scale     float64 `xml:"Scale"`
		Label     string  `xml:"Label"`
		Poiname   string  `xml:"Poiname"`
	}

	// 第三方平台相关
	InfoType                     InfoType `xml:"InfoType"`
	AppID                        string   `xml:"AppId"`
	ComponentVerifyTicket        string   `xml:"ComponentVerifyTicket"`
	AuthorizerAppid              string   `xml:"AuthorizerAppid"`
	AuthorizationCode            string   `xml:"AuthorizationCode"`
	AuthorizationCodeExpiredTime int64    `xml:"AuthorizationCodeExpiredTime"`
	PreAuthCode                  string   `xml:"PreAuthCode"`

	// 卡券相关
	CardID              string `xml:"CardId"`
	RefuseReason        string `xml:"RefuseReason"`
	IsGiveByFriend      int32  `xml:"IsGiveByFriend"`
	FriendUserName      string `xml:"FriendUserName"`
	UserCardCode        string `xml:"UserCardCode"`
	OldUserCardCode     string `xml:"OldUserCardCode"`
	OuterStr            string `xml:"OuterStr"`
	IsRestoreMemberCard int32  `xml:"IsRestoreMemberCard"`
	UnionID             string `xml:"UnionId"`

	// 内容审核相关
	IsRisky       bool   `xml:"isrisky"`
	ExtraInfoJSON string `xml:"extra_info_json"`
	TraceID       string `xml:"trace_id"`
	StatusCode    int    `xml:"status_code"`
}

//EventPic 发图事件推送
type EventPic struct {
	PicMd5Sum string `xml:"PicMd5Sum"`
}

//EncryptedXMLMsg 安全模式下的消息体
type EncryptedXMLMsg struct {
	XMLName      struct{} `xml:"xml" json:"-"`
	ToUserName   string   `xml:"ToUserName" json:"ToUserName"`
	EncryptedMsg string   `xml:"Encrypt"    json:"Encrypt"`
}

//ResponseEncryptedXMLMsg 需要返回的消息体
type ResponseEncryptedXMLMsg struct {
	XMLName      struct{} `xml:"xml" json:"-"`
	EncryptedMsg string   `xml:"Encrypt"      json:"Encrypt"`
	MsgSignature string   `xml:"MsgSignature" json:"MsgSignature"`
	Timestamp    int64    `xml:"TimeStamp"    json:"TimeStamp"`
	Nonce        string   `xml:"Nonce"        json:"Nonce"`
}

// CommonToken 消息中通用的结构
type CommonToken struct {
	XMLName      xml.Name `xml:"xml"`
	ToUserName   CDATA    `xml:"ToUserName"`
	FromUserName CDATA    `xml:"FromUserName"`
	CreateTime   int64    `xml:"CreateTime"`
	MsgType      MsgType  `xml:"MsgType"`
}

// CDATA  使用该类型,在序列化为 xml 文本时文本会被解析器忽略
type CDATA string

func (c CDATA) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	return e.EncodeElement(struct {
		string `xml:",cdata"`
	}{string(c)}, start)
}

//SetToUserName set ToUserName
func (msg *CommonToken) SetToUserName(toUserName string) {
	msg.ToUserName = CDATA(toUserName)
}

//SetFromUserName set FromUserName
func (msg *CommonToken) SetFromUserName(fromUserName string) {
	msg.FromUserName = CDATA(fromUserName)
}

//SetCreateTime set createTime
func (msg *CommonToken) SetCreateTime(createTime int64) {
	msg.CreateTime = createTime
}

//SetMsgType set MsgType
func (msg *CommonToken) SetMsgType(msgType MsgType) {
	msg.MsgType = msgType
}
<commit_msg>fix: 添加 CDATA.MarshalXML 注释<commit_after>package message

import "encoding/xml"

// MsgType 基本消息类型
type MsgType string

// EventType 事件类型
type EventType string

// InfoType 第三方平台授权事件类型
type InfoType string

const (
	//MsgTypeText 表示文本消息
	MsgTypeText MsgType = "text"
	//MsgTypeImage 表示图片消息
	MsgTypeImage = "image"
	//MsgTypeVoice 表示语音消息
	MsgTypeVoice = "voice"
	//MsgTypeVideo 表示视频消息
	MsgTypeVideo = "video"
	//MsgTypeShortVideo 表示短视频消息[限接收]
	MsgTypeShortVideo = "shortvideo"
	//MsgTypeLocation 表示坐标消息[限接收]
	MsgTypeLocation = "location"
	//MsgTypeLink 表示链接消息[限接收]
	MsgTypeLink = "link"
	//MsgTypeMusic 表示音乐消息[限回复]
	MsgTypeMusic = "music"
	//MsgTypeNews 表示图文消息[限回复]
	MsgTypeNews = "news"
	//MsgTypeTransfer 表示消息消息转发到客服
	MsgTypeTransfer = "transfer_customer_service"
	//MsgTypeEvent 表示事件推送消息
	MsgTypeEvent = "event"
)

const (
	//EventSubscribe 订阅
	EventSubscribe EventType = "subscribe"
	//EventUnsubscribe 取消订阅
	EventUnsubscribe = "unsubscribe"
	//EventScan 用户已经关注公众号，则微信会将带场景值扫描事件推送给开发者
	EventScan = "SCAN"
	//EventLocation 上报地理位置事件
	EventLocation = "LOCATION"
	//EventClick 点击菜单拉取消息时的事件推送
	EventClick = "CLICK"
	//EventView 点击菜单跳转链接时的事件推送
	EventView = "VIEW"
	//EventScancodePush 扫码推事件的事件推送
	EventScancodePush = "scancode_push"
	//EventScancodeWaitmsg 扫码推事件且弹出“消息接收中”提示框的事件推送
	EventScancodeWaitmsg = "scancode_waitmsg"
	//EventPicSysphoto 弹出系统拍照发图的事件推送
	EventPicSysphoto = "pic_sysphoto"
	//EventPicPhotoOrAlbum 弹出拍照或者相册发图的事件推送
	EventPicPhotoOrAlbum = "pic_photo_or_album"
	//EventPicWeixin 弹出微信相册发图器的事件推送
	EventPicWeixin = "pic_weixin"
	//EventLocationSelect 弹出地理位置选择器的事件推送
	EventLocationSelect = "location_select"
	//EventTemplateSendJobFinish 发送模板消息推送通知
	EventTemplateSendJobFinish = "TEMPLATESENDJOBFINISH"
	//EventWxaMediaCheck 异步校验图片/音频是否含有违法违规内容推送事件
	EventWxaMediaCheck = "wxa_media_check"
)

const (
	// InfoTypeVerifyTicket 返回ticket
	InfoTypeVerifyTicket InfoType = "component_verify_ticket"
	// InfoTypeAuthorized 授权
	InfoTypeAuthorized = "authorized"
	// InfoTypeUnauthorized 取消授权
	InfoTypeUnauthorized = "unauthorized"
	// InfoTypeUpdateAuthorized 更新授权
	InfoTypeUpdateAuthorized = "updateauthorized"
)

//MixMessage 存放所有微信发送过来的消息和事件
type MixMessage struct {
	CommonToken

	//基本消息
	MsgID        int64   `xml:"MsgId"`
	Content      string  `xml:"Content"`
	Recognition  string  `xml:"Recognition"`
	PicURL       string  `xml:"PicUrl"`
	MediaID      string  `xml:"MediaId"`
	Format       string  `xml:"Format"`
	ThumbMediaID string  `xml:"ThumbMediaId"`
	LocationX    float64 `xml:"Location_X"`
	LocationY    float64 `xml:"Location_Y"`
	Scale        float64 `xml:"Scale"`
	Label        string  `xml:"Label"`
	Title        string  `xml:"Title"`
	Description  string  `xml:"Description"`
	URL          string  `xml:"Url"`

	//事件相关
	Event       EventType `xml:"Event"`
	EventKey    string    `xml:"EventKey"`
	Ticket      string    `xml:"Ticket"`
	Latitude    string    `xml:"Latitude"`
	Longitude   string    `xml:"Longitude"`
	Precision   string    `xml:"Precision"`
	MenuID      string    `xml:"MenuId"`
	Status      string    `xml:"Status"`
	SessionFrom string    `xml:"SessionFrom"`

	ScanCodeInfo struct {
		ScanType   string `xml:"ScanType"`
		ScanResult string `xml:"ScanResult"`
	} `xml:"ScanCodeInfo"`

	SendPicsInfo struct {
		Count   int32      `xml:"Count"`
		PicList []EventPic `xml:"PicList>item"`
	} `xml:"SendPicsInfo"`

	SendLocationInfo struct {
		LocationX float64 `xml:"Location_X"`
		LocationY float64 `xml:"Location_Y"`
		Scale     float64 `xml:"Scale"`
		Label     string  `xml:"Label"`
		Poiname   string  `xml:"Poiname"`
	}

	// 第三方平台相关
	InfoType                     InfoType `xml:"InfoType"`
	AppID                        string   `xml:"AppId"`
	ComponentVerifyTicket        string   `xml:"ComponentVerifyTicket"`
	AuthorizerAppid              string   `xml:"AuthorizerAppid"`
	AuthorizationCode            string   `xml:"AuthorizationCode"`
	AuthorizationCodeExpiredTime int64    `xml:"AuthorizationCodeExpiredTime"`
	PreAuthCode                  string   `xml:"PreAuthCode"`

	// 卡券相关
	CardID              string `xml:"CardId"`
	RefuseReason        string `xml:"RefuseReason"`
	IsGiveByFriend      int32  `xml:"IsGiveByFriend"`
	FriendUserName      string `xml:"FriendUserName"`
	UserCardCode        string `xml:"UserCardCode"`
	OldUserCardCode     string `xml:"OldUserCardCode"`
	OuterStr            string `xml:"OuterStr"`
	IsRestoreMemberCard int32  `xml:"IsRestoreMemberCard"`
	UnionID             string `xml:"UnionId"`

	// 内容审核相关
	IsRisky       bool   `xml:"isrisky"`
	ExtraInfoJSON string `xml:"extra_info_json"`
	TraceID       string `xml:"trace_id"`
	StatusCode    int    `xml:"status_code"`
}

//EventPic 发图事件推送
type EventPic struct {
	PicMd5Sum string `xml:"PicMd5Sum"`
}

//EncryptedXMLMsg 安全模式下的消息体
type EncryptedXMLMsg struct {
	XMLName      struct{} `xml:"xml" json:"-"`
	ToUserName   string   `xml:"ToUserName" json:"ToUserName"`
	EncryptedMsg string   `xml:"Encrypt"    json:"Encrypt"`
}

//ResponseEncryptedXMLMsg 需要返回的消息体
type ResponseEncryptedXMLMsg struct {
	XMLName      struct{} `xml:"xml" json:"-"`
	EncryptedMsg string   `xml:"Encrypt"      json:"Encrypt"`
	MsgSignature string   `xml:"MsgSignature" json:"MsgSignature"`
	Timestamp    int64    `xml:"TimeStamp"    json:"TimeStamp"`
	Nonce        string   `xml:"Nonce"        json:"Nonce"`
}

// CommonToken 消息中通用的结构
type CommonToken struct {
	XMLName      xml.Name `xml:"xml"`
	ToUserName   CDATA    `xml:"ToUserName"`
	FromUserName CDATA    `xml:"FromUserName"`
	CreateTime   int64    `xml:"CreateTime"`
	MsgType      MsgType  `xml:"MsgType"`
}

// CDATA  使用该类型,在序列化为 xml 文本时文本会被解析器忽略
type CDATA string

//  MarshalXML 实现自己的序列化方法
func (c CDATA) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	return e.EncodeElement(struct {
		string `xml:",cdata"`
	}{string(c)}, start)
}

//SetToUserName set ToUserName
func (msg *CommonToken) SetToUserName(toUserName string) {
	msg.ToUserName = CDATA(toUserName)
}

//SetFromUserName set FromUserName
func (msg *CommonToken) SetFromUserName(fromUserName string) {
	msg.FromUserName = CDATA(fromUserName)
}

//SetCreateTime set createTime
func (msg *CommonToken) SetCreateTime(createTime int64) {
	msg.CreateTime = createTime
}

//SetMsgType set MsgType
func (msg *CommonToken) SetMsgType(msgType MsgType) {
	msg.MsgType = msgType
}
<|endoftext|>


<commit_before>package metrics

import (
	"regexp"
	"sort"
	"time"

	"github.com/DataDog/datadog-go/statsd"
	"github.com/buildkite/agent/logger"
)

type Collector struct {
	Datadog     bool
	DatadogHost string

	client *statsd.Client
}

func (c *Collector) Start() error {
	logger.Info("Starting metrics collection to %s", c.DatadogHost)

	var err error
	c.client, err = statsd.New(c.DatadogHost)
	if err != nil {
		return err
	}

	c.client.Namespace = "buildkite."
	return nil
}

func (c *Collector) Stop() error {
	logger.Info("Stopping metrics collection")
	return c.client.Close()
}

func (c *Collector) Scope(tags Tags) *Scope {
	return &Scope{
		Tags: tags,
		c:    c,
	}
}

type Scope struct {
	Tags Tags
	c    *Collector
}

// Timing sends timing information in milliseconds.
func (s *Scope) Timing(name string, value time.Duration, tags ...Tags) {
	mergedTags := s.mergeTags(tags...).StringSlice()
	logger.Debug("Metrics timing %s=%v %v", name, value, mergedTags)

	if err := s.c.client.Timing(name, value, mergedTags, 1); err != nil {
		logger.Error("Metrics timing failed: %v", err)
	}
}

// Count tracks how many times something happened per second.
func (s *Scope) Count(name string, value int64, tags ...Tags) {
	mergedTags := s.mergeTags(tags...).StringSlice()
	logger.Debug("Metrics count %s=%v %v", name, value, mergedTags)

	if err := s.c.client.Count(name, value, mergedTags, 1); err != nil {
		logger.Error("Metrics count failed: %v", err)
	}
}

func (s *Scope) mergeTags(tagsSlice ...Tags) Tags {
	merged := Tags{}
	for k, v := range s.Tags {
		merged[formatName(k)] = formatName(v)
	}
	for _, tags := range tagsSlice {
		for k, v := range tags {
			merged[formatName(k)] = formatName(v)
		}
	}
	return merged
}

type Tags map[string]string

func (tags Tags) StringSlice() []string {
	var stringSlice []string
	for k, v := range tags {
		if k != "" && v != "" {
			stringSlice = append(stringSlice, formatName(k)+":"+formatName(v))
		}
	}
	sort.Strings(stringSlice)
	return stringSlice
}

// Datadog allows '.', '_' and alphas only.
// If we don't validate this here then the datadog error logs can fill up disk really quickly
var nameRegex = regexp.MustCompile(`[^\._a-zA-Z0-9]+`)

func formatName(name string) string {
	return nameRegex.ReplaceAllString(name, "_")
}
<commit_msg>Metrics is a no-op if datadog disabled<commit_after>package metrics

import (
	"regexp"
	"sort"
	"time"

	"github.com/DataDog/datadog-go/statsd"
	"github.com/buildkite/agent/logger"
)

type Collector struct {
	Datadog     bool
	DatadogHost string

	client *statsd.Client
}

func (c *Collector) Start() error {
	if c.Datadog {
		logger.Info("Starting datadog metrics collection to %s", c.DatadogHost)

		var err error
		c.client, err = statsd.New(c.DatadogHost)
		if err != nil {
			return err
		}

		c.client.Namespace = "buildkite."
	}
	return nil
}

func (c *Collector) Stop() error {
	if c.Datadog && c.client != nil {
		logger.Info("Stopping metrics collection")
		return c.client.Close()
	}
	return nil
}

func (c *Collector) Scope(tags Tags) *Scope {
	return &Scope{
		Tags: tags,
		c:    c,
	}
}

type Scope struct {
	Tags Tags
	c    *Collector
}

// Timing sends timing information in milliseconds.
func (s *Scope) Timing(name string, value time.Duration, tags ...Tags) {
	if s.c.client == nil {
		return
	}

	mergedTags := s.mergeTags(tags...).StringSlice()
	logger.Debug("Metrics timing %s=%v %v", name, value, mergedTags)

	if err := s.c.client.Timing(name, value, mergedTags, 1); err != nil {
		logger.Error("Metrics timing failed: %v", err)
	}
}

// Count tracks how many times something happened per second.
func (s *Scope) Count(name string, value int64, tags ...Tags) {
	if s.c.client == nil {
		return
	}

	mergedTags := s.mergeTags(tags...).StringSlice()
	logger.Debug("Metrics count %s=%v %v", name, value, mergedTags)

	if err := s.c.client.Count(name, value, mergedTags, 1); err != nil {
		logger.Error("Metrics count failed: %v", err)
	}
}

func (s *Scope) mergeTags(tagsSlice ...Tags) Tags {
	merged := Tags{}
	for k, v := range s.Tags {
		merged[formatName(k)] = formatName(v)
	}
	for _, tags := range tagsSlice {
		for k, v := range tags {
			merged[formatName(k)] = formatName(v)
		}
	}
	return merged
}

type Tags map[string]string

func (tags Tags) StringSlice() []string {
	var stringSlice []string
	for k, v := range tags {
		if k != "" && v != "" {
			stringSlice = append(stringSlice, formatName(k)+":"+formatName(v))
		}
	}
	sort.Strings(stringSlice)
	return stringSlice
}

// Datadog allows '.', '_' and alphas only.
// If we don't validate this here then the datadog error logs can fill up disk really quickly
var nameRegex = regexp.MustCompile(`[^\._a-zA-Z0-9]+`)

func formatName(name string) string {
	return nameRegex.ReplaceAllString(name, "_")
}
<|endoftext|>


<commit_before>package main

import (
	"archive/zip"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"gopkg.in/yaml.v2"
)

var (
	err error
)

type Config struct {
	Static []StaticProject
}

type StaticProject struct {
	Name       string
	Branch     string
	Domain     string
	Subdomain  string
	GitHub     bool
	Bucket     string
	Owner      string
	Repository string
}

func main() {
	data, err := ioutil.ReadFile("deployd.conf")

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

	var config Config
	err = yaml.Unmarshal(data, &config)

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

	for _, project := range config.Static {
		archivePath := fmt.Sprintf("/tmp/%v-%v.zip", project.Name, project.Branch)

		err = os.RemoveAll(archivePath)

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

		archive, err := os.Create(archivePath)
		defer archive.Close()

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

		var archiveLocation string

		if project.GitHub {
			archiveLocation = fmt.Sprintf("https://github.com/%v/%v/archive/%v.zip", project.Owner, project.Repository, project.Branch)
		} else {
			archiveLocation = fmt.Sprintf("https://s3.amazonaws.com/%v/%v-latest.zip", project.Bucket, project.Branch)
		}

		response, err := http.Get(archiveLocation)
		defer response.Body.Close()

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

		_, err = io.Copy(archive, response.Body)

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

		unarchivedPath := fmt.Sprintf("/tmp/%v-%v", project.Name, project.Branch)

		err = os.RemoveAll(unarchivedPath)

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

		err = unzip(archivePath, unarchivedPath)

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

		if project.GitHub {
			unarchivedPath = fmt.Sprintf("%v/%v-%v", unarchivedPath, project.Repository, project.Branch)
		}

		domainPath := fmt.Sprintf("srv/%v", project.Domain)
		projectPath := fmt.Sprintf("srv/%v/%v", project.Domain, project.Subdomain)

		err = os.RemoveAll(projectPath)

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

		os.Mkdir(domainPath, 0700)

		err = os.Rename(unarchivedPath, projectPath)

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

// unzip function by http://stackoverflow.com/users/1129149/swtdrgn
// http://stackoverflow.com/questions/20357223/easy-way-to-unzip-file-with-golang/24430720#24430720

func unzip(src, dest string) error {
	r, err := zip.OpenReader(src)
	if err != nil {
		return err
	}
	defer r.Close()

	for _, f := range r.File {
		rc, err := f.Open()
		if err != nil {
			return err
		}
		defer rc.Close()

		fpath := filepath.Join(dest, f.Name)
		if f.FileInfo().IsDir() {
			os.MkdirAll(fpath, f.Mode())
		} else {
			var fdir string
			if lastIndex := strings.LastIndex(fpath, string(os.PathSeparator)); lastIndex > -1 {
				fdir = fpath[:lastIndex]
			}

			err = os.MkdirAll(fdir, f.Mode())
			if err != nil {
				log.Fatal(err)
				return err
			}
			f, err := os.OpenFile(
				fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
			if err != nil {
				return err
			}
			defer f.Close()

			_, err = io.Copy(f, rc)
			if err != nil {
				return err
			}
		}
	}
	return nil
}
<commit_msg>Added a flag for parsing the location of the config file<commit_after>package main

import (
	"archive/zip"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"gopkg.in/yaml.v2"
)

var (
	err error
)

type Config struct {
	Static []StaticProject
}

type StaticProject struct {
	Name       string
	Branch     string
	Domain     string
	Subdomain  string
	GitHub     bool
	Bucket     string
	Owner      string
	Repository string
}

func main() {
	configPath := flag.String("config", "/etc/deployd.conf", "Path to the config file")

	flag.Parse()

	if _, err = os.Stat(*configPath); os.IsNotExist(err) {
		log.Fatal(err)
	}

	data, err := ioutil.ReadFile(*configPath)

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

	var config Config
	err = yaml.Unmarshal(data, &config)

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

	for _, project := range config.Static {
		archivePath := fmt.Sprintf("/tmp/%v-%v.zip", project.Name, project.Branch)

		err = os.RemoveAll(archivePath)

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

		archive, err := os.Create(archivePath)
		defer archive.Close()

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

		var archiveLocation string

		if project.GitHub {
			archiveLocation = fmt.Sprintf("https://github.com/%v/%v/archive/%v.zip", project.Owner, project.Repository, project.Branch)
		} else {
			archiveLocation = fmt.Sprintf("https://s3.amazonaws.com/%v/%v-latest.zip", project.Bucket, project.Branch)
		}

		response, err := http.Get(archiveLocation)
		defer response.Body.Close()

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

		_, err = io.Copy(archive, response.Body)

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

		unarchivedPath := fmt.Sprintf("/tmp/%v-%v", project.Name, project.Branch)

		err = os.RemoveAll(unarchivedPath)

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

		err = unzip(archivePath, unarchivedPath)

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

		if project.GitHub {
			unarchivedPath = fmt.Sprintf("%v/%v-%v", unarchivedPath, project.Repository, project.Branch)
		}

		domainPath := fmt.Sprintf("srv/%v", project.Domain)
		projectPath := fmt.Sprintf("srv/%v/%v", project.Domain, project.Subdomain)

		err = os.RemoveAll(projectPath)

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

		os.Mkdir(domainPath, 0700)

		err = os.Rename(unarchivedPath, projectPath)

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

// unzip function by http://stackoverflow.com/users/1129149/swtdrgn
// http://stackoverflow.com/questions/20357223/easy-way-to-unzip-file-with-golang/24430720#24430720

func unzip(src, dest string) error {
	r, err := zip.OpenReader(src)
	if err != nil {
		return err
	}
	defer r.Close()

	for _, f := range r.File {
		rc, err := f.Open()
		if err != nil {
			return err
		}
		defer rc.Close()

		fpath := filepath.Join(dest, f.Name)
		if f.FileInfo().IsDir() {
			os.MkdirAll(fpath, f.Mode())
		} else {
			var fdir string
			if lastIndex := strings.LastIndex(fpath, string(os.PathSeparator)); lastIndex > -1 {
				fdir = fpath[:lastIndex]
			}

			err = os.MkdirAll(fdir, f.Mode())
			if err != nil {
				log.Fatal(err)
				return err
			}
			f, err := os.OpenFile(
				fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
			if err != nil {
				return err
			}
			defer f.Close()

			_, err = io.Copy(f, rc)
			if err != nil {
				return err
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
//

package gcmd

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"

	"github.com/gogf/gf/text/gstr"

	"errors"

	"github.com/gogf/gf/container/gvar"

	"github.com/gogf/gf/text/gregex"
)

// Parser for arguments.
type Parser struct {
	strict           bool              // Whether stops parsing and returns error if invalid option passed.
	parsedArgs       []string          // As name described.
	parsedOptions    map[string]string // As name described.
	passedOptions    map[string]bool   // User passed supported options.
	supportedOptions map[string]bool   // Option [option name : need argument].
	commandFuncMap   map[string]func() // Command function map for function handler.
}

// Parse creates and returns a new Parser with os.Args and supported options.
//
// Note that the parameter <supportedOptions> is as [option name: need argument], which means
// the value item of <supportedOptions> indicates whether corresponding option name needs argument or not.
//
// The optional parameter <strict> specifies whether stops parsing and returns error if invalid option passed.
func Parse(supportedOptions map[string]bool, strict ...bool) (*Parser, error) {
	return ParseWithArgs(os.Args, supportedOptions, strict...)
}

// ParseWithArgs creates and returns a new Parser with given arguments and supported options.
//
// Note that the parameter <supportedOptions> is as [option name: need argument], which means
// the value item of <supportedOptions> indicates whether corresponding option name needs argument or not.
//
// The optional parameter <strict> specifies whether stops parsing and returns error if invalid option passed.
func ParseWithArgs(args []string, supportedOptions map[string]bool, strict ...bool) (*Parser, error) {
	strictParsing := false
	if len(strict) > 0 {
		strictParsing = strict[0]
	}
	parser := &Parser{
		strict:           strictParsing,
		parsedArgs:       make([]string, 0),
		parsedOptions:    make(map[string]string),
		passedOptions:    supportedOptions,
		supportedOptions: make(map[string]bool),
		commandFuncMap:   make(map[string]func()),
	}
	for name, needArgument := range supportedOptions {
		for _, v := range strings.Split(name, ",") {
			parser.supportedOptions[strings.TrimSpace(v)] = needArgument
		}
	}

	for i := 0; i < len(args); {
		if option := parser.parseOption(args[i]); option != "" {
			array, _ := gregex.MatchString(`^(.+?)=(.+)$`, option)
			if len(array) == 3 {
				if parser.isOptionValid(array[1]) {
					parser.setOptionValue(array[1], array[2])
				}
			} else {
				if parser.isOptionValid(option) {
					if parser.isOptionNeedArgument(option) {
						if i+1 < len(args) {
							parser.setOptionValue(option, args[i+1])
							i += 2
							continue
						}
					} else {
						parser.setOptionValue(option, "")
						i++
						continue
					}
				} else {
					// Multiple options?
					if array := parser.parseMultiOption(option); len(array) > 0 {
						for _, v := range array {
							parser.setOptionValue(v, "")
						}
						i++
						continue
					} else if parser.strict {
						return nil, errors.New(fmt.Sprintf(`invalid option '%s'`, args[i]))
					}
				}
			}
		} else {
			parser.parsedArgs = append(parser.parsedArgs, args[i])
		}
		i++
	}
	return parser, nil
}

// parseMultiOption parses option to multiple valid options like: --dav.
// It returns nil if given option is not multi-option.
func (p *Parser) parseMultiOption(option string) []string {
	for i := 1; i <= len(option); i++ {
		s := option[:i]
		if p.isOptionValid(s) && !p.isOptionNeedArgument(s) {
			if i == len(option) {
				return []string{s}
			}
			array := p.parseMultiOption(option[i:])
			if len(array) == 0 {
				return nil
			}
			return append(array, s)
		}
	}
	return nil
}

func (p *Parser) parseOption(argument string) string {
	array, _ := gregex.MatchString(`^\-{1,2}(.+)$`, argument)
	if len(array) == 2 {
		return array[1]
	}
	return ""
}

func (p *Parser) isOptionValid(name string) bool {
	if !p.strict {
		return true
	}
	_, ok := p.supportedOptions[name]
	return ok
}

func (p *Parser) isOptionNeedArgument(name string) bool {
	return p.supportedOptions[name]
}

// setOptionValue sets the option value for name and according alias.
func (p *Parser) setOptionValue(name, value string) {
	for optionName, _ := range p.passedOptions {
		array := gstr.SplitAndTrim(optionName, ",")
		for _, v := range array {
			if strings.EqualFold(v, name) {
				for _, v := range array {
					p.parsedOptions[v] = value
				}
				return
			}
		}
	}
}

// GetOpt returns the option value named <name>.
func (p *Parser) GetOpt(name string, def ...string) string {
	if v, ok := p.parsedOptions[name]; ok {
		return v
	}
	if len(def) > 0 {
		return def[0]
	}
	return ""
}

// GetOptVar returns the option value named <name> as *gvar.Var.
func (p *Parser) GetOptVar(name string, def ...string) *gvar.Var {
	return gvar.New(p.GetOpt(name, def...))
}

// GetOptAll returns all parsed options.
func (p *Parser) GetOptAll() map[string]string {
	return p.parsedOptions
}

// ContainsOpt checks whether option named <name> exist in the arguments.
func (p *Parser) ContainsOpt(name string, def ...string) bool {
	_, ok := p.parsedOptions[name]
	return ok
}

// GetArg returns the argument at <index>.
func (p *Parser) GetArg(index int, def ...string) string {
	if index < len(p.parsedArgs) {
		return p.parsedArgs[index]
	}
	if len(def) > 0 {
		return def[0]
	}
	return ""
}

// GetArgVar returns the argument at <index> as *gvar.Var.
func (p *Parser) GetArgVar(index int, def ...string) *gvar.Var {
	return gvar.New(p.GetArg(index, def...))
}

// GetArgAll returns all parsed arguments.
func (p *Parser) GetArgAll() []string {
	return p.parsedArgs
}

// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (p *Parser) MarshalJSON() ([]byte, error) {
	return json.Marshal(map[string]interface{}{
		"parsedArgs":       p.parsedArgs,
		"parsedOptions":    p.parsedOptions,
		"passedOptions":    p.passedOptions,
		"supportedOptions": p.supportedOptions,
	})
}
<commit_msg>improve strict parsing feature for gcmd<commit_after>// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
//

package gcmd

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"

	"github.com/gogf/gf/text/gstr"

	"errors"

	"github.com/gogf/gf/container/gvar"

	"github.com/gogf/gf/text/gregex"
)

// Parser for arguments.
type Parser struct {
	strict           bool              // Whether stops parsing and returns error if invalid option passed.
	parsedArgs       []string          // As name described.
	parsedOptions    map[string]string // As name described.
	passedOptions    map[string]bool   // User passed supported options.
	supportedOptions map[string]bool   // Option [option name : need argument].
	commandFuncMap   map[string]func() // Command function map for function handler.
}

// Parse creates and returns a new Parser with os.Args and supported options.
//
// Note that the parameter <supportedOptions> is as [option name: need argument], which means
// the value item of <supportedOptions> indicates whether corresponding option name needs argument or not.
//
// The optional parameter <strict> specifies whether stops parsing and returns error if invalid option passed.
func Parse(supportedOptions map[string]bool, strict ...bool) (*Parser, error) {
	return ParseWithArgs(os.Args, supportedOptions, strict...)
}

// ParseWithArgs creates and returns a new Parser with given arguments and supported options.
//
// Note that the parameter <supportedOptions> is as [option name: need argument], which means
// the value item of <supportedOptions> indicates whether corresponding option name needs argument or not.
//
// The optional parameter <strict> specifies whether stops parsing and returns error if invalid option passed.
func ParseWithArgs(args []string, supportedOptions map[string]bool, strict ...bool) (*Parser, error) {
	strictParsing := false
	if len(strict) > 0 {
		strictParsing = strict[0]
	}
	parser := &Parser{
		strict:           strictParsing,
		parsedArgs:       make([]string, 0),
		parsedOptions:    make(map[string]string),
		passedOptions:    supportedOptions,
		supportedOptions: make(map[string]bool),
		commandFuncMap:   make(map[string]func()),
	}
	for name, needArgument := range supportedOptions {
		for _, v := range strings.Split(name, ",") {
			parser.supportedOptions[strings.TrimSpace(v)] = needArgument
		}
	}

	for i := 0; i < len(args); {
		if option := parser.parseOption(args[i]); option != "" {
			array, _ := gregex.MatchString(`^(.+?)=(.+)$`, option)
			if len(array) == 3 {
				if parser.isOptionValid(array[1]) {
					parser.setOptionValue(array[1], array[2])
				}
			} else {
				if parser.isOptionValid(option) {
					if parser.isOptionNeedArgument(option) {
						if i+1 < len(args) {
							parser.setOptionValue(option, args[i+1])
							i += 2
							continue
						}
					} else {
						parser.setOptionValue(option, "")
						i++
						continue
					}
				} else {
					// Multiple options?
					if array := parser.parseMultiOption(option); len(array) > 0 {
						for _, v := range array {
							parser.setOptionValue(v, "")
						}
						i++
						continue
					} else if parser.strict {
						return nil, errors.New(fmt.Sprintf(`invalid option '%s'`, args[i]))
					}
				}
			}
		} else {
			parser.parsedArgs = append(parser.parsedArgs, args[i])
		}
		i++
	}
	return parser, nil
}

// parseMultiOption parses option to multiple valid options like: --dav.
// It returns nil if given option is not multi-option.
func (p *Parser) parseMultiOption(option string) []string {
	for i := 1; i <= len(option); i++ {
		s := option[:i]
		if p.isOptionValid(s) && !p.isOptionNeedArgument(s) {
			if i == len(option) {
				return []string{s}
			}
			array := p.parseMultiOption(option[i:])
			if len(array) == 0 {
				return nil
			}
			return append(array, s)
		}
	}
	return nil
}

func (p *Parser) parseOption(argument string) string {
	array, _ := gregex.MatchString(`^\-{1,2}(.+)$`, argument)
	if len(array) == 2 {
		return array[1]
	}
	return ""
}

func (p *Parser) isOptionValid(name string) bool {
	_, ok := p.supportedOptions[name]
	return ok
}

func (p *Parser) isOptionNeedArgument(name string) bool {
	return p.supportedOptions[name]
}

// setOptionValue sets the option value for name and according alias.
func (p *Parser) setOptionValue(name, value string) {
	for optionName, _ := range p.passedOptions {
		array := gstr.SplitAndTrim(optionName, ",")
		for _, v := range array {
			if strings.EqualFold(v, name) {
				for _, v := range array {
					p.parsedOptions[v] = value
				}
				return
			}
		}
	}
}

// GetOpt returns the option value named <name>.
func (p *Parser) GetOpt(name string, def ...string) string {
	if v, ok := p.parsedOptions[name]; ok {
		return v
	}
	if len(def) > 0 {
		return def[0]
	}
	return ""
}

// GetOptVar returns the option value named <name> as *gvar.Var.
func (p *Parser) GetOptVar(name string, def ...string) *gvar.Var {
	return gvar.New(p.GetOpt(name, def...))
}

// GetOptAll returns all parsed options.
func (p *Parser) GetOptAll() map[string]string {
	return p.parsedOptions
}

// ContainsOpt checks whether option named <name> exist in the arguments.
func (p *Parser) ContainsOpt(name string, def ...string) bool {
	_, ok := p.parsedOptions[name]
	return ok
}

// GetArg returns the argument at <index>.
func (p *Parser) GetArg(index int, def ...string) string {
	if index < len(p.parsedArgs) {
		return p.parsedArgs[index]
	}
	if len(def) > 0 {
		return def[0]
	}
	return ""
}

// GetArgVar returns the argument at <index> as *gvar.Var.
func (p *Parser) GetArgVar(index int, def ...string) *gvar.Var {
	return gvar.New(p.GetArg(index, def...))
}

// GetArgAll returns all parsed arguments.
func (p *Parser) GetArgAll() []string {
	return p.parsedArgs
}

// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (p *Parser) MarshalJSON() ([]byte, error) {
	return json.Marshal(map[string]interface{}{
		"parsedArgs":       p.parsedArgs,
		"parsedOptions":    p.parsedOptions,
		"passedOptions":    p.passedOptions,
		"supportedOptions": p.supportedOptions,
	})
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/codegangsta/cli"
	"github.com/fsouza/go-dockerclient"
	"log"
	"os"
	"sync"
	"time"
)

func readExcludes(fileName string) []string {
	var excludeNames []string = []
	if file, err := os.Open(fileName); err != nil {
    		log.Fatal("Error opening input file:", err)
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		append(excludeNames, scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		log.Fatal("Error reading exclude file:", scanner.Err())
	}
	return excludeNames
}

func collectAPIImages(images []docker.APIImages, client *docker.Client, ctx *cli.Context, excludes []string) {
	var imageSync sync.WaitGroup
	grace := ctx.Duration("grace")
	quiet := ctx.Bool("quiet")
	options := docker.RemoveImageOptions{
		Force:   ctx.Bool("force"),
		NoPrune: ctx.Bool("no-prune"),
	}
	for _, image := range images {
		imageSync.Add(1)
		go func(image docker.APIImages) {
			defer imageSync.Done()
			imageDetail, _ := client.InspectImage(image.ID)
			now := time.Now()
			if now.Sub(imageDetail.Created) >= grace {
				if err := client.RemoveImageExtended(imageDetail.ID, options); err == nil {
					if !quiet {
						log.Printf("Deleted image: %s.\n", imageDetail.ID)
					}
				}
			}
		}(image)
	}
	imageSync.Wait()
}

func collectAPIContainers(containers []docker.APIContainers, client *docker.Client, ctx *cli.Context, excludes []string) {
	var containerSync sync.WaitGroup
	grace := ctx.Duration("grace")
	quiet := ctx.Bool("quiet")
	for _, container := range containers {
		containerSync.Add(1)
		go func(container docker.APIContainers){
			defer containerSync.Done()
			containerDetail, _ := client.InspectContainer(container.ID)
			now := time.Now()
			if now.Sub(containerDetail.Created) >= grace {
				options := docker.RemoveContainerOptions{
					ID:            containerDetail.ID,
					RemoveVolumes: ctx.Bool("remove-volumes"),
					Force:         ctx.Bool("force"),
				}
				if err := client.RemoveContainer(options); err == nil {
					if !quiet {
						log.Printf("Deleted container: %s.\n", containerDetail.ID)
					}
				}
			}
		}(container)
	}
	containerSync.Wait()
}

func runDgc(ctx *cli.Context) {
	var dgcSync sync.WaitGroup
	client, _ := docker.NewClient(ctx.String("socket"))
	images, _ := client.ListImages(docker.ListImagesOptions{All: true})
	containers, _ := client.ListContainers(docker.ListContainersOptions{All: true})
	excludes := readExcludes(ctx.String("exclude"))
	dgcSync.Add(2)
	go func() {
		defer dgcSync.Done()
		collectAPIContainers(containers, client, ctx, excludes)
	}()
	go func() {
		defer dgcSync.Done()
		collectAPIImages(images, client, ctx, excludes)
	}()
	dgcSync.Wait()
}

func main() {
	dgc := cli.NewApp()
	dgc.EnableBashCompletion = true
	dgc.Name = "dgc"
	dgc.Usage = "A minimal docker garbage collector"
	dgc.Version = "0.1.0"
	dgc.Author = "David J Felix <davidjfelix@davidjfelix.com>"
	dgc.Action = runDgc
	dgc.Flags = []cli.Flag{
		cli.DurationFlag{
			Name:   "grace, g",
			Value:  time.Duration(3600) * time.Second,
			Usage:  "the grace period for a container. Accepted compostable time units: [h, m, s, ms, ns us]",
			EnvVar: "GRACE_PERIOD_SECONDS,GRACE_PERIOD",
		},
		cli.StringFlag{
			Name:   "socket, s",
			Value:  "unix:///var/run/docker.sock",
			Usage:  "the docker remote socket",
			EnvVar: "DOCKER_SOCKET",
		},
		cli.StringFlag{
			Name:   "exclude, e",
			Value:  "/etc/docker-gc-exclude",
			Usage:  "the list of containers to exclude from garbage collection, as a file or directory",
			EnvVar: "EXCLUDE_FROM_GC",
		},
		cli.BoolFlag{
			Name:  "quiet, q",
			Usage: "don't print name of garbage-collected containers",
		},
		cli.BoolTFlag{
			Name:  "remove-volumes, r",
			Usage: "remove volumes with the container",
		},
		cli.BoolFlag{
			Name:  "force, f",
			Usage: "force images and containers to stop and be collected",
		},
		cli.BoolFlag{
			Name:  "no-prune, n",
			Usage: "don't prune parent images to a GC'd image",
		},
	}
	dgc.Run(os.Args)
}
<commit_msg>Refactor deletes to check for excludes<commit_after>package main

import (
	"github.com/codegangsta/cli"
	"github.com/fsouza/go-dockerclient"
	"log"
	"os"
	"sync"
	"time"
)

func readExcludes(fileName string) []string {
	var excludeNames []string = []
	if file, err := os.Open(fileName); err != nil {
    		log.Fatal("Error opening input file:", err)
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		append(excludeNames, scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		log.Fatal("Error reading exclude file:", scanner.Err())
	}
	return excludeNames
}

func collectAPIImages(images []docker.APIImages, client *docker.Client, ctx *cli.Context, excludes []string) {
	var imageSync sync.WaitGroup
	grace := ctx.Duration("grace")
	quiet := ctx.Bool("quiet")
	options := docker.RemoveImageOptions{
		Force:   ctx.Bool("force"),
		NoPrune: ctx.Bool("no-prune"),
	}

	for _, image := range images {
		imageSync.Add(1)
		go func(image docker.APIImages) {
			defer imageSync.Done()

			// Check if the image id or tag is on excludes list
			for _, excludeName := range excludes {
				if image.ID == excludeName {
					return
				}
				for _, tag := range image.RepoTags {
					if tag == excludeName {
						return
					}
				}
			}

			// End if the image is still in the grace period
			imageDetail, _ := client.InspectImage(image.ID)
			now := time.Now()
			if now.Sub(imageDetail.Created) < grace {
				return
			}

			// Delete image
			if err := client.RemoveImageExtended(imageDetail.ID, options); err == nil {
				if !quiet {
					log.Printf("Deleted image: %s.\n", imageDetail.ID)
				}
			}
		}(image)
	}

	imageSync.Wait()
}

func collectAPIContainers(containers []docker.APIContainers, client *docker.Client, ctx *cli.Context, excludes []string) {
	var containerSync sync.WaitGroup
	grace := ctx.Duration("grace")
	quiet := ctx.Bool("quiet")

	for _, container := range containers {
		containerSync.Add(1)
		go func(container docker.APIContainers){
			defer containerSync.Done()

			// Check if the container id or tag is on excludes list
			for _, excludeName := range excludes {
				if container.ID == excludeName {
					return
				}
				if container.Image == excludeName {
					return
				}
				for _, name := range container.Names {
					if name == excludeName {
						return
					}
				}
			}

			// End if the container is still in the grace period
			containerDetail, _ := client.InspectContainer(container.ID)
			now := time.Now()
			if now.Sub(containerDetail.Created) < grace {
				return
			}

			// Delete container
			options := docker.RemoveContainerOptions{
				ID:            containerDetail.ID,
				RemoveVolumes: ctx.Bool("remove-volumes"),
				Force:         ctx.Bool("force"),
			}
			if err := client.RemoveContainer(options); err == nil {
				if !quiet {
					log.Printf("Deleted container: %s.\n", containerDetail.ID)
				}
			}
		}(container)
	}

	containerSync.Wait()
}

func runDgc(ctx *cli.Context) {
	var dgcSync sync.WaitGroup
	client, _ := docker.NewClient(ctx.String("socket"))
	images, _ := client.ListImages(docker.ListImagesOptions{All: true})
	containers, _ := client.ListContainers(docker.ListContainersOptions{All: true})
	excludes := readExcludes(ctx.String("exclude"))
	dgcSync.Add(2)
	go func() {
		defer dgcSync.Done()
		collectAPIContainers(containers, client, ctx, excludes)
	}()
	go func() {
		defer dgcSync.Done()
		collectAPIImages(images, client, ctx, excludes)
	}()
	dgcSync.Wait()
}

func main() {
	dgc := cli.NewApp()
	dgc.EnableBashCompletion = true
	dgc.Name = "dgc"
	dgc.Usage = "A minimal docker garbage collector"
	dgc.Version = "0.1.0"
	dgc.Author = "David J Felix <davidjfelix@davidjfelix.com>"
	dgc.Action = runDgc
	dgc.Flags = []cli.Flag{
		cli.DurationFlag{
			Name:   "grace, g",
			Value:  time.Duration(3600) * time.Second,
			Usage:  "the grace period for a container. Accepted compostable time units: [h, m, s, ms, ns us]",
			EnvVar: "GRACE_PERIOD_SECONDS,GRACE_PERIOD",
		},
		cli.StringFlag{
			Name:   "socket, s",
			Value:  "unix:///var/run/docker.sock",
			Usage:  "the docker remote socket",
			EnvVar: "DOCKER_SOCKET",
		},
		cli.StringFlag{
			Name:   "exclude, e",
			Value:  "/etc/docker-gc-exclude",
			Usage:  "the list of containers to exclude from garbage collection, as a file or directory",
			EnvVar: "EXCLUDE_FROM_GC",
		},
		cli.BoolFlag{
			Name:  "quiet, q",
			Usage: "don't print name of garbage-collected containers",
		},
		cli.BoolTFlag{
			Name:  "remove-volumes, r",
			Usage: "remove volumes with the container",
		},
		cli.BoolFlag{
			Name:  "force, f",
			Usage: "force images and containers to stop and be collected",
		},
		cli.BoolFlag{
			Name:  "no-prune, n",
			Usage: "don't prune parent images to a GC'd image",
		},
	}
	dgc.Run(os.Args)
}
<|endoftext|>


<commit_before>package sqldb

import (
	"fmt"
	"strings"
)

type Postgres struct{}

func (Postgres) defaultVal(def, val string, quote bool) string {
	if val == "" {
		val = def
	}
	if quote {
		if strings.Contains(val, "'") {
			val = strings.Replace(val, "\\", "\\\\'", -1)
			val = strings.Replace(val, "'", "\\'", -1)
			val = `E'` + val + `'`
		} else {
			val = `'` + val + `'`
		}
	}
	return val
}

func (p Postgres) Type(typ, precision, val string) (dbtyp, defval string, err error) {
	switch typ {
	case "bool":
		return "BOOLEAN", p.defaultVal("false", val, false), nil
	case "int":
		return "INTEGER", p.defaultVal("0", val, false), nil
	case "int8":
		return "SMALLINT", p.defaultVal("0", val, false), nil
	case "int16":
		return "SMALLINT", p.defaultVal("0", val, false), nil
	case "int32":
		return "INTEGER", p.defaultVal("0", val, false), nil
	case "int64":
		return "BIGINT", p.defaultVal("0", val, false), nil
	case "uint":
		return "BIGINT", p.defaultVal("0", val, false), nil
	case "uint8":
		return "SMALLINT", p.defaultVal("0", val, false), nil
	case "uint16":
		return "INTEGER", p.defaultVal("0", val, false), nil
	case "uint32":
		return "BIGINT", p.defaultVal("0", val, false), nil
	case "uint64":
		return "BIGINT", p.defaultVal("0", val, false), nil
	case "float32", "float64":
		if precision != "" {
			typ = fmt.Sprintf("NUMERIC(%s)", precision)
		} else if typ == "float32" {
			typ = "REAL"
		} else {
			typ = "DOUBLE PRECISION"
		}
		return typ, p.defaultVal("0", val, false), nil
	case "string":
		if precision == "" {
			precision = "1024"
		}
		return fmt.Sprintf("VARCHAR(%s)", precision), p.defaultVal("", val, true), nil
	case "char":
		if precision == "" {
			precision = "256"
		}
		return fmt.Sprintf("CHAR(%s)", precision), p.defaultVal("", val, true), nil
	case "text":
		return "text", p.defaultVal("", val, true), nil
	default:
		return "", "", fmt.Errorf("postgres: unsupported type: %s", typ)
	}
}

func (Postgres) DSN(config DBConfig) string {
	if config.Host == "" {
		config.Host = "localhost"
	}
	if config.Port == 0 {
		config.Port = 5432
	}
	userPass := config.User
	if userPass != "" {
		if config.Password != "" {
			userPass += ":" + config.Password
		}
		userPass += "@"
	}
	return fmt.Sprintf(
		"postgres://%s%s:%d/%s?%s",
		userPass,
		config.Host,
		config.Port,
		config.DBName,
		config.JoinOptions("=", "&"),
	)
}

type SQLite3 struct{}

func (SQLite3) DSN(config DBConfig) string {
	if config.Host == "" || config.DBName == "" {
		return ":memory:"
	}
	return fmt.Sprintf("file:%s?%s", config.DBName, config.JoinOptions("=", "&"))
}

func (s SQLite3) defaultVal(def, val string, quote bool) string {
	if val == "" {
		val = def
	}
	if quote {
		if strings.Contains(val, "'") {
			val = strings.Replace(val, "'", "''", -1)
		}
		val = `'` + val + `'`
	}
	return val
}

func (s SQLite3) Type(typ, precision, val string) (dbtyp, defval string, err error) {
	switch typ {
	case "bool",
		"int",
		"int8",
		"int16",
		"int32",
		"int64",
		"uint",
		"uint8",
		"uint16",
		"uint32",
		"uint64":
		return "INTEGER", s.defaultVal("0", val, false), nil
	case "float32",
		"float64":
		return "FLOAT", s.defaultVal("0", val, false), nil
	case "string",
		"text",
		"char":
		return "TEXT", s.defaultVal("", val, true), nil
	default:
		return "", "", fmt.Errorf("postgres: unsupported type: %s", typ)
	}
}
<commit_msg>fixed error info.<commit_after>package sqldb

import (
	"fmt"
	"strings"
)

type Postgres struct{}

func (Postgres) defaultVal(def, val string, quote bool) string {
	if val == "" {
		val = def
	}
	if quote {
		if strings.Contains(val, "'") {
			val = strings.Replace(val, "\\", "\\\\'", -1)
			val = strings.Replace(val, "'", "\\'", -1)
			val = `E'` + val + `'`
		} else {
			val = `'` + val + `'`
		}
	}
	return val
}

func (p Postgres) Type(typ, precision, val string) (dbtyp, defval string, err error) {
	switch typ {
	case "bool":
		return "BOOLEAN", p.defaultVal("false", val, false), nil
	case "int":
		return "INTEGER", p.defaultVal("0", val, false), nil
	case "int8":
		return "SMALLINT", p.defaultVal("0", val, false), nil
	case "int16":
		return "SMALLINT", p.defaultVal("0", val, false), nil
	case "int32":
		return "INTEGER", p.defaultVal("0", val, false), nil
	case "int64":
		return "BIGINT", p.defaultVal("0", val, false), nil
	case "uint":
		return "BIGINT", p.defaultVal("0", val, false), nil
	case "uint8":
		return "SMALLINT", p.defaultVal("0", val, false), nil
	case "uint16":
		return "INTEGER", p.defaultVal("0", val, false), nil
	case "uint32":
		return "BIGINT", p.defaultVal("0", val, false), nil
	case "uint64":
		return "BIGINT", p.defaultVal("0", val, false), nil
	case "float32", "float64":
		if precision != "" {
			typ = fmt.Sprintf("NUMERIC(%s)", precision)
		} else if typ == "float32" {
			typ = "REAL"
		} else {
			typ = "DOUBLE PRECISION"
		}
		return typ, p.defaultVal("0", val, false), nil
	case "string":
		if precision == "" {
			precision = "1024"
		}
		return fmt.Sprintf("VARCHAR(%s)", precision), p.defaultVal("", val, true), nil
	case "char":
		if precision == "" {
			precision = "256"
		}
		return fmt.Sprintf("CHAR(%s)", precision), p.defaultVal("", val, true), nil
	case "text":
		return "text", p.defaultVal("", val, true), nil
	default:
		return "", "", fmt.Errorf("postgres: unsupported type: %s", typ)
	}
}

func (Postgres) DSN(config DBConfig) string {
	if config.Host == "" {
		config.Host = "localhost"
	}
	if config.Port == 0 {
		config.Port = 5432
	}
	userPass := config.User
	if userPass != "" {
		if config.Password != "" {
			userPass += ":" + config.Password
		}
		userPass += "@"
	}
	return fmt.Sprintf(
		"postgres://%s%s:%d/%s?%s",
		userPass,
		config.Host,
		config.Port,
		config.DBName,
		config.JoinOptions("=", "&"),
	)
}

type SQLite3 struct{}

func (SQLite3) DSN(config DBConfig) string {
	if config.Host == "" || config.DBName == "" {
		return ":memory:"
	}
	return fmt.Sprintf("file:%s?%s", config.DBName, config.JoinOptions("=", "&"))
}

func (s SQLite3) defaultVal(def, val string, quote bool) string {
	if val == "" {
		val = def
	}
	if quote {
		if strings.Contains(val, "'") {
			val = strings.Replace(val, "'", "''", -1)
		}
		val = `'` + val + `'`
	}
	return val
}

func (s SQLite3) Type(typ, precision, val string) (dbtyp, defval string, err error) {
	switch typ {
	case "bool",
		"int",
		"int8",
		"int16",
		"int32",
		"int64",
		"uint",
		"uint8",
		"uint16",
		"uint32",
		"uint64":
		return "INTEGER", s.defaultVal("0", val, false), nil
	case "float32",
		"float64":
		return "FLOAT", s.defaultVal("0", val, false), nil
	case "string",
		"text",
		"char":
		return "TEXT", s.defaultVal("", val, true), nil
	default:
		return "", "", fmt.Errorf("sqlite3: unsupported type: %s", typ)
	}
}
<|endoftext|>


<commit_before>package gorp

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

// The Dialect interface encapsulates behaviors that differ across
// SQL databases.  At present the Dialect is only used by CreateTables()
// but this could change in the future
type Dialect interface {

	// ToSqlType returns the SQL column type to use when creating a
	// table of the given Go Type.  maxsize can be used to switch based on
	// size.  For example, in MySQL []byte could map to BLOB, MEDIUMBLOB,
	// or LONGBLOB depending on the maxsize
	ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string

	// string to append to primary key column definitions
	AutoIncrStr() string

	// string to append to "create table" statement for vendor specific
	// table attributes
	CreateTableSuffix() string

	LastInsertId(res *sql.Result, table *TableMap, exec SqlExecutor) (int64, error)

	// bind variable string to use when forming SQL statements
	// in many dbs it is "?", but Postgres appears to use $1
	//
	// i is a zero based index of the bind variable in this statement
	//
	BindVar(i int) string
}

///////////////////////////////////////////////////////
// sqlite3 //
/////////////

type SqliteDialect struct {
	suffix   string
}

func (d SqliteDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
	switch val.Kind() {
	case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
		return "integer"
	case reflect.Float64, reflect.Float32:
		return "real"
	case reflect.Slice:
		if val.Elem().Kind() == reflect.Uint8 {
			return "blob"
		}
	}

	switch val.Name() {
	case "NullableInt64":
		return "integer"
	case "NullableFloat64":
		return "real"
	case "NullableBool":
		return "integer"
	case "NullableBytes":
		return "blob"
	}

	if maxsize < 1 {
		maxsize = 255
	}
	return fmt.Sprintf("varchar(%d)", maxsize)
}

// Returns autoincrement
func (d SqliteDialect) AutoIncrStr() string {
	return "autoincrement"
}

// Returns suffix
func (d SqliteDialect) CreateTableSuffix() string {
	return d.suffix
}

// Returns "?"
func (d SqliteDialect) BindVar(i int) string {
	return "?"
}

func (d SqliteDialect) LastInsertId(res *sql.Result, table *TableMap, exec SqlExecutor) (int64, error) {
	return (*res).LastInsertId()
}

///////////////////////////////////////////////////////
// PostgreSQL //
////////////////

type PostgresDialect struct {
	suffix string
}

func (d PostgresDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
	switch val.Kind() {
	case reflect.Int, reflect.Int16, reflect.Int32:
		if isAutoIncr {
			return "serial"
		}
		return "integer"
	case reflect.Int64:
		if isAutoIncr {
			return "serial"
		}
		return "bigint"
	case reflect.Float64, reflect.Float32:
		return "real"
	case reflect.Slice:
		if val.Elem().Kind() == reflect.Uint8 {
			return "bytea"
		}
	}

	switch val.Name() {
	case "NullableInt64":
		return "bigint"
	case "NullableFloat64":
		return "double"
	case "NullableBool":
		return "smallint"
	case "NullableBytes":
		return "bytea"
	}

	if maxsize < 1 {
		maxsize = 255
	}
	return fmt.Sprintf("varchar(%d)", maxsize)
}

// Returns empty string
func (d PostgresDialect) AutoIncrStr() string {
	return ""
}

// Returns suffix
func (d PostgresDialect) CreateTableSuffix() string {
	return d.suffix
}

// Returns "$(i+1)"
func (d PostgresDialect) BindVar(i int) string {
	return fmt.Sprintf("$%d", i+1)
}

func (d PostgresDialect) LastInsertId(res *sql.Result, table *TableMap, exec SqlExecutor) (int64, error) {
	sql := fmt.Sprintf("select currval('%s_%s_seq')", table.TableName, table.keys[0].ColumnName)
	rows, err := exec.query(sql)
	if err != nil {
		return 0, err
	}
	defer rows.Close()

	if rows.Next() {
		var dest int64
		err = rows.Scan(&dest)
		return dest, nil
	}
	return 0, errors.New(fmt.Sprintf("PostgresDialect: %s did not return a row", sql))
}

///////////////////////////////////////////////////////
// MySQL //
///////////

// Implementation of Dialect for MySQL databases.
type MySQLDialect struct {

	// Engine is the storage engine to use "InnoDB" vs "MyISAM" for example
	Engine string

	// Encoding is the character encoding to use for created tables
	Encoding string
}

func (m MySQLDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
	switch val.Kind() {
	case reflect.Int, reflect.Int16, reflect.Int32:
		return "int"
	case reflect.Int64:
		return "bigint"
	case reflect.Float64, reflect.Float32:
		return "double"
	case reflect.Slice:
		if val.Elem().Kind() == reflect.Uint8 {
			return "mediumblob"
		}
	}

	switch val.Name() {
	case "NullableInt64":
		return "bigint"
	case "NullableFloat64":
		return "double"
	case "NullableBool":
		return "tinyint"
	case "NullableBytes":
		return "mediumblob"
	}

	if maxsize < 1 {
		maxsize = 255
	}
	return fmt.Sprintf("varchar(%d)", maxsize)
}

// Returns auto_increment
func (m MySQLDialect) AutoIncrStr() string {
	return "auto_increment"
}

// Returns engine=%s charset=%s  based on values stored on struct
func (m MySQLDialect) CreateTableSuffix() string {
	return fmt.Sprintf(" engine=%s charset=%s", m.Engine, m.Encoding)
}

// Returns "?"
func (m MySQLDialect) BindVar(i int) string {
	return "?"
}

func (m MySQLDialect) LastInsertId(res *sql.Result, table *TableMap, exec SqlExecutor) (int64, error) {
	return (*res).LastInsertId()
}
<commit_msg>removed unused time import<commit_after>package gorp

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

// The Dialect interface encapsulates behaviors that differ across
// SQL databases.  At present the Dialect is only used by CreateTables()
// but this could change in the future
type Dialect interface {

	// ToSqlType returns the SQL column type to use when creating a
	// table of the given Go Type.  maxsize can be used to switch based on
	// size.  For example, in MySQL []byte could map to BLOB, MEDIUMBLOB,
	// or LONGBLOB depending on the maxsize
	ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string

	// string to append to primary key column definitions
	AutoIncrStr() string

	// string to append to "create table" statement for vendor specific
	// table attributes
	CreateTableSuffix() string

	LastInsertId(res *sql.Result, table *TableMap, exec SqlExecutor) (int64, error)

	// bind variable string to use when forming SQL statements
	// in many dbs it is "?", but Postgres appears to use $1
	//
	// i is a zero based index of the bind variable in this statement
	//
	BindVar(i int) string
}

///////////////////////////////////////////////////////
// sqlite3 //
/////////////

type SqliteDialect struct {
	suffix string
}

func (d SqliteDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
	switch val.Kind() {
	case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
		return "integer"
	case reflect.Float64, reflect.Float32:
		return "real"
	case reflect.Slice:
		if val.Elem().Kind() == reflect.Uint8 {
			return "blob"
		}
	}

	switch val.Name() {
	case "NullableInt64":
		return "integer"
	case "NullableFloat64":
		return "real"
	case "NullableBool":
		return "integer"
	case "NullableBytes":
		return "blob"
	}

	if maxsize < 1 {
		maxsize = 255
	}
	return fmt.Sprintf("varchar(%d)", maxsize)
}

// Returns autoincrement
func (d SqliteDialect) AutoIncrStr() string {
	return "autoincrement"
}

// Returns suffix
func (d SqliteDialect) CreateTableSuffix() string {
	return d.suffix
}

// Returns "?"
func (d SqliteDialect) BindVar(i int) string {
	return "?"
}

func (d SqliteDialect) LastInsertId(res *sql.Result, table *TableMap, exec SqlExecutor) (int64, error) {
	return (*res).LastInsertId()
}

///////////////////////////////////////////////////////
// PostgreSQL //
////////////////

type PostgresDialect struct {
	suffix string
}

func (d PostgresDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
	switch val.Kind() {
	case reflect.Int, reflect.Int16, reflect.Int32:
		if isAutoIncr {
			return "serial"
		}
		return "integer"
	case reflect.Int64:
		if isAutoIncr {
			return "serial"
		}
		return "bigint"
	case reflect.Float64, reflect.Float32:
		return "real"
	case reflect.Slice:
		if val.Elem().Kind() == reflect.Uint8 {
			return "bytea"
		}
	}

	switch val.Name() {
	case "NullableInt64":
		return "bigint"
	case "NullableFloat64":
		return "double"
	case "NullableBool":
		return "smallint"
	case "NullableBytes":
		return "bytea"
	}

	if maxsize < 1 {
		maxsize = 255
	}
	return fmt.Sprintf("varchar(%d)", maxsize)
}

// Returns empty string
func (d PostgresDialect) AutoIncrStr() string {
	return ""
}

// Returns suffix
func (d PostgresDialect) CreateTableSuffix() string {
	return d.suffix
}

// Returns "$(i+1)"
func (d PostgresDialect) BindVar(i int) string {
	return fmt.Sprintf("$%d", i+1)
}

func (d PostgresDialect) LastInsertId(res *sql.Result, table *TableMap, exec SqlExecutor) (int64, error) {
	sql := fmt.Sprintf("select currval('%s_%s_seq')", table.TableName, table.keys[0].ColumnName)
	rows, err := exec.query(sql)
	if err != nil {
		return 0, err
	}
	defer rows.Close()

	if rows.Next() {
		var dest int64
		err = rows.Scan(&dest)
		return dest, nil
	}
	return 0, errors.New(fmt.Sprintf("PostgresDialect: %s did not return a row", sql))
}

///////////////////////////////////////////////////////
// MySQL //
///////////

// Implementation of Dialect for MySQL databases.
type MySQLDialect struct {

	// Engine is the storage engine to use "InnoDB" vs "MyISAM" for example
	Engine string

	// Encoding is the character encoding to use for created tables
	Encoding string
}

func (m MySQLDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
	switch val.Kind() {
	case reflect.Int, reflect.Int16, reflect.Int32:
		return "int"
	case reflect.Int64:
		return "bigint"
	case reflect.Float64, reflect.Float32:
		return "double"
	case reflect.Slice:
		if val.Elem().Kind() == reflect.Uint8 {
			return "mediumblob"
		}
	}

	switch val.Name() {
	case "NullableInt64":
		return "bigint"
	case "NullableFloat64":
		return "double"
	case "NullableBool":
		return "tinyint"
	case "NullableBytes":
		return "mediumblob"
	}

	if maxsize < 1 {
		maxsize = 255
	}
	return fmt.Sprintf("varchar(%d)", maxsize)
}

// Returns auto_increment
func (m MySQLDialect) AutoIncrStr() string {
	return "auto_increment"
}

// Returns engine=%s charset=%s  based on values stored on struct
func (m MySQLDialect) CreateTableSuffix() string {
	return fmt.Sprintf(" engine=%s charset=%s", m.Engine, m.Encoding)
}

// Returns "?"
func (m MySQLDialect) BindVar(i int) string {
	return "?"
}

func (m MySQLDialect) LastInsertId(res *sql.Result, table *TableMap, exec SqlExecutor) (int64, error) {
	return (*res).LastInsertId()
}
<|endoftext|>


<commit_before>//
// handlers
//
// @author darryl.west <darryl.west@raincitysoftware.com>
// @created 2017-07-01 12:57:59
//

package geozipdb

import (
	"fmt"
	"strconv"
	"strings"
    "github.com/julienschmidt/httprouter"
    "net/http"
)

// Keytype - keys are strings
type Keytype string

// Ziptype - zipcodes are strings
type Ziptype string

// Coord - lat/lng
type Coord struct {
	Lat float64
	Lng float64
}

// ZipcodeCoord - zipcode and coord lat/lng
type ZipcodeCoord struct {
	Zipcode Ziptype
	Coord
}

// Service - the primary service struct
type Service struct {
    config *Config
}

var keyMap = make(map[Keytype][]*ZipcodeCoord)
var zipMap = make(map[Ziptype]*Coord)
var initialized = false

// CreateKey - create a key from lat/lng
func (svc Service) CreateKey(lat, lng float64) Keytype {
	llat := int(lat * 10)
	llng := int(lng * 10)

	return Keytype(fmt.Sprintf("%d:%d", llat, llng))
}

// Initialize - initialize the data
func (svc Service) Initialize() {
	fmt.Println("initialize the database...")
	lines := strings.Split(geodata, "\n")

	fmt.Printf("processing %d data rows...\n", len(lines))
	for i := 0; i < len(lines); i++ {
		fields := strings.Split(lines[i], ",")
		zipcode := Ziptype(fields[0])

		lat, _ := strconv.ParseFloat(fields[1], 64)
		lng, _ := strconv.ParseFloat(fields[2], 64)

		key := svc.CreateKey(lat, lng)

		coord := Coord{lat, lng}
		zipMap[zipcode] = &coord

		zcoord := ZipcodeCoord{zipcode, coord}
		keyMap[key] = append(keyMap[key], &zcoord)
	}

	fmt.Printf("processed %d rows...\n", len(lines))
}

// CoordFromZip - return the coordinate of this zipcode
func (svc Service) CoordFromZip(code Ziptype) (*Coord, bool) {
	v, ok := zipMap[code]
	return v, ok
}

// ZipListFromCoord - return a list of zip codes that are near the coordinates
func (svc Service) ZipListFromCoord(coord *Coord) ([]*ZipcodeCoord, bool) {
	key := svc.CreateKey(coord.Lat, coord.Lng)
	v, ok := keyMap[key]

	return v, ok
}

// NewService - create the service based on config
func NewService(config *Config) *Service {
	svc := new(Service)

    svc.config = config

	return svc
}

// return the zip list for a given coordinate lat/lng
func (svc Service) ziplistHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    fmt.Fprintf(w, "p %s", ps.ByName("coord"))
}

// return the coordinates for a given zip
func (svc Service) coordHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    fmt.Fprintf(w, "p %s", ps.ByName("zip"))
}

// Start - initialize the data and start the listener service
func (svc Service) Start() {
	if initialized == false {
		svc.Initialize()
	}

    router := httprouter.New()

    router.GET("/v1/zipdb/coord/:zip", svc.coordHandler)
    router.GET("/v1/zipdb/ziplist/:coord", svc.ziplistHandler)

    port := svc.config.Port
    host := fmt.Sprintf(":%d", port)
    fmt.Printf("listening on port %d", port)

    err := http.ListenAndServe(host, router)
    if err != nil {
        panic(err)
    }
}

<commit_msg>implemented coords for zip code<commit_after>//
// handlers
//
// @author darryl.west <darryl.west@raincitysoftware.com>
// @created 2017-07-01 12:57:59
//

package geozipdb

import (
	"fmt"
	"github.com/julienschmidt/httprouter"
	"net/http"
	"strconv"
	"strings"
)

// Keytype - keys are strings
type Keytype string

// Ziptype - zipcodes are strings
type Ziptype string

// Coord - lat/lng
type Coord struct {
	Lat float64
	Lng float64
}

// ZipcodeCoord - zipcode and coord lat/lng
type ZipcodeCoord struct {
	Zipcode Ziptype
	Coord
}

// Service - the primary service struct
type Service struct {
	config *Config
}

var keyMap = make(map[Keytype][]*ZipcodeCoord)
var zipMap = make(map[Ziptype]*Coord)
var initialized = false

// CreateKey - create a key from lat/lng
func (svc Service) CreateKey(lat, lng float64) Keytype {
	llat := int(lat * 10)
	llng := int(lng * 10)

	return Keytype(fmt.Sprintf("%d:%d", llat, llng))
}

// Initialize - initialize the data
func (svc Service) Initialize() {
	fmt.Println("initialize the database...")
	lines := strings.Split(geodata, "\n")

	fmt.Printf("processing %d data rows...\n", len(lines))
	for i := 0; i < len(lines); i++ {
		fields := strings.Split(lines[i], ",")
		zipcode := Ziptype(fields[0])

		lat, _ := strconv.ParseFloat(fields[1], 64)
		lng, _ := strconv.ParseFloat(fields[2], 64)

		key := svc.CreateKey(lat, lng)

		coord := Coord{lat, lng}
		zipMap[zipcode] = &coord

		zcoord := ZipcodeCoord{zipcode, coord}
		keyMap[key] = append(keyMap[key], &zcoord)
	}

	fmt.Printf("processed %d rows...\n", len(lines))
}

// CoordFromZip - return the coordinate of this zipcode
func (svc Service) CoordFromZip(code Ziptype) (*Coord, bool) {
	v, ok := zipMap[code]
	return v, ok
}

// ZipListFromCoord - return a list of zip codes that are near the coordinates
func (svc Service) ZipListFromCoord(coord *Coord) ([]*ZipcodeCoord, bool) {
	key := svc.CreateKey(coord.Lat, coord.Lng)
	v, ok := keyMap[key]

	return v, ok
}

// NewService - create the service based on config
func NewService(config *Config) *Service {
	svc := new(Service)

	svc.config = config

	return svc
}

// return the zip list for a given coordinate lat/lng
func (svc Service) ziplistHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	p := ps.ByName("coords")
	fmt.Printf("find zip list for coords %s\n", p)

	fmt.Fprintf(w, "p %s\n\r", ps.ByName("coord"))
}

// return the coordinates for a given zip
func (svc Service) coordHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	zipcode := ps.ByName("zip")
	fmt.Printf("find coords for zip %s\n", zipcode)

	if coord, ok := svc.CoordFromZip(Ziptype(zipcode)); ok {
		fmt.Fprintf(w, "coord:%f/%f\n\r", coord.Lat, coord.Lng)
	} else {
		fmt.Fprintf(w, "not found for zip %s\n\r", zipcode)
	}
}

// Start - initialize the data and start the listener service
func (svc Service) Start() {
	if initialized == false {
		svc.Initialize()
	}

	router := httprouter.New()

	router.GET("/v1/zipdb/coord/:zip", svc.coordHandler)
	router.GET("/v1/zipdb/ziplist/:coord", svc.ziplistHandler)

	port := svc.config.Port
	host := fmt.Sprintf(":%d", port)
	fmt.Printf("listening on port %d\n", port)

	err := http.ListenAndServe(host, router)
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/golang/groupcache/lru"
	"github.com/zyedidia/tcell"
	"github.com/zyedidia/tcell/encoding"
)

var topRow = 0
var leftColumn = 0
var height = 0
var width = 0
var offset = 0
var s tcell.Screen

func createDisplay() {
	loadDark()
	os.Setenv("TERM", "xterm-truecolor")
	sc, e := tcell.NewScreen()
	s = sc
	if e != nil {
		fmt.Fprintf(os.Stderr, "%v\n", e)
		os.Exit(1)
	}
	encoding.Register()

	if e = s.Init(); e != nil {
		fmt.Fprintf(os.Stderr, "%v\n", e)
		os.Exit(1)
	}

	s.SetStyle(tcell.StyleDefault.
		Foreground(tcell.ColorWhite).
		Background(tcell.ColorDefault))
	s.Clear()
	s.Show()
	w, h := s.Size()
	width = w
	height = h
}

//Display renders the editor
func Display(buffer *Buffer) {
	createDisplay()
	quit := make(chan struct{})
	b := make(chan [][]Backing)
	evName := ""
	go func() {
		for {
			t := time.Now()
			offset = drawRuler(topRow, height-1, s)
			var w = width - offset + 1
			lines := buffer.GetLines(topRow, leftColumn, height-1, w)
			go drawBufferHighlight(b, topRow, lines)

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), 0, height-1, "                                                                                ")
			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), 0, height-1, buffer.filename)

			var text = strconv.Itoa(GetCursor().loc.row+1) + ":" + strconv.Itoa(GetCursor().loc.column+1) +
				"/" + strconv.Itoa(buffer.Len()) + ":" + strconv.Itoa(len(buffer.GetLine(GetCursor().loc.row)))

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), width-len(text), height-1, text)

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), width-len(text)-2-len(evName), height-1, evName)

			drawBack(<-b)
			time2 := time.Now().Sub(t).String()
			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), len(buffer.filename)+1, height-1, time2)

			s.Show()
			ev := s.PollEvent()
			switch ev := ev.(type) {
			case *tcell.EventKey:
				switch ev.Key() {
				case tcell.KeyDown:
					c := GetCursor()
					c.MoveDown()
				case tcell.KeyUp:
					c := GetCursor()
					c.MoveUp()
				case tcell.KeyLeft:
					c := GetCursor()
					c.MoveLeft()
				case tcell.KeyRight:
					c := GetCursor()
					c.MoveRight()
				case tcell.KeyPgDn:
					c := GetCursor()
					c.MovePageDown()
				case tcell.KeyPgUp:
					c := GetCursor()
					c.MovePageUp()
				case tcell.KeyEscape:
					close(quit)
					return
				case tcell.KeyCtrlD:
					c := GetCursor()
					buffer.Insert(c.loc.row, 0, buffer.GetLine(c.loc.row))
					c.MoveDown()
				case tcell.KeyCtrlY:
					c := GetCursor()
					buffer.RemoveRow(c.loc.row)
				case tcell.KeyDelete:
					c := GetCursor()
					buffer.Delete(c.loc.row, c.loc.column)
				case tcell.KeyBackspace2:
					c := GetCursor()
					l := buffer.Backspace(c.loc.row, c.loc.column)
					if c.loc.column == 0 && c.loc.row > 0 {
						c.MoveUp()
						c.MoveToEndOfLine()
						for index := 0; index < l; index++ {
							c.MoveLeft()
						}
					} else {
						c.MoveLeft()
					}
				case tcell.KeyCtrlS:
					buffer.Save()
				case tcell.KeyTab:
					c := GetCursor()
					buffer.Insert(c.loc.row, c.loc.column, []rune(string('\t')))
					c.MoveRight()
				case tcell.KeyRune:
					evName = ev.Name()
					c := GetCursor()
					buffer.Insert(c.loc.row, c.loc.column, []rune(string(ev.Rune())))
					c.MoveRight()
				case tcell.KeyEnter:
					c := GetCursor()
					//buffer.Insert(c.loc.row, c.loc.column, []rune(string(ev.Rune())))
					buffer.InsertEnter(c.loc.row, c.loc.column)
					c.MoveDown()
					c.MoveStartOfLine()
				default:
					evName = ev.Name()
				}

			case *tcell.EventResize:
				s.Sync()
			}
		}
	}()

	<-quit

	s.Fini()
}

// Backing backs the display so we don't overwrite runes unnessecary
type Backing struct {
	value rune
	style tcell.Style
}

var cache = lru.New(1000)

func drawBufferHighlight(b chan [][]Backing, topRow int, lines [][]rune) {
	backing := highlightLines(lines)
	backing = paintCursor(backing, topRow)
	b <- backing
}
func drawBuffer(b chan [][]Backing, topRow int, lines [][]rune) {
	backing := drawLines(lines)
	backing = paintCursor(backing, topRow)
	b <- backing
}
func paintCursor(backing [][]Backing, topRow int) [][]Backing {
	c := GetCursor()
	if len(backing) > c.loc.row-topRow && len(backing[c.loc.row-topRow]) > c.loc.column-leftColumn {
		for i := range backing[c.loc.row-topRow] {
			backing[c.loc.row-topRow][i].style = backing[c.loc.row-topRow][i].style.Background(tcell.GetColor("#282828"))
		}
		// No cursor on enter
		if c.loc.column != -1 {
			if backing[c.loc.row-topRow][c.loc.column-leftColumn].value == '\n' {
				backing[c.loc.row-topRow][c.loc.column-leftColumn+1].style = tcell.StyleDefault.Reverse(true)
			} else {
				backing[c.loc.row-topRow][c.loc.column-leftColumn].style = tcell.StyleDefault.Reverse(true)
			}
		}
	}
	return backing
}
func highlightLines(lines [][]rune) [][]Backing {
	w, h := s.Size()
	backing := make([][]Backing, h-1, h-1)
	for i := range backing {
		backing[i] = make([]Backing, w, w)
	}

	var style = getThemeColor("editor")
	var syn = Syntax{}
	for _, s := range syntaxDef {
		for _, f := range s.FileTypes {
			if strings.HasSuffix(buffer.filename, f) {
				syn = s
				break
			}
		}
	}
	for i := range backing {
		if len(lines)-1 < i {
			continue
		}
		jj := 0
		tokens := []Token{}
		key := string(lines[i])
		if val, ok := cache.Get(key); !ok {
			val = highlightLine(lines[i], syn)
			cache.Add(key, val)
			tokens = val.([]Token)
		} else {
			tokens = val.([]Token)
		}
		for _, r := range lines[i] {
			found := false
			for _, token := range tokens {
				if jj >= token.Loc[0] && jj < token.Loc[1] {
					backing[i][jj].style = getThemeColor(token.Scope...)
					found = true
					break
				}
			}

			backing[i][jj].value = r
			if !found {
				backing[i][jj].style = style
			}
			jj++
		}
		for index := len(lines[i]); index < width; index++ {
			backing[i][index].value = ' '
			backing[i][index].style = style
		}
	}
	return backing
}
func drawLines(lines [][]rune) [][]Backing {
	w, h := s.Size()
	backing := make([][]Backing, h-1, h-1)
	for i := range backing {
		backing[i] = make([]Backing, w, w)
	}

	var style = getThemeColor("editor")

	for i := range backing {
		if len(lines)-1 < i {
			continue
		}
		jj := 0
		for _, r := range lines[i] {
			backing[i][jj].value = r
			backing[i][jj].style = style
			jj++
		}
		for index := len(lines[i]); index < width; index++ {
			backing[i][index].value = ' '
			backing[i][index].style = style
		}
	}
	return backing
}
func drawBack(backing [][]Backing) {
	for ir, row := range backing {
		x := offset
		for _, column := range row {
			if column.value == '\t' {
				s.SetContent(x, ir, ' ', nil, column.style)
				s.SetContent(x+1, ir, ' ', nil, column.style)
				s.SetContent(x+2, ir, ' ', nil, column.style)
				s.SetContent(x+3, ir, ' ', nil, column.style)
				x += 4
				continue
			}
			if column.value == '\n' {
				continue
			}
			s.SetContent(x, ir, column.value, nil, column.style)
			x++
		}
	}
}

func drawRuler(topRow, h int, s tcell.Screen) int {
	length := strconv.Itoa(len(fmt.Sprintf("%d", topRow+h)))
	ret := len(fmt.Sprintf("%d", topRow+h))
	style := getThemeColor("editor.ruler")
	for index := 0; index < h; index++ {
		puts(s, style, 0, index, fmt.Sprintf("%"+length+"d ", topRow+index+1))
	}
	return ret + 1
}
<commit_msg>Paste<commit_after>package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/golang/groupcache/lru"
	"github.com/zyedidia/tcell"
	"github.com/zyedidia/tcell/encoding"
)

var topRow = 0
var leftColumn = 0
var height = 0
var width = 0
var offset = 0
var s tcell.Screen

func createDisplay() {
	loadDark()
	os.Setenv("TERM", "xterm-truecolor")
	sc, e := tcell.NewScreen()
	s = sc
	if e != nil {
		fmt.Fprintf(os.Stderr, "%v\n", e)
		os.Exit(1)
	}
	encoding.Register()

	if e = s.Init(); e != nil {
		fmt.Fprintf(os.Stderr, "%v\n", e)
		os.Exit(1)
	}

	s.SetStyle(tcell.StyleDefault.
		Foreground(tcell.ColorWhite).
		Background(tcell.ColorDefault))
	s.Clear()
	s.Show()
	w, h := s.Size()
	width = w
	height = h
}

//Display renders the editor
func Display(buffer *Buffer) {
	createDisplay()
	quit := make(chan struct{})
	b := make(chan [][]Backing)
	evName := ""
	go func() {
		for {
			t := time.Now()
			offset = drawRuler(topRow, height-1, s)
			var w = width - offset + 1
			lines := buffer.GetLines(topRow, leftColumn, height-1, w)
			go drawBufferHighlight(b, topRow, lines)

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), 0, height-1, "                                                                                ")
			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), 0, height-1, buffer.filename)

			var text = strconv.Itoa(GetCursor().loc.row+1) + ":" + strconv.Itoa(GetCursor().loc.column+1) +
				"/" + strconv.Itoa(buffer.Len()) + ":" + strconv.Itoa(len(buffer.GetLine(GetCursor().loc.row)))

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), width-len(text), height-1, text)

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), width-len(text)-2-len(evName), height-1, evName)

			drawBack(<-b)
			time2 := time.Now().Sub(t).String()
			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), len(buffer.filename)+1, height-1, time2)

			s.Show()
			ev := s.PollEvent()
			switch ev := ev.(type) {
			case *tcell.EventKey:
				switch ev.Key() {
				case tcell.KeyDown:
					c := GetCursor()
					c.MoveDown()
				case tcell.KeyUp:
					c := GetCursor()
					c.MoveUp()
				case tcell.KeyLeft:
					c := GetCursor()
					c.MoveLeft()
				case tcell.KeyRight:
					c := GetCursor()
					c.MoveRight()
				case tcell.KeyPgDn:
					c := GetCursor()
					c.MovePageDown()
				case tcell.KeyPgUp:
					c := GetCursor()
					c.MovePageUp()
				case tcell.KeyEscape:
					close(quit)
					return
				case tcell.KeyCtrlD:
					c := GetCursor()
					buffer.Insert(c.loc.row, 0, buffer.GetLine(c.loc.row))
					c.MoveDown()
				case tcell.KeyCtrlY:
					c := GetCursor()
					buffer.RemoveRow(c.loc.row)
				case tcell.KeyDelete:
					c := GetCursor()
					buffer.Delete(c.loc.row, c.loc.column)
				case tcell.KeyBackspace2:
					c := GetCursor()
					l := buffer.Backspace(c.loc.row, c.loc.column)
					if c.loc.column == 0 && c.loc.row > 0 {
						c.MoveUp()
						c.MoveToEndOfLine()
						for index := 0; index < l; index++ {
							c.MoveLeft()
						}
					} else {
						c.MoveLeft()
					}
				case tcell.KeyCtrlS:
					buffer.Save()
				case tcell.KeyTab:
					c := GetCursor()
					buffer.Insert(c.loc.row, c.loc.column, []rune(string('\t')))
					c.MoveRight()
				case tcell.KeyRune:
					evName = ev.Name()
					c := GetCursor()
					buffer.Insert(c.loc.row, c.loc.column, []rune(string(ev.Rune())))
					c.MoveRight()
				case tcell.KeyEnter:
					c := GetCursor()
					//buffer.Insert(c.loc.row, c.loc.column, []rune(string(ev.Rune())))
					buffer.InsertEnter(c.loc.row, c.loc.column)
					c.MoveDown()
					c.MoveStartOfLine()
				default:
					evName = ev.Name()
				}
			case *tcell.EventPaste:
				clip := ev.Text()
				c := GetCursor()
				buffer.Insert(c.loc.row, c.loc.column, []rune(clip))
				for range clip {
					c.MoveRight()
				}

			case *tcell.EventResize:
				s.Sync()
			}
		}
	}()

	<-quit

	s.Fini()
}

// Backing backs the display so we don't overwrite runes unnessecary
type Backing struct {
	value rune
	style tcell.Style
}

var cache = lru.New(1000)

func drawBufferHighlight(b chan [][]Backing, topRow int, lines [][]rune) {
	backing := highlightLines(lines)
	backing = paintCursor(backing, topRow)
	b <- backing
}
func drawBuffer(b chan [][]Backing, topRow int, lines [][]rune) {
	backing := drawLines(lines)
	backing = paintCursor(backing, topRow)
	b <- backing
}
func paintCursor(backing [][]Backing, topRow int) [][]Backing {
	c := GetCursor()
	if len(backing) > c.loc.row-topRow && len(backing[c.loc.row-topRow]) > c.loc.column-leftColumn {
		for i := range backing[c.loc.row-topRow] {
			backing[c.loc.row-topRow][i].style = backing[c.loc.row-topRow][i].style.Background(tcell.GetColor("#282828"))
		}
		// No cursor on enter
		if c.loc.column != -1 {
			if backing[c.loc.row-topRow][c.loc.column-leftColumn].value == '\n' {
				backing[c.loc.row-topRow][c.loc.column-leftColumn+1].style = tcell.StyleDefault.Reverse(true)
			} else {
				backing[c.loc.row-topRow][c.loc.column-leftColumn].style = tcell.StyleDefault.Reverse(true)
			}
		}
	}
	return backing
}
func highlightLines(lines [][]rune) [][]Backing {
	w, h := s.Size()
	backing := make([][]Backing, h-1, h-1)
	for i := range backing {
		backing[i] = make([]Backing, w, w)
	}

	var style = getThemeColor("editor")
	var syn = Syntax{}
	for _, s := range syntaxDef {
		for _, f := range s.FileTypes {
			if strings.HasSuffix(buffer.filename, f) {
				syn = s
				break
			}
		}
	}
	for i := range backing {
		if len(lines)-1 < i {
			continue
		}
		jj := 0
		tokens := []Token{}
		key := string(lines[i])
		if val, ok := cache.Get(key); !ok {
			val = highlightLine(lines[i], syn)
			cache.Add(key, val)
			tokens = val.([]Token)
		} else {
			tokens = val.([]Token)
		}
		for _, r := range lines[i] {
			found := false
			for _, token := range tokens {
				if jj >= token.Loc[0] && jj < token.Loc[1] {
					backing[i][jj].style = getThemeColor(token.Scope...)
					found = true
					break
				}
			}

			backing[i][jj].value = r
			if !found {
				backing[i][jj].style = style
			}
			jj++
		}
		for index := len(lines[i]); index < width; index++ {
			backing[i][index].value = ' '
			backing[i][index].style = style
		}
	}
	return backing
}
func drawLines(lines [][]rune) [][]Backing {
	w, h := s.Size()
	backing := make([][]Backing, h-1, h-1)
	for i := range backing {
		backing[i] = make([]Backing, w, w)
	}

	var style = getThemeColor("editor")

	for i := range backing {
		if len(lines)-1 < i {
			continue
		}
		jj := 0
		for _, r := range lines[i] {
			backing[i][jj].value = r
			backing[i][jj].style = style
			jj++
		}
		for index := len(lines[i]); index < width; index++ {
			backing[i][index].value = ' '
			backing[i][index].style = style
		}
	}
	return backing
}
func drawBack(backing [][]Backing) {
	for ir, row := range backing {
		x := offset
		for _, column := range row {
			if column.value == '\t' {
				s.SetContent(x, ir, ' ', nil, column.style)
				s.SetContent(x+1, ir, ' ', nil, column.style)
				s.SetContent(x+2, ir, ' ', nil, column.style)
				s.SetContent(x+3, ir, ' ', nil, column.style)
				x += 4
				continue
			}
			if column.value == '\n' {
				continue
			}
			s.SetContent(x, ir, column.value, nil, column.style)
			x++
		}
	}
}

func drawRuler(topRow, h int, s tcell.Screen) int {
	length := strconv.Itoa(len(fmt.Sprintf("%d", topRow+h)))
	ret := len(fmt.Sprintf("%d", topRow+h))
	style := getThemeColor("editor.ruler")
	for index := 0; index < h; index++ {
		puts(s, style, 0, index, fmt.Sprintf("%"+length+"d ", topRow+index+1))
	}
	return ret + 1
}
<|endoftext|>


<commit_before>package model

// ErrorResponse is a response type returned when HTTP error is raised
type ErrorResponse struct {
	Message string `json:"message"`
	Error   string `json:"error,omitempty"`
}

// HealthCheckResponse is a response type returned from HealthCheckHandler
type HealthCheckResponse struct {
	Message string `json:"message"`
}

// CreateUserResponse is a response type returned from CreateUserHandler
type CreateUserResponse struct {
	Message string `json:"message"`
}

// LookupUserResponse is a response type returned from LookupUserHandler
type LookupUserResponse struct {
	User User `json:"user"`
}

// UpdateUserResponse is a resposne type returned from UpdateUserHandler
type UpdateUserResponse struct {
	Message string `json:"message"`
}

// DeleteUserResponse is a response type returned from DeleteUserHandler
type DeleteUserResponse struct {
	Message string `json:"message"`
}

// ListupUserResponse is a response type returned from ListupUserHandler
type ListupUserResponse struct {
	Users UserList `json:"user"`
}

// AuthResponse is a response type returned from AuthHandler
type AuthResponse struct {
	Message string `json:"message"`
	Token   string `json:"token"`
}

// GetAlgorithmResponse is a response type returned from GetAlgorithmHandler
type GetAlgorithmResponse struct {
	Algorithm string `json:"algorithm"`
}

// GetKeyResponse is a response type returned from GetKeyHandler
type GetKeyResponse struct {
	PublicKey string `json:"publickey"`
}
<commit_msg>add: verify handler response<commit_after>package model

// ErrorResponse is a response type returned when HTTP error is raised
type ErrorResponse struct {
	Message string `json:"message"`
	Error   string `json:"error,omitempty"`
}

// HealthCheckResponse is a response type returned from HealthCheckHandler
type HealthCheckResponse struct {
	Message string `json:"message"`
}

// CreateUserResponse is a response type returned from CreateUserHandler
type CreateUserResponse struct {
	Message string `json:"message"`
}

// LookupUserResponse is a response type returned from LookupUserHandler
type LookupUserResponse struct {
	User User `json:"user"`
}

// UpdateUserResponse is a resposne type returned from UpdateUserHandler
type UpdateUserResponse struct {
	Message string `json:"message"`
}

// DeleteUserResponse is a response type returned from DeleteUserHandler
type DeleteUserResponse struct {
	Message string `json:"message"`
}

// ListupUserResponse is a response type returned from ListupUserHandler
type ListupUserResponse struct {
	Users UserList `json:"user"`
}

// AuthResponse is a response type returned from AuthHandler
type AuthResponse struct {
	Message string `json:"message"`
	Token   string `json:"token"`
}

// GetAlgorithmResponse is a response type returned from GetAlgorithmHandler
type GetAlgorithmResponse struct {
	Algorithm string `json:"algorithm"`
}

// GetKeyResponse is a response type returned from GetKeyHandler
type GetKeyResponse struct {
	PublicKey string `json:"publickey"`
}

// VerifyResponse is a response type returned from VerifyHandler
type VerifyResponse struct {
	Status bool `json:"status"`
}
<|endoftext|>