<commit_before>package config

import (
	"fmt"
	"reflect"
	"regexp"
	"strings"

	"github.com/mitchellh/reflectwalk"
)

// varRegexp is a regexp that matches variables such as ${foo.bar}
var varRegexp *regexp.Regexp

func init() {
	varRegexp = regexp.MustCompile(`(?i)(\$+)\{([-.a-z0-9_]+)\}`)
}

// replaceVariables takes a configuration and a mapping of variables
// and performs the structure walking necessary to properly replace
// all the variables.
func replaceVariables(
	c map[string]interface{},
	vs map[string]string) error {
	w := &variableReplaceWalker{Values: vs}
	return reflectwalk.Walk(c, w)
}

// variableDetectWalker implements interfaces for the reflectwalk package
// (github.com/mitchellh/reflectwalk) that can be used to automatically
// pull out the variables that need replacing.
type variableDetectWalker struct {
	Variables map[string]InterpolatedVariable
}

func (w *variableDetectWalker) Primitive(v reflect.Value) error {
	// We only care about strings
	if v.Kind() == reflect.Interface {
		v = v.Elem()
	}
	if v.Kind() != reflect.String {
		return nil
	}

	// XXX: This can be a lot more efficient if we used a real
	// parser. A regexp is a hammer though that will get this working.

	matches := varRegexp.FindAllStringSubmatch(v.String(), -1)
	if len(matches) == 0 {
		return nil
	}

	for _, match := range matches {
		dollars := len(match[1])

		// If there are even amounts of dollar signs, then it is escaped
		if dollars%2 == 0 {
			continue
		}

		// Otherwise, record it
		key := match[2]
		if w.Variables == nil {
			w.Variables = make(map[string]InterpolatedVariable)
		}
		if _, ok := w.Variables[key]; ok {
			continue
		}

		var err error
		var iv InterpolatedVariable
		if strings.Index(key, ".") == -1 {
			return fmt.Errorf(
				"Interpolated variable '%s' has bad format. "+
					"Did you mean 'var.%s'?",
				key, key)
		}

		if strings.HasPrefix(key, "var.") {
			iv, err = NewUserVariable(key)
		} else {
			iv, err = NewResourceVariable(key)
		}

		if err != nil {
			return err
		}

		w.Variables[key] = iv
	}

	return nil
}

// variableReplaceWalker implements interfaces for reflectwalk that
// is used to replace variables with their values.
//
// If Values does not have every available value, then the program
// will _panic_. The variableDetectWalker will tell you all variables
// you need.
type variableReplaceWalker struct {
	Values      map[string]string
	UnknownKeys []string

	key    []string
	loc    reflectwalk.Location
	m, mk  reflect.Value
	cs     []reflect.Value
	csData interface{}
}

func (w *variableReplaceWalker) Enter(loc reflectwalk.Location) error {
	w.loc = loc
	return nil
}

func (w *variableReplaceWalker) Exit(loc reflectwalk.Location) error {
	w.loc = reflectwalk.None

	switch loc {
	case reflectwalk.Map:
		w.cs = w.cs[:len(w.cs)-1]
	case reflectwalk.MapValue:
		w.key = w.key[:len(w.key)-1]
	}

	return nil
}

func (w *variableReplaceWalker) Map(m reflect.Value) error {
	w.cs = append(w.cs, m)
	return nil
}

func (w *variableReplaceWalker) MapElem(m, k, v reflect.Value) error {
	w.m = m
	w.mk = k
	w.csData = k
	w.key = append(w.key, k.String())
	return nil
}

func (w *variableReplaceWalker) Primitive(v reflect.Value) error {
	// We only care about strings
	setV := v
	if v.Kind() == reflect.Interface {
		setV = v
		v = v.Elem()
	}
	if v.Kind() != reflect.String {
		return nil
	}

	matches := varRegexp.FindAllStringSubmatch(v.String(), -1)
	if len(matches) == 0 {
		return nil
	}

	result := v.String()
	for _, match := range matches {
		dollars := len(match[1])

		// If there are even amounts of dollar signs, then it is escaped
		if dollars%2 == 0 {
			continue
		}

		// Get the key
		key := match[2]
		value, ok := w.Values[key]
		if !ok {
			panic("no value for variable key: " + key)
		}

		// If this is an unknown variable, then we remove it from
		// the configuration.
		if value == UnknownVariableValue {
			w.removeCurrent()
			return nil
		}

		// Replace
		result = strings.Replace(result, match[0], value, -1)
	}

	resultVal := reflect.ValueOf(result)
	if w.loc == reflectwalk.MapValue {
		// If we're in a map, then the only way to set a map value is
		// to set it directly.
		w.m.SetMapIndex(w.mk, resultVal)
	} else {
		// Otherwise, we should be addressable
		setV.Set(resultVal)
	}

	return nil
}

func (w *variableReplaceWalker) removeCurrent() {
	c := w.cs[len(w.cs)-1]
	switch c.Kind() {
	case reflect.Map:
		// Zero value so that we delete the map key
		var val reflect.Value

		// Get the key and delete it
		k := w.csData.(reflect.Value)
		c.SetMapIndex(k, val)
	}

	// Append the key to the unknown keys
	w.UnknownKeys = append(w.UnknownKeys, strings.Join(w.key, "."))
}
<commit_msg>config: remove redundant variables<commit_after>package config

import (
	"fmt"
	"reflect"
	"regexp"
	"strings"

	"github.com/mitchellh/reflectwalk"
)

// varRegexp is a regexp that matches variables such as ${foo.bar}
var varRegexp *regexp.Regexp

func init() {
	varRegexp = regexp.MustCompile(`(?i)(\$+)\{([-.a-z0-9_]+)\}`)
}

// replaceVariables takes a configuration and a mapping of variables
// and performs the structure walking necessary to properly replace
// all the variables.
func replaceVariables(
	c map[string]interface{},
	vs map[string]string) error {
	w := &variableReplaceWalker{Values: vs}
	return reflectwalk.Walk(c, w)
}

// variableDetectWalker implements interfaces for the reflectwalk package
// (github.com/mitchellh/reflectwalk) that can be used to automatically
// pull out the variables that need replacing.
type variableDetectWalker struct {
	Variables map[string]InterpolatedVariable
}

func (w *variableDetectWalker) Primitive(v reflect.Value) error {
	// We only care about strings
	if v.Kind() == reflect.Interface {
		v = v.Elem()
	}
	if v.Kind() != reflect.String {
		return nil
	}

	// XXX: This can be a lot more efficient if we used a real
	// parser. A regexp is a hammer though that will get this working.

	matches := varRegexp.FindAllStringSubmatch(v.String(), -1)
	if len(matches) == 0 {
		return nil
	}

	for _, match := range matches {
		dollars := len(match[1])

		// If there are even amounts of dollar signs, then it is escaped
		if dollars%2 == 0 {
			continue
		}

		// Otherwise, record it
		key := match[2]
		if w.Variables == nil {
			w.Variables = make(map[string]InterpolatedVariable)
		}
		if _, ok := w.Variables[key]; ok {
			continue
		}

		var err error
		var iv InterpolatedVariable
		if strings.Index(key, ".") == -1 {
			return fmt.Errorf(
				"Interpolated variable '%s' has bad format. "+
					"Did you mean 'var.%s'?",
				key, key)
		}

		if strings.HasPrefix(key, "var.") {
			iv, err = NewUserVariable(key)
		} else {
			iv, err = NewResourceVariable(key)
		}

		if err != nil {
			return err
		}

		w.Variables[key] = iv
	}

	return nil
}

// variableReplaceWalker implements interfaces for reflectwalk that
// is used to replace variables with their values.
//
// If Values does not have every available value, then the program
// will _panic_. The variableDetectWalker will tell you all variables
// you need.
type variableReplaceWalker struct {
	Values      map[string]string
	UnknownKeys []string

	key    []string
	loc    reflectwalk.Location
	cs     []reflect.Value
	csData interface{}
}

func (w *variableReplaceWalker) Enter(loc reflectwalk.Location) error {
	w.loc = loc
	return nil
}

func (w *variableReplaceWalker) Exit(loc reflectwalk.Location) error {
	w.loc = reflectwalk.None

	switch loc {
	case reflectwalk.Map:
		w.cs = w.cs[:len(w.cs)-1]
	case reflectwalk.MapValue:
		w.key = w.key[:len(w.key)-1]
	}

	return nil
}

func (w *variableReplaceWalker) Map(m reflect.Value) error {
	w.cs = append(w.cs, m)
	return nil
}

func (w *variableReplaceWalker) MapElem(m, k, v reflect.Value) error {
	w.csData = k
	w.key = append(w.key, k.String())
	return nil
}

func (w *variableReplaceWalker) Primitive(v reflect.Value) error {
	// We only care about strings
	setV := v
	if v.Kind() == reflect.Interface {
		setV = v
		v = v.Elem()
	}
	if v.Kind() != reflect.String {
		return nil
	}

	matches := varRegexp.FindAllStringSubmatch(v.String(), -1)
	if len(matches) == 0 {
		return nil
	}

	result := v.String()
	for _, match := range matches {
		dollars := len(match[1])

		// If there are even amounts of dollar signs, then it is escaped
		if dollars%2 == 0 {
			continue
		}

		// Get the key
		key := match[2]
		value, ok := w.Values[key]
		if !ok {
			panic("no value for variable key: " + key)
		}

		// If this is an unknown variable, then we remove it from
		// the configuration.
		if value == UnknownVariableValue {
			w.removeCurrent()
			return nil
		}

		// Replace
		result = strings.Replace(result, match[0], value, -1)
	}

	resultVal := reflect.ValueOf(result)
	if w.loc == reflectwalk.MapValue {
		// If we're in a map, then the only way to set a map value is
		// to set it directly.
		m := w.cs[len(w.cs)-1]
		mk := w.csData.(reflect.Value)
		m.SetMapIndex(mk, resultVal)
	} else {
		// Otherwise, we should be addressable
		setV.Set(resultVal)
	}

	return nil
}

func (w *variableReplaceWalker) removeCurrent() {
	c := w.cs[len(w.cs)-1]
	switch c.Kind() {
	case reflect.Map:
		// Zero value so that we delete the map key
		var val reflect.Value

		// Get the key and delete it
		k := w.csData.(reflect.Value)
		c.SetMapIndex(k, val)
	}

	// Append the key to the unknown keys
	w.UnknownKeys = append(w.UnknownKeys, strings.Join(w.key, "."))
}
<|endoftext|>


<commit_before>package parser

import (
	"strings"
	"testing"
)

