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

package tinynet

import (
	"net"
	"path/filepath"

	"github.com/containernetworking/plugins/pkg/ip"
	"github.com/containernetworking/plugins/pkg/ns"
	log "github.com/sirupsen/logrus"
)

// Host is a host instance
type Host struct {
	NodeType string
	Name     string
	vethName string
	IfName   string
	Sandbox  string
	IP       string
	MAC      string
}

// NewHost for creating a network namespace
func NewHost(name string, docker bool) (*Host, error) {
	h := new(Host)
	h.NodeType = "Host"
	h.Name = name

	if docker {
		_, sandboxKey := initDocker("library/busybox")
		h.Sandbox = sandboxKey
		log.Info("netns mouted into the host: ", h.Sandbox)
		return h, nil
	}

	// Create a network namespace
	targetNs, err := ns.NewNS()
	if err != nil {
		log.Fatal("failed to open netns: ", err)
	}
	// log.Info("netns mouted into the host: ", targetNs.Path())
	log.Infof("Adding a host: %s, namespace: %s", h.Name, filepath.Base(targetNs.Path()))

	h.Sandbox = targetNs.Path()

	return h, nil
}

func (h *Host) setupVeth(ifName string, mtu int) (*Host, error) {
	// Get network namespace object
	netns, err := ns.GetNS(h.Sandbox)
	if err != nil {
		log.Fatal("failed to open netns: ", err)
	}
	defer netns.Close()

	// attach network namespace and setup veth pair
	err = netns.Do(func(hostNS ns.NetNS) error {
		// create the veth pair in the container and move host end into host netns
		hostVeth, containerVeth, err := ip.SetupVeth(ifName, mtu, hostNS)
		if err != nil {
			return err
		}
		// Host interface name
		h.IfName = containerVeth.Name

		// h.mac = containerVeth.HardwareAddr.String()

		// Host veth name
		h.vethName = hostVeth.Name

		// ip link set lo up
		_, err = ifaceUp("lo")
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	return h, nil
}

func (h *Host) setIfaceIP(address string) error {
	// Get network namespace object
	netns, err := ns.GetNS(h.Sandbox)
	if err != nil {
		log.Fatal("failed to open netns: ", err)
	}
	defer netns.Close()

	ipv4Addr, _, err := net.ParseCIDR(address)
	if err != nil {
		return err
	}
	h.IP = ipv4Addr.String()
	err = netns.Do(func(hostNS ns.NetNS) error {
		if err := setIP(h.IfName, address); err != nil {
			return err
		}
		// ip link set ifName up
		_, err := ifaceUp(h.IfName)
		if err != nil {
			return err
		}
		// get Host mac address
		hostIface, err := net.InterfaceByName(h.IfName)
		if err != nil {
			return err
		}
		h.MAC = hostIface.HardwareAddr.String()
		return nil
	})
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>add a method for new a container<commit_after>// Copyright (c) 2017 Che Wei, Lin
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package tinynet

import (
	"net"
	"path/filepath"

	"github.com/containernetworking/plugins/pkg/ip"
	"github.com/containernetworking/plugins/pkg/ns"
	log "github.com/sirupsen/logrus"
)

// Host is a host instance
type Host struct {
	NodeType string
	Name     string
	vethName string
	IfName   string
	Sandbox  string
	IP       string
	MAC      string
}

// NewHost for creating a network namespace
func NewHost(name string, docker bool) (*Host, error) {
	h := new(Host)
	h.NodeType = "Host"
	h.Name = name

	// Create a network namespace
	targetNs, err := ns.NewNS()
	if err != nil {
		log.Fatal("failed to open netns: ", err)
	}
	// log.Info("netns mouted into the host: ", targetNs.Path())
	log.Infof("Adding a host: %s, namespace: %s", h.Name, filepath.Base(targetNs.Path()))
	h.Sandbox = targetNs.Path()
	return h, nil
}

func NewContainer(name string, imageRef string) (*Host, error) {
	h := new(Host)
	h.NodeType = "Host"
	h.Name = name
	_, sandboxKey := ensureDocker(imageRef)
	h.Sandbox = sandboxKey
	log.Info("netns mouted into the host: ", h.Sandbox)
	return h, nil
}

func (h *Host) setupVeth(ifName string, mtu int) (*Host, error) {
	// Get network namespace object
	netns, err := ns.GetNS(h.Sandbox)
	if err != nil {
		log.Fatal("failed to open netns: ", err)
	}
	defer netns.Close()

	// attach network namespace and setup veth pair
	err = netns.Do(func(hostNS ns.NetNS) error {
		// create the veth pair in the container and move host end into host netns
		hostVeth, containerVeth, err := ip.SetupVeth(ifName, mtu, hostNS)
		if err != nil {
			return err
		}
		// Host interface name
		h.IfName = containerVeth.Name

		// h.mac = containerVeth.HardwareAddr.String()

		// Host veth name
		h.vethName = hostVeth.Name

		// ip link set lo up
		_, err = ifaceUp("lo")
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	return h, nil
}

func (h *Host) setIfaceIP(address string) error {
	// Get network namespace object
	netns, err := ns.GetNS(h.Sandbox)
	if err != nil {
		log.Fatal("failed to open netns: ", err)
	}
	defer netns.Close()

	ipv4Addr, _, err := net.ParseCIDR(address)
	if err != nil {
		return err
	}
	h.IP = ipv4Addr.String()
	err = netns.Do(func(hostNS ns.NetNS) error {
		if err := setIP(h.IfName, address); err != nil {
			return err
		}
		// ip link set ifName up
		_, err := ifaceUp(h.IfName)
		if err != nil {
			return err
		}
		// get Host mac address
		hostIface, err := net.InterfaceByName(h.IfName)
		if err != nil {
			return err
		}
		h.MAC = hostIface.HardwareAddr.String()
		return nil
	})
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package retryablehttp

import "net/http"

func NewHTTPClient() RetryingHTTPClient {
	return RetryingHTTPClient{NewClient()}
}

type RetryingHTTPClient struct {
	*Client
}

func (c *RetryingHTTPClient) Do(req *http.Request) (*http.Response, error) {
	wrappedReq, err := FromRequest(req)
	if err != nil {
		return nil, err
	}

	return c.Client.Do(wrappedReq)
}
<commit_msg>Added documentation<commit_after>package retryablehttp

import "net/http"

// NewHTTPClient creates a new RetryingHTTPClient with a Client with default settings.
func NewHTTPClient() RetryingHTTPClient {
	return RetryingHTTPClient{NewClient()}
}

// RetryingHTTPClient is a thin wrapper around Client to fulfill the Do function signature of http.Client
type RetryingHTTPClient struct {
	*Client
}

// Do wraps calling an HTTP method with retries and fulfilling the Do function signature of http.Client
func (c *RetryingHTTPClient) Do(req *http.Request) (*http.Response, error) {
	wrappedReq, err := FromRequest(req)
	if err != nil {
		return nil, err
	}

	return c.Client.Do(wrappedReq)
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"log"
	"net/http"
)

//curl -v http://web.archive.org/web/20160513214322/http:/justpaste.it/u8j7//
//*   Trying 207.241.224.26...
//* TCP_NODELAY set
//* Connected to web.archive.org (207.241.224.26) port 80 (#0)
//> GET /web/20160513214322/http:/justpaste.it/u8j7// HTTP/1.1
//> Host: web.archive.org
//> User-Agent: curl/7.55.1
//> Accept: */*
//>
//< HTTP/1.1 200 OK
//< Server: nginx/1.15.5
//< Date: Sat, 05 Jan 2019 00:49:43 GMT
//< Content-Type: text/html; charset=UTF-8
//< Content-Length: 35579
//< Connection: keep-alive
//< X-Archive-Orig-content-length: -1
//< X-Archive-Orig-strict-transport-security: max-age=15552000; includeSubDomains; preload
//< X-Archive-Orig-x-content-type-options: nosniff
//< X-Archive-Orig-set-cookie: PHPSESSID=ht1e342e9re2dvetp0o6n7e8oe8pbs3jdh31lpqd3ne7iuubo7j0; path=/; HttpOnly
//< X-Archive-Orig-expires: Thu, 19 Nov 1981 08:52:00 GMT
//< X-Archive-Orig-vary: Accept-Encoding
//< X-Archive-Orig-server: cloudflare-nginx
//< X-Archive-Orig-connection: close
//< X-Archive-Orig-pragma: no-cache
//< X-Archive-Orig-cache-control: no-store, no-cache, must-revalidate
//< X-Archive-Orig-date: Fri, 13 May 2016 21:43:22 GMT
//< X-Archive-Orig-cf-ray: 2a29491c158a1207-SJC
//< X-Archive-Guessed-Content-Type: text/html
//< X-Archive-Guessed-Charset: utf-8
//< Memento-Datetime: Fri, 13 May 2016 21:43:22 GMT
//< Link: <https://justpaste.it/u8j7>; rel="original", <http://web.archive.org/web/timemap/link/https://justpaste.it/u8j7>; rel="timemap"; type="application/link-format", <http://web.archive.org/web/https://justpaste.it/u8j7>; rel="timegate", <http://web.archive.org/web/20160513214322/https://justpaste.it/u8j7>; rel="first memento"; datetime="Fri, 13 May 2016 21:43:22 GMT", <http://web.archive.org/web/20160513214322/https://justpaste.it/u8j7>; rel="memento"; datetime="Fri, 13 May 2016 21:43:22 GMT", <http://web.archive.org/web/20160513214322/https://justpaste.it/u8j7>; rel="last memento"; datetime="Fri, 13 May 2016 21:43:22 GMT"
//< Content-Security-Policy: default-src 'self' 'unsafe-eval' 'unsafe-inline' data: blob: archive.org web.archive.org analytics.archive.org pragma.archivelab.org
//< X-Archive-Src: liveweb-20160514001702/live-20160513205105-wwwb-app11.us.archive.org.warc.gz
//< X-App-Server: wwwb-app12
//< X-ts: ----
//< X-location: All
//< X-Cache-Key: httpweb.archive.org/web/20160513214322/http:/justpaste.it/u8j7//CA
//< X-Page-Cache: MISS
//<

func main() {
	// Hello world, the web server

	helloHandler := func(w http.ResponseWriter, req *http.Request) {
		io.WriteString(w, req.RequestURI + "\n")
	}

	http.HandleFunc("/", helloHandler)
	log.Fatal(http.ListenAndServe(":80", nil))
}
<commit_msg>	修改：     http.go<commit_after>package main

import (
	"io"
	"log"
	"net/http"
)

//curl -v http://web.archive.org/web/20160513214322/http:/justpaste.it/u8j7//
//*   Trying 207.241.224.26...
//* TCP_NODELAY set
//* Connected to web.archive.org (207.241.224.26) port 80 (#0)
//> GET /web/20160513214322/http:/justpaste.it/u8j7// HTTP/1.1
//> Host: web.archive.org
//> User-Agent: curl/7.55.1
//> Accept: */*
//>
//< HTTP/1.1 200 OK
//< Server: nginx/1.15.5
//< Date: Sat, 05 Jan 2019 00:49:43 GMT
//< Content-Type: text/html; charset=UTF-8
//< Content-Length: 35579
//< Connection: keep-alive
//< X-Archive-Orig-content-length: -1
//< X-Archive-Orig-strict-transport-security: max-age=15552000; includeSubDomains; preload
//< X-Archive-Orig-x-content-type-options: nosniff
//< X-Archive-Orig-set-cookie: PHPSESSID=ht1e342e9re2dvetp0o6n7e8oe8pbs3jdh31lpqd3ne7iuubo7j0; path=/; HttpOnly
//< X-Archive-Orig-expires: Thu, 19 Nov 1981 08:52:00 GMT
//< X-Archive-Orig-vary: Accept-Encoding
//< X-Archive-Orig-server: cloudflare-nginx
//< X-Archive-Orig-connection: close
//< X-Archive-Orig-pragma: no-cache
//< X-Archive-Orig-cache-control: no-store, no-cache, must-revalidate
//< X-Archive-Orig-date: Fri, 13 May 2016 21:43:22 GMT
//< X-Archive-Orig-cf-ray: 2a29491c158a1207-SJC
//< X-Archive-Guessed-Content-Type: text/html
//< X-Archive-Guessed-Charset: utf-8
//< Memento-Datetime: Fri, 13 May 2016 21:43:22 GMT
//< Link: <https://justpaste.it/u8j7>; rel="original", <http://web.archive.org/web/timemap/link/https://justpaste.it/u8j7>; rel="timemap"; type="application/link-format", <http://web.archive.org/web/https://justpaste.it/u8j7>; rel="timegate", <http://web.archive.org/web/20160513214322/https://justpaste.it/u8j7>; rel="first memento"; datetime="Fri, 13 May 2016 21:43:22 GMT", <http://web.archive.org/web/20160513214322/https://justpaste.it/u8j7>; rel="memento"; datetime="Fri, 13 May 2016 21:43:22 GMT", <http://web.archive.org/web/20160513214322/https://justpaste.it/u8j7>; rel="last memento"; datetime="Fri, 13 May 2016 21:43:22 GMT"
//< Content-Security-Policy: default-src 'self' 'unsafe-eval' 'unsafe-inline' data: blob: archive.org web.archive.org analytics.archive.org pragma.archivelab.org
//< X-Archive-Src: liveweb-20160514001702/live-20160513205105-wwwb-app11.us.archive.org.warc.gz
//< X-App-Server: wwwb-app12
//< X-ts: ----
//< X-location: All
//< X-Cache-Key: httpweb.archive.org/web/20160513214322/http:/justpaste.it/u8j7//CA
//< X-Page-Cache: MISS
//<

func main() {
	// Hello world, the web server

	helloHandler := func(w http.ResponseWriter, req *http.Request) {
        //w.Header().Set("Content-Type", "text/html; charset=utf-8")
        w.Header().Set("Link", `<https://justpaste.it/u8j7>; rel="original", <http://web.archive.org/web/timemap/link/https://justpaste.it/u8j7>; rel="timemap"; type="application/link-format", <http://web.archive.org/web/https://justpaste.it/u8j7>; rel="timegate", <http://web.archive.org/web/20160513214322/https://justpaste.it/u8j7>; rel="first memento"; datetime="Fri, 13 May 2016 21:43:22 GMT", <http://web.archive.org/web/20160513214322/https://justpaste.it/u8j7>; rel="memento"; datetime="Fri, 13 May 2016 21:43:22 GMT", <http://web.archive.org/web/20160513214322/https://justpaste.it/u8j7>; rel="last memento"; datetime="Fri, 13 May 2016 21:43:22 GMT"`)
        w.Header().Set("X-Cache-Key", `httpweb.archive.org/web/20160513214322/http:/justpaste.it/u8j7//CA`)
        w.Header().Set("X-Archive-Orig-vary", "Accept-Encoding")
        w.Header().Set("X-location", "All")
		io.WriteString(w, req.RequestURI + "\n")
	}

	http.HandleFunc("/", helloHandler)
	log.Fatal(http.ListenAndServe(":8081", nil))
}
<|endoftext|>


<commit_before>package typhon

import (
	"io"
	"net"
	"net/http"
	"net/textproto"
	"os"
	"strings"
	"sync"
	"syscall"

	"github.com/monzo/slog"
	"golang.org/x/net/http/httpguts"
)

const (
	// chunkThreshold is a byte threshold above which request and response bodies that result from using buffered I/O
	// within Typhon will be transferred with chunked encoding on the wire.
	chunkThreshold = 5 * 1000000 // 5 megabytes
)

var httpChunkBufPool = sync.Pool{
	New: func() interface{} {
		buf := make([]byte, 32*1024) // size is the same as io.Copy uses internally
		return &buf
	}}

func isStreamingRsp(rsp Response) bool {
	// Most straightforward: service may have set rsp.Body to a streamer
	if s, ok := rsp.Body.(*streamer); ok && s != nil {
		return true
	}

	// If the content length is unknown, it should stream
	if rsp.ContentLength <= 0 {
		return true
	}

	// If the response body is the same as the request body and the request is streaming, the response should be too
	if rsp.Request != nil && rsp.Request.ContentLength <= 0 && rsp.Body == rsp.Request.Body {
		return true
	}

	// Chunked transfer encoding (only in HTTP/1.1) gives us an additional clue
	if !rsp.ProtoAtLeast(2, 0) {
		if httpguts.HeaderValuesContainsToken(rsp.Header[textproto.CanonicalMIMEHeaderKey("Transfer-Encoding")], "chunked") {
			return true
		}
		// Annoyingly, this can be removed from headers by net/http and promoted to its own field
		for _, v := range rsp.TransferEncoding {
			if v == "chunked" {
				return true
			}
		}
	}

	return false
}

// copyErrSeverity returns a slog error severity that should be used to report an error from an io.Copy operation to
// send the response body to a client. This exists because these errors often do not indicate actual problems. For
// example, a client may disconnect before the response body is copied to it; this doesn't mean the server is
// misbehaving.
func copyErrSeverity(err error) slog.Severity {

	switch {
	case strings.HasSuffix(err.Error(), "read on closed response body"):
		return slog.DebugSeverity
	}

	// Annoyingly, these errors can be deeply nested; &net.OpError{&os.SyscallError{syscall.Errno}}
	switch err := err.(type) {
	case syscall.Errno:
		return copyErrnoSeverity(err) // platform-specific

	case *os.SyscallError:
		return copyErrSeverity(err.Err)

	case *net.OpError:
		return copyErrSeverity(err.Err)

	default:
		return slog.WarnSeverity
	}
}

// HttpHandler transforms the given Service into a standard library HTTP handler. It is one of the main "bridges"
// between Typhon and net/http.
func HttpHandler(svc Service) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, httpReq *http.Request) {
		if httpReq.Body != nil {
			defer httpReq.Body.Close()
		}

		req := Request{
			Context: httpReq.Context(),
			Request: *httpReq}
		if h, ok := rw.(http.Hijacker); ok {
			req.hijacker = h
		}
		rsp := svc(req)

		// If the connection was hijacked, we should not attempt to write anything out
		if rsp.hijacked {
			return
		}

		rwHeader := rw.Header()
		for k, v := range rsp.Header {
			rwHeader[k] = v
		}
		rw.WriteHeader(rsp.StatusCode)
		if rsp.Body != nil {
			defer rsp.Body.Close()
			buf := *httpChunkBufPool.Get().(*[]byte)
			defer httpChunkBufPool.Put(&buf)
			if isStreamingRsp(rsp) {
				// Streaming responses use copyChunked(), which takes care of flushing transparently
				if _, err := copyChunked(rw, rsp.Body, buf); err != nil {
					slog.Log(slog.Eventf(copyErrSeverity(err), req, "Couldn't send streaming response body", err))

					// Prevent the client from accidentally consuming a truncated stream by aborting the response.
					// The official way of interrupting an HTTP reply mid-stream is panic(http.ErrAbortHandler), which
					// works for both HTTP/1.1 and HTTP.2. https://github.com/golang/go/issues/17790
					panic(http.ErrAbortHandler)
				}
			} else {
				if _, err := io.CopyBuffer(rw, rsp.Body, buf); err != nil {
					slog.Log(slog.Eventf(copyErrSeverity(err), req, "Couldn't send response body", err))
				}
			}
		}
	})
}
<commit_msg>Log connection reset by peer as debug-level<commit_after>package typhon

import (
	"io"
	"net"
	"net/http"
	"net/textproto"
	"os"
	"strings"
	"sync"
	"syscall"

	"github.com/monzo/slog"
	"golang.org/x/net/http/httpguts"
)

const (
	// chunkThreshold is a byte threshold above which request and response bodies that result from using buffered I/O
	// within Typhon will be transferred with chunked encoding on the wire.
	chunkThreshold = 5 * 1000000 // 5 megabytes
)

var httpChunkBufPool = sync.Pool{
	New: func() interface{} {
		buf := make([]byte, 32*1024) // size is the same as io.Copy uses internally
		return &buf
	}}

func isStreamingRsp(rsp Response) bool {
	// Most straightforward: service may have set rsp.Body to a streamer
	if s, ok := rsp.Body.(*streamer); ok && s != nil {
		return true
	}

	// If the content length is unknown, it should stream
	if rsp.ContentLength <= 0 {
		return true
	}

	// If the response body is the same as the request body and the request is streaming, the response should be too
	if rsp.Request != nil && rsp.Request.ContentLength <= 0 && rsp.Body == rsp.Request.Body {
		return true
	}

	// Chunked transfer encoding (only in HTTP/1.1) gives us an additional clue
	if !rsp.ProtoAtLeast(2, 0) {
		if httpguts.HeaderValuesContainsToken(rsp.Header[textproto.CanonicalMIMEHeaderKey("Transfer-Encoding")], "chunked") {
			return true
		}
		// Annoyingly, this can be removed from headers by net/http and promoted to its own field
		for _, v := range rsp.TransferEncoding {
			if v == "chunked" {
				return true
			}
		}
	}

	return false
}

// copyErrSeverity returns a slog error severity that should be used to report an error from an io.Copy operation to
// send the response body to a client. This exists because these errors often do not indicate actual problems. For
// example, a client may disconnect before the response body is copied to it; this doesn't mean the server is
// misbehaving.
func copyErrSeverity(err error) slog.Severity {

	switch {
	case strings.HasSuffix(err.Error(), "read on closed response body"),
		strings.HasSuffix(err.Error(), "connection reset by peer"):
		return slog.DebugSeverity
	}

	// Annoyingly, these errors can be deeply nested; &net.OpError{&os.SyscallError{syscall.Errno}}
	switch err := err.(type) {
	case syscall.Errno:
		return copyErrnoSeverity(err) // platform-specific

	case *os.SyscallError:
		return copyErrSeverity(err.Err)

	case *net.OpError:
		return copyErrSeverity(err.Err)

	default:
		return slog.WarnSeverity
	}
}

// HttpHandler transforms the given Service into a standard library HTTP handler. It is one of the main "bridges"
// between Typhon and net/http.
func HttpHandler(svc Service) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, httpReq *http.Request) {
		if httpReq.Body != nil {
			defer httpReq.Body.Close()
		}

		req := Request{
			Context: httpReq.Context(),
			Request: *httpReq}
		if h, ok := rw.(http.Hijacker); ok {
			req.hijacker = h
		}
		rsp := svc(req)

		// If the connection was hijacked, we should not attempt to write anything out
		if rsp.hijacked {
			return
		}

		rwHeader := rw.Header()
		for k, v := range rsp.Header {
			rwHeader[k] = v
		}
		rw.WriteHeader(rsp.StatusCode)
		if rsp.Body != nil {
			defer rsp.Body.Close()
			buf := *httpChunkBufPool.Get().(*[]byte)
			defer httpChunkBufPool.Put(&buf)
			if isStreamingRsp(rsp) {
				// Streaming responses use copyChunked(), which takes care of flushing transparently
				if _, err := copyChunked(rw, rsp.Body, buf); err != nil {
					slog.Log(slog.Eventf(copyErrSeverity(err), req, "Couldn't send streaming response body", err))

					// Prevent the client from accidentally consuming a truncated stream by aborting the response.
					// The official way of interrupting an HTTP reply mid-stream is panic(http.ErrAbortHandler), which
					// works for both HTTP/1.1 and HTTP.2. https://github.com/golang/go/issues/17790
					panic(http.ErrAbortHandler)
				}
			} else {
				if _, err := io.CopyBuffer(rw, rsp.Body, buf); err != nil {
					slog.Log(slog.Eventf(copyErrSeverity(err), req, "Couldn't send response body", err))
				}
			}
		}
	})
}
<|endoftext|>


