<commit_before>package context

import (
	"fmt"
	"os"
	"path"

	"github.com/spf13/afero"
)

var extensionToLanguage = map[string]string{
	".go":   "golang",
	".java": "java",
	".js":   "javascript",
	".py":   "python",
	".rb":   "ruby",
	".sh":   "shell",
	".ts":   "typescript",
}

// acquireMainLanguageForDir attempts to determine the main programming language of the target directory
func acquireMainLanguageForDir(fs *afero.Afero, targetDir string) (string, error) {
	languageCount := make(map[string]int)

	fs.Walk(targetDir, func(targetPath string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		lang, err := acquireMainLanguageForFile(fs, targetPath)
		if err != nil {
			return fmt.Errorf("acquiring language for file %s: %w", targetPath, err)
		}

		if lang == "" {
			return nil
		}

		_, ok := extensionToLanguage[lang]
		if !ok {
			languageCount[lang] = 0
		}

		languageCount[lang]++

		return nil
	})

	strongestLanguage := ""
	strongestLanguageCount := 0

	for key, val := range languageCount {
		if val > strongestLanguageCount {
			strongestLanguage = key
			strongestLanguageCount = val
		}
	}

	return strongestLanguage, nil
}

// acquireMainLanguageForFile attempts to determine the main programming language of the target file
func acquireMainLanguageForFile(fs *afero.Afero, targetFile string) (string, error) {
	ext := path.Ext(targetFile)

	if language, ok := extensionToLanguage[ext]; ok {
		return language, nil
	}

	return "", nil
}
<commit_msg>🐛 Fix erreonously testing wrong dict<commit_after>package context

import (
	"fmt"
	"os"
	"path"

	"github.com/spf13/afero"
)

// acquireMainLanguageForDir attempts to determine the main programming language of the target directory
func acquireMainLanguageForDir(fs *afero.Afero, targetDir string) (string, error) {
	languageCount := make(map[string]int)

	fs.Walk(targetDir, func(targetPath string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		lang, err := acquireMainLanguageForFile(fs, targetPath)
		if err != nil {
			return fmt.Errorf("acquiring language for file %s: %w", targetPath, err)
		}

		if lang == "" {
			return nil
		}

		_, ok := languageCount[lang]
		if !ok {
			languageCount[lang] = 0
		}

		languageCount[lang]++

		return nil
	})

	strongestLanguage := ""
	strongestLanguageCount := 0

	for key, val := range languageCount {
		if val > strongestLanguageCount {
			strongestLanguage = key
			strongestLanguageCount = val
		}
	}

	return strongestLanguage, nil
}

// acquireMainLanguageForFile attempts to determine the main programming language of the target file
func acquireMainLanguageForFile(fs *afero.Afero, targetFile string) (string, error) {
	ext := path.Ext(targetFile)

	if language, ok := extensionToLanguage[ext]; ok {
		return language, nil
	}

	return "", nil
}

var extensionToLanguage = map[string]string{
	".go":   "golang",
	".java": "java",
	".js":   "javascript",
	".py":   "python",
	".rb":   "ruby",
	".sh":   "shell",
	".ts":   "typescript",
}
<|endoftext|>


<commit_before>package version

// Version is the version of the build.
const Version = "0.2.0"
<commit_msg>Move to v0.2.1-dev<commit_after>package version

// Version is the version of the build.
const Version = "0.2.0-dev"
<|endoftext|>


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

// package seconf allows your software to store non-plaintext configuration files.
package seconf

// Here is an example application that stores and retrieves four fields.
// The third field in this example begins with "pass" so is interpreted as a password and will not be echoed.
//
// ```
//
// package main
//
// import (
// 	"fmt"
// 	"os"
// 	"strings"
//
// 	"github.com/aerth/seconf"
// )
//
// func main() {
// 	if len(os.Args) < 3 {
// 		fmt.Println("Usage:")
// 		fmt.Println(os.Args[0] + " configname servicename field1 field2 etc")
// 		fmt.Println("Example:")
// 		fmt.Println(os.Args[0] + " SuperConfig FirstSeconf username favorite-color password favorite-celebrity")
// 		os.Exit(1)
// 	}
//
// 	s := os.Args[1]
// 	sn := os.Args[2]
// 	var fields []string
// 	fields = os.Args[3:]
//
// 	if !seconf.Detect(s) {
// 		seconf.Create(s, sn, fields...)
// 	} else {
// 		configdecoded, err := seconf.Read(s)
// 		if err != nil {
// 			fmt.Println("error:")
// 			fmt.Println(err)
// 			os.Exit(1)
// 		}
// 		configarray := strings.Split(configdecoded, "::::")
// 		if len(configarray) < 2 {
// 			fmt.Println("Broken config file. Create a new one.")
// 			os.Exit(1)
// 		}
// 		if err != nil {
// 			fmt.Println(err)
// 			os.Exit(1)
// 		}
// 		fmt.Println("Welcome to " + sn + ", " + configarray[0])
// 		fmt.Printf("Your %s is %s \n", os.Args[3], configarray[0])
// 		fmt.Printf("Your %s is %s \n", os.Args[4], configarray[1])
// 		fmt.Printf("Your %s is %s \n", os.Args[5], configarray[2])
//
// 	}
// }
//
// ```
//
import (
	"bufio"
	"crypto/rand"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"

	"github.com/bgentry/speakeasy"
	"golang.org/x/crypto/nacl/secretbox"
)

const keySize = 32
const nonceSize = 24

// secustom is the filename that gets stored. for example, if secustom is "test", the configuration file will be saved as $HOME/.test
var secustom string
var username string
var password string
var hashbar = strings.Repeat("#", 80)

var configuser = ""
var configpass = ""

var configlock = ""

// Seconf is the struct for the seconf pathname and fields.
type Seconf struct {
	Id   int64
	Path string
	Args []string
}

/*
type Fielder struct {
	Id       int64
	Name     string
	Password bool
}
*/

// constainsString returns true if a slice contains a string.
func containsString(slice []string, element string) bool {
	return !(posString(slice, element) == -1)
}

// askForConfirmation returns true if the user types one of the "okayResponses"
// https://gist.github.com/albrow/5882501
func askForConfirmation() bool {
	var response string
	_, err := fmt.Scanln(&response)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	okayResponses := []string{"y", "Y", "yes", "Yes", "YES"}
	nokayResponses := []string{"n", "N", "no", "No", "NO"}
	quitResponses := []string{"q", "Q", "exit", "quit"}
	if containsString(okayResponses, response) {
		return true
	} else if containsString(nokayResponses, response) {
		return false
	} else if containsString(quitResponses, response) {
		return false
	} else {
		fmt.Println("\nNot valid answer, try again. [y/n] [yes/no]")
		return askForConfirmation()
	}
}

// posString returns the first index of element in slice.
// If slice does not contain element, returns -1.
func posString(slice []string, element string) int {
	for index, elem := range slice {
		if elem == element {
			return index
		}
	}
	return -1
}

