<commit_before>package interpreter

import (
	"bytes"
	"errors"
	"os"
	"regexp"
	"strconv"
	"strings"
	"unicode"

	"../dos"
)

type StatementT struct {
	Argv     []string
	Redirect []*Redirecter
	Term     string
}

var prefix []string = []string{" 0<", " 1>", " 2>"}

var PercentFunc = map[string]func() string{
	"CD": func() string {
		wd, err := os.Getwd()
		if err == nil {
			return wd
		} else {
			return ""
		}
	},
	"ERRORLEVEL": func() string {
		return ErrorLevel
	},
}

var rxUnicode = regexp.MustCompile("^[uU]\\+?([0-9a-fA-F]+)$")

func chomp(buffer *bytes.Buffer) {
	original := buffer.String()
	buffer.Reset()
	var lastchar rune
	for i, ch := range original {
		if i > 0 {
			buffer.WriteRune(lastchar)
		}
		lastchar = ch
	}
}

func dequote(source *bytes.Buffer) string {
	var buffer bytes.Buffer

	lastchar := ' '
	lastquote := '\000'
	for {
		ch, _, err := source.ReadRune()
		if err != nil {
			break
		}
		if ch == '~' && unicode.IsSpace(lastchar) {
			if home := dos.GetHome(); home != "" {
				buffer.WriteString(home)
			} else {
				buffer.WriteRune('~')
			}
			lastchar = '~'
			continue
		}
		if ch == '%' && lastquote != '\'' {
			var nameBuf bytes.Buffer
			for {
				ch, _, err = source.ReadRune()
				if err != nil {
					buffer.WriteRune('%')
					buffer.WriteString(nameBuf.String())
					return buffer.String()
				}
				if ch == '%' {
					nameStr := nameBuf.String()
					value := os.Getenv(nameStr)
					if value != "" {
						buffer.WriteString(value)
					} else if m := rxUnicode.FindStringSubmatch(nameStr); m != nil {
						ucode, _ := strconv.ParseInt(m[1], 16, 32)
						buffer.WriteRune(rune(ucode))
					} else if f, ok := PercentFunc[nameStr]; ok {
						buffer.WriteString(f())
					} else {
						buffer.WriteRune('%')
						buffer.WriteString(nameStr)
						buffer.WriteRune('%')
					}
					break
				}
				if !unicode.IsLower(ch) && !unicode.IsUpper(ch) && !unicode.IsNumber(ch) && ch != '_' {
					source.UnreadRune()
					buffer.WriteRune('%')
					buffer.WriteString(nameBuf.String())
					break
				}
				nameBuf.WriteRune(ch)
			}
			continue
		}

		if lastquote != '\000' && ch == lastquote {
			lastquote = '\000'
		} else if (ch == '\'' || ch == '"') && lastquote == '\000' {
			lastquote = ch
			if ch == lastchar {
				buffer.WriteRune(ch)
			}
		} else {
			buffer.WriteRune(ch)
		}
		lastchar = ch
	}
	return buffer.String()
}

func terminate(statements *[]StatementT,
	isRedirected *bool,
	redirect *[]*Redirecter,
	buffer *bytes.Buffer,
	argv *[]string,
	term string) {

	var statement1 StatementT
	if buffer.Len() > 0 {
		if *isRedirected && len(*redirect) > 0 {
			(*redirect)[len(*redirect)-1].SetPath(dequote(buffer))
			*isRedirected = false
			statement1.Argv = *argv
		} else {
			statement1.Argv = append(*argv, dequote(buffer))
		}
		buffer.Reset()
	} else if len(*argv) <= 0 {
		return
	} else {
		statement1.Argv = *argv
	}
	statement1.Redirect = *redirect
	*redirect = make([]*Redirecter, 0, 3)
	*argv = make([]string, 0)
	statement1.Term = term
	*statements = append(*statements, statement1)
}

func parse1(text string) ([]StatementT, error) {
	lastquote := '\000'
	statements := make([]StatementT, 0)
	argv := make([]string, 0)
	lastchar := ' '
	var buffer bytes.Buffer
	isNextRedirect := false
	redirect := make([]*Redirecter, 0, 3)

	reader := strings.NewReader(text)
	for reader.Len() > 0 {
		ch, chSize, chErr := reader.ReadRune()
		if chSize <= 0 {
			break
		}
		if chErr != nil {
			return nil, chErr
		}
		if lastquote == '\000' {
			if ch == '"' || ch == '\'' {
				lastquote = ch
			}
		} else if ch == lastquote {
			lastquote = '\000'
		}
		if lastquote != '\000' {
			buffer.WriteRune(ch)
		} else if ch == ' ' {
			if buffer.Len() > 0 {
				if isNextRedirect && len(redirect) > 0 {
					redirect[len(redirect)-1].SetPath(dequote(&buffer))
				} else {
					argv = append(argv, dequote(&buffer))
				}
				buffer.Reset()
				isNextRedirect = false
			}
		} else if lastchar == ' ' && ch == ';' {
			terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, ";")
		} else if ch == '|' {
			if lastchar == '|' {
				statements[len(statements)-1].Term = "||"
			} else {
				terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, "|")
			}
		} else if ch == '&' {
			switch lastchar {
			case '&':
				statements[len(statements)-1].Term = "&&"
			case '|':
				statements[len(statements)-1].Term = "|&"
			case '>':
				// >&[n]
				ch2, ch2siz, ch2err := reader.ReadRune()
				if ch2err != nil {
					return nil, ch2err
				}
				if ch2siz <= 0 {
					return nil, errors.New("Too Near EOF for >&")
				}
				red := redirect[len(redirect)-1]
				switch ch2 {
				case '1':
					red.DupFrom(1)
				case '2':
					red.DupFrom(2)
				default:
					return nil, errors.New("Syntax error after >&")
				}
				isNextRedirect = false
			default:
				terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, "&")
			}
		} else if ch == '>' {
			switch lastchar {
			case '1':
				// 1>
				chomp(&buffer)
				redirect = append(redirect, NewRedirecter(1))
			case '2':
				// 2>
				chomp(&buffer)
				redirect = append(redirect, NewRedirecter(2))
			case '>':
				// >>
				if len(redirect) >= 0 {
					redirect[len(redirect)-1].SetAppend()
				}
			default:
				// >
				redirect = append(redirect, NewRedirecter(1))
			}
			isNextRedirect = true
		} else if ch == '<' {
			redirect = append(redirect, NewRedirecter(0))
			isNextRedirect = true
		} else {
			buffer.WriteRune(ch)
		}
		lastchar = ch
	}
	terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, " ")
	return statements, nil
}

// Make arrays whose elements are pipelines
func parse2(statements []StatementT) [][]StatementT {
	result := make([][]StatementT, 1)
	for _, statement1 := range statements {
		result[len(result)-1] = append(result[len(result)-1], statement1)
		switch statement1.Term {
		case "|", "|&":

		default:
			result = append(result, make([]StatementT, 0))
		}
	}
	if len(result[len(result)-1]) <= 0 {
		result = result[0 : len(result)-1]
	}
	return result
}

func Parse(text string) ([][]StatementT, error) {
	result1, err := parse1(text)
	if err != nil {
		return nil, err
	}
	result2 := parse2(result1)
	return result2, nil
}
<commit_msg>Rename variables and constants about Single-quotations<commit_after>package interpreter

import (
	"bytes"
	"errors"
	"os"
	"regexp"
	"strconv"
	"strings"
	"unicode"

	"../dos"
)

type StatementT struct {
	Argv     []string
	Redirect []*Redirecter
	Term     string
}

var prefix []string = []string{" 0<", " 1>", " 2>"}

var PercentFunc = map[string]func() string{
	"CD": func() string {
		wd, err := os.Getwd()
		if err == nil {
			return wd
		} else {
			return ""
		}
	},
	"ERRORLEVEL": func() string {
		return ErrorLevel
	},
}

var rxUnicode = regexp.MustCompile("^[uU]\\+?([0-9a-fA-F]+)$")

func chomp(buffer *bytes.Buffer) {
	original := buffer.String()
	buffer.Reset()
	var lastchar rune
	for i, ch := range original {
		if i > 0 {
			buffer.WriteRune(lastchar)
		}
		lastchar = ch
	}
}

const NOTQUOTED = '\000'

func dequote(source *bytes.Buffer) string {
	var buffer bytes.Buffer

	lastchar := ' '
	quoteNow := NOTQUOTED
	for {
		ch, _, err := source.ReadRune()
		if err != nil {
			break
		}
		if ch == '~' && unicode.IsSpace(lastchar) {
			if home := dos.GetHome(); home != "" {
				buffer.WriteString(home)
			} else {
				buffer.WriteRune('~')
			}
			lastchar = '~'
			continue
		}
		if ch == '%' && quoteNow != '\'' {
			var nameBuf bytes.Buffer
			for {
				ch, _, err = source.ReadRune()
				if err != nil {
					buffer.WriteRune('%')
					buffer.WriteString(nameBuf.String())
					return buffer.String()
				}
				if ch == '%' {
					nameStr := nameBuf.String()
					value := os.Getenv(nameStr)
					if value != "" {
						buffer.WriteString(value)
					} else if m := rxUnicode.FindStringSubmatch(nameStr); m != nil {
						ucode, _ := strconv.ParseInt(m[1], 16, 32)
						buffer.WriteRune(rune(ucode))
					} else if f, ok := PercentFunc[nameStr]; ok {
						buffer.WriteString(f())
					} else {
						buffer.WriteRune('%')
						buffer.WriteString(nameStr)
						buffer.WriteRune('%')
					}
					break
				}
				if !unicode.IsLower(ch) && !unicode.IsUpper(ch) && !unicode.IsNumber(ch) && ch != '_' {
					source.UnreadRune()
					buffer.WriteRune('%')
					buffer.WriteString(nameBuf.String())
					break
				}
				nameBuf.WriteRune(ch)
			}
			continue
		}

		if quoteNow != NOTQUOTED && ch == quoteNow {
			quoteNow = NOTQUOTED
		} else if (ch == '\'' || ch == '"') && quoteNow == NOTQUOTED {
			quoteNow = ch
			if ch == lastchar {
				buffer.WriteRune(ch)
			}
		} else {
			buffer.WriteRune(ch)
		}
		lastchar = ch
	}
	return buffer.String()
}

func terminate(statements *[]StatementT,
	isRedirected *bool,
	redirect *[]*Redirecter,
	buffer *bytes.Buffer,
	argv *[]string,
	term string) {

	var statement1 StatementT
	if buffer.Len() > 0 {
		if *isRedirected && len(*redirect) > 0 {
			(*redirect)[len(*redirect)-1].SetPath(dequote(buffer))
			*isRedirected = false
			statement1.Argv = *argv
		} else {
			statement1.Argv = append(*argv, dequote(buffer))
		}
		buffer.Reset()
	} else if len(*argv) <= 0 {
		return
	} else {
		statement1.Argv = *argv
	}
	statement1.Redirect = *redirect
	*redirect = make([]*Redirecter, 0, 3)
	*argv = make([]string, 0)
	statement1.Term = term
	*statements = append(*statements, statement1)
}

func parse1(text string) ([]StatementT, error) {
	quoteNow := NOTQUOTED
	statements := make([]StatementT, 0)
	argv := make([]string, 0)
	lastchar := ' '
	var buffer bytes.Buffer
	isNextRedirect := false
	redirect := make([]*Redirecter, 0, 3)

	reader := strings.NewReader(text)
	for reader.Len() > 0 {
		ch, chSize, chErr := reader.ReadRune()
		if chSize <= 0 {
			break
		}
		if chErr != nil {
			return nil, chErr
		}
		if quoteNow == NOTQUOTED {
			if ch == '"' || ch == '\'' {
				quoteNow = ch
			}
		} else if ch == quoteNow {
			quoteNow = NOTQUOTED
		}
		if quoteNow != NOTQUOTED {
			buffer.WriteRune(ch)
		} else if ch == ' ' {
			if buffer.Len() > 0 {
				if isNextRedirect && len(redirect) > 0 {
					redirect[len(redirect)-1].SetPath(dequote(&buffer))
				} else {
					argv = append(argv, dequote(&buffer))
				}
				buffer.Reset()
				isNextRedirect = false
			}
		} else if lastchar == ' ' && ch == ';' {
			terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, ";")
		} else if ch == '|' {
			if lastchar == '|' {
				statements[len(statements)-1].Term = "||"
			} else {
				terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, "|")
			}
		} else if ch == '&' {
			switch lastchar {
			case '&':
				statements[len(statements)-1].Term = "&&"
			case '|':
				statements[len(statements)-1].Term = "|&"
			case '>':
				// >&[n]
				ch2, ch2siz, ch2err := reader.ReadRune()
				if ch2err != nil {
					return nil, ch2err
				}
				if ch2siz <= 0 {
					return nil, errors.New("Too Near EOF for >&")
				}
				red := redirect[len(redirect)-1]
				switch ch2 {
				case '1':
					red.DupFrom(1)
				case '2':
					red.DupFrom(2)
				default:
					return nil, errors.New("Syntax error after >&")
				}
				isNextRedirect = false
			default:
				terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, "&")
			}
		} else if ch == '>' {
			switch lastchar {
			case '1':
				// 1>
				chomp(&buffer)
				redirect = append(redirect, NewRedirecter(1))
			case '2':
				// 2>
				chomp(&buffer)
				redirect = append(redirect, NewRedirecter(2))
			case '>':
				// >>
				if len(redirect) >= 0 {
					redirect[len(redirect)-1].SetAppend()
				}
			default:
				// >
				redirect = append(redirect, NewRedirecter(1))
			}
			isNextRedirect = true
		} else if ch == '<' {
			redirect = append(redirect, NewRedirecter(0))
			isNextRedirect = true
		} else {
			buffer.WriteRune(ch)
		}
		lastchar = ch
	}
	terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, " ")
	return statements, nil
}