<commit_before>// Copyright 2016 Comcast Cable Communications Management, LLC
//
// 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.
//
// End Copyright

package money

import (
	"bytes"
	"net/http"
)

//Header keys
const (
	MoneyHeader      = "X-Money-Trace"
	MoneySpansHeader = "X-Money-Spans"

	//money-trace context keys
	tIDKey = "trace-id"
	pIDKey = "parent-id"
	sIDKey = "span-id"
)

//MainSpan is a handy way to decorate your handler with money capabilities
//TODO: need to explain about how to use the money trace context values in in the request context
//TODO: disclaimer that this decorator changes the behavior of the responseWriter by allowing multiply
//body and code writes
func MainSpan(appName string) func(http.Handler) http.Handler {
	return func(h http.Handler) http.Handler {
		return http.HandlerFunc(
			func(w http.ResponseWriter, r *http.Request) {

				rw := &rwInterceptor{ResponseWriter: w, Code: http.StatusOK, Body: new(bytes.Buffer)}
				defer rw.Flush()

				if r.Header.Get(MoneyHeader) != "" {
					var (
						tc  *TraceContext
						err error
					)

					if tc, err = decodeTraceContext(r.Header.Get(MoneyHeader)); err == nil {
						r = r.WithContext(traceCxt(tc))

						var done = Start(rw.Header())

						defer func() {
							//finish this main handler span
							done(&SpanReport{
								Name:    "HTTPHandler",
								AppName: appName,
								Code:    rw.Code,
								Success: rw.Code < 400,
								TC:      tc,
							})

						}()
					}
				}

				h.ServeHTTP(rw, r)
			})
	}
}

//rwInterceptor allows temporary buffering of
//body and code for an original responseWriter
type rwInterceptor struct {
	http.ResponseWriter
	Code int
	Body *bytes.Buffer
}