// Prompt the user for the particular field.
func Prompt(header string) string {
	fmt.Printf("\n### " + header + " ###\n")
	fmt.Printf("\nPress ENTER when you are finished typing.\n\n")
	scanner := bufio.NewScanner(os.Stdin)
	if scanner.Scan() {
		line := scanner.Text()
		return line
	}
	if err := scanner.Err(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	return ""
}

// Create initializes a new configuration file, at $HOME/secustom with the title servicename and as many fields as needed. Any field starting with "pass" will be assumed a password and input will not be echoed.
func Create(secustom string, servicename string, arg ...string) {
	bar(servicename)
	configfields := &Seconf{
		Path: secustom,
		Args: arg,
	}

	var m1 map[int]string = map[int]string{}
	var newsplice []string
	for i := range configfields.Args {
		bar(servicename)
		if len(configfields.Args[i]) > 4 {
			if configfields.Args[i][0:4] == "pass" || configfields.Args[i][0:4] == "Pass" {
				//		fmt.Printf("\n### " + servicename + " ###\n")
				m1[i], _ = speakeasy.Ask(servicename + " " + configfields.Args[i] + ":")
				if m1[i] == "" {
					bar(secustom)
					m1[i], _ = speakeasy.Ask(servicename + " " + configfields.Args[i] + ":")
				}
				if m1[i] == "" {
					bar(secustom)
					m1[i], _ = speakeasy.Ask(servicename + " " + configfields.Args[i] + ":")
				}
				if m1[i] == "" {
					bar(secustom)
					fmt.Println(configfields.Args[i] + " cannot be blank.")
					os.Exit(1)
				}

			} else {
				m1[i] = Prompt(configfields.Args[i])
				if m1[i] == "" {
					bar(servicename)
					fmt.Println("Can not be blank.")
					m1[i] = Prompt(configfields.Args[i])
				}
				if m1[i] == "" {
					bar(servicename)
					fmt.Println("Can not be blank.")
					m1[i] = Prompt(configfields.Args[i])
				}
				if m1[i] == "" {
					bar(servicename)
					fmt.Println(configfields.Args[i] + " cannot be blank.")
					os.Exit(1)
				}
			}
		} else {
			m1[i] = Prompt(configfields.Args[i])
		}
		newsplice = append(newsplice, m1[i]+"::::")
	}

	bar(servicename)
	configlock, _ := speakeasy.Ask("Create a password to encrypt config file:\nPress ENTER for no password.")
	var userKey = configlock
	var pad = []byte("«super jumpy fox jumps all over»")

	var messagebox = strings.Join(newsplice, "")
	messagebox = strings.TrimSuffix(messagebox, "::::")
	var message = []byte(messagebox)
	key := []byte(userKey)
	key = append(key, pad...)
	naclKey := new([keySize]byte)
	copy(naclKey[:], key[:keySize])
	nonce := new([nonceSize]byte)
	// Read bytes from random and put them in nonce until it is full.
	_, err := io.ReadFull(rand.Reader, nonce[:])
	if err != nil {
		fmt.Println("Could not read from random:", err)
		os.Exit(1)
	}
	out := make([]byte, nonceSize)
	copy(out, nonce[:])
	out = secretbox.Seal(out, message, nonce, naclKey)
	err = ioutil.WriteFile(ReturnHome()+"/."+secustom, out, 0600)
	if err != nil {
		fmt.Println("Error while writing config file: ", err)
		os.Exit(1)
	}
	fmt.Printf("Config file saved at "+ReturnHome()+"/."+secustom+" \nTotal size is %d bytes.\n",
		len(out))
	os.Exit(0)
}

// Detect returns TRUE if a seconf file exists.
func Detect(secustom string) bool {
	_, err := ioutil.ReadFile(ReturnHome() + "/." + secustom)
	if err != nil {
		return false
	}
	return true
}

// Read returns the decoded configuration file, or an error. Fields are separated by 4 colons. ("::::")
func Read(secustom string) (config string, err error) {
	bar(secustom)
	fmt.Println("Unlocking config file")
	configlock, err = speakeasy.Ask("Password: ")
	bar(secustom)
	var userKey = configlock
	var pad = []byte("«super jumpy fox jumps all over»")
	key := []byte(userKey)
	key = append(key, pad...)
	naclKey := new([keySize]byte)
	copy(naclKey[:], key[:keySize])
	nonce := new([nonceSize]byte)
	in, err := ioutil.ReadFile(ReturnHome() + "/." + secustom)
	if err != nil {
		fmt.Println(err)

	}
	copy(nonce[:], in[:nonceSize])
	configbytes, ok := secretbox.Open(nil, in[nonceSize:], nonce, naclKey)
	if !ok {
		fmt.Println("Could not decrypt the config file. Wrong password?")
		os.Exit(1)
	}
	return string(configbytes), nil

}

// Cheap and effective way of clearing screen on unix. Ugly on windows.
func bar(secustom string) {
	versionbar := strings.Repeat("#", 10) + "\t" + secustom + "\t" + strings.Repeat("#", 30)
	print("\033[H\033[2J")
	fmt.Println(versionbar)
}

// ReturnHome is a cross-OS way of getting a HOMEDIR.
func ReturnHome() (homedir string) {
	homedir = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
	if homedir == "" {
		homedir = os.Getenv("USERPROFILE")
	}
	if homedir == "" {
		homedir = os.Getenv("HOME")
	}
	return
}
<commit_msg>godoc<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2016 aerth
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

// package seconf allows your software to store non-plaintext configuration files.
// Here is an example application that stores and retrieves four fields.
// The third field in this example begins with "pass" so is interpreted as a password and will not be echoed.
//
// ```
//
// package main
//
// import (
// 	"fmt"
// 	"os"
// 	"strings"
//
// 	"github.com/aerth/seconf"
// )
//
// func main() {
// 	if len(os.Args) < 3 {
// 		fmt.Println("Usage:")
// 		fmt.Println(os.Args[0] + " configname servicename field1 field2 etc")
// 		fmt.Println("Example:")
// 		fmt.Println(os.Args[0] + " SuperConfig FirstSeconf username favorite-color password favorite-celebrity")
// 		os.Exit(1)
// 	}
//
// 	s := os.Args[1]
// 	sn := os.Args[2]
// 	var fields []string
// 	fields = os.Args[3:]
//
// 	if !seconf.Detect(s) {
// 		seconf.Create(s, sn, fields...)
// 	} else {
// 		configdecoded, err := seconf.Read(s)
// 		if err != nil {
// 			fmt.Println("error:")
// 			fmt.Println(err)
// 			os.Exit(1)
// 		}
// 		configarray := strings.Split(configdecoded, "::::")
// 		if len(configarray) < 2 {
// 			fmt.Println("Broken config file. Create a new one.")
// 			os.Exit(1)
// 		}
// 		if err != nil {
// 			fmt.Println(err)
// 			os.Exit(1)
// 		}
// 		fmt.Println("Welcome to " + sn + ", " + configarray[0])
// 		fmt.Printf("Your %s is %s \n", os.Args[3], configarray[0])
// 		fmt.Printf("Your %s is %s \n", os.Args[4], configarray[1])
// 		fmt.Printf("Your %s is %s \n", os.Args[5], configarray[2])
//
// 	}
// }
//
// ```
//

package seconf

import (
	"bufio"
	"crypto/rand"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"

	"github.com/bgentry/speakeasy"
	"golang.org/x/crypto/nacl/secretbox"
)

const keySize = 32
const nonceSize = 24

// secustom is the filename that gets stored. for example, if secustom is "test", the configuration file will be saved as $HOME/.test
var secustom string
var username string
var password string
var hashbar = strings.Repeat("#", 80)

var configuser = ""
var configpass = ""

var configlock = ""

// Seconf is the struct for the seconf pathname and fields.
type Seconf struct {
	Id   int64
	Path string
	Args []string
}

/*
type Fielder struct {
	Id       int64
	Name     string
	Password bool
}
*/

// constainsString returns true if a slice contains a string.
func containsString(slice []string, element string) bool {
	return !(posString(slice, element) == -1)
}

// askForConfirmation returns true if the user types one of the "okayResponses"
// https://gist.github.com/albrow/5882501
func askForConfirmation() bool {
	var response string
	_, err := fmt.Scanln(&response)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	okayResponses := []string{"y", "Y", "yes", "Yes", "YES"}
	nokayResponses := []string{"n", "N", "no", "No", "NO"}
	quitResponses := []string{"q", "Q", "exit", "quit"}
	if containsString(okayResponses, response) {
		return true
	} else if containsString(nokayResponses, response) {
		return false
	} else if containsString(quitResponses, response) {
		return false
	} else {
		fmt.Println("\nNot valid answer, try again. [y/n] [yes/no]")
		return askForConfirmation()
	}
}

// posString returns the first index of element in slice.
// If slice does not contain element, returns -1.
func posString(slice []string, element string) int {
	for index, elem := range slice {
		if elem == element {
			return index
		}
	}
	return -1
}

// Prompt the user for the particular field.
func Prompt(header string) string {
	fmt.Printf("\n### " + header + " ###\n")
	fmt.Printf("\nPress ENTER when you are finished typing.\n\n")
	scanner := bufio.NewScanner(os.Stdin)
	if scanner.Scan() {
		line := scanner.Text()
		return line
	}
	if err := scanner.Err(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	return ""
}

// Create initializes a new configuration file, at $HOME/secustom with the title servicename and as many fields as needed. Any field starting with "pass" will be assumed a password and input will not be echoed.
func Create(secustom string, servicename string, arg ...string) {
	bar(servicename)
	configfields := &Seconf{
		Path: secustom,
		Args: arg,
	}

	var m1 map[int]string = map[int]string{}
	var newsplice []string
	for i := range configfields.Args {
		bar(servicename)
		if len(configfields.Args[i]) > 4 {
			if configfields.Args[i][0:4] == "pass" || configfields.Args[i][0:4] == "Pass" {
				//		fmt.Printf("\n### " + servicename + " ###\n")
				m1[i], _ = speakeasy.Ask(servicename + " " + configfields.Args[i] + ":")
				if m1[i] == "" {
					bar(secustom)
					m1[i], _ = speakeasy.Ask(servicename + " " + configfields.Args[i] + ":")
				}
				if m1[i] == "" {
					bar(secustom)
					m1[i], _ = speakeasy.Ask(servicename + " " + configfields.Args[i] + ":")
				}
				if m1[i] == "" {
					bar(secustom)
					fmt.Println(configfields.Args[i] + " cannot be blank.")
					os.Exit(1)
				}

			} else {
				m1[i] = Prompt(configfields.Args[i])
				if m1[i] == "" {
					bar(servicename)
					fmt.Println("Can not be blank.")
					m1[i] = Prompt(configfields.Args[i])
				}
				if m1[i] == "" {
					bar(servicename)
					fmt.Println("Can not be blank.")
					m1[i] = Prompt(configfields.Args[i])
				}
				if m1[i] == "" {
					bar(servicename)
					fmt.Println(configfields.Args[i] + " cannot be blank.")
					os.Exit(1)
				}
			}
		} else {
			m1[i] = Prompt(configfields.Args[i])
		}
		newsplice = append(newsplice, m1[i]+"::::")
	}

	bar(servicename)
	configlock, _ := speakeasy.Ask("Create a password to encrypt config file:\nPress ENTER for no password.")
	var userKey = configlock
	var pad = []byte("«super jumpy fox jumps all over»")

	var messagebox = strings.Join(newsplice, "")
	messagebox = strings.TrimSuffix(messagebox, "::::")
	var message = []byte(messagebox)
	key := []byte(userKey)
	key = append(key, pad...)
	naclKey := new([keySize]byte)
	copy(naclKey[:], key[:keySize])
	nonce := new([nonceSize]byte)
	// Read bytes from random and put them in nonce until it is full.
	_, err := io.ReadFull(rand.Reader, nonce[:])
	if err != nil {
		fmt.Println("Could not read from random:", err)
		os.Exit(1)
	}
	out := make([]byte, nonceSize)
	copy(out, nonce[:])
	out = secretbox.Seal(out, message, nonce, naclKey)
	err = ioutil.WriteFile(ReturnHome()+"/."+secustom, out, 0600)
	if err != nil {
		fmt.Println("Error while writing config file: ", err)
		os.Exit(1)
	}
	fmt.Printf("Config file saved at "+ReturnHome()+"/."+secustom+" \nTotal size is %d bytes.\n",
		len(out))
	os.Exit(0)
}

// Detect returns TRUE if a seconf file exists.
func Detect(secustom string) bool {
	_, err := ioutil.ReadFile(ReturnHome() + "/." + secustom)
	if err != nil {
		return false
	}
	return true
}

// Read returns the decoded configuration file, or an error. Fields are separated by 4 colons. ("::::")
func Read(secustom string) (config string, err error) {
	bar(secustom)
	fmt.Println("Unlocking config file")
	configlock, err = speakeasy.Ask("Password: ")
	bar(secustom)
	var userKey = configlock
	var pad = []byte("«super jumpy fox jumps all over»")
	key := []byte(userKey)
	key = append(key, pad...)
	naclKey := new([keySize]byte)
	copy(naclKey[:], key[:keySize])
	nonce := new([nonceSize]byte)
	in, err := ioutil.ReadFile(ReturnHome() + "/." + secustom)
	if err != nil {
		fmt.Println(err)

	}
	copy(nonce[:], in[:nonceSize])
	configbytes, ok := secretbox.Open(nil, in[nonceSize:], nonce, naclKey)
	if !ok {
		fmt.Println("Could not decrypt the config file. Wrong password?")
		os.Exit(1)
	}
	return string(configbytes), nil

}

// Cheap and effective way of clearing screen on unix. Ugly on windows.
func bar(secustom string) {
	versionbar := strings.Repeat("#", 10) + "\t" + secustom + "\t" + strings.Repeat("#", 30)
	print("\033[H\033[2J")
	fmt.Println(versionbar)
}

// ReturnHome is a cross-OS way of getting a HOMEDIR.
func ReturnHome() (homedir string) {
	homedir = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
	if homedir == "" {
		homedir = os.Getenv("USERPROFILE")
	}
	if homedir == "" {
		homedir = os.Getenv("HOME")
	}
	return
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package service

import (
	"strings"
	"time"

	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/utils"
	"github.com/juju/utils/series"

	"github.com/juju/juju/juju/paths"
	"github.com/juju/juju/service/common"
	"github.com/juju/juju/service/systemd"
	"github.com/juju/juju/service/upstart"
	"github.com/juju/juju/service/windows"
)

var (
	logger = loggo.GetLogger("juju.service")
)

// These are the names of the init systems regognized by juju.
const (
	InitSystemSystemd = "systemd"
	InitSystemUpstart = "upstart"
	InitSystemWindows = "windows"
)

// linuxInitSystems lists the names of the init systems that juju might
// find on a linux host.
var linuxInitSystems = []string{
	InitSystemSystemd,
	InitSystemUpstart,
}

// ServiceActions represents the actions that may be requested for
// an init system service.
type ServiceActions interface {
	// Start will try to start the service.
	Start() error

	// Stop will try to stop the service.
	Stop() error

	// Install installs a service.
	Install() error

	// Remove will remove the service.
	Remove() error
}

// Service represents a service in the init system running on a host.
type Service interface {
	ServiceActions

	// Name returns the service's name.
	Name() string

	// Conf returns the service's conf data.
	Conf() common.Conf

	// Running returns a boolean value that denotes
	// whether or not the service is running.
	Running() (bool, error)

	// Exists returns whether the service configuration exists in the
	// init directory with the same content that this Service would have
	// if installed.
	Exists() (bool, error)

	// Installed will return a boolean value that denotes
	// whether or not the service is installed.
	Installed() (bool, error)

	// TODO(ericsnow) Move all the commands into a separate interface.

	// InstallCommands returns the list of commands to run on a
	// (remote) host to install the service.
	InstallCommands() ([]string, error)

	// StartCommands returns the list of commands to run on a
	// (remote) host to start the service.
	StartCommands() ([]string, error)
}

// RestartableService is a service that directly supports restarting.
type RestartableService interface {
	// Restart restarts the service.
	Restart() error
}

// TODO(ericsnow) bug #1426458
// Eliminate the need to pass an empty conf for most service methods
// and several helper functions.

// NewService returns a new Service based on the provided info.
var NewService = func(name string, conf common.Conf, series string) (Service, error) {
	if name == "" {
		return nil, errors.New("missing name")
	}

	initSystem, err := versionInitSystem(series)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return newService(name, conf, initSystem, series)
}

// this needs to be stubbed out in some tests
func newService(name string, conf common.Conf, initSystem, series string) (Service, error) {
	switch initSystem {
	case InitSystemWindows:
		svc, err := windows.NewService(name, conf)
		if err != nil {
			return nil, errors.Annotatef(err, "failed to wrap service %q", name)
		}
		return svc, nil
	case InitSystemUpstart:
		return upstart.NewService(name, conf), nil
	case InitSystemSystemd:
		dataDir, err := paths.DataDir(series)
		if err != nil {
			return nil, errors.Annotatef(err, "failed to find juju data dir for application %q", name)
		}

		svc, err := systemd.NewService(name, conf, dataDir)
		if err != nil {
			return nil, errors.Annotatef(err, "failed to wrap service %q", name)
		}
		return svc, nil
	default:
		return nil, errors.NotFoundf("init system %q", initSystem)
	}
}

// ListServices lists all installed services on the running system
var ListServices = func() ([]string, error) {
	hostSeries, err := series.HostSeries()
	if err != nil {
		return nil, errors.Trace(err)
	}
	initName, err := VersionInitSystem(hostSeries)
	if err != nil {
		return nil, errors.Trace(err)
	}

	switch initName {
	case InitSystemWindows:
		services, err := windows.ListServices()
		if err != nil {
			return nil, errors.Annotatef(err, "failed to list %s services", initName)
		}
		return services, nil
	case InitSystemUpstart:
		services, err := upstart.ListServices()
		if err != nil {
			return nil, errors.Annotatef(err, "failed to list %s services", initName)
		}
		return services, nil
	case InitSystemSystemd:
		services, err := systemd.ListServices()
		if err != nil {
			return nil, errors.Annotatef(err, "failed to list %s services", initName)
		}
		return services, nil
	default:
		return nil, errors.NotFoundf("init system %q", initName)
	}
}

// ListServicesScript returns the commands that should be run to get
// a list of service names on a host.
func ListServicesScript() string {
	commands := []string{
		"init_system=$(" + DiscoverInitSystemScript() + ")",
		// If the init system is not identified then the script will
		// "exit 1". This is correct since the script should fail if no
		// init system can be identified.
		newShellSelectCommand("init_system", "exit 1", listServicesCommand),
	}
	return strings.Join(commands, "\n")
}

func listServicesCommand(initSystem string) (string, bool) {
	switch initSystem {
	case InitSystemWindows:
		return windows.ListCommand(), true
	case InitSystemUpstart:
		return upstart.ListCommand(), true
	case InitSystemSystemd:
		return systemd.ListCommand(), true
	default:
		return "", false
	}
}

// installStartRetryAttempts defines how much InstallAndStart retries
// upon Start failures.
//
// TODO(katco): 2016-08-09: lp:1611427
var installStartRetryAttempts = utils.AttemptStrategy{
	Total: 1 * time.Second,
	Delay: 250 * time.Millisecond,
}

// InstallAndStart installs the provided service and tries starting it.
// The first few Start failures are ignored.
func InstallAndStart(svc ServiceActions) error {
	logger.Infof("Installing and starting service %+v", svc)
	if err := svc.Install(); err != nil {
		return errors.Trace(err)
	}

	// For various reasons the init system may take a short time to
	// realise that the service has been installed.
	var err error
	for attempt := installStartRetryAttempts.Start(); attempt.Next(); {
		if err != nil {
			logger.Errorf("retrying start request (%v)", errors.Cause(err))
		}
		if err = restartOrStart(svc); err == nil {
			break
		}
	}
	return errors.Trace(err)
}

// discoverService is patched out during some tests.
var discoverService = func(name string) (Service, error) {
	return DiscoverService(name, common.Conf{})
}

// TODO(ericsnow) Add one-off helpers for Start and Stop too?

// Restart restarts the named service.
func Restart(name string) error {
	svc, err := discoverService(name)
	if err != nil {
		return errors.Annotatef(err, "failed to find service %q", name)
	}
	if err := restart(svc); err != nil {
		return errors.Annotatef(err, "failed to restart service %q", name)
	}
	return nil
}

func restartOrStart(svc ServiceActions) error {
	// Use the Restart method, if there is one.
	if svc, ok := svc.(RestartableService); ok {
		if err := svc.Restart(); err != nil {
			return errors.Trace(err)
		}
		return nil
	}

	// Otherwise explicitly stop and start the service.
	if err := svc.Stop(); err != nil {
		logger.Errorf("could not stop service: %v", err)
	}
	if err := svc.Start(); err != nil {
		return errors.Trace(err)
	}
	return nil

}

func restart(svc Service) error {
	// Use the Restart method, if there is one.
	if svc, ok := svc.(RestartableService); ok {
		if err := svc.Restart(); err != nil {
			return errors.Trace(err)
		}
		return nil
	}

	// Otherwise explicitly stop and start the service.
	if err := svc.Stop(); err != nil {
		return errors.Trace(err)
	}
	if err := svc.Start(); err != nil {
		return errors.Trace(err)
	}
	return nil
}
<commit_msg>Use only start/stop for restart on mongo.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package service

import (
	"strings"
	"time"

	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/utils"
	"github.com/juju/utils/series"

	"github.com/juju/juju/juju/paths"
	"github.com/juju/juju/service/common"
	"github.com/juju/juju/service/systemd"
	"github.com/juju/juju/service/upstart"
	"github.com/juju/juju/service/windows"
)

var (
	logger = loggo.GetLogger("juju.service")
)

// These are the names of the init systems regognized by juju.
const (
	InitSystemSystemd = "systemd"
	InitSystemUpstart = "upstart"
	InitSystemWindows = "windows"
)

// linuxInitSystems lists the names of the init systems that juju might
// find on a linux host.
var linuxInitSystems = []string{
	InitSystemSystemd,
	InitSystemUpstart,
}

// ServiceActions represents the actions that may be requested for
// an init system service.
type ServiceActions interface {
	// Start will try to start the service.
	Start() error

	// Stop will try to stop the service.
	Stop() error

	// Install installs a service.
	Install() error

	// Remove will remove the service.
	Remove() error
}

// Service represents a service in the init system running on a host.
type Service interface {
	ServiceActions

	// Name returns the service's name.
	Name() string

	// Conf returns the service's conf data.
	Conf() common.Conf

	// Running returns a boolean value that denotes
	// whether or not the service is running.
	Running() (bool, error)

	// Exists returns whether the service configuration exists in the
	// init directory with the same content that this Service would have
	// if installed.
	Exists() (bool, error)

	// Installed will return a boolean value that denotes
	// whether or not the service is installed.
	Installed() (bool, error)

	// TODO(ericsnow) Move all the commands into a separate interface.

	// InstallCommands returns the list of commands to run on a
	// (remote) host to install the service.
	InstallCommands() ([]string, error)

	// StartCommands returns the list of commands to run on a
	// (remote) host to start the service.
	StartCommands() ([]string, error)
}

// RestartableService is a service that directly supports restarting.
type RestartableService interface {
	// Restart restarts the service.
	Restart() error
}

// TODO(ericsnow) bug #1426458
// Eliminate the need to pass an empty conf for most service methods
// and several helper functions.

// NewService returns a new Service based on the provided info.
var NewService = func(name string, conf common.Conf, series string) (Service, error) {
	if name == "" {
		return nil, errors.New("missing name")
	}

	initSystem, err := versionInitSystem(series)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return newService(name, conf, initSystem, series)
}

// this needs to be stubbed out in some tests
func newService(name string, conf common.Conf, initSystem, series string) (Service, error) {
	switch initSystem {
	case InitSystemWindows:
		svc, err := windows.NewService(name, conf)
		if err != nil {
			return nil, errors.Annotatef(err, "failed to wrap service %q", name)
		}
		return svc, nil
	case InitSystemUpstart:
		return upstart.NewService(name, conf), nil
	case InitSystemSystemd:
		dataDir, err := paths.DataDir(series)
		if err != nil {
			return nil, errors.Annotatef(err, "failed to find juju data dir for application %q", name)
		}

		svc, err := systemd.NewService(name, conf, dataDir)
		if err != nil {
			return nil, errors.Annotatef(err, "failed to wrap service %q", name)
		}
		return svc, nil
	default:
		return nil, errors.NotFoundf("init system %q", initSystem)
	}
}

// ListServices lists all installed services on the running system
var ListServices = func() ([]string, error) {
	hostSeries, err := series.HostSeries()
	if err != nil {
		return nil, errors.Trace(err)
	}
	initName, err := VersionInitSystem(hostSeries)
	if err != nil {
		return nil, errors.Trace(err)
	}

	switch initName {
	case InitSystemWindows:
		services, err := windows.ListServices()
		if err != nil {
			return nil, errors.Annotatef(err, "failed to list %s services", initName)
		}
		return services, nil
	case InitSystemUpstart:
		services, err := upstart.ListServices()
		if err != nil {
			return nil, errors.Annotatef(err, "failed to list %s services", initName)
		}
		return services, nil
	case InitSystemSystemd:
		services, err := systemd.ListServices()
		if err != nil {
			return nil, errors.Annotatef(err, "failed to list %s services", initName)
		}
		return services, nil
	default:
		return nil, errors.NotFoundf("init system %q", initName)
	}
}

// ListServicesScript returns the commands that should be run to get
// a list of service names on a host.
func ListServicesScript() string {
	commands := []string{
		"init_system=$(" + DiscoverInitSystemScript() + ")",
		// If the init system is not identified then the script will
		// "exit 1". This is correct since the script should fail if no
		// init system can be identified.
		newShellSelectCommand("init_system", "exit 1", listServicesCommand),
	}
	return strings.Join(commands, "\n")
}

func listServicesCommand(initSystem string) (string, bool) {
	switch initSystem {
	case InitSystemWindows:
		return windows.ListCommand(), true
	case InitSystemUpstart:
		return upstart.ListCommand(), true
	case InitSystemSystemd:
		return systemd.ListCommand(), true
	default:
		return "", false
	}
}

// installStartRetryAttempts defines how much InstallAndStart retries
// upon Start failures.
//
// TODO(katco): 2016-08-09: lp:1611427
var installStartRetryAttempts = utils.AttemptStrategy{
	Total: 1 * time.Second,
	Delay: 250 * time.Millisecond,
}

// InstallAndStart installs the provided service and tries starting it.
// The first few Start failures are ignored.
func InstallAndStart(svc ServiceActions) error {
	logger.Infof("Installing and starting service %+v", svc)
	if err := svc.Install(); err != nil {
		return errors.Trace(err)
	}

	// For various reasons the init system may take a short time to
	// realise that the service has been installed.
	var err error
	for attempt := installStartRetryAttempts.Start(); attempt.Next(); {
		if err != nil {
			logger.Errorf("retrying start request (%v)", errors.Cause(err))
		}
		// we attempt restart if the service is running in case daemon parameters
		// have changed, if its not running a regular start will happen.
		if err = restartOrStart(svc); err == nil {
			break
		}
	}
	return errors.Trace(err)
}

// discoverService is patched out during some tests.
var discoverService = func(name string) (Service, error) {
	return DiscoverService(name, common.Conf{})
}

// TODO(ericsnow) Add one-off helpers for Start and Stop too?

// Restart restarts the named service.
func Restart(name string) error {
	svc, err := discoverService(name)
	if err != nil {
		return errors.Annotatef(err, "failed to find service %q", name)
	}
	if err := restart(svc); err != nil {
		return errors.Annotatef(err, "failed to restart service %q", name)
	}
	return nil
}

func restartOrStart(svc ServiceActions) error {
	// Explicitly omit Restart as it is not properly supported on trusty.
	// Otherwise explicitly stop and start the service.
	if err := svc.Stop(); err != nil {
		logger.Errorf("could not stop service: %v", err)
	}
	if err := svc.Start(); err != nil {
		return errors.Trace(err)
	}
	return nil

}

func restart(svc Service) error {
	// Use the Restart method, if there is one.
	if svc, ok := svc.(RestartableService); ok {
		if err := svc.Restart(); err != nil {
			return errors.Trace(err)
		}
		return nil
	}

	// Otherwise explicitly stop and start the service.
	if err := svc.Stop(); err != nil {
		return errors.Trace(err)
	}
	if err := svc.Start(); err != nil {
		return errors.Trace(err)
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2011 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 dsa

import (
	"crypto/rand"
	"math/big"
	"testing"
)

func testSignAndVerify(t *testing.T, i int, priv *PrivateKey) {
	hashed := []byte("testing")
	r, s, err := Sign(rand.Reader, priv, hashed)
	if err != nil {
		t.Errorf("%d: error signing: %s", i, err)
		return
	}

	if !Verify(&priv.PublicKey, hashed, r, s) {
		t.Errorf("%d: Verify failed", i)
	}
}

func testParameterGeneration(t *testing.T, sizes ParameterSizes, L, N int) {
	var priv PrivateKey
	params := &priv.Parameters

	err := GenerateParameters(params, rand.Reader, sizes)
	if err != nil {
		t.Errorf("%d: %s", int(sizes), err)
		return
	}

	if params.P.BitLen() != L {
		t.Errorf("%d: params.BitLen got:%d want:%d", int(sizes), params.P.BitLen(), L)
	}

	if params.Q.BitLen() != N {
		t.Errorf("%d: q.BitLen got:%d want:%d", int(sizes), params.Q.BitLen(), L)
	}

	one := new(big.Int)
	one.SetInt64(1)
	pm1 := new(big.Int).Sub(params.P, one)
	quo, rem := new(big.Int).DivMod(pm1, params.Q, new(big.Int))
	if rem.Sign() != 0 {
		t.Errorf("%d: p-1 mod q != 0", int(sizes))
	}
	x := new(big.Int).Exp(params.G, quo, params.P)
	if x.Cmp(one) == 0 {
		t.Errorf("%d: invalid generator", int(sizes))
	}

	err = GenerateKey(&priv, rand.Reader)
	if err != nil {
		t.Errorf("error generating key: %s", err)
		return
	}

	testSignAndVerify(t, int(sizes), &priv)
}

func TestParameterGeneration(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping parameter generation test in short mode")
	}

	testParameterGeneration(t, L1024N160, 1024, 160)
	testParameterGeneration(t, L2048N224, 2048, 224)
	testParameterGeneration(t, L2048N256, 2048, 256)
	testParameterGeneration(t, L3072N256, 3072, 256)
}

func fromHex(s string) *big.Int {
	result, ok := new(big.Int).SetString(s, 16)
	if !ok {
		panic(s)
	}
	return result
}

func TestSignAndVerify(t *testing.T) {
	var priv PrivateKey
	priv.P, _ = new(big.Int).SetString("A9B5B793FB4785793D246BAE77E8FF63CA52F442DA763C440259919FE1BC1D6065A9350637A04F75A2F039401D49F08E066C4D275A5A65DA5684BC563C14289D7AB8A67163BFBF79D85972619AD2CFF55AB0EE77A9002B0EF96293BDD0F42685EBB2C66C327079F6C98000FBCB79AACDE1BC6F9D5C7B1A97E3D9D54ED7951FEF", 16)
	priv.Q, _ = new(big.Int).SetString("E1D3391245933D68A0714ED34BBCB7A1F422B9C1", 16)
	priv.G, _ = new(big.Int).SetString("634364FC25248933D01D1993ECABD0657CC0CB2CEED7ED2E3E8AECDFCDC4A25C3B15E9E3B163ACA2984B5539181F3EFF1A5E8903D71D5B95DA4F27202B77D2C44B430BB53741A8D59A8F86887525C9F2A6A5980A195EAA7F2FF910064301DEF89D3AA213E1FAC7768D89365318E370AF54A112EFBA9246D9158386BA1B4EEFDA", 16)
	priv.Y, _ = new(big.Int).SetString("32969E5780CFE1C849A1C276D7AEB4F38A23B591739AA2FE197349AEEBD31366AEE5EB7E6C6DDB7C57D02432B30DB5AA66D9884299FAA72568944E4EEDC92EA3FBC6F39F53412FBCC563208F7C15B737AC8910DBC2D9C9B8C001E72FDC40EB694AB1F06A5A2DBD18D9E36C66F31F566742F11EC0A52E9F7B89355C02FB5D32D2", 16)
	priv.X, _ = new(big.Int).SetString("5078D4D29795CBE76D3AACFE48C9AF0BCDBEE91A", 16)

	testSignAndVerify(t, 0, &priv)
}

func TestSigningWithDegenerateKeys(t *testing.T) {
	// Signing with degenerate private keys should not cause an infinite
	// loop.
	badKeys := []struct {
		p, q, g, y, x string
	}{
		{"00", "01", "00", "00", "00"},
		{"01", "ff", "00", "00", "00"},
	}

	for i, test := range badKeys {
		priv := PrivateKey{
			PublicKey: PublicKey{
				Parameters: Parameters{
					P: fromHex(test.p),
					Q: fromHex(test.q),
					G: fromHex(test.g),
				},
				Y: fromHex(test.y),
			},
			X: fromHex(test.x),
		}

		hashed := []byte("testing")
		if _, _, err := Sign(rand.Reader, &priv, hashed); err == nil {
			t.Errorf("#%d: unexpected success", i)
		}
	}
}
<commit_msg>crypto/dsa: also use fromHex in TestSignAndVerify.<commit_after>// Copyright 2011 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 dsa

import (
	"crypto/rand"
	"math/big"
	"testing"
)

func testSignAndVerify(t *testing.T, i int, priv *PrivateKey) {
	hashed := []byte("testing")
	r, s, err := Sign(rand.Reader, priv, hashed)
	if err != nil {
		t.Errorf("%d: error signing: %s", i, err)
		return
	}

	if !Verify(&priv.PublicKey, hashed, r, s) {
		t.Errorf("%d: Verify failed", i)
	}
}

func testParameterGeneration(t *testing.T, sizes ParameterSizes, L, N int) {
	var priv PrivateKey
	params := &priv.Parameters

	err := GenerateParameters(params, rand.Reader, sizes)
	if err != nil {
		t.Errorf("%d: %s", int(sizes), err)
		return
	}

	if params.P.BitLen() != L {
		t.Errorf("%d: params.BitLen got:%d want:%d", int(sizes), params.P.BitLen(), L)
	}

	if params.Q.BitLen() != N {
		t.Errorf("%d: q.BitLen got:%d want:%d", int(sizes), params.Q.BitLen(), L)
	}

	one := new(big.Int)
	one.SetInt64(1)
	pm1 := new(big.Int).Sub(params.P, one)
	quo, rem := new(big.Int).DivMod(pm1, params.Q, new(big.Int))
	if rem.Sign() != 0 {
		t.Errorf("%d: p-1 mod q != 0", int(sizes))
	}
	x := new(big.Int).Exp(params.G, quo, params.P)
	if x.Cmp(one) == 0 {
		t.Errorf("%d: invalid generator", int(sizes))
	}

	err = GenerateKey(&priv, rand.Reader)
	if err != nil {
		t.Errorf("error generating key: %s", err)
		return
	}

	testSignAndVerify(t, int(sizes), &priv)
}

func TestParameterGeneration(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping parameter generation test in short mode")
	}

	testParameterGeneration(t, L1024N160, 1024, 160)
	testParameterGeneration(t, L2048N224, 2048, 224)
	testParameterGeneration(t, L2048N256, 2048, 256)
	testParameterGeneration(t, L3072N256, 3072, 256)
}

func fromHex(s string) *big.Int {
	result, ok := new(big.Int).SetString(s, 16)
	if !ok {
		panic(s)
	}
	return result
}

func TestSignAndVerify(t *testing.T) {
	priv := PrivateKey{
		PublicKey: PublicKey {
			Parameters: Parameters{
				P: fromHex("A9B5B793FB4785793D246BAE77E8FF63CA52F442DA763C440259919FE1BC1D6065A9350637A04F75A2F039401D49F08E066C4D275A5A65DA5684BC563C14289D7AB8A67163BFBF79D85972619AD2CFF55AB0EE77A9002B0EF96293BDD0F42685EBB2C66C327079F6C98000FBCB79AACDE1BC6F9D5C7B1A97E3D9D54ED7951FEF"),
				Q: fromHex("E1D3391245933D68A0714ED34BBCB7A1F422B9C1"),
				G: fromHex("634364FC25248933D01D1993ECABD0657CC0CB2CEED7ED2E3E8AECDFCDC4A25C3B15E9E3B163ACA2984B5539181F3EFF1A5E8903D71D5B95DA4F27202B77D2C44B430BB53741A8D59A8F86887525C9F2A6A5980A195EAA7F2FF910064301DEF89D3AA213E1FAC7768D89365318E370AF54A112EFBA9246D9158386BA1B4EEFDA"),
			},
			Y: fromHex("32969E5780CFE1C849A1C276D7AEB4F38A23B591739AA2FE197349AEEBD31366AEE5EB7E6C6DDB7C57D02432B30DB5AA66D9884299FAA72568944E4EEDC92EA3FBC6F39F53412FBCC563208F7C15B737AC8910DBC2D9C9B8C001E72FDC40EB694AB1F06A5A2DBD18D9E36C66F31F566742F11EC0A52E9F7B89355C02FB5D32D2"),
		},
		X: fromHex("5078D4D29795CBE76D3AACFE48C9AF0BCDBEE91A"),
	}

	testSignAndVerify(t, 0, &priv)
}

func TestSigningWithDegenerateKeys(t *testing.T) {
	// Signing with degenerate private keys should not cause an infinite
	// loop.
	badKeys := []struct {
		p, q, g, y, x string
	}{
		{"00", "01", "00", "00", "00"},
		{"01", "ff", "00", "00", "00"},
	}

	for i, test := range badKeys {
		priv := PrivateKey{
			PublicKey: PublicKey{
				Parameters: Parameters{
					P: fromHex(test.p),
					Q: fromHex(test.q),
					G: fromHex(test.g),
				},
				Y: fromHex(test.y),
			},
			X: fromHex(test.x),
		}

		hashed := []byte("testing")
		if _, _, err := Sign(rand.Reader, &priv, hashed); err == nil {
			t.Errorf("#%d: unexpected success", i)
		}
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright 2019 The Kythe Authors. 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 mergecmd provides the kzip command for merging archives.
package mergecmd // import "kythe.io/kythe/go/platform/tools/kzip/mergecmd"

import (
	"context"
	"flag"
	"fmt"
	"io"
	"log"
	"path/filepath"

	"kythe.io/kythe/go/platform/kzip"
	"kythe.io/kythe/go/platform/tools/kzip/flags"
	"kythe.io/kythe/go/platform/vfs"
	"kythe.io/kythe/go/util/cmdutil"

	"bitbucket.org/creachadair/stringset"
	"github.com/google/subcommands"
)

type mergeCommand struct {
	cmdutil.Info

	output   string
	append   bool
	encoding flags.EncodingFlag
}

// New creates a new subcommand for merging kzip files.
func New() subcommands.Command {
	return &mergeCommand{
		Info:     cmdutil.NewInfo("merge", "merge kzip files", "--output path kzip-file*"),
		encoding: flags.EncodingFlag{Encoding: kzip.EncodingJSON},
	}
}

// SetFlags implements the subcommands interface and provides command-specific flags
// for merging kzip files.
func (c *mergeCommand) SetFlags(fs *flag.FlagSet) {
	fs.StringVar(&c.output, "output", "", "Path to output kzip file")
	fs.BoolVar(&c.append, "append", false, "Whether to additionally merge the contents of the existing output file, if it exists")
	fs.Var(&c.encoding, "encoding", "Encoding to use on output, one of JSON, PROTO, or ALL")
}

// Execute implements the subcommands interface and merges the provided files.
func (c *mergeCommand) Execute(ctx context.Context, fs *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
	if c.output == "" {
		return c.Fail("Required --output path missing")
	}
	opt := kzip.WithEncoding(c.encoding.Encoding)
	dir, file := filepath.Split(c.output)
	if dir == "" {
		dir = "."
	}
	tmpOut, err := vfs.CreateTempFile(ctx, dir, file)
	tmpName := tmpOut.Name()
	defer func() {
		if tmpOut != nil {
			tmpOut.Close()
			vfs.Remove(ctx, tmpName)
		}
	}()
	if err != nil {
		return c.Fail("Error creating temp output: %v", err)
	}
	archives := fs.Args()
	if c.append {
		orig, err := vfs.Open(ctx, c.output)
		if err == nil {
			archives = append([]string{c.output}, archives...)
			if err := orig.Close(); err != nil {
				return c.Fail("Error closing original: %v", err)
			}
		}
	}
	if err := mergeArchives(ctx, tmpOut, archives, opt); err != nil {
		return c.Fail("Error merging archives: %v", err)
	}
	if err := vfs.Rename(ctx, tmpName, c.output); err != nil {
		return c.Fail("Error renaming tmp to output: %v", err)
	}
	return subcommands.ExitSuccess
}

func mergeArchives(ctx context.Context, out io.WriteCloser, archives []string, opts ...kzip.WriterOption) error {
	wr, err := kzip.NewWriteCloser(out, opts...)
	if err != nil {
		out.Close()
		return fmt.Errorf("error creating writer: %v", err)
	}

	filesAdded := stringset.New()
	for _, path := range archives {
		if err := mergeInto(ctx, wr, path, filesAdded); err != nil {
			wr.Close()
			return err
		}
	}

	if err := wr.Close(); err != nil {
		return fmt.Errorf("error closing writer: %v", err)
	}
	return nil
}

func mergeInto(ctx context.Context, wr *kzip.Writer, path string, filesAdded stringset.Set) error {
	f, err := vfs.Open(ctx, path)
	if err != nil {
		return fmt.Errorf("error opening archive: %v", err)
	}
	defer f.Close()

	stat, err := vfs.Stat(ctx, path)
	if err != nil {
		return err
	}
	size := stat.Size()
	if size == 0 {
		log.Printf("Skipping empty .kzip: %s", path)
		return nil
	}

	rd, err := kzip.NewReader(f, size)
	if err != nil {
		return fmt.Errorf("error creating reader: %v", err)
	}

	return rd.Scan(func(u *kzip.Unit) error {
		for _, ri := range u.Proto.RequiredInput {
			if filesAdded.Add(ri.Info.Digest) {
				r, err := rd.Open(ri.Info.Digest)
				if err != nil {
					return fmt.Errorf("error opening file: %v", err)
				}
				if _, err := wr.AddFile(r); err != nil {
					r.Close()
					return fmt.Errorf("error adding file: %v", err)
				} else if err := r.Close(); err != nil {
					return fmt.Errorf("error closing file: %v", err)
				}
			}
		}
		// TODO(schroederc): duplicate compilations with different revisions
		_, err = wr.AddUnit(u.Proto, u.Index)
		return err
	})
}
<commit_msg>fix(tooling): check error before result (#4110)<commit_after>/*
 * Copyright 2019 The Kythe Authors. 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 mergecmd provides the kzip command for merging archives.
package mergecmd // import "kythe.io/kythe/go/platform/tools/kzip/mergecmd"

import (
	"context"
	"flag"
	"fmt"
	"io"
	"log"
	"path/filepath"

	"kythe.io/kythe/go/platform/kzip"
	"kythe.io/kythe/go/platform/tools/kzip/flags"
	"kythe.io/kythe/go/platform/vfs"
	"kythe.io/kythe/go/util/cmdutil"

	"bitbucket.org/creachadair/stringset"
	"github.com/google/subcommands"
)

type mergeCommand struct {
	cmdutil.Info

	output   string
	append   bool
	encoding flags.EncodingFlag
}

// New creates a new subcommand for merging kzip files.
func New() subcommands.Command {
	return &mergeCommand{
		Info:     cmdutil.NewInfo("merge", "merge kzip files", "--output path kzip-file*"),
		encoding: flags.EncodingFlag{Encoding: kzip.EncodingJSON},
	}
}

// SetFlags implements the subcommands interface and provides command-specific flags
// for merging kzip files.
func (c *mergeCommand) SetFlags(fs *flag.FlagSet) {
	fs.StringVar(&c.output, "output", "", "Path to output kzip file")
	fs.BoolVar(&c.append, "append", false, "Whether to additionally merge the contents of the existing output file, if it exists")
	fs.Var(&c.encoding, "encoding", "Encoding to use on output, one of JSON, PROTO, or ALL")
}

// Execute implements the subcommands interface and merges the provided files.
func (c *mergeCommand) Execute(ctx context.Context, fs *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
	if c.output == "" {
		return c.Fail("Required --output path missing")
	}
	opt := kzip.WithEncoding(c.encoding.Encoding)
	dir, file := filepath.Split(c.output)
	if dir == "" {
		dir = "."
	}
	tmpOut, err := vfs.CreateTempFile(ctx, dir, file)
	if err != nil {
		return c.Fail("Error creating temp output: %v", err)
	}
	tmpName := tmpOut.Name()
	defer func() {
		if tmpOut != nil {
			tmpOut.Close()
			vfs.Remove(ctx, tmpName)
		}
	}()
	archives := fs.Args()
	if c.append {
		orig, err := vfs.Open(ctx, c.output)
		if err == nil {
			archives = append([]string{c.output}, archives...)
			if err := orig.Close(); err != nil {
				return c.Fail("Error closing original: %v", err)
			}
		}
	}
	if err := mergeArchives(ctx, tmpOut, archives, opt); err != nil {
		return c.Fail("Error merging archives: %v", err)
	}
	if err := vfs.Rename(ctx, tmpName, c.output); err != nil {
		return c.Fail("Error renaming tmp to output: %v", err)
	}
	return subcommands.ExitSuccess
}

func mergeArchives(ctx context.Context, out io.WriteCloser, archives []string, opts ...kzip.WriterOption) error {
	wr, err := kzip.NewWriteCloser(out, opts...)
	if err != nil {
		out.Close()
		return fmt.Errorf("error creating writer: %v", err)
	}

	filesAdded := stringset.New()
	for _, path := range archives {
		if err := mergeInto(ctx, wr, path, filesAdded); err != nil {
			wr.Close()
			return err
		}
	}

	if err := wr.Close(); err != nil {
		return fmt.Errorf("error closing writer: %v", err)
	}
	return nil
}

func mergeInto(ctx context.Context, wr *kzip.Writer, path string, filesAdded stringset.Set) error {
	f, err := vfs.Open(ctx, path)
	if err != nil {
		return fmt.Errorf("error opening archive: %v", err)
	}
	defer f.Close()

	stat, err := vfs.Stat(ctx, path)
	if err != nil {
		return err
	}
	size := stat.Size()
	if size == 0 {
		log.Printf("Skipping empty .kzip: %s", path)
		return nil
	}

	rd, err := kzip.NewReader(f, size)
	if err != nil {
		return fmt.Errorf("error creating reader: %v", err)
	}

	return rd.Scan(func(u *kzip.Unit) error {
		for _, ri := range u.Proto.RequiredInput {
			if filesAdded.Add(ri.Info.Digest) {
				r, err := rd.Open(ri.Info.Digest)
				if err != nil {
					return fmt.Errorf("error opening file: %v", err)
				}
				if _, err := wr.AddFile(r); err != nil {
					r.Close()
					return fmt.Errorf("error adding file: %v", err)
				} else if err := r.Close(); err != nil {
					return fmt.Errorf("error closing file: %v", err)
				}
			}
		}
		// TODO(schroederc): duplicate compilations with different revisions
		_, err = wr.AddUnit(u.Proto, u.Index)
		return err
	})
}
<|endoftext|>


<commit_before>/*
The search library provides a simple query language for searching records.

The query language features are:

 * boat whale - must contain both `boat` and `whale`
 * boat OR whale - must contain either `boat` or `whale`
 * boat whale OR shark - must contain `boat` and either `whale` or `shark`
 * boat whale NOT shark - must contain both `boat` and `whale` and not contain `shark`
 * "floating boat" whale - must contain the phrase "floating boat" and the word `whale`
 * boat whale tag:book - must contain both `boat` and `whale` and the `tag` field must contain the word `book`
 * boat tag:book OR tag:"published leaflet" - must contain the word `boat` and either the `tag` field must have the word `book` or the phrase `published leaflet`

Such queries are parsed using the QueryParser function, which returns a Query
object.  Query objects are able to search any object that implements the
Searchable interface.

*/
package search

import (
	// "log"
	"strings"
	"unicode"
)

/*
Searchable objects can be searched by a Query to see if they match.
*/
type Searchable interface {
	/*
		Contains returns true if the phrase is present in the object, optionally restricted to the given field.
	*/
	Contains(field, phrase string) (present bool)
}

/*
SearchableFunc allows functions to implement the Searchable interface.
*/
type SearchableFunc func(field, phrase string) (present bool)

/*
Contains calls the SearchableFunc
*/
func (sf SearchableFunc) Contains(field, phrase string) (present bool) {
	return sf(field, phrase)
}

/*
SearchableStringSlice makes a slice of strings Searchable.

Each string in the slice is tested against the Query and returns true if any
matches.
*/
func SearchableStringSlice(record []string) SearchableFunc {
	return func(field, phrase string) bool {
		for _, str := range record {
			if strings.Contains(str, phrase) {
				return true
			}
		}
		return false
	}
}

/*
SearchableString makes a string Searchable.

The query will be tested against the string, returning true if it matches.
*/
func SearchableString(record string) SearchableFunc {
	return func(field, phrase string) bool {
		return strings.Contains(record, phrase)
	}
}

/*
A filter function is part of a Query that executes searches.

The filter function calls Search on the Searchable interface and tells the
Query whether it matches.

`match` is true if the Searchable does match the filter.
*/
type filter func(Searchable) (match bool)

/*
A Query object is returned by QueryPraser to handle executing seraches.

The Search method takes an object implementing the Searchable inteface and
returns whether it matches the query.
*/
type Query interface {
	/*
		Execute the query against the Searchable object s.

		Match is true if the searchable object satisfies the query.
	*/
	Search(s Searchable) (match bool)
}

// filters implements the Query interface for the package
type filters []filter

// Filters default to AND - as soon as one term doesn't match, return false
func (q filters) Search(s Searchable) (result bool) {
	for _, filt := range q {
		if !filt(s) {
			// log.Printf("Search of filter %v returned false\n", i)
			return false
		}
	}
	return true
}

// mustContain returns true if the Searchable matches the field and phrase
func mustContain(field, phrase string) filter {
	// log.Printf("Adding must contain %v:%v\n", field, phrase)
	return func(s Searchable) bool {
		if s.Contains(field, phrase) {
			// log.Printf("Must contain %v:%v returns true\n", field, phrase)
			return true
		}
		// log.Printf("Must contain %v:%v returns false\n", field, phrase)
		return false
	}
}

// mustContain returns true if the Searchable does not match the field and phrase
func mustNotContain(field, phrase string) filter {
	// log.Printf("Adding must NOT contain %v:%v\n", field, phrase)
	return func(s Searchable) bool {
		if s.Contains(field, phrase) {
			return false
		}
		return true
	}
}

// orFilter tries each subfilter until one matches.  If none match it returns false
func orFilter(subfilters ...filter) filter {
	// log.Printf("Adding OR filter with %v\n", subfilters)
	return func(s Searchable) bool {
		for _, f := range subfilters {
			if f(s) {
				// log.Printf("orFilter %v returned true\n", i)
				return true
			}
		}
		return false
	}
}

// notFilter runs each subfilter as AND and then inverts the result
func notFilter(subfilters ...filter) filter {
	// log.Printf("Adding NOT filter with %v\n", subfilters)
	return func(s Searchable) bool {
		for _, f := range subfilters {
			// If the result is false, then the AND is false, so we return true
			if !f(s) {
				// log.Printf("notFilter %v returned false, returning true\n", i)
				return true
			}
		}
		// All results were true, so we return false
		// log.Printf("All not filters returned true, returning false\n")
		return false
	}
}

type queryParserFrame struct {
	filters   filters
	orPhrase  bool
	notPhrase bool
}

/*
QueryParser truns a string such as "book whale" into a Query.
*/
func QueryParser(query string) (q Query) {
	var phraseStart, phraseEnd int
	var orPhrase, notPhrase, inquote bool

	query = strings.TrimSpace(query)

	results := make(filters, 0, 5)

	stack := make([]queryParserFrame, 0, 2)

	popStack := func() {
		// Do nothing if there is nothing on the stack.
		if len(stack) == 0 {
			return
		}
		stackFrame := stack[len(stack)-1]
		// log.Printf("Popping stack: %v\n", stackFrame)
		stack = stack[:len(stack)-1]
		// Stick the nested results into the previous frame
		bracketResults := results
		results = stackFrame.filters
		orPhrase = stackFrame.orPhrase
		notPhrase = stackFrame.notPhrase

		// We have just closed brackets - now need to add the contents into the main results.
		// To do this we need to know whether they are NOT or OR or default AND
		if orPhrase {
			// Try and build an OR with the previous phrase
			if len(results) > 0 {
				previousFilter := results[len(results)-1]
				// Is this a compound OR NOT search?
				if notPhrase {
					// log.Printf("Adding in the OR with NOT the bracketResults.Search %v\n", bracketResults)
					results[len(results)-1] = orFilter(previousFilter, notFilter(bracketResults...))
				} else {
					// log.Printf("Adding in the OR with the bracketResults.Search %v\n", bracketResults)
					results[len(results)-1] = orFilter(previousFilter, bracketResults.Search)
				}
			} else {
				// Suppress the OR and search for it
				// log.Printf("Suppressing OR and adding %v as AND\n", bracketResults)
				results = append(results, bracketResults.Search)
			}
		} else if notPhrase {
			// log.Printf("Adding bracket results %v as a NOT AND\n", bracketResults)
			results = append(results, notFilter(bracketResults...))
		} else {
			// log.Printf("Adding bracket results %v as an AND\n", bracketResults)
			results = append(results, bracketResults.Search)
		}

		orPhrase = false
		notPhrase = false
	}

	pushStack := func() {
		stackFrame := queryParserFrame{
			filters:   results,
			orPhrase:  orPhrase,
			notPhrase: notPhrase,
		}
		// log.Printf("Pushing stack: %v\n", stackFrame)
		stack = append(stack, stackFrame)
		results = make(filters, 0, 5)
		orPhrase = false
		notPhrase = false
	}

	// Closure to handle any found search phrases
	// The closure ensures that the same logic is used inside and outside of the loop
	phraseHandler := func() {
		if phraseStart < phraseEnd {
			phraseValue := query[phraseStart : phraseEnd+1]
			if phraseValue == "OR" {
				// Treat the next phrase as an OR with the previous one
				orPhrase = true
			} else if phraseValue == "NOT" {
				// Treat next phrase as a must not contain
				notPhrase = true
			} else {
				fieldBreak := strings.Index(phraseValue, ":")
				var fieldName, fieldValue string
				if fieldBreak > 0 {
					fieldName = phraseValue[:fieldBreak]
					fieldValue = phraseValue[fieldBreak+1:]
					// Remove any stray quotes, handles the form title:"A book"
					fieldValue = strings.Replace(fieldValue, "'", "", -1)
					fieldValue = strings.Replace(fieldValue, "\"", "", -1)
				} else {
					fieldValue = phraseValue
				}
				if orPhrase {
					// Try and build an OR with the previous phrase
					if len(results) > 0 {
						previousFilter := results[len(results)-1]
						// Is this a compound OR NOT search?
						if notPhrase {
							results[len(results)-1] = orFilter(previousFilter, mustNotContain(fieldName, fieldValue))
						} else {
							results[len(results)-1] = orFilter(previousFilter, mustContain(fieldName, fieldValue))
						}
					} else {
						// Suppress the OR and search for it
						results = append(results, mustContain(fieldName, fieldValue))
					}
				} else if notPhrase {
					results = append(results, mustNotContain(fieldName, fieldValue))
				} else {
					results = append(results, mustContain(fieldName, fieldValue))
				}
				orPhrase = false
				notPhrase = false
			}
		}
	}

	for pos, char := range query {
		if unicode.IsSpace(char) {
			if !inquote {
				// End of a phrase, spit it out.
				phraseHandler()
				phraseStart = pos + 1
			} else {
				phraseEnd = pos
			}
		} else if pos == phraseStart {
			// Begining of a new phrase.
			// Assume we are going to consume a character
			phraseStart++
			if !inquote && (char == '"' || char == '\'') {
				inquote = true
			} else if !inquote && char == '(' {
				pushStack()
			} else if !inquote && char == ')' {
				phraseEnd = pos - 1
				phraseHandler()
				phraseStart = pos + 1
				popStack()
			} else {
				// We didn't consume a character, so keep where we are
				phraseStart--
			}
			phraseEnd = pos
		} else {
			if inquote && (char == '"' || char == '\'') {
				inquote = false
				phraseEnd = pos - 1
			} else if !inquote && (char == '"' || char == '\'') {
				// Quote part way through the phrase, e.g. title:"A book"
				inquote = true
			} else if !inquote && char == ')' {
				phraseEnd = pos - 1
				phraseHandler()
				phraseStart = pos + 1
				popStack()
			} else {
				phraseEnd = pos
			}
		}
	}
	// End of all phrases, spit it out.
	phraseHandler()

	// Close any still open brackets
	for _ = range stack {
		// log.Printf("Handling un-closed stack\n")
		popStack()
	}

	return results
}
<commit_msg>Updated query documentation to include brackets.<commit_after>/*
The search library provides a simple query language for searching records.

The query language features are:

 * boat whale - must contain both `boat` and `whale`
 * boat OR whale - must contain either `boat` or `whale`
 * boat whale OR shark - must contain `boat` and either `whale` or `shark`
 * boat whale NOT shark - must contain both `boat` and `whale` and not contain `shark`
 * "floating boat" whale - must contain the phrase "floating boat" and the word `whale`
 * boat whale tag:book - must contain both `boat` and `whale` and the `tag` field must contain `book`
 * boat tag:book OR tag:"published leaflet" - must contain the word `boat` and the `tag` field must either have `book` or the phrase `published leaflet`
 * boat OR NOT (tag:book OR tag:leaflet) - must contain 'boat' or the tag field must not contain 'book' or 'leaflet'

Such queries are parsed using the QueryParser function, which returns a Query
object.  Query objects are able to search any object that implements the
Searchable interface.

*/
package search

import (
	// "log"
	"strings"
	"unicode"
)

/*
Searchable objects can be searched by a Query to see if they match.
*/
type Searchable interface {
	/*
		Contains returns true if the phrase is present in the object, optionally restricted to the given field.
	*/
	Contains(field, phrase string) (present bool)
}

/*
SearchableFunc allows functions to implement the Searchable interface.
*/
type SearchableFunc func(field, phrase string) (present bool)

/*
Contains calls the SearchableFunc
*/
func (sf SearchableFunc) Contains(field, phrase string) (present bool) {
	return sf(field, phrase)
}

/*
SearchableStringSlice makes a slice of strings Searchable.

Each string in the slice is tested against the Query and returns true if any
matches.
*/
func SearchableStringSlice(record []string) SearchableFunc {
	return func(field, phrase string) bool {
		for _, str := range record {
			if strings.Contains(str, phrase) {
				return true
			}
		}
		return false
	}
}

/*
SearchableString makes a string Searchable.

The query will be tested against the string, returning true if it matches.
*/
func SearchableString(record string) SearchableFunc {
	return func(field, phrase string) bool {
		return strings.Contains(record, phrase)
	}
}

/*
A filter function is part of a Query that executes searches.

The filter function calls Search on the Searchable interface and tells the
Query whether it matches.

`match` is true if the Searchable does match the filter.
*/
type filter func(Searchable) (match bool)

/*
A Query object is returned by QueryPraser to handle executing seraches.

The Search method takes an object implementing the Searchable inteface and
returns whether it matches the query.
*/
type Query interface {
	/*
		Execute the query against the Searchable object s.

		Match is true if the searchable object satisfies the query.
	*/
	Search(s Searchable) (match bool)
}

// filters implements the Query interface for the package
type filters []filter

// Filters default to AND - as soon as one term doesn't match, return false
func (q filters) Search(s Searchable) (result bool) {
	for _, filt := range q {
		if !filt(s) {
			// log.Printf("Search of filter %v returned false\n", i)
			return false
		}
	}
	return true
}

// mustContain returns true if the Searchable matches the field and phrase
func mustContain(field, phrase string) filter {
	// log.Printf("Adding must contain %v:%v\n", field, phrase)
	return func(s Searchable) bool {
		if s.Contains(field, phrase) {
			// log.Printf("Must contain %v:%v returns true\n", field, phrase)
			return true
		}
		// log.Printf("Must contain %v:%v returns false\n", field, phrase)
		return false
	}
}

// mustContain returns true if the Searchable does not match the field and phrase
func mustNotContain(field, phrase string) filter {
	// log.Printf("Adding must NOT contain %v:%v\n", field, phrase)
	return func(s Searchable) bool {
		if s.Contains(field, phrase) {
			return false
		}
		return true
	}
}

// orFilter tries each subfilter until one matches.  If none match it returns false
func orFilter(subfilters ...filter) filter {
	// log.Printf("Adding OR filter with %v\n", subfilters)
	return func(s Searchable) bool {
		for _, f := range subfilters {
			if f(s) {
				// log.Printf("orFilter %v returned true\n", i)
				return true
			}
		}
		return false
	}
}

// notFilter runs each subfilter as AND and then inverts the result
func notFilter(subfilters ...filter) filter {
	// log.Printf("Adding NOT filter with %v\n", subfilters)
	return func(s Searchable) bool {
		for _, f := range subfilters {
			// If the result is false, then the AND is false, so we return true
			if !f(s) {
				// log.Printf("notFilter %v returned false, returning true\n", i)
				return true
			}
		}
		// All results were true, so we return false
		// log.Printf("All not filters returned true, returning false\n")
		return false
	}
}

type queryParserFrame struct {
	filters   filters
	orPhrase  bool
	notPhrase bool
}

/*
QueryParser truns a string such as "book whale" into a Query.
*/
func QueryParser(query string) (q Query) {
	var phraseStart, phraseEnd int
	var orPhrase, notPhrase, inquote bool

	query = strings.TrimSpace(query)

	results := make(filters, 0, 5)

	stack := make([]queryParserFrame, 0, 2)

	popStack := func() {
		// Do nothing if there is nothing on the stack.
		if len(stack) == 0 {
			return
		}
		stackFrame := stack[len(stack)-1]
		// log.Printf("Popping stack: %v\n", stackFrame)
		stack = stack[:len(stack)-1]
		// Stick the nested results into the previous frame
		bracketResults := results
		results = stackFrame.filters
		orPhrase = stackFrame.orPhrase
		notPhrase = stackFrame.notPhrase

		// We have just closed brackets - now need to add the contents into the main results.
		// To do this we need to know whether they are NOT or OR or default AND
		if orPhrase {
			// Try and build an OR with the previous phrase
			if len(results) > 0 {
				previousFilter := results[len(results)-1]
				// Is this a compound OR NOT search?
				if notPhrase {
					// log.Printf("Adding in the OR with NOT the bracketResults.Search %v\n", bracketResults)
					results[len(results)-1] = orFilter(previousFilter, notFilter(bracketResults...))
				} else {
					// log.Printf("Adding in the OR with the bracketResults.Search %v\n", bracketResults)
					results[len(results)-1] = orFilter(previousFilter, bracketResults.Search)
				}
			} else {
				// Suppress the OR and search for it
				// log.Printf("Suppressing OR and adding %v as AND\n", bracketResults)
				results = append(results, bracketResults.Search)
			}
		} else if notPhrase {
			// log.Printf("Adding bracket results %v as a NOT AND\n", bracketResults)
			results = append(results, notFilter(bracketResults...))
		} else {
			// log.Printf("Adding bracket results %v as an AND\n", bracketResults)
			results = append(results, bracketResults.Search)
		}

		orPhrase = false
		notPhrase = false
	}

	pushStack := func() {
		stackFrame := queryParserFrame{
			filters:   results,
			orPhrase:  orPhrase,
			notPhrase: notPhrase,
		}
		// log.Printf("Pushing stack: %v\n", stackFrame)
		stack = append(stack, stackFrame)
		results = make(filters, 0, 5)
		orPhrase = false
		notPhrase = false
	}

	// Closure to handle any found search phrases
	// The closure ensures that the same logic is used inside and outside of the loop
	phraseHandler := func() {
		if phraseStart < phraseEnd {
			phraseValue := query[phraseStart : phraseEnd+1]
			if phraseValue == "OR" {
				// Treat the next phrase as an OR with the previous one
				orPhrase = true
			} else if phraseValue == "NOT" {
				// Treat next phrase as a must not contain
				notPhrase = true
			} else {
				fieldBreak := strings.Index(phraseValue, ":")
				var fieldName, fieldValue string
				if fieldBreak > 0 {
					fieldName = phraseValue[:fieldBreak]
					fieldValue = phraseValue[fieldBreak+1:]
					// Remove any stray quotes, handles the form title:"A book"
					fieldValue = strings.Replace(fieldValue, "'", "", -1)
					fieldValue = strings.Replace(fieldValue, "\"", "", -1)
				} else {
					fieldValue = phraseValue
				}
				if orPhrase {
					// Try and build an OR with the previous phrase
					if len(results) > 0 {
						previousFilter := results[len(results)-1]
						// Is this a compound OR NOT search?
						if notPhrase {
							results[len(results)-1] = orFilter(previousFilter, mustNotContain(fieldName, fieldValue))
						} else {
							results[len(results)-1] = orFilter(previousFilter, mustContain(fieldName, fieldValue))
						}
					} else {
						// Suppress the OR and search for it
						results = append(results, mustContain(fieldName, fieldValue))
					}
				} else if notPhrase {
					results = append(results, mustNotContain(fieldName, fieldValue))
				} else {
					results = append(results, mustContain(fieldName, fieldValue))
				}
				orPhrase = false
				notPhrase = false
			}
		}
	}

	for pos, char := range query {
		if unicode.IsSpace(char) {
			if !inquote {
				// End of a phrase, spit it out.
				phraseHandler()
				phraseStart = pos + 1
			} else {
				phraseEnd = pos
			}
		} else if pos == phraseStart {
			// Begining of a new phrase.
			// Assume we are going to consume a character
			phraseStart++
			if !inquote && (char == '"' || char == '\'') {
				inquote = true
			} else if !inquote && char == '(' {
				pushStack()
			} else if !inquote && char == ')' {
				phraseEnd = pos - 1
				phraseHandler()
				phraseStart = pos + 1
				popStack()
			} else {
				// We didn't consume a character, so keep where we are
				phraseStart--
			}
			phraseEnd = pos
		} else {
			if inquote && (char == '"' || char == '\'') {
				inquote = false
				phraseEnd = pos - 1
			} else if !inquote && (char == '"' || char == '\'') {
				// Quote part way through the phrase, e.g. title:"A book"
				inquote = true
			} else if !inquote && char == ')' {
				phraseEnd = pos - 1
				phraseHandler()
				phraseStart = pos + 1
				popStack()
			} else {
				phraseEnd = pos
			}
		}
	}
	// End of all phrases, spit it out.
	phraseHandler()

	// Close any still open brackets
	for _ = range stack {
		// log.Printf("Handling un-closed stack\n")
		popStack()
	}

	return results
}
<|endoftext|>


<commit_before>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package charmstore

// Client exposes the functionality of the charm store, as provided
// by github.com/juju/charmrepo/csclient.Client.
type Client interface {
	// TODO(ericsnow) Replace use of Get with use of more specific API methods?

	// Get makes a GET request to the given path in the charm store. The
	// path must have a leading slash, but must not include the host
	// name or version prefix. The result is parsed as JSON into the
	// given result value, which should be a pointer to the expected
	// data, but may be nil if no result is desired.
	Get(path string, result interface{}) error
}

// TestingClient expands Client with methods needed during testing.
type TestingClient interface {
	Client

	// Put makes a PUT request to the given path in the charm store. The
	// path must have a leading slash, but must not include the host
	// name or version prefix. The given value is marshaled as JSON to
	// use as the request body.
	Put(path string, val interface{}) error

	// UploadCharm uploads the given charm to the charm store with the
	// given id, which must not specify a revision. The accepted charm
	// implementations are charm.CharmDir and charm.CharmArchive.
	//
	// UploadCharm returns the id that the charm has been given in the
	// store - this will be the same as id except the revision.
	UploadCharm(id *charm.URL, ch charm.Charm) (*charm.URL, error)

	// UploadCharmWithRevision uploads the given charm to the given id
	// in the charm store, which must contain a revision. If
	// promulgatedRevision is not -1, it specifies that the charm should
	// be marked as promulgated with that revision.
	UploadCharmWithRevision(id *charm.URL, ch charm.Charm, promulgatedRevision int) error

	// UploadBundleWithRevision uploads the given bundle to the given id
	// in the charm store, which must contain a revision. If
	// promulgatedRevision is not -1, it specifies that the charm should
	// be marked as promulgated with that revision.
	UploadBundleWithRevision()
}
<commit_msg>Add ListResources to the charm store client.<commit_after>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package charmstore

// Client exposes the functionality of the charm store, as provided
// by github.com/juju/charmrepo/csclient.Client.
type Client interface {
	// TODO(ericsnow) Replace use of Get with use of more specific API methods?

	// Get makes a GET request to the given path in the charm store. The
	// path must have a leading slash, but must not include the host
	// name or version prefix. The result is parsed as JSON into the
	// given result value, which should be a pointer to the expected
	// data, but may be nil if no result is desired.
	Get(path string, result interface{}) error

	// ListResources composes, for each of the identified charms, the
	// list of details for each of the charm's resources. Those details
	// are those associated with the specific charm revision. They
	// include the resource's metadata and revision.
	ListResources(charmURLs []charm.URL) ([][]charmresource.Resource, error)
}

// TestingClient expands Client with methods needed during testing.
type TestingClient interface {
	Client

	// Put makes a PUT request to the given path in the charm store. The
	// path must have a leading slash, but must not include the host
	// name or version prefix. The given value is marshaled as JSON to
	// use as the request body.
	Put(path string, val interface{}) error

	// UploadCharm uploads the given charm to the charm store with the
	// given id, which must not specify a revision. The accepted charm
	// implementations are charm.CharmDir and charm.CharmArchive.
	//
	// UploadCharm returns the id that the charm has been given in the
	// store - this will be the same as id except the revision.
	UploadCharm(id *charm.URL, ch charm.Charm) (*charm.URL, error)

	// UploadCharmWithRevision uploads the given charm to the given id
	// in the charm store, which must contain a revision. If
	// promulgatedRevision is not -1, it specifies that the charm should
	// be marked as promulgated with that revision.
	UploadCharmWithRevision(id *charm.URL, ch charm.Charm, promulgatedRevision int) error

	// UploadBundleWithRevision uploads the given bundle to the given id
	// in the charm store, which must contain a revision. If
	// promulgatedRevision is not -1, it specifies that the charm should
	// be marked as promulgated with that revision.
	UploadBundleWithRevision()
}
<|endoftext|>


<commit_before>package conservator

import (
	"os"
	"testing"

	"github.com/antihax/evedata/internal/nsqhelper"
	"github.com/antihax/evedata/internal/redigohelper"
	"github.com/antihax/evedata/internal/sqlhelper"
	"github.com/prometheus/common/log"
	"github.com/stretchr/testify/assert"
)

var conserv *Conservator

func TestMain(m *testing.M) {
	sql := sqlhelper.NewTestDatabase()
	// Setup a hammer service
	redis := redigohelper.ConnectRedisTestPool()
	defer redis.Close()

	conserv = NewConservator(redis, sql, nsqhelper.Test, "TEST")

	inserts := []string{
		`INSERT INTO evedata.botServices
			(botServiceID, entityID, address, authentication, type, services, options)
			VALUES
			(1, 234, "127.0.0.1:10011", "serveradmin:nothinguseful", "ts3", "auth,auth5,auth10", ""),
			(2, 567, "127.0.0.2:10011", "serveradmin:nothinguseful", "ts3", "", "")
			ON DUPLICATE KEY UPDATE botServiceID=botServiceID`,
		`INSERT INTO evedata.botChannels
			(botServiceID, channelID, services, options)
			VALUES
			(1, 12345, "kill", ""),
			(1, 12346, "locator", ""),
			(2, 12347, "kill,locator,structure", "")
			ON DUPLICATE KEY UPDATE botServiceID=botServiceID`,
		`INSERT INTO evedata.sharing
			(characterID, tokenCharacterID, entityID, types)
			VALUES
			(1123123, 24234234, 235, "kill,locator,structure"),
			(1123123, 24234234, 234, "kill"),
			(1123125, 24234235, 234, "locator"),
			(1123123, 24234234, 567, "war,locator,structure")
			ON DUPLICATE KEY UPDATE characterID=characterID`,
	}

	for _, insert := range inserts {
		if _, err := conserv.db.Exec(insert); err != nil {
			log.Fatal(err)
		}
	}

	// Run tests
	r := m.Run()
	conserv.Close()
	os.Exit(r)
}

func TestConservator(t *testing.T) {
	err := conserv.loadServices()
	assert.Nil(t, err)

	si, ok := conserv.services.Load(int32(1))
	assert.True(t, ok)
	assert.NotNil(t, si)

	service := si.(Service)
	assert.Equal(t, "ts3", service.Type)

	err = conserv.loadChannels()
	assert.Nil(t, err)
	ci, ok := conserv.channels.Load("12345")
	assert.True(t, ok)
	assert.NotNil(t, ci)

	channel := ci.(Channel)
	assert.Equal(t, "kill", channel.Services)

	err = conserv.loadShares()
	assert.Nil(t, err)

	assert.Equal(t, int32(234), conserv.notifications["kill"][int32(24234234)][0].EntityID)
	assert.Equal(t, int32(234), conserv.notifications["locator"][int32(24234235)][0].EntityID)
	assert.Equal(t, int32(567), conserv.notifications["structure"][int32(24234234)][0].EntityID)

	// Test we can properly delete entries
	conserv.db.Exec("DELETE FROM evedata.sharing")
	err = conserv.loadShares()
	assert.Nil(t, err)
	assert.Zero(t, len(conserv.notifications["structure"]))
	assert.Zero(t, len(conserv.notifications["kill"]))
	assert.Zero(t, len(conserv.notifications["locator"]))
}
<commit_msg>fix test<commit_after>package conservator

import (
	"os"
	"testing"

	"github.com/antihax/evedata/internal/nsqhelper"
	"github.com/antihax/evedata/internal/redigohelper"
	"github.com/antihax/evedata/internal/sqlhelper"
	"github.com/prometheus/common/log"
	"github.com/stretchr/testify/assert"
)

var conserv *Conservator

func TestMain(m *testing.M) {
	sql := sqlhelper.NewTestDatabase()
	// Setup a hammer service
	redis := redigohelper.ConnectRedisTestPool()
	defer redis.Close()

	conserv = NewConservator(redis, sql, nsqhelper.Test, "TEST")

	inserts := []string{
		`INSERT INTO evedata.botServices
			(botServiceID, entityID, address, authentication, type, services, options)
			VALUES
			(1, 234, "127.0.0.1:10011", "serveradmin:nothinguseful", "ts3", "auth", ""),
			(2, 567, "127.0.0.2:10011", "serveradmin:nothinguseful", "ts3", "", "")
			ON DUPLICATE KEY UPDATE botServiceID=botServiceID`,
		`INSERT INTO evedata.botChannels
			(botServiceID, channelID, services, options)
			VALUES
			(1, 12345, "kill", ""),
			(1, 12346, "locator", ""),
			(2, 12347, "kill,locator,structure", "")
			ON DUPLICATE KEY UPDATE botServiceID=botServiceID`,
		`INSERT INTO evedata.sharing
			(characterID, tokenCharacterID, entityID, types)
			VALUES
			(1123123, 24234234, 235, "kill,locator,structure"),
			(1123123, 24234234, 234, "kill"),
			(1123125, 24234235, 234, "locator"),
			(1123123, 24234234, 567, "war,locator,structure")
			ON DUPLICATE KEY UPDATE characterID=characterID`,
	}

	for _, insert := range inserts {
		if _, err := conserv.db.Exec(insert); err != nil {
			log.Fatal(err)
		}
	}

	// Run tests
	r := m.Run()
	conserv.Close()
	os.Exit(r)
}

func TestConservator(t *testing.T) {
	err := conserv.loadServices()
	assert.Nil(t, err)

	si, ok := conserv.services.Load(int32(1))
	assert.True(t, ok)
	assert.NotNil(t, si)

	service := si.(Service)
	assert.Equal(t, "ts3", service.Type)

	err = conserv.loadChannels()
	assert.Nil(t, err)
	ci, ok := conserv.channels.Load("12345")
	assert.True(t, ok)
	assert.NotNil(t, ci)

	channel := ci.(Channel)
	assert.Equal(t, "kill", channel.Services)

	err = conserv.loadShares()
	assert.Nil(t, err)

	assert.Equal(t, int32(234), conserv.notifications["kill"][int32(24234234)][0].EntityID)
	assert.Equal(t, int32(234), conserv.notifications["locator"][int32(24234235)][0].EntityID)
	assert.Equal(t, int32(567), conserv.notifications["structure"][int32(24234234)][0].EntityID)

	// Test we can properly delete entries
	conserv.db.Exec("DELETE FROM evedata.sharing")
	err = conserv.loadShares()
	assert.Nil(t, err)
	assert.Zero(t, len(conserv.notifications["structure"]))
	assert.Zero(t, len(conserv.notifications["kill"]))
	assert.Zero(t, len(conserv.notifications["locator"]))
}
<|endoftext|>


<commit_before>package jira

import (
	"fmt"
	"net/http"

	"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
)

func responseError(resp *http.Response) error {
	results := &jiradata.ErrorCollection{}
	if err := readJSON(resp.Body, results); err != nil {
		return err
	}
	if len(results.ErrorMessages) == 0 && len(results.Errors) == 0 {
		return fmt.Errorf(resp.Status)
	}
	return results
}
<commit_msg>[#141] better handling in responseError for non-json error responses<commit_after>package jira

import (
	"net/http"

	"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
)

func responseError(resp *http.Response) error {
	results := &jiradata.ErrorCollection{}
	if err := readJSON(resp.Body, results); err != nil {
		results.Status = resp.StatusCode
		results.ErrorMessages = append(results.ErrorMessages, err.Error())
	}
	if len(results.ErrorMessages) == 0 && len(results.Errors) == 0 {
		results.Status = resp.StatusCode
		results.ErrorMessages = append(results.ErrorMessages, resp.Status)
	}
	return results
}
<|endoftext|>


<commit_before>package version

import "fmt"

const (
	// VersionMajor is for an API incompatible changes
	VersionMajor = 5
	// VersionMinor is for functionality in a backwards-compatible manner
	VersionMinor = 4
	// VersionPatch is for backwards-compatible bug fixes
	VersionPatch = 2

	// VersionDev indicates development branch. Releases will be empty string.
	VersionDev = "-dev"
)

// Version is the specification version that the package types support.
var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev)
<commit_msg>v5.4.2<commit_after>package version

import "fmt"

const (
	// VersionMajor is for an API incompatible changes
	VersionMajor = 5
	// VersionMinor is for functionality in a backwards-compatible manner
	VersionMinor = 4
	// VersionPatch is for backwards-compatible bug fixes
	VersionPatch = 2

	// VersionDev indicates development branch. Releases will be empty string.
	VersionDev = ""
)

// Version is the specification version that the package types support.
var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev)
<|endoftext|>


<commit_before>package version

// Version is the version of the build.
const Version = "1.6.0"
<commit_msg>Bump to v1.6.1-dev<commit_after>package version

// Version is the version of the build.
const Version = "1.6.1-dev"
<|endoftext|>