// Make arrays whose elements are pipelines
func parse2(statements []StatementT) [][]StatementT {
	result := make([][]StatementT, 1)
	for _, statement1 := range statements {
		result[len(result)-1] = append(result[len(result)-1], statement1)
		switch statement1.Term {
		case "|", "|&":

		default:
			result = append(result, make([]StatementT, 0))
		}
	}
	if len(result[len(result)-1]) <= 0 {
		result = result[0 : len(result)-1]
	}
	return result
}

func Parse(text string) ([][]StatementT, error) {
	result1, err := parse1(text)
	if err != nil {
		return nil, err
	}
	result2 := parse2(result1)
	return result2, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"syscall"

	"gopkg.in/urfave/cli.v1"
)

const (
	kubectl          = "kubectl"
	k8sNamespace     = "gophr"
	k8sDevContext    = "minikube"
	k8sSecretsName   = "gophr-secrets"
	k8sNamespaceFlag = "--namespace=gophr"
)

// K8SPod is a kubernetes pod.
type K8SPod struct {
	Status   K8SPodStatus   `json:"status"`
	Metadata K8SPodMetadata `json:"metadata"`
}

// K8SPodList is a list of kubernetes pods.
type K8SPodList struct {
	Pods []K8SPod `json:"items"`
}

// K8SPodStatus is the status of a kubernetes pod.
type K8SPodStatus struct {
	Phase string `json:"phase"`
}

// K8SPodMetadata is the metadata of a kubernetes pod.
type K8SPodMetadata struct {
	Name string `json:"name"`
}

var (
	serviceK8SFileRegex  = regexp.MustCompile(`service\.[a-z]+\.yml$`)
	prodK8SImageURLRegex = regexp.MustCompile(`gcr\.io/([a-zA-Z0-9\-]+)/([a-zA-Z0-9\-:\.]+)`)
)

func isPersistentK8SResource(k8sfile string) bool {
	return serviceK8SFileRegex.MatchString(k8sfile)
}

func readK8SProdContext(c *cli.Context) (string, error) {
	context := c.GlobalString(flagNameK8SProdContext)
	if len(context) < 1 {
		return context, errors.New("The kubernetes production context must be specified for this command to function.")
	}

	return context, nil
}

// Returns the old kubernetes context, whether the context needs to be switched
// back, and the error.
func switchK8SContext(newK8SContext string, switchingBack bool) (string, bool, error) {
	// First, get the current context.
	output, err := exec.Command(kubectl, "config", "current-context").CombinedOutput()
	if err != nil {
		return "", false, err
	}

	// Save the old context, so that we can return it later.
	oldK8SContext := strings.TrimSpace(string(output[:]))

	// If the k8s context is already switched, then return.
	if newK8SContext == oldK8SContext {
		return oldK8SContext, false, nil
	}

	// Only say something if the context is changing.
	if switchingBack {
		startSpinner(fmt.Sprintf("Switching back to the \"%s\" kubernetes context", newK8SContext))
	} else {
		startSpinner(fmt.Sprintf("Switching to the \"%s\" kubernetes context", newK8SContext))
	}

	// Switch to the new context.
	_, err = exec.Command(kubectl, "config", "use-context", newK8SContext).CombinedOutput()
	if err != nil {
		stopSpinner(false)
		return "", false, err
	}

	stopSpinner(true)
	return oldK8SContext, true, nil
}

func runInK8S(c *cli.Context, fn func() error) error {
	var (
		err                      error
		oldK8SContext            string
		mustSwitchK8SContextBack bool

		env        = readEnvironment(c)
		k8sContext = k8sDevContext
	)

	// If the environment is prod, change the kubernetes context accordingly.
	if env == environmentProd {
		// Read the production context before continuing.
		if k8sContext, err = readK8SProdContext(c); err != nil {
			return err
		}
	}

	// Switch the kubernetes context before continuing.
	if oldK8SContext, mustSwitchK8SContextBack, err = switchK8SContext(k8sContext, false); err != nil {
		return err
	} else if mustSwitchK8SContextBack {
		// If a message was printed, add a new line for padding.
		fmt.Println()
	}

	// Make sure we have a namespace before doing anything else.
	if err = assertNamespaceInK8S(); err != nil {
		return err
	}

	// Check if the secrets are here. If not, scream & shout.
	if !secretsExistInK8S() {
		return errors.New("The gophr secrets have not been installed yet. Use `gophrctl secrets cycle` to correct that.")
	}

	// Execute fn now that the context has been switched.
	if err = fn(); err != nil {
		// Before returning with an error, return the context back to where it was.
		if mustSwitchK8SContextBack {
			if _, _, switchErr := switchK8SContext(oldK8SContext, true); switchErr != nil {
				printError("Failed to reset the kubernetes context:", switchErr)
			}
		}

		return err
	}

	// Switch the context back, error out if there was a problem switching.
	if mustSwitchK8SContextBack {
		fmt.Println()
		if _, _, err := switchK8SContext(oldK8SContext, true); err != nil {
			return err
		}
	}

	return nil
}

// TODO(skeswa): figure out a way to make this function work in every case.
func existsInK8S(k8sConfigFilePath string) bool {
	_, err := exec.Command(kubectl, k8sNamespaceFlag, "describe", "-f", k8sConfigFilePath).CombinedOutput()
	if err != nil {
		return false
	}

	return true
}

func secretsExistInK8S() bool {
	if _, err := exec.Command(kubectl, k8sNamespaceFlag, "describe", "secret", "gophr-secrets").CombinedOutput(); err != nil {
		return false
	}

	return true
}

func assertNamespaceInK8S() error {
	output, err := exec.Command(kubectl, k8sNamespaceFlag, "get", "namespaces", "--output=jsonpath={.items..metadata.name}").CombinedOutput()
	if err != nil {
		return err
	}

	// Loop through all the namespaces, and look for our namespace.
	namespaces := strings.Split(strings.TrimSpace(string(output[:])), " ")
	for _, namespace := range namespaces {
		if k8sNamespace == strings.TrimSpace(namespace) {
			return nil
		}
	}

	// If we're here then the namespace does not exist. Time to create it.
	startSpinner("Creating namespace in kubernetes")
	output, err = exec.Command(kubectl, k8sNamespaceFlag, "create", "namespace", k8sNamespace).CombinedOutput()
	if err != nil {
		stopSpinner(false)
		return err
	}

	stopSpinner(true)
	return nil
}

func applyInK8S(k8sConfigFilePath string) error {
	startSpinner(fmt.Sprintf("Applying \"%s\" in kubernetes", abbreviateK8SPath(k8sConfigFilePath)))
	output, err := exec.Command(kubectl, k8sNamespaceFlag, "apply", "-f", k8sConfigFilePath).CombinedOutput()
	if err != nil {
		stopSpinner(false)
		return newExecError(output, err)
	}

	stopSpinner(true)
	return nil
}

func createInK8S(k8sConfigFilePath string) error {
	startSpinner(fmt.Sprintf("Creating \"%s\" in kubernetes", abbreviateK8SPath(k8sConfigFilePath)))
	output, err := exec.Command(kubectl, k8sNamespaceFlag, "create", "-f", k8sConfigFilePath).CombinedOutput()
	if err != nil {
		stopSpinner(false)
		return newExecError(output, err)
	}

	stopSpinner(true)
	return nil
}

func execK8SLogs(podName string, follow bool) {
	// Find kubectl - panic if it ain't here.
	binary, err := exec.LookPath(kubectl)
	if err != nil {
		panic(err)
	}

	if follow {
		syscall.Exec(binary, []string{kubectl, k8sNamespaceFlag, "logs", "-f", podName}, os.Environ())
	} else {
		syscall.Exec(binary, []string{kubectl, k8sNamespaceFlag, "logs", podName}, os.Environ())
	}
}

func execK8SBash(podName string) {
	// Find kubectl - panic if it ain't here.
	binary, err := exec.LookPath(kubectl)
	if err != nil {
		panic(err)
	}

	syscall.Exec(binary, []string{kubectl, k8sNamespaceFlag, "exec", podName, "-i", "-t", "/bin/bash"}, os.Environ())
}

func deleteInK8S(k8sConfigFilePath string) error {
	startSpinner(fmt.Sprintf("Deleting \"%s\" in kubernetes", abbreviateK8SPath(k8sConfigFilePath)))
	output, err := exec.Command(kubectl, k8sNamespaceFlag, "delete", "-f", k8sConfigFilePath).CombinedOutput()
	if err != nil {
		stopSpinner(false)
		return newExecError(output, err)
	}

	stopSpinner(true)
	return nil
}

func getPodsInK8S() (string, error) {
	output, err := exec.Command(kubectl, k8sNamespaceFlag, "get", "pods").CombinedOutput()
	if err != nil {
		return "", err
	}

	return string(output[:]), nil
}

func filterK8SPods(moduleName string) ([]string, error) {
	output, err := exec.Command(kubectl, k8sNamespaceFlag, "get", "pods", "--selector=module="+moduleName, "--output=json").CombinedOutput()
	if err != nil {
		return nil, newExecError(output, err)
	}

	podList := K8SPodList{}
	if err = json.Unmarshal(output, &podList); err != nil {
		return nil, newExecError(output, errors.New("Could not read pod metadata"))
	}

	var runningPodNames []string
	for _, pod := range podList.Pods {
		if pod.Status.Phase == "Running" {
			runningPodNames = append(runningPodNames, pod.Metadata.Name)
		}
	}

	return runningPodNames, nil
}

func abbreviateK8SPath(k8sPath string) string {
	sep := string(os.PathSeparator)
	parts := strings.Split(k8sPath, sep)
	return strings.Join(parts[len(parts)-2:], sep)
}

func secretExistsInK8S() bool {
	_, err := exec.Command(kubectl, k8sNamespaceFlag, "describe", "secret", k8sSecretsName).CombinedOutput()
	if err != nil {
		return false
	}

	return true
}

func createSecretsInK8S(secretFilePaths []string) error {
	startSpinner("Creating secrets in kubernetes")
	args := []string{
		k8sNamespaceFlag,
		"create",
		"secret",
		"generic",
		k8sSecretsName,
	}

	for _, secretFilePath := range secretFilePaths {
		args = append(args, "--from-file="+secretFilePath)
	}

	output, err := exec.Command(kubectl, args...).CombinedOutput()
	if err != nil {
		stopSpinner(false)
		return newExecError(output, err)
	}

	stopSpinner(true)
	return nil
}

func deleteSecretsInK8S() error {
	startSpinner("Deleting secrets in kubernetes")
	output, err := exec.Command(kubectl, k8sNamespaceFlag, "delete", "secret", k8sSecretsName).CombinedOutput()
	if err != nil {
		stopSpinner(false)
		return newExecError(output, err)
	}

	stopSpinner(true)
	return nil
}

func updateProdK8SFileImage(newImageURL, k8sfilePath string) error {
	versionfileData, err := ioutil.ReadFile(k8sfilePath)
	if err != nil {
		return err
	}

	updatedVersionfileData := prodK8SImageURLRegex.ReplaceAll(versionfileData, []byte(newImageURL))
	if err = ioutil.WriteFile(k8sfilePath, updatedVersionfileData, 0644); err != nil {
		return err
	}

	return nil
}
<commit_msg>fixed the secret chicken & egg problem<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"syscall"

	"gopkg.in/urfave/cli.v1"
)

