<commit_before>// Copyright 2015 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 main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"math"
	"os"
	"time"

	"github.com/aclements/go-moremath/stats"
)

var cmdPlotFlags = flag.NewFlagSet(os.Args[0]+" plot", flag.ExitOnError)

var plot struct {
	baseline string
	json     bool
	filter   bool
}

// Currently I'm plotting this using gnuplot:
//
// plot for [i=3:50] 'data' using 0:i index "ns/op" with l title columnhead

// TODO: HTML output using Google Charts?

func init() {
	f := cmdPlotFlags
	f.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s plot [flags] <revision range>\n", os.Args[0])
		f.PrintDefaults()
	}
	f.StringVar(&gitDir, "C", "", "run git in `dir`")
	f.StringVar(&plot.baseline, "baseline", "", "normalize to `revision`; revision may be \"first\" or \"last\"")
	f.BoolVar(&plot.json, "json", false, "emit data in JSON")
	f.BoolVar(&plot.filter, "filter", false, "KZA filter benchmark results to reduce noise")
	registerSubcommand("plot", "[flags] <revision range> - print benchmark results", cmdPlot, f)
}

func cmdPlot() {
	if cmdPlotFlags.NArg() < 1 {
		cmdPlotFlags.Usage()
		os.Exit(2)
	}

	commits := getCommits(cmdPlotFlags.Args())

	// Put commits in forward chronological order.
	for i, j := 0, len(commits)-1; i < j; i, j = i+1, j-1 {
		commits[i], commits[j] = commits[j], commits[i]
	}

	// Load benchmark statistics.
	logFiles := []string{}
	for _, commit := range commits {
		if exists(commit.logPath) {
			logFiles = append(logFiles, commit.logPath)
		}
	}
	c := readFiles(logFiles...)
	for _, stat := range c.Stats {
		stat.ComputeStats()
	}

	// Get baseline commit.
	var baselineCommit *commitInfo
	if plot.baseline == "first" || plot.baseline == "last" {
		// Find the first/last commit with benchmark data.
		for _, commit := range commits {
			if c.ConfigSet[commit.logPath] {
				baselineCommit = commit
				if plot.baseline == "first" {
					break
				}
			}
		}
	} else if plot.baseline != "" {
		hash := trimNL(git("rev-parse", "--", plot.baseline))
		for _, commit := range commits {
			if hash == commit.hash {
				baselineCommit = commit
				break
			}
		}
		if baselineCommit == nil {
			fmt.Fprintf(os.Stderr, "baseline revision %s not found in revision range\n", hash)
			os.Exit(2)
		}
	}
	if baselineCommit != nil && !c.ConfigSet[baselineCommit.logPath] {
		fmt.Fprintf(os.Stderr, "no benchmark data for baseline commit\n")
		os.Exit(2)
	}

	// Build tables.
	var tables []*Table
	var units []string
	var key BenchKey
	baseline := make([]float64, 0)
	means := make([]float64, 0)
	for _, unit := range c.Units {
		key.Unit = unit
		table := NewTable()
		tables = append(tables, table)
		units = append(units, unit)
		if unit == "ns/op" {
			units[len(units)-1] = "op/ns"
		}

		subc := c.Filter(BenchKey{Unit: unit})

		// Get baseline numbers.
		baseline = baseline[:0]
		if baselineCommit == nil {
			for range subc.Benchmarks {
				baseline = append(baseline, 1)
			}
		} else {
			key.Config = baselineCommit.logPath
			for _, bench := range subc.Benchmarks {
				key.Benchmark = bench
				baseline = append(baseline, subc.Stats[key].Mean)
			}
		}

		// Build columns.
		dateCol, commitCol, idxCol := []string{}, []string{}, []int{}
		geomeanCol, benchCols := []float64{}, make([][]float64, len(subc.Benchmarks))
		baselineIdx := -1
		for i, commit := range commits {
			key.Config = commit.logPath
			if !subc.ConfigSet[commit.logPath] {
				continue
			}
			if commit == baselineCommit {
				baselineIdx = len(dateCol)
			}

			means = means[:0]
			for _, bench := range subc.Benchmarks {
				key.Benchmark = bench
				means = append(means, subc.Stats[key].Mean)
			}

			dateCol = append(dateCol, commit.commitDate.Format(time.RFC3339))
			commitCol = append(commitCol, commit.hash[:7])
			idxCol = append(idxCol, i)
			geomeanCol = append(geomeanCol, stats.GeoMean(means)/stats.GeoMean(baseline))
			for i, bench := range subc.Benchmarks {
				key.Benchmark = bench
				benchCols[i] = append(benchCols[i], subc.Stats[key].Mean/baseline[i])
			}
			if unit == "ns/op" {
				j := len(geomeanCol) - 1
				geomeanCol[j] = 1 / geomeanCol[j]
				for i := range benchCols {
					benchCols[i][j] = 1 / benchCols[i][j]
				}
			}
		}

		// Filter the columns.
		if plot.filter {
			geomeanCol = AdaptiveKolmogorovZurbenko(geomeanCol, 15, 3)
			for i := range benchCols {
				benchCols[i] = AdaptiveKolmogorovZurbenko(benchCols[i], 15, 3)
			}
		}

		// Normalize the columns.
		if baselineCommit != nil {
			if baselineIdx == -1 {
				fmt.Fprintf(os.Stderr, "baseline commit has no data for %s\n", key.Unit)
				os.Exit(2)
			}
			divideCol(geomeanCol, geomeanCol[baselineIdx])
			for _, benchCol := range benchCols {
				divideCol(benchCol, benchCol[baselineIdx])
			}
		}

		// Trim the number of significant figures.
		roundCol(geomeanCol, 5)
		for _, benchCol := range benchCols {
			roundCol(benchCol, 5)
		}

		// Build the table.
		table.AddColumn("date", dateCol)
		table.AddColumn("i", idxCol)
		table.AddColumn("commit", commitCol)
		if len(benchCols) > 1 {
			table.AddColumn("geomean", geomeanCol)
		}
		for i, bench := range subc.Benchmarks {
			table.AddColumn(bench, benchCols[i])
		}
	}

	if plot.json {
		var jsonTables []JSONTable
		for i, table := range tables {
			jsonTables = append(jsonTables, JSONTable{Unit: units[i], Rows: table.ToRows(true)})
		}
		if err := json.NewEncoder(os.Stdout).Encode(jsonTables); err != nil {
			log.Fatal(err)
		}
	} else {
		// Print tables.
		for i, table := range tables {
			if i > 0 {
				fmt.Printf("\n\n")
			}
			fmt.Printf("# %s\n", units[i])
			if err := table.WriteTSV(os.Stdout, true); err != nil {
				log.Fatal(err)
			}
		}
	}
}

type JSONTable struct {
	Unit string
	Rows [][]interface{}
}

func divideCol(xs []float64, by float64) {
	for i, x := range xs {
		xs[i] = x / by
	}
}

func roundCol(xs []float64, sigfigs int) {
	for i, x := range xs {
		if x == 0 {
			continue
		}
		f := math.Pow(10, float64(sigfigs)-math.Ceil(math.Log10(math.Abs(x))))
		xs[i] = math.Floor(x*f+0.5) / f
	}
}
<commit_msg>benchmany: leave JSON table as columns<commit_after>// Copyright 2015 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 main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"math"
	"os"
	"time"

	"github.com/aclements/go-moremath/stats"
)

var cmdPlotFlags = flag.NewFlagSet(os.Args[0]+" plot", flag.ExitOnError)

var plot struct {
	baseline string
	json     bool
	filter   bool
}

// Currently I'm plotting this using gnuplot:
//
// plot for [i=3:50] 'data' using 0:i index "ns/op" with l title columnhead

// TODO: HTML output using Google Charts?

func init() {
	f := cmdPlotFlags
	f.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s plot [flags] <revision range>\n", os.Args[0])
		f.PrintDefaults()
	}
	f.StringVar(&gitDir, "C", "", "run git in `dir`")
	f.StringVar(&plot.baseline, "baseline", "", "normalize to `revision`; revision may be \"first\" or \"last\"")
	f.BoolVar(&plot.json, "json", false, "emit data in JSON")
	f.BoolVar(&plot.filter, "filter", false, "KZA filter benchmark results to reduce noise")
	registerSubcommand("plot", "[flags] <revision range> - print benchmark results", cmdPlot, f)
}

func cmdPlot() {
	if cmdPlotFlags.NArg() < 1 {
		cmdPlotFlags.Usage()
		os.Exit(2)
	}

	commits := getCommits(cmdPlotFlags.Args())

	// Put commits in forward chronological order.
	for i, j := 0, len(commits)-1; i < j; i, j = i+1, j-1 {
		commits[i], commits[j] = commits[j], commits[i]
	}

	// Load benchmark statistics.
	logFiles := []string{}
	for _, commit := range commits {
		if exists(commit.logPath) {
			logFiles = append(logFiles, commit.logPath)
		}
	}
	c := readFiles(logFiles...)
	for _, stat := range c.Stats {
		stat.ComputeStats()
	}

	// Get baseline commit.
	var baselineCommit *commitInfo
	if plot.baseline == "first" || plot.baseline == "last" {
		// Find the first/last commit with benchmark data.
		for _, commit := range commits {
			if c.ConfigSet[commit.logPath] {
				baselineCommit = commit
				if plot.baseline == "first" {
					break
				}
			}
		}
	} else if plot.baseline != "" {
		hash := trimNL(git("rev-parse", "--", plot.baseline))
		for _, commit := range commits {
			if hash == commit.hash {
				baselineCommit = commit
				break
			}
		}
		if baselineCommit == nil {
			fmt.Fprintf(os.Stderr, "baseline revision %s not found in revision range\n", hash)
			os.Exit(2)
		}
	}
	if baselineCommit != nil && !c.ConfigSet[baselineCommit.logPath] {
		fmt.Fprintf(os.Stderr, "no benchmark data for baseline commit\n")
		os.Exit(2)
	}

	// Build tables.
	var tables []*Table
	var units []string
	var key BenchKey
	baseline := make([]float64, 0)
	means := make([]float64, 0)
	for _, unit := range c.Units {
		key.Unit = unit
		table := NewTable()
		tables = append(tables, table)
		units = append(units, unit)
		if unit == "ns/op" {
			units[len(units)-1] = "op/ns"
		}

		subc := c.Filter(BenchKey{Unit: unit})

		// Get baseline numbers.
		baseline = baseline[:0]
		if baselineCommit == nil {
			for range subc.Benchmarks {
				baseline = append(baseline, 1)
			}
		} else {
			key.Config = baselineCommit.logPath
			for _, bench := range subc.Benchmarks {
				key.Benchmark = bench
				baseline = append(baseline, subc.Stats[key].Mean)
			}
		}

		// Build columns.
		dateCol, commitCol, idxCol := []string{}, []string{}, []int{}
		geomeanCol, benchCols := []float64{}, make([][]float64, len(subc.Benchmarks))
		baselineIdx := -1
		for i, commit := range commits {
			key.Config = commit.logPath
			if !subc.ConfigSet[commit.logPath] {
				continue
			}
			if commit == baselineCommit {
				baselineIdx = len(dateCol)
			}

			means = means[:0]
			for _, bench := range subc.Benchmarks {
				key.Benchmark = bench
				means = append(means, subc.Stats[key].Mean)
			}

			dateCol = append(dateCol, commit.commitDate.Format(time.RFC3339))
			commitCol = append(commitCol, commit.hash[:7])
			idxCol = append(idxCol, i)
			geomeanCol = append(geomeanCol, stats.GeoMean(means)/stats.GeoMean(baseline))
			for i, bench := range subc.Benchmarks {
				key.Benchmark = bench
				benchCols[i] = append(benchCols[i], subc.Stats[key].Mean/baseline[i])
			}
			if unit == "ns/op" {
				j := len(geomeanCol) - 1
				geomeanCol[j] = 1 / geomeanCol[j]
				for i := range benchCols {
					benchCols[i][j] = 1 / benchCols[i][j]
				}
			}
		}

		// Filter the columns.
		if plot.filter {
			geomeanCol = AdaptiveKolmogorovZurbenko(geomeanCol, 15, 3)
			for i := range benchCols {
				benchCols[i] = AdaptiveKolmogorovZurbenko(benchCols[i], 15, 3)
			}
		}

		// Normalize the columns.
		if baselineCommit != nil {
			if baselineIdx == -1 {
				fmt.Fprintf(os.Stderr, "baseline commit has no data for %s\n", key.Unit)
				os.Exit(2)
			}
			divideCol(geomeanCol, geomeanCol[baselineIdx])
			for _, benchCol := range benchCols {
				divideCol(benchCol, benchCol[baselineIdx])
			}
		}

		// Trim the number of significant figures.
		roundCol(geomeanCol, 5)
		for _, benchCol := range benchCols {
			roundCol(benchCol, 5)
		}

		// Build the table.
		table.AddColumn("date", dateCol)
		table.AddColumn("i", idxCol)
		table.AddColumn("commit", commitCol)
		if len(benchCols) > 1 {
			table.AddColumn("geomean", geomeanCol)
		}
		for i, bench := range subc.Benchmarks {
			table.AddColumn(bench, benchCols[i])
		}
	}

	if plot.json {
		var jsonTables []JSONTable
		for i, table := range tables {
			jsonTables = append(jsonTables, JSONTable{units[i], table})
		}
		if err := json.NewEncoder(os.Stdout).Encode(jsonTables); err != nil {
			log.Fatal(err)
		}
	} else {
		// Print tables.
		for i, table := range tables {
			if i > 0 {
				fmt.Printf("\n\n")
			}
			fmt.Printf("# %s\n", units[i])
			if err := table.WriteTSV(os.Stdout, true); err != nil {
				log.Fatal(err)
			}
		}
	}
}

