<commit_before>/* Data functions */

/*
 * Copyright (c) 2013-2014, Jeremy Bingham (<jbingham@gmail.com>)
 *
 * 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 (
	"net/http"
	"encoding/json"
	"fmt"
	"github.com/ctdk/goiardi/data_bag"
	"github.com/ctdk/goiardi/util"
	"github.com/ctdk/goiardi/actor"
	"github.com/ctdk/goiardi/log_info"
)

func data_handler(w http.ResponseWriter, r *http.Request){
	w.Header().Set("Content-Type", "application/json")

	path_array := SplitPath(r.URL.Path)

	db_response := make(map[string]interface{})
	opUser, oerr := actor.GetReqUser(r.Header.Get("X-OPS-USERID"))
	if oerr != nil {
		JsonErrorReport(w, r, oerr.Error(), oerr.Status())
		return
	}

	if len(path_array) == 1 {
		/* Either a list of data bags, or a POST to create a new one */
		switch r.Method {
			case "GET":
				if opUser.IsValidator() {
					JsonErrorReport(w, r, "You are not allowed to perform this action", http.StatusForbidden)
					return
				}
				/* The list */
				db_list := data_bag.GetList()
				for _, k := range db_list {
					item_url := fmt.Sprintf("/data/%s", k)
					db_response[k] = util.CustomURL(item_url)
				}
			case "POST":
				if !opUser.IsAdmin() {
					JsonErrorReport(w, r, "You are not allowed to perform this action", http.StatusForbidden)
					return
				}
				db_data, jerr := ParseObjJson(r.Body)
				if jerr != nil {
					JsonErrorReport(w, r, jerr.Error(), http.StatusBadRequest)
					return
				}
				/* check that the name exists */
				switch t := db_data["name"].(type) {
					case string:
						if t == "" {
							JsonErrorReport(w, r, "Field 'name' missing", http.StatusBadRequest)
							return
						}
					default:
						JsonErrorReport(w, r, "Field 'name' missing", http.StatusBadRequest)
						return
				}
				chef_dbag, _ := data_bag.Get(db_data["name"].(string))
				if chef_dbag != nil {
					httperr := fmt.Errorf("Data bag %s already exists.", db_data["name"].(string))
					JsonErrorReport(w, r, httperr.Error(), http.StatusConflict)
					return
				}
				chef_dbag, nerr := data_bag.New(db_data["name"].(string))
				if nerr != nil {
					JsonErrorReport(w, r, nerr.Error(), nerr.Status())
					return
				}
				serr := chef_dbag.Save()
				if serr != nil {
					JsonErrorReport(w, r, serr.Error(), http.StatusInternalServerError)
					return
				}
				if lerr := log_info.LogEvent(opUser, chef_dbag, "create"); lerr != nil {
					JsonErrorReport(w, r, lerr.Error(), http.StatusInternalServerError)
					return
				}
				db_response["uri"] = util.ObjURL(chef_dbag)
				w.WriteHeader(http.StatusCreated)
			default:
				/* The chef-pedant spec wants this response for
				 * some reason. Mix it up, I guess. */
				JsonErrorReport(w, r, "GET, PUT", http.StatusMethodNotAllowed)
				return
		}
	} else {
		db_name := path_array[1]

		/* chef-pedant is unhappy about not reporting the HTTP status
		 * as 404 by fetching the data bag before we see if the method
		 * is allowed, so do a quick check for that here. */
		if (len(path_array) == 2  && r.Method == "PUT") || (len(path_array) == 3 && r.Method == "POST"){
			JsonErrorReport(w, r, "Method not allowed", http.StatusMethodNotAllowed)
			return
		}
		if opUser.IsValidator() || (!opUser.IsAdmin() && r.Method != "GET") {
			JsonErrorReport(w, r, "You are not allowed to perform this action", http.StatusForbidden)
			return
		}
		chef_dbag, err := data_bag.Get(db_name)
		if err != nil {
			var err_msg string
			status := err.Status()
			if r.Method == "POST" {
				/* Posts get a special snowflake message */
				err_msg = fmt.Sprintf("No data bag '%s' could be found. Please create this data bag before adding items to it.", db_name)
			} else {
				if len(path_array) == 3 {
					/* This is nuts. */
					if r.Method == "DELETE" {
						err_msg = fmt.Sprintf("Cannot load data bag %s item %s", db_name, path_array[2])
					} else {
						err_msg = fmt.Sprintf("Cannot load data bag item %s for data bag %s", path_array[2], db_name)
					}
				} else {
					err_msg = err.Error()
				}
			}
			JsonErrorReport(w, r, err_msg, status)
			return
		}
		if len(path_array) == 2 {
			/* getting list of data bag items and creating data bag
			 * items. */
			switch r.Method {
				case "GET":

					for _, k := range chef_dbag.ListDBItems() {
						db_response[k] = util.CustomObjURL(chef_dbag, k)
					}
				case "DELETE":
					/* The chef API docs don't say anything
					 * about this existing, but it does,
					 * and without it you can't delete data
					 * bags at all. */
					db_response["chef_type"] = "data_bag"
					db_response["json_class"] = "Chef::DataBag"
					db_response["name"] = chef_dbag.Name
					err := chef_dbag.Delete()
					if err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					if lerr := log_info.LogEvent(opUser, chef_dbag, "delete"); lerr != nil {
						JsonErrorReport(w, r, lerr.Error(), http.StatusInternalServerError)
						return
					}
				case "POST":
					raw_data := data_bag.RawDataBagJson(r.Body)
					dbitem, nerr := chef_dbag.NewDBItem(raw_data)
					if nerr != nil {
						JsonErrorReport(w, r, nerr.Error(), nerr.Status())
						return
					}
					if lerr := log_info.LogEvent(opUser, dbitem, "create"); lerr != nil {
						JsonErrorReport(w, r, lerr.Error(), http.StatusInternalServerError)
						return
					}

					/* The data bag return values are all
					 * kinds of weird. Sometimes it sends
					 * just the raw data, sometimes it sends
					 * the whole object, sometimes a special
					 * snowflake version. Ugh. Have to loop
					 * through to avoid updating the pointer
					 * in the cache by just assigning
					 * dbitem.RawData to db_response. Urk.
					 */
					for k, v := range dbitem.RawData {
						db_response[k] = v
					}
					db_response["data_bag"] = dbitem.DataBagName
					db_response["chef_type"] = dbitem.ChefType
					w.WriteHeader(http.StatusCreated)
				default:
					JsonErrorReport(w, r, "GET, DELETE, POST", http.StatusMethodNotAllowed)
					return
			}
		} else {
			/* getting, editing, and deleting existing data bag items. */
			db_item_name := path_array[2]
			if _, err := chef_dbag.GetDBItem(db_item_name); err != nil {
				var httperr string
				if r.Method != "DELETE" {
					httperr = fmt.Sprintf("Cannot load data bag item %s for data bag %s", db_item_name, chef_dbag.Name)
				} else {
					httperr = fmt.Sprintf("Cannot load data bag %s item %s", chef_dbag.Name, db_item_name)
				}
				JsonErrorReport(w, r, httperr, http.StatusNotFound)
				return
			}
			switch r.Method {
				case "GET":
					dbi, err := chef_dbag.GetDBItem(db_item_name)
					if err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					db_response = dbi.RawData
				case "DELETE":
					dbi, err := chef_dbag.GetDBItem(db_item_name)
					if err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					/* Gotta short circuit this */
					enc := json.NewEncoder(w)
					if err := enc.Encode(&dbi); err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					err = chef_dbag.DeleteDBItem(db_item_name)
					if err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					if lerr := log_info.LogEvent(opUser, dbi, "delete"); lerr != nil {
						JsonErrorReport(w, r, lerr.Error(), http.StatusInternalServerError)
						return
					}
					return
				case "PUT":
					raw_data := data_bag.RawDataBagJson(r.Body)
					if raw_id, ok := raw_data["id"]; ok {
						switch raw_id := raw_id.(type) {
							case string:
								if raw_id != db_item_name {
									JsonErrorReport(w, r, "DataBagItem name mismatch.", http.StatusBadRequest)
									return
								}
							default:
								JsonErrorReport(w, r, "Bad request", http.StatusBadRequest)
								return
						}
					}
					dbitem, err := chef_dbag.UpdateDBItem(db_item_name, raw_data)
					if err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					if lerr := log_info.LogEvent(opUser, dbitem, "modify"); lerr != nil {
						JsonErrorReport(w, r, lerr.Error(), http.StatusInternalServerError)
						return
					}
					/* Another weird data bag item response
					 * which isn't at all unusual. */
					for k, v := range dbitem.RawData {
						db_response[k] = v
					}
					db_response["data_bag"] = dbitem.DataBagName
					db_response["chef_type"] = dbitem.ChefType
					db_response["id"] = db_item_name
				default:
					JsonErrorReport(w, r, "GET, DELETE, PUT", http.StatusMethodNotAllowed)
					return
			}
		}
	}

	enc := json.NewEncoder(w)
	if err := enc.Encode(&db_response); err != nil {
		JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
	}
}

<commit_msg>Finally got to the bottom of the annoying data bag error<commit_after>/* Data functions */

