<commit_before>package xpath

/*
#cgo pkg-config: libxml-2.0
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include <libxml/parser.h>

int getXPathObjectType(xmlXPathObject* o);

*/
import "C"

import "unsafe"
import "reflect"
import . "github.com/moovweb/gokogiri/util"

//export go_resolve_variables
func go_resolve_variables(ctxt unsafe.Pointer, name, ns *C.char) (ret C.xmlXPathObjectPtr) {
	variable := C.GoString(name)
	namespace := C.GoString(ns)

	context := (*VariableScope)(ctxt)
	if context != nil {
		val := (*context).ResolveVariable(variable, namespace)
		ret = ValueToXPathObject(val)
	}
	return
}

// Convert an arbitrary value into a C.xmlXPathObjectPtr
// Unrecognised and nil values are converted to empty node sets.
func ValueToXPathObject(val interface{}) (ret C.xmlXPathObjectPtr) {
	if val == nil {
		//return the empty node set
		ret = C.xmlXPathNewNodeSet(nil)
		return
	}
	switch val.(type) {
	case []unsafe.Pointer:
		ptrs := val.([]unsafe.Pointer)
		if len(ptrs) > 0 {
			//default - return a node set
			ret = C.xmlXPathNewNodeSet(nil)
			for _, p := range ptrs {
				_ = C.xmlXPathNodeSetAdd(ret.nodesetval, (*C.xmlNode)(p))
			}
		} else {
			ret = C.xmlXPathNewNodeSet(nil)
			return
		}
	case float64:
		content := val.(float64)
		ret = C.xmlXPathNewFloat(C.double(content))
	case string:
		content := val.(string)
		xpathBytes := GetCString([]byte(content))
		xpathPtr := unsafe.Pointer(&xpathBytes[0])
		ret = C.xmlXPathNewString((*C.xmlChar)(xpathPtr))
	default:
		typ := reflect.TypeOf(val)
		// if a pointer to a struct is passed, get the type of the dereferenced object
		if typ.Kind() == reflect.Ptr {
			typ = typ.Elem()
		}
		//log the unknown type, return an empty node set
		//fmt.Println("go-resolve wrong-type", typ.Kind())
		ret = C.xmlXPathNewNodeSet(nil)
	}
	return
}

func XPathObjectToValue(obj C.xmlXPathObjectPtr) (result interface{}) {
	rt := XPathObjectType(C.getXPathObjectType(obj))
	switch rt {
	case XPATH_NODESET:
		//nodePtrs, _ := xpathCtx.ResultAsNodeset()
		result = nil
	case XPATH_NUMBER:
		obj = C.xmlXPathConvertNumber(obj)
		result = float64(obj.floatval)
	case XPATH_BOOLEAN:
		obj = C.xmlXPathConvertBoolean(obj)
		result = obj.boolval != 0
	default:
		obj = C.xmlXPathConvertString(obj)
		result = C.GoString((*C.char)(unsafe.Pointer(obj.stringval)))
	}
	return
}

//export exec_xpath_function
func exec_xpath_function(ctxt C.xmlXPathParserContextPtr, nargs C.int) {
	function := C.GoString((*C.char)(unsafe.Pointer(ctxt.context.function)))
	namespace := C.GoString((*C.char)(unsafe.Pointer(ctxt.context.functionURI)))
	context := (*VariableScope)(ctxt.context.funcLookupData)

	argcount := int(nargs)
	var args []interface{}

	for i := 0; i < argcount; i = i + 1 {
		args = append(args, XPathObjectToValue(C.valuePop(ctxt)))
	}

	if argcount > 1 {
		for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 {
			args[i], args[j] = args[j], args[i]
		}
	}

	f := (*context).ResolveFunction(function, namespace)
	if f != nil {
		retval := f(*context, args)
		C.valuePush(ctxt, ValueToXPathObject(retval))
	} else {
		ret := C.xmlXPathNewNodeSet(nil)
		C.valuePush(ctxt, ret)
	}

}

//export go_can_resolve_function
func go_can_resolve_function(ctxt unsafe.Pointer, name, ns *C.char) (ret C.int) {
	function := C.GoString(name)
	namespace := C.GoString(ns)
	context := (*VariableScope)(ctxt)
	if (*context).IsFunctionRegistered(function, namespace) {
		return C.int(1)
	}
	return C.int(0)
}
<commit_msg>Correctly handle nodesets in XPathObjectToValue<commit_after>package xpath

/*
#cgo pkg-config: libxml-2.0
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include <libxml/parser.h>

int getXPathObjectType(xmlXPathObject* o);

*/
import "C"

import "unsafe"
import "reflect"
import . "github.com/moovweb/gokogiri/util"

//export go_resolve_variables
func go_resolve_variables(ctxt unsafe.Pointer, name, ns *C.char) (ret C.xmlXPathObjectPtr) {
	variable := C.GoString(name)
	namespace := C.GoString(ns)

	context := (*VariableScope)(ctxt)
	if context != nil {
		val := (*context).ResolveVariable(variable, namespace)
		ret = ValueToXPathObject(val)
	}
	return
}

// Convert an arbitrary value into a C.xmlXPathObjectPtr
// Unrecognised and nil values are converted to empty node sets.
func ValueToXPathObject(val interface{}) (ret C.xmlXPathObjectPtr) {
	if val == nil {
		//return the empty node set
		ret = C.xmlXPathNewNodeSet(nil)
		return
	}
	switch val.(type) {
	case []unsafe.Pointer:
		ptrs := val.([]unsafe.Pointer)
		if len(ptrs) > 0 {
			//default - return a node set
			ret = C.xmlXPathNewNodeSet(nil)
			for _, p := range ptrs {
				_ = C.xmlXPathNodeSetAdd(ret.nodesetval, (*C.xmlNode)(p))
			}
		} else {
			ret = C.xmlXPathNewNodeSet(nil)
			return
		}
	case float64:
		content := val.(float64)
		ret = C.xmlXPathNewFloat(C.double(content))
	case string:
		content := val.(string)
		xpathBytes := GetCString([]byte(content))
		xpathPtr := unsafe.Pointer(&xpathBytes[0])
		ret = C.xmlXPathNewString((*C.xmlChar)(xpathPtr))
	default:
		typ := reflect.TypeOf(val)
		// if a pointer to a struct is passed, get the type of the dereferenced object
		if typ.Kind() == reflect.Ptr {
			typ = typ.Elem()
		}
		//log the unknown type, return an empty node set
		//fmt.Println("go-resolve wrong-type", typ.Kind())
		ret = C.xmlXPathNewNodeSet(nil)
	}
	return
}

func XPathObjectToValue(obj C.xmlXPathObjectPtr) (result interface{}) {
	rt := XPathObjectType(C.getXPathObjectType(obj))
	switch rt {
	case XPATH_NODESET:
		if nodesetPtr := obj.nodesetval; nodesetPtr != nil {
			if nodesetSize := int(nodesetPtr.nodeNr); nodesetSize > 0 {
				nodes := make([]unsafe.Pointer, nodesetSize)
				for i := 0; i < nodesetSize; i++ {
					nodes[i] = unsafe.Pointer(C.fetchNode(nodesetPtr, C.int(i)))
				}
				result = nodes
				return
			}
		}
		result = nil
	case XPATH_NUMBER:
		obj = C.xmlXPathConvertNumber(obj)
		result = float64(obj.floatval)
	case XPATH_BOOLEAN:
		obj = C.xmlXPathConvertBoolean(obj)
		result = obj.boolval != 0
	default:
		obj = C.xmlXPathConvertString(obj)
		result = C.GoString((*C.char)(unsafe.Pointer(obj.stringval)))
	}
	return
}

//export exec_xpath_function
func exec_xpath_function(ctxt C.xmlXPathParserContextPtr, nargs C.int) {
	function := C.GoString((*C.char)(unsafe.Pointer(ctxt.context.function)))
	namespace := C.GoString((*C.char)(unsafe.Pointer(ctxt.context.functionURI)))
	context := (*VariableScope)(ctxt.context.funcLookupData)

	argcount := int(nargs)
	var args []interface{}

	for i := 0; i < argcount; i = i + 1 {
		args = append(args, XPathObjectToValue(C.valuePop(ctxt)))
	}

	// arguments are popped off the stack in reverse order, so
	// we reverse the slice before invoking our callback
	if argcount > 1 {
		for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 {
			args[i], args[j] = args[j], args[i]
		}
	}

	// push the result onto the stack
	// if for some reason we are unable to resolve the
	// function we push an empty nodeset
	f := (*context).ResolveFunction(function, namespace)
	if f != nil {
		retval := f(*context, args)
		C.valuePush(ctxt, ValueToXPathObject(retval))
	} else {
		ret := C.xmlXPathNewNodeSet(nil)
		C.valuePush(ctxt, ret)
	}

}

//export go_can_resolve_function
func go_can_resolve_function(ctxt unsafe.Pointer, name, ns *C.char) (ret C.int) {
	function := C.GoString(name)
	namespace := C.GoString(ns)
	context := (*VariableScope)(ctxt)
	if (*context).IsFunctionRegistered(function, namespace) {
		return C.int(1)
	}
	return C.int(0)
}
<|endoftext|>


<commit_before>package main

import (
  "math"
  "fmt"
  "database/sql"
  _ "github.com/mattn/go-sqlite3"
)

// Store a geolocation
type Location struct {
  lat float64
  long float64
}

// Helper function to take care of errors
func handleErrors(err error) {
  if err != nil {
    panic(err)
  }
}

// Converts degrees into radians
func degreesToRadians(degrees float64) float64 {
  for degrees >= 360.0 {
    degrees -= 360
  }

  return degrees * (math.Pi / 180)
}

// Haversine distance computation
// Takes two locations and returns the distance between the two
func haversineDistance(loc0 Location, loc1 Location) float64 {
  lat0 := degreesToRadians(loc0.lat)
  lat1 := degreesToRadians(loc1.lat)
  delta_lat := degreesToRadians(loc1.lat - loc0.lat)
  delta_long := degreesToRadians(loc1.long - loc0.long)
  r := 6365.079 // Earth radius under London

  return 2.0 * r * math.Asin(math.Sqrt(math.Pow(delta_lat / 2.0, 2.0) + math.Cos(lat1) * math.Cos(lat0) * math.Pow(delta_long / 2.0, 2.0)))
}

// Fetches location data from a string postcode
func getLocationFromPostcode(postcode string) Location {
  db, err := sql.Open("sqlite3", "./postcode.sl3")
  handleErrors(err)
  defer db.Close()

  var location Location
  rows, err := db.Query(fmt.Sprintf("select lat, long from postcode where code='%s' limit 1", postcode))
  rows.Next()
  handleErrors(rows.Scan(&location.lat, &location.long))

  return location
}

func main() {

}
<commit_msg>Impl distanceBetweenPostcodes<commit_after>package main

import (
  "math"
  "fmt"
  "database/sql"
  _ "github.com/mattn/go-sqlite3"
)

// Store a geolocation
type Location struct {
  lat float64
  long float64
}

// Helper function to take care of errors
func handleErrors(err error) {
  if err != nil {
    panic(err)
  }
}

// Converts degrees into radians
func degreesToRadians(degrees float64) float64 {
  for degrees >= 360.0 {
    degrees -= 360
  }

  return degrees * (math.Pi / 180)
}

// Haversine distance computation
// Takes two locations and returns the distance between the two
func haversineDistance(loc0 Location, loc1 Location) float64 {
  lat0 := degreesToRadians(loc0.lat)
  lat1 := degreesToRadians(loc1.lat)
  delta_lat := degreesToRadians(loc1.lat - loc0.lat)
  delta_long := degreesToRadians(loc1.long - loc0.long)
  r := 6365.079 // Earth radius under London

  return 2.0 * r * math.Asin(math.Sqrt(math.Pow(delta_lat / 2.0, 2.0) + math.Cos(lat1) * math.Cos(lat0) * math.Pow(delta_long / 2.0, 2.0)))
}

// Fetches location data from a string postcode
func getLocationFromPostcode(postcode string) Location {
  db, err := sql.Open("sqlite3", "./postcode.sl3")
  handleErrors(err)
  defer db.Close()

  var location Location
  rows, err := db.Query(fmt.Sprintf("select lat, long from postcode where code='%s' limit 1", postcode))
  rows.Next()
  handleErrors(rows.Scan(&location.lat, &location.long))

  return location
}