//Write simply saves the last array of bytes written. Such data
//is then written to the original responseWriter once Flush() is called
func (rw *rwInterceptor) Write(b []byte) (int, error) {
	rw.Body.Reset() //starting fresh
	return rw.Body.Write(b)
}

//WriteHeader saves the last code written to the it. Such code
//is then written to the original responseWriter once Flush() is called
func (rw *rwInterceptor) WriteHeader(code int) {
	rw.Code = code
}

//Flush transfers the temporary buffer data into the original responseWriter
func (rw *rwInterceptor) Flush() (int, error) {
	rw.ResponseWriter.WriteHeader(rw.Code)
	return rw.ResponseWriter.Write(rw.Body.Bytes())
}
<commit_msg>simplify defer statement<commit_after>// Copyright 2016 Comcast Cable Communications Management, LLC
//
// 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.
//
// End Copyright

package money

import (
	"bytes"
	"net/http"
)

//Header keys
const (
	MoneyHeader      = "X-Money-Trace"
	MoneySpansHeader = "X-Money-Spans"

	//money-trace context keys
	tIDKey = "trace-id"
	pIDKey = "parent-id"
	sIDKey = "span-id"
)

//MainSpan is a handy way to decorate your handler with money capabilities
//TODO: need to explain about how to use the money trace context values in in the request context
//TODO: disclaimer that this decorator changes the behavior of the responseWriter by allowing multiply
//body and code writes
func MainSpan(appName string) func(http.Handler) http.Handler {
	return func(h http.Handler) http.Handler {
		return http.HandlerFunc(
			func(w http.ResponseWriter, r *http.Request) {

				rw := &rwInterceptor{ResponseWriter: w, Code: http.StatusOK, Body: new(bytes.Buffer)}
				defer rw.Flush()

				if r.Header.Get(MoneyHeader) != "" {
					var (
						tc  *TraceContext
						err error
					)

					if tc, err = decodeTraceContext(r.Header.Get(MoneyHeader)); err == nil {
						r = r.WithContext(traceCxt(tc))

						var done = Start(rw.Header())

						//finish this main handler span
						defer done(&SpanReport{
							Name:    "HTTPHandler",
							AppName: appName,
							Code:    rw.Code,
							Success: rw.Code < 400,
							TC:      tc,
						})
					}
				}

				h.ServeHTTP(rw, r)
			})
	}
}

//rwInterceptor allows temporary buffering of
//body and code for an original responseWriter
type rwInterceptor struct {
	http.ResponseWriter
	Code int
	Body *bytes.Buffer
}

//Write simply saves the last array of bytes written. Such data
//is then written to the original responseWriter once Flush() is called
func (rw *rwInterceptor) Write(b []byte) (int, error) {
	rw.Body.Reset() //starting fresh
	return rw.Body.Write(b)
}

//WriteHeader saves the last code written to the it. Such code
//is then written to the original responseWriter once Flush() is called
func (rw *rwInterceptor) WriteHeader(code int) {
	rw.Code = code
}

//Flush transfers the temporary buffer data into the original responseWriter
func (rw *rwInterceptor) Flush() (int, error) {
	rw.ResponseWriter.WriteHeader(rw.Code)
	return rw.ResponseWriter.Write(rw.Body.Bytes())
}
<|endoftext|>


<commit_before>package ranger

import (
	"errors"
	"fmt"
	"io"
	"mime"
	"mime/multipart"
	"net/http"
	"net/url"
	"strings"
)

// HTTPClient is an interface describing the methods required from net/http.Client
type HTTPClient interface {
	Do(*http.Request) (*http.Response, error)
	Get(string) (*http.Response, error)
	Head(string) (*http.Response, error)
}

// HTTPRanger is a RangeFetcher that uses the HTTP Range: header to fetch blocks.
//
// HTTPRanger first makes a HEAD request and then between 0 and Length()/BlockSize GET requests, attempting
// whenever possible to optimize for a lower number of requests.
//
// No network requests are made until the first I/O-related function call.
type HTTPRanger struct {
	URL    *url.URL
	Client HTTPClient

	etag, lastModified string
	length             int64
	blockSize          int
}

func statusCodeError(status int) error {
	return fmt.Errorf("unexpected response (status %d)", status)
}

func statusIsAcceptable(status int) bool {
	return status >= 200 && status < 300
}

// Initialize implements the Initialize function from the RangeFetcher interface.
// It performs a HEAD request to retrieve the required information from the server.
func (r *HTTPRanger) Initialize(bs int) error {
	if r.Client == nil {
		r.Client = &http.Client{}
	}

	resp, err := r.Client.Head(r.URL.String())
	if err != nil {
		return err
	}

	if !statusIsAcceptable(resp.StatusCode) {
		return statusCodeError(resp.StatusCode)
	}

	if !strings.Contains(resp.Header.Get("Accept-Ranges"), "bytes") {
		return errors.New(r.URL.Host + " does not support byte-ranged requests.")
	}

	r.blockSize = bs
	r.etag = resp.Header.Get("ETag")
	r.lastModified = resp.Header.Get("Last-Modified")
	r.length = resp.ContentLength
	return nil
}

// Length returns the length, in bytes, of the ranged-over file.
func (r *HTTPRanger) Length() int64 {
	return r.length
}

// FetchBlocks requests blocks from the HTTP server.
func (r *HTTPRanger) FetchBlocks(ranges []BlockByteRange) ([]Block, error) {
	blox := make([]Block, len(ranges))
	if len(ranges) > 0 {
		rs := make([]string, len(ranges))
		for i, rng := range ranges {
			rs[i] = fmt.Sprintf("%d-%d", rng.Start, rng.End)
		}
		rangeString := strings.Join(rs, ",")

		req, _ := http.NewRequest("GET", r.URL.String(), nil)
		req.Header.Set("Range", fmt.Sprintf("bytes=%s", rangeString))
		if r.etag != "" {
			req.Header.Set("If-Range", r.etag)
		} else if r.lastModified != "" {
			req.Header.Set("If-Range", r.lastModified)
		}

		resp, err := r.Client.Do(req)
		if err != nil {
			return nil, err
		}

		switch resp.StatusCode {
		case http.StatusPreconditionFailed:
			return nil, errors.New("ranged request failed; document may have mutated")
		case http.StatusNotFound:
			return nil, errors.New("ranged request failed; document may have disappeared")
		default:
			if !statusIsAcceptable(resp.StatusCode) {
				return nil, statusCodeError(resp.StatusCode)
			}
		}

		defer resp.Body.Close()

		typ, params, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
		if err != nil {
			return nil, err
		}

		if typ == "multipart/byteranges" {
			multipart := multipart.NewReader(resp.Body, params["boundary"])
			i := 0
			for {
				if part, err := multipart.NextPart(); err == nil {
					rng := ranges[i]
					bn := rng.Number
					blocklen := (rng.End - rng.Start) + 1
					blox[i] = Block{Number: bn, Data: make([]byte, blocklen)}
					io.ReadFull(part, blox[i].Data)
					i++
				} else {
					if err == io.EOF {
						break
					}
					return nil, err
				}
			}
		} else {
			bn := 0
			if resp.StatusCode == http.StatusPartialContent {
				// If we've received 206 Partial Content but no multipart parts,
				// we received a contiguous section starting at the first requested block.
				bn = ranges[0].Number
			}
			body := make([]byte, resp.ContentLength)
			io.ReadFull(resp.Body, body)
			blox = blox[0:0]
			remaining := resp.ContentLength
			ncopied := int64(0)
			for remaining > 0 {
				bs := int64(r.blockSize)
				if bs > remaining {
					bs = remaining
				}

				blk := Block{bn, make([]byte, bs)}
				bodySlice := body[ncopied : ncopied+bs]
				copy(blk.Data, bodySlice)
				blox = append(blox, blk)

				bn++
				ncopied += bs
				remaining -= bs
			}
		}
	}
	return blox, nil
}
<commit_msg>make HTTPFetcher ignore weak ETags & clean up FetchBlocks<commit_after>package ranger

import (
	"errors"
	"fmt"
	"io"
	"mime"
	"mime/multipart"
	"net/http"
	"net/url"
	"strings"
)

// HTTPClient is an interface describing the methods required from net/http.Client
type HTTPClient interface {
	Do(*http.Request) (*http.Response, error)
	Get(string) (*http.Response, error)
	Head(string) (*http.Response, error)
}

// HTTPRanger is a RangeFetcher that uses the HTTP Range: header to fetch blocks.
//
// HTTPRanger first makes a HEAD request and then between 0 and Length()/BlockSize GET requests, attempting
// whenever possible to optimize for a lower number of requests.
//
// No network requests are made until the first I/O-related function call.
type HTTPRanger struct {
	URL    *url.URL
	Client HTTPClient

	etag, lastModified string
	length             int64
	blockSize          int
}

func statusCodeError(status int) error {
	return fmt.Errorf("unexpected response (status %d)", status)
}

func statusIsAcceptable(status int) bool {
	return status >= 200 && status < 300
}

// Initialize implements the Initialize function from the RangeFetcher interface.
// It performs a HEAD request to retrieve the required information from the server.
func (r *HTTPRanger) Initialize(bs int) error {
	if r.Client == nil {
		r.Client = &http.Client{}
	}

	resp, err := r.Client.Head(r.URL.String())
	if err != nil {
		return err
	}

	if !statusIsAcceptable(resp.StatusCode) {
		return statusCodeError(resp.StatusCode)
	}

	if !strings.Contains(resp.Header.Get("Accept-Ranges"), "bytes") {
		return errors.New(r.URL.Host + " does not support byte-ranged requests.")
	}

	r.blockSize = bs
	etag := resp.Header.Get("ETag")
	if len(etag) >= 2 && etag[0:1] != "W/" {
		// ETag is present and not weak
		r.etag = etag
	}
	r.lastModified = resp.Header.Get("Last-Modified")
	r.length = resp.ContentLength
	return nil
}

// Length returns the length, in bytes, of the ranged-over file.
func (r *HTTPRanger) Length() int64 {
	return r.length
}

func generateByteRangeHeader(ranges []BlockByteRange) string {
	if len(ranges) > 0 {
		rs := make([]string, len(ranges))
		for i, rng := range ranges {
			rs[i] = fmt.Sprintf("%d-%d", rng.Start, rng.End)
		}
		return "bytes=" + strings.Join(rs, ",")
	}
	return ""
}

// FetchBlocks requests blocks from the HTTP server.
func (r *HTTPRanger) FetchBlocks(ranges []BlockByteRange) ([]Block, error) {
	blox := make([]Block, len(ranges))
	if len(ranges) > 0 {
		req, err := http.NewRequest("GET", r.URL.String(), nil)
		if err != nil {
			return nil, err
		}

		req.Header.Set("Range", generateByteRangeHeader(ranges))
		if r.etag != "" {
			req.Header.Set("If-Range", r.etag)
		} else if r.lastModified != "" {
			req.Header.Set("If-Range", r.lastModified)
		}

		resp, err := r.Client.Do(req)
		if err != nil {
			return nil, err
		}

		switch resp.StatusCode {
		case http.StatusPreconditionFailed:
			return nil, errors.New("ranged request failed; document may have mutated")
		case http.StatusNotFound:
			return nil, errors.New("ranged request failed; document may have disappeared")
		default:
			if !statusIsAcceptable(resp.StatusCode) {
				return nil, statusCodeError(resp.StatusCode)
			}
		}

		defer resp.Body.Close()

		typ, params, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
		if err != nil {
			return nil, err
		}

		if typ == "multipart/byteranges" {
			multipart := multipart.NewReader(resp.Body, params["boundary"])
			i := 0
			for {
				if part, err := multipart.NextPart(); err == nil {
					rng := ranges[i]
					bn := rng.Number
					blocklen := (rng.End - rng.Start) + 1
					blox[i] = Block{Number: bn, Data: make([]byte, blocklen)}
					io.ReadFull(part, blox[i].Data)
					i++
				} else {
					if err == io.EOF {
						break
					}
					return nil, err
				}
			}
		} else {
			bn := 0
			if resp.StatusCode == http.StatusPartialContent {
				// If we've received 206 Partial Content but no multipart parts,
				// we received a contiguous section starting at the first requested block.
				bn = ranges[0].Number
			}
			body := make([]byte, resp.ContentLength)
			io.ReadFull(resp.Body, body)
			blox = blox[0:0]
			remaining := resp.ContentLength
			ncopied := int64(0)
			for remaining > 0 {
				bs := int64(r.blockSize)
				if bs > remaining {
					bs = remaining
				}

				blk := Block{bn, make([]byte, bs)}
				bodySlice := body[ncopied : ncopied+bs]
				copy(blk.Data, bodySlice)
				blox = append(blox, blk)

				bn++
				ncopied += bs
				remaining -= bs
			}
		}
	}
	return blox, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"html/template"
	"io"
	"log"
	"strings"
	"time"

	"github.com/emersion/go-imap/client"
	message "github.com/emersion/go-message"
	"github.com/emersion/go-message/mail"
	"github.com/mmcdole/gofeed"
	"github.com/spf13/viper"
)

