<commit_before>package main // import "github.com/omie/shruti-providers/hn"

import (
	"log"
	"os"
	"strconv"
	"time"

	shruti "github.com/omie/shruti-go"
	"github.com/peterhellberg/hn"
)

const (
	PROVIDER_NAME = "hackernews"
)

var (
	sClient shruti.Client
	visited map[int]bool
)

func Register() (err error) {

	p := shruti.Provider{Name: PROVIDER_NAME,
		DisplayName: "Hacker News",
		Description: "updates around the globe, mostly tech related",
		WebURL:      "https://news.ycombinator.com/",
		IconURL:     "https://news.ycombinator.com/favicon.ico",
		Voice:       "Emma",
	}

	err = sClient.RegisterProvider(p)

	return

}

func doWork() (err error) {
	hn := hn.NewClient(nil)

	for {
		ids, err := hn.TopStories()
		if err != nil {
			return err
		}

		for _, id := range ids[:10] {
			item, err := hn.Item(id)
			if _, ok := visited[id]; ok {
				continue
			}
			if err == nil {
				n := shruti.Notification{
					Title:        item.Title,
					Url:          item.URL,
					Key:          PROVIDER_NAME + strconv.Itoa(item.ID),
					Priority:     shruti.PRIO_MED,
					Action:       shruti.ACT_PUSH,
					ProviderName: PROVIDER_NAME,
				}
				err = sClient.PushNotification(n)
				msg := "submitted"
				if err != nil {
					msg = err.Error()
				}
				log.Println(msg)
				visited[id] = true
			}
		}
		log.Println("Sleeping")
		time.Sleep(5 * time.Second)
		log.Println("woke up")
	}
}

func main() {

	log.Println("Starting Hacker News Scraper")

	shrutiServer := os.Getenv("SHRUTI_SERVER")
	if shrutiServer == "" {
		log.Println("SHRUTI_SERVER not set")
		return
	}

	sClient = shruti.Client{"http://", shrutiServer}

	err := Register()
	if err != nil {
		log.Println(err)
		return
	}

	visited = make(map[int]bool)

	err = doWork()
	if err != nil {
		log.Println(err)
	}
}
<commit_msg>fixed hn provider refresh time<commit_after>package main // import "github.com/omie/shruti-providers/hn"

import (
	"log"
	"os"
	"strconv"
	"time"

	shruti "github.com/omie/shruti-go"
	"github.com/peterhellberg/hn"
)

const (
	PROVIDER_NAME = "hackernews"
)

var (
	sClient shruti.Client
	visited map[int]bool
)

func Register() (err error) {

	p := shruti.Provider{Name: PROVIDER_NAME,
		DisplayName: "Hacker News",
		Description: "updates around the globe, mostly tech related",
		WebURL:      "https://news.ycombinator.com/",
		IconURL:     "https://news.ycombinator.com/favicon.ico",
		Voice:       "Emma",
	}

	err = sClient.RegisterProvider(p)

	return

}

func doWork() (err error) {
	hn := hn.NewClient(nil)

	for {
		ids, err := hn.TopStories()
		if err != nil {
			return err
		}

		for _, id := range ids[:10] {
			item, err := hn.Item(id)
			if _, ok := visited[id]; ok {
				continue
			}
			if err == nil {
				n := shruti.Notification{
					Title:        item.Title,
					Url:          item.URL,
					Key:          PROVIDER_NAME + strconv.Itoa(item.ID),
					Priority:     shruti.PRIO_MED,
					Action:       shruti.ACT_PUSH,
					ProviderName: PROVIDER_NAME,
				}
				err = sClient.PushNotification(n)
				msg := "submitted"
				if err != nil {
					msg = err.Error()
				}
				log.Println(msg)
				visited[id] = true
			}
		}
		log.Println("Sleeping")
		time.Sleep(5 * time.Minute)
		log.Println("woke up")
	}
}

func main() {

	log.Println("Starting Hacker News Scraper")

	shrutiServer := os.Getenv("SHRUTI_SERVER")
	if shrutiServer == "" {
		log.Println("SHRUTI_SERVER not set")
		return
	}

	sClient = shruti.Client{"http://", shrutiServer}

	err := Register()
	if err != nil {
		log.Println(err)
		return
	}

	visited = make(map[int]bool)

	err = doWork()
	if err != nil {
		log.Println(err)
	}
}
<|endoftext|>


<commit_before>package santander

const registerBoleto = `
## SOAPAction:create
## Content-Type:text/xml

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:impl="http://impl.webservice.ymb.app.bsbr.altec.com/">
   <soapenv:Header/>
   <soapenv:Body>
      <impl:registraTitulo>
         <dto>
            <dtNsu>{{today | brDateWithoutDelimiter }}</dtNsu>
            <estacao>HYW3</estacao>
            <nsu>{{santanderNSUPrefix .Title.DocumentNumber}}</nsu>
            <ticket>{{unscape .Authentication.AuthorizationToken}}</ticket>
            <tpAmbiente>{{santanderEnv}}</tpAmbiente>
         </dto>
      </impl:registraTitulo>
   </soapenv:Body>
</soapenv:Envelope>
`

const registerSantanderResponse = `
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Body>
      <dlwmin:registraTituloResponse xmlns:dlwmin="http://impl.webservice.ymb.app.bsbr.altec.com/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
         <return xmlns:ns2="http://impl.webservice.ymb.app.bsbr.altec.com/">
            <descricaoErro>{{message}}</descricaoErro>
            <situacao>{{errorCode}}</situacao>
            <titulo>
               <cdBarra>{{barcodeNumber}}</cdBarra>
               <linDig>{{digitableLine}}</linDig>
               <nossoNumero>{{ourNumber}}</nossoNumero>
            </titulo>
         </return>
      </dlwmin:registraTituloResponse>
   </soapenv:Body>
</soapenv:Envelope>
`

const requestTicket = `

## SOAPAction:create
## Content-Type:text/xml

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:impl="http://impl.webservice.dl.app.bsbr.altec.com/">
   <soapenv:Header/>
   <soapenv:Body>
      <impl:create>
         <TicketRequest>
            <dados>
                <entry>
                    <key>CONVENIO.COD-BANCO</key>
                    <value>0033</value>
                </entry>
                <entry>
                    <key>CONVENIO.COD-CONVENIO</key>
                    <value>{{.Agreement.AgreementNumber}}</value>
                </entry>
                <entry>
                    <key>PAGADOR.TP-DOC</key>
                    {{if eq .Buyer.Document.Type "CPF"}}
					 	<value>01</value>
                    {{else}}
					 	<value>02</value>
					{{end}}
                </entry>
                <entry>
                    <key>PAGADOR.NUM-DOC</key>
                    <value>{{.Buyer.Document.Number}}</value>
                </entry>
                <entry>
                    <key>PAGADOR.NOME</key>
                    <value>{{clearString (truncate .Buyer.Name 40)}}</value>
                </entry>
                <entry>
                    <key>PAGADOR.ENDER</key>
                    <value>{{clearString (truncate .Buyer.Address.Street 40)}}</value>
                </entry>
                <entry>
                    <key>PAGADOR.BAIRRO</key>
                    <value>{{clearString (truncate .Buyer.Address.District 30)}}</value>
                </entry>
                <entry>
                    <key>PAGADOR.CIDADE</key>
                    <value>{{clearString (truncate .Buyer.Address.City 20)}}</value>
                </entry>
                <entry>
                    <key>PAGADOR.UF</key>
                    <value>{{clearString (truncate .Buyer.Address.StateCode 2)}}</value>
                </entry>
                <entry>
                    <key>PAGADOR.CEP</key>
                    <value>{{clearString (truncate .Buyer.Address.ZipCode 8)}}</value>
                </entry>
                <entry>
                    <key>TITULO.NOSSO-NUMERO</key>
                    <value>{{padLeft (toString .Title.OurNumber) "0" 13}}</value>
                </entry>
                <entry>
                    <key>TITULO.SEU-NUMERO </key>
                    <value>{{.Title.DocumentNumber}}</value>
                </entry>
                <entry>
                    <key>TITULO.DT-VENCTO</key>
                    <value>{{brDateWithoutDelimiter .Title.ExpireDateTime}}</value>
                </entry>
                <entry>
                    <key>TITULO.DT-EMISSAO</key>
                    <value>{{today | brDateWithoutDelimiter}}</value>
                </entry>
                <entry>
                    <key>TITULO.ESPECIE</key>
                    <value>99</value>
                </entry>
                <entry>
                    <key>TITULO.TP-DESC</key>
                    <value>0</value>
                </entry>
                <entry>
                    <key>TITULO.TP-PROTESTO</key>
                    <value>0</value>
                </entry>
                <entry>
                    <key>TITULO.QT-DIAS-PROTESTO</key>
                    <value>0</value>
                </entry>
                <entry>
                    <key>TITULO.QT-DIAS-BAIXA</key>
                    <value>0</value>
                </entry>
                <entry>
                    <key>TITULO.VL-NOMINAL</key>
                    <value>{{.Title.AmountInCents}}</value>
                </entry>
                <entry>
                    <key>MENSAGEM</key>
                    <value>{{truncate .Title.Instructions 100}}</value>
                </entry>
            </dados>
            <expiracao>600</expiracao>
            <sistema>YMB</sistema>
         </TicketRequest>
      </impl:create>
   </soapenv:Body>
</soapenv:Envelope>
`

const ticketReponse = `
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Body>
        <dlwmin:createResponse xmlns:dlwmin="http://impl.webservice.dl.app.bsbr.altec.com/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <TicketResponse>
                <retCode>{{returnCode}}</retCode>
                <ticket>{{ticket}}</ticket>
            </TicketResponse>
        </dlwmin:createResponse>
    </soapenv:Body>
</soapenv:Envelope>
`

func getResponseSantander() string {
	return registerSantanderResponse
}

func getRequestSantander() string {
	return registerBoleto
}

func getRequestTicket() string {
	return requestTicket
}

func getTicketResponse() string {
	return ticketReponse
}
<commit_msg>add username to field estacao on Santander request<commit_after>package santander

const registerBoleto = `
## SOAPAction:create
## Content-Type:text/xml

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:impl="http://impl.webservice.ymb.app.bsbr.altec.com/">
   <soapenv:Header/>
   <soapenv:Body>
      <impl:registraTitulo>
         <dto>
            <dtNsu>{{today | brDateWithoutDelimiter }}</dtNsu>
            <estacao>{{.Authentication.Username}}</estacao>
            <nsu>{{santanderNSUPrefix .Title.DocumentNumber}}</nsu>
            <ticket>{{unscape .Authentication.AuthorizationToken}}</ticket>
            <tpAmbiente>{{santanderEnv}}</tpAmbiente>
         </dto>
      </impl:registraTitulo>
   </soapenv:Body>
</soapenv:Envelope>
`

const registerSantanderResponse = `
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Body>
      <dlwmin:registraTituloResponse xmlns:dlwmin="http://impl.webservice.ymb.app.bsbr.altec.com/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
         <return xmlns:ns2="http://impl.webservice.ymb.app.bsbr.altec.com/">
            <descricaoErro>{{message}}</descricaoErro>
            <situacao>{{errorCode}}</situacao>
            <titulo>
               <cdBarra>{{barcodeNumber}}</cdBarra>
               <linDig>{{digitableLine}}</linDig>
               <nossoNumero>{{ourNumber}}</nossoNumero>
            </titulo>
         </return>
      </dlwmin:registraTituloResponse>
   </soapenv:Body>
</soapenv:Envelope>
`

