<commit_before>package main

import (
	"crypto/x509"
	"fmt"
	"net/http"

	"io/ioutil"

	"encoding/base64"
	"encoding/xml"

	saml2 "github.com/russellhaering/gosaml2"
	"github.com/russellhaering/gosaml2/types"
	dsig "github.com/russellhaering/goxmldsig"
)

func main() {
	res, err := http.Get("http://idp.oktadev.com/metadata")
	if err != nil {
		panic(err)
	}

	rawMetadata, err := ioutil.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}

	metadata := &types.EntityDescriptor{}
	err = xml.Unmarshal(rawMetadata, metadata)
	if err != nil {
		panic(err)
	}

	certStore := dsig.MemoryX509CertificateStore{
		Roots: []*x509.Certificate{},
	}

	for _, kd := range metadata.IDPSSODescriptor.KeyDescriptors {
		certData, err := base64.StdEncoding.DecodeString(kd.KeyInfo.X509Data.X509Certificate.Data)
		if err != nil {
			panic(err)
		}

		idpCert, err := x509.ParseCertificate(certData)
		if err != nil {
			panic(err)
		}

		certStore.Roots = append(certStore.Roots, idpCert)
	}

	// We sign the AuthnRequest with a random key because Okta doesn't seem
	// to verify these.
	randomKeyStore := dsig.RandomKeyStoreForTest()

	sp := &saml2.SAMLServiceProvider{
		IdentityProviderSSOURL:      metadata.IDPSSODescriptor.SingleSignOnService.Location,
		IdentityProviderIssuer:      "http://example.com/saml/acs/example",
		AssertionConsumerServiceURL: "http://localhost:8080/v1/_saml_callback",
		SignAuthnRequests:           true,
		AudienceURI:                 "http://example.com/saml/acs/example",
		IDPCertificateStore:         &certStore,
		SPKeyStore:                  randomKeyStore,
	}

	http.HandleFunc("/v1/_saml_callback", func(rw http.ResponseWriter, req *http.Request) {
		err := req.ParseForm()
		if err != nil {
			rw.WriteHeader(http.StatusBadRequest)
			return
		}

		assertionInfo, err := sp.RetrieveAssertionInfo(req.FormValue("SAMLResponse"))
		if err != nil {
			rw.WriteHeader(http.StatusForbidden)
			return
		}

		fmt.Fprintf(rw, "NameID: %s\n", assertionInfo.NameID)

		fmt.Fprintf(rw, "Assertions:\n")

		for key, val := range assertionInfo.Values {
			fmt.Fprintf(rw, "  %s: %+v\n", key, val)
		}

		fmt.Fprintf(rw, "\n")

		fmt.Fprintf(rw, "Warnings:\n")
		fmt.Fprintf(rw, "%+v\n", assertionInfo.WarningInfo)
	})

	println("Visit this URL To Authenticate:")
	authURL, err := sp.BuildAuthURL("")
	if err != nil {
		panic(err)
	}

	println(authURL)

	println("Supply:")
	fmt.Printf("  SP ACS URL      : %s\n", sp.AssertionConsumerServiceURL)

	err = http.ListenAndServe(":8080", nil)
	if err != nil {
		panic(err)
	}
}
<commit_msg>Check WarningInfo in example app<commit_after>package main

import (
	"crypto/x509"
	"fmt"
	"net/http"

	"io/ioutil"

	"encoding/base64"
	"encoding/xml"

	saml2 "github.com/russellhaering/gosaml2"
	"github.com/russellhaering/gosaml2/types"
	dsig "github.com/russellhaering/goxmldsig"
)

func main() {
	res, err := http.Get("http://idp.oktadev.com/metadata")
	if err != nil {
		panic(err)
	}

	rawMetadata, err := ioutil.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}

	metadata := &types.EntityDescriptor{}
	err = xml.Unmarshal(rawMetadata, metadata)
	if err != nil {
		panic(err)
	}

	certStore := dsig.MemoryX509CertificateStore{
		Roots: []*x509.Certificate{},
	}

	for _, kd := range metadata.IDPSSODescriptor.KeyDescriptors {
		certData, err := base64.StdEncoding.DecodeString(kd.KeyInfo.X509Data.X509Certificate.Data)
		if err != nil {
			panic(err)
		}

		idpCert, err := x509.ParseCertificate(certData)
		if err != nil {
			panic(err)
		}

		certStore.Roots = append(certStore.Roots, idpCert)
	}

	// We sign the AuthnRequest with a random key because Okta doesn't seem
	// to verify these.
	randomKeyStore := dsig.RandomKeyStoreForTest()

	sp := &saml2.SAMLServiceProvider{
		IdentityProviderSSOURL:      metadata.IDPSSODescriptor.SingleSignOnService.Location,
		IdentityProviderIssuer:      "http://example.com/saml/acs/example",
		AssertionConsumerServiceURL: "http://localhost:8080/v1/_saml_callback",
		SignAuthnRequests:           true,
		AudienceURI:                 "http://example.com/saml/acs/example",
		IDPCertificateStore:         &certStore,
		SPKeyStore:                  randomKeyStore,
	}

	http.HandleFunc("/v1/_saml_callback", func(rw http.ResponseWriter, req *http.Request) {
		err := req.ParseForm()
		if err != nil {
			rw.WriteHeader(http.StatusBadRequest)
			return
		}

		assertionInfo, err := sp.RetrieveAssertionInfo(req.FormValue("SAMLResponse"))
		if err != nil {
			rw.WriteHeader(http.StatusForbidden)
			return
		}

		if assertionInfo.WarningInfo.InvalidTime {
			rw.WriteHeader(http.StatusForbidden)
			return
		}

		if assertionInfo.WarningInfo.NotInAudience {
			rw.WriteHeader(http.StatusForbidden)
			return
		}

		fmt.Fprintf(rw, "NameID: %s\n", assertionInfo.NameID)

		fmt.Fprintf(rw, "Assertions:\n")

		for key, val := range assertionInfo.Values {
			fmt.Fprintf(rw, "  %s: %+v\n", key, val)
		}

		fmt.Fprintf(rw, "\n")

		fmt.Fprintf(rw, "Warnings:\n")
		fmt.Fprintf(rw, "%+v\n", assertionInfo.WarningInfo)
	})

	println("Visit this URL To Authenticate:")
	authURL, err := sp.BuildAuthURL("")
	if err != nil {
		panic(err)
	}

	println(authURL)

	println("Supply:")
	fmt.Printf("  SP ACS URL      : %s\n", sp.AssertionConsumerServiceURL)

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


<commit_before>package visual

import (
	"fmt"
	"io"
	"math"
	"math/rand"

	"github.com/aaasen/kapok/graph"
	svg "github.com/ajstarks/svgo"
)

const width = 1024
const height = 1024

type positions struct {
	Positions map[string]*Vector
	Graph     *graph.Graph
}

func newPositions(graph *graph.Graph) *positions {
	return &positions{
		Positions: make(map[string]*Vector),
		Graph:     graph,
	}
}

func (self *positions) SafeGet(node *graph.Node) *Vector {
	vector := self.Positions[node.Name]

	if vector == nil {
		angle := rand.Float64() * math.Pi * 2
		r := (width / 2.5) / float64(len(self.Graph.Nodes[node])+1)

		rotationCorrection := 0.0

		if angle > math.Pi/2.0 && angle < 3*(math.Pi/2) {
			rotationCorrection = math.Pi
		}

		vector = &Vector{
			X:   int((width / 2) + math.Cos(angle)*r),
			Y:   int((height / 2) + math.Sin(angle)*r),
			Rot: (angle + rotationCorrection) * (180 / math.Pi),
			Rad: r,
		}

		self.Positions[node.Name] = vector
	}

	return vector
}

func Visualise(g *graph.Graph, writer io.Writer) *svg.SVG {
	canvas := svg.New(writer)
	canvas.Start(width, height)

	// positionsA := newPositions(g)

	levels := make(map[int][]*graph.Node)

	for node, _ := range g.Nodes {
		mag := len(g.Nodes[node])

		if levels[mag] == nil {
			levels[mag] = make([]*graph.Node, 0)
		}

		levels[mag] = append(levels[mag], node)
	}

	for mag, nodes := range levels {
		angle := 0.0

		for _, node := range nodes {
			rad := (width / 2.5) / float64(mag+1)

			rotationCorrection := 0.0

			if angle > math.Pi/2.0 && angle < 3*(math.Pi/2) {
				rotationCorrection = math.Pi
			}

			nodePos := &Vector{
				X:   int((width / 2) + math.Cos(angle)*rad),
				Y:   int((height / 2) + math.Sin(angle)*rad),
				Rot: (angle + rotationCorrection) * (180 / math.Pi),
				Rad: rad,
			}

			canvas.Circle(
				nodePos.X,
				nodePos.Y,
				1,
				"fill:black")

			// for neighbor, _ := range g.Nodes[node] {
			// 	neighborPos := positionsA.SafeGet(neighbor)

			// 	canvas.Line(
			// 		nodePos.X, nodePos.Y,
			// 		neighborPos.X, neighborPos.Y,
			// 		"stroke:rgba(0, 0, 0, 0.2);stroke-width:0.5")
			// }

			canvas.Text(
				0,
				0,
				node.Name,
				`style="text-anchor:middle;font-size:12px;fill:#5bb4c0;"`,
				fmt.Sprintf(`transform="rotate(%v, %v, %v) translate(%v, %v)"`, nodePos.Rot, nodePos.X, nodePos.Y, nodePos.X, nodePos.Y))

			angle += (math.Pi * 2) / float64(len(nodes))
		}
	}

	canvas.End()

	return canvas
}
<commit_msg>text closer to the center is now bigger. stylized a bit as well<commit_after>package visual

import (
	"fmt"
	"io"
	"math"
	"math/rand"

	"github.com/aaasen/kapok/graph"
	svg "github.com/ajstarks/svgo"
)

const width = 2048
const height = width

type positions struct {
	Positions map[string]*Vector
	Graph     *graph.Graph
}

func newPositions(graph *graph.Graph) *positions {
	return &positions{
		Positions: make(map[string]*Vector),
		Graph:     graph,
	}
}

func (self *positions) SafeGet(node *graph.Node) *Vector {
	vector := self.Positions[node.Name]

	if vector == nil {
		angle := rand.Float64() * math.Pi * 2
		r := (width / 2.5) / float64(len(self.Graph.Nodes[node])+1)

		rotationCorrection := 0.0

		if angle > math.Pi/2.0 && angle < 3*(math.Pi/2) {
			rotationCorrection = math.Pi
		}

		vector = &Vector{
			X:   int((width / 2) + math.Cos(angle)*r),
			Y:   int((height / 2) + math.Sin(angle)*r),
			Rot: (angle + rotationCorrection) * (180 / math.Pi),
			Rad: r,
		}

		self.Positions[node.Name] = vector
	}

	return vector
}

func Visualise(g *graph.Graph, writer io.Writer) *svg.SVG {
	canvas := svg.New(writer)
	canvas.Start(width, height)

	positions := make(map[string]*Vector)
	levels := make(map[int][]*graph.Node)

	for node, _ := range g.Nodes {
		mag := len(g.Nodes[node])

		if levels[mag] == nil {
			levels[mag] = make([]*graph.Node, 0)
		}

		levels[mag] = append(levels[mag], node)
	}

	for mag, nodes := range levels {
		angle := 0.0

		for _, node := range nodes {
			rad := (width / 2.5) / float64(mag+1)

			rotationCorrection := 0.0

			if angle > math.Pi/2.0 && angle < 3*(math.Pi/2) {
				rotationCorrection = math.Pi
			}

			positions[node.Name] = &Vector{
				X:   int((width / 2) + math.Cos(angle)*rad),
				Y:   int((height / 2) + math.Sin(angle)*rad),
				Rot: (angle + rotationCorrection) * (180 / math.Pi),
				Rad: rad,
			}

			angle += (math.Pi * 2) / float64(len(nodes))
		}
	}

	for node, _ := range g.Nodes {
		for neighbor, _ := range g.Nodes[node] {
			vector := positions[node.Name]
			neighborVec := positions[neighbor.Name]

			canvas.Line(
				vector.X, vector.Y,
				neighborVec.X, neighborVec.Y,
				"stroke:#e74c3c;stroke-width:0.5")
		}
	}

	for node, vector := range positions {
		canvas.Circle(
			vector.X,
			vector.Y,
			1,
			"fill:rgba(0, 0, 0, 0.2)")

		canvas.Text(
			0,
			0,
			node,
			fmt.Sprintf(`style="text-anchor:middle;font-size:%vpx;fill:rbga(0, 0, 0, 1);"`,
				int(64.0/(vector.Rad))+12),
			fmt.Sprintf(`transform="rotate(%v, %v, %v) translate(%v, %v)"`,
				vector.Rot,
				vector.X, vector.Y,
				vector.X, vector.Y))
	}

	canvas.End()

	return canvas
}
<|endoftext|>


<commit_before>package oauth

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"
)

type Consumer struct {
	Key              string
	Secret           string
	Service          string
	RequestTokenURL  string
	AccessTokenURL   string
	AuthorizationURL string
	CallbackURL      string
}

func (c *Consumer) defaultParameters() url.Values {
	values := make(url.Values)
	values.Add("oauth_version", "1.0")
	values.Add("oauth_timestamp", strconv.FormatInt(time.Now().Unix(), 10))
	values.Add("oauth_consumer_key", c.Key)
	values.Add("oauth_nonce", strconv.FormatInt(rand.Int63(), 10))
	values.Add("oauth_signature_method", "HMAC-SHA1")
	return values
}

func (c *Consumer) sign(method string, url string, values url.Values, secret string) string {
	base := fmt.Sprintf("%s&%s&%s", method, encode(url), encodePlusEncoded(values.Encode()))
	key := encode(c.Secret) + "&" + encode(secret)
	return c.digest(key, base)
}

// digest generates a HMAC-SHA1 signature with the given key and data.
func (c *Consumer) digest(key string, data string) string {
	h := hmac.New(sha1.New, []byte(key))
	// TODO: Check for errors here?
	io.WriteString(h, data)
	return base64.StdEncoding.EncodeToString(h.Sum(nil))
}