var mailTemplate = template.Must(template.New("mail").Parse(
	`

	{{ .Title }}

	{{ .Author }}

	{{ .Content }}

`))

type templatePayload struct {
	Link    string
	Title   string
	Author  string
	Content template.HTML
}

func FormatContent(item *gofeed.Item) (string, error) {
	var payload templatePayload

	payload.Link = item.Link
	payload.Title = item.Title

	if item.Author != nil {
		payload.Author = fmt.Sprintf("%s %s", item.Author.Name, item.Author.Email)
	}

	if len(item.Content) > 0 {
		payload.Content = template.HTML(item.Content)
	} else {
		payload.Content = template.HTML(item.Description)
	}

	var buffer bytes.Buffer

	err := mailTemplate.Execute(&buffer, payload)

	if err != nil {
		return "", err
	}

	return buffer.String(), nil
}

func NewMessage(item *gofeed.Item) (bytes.Buffer, error) {
	var b bytes.Buffer

	fromName := viper.GetString("imap.from.name")

	if item.Author != nil {
		fromName = fmt.Sprintf("%s %s", item.Author.Name, item.Author.Email)
	}

	from := []*mail.Address{{fromName, viper.GetString("imap.from.email")}}
	to := []*mail.Address{{viper.GetString("imap.to.name"), viper.GetString("imap.to.email")}}

	h := mail.NewHeader()
	h.SetContentType("multipart/alternative", nil)
	h.SetDate(*item.PublishedParsed)
	h.SetAddressList("From", from)
	h.SetAddressList("To", to)
	h.SetSubject(item.Title)

	// Create a new mail writer
	messageWriter, err := message.CreateWriter(&b, h.Header)
	defer messageWriter.Close()
	if err != nil {
		return b, err
	}

	htmlHeader := make(message.Header)
	htmlHeader.SetContentType("text/html", nil)
	htmlWriter, err := messageWriter.CreatePart(htmlHeader)
	defer htmlWriter.Close()
	if err != nil {
		return b, err
	}

	content, err := FormatContent(item)
	if err != nil {
		return b, err
	}

	io.WriteString(htmlWriter, content)

	return b, nil
}