type JSONTable struct {
	Unit string
	*Table
}

func divideCol(xs []float64, by float64) {
	for i, x := range xs {
		xs[i] = x / by
	}
}

func roundCol(xs []float64, sigfigs int) {
	for i, x := range xs {
		if x == 0 {
			continue
		}
		f := math.Pow(10, float64(sigfigs)-math.Ceil(math.Log10(math.Abs(x))))
		xs[i] = math.Floor(x*f+0.5) / f
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 Citrix Systems, Inc

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package netscaler

import (
	"github.com/chiradeep/go-nitro/config/lb"
	"github.com/chiradeep/go-nitro/netscaler"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"

	"fmt"
	"log"
)

func resourceNetScalerLB() *schema.Resource {
	return &schema.Resource{
		SchemaVersion: 1,
		Create:        createLbFunc,
		Read:          readLbFunc,
		Update:        updateLbFunc,
		Delete:        deleteLbFunc,
		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"vip": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"service_type": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"port": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
			},
			"persistence_type": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"lb_method": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
		},
	}
}

func createLbFunc(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*NetScalerNitroClient).client
	var lbName string
	if v, ok := d.GetOk("name"); ok {
		lbName = v.(string)
	} else {
		lbName = resource.PrefixedUniqueId("tf-lb-")
		d.Set("name", lbName)
	}
	lb := lb.Lbvserver{
		Name:            lbName,
		Ipv46:           d.Get("vip").(string),
		Port:            d.Get("port").(int),
		Servicetype:     d.Get("service_type").(string),
		Persistencetype: d.Get("persistence_type").(string),
		Lbmethod:        d.Get("lb_method").(string),
	}

	_, err := client.AddResource(netscaler.Lbvserver.Type(), lbName, &lb)
	if err != nil {
		return err
	}

	d.SetId(lbName)
	err = readLbFunc(d, meta)
	if err != nil {
		log.Printf("?? we just created this loadbalancer but we can't read it ?? %s", lbName)
		return nil
	}
	return nil
}

func readLbFunc(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*NetScalerNitroClient).client
	lbName := d.Id()
	log.Printf("Reading loadbalancer state %s", lbName)
	data, err := client.FindResource(netscaler.Lbvserver.Type(), lbName)
	if err != nil {
		log.Printf("Clearing loadbalancer state %s", lbName)
		d.SetId("")
		return nil
	}
	/* { "name": "sample_lb2", "insertvserveripport": "OFF", "ipv46": "10.71.136.151", "ippattern": "0.0.0.0", "ipmask": "*", "listenpolicy": "NONE",
	   "ipmapping": "0.0.0.0", "port": 443, "range": "1", "servicetype": "HTTP", "type": "ADDRESS", "curstate": "DOWN", "effectivestate": "DOWN", "status": 1,
	   "lbrrreason": 0, "cachetype": "SERVER", "authentication": "OFF", "authn401": "OFF", "dynamicweight": "0", "priority": "0", "clttimeout": "180",
	   "somethod": "NONE", "sopersistence": "DISABLED", "sopersistencetimeout": "2", "healththreshold": "0", "lbmethod": "LEASTCONNECTION", "backuplbmethod": "ROUNDROBIN",
	   "dataoffset": "0", "health": "0", "datalength": "0", "ruletype": "0", "m": "IP", "persistencetype": "NONE", "timeout": 2, "persistmask": "255.255.255.255",
	   "v6persistmasklen": "128", "persistencebackup": "NONE", "cacheable": "NO", "pq": "OFF", "sc": "OFF", "rtspnat": "OFF", "sessionless": "DISABLED", "map": "OFF",
	   "connfailover": "DISABLED", "redirectportrewrite": "DISABLED", "downstateflush": "ENABLED", "disableprimaryondown": "DISABLED", "gt2gb": "DISABLED", "consolidatedlconn":
	   "GLOBAL", "consolidatedlconngbl": "YES", "thresholdvalue": 0, "invoke": false, "version": 0, "totalservices": "2", "activeservices": "0",
	   "statechangetimesec": "Fri Jul 29 19:14:02 2016", "statechangetimeseconds": "1469819642", "statechangetimemsec": "382", "tickssincelaststatechange": "728421",
	   "hits": "0", "pipolicyhits": "0", "push": "DISABLED", "pushlabel": "none", "pushmulticlients": "NO", "policysubtype": "0", "l2conn": "OFF", "appflowlog": "ENABLED",
	   "isgslb": false, "icmpvsrresponse": "PASSIVE", "rhistate": "PASSIVE", "newservicerequestunit": "PER_SECOND", "vsvrbindsvcip": "10.71.136.151", "vsvrbindsvcport": 0,
	   "skippersistency": "None", "td": "0", "minautoscalemembers": "0", "maxautoscalemembers": "0", "macmoderetainvlan": "DISABLED", "dns64": "DISABLED", "bypassaaaa": "NO",
	   "processlocal": "DISABLED", "vsvrdynconnsothreshold": "0" }
	*/
	d.Set("name", data["name"])
	d.Set("persistence_type", data["persistencetype"])
	d.Set("lb_method", data["lbmethod"])
	d.Set("service_type", data["servicetype"])

	return nil

}

func updateLbFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG] In update func")
	client := meta.(*NetScalerNitroClient).client
	name := d.Get("name").(string)

	lb := lb.Lbvserver{
		Name: d.Get("name").(string),
	}
	if d.HasChange("vip") {
		log.Printf("[DEBUG] VIP has changed for lb vserver %s, starting update", name)
		lb.Ipv46 = d.Get("vip").(string)
	}
	if d.HasChange("persistence_type") {
		log.Printf("[DEBUG] persistence_type has changed for lb vserver %s, starting update", name)
		lb.Persistencetype = d.Get("persistence_type").(string)
	}
	if d.HasChange("lb_method") {
		log.Printf("[DEBUG] lb_method has changed for lb vserver %s, starting update", name)
		lb.Lbmethod = d.Get("lb_method").(string)
	}

	_, err := client.UpdateResource(netscaler.Lbvserver.Type(), name, &lb)
	if err != nil {
		return fmt.Errorf("Error updating lb vserver %s", name)
	}
	return readLbFunc(d, meta)
}

func deleteLbFunc(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*NetScalerNitroClient).client
	lbName := d.Id()
	err := client.DeleteResource(netscaler.Lbvserver.Type(), lbName)
	if err != nil {
		return err
	}

	d.SetId("")

	return nil
}
<commit_msg>remove comment<commit_after>/*
Copyright 2016 Citrix Systems, Inc

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package netscaler

import (
	"github.com/chiradeep/go-nitro/config/lb"
	"github.com/chiradeep/go-nitro/netscaler"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"

	"fmt"
	"log"
)

func resourceNetScalerLB() *schema.Resource {
	return &schema.Resource{
		SchemaVersion: 1,
		Create:        createLbFunc,
		Read:          readLbFunc,
		Update:        updateLbFunc,
		Delete:        deleteLbFunc,
		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"vip": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"service_type": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"port": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
			},
			"persistence_type": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"lb_method": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
		},
	}
}

func createLbFunc(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*NetScalerNitroClient).client
	var lbName string
	if v, ok := d.GetOk("name"); ok {
		lbName = v.(string)
	} else {
		lbName = resource.PrefixedUniqueId("tf-lb-")
		d.Set("name", lbName)
	}
	lb := lb.Lbvserver{
		Name:            lbName,
		Ipv46:           d.Get("vip").(string),
		Port:            d.Get("port").(int),
		Servicetype:     d.Get("service_type").(string),
		Persistencetype: d.Get("persistence_type").(string),
		Lbmethod:        d.Get("lb_method").(string),
	}

	_, err := client.AddResource(netscaler.Lbvserver.Type(), lbName, &lb)
	if err != nil {
		return err
	}

	d.SetId(lbName)
	err = readLbFunc(d, meta)
	if err != nil {
		log.Printf("?? we just created this loadbalancer but we can't read it ?? %s", lbName)
		return nil
	}
	return nil
}

func readLbFunc(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*NetScalerNitroClient).client
	lbName := d.Id()
	log.Printf("Reading loadbalancer state %s", lbName)
	data, err := client.FindResource(netscaler.Lbvserver.Type(), lbName)
	if err != nil {
		log.Printf("Clearing loadbalancer state %s", lbName)
		d.SetId("")
		return nil
	}
	/* see: the go-nitro jsonconfig to find the actual keys into 'data' */
	d.Set("name", data["name"])
	d.Set("persistence_type", data["persistencetype"])
	d.Set("lb_method", data["lbmethod"])
	d.Set("service_type", data["servicetype"])

	return nil

}

func updateLbFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG] In update func")
	client := meta.(*NetScalerNitroClient).client
	name := d.Get("name").(string)

	lb := lb.Lbvserver{
		Name: d.Get("name").(string),
	}
	if d.HasChange("vip") {
		log.Printf("[DEBUG] VIP has changed for lb vserver %s, starting update", name)
		lb.Ipv46 = d.Get("vip").(string)
	}
	if d.HasChange("persistence_type") {
		log.Printf("[DEBUG] persistence_type has changed for lb vserver %s, starting update", name)
		lb.Persistencetype = d.Get("persistence_type").(string)
	}
	if d.HasChange("lb_method") {
		log.Printf("[DEBUG] lb_method has changed for lb vserver %s, starting update", name)
		lb.Lbmethod = d.Get("lb_method").(string)
	}

	_, err := client.UpdateResource(netscaler.Lbvserver.Type(), name, &lb)
	if err != nil {
		return fmt.Errorf("Error updating lb vserver %s", name)
	}
	return readLbFunc(d, meta)
}

func deleteLbFunc(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*NetScalerNitroClient).client
	lbName := d.Id()
	err := client.DeleteResource(netscaler.Lbvserver.Type(), lbName)
	if err != nil {
		return err
	}

	d.SetId("")

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 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 main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)

type commitInfo struct {
	hash         string
	commitDate   time.Time
	gover        bool
	logPath      string
	count, fails int
	buildFailed  bool
}

// getCommits returns the commit info for all of the revisions in the
// given git revision range, where the revision range is spelled as
// documented in gitrevisions(7). Commits are returned in reverse
// chronological order, most recent commit first (the same as
// git-rev-list(1)).
func getCommits(revRange []string, logPath string) []*commitInfo {
	// Get commit sequence.
	hashes := lines(git("rev-list", append([]string{"--no-walk", "--"}, revRange...)...))
	commits := make([]*commitInfo, len(hashes))
	commitMap := make(map[string]*commitInfo)
	for i, hash := range hashes {
		commits[i] = &commitInfo{
			hash:    hash,
			logPath: logPath,
		}
		commitMap[hash] = commits[i]
	}

	// Get commit dates.
	//
	// TODO: This can produce a huge command line.
	args := append([]string{"-s", "--format=format:%cI"}, hashes...)
	dates := lines(git("show", args...))
	for i := range commits {
		d, err := time.Parse(time.RFC3339, dates[i])
		if err != nil {
			log.Fatalf("cannot parse commit date: %v", err)
		}
		commits[i].commitDate = d
	}

	// Get gover-cached builds. It's okay if this fails.
	if fis, err := ioutil.ReadDir(goverDir()); err == nil {
		for _, fi := range fis {
			if ci := commitMap[fi.Name()]; ci != nil && fi.IsDir() {
				ci.gover = true
			}
		}
	}

	// Load current benchmark state.
	logf, err := os.Open(logPath)
	if err != nil {
		if !os.IsNotExist(err) {
			log.Fatalf("opening %s: %v", logPath, err)
		}
	} else {
		defer logf.Close()
		parseLog(commitMap, logf)
	}

	return commits
}

