<commit_before>// Copyright 2017 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package config

import (
	"errors"
	"path"
	"strings"
)

type permissionsV1 struct {
	read bool
	list bool
}

type accessControlV1 struct {
	// whitelistAdditional is the internal version of
	// AccessControlV1.WhitelistAdditionalPermissions. See comment of latter
	// for more details. It's a map of username -> permissionsV1.
	whitelistAdditional map[string]permissionsV1
	anonymous           permissionsV1
}

func parsePermissionsV1(permsStr string) (permissionsV1, error) {
	permsStr = strings.TrimSpace(permsStr)
	var perms permissionsV1
	if len(permsStr) == 0 {
		return perms, nil
	}
	for _, p := range strings.Split(permsStr, ",") {
		switch p {
		case PermRead:
			perms.read = true
		case PermList:
			perms.list = true
		default:
			return permissionsV1{}, ErrInvalidPermissions{
				permissions: permsStr}
		}
	}
	return perms, nil
}

func makeAccessControlV1Internal(a *AccessControlV1, users map[string][]byte) (
	ac *accessControlV1, err error) {
	if a == nil {
		return nil, errors.New("nil AccessControlV1")
	}
	ac = &accessControlV1{}
	ac.anonymous, err = parsePermissionsV1(a.AnonymousPermissions)
	if err != nil {
		return nil, err
	}
	for username, permissions := range a.WhitelistAdditionalPermissions {
		if _, ok := users[username]; !ok {
			return nil, ErrUndefinedUsername{username: username}
		}
		if ac.whitelistAdditional == nil {
			ac.whitelistAdditional = make(map[string]permissionsV1)
		}
		ac.whitelistAdditional[username], err = parsePermissionsV1(permissions)
		if err != nil {
			return nil, err
		}
	}
	return ac, nil
}

func defaultAccessControlV1InternalForRoot() *accessControlV1 {
	return &accessControlV1{
		anonymous: permissionsV1{read: true, list: true},
	}
}

type aclCheckerV1 struct {
	children map[string]*aclCheckerV1
	ac       *accessControlV1
}

// cleanPath cleans p in by first calling path.Clean, then removing any leading
// and trailing "/".
//
// If p represents root, an empty string is returned.
//
// Use cleanPathAndSplit2 or cleanPathAndSplit to further split the path.
func cleanPath(p string) string {
	p = path.Clean(p)
	if p == "/" || p == "." {
		// Examples of p (before clean) that lead us here:
		//   ""
		//   "/"
		//   "."
		//   "/.."
		//   "/."
		return ""
	}
	// After the trim, p can only be form of "a/b/c", i.e., no leading or
	// trailing "/". Examples:
	//   "a"
	//   "a/b"
	//   "a/b/c"
	return strings.Trim(p, "/")
}

// cleanPathAndSplit calls cleanPath on p and splits the result using separator
// "/", into a slice consisting of at least 1 element.
func cleanPathAndSplit(p string) (elems []string) {
	return strings.Split(cleanPath(p), "/")
}

// cleanPathAndSplit2 calls cleanPath on p and splits the result using
// separator "/", into a slice of either 1 or 2 elements.
func cleanPathAndSplit2(p string) (elems []string) {
	return strings.SplitN(cleanPath(p), "/", 2)
}

// getAccessControl gets the corresponding accessControlV1 for p.
// If a specifically defined one exists, it's returned. Otherwise the default
// one is returned.
func (c *aclCheckerV1) getAccessControl(
	parentAC *accessControlV1, p string) (ac *accessControlV1) {
	effectiveAC := c.ac
	if c.ac == nil {
		// If c.ac == nil, it means user didn't specify an ACL for this
		// directory. So just inherit from the parent.
		effectiveAC = parentAC
	}
	elems := cleanPathAndSplit2(p)
	if len(elems[0]) == 0 || c.children == nil {
		return effectiveAC
	}
	if subChecker, ok := c.children[elems[0]]; ok {
		if len(elems) > 1 {
			return subChecker.getAccessControl(effectiveAC, elems[1])
		}
		return subChecker.getAccessControl(effectiveAC, ".")
	}

	return effectiveAC
}

func (c *aclCheckerV1) getPermissions(
	p string, username *string) (permissions permissionsV1) {
	// This is only called on the root aclCheckerV1, and c.ac is always
	// populated here.
	ac := c.getAccessControl(nil, p)
	permissions = ac.anonymous
	if ac.whitelistAdditional == nil || username == nil {
		return permissions
	}
	if perms, ok := ac.whitelistAdditional[*username]; ok {
		permissions.read = perms.read || permissions.read
		permissions.list = perms.list || permissions.list
	}
	return permissions
}

func makeACLCheckerV1(acl map[string]AccessControlV1,
	users map[string][]byte) (*aclCheckerV1, error) {
	root := &aclCheckerV1{ac: defaultAccessControlV1InternalForRoot()}
	if acl == nil {
		return root, nil
	}
	// path -> *AccessControlV1
	cleaned := make(map[string]*AccessControlV1)

	// Make sure there's no duplicate paths.
	for p := range acl {
		// We are doing a separate declaration here instead of in the for
		// statement above because we need to take the address of ac for each
		// element in acl, declarations in the for statement don't change
		// address.
		ac := acl[p]
		cleanedPath := path.Clean(p)
		if cleanedPath == "." {
			// Override "." with "/" since they both represent the site root.
			cleanedPath = "/"
		}
		if _, ok := cleaned[cleanedPath]; ok {
			return nil, ErrDuplicateAccessControlPath{cleanedPath: cleanedPath}
		}
		cleaned[cleanedPath] = &ac
	}

	for p, a := range cleaned {
		ac, err := makeAccessControlV1Internal(a, users)
		if err != nil {
			return nil, err
		}

		elems := cleanPathAndSplit(p)
		if len(elems[0]) == 0 {
			root.ac = ac
			continue
		}

		c := root
		// Construct aclCheckerV1 objects along the path and populate
		// defaultAC.
		for _, elem := range elems {
			if c.children == nil {
				// path element -> *aclCheckerV1
				c.children = make(map[string]*aclCheckerV1)
			}
			if c.children[elem] == nil {
				// Intentionally leave the ac field empty so if no ACL is
				// specified for this directory we'd use the one from its
				// parent (see getAccessControl).
				c.children[elem] = &aclCheckerV1{}
			}
			c = c.children[elem]
		}
		c.ac = ac
	}

	return root, nil
}
<commit_msg>add more comment for the ACL checker as suggested by jzila<commit_after>// Copyright 2017 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package config

import (
	"errors"
	"path"
	"strings"
)

// permissionsV1 is the parsed version of a permission string.
type permissionsV1 struct {
	read bool
	list bool
}

func parsePermissionsV1(permsStr string) (permissionsV1, error) {
	permsStr = strings.TrimSpace(permsStr)
	var perms permissionsV1
	if len(permsStr) == 0 {
		return perms, nil
	}
	for _, p := range strings.Split(permsStr, ",") {
		switch p {
		case PermRead:
			perms.read = true
		case PermList:
			perms.list = true
		default:
			return permissionsV1{}, ErrInvalidPermissions{
				permissions: permsStr}
		}
	}
	return perms, nil
}

// accessControlV1 is the parsed version of AccessControlV1. It can be used
// directly with no parsing errors by the ACL checker.
type accessControlV1 struct {
	// whitelistAdditional is the internal version of
	// AccessControlV1.WhitelistAdditionalPermissions. See comment of latter
	// for more details. It's a map of username -> permissionsV1.
	whitelistAdditional map[string]permissionsV1
	anonymous           permissionsV1
}

// makeAccessControlV1Internal makes an *accessControlV1 out of an
// *AccessControlV1. The users map is used to check if every username defined
// in WhitelistAdditionalPermissions is defined.
func makeAccessControlV1Internal(a *AccessControlV1, users map[string][]byte) (
	ac *accessControlV1, err error) {
	if a == nil {
		return nil, errors.New("nil AccessControlV1")
	}
	ac = &accessControlV1{}
	ac.anonymous, err = parsePermissionsV1(a.AnonymousPermissions)
	if err != nil {
		return nil, err
	}
	for username, permissions := range a.WhitelistAdditionalPermissions {
		if _, ok := users[username]; !ok {
			return nil, ErrUndefinedUsername{username: username}
		}
		if ac.whitelistAdditional == nil {
			ac.whitelistAdditional = make(map[string]permissionsV1)
		}
		ac.whitelistAdditional[username], err = parsePermissionsV1(permissions)
		if err != nil {
			return nil, err
		}
	}
	return ac, nil
}

func defaultAccessControlV1InternalForRoot() *accessControlV1 {
	return &accessControlV1{
		anonymous: permissionsV1{read: true, list: true},
	}
}

type aclCheckerV1 struct {
	children map[string]*aclCheckerV1
	// ac, if not nil, defines the access control that should be applied to the
	// path that the *aclCheckerV1 represents. If it's nil, it means no
	// specific access control is defined for the path, and the object exists
	// most likely for the purpose of the children field to realy to checkers
	// under this path. In this case, the parent's access control is the
	// effective one for this path.
	ac *accessControlV1
}

// cleanPath cleans p in by first calling path.Clean, then removing any leading
// and trailing "/".
//
// If p represents root, an empty string is returned.
//
// Use cleanPathAndSplit2 or cleanPathAndSplit to further split the path.
func cleanPath(p string) string {
	p = path.Clean(p)
	if p == "/" || p == "." {
		// Examples of p (before clean) that lead us here:
		//   ""
		//   "/"
		//   "."
		//   "/.."
		//   "/."
		return ""
	}
	// After the trim, p can only be form of "a/b/c", i.e., no leading or
	// trailing "/". Examples:
	//   "a"
	//   "a/b"
	//   "a/b/c"
	return strings.Trim(p, "/")
}

// cleanPathAndSplit calls cleanPath on p and splits the result using separator
// "/", into a slice consisting of at least 1 element.
func cleanPathAndSplit(p string) (elems []string) {
	return strings.Split(cleanPath(p), "/")
}

// cleanPathAndSplit2 calls cleanPath on p and splits the result using
// separator "/", into a slice of either 1 or 2 elements.
func cleanPathAndSplit2(p string) (elems []string) {
	return strings.SplitN(cleanPath(p), "/", 2)
}

// getAccessControl gets the corresponding accessControlV1 for p. It walks
// along the children field recursively.  If a specifically defined one exists,
// it's returned. Otherwise the parent's (parentAC) is returned.
func (c *aclCheckerV1) getAccessControl(
	parentAC *accessControlV1, p string) (ac *accessControlV1) {
	effectiveAC := c.ac
	if c.ac == nil {
		// If c.ac == nil, it means user didn't specify an ACL for the
		// path that c represents. So just inherit from the parent.
		effectiveAC = parentAC
	}
	elems := cleanPathAndSplit2(p)
	if len(elems[0]) == 0 || c.children == nil {
		// Either what we are looking for is exactly what c represents, or c
		// doesn't have any children *aclCheckerV1's. Either way, c should be
		// the checker that controls the path p, so we can just returned the
		// current effectiveAC.
		return effectiveAC
	}
	// See if we have a sub-checker for the next element in the path p.
	if subChecker, ok := c.children[elems[0]]; ok {
		if len(elems) > 1 {
			// There are more elements in the path p, so ask the sub-checker
			// for check for the rest.
			return subChecker.getAccessControl(effectiveAC, elems[1])
		}
		// The sub-checker is what we need in order to know get the
		// *accessControlV1 for path p, so call it with "." to indicate that.
		return subChecker.getAccessControl(effectiveAC, ".")
	}

	// We don't have a sub-checker for the next element in the path p, so just
	// use the current effectiveAC like the `len(elems[0]) == 0 || c.children
	// == nil` above.
	return effectiveAC
}

// getPermissions returns the permissions that username has on p. This method
// should only be called on the root aclCheckerV1.
func (c *aclCheckerV1) getPermissions(
	p string, username *string) (permissions permissionsV1) {
	// This is only called on the root aclCheckerV1, and c.ac is always
	// populated here. So even if no other path shows up in the ACLs, any path
	// will get root's *accessControlV1 as the last resort.
	ac := c.getAccessControl(nil, p)
	permissions = ac.anonymous
	if ac.whitelistAdditional == nil || username == nil {
		return permissions
	}
	if perms, ok := ac.whitelistAdditional[*username]; ok {
		permissions.read = perms.read || permissions.read
		permissions.list = perms.list || permissions.list
	}
	return permissions
}