// Returns the distance between two postcodes
func distanceBetweenPostcodes(postcode0, postcode1 string) float64 {
  loc0 := getLocationFromPostcode(postcode0)
  loc1 := getLocationFromPostcode(postcode1)

  return haversineDistance(loc0, loc1)
}

func main() {

}
<|endoftext|>


<commit_before>package addrs

import (
	"fmt"
	"strings"

	"golang.org/x/net/idna"

	"github.com/hashicorp/hcl/v2"
	svchost "github.com/hashicorp/terraform-svchost"
	"github.com/hashicorp/terraform/tfdiags"
)

// Provider encapsulates a single provider type. In the future this will be
// extended to include additional fields including Namespace and SourceHost
type Provider struct {
	Type      string
	Namespace string
	Hostname  svchost.Hostname
}

// DefaultRegistryHost is the hostname used for provider addresses that do
// not have an explicit hostname.
const DefaultRegistryHost = svchost.Hostname("registry.terraform.io")

// LegacyProviderNamespace is the special string used in the Namespace field
// of type Provider to mark a legacy provider address. This special namespace
// value would normally be invalid, and can be used only when the hostname is
// DefaultRegistryHost because that host owns the mapping from legacy name to
// FQN.
const LegacyProviderNamespace = "-"

// String returns an FQN string, indended for use in output.
func (pt Provider) String() string {
	if pt.IsZero() {
		panic("called String on zero-value addrs.Provider")
	}
	return pt.Hostname.ForDisplay() + "/" + pt.Namespace + "/" + pt.Type
}

// NewProvider constructs a provider address from its parts, and normalizes
// the namespace and type parts to lowercase using unicode case folding rules
// so that resulting addrs.Provider values can be compared using standard
// Go equality rules (==).
//
// The hostname is given as a svchost.Hostname, which is required by the
// contract of that type to have already been normalized for equality testing.
//
// This function will panic if the given namespace or type name are not valid.
// When accepting namespace or type values from outside the program, use
// ParseProviderPart first to check that the given value is valid.
func NewProvider(hostname svchost.Hostname, namespace, typeName string) Provider {
	if namespace == LegacyProviderNamespace {
		// Legacy provider addresses must always be created via
		// NewLegacyProvider so that we can use static analysis to find
		// codepaths still working with those.
		panic("attempt to create legacy provider address using NewProvider; use NewLegacyProvider instead")
	}

	return Provider{
		Type:      MustParseProviderPart(typeName),
		Namespace: MustParseProviderPart(namespace),
		Hostname:  hostname,
	}
}

// NewDefaultProvider returns the default address of a HashiCorp-maintained,
// Registry-hosted provider.
func NewDefaultProvider(name string) Provider {
	return Provider{
		Type:      MustParseProviderPart(name),
		Namespace: "hashicorp",
		Hostname:  DefaultRegistryHost,
	}
}

// NewLegacyProvider returns a mock address for a provider.
// This will be removed when ProviderType is fully integrated.
func NewLegacyProvider(name string) Provider {
	return Provider{
		// We intentionally don't normalize and validate the legacy names,
		// because existing code expects legacy provider names to pass through
		// verbatim, even if not compliant with our new naming rules.
		Type:      name,
		Namespace: LegacyProviderNamespace,
		Hostname:  DefaultRegistryHost,
	}
}

// LegacyString returns the provider type, which is frequently used
// interchangeably with provider name. This function can and should be removed
// when provider type is fully integrated. As a safeguard for future
// refactoring, this function panics if the Provider is not a legacy provider.
func (pt Provider) LegacyString() string {
	if pt.IsZero() {
		panic("called LegacyString on zero-value addrs.Provider")
	}
	if pt.Namespace != LegacyProviderNamespace {
		panic(pt.String() + " is not a legacy addrs.Provider")
	}
	return pt.Type
}

// IsZero returns true if the receiver is the zero value of addrs.Provider.
//
// The zero value is not a valid addrs.Provider and calling other methods on
// such a value is likely to either panic or otherwise misbehave.
func (pt Provider) IsZero() bool {
	return pt == Provider{}
}

// ParseProviderSourceString parses the source attribute and returns a provider.
// This is intended primarily to parse the FQN-like strings returned by
// terraform-config-inspect.
//
// The following are valid source string formats:
// 		name
// 		namespace/name
// 		hostname/namespace/name
func ParseProviderSourceString(str string) (Provider, tfdiags.Diagnostics) {
	var ret Provider
	var diags tfdiags.Diagnostics

	// split the source string into individual components
	parts := strings.Split(str, "/")
	if len(parts) == 0 || len(parts) > 3 {
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid provider source string",
			Detail:   `The "source" attribute must be in the format "[hostname/][namespace/]name"`,
		})
		return ret, diags
	}

	// check for an invalid empty string in any part
	for i := range parts {
		if parts[i] == "" {
			diags = diags.Append(&hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Invalid provider source string",
				Detail:   `The "source" attribute must be in the format "[hostname/][namespace/]name"`,
			})
			return ret, diags
		}
	}

	// check the 'name' portion, which is always the last part
	givenName := parts[len(parts)-1]
	name, err := ParseProviderPart(givenName)
	if err != nil {
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid provider type",
			Detail:   fmt.Sprintf(`Invalid provider type %q in source %q: %s"`, name, str, err),
		})
		return ret, diags
	}
	ret.Type = name
	ret.Hostname = DefaultRegistryHost

	if len(parts) == 1 {
		// FIXME: update this to NewDefaultProvider in the provider source release
		return NewLegacyProvider(parts[0]), diags
	}

	if len(parts) >= 2 {
		// the namespace is always the second-to-last part
		givenNamespace := parts[len(parts)-2]
		if givenNamespace == LegacyProviderNamespace {
			// For now we're tolerating legacy provider addresses until we've
			// finished updating the rest of the codebase to no longer use them,
			// or else we'd get errors round-tripping through legacy subsystems.
			ret.Namespace = LegacyProviderNamespace
		} else {
			namespace, err := ParseProviderPart(givenNamespace)
			if err != nil {
				diags = diags.Append(&hcl.Diagnostic{
					Severity: hcl.DiagError,
					Summary:  "Invalid provider namespace",
					Detail:   fmt.Sprintf(`Invalid provider namespace %q in source %q: %s"`, namespace, str, err),
				})
				return Provider{}, diags
			}
			ret.Namespace = namespace
		}
	}

	// Final Case: 3 parts
	if len(parts) == 3 {
		// the namespace is always the first part in a three-part source string
		hn, err := svchost.ForComparison(parts[0])
		if err != nil {
			diags = diags.Append(&hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Invalid provider source hostname",
				Detail:   fmt.Sprintf(`Invalid provider source hostname namespace %q in source %q: %s"`, hn, str, err),
			})
			return Provider{}, diags
		}
		ret.Hostname = hn
	}

	if ret.Namespace == LegacyProviderNamespace && ret.Hostname != DefaultRegistryHost {
		// Legacy provider addresses must always be on the default registry
		// host, because the default registry host decides what actual FQN
		// each one maps to.
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid provider namespace",
			Detail:   "The legacy provider namespace \"-\" can be used only with hostname " + DefaultRegistryHost.ForDisplay() + ".",
		})
		return Provider{}, diags
	}

	return ret, diags
}

// ParseProviderPart processes an addrs.Provider namespace or type string
// provided by an end-user, producing a normalized version if possible or
// an error if the string contains invalid characters.
//
// A provider part is processed in the same way as an individual label in a DNS
// domain name: it is transformed to lowercase per the usual DNS case mapping
// and normalization rules and may contain only letters, digits, and dashes.
// Additionally, dashes may not appear at the start or end of the string.
//
// These restrictions are intended to allow these names to appear in fussy
// contexts such as directory/file names on case-insensitive filesystems,
// repository names on GitHub, etc. We're using the DNS rules in particular,
// rather than some similar rules defined locally, because the hostname part
// of an addrs.Provider is already a hostname and it's ideal to use exactly
// the same case folding and normalization rules for all of the parts.
//
// In practice a provider type string conventionally does not contain dashes
// either. Such names are permitted, but providers with such type names will be
// hard to use because their resource type names will not be able to contain
// the provider type name and thus each resource will need an explicit provider
// address specified. (A real-world example of such a provider is the
// "google-beta" variant of the GCP provider, which has resource types that
// start with the "google_" prefix instead.)
//
// It's valid to pass the result of this function as the argument to a
// subsequent call, in which case the result will be identical.
func ParseProviderPart(given string) (string, error) {
	if len(given) == 0 {
		return "", fmt.Errorf("must have at least one character")
	}

	// We're going to process the given name using the same "IDNA" library we
	// use for the hostname portion, since it already implements the case
	// folding rules we want.
	//
	// The idna library doesn't expose individual label parsing directly, but
	// once we've verified it doesn't contain any dots we can just treat it
	// like a top-level domain for this library's purposes.
	if strings.ContainsRune(given, '.') {
		return "", fmt.Errorf("dots are not allowed")
	}

	// We don't allow names containing multiple consecutive dashes, just as
	// a matter of preference: they look weird, confusing, or incorrect.
	// This also, as a side-effect, prevents the use of the "punycode"
	// indicator prefix "xn--" that would cause the IDNA library to interpret
	// the given name as punycode, because that would be weird and unexpected.
	if strings.Contains(given, "--") {
		return "", fmt.Errorf("cannot use multiple consecutive dashes")
	}

	result, err := idna.Lookup.ToUnicode(given)
	if err != nil {
		return "", fmt.Errorf("must contain only letters, digits, and dashes, and may not use leading or trailing dashes")
	}

	return result, nil
}

// MustParseProviderPart is a wrapper around ParseProviderPart that panics if
// it returns an error.
func MustParseProviderPart(given string) string {
	result, err := ParseProviderPart(given)
	if err != nil {
		panic(err.Error())
	}
	return result
}
<commit_msg>addrs: Provider.LessThan<commit_after>package addrs

import (
	"fmt"
	"strings"

	"golang.org/x/net/idna"

	"github.com/hashicorp/hcl/v2"
	svchost "github.com/hashicorp/terraform-svchost"
	"github.com/hashicorp/terraform/tfdiags"
)

// Provider encapsulates a single provider type. In the future this will be
// extended to include additional fields including Namespace and SourceHost
type Provider struct {
	Type      string
	Namespace string
	Hostname  svchost.Hostname
}

// DefaultRegistryHost is the hostname used for provider addresses that do
// not have an explicit hostname.
const DefaultRegistryHost = svchost.Hostname("registry.terraform.io")

// LegacyProviderNamespace is the special string used in the Namespace field
// of type Provider to mark a legacy provider address. This special namespace
// value would normally be invalid, and can be used only when the hostname is
// DefaultRegistryHost because that host owns the mapping from legacy name to
// FQN.
const LegacyProviderNamespace = "-"

// String returns an FQN string, indended for use in output.
func (pt Provider) String() string {
	if pt.IsZero() {
		panic("called String on zero-value addrs.Provider")
	}
	return pt.Hostname.ForDisplay() + "/" + pt.Namespace + "/" + pt.Type
}

// NewProvider constructs a provider address from its parts, and normalizes
// the namespace and type parts to lowercase using unicode case folding rules
// so that resulting addrs.Provider values can be compared using standard
// Go equality rules (==).
//
// The hostname is given as a svchost.Hostname, which is required by the
// contract of that type to have already been normalized for equality testing.
//
// This function will panic if the given namespace or type name are not valid.
// When accepting namespace or type values from outside the program, use
// ParseProviderPart first to check that the given value is valid.
func NewProvider(hostname svchost.Hostname, namespace, typeName string) Provider {
	if namespace == LegacyProviderNamespace {
		// Legacy provider addresses must always be created via
		// NewLegacyProvider so that we can use static analysis to find
		// codepaths still working with those.
		panic("attempt to create legacy provider address using NewProvider; use NewLegacyProvider instead")
	}

	return Provider{
		Type:      MustParseProviderPart(typeName),
		Namespace: MustParseProviderPart(namespace),
		Hostname:  hostname,
	}
}

// NewDefaultProvider returns the default address of a HashiCorp-maintained,
// Registry-hosted provider.
func NewDefaultProvider(name string) Provider {
	return Provider{
		Type:      MustParseProviderPart(name),
		Namespace: "hashicorp",
		Hostname:  DefaultRegistryHost,
	}
}

