<commit_before>package goreq

import (
	"bufio"
	"bytes"
	"compress/flate"
	"compress/gzip"
	"compress/zlib"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"reflect"
	"strings"
	"time"
)

type Request struct {
	headers           []headerTuple
	Method            string
	Uri               string
	Body              interface{}
	QueryString       interface{}
	Timeout           time.Duration
	ContentType       string
	Accept            string
	Host              string
	UserAgent         string
	Insecure          bool
	MaxRedirects      int
	RedirectHeaders   bool
	Proxy             string
	Compression       *compression
	BasicAuthUsername string
	BasicAuthPassword string
	ShowDebug         bool
}

type compression struct {
	writer          func(buffer io.Writer) (io.WriteCloser, error)
	reader          func(buffer io.Reader) (io.ReadCloser, error)
	ContentEncoding string
}

type Response struct {
	StatusCode    int
	ContentLength int64
	Body          *Body
	Header        http.Header
}

type headerTuple struct {
	name  string
	value string
}

type Body struct {
	reader           io.ReadCloser
	compressedReader io.ReadCloser
}

type Error struct {
	timeout bool
	Err     error
}

func (e *Error) Timeout() bool {
	return e.timeout
}

func (e *Error) Error() string {
	return e.Err.Error()
}

func (b *Body) Read(p []byte) (int, error) {
	if b.compressedReader != nil {
		return b.compressedReader.Read(p)
	}
	return b.reader.Read(p)
}

func (b *Body) Close() error {
	err := b.reader.Close()
	if b.compressedReader != nil {
		return b.compressedReader.Close()
	}
	return err
}

func (b *Body) FromJsonTo(o interface{}) error {
	if body, err := ioutil.ReadAll(b); err != nil {
		return err
	} else if err := json.Unmarshal(body, o); err != nil {
		return err
	}

	return nil
}

func (b *Body) ToString() (string, error) {
	body, err := ioutil.ReadAll(b)
	if err != nil {
		return "", err
	}
	return string(body), nil
}

func Gzip() *compression {
	reader := func(buffer io.Reader) (io.ReadCloser, error) {
		return gzip.NewReader(buffer)
	}
	writer := func(buffer io.Writer) (io.WriteCloser, error) {
		return gzip.NewWriter(buffer), nil
	}
	return &compression{writer: writer, reader: reader, ContentEncoding: "gzip"}
}

func Deflate() *compression {
	reader := func(buffer io.Reader) (io.ReadCloser, error) {
		return flate.NewReader(buffer), nil
	}
	writer := func(buffer io.Writer) (io.WriteCloser, error) {
		return flate.NewWriter(buffer, -1)
	}
	return &compression{writer: writer, reader: reader, ContentEncoding: "deflate"}
}

func Zlib() *compression {
	reader := func(buffer io.Reader) (io.ReadCloser, error) {
		return zlib.NewReader(buffer)
	}
	writer := func(buffer io.Writer) (io.WriteCloser, error) {
		return zlib.NewWriter(buffer), nil
	}
	return &compression{writer: writer, reader: reader, ContentEncoding: "deflate"}
}

func paramParse(query interface{}) (string, error) {
	switch query.(type) {
	case url.Values:
		return query.(url.Values).Encode(), nil
	case *url.Values:
		return query.(*url.Values).Encode(), nil
	default:
		var (
			v = &url.Values{}
			s = reflect.ValueOf(query)
			t = reflect.TypeOf(query)
		)
		for t.Kind() == reflect.Ptr || t.Kind() == reflect.Interface {
			s = s.Elem()
			t = s.Type()
		}
		if t.Kind() == reflect.Struct {
			for i := 0; i < t.NumField(); i++ {
				if !s.Field(i).CanInterface() {
					continue
				}
				v.Add(strings.ToLower(t.Field(i).Name), fmt.Sprintf("%v", s.Field(i).Interface()))
			}
			return v.Encode(), nil
		} else {
			return "", errors.New("Can not parse QueryString.")
		}
	}
}

func prepareRequestBody(b interface{}) (io.Reader, error) {
	switch b.(type) {
	case string:
		// treat is as text
		return strings.NewReader(b.(string)), nil
	case io.Reader:
		// treat is as text
		return b.(io.Reader), nil
	case []byte:
		//treat as byte array
		return bytes.NewReader(b.([]byte)), nil
	case nil:
		return nil, nil
	default:
		// try to jsonify it
		j, err := json.Marshal(b)
		if err == nil {
			return bytes.NewReader(j), nil
		}
		return nil, err
	}
}

var defaultDialer = &net.Dialer{Timeout: 1000 * time.Millisecond}
var defaultTransport = &http.Transport{Dial: defaultDialer.Dial, Proxy: http.ProxyFromEnvironment}
var defaultClient = &http.Client{Transport: defaultTransport}

var proxyTransport *http.Transport
var proxyClient *http.Client

func SetConnectTimeout(duration time.Duration) {
	defaultDialer.Timeout = duration
}

func (r *Request) AddHeader(name string, value string) {
	if r.headers == nil {
		r.headers = []headerTuple{}
	}
	r.headers = append(r.headers, headerTuple{name: name, value: value})
}

func (r Request) Do() (*Response, error) {
	var req *http.Request
	var er error
	var transport = defaultTransport
	var client = defaultClient
	var redirectFailed bool

	r.Method = valueOrDefault(r.Method, "GET")

	if r.Proxy != "" {
		proxyUrl, err := url.Parse(r.Proxy)
		if err != nil {
			// proxy address is in a wrong format
			return nil, &Error{Err: err}
		}
		if proxyTransport == nil {
			proxyTransport = &http.Transport{Dial: defaultDialer.Dial, Proxy: http.ProxyURL(proxyUrl)}
			proxyClient = &http.Client{Transport: proxyTransport}
		} else {
			proxyTransport.Proxy = http.ProxyURL(proxyUrl)
		}
		transport = proxyTransport
		client = proxyClient
	}

	client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
		if len(via) > r.MaxRedirects {
			redirectFailed = true
			return errors.New("Error redirecting. MaxRedirects reached")
		}

		//By default Golang will not redirect request headers
		// https://code.google.com/p/go/issues/detail?id=4800&q=request%20header
		if r.RedirectHeaders {
			for key, val := range via[0].Header {
				req.Header[key] = val
			}
		}
		return nil
	}

	if r.Insecure {
		transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	} else if transport.TLSClientConfig != nil {
		// the default TLS client (when transport.TLSClientConfig==nil) is
		// already set to verify, so do nothing in that case
		transport.TLSClientConfig.InsecureSkipVerify = false
	}

	b, e := prepareRequestBody(r.Body)
	if e != nil {
		// there was a problem marshaling the body
		return nil, &Error{Err: e}
	}

	if r.QueryString != nil {
		param, e := paramParse(r.QueryString)
		if e != nil {
			return nil, &Error{Err: e}
		}
		r.Uri = r.Uri + "?" + param
	}

	var bodyReader io.Reader
	if b != nil && r.Compression != nil {
		buffer := bytes.NewBuffer([]byte{})
		readBuffer := bufio.NewReader(b)
		writer, err := r.Compression.writer(buffer)
		if err != nil {
			return nil, &Error{Err: err}
		}
		_, e = readBuffer.WriteTo(writer)
		writer.Close()
		if e != nil {
			return nil, &Error{Err: e}
		}
		bodyReader = buffer
	} else {
		bodyReader = b
	}
	req, er = http.NewRequest(r.Method, r.Uri, bodyReader)

	if er != nil {
		// we couldn't parse the URL.
		return nil, &Error{Err: er}
	}

	// add headers to the request
	req.Host = r.Host

	r.addHeaders(req.Header)
	if r.Compression != nil {
		req.Header.Add("Content-Encoding", r.Compression.ContentEncoding)
		req.Header.Add("Accept-Encoding", r.Compression.ContentEncoding)
	}
	if r.headers != nil {
		for _, header := range r.headers {
			req.Header.Add(header.name, header.value)
		}
	}

	//use basic auth if required
	if r.BasicAuthUsername != "" {
		req.SetBasicAuth(r.BasicAuthUsername, r.BasicAuthPassword)
	}

	timeout := false
	var timer *time.Timer
	if r.Timeout > 0 {
		timer = time.AfterFunc(r.Timeout, func() {
			transport.CancelRequest(req)
			timeout = true
		})
	}

	if r.ShowDebug {
		dump, err := httputil.DumpRequest(req, true)
		if err != nil {
			println(err.Error())
		}
		println(string(dump))
	}

	res, err := client.Do(req)
	if timer != nil {
		timer.Stop()
	}

	if err != nil {
		if !timeout {
			switch err := err.(type) {
			case *net.OpError:
				timeout = err.Timeout()
			case *url.Error:
				if op, ok := err.Err.(*net.OpError); ok {
					timeout = op.Timeout()
				}
			}
		}

		var response *Response
		//If redirect fails we still want to return response data
		if redirectFailed {
			response = &Response{StatusCode: res.StatusCode, ContentLength: res.ContentLength, Header: res.Header, Body: &Body{reader: res.Body}}
		}

		return response, &Error{timeout: timeout, Err: err}
	}

	if r.Compression != nil && strings.Contains(res.Header.Get("Content-Encoding"), r.Compression.ContentEncoding) {
		compressedReader, err := r.Compression.reader(res.Body)
		if err != nil {
			return nil, &Error{Err: err}
		}
		return &Response{StatusCode: res.StatusCode, ContentLength: res.ContentLength, Header: res.Header, Body: &Body{reader: res.Body, compressedReader: compressedReader}}, nil
	} else {
		return &Response{StatusCode: res.StatusCode, ContentLength: res.ContentLength, Header: res.Header, Body: &Body{reader: res.Body}}, nil
	}
}

func (r Request) addHeaders(headersMap http.Header) {
	if len(r.UserAgent) > 0 {
		headersMap.Add("User-Agent", r.UserAgent)
	}
	headersMap.Add("Accept", r.Accept)
	headersMap.Add("Content-Type", r.ContentType)
}

func (r Request) Debug(is_debug bool) {
	r.ShowDebug = is_debug
}

// Return value if nonempty, def otherwise.
func valueOrDefault(value, def string) string {
	if value != "" {
		return value
	}
	return def
}
<commit_msg>remove Debug function & change `println` to `fmt.Printn`<commit_after>package goreq

import (
	"bufio"
	"bytes"
	"compress/flate"
	"compress/gzip"
	"compress/zlib"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"reflect"
	"strings"
	"time"
)

type Request struct {
	headers           []headerTuple
	Method            string
	Uri               string
	Body              interface{}
	QueryString       interface{}
	Timeout           time.Duration
	ContentType       string
	Accept            string
	Host              string
	UserAgent         string
	Insecure          bool
	MaxRedirects      int
	RedirectHeaders   bool
	Proxy             string
	Compression       *compression
	BasicAuthUsername string
	BasicAuthPassword string
	ShowDebug         bool
}

type compression struct {
	writer          func(buffer io.Writer) (io.WriteCloser, error)
	reader          func(buffer io.Reader) (io.ReadCloser, error)
	ContentEncoding string
}

type Response struct {
	StatusCode    int
	ContentLength int64
	Body          *Body
	Header        http.Header
}

type headerTuple struct {
	name  string
	value string
}

type Body struct {
	reader           io.ReadCloser
	compressedReader io.ReadCloser
}

type Error struct {
	timeout bool
	Err     error
}

func (e *Error) Timeout() bool {
	return e.timeout
}

func (e *Error) Error() string {
	return e.Err.Error()
}

func (b *Body) Read(p []byte) (int, error) {
	if b.compressedReader != nil {
		return b.compressedReader.Read(p)
	}
	return b.reader.Read(p)
}

func (b *Body) Close() error {
	err := b.reader.Close()
	if b.compressedReader != nil {
		return b.compressedReader.Close()
	}
	return err
}

func (b *Body) FromJsonTo(o interface{}) error {
	if body, err := ioutil.ReadAll(b); err != nil {
		return err
	} else if err := json.Unmarshal(body, o); err != nil {
		return err
	}

	return nil
}

func (b *Body) ToString() (string, error) {
	body, err := ioutil.ReadAll(b)
	if err != nil {
		return "", err
	}
	return string(body), nil
}

func Gzip() *compression {
	reader := func(buffer io.Reader) (io.ReadCloser, error) {
		return gzip.NewReader(buffer)
	}
	writer := func(buffer io.Writer) (io.WriteCloser, error) {
		return gzip.NewWriter(buffer), nil
	}
	return &compression{writer: writer, reader: reader, ContentEncoding: "gzip"}
}

