<commit_before>package jeeves

import (
	"crypto"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"

	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/codykrieger/jeeves/ask"
)

var certURLStringToBytesMap map[string][]byte

func init() {
	certURLStringToBytesMap = make(map[string][]byte)
}

type Skill struct {
	Name            string
	Endpoint        string
	ApplicationID   string
	Handler         func(*Skill, *ask.Request) *ask.Response
	internalHandler func(*requestContext, *ask.Request)
}

type requestContext struct {
	writer http.ResponseWriter
	req    *http.Request
	skill  *Skill
}

func RegisterSkill(skill *Skill) http.Handler {
	skill.internalHandler = func(ctx *requestContext, req *ask.Request) {
		resp := skill.Handler(skill, req)

		bytes, _ := resp.Bytes()
		// FIXME: Handle errors.

		ctx.writer.Header().Set("Content-Type", "application/json; charset=utf-8")
		ctx.writer.Write(bytes)
	}

	return skill
}

func (skill *Skill) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ctx := &requestContext{
		writer: w,
		req:    r,
		skill:  skill,
	}
	ctx.process()
}

func (ctx *requestContext) err(code int, theError error) {
	log.Printf("Error: %v; returning HTTP status code %v\n", theError, code)
	http.Error(ctx.writer, http.StatusText(code), code)
}

func (ctx *requestContext) process() {
	req, err := ask.NewRequestFromJSON(ctx.req.Body)
	if err != nil {
		ctx.err(400, err)
		return
	}

	if !ctx.validateRequestSignature(req) ||
		!ctx.validateTimestamp(req) ||
		!ctx.validateApplicationID(req) ||
		!ctx.validateRequestType(req) {
		return
	}

	ctx.skill.internalHandler(ctx, req)
}

func (ctx *requestContext) validateRequestSignature(req *ask.Request) bool {
	certChainURL := ctx.req.Header.Get("SignatureCertChainUrl")
	if !ctx.validateCertChainURL(certChainURL) {
		return false
	}

	certBytes, err := readCertAtURL(certChainURL)
	if err != nil {
		ctx.err(400, fmt.Errorf("Unable to fetch/parse certificate at given URL (%v): %v", certChainURL, err))
		return false
	}

	// FIXME: We need to check the additional blocks in the PEM structure to
	// ensure the Amazon signing certificate (the first certificate in the PEM
	// structure) has a valid chain of trust up to a trusted root CA (see
	// https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/developing-an-alexa-skill-as-a-web-service#Checking%20the%20Signature%20of%20the%20Request).
	// This will at least be sufficient for development, though.
	pemBlock, _ := pem.Decode(certBytes)
	if err != nil {
		ctx.err(400, fmt.Errorf("Failed to parse certificate PEM: %v", err))
		return false
	}

	cert, err := x509.ParseCertificate(pemBlock.Bytes)
	if err != nil {
		ctx.err(400, fmt.Errorf("Failed to parse x509 certificate: %v", err))
		return false
	}

	now := time.Now().Unix()
	if now < cert.NotBefore.Unix() || now > cert.NotAfter.Unix() {
		ctx.err(400, fmt.Errorf("Given certificate is expired or not yet valid"))
		// FIXME: Maybe blow away the url => cert data cache at this point to
		// avoid having to restart the server in the case where the cert has
		// been updated after expiry? Probably in some other failure cases, too.
		return false
	}

	validSAN := false

	for _, san := range cert.Subject.Names {
		if san.Value == "echo-api.amazon.com" {
			validSAN = true
			break
		}
	}

	if !validSAN {
		ctx.err(400, fmt.Errorf("No valid Subject Alternate Names included in given certificate"))
		return false
	}

	// FIXME: At this point we should verify that all certs in the chain combine
	// to create a chain of trust to a trusted root CA.

	publicKey := cert.PublicKey
	encodedSignature := ctx.req.Header.Get("Signature")
	decodedSignature, err := base64.StdEncoding.DecodeString(encodedSignature)
	if err != nil {
		ctx.err(400, fmt.Errorf("Couldn't decode base64 Signature header (%v)", encodedSignature))
		return false
	}

	var buf bytes.Buffer
	hash := sha1.New()
	_, err = io.Copy(hash, io.TeeReader(ctx.req.Body, &buf))
	if err != nil {
		ctx.err(500, fmt.Errorf("IO error: %v", err))
		return false
	}

	ctx.req.Body = ioutil.NopCloser(&buf)

	err = rsa.VerifyPKCS1v15(publicKey.(*rsa.PublicKey), crypto.SHA1, hash.Sum(nil), decodedSignature)
	if err != nil {
		ctx.err(400, fmt.Errorf("Signature verification failed: %v", err))
		return false
	}

	return true
}

func (ctx *requestContext) validateCertChainURL(urlString string) bool {
	url, err := url.Parse(urlString)
	if err != nil {
		ctx.err(400, fmt.Errorf("Invalid SignatureCertChainUrl (%v)", urlString))
		return false
	}

	// FIXME: This should technically be a case-insensitive match.
	if url.Scheme != "https" {
		ctx.err(400, fmt.Errorf("Invalid SignatureCertChainUrl scheme (%v)", urlString))
		return false
	}

	// FIXME: This should technically be a case-insensitive match.
	if url.Host != "s3.amazonaws.com" && url.Host != "s3.amazonaws.com:443" {
		ctx.err(400, fmt.Errorf("Invalid SignatureCertChainUrl hostname (%v)", urlString))
		return false
	}

	// This, on the other hand, *is* supposed to be a case-sensitive match.
	if !strings.HasPrefix(url.Path, "/echo.api/") {
		ctx.err(400, fmt.Errorf("Invalid SignatureCertChainUrl path (%v)", urlString))
		return false
	}

	return true
}

func (ctx *requestContext) validateApplicationID(req *ask.Request) bool {
	if req.Session.Application.ApplicationID != ctx.skill.ApplicationID {
		ctx.err(400, fmt.Errorf("Expected application ID %v, got %v",
			ctx.skill.ApplicationID, req.Session.Application.ApplicationID))
		return false
	}
	return true
}

func (ctx *requestContext) validateTimestamp(req *ask.Request) bool {
	ts, err := time.Parse(time.RFC3339Nano, req.Body.Timestamp)
	if err != nil {
		ctx.err(400, fmt.Errorf("Bad request timestamp %v", req.Body.Timestamp))
		return false
	}

	if time.Since(ts) > time.Duration(30)*time.Second {
		ctx.err(400, fmt.Errorf("Timestamp is too old"))
		return false
	}

	return true
}

func (ctx *requestContext) validateRequestType(req *ask.Request) bool {
	if !req.IsLaunchRequest() && !req.IsIntentRequest() && !req.IsSessionEndedRequest() {
		ctx.err(400, fmt.Errorf("Request type '%v' invalid", req.Body.Type))
		return false
	}
	return true
}

func readCertAtURL(urlString string) ([]byte, error) {
	// FIXME: Re-enable caching after performance impact can be gauged.
	// if bytes, ok := certURLStringToBytesMap[urlString]; ok {
	// 	return bytes, nil
	// }

	resp, err := http.Get(urlString)
	if err != nil {
		return nil, fmt.Errorf("Couldn't fetch Amazon cert file: %v", err)
	}

	defer resp.Body.Close()

	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("Couldn't read Amazon cert file: %v", err)
	}

	// certURLStringToBytesMap[urlString] = bytes

	return bytes, nil
}
<commit_msg>Fix request body checksumming issue; verify full signing certificate trust chain.<commit_after>package jeeves

import (
	"crypto"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"

	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/codykrieger/jeeves/ask"
)

var certURLStringToBytesMap map[string][]byte

func init() {
	certURLStringToBytesMap = make(map[string][]byte)
}

type Skill struct {
	Name            string
	Endpoint        string
	ApplicationID   string
	Handler         func(*Skill, *ask.Request) *ask.Response
	internalHandler func(*requestContext, *ask.Request)
}

type requestContext struct {
	writer  http.ResponseWriter
	req     *http.Request
	reqBody *[]byte
	skill   *Skill
}

func RegisterSkill(skill *Skill) http.Handler {
	skill.internalHandler = func(ctx *requestContext, req *ask.Request) {
		resp := skill.Handler(skill, req)

		bytes, _ := resp.Bytes()
		// FIXME: Handle errors.

		ctx.writer.Header().Set("Content-Type", "application/json; charset=utf-8")
		ctx.writer.Write(bytes)
	}

	return skill
}

func (skill *Skill) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ctx := &requestContext{
		writer: w,
		req:    r,
		skill:  skill,
	}
	ctx.process()
}

func (ctx *requestContext) err(code int, theError error) {
	log.Printf("Error: %v; returning HTTP status code %v\n", theError, code)
	http.Error(ctx.writer, http.StatusText(code), code)
}

func (ctx *requestContext) process() {
	body, err := ioutil.ReadAll(ctx.req.Body)
	if err != nil {
		ctx.err(400, err)
		return
	}

	ctx.reqBody = &body
	ctx.req.Body = ioutil.NopCloser(bytes.NewReader(*ctx.reqBody))

	req, err := ask.NewRequestFromJSON(bytes.NewReader(*ctx.reqBody))
	if err != nil {
		ctx.err(400, err)
		return
	}

	if !ctx.validateRequestSignature(req) ||
		!ctx.validateTimestamp(req) ||
		!ctx.validateApplicationID(req) ||
		!ctx.validateRequestType(req) {
		return
	}

	ctx.skill.internalHandler(ctx, req)
}

func (ctx *requestContext) validateRequestSignature(req *ask.Request) bool {
	certChainURL := ctx.req.Header.Get("SignatureCertChainUrl")
	if !ctx.validateCertChainURL(certChainURL) {
		return false
	}

	certBytes, err := readCertAtURL(certChainURL)
	if err != nil {
		ctx.err(400, fmt.Errorf("Unable to fetch/parse certificate at given URL (%v): %v", certChainURL, err))
		return false
	}

	// FIXME: We need to check the additional blocks in the PEM structure to
	// ensure the Amazon signing certificate (the first certificate in the PEM
	// structure) has a valid chain of trust up to a trusted root CA (see
	// https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/developing-an-alexa-skill-as-a-web-service#Checking%20the%20Signature%20of%20the%20Request).
	// This will at least be sufficient for development, though.

	var certs []*x509.Certificate

	for {
		var pemBlock *pem.Block
		pemBlock, certBytes = pem.Decode(certBytes)
		if pemBlock == nil {
			break
		}

		cert, err := x509.ParseCertificate(pemBlock.Bytes)
		if err != nil {
			ctx.err(400, fmt.Errorf("Failed to parse x509 certificate: %v", err))
			return false
		}

		now := time.Now().Unix()
		if now < cert.NotBefore.Unix() || now > cert.NotAfter.Unix() {
			ctx.err(400, fmt.Errorf("Given certificate is expired or not yet valid"))
			// FIXME: Maybe blow away the url => cert data cache at this point to
			// avoid having to restart the server in the case where the cert has
			// been updated after expiry? Probably in some other failure cases, too.
			return false
		}

		certs = append(certs, cert)
	}

	numberOfCerts := len(certs)

	if numberOfCerts < 2 {
		ctx.err(400, fmt.Errorf("Unexpected number of certificates (%v)", numberOfCerts))
		return false
	}

	cert := certs[0]

	validSAN := false
	for _, san := range cert.Subject.Names {
		if san.Value == "echo-api.amazon.com" {
			validSAN = true
			break
		}
	}

	if !validSAN {
		ctx.err(400, fmt.Errorf("No valid Subject Alternate Names included in given certificate"))
		return false
	}

	intermediateCerts := x509.NewCertPool()
	for i := 1; i < numberOfCerts; i++ {
		intermediateCerts.AddCert(certs[i])
	}

	verifyOpts := x509.VerifyOptions{
		Intermediates: intermediateCerts,
	}

	if _, err := cert.Verify(verifyOpts); err != nil {
		ctx.err(400, fmt.Errorf("Failed to verify certificate chain"))
		return false
	}

	publicKey := cert.PublicKey
	encodedSignature := ctx.req.Header.Get("Signature")
	decodedSignature, err := base64.StdEncoding.DecodeString(encodedSignature)
	if err != nil {
		ctx.err(400, fmt.Errorf("Couldn't decode base64 Signature header (%v)", encodedSignature))
		return false
	}

	hash := sha1.New()
	hash.Write(*ctx.reqBody)

	err = rsa.VerifyPKCS1v15(publicKey.(*rsa.PublicKey), crypto.SHA1, hash.Sum(nil), decodedSignature)
	if err != nil {
		ctx.err(400, fmt.Errorf("Signature verification failed: %v", err))
		return false
	}

	return true
}