// goverDir returns the directory containing gover-cached builds.
func goverDir() string {
	cache := os.Getenv("XDG_CACHE_HOME")
	if cache == "" {
		home := os.Getenv("HOME")
		if home == "" {
			u, err := user.Current()
			if err != nil {
				home = u.HomeDir
			}
		}
		cache = filepath.Join(home, ".cache")
	}
	return filepath.Join(cache, "gover")
}

// parseLog parses benchmark runs and failures from r and updates
// commits in commitMap.
func parseLog(commitMap map[string]*commitInfo, r io.Reader) {
	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		b := scanner.Bytes()
		switch {
		case bytes.HasPrefix(b, []byte("commit: ")):
			hash := scanner.Text()[len("commit: "):]
			if ci := commitMap[hash]; ci != nil {
				ci.count++
			}

		case bytes.HasPrefix(b, []byte("# FAILED at ")):
			hash := scanner.Text()[len("# FAILED at "):]
			if ci := commitMap[hash]; ci != nil {
				ci.fails++
			}

		case bytes.HasPrefix(b, []byte("# BUILD FAILED at ")):
			hash := scanner.Text()[len("# BUILD FAILED at "):]
			if ci := commitMap[hash]; ci != nil {
				ci.buildFailed = true
			}
		}
	}
	if err := scanner.Err(); err != nil {
		log.Fatal("parsing benchmark log: ", err)
	}
}

// binPath returns the file name of the binary for this commit.
func (c *commitInfo) binPath() string {
	// TODO: This assumes the short commit hash is unique.
	return fmt.Sprintf("bench.%s", c.hash[:7])
}

// failed returns whether commit c has failed and should not be run
// any more.
func (c *commitInfo) failed() bool {
	return c.buildFailed || c.fails >= maxFails
}

// runnable returns whether commit c needs to be benchmarked at least
// one more time.
func (c *commitInfo) runnable() bool {
	return !c.buildFailed && c.fails < maxFails && c.count < run.iterations
}

// partial returns true if this commit is both runnable and already
// has some runs.
func (c *commitInfo) partial() bool {
	return c.count > 0 && c.runnable()
}

var commitRe = regexp.MustCompile(`^commit: |^# FAILED|^# BUILD FAILED`)

// cleanLog escapes lines in l that may confuse the log parser and
// makes sure l is newline terminated.
func cleanLog(l string) string {
	l = commitRe.ReplaceAllString(l, "# $0")
	if !strings.HasSuffix(l, "\n") {
		l += "\n"
	}
	return l
}

// logRun updates c with a successful run.
func (c *commitInfo) logRun(out string) {
	c.writeLog(fmt.Sprintf("commit: %s\n\n%s\n", c.hash, cleanLog(out)))
	c.count++
}

// logFailed updates c with a failed run. If buildFailed is true, this
// is considered a permanent failure and buildFailed is set.
func (c *commitInfo) logFailed(buildFailed bool, out string) {
	typ := "FAILED"
	if buildFailed {
		typ = "BUILD FAILED"
	}
	c.writeLog(fmt.Sprintf("# %s at %s\n# %s\n", typ, c.hash, strings.Replace(cleanLog(out), "\n", "\n# ", -1)))
	if buildFailed {
		c.buildFailed = true
	} else {
		c.fails++
	}
}

// writeLog appends msg to c's log file. The caller is responsible for
// properly formatting it.
func (c *commitInfo) writeLog(msg string) {
	logFile, err := os.OpenFile(c.logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatalf("opening %s: %v", c.logPath, err)
	}
	if _, err := logFile.WriteString(msg); err != nil {
		log.Fatalf("writing to %s: %v", c.logPath, err)
	}
	if err := logFile.Close(); err != nil {
		log.Fatalf("closing %s: %v", c.logPath, err)
	}
}
<commit_msg>benchmany: output commit-time header<commit_after>// Copyright 2015 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 main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)

type commitInfo struct {
	hash         string
	commitDate   time.Time
	gover        bool
	logPath      string
	count, fails int
	buildFailed  bool
}

// getCommits returns the commit info for all of the revisions in the
// given git revision range, where the revision range is spelled as
// documented in gitrevisions(7). Commits are returned in reverse
// chronological order, most recent commit first (the same as
// git-rev-list(1)).
func getCommits(revRange []string, logPath string) []*commitInfo {
	// Get commit sequence.
	hashes := lines(git("rev-list", append([]string{"--no-walk", "--"}, revRange...)...))
	commits := make([]*commitInfo, len(hashes))
	commitMap := make(map[string]*commitInfo)
	for i, hash := range hashes {
		commits[i] = &commitInfo{
			hash:    hash,
			logPath: logPath,
		}
		commitMap[hash] = commits[i]
	}

	// Get commit dates.
	//
	// TODO: This can produce a huge command line.
	args := append([]string{"-s", "--format=format:%cI"}, hashes...)
	dates := lines(git("show", args...))
	for i := range commits {
		d, err := time.Parse(time.RFC3339, dates[i])
		if err != nil {
			log.Fatalf("cannot parse commit date: %v", err)
		}
		commits[i].commitDate = d
	}

	// Get gover-cached builds. It's okay if this fails.
	if fis, err := ioutil.ReadDir(goverDir()); err == nil {
		for _, fi := range fis {
			if ci := commitMap[fi.Name()]; ci != nil && fi.IsDir() {
				ci.gover = true
			}
		}
	}

	// Load current benchmark state.
	logf, err := os.Open(logPath)
	if err != nil {
		if !os.IsNotExist(err) {
			log.Fatalf("opening %s: %v", logPath, err)
		}
	} else {
		defer logf.Close()
		parseLog(commitMap, logf)
	}

	return commits
}

// goverDir returns the directory containing gover-cached builds.
func goverDir() string {
	cache := os.Getenv("XDG_CACHE_HOME")
	if cache == "" {
		home := os.Getenv("HOME")
		if home == "" {
			u, err := user.Current()
			if err != nil {
				home = u.HomeDir
			}
		}
		cache = filepath.Join(home, ".cache")
	}
	return filepath.Join(cache, "gover")
}

// parseLog parses benchmark runs and failures from r and updates
// commits in commitMap.
func parseLog(commitMap map[string]*commitInfo, r io.Reader) {
	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		b := scanner.Bytes()
		switch {
		case bytes.HasPrefix(b, []byte("commit: ")):
			hash := scanner.Text()[len("commit: "):]
			if ci := commitMap[hash]; ci != nil {
				ci.count++
			}

		case bytes.HasPrefix(b, []byte("# FAILED at ")):
			hash := scanner.Text()[len("# FAILED at "):]
			if ci := commitMap[hash]; ci != nil {
				ci.fails++
			}

		case bytes.HasPrefix(b, []byte("# BUILD FAILED at ")):
			hash := scanner.Text()[len("# BUILD FAILED at "):]
			if ci := commitMap[hash]; ci != nil {
				ci.buildFailed = true
			}
		}
	}
	if err := scanner.Err(); err != nil {
		log.Fatal("parsing benchmark log: ", err)
	}
}

// binPath returns the file name of the binary for this commit.
func (c *commitInfo) binPath() string {
	// TODO: This assumes the short commit hash is unique.
	return fmt.Sprintf("bench.%s", c.hash[:7])
}

// failed returns whether commit c has failed and should not be run
// any more.
func (c *commitInfo) failed() bool {
	return c.buildFailed || c.fails >= maxFails
}

// runnable returns whether commit c needs to be benchmarked at least
// one more time.
func (c *commitInfo) runnable() bool {
	return !c.buildFailed && c.fails < maxFails && c.count < run.iterations
}

// partial returns true if this commit is both runnable and already
// has some runs.
func (c *commitInfo) partial() bool {
	return c.count > 0 && c.runnable()
}

var commitRe = regexp.MustCompile(`^commit: |^# FAILED|^# BUILD FAILED`)

// cleanLog escapes lines in l that may confuse the log parser and
// makes sure l is newline terminated.
func cleanLog(l string) string {
	l = commitRe.ReplaceAllString(l, "# $0")
	if !strings.HasSuffix(l, "\n") {
		l += "\n"
	}
	return l
}

// logRun updates c with a successful run.
func (c *commitInfo) logRun(out string) {
	var log bytes.Buffer
	fmt.Fprintf(&log, "commit: %s\n", c.hash)
	fmt.Fprintf(&log, "commit-time: %s\n", c.commitDate.UTC().Format(time.RFC3339))
	fmt.Fprintf(&log, "\n%s\n", cleanLog(out))
	c.writeLog(log.String())
	c.count++
}

// logFailed updates c with a failed run. If buildFailed is true, this
// is considered a permanent failure and buildFailed is set.
func (c *commitInfo) logFailed(buildFailed bool, out string) {
	typ := "FAILED"
	if buildFailed {
		typ = "BUILD FAILED"
	}
	c.writeLog(fmt.Sprintf("# %s at %s\n# %s\n", typ, c.hash, strings.Replace(cleanLog(out), "\n", "\n# ", -1)))
	if buildFailed {
		c.buildFailed = true
	} else {
		c.fails++
	}
}

// writeLog appends msg to c's log file. The caller is responsible for
// properly formatting it.
func (c *commitInfo) writeLog(msg string) {
	logFile, err := os.OpenFile(c.logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatalf("opening %s: %v", c.logPath, err)
	}
	if _, err := logFile.WriteString(msg); err != nil {
		log.Fatalf("writing to %s: %v", c.logPath, err)
	}
	if err := logFile.Close(); err != nil {
		log.Fatalf("closing %s: %v", c.logPath, err)
	}
}
<|endoftext|>


<commit_before>package opds2

import (
	"time"

	"github.com/readium/r2-streamer-go/models"
)

// OPDSFeed represent the root opds2 feed
type OPDSFeed struct {
	Context      []string             `json:"@context"`
	Metadata     OPDSMetadata         `json:"metadata"`
	Links        []models.Link        `json:"links"`
	Publications []models.Publication `json:"publications,omitempty"`
	Navigation   []models.Link        `json:"navigation,omitempty"`
	Facets       []OPDSFacet          `json:"facets,omitempty"`
	Groups       []OPDSCollection     `json:"groups,omitempty"`
	Modified     *time.Time           `json:"modified,omitempty"`
}

// OPDSMetadata information for the feed, facets or collections
type OPDSMetadata struct {
	RDFType       string `json:"@type,omitempty"`
	Title         string `json:"title"`
	NumberOfItems int    `json:"numberOfItems,omitempty"`
}

// OPDSFacet facet information for filtering
type OPDSFacet struct {
	Metadata OPDSMetadata  `json:"metadata"`
	Links    []models.Link `json:"links"`
}

// OPDSCollection collection of publication
type OPDSCollection struct {
	Metadata     OPDSMetadata         `json:"metadata"`
	Publications []models.Publication `json:"publications"`
	Links        []models.Link        `json:"links"`
}
<commit_msg>related #37, change some name and fields<commit_after>package opds2

import (
	"time"

	"github.com/readium/r2-streamer-go/models"
)

// OPDSFeed is a collection as defined in Readium Web Publication Manifest
type OPDSFeed struct {
	Context      []string             `json:"@context"`
	Metadata     OPDSMetadata         `json:"metadata"`
	Links        []models.Link        `json:"links"`
	Publications []models.Publication `json:"publications,omitempty"`
	Navigation   []models.Link        `json:"navigation,omitempty"`
	Facets       []OPDSFacet          `json:"facets,omitempty"`
	Groups       []OPDSGroup          `json:"groups,omitempty"`
}

// OPDSMetadata has a limited subset of metadata compared to a publication
type OPDSMetadata struct {
	RDFType       string     `json:"@type,omitempty"`
	Title         string     `json:"title"`
	NumberOfItems int        `json:"numberOfItems,omitempty"`
	Modified      *time.Time `json:"modified,omitempty"`
}