func TestParseFailures(t *testing.T) {
	tests := []struct {
		src string
		err string
	}{
		// Undefined token
		{"unexpected", "1:1: Unexpected token IDENT(unexpected)"},
		{"bar () {}", "1:1: Unexpected token IDENT(bar)"},
		// Invalid functions
		{"fn test{}", "1:8: Expected [LPAREN] got LBRACE"},
		{"fn test()", "1:10: Expected code block got EOF"},
		{"fn test(,) {}", "1:9: Expected [IDENT RPAREN] got COMMA"},
		{"fn test(bar i = 1) {}", "1:13: Expected [RPAREN COMMA] got i"},
		{"fn test(int) : {}", "1:16: Expected function return type got LBRACE({)"},
		{"fn test(foo : float foo) {}", "1:21: Expected [RPAREN COMMA] got foo"},
		{"fn test(foo : ) {}", "1:15: Expected [IDENT] got RPAREN"},
		{"fn test(foo : bar = ) {}", "1:21: Expected expression got RPAREN())"},
		{"fn test(foo : int) {]", "1:21: Expected code block got RBRACK(])"},
		{"fn", "1:3: Expected function name or argument list got EOF"},
		{"fn (foo : int) {}", "1:17: Root level functions can't be anonymous"},
		{"fn ( {}", "1:6: Expected [IDENT RPAREN] got LBRACE"},
		// Variable declarations
		{"var [", "1:5: Expected variable or tuple declaration got LBRACK([)"},
		{"var foo = (1", "1:13: Expected [RPAREN] got EOF"},
		{"var foo = ()", "1:12: Expected expression got RPAREN())"},
		{"var foo = (1,)", "1:14: Expected expression got RPAREN())"},
		{"var foo : (int, int", "1:20: Expected [RPAREN] got EOF"},
		{"var foo : ()", "1:12: Expected type got RPAREN())"},
		{"var foo : (int,)", "1:16: Expected type got RPAREN())"},
		{"var (foo) :", "1:12: Expected type got EOF"},
		{"var foo :", "1:10: Expected type got EOF"},
		{"var (", "1:6: Expected [RPAREN] got EOF"},
		{"var foo : (int32, float32) :", "1:29: Expected function return type got EOF"},
		{"var foo : [int32", "1:17: Expected [RBRACK] got EOF"},
		{"var foo : [", "1:12: Expected length expression got EOF"},
		// For loops
		{"fn foobar() { for var i = 0; i; [] }", "1:36: Expected array type got RBRACE(})"},
		{"fn foobar() { for var i = 0; {}}", "1:30: Expected expression got LBRACE({)"},
		{"fn foobar() { for var i = 0; true {}}", "1:35: Expected ; got LBRACE({)"},
		{"fn foobar() { for }", "1:19: Expected statement, ; or code block got RBRACE(})"},
		{"fn foobar() { for true true {} }", "1:24: Expected ; or code block got BOOL(true)"},
		{"fn foobar() { foo = , }", "1:21: Expected expression got COMMA(,)"},
		// Arrays
		{"var foo : []", "1:13: Expected array type got EOF"},
		{"var foo : []int32 = []", "1:23: Expected array type got EOF"},
		{"var foo : []int32 = []int32", "1:28: Expected [LBRACE] got EOF"},
		{"var foo : []int32 = []int32{", "1:29: Expected expression list or left brace got EOF"},
		{"var foo : []int32 = []int32{1", "1:30: Expected [RBRACE] got EOF"},
		// If statemts
		{"fn foobar() {  if }", "1:19: Expected expression got RBRACE(})"},
		{"fn foobar() {  if 1 < {} }", "1:23: Expected expression got LBRACE({)"},
		{"fn foobar() {  if 1 ! {} }", "1:21: Expected code block got EXCLAMATION(!)"},
		{"fn foobar() {  if true foo }", "1:24: Expected code block got IDENT(foo)"},
		{"fn foobar() {  if true {} else f", "1:32: Expected if statement or code block got IDENT(f)"},
		// Function calls
		{"fn foobar() {  foobar(.) }", "1:23: Expected [RPAREN] got PERIOD"},
		{"fn foobar() {  fn foobar(i:int;) {} }", "1:31: Expected [RPAREN COMMA] got SEMICOLON"},
		// Member expressions
		{"fn foobar() {  foobar.false }", "1:23: Expected property name got BOOL(false)"},
		// Reservedkeyword
		{"fn return() {  }", "1:4: return is a reserved keyword"},
		{"fn foobar() { var fn = 1 }", "1:19: fn is a reserved keyword"},
		{"fn foobar() { foo.return }", "1:19: return is a reserved keyword"},
		{"fn foobar(fn: int) {  }", "1:11: fn is a reserved keyword"},
		{"fn foobar(int: fn) {  }", "1:16: fn is a reserved keyword"},
		{"fn foobar() { foobar(int:return) }", "1:26: return is a reserved keyword"},
		{"fn foobar() { foobar(return:0) }", "1:28: return is a reserved keyword"},
		// BinaryExpression
		{"fn foobar() { var foo = 1 + }", "1:29: Expected expression got RBRACE(})"},
		// UnaryExpression
		{"fn foobar() { var foo = - }", "1:27: Expected expression got RBRACE(})"},
		// Ellipsis
		{"fn foobar() { var foo = ... }", "1:25: Expected expression got ..."},
		// MacroSubstitutions inside normal code
		{"fn foobar() {var foo = $f}", "1:24: Could not find matching node for $f"},
		{"fn foobar() {$f}", "1:14: Could not find matching node for $f"},
		// Macros
		{"macro M { ($(foo),,*) : () }", "1:19: Macro repetition can only have one token as a delimiter"},
		{"macro M { () : () } fn main() { M!(foo) }", "1:36: No rules expected token IDENT(foo)"},
		{"macro M { () : () } fn main() { M!( }", "1:37: No rules expected token RBRACE(})"},
		{"macro M { () : ($()) } fn main() { M!() }", "1:20: Expected [ASTERISK] got RPAREN"},
		{"macro M { () : ($(", "1:19: Expected token but got eof"},
		{"macro M { (()) : ($(", "1:21: Expected token but got eof"},
		{"macro M { ($foo) : ()", "1:16: Expected [COLON] got RPAREN"},
		{"macro M { ($foo:) : ()", "1:17: Expected pattern key type got RPAREN())"},
		{"macro M { ($) : ()", "1:13: Expected macro pattern got RPAREN())"},
		{"macro { () : () }", "1:7: Expected macro name got LBRACE({)"},
		{"macro M () : () }", "1:9: Expected [LBRACE] got LPAREN"},
		{"macro M { () : }", "1:17: Expected [LPAREN LBRACE LBRACK] got EOF"},
		{"macro M { () : ($a) } fn main() { M!() }", "1:38: Could not find macro argument for metavariable $a"},
		{"macro M { () : ($a) } fn main() { M! }", "1:38: Could not find macro argument for metavariable $a"},
		{"macro M { ($(foo)+) : () } fn main() { M!(bar) }", "1:43: No rules expected token IDENT(bar)"},
		{"fn main() { M!(foo) }", "1:13: No macro with name M"},
		{"macro M { (", "1:12: Expected token but got eof"},
		{"macro M { ($()", "1:15: Expected macro repetition delimeter or operand (+, * or ?) got EOF"},
	}
	for _, test := range tests {
		_, err := Parse(strings.NewReader(test.src))

		if err == nil {
			t.Errorf("Expected %s to return error %s, but got nothing", test.src, test.err)
		}

		if err.Error() != test.err {
			t.Errorf("Expected %s to return error %s, but got %s", test.src, test.err, err.Error())
		}
	}
}
<commit_msg>Add fuzz test result as ref<commit_after>package parser

import (
	"strings"
	"testing"
)

func TestParseFailures(t *testing.T) {
	tests := []struct {
		src string
		err string
	}{
		// Undefined token
		{"unexpected", "1:1: Unexpected token IDENT(unexpected)"},
		{"bar () {}", "1:1: Unexpected token IDENT(bar)"},
		// Invalid functions
		{"fn test{}", "1:8: Expected [LPAREN] got LBRACE"},
		{"fn test()", "1:10: Expected code block got EOF"},
		{"fn test(,) {}", "1:9: Expected [IDENT RPAREN] got COMMA"},
		{"fn test(bar i = 1) {}", "1:13: Expected [RPAREN COMMA] got i"},
		{"fn test(int) : {}", "1:16: Expected function return type got LBRACE({)"},
		{"fn test(foo : float foo) {}", "1:21: Expected [RPAREN COMMA] got foo"},
		{"fn test(foo : ) {}", "1:15: Expected [IDENT] got RPAREN"},
		{"fn test(foo : bar = ) {}", "1:21: Expected expression got RPAREN())"},
		{"fn test(foo : int) {]", "1:21: Expected code block got RBRACK(])"},
		{"fn", "1:3: Expected function name or argument list got EOF"},
		{"fn (foo : int) {}", "1:17: Root level functions can't be anonymous"},
		{"fn ( {}", "1:6: Expected [IDENT RPAREN] got LBRACE"},
		// Variable declarations
		{"var [", "1:5: Expected variable or tuple declaration got LBRACK([)"},
		{"var foo = (1", "1:13: Expected [RPAREN] got EOF"},
		{"var foo = ()", "1:12: Expected expression got RPAREN())"},
		{"var foo = (1,)", "1:14: Expected expression got RPAREN())"},
		{"var foo : (int, int", "1:20: Expected [RPAREN] got EOF"},
		{"var foo : ()", "1:12: Expected type got RPAREN())"},
		{"var foo : (int,)", "1:16: Expected type got RPAREN())"},
		{"var (foo) :", "1:12: Expected type got EOF"},
		{"var foo :", "1:10: Expected type got EOF"},
		{"var (", "1:6: Expected [RPAREN] got EOF"},
		{"var foo : (int32, float32) :", "1:29: Expected function return type got EOF"},
		{"var foo : [int32", "1:17: Expected [RBRACK] got EOF"},
		{"var foo : [", "1:12: Expected length expression got EOF"},
		// For loops
		{"fn foobar() { for var i = 0; i; [] }", "1:36: Expected array type got RBRACE(})"},
		{"fn foobar() { for var i = 0; {}}", "1:30: Expected expression got LBRACE({)"},
		{"fn foobar() { for var i = 0; true {}}", "1:35: Expected ; got LBRACE({)"},
		{"fn foobar() { for }", "1:19: Expected statement, ; or code block got RBRACE(})"},
		{"fn foobar() { for true true {} }", "1:24: Expected ; or code block got BOOL(true)"},
		{"fn foobar() { foo = , }", "1:21: Expected expression got COMMA(,)"},
		// Arrays
		{"var foo : []", "1:13: Expected array type got EOF"},
		{"var foo : []int32 = []", "1:23: Expected array type got EOF"},
		{"var foo : []int32 = []int32", "1:28: Expected [LBRACE] got EOF"},
		{"var foo : []int32 = []int32{", "1:29: Expected expression list or left brace got EOF"},
		{"var foo : []int32 = []int32{1", "1:30: Expected [RBRACE] got EOF"},
		// If statemts
		{"fn foobar() {  if }", "1:19: Expected expression got RBRACE(})"},
		{"fn foobar() {  if 1 < {} }", "1:23: Expected expression got LBRACE({)"},
		{"fn foobar() {  if 1 ! {} }", "1:21: Expected code block got EXCLAMATION(!)"},
		{"fn foobar() {  if true foo }", "1:24: Expected code block got IDENT(foo)"},
		{"fn foobar() {  if true {} else f", "1:32: Expected if statement or code block got IDENT(f)"},
		// Function calls
		{"fn foobar() {  foobar(.) }", "1:23: Expected [RPAREN] got PERIOD"},
		{"fn foobar() {  fn foobar(i:int;) {} }", "1:31: Expected [RPAREN COMMA] got SEMICOLON"},
		// Member expressions
		{"fn foobar() {  foobar.false }", "1:23: Expected property name got BOOL(false)"},
		// Reservedkeyword
		{"fn return() {  }", "1:4: return is a reserved keyword"},
		{"fn foobar() { var fn = 1 }", "1:19: fn is a reserved keyword"},
		{"fn foobar() { foo.return }", "1:19: return is a reserved keyword"},
		{"fn foobar(fn: int) {  }", "1:11: fn is a reserved keyword"},
		{"fn foobar(int: fn) {  }", "1:16: fn is a reserved keyword"},
		{"fn foobar() { foobar(int:return) }", "1:26: return is a reserved keyword"},
		{"fn foobar() { foobar(return:0) }", "1:28: return is a reserved keyword"},
		// BinaryExpression
		{"fn foobar() { var foo = 1 + }", "1:29: Expected expression got RBRACE(})"},
		// UnaryExpression
		{"fn foobar() { var foo = - }", "1:27: Expected expression got RBRACE(})"},
		// Ellipsis
		{"fn foobar() { var foo = ... }", "1:25: Expected expression got ..."},
		// MacroSubstitutions inside normal code
		{"fn foobar() {var foo = $f}", "1:24: Could not find matching node for $f"},
		{"fn foobar() {$f}", "1:14: Could not find matching node for $f"},
		// Macros
		{"macro M { ($(foo),,*) : () }", "1:19: Macro repetition can only have one token as a delimiter"},
		{"macro M { () : () } fn main() { M!(foo) }", "1:36: No rules expected token IDENT(foo)"},
		{"macro M { () : () } fn main() { M!( }", "1:37: No rules expected token RBRACE(})"},
		{"macro M { () : ($()) } fn main() { M!() }", "1:20: Expected [ASTERISK] got RPAREN"},
		{"macro M { () : ($(", "1:19: Expected token but got eof"},
		{"macro M { (()) : ($(", "1:21: Expected token but got eof"},
		{"macro M { ($foo) : ()", "1:16: Expected [COLON] got RPAREN"},
		{"macro M { ($foo:) : ()", "1:17: Expected pattern key type got RPAREN())"},
		{"macro M { ($) : ()", "1:13: Expected macro pattern got RPAREN())"},
		{"macro { () : () }", "1:7: Expected macro name got LBRACE({)"},
		{"macro M () : () }", "1:9: Expected [LBRACE] got LPAREN"},
		{"macro M { () : }", "1:17: Expected [LPAREN LBRACE LBRACK] got EOF"},
		{"macro M { () : ($a) } fn main() { M!() }", "1:38: Could not find macro argument for metavariable $a"},
		{"macro M { () : ($a) } fn main() { M! }", "1:38: Could not find macro argument for metavariable $a"},
		{"macro M { ($(foo)+) : () } fn main() { M!(bar) }", "1:43: No rules expected token IDENT(bar)"},
		{"fn main() { M!(foo) }", "1:13: No macro with name M"},
		{"macro M { (", "1:12: Expected token but got eof"},
		{"macro M { ($()", "1:15: Expected macro repetition delimeter or operand (+, * or ?) got EOF"},
		// fuzz test results
		//{"var r=foo(0(//", ""},
	}
	for _, test := range tests {
		_, err := Parse(strings.NewReader(test.src))

		if err == nil {
			t.Errorf("Expected %s to return error %s, but got nothing", test.src, test.err)
		}

		if err.Error() != test.err {
			t.Errorf("Expected %s to return error %s, but got %s", test.src, test.err, err.Error())
		}
	}
}
<|endoftext|>