// NewLegacyProvider returns a mock address for a provider.
// This will be removed when ProviderType is fully integrated.
func NewLegacyProvider(name string) Provider {
	return Provider{
		// We intentionally don't normalize and validate the legacy names,
		// because existing code expects legacy provider names to pass through
		// verbatim, even if not compliant with our new naming rules.
		Type:      name,
		Namespace: LegacyProviderNamespace,
		Hostname:  DefaultRegistryHost,
	}
}

// LegacyString returns the provider type, which is frequently used
// interchangeably with provider name. This function can and should be removed
// when provider type is fully integrated. As a safeguard for future
// refactoring, this function panics if the Provider is not a legacy provider.
func (pt Provider) LegacyString() string {
	if pt.IsZero() {
		panic("called LegacyString on zero-value addrs.Provider")
	}
	if pt.Namespace != LegacyProviderNamespace {
		panic(pt.String() + " is not a legacy addrs.Provider")
	}
	return pt.Type
}

// IsZero returns true if the receiver is the zero value of addrs.Provider.
//
// The zero value is not a valid addrs.Provider and calling other methods on
// such a value is likely to either panic or otherwise misbehave.
func (pt Provider) IsZero() bool {
	return pt == Provider{}
}

// LessThan returns true if the receiver should sort before the other given
// address in an ordered list of provider addresses.
//
// This ordering is an arbitrary one just to allow deterministic results from
// functions that would otherwise have no natural ordering. It's subject
// to change in future.
func (pt Provider) LessThan(other Provider) bool {
	switch {
	case pt.Hostname != other.Hostname:
		return pt.Hostname < other.Hostname
	case pt.Namespace != other.Namespace:
		return pt.Namespace < other.Namespace
	default:
		return pt.Type < other.Type
	}
}

// ParseProviderSourceString parses the source attribute and returns a provider.
// This is intended primarily to parse the FQN-like strings returned by
// terraform-config-inspect.
//
// The following are valid source string formats:
// 		name
// 		namespace/name
// 		hostname/namespace/name
func ParseProviderSourceString(str string) (Provider, tfdiags.Diagnostics) {
	var ret Provider
	var diags tfdiags.Diagnostics

	// split the source string into individual components
	parts := strings.Split(str, "/")
	if len(parts) == 0 || len(parts) > 3 {
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid provider source string",
			Detail:   `The "source" attribute must be in the format "[hostname/][namespace/]name"`,
		})
		return ret, diags
	}

	// check for an invalid empty string in any part
	for i := range parts {
		if parts[i] == "" {
			diags = diags.Append(&hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Invalid provider source string",
				Detail:   `The "source" attribute must be in the format "[hostname/][namespace/]name"`,
			})
			return ret, diags
		}
	}

	// check the 'name' portion, which is always the last part
	givenName := parts[len(parts)-1]
	name, err := ParseProviderPart(givenName)
	if err != nil {
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid provider type",
			Detail:   fmt.Sprintf(`Invalid provider type %q in source %q: %s"`, name, str, err),
		})
		return ret, diags
	}
	ret.Type = name
	ret.Hostname = DefaultRegistryHost

	if len(parts) == 1 {
		// FIXME: update this to NewDefaultProvider in the provider source release
		return NewLegacyProvider(parts[0]), diags
	}

	if len(parts) >= 2 {
		// the namespace is always the second-to-last part
		givenNamespace := parts[len(parts)-2]
		if givenNamespace == LegacyProviderNamespace {
			// For now we're tolerating legacy provider addresses until we've
			// finished updating the rest of the codebase to no longer use them,
			// or else we'd get errors round-tripping through legacy subsystems.
			ret.Namespace = LegacyProviderNamespace
		} else {
			namespace, err := ParseProviderPart(givenNamespace)
			if err != nil {
				diags = diags.Append(&hcl.Diagnostic{
					Severity: hcl.DiagError,
					Summary:  "Invalid provider namespace",
					Detail:   fmt.Sprintf(`Invalid provider namespace %q in source %q: %s"`, namespace, str, err),
				})
				return Provider{}, diags
			}
			ret.Namespace = namespace
		}
	}

	// Final Case: 3 parts
	if len(parts) == 3 {
		// the namespace is always the first part in a three-part source string
		hn, err := svchost.ForComparison(parts[0])
		if err != nil {
			diags = diags.Append(&hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Invalid provider source hostname",
				Detail:   fmt.Sprintf(`Invalid provider source hostname namespace %q in source %q: %s"`, hn, str, err),
			})
			return Provider{}, diags
		}
		ret.Hostname = hn
	}

	if ret.Namespace == LegacyProviderNamespace && ret.Hostname != DefaultRegistryHost {
		// Legacy provider addresses must always be on the default registry
		// host, because the default registry host decides what actual FQN
		// each one maps to.
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid provider namespace",
			Detail:   "The legacy provider namespace \"-\" can be used only with hostname " + DefaultRegistryHost.ForDisplay() + ".",
		})
		return Provider{}, diags
	}

	return ret, diags
}

// ParseProviderPart processes an addrs.Provider namespace or type string
// provided by an end-user, producing a normalized version if possible or
// an error if the string contains invalid characters.
//
// A provider part is processed in the same way as an individual label in a DNS
// domain name: it is transformed to lowercase per the usual DNS case mapping
// and normalization rules and may contain only letters, digits, and dashes.
// Additionally, dashes may not appear at the start or end of the string.
//
// These restrictions are intended to allow these names to appear in fussy
// contexts such as directory/file names on case-insensitive filesystems,
// repository names on GitHub, etc. We're using the DNS rules in particular,
// rather than some similar rules defined locally, because the hostname part
// of an addrs.Provider is already a hostname and it's ideal to use exactly
// the same case folding and normalization rules for all of the parts.
//
// In practice a provider type string conventionally does not contain dashes
// either. Such names are permitted, but providers with such type names will be
// hard to use because their resource type names will not be able to contain
// the provider type name and thus each resource will need an explicit provider
// address specified. (A real-world example of such a provider is the
// "google-beta" variant of the GCP provider, which has resource types that
// start with the "google_" prefix instead.)
//
// It's valid to pass the result of this function as the argument to a
// subsequent call, in which case the result will be identical.
func ParseProviderPart(given string) (string, error) {
	if len(given) == 0 {
		return "", fmt.Errorf("must have at least one character")
	}

	// We're going to process the given name using the same "IDNA" library we
	// use for the hostname portion, since it already implements the case
	// folding rules we want.
	//
	// The idna library doesn't expose individual label parsing directly, but
	// once we've verified it doesn't contain any dots we can just treat it
	// like a top-level domain for this library's purposes.
	if strings.ContainsRune(given, '.') {
		return "", fmt.Errorf("dots are not allowed")
	}

	// We don't allow names containing multiple consecutive dashes, just as
	// a matter of preference: they look weird, confusing, or incorrect.
	// This also, as a side-effect, prevents the use of the "punycode"
	// indicator prefix "xn--" that would cause the IDNA library to interpret
	// the given name as punycode, because that would be weird and unexpected.
	if strings.Contains(given, "--") {
		return "", fmt.Errorf("cannot use multiple consecutive dashes")
	}

	result, err := idna.Lookup.ToUnicode(given)
	if err != nil {
		return "", fmt.Errorf("must contain only letters, digits, and dashes, and may not use leading or trailing dashes")
	}

	return result, nil
}

// MustParseProviderPart is a wrapper around ParseProviderPart that panics if
// it returns an error.
func MustParseProviderPart(given string) string {
	result, err := ParseProviderPart(given)
	if err != nil {
		panic(err.Error())
	}
	return result
}
<|endoftext|>


<commit_before>package main

import (
	"testing"
	"time"
)

type mockSrc struct {
	count int
}

func (src mockSrc) read(out chan map[string]interface{}) error {
	for i := 0; i < 5; i++ {
		rec := map[string]interface{}{
			"LicenseId":       "license1",
			"BilledProductId": "product1",
			"Date":            time.Now().AddDate(0, 0, -i),
			"Value":           1,
		}
		out <- rec
	}
	return nil
}

func TestAggregateByLicense(t *testing.T) {
	target := &aggregator{&mockSrc{5}, 1, 1, time.RFC3339, "Date"}
	param := aggrParam{
		GroupBy:         []string{"LicenseId"},
		Interval:        "daily",
		AggregatedField: "Value",
	}

	var results []map[string]interface{}
	for r := range target.Aggregate(param) {
		results = append(results, r)
	}

	if len(results) != 5 {
		t.Error("Expected results to be 5")
	}

	for _, r := range results {
		if v, ok := r["license1"]; ok {
			if v != 1 {
				t.Errorf("aggregated value not as expected, expect %d get %d", 1, v)
			}
		} else {
			t.Errorf("aggregate group is not as expected, expect license1 get keys %v", r)
		}
	}
}

func TestAggregateByProduct(t *testing.T) {
	target := &aggregator{&mockSrc{5}, 1, 1, time.RFC3339, "Date"}
	param := aggrParam{
		GroupBy:         []string{"LicenseId", "BilledProductId"},
		Interval:        "monthly",
		AggregatedField: "Value",
	}

	var results []map[string]interface{}
	for r := range target.Aggregate(param) {
		results = append(results, r)
	}

	if len(results) != 1 {
		t.Error("Expected results to be 1")
	}

	if v, ok := results[0]["license1,product1"]; ok {
		if v != 5 {
			t.Errorf("aggregated value not as expected, expect %d get %d", 5, v)
		}
	} else {
		t.Errorf("aggregate group is not as expected, expect license1 get keys %v", results[0])
	}
}

var benchRes []map[string]interface{}

func BenchmarkAggregate(b *testing.B) {
	target := &aggregator{&mockSrc{1000000000}, 1, 1, time.RFC3339, "Date"}
	param := aggrParam{
		GroupBy:         []string{"LicenseId"},
		Interval:        "daily",
		AggregatedField: "Value",
	}
	var results []map[string]interface{}
	for n := 0; n < b.N; n++ {
		for r := range target.Aggregate(param) {
			results = append(results, r)
		}
	}
	benchRes = results
}
<commit_msg>add 2 benchmark test<commit_after>package main

import (
	"testing"
	"time"
)

type mockSrc struct {
	count int
}

func (src mockSrc) read(out chan map[string]interface{}) error {
	for i := 0; i < src.count; i++ {
		rec := map[string]interface{}{
			"LicenseId":       "license1",
			"BilledProductId": "product1",
			"Date":            time.Now().AddDate(0, 0, -i),
			"Value":           1,
		}
		out <- rec
	}
	return nil
}

func TestAggregateByLicense(t *testing.T) {
	target := &aggregator{&mockSrc{5}, 1, 1, time.RFC3339, "Date"}
	param := aggrParam{
		GroupBy:         []string{"LicenseId"},
		Interval:        "daily",
		AggregatedField: "Value",
	}

	var results []map[string]interface{}
	for r := range target.Aggregate(param) {
		results = append(results, r)
	}

	if len(results) != 5 {
		t.Error("Expected results to be 5")
	}

	for _, r := range results {
		if v, ok := r["license1"]; ok {
			if v != 1 {
				t.Errorf("aggregated value not as expected, expect %d get %d", 1, v)
			}
		} else {
			t.Errorf("aggregate group is not as expected, expect license1 get keys %v", r)
		}
	}
}

func TestAggregateByProduct(t *testing.T) {
	target := &aggregator{&mockSrc{5}, 1, 1, time.RFC3339, "Date"}
	param := aggrParam{
		GroupBy:         []string{"LicenseId", "BilledProductId"},
		Interval:        "monthly",
		AggregatedField: "Value",
	}

	var results []map[string]interface{}
	for r := range target.Aggregate(param) {
		results = append(results, r)
	}

	if len(results) != 1 {
		t.Error("Expected results to be 1")
	}

	if v, ok := results[0]["license1,product1"]; ok {
		if v != 5 {
			t.Errorf("aggregated value not as expected, expect %d get %d", 5, v)
		}
	} else {
		t.Errorf("aggregate group is not as expected, expect license1 get keys %v", results[0])
	}
}

var benchRes []map[string]interface{}

func benchmarkAggregate(i int, b *testing.B) {
	b.ReportAllocs()
	target := &aggregator{&mockSrc{i}, 1, 1, time.RFC3339, "Date"}
	param := aggrParam{
		GroupBy:         []string{"LicenseId"},
		Interval:        "daily",
		AggregatedField: "Value",
	}
	var results []map[string]interface{}
	for n := 0; n < b.N; n++ {
		for r := range target.Aggregate(param) {
			results = append(results, r)
		}
	}
	benchRes = results
}