func Deflate() *compression {
	reader := func(buffer io.Reader) (io.ReadCloser, error) {
		return flate.NewReader(buffer), nil
	}
	writer := func(buffer io.Writer) (io.WriteCloser, error) {
		return flate.NewWriter(buffer, -1)
	}
	return &compression{writer: writer, reader: reader, ContentEncoding: "deflate"}
}

func Zlib() *compression {
	reader := func(buffer io.Reader) (io.ReadCloser, error) {
		return zlib.NewReader(buffer)
	}
	writer := func(buffer io.Writer) (io.WriteCloser, error) {
		return zlib.NewWriter(buffer), nil
	}
	return &compression{writer: writer, reader: reader, ContentEncoding: "deflate"}
}

func paramParse(query interface{}) (string, error) {
	switch query.(type) {
	case url.Values:
		return query.(url.Values).Encode(), nil
	case *url.Values:
		return query.(*url.Values).Encode(), nil
	default:
		var (
			v = &url.Values{}
			s = reflect.ValueOf(query)
			t = reflect.TypeOf(query)
		)
		for t.Kind() == reflect.Ptr || t.Kind() == reflect.Interface {
			s = s.Elem()
			t = s.Type()
		}
		if t.Kind() == reflect.Struct {
			for i := 0; i < t.NumField(); i++ {
				if !s.Field(i).CanInterface() {
					continue
				}
				v.Add(strings.ToLower(t.Field(i).Name), fmt.Sprintf("%v", s.Field(i).Interface()))
			}
			return v.Encode(), nil
		} else {
			return "", errors.New("Can not parse QueryString.")
		}
	}
}

func prepareRequestBody(b interface{}) (io.Reader, error) {
	switch b.(type) {
	case string:
		// treat is as text
		return strings.NewReader(b.(string)), nil
	case io.Reader:
		// treat is as text
		return b.(io.Reader), nil
	case []byte:
		//treat as byte array
		return bytes.NewReader(b.([]byte)), nil
	case nil:
		return nil, nil
	default:
		// try to jsonify it
		j, err := json.Marshal(b)
		if err == nil {
			return bytes.NewReader(j), nil
		}
		return nil, err
	}
}

var defaultDialer = &net.Dialer{Timeout: 1000 * time.Millisecond}
var defaultTransport = &http.Transport{Dial: defaultDialer.Dial, Proxy: http.ProxyFromEnvironment}
var defaultClient = &http.Client{Transport: defaultTransport}

var proxyTransport *http.Transport
var proxyClient *http.Client

func SetConnectTimeout(duration time.Duration) {
	defaultDialer.Timeout = duration
}

func (r *Request) AddHeader(name string, value string) {
	if r.headers == nil {
		r.headers = []headerTuple{}
	}
	r.headers = append(r.headers, headerTuple{name: name, value: value})
}

func (r Request) Do() (*Response, error) {
	var req *http.Request
	var er error
	var transport = defaultTransport
	var client = defaultClient
	var redirectFailed bool

	r.Method = valueOrDefault(r.Method, "GET")

	if r.Proxy != "" {
		proxyUrl, err := url.Parse(r.Proxy)
		if err != nil {
			// proxy address is in a wrong format
			return nil, &Error{Err: err}
		}
		if proxyTransport == nil {
			proxyTransport = &http.Transport{Dial: defaultDialer.Dial, Proxy: http.ProxyURL(proxyUrl)}
			proxyClient = &http.Client{Transport: proxyTransport}
		} else {
			proxyTransport.Proxy = http.ProxyURL(proxyUrl)
		}
		transport = proxyTransport
		client = proxyClient
	}

	client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
		if len(via) > r.MaxRedirects {
			redirectFailed = true
			return errors.New("Error redirecting. MaxRedirects reached")
		}

		//By default Golang will not redirect request headers
		// https://code.google.com/p/go/issues/detail?id=4800&q=request%20header
		if r.RedirectHeaders {
			for key, val := range via[0].Header {
				req.Header[key] = val
			}
		}
		return nil
	}

	if r.Insecure {
		transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	} else if transport.TLSClientConfig != nil {
		// the default TLS client (when transport.TLSClientConfig==nil) is
		// already set to verify, so do nothing in that case
		transport.TLSClientConfig.InsecureSkipVerify = false
	}

	b, e := prepareRequestBody(r.Body)
	if e != nil {
		// there was a problem marshaling the body
		return nil, &Error{Err: e}
	}

	if r.QueryString != nil {
		param, e := paramParse(r.QueryString)
		if e != nil {
			return nil, &Error{Err: e}
		}
		r.Uri = r.Uri + "?" + param
	}

	var bodyReader io.Reader
	if b != nil && r.Compression != nil {
		buffer := bytes.NewBuffer([]byte{})
		readBuffer := bufio.NewReader(b)
		writer, err := r.Compression.writer(buffer)
		if err != nil {
			return nil, &Error{Err: err}
		}
		_, e = readBuffer.WriteTo(writer)
		writer.Close()
		if e != nil {
			return nil, &Error{Err: e}
		}
		bodyReader = buffer
	} else {
		bodyReader = b
	}
	req, er = http.NewRequest(r.Method, r.Uri, bodyReader)

	if er != nil {
		// we couldn't parse the URL.
		return nil, &Error{Err: er}
	}

	// add headers to the request
	req.Host = r.Host

	r.addHeaders(req.Header)
	if r.Compression != nil {
		req.Header.Add("Content-Encoding", r.Compression.ContentEncoding)
		req.Header.Add("Accept-Encoding", r.Compression.ContentEncoding)
	}
	if r.headers != nil {
		for _, header := range r.headers {
			req.Header.Add(header.name, header.value)
		}
	}

	//use basic auth if required
	if r.BasicAuthUsername != "" {
		req.SetBasicAuth(r.BasicAuthUsername, r.BasicAuthPassword)
	}

	timeout := false
	var timer *time.Timer
	if r.Timeout > 0 {
		timer = time.AfterFunc(r.Timeout, func() {
			transport.CancelRequest(req)
			timeout = true
		})
	}

	if r.ShowDebug {
		dump, err := httputil.DumpRequest(req, true)
		if err != nil {
			fmt.Println(err.Error())
		}
		fmt.Println(string(dump))
	}

	res, err := client.Do(req)
	if timer != nil {
		timer.Stop()
	}

	if err != nil {
		if !timeout {
			switch err := err.(type) {
			case *net.OpError:
				timeout = err.Timeout()
			case *url.Error:
				if op, ok := err.Err.(*net.OpError); ok {
					timeout = op.Timeout()
				}
			}
		}

		var response *Response
		//If redirect fails we still want to return response data
		if redirectFailed {
			response = &Response{StatusCode: res.StatusCode, ContentLength: res.ContentLength, Header: res.Header, Body: &Body{reader: res.Body}}
		}

		return response, &Error{timeout: timeout, Err: err}
	}

	if r.Compression != nil && strings.Contains(res.Header.Get("Content-Encoding"), r.Compression.ContentEncoding) {
		compressedReader, err := r.Compression.reader(res.Body)
		if err != nil {
			return nil, &Error{Err: err}
		}
		return &Response{StatusCode: res.StatusCode, ContentLength: res.ContentLength, Header: res.Header, Body: &Body{reader: res.Body, compressedReader: compressedReader}}, nil
	} else {
		return &Response{StatusCode: res.StatusCode, ContentLength: res.ContentLength, Header: res.Header, Body: &Body{reader: res.Body}}, nil
	}
}

func (r Request) addHeaders(headersMap http.Header) {
	if len(r.UserAgent) > 0 {
		headersMap.Add("User-Agent", r.UserAgent)
	}
	headersMap.Add("Accept", r.Accept)
	headersMap.Add("Content-Type", r.ContentType)
}

// Return value if nonempty, def otherwise.
func valueOrDefault(value, def string) string {
	if value != "" {
		return value
	}
	return def
}
<|endoftext|>


<commit_before>package goreq

import (
	"bufio"
	"bytes"
	"compress/flate"
	"compress/gzip"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"time"
)

type Request struct {
	headers      []headerTuple
	Method       string
	Uri          string
	Body         interface{}
	QueryString  interface{}
	Timeout      time.Duration
	ContentType  string
	Accept       string
	Host         string
	UserAgent    string
	Insecure     bool
	MaxRedirects int
	Proxy        string
	Compression  *compression
}

type compression struct {
	writer          func(buffer io.Writer) (io.WriteCloser, error)
	reader          func(buffer io.Reader) (io.ReadCloser, error)
	ContentEncoding string
}

type Response struct {
	StatusCode    int
	ContentLength int64
	Body          *Body
	Header        http.Header
}

type headerTuple struct {
	name  string
	value string
}

type Body struct {
	reader           io.ReadCloser
	compressedReader io.ReadCloser
}

type Error struct {
	timeout bool
	Err     error
}

func (e *Error) Timeout() bool {
	return e.timeout
}

func (e *Error) Error() string {
	return e.Err.Error()
}

func (b *Body) Read(p []byte) (int, error) {
	if b.compressedReader != nil {
		return b.compressedReader.Read(p)
	}
	return b.reader.Read(p)
}

func (b *Body) Close() error {
	err := b.reader.Close()
	if b.compressedReader != nil {
		return b.compressedReader.Close()
	}
	return err
}

func (b *Body) FromJsonTo(o interface{}) error {
	if body, err := ioutil.ReadAll(b); err != nil {
		return err
	} else if err := json.Unmarshal(body, o); err != nil {
		return err
	}

	return nil
}

func (b *Body) ToString() (string, error) {
	body, err := ioutil.ReadAll(b)
	if err != nil {
		return "", err
	}
	return string(body), nil
}

func Gzip() *compression {
	reader := func(buffer io.Reader) (io.ReadCloser, error) {
		return gzip.NewReader(buffer)
	}
	writer := func(buffer io.Writer) (io.WriteCloser, error) {
		return gzip.NewWriter(buffer), nil
	}
	return &compression{writer: writer, reader: reader, ContentEncoding: "gzip"}
}

func Deflate() *compression {
	reader := func(buffer io.Reader) (io.ReadCloser, error) {
		return flate.NewReader(buffer), nil
	}
	writer := func(buffer io.Writer) (io.WriteCloser, error) {
		return flate.NewWriter(buffer, -1)
	}
	return &compression{writer: writer, reader: reader, ContentEncoding: "deflate"}
}

func paramParse(query interface{}) (string, error) {
	var (
		v = &url.Values{}
		s = reflect.ValueOf(query)
		t = reflect.TypeOf(query)
	)

	switch query.(type) {
	case url.Values:
		return query.(url.Values).Encode(), nil
	default:
		for i := 0; i < s.NumField(); i++ {
			v.Add(strings.ToLower(t.Field(i).Name), fmt.Sprintf("%v", s.Field(i).Interface()))
		}
		return v.Encode(), nil
	}
}

func prepareRequestBody(b interface{}) (io.Reader, error) {
	switch b.(type) {
	case string:
		// treat is as text
		return strings.NewReader(b.(string)), nil
	case io.Reader:
		// treat is as text
		return b.(io.Reader), nil
	case []byte:
		//treat as byte array
		return bytes.NewReader(b.([]byte)), nil
	case nil:
		return nil, nil
	default:
		// try to jsonify it
		j, err := json.Marshal(b)
		if err == nil {
			return bytes.NewReader(j), nil
		}
		return nil, err
	}
}

var defaultDialer = &net.Dialer{Timeout: 1000 * time.Millisecond}
var defaultTransport = &http.Transport{Dial: defaultDialer.Dial, Proxy: http.ProxyFromEnvironment}
var defaultClient = &http.Client{Transport: defaultTransport}
var proxyTransport *http.Transport
var proxyClient *http.Client

func SetConnectTimeout(duration time.Duration) {
	defaultDialer.Timeout = duration
}

func (r *Request) AddHeader(name string, value string) {
	if r.headers == nil {
		r.headers = []headerTuple{}
	}
	r.headers = append(r.headers, headerTuple{name: name, value: value})
}