/*
 * Copyright (c) 2013-2014, Jeremy Bingham (<jbingham@gmail.com>)
 *
 * 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 (
	"net/http"
	"encoding/json"
	"fmt"
	"github.com/ctdk/goiardi/data_bag"
	"github.com/ctdk/goiardi/util"
	"github.com/ctdk/goiardi/actor"
	"github.com/ctdk/goiardi/log_info"
)

func data_handler(w http.ResponseWriter, r *http.Request){
	w.Header().Set("Content-Type", "application/json")

	path_array := SplitPath(r.URL.Path)

	db_response := make(map[string]interface{})
	opUser, oerr := actor.GetReqUser(r.Header.Get("X-OPS-USERID"))
	if oerr != nil {
		JsonErrorReport(w, r, oerr.Error(), oerr.Status())
		return
	}

	if len(path_array) == 1 {
		/* Either a list of data bags, or a POST to create a new one */
		switch r.Method {
			case "GET":
				if opUser.IsValidator() {
					JsonErrorReport(w, r, "You are not allowed to perform this action", http.StatusForbidden)
					return
				}
				/* The list */
				db_list := data_bag.GetList()
				for _, k := range db_list {
					item_url := fmt.Sprintf("/data/%s", k)
					db_response[k] = util.CustomURL(item_url)
				}
			case "POST":
				if !opUser.IsAdmin() {
					JsonErrorReport(w, r, "You are not allowed to perform this action", http.StatusForbidden)
					return
				}
				db_data, jerr := ParseObjJson(r.Body)
				if jerr != nil {
					JsonErrorReport(w, r, jerr.Error(), http.StatusBadRequest)
					return
				}
				/* check that the name exists */
				switch t := db_data["name"].(type) {
					case string:
						if t == "" {
							JsonErrorReport(w, r, "Field 'name' missing", http.StatusBadRequest)
							return
						}
					default:
						JsonErrorReport(w, r, "Field 'name' missing", http.StatusBadRequest)
						return
				}
				chef_dbag, _ := data_bag.Get(db_data["name"].(string))
				if chef_dbag != nil {
					httperr := fmt.Errorf("Data bag %s already exists.", db_data["name"].(string))
					JsonErrorReport(w, r, httperr.Error(), http.StatusConflict)
					return
				}
				chef_dbag, nerr := data_bag.New(db_data["name"].(string))
				if nerr != nil {
					JsonErrorReport(w, r, nerr.Error(), nerr.Status())
					return
				}
				serr := chef_dbag.Save()
				if serr != nil {
					JsonErrorReport(w, r, serr.Error(), http.StatusInternalServerError)
					return
				}
				if lerr := log_info.LogEvent(opUser, chef_dbag, "create"); lerr != nil {
					JsonErrorReport(w, r, lerr.Error(), http.StatusInternalServerError)
					return
				}
				db_response["uri"] = util.ObjURL(chef_dbag)
				w.WriteHeader(http.StatusCreated)
			default:
				/* The chef-pedant spec wants this response for
				 * some reason. Mix it up, I guess. */
				w.Header().Set("Allow", "GET, POST")
				JsonErrorReport(w, r, "GET, POST", http.StatusMethodNotAllowed)
				return
		}
	} else {
		db_name := path_array[1]

		/* chef-pedant is unhappy about not reporting the HTTP status
		 * as 404 by fetching the data bag before we see if the method
		 * is allowed, so do a quick check for that here. */
		if (len(path_array) == 2  && r.Method == "PUT") || (len(path_array) == 3 && r.Method == "POST"){
			var allowed string
			if len(path_array) == 2 {
				allowed = "GET, POST, DELETE"
			} else {
				allowed = "GET, PUT, DELETE"
			}
			w.Header().Set("Allow", allowed)
			JsonErrorReport(w, r, "Method not allowed", http.StatusMethodNotAllowed)
			return
		}
		if opUser.IsValidator() || (!opUser.IsAdmin() && r.Method != "GET") {
			JsonErrorReport(w, r, "You are not allowed to perform this action", http.StatusForbidden)
			return
		}
		chef_dbag, err := data_bag.Get(db_name)
		if err != nil {
			var err_msg string
			status := err.Status()
			if r.Method == "POST" {
				/* Posts get a special snowflake message */
				err_msg = fmt.Sprintf("No data bag '%s' could be found. Please create this data bag before adding items to it.", db_name)
			} else {
				if len(path_array) == 3 {
					/* This is nuts. */
					if r.Method == "DELETE" {
						err_msg = fmt.Sprintf("Cannot load data bag %s item %s", db_name, path_array[2])
					} else {
						err_msg = fmt.Sprintf("Cannot load data bag item %s for data bag %s", path_array[2], db_name)
					}
				} else {
					err_msg = err.Error()
				}
			}
			JsonErrorReport(w, r, err_msg, status)
			return
		}
		if len(path_array) == 2 {
			/* getting list of data bag items and creating data bag
			 * items. */
			switch r.Method {
				case "GET":

					for _, k := range chef_dbag.ListDBItems() {
						db_response[k] = util.CustomObjURL(chef_dbag, k)
					}
				case "DELETE":
					/* The chef API docs don't say anything
					 * about this existing, but it does,
					 * and without it you can't delete data
					 * bags at all. */
					db_response["chef_type"] = "data_bag"
					db_response["json_class"] = "Chef::DataBag"
					db_response["name"] = chef_dbag.Name
					err := chef_dbag.Delete()
					if err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					if lerr := log_info.LogEvent(opUser, chef_dbag, "delete"); lerr != nil {
						JsonErrorReport(w, r, lerr.Error(), http.StatusInternalServerError)
						return
					}
				case "POST":
					raw_data := data_bag.RawDataBagJson(r.Body)
					dbitem, nerr := chef_dbag.NewDBItem(raw_data)
					if nerr != nil {
						JsonErrorReport(w, r, nerr.Error(), nerr.Status())
						return
					}
					if lerr := log_info.LogEvent(opUser, dbitem, "create"); lerr != nil {
						JsonErrorReport(w, r, lerr.Error(), http.StatusInternalServerError)
						return
					}

					/* The data bag return values are all
					 * kinds of weird. Sometimes it sends
					 * just the raw data, sometimes it sends
					 * the whole object, sometimes a special
					 * snowflake version. Ugh. Have to loop
					 * through to avoid updating the pointer
					 * in the cache by just assigning
					 * dbitem.RawData to db_response. Urk.
					 */
					for k, v := range dbitem.RawData {
						db_response[k] = v
					}
					db_response["data_bag"] = dbitem.DataBagName
					db_response["chef_type"] = dbitem.ChefType
					w.WriteHeader(http.StatusCreated)
				default:
					w.Header().Set("Allow", "GET, DELETE, POST")
					JsonErrorReport(w, r, "GET, DELETE, POST", http.StatusMethodNotAllowed)
					return
			}
		} else {
			/* getting, editing, and deleting existing data bag items. */
			db_item_name := path_array[2]
			if _, err := chef_dbag.GetDBItem(db_item_name); err != nil {
				var httperr string
				if r.Method != "DELETE" {
					httperr = fmt.Sprintf("Cannot load data bag item %s for data bag %s", db_item_name, chef_dbag.Name)
				} else {
					httperr = fmt.Sprintf("Cannot load data bag %s item %s", chef_dbag.Name, db_item_name)
				}
				JsonErrorReport(w, r, httperr, http.StatusNotFound)
				return
			}
			switch r.Method {
				case "GET":
					dbi, err := chef_dbag.GetDBItem(db_item_name)
					if err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					db_response = dbi.RawData
				case "DELETE":
					dbi, err := chef_dbag.GetDBItem(db_item_name)
					if err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					/* Gotta short circuit this */
					enc := json.NewEncoder(w)
					if err := enc.Encode(&dbi); err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					err = chef_dbag.DeleteDBItem(db_item_name)
					if err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					if lerr := log_info.LogEvent(opUser, dbi, "delete"); lerr != nil {
						JsonErrorReport(w, r, lerr.Error(), http.StatusInternalServerError)
						return
					}
					return
				case "PUT":
					raw_data := data_bag.RawDataBagJson(r.Body)
					if raw_id, ok := raw_data["id"]; ok {
						switch raw_id := raw_id.(type) {
							case string:
								if raw_id != db_item_name {
									JsonErrorReport(w, r, "DataBagItem name mismatch.", http.StatusBadRequest)
									return
								}
							default:
								JsonErrorReport(w, r, "Bad request", http.StatusBadRequest)
								return
						}
					}
					dbitem, err := chef_dbag.UpdateDBItem(db_item_name, raw_data)
					if err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					if lerr := log_info.LogEvent(opUser, dbitem, "modify"); lerr != nil {
						JsonErrorReport(w, r, lerr.Error(), http.StatusInternalServerError)
						return
					}
					/* Another weird data bag item response
					 * which isn't at all unusual. */
					for k, v := range dbitem.RawData {
						db_response[k] = v
					}
					db_response["data_bag"] = dbitem.DataBagName
					db_response["chef_type"] = dbitem.ChefType
					db_response["id"] = db_item_name
				default:
					w.Header().Set("Allow", "GET, DELETE, PUT")
					JsonErrorReport(w, r, "GET, DELETE, PUT", http.StatusMethodNotAllowed)
					return
			}
		}
	}

	enc := json.NewEncoder(w)
	if err := enc.Encode(&db_response); err != nil {
		JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
	}
}

<|endoftext|>


<commit_before>// Copyright 2013 Google Inc. All rights reserved.
//
// 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 imageproxy

import (
	"bytes"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

const (
	optFit            = "fit"
	optFlipVertical   = "fv"
	optFlipHorizontal = "fh"
	optRotatePrefix   = "r"
	optQualityPrefix  = "q"
	optSizeDelimiter  = "x"
)

// URLError reports a malformed URL error.
type URLError struct {
	Message string
	URL     *url.URL
}

func (e URLError) Error() string {
	return fmt.Sprintf("malformed URL %q: %s", e.URL, e.Message)
}

// Options specifies transformations to be performed on the requested image.
type Options struct {
	// See ParseOptions for interpretation of Width and Height values
	Width  float64
	Height float64

	// If true, resize the image to fit in the specified dimensions.  Image
	// will not be cropped, and aspect ratio will be maintained.
	Fit bool

	// Rotate image the specified degrees counter-clockwise.  Valid values
	// are 90, 180, 270.
	Rotate int

	FlipVertical   bool
	FlipHorizontal bool

	// Quality of output image
	Quality int
}

var emptyOptions = Options{}

func (o Options) String() string {
	buf := new(bytes.Buffer)
	fmt.Fprintf(buf, "%v%s%v", o.Width, optSizeDelimiter, o.Height)
	if o.Fit {
		fmt.Fprintf(buf, ",%s", optFit)
	}
	if o.Rotate != 0 {
		fmt.Fprintf(buf, ",%s%d", string(optRotatePrefix), o.Rotate)
	}
	if o.FlipVertical {
		fmt.Fprintf(buf, ",%s", optFlipVertical)
	}
	if o.FlipHorizontal {
		fmt.Fprintf(buf, ",%s", optFlipHorizontal)
	}
	fmt.Fprintf(buf, ",%s%d", string(optQualityPrefix), o.Quality)
	return buf.String()
}

// ParseOptions parses str as a list of comma separated transformation options.
// The following options can be specified in any order:
//
// Size and Cropping
//
// The size option takes the general form "{width}x{height}", where width and
// height are numbers. Integer values greater than 1 are interpreted as exact
// pixel values. Floats between 0 and 1 are interpreted as percentages of the
// original image size. If either value is omitted or set to 0, it will be
// automatically set to preserve the aspect ratio based on the other dimension.
// If a single number is provided (with no "x" separator), it will be used for
// both height and width.
//
// Depending on the size options specified, an image may be cropped to fit the
// requested size. In all cases, the original aspect ratio of the image will be
// preserved; imageproxy will never stretch the original image.
//
// When no explicit crop mode is specified, the following rules are followed:
//
// - If both width and height values are specified, the image will be scaled to
// fill the space, cropping if necessary to fit the exact dimension.
//
// - If only one of the width or height values is specified, the image will be
// resized to fit the specified dimension, scaling the other dimension as
// needed to maintain the aspect ratio.
//
// If the "fit" option is specified together with a width and height value, the
// image will be resized to fit within a containing box of the specified size.
// As always, the original aspect ratio will be preserved. Specifying the "fit"
// option with only one of either width or height does the same thing as if
// "fit" had not been specified.
//
// Rotation and Flips
//
// The "r{degrees}" option will rotate the image the specified number of
// degrees, counter-clockwise. Valid degrees values are 90, 180, and 270.
//
// The "fv" option will flip the image vertically. The "fh" option will flip
// the image horizontally. Images are flipped after being rotated.
//
// Quality
//
// The "q{qualityPercentage}" option can be used to specify the quality of the
// output file (JPEG only)
//
// Examples
//
// 	0x0       - no resizing
// 	200x      - 200 pixels wide, proportional height
// 	0.15x     - 15% original width, proportional height
// 	x100      - 100 pixels tall, proportional width
// 	100x150   - 100 by 150 pixels, cropping as needed
// 	100       - 100 pixels square, cropping as needed
// 	150,fit   - scale to fit 150 pixels square, no cropping
// 	100,r90   - 100 pixels square, rotated 90 degrees
// 	100,fv,fh - 100 pixels square, flipped horizontal and vertical
// 	200x,q80  - 200 pixels wide, proportional height, 80% quality
func ParseOptions(str string) Options {
	options := Options{}

	for _, opt := range strings.Split(str, ",") {
		switch {
		case len(opt) == 0:
			break
		case opt == optFit:
			options.Fit = true
		case opt == optFlipVertical:
			options.FlipVertical = true
		case opt == optFlipHorizontal:
			options.FlipHorizontal = true
		case strings.HasPrefix(opt, optRotatePrefix):
			value := strings.TrimPrefix(opt, optRotatePrefix)
			options.Rotate, _ = strconv.Atoi(value)
		case strings.HasPrefix(opt, optQualityPrefix):
			value := strings.TrimPrefix(opt, optQualityPrefix)
			options.Quality, _ = strconv.Atoi(value)
		case strings.Contains(opt, optSizeDelimiter):
			size := strings.SplitN(opt, optSizeDelimiter, 2)
			if w := size[0]; w != "" {
				options.Width, _ = strconv.ParseFloat(w, 64)
			}
			if h := size[1]; h != "" {
				options.Height, _ = strconv.ParseFloat(h, 64)
			}
		default:
			if size, err := strconv.ParseFloat(opt, 64); err == nil {
				options.Width = size
				options.Height = size
			}
		}
	}

	return options
}

// Request is an imageproxy request which includes a remote URL of an image to
// proxy, and an optional set of transformations to perform.
type Request struct {
	URL     *url.URL // URL of the image to proxy
	Options Options  // Image transformation to perform
}

// NewRequest parses an http.Request into an imageproxy Request.  Options and
// the remote image URL are specified in the request path, formatted as:
// /{options}/{remote_url}.  Options may be omitted, so a request path may
// simply contian /{remote_url}.  The remote URL must be an absolute "http" or
// "https" URL, should not be URL encoded, and may contain a query string.
//
// Assuming an imageproxy server running on localhost, the following are all
// valid imageproxy requests:
//
// 	http://localhost/100x200/http://example.com/image.jpg
// 	http://localhost/100x200,r90/http://example.com/image.jpg?foo=bar
// 	http://localhost//http://example.com/image.jpg
// 	http://localhost/http://example.com/image.jpg
func NewRequest(r *http.Request, baseURL *url.URL) (*Request, error) {
	var err error
	req := new(Request)

	path := r.URL.Path[1:] // strip leading slash
	req.URL, err = url.Parse(path)
	if err != nil || !req.URL.IsAbs() {
		// first segment should be options
		parts := strings.SplitN(path, "/", 2)
		if len(parts) != 2 {
			return nil, URLError{"too few path segments", r.URL}
		}

		req.URL, err = url.Parse(parts[1])
		if err != nil {
			return nil, URLError{fmt.Sprintf("unable to parse remote URL: %v", err), r.URL}
		}

		req.Options = ParseOptions(parts[0])
	}

	if baseURL != nil {
		req.URL = baseURL.ResolveReference(req.URL)
	}

	if !req.URL.IsAbs() {
		return nil, URLError{"must provide absolute remote URL", r.URL}
	}

	if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
		return nil, URLError{"remote URL must have http or https scheme", r.URL}
	}

	// query string is always part of the remote URL
	req.URL.RawQuery = r.URL.RawQuery
	return req, nil
}
<commit_msg>rsc.io/grind cleanup<commit_after>// Copyright 2013 Google Inc. All rights reserved.
//
// 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 imageproxy