func BenchmarkAggregate1000(b *testing.B)  { benchmarkAggregate(1000, b) }
func BenchmarkAggregate10000(b *testing.B) { benchmarkAggregate(10000, b) }
<|endoftext|>


<commit_before>package vartypes

import (
	"errors"

	"github.com/elves/elvish/eval/types"
)

var errRoCannotBeSet = errors.New("read-only variable; cannot be set")

// Variable represents an Elvish variable.
type Variable interface {
	Set(v types.Value)
	Get() types.Value
}

type ptrVariable struct {
	valuePtr *types.Value
}

func (pv ptrVariable) Set(val types.Value) {
	*pv.valuePtr = val
}

func (pv ptrVariable) Get() types.Value {
	return *pv.valuePtr
}

func NewPtrVariable(v types.Value) Variable {
	return ptrVariable{&v}
}

type validatedPtrVariable struct {
	valuePtr  *types.Value
	validator func(types.Value) error
}

type invalidValueError struct {
	inner error
}

func (err invalidValueError) Error() string {
	return "invalid value: " + err.inner.Error()
}

func NewValidatedPtrVariable(v types.Value, vld func(types.Value) error) Variable {
	return validatedPtrVariable{&v, vld}
}

func (iv validatedPtrVariable) Set(val types.Value) {
	if iv.validator != nil {
		if err := iv.validator(val); err != nil {
			throw(invalidValueError{err})
		}
	}
	*iv.valuePtr = val
}

func (iv validatedPtrVariable) Get() types.Value {
	return *iv.valuePtr
}

type roVariable struct {
	value types.Value
}

func NewRoVariable(v types.Value) Variable {
	return roVariable{v}
}

func (rv roVariable) Set(val types.Value) {
	throw(errRoCannotBeSet)
}

func (rv roVariable) Get() types.Value {
	return rv.value
}

type cbVariable struct {
	set func(types.Value)
	get func() types.Value
}

// NewCallbackVariable makes a variable from a set callback and a get callback.
func NewCallbackVariable(set func(types.Value), get func() types.Value) Variable {
	return &cbVariable{set, get}
}

func (cv *cbVariable) Set(val types.Value) {
	cv.set(val)
}

func (cv *cbVariable) Get() types.Value {
	return cv.get()
}

type roCbVariable func() types.Value

// NewRoCallbackVariable makes a read-only variable from a get callback.
func NewRoCallbackVariable(get func() types.Value) Variable {
	return roCbVariable(get)
}

func (cv roCbVariable) Set(types.Value) {
	throw(errRoCannotBeSet)
}

func (cv roCbVariable) Get() types.Value {
	return cv()
}
<commit_msg>eval/vartypes: Document.<commit_after>// Package vartypes contains basic types for manipulating Elvish variables.
package vartypes

import (
	"errors"

	"github.com/elves/elvish/eval/types"
)

var errRoCannotBeSet = errors.New("read-only variable; cannot be set")

// Variable represents an Elvish variable.
type Variable interface {
	Set(v types.Value)
	Get() types.Value
}

type ptrVariable struct {
	valuePtr *types.Value
}

func (pv ptrVariable) Set(val types.Value) {
	*pv.valuePtr = val
}

func (pv ptrVariable) Get() types.Value {
	return *pv.valuePtr
}

func NewPtrVariable(v types.Value) Variable {
	return ptrVariable{&v}
}

type validatedPtrVariable struct {
	valuePtr  *types.Value
	validator func(types.Value) error
}

type invalidValueError struct {
	inner error
}

func (err invalidValueError) Error() string {
	return "invalid value: " + err.inner.Error()
}

func NewValidatedPtrVariable(v types.Value, vld func(types.Value) error) Variable {
	return validatedPtrVariable{&v, vld}
}

func (iv validatedPtrVariable) Set(val types.Value) {
	if iv.validator != nil {
		if err := iv.validator(val); err != nil {
			throw(invalidValueError{err})
		}
	}
	*iv.valuePtr = val
}

func (iv validatedPtrVariable) Get() types.Value {
	return *iv.valuePtr
}

type roVariable struct {
	value types.Value
}

func NewRoVariable(v types.Value) Variable {
	return roVariable{v}
}

func (rv roVariable) Set(val types.Value) {
	throw(errRoCannotBeSet)
}

func (rv roVariable) Get() types.Value {
	return rv.value
}

type cbVariable struct {
	set func(types.Value)
	get func() types.Value
}

// NewCallbackVariable makes a variable from a set callback and a get callback.
func NewCallbackVariable(set func(types.Value), get func() types.Value) Variable {
	return &cbVariable{set, get}
}

func (cv *cbVariable) Set(val types.Value) {
	cv.set(val)
}

func (cv *cbVariable) Get() types.Value {
	return cv.get()
}

type roCbVariable func() types.Value

// NewRoCallbackVariable makes a read-only variable from a get callback.
func NewRoCallbackVariable(get func() types.Value) Variable {
	return roCbVariable(get)
}

func (cv roCbVariable) Set(types.Value) {
	throw(errRoCannotBeSet)
}

func (cv roCbVariable) Get() types.Value {
	return cv()
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"html"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

var latlongFile = flag.String("file", "latlong.json", "JSON file storing the results")
var waveguideUrl = flag.String("waveguide", "http://104.198.243.193", "URL for waveguide")

type Spot struct {
	Name       string
	ReportPath string // on magicseaweed
}

type SpotCoords struct {
	Spot
	Coords *Coordinates
}

func main() {
	spotsCoords := loadLatLongFile()
	spotsCoords = addSpotsFromMswSiteMap(spotsCoords)
	spotsCoords, err := orderByStarsOnWaveguide(spotsCoords)
	if err != nil {
		log.Printf("Failed to order by stars on Waveguide. %v", err)
	}
	saveLatLongFile(spotsCoords)

	// For each Spot without coordinates
	defer saveLatLongFile(spotsCoords)
	for _, sc := range spotsCoords {
		if sc.Coords != nil {
			fmt.Printf("Already got coordinates for %s: %v\n", sc.Name, sc.Coords)
			continue
		}

		// Show the user info to help get the coordinates.
		open(sc.Url())
		open(sc.GuideUrl())
		searchForCoordinates(sc.Spot)
		showMapForName(sc.Spot.Name)

		coordLoop:
		for {
			coordsStr, err := askf("\nEnter coordinates for %s, or empty to skip. For details see %s\n", sc.Name, sc.Url())
			if len(strings.TrimSpace(coordsStr)) == 0 {
				fmt.Println("Skipping.")
				continue
			}
			if err != nil {
				fmt.Println(err)
				continue
			}
			coords, err := parseCoords(coordsStr)
			if err != nil {
				fmt.Println(err)
				continue
			}
			yn, err := askf("Got %.4f, %.4f. OK (y/n/s)\n", coords.Lat, coords.Long)
			if err != nil {
				log.Fatal(err)
			}
			switch strings.ToLower(yn)[0] {
			case 's':
				fmt.Println("Skipping.\n")
				break coordLoop
			case 'n':
				fmt.Println("Trying again.\n")
				continue coordLoop
			default:
				sc.Coords = coords
				saveLatLongFile(spotsCoords)
				break coordLoop
			}
		}
	}
}

func orderByStarsOnWaveguide(spotsCoords []*SpotCoords) ([]*SpotCoords, error) {
	log.Printf("Ordering by stars on waveguide.")

	// Map from report path to SpotCoords
	rp2sc := make(map[string]*SpotCoords)
	for _, sc := range spotsCoords {
		rp2sc[sc.ReportPath] = sc
	}

	reportPaths, err := getReportPathsFromWaveguide()
	if err != nil {
		return spotsCoords, fmt.Errorf("Failed to get report paths from waveguide. %v", err)
	}

	// Make a new version of spot coords with the given order
	sc2 := make([]*SpotCoords, 0, len(spotsCoords))
	for _, rp := range reportPaths {
		sc2 = append(sc2, rp2sc[rp])
	}
	return sc2, nil
}

func getReportPathsFromWaveguide() ([]string, error) {
	resp, err := http.Get(*waveguideUrl)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	bodyBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("Failed to read body of %s. %v", *waveguideUrl, err)
	}
	body := string(bodyBytes)
	reportPaths := reportRx.FindAllString(body, -1)
	return reportPaths, nil
}

// askf asks the user to input something in the terminal.
func askf(promptFmt string, args ...interface{}) (string, error) {
	fmt.Printf(promptFmt, args...)
	r := bufio.NewReader(os.Stdin)
	return r.ReadString('\n')
}

func (s Spot) Url() string {
	return fmt.Sprintf("http://magicseaweed.com/%s", s.ReportPath)
}

func (s Spot) GuideUrl() string {
	guidePath := strings.Replace(s.ReportPath, "Report", "Guide", -1)
	return fmt.Sprintf("http://magicseaweed.com/%s", guidePath)
}

func searchForCoordinates(s Spot) {
	// TODO: Bring in more context from the msw surf report page.
	// For example, it could be the Santa Cruz in Portugal.
	url := fmt.Sprintf("https://www.google.com/search?q=%s coordinates", s.Name)
	open(url)
}

func loadLatLongFile() (spotsCoords []*SpotCoords) {
	contents, err := ioutil.ReadFile(*latlongFile)
	if err != nil {
		fmt.Printf("Didn't find %s. That's okay, starting fresh.\n", *latlongFile)
		return
	}
	err = json.Unmarshal(contents, &spotsCoords)
	if err != nil {
		log.Fatalf("Failed to parse %s. %v", *latlongFile, err)
	}
	return
}

func saveLatLongFile(spotsCoords []*SpotCoords) {
	contents, err := json.Marshal(&spotsCoords)
	if err != nil {
		log.Fatalf("Failed to encode data to json. %v", err)
	}
	err = ioutil.WriteFile(*latlongFile, contents, 0644)
	if err != nil {
		log.Fatalln(err)
	}
	log.Println("Wrote", *latlongFile)
}

func addSpotsFromMswSiteMap(spotsCoords []*SpotCoords) []*SpotCoords {
	// Grab the beach names and urls from the msw sitemap.
	resp, err := http.Get("http://magicseaweed.com/site-map.php")
	if err != nil {
		log.Fatalf("Failed to get site map. %v", err)
	}
	defer resp.Body.Close()
	contents, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatalf("Failed to read body of response for site map. %v", err)
	}
	srPaths := reportRx.FindAll(contents, -1)
	fmt.Printf("Got %d paths from site map.\n", len(srPaths))
	for _, path := range srPaths {
		sPath := string(path)
		name, err := surfReportPathToName(sPath)
		if err != nil {
			log.Println(err)
			continue
		}

		found := false
		for _, sc := range spotsCoords {
			if sc.ReportPath == sPath {
				found = true
			}
		}

		if !found {
			sc := &SpotCoords{
				Spot: Spot{
					Name:       name,
					ReportPath: string(path),
				},
				Coords: nil,
			}
			spotsCoords = append(spotsCoords, sc)
		}
	}
	return spotsCoords
}

var reportRx = regexp.MustCompile(`/[^"/]+-Surf-Report/\d+/`)
var srpTailRx = regexp.MustCompile(`-Surf-Report/\d+/`)

func surfReportPathToName(srp string) (string, error) {
	s := srpTailRx.ReplaceAllString(srp, "")
	s, err := url.PathUnescape(s)
	if err != nil {
		return "", fmt.Errorf("Failed to unescape path in surfReportPathToName(%q). %v", srp, err)
	}
	s = html.UnescapeString(s)
	s = s[1:] // Remove leading /
	s = strings.Replace(s, "-", " ", -1)
	return s, nil
}

func showMap(lat float32, long float32, zoom int) {
	url := fmt.Sprintf("https://www.google.com/maps/@%f,%f,%dz", lat, long, zoom)
	open(url)
}

func showMapForName(name string) {
	url := fmt.Sprintf("https://www.google.com/maps/place/%s", name)
	open(url)
}

// open opens a url in a browser. This works on Mac, but probably not elsewhere.
func open(url string) {
	cmd := exec.Command("open", url)
	go func() {
		err := cmd.Run()
		if err != nil {
			log.Printf("Failed to open %s. %v", url, err)
		}
	}()
}
<commit_msg>Pretty print json.<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"html"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