func (r Request) Do() (*Response, error) {
	var req *http.Request
	var er error
	var transport = defaultTransport
	var client = defaultClient

	if r.Proxy != "" {
		proxyUrl, err := url.Parse(r.Proxy)
		if err != nil {
			// proxy address is in a wrong format
			return nil, &Error{Err: err}
		}
		if proxyTransport == nil {
			proxyTransport = &http.Transport{Dial: defaultDialer.Dial, Proxy: http.ProxyURL(proxyUrl)}
			proxyClient = &http.Client{Transport: proxyTransport}
		} else {
			proxyTransport.Proxy = http.ProxyURL(proxyUrl)
		}
		transport = proxyTransport
		client = proxyClient
	}

	if r.Insecure {
		transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	} else if transport.TLSClientConfig != nil {
		// the default TLS client (when transport.TLSClientConfig==nil) is
		// already set to verify, so do nothing in that case
		transport.TLSClientConfig.InsecureSkipVerify = false
	}

	b, e := prepareRequestBody(r.Body)
	if e != nil {
		// there was a problem marshaling the body
		return nil, &Error{Err: e}
	}

	if r.QueryString != nil {
		param, e := paramParse(r.QueryString)
		if e != nil {
			return nil, &Error{Err: e}
		}
		r.Uri = r.Uri + "?" + param
	}

	var bodyReader io.Reader
	if b != nil && r.Compression != nil {
		buffer := bytes.NewBuffer([]byte{})
		readBuffer := bufio.NewReader(b)
		writer, err := r.Compression.writer(buffer)
		if err != nil {
			return nil, &Error{Err: err}
		}
		_, e = readBuffer.WriteTo(writer)
		writer.Close()
		if e != nil {
			return nil, &Error{Err: e}
		}
		bodyReader = buffer
	} else {
		bodyReader = b
	}
	req, er = http.NewRequest(r.Method, r.Uri, bodyReader)

	if er != nil {
		// we couldn't parse the URL.
		return nil, &Error{Err: er}
	}

	// add headers to the request
	req.Host = r.Host
	req.Header.Add("User-Agent", r.UserAgent)
	req.Header.Add("Content-Type", r.ContentType)
	req.Header.Add("Accept", r.Accept)
	if r.Compression != nil {
		req.Header.Add("Content-Encoding", r.Compression.ContentEncoding)
		req.Header.Add("Accept-Encoding", r.Compression.ContentEncoding)
	}
	if r.headers != nil {
		for _, header := range r.headers {
			req.Header.Add(header.name, header.value)
		}
	}

	timeout := false
	var timer *time.Timer
	if r.Timeout > 0 {
		timer = time.AfterFunc(r.Timeout, func() {
			transport.CancelRequest(req)
			timeout = true
		})
	}

	res, err := client.Do(req)
	if timer != nil {
		timer.Stop()
	}

	if err != nil {
		if op, ok := err.(*net.OpError); !timeout && ok {
			timeout = op.Timeout()
		}
		return nil, &Error{timeout: timeout, Err: err}
	}

	if isRedirect(res.StatusCode) && r.MaxRedirects > 0 {
		loc, _ := res.Location()
		r.MaxRedirects--
		r.Uri = loc.String()
		return r.Do()
	}

	if r.Compression != nil && strings.Contains(res.Header.Get("Content-Encoding"), r.Compression.ContentEncoding) {
		compressedReader, err := r.Compression.reader(res.Body)
		if err != nil {
			return nil, &Error{Err: err}
		}
		return &Response{StatusCode: res.StatusCode, ContentLength: res.ContentLength, Header: res.Header, Body: &Body{reader: res.Body, compressedReader: compressedReader}}, nil
	} else {
		return &Response{StatusCode: res.StatusCode, ContentLength: res.ContentLength, Header: res.Header, Body: &Body{reader: res.Body}}, nil
	}
}

func isRedirect(status int) bool {
	switch status {
	case http.StatusMovedPermanently:
		return true
	case http.StatusFound:
		return true
	case http.StatusSeeOther:
		return true
	case http.StatusTemporaryRedirect:
		return true
	default:
		return false
	}
}
<commit_msg>add supprot for zlib compression<commit_after>package goreq

import (
	"bufio"
	"bytes"
	"compress/flate"
	"compress/gzip"
  "compress/zlib"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"time"
)

type Request struct {
	headers      []headerTuple
	Method       string
	Uri          string
	Body         interface{}
	QueryString  interface{}
	Timeout      time.Duration
	ContentType  string
	Accept       string
	Host         string
	UserAgent    string
	Insecure     bool
	MaxRedirects int
	Proxy        string
	Compression  *compression
}

type compression struct {
	writer          func(buffer io.Writer) (io.WriteCloser, error)
	reader          func(buffer io.Reader) (io.ReadCloser, error)
	ContentEncoding string
}

type Response struct {
	StatusCode    int
	ContentLength int64
	Body          *Body
	Header        http.Header
}

type headerTuple struct {
	name  string
	value string
}

type Body struct {
	reader           io.ReadCloser
	compressedReader io.ReadCloser
}

type Error struct {
	timeout bool
	Err     error
}

func (e *Error) Timeout() bool {
	return e.timeout
}

func (e *Error) Error() string {
	return e.Err.Error()
}

func (b *Body) Read(p []byte) (int, error) {
	if b.compressedReader != nil {
		return b.compressedReader.Read(p)
	}
	return b.reader.Read(p)
}

func (b *Body) Close() error {
	err := b.reader.Close()
	if b.compressedReader != nil {
		return b.compressedReader.Close()
	}
	return err
}

func (b *Body) FromJsonTo(o interface{}) error {
	if body, err := ioutil.ReadAll(b); err != nil {
		return err
	} else if err := json.Unmarshal(body, o); err != nil {
		return err
	}

	return nil
}

func (b *Body) ToString() (string, error) {
	body, err := ioutil.ReadAll(b)
	if err != nil {
		return "", err
	}
	return string(body), nil
}

func Gzip() *compression {
	reader := func(buffer io.Reader) (io.ReadCloser, error) {
		return gzip.NewReader(buffer)
	}
	writer := func(buffer io.Writer) (io.WriteCloser, error) {
		return gzip.NewWriter(buffer), nil
	}
	return &compression{writer: writer, reader: reader, ContentEncoding: "gzip"}
}

func Deflate() *compression {
	reader := func(buffer io.Reader) (io.ReadCloser, error) {
		return flate.NewReader(buffer), nil
	}
	writer := func(buffer io.Writer) (io.WriteCloser, error) {
		return flate.NewWriter(buffer, -1)
	}
	return &compression{writer: writer, reader: reader, ContentEncoding: "deflate"}
}

func Zlib() *compression {
	reader := func(buffer io.Reader) (io.ReadCloser, error) {
		return zlib.NewReader(buffer)
	}
	writer := func(buffer io.Writer) (io.WriteCloser, error) {
		return zlib.NewWriter(buffer), nil
	}
	return &compression{writer: writer, reader: reader, ContentEncoding: "deflate"}
}

func paramParse(query interface{}) (string, error) {
	var (
		v = &url.Values{}
		s = reflect.ValueOf(query)
		t = reflect.TypeOf(query)
	)

	switch query.(type) {
	case url.Values:
		return query.(url.Values).Encode(), nil
	default:
		for i := 0; i < s.NumField(); i++ {
			v.Add(strings.ToLower(t.Field(i).Name), fmt.Sprintf("%v", s.Field(i).Interface()))
		}
		return v.Encode(), nil
	}
}

func prepareRequestBody(b interface{}) (io.Reader, error) {
	switch b.(type) {
	case string:
		// treat is as text
		return strings.NewReader(b.(string)), nil
	case io.Reader:
		// treat is as text
		return b.(io.Reader), nil
	case []byte:
		//treat as byte array
		return bytes.NewReader(b.([]byte)), nil
	case nil:
		return nil, nil
	default:
		// try to jsonify it
		j, err := json.Marshal(b)
		if err == nil {
			return bytes.NewReader(j), nil
		}
		return nil, err
	}
}

var defaultDialer = &net.Dialer{Timeout: 1000 * time.Millisecond}
var defaultTransport = &http.Transport{Dial: defaultDialer.Dial, Proxy: http.ProxyFromEnvironment}
var defaultClient = &http.Client{Transport: defaultTransport}
var proxyTransport *http.Transport
var proxyClient *http.Client

func SetConnectTimeout(duration time.Duration) {
	defaultDialer.Timeout = duration
}

func (r *Request) AddHeader(name string, value string) {
	if r.headers == nil {
		r.headers = []headerTuple{}
	}
	r.headers = append(r.headers, headerTuple{name: name, value: value})
}

func (r Request) Do() (*Response, error) {
	var req *http.Request
	var er error
	var transport = defaultTransport
	var client = defaultClient

	if r.Proxy != "" {
		proxyUrl, err := url.Parse(r.Proxy)
		if err != nil {
			// proxy address is in a wrong format
			return nil, &Error{Err: err}
		}
		if proxyTransport == nil {
			proxyTransport = &http.Transport{Dial: defaultDialer.Dial, Proxy: http.ProxyURL(proxyUrl)}
			proxyClient = &http.Client{Transport: proxyTransport}
		} else {
			proxyTransport.Proxy = http.ProxyURL(proxyUrl)
		}
		transport = proxyTransport
		client = proxyClient
	}

	if r.Insecure {
		transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	} else if transport.TLSClientConfig != nil {
		// the default TLS client (when transport.TLSClientConfig==nil) is
		// already set to verify, so do nothing in that case
		transport.TLSClientConfig.InsecureSkipVerify = false
	}

	b, e := prepareRequestBody(r.Body)
	if e != nil {
		// there was a problem marshaling the body
		return nil, &Error{Err: e}
	}

	if r.QueryString != nil {
		param, e := paramParse(r.QueryString)
		if e != nil {
			return nil, &Error{Err: e}
		}
		r.Uri = r.Uri + "?" + param
	}

	var bodyReader io.Reader
	if b != nil && r.Compression != nil {
		buffer := bytes.NewBuffer([]byte{})
		readBuffer := bufio.NewReader(b)
		writer, err := r.Compression.writer(buffer)
		if err != nil {
			return nil, &Error{Err: err}
		}
		_, e = readBuffer.WriteTo(writer)
		writer.Close()
		if e != nil {
			return nil, &Error{Err: e}
		}
		bodyReader = buffer
	} else {
		bodyReader = b
	}
	req, er = http.NewRequest(r.Method, r.Uri, bodyReader)

	if er != nil {
		// we couldn't parse the URL.
		return nil, &Error{Err: er}
	}

	// add headers to the request
	req.Host = r.Host
	req.Header.Add("User-Agent", r.UserAgent)
	req.Header.Add("Content-Type", r.ContentType)
	req.Header.Add("Accept", r.Accept)
	if r.Compression != nil {
		req.Header.Add("Content-Encoding", r.Compression.ContentEncoding)
		req.Header.Add("Accept-Encoding", r.Compression.ContentEncoding)
	}
	if r.headers != nil {
		for _, header := range r.headers {
			req.Header.Add(header.name, header.value)
		}
	}

	timeout := false
	var timer *time.Timer
	if r.Timeout > 0 {
		timer = time.AfterFunc(r.Timeout, func() {
			transport.CancelRequest(req)
			timeout = true
		})
	}

	res, err := client.Do(req)
	if timer != nil {
		timer.Stop()
	}

	if err != nil {
		if op, ok := err.(*net.OpError); !timeout && ok {
			timeout = op.Timeout()
		}
		return nil, &Error{timeout: timeout, Err: err}
	}

	if isRedirect(res.StatusCode) && r.MaxRedirects > 0 {
		loc, _ := res.Location()
		r.MaxRedirects--
		r.Uri = loc.String()
		return r.Do()
	}

	if r.Compression != nil && strings.Contains(res.Header.Get("Content-Encoding"), r.Compression.ContentEncoding) {
		compressedReader, err := r.Compression.reader(res.Body)
		if err != nil {
			return nil, &Error{Err: err}
		}
		return &Response{StatusCode: res.StatusCode, ContentLength: res.ContentLength, Header: res.Header, Body: &Body{reader: res.Body, compressedReader: compressedReader}}, nil
	} else {
		return &Response{StatusCode: res.StatusCode, ContentLength: res.ContentLength, Header: res.Header, Body: &Body{reader: res.Body}}, nil
	}
}