// OPDSFacet is a collection that contains a facet group
type OPDSFacet struct {
	Metadata OPDSMetadata  `json:"metadata"`
	Links    []models.Link `json:"links"`
}

// OPDSGroup is a group collection that must contain publications
type OPDSGroup struct {
	Metadata     OPDSMetadata         `json:"metadata"`
	Publications []models.Publication `json:"publications"`
	Links        []models.Link        `json:"links"`
}
<|endoftext|>


<commit_before>package linux_backend

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"sync"
	"time"

	"github.com/cloudfoundry-incubator/garden"
	"github.com/cloudfoundry-incubator/garden-linux/old/system_info"
	"github.com/pivotal-golang/lager"
)

type Container interface {
	ID() string
	Properties() garden.Properties
	GraceTime() time.Duration

	Start() error

	Snapshot(io.Writer) error
	Cleanup()

	garden.Container
}

type ContainerPool interface {
	Setup() error
	Create(garden.ContainerSpec) (Container, error)
	Restore(io.Reader) (Container, error)
	Destroy(Container) error
	Prune(keep map[string]bool) error
	MaxContainers() int
}

type ContainerRepository interface {
	All() []Container
	Add(Container)
	FindByHandle(string) (Container, bool)
	Delete(Container)
}

type InMemoryContainerRepository struct {
	store map[string]Container
}

func (cr *InMemoryContainerRepository) All() []Container {
	all := []Container{}
	for _, container := range cr.store {
		all = append(all, container)
	}
	return all
}

func (cr *InMemoryContainerRepository) Add(container Container) {
	cr.store[container.Handle()] = container
}

func (cr *InMemoryContainerRepository) FindByHandle(handle string) (Container, bool) {
	// Yep, you actually can't inline these...
	container, ok := cr.store[handle]
	return container, ok
}

func (cr *InMemoryContainerRepository) Delete(container Container) {
	delete(cr.store, container.Handle())
}

type LinuxBackend struct {
	logger lager.Logger

	containerPool ContainerPool
	systemInfo    system_info.Provider
	snapshotsPath string

	containerRepo   ContainerRepository
	containersMutex *sync.RWMutex
}

type HandleExistsError struct {
	Handle string
}

func (e HandleExistsError) Error() string {
	return fmt.Sprintf("handle already exists: %s", e.Handle)
}

type FailedToSnapshotError struct {
	OriginalError error
}

func (e FailedToSnapshotError) Error() string {
	return fmt.Sprintf("failed to save snapshot: %s", e.OriginalError)
}

func New(logger lager.Logger, containerPool ContainerPool, systemInfo system_info.Provider, snapshotsPath string) *LinuxBackend {
	return &LinuxBackend{
		logger: logger.Session("backend"),

		containerPool: containerPool,
		systemInfo:    systemInfo,
		snapshotsPath: snapshotsPath,

		containerRepo: &InMemoryContainerRepository{
			store: map[string]Container{},
		},
		containersMutex: new(sync.RWMutex),
	}
}

func (b *LinuxBackend) Setup() error {
	return b.containerPool.Setup()
}

func (b *LinuxBackend) Start() error {
	if b.snapshotsPath != "" {
		_, err := os.Stat(b.snapshotsPath)
		if err == nil {
			b.restoreSnapshots()
			os.RemoveAll(b.snapshotsPath)
		}

		err = os.MkdirAll(b.snapshotsPath, 0755)
		if err != nil {
			return err
		}
	}

	keep := map[string]bool{}

	b.containersMutex.RLock()
	containers := b.containerRepo.All()
	b.containersMutex.RUnlock()

	for _, container := range containers {
		keep[container.ID()] = true
	}

	return b.containerPool.Prune(keep)
}

func (b *LinuxBackend) Ping() error {
	return nil
}

func (b *LinuxBackend) Capacity() (garden.Capacity, error) {
	totalMemory, err := b.systemInfo.TotalMemory()
	if err != nil {
		return garden.Capacity{}, err
	}

	totalDisk, err := b.systemInfo.TotalDisk()
	if err != nil {
		return garden.Capacity{}, err
	}

	return garden.Capacity{
		MemoryInBytes: totalMemory,
		DiskInBytes:   totalDisk,
		MaxContainers: uint64(b.containerPool.MaxContainers()),
	}, nil
}

func (b *LinuxBackend) Create(spec garden.ContainerSpec) (garden.Container, error) {
	if spec.Handle != "" {
		b.containersMutex.RLock()
		_, exists := b.containerRepo.FindByHandle(spec.Handle)
		b.containersMutex.RUnlock()

		if exists {
			return nil, HandleExistsError{Handle: spec.Handle}
		}
	}

	container, err := b.containerPool.Create(spec)
	if err != nil {
		return nil, err
	}

	err = container.Start()
	if err != nil {
		return nil, err
	}

	b.containersMutex.Lock()
	b.containerRepo.Add(container)
	b.containersMutex.Unlock()

	return container, nil
}

func (b *LinuxBackend) Destroy(handle string) error {
	b.containersMutex.RLock()
	container, found := b.containerRepo.FindByHandle(handle)
	b.containersMutex.RUnlock()

	if !found {
		return garden.ContainerNotFoundError{handle}
	}

	err := b.containerPool.Destroy(container)
	if err != nil {
		return err
	}

	b.containersMutex.Lock()
	b.containerRepo.Delete(container)
	b.containersMutex.Unlock()

	return nil
}

func (b *LinuxBackend) Containers(filter garden.Properties) (containers []garden.Container, err error) {
	b.containersMutex.RLock()
	defer b.containersMutex.RUnlock()

	for _, container := range b.containerRepo.All() {
		if containerHasProperties(container, filter) {
			containers = append(containers, container)
		}
	}

	return containers, nil
}

func (b *LinuxBackend) Lookup(handle string) (garden.Container, error) {
	b.containersMutex.RLock()
	defer b.containersMutex.RUnlock()

	container, found := b.containerRepo.FindByHandle(handle)
	if !found {
		return nil, garden.ContainerNotFoundError{handle}
	}

	return container, nil
}

func (b *LinuxBackend) GraceTime(container garden.Container) time.Duration {
	return container.(Container).GraceTime()
}

func (b *LinuxBackend) Stop() {
	b.containersMutex.RLock()
	defer b.containersMutex.RUnlock()

	for _, container := range b.containerRepo.All() {
		container.Cleanup()
		err := b.saveSnapshot(container)
		if err != nil {
			b.logger.Error("failed-to-save-snapshot", err, lager.Data{
				"container": container.ID(),
			})
		}
	}
}

func (b *LinuxBackend) restoreSnapshots() {
	sLog := b.logger.Session("restore")

	entries, err := ioutil.ReadDir(b.snapshotsPath)
	if err != nil {
		b.logger.Error("failed-to-read-snapshots", err, lager.Data{
			"from": b.snapshotsPath,
		})
	}

	for _, entry := range entries {
		snapshot := path.Join(b.snapshotsPath, entry.Name())

		lLog := sLog.Session("load", lager.Data{
			"snapshot": entry.Name(),
		})

		lLog.Debug("loading")

		file, err := os.Open(snapshot)
		if err != nil {
			lLog.Error("failed-to-open", err)
		}

		_, err = b.restore(file)
		if err != nil {
			lLog.Error("failed-to-restore", err)
		}
	}
}

func (b *LinuxBackend) saveSnapshot(container Container) error {
	if b.snapshotsPath == "" {
		return nil
	}

	b.logger.Info("save-snapshot", lager.Data{
		"container": container.ID(),
	})

	snapshotPath := path.Join(b.snapshotsPath, container.ID())

	snapshot, err := os.Create(snapshotPath)
	if err != nil {
		return &FailedToSnapshotError{err}
	}

	err = container.Snapshot(snapshot)
	if err != nil {
		return &FailedToSnapshotError{err}
	}

	return snapshot.Close()
}

func (b *LinuxBackend) restore(snapshot io.Reader) (garden.Container, error) {
	container, err := b.containerPool.Restore(snapshot)
	if err != nil {
		return nil, err
	}

	b.containersMutex.Lock()
	b.containerRepo.Add(container)
	b.containersMutex.Unlock()

	return container, nil
}

func containerHasProperties(container Container, properties garden.Properties) bool {
	containerProps := container.Properties()

	for key, val := range properties {
		cval, ok := containerProps[key]
		if !ok {
			return false
		}

		if cval != val {
			return false
		}
	}

	return true
}
<commit_msg>move mutex from LinuxBackend to ContainerRepo<commit_after>package linux_backend

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"sync"
	"time"

	"github.com/cloudfoundry-incubator/garden"
	"github.com/cloudfoundry-incubator/garden-linux/old/system_info"
	"github.com/pivotal-golang/lager"
)

type Container interface {
	ID() string
	Properties() garden.Properties
	GraceTime() time.Duration

	Start() error

	Snapshot(io.Writer) error
	Cleanup()

	garden.Container
}

type ContainerPool interface {
	Setup() error
	Create(garden.ContainerSpec) (Container, error)
	Restore(io.Reader) (Container, error)
	Destroy(Container) error
	Prune(keep map[string]bool) error
	MaxContainers() int
}

type ContainerRepository interface {
	All() []Container
	Add(Container)
	FindByHandle(string) (Container, bool)
	Delete(Container)
}

type InMemoryContainerRepository struct {
	store map[string]Container
	mutex *sync.RWMutex
}

func (cr *InMemoryContainerRepository) All() []Container {
	cr.mutex.RLock()
	defer cr.mutex.RUnlock()

	all := []Container{}
	for _, container := range cr.store {
		all = append(all, container)
	}
	return all
}

func (cr *InMemoryContainerRepository) Add(container Container) {
	cr.mutex.Lock()
	defer cr.mutex.Unlock()

	cr.store[container.Handle()] = container
}

func (cr *InMemoryContainerRepository) FindByHandle(handle string) (Container, bool) {
	cr.mutex.RLock()
	defer cr.mutex.RUnlock()

	// Yep, you actually can't inline these...
	container, ok := cr.store[handle]
	return container, ok
}

func (cr *InMemoryContainerRepository) Delete(container Container) {
	cr.mutex.Lock()
	defer cr.mutex.Unlock()

	delete(cr.store, container.Handle())
}

type LinuxBackend struct {
	logger lager.Logger

	containerPool ContainerPool
	systemInfo    system_info.Provider
	snapshotsPath string

	containerRepo ContainerRepository
}

type HandleExistsError struct {
	Handle string
}

func (e HandleExistsError) Error() string {
	return fmt.Sprintf("handle already exists: %s", e.Handle)
}

type FailedToSnapshotError struct {
	OriginalError error
}

func (e FailedToSnapshotError) Error() string {
	return fmt.Sprintf("failed to save snapshot: %s", e.OriginalError)
}

func New(logger lager.Logger, containerPool ContainerPool, systemInfo system_info.Provider, snapshotsPath string) *LinuxBackend {
	return &LinuxBackend{
		logger: logger.Session("backend"),

		containerPool: containerPool,
		systemInfo:    systemInfo,
		snapshotsPath: snapshotsPath,

		containerRepo: &InMemoryContainerRepository{
			store: map[string]Container{},
			mutex: &sync.RWMutex{},
		},
	}
}

func (b *LinuxBackend) Setup() error {
	return b.containerPool.Setup()
}

func (b *LinuxBackend) Start() error {
	if b.snapshotsPath != "" {
		_, err := os.Stat(b.snapshotsPath)
		if err == nil {
			b.restoreSnapshots()
			os.RemoveAll(b.snapshotsPath)
		}

		err = os.MkdirAll(b.snapshotsPath, 0755)
		if err != nil {
			return err
		}
	}

	keep := map[string]bool{}

	containers := b.containerRepo.All()

	for _, container := range containers {
		keep[container.ID()] = true
	}

	return b.containerPool.Prune(keep)
}

func (b *LinuxBackend) Ping() error {
	return nil
}

func (b *LinuxBackend) Capacity() (garden.Capacity, error) {
	totalMemory, err := b.systemInfo.TotalMemory()
	if err != nil {
		return garden.Capacity{}, err
	}

	totalDisk, err := b.systemInfo.TotalDisk()
	if err != nil {
		return garden.Capacity{}, err
	}

	return garden.Capacity{
		MemoryInBytes: totalMemory,
		DiskInBytes:   totalDisk,
		MaxContainers: uint64(b.containerPool.MaxContainers()),
	}, nil
}