const requestTicket = `

## SOAPAction:create
## Content-Type:text/xml

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:impl="http://impl.webservice.dl.app.bsbr.altec.com/">
   <soapenv:Header/>
   <soapenv:Body>
      <impl:create>
         <TicketRequest>
            <dados>
                <entry>
                    <key>CONVENIO.COD-BANCO</key>
                    <value>0033</value>
                </entry>
                <entry>
                    <key>CONVENIO.COD-CONVENIO</key>
                    <value>{{.Agreement.AgreementNumber}}</value>
                </entry>
                <entry>
                    <key>PAGADOR.TP-DOC</key>
                    {{if eq .Buyer.Document.Type "CPF"}}
					 	<value>01</value>
                    {{else}}
					 	<value>02</value>
					{{end}}
                </entry>
                <entry>
                    <key>PAGADOR.NUM-DOC</key>
                    <value>{{.Buyer.Document.Number}}</value>
                </entry>
                <entry>
                    <key>PAGADOR.NOME</key>
                    <value>{{clearString (truncate .Buyer.Name 40)}}</value>
                </entry>
                <entry>
                    <key>PAGADOR.ENDER</key>
                    <value>{{clearString (truncate .Buyer.Address.Street 40)}}</value>
                </entry>
                <entry>
                    <key>PAGADOR.BAIRRO</key>
                    <value>{{clearString (truncate .Buyer.Address.District 30)}}</value>
                </entry>
                <entry>
                    <key>PAGADOR.CIDADE</key>
                    <value>{{clearString (truncate .Buyer.Address.City 20)}}</value>
                </entry>
                <entry>
                    <key>PAGADOR.UF</key>
                    <value>{{clearString (truncate .Buyer.Address.StateCode 2)}}</value>
                </entry>
                <entry>
                    <key>PAGADOR.CEP</key>
                    <value>{{clearString (truncate .Buyer.Address.ZipCode 8)}}</value>
                </entry>
                <entry>
                    <key>TITULO.NOSSO-NUMERO</key>
                    <value>{{padLeft (toString .Title.OurNumber) "0" 13}}</value>
                </entry>
                <entry>
                    <key>TITULO.SEU-NUMERO </key>
                    <value>{{.Title.DocumentNumber}}</value>
                </entry>
                <entry>
                    <key>TITULO.DT-VENCTO</key>
                    <value>{{brDateWithoutDelimiter .Title.ExpireDateTime}}</value>
                </entry>
                <entry>
                    <key>TITULO.DT-EMISSAO</key>
                    <value>{{today | brDateWithoutDelimiter}}</value>
                </entry>
                <entry>
                    <key>TITULO.ESPECIE</key>
                    <value>99</value>
                </entry>
                <entry>
                    <key>TITULO.TP-DESC</key>
                    <value>0</value>
                </entry>
                <entry>
                    <key>TITULO.TP-PROTESTO</key>
                    <value>0</value>
                </entry>
                <entry>
                    <key>TITULO.QT-DIAS-PROTESTO</key>
                    <value>0</value>
                </entry>
                <entry>
                    <key>TITULO.QT-DIAS-BAIXA</key>
                    <value>0</value>
                </entry>
                <entry>
                    <key>TITULO.VL-NOMINAL</key>
                    <value>{{.Title.AmountInCents}}</value>
                </entry>
                <entry>
                    <key>MENSAGEM</key>
                    <value>{{truncate .Title.Instructions 100}}</value>
                </entry>
            </dados>
            <expiracao>600</expiracao>
            <sistema>YMB</sistema>
         </TicketRequest>
      </impl:create>
   </soapenv:Body>
</soapenv:Envelope>
`

const ticketReponse = `
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Body>
        <dlwmin:createResponse xmlns:dlwmin="http://impl.webservice.dl.app.bsbr.altec.com/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <TicketResponse>
                <retCode>{{returnCode}}</retCode>
                <ticket>{{ticket}}</ticket>
            </TicketResponse>
        </dlwmin:createResponse>
    </soapenv:Body>
</soapenv:Envelope>
`

func getResponseSantander() string {
	return registerSantanderResponse
}

func getRequestSantander() string {
	return registerBoleto
}

func getRequestTicket() string {
	return requestTicket
}

func getTicketResponse() string {
	return ticketReponse
}
<|endoftext|>


<commit_before>package goproxy

import (
	"bufio"
	"crypto/tls"
	"io"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"sync/atomic"
)

type ConnectActionLiteral int

const (
	ConnectAccept = iota
	ConnectReject
	ConnectMitm
)

var (
	OkConnect     = &ConnectAction{Action: ConnectAccept}
	MitmConnect   = &ConnectAction{Action: ConnectMitm}
	RejectConnect = &ConnectAction{Action: ConnectReject}
)

type ConnectAction struct {
	Action    ConnectActionLiteral
	TlsConfig *tls.Config
	Ca        *tls.Certificate
}

func stripPort(s string) string {
	ix := strings.IndexRune(s, ':')
	if ix == -1 {
		return s
	}
	return s[:ix]
}

func (proxy *ProxyHttpServer) dial(network, addr string) (c net.Conn, err error) {
	if proxy.Tr.Dial != nil {
		return proxy.Tr.Dial(network, addr)
	}
	return net.Dial(network, addr)
}

func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request) {
	ctx := &ProxyCtx{Req: r, Session: atomic.AddInt64(&proxy.sess, 1), proxy: proxy}

	hij, ok := w.(http.Hijacker)
	if !ok {
		panic("httpserver does not support hijacking")
	}

	proxyClient, _, e := hij.Hijack()
	if e != nil {
		panic("Cannot hijack connection " + e.Error())
	}

	ctx.Logf("Running %d CONNECT handlers", len(proxy.httpsHandlers))
	todo, host := OkConnect, r.URL.Host
	ctx.Req = r
	for _, h := range proxy.httpsHandlers {
		newtodo, newhost := h.HandleConnect(host, ctx)
		if newtodo != nil {
			todo, host = newtodo, newhost
		}
		ctx.Logf("handler: %v %s", todo, host)
	}
	switch todo.Action {
	case ConnectAccept:
		if !hasPort.MatchString(host) {
			host += ":80"
		}
		https_proxy := os.Getenv("https_proxy")
		if https_proxy == "" {
			https_proxy = os.Getenv("HTTPS_PROXY")
		}
		var targetSiteCon net.Conn
		var e error
		if https_proxy != "" {
			targetSiteCon, e = proxy.dial("tcp", https_proxy)
		} else {
			targetSiteCon, e = proxy.dial("tcp", host)
		}
		if e, ok := e.(net.Error); ok && e.Timeout() {
			// trying to mimic the behaviour of the offending website
			// don't answer at all
			ctx.Warnf("Timeout dialing to %s: %s", host, e.Error())
			return
		}
		if e != nil {
			if _, err := io.WriteString(proxyClient, "HTTP/1.1 502 Bad Gateway\r\n\r\n"); err != nil {
				ctx.Warnf("Error responding to client: %s", err)
			}
			ctx.Warnf("Error dialing to %s: %s", host, e.Error())
			if err := proxyClient.Close(); err != nil {
				ctx.Warnf("Error closing client connection: %s", err)
			}
			return
		}
		if https_proxy != "" {
			connectReq := &http.Request{
				Method: "CONNECT",
				URL:    &url.URL{Opaque: host},
				Host:   host,
				Header: make(http.Header),
			}
			connectReq.Write(targetSiteCon)

			// Read response.
			// Okay to use and discard buffered reader here, because
			// TLS server will not speak until spoken to.
			br := bufio.NewReader(targetSiteCon)
			resp, err := http.ReadResponse(br, connectReq)
			if err != nil {
				targetSiteCon.Close()
				w.WriteHeader(500)
				return
			}
			if resp.StatusCode != 200 {
				targetSiteCon.Close()
				w.WriteHeader(resp.StatusCode)
				io.Copy(w, resp.Body)
				resp.Body.Close()
				return
			}
		}
		ctx.Logf("Accepting CONNECT to %s", host)
		proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))
		go proxy.copyAndClose(targetSiteCon, proxyClient)
		go proxy.copyAndClose(proxyClient, targetSiteCon)
	case ConnectMitm:
		proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))
		ctx.Logf("Assuming CONNECT is TLS, mitm proxying it")
		// this goes in a separate goroutine, so that the net/http server won't think we're
		// still handling the request even after hijacking the connection. Those HTTP CONNECT
		// request can take forever, and the server will be stuck when "closed".
		// TODO: Allow Server.Close() mechanism to shut down this connection as nicely as possible
		ca := todo.Ca
		if ca == nil {
			ca = &GoproxyCa
		}
		cert, err := signHost(*ca, []string{stripPort(host)})
		if err != nil {
			ctx.Warnf("Cannot sign host certificate with provided CA: %s", err)
			return
		}
		tlsConfig := tls.Config{}
		if todo.TlsConfig != nil {
			tlsConfig = *todo.TlsConfig
		} else {
			tlsConfig = *defaultTlsConfig
		}
		tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
		go func() {
			//TODO: cache connections to the remote website
			rawClientTls := tls.Server(proxyClient, &tlsConfig)
			if err := rawClientTls.Handshake(); err != nil {
				ctx.Warnf("Cannot handshake client %v %v", r.Host, err)
				return
			}
			defer rawClientTls.Close()
			clientTlsReader := bufio.NewReader(rawClientTls)
			for !isEof(clientTlsReader) {
				req, err := http.ReadRequest(clientTlsReader)
				if err != nil {
					ctx.Warnf("Cannot read TLS request from mitm'd client %v %v", r.Host, err)
					return
				}
				ctx.Logf("req %v", r.Host)
				req, resp := proxy.filterRequest(req, ctx)
				if resp == nil {
					req.URL, err = url.Parse("https://" + r.Host + req.URL.String())
					if err != nil {
						ctx.Warnf("Illegal URL %s", "https://"+r.Host+req.URL.Path)
						return
					}
					removeProxyHeaders(ctx, req)
					resp, err = proxy.Tr.RoundTrip(req)
					if err != nil {
						ctx.Warnf("Cannot read TLS response from mitm'd server %v", err)
						return
					}
					ctx.Logf("resp %v", resp.Status)
				}
				resp = proxy.filterResponse(resp, ctx)
				text := resp.Status
				statusCode := strconv.Itoa(resp.StatusCode) + " "
				if strings.HasPrefix(text, statusCode) {
					text = text[len(statusCode):]
				}
				// always use 1.1 to support encoding
				if _, err := io.WriteString(rawClientTls, "HTTP/1.1"+" "+statusCode+text+"\r\n"); err != nil {
					ctx.Warnf("Cannot write TLS response HTTP status from mitm'd client: %v", err)
					return
				}
				// Since we don't know the length of resp, return chunked encoded response
				// TODO: use a more reasonable scheme
				resp.Header.Del("Content-Length")
				resp.Header.Set("Transfer-Encoding", "chunked")
				if err := resp.Header.Write(rawClientTls); err != nil {
					ctx.Warnf("Cannot write TLS response header from mitm'd client: %v", err)
					return
				}
				if _, err = io.WriteString(rawClientTls, "\r\n"); err != nil {
					ctx.Warnf("Cannot write TLS response header end from mitm'd client: %v", err)
					return
				}
				chunked := newChunkedWriter(rawClientTls)
				if _, err := io.Copy(chunked, resp.Body); err != nil {
					ctx.Warnf("Cannot write TLS response body from mitm'd client: %v", err)
					return
				}
				if err := chunked.Close(); err != nil {
					ctx.Warnf("Cannot write TLS chunked EOF from mitm'd client: %v", err)
					return
				}
				if _, err = io.WriteString(rawClientTls, "\r\n"); err != nil {
					ctx.Warnf("Cannot write TLS response chunked trailer from mitm'd client: %v", err)
					return
				}
			}
			ctx.Logf("Exiting on EOF")
		}()
	case ConnectReject:
		if ctx.Resp != nil {
			if err := ctx.Resp.Write(proxyClient); err != nil {
				ctx.Warnf("Cannot write response that reject http CONNECT: %v", err)
			}
		}
		proxyClient.Close()
	}
}
<commit_msg>fixes #34, always reply, apparently bugs in ruby makes proxy timeouts unacceptable<commit_after>package goproxy