// makeACLCheckerV1 makes an *aclCheckerV1 out of user-defined ACLs (acl). It
// recursively constructs nested *aclCheckerV1 so that each defined path has a
// corresponding checker, and all intermediate nodes have a checker populated.
func makeACLCheckerV1(acl map[string]AccessControlV1,
	users map[string][]byte) (*aclCheckerV1, error) {
	root := &aclCheckerV1{ac: defaultAccessControlV1InternalForRoot()}
	if acl == nil {
		return root, nil
	}
	// path -> *AccessControlV1
	cleaned := make(map[string]*AccessControlV1)

	// Make sure there's no duplicate paths.
	for p := range acl {
		// We are doing a separate declaration here instead of in the for
		// statement above because we need to take the address of ac for each
		// element in acl, declarations in the for statement don't change
		// address.
		ac := acl[p]
		cleanedPath := path.Clean(p)
		if cleanedPath == "." {
			// Override "." with "/" since they both represent the site root.
			cleanedPath = "/"
		}
		if _, ok := cleaned[cleanedPath]; ok {
			return nil, ErrDuplicateAccessControlPath{cleanedPath: cleanedPath}
		}
		cleaned[cleanedPath] = &ac
	}

	// Iterate through the cleaned slice, and construct *aclCheckerV1 objects
	// along each path.
	for p, a := range cleaned {
		ac, err := makeAccessControlV1Internal(a, users)
		if err != nil {
			return nil, err
		}

		elems := cleanPathAndSplit(p)
		if len(elems[0]) == 0 {
			root.ac = ac
			continue
		}

		c := root
		// Construct aclCheckerV1 objects along the path if needed.
		for _, elem := range elems {
			if c.children == nil {
				// path element -> *aclCheckerV1
				c.children = make(map[string]*aclCheckerV1)
			}
			if c.children[elem] == nil {
				// Intentionally leave the ac field empty so if no ACL is
				// specified for this directory we'd use the one from its
				// parent (see getAccessControl).
				c.children[elem] = &aclCheckerV1{}
			}
			c = c.children[elem]
		}
		// Now that c points the the *aclCheckerV1 that represents the path p,
		// populate c.ac for it.
		c.ac = ac
	}

	return root, nil
}
<|endoftext|>


<commit_before>package pascal

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

const targetTestVersion = 1

var t20 = [][]int{
	{1},
	{1, 1},
	{1, 2, 1},
	{1, 3, 3, 1},
	{1, 4, 6, 4, 1},
	{1, 5, 10, 10, 5, 1},
	{1, 6, 15, 20, 15, 6, 1},
	{1, 7, 21, 35, 35, 21, 7, 1},
	{1, 8, 28, 56, 70, 56, 28, 8, 1},
	{1, 9, 36, 84, 126, 126, 84, 36, 9, 1},
	{1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1},
	{1, 11, 55, 165, 330, 462, 462, 330, 165, 55, 11, 1},
	{1, 12, 66, 220, 495, 792, 924, 792, 495, 220, 66, 12, 1},
	{1, 13, 78, 286, 715, 1287, 1716, 1716, 1287, 715, 286, 78, 13, 1},
	{1, 14, 91, 364, 1001, 2002, 3003, 3432, 3003, 2002, 1001, 364, 91, 14, 1},
	{1, 15, 105, 455, 1365, 3003, 5005, 6435, 6435, 5005, 3003, 1365, 455, 105, 15, 1},
	{1, 16, 120, 560, 1820, 4368, 8008, 11440, 12870, 11440, 8008, 4368, 1820, 560, 120, 16, 1},
	{1, 17, 136, 680, 2380, 6188, 12376, 19448, 24310, 24310, 19448, 12376, 6188, 2380, 680, 136, 17, 1},
	{1, 18, 153, 816, 3060, 8568, 18564, 31824, 43758, 48620, 43758, 31824, 18564, 8568, 3060, 816, 153, 18, 1},
	{1, 19, 171, 969, 3876, 11628, 27132, 50388, 75582, 92378, 92378, 75582, 50388, 27132, 11628, 3876, 969, 171, 19, 1},
}

func TestTestVersion(t *testing.T) {
	if testVersion != targetTestVersion {
		t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
	}
}

func TestTriangle(t *testing.T) {
	for n := 1; n <= 20; n++ {
		res := Triangle(n)
		want := t20[:n]
		if !reflect.DeepEqual(res, want) {
			t.Fatalf("Triangle(%d) = %s,\nwant:%s\n",
				n, format(res), format(want))
		}
	}
	t.Log(format(Triangle(20)))
}

func format(t [][]int) (s string) {
	for _, r := range t {
		s = fmt.Sprintf("%s\n%v", s, r)
	}
	return
}
<commit_msg>pascals-triangle: add benchmarks (#649)<commit_after>package pascal

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

const targetTestVersion = 1

var triangleTestCases = [][]int{
	{1},
	{1, 1},
	{1, 2, 1},
	{1, 3, 3, 1},
	{1, 4, 6, 4, 1},
	{1, 5, 10, 10, 5, 1},
	{1, 6, 15, 20, 15, 6, 1},
	{1, 7, 21, 35, 35, 21, 7, 1},
	{1, 8, 28, 56, 70, 56, 28, 8, 1},
	{1, 9, 36, 84, 126, 126, 84, 36, 9, 1},
	{1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1},
	{1, 11, 55, 165, 330, 462, 462, 330, 165, 55, 11, 1},
	{1, 12, 66, 220, 495, 792, 924, 792, 495, 220, 66, 12, 1},
	{1, 13, 78, 286, 715, 1287, 1716, 1716, 1287, 715, 286, 78, 13, 1},
	{1, 14, 91, 364, 1001, 2002, 3003, 3432, 3003, 2002, 1001, 364, 91, 14, 1},
	{1, 15, 105, 455, 1365, 3003, 5005, 6435, 6435, 5005, 3003, 1365, 455, 105, 15, 1},
	{1, 16, 120, 560, 1820, 4368, 8008, 11440, 12870, 11440, 8008, 4368, 1820, 560, 120, 16, 1},
	{1, 17, 136, 680, 2380, 6188, 12376, 19448, 24310, 24310, 19448, 12376, 6188, 2380, 680, 136, 17, 1},
	{1, 18, 153, 816, 3060, 8568, 18564, 31824, 43758, 48620, 43758, 31824, 18564, 8568, 3060, 816, 153, 18, 1},
	{1, 19, 171, 969, 3876, 11628, 27132, 50388, 75582, 92378, 92378, 75582, 50388, 27132, 11628, 3876, 969, 171, 19, 1},
}

var testSize = len(triangleTestCases)

func TestTestVersion(t *testing.T) {
	if testVersion != targetTestVersion {
		t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
	}
}

func TestTriangle(t *testing.T) {
	for n := 1; n <= testSize; n++ {
		res := Triangle(n)
		want := triangleTestCases[:n]
		if !reflect.DeepEqual(res, want) {
			t.Fatalf("Triangle(%d) = %s,\nwant:%s\n",
				n, format(res), format(want))
		}
	}
	t.Log(format(Triangle(testSize)))
}

func format(t [][]int) (s string) {
	for _, r := range t {
		s = fmt.Sprintf("%s\n%v", s, r)
	}
	return
}

// BenchmarkPascalsTriangleFixed will generate Pascals Triangles against the
// solution using triangles of fixed size 20.
func BenchmarkPascalsTriangleFixed(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Triangle(testSize) // same length as the test for correctness above
	}
}

// BenchmarkPascalsTriangleIncreasing will generate Pascals Triangles against the
// solution using triangles of an increasingly larger size from 1 to 20.
func BenchmarkPascalsTriangleIncreasing(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for x := 0; x <= testSize; x++ {
			Triangle(x)
		}
	}
}
<|endoftext|>


<commit_before>package types

import (
	"encoding/xml"
	"time"

	dsigtypes "github.com/russellhaering/goxmldsig/types"
)

type EntityDescriptor struct {
	XMLName    xml.Name  `xml:"urn:oasis:names:tc:SAML:2.0:metadata EntityDescriptor"`
	ValidUntil time.Time `xml:"validUntil,attr"`
	// SAML 2.0 8.3.6 Entity Identifier could be used to represent issuer
	EntityID         string           `xml:"entityID,attr"`
	SPSSODescriptor  SPSSODescriptor  `xml:"SPSSODescriptor"`
	IDPSSODescriptor IDPSSODescriptor `xml:"IDPSSODescriptor"`
}

type Endpoint struct {
	Binding          string `xml:"Binding,attr"`
	Location         string `xml:"Location,attr"`
	ResponseLocation string `xml:"ResponseLocation,attr,omitempty"`
}

type IndexedEndpoint struct {
	Binding  string `xml:"Binding,attr"`
	Location string `xml:"Location,attr"`
	Index    int    `xml:"index,attr"`
}

type SPSSODescriptor struct {
	XMLName                    xml.Name          `xml:"urn:oasis:names:tc:SAML:2.0:metadata SPSSODescriptor"`
	AuthnRequestsSigned        bool              `xml:"AuthnRequestsSigned,attr"`
	WantAssertionsSigned       bool              `xml:"WantAssertionsSigned,attr"`
	ProtocolSupportEnumeration string            `xml:"protocolSupportEnumeration,attr"`
	KeyDescriptors             []KeyDescriptor   `xml:"KeyDescriptor"`
	SingleLogoutServices       []Endpoint        `xml:"SingleLogoutService"`
	NameIDFormats              []string          `xml:"NameIDFormat"`
	AssertionConsumerServices  []IndexedEndpoint `xml:"AssertionConsumerService"`
}

type IDPSSODescriptor struct {
	XMLName              xml.Name              `xml:"urn:oasis:names:tc:SAML:2.0:metadata IDPSSODescriptor"`
	KeyDescriptors       []KeyDescriptor       `xml:"KeyDescriptor"`
	NameIDFormats        []NameIDFormat        `xml:"NameIDFormat"`
	SingleSignOnServices []SingleSignOnService `xml:"SingleSignOnService"`
	Attributes           []Attribute           `xml:"Attribute"`
}

type KeyDescriptor struct {
	XMLName           xml.Name           `xml:"urn:oasis:names:tc:SAML:2.0:metadata KeyDescriptor"`
	Use               string             `xml:"use,attr"`
	KeyInfo           dsigtypes.KeyInfo  `xml:"KeyInfo"`
	EncryptionMethods []EncryptionMethod `xml:"EncryptionMethod"`
}

type NameIDFormat struct {
	XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata NameIDFormat"`
	Value   string   `xml:",chardata"`
}

type SingleSignOnService struct {
	XMLName  xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata SingleSignOnService"`
	Binding  string   `xml:"Binding,attr"`
	Location string   `xml:"Location,attr"`
}
<commit_msg>Added IDPSSODescriptor.WantAuthnRequestsSigned<commit_after>package types

import (
	"encoding/xml"
	"time"

	dsigtypes "github.com/russellhaering/goxmldsig/types"
)

type EntityDescriptor struct {
	XMLName    xml.Name  `xml:"urn:oasis:names:tc:SAML:2.0:metadata EntityDescriptor"`
	ValidUntil time.Time `xml:"validUntil,attr"`
	// SAML 2.0 8.3.6 Entity Identifier could be used to represent issuer
	EntityID         string           `xml:"entityID,attr"`
	SPSSODescriptor  SPSSODescriptor  `xml:"SPSSODescriptor"`
	IDPSSODescriptor IDPSSODescriptor `xml:"IDPSSODescriptor"`
}

type Endpoint struct {
	Binding          string `xml:"Binding,attr"`
	Location         string `xml:"Location,attr"`
	ResponseLocation string `xml:"ResponseLocation,attr,omitempty"`
}

type IndexedEndpoint struct {
	Binding  string `xml:"Binding,attr"`
	Location string `xml:"Location,attr"`
	Index    int    `xml:"index,attr"`
}

type SPSSODescriptor struct {
	XMLName                    xml.Name          `xml:"urn:oasis:names:tc:SAML:2.0:metadata SPSSODescriptor"`
	AuthnRequestsSigned        bool              `xml:"AuthnRequestsSigned,attr"`
	WantAssertionsSigned       bool              `xml:"WantAssertionsSigned,attr"`
	ProtocolSupportEnumeration string            `xml:"protocolSupportEnumeration,attr"`
	KeyDescriptors             []KeyDescriptor   `xml:"KeyDescriptor"`
	SingleLogoutServices       []Endpoint        `xml:"SingleLogoutService"`
	NameIDFormats              []string          `xml:"NameIDFormat"`
	AssertionConsumerServices  []IndexedEndpoint `xml:"AssertionConsumerService"`
}

type IDPSSODescriptor struct {
	XMLName                 xml.Name              `xml:"urn:oasis:names:tc:SAML:2.0:metadata IDPSSODescriptor"`
	WantAuthnRequestsSigned bool                  `xml:"WantAuthnRequestsSigned,attr"`
	KeyDescriptors          []KeyDescriptor       `xml:"KeyDescriptor"`
	NameIDFormats           []NameIDFormat        `xml:"NameIDFormat"`
	SingleSignOnServices    []SingleSignOnService `xml:"SingleSignOnService"`
	Attributes              []Attribute           `xml:"Attribute"`
}

type KeyDescriptor struct {
	XMLName           xml.Name           `xml:"urn:oasis:names:tc:SAML:2.0:metadata KeyDescriptor"`
	Use               string             `xml:"use,attr"`
	KeyInfo           dsigtypes.KeyInfo  `xml:"KeyInfo"`
	EncryptionMethods []EncryptionMethod `xml:"EncryptionMethod"`
}