func (b *LinuxBackend) Create(spec garden.ContainerSpec) (garden.Container, error) {
	if spec.Handle != "" {
		_, exists := b.containerRepo.FindByHandle(spec.Handle)

		if exists {
			return nil, HandleExistsError{Handle: spec.Handle}
		}
	}

	container, err := b.containerPool.Create(spec)
	if err != nil {
		return nil, err
	}

	err = container.Start()
	if err != nil {
		return nil, err
	}

	b.containerRepo.Add(container)

	return container, nil
}

func (b *LinuxBackend) Destroy(handle string) error {
	container, found := b.containerRepo.FindByHandle(handle)

	if !found {
		return garden.ContainerNotFoundError{handle}
	}

	err := b.containerPool.Destroy(container)
	if err != nil {
		return err
	}

	b.containerRepo.Delete(container)

	return nil
}

func (b *LinuxBackend) Containers(filter garden.Properties) (containers []garden.Container, err error) {
	for _, container := range b.containerRepo.All() {
		if containerHasProperties(container, filter) {
			containers = append(containers, container)
		}
	}

	return containers, nil
}

func (b *LinuxBackend) Lookup(handle string) (garden.Container, error) {
	container, found := b.containerRepo.FindByHandle(handle)
	if !found {
		return nil, garden.ContainerNotFoundError{handle}
	}

	return container, nil
}

func (b *LinuxBackend) GraceTime(container garden.Container) time.Duration {
	return container.(Container).GraceTime()
}

func (b *LinuxBackend) Stop() {
	for _, container := range b.containerRepo.All() {
		container.Cleanup()
		err := b.saveSnapshot(container)
		if err != nil {
			b.logger.Error("failed-to-save-snapshot", err, lager.Data{
				"container": container.ID(),
			})
		}
	}
}

func (b *LinuxBackend) restoreSnapshots() {
	sLog := b.logger.Session("restore")

	entries, err := ioutil.ReadDir(b.snapshotsPath)
	if err != nil {
		b.logger.Error("failed-to-read-snapshots", err, lager.Data{
			"from": b.snapshotsPath,
		})
	}

	for _, entry := range entries {
		snapshot := path.Join(b.snapshotsPath, entry.Name())

		lLog := sLog.Session("load", lager.Data{
			"snapshot": entry.Name(),
		})

		lLog.Debug("loading")

		file, err := os.Open(snapshot)
		if err != nil {
			lLog.Error("failed-to-open", err)
		}

		_, err = b.restore(file)
		if err != nil {
			lLog.Error("failed-to-restore", err)
		}
	}
}

func (b *LinuxBackend) saveSnapshot(container Container) error {
	if b.snapshotsPath == "" {
		return nil
	}

	b.logger.Info("save-snapshot", lager.Data{
		"container": container.ID(),
	})

	snapshotPath := path.Join(b.snapshotsPath, container.ID())

	snapshot, err := os.Create(snapshotPath)
	if err != nil {
		return &FailedToSnapshotError{err}
	}

	err = container.Snapshot(snapshot)
	if err != nil {
		return &FailedToSnapshotError{err}
	}

	return snapshot.Close()
}

func (b *LinuxBackend) restore(snapshot io.Reader) (garden.Container, error) {
	container, err := b.containerPool.Restore(snapshot)
	if err != nil {
		return nil, err
	}
	b.containerRepo.Add(container)
	return container, nil
}

func containerHasProperties(container Container, properties garden.Properties) bool {
	containerProps := container.Properties()

	for key, val := range properties {
		cval, ok := containerProps[key]
		if !ok {
			return false
		}

		if cval != val {
			return false
		}
	}

	return true
}
<|endoftext|>


<commit_before>package middleware

import (
	"github.com/gin-gonic/gin"
	"strings"

	u "github.com/techjanitor/pram-get/utils"
)

// Cache will check for the key in Redis and serve it. If not found, it will
// take the marshalled JSON from the controller and set it in Redis
func Cache() gin.HandlerFunc {
	return func(c *gin.Context) {
		var result []byte
		var err error

		// Get request path
		path := c.Request.URL.Path

		// Trim leading / from path and split
		params := strings.Split(strings.Trim(path, "/"), "/")

		// Make key from path
		key := redisKey{}
		key.expireKey(params[0])
		key.generateKey(params...)

		// Initialize cache handle
		cache := u.RedisCache

		if key.Hash {
			// Check to see if there is already a key we can serve
			result, err = cache.HGet(key.Key, key.Field)
			if err == u.ErrCacheMiss {
				c.Next()

				// Check if there was an error from the controller
				controllerError, _ := c.Get("controllerError")
				if controllerError != nil {
					c.Abort()
					return
				}

				// Get data from controller
				data := c.MustGet("data").([]byte)

				// Set output to cache
				err = cache.HMSet(key.Key, key.Field, data)
				if err != nil {
					c.Error(err, "Operation aborted")
					c.Abort()
					return
				}

			}
			if err != nil {
				c.Error(err, "Operation aborted")
				c.Abort()
				return
			}

		}

		if !key.Hash {
			// Check to see if there is already a key we can serve
			result, err = cache.Get(key.Key)
			if err == u.ErrCacheMiss {
				c.Next()

				// Check if there was an error from the controller
				controllerError, _ := c.Get("controllerError")
				if controllerError != nil {
					c.Abort()
					return
				}

				// Get data from controller
				data := c.MustGet("data").([]byte)

				if key.Expire {

					// Set output to cache
					err = cache.SetEx(key.Key, 60, data)
					if err != nil {
						c.Error(err, "Operation aborted")
						c.Abort()
						return
					}

				} else {

					// Set output to cache
					err = cache.Set(key.Key, data)
					if err != nil {
						c.Error(err, "Operation aborted")
						c.Abort()
						return
					}

				}

			}
			if err != nil {
				c.Error(err, "Operation aborted")
				c.Abort()
				return
			}

		}

		c.Writer.Header().Set("Content-Type", "application/json")
		c.Writer.Write(result)
		c.Abort()

	}

}

type redisKey struct {
	Key    string
	Field  string
	Hash   bool
	Expire bool
}

// Will take the params from the request and turn them into a key
func (r *redisKey) generateKey(params ...string) {
	var keys []string

	for i, param := range params {
		// Add key
		if i == 0 || i == 1 {
			keys = append(keys, param)
		}
		// Add field for redis hash if present
		if i == 2 {
			r.Field = param
			r.Hash = true
		}

	}

	// Create redis key
	r.Key = strings.Join(keys, ":")

	return

}

// Check if key should be expired
func (r *redisKey) expireKey(key string) {

	keyList := map[string]bool{
		"image": true,
		"pram":  true,
	}

	if keyList[strings.ToLower(key)] {
		r.Expire = true
	}

	return

}
<commit_msg>add tag pages to cache expiry<commit_after>package middleware

import (
	"github.com/gin-gonic/gin"
	"strings"

	u "github.com/techjanitor/pram-get/utils"
)

// Cache will check for the key in Redis and serve it. If not found, it will
// take the marshalled JSON from the controller and set it in Redis
func Cache() gin.HandlerFunc {
	return func(c *gin.Context) {
		var result []byte
		var err error

		// Get request path
		path := c.Request.URL.Path

		// Trim leading / from path and split
		params := strings.Split(strings.Trim(path, "/"), "/")

		// Make key from path
		key := redisKey{}
		key.expireKey(params[0])
		key.generateKey(params...)

		// Initialize cache handle
		cache := u.RedisCache

		if key.Hash {
			// Check to see if there is already a key we can serve
			result, err = cache.HGet(key.Key, key.Field)
			if err == u.ErrCacheMiss {
				c.Next()

				// Check if there was an error from the controller
				controllerError, _ := c.Get("controllerError")
				if controllerError != nil {
					c.Abort()
					return
				}

				// Get data from controller
				data := c.MustGet("data").([]byte)

				// Set output to cache
				err = cache.HMSet(key.Key, key.Field, data)
				if err != nil {
					c.Error(err, "Operation aborted")
					c.Abort()
					return
				}

			}
			if err != nil {
				c.Error(err, "Operation aborted")
				c.Abort()
				return
			}

		}

		if !key.Hash {
			// Check to see if there is already a key we can serve
			result, err = cache.Get(key.Key)
			if err == u.ErrCacheMiss {
				c.Next()

				// Check if there was an error from the controller
				controllerError, _ := c.Get("controllerError")
				if controllerError != nil {
					c.Abort()
					return
				}

				// Get data from controller
				data := c.MustGet("data").([]byte)

				if key.Expire {

					// Set output to cache
					err = cache.SetEx(key.Key, 60, data)
					if err != nil {
						c.Error(err, "Operation aborted")
						c.Abort()
						return
					}

				} else {

					// Set output to cache
					err = cache.Set(key.Key, data)
					if err != nil {
						c.Error(err, "Operation aborted")
						c.Abort()
						return
					}

				}

			}
			if err != nil {
				c.Error(err, "Operation aborted")
				c.Abort()
				return
			}

		}

		c.Writer.Header().Set("Content-Type", "application/json")
		c.Writer.Write(result)
		c.Abort()

	}

}

type redisKey struct {
	Key    string
	Field  string
	Hash   bool
	Expire bool
}

// Will take the params from the request and turn them into a key
func (r *redisKey) generateKey(params ...string) {
	var keys []string

	for i, param := range params {
		// Add key
		if i == 0 || i == 1 {
			keys = append(keys, param)
		}
		// Add field for redis hash if present
		if i == 2 {
			r.Field = param
			r.Hash = true
		}

	}

	// Create redis key
	r.Key = strings.Join(keys, ":")

	return

}

// Check if key should be expired
func (r *redisKey) expireKey(key string) {

	keyList := map[string]bool{
		"image": true,
		"pram":  true,
		"tag":   true,
	}

	if keyList[strings.ToLower(key)] {
		r.Expire = true
	}

	return

}
<|endoftext|>


<commit_before>package rbg2p

import (
	"bufio"
	"fmt"
	"os"
	"regexp"
	"strings"
)

var commentAtEndRe = regexp.MustCompile("^(.*[^/]+)//+.*$")

func trimComment(s string) string {
	return strings.TrimSpace(commentAtEndRe.ReplaceAllString(s, "$1"))
}

func isComment(s string) bool {
	return strings.HasPrefix(s, "//")
}

func isVar(s string) bool {
	return strings.HasPrefix(s, "VAR ")
}

func isTest(s string) bool {
	return strings.HasPrefix(s, "TEST ")
}

func isBlankLine(s string) bool {
	return len(s) == 0
}

// LoadFile loads a g2p rule set from the specified file
func LoadFile(fName string) (RuleSet, error) {
	ruleSet := RuleSet{Vars: map[string]string{}}
	ruleSet.DefaultPhoneme = "_"
	ruleSet.PhonemeDelimiter = " "
	fh, err := os.Open(fName)
	defer fh.Close()
	if err != nil {
		return ruleSet, err
	}
	n := 0
	s := bufio.NewScanner(fh)
	var ruleLines []string
	for s.Scan() {
		if err := s.Err(); err != nil {
			return ruleSet, err
		}
		n++
		l := trimComment(strings.TrimSpace(s.Text()))
		if isBlankLine(l) || isComment(l) {
		} else if isConst(l) {
			err := parseConst(l, &ruleSet)
			if err != nil {
				return ruleSet, err
			}
		} else if isVar(l) {
			name, value, err := newVar(l)
			if err != nil {
				return ruleSet, err
			}
			ruleSet.Vars[name] = value
		} else if isTest(l) {
			t, err := newTest(l)
			if err != nil {
				return ruleSet, err
			}
			ruleSet.Tests = append(ruleSet.Tests, t)
		} else { // is a rule
			ruleLines = append(ruleLines, l)
		}

	}
	for _, l := range ruleLines {
		r, err := newRule(l, ruleSet.Vars)
		if err != nil {
			return ruleSet, err
		}
		ruleSet.Rules = append(ruleSet.Rules, r)
	}
	if ruleSet.CharacterSet == nil || len(ruleSet.CharacterSet) == 0 {
		return ruleSet, fmt.Errorf("No character set defined for input file %s", fName)
	}
	return ruleSet, nil
}

var charSetRe = regexp.MustCompile("^(CHARACTER_SET|SYMBOL_SET|DEFAULT_PHONEME|PHONEME_DELIMITER) +\"(.*)\"$")
var isConstRe = regexp.MustCompile("^(CHARACTER_SET|SYMBOL_SET|DEFAULT_PHONEME|PHONEME_DELIMITER) .*")