func (c *Consumer) headers(method string, url string, values url.Values, secret string) map[string]string {
	signature := c.sign(method, url, values, secret)
	var headers []string
	for k, v := range values {
		if strings.HasPrefix(k, "oauth") {
			headers = append(headers, encodeQuoted(k, v[0]))
			values.Del(k)
		}
	}
	headers = append(headers, encodeQuoted("oauth_signature", signature))
	sort.Strings(headers)
	return map[string]string{
		"Authorization": "OAuth " + strings.Join(headers, ", "),
	}
}

// Authorization requests a Request Token and returns the URL the user should
// visit to authorize it as well as the token, which needs to be used later
// for exchanging it for an Access Token.
func (c *Consumer) Authorization() (string, *Token, error) {
	values := c.defaultParameters()
	values.Add("oauth_callback", c.CallbackURL)
	headers := c.headers("GET", c.RequestTokenURL, values, "")
	resp, err := sendReq("GET", c.RequestTokenURL, headers, nil)
	if err != nil {
		return "", nil, err
	}
	token, err := parseToken(resp)
	if err != nil {
		return "", nil, err
	}
	return c.AuthorizationURL + "?oauth_token=" + token.Key, token, nil
}

// Exchange exchanges a Request Token for an Access Token using the given
// verifier. The verifier is sent by the provider to the consumer at the
// callback URL. If the provider you're using doesn't require a verifier, just
// pass an empty string.
func (c *Consumer) Exchange(token *Token, verifier string) (*Token, error) {
	p := c.defaultParameters()
	p.Add("oauth_token", token.Key)
	if verifier != "" {
		p.Add("oauth_verifier", verifier)
	}
	headers := c.headers("POST", c.AccessTokenURL, p, token.Secret)
	resp, err := sendReq("POST", c.AccessTokenURL, headers, nil)
	if err != nil {
		return nil, err
	}
	return parseToken(resp)
}

// Get performs a GET request to the given URL with the given values and
// signed with the consumer and the given token (if any). The url parameter
// can't contain a query string. All url parameters should be passed using
// the values parameter.
func (c *Consumer) Get(url string, values url.Values, token *Token) (*http.Response, error) {
	return c.SendRequest("GET", url, values, token)
}

// Post performs a POST request to the given URL with the given values and
// signed with the given token (if any).
func (c *Consumer) Post(url string, values url.Values, token *Token) (*http.Response, error) {
	return c.SendRequest("POST", url, values, token)
}

// Request returns a *http.Request with the given method, url and values, which is already
// signed using the given token (if any).
func (c *Consumer) Request(method string, url string, values url.Values, token *Token) (*http.Request, error) {
	vals := c.defaultParameters()
	for k, v := range values {
		vals[k] = append(vals[k], v...)
	}
	vals.Add("oauth_token", token.Key)
	var secret string
	if token != nil {
		secret = token.Secret
	}
	headers := c.headers(method, url, vals, secret)
	return req(method, url, headers, vals)
}

// SendRequest works like Requests, but it also sends the request and
// returns an *http.Response.
func (c *Consumer) SendRequest(method string, url string, values url.Values, token *Token) (*http.Response, error) {
	r, err := c.Request(method, url, values, token)
	if err != nil {
		return nil, err
	}
	return client.Do(r)
}
<commit_msg>Use POST for the request_token step<commit_after>package oauth

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"
)

type Consumer struct {
	Key              string
	Secret           string
	Service          string
	RequestTokenURL  string
	AccessTokenURL   string
	AuthorizationURL string
	CallbackURL      string
}

func (c *Consumer) defaultParameters() url.Values {
	values := make(url.Values)
	values.Add("oauth_version", "1.0")
	values.Add("oauth_timestamp", strconv.FormatInt(time.Now().Unix(), 10))
	values.Add("oauth_consumer_key", c.Key)
	values.Add("oauth_nonce", strconv.FormatInt(rand.Int63(), 10))
	values.Add("oauth_signature_method", "HMAC-SHA1")
	return values
}

func (c *Consumer) sign(method string, url string, values url.Values, secret string) string {
	base := fmt.Sprintf("%s&%s&%s", method, encode(url), encodePlusEncoded(values.Encode()))
	key := encode(c.Secret) + "&" + encode(secret)
	return c.digest(key, base)
}

// digest generates a HMAC-SHA1 signature with the given key and data.
func (c *Consumer) digest(key string, data string) string {
	h := hmac.New(sha1.New, []byte(key))
	// TODO: Check for errors here?
	io.WriteString(h, data)
	return base64.StdEncoding.EncodeToString(h.Sum(nil))
}

func (c *Consumer) headers(method string, url string, values url.Values, secret string) map[string]string {
	signature := c.sign(method, url, values, secret)
	var headers []string
	for k, v := range values {
		if strings.HasPrefix(k, "oauth") {
			headers = append(headers, encodeQuoted(k, v[0]))
			values.Del(k)
		}
	}
	headers = append(headers, encodeQuoted("oauth_signature", signature))
	sort.Strings(headers)
	return map[string]string{
		"Authorization": "OAuth " + strings.Join(headers, ", "),
	}
}

// Authorization requests a Request Token and returns the URL the user should
// visit to authorize it as well as the token, which needs to be used later
// for exchanging it for an Access Token.
func (c *Consumer) Authorization() (string, *Token, error) {
	values := c.defaultParameters()
	values.Add("oauth_callback", c.CallbackURL)
	headers := c.headers("POST", c.RequestTokenURL, values, "")
	fmt.Println(headers, c.RequestTokenURL)
	resp, err := sendReq("POST", c.RequestTokenURL, headers, values)
	if err != nil {
		return "", nil, err
	}
	token, err := parseToken(resp)
	if err != nil {
		return "", nil, err
	}
	return c.AuthorizationURL + "?oauth_token=" + token.Key, token, nil
}

// Exchange exchanges a Request Token for an Access Token using the given
// verifier. The verifier is sent by the provider to the consumer at the
// callback URL. If the provider you're using doesn't require a verifier, just
// pass an empty string.
func (c *Consumer) Exchange(token *Token, verifier string) (*Token, error) {
	p := c.defaultParameters()
	p.Add("oauth_token", token.Key)
	if verifier != "" {
		p.Add("oauth_verifier", verifier)
	}
	headers := c.headers("POST", c.AccessTokenURL, p, token.Secret)
	resp, err := sendReq("POST", c.AccessTokenURL, headers, nil)
	if err != nil {
		return nil, err
	}
	return parseToken(resp)
}

// Get performs a GET request to the given URL with the given values and
// signed with the consumer and the given token (if any). The url parameter
// can't contain a query string. All url parameters should be passed using
// the values parameter.
func (c *Consumer) Get(url string, values url.Values, token *Token) (*http.Response, error) {
	return c.SendRequest("GET", url, values, token)
}

// Post performs a POST request to the given URL with the given values and
// signed with the given token (if any).
func (c *Consumer) Post(url string, values url.Values, token *Token) (*http.Response, error) {
	return c.SendRequest("POST", url, values, token)
}

// Request returns a *http.Request with the given method, url and values, which is already
// signed using the given token (if any).
func (c *Consumer) Request(method string, url string, values url.Values, token *Token) (*http.Request, error) {
	vals := c.defaultParameters()
	for k, v := range values {
		vals[k] = append(vals[k], v...)
	}
	vals.Add("oauth_token", token.Key)
	var secret string
	if token != nil {
		secret = token.Secret
	}
	headers := c.headers(method, url, vals, secret)
	return req(method, url, headers, vals)
}

// SendRequest works like Requests, but it also sends the request and
// returns an *http.Response.
func (c *Consumer) SendRequest(method string, url string, values url.Values, token *Token) (*http.Response, error) {
	r, err := c.Request(method, url, values, token)
	if err != nil {
		return nil, err
	}
	return client.Do(r)
}
<|endoftext|>


<commit_before>package util

import (
	"fmt"
	"reflect"
)

const (
	INDENT_WIDTH = 2
)

func PrettySprintf(name string, data interface{}) string {
	return prettySprintf(name, reflect.ValueOf(data), 0)
}

func prettySprintf(name string, v reflect.Value, level int) (output string) {
	var indent, indentFormat string

	if !v.CanInterface() {
		return
	}
	// output = indent + name + ": \n"

	// Contstruct the indentation string, first the format...
	indentFormat = fmt.Sprintf("%%%ds", INDENT_WIDTH * level)
	// ... then the string itself
	indent = fmt.Sprintf(indentFormat, "")

	switch v.Kind() {
	case reflect.Struct:
		for i := 0; i < v.NumField(); i++ {
			f := v.Field(i)
			t := v.Type().Field(i)
			fieldName := t.Tag.Get("display")
			if fieldName == "" {
				fieldName = t.Name
			}
			output = output + prettySprintf(fieldName, f, level+1)
		}
	case reflect.Slice:
		output = indent + name + ": [\n"
		for j := 0; j < v.Len(); j++ {
			indexName := fmt.Sprintf("%d", j)
			output = output + prettySprintf(indexName, v.Index(j), level+1)
		}
		output = output + indent + "]\n"
	case reflect.Ptr:
		if v.IsNil() {
			// output = indent + fmt.Sprintf("%s is empty\n", name)
		} else {
			output = prettySprintf(name, v.Elem(), level)
		}
	case reflect.Map:
		output = indent + name + ": [\n"
		for _, k := range v.MapKeys() {
			keyName := fmt.Sprintf("%v", k.Interface())
			output = output + prettySprintf(keyName, v.MapIndex(k), level+1)
		}
		output = output + indent + "]\n"
	default:
		namePrefix := ""
		if name != "" {
			namePrefix = name + ": "
		}
		output = indent + fmt.Sprintf("%s%v\n", namePrefix, v.Interface())
	}
	return
}
<commit_msg>util prettyprint: supress printing colons when name is empty for slices and maps<commit_after>package util

import (
	"fmt"
	"reflect"
)

const (
	INDENT_WIDTH = 2
)

func PrettySprintf(name string, data interface{}) string {
	return prettySprintf(name, reflect.ValueOf(data), 0)
}

func prettySprintf(name string, v reflect.Value, level int) (output string) {
	var indent, indentFormat string

	if !v.CanInterface() {
		return
	}
	// output = indent + name + ": \n"

	// Contstruct the indentation string, first the format...
	indentFormat = fmt.Sprintf("%%%ds", INDENT_WIDTH * level)
	// ... then the string itself
	indent = fmt.Sprintf(indentFormat, "")

	namePrefix := ""
	if name != "" {
		namePrefix = name + ": "
	}

	switch v.Kind() {
	case reflect.Struct:
		for i := 0; i < v.NumField(); i++ {
			f := v.Field(i)
			t := v.Type().Field(i)
			fieldName := t.Tag.Get("display")
			if fieldName == "" {
				fieldName = t.Name
			}
			output = output + prettySprintf(fieldName, f, level+1)
		}
	case reflect.Slice:
		output = indent + namePrefix + "[\n"
		for j := 0; j < v.Len(); j++ {
			indexName := fmt.Sprintf("%d", j)
			output = output + prettySprintf(indexName, v.Index(j), level+1)
		}
		output = output + indent + "]\n"
	case reflect.Ptr:
		if v.IsNil() {
			// output = indent + fmt.Sprintf("%s is empty\n", name)
		} else {
			output = prettySprintf(name, v.Elem(), level)
		}
	case reflect.Map:
		output = indent + namePrefix + "[\n"
		for _, k := range v.MapKeys() {
			keyName := fmt.Sprintf("%v", k.Interface())
			output = output + prettySprintf(keyName, v.MapIndex(k), level+1)
		}
		output = output + indent + "]\n"
	default:
		output = indent + fmt.Sprintf("%s%v\n", namePrefix, v.Interface())
	}
	return
}
<|endoftext|>


<commit_before><commit_msg>relapse now has all the neccesary functions in a top level package<commit_after><|endoftext|>


<commit_before><commit_msg>Fix panic in asset maker<commit_after><|endoftext|>


<commit_before>package fs

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"syscall"
	"unicode"
)

func isDigits(s string) bool {
	for _, c := range s {
		if !unicode.IsDigit(c) {
			return false
		}
	}
	return true
}

// GetInode returns Inode for file
func GetInode(file string) (uint64, error) {
	fileinfo, err := os.Stat(file)
	if err != nil {
		return 0, fmt.Errorf("error stat file: %+v", err)
	}
	stat, ok := fileinfo.Sys().(*syscall.Stat_t)
	if !ok {
		return 0, fmt.Errorf("not a stat_t")
	}
	return stat.Ino, nil
}

// FindFileInProc Traverse /proc/<pid>/<suffix> files,
// compare their inodes with inode parameter and returns file if inode matches
// use FindProcInode(xxx, "/ns/net") for example
func FindFileInProc(inode uint64, suffix string) (string, error) {
	files, err := ioutil.ReadDir("/proc")
	if err != nil {
		return "", fmt.Errorf("can't read /proc directory: %+v", err)
	}

	for _, f := range files {
		name := f.Name()
		if isDigits(name) {
			filename := "/proc/" + name + suffix
			tryInode, err := GetInode(filename)
			if err != nil {
				return "", err
			}
			if tryInode == inode {
				return filename, nil
			}
		}
	}

	return "", fmt.Errorf("not found")
}