import (
	"bytes"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

const (
	optFit            = "fit"
	optFlipVertical   = "fv"
	optFlipHorizontal = "fh"
	optRotatePrefix   = "r"
	optQualityPrefix  = "q"
	optSizeDelimiter  = "x"
)

// URLError reports a malformed URL error.
type URLError struct {
	Message string
	URL     *url.URL
}

func (e URLError) Error() string {
	return fmt.Sprintf("malformed URL %q: %s", e.URL, e.Message)
}

// Options specifies transformations to be performed on the requested image.
type Options struct {
	// See ParseOptions for interpretation of Width and Height values
	Width  float64
	Height float64

	// If true, resize the image to fit in the specified dimensions.  Image
	// will not be cropped, and aspect ratio will be maintained.
	Fit bool

	// Rotate image the specified degrees counter-clockwise.  Valid values
	// are 90, 180, 270.
	Rotate int

	FlipVertical   bool
	FlipHorizontal bool

	// Quality of output image
	Quality int
}

var emptyOptions = Options{}

func (o Options) String() string {
	buf := new(bytes.Buffer)
	fmt.Fprintf(buf, "%v%s%v", o.Width, optSizeDelimiter, o.Height)
	if o.Fit {
		fmt.Fprintf(buf, ",%s", optFit)
	}
	if o.Rotate != 0 {
		fmt.Fprintf(buf, ",%s%d", string(optRotatePrefix), o.Rotate)
	}
	if o.FlipVertical {
		fmt.Fprintf(buf, ",%s", optFlipVertical)
	}
	if o.FlipHorizontal {
		fmt.Fprintf(buf, ",%s", optFlipHorizontal)
	}
	fmt.Fprintf(buf, ",%s%d", string(optQualityPrefix), o.Quality)
	return buf.String()
}

// ParseOptions parses str as a list of comma separated transformation options.
// The following options can be specified in any order:
//
// Size and Cropping
//
// The size option takes the general form "{width}x{height}", where width and
// height are numbers. Integer values greater than 1 are interpreted as exact
// pixel values. Floats between 0 and 1 are interpreted as percentages of the
// original image size. If either value is omitted or set to 0, it will be
// automatically set to preserve the aspect ratio based on the other dimension.
// If a single number is provided (with no "x" separator), it will be used for
// both height and width.
//
// Depending on the size options specified, an image may be cropped to fit the
// requested size. In all cases, the original aspect ratio of the image will be
// preserved; imageproxy will never stretch the original image.
//
// When no explicit crop mode is specified, the following rules are followed:
//
// - If both width and height values are specified, the image will be scaled to
// fill the space, cropping if necessary to fit the exact dimension.
//
// - If only one of the width or height values is specified, the image will be
// resized to fit the specified dimension, scaling the other dimension as
// needed to maintain the aspect ratio.
//
// If the "fit" option is specified together with a width and height value, the
// image will be resized to fit within a containing box of the specified size.
// As always, the original aspect ratio will be preserved. Specifying the "fit"
// option with only one of either width or height does the same thing as if
// "fit" had not been specified.
//
// Rotation and Flips
//
// The "r{degrees}" option will rotate the image the specified number of
// degrees, counter-clockwise. Valid degrees values are 90, 180, and 270.
//
// The "fv" option will flip the image vertically. The "fh" option will flip
// the image horizontally. Images are flipped after being rotated.
//
// Quality
//
// The "q{qualityPercentage}" option can be used to specify the quality of the
// output file (JPEG only)
//
// Examples
//
// 	0x0       - no resizing
// 	200x      - 200 pixels wide, proportional height
// 	0.15x     - 15% original width, proportional height
// 	x100      - 100 pixels tall, proportional width
// 	100x150   - 100 by 150 pixels, cropping as needed
// 	100       - 100 pixels square, cropping as needed
// 	150,fit   - scale to fit 150 pixels square, no cropping
// 	100,r90   - 100 pixels square, rotated 90 degrees
// 	100,fv,fh - 100 pixels square, flipped horizontal and vertical
// 	200x,q80  - 200 pixels wide, proportional height, 80% quality
func ParseOptions(str string) Options {
	var options Options

	for _, opt := range strings.Split(str, ",") {
		switch {
		case len(opt) == 0:
			break
		case opt == optFit:
			options.Fit = true
		case opt == optFlipVertical:
			options.FlipVertical = true
		case opt == optFlipHorizontal:
			options.FlipHorizontal = true
		case strings.HasPrefix(opt, optRotatePrefix):
			value := strings.TrimPrefix(opt, optRotatePrefix)
			options.Rotate, _ = strconv.Atoi(value)
		case strings.HasPrefix(opt, optQualityPrefix):
			value := strings.TrimPrefix(opt, optQualityPrefix)
			options.Quality, _ = strconv.Atoi(value)
		case strings.Contains(opt, optSizeDelimiter):
			size := strings.SplitN(opt, optSizeDelimiter, 2)
			if w := size[0]; w != "" {
				options.Width, _ = strconv.ParseFloat(w, 64)
			}
			if h := size[1]; h != "" {
				options.Height, _ = strconv.ParseFloat(h, 64)
			}
		default:
			if size, err := strconv.ParseFloat(opt, 64); err == nil {
				options.Width = size
				options.Height = size
			}
		}
	}

	return options
}

// Request is an imageproxy request which includes a remote URL of an image to
// proxy, and an optional set of transformations to perform.
type Request struct {
	URL     *url.URL // URL of the image to proxy
	Options Options  // Image transformation to perform
}

// NewRequest parses an http.Request into an imageproxy Request.  Options and
// the remote image URL are specified in the request path, formatted as:
// /{options}/{remote_url}.  Options may be omitted, so a request path may
// simply contian /{remote_url}.  The remote URL must be an absolute "http" or
// "https" URL, should not be URL encoded, and may contain a query string.
//
// Assuming an imageproxy server running on localhost, the following are all
// valid imageproxy requests:
//
// 	http://localhost/100x200/http://example.com/image.jpg
// 	http://localhost/100x200,r90/http://example.com/image.jpg?foo=bar
// 	http://localhost//http://example.com/image.jpg
// 	http://localhost/http://example.com/image.jpg
func NewRequest(r *http.Request, baseURL *url.URL) (*Request, error) {
	var err error
	req := new(Request)

	path := r.URL.Path[1:] // strip leading slash
	req.URL, err = url.Parse(path)
	if err != nil || !req.URL.IsAbs() {
		// first segment should be options
		parts := strings.SplitN(path, "/", 2)
		if len(parts) != 2 {
			return nil, URLError{"too few path segments", r.URL}
		}

		var err error
		req.URL, err = url.Parse(parts[1])
		if err != nil {
			return nil, URLError{fmt.Sprintf("unable to parse remote URL: %v", err), r.URL}
		}

		req.Options = ParseOptions(parts[0])
	}

	if baseURL != nil {
		req.URL = baseURL.ResolveReference(req.URL)
	}

	if !req.URL.IsAbs() {
		return nil, URLError{"must provide absolute remote URL", r.URL}
	}

	if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
		return nil, URLError{"remote URL must have http or https scheme", r.URL}
	}

	// query string is always part of the remote URL
	req.URL.RawQuery = r.URL.RawQuery
	return req, nil
}
<|endoftext|>


<commit_before>package response

import (
	"encoding/json"
	"errors"
	"html/template"
	"net/http"
	"os"
	"path/filepath"

	"github.com/volatile/core"
	"github.com/volatile/core/httputil"
	"github.com/volatile/core/log"
)

const viewsDir = "views"

var views *template.Template

func init() {
	if _, err := os.Stat(viewsDir); err != nil {
		return
	}

	views = template.New("views")

	// Built-in views funcs
	views.Funcs(template.FuncMap{
		"html":  viewsFuncHTML,
		"nl2br": viewsFuncNL2BR,
	})

	core.BeforeRun(func() {
		if err := filepath.Walk(viewsDir, viewsWalk); err != nil {
			panic("response: " + err.Error())
		}
	})
}

// walk is the path/filepath.WalkFunc used to walk viewsDir in order to initialize views.
// It will try to parse all files it encounters and recurse into subdirectories.
func viewsWalk(path string, f os.FileInfo, err error) error {
	if err != nil {
		return err
	}

	if f.IsDir() {
		return nil
	}

	_, err = views.ParseFiles(path)

	return err
}

// FuncMap is the type of the map defining the mapping from names to functions.
// Each function must have either a single return value, or two return values of which the second has type error.
// In that case, if the second (error) argument evaluates to non-nil during execution, execution terminates and Execute returns that error.
// FuncMap has the same base type as FuncMap in "text/template", copied here so clients need not import "text/template".
type FuncMap map[string]interface{}

// ViewsFuncs adds a function that will be available to all templates.
func ViewsFuncs(funcMap FuncMap) {
	if views == nil {
		panic(`response: views can't be used without a "views" directory`)
	}
	views.Funcs(template.FuncMap(funcMap))
}

// Status responds with the given status code.
func Status(c *core.Context, v int) {
	http.Error(c.ResponseWriter, http.StatusText(v), v)
}

// String responds with the given string.
func String(c *core.Context, s string) {
	StringStatus(c, s, http.StatusOK)
}

// StringStatus responds with the given string and status code.
func StringStatus(c *core.Context, s string, code int) {
	httputil.SetDetectedContentType(c.ResponseWriter, []byte(s))
	c.ResponseWriter.WriteHeader(code)
	c.ResponseWriter.Write([]byte(s))
}

// Bytes responds with the given slice of byte.
func Bytes(c *core.Context, b []byte) {
	BytesStatus(c, b, http.StatusOK)
}

// BytesStatus responds with the given slice of byte and status code.
func BytesStatus(c *core.Context, b []byte, code int) {
	httputil.SetDetectedContentType(c.ResponseWriter, b)
	c.ResponseWriter.WriteHeader(code)
	c.ResponseWriter.Write(b)
}

// JSON set the correct header and responds with the marshalled content.
func JSON(c *core.Context, v interface{}) {
	JSONStatus(c, v, http.StatusOK)
}