func isConst(s string) bool {
	return isConstRe.MatchString(s)
}

func parseConst(s string, ruleSet *RuleSet) error {
	var matchRes []string
	matchRes = charSetRe.FindStringSubmatch(s)
	if matchRes != nil {
		name := matchRes[1]
		value := matchRes[2]
		if name == "CHARACTER_SET" {
			ruleSet.CharacterSet = strings.Split(value, "")
		} else if name == "SYMBOL_SET" {
			ruleSet.SymbolSet = value
		} else if name == "DEFAULT_PHONEME" {
			ruleSet.DefaultPhoneme = value
		} else if name == "PHONEME_DELIMITER" {
			ruleSet.PhonemeDelimiter = value
		} else {
			return fmt.Errorf("invalid const definition: " + s)
		}
	} else {
		return fmt.Errorf("invalid const definition: " + s)
	}
	return nil
}

var varRe = regexp.MustCompile("^VAR +([^ ]+) +([^ ]+)$")

func newVar(s string) (string, string, error) {
	// VAR NAME VALUE
	matchRes := varRe.FindStringSubmatch(s)
	if matchRes == nil {
		return "", "", fmt.Errorf("invalid var definition: " + s)
	}
	name := matchRes[1]
	value := matchRes[2]
	_, err := regexp.Compile(value)
	if err != nil {
		return "", "", fmt.Errorf("invalid var in input (regular expression failed) for /%s/: %s", s, err)
	}
	return name, value, nil
}

var testReSimple = regexp.MustCompile("^TEST +([^ ]+) +-> +([^,()]+)$")
var testReVariants = regexp.MustCompile("^TEST +([^ ]+) +-> +[(](.+,.+)[)]$")
var commaSplit = regexp.MustCompile(" *, *")

func newTest(s string) (Test, error) {
	var outputS string
	var matchRes []string
	matchRes = testReSimple.FindStringSubmatch(s)
	if matchRes != nil {
		outputS = matchRes[2]
	} else {
		matchRes = testReVariants.FindStringSubmatch(s)
		if matchRes == nil {
			return Test{}, fmt.Errorf("invalid test definition: " + s)
		}
		outputS = matchRes[2]
	}
	if strings.Contains(outputS, "->") {
		return Test{}, fmt.Errorf("invalid test definition: " + s)
	}
	input := matchRes[1]
	output := commaSplit.Split(outputS, -1)
	return Test{Input: input, Output: output}, nil
}

func expandVars(s0 string, isLeft bool, vars map[string]string) (*regexp.Regexp, error) {
	if isLeft {
		s0 = strings.Replace(s0, "#", "^", -1)
	} else {
		s0 = strings.Replace(s0, "#", "$", -1)
	}
	splitted := strings.Split(s0, " ")
	for i, s := range splitted {
		if val, ok := vars[strings.TrimSpace(s)]; ok {
			splitted[i] = val
		}
	}
	if isLeft {
		return regexp.Compile(strings.Join(splitted, "") + "$")
	}
	return regexp.Compile("^" + strings.Join(splitted, ""))
}

var contextRe = regexp.MustCompile("^ +/ +((?:[^_>]+)?) *_ *((?:[^_>]+)?)$")

func newContext(s string, vars map[string]string) (Context, Context, error) {
	if len(strings.TrimSpace(s)) == 0 {
		return Context{}, Context{}, nil
	}
	matchRes := contextRe.FindStringSubmatch(s)
	if matchRes == nil {
		return Context{}, Context{}, fmt.Errorf("invalid context definition: " + s)
	}
	left := Context{}
	right := Context{}
	leftS := strings.TrimSpace(matchRes[1])
	if len(leftS) > 0 {
		re, err := expandVars(leftS, true, vars)
		if err != nil {
			return Context{}, Context{}, fmt.Errorf("invalid context definition: %s", err)
		}
		left.Regexp = re
		left.Input = leftS
	}
	rightS := strings.TrimSpace(matchRes[2])
	if len(rightS) > 0 {
		re, err := expandVars(rightS, false, vars)
		if err != nil {
			return Context{}, Context{}, fmt.Errorf("invalid context definition: %s", err)
		}
		right.Regexp = re
		right.Input = rightS
	}
	return left, right, nil
}

var ruleRe = regexp.MustCompile("^([^ ]+) +-> +([^/]+)( +/.*$|$)")
var ruleOutputReSimple = regexp.MustCompile("^([^,()]+)$")
var ruleOutputReVariants = regexp.MustCompile("^[(](.+,.+)[)]$")

func newRuleOutput(s string, l string) ([]string, error) {
	s = strings.TrimSpace(s)
	var outputS string
	var matchRes []string
	matchRes = ruleOutputReSimple.FindStringSubmatch(s)
	if matchRes != nil {
		outputS = matchRes[1]
	} else {
		matchRes = ruleOutputReVariants.FindStringSubmatch(s)
		if matchRes == nil {
			return []string{}, fmt.Errorf("invalid rule output definition: " + l)
		}
		outputS = matchRes[1]
	}
	if strings.Contains(outputS, "->") {
		return []string{}, fmt.Errorf("invalid rule output definition: " + l)
	}
	return commaSplit.Split(outputS, -1), nil
}

func newRule(s string, vars map[string]string) (Rule, error) {
	// INPUT -> OUTPUT
	// INPUT -> OUTPUT / LEFTCONTEXT _ RIGHTCONTEXT
	matchRes := ruleRe.FindStringSubmatch(s)
	if matchRes == nil {
		return Rule{}, fmt.Errorf("invalid rule definition: " + s)
	}
	input := matchRes[1]
	output, err := newRuleOutput(matchRes[2], s)
	if err != nil {
		return Rule{}, err
	}
	left, right, err := newContext(matchRes[3], vars)
	if err != nil {
		return Rule{}, err
	}
	return Rule{Input: input, Output: output, LeftContext: left, RightContext: right}, nil
}
<commit_msg>double quotes not allowed in VARs<commit_after>package rbg2p

import (
	"bufio"
	"fmt"
	"os"
	"regexp"
	"strings"
)

var commentAtEndRe = regexp.MustCompile("^(.*[^/]+)//+.*$")

func trimComment(s string) string {
	return strings.TrimSpace(commentAtEndRe.ReplaceAllString(s, "$1"))
}

func isComment(s string) bool {
	return strings.HasPrefix(s, "//")
}

func isVar(s string) bool {
	return strings.HasPrefix(s, "VAR ")
}

func isTest(s string) bool {
	return strings.HasPrefix(s, "TEST ")
}

func isBlankLine(s string) bool {
	return len(s) == 0
}

// LoadFile loads a g2p rule set from the specified file
func LoadFile(fName string) (RuleSet, error) {
	ruleSet := RuleSet{Vars: map[string]string{}}
	ruleSet.DefaultPhoneme = "_"
	ruleSet.PhonemeDelimiter = " "
	fh, err := os.Open(fName)
	defer fh.Close()
	if err != nil {
		return ruleSet, err
	}
	n := 0
	s := bufio.NewScanner(fh)
	var ruleLines []string
	for s.Scan() {
		if err := s.Err(); err != nil {
			return ruleSet, err
		}
		n++
		l := trimComment(strings.TrimSpace(s.Text()))
		if isBlankLine(l) || isComment(l) {
		} else if isConst(l) {
			err := parseConst(l, &ruleSet)
			if err != nil {
				return ruleSet, err
			}
		} else if isVar(l) {
			name, value, err := newVar(l)
			if err != nil {
				return ruleSet, err
			}
			ruleSet.Vars[name] = value
		} else if isTest(l) {
			t, err := newTest(l)
			if err != nil {
				return ruleSet, err
			}
			ruleSet.Tests = append(ruleSet.Tests, t)
		} else { // is a rule
			ruleLines = append(ruleLines, l)
		}

	}
	for _, l := range ruleLines {
		r, err := newRule(l, ruleSet.Vars)
		if err != nil {
			return ruleSet, err
		}
		ruleSet.Rules = append(ruleSet.Rules, r)
	}
	if ruleSet.CharacterSet == nil || len(ruleSet.CharacterSet) == 0 {
		return ruleSet, fmt.Errorf("No character set defined for input file %s", fName)
	}
	return ruleSet, nil
}

var charSetRe = regexp.MustCompile("^(CHARACTER_SET|SYMBOL_SET|DEFAULT_PHONEME|PHONEME_DELIMITER) +\"(.*)\"$")
var isConstRe = regexp.MustCompile("^(CHARACTER_SET|SYMBOL_SET|DEFAULT_PHONEME|PHONEME_DELIMITER) .*")

func isConst(s string) bool {
	return isConstRe.MatchString(s)
}

func parseConst(s string, ruleSet *RuleSet) error {
	var matchRes []string
	matchRes = charSetRe.FindStringSubmatch(s)
	if matchRes != nil {
		name := matchRes[1]
		value := matchRes[2]
		if name == "CHARACTER_SET" {
			ruleSet.CharacterSet = strings.Split(value, "")
		} else if name == "SYMBOL_SET" {
			ruleSet.SymbolSet = value
		} else if name == "DEFAULT_PHONEME" {
			ruleSet.DefaultPhoneme = value
		} else if name == "PHONEME_DELIMITER" {
			ruleSet.PhonemeDelimiter = value
		} else {
			return fmt.Errorf("invalid const definition: " + s)
		}
	} else {
		return fmt.Errorf("invalid const definition: " + s)
	}
	return nil
}

var varRe = regexp.MustCompile("^VAR +([^ \"]+) +([^ \"]+)$")

func newVar(s string) (string, string, error) {
	// VAR NAME VALUE
	matchRes := varRe.FindStringSubmatch(s)
	if matchRes == nil {
		return "", "", fmt.Errorf("invalid var definition: " + s)
	}
	name := matchRes[1]
	value := matchRes[2]
	_, err := regexp.Compile(value)
	if err != nil {
		return "", "", fmt.Errorf("invalid var in input (regular expression failed) for /%s/: %s", s, err)
	}
	return name, value, nil
}

var testReSimple = regexp.MustCompile("^TEST +([^ ]+) +-> +([^,()]+)$")
var testReVariants = regexp.MustCompile("^TEST +([^ ]+) +-> +[(](.+,.+)[)]$")
var commaSplit = regexp.MustCompile(" *, *")

func newTest(s string) (Test, error) {
	var outputS string
	var matchRes []string
	matchRes = testReSimple.FindStringSubmatch(s)
	if matchRes != nil {
		outputS = matchRes[2]
	} else {
		matchRes = testReVariants.FindStringSubmatch(s)
		if matchRes == nil {
			return Test{}, fmt.Errorf("invalid test definition: " + s)
		}
		outputS = matchRes[2]
	}
	if strings.Contains(outputS, "->") {
		return Test{}, fmt.Errorf("invalid test definition: " + s)
	}
	input := matchRes[1]
	output := commaSplit.Split(outputS, -1)
	return Test{Input: input, Output: output}, nil
}

func expandVars(s0 string, isLeft bool, vars map[string]string) (*regexp.Regexp, error) {
	if isLeft {
		s0 = strings.Replace(s0, "#", "^", -1)
	} else {
		s0 = strings.Replace(s0, "#", "$", -1)
	}
	splitted := strings.Split(s0, " ")
	for i, s := range splitted {
		if val, ok := vars[strings.TrimSpace(s)]; ok {
			splitted[i] = val
		}
	}
	if isLeft {
		return regexp.Compile(strings.Join(splitted, "") + "$")
	}
	return regexp.Compile("^" + strings.Join(splitted, ""))
}

var contextRe = regexp.MustCompile("^ +/ +((?:[^_>]+)?) *_ *((?:[^_>]+)?)$")

func newContext(s string, vars map[string]string) (Context, Context, error) {
	if len(strings.TrimSpace(s)) == 0 {
		return Context{}, Context{}, nil
	}
	matchRes := contextRe.FindStringSubmatch(s)
	if matchRes == nil {
		return Context{}, Context{}, fmt.Errorf("invalid context definition: " + s)
	}
	left := Context{}
	right := Context{}
	leftS := strings.TrimSpace(matchRes[1])
	if len(leftS) > 0 {
		re, err := expandVars(leftS, true, vars)
		if err != nil {
			return Context{}, Context{}, fmt.Errorf("invalid context definition: %s", err)
		}
		left.Regexp = re
		left.Input = leftS
	}
	rightS := strings.TrimSpace(matchRes[2])
	if len(rightS) > 0 {
		re, err := expandVars(rightS, false, vars)
		if err != nil {
			return Context{}, Context{}, fmt.Errorf("invalid context definition: %s", err)
		}
		right.Regexp = re
		right.Input = rightS
	}
	return left, right, nil
}