var latlongFile = flag.String("file", "latlong.json", "JSON file storing the results")
var waveguideUrl = flag.String("waveguide", "http://104.198.243.193", "URL for waveguide")

type Spot struct {
	Name       string
	ReportPath string // on magicseaweed
}

type SpotCoords struct {
	Spot
	Coords *Coordinates
}

func main() {
	spotsCoords := loadLatLongFile()
	spotsCoords = addSpotsFromMswSiteMap(spotsCoords)
	spotsCoords, err := orderByStarsOnWaveguide(spotsCoords)
	if err != nil {
		log.Printf("Failed to order by stars on Waveguide. %v", err)
	}
	saveLatLongFile(spotsCoords)

	// For each Spot without coordinates
	defer saveLatLongFile(spotsCoords)
	for _, sc := range spotsCoords {
		if sc.Coords != nil {
			fmt.Printf("Already got coordinates for %s: %v\n", sc.Name, sc.Coords)
			continue
		}

		// Show the user info to help get the coordinates.
		open(sc.Url())
		open(sc.GuideUrl())
		searchForCoordinates(sc.Spot)
		showMapForName(sc.Spot.Name)

		coordLoop:
		for {
			coordsStr, err := askf("\nEnter coordinates for %s, or empty to skip. For details see %s\n", sc.Name, sc.Url())
			if len(strings.TrimSpace(coordsStr)) == 0 {
				fmt.Println("Skipping.")
				continue
			}
			if err != nil {
				fmt.Println(err)
				continue
			}
			coords, err := parseCoords(coordsStr)
			if err != nil {
				fmt.Println(err)
				continue
			}
			yn, err := askf("Got %.4f, %.4f. OK (y/n/s)\n", coords.Lat, coords.Long)
			if err != nil {
				log.Fatal(err)
			}
			switch strings.ToLower(yn)[0] {
			case 's':
				fmt.Println("Skipping.\n")
				break coordLoop
			case 'n':
				fmt.Println("Trying again.\n")
				continue coordLoop
			default:
				sc.Coords = coords
				saveLatLongFile(spotsCoords)
				break coordLoop
			}
		}
	}
}

func orderByStarsOnWaveguide(spotsCoords []*SpotCoords) ([]*SpotCoords, error) {
	log.Printf("Ordering by stars on waveguide.")

	// Map from report path to SpotCoords
	rp2sc := make(map[string]*SpotCoords)
	for _, sc := range spotsCoords {
		rp2sc[sc.ReportPath] = sc
	}

	reportPaths, err := getReportPathsFromWaveguide()
	if err != nil {
		return spotsCoords, fmt.Errorf("Failed to get report paths from waveguide. %v", err)
	}

	// Make a new version of spot coords with the given order
	sc2 := make([]*SpotCoords, 0, len(spotsCoords))
	for _, rp := range reportPaths {
		sc2 = append(sc2, rp2sc[rp])
	}
	return sc2, nil
}

func getReportPathsFromWaveguide() ([]string, error) {
	resp, err := http.Get(*waveguideUrl)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	bodyBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("Failed to read body of %s. %v", *waveguideUrl, err)
	}
	body := string(bodyBytes)
	reportPaths := reportRx.FindAllString(body, -1)
	return reportPaths, nil
}

// askf asks the user to input something in the terminal.
func askf(promptFmt string, args ...interface{}) (string, error) {
	fmt.Printf(promptFmt, args...)
	r := bufio.NewReader(os.Stdin)
	return r.ReadString('\n')
}

func (s Spot) Url() string {
	return fmt.Sprintf("http://magicseaweed.com/%s", s.ReportPath)
}

func (s Spot) GuideUrl() string {
	guidePath := strings.Replace(s.ReportPath, "Report", "Guide", -1)
	return fmt.Sprintf("http://magicseaweed.com/%s", guidePath)
}

func searchForCoordinates(s Spot) {
	// TODO: Bring in more context from the msw surf report page.
	// For example, it could be the Santa Cruz in Portugal.
	url := fmt.Sprintf("https://www.google.com/search?q=%s coordinates", s.Name)
	open(url)
}

func loadLatLongFile() (spotsCoords []*SpotCoords) {
	contents, err := ioutil.ReadFile(*latlongFile)
	if err != nil {
		fmt.Printf("Didn't find %s. That's okay, starting fresh.\n", *latlongFile)
		return
	}
	err = json.Unmarshal(contents, &spotsCoords)
	if err != nil {
		log.Fatalf("Failed to parse %s. %v", *latlongFile, err)
	}
	return
}

func saveLatLongFile(spotsCoords []*SpotCoords) {
	contents, err := json.MarshalIndent(&spotsCoords, "", "    ")
	if err != nil {
		log.Fatalf("Failed to encode data to json. %v", err)
	}
	err = ioutil.WriteFile(*latlongFile, contents, 0644)
	if err != nil {
		log.Fatalln(err)
	}
	log.Println("Wrote", *latlongFile)
}

func addSpotsFromMswSiteMap(spotsCoords []*SpotCoords) []*SpotCoords {
	// Grab the beach names and urls from the msw sitemap.
	resp, err := http.Get("http://magicseaweed.com/site-map.php")
	if err != nil {
		log.Fatalf("Failed to get site map. %v", err)
	}
	defer resp.Body.Close()
	contents, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatalf("Failed to read body of response for site map. %v", err)
	}
	srPaths := reportRx.FindAll(contents, -1)
	fmt.Printf("Got %d paths from site map.\n", len(srPaths))
	for _, path := range srPaths {
		sPath := string(path)
		name, err := surfReportPathToName(sPath)
		if err != nil {
			log.Println(err)
			continue
		}

		found := false
		for _, sc := range spotsCoords {
			if sc.ReportPath == sPath {
				found = true
			}
		}

		if !found {
			sc := &SpotCoords{
				Spot: Spot{
					Name:       name,
					ReportPath: string(path),
				},
				Coords: nil,
			}
			spotsCoords = append(spotsCoords, sc)
		}
	}
	return spotsCoords
}

var reportRx = regexp.MustCompile(`/[^"/]+-Surf-Report/\d+/`)
var srpTailRx = regexp.MustCompile(`-Surf-Report/\d+/`)

func surfReportPathToName(srp string) (string, error) {
	s := srpTailRx.ReplaceAllString(srp, "")
	s, err := url.PathUnescape(s)
	if err != nil {
		return "", fmt.Errorf("Failed to unescape path in surfReportPathToName(%q). %v", srp, err)
	}
	s = html.UnescapeString(s)
	s = s[1:] // Remove leading /
	s = strings.Replace(s, "-", " ", -1)
	return s, nil
}

func showMap(lat float32, long float32, zoom int) {
	url := fmt.Sprintf("https://www.google.com/maps/@%f,%f,%dz", lat, long, zoom)
	open(url)
}

func showMapForName(name string) {
	url := fmt.Sprintf("https://www.google.com/maps/place/%s", name)
	open(url)
}

// open opens a url in a browser. This works on Mac, but probably not elsewhere.
func open(url string) {
	cmd := exec.Command("open", url)
	go func() {
		err := cmd.Run()
		if err != nil {
			log.Printf("Failed to open %s. %v", url, err)
		}
	}()
}
<|endoftext|>


<commit_before>package sidekick

import (
	"flag"
	"log"
	"os"
	"reflect"
	"testing"
)

func init() {
	log.SetFlags(log.Lshortfile)
}

func TestSkipCase(t *testing.T) {
	oldArgs := os.Args
	for _, c := range []struct {
		input        []string
		intResult    map[int]bool
		stringResult map[string]bool
	}{
		{
			input:        []string{"-skip", "1", "-skip", "2"},
			intResult:    map[int]bool{0: true, 3: true},
			stringResult: map[string]bool{"0": true, "3": true},
		},
		{
			input:        []string{"-case", "1", "-case", "2"},
			intResult:    map[int]bool{1: true, 2: true},
			stringResult: map[string]bool{"1": true, "2": true},
		},
	} {
		os.Args = append(oldArgs, c.input...)
		skips = []string{}
		cases = []string{}
		flag.Parse()

		intResult := map[int]bool{}
		for i := range []int{0, 1, 2, 3} {
			if SkipCase(i) {
				continue
			}
			intResult[i] = true
		}
		if got, want := intResult, c.intResult; !reflect.DeepEqual(got, want) {
			t.Errorf("intResult = %#v; want %#v", got, want)
		}

		stringResult := map[string]bool{}
		for _, i := range []string{"0", "1", "2", "3"} {
			if SkipCase(i) {
				continue
			}
			stringResult[i] = true
		}
		if got, want := stringResult, c.stringResult; !reflect.DeepEqual(got, want) {
			t.Errorf("stringResult = %#v; want %#v", got, want)
		}
	}
}

func ExampleSkipCase() {
	// skip case or cases: go test -skip 1 -skip 2
	for i, c := range cases {
		if SkipCase(i) {
			continue
		}
		// you can filter cases not needed when debugging tests
		_ = c
	}
}
<commit_msg>update example<commit_after>package sidekick

import (
	"flag"
	"log"
	"os"
	"reflect"
	"testing"
)

func init() {
	log.SetFlags(log.Lshortfile)
}

func TestSkipCase(t *testing.T) {
	oldArgs := os.Args
	for _, c := range []struct {
		input        []string
		intResult    map[int]bool
		stringResult map[string]bool
	}{
		{
			input:        []string{"-skip", "1", "-skip", "2"},
			intResult:    map[int]bool{0: true, 3: true},
			stringResult: map[string]bool{"0": true, "3": true},
		},
		{
			input:        []string{"-case", "1", "-case", "2"},
			intResult:    map[int]bool{1: true, 2: true},
			stringResult: map[string]bool{"1": true, "2": true},
		},
	} {
		os.Args = append(oldArgs, c.input...)
		skips = []string{}
		cases = []string{}
		flag.Parse()

		intResult := map[int]bool{}
		for i := range []int{0, 1, 2, 3} {
			if SkipCase(i) {
				continue
			}
			intResult[i] = true
		}
		if got, want := intResult, c.intResult; !reflect.DeepEqual(got, want) {
			t.Errorf("intResult = %#v; want %#v", got, want)
		}

		stringResult := map[string]bool{}
		for _, i := range []string{"0", "1", "2", "3"} {
			if SkipCase(i) {
				continue
			}
			stringResult[i] = true
		}
		if got, want := stringResult, c.stringResult; !reflect.DeepEqual(got, want) {
			t.Errorf("stringResult = %#v; want %#v", got, want)
		}
	}
}

func ExampleSkipCase() {
	// skip case or cases: go test -skip 1 -skip 2 --> skip case 1 and 2
	// Or: go test -case 1 -case 2 --> run case 1 and case 2
	for i, c := range cases {
		if SkipCase(i) {
			continue
		}
		// you can filter cases not needed when debugging tests
		_ = c
	}
}
<|endoftext|>


<commit_before>package destination

import (
	"database/sql"
	"errors"
)

type Destination struct {
	ID  int
	URI string
}

func NewDestination(uri string) *Destination {
	return &Destination{-1, uri}
}

func ReadOrCreate(db *sql.DB, uri string) (*Destination, error) {
	dest, err := ReadURI(db, uri)
	if err != nil {
		dest = NewDestination(uri)
		err = Insert(db, dest)
		dest, err = ReadURI(db, uri)
	}
	return dest, err
}

func CreateTable(db *sql.DB) error {
	_, err:= db.Exec("CREATE TABLE destinations ( id INTEGER PRIMARY KEY AUTOINCREMENT, uri TEXT )")
	return err
}

func Insert(db *sql.DB, d *Destination) error {
	_, err := db.Exec("INSERT INTO destinations VALUES( NULL , ? )", d.URI)
	return err
}

func ReadURI(db *sql.DB, uri string) (*Destination, error) {
	var d *Destination
	var err error
	row := db.QueryRow("SELECT * FROM destinations WHERE uri=?", uri)
	if row == nil {
		err = errors.New("Destination not found")
	} else {
		var id int
		var uri string
		err = row.Scan(&id, &uri)
		if err == nil {
			d = &Destination{id, uri}
		}
	}
	return d, err
}