func isRedirect(status int) bool {
	switch status {
	case http.StatusMovedPermanently:
		return true
	case http.StatusFound:
		return true
	case http.StatusSeeOther:
		return true
	case http.StatusTemporaryRedirect:
		return true
	default:
		return false
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"go/build"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"

	"golang.org/x/tools/go/loader"

	"sourcegraph.com/sourcegraph/srclib-go/gog"
	"sourcegraph.com/sourcegraph/srclib-go/gog/definfo"
	defpkg "sourcegraph.com/sourcegraph/srclib-go/golang_def"
	"sourcegraph.com/sourcegraph/srclib/graph"
	"sourcegraph.com/sourcegraph/srclib/unit"
)

func init() {
	_, err := parser.AddCommand("graph",
		"graph a Go package",
		"Graph a Go package, producing all defs, refs, and docs.",
		&graphCmd,
	)
	if err != nil {
		log.Fatal(err)
	}

	// Check that we have the '-i' flag.
	cmd := exec.Command("go", "help", "build")
	o, err := cmd.Output()
	if err != nil {
		log.Fatal(err)
	}
	usage := strings.Split(string(o), "\n")[0] // The usage is on the first line.
	matched, err := regexp.MatchString("-i", usage)
	if err != nil {
		log.Fatal(err)
	}
	if !matched {
		log.Fatal("'go build' does not have the '-i' flag. Please upgrade to go1.3+.")
	}
}

type GraphCmd struct{}

var graphCmd GraphCmd

// allowErrorsInGoGet is whether the grapher should continue after
// if `go get` fails.
var allowErrorsInGoGet = true

func (c *GraphCmd) Execute(args []string) error {
	var unit *unit.SourceUnit
	if err := json.NewDecoder(os.Stdin).Decode(&unit); err != nil {
		return err
	}
	if err := os.Stdin.Close(); err != nil {
		return err
	}

	if err := unmarshalTypedConfig(unit.Config); err != nil {
		return err
	}
	if err := config.apply(); err != nil {
		return err
	}

	out, err := Graph(unit)
	if err != nil {
		return err
	}

	// Make paths relative to repo.
	for _, gs := range out.Defs {
		if gs.File == "" {
			log.Printf("no file %+v", gs)
		}
		if gs.File != "" {
			gs.File = relPath(cwd, gs.File)
		}
	}
	for _, gr := range out.Refs {
		if gr.File != "" {
			gr.File = relPath(cwd, gr.File)
		}
	}
	for _, gd := range out.Docs {
		if gd.File != "" {
			gd.File = relPath(cwd, gd.File)
		}
	}

	if err := json.NewEncoder(os.Stdout).Encode(out); err != nil {
		return err
	}
	return nil
}

func relPath(base, path string) string {
	rp, err := filepath.Rel(evalSymlinks(base), evalSymlinks(path))
	if err != nil {
		log.Fatalf("Failed to make path %q relative to %q: %s", path, base, err)
	}
	return filepath.ToSlash(rp)
}

func Graph(unit *unit.SourceUnit) (*graph.Output, error) {
	pkg, err := UnitDataAsBuildPackage(unit)
	if err != nil {
		return nil, err
	}

	o, err := doGraph(pkg)
	if err != nil {
		return nil, err
	}

	o2 := graph.Output{}

	for _, gs := range o.Defs {
		d, err := convertGoDef(gs)
		if err != nil {
			return nil, err
		}
		if d != nil {
			o2.Defs = append(o2.Defs, d)
		}
	}
	for _, gr := range o.Refs {
		r, err := convertGoRef(gr)
		if err != nil {
			return nil, err
		}
		if r != nil {
			o2.Refs = append(o2.Refs, r)
		}
	}
	for _, gd := range o.Docs {
		d, err := convertGoDoc(gd)
		if err != nil {
			return nil, err
		}
		if d != nil {
			o2.Docs = append(o2.Docs, d)
		}
	}

	return &o2, nil
}

func convertGoDef(gs *gog.Def) (*graph.Def, error) {
	resolvedTarget, err := ResolveDep(gs.DefKey.PackageImportPath)
	if err != nil {
		return nil, err
	}
	path := filepath.ToSlash(pathOrDot(filepath.Join(gs.Path...)))
	treePath := treePath(strings.Replace(string(path), ".go", "", -1))
	if !graph.IsValidTreePath(treePath) {
		return nil, fmt.Errorf("'%s' is not a valid tree-path", treePath)
	}

	def := &graph.Def{
		DefKey: graph.DefKey{
			Unit:     resolvedTarget.ToUnit,
			UnitType: resolvedTarget.ToUnitType,
			Path:     path,
		},
		TreePath: treePath,

		Name: gs.Name,
		Kind: definfo.GeneralKindMap[gs.Kind],

		File:     filepath.ToSlash(gs.File),
		DefStart: gs.DeclSpan[0],
		DefEnd:   gs.DeclSpan[1],

		Exported: gs.DefInfo.Exported,
		Local:    !gs.DefInfo.Exported && !gs.DefInfo.PkgScope,
		Test:     strings.HasSuffix(gs.File, "_test.go"),
	}

	d := defpkg.DefData{
		PackageImportPath: gs.DefKey.PackageImportPath,
		DefInfo:           gs.DefInfo,
	}
	def.Data, err = json.Marshal(d)
	if err != nil {
		return nil, err
	}

	if def.File == "" {
		// some cgo defs have empty File; omit them
		return nil, nil
	}

	return def, nil
}

func convertGoRef(gr *gog.Ref) (*graph.Ref, error) {
	resolvedTarget, err := ResolveDep(gr.Def.PackageImportPath)
	if err != nil {
		return nil, err
	}
	if resolvedTarget == nil {
		return nil, nil
	}

	return &graph.Ref{
		DefRepo:     filepath.ToSlash(uriOrEmpty(resolvedTarget.ToRepoCloneURL)),
		DefPath:     filepath.ToSlash(pathOrDot(filepath.Join(gr.Def.Path...))),
		DefUnit:     resolvedTarget.ToUnit,
		DefUnitType: resolvedTarget.ToUnitType,
		Def:         gr.IsDef,
		File:        filepath.ToSlash(gr.File),
		Start:       gr.Span[0],
		End:         gr.Span[1],
	}, nil
}

func convertGoDoc(gd *gog.Doc) (*graph.Doc, error) {
	var key graph.DefKey
	if gd.DefKey != nil {
		resolvedTarget, err := ResolveDep(gd.PackageImportPath)
		if err != nil {
			return nil, err
		}
		key = graph.DefKey{
			Path:     filepath.ToSlash(pathOrDot(filepath.Join(gd.Path...))),
			Unit:     resolvedTarget.ToUnit,
			UnitType: resolvedTarget.ToUnitType,
		}
	}
	return &graph.Doc{
		DefKey: key,
		Format: gd.Format,
		Data:   gd.Data,
		File:   filepath.ToSlash(gd.File),
		Start:  gd.Span[0],
		End:    gd.Span[1],
	}, nil
}

func uriOrEmpty(cloneURL string) string {
	if cloneURL == "" {
		return ""
	}
	return graph.MakeURI(cloneURL)
}

func pathOrDot(path string) string {
	if path == "" {
		return "."
	}
	return path
}

func treePath(path string) string {
	if path == "" || path == "." {
		return string(".")
	}
	return "./" + path
}

// allowErrorsInGraph is whether the grapher should continue after
// encountering "reasonably common" errors (such as compile errors).
var allowErrorsInGraph = true

func doGraph(pkg *build.Package) (*gog.Output, error) {
	importPath := pkg.ImportPath
	importUnsafe := importPath == "unsafe"

	// Special-case: if this is a Cgo package, treat the CgoFiles as GoFiles or
	// else the character offsets will be junk.
	//
	// See https://codereview.appspot.com/86140043.
	loaderConfig.Build.CgoEnabled = false
	build.Default = *loaderConfig.Build
	if len(pkg.CgoFiles) > 0 {
		var allGoFiles []string
		allGoFiles = append(allGoFiles, pkg.GoFiles...)
		allGoFiles = append(allGoFiles, pkg.CgoFiles...)
		allGoFiles = append(allGoFiles, pkg.TestGoFiles...)
		for i, f := range allGoFiles {
			allGoFiles[i] = filepath.Join(cwd, pkg.Dir, f)
		}
		loaderConfig.CreateFromFilenames(pkg.ImportPath, allGoFiles...)
	} else {
		// Normal import
		loaderConfig.ImportWithTests(importPath)
	}

	if importUnsafe {
		// Special-case "unsafe" because go/loader does not let you load it
		// directly.
		if loaderConfig.ImportPkgs == nil {
			loaderConfig.ImportPkgs = make(map[string]bool)
		}
		loaderConfig.ImportPkgs["unsafe"] = true
	}

	prog, err := loaderConfig.Load()
	if err != nil {
		log.Println("XXX", err)
		return nil, err
	}

	g := gog.New(prog)

	var pkgs []*loader.PackageInfo
	for _, pkg := range prog.Created {
		if strings.HasSuffix(pkg.Pkg.Name(), "_test") {
			// ignore xtest packages
			continue
		}
		pkgs = append(pkgs, pkg)
	}
	for _, pkg := range prog.Imported {
		pkgs = append(pkgs, pkg)
	}

	for _, pkg := range pkgs {
		if err := g.Graph(pkg); err != nil {
			return nil, err
		}
	}

	return &g.Output, nil
}
<commit_msg>graph: accept a list of source units as input to graph<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"go/build"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"

	"golang.org/x/tools/go/loader"

	"sourcegraph.com/sourcegraph/srclib-go/gog"
	"sourcegraph.com/sourcegraph/srclib-go/gog/definfo"
	defpkg "sourcegraph.com/sourcegraph/srclib-go/golang_def"
	"sourcegraph.com/sourcegraph/srclib/graph"
	"sourcegraph.com/sourcegraph/srclib/unit"
)

func init() {
	_, err := parser.AddCommand("graph",
		"graph a Go package",
		"Graph a Go package, producing all defs, refs, and docs.",
		&graphCmd,
	)
	if err != nil {
		log.Fatal(err)
	}

	// Check that we have the '-i' flag.
	cmd := exec.Command("go", "help", "build")
	o, err := cmd.Output()
	if err != nil {
		log.Fatal(err)
	}
	usage := strings.Split(string(o), "\n")[0] // The usage is on the first line.
	matched, err := regexp.MatchString("-i", usage)
	if err != nil {
		log.Fatal(err)
	}
	if !matched {
		log.Fatal("'go build' does not have the '-i' flag. Please upgrade to go1.3+.")
	}
}

type GraphCmd struct{}

var graphCmd GraphCmd

// allowErrorsInGoGet is whether the grapher should continue after
// if `go get` fails.
var allowErrorsInGoGet = true

func (c *GraphCmd) Execute(args []string) error {
	var units unit.SourceUnits
	if err := json.NewDecoder(os.Stdin).Decode(&units); err != nil {
		// Legacy API: try parsing input as a single source unit
		var unit *unit.SourceUnit
		if err := json.NewDecoder(os.Stdin).Decode(&unit); err != nil {
			return err
		}
		units = append(units, unit)
	}
	if err := os.Stdin.Close(); err != nil {
		return err
	}

	// HACK: fix this. Is this required? We only seem to be setting
	// GOROOT and GOPATH
	if err := unmarshalTypedConfig(units[0].Config); err != nil {
		return err
	}
	if err := config.apply(); err != nil {
		return err
	}

	out, err := Graph(units)
	if err != nil {
		return err
	}

	// Make paths relative to repo.
	for _, gs := range out.Defs {
		if gs.File == "" {
			log.Printf("no file %+v", gs)
		}
		if gs.File != "" {
			gs.File = relPath(cwd, gs.File)
		}
	}
	for _, gr := range out.Refs {
		if gr.File != "" {
			gr.File = relPath(cwd, gr.File)
		}
	}
	for _, gd := range out.Docs {
		if gd.File != "" {
			gd.File = relPath(cwd, gd.File)
		}
	}

	if err := json.NewEncoder(os.Stdout).Encode(out); err != nil {
		return err
	}
	return nil
}

func relPath(base, path string) string {
	rp, err := filepath.Rel(evalSymlinks(base), evalSymlinks(path))
	if err != nil {
		log.Fatalf("Failed to make path %q relative to %q: %s", path, base, err)
	}
	return filepath.ToSlash(rp)
}

func Graph(units unit.SourceUnits) (*graph.Output, error) {
	var pkgs []*build.Package
	for _, u := range units {
		pkg, err := UnitDataAsBuildPackage(u)
		if err != nil {
			return nil, err
		}
		pkgs = append(pkgs, pkg)
	}

	o, err := doGraph(pkgs)
	if err != nil {
		return nil, err
	}

	o2 := graph.Output{}

	for _, gs := range o.Defs {
		d, err := convertGoDef(gs)
		if err != nil {
			return nil, err
		}
		if d != nil {
			o2.Defs = append(o2.Defs, d)
		}
	}
	for _, gr := range o.Refs {
		r, err := convertGoRef(gr)
		if err != nil {
			return nil, err
		}
		if r != nil {
			o2.Refs = append(o2.Refs, r)
		}
	}
	for _, gd := range o.Docs {
		d, err := convertGoDoc(gd)
		if err != nil {
			return nil, err
		}
		if d != nil {
			o2.Docs = append(o2.Docs, d)
		}
	}

	return &o2, nil
}