type NameIDFormat struct {
	XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata NameIDFormat"`
	Value   string   `xml:",chardata"`
}

type SingleSignOnService struct {
	XMLName  xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata SingleSignOnService"`
	Binding  string   `xml:"Binding,attr"`
	Location string   `xml:"Location,attr"`
}
<|endoftext|>


<commit_before>package controllers

import (
	"encoding/json"
	"errors"
	"strconv"

	"github.com/lfq7413/tomato/errs"
	"github.com/lfq7413/tomato/rest"
	"github.com/lfq7413/tomato/types"
	"github.com/lfq7413/tomato/utils"
)

// ClassesController 对象操作 API 的基础结构
// 处理 /classes 接口的所有请求，处理内部类的部分请求
// ClassName 要操作的类名
// ObjectID 要操作的对象 id
type ClassesController struct {
	BaseController
	ClassName string
	ObjectID  string
}

// HandleCreate 处理对象创建请求，返回对象 id 与对象位置
// @router /:className [post]
func (c *ClassesController) HandleCreate() {

	if c.ClassName == "" {
		c.ClassName = c.Ctx.Input.Param(":className")
	}

	if c.JSONBody == nil {
		c.HandleError(errs.E(errs.InvalidJSON, "request body is empty"), 0)
		return
	}

	result, err := rest.Create(c.Auth, c.ClassName, c.JSONBody, c.Info.ClientSDK)
	if err != nil {
		c.HandleError(err, 0)
		return
	}

	c.Data["json"] = result["response"]
	c.Ctx.Output.SetStatus(201)
	c.Ctx.Output.Header("Location", result["location"].(string))
	c.ServeJSON()

}

// HandleGet 处理查询指定对象请求，返回查询到的对象
// @router /:className/:objectId [get]
func (c *ClassesController) HandleGet() {
	if c.ClassName == "" {
		c.ClassName = c.Ctx.Input.Param(":className")
	}
	if c.ObjectID == "" {
		c.ObjectID = c.Ctx.Input.Param(":objectId")
	}
	options := types.M{}
	if c.GetString("keys") != "" {
		options["keys"] = c.GetString("keys")
	}
	if c.GetString("include") != "" {
		options["include"] = c.GetString("include")
	}
	response, err := rest.Get(c.Auth, c.ClassName, c.ObjectID, options, c.Info.ClientSDK)

	if err != nil {
		c.HandleError(err, 0)
		return
	}

	results := utils.A(response["results"])
	if results == nil || len(results) == 0 {
		c.HandleError(errs.E(errs.ObjectNotFound, "Object not found."), 0)
		return
	}

	result := utils.M(results[0])

	if c.ClassName == "_User" {
		delete(result, "sessionToken")
		if c.Auth.User != nil && result["objectId"].(string) == c.Auth.User["objectId"].(string) {
			// 重新设置 session token
			result["sessionToken"] = c.Info.SessionToken
		}
	}

	c.Data["json"] = result
	c.ServeJSON()

}

// HandleUpdate 处理更新指定对象请求
// @router /:className/:objectId [put]
func (c *ClassesController) HandleUpdate() {

	if c.ClassName == "" {
		c.ClassName = c.Ctx.Input.Param(":className")
	}
	if c.ObjectID == "" {
		c.ObjectID = c.Ctx.Input.Param(":objectId")
	}

	if c.JSONBody == nil {
		c.HandleError(errs.E(errs.InvalidJSON, "request body is empty"), 0)
		return
	}

	result, err := rest.Update(c.Auth, c.ClassName, c.ObjectID, c.JSONBody, c.Info.ClientSDK)
	if err != nil {
		c.HandleError(err, 0)
		return
	}

	c.Data["json"] = result["response"]
	c.ServeJSON()

}

// HandleFind 处理查找对象请求
// @router /:className [get]
func (c *ClassesController) HandleFind() {
	if c.ClassName == "" {
		c.ClassName = c.Ctx.Input.Param(":className")
	}

	// 获取查询参数，并组装
	options := types.M{}
	if c.GetString("skip") != "" {
		if i, err := strconv.Atoi(c.GetString("skip")); err == nil {
			options["skip"] = i
		} else {
			c.HandleError(errs.E(errs.InvalidQuery, "skip should be int"), 0)
			return
		}
	}
	if c.GetString("limit") != "" {
		if i, err := strconv.Atoi(c.GetString("limit")); err == nil {
			options["limit"] = i
		} else {
			c.HandleError(errs.E(errs.InvalidQuery, "limit should be int"), 0)
			return
		}
	} else {
		options["limit"] = 100
	}
	if c.GetString("order") != "" {
		options["order"] = c.GetString("order")
	}
	if c.GetString("count") != "" {
		options["count"] = true
	}
	if c.GetString("keys") != "" {
		options["keys"] = c.GetString("keys")
	}
	if c.GetString("include") != "" {
		options["include"] = c.GetString("include")
	}
	if c.GetString("redirectClassNameForKey") != "" {
		options["redirectClassNameForKey"] = c.GetString("redirectClassNameForKey")
	}

	where := types.M{}
	if c.GetString("where") != "" {
		err := json.Unmarshal([]byte(c.GetString("where")), &where)
		if err != nil {
			c.HandleError(errs.E(errs.InvalidJSON, "where should be valid json"), 0)
			return
		}
	}

	response, err := rest.Find(c.Auth, c.ClassName, where, options, c.Info.ClientSDK)
	if err != nil {
		c.HandleError(err, 0)
		return
	}
	if utils.HasResults(response) {
		results := utils.A(response["results"])
		for _, v := range results {
			result := utils.M(v)
			if result["sessionToken"] != nil && c.Info.SessionToken != "" {
				result["sessionToken"] = c.Info.SessionToken
			}
		}
	}

	c.Data["json"] = response
	c.ServeJSON()
}

// HandleDelete 处理删除指定对象请求
// @router /:className/:objectId [delete]
func (c *ClassesController) HandleDelete() {

	if c.ClassName == "" {
		c.ClassName = c.Ctx.Input.Param(":className")
	}
	if c.ObjectID == "" {
		c.ObjectID = c.Ctx.Input.Param(":objectId")
	}

	err := rest.Delete(c.Auth, c.ClassName, c.ObjectID, c.Info.ClientSDK)
	if err != nil {
		c.HandleError(err, 0)
		return
	}

	c.Data["json"] = types.M{}
	c.ServeJSON()
}

// Get ...
// @router / [get]
func (c *ClassesController) Get() {
	c.HandleError(errors.New("Method Not Allowed"), 405)
}

// Post ...
// @router / [post]
func (c *ClassesController) Post() {
	c.HandleError(errors.New("Method Not Allowed"), 405)
}

// Delete ...
// @router / [delete]
func (c *ClassesController) Delete() {
	c.HandleError(errors.New("Method Not Allowed"), 405)
}

// Put ...
// @router / [put]
func (c *ClassesController) Put() {
	c.HandleError(errors.New("Method Not Allowed"), 405)
}
<commit_msg>修复部分格式转换问题<commit_after>package controllers

import (
	"encoding/json"
	"errors"
	"strconv"

	"github.com/lfq7413/tomato/errs"
	"github.com/lfq7413/tomato/rest"
	"github.com/lfq7413/tomato/types"
	"github.com/lfq7413/tomato/utils"
)

// ClassesController 对象操作 API 的基础结构
// 处理 /classes 接口的所有请求，处理内部类的部分请求
// ClassName 要操作的类名
// ObjectID 要操作的对象 id
type ClassesController struct {
	BaseController
	ClassName string
	ObjectID  string
}

// HandleCreate 处理对象创建请求，返回对象 id 与对象位置
// @router /:className [post]
func (c *ClassesController) HandleCreate() {
	if c.ClassName == "" {
		c.ClassName = c.Ctx.Input.Param(":className")
	}
	if c.JSONBody == nil {
		c.HandleError(errs.E(errs.InvalidJSON, "request body is empty"), 0)
		return
	}

	result, err := rest.Create(c.Auth, c.ClassName, c.JSONBody, c.Info.ClientSDK)
	if err != nil {
		c.HandleError(err, 0)
		return
	}

	c.Data["json"] = result["response"]
	c.Ctx.Output.SetStatus(201)
	c.Ctx.Output.Header("Location", utils.S(result["location"]))
	c.ServeJSON()
}

// HandleGet 处理查询指定对象请求，返回查询到的对象
// @router /:className/:objectId [get]
func (c *ClassesController) HandleGet() {
	if c.ClassName == "" {
		c.ClassName = c.Ctx.Input.Param(":className")
	}
	if c.ObjectID == "" {
		c.ObjectID = c.Ctx.Input.Param(":objectId")
	}

	options := types.M{}
	if c.GetString("keys") != "" {
		options["keys"] = c.GetString("keys")
	}
	if c.GetString("include") != "" {
		options["include"] = c.GetString("include")
	}

	response, err := rest.Get(c.Auth, c.ClassName, c.ObjectID, options, c.Info.ClientSDK)
	if err != nil {
		c.HandleError(err, 0)
		return
	}

	results := utils.A(response["results"])
	if results == nil || len(results) == 0 {
		c.HandleError(errs.E(errs.ObjectNotFound, "Object not found."), 0)
		return
	}

	result := utils.M(results[0])

	if c.ClassName == "_User" {
		delete(result, "sessionToken")
		if c.Auth.User != nil && utils.S(result["objectId"]) == utils.S(c.Auth.User["objectId"]) {
			// 重新设置 session token
			result["sessionToken"] = c.Info.SessionToken
		}
	}

	c.Data["json"] = result
	c.ServeJSON()
}

// HandleUpdate 处理更新指定对象请求
// @router /:className/:objectId [put]
func (c *ClassesController) HandleUpdate() {
	if c.ClassName == "" {
		c.ClassName = c.Ctx.Input.Param(":className")
	}
	if c.ObjectID == "" {
		c.ObjectID = c.Ctx.Input.Param(":objectId")
	}
	if c.JSONBody == nil {
		c.HandleError(errs.E(errs.InvalidJSON, "request body is empty"), 0)
		return
	}

	result, err := rest.Update(c.Auth, c.ClassName, c.ObjectID, c.JSONBody, c.Info.ClientSDK)
	if err != nil {
		c.HandleError(err, 0)
		return
	}

	c.Data["json"] = result["response"]
	c.ServeJSON()
}

// HandleFind 处理查找对象请求
// @router /:className [get]
func (c *ClassesController) HandleFind() {
	if c.ClassName == "" {
		c.ClassName = c.Ctx.Input.Param(":className")
	}

	// 获取查询参数，并组装
	options := types.M{}
	if c.GetString("skip") != "" {
		if i, err := strconv.Atoi(c.GetString("skip")); err == nil {
			options["skip"] = i
		} else {
			c.HandleError(errs.E(errs.InvalidQuery, "skip should be int"), 0)
			return
		}
	}
	if c.GetString("limit") != "" {
		if i, err := strconv.Atoi(c.GetString("limit")); err == nil {
			options["limit"] = i
		} else {
			c.HandleError(errs.E(errs.InvalidQuery, "limit should be int"), 0)
			return
		}
	} else {
		options["limit"] = 100
	}
	if c.GetString("order") != "" {
		options["order"] = c.GetString("order")
	}
	if c.GetString("count") != "" {
		options["count"] = true
	}
	if c.GetString("keys") != "" {
		options["keys"] = c.GetString("keys")
	}
	if c.GetString("include") != "" {
		options["include"] = c.GetString("include")
	}
	if c.GetString("redirectClassNameForKey") != "" {
		options["redirectClassNameForKey"] = c.GetString("redirectClassNameForKey")
	}

	where := types.M{}
	if c.GetString("where") != "" {
		err := json.Unmarshal([]byte(c.GetString("where")), &where)
		if err != nil {
			c.HandleError(errs.E(errs.InvalidJSON, "where should be valid json"), 0)
			return
		}
	}

	response, err := rest.Find(c.Auth, c.ClassName, where, options, c.Info.ClientSDK)
	if err != nil {
		c.HandleError(err, 0)
		return
	}
	if utils.HasResults(response) {
		results := utils.A(response["results"])
		for _, v := range results {
			result := utils.M(v)
			if result["sessionToken"] != nil && c.Info.SessionToken != "" {
				result["sessionToken"] = c.Info.SessionToken
			}
		}
	}

	c.Data["json"] = response
	c.ServeJSON()
}

// HandleDelete 处理删除指定对象请求
// @router /:className/:objectId [delete]
func (c *ClassesController) HandleDelete() {
	if c.ClassName == "" {
		c.ClassName = c.Ctx.Input.Param(":className")
	}
	if c.ObjectID == "" {
		c.ObjectID = c.Ctx.Input.Param(":objectId")
	}

	err := rest.Delete(c.Auth, c.ClassName, c.ObjectID, c.Info.ClientSDK)
	if err != nil {
		c.HandleError(err, 0)
		return
	}

	c.Data["json"] = types.M{}
	c.ServeJSON()
}