func (ctx *requestContext) validateCertChainURL(urlString string) bool {
	url, err := url.Parse(urlString)
	if err != nil {
		ctx.err(400, fmt.Errorf("Invalid SignatureCertChainUrl (%v)", urlString))
		return false
	}

	// FIXME: This should technically be a case-insensitive match.
	if url.Scheme != "https" {
		ctx.err(400, fmt.Errorf("Invalid SignatureCertChainUrl scheme (%v)", urlString))
		return false
	}

	// FIXME: This should technically be a case-insensitive match.
	if url.Host != "s3.amazonaws.com" && url.Host != "s3.amazonaws.com:443" {
		ctx.err(400, fmt.Errorf("Invalid SignatureCertChainUrl hostname (%v)", urlString))
		return false
	}

	// This, on the other hand, *is* supposed to be a case-sensitive match.
	if !strings.HasPrefix(url.Path, "/echo.api/") {
		ctx.err(400, fmt.Errorf("Invalid SignatureCertChainUrl path (%v)", urlString))
		return false
	}

	return true
}

func (ctx *requestContext) validateApplicationID(req *ask.Request) bool {
	if req.Session.Application.ApplicationID != ctx.skill.ApplicationID {
		ctx.err(400, fmt.Errorf("Expected application ID %v, got %v",
			ctx.skill.ApplicationID, req.Session.Application.ApplicationID))
		return false
	}
	return true
}

func (ctx *requestContext) validateTimestamp(req *ask.Request) bool {
	ts, err := time.Parse(time.RFC3339Nano, req.Body.Timestamp)
	if err != nil {
		ctx.err(400, fmt.Errorf("Bad request timestamp %v", req.Body.Timestamp))
		return false
	}

	if time.Since(ts) > time.Duration(30)*time.Second {
		ctx.err(400, fmt.Errorf("Timestamp is too old"))
		return false
	}

	return true
}

func (ctx *requestContext) validateRequestType(req *ask.Request) bool {
	if !req.IsLaunchRequest() && !req.IsIntentRequest() && !req.IsSessionEndedRequest() {
		ctx.err(400, fmt.Errorf("Request type '%v' invalid", req.Body.Type))
		return false
	}
	return true
}

func readCertAtURL(urlString string) ([]byte, error) {
	// FIXME: Re-enable caching after performance impact can be gauged.
	// if bytes, ok := certURLStringToBytesMap[urlString]; ok {
	// 	return bytes, nil
	// }

	resp, err := http.Get(urlString)
	if err != nil {
		return nil, fmt.Errorf("Couldn't fetch Amazon cert file: %v", err)
	}

	defer resp.Body.Close()

	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("Couldn't read Amazon cert file: %v", err)
	}

	// certURLStringToBytesMap[urlString] = bytes

	return bytes, nil
}
<|endoftext|>


<commit_before>package operations

import (
	"errors"
	"fmt"
	"github.com/qiniu/go-sdk/v7/auth/qbox"
	"github.com/qiniu/go-sdk/v7/storage"
	"github.com/qiniu/qshell/v2/iqshell"
	"github.com/qiniu/qshell/v2/iqshell/common/alert"
	"github.com/qiniu/qshell/v2/iqshell/common/log"
	"github.com/qiniu/qshell/v2/iqshell/common/progress"
	"github.com/qiniu/qshell/v2/iqshell/common/utils"
	"github.com/qiniu/qshell/v2/iqshell/common/workspace"
	"github.com/qiniu/qshell/v2/iqshell/storage/object/upload"
	"os"
	"time"
)

type UploadInfo struct {
	upload.ApiInfo
	Policy          *storage.PutPolicy
	DeleteOnSuccess bool
}

func (info *UploadInfo) Check() error {
	if len(info.ToBucket) == 0 {
		return alert.CannotEmptyError("Bucket", "")
	}
	if len(info.SaveKey) == 0 && len(info.FilePath) == 0 {
		return alert.CannotEmptyError("Key", "")
	}
	if len(info.FilePath) == 0 {
		return alert.CannotEmptyError("LocalFile", "")
	}
	if utils.IsNetworkSource(info.FilePath) {
		return alert.Error("file can't be network source", "")
	}
	return nil
}

func UploadFile(cfg *iqshell.Config, info UploadInfo) {
	if shouldContinue := iqshell.CheckAndLoad(cfg, iqshell.CheckAndLoadInfo{
		Checker: &info,
	}); !shouldContinue {
		return
	}

	info.Progress = progress.NewPrintProgress(" 进度")
	ret, err := uploadFile(&info)
	if err != nil {
		if v, ok := err.(*storage.ErrorInfo); ok {
			log.ErrorF("Upload file error %d: %s, Reqid: %s", v.Code, v.Err, v.Reqid)
		}
	} else {
		log.Alert("")
		log.Alert("-------------- File Info --------------")
		log.AlertF("%10s%s", "Key: ", ret.Key)
		log.AlertF("%10s%s", "Hash: ", ret.Hash)
		log.AlertF("%10s%d%s", "Fsize: ", ret.FSize, "("+utils.FormatFileSize(ret.FSize)+")")
		log.AlertF("%10s%s", "MimeType: ", ret.MimeType)
	}
}

func uploadFile(info *UploadInfo) (res upload.ApiResult, err error) {
	startTime := time.Now().UnixNano() / 1e6
	if info.TokenProvider == nil {
		info.TokenProvider, err = createTokenProvider(info)
	}
	if err != nil {
		log.ErrorF("Upload  failed because get token provider error:%s => [%s:%s] error:%v", info.FilePath, info.ToBucket, info.SaveKey, err)
		return
	}

	res, err = upload.Upload(&info.ApiInfo)
	if err != nil {
		log.ErrorF("Upload  failed:%s => [%s:%s] error:%v", info.FilePath, info.ToBucket, info.SaveKey, err)
		return
	}
	endTime := time.Now().UnixNano() / 1e6

	duration := float64(endTime-startTime) / 1000
	speed := fmt.Sprintf("%.2fKB/s", float64(res.FSize)/duration/1024)
	if res.IsSkip {
		log.AlertF("Upload skip because file exist:%s => [%s:%s]", info.FilePath, info.ToBucket, info.SaveKey)
	} else {
		log.AlertF("Upload File success %s => [%s:%s] duration:%.2fs Speed:%s", info.FilePath, info.ToBucket, info.SaveKey, duration, speed)

		//delete on success
		if info.DeleteOnSuccess {
			deleteErr := os.Remove(info.FilePath)
			if deleteErr != nil {
				log.ErrorF("Delete `%s` on upload success error due to `%s`", info.FilePath, deleteErr)
			} else {
				log.InfoF("Delete `%s` on upload success done", info.FilePath)
			}
		}
	}

	return res, nil
}

func createTokenProvider(info *UploadInfo) (provider func() string, err error) {
	mac, gErr := workspace.GetMac()
	if gErr != nil {
		return nil, errors.New("get mac error:" + gErr.Error())
	}

	provider = createTokenProviderWithMac(mac, info)
	return
}

func createTokenProviderWithMac(mac *qbox.Mac, info *UploadInfo) func() string {
	policy := info.Policy
	policy.Scope = info.ToBucket
	policy.InsertOnly = 1 // 仅新增不覆盖
	if info.Overwrite {
		policy.Scope = fmt.Sprintf("%s:%s", info.ToBucket, info.SaveKey)
		policy.InsertOnly = 0
	}
	policy.ReturnBody = upload.ApiResultFormat()
	policy.FileType = info.FileType
	return func() string {
		policy.Expires = 7 * 24 * 3600
		return policy.UploadToken(mac)
	}
}
<commit_msg>adjust upload createTokenProvider<commit_after>package operations

import (
	"errors"
	"fmt"
	"github.com/qiniu/go-sdk/v7/auth/qbox"
	"github.com/qiniu/go-sdk/v7/storage"
	"github.com/qiniu/qshell/v2/iqshell"
	"github.com/qiniu/qshell/v2/iqshell/common/alert"
	"github.com/qiniu/qshell/v2/iqshell/common/log"
	"github.com/qiniu/qshell/v2/iqshell/common/progress"
	"github.com/qiniu/qshell/v2/iqshell/common/utils"
	"github.com/qiniu/qshell/v2/iqshell/common/workspace"
	"github.com/qiniu/qshell/v2/iqshell/storage/object/upload"
	"os"
	"time"
)

type UploadInfo struct {
	upload.ApiInfo
	Policy          *storage.PutPolicy
	DeleteOnSuccess bool
}

func (info *UploadInfo) Check() error {
	if len(info.ToBucket) == 0 {
		return alert.CannotEmptyError("Bucket", "")
	}
	if len(info.SaveKey) == 0 && len(info.FilePath) == 0 {
		return alert.CannotEmptyError("Key", "")
	}
	if len(info.FilePath) == 0 {
		return alert.CannotEmptyError("LocalFile", "")
	}
	if utils.IsNetworkSource(info.FilePath) {
		return alert.Error("file can't be network source", "")
	}
	return nil
}

func UploadFile(cfg *iqshell.Config, info UploadInfo) {
	if shouldContinue := iqshell.CheckAndLoad(cfg, iqshell.CheckAndLoadInfo{
		Checker: &info,
	}); !shouldContinue {
		return
	}

	info.Progress = progress.NewPrintProgress(" 进度")
	ret, err := uploadFile(&info)
	if err != nil {
		if v, ok := err.(*storage.ErrorInfo); ok {
			log.ErrorF("Upload file error %d: %s, Reqid: %s", v.Code, v.Err, v.Reqid)
		}
	} else {
		log.Alert("")
		log.Alert("-------------- File Info --------------")
		log.AlertF("%10s%s", "Key: ", ret.Key)
		log.AlertF("%10s%s", "Hash: ", ret.Hash)
		log.AlertF("%10s%d%s", "Fsize: ", ret.FSize, "("+utils.FormatFileSize(ret.FSize)+")")
		log.AlertF("%10s%s", "MimeType: ", ret.MimeType)
	}
}

func uploadFile(info *UploadInfo) (res upload.ApiResult, err error) {
	startTime := time.Now().UnixNano() / 1e6
	if info.TokenProvider == nil {
		info.TokenProvider, err = createTokenProvider(info)
	}
	if err != nil {
		log.ErrorF("Upload  failed because get token provider error:%s => [%s:%s] error:%v", info.FilePath, info.ToBucket, info.SaveKey, err)
		return
	}

	res, err = upload.Upload(&info.ApiInfo)
	if err != nil {
		log.ErrorF("Upload  failed:%s => [%s:%s] error:%v", info.FilePath, info.ToBucket, info.SaveKey, err)
		return
	}
	endTime := time.Now().UnixNano() / 1e6

	duration := float64(endTime-startTime) / 1000
	speed := fmt.Sprintf("%.2fKB/s", float64(res.FSize)/duration/1024)
	if res.IsSkip {
		log.AlertF("Upload skip because file exist:%s => [%s:%s]", info.FilePath, info.ToBucket, info.SaveKey)
	} else {
		log.AlertF("Upload File success %s => [%s:%s] duration:%.2fs Speed:%s", info.FilePath, info.ToBucket, info.SaveKey, duration, speed)

		//delete on success
		if info.DeleteOnSuccess {
			deleteErr := os.Remove(info.FilePath)
			if deleteErr != nil {
				log.ErrorF("Delete `%s` on upload success error due to `%s`", info.FilePath, deleteErr)
			} else {
				log.InfoF("Delete `%s` on upload success done", info.FilePath)
			}
		}
	}

	return res, nil
}

func createTokenProvider(info *UploadInfo) (provider func() string, err error) {
	mac, gErr := workspace.GetMac()
	if gErr != nil {
		return nil, errors.New("get mac error:" + gErr.Error())
	}

	provider = createTokenProviderWithMac(mac, info)
	return
}

func createTokenProviderWithMac(mac *qbox.Mac, info *UploadInfo) func() string {
	policy := *info.Policy
	policy.Scope = info.ToBucket
	policy.InsertOnly = 1 // 仅新增不覆盖
	if info.Overwrite {
		policy.Scope = fmt.Sprintf("%s:%s", info.ToBucket, info.SaveKey)
		policy.InsertOnly = 0
	}
	policy.ReturnBody = upload.ApiResultFormat()
	policy.FileType = info.FileType
	return func() string {
		policy.Expires = 7 * 24 * 3600
		return policy.UploadToken(mac)
	}
}
<|endoftext|>


<commit_before>// Copyright © 2016 Thomas Rabaix <thomas.rabaix@gmail.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package flowdock

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/mitchellh/cli"
	gitlab "github.com/plouc/go-gitlab-client"
	helper "github.com/rande/gitlab-ci-helper"
	"html/template"
	"net/http"
	"os"
	"strconv"
	"strings"
)

type CiFlowdockStatusCommand struct {
	Ui           cli.Ui
	Verbose      bool
	Last         bool
	BuildRef     string
	BuildName    string
	BuildRefName string
	Project      string
}