const (
	kubectl          = "kubectl"
	k8sNamespace     = "gophr"
	k8sDevContext    = "minikube"
	k8sSecretsName   = "gophr-secrets"
	k8sNamespaceFlag = "--namespace=gophr"
)

// K8SPod is a kubernetes pod.
type K8SPod struct {
	Status   K8SPodStatus   `json:"status"`
	Metadata K8SPodMetadata `json:"metadata"`
}

// K8SPodList is a list of kubernetes pods.
type K8SPodList struct {
	Pods []K8SPod `json:"items"`
}

// K8SPodStatus is the status of a kubernetes pod.
type K8SPodStatus struct {
	Phase string `json:"phase"`
}

// K8SPodMetadata is the metadata of a kubernetes pod.
type K8SPodMetadata struct {
	Name string `json:"name"`
}

var (
	serviceK8SFileRegex  = regexp.MustCompile(`service\.[a-z]+\.yml$`)
	prodK8SImageURLRegex = regexp.MustCompile(`gcr\.io/([a-zA-Z0-9\-]+)/([a-zA-Z0-9\-:\.]+)`)
)

func isPersistentK8SResource(k8sfile string) bool {
	return serviceK8SFileRegex.MatchString(k8sfile)
}

func readK8SProdContext(c *cli.Context) (string, error) {
	context := c.GlobalString(flagNameK8SProdContext)
	if len(context) < 1 {
		return context, errors.New("The kubernetes production context must be specified for this command to function.")
	}

	return context, nil
}

// Returns the old kubernetes context, whether the context needs to be switched
// back, and the error.
func switchK8SContext(newK8SContext string, switchingBack bool) (string, bool, error) {
	// First, get the current context.
	output, err := exec.Command(kubectl, "config", "current-context").CombinedOutput()
	if err != nil {
		return "", false, err
	}

	// Save the old context, so that we can return it later.
	oldK8SContext := strings.TrimSpace(string(output[:]))

	// If the k8s context is already switched, then return.
	if newK8SContext == oldK8SContext {
		return oldK8SContext, false, nil
	}

	// Only say something if the context is changing.
	if switchingBack {
		startSpinner(fmt.Sprintf("Switching back to the \"%s\" kubernetes context", newK8SContext))
	} else {
		startSpinner(fmt.Sprintf("Switching to the \"%s\" kubernetes context", newK8SContext))
	}

	// Switch to the new context.
	_, err = exec.Command(kubectl, "config", "use-context", newK8SContext).CombinedOutput()
	if err != nil {
		stopSpinner(false)
		return "", false, err
	}

	stopSpinner(true)
	return oldK8SContext, true, nil
}

func runInK8S(c *cli.Context, fn func() error) error {
	var (
		err                      error
		oldK8SContext            string
		mustSwitchK8SContextBack bool

		env        = readEnvironment(c)
		k8sContext = k8sDevContext
	)

	// If the environment is prod, change the kubernetes context accordingly.
	if env == environmentProd {
		// Read the production context before continuing.
		if k8sContext, err = readK8SProdContext(c); err != nil {
			return err
		}
	}

	// Switch the kubernetes context before continuing.
	if oldK8SContext, mustSwitchK8SContextBack, err = switchK8SContext(k8sContext, false); err != nil {
		return err
	} else if mustSwitchK8SContextBack {
		// If a message was printed, add a new line for padding.
		fmt.Println()
	}

	// Make sure we have a namespace before doing anything else.
	if err = assertNamespaceInK8S(); err != nil {
		return err
	}

	// Check if the secrets are here. If not, scream & shout.
	if c.Command.FullName() != "secrets cycle" && !secretsExistInK8S() {
		return errors.New("The gophr secrets have not been installed yet. Use `gophrctl secrets cycle` to correct that.")
	}

	// Execute fn now that the context has been switched.
	if err = fn(); err != nil {
		// Before returning with an error, return the context back to where it was.
		if mustSwitchK8SContextBack {
			if _, _, switchErr := switchK8SContext(oldK8SContext, true); switchErr != nil {
				printError("Failed to reset the kubernetes context:", switchErr)
			}
		}

		return err
	}

	// Switch the context back, error out if there was a problem switching.
	if mustSwitchK8SContextBack {
		fmt.Println()
		if _, _, err := switchK8SContext(oldK8SContext, true); err != nil {
			return err
		}
	}

	return nil
}

// TODO(skeswa): figure out a way to make this function work in every case.
func existsInK8S(k8sConfigFilePath string) bool {
	_, err := exec.Command(kubectl, k8sNamespaceFlag, "describe", "-f", k8sConfigFilePath).CombinedOutput()
	if err != nil {
		return false
	}

	return true
}

func secretsExistInK8S() bool {
	if _, err := exec.Command(kubectl, k8sNamespaceFlag, "describe", "secret", "gophr-secrets").CombinedOutput(); err != nil {
		return false
	}

	return true
}

func assertNamespaceInK8S() error {
	output, err := exec.Command(kubectl, k8sNamespaceFlag, "get", "namespaces", "--output=jsonpath={.items..metadata.name}").CombinedOutput()
	if err != nil {
		return err
	}

	// Loop through all the namespaces, and look for our namespace.
	namespaces := strings.Split(strings.TrimSpace(string(output[:])), " ")
	for _, namespace := range namespaces {
		if k8sNamespace == strings.TrimSpace(namespace) {
			return nil
		}
	}

	// If we're here then the namespace does not exist. Time to create it.
	startSpinner("Creating namespace in kubernetes")
	output, err = exec.Command(kubectl, k8sNamespaceFlag, "create", "namespace", k8sNamespace).CombinedOutput()
	if err != nil {
		stopSpinner(false)
		return err
	}

	stopSpinner(true)
	return nil
}

func applyInK8S(k8sConfigFilePath string) error {
	startSpinner(fmt.Sprintf("Applying \"%s\" in kubernetes", abbreviateK8SPath(k8sConfigFilePath)))
	output, err := exec.Command(kubectl, k8sNamespaceFlag, "apply", "-f", k8sConfigFilePath).CombinedOutput()
	if err != nil {
		stopSpinner(false)
		return newExecError(output, err)
	}

	stopSpinner(true)
	return nil
}

func createInK8S(k8sConfigFilePath string) error {
	startSpinner(fmt.Sprintf("Creating \"%s\" in kubernetes", abbreviateK8SPath(k8sConfigFilePath)))
	output, err := exec.Command(kubectl, k8sNamespaceFlag, "create", "-f", k8sConfigFilePath).CombinedOutput()
	if err != nil {
		stopSpinner(false)
		return newExecError(output, err)
	}

	stopSpinner(true)
	return nil
}

func execK8SLogs(podName string, follow bool) {
	// Find kubectl - panic if it ain't here.
	binary, err := exec.LookPath(kubectl)
	if err != nil {
		panic(err)
	}

	if follow {
		syscall.Exec(binary, []string{kubectl, k8sNamespaceFlag, "logs", "-f", podName}, os.Environ())
	} else {
		syscall.Exec(binary, []string{kubectl, k8sNamespaceFlag, "logs", podName}, os.Environ())
	}
}

func execK8SBash(podName string) {
	// Find kubectl - panic if it ain't here.
	binary, err := exec.LookPath(kubectl)
	if err != nil {
		panic(err)
	}

	syscall.Exec(binary, []string{kubectl, k8sNamespaceFlag, "exec", podName, "-i", "-t", "/bin/bash"}, os.Environ())
}

func deleteInK8S(k8sConfigFilePath string) error {
	startSpinner(fmt.Sprintf("Deleting \"%s\" in kubernetes", abbreviateK8SPath(k8sConfigFilePath)))
	output, err := exec.Command(kubectl, k8sNamespaceFlag, "delete", "-f", k8sConfigFilePath).CombinedOutput()
	if err != nil {
		stopSpinner(false)
		return newExecError(output, err)
	}

	stopSpinner(true)
	return nil
}

func getPodsInK8S() (string, error) {
	output, err := exec.Command(kubectl, k8sNamespaceFlag, "get", "pods").CombinedOutput()
	if err != nil {
		return "", err
	}

	return string(output[:]), nil
}

func filterK8SPods(moduleName string) ([]string, error) {
	output, err := exec.Command(kubectl, k8sNamespaceFlag, "get", "pods", "--selector=module="+moduleName, "--output=json").CombinedOutput()
	if err != nil {
		return nil, newExecError(output, err)
	}

	podList := K8SPodList{}
	if err = json.Unmarshal(output, &podList); err != nil {
		return nil, newExecError(output, errors.New("Could not read pod metadata"))
	}

	var runningPodNames []string
	for _, pod := range podList.Pods {
		if pod.Status.Phase == "Running" {
			runningPodNames = append(runningPodNames, pod.Metadata.Name)
		}
	}

	return runningPodNames, nil
}

func abbreviateK8SPath(k8sPath string) string {
	sep := string(os.PathSeparator)
	parts := strings.Split(k8sPath, sep)
	return strings.Join(parts[len(parts)-2:], sep)
}

func secretExistsInK8S() bool {
	_, err := exec.Command(kubectl, k8sNamespaceFlag, "describe", "secret", k8sSecretsName).CombinedOutput()
	if err != nil {
		return false
	}

	return true
}

func createSecretsInK8S(secretFilePaths []string) error {
	startSpinner("Creating secrets in kubernetes")
	args := []string{
		k8sNamespaceFlag,
		"create",
		"secret",
		"generic",
		k8sSecretsName,
	}

	for _, secretFilePath := range secretFilePaths {
		args = append(args, "--from-file="+secretFilePath)
	}

	output, err := exec.Command(kubectl, args...).CombinedOutput()
	if err != nil {
		stopSpinner(false)
		return newExecError(output, err)
	}

	stopSpinner(true)
	return nil
}

func deleteSecretsInK8S() error {
	startSpinner("Deleting secrets in kubernetes")
	output, err := exec.Command(kubectl, k8sNamespaceFlag, "delete", "secret", k8sSecretsName).CombinedOutput()
	if err != nil {
		stopSpinner(false)
		return newExecError(output, err)
	}

	stopSpinner(true)
	return nil
}