import (
	"bufio"
	"crypto/tls"
	"io"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"sync/atomic"
)

type ConnectActionLiteral int

const (
	ConnectAccept = iota
	ConnectReject
	ConnectMitm
)

var (
	OkConnect     = &ConnectAction{Action: ConnectAccept}
	MitmConnect   = &ConnectAction{Action: ConnectMitm}
	RejectConnect = &ConnectAction{Action: ConnectReject}
)

type ConnectAction struct {
	Action    ConnectActionLiteral
	TlsConfig *tls.Config
	Ca        *tls.Certificate
}

func stripPort(s string) string {
	ix := strings.IndexRune(s, ':')
	if ix == -1 {
		return s
	}
	return s[:ix]
}

func (proxy *ProxyHttpServer) dial(network, addr string) (c net.Conn, err error) {
	if proxy.Tr.Dial != nil {
		return proxy.Tr.Dial(network, addr)
	}
	return net.Dial(network, addr)
}

func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request) {
	ctx := &ProxyCtx{Req: r, Session: atomic.AddInt64(&proxy.sess, 1), proxy: proxy}

	hij, ok := w.(http.Hijacker)
	if !ok {
		panic("httpserver does not support hijacking")
	}

	proxyClient, _, e := hij.Hijack()
	if e != nil {
		panic("Cannot hijack connection " + e.Error())
	}

	ctx.Logf("Running %d CONNECT handlers", len(proxy.httpsHandlers))
	todo, host := OkConnect, r.URL.Host
	ctx.Req = r
	for _, h := range proxy.httpsHandlers {
		newtodo, newhost := h.HandleConnect(host, ctx)
		if newtodo != nil {
			todo, host = newtodo, newhost
		}
		ctx.Logf("handler: %v %s", todo, host)
	}
	switch todo.Action {
	case ConnectAccept:
		if !hasPort.MatchString(host) {
			host += ":80"
		}
		https_proxy := os.Getenv("https_proxy")
		if https_proxy == "" {
			https_proxy = os.Getenv("HTTPS_PROXY")
		}
		var targetSiteCon net.Conn
		var e error
		if https_proxy != "" {
			targetSiteCon, e = proxy.dial("tcp", https_proxy)
		} else {
			targetSiteCon, e = proxy.dial("tcp", host)
		}
		if e != nil {
			if _, err := io.WriteString(proxyClient, "HTTP/1.1 502 Bad Gateway\r\n\r\n"); err != nil {
				ctx.Warnf("Error responding to client: %s", err)
			}
			ctx.Warnf("Error dialing to %s: %s", host, e.Error())
			if err := proxyClient.Close(); err != nil {
				ctx.Warnf("Error closing client connection: %s", err)
			}
			return
		}
		if https_proxy != "" {
			connectReq := &http.Request{
				Method: "CONNECT",
				URL:    &url.URL{Opaque: host},
				Host:   host,
				Header: make(http.Header),
			}
			connectReq.Write(targetSiteCon)

			// Read response.
			// Okay to use and discard buffered reader here, because
			// TLS server will not speak until spoken to.
			br := bufio.NewReader(targetSiteCon)
			resp, err := http.ReadResponse(br, connectReq)
			if err != nil {
				targetSiteCon.Close()
				w.WriteHeader(500)
				return
			}
			if resp.StatusCode != 200 {
				targetSiteCon.Close()
				w.WriteHeader(resp.StatusCode)
				io.Copy(w, resp.Body)
				resp.Body.Close()
				return
			}
		}
		ctx.Logf("Accepting CONNECT to %s", host)
		proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))
		go proxy.copyAndClose(targetSiteCon, proxyClient)
		go proxy.copyAndClose(proxyClient, targetSiteCon)
	case ConnectMitm:
		proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))
		ctx.Logf("Assuming CONNECT is TLS, mitm proxying it")
		// this goes in a separate goroutine, so that the net/http server won't think we're
		// still handling the request even after hijacking the connection. Those HTTP CONNECT
		// request can take forever, and the server will be stuck when "closed".
		// TODO: Allow Server.Close() mechanism to shut down this connection as nicely as possible
		ca := todo.Ca
		if ca == nil {
			ca = &GoproxyCa
		}
		cert, err := signHost(*ca, []string{stripPort(host)})
		if err != nil {
			ctx.Warnf("Cannot sign host certificate with provided CA: %s", err)
			return
		}
		tlsConfig := tls.Config{}
		if todo.TlsConfig != nil {
			tlsConfig = *todo.TlsConfig
		} else {
			tlsConfig = *defaultTlsConfig
		}
		tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
		go func() {
			//TODO: cache connections to the remote website
			rawClientTls := tls.Server(proxyClient, &tlsConfig)
			if err := rawClientTls.Handshake(); err != nil {
				ctx.Warnf("Cannot handshake client %v %v", r.Host, err)
				return
			}
			defer rawClientTls.Close()
			clientTlsReader := bufio.NewReader(rawClientTls)
			for !isEof(clientTlsReader) {
				req, err := http.ReadRequest(clientTlsReader)
				if err != nil {
					ctx.Warnf("Cannot read TLS request from mitm'd client %v %v", r.Host, err)
					return
				}
				ctx.Logf("req %v", r.Host)
				req, resp := proxy.filterRequest(req, ctx)
				if resp == nil {
					req.URL, err = url.Parse("https://" + r.Host + req.URL.String())
					if err != nil {
						ctx.Warnf("Illegal URL %s", "https://"+r.Host+req.URL.Path)
						return
					}
					removeProxyHeaders(ctx, req)
					resp, err = proxy.Tr.RoundTrip(req)
					if err != nil {
						ctx.Warnf("Cannot read TLS response from mitm'd server %v", err)
						return
					}
					ctx.Logf("resp %v", resp.Status)
				}
				resp = proxy.filterResponse(resp, ctx)
				text := resp.Status
				statusCode := strconv.Itoa(resp.StatusCode) + " "
				if strings.HasPrefix(text, statusCode) {
					text = text[len(statusCode):]
				}
				// always use 1.1 to support encoding
				if _, err := io.WriteString(rawClientTls, "HTTP/1.1"+" "+statusCode+text+"\r\n"); err != nil {
					ctx.Warnf("Cannot write TLS response HTTP status from mitm'd client: %v", err)
					return
				}
				// Since we don't know the length of resp, return chunked encoded response
				// TODO: use a more reasonable scheme
				resp.Header.Del("Content-Length")
				resp.Header.Set("Transfer-Encoding", "chunked")
				if err := resp.Header.Write(rawClientTls); err != nil {
					ctx.Warnf("Cannot write TLS response header from mitm'd client: %v", err)
					return
				}
				if _, err = io.WriteString(rawClientTls, "\r\n"); err != nil {
					ctx.Warnf("Cannot write TLS response header end from mitm'd client: %v", err)
					return
				}
				chunked := newChunkedWriter(rawClientTls)
				if _, err := io.Copy(chunked, resp.Body); err != nil {
					ctx.Warnf("Cannot write TLS response body from mitm'd client: %v", err)
					return
				}
				if err := chunked.Close(); err != nil {
					ctx.Warnf("Cannot write TLS chunked EOF from mitm'd client: %v", err)
					return
				}
				if _, err = io.WriteString(rawClientTls, "\r\n"); err != nil {
					ctx.Warnf("Cannot write TLS response chunked trailer from mitm'd client: %v", err)
					return
				}
			}
			ctx.Logf("Exiting on EOF")
		}()
	case ConnectReject:
		if ctx.Resp != nil {
			if err := ctx.Resp.Write(proxyClient); err != nil {
				ctx.Warnf("Cannot write response that reject http CONNECT: %v", err)
			}
		}
		proxyClient.Close()
	}
}
<|endoftext|>


<commit_before>package crud

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"
)

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

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

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

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

// }

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

}

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

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

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

}

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

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

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

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

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

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

// Delete 删除
func (t *Table) Delete(m map[string]interface{}) (int64, error) {
	ks, vs := ksvs(m, " = ? ")
	if t.tableColumns[t.tableName].HaveColumn(IsDeleted) {
		return t.Exec(fmt.Sprintf("UPDATE `%s` SET is_deleted = '1', deleted_at = '%s' WHERE %s", t.tableName, time.Now().Format(TimeFormat), strings.Join(ks, "AND")), vs...).RowsAffected()
	}
	return t.Exec(fmt.Sprintf("DELETE FROM %s WHERE %s", t.tableName, strings.Join(ks, "AND")), vs...).RowsAffected()
}

// Clone 克隆
// 克隆要保证状态在每个链式操作后都是独立的。
func (t *Table) Clone() *Table {
	newTable := &Table{
		DataBase:  t.DataBase,
		tableName: t.tableName,
	}
	if t.Search == nil {
		newTable.Search = &Search{table: newTable, tableName: t.tableName}
	} else {
		newTable.Search = t.Search.Clone()
		newTable.Search.table = newTable
	}
	return newTable
}

//Where where
func (t *Table) Where(query string, args ...interface{}) *Table {
	return t.Clone().Search.Where(query, args...).table
}

//WhereNotEmpty 不允许空的
func (t *Table) WhereNotEmpty(query, arg string) *Table {
	if arg == "" {
		return t
	}
	return t.Clone().Search.Where(query, arg).table
}

//WhereStartEndDay 2017-07-07
func (t *Table) WhereStartEndDay(field, startDay, endDay string) *Table {
	if startDay == "" && endDay == "" {
		return t
	}
	if startDay != "" && endDay == "" {
		endDay = startDay
	}
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m-%d') >= ? AND DATE_FORMAT("+field+",'%Y-%m-%d') <= ?", startDay, endDay).table
}

//WhereStartEndMonth 2007-01
func (t *Table) WhereStartEndMonth(field, startMonth, endMonth string) *Table {
	if startMonth == "" && endMonth == "" {
		return t
	}
	if startMonth != "" && endMonth == "" {
		endMonth = startMonth
	}
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m') >= ? AND DATE_FORMAT("+field+",'%Y-%m') <= ?", startMonth, endMonth).table
}