func Read(db *sql.DB, id int) (*Destination, error) {
	var d *Destination
	var err error
	row := db.QueryRow("SELECT * FROM destinations WHERE id=?", id)
	if row == nil {
		err = errors.New("Destination not found")
	} else {
		var id int
		var uri string
		err = row.Scan(&id, &uri)
		if err == nil {
			d = &Destination{id, uri}
		}
	}
	return d, err
}

func ReadAll(d *sql.DB) ([]*Destination, error) {
	ds := make([]*Destination, 0)
	tx, err := d.Begin()
	if err == nil {
		rows, err := tx.Query("SELECT * FROM destinations")
		if err != nil {
			tx.Rollback()
		} else {
			var id int
			var uri string
			rows.Scan(&id, &uri)
			if id >= 0 && len(uri) > 0 {
				ds = append(ds, &Destination{id, uri})
			}
			for rows.Next() {
				rows.Scan(&id, &uri)
				if id >= 0 && len(uri) > 0 {
					ds = append(ds, &Destination{id, uri})
				}
			}
			rows.Close()
			tx.Commit()
		}
	}
	return ds, err
}

func Update(db *sql.DB, d *Destination) error {
	_, err := db.Query("UPDATE destinations SET uri=? WHERE id=?", d.URI, d.ID)
	return err
}
<commit_msg>Simplified source<commit_after>package destination

import (
	"database/sql"
	"errors"
)

type Destination struct {
	ID  int
	URI string
}

func NewDestination(uri string) *Destination {
	return &Destination{-1, uri}
}

func ReadOrCreate(db *sql.DB, uri string) (*Destination, error) {
	dest, err := ReadURI(db, uri)
	if err != nil {
		dest = NewDestination(uri)
		err = Insert(db, dest)
		dest, err = ReadURI(db, uri)
	}
	return dest, err
}

func CreateTable(db *sql.DB) error {
	_, err:= db.Exec("CREATE TABLE destinations ( id INTEGER PRIMARY KEY AUTOINCREMENT, uri TEXT )")
	return err
}

func Insert(db *sql.DB, d *Destination) error {
	_, err := db.Exec("INSERT INTO destinations VALUES( NULL , ? )", d.URI)
	return err
}

func ReadURI(db *sql.DB, uri string) (*Destination, error) {
	d := &Destination{}
	var err error
	row := db.QueryRow("SELECT * FROM destinations WHERE uri=?", uri)
	if row == nil {
		err = errors.New("Destination not found")
	} else {
		err = row.Scan(&d.ID, &d.URI)
	}
	return d, err
}

func Read(db *sql.DB, id int) (*Destination, error) {
	d := &Destination{}
	var err error
	row := db.QueryRow("SELECT * FROM destinations WHERE id=?", id)
	if row == nil {
		err = errors.New("Destination not found")
	} else {
		err = row.Scan(&d.ID, &d.URI)
	}
	return d, err
}

func ReadAll(d *sql.DB) ([]*Destination, error) {
	ds := make([]*Destination, 0)
	rows, err := d.Query("SELECT * FROM destinations")
	if err != nil {
		for rows.Next() {
			d := &Destination{}
			rows.Scan(&d.ID, &d.URI)
			if d.ID >= 0 && len(d.URI) > 0 {
				ds = append(ds, d)
			}
		}
		rows.Close()
	}
	return ds, err
}

func Update(db *sql.DB, d *Destination) error {
	_, err := db.Query("UPDATE destinations SET uri=? WHERE id=?", d.URI, d.ID)
	return err
}
<|endoftext|>


<commit_before>package main

import (
	log "code.google.com/p/log4go"
)

const (
	// signal command
	SignalNum   = 1
	ProtoFinish = 0
	ProtoReady  = 1
)

type Ring struct {
	// read
	rn int
	rp int
	// write
	wn int
	wp int
	// info
	num  int
	data []Proto
}

func NewRing(num int) *Ring {
	r := new(Ring)
	r.init(num)
	return r
}

func (r *Ring) init(num int) {
	r.data = make([]Proto, num)
	r.num = num
}

func (r *Ring) Init(num int) {
	r.init(num)
}

func (r *Ring) Get() (proto *Proto, err error) {
	if r.wn == r.rn {
		return nil, ErrRingEmpty
	}
	proto = &r.data[r.rp]
	return
}

func (r *Ring) GetAdv() {
	if r.rp++; r.rp >= r.num {
		r.rp = 0
	}
	r.rn++
	if Debug {
		log.Debug("ring rn: %d, rp: %d", r.rn, r.rp)
	}
}

func (r *Ring) Set() (proto *Proto, err error) {
	if r.wn-r.rn >= r.num {
		return nil, ErrRingFull
	}
	proto = &r.data[r.wp]
	return
}

func (r *Ring) SetAdv() {
	if r.wp++; r.wp >= r.num {
		r.wp = 0
	}
	r.wn++
	if Debug {
		log.Debug("ring wn: %d, wp: %d", r.wn, r.wp)
	}
}

func (r *Ring) Reset() {
	r.rn = 0
	r.rp = 0
	r.wn = 0
	r.wp = 0
}
<commit_msg>improve performance ring<commit_after>package main

import (
	log "code.google.com/p/log4go"
)

const (
	// signal command
	SignalNum   = 1
	ProtoFinish = 0
	ProtoReady  = 1
)

type Ring struct {
	// read
	// rn int
	rp int
	// add data here split reade & write in one cacheline
	data []Proto
	num  int
	// write
	// wn int
	wp int
}

func NewRing(num int) *Ring {
	r := new(Ring)
	r.init(num)
	return r
}

func (r *Ring) init(num int) {
	r.data = make([]Proto, num)
	r.num = num
}

func (r *Ring) Init(num int) {
	r.init(num)
}

func (r *Ring) Get() (proto *Proto, err error) {
	if r.rp == r.wp {
		return nil, ErrRingEmpty
	}
	proto = &r.data[r.rp%r.num]
	return
}

func (r *Ring) GetAdv() {
	r.rp++
	if Debug {
		log.Debug("ring rp: %d, idx: %d", r.rp, r.rp%r.num)
	}
}

func (r *Ring) Set() (proto *Proto, err error) {
	if r.wp-r.rp >= r.num {
		return nil, ErrRingFull
	}
	proto = &r.data[r.wp%r.num]
	return
}

func (r *Ring) SetAdv() {
	r.wp++
	if Debug {
		log.Debug("ring wp: %d, idx: %d", r.wp, r.wp%r.num)
	}
}

func (r *Ring) Reset() {
	r.rp = 0
	r.wp = 0
}
<|endoftext|>


<commit_before>package main

import (
	"goim/libs/proto"
	itime "goim/libs/time"
	"sync"
)

type RoomOptions struct {
	ChannelSize int
}

type Room struct {
	id    int32
	rLock sync.RWMutex
	// map room id with channels
	// TODO use double-linked list
	chs map[*Channel]struct{}
}

// NewRoom new a room struct, store channel room info.
func NewRoom(id int32, t *itime.Timer, options RoomOptions) (r *Room) {
	r = new(Room)
	r.id = id
	r.chs = make(map[*Channel]struct{}, options.ChannelSize)
	return
}

// Put put channel into the room.
func (r *Room) Put(ch *Channel) (err error) {
	r.rLock.Lock()
	r.chs[ch] = struct{}{}
	r.rLock.Unlock()
	return
}

// Del delete channel from the room.
func (r *Room) Del(ch *Channel) (o int) {
	r.rLock.Lock()
	delete(r.chs, ch)
	o = len(r.chs)
	r.rLock.Unlock()
	return
}

// Push push msg to the room, if chan full discard it.
func (r *Room) Push(p *proto.Proto) {
	var ch *Channel
	r.rLock.RLock()
	for ch, _ = range r.chs {
		ch.Push(p)
	}
	r.rLock.RUnlock()
	return
}

// Online get online number.
func (r *Room) Online() (o int) {
	r.rLock.RLock()
	o = len(r.chs)
	r.rLock.RUnlock()
	return
}

// Close close the room.
func (r *Room) Close() {
	var ch *Channel
	r.rLock.RLock()
	for ch, _ = range r.chs {
		ch.Close()
	}
	r.rLock.RUnlock()
}
<commit_msg>useless return error<commit_after>package main

import (
	"goim/libs/proto"
	itime "goim/libs/time"
	"sync"
)

type RoomOptions struct {
	ChannelSize int
}

type Room struct {
	id    int32
	rLock sync.RWMutex
	// map room id with channels
	// TODO use double-linked list
	chs map[*Channel]struct{}
}

// NewRoom new a room struct, store channel room info.
func NewRoom(id int32, t *itime.Timer, options RoomOptions) (r *Room) {
	r = new(Room)
	r.id = id
	r.chs = make(map[*Channel]struct{}, options.ChannelSize)
	return
}

// Put put channel into the room.
func (r *Room) Put(ch *Channel) {
	r.rLock.Lock()
	r.chs[ch] = struct{}{}
	r.rLock.Unlock()
	return
}

// Del delete channel from the room.
func (r *Room) Del(ch *Channel) (o int) {
	r.rLock.Lock()
	delete(r.chs, ch)
	o = len(r.chs)
	r.rLock.Unlock()
	return
}

// Push push msg to the room, if chan full discard it.
func (r *Room) Push(p *proto.Proto) {
	var ch *Channel
	r.rLock.RLock()
	for ch, _ = range r.chs {
		ch.Push(p)
	}
	r.rLock.RUnlock()
	return
}

// Online get online number.
func (r *Room) Online() (o int) {
	r.rLock.RLock()
	o = len(r.chs)
	r.rLock.RUnlock()
	return
}

// Close close the room.
func (r *Room) Close() {
	var ch *Channel
	r.rLock.RLock()
	for ch, _ = range r.chs {
		ch.Close()
	}
	r.rLock.RUnlock()
}
<|endoftext|>


<commit_before>package elements

import (
	"errors"
	"fmt"
)

type Registry struct {
	identifier map[string]*Component
}

func NewRegistry() *Registry {
	i := make(map[string]*Component)
	registry := &Registry{
		i,
	}

	return registry
}

func (r *Registry) Add(c *Component) error {
	if c == nil {
		return errors.New("Nil given")
	}

	if c.Alias != "" {
		_, ok := r.identifier[c.Alias]
		if ok {
			return errors.New("Alias " + c.Alias + " already exists")
		}

		r.identifier[c.Alias] = c
		return nil
	}

	if c.Identifier != "" {
		_, ok := r.identifier[c.Identifier]
		if ok {
			return errors.New("Identifier " + c.Identifier + " already exists")
		}

		r.identifier[c.Identifier] = c
		return nil
	}

	return errors.New("Neither alias nor identifier set")
}

func Get(r *Registry, i string) *Component {
	c, ok := r.identifier[i]
	fmt.Println(ok)
	if ok {
		return c
	}

	return nil
}
<commit_msg>Removed output<commit_after>package elements

import "errors"

type Registry struct {
	identifier map[string]*Component
}

func NewRegistry() *Registry {
	i := make(map[string]*Component)
	registry := &Registry{
		i,
	}

	return registry
}

func (r *Registry) Add(c *Component) error {
	if c == nil {
		return errors.New("Nil given")
	}

	if c.Alias != "" {
		_, ok := r.identifier[c.Alias]
		if ok {
			return errors.New("Alias " + c.Alias + " already exists")
		}

		r.identifier[c.Alias] = c
		return nil
	}

	if c.Identifier != "" {
		_, ok := r.identifier[c.Identifier]
		if ok {
			return errors.New("Identifier " + c.Identifier + " already exists")
		}

		r.identifier[c.Identifier] = c
		return nil
	}

	return errors.New("Neither alias nor identifier set")
}

func Get(r *Registry, i string) *Component {
	c, ok := r.identifier[i]
	if ok {
		return c
	}

	return nil
}
<|endoftext|>


<commit_before>package collector

import (
	"strings"
	"time"

	"github.com/gempir/go-twitch-irc/v2"
	"github.com/gempir/spamchamp/pkg/config"
	"github.com/gempir/spamchamp/services/bot/helix"
	"github.com/gempir/spamchamp/services/bot/store"
	log "github.com/sirupsen/logrus"
)

// Bot basic logging bot
type Bot struct {
	startTime    time.Time
	cfg          *config.Config
	helixClient  *helix.Client
	twitchClient *twitch.Client
	store        *store.Store
	channels     map[string]helix.UserData
	joined       map[string]bool
}