func AppendNewItemsViaIMAP(items ItemsWithFolders) error {
	if viper.GetBool("debug") {
		log.Printf("Found %d new items", len(items))
	}

	hostPort := fmt.Sprintf("%s:%d", viper.GetString("imap.host"), viper.GetInt("imap.port"))
	c, err := client.DialTLS(hostPort, nil)
	if err != nil {
		return err
	}

	defer c.Logout()

	if err := c.Login(viper.GetString("imap.username"), viper.GetString("imap.password")); err != nil {
		return err
	}

	if viper.GetBool("debug") {
		log.Println("Logged in to IMAP")
	}

	for _, entry := range items {
		if entry.Item.PublishedParsed == nil {
			t := time.Now()
			entry.Item.PublishedParsed = &t
		}

		folderName := entry.Folder
		if viper.GetBool("imap.folder_capitalize") {
			folderName = strings.Title(folderName)
		}
		folder := fmt.Sprintf("%s/%s", viper.GetString("imap.folder_prefix"), folderName)

		_ = c.Create(folder)

		msg, err := NewMessage(entry.Item)
		if err != nil {
			return err
		}

		if viper.GetBool("debug") {
			log.Printf("Appending item to %s", folder)
		}

		literal := bytes.NewReader(msg.Bytes())
		err = c.Append(folder, []string{}, *entry.Item.PublishedParsed, literal)
		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>Simplify html template<commit_after>package main

import (
	"bytes"
	"fmt"
	"html/template"
	"io"
	"log"
	"strings"
	"time"

	"github.com/emersion/go-imap/client"
	message "github.com/emersion/go-message"
	"github.com/emersion/go-message/mail"
	"github.com/mmcdole/gofeed"
	"github.com/spf13/viper"
)

var mailTemplate = template.Must(template.New("mail").Parse(
	`

	{{ .Title }} {{ .Author }}

	{{ .Content }}

`))

type templatePayload struct {
	Link    string
	Title   string
	Author  string
	Content template.HTML
}

func FormatContent(item *gofeed.Item) (string, error) {
	var payload templatePayload

	payload.Link = item.Link
	payload.Title = item.Title

	if item.Author != nil {
		payload.Author = fmt.Sprintf("%s %s", item.Author.Name, item.Author.Email)
	}

	if len(item.Content) > 0 {
		payload.Content = template.HTML(item.Content)
	} else {
		payload.Content = template.HTML(item.Description)
	}

	var buffer bytes.Buffer

	err := mailTemplate.Execute(&buffer, payload)

	if err != nil {
		return "", err
	}

	return buffer.String(), nil
}

func NewMessage(item *gofeed.Item) (bytes.Buffer, error) {
	var b bytes.Buffer

	fromName := viper.GetString("imap.from.name")

	if item.Author != nil {
		fromName = fmt.Sprintf("%s %s", item.Author.Name, item.Author.Email)
	}

	from := []*mail.Address{{fromName, viper.GetString("imap.from.email")}}
	to := []*mail.Address{{viper.GetString("imap.to.name"), viper.GetString("imap.to.email")}}

	h := mail.NewHeader()
	h.SetContentType("multipart/alternative", nil)
	h.SetDate(*item.PublishedParsed)
	h.SetAddressList("From", from)
	h.SetAddressList("To", to)
	h.SetSubject(item.Title)

	// Create a new mail writer
	messageWriter, err := message.CreateWriter(&b, h.Header)
	defer messageWriter.Close()
	if err != nil {
		return b, err
	}

	htmlHeader := make(message.Header)
	htmlHeader.SetContentType("text/html", nil)
	htmlWriter, err := messageWriter.CreatePart(htmlHeader)
	defer htmlWriter.Close()
	if err != nil {
		return b, err
	}

	content, err := FormatContent(item)
	if err != nil {
		return b, err
	}

	io.WriteString(htmlWriter, content)

	return b, nil
}

func AppendNewItemsViaIMAP(items ItemsWithFolders) error {
	if viper.GetBool("debug") {
		log.Printf("Found %d new items", len(items))
	}

	hostPort := fmt.Sprintf("%s:%d", viper.GetString("imap.host"), viper.GetInt("imap.port"))
	c, err := client.DialTLS(hostPort, nil)
	if err != nil {
		return err
	}

	defer c.Logout()

	if err := c.Login(viper.GetString("imap.username"), viper.GetString("imap.password")); err != nil {
		return err
	}

	if viper.GetBool("debug") {
		log.Println("Logged in to IMAP")
	}

	for _, entry := range items {
		if entry.Item.PublishedParsed == nil {
			t := time.Now()
			entry.Item.PublishedParsed = &t
		}

		folderName := entry.Folder
		if viper.GetBool("imap.folder_capitalize") {
			folderName = strings.Title(folderName)
		}
		folder := fmt.Sprintf("%s/%s", viper.GetString("imap.folder_prefix"), folderName)

		_ = c.Create(folder)

		msg, err := NewMessage(entry.Item)
		if err != nil {
			return err
		}

		if viper.GetBool("debug") {
			log.Printf("Appending item to %s", folder)
		}

		literal := bytes.NewReader(msg.Bytes())
		err = c.Append(folder, []string{}, *entry.Item.PublishedParsed, literal)
		if err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2014 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package orm

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

	"github.com/issue9/orm/fetch"
)

// DB与Tx的共有接口，方便以下方法调用。
type engine interface {
	Dialect() Dialect
	Query(replace bool, query string, args ...interface{}) (*sql.Rows, error)
	Exec(replace bool, query string, args ...interface{}) (sql.Result, error)
	Prepare(replace bool, query string) (*sql.Stmt, error)
	Prefix() string
}

// 检测rval中与cols对应的字段都是有效的，且为非零值。
// 若cols的长度为0，返回false。
func checkCols(cols []*Column, rval reflect.Value) bool {
	if len(cols) == 0 {
		return false
	}

	for _, col := range cols {
		field := rval.FieldByName(col.GoName)
		if !field.IsValid() {
			return false
		}

		if reflect.Zero(col.GoType).Interface() == field.Interface() {
			return false
		}
	}
	return true
}

// 根据model中的主键或是唯一索引为sql产生where语句，
// 若两者都不存在，则返回错误信息。rval为struct的reflect.Value
func where(e engine, sql *bytes.Buffer, m *Model, rval reflect.Value) ([]interface{}, error) {
	ret := []interface{}{}

	if checkCols(m.PK, rval) {
		sql.WriteString(" WHERE ")
		for _, col := range m.PK {
			e.Dialect().Quote(sql, col.Name)
			sql.WriteString("=?")
			ret = append(ret, rval.FieldByName(col.GoName).Interface())
		}
		return ret, nil
	}

	// 若不存在pk，也不存在唯一约束
	for _, cols := range m.UniqueIndexes {
		if !checkCols(cols, rval) {
			continue
		}

		sql.WriteString(" WHERE ")
		for _, col := range cols {
			e.Dialect().Quote(sql, col.Name)
			sql.WriteString("=?")
			ret = append(ret, rval.FieldByName(col.GoName).Interface())
			sql.WriteString(" AND ")
		}
		sql.Truncate(sql.Len() - 5) // 去掉最后的" AND "五个字符
		return ret, nil
	} // end range m.UniqueIndexes

	return nil, errors.New("where:无法产生where部分语句")
}

// 插入一个对象到数据库
// 以v中的主键或是唯一索引作为where条件语句。
// 自增字段，即使指定了值，也不会被添加
func insertOne(e engine, v interface{}) error {
	m, err := newModel(v)
	if err != nil {
		return err
	}

	rval := reflect.ValueOf(v)
	for rval.Kind() == reflect.Ptr {
		rval = rval.Elem()
	}

	if rval.Kind() != reflect.Struct {
		return errors.New("insertOne:无效的v.Kind()")
	}

	keys := make([]string, 0, len(m.Cols))
	vals := make([]interface{}, 0, len(m.Cols))
	for name, col := range m.Cols {
		field := rval.FieldByName(col.GoName)
		if !field.IsValid() {
			return fmt.Errorf("insertOne:未找到该名称[%v]的值", col.GoName)
		}

		// 在为零值的情况下，若该列是AI或是有默认值，则过滤掉。无论该零值是否为手动设置的。
		if reflect.Zero(col.GoType).Interface() == field.Interface() &&
			(col.IsAI() || col.HasDefault) {
			continue
		}

		keys = append(keys, name)
		vals = append(vals, field.Interface())
	}

	if len(keys) == 0 {
		return errors.New("insertOne:未指定任何插入的列数据")
	}

	sql := bytes.NewBufferString("INSERT INTO ")
	e.Dialect().Quote(sql, e.Prefix()+m.Name)

	sql.WriteByte('(')
	for _, col := range keys {
		e.Dialect().Quote(sql, col)
		sql.WriteByte(',')
	}
	sql.Truncate(sql.Len() - 1)
	sql.WriteString(")VALUES(")
	for range vals {
		sql.WriteString("?,")
	}
	sql.Truncate(sql.Len() - 1)
	sql.WriteByte(')')

	_, err = e.Exec(false, sql.String(), vals...)
	return err
}

// 获取v对象的表名，v可以是一个结构体，也可以是一个字符串。
func getTableName(e engine, v interface{}) (string, error) {
	switch tbl := v.(type) {
	case string:
		return tbl, nil
	case []rune:
		return string(tbl), nil
	case []byte:
		return string(tbl), nil
	}

	m, err := newModel(v)
	if err != nil {
		return "", err
	}
	return m.Name, nil
}

// 创建一个或多个数据表
func createMult(e engine, objs ...interface{}) error {
	sql := new(bytes.Buffer)
	d := e.Dialect()

	for i, v := range objs {
		m, err := newModel(v)
		if err != nil {
			return err
		}

		rval := reflect.ValueOf(v)
		for rval.Kind() == reflect.Ptr {
			rval = rval.Elem()
		}

		if rval.Kind() != reflect.Struct {
			return fmt.Errorf("createMult:objs[%v]类型必须为结构体或是结构体指针", i)
		}

		sql.Reset()
		sql.WriteString("CREATE TABLE IF NOT EXISTS ")
		d.Quote(sql, e.Prefix()+m.Name)
		sql.WriteByte('(')
		d.AIColSQL(sql, m)
		d.NoAIColSQL(sql, m)
		d.ConstraintsSQL(sql, m)
		sql.Truncate(sql.Len() - 1)
		sql.WriteByte(')')

		if _, err = e.Exec(false, sql.String()); err != nil {
			return err
		}
	}

	return nil
}

// 插入一个或多个数据
// v可以是对象或是对象数组
func insertMult(e engine, objs ...interface{}) error {
	for _, obj := range objs {
		if err := insertOne(e, obj); err != nil {
			return err
		}
	}
	return nil
}

// 查找多个数据
// 根据v的pk或中唯一索引列查找一行数据，并赋值给v
func findMult(e engine, objs ...interface{}) error {
	sql := new(bytes.Buffer)

	for i, v := range objs {
		m, err := newModel(v)
		if err != nil {
			return err
		}

		rval := reflect.ValueOf(v)
		for rval.Kind() == reflect.Ptr {
			rval = rval.Elem()
		}

		if rval.Kind() != reflect.Struct {
			return fmt.Errorf("findMult:objs[%v]类型必须为结构体或是结构体指针", i)
		}

		sql.Reset()
		sql.WriteString("SELECT * FROM ")
		e.Dialect().Quote(sql, e.Prefix()+m.Name)

		vals, err := where(e, sql, m, rval)
		if err != nil {
			return err
		}

		rows, err := e.Query(false, sql.String(), vals...)
		if err != nil {
			return err
		}
		defer rows.Close()

		if err := fetch.Obj(v, rows); err != nil {
			return err
		}
	}
	return nil
}

// 更新一个或多个类型。
// 更新依据为每个对象的主键或是唯一索引列。
// 若不存在此两个类型的字段，则返回错误信息。
func updateMult(e engine, objs ...interface{}) error {
	sql := new(bytes.Buffer)
	vals := make([]interface{}, 0, 10)

	for i, v := range objs {
		m, err := newModel(v)
		if err != nil {
			return err
		}

		rval := reflect.ValueOf(v)
		for rval.Kind() == reflect.Ptr {
			rval = rval.Elem()
		}

		if rval.Kind() != reflect.Struct {
			return fmt.Errorf("updateMult:objs[%v]类型必须为结构体或是结构体指针", i)
		}

		sql.Reset()
		vals = vals[:0]

		sql.WriteString("UPDATE ")
		e.Dialect().Quote(sql, e.Prefix()+m.Name)
		sql.WriteString(" SET ")

		for name, col := range m.Cols {
			field := rval.FieldByName(col.GoName)
			if !field.IsValid() {
				return fmt.Errorf("updateMult:未找到该名称[%v]的值", col.GoName)
			}

			// 忽略零值，TODO:还需要对比默认值
			if reflect.Zero(col.GoType).Interface() == field.Interface() {
				continue
			}

			e.Dialect().Quote(sql, name)
			sql.WriteString("=?,")
			vals = append(vals, field.Interface())
		}
		sql.Truncate(sql.Len() - 1)

		whereVals, err := where(e, sql, m, rval)
		if err != nil {
			return err
		}
		vals = append(vals, whereVals...)

		if _, err = e.Exec(false, sql.String(), vals...); err != nil {
			return err
		}
	}
	return nil
}

// 删除objs每个元素表示的数据。
// 以objs中每个元素的主键或是唯一索引作为where条件语句。
func deleteMult(e engine, objs ...interface{}) error {
	sql := new(bytes.Buffer)
	for i, v := range objs {
		m, err := newModel(v)
		if err != nil {
			return err
		}

		rval := reflect.ValueOf(v)
		for rval.Kind() == reflect.Ptr {
			rval = rval.Elem()
		}

		if rval.Kind() != reflect.Struct {
			return fmt.Errorf("deleteMult:objs[%v]类型必须为结构体或是结构体指针", i)
		}

		sql.Reset()
		sql.WriteString("DELETE FROM ")
		e.Dialect().Quote(sql, e.Prefix()+m.Name)

		vals, err := where(e, sql, m, rval)
		if err != nil {
			return err
		}

		if _, err = e.Exec(false, sql.String(), vals...); err != nil {
			return err
		}
	}
	return nil
}

// 删除objs中指定的表名。
// objs可以是字符串表名，或是一个表示model的实例。
// 系统会默认给表名加上表名前缀。
func dropMult(e engine, objs ...interface{}) error {
	sql := new(bytes.Buffer)
	for _, v := range objs {
		tbl, err := getTableName(e, v)
		if err != nil {
			return err
		}

		sql.Reset()
		sql := bytes.NewBufferString("DROP TABLE IF EXISTS ")
		e.Dialect().Quote(sql, e.Prefix()+tbl)
		if _, err = e.Exec(false, sql.String()); err != nil {
			return err
		}
	}

	return nil
}

// 清空表，并重置AI计数。
// objs可以是字符串表名，或是一个表示model的实例。
// 系统会默认给表名加上表名前缀。
func truncateMult(e engine, objs ...interface{}) error {
	for _, v := range objs {
		tbl, err := getTableName(e, v)
		if err != nil {
			return err
		}

		sql := e.Dialect().TruncateTableSQL(e.Prefix() + tbl)
		if _, err = e.Exec(false, sql); err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>合并insertOne到insertMult<commit_after>// Copyright 2014 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package orm

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

	"github.com/issue9/orm/fetch"
)

// DB与Tx的共有接口，方便以下方法调用。
type engine interface {
	Dialect() Dialect
	Query(replace bool, query string, args ...interface{}) (*sql.Rows, error)
	Exec(replace bool, query string, args ...interface{}) (sql.Result, error)
	Prepare(replace bool, query string) (*sql.Stmt, error)
	Prefix() string
}

// 检测rval中与cols对应的字段都是有效的，且为非零值。
// 若cols的长度为0，返回false。
func checkCols(cols []*Column, rval reflect.Value) bool {
	if len(cols) == 0 {
		return false
	}

	for _, col := range cols {
		field := rval.FieldByName(col.GoName)
		if !field.IsValid() {
			return false
		}

		if reflect.Zero(col.GoType).Interface() == field.Interface() {
			return false
		}
	}
	return true
}

// 根据model中的主键或是唯一索引为sql产生where语句，
// 若两者都不存在，则返回错误信息。rval为struct的reflect.Value
func where(e engine, sql *bytes.Buffer, m *Model, rval reflect.Value) ([]interface{}, error) {
	ret := []interface{}{}

	if checkCols(m.PK, rval) {
		sql.WriteString(" WHERE ")
		for _, col := range m.PK {
			e.Dialect().Quote(sql, col.Name)
			sql.WriteString("=?")
			ret = append(ret, rval.FieldByName(col.GoName).Interface())
		}
		return ret, nil
	}

	// 若不存在pk，也不存在唯一约束
	for _, cols := range m.UniqueIndexes {
		if !checkCols(cols, rval) {
			continue
		}

		sql.WriteString(" WHERE ")
		for _, col := range cols {
			e.Dialect().Quote(sql, col.Name)
			sql.WriteString("=?")
			ret = append(ret, rval.FieldByName(col.GoName).Interface())
			sql.WriteString(" AND ")
		}
		sql.Truncate(sql.Len() - 5) // 去掉最后的" AND "五个字符
		return ret, nil
	} // end range m.UniqueIndexes

	return nil, errors.New("where:无法产生where部分语句")
}

// 获取v对象的表名，v可以是一个结构体，也可以是一个字符串。
func getTableName(e engine, v interface{}) (string, error) {
	switch tbl := v.(type) {
	case string:
		return tbl, nil
	case []rune:
		return string(tbl), nil
	case []byte:
		return string(tbl), nil
	}

	m, err := newModel(v)
	if err != nil {
		return "", err
	}
	return m.Name, nil
}

// 创建一个或多个数据表
func createMult(e engine, objs ...interface{}) error {
	sql := new(bytes.Buffer)
	d := e.Dialect()

	for i, v := range objs {
		m, err := newModel(v)
		if err != nil {
			return err
		}

		rval := reflect.ValueOf(v)
		for rval.Kind() == reflect.Ptr {
			rval = rval.Elem()
		}

		if rval.Kind() != reflect.Struct {
			return fmt.Errorf("createMult:objs[%v]类型必须为结构体或是结构体指针", i)
		}

		sql.Reset()
		sql.WriteString("CREATE TABLE IF NOT EXISTS ")
		d.Quote(sql, e.Prefix()+m.Name)
		sql.WriteByte('(')
		d.AIColSQL(sql, m)
		d.NoAIColSQL(sql, m)
		d.ConstraintsSQL(sql, m)
		sql.Truncate(sql.Len() - 1)
		sql.WriteByte(')')

		if _, err = e.Exec(false, sql.String()); err != nil {
			return err
		}
	}

	return nil
}

// 插入一个或多个数据
// v可以是对象或是对象数组
func insertMult(e engine, objs ...interface{}) error {
	sql := new(bytes.Buffer)
	keys := make([]string, 0, 10)
	vals := make([]interface{}, 0, 10)

	for _, v := range objs {
		m, err := newModel(v)
		if err != nil {
			return err
		}

		rval := reflect.ValueOf(v)
		for rval.Kind() == reflect.Ptr {
			rval = rval.Elem()
		}

		if rval.Kind() != reflect.Struct {
			return errors.New("insertOne:无效的v.Kind()")
		}

		keys = keys[:0]
		vals = vals[:0]
		for name, col := range m.Cols {
			field := rval.FieldByName(col.GoName)
			if !field.IsValid() {
				return fmt.Errorf("insertOne:未找到该名称[%v]的值", col.GoName)
			}

			// 在为零值的情况下，若该列是AI或是有默认值，则过滤掉。无论该零值是否为手动设置的。
			if reflect.Zero(col.GoType).Interface() == field.Interface() &&
				(col.IsAI() || col.HasDefault) {
				continue
			}

			keys = append(keys, name)
			vals = append(vals, field.Interface())
		}

		if len(keys) == 0 {
			return errors.New("insertOne:未指定任何插入的列数据")
		}

		sql.Reset()
		sql.WriteString("INSERT INTO ")
		e.Dialect().Quote(sql, e.Prefix()+m.Name)

		sql.WriteByte('(')
		for _, col := range keys {
			e.Dialect().Quote(sql, col)
			sql.WriteByte(',')
		}
		sql.Truncate(sql.Len() - 1)
		sql.WriteString(")VALUES(")
		for range vals {
			sql.WriteString("?,")
		}
		sql.Truncate(sql.Len() - 1)
		sql.WriteByte(')')

		if _, err = e.Exec(false, sql.String(), vals...); err != nil {
			return err
		}
	}
	return nil
}

// 查找多个数据
// 根据v的pk或中唯一索引列查找一行数据，并赋值给v
func findMult(e engine, objs ...interface{}) error {
	sql := new(bytes.Buffer)

	for i, v := range objs {
		m, err := newModel(v)
		if err != nil {
			return err
		}

		rval := reflect.ValueOf(v)
		for rval.Kind() == reflect.Ptr {
			rval = rval.Elem()
		}

		if rval.Kind() != reflect.Struct {
			return fmt.Errorf("findMult:objs[%v]类型必须为结构体或是结构体指针", i)
		}

		sql.Reset()
		sql.WriteString("SELECT * FROM ")
		e.Dialect().Quote(sql, e.Prefix()+m.Name)

		vals, err := where(e, sql, m, rval)
		if err != nil {
			return err
		}

		rows, err := e.Query(false, sql.String(), vals...)
		if err != nil {
			return err
		}
		defer rows.Close()

		if err := fetch.Obj(v, rows); err != nil {
			return err
		}
	}
	return nil
}

// 更新一个或多个类型。
// 更新依据为每个对象的主键或是唯一索引列。
// 若不存在此两个类型的字段，则返回错误信息。
func updateMult(e engine, objs ...interface{}) error {
	sql := new(bytes.Buffer)
	vals := make([]interface{}, 0, 10)

	for i, v := range objs {
		m, err := newModel(v)
		if err != nil {
			return err
		}

		rval := reflect.ValueOf(v)
		for rval.Kind() == reflect.Ptr {
			rval = rval.Elem()
		}

		if rval.Kind() != reflect.Struct {
			return fmt.Errorf("updateMult:objs[%v]类型必须为结构体或是结构体指针", i)
		}

		sql.Reset()
		vals = vals[:0]

		sql.WriteString("UPDATE ")
		e.Dialect().Quote(sql, e.Prefix()+m.Name)
		sql.WriteString(" SET ")

		for name, col := range m.Cols {
			field := rval.FieldByName(col.GoName)
			if !field.IsValid() {
				return fmt.Errorf("updateMult:未找到该名称[%v]的值", col.GoName)
			}

			// 忽略零值，TODO:还需要对比默认值
			if reflect.Zero(col.GoType).Interface() == field.Interface() {
				continue
			}

			e.Dialect().Quote(sql, name)
			sql.WriteString("=?,")
			vals = append(vals, field.Interface())
		}
		sql.Truncate(sql.Len() - 1)

		whereVals, err := where(e, sql, m, rval)
		if err != nil {
			return err
		}
		vals = append(vals, whereVals...)

		if _, err = e.Exec(false, sql.String(), vals...); err != nil {
			return err
		}
	}
	return nil
}

// 删除objs每个元素表示的数据。
// 以objs中每个元素的主键或是唯一索引作为where条件语句。
func deleteMult(e engine, objs ...interface{}) error {
	sql := new(bytes.Buffer)
	for i, v := range objs {
		m, err := newModel(v)
		if err != nil {
			return err
		}

		rval := reflect.ValueOf(v)
		for rval.Kind() == reflect.Ptr {
			rval = rval.Elem()
		}

		if rval.Kind() != reflect.Struct {
			return fmt.Errorf("deleteMult:objs[%v]类型必须为结构体或是结构体指针", i)
		}

		sql.Reset()
		sql.WriteString("DELETE FROM ")
		e.Dialect().Quote(sql, e.Prefix()+m.Name)

		vals, err := where(e, sql, m, rval)
		if err != nil {
			return err
		}

		if _, err = e.Exec(false, sql.String(), vals...); err != nil {
			return err
		}
	}
	return nil
}

// 删除objs中指定的表名。
// objs可以是字符串表名，或是一个表示model的实例。
// 系统会默认给表名加上表名前缀。
func dropMult(e engine, objs ...interface{}) error {
	sql := new(bytes.Buffer)
	for _, v := range objs {
		tbl, err := getTableName(e, v)
		if err != nil {
			return err
		}

		sql.Reset()
		sql := bytes.NewBufferString("DROP TABLE IF EXISTS ")
		e.Dialect().Quote(sql, e.Prefix()+tbl)
		if _, err = e.Exec(false, sql.String()); err != nil {
			return err
		}
	}

	return nil
}

// 清空表，并重置AI计数。
// objs可以是字符串表名，或是一个表示model的实例。
// 系统会默认给表名加上表名前缀。
func truncateMult(e engine, objs ...interface{}) error {
	for _, v := range objs {
		tbl, err := getTableName(e, v)
		if err != nil {
			return err
		}

		sql := e.Dialect().TruncateTableSQL(e.Prefix() + tbl)
		if _, err = e.Exec(false, sql); err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// impl generates method stubs for implementing an interface.
package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/ast"
	"go/build"
	"go/format"
	"go/parser"
	"go/printer"
	"go/token"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"text/template"

	"golang.org/x/tools/imports"
)

const usage = `impl [-dir directory] <recv> <iface>

impl generates method stubs for recv to implement iface.

Examples:

impl 'f *File' io.Reader
impl Murmur hash.Hash
impl -dir $GOPATH/src/github.com/josharian/impl Murmur hash.Hash

Don't forget the single quotes around the receiver type
to prevent shell globbing.
`

var (
	flagSrcDir = flag.String("dir", "", "package source directory, useful for vendored code")
)

// findInterface returns the import path and identifier of an interface.
// For example, given "http.ResponseWriter", findInterface returns
// "net/http", "ResponseWriter".
// If a fully qualified interface is given, such as "net/http.ResponseWriter",
// it simply parses the input.
func findInterface(iface string, srcDir string) (path string, id string, err error) {
	if len(strings.Fields(iface)) != 1 {
		return "", "", fmt.Errorf("couldn't parse interface: %s", iface)
	}

	srcPath := filepath.Join(srcDir, "__go_impl__.go")

	if slash := strings.LastIndex(iface, "/"); slash > -1 {
		// package path provided
		dot := strings.LastIndex(iface, ".")
		// make sure iface does not end with "/" (e.g. reject net/http/)
		if slash+1 == len(iface) {
			return "", "", fmt.Errorf("interface name cannot end with a '/' character: %s", iface)
		}
		// make sure iface does not end with "." (e.g. reject net/http.)
		if dot+1 == len(iface) {
			return "", "", fmt.Errorf("interface name cannot end with a '.' character: %s", iface)
		}
		// make sure iface has exactly one "." after "/" (e.g. reject net/http/httputil)
		if strings.Count(iface[slash:], ".") != 1 {
			return "", "", fmt.Errorf("invalid interface name: %s", iface)
		}
		return iface[:dot], iface[dot+1:], nil
	}

	src := []byte("package hack\n" + "var i " + iface)
	// If we couldn't determine the import path, goimports will
	// auto fix the import path.
	imp, err := imports.Process(srcPath, src, nil)
	if err != nil {
		return "", "", fmt.Errorf("couldn't parse interface: %s", iface)
	}

	// imp should now contain an appropriate import.
	// Parse out the import and the identifier.
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, srcPath, imp, 0)
	if err != nil {
		panic(err)
	}
	if len(f.Imports) == 0 {
		return "", "", fmt.Errorf("unrecognized interface: %s", iface)
	}
	raw := f.Imports[0].Path.Value   // "io"
	path, err = strconv.Unquote(raw) // io
	if err != nil {
		panic(err)
	}
	decl := f.Decls[1].(*ast.GenDecl)      // var i io.Reader
	spec := decl.Specs[0].(*ast.ValueSpec) // i io.Reader
	sel := spec.Type.(*ast.SelectorExpr)   // io.Reader
	id = sel.Sel.Name                      // Reader
	return path, id, nil
}

// Pkg is a parsed build.Package.
type Pkg struct {
	*build.Package
	*token.FileSet
}

// typeSpec locates the *ast.TypeSpec for type id in the import path.
func typeSpec(path string, id string, srcDir string) (Pkg, *ast.TypeSpec, error) {
	pkg, err := build.Import(path, srcDir, 0)
	if err != nil {
		return Pkg{}, nil, fmt.Errorf("couldn't find package %s: %v", path, err)
	}

	fset := token.NewFileSet() // share one fset across the whole package
	for _, file := range pkg.GoFiles {
		f, err := parser.ParseFile(fset, filepath.Join(pkg.Dir, file), nil, 0)
		if err != nil {
			continue
		}

		for _, decl := range f.Decls {
			decl, ok := decl.(*ast.GenDecl)
			if !ok || decl.Tok != token.TYPE {
				continue
			}
			for _, spec := range decl.Specs {
				spec := spec.(*ast.TypeSpec)
				if spec.Name.Name != id {
					continue
				}
				return Pkg{Package: pkg, FileSet: fset}, spec, nil
			}
		}
	}
	return Pkg{}, nil, fmt.Errorf("type %s not found in %s", id, path)
}

// gofmt pretty-prints e.
func (p Pkg) gofmt(e ast.Expr) string {
	var buf bytes.Buffer
	printer.Fprint(&buf, p.FileSet, e)
	return buf.String()
}

// fullType returns the fully qualified type of e.
// Examples, assuming package net/http:
// 	fullType(int) => "int"
// 	fullType(Handler) => "http.Handler"
// 	fullType(io.Reader) => "io.Reader"
// 	fullType(*Request) => "*http.Request"
func (p Pkg) fullType(e ast.Expr) string {
	ast.Inspect(e, func(n ast.Node) bool {
		switch n := n.(type) {
		case *ast.Ident:
			// Using typeSpec instead of IsExported here would be
			// more accurate, but it'd be crazy expensive, and if
			// the type isn't exported, there's no point trying
			// to implement it anyway.
			if n.IsExported() {
				n.Name = p.Package.Name + "." + n.Name
			}
		case *ast.SelectorExpr:
			return false
		}
		return true
	})
	return p.gofmt(e)
}

func (p Pkg) params(field *ast.Field) []Param {
	var params []Param
	typ := p.fullType(field.Type)
	for _, name := range field.Names {
		params = append(params, Param{Name: name.Name, Type: typ})
	}
	// Handle anonymous params
	if len(params) == 0 {
		params = []Param{Param{Type: typ}}
	}
	return params
}

// Method represents a method signature.
type Method struct {
	Recv string
	Func
}

// Func represents a function signature.
type Func struct {
	Name   string
	Params []Param
	Res    []Param
}

// Param represents a parameter in a function or method signature.
type Param struct {
	Name string
	Type string
}

func (p Pkg) funcsig(f *ast.Field) Func {
	fn := Func{Name: f.Names[0].Name}
	typ := f.Type.(*ast.FuncType)
	if typ.Params != nil {
		for _, field := range typ.Params.List {
			fn.Params = append(fn.Params, p.params(field)...)
		}
	}
	if typ.Results != nil {
		for _, field := range typ.Results.List {
			fn.Res = append(fn.Res, p.params(field)...)
		}
	}
	return fn
}

// funcs returns the set of methods required to implement iface.
// It is called funcs rather than methods because the
// function descriptions are functions; there is no receiver.
func funcs(iface string, srcDir string) ([]Func, error) {
	// Locate the interface.
	path, id, err := findInterface(iface, srcDir)
	if err != nil {
		return nil, err
	}

	// Parse the package and find the interface declaration.
	p, spec, err := typeSpec(path, id, srcDir)
	if err != nil {
		return nil, fmt.Errorf("interface %s not found: %s", iface, err)
	}
	idecl, ok := spec.Type.(*ast.InterfaceType)
	if !ok {
		return nil, fmt.Errorf("not an interface: %s", iface)
	}

	if idecl.Methods == nil {
		return nil, fmt.Errorf("empty interface: %s", iface)
	}

	var fns []Func
	for _, fndecl := range idecl.Methods.List {
		if len(fndecl.Names) == 0 {
			// Embedded interface: recurse
			embedded, err := funcs(p.fullType(fndecl.Type), srcDir)
			if err != nil {
				return nil, err
			}
			fns = append(fns, embedded...)
			continue
		}

		fn := p.funcsig(fndecl)
		fns = append(fns, fn)
	}
	return fns, nil
}

const stub = "func ({{.Recv}}) {{.Name}}" +
	"({{range .Params}}{{.Name}} {{.Type}}, {{end}})" +
	"({{range .Res}}{{.Name}} {{.Type}}, {{end}})" +
	"{\n" + "panic(\"not implemented\")" + "}\n\n"

var tmpl = template.Must(template.New("test").Parse(stub))

// genStubs prints nicely formatted method stubs
// for fns using receiver expression recv.
// If recv is not a valid receiver expression,
// genStubs will panic.
func genStubs(recv string, fns []Func) []byte {
	var buf bytes.Buffer
	for _, fn := range fns {
		meth := Method{Recv: recv, Func: fn}
		tmpl.Execute(&buf, meth)
	}

	pretty, err := format.Source(buf.Bytes())
	if err != nil {
		panic(err)
	}
	return pretty
}

// validReceiver reports whether recv is a valid receiver expression.
func validReceiver(recv string) bool {
	if recv == "" {
		// The parse will parse empty receivers, but we don't want to accept them,
		// since it won't generate a usable code snippet.
		return false
	}
	fset := token.NewFileSet()
	_, err := parser.ParseFile(fset, "", "package hack\nfunc ("+recv+") Foo()", 0)
	return err == nil
}

func main() {
	flag.Parse()

	if len(flag.Args()) < 2 {
		fmt.Fprint(os.Stderr, usage)
		os.Exit(2)
	}

	recv, iface := flag.Arg(0), flag.Arg(1)
	if !validReceiver(recv) {
		fatal(fmt.Sprintf("invalid receiver: %q", recv))
	}

	if *flagSrcDir == "" {
		if dir, err := os.Getwd(); err != nil {
			*flagSrcDir = dir
		}
	}

	fns, err := funcs(iface, *flagSrcDir)
	if err != nil {
		fatal(err)
	}

	src := genStubs(recv, fns)
	fmt.Print(string(src))
}

func fatal(msg interface{}) {
	fmt.Fprintln(os.Stderr, msg)
	os.Exit(1)
}
<commit_msg>Fix inverted err == nil check.<commit_after>// impl generates method stubs for implementing an interface.
package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/ast"
	"go/build"
	"go/format"
	"go/parser"
	"go/printer"
	"go/token"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"text/template"

	"golang.org/x/tools/imports"
)

const usage = `impl [-dir directory] <recv> <iface>

impl generates method stubs for recv to implement iface.

Examples:

impl 'f *File' io.Reader
impl Murmur hash.Hash
impl -dir $GOPATH/src/github.com/josharian/impl Murmur hash.Hash

Don't forget the single quotes around the receiver type
to prevent shell globbing.
`

var (
	flagSrcDir = flag.String("dir", "", "package source directory, useful for vendored code")
)

// findInterface returns the import path and identifier of an interface.
// For example, given "http.ResponseWriter", findInterface returns
// "net/http", "ResponseWriter".
// If a fully qualified interface is given, such as "net/http.ResponseWriter",
// it simply parses the input.
func findInterface(iface string, srcDir string) (path string, id string, err error) {
	if len(strings.Fields(iface)) != 1 {
		return "", "", fmt.Errorf("couldn't parse interface: %s", iface)
	}

	srcPath := filepath.Join(srcDir, "__go_impl__.go")

	if slash := strings.LastIndex(iface, "/"); slash > -1 {
		// package path provided
		dot := strings.LastIndex(iface, ".")
		// make sure iface does not end with "/" (e.g. reject net/http/)
		if slash+1 == len(iface) {
			return "", "", fmt.Errorf("interface name cannot end with a '/' character: %s", iface)
		}
		// make sure iface does not end with "." (e.g. reject net/http.)
		if dot+1 == len(iface) {
			return "", "", fmt.Errorf("interface name cannot end with a '.' character: %s", iface)
		}
		// make sure iface has exactly one "." after "/" (e.g. reject net/http/httputil)
		if strings.Count(iface[slash:], ".") != 1 {
			return "", "", fmt.Errorf("invalid interface name: %s", iface)
		}
		return iface[:dot], iface[dot+1:], nil
	}

	src := []byte("package hack\n" + "var i " + iface)
	// If we couldn't determine the import path, goimports will
	// auto fix the import path.
	imp, err := imports.Process(srcPath, src, nil)
	if err != nil {
		return "", "", fmt.Errorf("couldn't parse interface: %s", iface)
	}

	// imp should now contain an appropriate import.
	// Parse out the import and the identifier.
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, srcPath, imp, 0)
	if err != nil {
		panic(err)
	}
	if len(f.Imports) == 0 {
		return "", "", fmt.Errorf("unrecognized interface: %s", iface)
	}
	raw := f.Imports[0].Path.Value   // "io"
	path, err = strconv.Unquote(raw) // io
	if err != nil {
		panic(err)
	}
	decl := f.Decls[1].(*ast.GenDecl)      // var i io.Reader
	spec := decl.Specs[0].(*ast.ValueSpec) // i io.Reader
	sel := spec.Type.(*ast.SelectorExpr)   // io.Reader
	id = sel.Sel.Name                      // Reader
	return path, id, nil
}

// Pkg is a parsed build.Package.
type Pkg struct {
	*build.Package
	*token.FileSet
}

// typeSpec locates the *ast.TypeSpec for type id in the import path.
func typeSpec(path string, id string, srcDir string) (Pkg, *ast.TypeSpec, error) {
	pkg, err := build.Import(path, srcDir, 0)
	if err != nil {
		return Pkg{}, nil, fmt.Errorf("couldn't find package %s: %v", path, err)
	}

	fset := token.NewFileSet() // share one fset across the whole package
	for _, file := range pkg.GoFiles {
		f, err := parser.ParseFile(fset, filepath.Join(pkg.Dir, file), nil, 0)
		if err != nil {
			continue
		}

		for _, decl := range f.Decls {
			decl, ok := decl.(*ast.GenDecl)
			if !ok || decl.Tok != token.TYPE {
				continue
			}
			for _, spec := range decl.Specs {
				spec := spec.(*ast.TypeSpec)
				if spec.Name.Name != id {
					continue
				}
				return Pkg{Package: pkg, FileSet: fset}, spec, nil
			}
		}
	}
	return Pkg{}, nil, fmt.Errorf("type %s not found in %s", id, path)
}

// gofmt pretty-prints e.
func (p Pkg) gofmt(e ast.Expr) string {
	var buf bytes.Buffer
	printer.Fprint(&buf, p.FileSet, e)
	return buf.String()
}

// fullType returns the fully qualified type of e.
// Examples, assuming package net/http:
// 	fullType(int) => "int"
// 	fullType(Handler) => "http.Handler"
// 	fullType(io.Reader) => "io.Reader"
// 	fullType(*Request) => "*http.Request"
func (p Pkg) fullType(e ast.Expr) string {
	ast.Inspect(e, func(n ast.Node) bool {
		switch n := n.(type) {
		case *ast.Ident:
			// Using typeSpec instead of IsExported here would be
			// more accurate, but it'd be crazy expensive, and if
			// the type isn't exported, there's no point trying
			// to implement it anyway.
			if n.IsExported() {
				n.Name = p.Package.Name + "." + n.Name
			}
		case *ast.SelectorExpr:
			return false
		}
		return true
	})
	return p.gofmt(e)
}

func (p Pkg) params(field *ast.Field) []Param {
	var params []Param
	typ := p.fullType(field.Type)
	for _, name := range field.Names {
		params = append(params, Param{Name: name.Name, Type: typ})
	}
	// Handle anonymous params
	if len(params) == 0 {
		params = []Param{Param{Type: typ}}
	}
	return params
}

// Method represents a method signature.
type Method struct {
	Recv string
	Func
}

// Func represents a function signature.
type Func struct {
	Name   string
	Params []Param
	Res    []Param
}

// Param represents a parameter in a function or method signature.
type Param struct {
	Name string
	Type string
}

func (p Pkg) funcsig(f *ast.Field) Func {
	fn := Func{Name: f.Names[0].Name}
	typ := f.Type.(*ast.FuncType)
	if typ.Params != nil {
		for _, field := range typ.Params.List {
			fn.Params = append(fn.Params, p.params(field)...)
		}
	}
	if typ.Results != nil {
		for _, field := range typ.Results.List {
			fn.Res = append(fn.Res, p.params(field)...)
		}
	}
	return fn
}

// funcs returns the set of methods required to implement iface.
// It is called funcs rather than methods because the
// function descriptions are functions; there is no receiver.
func funcs(iface string, srcDir string) ([]Func, error) {
	// Locate the interface.
	path, id, err := findInterface(iface, srcDir)
	if err != nil {
		return nil, err
	}

	// Parse the package and find the interface declaration.
	p, spec, err := typeSpec(path, id, srcDir)
	if err != nil {
		return nil, fmt.Errorf("interface %s not found: %s", iface, err)
	}
	idecl, ok := spec.Type.(*ast.InterfaceType)
	if !ok {
		return nil, fmt.Errorf("not an interface: %s", iface)
	}

	if idecl.Methods == nil {
		return nil, fmt.Errorf("empty interface: %s", iface)
	}

	var fns []Func
	for _, fndecl := range idecl.Methods.List {
		if len(fndecl.Names) == 0 {
			// Embedded interface: recurse
			embedded, err := funcs(p.fullType(fndecl.Type), srcDir)
			if err != nil {
				return nil, err
			}
			fns = append(fns, embedded...)
			continue
		}

		fn := p.funcsig(fndecl)
		fns = append(fns, fn)
	}
	return fns, nil
}

const stub = "func ({{.Recv}}) {{.Name}}" +
	"({{range .Params}}{{.Name}} {{.Type}}, {{end}})" +
	"({{range .Res}}{{.Name}} {{.Type}}, {{end}})" +
	"{\n" + "panic(\"not implemented\")" + "}\n\n"

var tmpl = template.Must(template.New("test").Parse(stub))

// genStubs prints nicely formatted method stubs
// for fns using receiver expression recv.
// If recv is not a valid receiver expression,
// genStubs will panic.
func genStubs(recv string, fns []Func) []byte {
	var buf bytes.Buffer
	for _, fn := range fns {
		meth := Method{Recv: recv, Func: fn}
		tmpl.Execute(&buf, meth)
	}

	pretty, err := format.Source(buf.Bytes())
	if err != nil {
		panic(err)
	}
	return pretty
}

// validReceiver reports whether recv is a valid receiver expression.
func validReceiver(recv string) bool {
	if recv == "" {
		// The parse will parse empty receivers, but we don't want to accept them,
		// since it won't generate a usable code snippet.
		return false
	}
	fset := token.NewFileSet()
	_, err := parser.ParseFile(fset, "", "package hack\nfunc ("+recv+") Foo()", 0)
	return err == nil
}

func main() {
	flag.Parse()

	if len(flag.Args()) < 2 {
		fmt.Fprint(os.Stderr, usage)
		os.Exit(2)
	}

	recv, iface := flag.Arg(0), flag.Arg(1)
	if !validReceiver(recv) {
		fatal(fmt.Sprintf("invalid receiver: %q", recv))
	}

	if *flagSrcDir == "" {
		if dir, err := os.Getwd(); err == nil {
			*flagSrcDir = dir
		}
	}

	fns, err := funcs(iface, *flagSrcDir)
	if err != nil {
		fatal(err)
	}

	src := genStubs(recv, fns)
	fmt.Print(string(src))
}

func fatal(msg interface{}) {
	fmt.Fprintln(os.Stderr, msg)
	os.Exit(1)
}
<|endoftext|>


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

import (
	"testing"

	"github.com/google/certificate-transparency-go/testdata"
	"github.com/google/certificate-transparency-go/trillian/ctfe"
	"github.com/google/certificate-transparency-go/x509"
	"github.com/google/certificate-transparency-go/x509util"

	"github.com/kylelemons/godebug/pretty"
)

func subLogList(logURLs map[string]bool) LogList {
	var ll LogList
	for _, op := range sampleLogList.Operators {
		opCopy := *op
		opCopy.Logs = []*Log{}
		for _, l := range op.Logs {
			if logURLs[l.URL] {
				opCopy.Logs = append(opCopy.Logs, l)
			}
		}
		if len(opCopy.Logs) > 0 {
			ll.Operators = append(ll.Operators, &opCopy)
		}
	}
	return ll
}

func TestSelectUsable(t *testing.T) {
	tests := []struct {
		name string
		in   LogList
		want LogList
	}{
		{
			name: "Sample",
			in:   sampleLogList,
			want: subLogList(map[string]bool{"ct.googleapis.com/icarus/": true}),
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			got := test.in.SelectUsable()
			if diff := pretty.Compare(got, test.want); diff != "" {
				t.Errorf("Extracting active logs out of %v diff: (-got + want)\n%s", test.in, diff)
			}
		})
	}
}

func certChain() []*x509.Certificate {
	cert, _ := x509util.CertificateFromPEM([]byte(testdata.TestPreCertPEM))
	caCert, _ := x509util.CertificateFromPEM([]byte(testdata.CACertPEM))
	return []*x509.Certificate{cert, caCert}
}

func singleCert() []*x509.Certificate {
	cert, _ := x509util.CertificateFromPEM([]byte(testdata.TestPreCertPEM))
	return []*x509.Certificate{cert}
}

func artificialRoots(source string) LogRoots {
	roots := LogRoots{
		"log.bob.io":                           ctfe.NewPEMCertPool(),
		"https://ct.googleapis.com/racketeer/": ctfe.NewPEMCertPool(),
		"https://ct.googleapis.com/rocketeer/": ctfe.NewPEMCertPool(),
		"https://ct.googleapis.com/aviator/":   ctfe.NewPEMCertPool(),
	}
	roots["log.bob.io"].AppendCertsFromPEM([]byte(source))
	return roots
}

func TestRootCompatible(t *testing.T) {
	tests := []struct {
		name  string
		in    LogList
		chain []*x509.Certificate
		roots LogRoots
		want  LogList
	}{
		{
			name:  "RootedChain",
			in:    sampleLogList,
			chain: certChain(),
			roots: artificialRoots(testdata.CACertPEM),
			want:  subLogList(map[string]bool{"log.bob.io": true, "https://ct.googleapis.com/icarus/": true}), // icarus has no root info.
		},
		{
			name:  "RootedChainNoRootAccepted",
			in:    sampleLogList,
			chain: certChain(),
			roots: artificialRoots(testdata.TestPreCertPEM),
			want:  subLogList(map[string]bool{"https://ct.googleapis.com/icarus/": true}), // icarus has no root info.
		},
		{
			name:  "UnRootedChain",
			in:    sampleLogList,
			chain: singleCert(),
			roots: artificialRoots(testdata.CACertPEM),
			want:  subLogList(map[string]bool{}),
		},
		{
			name:  "EmptyChain",
			in:    sampleLogList,
			chain: []*x509.Certificate{},
			roots: artificialRoots(testdata.CACertPEM),
			want:  subLogList(map[string]bool{"https://ct.googleapis.com/icarus/": true}),
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			got := test.in.RootCompatible(test.chain, test.roots)
			if diff := pretty.Compare(got, test.want); diff != "" {
				t.Errorf("Getting compatible logs diff: (-got +want)\n%s", diff)
			}
		})
	}
}
<commit_msg>Use conventional output for diffs in logfilter_test.go<commit_after>// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package loglist2