func convertGoDef(gs *gog.Def) (*graph.Def, error) {
	resolvedTarget, err := ResolveDep(gs.DefKey.PackageImportPath)
	if err != nil {
		return nil, err
	}
	path := filepath.ToSlash(pathOrDot(filepath.Join(gs.Path...)))
	treePath := treePath(strings.Replace(string(path), ".go", "", -1))
	if !graph.IsValidTreePath(treePath) {
		return nil, fmt.Errorf("'%s' is not a valid tree-path", treePath)
	}

	def := &graph.Def{
		DefKey: graph.DefKey{
			Unit:     resolvedTarget.ToUnit,
			UnitType: resolvedTarget.ToUnitType,
			Path:     path,
		},
		TreePath: treePath,

		Name: gs.Name,
		Kind: definfo.GeneralKindMap[gs.Kind],

		File:     filepath.ToSlash(gs.File),
		DefStart: gs.DeclSpan[0],
		DefEnd:   gs.DeclSpan[1],

		Exported: gs.DefInfo.Exported,
		Local:    !gs.DefInfo.Exported && !gs.DefInfo.PkgScope,
		Test:     strings.HasSuffix(gs.File, "_test.go"),
	}

	d := defpkg.DefData{
		PackageImportPath: gs.DefKey.PackageImportPath,
		DefInfo:           gs.DefInfo,
	}
	def.Data, err = json.Marshal(d)
	if err != nil {
		return nil, err
	}

	if def.File == "" {
		// some cgo defs have empty File; omit them
		return nil, nil
	}

	return def, nil
}

func convertGoRef(gr *gog.Ref) (*graph.Ref, error) {
	resolvedTarget, err := ResolveDep(gr.Def.PackageImportPath)
	if err != nil {
		return nil, err
	}
	if resolvedTarget == nil {
		return nil, nil
	}

	return &graph.Ref{
		DefRepo:     filepath.ToSlash(uriOrEmpty(resolvedTarget.ToRepoCloneURL)),
		DefPath:     filepath.ToSlash(pathOrDot(filepath.Join(gr.Def.Path...))),
		DefUnit:     resolvedTarget.ToUnit,
		DefUnitType: resolvedTarget.ToUnitType,
		Def:         gr.IsDef,
		File:        filepath.ToSlash(gr.File),
		Start:       gr.Span[0],
		End:         gr.Span[1],
	}, nil
}

func convertGoDoc(gd *gog.Doc) (*graph.Doc, error) {
	var key graph.DefKey
	if gd.DefKey != nil {
		resolvedTarget, err := ResolveDep(gd.PackageImportPath)
		if err != nil {
			return nil, err
		}
		key = graph.DefKey{
			Path:     filepath.ToSlash(pathOrDot(filepath.Join(gd.Path...))),
			Unit:     resolvedTarget.ToUnit,
			UnitType: resolvedTarget.ToUnitType,
		}
	}
	return &graph.Doc{
		DefKey: key,
		Format: gd.Format,
		Data:   gd.Data,
		File:   filepath.ToSlash(gd.File),
		Start:  gd.Span[0],
		End:    gd.Span[1],
	}, nil
}

func uriOrEmpty(cloneURL string) string {
	if cloneURL == "" {
		return ""
	}
	return graph.MakeURI(cloneURL)
}

func pathOrDot(path string) string {
	if path == "" {
		return "."
	}
	return path
}

func treePath(path string) string {
	if path == "" || path == "." {
		return string(".")
	}
	return "./" + path
}

// allowErrorsInGraph is whether the grapher should continue after
// encountering "reasonably common" errors (such as compile errors).
var allowErrorsInGraph = true

func doGraph(pkgs []*build.Package) (*gog.Output, error) {
	// Special-case: if this is a Cgo package, treat the CgoFiles as GoFiles or
	// else the character offsets will be junk.
	//
	// See https://codereview.appspot.com/86140043.
	loaderConfig.Build.CgoEnabled = false
	build.Default = *loaderConfig.Build

	for _, pkg := range pkgs {
		importPath := pkg.ImportPath
		importUnsafe := importPath == "unsafe"

		if len(pkg.CgoFiles) > 0 {
			var allGoFiles []string
			allGoFiles = append(allGoFiles, pkg.GoFiles...)
			allGoFiles = append(allGoFiles, pkg.CgoFiles...)
			allGoFiles = append(allGoFiles, pkg.TestGoFiles...)
			for i, f := range allGoFiles {
				allGoFiles[i] = filepath.Join(cwd, pkg.Dir, f)
			}
			loaderConfig.CreateFromFilenames(pkg.ImportPath, allGoFiles...)
		} else {
			// Normal import
			loaderConfig.ImportWithTests(importPath)
		}

		if importUnsafe {
			// Special-case "unsafe" because go/loader does not let you load it
			// directly.
			if loaderConfig.ImportPkgs == nil {
				loaderConfig.ImportPkgs = make(map[string]bool)
			}
			loaderConfig.ImportPkgs["unsafe"] = true
		}
	}

	prog, err := loaderConfig.Load()
	if err != nil {
		log.Println("XXX", err)
		return nil, err
	}

	g := gog.New(prog)

	var pkgInfos []*loader.PackageInfo
	for _, pkg := range prog.Created {
		if strings.HasSuffix(pkg.Pkg.Name(), "_test") {
			// ignore xtest packages
			continue
		}
		pkgInfos = append(pkgInfos, pkg)
	}
	for _, pkg := range prog.Imported {
		pkgInfos = append(pkgInfos, pkg)
	}

	for _, pkg := range pkgInfos {
		if err := g.Graph(pkg); err != nil {
			return nil, err
		}
	}

	return &g.Output, nil
}
<|endoftext|>


<commit_before>package kubernetes

import (
	"fmt"
	"strconv"

	"github.com/hashicorp/terraform/helper/schema"
	api "k8s.io/api/core/v1"
	"k8s.io/api/networking/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
)

// Flatteners

func flattenNetworkPolicySpec(in v1.NetworkPolicySpec) []interface{} {
	att := make(map[string]interface{})
	att["ingress"] = flattenNetworkPolicyIngress(in.Ingress)
	att["egress"] = flattenNetworkPolicyEgress(in.Egress)
	if len(in.PodSelector.MatchExpressions) > 0 || len(in.PodSelector.MatchLabels) > 0 {
		att["pod_selector"] = flattenLabelSelector(&in.PodSelector)
	} else {
		att["pod_selector"] = []interface{}{make(map[string]interface{})}
	}
	if len(in.PolicyTypes) > 0 {
		att["policy_types"] = in.PolicyTypes
	}
	return []interface{}{att}
}

func flattenNetworkPolicyIngress(in []v1.NetworkPolicyIngressRule) []interface{} {
	att := make([]interface{}, len(in), len(in))
	for i, ingress := range in {
		m := make(map[string]interface{})
		if ingress.Ports != nil && len(ingress.Ports) > 0 {
			m["ports"] = flattenNetworkPolicyPorts(ingress.Ports)
		}
		if ingress.From != nil && len(ingress.From) > 0 {
			m["from"] = flattenNetworkPolicyPeer(ingress.From)
		}
		att[i] = m
	}
	return att
}

func flattenNetworkPolicyEgress(in []v1.NetworkPolicyEgressRule) []interface{} {
	att := make([]interface{}, len(in), len(in))
	for i, egress := range in {
		m := make(map[string]interface{})
		if egress.Ports != nil && len(egress.Ports) > 0 {
			m["ports"] = flattenNetworkPolicyPorts(egress.Ports)
		}
		if egress.To != nil && len(egress.To) > 0 {
			m["to"] = flattenNetworkPolicyPeer(egress.To)
		}
		att[i] = m
	}
	return att
}

func flattenNetworkPolicyPorts(in []v1.NetworkPolicyPort) []interface{} {
	att := make([]interface{}, len(in), len(in))
	for i, port := range in {
		m := make(map[string]interface{})
		if port.Port != nil {
			if (*port.Port).Type == intstr.Int {
				m["port"] = strconv.Itoa(int((*port.Port).IntVal))
			} else {
				m["port"] = (*port.Port).StrVal
			}
		}
		if port.Protocol != nil {
			m["protocol"] = string(*port.Protocol)
		}
		att[i] = m
	}
	return att
}

func flattenNetworkPolicyPeer(in []v1.NetworkPolicyPeer) []interface{} {
	att := make([]interface{}, len(in), len(in))
	for i, peer := range in {
		m := make(map[string]interface{})
		if peer.IPBlock != nil {
			m["ip_block"] = flattenIPBlock(peer.IPBlock)
		}
		if peer.NamespaceSelector != nil {
			m["namespace_selector"] = flattenLabelSelector(peer.NamespaceSelector)
		}
		if peer.PodSelector != nil {
			m["pod_selector"] = flattenLabelSelector(peer.PodSelector)
		}
		att[i] = m
	}
	return att
}

func flattenIPBlock(in *v1.IPBlock) []interface{} {
	att := make(map[string]interface{})
	if in.CIDR != "" {
		att["cidr"] = in.CIDR
	}
	if len(in.Except) > 0 {
		att["except"] = in.Except
	}
	return []interface{}{att}
}

// Expanders

func expandNetworkPolicySpec(in []interface{}) (*v1.NetworkPolicySpec, error) {
	spec := v1.NetworkPolicySpec{}

	if len(in) == 0 || in[0] == nil {
		return nil, fmt.Errorf("failed to expand NetworkPolicy.Spec: null or empty input")
	}

	m := in[0].(map[string]interface{})
	spec.PodSelector = *expandLabelSelector(m["pod_selector"].([]interface{}))
	if v, ok := m["ingress"].([]interface{}); ok && len(v) > 0 {
		ingress, err := expandNetworkPolicyIngress(v)
		if err != nil {
			return nil, err
		}
		spec.Ingress = *ingress
	}
	if v, ok := m["egress"].([]interface{}); ok && len(v) > 0 {
		egress, err := expandNetworkPolicyEgress(v)
		if err != nil {
			return nil, err
		}
		spec.Egress = *egress
	}
	policyTypes, err := expandNetworkPolicyTypes(m["policy_types"].([]interface{}))
	if err != nil {
		return nil, err
	}
	spec.PolicyTypes = *policyTypes

	return &spec, nil
}

func expandNetworkPolicyIngress(l []interface{}) (*[]v1.NetworkPolicyIngressRule, error) {
	ingresses := make([]v1.NetworkPolicyIngressRule, len(l), len(l))
	for i, ingress := range l {
		if ingress != nil {
			in := ingress.(map[string]interface{})
			ingresses[i] = v1.NetworkPolicyIngressRule{}
			if v, ok := in["ports"].([]interface{}); ok && len(v) > 0 {
				policyPorts, err := expandNetworkPolicyPorts(v)
				if err != nil {
					return nil, err
				}
				ingresses[i].Ports = *policyPorts
			}
			if v, ok := in["from"].([]interface{}); ok && len(v) > 0 {
				policyPeers, err := expandNetworkPolicyPeer(v)
				if err != nil {
					return nil, err
				}
				ingresses[i].From = *policyPeers
			}
		}
	}
	return &ingresses, nil
}

func expandNetworkPolicyEgress(l []interface{}) (*[]v1.NetworkPolicyEgressRule, error) {
	egresses := make([]v1.NetworkPolicyEgressRule, len(l), len(l))
	for i, egress := range l {
		if egress != nil {
			in := egress.(map[string]interface{})
			egresses[i] = v1.NetworkPolicyEgressRule{}
			if v, ok := in["ports"].([]interface{}); ok && len(v) > 0 {
				policyPorts, err := expandNetworkPolicyPorts(v)
				if err != nil {
					return nil, err
				}
				egresses[i].Ports = *policyPorts
			}
			if v, ok := in["to"].([]interface{}); ok && len(v) > 0 {
				policyPeers, err := expandNetworkPolicyPeer(v)
				if err != nil {
					return nil, err
				}
				egresses[i].To = *policyPeers
			}
		}
	}
	return &egresses, nil
}