func (c *CiFlowdockStatusCommand) Run(args []string) int {

	config := &FlowdockConfig{
		Server: "https://api.flowdock.com",
	}

	levels := map[string]int{
		"pending":  BUILD_PENDING,
		"running":  BUILD_RUNNING,
		"success":  BUILD_SUCCESS,
		"failed":   BUILD_FAILED,
		"canceled": BUILD_CANCELLED,
	}

	// flowdock colors: red, green, yellow, cyan, orange, grey, black, lime, purple, blue
	colors := map[int]string{
		BUILD_PENDING:   "black",
		BUILD_RUNNING:   "orange",
		BUILD_SUCCESS:   "green",
		BUILD_FAILED:    "red",
		BUILD_CANCELLED: "grey",
	}

	cmdFlags := flag.NewFlagSet("flowdock:thread", flag.ContinueOnError)
	cmdFlags.Usage = func() {
		c.Ui.Output(c.Help())
	}

	cmdFlags.BoolVar(&c.Verbose, "verbose", false, "")
	cmdFlags.BoolVar(&c.Last, "last", false, "")
	cmdFlags.StringVar(&config.Token, "token", os.Getenv("FLOWDOCK_SOURCE_TOKEN"), "The room's token (default: env var FLOWDOCK_SOURCE_TOKEN)")
	cmdFlags.StringVar(&c.BuildRef, "ref", os.Getenv("CI_BUILD_REF"), "The commit related to the build (default: env var CI_BUILD_REF)")
	cmdFlags.StringVar(&c.Project, "project", os.Getenv("CI_PROJECT_ID"), "The project related to the build (default: env var CI_PROJECT_ID)")
	cmdFlags.StringVar(&c.BuildName, "name", os.Getenv("CI_BUILD_NAME"), "The build's name (default: env var CI_BUILD_NAME)")
	cmdFlags.StringVar(&c.BuildRefName, "ref-name", os.Getenv("CI_BUILD_REF_NAME"), "The reference name (default: env var CI_BUILD_REF_NAME)")

	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	args = cmdFlags.Args()

	if len(args) != 2 {
		c.Ui.Error("Invalid number of arguments\n")
		cmdFlags.Usage()

		return 1
	}

	config.Organization = args[0]
	config.Flow = args[1]

	gitlabConfig := helper.NewConfig()
	gitLabClient := gitlab.NewGitlab(gitlabConfig.Gitlab.Host, gitlabConfig.Gitlab.ApiPath, gitlabConfig.Gitlab.Token)

	project, err := helper.GetProject(c.Project, gitLabClient)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Unable to fetch the project: %s", err.Error()))

		return 1
	}

	c.Ui.Output(fmt.Sprintf("Found project: %s/%s (id: %d)", project.Namespace.Name, project.Name, project.Id))

	builds, err := gitLabClient.ProjectCommitBuilds(strconv.FormatInt(int64(project.Id), 10), c.BuildRef)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

		return 1
	}

	var level = 0
	var status = "-"

	activeBuilds := make(map[string]*gitlab.Build, 0)

	// filter builds to only get active ones
	for _, b := range builds {
		if ib, ok := activeBuilds[b.Name]; !ok {
			activeBuilds[b.Name] = b
		} else {
			if ib.Id > b.Id {
				activeBuilds[b.Name] = ib
			}
		}
	}

	// found the valid status build
	for _, b := range activeBuilds {
		if levels[b.Status] > level {
			level = levels[b.Status]
			status = b.Status
		}
	}

	// if it the last pass with no previous error, we force the final job to be green
	// the command should be the last one (reporting ...)
	if c.Last && level == BUILD_RUNNING {
		status = "success"
		level = BUILD_SUCCESS

		activeBuilds[c.BuildName].Status = "success"
	}

	tpl := `
{{ .Project.Namespace.Name }} / {{ .Project.Name }}

Project: {{ .Project.WebUrl }}

Author: {{ .Build.Commit.Author_Name }}

Title: {{ .Build.Commit.Title }}

Builds commit: {{ .Project.WebUrl }}/commit/{{ .Build.Commit.Id }}/builds

                Status
                Name
                Stage

            {{ range .Builds }}

                        {{ $k := index $.Levels .Status }}
                        {{ $color := index $.Colors $k }}

                        ✪</style> #{{ .Id }} - {{ .Status }}

                        {{ if gt .ArtifactsFile.Size 0 }}
                             📦
                        {{ end }}

                    {{ .Name }}
                    {{ .Stage }}

            {{ end }}

`

	t, err := template.New("foo").Parse(tpl)

	body := bytes.NewBuffer([]byte(""))

	err = t.Execute(body, map[string]interface{}{
		"Builds":  activeBuilds,
		"Build":   builds[0],
		"Project": project,
		"Levels":  levels,
		"Colors":  colors,
	})

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

		return 1
	}

	message := &FlowdockMessage{
		Event:      "activity",
		ExternalId: fmt.Sprintf("gitlab:%s:%s", c.BuildRef, c.BuildRefName),
		Title:      fmt.Sprintf("Update status, job:%s", c.BuildName),
		Thread: &FlowdockThread{
			Status: &FlowdockThreadStatus{
				Color: colors[level],
				Value: status,
			},
			Title:       fmt.Sprintf("Jobs for %s - %s", project.Name, c.BuildRefName),
			Body:        body.String(),
			ExternalUrl: fmt.Sprintf("%s/commit/%s/builds", project.WebUrl, c.BuildRef),
		},
		Author: &FlowdockAuthor{
			Name:   "GitlabCi",
			Avatar: "https://about.gitlab.com/images/gitlab_logo.png",
		},
	}

	message.Token = config.Token
	message.Flow = config.Flow

	buf := bytes.NewBuffer([]byte(""))
	e := json.NewEncoder(buf)

	e.Encode(message)

	client := &http.Client{}

	r, _ := http.NewRequest("POST", fmt.Sprintf("%s/flows/%s/%s/messages", config.Server, config.Organization, config.Flow), buf)
	r.Header.Add("Content-Type", "application/json")

	_, err = client.Do(r)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Err: %s", err.Error()))

		return 1
	}

	return 0
}

func (c *CiFlowdockStatusCommand) Synopsis() string {
	return "Send a message to one Flowdock's room."
}

func (c *CiFlowdockStatusCommand) Help() string {
	helpText := fmt.Sprintf(`
Usage: gitlab-ci-helper flowdock:message [options] organisation flow

  Build a flowdock thread from the current build. Information are retrieved from
  environment variables.

  The external thread id is: gitlab:sha1

  You can use the -last option to indicate that the current job is the last one.

Arguments:
  organisation        The organisation name
  flow                The flow reference

Options:
  -ref                The commit related to the build (default: env var CI_BUILD_REF)
  -project            The project related to the build (default: env var CI_PROJECT_ID)
  -name               The build's name (default: env var CI_BUILD_NAME)
  -ref-name           The reference name (default: env var CI_BUILD_REF_NAME)
  -last               Indicate if the current build is the last one
  -token              The flow's token (default: env var FLOWDOCK_SOURCE_TOKEN)
  -verbose            Add verbose information to the output

%s

Gitlab's credentials are retrieved from environment:

  GITLAB_HOST         The gitlab host
  GITLAB_TOKEN        The user's token
  GITLAB_API_PATH     (optional) the api path, default to: "/api/v3"

`, flowdockConfiguration)

	return strings.TrimSpace(helpText)
}
<commit_msg>fix(flowdock): fix broken link to GitLab<commit_after>// Copyright © 2016 Thomas Rabaix <thomas.rabaix@gmail.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package flowdock

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/mitchellh/cli"
	gitlab "github.com/plouc/go-gitlab-client"
	helper "github.com/rande/gitlab-ci-helper"
	"html/template"
	"net/http"
	"os"
	"strconv"
	"strings"
)

type CiFlowdockStatusCommand struct {
	Ui           cli.Ui
	Verbose      bool
	Last         bool
	BuildRef     string
	BuildName    string
	BuildRefName string
	Project      string
}

func (c *CiFlowdockStatusCommand) Run(args []string) int {

	config := &FlowdockConfig{
		Server: "https://api.flowdock.com",
	}

	levels := map[string]int{
		"pending":  BUILD_PENDING,
		"running":  BUILD_RUNNING,
		"success":  BUILD_SUCCESS,
		"failed":   BUILD_FAILED,
		"canceled": BUILD_CANCELLED,
	}

	// flowdock colors: red, green, yellow, cyan, orange, grey, black, lime, purple, blue
	colors := map[int]string{
		BUILD_PENDING:   "black",
		BUILD_RUNNING:   "orange",
		BUILD_SUCCESS:   "green",
		BUILD_FAILED:    "red",
		BUILD_CANCELLED: "grey",
	}

	cmdFlags := flag.NewFlagSet("flowdock:thread", flag.ContinueOnError)
	cmdFlags.Usage = func() {
		c.Ui.Output(c.Help())
	}

	cmdFlags.BoolVar(&c.Verbose, "verbose", false, "")
	cmdFlags.BoolVar(&c.Last, "last", false, "")
	cmdFlags.StringVar(&config.Token, "token", os.Getenv("FLOWDOCK_SOURCE_TOKEN"), "The room's token (default: env var FLOWDOCK_SOURCE_TOKEN)")
	cmdFlags.StringVar(&c.BuildRef, "ref", os.Getenv("CI_BUILD_REF"), "The commit related to the build (default: env var CI_BUILD_REF)")
	cmdFlags.StringVar(&c.Project, "project", os.Getenv("CI_PROJECT_ID"), "The project related to the build (default: env var CI_PROJECT_ID)")
	cmdFlags.StringVar(&c.BuildName, "name", os.Getenv("CI_BUILD_NAME"), "The build's name (default: env var CI_BUILD_NAME)")
	cmdFlags.StringVar(&c.BuildRefName, "ref-name", os.Getenv("CI_BUILD_REF_NAME"), "The reference name (default: env var CI_BUILD_REF_NAME)")

	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	args = cmdFlags.Args()

	if len(args) != 2 {
		c.Ui.Error("Invalid number of arguments\n")
		cmdFlags.Usage()

		return 1
	}

	config.Organization = args[0]
	config.Flow = args[1]

	gitlabConfig := helper.NewConfig()
	gitLabClient := gitlab.NewGitlab(gitlabConfig.Gitlab.Host, gitlabConfig.Gitlab.ApiPath, gitlabConfig.Gitlab.Token)

	project, err := helper.GetProject(c.Project, gitLabClient)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Unable to fetch the project: %s", err.Error()))

		return 1
	}

	c.Ui.Output(fmt.Sprintf("Found project: %s/%s (id: %d)", project.Namespace.Name, project.Name, project.Id))

	builds, err := gitLabClient.ProjectCommitBuilds(strconv.FormatInt(int64(project.Id), 10), c.BuildRef)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

		return 1
	}

	var level = 0
	var status = "-"

	activeBuilds := make(map[string]*gitlab.Build, 0)

	// filter builds to only get active ones
	for _, b := range builds {
		if ib, ok := activeBuilds[b.Name]; !ok {
			activeBuilds[b.Name] = b
		} else {
			if ib.Id > b.Id {
				activeBuilds[b.Name] = ib
			}
		}
	}

	// found the valid status build
	for _, b := range activeBuilds {
		if levels[b.Status] > level {
			level = levels[b.Status]
			status = b.Status
		}
	}

	// if it the last pass with no previous error, we force the final job to be green
	// the command should be the last one (reporting ...)
	if c.Last && level == BUILD_RUNNING {
		status = "success"
		level = BUILD_SUCCESS

		activeBuilds[c.BuildName].Status = "success"
	}

	tpl := `
{{ .Project.Namespace.Name }} / {{ .Project.Name }}

Project: {{ .Project.WebUrl }}

Author: {{ .Build.Commit.Author_Name }}

Title: {{ .Build.Commit.Title }}

Builds commit: {{ .Project.WebUrl }}/commit/{{ .Build.Commit.Id }}/pipelines

                Status
                Name
                Stage

            {{ range .Builds }}

                        {{ $k := index $.Levels .Status }}
                        {{ $color := index $.Colors $k }}

                        ✪</style> #{{ .Id }} - {{ .Status }}

                        {{ if gt .ArtifactsFile.Size 0 }}
                             📦
                        {{ end }}

                    {{ .Name }}
                    {{ .Stage }}

            {{ end }}

`

	t, err := template.New("foo").Parse(tpl)

	body := bytes.NewBuffer([]byte(""))

	err = t.Execute(body, map[string]interface{}{
		"Builds":  activeBuilds,
		"Build":   builds[0],
		"Project": project,
		"Levels":  levels,
		"Colors":  colors,
	})

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

		return 1
	}

	message := &FlowdockMessage{
		Event:      "activity",
		ExternalId: fmt.Sprintf("gitlab:%s:%s", c.BuildRef, c.BuildRefName),
		Title:      fmt.Sprintf("Update status, job:%s", c.BuildName),
		Thread: &FlowdockThread{
			Status: &FlowdockThreadStatus{
				Color: colors[level],
				Value: status,
			},
			Title:       fmt.Sprintf("Jobs for %s - %s", project.Name, c.BuildRefName),
			Body:        body.String(),
			ExternalUrl: fmt.Sprintf("%s/commit/%s/pipelines", project.WebUrl, c.BuildRef),
		},
		Author: &FlowdockAuthor{
			Name:   "GitlabCi",
			Avatar: "https://about.gitlab.com/images/gitlab_logo.png",
		},
	}

	message.Token = config.Token
	message.Flow = config.Flow

	buf := bytes.NewBuffer([]byte(""))
	e := json.NewEncoder(buf)

	e.Encode(message)

	client := &http.Client{}

	r, _ := http.NewRequest("POST", fmt.Sprintf("%s/flows/%s/%s/messages", config.Server, config.Organization, config.Flow), buf)
	r.Header.Add("Content-Type", "application/json")

	_, err = client.Do(r)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Err: %s", err.Error()))

		return 1
	}

	return 0
}