import (
	"testing"

	"github.com/google/certificate-transparency-go/testdata"
	"github.com/google/certificate-transparency-go/trillian/ctfe"
	"github.com/google/certificate-transparency-go/x509"
	"github.com/google/certificate-transparency-go/x509util"

	"github.com/kylelemons/godebug/pretty"
)

func subLogList(logURLs map[string]bool) LogList {
	var ll LogList
	for _, op := range sampleLogList.Operators {
		opCopy := *op
		opCopy.Logs = []*Log{}
		for _, l := range op.Logs {
			if logURLs[l.URL] {
				opCopy.Logs = append(opCopy.Logs, l)
			}
		}
		if len(opCopy.Logs) > 0 {
			ll.Operators = append(ll.Operators, &opCopy)
		}
	}
	return ll
}

func TestSelectUsable(t *testing.T) {
	tests := []struct {
		name string
		in   LogList
		want LogList
	}{
		{
			name: "Sample",
			in:   sampleLogList,
			want: subLogList(map[string]bool{"ct.googleapis.com/icarus/": true}),
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			got := test.in.SelectUsable()
			if diff := pretty.Compare(test.want, got); diff != "" {
				t.Errorf("Extracting active logs out of %v diff: (-want +got)\n%s", test.in, diff)
			}
		})
	}
}