// JSONStatus set the correct header and responds with the marshalled content and status code.
func JSONStatus(c *core.Context, v interface{}, code int) {
	b, err := json.Marshal(v)
	if err != nil {
		log.Stack(err)
		http.Error(c.ResponseWriter, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}

	c.ResponseWriter.Header().Set("Content-Type", "application/json")
	c.ResponseWriter.WriteHeader(code)
	c.ResponseWriter.Write(b)
}

// View pass the data to the template associated to name, and responds with it.
func View(c *core.Context, name string, data map[string]interface{}) {
	if views == nil {
		log.Stack(errors.New(`views can't be used without a "views" directory`))
		http.Error(c.ResponseWriter, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	if data == nil {
		data = make(map[string]interface{})
	}
	data["c"] = c

	c.ResponseWriter.Header().Set("Content-Type", "text/html; charset=utf-8")
	if err := views.ExecuteTemplate(c.ResponseWriter, name, data); err != nil {
		log.Stack(err)
		http.Error(c.ResponseWriter, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}
}
<commit_msg>Panic instead of log.Stack<commit_after>package response

import (
	"encoding/json"
	"html/template"
	"net/http"
	"os"
	"path/filepath"

	"github.com/volatile/core"
	"github.com/volatile/core/httputil"
)

const viewsDir = "views"

var views *template.Template

func init() {
	if _, err := os.Stat(viewsDir); err != nil {
		return
	}

	views = template.New("views")

	// Built-in views funcs
	views.Funcs(template.FuncMap{
		"html":  viewsFuncHTML,
		"nl2br": viewsFuncNL2BR,
	})

	core.BeforeRun(func() {
		if err := filepath.Walk(viewsDir, viewsWalk); err != nil {
			panic("response: " + err.Error())
		}
	})
}

// walk is the path/filepath.WalkFunc used to walk viewsDir in order to initialize views.
// It will try to parse all files it encounters and recurse into subdirectories.
func viewsWalk(path string, f os.FileInfo, err error) error {
	if err != nil {
		return err
	}

	if f.IsDir() {
		return nil
	}

	_, err = views.ParseFiles(path)

	return err
}

// FuncMap is the type of the map defining the mapping from names to functions.
// Each function must have either a single return value, or two return values of which the second has type error.
// In that case, if the second (error) argument evaluates to non-nil during execution, execution terminates and Execute returns that error.
// FuncMap has the same base type as FuncMap in "text/template", copied here so clients need not import "text/template".
type FuncMap map[string]interface{}

// ViewsFuncs adds a function that will be available to all templates.
func ViewsFuncs(funcMap FuncMap) {
	if views == nil {
		panic(`response: views can't be used without a "views" directory`)
	}
	views.Funcs(template.FuncMap(funcMap))
}

// Status responds with the given status code.
func Status(c *core.Context, v int) {
	http.Error(c.ResponseWriter, http.StatusText(v), v)
}

// String responds with the given string.
func String(c *core.Context, s string) {
	StringStatus(c, s, http.StatusOK)
}

// StringStatus responds with the given string and status code.
func StringStatus(c *core.Context, s string, code int) {
	httputil.SetDetectedContentType(c.ResponseWriter, []byte(s))
	c.ResponseWriter.WriteHeader(code)
	c.ResponseWriter.Write([]byte(s))
}

// Bytes responds with the given slice of byte.
func Bytes(c *core.Context, b []byte) {
	BytesStatus(c, b, http.StatusOK)
}

// BytesStatus responds with the given slice of byte and status code.
func BytesStatus(c *core.Context, b []byte, code int) {
	httputil.SetDetectedContentType(c.ResponseWriter, b)
	c.ResponseWriter.WriteHeader(code)
	c.ResponseWriter.Write(b)
}

// JSON set the correct header and responds with the marshalled content.
func JSON(c *core.Context, v interface{}) {
	JSONStatus(c, v, http.StatusOK)
}

// JSONStatus set the correct header and responds with the marshalled content and status code.
func JSONStatus(c *core.Context, v interface{}, code int) {
	b, err := json.Marshal(v)
	if err != nil {
		panic(err)
	}

	c.ResponseWriter.Header().Set("Content-Type", "application/json")
	c.ResponseWriter.WriteHeader(code)
	c.ResponseWriter.Write(b)
}

// View pass the data to the template associated to name, and responds with it.
func View(c *core.Context, name string, data map[string]interface{}) {
	if views == nil {
		panic(`response: views can't be used without a "views" directory`)
	}

	if data == nil {
		data = make(map[string]interface{})
	}
	data["c"] = c

	c.ResponseWriter.Header().Set("Content-Type", "text/html; charset=utf-8")
	if err := views.ExecuteTemplate(c.ResponseWriter, name, data); err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>// Package hiprus provides a Hipchat hook for the logrus loggin package.
package hiprus

import (
	"github.com/Sirupsen/logrus"
	"github.com/andybons/hipchat"
)

const (
	VERISON = "1.0.2"
)

// HiprusHook is a logrus Hook for dispatching messages to the specified
// channel on Hipchat.
type HiprusHook struct {
	// Messages with a log level not contained in this array
	// will not be dispatched. If nil, all messages will be dispatched.
	AcceptedLevels []logrus.Level
	AuthToken      string
	RoomName       string
	// If empty, "Hiprus" will be used.
	Username string
	c        *hipchat.Client
}

func (hh *HiprusHook) Levels() []logrus.Level {
	if hh.AcceptedLevels == nil {
		return AllLevels
	}
	return hh.AcceptedLevels
}

func (hh *HiprusHook) Fire(e *logrus.Entry) error {
	if hh.c == nil {
		if err := hh.initClient(); err != nil {
			return err
		}
	}

	color := ""
	switch e.Level {
	case logrus.DebugLevel:
		color = hipchat.ColorPurple
	case logrus.InfoLevel:
		color = hipchat.ColorGreen
	case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel:
		color = hipchat.ColorRed
	default:
		color = hipchat.ColorYellow
	}

	return hh.c.PostMessage(hipchat.MessageRequest{
		RoomId:        hh.RoomName,
		From:          hh.Username,
		Message:       e.Message,
		MessageFormat: "text",
		Notify:        true,
		Color:         color,
	})
}

func (hh *HiprusHook) initClient() error {
	c := hipchat.NewClient(hh.AuthToken)
	hh.c = &c

	if hh.Username == "" {
		hh.Username = "HipRus"
	}

	return nil
}
<commit_msg>fix: const typo VERISON to VERSION<commit_after>// Package hiprus provides a Hipchat hook for the logrus loggin package.
package hiprus

import (
	"github.com/Sirupsen/logrus"
	"github.com/andybons/hipchat"
)

const (
	VERSION = "1.0.2"
)

// HiprusHook is a logrus Hook for dispatching messages to the specified
// channel on Hipchat.
type HiprusHook struct {
	// Messages with a log level not contained in this array
	// will not be dispatched. If nil, all messages will be dispatched.
	AcceptedLevels []logrus.Level
	AuthToken      string
	RoomName       string
	// If empty, "Hiprus" will be used.
	Username string
	c        *hipchat.Client
}

func (hh *HiprusHook) Levels() []logrus.Level {
	if hh.AcceptedLevels == nil {
		return AllLevels
	}
	return hh.AcceptedLevels
}

func (hh *HiprusHook) Fire(e *logrus.Entry) error {
	if hh.c == nil {
		if err := hh.initClient(); err != nil {
			return err
		}
	}

	color := ""
	switch e.Level {
	case logrus.DebugLevel:
		color = hipchat.ColorPurple
	case logrus.InfoLevel:
		color = hipchat.ColorGreen
	case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel:
		color = hipchat.ColorRed
	default:
		color = hipchat.ColorYellow
	}

	return hh.c.PostMessage(hipchat.MessageRequest{
		RoomId:        hh.RoomName,
		From:          hh.Username,
		Message:       e.Message,
		MessageFormat: "text",
		Notify:        true,
		Color:         color,
	})
}

func (hh *HiprusHook) initClient() error {
	c := hipchat.NewClient(hh.AuthToken)
	hh.c = &c

	if hh.Username == "" {
		hh.Username = "HipRus"
	}

	return nil
}
<|endoftext|>


<commit_before>package gumbleutil

import (
	"github.com/layeh/gumble/gumble"
)

var autoBitrate = &Listener{
	Connect: func(e *gumble.ConnectEvent) {
		if e.MaximumBitrate > 0 {
			dataBytes := e.Client.Config().AudioDataBytes
			if dataBytes <= 0 {
				dataBytes = gumble.AudioDefaultDataBytes
			}
			bitrate := e.MaximumBitrate - (20 + 8 + 4 + ((1 + 5 + 2 + dataBytes) / 100) * 10) * 8 * 100
			e.Client.AudioEncoder().SetBitrate(bitrate)
		}
	},
}

// AutoBitrate is a gumble.EventListener that automatically sets the client's
// maximum outgoing audio bitrate to suitable maximum.
var AutoBitrate gumble.EventListener = autoBitrate
<commit_msg>gumbleutil: increase auto bitrate buffer<commit_after>package gumbleutil

import (
	"github.com/layeh/gumble/gumble"
)

var autoBitrate = &Listener{
	Connect: func(e *gumble.ConnectEvent) {
		if e.MaximumBitrate > 0 {
			dataBytes := e.Client.Config().AudioDataBytes
			if dataBytes <= 0 {
				dataBytes = gumble.AudioDefaultDataBytes
			}
			bitrate := e.MaximumBitrate - (20 + 8 + 4 + ((1 + 5 + 2 + dataBytes) / 100) * 25) * 8 * 100
			e.Client.AudioEncoder().SetBitrate(bitrate)
		}
	},
}

// AutoBitrate is a gumble.EventListener that automatically sets the client's
// maximum outgoing audio bitrate to suitable maximum.
var AutoBitrate gumble.EventListener = autoBitrate
<|endoftext|>


<commit_before>// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package modconv

import (
	"bytes"
	"fmt"
	"internal/testenv"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"

	"cmd/go/internal/cfg"
	"cmd/go/internal/modfetch"
	"cmd/go/internal/modfetch/codehost"
	"cmd/go/internal/modfile"
	"cmd/go/internal/module"
)

func TestMain(m *testing.M) {
	os.Exit(testMain(m))
}

func testMain(m *testing.M) int {
	if _, err := exec.LookPath("git"); err != nil {
		fmt.Fprintln(os.Stderr, "skipping because git binary not found")
		fmt.Println("PASS")
		return 0
	}

	dir, err := ioutil.TempDir("", "modconv-test-")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(dir)
	modfetch.PkgMod = filepath.Join(dir, "pkg/mod")
	codehost.WorkRoot = filepath.Join(dir, "codework")

	return m.Run()
}

func TestConvertLegacyConfig(t *testing.T) {
	testenv.MustHaveExternalNetwork(t)

	if testing.Verbose() {
		old := cfg.BuildX
		defer func() {
			cfg.BuildX = old
		}()
		cfg.BuildX = true
	}

	var tests = []struct {
		path  string
		vers  string
		gomod string
	}{
		{
			// Gopkg.lock parsing.
			"github.com/golang/dep", "v0.4.0",
			`module github.com/golang/dep

			require (
				github.com/Masterminds/semver v0.0.0-20170726230514-a93e51b5a57e
				github.com/Masterminds/vcs v1.11.1
				github.com/armon/go-radix v0.0.0-20160115234725-4239b77079c7
				github.com/boltdb/bolt v1.3.1
				github.com/go-yaml/yaml v0.0.0-20170407172122-cd8b52f8269e
				github.com/golang/protobuf v0.0.0-20170901042739-5afd06f9d81a
				github.com/jmank88/nuts v0.3.0
				github.com/nightlyone/lockfile v0.0.0-20170707060451-e83dc5e7bba0
				github.com/pelletier/go-toml v0.0.0-20171218135716-b8b5e7696574
				github.com/pkg/errors v0.8.0
				github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353
				golang.org/x/net v0.0.0-20170828231752-66aacef3dd8a
				golang.org/x/sync v0.0.0-20170517211232-f52d1811a629
				golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea
			)`,
		},

		// TODO: https://github.com/docker/distribution uses vendor.conf

		{
			// Godeps.json parsing.
			// TODO: Should v2.0.0 work here too?
			"github.com/docker/distribution", "v0.0.0-20150410205453-85de3967aa93",
			`module github.com/docker/distribution

			require (
				github.com/AdRoll/goamz v0.0.0-20150130162828-d3664b76d905
				github.com/MSOpenTech/azure-sdk-for-go v0.0.0-20150323223030-d90753bcad2e
				github.com/Sirupsen/logrus v0.7.3
				github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd
				github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b
				github.com/bugsnag/panicwrap v0.0.0-20141110184334-e5f9854865b9
				github.com/codegangsta/cli v0.0.0-20150131031259-6086d7927ec3
				github.com/docker/docker v0.0.0-20150204013315-165ea5c158cf
				github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1
				github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7
				github.com/gorilla/context v0.0.0-20140604161150-14f550f51af5
				github.com/gorilla/handlers v0.0.0-20140825150757-0e84b7d810c1
				github.com/gorilla/mux v0.0.0-20140926153814-e444e69cbd2e
				github.com/jlhawn/go-crypto v0.0.0-20150401213827-cd738dde20f0
				github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43
				github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50
				github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f
				golang.org/x/net v0.0.0-20150202051010-1dfe7915deaf
				gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789
				gopkg.in/yaml.v2 v2.0.0-20150116202057-bef53efd0c76
			)`,
		},

		{
			// golang.org/issue/24585 - confusion about v2.0.0 tag in legacy non-v2 module
			"github.com/fishy/gcsbucket", "v0.0.0-20150410205453-618d60fe84e0",
			`module github.com/fishy/gcsbucket

			require (
				cloud.google.com/go v0.18.0
				github.com/fishy/fsdb v0.0.0-20180217030800-5527ded01371
				github.com/golang/protobuf v1.0.0
				github.com/googleapis/gax-go v2.0.0+incompatible
				golang.org/x/net v0.0.0-20180216171745-136a25c244d3
				golang.org/x/oauth2 v0.0.0-20180207181906-543e37812f10
				golang.org/x/text v0.0.0-20180208041248-4e4a3210bb54
				google.golang.org/api v0.0.0-20180217000815-c7a403bb5fe1
				google.golang.org/appengine v1.0.0
				google.golang.org/genproto v0.0.0-20180206005123-2b5a72b8730b
				google.golang.org/grpc v1.10.0
			)`,
		},
	}

	for _, tt := range tests {
		t.Run(strings.Replace(tt.path, "/", "_", -1)+"_"+tt.vers, func(t *testing.T) {
			f, err := modfile.Parse("golden", []byte(tt.gomod), nil)
			if err != nil {
				t.Fatal(err)
			}
			want, err := f.Format()
			if err != nil {
				t.Fatal(err)
			}

			dir, err := modfetch.Download(module.Version{Path: tt.path, Version: tt.vers})
			if err != nil {
				t.Fatal(err)
			}

			for name := range Converters {
				file := filepath.Join(dir, name)
				data, err := ioutil.ReadFile(file)
				if err == nil {
					f := new(modfile.File)
					f.AddModuleStmt(tt.path)
					if err := ConvertLegacyConfig(f, filepath.ToSlash(file), data); err != nil {
						t.Fatal(err)
					}
					out, err := f.Format()
					if err != nil {
						t.Fatalf("format after conversion: %v", err)
					}
					if !bytes.Equal(out, want) {
						t.Fatalf("final go.mod:\n%s\n\nwant:\n%s", out, want)
					}
					return
				}
			}
			t.Fatalf("no converter found for %s@%s", tt.path, tt.vers)
		})
	}
}
<commit_msg>cmd/go/internal/modconv: fix TestConvertLegacyConfig expectations<commit_after>// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package modconv

import (
	"bytes"
	"fmt"
	"internal/testenv"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"

	"cmd/go/internal/cfg"
	"cmd/go/internal/modfetch"
	"cmd/go/internal/modfetch/codehost"
	"cmd/go/internal/modfile"
	"cmd/go/internal/module"
)

func TestMain(m *testing.M) {
	os.Exit(testMain(m))
}

func testMain(m *testing.M) int {
	if _, err := exec.LookPath("git"); err != nil {
		fmt.Fprintln(os.Stderr, "skipping because git binary not found")
		fmt.Println("PASS")
		return 0
	}

	dir, err := ioutil.TempDir("", "modconv-test-")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(dir)
	modfetch.PkgMod = filepath.Join(dir, "pkg/mod")
	codehost.WorkRoot = filepath.Join(dir, "codework")

	return m.Run()
}

func TestConvertLegacyConfig(t *testing.T) {
	testenv.MustHaveExternalNetwork(t)

	if testing.Verbose() {
		old := cfg.BuildX
		defer func() {
			cfg.BuildX = old
		}()
		cfg.BuildX = true
	}

	var tests = []struct {
		path  string
		vers  string
		gomod string
	}{
		/*
			Different versions of git seem to find or not find
			github.com/Masterminds/semver's a93e51b5a57e,
			which is an unmerged pull request.
			We'd rather not provide access to unmerged pull requests,
			so the line is removed from the golden file here,
			but some git commands still find it somehow.

			{
				// Gopkg.lock parsing.
				"github.com/golang/dep", "v0.4.0",
				`module github.com/golang/dep

				require (
					github.com/Masterminds/vcs v1.11.1
					github.com/armon/go-radix v0.0.0-20160115234725-4239b77079c7
					github.com/boltdb/bolt v1.3.1
					github.com/go-yaml/yaml v0.0.0-20170407172122-cd8b52f8269e
					github.com/golang/protobuf v0.0.0-20170901042739-5afd06f9d81a
					github.com/jmank88/nuts v0.3.0
					github.com/nightlyone/lockfile v0.0.0-20170707060451-e83dc5e7bba0
					github.com/pelletier/go-toml v0.0.0-20171218135716-b8b5e7696574
					github.com/pkg/errors v0.8.0
					github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353
					golang.org/x/net v0.0.0-20170828231752-66aacef3dd8a
					golang.org/x/sync v0.0.0-20170517211232-f52d1811a629
					golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea
				)`,
			},
		*/

		// TODO: https://github.com/docker/distribution uses vendor.conf

		{
			// Godeps.json parsing.
			// TODO: Should v2.0.0 work here too?
			"github.com/docker/distribution", "v0.0.0-20150410205453-85de3967aa93",
			`module github.com/docker/distribution

			require (
				github.com/AdRoll/goamz v0.0.0-20150130162828-d3664b76d905
				github.com/MSOpenTech/azure-sdk-for-go v0.0.0-20150323223030-d90753bcad2e
				github.com/Sirupsen/logrus v0.7.3
				github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd
				github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b
				github.com/bugsnag/panicwrap v0.0.0-20141110184334-e5f9854865b9
				github.com/codegangsta/cli v0.0.0-20150131031259-6086d7927ec3
				github.com/docker/docker v0.0.0-20150204013315-165ea5c158cf
				github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1
				github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7
				github.com/gorilla/context v0.0.0-20140604161150-14f550f51af5
				github.com/gorilla/handlers v0.0.0-20140825150757-0e84b7d810c1
				github.com/gorilla/mux v0.0.0-20140926153814-e444e69cbd2e
				github.com/jlhawn/go-crypto v0.0.0-20150401213827-cd738dde20f0
				github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43
				github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50
				github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f
				golang.org/x/net v0.0.0-20150202051010-1dfe7915deaf
				gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789
				gopkg.in/yaml.v2 v2.0.0-20150116202057-bef53efd0c76
			)`,
		},

		{
			// golang.org/issue/24585 - confusion about v2.0.0 tag in legacy non-v2 module
			"github.com/fishy/gcsbucket", "v0.0.0-20150410205453-618d60fe84e0",
			`module github.com/fishy/gcsbucket

			require (
				cloud.google.com/go v0.18.0
				github.com/fishy/fsdb v0.0.0-20180217030800-5527ded01371
				github.com/golang/protobuf v1.0.0
				github.com/googleapis/gax-go v2.0.0+incompatible
				golang.org/x/net v0.0.0-20180216171745-136a25c244d3
				golang.org/x/oauth2 v0.0.0-20180207181906-543e37812f10
				golang.org/x/text v0.0.0-20180208041248-4e4a3210bb54
				google.golang.org/api v0.0.0-20180217000815-c7a403bb5fe1
				google.golang.org/appengine v1.0.0
				google.golang.org/genproto v0.0.0-20180206005123-2b5a72b8730b
				google.golang.org/grpc v1.10.0
			)`,
		},
	}

	for _, tt := range tests {
		t.Run(strings.Replace(tt.path, "/", "_", -1)+"_"+tt.vers, func(t *testing.T) {
			f, err := modfile.Parse("golden", []byte(tt.gomod), nil)
			if err != nil {
				t.Fatal(err)
			}
			want, err := f.Format()
			if err != nil {
				t.Fatal(err)
			}

			dir, err := modfetch.Download(module.Version{Path: tt.path, Version: tt.vers})
			if err != nil {
				t.Fatal(err)
			}

			for name := range Converters {
				file := filepath.Join(dir, name)
				data, err := ioutil.ReadFile(file)
				if err == nil {
					f := new(modfile.File)
					f.AddModuleStmt(tt.path)
					if err := ConvertLegacyConfig(f, filepath.ToSlash(file), data); err != nil {
						t.Fatal(err)
					}
					out, err := f.Format()
					if err != nil {
						t.Fatalf("format after conversion: %v", err)
					}
					if !bytes.Equal(out, want) {
						t.Fatalf("final go.mod:\n%s\n\nwant:\n%s", out, want)
					}
					return
				}
			}
			t.Fatalf("no converter found for %s@%s", tt.path, tt.vers)
		})
	}
}
<|endoftext|>


<commit_before>package the_platinum_searcher

import (
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/monochromegane/go-gitignore"
	"github.com/monochromegane/go-home"
)

type ignoreMatchers []gitignore.IgnoreMatcher

func (im ignoreMatchers) Match(path string, isDir bool) bool {
	for _, ig := range im {
		if ig == nil {
			return false
		}
		if ig.Match(path, isDir) {
			return true
		}
	}
	return false
}

func newIgnoreMatchers(path string, ignores []string) ignoreMatchers {
	var matchers ignoreMatchers
	for _, i := range ignores {
		if matcher, err := gitignore.NewGitIgnore(filepath.Join(path, i)); err == nil {
			matchers = append(matchers, matcher)
		}
	}
	return matchers
}

func globalGitIgnore(base string) gitignore.IgnoreMatcher {
	if homeDir := home.Dir(); homeDir != "" {
		globalIgnore := globalGitIgnoreName()
		if globalIgnore != "" {
			if matcher, err := gitignore.NewGitIgnore(filepath.Join(homeDir, globalIgnore), base); err == nil {
				return matcher
			}
		}
	}
	return nil
}

func globalGitIgnoreName() string {
	gitCmd, err := exec.LookPath("git")
	if err != nil {
		return ""
	}

	file, err := exec.Command(gitCmd, "config", "--get", "core.excludesfile").Output()
	if err != nil {
		return ""
	}
	return strings.TrimSpace(filepath.Base(string(file)))
}

func homePtIgnore(base string) gitignore.IgnoreMatcher {
	if homeDir := home.Dir(); homeDir != "" {
		if matcher, err := gitignore.NewGitIgnore(filepath.Join(homeDir, ".ptignore"), base); err == nil {
			return matcher
		}
	}
	return nil
}
<commit_msg>Specify base for avoid unnecessary filepath.Dir in gitignore package.<commit_after>package the_platinum_searcher

import (
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/monochromegane/go-gitignore"
	"github.com/monochromegane/go-home"
)

type ignoreMatchers []gitignore.IgnoreMatcher

func (im ignoreMatchers) Match(path string, isDir bool) bool {
	for _, ig := range im {
		if ig == nil {
			return false
		}
		if ig.Match(path, isDir) {
			return true
		}
	}
	return false
}

func newIgnoreMatchers(path string, ignores []string) ignoreMatchers {
	var matchers ignoreMatchers
	for _, i := range ignores {
		if matcher, err := gitignore.NewGitIgnore(filepath.Join(path, i), path); err == nil {
			matchers = append(matchers, matcher)
		}
	}
	return matchers
}

func globalGitIgnore(base string) gitignore.IgnoreMatcher {
	if homeDir := home.Dir(); homeDir != "" {
		globalIgnore := globalGitIgnoreName()
		if globalIgnore != "" {
			if matcher, err := gitignore.NewGitIgnore(filepath.Join(homeDir, globalIgnore), base); err == nil {
				return matcher
			}
		}
	}
	return nil
}

func globalGitIgnoreName() string {
	gitCmd, err := exec.LookPath("git")
	if err != nil {
		return ""
	}

	file, err := exec.Command(gitCmd, "config", "--get", "core.excludesfile").Output()
	if err != nil {
		return ""
	}
	return strings.TrimSpace(filepath.Base(string(file)))
}

func homePtIgnore(base string) gitignore.IgnoreMatcher {
	if homeDir := home.Dir(); homeDir != "" {
		if matcher, err := gitignore.NewGitIgnore(filepath.Join(homeDir, ".ptignore"), base); err == nil {
			return matcher
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/fsouza/go-dockerclient"

	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

type Image struct {
	Id          string
	ParentId    string   `json:",omitempty"`
	RepoTags    []string `json:",omitempty"`
	VirtualSize int64
	Size        int64
	Created     int64
}

type ImagesCommand struct {
	Dot         bool `short:"d" long:"dot" description:"Show image information as Graphviz dot."`
	Tree        bool `short:"t" long:"tree" description:"Show image information as tree."`
	Short       bool `short:"s" long:"short" description:"Show short summary of images (repo name and list of tags)."`
	NoTruncate  bool `short:"n" long:"no-trunc" description:"Don't truncate the image IDs."`
	OnlyLabeled bool `short:"l" long:"only-labeled" description:"Print only labeled images/containers."`
}

var imagesCommand ImagesCommand

func (x *ImagesCommand) Execute(args []string) error {

	var images *[]Image

	stat, err := os.Stdin.Stat()
	if err != nil {
		return fmt.Errorf("error reading stdin stat", err)
	}

	if (stat.Mode() & os.ModeCharDevice) == 0 {
		// read in stdin
		stdin, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			return fmt.Errorf("error reading all input", err)
		}

		images, err = parseImagesJSON(stdin)
		if err != nil {
			return err
		}

	} else {

		client, err := connect()
		if err != nil {
			return err
		}

		clientImages, err := client.ListImages(docker.ListImagesOptions{All: true})
		if err != nil {
			if in_docker := os.Getenv("IN_DOCKER"); len(in_docker) > 0 {
				return fmt.Errorf("Unable to access Docker socket, please run like this:\n  docker run --rm -v /var/run/docker.sock:/var/run/docker.sock nate/dockviz images <args>\nFor more help, run 'dockviz help'")
			} else {
				return fmt.Errorf("Unable to connect: %s\nFor help, run 'dockviz help'", err)
			}
		}

		var ims []Image
		for _, image := range clientImages {
			// fmt.Println(image)
			ims = append(ims, Image{
				image.ID,
				image.ParentID,
				image.RepoTags,
				image.VirtualSize,
				image.Size,
				image.Created,
			})
		}

		images = &ims
	}

	if imagesCommand.Dot {
		fmt.Printf(jsonToDot(images))
	} else if imagesCommand.Tree {

		var startImage = ""
		if len(args) > 0 {

			// attempt to find the start image, which can be specified as an
			// image ID or a repository name
			startImageArg := args[0]
			startImageRepo := args[0]

			// if tag is not defined, find by :latest tag
			if strings.Index(startImageRepo, ":") == -1 {
				startImageRepo = fmt.Sprintf("%s:latest", startImageRepo)
			}

		IMAGES:
			for _, image := range *images {
				// find by image id
				if strings.Index(image.Id, startImageArg) == 0 {
					startImage = image.Id
					break IMAGES
				}

				// find by image name (name and tag)
				for _, repotag := range image.RepoTags {
					if repotag == startImageRepo {
						startImage = image.Id
						break IMAGES
					}
				}
			}

			if startImage == "" {
				return fmt.Errorf("Unable to find image %s = %s.", startImageArg, startImageRepo)
			}
		}

		fmt.Printf(jsonToTree(images, startImage, imagesCommand.NoTruncate, imagesCommand.OnlyLabeled))
	} else if imagesCommand.Short {
		fmt.Printf(jsonToShort(images))
	} else {
		return fmt.Errorf("Please specify either --dot, --tree, or --short")
	}

	return nil
}

func jsonToTree(images *[]Image, startImageArg string, noTrunc bool, onlyLabeled bool) string {
	var buffer bytes.Buffer

	var startImage Image

	var roots []Image
	var byParent = make(map[string][]Image)
	for _, image := range *images {
		if image.ParentId == "" {
			roots = append(roots, image)
		} else {
			if children, exists := byParent[image.ParentId]; exists {
				byParent[image.ParentId] = append(children, image)
			} else {
				byParent[image.ParentId] = []Image{image}
			}
		}

		if startImageArg != "" {
			if startImageArg == image.Id || startImageArg == truncate(image.Id) {
				startImage = image
			}

			for _, repotag := range image.RepoTags {
				if repotag == startImageArg {
					startImage = image
				}
			}
		}
	}

	if startImageArg != "" {
		WalkTree(&buffer, noTrunc, onlyLabeled, []Image{startImage}, byParent, "")
	} else {
		WalkTree(&buffer, noTrunc, onlyLabeled, roots, byParent, "")
	}

	return buffer.String()
}

func WalkTree(buffer *bytes.Buffer, noTrunc bool, onlyLabeled bool, images []Image, byParent map[string][]Image, prefix string) {
	var length = len(images)
	if length > 1 {
		for index, image := range images {
			var nextPrefix string = ""
			// image is visible
			//   1. it has a label
			//   2. it is root
			//   3. it is a node
			var visible bool = onlyLabeled && image.RepoTags[0] != "<none>:<none>" || !onlyLabeled || image.ParentId == "" || len(byParent[image.Id]) > 1
			if visible {
				if index+1 == length {
					PrintTreeNode(buffer, noTrunc, image, prefix+"└─")
					nextPrefix = "  "
				} else {
					PrintTreeNode(buffer, noTrunc, image, prefix+"├─")
					nextPrefix = "│ "
				}
			}
			if subimages, exists := byParent[image.Id]; exists {
				WalkTree(buffer, noTrunc, onlyLabeled, subimages, byParent, prefix+nextPrefix)
			}
		}
	} else {
		for _, image := range images {
			var nextPrefix string = ""
			// image is visible
			//   1. it has a label
			//   2. it is root
			//   3. it is a node
			var visible bool = onlyLabeled && image.RepoTags[0] != "<none>:<none>" || !onlyLabeled || image.ParentId == "" || len(byParent[image.Id]) > 1
			if visible {
				PrintTreeNode(buffer, noTrunc, image, prefix+"└─")
				nextPrefix = "  "
			}
			if subimages, exists := byParent[image.Id]; exists {
				WalkTree(buffer, noTrunc, onlyLabeled, subimages, byParent, prefix+nextPrefix)
			}
		}
	}
}

func PrintTreeNode(buffer *bytes.Buffer, noTrunc bool, image Image, prefix string) {
	var imageID string
	if noTrunc {
		imageID = image.Id
	} else {
		imageID = truncate(image.Id)
	}

	buffer.WriteString(fmt.Sprintf("%s%s Virtual Size: %s", prefix, imageID, humanSize(image.VirtualSize)))
	if image.RepoTags[0] != "<none>:<none>" {
		buffer.WriteString(fmt.Sprintf(" Tags: %s\n", strings.Join(image.RepoTags, ", ")))
	} else {
		buffer.WriteString(fmt.Sprintf("\n"))
	}
}

func humanSize(raw int64) string {
	sizes := []string{"B", "KB", "MB", "GB", "TB"}

	rawFloat := float64(raw)
	ind := 0

	for {
		if rawFloat < 1000 {
			break
		} else {
			rawFloat = rawFloat / 1000
			ind = ind + 1
		}
	}

	return fmt.Sprintf("%.01f %s", rawFloat, sizes[ind])
}

func truncate(id string) string {
	return id[0:12]
}

func parseImagesJSON(rawJSON []byte) (*[]Image, error) {

	var images []Image
	err := json.Unmarshal(rawJSON, &images)

	if err != nil {
		return nil, fmt.Errorf("Error reading JSON: ", err)
	}

	return &images, nil
}

func jsonToDot(images *[]Image) string {

	var buffer bytes.Buffer
	buffer.WriteString("digraph docker {\n")

	for _, image := range *images {
		if image.ParentId == "" {
			buffer.WriteString(fmt.Sprintf(" base -> \"%s\" [style=invis]\n", truncate(image.Id)))
		} else {
			buffer.WriteString(fmt.Sprintf(" \"%s\" -> \"%s\"\n", truncate(image.ParentId), truncate(image.Id)))
		}
		if image.RepoTags[0] != "<none>:<none>" {
			buffer.WriteString(fmt.Sprintf(" \"%s\" [label=\"%s\\n%s\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n", truncate(image.Id), truncate(image.Id), strings.Join(image.RepoTags, "\\n")))
		}
	}

	buffer.WriteString(" base [style=invisible]\n}\n")

	return buffer.String()
}

func jsonToShort(images *[]Image) string {
	var buffer bytes.Buffer

	var byRepo = make(map[string][]string)

	for _, image := range *images {
		for _, repotag := range image.RepoTags {
			if repotag != "<none>:<none>" {

				// parse the repo name and tag name out
				// tag is after the last colon
				lastColonIndex := strings.LastIndex(repotag, ":")
				tagname := repotag[lastColonIndex+1:]
				reponame := repotag[0:lastColonIndex]

				if tags, exists := byRepo[reponame]; exists {
					byRepo[reponame] = append(tags, tagname)
				} else {
					byRepo[reponame] = []string{tagname}
				}
			}
		}
	}

	for repo, tags := range byRepo {
		buffer.WriteString(fmt.Sprintf("%s: %s\n", repo, strings.Join(tags, ", ")))
	}

	return buffer.String()
}

func init() {
	parser.AddCommand("images",
		"Visualize docker images.",
		"",
		&imagesCommand)
}
<commit_msg>--only-labeled for dot<commit_after>package main

import (
	"github.com/fsouza/go-dockerclient"

	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

type Image struct {
	Id          string
	ParentId    string   `json:",omitempty"`
	RepoTags    []string `json:",omitempty"`
	VirtualSize int64
	Size        int64
	Created     int64
}

type ImagesCommand struct {
	Dot         bool `short:"d" long:"dot" description:"Show image information as Graphviz dot."`
	Tree        bool `short:"t" long:"tree" description:"Show image information as tree."`
	Short       bool `short:"s" long:"short" description:"Show short summary of images (repo name and list of tags)."`
	NoTruncate  bool `short:"n" long:"no-trunc" description:"Don't truncate the image IDs."`
	OnlyLabeled bool `short:"l" long:"only-labeled" description:"Print only labeled images/containers."`
}

var imagesCommand ImagesCommand

func (x *ImagesCommand) Execute(args []string) error {
	var images *[]Image

	stat, err := os.Stdin.Stat()
	if err != nil {
		return fmt.Errorf("error reading stdin stat", err)
	}

	if (stat.Mode() & os.ModeCharDevice) == 0 {
		// read in stdin
		stdin, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			return fmt.Errorf("error reading all input", err)
		}

		images, err = parseImagesJSON(stdin)
		if err != nil {
			return err
		}

	} else {

		client, err := connect()
		if err != nil {
			return err
		}

		clientImages, err := client.ListImages(docker.ListImagesOptions{All: true})
		if err != nil {
			if in_docker := os.Getenv("IN_DOCKER"); len(in_docker) > 0 {
				return fmt.Errorf("Unable to access Docker socket, please run like this:\n  docker run --rm -v /var/run/docker.sock:/var/run/docker.sock nate/dockviz images <args>\nFor more help, run 'dockviz help'")
			} else {
				return fmt.Errorf("Unable to connect: %s\nFor help, run 'dockviz help'", err)
			}
		}

		var ims []Image
		for _, image := range clientImages {
			// fmt.Println(image)
			ims = append(ims, Image{
				image.ID,
				image.ParentID,
				image.RepoTags,
				image.VirtualSize,
				image.Size,
				image.Created,
			})
		}

		images = &ims
	}

	 if imagesCommand.Tree || imagesCommand.Dot {
		var startImage *Image
		if len(args) > 0 {

			// attempt to find the start image, which can be specified as an
			// image ID or a repository name
			startImageArg := args[0]
			startImageRepo := args[0]

			// if tag is not defined, find by :latest tag
			if strings.Index(startImageRepo, ":") == -1 {
				startImageRepo = fmt.Sprintf("%s:latest", startImageRepo)
			}

		IMAGES:
			for _, image := range *images {
				// find by image id
				if strings.Index(image.Id, startImageArg) == 0 {
					startImage = &image
					break IMAGES
				}

				// find by image name (name and tag)
				for _, repotag := range image.RepoTags {
					if repotag == startImageRepo {
						startImage = &image
						break IMAGES
					}
				}
			}

			if startImage == nil {
				return fmt.Errorf("Unable to find image %s = %s.", startImageArg, startImageRepo)
			}
		}

		// select the start image of the tree
		var roots []Image
		if startImage == nil {
			roots = collectRoots(images)
		} else {
			roots = []Image{*startImage}
		}

		// build helper map (image -> children)
		var imagesByParent = make(map[string][]Image)
		imagesByParent = collectChildren(images);

		// image ids truncate
		// initialize image informations

		// filter images
		if imagesCommand.OnlyLabeled{
			*images, imagesByParent = filterImages(images, &imagesByParent)
		}

		var buffer bytes.Buffer

		if imagesCommand.Tree {
			buffer.WriteString(fmt.Sprintf("Images: %d\n", len(imagesByParent)))
			jsonToText(&buffer, imagesCommand.NoTruncate, imagesCommand.OnlyLabeled, roots, imagesByParent, "")
		}
		if imagesCommand.Dot {
			imagesToDot(&buffer, images)
		}

		fmt.Print(buffer.String())
	} else if imagesCommand.Short {
		fmt.Printf(jsonToShort(images))
	} else {
		return fmt.Errorf("Please specify either --dot, --tree, or --short")
	}

	return nil
}

func collectChildren(images *[]Image) map[string][]Image {
	var imagesByParent = make(map[string][]Image)
	for _, image := range *images {
		if children, exists := imagesByParent[image.ParentId]; exists {
			imagesByParent[image.ParentId] = append(children, image)
		} else {
			imagesByParent[image.ParentId] = []Image{image}
		}
	}

	return imagesByParent
}

func collectRoots(images *[]Image) []Image {
	var roots []Image
	for _, image := range *images {
		if image.ParentId == "" {
			roots = append(roots, image)
		}
	}

	return roots
}

func filterImages (images *[]Image, byParent *map[string][]Image) (filteredImages []Image, filteredChildren map[string][]Image) {
	for i := 0; i<len(*images); i++ {
		// image is visible
		//   1. it has a label
		//   2. it is root
		//   3. it is a node
		var visible bool = (*images)[i].RepoTags[0] != "<none>:<none>" || (*images)[i].ParentId == "" || len((*byParent)[(*images)[i].Id]) > 1
		if visible {
			filteredImages = append(filteredImages, (*images)[i])
		} else {
			// change childs parent id
			// if items are filtered with only one child
			for j := 0; j<len(filteredImages); j++ {
				if filteredImages[j].ParentId == (*images)[i].Id {
					filteredImages[j].ParentId = (*images)[i].ParentId
				}
			}
			for j := 0; j<len(*images); j++ {
				if (*images)[j].ParentId == (*images)[i].Id {
					(*images)[j].ParentId = (*images)[i].ParentId
				}
			}
		}
	}

	filteredChildren = collectChildren(&filteredImages)

	return filteredImages, filteredChildren
}

func jsonToText(buffer *bytes.Buffer, noTrunc bool, onlyLabeled bool, images []Image, byParent map[string][]Image, prefix string) {
	var length = len(images)
	if length > 1 {
		for index, image := range images {
			var nextPrefix string = ""
			if index+1 == length {
				PrintTreeNode(buffer, noTrunc, image, prefix+"└─")
				nextPrefix = "  "
			} else {
				PrintTreeNode(buffer, noTrunc, image, prefix+"├─")
				nextPrefix = "│ "
			}
			if subimages, exists := byParent[image.Id]; exists {
				jsonToText(buffer, noTrunc, onlyLabeled, subimages, byParent, prefix+nextPrefix)
			}
		}
	} else {
		for _, image := range images {
			PrintTreeNode(buffer, noTrunc, image, prefix+"└─")
			if subimages, exists := byParent[image.Id]; exists {
				jsonToText(buffer, noTrunc, onlyLabeled, subimages, byParent, prefix+"  ")
			}
		}
	}
}

func PrintTreeNode(buffer *bytes.Buffer, noTrunc bool, image Image, prefix string) {
	var imageID string
	if noTrunc {
		imageID = image.Id
	} else {
		imageID = truncate(image.Id)
	}

	buffer.WriteString(fmt.Sprintf("%s%s Virtual Size: %s", prefix, imageID, humanSize(image.VirtualSize)))
	if image.RepoTags[0] != "<none>:<none>" {
		buffer.WriteString(fmt.Sprintf(" Tags: %s\n", strings.Join(image.RepoTags, ", ")))
	} else {
		buffer.WriteString(fmt.Sprintf("\n"))
	}
}

func humanSize(raw int64) string {
	sizes := []string{"B", "KB", "MB", "GB", "TB"}

	rawFloat := float64(raw)
	ind := 0

	for {
		if rawFloat < 1000 {
			break
		} else {
			rawFloat = rawFloat / 1000
			ind = ind + 1
		}
	}

	return fmt.Sprintf("%.01f %s", rawFloat, sizes[ind])
}

func truncate(id string) string {
	return id[0:12]
}

func parseImagesJSON(rawJSON []byte) (*[]Image, error) {

	var images []Image
	err := json.Unmarshal(rawJSON, &images)

	if err != nil {
		return nil, fmt.Errorf("Error reading JSON: ", err)
	}

	return &images, nil
}

func imagesToDot(buffer *bytes.Buffer, images *[]Image) string {
	buffer.WriteString("digraph docker {\n")

	for _, image := range *images {
		if image.ParentId == "" {
			buffer.WriteString(fmt.Sprintf(" base -> \"%s\" [style=invis]\n", truncate(image.Id)))
		} else {
			buffer.WriteString(fmt.Sprintf(" \"%s\" -> \"%s\"\n", truncate(image.ParentId), truncate(image.Id)))
		}
		if image.RepoTags[0] != "<none>:<none>" {
			buffer.WriteString(fmt.Sprintf(" \"%s\" [label=\"%s\\n%s\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n", truncate(image.Id), truncate(image.Id), strings.Join(image.RepoTags, "\\n")))
		}
	}

	buffer.WriteString(" base [style=invisible]\n}\n")

	return buffer.String()
}

func jsonToShort(images *[]Image) string {
	var buffer bytes.Buffer

	var byRepo = make(map[string][]string)

	for _, image := range *images {
		for _, repotag := range image.RepoTags {
			if repotag != "<none>:<none>" {

				// parse the repo name and tag name out
				// tag is after the last colon
				lastColonIndex := strings.LastIndex(repotag, ":")
				tagname := repotag[lastColonIndex+1:]
				reponame := repotag[0:lastColonIndex]

				if tags, exists := byRepo[reponame]; exists {
					byRepo[reponame] = append(tags, tagname)
				} else {
					byRepo[reponame] = []string{tagname}
				}
			}
		}
	}

	for repo, tags := range byRepo {
		buffer.WriteString(fmt.Sprintf("%s: %s\n", repo, strings.Join(tags, ", ")))
	}

	return buffer.String()
}

func init() {
	parser.AddCommand("images",
		"Visualize docker images.",
		"",
		&imagesCommand)
}
<|endoftext|>


<commit_before>// Package httpgzip is a simple wrapper around http.FileServer that looks for
// a gzip compressed version of a file and serves that if the client requested
// gzip content
package httpgzip

import (
	"mime"
	"net/http"
	"path"
	"path/filepath"
	"strconv"
	"strings"
)

type fileServer struct {
	root http.FileSystem
	h    http.Handler
}

// FileServer creates a wrapper around http.FileServer using the given
// http.FileSystem
func FileServer(root http.FileSystem) http.Handler {
	return fileServer{
		root,
		http.FileServer(root),
	}
}

// ServerHTTP implements the http.Handler interface
func (f fileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	for _, e := range strings.Split(r.Header.Get("Accept-Encoding"), ",") {
		if strings.TrimSpace(e) == "gzip" {
			if nf, err := f.root.Open(path.Clean(r.URL.Path + ".gz")); err == nil {
				w.Header().Set("Content-Encoding", "gzip")
				if ctype := mime.TypeByExtension(filepath.Ext(r.URL.Path)); ctype != "" {
					w.Header().Set("Content-Type", ctype)
					s, _ := nf.Stat()
					w.Header().Set("Content-Length", strconv.FormatInt(s.Size(), 10))
				}
				r.URL.Path += ".gz"
			}
			break
		}
	}
	f.h.ServeHTTP(w, r)
}
<commit_msg>will now check for a gzipped index.html (index.html.gz) for a directory instead of just /.gz<commit_after>// Package httpgzip is a simple wrapper around http.FileServer that looks for
// a gzip compressed version of a file and serves that if the client requested
// gzip content
package httpgzip

import (
	"mime"
	"net/http"
	"path"
	"path/filepath"
	"strconv"
	"strings"
)

type fileServer struct {
	root http.FileSystem
	h    http.Handler
}

// FileServer creates a wrapper around http.FileServer using the given
// http.FileSystem
func FileServer(root http.FileSystem) http.Handler {
	return fileServer{
		root,
		http.FileServer(root),
	}
}

// ServerHTTP implements the http.Handler interface
func (f fileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	for _, e := range strings.Split(r.Header.Get("Accept-Encoding"), ",") {
		if strings.TrimSpace(e) == "gzip" {
			p := path.Clean(r.URL.Path)
			nf, err := f.root.Open(p + ".gz")
			if err != nil && strings.HasSuffix(p, "/") {
				p += "index.html"
				nf, err = f.root.Open(p + ".gz")
			}
			if err == nil {
				w.Header().Set("Content-Encoding", "gzip")
				if ctype := mime.TypeByExtension(filepath.Ext(r.URL.Path)); ctype != "" {
					w.Header().Set("Content-Type", ctype)
					s, _ := nf.Stat()
					w.Header().Set("Content-Length", strconv.FormatInt(s.Size(), 10))
				}
				r.URL.Path = p + ".gz"
			}
			break
		}
	}
	f.h.ServeHTTP(w, r)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"context"
	"crypto/rand"
	"flag"
	"fmt"
	"time"

	"log"

	gorpc "github.com/hsanjuan/go-libp2p-gorpc"
	libp2p "github.com/libp2p/go-libp2p"
	host "github.com/libp2p/go-libp2p-host"
	peerstore "github.com/libp2p/go-libp2p-peerstore"
	protocol "github.com/libp2p/go-libp2p-protocol"
	multiaddr "github.com/multiformats/go-multiaddr"
)

type PingArgs struct {
	Data []byte
}
type PingReply struct {
	Data []byte
}
type PingService struct{}

func (t *PingService) Ping(argType PingArgs, replyType *PingReply) error {
	log.Println("Received a Ping call")
	replyType.Data = argType.Data
	return nil
}

func createPeer(listenAddr string) host.Host {
	ctx := context.Background()

	// Create a new libp2p host
	h, err := libp2p.New(ctx, libp2p.ListenAddrStrings(listenAddr))
	if err != nil {
		panic(err)
	}
	return h
}

var protocolID = protocol.ID("/p2p/rpc/ping")

func startServer() {
	log.Println("Launching host")
	host := createPeer("/ip4/0.0.0.0/tcp/9000")

	log.Printf("Hello World, my hosts ID is %s\n", host.ID().Pretty())
	for _, addr := range host.Addrs() {
		ipfsAddr, err := multiaddr.NewMultiaddr("/ipfs/" + host.ID().Pretty())
		if err != nil {
			panic(err)
		}
		peerAddr := addr.Encapsulate(ipfsAddr)
		log.Printf("I'm listening on %s\n", peerAddr)
	}

	rpcHost := gorpc.NewServer(host, protocolID)

	svc := PingService{}
	err := rpcHost.Register(&svc)
	if err != nil {
		panic(err)
	}

	fmt.Println("Done")

	for {
		time.Sleep(time.Second * 1)
	}
}

func startClient(host string, pingCount, randomDataSize int) {
	fmt.Println("Launching client")
	client := createPeer("/ip4/0.0.0.0/tcp/9001")
	fmt.Printf("Hello World, my hosts ID is %s\n", client.ID().Pretty())
	ma, err := multiaddr.NewMultiaddr(host)
	if err != nil {
		panic(err)
	}
	peerInfo, err := peerstore.InfoFromP2pAddr(ma)
	if err != nil {
		panic(err)
	}
	ctx := context.Background()
	err = client.Connect(ctx, *peerInfo)
	if err != nil {
		panic(err)
	}
	rpcClient := gorpc.NewClient(client, protocolID)
	numCalls := 0
	durations := []time.Duration{}
	betweenPingsSleep := time.Second * 1

	for numCalls < pingCount {
		var reply PingReply
		var args PingArgs

		c := randomDataSize
		b := make([]byte, c)
		_, err := rand.Read(b)
		if err != nil {
			panic(err)
		}

		args.Data = b

		time.Sleep(betweenPingsSleep)
		startTime := time.Now()
		err = rpcClient.Call(peerInfo.ID, "PingService", "Ping", args, &reply)
		if err != nil {
			panic(err)
		}
		if !bytes.Equal(reply.Data, b) {
			panic("Received wrong amount of bytes back!")
		}
		endTime := time.Now()
		diff := endTime.Sub(startTime)
		fmt.Printf("%d bytes from %s (%s): seq=%d time=%s\n", c, peerInfo.ID.String(), peerInfo.Addrs[0].String(), numCalls+1, diff)
		numCalls += 1
		durations = append(durations, diff)
	}

	totalDuration := int64(0)
	for _, dur := range durations {
		totalDuration = totalDuration + dur.Nanoseconds()
	}
	averageDuration := totalDuration / int64(len(durations))
	fmt.Printf("Average duration for ping reply: %s\n", time.Duration(averageDuration))

}

func main() {

	var mode string
	var host string
	var count int
	var size int
	flag.StringVar(&mode, "mode", "", "host or client mode")
	flag.StringVar(&host, "host", "", "address of host to connect to")
	flag.IntVar(&count, "count", 10, "number of pings to make")
	flag.IntVar(&size, "size", 64, "size of random data in ping message")
	flag.Parse()

	if mode == "" {
		log.Fatal("You need to specify '-mode' to be either 'host' or 'client'")
	}

	if mode == "host" {
		startServer()
		return
	}
	if mode == "client" {
		if host == "" {
			log.Fatal("You need to specify '-host' when running as a client")
		}
		startClient(host, count, size)
		return
	}
	log.Fatal("Mode '" + mode + "' not recognized. It has to be either 'host' or 'client'")

}
<commit_msg>fix: ping example to use context<commit_after>package main

import (
	"bytes"
	"context"
	"crypto/rand"
	"flag"
	"fmt"
	"time"

	"log"

	gorpc "github.com/hsanjuan/go-libp2p-gorpc"
	libp2p "github.com/libp2p/go-libp2p"
	host "github.com/libp2p/go-libp2p-host"
	peerstore "github.com/libp2p/go-libp2p-peerstore"
	protocol "github.com/libp2p/go-libp2p-protocol"
	multiaddr "github.com/multiformats/go-multiaddr"
)

type PingArgs struct {
	Data []byte
}
type PingReply struct {
	Data []byte
}
type PingService struct{}

func (t *PingService) Ping(ctx context.Context, argType PingArgs, replyType *PingReply) error {
	log.Println("Received a Ping call")
	replyType.Data = argType.Data
	return nil
}

func createPeer(listenAddr string) host.Host {
	ctx := context.Background()

	// Create a new libp2p host
	h, err := libp2p.New(ctx, libp2p.ListenAddrStrings(listenAddr))
	if err != nil {
		panic(err)
	}
	return h
}

var protocolID = protocol.ID("/p2p/rpc/ping")

func startServer() {
	log.Println("Launching host")
	host := createPeer("/ip4/0.0.0.0/tcp/9000")

	log.Printf("Hello World, my hosts ID is %s\n", host.ID().Pretty())
	for _, addr := range host.Addrs() {
		ipfsAddr, err := multiaddr.NewMultiaddr("/ipfs/" + host.ID().Pretty())
		if err != nil {
			panic(err)
		}
		peerAddr := addr.Encapsulate(ipfsAddr)
		log.Printf("I'm listening on %s\n", peerAddr)
	}

	rpcHost := gorpc.NewServer(host, protocolID)

	svc := PingService{}
	err := rpcHost.Register(&svc)
	if err != nil {
		panic(err)
	}

	fmt.Println("Done")

	for {
		time.Sleep(time.Second * 1)
	}
}

func startClient(host string, pingCount, randomDataSize int) {
	fmt.Println("Launching client")
	client := createPeer("/ip4/0.0.0.0/tcp/9001")
	fmt.Printf("Hello World, my hosts ID is %s\n", client.ID().Pretty())
	ma, err := multiaddr.NewMultiaddr(host)
	if err != nil {
		panic(err)
	}
	peerInfo, err := peerstore.InfoFromP2pAddr(ma)
	if err != nil {
		panic(err)
	}
	ctx := context.Background()
	err = client.Connect(ctx, *peerInfo)
	if err != nil {
		panic(err)
	}
	rpcClient := gorpc.NewClient(client, protocolID)
	numCalls := 0
	durations := []time.Duration{}
	betweenPingsSleep := time.Second * 1

	for numCalls < pingCount {
		var reply PingReply
		var args PingArgs

		c := randomDataSize
		b := make([]byte, c)
		_, err := rand.Read(b)
		if err != nil {
			panic(err)
		}

		args.Data = b

		time.Sleep(betweenPingsSleep)
		startTime := time.Now()
		err = rpcClient.Call(peerInfo.ID, "PingService", "Ping", args, &reply)
		if err != nil {
			panic(err)
		}
		if !bytes.Equal(reply.Data, b) {
			panic("Received wrong amount of bytes back!")
		}
		endTime := time.Now()
		diff := endTime.Sub(startTime)
		fmt.Printf("%d bytes from %s (%s): seq=%d time=%s\n", c, peerInfo.ID.String(), peerInfo.Addrs[0].String(), numCalls+1, diff)
		numCalls += 1
		durations = append(durations, diff)
	}

	totalDuration := int64(0)
	for _, dur := range durations {
		totalDuration = totalDuration + dur.Nanoseconds()
	}
	averageDuration := totalDuration / int64(len(durations))
	fmt.Printf("Average duration for ping reply: %s\n", time.Duration(averageDuration))

}

func main() {

	var mode string
	var host string
	var count int
	var size int
	flag.StringVar(&mode, "mode", "", "host or client mode")
	flag.StringVar(&host, "host", "", "address of host to connect to")
	flag.IntVar(&count, "count", 10, "number of pings to make")
	flag.IntVar(&size, "size", 64, "size of random data in ping message")
	flag.Parse()

	if mode == "" {
		log.Fatal("You need to specify '-mode' to be either 'host' or 'client'")
	}

	if mode == "host" {
		startServer()
		return
	}
	if mode == "client" {
		if host == "" {
			log.Fatal("You need to specify '-host' when running as a client")
		}
		startClient(host, count, size)
		return
	}
	log.Fatal("Mode '" + mode + "' not recognized. It has to be either 'host' or 'client'")

}
<|endoftext|>


<commit_before>package cache

import (
	"context"
	"sync"

	"github.com/grafana/metrictank/mdata/chunk"
)

type MockCache struct {
	sync.Mutex
	AddCount          int
	CacheIfHotCount   int
	CacheIfHotCb      func()
	StopCount         int
	SearchCount       int
	DelMetricArchives int
	DelMetricSeries   int
	DelMetricKeys     []string
}

func NewMockCache() *MockCache {
	return &MockCache{
		DelMetricKeys: make([]string, 0),
	}
}

func (mc *MockCache) Add(m, r string, t uint32, i chunk.IterGen) {
	mc.Lock()
	defer mc.Unlock()
	mc.AddCount++
}

func (mc *MockCache) CacheIfHot(m string, t uint32, i chunk.IterGen) {
	mc.Lock()
	defer mc.Unlock()
	mc.CacheIfHotCount++
	if mc.CacheIfHotCb != nil {
		mc.CacheIfHotCb()
	}
}

func (mc *MockCache) Stop() {
	mc.Lock()
	defer mc.Unlock()
	mc.StopCount++
}

func (mc *MockCache) Search(ctx context.Context, m string, f uint32, u uint32) *CCSearchResult {
	mc.Lock()
	defer mc.Unlock()
	mc.SearchCount++
	return nil
}

func (mc *MockCache) DelMetric(key string) (int, int) {
	mc.DelMetricKeys = append(mc.DelMetricKeys, key)
	return mc.DelMetricSeries, mc.DelMetricArchives
}

func (mc *MockCache) Reset() (int, int) {
	return mc.DelMetricSeries, mc.DelMetricArchives
}
<commit_msg>do not need to make slice<commit_after>package cache

import (
	"context"
	"sync"

	"github.com/grafana/metrictank/mdata/chunk"
)

type MockCache struct {
	sync.Mutex
	AddCount          int
	CacheIfHotCount   int
	CacheIfHotCb      func()
	StopCount         int
	SearchCount       int
	DelMetricArchives int
	DelMetricSeries   int
	DelMetricKeys     []string
}

func NewMockCache() *MockCache {
	return &MockCache{}
}

func (mc *MockCache) Add(m, r string, t uint32, i chunk.IterGen) {
	mc.Lock()
	defer mc.Unlock()
	mc.AddCount++
}

func (mc *MockCache) CacheIfHot(m string, t uint32, i chunk.IterGen) {
	mc.Lock()
	defer mc.Unlock()
	mc.CacheIfHotCount++
	if mc.CacheIfHotCb != nil {
		mc.CacheIfHotCb()
	}
}

func (mc *MockCache) Stop() {
	mc.Lock()
	defer mc.Unlock()
	mc.StopCount++
}

func (mc *MockCache) Search(ctx context.Context, m string, f uint32, u uint32) *CCSearchResult {
	mc.Lock()
	defer mc.Unlock()
	mc.SearchCount++
	return nil
}

func (mc *MockCache) DelMetric(key string) (int, int) {
	mc.DelMetricKeys = append(mc.DelMetricKeys, key)
	return mc.DelMetricSeries, mc.DelMetricArchives
}

func (mc *MockCache) Reset() (int, int) {
	return mc.DelMetricSeries, mc.DelMetricArchives
}
<|endoftext|>


<commit_before>package term

import . "fmt"

import "bytes"

// NewTerm creates a new term with the given functor and optional arguments
func NewTerm(functor string, arguments ...Term) Term {
    if len(arguments) == 0 {
        return NewAtom(functor)
    }
    return &Compound{
        Func:   functor,
        Args:   arguments,
    }
}

// NewCodeList returns a compound term consisting of the character codes
// of the given string.  The internal representation may eventually optimize
// for storing character codes.
func NewCodeList(s string) Term {
    runes := []rune(s)
    list := NewAtom("[]")
    for i:=len(runes)-1; i>=0; i-- {
        list = NewTerm(".", NewCode(runes[i]), list)
    }
    return list
}

// NewTermList returns a list term consisting of each term from the slice.
// A future implementation may optimize the data structure that's returned.
func NewTermList(terms []Term) Term {
    list := NewAtom("[]")
    for i:=len(terms)-1; i>=0; i-- {
        list = NewTerm(".", terms[i], list)
    }
    return list
}

// ISO calls this a "compound term" see §6.1.2(e)
// We currently use this type to cover atoms defined in §6.1.2(b)
// by treating atoms as compound terms with 0 arity.
type Compound struct {
    Func    string
    Args    []Term

    // 0 means UnificationHash hasn't been calculated yet
    phash   uint64  // prepared hash
    qhash   uint64  // query hash
}
func (self *Compound) Functor() string {
    return self.Func
}
func (self *Compound) Arity() int {
    return len(self.Args)
}
func (self *Compound) Arguments() []Term {
    return self.Args
}
func (self *Compound) Body() Term {
    return self.Args[1]
}
func (self *Compound) Head() Term {
    return self.Args[0]
}
func (self *Compound) IsClause() bool {
    return self.Arity() == 2 && self.Functor() == ":-"
}
func (self *Compound) String() string {
    quotedFunctor := QuoteFunctor(self.Functor())

    var buf bytes.Buffer
    Fprintf(&buf, "%s(", quotedFunctor)
    arity := self.Arity()
    for i := 0; i<arity; i++ {
        if i>0 {
            Fprintf(&buf, ", ")
        }
        Fprintf(&buf, "%s", self.Arguments()[i])
    }
    Fprintf(&buf, ")")
    return buf.String()
}
func (self *Compound) Indicator() string {
    return Sprintf("%s/%d", self.Functor(), self.Arity())
}
func (self *Compound) Error() error {
    panic("Can't call Error() on a Structure")
}

func (self *Compound) ReplaceVariables(env Bindings) Term {
    arity := self.Arity()
    newArgs := make([]Term, arity)
    for i, arg := range self.Arguments() {
        newArgs[i] = arg.ReplaceVariables(env)
    }
    return NewTerm(self.Functor(), newArgs...)
}

func (a *Compound) Unify(e Bindings, b Term) (Bindings, error) {
    if IsVariable(b) {
        return b.Unify(e, a)
    }
    if !IsCompound(b) {
        return e, CantUnify
    }

    // functor and arity must match for unification to work
    arity := a.Arity()
    if arity != b.Arity() {
        return e, CantUnify
    }
    if a.Functor() != b.Functor() {
        return e, CantUnify
    }

    // try unifying each subterm
    var err error
    env := e
    aArgs := a.Arguments()
    bArgs := b.Arguments()
    for i:=0; i<arity; i++ {
        env, err = aArgs[i].Unify(env, bArgs[i])
        if err != nil {
            return e, err // return original environment along with error
        }
    }

    // unification succeeded
    return env, nil
}

// Univ is just like =../2 in ISO Prolog
func (self *Compound) Univ() []Term {
    ts := make([]Term, 0)
    ts = append(ts, NewAtom(self.Functor()))
    ts = append(ts, self.Arguments()...)
    return ts
}

// Returns true if a and b might unify.  This is an optimization
// for times when a and b are frequently unified with other
// compound terms.  For example, goals and clause heads.
func (a *Compound) MightUnify(b *Compound) bool {
    if a.qhash == 0 {
        a.qhash = UnificationHash(a.Univ(), 64, false)
    }
    if b.phash == 0 {
        b.phash = UnificationHash(b.Univ(), 64, true)
    }

    return (a.qhash & b.phash) == a.qhash
}
<commit_msg>Extra indirection for unification hashes<commit_after>package term

import . "fmt"

import "bytes"

// NewTerm creates a new term with the given functor and optional arguments
func NewTerm(functor string, arguments ...Term) Term {
    if len(arguments) == 0 {
        return NewAtom(functor)
    }
    return &Compound{
        Func:   functor,
        Args:   arguments,
        ucache: &unificationCache{},
    }
}

// NewCodeList returns a compound term consisting of the character codes
// of the given string.  The internal representation may eventually optimize
// for storing character codes.
func NewCodeList(s string) Term {
    runes := []rune(s)
    list := NewAtom("[]")
    for i:=len(runes)-1; i>=0; i-- {
        list = NewTerm(".", NewCode(runes[i]), list)
    }
    return list
}

// NewTermList returns a list term consisting of each term from the slice.
// A future implementation may optimize the data structure that's returned.
func NewTermList(terms []Term) Term {
    list := NewAtom("[]")
    for i:=len(terms)-1; i>=0; i-- {
        list = NewTerm(".", terms[i], list)
    }
    return list
}

// ISO calls this a "compound term" see §6.1.2(e)
// We currently use this type to cover atoms defined in §6.1.2(b)
// by treating atoms as compound terms with 0 arity.
type Compound struct {
    Func    string
    Args    []Term
    ucache  *unificationCache
}
type unificationCache struct {
    // 0 means UnificationHash hasn't been calculated yet
    phash   uint64  // prepared hash
    qhash   uint64  // query hash
}
func (self *Compound) Functor() string {
    return self.Func
}
func (self *Compound) Arity() int {
    return len(self.Args)
}
func (self *Compound) Arguments() []Term {
    return self.Args
}
func (self *Compound) Body() Term {
    return self.Args[1]
}
func (self *Compound) Head() Term {
    return self.Args[0]
}
func (self *Compound) IsClause() bool {
    return self.Arity() == 2 && self.Functor() == ":-"
}
func (self *Compound) String() string {
    quotedFunctor := QuoteFunctor(self.Functor())

    var buf bytes.Buffer
    Fprintf(&buf, "%s(", quotedFunctor)
    arity := self.Arity()
    for i := 0; i<arity; i++ {
        if i>0 {
            Fprintf(&buf, ", ")
        }
        Fprintf(&buf, "%s", self.Arguments()[i])
    }
    Fprintf(&buf, ")")
    return buf.String()
}
func (self *Compound) Indicator() string {
    return Sprintf("%s/%d", self.Functor(), self.Arity())
}
func (self *Compound) Error() error {
    panic("Can't call Error() on a Structure")
}

func (self *Compound) ReplaceVariables(env Bindings) Term {
    arity := self.Arity()
    newArgs := make([]Term, arity)
    for i, arg := range self.Arguments() {
        newArgs[i] = arg.ReplaceVariables(env)
    }
    return NewTerm(self.Functor(), newArgs...)
}

func (a *Compound) Unify(e Bindings, b Term) (Bindings, error) {
    if IsVariable(b) {
        return b.Unify(e, a)
    }
    if !IsCompound(b) {
        return e, CantUnify
    }

    // functor and arity must match for unification to work
    arity := a.Arity()
    if arity != b.Arity() {
        return e, CantUnify
    }
    if a.Functor() != b.Functor() {
        return e, CantUnify
    }

    // try unifying each subterm
    var err error
    env := e
    aArgs := a.Arguments()
    bArgs := b.Arguments()
    for i:=0; i<arity; i++ {
        env, err = aArgs[i].Unify(env, bArgs[i])
        if err != nil {
            return e, err // return original environment along with error
        }
    }

    // unification succeeded
    return env, nil
}

// Univ is just like =../2 in ISO Prolog
func (self *Compound) Univ() []Term {
    ts := make([]Term, 0)
    ts = append(ts, NewAtom(self.Functor()))
    ts = append(ts, self.Arguments()...)
    return ts
}

// Returns true if a and b might unify.  This is an optimization
// for times when a and b are frequently unified with other
// compound terms.  For example, goals and clause heads.
func (a *Compound) MightUnify(b *Compound) bool {
    if a.ucache.qhash == 0 {
        a.ucache.qhash = UnificationHash(a.Univ(), 64, false)
    }
    if b.ucache.phash == 0 {
        b.ucache.phash = UnificationHash(b.Univ(), 64, true)
    }

    return (a.ucache.qhash & b.ucache.phash) == a.ucache.qhash
}
<|endoftext|>