//WhereStartEndTime 00:00
func (t *Table) WhereStartEndTime(field, startTime, endTime string) *Table {
	if startTime == "" && endTime == "" {
		return t
	}
	if startTime != "" && endTime == "" {
		endTime = startTime
	}
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%H:%i') >= ? AND DATE_FORMAT("+field+",'%H:%i') <= ?", startTime, endTime).table
}

//WhereToday 查看今天的。
func (t *Table) WhereToday(field string) *Table {
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m-%d') = ?", time.Now().Format("2006-01-02")).table
}

//WhereDay 这个字段等于这个时间
func (t *Table) WhereDay(field, time string) *Table {
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m-%d') = ?", time).table
}

//WhereMonth 这个字段等于这个时间
func (t *Table) WhereMonth(field, time string) *Table {
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m') = ?", time).table
}

//WhereBeforeToday 小于今天的时间
func (t *Table) WhereBeforeToday(field string) *Table {
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m-%d') < ?", time.Now().Format("2006-01-02")).table
}

//WhereLike field LIKE %like%
func (t *Table) WhereLike(field, like string) *Table {
	if like == "" {
		return t
	}
	return t.Clone().Search.Where(field+" LIKE ?", "%"+like+"%").table
}

//WhereLikeLeft field LIKE %like
func (t *Table) WhereLikeLeft(field, like string) *Table {
	return t.Clone().Search.Where(field+" LIKE ?", "%"+like).table
}

//WhereID id = ?
func (t *Table) WhereID(id interface{}) *Table {
	return t.Clone().Search.WhereID(id).table
}

//In In
func (t *Table) In(field string, args ...interface{}) *Table {
	return t.Clone().Search.In(field, args...).table
}

//Joins joins
func (t *Table) Joins(query string, args ...string) *Table {
	return t.Clone().Search.Joins(query, args...).table
}

//OrderBy OrderBy
func (t *Table) OrderBy(field string, isDESC ...bool) *Table {
	return t.Clone().Search.OrderBy(field, isDESC...).table
}

//Limit limit
func (t *Table) Limit(n interface{}) *Table {
	return t.Clone().Search.Limit(n).table
}

//Fields fields
func (t *Table) Fields(args ...string) *Table {
	return t.Clone().Search.Fields(args...).table
}
<commit_msg>unfinshed table<commit_after>package crud

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"
)

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

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

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

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

// }

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

}

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

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

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

}

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

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

// CreateBatch create batch
// func (t *Table) CreateBatch(ms []map[string]interface{}) (int, error) {

// }

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

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

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

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

// Delete 删除
func (t *Table) Delete(m map[string]interface{}) (int64, error) {
	ks, vs := ksvs(m, " = ? ")
	if t.tableColumns[t.tableName].HaveColumn(IsDeleted) {
		return t.Exec(fmt.Sprintf("UPDATE `%s` SET is_deleted = '1', deleted_at = '%s' WHERE %s", t.tableName, time.Now().Format(TimeFormat), strings.Join(ks, "AND")), vs...).RowsAffected()
	}
	return t.Exec(fmt.Sprintf("DELETE FROM %s WHERE %s", t.tableName, strings.Join(ks, "AND")), vs...).RowsAffected()
}

// Clone 克隆
// 克隆要保证状态在每个链式操作后都是独立的。
func (t *Table) Clone() *Table {
	newTable := &Table{
		DataBase:  t.DataBase,
		tableName: t.tableName,
	}
	if t.Search == nil {
		newTable.Search = &Search{table: newTable, tableName: t.tableName}
	} else {
		newTable.Search = t.Search.Clone()
		newTable.Search.table = newTable
	}
	return newTable
}

//Where where
func (t *Table) Where(query string, args ...interface{}) *Table {
	return t.Clone().Search.Where(query, args...).table
}

//WhereNotEmpty 不允许空的
func (t *Table) WhereNotEmpty(query, arg string) *Table {
	if arg == "" {
		return t
	}
	return t.Clone().Search.Where(query, arg).table
}

//WhereStartEndDay 2017-07-07
func (t *Table) WhereStartEndDay(field, startDay, endDay string) *Table {
	if startDay == "" && endDay == "" {
		return t
	}
	if startDay != "" && endDay == "" {
		endDay = startDay
	}
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m-%d') >= ? AND DATE_FORMAT("+field+",'%Y-%m-%d') <= ?", startDay, endDay).table
}

//WhereStartEndMonth 2007-01
func (t *Table) WhereStartEndMonth(field, startMonth, endMonth string) *Table {
	if startMonth == "" && endMonth == "" {
		return t
	}
	if startMonth != "" && endMonth == "" {
		endMonth = startMonth
	}
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m') >= ? AND DATE_FORMAT("+field+",'%Y-%m') <= ?", startMonth, endMonth).table
}

//WhereStartEndTime 00:00
func (t *Table) WhereStartEndTime(field, startTime, endTime string) *Table {
	if startTime == "" && endTime == "" {
		return t
	}
	if startTime != "" && endTime == "" {
		endTime = startTime
	}
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%H:%i') >= ? AND DATE_FORMAT("+field+",'%H:%i') <= ?", startTime, endTime).table
}

//WhereToday 查看今天的。
func (t *Table) WhereToday(field string) *Table {
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m-%d') = ?", time.Now().Format("2006-01-02")).table
}

//WhereDay 这个字段等于这个时间
func (t *Table) WhereDay(field, time string) *Table {
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m-%d') = ?", time).table
}

//WhereMonth 这个字段等于这个时间
func (t *Table) WhereMonth(field, time string) *Table {
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m') = ?", time).table
}

//WhereBeforeToday 小于今天的时间
func (t *Table) WhereBeforeToday(field string) *Table {
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m-%d') < ?", time.Now().Format("2006-01-02")).table
}

//WhereLike field LIKE %like%
func (t *Table) WhereLike(field, like string) *Table {
	if like == "" {
		return t
	}
	return t.Clone().Search.Where(field+" LIKE ?", "%"+like+"%").table
}

//WhereLikeLeft field LIKE %like
func (t *Table) WhereLikeLeft(field, like string) *Table {
	return t.Clone().Search.Where(field+" LIKE ?", "%"+like).table
}

//WhereID id = ?
func (t *Table) WhereID(id interface{}) *Table {
	return t.Clone().Search.WhereID(id).table
}

//In In
func (t *Table) In(field string, args ...interface{}) *Table {
	return t.Clone().Search.In(field, args...).table
}

//Joins joins
func (t *Table) Joins(query string, args ...string) *Table {
	return t.Clone().Search.Joins(query, args...).table
}

//OrderBy OrderBy
func (t *Table) OrderBy(field string, isDESC ...bool) *Table {
	return t.Clone().Search.OrderBy(field, isDESC...).table
}

//Limit limit
func (t *Table) Limit(n interface{}) *Table {
	return t.Clone().Search.Limit(n).table
}

//Fields fields
func (t *Table) Fields(args ...string) *Table {
	return t.Clone().Search.Fields(args...).table
}
<|endoftext|>


<commit_before>package gosmi

/*
#cgo LDFLAGS: -lsmi
#include <stdlib.h>
#include <smi.h>
*/
import "C"

import "github.com/sleepinggenius2/gosmi/types"

type Table struct {
	SmiNode
	Columns     map[string]SmiNode
	ColumnOrder []string
	Implied     bool
	Index       []SmiNode
}

func (t SmiNode) AsTable() Table {
	columns, columnOrder := t.GetColumns()
	return Table{
		SmiNode:     t,
		Columns:     columns,
		ColumnOrder: columnOrder,
		Implied:     t.GetImplied(),
		Index:       t.GetIndex(),
	}
}

func (t SmiNode) getRow() (row *C.struct_SmiNode) {
	switch t.Kind {
	case types.NodeRow:
		row = t.GetRaw()
	case types.NodeTable:
		row = C.smiGetFirstChildNode(t.smiNode)
		if row == nil {
			return
		}
	default:
		return
	}

	if types.NodeKind(row.nodekind) != types.NodeRow {
		// TODO: error
		return nil
	}

	return
}

func (t SmiNode) GetRow() (row SmiNode) {
	smiRow := t.getRow()
	if smiRow == nil {
		return
	}
	return CreateNode(smiRow)
}

func (t SmiNode) GetColumns() (columns map[string]SmiNode, columnOrder []string) {
	row := t.getRow()
	if row == nil {
		return
	}

	columns = make(map[string]SmiNode)
	columnOrder = make([]string, 0, 2)

	for smiColumn := C.smiGetFirstChildNode(row); smiColumn != nil; smiColumn = C.smiGetNextChildNode(smiColumn) {
		if types.NodeKind(smiColumn.nodekind) != types.NodeColumn {
			// TODO: error
			return
		}
		column := CreateNode(smiColumn)
		columns[column.Name] = column
		columnOrder = append(columnOrder, column.Name)
	}
	return
}

func (t SmiNode) GetImplied() (implied bool) {
	row := t.getRow()
	if row == nil {
		return false
	}

	return int(row.implied) > 0
}

func (t SmiNode) GetIndex() (index []SmiNode) {
	row := t.getRow()
	if row == nil {
		return
	}

	if types.IndexKind(row.indexkind) == types.IndexAugment {
		row = C.smiGetRelatedNode(row)
		if row == nil {
			return
		}

		if types.NodeKind(row.nodekind) != types.NodeRow {
			// TODO: error
			return
		}
	} else if types.IndexKind(row.indexkind) != types.IndexIndex {
		// TODO: unsupported
		return
	}

	for smiElement := C.smiGetFirstElement(row); smiElement != nil; smiElement = C.smiGetNextElement(smiElement) {
		smiColumn := C.smiGetElementNode(smiElement)
		if smiColumn == nil {
			// TODO: error
			return
		}
		if types.NodeKind(smiColumn.nodekind) != types.NodeColumn {
			// TODO: error
			return
		}
		index = append(index, CreateNode(smiColumn))
	}
	return
}
<commit_msg>Adds GetAugment for row nodes<commit_after>package gosmi

/*
#cgo LDFLAGS: -lsmi
#include <stdlib.h>
#include <smi.h>
*/
import "C"

import "github.com/sleepinggenius2/gosmi/types"

type Table struct {
	SmiNode
	Columns     map[string]SmiNode
	ColumnOrder []string
	Implied     bool
	Index       []SmiNode
}

func (t SmiNode) AsTable() Table {
	columns, columnOrder := t.GetColumns()
	return Table{
		SmiNode:     t,
		Columns:     columns,
		ColumnOrder: columnOrder,
		Implied:     t.GetImplied(),
		Index:       t.GetIndex(),
	}
}

func (t SmiNode) getRow() (row *C.struct_SmiNode) {
	switch t.Kind {
	case types.NodeRow:
		row = t.GetRaw()
	case types.NodeTable:
		row = C.smiGetFirstChildNode(t.smiNode)
		if row == nil {
			return
		}
	default:
		return
	}

	if types.NodeKind(row.nodekind) != types.NodeRow {
		// TODO: error
		return nil
	}

	return
}

func (t SmiNode) GetRow() (row SmiNode) {
	smiRow := t.getRow()
	if smiRow == nil {
		return
	}
	return CreateNode(smiRow)
}