func updateProdK8SFileImage(newImageURL, k8sfilePath string) error {
	versionfileData, err := ioutil.ReadFile(k8sfilePath)
	if err != nil {
		return err
	}

	updatedVersionfileData := prodK8SImageURLRegex.ReplaceAll(versionfileData, []byte(newImageURL))
	if err = ioutil.WriteFile(k8sfilePath, updatedVersionfileData, 0644); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package inout

import (
	"time"

	"github.com/JREAMLU/core/global"
	"github.com/astaxie/beego"

	"github.com/beego/i18n"
)

//logic service
const (
	SUCCESS           = 0
	DATAPARAMSILLEGAL = 10000
	METAPARAMSILLEGAL = 15000
	LOGICILLEGAL      = 20000
	SYSTEMILLEGAL     = 30000
)

type Output struct {
	Meta       MetaList    `json:"meta"`
	StatusCode int         `json:"status_code"`
	Message    interface{} `json:"message"`
	Data       interface{} `json:"data"`
}

type MetaList struct {
	RequestId string    `json:"Request-Id"`
	UpdatedAt time.Time `json:"updated_at"`
	Timezone  string    `json:"timezone"`
}

/**
 *	@auther		jream.lu
 *	@intro		出参成功
 *	@logic
 *	@todo		返回值
 *	@params		params ...interface{}	切片指针
 *	@return 	?
 */
func Suc(data interface{}, requestID string) Output {
	var op Output
	op.Meta.RequestId = requestID
	op.Meta.UpdatedAt = time.Now()
	op.Meta.Timezone = beego.AppConfig.String("Timezone")

	op.StatusCode = SUCCESS
	op.Message = i18n.Tr(global.Lang, "outputParams.SUCCESS")
	op.Data = data

	return op
}

func Fail(msg interface{}, status string, requestID string) Output {
	var op Output
	op.Meta.RequestId = requestID
	op.Meta.UpdatedAt = time.Now()
	op.Meta.Timezone = beego.AppConfig.String("Timezone")

	switch status {
	case "SUCCESS":
		op.StatusCode = SUCCESS
	case "DATAPARAMSILLEGAL":
		op.StatusCode = DATAPARAMSILLEGAL
	case "METAPARAMSILLEGAL":
		op.StatusCode = METAPARAMSILLEGAL
	case "LOGICILLEGAL":
		op.StatusCode = LOGICILLEGAL
	case "SYSTEMILLEGAL":
		op.StatusCode = SYSTEMILLEGAL
	}

	op.Message = msg
	op.Data = make(map[string]interface{})

	return op
}
<commit_msg>update meta mesg<commit_after>package inout

import (
	"time"

	"github.com/JREAMLU/core/global"
	"github.com/astaxie/beego"

	"github.com/beego/i18n"
)

//logic service
const (
	SUCCESS           = 0
	DATAPARAMSILLEGAL = 10000
	METAPARAMSILLEGAL = 15000
	LOGICILLEGAL      = 20000
	SYSTEMILLEGAL     = 30000
)

type Output struct {
	Meta       MetaList    `json:"meta"`
	StatusCode int         `json:"statusCode"`
	Message    interface{} `json:"message"`
	Data       interface{} `json:"data"`
}

type MetaList struct {
	RequestId string    `json:"RequestID"`
	UpdatedAt time.Time `json:"updatedAT"`
	Timezone  string    `json:"timezone"`
}

/**
 *	@auther		jream.lu
 *	@intro		出参成功
 *	@logic
 *	@todo		返回值
 *	@params		params ...interface{}	切片指针
 *	@return 	?
 */
func Suc(data interface{}, requestID string) Output {
	var op Output
	op.Meta.RequestId = requestID
	op.Meta.UpdatedAt = time.Now()
	op.Meta.Timezone = beego.AppConfig.String("Timezone")

	op.StatusCode = SUCCESS
	op.Message = i18n.Tr(global.Lang, "outputParams.SUCCESS")
	op.Data = data

	return op
}

func Fail(msg interface{}, status string, requestID string) Output {
	var op Output
	op.Meta.RequestId = requestID
	op.Meta.UpdatedAt = time.Now()
	op.Meta.Timezone = beego.AppConfig.String("Timezone")

	switch status {
	case "SUCCESS":
		op.StatusCode = SUCCESS
	case "DATAPARAMSILLEGAL":
		op.StatusCode = DATAPARAMSILLEGAL
	case "METAPARAMSILLEGAL":
		op.StatusCode = METAPARAMSILLEGAL
	case "LOGICILLEGAL":
		op.StatusCode = LOGICILLEGAL
	case "SYSTEMILLEGAL":
		op.StatusCode = SYSTEMILLEGAL
	}

	op.Message = msg
	op.Data = make(map[string]interface{})

	return op
}
<|endoftext|>


<commit_before>// Package export handles exporting parts of the repo to other directories.
// This is useful if, for example, one wanted to separate out part of
// their repo with all dependencies.
package export

import (
	"os"
	"path"
	"strings"

	"gopkg.in/op/go-logging.v1"

	"github.com/thought-machine/please/src/core"
	"github.com/thought-machine/please/src/fs"
	"github.com/thought-machine/please/src/gc"
	"github.com/thought-machine/please/src/parse"
)

var log = logging.MustGetLogger("export")

// ToDir exports a set of targets to the given directory.
// It dies on any errors.
func ToDir(state *core.BuildState, dir string, targets []core.BuildLabel) {
	done := map[*core.BuildTarget]bool{}
	for _, target := range targets {
		export(state.Graph, dir, state.Graph.TargetOrDie(target), done)
	}
	// Now write all the build files
	packages := map[*core.Package]bool{}
	for target := range done {
		packages[state.Graph.PackageOrDie(target.Label)] = true
	}
	for pkg := range packages {
		if pkg.Name == parse.InternalPackageName {
			continue // This isn't a real package to be copied
		}
		dest := path.Join(dir, pkg.Filename)
		if err := fs.RecursiveCopy(pkg.Filename, dest, 0); err != nil {
			log.Fatalf("Failed to copy BUILD file %s: %s\n", pkg.Filename, err)
		}
		// Now rewrite the unused targets out of it
		victims := []string{}
		for _, target := range pkg.AllTargets() {
			if !done[target] && !target.HasParent() {
				victims = append(victims, target.Label.Name)
			}
		}
		if err := gc.RewriteFile(state, dest, victims); err != nil {
			log.Fatalf("Failed to rewrite BUILD file: %s\n", err)
		}
	}
	// Write any preloaded build defs as well; preloaded subincludes should be fine though.
	for _, preload := range state.Config.Parse.PreloadBuildDefs {
		if err := fs.RecursiveCopy(preload, path.Join(dir, preload), 0); err != nil {
			log.Fatalf("Failed to copy preloaded build def %s: %s", preload, err)
		}
	}
}

// export implements the logic of ToDir, but prevents repeating targets.
func export(graph *core.BuildGraph, dir string, target *core.BuildTarget, done map[*core.BuildTarget]bool) {
	if done[target] {
		return
	}
	for _, src := range target.AllSources() {
		if _, ok := src.Label(); !ok { // We'll handle these dependencies later
			for _, p := range src.FullPaths(graph) {
				if !strings.HasPrefix(p, "/") { // Don't copy system file deps.
					if err := fs.RecursiveCopy(p, path.Join(dir, p), 0); err != nil {
						log.Fatalf("Error copying file: %s\n", err)
					}
				}
			}
		}
	}
	done[target] = true
	for _, dep := range target.Dependencies() {
		export(graph, dir, dep, done)
	}
	for _, subinclude := range graph.PackageOrDie(target.Label).Subincludes {
		export(graph, dir, graph.TargetOrDie(subinclude), done)
	}
	if parent := target.Parent(graph); parent != nil && parent != target {
		export(graph, dir, parent, done)
	}
}

// Outputs exports the outputs of a target.
func Outputs(state *core.BuildState, dir string, targets []core.BuildLabel) {
	for _, label := range targets {
		target := state.Graph.TargetOrDie(label)
		for _, out := range target.Outputs() {
			fullPath := path.Join(dir, out)
			outDir := path.Dir(fullPath)
			if err := os.MkdirAll(outDir, core.DirPermissions); err != nil {
				log.Fatalf("Failed to create export dir %s: %s", outDir, err)
			}
			if err := fs.RecursiveCopy(path.Join(target.OutDir(), out), fullPath, target.OutMode()|0200); err != nil {
				log.Fatalf("Failed to copy export file: %s", err)
			}
		}
	}
}
<commit_msg>Export data as well (#2294)<commit_after>// Package export handles exporting parts of the repo to other directories.
// This is useful if, for example, one wanted to separate out part of
// their repo with all dependencies.
package export

import (
	"os"
	"path"
	"strings"

	"gopkg.in/op/go-logging.v1"

	"github.com/thought-machine/please/src/core"
	"github.com/thought-machine/please/src/fs"
	"github.com/thought-machine/please/src/gc"
	"github.com/thought-machine/please/src/parse"
)

var log = logging.MustGetLogger("export")

// ToDir exports a set of targets to the given directory.
// It dies on any errors.
func ToDir(state *core.BuildState, dir string, targets []core.BuildLabel) {
	done := map[*core.BuildTarget]bool{}
	for _, target := range targets {
		export(state.Graph, dir, state.Graph.TargetOrDie(target), done)
	}
	// Now write all the build files
	packages := map[*core.Package]bool{}
	for target := range done {
		packages[state.Graph.PackageOrDie(target.Label)] = true
	}
	for pkg := range packages {
		if pkg.Name == parse.InternalPackageName {
			continue // This isn't a real package to be copied
		}
		dest := path.Join(dir, pkg.Filename)
		if err := fs.RecursiveCopy(pkg.Filename, dest, 0); err != nil {
			log.Fatalf("Failed to copy BUILD file %s: %s\n", pkg.Filename, err)
		}
		// Now rewrite the unused targets out of it
		victims := []string{}
		for _, target := range pkg.AllTargets() {
			if !done[target] && !target.HasParent() {
				victims = append(victims, target.Label.Name)
			}
		}
		if err := gc.RewriteFile(state, dest, victims); err != nil {
			log.Fatalf("Failed to rewrite BUILD file: %s\n", err)
		}
	}
	// Write any preloaded build defs as well; preloaded subincludes should be fine though.
	for _, preload := range state.Config.Parse.PreloadBuildDefs {
		if err := fs.RecursiveCopy(preload, path.Join(dir, preload), 0); err != nil {
			log.Fatalf("Failed to copy preloaded build def %s: %s", preload, err)
		}
	}
}

// export implements the logic of ToDir, but prevents repeating targets.
func export(graph *core.BuildGraph, dir string, target *core.BuildTarget, done map[*core.BuildTarget]bool) {
	if done[target] {
		return
	}
	for _, src := range append(target.AllSources(), target.AllData()...) {
		if _, ok := src.Label(); !ok { // We'll handle these dependencies later
			for _, p := range src.FullPaths(graph) {
				if !strings.HasPrefix(p, "/") { // Don't copy system file deps.
					if err := fs.RecursiveCopy(p, path.Join(dir, p), 0); err != nil {
						log.Fatalf("Error copying file: %s\n", err)
					}
				}
			}
		}
	}
	done[target] = true
	for _, dep := range target.Dependencies() {
		export(graph, dir, dep, done)
	}
	for _, subinclude := range graph.PackageOrDie(target.Label).Subincludes {
		export(graph, dir, graph.TargetOrDie(subinclude), done)
	}
	if parent := target.Parent(graph); parent != nil && parent != target {
		export(graph, dir, parent, done)
	}
}

// Outputs exports the outputs of a target.
func Outputs(state *core.BuildState, dir string, targets []core.BuildLabel) {
	for _, label := range targets {
		target := state.Graph.TargetOrDie(label)
		for _, out := range target.Outputs() {
			fullPath := path.Join(dir, out)
			outDir := path.Dir(fullPath)
			if err := os.MkdirAll(outDir, core.DirPermissions); err != nil {
				log.Fatalf("Failed to create export dir %s: %s", outDir, err)
			}
			if err := fs.RecursiveCopy(path.Join(target.OutDir(), out), fullPath, target.OutMode()|0200); err != nil {
				log.Fatalf("Failed to copy export file: %s", err)
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Shivaram Lingamneni <slingamn@cs.stanford.edu>
// released under the MIT license

package irc

import (
	"errors"
	"sync"
)

var (
	InvalidChannelName = errors.New("Invalid channel name")
	NoSuchChannel      = errors.New("No such channel")
	ChannelNameInUse   = errors.New("Channel name in use")
)

type channelManagerEntry struct {
	channel *Channel
	// this is a refcount for joins, so we can avoid a race where we incorrectly
	// think the channel is empty (without holding a lock across the entire Channel.Join()
	// call)
	pendingJoins int
}

// ChannelManager keeps track of all the channels on the server,
// providing synchronization for creation of new channels on first join,
// cleanup of empty channels on last part, and renames.
type ChannelManager struct {
	sync.RWMutex // tier 2
	chans        map[string]*channelManagerEntry
}

// NewChannelManager returns a new ChannelManager.
func NewChannelManager() *ChannelManager {
	return &ChannelManager{
		chans: make(map[string]*channelManagerEntry),
	}
}

// Get returns an existing channel with name equivalent to `name`, or nil
func (cm *ChannelManager) Get(name string) *Channel {
	name, err := CasefoldChannel(name)
	if err == nil {
		cm.RLock()
		defer cm.RUnlock()
		return cm.chans[name].channel
	}
	return nil
}

// Join causes `client` to join the channel named `name`, creating it if necessary.
func (cm *ChannelManager) Join(client *Client, name string, key string) error {
	server := client.server
	casefoldedName, err := CasefoldChannel(name)
	if err != nil || len(casefoldedName) > server.Limits().ChannelLen {
		return NoSuchChannel
	}

	cm.Lock()
	entry := cm.chans[casefoldedName]
	if entry == nil {
		// XXX give up the lock to check for a registration, then check again
		// to see if we need to create the channel. we could solve this by doing LoadChannel
		// outside the lock initially on every join, so this is best thought of as an
		// optimization to avoid that.
		cm.Unlock()
		info := client.server.channelRegistry.LoadChannel(casefoldedName)
		cm.Lock()
		entry = cm.chans[casefoldedName]
		if entry == nil {
			entry = &channelManagerEntry{
				channel:      NewChannel(server, name, true, info),
				pendingJoins: 0,
			}
			cm.chans[casefoldedName] = entry
		}
	}
	entry.pendingJoins += 1
	cm.Unlock()

	entry.channel.Join(client, key)

	cm.maybeCleanup(entry, true)

	return nil
}

func (cm *ChannelManager) maybeCleanup(entry *channelManagerEntry, afterJoin bool) {
	cm.Lock()
	defer cm.Unlock()

	if entry.channel == nil {
		return
	}
	if afterJoin {
		entry.pendingJoins -= 1
	}
	// TODO(slingamn) right now, registered channels cannot be cleaned up.
	// this is because once ChannelManager becomes the source of truth about a channel,
	// we can't move the source of truth back to the database unless we do an ACID
	// store while holding the ChannelManager's Lock(). This is pending more decisions
	// about where the database transaction lock fits into the overall lock model.
	if !entry.channel.IsRegistered() && entry.channel.IsEmpty() && entry.pendingJoins == 0 {
		// reread the name, handling the case where the channel was renamed
		casefoldedName := entry.channel.NameCasefolded()
		delete(cm.chans, casefoldedName)
		// invalidate the entry (otherwise, a subsequent cleanup attempt could delete
		// a valid, distinct entry under casefoldedName):
		entry.channel = nil
	}
}

// Part parts `client` from the channel named `name`, deleting it if it's empty.
func (cm *ChannelManager) Part(client *Client, name string, message string) error {
	casefoldedName, err := CasefoldChannel(name)
	if err != nil {
		return NoSuchChannel
	}

	cm.RLock()
	entry := cm.chans[casefoldedName]
	cm.RUnlock()

	if entry == nil {
		return NoSuchChannel
	}
	entry.channel.Part(client, message)
	cm.maybeCleanup(entry, false)
	return nil
}

// Rename renames a channel (but does not notify the members)
func (cm *ChannelManager) Rename(name string, newname string) error {
	cfname, err := CasefoldChannel(name)
	if err != nil {
		return NoSuchChannel
	}

	cfnewname, err := CasefoldChannel(newname)
	if err != nil {
		return InvalidChannelName
	}

	cm.Lock()
	defer cm.Unlock()

	if cm.chans[cfnewname] != nil {
		return ChannelNameInUse
	}
	entry := cm.chans[cfname]
	if entry == nil {
		return NoSuchChannel
	}
	delete(cm.chans, cfname)
	cm.chans[cfnewname] = entry
	entry.channel.setName(newname)
	entry.channel.setNameCasefolded(cfnewname)
	return nil

}

// Len returns the number of channels
func (cm *ChannelManager) Len() int {
	cm.RLock()
	defer cm.RUnlock()
	return len(cm.chans)
}

// Channels returns a slice containing all current channels
func (cm *ChannelManager) Channels() (result []*Channel) {
	cm.RLock()
	defer cm.RUnlock()
	for _, entry := range cm.chans {
		result = append(result, entry.channel)
	}
	return
}
<commit_msg>fix a wide-ranging assortment of crash bugs<commit_after>// Copyright (c) 2017 Shivaram Lingamneni <slingamn@cs.stanford.edu>
// released under the MIT license

package irc

import (
	"errors"
	"sync"
)

var (
	InvalidChannelName = errors.New("Invalid channel name")
	NoSuchChannel      = errors.New("No such channel")
	ChannelNameInUse   = errors.New("Channel name in use")
)

type channelManagerEntry struct {
	channel *Channel
	// this is a refcount for joins, so we can avoid a race where we incorrectly
	// think the channel is empty (without holding a lock across the entire Channel.Join()
	// call)
	pendingJoins int
}

// ChannelManager keeps track of all the channels on the server,
// providing synchronization for creation of new channels on first join,
// cleanup of empty channels on last part, and renames.
type ChannelManager struct {
	sync.RWMutex // tier 2
	chans        map[string]*channelManagerEntry
}

// NewChannelManager returns a new ChannelManager.
func NewChannelManager() *ChannelManager {
	return &ChannelManager{
		chans: make(map[string]*channelManagerEntry),
	}
}

// Get returns an existing channel with name equivalent to `name`, or nil
func (cm *ChannelManager) Get(name string) *Channel {
	name, err := CasefoldChannel(name)
	if err == nil {
		cm.RLock()
		defer cm.RUnlock()
		entry := cm.chans[name]
		if entry != nil {
			return entry.channel
		}
	}
	return nil
}

// Join causes `client` to join the channel named `name`, creating it if necessary.
func (cm *ChannelManager) Join(client *Client, name string, key string) error {
	server := client.server
	casefoldedName, err := CasefoldChannel(name)
	if err != nil || len(casefoldedName) > server.Limits().ChannelLen {
		return NoSuchChannel
	}

	cm.Lock()
	entry := cm.chans[casefoldedName]
	if entry == nil {
		// XXX give up the lock to check for a registration, then check again
		// to see if we need to create the channel. we could solve this by doing LoadChannel
		// outside the lock initially on every join, so this is best thought of as an
		// optimization to avoid that.
		cm.Unlock()
		info := client.server.channelRegistry.LoadChannel(casefoldedName)
		cm.Lock()
		entry = cm.chans[casefoldedName]
		if entry == nil {
			entry = &channelManagerEntry{
				channel:      NewChannel(server, name, true, info),
				pendingJoins: 0,
			}
			cm.chans[casefoldedName] = entry
		}
	}
	entry.pendingJoins += 1
	cm.Unlock()

	entry.channel.Join(client, key)

	cm.maybeCleanup(entry, true)

	return nil
}

func (cm *ChannelManager) maybeCleanup(entry *channelManagerEntry, afterJoin bool) {
	cm.Lock()
	defer cm.Unlock()

	if entry.channel == nil {
		return
	}
	if afterJoin {
		entry.pendingJoins -= 1
	}
	// TODO(slingamn) right now, registered channels cannot be cleaned up.
	// this is because once ChannelManager becomes the source of truth about a channel,
	// we can't move the source of truth back to the database unless we do an ACID
	// store while holding the ChannelManager's Lock(). This is pending more decisions
	// about where the database transaction lock fits into the overall lock model.
	if !entry.channel.IsRegistered() && entry.channel.IsEmpty() && entry.pendingJoins == 0 {
		// reread the name, handling the case where the channel was renamed
		casefoldedName := entry.channel.NameCasefolded()
		delete(cm.chans, casefoldedName)
		// invalidate the entry (otherwise, a subsequent cleanup attempt could delete
		// a valid, distinct entry under casefoldedName):
		entry.channel = nil
	}
}

// Part parts `client` from the channel named `name`, deleting it if it's empty.
func (cm *ChannelManager) Part(client *Client, name string, message string) error {
	casefoldedName, err := CasefoldChannel(name)
	if err != nil {
		return NoSuchChannel
	}

	cm.RLock()
	entry := cm.chans[casefoldedName]
	cm.RUnlock()

	if entry == nil {
		return NoSuchChannel
	}
	entry.channel.Part(client, message)
	cm.maybeCleanup(entry, false)
	return nil
}

// Rename renames a channel (but does not notify the members)
func (cm *ChannelManager) Rename(name string, newname string) error {
	cfname, err := CasefoldChannel(name)
	if err != nil {
		return NoSuchChannel
	}

	cfnewname, err := CasefoldChannel(newname)
	if err != nil {
		return InvalidChannelName
	}

	cm.Lock()
	defer cm.Unlock()

	if cm.chans[cfnewname] != nil {
		return ChannelNameInUse
	}
	entry := cm.chans[cfname]
	if entry == nil {
		return NoSuchChannel
	}
	delete(cm.chans, cfname)
	cm.chans[cfnewname] = entry
	entry.channel.setName(newname)
	entry.channel.setNameCasefolded(cfnewname)
	return nil

}

// Len returns the number of channels
func (cm *ChannelManager) Len() int {
	cm.RLock()
	defer cm.RUnlock()
	return len(cm.chans)
}

// Channels returns a slice containing all current channels
func (cm *ChannelManager) Channels() (result []*Channel) {
	cm.RLock()
	defer cm.RUnlock()
	for _, entry := range cm.chans {
		result = append(result, entry.channel)
	}
	return
}
<|endoftext|>


<commit_before>// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package validator

import "container/heap"

// EndTimeHeap orders validators by EndTime from earliest to latest.
type EndTimeHeap []*Validator

func (h *EndTimeHeap) Len() int                 { return len(*h) }
func (h *EndTimeHeap) Less(i, j int) bool       { return (*h)[i].EndTime().Before((*h)[j].EndTime()) }
func (h *EndTimeHeap) Swap(i, j int)            { (*h)[i], (*h)[j] = (*h)[j], (*h)[i] }
func (h *EndTimeHeap) Add(validator *Validator) { heap.Push(h, validator) }
func (h *EndTimeHeap) Peek() *Validator         { return (*h)[0] }
func (h *EndTimeHeap) Remove() *Validator       { return heap.Pop(h).(*Validator) }
func (h *EndTimeHeap) Push(x interface{})       { *h = append(*h, x.(*Validator)) }
func (h *EndTimeHeap) Pop() interface{} {
	newLen := len(*h) - 1
	val := (*h)[newLen]
	(*h)[newLen] = nil
	*h = (*h)[:newLen]
	return val
}
<commit_msg>Remove EndTimeHeap (#1986)<commit_after><|endoftext|>


<commit_before>package client

import (
	"net/http"
	"net/url"
	"sort"
	"testing"

	"github.com/stretchr/testify/assert"
	got "github.com/taskcluster/go-got"
)

func testScopeIntersection(scopesA, scopesB, scopesAB []string) func(*testing.T) {
	return func(t *testing.T) {
		sort.Strings(scopesAB)
		result := intersectScopes(scopesA, scopesB)
		sort.Strings(result)
		assert.EqualValues(t, scopesAB, result, "intersection failed")
		result = intersectScopes(scopesB, scopesA)
		sort.Strings(result)
		assert.EqualValues(t, scopesAB, result, "reversing failed")
	}
}

func TestIntersectScopes(t *testing.T) {
	t.Run("empty tests", testScopeIntersection([]string{}, []string{}, []string{}))

	t.Run("A + B = A,B", testScopeIntersection(
		[]string{"A"}, []string{"B"}, []string{"A", "B"},
	))
	t.Run("A + B,C = A,B,C", testScopeIntersection(
		[]string{"A"}, []string{"B", "C"}, []string{"A", "B", "C"},
	))
	t.Run("A,C + B,C = A,B,C", testScopeIntersection(
		[]string{"A", "C"}, []string{"B", "C"}, []string{"A", "B", "C"},
	))
	t.Run("a* + b* = a*,b*", testScopeIntersection(
		[]string{"a*"}, []string{"b*"}, []string{"a*", "b*"},
	))
	t.Run("a* + a* = a*", testScopeIntersection(
		[]string{"a*"}, []string{"a*"}, []string{"a*"},
	))
	t.Run("a* + ab* = a*", testScopeIntersection(
		[]string{"a*"}, []string{"ab*"}, []string{"a*"},
	))
	t.Run("ac* + ab* = ac*,ab*", testScopeIntersection(
		[]string{"ac*"}, []string{"ab*"}, []string{"ac*", "ab*"},
	))
	t.Run("ac*,ab + ab* = ac*,ab*", testScopeIntersection(
		[]string{"ac*", "ab"}, []string{"ab*"}, []string{"ac*", "ab*"},
	))
}

func testAuthorizer(secret string, clientScopes, requiredScopes, authorizedScopes []string, expectOk bool) func(*testing.T) {
	return func(t *testing.T) {
		u, _ := url.Parse("https://auth.taskcluster.net/v1/test-authenticate")
		a := NewAuthorizer(func() (string, string, string, error) {
			return "tester", secret, "", nil
		})
		if authorizedScopes != nil {
			a = a.WithAuthorizedScopes(authorizedScopes...)
		}
		g := got.New()
		req := g.Post(u.String(), nil)
		req.JSON(map[string]interface{}{
			"clientScopes":   clientScopes,
			"requiredScopes": requiredScopes,
		})
		authorization, err := a.SignHeader(req.Method, u, nil)
		assert.NoError(t, err, "SignHeader failed")
		req.Header.Set("Authorization", authorization)
		res, err := req.Send()
		if expectOk {
			assert.NoError(t, err, "request failed")
			if e, ok := err.(got.BadResponseCodeError); ok {
				t.Error(string(e.Body))
			}
			if err == nil {
				assert.Equal(t, http.StatusOK, res.StatusCode, "expected 200 ok")
			}
		} else {
			if _, ok := err.(got.BadResponseCodeError); !ok {
				assert.Fail(t, "Expected bad response code")
			}
		}
	}
}

func TestAuthorizer(t *testing.T) {
	t.Run("empty scopes", testAuthorizer("no-secret", []string{}, []string{}, nil, true))
	t.Run("a, b", testAuthorizer("no-secret", []string{"a", "b"}, []string{}, nil, true))
	t.Run("a required", testAuthorizer("no-secret", []string{"a", "b"}, []string{"a"}, nil, true))
	t.Run("aa cover by a*", testAuthorizer("no-secret", []string{"a*", "b"}, []string{"aa"}, nil, true))
	t.Run("authorizedScopes", testAuthorizer("no-secret", []string{"a", "b"}, []string{"a"}, []string{"a"}, true))
	t.Run("authorizedScopes w. aa*", testAuthorizer("no-secret", []string{"a*", "b"}, []string{"aa"}, []string{"aa*"}, true))
	t.Run("authorizedScopes fail", testAuthorizer("no-secret", []string{"a*", "b"}, []string{"aa"}, []string{"a"}, false))
	t.Run("a, b - wrong key", testAuthorizer("wrong-secret", []string{"a", "b"}, []string{}, nil, false))
}
<commit_msg>Added SignURL tests for Authorizer<commit_after>package client

import (
	"net/http"
	"net/url"
	"sort"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	got "github.com/taskcluster/go-got"
)

func testScopeIntersection(scopesA, scopesB, scopesAB []string) func(*testing.T) {
	return func(t *testing.T) {
		sort.Strings(scopesAB)
		result := intersectScopes(scopesA, scopesB)
		sort.Strings(result)
		assert.EqualValues(t, scopesAB, result, "intersection failed")
		result = intersectScopes(scopesB, scopesA)
		sort.Strings(result)
		assert.EqualValues(t, scopesAB, result, "reversing failed")
	}
}

func TestIntersectScopes(t *testing.T) {
	t.Run("empty tests", testScopeIntersection([]string{}, []string{}, []string{}))

	t.Run("A + B = A,B", testScopeIntersection(
		[]string{"A"}, []string{"B"}, []string{"A", "B"},
	))
	t.Run("A + B,C = A,B,C", testScopeIntersection(
		[]string{"A"}, []string{"B", "C"}, []string{"A", "B", "C"},
	))
	t.Run("A,C + B,C = A,B,C", testScopeIntersection(
		[]string{"A", "C"}, []string{"B", "C"}, []string{"A", "B", "C"},
	))
	t.Run("a* + b* = a*,b*", testScopeIntersection(
		[]string{"a*"}, []string{"b*"}, []string{"a*", "b*"},
	))
	t.Run("a* + a* = a*", testScopeIntersection(
		[]string{"a*"}, []string{"a*"}, []string{"a*"},
	))
	t.Run("a* + ab* = a*", testScopeIntersection(
		[]string{"a*"}, []string{"ab*"}, []string{"a*"},
	))
	t.Run("ac* + ab* = ac*,ab*", testScopeIntersection(
		[]string{"ac*"}, []string{"ab*"}, []string{"ac*", "ab*"},
	))
	t.Run("ac*,ab + ab* = ac*,ab*", testScopeIntersection(
		[]string{"ac*", "ab"}, []string{"ab*"}, []string{"ac*", "ab*"},
	))
}

func testAuthorizerSignHeader(secret string, clientScopes, requiredScopes, authorizedScopes []string, expectOk bool) func(*testing.T) {
	return func(t *testing.T) {
		u, _ := url.Parse("https://auth.taskcluster.net/v1/test-authenticate")
		a := NewAuthorizer(func() (string, string, string, error) {
			return "tester", secret, "", nil
		})
		if authorizedScopes != nil {
			a = a.WithAuthorizedScopes(authorizedScopes...)
		}
		g := got.New()
		req := g.Post(u.String(), nil)
		req.JSON(map[string]interface{}{
			"clientScopes":   clientScopes,
			"requiredScopes": requiredScopes,
		})
		authorization, err := a.SignHeader(req.Method, u, nil)
		assert.NoError(t, err, "SignHeader failed")
		req.Header.Set("Authorization", authorization)
		res, err := req.Send()
		if expectOk {
			assert.NoError(t, err, "request failed")
			if e, ok := err.(got.BadResponseCodeError); ok {
				t.Error(string(e.Body))
			}
			if err == nil {
				assert.Equal(t, http.StatusOK, res.StatusCode, "expected 200 ok")
			}
		} else {
			if _, ok := err.(got.BadResponseCodeError); !ok {
				assert.Fail(t, "Expected bad response code")
			}
		}
	}
}

func TestAuthorizerSignHeader(t *testing.T) {
	t.Run("empty scopes", testAuthorizerSignHeader("no-secret", []string{}, []string{}, nil, true))
	t.Run("a, b", testAuthorizerSignHeader("no-secret", []string{"a", "b"}, []string{}, nil, true))
	t.Run("a required", testAuthorizerSignHeader("no-secret", []string{"a", "b"}, []string{"a"}, nil, true))
	t.Run("aa cover by a*", testAuthorizerSignHeader("no-secret", []string{"a*", "b"}, []string{"aa"}, nil, true))
	t.Run("authorizedScopes", testAuthorizerSignHeader("no-secret", []string{"a", "b"}, []string{"a"}, []string{"a"}, true))
	t.Run("authorizedScopes w. aa*", testAuthorizerSignHeader("no-secret", []string{"a*", "b"}, []string{"aa"}, []string{"aa*"}, true))
	t.Run("authorizedScopes fail", testAuthorizerSignHeader("no-secret", []string{"a*", "b"}, []string{"aa"}, []string{"a"}, false))
	t.Run("a, b - wrong key", testAuthorizerSignHeader("wrong-secret", []string{"a", "b"}, []string{}, nil, false))
}

func testAuthorizerSignURL(secret string, authorizedScopes []string, expectOk bool) func(*testing.T) {
	return func(t *testing.T) {
		u, _ := url.Parse("https://auth.taskcluster.net/v1/test-authenticate-get")
		a := NewAuthorizer(func() (string, string, string, error) {
			return "tester", secret, "", nil
		})
		if authorizedScopes != nil {
			a = a.WithAuthorizedScopes(authorizedScopes...)
		}
		u2, err := a.SignURL(u, 15*time.Minute)
		assert.NoError(t, err, "SignURL failed")
		g := got.New()
		res, err := g.Get(u2.String()).Send()
		if expectOk {
			assert.NoError(t, err, "request failed")
			if e, ok := err.(got.BadResponseCodeError); ok {
				t.Error(string(e.Body))
			}
			if err == nil {
				assert.Equal(t, http.StatusOK, res.StatusCode, "expected 200 ok")
			}
		} else {
			if _, ok := err.(got.BadResponseCodeError); !ok {
				assert.Fail(t, "Expected bad response code")
			}
		}
	}
}

func TestAuthorizerSignURL(t *testing.T) {
	t.Run("simple", testAuthorizerSignURL("no-secret", nil, true))
	t.Run("wrong secret", testAuthorizerSignURL("wrong-secret", nil, false))
	t.Run("authorizedScopes", testAuthorizerSignURL("no-secret", []string{"test:auth*"}, true))
}
<|endoftext|>


<commit_before>// Copyright (C) 2018 Nippon Telegraph and Telephone Corporation.
//
// 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 version

import "fmt"

const MAJOR uint = 2
const MINOR uint = 30
const PATCH uint = 0

var COMMIT string = ""
var IDENTIFIER string = ""
var METADATA string = ""

func Version() string {
	var suffix string = ""
	if len(IDENTIFIER) > 0 {
		suffix = fmt.Sprintf("-%s", IDENTIFIER)
	}

	if len(COMMIT) > 0 || len(METADATA) > 0 {
		suffix = suffix + "+"
	}

	if len(COMMIT) > 0 {
		suffix = fmt.Sprintf("%s"+"commit.%s", suffix, COMMIT)

	}

	if len(METADATA) > 0 {
		if len(COMMIT) > 0 {
			suffix = suffix + "."
		}
		suffix = suffix + METADATA
	}

	return fmt.Sprintf("%d.%d.%d%s", MAJOR, MINOR, PATCH, suffix)
}
<commit_msg>v2.31.0<commit_after>// Copyright (C) 2018 Nippon Telegraph and Telephone Corporation.
//
// 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 version

import "fmt"

const MAJOR uint = 2
const MINOR uint = 31
const PATCH uint = 0

var COMMIT string = ""
var IDENTIFIER string = ""
var METADATA string = ""

func Version() string {
	var suffix string = ""
	if len(IDENTIFIER) > 0 {
		suffix = fmt.Sprintf("-%s", IDENTIFIER)
	}

	if len(COMMIT) > 0 || len(METADATA) > 0 {
		suffix = suffix + "+"
	}

	if len(COMMIT) > 0 {
		suffix = fmt.Sprintf("%s"+"commit.%s", suffix, COMMIT)

	}

	if len(METADATA) > 0 {
		if len(COMMIT) > 0 {
			suffix = suffix + "."
		}
		suffix = suffix + METADATA
	}

	return fmt.Sprintf("%d.%d.%d%s", MAJOR, MINOR, PATCH, suffix)
}
<|endoftext|>


<commit_before>package techniques

import "../"

type empty struct {}

type Basic struct {
	rules []*bonsai.Rule
}

func (b *Basic) Learn(rules []*bonsai.Rule) {
	b.rules = rules
}

func (b *Basic) Transform(node *bonsai.Node, matched *map[*bonsai.Rule]bool, mismatched *map[*bonsai.Rule]bool) (children []*bonsai.Node, techniques []bonsai.Technique, continueUsing bool) {
	continueUsing = true

	transformations := make(chan []*bonsai.Node, 1)
	notTransformed := make(chan empty)

	for _, rule := range b.rules {
		go func() {
			if (*matched)[rule] || (*mismatched)[rule] {
				notTransformed <- empty{}
			} else {
				matches, matchedConditions := b.matchesChildren(rule, node)
				if matches && b.matchesVariables(matchedConditions) {
					(*matched)[rule] = true
					transformation := b.transform(rule, matchedConditions)
					if transformation != nil {
						transformations <- transformation
					} else {
						notTransformed <- empty{}
					}
				} else {
					(*mismatched)[rule] = true
					notTransformed <- empty{}
				}
			}
		}()
	}

	for _ = range b.rules {
		select {
		case transformation := <-transformations:
			children = transformation
			return
		case <-notTransformed:
		}
	}

	return
}

func (b *Basic) matchesChildren(rule *bonsai.Rule, node *bonsai.Node) (matches bool, matchedConditions map[*bonsai.Node][]chan *bonsai.Condition) {
	if rule.ConditionsAreOrdered != node.ChildrenAreOrdered && !rule.TopLevel {
		return
	}

	if node.ChildrenAreOrdered {
		matches, matchedConditions = b.matchesChildrenInOrder(rule, node, 0, 0)
		return
	}

	matchedConditions = make(map[*bonsai.Node][]chan *bonsai.Condition)

	preventedCount := 0
	prevented := make(chan bool)

	singlyMatchedConditions := make(map[*bonsai.Node]chan *bonsai.Condition)
	singlyMatchedNodes := make(map[*bonsai.Condition]chan *bonsai.Node)
	singlyMatchedNodeChildConditions := make(map[*bonsai.Node]map[*bonsai.Condition]*map[*bonsai.Node][]chan *bonsai.Condition)

	for _, child := range node.Children {
		singlyMatchedConditions[child] = make(chan *bonsai.Condition, len(rule.Conditions))
		singlyMatchedNodeChildConditions[child] = make(map[*bonsai.Condition]*map[*bonsai.Node][]chan *bonsai.Condition)
	}

	multiplyMatchedConditions := make([]*bonsai.Condition, 0, len(rule.Conditions))

	for index := range node.Children {
		b.initMatchedCondition(&matchedConditions, node, index, rule)
	}

	for _, condition := range rule.Conditions {
		if condition.PreventsMatch {
			for index, child := range node.Children {
				go func() {
					preventing, childMatchedConditions := b.matchesNode(&condition, child)
					if preventing {
						b.merge(&matchedConditions, &childMatchedConditions)
						if condition.Variable != "" {
							b.storeMatchedCondition(&matchedConditions, node, index, &condition, rule)
							preventing = false
						}
					}
					prevented <- preventing
				}()
				preventedCount++
			}
		} else if !condition.CreatesNode && !condition.MatchesMultipleNodes {
			singlyMatchedNodes[&condition] = make(chan *bonsai.Node, len(node.Children))
			preventedCount++
			go func() {
				childrenDone := make(chan empty)
				for _, child := range node.Children {
					go func() {
						if matchesNode, childMatchedConditions := b.matchesNode(&condition, child) ; matchesNode {
							singlyMatchedNodeChildConditions[child][&condition] = &childMatchedConditions;
							singlyMatchedConditions[child] <- &condition
							singlyMatchedNodes[&condition] <- node
						}
						childrenDone <- empty{}
					}()
				}
				for _ = range node.Children { <-childrenDone }
				select {
				case matched := <-singlyMatchedNodes[&condition]:
					singlyMatchedNodes[&condition] <- matched
					prevented <- false
				default:
					prevented <- true
				}
			}()
		} else if condition.MatchesMultipleNodes {
			multiplyMatchedConditions[len(multiplyMatchedConditions)] = &condition
		}
	}
	for i := 0; i < preventedCount; i++ {
		if <-prevented {
			matchedConditions = nil
			return
		}
	}

	singlyMatchedConditionLists := make(map[*bonsai.Node][]*bonsai.Condition)
	for matchedNode, conditionChan := range singlyMatchedConditions {
		select {
		case condition := <-conditionChan:
			conditionChan <- condition

			singlyMatchedConditionLists[matchedNode] = make([]*bonsai.Condition, 0, len(rule.Conditions))
			for condition = range conditionChan {
				singlyMatchedConditionLists[matchedNode][len(singlyMatchedConditionLists[matchedNode])] = condition
			}
		default:
		}
	}

	pairings := b.findPairings(singlyMatchedConditionLists, make(map[*bonsai.Condition]bool))
	if pairings == nil {
		matchedConditions = nil
		return
	}

	childrenDone := make(chan empty, len(node.Children))
	for index, child := range node.Children {
		go func() {
			if pairings[child] != nil {
				if pairings[child].RemovesNode || pairings[child].Variable != "" {
					b.storeMatchedCondition(&matchedConditions, node, index, pairings[child], rule)
				}
				b.merge(&matchedConditions, singlyMatchedNodeChildConditions[child][pairings[child]])
			} else {
				multiplyMatchedNodeChildConditions := make(chan map[*bonsai.Node][]chan *bonsai.Condition, len(multiplyMatchedConditions))
				conditionsDone := make(chan empty, len(multiplyMatchedConditions))
				for _, condition := range multiplyMatchedConditions {
					go func() {
						if childMatches, childMatchedConditions := b.matchesNode(condition, child) ; childMatches {
							multiplyMatchedNodeChildConditions <- childMatchedConditions
						} else {
							conditionsDone <- empty{}
						}
					}()
				}
				for _, condition := range multiplyMatchedConditions {
					select {
					case childMatchedConditions := <-multiplyMatchedNodeChildConditions:
						if condition.RemovesNode || condition.Variable != "" {
							b.storeMatchedCondition(&childMatchedConditions, node, index, condition, rule)
						}
						b.merge(&matchedConditions, &childMatchedConditions)
						break
					case <-conditionsDone:
					}
				}
			}
			childrenDone <- empty{}
		}()
	}
	for _ = range node.Children {
		<-childrenDone
	}

	// TODO: include creating conditions
	// TODO: don't match if rule.MustMatchAllNodes and we don't match all nodes

	return
}

func (b *Basic) findPairings(matchedConditions map[*bonsai.Node][]*bonsai.Condition, ignored map[*bonsai.Condition]bool) (pairings map[*bonsai.Node]*bonsai.Condition) {
	pairings = make(map[*bonsai.Node]*bonsai.Condition)
	for node, conditions := range matchedConditions {
		matched := make([]*bonsai.Condition, len(conditions))
		for _, condition := range conditions {
			if !ignored[condition] {
				matched[len(matched)] = condition
			}
		}

		if len(matched) > 1 {
			delete(matchedConditions, node)
			for _, condition := range matched {
				ignored[condition] = true
				if pairings = b.findPairings(matchedConditions, ignored) ; pairings != nil {
					pairings[node] = condition
					return
				}
				ignored[condition] = false
			}
			return
		} else if len(matched) == 1 {
			pairings[node] = matched[0]
		}
	}
	return
}

func (b *Basic) matchesNode(condition *bonsai.Condition, node *bonsai.Node) (matches bool, matchedConditions map[*bonsai.Node][]chan *bonsai.Condition) {
	// TODO: document that ** means to matched either ordered conditions or unordered conditions
	//				- can not have child conditions
	//				- can not require matching all nodes
	//				- useful for **:* to match any number of any kind of node
	//				- allows *: and *:: to be used for matching only ordered or only unordered
	if condition.NodeType == "**" {
		matches, matchedConditions = true, make(map[*bonsai.Node][]chan *bonsai.Condition)
	} else if condition.NodeType == node.Label || condition.NodeType == "*" {
		matches, matchedConditions = b.matchesChildren(&condition.ChildRule, node)
	}

	// TODO: compare values

	return
}

func (b *Basic) matchesChildrenInOrder(rule *bonsai.Rule, node *bonsai.Node, condition_i int, child_i int) (matches bool, matchedConditions map[*bonsai.Node][]chan *bonsai.Condition) {
	if condition_i == len(rule.Conditions) {
		matches = !rule.MustMatchAllNodes || child_i == len(node.Children)
		if matches {
			matchedConditions = make(map[*bonsai.Node][]chan *bonsai.Condition)
		}
		return
	}

	condition := &rule.Conditions[condition_i]

	if child_i == len(node.Children) {
		if condition.PreventsMatch || condition.CreatesNode || condition.MatchesMultipleNodes {
			matches, matchedConditions = b.matchesChildrenInOrder(rule, node, condition_i + 1, child_i)
			if matches && condition.CreatesNode {
				b.storeMatchedCondition(&matchedConditions, node, child_i, condition, rule)
			}
		}
		return
	}

	child := node.Children[child_i]

	if condition.PreventsMatch {
		if prevents, _ := b.matchesNode(condition, child) ; prevents {
			if condition.MatchesMultipleNodes {
				matches, matchedConditions = b.matchesChildrenInOrder(rule, node, condition_i + 1, child_i)
			}
			return
		}

		if condition.MatchesMultipleNodes {
			matches, matchedConditions = b.matchesChildrenInOrder(rule, node, condition_i, child_i + 1)
			if matches {
				return
			}
		}

		matches, matchedConditions = b.matchesChildrenInOrder(rule, node, condition_i + 1, child_i + 1)
		return
	}

	if condition.CreatesNode {
		matches, matchedConditions = b.matchesChildrenInOrder(rule, node, condition_i + 1, child_i)
		if matches {
			b.storeMatchedCondition(&matchedConditions, node, child_i, condition, rule)
		}
		return
	}

	if nodeMatches, childMatchedConditions := b.matchesNode(condition, child) ; nodeMatches {
		if condition.MatchesMultipleNodes {
			matches, matchedConditions = b.matchesChildrenInOrder(rule, node, condition_i, child_i + 1)
			if matches {
				b.merge(&matchedConditions, &childMatchedConditions)
				if condition.RemovesNode {
					b.storeMatchedCondition(&matchedConditions, node, child_i, condition, rule)
				}
				return
			}
		}

		matches, matchedConditions = b.matchesChildrenInOrder(rule, node, condition_i + 1, child_i + 1)
		if matches {
			b.merge(&matchedConditions, &childMatchedConditions)
			if condition.RemovesNode {
				b.storeMatchedCondition(&matchedConditions, node, child_i, condition, rule)
			}
		}
	}

	if condition.MatchesMultipleNodes {
		matches, matchedConditions = b.matchesChildrenInOrder(rule, node, condition_i + 1, child_i)
	}
	return
}

func (b *Basic) matchesVariables(matchedConditions map[*bonsai.Node][]chan *bonsai.Condition) (matches bool) {
	// TODO: make sure variables match
	return
}

func (b *Basic) transform(rule *bonsai.Rule, matchedConditions map[*bonsai.Node][]chan *bonsai.Condition) (children []*bonsai.Node) {
	// TODO: transform node based on matched conditions
	return
}

func (b *Basic) initMatchedCondition(matchedConditions *map[*bonsai.Node][]chan *bonsai.Condition, parent *bonsai.Node, child_i int, rule *bonsai.Rule) {
	if (*matchedConditions)[parent] == nil {
		(*matchedConditions)[parent] = make([]chan *bonsai.Condition, len(parent.Children) + 1)
	}
	if (*matchedConditions)[parent][child_i] == nil {
		(*matchedConditions)[parent][child_i] = make(chan *bonsai.Condition, len(rule.Conditions))
	}
}

func (b *Basic) storeMatchedCondition(matchedConditions *map[*bonsai.Node][]chan *bonsai.Condition, parent *bonsai.Node, child_i int, condition *bonsai.Condition, rule *bonsai.Rule) {
	b.initMatchedCondition(matchedConditions, parent, child_i, rule)
	(*matchedConditions)[parent][child_i] <- condition
}

func (b *Basic) merge(destination *map[*bonsai.Node][]chan *bonsai.Condition, source *map[*bonsai.Node][]chan *bonsai.Condition) {
	for node, chans := range *source {
		(*destination)[node] = chans
	}
}
<commit_msg>could have gotten wrong condition with child matched conditions<commit_after>package techniques

import "../"

type empty struct {}

type Basic struct {
	rules []*bonsai.Rule
}

func (b *Basic) Learn(rules []*bonsai.Rule) {
	b.rules = rules
}

func (b *Basic) Transform(node *bonsai.Node, matched *map[*bonsai.Rule]bool, mismatched *map[*bonsai.Rule]bool) (children []*bonsai.Node, techniques []bonsai.Technique, continueUsing bool) {
	continueUsing = true

	transformations := make(chan []*bonsai.Node, 1)
	notTransformed := make(chan empty)

	for _, rule := range b.rules {
		go func() {
			if (*matched)[rule] || (*mismatched)[rule] {
				notTransformed <- empty{}
			} else {
				matches, matchedConditions := b.matchesChildren(rule, node)
				if matches && b.matchesVariables(matchedConditions) {
					(*matched)[rule] = true
					transformation := b.transform(rule, matchedConditions)
					if transformation != nil {
						transformations <- transformation
					} else {
						notTransformed <- empty{}
					}
				} else {
					(*mismatched)[rule] = true
					notTransformed <- empty{}
				}
			}
		}()
	}

	for _ = range b.rules {
		select {
		case transformation := <-transformations:
			children = transformation
			return
		case <-notTransformed:
		}
	}

	return
}

func (b *Basic) matchesChildren(rule *bonsai.Rule, node *bonsai.Node) (matches bool, matchedConditions map[*bonsai.Node][]chan *bonsai.Condition) {
	if rule.ConditionsAreOrdered != node.ChildrenAreOrdered && !rule.TopLevel {
		return
	}

	if node.ChildrenAreOrdered {
		matches, matchedConditions = b.matchesChildrenInOrder(rule, node, 0, 0)
		return
	}

	matchedConditions = make(map[*bonsai.Node][]chan *bonsai.Condition)

	preventedCount := 0
	prevented := make(chan bool)

	singlyMatchedConditions := make(map[*bonsai.Node]chan *bonsai.Condition)
	singlyMatchedNodes := make(map[*bonsai.Condition]chan *bonsai.Node)
	singlyMatchedNodeChildConditions := make(map[*bonsai.Node]map[*bonsai.Condition]*map[*bonsai.Node][]chan *bonsai.Condition)

	for _, child := range node.Children {
		singlyMatchedConditions[child] = make(chan *bonsai.Condition, len(rule.Conditions))
		singlyMatchedNodeChildConditions[child] = make(map[*bonsai.Condition]*map[*bonsai.Node][]chan *bonsai.Condition)
	}

	multiplyMatchedConditions := make([]*bonsai.Condition, 0, len(rule.Conditions))

	for index := range node.Children {
		b.initMatchedCondition(&matchedConditions, node, index, rule)
	}

	for _, condition := range rule.Conditions {
		if condition.PreventsMatch {
			for index, child := range node.Children {
				go func() {
					preventing, childMatchedConditions := b.matchesNode(&condition, child)
					if preventing {
						b.merge(&matchedConditions, &childMatchedConditions)
						if condition.Variable != "" {
							b.storeMatchedCondition(&matchedConditions, node, index, &condition, rule)
							preventing = false
						}
					}
					prevented <- preventing
				}()
				preventedCount++
			}
		} else if !condition.CreatesNode && !condition.MatchesMultipleNodes {
			singlyMatchedNodes[&condition] = make(chan *bonsai.Node, len(node.Children))
			preventedCount++
			go func() {
				childrenDone := make(chan empty)
				for _, child := range node.Children {
					go func() {
						if matchesNode, childMatchedConditions := b.matchesNode(&condition, child) ; matchesNode {
							singlyMatchedNodeChildConditions[child][&condition] = &childMatchedConditions;
							singlyMatchedConditions[child] <- &condition
							singlyMatchedNodes[&condition] <- node
						}
						childrenDone <- empty{}
					}()
				}
				for _ = range node.Children { <-childrenDone }
				select {
				case matched := <-singlyMatchedNodes[&condition]:
					singlyMatchedNodes[&condition] <- matched
					prevented <- false
				default:
					prevented <- true
				}
			}()
		} else if condition.MatchesMultipleNodes {
			multiplyMatchedConditions[len(multiplyMatchedConditions)] = &condition
		}
	}
	for i := 0; i < preventedCount; i++ {
		if <-prevented {
			matchedConditions = nil
			return
		}
	}

	singlyMatchedConditionLists := make(map[*bonsai.Node][]*bonsai.Condition)
	for matchedNode, conditionChan := range singlyMatchedConditions {
		select {
		case condition := <-conditionChan:
			conditionChan <- condition

			singlyMatchedConditionLists[matchedNode] = make([]*bonsai.Condition, 0, len(rule.Conditions))
			for condition = range conditionChan {
				singlyMatchedConditionLists[matchedNode][len(singlyMatchedConditionLists[matchedNode])] = condition
			}
		default:
		}
	}

	pairings := b.findPairings(singlyMatchedConditionLists, make(map[*bonsai.Condition]bool))
	if pairings == nil {
		matchedConditions = nil
		return
	}

	childrenDone := make(chan empty, len(node.Children))
	for index, child := range node.Children {
		go func() {
			if pairings[child] != nil {
				if pairings[child].RemovesNode || pairings[child].Variable != "" {
					b.storeMatchedCondition(&matchedConditions, node, index, pairings[child], rule)
				}
				b.merge(&matchedConditions, singlyMatchedNodeChildConditions[child][pairings[child]])
			} else {
				multiplyMatchedNodeChildConditions := make(chan map[*bonsai.Node][]chan *bonsai.Condition, len(multiplyMatchedConditions))
				conditionsDone := make(chan empty, len(multiplyMatchedConditions))
				for _, condition := range multiplyMatchedConditions {
					go func() {
						if childMatches, childMatchedConditions := b.matchesNode(condition, child) ; childMatches {
							if condition.RemovesNode || condition.Variable != "" {
								b.storeMatchedCondition(&childMatchedConditions, node, index, condition, rule)
							}
							multiplyMatchedNodeChildConditions <- childMatchedConditions
						} else {
							conditionsDone <- empty{}
						}
					}()
				}
				for _ = range multiplyMatchedConditions {
					select {
					case childMatchedConditions := <-multiplyMatchedNodeChildConditions:
						b.merge(&matchedConditions, &childMatchedConditions)
						break
					case <-conditionsDone:
					}
				}
			}
			childrenDone <- empty{}
		}()
	}
	for _ = range node.Children {
		<-childrenDone
	}

	// TODO: include creating conditions
	// TODO: don't match if rule.MustMatchAllNodes and we don't match all nodes

	return
}

func (b *Basic) findPairings(matchedConditions map[*bonsai.Node][]*bonsai.Condition, ignored map[*bonsai.Condition]bool) (pairings map[*bonsai.Node]*bonsai.Condition) {
	pairings = make(map[*bonsai.Node]*bonsai.Condition)
	for node, conditions := range matchedConditions {
		matched := make([]*bonsai.Condition, len(conditions))
		for _, condition := range conditions {
			if !ignored[condition] {
				matched[len(matched)] = condition
			}
		}

		if len(matched) > 1 {
			delete(matchedConditions, node)
			for _, condition := range matched {
				ignored[condition] = true
				if pairings = b.findPairings(matchedConditions, ignored) ; pairings != nil {
					pairings[node] = condition
					return
				}
				ignored[condition] = false
			}
			return
		} else if len(matched) == 1 {
			pairings[node] = matched[0]
		}
	}
	return
}

func (b *Basic) matchesNode(condition *bonsai.Condition, node *bonsai.Node) (matches bool, matchedConditions map[*bonsai.Node][]chan *bonsai.Condition) {
	// TODO: document that ** means to matched either ordered conditions or unordered conditions
	//				- can not have child conditions
	//				- can not require matching all nodes
	//				- useful for **:* to match any number of any kind of node
	//				- allows *: and *:: to be used for matching only ordered or only unordered
	if condition.NodeType == "**" {
		matches, matchedConditions = true, make(map[*bonsai.Node][]chan *bonsai.Condition)
	} else if condition.NodeType == node.Label || condition.NodeType == "*" {
		matches, matchedConditions = b.matchesChildren(&condition.ChildRule, node)
	}

	// TODO: compare values

	return
}

func (b *Basic) matchesChildrenInOrder(rule *bonsai.Rule, node *bonsai.Node, condition_i int, child_i int) (matches bool, matchedConditions map[*bonsai.Node][]chan *bonsai.Condition) {
	if condition_i == len(rule.Conditions) {
		matches = !rule.MustMatchAllNodes || child_i == len(node.Children)
		if matches {
			matchedConditions = make(map[*bonsai.Node][]chan *bonsai.Condition)
		}
		return
	}

	condition := &rule.Conditions[condition_i]

	if child_i == len(node.Children) {
		if condition.PreventsMatch || condition.CreatesNode || condition.MatchesMultipleNodes {
			matches, matchedConditions = b.matchesChildrenInOrder(rule, node, condition_i + 1, child_i)
			if matches && condition.CreatesNode {
				b.storeMatchedCondition(&matchedConditions, node, child_i, condition, rule)
			}
		}
		return
	}

	child := node.Children[child_i]

	if condition.PreventsMatch {
		if prevents, _ := b.matchesNode(condition, child) ; prevents {
			if condition.MatchesMultipleNodes {
				matches, matchedConditions = b.matchesChildrenInOrder(rule, node, condition_i + 1, child_i)
			}
			return
		}

		if condition.MatchesMultipleNodes {
			matches, matchedConditions = b.matchesChildrenInOrder(rule, node, condition_i, child_i + 1)
			if matches {
				return
			}
		}

		matches, matchedConditions = b.matchesChildrenInOrder(rule, node, condition_i + 1, child_i + 1)
		return
	}

	if condition.CreatesNode {
		matches, matchedConditions = b.matchesChildrenInOrder(rule, node, condition_i + 1, child_i)
		if matches {
			b.storeMatchedCondition(&matchedConditions, node, child_i, condition, rule)
		}
		return
	}

	if nodeMatches, childMatchedConditions := b.matchesNode(condition, child) ; nodeMatches {
		if condition.MatchesMultipleNodes {
			matches, matchedConditions = b.matchesChildrenInOrder(rule, node, condition_i, child_i + 1)
			if matches {
				b.merge(&matchedConditions, &childMatchedConditions)
				if condition.RemovesNode {
					b.storeMatchedCondition(&matchedConditions, node, child_i, condition, rule)
				}
				return
			}
		}

		matches, matchedConditions = b.matchesChildrenInOrder(rule, node, condition_i + 1, child_i + 1)
		if matches {
			b.merge(&matchedConditions, &childMatchedConditions)
			if condition.RemovesNode {
				b.storeMatchedCondition(&matchedConditions, node, child_i, condition, rule)
			}
		}
	}

	if condition.MatchesMultipleNodes {
		matches, matchedConditions = b.matchesChildrenInOrder(rule, node, condition_i + 1, child_i)
	}
	return
}

func (b *Basic) matchesVariables(matchedConditions map[*bonsai.Node][]chan *bonsai.Condition) (matches bool) {
	// TODO: make sure variables match
	return
}

func (b *Basic) transform(rule *bonsai.Rule, matchedConditions map[*bonsai.Node][]chan *bonsai.Condition) (children []*bonsai.Node) {
	// TODO: transform node based on matched conditions
	return
}

func (b *Basic) initMatchedCondition(matchedConditions *map[*bonsai.Node][]chan *bonsai.Condition, parent *bonsai.Node, child_i int, rule *bonsai.Rule) {
	if (*matchedConditions)[parent] == nil {
		(*matchedConditions)[parent] = make([]chan *bonsai.Condition, len(parent.Children) + 1)
	}
	if (*matchedConditions)[parent][child_i] == nil {
		(*matchedConditions)[parent][child_i] = make(chan *bonsai.Condition, len(rule.Conditions))
	}
}

func (b *Basic) storeMatchedCondition(matchedConditions *map[*bonsai.Node][]chan *bonsai.Condition, parent *bonsai.Node, child_i int, condition *bonsai.Condition, rule *bonsai.Rule) {
	b.initMatchedCondition(matchedConditions, parent, child_i, rule)
	(*matchedConditions)[parent][child_i] <- condition
}

func (b *Basic) merge(destination *map[*bonsai.Node][]chan *bonsai.Condition, source *map[*bonsai.Node][]chan *bonsai.Condition) {
	for node, chans := range *source {
		(*destination)[node] = chans
	}
}
<|endoftext|>


<commit_before>package lsh

type TreeNode struct {
	// Hash key for this intermediate node. nil/empty for root nodes.
	hashKey TableKey
	// A list of indices to the source dataset.
	indices []int
	// Child nodes.
	children []TreeNode
}

type Tree struct {
	// Number of distinct elements in the tree.
	count int
	// Pointer to the root node.
	root TreeNode
}

type ForestIndex struct {
	// Embedded type
	*LshSettings
	// Number of leaves in the tree.
	count int
	// Trees.
	trees []Tree
}

func NewLshForest(dim, l, m int, w float64) *ForestIndex {
	trees := make([]Tree, l)
	return &ForestIndex{
		LshSettings: NewLshSettings(m, l, dim, w),
		count:       0,
		trees:       trees,
	}
}

// Inserts a point into the index.
func (index *ForestIndex) Insert(point Point) {
	// Apply hash functions.
	//	hvs := index.Hash(point)

}
<commit_msg>Query for forest<commit_after>package lsh

type TreeNode struct {
	// Hash key for this intermediate node. nil/empty for root nodes.
	hashKey int
	// A list of indices to the source dataset.
	indices []int
	// Child nodes, keyed by partial hash value.
	children map[int]*TreeNode
}

// recursiveAdd recurses down the tree to find the correct location to insert id.
// Returns whether a new hash value was added.
func (node *TreeNode) recursiveAdd(level int, id int, tableKey TableKey) bool {
	if level == len(tableKey) {
		node.indices = append(node.indices, id)
		return false
	} else {
		// Check if next hash exists in children map. If not, create.
		var next *TreeNode
		hasNewHash := false
		if node, ok := node.children[tableKey[level]]; !ok {
			next = &TreeNode{
				hashKey:  tableKey[level],
				indices:  make([]int, 0),
				children: make(map[int]*TreeNode),
			}
			node.children[tableKey[level]] = next
			hasNewHash = true
		} else {
			next = node
		}
		// Recurse using next level's hash value.
		return hasNewHash || next.recursiveAdd(level+1, id, tableKey)
	}
}

type Tree struct {
	// Number of distinct elements in the tree.
	count int
	// Pointer to the root node.
	root TreeNode
}

func (tree *Tree) insertIntoTree(id int, tableKey TableKey) {
	if tree.root.recursiveAdd(0, id, tableKey) {
		tree.count++
	}
}

type ForestIndex struct {
	// Embedded type
	*LshSettings
	// Trees.
	trees []Tree
}

func NewLshForest(dim, l, m int, w float64) *ForestIndex {
	trees := make([]Tree, l)
	for _, treeRoot := range trees {
		treeRoot.root = TreeNode{
			indices:  make([]int, 0),
			children: make(map[int]*TreeNode),
		}
	}
	return &ForestIndex{
		LshSettings: NewLshSettings(m, l, dim, w),
		trees:       trees,
	}
}

// Inserts a point into the index.
func (index *ForestIndex) Insert(point Point, id int) {
	// Apply hash functions.
	hvs := index.Hash(point)
	for treeId, hv := range hvs {
		index.trees[treeId].insertIntoTree(id, hv)
	}
}

/*
// Query searches for candidate keys given the signature
// and writes them to an output channel
func (index *ForestIndex) Query(q Point, out chan int) {
	// Apply hash functions
	hvs := index.Hash(q)
	// Keep track of keys seen
	seens := make(map[int]bool)
	for i, tree := range index.trees {
		if candidates, exist := table[hvs[i]]; exist {
			for _, id := range candidates {
				if _, seen := seens[id]; !seen {
					seens[id] = true
					out <- id
				}
			}
		}
	}
}*/
<|endoftext|>