<commit_before>package server

var (
	// This must be kept in sync with the build exceptions in "isaac-tournament-bot".
	buildExceptions = [][]string{
		// #0 - n/a
		{},

		// -------------------
		// Treasure Room items
		// -------------------

		// #1 - Cricket's Body
		{
			// The Brimstone beam inherits the split shots but they are not very good.
			"Azazel", // 7

			// The Brimstone beam inherits the split shots but they are not very good.
			"Tainted Azazel", // 28
		},

		{}, // #2 - Cricket's Head

		// #3 - Dead Eye
		{
			// The Brimstone beam prevents it from working.
			"Azazel", // 7

			// It is hard to be accurate when shots come from Incubus.
			"Lilith", // 13

			// It is hard to be accurate with triple shot.
			"Keeper", // 14

			// The Brimstone beam prevents it from working.
			"Tainted Azazel", // 28

			// It is hard to be accurate with quad shot.
			"Tainted Keeper", // 33
		},

		// #4 - Death's Touch
		{
			// The piercing shots do nothing for the bone club.
			"The Forgotten", // 16

			// The piercing shots do nothing for the bone club.
			"Tainted Forgotten", // 35

			// The piercing shots do nothing for Azazel's Brimstone.
			"Azazel", // 7
		},

		// #5 - Dr. Fetus
		{
			// Very annoying to use with the skeleton body.
			"Tainted Forgotten", // 35
		},

		// #6 - Ipecac
		{
			// The short-range brimstone causes self-damage.
			"Azazel", // 7

			// Can cause unavoidable damage if a clot is behind you or shoots at an obstacle near
			// you.
			"Tainted Eve", // 26
		},

		// #7 - Magic Mushroom
		{},

		// #8 - Mom's Knife
		{},

		// #9 - Polyphemus
		{},

		// #10 - Proptosis
		{},

		// #11 - Tech.5
		{},

		// #12 - Tech X
		{},

		// #13 - C Section
		{},

		// ----------------
		// Devil Room items
		// ----------------

		// #14 - Brimstone
		{
			// Gello fires the brim very slowly and auto-fire is not always accurate.
			"Tainted Lilith", // 32
		},

		// #15 - Maw of the Void
		{},

		// ----------------
		// Angel Room items
		// ----------------

		// #16 - Crown of Light
		{
			// Eve cannot use the razor with this start.
			"Eve", // 5

			// Crown is never full with the depleting hearts.
			"Tainted Magdalene", // 22

			// Crown can't be active with the clot mechanic.
			"Tainted Eve", // 26
		},

		// #17 - Sacred Heart
		{},

		// #18 - Spirit Sword
		{
			// Annoying because the sword goes to Incubus.
			"Lilith", // 13

			// No synergy with the bone club.
			"The Forgotten", // 16

			// No synergy with the bone club.
			"Tainted Forgotten", // 35
		},

		// #19 - Revelation
		{},

		// -----------------
		// Secret Room items
		// -----------------

		// #20 - Epic Fetus
		{
			// The target keeps moving and you can't control it, making it impossible to target
			// enemies.
			"Tainted Lilith", // 32
		},

		// ------------
		// Custom items
		// ------------

		// #21 - Sawblade
		{
			// Very complicated to play orbitals with her because she can't protect herself from
			// losing the deal with soul hearts.
			"Bethany", // 18

			// Impossible to play orbitals with Tainted Eve's clots, they will disappear very
			// quickly.
			"Tainted Eve", // 26

			// With his health mechanic, it is too dangerous to use orbitals.
			"Tainted Lost", // 31
		},

		// ------
		// Builds
		// ------

		// #22 - 20/20 + The Inner Eye
		{},

		// #23 - Chocolate Milk + Steven
		{},

		// #24 - Godhead + Cupid's Arrow
		{
			// Small damage up for a tears down, resulting in a loss of DPS overall.
			"Azazel", // 7

			// Does nothing with the bone club.
			"The Forgotten", // 16

			// Does nothing with the bone club.
			"Tainted Forgotten", // 35
		},

		// #25 - Haemolacria + The Sad Onion
		{},

		// #26 - Incubus + Incubus
		{},

		// #27 - Monstro's Lung + The Sad Onion
		{
			// Huge tears down, resulting in a loss of DPS overall.
			"Keeper", // 14

			// Tears down, worse than having no starter with the fetus.
			"Tainted Lilith", // 32

			// Huge tears down, resulting in a loss of DPS overall.
			"Tainted Keeper", // 33
		},

		// #28 - Technology + A Lump of Coal
		{},

		// #29 - Twisted Pair + Twisted Pair
		{},

		// #30 - Pointy Rib + Eve's Mascara
		{},

		// #31 - Fire Mind + Mysterious Liquid + 13 Luck.
		{
			// The synergy is only useful with a tear build.
			"Azazel", // 7

			// Luck does not apply to Incubus for some reason.
			"Lilith", // 13

			// The synergy is only useful with a tear build.
			"The Forgotten", // 16

			// The synergy is only useful with a tear build.
			"Tainted Azazel", // 28

			// Too dangerous to be synergistic.
			"Tainted Lost", // 31

			// The synergy is only useful with a tear build.
			"Tainted Forgotten", // 35
		},

		// #32 - Eye of the Occult + Loki's Horns + 15 Luck
		{
			// Homing brimstone is too powerful, resulting in a build with a low-skill requirement.
			"Azazel", // 7

			// It is only a damage up on the bone club.
			"The Forgotten", // 16

			// Homing brimstone is too powerful, resulting in a build with a low-skill requirement.
			"Tainted Azazel", // 28

			// It is only a damage up on the bone club.
			"Tainted Forgotten", // 35
		},

		// #33 - Distant Admiration + Friend Zone + Forever Alone + BFFS!
		{},
	}
)
<commit_msg>Reorder and T. Azazel Death Touch (#83)<commit_after>package server

var (
	// This must be kept in sync with the build exceptions in "isaac-tournament-bot".
	buildExceptions = [][]string{
		// #0 - n/a
		{},

		// -------------------
		// Treasure Room items
		// -------------------

		// #1 - Cricket's Body
		{
			// The Brimstone beam inherits the split shots but they are not very good.
			"Azazel", // 7

			// The Brimstone beam inherits the split shots but they are not very good.
			"Tainted Azazel", // 28
		},

		{}, // #2 - Cricket's Head

		// #3 - Dead Eye
		{
			// The Brimstone beam prevents it from working.
			"Azazel", // 7

			// It is hard to be accurate when shots come from Incubus.
			"Lilith", // 13

			// It is hard to be accurate with triple shot.
			"Keeper", // 14

			// The Brimstone beam prevents it from working.
			"Tainted Azazel", // 28

			// It is hard to be accurate with quad shot.
			"Tainted Keeper", // 33
		},

		// #4 - Death's Touch
		{
			// The piercing shots do nothing for Azazel's Brimstone.
			"Azazel", // 7

			// The piercing shots do nothing for the bone club.
			"The Forgotten", // 16

			// The piercing shots do nothing for Tainted Azazel's Brimstone.
			"Tainted Azazel", // 28

			// The piercing shots do nothing for the bone club.
			"Tainted Forgotten", // 35
		},

		// #5 - Dr. Fetus
		{
			// Very annoying to use with the skeleton body.
			"Tainted Forgotten", // 35
		},

		// #6 - Ipecac
		{
			// The short-range brimstone causes self-damage.
			"Azazel", // 7

			// Can cause unavoidable damage if a clot is behind you or shoots at an obstacle near
			// you.
			"Tainted Eve", // 26
		},

		// #7 - Magic Mushroom
		{},

		// #8 - Mom's Knife
		{},

		// #9 - Polyphemus
		{},

		// #10 - Proptosis
		{},

		// #11 - Tech.5
		{},

		// #12 - Tech X
		{},

		// #13 - C Section
		{},

		// ----------------
		// Devil Room items
		// ----------------

		// #14 - Brimstone
		{
			// Gello fires the brim very slowly and auto-fire is not always accurate.
			"Tainted Lilith", // 32
		},

		// #15 - Maw of the Void
		{},

		// ----------------
		// Angel Room items
		// ----------------

		// #16 - Crown of Light
		{
			// Eve cannot use the razor with this start.
			"Eve", // 5

			// Crown is never full with the depleting hearts.
			"Tainted Magdalene", // 22

			// Crown can't be active with the clot mechanic.
			"Tainted Eve", // 26
		},

		// #17 - Sacred Heart
		{},

		// #18 - Spirit Sword
		{
			// Annoying because the sword goes to Incubus.
			"Lilith", // 13

			// No synergy with the bone club.
			"The Forgotten", // 16

			// No synergy with the bone club.
			"Tainted Forgotten", // 35
		},

		// #19 - Revelation
		{},

		// -----------------
		// Secret Room items
		// -----------------

		// #20 - Epic Fetus
		{
			// The target keeps moving and you can't control it, making it impossible to target
			// enemies.
			"Tainted Lilith", // 32
		},

		// ------------
		// Custom items
		// ------------

		// #21 - Sawblade
		{
			// Very complicated to play orbitals with her because she can't protect herself from
			// losing the deal with soul hearts.
			"Bethany", // 18

			// Impossible to play orbitals with Tainted Eve's clots, they will disappear very
			// quickly.
			"Tainted Eve", // 26

			// With his health mechanic, it is too dangerous to use orbitals.
			"Tainted Lost", // 31
		},

		// ------
		// Builds
		// ------

		// #22 - 20/20 + The Inner Eye
		{},

		// #23 - Chocolate Milk + Steven
		{},

		// #24 - Godhead + Cupid's Arrow
		{
			// Small damage up for a tears down, resulting in a loss of DPS overall.
			"Azazel", // 7

			// Does nothing with the bone club.
			"The Forgotten", // 16

			// Does nothing with the bone club.
			"Tainted Forgotten", // 35
		},

		// #25 - Haemolacria + The Sad Onion
		{},

		// #26 - Incubus + Incubus
		{},

		// #27 - Monstro's Lung + The Sad Onion
		{
			// Huge tears down, resulting in a loss of DPS overall.
			"Keeper", // 14

			// Tears down, worse than having no starter with the fetus.
			"Tainted Lilith", // 32

			// Huge tears down, resulting in a loss of DPS overall.
			"Tainted Keeper", // 33
		},

		// #28 - Technology + A Lump of Coal
		{},

		// #29 - Twisted Pair + Twisted Pair
		{},

		// #30 - Pointy Rib + Eve's Mascara
		{},

		// #31 - Fire Mind + Mysterious Liquid + 13 Luck.
		{
			// The synergy is only useful with a tear build.
			"Azazel", // 7

			// Luck does not apply to Incubus for some reason.
			"Lilith", // 13

			// The synergy is only useful with a tear build.
			"The Forgotten", // 16

			// The synergy is only useful with a tear build.
			"Tainted Azazel", // 28

			// Too dangerous to be synergistic.
			"Tainted Lost", // 31

			// The synergy is only useful with a tear build.
			"Tainted Forgotten", // 35
		},

		// #32 - Eye of the Occult + Loki's Horns + 15 Luck
		{
			// Homing brimstone is too powerful, resulting in a build with a low-skill requirement.
			"Azazel", // 7

			// It is only a damage up on the bone club.
			"The Forgotten", // 16

			// Homing brimstone is too powerful, resulting in a build with a low-skill requirement.
			"Tainted Azazel", // 28

			// It is only a damage up on the bone club.
			"Tainted Forgotten", // 35
		},

		// #33 - Distant Admiration + Friend Zone + Forever Alone + BFFS!
		{},
	}
)
<|endoftext|>


<commit_before>/*
 * Minio S3Verify Library for Amazon S3 Compatible Cloud Storage (C) 2016 Minio, Inc.
 *
 * 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 main

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
)

// overWrittenHeaers - map the request headers that can be sent
// to the response headers they will overwrite.
// These are the headers that support overwriting according to
// http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html
var overWrittenHeaders = map[string]string{
	"response-content-type":        "Content-Type",
	"response-content-language":    "Content-Language",
	"response-expires":             "Expires",
	"response-cache-control":       "Cache-Control",
	"response-content-disposition": "Content-Disposition",
	"response-content-encoding":    "Content-Encoding",
}

// newGetObjectReq - Create a new HTTP requests to perform.
func newGetObjectReq(bucketName, objectName string, responseHeaders map[string]string) (Request, error) {
	// getObjectReq - a new HTTP request for a GET object.
	var getObjectReq = Request{
		customHeader: http.Header{},
	}

	// Set the bucketName and objectName.
	getObjectReq.bucketName = bucketName
	getObjectReq.objectName = objectName

	reader := bytes.NewReader([]byte{}) // Compute hash using empty body because GET requests do not send a body.
	_, sha256Sum, _, err := computeHash(reader)
	if err != nil {
		return Request{}, err
	}

	urlValues := make(url.Values)
	// Overide response headers.
	for k, v := range responseHeaders {
		urlValues.Set(k, v)
	}
	getObjectReq.queryValues = urlValues

	// Set the headers.
	getObjectReq.customHeader.Set("User-Agent", appUserAgent)
	getObjectReq.customHeader.Set("X-Amz-Content-Sha256", hex.EncodeToString(sha256Sum))

	return getObjectReq, nil
}

// TODO: These checks only verify correctly formatted requests. There is no request that is made to fail / check failure yet.

// getObjectVerify - Check a Response's Status, Headers, and Body for AWS S3 compliance.
func getObjectVerify(res *http.Response, expectedBody []byte, expectedStatusCode int, expectedHeader map[string]string, expectedError ErrorResponse) error {
	if err := verifyHeaderGetObject(res.Header, expectedHeader); err != nil {
		return err
	}
	if err := verifyStatusGetObject(res.StatusCode, expectedStatusCode); err != nil {
		return err
	}
	if err := verifyBodyGetObject(res.Body, expectedBody, expectedError); err != nil {
		return err
	}
	return nil
}

// verifyHeaderGetObject - Verify that the header returned matches what is expected.
func verifyHeaderGetObject(header http.Header, expectedHeaders map[string]string) error {
	if err := verifyStandardHeaders(header); err != nil {
		return err
	}
	for k, v := range expectedHeaders {
		headerKey := overWrittenHeaders[k]
		if header.Get(headerKey) != v {
			err := fmt.Errorf("Unexpected Header Value Received for %s: wanted %s, got %s", k, v, header.Get(headerKey))
			return err
		}
	}
	return nil
}

// verifyBodyGetObject - Verify that the body returned matches what is expected.
func verifyBodyGetObject(resBody io.Reader, expectedBody []byte, expectedError ErrorResponse) error {
	if expectedError.Message == "" {
		body, err := ioutil.ReadAll(resBody)
		if err != nil {
			return err
		}
		// Compare what was created to be uploaded and what is contained in the response body.
		if !bytes.Equal(body, expectedBody) {
			err := fmt.Errorf("Unexpected Body Recieved: wanted %v, got %v", string(expectedBody), string(body))
			return err
		}
		return nil
	}
	// Verify the error returned was correct.
	receivedError := ErrorResponse{}
	if err := xmlDecoder(resBody, &receivedError); err != nil {
		return err
	}
	if receivedError.Message != expectedError.Message {
		err := fmt.Errorf("Unexpected Error Message Received: wanted %s, got %s", expectedError.Message, receivedError.Message)
		return err
	}
	if receivedError.Code != expectedError.Code {
		err := fmt.Errorf("Unexpected Error Code Received: wanted %s, got %s", expectedError.Code, receivedError.Code)
		return err
	}
	return nil
}

// verifyStatusGetObject - Verify that the status returned matches what is expected.
func verifyStatusGetObject(respStatusCode, expectedStatusCode int) error {
	if respStatusCode != expectedStatusCode {
		err := fmt.Errorf("Unexpected Response Status Code: wanted %v, got %v", expectedStatusCode, respStatusCode)
		return err
	}
	return nil
}

// mainGetObject - test a get object request.
func mainGetObject(config ServerConfig, curTest int) bool {
	message := fmt.Sprintf("[%02d/%d] GetObject:", curTest, globalTotalNumTest)
	// Use the bucket created in the mainPutBucketPrepared Test.
	// Set the response headers to be overwritten.
	expectedHeaders := map[string]string{
		"response-content-type":        "image/gif",
		"response-content-language":    "da",
		"response-expires":             "Thu, 01 Dec 1994 16:00:00 GMT",
		"response-cache-control":       "no-cache",
		"response-content-disposition": "attachment; filename=\"s3verify.txt\"",
		// "response-content-encoding":    "gzip",
	}
	// All getobject tests happen in s3verify created buckets
	// on s3verify objects.
	bucketName := s3verifyBuckets[0].Name
	for _, object := range s3verifyObjects {
		// Spin scanBar
		scanBar(message)
		// Create new GET object request.
		req, err := newGetObjectReq(bucketName, object.Key, expectedHeaders)
		if err != nil {
			printMessage(message, err)
			return false
		}
		// Execute the request.
		res, err := config.execRequest("GET", req)
		if err != nil {
			printMessage(message, err)
			return false
		}
		defer closeResponse(res)
		// Verify the response.
		if err := getObjectVerify(res, object.Body, http.StatusOK, expectedHeaders, ErrorResponse{}); err != nil {
			printMessage(message, err)
			return false
		}
		// Spin scanBar
		scanBar(message)
	}
	// Spin scanBar
	scanBar(message)
	// Test passed.
	printMessage(message, nil)
	return true
}
<commit_msg>GetObject: add new test for invalid key getobject (#186)<commit_after>/*
 * Minio S3Verify Library for Amazon S3 Compatible Cloud Storage (C) 2016 Minio, Inc.
 *
 * 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 main

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
)

// overWrittenHeaers - map the request headers that can be sent
// to the response headers they will overwrite.
// These are the headers that support overwriting according to
// http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html
var overWrittenHeaders = map[string]string{
	"response-content-type":        "Content-Type",
	"response-content-language":    "Content-Language",
	"response-expires":             "Expires",
	"response-cache-control":       "Cache-Control",
	"response-content-disposition": "Content-Disposition",
	"response-content-encoding":    "Content-Encoding",
}

// newGetObjectReq - Create a new HTTP requests to perform.
func newGetObjectReq(bucketName, objectName string, responseHeaders map[string]string) (Request, error) {
	// getObjectReq - a new HTTP request for a GET object.
	var getObjectReq = Request{
		customHeader: http.Header{},
	}

	// Set the bucketName and objectName.
	getObjectReq.bucketName = bucketName
	getObjectReq.objectName = objectName

	reader := bytes.NewReader([]byte{}) // Compute hash using empty body because GET requests do not send a body.
	_, sha256Sum, _, err := computeHash(reader)
	if err != nil {
		return Request{}, err
	}

	urlValues := make(url.Values)
	// Overide response headers.
	for k, v := range responseHeaders {
		urlValues.Set(k, v)
	}
	getObjectReq.queryValues = urlValues

	// Set the headers.
	getObjectReq.customHeader.Set("User-Agent", appUserAgent)
	getObjectReq.customHeader.Set("X-Amz-Content-Sha256", hex.EncodeToString(sha256Sum))

	return getObjectReq, nil
}

// TODO: These checks only verify correctly formatted requests. There is no request that is made to fail / check failure yet.

// getObjectVerify - Check a Response's Status, Headers, and Body for AWS S3 compliance.
func getObjectVerify(res *http.Response, expectedBody []byte, expectedStatusCode int, expectedHeader map[string]string, expectedError ErrorResponse) error {
	if err := verifyHeaderGetObject(res.Header, expectedHeader); err != nil {
		return err
	}
	if err := verifyStatusGetObject(res.StatusCode, expectedStatusCode); err != nil {
		return err
	}
	if err := verifyBodyGetObject(res.Body, expectedBody, expectedError); err != nil {
		return err
	}
	return nil
}

// verifyHeaderGetObject - Verify that the header returned matches what is expected.
func verifyHeaderGetObject(header http.Header, expectedHeaders map[string]string) error {
	if err := verifyStandardHeaders(header); err != nil {
		return err
	}
	for k, v := range expectedHeaders {
		headerKey := overWrittenHeaders[k]
		if header.Get(headerKey) != v {
			err := fmt.Errorf("Unexpected Header Value Received for %s: wanted %s, got %s", k, v, header.Get(headerKey))
			return err
		}
	}
	return nil
}

// verifyBodyGetObject - Verify that the body returned matches what is expected.
func verifyBodyGetObject(resBody io.Reader, expectedBody []byte, expectedError ErrorResponse) error {
	if expectedError.Message == "" {
		body, err := ioutil.ReadAll(resBody)
		if err != nil {
			return err
		}
		// Compare what was created to be uploaded and what is contained in the response body.
		if !bytes.Equal(body, expectedBody) {
			err := fmt.Errorf("Unexpected Body Recieved: wanted %v, got %v", string(expectedBody), string(body))
			return err
		}
		return nil
	}
	// Verify the error returned was correct.
	receivedError := ErrorResponse{}
	if err := xmlDecoder(resBody, &receivedError); err != nil {
		return err
	}
	if receivedError.Message != expectedError.Message {
		err := fmt.Errorf("Unexpected Error Message Received: wanted %s, got %s", expectedError.Message, receivedError.Message)
		return err
	}
	if receivedError.Code != expectedError.Code {
		err := fmt.Errorf("Unexpected Error Code Received: wanted %s, got %s", expectedError.Code, receivedError.Code)
		return err
	}
	return nil
}

// verifyStatusGetObject - Verify that the status returned matches what is expected.
func verifyStatusGetObject(respStatusCode, expectedStatusCode int) error {
	if respStatusCode != expectedStatusCode {
		err := fmt.Errorf("Unexpected Response Status Code: wanted %v, got %v", expectedStatusCode, respStatusCode)
		return err
	}
	return nil
}

// mainGetObject - test a get object request.
func mainGetObject(config ServerConfig, curTest int) bool {
	message := fmt.Sprintf("[%02d/%d] GetObject:", curTest, globalTotalNumTest)
	// Use the bucket created in the mainPutBucketPrepared Test.
	// Set the response headers to be overwritten.
	expectedHeaders := map[string]string{
		"response-content-type":        "image/gif",
		"response-content-language":    "da",
		"response-expires":             "Thu, 01 Dec 1994 16:00:00 GMT",
		"response-cache-control":       "no-cache",
		"response-content-disposition": "attachment; filename=\"s3verify.txt\"",
		// "response-content-encoding":    "gzip",
	}
	// All getobject tests happen in s3verify created buckets
	// on s3verify objects.
	bucketName := s3verifyBuckets[0].Name
	testObject := s3verifyObjects[0]
	// Spin scanBar
	scanBar(message)

	// Create new valid GET object request.
	req, err := newGetObjectReq(bucketName, testObject.Key, expectedHeaders)
	if err != nil {
		printMessage(message, err)
		return false
	}
	// Execute the request.
	res, err := config.execRequest("GET", req)
	if err != nil {
		printMessage(message, err)
		return false
	}
	defer closeResponse(res)
	// Verify the response.
	if err := getObjectVerify(res, testObject.Body, http.StatusOK, expectedHeaders, ErrorResponse{}); err != nil {
		printMessage(message, err)
		return false
	}

	// Test getobject on an object that DNE.
	invalidKeyError := ErrorResponse{
		Code:    "NoSuchKey",
		Message: "The specified key does not exist.",
	}
	invalidKeyReq, err := newGetObjectReq(bucketName, testObject.Key+"-DNE", nil)
	if err != nil {
		printMessage(message, err)
		return false
	}
	// Execute the request.
	invalidKeyRes, err := config.execRequest("GET", invalidKeyReq)
	if err != nil {
		printMessage(message, err)
		return false
	}
	defer closeResponse(invalidKeyRes)
	// Verify the request failed.
	if err := getObjectVerify(invalidKeyRes, []byte{}, http.StatusNotFound, nil, invalidKeyError); err != nil {
		printMessage(message, err)
		return false
	}

	// Spin scanBar
	scanBar(message)
	// Test passed.
	printMessage(message, nil)
	return true
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"os/exec"

	"github.com/herman-rogers/gogeta/config"
	"github.com/herman-rogers/gogeta/logger"
	git "github.com/libgit2/git2go"
	uuid "github.com/satori/go.uuid"
)

const UP_TO_DATE = "UP_TO_DATE"
const NORMAL_MERGE = "NORMAL_MERGE"
const FAST_FORWARD = "FAST_FORWARD"

func GitProcessSQSMessages(data scmServiceRequest) error {
	if data.Usr == "" || data.Project == "" || data.Repo == "" {
		message := "Missing git data"
		SendRawMessage(
			data.Id,
			data.Buildcount,
			message,
			message)
		return errors.New("Missing Git Service Properties")
	}
	switch data.Type {
	case "GOGETA_CLONE":
		go GitShallowClone(data)
	case "GOGETA_NEW_BUILD":
		go RunNewGitBuild(data)
	}
	return nil
}

func GitShallowClone(data scmServiceRequest) {
	repoPath, relativePath := GetRepoPath(data.Project)
	cmd := exec.Command("git", "clone", "--depth", "2", data.Repo, repoPath)

	cloneMsgDev := "git clone --depth 2 " + data.Repo + " " + repoPath
	SendRawMessage(
		data.Id,
		data.Buildcount,
		"git clone started",
		cloneMsgDev)

	logfile := logger.GetLogFile()
	defer logfile.Close()

	cmd.Stdout = logfile
	cmd.Stderr = logfile

	commandErr := cmd.Start()
	logger.LogError(commandErr, "Git Clone")
	if commandErr != nil {
		SendRawMessage(
			data.Id,
			data.Buildcount,
			"git clone failed",
			commandErr.Error())
	}

	cloneErr := cmd.Wait()
	logger.LogError(cloneErr, "Git Clone")
	if cloneErr != nil {
		SendRawMessage(
			data.Id,
			data.Buildcount,
			"git clone failed",
			cloneErr.Error())
	}
	if cloneErr == nil {
		cloneSuccess := "git clone succeded"
		SendRawMessage(
			data.Id,
			data.Buildcount,
			cloneSuccess,
			cloneSuccess)
		gitData := &GogetaRepo{
			data.Id,
			data.Usr,
			data.Repo,
			relativePath,
			data.SCMType,
			data.Engine,
			data.Platform,
			data.Buildcount,
		}
		CreateGitCredentials(repoPath)
		go SaveRepo(*gitData)
		go TriggerMrRobotBuild(*gitData)
	}
}

func RunNewGitBuild(data scmServiceRequest) {
	gitData := FindRepo(data.Usr, data.Id)
	gitData.BuildCount = data.Buildcount + 1
	// put raw message in new build
	SendRawMessage(
		gitData.BuildrId,
		gitData.BuildCount,
		"new build started",
		"new build started")
	go TriggerMrRobotBuild(gitData)
	go UpdateRepo(gitData)
}

func UpdateGitRepositories() {
	repos := FindAllRepos()
	for i := 0; i < len(repos); i++ {
		folder := config.File.RepoPath + repos[i].Folder
		repo, err := git.OpenRepository(folder)
		if err != nil {
			logger.Error(err.Error())
			continue
		}
		if repo.IsBare() {
			return
		}
		go GitPull(repos[i], repo)
	}
}

func GetRepoPath(project string) (string, string) {
	var uuid uuid.UUID = uuid.NewV4()
	relativePath := project + "_" + uuid.String()
	repoPath := config.File.RepoPath + relativePath
	return repoPath, relativePath
}

func CreateGitCredentials(repo string) {
	openRepo, err := git.OpenRepository(repo)
	if err != nil {
		logger.LogError(err, "Git Create Credentials")
	}
	config, configErr := openRepo.Config()
	if configErr != nil {
		logger.LogError(configErr, "Git Config Set")
	}
	configNameErr := config.SetString("user.name", "gamebuildr")
	if configNameErr != nil {
		logger.LogError(configNameErr, "Git Config Name")
	}
	configEmailErr := config.SetString("user.email", "contact@gamebuildr.io")
	if configEmailErr != nil {
		logger.LogError(configEmailErr, "Git Config Email")
	}
}

func GitPull(gitData GogetaRepo, repo *git.Repository) {
	FetchLatestsUpdates(gitData)
	remoteBranch := GetRemoteBranch(repo)
	remoteBranchID := remoteBranch.Target()

	analysis, err := AnalyzeLatestUpdates(repo, remoteBranch)
	if err != nil {
		logger.Error(err.Error())
	}
	mergeType := CheckMergeAnalysis(analysis)
	msg := "Git Merge " + mergeType

	switch mergeType {
	case UP_TO_DATE:
		break
	case NORMAL_MERGE:
		err := MergeNormal(repo, remoteBranch, remoteBranchID)
		gitData.BuildCount = gitData.BuildCount + 1
		//put raw message in new build
		SendRawMessage(
			gitData.BuildrId,
			gitData.BuildCount,
			"new code changes detected",
			"git merge origin master")
		BuildAfterMerge(err, msg, gitData)
		go UpdateRepo(gitData)
		break
	case FAST_FORWARD:
		err := MergeFastForward(repo, remoteBranchID)
		gitData.BuildCount = gitData.BuildCount + 1
		//put raw message in new build
		SendRawMessage(
			gitData.BuildrId,
			gitData.BuildCount,
			"new code changes detected",
			"git merge fast forward")
		BuildAfterMerge(err, msg, gitData)
		go UpdateRepo(gitData)
		break
	}
}

func SendRawMessage(dataId string, dataCount int, message string, devMessage string) {
	gitMessage := GamebuildrMessage{
		dataId,
		dataCount,
		message,
		devMessage,
		"BUILDR_MESSAGE",
	}
	SendGamebuildrMessage(gitMessage)
}

func GetRemoteBranch(repo *git.Repository) *git.Reference {
	remoteBranch, err := repo.References.Lookup("refs/remotes/origin/master")
	logger.LogError(err, "Git Remote Branch")
	return remoteBranch
}

func FetchLatestsUpdates(data GogetaRepo) {
	cmd := exec.Command("git", "fetch", "--depth", "1", "origin", "master")
	cmd.Dir = config.File.RepoPath + data.Folder

	commandErr := cmd.Start()
	logger.LogError(commandErr, "Git Fetch")

	fetchErr := cmd.Wait()
	logger.LogError(fetchErr, "Git Fetch")
}

func AnalyzeLatestUpdates(repo *git.Repository, remoteBranch *git.Reference) (git.MergeAnalysis, error) {
	annotatedCommit, err := repo.AnnotatedCommitFromRef(remoteBranch)
	if err != nil {
		var nullAnalysis git.MergeAnalysis
		return nullAnalysis, err
	}
	mergeHeads := make([]*git.AnnotatedCommit, 1)
	mergeHeads[0] = annotatedCommit
	analysis, _, err := repo.MergeAnalysis(mergeHeads)
	logger.LogError(err, "Git Merge Analysis")
	return analysis, nil
}

func CheckMergeAnalysis(analysis git.MergeAnalysis) string {
	if analysis&git.MergeAnalysisUpToDate != 0 {
		return UP_TO_DATE
	}
	if analysis&git.MergeAnalysisNormal != 0 {
		return NORMAL_MERGE
	}
	return FAST_FORWARD
}

func MergeNormal(repo *git.Repository, remoteBranch *git.Reference, remoteBranchID *git.Oid) error {
	head, err := repo.Head()
	if err != nil {
		return err
	}
	annotatedCommit, err := repo.AnnotatedCommitFromRef(remoteBranch)
	if err != nil {
		return err
	}
	if err := repo.Merge([]*git.AnnotatedCommit{annotatedCommit}, nil, nil); err != nil {
		return err
	}
	index, err := repo.Index()
	if err != nil {
		return err
	}
	if index.HasConflicts() {
		return errors.New("Conflicts encountered, Please Resolve Them")
	}
	sig, err := repo.DefaultSignature()
	if err != nil {
		return err
	}
	treeId, err := index.WriteTree()
	if err != nil {
		return err
	}
	tree, err := repo.LookupTree(treeId)
	if err != nil {
		return err
	}
	localCommit, err := repo.LookupCommit(head.Target())
	if err != nil {
		return err
	}
	remoteCommit, err := repo.LookupCommit(remoteBranchID)
	if err != nil {
		return err
	}
	repo.CreateCommit("HEAD", sig, sig, "", tree, localCommit, remoteCommit)
	repo.StateCleanup()
	return nil
}

func MergeFastForward(repo *git.Repository, remoteBranchID *git.Oid) error {
	head, err := repo.Head()
	if err != nil {
		return err
	}
	remoteTree, err := repo.LookupTree(remoteBranchID)
	if err != nil {
		return err
	}
	if err := repo.CheckoutTree(remoteTree, nil); err != nil {
		return err
	}
	branchRef, err := repo.References.Lookup("refs/heads/master")
	if err != nil {
		return err
	}
	branchRef.SetTarget(remoteBranchID, "")
	if _, err := head.SetTarget(remoteBranchID, ""); err != nil {
		return err
	}
	return nil
}
<commit_msg>Fix error with incrementing build count<commit_after>package main

import (
	"errors"
	"os/exec"

	"github.com/herman-rogers/gogeta/config"
	"github.com/herman-rogers/gogeta/logger"
	git "github.com/libgit2/git2go"
	uuid "github.com/satori/go.uuid"
)

const UP_TO_DATE = "UP_TO_DATE"
const NORMAL_MERGE = "NORMAL_MERGE"
const FAST_FORWARD = "FAST_FORWARD"

func GitProcessSQSMessages(data scmServiceRequest) error {
	if data.Usr == "" || data.Project == "" || data.Repo == "" {
		message := "Missing git data"
		SendRawMessage(
			data.Id,
			data.Buildcount,
			message,
			message)
		return errors.New("Missing Git Service Properties")
	}
	switch data.Type {
	case "GOGETA_CLONE":
		go GitShallowClone(data)
	case "GOGETA_NEW_BUILD":
		go RunNewGitBuild(data)
	}
	return nil
}

func GitShallowClone(data scmServiceRequest) {
	repoPath, relativePath := GetRepoPath(data.Project)
	cmd := exec.Command("git", "clone", "--depth", "2", data.Repo, repoPath)

	cloneMsgDev := "git clone --depth 2 " + data.Repo + " " + repoPath
	SendRawMessage(
		data.Id,
		data.Buildcount,
		"git clone started",
		cloneMsgDev)

	logfile := logger.GetLogFile()
	defer logfile.Close()

	cmd.Stdout = logfile
	cmd.Stderr = logfile

	commandErr := cmd.Start()
	logger.LogError(commandErr, "Git Clone")
	if commandErr != nil {
		SendRawMessage(
			data.Id,
			data.Buildcount,
			"git clone failed",
			commandErr.Error())
	}

	cloneErr := cmd.Wait()
	logger.LogError(cloneErr, "Git Clone")
	if cloneErr != nil {
		SendRawMessage(
			data.Id,
			data.Buildcount,
			"git clone failed",
			cloneErr.Error())
	}
	if cloneErr == nil {
		cloneSuccess := "git clone succeded"
		SendRawMessage(
			data.Id,
			data.Buildcount,
			cloneSuccess,
			cloneSuccess)
		gitData := &GogetaRepo{
			data.Id,
			data.Usr,
			data.Repo,
			relativePath,
			data.SCMType,
			data.Engine,
			data.Platform,
			data.Buildcount,
		}
		CreateGitCredentials(repoPath)
		go SaveRepo(*gitData)
		go TriggerMrRobotBuild(*gitData)
	}
}

func RunNewGitBuild(data scmServiceRequest) {
	gitData := FindRepo(data.Usr, data.Id)
	gitData.BuildCount = gitData.BuildCount + 1
	// put raw message in new build
	SendRawMessage(
		gitData.BuildrId,
		gitData.BuildCount,
		"new build started",
		"new build started")
	go TriggerMrRobotBuild(gitData)
	go UpdateRepo(gitData)
}

func UpdateGitRepositories() {
	repos := FindAllRepos()
	for i := 0; i < len(repos); i++ {
		folder := config.File.RepoPath + repos[i].Folder
		repo, err := git.OpenRepository(folder)
		if err != nil {
			logger.Error(err.Error())
			continue
		}
		if repo.IsBare() {
			return
		}
		go GitPull(repos[i], repo)
	}
}

func GetRepoPath(project string) (string, string) {
	var uuid uuid.UUID = uuid.NewV4()
	relativePath := project + "_" + uuid.String()
	repoPath := config.File.RepoPath + relativePath
	return repoPath, relativePath
}

func CreateGitCredentials(repo string) {
	openRepo, err := git.OpenRepository(repo)
	if err != nil {
		logger.LogError(err, "Git Create Credentials")
	}
	config, configErr := openRepo.Config()
	if configErr != nil {
		logger.LogError(configErr, "Git Config Set")
	}
	configNameErr := config.SetString("user.name", "gamebuildr")
	if configNameErr != nil {
		logger.LogError(configNameErr, "Git Config Name")
	}
	configEmailErr := config.SetString("user.email", "contact@gamebuildr.io")
	if configEmailErr != nil {
		logger.LogError(configEmailErr, "Git Config Email")
	}
}

func GitPull(gitData GogetaRepo, repo *git.Repository) {
	FetchLatestsUpdates(gitData)
	remoteBranch := GetRemoteBranch(repo)
	remoteBranchID := remoteBranch.Target()

	analysis, err := AnalyzeLatestUpdates(repo, remoteBranch)
	if err != nil {
		logger.Error(err.Error())
	}
	mergeType := CheckMergeAnalysis(analysis)
	msg := "Git Merge " + mergeType

	switch mergeType {
	case UP_TO_DATE:
		break
	case NORMAL_MERGE:
		err := MergeNormal(repo, remoteBranch, remoteBranchID)
		gitData.BuildCount = gitData.BuildCount + 1
		//put raw message in new build
		SendRawMessage(
			gitData.BuildrId,
			gitData.BuildCount,
			"new code changes detected",
			"git merge origin master")
		BuildAfterMerge(err, msg, gitData)
		go UpdateRepo(gitData)
		break
	case FAST_FORWARD:
		err := MergeFastForward(repo, remoteBranchID)
		gitData.BuildCount = gitData.BuildCount + 1
		//put raw message in new build
		SendRawMessage(
			gitData.BuildrId,
			gitData.BuildCount,
			"new code changes detected",
			"git merge fast forward")
		BuildAfterMerge(err, msg, gitData)
		go UpdateRepo(gitData)
		break
	}
}

func SendRawMessage(dataId string, dataCount int, message string, devMessage string) {
	gitMessage := GamebuildrMessage{
		dataId,
		dataCount,
		message,
		devMessage,
		"BUILDR_MESSAGE",
	}
	SendGamebuildrMessage(gitMessage)
}

func GetRemoteBranch(repo *git.Repository) *git.Reference {
	remoteBranch, err := repo.References.Lookup("refs/remotes/origin/master")
	logger.LogError(err, "Git Remote Branch")
	return remoteBranch
}

func FetchLatestsUpdates(data GogetaRepo) {
	cmd := exec.Command("git", "fetch", "--depth", "1", "origin", "master")
	cmd.Dir = config.File.RepoPath + data.Folder

	commandErr := cmd.Start()
	logger.LogError(commandErr, "Git Fetch")

	fetchErr := cmd.Wait()
	logger.LogError(fetchErr, "Git Fetch")
}

func AnalyzeLatestUpdates(repo *git.Repository, remoteBranch *git.Reference) (git.MergeAnalysis, error) {
	annotatedCommit, err := repo.AnnotatedCommitFromRef(remoteBranch)
	if err != nil {
		var nullAnalysis git.MergeAnalysis
		return nullAnalysis, err
	}
	mergeHeads := make([]*git.AnnotatedCommit, 1)
	mergeHeads[0] = annotatedCommit
	analysis, _, err := repo.MergeAnalysis(mergeHeads)
	logger.LogError(err, "Git Merge Analysis")
	return analysis, nil
}

func CheckMergeAnalysis(analysis git.MergeAnalysis) string {
	if analysis&git.MergeAnalysisUpToDate != 0 {
		return UP_TO_DATE
	}
	if analysis&git.MergeAnalysisNormal != 0 {
		return NORMAL_MERGE
	}
	return FAST_FORWARD
}

func MergeNormal(repo *git.Repository, remoteBranch *git.Reference, remoteBranchID *git.Oid) error {
	head, err := repo.Head()
	if err != nil {
		return err
	}
	annotatedCommit, err := repo.AnnotatedCommitFromRef(remoteBranch)
	if err != nil {
		return err
	}
	if err := repo.Merge([]*git.AnnotatedCommit{annotatedCommit}, nil, nil); err != nil {
		return err
	}
	index, err := repo.Index()
	if err != nil {
		return err
	}
	if index.HasConflicts() {
		return errors.New("Conflicts encountered, Please Resolve Them")
	}
	sig, err := repo.DefaultSignature()
	if err != nil {
		return err
	}
	treeId, err := index.WriteTree()
	if err != nil {
		return err
	}
	tree, err := repo.LookupTree(treeId)
	if err != nil {
		return err
	}
	localCommit, err := repo.LookupCommit(head.Target())
	if err != nil {
		return err
	}
	remoteCommit, err := repo.LookupCommit(remoteBranchID)
	if err != nil {
		return err
	}
	repo.CreateCommit("HEAD", sig, sig, "", tree, localCommit, remoteCommit)
	repo.StateCleanup()
	return nil
}

func MergeFastForward(repo *git.Repository, remoteBranchID *git.Oid) error {
	head, err := repo.Head()
	if err != nil {
		return err
	}
	remoteTree, err := repo.LookupTree(remoteBranchID)
	if err != nil {
		return err
	}
	if err := repo.CheckoutTree(remoteTree, nil); err != nil {
		return err
	}
	branchRef, err := repo.References.Lookup("refs/heads/master")
	if err != nil {
		return err
	}
	branchRef.SetTarget(remoteBranchID, "")
	if _, err := head.SetTarget(remoteBranchID, ""); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package gkquantile

import "fmt"
import "math"
import "sort"

/* This work is based on
"Space-Efficient Online Computation of Quantile Summaries"
by M. Greenwald and S. Khanna (known as GK-algorithm)
http://infolab.stanford.edu/~datar/courses/cs361a/papers/quantiles.pdf

Also ideas for the implementation are taken from
http://www.mathcs.emory.edu/~cheung/Courses/584-StreamDB/Syllabus/08-Quantile/Greenwald.html
*/

// === GK implementation ===========================

type GKItem struct {
    value float64
    g     int
    delta int
}

type GKSummary struct {
    Items   []*GKItem
    count   uint
    epsilon float64
}

func NewGKSummary(eps float64) *GKSummary {
    td := new(GKSummary)
    td.count = 0
    td.epsilon = eps
    td.Items = make([]*GKItem, 0)
    return td
}

func (t *GKSummary) Add(val float64) {

    // We maintain t.Items sorted, so can use a binary search
    idx := sort.Search(len(t.Items), func(i int) bool { return t.Items[i].value >= val })

    if idx > 0 {
        for idx < len(t.Items) && t.Items[idx].value == val {
            //  find first index where value > t.Items[i]
            idx++
        }
    }

    delta := 0
    // for first and last index - delta is always 0
    if idx != 0 && idx != len(t.Items) {
        delta = t.Items[idx].g + t.Items[idx].delta - 1
        // sanity check, if condition is true something went wrong. this should never happen
        if delta > int(math.Floor(t.epsilon*float64(t.count*2))) {
            panic("Delta is greater than allowable error. This never should happen. Really.")
        }
    }

    nItem := &GKItem{value: val, g: 1, delta: delta}
    t.Items = append(t.Items, nItem)
    // insert item into position idx, unless it was the last idx
    if idx != len(t.Items)-1 {
        copy(t.Items[idx+1:], t.Items[idx:])
        t.Items[idx] = nItem
    }

    t.count += 1

    // Periodically perform compress
    if int(math.Mod(float64(t.count), math.Floor(1/(2*t.epsilon)))) == 0 {
        t.Compress()
    }
}

func (t *GKSummary) Compress() {
    /* original logic from
        http://www.mathcs.emory.edu/~cheung/Courses/584-StreamDB/Syllabus/08-Quantile/Greenwald.html
            for ( i = s-1; i = 2; i = j - 1 )
           {
              j = i-1;

              while ( j = 1 && gj + ... + gi + ?i < 2eN )
          {
             j--;
          }

          j++;        // We went one index too far in the while...

              if ( j < i )
          {
             replace entries j, .., i with the entry (vi, gj+ ... + gi, ?i);
          }
           }
    */
    j := 0
    for i := len(t.Items) - 1; i >= 2; i = j - 1 {
        j = i - 1
        rollSum := t.Items[i].g
        for {
            if j < 1 {
                break
            }
            if rollSum+t.Items[j].g+t.Items[i].delta > int(math.Floor(t.epsilon*float64(t.count*2))) {
                // error too big, we stop now
                break
            }
            //fmt.Println("Merge items", i, j)
            rollSum = rollSum + t.Items[j].g
            t.Items[j] = nil
            j = j - 1
        }
        j = j + 1 // We went one index too far in the while...

        if j < i {
            // remove all items from j to i-1
            // and use i as a new item
            t.Items[i].g = rollSum
            copy(t.Items[j:], t.Items[i:])
            t.Items = t.Items[:len(t.Items)-(i-j)]
        }
    }
    return
}

func (t *GKSummary) Query(q float64) float64 {
    rankMin := 0
    desired := int(q * (float64(t.count)))
    for i := 1; i < len(t.Items); i++ {
        prev := t.Items[i-1]
        curr := t.Items[i]
        rankMin += prev.g
        if rankMin+curr.g+curr.delta > desired+int(t.epsilon*float64(t.count)) {
            return prev.value
        }
    }
    return t.Items[len(t.Items)-1].value
}

func (t *GKSummary) Print() {
    rank := 0
    for i, v := range t.Items { // loop through all values
        rank = rank + v.g
        fmt.Printf("el: %d v: %f, range (%d-%d), g,d: %d, %d, maxerr: %d\n", i, v.value, rank, rank+v.delta, v.g, v.delta, (v.g+v.delta)/2)
    }
    fmt.Printf("Error range: %d\n", int(math.Floor(t.epsilon*float64(t.count))))
    return
}
<commit_msg>added QueryRank<commit_after>package gkquantile

import "fmt"
import "math"
import "sort"

/* This work is based on
"Space-Efficient Online Computation of Quantile Summaries"
by M. Greenwald and S. Khanna (known as GK-algorithm)
http://infolab.stanford.edu/~datar/courses/cs361a/papers/quantiles.pdf

Also ideas for the implementation are taken from
http://www.mathcs.emory.edu/~cheung/Courses/584-StreamDB/Syllabus/08-Quantile/Greenwald.html
*/

// === GK implementation ===========================

type GKItem struct {
    value float64
    g     int
    delta int
}

type GKSummary struct {
    Items   []*GKItem
    count   uint
    epsilon float64
}

func NewGKSummary(eps float64) *GKSummary {
    td := new(GKSummary)
    td.count = 0
    td.epsilon = eps
    td.Items = make([]*GKItem, 0)
    return td
}

func (t *GKSummary) Add(val float64) {

    // We maintain t.Items sorted, so can use a binary search
    idx := sort.Search(len(t.Items), func(i int) bool { return t.Items[i].value >= val })

    if idx > 0 {
        for idx < len(t.Items) && t.Items[idx].value == val {
            //  find first index where value > t.Items[i]
            idx++
        }
    }

    delta := 0
    // for first and last index - delta is always 0
    if idx != 0 && idx != len(t.Items) {
        delta = t.Items[idx].g + t.Items[idx].delta - 1
        // sanity check, if condition is true something went wrong. this should never happen
        if delta > int(math.Floor(t.epsilon*float64(t.count*2))) {
            panic("Delta is greater than allowable error. This never should happen. Really.")
        }
    }

    nItem := &GKItem{value: val, g: 1, delta: delta}
    t.Items = append(t.Items, nItem)
    // insert item into position idx, unless it was the last idx
    if idx != len(t.Items)-1 {
        copy(t.Items[idx+1:], t.Items[idx:])
        t.Items[idx] = nItem
    }

    t.count += 1

    // Periodically perform compress
    if int(math.Mod(float64(t.count), math.Floor(1/(2*t.epsilon)))) == 0 {
        t.Compress()
    }
}

func (t *GKSummary) Compress() {
    /* original logic from
        http://www.mathcs.emory.edu/~cheung/Courses/584-StreamDB/Syllabus/08-Quantile/Greenwald.html
            for ( i = s-1; i = 2; i = j - 1 )
           {
              j = i-1;

              while ( j = 1 && gj + ... + gi + ?i < 2eN )
          {
             j--;
          }

          j++;        // We went one index too far in the while...

              if ( j < i )
          {
             replace entries j, .., i with the entry (vi, gj+ ... + gi, ?i);
          }
           }
    */
    j := 0
    for i := len(t.Items) - 1; i >= 2; i = j - 1 {
        j = i - 1
        rollSum := t.Items[i].g
        for {
            if j < 1 {
                break
            }
            if rollSum+t.Items[j].g+t.Items[i].delta > int(math.Floor(t.epsilon*float64(t.count*2))) {
                // error too big, we stop now
                break
            }
            //fmt.Println("Merge items", i, j)
            rollSum = rollSum + t.Items[j].g
            t.Items[j] = nil
            j = j - 1
        }
        j = j + 1 // We went one index too far in the while...

        if j < i {
            // remove all items from j to i-1
            // and use i as a new item
            t.Items[i].g = rollSum
            copy(t.Items[j:], t.Items[i:])
            t.Items = t.Items[:len(t.Items)-(i-j)]
        }
    }
    return
}

func (t *GKSummary) Query(q float64) float64 {
rankMin := 0
		 if q==0 {
			 return t.Items[0].value
		 }
	 if q==1 {
		 return t.Items[len(t.Items)-1].value
	 }
desired := int(q * (float64(t.count)))
		 for i := 1; i < len(t.Items); i++ {
prev := t.Items[i-1]
	      curr := t.Items[i]
	      rankMin += prev.g
	      if rankMin+curr.g+curr.delta > desired+int(t.epsilon*float64(t.count)) {
		      return prev.value
	      }
		 }
	 return t.Items[len(t.Items)-1].value
}

func (t *GKSummary) QueryRank(q float64) (float64, int, int) {
rankMin := 0
		 if q==0 {
			 return t.Items[0].value, t.Items[0].g, t.Items[0].g+t.Items[0].delta
		 }

desired := int(q * (float64(t.count)))

		 for i := 1; i < len(t.Items); i++ {
	prev := t.Items[i-1]
	      curr := t.Items[i]
	      rankMin += prev.g
	      if rankMin+curr.g+curr.delta > desired+int(t.epsilon*float64(t.count)) {
		      return prev.value, rankMin, rankMin + prev.delta
	      }
		 }
	 return t.Items[len(t.Items)-1].value, rankMin+t.Items[len(t.Items)-1].g, rankMin+t.Items[len(t.Items)-1].g+t.Items[len(t.Items)-1].delta
}

func (t *GKSummary) Print() {
    rank := 0
    for i, v := range t.Items { // loop through all values
        rank = rank + v.g
        fmt.Printf("el: %d v: %f, range (%d-%d), g,d: %d, %d, maxerr: %d\n", i, v.value, rank, rank+v.delta, v.g, v.delta, (v.g+v.delta)/2)
    }
    fmt.Printf("Error range: %d\n", int(math.Floor(t.epsilon*float64(t.count))))
    return
}
<|endoftext|>


<commit_before><commit_msg>test is not constant for east asian<commit_after><|endoftext|>


<commit_before>package apps

import (
	"io"
	"net/url"
	"regexp"
	"time"

	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/hooks"
	"github.com/cozy/cozy-stack/pkg/logger"
	"github.com/sirupsen/logrus"
)

var slugReg = regexp.MustCompile(`^[a-z0-9\-]+$`)

// Operation is the type of operation the installer is created for.
type Operation int

const (
	// Install operation for installing an application
	Install Operation = iota + 1
	// Update operation for updating an application
	Update
	// Delete operation for deleting an application
	Delete
)

// Installer is used to install or update applications.
type Installer struct {
	fetcher  Fetcher
	op       Operation
	fs       Copier
	db       couchdb.Database
	endState State

	man  Manifest
	src  *url.URL
	slug string

	errc chan error
	manc chan Manifest
	log  *logrus.Entry
}

// InstallerOptions provides the slug name of the application along with the
// source URL.
type InstallerOptions struct {
	Type        AppType
	Operation   Operation
	Slug        string
	SourceURL   string
	Deactivated bool
	Registries  []*url.URL
}

// Fetcher interface should be implemented by the underlying transport
// used to fetch the application data.
type Fetcher interface {
	// FetchManifest should returns an io.ReadCloser to read the
	// manifest data
	FetchManifest(src *url.URL) (io.ReadCloser, error)
	// Fetch should download the application and install it in the given
	// directory.
	Fetch(src *url.URL, fs Copier, man Manifest) error
}

// NewInstaller creates a new Installer
func NewInstaller(db couchdb.Database, fs Copier, opts *InstallerOptions) (*Installer, error) {
	if opts.Operation == 0 {
		panic("Missing installer operation")
	}
	if opts.Type != Webapp && opts.Type != Konnector {
		panic("Bad or missing installer type")
	}

	slug := opts.Slug
	if slug == "" || !slugReg.MatchString(slug) {
		return nil, ErrInvalidSlugName
	}

	// For konnectors applications, we actually create a tar archive in which the
	// sources are stored before copying the archive into the application
	// storage.
	if opts.Type == Konnector {
		fs = newTarCopier(fs, KonnectorArchiveName)
	}

	man, err := GetBySlug(db, slug, opts.Type)
	if opts.Operation == Install {
		if err == nil {
			return nil, ErrAlreadyExists
		}
		if err != ErrNotFound {
			return nil, err
		}
		err = nil
		switch opts.Type {
		case Webapp:
			man = &WebappManifest{}
		case Konnector:
			man = &KonnManifest{}
		}
	} else if err != nil {
		return nil, err
	}

	var src *url.URL
	switch opts.Operation {
	case Install:
		if opts.SourceURL == "" {
			return nil, ErrMissingSource
		}
		src, err = url.Parse(opts.SourceURL)
	case Update, Delete:
		var srcString string
		if opts.SourceURL == "" {
			srcString = man.Source()
		} else {
			srcString = opts.SourceURL
		}
		src, err = url.Parse(srcString)
	}
	if err != nil {
		return nil, err
	}

	var endState State
	if opts.Deactivated || man.State() == Installed {
		endState = Installed
	} else {
		endState = Ready
	}

	log := logger.WithDomain(db.Prefix())

	var manFilename string
	switch opts.Type {
	case Webapp:
		manFilename = WebappManifestName
	case Konnector:
		manFilename = KonnectorManifestName
	}

	var fetcher Fetcher
	switch src.Scheme {
	case "git", "git+ssh", "ssh+git":
		fetcher = newGitFetcher(manFilename, log)
	case "http", "https":
		fetcher = newHTTPFetcher(manFilename, log)
	case "registry":
		fetcher = newRegistryFetcher(opts.Registries, log)
	case "file":
		fetcher = newFileFetcher(manFilename, log)
	default:
		return nil, ErrNotSupportedSource
	}

	return &Installer{
		fetcher:  fetcher,
		op:       opts.Operation,
		db:       db,
		fs:       fs,
		endState: endState,

		man:  man,
		src:  src,
		slug: slug,

		errc: make(chan error, 1),
		manc: make(chan Manifest, 2),
		log:  log,
	}, nil
}

// Run will install, update or delete the application linked to the installer,
// depending on specified operation. It will report its progress or error (see
// Poll method) and should be run asynchronously.
func (i *Installer) Run() {
	var err error

	if i.man == nil {
		panic("Manifest is nil")
	}

	switch i.op {
	case Install:
		err = i.install()
	case Update:
		err = i.update()
	case Delete:
		err = i.delete()
	default:
		panic("Unknown operation")
	}

	if err != nil {
		i.errc <- err
	} else {
		i.manc <- i.man
	}
}

// RunSync does the same work as Run but can be used synchronously.
func (i *Installer) RunSync() (Manifest, error) {
	go i.Run()
	for {
		man, done, err := i.Poll()
		if err != nil {
			return nil, err
		}
		if done {
			return man, nil
		}
	}
}

// install will perform the installation of an application. It returns the
// freshly fetched manifest from the source along with a possible error in case
// the installation went wrong.
//
// Note that the fetched manifest is returned even if an error occurred while
// upgrading.
func (i *Installer) install() error {
	i.log.Infof("[apps] Start install: %s %s", i.slug, i.src.String())
	args := []string{i.db.Prefix(), i.slug}
	return hooks.Execute("install-app", args, func() error {
		if err := i.ReadManifest(Installing); err != nil {
			return err
		}
		i.manc <- i.man
		if err := i.fetcher.Fetch(i.src, i.fs, i.man); err != nil {
			return err
		}
		i.man.SetState(i.endState)
		return i.man.Create(i.db)
	})
}

// update will perform the update of an already installed application. It
// returns the freshly fetched manifest from the source along with a possible
// error in case the update went wrong.
//
// Note that the fetched manifest is returned even if an error occurred while
// upgrading.
func (i *Installer) update() error {
	i.log.Infof("[apps] Start update: %s %s", i.slug, i.src.String())
	if err := i.checkState(i.man); err != nil {
		return err
	}
	if err := i.ReadManifest(Upgrading); err != nil {
		return err
	}
	i.manc <- i.man
	if err := i.fetcher.Fetch(i.src, i.fs, i.man); err != nil {
		return err
	}
	i.man.SetState(i.endState)
	return i.man.Update(i.db)
}

func (i *Installer) delete() error {
	i.log.Infof("[apps] Start delete: %s %s", i.slug, i.src.String())
	if err := i.checkState(i.man); err != nil {
		return err
	}
	args := []string{i.db.Prefix(), i.slug}
	return hooks.Execute("uninstall-app", args, func() error {
		return i.man.Delete(i.db)
	})
}

// checkState returns whether or not the manifest is in the right state to
// perform an update or deletion.
func (i *Installer) checkState(man Manifest) error {
	state := man.State()
	if state == Ready || state == Installed {
		return nil
	}
	if time.Since(man.LastUpdate()) > 15*time.Minute {
		return nil
	}
	return ErrBadState
}

// ReadManifest will fetch the manifest and read its JSON content into the
// passed manifest pointer.
//
// The State field of the manifest will be set to the specified state.
func (i *Installer) ReadManifest(state State) error {
	r, err := i.fetcher.FetchManifest(i.src)
	if err != nil {
		return err
	}
	defer r.Close()
	i.man.SetState(state)
	return i.man.ReadManifest(io.LimitReader(r, ManifestMaxSize), i.slug, i.src.String())
}

// Poll should be used to monitor the progress of the Installer.
func (i *Installer) Poll() (Manifest, bool, error) {
	select {
	case man := <-i.manc:
		state := man.State()
		done := (state == Ready || state == Installed)
		return man, done, nil
	case err := <-i.errc:
		return nil, false, err
	}
}
<commit_msg>Fast path for file:// and registry:// schemes<commit_after>package apps

import (
	"io"
	"net/url"
	"regexp"
	"time"

	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/hooks"
	"github.com/cozy/cozy-stack/pkg/logger"
	"github.com/sirupsen/logrus"
)

var slugReg = regexp.MustCompile(`^[a-z0-9\-]+$`)

// Operation is the type of operation the installer is created for.
type Operation int

const (
	// Install operation for installing an application
	Install Operation = iota + 1
	// Update operation for updating an application
	Update
	// Delete operation for deleting an application
	Delete
)

// Installer is used to install or update applications.
type Installer struct {
	fetcher  Fetcher
	op       Operation
	fs       Copier
	db       couchdb.Database
	endState State

	man  Manifest
	src  *url.URL
	slug string

	errc chan error
	manc chan Manifest
	log  *logrus.Entry
}

// InstallerOptions provides the slug name of the application along with the
// source URL.
type InstallerOptions struct {
	Type        AppType
	Operation   Operation
	Slug        string
	SourceURL   string
	Deactivated bool
	Registries  []*url.URL
}

// Fetcher interface should be implemented by the underlying transport
// used to fetch the application data.
type Fetcher interface {
	// FetchManifest should returns an io.ReadCloser to read the
	// manifest data
	FetchManifest(src *url.URL) (io.ReadCloser, error)
	// Fetch should download the application and install it in the given
	// directory.
	Fetch(src *url.URL, fs Copier, man Manifest) error
}

// NewInstaller creates a new Installer
func NewInstaller(db couchdb.Database, fs Copier, opts *InstallerOptions) (*Installer, error) {
	if opts.Operation == 0 {
		panic("Missing installer operation")
	}
	if opts.Type != Webapp && opts.Type != Konnector {
		panic("Bad or missing installer type")
	}

	slug := opts.Slug
	if slug == "" || !slugReg.MatchString(slug) {
		return nil, ErrInvalidSlugName
	}

	// For konnectors applications, we actually create a tar archive in which the
	// sources are stored before copying the archive into the application
	// storage.
	if opts.Type == Konnector {
		fs = newTarCopier(fs, KonnectorArchiveName)
	}

	man, err := GetBySlug(db, slug, opts.Type)
	if opts.Operation == Install {
		if err == nil {
			return nil, ErrAlreadyExists
		}
		if err != ErrNotFound {
			return nil, err
		}
		err = nil
		switch opts.Type {
		case Webapp:
			man = &WebappManifest{}
		case Konnector:
			man = &KonnManifest{}
		}
	} else if err != nil {
		return nil, err
	}

	var src *url.URL
	switch opts.Operation {
	case Install:
		if opts.SourceURL == "" {
			return nil, ErrMissingSource
		}
		src, err = url.Parse(opts.SourceURL)
	case Update, Delete:
		var srcString string
		if opts.SourceURL == "" {
			srcString = man.Source()
		} else {
			srcString = opts.SourceURL
		}
		src, err = url.Parse(srcString)
	}
	if err != nil {
		return nil, err
	}

	var endState State
	if opts.Deactivated || man.State() == Installed {
		endState = Installed
	} else {
		endState = Ready
	}

	log := logger.WithDomain(db.Prefix())

	var manFilename string
	switch opts.Type {
	case Webapp:
		manFilename = WebappManifestName
	case Konnector:
		manFilename = KonnectorManifestName
	}

	var fetcher Fetcher
	switch src.Scheme {
	case "git", "git+ssh", "ssh+git":
		fetcher = newGitFetcher(manFilename, log)
	case "http", "https":
		fetcher = newHTTPFetcher(manFilename, log)
	case "registry":
		fetcher = newRegistryFetcher(opts.Registries, log)
	case "file":
		fetcher = newFileFetcher(manFilename, log)
	default:
		return nil, ErrNotSupportedSource
	}

	return &Installer{
		fetcher:  fetcher,
		op:       opts.Operation,
		db:       db,
		fs:       fs,
		endState: endState,

		man:  man,
		src:  src,
		slug: slug,

		errc: make(chan error, 1),
		manc: make(chan Manifest, 2),
		log:  log,
	}, nil
}

// Run will install, update or delete the application linked to the installer,
// depending on specified operation. It will report its progress or error (see
// Poll method) and should be run asynchronously.
func (i *Installer) Run() {
	var err error

	if i.man == nil {
		panic("Manifest is nil")
	}

	switch i.op {
	case Install:
		err = i.install()
	case Update:
		err = i.update()
	case Delete:
		err = i.delete()
	default:
		panic("Unknown operation")
	}

	if err != nil {
		i.errc <- err
	} else {
		i.manc <- i.man
	}
}

// RunSync does the same work as Run but can be used synchronously.
func (i *Installer) RunSync() (Manifest, error) {
	go i.Run()
	for {
		man, done, err := i.Poll()
		if err != nil {
			return nil, err
		}
		if done {
			return man, nil
		}
	}
}

// install will perform the installation of an application. It returns the
// freshly fetched manifest from the source along with a possible error in case
// the installation went wrong.
//
// Note that the fetched manifest is returned even if an error occurred while
// upgrading.
func (i *Installer) install() error {
	i.log.Infof("[apps] Start install: %s %s", i.slug, i.src.String())
	args := []string{i.db.Prefix(), i.slug}
	return hooks.Execute("install-app", args, func() error {
		if err := i.ReadManifest(Installing); err != nil {
			return err
		}
		i.manc <- i.man
		if err := i.fetcher.Fetch(i.src, i.fs, i.man); err != nil {
			return err
		}
		i.man.SetState(i.endState)
		return i.man.Create(i.db)
	})
}

// update will perform the update of an already installed application. It
// returns the freshly fetched manifest from the source along with a possible
// error in case the update went wrong.
//
// Note that the fetched manifest is returned even if an error occurred while
// upgrading.
func (i *Installer) update() error {
	i.log.Infof("[apps] Start update: %s %s", i.slug, i.src.String())
	if err := i.checkState(i.man); err != nil {
		return err
	}

	version := i.man.Version()
	if err := i.ReadManifest(Upgrading); err != nil {
		return err
	}

	i.manc <- i.man

	// Fast path for registry:// and http:// sources: we do not need to go
	// further in the case where the fetched manifest has the same version has
	// the one in database.
	//
	// For git:// and file:// sources, it may be more complicated since we need
	// to actually fetch the data to extract the exact version of the manifest.
	var sameVersion bool
	switch i.src.Scheme {
	case "registry", "http", "https":
		sameVersion = (version == i.man.Version())
	}

	if !sameVersion {
		if err := i.fetcher.Fetch(i.src, i.fs, i.man); err != nil {
			return err
		}
	}

	i.man.SetState(i.endState)
	return i.man.Update(i.db)
}

func (i *Installer) delete() error {
	i.log.Infof("[apps] Start delete: %s %s", i.slug, i.src.String())
	if err := i.checkState(i.man); err != nil {
		return err
	}
	args := []string{i.db.Prefix(), i.slug}
	return hooks.Execute("uninstall-app", args, func() error {
		return i.man.Delete(i.db)
	})
}

// checkState returns whether or not the manifest is in the right state to
// perform an update or deletion.
func (i *Installer) checkState(man Manifest) error {
	state := man.State()
	if state == Ready || state == Installed {
		return nil
	}
	if time.Since(man.LastUpdate()) > 15*time.Minute {
		return nil
	}
	return ErrBadState
}

// ReadManifest will fetch the manifest and read its JSON content into the
// passed manifest pointer.
//
// The State field of the manifest will be set to the specified state.
func (i *Installer) ReadManifest(state State) error {
	r, err := i.fetcher.FetchManifest(i.src)
	if err != nil {
		return err
	}
	defer r.Close()
	i.man.SetState(state)
	return i.man.ReadManifest(io.LimitReader(r, ManifestMaxSize), i.slug, i.src.String())
}

// Poll should be used to monitor the progress of the Installer.
func (i *Installer) Poll() (Manifest, bool, error) {
	select {
	case man := <-i.manc:
		state := man.State()
		done := (state == Ready || state == Installed)
		return man, done, nil
	case err := <-i.errc:
		return nil, false, err
	}
}
<|endoftext|>


<commit_before>package apps

import (
	"encoding/json"
	"io"
	"net/url"
	"regexp"
	"time"

	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/hooks"
	"github.com/cozy/cozy-stack/pkg/logger"
	"github.com/cozy/cozy-stack/pkg/realtime"
	"github.com/cozy/cozy-stack/pkg/utils"
	"github.com/sirupsen/logrus"
)

var slugReg = regexp.MustCompile(`^[a-z0-9\-]+$`)

// Operation is the type of operation the installer is created for.
type Operation int

const (
	// Install operation for installing an application
	Install Operation = iota + 1
	// Update operation for updating an application
	Update
	// Delete operation for deleting an application
	Delete
)

// Installer is used to install or update applications.
type Installer struct {
	fetcher  Fetcher
	op       Operation
	fs       Copier
	db       couchdb.Database
	endState State

	overridenParameters *json.RawMessage

	man  Manifest
	src  *url.URL
	slug string

	errc chan error
	manc chan Manifest
	log  *logrus.Entry
}

// InstallerOptions provides the slug name of the application along with the
// source URL.
type InstallerOptions struct {
	Type        AppType
	Operation   Operation
	Manifest    Manifest
	Slug        string
	SourceURL   string
	Deactivated bool
	Registries  []*url.URL

	// Used to override the "Parameters" field of konnectors during installation.
	// This modification is usefull to allow the parameterization of a konnector
	// at its installation as we do not have yet a registry up and running.
	OverridenParameters *json.RawMessage
}

// Fetcher interface should be implemented by the underlying transport
// used to fetch the application data.
type Fetcher interface {
	// FetchManifest should returns an io.ReadCloser to read the
	// manifest data
	FetchManifest(src *url.URL) (io.ReadCloser, error)
	// Fetch should download the application and install it in the given
	// directory.
	Fetch(src *url.URL, fs Copier, man Manifest) error
}

// NewInstaller creates a new Installer
func NewInstaller(db couchdb.Database, fs Copier, opts *InstallerOptions) (*Installer, error) {
	man, err := initManifest(db, opts)
	if err != nil {
		return nil, err
	}

	// For konnectors applications, we actually create a tar archive in which the
	// sources are stored before copying the archive into the application
	// storage.
	if man.AppType() == Konnector {
		fs = newTarCopier(fs, KonnectorArchiveName)
	}

	var src *url.URL
	switch opts.Operation {
	case Install:
		if opts.SourceURL == "" {
			return nil, ErrMissingSource
		}
		src, err = url.Parse(opts.SourceURL)
	case Update, Delete:
		var srcString string
		if opts.SourceURL == "" {
			srcString = man.Source()
		} else {
			srcString = opts.SourceURL
		}
		src, err = url.Parse(srcString)
	default:
		panic("Unknwon installer operation")
	}
	if err != nil {
		return nil, err
	}

	var endState State
	if opts.Deactivated || man.State() == Installed {
		endState = Installed
	} else {
		endState = Ready
	}

	log := logger.WithDomain(db.Prefix())

	var manFilename string
	switch man.AppType() {
	case Webapp:
		manFilename = WebappManifestName
	case Konnector:
		manFilename = KonnectorManifestName
	}

	var fetcher Fetcher
	switch src.Scheme {
	case "git", "git+ssh", "ssh+git":
		fetcher = newGitFetcher(manFilename, log)
	case "http", "https":
		fetcher = newHTTPFetcher(manFilename, log)
	case "registry":
		fetcher = newRegistryFetcher(opts.Registries, log)
	case "file":
		fetcher = newFileFetcher(manFilename, log)
	default:
		return nil, ErrNotSupportedSource
	}

	return &Installer{
		fetcher:  fetcher,
		op:       opts.Operation,
		db:       db,
		fs:       fs,
		endState: endState,

		overridenParameters: opts.OverridenParameters,

		man:  man,
		src:  src,
		slug: man.Slug(),

		errc: make(chan error, 1),
		manc: make(chan Manifest, 2),
		log:  log,
	}, nil
}

func initManifest(db couchdb.Database, opts *InstallerOptions) (man Manifest, err error) {
	if man = opts.Manifest; man != nil {
		return man, nil
	}

	slug := opts.Slug
	if slug == "" || !slugReg.MatchString(slug) {
		return nil, ErrInvalidSlugName
	}

	if opts.Operation == Install {
		_, err = GetBySlug(db, slug, opts.Type)
		if err == nil {
			return nil, ErrAlreadyExists
		}
		if err != ErrNotFound {
			return nil, err
		}
		switch opts.Type {
		case Webapp:
			man = &WebappManifest{DocSlug: slug}
		case Konnector:
			man = &KonnManifest{DocSlug: slug}
		}
	} else {
		man, err = GetBySlug(db, slug, opts.Type)
		if err != nil {
			return nil, err
		}
	}

	if man == nil {
		panic("Bad or missing installer type")
	}

	return man, nil
}

// Run will install, update or delete the application linked to the installer,
// depending on specified operation. It will report its progress or error (see
// Poll method) and should be run asynchronously.
func (i *Installer) Run() {
	var err error

	if i.man == nil {
		panic("Manifest is nil")
	}

	switch i.op {
	case Install:
		err = i.install()
	case Update:
		err = i.update()
	case Delete:
		err = i.delete()
	default:
		panic("Unknown operation")
	}

	man := i.man.Clone().(Manifest)
	if err != nil {
		man.SetError(err)
		realtime.GetHub().Publish(&realtime.Event{
			Verb:   realtime.EventUpdate,
			Doc:    man.Clone(),
			Domain: i.db.Prefix(),
		})
	}
	i.manc <- man
}

// RunSync does the same work as Run but can be used synchronously.
func (i *Installer) RunSync() (Manifest, error) {
	go i.Run()
	for {
		man, done, err := i.Poll()
		if err != nil {
			return nil, err
		}
		if done {
			return man, nil
		}
	}
}

// install will perform the installation of an application. It returns the
// freshly fetched manifest from the source along with a possible error in case
// the installation went wrong.
//
// Note that the fetched manifest is returned even if an error occurred while
// upgrading.
func (i *Installer) install() error {
	i.log.Infof("[apps] Start install: %s %s", i.slug, i.src.String())
	args := []string{i.db.Prefix(), i.slug}
	return hooks.Execute("install-app", args, func() error {
		if err := i.ReadManifest(Installing); err != nil {
			return err
		}
		i.manc <- i.man.Clone().(Manifest)
		if err := i.fetcher.Fetch(i.src, i.fs, i.man); err != nil {
			return err
		}
		i.man.SetState(i.endState)
		return i.man.Create(i.db)
	})
}

// update will perform the update of an already installed application. It
// returns the freshly fetched manifest from the source along with a possible
// error in case the update went wrong.
//
// Note that the fetched manifest is returned even if an error occurred while
// upgrading.
func (i *Installer) update() error {
	i.log.Infof("[apps] Start update: %s %s", i.slug, i.src.String())
	if err := i.checkState(i.man); err != nil {
		return err
	}

	version := i.man.Version()
	oldPermissions := i.man.Permissions()
	if err := i.ReadManifest(Upgrading); err != nil {
		return err
	}

	i.manc <- i.man.Clone().(Manifest)

	// Fast path for registry:// and http:// sources: we do not need to go
	// further in the case where the fetched manifest has the same version has
	// the one in database.
	//
	// For git:// and file:// sources, it may be more complicated since we need
	// to actually fetch the data to extract the exact version of the manifest.
	var sameVersion bool
	switch i.src.Scheme {
	case "registry", "http", "https":
		sameVersion = (version == i.man.Version())
	}

	// TODO: decide what behavior to chose for auto-updating konnectors that
	// would change their permissions...
	if i.man.AppType() == Webapp && !isPlatformApp(i.man) {
		// If the permissions have changed, we set the end state of the applications
		// as "installed", meaning the user will have to accept the new set of
		// permissions before accessing to set the application state as "ready".
		newPermissions := i.man.Permissions()
		samePermissions := newPermissions != nil && oldPermissions != nil &&
			newPermissions.HasSameRules(oldPermissions)
		if !samePermissions {
			i.endState = Installed
		}
	}

	if !sameVersion {
		if err := i.fetcher.Fetch(i.src, i.fs, i.man); err != nil {
			return err
		}
	}

	i.man.SetState(i.endState)
	return i.man.Update(i.db)
}

func (i *Installer) delete() error {
	i.log.Infof("[apps] Start delete: %s %s", i.slug, i.src.String())
	if err := i.checkState(i.man); err != nil {
		return err
	}
	args := []string{i.db.Prefix(), i.slug}
	return hooks.Execute("uninstall-app", args, func() error {
		return i.man.Delete(i.db)
	})
}

// checkState returns whether or not the manifest is in the right state to
// perform an update or deletion.
func (i *Installer) checkState(man Manifest) error {
	state := man.State()
	if state == Ready || state == Installed {
		return nil
	}
	if time.Since(man.LastUpdate()) > 15*time.Minute {
		return nil
	}
	return ErrBadState
}

// ReadManifest will fetch the manifest and read its JSON content into the
// passed manifest pointer.
//
// The State field of the manifest will be set to the specified state.
func (i *Installer) ReadManifest(state State) error {
	r, err := i.fetcher.FetchManifest(i.src)
	if err != nil {
		return err
	}
	defer r.Close()
	i.man.SetState(state)

	err = i.man.ReadManifest(io.LimitReader(r, ManifestMaxSize), i.slug, i.src.String())
	if err != nil {
		return err
	}

	shouldOverrideParameters := (i.overridenParameters != nil &&
		i.man.AppType() == Konnector &&
		i.src.Scheme != "registry")
	if shouldOverrideParameters {
		if m, ok := i.man.(*KonnManifest); ok {
			m.Parameters = i.overridenParameters
		}
	}

	realtime.GetHub().Publish(&realtime.Event{
		Verb:   realtime.EventUpdate,
		Doc:    i.man.Clone(),
		Domain: i.db.Prefix(),
	})

	return nil
}

// Poll should be used to monitor the progress of the Installer.
func (i *Installer) Poll() (Manifest, bool, error) {
	man := <-i.manc
	done := false
	if s := man.State(); s == Ready || s == Installed || s == Errored {
		done = true
	}
	return man, done, man.Error()
}

func isPlatformApp(man Manifest) bool {
	if man.AppType() != Webapp {
		return false
	}
	return utils.IsInArray(man.Slug(), []string{
		"onboarding",
		"settings",
		"collect",
		"photos",
		"drive",
		"store",
	})
}
<commit_msg>Add "bank" slug to the application platform list<commit_after>package apps

import (
	"encoding/json"
	"io"
	"net/url"
	"regexp"
	"time"

	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/hooks"
	"github.com/cozy/cozy-stack/pkg/logger"
	"github.com/cozy/cozy-stack/pkg/realtime"
	"github.com/cozy/cozy-stack/pkg/utils"
	"github.com/sirupsen/logrus"
)

var slugReg = regexp.MustCompile(`^[a-z0-9\-]+$`)

// Operation is the type of operation the installer is created for.
type Operation int

const (
	// Install operation for installing an application
	Install Operation = iota + 1
	// Update operation for updating an application
	Update
	// Delete operation for deleting an application
	Delete
)

// Installer is used to install or update applications.
type Installer struct {
	fetcher  Fetcher
	op       Operation
	fs       Copier
	db       couchdb.Database
	endState State

	overridenParameters *json.RawMessage

	man  Manifest
	src  *url.URL
	slug string

	errc chan error
	manc chan Manifest
	log  *logrus.Entry
}

// InstallerOptions provides the slug name of the application along with the
// source URL.
type InstallerOptions struct {
	Type        AppType
	Operation   Operation
	Manifest    Manifest
	Slug        string
	SourceURL   string
	Deactivated bool
	Registries  []*url.URL

	// Used to override the "Parameters" field of konnectors during installation.
	// This modification is usefull to allow the parameterization of a konnector
	// at its installation as we do not have yet a registry up and running.
	OverridenParameters *json.RawMessage
}

// Fetcher interface should be implemented by the underlying transport
// used to fetch the application data.
type Fetcher interface {
	// FetchManifest should returns an io.ReadCloser to read the
	// manifest data
	FetchManifest(src *url.URL) (io.ReadCloser, error)
	// Fetch should download the application and install it in the given
	// directory.
	Fetch(src *url.URL, fs Copier, man Manifest) error
}

// NewInstaller creates a new Installer
func NewInstaller(db couchdb.Database, fs Copier, opts *InstallerOptions) (*Installer, error) {
	man, err := initManifest(db, opts)
	if err != nil {
		return nil, err
	}

	// For konnectors applications, we actually create a tar archive in which the
	// sources are stored before copying the archive into the application
	// storage.
	if man.AppType() == Konnector {
		fs = newTarCopier(fs, KonnectorArchiveName)
	}

	var src *url.URL
	switch opts.Operation {
	case Install:
		if opts.SourceURL == "" {
			return nil, ErrMissingSource
		}
		src, err = url.Parse(opts.SourceURL)
	case Update, Delete:
		var srcString string
		if opts.SourceURL == "" {
			srcString = man.Source()
		} else {
			srcString = opts.SourceURL
		}
		src, err = url.Parse(srcString)
	default:
		panic("Unknwon installer operation")
	}
	if err != nil {
		return nil, err
	}

	var endState State
	if opts.Deactivated || man.State() == Installed {
		endState = Installed
	} else {
		endState = Ready
	}

	log := logger.WithDomain(db.Prefix())

	var manFilename string
	switch man.AppType() {
	case Webapp:
		manFilename = WebappManifestName
	case Konnector:
		manFilename = KonnectorManifestName
	}

	var fetcher Fetcher
	switch src.Scheme {
	case "git", "git+ssh", "ssh+git":
		fetcher = newGitFetcher(manFilename, log)
	case "http", "https":
		fetcher = newHTTPFetcher(manFilename, log)
	case "registry":
		fetcher = newRegistryFetcher(opts.Registries, log)
	case "file":
		fetcher = newFileFetcher(manFilename, log)
	default:
		return nil, ErrNotSupportedSource
	}

	return &Installer{
		fetcher:  fetcher,
		op:       opts.Operation,
		db:       db,
		fs:       fs,
		endState: endState,

		overridenParameters: opts.OverridenParameters,

		man:  man,
		src:  src,
		slug: man.Slug(),

		errc: make(chan error, 1),
		manc: make(chan Manifest, 2),
		log:  log,
	}, nil
}

func initManifest(db couchdb.Database, opts *InstallerOptions) (man Manifest, err error) {
	if man = opts.Manifest; man != nil {
		return man, nil
	}

	slug := opts.Slug
	if slug == "" || !slugReg.MatchString(slug) {
		return nil, ErrInvalidSlugName
	}

	if opts.Operation == Install {
		_, err = GetBySlug(db, slug, opts.Type)
		if err == nil {
			return nil, ErrAlreadyExists
		}
		if err != ErrNotFound {
			return nil, err
		}
		switch opts.Type {
		case Webapp:
			man = &WebappManifest{DocSlug: slug}
		case Konnector:
			man = &KonnManifest{DocSlug: slug}
		}
	} else {
		man, err = GetBySlug(db, slug, opts.Type)
		if err != nil {
			return nil, err
		}
	}

	if man == nil {
		panic("Bad or missing installer type")
	}

	return man, nil
}

// Run will install, update or delete the application linked to the installer,
// depending on specified operation. It will report its progress or error (see
// Poll method) and should be run asynchronously.
func (i *Installer) Run() {
	var err error

	if i.man == nil {
		panic("Manifest is nil")
	}

	switch i.op {
	case Install:
		err = i.install()
	case Update:
		err = i.update()
	case Delete:
		err = i.delete()
	default:
		panic("Unknown operation")
	}

	man := i.man.Clone().(Manifest)
	if err != nil {
		man.SetError(err)
		realtime.GetHub().Publish(&realtime.Event{
			Verb:   realtime.EventUpdate,
			Doc:    man.Clone(),
			Domain: i.db.Prefix(),
		})
	}
	i.manc <- man
}

// RunSync does the same work as Run but can be used synchronously.
func (i *Installer) RunSync() (Manifest, error) {
	go i.Run()
	for {
		man, done, err := i.Poll()
		if err != nil {
			return nil, err
		}
		if done {
			return man, nil
		}
	}
}

// install will perform the installation of an application. It returns the
// freshly fetched manifest from the source along with a possible error in case
// the installation went wrong.
//
// Note that the fetched manifest is returned even if an error occurred while
// upgrading.
func (i *Installer) install() error {
	i.log.Infof("[apps] Start install: %s %s", i.slug, i.src.String())
	args := []string{i.db.Prefix(), i.slug}
	return hooks.Execute("install-app", args, func() error {
		if err := i.ReadManifest(Installing); err != nil {
			return err
		}
		i.manc <- i.man.Clone().(Manifest)
		if err := i.fetcher.Fetch(i.src, i.fs, i.man); err != nil {
			return err
		}
		i.man.SetState(i.endState)
		return i.man.Create(i.db)
	})
}

// update will perform the update of an already installed application. It
// returns the freshly fetched manifest from the source along with a possible
// error in case the update went wrong.
//
// Note that the fetched manifest is returned even if an error occurred while
// upgrading.
func (i *Installer) update() error {
	i.log.Infof("[apps] Start update: %s %s", i.slug, i.src.String())
	if err := i.checkState(i.man); err != nil {
		return err
	}

	version := i.man.Version()
	oldPermissions := i.man.Permissions()
	if err := i.ReadManifest(Upgrading); err != nil {
		return err
	}

	i.manc <- i.man.Clone().(Manifest)

	// Fast path for registry:// and http:// sources: we do not need to go
	// further in the case where the fetched manifest has the same version has
	// the one in database.
	//
	// For git:// and file:// sources, it may be more complicated since we need
	// to actually fetch the data to extract the exact version of the manifest.
	var sameVersion bool
	switch i.src.Scheme {
	case "registry", "http", "https":
		sameVersion = (version == i.man.Version())
	}

	// TODO: decide what behavior to chose for auto-updating konnectors that
	// would change their permissions...
	if i.man.AppType() == Webapp && !isPlatformApp(i.man) {
		// If the permissions have changed, we set the end state of the applications
		// as "installed", meaning the user will have to accept the new set of
		// permissions before accessing to set the application state as "ready".
		newPermissions := i.man.Permissions()
		samePermissions := newPermissions != nil && oldPermissions != nil &&
			newPermissions.HasSameRules(oldPermissions)
		if !samePermissions {
			i.endState = Installed
		}
	}

	if !sameVersion {
		if err := i.fetcher.Fetch(i.src, i.fs, i.man); err != nil {
			return err
		}
	}

	i.man.SetState(i.endState)
	return i.man.Update(i.db)
}

func (i *Installer) delete() error {
	i.log.Infof("[apps] Start delete: %s %s", i.slug, i.src.String())
	if err := i.checkState(i.man); err != nil {
		return err
	}
	args := []string{i.db.Prefix(), i.slug}
	return hooks.Execute("uninstall-app", args, func() error {
		return i.man.Delete(i.db)
	})
}

// checkState returns whether or not the manifest is in the right state to
// perform an update or deletion.
func (i *Installer) checkState(man Manifest) error {
	state := man.State()
	if state == Ready || state == Installed {
		return nil
	}
	if time.Since(man.LastUpdate()) > 15*time.Minute {
		return nil
	}
	return ErrBadState
}

// ReadManifest will fetch the manifest and read its JSON content into the
// passed manifest pointer.
//
// The State field of the manifest will be set to the specified state.
func (i *Installer) ReadManifest(state State) error {
	r, err := i.fetcher.FetchManifest(i.src)
	if err != nil {
		return err
	}
	defer r.Close()
	i.man.SetState(state)

	err = i.man.ReadManifest(io.LimitReader(r, ManifestMaxSize), i.slug, i.src.String())
	if err != nil {
		return err
	}

	shouldOverrideParameters := (i.overridenParameters != nil &&
		i.man.AppType() == Konnector &&
		i.src.Scheme != "registry")
	if shouldOverrideParameters {
		if m, ok := i.man.(*KonnManifest); ok {
			m.Parameters = i.overridenParameters
		}
	}

	realtime.GetHub().Publish(&realtime.Event{
		Verb:   realtime.EventUpdate,
		Doc:    i.man.Clone(),
		Domain: i.db.Prefix(),
	})

	return nil
}

// Poll should be used to monitor the progress of the Installer.
func (i *Installer) Poll() (Manifest, bool, error) {
	man := <-i.manc
	done := false
	if s := man.State(); s == Ready || s == Installed || s == Errored {
		done = true
	}
	return man, done, man.Error()
}

func isPlatformApp(man Manifest) bool {
	if man.AppType() != Webapp {
		return false
	}
	return utils.IsInArray(man.Slug(), []string{
		"onboarding",
		"settings",
		"collect",
		"photos",
		"drive",
		"store",
		"bank",
	})
}
<|endoftext|>


<commit_before>package mpb

import (
	"io"

	"github.com/acarl005/stripansi"
	"github.com/mattn/go-runewidth"
	"github.com/vbauerster/mpb/v8/decor"
	"github.com/vbauerster/mpb/v8/internal"
)

const (
	iLbound = iota
	iRbound
	iFiller
	iRefiller
	iPadding
	components
)

// BarStyleComposer interface.
type BarStyleComposer interface {
	BarFillerBuilder
	Lbound(string) BarStyleComposer
	Rbound(string) BarStyleComposer
	Filler(string) BarStyleComposer
	Refiller(string) BarStyleComposer
	Padding(string) BarStyleComposer
	TipOnComplete(string) BarStyleComposer
	Tip(frames ...string) BarStyleComposer
	Reverse() BarStyleComposer
}

type bFiller struct {
	rev        bool
	components [components]*component
	tip        struct {
		count      uint
		onComplete *component
		frames     []*component
	}
}

type component struct {
	width int
	bytes []byte
}

type barStyle struct {
	lbound        string
	rbound        string
	filler        string
	refiller      string
	padding       string
	tipOnComplete string
	tipFrames     []string
	rev           bool
}

// BarStyle constructs default bar style which can be altered via
// BarStyleComposer interface.
func BarStyle() BarStyleComposer {
	return &barStyle{
		lbound:    "[",
		rbound:    "]",
		filler:    "=",
		refiller:  "+",
		padding:   "-",
		tipFrames: []string{">"},
	}
}

func (s *barStyle) Lbound(bound string) BarStyleComposer {
	s.lbound = bound
	return s
}

func (s *barStyle) Rbound(bound string) BarStyleComposer {
	s.rbound = bound
	return s
}

func (s *barStyle) Filler(filler string) BarStyleComposer {
	s.filler = filler
	return s
}

func (s *barStyle) Refiller(refiller string) BarStyleComposer {
	s.refiller = refiller
	return s
}

func (s *barStyle) Padding(padding string) BarStyleComposer {
	s.padding = padding
	return s
}

func (s *barStyle) TipOnComplete(tip string) BarStyleComposer {
	s.tipOnComplete = tip
	return s
}

func (s *barStyle) Tip(frames ...string) BarStyleComposer {
	if len(frames) != 0 {
		s.tipFrames = append(s.tipFrames[:0], frames...)
	}
	return s
}

func (s *barStyle) Reverse() BarStyleComposer {
	s.rev = true
	return s
}

func (s *barStyle) Build() BarFiller {
	bf := &bFiller{rev: s.rev}
	bf.components[iLbound] = &component{
		width: runewidth.StringWidth(stripansi.Strip(s.lbound)),
		bytes: []byte(s.lbound),
	}
	bf.components[iRbound] = &component{
		width: runewidth.StringWidth(stripansi.Strip(s.rbound)),
		bytes: []byte(s.rbound),
	}
	bf.components[iFiller] = &component{
		width: runewidth.StringWidth(stripansi.Strip(s.filler)),
		bytes: []byte(s.filler),
	}
	bf.components[iRefiller] = &component{
		width: runewidth.StringWidth(stripansi.Strip(s.refiller)),
		bytes: []byte(s.refiller),
	}
	bf.components[iPadding] = &component{
		width: runewidth.StringWidth(stripansi.Strip(s.padding)),
		bytes: []byte(s.padding),
	}
	bf.tip.onComplete = &component{
		width: runewidth.StringWidth(stripansi.Strip(s.tipOnComplete)),
		bytes: []byte(s.tipOnComplete),
	}
	bf.tip.frames = make([]*component, len(s.tipFrames))
	for i, t := range s.tipFrames {
		bf.tip.frames[i] = &component{
			width: runewidth.StringWidth(stripansi.Strip(t)),
			bytes: []byte(t),
		}
	}
	return bf
}

func (s *bFiller) Fill(w io.Writer, stat decor.Statistics) {
	width := internal.CheckRequestedWidth(stat.RequestedWidth, stat.AvailableWidth)
	brackets := s.components[iLbound].width + s.components[iRbound].width
	// don't count brackets as progress
	width -= brackets
	if width < 0 {
		return
	}

	mustWrite(w, s.components[iLbound].bytes)
	defer mustWrite(w, s.components[iRbound].bytes)

	if width == 0 {
		return
	}

	var filling [][]byte
	var padding [][]byte
	var tip *component
	var filled int
	var refWidth int
	curWidth := int(internal.PercentageRound(stat.Total, stat.Current, uint(width)))

	if stat.Current >= stat.Total {
		tip = s.tip.onComplete
	} else {
		tip = s.tip.frames[s.tip.count%uint(len(s.tip.frames))]
	}

	if curWidth > 0 {
		filling = append(filling, tip.bytes)
		filled += tip.width
		s.tip.count++
	}

	if stat.Refill > 0 {
		refWidth = int(internal.PercentageRound(stat.Total, stat.Refill, uint(width)))
		curWidth -= refWidth
		refWidth += curWidth
	}

	for filled < curWidth {
		if curWidth-filled >= s.components[iFiller].width {
			filling = append(filling, s.components[iFiller].bytes)
			if s.components[iFiller].width == 0 {
				break
			}
			filled += s.components[iFiller].width
		} else {
			filling = append(filling, []byte("…"))
			filled++
		}
	}

	for filled < refWidth {
		if refWidth-filled >= s.components[iRefiller].width {
			filling = append(filling, s.components[iRefiller].bytes)
			if s.components[iRefiller].width == 0 {
				break
			}
			filled += s.components[iRefiller].width
		} else {
			filling = append(filling, []byte("…"))
			filled++
		}
	}

	padWidth := width - filled
	for padWidth > 0 {
		if padWidth >= s.components[iPadding].width {
			padding = append(padding, s.components[iPadding].bytes)
			if s.components[iPadding].width == 0 {
				break
			}
			padWidth -= s.components[iPadding].width
		} else {
			padding = append(padding, []byte("…"))
			padWidth--
		}
	}

	if s.rev {
		flush(w, padding, filling)
	} else {
		flush(w, filling, padding)
	}
}

func flush(w io.Writer, filling, padding [][]byte) {
	for i := len(filling) - 1; i >= 0; i-- {
		mustWrite(w, filling[i])
	}
	for i := 0; i < len(padding); i++ {
		mustWrite(w, padding[i])
	}
}

func mustWrite(w io.Writer, p []byte) {
	_, err := w.Write(p)
	if err != nil {
		panic(err)
	}
}
<commit_msg>refactoring: bar filler<commit_after>package mpb

import (
	"io"

	"github.com/acarl005/stripansi"
	"github.com/mattn/go-runewidth"
	"github.com/vbauerster/mpb/v8/decor"
	"github.com/vbauerster/mpb/v8/internal"
)

const (
	iLbound = iota
	iRbound
	iFiller
	iRefiller
	iPadding
	components
)

// BarStyleComposer interface.
type BarStyleComposer interface {
	BarFillerBuilder
	Lbound(string) BarStyleComposer
	Rbound(string) BarStyleComposer
	Filler(string) BarStyleComposer
	Refiller(string) BarStyleComposer
	Padding(string) BarStyleComposer
	TipOnComplete(string) BarStyleComposer
	Tip(frames ...string) BarStyleComposer
	Reverse() BarStyleComposer
}

type bFiller struct {
	rev        bool
	components [components]*component
	tip        struct {
		count      uint
		onComplete *component
		frames     []*component
	}
}

type component struct {
	width int
	bytes []byte
}

type barStyle struct {
	lbound        string
	rbound        string
	filler        string
	refiller      string
	padding       string
	tipOnComplete string
	tipFrames     []string
	rev           bool
}

// BarStyle constructs default bar style which can be altered via
// BarStyleComposer interface.
func BarStyle() BarStyleComposer {
	return &barStyle{
		lbound:    "[",
		rbound:    "]",
		filler:    "=",
		refiller:  "+",
		padding:   "-",
		tipFrames: []string{">"},
	}
}

func (s *barStyle) Lbound(bound string) BarStyleComposer {
	s.lbound = bound
	return s
}

func (s *barStyle) Rbound(bound string) BarStyleComposer {
	s.rbound = bound
	return s
}

func (s *barStyle) Filler(filler string) BarStyleComposer {
	s.filler = filler
	return s
}

func (s *barStyle) Refiller(refiller string) BarStyleComposer {
	s.refiller = refiller
	return s
}

func (s *barStyle) Padding(padding string) BarStyleComposer {
	s.padding = padding
	return s
}

func (s *barStyle) TipOnComplete(tip string) BarStyleComposer {
	s.tipOnComplete = tip
	return s
}

func (s *barStyle) Tip(frames ...string) BarStyleComposer {
	if len(frames) != 0 {
		s.tipFrames = append(s.tipFrames[:0], frames...)
	}
	return s
}

func (s *barStyle) Reverse() BarStyleComposer {
	s.rev = true
	return s
}

func (s *barStyle) Build() BarFiller {
	bf := &bFiller{rev: s.rev}
	bf.components[iLbound] = &component{
		width: runewidth.StringWidth(stripansi.Strip(s.lbound)),
		bytes: []byte(s.lbound),
	}
	bf.components[iRbound] = &component{
		width: runewidth.StringWidth(stripansi.Strip(s.rbound)),
		bytes: []byte(s.rbound),
	}
	bf.components[iFiller] = &component{
		width: runewidth.StringWidth(stripansi.Strip(s.filler)),
		bytes: []byte(s.filler),
	}
	bf.components[iRefiller] = &component{
		width: runewidth.StringWidth(stripansi.Strip(s.refiller)),
		bytes: []byte(s.refiller),
	}
	bf.components[iPadding] = &component{
		width: runewidth.StringWidth(stripansi.Strip(s.padding)),
		bytes: []byte(s.padding),
	}
	bf.tip.onComplete = &component{
		width: runewidth.StringWidth(stripansi.Strip(s.tipOnComplete)),
		bytes: []byte(s.tipOnComplete),
	}
	bf.tip.frames = make([]*component, len(s.tipFrames))
	for i, t := range s.tipFrames {
		bf.tip.frames[i] = &component{
			width: runewidth.StringWidth(stripansi.Strip(t)),
			bytes: []byte(t),
		}
	}
	return bf
}

func (s *bFiller) Fill(w io.Writer, stat decor.Statistics) (err error) {
	width := internal.CheckRequestedWidth(stat.RequestedWidth, stat.AvailableWidth)
	brackets := s.components[iLbound].width + s.components[iRbound].width
	// don't count brackets as progress
	width -= brackets
	if width < 0 {
		return nil
	}

	_, err = w.Write(s.components[iLbound].bytes)
	if err != nil {
		return err
	}
	defer func() {
		if err == nil {
			_, err = w.Write(s.components[iRbound].bytes)
		}
	}()

	if width == 0 {
		return nil
	}

	var filling [][]byte
	var padding [][]byte
	var tip *component
	var filled int
	var refWidth int
	curWidth := int(internal.PercentageRound(stat.Total, stat.Current, uint(width)))

	if stat.Current >= stat.Total {
		tip = s.tip.onComplete
	} else {
		tip = s.tip.frames[s.tip.count%uint(len(s.tip.frames))]
	}

	if curWidth > 0 {
		filling = append(filling, tip.bytes)
		filled += tip.width
		s.tip.count++
	}

	if stat.Refill > 0 {
		refWidth = int(internal.PercentageRound(stat.Total, stat.Refill, uint(width)))
		curWidth -= refWidth
		refWidth += curWidth
	}

	for filled < curWidth {
		if curWidth-filled >= s.components[iFiller].width {
			filling = append(filling, s.components[iFiller].bytes)
			if s.components[iFiller].width == 0 {
				break
			}
			filled += s.components[iFiller].width
		} else {
			filling = append(filling, []byte("…"))
			filled++
		}
	}

	for filled < refWidth {
		if refWidth-filled >= s.components[iRefiller].width {
			filling = append(filling, s.components[iRefiller].bytes)
			if s.components[iRefiller].width == 0 {
				break
			}
			filled += s.components[iRefiller].width
		} else {
			filling = append(filling, []byte("…"))
			filled++
		}
	}

	padWidth := width - filled
	for padWidth > 0 {
		if padWidth >= s.components[iPadding].width {
			padding = append(padding, s.components[iPadding].bytes)
			if s.components[iPadding].width == 0 {
				break
			}
			padWidth -= s.components[iPadding].width
		} else {
			padding = append(padding, []byte("…"))
			padWidth--
		}
	}

	if s.rev {
		return flush(w, padding, filling)
	}
	return flush(w, filling, padding)
}

func flush(w io.Writer, filling, padding [][]byte) error {
	for i := len(filling) - 1; i >= 0; i-- {
		_, err := w.Write(filling[i])
		if err != nil {
			return err
		}
	}
	for i := 0; i < len(padding); i++ {
		_, err := w.Write(padding[i])
		if err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package stonelizard

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

func (svc *Service) FetchEndpointHandler(proto, method, path string) (*UrlNode, []interface{}, map[string]interface{}, int) {
   var i, j int
   var pathId string
   var match []string
   var parms  []interface{}
   var authparms map[string]interface{}
   var endpoint UrlNode

   pathId = fmt.Sprintf("%s+%s:%s", strings.Split(proto,"/")[0], method, path)

   Goose.Serve.Logf(6,"Matcher will look on %#v", svc.Matcher)
   Goose.Serve.Logf(6,"Matcher will look for [%s]", pathId)
   match = svc.Matcher.FindStringSubmatch(pathId)
   Goose.Serve.Logf(0,"Matcher found this %#v\n", match) //6
   if len(match) == 0 {
      Goose.Serve.Logf(1,"Invalid service handler : %s", pathId)
      return nil, nil, nil, http.StatusBadRequest
   }

   parms = []interface{}{}
   authparms = map[string]interface{}{}

//   for _, endpoint = range svc.Svc {
   for i=1; i<len(match); i++ {
      Goose.Serve.Logf(6,"trying %s with endpoint:  %s",pathId,svc.Svc[svc.MatchedOps[i-1]].Path)
      if len(match[i]) > 0 {
         Goose.Serve.Logf(0,"Found endpoint %s for: %s",svc.Svc[svc.MatchedOps[i-1]].Path,pathId)//4
         Goose.Serve.Logf(0,"Endpoint %#v",svc.Svc[svc.MatchedOps[i-1]])//7
         Goose.Serve.Logf(0,"method: %s",method)
         endpoint = svc.Svc[svc.MatchedOps[i-1]]
         if method == "OPTIONS" {
            break
         }
         for j=i+1; (j<len(match)) && (len(match[j])>0); j++ {
            Goose.Serve.Logf(0,"j=%d, i=%d, endpoint.ParmNames: %#v, authparms: %#v", j, i, endpoint.ParmNames, authparms)
            authparms[endpoint.ParmNames[j-i-1]] = match[j]
         }
         for k := i+1; k<j; k++ { // parms = []interface{}(match[i+1:j])
            parms = append(parms,match[k])
         }
         j -= i + 1
         break
      }
   }

   return &endpoint, parms, authparms, 0
}

<commit_msg>@{doc}Clearing some debug message garbage<commit_after>package stonelizard

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

func (svc *Service) FetchEndpointHandler(proto, method, path string) (*UrlNode, []interface{}, map[string]interface{}, int) {
   var i, j int
   var pathId string
   var match []string
   var parms  []interface{}
   var authparms map[string]interface{}
   var endpoint UrlNode

   pathId = fmt.Sprintf("%s+%s:%s", strings.Split(proto,"/")[0], method, path)

   Goose.Serve.Logf(6,"Matcher will look on %#v", svc.Matcher)
   Goose.Serve.Logf(6,"Matcher will look for [%s]", pathId)
   match = svc.Matcher.FindStringSubmatch(pathId)
   Goose.Serve.Logf(6,"Matcher found this %#v\n", match) //6
   if len(match) == 0 {
      Goose.Serve.Logf(1,"Invalid service handler : %s", pathId)
      return nil, nil, nil, http.StatusBadRequest
   }

   parms = []interface{}{}
   authparms = map[string]interface{}{}

//   for _, endpoint = range svc.Svc {
   for i=1; i<len(match); i++ {
      Goose.Serve.Logf(6,"trying %s with endpoint:  %s",pathId,svc.Svc[svc.MatchedOps[i-1]].Path)
      if len(match[i]) > 0 {
         Goose.Serve.Logf(6,"Found endpoint %s for: %s",svc.Svc[svc.MatchedOps[i-1]].Path,pathId)//4
         Goose.Serve.Logf(6,"Endpoint %#v",svc.Svc[svc.MatchedOps[i-1]])//7
         Goose.Serve.Logf(5,"method: %s",method)
         endpoint = svc.Svc[svc.MatchedOps[i-1]]
         if method == "OPTIONS" {
            break
         }
         for j=i+1; (j<len(match)) && (len(match[j])>0); j++ {
            Goose.Serve.Logf(7,"j=%d, i=%d, endpoint.ParmNames: %#v, authparms: %#v", j, i, endpoint.ParmNames, authparms)
            authparms[endpoint.ParmNames[j-i-1]] = match[j]
         }
         for k := i+1; k<j; k++ { // parms = []interface{}(match[i+1:j])
            parms = append(parms,match[k])
         }
         j -= i + 1
         break
      }
   }

   return &endpoint, parms, authparms, 0
}

<|endoftext|>