func GetAllNetNs() ([]uint64, error) {
	files, err := ioutil.ReadDir("/proc")
	if err != nil {
		return nil, fmt.Errorf("can't read /proc directory: %+v", err)
	}
	inodes := make([]uint64, 0, len(files))
	for _, f := range files {
		name := f.Name()
		if isDigits(name) {
			filename := path.Join("/proc", name, "/ns/net")
			inode, err := GetInode(filename)
			if err != nil {
				continue
			}
			inodes = append(inodes, inode)
		}
	}
	return inodes, nil
}
<commit_msg>Check all /proc/* locations (#658)<commit_after>package fs

import (
	"fmt"
	"github.com/sirupsen/logrus"
	"io/ioutil"
	"os"
	"path"
	"syscall"
	"unicode"
)

func isDigits(s string) bool {
	for _, c := range s {
		if !unicode.IsDigit(c) {
			return false
		}
	}
	return true
}

// GetInode returns Inode for file
func GetInode(file string) (uint64, error) {
	fileinfo, err := os.Stat(file)
	if err != nil {
		return 0, fmt.Errorf("error stat file: %+v", err)
	}
	stat, ok := fileinfo.Sys().(*syscall.Stat_t)
	if !ok {
		return 0, fmt.Errorf("not a stat_t")
	}
	return stat.Ino, nil
}

// FindFileInProc Traverse /proc/<pid>/<suffix> files,
// compare their inodes with inode parameter and returns file if inode matches
// use FindProcInode(xxx, "/ns/net") for example
func FindFileInProc(inode uint64, suffix string) (string, error) {
	files, err := ioutil.ReadDir("/proc")
	if err != nil {
		return "", fmt.Errorf("can't read /proc directory: %+v", err)
	}

	for _, f := range files {
		name := f.Name()
		if isDigits(name) {
			filename := "/proc/" + name + suffix
			tryInode, err := GetInode(filename)
			if err != nil {
				// Just report into log, do not exit
				logrus.Errorf("Can't find %s Error: %v", filename, err)
				continue
			}
			if tryInode == inode {
				return filename, nil
			}
		}
	}

	return "", fmt.Errorf("not found")
}

func GetAllNetNs() ([]uint64, error) {
	files, err := ioutil.ReadDir("/proc")
	if err != nil {
		return nil, fmt.Errorf("can't read /proc directory: %+v", err)
	}
	inodes := make([]uint64, 0, len(files))
	for _, f := range files {
		name := f.Name()
		if isDigits(name) {
			filename := path.Join("/proc", name, "/ns/net")
			inode, err := GetInode(filename)
			if err != nil {
				continue
			}
			inodes = append(inodes, inode)
		}
	}
	return inodes, nil
}
<|endoftext|>


<commit_before>package msds

import (
	"bufio"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"
)

const sentinelString = "****SENTINEL-STRING****"

// ChannelBus a struct to hold all channels used by the different go routines
type ChannelBus struct {
	Finished    chan bool
	Log         chan string
	CurrentLine chan string
	TableName   chan string
	TableScheme chan string
	TableData   chan string
}

func isGzip(b *bufio.Reader) bool {
	if m, err := b.Peek(2); err != nil && m[0] != 0x1f && m[1] != 0x8b {
		return false
	}
	return true
}

func openReader(f *os.File) *bufio.Reader {
	pageSize := os.Getpagesize() * 2
	buf := bufio.NewReaderSize(f, pageSize)
	if isGzip(buf) {
		gbuf, _ := gzip.NewReader(buf)
		return bufio.NewReaderSize(gbuf, pageSize)
	}
	return buf
}

// LineReader reads `file` line-by-line and adds it to the `bus.CurrentLine` channel.
// Note: This function closes `file`.
func LineReader(file *os.File, bus ChannelBus) {
	r := openReader(file)
	for line, err := r.ReadString('\n'); err == nil; line, err = r.ReadString('\n') {
		bus.CurrentLine <- line
	}
	file.Close()
	close(bus.CurrentLine)
}

// LineParser reads the CurrentLine and figures out which channel to put it in.
func LineParser(bus ChannelBus, combineFiles bool) {
	onTableScheme, onTableData, pastHeader := false, false, false
	headerMetaData := fmt.Sprintf("-- Generated with mysqldumpsplit on %s\n\n", time.Now())
	for line := range bus.CurrentLine {
		// The beginning of a mysqldump has some flags at the top of the file. Capture them into a variable.
		if !pastHeader && strings.Contains(line, "/*!40") {
			headerMetaData += line
		}

		if strings.Contains(line, "Table structure for table") {
			onTableScheme, onTableData = true, false
			tableName := strings.Replace(line, "-- Table structure for table ", "", 1)
			bus.TableName <- strings.TrimSpace(strings.Replace(tableName, "`", "", -1))
			// add headers to each file unless we are combining all of them into 1 file.
			if !combineFiles {
				bus.TableScheme <- headerMetaData
			} else if !pastHeader {
				// add the meta data to only the first table.
				bus.TableScheme <- headerMetaData
			}

			pastHeader = true
			bus.TableScheme <- "\n--\n" + line
		} else if strings.Contains(line, "LOCK TABLES `") {
			onTableData, onTableScheme = true, false
			bus.TableData <- line
		} else {
			if onTableScheme {
				bus.TableScheme <- line
			}
			if onTableData {
				bus.TableData <- line
			}

			if strings.Contains(line, "-- Dumping data for table") {
				onTableScheme = false
				bus.TableScheme <- "--\n"
				bus.TableScheme <- sentinelString
			} else if strings.Contains(line, "UNLOCK TABLES;") {
				onTableData = false
				bus.TableData <- sentinelString
			}
		}
	}

	close(bus.TableName)
	close(bus.TableData)
	close(bus.TableScheme)
}

// Writer writes the data from the different channels to different files.
func Writer(outputDir string, skipData []string, skipTable []string, bus ChannelBus) {
	os.Mkdir(outputDir, os.ModePerm)
	numTables := 0

	for tableName := range bus.TableName {
		if StringInArray(tableName, &skipTable) {
			bus.Log <- fmt.Sprintf("skipping table: %s\n", tableName)
			for tableData := range bus.TableScheme {
				if tableData == sentinelString {
					break
				}
			}
			for tableData := range bus.TableData {
				if tableData == sentinelString {
					break
				}
			}

			continue
		}

		bus.Log <- fmt.Sprintf("extracting table: %s\n", tableName)
		numTables++
		tablePath := filepath.Join(outputDir, tableName+".sql")
		tableFile, _ := os.Create(tablePath)

		for tableData := range bus.TableScheme {
			if tableData == sentinelString {
				break
			}
			tableFile.WriteString(tableData)
		}

		skipTableData := StringInArray(tableName, &skipData)

		for tableData := range bus.TableData {
			if tableData == sentinelString {
				break
			}

			if !skipTableData {
				tableFile.WriteString(tableData)
			}
		}
		tableFile.Close()
	}
	bus.Log <- fmt.Sprintf("extracted %d tables\n", numTables)
	bus.Finished <- true
}

// CombineFiles combines all files ina directory into a single file
func CombineFiles(filePath, outputDir string, bus ChannelBus) {
	combineFile, _ := os.Create(filePath)
	cleanUpOutputDir := true

	files, _ := ioutil.ReadDir(outputDir)
	bus.Log <- fmt.Sprintf("Combining all %d files into %s\n", len(files), filePath)

	for _, file := range files {
		fullPath := path.Join(outputDir, file.Name())
		if combineFile.Name() == fullPath {
			cleanUpOutputDir = false
			continue
		}

		sqlFile, _ := OpenFile(filepath.Join(outputDir, file.Name()))
		r := bufio.NewReader(sqlFile)

		for line, err := r.ReadString('\n'); err == nil; line, err = r.ReadString('\n') {
			combineFile.WriteString(line)
		}
		// write a newline between each file
		combineFile.WriteString("\n")
		// close then delete the table file
		fileName := sqlFile.Name()
		sqlFile.Close()
		os.Remove(fileName)
	}

	info, _ := combineFile.Stat()
	bus.Log <- fmt.Sprintf("New file size %s\n", StringifyFileSize(info.Size()))
	combineFile.Close()

	if cleanUpOutputDir {
		bus.Log <- fmt.Sprintf("Deleting output directory")
		os.RemoveAll(outputDir)
	}
}

// Logger reads messages from `bus.Log` and outputs them to the logger.
func Logger(bus ChannelBus) {
	for msg := range bus.Log {
		if msg != "" {
			log.Output(3, msg)
		}
	}
}
<commit_msg>DRY up Writer code<commit_after>package msds

import (
	"bufio"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"
)

const sentinelString = "****SENTINEL-STRING****"

// ChannelBus a struct to hold all channels used by the different go routines
type ChannelBus struct {
	Finished    chan bool
	Log         chan string
	CurrentLine chan string
	TableName   chan string
	TableScheme chan string
	TableData   chan string
}

func isGzip(b *bufio.Reader) bool {
	if m, err := b.Peek(2); err != nil && m[0] != 0x1f && m[1] != 0x8b {
		return false
	}
	return true
}

func openReader(f *os.File) *bufio.Reader {
	pageSize := os.Getpagesize() * 2
	buf := bufio.NewReaderSize(f, pageSize)
	if isGzip(buf) {
		gbuf, _ := gzip.NewReader(buf)
		return bufio.NewReaderSize(gbuf, pageSize)
	}
	return buf
}

// LineReader reads `file` line-by-line and adds it to the `bus.CurrentLine` channel.
// Note: This function closes `file`.
func LineReader(file *os.File, bus ChannelBus) {
	r := openReader(file)
	for line, err := r.ReadString('\n'); err == nil; line, err = r.ReadString('\n') {
		bus.CurrentLine <- line
	}
	file.Close()
	close(bus.CurrentLine)
}

// LineParser reads the CurrentLine and figures out which channel to put it in.
func LineParser(bus ChannelBus, combineFiles bool) {
	onTableScheme, onTableData, pastHeader := false, false, false
	headerMetaData := fmt.Sprintf("-- Generated with mysqldumpsplit on %s\n\n", time.Now())
	for line := range bus.CurrentLine {
		// The beginning of a mysqldump has some flags at the top of the file. Capture them into a variable.
		if !pastHeader && strings.Contains(line, "/*!40") {
			headerMetaData += line
		}

		if strings.Contains(line, "Table structure for table") {
			onTableScheme, onTableData = true, false
			tableName := strings.Replace(line, "-- Table structure for table ", "", 1)
			bus.TableName <- strings.TrimSpace(strings.Replace(tableName, "`", "", -1))
			// add headers to each file unless we are combining all of them into 1 file.
			if !combineFiles {
				bus.TableScheme <- headerMetaData
			} else if !pastHeader {
				// add the meta data to only the first table.
				bus.TableScheme <- headerMetaData
			}

			pastHeader = true
			bus.TableScheme <- "\n--\n" + line
		} else if strings.Contains(line, "LOCK TABLES `") {
			onTableData, onTableScheme = true, false
			bus.TableData <- line
		} else {
			if onTableScheme {
				bus.TableScheme <- line
			}
			if onTableData {
				bus.TableData <- line
			}

			if strings.Contains(line, "-- Dumping data for table") {
				onTableScheme = false
				bus.TableScheme <- "--\n"
				bus.TableScheme <- sentinelString
			} else if strings.Contains(line, "UNLOCK TABLES;") {
				onTableData = false
				bus.TableData <- sentinelString
			}
		}
	}

	close(bus.TableName)
	close(bus.TableData)
	close(bus.TableScheme)
}

// Writer writes the data from the different channels to different files.
func Writer(outputDir string, skipData []string, skipTables []string, bus ChannelBus) {
	os.Mkdir(outputDir, os.ModePerm)
	numTables := 0

	for tableName := range bus.TableName {
		var skipTableData bool
		skipTable := StringInArray(tableName, &skipTables)

		if skipTable {
			// also skip the data for the table.
			skipTableData = true
			bus.Log <- fmt.Sprintf("skipping table: %s\n", tableName)
		} else {
			bus.Log <- fmt.Sprintf("extracting table: %s\n", tableName)
			numTables++
			skipTableData = StringInArray(tableName, &skipData)
		}

		// to keep the code somewhat DRY, we create the sql file even if skipping the table.
		// we then delete it at the end of the loop.
		tablePath := filepath.Join(outputDir, tableName+".sql")
		tableFile, _ := os.Create(tablePath)

		for tableData := range bus.TableScheme {
			if tableData == sentinelString {
				break
			}

			if !skipTable {
				tableFile.WriteString(tableData)
			}
		}

		if skipTableData && !skipTable {
			// not skipping table but skipping data
			bus.Log <- fmt.Sprintf("skipping data for table: %s\n", tableName)
		}

		for tableData := range bus.TableData {
			if tableData == sentinelString {
				break
			}

			if !skipTableData {
				tableFile.WriteString(tableData)
			}
		}

		tableFile.Close()

		if skipTable {
			os.Remove(tablePath)
		}
	}
	bus.Log <- fmt.Sprintf("extracted %d tables\n", numTables)
	bus.Finished <- true
}

// CombineFiles combines all files ina directory into a single file
func CombineFiles(filePath, outputDir string, bus ChannelBus) {
	combineFile, _ := os.Create(filePath)
	cleanUpOutputDir := true

	files, _ := ioutil.ReadDir(outputDir)
	bus.Log <- fmt.Sprintf("Combining all %d files into %s\n", len(files), filePath)

	for _, file := range files {
		fullPath := path.Join(outputDir, file.Name())
		if combineFile.Name() == fullPath {
			cleanUpOutputDir = false
			continue
		}

		sqlFile, _ := OpenFile(filepath.Join(outputDir, file.Name()))
		r := bufio.NewReader(sqlFile)

		for line, err := r.ReadString('\n'); err == nil; line, err = r.ReadString('\n') {
			combineFile.WriteString(line)
		}
		// write a newline between each file
		combineFile.WriteString("\n")
		// close then delete the table file
		fileName := sqlFile.Name()
		sqlFile.Close()
		os.Remove(fileName)
	}

	info, _ := combineFile.Stat()
	bus.Log <- fmt.Sprintf("New file size %s\n", StringifyFileSize(info.Size()))
	combineFile.Close()

	if cleanUpOutputDir {
		bus.Log <- fmt.Sprintf("Deleting output directory")
		os.RemoveAll(outputDir)
	}
}

// Logger reads messages from `bus.Log` and outputs them to the logger.
func Logger(bus ChannelBus) {
	for msg := range bus.Log {
		if msg != "" {
			log.Output(3, msg)
		}
	}
}
<|endoftext|>


<commit_before>package have

import (
	"bytes"
	"fmt"
	"regexp"
)

// CodeChunk can either be a string of smaller CodeChunks
// or its value can be a string.
// Think of it as of a union type.
// Every statement should have a separate CodeChunk.
// Statements that (directly) contain a block of code should
// be flagged (it will be used to insert code that "spills"
// beyond the current block of code).
type CodeChunk struct {
	chunks []*CodeChunk
	code   string

	// Indicates if this CodeChunk is composed of statements
	// (it means that other statements can be inserted between them,
	// and the result will be valid code)
	blockOfStmts bool

	// If this CodeChunk is a member of `chunks` of some other CodeChunk,
	// this will point to thas other CodeChunk (and will be nil otherwise)
	parent *CodeChunk

	indent string
}

// TODO: implement it in a io.Reader form, not keeping all results in memory
func (cc *CodeChunk) ReadAll() string {
	return cc.readAll("")
}

func (cc *CodeChunk) readAll(indent string) string {
	if cc == forcedIndentChunk {
		return indent
	}

	if cc.code != "" {
		return cc.code
	}

	buf := bytes.Buffer{}
	for _, chk := range cc.chunks {
		trailer := ""
		if cc.blockOfStmts {
			trailer = "\t"
			buf.WriteString(indent + trailer)
		}
		buf.WriteString(chk.readAll(indent + trailer))
	}
	return buf.String()
}

func (cc *CodeChunk) AddString(s string) {
	cc.chunks = append(cc.chunks, cc.NewStrChunk(s))
}

func (cc *CodeChunk) AddChunks(chunks ...*CodeChunk) {
	cc.chunks = append(cc.chunks, chunks...)
}

func (cc *CodeChunk) NewStrChunk(s string) *CodeChunk {
	ch := &CodeChunk{code: s, parent: cc}
	//if cc.blockOfStmts {
	//	ch.code = cc.indent + cc.code
	//}
	return ch
}

// Created a new empty chunk whose parent is the receiver.
func (cc *CodeChunk) NewChunk() *CodeChunk {
	ch := &CodeChunk{parent: cc, indent: cc.indent}
	//if cc.blockOfStmts {
	//	ch.AddString(cc.indent)
	//}
	cc.chunks = append(cc.chunks, ch)
	return ch
}

func (cc *CodeChunk) NewBlockChunk() *CodeChunk {
	ch := cc.NewChunk()
	ch.blockOfStmts = true
	return ch
}

type forcedIndent struct{}

func (f forcedIndent) Generate(current *CodeChunk) {
	current.AddChunks(forcedIndentChunk)
}

var splitter = regexp.MustCompile("%i?C")
var forcedIndentChunk = &CodeChunk{}
var ForcedIndent = &forcedIndent{}

// Format with fmt.Sprintf, but one addition: "%C" can be use to add Generables.
// Use "%iC" variant for InlineGenerables.
// Indents are inserted automatically, but for multi-line statements that need
// extra indents to be added in the middle, use the ForcedIndent generable.
func (cc *CodeChunk) AddChprintf(format string, a ...interface{}) {
	var nonGenerables []interface{}

	//format = strings.Replace(format, "%I", cc.indent, -1)

	ops := splitter.FindAllString(format, -1)
	parts := splitter.Split(format, -1)

	i := 0
	for _, arg := range a {
		if arg == nil {
			arg = EmptyGenerable{}
		}

		switch v := arg.(type) {
		case Generable:
			//generables = append(generables, v)
			cc.AddString(fmt.Sprintf(parts[i], nonGenerables...))
			nonGenerables = nil

			switch ops[i] {
			case "%iC":
				v.(InlineGenerable).InlineGenerate(cc, false)
			case "%C":
				v.Generate(cc)
			}

			i++
		default:
			nonGenerables = append(nonGenerables, v)
		}
	}

	// Leftovers
	cc.AddString(fmt.Sprintf(parts[i], nonGenerables...))
}

type Generable interface {
	// Generate the full version of the output code.
	Generate(current *CodeChunk)
}

type InlineGenerable interface {
	Generable
	// Generate a shorter version of the code that e.g. fits into for-loop header.
	InlineGenerate(current *CodeChunk, noParenth bool)
}

type EmptyGenerable struct{}

func (eg EmptyGenerable) Generate(current *CodeChunk) {}

func (id *Ident) Generate(current *CodeChunk) {
	current.AddString(id.name)
}

func (lit *BasicLit) Generate(current *CodeChunk) {
	val := ""
	switch lit.token.Type {
	case TOKEN_TRUE:
		current.AddString("true")
		return
	case TOKEN_FALSE:
		current.AddString("false")
		return
	case TOKEN_NUM:
		val = lit.token.Value.(string)
	case TOKEN_STR:
		val = fmt.Sprintf("\"%s\"", lit.token.Value.(string))
	default:
		panic("impossible")
	}

	// Don't generate type conversions if not necessary.
	//current.AddString(fmt.Sprintf("(%s)(%s)", lit.typ, val))
	current.AddString(val)
}

func (lit *CompoundLit) Generate(current *CodeChunk) {
	current.AddChprintf("%s{", lit.typ)

	switch lit.kind {
	case COMPOUND_LISTLIKE:
		current.AddChprintf("\n")
		ch := current.NewBlockChunk()

		for _, val := range lit.elems {
			ch.NewChunk().AddChprintf("%C,\n", val)
		}
	case COMPOUND_MAPLIKE:
		current.AddChprintf("\n")
		ch := current.NewBlockChunk()

		lit.eachKeyVal(func(key, val Expr) {
			ch.NewChunk().AddChprintf("%C: %C,\n", key, val)
		})
	case COMPOUND_EMPTY:
	default:
		panic("shouldn't ever happen, please report a bug")
	}

	current.AddChprintf("}")
}

func (op *UnaryOp) Generate(current *CodeChunk) {
	// TODO: Put the right operator in
	current.AddChprintf("(%s%C)", op.op.Value.(string), op.Right.(Generable))
}

func (op *BinaryOp) Generate(current *CodeChunk) {
	// TODO: Put the right operator in
	current.AddChprintf("(%C %s %C)", op.Left.(Generable), op.op.Value.(string), op.Right.(Generable))
}

func (vd *VarDecl) Generate(current *CodeChunk) {
	current = current.NewChunk()

	names := current.NewChunk()
	inits := current.NewChunk()

	i, count := 0, len(vd.Vars)
	vd.eachPair(func(v *Variable, init Expr) {
		names.AddChprintf("%s", v.name)
		inits.AddChprintf("(%s)(%C)", v.Type, init)
		if i+1 < count {
			names.AddChprintf(", ")
			inits.AddChprintf(", ")
		}
	})

	names.AddChprintf(" = ")
}

func (dc DeclChain) Generate(current *CodeChunk) {
	current = current.NewChunk()

	names := current.NewChunk()
	inits := current.NewChunk()

	i, count := 0, dc.countVars()
	dc.eachPair(func(v *Variable, init Expr) {
		names.AddChprintf("%s", v.name)
		inits.AddChprintf("(%s)(%C)", v.Type, init)
		if i+1 < count {
			names.AddChprintf(", ")
			inits.AddChprintf(", ")
		}
	})

	names.AddChprintf(" = ")
}

func (vs *VarStmt) Generate(current *CodeChunk) {
	if vs.IsFuncStmt {
		vs.Vars[0].Inits[0].(Generable).Generate(current)
		return
	}
	for _, vd := range vs.Vars {
		current.AddChprintf("var %C\n", vd)
	}
}

func (vs *VarStmt) InlineGenerate(current *CodeChunk, noParenth bool) {
	if vs == nil || len(vs.Vars) == 0 {
		return
	}

	i := 0
	vs.Vars.eachPair(func(vd *Variable, init Expr) {
		current.AddString(vd.name)
		if i+1 < len(vs.Vars) {
			current.AddString(", ")
		}
		i++
	})

	current.AddString(" := ")

	i = 0
	vs.Vars.eachPair(func(vd *Variable, init Expr) {
		current.AddChprintf("(%s)(%C)", vd.Type, init.(Generable))
		if i+1 < len(vs.Vars) {
			current.AddString(", ")
		}
		i++
	})
}

func (ds *DotSelector) Generate(current *CodeChunk) {
	current.AddChprintf("%C.%C", ds.Left, ds.Right)
}

func (as *AssignStmt) Generate(current *CodeChunk) {
	as.InlineGenerate(current, true)
	current.AddString("\n")
}

func (as *AssignStmt) InlineGenerate(current *CodeChunk, noParenth bool) {
	for i, v := range as.Lhs {
		v.(Generable).Generate(current)
		if i+1 < len(as.Lhs) {
			current.AddString(", ")
		}
	}

	current.AddChprintf(" %s ", as.Token.Value)

	for i, v := range as.Rhs {
		v.(Generable).Generate(current)
		if i+1 < len(as.Rhs) {
			current.AddString(", ")
		}
	}

}

func (fc *FuncCallExpr) Generate(current *CodeChunk) {
	current.AddChprintf("%C(", fc.Left.(Generable))
	for i, arg := range fc.Args {
		arg.(Generable).Generate(current)
		if i+1 < len(fc.Args) {
			current.AddString(", ")
		}
	}
	current.AddString(")")
}

func (fd *FuncDecl) Generate(current *CodeChunk) {
	current = current.NewChunk()
	if fd.Receiver == nil {
		current.AddChprintf("func %s(", fd.name)
	} else {
		current.AddChprintf("func (self %s) %s(", fd.Receiver.Type, fd.name)
	}

	i := 0

	fd.Args.eachPair(func(arg *Variable, init Expr) {
		current.AddChprintf("%s %s", arg.name, arg.Type)
		if i+1 < len(fd.Args) {
			current.AddString(", ")
		}
		i++
	})
	current.AddString(")")

	if len(fd.Results) > 0 {
		current.AddString(" (")
		i = 0
		fd.Args.eachPair(func(arg *Variable, init Expr) {
			current.AddChprintf("%s %s", arg.name, arg.Type)
			if i+1 < len(fd.Args) {
				current.AddString(", ")
			}
			i++
		})
		current.AddString(")")
	}

	current.AddString(" {\n")
	fd.Code.Generate(current)

	current.AddString("}\n")
}

func (bl *CodeBlock) Generate(current *CodeChunk) {
	block := current.NewBlockChunk()
	for _, stmt := range bl.Statements {
		stmt.(Generable).Generate(block.NewChunk())
	}
}

func (es *ExprStmt) Generate(current *CodeChunk) {
	current.AddChprintf("%C\n", es.Expression.(Generable))
}

func (es *ExprStmt) InlineGenerate(current *CodeChunk, noParenth bool) {
	es.Expression.(Generable).Generate(current)
}

func (fs *IfStmt) Generate(current *CodeChunk) {
	current = current.NewChunk()

	if fs.Branches[0].ScopedVarDecl != nil {
		current.AddChprintf("if %iC; %C {\n", fs.Branches[0].ScopedVarDecl, fs.Branches[0].Condition)
	} else {
		current.AddChprintf("if %C {\n", fs.Branches[0].Condition)
	}

	fs.Branches[0].Code.Generate(current)
	current.AddChprintf("%C}", ForcedIndent)

	for i, branch := range fs.Branches {
		if i == 0 {
			continue // It's already generated
		}

		if branch.Condition != nil {
			current.AddChprintf(" else if %C {\n", branch.Condition)
		} else {
			current.AddChprintf(" else {\n")
		}

		branch.Code.Generate(current)
		current.AddChprintf("%C}", ForcedIndent)
	}

	current.AddString("\n")
}

func (fs *ForStmt) Generate(current *CodeChunk) {
	current = current.NewChunk()

	// TODO: Handle `for` variants other than 3-way
	current.AddChprintf("for %iC; %C; %iC {\n%C%C}\n", fs.ScopedVarDecl, fs.Condition, fs.RepeatStmt, fs.Code, ForcedIndent)
}

func (f *File) Generate(current *CodeChunk) {
	current.AddChprintf("package %s\n\n", f.Pkg)
	for _, stmt := range f.Statements {
		stmt.(Generable).Generate(current)
	}
}

func (bs *BranchStmt) Generate(current *CodeChunk) {
	var typ = ""
	switch bs.Token.Type {
	case TOKEN_BREAK:
		typ = "break"
	case TOKEN_CONTINUE:
		typ = "continue"
	case TOKEN_GOTO:
		typ = "goto"
	default:
		panic("impossible")
	}
	if bs.Right == nil {
		current.AddChprintf("%s\n", typ)
	} else {
		current.AddChprintf("%s %C\n", typ, bs.Right)
	}
}

func (ls *LabelStmt) Generate(current *CodeChunk) {
	current.AddChprintf("%s:\n", ls.Name())
}

func (ss *StructStmt) Generate(current *CodeChunk) {
	current.AddChprintf("type %s struct {\n", ss.Struct.Name)

	ch := current.NewBlockChunk()
	for _, name := range ss.Struct.Keys {
		if _, ok := ss.Struct.Members[name]; !ok {
			// Not a plain member, but a method
			continue
		}
		ch.AddChprintf("%s %s\n", name, ss.Struct.Members[name])
	}

	current.AddChprintf("}\n\n")

	for _, name := range ss.Struct.Keys {
		if _, ok := ss.Struct.Methods[name]; !ok {
			// Not a method, a plain member
			continue
		}
		current.AddChprintf("%C\n", ss.Struct.Methods[name])
	}
}

// TODO: Now just write Generables for all statements/expressions and we're done...
<commit_msg>Minor refactoring<commit_after>package have

import (
	"bytes"
	"fmt"
	"regexp"
)

// CodeChunk can either be a string of smaller CodeChunks
// or its value can be a string.
// Think of it as of a union type.
// Every statement should have a separate CodeChunk.
// Statements that (directly) contain a block of code should
// be flagged (it will be used to insert code that "spills"
// beyond the current block of code).
type CodeChunk struct {
	chunks []*CodeChunk
	code   string

	// Indicates if this CodeChunk is composed of statements
	// (it means that other statements can be inserted between them,
	// and the result will be valid code)
	blockOfStmts bool

	// If this CodeChunk is a member of `chunks` of some other CodeChunk,
	// this will point to thas other CodeChunk (and will be nil otherwise)
	parent *CodeChunk

	indent string
}

// TODO: implement it in a io.Reader form, not keeping all results in memory
func (cc *CodeChunk) ReadAll() string {
	return cc.readAll("")
}

func (cc *CodeChunk) readAll(indent string) string {
	if cc == forcedIndentChunk {
		return indent
	}

	if cc.code != "" {
		return cc.code
	}

	buf := bytes.Buffer{}
	for _, chk := range cc.chunks {
		trailer := ""
		if cc.blockOfStmts {
			trailer = "\t"
			buf.WriteString(indent + trailer)
		}
		buf.WriteString(chk.readAll(indent + trailer))
	}
	return buf.String()
}

func (cc *CodeChunk) AddString(s string) {
	cc.chunks = append(cc.chunks, cc.NewStrChunk(s))
}

func (cc *CodeChunk) AddChunks(chunks ...*CodeChunk) {
	cc.chunks = append(cc.chunks, chunks...)
}

func (cc *CodeChunk) NewStrChunk(s string) *CodeChunk {
	ch := &CodeChunk{code: s, parent: cc}
	//if cc.blockOfStmts {
	//	ch.code = cc.indent + cc.code
	//}
	return ch
}

// Created a new empty chunk whose parent is the receiver.
func (cc *CodeChunk) NewChunk() *CodeChunk {
	ch := &CodeChunk{parent: cc, indent: cc.indent}
	//if cc.blockOfStmts {
	//	ch.AddString(cc.indent)
	//}
	cc.chunks = append(cc.chunks, ch)
	return ch
}

func (cc *CodeChunk) NewBlockChunk() *CodeChunk {
	ch := cc.NewChunk()
	ch.blockOfStmts = true
	return ch
}

type forcedIndent struct{}

func (f forcedIndent) Generate(current *CodeChunk) {
	current.AddChunks(forcedIndentChunk)
}

var splitter = regexp.MustCompile("%i?C")
var forcedIndentChunk = &CodeChunk{}
var ForcedIndent = &forcedIndent{}

// Format with fmt.Sprintf, but one addition: "%C" can be use to add Generables.
// Use "%iC" variant for InlineGenerables.
// Indents are inserted automatically, but for multi-line statements that need
// extra indents to be added in the middle, use the ForcedIndent generable.
func (cc *CodeChunk) AddChprintf(format string, a ...interface{}) {
	var nonGenerables []interface{}

	//format = strings.Replace(format, "%I", cc.indent, -1)

	ops := splitter.FindAllString(format, -1)
	parts := splitter.Split(format, -1)

	i := 0
	for _, arg := range a {
		if arg == nil {
			arg = EmptyGenerable{}
		}

		switch v := arg.(type) {
		case Generable:
			//generables = append(generables, v)
			cc.AddString(fmt.Sprintf(parts[i], nonGenerables...))
			nonGenerables = nil

			switch ops[i] {
			case "%iC":
				v.(InlineGenerable).InlineGenerate(cc, false)
			case "%C":
				v.Generate(cc)
			}

			i++
		default:
			nonGenerables = append(nonGenerables, v)
		}
	}

	// Leftovers
	cc.AddString(fmt.Sprintf(parts[i], nonGenerables...))
}

type Generable interface {
	// Generate the full version of the output code.
	Generate(current *CodeChunk)
}

type InlineGenerable interface {
	Generable
	// Generate a shorter version of the code that e.g. fits into for-loop header.
	InlineGenerate(current *CodeChunk, noParenth bool)
}

type EmptyGenerable struct{}

func (eg EmptyGenerable) Generate(current *CodeChunk) {}

func (id *Ident) Generate(current *CodeChunk) {
	current.AddString(id.name)
}

func (lit *BasicLit) Generate(current *CodeChunk) {
	val := ""
	switch lit.token.Type {
	case TOKEN_TRUE:
		current.AddString("true")
		return
	case TOKEN_FALSE:
		current.AddString("false")
		return
	case TOKEN_NUM:
		val = lit.token.Value.(string)
	case TOKEN_STR:
		val = fmt.Sprintf("\"%s\"", lit.token.Value.(string))
	default:
		panic("impossible")
	}

	// Don't generate type conversions if not necessary.
	//current.AddString(fmt.Sprintf("(%s)(%s)", lit.typ, val))
	current.AddString(val)
}

func (lit *CompoundLit) Generate(current *CodeChunk) {
	current.AddChprintf("%s{", lit.typ)

	if lit.kind == COMPOUND_EMPTY {
		current.AddChprintf("}")
		return
	}

	current.AddChprintf("\n")
	ch := current.NewBlockChunk()

	switch lit.kind {
	case COMPOUND_LISTLIKE:
		for _, val := range lit.elems {
			ch.NewChunk().AddChprintf("%C,\n", val)
		}
	case COMPOUND_MAPLIKE:
		lit.eachKeyVal(func(key, val Expr) {
			ch.NewChunk().AddChprintf("%C: %C,\n", key, val)
		})
	default:
		panic("shouldn't ever happen, please report a bug")
	}

	current.AddChprintf("%C}", ForcedIndent)
}

func (op *UnaryOp) Generate(current *CodeChunk) {
	// TODO: Put the right operator in
	current.AddChprintf("(%s%C)", op.op.Value.(string), op.Right.(Generable))
}

func (op *BinaryOp) Generate(current *CodeChunk) {
	// TODO: Put the right operator in
	current.AddChprintf("(%C %s %C)", op.Left.(Generable), op.op.Value.(string), op.Right.(Generable))
}

func (vd *VarDecl) Generate(current *CodeChunk) {
	current = current.NewChunk()

	names := current.NewChunk()
	inits := current.NewChunk()

	i, count := 0, len(vd.Vars)
	vd.eachPair(func(v *Variable, init Expr) {
		names.AddChprintf("%s", v.name)
		inits.AddChprintf("(%s)(%C)", v.Type, init)
		if i+1 < count {
			names.AddChprintf(", ")
			inits.AddChprintf(", ")
		}
	})

	names.AddChprintf(" = ")
}

func (dc DeclChain) Generate(current *CodeChunk) {
	current = current.NewChunk()

	names := current.NewChunk()
	inits := current.NewChunk()

	i, count := 0, dc.countVars()
	dc.eachPair(func(v *Variable, init Expr) {
		names.AddChprintf("%s", v.name)
		inits.AddChprintf("(%s)(%C)", v.Type, init)
		if i+1 < count {
			names.AddChprintf(", ")
			inits.AddChprintf(", ")
		}
	})

	names.AddChprintf(" = ")
}

func (vs *VarStmt) Generate(current *CodeChunk) {
	if vs.IsFuncStmt {
		vs.Vars[0].Inits[0].(Generable).Generate(current)
		return
	}
	for _, vd := range vs.Vars {
		current.AddChprintf("var %C\n", vd)
	}
}

func (vs *VarStmt) InlineGenerate(current *CodeChunk, noParenth bool) {
	if vs == nil || len(vs.Vars) == 0 {
		return
	}

	i := 0
	vs.Vars.eachPair(func(vd *Variable, init Expr) {
		current.AddString(vd.name)
		if i+1 < len(vs.Vars) {
			current.AddString(", ")
		}
		i++
	})

	current.AddString(" := ")

	i = 0
	vs.Vars.eachPair(func(vd *Variable, init Expr) {
		current.AddChprintf("(%s)(%C)", vd.Type, init.(Generable))
		if i+1 < len(vs.Vars) {
			current.AddString(", ")
		}
		i++
	})
}

func (ds *DotSelector) Generate(current *CodeChunk) {
	current.AddChprintf("%C.%C", ds.Left, ds.Right)
}

func (as *AssignStmt) Generate(current *CodeChunk) {
	as.InlineGenerate(current, true)
	current.AddString("\n")
}

func (as *AssignStmt) InlineGenerate(current *CodeChunk, noParenth bool) {
	for i, v := range as.Lhs {
		v.(Generable).Generate(current)
		if i+1 < len(as.Lhs) {
			current.AddString(", ")
		}
	}

	current.AddChprintf(" %s ", as.Token.Value)

	for i, v := range as.Rhs {
		v.(Generable).Generate(current)
		if i+1 < len(as.Rhs) {
			current.AddString(", ")
		}
	}

}

func (fc *FuncCallExpr) Generate(current *CodeChunk) {
	current.AddChprintf("%C(", fc.Left.(Generable))
	for i, arg := range fc.Args {
		arg.(Generable).Generate(current)
		if i+1 < len(fc.Args) {
			current.AddString(", ")
		}
	}
	current.AddString(")")
}

func (fd *FuncDecl) Generate(current *CodeChunk) {
	current = current.NewChunk()
	if fd.Receiver == nil {
		current.AddChprintf("func %s(", fd.name)
	} else {
		current.AddChprintf("func (self %s) %s(", fd.Receiver.Type, fd.name)
	}

	i := 0

	fd.Args.eachPair(func(arg *Variable, init Expr) {
		current.AddChprintf("%s %s", arg.name, arg.Type)
		if i+1 < len(fd.Args) {
			current.AddString(", ")
		}
		i++
	})
	current.AddString(")")

	if len(fd.Results) > 0 {
		current.AddString(" (")
		i = 0
		fd.Args.eachPair(func(arg *Variable, init Expr) {
			current.AddChprintf("%s %s", arg.name, arg.Type)
			if i+1 < len(fd.Args) {
				current.AddString(", ")
			}
			i++
		})
		current.AddString(")")
	}

	current.AddString(" {\n")
	fd.Code.Generate(current)

	current.AddString("}\n")
}

func (bl *CodeBlock) Generate(current *CodeChunk) {
	block := current.NewBlockChunk()
	for _, stmt := range bl.Statements {
		stmt.(Generable).Generate(block.NewChunk())
	}
}

func (es *ExprStmt) Generate(current *CodeChunk) {
	current.AddChprintf("%C\n", es.Expression.(Generable))
}

func (es *ExprStmt) InlineGenerate(current *CodeChunk, noParenth bool) {
	es.Expression.(Generable).Generate(current)
}

func (fs *IfStmt) Generate(current *CodeChunk) {
	current = current.NewChunk()

	if fs.Branches[0].ScopedVarDecl != nil {
		current.AddChprintf("if %iC; %C {\n", fs.Branches[0].ScopedVarDecl, fs.Branches[0].Condition)
	} else {
		current.AddChprintf("if %C {\n", fs.Branches[0].Condition)
	}

	fs.Branches[0].Code.Generate(current)
	current.AddChprintf("%C}", ForcedIndent)

	for i, branch := range fs.Branches {
		if i == 0 {
			continue // It's already generated
		}

		if branch.Condition != nil {
			current.AddChprintf(" else if %C {\n", branch.Condition)
		} else {
			current.AddChprintf(" else {\n")
		}

		branch.Code.Generate(current)
		current.AddChprintf("%C}", ForcedIndent)
	}

	current.AddString("\n")
}

func (fs *ForStmt) Generate(current *CodeChunk) {
	current = current.NewChunk()

	// TODO: Handle `for` variants other than 3-way
	current.AddChprintf("for %iC; %C; %iC {\n%C%C}\n", fs.ScopedVarDecl, fs.Condition, fs.RepeatStmt, fs.Code, ForcedIndent)
}

func (f *File) Generate(current *CodeChunk) {
	current.AddChprintf("package %s\n\n", f.Pkg)
	for _, stmt := range f.Statements {
		stmt.(Generable).Generate(current)
	}
}

func (bs *BranchStmt) Generate(current *CodeChunk) {
	var typ = ""
	switch bs.Token.Type {
	case TOKEN_BREAK:
		typ = "break"
	case TOKEN_CONTINUE:
		typ = "continue"
	case TOKEN_GOTO:
		typ = "goto"
	default:
		panic("impossible")
	}
	if bs.Right == nil {
		current.AddChprintf("%s\n", typ)
	} else {
		current.AddChprintf("%s %C\n", typ, bs.Right)
	}
}

func (ls *LabelStmt) Generate(current *CodeChunk) {
	current.AddChprintf("%s:\n", ls.Name())
}

func (ss *StructStmt) Generate(current *CodeChunk) {
	current.AddChprintf("type %s struct {\n", ss.Struct.Name)

	ch := current.NewBlockChunk()
	for _, name := range ss.Struct.Keys {
		if _, ok := ss.Struct.Members[name]; !ok {
			// Not a plain member, but a method
			continue
		}
		ch.AddChprintf("%s %s\n", name, ss.Struct.Members[name])
	}

	current.AddChprintf("%C}\n\n", ForcedIndent)

	for _, name := range ss.Struct.Keys {
		if _, ok := ss.Struct.Methods[name]; !ok {
			// Not a method, a plain member
			continue
		}
		current.AddChprintf("%C\n", ss.Struct.Methods[name])
	}
}

// TODO: Now just write Generables for all statements/expressions and we're done...
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/yvasiyarov/swagger/markup"
	"github.com/yvasiyarov/swagger/parser"
	"go/ast"
	"log"
	"os"
	"path"
	"regexp"
	"strings"
)

const (
	AVAILABLE_FORMATS = "go|swagger|asciidoc|markdown|confluence"
)

var apiPackage = flag.String("apiPackage", "", "The package that implements the API controllers, relative to $GOPATH/src")
var mainApiFile = flag.String("mainApiFile", "", "The file that contains the general API annotations, relative to $GOPATH/src")
var basePath = flag.String("basePath", "http://127.0.0.1:3000", "Web service base path")
var outputFormat = flag.String("format", "go", "Output format type for the generated files: "+AVAILABLE_FORMATS)
var outputSpec = flag.String("output", "", "Output (path) for the generated file(s)")
var controllerClass = flag.String("controllerClass", "", "Speed up parsing by specifying which receiver objects have the controller methods")

var generatedFileTemplate = `
package main
//This file is generated automatically. Do not try to edit it manually.

var resourceListingJson = {{resourceListing}}
var apiDescriptionsJson = {{apiDescriptions}}
`

// It must return true if funcDeclaration is controller. We will try to parse only comments before controllers
func IsController(funcDeclaration *ast.FuncDecl) bool {
	if len(*controllerClass) == 0 {
		// Search every method
		return true
	}
	if funcDeclaration.Recv != nil && len(funcDeclaration.Recv.List) > 0 {
		if starExpression, ok := funcDeclaration.Recv.List[0].Type.(*ast.StarExpr); ok {
			receiverName := fmt.Sprint(starExpression.X)
			matched, err := regexp.MatchString(string(*controllerClass), receiverName)
			if err != nil {
				log.Fatalf("The -controllerClass argument is not a valid regular expression: %v\n", err)
			}
			return matched
		}
	}
	return false
}

func generateSwaggerDocs(parser *parser.Parser) {
	fd, err := os.Create(path.Join("./", "docs.go"))
	if err != nil {
		log.Fatalf("Can not create document file: %v\n", err)
	}
	defer fd.Close()

	var apiDescriptions bytes.Buffer
	for apiKey, apiDescription := range parser.TopLevelApis {
		apiDescriptions.WriteString("\"" + apiKey + "\":")

		apiDescriptions.WriteString("`")
		json, err := json.MarshalIndent(apiDescription, "", "    ")
		if err != nil {
			log.Fatalf("Can not serialise []ApiDescription to JSON: %v\n", err)
		}
		apiDescriptions.Write(json)
		apiDescriptions.WriteString("`,")
	}

	doc := strings.Replace(generatedFileTemplate, "{{resourceListing}}", "`"+string(parser.GetResourceListingJson())+"`", -1)
	doc = strings.Replace(doc, "{{apiDescriptions}}", "map[string]string{"+apiDescriptions.String()+"}", -1)

	fd.WriteString(doc)
}

func generateSwaggerUiFiles(parser *parser.Parser) {
	fd, err := os.Create(path.Join(*outputSpec, "index.json"))
	if err != nil {
		log.Fatalf("Can not create the master index.json file: %v\n", err)
	}
	defer fd.Close()
	fd.WriteString(string(parser.GetResourceListingJson()))

	for apiKey, apiDescription := range parser.TopLevelApis {
		err = os.MkdirAll(path.Join(*outputSpec, apiKey), 0777)
		fd, err = os.Create(path.Join(*outputSpec, apiKey, "index.json"))
		if err != nil {
			log.Fatalf("Can not create the %s/index.json file: %v\n", apiKey, err)
		}
		defer fd.Close()
		json, err := json.MarshalIndent(apiDescription, "", "    ")
		if err != nil {
			log.Fatalf("Can not serialise []ApiDescription to JSON: %v\n", err)
		}
		fd.Write(json)
		log.Printf("Wrote %v/index.json", apiKey)
	}
}

func InitParser() *parser.Parser {
	parser := parser.NewParser()

	parser.BasePath = *basePath
	parser.IsController = IsController

	parser.TypesImplementingMarshalInterface["NullString"] = "string"
	parser.TypesImplementingMarshalInterface["NullInt64"] = "int"
	parser.TypesImplementingMarshalInterface["NullFloat64"] = "float"
	parser.TypesImplementingMarshalInterface["NullBool"] = "bool"

	return parser
}

func main() {
	flag.Parse()

	if *mainApiFile == "" {
		*mainApiFile = *apiPackage + "/main.go"
	}
	if *apiPackage == "" {
		flag.PrintDefaults()
		return
	}

	parser := InitParser()
	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		log.Fatalf("Please, set $GOPATH environment variable\n")
	}

	log.Println("Start parsing")
	parser.ParseGeneralApiInfo(path.Join(gopath, "src", *mainApiFile))
	parser.ParseApi(*apiPackage)
	log.Println("Finish parsing")

	format := strings.ToLower(*outputFormat)
	switch format {
	case "go":
		generateSwaggerDocs(parser)
		log.Println("Doc file generated")
	case "asciidoc":
		markup.GenerateMarkup(parser, new(markup.MarkupAsciiDoc), outputSpec, ".adoc")
		log.Println("AsciiDoc file generated")
	case "markdown":
		markup.GenerateMarkup(parser, new(markup.MarkupMarkDown), outputSpec, ".md")
		log.Println("MarkDown file generated")
	case "confluence":
		markup.GenerateMarkup(parser, new(markup.MarkupConfluence), outputSpec, ".confluence")
		log.Println("Confluence file generated")
	case "swagger":
		generateSwaggerUiFiles(parser)
		log.Println("Swagger UI files generated")
	default:
		log.Fatalf("Invalid -format specified. Must be one of %v.", AVAILABLE_FORMATS)
	}

}
<commit_msg>Output for go format<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/yvasiyarov/swagger/markup"
	"github.com/yvasiyarov/swagger/parser"
	"go/ast"
	"log"
	"os"
	"path"
	"regexp"
	"strings"
)

const (
	AVAILABLE_FORMATS = "go|swagger|asciidoc|markdown|confluence"
)

var apiPackage = flag.String("apiPackage", "", "The package that implements the API controllers, relative to $GOPATH/src")
var mainApiFile = flag.String("mainApiFile", "", "The file that contains the general API annotations, relative to $GOPATH/src")
var basePath = flag.String("basePath", "http://127.0.0.1:3000", "Web service base path")
var outputFormat = flag.String("format", "go", "Output format type for the generated files: "+AVAILABLE_FORMATS)
var outputSpec = flag.String("output", "", "Output (path) for the generated file(s)")
var controllerClass = flag.String("controllerClass", "", "Speed up parsing by specifying which receiver objects have the controller methods")

var generatedFileTemplate = `
package main
//This file is generated automatically. Do not try to edit it manually.

var resourceListingJson = {{resourceListing}}
var apiDescriptionsJson = {{apiDescriptions}}
`

// It must return true if funcDeclaration is controller. We will try to parse only comments before controllers
func IsController(funcDeclaration *ast.FuncDecl) bool {
	if len(*controllerClass) == 0 {
		// Search every method
		return true
	}
	if funcDeclaration.Recv != nil && len(funcDeclaration.Recv.List) > 0 {
		if starExpression, ok := funcDeclaration.Recv.List[0].Type.(*ast.StarExpr); ok {
			receiverName := fmt.Sprint(starExpression.X)
			matched, err := regexp.MatchString(string(*controllerClass), receiverName)
			if err != nil {
				log.Fatalf("The -controllerClass argument is not a valid regular expression: %v\n", err)
			}
			return matched
		}
	}
	return false
}

func generateSwaggerDocs(parser *parser.Parser) {
	fd, err := os.Create(path.Join(*outputSpec, "docs.go"))
	if err != nil {
		log.Fatalf("Can not create document file: %v\n", err)
	}
	defer fd.Close()

	var apiDescriptions bytes.Buffer
	for apiKey, apiDescription := range parser.TopLevelApis {
		apiDescriptions.WriteString("\"" + apiKey + "\":")

		apiDescriptions.WriteString("`")
		json, err := json.MarshalIndent(apiDescription, "", "    ")
		if err != nil {
			log.Fatalf("Can not serialise []ApiDescription to JSON: %v\n", err)
		}
		apiDescriptions.Write(json)
		apiDescriptions.WriteString("`,")
	}

	doc := strings.Replace(generatedFileTemplate, "{{resourceListing}}", "`"+string(parser.GetResourceListingJson())+"`", -1)
	doc = strings.Replace(doc, "{{apiDescriptions}}", "map[string]string{"+apiDescriptions.String()+"}", -1)

	fd.WriteString(doc)
}

func generateSwaggerUiFiles(parser *parser.Parser) {
	fd, err := os.Create(path.Join(*outputSpec, "index.json"))
	if err != nil {
		log.Fatalf("Can not create the master index.json file: %v\n", err)
	}
	defer fd.Close()
	fd.WriteString(string(parser.GetResourceListingJson()))

	for apiKey, apiDescription := range parser.TopLevelApis {
		err = os.MkdirAll(path.Join(*outputSpec, apiKey), 0777)
		fd, err = os.Create(path.Join(*outputSpec, apiKey, "index.json"))
		if err != nil {
			log.Fatalf("Can not create the %s/index.json file: %v\n", apiKey, err)
		}
		defer fd.Close()
		json, err := json.MarshalIndent(apiDescription, "", "    ")
		if err != nil {
			log.Fatalf("Can not serialise []ApiDescription to JSON: %v\n", err)
		}
		fd.Write(json)
		log.Printf("Wrote %v/index.json", apiKey)
	}
}

func InitParser() *parser.Parser {
	parser := parser.NewParser()

	parser.BasePath = *basePath
	parser.IsController = IsController

	parser.TypesImplementingMarshalInterface["NullString"] = "string"
	parser.TypesImplementingMarshalInterface["NullInt64"] = "int"
	parser.TypesImplementingMarshalInterface["NullFloat64"] = "float"
	parser.TypesImplementingMarshalInterface["NullBool"] = "bool"

	return parser
}

func main() {
	flag.Parse()

	if *mainApiFile == "" {
		*mainApiFile = *apiPackage + "/main.go"
	}
	if *apiPackage == "" {
		flag.PrintDefaults()
		return
	}

	parser := InitParser()
	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		log.Fatalf("Please, set $GOPATH environment variable\n")
	}

	log.Println("Start parsing")
	parser.ParseGeneralApiInfo(path.Join(gopath, "src", *mainApiFile))
	parser.ParseApi(*apiPackage)
	log.Println("Finish parsing")

	format := strings.ToLower(*outputFormat)
	switch format {
	case "go":
		generateSwaggerDocs(parser)
		log.Println("Doc file generated")
	case "asciidoc":
		markup.GenerateMarkup(parser, new(markup.MarkupAsciiDoc), outputSpec, ".adoc")
		log.Println("AsciiDoc file generated")
	case "markdown":
		markup.GenerateMarkup(parser, new(markup.MarkupMarkDown), outputSpec, ".md")
		log.Println("MarkDown file generated")
	case "confluence":
		markup.GenerateMarkup(parser, new(markup.MarkupConfluence), outputSpec, ".confluence")
		log.Println("Confluence file generated")
	case "swagger":
		generateSwaggerUiFiles(parser)
		log.Println("Swagger UI files generated")
	default:
		log.Fatalf("Invalid -format specified. Must be one of %v.", AVAILABLE_FORMATS)
	}

}
<|endoftext|>


<commit_before>package jwt_test

import (
	"encoding/json"
	"testing"
	"time"

	"github.com/lestrrat/go-jwx/jwt"
	"github.com/stretchr/testify/assert"
)

func TestClaimSet(t *testing.T) {
	c1 := jwt.NewClaimSet()
	err := c1.Set("jti", "AbCdEfG")
	if !assert.NoError(t, err) {
		return
	}
	err = c1.Set("sub", "foobar@example.com")
	if !assert.NoError(t, err) {
		return
	}

	// Silly fix to remove monotonic element from time.Time obatained
	// from time.Now(). Without this, the equality comparison goes
	// ga-ga for golang tip (1.9)
	now := time.Unix(time.Now().Unix(), 0)
	err = c1.Set("iat", now)
	if !assert.NoError(t, err) {
		return
	}
	err = c1.Set("nbf", now.Add(5*time.Second))
	if !assert.NoError(t, err) {
		return
	}
	err = c1.Set("exp", now.Add(10*time.Second))
	if !assert.NoError(t, err) {
		return
	}
	err = c1.Set("custom", "MyValue")
	if !assert.NoError(t, err) {
		return
	}

	jsonbuf1, err := json.MarshalIndent(c1, "", "  ")
	if !assert.NoError(t, err, "JSON marshal should succeed") {
		return
	}
	t.Logf("%s", jsonbuf1)

	c2 := jwt.NewClaimSet()
	if !assert.NoError(t, json.Unmarshal(jsonbuf1, c2), "JSON unmarshal should succeed") {
		return
	}

	jsonbuf2, err := json.MarshalIndent(c2, "", "  ")
	if !assert.NoError(t, err, "JSON marshal should succeed") {
		return
	}
	t.Logf("%s", jsonbuf2)

	if !assert.Equal(t, c1, c2, "Claim sets match") {
		return
	}
}

func TestGHIssue10_iss(t *testing.T) {
	c := jwt.NewClaimSet()
	c.Issuer = "github.com/lestrrat/go-jwx"

	// This should succeed, because WithIssuer is not provided in the
	// optinal parameters
	if !assert.NoError(t, c.Verify(), "claimset.Verify should succeed") {
		return
	}

	// This should succeed, because WithIssuer is provided with same value
	if !assert.NoError(t, c.Verify(jwt.WithIssuer(c.Issuer)), "claimset.Verify should succeed") {
		return
	}

	if !assert.Error(t, c.Verify(jwt.WithIssuer("poop")), "claimset.Verify should fail") {
		return
	}
}

func TestGHIssue10_aud(t *testing.T) {
	c := jwt.NewClaimSet()
	c.Audience = []string{
		"foo",
		"bar",
		"baz",
	}

	// This should succeed, because WithAudience is not provided in the
	// optinal parameters
	if !assert.NoError(t, c.Verify(), "claimset.Verify should succeed") {
		return
	}

	// This should succeed, because WithAudience is provided, and its
	// value matches one of the audience values
	if !assert.NoError(t, c.Verify(jwt.WithAudience("baz")), "claimset.Verify should succeed") {
		return
	}

	if !assert.Error(t, c.Verify(jwt.WithAudience("poop")), "claimset.Verify should fail") {
		return
	}
}

func TestGHIssue10_sub(t *testing.T) {
	c := jwt.NewClaimSet()
	c.Subject = "github.com/lestrrat/go-jwx"

	// This should succeed, because WithSubject is not provided in the
	// optinal parameters
	if !assert.NoError(t, c.Verify(), "claimset.Verify should succeed") {
		return
	}

	// This should succeed, because WithSubject is provided with same value
	if !assert.NoError(t, c.Verify(jwt.WithSubject(c.Subject)), "claimset.Verify should succeed") {
		return
	}

	if !assert.Error(t, c.Verify(jwt.WithSubject("poop")), "claimset.Verify should fail") {
		return
	}
}

func TestGHIssue10_nbf(t *testing.T) {
	c := jwt.NewClaimSet()

	// NotBefore is set to future date
	tm := time.Now().Add(72 * time.Hour)
	c.NotBefore = &jwt.NumericDate{tm}

	// This should fail, because nbf is the future
	if !assert.Error(t, c.Verify(), "claimset.Verify should fail") {
		return
	}

	// This should succeed, because we have given reaaaaaaly big skew
	// that is well enough to get us accepted
	if !assert.NoError(t, c.Verify(jwt.WithAcceptableSkew(73*time.Hour)), "claimset.Verify should succeed") {
		return
	}

	// This should succeed, because we have given a time
	// that is well enough into the future
	if !assert.NoError(t, c.Verify(jwt.WithClock(jwt.ClockFunc(func() time.Time { return tm.Add(time.Hour) }))), "claimset.Verify should succeed") {
		return
	}
}

func TestGHIssue10_exp(t *testing.T) {
	c := jwt.NewClaimSet()

	// issuedat = 1 Hr before current time
	tm := time.Now()
	c.IssuedAt = tm.Unix() - 3600

	// valid for 2 minutes only from IssuedAt
	c.Expiration = c.IssuedAt + 120

	// This should fail, because exp is set in the past
	if !assert.Error(t, c.Verify(), "claimset.Verify should fail") {
		return
	}

	// This should succeed, because we have given big skew
	// that is well enough to get us accepted
	if !assert.NoError(t, c.Verify(jwt.WithAcceptableSkew(time.Hour)), "claimset.Verify should succeed") {
		return
	}

	// This should succeed, because we have given a time
	// that is well enough into the past
	if !assert.NoError(t, c.Verify(jwt.WithClock(jwt.ClockFunc(func() time.Time { return tm.Add(-3500 * time.Second) }))), "claimset.Verify should succeed") {
		return
	}
}
<commit_msg>Fix tests<commit_after>package jwt_test

import (
	"encoding/json"
	"testing"
	"time"

	"github.com/lestrrat/go-jwx/jwt"
	"github.com/stretchr/testify/assert"
)

func TestClaimSet(t *testing.T) {
	c1 := jwt.NewClaimSet()
	if !assert.NoError(t, c1.Set("jti", "AbCdEfG"), "setting jti should work") {
		return
	}
	if !assert.NoError(t, c1.Set("sub", "foobar@example.com"), "setting sub should work") {
		return
	}

	// Silly fix to remove monotonic element from time.Time obatained
	// from time.Now(). Without this, the equality comparison goes
	// ga-ga for golang tip (1.9)
	now := time.Unix(time.Now().Unix(), 0)
	if !assert.NoError(t, c1.Set("iat", now.Unix()), "setting iat to now should work") {
		return
	}
	if !assert.NoError(t, c1.Set("nbf", now.Add(5*time.Second)), "setting nbf should work") {
		return
	}
	if !assert.NoError(t, c1.Set("exp", now.Add(10*time.Second).Unix()), "setting exp should work") {
		return
	}
	if !assert.NoError(t, c1.Set("custom", "MyValue"), "setting custom should work") {
		return
	}

	jsonbuf1, err := json.MarshalIndent(c1, "", "  ")
	if !assert.NoError(t, err, "JSON marshal should succeed") {
		return
	}
	t.Logf("%s", jsonbuf1)

	c2 := jwt.NewClaimSet()
	if !assert.NoError(t, json.Unmarshal(jsonbuf1, c2), "JSON unmarshal should succeed") {
		return
	}

	jsonbuf2, err := json.MarshalIndent(c2, "", "  ")
	if !assert.NoError(t, err, "JSON marshal should succeed") {
		return
	}
	t.Logf("%s", jsonbuf2)

	if !assert.Equal(t, c1, c2, "Claim sets match") {
		return
	}
}

func TestGHIssue10_iss(t *testing.T) {
	c := jwt.NewClaimSet()
	c.Issuer = "github.com/lestrrat/go-jwx"

	// This should succeed, because WithIssuer is not provided in the
	// optinal parameters
	if !assert.NoError(t, c.Verify(), "claimset.Verify should succeed") {
		return
	}

	// This should succeed, because WithIssuer is provided with same value
	if !assert.NoError(t, c.Verify(jwt.WithIssuer(c.Issuer)), "claimset.Verify should succeed") {
		return
	}

	if !assert.Error(t, c.Verify(jwt.WithIssuer("poop")), "claimset.Verify should fail") {
		return
	}
}

func TestGHIssue10_aud(t *testing.T) {
	c := jwt.NewClaimSet()
	c.Audience = []string{
		"foo",
		"bar",
		"baz",
	}

	// This should succeed, because WithAudience is not provided in the
	// optinal parameters
	if !assert.NoError(t, c.Verify(), "claimset.Verify should succeed") {
		return
	}

	// This should succeed, because WithAudience is provided, and its
	// value matches one of the audience values
	if !assert.NoError(t, c.Verify(jwt.WithAudience("baz")), "claimset.Verify should succeed") {
		return
	}

	if !assert.Error(t, c.Verify(jwt.WithAudience("poop")), "claimset.Verify should fail") {
		return
	}
}

func TestGHIssue10_sub(t *testing.T) {
	c := jwt.NewClaimSet()
	c.Subject = "github.com/lestrrat/go-jwx"

	// This should succeed, because WithSubject is not provided in the
	// optinal parameters
	if !assert.NoError(t, c.Verify(), "claimset.Verify should succeed") {
		return
	}

	// This should succeed, because WithSubject is provided with same value
	if !assert.NoError(t, c.Verify(jwt.WithSubject(c.Subject)), "claimset.Verify should succeed") {
		return
	}

	if !assert.Error(t, c.Verify(jwt.WithSubject("poop")), "claimset.Verify should fail") {
		return
	}
}

func TestGHIssue10_nbf(t *testing.T) {
	c := jwt.NewClaimSet()

	// NotBefore is set to future date
	tm := time.Now().Add(72 * time.Hour)
	c.NotBefore = &jwt.NumericDate{tm}

	// This should fail, because nbf is the future
	if !assert.Error(t, c.Verify(), "claimset.Verify should fail") {
		return
	}

	// This should succeed, because we have given reaaaaaaly big skew
	// that is well enough to get us accepted
	if !assert.NoError(t, c.Verify(jwt.WithAcceptableSkew(73*time.Hour)), "claimset.Verify should succeed") {
		return
	}

	// This should succeed, because we have given a time
	// that is well enough into the future
	if !assert.NoError(t, c.Verify(jwt.WithClock(jwt.ClockFunc(func() time.Time { return tm.Add(time.Hour) }))), "claimset.Verify should succeed") {
		return
	}
}

func TestGHIssue10_exp(t *testing.T) {
	c := jwt.NewClaimSet()

	// issuedat = 1 Hr before current time
	tm := time.Now()
	c.IssuedAt = tm.Unix() - 3600

	// valid for 2 minutes only from IssuedAt
	c.Expiration = c.IssuedAt + 120

	// This should fail, because exp is set in the past
	if !assert.Error(t, c.Verify(), "claimset.Verify should fail") {
		return
	}

	// This should succeed, because we have given big skew
	// that is well enough to get us accepted
	if !assert.NoError(t, c.Verify(jwt.WithAcceptableSkew(time.Hour)), "claimset.Verify should succeed") {
		return
	}

	// This should succeed, because we have given a time
	// that is well enough into the past
	if !assert.NoError(t, c.Verify(jwt.WithClock(jwt.ClockFunc(func() time.Time { return tm.Add(-3500 * time.Second) }))), "claimset.Verify should succeed") {
		return
	}
}
<|endoftext|>


<commit_before>package mdns

import (
	"context"
	"errors"
	"io"
	"net"
	"strings"
	"sync"

	logging "github.com/ipfs/go-log/v2"
	"github.com/libp2p/go-libp2p-core/host"
	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/libp2p/zeroconf/v2"

	ma "github.com/multiformats/go-multiaddr"
	manet "github.com/multiformats/go-multiaddr/net"
)

const (
	ServiceName   = "_p2p._udp"
	mdnsDomain    = "local"
	dnsaddrPrefix = "dnsaddr="
)

var log = logging.Logger("mdns")

type Service interface {
	Start() error
	io.Closer
}

type Notifee interface {
	HandlePeerFound(peer.AddrInfo)
}

type mdnsService struct {
	host        host.Host
	serviceName string

	// The context is canceled when Close() is called.
	ctx       context.Context
	ctxCancel context.CancelFunc

	resolverWG sync.WaitGroup
	server     *zeroconf.Server

	notifee Notifee
}

func NewMdnsService(host host.Host, serviceName string, notifee Notifee) *mdnsService {
	if serviceName == "" {
		serviceName = ServiceName
	}
	s := &mdnsService{
		host:        host,
		serviceName: serviceName,
		notifee:     notifee,
	}
	s.ctx, s.ctxCancel = context.WithCancel(context.Background())
	return s
}

func (s *mdnsService) Start() error {
	if err := s.startServer(); err != nil {
		return err
	}
	s.startResolver(s.ctx)
	return nil
}

func (s *mdnsService) Close() error {
	s.ctxCancel()
	if s.server != nil {
		s.server.Shutdown()
	}
	s.resolverWG.Wait()
	return nil
}

// We don't really care about the IP addresses, but the spec (and various routers / firewalls) require us
// to send A and AAAA records.
func (s *mdnsService) getIPs(addrs []ma.Multiaddr) ([]string, error) {
	var ip4, ip6 string
	for _, addr := range addrs {
		network, hostport, err := manet.DialArgs(addr)
		if err != nil {
			continue
		}
		host, _, err := net.SplitHostPort(hostport)
		if err != nil {
			continue
		}
		if ip4 == "" && (network == "udp4" || network == "tcp4") {
			ip4 = host
		} else if ip6 == "" && (network == "udp6" || network == "tcp6") {
			ip6 = host
		}
	}
	ips := make([]string, 0, 2)
	if ip4 != "" {
		ips = append(ips, ip4)
	}
	if ip6 != "" {
		ips = append(ips, ip6)
	}
	if len(ips) == 0 {
		return nil, errors.New("didn't find any IP addresses")
	}
	return ips, nil
}

func (s *mdnsService) mdnsInstance() string {
	return string(s.host.ID())
}

func (s *mdnsService) startServer() error {
	interfaceAddrs, err := s.host.Network().InterfaceListenAddresses()
	if err != nil {
		return err
	}
	addrs, err := peer.AddrInfoToP2pAddrs(&peer.AddrInfo{
		ID:    s.host.ID(),
		Addrs: interfaceAddrs,
	})
	if err != nil {
		return err
	}
	var txts []string
	for _, addr := range addrs {
		if manet.IsThinWaist(addr) { // don't announce circuit addresses
			txts = append(txts, dnsaddrPrefix+addr.String())
		}
	}

	ips, err := s.getIPs(addrs)
	if err != nil {
		return err
	}

	server, err := zeroconf.RegisterProxy(
		s.mdnsInstance(),
		s.serviceName,
		mdnsDomain,
		4001,                 // we have to pass in a port number here, but libp2p only uses the TXT records
		s.host.ID().Pretty(), // TODO: deals with peer IDs longer than 63 characters
		ips,
		txts,
		nil,
	)
	if err != nil {
		return err
	}
	s.server = server
	return nil
}

func (s *mdnsService) startResolver(ctx context.Context) {
	s.resolverWG.Add(2)
	entryChan := make(chan *zeroconf.ServiceEntry, 1000)
	go func() {
		defer s.resolverWG.Done()
		for entry := range entryChan {
			// We only care about the TXT records.
			// Ignore A, AAAA and PTR.
			addrs := make([]ma.Multiaddr, 0, len(entry.Text)) // assume that all TXT records are dnsaddrs
			for _, s := range entry.Text {
				if !strings.HasPrefix(s, dnsaddrPrefix) {
					log.Debug("missing dnsaddr prefix")
					continue
				}
				addr, err := ma.NewMultiaddr(s[len(dnsaddrPrefix):])
				if err != nil {
					log.Debugf("failed to parse multiaddr: %s", err)
					continue
				}
				addrs = append(addrs, addr)
			}
			infos, err := peer.AddrInfosFromP2pAddrs(addrs...)
			if err != nil {
				log.Debugf("failed to get peer info: %s", err)
				continue
			}
			for _, info := range infos {
				go s.notifee.HandlePeerFound(info)
			}
		}
	}()
	go func() {
		defer s.resolverWG.Done()
		if err := zeroconf.Browse(ctx, s.serviceName, mdnsDomain, entryChan); err != nil {
			log.Debugf("zeroconf browsing failed: %s", err)
		}
	}()
}
<commit_msg>use a random string for the mDNS peer-name<commit_after>package mdns

import (
	"context"
	"errors"
	"io"
	"math/rand"
	"net"
	"strings"
	"sync"

	logging "github.com/ipfs/go-log/v2"
	"github.com/libp2p/go-libp2p-core/host"
	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/libp2p/zeroconf/v2"

	ma "github.com/multiformats/go-multiaddr"
	manet "github.com/multiformats/go-multiaddr/net"
)

const (
	ServiceName   = "_p2p._udp"
	mdnsDomain    = "local"
	dnsaddrPrefix = "dnsaddr="
)

var log = logging.Logger("mdns")

type Service interface {
	Start() error
	io.Closer
}

type Notifee interface {
	HandlePeerFound(peer.AddrInfo)
}

type mdnsService struct {
	host        host.Host
	serviceName string
	peerName    string

	// The context is canceled when Close() is called.
	ctx       context.Context
	ctxCancel context.CancelFunc

	resolverWG sync.WaitGroup
	server     *zeroconf.Server

	notifee Notifee
}

func NewMdnsService(host host.Host, serviceName string, notifee Notifee) *mdnsService {
	if serviceName == "" {
		serviceName = ServiceName
	}
	s := &mdnsService{
		host:        host,
		serviceName: serviceName,
		peerName:    randomString(32 + rand.Intn(32)), // generate a random string between 32 and 63 characters long
		notifee:     notifee,
	}
	s.ctx, s.ctxCancel = context.WithCancel(context.Background())
	return s
}

func (s *mdnsService) Start() error {
	if err := s.startServer(); err != nil {
		return err
	}
	s.startResolver(s.ctx)
	return nil
}

func (s *mdnsService) Close() error {
	s.ctxCancel()
	if s.server != nil {
		s.server.Shutdown()
	}
	s.resolverWG.Wait()
	return nil
}

// We don't really care about the IP addresses, but the spec (and various routers / firewalls) require us
// to send A and AAAA records.
func (s *mdnsService) getIPs(addrs []ma.Multiaddr) ([]string, error) {
	var ip4, ip6 string
	for _, addr := range addrs {
		network, hostport, err := manet.DialArgs(addr)
		if err != nil {
			continue
		}
		host, _, err := net.SplitHostPort(hostport)
		if err != nil {
			continue
		}
		if ip4 == "" && (network == "udp4" || network == "tcp4") {
			ip4 = host
		} else if ip6 == "" && (network == "udp6" || network == "tcp6") {
			ip6 = host
		}
	}
	ips := make([]string, 0, 2)
	if ip4 != "" {
		ips = append(ips, ip4)
	}
	if ip6 != "" {
		ips = append(ips, ip6)
	}
	if len(ips) == 0 {
		return nil, errors.New("didn't find any IP addresses")
	}
	return ips, nil
}

func (s *mdnsService) startServer() error {
	interfaceAddrs, err := s.host.Network().InterfaceListenAddresses()
	if err != nil {
		return err
	}
	addrs, err := peer.AddrInfoToP2pAddrs(&peer.AddrInfo{
		ID:    s.host.ID(),
		Addrs: interfaceAddrs,
	})
	if err != nil {
		return err
	}
	var txts []string
	for _, addr := range addrs {
		if manet.IsThinWaist(addr) { // don't announce circuit addresses
			txts = append(txts, dnsaddrPrefix+addr.String())
		}
	}

	ips, err := s.getIPs(addrs)
	if err != nil {
		return err
	}

	server, err := zeroconf.RegisterProxy(
		s.peerName,
		s.serviceName,
		mdnsDomain,
		4001, // we have to pass in a port number here, but libp2p only uses the TXT records
		s.peerName,
		ips,
		txts,
		nil,
	)
	if err != nil {
		return err
	}
	s.server = server
	return nil
}

func (s *mdnsService) startResolver(ctx context.Context) {
	s.resolverWG.Add(2)
	entryChan := make(chan *zeroconf.ServiceEntry, 1000)
	go func() {
		defer s.resolverWG.Done()
		for entry := range entryChan {
			// We only care about the TXT records.
			// Ignore A, AAAA and PTR.
			addrs := make([]ma.Multiaddr, 0, len(entry.Text)) // assume that all TXT records are dnsaddrs
			for _, s := range entry.Text {
				if !strings.HasPrefix(s, dnsaddrPrefix) {
					log.Debug("missing dnsaddr prefix")
					continue
				}
				addr, err := ma.NewMultiaddr(s[len(dnsaddrPrefix):])
				if err != nil {
					log.Debugf("failed to parse multiaddr: %s", err)
					continue
				}
				addrs = append(addrs, addr)
			}
			infos, err := peer.AddrInfosFromP2pAddrs(addrs...)
			if err != nil {
				log.Debugf("failed to get peer info: %s", err)
				continue
			}
			for _, info := range infos {
				go s.notifee.HandlePeerFound(info)
			}
		}
	}()
	go func() {
		defer s.resolverWG.Done()
		if err := zeroconf.Browse(ctx, s.serviceName, mdnsDomain, entryChan); err != nil {
			log.Debugf("zeroconf browsing failed: %s", err)
		}
	}()
}

func randomString(l int) string {
	const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789"
	s := make([]byte, 0, l)
	for i := 0; i < l; i++ {
		s = append(s, alphabet[rand.Intn(len(alphabet))])
	}
	return string(s)
}
<|endoftext|>


<commit_before>package gfx

import (
	"bytes"
	"fmt"
	img "image"
	"image/color"
	"image/png"
	"os"
)

// Image represents a complete picture converted from a PNG image
type Image struct {
	img     *img.Paletted
	palette []color.Color
	colors  []byte
	mcol    bool
	BgColor byte
}

// NewImage reads an image from a PNG file and returns a Image pointer
func NewImage(filename string, mcol bool, bgColor byte) *Image {
	img := pngImage(filename)
	return &Image{img, Pepto, remapIndices(img.Palette, Pepto), mcol, bgColor}
}

// MulticolorImage reads an image from a PNG file and returns a Image pointer
func MulticolorImage(filename string, bgColor byte) *Image {
	return NewImage(filename, true, bgColor)
}

// HiresImage reads an image from a PNG file and returns a Image pointer
func HiresImage(filename string, bgColor byte) *Image {
	return NewImage(filename, false, bgColor)
}

// KoalaImage reads an image from a PNG file and returns a Koala pointer
func KoalaImage(filename string, bgColor byte) *Koala {
	image := MulticolorImage(filename, bgColor)
	return image.Koala(0, 0)
}

func (image *Image) PixelAt(x, y int) byte {
	if (img.Point{x, y}).In(image.img.Rect) {
		return image.colors[image.img.ColorIndexAt(x, y)]
	} else {
		return image.BgColor
	}
}

func (image *Image) HiresByte(x, y, c int) byte {
	value := byte(0)
	for i := 0; i < 8; i++ {
		p := image.PixelAt(x + i, y)
		if p == byte(c) {
			value += byte(1 << uint(7 - i))
		}
	}
	return value
}

// Pixels return an array of multicolor pixels (color indices) from the given area in the Image
func (image *Image) Pixels(xoffset, yoffset, width, height int) [][]byte {
	pix := make([][]byte, height)
	for y := 0; y < height; y++ {
		pix[y] = make([]byte, width)
		for x := 0; x < width; x++ {
			if image.mcol {
				pix[y][x] = image.PixelAt((xoffset + x) * 2, yoffset + y)
			} else {
				pix[y][x] = image.PixelAt(xoffset + x, yoffset + y)
			}
		}
	}
	return pix
}

// MulticolorSprite extracts a multicolor sprite as a 64-byte array
func (image *Image) MulticolorSprite(xoffset, yoffset int, colors []byte) []byte {
	spr := make([]byte, 64)
	pixels := image.Pixels(xoffset, yoffset, 12, 21)
	fmt.Printf("[%d,%d] %v\n", xoffset, yoffset, pixels)
	for y := 0; y < 21; y++ {
		for c := 0; c < 3; c++ {
			i := y * 3 + c
			for x := 0; x < 4; x++ {
				n := bytes.IndexByte(colors, pixels[y][c * 4 + x])
				if n >= 0 {
					spr[i] = (spr[i] << 2) + byte(n)
				}
			}
		}
	}
	return spr
}

// MulticolorCell extracts a 4x8 pixels multicolor cell as a 10-byte array,
// the first 8 bytes are bitmap data, followed by a screen byte and
// a colmap byte
func (image *Image) MulticolorCell(xoffset, yoffset int) []byte {
	cell := make([]byte, 10)
	pixels := image.Pixels(xoffset, yoffset, 4, 8)
	colors := colorsUsed(pixels, image.BgColor)
	for len(colors) < 4 {
		colors = append(colors, 0)
	}
	index := make(map[byte]byte)
	for i, c := range colors {
		if _, found := index[c]; !found {
			index[c] = byte(i)
		}
	}
	for y := 0; y < 8; y++ {
		for x := 0; x < 4; x++ {
			cell[y] = (cell[y] << 2) + index[pixels[y][x]]
		}
	}
	cell[8] = colors[1] * 16 + colors[2]
	cell[9] = colors[3]
	return cell
}

// Koala extracts a full-screen 160x200 multicolor image in Koala format
func (image *Image) Koala(xoffset, yoffset int) *Koala {
	koala := Koala{
		Bitmap: make([]byte, 8000),
		Screen: make([]byte, 1000),
		Colmap: make([]byte, 1000),
		BgColor: image.BgColor}
	for row := 0; row < 25; row++ {
		for col := 0; col < 40; col++ {
			cell := image.MulticolorCell(xoffset + col * 4, yoffset + row * 8)
			copy(koala.Bitmap[row * 320 + col * 8:], cell[0:8])
			koala.Screen[row * 40 + col] = cell[8]
			koala.Colmap[row * 40 + col] = cell[9]
		}
	}
	return &koala
}

// Koala represents a full-screen image in KoalaPainter format
type Koala struct {
	Bitmap  []byte
	Screen  []byte
	Colmap  []byte
	BgColor byte
}

// Bytes returns Koala format as raw bytes. If align is false, there
// will be no padding between data segments. If align is true, screen
// and color map data will be aligned to 1024 bytes offsets.
func (koala *Koala) Bytes(align bool) []byte {
	if align {
		return bytes.Join([][]byte{
			koala.Bitmap, make([]byte, 192),
			koala.Screen, make([]byte, 24),
			koala.Colmap, []byte{koala.BgColor}}, []byte{})
	} else {
		return bytes.Join([][]byte{
			koala.Bitmap, koala.Screen, koala.Colmap,
			[]byte{koala.BgColor}}, []byte{})
	}
}

func pngImage(filename string) *img.Paletted {
	file, err := os.Open(filename)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	decoded, err := png.Decode(file)
	if err != nil {
		panic(err)
	}
	return decoded.(*img.Paletted)
}

func remapIndices(from color.Palette, to []color.Color) []byte {
	colors := make([]byte, 16)
	for index, color := range to {
		i := from.Index(color)
		colors[i] = byte(index)
	}
	return colors
}

func histogram(pixels [][]byte) map[byte]int {
	counts := make(map[byte]int)
	for _, row := range pixels {
		for _, col := range row {
			counts[col]++
		}
	}
	return counts
}

func colorsUsed(pixels [][]byte, bgColor byte) []byte {
	counts := histogram(pixels)
	colors := []byte{bgColor}
	for key, _ := range counts {
		if key != bgColor {
			colors = append(colors, key)
		}
	}
	return colors
}
<commit_msg>Commented out debug output in sprite generation.<commit_after>package gfx

import (
	"bytes"
	img "image"
	"image/color"
	"image/png"
	"os"
)

// Image represents a complete picture converted from a PNG image
type Image struct {
	img     *img.Paletted
	palette []color.Color
	colors  []byte
	mcol    bool
	BgColor byte
}

// NewImage reads an image from a PNG file and returns a Image pointer
func NewImage(filename string, mcol bool, bgColor byte) *Image {
	img := pngImage(filename)
	return &Image{img, Pepto, remapIndices(img.Palette, Pepto), mcol, bgColor}
}

// MulticolorImage reads an image from a PNG file and returns a Image pointer
func MulticolorImage(filename string, bgColor byte) *Image {
	return NewImage(filename, true, bgColor)
}

// HiresImage reads an image from a PNG file and returns a Image pointer
func HiresImage(filename string, bgColor byte) *Image {
	return NewImage(filename, false, bgColor)
}

// KoalaImage reads an image from a PNG file and returns a Koala pointer
func KoalaImage(filename string, bgColor byte) *Koala {
	image := MulticolorImage(filename, bgColor)
	return image.Koala(0, 0)
}

func (image *Image) PixelAt(x, y int) byte {
	if (img.Point{x, y}).In(image.img.Rect) {
		return image.colors[image.img.ColorIndexAt(x, y)]
	} else {
		return image.BgColor
	}
}

func (image *Image) HiresByte(x, y, c int) byte {
	value := byte(0)
	for i := 0; i < 8; i++ {
		p := image.PixelAt(x+i, y)
		if p == byte(c) {
			value += byte(1 << uint(7-i))
		}
	}
	return value
}

// Pixels return an array of multicolor pixels (color indices) from the given area in the Image
func (image *Image) Pixels(xoffset, yoffset, width, height int) [][]byte {
	pix := make([][]byte, height)
	for y := 0; y < height; y++ {
		pix[y] = make([]byte, width)
		for x := 0; x < width; x++ {
			if image.mcol {
				pix[y][x] = image.PixelAt((xoffset+x)*2, yoffset+y)
			} else {
				pix[y][x] = image.PixelAt(xoffset+x, yoffset+y)
			}
		}
	}
	return pix
}

// MulticolorSprite extracts a multicolor sprite as a 64-byte array
func (image *Image) MulticolorSprite(xoffset, yoffset int, colors []byte) []byte {
	spr := make([]byte, 64)
	pixels := image.Pixels(xoffset, yoffset, 12, 21)
	//fmt.Printf("[%d,%d] %v\n", xoffset, yoffset, pixels)
	for y := 0; y < 21; y++ {
		for c := 0; c < 3; c++ {
			i := y*3 + c
			for x := 0; x < 4; x++ {
				n := bytes.IndexByte(colors, pixels[y][c*4+x])
				if n >= 0 {
					spr[i] = (spr[i] << 2) + byte(n)
				}
			}
		}
	}
	return spr
}

// MulticolorCell extracts a 4x8 pixels multicolor cell as a 10-byte array,
// the first 8 bytes are bitmap data, followed by a screen byte and
// a colmap byte
func (image *Image) MulticolorCell(xoffset, yoffset int) []byte {
	cell := make([]byte, 10)
	pixels := image.Pixels(xoffset, yoffset, 4, 8)
	colors := colorsUsed(pixels, image.BgColor)
	for len(colors) < 4 {
		colors = append(colors, 0)
	}
	index := make(map[byte]byte)
	for i, c := range colors {
		if _, found := index[c]; !found {
			index[c] = byte(i)
		}
	}
	for y := 0; y < 8; y++ {
		for x := 0; x < 4; x++ {
			cell[y] = (cell[y] << 2) + index[pixels[y][x]]
		}
	}
	cell[8] = colors[1]*16 + colors[2]
	cell[9] = colors[3]
	return cell
}

// Koala extracts a full-screen 160x200 multicolor image in Koala format
func (image *Image) Koala(xoffset, yoffset int) *Koala {
	koala := Koala{
		Bitmap:  make([]byte, 8000),
		Screen:  make([]byte, 1000),
		Colmap:  make([]byte, 1000),
		BgColor: image.BgColor}
	for row := 0; row < 25; row++ {
		for col := 0; col < 40; col++ {
			cell := image.MulticolorCell(xoffset+col*4, yoffset+row*8)
			copy(koala.Bitmap[row*320+col*8:], cell[0:8])
			koala.Screen[row*40+col] = cell[8]
			koala.Colmap[row*40+col] = cell[9]
		}
	}
	return &koala
}

// Koala represents a full-screen image in KoalaPainter format
type Koala struct {
	Bitmap  []byte
	Screen  []byte
	Colmap  []byte
	BgColor byte
}

// Bytes returns Koala format as raw bytes. If align is false, there
// will be no padding between data segments. If align is true, screen
// and color map data will be aligned to 1024 bytes offsets.
func (koala *Koala) Bytes(align bool) []byte {
	if align {
		return bytes.Join([][]byte{
			koala.Bitmap, make([]byte, 192),
			koala.Screen, make([]byte, 24),
			koala.Colmap, []byte{koala.BgColor}}, []byte{})
	} else {
		return bytes.Join([][]byte{
			koala.Bitmap, koala.Screen, koala.Colmap,
			[]byte{koala.BgColor}}, []byte{})
	}
}

func pngImage(filename string) *img.Paletted {
	file, err := os.Open(filename)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	decoded, err := png.Decode(file)
	if err != nil {
		panic(err)
	}
	return decoded.(*img.Paletted)
}

func remapIndices(from color.Palette, to []color.Color) []byte {
	colors := make([]byte, 16)
	for index, color := range to {
		i := from.Index(color)
		colors[i] = byte(index)
	}
	return colors
}

func histogram(pixels [][]byte) map[byte]int {
	counts := make(map[byte]int)
	for _, row := range pixels {
		for _, col := range row {
			counts[col]++
		}
	}
	return counts
}

func colorsUsed(pixels [][]byte, bgColor byte) []byte {
	counts := histogram(pixels)
	colors := []byte{bgColor}
	for key, _ := range counts {
		if key != bgColor {
			colors = append(colors, key)
		}
	}
	return colors
}
<|endoftext|>