// NewBot create new bot instance
func NewBot(cfg *config.Config, helixClient *helix.Client, store *store.Store) *Bot {
	channels, err := helixClient.GetUsersByUserIds(cfg.Channels)
	if err != nil {
		log.Fatalf("[collector] failed to load configured channels %s", err.Error())
	}

	return &Bot{
		cfg:         cfg,
		helixClient: helixClient,
		store:       store,
		channels:    channels,
		joined:      map[string]bool{},
	}
}

// Connect startup the logger and bot
func (b *Bot) Connect() {
	b.startTime = time.Now()
	b.twitchClient = twitch.NewClient(b.cfg.Username, "oauth:"+b.cfg.OAuth)
	b.twitchClient.IrcAddress = "127.0.0.1:3333"
	b.twitchClient.TLS = false
	b.twitchClient.SetupCmd = "LOGIN spamchampbot"

	if strings.HasPrefix(b.cfg.Username, "justinfan") {
		log.Info("[collector] joining as anonymous")
	} else {
		log.Info("[collector] joining as user " + b.cfg.Username)
	}
	b.initialJoins()

	b.twitchClient.OnPrivateMessage(func(message twitch.PrivateMessage) {
		b.handlePrivateMessage(message)
	})

	go func() {
		ticker := time.NewTicker(15 * time.Minute)

		for range ticker.C {
			b.LoadTopChannelsAndJoin()
		}
	}()

	log.Fatal(b.twitchClient.Connect())
}

func (b *Bot) initialJoins() {
	for _, channel := range b.channels {
		log.Info("[collector] joining " + channel.Login)
		b.twitchClient.Join(channel.Login)
	}
}

func (b *Bot) joinStoreChannels() {
	go func() {
		allChannelIds := b.store.GetAllChannels()
		channels, err := b.helixClient.GetUsersByUserIds(allChannelIds)
		if err != nil {
			log.Error(err)
			return
		}

		go func() {
			for _, channelID := range allChannelIds {
				if _, ok := channels[channelID]; !ok {
					b.store.RemoveChannel(channelID)
				}
			}
		}()

		for _, userData := range channels {
			channelName := userData.Login

			if _, ok := b.joined[channelName]; !ok {
				b.joined[channelName] = true
				b.twitchClient.Join(channelName)
				log.Debugf("[collector] joined %s", channelName)
			}
		}
	}()
}

func (b *Bot) SaveAndJoinChannel(channelName string) {
	users, err := b.helixClient.GetUsersByUsernames([]string{channelName})
	if err != nil {
		log.Error(err)
		return
	}

	userids := []string{}
	for _, channel := range users {
		userids = append(userids, channel.ID)
	}

	b.store.AddChannels(userids...)
	b.joinStoreChannels()
}

func (b *Bot) LoadTopChannelsAndJoin() {
	log.Info("[collector] fetching top channels and joining")
	b.store.AddChannels(b.helixClient.GetTopChannels()...)

	b.joinStoreChannels()
}
<commit_msg>remove remover<commit_after>package collector

import (
	"strings"
	"time"

	"github.com/gempir/go-twitch-irc/v2"
	"github.com/gempir/spamchamp/pkg/config"
	"github.com/gempir/spamchamp/services/bot/helix"
	"github.com/gempir/spamchamp/services/bot/store"
	log "github.com/sirupsen/logrus"
)

// Bot basic logging bot
type Bot struct {
	startTime    time.Time
	cfg          *config.Config
	helixClient  *helix.Client
	twitchClient *twitch.Client
	store        *store.Store
	channels     map[string]helix.UserData
	joined       map[string]bool
}

// NewBot create new bot instance
func NewBot(cfg *config.Config, helixClient *helix.Client, store *store.Store) *Bot {
	channels, err := helixClient.GetUsersByUserIds(cfg.Channels)
	if err != nil {
		log.Fatalf("[collector] failed to load configured channels %s", err.Error())
	}

	return &Bot{
		cfg:         cfg,
		helixClient: helixClient,
		store:       store,
		channels:    channels,
		joined:      map[string]bool{},
	}
}

// Connect startup the logger and bot
func (b *Bot) Connect() {
	b.startTime = time.Now()
	b.twitchClient = twitch.NewClient(b.cfg.Username, "oauth:"+b.cfg.OAuth)
	b.twitchClient.IrcAddress = "127.0.0.1:3333"
	b.twitchClient.TLS = false
	b.twitchClient.SetupCmd = "LOGIN spamchampbot"

	if strings.HasPrefix(b.cfg.Username, "justinfan") {
		log.Info("[collector] joining as anonymous")
	} else {
		log.Info("[collector] joining as user " + b.cfg.Username)
	}
	b.initialJoins()

	b.twitchClient.OnPrivateMessage(func(message twitch.PrivateMessage) {
		b.handlePrivateMessage(message)
	})

	go func() {
		ticker := time.NewTicker(15 * time.Minute)

		for range ticker.C {
			b.LoadTopChannelsAndJoin()
		}
	}()

	log.Fatal(b.twitchClient.Connect())
}

func (b *Bot) initialJoins() {
	for _, channel := range b.channels {
		log.Info("[collector] joining " + channel.Login)
		b.twitchClient.Join(channel.Login)
	}
}

func (b *Bot) joinStoreChannels() {
	go func() {
		allChannelIds := b.store.GetAllChannels()
		channels, err := b.helixClient.GetUsersByUserIds(allChannelIds)
		if err != nil {
			log.Error(err)
			return
		}

		for _, userData := range channels {
			channelName := userData.Login

			if _, ok := b.joined[channelName]; !ok {
				b.joined[channelName] = true
				b.twitchClient.Join(channelName)
				log.Debugf("[collector] joined %s", channelName)
			}
		}
	}()
}

func (b *Bot) SaveAndJoinChannel(channelName string) {
	users, err := b.helixClient.GetUsersByUsernames([]string{channelName})
	if err != nil {
		log.Error(err)
		return
	}

	userids := []string{}
	for _, channel := range users {
		userids = append(userids, channel.ID)
	}

	b.store.AddChannels(userids...)
	b.joinStoreChannels()
}

func (b *Bot) LoadTopChannelsAndJoin() {
	log.Info("[collector] fetching top channels and joining")
	b.store.AddChannels(b.helixClient.GetTopChannels()...)

	b.joinStoreChannels()
}
<|endoftext|>


<commit_before>package ics

import (
	"errors"
	"strconv"
	"strings"
)

const (
	beginc           = "BEGIN"
	endc             = "END"
	calscalec        = "CALSCALE"
	methodc          = "METHOD"
	prodidc          = "PRODID"
	versionc         = "VERSION"
	attachc          = "ATTACH"
	categoriesc      = "CATEGORIES"
	classc           = "CLASS"
	commentc         = "COMMENT"
	descriptionc     = "DESCRIPTION"
	geoc             = "GEO"
	locationc        = "LOCATION"
	percentcompletec = "PERCENT-COMPLETE"
	priorityc        = "PRIORITY"
	resourcesc       = "RESOURCES"
	statusc          = "STATUS"
	summaryc         = "SUMMARY"
	completedc       = "COMPLETED"
	dtendc           = "DTEND"
	duec             = "DUE"
	dtstartc         = "DTSTART"
	durationc        = "DURATION"
	freebusyc        = "FREEBUSY"
	transpc          = "TRANSP"
	tzidc            = "TZID"
	tznamec          = "TZNAME"
	tzoffsetfromc    = "TZOFFSETFROM"
	tzoffsettoc      = "TZOFFSETTO"
	tzurlc           = "TZURL"
	attendeec        = "ATTENDEE"
	contactc         = "CONTACT"
	organizerc       = "ORGANIZER"
	recuridc         = "RECURRENCE-ID"
	relatedc         = "RELATED-TO"
	urlc             = "URL"
	uidc             = "UID"
	exdatec          = "EXDATE"
	rdatec           = "RDATE"
	rrulec           = "RRULE"
	actionc          = "ACTION"
	repeatc          = "REPEAT"
	triggerc         = "TRIGGER"
	createdc         = "CREATED"
	dtstampc         = "DTSTAMP"
	lastmodc         = "LAST-MODIFIED"
	seqc             = "SEQUENCE"
	rstatusc         = "REQUEST-STATUS"
)

type component interface{}

type begin string

func (p *parser) readBeginComponent() (component, error) {
	v, err := p.readValue()
	if err != nil {
		return nil, err
	}
	return begin(unescape(v)), nil
}

type end string

func (p *parser) readEndComponent() (component, error) {
	v, err := p.readValue()
	if err != nil {
		return nil, err
	}
	return end(unescape(v)), nil
}

type requestStatus struct {
	Language          string
	StatusCode        int
	StatusDescription string
	Extra             string
}

func (p *parser) readRequestStatusComponent() (component, error) {
	as, err := p.readAttributes(languageparam)
	if err != nil {
		return nil, err
	}
	v, err := p.readValue()
	if err != nil {
		return nil, err
	}
	parts := strings.SplitN(v, ";", 3)
	if len(parts) < 2 {
		return nil, ErrUnsupportedValue
	}
	c, err := strconv.ParseFloat(parts[0], 32)
	if err != nil {
		return nil, err
	}
	ci := int(c * 100)
	if ci < 100 || ci > 499 {
		return nil, ErrUnsupportedValue
	}
	r := requestStatus{
		StatusCode:        ci,
		StatusDescription: parts[1],
	}
	if len(parts) == 3 {
		r.Extra = parts[2]
	}
	if l, ok := as[languageparam]; ok {
		r.Language = l.String()
	}
	return r, nil
}

type unknown struct {
	Name   string
	Params map[string]attribute
	Value  string
}

func (p *parser) readUnknownComponent(name string) (component, error) {
	vs, err := p.readAttributes("*")
	if err != nil {
		return nil, err
	}
	v, err := p.readValue()
	if err != nil {
		return nil, err
	}
	return unknown{
		name,
		vs,
		v,
	}, err
}

// Errors

var (
	ErrUnsupportedValue            = errors.New("attribute contained unsupported value")
	ErrInvalidAttributeCombination = errors.New("invalid combination of attributes")
)
<commit_msg>reworked requestStatus parsing<commit_after>package ics

import (
	"errors"
	"strings"
)

const (
	beginc           = "BEGIN"
	endc             = "END"
	calscalec        = "CALSCALE"
	methodc          = "METHOD"
	prodidc          = "PRODID"
	versionc         = "VERSION"
	attachc          = "ATTACH"
	categoriesc      = "CATEGORIES"
	classc           = "CLASS"
	commentc         = "COMMENT"
	descriptionc     = "DESCRIPTION"
	geoc             = "GEO"
	locationc        = "LOCATION"
	percentcompletec = "PERCENT-COMPLETE"
	priorityc        = "PRIORITY"
	resourcesc       = "RESOURCES"
	statusc          = "STATUS"
	summaryc         = "SUMMARY"
	completedc       = "COMPLETED"
	dtendc           = "DTEND"
	duec             = "DUE"
	dtstartc         = "DTSTART"
	durationc        = "DURATION"
	freebusyc        = "FREEBUSY"
	transpc          = "TRANSP"
	tzidc            = "TZID"
	tznamec          = "TZNAME"
	tzoffsetfromc    = "TZOFFSETFROM"
	tzoffsettoc      = "TZOFFSETTO"
	tzurlc           = "TZURL"
	attendeec        = "ATTENDEE"
	contactc         = "CONTACT"
	organizerc       = "ORGANIZER"
	recuridc         = "RECURRENCE-ID"
	relatedc         = "RELATED-TO"
	urlc             = "URL"
	uidc             = "UID"
	exdatec          = "EXDATE"
	rdatec           = "RDATE"
	rrulec           = "RRULE"
	actionc          = "ACTION"
	repeatc          = "REPEAT"
	triggerc         = "TRIGGER"
	createdc         = "CREATED"
	dtstampc         = "DTSTAMP"
	lastmodc         = "LAST-MODIFIED"
	seqc             = "SEQUENCE"
	rstatusc         = "REQUEST-STATUS"
)

type component interface{}

type begin string

func (p *parser) readBeginComponent() (component, error) {
	v, err := p.readValue()
	if err != nil {
		return nil, err
	}
	return begin(unescape(v)), nil
}

type end string

func (p *parser) readEndComponent() (component, error) {
	v, err := p.readValue()
	if err != nil {
		return nil, err
	}
	return end(unescape(v)), nil
}

type requestStatus struct {
	Language          string
	StatusCode        int
	StatusDescription string
	Extra             string
}