func expandNetworkPolicyPorts(l []interface{}) (*[]v1.NetworkPolicyPort, error) {
	policyPorts := make([]v1.NetworkPolicyPort, len(l), len(l))
	for i, port := range l {
		in := port.(map[string]interface{})
		if in["port"] != nil && in["port"] != "" {
			portStr := in["port"].(string)
			if portInt, err := strconv.Atoi(portStr); err == nil && strconv.Itoa(portInt) == portStr {
				v := intstr.FromInt(portInt)
				policyPorts[i].Port = &v
			} else {
				v := intstr.FromString(portStr)
				policyPorts[i].Port = &v
			}
		}
		if in["protocol"] != nil && in["protocol"] != "" {
			v := api.Protocol(in["protocol"].(string))
			policyPorts[i].Protocol = &v

		}
	}
	return &policyPorts, nil
}

func expandNetworkPolicyPeer(l []interface{}) (*[]v1.NetworkPolicyPeer, error) {
	policyPeers := make([]v1.NetworkPolicyPeer, len(l), len(l))
	for i, peer := range l {
		in := peer.(map[string]interface{})
		if v, ok := in["ip_block"].([]interface{}); ok && len(v) > 0 {
			ipBlock, err := expandIPBlock(v)
			if err != nil {
				return nil, err
			}
			policyPeers[i].IPBlock = ipBlock
		}
		if v, ok := in["namespace_selector"].([]interface{}); ok && len(v) > 0 {
			policyPeers[i].NamespaceSelector = expandLabelSelector(v)
		}
		if v, ok := in["pod_selector"].([]interface{}); ok && len(v) > 0 {
			policyPeers[i].PodSelector = expandLabelSelector(v)
		}
	}
	return &policyPeers, nil
}

func expandIPBlock(l []interface{}) (*v1.IPBlock, error) {
	ipBlock := v1.IPBlock{}
	if len(l) == 0 || l[0] == nil {
		return nil, fmt.Errorf("failed to expand IPBlock: null or empty input")
	}
	in := l[0].(map[string]interface{})
	if v, ok := in["cidr"].(string); ok && v != "" {
		ipBlock.CIDR = v
	}
	if v, ok := in["except"].([]interface{}); ok && len(v) > 0 {
		ipBlock.Except = expandStringSlice(v)
	}
	return &ipBlock, nil
}

func expandNetworkPolicyTypes(l []interface{}) (*[]v1.PolicyType, error) {
	policyTypes := make([]v1.PolicyType, 0, 0)
	for _, policyType := range l {
		policyTypes = append(policyTypes, v1.PolicyType(policyType.(string)))
	}
	return &policyTypes, nil
}

// Patchers

func patchNetworkPolicySpec(keyPrefix, pathPrefix string, d *schema.ResourceData) (*PatchOperations, error) {
	ops := make(PatchOperations, 0, 0)
	if d.HasChange(keyPrefix + "ingress") {
		oldV, _ := d.GetChange(keyPrefix + "ingress")
		ingress, err := expandNetworkPolicyIngress(d.Get(keyPrefix + "ingress").([]interface{}))
		if err != nil {
			return nil, err
		}
		if len(oldV.([]interface{})) == 0 {
			ops = append(ops, &AddOperation{
				Path:  pathPrefix + "/ingress",
				Value: ingress,
			})
		} else {
			ops = append(ops, &ReplaceOperation{
				Path:  pathPrefix + "/ingress",
				Value: ingress,
			})
		}
	}
	if d.HasChange(keyPrefix + "egress") {
		oldV, _ := d.GetChange(keyPrefix + "egress")
		egress, err := expandNetworkPolicyEgress(d.Get(keyPrefix + "egress").([]interface{}))
		if err != nil {
			return nil, err
		}
		if len(oldV.([]interface{})) == 0 {
			ops = append(ops, &AddOperation{
				Path:  pathPrefix + "/egress",
				Value: egress,
			})
		} else {
			ops = append(ops, &ReplaceOperation{
				Path:  pathPrefix + "/egress",
				Value: egress,
			})
		}
	}
	if d.HasChange(keyPrefix + "pod_selector") {
		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "/podSelector",
			Value: expandLabelSelector(d.Get(keyPrefix + "pod_selector").([]interface{})),
		})
	}
	if d.HasChange(keyPrefix + "policy_types") {
		policyTypes, err := expandNetworkPolicyTypes(d.Get(keyPrefix + "policy_types").([]interface{}))
		if err != nil {
			return nil, err
		}
		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "/policyTypes",
			Value: *policyTypes,
		})
	}
	return &ops, nil
}
<commit_msg>Consolidate network policy expanders (#537)<commit_after>package kubernetes

import (
	"fmt"
	"strconv"

	"github.com/hashicorp/terraform/helper/schema"
	api "k8s.io/api/core/v1"
	"k8s.io/api/networking/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
)

// Flatteners

func flattenNetworkPolicySpec(in v1.NetworkPolicySpec) []interface{} {
	att := make(map[string]interface{})
	att["ingress"] = flattenNetworkPolicyIngress(in.Ingress)
	att["egress"] = flattenNetworkPolicyEgress(in.Egress)
	if len(in.PodSelector.MatchExpressions) > 0 || len(in.PodSelector.MatchLabels) > 0 {
		att["pod_selector"] = flattenLabelSelector(&in.PodSelector)
	} else {
		att["pod_selector"] = []interface{}{make(map[string]interface{})}
	}
	if len(in.PolicyTypes) > 0 {
		att["policy_types"] = in.PolicyTypes
	}
	return []interface{}{att}
}

func flattenNetworkPolicyIngress(in []v1.NetworkPolicyIngressRule) []interface{} {
	att := make([]interface{}, len(in), len(in))
	for i, ingress := range in {
		m := make(map[string]interface{})
		if ingress.Ports != nil && len(ingress.Ports) > 0 {
			m["ports"] = flattenNetworkPolicyPorts(ingress.Ports)
		}
		if ingress.From != nil && len(ingress.From) > 0 {
			m["from"] = flattenNetworkPolicyPeer(ingress.From)
		}
		att[i] = m
	}
	return att
}

func flattenNetworkPolicyEgress(in []v1.NetworkPolicyEgressRule) []interface{} {
	att := make([]interface{}, len(in), len(in))
	for i, egress := range in {
		m := make(map[string]interface{})
		if egress.Ports != nil && len(egress.Ports) > 0 {
			m["ports"] = flattenNetworkPolicyPorts(egress.Ports)
		}
		if egress.To != nil && len(egress.To) > 0 {
			m["to"] = flattenNetworkPolicyPeer(egress.To)
		}
		att[i] = m
	}
	return att
}

func flattenNetworkPolicyPorts(in []v1.NetworkPolicyPort) []interface{} {
	att := make([]interface{}, len(in), len(in))
	for i, port := range in {
		m := make(map[string]interface{})
		if port.Port != nil {
			if (*port.Port).Type == intstr.Int {
				m["port"] = strconv.Itoa(int((*port.Port).IntVal))
			} else {
				m["port"] = (*port.Port).StrVal
			}
		}
		if port.Protocol != nil {
			m["protocol"] = string(*port.Protocol)
		}
		att[i] = m
	}
	return att
}

func flattenNetworkPolicyPeer(in []v1.NetworkPolicyPeer) []interface{} {
	att := make([]interface{}, len(in), len(in))
	for i, peer := range in {
		m := make(map[string]interface{})
		if peer.IPBlock != nil {
			m["ip_block"] = flattenIPBlock(peer.IPBlock)
		}
		if peer.NamespaceSelector != nil {
			m["namespace_selector"] = flattenLabelSelector(peer.NamespaceSelector)
		}
		if peer.PodSelector != nil {
			m["pod_selector"] = flattenLabelSelector(peer.PodSelector)
		}
		att[i] = m
	}
	return att
}

func flattenIPBlock(in *v1.IPBlock) []interface{} {
	att := make(map[string]interface{})
	if in.CIDR != "" {
		att["cidr"] = in.CIDR
	}
	if len(in.Except) > 0 {
		att["except"] = in.Except
	}
	return []interface{}{att}
}

// Expanders

func expandNetworkPolicySpec(in []interface{}) (*v1.NetworkPolicySpec, error) {
	spec := v1.NetworkPolicySpec{}

	if len(in) == 0 || in[0] == nil {
		return nil, fmt.Errorf("failed to expand NetworkPolicy.Spec: null or empty input")
	}

	m, ok := in[0].(map[string]interface{})
	if !ok {
		return nil, fmt.Errorf("failed to expand NetworkPolicy.Spec: malformed input")
	}
	spec.PodSelector = *expandLabelSelector(m["pod_selector"].([]interface{}))
	if v, ok := m["ingress"].([]interface{}); ok && len(v) > 0 {
		ingress, err := expandNetworkPolicyIngress(v)
		if err != nil {
			return nil, err
		}
		spec.Ingress = *ingress
	}
	if v, ok := m["egress"].([]interface{}); ok && len(v) > 0 {
		egress, err := expandNetworkPolicyEgress(v)
		if err != nil {
			return nil, err
		}
		spec.Egress = *egress
	}
	policyTypes, err := expandNetworkPolicyTypes(m["policy_types"].([]interface{}))
	if err != nil {
		return nil, err
	}
	spec.PolicyTypes = *policyTypes

	return &spec, nil
}

func expandNetworkPolicyIngress(l []interface{}) (*[]v1.NetworkPolicyIngressRule, error) {
	ingresses := make([]v1.NetworkPolicyIngressRule, len(l), len(l))
	for i, ingress := range l {
		if ingress == nil {
			continue
		}
		in, ok := ingress.(map[string]interface{})
		if !ok {
			continue
		}
		ingresses[i] = v1.NetworkPolicyIngressRule{}
		if v, ok := in["ports"].([]interface{}); ok && len(v) > 0 {
			policyPorts, err := expandNetworkPolicyPorts(v)
			if err != nil {
				return nil, err
			}
			ingresses[i].Ports = *policyPorts
		}
		if v, ok := in["from"].([]interface{}); ok && len(v) > 0 {
			policyPeers, err := expandNetworkPolicyPeer(v)
			if err != nil {
				return nil, err
			}
			ingresses[i].From = *policyPeers
		}
	}
	return &ingresses, nil
}

func expandNetworkPolicyEgress(l []interface{}) (*[]v1.NetworkPolicyEgressRule, error) {
	egresses := make([]v1.NetworkPolicyEgressRule, len(l), len(l))
	for i, egress := range l {
		if egress == nil {
			continue
		}
		in, ok := egress.(map[string]interface{})
		if !ok {
			continue
		}
		egresses[i] = v1.NetworkPolicyEgressRule{}
		if v, ok := in["ports"].([]interface{}); ok && len(v) > 0 {
			policyPorts, err := expandNetworkPolicyPorts(v)
			if err != nil {
				return nil, err
			}
			egresses[i].Ports = *policyPorts
		}
		if v, ok := in["to"].([]interface{}); ok && len(v) > 0 {
			policyPeers, err := expandNetworkPolicyPeer(v)
			if err != nil {
				return nil, err
			}
			egresses[i].To = *policyPeers
		}
	}
	return &egresses, nil
}

func expandNetworkPolicyPorts(l []interface{}) (*[]v1.NetworkPolicyPort, error) {
	policyPorts := make([]v1.NetworkPolicyPort, len(l), len(l))
	for i, port := range l {
		in, ok := port.(map[string]interface{})
		if !ok {
			continue
		}
		if in["port"] != nil && in["port"] != "" {
			portStr := in["port"].(string)
			if portInt, err := strconv.Atoi(portStr); err == nil && strconv.Itoa(portInt) == portStr {
				v := intstr.FromInt(portInt)
				policyPorts[i].Port = &v
			} else {
				v := intstr.FromString(portStr)
				policyPorts[i].Port = &v
			}
		}
		if in["protocol"] != nil && in["protocol"] != "" {
			v := api.Protocol(in["protocol"].(string))
			policyPorts[i].Protocol = &v

		}
	}
	return &policyPorts, nil
}

func expandNetworkPolicyPeer(l []interface{}) (*[]v1.NetworkPolicyPeer, error) {
	policyPeers := make([]v1.NetworkPolicyPeer, len(l), len(l))
	for i, peer := range l {
		if peer == nil {
			continue
		}
		in, ok := peer.(map[string]interface{})
		if !ok {
			continue
		}
		if v, ok := in["ip_block"].([]interface{}); ok && len(v) > 0 {
			ipBlock, err := expandIPBlock(v)
			if err != nil {
				return nil, err
			}
			policyPeers[i].IPBlock = ipBlock
		}
		if v, ok := in["namespace_selector"].([]interface{}); ok && len(v) > 0 {
			policyPeers[i].NamespaceSelector = expandLabelSelector(v)
		}
		if v, ok := in["pod_selector"].([]interface{}); ok && len(v) > 0 {
			policyPeers[i].PodSelector = expandLabelSelector(v)
		}
	}
	return &policyPeers, nil
}

func expandIPBlock(l []interface{}) (*v1.IPBlock, error) {
	ipBlock := v1.IPBlock{}
	if len(l) == 0 || l[0] == nil {
		return nil, fmt.Errorf("failed to expand IPBlock: null or empty input")
	}
	in, ok := l[0].(map[string]interface{})
	if !ok {
		return nil, fmt.Errorf("failed to expand IPBlock: malformed input")
	}
	if v, ok := in["cidr"].(string); ok && v != "" {
		ipBlock.CIDR = v
	}
	if v, ok := in["except"].([]interface{}); ok && len(v) > 0 {
		ipBlock.Except = expandStringSlice(v)
	}
	return &ipBlock, nil
}

func expandNetworkPolicyTypes(l []interface{}) (*[]v1.PolicyType, error) {
	policyTypes := make([]v1.PolicyType, 0, 0)
	for _, policyType := range l {
		policyTypes = append(policyTypes, v1.PolicyType(policyType.(string)))
	}
	return &policyTypes, nil
}

// Patchers

func patchNetworkPolicySpec(keyPrefix, pathPrefix string, d *schema.ResourceData) (*PatchOperations, error) {
	ops := make(PatchOperations, 0, 0)
	if d.HasChange(keyPrefix + "ingress") {
		oldV, _ := d.GetChange(keyPrefix + "ingress")
		ingress, err := expandNetworkPolicyIngress(d.Get(keyPrefix + "ingress").([]interface{}))
		if err != nil {
			return nil, err
		}
		if len(oldV.([]interface{})) == 0 {
			ops = append(ops, &AddOperation{
				Path:  pathPrefix + "/ingress",
				Value: ingress,
			})
		} else {
			ops = append(ops, &ReplaceOperation{
				Path:  pathPrefix + "/ingress",
				Value: ingress,
			})
		}
	}
	if d.HasChange(keyPrefix + "egress") {
		oldV, _ := d.GetChange(keyPrefix + "egress")
		egress, err := expandNetworkPolicyEgress(d.Get(keyPrefix + "egress").([]interface{}))
		if err != nil {
			return nil, err
		}
		if len(oldV.([]interface{})) == 0 {
			ops = append(ops, &AddOperation{
				Path:  pathPrefix + "/egress",
				Value: egress,
			})
		} else {
			ops = append(ops, &ReplaceOperation{
				Path:  pathPrefix + "/egress",
				Value: egress,
			})
		}
	}
	if d.HasChange(keyPrefix + "pod_selector") {
		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "/podSelector",
			Value: expandLabelSelector(d.Get(keyPrefix + "pod_selector").([]interface{})),
		})
	}
	if d.HasChange(keyPrefix + "policy_types") {
		policyTypes, err := expandNetworkPolicyTypes(d.Get(keyPrefix + "policy_types").([]interface{}))
		if err != nil {
			return nil, err
		}
		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "/policyTypes",
			Value: *policyTypes,
		})
	}
	return &ops, nil
}
<|endoftext|>