func certChain() []*x509.Certificate {
	cert, _ := x509util.CertificateFromPEM([]byte(testdata.TestPreCertPEM))
	caCert, _ := x509util.CertificateFromPEM([]byte(testdata.CACertPEM))
	return []*x509.Certificate{cert, caCert}
}

func singleCert() []*x509.Certificate {
	cert, _ := x509util.CertificateFromPEM([]byte(testdata.TestPreCertPEM))
	return []*x509.Certificate{cert}
}

func artificialRoots(source string) LogRoots {
	roots := LogRoots{
		"log.bob.io":                           ctfe.NewPEMCertPool(),
		"https://ct.googleapis.com/racketeer/": ctfe.NewPEMCertPool(),
		"https://ct.googleapis.com/rocketeer/": ctfe.NewPEMCertPool(),
		"https://ct.googleapis.com/aviator/":   ctfe.NewPEMCertPool(),
	}
	roots["log.bob.io"].AppendCertsFromPEM([]byte(source))
	return roots
}

func TestRootCompatible(t *testing.T) {
	tests := []struct {
		name  string
		in    LogList
		chain []*x509.Certificate
		roots LogRoots
		want  LogList
	}{
		{
			name:  "RootedChain",
			in:    sampleLogList,
			chain: certChain(),
			roots: artificialRoots(testdata.CACertPEM),
			want:  subLogList(map[string]bool{"log.bob.io": true, "https://ct.googleapis.com/icarus/": true}), // icarus has no root info.
		},
		{
			name:  "RootedChainNoRootAccepted",
			in:    sampleLogList,
			chain: certChain(),
			roots: artificialRoots(testdata.TestPreCertPEM),
			want:  subLogList(map[string]bool{"https://ct.googleapis.com/icarus/": true}), // icarus has no root info.
		},
		{
			name:  "UnRootedChain",
			in:    sampleLogList,
			chain: singleCert(),
			roots: artificialRoots(testdata.CACertPEM),
			want:  subLogList(map[string]bool{}),
		},
		{
			name:  "EmptyChain",
			in:    sampleLogList,
			chain: []*x509.Certificate{},
			roots: artificialRoots(testdata.CACertPEM),
			want:  subLogList(map[string]bool{"https://ct.googleapis.com/icarus/": true}),
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			got := test.in.RootCompatible(test.chain, test.roots)
			if diff := pretty.Compare(test.want, got); diff != "" {
				t.Errorf("Getting compatible logs diff: (-want +got)\n%s", diff)
			}
		})
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"time"

	"github.com/apcera/termtables"
	"github.com/aws/aws-sdk-go/service/lambda"
	"github.com/cheggaaa/pb"
)

