<commit_before>package jiracli

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"text/template"

	yaml "gopkg.in/coryb/yaml.v2"

	"github.com/coryb/figtree"
	"github.com/mgutz/ansi"
	"golang.org/x/crypto/ssh/terminal"
)

func findTemplate(name string) ([]byte, error) {
	if file, err := findClosestParentPath(filepath.Join(".jira.d", "templates", name)); err == nil {
		b, err := ioutil.ReadFile(file)
		if err != nil {
			return nil, err
		}
		return b, nil
	}
	return nil, nil
}

func getTemplate(name string) (string, error) {
	if _, err := os.Stat(name); err == nil {
		b, err := ioutil.ReadFile(name)
		if err != nil {
			return "", err
		}
		return string(b), nil
	}
	b, err := findTemplate(name)
	if err != nil {
		return "", err
	} else if b != nil {
		return string(b), nil
	}
	if s, ok := AllTemplates[name]; ok {
		return s, nil
	}
	return "", fmt.Errorf("No Template found for %q", name)
}

func tmpTemplate(templateName string, data interface{}) (string, error) {
	tmpFile, err := tmpYml(templateName)
	if err != nil {
		return "", err
	}
	defer tmpFile.Close()
	return tmpFile.Name(), RunTemplate(templateName, data, tmpFile)
}

func TemplateProcessor() *template.Template {
	funcs := map[string]interface{}{
		"jira": func() string {
			return os.Args[0]
		},
		"toJson": func(content interface{}) (string, error) {
			bytes, err := json.MarshalIndent(content, "", "    ")
			if err != nil {
				return "", err
			}
			return string(bytes), nil
		},
		"termWidth": func() int {
			w, _, err := terminal.GetSize(int(os.Stdout.Fd()))
			if err == nil {
				return w
			}
			if os.Getenv("COLUMNS") != "" {
				w, err = strconv.Atoi(os.Getenv("COLUMNS"))
			}
			if err == nil {
				return w
			}
			return 120
		},
		"sub": func(a, b int) int {
			return a - b
		},
		"append": func(more string, content interface{}) (string, error) {
			switch value := content.(type) {
			case string:
				return string(append([]byte(content.(string)), []byte(more)...)), nil
			case []byte:
				return string(append(content.([]byte), []byte(more)...)), nil
			default:
				return "", fmt.Errorf("Unknown type: %s", value)
			}
		},
		"indent": func(spaces int, content string) string {
			indent := make([]rune, spaces+1, spaces+1)
			indent[0] = '\n'
			for i := 1; i < spaces+1; i++ {
				indent[i] = ' '
			}

			lineSeps := []rune{'\n', '\u0085', '\u2028', '\u2029'}
			for _, sep := range lineSeps {
				indent[0] = sep
				content = strings.Replace(content, string(sep), string(indent), -1)
			}
			return content

		},
		"comment": func(content string) string {
			lineSeps := []rune{'\n', '\u0085', '\u2028', '\u2029'}
			for _, sep := range lineSeps {
				content = strings.Replace(content, string(sep), string([]rune{sep, '#', ' '}), -1)
			}
			return content
		},
		"color": func(color string) string {
			return ansi.ColorCode(color)
		},
		"split": func(sep string, content string) []string {
			return strings.Split(content, sep)
		},
		"join": func(sep string, content []interface{}) string {
			vals := make([]string, len(content))
			for i, v := range content {
				vals[i] = v.(string)
			}
			return strings.Join(vals, sep)
		},
		"abbrev": func(max int, content string) string {
			if len(content) > max {
				var buffer bytes.Buffer
				buffer.WriteString(content[:max-3])
				buffer.WriteString("...")
				return buffer.String()
			}
			return content
		},
		"rep": func(count int, content string) string {
			var buffer bytes.Buffer
			for i := 0; i < count; i++ {
				buffer.WriteString(content)
			}
			return buffer.String()
		},
		"age": func(content string) (string, error) {
			return fuzzyAge(content)
		},
		"dateFormat": func(format string, content string) (string, error) {
			return dateFormat(format, content)
		},
	}
	return template.New("gojira").Funcs(funcs)
}

func ConfigTemplate(fig *figtree.FigTree, template, command string, opts interface{}) (string, error) {
	var tmp interface{}
	err := ConvertType(opts, &tmp)
	if err != nil {
		return "", err
	}
	fig.LoadAllConfigs(command+".yml", tmp)
	fig.LoadAllConfigs("config.yml", tmp)

	tmpl, err := TemplateProcessor().Parse(template)
	if err != nil {
		return "", err
	}
	buf := bytes.NewBufferString("")
	if err := tmpl.Execute(buf, &tmp); err != nil {
		return "", err
	}
	return buf.String(), nil
}

func ConvertType(input interface{}, output interface{}) error {
	// HACK HACK HACK: convert data formats to json for backwards compatibilty with templates
	jsonData, err := json.Marshal(input)
	if err != nil {
		return err
	}

	defer func(mapType, iface reflect.Type) {
		yaml.DefaultMapType = mapType
		yaml.IfaceType = iface
	}(yaml.DefaultMapType, yaml.IfaceType)<commit_msg>fix named query template expansion<commit_after>package jiracli

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"text/template"

	yaml "gopkg.in/coryb/yaml.v2"

	"github.com/coryb/figtree"
	"github.com/mgutz/ansi"
	"golang.org/x/crypto/ssh/terminal"
)