func (c *CiFlowdockStatusCommand) Synopsis() string {
	return "Send a message to one Flowdock's room."
}

func (c *CiFlowdockStatusCommand) Help() string {
	helpText := fmt.Sprintf(`
Usage: gitlab-ci-helper flowdock:message [options] organisation flow

  Build a flowdock thread from the current build. Information are retrieved from
  environment variables.

  The external thread id is: gitlab:sha1

  You can use the -last option to indicate that the current job is the last one.

Arguments:
  organisation        The organisation name
  flow                The flow reference

Options:
  -ref                The commit related to the build (default: env var CI_BUILD_REF)
  -project            The project related to the build (default: env var CI_PROJECT_ID)
  -name               The build's name (default: env var CI_BUILD_NAME)
  -ref-name           The reference name (default: env var CI_BUILD_REF_NAME)
  -last               Indicate if the current build is the last one
  -token              The flow's token (default: env var FLOWDOCK_SOURCE_TOKEN)
  -verbose            Add verbose information to the output

%s

Gitlab's credentials are retrieved from environment:

  GITLAB_HOST         The gitlab host
  GITLAB_TOKEN        The user's token
  GITLAB_API_PATH     (optional) the api path, default to: "/api/v3"

`, flowdockConfiguration)

	return strings.TrimSpace(helpText)
}
<|endoftext|>


<commit_before>package provider

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"github.com/mackerelio/mackerel-client-go"
)

func resourceMackerelChannel() *schema.Resource {
	return &schema.Resource{
		Create: resourceMackerelChannelCreate,
		Read:   resourceMackerelChannelRead,
		Update: resourceMackerelChannelUpdate,
		Delete: resourceMackerelChannelDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"type": {
				Type:     schema.TypeString,
				Required: true,
			},
			"name": {
				Type:     schema.TypeString,
				Required: true,
			},
			"emails": {
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
			// Field name may only contain lowercase alphanumeric characters & underscores.
			"user_ids": {
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
			"events": {
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
			"url": {
				Type:     schema.TypeString,
				Optional: true,
				ConflictsWith: []string{"emails", "user_ids"},
			},
			"mentions": {
				Type:     schema.TypeMap,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
			"enabled_graph_image": {
				Type:     schema.TypeBool,
				Optional: true,
			},
		},
	}
}

func resourceMackerelChannelCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*mackerel.Client)

	input, err := buildChannelParameter(d)
	if err != nil {
		return err
	}

	channel, err := client.CreateChannel(input)
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] mackerel channel %q created.", channel.ID)
	d.SetId(channel.ID)

	return resourceMackerelChannelRead(d, meta)
}

func resourceMackerelChannelRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*mackerel.Client)

	log.Printf("[DEBUG] Reading mackerel channel: %q", d.Id())
	channels, err := client.FindChannels()
	if err != nil {
		return err
	}

	for _, channel := range channels {
		if channel.ID == d.Id() {
			_ = d.Set("id", channel.ID)
			_ = d.Set("name", channel.Name)
			_ = d.Set("type", channel.Type)
			_ = d.Set("url", channel.URL)
			_ = d.Set("enabled_graph_image", channel.EnabledGraphImage)
			_ = d.Set("user_ids", channel.UserIDs)
			_ = d.Set("mentions", channel.Mentions)
			_ = d.Set("events", channel.Events)
			break
		}
	}

	return nil
}

// DeleteChannel() -> CreateChannel()
// Bacause, Mackerel API does not have UpdateChannel()
func resourceMackerelChannelUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*mackerel.Client)

	_, err := client.DeleteChannel(d.Id())
	if err != nil {
		return err
	}

	input, err := buildChannelParameter(d)
	if err != nil {
		return err
	}

	channel, err := client.CreateChannel(input)
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] mackerel channel %q updated.", channel.ID)
	d.SetId(channel.ID)

	return resourceMackerelChannelRead(d, meta)
}

func resourceMackerelChannelDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*mackerel.Client)

	_, err := client.DeleteChannel(d.Id())
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] mackerel channel %q deleted.", d.Id())
	d.SetId("")

	return nil
}

func buildChannelParameter(d *schema.ResourceData) (*mackerel.Channel, error) {
	switch d.Get("type").(string) {
	case "email":
		return buildEmailParameter(d)
	case "slack":
		return buildSlackParameter(d)
	case "webhook":
		return buildWebhookParameter(d)
	default:
		return nil, fmt.Errorf("%v is not valid input for type", d.Get("type"))
	}
}

// build parameter for email
func buildEmailParameter(d *schema.ResourceData) (*mackerel.Channel, error) {
	input := &mackerel.Channel{
		Name: d.Get("name").(string),
		Type: d.Get("type").(string),
	}

	if v, ok := d.GetOk("emails"); ok {
		tmp := expandStringList(v.([]interface{}))
		input.Emails = &tmp
	}

	if v, ok := d.GetOk("user_ids"); ok {
		tmp := expandStringList(v.([]interface{}))
		input.UserIDs = &tmp
	}

	if input.Emails == nil && input.UserIDs == nil {
		return nil, fmt.Errorf("emails or user_ids is required")
	}

	if v, ok := d.GetOk("events"); ok {
		tmp := expandStringList(v.([]interface{}))
		err := validateChannelEvent(tmp, []string{"alert", "alertGroup"})
		if err != nil {
			return nil, err
		}
		input.Events = &tmp
	}

	return input, nil
}

// build parameter for slack
func buildSlackParameter(d *schema.ResourceData) (*mackerel.Channel, error) {
	input := &mackerel.Channel{
		Name: d.Get("name").(string),
		Type: d.Get("type").(string),
		URL:  d.Get("url").(string),
	}

	if v, ok := d.Get("enabled_graph_image").(bool); ok {
		input.EnabledGraphImage = &v
	}

	if v, ok := d.GetOk("mentions"); ok {
		// Convert from schema.TypeMap to mackerel.Mentions
		mentionJSON, err := json.Marshal(v)
		if err != nil {
			return nil, err
		}
		var mentions mackerel.Mentions
		err = json.Unmarshal(mentionJSON, &mentions)
		if err != nil {
			return nil, err
		}
		input.Mentions = mentions
	}

	if v, ok := d.GetOk("events"); ok {
		tmp := expandStringList(v.([]interface{}))
		err := validateChannelEvent(tmp, []string{"alert", "alertGroup", "hostStatus", "hostRegister", "hostRetire", "monitor"})
		if err != nil {
			return nil, err
		}
		input.Events = &tmp
	}

	return input, nil
}

// build parameter for webhook
func buildWebhookParameter(d *schema.ResourceData) (*mackerel.Channel, error) {
	input := &mackerel.Channel{
		Name: d.Get("name").(string),
		Type: d.Get("type").(string),
		URL:  d.Get("url").(string),
	}

	if v, ok := d.Get("enabled_graph_image").(bool); ok {
		input.EnabledGraphImage = &v
	}

	if v, ok := d.GetOk("events"); ok {
		tmp := expandStringList(v.([]interface{}))
		err := validateChannelEvent(tmp, []string{"alert", "alertGroup", "hostStatus", "hostRegister", "hostRetire", "monitor"})
		if err != nil {
			return nil, err
		}
		input.Events = &tmp
	}

	return input, nil
}
<commit_msg>Update internal/provider/resource_mackerel_channel.go<commit_after>package provider

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"github.com/mackerelio/mackerel-client-go"
)

func resourceMackerelChannel() *schema.Resource {
	return &schema.Resource{
		Create: resourceMackerelChannelCreate,
		Read:   resourceMackerelChannelRead,
		Update: resourceMackerelChannelUpdate,
		Delete: resourceMackerelChannelDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"type": {
				Type:     schema.TypeString,
				Required: true,
				ValidateFunc: validation.StringInSlice([]string{
					"email",
					"slack",
					"webhook",
				}, false),
			},
			"name": {
				Type:     schema.TypeString,
				Required: true,
			},
			"emails": {
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
			// Field name may only contain lowercase alphanumeric characters & underscores.
			"user_ids": {
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
			"events": {
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
			"url": {
				Type:     schema.TypeString,
				Optional: true,
				ConflictsWith: []string{"emails", "user_ids"},
			},
			"mentions": {
				Type:     schema.TypeMap,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
			"enabled_graph_image": {
				Type:     schema.TypeBool,
				Optional: true,
			},
		},
	}
}

func resourceMackerelChannelCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*mackerel.Client)

	input, err := buildChannelParameter(d)
	if err != nil {
		return err
	}

	channel, err := client.CreateChannel(input)
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] mackerel channel %q created.", channel.ID)
	d.SetId(channel.ID)

	return resourceMackerelChannelRead(d, meta)
}

func resourceMackerelChannelRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*mackerel.Client)

	log.Printf("[DEBUG] Reading mackerel channel: %q", d.Id())
	channels, err := client.FindChannels()
	if err != nil {
		return err
	}

	for _, channel := range channels {
		if channel.ID == d.Id() {
			_ = d.Set("id", channel.ID)
			_ = d.Set("name", channel.Name)
			_ = d.Set("type", channel.Type)
			_ = d.Set("url", channel.URL)
			_ = d.Set("enabled_graph_image", channel.EnabledGraphImage)
			_ = d.Set("user_ids", channel.UserIDs)
			_ = d.Set("mentions", channel.Mentions)
			_ = d.Set("events", channel.Events)
			break
		}
	}

	return nil
}

// DeleteChannel() -> CreateChannel()
// Bacause, Mackerel API does not have UpdateChannel()
func resourceMackerelChannelUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*mackerel.Client)

	_, err := client.DeleteChannel(d.Id())
	if err != nil {
		return err
	}

	input, err := buildChannelParameter(d)
	if err != nil {
		return err
	}

	channel, err := client.CreateChannel(input)
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] mackerel channel %q updated.", channel.ID)
	d.SetId(channel.ID)

	return resourceMackerelChannelRead(d, meta)
}

func resourceMackerelChannelDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*mackerel.Client)

	_, err := client.DeleteChannel(d.Id())
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] mackerel channel %q deleted.", d.Id())
	d.SetId("")

	return nil
}

func buildChannelParameter(d *schema.ResourceData) (*mackerel.Channel, error) {
	switch d.Get("type").(string) {
	case "email":
		return buildEmailParameter(d)
	case "slack":
		return buildSlackParameter(d)
	case "webhook":
		return buildWebhookParameter(d)
	default:
		return nil, fmt.Errorf("%v is not valid input for type", d.Get("type"))
	}
}

// build parameter for email
func buildEmailParameter(d *schema.ResourceData) (*mackerel.Channel, error) {
	input := &mackerel.Channel{
		Name: d.Get("name").(string),
		Type: d.Get("type").(string),
	}

	if v, ok := d.GetOk("emails"); ok {
		tmp := expandStringList(v.([]interface{}))
		input.Emails = &tmp
	}

	if v, ok := d.GetOk("user_ids"); ok {
		tmp := expandStringList(v.([]interface{}))
		input.UserIDs = &tmp
	}

	if input.Emails == nil && input.UserIDs == nil {
		return nil, fmt.Errorf("emails or user_ids is required")
	}

	if v, ok := d.GetOk("events"); ok {
		tmp := expandStringList(v.([]interface{}))
		err := validateChannelEvent(tmp, []string{"alert", "alertGroup"})
		if err != nil {
			return nil, err
		}
		input.Events = &tmp
	}

	return input, nil
}

// build parameter for slack
func buildSlackParameter(d *schema.ResourceData) (*mackerel.Channel, error) {
	input := &mackerel.Channel{
		Name: d.Get("name").(string),
		Type: d.Get("type").(string),
		URL:  d.Get("url").(string),
	}

	if v, ok := d.Get("enabled_graph_image").(bool); ok {
		input.EnabledGraphImage = &v
	}

	if v, ok := d.GetOk("mentions"); ok {
		// Convert from schema.TypeMap to mackerel.Mentions
		mentionJSON, err := json.Marshal(v)
		if err != nil {
			return nil, err
		}
		var mentions mackerel.Mentions
		err = json.Unmarshal(mentionJSON, &mentions)
		if err != nil {
			return nil, err
		}
		input.Mentions = mentions
	}

	if v, ok := d.GetOk("events"); ok {
		tmp := expandStringList(v.([]interface{}))
		err := validateChannelEvent(tmp, []string{"alert", "alertGroup", "hostStatus", "hostRegister", "hostRetire", "monitor"})
		if err != nil {
			return nil, err
		}
		input.Events = &tmp
	}

	return input, nil
}