func (t SmiNode) GetColumns() (columns map[string]SmiNode, columnOrder []string) {
	row := t.getRow()
	if row == nil {
		return
	}

	columns = make(map[string]SmiNode)
	columnOrder = make([]string, 0, 2)

	for smiColumn := C.smiGetFirstChildNode(row); smiColumn != nil; smiColumn = C.smiGetNextChildNode(smiColumn) {
		if types.NodeKind(smiColumn.nodekind) != types.NodeColumn {
			// TODO: error
			return
		}
		column := CreateNode(smiColumn)
		columns[column.Name] = column
		columnOrder = append(columnOrder, column.Name)
	}
	return
}

func (t SmiNode) GetImplied() (implied bool) {
	row := t.getRow()
	if row == nil {
		return false
	}

	return int(row.implied) > 0
}

func (t SmiNode) GetAugment() (row SmiNode) {
	smiRow := t.getRow()
	if smiRow == nil {
		return
	}

	if types.IndexKind(smiRow.indexkind) != types.IndexAugment {
		return
	}

	smiRow = C.smiGetRelatedNode(smiRow)
	if smiRow == nil {
		return
	}

	if types.NodeKind(smiRow.nodekind) != types.NodeRow {
		// TODO: error
		return
	}

	return CreateNode(smiRow)
}

func (t SmiNode) GetIndex() (index []SmiNode) {
	row := t.getRow()
	if row == nil {
		return
	}

	if types.IndexKind(row.indexkind) == types.IndexAugment {
		row = C.smiGetRelatedNode(row)
		if row == nil {
			return
		}

		if types.NodeKind(row.nodekind) != types.NodeRow {
			// TODO: error
			return
		}
	} else if types.IndexKind(row.indexkind) != types.IndexIndex {
		// TODO: unsupported
		return
	}

	for smiElement := C.smiGetFirstElement(row); smiElement != nil; smiElement = C.smiGetNextElement(smiElement) {
		smiColumn := C.smiGetElementNode(smiElement)
		if smiColumn == nil {
			// TODO: error
			return
		}
		if types.NodeKind(smiColumn.nodekind) != types.NodeColumn {
			// TODO: error
			return
		}
		index = append(index, CreateNode(smiColumn))
	}
	return
}
<|endoftext|>


<commit_before>package salsa20

import (
	"bytes"
	"testing"
)

var testData = []struct {
	m, n, k, correct []byte
}{
	{
		[]byte("Hello world!"),
		[]byte("nonceS20"),
		[]byte("this is 32-byte key for salsa20!"),
		[]byte{0x55, 0xf2, 0xd8, 0x68, 0xdd, 0x5c, 0x50, 0xff, 0xdb, 0xd3, 0x02, 0xad},
	},
	{
		make([]byte, 64),
		[]byte("nonceS20"),
		[]byte("this is 32-byte key for salsa20!"),
		[]byte{0x1d, 0x97, 0xb4, 0x04, 0xb2, 0x7c, 0x27, 0x90, 0xa9,
			0xbf, 0x66, 0x8c, 0xb7, 0x65, 0x30, 0x2b, 0x25, 0x16, 0x3a,
			0x27, 0x84, 0xb6, 0x93, 0x05, 0x82, 0xb4, 0xd1, 0x93, 0xa4,
			0x34, 0x66, 0x92, 0xf3, 0x5f, 0x62, 0x87, 0x14, 0x4f, 0xac,
			0x4d, 0x97, 0xaa, 0x0d, 0xbc, 0x5b, 0xda, 0xae, 0x90, 0x39,
			0x6f, 0xcc, 0x9f, 0x54, 0xa5, 0x98, 0xa6, 0xf7, 0x09, 0xa4,
			0xc3, 0xde, 0x66, 0x95, 0x90},
	},
}

/*
func TestXor(t *testing.T) {
	for i, v := range testData {
		c := make([]byte, len(v.m))
		Xor(c, v.m, v.n, v.k)
		if !bytes.Equal(c, v.correct) {
			t.Errorf("[%d] expected %x, got %x", i, v.correct, c)
		}
	}
}
*/

func TestCore(t *testing.T) {
	var in [16]byte //:= make([]byte, 16)
	var out [64]byte //out := make([]byte, 64)
	k := []byte("this is 32-byte key for salsa20!")
	var ka [32]byte
	copy(ka[:], k)
	correct := []byte{0x8c, 0xeb, 0x15, 0x4a, 0xed, 0x8d, 0x2b, 0xa1, 0xe6, 0xf0, 0x88, 0x8e, 0x69, 0x1f, 0xef, 0x0f, 0x90, 0x10, 0xb6, 0xda, 0xf1, 0x07, 0x11, 0x43, 0x3e, 0x05, 0xda, 0xce, 0x14, 0x2b, 0x17, 0xcd, 0x4a, 0x12, 0x1e, 0x24, 0x23, 0x33, 0x17, 0x11, 0xe5, 0x4a, 0xe9, 0xa9, 0x1c, 0x7a, 0x6a, 0x80, 0x29, 0xe6, 0x97, 0x18, 0xf4, 0x0d, 0x65, 0x35, 0x30, 0x91, 0x43, 0x6e, 0x66, 0x80, 0x32, 0x5b}
	Core(&out, &in, &ka, &sigma)
	if !bytes.Equal(out[:], correct) {
		t.Errorf("expected %x, got %x", correct, out)
	}
}

var (
	keyarr [32]byte
	key = &keyarr
	noncearr [8]byte
	nonce = &noncearr
	msg = make([]byte, 8<<10)
)

func BenchmarkCore(b *testing.B) {
	b.StopTimer()
	var out [64]byte
	var in [16]byte
	b.SetBytes(64)
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		Core(&out, &in, key, &sigma)
	}
}

func BenchmarkXOR1K(b *testing.B) {
	b.StopTimer()
	out := make([]byte, 1024)
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		Xor(out, msg[:1024], nonce, key)
	}
	b.SetBytes(1024)
}

func BenchmarkXOR8K(b *testing.B) {
	b.StopTimer()
	out := make([]byte, len(msg))
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		Xor(out, msg, nonce, key)
	}
	b.SetBytes(int64(len(msg)))
}
<commit_msg>Enable sals20 test.<commit_after>package salsa20

import (
	"bytes"
	"testing"
)

var testData = []struct {
	m, n, k, correct []byte
}{
	{
		[]byte("Hello world!"),
		[]byte("nonceS20"),
		[]byte("this is 32-byte key for salsa20!"),
		[]byte{0x55, 0xf2, 0xd8, 0x68, 0xdd, 0x5c, 0x50, 0xff, 0xdb, 0xd3, 0x02, 0xad},
	},
	{
		make([]byte, 64),
		[]byte("nonceS20"),
		[]byte("this is 32-byte key for salsa20!"),
		[]byte{0x1d, 0x97, 0xb4, 0x04, 0xb2, 0x7c, 0x27, 0x90, 0xa9,
			0xbf, 0x66, 0x8c, 0xb7, 0x65, 0x30, 0x2b, 0x25, 0x16, 0x3a,
			0x27, 0x84, 0xb6, 0x93, 0x05, 0x82, 0xb4, 0xd1, 0x93, 0xa4,
			0x34, 0x66, 0x92, 0xf3, 0x5f, 0x62, 0x87, 0x14, 0x4f, 0xac,
			0x4d, 0x97, 0xaa, 0x0d, 0xbc, 0x5b, 0xda, 0xae, 0x90, 0x39,
			0x6f, 0xcc, 0x9f, 0x54, 0xa5, 0x98, 0xa6, 0xf7, 0x09, 0xa4,
			0xc3, 0xde, 0x66, 0x95, 0x90},
	},
}

func TestXor(t *testing.T) {
	var n [8]byte
	var k [32]byte
	for i, v := range testData {
		c := make([]byte, len(v.m))
		copy(n[:], v.n)
		copy(k[:], v.k)
		Xor(c, v.m, &n, &k)
		if !bytes.Equal(c, v.correct) {
			t.Errorf("[%d] expected %x, got %x", i, v.correct, c)
		}
	}
}

func TestCore(t *testing.T) {
	var in [16]byte //:= make([]byte, 16)
	var out [64]byte //out := make([]byte, 64)
	k := []byte("this is 32-byte key for salsa20!")
	var ka [32]byte
	copy(ka[:], k)
	correct := []byte{0x8c, 0xeb, 0x15, 0x4a, 0xed, 0x8d, 0x2b, 0xa1, 0xe6, 0xf0, 0x88, 0x8e, 0x69, 0x1f, 0xef, 0x0f, 0x90, 0x10, 0xb6, 0xda, 0xf1, 0x07, 0x11, 0x43, 0x3e, 0x05, 0xda, 0xce, 0x14, 0x2b, 0x17, 0xcd, 0x4a, 0x12, 0x1e, 0x24, 0x23, 0x33, 0x17, 0x11, 0xe5, 0x4a, 0xe9, 0xa9, 0x1c, 0x7a, 0x6a, 0x80, 0x29, 0xe6, 0x97, 0x18, 0xf4, 0x0d, 0x65, 0x35, 0x30, 0x91, 0x43, 0x6e, 0x66, 0x80, 0x32, 0x5b}
	Core(&out, &in, &ka, &sigma)
	if !bytes.Equal(out[:], correct) {
		t.Errorf("expected %x, got %x", correct, out)
	}
}

var (
	keyarr [32]byte
	key = &keyarr
	noncearr [8]byte
	nonce = &noncearr
	msg = make([]byte, 8<<10)
)

func BenchmarkCore(b *testing.B) {
	b.StopTimer()
	var out [64]byte
	var in [16]byte
	b.SetBytes(64)
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		Core(&out, &in, key, &sigma)
	}
}

func BenchmarkXOR1K(b *testing.B) {
	b.StopTimer()
	out := make([]byte, 1024)
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		Xor(out, msg[:1024], nonce, key)
	}
	b.SetBytes(1024)
}

func BenchmarkXOR8K(b *testing.B) {
	b.StopTimer()
	out := make([]byte, len(msg))
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		Xor(out, msg, nonce, key)
	}
	b.SetBytes(int64(len(msg)))
}
<|endoftext|>


<commit_before>package scene

import (
	"image"
	"math"

	"github.com/pankona/gomo-simra/simra"
)

type sampleUnit struct {
	*unitBase
	attackinfo *attackInfo
	target     uniter
}

func (u *sampleUnit) Initialize() {
	simra.GetInstance().AddSprite("player.png",
		image.Rect(0, 0, 384, 384),
		&u.sprite)
}

func (u *sampleUnit) OnEvent(i interface{}) {
	c, ok := i.(*command)
	if !ok {
		panic("unexpected command received. fatal.")
	}

	switch c.commandtype {
	case commandSpawn:
		d, ok := c.data.(*sampleUnit)
		if !ok {
			// unhandled event. ignore.
			return
		}
		if u.id != d.GetID() {
			// this spawn event is not for me. nop.
			return
		}
		u.action = newAction(actionSpawn, d)

	case commandDead:
		target, ok := c.data.(uniter)
		if !ok {
			return
		}

		// all players are eliminated
		if len(u.game.players) == 0 {
			simra.LogDebug("we won!")
			u.action = nil
			break
		}

		if target.GetID() == u.target.GetID() {
			// target is down. search next target
			u.action = newAction(actionMoveToNearestTarget, nil)
		} else {
			// nop.
		}
	default:
		// nop
	}
}