<commit_before>package rnd

import (
	glfw "github.com/go-gl/glfw3"
	"github.com/tedsta/fission/core"
	"github.com/tedsta/gosfml"
)

const Ptu = 32.0

type RenderSystem struct {
	Window  *glfw.Window
	Target  *sf.RenderTarget
	View    *sf.View
	BgColor sf.Color // Background color
	optBits core.TypeBits
}

func NewRenderSystem(sizeX, sizeY int, winTitle string, optBits core.TypeBits) *RenderSystem {
	w, err := glfw.CreateWindow(sizeX, sizeY, winTitle, nil, nil)
	if err != nil {
		panic(err)
	}
	w.MakeContextCurrent()

	rt := sf.NewRenderTarget(sf.Vector2{float32(sizeX), float32(sizeY)})
	view := rt.DefaultView()
	r := &RenderSystem{w, rt, &view, sf.Color{0, 0, 0, 0}, SpriteComponentType | optBits}

	w.SetFramebufferSizeCallback(r.onResize)

	return r
}

func (r *RenderSystem) Begin(dt float32) {
	r.Target.Clear(r.BgColor)
	r.Target.SetView(*r.View)
}

func (r *RenderSystem) ProcessEntity(e *core.Entity, dt float32) {
	trans := e.Component(TransformComponentType).(*TransformComponent)

	rs := sf.RenderStates{sf.BlendAlpha, trans.T.Transform(), nil}

	renderCmpnts := e.Components(r.optBits)
	for _, cmpnt := range renderCmpnts {
		render := cmpnt.(RenderComponent).Render
		if render != nil {
			render(r.Target, rs)
		}
	}
}

func (r *RenderSystem) End(dt float32) {
	r.Window.SwapBuffers()
}

func (r *RenderSystem) TypeBits() (core.TypeBits, core.TypeBits) {
	return TransformComponentType, r.optBits
}

// init ##########################################################

func init() {
	SpriteComponentType = core.RegisterComponent(SpriteComponentFactory)
	TransformComponentType = core.RegisterComponent(TransformComponentFactory)
}

// Callbacks ###################################################################

func (r *RenderSystem) onResize(wnd *glfw.Window, w, h int) {
	r.Target.Size.X = float32(w)
	r.Target.Size.Y = float32(h)
}
<commit_msg>Fixed screen scaling<commit_after>package rnd

import (
	glfw "github.com/go-gl/glfw3"
	"github.com/tedsta/fission/core"
	"github.com/tedsta/gosfml"
)

const Ptu = 32.0

type RenderSystem struct {
	Window  *glfw.Window
	Target  *sf.RenderTarget
	View    *sf.View
	BgColor sf.Color // Background color
	optBits core.TypeBits
}

func NewRenderSystem(sizeX, sizeY int, winTitle string, optBits core.TypeBits) *RenderSystem {
	w, err := glfw.CreateWindow(sizeX, sizeY, winTitle, nil, nil)
	if err != nil {
		panic(err)
	}
	w.MakeContextCurrent()

	rt := sf.NewRenderTarget(sf.Vector2{float32(sizeX), float32(sizeY)})
	view := rt.DefaultView()
	r := &RenderSystem{w, rt, &view, sf.Color{0, 0, 0, 0}, SpriteComponentType | optBits}

	w.SetFramebufferSizeCallback(r.onResize)

	return r
}

func (r *RenderSystem) Begin(dt float32) {
	r.Target.Clear(r.BgColor)
	r.Target.SetView(*r.View)
}

func (r *RenderSystem) ProcessEntity(e *core.Entity, dt float32) {
	trans := e.Component(TransformComponentType).(*TransformComponent)

	rs := sf.RenderStates{sf.BlendAlpha, trans.T.Transform(), nil}

	renderCmpnts := e.Components(r.optBits)
	for _, cmpnt := range renderCmpnts {
		render := cmpnt.(RenderComponent).Render
		if render != nil {
			render(r.Target, rs)
		}
	}
}

func (r *RenderSystem) End(dt float32) {
	r.Window.SwapBuffers()
}

func (r *RenderSystem) TypeBits() (core.TypeBits, core.TypeBits) {
	return TransformComponentType, r.optBits
}

// init ##########################################################

func init() {
	SpriteComponentType = core.RegisterComponent(SpriteComponentFactory)
	TransformComponentType = core.RegisterComponent(TransformComponentFactory)
}

// Callbacks ###################################################################

func (r *RenderSystem) onResize(wnd *glfw.Window, w, h int) {
	r.Target.SetSize(sf.Vector2{float32(w), float32(h)})
	r.Window.SetSize(w, h)
	r.View.Reset(sf.Rect{0, 0, float32(w), float32(h)})
}
<|endoftext|>


<commit_before>package main

// go run client.go -f ~/skypi/sbs1.out
// go run client.go -h skypi:30003

import (
	"bufio"
	//"fmt"
	"flag"
	"io"
	"log"
	"net"
	"os"
	//"time"

	"github.com/skypies/adsb"
	"github.com/skypies/pi/tracktable"
)

var Log *log.Logger

var fHostPort string
var fFilename string
var fDumpPos  bool

func init() {
	flag.StringVar(&fHostPort, "h", "", "host:port of dump1090:30003")
	flag.StringVar(&fFilename, "f", "", "sbs formatted CSV file thing to read")
	flag.BoolVar  (&fDumpPos,  "pos", false, "just dump out positions")
	flag.Parse()

	Log = log.New(os.Stdout,"", log.Ldate|log.Ltime)//|log.Lshortfile)
}

func getIoReader() io.Reader {
	if fFilename != "" {
		if osFile, err := os.Open(fFilename); err != nil {
			panic(err)
		} else {
			Log.Printf("reading file '%s' (dumpPos=%v)", fFilename, fDumpPos)
			return osFile
		}
	} else if fHostPort != "" {
		if conn,err := net.Dial("tcp", fHostPort); err != nil {
			panic(err)
		} else {
			Log.Printf("connecting to '%s' (dumpPos=%v)", fHostPort, fDumpPos)
			return conn // a net.Conn implements io.Reader
		}

	} else {
		panic("No inputs defined")
	}
}

var kSweepAfter = 100

func main() {
	scanner := bufio.NewScanner(getIoReader())

	table := tracktable.New()
	table.WaitTime = 300 // If a given transponder goes quiet for 3000, ship the track
	table.StationName = "ScottsValley"
	// ... etc etc

	// Main goroutine: read input, add it to the TrackTable
	i := 1
	for scanner.Scan() {
		m := adsb.Msg{}
		text := scanner.Text()
		if err := m.FromSBS1(text); err != nil {
			Log.Fatal(err)
			continue
		}

		//Log.Printf("   --- %s\n", text)
		table.AddMessage(&m)
		if (i % kSweepAfter) == 0 { table.Sweep() }
		i++

		if err := scanner.Err(); err != nil {
			Log.Fatal(err)
		}
	}
//	table.Sweep()
//	time.Sleep(1 * time.Second)
//	Log.Printf("Processed %d records\nTable:-\n%s", i, table)
}
<commit_msg>More tolerant of dump1090 flakiness<commit_after>package main

// go run client.go -f ~/skypi/sbs1.out
// go run client.go -h skypi:30003

import (
	"bufio"
	//"fmt"
	"flag"
	"io"
	"log"
	"net"
	"os"
	"time"

	"github.com/skypies/adsb"
	"github.com/skypies/pi/tracktable"
)

var Log *log.Logger

var fHostPort string
var fFilename string
var fDumpPos  bool

func init() {
	flag.StringVar(&fHostPort, "h", "", "host:port of dump1090:30003")
	flag.StringVar(&fFilename, "f", "", "sbs formatted CSV file thing to read")
	flag.BoolVar  (&fDumpPos,  "pos", false, "just dump out positions")
	flag.Parse()

	Log = log.New(os.Stdout,"", log.Ldate|log.Ltime)//|log.Lshortfile)
}

func getIoReader() io.Reader {
	if fFilename != "" {
		if osFile, err := os.Open(fFilename); err != nil {
			panic(err)
		} else {
			Log.Printf("reading file '%s' (dumpPos=%v)", fFilename, fDumpPos)
			return osFile
		}
	} else if fHostPort != "" {
		if conn,err := net.Dial("tcp", fHostPort); err != nil {
			panic(err)
		} else {
			Log.Printf("connecting to '%s' (dumpPos=%v)", fHostPort, fDumpPos)
			return conn // a net.Conn implements io.Reader
		}

	} else {
		panic("No inputs defined")
	}
}

var kSweepAfter = 100

func main() {
	table := tracktable.New()
	table.WaitTime = 300 // If a given transponder goes quiet for 3000, ship the track
	table.StationName = "ScottsValley"
	// ... etc etc

	// Main goroutine: read input, add it to the TrackTable
	i := 1
outerLoop:
	for {
		scanner := bufio.NewScanner(getIoReader())
		for scanner.Scan() {
			if err := scanner.Err(); err != nil {
				Log.Printf("scanner err (will retry): %v\n", err)
				time.Sleep(time.Second * 10)
				continue outerLoop
			}

			m := adsb.Msg{}
			text := scanner.Text()
			if err := m.FromSBS1(text); err != nil {
				Log.Printf("SBS parse fail '%v', input:%q", err, text)
				continue
			}

			//Log.Printf("   --- %s\n", text)
			table.AddMessage(&m)
			if (i % kSweepAfter) == 0 { table.Sweep() }
			i++
		}

		Log.Print("Scanner died, starting another in 5s ...")
		time.Sleep(time.Second * 5)
	}
//	table.Sweep()
//	time.Sleep(1 * time.Second)
//	Log.Printf("Processed %d records\nTable:-\n%s", i, table)
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013-2014, Jeremy Bingham (<jbingham@gmail.com>)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"encoding/json"
	"fmt"
	"github.com/ctdk/goas/v2/logger"
	"github.com/ctdk/goiardi/actor"
	"github.com/ctdk/goiardi/shovey"
	"github.com/ctdk/goiardi/util"
	"net/http"
	"strconv"
)

func shoveyHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	opUser, oerr := actor.GetReqUser(r.Header.Get("X-OPS-USERID"))
	if oerr != nil {
		jsonErrorReport(w, r, oerr.Error(), oerr.Status())
		return
	}
	if !opUser.IsAdmin() {
		jsonErrorReport(w, r, "you cannot perform this action", http.StatusForbidden)
		return
	}
	pathArray := splitPath(r.URL.Path)
	pathArrayLen := len(pathArray)

	if pathArrayLen < 2 || pathArrayLen > 3 || pathArray[1] == "" {
		jsonErrorReport(w, r, "Bad request", http.StatusBadRequest)
		return
	}

	shoveyResponse := make(map[string]interface{})

	switch r.Method {
	case "GET":
		if pathArrayLen == 3 {
			shove, err := shovey.Get(pathArray[2])
			if err != nil {
				jsonErrorReport(w, r, err.Error(), err.Status())
				return
			}
			shoveyResponse, err = shove.ToJSON()
			if err != nil {
				jsonErrorReport(w, r, err.Error(), err.Status())
				return
			}
		} else {
			shoveyIDs, err := shovey.AllShoveyIDs()
			if err != nil {
				jsonErrorReport(w, r, err.Error(), err.Status())
				return
			}
			enc := json.NewEncoder(w)
			if jerr := enc.Encode(&shoveyIDs); err != nil {
				jsonErrorReport(w, r, jerr.Error(), http.StatusInternalServerError)
			}
			return
		}
	case "POST":
		if pathArrayLen == 3 {
			jsonErrorReport(w, r, "Bad request", http.StatusBadRequest)
			return
		}
		shvData, err := parseObjJSON(r.Body)
		if err != nil {
			jsonErrorReport(w, r, err.Error(), http.StatusBadRequest)
			return
		}
		logger.Debugf("shvData: %v", shvData)
		var quorum string
		var timeout int
		var ok bool
		if quorum, ok = shvData["quorum"].(string); !ok {
			quorum = "100%"
		}
		if t, ok := shvData["run_timeout"].(string); !ok {
			timeout = 300
		} else {
			timeout, err = strconv.Atoi(t)
			if err != nil {
				jsonErrorReport(w, r, err.Error(), http.StatusBadRequest)
				return
			}
		}
		if len(shvData["nodes"].([]interface{})) == 0 {
			jsonErrorReport(w, r, "no nodes provided", http.StatusBadRequest)
			return
		}
		nodeNames := make([]string, len(shvData["nodes"].([]interface{})))
		for i, v := range shvData["nodes"].([]interface{}) {
			nodeNames[i] = v.(string)
		}

		s, gerr := shovey.New(shvData["command"].(string), timeout, quorum, nodeNames)
		if gerr != nil {
			jsonErrorReport(w, r, gerr.Error(), gerr.Status())
			return
		}
		shoveyResponse["id"] = s.RunID
		shoveyResponse["uri"] = util.CustomURL(fmt.Sprintf("/shovey/jobs/%s", s.RunID))
	default:
		jsonErrorReport(w, r, "Unrecognized method", http.StatusMethodNotAllowed)
		return
	}

	enc := json.NewEncoder(w)
	if jerr := enc.Encode(&shoveyResponse); jerr != nil {
		jsonErrorReport(w, r, jerr.Error(), http.StatusInternalServerError)
	}

	return
}
<commit_msg>Start on a needed PUT for shovey<commit_after>/*
 * Copyright (c) 2013-2014, Jeremy Bingham (<jbingham@gmail.com>)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"encoding/json"
	"fmt"
	"github.com/ctdk/goas/v2/logger"
	"github.com/ctdk/goiardi/actor"
	"github.com/ctdk/goiardi/shovey"
	"github.com/ctdk/goiardi/util"
	"net/http"
	"strconv"
)

func shoveyHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	opUser, oerr := actor.GetReqUser(r.Header.Get("X-OPS-USERID"))
	if oerr != nil {
		jsonErrorReport(w, r, oerr.Error(), oerr.Status())
		return
	}
	if !opUser.IsAdmin() {
		jsonErrorReport(w, r, "you cannot perform this action", http.StatusForbidden)
		return
	}
	pathArray := splitPath(r.URL.Path)
	pathArrayLen := len(pathArray)

	if pathArrayLen < 2 || pathArrayLen > 4 || pathArray[1] == "" {
		jsonErrorReport(w, r, "Bad request", http.StatusBadRequest)
		return
	}

	shoveyResponse := make(map[string]interface{})

	switch r.Method {
	case "GET":
		if pathArrayLen == 3 {
			shove, err := shovey.Get(pathArray[2])
			if err != nil {
				jsonErrorReport(w, r, err.Error(), err.Status())
				return
			}
			shoveyResponse, err = shove.ToJSON()
			if err != nil {
				jsonErrorReport(w, r, err.Error(), err.Status())
				return
			}
		} else {
			shoveyIDs, err := shovey.AllShoveyIDs()
			if err != nil {
				jsonErrorReport(w, r, err.Error(), err.Status())
				return
			}
			enc := json.NewEncoder(w)
			if jerr := enc.Encode(&shoveyIDs); err != nil {
				jsonErrorReport(w, r, jerr.Error(), http.StatusInternalServerError)
			}
			return
		}
	case "POST":
		if pathArrayLen == 3 {
			jsonErrorReport(w, r, "Bad request", http.StatusBadRequest)
			return
		}
		shvData, err := parseObjJSON(r.Body)
		if err != nil {
			jsonErrorReport(w, r, err.Error(), http.StatusBadRequest)
			return
		}
		logger.Debugf("shvData: %v", shvData)
		var quorum string
		var timeout int
		var ok bool
		if quorum, ok = shvData["quorum"].(string); !ok {
			quorum = "100%"
		}
		if t, ok := shvData["run_timeout"].(string); !ok {
			timeout = 300
		} else {
			timeout, err = strconv.Atoi(t)
			if err != nil {
				jsonErrorReport(w, r, err.Error(), http.StatusBadRequest)
				return
			}
		}
		if len(shvData["nodes"].([]interface{})) == 0 {
			jsonErrorReport(w, r, "no nodes provided", http.StatusBadRequest)
			return
		}
		nodeNames := make([]string, len(shvData["nodes"].([]interface{})))
		for i, v := range shvData["nodes"].([]interface{}) {
			nodeNames[i] = v.(string)
		}

		s, gerr := shovey.New(shvData["command"].(string), timeout, quorum, nodeNames)
		if gerr != nil {
			jsonErrorReport(w, r, gerr.Error(), gerr.Status())
			return
		}
		shoveyResponse["id"] = s.RunID
		shoveyResponse["uri"] = util.CustomURL(fmt.Sprintf("/shovey/jobs/%s", s.RunID))
	case "PUT":

	default:
		jsonErrorReport(w, r, "Unrecognized method", http.StatusMethodNotAllowed)
		return
	}

	enc := json.NewEncoder(w)
	if jerr := enc.Encode(&shoveyResponse); jerr != nil {
		jsonErrorReport(w, r, jerr.Error(), http.StatusInternalServerError)
	}

	return
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"html/template"
	"regexp"
	"strings"
	"time"

	"github.com/kennygrant/sanitize"
	"golang.org/x/net/context"
	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/log"
)

type Entry struct {
	Id       int64     `json:"id"`
	Title    string    `json:"title"`                     // optional
	Content  string    `datastore:",noindex" json:"text"` // Markdown
	Datetime time.Time `json:"date"`
	Created  time.Time `json:"created"`
	Modified time.Time `json:"modified"`
	Tags     []string  `json:"tags"`
	Longform string    `json:"-"`
	Public   bool      `json:"-"`
	Draft    bool      `json:"-"`
}

func NewEntry(title string, content string, datetime time.Time, tags []string) *Entry {
	e := new(Entry)

	// User supplied content
	e.Title = title
	e.Content = content
	e.Datetime = datetime
	e.Tags = tags

	// Computer generated content
	e.Created = time.Now()
	e.Modified = time.Now()
	e.Draft = false

	return e
}

func ParseTags(text string) ([]string, error) {
	// http://golang.org/pkg/regexp/#Regexp.FindAllStringSubmatch
	finds := HashtagRegex.FindAllStringSubmatch(text, -1)
	ret := make([]string, 0)
	for _, v := range finds {
		if len(v) > 2 {
			ret = append(ret, strings.ToLower(v[2]))
		}
	}

	return ret, nil
}

func GetEntry(c context.Context, id int64) (*Entry, error) {
	var entry Entry
	q := datastore.NewQuery("Entry").Filter("Id =", id).Limit(1)
	_, err := q.Run(c).Next(&entry)
	if err != nil {
		return nil, err
	}

	return &entry, nil
}

func GetLongform(c context.Context, longform string) (*Entry, error) {
	var entry Entry
	q := datastore.NewQuery("Entry").Filter("Longform =", longform).Limit(1)
	_, err := q.Run(c).Next(&entry)
	if err != nil {
		return nil, err
	}

	log.Infof(c, "Attempted to find %v. %+v %+v", longform, entry, err)

	return &entry, nil
}

func MaxId(c context.Context) (int64, error) {
	var entry Entry
	q := datastore.NewQuery("Entry").Order("-Id").Limit(1)
	_, err := q.Run(c).Next(&entry)
	if err != nil {
		return 0, err
	}
	return entry.Id, nil
}

func AllPosts(c context.Context) (*[]Entry, error) {
	return Posts(c, -1, true)
}

func Pagination(c context.Context, posts, offset int) (*[]Entry, error) {
	q := datastore.NewQuery("Entry").
		Filter("Draft =", false).
		Order("-Datetime").
		Limit(posts).
		Offset(offset)

	entries := new([]Entry)
	_, err := q.GetAll(c, entries)
	return entries, err
}

func ArchivePageQuery() *datastore.Query {
	return datastore.NewQuery("Entry").
		Filter("Draft =", false).
		Project("Id", "Datetime").
		Order("-Datetime").
		Limit(50)
}

func Posts(c context.Context, limit int, recentFirst bool) (*[]Entry, error) {
	q := datastore.NewQuery("Entry").Filter("Draft =", false)

	if recentFirst {
		q = q.Order("-Datetime")
	} else {
		q = q.Order("Datetime")
	}

	if limit > 0 {
		q = q.Limit(limit)
	}

	entries := new([]Entry)
	_, err := q.GetAll(c, entries)
	return entries, err
}

func Drafts(c context.Context) (*[]Entry, error) {
	q := datastore.NewQuery("Entry").Filter("Draft =", true).Order("-Datetime")
	entries := new([]Entry)
	_, err := q.GetAll(c, entries)
	return entries, err
}

func LongformPosts(c context.Context) (*[]Entry, error) {
	q := datastore.NewQuery("Entry").Filter("Longform >", "").Order("-Longform")
	entries := new([]Entry)
	_, err := q.GetAll(c, entries)
	return entries, err
}

func RecentPosts(c context.Context) (*[]Entry, error) {
	return Posts(c, 20, true)
}

func (e *Entry) HasId() bool {
	return (e.Id > 0)
}

func (e *Entry) Save(c context.Context) error {
	var k *datastore.Key
	if !e.HasId() {
		id, _ := MaxId(c)
		e.Id = id + 1
		k = datastore.NewIncompleteKey(c, "Entry", nil)
	} else {
		// Find the key
		var err error
		q := datastore.NewQuery("Entry").Filter("Id =", e.Id).Limit(1).KeysOnly()
		k, err = q.Run(c).Next(nil)
		if err != nil {
			return err
		}
	}

	cnt, err := datastore.NewQuery("Entry").Filter("Id =", e.Id).Count(c)
	if err != nil {
		return err
	}
	log.Infof(c, "ID: %v: %v", e.Id, cnt)
	if cnt >= 2 {
		id, _ := MaxId(c)
		e.Id = id + 1
	}

	// Pull out links
	// TODO: Do something with the output
	GetLinksFromContent(c, e.Content)

	// Figure out Tags
	tags, err := ParseTags(e.Content)
	if err != nil {
		return err
	}
	e.Tags = tags

	k2, err := datastore.Put(c, k, e)
	if err == nil {
		// log.Infof(c, "Wrote %+v", e)
		log.Infof(c, "Old key: %+v; New Key: %+v", k, k2)
	} else {
		log.Warningf(c, "Error writing entry: %v", e)
	}

	return err
}

func (e *Entry) Url() string {
	return fmt.Sprintf("/post/%d", e.Id)
}

func (e *Entry) EditUrl() string {
	return fmt.Sprintf("/edit/%d", e.Id)
}

func (e *Entry) Html() template.HTML {
	return Markdown(e.Content)
}

func (e *Entry) Summary() string {
	// truncate(strip_tags(m(p.text)), :length => 100).strip
	stripped := sanitize.HTML(string(e.Html()))
	if len(stripped) > 100 {
		return fmt.Sprintf("%s...", stripped[:100])
	} else {
		return stripped
	}
}

func (e *Entry) PrevPost(c context.Context) string {
	var entry Entry
	q := datastore.NewQuery("Entry").Order("-Datetime").Filter("Datetime <", e.Datetime).Limit(1)
	_, err := q.Run(c).Next(&entry)
	if err != nil {
		log.Infof(c, "Error getting previous post for %d.", e.Id)
		return ""
	}

	return entry.Url()
}

func (e *Entry) NextPost(c context.Context) string {
	var entry Entry
	q := datastore.NewQuery("Entry").Order("Datetime").Filter("Datetime >", e.Datetime).Limit(1)
	_, err := q.Run(c).Next(&entry)
	if err != nil {
		log.Infof(c, "Error getting next post for %d.", e.Id)
		return ""
	}

	return entry.Url()
}

func GetLinksFromContent(c context.Context, content string) ([]string, error) {
	httpRegex := regexp.MustCompile(`https?:\/\/((\w|\.)+)`)
	matches := httpRegex.FindAllString(content, -1)
	if matches == nil {
		return []string{}, nil
	}

	log.Infof(c, "URLs Found: %+v", matches)

	return matches, nil
}

func PostsForDay(c context.Context, year, month, day int64) (*[]Entry, error) {
	entries := new([]Entry)
	start := time.Date(int(year), time.Month(month), int(day), 0, 0, 0, 0, time.UTC)
	end := start.AddDate(0, 0, 1)
	q := datastore.NewQuery("Entry").Order("-Datetime").Filter("Datetime >=", start).Filter("Datetime <", end).Filter("Draft =", false)
	_, err := q.GetAll(c, entries)
	return entries, err
}

func PostsWithTag(c context.Context, tag string) (*map[int64]Entry, error) {
	entries := make(map[int64]Entry, 0)
	aliases := GetTagAliases(c, tag)
	aliasesAndTag := append(*aliases, tag)

	for _, v := range aliasesAndTag {
		more_entries := new([]Entry)
		q := datastore.NewQuery("Entry").Order("-Datetime").Filter("Tags =", v)
		_, err := q.GetAll(c, more_entries)
		if err != nil {
			return &entries, err
		}
		for _, e := range *more_entries {
			entries[e.Id] = e
		}
	}

	return &entries, nil
}
<commit_msg>add delete function<commit_after>package main

import (
	"fmt"
	"html/template"
	"regexp"
	"strings"
	"time"

	"github.com/kennygrant/sanitize"
	"golang.org/x/net/context"
	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/log"
)

type Entry struct {
	Id       int64     `json:"id"`
	Title    string    `json:"title"`                     // optional
	Content  string    `datastore:",noindex" json:"text"` // Markdown
	Datetime time.Time `json:"date"`
	Created  time.Time `json:"created"`
	Modified time.Time `json:"modified"`
	Tags     []string  `json:"tags"`
	Longform string    `json:"-"`
	Public   bool      `json:"-"`
	Draft    bool      `json:"-"`
}

func NewEntry(title string, content string, datetime time.Time, tags []string) *Entry {
	e := new(Entry)

	// User supplied content
	e.Title = title
	e.Content = content
	e.Datetime = datetime
	e.Tags = tags

	// Computer generated content
	e.Created = time.Now()
	e.Modified = time.Now()
	e.Draft = false

	return e
}

func ParseTags(text string) ([]string, error) {
	// http://golang.org/pkg/regexp/#Regexp.FindAllStringSubmatch
	finds := HashtagRegex.FindAllStringSubmatch(text, -1)
	ret := make([]string, 0)
	for _, v := range finds {
		if len(v) > 2 {
			ret = append(ret, strings.ToLower(v[2]))
		}
	}

	return ret, nil
}

func GetEntry(c context.Context, id int64) (*Entry, error) {
	var entry Entry
	q := datastore.NewQuery("Entry").Filter("Id =", id).Limit(1)
	_, err := q.Run(c).Next(&entry)
	if err != nil {
		return nil, err
	}

	return &entry, nil
}

func GetLongform(c context.Context, longform string) (*Entry, error) {
	var entry Entry
	q := datastore.NewQuery("Entry").Filter("Longform =", longform).Limit(1)
	_, err := q.Run(c).Next(&entry)
	if err != nil {
		return nil, err
	}

	log.Infof(c, "Attempted to find %v. %+v %+v", longform, entry, err)

	return &entry, nil
}

func MaxId(c context.Context) (int64, error) {
	var entry Entry
	q := datastore.NewQuery("Entry").Order("-Id").Limit(1)
	_, err := q.Run(c).Next(&entry)
	if err != nil {
		return 0, err
	}
	return entry.Id, nil
}

func AllPosts(c context.Context) (*[]Entry, error) {
	return Posts(c, -1, true)
}

func Pagination(c context.Context, posts, offset int) (*[]Entry, error) {
	q := datastore.NewQuery("Entry").
		Filter("Draft =", false).
		Order("-Datetime").
		Limit(posts).
		Offset(offset)

	entries := new([]Entry)
	_, err := q.GetAll(c, entries)
	return entries, err
}

func ArchivePageQuery() *datastore.Query {
	return datastore.NewQuery("Entry").
		Filter("Draft =", false).
		Project("Id", "Datetime").
		Order("-Datetime").
		Limit(50)
}

func Posts(c context.Context, limit int, recentFirst bool) (*[]Entry, error) {
	q := datastore.NewQuery("Entry").Filter("Draft =", false)

	if recentFirst {
		q = q.Order("-Datetime")
	} else {
		q = q.Order("Datetime")
	}

	if limit > 0 {
		q = q.Limit(limit)
	}

	entries := new([]Entry)
	_, err := q.GetAll(c, entries)
	return entries, err
}

func Drafts(c context.Context) (*[]Entry, error) {
	q := datastore.NewQuery("Entry").Filter("Draft =", true).Order("-Datetime")
	entries := new([]Entry)
	_, err := q.GetAll(c, entries)
	return entries, err
}

func LongformPosts(c context.Context) (*[]Entry, error) {
	q := datastore.NewQuery("Entry").Filter("Longform >", "").Order("-Longform")
	entries := new([]Entry)
	_, err := q.GetAll(c, entries)
	return entries, err
}

func RecentPosts(c context.Context) (*[]Entry, error) {
	return Posts(c, 20, true)
}

func (e *Entry) HasId() bool {
	return (e.Id > 0)
}

func (e *Entry) Delete(c context.Context) error {
	// Find the key
	var err error
	q := datastore.NewQuery("Entry").Filter("Id =", e.Id).Limit(1).KeysOnly()
	k, err = q.Run(c).Next(nil)
	if err != nil {
		return err
	}

	return datastore.Delete(c, k)
}

func (e *Entry) Save(c context.Context) error {
	var k *datastore.Key
	if !e.HasId() {
		id, _ := MaxId(c)
		e.Id = id + 1
		k = datastore.NewIncompleteKey(c, "Entry", nil)
	} else {
		// Find the key
		var err error
		q := datastore.NewQuery("Entry").Filter("Id =", e.Id).Limit(1).KeysOnly()
		k, err = q.Run(c).Next(nil)
		if err != nil {
			return err
		}
	}

	cnt, err := datastore.NewQuery("Entry").Filter("Id =", e.Id).Count(c)
	if err != nil {
		return err
	}
	log.Infof(c, "ID: %v: %v", e.Id, cnt)
	if cnt >= 2 {
		id, _ := MaxId(c)
		e.Id = id + 1
	}

	// Pull out links
	// TODO: Do something with the output
	GetLinksFromContent(c, e.Content)

	// Figure out Tags
	tags, err := ParseTags(e.Content)
	if err != nil {
		return err
	}
	e.Tags = tags

	k2, err := datastore.Put(c, k, e)
	if err == nil {
		// log.Infof(c, "Wrote %+v", e)
		log.Infof(c, "Old key: %+v; New Key: %+v", k, k2)
	} else {
		log.Warningf(c, "Error writing entry: %v", e)
	}

	return err
}

func (e *Entry) Url() string {
	return fmt.Sprintf("/post/%d", e.Id)
}

func (e *Entry) EditUrl() string {
	return fmt.Sprintf("/edit/%d", e.Id)
}

func (e *Entry) Html() template.HTML {
	return Markdown(e.Content)
}

func (e *Entry) Summary() string {
	// truncate(strip_tags(m(p.text)), :length => 100).strip
	stripped := sanitize.HTML(string(e.Html()))
	if len(stripped) > 100 {
		return fmt.Sprintf("%s...", stripped[:100])
	} else {
		return stripped
	}
}

func (e *Entry) PrevPost(c context.Context) string {
	var entry Entry
	q := datastore.NewQuery("Entry").Order("-Datetime").Filter("Datetime <", e.Datetime).Limit(1)
	_, err := q.Run(c).Next(&entry)
	if err != nil {
		log.Infof(c, "Error getting previous post for %d.", e.Id)
		return ""
	}

	return entry.Url()
}

func (e *Entry) NextPost(c context.Context) string {
	var entry Entry
	q := datastore.NewQuery("Entry").Order("Datetime").Filter("Datetime >", e.Datetime).Limit(1)
	_, err := q.Run(c).Next(&entry)
	if err != nil {
		log.Infof(c, "Error getting next post for %d.", e.Id)
		return ""
	}

	return entry.Url()
}

func GetLinksFromContent(c context.Context, content string) ([]string, error) {
	httpRegex := regexp.MustCompile(`https?:\/\/((\w|\.)+)`)
	matches := httpRegex.FindAllString(content, -1)
	if matches == nil {
		return []string{}, nil
	}

	log.Infof(c, "URLs Found: %+v", matches)

	return matches, nil
}

func PostsForDay(c context.Context, year, month, day int64) (*[]Entry, error) {
	entries := new([]Entry)
	start := time.Date(int(year), time.Month(month), int(day), 0, 0, 0, 0, time.UTC)
	end := start.AddDate(0, 0, 1)
	q := datastore.NewQuery("Entry").Order("-Datetime").Filter("Datetime >=", start).Filter("Datetime <", end).Filter("Draft =", false)
	_, err := q.GetAll(c, entries)
	return entries, err
}

func PostsWithTag(c context.Context, tag string) (*map[int64]Entry, error) {
	entries := make(map[int64]Entry, 0)
	aliases := GetTagAliases(c, tag)
	aliasesAndTag := append(*aliases, tag)

	for _, v := range aliasesAndTag {
		more_entries := new([]Entry)
		q := datastore.NewQuery("Entry").Order("-Datetime").Filter("Tags =", v)
		_, err := q.GetAll(c, more_entries)
		if err != nil {
			return &entries, err
		}
		for _, e := range *more_entries {
			entries[e.Id] = e
		}
	}

	return &entries, nil
}
<|endoftext|>


<commit_before>package main

import . "github.com/strickyak/basic_basic"
import "github.com/strickyak/basic_basic/draw"

import (
	"flag"
	. "fmt"
	//"io/ioutil"
	"log"
	"net/http"
	//"os"
)

func main() {
	flag.BoolVar(&Debug, "d", false, "debug bit")
	flag.Parse()

	http.HandleFunc("/", Render)
	err := http.ListenAndServe(":12345", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

func Render(w http.ResponseWriter, req *http.Request) {
	defer func() {
		r := recover()
		if r != nil {
			w.Header().Set("Content-Type", "text/plain")
			Fprintf(w, "%v", r)
		}
	}()

	var code string
	req.ParseForm()
	if x, ok := req.Form["code"]; ok {
		code = x[0]

		terp := NewTerp(code)
		terp.SetExpiration("30s")
		d := draw.Register(terp)
		terp.Run()
		Printf("\n")
		w.Header().Set("Content-Type", "image/png")
		if d.HasImage() {
			d.WritePng(w)
		}
	} else {
		Fprintf(w, `

        <form method="GET" action="/">
          <textarea name=code cols=80 rows=25>
1  REM Draw big grey triangle, then many smaller colored ones.
5  CALL triangle( 0,0, 0,99, 99,0, 444 )
10 for i = 0 to 9
20   for j = 0 to 9
30     for k = 0 to 9
40       let kk = 9 - k
44       call triangle (i*10,k+j*10,  9+i*10,j*10,  9+i*10,9+j*10, i+j*10+kk*100)
70     next k
80   next j
90 next i
          </textarea>
          <input type=submit name=submit value=Submit>
        </form>

      `)
	}
}
<commit_msg>Explanatory text for the web page.<commit_after>package main

import . "github.com/strickyak/basic_basic"
import "github.com/strickyak/basic_basic/draw"

import (
	"flag"
	. "fmt"
	//"io/ioutil"
	"log"
	"net/http"
	//"os"
)

func main() {
	flag.BoolVar(&Debug, "d", false, "debug bit")
	flag.Parse()

	http.HandleFunc("/", Render)
	err := http.ListenAndServe(":12345", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

func Render(w http.ResponseWriter, req *http.Request) {
	defer func() {
		r := recover()
		if r != nil {
			w.Header().Set("Content-Type", "text/plain")
			Fprintf(w, "%v", r)
		}
	}()

	var code string
	req.ParseForm()
	if x, ok := req.Form["code"]; ok {
		code = x[0]

		terp := NewTerp(code)
		terp.SetExpiration("30s")
		d := draw.Register(terp)
		terp.Run()
		Printf("\n")
		w.Header().Set("Content-Type", "image/png")
		if d.HasImage() {
			d.WritePng(w)
		}
	} else {
		Fprintf(w, `

        <form method="GET" action="/">
          <textarea name=code cols=80 rows=25>
1  REM Draw big grey triangle, then many smaller colored ones.
5  CALL triangle( 0,0, 0,99, 99,0, 444 )
10 for i = 0 to 9
20   for j = 0 to 9
30     for k = 0 to 9
40       let kk = 9 - k
44       call triangle (i*10,k+j*10,  9+i*10,j*10,  9+i*10,9+j*10, i+j*10+kk*100)
70     next k
80   next j
90 next i
          </textarea>
          <input type=submit name=submit value=Submit>
        </form>

This is a simple BASIC computer.

The only data type is floating point numbers.

THe only output is the "CALL triangle" statement,
which draws colored triangles on a canvas with
coordinates [0 .. 100) on both x and y axes.

Statement ::= LineNumber Stmt
Stmt := REM remark...
  | LET var := expr
  | GOTO n
  | IF expr THEN y
  | IF expr THEN y ELSE n
  | FOR var = a TO b
  | NEXT var
  | GOSUB n
  | RETURN
  | CALL triangle( x1, y1, x2, y2, x3, y3, rgb )
       ... where n & y are line numbers
       ... where rgb is decimal (r=hundreds, g=tens, b=ones)
expr ::= sum relop expr     ...where relop can be == != < > <= >=
sum ::= prod addop sum      ...where addop can be + -
prod ::= prim mulop prod    ...where mulop can be * / %%
prim ::= number
  | var
  | ( expr )

      `)
	}
}
<|endoftext|>


<commit_before>package model

import (
	"github.com/guregu/null"
)

// Subscription represents user's notification settings.
type Subscription struct {
	ID          int      `json:"id"`
	UserID      null.Int `json:"user_id"`
	ProjectUUID string   `json:"project_uuid"`

	Email     string `json:"email"`
	DeletedAt Time   `json:"deleted_at"`
}
<commit_msg>Set foreign key information to Subscription<commit_after>package model

import (
	"github.com/guregu/null"
)

// Subscription represents user's notification settings.
type Subscription struct {
	ID          int      `json:"id" gorm:"primary_key"`
	UserID      null.Int `json:"user_id" gorm:"ForeignKey:ID"`
	ProjectUUID string   `json:"project_uuid" gorm:"ForeignKey:UUID"`

	Email     string `json:"email"`
	DeletedAt Time   `json:"deleted_at"`
}
<|endoftext|>


<commit_before>package parser

import (
	"testing"

	"github.com/hansrodtang/semver"
)

type test struct {
	expected bool
	version  *semver.Version
}

var parsables = map[string][]test{
	"1.2.7 || >=1.2.9 <2.0.0": {
		{true, semver.Build(1, 2, 7)},
		{true, semver.Build(1, 2, 9)},
		{true, semver.Build(1, 4, 6)},
		{false, semver.Build(1, 2, 8)},
		{false, semver.Build(2, 0, 0)},
	},
}

var parsable = "1.2.7 || >=1.2.9 <2.0.0"

var matches = []*semver.Version{
	semver.Build(1, 2, 7),
	semver.Build(1, 2, 9),
	semver.Build(1, 4, 6),
}

var mismatches = []*semver.Version{
	semver.Build(1, 2, 8),
	semver.Build(2, 0, 0),
}

func TestParse(t *testing.T) {

	for k, v := range parsables {
		n, _ := Parse(k)
		for _, x := range v {
			if response := n.Run(x.version); response != x.expected {
				t.Errorf("%q.Run(%q) => %t, want %t", k, x.version, response, x.expected)
			}
		}

	}
}
<commit_msg>Removed old matches array.<commit_after>package parser

import (
	"testing"

	"github.com/hansrodtang/semver"
)

type test struct {
	expected bool
	version  *semver.Version
}

var parsables = map[string][]test{
	"1.2.7 || >=1.2.9 <2.0.0": {
		{true, semver.Build(1, 2, 7)},
		{true, semver.Build(1, 2, 9)},
		{true, semver.Build(1, 4, 6)},
		{false, semver.Build(1, 2, 8)},
		{false, semver.Build(2, 0, 0)},
	},
}

func TestParser(t *testing.T) {

	for k, v := range parsables {
		n, _ := Parse(k)
		for _, x := range v {
			if response := n.Run(x.version); response != x.expected {
				t.Errorf("%q.Run(%q) => %t, want %t", k, x.version, response, x.expected)
			}
		}

	}
}
<|endoftext|>


<commit_before>package gitlab

import (
	"fmt"
	"time"
)

// EpicsService handles communication with the epic related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html
type EpicsService struct {
	client *Client
}

// EpicAuthor represents a author of the epic.
type EpicAuthor struct {
	ID        int    `json:"id"`
	State     string `json:"state"`
	WebURL    string `json:"web_url"`
	Name      string `json:"name"`
	AvatarURL string `json:"avatar_url"`
	Username  string `json:"username"`
}

// Epic represents a GitLab epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html
type Epic struct {
	ID                      int         `json:"id"`
	IID                     int         `json:"iid"`
	GroupID                 int         `json:"group_id"`
	Author                  *EpicAuthor `json:"author"`
	Description             string      `json:"description"`
	State                   string      `json:"state"`
	Upvotes                 int         `json:"upvotes"`
	Downvotes               int         `json:"downvotes"`
	Labels                  []string    `json:"labels"`
	Title                   string      `json:"title"`
	UpdatedAt               *time.Time  `json:"updated_at"`
	CreatedAt               *time.Time  `json:"created_at"`
	UserNotesCount          int         `json:"user_notes_count"`
	StartDate               *ISOTime    `json:"start_date"`
	StartDateIsFixed        bool        `json:"start_date_is_fixed"`
	StartDateFixed          *ISOTime    `json:"start_date_fixed"`
	StartDateFromMilestones *ISOTime    `json:"start_date_from_milestones"`
	DueDate                 *ISOTime    `json:"due_date"`
	DueDateIsFixed          bool        `json:"due_date_is_fixed"`
	DueDateFixed            *ISOTime    `json:"due_date_fixed"`
	DueDateFromMilestones   *ISOTime    `json:"due_date_from_milestones"`
}

func (e Epic) String() string {
	return Stringify(e)
}

// ListGroupEpicsOptions represents the available ListGroupEpics() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#list-epics-for-a-group
type ListGroupEpicsOptions struct {
	ListOptions
	State    *string `url:"state,omitempty" json:"state,omitempty"`
	Labels   Labels  `url:"labels,comma,omitempty" json:"labels,omitempty"`
	AuthorID *int    `url:"author_id,omitempty" json:"author_id,omitempty"`
	OrderBy  *string `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort     *string `url:"sort,omitempty" json:"sort,omitempty"`
	Search   *string `url:"search,omitempty" json:"search,omitempty"`
}

// ListGroupEpics gets a list of group epics. This function accepts pagination
// parameters page and per_page to return the list of group epics.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#list-epics-for-a-group
func (s *EpicsService) ListGroupEpics(gid interface{}, opt *ListGroupEpicsOptions, options ...RequestOptionFunc) ([]*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics", pathEscape(group))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var es []*Epic
	resp, err := s.client.Do(req, &es)
	if err != nil {
		return nil, resp, err
	}

	return es, resp, err
}

// GetEpic gets a single group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#single-epic
func (s *EpicsService) GetEpic(gid interface{}, epic int, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", pathEscape(group), epic)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// CreateEpicOptions represents the available CreateEpic() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#new-epic
type CreateEpicOptions struct {
	Title            *string  `url:"title,omitempty" json:"title,omitempty"`
	Description      *string  `url:"description,omitempty" json:"description,omitempty"`
	Labels           Labels   `url:"labels,comma,omitempty" json:"labels,omitempty"`
	StartDateIsFixed *bool    `url:"start_date_is_fixed,omitempty" json:"start_date_is_fixed,omitempty"`
	StartDateFixed   *ISOTime `url:"start_date_fixed,omitempty" json:"start_date_fixed,omitempty"`
	DueDateIsFixed   *bool    `url:"due_date_is_fixed,omitempty" json:"due_date_is_fixed,omitempty"`
	DueDateFixed     *ISOTime `url:"due_date_fixed,omitempty" json:"due_date_fixed,omitempty"`
}

// CreateEpic creates a new group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#new-epic
func (s *EpicsService) CreateEpic(gid interface{}, opt *CreateEpicOptions, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics", pathEscape(group))

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// UpdateEpicOptions represents the available UpdateEpic() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#update-epic
type UpdateEpicOptions struct {
	Title            *string  `url:"title,omitempty" json:"title,omitempty"`
	Description      *string  `url:"description,omitempty" json:"description,omitempty"`
	Labels           Labels   `url:"labels,comma,omitempty" json:"labels,omitempty"`
	StartDateIsFixed *bool    `url:"start_date_is_fixed,omitempty" json:"start_date_is_fixed,omitempty"`
	StartDateFixed   *ISOTime `url:"start_date_fixed,omitempty" json:"start_date_fixed,omitempty"`
	DueDateIsFixed   *bool    `url:"due_date_is_fixed,omitempty" json:"due_date_is_fixed,omitempty"`
	DueDateFixed     *ISOTime `url:"due_date_fixed,omitempty" json:"due_date_fixed,omitempty"`
	StateEvent       *string  `url:"state_event,omitempty" json:"state_event,omitempty"`
}

// UpdateEpic updates an existing group epic. This function is also used
// to mark an epic as closed.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#update-epic
func (s *EpicsService) UpdateEpic(gid interface{}, epic int, opt *UpdateEpicOptions, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", pathEscape(group), epic)

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// DeleteEpic deletes a single group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#delete-epic
func (s *EpicsService) DeleteEpic(gid interface{}, epic int, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", pathEscape(group), epic)

	req, err := s.client.NewRequest("DELETE", u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
<commit_msg>Update epics.go<commit_after>package gitlab

import (
	"fmt"
	"time"
)

// EpicsService handles communication with the epic related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html
type EpicsService struct {
	client *Client
}

// EpicAuthor represents a author of the epic.
type EpicAuthor struct {
	ID        int    `json:"id"`
	State     string `json:"state"`
	WebURL    string `json:"web_url"`
	Name      string `json:"name"`
	AvatarURL string `json:"avatar_url"`
	Username  string `json:"username"`
}

// Epic represents a GitLab epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html
type Epic struct {
	ID                      int         `json:"id"`
	IID                     int         `json:"iid"`
	GroupID                 int         `json:"group_id"`
	Author                  *EpicAuthor `json:"author"`
	Description             string      `json:"description"`
	State                   string      `json:"state"`
	Upvotes                 int         `json:"upvotes"`
	Downvotes               int         `json:"downvotes"`
	Labels                  []string    `json:"labels"`
	Title                   string      `json:"title"`
	UpdatedAt               *time.Time  `json:"updated_at"`
	CreatedAt               *time.Time  `json:"created_at"`
	UserNotesCount          int         `json:"user_notes_count"`
	StartDate               *ISOTime    `json:"start_date"`
	StartDateIsFixed        bool        `json:"start_date_is_fixed"`
	StartDateFixed          *ISOTime    `json:"start_date_fixed"`
	StartDateFromMilestones *ISOTime    `json:"start_date_from_milestones"`
	DueDate                 *ISOTime    `json:"due_date"`
	DueDateIsFixed          bool        `json:"due_date_is_fixed"`
	DueDateFixed            *ISOTime    `json:"due_date_fixed"`
	DueDateFromMilestones   *ISOTime    `json:"due_date_from_milestones"`
}

func (e Epic) String() string {
	return Stringify(e)
}

// ListGroupEpicsOptions represents the available ListGroupEpics() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#list-epics-for-a-group
type ListGroupEpicsOptions struct {
	ListOptions
	State            *string    `url:"state,omitempty" json:"state,omitempty"`
	Labels           Labels     `url:"labels,comma,omitempty" json:"labels,omitempty"`
	WithLabelDetails *bool      `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"`
	Milestone        *string    `url:"milestone,omitempty" json:"milestone,omitempty"`
	Scope            *string    `url:"scope,omitempty" json:"scope,omitempty"`
	AuthorID         *int       `url:"author_id,omitempty" json:"author_id,omitempty"`
	AssigneeID       *int       `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
	MyReactionEmoji  *string    `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
	IIDs             []int      `url:"iids[],omitempty" json:"iids,omitempty"`
	OrderBy          *string    `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort             *string    `url:"sort,omitempty" json:"sort,omitempty"`
	Search           *string    `url:"search,omitempty" json:"search,omitempty"`
	CreatedAfter     *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
	CreatedBefore    *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
	UpdatedAfter     *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
	UpdatedBefore    *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
	Confidential     *bool      `url:"confidential,omitempty" json:"confidential,omitempty"`
}

// ListGroupEpics gets a list of group epics. This function accepts pagination
// parameters page and per_page to return the list of group epics.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#list-epics-for-a-group
func (s *EpicsService) ListGroupEpics(gid interface{}, opt *ListGroupEpicsOptions, options ...RequestOptionFunc) ([]*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics", pathEscape(group))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var es []*Epic
	resp, err := s.client.Do(req, &es)
	if err != nil {
		return nil, resp, err
	}

	return es, resp, err
}

// GetEpic gets a single group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#single-epic
func (s *EpicsService) GetEpic(gid interface{}, epic int, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", pathEscape(group), epic)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// CreateEpicOptions represents the available CreateEpic() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#new-epic
type CreateEpicOptions struct {
	Title            *string  `url:"title,omitempty" json:"title,omitempty"`
	Description      *string  `url:"description,omitempty" json:"description,omitempty"`
	Labels           Labels   `url:"labels,comma,omitempty" json:"labels,omitempty"`
	StartDateIsFixed *bool    `url:"start_date_is_fixed,omitempty" json:"start_date_is_fixed,omitempty"`
	StartDateFixed   *ISOTime `url:"start_date_fixed,omitempty" json:"start_date_fixed,omitempty"`
	DueDateIsFixed   *bool    `url:"due_date_is_fixed,omitempty" json:"due_date_is_fixed,omitempty"`
	DueDateFixed     *ISOTime `url:"due_date_fixed,omitempty" json:"due_date_fixed,omitempty"`
}

// CreateEpic creates a new group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#new-epic
func (s *EpicsService) CreateEpic(gid interface{}, opt *CreateEpicOptions, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics", pathEscape(group))

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// UpdateEpicOptions represents the available UpdateEpic() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#update-epic
type UpdateEpicOptions struct {
	Title            *string  `url:"title,omitempty" json:"title,omitempty"`
	Description      *string  `url:"description,omitempty" json:"description,omitempty"`
	Labels           Labels   `url:"labels,comma,omitempty" json:"labels,omitempty"`
	StartDateIsFixed *bool    `url:"start_date_is_fixed,omitempty" json:"start_date_is_fixed,omitempty"`
	StartDateFixed   *ISOTime `url:"start_date_fixed,omitempty" json:"start_date_fixed,omitempty"`
	DueDateIsFixed   *bool    `url:"due_date_is_fixed,omitempty" json:"due_date_is_fixed,omitempty"`
	DueDateFixed     *ISOTime `url:"due_date_fixed,omitempty" json:"due_date_fixed,omitempty"`
	StateEvent       *string  `url:"state_event,omitempty" json:"state_event,omitempty"`
}

// UpdateEpic updates an existing group epic. This function is also used
// to mark an epic as closed.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#update-epic
func (s *EpicsService) UpdateEpic(gid interface{}, epic int, opt *UpdateEpicOptions, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", pathEscape(group), epic)

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// DeleteEpic deletes a single group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#delete-epic
func (s *EpicsService) DeleteEpic(gid interface{}, epic int, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", pathEscape(group), epic)

	req, err := s.client.NewRequest("DELETE", u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
<|endoftext|>


<commit_before>// Copyright 2009 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.

// A package of simple functions to manipulate strings.
package strings

import (
	"unicode"
	"utf8"
)

// explode splits s into an array of UTF-8 sequences, one per Unicode character (still strings) up to a maximum of n (n <= 0 means no limit).
// Invalid UTF-8 sequences become correct encodings of U+FFF8.
func explode(s string, n int) []string {
	l := utf8.RuneCountInString(s)
	if n <= 0 || n > l {
		n = l
	}
	a := make([]string, n)
	var size, rune int
	i, cur := 0, 0
	for ; i+1 < n; i++ {
		rune, size = utf8.DecodeRuneInString(s[cur:])
		a[i] = string(rune)
		cur += size
	}
	// add the rest
	a[i] = s[cur:]
	return a
}

// Count counts the number of non-overlapping instances of sep in s.
func Count(s, sep string) int {
	if sep == "" {
		return utf8.RuneCountInString(s) + 1
	}
	c := sep[0]
	l := len(sep)
	n := 0
	if l == 1 {
		// special case worth making fast
		for i := 0; i < len(s); i++ {
			if s[i] == c {
				n++
			}
		}
		return n
	}
	for i := 0; i+l <= len(s); i++ {
		if s[i] == c && s[i:i+l] == sep {
			n++
			i += l - 1
		}
	}
	return n
}

// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
func Index(s, sep string) int {
	n := len(sep)
	if n == 0 {
		return 0
	}
	c := sep[0]
	if n == 1 {
		// special case worth making fast
		for i := 0; i < len(s); i++ {
			if s[i] == c {
				return i
			}
		}
		return -1
	}
	// n > 1
	for i := 0; i+n <= len(s); i++ {
		if s[i] == c && s[i:i+n] == sep {
			return i
		}
	}
	return -1
}

// LastIndex returns the index of the last instance of sep in s, or -1 if sep is not present in s.
func LastIndex(s, sep string) int {
	n := len(sep)
	if n == 0 {
		return len(s)
	}
	c := sep[0]
	if n == 1 {
		// special case worth making fast
		for i := len(s) - 1; i >= 0; i-- {
			if s[i] == c {
				return i
			}
		}
		return -1
	}
	// n > 1
	for i := len(s) - n; i >= 0; i-- {
		if s[i] == c && s[i:i+n] == sep {
			return i
		}
	}
	return -1
}

// IndexRune returns the index of the first instance of the Unicode code point
// rune, or -1 if rune is not present in s.
func IndexRune(s string, rune int) int {
	for i, c := range s {
		if c == rune {
			return i
		}
	}
	return -1
}

// IndexAny returns the index of the first instance of any Unicode code point
// from chars in s, or -1 if no Unicode code point from chars is present in s.
func IndexAny(s, chars string) int {
	if len(chars) > 0 {
		for i, c := range s {
			for _, m := range chars {
				if c == m {
					return i
				}
			}
		}
	}
	return -1
}

// Generic split: splits after each instance of sep,
// including sepSave bytes of sep in the subarrays.
func genSplit(s, sep string, sepSave, n int) []string {
	if sep == "" {
		return explode(s, n)
	}
	if n <= 0 {
		n = Count(s, sep) + 1
	}
	c := sep[0]
	start := 0
	a := make([]string, n)
	na := 0
	for i := 0; i+len(sep) <= len(s) && na+1 < n; i++ {
		if s[i] == c && (len(sep) == 1 || s[i:i+len(sep)] == sep) {
			a[na] = s[start : i+sepSave]
			na++
			start = i + len(sep)
			i += len(sep) - 1
		}
	}
	a[na] = s[start:]
	return a[0 : na+1]
}

// Split splits the string s around each instance of sep, returning an array of substrings of s.
// If sep is empty, Split splits s after each UTF-8 sequence.
// If n > 0, Split splits s into at most n substrings; the last substring will be the unsplit remainder.
func Split(s, sep string, n int) []string { return genSplit(s, sep, 0, n) }

// SplitAfter splits the string s after each instance of sep, returning an array of substrings of s.
// If sep is empty, SplitAfter splits s after each UTF-8 sequence.
// If n > 0, SplitAfter splits s into at most n substrings; the last substring will be the unsplit remainder.
func SplitAfter(s, sep string, n int) []string {
	return genSplit(s, sep, len(sep), n)
}

// Fields splits the string s around each instance of one or more consecutive white space
// characters, returning an array of substrings of s or an empty list if s contains only white space.
func Fields(s string) []string {
	return FieldsFunc(s, unicode.IsSpace)
}

// FieldsFunc splits the string s at each run of Unicode code points c satifying f(c)
// and returns an array of slices of s. If no code points in s satisfy f(c), an empty slice
// is returned.
func FieldsFunc(s string, f func(int) bool) []string {
	// First count the fields.
	n := 0
	inField := false
	for _, rune := range s {
		wasInField := inField
		inField = !f(rune)
		if inField && !wasInField {
			n++
		}
	}

	// Now create them.
	a := make([]string, n)
	na := 0
	fieldStart := -1 // Set to -1 when looking for start of field.
	for i, rune := range s {
		if f(rune) {
			if fieldStart >= 0 {
				a[na] = s[fieldStart:i]
				na++
				fieldStart = -1
			}
		} else if fieldStart == -1 {
			fieldStart = i
		}
	}
	if fieldStart != -1 { // Last field might end at EOF.
		a[na] = s[fieldStart:]
	}
	return a
}

// Join concatenates the elements of a to create a single string.   The separator string
// sep is placed between elements in the resulting string.
func Join(a []string, sep string) string {
	if len(a) == 0 {
		return ""
	}
	if len(a) == 1 {
		return a[0]
	}
	n := len(sep) * (len(a) - 1)
	for i := 0; i < len(a); i++ {
		n += len(a[i])
	}

	b := make([]byte, n)
	bp := 0
	for i := 0; i < len(a); i++ {
		s := a[i]
		for j := 0; j < len(s); j++ {
			b[bp] = s[j]
			bp++
		}
		if i+1 < len(a) {
			s = sep
			for j := 0; j < len(s); j++ {
				b[bp] = s[j]
				bp++
			}
		}
	}
	return string(b)
}

// HasPrefix tests whether the string s begins with prefix.
func HasPrefix(s, prefix string) bool {
	return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
}

// HasSuffix tests whether the string s ends with suffix.
func HasSuffix(s, suffix string) bool {
	return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
}

// Map returns a copy of the string s with all its characters modified
// according to the mapping function. If mapping returns a negative value, the character is
// dropped from the string with no replacement.
func Map(mapping func(rune int) int, s string) string {
	// In the worst case, the string can grow when mapped, making
	// things unpleasant.  But it's so rare we barge in assuming it's
	// fine.  It could also shrink but that falls out naturally.
	maxbytes := len(s) // length of b
	nbytes := 0        // number of bytes encoded in b
	b := make([]byte, maxbytes)
	for _, c := range s {
		rune := mapping(c)
		if rune >= 0 {
			wid := 1
			if rune >= utf8.RuneSelf {
				wid = utf8.RuneLen(rune)
			}
			if nbytes+wid > maxbytes {
				// Grow the buffer.
				maxbytes = maxbytes*2 + utf8.UTFMax
				nb := make([]byte, maxbytes)
				for i, c := range b[0:nbytes] {
					nb[i] = c
				}
				b = nb
			}
			nbytes += utf8.EncodeRune(rune, b[nbytes:maxbytes])
		}
	}
	return string(b[0:nbytes])
}

// Repeat returns a new string consisting of count copies of the string s.
func Repeat(s string, count int) string {
	b := make([]byte, len(s)*count)
	bp := 0
	for i := 0; i < count; i++ {
		for j := 0; j < len(s); j++ {
			b[bp] = s[j]
			bp++
		}
	}
	return string(b)
}

// ToUpper returns a copy of the string s with all Unicode letters mapped to their upper case.
func ToUpper(s string) string { return Map(unicode.ToUpper, s) }

// ToLower returns a copy of the string s with all Unicode letters mapped to their lower case.
func ToLower(s string) string { return Map(unicode.ToLower, s) }

// ToTitle returns a copy of the string s with all Unicode letters mapped to their title case.
func ToTitle(s string) string { return Map(unicode.ToTitle, s) }

// ToUpperSpecial returns a copy of the string s with all Unicode letters mapped to their
// upper case, giving priority to the special casing rules.
func ToUpperSpecial(_case unicode.SpecialCase, s string) string {
	return Map(func(r int) int { return _case.ToUpper(r) }, s)
}

// ToLowerSpecial returns a copy of the string s with all Unicode letters mapped to their
// lower case, giving priority to the special casing rules.
func ToLowerSpecial(_case unicode.SpecialCase, s string) string {
	return Map(func(r int) int { return _case.ToLower(r) }, s)
}

// ToTitleSpecial returns a copy of the string s with all Unicode letters mapped to their
// title case, giving priority to the special casing rules.
func ToTitleSpecial(_case unicode.SpecialCase, s string) string {
	return Map(func(r int) int { return _case.ToTitle(r) }, s)
}

// TrimLeftFunc returns a slice of the string s with all leading
// Unicode code points c satisfying f(c) removed.
func TrimLeftFunc(s string, f func(r int) bool) string {
	start, end := 0, len(s)
	for start < end {
		wid := 1
		rune := int(s[start])
		if rune >= utf8.RuneSelf {
			rune, wid = utf8.DecodeRuneInString(s[start:end])
		}
		if !f(rune) {
			return s[start:]
		}
		start += wid
	}
	return s[start:]
}

// TrimRightFunc returns a slice of the string s with all trailing
// Unicode code points c satisfying f(c) removed.
func TrimRightFunc(s string, f func(r int) bool) string {
	start, end := 0, len(s)
	for start < end {
		wid := 1
		rune := int(s[end-wid])
		if rune >= utf8.RuneSelf {
			// Back up & look for beginning of rune. Mustn't pass start.
			for wid = 2; start <= end-wid && !utf8.RuneStart(s[end-wid]); wid++ {
			}
			if start > end-wid { // invalid UTF-8 sequence; stop processing
				return s[start:end]
			}
			rune, wid = utf8.DecodeRuneInString(s[end-wid : end])
		}
		if !f(rune) {
			return s[0:end]
		}
		end -= wid
	}
	return s[0:end]
}

// TrimFunc returns a slice of the string s with all leading
// and trailing Unicode code points c satisfying f(c) removed.
func TrimFunc(s string, f func(r int) bool) string {
	return TrimRightFunc(TrimLeftFunc(s, f), f)
}

func makeCutsetFunc(cutset string) func(rune int) bool {
	return func(rune int) bool { return IndexRune(cutset, rune) != -1 }
}

// Trim returns a slice of the string s with all leading and
// trailing Unicode code points contained in cutset removed.
func Trim(s string, cutset string) string {
	if s == "" || cutset == "" {
		return s
	}
	return TrimFunc(s, makeCutsetFunc(cutset))
}

// TrimLeft returns a slice of the string s with all leading
// Unicode code points contained in cutset removed.
func TrimLeft(s string, cutset string) string {
	if s == "" || cutset == "" {
		return s
	}
	return TrimLeftFunc(s, makeCutsetFunc(cutset))
}

// TrimRight returns a slice of the string s, with all trailing
// Unicode code points contained in cutset removed.
func TrimRight(s string, cutset string) string {
	if s == "" || cutset == "" {
		return s
	}
	return TrimRightFunc(s, makeCutsetFunc(cutset))
}

// TrimSpace returns a slice of the string s, with all leading
// and trailing white space removed, as defined by Unicode.
func TrimSpace(s string) string {
	return TrimFunc(s, unicode.IsSpace)
}
<commit_msg>Conversion from loop to copy().<commit_after>// Copyright 2009 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.

// A package of simple functions to manipulate strings.
package strings

import (
	"unicode"
	"utf8"
)

// explode splits s into an array of UTF-8 sequences, one per Unicode character (still strings) up to a maximum of n (n <= 0 means no limit).
// Invalid UTF-8 sequences become correct encodings of U+FFF8.
func explode(s string, n int) []string {
	l := utf8.RuneCountInString(s)
	if n <= 0 || n > l {
		n = l
	}
	a := make([]string, n)
	var size, rune int
	i, cur := 0, 0
	for ; i+1 < n; i++ {
		rune, size = utf8.DecodeRuneInString(s[cur:])
		a[i] = string(rune)
		cur += size
	}
	// add the rest
	a[i] = s[cur:]
	return a
}

// Count counts the number of non-overlapping instances of sep in s.
func Count(s, sep string) int {
	if sep == "" {
		return utf8.RuneCountInString(s) + 1
	}
	c := sep[0]
	l := len(sep)
	n := 0
	if l == 1 {
		// special case worth making fast
		for i := 0; i < len(s); i++ {
			if s[i] == c {
				n++
			}
		}
		return n
	}
	for i := 0; i+l <= len(s); i++ {
		if s[i] == c && s[i:i+l] == sep {
			n++
			i += l - 1
		}
	}
	return n
}

// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
func Index(s, sep string) int {
	n := len(sep)
	if n == 0 {
		return 0
	}
	c := sep[0]
	if n == 1 {
		// special case worth making fast
		for i := 0; i < len(s); i++ {
			if s[i] == c {
				return i
			}
		}
		return -1
	}
	// n > 1
	for i := 0; i+n <= len(s); i++ {
		if s[i] == c && s[i:i+n] == sep {
			return i
		}
	}
	return -1
}

// LastIndex returns the index of the last instance of sep in s, or -1 if sep is not present in s.
func LastIndex(s, sep string) int {
	n := len(sep)
	if n == 0 {
		return len(s)
	}
	c := sep[0]
	if n == 1 {
		// special case worth making fast
		for i := len(s) - 1; i >= 0; i-- {
			if s[i] == c {
				return i
			}
		}
		return -1
	}
	// n > 1
	for i := len(s) - n; i >= 0; i-- {
		if s[i] == c && s[i:i+n] == sep {
			return i
		}
	}
	return -1
}

// IndexRune returns the index of the first instance of the Unicode code point
// rune, or -1 if rune is not present in s.
func IndexRune(s string, rune int) int {
	for i, c := range s {
		if c == rune {
			return i
		}
	}
	return -1
}

// IndexAny returns the index of the first instance of any Unicode code point
// from chars in s, or -1 if no Unicode code point from chars is present in s.
func IndexAny(s, chars string) int {
	if len(chars) > 0 {
		for i, c := range s {
			for _, m := range chars {
				if c == m {
					return i
				}
			}
		}
	}
	return -1
}

// Generic split: splits after each instance of sep,
// including sepSave bytes of sep in the subarrays.
func genSplit(s, sep string, sepSave, n int) []string {
	if sep == "" {
		return explode(s, n)
	}
	if n <= 0 {
		n = Count(s, sep) + 1
	}
	c := sep[0]
	start := 0
	a := make([]string, n)
	na := 0
	for i := 0; i+len(sep) <= len(s) && na+1 < n; i++ {
		if s[i] == c && (len(sep) == 1 || s[i:i+len(sep)] == sep) {
			a[na] = s[start : i+sepSave]
			na++
			start = i + len(sep)
			i += len(sep) - 1
		}
	}
	a[na] = s[start:]
	return a[0 : na+1]
}

// Split splits the string s around each instance of sep, returning an array of substrings of s.
// If sep is empty, Split splits s after each UTF-8 sequence.
// If n > 0, Split splits s into at most n substrings; the last substring will be the unsplit remainder.
func Split(s, sep string, n int) []string { return genSplit(s, sep, 0, n) }

// SplitAfter splits the string s after each instance of sep, returning an array of substrings of s.
// If sep is empty, SplitAfter splits s after each UTF-8 sequence.
// If n > 0, SplitAfter splits s into at most n substrings; the last substring will be the unsplit remainder.
func SplitAfter(s, sep string, n int) []string {
	return genSplit(s, sep, len(sep), n)
}

// Fields splits the string s around each instance of one or more consecutive white space
// characters, returning an array of substrings of s or an empty list if s contains only white space.
func Fields(s string) []string {
	return FieldsFunc(s, unicode.IsSpace)
}

// FieldsFunc splits the string s at each run of Unicode code points c satifying f(c)
// and returns an array of slices of s. If no code points in s satisfy f(c), an empty slice
// is returned.
func FieldsFunc(s string, f func(int) bool) []string {
	// First count the fields.
	n := 0
	inField := false
	for _, rune := range s {
		wasInField := inField
		inField = !f(rune)
		if inField && !wasInField {
			n++
		}
	}

	// Now create them.
	a := make([]string, n)
	na := 0
	fieldStart := -1 // Set to -1 when looking for start of field.
	for i, rune := range s {
		if f(rune) {
			if fieldStart >= 0 {
				a[na] = s[fieldStart:i]
				na++
				fieldStart = -1
			}
		} else if fieldStart == -1 {
			fieldStart = i
		}
	}
	if fieldStart != -1 { // Last field might end at EOF.
		a[na] = s[fieldStart:]
	}
	return a
}

// Join concatenates the elements of a to create a single string.   The separator string
// sep is placed between elements in the resulting string.
func Join(a []string, sep string) string {
	if len(a) == 0 {
		return ""
	}
	if len(a) == 1 {
		return a[0]
	}
	n := len(sep) * (len(a) - 1)
	for i := 0; i < len(a); i++ {
		n += len(a[i])
	}

	b := make([]byte, n)
	bp := 0
	for i := 0; i < len(a); i++ {
		s := a[i]
		for j := 0; j < len(s); j++ {
			b[bp] = s[j]
			bp++
		}
		if i+1 < len(a) {
			s = sep
			for j := 0; j < len(s); j++ {
				b[bp] = s[j]
				bp++
			}
		}
	}
	return string(b)
}

// HasPrefix tests whether the string s begins with prefix.
func HasPrefix(s, prefix string) bool {
	return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
}

// HasSuffix tests whether the string s ends with suffix.
func HasSuffix(s, suffix string) bool {
	return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
}

// Map returns a copy of the string s with all its characters modified
// according to the mapping function. If mapping returns a negative value, the character is
// dropped from the string with no replacement.
func Map(mapping func(rune int) int, s string) string {
	// In the worst case, the string can grow when mapped, making
	// things unpleasant.  But it's so rare we barge in assuming it's
	// fine.  It could also shrink but that falls out naturally.
	maxbytes := len(s) // length of b
	nbytes := 0        // number of bytes encoded in b
	b := make([]byte, maxbytes)
	for _, c := range s {
		rune := mapping(c)
		if rune >= 0 {
			wid := 1
			if rune >= utf8.RuneSelf {
				wid = utf8.RuneLen(rune)
			}
			if nbytes+wid > maxbytes {
				// Grow the buffer.
				maxbytes = maxbytes*2 + utf8.UTFMax
				nb := make([]byte, maxbytes)
				copy(nb, b[0:nbytes])
				b = nb
			}
			nbytes += utf8.EncodeRune(rune, b[nbytes:maxbytes])
		}
	}
	return string(b[0:nbytes])
}

// Repeat returns a new string consisting of count copies of the string s.
func Repeat(s string, count int) string {
	b := make([]byte, len(s)*count)
	bp := 0
	for i := 0; i < count; i++ {
		for j := 0; j < len(s); j++ {
			b[bp] = s[j]
			bp++
		}
	}
	return string(b)
}

// ToUpper returns a copy of the string s with all Unicode letters mapped to their upper case.
func ToUpper(s string) string { return Map(unicode.ToUpper, s) }

// ToLower returns a copy of the string s with all Unicode letters mapped to their lower case.
func ToLower(s string) string { return Map(unicode.ToLower, s) }

// ToTitle returns a copy of the string s with all Unicode letters mapped to their title case.
func ToTitle(s string) string { return Map(unicode.ToTitle, s) }

// ToUpperSpecial returns a copy of the string s with all Unicode letters mapped to their
// upper case, giving priority to the special casing rules.
func ToUpperSpecial(_case unicode.SpecialCase, s string) string {
	return Map(func(r int) int { return _case.ToUpper(r) }, s)
}

// ToLowerSpecial returns a copy of the string s with all Unicode letters mapped to their
// lower case, giving priority to the special casing rules.
func ToLowerSpecial(_case unicode.SpecialCase, s string) string {
	return Map(func(r int) int { return _case.ToLower(r) }, s)
}

// ToTitleSpecial returns a copy of the string s with all Unicode letters mapped to their
// title case, giving priority to the special casing rules.
func ToTitleSpecial(_case unicode.SpecialCase, s string) string {
	return Map(func(r int) int { return _case.ToTitle(r) }, s)
}

// TrimLeftFunc returns a slice of the string s with all leading
// Unicode code points c satisfying f(c) removed.
func TrimLeftFunc(s string, f func(r int) bool) string {
	start, end := 0, len(s)
	for start < end {
		wid := 1
		rune := int(s[start])
		if rune >= utf8.RuneSelf {
			rune, wid = utf8.DecodeRuneInString(s[start:end])
		}
		if !f(rune) {
			return s[start:]
		}
		start += wid
	}
	return s[start:]
}

// TrimRightFunc returns a slice of the string s with all trailing
// Unicode code points c satisfying f(c) removed.
func TrimRightFunc(s string, f func(r int) bool) string {
	start, end := 0, len(s)
	for start < end {
		wid := 1
		rune := int(s[end-wid])
		if rune >= utf8.RuneSelf {
			// Back up & look for beginning of rune. Mustn't pass start.
			for wid = 2; start <= end-wid && !utf8.RuneStart(s[end-wid]); wid++ {
			}
			if start > end-wid { // invalid UTF-8 sequence; stop processing
				return s[start:end]
			}
			rune, wid = utf8.DecodeRuneInString(s[end-wid : end])
		}
		if !f(rune) {
			return s[0:end]
		}
		end -= wid
	}
	return s[0:end]
}

// TrimFunc returns a slice of the string s with all leading
// and trailing Unicode code points c satisfying f(c) removed.
func TrimFunc(s string, f func(r int) bool) string {
	return TrimRightFunc(TrimLeftFunc(s, f), f)
}

func makeCutsetFunc(cutset string) func(rune int) bool {
	return func(rune int) bool { return IndexRune(cutset, rune) != -1 }
}

// Trim returns a slice of the string s with all leading and
// trailing Unicode code points contained in cutset removed.
func Trim(s string, cutset string) string {
	if s == "" || cutset == "" {
		return s
	}
	return TrimFunc(s, makeCutsetFunc(cutset))
}

// TrimLeft returns a slice of the string s with all leading
// Unicode code points contained in cutset removed.
func TrimLeft(s string, cutset string) string {
	if s == "" || cutset == "" {
		return s
	}
	return TrimLeftFunc(s, makeCutsetFunc(cutset))
}

// TrimRight returns a slice of the string s, with all trailing
// Unicode code points contained in cutset removed.
func TrimRight(s string, cutset string) string {
	if s == "" || cutset == "" {
		return s
	}
	return TrimRightFunc(s, makeCutsetFunc(cutset))
}

// TrimSpace returns a slice of the string s, with all leading
// and trailing white space removed, as defined by Unicode.
func TrimSpace(s string) string {
	return TrimFunc(s, unicode.IsSpace)
}
<|endoftext|>