// build parameter for webhook
func buildWebhookParameter(d *schema.ResourceData) (*mackerel.Channel, error) {
	input := &mackerel.Channel{
		Name: d.Get("name").(string),
		Type: d.Get("type").(string),
		URL:  d.Get("url").(string),
	}

	if v, ok := d.Get("enabled_graph_image").(bool); ok {
		input.EnabledGraphImage = &v
	}

	if v, ok := d.GetOk("events"); ok {
		tmp := expandStringList(v.([]interface{}))
		err := validateChannelEvent(tmp, []string{"alert", "alertGroup", "hostStatus", "hostRegister", "hostRetire", "monitor"})
		if err != nil {
			return nil, err
		}
		input.Events = &tmp
	}

	return input, nil
}
<|endoftext|>


<commit_before>package text

import (
	"bytes"
	"fmt"
	"strings"
)

type Mutation interface {
	Apply(text string) string
}

type Set struct {
	Text string
}

type Sub struct {
	Search string
	Sub    string
}

type SubQuery struct {
	Search Query
	Sub    string
}

type Append struct {
	Appendix string
}

type Prepend struct {
	Prefix string
}

func (r *Set) Apply(text string) string {
	return r.Text
}

func (s *Sub) Apply(text string) string {
	return strings.Replace(text, s.Search, s.Sub, -1)
}

func (s *SubQuery) Apply(text string) string {
	for i, ln := s.Search.Match(text); i >= 0; i, ln = s.Search.Match(text) {
		tr := []rune(text)

		beginning := tr[:i]
		end := tr[i+ln:]

		var buf bytes.Buffer

		buf.WriteString(string(beginning))
		buf.WriteString(s.Sub)
		buf.WriteString(string(end))

		text = buf.String()
	}

	return text
}

func (a *Append) Apply(text string) string {
	if len(a.Appendix) == 0 {
		return text
	}

	return text + a.Appendix
}

func (p *Prepend) Apply(text string) string {
	if len(p.Prefix) == 0 {
		return text
	}

	return p.Prefix + text
}
<commit_msg>Remove unused import (oops)<commit_after>package text

import (
	"bytes"
	"strings"
)

type Mutation interface {
	Apply(text string) string
}

type Set struct {
	Text string
}

type Sub struct {
	Search string
	Sub    string
}

type SubQuery struct {
	Search Query
	Sub    string
}

type Append struct {
	Appendix string
}

type Prepend struct {
	Prefix string
}

func (r *Set) Apply(text string) string {
	return r.Text
}

func (s *Sub) Apply(text string) string {
	return strings.Replace(text, s.Search, s.Sub, -1)
}

func (s *SubQuery) Apply(text string) string {
	for i, ln := s.Search.Match(text); i >= 0; i, ln = s.Search.Match(text) {
		tr := []rune(text)

		beginning := tr[:i]
		end := tr[i+ln:]

		var buf bytes.Buffer

		buf.WriteString(string(beginning))
		buf.WriteString(s.Sub)
		buf.WriteString(string(end))

		text = buf.String()
	}

	return text
}

func (a *Append) Apply(text string) string {
	if len(a.Appendix) == 0 {
		return text
	}

	return text + a.Appendix
}

func (p *Prepend) Apply(text string) string {
	if len(p.Prefix) == 0 {
		return text
	}

	return p.Prefix + text
}
<|endoftext|>