func (u *sampleUnit) DoAction() {
	a := u.action
	if a == nil {
		// idle
		return
	}

	switch a.actiontype {
	case actionSpawn:
		d := a.data.(*sampleUnit)
		u.sprite.W = 64
		u.sprite.H = 64
		u.SetPosition(d.GetPosition())
		simra.LogDebug("@@@@@@ [SPAWN] i'm %s", u.GetID())

		// start moving to target
		u.action = newAction(actionMoveToNearestTarget, nil)

	case actionMoveToNearestTarget:
		u.target = u.nearestPlayer(u.game.players)
		if u.target == nil {
			u.action = nil
			break
		}
		u.moveToTarget(u.target)

		if u.canAttackToTarget(u.target) {
			u.action = newAction(actionAttack, u.target)
		}

	case actionAttack:
		// TODO: start animation

		target := a.data.(uniter)
		if !u.canAttackToTarget(target) {
			u.action = newAction(actionMoveToNearestTarget, nil)
			break
		}

		if u.game.currentFrame-u.attackinfo.lastAttackTime >=
			(int64)(u.attackinfo.cooltime*fps) {
			simra.LogDebug("[ATTACK] i'm %s", u.GetID())
			u.attackinfo.lastAttackTime = u.game.currentFrame

			u.game.eventqueue <- newCommand(commandDamage, &damage{target, u.attackinfo.power})
		}
	default:
		// nop
	}
}

func (u *sampleUnit) nearestPlayer(players map[string]uniter) uniter {
	var (
		distance float64
		retID    string
	)
	for i, v := range players {
		d := getDistanceBetweenUnit(u, v)
		if distance == 0 {
			distance = d
			retID = i
			continue
		}
		if distance > d {
			distance = d
			retID = i
		}
	}

	if retID == "" {
		return nil
	}
	return players[retID]
}

func (u *sampleUnit) moveToTarget(target uniter) {
	ux, uy := u.GetPosition()
	tx, ty := target.GetPosition()

	// calculate which way to go
	// move speed is temporary
	dx, dy := tx-ux, ty-uy
	newx := (float64)(u.moveSpeed) / getDistance(ux, uy, tx, ty) * (float64)(dx)
	newy := (float64)(u.moveSpeed) / getDistance(ux, uy, tx, ty) * (float64)(dy)
	u.sprite.X += (float32)(newx)
	u.sprite.Y += (float32)(newy)
}

func (u *sampleUnit) canAttackToTarget(target uniter) bool {
	ux, uy := u.GetPosition()
	tx, ty := target.GetPosition()

	if (float64)(u.attackinfo.attackRange) >= getDistance(ux, uy, tx, ty) {
		return true
	}
	return false
}

func getDistance(ax, ay, bx, by float32) float64 {
	dx, dy := ax-bx, ay-by
	return math.Sqrt((float64)(dx*dx + dy*dy))
}

func getDistanceBetweenUnit(u1, u2 uniter) float64 {
	ax, ay := u1.GetPosition()
	bx, by := u2.GetPosition()
	return getDistance(ax, ay, bx, by)
}
<commit_msg>move to nearest target if new player is spawned<commit_after>package scene

import (
	"image"
	"math"

	"github.com/pankona/gomo-simra/simra"
)

type sampleUnit struct {
	*unitBase
	attackinfo *attackInfo
	target     uniter
	isSpawned  bool
}

func (u *sampleUnit) Initialize() {
	simra.GetInstance().AddSprite("player.png",
		image.Rect(0, 0, 384, 384),
		&u.sprite)
}

func (u *sampleUnit) OnEvent(i interface{}) {
	c, ok := i.(*command)
	if !ok {
		panic("unexpected command received. fatal.")
	}

	switch c.commandtype {
	case commandSpawn:
		d, ok := c.data.(uniter)
		if !ok {
			// unhandled event. ignore.
			return
		}
		if u.id == d.GetID() {
			// my spawn.
			u.action = newAction(actionSpawn, d)
			break
		} else if u.id != d.GetID() {
			// this spawn event is not for me.
			_, ok := d.(*player)
			if ok && u.isSpawned {
				// player's spawn. move to defeat.
				u.action = newAction(actionMoveToNearestTarget, nil)
			}
			return
		}

	case commandDead:
		target, ok := c.data.(uniter)
		if !ok {
			return
		}

		// all players are eliminated
		if len(u.game.players) == 0 {
			simra.LogDebug("we won!")
			u.action = nil
			break
		}

		if target.GetID() == u.target.GetID() {
			// target is down. search next target
			u.action = newAction(actionMoveToNearestTarget, nil)
		} else {
			// nop.
		}
	default:
		// nop
	}
}

func (u *sampleUnit) DoAction() {
	a := u.action
	if a == nil {
		// idle
		return
	}

	switch a.actiontype {
	case actionSpawn:
		d := a.data.(*sampleUnit)
		u.sprite.W = 64
		u.sprite.H = 64
		u.SetPosition(d.GetPosition())
		simra.LogDebug("@@@@@@ [SPAWN] i'm %s", u.GetID())
		u.isSpawned = true

		// start moving to target
		u.action = newAction(actionMoveToNearestTarget, nil)

	case actionMoveToNearestTarget:
		u.target = u.nearestPlayer(u.game.players)
		if u.target == nil {
			u.action = nil
			break
		}
		u.moveToTarget(u.target)

		if u.canAttackToTarget(u.target) {
			u.action = newAction(actionAttack, u.target)
		}

	case actionAttack:
		// TODO: start animation

		target := a.data.(uniter)
		if !u.canAttackToTarget(target) {
			u.action = newAction(actionMoveToNearestTarget, nil)
			break
		}

		if u.game.currentFrame-u.attackinfo.lastAttackTime >=
			(int64)(u.attackinfo.cooltime*fps) {
			simra.LogDebug("[ATTACK] i'm %s", u.GetID())
			u.attackinfo.lastAttackTime = u.game.currentFrame

			u.game.eventqueue <- newCommand(commandDamage, &damage{target, u.attackinfo.power})
		}
	default:
		// nop
	}
}

func (u *sampleUnit) nearestPlayer(players map[string]uniter) uniter {
	var (
		distance float64
		retID    string
	)
	for i, v := range players {
		d := getDistanceBetweenUnit(u, v)
		if distance == 0 {
			distance = d
			retID = i
			continue
		}
		if distance > d {
			distance = d
			retID = i
		}
	}

	if retID == "" {
		return nil
	}
	return players[retID]
}

func (u *sampleUnit) moveToTarget(target uniter) {
	ux, uy := u.GetPosition()
	tx, ty := target.GetPosition()

	// calculate which way to go
	// move speed is temporary
	dx, dy := tx-ux, ty-uy
	newx := (float64)(u.moveSpeed) / getDistance(ux, uy, tx, ty) * (float64)(dx)
	newy := (float64)(u.moveSpeed) / getDistance(ux, uy, tx, ty) * (float64)(dy)
	u.sprite.X += (float32)(newx)
	u.sprite.Y += (float32)(newy)
}

func (u *sampleUnit) canAttackToTarget(target uniter) bool {
	ux, uy := u.GetPosition()
	tx, ty := target.GetPosition()

	if (float64)(u.attackinfo.attackRange) >= getDistance(ux, uy, tx, ty) {
		return true
	}
	return false
}

func getDistance(ax, ay, bx, by float32) float64 {
	dx, dy := ax-bx, ay-by
	return math.Sqrt((float64)(dx*dx + dy*dy))
}

func getDistanceBetweenUnit(u1, u2 uniter) float64 {
	ax, ay := u1.GetPosition()
	bx, by := u2.GetPosition()
	return getDistance(ax, ay, bx, by)
}
<|endoftext|>


<commit_before>package fetch

import (
	"fmt"
	"os"
	"testing"
)

func TestGetMaxWidth(t *testing.T) {
	os.Setenv("VIP_MAX_WIDTH", "500")
	if width := getMaxWidth(); width != 500 {
		t.Fail()
	}

	os.Setenv("VIP_MAX_WIDTH", "1024")
	if width := getMaxWidth(); width != 1024 {
		t.Fail()
	}

	// Test default value
	os.Setenv("VIP_MAX_WIDTH", "")
	if width := getMaxWidth(); width != 720 {
		t.Fail()
	}
}

func TestNeedsRotation(t *testing.T) {
	for i := 1; i <= 8; i++ {
		filename := fmt.Sprintf("f%d-exif.jpg", i)
		f, err := os.Open(fmt.Sprintf("../test/%s", filename))
		if err != nil {
			t.Errorf("Could not open %s.", filename)
		}

		rotate, angle := needsRotation(f)

		fmt.Println(rotate, angle)

		switch i {
		case 6:
			if angle != 90 && rotate != true {
				t.Errorf("Expected true, 90; got %d, %t", rotate, angle)
			}
		case 3:
			if angle != 180 && rotate != true {
				t.Errorf("Expected true, 180; got %d, %t", rotate, angle)
			}
		case 8:
			if angle != 270 && rotate != true {
				t.Errorf("Expected true, 270; got %d, %t", rotate, angle)
			}
		default:
			if angle != 0 && rotate != false {
				t.Errorf("Expected false, 0; got %d, %t", rotate, angle)
			}
		}
	}
}

func TestNeedsRotationAltFiles(t *testing.T) {
	filenames := map[int]string{
		1: "awesome.jpeg",
		2: "exif_test_img.jpg",
	}

	for key, filename := range filenames {
		f, err := os.Open(fmt.Sprintf("../test/%s", filename))
		if err != nil {
			t.Errorf("Could not open %s.", filename)
		}

		rotate, angle := needsRotation(f)

		fmt.Println(rotate, angle)

		switch key {
		case 1:
			if angle != 0 && rotate != false {
				t.Errorf("Expected true, 90; got %d, %t", rotate, angle)
			}
		case 2:
			if angle != 90 && rotate != true {
				t.Errorf("Expected true, 90; got %d, %t", rotate, angle)
			}
		}
	}
}
<commit_msg>Leave out extraneous print<commit_after>package fetch

import (
	"fmt"
	"os"
	"testing"
)

func TestGetMaxWidth(t *testing.T) {
	os.Setenv("VIP_MAX_WIDTH", "500")
	if width := getMaxWidth(); width != 500 {
		t.Fail()
	}

	os.Setenv("VIP_MAX_WIDTH", "1024")
	if width := getMaxWidth(); width != 1024 {
		t.Fail()
	}

	// Test default value
	os.Setenv("VIP_MAX_WIDTH", "")
	if width := getMaxWidth(); width != 720 {
		t.Fail()
	}
}

func TestNeedsRotation(t *testing.T) {
	for i := 1; i <= 8; i++ {
		filename := fmt.Sprintf("f%d-exif.jpg", i)
		f, err := os.Open(fmt.Sprintf("../test/%s", filename))
		if err != nil {
			t.Errorf("Could not open %s.", filename)
		}

		rotate, angle := needsRotation(f)

		switch i {
		case 6:
			if angle != 90 && rotate != true {
				t.Errorf("Expected true, 90; got %d, %t", rotate, angle)
			}
		case 3:
			if angle != 180 && rotate != true {
				t.Errorf("Expected true, 180; got %d, %t", rotate, angle)
			}
		case 8:
			if angle != 270 && rotate != true {
				t.Errorf("Expected true, 270; got %d, %t", rotate, angle)
			}
		default:
			if angle != 0 && rotate != false {
				t.Errorf("Expected false, 0; got %d, %t", rotate, angle)
			}
		}
	}
}