<commit_before>package tango

import (
	"reflect"
	"path"
)

type groupRouter struct {
	methods []string
	url string
	c interface{}
}

type Group struct {
	routers []groupRouter
}

func NewGroup() *Group {
	return &Group{
		routers : make([]groupRouter, 0),
	}
}

func (g *Group) Get(url string, c interface{}) {
	g.Route([]string{"GET", "HEAD"}, url, c)
}

func (g *Group) Post(url string, c interface{}) {
	g.Route([]string{"POST"}, url, c)
}

func (g *Group) Head(url string, c interface{}) {
	g.Route([]string{"HEAD"}, url, c)
}

func (g *Group) Options(url string, c interface{}) {
	g.Route([]string{"OPTIONS"}, url, c)
}

func (g *Group) Trace(url string, c interface{}) {
	g.Route([]string{"TRACE"}, url, c)
}

func (g *Group) Patch(url string, c interface{}) {
	g.Route([]string{"PATCH"}, url, c)
}

func (g *Group) Delete(url string, c interface{}) {
	g.Route([]string{"DELETE"}, url, c)
}

func (g *Group) Put(url string, c interface{}) {
	g.Route([]string{"PUT"}, url, c)
}

func (g *Group) Any(url string, c interface{}) {
	g.Route(SupportMethods, url, c)
}

func (g *Group) Route(methods []string, url string, c interface{}) {
	g.routers = append(g.routers, groupRouter{methods, url, c})
}

func (t *Tango) addGroup(p string, g *Group) {
	for _, r := range g.routers {
		t.Route(r.methods, path.Join(p, r.url), r.c)
	}
}

var (
	gt = reflect.TypeOf(new(Group))
)

func (t *Tango) Group(p string, o interface{}) {
	vc := reflect.ValueOf(o)
	tp := vc.Type()
	var g *Group
	if tp == gt {
		g = o.(*Group)
	} else if tp.Kind() == reflect.Func &&
		tp.NumIn() == 1 && tp.In(0) == gt {
		g = NewGroup()
		vc.Call([]reflect.Value{reflect.ValueOf(g)})
	} else {
		panic("not allowed group parameter")
	}
	t.addGroup(p, g)
}
<commit_msg>group of group support<commit_after>package tango

import (
	"reflect"
	"path"
)

type groupRouter struct {
	methods []string
	url string
	c interface{}
}

type Group struct {
	routers []groupRouter
}

func NewGroup() *Group {
	return &Group{
		routers : make([]groupRouter, 0),
	}
}

func (g *Group) Get(url string, c interface{}) {
	g.Route([]string{"GET", "HEAD"}, url, c)
}

func (g *Group) Post(url string, c interface{}) {
	g.Route([]string{"POST"}, url, c)
}

func (g *Group) Head(url string, c interface{}) {
	g.Route([]string{"HEAD"}, url, c)
}

func (g *Group) Options(url string, c interface{}) {
	g.Route([]string{"OPTIONS"}, url, c)
}

func (g *Group) Trace(url string, c interface{}) {
	g.Route([]string{"TRACE"}, url, c)
}

func (g *Group) Patch(url string, c interface{}) {
	g.Route([]string{"PATCH"}, url, c)
}

func (g *Group) Delete(url string, c interface{}) {
	g.Route([]string{"DELETE"}, url, c)
}

func (g *Group) Put(url string, c interface{}) {
	g.Route([]string{"PUT"}, url, c)
}

func (g *Group) Any(url string, c interface{}) {
	g.Route(SupportMethods, url, c)
}

func (g *Group) Route(methods []string, url string, c interface{}) {
	g.routers = append(g.routers, groupRouter{methods, url, c})
}

func (g *Group) Group(p string, o interface{}) {
	gr := getGroup(o)
	for _, gchild := range gr.routers {
		g.Route(gchild.methods, path.Join(p, gchild.url), gchild.c)
	}
}

func getGroup(o interface{}) *Group {
	vc := reflect.ValueOf(o)
	tp := vc.Type()
	var g *Group
	if tp == gt {
		g = o.(*Group)
	} else if tp.Kind() == reflect.Func &&
		tp.NumIn() == 1 && tp.In(0) == gt {
		g = NewGroup()
		vc.Call([]reflect.Value{reflect.ValueOf(g)})
	} else {
		panic("not allowed group parameter")
	}
	return g
}

func (t *Tango) addGroup(p string, g *Group) {
	for _, r := range g.routers {
		t.Route(r.methods, path.Join(p, r.url), r.c)
	}
}

var (
	gt = reflect.TypeOf(new(Group))
)

func (t *Tango) Group(p string, o interface{}) {
	t.addGroup(p, getGroup(o))
}
<|endoftext|>


<commit_before>package main

// See instructions in the README.md file that accompanies this program.
//
// (C) 2014 by Marco Paganini <paganini AT paganini DOT net>

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path"
	"strings"
	"time"

	"github.com/marcopaganini/gsync/vfs/gdrive"
	"github.com/marcopaganini/gsync/vfs/local"
)

const (
	AUTH_CACHE_FILE  = ".gsync-token-cache.json"
	CREDENTIALS_FILE = ".gsync-credentials.json"
)

var (
	clientId     = flag.String("id", "", "Client ID")
	clientSecret = flag.String("secret", "", "Client Secret")
	code         = flag.String("code", "", "Authorization Code")
)

type GdriveCredentials struct {
	ClientId     string
	ClientSecret string
}

// VFS interface
type gsyncVfs interface {
	FileTree() ([]string, error)
	FileExists(string) (bool, error)
	IsDir(string) (bool, error)
	IsRegular(string) (bool, error)
	Mkdir(string) error
	Mtime(string) (time.Time, error)
	Path() string
	ReadFromFile(string) (io.Reader, error)
	Size(string) (int64, error)
	WriteToFile(string, io.Reader) error
}

// Retrieve the source and destination from the command-line, performing basic sanity checking
//
// Returns:
// 	string: source directory
// 	string: destination directory
// 	error
func getSourceDest() (string, string, error) {
	if flag.NArg() != 2 {
		return "", "", fmt.Errorf("Must specify source and destination directories")
	}

	src := flag.Arg(0)
	dst := flag.Arg(1)

	// Only supports copies from local to Gdrive or vice versa.
	// Local->Local and Remote->Remote are not supported.
	srcGdrive, _ := isGdrivePath(src)
	dstGdrive, _ := isGdrivePath(dst)

	if (srcGdrive && dstGdrive) || (!srcGdrive && !dstGdrive) {
		return "", "", fmt.Errorf("Local/Local and Remote/Remote copies not supported.")
	}
	return src, dst, nil
}

// Save and/or load credentials from disk.
//
// If clientId and clientSecret are set, this function saves them to credFile. If not,
// it loads those parameters from disk and returns them.
//
// Returns:
//   - *GdriveCredentials: containing the credentials.
//   - error
func handleCredentials(credFile string, clientId string, clientSecret string) (*GdriveCredentials, error) {
	var cred *GdriveCredentials

	// If client, secret and code specified, save config
	if clientId != "" && clientSecret != "" {
		cred = &GdriveCredentials{ClientId: clientId, ClientSecret: clientSecret}
		j, err := json.Marshal(*cred)
		if err != nil {
			return nil, fmt.Errorf("Unable to convert configuration to JSON: %v", err)
		}

		if ioutil.WriteFile(credFile, j, 0600) != nil {
			return nil, fmt.Errorf("Unable to write configuration file \"%s\": %v", credFile, err)
		}
	} else {
		j, err := ioutil.ReadFile(credFile)
		if err != nil {
			return nil, fmt.Errorf("Unable to read configuration from \"%s\": %v", credFile, err)
		}
		cred = &GdriveCredentials{}
		if json.Unmarshal(j, cred) != nil {
			return nil, fmt.Errorf("Unable to decode configuration form \"%s\": %v", credFile, err)
		}
	}
	return cred, nil
}

// Prints error message and program usage to stderr, exit the program.
func usage(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n\n", err)
	}
	fmt.Fprintf(os.Stderr, "Usage%s:\n", os.Args[0])
	flag.PrintDefaults()
	os.Exit(2)
}

// Check if fullpath looks like a gdrive path (starting with g: or gdrive:). If
// so, return true and the path without the prefix. Otherwise, return false and
// the path itself.
//
// Returns
//   bool
//   realpath
func isGdrivePath(fullpath string) (bool, string) {
	if strings.HasPrefix(fullpath, "g:") || strings.HasPrefix(fullpath, "gdrive:") {
		idx := strings.Index(fullpath, ":")
		return true, fullpath[idx+1:]
	}
	return false, fullpath
}

// Determine if we need to copy the file pointed by srcpath in srcvfs to
// the file dstpath in dstvfs.
//
// Return:
// 	 bool
// 	 error
func needToCopy(srcvfs gsyncVfs, dstvfs gsyncVfs, srcpath string, dstpath string) (bool, error) {
	// If destination doesn't exist we need to copy
	exists, err := dstvfs.FileExists(dstpath)
	if err != nil {
		return false, err
	}
	if !exists {
		return true, nil
	}

	// If destination exists, we check mtimes
	srcMtime, err := srcvfs.Mtime(srcpath)
	if err != nil {
		return false, err
	}
	dstMtime, err := dstvfs.Mtime(dstpath)
	if err != nil {
		return false, err
	}
	if srcMtime.After(dstMtime) {
		return true, nil
	}

	return false, nil
}