func (p *parser) readRequestStatusComponent() (component, error) {
	as, err := p.readAttributes(languageparam)
	if err != nil {
		return nil, err
	}
	v, err := p.readValue()
	if err != nil {
		return nil, err
	}
	parts := strings.SplitN(v, ";", 3)
	if len(parts) < 2 {
		return nil, ErrUnsupportedValue
	}
	if len(parts[0]) != 4 {
		return nil, ErrUnsupportedValue
	}
	if parts[0][0] < '1' || parts[0][0] > '4' || parts[0][1] != '.' || parts[0][2] < '0' || parts[0][2] > '9' || parts[0][3] < '0' || parts[0][3] > '9' {
		return nil, ErrUnsupportedValue
	}

	r := requestStatus{
		StatusCode:        int(parts[0][0]-'0')*100 + int(parts[0][2]-'0')*10 + int(parts[0][3]-'0'),
		StatusDescription: string(unescape(parts[1])),
	}
	if len(parts) == 3 {
		r.Extra = string(unescape(parts[2]))
	}
	if l, ok := as[languageparam]; ok {
		r.Language = l.String()
	}
	return r, nil
}

type unknown struct {
	Name   string
	Params map[string]attribute
	Value  string
}

func (p *parser) readUnknownComponent(name string) (component, error) {
	vs, err := p.readAttributes("*")
	if err != nil {
		return nil, err
	}
	v, err := p.readValue()
	if err != nil {
		return nil, err
	}
	return unknown{
		name,
		vs,
		v,
	}, err
}

// Errors

var (
	ErrUnsupportedValue            = errors.New("attribute contained unsupported value")
	ErrInvalidAttributeCombination = errors.New("invalid combination of attributes")
)
<|endoftext|>


<commit_before>package handlers

import (
	"net/http"

	"github.com/gorilla/mux"
	"github.com/play-with-docker/play-with-docker/config"
)

func Home(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	sessionId := vars["sessionId"]

	s := core.SessionGet(sessionId)
	if s == nil {
		// Session doesn't exist (can happen if closing the sessions an reloading the page, or similar).
		w.WriteHeader(http.StatusNotFound)
		return
	}
	if s.Stack != "" {
		s.Host = r.Host
		go core.SessionDeployStack(s)
	}

	if config.NoWindows {
		http.ServeFile(w, r, "./www/index-nw.html")
	} else {
		http.ServeFile(w, r, "./www/index.html")
	}
}
<commit_msg>Remove incorrect host replacement<commit_after>package handlers

import (
	"net/http"

	"github.com/gorilla/mux"
	"github.com/play-with-docker/play-with-docker/config"
)

func Home(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	sessionId := vars["sessionId"]

	s := core.SessionGet(sessionId)
	if s == nil {
		// Session doesn't exist (can happen if closing the sessions an reloading the page, or similar).
		w.WriteHeader(http.StatusNotFound)
		return
	}
	if s.Stack != "" {
		go core.SessionDeployStack(s)
	}

	if config.NoWindows {
		http.ServeFile(w, r, "./www/index-nw.html")
	} else {
		http.ServeFile(w, r, "./www/index.html")
	}
}
<|endoftext|>


<commit_before>package lexer

import (
	"strings"
	"unicode"

	"github.com/sysr-q/lua/token"
)

const (
	eof        = -1
	alphabet   = "abcdefghijklymnopqrstuvwxyzABCDEFGHIJKLYMNOPQRSTUVWXYZ"
	digits     = "01234567890"
	underscore = "_"
)

// lexText is the starting point for a new Lua lexer.
func lexText(l *lexer) stateFn {
	l.skipSpace()

	if r := l.peek(); unicode.IsDigit(r) {
		return lexNumberLiteral
	} else if r == '.' {
		l.next() // must advance because we only peeked before
		r2 := l.peek()
		l.backup()
		if unicode.IsDigit(r2) {
			return lexNumberLiteral
		}
	}

	if strings.HasPrefix(l.input[l.pos:], "--[") {
		// TODO
		return lexBlockComment
	}

	if strings.HasPrefix(l.input[l.pos:], "--") {
		return lexLineComment
	}

	if l.peek() == eof {
		l.emit(token.EOF)
		return nil
	}

	if l.peek() == '\'' {
		return lexSingleQuotedStringLiteral
	}

	if l.peek() == '"' {
		return lexDoubleQuotedStringLiteral
	}

	if l.peek() == '[' {
		// TODO
		return lexDoubleBracketStringLiteral
	}

	for _, tokenString := range token.TokenList {
		t := token.TokenMap[tokenString]
		potentialToken := l.input[l.pos:]
		if len(potentialToken) > len(tokenString) {
			potentialToken = potentialToken[:len(tokenString)]
		}
		if strings.HasPrefix(strings.ToLower(potentialToken), tokenString) {
			l.pos += token.Pos(len(tokenString))
			if IsKeyword(t, tokenString) && l.accept(alphabet+underscore+digits) {
				l.backup() // to account for the character consumed by accept
				l.pos -= token.Pos(len(tokenString))
				break
			}
			l.emit(t)
			return lexText
		}
	}

	l.acceptRun(alphabet + underscore + digits)
	l.emit(token.Identifier)
	return lexText
}

func lexNumberLiteral(l *lexer) stateFn {
	if l.accept("0") && l.accept("xX") {
		// hexadecimal?
		l.acceptRun(digits + "abcdefABCDEF")
		l.emit(token.NumberLiteral)
		return lexText
	}

	// is decimal?
	l.acceptRun(digits)
	if l.accept(".") {
		l.acceptRun(digits)
	}

	// TODO: 3.1e-1 etc
	if l.accept("eE") {
		l.acceptRun(digits)
	}

	l.emit(token.NumberLiteral)
	return lexText
}

func lexBlockComment(l *lexer) stateFn {
	// Eat the --[
	l.acceptRun("-")
	l.next()

	equalsRun := l.acceptRun("=")
	if !l.accept("[") {
		l.errorf("didn't find another opening bracket")
	}

	for {
		switch l.next() {
		case '\\':
			l.next()
			continue
		case ']':
			if l.acceptRun("=") != equalsRun {
				// Not the same amount as when we opened this string.
				continue
			}
			l.accept("]")
			l.ignore()
			//l.emit(token.Comment)
			return lexText
		}
	}
}

func lexLineComment(l *lexer) stateFn {
	lineLength := strings.Index(l.input[l.pos:], "\n")
	if lineLength == 0 {
		// this is the last line, so lex until the end
		lineLength = len(l.input[l.pos:])
	}

	l.pos += token.Pos(lineLength)
	l.ignore()
	//l.emit(token.Comment)
	return lexText
}

func lexSingleQuotedStringLiteral(l *lexer) stateFn {
	l.next()
	for {
		switch l.next() {
		case '\\':
			l.next()
			continue
		case '\'':
			l.emit(token.StringLiteral)
			return lexText
		}
	}
}

func lexDoubleQuotedStringLiteral(l *lexer) stateFn {
	l.next()
	for {
		switch l.next() {
		case '\\':
			l.next()
			continue
		case '"':
			l.emit(token.StringLiteral)
			return lexText
		}
	}
}

func lexDoubleBracketStringLiteral(l *lexer) stateFn {
	// Eat the opening '['
	l.next()
	equalsRun := l.acceptRun("=")
	if !l.accept("[") {
		l.errorf("didn't find another opening bracket")
	}

	// Potentially eat up a newline.
	l.accept("\n")

	for {
		switch l.next() {
		case '\\':
			l.next()
			continue
		case ']':
			if l.acceptRun("=") != equalsRun {
				// Not the same amount as when we opened this string.
				continue
			}
			l.accept("]")
			l.emit(token.StringLiteral)
			return lexText
		}
	}
}
<commit_msg>Fix up l.errorf() calls.<commit_after>package lexer

import (
	"strings"
	"unicode"

	"github.com/sysr-q/lua/token"
)

const (
	eof        = -1
	alphabet   = "abcdefghijklymnopqrstuvwxyzABCDEFGHIJKLYMNOPQRSTUVWXYZ"
	digits     = "01234567890"
	underscore = "_"
)

// lexText is the starting point for a new Lua lexer.
func lexText(l *lexer) stateFn {
	l.skipSpace()

	if r := l.peek(); unicode.IsDigit(r) {
		return lexNumberLiteral
	} else if r == '.' {
		l.next() // must advance because we only peeked before
		r2 := l.peek()
		l.backup()
		if unicode.IsDigit(r2) {
			return lexNumberLiteral
		}
	}

	if strings.HasPrefix(l.input[l.pos:], "--[") {
		return lexBlockComment
	}

	if strings.HasPrefix(l.input[l.pos:], "--") {
		return lexLineComment
	}

	if l.peek() == eof {
		l.emit(token.EOF)
		return nil
	}

	if l.peek() == '\'' {
		return lexSingleQuotedStringLiteral
	}

	if l.peek() == '"' {
		return lexDoubleQuotedStringLiteral
	}

	if l.peek() == '[' {
		return lexDoubleBracketStringLiteral
	}

	for _, tokenString := range token.TokenList {
		t := token.TokenMap[tokenString]
		potentialToken := l.input[l.pos:]
		if len(potentialToken) > len(tokenString) {
			potentialToken = potentialToken[:len(tokenString)]
		}
		if strings.HasPrefix(strings.ToLower(potentialToken), tokenString) {
			l.pos += token.Pos(len(tokenString))
			if IsKeyword(t, tokenString) && l.accept(alphabet+underscore+digits) {
				l.backup() // to account for the character consumed by accept
				l.pos -= token.Pos(len(tokenString))
				break
			}
			l.emit(t)
			return lexText
		}
	}

	l.acceptRun(alphabet + underscore + digits)
	l.emit(token.Identifier)
	return lexText
}

func lexNumberLiteral(l *lexer) stateFn {
	if l.accept("0") && l.accept("xX") {
		// hexadecimal?
		l.acceptRun(digits + "abcdefABCDEF")
		l.emit(token.NumberLiteral)
		return lexText
	}

	// is decimal?
	l.acceptRun(digits)
	if l.accept(".") {
		l.acceptRun(digits)
	}

	// TODO: 3.1e-1 etc
	if l.accept("eE") {
		l.acceptRun(digits)
	}

	l.emit(token.NumberLiteral)
	return lexText
}

func lexBlockComment(l *lexer) stateFn {
	// Eat the --[
	l.acceptRun("-")
	l.next()

	equalsRun := l.acceptRun("=")
	if !l.accept("[") {
		return l.errorf("didn't find another opening bracket")
	}

	for {
		switch l.next() {
		case '\\':
			l.next()
			continue
		case ']':
			if l.acceptRun("=") != equalsRun {
				// Not the same amount as when we opened this string.
				continue
			}
			if !l.accept("]") {
				return l.errorf("didn't find closing bracket")
			}
			l.ignore()
			return lexText
		}
	}
}

func lexLineComment(l *lexer) stateFn {
	lineLength := strings.Index(l.input[l.pos:], "\n")
	if lineLength == 0 {
		// this is the last line, so lex until the end
		lineLength = len(l.input[l.pos:])
	}

	l.pos += token.Pos(lineLength)
	l.ignore()
	return lexText
}

func lexSingleQuotedStringLiteral(l *lexer) stateFn {
	l.next()
	for {
		switch l.next() {
		case '\\':
			l.next()
			continue
		case '\'':
			l.emit(token.StringLiteral)
			return lexText
		}
	}
}

func lexDoubleQuotedStringLiteral(l *lexer) stateFn {
	l.next()
	for {
		switch l.next() {
		case '\\':
			l.next()
			continue
		case '"':
			l.emit(token.StringLiteral)
			return lexText
		}
	}
}

func lexDoubleBracketStringLiteral(l *lexer) stateFn {
	// Eat the opening '['
	l.next()
	equalsRun := l.acceptRun("=")
	if !l.accept("[") {
		return l.errorf("didn't find another opening bracket")
	}

	// Potentially eat up a newline.
	l.accept("\n")

	for {
		switch l.next() {
		case '\\':
			l.next()
			continue
		case ']':
			if l.acceptRun("=") != equalsRun {
				// Not the same amount as when we opened this string.
				continue
			}
			if !l.accept("]") {
				return l.errorf("didn't find closing bracket")
			}
			l.emit(token.StringLiteral)
			return lexText
		}
	}
}
<|endoftext|>