// Get ...
// @router / [get]
func (c *ClassesController) Get() {
	c.HandleError(errors.New("Method Not Allowed"), 405)
}

// Post ...
// @router / [post]
func (c *ClassesController) Post() {
	c.HandleError(errors.New("Method Not Allowed"), 405)
}

// Delete ...
// @router / [delete]
func (c *ClassesController) Delete() {
	c.HandleError(errors.New("Method Not Allowed"), 405)
}

// Put ...
// @router / [put]
func (c *ClassesController) Put() {
	c.HandleError(errors.New("Method Not Allowed"), 405)
}
<|endoftext|>


<commit_before>package pagerduty

import "net/http"

// IncidentsService type
type IncidentsService struct {
	client *Client
}

// Incident type
type Incident struct {
	ID                    string            `json:"id,omitempty"`
	IncidentNumber        int               `json:"incident_number,omitempty"`
	Status                string            `json:"status,omitempty"`
	CreatedOn             string            `json:"created_on,omitempty"`
	Summary               *IncidentSummary  `json:"trigger_summary_data,omitempty"`
	User                  *User             `json:"assigned_to_user,omitempty"`
	Service               *Service          `json:"service,omitempty"`
	EscalationPolicy      *EscalationPolicy `json:"escalation_policy,omitempty"`
	HTMLURL               string            `json:"html_url,omitempty"`
	IncidentKey           string            `json:"incident_key,omitempty"`
	TriggerDetailsHTMLURL string            `json:"trigger_details_html_url,omitempty"`
	TriggerType           string            `json:"trigger_type,omitempty"`
	LastStatusChangeOn    string            `json:"last_status_change_on,omitempty"`
	LastStatusChangeBy    *User             `json:"last_status_change_by,omitempty"`
	NumberOfEscalations   int               `json:"number_of_escalations,omitempty"`
	ResolvedByUser        *User             `json:"resolved_by_user,omitempty"`
	AssignedToUser        *User             `json:"assigned_to_user,omitempty"`
	AssignedTo            []*User           `json:"assigned_to,omitempty"`
}

// Incidents is a list of incidents
type Incidents struct {
	Incidents []Incident
}

// IncidentSummary type
type IncidentSummary struct {
	Subject     string //`json:"subject,omitempty"`
	Description string //`json:"description,omitempty"`
}

// Get returns a single incident by id if found
func (s *IncidentsService) Get(id string) (*Incident, *http.Response, error) {
	incident := new(Incident)

	res, err := s.client.Get("incidents/"+id, incident)
	if err != nil {
		return nil, res, err
	}

	return incident, res, nil
}

// IncidentsOptions provides optional parameters to list requests
type IncidentsOptions struct {
	Status string `url:"status,omitempty"`
	SortBy string `url:"sort_by,omitempty"`
	Since  string `url:"since,omitempty"`
	Until  string `url:"until,omitempty"`
}

// List returns a list of incidents
func (s *IncidentsService) List(opt *IncidentsOptions) ([]Incident, *http.Response, error) {
	u, err := addOptions("incidents", opt)
	if err != nil {
		return nil, nil, err
	}

	incidents := new(Incidents)

	res, err := s.client.Get(u, incidents)
	if err != nil {
		return nil, res, err
	}

	return incidents.Incidents, res, err
}
<commit_msg>implement pagination<commit_after>package pagerduty

import "net/http"

// IncidentsService type
type IncidentsService struct {
	client *Client
}

// Incident type
type Incident struct {
	ID                    string            `json:"id,omitempty"`
	IncidentNumber        int               `json:"incident_number,omitempty"`
	Status                string            `json:"status,omitempty"`
	CreatedOn             string            `json:"created_on,omitempty"`
	Summary               *IncidentSummary  `json:"trigger_summary_data,omitempty"`
	User                  *User             `json:"assigned_to_user,omitempty"`
	Service               *Service          `json:"service,omitempty"`
	EscalationPolicy      *EscalationPolicy `json:"escalation_policy,omitempty"`
	HTMLURL               string            `json:"html_url,omitempty"`
	IncidentKey           string            `json:"incident_key,omitempty"`
	TriggerDetailsHTMLURL string            `json:"trigger_details_html_url,omitempty"`
	TriggerType           string            `json:"trigger_type,omitempty"`
	LastStatusChangeOn    string            `json:"last_status_change_on,omitempty"`
	LastStatusChangeBy    *User             `json:"last_status_change_by,omitempty"`
	NumberOfEscalations   int               `json:"number_of_escalations,omitempty"`
	ResolvedByUser        *User             `json:"resolved_by_user,omitempty"`
	AssignedToUser        *User             `json:"assigned_to_user,omitempty"`
	AssignedTo            []*User           `json:"assigned_to,omitempty"`
}

// Incidents is a list of incidents
type Incidents struct {
	Incidents []Incident
	Limit     int
	Offset    int
	Total     int
}

// IncidentSummary type
type IncidentSummary struct {
	Subject     string //`json:"subject,omitempty"`
	Description string //`json:"description,omitempty"`
}

// Get returns a single incident by id if found
func (s *IncidentsService) Get(id string) (*Incident, *http.Response, error) {
	incident := new(Incident)

	res, err := s.client.Get("incidents/"+id, incident)
	if err != nil {
		return nil, res, err
	}

	return incident, res, nil
}

// IncidentsOptions provides optional parameters to list requests
type IncidentsOptions struct {
	Status    string `url:"status,omitempty"`
	SortBy    string `url:"sort_by,omitempty"`
	Since     string `url:"since,omitempty"`
	Until     string `url:"until,omitempty"`
	Service   string `url:"service,omitempty"`
	DateRange string `url:"date_range,omitempty"`
	Offset    int    `url:"offset,omitempty"`
}