func Sync(srcvfs gsyncVfs, dstvfs gsyncVfs) error {
	var (
		srcdir string
		dstdir string
	)

	srcdir = srcvfs.Path()
	dstdir = dstvfs.Path()

	srctree, err := srcvfs.FileTree()
	if err != nil {
		log.Fatal(err)
	}
	for _, src := range srctree {
		// If the source path ends in a slash, we'll copy the *contents* of the
		// source directory to the destination. If it doesn't, we'll create a
		// directory inside the destination. This matches rsync's behavior
		//
		// Ex:
		// /a/b/c/ -> foo = /foo/<files>...
		// /a/b/c  -> foo = /foo/c/<files>...

		// Default == copy files INTO directory at destination
		dst := path.Join(dstdir, src[len(srcdir):])

		// If source does not end in "/", we create the directory specified
		// by srcdir as the first level inside the destination.
		if !strings.HasSuffix(srcdir, "/") {
			sdir := strings.Split(srcdir, "/")
			if len(sdir) > 1 {
				last := len(sdir) - 1
				ssrc := strings.Split(src, "/")
				dst = path.Join(dstdir, strings.Join(ssrc[last:], "/"))
			}
		}

		isdir, err := srcvfs.IsDir(src)
		if err != nil {
			log.Fatal(err)
		}
		isregular, err := srcvfs.IsRegular(src)
		if err != nil {
			log.Fatal(err)
		}

		// Start sync operation

		if isdir {
			fmt.Printf("====> %s\n", src)
			fmt.Printf("      %s\n", dst)

			// Create destination dir if needed
			exists, err := dstvfs.FileExists(dst)
			if err != nil {
				log.Fatalln(err)
			}
			if !exists {
				err := dstvfs.Mkdir(dst)
				if err != nil {
					log.Fatalln(err)
				}
			}
		} else if isregular {
			copyStat := "Not copied"
			copyNeeded, err := needToCopy(srcvfs, dstvfs, src, dst)
			if err != nil {
				log.Fatalln(err)
			}

			if copyNeeded {
				r, err := srcvfs.ReadFromFile(src)
				if err != nil {
					log.Fatalln(err)
				}
				err = dstvfs.WriteToFile(dst, r)
				if err != nil {
					log.Fatalln(err)
				}
				copyStat = "Copied"
			}
			// Print details
			size, err := srcvfs.Size(src)
			if err != nil {
				log.Fatal(err)
			}
			fmt.Printf("    %8d %s -> %s [%s]\n", size, src, dst, copyStat)
		} else {
			fmt.Printf("Warning: Ignoring \"%s\" which is not a file or directory.\n", src)
		}
	}

	return nil
}

func main() {
	var (
		srcvfs gsyncVfs
		dstvfs gsyncVfs
		gpath  string
		lpath  string
	)

	flag.Parse()

	srcdir, dstdir, err := getSourceDest()
	if err != nil {
		usage(err)
	}

	srcGdrive, srcPath := isGdrivePath(srcdir)
	_, dstPath := isGdrivePath(dstdir)

	// Credentials and cache file
	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}
	credfile := path.Join(usr.HomeDir, CREDENTIALS_FILE)
	cred, err := handleCredentials(credfile, *clientId, *clientSecret)
	if err != nil {
		log.Fatal(err)
	}
	cachefile := path.Join(usr.HomeDir, AUTH_CACHE_FILE)

	// Initialize virtual filesystems
	if srcGdrive {
		gpath = srcPath
		lpath = dstPath
	} else {
		lpath = srcPath
		gpath = dstPath
	}

	srcvfs, err = gdrivevfs.NewGdriveFileSystem(gpath, cred.ClientId, cred.ClientSecret, *code, cachefile)
	if err != nil {
		log.Fatal(err)
	}
	dstvfs, err = localvfs.NewLocalFileSystem(lpath)
	if err != nil {
		log.Fatal(err)
	}

	err = Sync(srcvfs, dstvfs)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Replaced all flag.String by flag.Stringvar<commit_after>package main

// See instructions in the README.md file that accompanies this program.
//
// (C) 2014 by Marco Paganini <paganini AT paganini DOT net>

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path"
	"strings"
	"time"

	"github.com/marcopaganini/gsync/vfs/gdrive"
	"github.com/marcopaganini/gsync/vfs/local"
)

const (
	AUTH_CACHE_FILE  = ".gsync-token-cache.json"
	CREDENTIALS_FILE = ".gsync-credentials.json"
)

var (
	// Command line Flags
	optClientId     string
	optClientSecret string
	optCode         string
)

type GdriveCredentials struct {
	ClientId     string
	ClientSecret string
}

// VFS interface
type gsyncVfs interface {
	FileTree() ([]string, error)
	FileExists(string) (bool, error)
	IsDir(string) (bool, error)
	IsRegular(string) (bool, error)
	Mkdir(string) error
	Mtime(string) (time.Time, error)
	Path() string
	ReadFromFile(string) (io.Reader, error)
	Size(string) (int64, error)
	WriteToFile(string, io.Reader) error
}

// Retrieve the source and destination from the command-line, performing basic sanity checking
//
// Returns:
// 	string: source directory
// 	string: destination directory
// 	error
func getSourceDest() (string, string, error) {
	if flag.NArg() != 2 {
		return "", "", fmt.Errorf("Must specify source and destination directories")
	}

	src := flag.Arg(0)
	dst := flag.Arg(1)

	// Only supports copies from local to Gdrive or vice versa.
	// Local->Local and Remote->Remote are not supported.
	srcGdrive, _ := isGdrivePath(src)
	dstGdrive, _ := isGdrivePath(dst)

	if (srcGdrive && dstGdrive) || (!srcGdrive && !dstGdrive) {
		return "", "", fmt.Errorf("Local/Local and Remote/Remote copies not supported.")
	}
	return src, dst, nil
}

// Save and/or load credentials from disk.
//
// If clientId and clientSecret are set, this function saves them to credFile. If not,
// it loads those parameters from disk and returns them.
//
// Returns:
//   - *GdriveCredentials: containing the credentials.
//   - error
func handleCredentials(credFile string, clientId string, clientSecret string) (*GdriveCredentials, error) {
	var cred *GdriveCredentials

	// If client, secret and code specified, save config
	if clientId != "" && clientSecret != "" {
		cred = &GdriveCredentials{ClientId: clientId, ClientSecret: clientSecret}
		j, err := json.Marshal(*cred)
		if err != nil {
			return nil, fmt.Errorf("Unable to convert configuration to JSON: %v", err)
		}

		if ioutil.WriteFile(credFile, j, 0600) != nil {
			return nil, fmt.Errorf("Unable to write configuration file \"%s\": %v", credFile, err)
		}
	} else {
		j, err := ioutil.ReadFile(credFile)
		if err != nil {
			return nil, fmt.Errorf("Unable to read configuration from \"%s\": %v", credFile, err)
		}
		cred = &GdriveCredentials{}
		if json.Unmarshal(j, cred) != nil {
			return nil, fmt.Errorf("Unable to decode configuration form \"%s\": %v", credFile, err)
		}
	}
	return cred, nil
}

// Prints error message and program usage to stderr, exit the program.
func usage(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n\n", err)
	}
	fmt.Fprintf(os.Stderr, "Usage%s:\n", os.Args[0])
	flag.PrintDefaults()
	os.Exit(2)
}

// Check if fullpath looks like a gdrive path (starting with g: or gdrive:). If
// so, return true and the path without the prefix. Otherwise, return false and
// the path itself.
//
// Returns
//   bool
//   realpath
func isGdrivePath(fullpath string) (bool, string) {
	if strings.HasPrefix(fullpath, "g:") || strings.HasPrefix(fullpath, "gdrive:") {
		idx := strings.Index(fullpath, ":")
		return true, fullpath[idx+1:]
	}
	return false, fullpath
}

// Determine if we need to copy the file pointed by srcpath in srcvfs to
// the file dstpath in dstvfs.
//
// Return:
// 	 bool
// 	 error
func needToCopy(srcvfs gsyncVfs, dstvfs gsyncVfs, srcpath string, dstpath string) (bool, error) {
	// If destination doesn't exist we need to copy
	exists, err := dstvfs.FileExists(dstpath)
	if err != nil {
		return false, err
	}
	if !exists {
		return true, nil
	}

	// If destination exists, we check mtimes
	srcMtime, err := srcvfs.Mtime(srcpath)
	if err != nil {
		return false, err
	}
	dstMtime, err := dstvfs.Mtime(dstpath)
	if err != nil {
		return false, err
	}
	if srcMtime.After(dstMtime) {
		return true, nil
	}

	return false, nil
}

func Sync(srcvfs gsyncVfs, dstvfs gsyncVfs) error {
	var (
		srcdir string
		dstdir string
	)

	srcdir = srcvfs.Path()
	dstdir = dstvfs.Path()

	srctree, err := srcvfs.FileTree()
	if err != nil {
		log.Fatal(err)
	}
	for _, src := range srctree {
		// If the source path ends in a slash, we'll copy the *contents* of the
		// source directory to the destination. If it doesn't, we'll create a
		// directory inside the destination. This matches rsync's behavior
		//
		// Ex:
		// /a/b/c/ -> foo = /foo/<files>...
		// /a/b/c  -> foo = /foo/c/<files>...

		// Default == copy files INTO directory at destination
		dst := path.Join(dstdir, src[len(srcdir):])

		// If source does not end in "/", we create the directory specified
		// by srcdir as the first level inside the destination.
		if !strings.HasSuffix(srcdir, "/") {
			sdir := strings.Split(srcdir, "/")
			if len(sdir) > 1 {
				last := len(sdir) - 1
				ssrc := strings.Split(src, "/")
				dst = path.Join(dstdir, strings.Join(ssrc[last:], "/"))
			}
		}

		isdir, err := srcvfs.IsDir(src)
		if err != nil {
			log.Fatal(err)
		}
		isregular, err := srcvfs.IsRegular(src)
		if err != nil {
			log.Fatal(err)
		}

		// Start sync operation

		if isdir {
			fmt.Printf("====> %s\n", src)
			fmt.Printf("      %s\n", dst)

			// Create destination dir if needed
			exists, err := dstvfs.FileExists(dst)
			if err != nil {
				log.Fatalln(err)
			}
			if !exists {
				err := dstvfs.Mkdir(dst)
				if err != nil {
					log.Fatalln(err)
				}
			}
		} else if isregular {
			copyStat := "Not copied"
			copyNeeded, err := needToCopy(srcvfs, dstvfs, src, dst)
			if err != nil {
				log.Fatalln(err)
			}

			if copyNeeded {
				r, err := srcvfs.ReadFromFile(src)
				if err != nil {
					log.Fatalln(err)
				}
				err = dstvfs.WriteToFile(dst, r)
				if err != nil {
					log.Fatalln(err)
				}
				copyStat = "Copied"
			}
			// Print details
			size, err := srcvfs.Size(src)
			if err != nil {
				log.Fatal(err)
			}
			fmt.Printf("    %8d %s -> %s [%s]\n", size, src, dst, copyStat)
		} else {
			fmt.Printf("Warning: Ignoring \"%s\" which is not a file or directory.\n", src)
		}
	}

	return nil
}

func main() {
	var (
		srcvfs gsyncVfs
		dstvfs gsyncVfs
		gpath  string
		lpath  string
	)

	// Parse command line
	flag.StringVar(&optClientId, "id", "", "Client ID")
	flag.StringVar(&optClientSecret, "secret", "", "Client Secret")
	flag.StringVar(&optCode, "code", "", "Authorization Code")
	flag.Parse()

	srcdir, dstdir, err := getSourceDest()
	if err != nil {
		usage(err)
	}

	srcGdrive, srcPath := isGdrivePath(srcdir)
	_, dstPath := isGdrivePath(dstdir)

	// Credentials and cache file
	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}
	credfile := path.Join(usr.HomeDir, CREDENTIALS_FILE)
	cred, err := handleCredentials(credfile, optClientId, optClientSecret)
	if err != nil {
		log.Fatal(err)
	}
	cachefile := path.Join(usr.HomeDir, AUTH_CACHE_FILE)

	// Initialize virtual filesystems
	if srcGdrive {
		gpath = srcPath
		lpath = dstPath
	} else {
		lpath = srcPath
		gpath = dstPath
	}

	srcvfs, err = gdrivevfs.NewGdriveFileSystem(gpath, cred.ClientId, cred.ClientSecret, optCode, cachefile)
	if err != nil {
		log.Fatal(err)
	}
	dstvfs, err = localvfs.NewLocalFileSystem(lpath)
	if err != nil {
		log.Fatal(err)
	}

	err = Sync(srcvfs, dstvfs)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>