func TestNeedsRotationAltFiles(t *testing.T) {
	filenames := map[int]string{
		1: "awesome.jpeg",
		2: "exif_test_img.jpg",
	}

	for key, filename := range filenames {
		f, err := os.Open(fmt.Sprintf("../test/%s", filename))
		if err != nil {
			t.Errorf("Could not open %s.", filename)
		}

		rotate, angle := needsRotation(f)

		switch key {
		case 1:
			if angle != 0 && rotate != false {
				t.Errorf("Expected true, 90; got %d, %t", rotate, angle)
			}
		case 2:
			if angle != 90 && rotate != true {
				t.Errorf("Expected true, 90; got %d, %t", rotate, angle)
			}
		}
	}
}
<|endoftext|>


<commit_before>package fetch

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/robertkrimen/otto"

	"fknsrs.biz/p/ottoext/loop"
)

func must(err error) {
	if err != nil {
		panic(err)
	}
}

func TestFetch(t *testing.T) {
	m := http.NewServeMux()
	m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("hello"))
	})
	s := httptest.NewServer(m)
	defer s.Close()

	vm := otto.New()
	l := loop.New(vm)

	if err := Define(vm, l); err != nil {
		panic(err)
	}

	must(l.EvalAndRun(`fetch('` + s.URL + `').then(function(r) {
    return r.text();
  }).then(function(d) {
    if (d.indexOf('hellox') === -1) {
      throw new Error('what');
    }
  });`))
}

func TestFetchCallback(t *testing.T) {
	m := http.NewServeMux()
	m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("hello"))
	})
	s := httptest.NewServer(m)
	defer s.Close()

	vm := otto.New()
	l := loop.New(vm)

	if err := Define(vm, l); err != nil {
		panic(err)
	}

	ch := make(chan bool, 1)

	if err := vm.Set("__capture", func(s string) {
		defer func() { ch <- true }()

		if !strings.Contains(s, "hello") {
			panic(fmt.Errorf("expected to find `hello' in response"))
		}
	}); err != nil {
		panic(err)
	}

	must(l.EvalAndRun(`fetch('` + s.URL + `').then(function(r) {
    return r.text();
  }).then(__capture)`))

	<-ch
}

func TestFetchHeaders(t *testing.T) {
	m := http.NewServeMux()
	m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("header-one", "1")
		w.Header().Add("header-two", "2a")
		w.Header().Add("header-two", "2b")

		w.Write([]byte("hello"))
	})
	s := httptest.NewServer(m)
	defer s.Close()

	vm := otto.New()
	l := loop.New(vm)

	if err := Define(vm, l); err != nil {
		panic(err)
	}

	ch := make(chan bool, 1)

	if err := vm.Set("__capture", func(s string) {
		defer func() { ch <- true }()

		if s != `{"header-one":["1"],"header-two":["2a","2b"]}` {
			panic(fmt.Errorf("expected headers to contain 1, 2a, and 2b"))
		}
	}); err != nil {
		panic(err)
	}

	must(l.EvalAndRun(`fetch('` + s.URL + `').then(function(r) {
    return __capture(JSON.stringify({
      'header-one': r.headers.getAll('header-one'),
      'header-two': r.headers.getAll('header-two'),
    }));
  })`))

	<-ch
}

func TestFetchJSON(t *testing.T) {
	m := http.NewServeMux()
	m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		// these spaces are here so we can disambiguate between this and the
		// re-encoded data the javascript below spits out
		w.Write([]byte("[ 1 , 2 , 3 ]"))
	})
	s := httptest.NewServer(m)
	defer s.Close()

	vm := otto.New()
	l := loop.New(vm)

	if err := Define(vm, l); err != nil {
		panic(err)
	}

	ch := make(chan bool, 1)

	if err := vm.Set("__capture", func(s string) {
		defer func() { ch <- true }()

		if s != `[1,2,3]` {
			panic(fmt.Errorf("expected data to be json, and for that json to be parsed"))
		}
	}); err != nil {
		panic(err)
	}

	must(l.EvalAndRun(`fetch('` + s.URL + `').then(function(r) { return r.json(); }).then(function(d) {
    return setTimeout(__capture, 4, JSON.stringify(d));
  })`))

	<-ch
}

func TestFetchJSONRepeated(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}

	for i := 0; i < 100; i++ {
		TestFetchJSON(t)
	}
}

func TestFetchWithHandler(t *testing.T) {
	m := http.NewServeMux()
	m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		// these spaces are here so we can disambiguate between this and the
		// re-encoded data the javascript below spits out
		w.Write([]byte("[ 1 , 2 , 3 ]"))
	})

	vm := otto.New()
	l := loop.New(vm)

	if err := DefineWithHandler(vm, l, m); err != nil {
		panic(err)
	}

	ch := make(chan bool, 1)

	if err := vm.Set("__capture", func(s string) {
		defer func() { ch <- true }()

		if s != `[1,2,3]` {
			panic(fmt.Errorf("expected data to be json, and for that json to be parsed"))
		}
	}); err != nil {
		panic(err)
	}

	must(l.EvalAndRun(`fetch('/').then(function(r) { return r.json(); }).then(function(d) {
    return setTimeout(__capture, 4, JSON.stringify(d));
  })`))

	<-ch
}
<commit_msg>add a test for parallel http requests<commit_after>package fetch

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/robertkrimen/otto"

	"fknsrs.biz/p/ottoext/loop"
)

func must(err error) {
	if err != nil {
		panic(err)
	}
}

func TestFetch(t *testing.T) {
	m := http.NewServeMux()
	m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("hello"))
	})
	s := httptest.NewServer(m)
	defer s.Close()

	vm := otto.New()
	l := loop.New(vm)

	if err := Define(vm, l); err != nil {
		panic(err)
	}

	must(l.EvalAndRun(`fetch('` + s.URL + `').then(function(r) {
    return r.text();
  }).then(function(d) {
    if (d.indexOf('hellox') === -1) {
      throw new Error('what');
    }
  });`))
}

func TestFetchCallback(t *testing.T) {
	m := http.NewServeMux()
	m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("hello"))
	})
	s := httptest.NewServer(m)
	defer s.Close()

	vm := otto.New()
	l := loop.New(vm)

	if err := Define(vm, l); err != nil {
		panic(err)
	}

	ch := make(chan bool, 1)

	if err := vm.Set("__capture", func(s string) {
		defer func() { ch <- true }()

		if !strings.Contains(s, "hello") {
			panic(fmt.Errorf("expected to find `hello' in response"))
		}
	}); err != nil {
		panic(err)
	}

	must(l.EvalAndRun(`fetch('` + s.URL + `').then(function(r) {
    return r.text();
  }).then(__capture)`))

	<-ch
}

func TestFetchHeaders(t *testing.T) {
	m := http.NewServeMux()
	m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("header-one", "1")
		w.Header().Add("header-two", "2a")
		w.Header().Add("header-two", "2b")

		w.Write([]byte("hello"))
	})
	s := httptest.NewServer(m)
	defer s.Close()

	vm := otto.New()
	l := loop.New(vm)

	if err := Define(vm, l); err != nil {
		panic(err)
	}

	ch := make(chan bool, 1)

	if err := vm.Set("__capture", func(s string) {
		defer func() { ch <- true }()

		if s != `{"header-one":["1"],"header-two":["2a","2b"]}` {
			panic(fmt.Errorf("expected headers to contain 1, 2a, and 2b"))
		}
	}); err != nil {
		panic(err)
	}

	must(l.EvalAndRun(`fetch('` + s.URL + `').then(function(r) {
    return __capture(JSON.stringify({
      'header-one': r.headers.getAll('header-one'),
      'header-two': r.headers.getAll('header-two'),
    }));
  })`))

	<-ch
}

func TestFetchJSON(t *testing.T) {
	m := http.NewServeMux()
	m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		// these spaces are here so we can disambiguate between this and the
		// re-encoded data the javascript below spits out
		w.Write([]byte("[ 1 , 2 , 3 ]"))
	})
	s := httptest.NewServer(m)
	defer s.Close()

	vm := otto.New()
	l := loop.New(vm)

	if err := Define(vm, l); err != nil {
		panic(err)
	}

	ch := make(chan bool, 1)

	if err := vm.Set("__capture", func(s string) {
		defer func() { ch <- true }()

		if s != `[1,2,3]` {
			panic(fmt.Errorf("expected data to be json, and for that json to be parsed"))
		}
	}); err != nil {
		panic(err)
	}

	must(l.EvalAndRun(`fetch('` + s.URL + `').then(function(r) { return r.json(); }).then(function(d) {
    return setTimeout(__capture, 4, JSON.stringify(d));
  })`))

	<-ch
}

func TestFetchJSONRepeated(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}

	for i := 0; i < 100; i++ {
		TestFetchJSON(t)
	}
}

func TestFetchWithHandler(t *testing.T) {
	m := http.NewServeMux()
	m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		// these spaces are here so we can disambiguate between this and the
		// re-encoded data the javascript below spits out
		w.Write([]byte("[ 1 , 2 , 3 ]"))
	})

	vm := otto.New()
	l := loop.New(vm)

	if err := DefineWithHandler(vm, l, m); err != nil {
		panic(err)
	}

	ch := make(chan bool, 1)

	if err := vm.Set("__capture", func(s string) {
		defer func() { ch <- true }()

		if s != `[1,2,3]` {
			panic(fmt.Errorf("expected data to be json, and for that json to be parsed"))
		}
	}); err != nil {
		panic(err)
	}

	must(l.EvalAndRun(`fetch('/').then(function(r) { return r.json(); }).then(function(d) {
    return setTimeout(__capture, 4, JSON.stringify(d));
  })`))

	<-ch
}

func TestFetchWithHandlerParallel(t *testing.T) {
	m := http.NewServeMux()
	m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("hello"))
	})

	vm := otto.New()
	l := loop.New(vm)

	if err := DefineWithHandler(vm, l, m); err != nil {
		panic(err)
	}

	ch := make(chan bool, 1)

	if err := vm.Set("__capture", func(c otto.FunctionCall) otto.Value {
		defer func() { ch <- true }()

		return otto.UndefinedValue()
	}); err != nil {
		panic(err)
	}

	must(l.EvalAndRun(`Promise.all([1,2,3,4,5].map(function(i) { return fetch('/' + i); })).then(__capture)`))

	<-ch
}
<|endoftext|>


<commit_before>package file

import (
	"fmt"
	"reflect"
	"testing"
)