func listFunctions(svc *lambda.Lambda, region string, rx *regexp.Regexp) {
	resp, err := svc.ListFunctions(nil)
	if err != nil {
		log.Fatalln(err)
	} else if len(resp.Functions) == 0 {
		fmt.Println("0 lambda functions available.")
		return
	}
	table := termtables.CreateTable()
	table.UTF8Box()
	table.AddHeaders("#", "NAME", "UPDATED", "SIZE", "MEM LIMIT", "TIMEOUT", "DESCRIPTION")
	var filtered int
	for i, f := range resp.Functions {
		if rx != nil && !rx.MatchString(*f.FunctionName) {
			continue
		}
		filtered++
		table.AddRow(strconv.Itoa(i+1), *f.FunctionName,
			parseDate(*f.LastModified).Format(time.RFC822),
			fmt.Sprintf("%.2fK", float32(*f.CodeSize)/1024.0),
			fmt.Sprintf("%dM", *f.MemorySize),
			time.Second*time.Duration(*f.Timeout), *f.Description)
	}
	if filtered > 0 {
		table.AddTitle(fmt.Sprintf("AWS LAMBDA FUNCTIONS: %d (%s)", filtered, region))
		fmt.Println(table.Render())
		return
	}
	fmt.Printf("0 lambda functions matched `%s` pattern.\n", rx.String())
}

func parseDate(v string) time.Time {
	t, _ := time.ParseInLocation("2006-01-02T15:04:05.999-0700", v, time.UTC)
	return t
}

func findFuction(svc *lambda.Lambda, id int, name string) *lambda.FunctionConfiguration {
	listResp, err := svc.ListFunctions(nil)
	if err != nil {
		log.Fatalln(err)
	} else if len(listResp.Functions) == 0 {
		fmt.Println("error: 0 lambda functions available.")
		os.Exit(-1)
	}
	for i, f := range listResp.Functions {
		if i+1 == id || *f.FunctionName == name {
			return f
		}
	}
	switch {
	case id > 0:
		fmt.Printf("error: no such function with ID=%d found.\n", id)
	case len(name) > 0:
		fmt.Printf("error: no such function with NAME=%s found.\n", name)
	}
	os.Exit(-1)
	return nil
}

func functionSource(svc *lambda.Lambda, f *lambda.FunctionConfiguration, path string, onlyURL bool) {
	getResp, err := svc.GetFunction(&lambda.GetFunctionInput{
		FunctionName: f.FunctionName,
		Qualifier:    f.Version,
	})
	if err != nil {
		log.Println("error:", err)
		return
	}
	if onlyURL {
		log.Println(*getResp.Code.Location)
		return
	}
	resp, err := http.Get(*getResp.Code.Location)
	if err != nil {
		log.Fatalln(err)
	}
	defer resp.Body.Close()
	out, err := os.Create(path)
	if err != nil {
		log.Fatalln(err)
	}
	bar := pb.New(int(resp.ContentLength)).SetUnits(pb.U_BYTES).SetRefreshRate(time.Millisecond * 10)
	barproxy := bar.NewProxyReader(resp.Body)
	bar.ShowSpeed = true
	bar.Start()
	io.Copy(out, barproxy)
	out.Close()
	bar.Finish()
}

func functionInfo(f *lambda.FunctionConfiguration, region string) {
	table := termtables.CreateTable()
	table.UTF8Box()
	table.AddTitle(fmt.Sprintf("AWS LAMBDA FUNCTION %s (%s)", *f.FunctionName, region))
	table.AddRow("SHA256 Hash", *f.CodeSha256)
	table.AddRow("Code Size", fmt.Sprintf("%.2fK", float32(*f.CodeSize)/1024.0))
	table.AddRow("Description", *f.Description)
	table.AddRow("Amazon Resource Name", *f.FunctionArn)
	table.AddRow("Handler", *f.Handler)
	table.AddRow("Last Modified", parseDate(*f.LastModified).Format(time.RFC822))
	table.AddRow("Memory Size", fmt.Sprintf("%dM", *f.MemorySize))
	table.AddRow("Role", *f.Role)
	table.AddRow("Runtime", *f.Runtime)
	table.AddRow("Timeout", time.Second*time.Duration(*f.Timeout))
	table.AddRow("Version", *f.Version)
	fmt.Println(table.Render())
}

func listRegions() {
	table := termtables.CreateTable()
	table.UTF8Box()
	table.AddTitle("AWS LAMBDA REGIONS (2015-12-20)")
	table.AddRow("us-east-1", "US East (N. Virginia)")
	table.AddRow("us-west-2", "US West (Oregon)")
	table.AddRow("eu-west-1", "EU (Ireland)")
	table.AddRow("ap-northeast-1", "Asia Pacific (Tokyo)")
	fmt.Println(table.Render())
}
<commit_msg>Timporarily disable pb.<commit_after>package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"time"

	"github.com/apcera/termtables"
	"github.com/aws/aws-sdk-go/service/lambda"
)

func listFunctions(svc *lambda.Lambda, region string, rx *regexp.Regexp) {
	resp, err := svc.ListFunctions(nil)
	if err != nil {
		log.Fatalln(err)
	} else if len(resp.Functions) == 0 {
		fmt.Println("0 lambda functions available.")
		return
	}
	table := termtables.CreateTable()
	table.UTF8Box()
	table.AddHeaders("#", "NAME", "UPDATED", "SIZE", "MEM LIMIT", "TIMEOUT", "DESCRIPTION")
	var filtered int
	for i, f := range resp.Functions {
		if rx != nil && !rx.MatchString(*f.FunctionName) {
			continue
		}
		filtered++
		table.AddRow(strconv.Itoa(i+1), *f.FunctionName,
			parseDate(*f.LastModified).Format(time.RFC822),
			fmt.Sprintf("%.2fK", float32(*f.CodeSize)/1024.0),
			fmt.Sprintf("%dM", *f.MemorySize),
			time.Second*time.Duration(*f.Timeout), *f.Description)
	}
	if filtered > 0 {
		table.AddTitle(fmt.Sprintf("AWS LAMBDA FUNCTIONS: %d (%s)", filtered, region))
		fmt.Println(table.Render())
		return
	}
	fmt.Printf("0 lambda functions matched `%s` pattern.\n", rx.String())
}

func parseDate(v string) time.Time {
	t, _ := time.ParseInLocation("2006-01-02T15:04:05.999-0700", v, time.UTC)
	return t
}

func findFuction(svc *lambda.Lambda, id int, name string) *lambda.FunctionConfiguration {
	listResp, err := svc.ListFunctions(nil)
	if err != nil {
		log.Fatalln(err)
	} else if len(listResp.Functions) == 0 {
		fmt.Println("error: 0 lambda functions available.")
		os.Exit(-1)
	}
	for i, f := range listResp.Functions {
		if i+1 == id || *f.FunctionName == name {
			return f
		}
	}
	switch {
	case id > 0:
		fmt.Printf("error: no such function with ID=%d found.\n", id)
	case len(name) > 0:
		fmt.Printf("error: no such function with NAME=%s found.\n", name)
	}
	os.Exit(-1)
	return nil
}

func functionSource(svc *lambda.Lambda, f *lambda.FunctionConfiguration, path string, onlyURL bool) {
	getResp, err := svc.GetFunction(&lambda.GetFunctionInput{
		FunctionName: f.FunctionName,
		Qualifier:    f.Version,
	})
	if err != nil {
		log.Println("error:", err)
		return
	}
	if onlyURL {
		log.Println(*getResp.Code.Location)
		return
	}
	resp, err := http.Get(*getResp.Code.Location)
	if err != nil {
		log.Fatalln(err)
	}
	defer resp.Body.Close()
	out, err := os.Create(path)
	if err != nil {
		log.Fatalln(err)
	}
	// Temporarily turn off pb due to an unknown bug in master
	//	bar := pb.New(int(resp.ContentLength)).SetUnits(pb.U_BYTES).SetRefreshRate(time.Millisecond * 10)
	//	barproxy := bar.NewProxyReader(resp.Body)
	//	bar.ShowSpeed = true
	//	bar.Start()
	//	io.Copy(out, barproxy)
	io.Copy(out, resp.Body)
	out.Close()
	// bar.Finish()
}

func functionInfo(f *lambda.FunctionConfiguration, region string) {
	table := termtables.CreateTable()
	table.UTF8Box()
	table.AddTitle(fmt.Sprintf("AWS LAMBDA FUNCTION %s (%s)", *f.FunctionName, region))
	table.AddRow("SHA256 Hash", *f.CodeSha256)
	table.AddRow("Code Size", fmt.Sprintf("%.2fK", float32(*f.CodeSize)/1024.0))
	table.AddRow("Description", *f.Description)
	table.AddRow("Amazon Resource Name", *f.FunctionArn)
	table.AddRow("Handler", *f.Handler)
	table.AddRow("Last Modified", parseDate(*f.LastModified).Format(time.RFC822))
	table.AddRow("Memory Size", fmt.Sprintf("%dM", *f.MemorySize))
	table.AddRow("Role", *f.Role)
	table.AddRow("Runtime", *f.Runtime)
	table.AddRow("Timeout", time.Second*time.Duration(*f.Timeout))
	table.AddRow("Version", *f.Version)
	fmt.Println(table.Render())
}

func listRegions() {
	table := termtables.CreateTable()
	table.UTF8Box()
	table.AddTitle("AWS LAMBDA REGIONS (2015-12-20)")
	table.AddRow("us-east-1", "US East (N. Virginia)")
	table.AddRow("us-west-2", "US West (Oregon)")
	table.AddRow("eu-west-1", "EU (Ireland)")
	table.AddRow("ap-northeast-1", "Asia Pacific (Tokyo)")
	fmt.Println(table.Render())
}
<|endoftext|>