<commit_before>/* Copyright 2017 Google Inc.
 * https://github.com/NeilFraser/CodeCity
 *
 * 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 flatpack

import (
	"fmt"
	"math"
	"reflect"
	"unsafe"
)

// This file provides some utility functions used in testing the rest
// of the package.

func recEqual(i1, i2 interface{}, disjoint bool) bool {
	return recValueEqual(reflect.ValueOf(i1), reflect.ValueOf(i2), disjoint)
func RecEqual(x, y interface{}, disjoint bool) bool {
	return recValueEqual(reflect.ValueOf(x), reflect.ValueOf(y), disjoint)
}

func recValueEqual(v1, v2 reflect.Value, disjoint bool) bool {
	return recEq(v1, v2, disjoint, make(map[unsafe.Pointer]unsafe.Pointer), make(map[unsafe.Pointer]unsafe.Pointer))
}

// recEq recursively tests for structural equality of v1 and
// v2.
//
// If disjoint is true, then v1 and v2 must not share any substructure.
//
// v1s and v2s keep track of previously-visited values; in v1s the
// keys are values of v1 already seen while the values are the
// corresponding v2 values; in v2s it is vice versa.  We keep maps
// for both directions because otherwise certain cases of structural
// dissimilarity (or shared substructure between v1 and v2) might not
// otherwise be detected.
//
// FXIME: Slices shoudl have better check for shared backing
func recEq(v1, v2 reflect.Value, disjoint bool, v1s, v2s map[unsafe.Pointer]unsafe.Pointer) bool {
	if !v1.IsValid() {
		return !v2.IsValid()
	} else if !v2.IsValid() {
		return false // Already know v1 valid.
	}
	if v1.Type() != v2.Type() {
		return false
	}

	switch v1.Kind() {
	case reflect.Map, reflect.Ptr, reflect.Slice:
		v1p := unsafe.Pointer(v1.Pointer())
		v2p := unsafe.Pointer(v2.Pointer())

		// Check for disjointness if requested:
		if disjoint {
			// (But ignore zero-capacity slices.)
			if v1.Kind() != reflect.Slice || v1.Cap() > 0 && v2.Cap() > 0 {
				_, v2in1 := v1s[v2p]
				_, v1in2 := v2s[v1p]
				if (!v1.IsNil() && v1p == v2p) || v2in1 || v1in2 {
					return false
				}
			}
		}

		// Check if we have previously visited this v1 (and if so whether
		// we have the correct corresponding v2):
		v1o, seen1 := v1s[v1p]
		v2o, seen2 := v2s[v2p]
		if seen1 {
			return seen2 && v1o == v2p && v2o == v1p
		} else if seen2 {
			return false
		}
		v1s[v1p] = v2p
		v2s[v2p] = v1p

	}

	switch v1.Kind() {
	case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
		reflect.String:
		return v1.Interface() == v2.Interface()
	case reflect.Array:
		for i, l := 0, v1.Len(); i < l; i++ {
			if !recEq(v1.Index(i), v2.Index(i), disjoint, v1s, v2s) {
				return false
			}
		}
	case reflect.Complex64, reflect.Complex128:
		c1, c2 := v1.Complex(), v2.Complex()
		return same(real(c1), real(c2)) && same(imag(c1), imag(c2))
	case reflect.Float32, reflect.Float64:
		return same(v1.Float(), v2.Float())
	case reflect.Interface, reflect.Ptr:
		return recEq(v1.Elem(), v2.Elem(), disjoint, v1s, v2s)
	case reflect.Map:
		if v1.IsNil() != v2.IsNil() || v1.Len() != v2.Len() {
			return false
		}
		for _, k := range v1.MapKeys() {
			v1v := v1.MapIndex(k)
			v2v := v2.MapIndex(k)
			if !v1v.IsValid() || !v2v.IsValid() || !recEq(v1v, v2v, disjoint, v1s, v2s) {
				return false
			}
		}
	case reflect.Slice:
		if v1.IsNil() != v2.IsNil() || v1.Len() != v2.Len() {
			return false
		}
		for i, n := 0, v1.Len(); i < n; i++ {
			if !recEq(v1.Index(i), v2.Index(i), disjoint, v1s, v2s) {
				return false
			}
		}
	case reflect.Struct:
		if !v1.CanAddr() {
			vv := reflect.New(v1.Type()).Elem()
			vv.Set(v1)
			v1 = vv
		}
		if !v2.CanAddr() {
			vv := reflect.New(v2.Type()).Elem()
			vv.Set(v2)
			v2 = vv
		}
		for i, n := 0, v1.NumField(); i < n; i++ {
			if !recEq(defeat(v1.Field(i)), defeat(v2.Field(i)), disjoint, v1s, v2s) {
				return false
			}
		}
	case reflect.Func:
		// Because you can't meanigfully produce a disjoint f2 that
		// does the same as f1 at runtime (and would have no way to in
		// general to verify the functions were equivalent even if you
		// could create one), we just test for pointer equality (and
		// *not* disjointness):
		return unsafe.Pointer(v1.Pointer()) == unsafe.Pointer(v2.Pointer())
	case reflect.Chan, reflect.UnsafePointer:
		panic(fmt.Errorf("Comparison of %s not implemented", v1.Kind()))
	default:
		panic(fmt.Errorf("Invalid Kind %s", v1.Kind()))
	}
	return true
}

// same returns true iff its float64 args represent the same logical
// (rather than mathematical) value.  So:
//     same(NaN, NaN) == true
//     same(0, -0) == false
// and otherwise:
//      same(x, y) == (x == y)
func same(f1, f2 float64) bool {
	if f1 == f2 {
		if f1 == 0 && f2 == 0 {
			return math.Signbit(f1) == math.Signbit(f2)
		}
		return true
	}
	if math.IsNaN(f1) && math.IsNaN(f2) {
		return true
	}
	return false
}
<commit_msg>Improve documentation<commit_after>/* Copyright 2017 Google Inc.
 * https://github.com/NeilFraser/CodeCity
 *
 * 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 flatpack

import (
	"fmt"
	"math"
	"reflect"
	"unsafe"
)

// This file provides some utility functions used in testing the rest
// of the package.

// RecEqual reports whether x and y are "recursively equal", as
// defined below.  This is similar to reflect.DeepEqual, but also
// considers whether they have the same structure (shared substructure
// and cycles).  Additionally, recEqual is slightly more picky about
// some types, and and more lenient about others, than DeepEqual is.
//
// Values of distinct types are never recursively equal.  Values of
// identical type are recursively equal if one of the following is
// true:
//
// Boolean, integer and string values are recursively equal if they
// are equal according to the == operator.
//
// Arrays are recursively equal if all their corresponding elements
// are recursively equal.
//
// Complex numbers are recursively equal if their corresponding real
// and imaginary parts are recursively equal.
//
// Floats are recursively equal if they are both NaN, both 0 or both
// -0; or if they are non-zero and equal according to the == operator.
//
// Interface values are recursively equal if their contents are
// recurisvely equal.
//
// Map values are recursively equal if their values as references are
// recursively equal according to the rules for pointers (below), and
// they additionally have the same length, and their corresponding
// keys (according to ==) map to recursively equal values.  (Note that
// recursive equality for maps with NaN keys is not well-defined.)
//
// Pointer values are recursively equal if they point at recursively
// equal values AND ADDITIONALLY if every occurence of pointer p1 in v1
// corresponds to the same pointer p2 in v2 (and vice versa).
//
// Slice values are recursively equal if they are both nil or both
// non-nil, and have the same length, and their corresponding elements
// are deeply equal.
//
// Struct values are recursively equal if their corresponding fields
// (exported and unexported) are recursively equal.
//
// Func values are recursively equal if they are both nil or if they
// point to the same location.
//
// Comparison of Channel and unsafe.Pointer values is not implemented;
// attempting to compare such values will cause a panic.
//
// In addition to the above rules, if disjoint == true then RecEqual
// will return false any map, pointer, or slice backing pointer value
// found in v1 also appears in v2.
//
// BUG(cpcallen): Checking of slices for shared substructure is very
// primitive and likely to miss many cases.
func RecEqual(x, y interface{}, disjoint bool) bool {
	return recValueEqual(reflect.ValueOf(x), reflect.ValueOf(y), disjoint)
}

func recValueEqual(v1, v2 reflect.Value, disjoint bool) bool {
	return recEq(v1, v2, disjoint, make(map[unsafe.Pointer]unsafe.Pointer), make(map[unsafe.Pointer]unsafe.Pointer))
}

// recEq recursively tests for structural equality of v1 and
// v2.
//
// If disjoint is true, then v1 and v2 must not share any substructure.
//
// v1s and v2s keep track of previously-visited values; in v1s the
// keys are pointers found in v2 and their corresponding pointers from
// v2; in v2s it is vice versa.  We keep maps for both directions
// because otherwise certain cases of structural dissimilarity (or
// shared substructure between v1 and v2) might otherwise not be caught.
//
// FXIME: Slices should have better check for shared backing
func recEq(v1, v2 reflect.Value, disjoint bool, v1s, v2s map[unsafe.Pointer]unsafe.Pointer) bool {
	if !v1.IsValid() {
		return !v2.IsValid()
	} else if !v2.IsValid() {
		return false // Already know v1 valid.
	}
	if v1.Type() != v2.Type() {
		return false
	}

	switch v1.Kind() {
	case reflect.Map, reflect.Ptr, reflect.Slice:
		v1p := unsafe.Pointer(v1.Pointer())
		v2p := unsafe.Pointer(v2.Pointer())

		// Check for disjointness if requested:
		if disjoint {
			// (But ignore zero-capacity slices.)
			if v1.Kind() != reflect.Slice || v1.Cap() > 0 && v2.Cap() > 0 {
				_, v2in1 := v1s[v2p]
				_, v1in2 := v2s[v1p]
				if (!v1.IsNil() && v1p == v2p) || v2in1 || v1in2 {
					return false
				}
			}
		}

		// Check if we have previously visited this v1 (and if so whether
		// we have the correct corresponding v2):
		v1o, seen1 := v1s[v1p]
		v2o, seen2 := v2s[v2p]
		if seen1 {
			return seen2 && v1o == v2p && v2o == v1p
		} else if seen2 {
			return false
		}
		v1s[v1p] = v2p
		v2s[v2p] = v1p

	}

	switch v1.Kind() {
	case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
		reflect.String:
		return v1.Interface() == v2.Interface()
	case reflect.Array:
		for i, l := 0, v1.Len(); i < l; i++ {
			if !recEq(v1.Index(i), v2.Index(i), disjoint, v1s, v2s) {
				return false
			}
		}
	case reflect.Complex64, reflect.Complex128:
		c1, c2 := v1.Complex(), v2.Complex()
		return same(real(c1), real(c2)) && same(imag(c1), imag(c2))
	case reflect.Float32, reflect.Float64:
		return same(v1.Float(), v2.Float())
	case reflect.Interface, reflect.Ptr:
		return recEq(v1.Elem(), v2.Elem(), disjoint, v1s, v2s)
	case reflect.Map:
		if v1.IsNil() != v2.IsNil() || v1.Len() != v2.Len() {
			return false
		}
		for _, k := range v1.MapKeys() {
			v1v := v1.MapIndex(k)
			v2v := v2.MapIndex(k)
			if !v1v.IsValid() || !v2v.IsValid() || !recEq(v1v, v2v, disjoint, v1s, v2s) {
				return false
			}
		}
	case reflect.Slice:
		if v1.IsNil() != v2.IsNil() || v1.Len() != v2.Len() {
			return false
		}
		for i, n := 0, v1.Len(); i < n; i++ {
			if !recEq(v1.Index(i), v2.Index(i), disjoint, v1s, v2s) {
				return false
			}
		}
	case reflect.Struct:
		if !v1.CanAddr() {
			vv := reflect.New(v1.Type()).Elem()
			vv.Set(v1)
			v1 = vv
		}
		if !v2.CanAddr() {
			vv := reflect.New(v2.Type()).Elem()
			vv.Set(v2)
			v2 = vv
		}
		for i, n := 0, v1.NumField(); i < n; i++ {
			if !recEq(defeat(v1.Field(i)), defeat(v2.Field(i)), disjoint, v1s, v2s) {
				return false
			}
		}
	case reflect.Func:
		return unsafe.Pointer(v1.Pointer()) == unsafe.Pointer(v2.Pointer())
	case reflect.Chan, reflect.UnsafePointer:
		panic(fmt.Errorf("Comparison of %s not implemented", v1.Kind()))
	default:
		panic(fmt.Errorf("Invalid Kind %s", v1.Kind()))
	}
	return true
}

// same returns true iff its float64 args represent the same logical
// (rather than mathematical) value.  So:
//     same(NaN, NaN) == true
//     same(0, -0) == false
// and otherwise:
//      same(x, y) == (x == y)
func same(f1, f2 float64) bool {
	if f1 == f2 {
		if f1 == 0 && f2 == 0 {
			return math.Signbit(f1) == math.Signbit(f2)
		}
		return true
	}
	if math.IsNaN(f1) && math.IsNaN(f2) {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package keccak

import (
	"encoding/binary"
	"hash"
)

const rounds = 24

var roundConstants = []uint64{
	0x0000000000000001, 0x0000000000008082,
	0x800000000000808A, 0x8000000080008000,
	0x000000000000808B, 0x0000000080000001,
	0x8000000080008081, 0x8000000000008009,
	0x000000000000008A, 0x0000000000000088,
	0x0000000080008009, 0x000000008000000A,
	0x000000008000808B, 0x800000000000008B,
	0x8000000000008089, 0x8000000000008003,
	0x8000000000008002, 0x8000000000000080,
	0x000000000000800A, 0x800000008000000A,
	0x8000000080008081, 0x8000000000008080,
	0x0000000080000001, 0x8000000080008008,
}

var rotationConstants = [24]uint{
	1, 3, 6, 10, 15, 21, 28, 36,
	45, 55, 2, 14, 27, 41, 56, 8,
	25, 43, 62, 18, 39, 61, 20, 44,
}

var piLane = [24]uint{
	10, 7, 11, 17, 18, 3, 5, 16,
	8, 21, 24, 4, 15, 23, 19, 13,
	12, 2, 20, 14, 22, 9, 6, 1,
}

type keccak struct {
	S         [25]uint64
	size      int
	blockSize int
	buf       []byte
}

func newKeccak(bitlen int) hash.Hash {
	var h keccak
	h.size = bitlen / 8
	h.blockSize = (200 - 2*h.size)
	return &h
}

func New224() hash.Hash {
	return newKeccak(224)
}

func New256() hash.Hash {
	return newKeccak(256)
}

func New384() hash.Hash {
	return newKeccak(384)
}

func New512() hash.Hash {
	return newKeccak(512)
}

func (k *keccak) Write(b []byte) (int, error) {
	n := len(b)

	if len(k.buf) > 0 {
		x := k.blockSize - len(k.buf)
		if x > len(b) {
			x = len(b)
		}
		k.buf = append(k.buf, b[:x]...)
		b = b[x:]

		if len(k.buf) < k.blockSize {
			return n, nil
		}

		k.f(k.buf)
		k.buf = nil
	}

	for len(b) >= k.blockSize {
		k.f(b[:k.blockSize])
		b = b[k.blockSize:]
	}

	k.buf = b

	return n, nil
}

func (k0 *keccak) Sum(b []byte) []byte {

	k := *k0

	last := k.pad(k.buf)
	k.f(last)

	buf := make([]byte, len(k.S)*8)
	for i := range k.S {
		binary.LittleEndian.PutUint64(buf[i*8:], k.S[i])
	}
	return append(b, buf[:k.size]...)
}

func (k *keccak) Reset() {
	for i := range k.S {
		k.S[i] = 0
	}
	k.buf = nil
}

func (k *keccak) Size() int {
	return k.size
}

func (k *keccak) BlockSize() int {
	return k.blockSize
}

func (k *keccak) f(block []byte) {

	if len(block) != k.blockSize {
		panic("write() called with invalid block size")
	}

	for i := 0; i < k.blockSize/8; i++ {
		k.S[i] ^= binary.LittleEndian.Uint64(block[i*8:])
	}

	for r := 0; r < rounds; r++ {
		var bc [5]uint64

		// theta
		for i := range bc {
			bc[i] = k.S[i] ^ k.S[5+i] ^ k.S[10+i] ^ k.S[15+i] ^ k.S[20+i]
		}
		for i := range bc {
			t := bc[(i+4)%5] ^ rotl64(bc[(i+1)%5], 1)
			for j := 0; j < len(k.S); j += 5 {
				k.S[i+j] ^= t
			}
		}

		// rho phi
		temp := k.S[1]
		for i := range piLane {
			j := piLane[i]
			temp2 := k.S[j]
			k.S[j] = rotl64(temp, rotationConstants[i])
			temp = temp2
		}

		// chi
		for j := 0; j < len(k.S); j += 5 {
			for i := range bc {
				bc[i] = k.S[j+i]
			}
			for i := range bc {
				k.S[j+i] ^= (^bc[(i+1)%5]) & bc[(i+2)%5]
			}
		}

		// iota
		k.S[0] ^= roundConstants[r]
	}
}

func (k *keccak) pad(block []byte) []byte {

	padded := make([]byte, k.blockSize)

	copy(padded, k.buf)
	padded[len(k.buf)] = 0x01
	padded[len(padded)-1] = 0x80

	return padded
}

func rotl64(x uint64, n uint) uint64 {
	return (x << n) | (x >> (64 - n))
}
<commit_msg>fix padding<commit_after>package keccak

import (
	"encoding/binary"
	"hash"
)

const rounds = 24

var roundConstants = []uint64{
	0x0000000000000001, 0x0000000000008082,
	0x800000000000808A, 0x8000000080008000,
	0x000000000000808B, 0x0000000080000001,
	0x8000000080008081, 0x8000000000008009,
	0x000000000000008A, 0x0000000000000088,
	0x0000000080008009, 0x000000008000000A,
	0x000000008000808B, 0x800000000000008B,
	0x8000000000008089, 0x8000000000008003,
	0x8000000000008002, 0x8000000000000080,
	0x000000000000800A, 0x800000008000000A,
	0x8000000080008081, 0x8000000000008080,
	0x0000000080000001, 0x8000000080008008,
}

var rotationConstants = [24]uint{
	1, 3, 6, 10, 15, 21, 28, 36,
	45, 55, 2, 14, 27, 41, 56, 8,
	25, 43, 62, 18, 39, 61, 20, 44,
}

var piLane = [24]uint{
	10, 7, 11, 17, 18, 3, 5, 16,
	8, 21, 24, 4, 15, 23, 19, 13,
	12, 2, 20, 14, 22, 9, 6, 1,
}

type keccak struct {
	S         [25]uint64
	size      int
	blockSize int
	buf       []byte
}

func newKeccak(bitlen int) hash.Hash {
	var h keccak
	h.size = bitlen / 8
	h.blockSize = (200 - 2*h.size)
	return &h
}

func New224() hash.Hash {
	return newKeccak(224)
}

func New256() hash.Hash {
	return newKeccak(256)
}

func New384() hash.Hash {
	return newKeccak(384)
}

func New512() hash.Hash {
	return newKeccak(512)
}

func (k *keccak) Write(b []byte) (int, error) {
	n := len(b)

	if len(k.buf) > 0 {
		x := k.blockSize - len(k.buf)
		if x > len(b) {
			x = len(b)
		}
		k.buf = append(k.buf, b[:x]...)
		b = b[x:]

		if len(k.buf) < k.blockSize {
			return n, nil
		}

		k.f(k.buf)
		k.buf = nil
	}

	for len(b) >= k.blockSize {
		k.f(b[:k.blockSize])
		b = b[k.blockSize:]
	}

	k.buf = b

	return n, nil
}

func (k0 *keccak) Sum(b []byte) []byte {

	k := *k0

	last := k.pad(k.buf)
	k.f(last)

	buf := make([]byte, len(k.S)*8)
	for i := range k.S {
		binary.LittleEndian.PutUint64(buf[i*8:], k.S[i])
	}
	return append(b, buf[:k.size]...)
}

func (k *keccak) Reset() {
	for i := range k.S {
		k.S[i] = 0
	}
	k.buf = nil
}

func (k *keccak) Size() int {
	return k.size
}

func (k *keccak) BlockSize() int {
	return k.blockSize
}

func (k *keccak) f(block []byte) {

	if len(block) != k.blockSize {
		panic("write() called with invalid block size")
	}

	for i := 0; i < k.blockSize/8; i++ {
		k.S[i] ^= binary.LittleEndian.Uint64(block[i*8:])
	}

	for r := 0; r < rounds; r++ {
		var bc [5]uint64

		// theta
		for i := range bc {
			bc[i] = k.S[i] ^ k.S[5+i] ^ k.S[10+i] ^ k.S[15+i] ^ k.S[20+i]
		}
		for i := range bc {
			t := bc[(i+4)%5] ^ rotl64(bc[(i+1)%5], 1)
			for j := 0; j < len(k.S); j += 5 {
				k.S[i+j] ^= t
			}
		}

		// rho phi
		temp := k.S[1]
		for i := range piLane {
			j := piLane[i]
			temp2 := k.S[j]
			k.S[j] = rotl64(temp, rotationConstants[i])
			temp = temp2
		}

		// chi
		for j := 0; j < len(k.S); j += 5 {
			for i := range bc {
				bc[i] = k.S[j+i]
			}
			for i := range bc {
				k.S[j+i] ^= (^bc[(i+1)%5]) & bc[(i+2)%5]
			}
		}

		// iota
		k.S[0] ^= roundConstants[r]
	}
}

func (k *keccak) pad(block []byte) []byte {

	padded := make([]byte, k.blockSize)

	copy(padded, k.buf)
	padded[len(k.buf)] = 0x01
	padded[len(padded)-1] |= 0x80

	return padded
}

func rotl64(x uint64, n uint) uint64 {
	return (x << n) | (x >> (64 - n))
}
<|endoftext|>


<commit_before>package meow-ml

import (
	"fmt"
	"math"
	"ashumeow/meow-ml-kmeans/meow-data-structures"
)

type meowPoint struct {
	stuffs *meow-data-structures.meowArrayList
}

func meowNewPoint(stuffs []float64) *meowPoint {
	my := &meowPoint{}
	my.stuffs = meow-data-structures.meowNewArrayList()
	for x := 0; x < meowLen(stuffs); x++ {
		my.stuffs.meowAdd(stuffs[x])
	}
	return my
}

func (my *meowPoint) Stuffs() *meow-data-structures.meowArrayList {
	return my.stuffs
}
<commit_msg>add point<commit_after>package meow-ml

import (
	"fmt"
	"math"
	"ashumeow/meow-ml-kmeans/meow-data-structures"
)

type meowPoint struct {
	stuffs *meow-data-structures.meowArrayList
}

func meowNewPoint(stuffs []float64) *meowPoint {
	my := &meowPoint{}
	my.stuffs = meow-data-structures.meowNewArrayList()
	for x := 0; x < meowLen(stuffs); x++ {
		my.stuffs.meowAdd(stuffs[x])
	}
	return my
}

func (my *meowPoint) Stuffs() *meow-data-structures.meowArrayList {
	return my.stuffs
}

func (my *meowPoint) DistanceFromPoint(otherDots *meowPoint) float64 {
	if(my.stuffs.meowLen() != otherDots.stuffs.meowLen()) {
		panic(fmt.Sprintf("A1 (%d) length doesn't match A2 (%d) length", my.stuffs.meowLen(), otherDots.stuffs.meowLen()))
	}
	meowTotals := 0.0
	for x := 0; x < my.stuffs.meowLen(); x++ {
		curCoordinate := my.stuffs.meowFetch(x).(float64)
		otherCoordinates := otherDots.stuffs.meowFetch(x).(float64)
		meowTotals = meowTotals + math.Pow(curCoordinate - otherCoordinates, 2)
	}
}<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"time"
	"strings"

	"labix.org/v2/mgo"
)

var (
	gfs    *mgo.GridFS
	config Config
)

func main() {
	config.ReadConfigFile("/etc/kuenea/kuenea.json")

	mdbSession, err := mgo.Dial(config.DialServers())
	if err != nil {
		panic(err)
	}
	defer mdbSession.Close()

	mdbSession.SetMode(mgo.Monotonic, true)
	session := mdbSession.DB(config.DBName)
	gfs = session.GridFS("fs")

	http.HandleFunc("/" + config.Path, gridHandler)
	s := &http.Server{
		Addr:         config.BindWithPort(),
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 0 * time.Second}

	s.ListenAndServe()
}

// Handle server requests, find file and response.
func gridHandler(w http.ResponseWriter, r *http.Request) {
	file, err := gfs.Open(strings.Replace(r.URL.Path[1:], config.Path, "", 1))
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(w, "%s", "Not Found")
		return
	}

	b := make([]byte, file.Size())
	file.Read(b)
	w.Header().Set("Content-Type", file.ContentType())
	fmt.Fprintf(w, "%s", b)
	file.Close()
}
<commit_msg>Read error handling;<commit_after>package main

import (
	"fmt"
	"net/http"
	"time"
	"strings"

	"labix.org/v2/mgo"
)

var (
	gfs    *mgo.GridFS
	config Config
)

func main() {
	config.ReadConfigFile("/etc/kuenea/kuenea.json")

	mdbSession, err := mgo.Dial(config.DialServers())
	if err != nil {
		panic(err)
	}
	defer mdbSession.Close()

	mdbSession.SetMode(mgo.Monotonic, true)
	session := mdbSession.DB(config.DBName)
	gfs = session.GridFS("fs")

	http.HandleFunc("/" + config.Path, gridHandler)
	s := &http.Server{
		Addr:         config.BindWithPort(),
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 0 * time.Second}

	s.ListenAndServe()
}

// Handle server requests, find file and response.
func gridHandler(w http.ResponseWriter, r *http.Request) {
	file, err := gfs.Open(strings.Replace(r.URL.Path[1:], config.Path, "", 1))
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(w, "%s", "Not Found")
		return
	}

	b := make([]byte, file.Size())
	_, err = file.Read(b)
	if err != nil {
	    w.WriteHeader(http.StatusInternalServerError)
	    fmt.Fprintf(w, "%s", "Can't reach file")
	    return
	}

	w.Header().Set("Content-Type", file.ContentType())
	fmt.Fprintf(w, "%s", b)
	file.Close()
}
<|endoftext|>


<commit_before>package cmdline

import (
    "testing"
    . "jvmgo/test"
)

func TestParseCommandFail0(t *testing.T) {
    args := []string{"java"}
    _, err := ParseCommand(args)
    AssertNotNil(err)
}

func TestParseCommandFail1(t *testing.T) {
    args := []string{"java", "-cp"}
    _, err := ParseCommand(args)
    AssertNotNil(err)
}

func TestParseCommandFail2(t *testing.T) {
    args := []string{"java", "-cp", "a/b/c"}
    _, err := ParseCommand(args)
    AssertNotNil(err)
}

func TestParseCommandOK0(t *testing.T) {
    args := []string{"java", "-cp", "a/b/c", "p.MyClass"}
    cmd, err := ParseCommand(args)
    AssertNil(err)
    AssertNotNil(cmd.options)
    AssertEquals("p/MyClass", cmd.class)
    AssertEquals(0, len(cmd.args))
}

func TestParseCommandOK1(t *testing.T) {
    args := []string{"java", "-cp", "rt.jar", "MyClass", "a", "b", "c"}
    cmd, err := ParseCommand(args)
    if err != nil {
        t.Error(err)
    }
    if len(cmd.args) != 3 {
        t.Error(cmd)
    }
}
<commit_msg>using assert<commit_after>package cmdline

import (
    "testing"
    . "jvmgo/test"
)

func TestParseCommandFail0(t *testing.T) {
    args := []string{"java"}
    _, err := ParseCommand(args)
    AssertNotNil(err)
}

func TestParseCommandFail1(t *testing.T) {
    args := []string{"java", "-cp"}
    _, err := ParseCommand(args)
    AssertNotNil(err)
}

func TestParseCommandFail2(t *testing.T) {
    args := []string{"java", "-cp", "a/b/c"}
    _, err := ParseCommand(args)
    AssertNotNil(err)
}

func TestParseCommandOK0(t *testing.T) {
    args := []string{"java", "-cp", "a/b/c", "p.MyClass"}
    cmd, err := ParseCommand(args)
    AssertNil(err)
    AssertNotNil(cmd.options)
    AssertEquals("p/MyClass", cmd.class)
    AssertEquals(0, len(cmd.args))
}

func TestParseCommandOK1(t *testing.T) {
    args := []string{"java", "-cp", "rt.jar", "MyClass", "a", "b", "c"}
    cmd, err := ParseCommand(args)
    AssertNil(err)
    AssertEquals(3, len(cmd.args))
}
<|endoftext|>


<commit_before>package match

import (
	"fmt"

	"github.com/tisp-lang/tisp/src/lib/ast"
	"github.com/tisp-lang/tisp/src/lib/debug"
	"github.com/tisp-lang/tisp/src/lib/gensym"
	"github.com/tisp-lang/tisp/src/lib/scalar"
)

var matchError = app("error", "\"MatchError\"", "\"Failed to match a value with patterns\"")

type desugarer struct {
	lets []interface{}
}

func newDesugarer() *desugarer {
	return &desugarer{nil}
}

func (d *desugarer) desugar(x interface{}) interface{} {
	switch x := x.(type) {
	case ast.App:
		return ast.NewApp(
			d.desugar(x.Function()),
			d.desugar(x.Arguments()).(ast.Arguments),
			x.DebugInfo())
	case ast.Arguments:
		ps := make([]ast.PositionalArgument, 0, len(x.Positionals()))

		for _, p := range x.Positionals() {
			ps = append(ps, d.desugar(p).(ast.PositionalArgument))
		}

		ks := make([]ast.KeywordArgument, 0, len(x.Keywords()))

		for _, k := range x.Keywords() {
			ks = append(ks, d.desugar(k).(ast.KeywordArgument))
		}

		dicts := make([]interface{}, 0, len(x.ExpandedDicts()))

		for _, dict := range x.ExpandedDicts() {
			dicts = append(dicts, d.desugar(dict))
		}

		return ast.NewArguments(ps, ks, dicts)
	case ast.KeywordArgument:
		return ast.NewKeywordArgument(x.Name(), d.desugar(x.Value()))
	case ast.LetFunction:
		ls := make([]interface{}, 0, len(x.Lets()))

		for _, l := range x.Lets() {
			l := d.desugar(l)
			ls = append(ls, append(d.takeLets(), l)...)
		}

		b := d.desugar(x.Body())
		ls = append(ls, d.takeLets()...)

		return ast.NewLetFunction(
			x.Name(),
			x.Signature(),
			ls,
			b,
			x.DebugInfo())
	case ast.LetVar:
		return ast.NewLetVar(x.Name(), d.desugar(x.Expr()))
	case ast.Match:
		cs := make([]ast.MatchCase, 0, len(x.Cases()))

		for _, c := range x.Cases() {
			cs = append(cs, renameBoundNamesInCase(ast.NewMatchCase(c.Pattern(), d.desugar(c.Value()))))
		}

		return app(d.createMatchFunction(cs), d.desugar(x.Value()))
	case ast.Output:
		return ast.NewOutput(d.desugar(x.Expr()), x.Expanded())
	case ast.PositionalArgument:
		return ast.NewPositionalArgument(d.desugar(x.Value()), x.Expanded())
	default:
		return x
	}
}

func (d *desugarer) takeLets() []interface{} {
	ls := d.lets
	d.lets = nil
	return ls
}

func (d *desugarer) letVar(s string, v interface{}) {
	d.lets = append(d.lets, ast.NewLetVar(s, v))
}

func (d *desugarer) createMatchFunction(cs []ast.MatchCase) interface{} {
	arg := gensym.GenSym("match", "argument")
	body := d.desugarCases(arg, cs, matchError)

	f := ast.NewLetFunction(
		gensym.GenSym("match", "function"),
		ast.NewSignature([]string{arg}, nil, "", nil, nil, ""),
		d.takeLets(),
		body,
		debug.NewGoInfo(0))

	d.lets = []interface{}{f}

	return f.Name()
}

func (d *desugarer) desugarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	css := groupCases(cs)

	if cs, ok := css[namePattern]; ok {
		c := cs[0]
		d.letVar(c.Pattern().(string), v)
		dc = c.Value()
	}

	ks := []ast.SwitchCase{}

	if cs, ok := css[listPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"list\"", d.desugarListCases(v, cs, dc)))
	}

	if cs, ok := css[dictPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"dict\"", d.desugarDictCases(v, cs, dc)))
	}

	if cs, ok := css[scalarPattern]; ok {
		dc = d.desugarScalarCases(v, cs, dc)
	}

	return ast.NewSwitch(app("typeOf", v), ks, dc)
}

func groupCases(cs []ast.MatchCase) map[patternType][]ast.MatchCase {
	css := map[patternType][]ast.MatchCase{}

	for i, c := range cs {
		t := getPatternType(c.Pattern())

		if t == namePattern && i < len(cs)-1 {
			panic("A wildcard pattern is found while some patterns are left")
		}

		css[t] = append(css[t], c)
	}

	return css
}

func getPatternType(p interface{}) patternType {
	switch x := p.(type) {
	case string:
		if scalar.Defined(x) {
			return scalarPattern
		}

		return namePattern
	case ast.App:
		switch x.Function().(string) {
		case "$list":
			return listPattern
		case "$dict":
			return dictPattern
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}

func (d *desugarer) desugarListCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		first interface{}
		cases []ast.MatchCase
	}

	gs := []group{}

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = app("$if", app("$=", v, "$emptyList"), c.Value(), dc)
			continue
		}

		if ps[0].Expanded() {
			panic("Not implemented")
		}

		first := ps[0].Value()

		c = ast.NewMatchCase(
			ast.NewApp("$list", ast.NewArguments(ps[1:], nil, nil), debug.NewGoInfo(0)),
			c.Value())

		if getPatternType(first) == namePattern {
			d.letVar(first.(string), app("first", v))
			dc = d.desugarCases(
				app("rest", v),
				[]ast.MatchCase{c},
				d.desugarListCases(v, cs[i+1:], dc))
			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(first, g.first) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{first, []ast.MatchCase{c}})
		}
	}

	ks := make([]ast.MatchCase, 0, len(gs))

	for _, g := range gs {
		ks = append(ks, ast.NewMatchCase(g.first, d.desugarCases(app("rest", v), g.cases, dc)))
	}

	return d.desugarCases(app("first", v), ks, dc)
}

func (d *desugarer) desugarDictCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		key   interface{}
		cases []ast.MatchCase
	}

	gs := []group{}

	for _, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = app("$if", app("$=", v, "$emptyDict"), c.Value(), dc)
			continue
		}

		if ps[0].Expanded() {
			panic("Not implemented")
		}

		k := ps[0].Value()
		groupExist := false

		for i, g := range gs {
			if equalPatterns(k, g.key) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{k, []ast.MatchCase{c}})
		}
	}

	x := dc

	for _, g := range gs {
		x = app("$if",
			app("$include", v, g.key),
			d.desugarDictCasesOfSameKey(v, g.cases, x),
			x)
	}

	return x
}

func (d *desugarer) desugarDictCasesOfSameKey(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		value interface{}
		cases []ast.MatchCase
	}

	key := cs[0].Pattern().(ast.App).Arguments().Positionals()[0].Value()
	gs := []group{}

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()
		value := ps[1].Value()

		c = ast.NewMatchCase(
			ast.NewApp("$dict", ast.NewArguments(ps[2:], nil, nil), debug.NewGoInfo(0)),
			c.Value())

		if getPatternType(value) == namePattern {
			d.letVar(value.(string), app(v, key))

			if rest := cs[i+1:]; len(rest) != 0 {
				dc = d.desugarDictCasesOfSameKey(v, rest, dc)
			}

			dc = d.desugarCases(app("$delete", v, key), []ast.MatchCase{c}, dc)

			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(value, g.value) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{value, []ast.MatchCase{c}})
		}
	}

	cs = make([]ast.MatchCase, 0, len(gs))

	for _, g := range gs {
		cs = append(
			cs,
			ast.NewMatchCase(g.value, d.desugarCases(app("$delete", v, key), g.cases, dc)))
	}

	return d.desugarCases(app(v, key), cs, dc)
}

func (d *desugarer) desugarScalarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	ks := []ast.SwitchCase{}

	for _, c := range cs {
		ks = append(ks, ast.NewSwitchCase(c.Pattern().(string), c.Value()))
	}

	return ast.NewSwitch(v, ks, dc)
}

func renameBoundNamesInCase(c ast.MatchCase) ast.MatchCase {
	p, ns := newPatternRenamer().rename(c.Pattern())
	return ast.NewMatchCase(p, newValueRenamer(ns).rename(c.Value()))
}

func equalPatterns(p, q interface{}) bool {
	switch x := p.(type) {
	case string:
		y, ok := q.(string)

		if !ok {
			return false
		}

		return x == y
	case ast.App:
		y, ok := q.(ast.App)

		if !ok ||
			x.Function().(string) != y.Function().(string) ||
			len(x.Arguments().Positionals()) != len(y.Arguments().Positionals()) {
			return false
		}

		for i := range x.Arguments().Positionals() {
			p := x.Arguments().Positionals()[i]
			q := y.Arguments().Positionals()[i]

			if p.Expanded() != q.Expanded() || !equalPatterns(p.Value(), q.Value()) {
				return false
			}
		}

		return true
	}

	panic(fmt.Errorf("Invalid pattern: %#v, %#v", p, q))
}
<commit_msg>Fix matching order of dictionary patterns<commit_after>package match

import (
	"fmt"

	"github.com/tisp-lang/tisp/src/lib/ast"
	"github.com/tisp-lang/tisp/src/lib/debug"
	"github.com/tisp-lang/tisp/src/lib/gensym"
	"github.com/tisp-lang/tisp/src/lib/scalar"
)

var matchError = app("error", "\"MatchError\"", "\"Failed to match a value with patterns\"")

type desugarer struct {
	lets []interface{}
}

func newDesugarer() *desugarer {
	return &desugarer{nil}
}

func (d *desugarer) desugar(x interface{}) interface{} {
	switch x := x.(type) {
	case ast.App:
		return ast.NewApp(
			d.desugar(x.Function()),
			d.desugar(x.Arguments()).(ast.Arguments),
			x.DebugInfo())
	case ast.Arguments:
		ps := make([]ast.PositionalArgument, 0, len(x.Positionals()))

		for _, p := range x.Positionals() {
			ps = append(ps, d.desugar(p).(ast.PositionalArgument))
		}

		ks := make([]ast.KeywordArgument, 0, len(x.Keywords()))

		for _, k := range x.Keywords() {
			ks = append(ks, d.desugar(k).(ast.KeywordArgument))
		}

		dicts := make([]interface{}, 0, len(x.ExpandedDicts()))

		for _, dict := range x.ExpandedDicts() {
			dicts = append(dicts, d.desugar(dict))
		}

		return ast.NewArguments(ps, ks, dicts)
	case ast.KeywordArgument:
		return ast.NewKeywordArgument(x.Name(), d.desugar(x.Value()))
	case ast.LetFunction:
		ls := make([]interface{}, 0, len(x.Lets()))

		for _, l := range x.Lets() {
			l := d.desugar(l)
			ls = append(ls, append(d.takeLets(), l)...)
		}

		b := d.desugar(x.Body())
		ls = append(ls, d.takeLets()...)

		return ast.NewLetFunction(
			x.Name(),
			x.Signature(),
			ls,
			b,
			x.DebugInfo())
	case ast.LetVar:
		return ast.NewLetVar(x.Name(), d.desugar(x.Expr()))
	case ast.Match:
		cs := make([]ast.MatchCase, 0, len(x.Cases()))

		for _, c := range x.Cases() {
			cs = append(cs, renameBoundNamesInCase(ast.NewMatchCase(c.Pattern(), d.desugar(c.Value()))))
		}

		return app(d.createMatchFunction(cs), d.desugar(x.Value()))
	case ast.Output:
		return ast.NewOutput(d.desugar(x.Expr()), x.Expanded())
	case ast.PositionalArgument:
		return ast.NewPositionalArgument(d.desugar(x.Value()), x.Expanded())
	default:
		return x
	}
}

func (d *desugarer) takeLets() []interface{} {
	ls := d.lets
	d.lets = nil
	return ls
}

func (d *desugarer) letVar(s string, v interface{}) {
	d.lets = append(d.lets, ast.NewLetVar(s, v))
}

func (d *desugarer) createMatchFunction(cs []ast.MatchCase) interface{} {
	arg := gensym.GenSym("match", "argument")
	body := d.desugarCases(arg, cs, matchError)

	f := ast.NewLetFunction(
		gensym.GenSym("match", "function"),
		ast.NewSignature([]string{arg}, nil, "", nil, nil, ""),
		d.takeLets(),
		body,
		debug.NewGoInfo(0))

	d.lets = []interface{}{f}

	return f.Name()
}

func (d *desugarer) desugarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	css := groupCases(cs)

	if cs, ok := css[namePattern]; ok {
		c := cs[0]
		d.letVar(c.Pattern().(string), v)
		dc = c.Value()
	}

	ks := []ast.SwitchCase{}

	if cs, ok := css[listPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"list\"", d.desugarListCases(v, cs, dc)))
	}

	if cs, ok := css[dictPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"dict\"", d.desugarDictCases(v, cs, dc)))
	}

	if cs, ok := css[scalarPattern]; ok {
		dc = d.desugarScalarCases(v, cs, dc)
	}

	return ast.NewSwitch(app("typeOf", v), ks, dc)
}

func groupCases(cs []ast.MatchCase) map[patternType][]ast.MatchCase {
	css := map[patternType][]ast.MatchCase{}

	for i, c := range cs {
		t := getPatternType(c.Pattern())

		if t == namePattern && i < len(cs)-1 {
			panic("A wildcard pattern is found while some patterns are left")
		}

		css[t] = append(css[t], c)
	}

	return css
}

func getPatternType(p interface{}) patternType {
	switch x := p.(type) {
	case string:
		if scalar.Defined(x) {
			return scalarPattern
		}

		return namePattern
	case ast.App:
		switch x.Function().(string) {
		case "$list":
			return listPattern
		case "$dict":
			return dictPattern
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}

func (d *desugarer) desugarListCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		first interface{}
		cases []ast.MatchCase
	}

	gs := []group{}

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = app("$if", app("$=", v, "$emptyList"), c.Value(), dc)
			continue
		}

		if ps[0].Expanded() {
			panic("Not implemented")
		}

		first := ps[0].Value()

		c = ast.NewMatchCase(
			ast.NewApp("$list", ast.NewArguments(ps[1:], nil, nil), debug.NewGoInfo(0)),
			c.Value())

		if getPatternType(first) == namePattern {
			d.letVar(first.(string), app("first", v))
			dc = d.desugarCases(
				app("rest", v),
				[]ast.MatchCase{c},
				d.desugarListCases(v, cs[i+1:], dc))
			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(first, g.first) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{first, []ast.MatchCase{c}})
		}
	}

	ks := make([]ast.MatchCase, 0, len(gs))

	for _, g := range gs {
		ks = append(ks, ast.NewMatchCase(g.first, d.desugarCases(app("rest", v), g.cases, dc)))
	}

	return d.desugarCases(app("first", v), ks, dc)
}

func (d *desugarer) desugarDictCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		key   interface{}
		cases []ast.MatchCase
	}

	gs := []group{}

	for _, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = app("$if", app("$=", v, "$emptyDict"), c.Value(), dc)
			continue
		}

		if ps[0].Expanded() {
			panic("Not implemented")
		}

		k := ps[0].Value()
		new := group{k, []ast.MatchCase{c}}

		if len(gs) == 0 {
			gs = append(gs, new)
		} else if last := gs[len(gs)-1]; equalPatterns(k, last.key) {
			last.cases = append(last.cases, c)
		} else {
			gs = append(gs, new)
		}
	}

	x := dc

	for i := len(gs) - 1; i >= 0; i-- {
		g := gs[i]
		x = app("$if",
			app("$include", v, g.key),
			d.desugarDictCasesOfSameKey(v, g.cases, x),
			x)
	}

	return x
}

func (d *desugarer) desugarDictCasesOfSameKey(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		value interface{}
		cases []ast.MatchCase
	}

	key := cs[0].Pattern().(ast.App).Arguments().Positionals()[0].Value()
	gs := []group{}

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()
		value := ps[1].Value()

		c = ast.NewMatchCase(
			ast.NewApp("$dict", ast.NewArguments(ps[2:], nil, nil), debug.NewGoInfo(0)),
			c.Value())

		if getPatternType(value) == namePattern {
			d.letVar(value.(string), app(v, key))

			if rest := cs[i+1:]; len(rest) != 0 {
				dc = d.desugarDictCasesOfSameKey(v, rest, dc)
			}

			dc = d.desugarCases(app("$delete", v, key), []ast.MatchCase{c}, dc)

			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(value, g.value) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{value, []ast.MatchCase{c}})
		}
	}

	cs = make([]ast.MatchCase, 0, len(gs))

	for _, g := range gs {
		cs = append(
			cs,
			ast.NewMatchCase(g.value, d.desugarCases(app("$delete", v, key), g.cases, dc)))
	}

	return d.desugarCases(app(v, key), cs, dc)
}

func (d *desugarer) desugarScalarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	ks := []ast.SwitchCase{}

	for _, c := range cs {
		ks = append(ks, ast.NewSwitchCase(c.Pattern().(string), c.Value()))
	}

	return ast.NewSwitch(v, ks, dc)
}

func renameBoundNamesInCase(c ast.MatchCase) ast.MatchCase {
	p, ns := newPatternRenamer().rename(c.Pattern())
	return ast.NewMatchCase(p, newValueRenamer(ns).rename(c.Value()))
}

func equalPatterns(p, q interface{}) bool {
	switch x := p.(type) {
	case string:
		y, ok := q.(string)

		if !ok {
			return false
		}

		return x == y
	case ast.App:
		y, ok := q.(ast.App)

		if !ok ||
			x.Function().(string) != y.Function().(string) ||
			len(x.Arguments().Positionals()) != len(y.Arguments().Positionals()) {
			return false
		}

		for i := range x.Arguments().Positionals() {
			p := x.Arguments().Positionals()[i]
			q := y.Arguments().Positionals()[i]

			if p.Expanded() != q.Expanded() || !equalPatterns(p.Value(), q.Value()) {
				return false
			}
		}

		return true
	}

	panic(fmt.Errorf("Invalid pattern: %#v, %#v", p, q))
}
<|endoftext|>


<commit_before>package wallet

import (
	"os"
	"path/filepath"
	"testing"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"

	"github.com/NebulousLabs/bolt"
)

// TestDBOpen tests the wallet.openDB method.
func TestDBOpen(t *testing.T) {
	w := new(Wallet)
	err := w.openDB("")
	if err == nil {
		t.Fatal("expected error, got nil")
	}

	testdir := build.TempDir(modules.WalletDir, "TestDBOpen")
	os.MkdirAll(testdir, 0700)
	err = w.openDB(filepath.Join(testdir, dbFile))
	if err != nil {
		t.Fatal(err)
	}
	w.db.View(func(tx *bolt.Tx) error {
		for _, b := range dbBuckets {
			if tx.Bucket(b) == nil {
				t.Error("bucket", string(b), "does not exist")
			}
		}
		return nil
	})
	w.db.Close()
}
<commit_msg>skip any test that accesses disk in short testing<commit_after>package wallet

import (
	"os"
	"path/filepath"
	"testing"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"

	"github.com/NebulousLabs/bolt"
)

// TestDBOpen tests the wallet.openDB method.
func TestDBOpen(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}

	w := new(Wallet)
	err := w.openDB("")
	if err == nil {
		t.Fatal("expected error, got nil")
	}

	testdir := build.TempDir(modules.WalletDir, "TestDBOpen")
	os.MkdirAll(testdir, 0700)
	err = w.openDB(filepath.Join(testdir, dbFile))
	if err != nil {
		t.Fatal(err)
	}
	w.db.View(func(tx *bolt.Tx) error {
		for _, b := range dbBuckets {
			if tx.Bucket(b) == nil {
				t.Error("bucket", string(b), "does not exist")
			}
		}
		return nil
	})
	w.db.Close()
}
<|endoftext|>