var ruleRe = regexp.MustCompile("^([^ ]+) +-> +([^/]+)( +/.*$|$)")
var ruleOutputReSimple = regexp.MustCompile("^([^,()]+)$")
var ruleOutputReVariants = regexp.MustCompile("^[(](.+,.+)[)]$")

func newRuleOutput(s string, l string) ([]string, error) {
	s = strings.TrimSpace(s)
	var outputS string
	var matchRes []string
	matchRes = ruleOutputReSimple.FindStringSubmatch(s)
	if matchRes != nil {
		outputS = matchRes[1]
	} else {
		matchRes = ruleOutputReVariants.FindStringSubmatch(s)
		if matchRes == nil {
			return []string{}, fmt.Errorf("invalid rule output definition: " + l)
		}
		outputS = matchRes[1]
	}
	if strings.Contains(outputS, "->") {
		return []string{}, fmt.Errorf("invalid rule output definition: " + l)
	}
	return commaSplit.Split(outputS, -1), nil
}

func newRule(s string, vars map[string]string) (Rule, error) {
	// INPUT -> OUTPUT
	// INPUT -> OUTPUT / LEFTCONTEXT _ RIGHTCONTEXT
	matchRes := ruleRe.FindStringSubmatch(s)
	if matchRes == nil {
		return Rule{}, fmt.Errorf("invalid rule definition: " + s)
	}
	input := matchRes[1]
	output, err := newRuleOutput(matchRes[2], s)
	if err != nil {
		return Rule{}, err
	}
	left, right, err := newContext(matchRes[3], vars)
	if err != nil {
		return Rule{}, err
	}
	return Rule{Input: input, Output: output, LeftContext: left, RightContext: right}, nil
}
<|endoftext|>


<commit_before>/*
	Go Language Raspberry Pi Interface
	(c) Copyright David Thorpe 2016-2017
	All Rights Reserved
	Documentation http://djthorpe.github.io/gopi/
	For Licensing and Usage information, please see LICENSE.md
*/

package gopi

import (
	"strings"
)

// InputManager allows you to open and close input devices
// and subscribe to events emitted by devices
type InputManager interface {
	Driver
	Publisher

	// Open Devices by name, type and bus
	OpenDevicesByName(name string, flags InputDeviceType, bus InputDeviceBus) ([]InputDevice, error)

	// Close Device
	CloseDevice(device InputDevice) error

	/*
		// Add a device to managed input devices
		AddDevice(device InputDevice) error

		// Return a list of open devices
		GetOpenDevices() []InputDevice
	*/
}

type InputDevice interface {
	Driver
	Publisher

	// Name of the input device
	Name() string

	// Type of device
	Type() InputDeviceType

	// Bus interface
	Bus() InputDeviceBus

	// Position of cursor (for mouse, joystick and touchscreen devices)
	Position() Point
	/*
		// Set absolute current cursor position
		SetPosition(Point)
	*/

	// Get key states (caps lock, shift, scroll lock, num lock, etc)
	KeyState() KeyState

	// Set key state (or states) to on or off. Will return error
	// for key states which are not modifiable
	SetKeyState(flags KeyState, state bool) error

	// Returns true if device matches conditions
	Matches(string, InputDeviceType, InputDeviceBus) bool
}

// Device type (keyboard, mouse, touchscreen, etc)
type InputDeviceType uint8

// Event type (button press, button release, etc)
type InputEventType uint16

// Key Code
type KeyCode uint16

// Key State
type KeyState uint16

// Bus type (USB, Bluetooth, etc)
type InputDeviceBus uint16

////////////////////////////////////////////////////////////////////////////////
// CONSTANTS

// Types of input device
const (
	INPUT_TYPE_NONE        InputDeviceType = 0x00
	INPUT_TYPE_KEYBOARD    InputDeviceType = 0x01
	INPUT_TYPE_MOUSE       InputDeviceType = 0x02
	INPUT_TYPE_TOUCHSCREEN InputDeviceType = 0x04
	INPUT_TYPE_JOYSTICK    InputDeviceType = 0x08
	INPUT_TYPE_REMOTE      InputDeviceType = 0x10
	INPUT_TYPE_ANY         InputDeviceType = 0xFF
)

// Types of input connection
const (
	INPUT_BUS_NONE      InputDeviceBus = 0x0000
	INPUT_BUS_PCI       InputDeviceBus = 0x0001
	INPUT_BUS_ISAPNP    InputDeviceBus = 0x0002
	INPUT_BUS_USB       InputDeviceBus = 0x0003
	INPUT_BUS_HIL       InputDeviceBus = 0x0004
	INPUT_BUS_BLUETOOTH InputDeviceBus = 0x0005
	INPUT_BUS_VIRTUAL   InputDeviceBus = 0x0006
	INPUT_BUS_ISA       InputDeviceBus = 0x0010
	INPUT_BUS_I8042     InputDeviceBus = 0x0011
	INPUT_BUS_XTKBD     InputDeviceBus = 0x0012
	INPUT_BUS_RS232     InputDeviceBus = 0x0013
	INPUT_BUS_GAMEPORT  InputDeviceBus = 0x0014
	INPUT_BUS_PARPORT   InputDeviceBus = 0x0015
	INPUT_BUS_AMIGA     InputDeviceBus = 0x0016
	INPUT_BUS_ADB       InputDeviceBus = 0x0017
	INPUT_BUS_I2C       InputDeviceBus = 0x0018
	INPUT_BUS_HOST      InputDeviceBus = 0x0019
	INPUT_BUS_GSC       InputDeviceBus = 0x001A
	INPUT_BUS_ATARI     InputDeviceBus = 0x001B
	INPUT_BUS_SPI       InputDeviceBus = 0x001C
	INPUT_BUS_ANY       InputDeviceBus = 0xFFFF
)

// Input events
const (
	INPUT_EVENT_NONE InputEventType = 0x0000

	// Mouse and/or keyboard key/button press events
	INPUT_EVENT_KEYPRESS   InputEventType = 0x0001
	INPUT_EVENT_KEYRELEASE InputEventType = 0x0002
	INPUT_EVENT_KEYREPEAT  InputEventType = 0x0003

	// Mouse and/or touchscreen move events
	INPUT_EVENT_ABSPOSITION InputEventType = 0x0004
	INPUT_EVENT_RELPOSITION InputEventType = 0x0005

	// Multi-touch events
	INPUT_EVENT_TOUCHPRESS    InputEventType = 0x0006
	INPUT_EVENT_TOUCHRELEASE  InputEventType = 0x0007
	INPUT_EVENT_TOUCHPOSITION InputEventType = 0x0008
)

// Input key state
const (
	KEYSTATE_NONE       KeyState = 0x0000
	KEYSTATE_MIN        KeyState = KEYSTATE_SCROLLLOCK
	KEYSTATE_SCROLLLOCK KeyState = 0x0001        // Scroll Lock
	KEYSTATE_NUMLOCK    KeyState = 0x0002        // Num Lock
	KEYSTATE_CAPSLOCK   KeyState = 0x0004        // Caps Lock
	KEYSTATE_LEFTSHIFT  KeyState = 0x0010        // Left Shift
	KEYSTATE_RIGHTSHIFT KeyState = 0x0020        // Right Shift
	KEYSTATE_SHIFT      KeyState = 0x0030        // Either Shift
	KEYSTATE_LEFTALT    KeyState = 0x0040        // Left Alt
	KEYSTATE_RIGHTALT   KeyState = 0x0080        // Right Alt
	KEYSTATE_ALT        KeyState = 0x00C0        // Either Alt
	KEYSTATE_LEFTMETA   KeyState = 0x0100        // Left Meta/Command
	KEYSTATE_RIGHTMETA  KeyState = 0x0200        // Right Meta/Command
	KEYSTATE_META       KeyState = 0x0300        // Either Meta/Command
	KEYSTATE_LEFTCTRL   KeyState = 0x0400        // Left Control
	KEYSTATE_RIGHTCTRL  KeyState = 0x0800        // Right Control
	KEYSTATE_CTRL       KeyState = 0x0C00        // Either Control
	KEYSTATE_MAX        KeyState = KEYSTATE_CTRL // Maximum
	KEYSTATE_MASK       KeyState = 0x0CFF        // Bitmask
)

////////////////////////////////////////////////////////////////////////////////
// STRINGIFY

func (t InputDeviceType) String() string {
	switch t {
	case INPUT_TYPE_NONE:
		return "INPUT_TYPE_NONE"
	case INPUT_TYPE_KEYBOARD:
		return "INPUT_TYPE_KEYBOARD"
	case INPUT_TYPE_MOUSE:
		return "INPUT_TYPE_MOUSE"
	case INPUT_TYPE_TOUCHSCREEN:
		return "INPUT_TYPE_TOUCHSCREEN"
	case INPUT_TYPE_JOYSTICK:
		return "INPUT_TYPE_JOYSTICK"
	case INPUT_TYPE_REMOTE:
		return "INPUT_TYPE_REMOTE"
	default:
		return "[?? Invalid InputDeviceType value]"
	}
}

func (b InputDeviceBus) String() string {
	switch b {
	case INPUT_BUS_NONE:
		return "INPUT_BUS_NONE"
	case INPUT_BUS_PCI:
		return "INPUT_BUS_PCI"
	case INPUT_BUS_ISAPNP:
		return "INPUT_BUS_ISAPNP"
	case INPUT_BUS_USB:
		return "INPUT_BUS_USB"
	case INPUT_BUS_HIL:
		return "INPUT_BUS_HIL"
	case INPUT_BUS_BLUETOOTH:
		return "INPUT_BUS_BLUETOOTH"
	case INPUT_BUS_VIRTUAL:
		return "INPUT_BUS_VIRTUAL"
	case INPUT_BUS_ISA:
		return "INPUT_BUS_ISA"
	case INPUT_BUS_I8042:
		return "INPUT_BUS_I8042"
	case INPUT_BUS_XTKBD:
		return "INPUT_BUS_XTKBD"
	case INPUT_BUS_RS232:
		return "INPUT_BUS_RS232"
	case INPUT_BUS_GAMEPORT:
		return "INPUT_BUS_GAMEPORT"
	case INPUT_BUS_PARPORT:
		return "INPUT_BUS_PARPORT"
	case INPUT_BUS_AMIGA:
		return "INPUT_BUS_AMIGA"
	case INPUT_BUS_ADB:
		return "INPUT_BUS_ADB"
	case INPUT_BUS_I2C:
		return "INPUT_BUS_I2C"
	case INPUT_BUS_HOST:
		return "INPUT_BUS_HOST"
	case INPUT_BUS_GSC:
		return "INPUT_BUS_GSC"
	case INPUT_BUS_ATARI:
		return "INPUT_BUS_ATARI"
	case INPUT_BUS_SPI:
		return "INPUT_BUS_SPI"
	default:
		return "[?? Invalid InputDeviceBus value]"
	}
}

func (e InputEventType) String() string {
	switch e {
	case INPUT_EVENT_NONE:
		return "INPUT_EVENT_NONE"
	case INPUT_EVENT_KEYPRESS:
		return "INPUT_EVENT_KEYPRESS"
	case INPUT_EVENT_KEYRELEASE:
		return "INPUT_EVENT_KEYRELEASE"
	case INPUT_EVENT_KEYREPEAT:
		return "INPUT_EVENT_KEYREPEAT"
	case INPUT_EVENT_ABSPOSITION:
		return "INPUT_EVENT_ABSPOSITION"
	case INPUT_EVENT_RELPOSITION:
		return "INPUT_EVENT_RELPOSITION"
	case INPUT_EVENT_TOUCHPRESS:
		return "INPUT_EVENT_TOUCHPRESS"
	case INPUT_EVENT_TOUCHRELEASE:
		return "INPUT_EVENT_TOUCHRELEASE"
	case INPUT_EVENT_TOUCHPOSITION:
		return "INPUT_EVENT_TOUCHPOSITION"
	default:
		return "[?? Invalid InputEventType value]"
	}
}