func TestUiSearch(t *testing.T) {
	testCases := []struct {
		path   string
		result []string
	}{
		{path: "XXX/**/", result: []string{}},
		{path: "**/", result: []string{"./", "testdata/", "testdata/include/"}},
		{path: "**/*", result: []string{"search.go", "search_test.go", "testdata/", "testdata/include/", "testdata/include/source.h", "testdata/source.c"}},
		{path: "./*/*", result: []string{"testdata/include/", "testdata/source.c"}},
		{path: "./**/*", result: []string{"search.go", "search_test.go", "testdata/", "testdata/include/", "testdata/include/source.h", "testdata/source.c"}},
		{path: "testdata/**/*.[ch]", result: []string{"testdata/include/source.h", "testdata/source.c"}},
		{path: "**/*.[ch]", result: []string{"testdata/include/source.h", "testdata/source.c"}},
		{path: "./**/*.[ch]", result: []string{"testdata/include/source.h", "testdata/source.c"}},
		{path: "**/../**/*.[ch]", result: []string{"../file/testdata/include/source.h", "../file/testdata/source.c", "testdata/include/source.h", "testdata/source.c"}},
	}
	for _, tc := range testCases {
		str := Search(tc.path, NewSearchOption(
			WithToSlash(true),
		))
		if !reflect.DeepEqual(str, tc.result) {
			t.Errorf("Search(\"%s\")  = %v, want %v.", tc.path, str, tc.result)
		}
	}
}

func TestUiSearchFileOnly(t *testing.T) {
	testCases := []struct {
		path   string
		result []string
	}{
		{path: "XXX/**/", result: []string{}},
		{path: "**/", result: []string{}},
		{path: "./*/*", result: []string{"testdata/source.c"}},
		{path: "./**/*", result: []string{"search.go", "search_test.go", "testdata/include/source.h", "testdata/source.c"}},
		{path: "testdata/**/*.[ch]", result: []string{"testdata/include/source.h", "testdata/source.c"}},
		{path: "**/*.[ch]", result: []string{"testdata/include/source.h", "testdata/source.c"}},
		{path: "./**/*.[ch]", result: []string{"testdata/include/source.h", "testdata/source.c"}},
		{path: "**/../**/*.[ch]", result: []string{"../file/testdata/include/source.h", "../file/testdata/source.c", "testdata/include/source.h", "testdata/source.c"}},
	}
	for _, tc := range testCases {
		str := Search(tc.path, NewSearchOption(
			WithToSlash(true),
			WithEnableFile(true),
			WithEnableDir(false),
		))
		if !reflect.DeepEqual(str, tc.result) {
			t.Errorf("Search(\"%s\")  = %v, want %v.", tc.path, str, tc.result)
		}
	}
}

func TestUiSearchDirOnly(t *testing.T) {
	testCases := []struct {
		path   string
		result []string
	}{
		{path: "XXX/**/", result: []string{}},
		{path: "**/", result: []string{"./", "testdata/", "testdata/include/"}},
		{path: "*", result: []string{"testdata/"}},
		{path: "**/*", result: []string{"testdata/", "testdata/include/"}},
		{path: "./**/*", result: []string{"testdata/", "testdata/include/"}},
		{path: "testdata", result: []string{"testdata/"}},
		{path: "testdata/**/", result: []string{"testdata/", "testdata/include/"}},
		{path: "test*", result: []string{"testdata/"}},
		{path: "test*/**/", result: []string{"testdata/", "testdata/include/"}},
		{path: "test*/**/XXX", result: []string{}},
	}
	for _, tc := range testCases {
		str := Search(tc.path, NewSearchOption(
			WithToSlash(true),
			WithEnableFile(false),
			WithEnableDir(true),
		))
		if !reflect.DeepEqual(str, tc.result) {
			t.Errorf("Search(\"%s\")  = %v, want %v.", tc.path, str, tc.result)
		}
	}
}

func TestUiSearchAbs(t *testing.T) {
	testCases := []struct {
		path   string
		result []string
	}{
		{path: "XXX/**/", result: []string{}},
		{path: "**/", result: []string{"/path/to/file/", "/path/to/file/testdata/", "/path/to/file/testdata/include"}},
		{path: "**/*", result: []string{"/path/to/file/search.go", "/path/to/file/search_test.go", "/path/to/file/testdata/", "/path/to/file/testdata/include", "/path/to/file/testdata/include/source.h", "/path/to/file/testdata/source.c"}},
		{path: "./**/*", result: []string{"/path/to/file/search.go", "/path/to/file/search_test.go", "/path/to/file/testdata/", "/path/to/file/testdata/include", "/path/to/file/testdata/include/source.h", "/path/to/file/testdata/source.c"}},
		{path: "testdata/**/*.[ch]", result: []string{"/path/to/file/testdata/source.c", "/path/to/file/testdata/source.h"}},
		{path: "**/*.[ch]", result: []string{"/path/to/file/testdata/include/source.h", "/path/to/file/testdata/source.c"}},
		{path: "./**/*.[ch]", result: []string{"/path/to/file/testdata/include/source.h", "/path/to/file/testdata/source.c"}},
		{path: "**/../**/*.[ch]", result: []string{"/path/to/file/testdata/include/source.h", "/path/to/file/testdata/source.c"}},
	}
	for _, tc := range testCases {
		str := Search(tc.path, NewSearchOption(
			WithToSlash(true),
			WithAbsPath(true),
		))
		if len(str) != len(tc.result) {
			t.Errorf("Search(\"%s\")  = %v, want %v.", tc.path, str, tc.result)
		}
	}
}

func ExampleSearch() {
	result := Search("**/*.[ch]", NewSearchOption(WithToSlash(true)))
	fmt.Println(result)
	// Output:
	// [testdata/include/source.h testdata/source.c]
}
<commit_msg>Update test for file subpackage<commit_after>package file

import (
	"fmt"
	"reflect"
	"testing"
)

func TestUiSearch(t *testing.T) {
	testCases := []struct {
		path   string
		result []string
	}{
		{path: "XXX/**/", result: []string{}},
		{path: "**/", result: []string{"./", "testdata/", "testdata/include/"}},
		{path: "**/*", result: []string{"search.go", "search_test.go", "testdata/", "testdata/include/", "testdata/include/source.h", "testdata/source.c"}},
		{path: "./*/*", result: []string{"testdata/include/", "testdata/source.c"}},
		{path: "./**/*", result: []string{"search.go", "search_test.go", "testdata/", "testdata/include/", "testdata/include/source.h", "testdata/source.c"}},
		{path: "testdata/**/*.[ch]", result: []string{"testdata/include/source.h", "testdata/source.c"}},
		{path: "**/*.[ch]", result: []string{"testdata/include/source.h", "testdata/source.c"}},
		{path: "./**/*.[ch]", result: []string{"testdata/include/source.h", "testdata/source.c"}},
		{path: "**/../**/*.[ch]", result: []string{"../file/testdata/include/source.h", "../file/testdata/source.c", "testdata/include/source.h", "testdata/source.c"}},
	}
	for _, tc := range testCases {
		str := Search(tc.path, NewSearchOption(
			WithToSlash(true),
		))
		if !reflect.DeepEqual(str, tc.result) {
			t.Errorf("Search(\"%s\")  = %v, want %v.", tc.path, str, tc.result)
		}
	}
}

func TestUiSearchFileOnly(t *testing.T) {
	testCases := []struct {
		path   string
		result []string
	}{
		{path: "XXX/**/", result: []string{}},
		{path: "**/", result: []string{}},
		{path: "./*/*", result: []string{"testdata/source.c"}},
		{path: "./**/*", result: []string{"search.go", "search_test.go", "testdata/include/source.h", "testdata/source.c"}},
		{path: "test*/**/*.[ch]", result: []string{"testdata/include/source.h", "testdata/source.c"}},
		{path: "test*/**/XXX", result: []string{}},
		{path: "**/*.[ch]", result: []string{"testdata/include/source.h", "testdata/source.c"}},
		{path: "./**/*.[ch]", result: []string{"testdata/include/source.h", "testdata/source.c"}},
		{path: "**/../**/*.[ch]", result: []string{"../file/testdata/include/source.h", "../file/testdata/source.c", "testdata/include/source.h", "testdata/source.c"}},
	}
	for _, tc := range testCases {
		str := Search(tc.path, NewSearchOption(
			WithToSlash(true),
			WithEnableFile(true),
			WithEnableDir(false),
		))
		if !reflect.DeepEqual(str, tc.result) {
			t.Errorf("Search(\"%s\")  = %v, want %v.", tc.path, str, tc.result)
		}
	}
}

func TestUiSearchDirOnly(t *testing.T) {
	testCases := []struct {
		path   string
		result []string
	}{
		{path: "XXX/**/", result: []string{}},
		{path: "**/", result: []string{"./", "testdata/", "testdata/include/"}},
		{path: "*", result: []string{"testdata/"}},
		{path: "**/*", result: []string{"testdata/", "testdata/include/"}},
		{path: "./*/*", result: []string{"testdata/include/"}},
		{path: "./**/*", result: []string{"testdata/", "testdata/include/"}},
		{path: "testdata", result: []string{"testdata/"}},
		{path: "testdata/**/", result: []string{"testdata/", "testdata/include/"}},
		{path: "test*", result: []string{"testdata/"}},
		{path: "test*/**/", result: []string{"testdata/", "testdata/include/"}},
		{path: "test*/**/XXX", result: []string{}},
	}
	for _, tc := range testCases {
		str := Search(tc.path, NewSearchOption(
			WithToSlash(true),
			WithEnableFile(false),
			WithEnableDir(true),
		))
		if !reflect.DeepEqual(str, tc.result) {
			t.Errorf("Search(\"%s\")  = %v, want %v.", tc.path, str, tc.result)
		}
	}
}

func TestUiSearchAbs(t *testing.T) {
	testCases := []struct {
		path   string
		result []string
	}{
		{path: "XXX/**/", result: []string{}},
		{path: "**/", result: []string{"/path/to/file/", "/path/to/file/testdata/", "/path/to/file/testdata/include"}},
		{path: "**/*", result: []string{"/path/to/file/search.go", "/path/to/file/search_test.go", "/path/to/file/testdata/", "/path/to/file/testdata/include", "/path/to/file/testdata/include/source.h", "/path/to/file/testdata/source.c"}},
		{path: "./**/*", result: []string{"/path/to/file/search.go", "/path/to/file/search_test.go", "/path/to/file/testdata/", "/path/to/file/testdata/include", "/path/to/file/testdata/include/source.h", "/path/to/file/testdata/source.c"}},
		{path: "testdata/**/*.[ch]", result: []string{"/path/to/file/testdata/source.c", "/path/to/file/testdata/source.h"}},
		{path: "**/*.[ch]", result: []string{"/path/to/file/testdata/include/source.h", "/path/to/file/testdata/source.c"}},
		{path: "./**/*.[ch]", result: []string{"/path/to/file/testdata/include/source.h", "/path/to/file/testdata/source.c"}},
		{path: "**/../**/*.[ch]", result: []string{"/path/to/file/testdata/include/source.h", "/path/to/file/testdata/source.c"}},
	}
	for _, tc := range testCases {
		str := Search(tc.path, NewSearchOption(
			WithToSlash(true),
			WithAbsPath(true),
		))
		if len(str) != len(tc.result) {
			t.Errorf("Search(\"%s\")  = %v, want %v.", tc.path, str, tc.result)
		}
	}
}

func ExampleSearch() {
	result := Search("**/*.[ch]", NewSearchOption(WithToSlash(true)))
	fmt.Println(result)
	// Output:
	// [testdata/include/source.h testdata/source.c]
}
<|endoftext|>