func findTemplate(name string) ([]byte, error) {
	if file, err := findClosestParentPath(filepath.Join(".jira.d", "templates", name)); err == nil {
		b, err := ioutil.ReadFile(file)
		if err != nil {
			return nil, err
		}
		return b, nil
	}
	return nil, nil
}

func getTemplate(name string) (string, error) {
	if _, err := os.Stat(name); err == nil {
		b, err := ioutil.ReadFile(name)
		if err != nil {
			return "", err
		}
		return string(b), nil
	}
	b, err := findTemplate(name)
	if err != nil {
		return "", err
	} else if b != nil {
		return string(b), nil
	}
	if s, ok := AllTemplates[name]; ok {
		return s, nil
	}
	return "", fmt.Errorf("No Template found for %q", name)
}

func tmpTemplate(templateName string, data interface{}) (string, error) {
	tmpFile, err := tmpYml(templateName)
	if err != nil {
		return "", err
	}
	defer tmpFile.Close()
	return tmpFile.Name(), RunTemplate(templateName, data, tmpFile)
}

func TemplateProcessor() *template.Template {
	funcs := map[string]interface{}{
		"jira": func() string {
			return os.Args[0]
		},
		"toJson": func(content interface{}) (string, error) {
			bytes, err := json.MarshalIndent(content, "", "    ")
			if err != nil {
				return "", err
			}
			return string(bytes), nil
		},
		"termWidth": func() int {
			w, _, err := terminal.GetSize(int(os.Stdout.Fd()))
			if err == nil {
				return w
			}
			if os.Getenv("COLUMNS") != "" {
				w, err = strconv.Atoi(os.Getenv("COLUMNS"))
			}
			if err == nil {
				return w
			}
			return 120
		},
		"sub": func(a, b int) int {
			return a - b
		},
		"append": func(more string, content interface{}) (string, error) {
			switch value := content.(type) {
			case string:
				return string(append([]byte(content.(string)), []byte(more)...)), nil
			case []byte:
				return string(append(content.([]byte), []byte(more)...)), nil
			default:
				return "", fmt.Errorf("Unknown type: %s", value)
			}
		},
		"indent": func(spaces int, content string) string {
			indent := make([]rune, spaces+1, spaces+1)
			indent[0] = '\n'
			for i := 1; i < spaces+1; i++ {
				indent[i] = ' '
			}

			lineSeps := []rune{'\n', '\u0085', '\u2028', '\u2029'}
			for _, sep := range lineSeps {
				indent[0] = sep
				content = strings.Replace(content, string(sep), string(indent), -1)
			}
			return content

		},
		"comment": func(content string) string {
			lineSeps := []rune{'\n', '\u0085', '\u2028', '\u2029'}
			for _, sep := range lineSeps {
				content = strings.Replace(content, string(sep), string([]rune{sep, '#', ' '}), -1)
			}
			return content
		},
		"color": func(color string) string {
			return ansi.ColorCode(color)
		},
		"split": func(sep string, content string) []string {
			return strings.Split(content, sep)
		},
		"join": func(sep string, content []interface{}) string {
			vals := make([]string, len(content))
			for i, v := range content {
				vals[i] = v.(string)
			}
			return strings.Join(vals, sep)
		},
		"abbrev": func(max int, content string) string {
			if len(content) > max {
				var buffer bytes.Buffer
				buffer.WriteString(content[:max-3])
				buffer.WriteString("...")
				return buffer.String()
			}
			return content
		},
		"rep": func(count int, content string) string {
			var buffer bytes.Buffer
			for i := 0; i < count; i++ {
				buffer.WriteString(content)
			}
			return buffer.String()
		},
		"age": func(content string) (string, error) {
			return fuzzyAge(content)
		},
		"dateFormat": func(format string, content string) (string, error) {
			return dateFormat(format, content)
		},
	}
	return template.New("gojira").Funcs(funcs)
}

func ConfigTemplate(fig *figtree.FigTree, template, command string, opts interface{}) (string, error) {
	var tmp map[string]interface{}
	err := ConvertType(opts, &tmp)
	if err != nil {
		return "", err
	}
	fig.LoadAllConfigs(command+".yml", &tmp)
	fig.LoadAllConfigs("config.yml", &tmp)

	tmpl, err := TemplateProcessor().Parse(template)
	if err != nil {
		return "", err
	}
	buf := bytes.NewBufferString("")
	if err := tmpl.Execute(buf, &tmp); err != nil {
		return "", err
	}
	return buf.String(), nil
}

func ConvertType(input interface{}, output interface{}) error {
	// HACK HACK HACK: convert data formats to json for backwards compatibilty with templates
	jsonData, err := json.Marshal(input)
	if err != nil {
		return err
	}

	defer func(mapType, iface reflect.Type) {
		yaml.DefaultMapType = mapType
		yaml.IfaceType = iface
	}(yaml.DefaultMapType, yaml.IfaceType)
{'fraction_non_alphanumeric': 0.1712544652237865, 'fraction_numerical': 0.008194998949359109, 'mean_word_length': 3.4502103786816267, 'pattern_counts': {'":': 28, '<': 7, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1263981', 'n_tokens_mistral': 3817, 'n_tokens_neox': 3355, 'n_words': 871}