// List returns a list of incidents
func (s *IncidentsService) List(opt *IncidentsOptions) ([]Incident, error) {
	result := []Incident{}
	for {
		u, err := addOptions("incidents", opt)
		if err != nil {
			return nil, err
		}
		incidents := new(Incidents)
		_, err = s.client.Get(u, incidents)
		if err != nil {
			return nil, err
		}
		result = append(result, incidents.Incidents...)
		if incidents.Limit+incidents.Offset > incidents.Total {
			break
		}
		opt.Offset += incidents.Limit
	}
	return result, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 Rene Richard

This file is part of zmq-soundtouch.

zmq-soundtouch is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

zmq-soundtouch is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with zmq-soundtouch.  If not, see <http://www.gnu.org/licenses/>.
*/

package messaging

import (
	"fmt"
	zmq "github.com/pebbe/zmq4"
	"strings"
)

type dealer struct {
	ctx      *zmq.Context
	msgChan  chan string
	doneChan chan bool
	errChan  chan error
	client   *zmq.Socket
}

func NewDealer() *dealer {
	ctx, _ := zmq.NewContext()

	msgChan := make(chan string)
	doneChan := make(chan bool)
	errChan := make(chan error)

	client, err := ctx.NewSocket(zmq.DEALER)

	//TODO fix...log and bail
	if err != nil {
		fmt.Println(err)
	}

	return &dealer{ctx, msgChan, doneChan, errChan, client}
}

func (this *dealer) processMessages() {
	for {
		select {
		//We receive a message on the message channel
		case msg := <-this.msgChan:
			fmt.Println("Processing Message: " + msg)
		//We have an error on the error channel
		case err := <-this.errChan:
			fmt.Println("Error in Processing Messages: ", err)
			this.errChan <- err // for receiveMessages method
			return
		//We're done
		case <-this.doneChan:
			fmt.Println("Done Processing Messages")
			return
		}
	}
}

func (this *dealer) receiveMessages() {
	for {
		select {
		//We have an error
		case err := <-this.errChan:
			this.errChan <- err // to notify processMessages()
			return

		// read data from socket connection (loop)
		default:
			reply, err := this.client.Recv(0)

			if err != nil {
				this.errChan <- err
			}
			if strings.Compare(reply, "KTHXBYE") == 0 {
				fmt.Println("Done reading cache")
				this.doneChan <- true //to notify processMessages()
				return
			} else {
				this.msgChan <- reply //will be picked up by processMessages()
			}

		}
	}
}

func (this *dealer) Start() {
	defer this.ctx.Term()

	//TODO log
	fmt.Println("Starting...")

	//TODO config
	this.client.Connect("tcp://127.0.0.1:8000")
	defer this.client.Close()

	//TODO config
	this.client.Send("ICANHAZ?", 0)

	go this.processMessages()
	this.receiveMessages()
}
<commit_msg>initial impl of zmqealer.go : no logging<commit_after>/*
Copyright 2016 Rene Richard

This file is part of zmq-soundtouch.

zmq-soundtouch is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

zmq-soundtouch is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with zmq-soundtouch.  If not, see <http://www.gnu.org/licenses/>.
*/

package messaging

import (
	"fmt"
	zmq "github.com/pebbe/zmq4"
	"strings"
)

const (
	ROUTER_URL        = "tcp://127.0.0.1:8000"
	CACHE_END_TOKEN   = "KTHXBYE"
	CACHE_START_TOKEN = "ICANHAZ?"
)

type dealer struct {
	ctx      *zmq.Context
	msgChan  chan string
	doneChan chan bool
	errChan  chan error
	client   *zmq.Socket
}

func NewDealer() *dealer {
	ctx, _ := zmq.NewContext()

	msgChan := make(chan string)
	doneChan := make(chan bool)
	errChan := make(chan error)

	client, err := ctx.NewSocket(zmq.DEALER)

	//TODO fix...log and bail
	if err != nil {
		fmt.Println(err)
	}

	return &dealer{ctx, msgChan, doneChan, errChan, client}
}

func (this *dealer) processMessages() {
	fmt.Println("Firing up processMessages loop")
	for {
		select {
		//We receive a message on the message channel
		case msg := <-this.msgChan:
			fmt.Println("Processing Message: " + msg)
		//We have an error on the error channel
		case err := <-this.errChan:
			fmt.Println("Error : ", err)
			return
		//We're done
		case <-this.doneChan:
			fmt.Println("Done Processing Messages")
			return
		}
	}
}

func (this *dealer) receiveMessages() {
	fmt.Println("Firing up receiveMessages loop")
	for {
		select {
		//We have an error
		case err := <-this.errChan:
			fmt.Println("Error :", err)
			this.errChan <- err // to notify processMessages()
			return

		//Read data from socket connection (loop)
		default:
			reply, err := this.client.Recv(0)

			if err != nil {
				this.errChan <- err
				return
			}
			if strings.Compare(reply, CACHE_END_TOKEN) == 0 {
				fmt.Println("Done reading cache")
				this.doneChan <- true //to notify processMessages()
				return
			} else {
				this.msgChan <- reply //will be picked up by processMessages()
			}
		}
	}
}

func (this *dealer) Start() {
	defer this.ctx.Term()

	//TODO log
	fmt.Println("Starting Dealer ...")

	//TODO config
	this.client.Connect(ROUTER_URL)
	defer this.client.Close()

	fmt.Println("Sending request for cache conntents")
	this.client.Send(CACHE_START_TOKEN, 0)

	go this.processMessages()
	this.receiveMessages()
}
<|endoftext|>


<commit_before>package corehttp

import (
	"fmt"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"

	core "github.com/ipfs/go-ipfs/core"

	protocol "gx/ipfs/QmZNkThpqfVXs9GNbexPrfBbXSLNYeKrE7jwFM2oqHbyqN/go-libp2p-protocol"
	p2phttp "gx/ipfs/QmcLYfmHLsaVRKGMZQovwEYhHAjWtRjg1Lij3pnzw5UkRD/go-libp2p-http"
)

// ProxyOption is an endpoint for proxying a HTTP request to another ipfs peer
func ProxyOption() ServeOption {
	return func(ipfsNode *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
		mux.HandleFunc("/proxy/http/", func(w http.ResponseWriter, request *http.Request) {
			// parse request
			parsedRequest, err := parseRequest(request)
			if err != nil {
				handleError(w, "Failed to parse request", err, 400)
				return
			}

			request.Host = "" // Let URL's Host take precedence.
			target, err := url.Parse(fmt.Sprintf("libp2p://%s/%s", parsedRequest.target, parsedRequest.httpPath))
			if err != nil {
				handleError(w, "Failed to parse url", err, 400)
				return
			}

			rt := p2phttp.NewTransport(ipfsNode.P2P.PeerHost, p2phttp.ProtocolOption(parsedRequest.name))
			proxy := httputil.NewSingleHostReverseProxy(target)
			proxy.Transport = rt
			proxy.ServeHTTP(w, request)
		})
		return mux, nil
	}
}

type proxyRequest struct {
	target   string
	name     protocol.ID
	httpPath string // path to send to the proxy-host
}

// from the url path parse the peer-ID, name and http path
// /proxy/http/$peer_id/$name/$http_path
func parseRequest(request *http.Request) (*proxyRequest, error) {
	path := request.URL.Path

	split := strings.SplitN(path, "/", 6)
	if len(split) < 6 {
		return nil, fmt.Errorf("Invalid request path '%s'", path)
	}

	return &proxyRequest{split[3], protocol.ID(split[4]), "/" + split[5]}, nil
}

func handleError(w http.ResponseWriter, msg string, err error, code int) {
	w.WriteHeader(code)
	fmt.Fprintf(w, "%s: %s\n", msg, err)
	log.Warningf("server error: %s: %s", err)
}
<commit_msg>Fix request path. Wasn't using proxy correctly<commit_after>package corehttp

import (
	"fmt"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"

	core "github.com/ipfs/go-ipfs/core"

	protocol "gx/ipfs/QmZNkThpqfVXs9GNbexPrfBbXSLNYeKrE7jwFM2oqHbyqN/go-libp2p-protocol"
	p2phttp "gx/ipfs/QmcLYfmHLsaVRKGMZQovwEYhHAjWtRjg1Lij3pnzw5UkRD/go-libp2p-http"
)

// ProxyOption is an endpoint for proxying a HTTP request to another ipfs peer
func ProxyOption() ServeOption {
	return func(ipfsNode *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
		mux.HandleFunc("/proxy/http/", func(w http.ResponseWriter, request *http.Request) {
			// parse request
			parsedRequest, err := parseRequest(request)
			if err != nil {
				handleError(w, "Failed to parse request", err, 400)
				return
			}

			request.Host = "" // Let URL's Host take precedence.
			request.URL.Path = parsedRequest.httpPath
			target, err := url.Parse(fmt.Sprintf("libp2p://%s", parsedRequest.target))
			if err != nil {
				handleError(w, "Failed to parse url", err, 400)
				return
			}

			rt := p2phttp.NewTransport(ipfsNode.P2P.PeerHost, p2phttp.ProtocolOption(parsedRequest.name))
			proxy := httputil.NewSingleHostReverseProxy(target)
			proxy.Transport = rt
			proxy.ServeHTTP(w, request)
		})
		return mux, nil
	}
}

type proxyRequest struct {
	target   string
	name     protocol.ID
	httpPath string // path to send to the proxy-host
}

// from the url path parse the peer-ID, name and http path
// /proxy/http/$peer_id/$name/$http_path
func parseRequest(request *http.Request) (*proxyRequest, error) {
	path := request.URL.Path

	split := strings.SplitN(path, "/", 6)
	if len(split) < 6 {
		return nil, fmt.Errorf("Invalid request path '%s'", path)
	}

	return &proxyRequest{split[3], protocol.ID(split[4]), split[5]}, nil
}

func handleError(w http.ResponseWriter, msg string, err error, code int) {
	w.WriteHeader(code)
	fmt.Fprintf(w, "%s: %s\n", msg, err)
	log.Warningf("server error: %s: %s", err)
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Hardik Bagdi <hbagdi1@binghamton.edu>
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package unsplash

import (
	"bytes"
	"strconv"
)

// GlobalStats shows the total photo stats of Unsplash.com
type GlobalStats struct {
	TotalPhotos    int `json:"total_photos,omitempty"`
	PhotoDownloads int `json:"photo_downloads,omitempty"`
	BatchDownloads int `json:"batch_downloads,omitempty"`
}

func (G *GlobalStats) String() string {
	var buf bytes.Buffer
	buf.WriteString("\nGlobal Stats:\n")
	buf.WriteString("Total Photos: " + strconv.Itoa(G.TotalPhotos) + "\n")
	buf.WriteString("Total downloads: " + strconv.Itoa(G.PhotoDownloads) + "\n")
	buf.WriteString("Batch downloads: " + strconv.Itoa(G.BatchDownloads) + "\n")
	return buf.String()
}
<commit_msg>Add custom UnmarshalJSON to GlobalStats; fixes #6<commit_after>// Copyright (c) 2017 Hardik Bagdi <hbagdi1@binghamton.edu>
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package unsplash

import (
	"bytes"
	"encoding/json"
	"strconv"
	"strings"
)

// GlobalStats shows the total photo stats of Unsplash.com
type GlobalStats struct {
	TotalPhotos    int `json:"total_photos,omitempty"`
	PhotoDownloads int `json:"photo_downloads,omitempty"`
	BatchDownloads int `json:"batch_downloads,omitempty"`
}

// processNumber converts a string or float or int representation into an int
// this hack is needed because the API strangely returns a float value in quotes in the JSON response for this endpoint
func processNumber(i interface{}) int {
	switch v := i.(type) {
	case int:
		return v
	case string:
		s := strings.Split(i.(string), ".")
		n, _ := strconv.Atoi(s[0])
		return n
	case float64:
		return int(v)
	}
	return 0
}

// UnmarshalJSON converts a JSON string representation of GlobalStats into a struct
func (gs *GlobalStats) UnmarshalJSON(b []byte) error {
	var f interface{}
	err := json.Unmarshal(b, &f)
	if err != nil {
		return err
	}
	m := f.(map[string]interface{})
	if _, ok := m["total_photos"]; ok {
		gs.TotalPhotos = processNumber(m["total_photos"])
	}
	if _, ok := m["photo_downloads"]; ok {
		gs.PhotoDownloads = processNumber(m["photo_downloads"])
	}
	if _, ok := m["batch_downloads"]; ok {
		gs.BatchDownloads = processNumber(m["batch_downloads"])
	}
	return nil
}
func (G *GlobalStats) String() string {
	var buf bytes.Buffer
	buf.WriteString("\nGlobal Stats:\n")
	buf.WriteString("Total Photos: " + strconv.Itoa(G.TotalPhotos) + "\n")
	buf.WriteString("Total downloads: " + strconv.Itoa(G.PhotoDownloads) + "\n")
	buf.WriteString("Batch downloads: " + strconv.Itoa(G.BatchDownloads) + "\n")
	return buf.String()
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

func UploadHandler(w http.ResponseWriter, r *http.Request) {
	delete_id := ""
	final_id := ""
	real_ip_t := r.Header.Get("X-Real-Ip")
    perm := os.ModeDir | 0744

	if real_ip_t == "" {
		real_ip_t = r.RemoteAddr
	}

	if RateLimit(real_ip_t) {
		fmt.Fprintf(w, MakeResult(r, "rate", ""))
		return
	}

	r.ParseMultipartForm(CONF_MAX_FILESIZE)
    log.Printf("Received request: %+v\n", r.MultipartForm)
	if r.MultipartForm != nil {
		if _, ok := r.MultipartForm.File["upload"]; ok {
			if len(r.MultipartForm.File["upload"]) == 0 {
				fmt.Fprintf(w, MakeResult(r, "error", ""))
				return
			}
			files_t := r.MultipartForm.File["upload"]
			for _, file_t := range files_t {
				buffer_t := make([]byte, CONF_MAX_FILESIZE+1)
				f, err := file_t.Open()
				defer f.Close()

				size_t, err := f.Read(buffer_t)
				if err != nil {
					fmt.Fprintf(w, MakeResult(r, "error", ""))
					return
				}
				if size_t > CONF_MAX_FILESIZE+1 {
					fmt.Fprintf(w, MakeResult(r, "size", ""))
					return
				}

				buffer_t = buffer_t[:size_t]

				md5_t := md5.New()
				md5_t.Write(buffer_t)
				hash_t := hex.EncodeToString(md5_t.Sum(nil))
				delete_id = UniqueID(30, false)
				exists_t, _ := Exists(HASH_DIR + hash_t + "/")
				if exists_t {
					old_id := GetIDHash(hash_t)
					final_id = UniqueID(8, true)
					os.Mkdir(UPLOAD_DIR+final_id, perm)

					files_t, _ := ioutil.ReadDir(UPLOAD_DIR + old_id + "/")

					filename := ""
					for a := range files_t {
						if files_t[a].Name() != "." && files_t[a].Name() != ".." {
							filename = files_t[a].Name()
							break
						}
					}

					fmt.Println(os.Symlink(UPLOAD_DIR+old_id+"/"+filename, UPLOAD_DIR+final_id+"/"+base64.StdEncoding.EncodeToString([]byte(r.Header["Up-Filename"][0]))))
					os.Mkdir(DELETE_DIR+delete_id, perm)
					WriteEmptyFile(DELETE_DIR + delete_id + "/" + final_id)
				} else {
					final_id = UniqueID(8, true)
					os.Mkdir(UPLOAD_DIR+final_id, perm)
					if WriteFileSafe(UPLOAD_DIR+final_id+"/"+base64.StdEncoding.EncodeToString([]byte(r.Header["Up-Filename"][0])), buffer_t) == false {
						fmt.Fprintf(w, MakeResult(r, "error", ""))
						return
					}
					os.Mkdir(HASH_DIR+hash_t, perm)
					WriteEmptyFile(HASH_DIR + hash_t + "/" + final_id)
					os.Mkdir(DELETE_DIR+delete_id, perm)
					WriteEmptyFile(DELETE_DIR + delete_id + "/" + final_id)
					/* Check image size and create forcedl here */
				}

			}
		} else {
			fmt.Fprintf(w, MakeResult(r, "error", ""))
			return
		}
	} else {
		p := new(bytes.Buffer)
		p.ReadFrom(r.Body)
		final_dat := p.Bytes()
		if r.FormValue("base64") == "true" {
			base64_t, _ := base64.StdEncoding.DecodeString(p.String())
			final_dat = []byte(base64_t)
		}

		if len(final_dat) > CONF_MAX_FILESIZE+1 {
			fmt.Fprintf(w, MakeResult(r, "error", ""))
			return
		}

		md5_t := md5.New()
		md5_t.Write(final_dat)
		hash_t := hex.EncodeToString(md5_t.Sum(nil))
		delete_id = UniqueID(30, false)
		exists_t, _ := Exists(HASH_DIR + hash_t + "/")
		if exists_t {
			old_id := GetIDHash(hash_t)
			files_t, _ := ioutil.ReadDir(UPLOAD_DIR + old_id + "/")

			filename := ""
			for a := range files_t {
				if files_t[a].Name() != "." && files_t[a].Name() != ".." {
					filename = files_t[a].Name()
					break
				}
			}

			filename_new := base64.StdEncoding.EncodeToString([]byte(r.Header["Up-Filename"][0]))
			if filename_new != filename {
				final_id = UniqueID(8, true)
				os.Mkdir(UPLOAD_DIR+final_id, perm)
				fmt.Println(os.Symlink(UPLOAD_DIR+old_id+"/"+filename, UPLOAD_DIR+final_id+"/"+base64.StdEncoding.EncodeToString([]byte(r.Header["Up-Filename"][0]))))
				os.Mkdir(DELETE_DIR+delete_id, perm)
				WriteEmptyFile(DELETE_DIR + delete_id + "/" + final_id)
			} else {
				final_id = old_id
			}
		} else {
			final_id = UniqueID(8, true)
			os.Mkdir(UPLOAD_DIR+final_id, perm)
			if WriteFileSafe(UPLOAD_DIR+final_id+"/"+base64.StdEncoding.EncodeToString([]byte(r.Header["Up-Filename"][0])), final_dat) == false {
				fmt.Fprintf(w, MakeResult(r, "error", ""))
				return
			}
			os.Mkdir(HASH_DIR+hash_t, perm)
			WriteEmptyFile(HASH_DIR + hash_t + "/" + final_id)
			os.Mkdir(DELETE_DIR+delete_id, perm)
			WriteEmptyFile(DELETE_DIR + delete_id + "/" + final_id)
		}
	}
	log.Printf("[LOG] File uploaded, assigned ID %s with deletion ID %s\n", final_id, delete_id)
	fmt.Fprintf(w, MakeResult(r, final_id, delete_id))
}
<commit_msg>Rewrote upload_handler.go to work with jquery-file-upload<commit_after>package main

import (
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

type UploadedFile struct {
	Name  string
	ID    string
	Error string
}

func UploadHandler(w http.ResponseWriter, r *http.Request) {
	delete_id := ""
	final_id := ""
	real_ip_t := r.Header.Get("X-Real-Ip")
	perm := os.ModeDir | 0744

	if real_ip_t == "" {
		real_ip_t = r.RemoteAddr
	}

	if RateLimit(real_ip_t) {
		fmt.Fprintf(w, MakeResult(r, "rate", ""))
		return
	}

	r.ParseMultipartForm(CONF_MAX_FILESIZE)
	log.Printf("Received request: %+v\n", r.MultipartForm)
    var ret_files []UploadedFile
	if _, ok := r.MultipartForm.File["upload"]; ok {
		if len(r.MultipartForm.File["upload"]) == 0 {
			fmt.Fprintf(w, MakeResult(r, "error", ""))
			return
		}
		files_t := r.MultipartForm.File["upload"]
        ret_files = make([]UploadedFile, len(r.MultipartForm.File["upload"]))
		for _, file_t := range files_t {
			buffer_t := make([]byte, CONF_MAX_FILESIZE+1)
			f, err := file_t.Open()
			defer f.Close()

			size_t, err := f.Read(buffer_t)
			if err != nil {
				ret_files = append(ret_files, UploadedFile{"", "", "error"})
				continue
			}
			if size_t > CONF_MAX_FILESIZE+1 {
				ret_files = append(ret_files, UploadedFile{"", "", "error"})
				continue
			}

			buffer_t = buffer_t[:size_t]

			md5_t := md5.New()
			md5_t.Write(buffer_t)
			hash_t := hex.EncodeToString(md5_t.Sum(nil))
			delete_id = UniqueID(30, false)
			exists_t, _ := Exists(HASH_DIR + hash_t + "/")
			if exists_t {
				old_id := GetIDHash(hash_t)
				final_id = UniqueID(8, true)
				os.Mkdir(UPLOAD_DIR+final_id, perm)

				files_t, _ := ioutil.ReadDir(UPLOAD_DIR + old_id + "/")

				filename := ""
				for a := range files_t {
					if files_t[a].Name() != "." && files_t[a].Name() != ".." {
						filename = files_t[a].Name()
						break
					}
				}

				fmt.Println(os.Symlink(UPLOAD_DIR+old_id+"/"+filename, UPLOAD_DIR+final_id+"/"+base64.StdEncoding.EncodeToString([]byte(file_t.Filename))))
				os.Mkdir(DELETE_DIR+delete_id, perm)
				WriteEmptyFile(DELETE_DIR + delete_id + "/" + final_id)
			} else {
				final_id = UniqueID(8, true)
				os.Mkdir(UPLOAD_DIR+final_id, perm)
				if WriteFileSafe(UPLOAD_DIR+final_id+"/"+base64.StdEncoding.EncodeToString([]byte(file_t.Filename)), buffer_t) == false {
                    ret_files = append(ret_files, UploadedFile{"", "", "error"})
					continue
				}
				os.Mkdir(HASH_DIR+hash_t, perm)
				WriteEmptyFile(HASH_DIR + hash_t + "/" + final_id)
				os.Mkdir(DELETE_DIR+delete_id, perm)
				WriteEmptyFile(DELETE_DIR + delete_id + "/" + final_id)
				/* Check image size and create forcedl here */
			}
            ret_files = append(ret_files, UploadedFile{file_t.Filename, final_id, ""})
		}
	} else {
		fmt.Fprintf(w, MakeResult(r, "error", ""))
		return
	}
	log.Printf("[LOG] File uploaded, assigned ID %s with deletion ID %s\n", final_id, delete_id)
	//fmt.Fprintf(w, MakeResult(r, final_id, delete_id))
	json_out, err := json.Marshal(map[string][]UploadedFile{"files": ret_files})
    if err != nil {
        panic(err)
    }
	fmt.Fprintf(w, string(json_out))
}
<|endoftext|>


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

package cmd

import (
	"fmt"
	"net/http"

	"github.com/Unknwon/log"
	"github.com/codegangsta/cli"
	"github.com/go-macaron/i18n"
	"github.com/go-macaron/pongo2"
	"gopkg.in/macaron.v1"

	// 项目模块:
	"github.com/peachdocs/peach/models"                       // 数据库模型
	"github.com/peachdocs/peach/modules/middleware"           // 中间件
	"github.com/peachdocs/peach/modules/setting"              // 配置部分
	"github.com/peachdocs/peach/routers"                      // 路由部分
)

/*
	关键入口:
	- 从命令行获取参数, 执行服务启动操作
	- Action: 执行启动操作
 */
var Web = cli.Command{
	Name:   "web",
	Usage:  "Start Peach web server",
	Action: runWeb,		// todo: 关键入口
	Flags: []cli.Flag{
		stringFlag("config, c", "custom/app.ini", "Custom configuration file path"),
	},
}

/*
	关键入口:
	- 创建项目实例, 并启动服务

 */
func runWeb(ctx *cli.Context) {
	if ctx.IsSet("config") {
		setting.CustomConf = ctx.String("config")
	}
	setting.NewContext()
	models.NewContext()

	log.Info("Peach %s", setting.AppVer)

	//---------------------------------------
	//          关键入口:
	//---------------------------------------
	// 创建web 服务对象
	m := macaron.New()

	// 日志
	m.Use(macaron.Logger())
	m.Use(macaron.Recovery())

	// 静态资源处理
	m.Use(macaron.Statics(macaron.StaticOptions{
		SkipLogging: setting.ProdMode,
	}, "custom/public", "public", models.HTMLRoot))

	m.Use(i18n.I18n(i18n.Options{
		Files:       setting.Docs.Locales,
		DefaultLang: setting.Docs.Langs[0],
	}))

	// 模板资源:
	tplDir := "templates"
	if setting.Page.UseCustomTpl {
		tplDir = "custom/templates"
	}
	m.Use(pongo2.Pongoer(pongo2.Options{
		Directory: tplDir,
	}))

	// 服务中间件:
	m.Use(middleware.Contexter())  // todo: 留意该自定义组件.

	//---------------------------------------
	//          路由配置:
	//---------------------------------------
	m.Get("/", routers.Home)		// 首页部分
	m.Get("/docs", routers.Docs)
	m.Get("/docs/images/*", routers.DocsStatic)
	m.Get("/docs/*", routers.Protect, routers.Docs)
	m.Post("/hook", routers.Hook)
	m.Get("/search", routers.Search)
	m.Get("/*", routers.Pages)

	m.NotFound(routers.NotFound)

	listenAddr := fmt.Sprintf("0.0.0.0:%d", setting.HTTPPort)	// 设置服务 IP + 端口
	log.Info("%s Listen on %s", setting.Site.Name, listenAddr)
	log.Fatal("Fail to start Peach: %v", http.ListenAndServe(listenAddr, m))   // 启动服务
}
<commit_msg>update - 注解关键路由分支.<commit_after>// Copyright 2015 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package cmd

import (
	"fmt"
	"net/http"

	"github.com/Unknwon/log"
	"github.com/codegangsta/cli"
	"github.com/go-macaron/i18n"
	"github.com/go-macaron/pongo2"
	"gopkg.in/macaron.v1"

	// 项目模块:
	"github.com/peachdocs/peach/models"                       // 数据库模型
	"github.com/peachdocs/peach/modules/middleware"           // 中间件
	"github.com/peachdocs/peach/modules/setting"              // 配置部分
	"github.com/peachdocs/peach/routers"                      // 路由部分
)

/*
	关键入口:
	- 从命令行获取参数, 执行服务启动操作
	- Action: 执行启动操作
 */
var Web = cli.Command{
	Name:   "web",
	Usage:  "Start Peach web server",
	Action: runWeb,		// todo: 关键入口
	Flags: []cli.Flag{
		stringFlag("config, c", "custom/app.ini", "Custom configuration file path"),
	},
}

/*
	关键入口:
	- 创建项目实例, 并启动服务

 */
func runWeb(ctx *cli.Context) {
	if ctx.IsSet("config") {
		setting.CustomConf = ctx.String("config")
	}
	setting.NewContext()
	models.NewContext()

	log.Info("Peach %s", setting.AppVer)

	//---------------------------------------
	//          关键入口:
	//---------------------------------------
	// 创建web 服务对象
	m := macaron.New()

	// 日志
	m.Use(macaron.Logger())
	m.Use(macaron.Recovery())

	// 静态资源处理
	m.Use(macaron.Statics(macaron.StaticOptions{
		SkipLogging: setting.ProdMode,
	}, "custom/public", "public", models.HTMLRoot))

	m.Use(i18n.I18n(i18n.Options{
		Files:       setting.Docs.Locales,
		DefaultLang: setting.Docs.Langs[0],
	}))

	// 模板资源:
	tplDir := "templates"
	if setting.Page.UseCustomTpl {
		tplDir = "custom/templates"
	}
	m.Use(pongo2.Pongoer(pongo2.Options{
		Directory: tplDir,
	}))

	// 服务中间件:
	m.Use(middleware.Contexter())  // todo: 留意该自定义组件.

	//---------------------------------------
	//          路由配置:
	//---------------------------------------
	m.Get("/", routers.Home)		// 首页部分: [github.com/peachdocs/peach/routers/home.go:30]
	m.Get("/docs", routers.Docs)
	m.Get("/docs/images/*", routers.DocsStatic)
	m.Get("/docs/*", routers.Protect, routers.Docs)
	m.Post("/hook", routers.Hook)
	m.Get("/search", routers.Search)
	m.Get("/*", routers.Pages)		// [页面遍历搜索, 找到即渲染页面]github.com/peachdocs/peach/routers/home.go:39

	m.NotFound(routers.NotFound)

	listenAddr := fmt.Sprintf("0.0.0.0:%d", setting.HTTPPort)	// 设置服务 IP + 端口
	log.Info("%s Listen on %s", setting.Site.Name, listenAddr)
	log.Fatal("Fail to start Peach: %v", http.ListenAndServe(listenAddr, m))   // 启动服务
}
<|endoftext|>


<commit_before>// Copyright 2012 The Plotinum Authors. All rights reserved.
// Use of this source code is governed by an MIT-style license
// that can be found in the LICENSE file.

// The vgsvg package uses svgo (github.com/ajstarks/svgo)
// as a backend for vg.
package vgsvg

import (
	"bufio"
	"bytes"
	"code.google.com/p/plotinum/vg"
	"fmt"
	svgo "github.com/ajstarks/svgo"
	"image/color"
	"io"
	"math"
)

const (
	// inkscape, Chrome, FireFox, and gpicview all seem
	// to use 90 dots-per-inch.  I can't find anywhere that
	// this is actually specified, however.
	dpi = 90

	// pr is the amount of precision to use when outputting float64s.
	pr = 5
)

type Canvas struct {
	svg  *svgo.SVG
	w, h vg.Length
	buf  *bytes.Buffer
	ht   float64
	stk  []context
}

type context struct {
	color      color.Color
	dashArray  []vg.Length
	dashOffset vg.Length
	lineWidth  vg.Length
	gEnds      int
}

func New(w, h vg.Length) *Canvas {
	buf := new(bytes.Buffer)
	c := &Canvas{
		svg: svgo.New(buf),
		w:   w,
		h:   h,
		buf: buf,
		ht:  w.Points(),
		stk: []context{context{}},
	}

	// This is like svg.Start, except it uses floats
	// and specifies the units.
	fmt.Fprintf(buf, `<?xml version="1.0"?>
<!-- Generated by SVGo and Plotinum VG -->
<svg width="%.*gin" height="%.*gin"
	xmlns="http://www.w3.org/2000/svg"
	xmlns:xlink="http://www.w3.org/1999/xlink">`+"\n",
		pr, w.Inches(), pr, h.Inches())

	// Swap the origin to the bottom left.
	// This must be matched with a </g> when saving,
	// before the closing </svg>.
	c.svg.Gtransform(fmt.Sprintf("scale(1, -1) translate(0, -%.*g)", pr, h.Dots(c)))

	vg.Initialize(c)
	return c
}

func (c *Canvas) Size() (w, h vg.Length) {
	return c.w, c.h
}

func (c *Canvas) cur() *context {
	return &c.stk[len(c.stk)-1]
}

func (c *Canvas) SetLineWidth(w vg.Length) {
	c.cur().lineWidth = w
}

func (c *Canvas) SetLineDash(dashes []vg.Length, offs vg.Length) {
	c.cur().dashArray = dashes
	c.cur().dashOffset = offs
}

func (c *Canvas) SetColor(clr color.Color) {
	c.cur().color = clr
}

func (c *Canvas) Rotate(rot float64) {
	rot = rot * 180 / math.Pi
	c.svg.Rotate(rot)
	c.cur().gEnds++
}

func (c *Canvas) Translate(x, y vg.Length) {
	c.svg.Gtransform(fmt.Sprintf("translate(%.*g, %.*g)", pr, x.Dots(c), pr, y.Dots(c)))
	c.cur().gEnds++
}

func (c *Canvas) Scale(x, y float64) {
	c.svg.ScaleXY(x, y)
	c.cur().gEnds++
}

func (c *Canvas) Push() {
	top := *c.cur()
	top.gEnds = 0
	c.stk = append(c.stk, top)
}

func (c *Canvas) Pop() {
	for i := 0; i < c.cur().gEnds; i++ {
		c.svg.Gend()
	}
	c.stk = c.stk[:len(c.stk)-1]
}

func (c *Canvas) Stroke(path vg.Path) {
	c.svg.Path(c.pathData(path),
		style(elm("fill", "#000000", "none"),
			elm("stroke", "none", colorString(c.cur().color)),
			elm("stroke-opacity", "1", opacityString(c.cur().color)),
			elm("stroke-width", "1", "%.*g", pr, c.cur().lineWidth.Dots(c)),
			elm("stroke-dasharray", "none", dashArrayString(c)),
			elm("stroke-dashoffset", "0", "%.*g", pr, c.cur().dashOffset.Dots(c))))
}

func (c *Canvas) Fill(path vg.Path) {
	c.svg.Path(c.pathData(path),
		style(elm("fill", "#000000", colorString(c.cur().color))))
}

func (c *Canvas) pathData(path vg.Path) string {
	buf := new(bytes.Buffer)
	var x, y float64
	for _, comp := range path {
		switch comp.Type {
		case vg.MoveComp:
			fmt.Fprintf(buf, "M%.*g,%.*g", pr, comp.X.Dots(c), pr, comp.Y.Dots(c))
			x = comp.X.Dots(c)
			y = comp.Y.Dots(c)
		case vg.LineComp:
			fmt.Fprintf(buf, "L%.*g,%.*g", pr, comp.X.Dots(c), pr, comp.Y.Dots(c))
			x = comp.X.Dots(c)
			y = comp.Y.Dots(c)
		case vg.ArcComp:
			r := comp.Radius.Dots(c)
			x0 := comp.X.Dots(c) + r*math.Cos(comp.Start)
			y0 := comp.Y.Dots(c) + r*math.Sin(comp.Start)
			if x0 != x || y0 != y {
				fmt.Fprintf(buf, "L%.*g,%.*g", pr, x0, pr, y0)
			}
			if math.Abs(comp.Angle) >= 2*math.Pi {
				x, y = circle(buf, c, &comp)
			} else {
				x, y = arc(buf, c, &comp)
			}
		case vg.CloseComp:
			buf.WriteString("Z")
		default:
			panic(fmt.Sprintf("Unknown path component type: %d\n", comp.Type))
		}
	}
	return buf.String()
}

// circle adds circle path data to the given writer.
// Circles must be drawn using two arcs because
// SVG disallows the start and end point of an arc
// from being at the same location.
func circle(w io.Writer, c *Canvas, comp *vg.PathComp) (x, y float64) {
	angle := 2 * math.Pi
	if comp.Angle < 0 {
		angle = -2 * math.Pi
	}
	angle += remainder(comp.Angle, 2*math.Pi)
	if angle >= 4*math.Pi {
		panic("Impossible angle")
	}

	r := comp.Radius.Dots(c)
	x0 := comp.X.Dots(c) + r*math.Cos(comp.Start+angle/2)
	y0 := comp.Y.Dots(c) + r*math.Sin(comp.Start+angle/2)
	x = comp.X.Dots(c) + r*math.Cos(comp.Start+angle)
	y = comp.Y.Dots(c) + r*math.Sin(comp.Start+angle)

	fmt.Fprintf(w, "A%.*g,%.*g 0 %d %d %.*g,%.*g", pr, r, pr, r,
		large(angle/2), sweep(angle/2), pr, x0, pr, y0) //
	fmt.Fprintf(w, "A%.*g,%.*g 0 %d %d %.*g,%.*g", pr, r, pr, r,
		large(angle/2), sweep(angle/2), pr, x, pr, y)
	return
}

// remainder returns the remainder of x/y.
// We don't use math.Remainder because it
// seems to return incorrect values due to how
// IEEE defines the remainder operation…
func remainder(x, y float64) float64 {
	return (x/y - math.Trunc(x/y)) * y
}

// arc adds arc path data to the given writer.
// Arc can only be used if the arc's angle is
// less than a full circle, if it is greater then
// circle should be used instead.
func arc(w io.Writer, c *Canvas, comp *vg.PathComp) (x, y float64) {
	r := comp.Radius.Dots(c)
	x = comp.X.Dots(c) + r*math.Cos(comp.Start+comp.Angle)
	y = comp.Y.Dots(c) + r*math.Sin(comp.Start+comp.Angle)
	fmt.Fprintf(w, "A%.*g,%.*g 0 %d %d %.*g,%.*g", pr, r, pr, r,
		large(comp.Angle), sweep(comp.Angle), pr, x, pr, y)
	return
}

// sweep returns the arc sweep flag value for
// the given angle.
func sweep(a float64) int {
	if a < 0 {
		return 0
	}
	return 1
}

// large returns the arc's large flag value for
// the given angle.
func large(a float64) int {
	if math.Abs(a) >= math.Pi {
		return 1
	}
	return 0
}

func (c *Canvas) FillString(font vg.Font, x, y vg.Length, str string) {
	fontStr, ok := fontMap[font.Name()]
	if !ok {
		panic(fmt.Sprintf("Unknown font: %s", font.Name()))
	}
	sty := style(fontStr,
		elm("font-size", "medium", "%.*gpt", pr, font.Size.Points()),
		elm("fill", "#000000", colorString(c.cur().color)))
	if sty != "" {
		sty = "\n\t" + sty
	}
	fmt.Fprintf(c.buf, `<text x="%.*g" y="%.*g" transform="scale(1, -1)"%s>%s</text>`+"\n",
		pr, x.Dots(c), pr, -y.Dots(c), sty, str)
}

var (
	// fontMap maps Postscript-style font names to their
	// corresponding SVG style string.
	fontMap = map[string]string{
		"Courier":               "font-family:Courier;font-weight:normal;font-style:normal",
		"Courier-Bold":          "font-family:Courier;font-weight:bold;font-style:normal",
		"Courier-Oblique":       "font-family:Courier;font-weight:normal;font-style:oblique",
		"Courier-BoldOblique":   "font-family:Courier;font-weight:bold;font-style:oblique",
		"Helvetica":             "font-family:Helvetica;font-weight:normal;font-style:normal",
		"Helvetica-Bold":        "font-family:Helvetica;font-weight:bold;font-style:normal",
		"Helvetica-Oblique":     "font-family:Helvetica;font-weight:normal;font-style:oblique",
		"Helvetica-BoldOblique": "font-family:Helvetica;font-weight:bold;font-style:oblique",
		"Times-Roman":           "font-family:Times;font-weight:normal;font-style:normal",
		"Times-Bold":            "font-family:Times;font-weight:bold;font-style:normal",
		"Times-Italic":          "font-family:Times;font-weight:normal;font-style:italic",
		"Times-BoldItalic":      "font-family:Times;font-weight:bold;font-style:italic",
	}
)

func (c *Canvas) DPI() float64 {
	return dpi
}

// WriteTo writes the canvas to an io.Writer.
func (c *Canvas) WriteTo(w io.Writer) (int64, error) {
	b := bufio.NewWriter(w)
	n, err := c.buf.WriteTo(b)
	if err != nil {
		return n, err
	}

	// Close the groups and svg in the output buffer
	// so that the Canvas is not closed and can be
	// used again if needed.
	for i := 0; i < c.nEnds(); i++ {
		m, err := fmt.Fprintln(b, "</g>")
		n += int64(m)
		if err != nil {
			return n, err
		}
	}

	m, err := fmt.Fprintln(b, "</svg>")
	n += int64(m)
	if err != nil {
		return n, err
	}

	return n, b.Flush()
}

// nEnds returns the number of group ends
// needed before the SVG is saved.
func (c *Canvas) nEnds() int {
	n := 1 // close the transform that moves the origin
	for _, ctx := range c.stk {
		n += ctx.gEnds
	}
	return n
}

// style returns a style string composed of
// all of the given elements.  If the elements
// are all empty then the empty string is
// returned.
func style(elms ...string) string {
	str := ""
	for _, e := range elms {
		if e == "" {
			continue
		}
		if str != "" {
			str += ";"
		}
		str += e
	}
	if str == "" {
		return ""
	}
	return "style=\"" + str + "\""
}

// elm returns a style element string with the
// given key and value.  If the value matches
// default then the empty string is returned.
func elm(key, def, f string, vls ...interface{}) string {
	value := fmt.Sprintf(f, vls...)
	if value == def {
		return ""
	}
	return key + ":" + value
}

// dashArrayString returns a string representing the
// dash array specification.
func dashArrayString(c *Canvas) string {
	str := ""
	for i, d := range c.cur().dashArray {
		str += fmt.Sprintf("%.*g", pr, d.Dots(c))
		if i < len(c.cur().dashArray)-1 {
			str += ","
		}
	}
	if str == "" {
		str = "none"
	}
	return str
}

// colorString returns the hexadecimal string
// representation of the coloro.
func colorString(clr color.Color) string {
	if clr == nil {
		clr = color.Black
	}
	r, g, b, _ := clr.RGBA()
	return fmt.Sprintf("#%02X%02X%02X",
		int(float64(r)/math.MaxUint16*255),
		int(float64(g)/math.MaxUint16*255),
		int(float64(b)/math.MaxUint16*255))
}

// opacityString returns the opacity value of
// the given color.
func opacityString(clr color.Color) string {
	if clr == nil {
		clr = color.Black
	}
	_, _, _, a := clr.RGBA()
	return fmt.Sprintf("%.*g", pr, float64(a)/math.MaxUint16)
}
<commit_msg>vgsvg: set fill-opacity.<commit_after>// Copyright 2012 The Plotinum Authors. All rights reserved.
// Use of this source code is governed by an MIT-style license
// that can be found in the LICENSE file.

// The vgsvg package uses svgo (github.com/ajstarks/svgo)
// as a backend for vg.
package vgsvg

import (
	"bufio"
	"bytes"
	"code.google.com/p/plotinum/vg"
	"fmt"
	svgo "github.com/ajstarks/svgo"
	"image/color"
	"io"
	"math"
)

const (
	// inkscape, Chrome, FireFox, and gpicview all seem
	// to use 90 dots-per-inch.  I can't find anywhere that
	// this is actually specified, however.
	dpi = 90

	// pr is the amount of precision to use when outputting float64s.
	pr = 5
)

type Canvas struct {
	svg  *svgo.SVG
	w, h vg.Length
	buf  *bytes.Buffer
	ht   float64
	stk  []context
}

type context struct {
	color      color.Color
	dashArray  []vg.Length
	dashOffset vg.Length
	lineWidth  vg.Length
	gEnds      int
}

func New(w, h vg.Length) *Canvas {
	buf := new(bytes.Buffer)
	c := &Canvas{
		svg: svgo.New(buf),
		w:   w,
		h:   h,
		buf: buf,
		ht:  w.Points(),
		stk: []context{context{}},
	}

	// This is like svg.Start, except it uses floats
	// and specifies the units.
	fmt.Fprintf(buf, `<?xml version="1.0"?>
<!-- Generated by SVGo and Plotinum VG -->
<svg width="%.*gin" height="%.*gin"
	xmlns="http://www.w3.org/2000/svg"
	xmlns:xlink="http://www.w3.org/1999/xlink">`+"\n",
		pr, w.Inches(), pr, h.Inches())

	// Swap the origin to the bottom left.
	// This must be matched with a </g> when saving,
	// before the closing </svg>.
	c.svg.Gtransform(fmt.Sprintf("scale(1, -1) translate(0, -%.*g)", pr, h.Dots(c)))

	vg.Initialize(c)
	return c
}

func (c *Canvas) Size() (w, h vg.Length) {
	return c.w, c.h
}

func (c *Canvas) cur() *context {
	return &c.stk[len(c.stk)-1]
}

func (c *Canvas) SetLineWidth(w vg.Length) {
	c.cur().lineWidth = w
}

func (c *Canvas) SetLineDash(dashes []vg.Length, offs vg.Length) {
	c.cur().dashArray = dashes
	c.cur().dashOffset = offs
}

func (c *Canvas) SetColor(clr color.Color) {
	c.cur().color = clr
}

func (c *Canvas) Rotate(rot float64) {
	rot = rot * 180 / math.Pi
	c.svg.Rotate(rot)
	c.cur().gEnds++
}

func (c *Canvas) Translate(x, y vg.Length) {
	c.svg.Gtransform(fmt.Sprintf("translate(%.*g, %.*g)", pr, x.Dots(c), pr, y.Dots(c)))
	c.cur().gEnds++
}

func (c *Canvas) Scale(x, y float64) {
	c.svg.ScaleXY(x, y)
	c.cur().gEnds++
}

func (c *Canvas) Push() {
	top := *c.cur()
	top.gEnds = 0
	c.stk = append(c.stk, top)
}

func (c *Canvas) Pop() {
	for i := 0; i < c.cur().gEnds; i++ {
		c.svg.Gend()
	}
	c.stk = c.stk[:len(c.stk)-1]
}

func (c *Canvas) Stroke(path vg.Path) {
	c.svg.Path(c.pathData(path),
		style(elm("fill", "#000000", "none"),
			elm("stroke", "none", colorString(c.cur().color)),
			elm("stroke-opacity", "1", opacityString(c.cur().color)),
			elm("stroke-width", "1", "%.*g", pr, c.cur().lineWidth.Dots(c)),
			elm("stroke-dasharray", "none", dashArrayString(c)),
			elm("stroke-dashoffset", "0", "%.*g", pr, c.cur().dashOffset.Dots(c))))
}

func (c *Canvas) Fill(path vg.Path) {
	c.svg.Path(c.pathData(path),
		style(elm("fill", "#000000", colorString(c.cur().color)),
			elm("fill-opacity", "1", opacityString(c.cur().color))))
}

func (c *Canvas) pathData(path vg.Path) string {
	buf := new(bytes.Buffer)
	var x, y float64
	for _, comp := range path {
		switch comp.Type {
		case vg.MoveComp:
			fmt.Fprintf(buf, "M%.*g,%.*g", pr, comp.X.Dots(c), pr, comp.Y.Dots(c))
			x = comp.X.Dots(c)
			y = comp.Y.Dots(c)
		case vg.LineComp:
			fmt.Fprintf(buf, "L%.*g,%.*g", pr, comp.X.Dots(c), pr, comp.Y.Dots(c))
			x = comp.X.Dots(c)
			y = comp.Y.Dots(c)
		case vg.ArcComp:
			r := comp.Radius.Dots(c)
			x0 := comp.X.Dots(c) + r*math.Cos(comp.Start)
			y0 := comp.Y.Dots(c) + r*math.Sin(comp.Start)
			if x0 != x || y0 != y {
				fmt.Fprintf(buf, "L%.*g,%.*g", pr, x0, pr, y0)
			}
			if math.Abs(comp.Angle) >= 2*math.Pi {
				x, y = circle(buf, c, &comp)
			} else {
				x, y = arc(buf, c, &comp)
			}
		case vg.CloseComp:
			buf.WriteString("Z")
		default:
			panic(fmt.Sprintf("Unknown path component type: %d\n", comp.Type))
		}
	}
	return buf.String()
}

// circle adds circle path data to the given writer.
// Circles must be drawn using two arcs because
// SVG disallows the start and end point of an arc
// from being at the same location.
func circle(w io.Writer, c *Canvas, comp *vg.PathComp) (x, y float64) {
	angle := 2 * math.Pi
	if comp.Angle < 0 {
		angle = -2 * math.Pi
	}
	angle += remainder(comp.Angle, 2*math.Pi)
	if angle >= 4*math.Pi {
		panic("Impossible angle")
	}

	r := comp.Radius.Dots(c)
	x0 := comp.X.Dots(c) + r*math.Cos(comp.Start+angle/2)
	y0 := comp.Y.Dots(c) + r*math.Sin(comp.Start+angle/2)
	x = comp.X.Dots(c) + r*math.Cos(comp.Start+angle)
	y = comp.Y.Dots(c) + r*math.Sin(comp.Start+angle)

	fmt.Fprintf(w, "A%.*g,%.*g 0 %d %d %.*g,%.*g", pr, r, pr, r,
		large(angle/2), sweep(angle/2), pr, x0, pr, y0) //
	fmt.Fprintf(w, "A%.*g,%.*g 0 %d %d %.*g,%.*g", pr, r, pr, r,
		large(angle/2), sweep(angle/2), pr, x, pr, y)
	return
}

// remainder returns the remainder of x/y.
// We don't use math.Remainder because it
// seems to return incorrect values due to how
// IEEE defines the remainder operation…
func remainder(x, y float64) float64 {
	return (x/y - math.Trunc(x/y)) * y
}

// arc adds arc path data to the given writer.
// Arc can only be used if the arc's angle is
// less than a full circle, if it is greater then
// circle should be used instead.
func arc(w io.Writer, c *Canvas, comp *vg.PathComp) (x, y float64) {
	r := comp.Radius.Dots(c)
	x = comp.X.Dots(c) + r*math.Cos(comp.Start+comp.Angle)
	y = comp.Y.Dots(c) + r*math.Sin(comp.Start+comp.Angle)
	fmt.Fprintf(w, "A%.*g,%.*g 0 %d %d %.*g,%.*g", pr, r, pr, r,
		large(comp.Angle), sweep(comp.Angle), pr, x, pr, y)
	return
}

// sweep returns the arc sweep flag value for
// the given angle.
func sweep(a float64) int {
	if a < 0 {
		return 0
	}
	return 1
}

// large returns the arc's large flag value for
// the given angle.
func large(a float64) int {
	if math.Abs(a) >= math.Pi {
		return 1
	}
	return 0
}

func (c *Canvas) FillString(font vg.Font, x, y vg.Length, str string) {
	fontStr, ok := fontMap[font.Name()]
	if !ok {
		panic(fmt.Sprintf("Unknown font: %s", font.Name()))
	}
	sty := style(fontStr,
		elm("font-size", "medium", "%.*gpt", pr, font.Size.Points()),
		elm("fill", "#000000", colorString(c.cur().color)))
	if sty != "" {
		sty = "\n\t" + sty
	}
	fmt.Fprintf(c.buf, `<text x="%.*g" y="%.*g" transform="scale(1, -1)"%s>%s</text>`+"\n",
		pr, x.Dots(c), pr, -y.Dots(c), sty, str)
}

var (
	// fontMap maps Postscript-style font names to their
	// corresponding SVG style string.
	fontMap = map[string]string{
		"Courier":               "font-family:Courier;font-weight:normal;font-style:normal",
		"Courier-Bold":          "font-family:Courier;font-weight:bold;font-style:normal",
		"Courier-Oblique":       "font-family:Courier;font-weight:normal;font-style:oblique",
		"Courier-BoldOblique":   "font-family:Courier;font-weight:bold;font-style:oblique",
		"Helvetica":             "font-family:Helvetica;font-weight:normal;font-style:normal",
		"Helvetica-Bold":        "font-family:Helvetica;font-weight:bold;font-style:normal",
		"Helvetica-Oblique":     "font-family:Helvetica;font-weight:normal;font-style:oblique",
		"Helvetica-BoldOblique": "font-family:Helvetica;font-weight:bold;font-style:oblique",
		"Times-Roman":           "font-family:Times;font-weight:normal;font-style:normal",
		"Times-Bold":            "font-family:Times;font-weight:bold;font-style:normal",
		"Times-Italic":          "font-family:Times;font-weight:normal;font-style:italic",
		"Times-BoldItalic":      "font-family:Times;font-weight:bold;font-style:italic",
	}
)

func (c *Canvas) DPI() float64 {
	return dpi
}

// WriteTo writes the canvas to an io.Writer.
func (c *Canvas) WriteTo(w io.Writer) (int64, error) {
	b := bufio.NewWriter(w)
	n, err := c.buf.WriteTo(b)
	if err != nil {
		return n, err
	}

	// Close the groups and svg in the output buffer
	// so that the Canvas is not closed and can be
	// used again if needed.
	for i := 0; i < c.nEnds(); i++ {
		m, err := fmt.Fprintln(b, "</g>")
		n += int64(m)
		if err != nil {
			return n, err
		}
	}

	m, err := fmt.Fprintln(b, "</svg>")
	n += int64(m)
	if err != nil {
		return n, err
	}

	return n, b.Flush()
}

// nEnds returns the number of group ends
// needed before the SVG is saved.
func (c *Canvas) nEnds() int {
	n := 1 // close the transform that moves the origin
	for _, ctx := range c.stk {
		n += ctx.gEnds
	}
	return n
}

// style returns a style string composed of
// all of the given elements.  If the elements
// are all empty then the empty string is
// returned.
func style(elms ...string) string {
	str := ""
	for _, e := range elms {
		if e == "" {
			continue
		}
		if str != "" {
			str += ";"
		}
		str += e
	}
	if str == "" {
		return ""
	}
	return "style=\"" + str + "\""
}

// elm returns a style element string with the
// given key and value.  If the value matches
// default then the empty string is returned.
func elm(key, def, f string, vls ...interface{}) string {
	value := fmt.Sprintf(f, vls...)
	if value == def {
		return ""
	}
	return key + ":" + value
}

// dashArrayString returns a string representing the
// dash array specification.
func dashArrayString(c *Canvas) string {
	str := ""
	for i, d := range c.cur().dashArray {
		str += fmt.Sprintf("%.*g", pr, d.Dots(c))
		if i < len(c.cur().dashArray)-1 {
			str += ","
		}
	}
	if str == "" {
		str = "none"
	}
	return str
}

// colorString returns the hexadecimal string
// representation of the coloro.
func colorString(clr color.Color) string {
	if clr == nil {
		clr = color.Black
	}
	r, g, b, _ := clr.RGBA()
	return fmt.Sprintf("#%02X%02X%02X",
		int(float64(r)/math.MaxUint16*255),
		int(float64(g)/math.MaxUint16*255),
		int(float64(b)/math.MaxUint16*255))
}

// opacityString returns the opacity value of
// the given color.
func opacityString(clr color.Color) string {
	if clr == nil {
		clr = color.Black
	}
	_, _, _, a := clr.RGBA()
	return fmt.Sprintf("%.*g", pr, float64(a)/math.MaxUint16)
}
<|endoftext|>


<commit_before>package regexes

import "regexp"

var DistinctSQL = regexp.MustCompile(`(?i)distinct[^a-z]+[a-z]+`)
<commit_msg>[regexes] Add more regexes<commit_after>package regexes

import "regexp"

var (
	DistinctSQL    = regexp.MustCompile(`(?i)distinct[^a-z]+[a-z]+`)
	Column         = regexp.MustCompile("^[a-zA-Z]+(\\.[a-zA-Z]+)*$") // only match string like `name`, `users.name`
	IsNumber       = regexp.MustCompile("^\\s*\\d+\\s*$")             // match if string is number
	Comparison     = regexp.MustCompile("(?i) (=|<>|>|<|LIKE|IS|IN) ")
	CcountingQuery = regexp.MustCompile("(?i)^count(.+)$")
)
<|endoftext|>


<commit_before>package stub

import (
	"github.com/workfit/tester/assert"
	"testing"
)

//If true, will save out the files generated. Useful for generating new golden
//output when output is changed.
const generateNewGolden = false

func TestGenerate(t *testing.T) {

	tmpls, err := DefaultTemplateSet()

	assert.For(t).ThatActual(err).IsNil()
	assert.For(t).ThatActual(len(tmpls)).Equals(1)

	opt := &Options{
		Name: "checkers",
	}

	contents, err := tmpls.Generate(opt)

	assert.For(t).ThatActual(err).IsNil()
	assert.For(t).ThatActual(len(contents)).Equals(1)

	assert.For(t).ThatActual(contents["checkers/main.go"]).IsNotNil()

	//Now that we unit tested underlying stuff, use Generate() top level,
	//which also formats.

	contents, err = Generate(opt)

	assert.For(t).ThatActual(err).IsNil()

	if generateNewGolden {

		//Save out contents as new golden files to compare against
		contents.Save("test")

		return
	}

	mainGo := contents["checkers/main.go"]

	assert.For(t).ThatActual(mainGo).IsNotNil()

}
<commit_msg>Made it so the test loads up the goldens to compare against. Part of #663.<commit_after>package stub

import (
	"errors"
	"github.com/workfit/tester/assert"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
)

//If true, will save out the files generated. Useful for generating new golden
//output when output is changed.
const generateNewGolden = false

const testDir = "test"

func TestGenerate(t *testing.T) {

	tmpls, err := DefaultTemplateSet()

	assert.For(t).ThatActual(err).IsNil()
	assert.For(t).ThatActual(len(tmpls)).Equals(1)

	opt := &Options{
		Name: "checkers",
	}

	contents, err := tmpls.Generate(opt)

	assert.For(t).ThatActual(err).IsNil()
	assert.For(t).ThatActual(len(contents)).Equals(1)

	assert.For(t).ThatActual(contents["checkers/main.go"]).IsNotNil()

	//Now that we unit tested underlying stuff, use Generate() top level,
	//which also formats.

	contents, err = Generate(opt)

	assert.For(t).ThatActual(err).IsNil()

	if generateNewGolden {

		//Save out contents as new golden files to compare against
		contents.Save(testDir)
		return
	}

	golden, err := fileContentsFromDir(testDir)

	assert.For(t).ThatActual(err).IsNil()

	assert.For(t).ThatActual(contents).Equals(golden).ThenDiffOnFail()

}

//fileContentsFromDir loads up filecontents from the given path so they can be
//compared to the golden.
func fileContentsFromDir(path string) (FileContents, error) {

	if _, err := os.Stat(path); os.IsNotExist(err) {
		return nil, errors.New(path + " doesnt' exist")
	}

	result := make(FileContents)

	if err := recursiveListFilesForFileContents(path, "", result); err != nil {
		return nil, errors.New("couldn't list files: " + err.Error())
	}

	return result, nil

}

//basePath is actual dir to list recursively; prefix is the prefix to affix to
//dir contenst to put in contents.
func recursiveListFilesForFileContents(basePath, prefix string, contents FileContents) error {

	infos, err := ioutil.ReadDir(basePath)

	if err != nil {
		return errors.New("Couldn't list path: " + err.Error())
	}

	for _, info := range infos {
		if info.IsDir() {
			if err := recursiveListFilesForFileContents(filepath.Join(basePath, info.Name()), filepath.Join(prefix, info.Name()), contents); err != nil {
				return err
			}
			continue
		}
		//info represents a file.

		content, err := ioutil.ReadFile(filepath.Join(basePath, info.Name()))

		if err != nil {
			return errors.New("couldn't read " + filepath.Join(basePath, info.Name()) + ": " + err.Error())
		}

		contents[filepath.Join(prefix, info.Name())] = content
	}

	return nil

}
<|endoftext|>