func (s KeyState) String() string {
	if s == KEYSTATE_NONE {
		return "KEYSTATE_NONE"
	}
	flags := ""
	if s&KEYSTATE_SCROLLLOCK != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_SCROLLLOCK"
	}
	if s&KEYSTATE_NUMLOCK != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_NUMLOCK"
	}
	if s&KEYSTATE_CAPSLOCK != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_CAPSLOCK"
	}
	if s&KEYSTATE_LEFTSHIFT != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_LEFTSHIFT"
	}
	if s&KEYSTATE_RIGHTSHIFT != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_RIGHTSHIFT"
	}
	if s&KEYSTATE_LEFTALT != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_LEFTALT"
	}
	if s&KEYSTATE_RIGHTALT != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_RIGHTALT"
	}
	if s&KEYSTATE_LEFTMETA != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_LEFTMETA"
	}
	if s&KEYSTATE_RIGHTMETA != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_RIGHTMETA"
	}
	if s&KEYSTATE_LEFTCTRL != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_LEFTCTRL"
	}
	if s&KEYSTATE_RIGHTCTRL != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_RIGHTCTRL"
	}
	return strings.Trim(flags, "|")
}
<commit_msg>Added SetPosition to input interface<commit_after>/*
	Go Language Raspberry Pi Interface
	(c) Copyright David Thorpe 2016-2017
	All Rights Reserved
	Documentation http://djthorpe.github.io/gopi/
	For Licensing and Usage information, please see LICENSE.md
*/

package gopi

import (
	"strings"
)

// InputManager allows you to open and close input devices
// and subscribe to events emitted by devices
type InputManager interface {
	Driver
	Publisher

	// Open Devices by name, type and bus
	OpenDevicesByName(name string, flags InputDeviceType, bus InputDeviceBus) ([]InputDevice, error)

	// Close Device
	CloseDevice(device InputDevice) error

	/*
		// Add a device to managed input devices
		AddDevice(device InputDevice) error

		// Return a list of open devices
		GetOpenDevices() []InputDevice
	*/
}

type InputDevice interface {
	Driver
	Publisher

	// Name of the input device
	Name() string

	// Type of device
	Type() InputDeviceType

	// Bus interface
	Bus() InputDeviceBus

	// Position of cursor (for mouse, joystick and touchscreen devices)
	Position() Point

	// Set absolute current cursor position
	SetPosition(Point)

	// Get key states (caps lock, shift, scroll lock, num lock, etc)
	KeyState() KeyState

	// Set key state (or states) to on or off. Will return error
	// for key states which are not modifiable
	SetKeyState(flags KeyState, state bool) error

	// Returns true if device matches conditions
	Matches(string, InputDeviceType, InputDeviceBus) bool
}

// Device type (keyboard, mouse, touchscreen, etc)
type InputDeviceType uint8

// Event type (button press, button release, etc)
type InputEventType uint16

// Key Code
type KeyCode uint16

// Key State
type KeyState uint16

// Bus type (USB, Bluetooth, etc)
type InputDeviceBus uint16

////////////////////////////////////////////////////////////////////////////////
// CONSTANTS

// Types of input device
const (
	INPUT_TYPE_NONE        InputDeviceType = 0x00
	INPUT_TYPE_KEYBOARD    InputDeviceType = 0x01
	INPUT_TYPE_MOUSE       InputDeviceType = 0x02
	INPUT_TYPE_TOUCHSCREEN InputDeviceType = 0x04
	INPUT_TYPE_JOYSTICK    InputDeviceType = 0x08
	INPUT_TYPE_REMOTE      InputDeviceType = 0x10
	INPUT_TYPE_ANY         InputDeviceType = 0xFF
)

// Types of input connection
const (
	INPUT_BUS_NONE      InputDeviceBus = 0x0000
	INPUT_BUS_PCI       InputDeviceBus = 0x0001
	INPUT_BUS_ISAPNP    InputDeviceBus = 0x0002
	INPUT_BUS_USB       InputDeviceBus = 0x0003
	INPUT_BUS_HIL       InputDeviceBus = 0x0004
	INPUT_BUS_BLUETOOTH InputDeviceBus = 0x0005
	INPUT_BUS_VIRTUAL   InputDeviceBus = 0x0006
	INPUT_BUS_ISA       InputDeviceBus = 0x0010
	INPUT_BUS_I8042     InputDeviceBus = 0x0011
	INPUT_BUS_XTKBD     InputDeviceBus = 0x0012
	INPUT_BUS_RS232     InputDeviceBus = 0x0013
	INPUT_BUS_GAMEPORT  InputDeviceBus = 0x0014
	INPUT_BUS_PARPORT   InputDeviceBus = 0x0015
	INPUT_BUS_AMIGA     InputDeviceBus = 0x0016
	INPUT_BUS_ADB       InputDeviceBus = 0x0017
	INPUT_BUS_I2C       InputDeviceBus = 0x0018
	INPUT_BUS_HOST      InputDeviceBus = 0x0019
	INPUT_BUS_GSC       InputDeviceBus = 0x001A
	INPUT_BUS_ATARI     InputDeviceBus = 0x001B
	INPUT_BUS_SPI       InputDeviceBus = 0x001C
	INPUT_BUS_ANY       InputDeviceBus = 0xFFFF
)

// Input events
const (
	INPUT_EVENT_NONE InputEventType = 0x0000

	// Mouse and/or keyboard key/button press events
	INPUT_EVENT_KEYPRESS   InputEventType = 0x0001
	INPUT_EVENT_KEYRELEASE InputEventType = 0x0002
	INPUT_EVENT_KEYREPEAT  InputEventType = 0x0003

	// Mouse and/or touchscreen move events
	INPUT_EVENT_ABSPOSITION InputEventType = 0x0004
	INPUT_EVENT_RELPOSITION InputEventType = 0x0005

	// Multi-touch events
	INPUT_EVENT_TOUCHPRESS    InputEventType = 0x0006
	INPUT_EVENT_TOUCHRELEASE  InputEventType = 0x0007
	INPUT_EVENT_TOUCHPOSITION InputEventType = 0x0008
)

// Input key state
const (
	KEYSTATE_NONE       KeyState = 0x0000
	KEYSTATE_MIN        KeyState = KEYSTATE_SCROLLLOCK
	KEYSTATE_SCROLLLOCK KeyState = 0x0001        // Scroll Lock
	KEYSTATE_NUMLOCK    KeyState = 0x0002        // Num Lock
	KEYSTATE_CAPSLOCK   KeyState = 0x0004        // Caps Lock
	KEYSTATE_LEFTSHIFT  KeyState = 0x0010        // Left Shift
	KEYSTATE_RIGHTSHIFT KeyState = 0x0020        // Right Shift
	KEYSTATE_SHIFT      KeyState = 0x0030        // Either Shift
	KEYSTATE_LEFTALT    KeyState = 0x0040        // Left Alt
	KEYSTATE_RIGHTALT   KeyState = 0x0080        // Right Alt
	KEYSTATE_ALT        KeyState = 0x00C0        // Either Alt
	KEYSTATE_LEFTMETA   KeyState = 0x0100        // Left Meta/Command
	KEYSTATE_RIGHTMETA  KeyState = 0x0200        // Right Meta/Command
	KEYSTATE_META       KeyState = 0x0300        // Either Meta/Command
	KEYSTATE_LEFTCTRL   KeyState = 0x0400        // Left Control
	KEYSTATE_RIGHTCTRL  KeyState = 0x0800        // Right Control
	KEYSTATE_CTRL       KeyState = 0x0C00        // Either Control
	KEYSTATE_MAX        KeyState = KEYSTATE_CTRL // Maximum
	KEYSTATE_MASK       KeyState = 0x0CFF        // Bitmask
)

////////////////////////////////////////////////////////////////////////////////
// STRINGIFY

func (t InputDeviceType) String() string {
	switch t {
	case INPUT_TYPE_NONE:
		return "INPUT_TYPE_NONE"
	case INPUT_TYPE_KEYBOARD:
		return "INPUT_TYPE_KEYBOARD"
	case INPUT_TYPE_MOUSE:
		return "INPUT_TYPE_MOUSE"
	case INPUT_TYPE_TOUCHSCREEN:
		return "INPUT_TYPE_TOUCHSCREEN"
	case INPUT_TYPE_JOYSTICK:
		return "INPUT_TYPE_JOYSTICK"
	case INPUT_TYPE_REMOTE:
		return "INPUT_TYPE_REMOTE"
	default:
		return "[?? Invalid InputDeviceType value]"
	}
}

func (b InputDeviceBus) String() string {
	switch b {
	case INPUT_BUS_NONE:
		return "INPUT_BUS_NONE"
	case INPUT_BUS_PCI:
		return "INPUT_BUS_PCI"
	case INPUT_BUS_ISAPNP:
		return "INPUT_BUS_ISAPNP"
	case INPUT_BUS_USB:
		return "INPUT_BUS_USB"
	case INPUT_BUS_HIL:
		return "INPUT_BUS_HIL"
	case INPUT_BUS_BLUETOOTH:
		return "INPUT_BUS_BLUETOOTH"
	case INPUT_BUS_VIRTUAL:
		return "INPUT_BUS_VIRTUAL"
	case INPUT_BUS_ISA:
		return "INPUT_BUS_ISA"
	case INPUT_BUS_I8042:
		return "INPUT_BUS_I8042"
	case INPUT_BUS_XTKBD:
		return "INPUT_BUS_XTKBD"
	case INPUT_BUS_RS232:
		return "INPUT_BUS_RS232"
	case INPUT_BUS_GAMEPORT:
		return "INPUT_BUS_GAMEPORT"
	case INPUT_BUS_PARPORT:
		return "INPUT_BUS_PARPORT"
	case INPUT_BUS_AMIGA:
		return "INPUT_BUS_AMIGA"
	case INPUT_BUS_ADB:
		return "INPUT_BUS_ADB"
	case INPUT_BUS_I2C:
		return "INPUT_BUS_I2C"
	case INPUT_BUS_HOST:
		return "INPUT_BUS_HOST"
	case INPUT_BUS_GSC:
		return "INPUT_BUS_GSC"
	case INPUT_BUS_ATARI:
		return "INPUT_BUS_ATARI"
	case INPUT_BUS_SPI:
		return "INPUT_BUS_SPI"
	default:
		return "[?? Invalid InputDeviceBus value]"
	}
}

func (e InputEventType) String() string {
	switch e {
	case INPUT_EVENT_NONE:
		return "INPUT_EVENT_NONE"
	case INPUT_EVENT_KEYPRESS:
		return "INPUT_EVENT_KEYPRESS"
	case INPUT_EVENT_KEYRELEASE:
		return "INPUT_EVENT_KEYRELEASE"
	case INPUT_EVENT_KEYREPEAT:
		return "INPUT_EVENT_KEYREPEAT"
	case INPUT_EVENT_ABSPOSITION:
		return "INPUT_EVENT_ABSPOSITION"
	case INPUT_EVENT_RELPOSITION:
		return "INPUT_EVENT_RELPOSITION"
	case INPUT_EVENT_TOUCHPRESS:
		return "INPUT_EVENT_TOUCHPRESS"
	case INPUT_EVENT_TOUCHRELEASE:
		return "INPUT_EVENT_TOUCHRELEASE"
	case INPUT_EVENT_TOUCHPOSITION:
		return "INPUT_EVENT_TOUCHPOSITION"
	default:
		return "[?? Invalid InputEventType value]"
	}
}

func (s KeyState) String() string {
	if s == KEYSTATE_NONE {
		return "KEYSTATE_NONE"
	}
	flags := ""
	if s&KEYSTATE_SCROLLLOCK != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_SCROLLLOCK"
	}
	if s&KEYSTATE_NUMLOCK != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_NUMLOCK"
	}
	if s&KEYSTATE_CAPSLOCK != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_CAPSLOCK"
	}
	if s&KEYSTATE_LEFTSHIFT != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_LEFTSHIFT"
	}
	if s&KEYSTATE_RIGHTSHIFT != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_RIGHTSHIFT"
	}
	if s&KEYSTATE_LEFTALT != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_LEFTALT"
	}
	if s&KEYSTATE_RIGHTALT != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_RIGHTALT"
	}
	if s&KEYSTATE_LEFTMETA != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_LEFTMETA"
	}
	if s&KEYSTATE_RIGHTMETA != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_RIGHTMETA"
	}
	if s&KEYSTATE_LEFTCTRL != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_LEFTCTRL"
	}
	if s&KEYSTATE_RIGHTCTRL != KEYSTATE_NONE {
		flags = flags + "|KEYSTATE_RIGHTCTRL"
	}
	return strings.Trim(flags, "|")
}
<|endoftext|>