<commit_before>// Copyright 2016 Red Hat, Inc, and individual contributors.
//
// 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 (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"sort"
	"strings"

	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	kubecmd "k8s.io/kubernetes/pkg/kubectl/cmd"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)

type CommandCompleter struct {
	Root *cobra.Command
}

func (cc *CommandCompleter) Do(line []rune, pos int) (newLine [][]rune, offset int) {
	cmd := cc.Root
	word := string(line[:pos])
	lastSpace := strings.LastIndex(string(line[:pos]), " ") + 1
	lastComma := strings.LastIndex(string(line[:pos]), ",") + 1
	if lastSpace > 0 {
		if lastComma > lastSpace {
			word = word[lastComma:pos]
		} else {
			word = word[lastSpace:pos]
		}
		var err error
		cmd, args, err = cc.Root.Find(strings.Split(string(line), " "))
		if err != nil {
			return
		}
		for _, arg := range args {
			if !strings.HasPrefix(arg, "-") {
				nouns = append(nouns, arg)
			}
		}
	}
	for _, completion := range completions(word, cmd, nouns) {
		if len(word) >= len(completion) {
			if len(word) == len(completion) {
				newLine = append(newLine, []rune{' '})
			} else {
				newLine = append(newLine, []rune(completion))
			}
			offset = len(completion)
		} else {
			newLine = append(newLine, []rune(completion)[len(word):])
			offset = len(word)
		}

	}
	return
}

func completions(prefix string, cmd *cobra.Command, nouns []string) []string {
	candidates := []string{}
	if strings.HasPrefix(prefix, "-") {
		candidates = flags(cmd)
	} else {
		candidates = subCommands(cmd)
		if len(candidates) == 0 {
			if len(nouns) > 1 {
				candidates = resources(nouns[0])
			} else {
				candidates = resourceTypes(cmd)
			}
		}
	}
	return complete(prefix, candidates)
}

func complete(prefix string, candidates []string) (results []string) {
	for _, s := range candidates {
		if strings.HasPrefix(s, prefix) {
			results = append(results, s)
		}
	}
	return
}

func subCommands(cmd *cobra.Command) []string {
	prefixes := make([]string, len(cmd.Commands()))
	for i, c := range cmd.Commands() {
		prefixes[i] = c.Name()
	}
	return prefixes
}

func resourceTypes(cmd *cobra.Command) []string {
	args := cmd.ValidArgs
	sort.Strings(args)
	return args
}

func resources(resourceType string) []string {
	// TODO: Replace this with something abstracted out for reuse
	// between here and setContextCommand in kubesh.go
	var buff bytes.Buffer
	writer := bufio.NewWriter(&buff)
	cmd := kubecmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, writer, ioutil.Discard)
	callArgs := []string{"get", resourceType, "--output=template",
		"--template={{ range .items }}{{ .metadata.name }} {{ end }}"}
	cmd.SetArgs(callArgs)
	cmd.Execute()
	writer.Flush()
	content, err := ioutil.ReadAll(bufio.NewReader(&buff))
	if err != nil {
		fmt.Println(err)
		return []string{}
	}
	return strings.Split(strings.TrimSpace(string(content)), " ")
}

func flags(cmd *cobra.Command) []string {
	flags := []string{}
	fn := func(f *pflag.Flag) {
		if len(f.Deprecated) > 0 || f.Hidden {
			return
		}
		flag := "--" + f.Name
		if len(f.NoOptDefVal) == 0 {
			flag += "="
		}
		flags = append(flags, flag)
	}
	cmd.NonInheritedFlags().VisitAll(fn)
	cmd.InheritedFlags().VisitAll(fn)
	return flags
}
<commit_msg>Be a bit smarter about what to complete for each command<commit_after>// Copyright 2016 Red Hat, Inc, and individual contributors.
//
// 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 (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"sort"
	"strings"

	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	kubecmd "k8s.io/kubernetes/pkg/kubectl/cmd"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)

type CommandCompleter struct {
	Root *cobra.Command
}

func (cc *CommandCompleter) Do(line []rune, pos int) (newLine [][]rune, offset int) {
	cmd := cc.Root
	word := string(line[:pos])
	lastSpace := strings.LastIndex(string(line[:pos]), " ") + 1
	lastComma := strings.LastIndex(string(line[:pos]), ",") + 1
	if lastSpace > 0 {
		if lastComma > lastSpace {
			word = word[lastComma:pos]
		} else {
			word = word[lastSpace:pos]
		}
		var err error
		cmd, args, err = cc.Root.Find(strings.Split(string(line), " "))
		if err != nil {
			return
		}
		for _, arg := range args {
			if !strings.HasPrefix(arg, "-") {
				nouns = append(nouns, arg)
			}
		}
	}
	for _, completion := range completions(word, cmd, nouns) {
		if len(word) >= len(completion) {
			if len(word) == len(completion) {
				newLine = append(newLine, []rune{' '})
			} else {
				newLine = append(newLine, []rune(completion))
			}
			offset = len(completion)
		} else {
			newLine = append(newLine, []rune(completion)[len(word):])
			offset = len(word)
		}

	}
	return
}

func completions(prefix string, cmd *cobra.Command, nouns []string) []string {
	candidates := []string{}
	if strings.HasPrefix(prefix, "-") {
		candidates = flags(cmd)
	} else {
		candidates = subCommands(cmd)
		if len(candidates) == 0 {
			switch cmd.Name() {
			case "get", "describe", "delete", "label", "stop", "edit", "patch",
				"annotate", "expose", "scale", "autoscale", "taint", "rollout":
				if len(nouns) > 1 {
					candidates = resources(nouns[0])
				} else {
					candidates = resourceTypes(cmd)
				}
			case "logs", "attach", "exec", "port-forward":
				candidates = resources("pods")
			case "rolling-update":
				candidates = resources("rc")
			case "cordon", "uncordon", "drain":
				candidates = resources("node")
			}
		}
	}
	return complete(prefix, candidates)
}

func complete(prefix string, candidates []string) (results []string) {
	for _, s := range candidates {
		if strings.HasPrefix(s, prefix) {
			results = append(results, s)
		}
	}
	return
}

func subCommands(cmd *cobra.Command) []string {
	prefixes := make([]string, len(cmd.Commands()))
	for i, c := range cmd.Commands() {
		prefixes[i] = c.Name()
	}
	return prefixes
}

func resourceTypes(cmd *cobra.Command) []string {
	args := cmd.ValidArgs
	sort.Strings(args)
	return args
}

func resources(resourceType string) []string {
	// TODO: Replace this with something abstracted out for reuse
	// between here and setContextCommand in kubesh.go
	var buff bytes.Buffer
	writer := bufio.NewWriter(&buff)
	cmd := kubecmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, writer, ioutil.Discard)
	callArgs := []string{"get", resourceType, "--output=template",
		"--template={{ range .items }}{{ .metadata.name }} {{ end }}"}
	cmd.SetArgs(callArgs)
	cmd.Execute()
	writer.Flush()
	content, err := ioutil.ReadAll(bufio.NewReader(&buff))
	if err != nil {
		fmt.Println(err)
		return []string{}
	}
	return strings.Split(strings.TrimSpace(string(content)), " ")
}

func flags(cmd *cobra.Command) []string {
	flags := []string{}
	fn := func(f *pflag.Flag) {
		if len(f.Deprecated) > 0 || f.Hidden {
			return
		}
		flag := "--" + f.Name
		if len(f.NoOptDefVal) == 0 {
			flag += "="
		}
		flags = append(flags, flag)
	}
	cmd.NonInheritedFlags().VisitAll(fn)
	cmd.InheritedFlags().VisitAll(fn)
	return flags
}
<|endoftext|>


<commit_before>// Copyright 2016 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// http://studygolang.com
// Author:polaris	polaris@studygolang.com

package logic

import (
	"os"
	"strconv"
	"text/template"
	"time"
	"util"

	"github.com/polaris1119/config"
	"github.com/polaris1119/logger"

	. "db"
	"model"
)

// 自定义模板函数
var funcMap = template.FuncMap{
	"time_format": func(i interface{}) string {
		if t, ok := i.(time.Time); ok {
			return t.Format(time.RFC3339)
		} else if t, ok := i.(model.OftenTime); ok {
			return time.Time(t).Format(time.RFC3339)
		}
		return ""
	},
}

var sitemapTpl = template.Must(template.New("sitemap.xml").Funcs(funcMap).ParseFiles(config.TemplateDir + "/sitemap.xml"))
var sitemapIndexTpl = template.Must(template.ParseFiles(config.TemplateDir + "/sitemapindex.xml"))

var sitemapPath = config.ROOT + "/sitemap/"

func init() {
	if !util.Exist(sitemapPath) {
		err := os.MkdirAll(sitemapPath, 0777)
		if err != nil {
			panic(err)
		}
	}
}

func GenSitemap() {
	sitemapFiles := []string{}

	loc := "http://" + WebsiteSetting.Domain
	if WebsiteSetting.OnlyHttps {
		loc = "https://" + WebsiteSetting.Domain
	}
	// 首页
	home := map[string]string{
		"loc":      loc,
		"lastmode": time.Now().Format(time.RFC3339),
	}

	var (
		little = 1
		step   = 4999
		large  = little + step
	)

	// 文章
	var (
		articles = make([]*model.Article, 0)
		err      error
	)
	for {
		sitemapFile := "sitemap_article_" + strconv.Itoa(large) + ".xml"

		err = MasterDB.Where("id BETWEEN ? AND ? AND status!=?", little, large, model.ArticleStatusOffline).Select("id,mtime").Find(&articles)
		little = large + 1
		large = little + step

		if err != nil {
			continue
		}

		if len(articles) == 0 {
			break
		}

		data := map[string]interface{}{
			"home":     home,
			"articles": articles,
		}

		if err = output(sitemapFile, data); err == nil {
			sitemapFiles = append(sitemapFiles, sitemapFile)
		}

		articles = make([]*model.Article, 0)
	}

	little = 1
	large = little + step

	// 主题（帖子）
	topics := make([]*model.Topic, 0)
	for {
		sitemapFile := "sitemap_topic_" + strconv.Itoa(large) + ".xml"

		err = MasterDB.Where("tid BETWEEN ? AND ? AND flag IN(?,?)", little, large, 0, 1).Select("tid,mtime").Find(&topics)
		little, large = large+1, little+step

		if err != nil {
			continue
		}

		if len(topics) == 0 {
			break
		}

		data := map[string]interface{}{
			"home":   home,
			"topics": topics,
		}

		if err = output(sitemapFile, data); err == nil {
			sitemapFiles = append(sitemapFiles, sitemapFile)
		}

		topics = make([]*model.Topic, 0)
	}

	little = 1
	large = little + step

	// 资源
	resources := make([]*model.Resource, 0)
	for {
		sitemapFile := "sitemap_resource_" + strconv.Itoa(large) + ".xml"

		err = MasterDB.Where("id BETWEEN ? AND ?", little, large).Select("id,mtime").Find(&resources)
		little, large = large+1, little+step

		if err != nil {
			logger.Errorln("sitemap resource find error:", err)
			continue
		}

		if len(resources) == 0 {
			break
		}

		data := map[string]interface{}{
			"home":      home,
			"resources": resources,
		}

		if err = output(sitemapFile, data); err == nil {
			sitemapFiles = append(sitemapFiles, sitemapFile)
		}

		resources = make([]*model.Resource, 0)
	}

	little = 1
	large = little + step

	// 项目
	projects := make([]*model.OpenProject, 0)
	for {
		sitemapFile := "sitemap_project_" + strconv.Itoa(large) + ".xml"

		err = MasterDB.Where("id BETWEEN ? AND ?", little, large).Select("id,uri,mtime").Find(&projects)
		little, large = large+1, little+step

		if err != nil {
			continue
		}

		if len(projects) == 0 {
			break
		}

		data := map[string]interface{}{
			"home":     home,
			"projects": projects,
		}

		if err = output(sitemapFile, data); err == nil {
			sitemapFiles = append(sitemapFiles, sitemapFile)
		}

		projects = make([]*model.OpenProject, 0)
	}

	little = 1
	large = little + step

	// 图书
	books := make([]*model.Book, 0)
	for {
		sitemapFile := "sitemap_book_" + strconv.Itoa(large) + ".xml"

		err = MasterDB.Where("id BETWEEN ? AND ?", little, large).Select("id,updated_at").Find(&books)
		little, large = large+1, little+step

		if err != nil {
			continue
		}

		if len(books) == 0 {
			break
		}

		data := map[string]interface{}{
			"home":  home,
			"books": books,
		}

		if err = output(sitemapFile, data); err == nil {
			sitemapFiles = append(sitemapFiles, sitemapFile)
		}

		books = make([]*model.Book, 0)
	}

	little = 1
	large = little + step

	// wiki
	wikis := make([]*model.Wiki, 0)
	for {
		sitemapFile := "sitemap_wiki_" + strconv.Itoa(large) + ".xml"

		err = MasterDB.Where("id BETWEEN ? AND ?", little, large).Select("id,uri,mtime").Find(&wikis)
		little, large = large+1, little+step

		if err != nil {
			continue
		}

		if len(wikis) == 0 {
			break
		}

		data := map[string]interface{}{
			"home":  home,
			"wikis": wikis,
		}

		if err = output(sitemapFile, data); err == nil {
			sitemapFiles = append(sitemapFiles, sitemapFile)
		}

		wikis = make([]*model.Wiki, 0)
	}

	file, err := os.Create(sitemapPath + "sitemapindex.xml")
	if err != nil {
		logger.Errorln("gen sitemap index file error:", err)
		return
	}
	defer file.Close()

	err = sitemapIndexTpl.Execute(file, map[string]interface{}{
		"home":         home,
		"sitemapFiles": sitemapFiles,
	})
	if err != nil {
		logger.Errorln("execute sitemap index template error:", err)
	}
}

func output(filename string, data map[string]interface{}) (err error) {
	var file *os.File
	file, err = os.Create(sitemapPath + filename)
	if err != nil {
		logger.Errorln("open file error:", err)
		return
	}
	defer file.Close()
	if err = sitemapTpl.Execute(file, data); err != nil {
		logger.Errorln("execute template error:", err)
	}

	return
}
<commit_msg>bugfix<commit_after>// Copyright 2016 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// http://studygolang.com
// Author:polaris	polaris@studygolang.com

package logic

import (
	"os"
	"strconv"
	"text/template"
	"time"
	"util"

	"github.com/polaris1119/config"
	"github.com/polaris1119/logger"

	. "db"
	"model"
)

// 自定义模板函数
var funcMap = template.FuncMap{
	"time_format": func(i interface{}) string {
		if t, ok := i.(time.Time); ok {
			return t.Format(time.RFC3339)
		} else if t, ok := i.(model.OftenTime); ok {
			return time.Time(t).Format(time.RFC3339)
		}
		return ""
	},
}

var sitemapTpl = template.Must(template.New("sitemap.xml").Funcs(funcMap).ParseFiles(config.TemplateDir + "/sitemap.xml"))
var sitemapIndexTpl = template.Must(template.ParseFiles(config.TemplateDir + "/sitemapindex.xml"))

var sitemapPath = config.ROOT + "/sitemap/"

func init() {
	if !util.Exist(sitemapPath) {
		err := os.MkdirAll(sitemapPath, 0777)
		if err != nil {
			panic(err)
		}
	}
}

func GenSitemap() {
	sitemapFiles := []string{}

	loc := "http://" + WebsiteSetting.Domain
	if WebsiteSetting.OnlyHttps {
		loc = "https://" + WebsiteSetting.Domain
	}
	// 首页
	home := map[string]string{
		"loc":      loc,
		"lastmode": time.Now().Format(time.RFC3339),
	}

	var (
		little = 1
		step   = 4999
		large  = little + step
	)

	// 文章
	var (
		articles = make([]*model.Article, 0)
		err      error
	)
	for {
		sitemapFile := "sitemap_article_" + strconv.Itoa(large) + ".xml"

		err = MasterDB.Where("id BETWEEN ? AND ? AND status!=?", little, large, model.ArticleStatusOffline).Select("id,mtime").Find(&articles)
		little = large + 1
		large = little + step

		if err != nil {
			continue
		}

		if len(articles) == 0 {
			break
		}

		data := map[string]interface{}{
			"home":     home,
			"articles": articles,
		}

		if err = output(sitemapFile, data); err == nil {
			sitemapFiles = append(sitemapFiles, sitemapFile)
		}

		articles = make([]*model.Article, 0)
	}

	little = 1
	large = little + step

	// 主题（帖子）
	topics := make([]*model.Topic, 0)
	for {
		sitemapFile := "sitemap_topic_" + strconv.Itoa(large) + ".xml"

		err = MasterDB.Where("tid BETWEEN ? AND ? AND flag IN(?,?)", little, large, 0, 1).Select("tid,mtime").Find(&topics)
		little = large + 1
		large = little + step

		if err != nil {
			continue
		}

		if len(topics) == 0 {
			break
		}

		data := map[string]interface{}{
			"home":   home,
			"topics": topics,
		}

		if err = output(sitemapFile, data); err == nil {
			sitemapFiles = append(sitemapFiles, sitemapFile)
		}

		topics = make([]*model.Topic, 0)
	}

	little = 1
	large = little + step

	// 资源
	resources := make([]*model.Resource, 0)
	for {
		sitemapFile := "sitemap_resource_" + strconv.Itoa(large) + ".xml"

		err = MasterDB.Where("id BETWEEN ? AND ?", little, large).Select("id,mtime").Find(&resources)
		little = large + 1
		large = little + step

		if err != nil {
			logger.Errorln("sitemap resource find error:", err)
			continue
		}

		if len(resources) == 0 {
			break
		}

		data := map[string]interface{}{
			"home":      home,
			"resources": resources,
		}

		if err = output(sitemapFile, data); err == nil {
			sitemapFiles = append(sitemapFiles, sitemapFile)
		}

		resources = make([]*model.Resource, 0)
	}

	little = 1
	large = little + step

	// 项目
	projects := make([]*model.OpenProject, 0)
	for {
		sitemapFile := "sitemap_project_" + strconv.Itoa(large) + ".xml"

		err = MasterDB.Where("id BETWEEN ? AND ?", little, large).Select("id,uri,mtime").Find(&projects)
		little = large + 1
		large = little + step

		if err != nil {
			continue
		}

		if len(projects) == 0 {
			break
		}

		data := map[string]interface{}{
			"home":     home,
			"projects": projects,
		}

		if err = output(sitemapFile, data); err == nil {
			sitemapFiles = append(sitemapFiles, sitemapFile)
		}

		projects = make([]*model.OpenProject, 0)
	}

	little = 1
	large = little + step

	// 图书
	books := make([]*model.Book, 0)
	for {
		sitemapFile := "sitemap_book_" + strconv.Itoa(large) + ".xml"

		err = MasterDB.Where("id BETWEEN ? AND ?", little, large).Select("id,updated_at").Find(&books)
		little = large + 1
		large = little + step

		if err != nil {
			continue
		}

		if len(books) == 0 {
			break
		}

		data := map[string]interface{}{
			"home":  home,
			"books": books,
		}

		if err = output(sitemapFile, data); err == nil {
			sitemapFiles = append(sitemapFiles, sitemapFile)
		}

		books = make([]*model.Book, 0)
	}

	little = 1
	large = little + step

	// wiki
	wikis := make([]*model.Wiki, 0)
	for {
		sitemapFile := "sitemap_wiki_" + strconv.Itoa(large) + ".xml"

		err = MasterDB.Where("id BETWEEN ? AND ?", little, large).Select("id,uri,mtime").Find(&wikis)
		little = large + 1
		large = little + step

		if err != nil {
			continue
		}

		if len(wikis) == 0 {
			break
		}

		data := map[string]interface{}{
			"home":  home,
			"wikis": wikis,
		}

		if err = output(sitemapFile, data); err == nil {
			sitemapFiles = append(sitemapFiles, sitemapFile)
		}

		wikis = make([]*model.Wiki, 0)
	}

	file, err := os.Create(sitemapPath + "sitemapindex.xml")
	if err != nil {
		logger.Errorln("gen sitemap index file error:", err)
		return
	}
	defer file.Close()

	err = sitemapIndexTpl.Execute(file, map[string]interface{}{
		"home":         home,
		"sitemapFiles": sitemapFiles,
	})
	if err != nil {
		logger.Errorln("execute sitemap index template error:", err)
	}
}

func output(filename string, data map[string]interface{}) (err error) {
	var file *os.File
	file, err = os.Create(sitemapPath + filename)
	if err != nil {
		logger.Errorln("open file error:", err)
		return
	}
	defer file.Close()
	if err = sitemapTpl.Execute(file, data); err != nil {
		logger.Errorln("execute template error:", err)
	}

	return
}
<|endoftext|>


<commit_before>// The flow package is a framework for Flow-based programming in Go.
package flow

import (
	"reflect"
	"sync"
	"fmt"
)

const (
	// ComponentModeUndefined stands for a fallback component mode (Async).
	ComponentModeUndefined = iota
	// ComponentModeAsync stands for asynchronous functioning mode.
	ComponentModeAsync
	// ComponentModeSync stands for synchronous functioning mode.
	ComponentModeSync
	// ComponentModePool stands for async functioning with a fixed pool.
	ComponentModePool
)

// DefaultComponentMode is the preselected functioning mode of all components being run.
var DefaultComponentMode = ComponentModeAsync

// Component is a generic flow component that has to be contained in concrete components.
// It stores network-specific information.
type Component struct {
	// Is running flag indicates that the process is currently running.
	IsRunning bool
	// Net is a pointer to network to inform it when the process is started and over
	// or to change its structure at run time.
	Net *Graph
	// Mode is component's functioning mode.
	Mode int8
	// PoolSize is used to define pool size when using ComponentModePool.
	PoolSize uint8
	// Term chan is used to terminate the process immediately without closing
	// any channels.
	Term chan struct{}
}

// Initalizable is the interface implemented by components/graphs with custom initialization code.
type Initializable interface {
	Init()
}

// Finalizable is the interface implemented by components/graphs with extra finalization code.
type Finalizable interface {
	Finish()
}

// Shutdowner is the interface implemented by components overriding default Shutdown() behavior.
type Shutdowner interface {
	Shutdown()
}

// Looper is a long-running process which actively receives data from its ports
// using a Loop function
type Looper interface {
	Loop()
}

// postHandler is used to bind handlers to a port
type portHandler struct {
	onRecv  reflect.Value
	onClose reflect.Value
}

// RunProc runs event handling loop on component ports.
// It returns true on success or panics with error message and returns false on error.
func RunProc(c interface{}) bool {
	// Check if passed interface is a valid pointer to struct
	v := reflect.ValueOf(c)
	if v.Kind() != reflect.Ptr || v.IsNil() {
		panic("Argument of flow.Run() is not a valid pointer")
		return false
	}
	vp := v
	v = v.Elem()
	if v.Kind() != reflect.Struct {
		panic("Argument of flow.Run() is not a valid pointer to structure. Got type: " + vp.Type().Name())
		return false
	}
	t := v.Type()

	// Get internal state lock if available
	hasLock := false
	var locker sync.Locker
	if lockField := v.FieldByName("StateLock"); lockField.IsValid() && lockField.Elem().IsValid() {
		locker, hasLock = lockField.Interface().(sync.Locker)
	}

	// Call user init function if exists
	if initable, ok := c.(Initializable); ok {
		initable.Init()
	}

	// A group to wait for all inputs to be closed
	inputsClose := new(sync.WaitGroup)
	// A group to wait for all recv handlers to finish
	handlersDone := new(sync.WaitGroup)

	// Get the embedded flow.Component
	vCom := v.FieldByName("Component")
	isComponent := vCom.IsValid() && vCom.Type().Name() == "Component"

	if !isComponent {
		panic("Argument of flow.Run() is not a flow.Component")
	}

	// Get the component mode
	componentMode := DefaultComponentMode
	var poolSize uint8 = 0
	if vComMode := vCom.FieldByName("Mode"); vComMode.IsValid() {
		componentMode = int(vComMode.Int())
	}
	if vComPoolSize := vCom.FieldByName("PoolSize"); vComPoolSize.IsValid() {
		poolSize = uint8(vComPoolSize.Uint())
	}

	// Create a slice of select cases and port handlers
	cases := make([]reflect.SelectCase, 0, t.NumField())
	handlers := make([]portHandler, 0, t.NumField())

	// Make and listen on termination channel
	vCom.FieldByName("Term").Set(reflect.MakeChan(vCom.FieldByName("Term").Type(), 0))
	cases = append(cases, reflect.SelectCase{Dir: reflect.SelectRecv, Chan: vCom.FieldByName("Term")})
	handlers = append(handlers, portHandler{})

	// Detect active components
	looper, isLooper := c.(Looper)

	// Iterate over struct fields and bind handlers
	inputCount := 0
	compName := "noname"
	for i := 0; i < t.NumField(); i++ {
		fv := v.Field(i)
		ff := t.Field(i)
		ft := fv.Type()
		compName = ff.Name
		// Detect control channels
		if fv.IsValid() && fv.Kind() == reflect.Chan && !fv.IsNil() && (ft.ChanDir()&reflect.RecvDir) != 0 {
			// Bind handlers for an input channel
			cases = append(cases, reflect.SelectCase{Dir: reflect.SelectRecv, Chan: fv})
			h := portHandler{onRecv: vp.MethodByName("On" + ff.Name), onClose: vp.MethodByName("On" + ff.Name + "Close")}
			handlers = append(handlers, h)
			if h.onClose.IsValid() || h.onRecv.IsValid() {
				// Add the input to the wait group
				inputsClose.Add(1)
				inputCount++
			}
		}
	}

	if inputCount == 0 && !isLooper {
		panic(fmt.Sprintf("Components with no input ports are not supported:%s",compName))
	}

	// Prepare handler closures
	recvHandler := func(onRecv, value reflect.Value) {
		if hasLock {
			locker.Lock()
		}
		valArr := [1]reflect.Value{value}
		onRecv.Call(valArr[:])
		if hasLock {
			locker.Unlock()
		}
		handlersDone.Done()
	}
	closeHandler := func(onClose reflect.Value) {
		if onClose.IsValid() {
			// Lock the state and call OnClose handler
			if hasLock {
				locker.Lock()
			}
			onClose.Call([]reflect.Value{})
			if hasLock {
				locker.Unlock()
			}
		}
		inputsClose.Done()
	}
	terminate := func() {
		if !vCom.FieldByName("IsRunning").Bool() {
			return
		}
		vCom.FieldByName("IsRunning").SetBool(false)
		for i := 0; i < inputCount; i++ {
			inputsClose.Done()
		}
	}
	// closePorts closes all output channels of a process.
	closePorts := func() {
		// Iterate over struct fields
		for i := 0; i < t.NumField(); i++ {
			fv := v.Field(i)
			ft := fv.Type()
			vNet := vCom.FieldByName("Net")
			// Detect and close send-only channels
			if fv.IsValid() {
				if fv.Kind() == reflect.Chan && (ft.ChanDir()&reflect.SendDir) != 0 && (ft.ChanDir()&reflect.RecvDir) == 0 {
					if vNet.IsValid() && !vNet.IsNil() {
						if vNet.Interface().(*Graph).DecSendChanRefCount(fv) {
							fv.Close()
						}
					} else {
						fv.Close()
					}
				} else if fv.Kind() == reflect.Slice && ft.Elem().Kind() == reflect.Chan {
					ll := fv.Len()
					if vNet.IsValid() && !vNet.IsNil() {
						for i := 0; i < ll; i += 1 {
							if vNet.Interface().(*Graph).DecSendChanRefCount(fv.Index(i)) {
								fv.Index(i).Close()
							}
						}
					} else {
						for i := 0; i < ll; i += 1 {
							fv.Index(i).Close()
						}
					}
				}
			}
		}
	}
	// shutdown represents a standard process shutdown procedure.
	shutdown := func() {
		if s, ok := c.(Shutdowner); ok {
			// Custom shutdown behavior
			s.Shutdown()
		} else {
			// Call user finish function if exists
			if finable, ok := c.(Finalizable); ok {
				finable.Finish()
			}
			// Close all output ports if the process is still running
			if vCom.FieldByName("IsRunning").Bool() {
				closePorts()
			}
		}
	}

	// Run the port handlers depending on component mode
	if componentMode == ComponentModePool && poolSize > 0 {
		// Pool mode, prefork limited goroutine pool for all inputs
		var poolIndex uint8
		poolWait := new(sync.WaitGroup)
		once := new(sync.Once)
		for poolIndex = 0; poolIndex < poolSize; poolIndex++ {
			poolWait.Add(1)
			go func() {
				// TODO add pool of Loopers support
				for {
					chosen, recv, recvOK := reflect.Select(cases)
					if !recvOK {
						poolWait.Done()
						if chosen == 0 {
							// Term signal
							terminate()
						} else {
							// Port has been closed
							once.Do(func() {
								// Wait for other workers
								poolWait.Wait()
								// Close output down
								closeHandler(handlers[chosen].onClose)
							})
						}
						return
					}
					if handlers[chosen].onRecv.IsValid() {
						handlersDone.Add(1)
						recvHandler(handlers[chosen].onRecv, recv)
					}
				}
			}()
		}
	} else {
		go func() {
			if isLooper {
				defer func() {
					terminate()
					handlersDone.Done()
				}()
				handlersDone.Add(1)
				looper.Loop()
				return
			}
			for {
				chosen, recv, recvOK := reflect.Select(cases)
				if !recvOK {
					if chosen == 0 {
						// Term signal
						terminate()
					} else {
						// Port has been closed
						closeHandler(handlers[chosen].onClose)
					}
					return
				}
				if handlers[chosen].onRecv.IsValid() {
					handlersDone.Add(1)
					if componentMode == ComponentModeAsync || componentMode == ComponentModeUndefined && DefaultComponentMode == ComponentModeAsync {
						// Async mode
						go recvHandler(handlers[chosen].onRecv, recv)
					} else {
						// Sync mode
						recvHandler(handlers[chosen].onRecv, recv)
					}
				}
			}
		}()
	}

	// Indicate the process as running
	vCom.FieldByName("IsRunning").SetBool(true)

	go func() {
		// Wait for all inputs to be closed
		inputsClose.Wait()
		// Wait all inport handlers to finish their job
		handlersDone.Wait()

		// Call shutdown handler (user or default)
		shutdown()

		// Get the embedded flow.Component and check if it belongs to a network
		if vNet := vCom.FieldByName("Net"); vNet.IsValid() && !vNet.IsNil() {
			if vNetCtr, hasNet := vNet.Interface().(netController); hasNet {
				// Remove the instance from the network's WaitGroup
				vNetCtr.getWait().Done()
			}
		}
	}()
	return true
}

// StopProc terminates the process if it is running.
// It doesn't close any in or out ports of the process, so it can be
// replaced without side effects.
func StopProc(c interface{}) bool {
	// Check if passed interface is a valid pointer to struct
	v := reflect.ValueOf(c)
	if v.Kind() != reflect.Ptr || v.IsNil() {
		panic("Argument of TermProc() is not a valid pointer")
		return false
	}
	vp := v
	v = v.Elem()
	if v.Kind() != reflect.Struct {
		panic("Argument of TermProc() is not a valid pointer to structure. Got type: " + vp.Type().Name())
		return false
	}
	// Get the embedded flow.Component
	vCom := v.FieldByName("Component")
	isComponent := vCom.IsValid() && vCom.Type().Name() == "Component"
	if !isComponent {
		panic("Argument of TermProc() is not a flow.Component")
	}
	// Send the termination signal
	vCom.FieldByName("Term").Close()
	return true
}
<commit_msg>Adding more debug info<commit_after>// The flow package is a framework for Flow-based programming in Go.
package flow

import (
	"reflect"
	"sync"
	"fmt"
)

const (
	// ComponentModeUndefined stands for a fallback component mode (Async).
	ComponentModeUndefined = iota
	// ComponentModeAsync stands for asynchronous functioning mode.
	ComponentModeAsync
	// ComponentModeSync stands for synchronous functioning mode.
	ComponentModeSync
	// ComponentModePool stands for async functioning with a fixed pool.
	ComponentModePool
)

// DefaultComponentMode is the preselected functioning mode of all components being run.
var DefaultComponentMode = ComponentModeAsync

// Component is a generic flow component that has to be contained in concrete components.
// It stores network-specific information.
type Component struct {
	// Is running flag indicates that the process is currently running.
	IsRunning bool
	// Net is a pointer to network to inform it when the process is started and over
	// or to change its structure at run time.
	Net *Graph
	// Mode is component's functioning mode.
	Mode int8
	// PoolSize is used to define pool size when using ComponentModePool.
	PoolSize uint8
	// Term chan is used to terminate the process immediately without closing
	// any channels.
	Term chan struct{}
}

// Initalizable is the interface implemented by components/graphs with custom initialization code.
type Initializable interface {
	Init()
}

// Finalizable is the interface implemented by components/graphs with extra finalization code.
type Finalizable interface {
	Finish()
}

// Shutdowner is the interface implemented by components overriding default Shutdown() behavior.
type Shutdowner interface {
	Shutdown()
}

// Looper is a long-running process which actively receives data from its ports
// using a Loop function
type Looper interface {
	Loop()
}

// postHandler is used to bind handlers to a port
type portHandler struct {
	onRecv  reflect.Value
	onClose reflect.Value
}

// RunProc runs event handling loop on component ports.
// It returns true on success or panics with error message and returns false on error.
func RunProc(c interface{}) bool {
	// Check if passed interface is a valid pointer to struct
	v := reflect.ValueOf(c)
	if v.Kind() != reflect.Ptr || v.IsNil() {
		panic("Argument of flow.Run() is not a valid pointer")
		return false
	}
	vp := v
	v = v.Elem()
	if v.Kind() != reflect.Struct {
		panic("Argument of flow.Run() is not a valid pointer to structure. Got type: " + vp.Type().Name())
		return false
	}
	t := v.Type()

	// Get internal state lock if available
	hasLock := false
	var locker sync.Locker
	if lockField := v.FieldByName("StateLock"); lockField.IsValid() && lockField.Elem().IsValid() {
		locker, hasLock = lockField.Interface().(sync.Locker)
	}

	// Call user init function if exists
	if initable, ok := c.(Initializable); ok {
		initable.Init()
	}

	// A group to wait for all inputs to be closed
	inputsClose := new(sync.WaitGroup)
	// A group to wait for all recv handlers to finish
	handlersDone := new(sync.WaitGroup)

	// Get the embedded flow.Component
	vCom := v.FieldByName("Component")
	isComponent := vCom.IsValid() && vCom.Type().Name() == "Component"

	if !isComponent {
		panic("Argument of flow.Run() is not a flow.Component")
	}

	// Get the component mode
	componentMode := DefaultComponentMode
	var poolSize uint8 = 0
	if vComMode := vCom.FieldByName("Mode"); vComMode.IsValid() {
		componentMode = int(vComMode.Int())
	}
	if vComPoolSize := vCom.FieldByName("PoolSize"); vComPoolSize.IsValid() {
		poolSize = uint8(vComPoolSize.Uint())
	}

	// Create a slice of select cases and port handlers
	cases := make([]reflect.SelectCase, 0, t.NumField())
	handlers := make([]portHandler, 0, t.NumField())

	// Make and listen on termination channel
	vCom.FieldByName("Term").Set(reflect.MakeChan(vCom.FieldByName("Term").Type(), 0))
	cases = append(cases, reflect.SelectCase{Dir: reflect.SelectRecv, Chan: vCom.FieldByName("Term")})
	handlers = append(handlers, portHandler{})

	// Detect active components
	looper, isLooper := c.(Looper)

	// Iterate over struct fields and bind handlers
	inputCount := 0
	portName := "noname"
	compName := "noname"
	for i := 0; i < t.NumField(); i++ {
		fv := v.Field(i)
		ff := t.Field(i)
		ft := fv.Type()
		portName = ff.Name
		compName = ff.PkgPath
		// Detect control channels
		if fv.IsValid() && fv.Kind() == reflect.Chan && !fv.IsNil() && (ft.ChanDir()&reflect.RecvDir) != 0 {
			// Bind handlers for an input channel
			cases = append(cases, reflect.SelectCase{Dir: reflect.SelectRecv, Chan: fv})
			h := portHandler{onRecv: vp.MethodByName("On" + ff.Name), onClose: vp.MethodByName("On" + ff.Name + "Close")}
			handlers = append(handlers, h)
			if h.onClose.IsValid() || h.onRecv.IsValid() {
				// Add the input to the wait group
				inputsClose.Add(1)
				inputCount++
			}
		}
	}

	if inputCount == 0 && !isLooper {
		panic(fmt.Sprintf("Components with no input ports are not supported:%s,%s",compName,portName))
	}

	// Prepare handler closures
	recvHandler := func(onRecv, value reflect.Value) {
		if hasLock {
			locker.Lock()
		}
		valArr := [1]reflect.Value{value}
		onRecv.Call(valArr[:])
		if hasLock {
			locker.Unlock()
		}
		handlersDone.Done()
	}
	closeHandler := func(onClose reflect.Value) {
		if onClose.IsValid() {
			// Lock the state and call OnClose handler
			if hasLock {
				locker.Lock()
			}
			onClose.Call([]reflect.Value{})
			if hasLock {
				locker.Unlock()
			}
		}
		inputsClose.Done()
	}
	terminate := func() {
		if !vCom.FieldByName("IsRunning").Bool() {
			return
		}
		vCom.FieldByName("IsRunning").SetBool(false)
		for i := 0; i < inputCount; i++ {
			inputsClose.Done()
		}
	}
	// closePorts closes all output channels of a process.
	closePorts := func() {
		// Iterate over struct fields
		for i := 0; i < t.NumField(); i++ {
			fv := v.Field(i)
			ft := fv.Type()
			vNet := vCom.FieldByName("Net")
			// Detect and close send-only channels
			if fv.IsValid() {
				if fv.Kind() == reflect.Chan && (ft.ChanDir()&reflect.SendDir) != 0 && (ft.ChanDir()&reflect.RecvDir) == 0 {
					if vNet.IsValid() && !vNet.IsNil() {
						if vNet.Interface().(*Graph).DecSendChanRefCount(fv) {
							fv.Close()
						}
					} else {
						fv.Close()
					}
				} else if fv.Kind() == reflect.Slice && ft.Elem().Kind() == reflect.Chan {
					ll := fv.Len()
					if vNet.IsValid() && !vNet.IsNil() {
						for i := 0; i < ll; i += 1 {
							if vNet.Interface().(*Graph).DecSendChanRefCount(fv.Index(i)) {
								fv.Index(i).Close()
							}
						}
					} else {
						for i := 0; i < ll; i += 1 {
							fv.Index(i).Close()
						}
					}
				}
			}
		}
	}
	// shutdown represents a standard process shutdown procedure.
	shutdown := func() {
		if s, ok := c.(Shutdowner); ok {
			// Custom shutdown behavior
			s.Shutdown()
		} else {
			// Call user finish function if exists
			if finable, ok := c.(Finalizable); ok {
				finable.Finish()
			}
			// Close all output ports if the process is still running
			if vCom.FieldByName("IsRunning").Bool() {
				closePorts()
			}
		}
	}

	// Run the port handlers depending on component mode
	if componentMode == ComponentModePool && poolSize > 0 {
		// Pool mode, prefork limited goroutine pool for all inputs
		var poolIndex uint8
		poolWait := new(sync.WaitGroup)
		once := new(sync.Once)
		for poolIndex = 0; poolIndex < poolSize; poolIndex++ {
			poolWait.Add(1)
			go func() {
				// TODO add pool of Loopers support
				for {
					chosen, recv, recvOK := reflect.Select(cases)
					if !recvOK {
						poolWait.Done()
						if chosen == 0 {
							// Term signal
							terminate()
						} else {
							// Port has been closed
							once.Do(func() {
								// Wait for other workers
								poolWait.Wait()
								// Close output down
								closeHandler(handlers[chosen].onClose)
							})
						}
						return
					}
					if handlers[chosen].onRecv.IsValid() {
						handlersDone.Add(1)
						recvHandler(handlers[chosen].onRecv, recv)
					}
				}
			}()
		}
	} else {
		go func() {
			if isLooper {
				defer func() {
					terminate()
					handlersDone.Done()
				}()
				handlersDone.Add(1)
				looper.Loop()
				return
			}
			for {
				chosen, recv, recvOK := reflect.Select(cases)
				if !recvOK {
					if chosen == 0 {
						// Term signal
						terminate()
					} else {
						// Port has been closed
						closeHandler(handlers[chosen].onClose)
					}
					return
				}
				if handlers[chosen].onRecv.IsValid() {
					handlersDone.Add(1)
					if componentMode == ComponentModeAsync || componentMode == ComponentModeUndefined && DefaultComponentMode == ComponentModeAsync {
						// Async mode
						go recvHandler(handlers[chosen].onRecv, recv)
					} else {
						// Sync mode
						recvHandler(handlers[chosen].onRecv, recv)
					}
				}
			}
		}()
	}

	// Indicate the process as running
	vCom.FieldByName("IsRunning").SetBool(true)

	go func() {
		// Wait for all inputs to be closed
		inputsClose.Wait()
		// Wait all inport handlers to finish their job
		handlersDone.Wait()

		// Call shutdown handler (user or default)
		shutdown()

		// Get the embedded flow.Component and check if it belongs to a network
		if vNet := vCom.FieldByName("Net"); vNet.IsValid() && !vNet.IsNil() {
			if vNetCtr, hasNet := vNet.Interface().(netController); hasNet {
				// Remove the instance from the network's WaitGroup
				vNetCtr.getWait().Done()
			}
		}
	}()
	return true
}

// StopProc terminates the process if it is running.
// It doesn't close any in or out ports of the process, so it can be
// replaced without side effects.
func StopProc(c interface{}) bool {
	// Check if passed interface is a valid pointer to struct
	v := reflect.ValueOf(c)
	if v.Kind() != reflect.Ptr || v.IsNil() {
		panic("Argument of TermProc() is not a valid pointer")
		return false
	}
	vp := v
	v = v.Elem()
	if v.Kind() != reflect.Struct {
		panic("Argument of TermProc() is not a valid pointer to structure. Got type: " + vp.Type().Name())
		return false
	}
	// Get the embedded flow.Component
	vCom := v.FieldByName("Component")
	isComponent := vCom.IsValid() && vCom.Type().Name() == "Component"
	if !isComponent {
		panic("Argument of TermProc() is not a flow.Component")
	}
	// Send the termination signal
	vCom.FieldByName("Term").Close()
	return true
}
<|endoftext|>


<commit_before>/*
 组件
*/
package casper

import (
	"encoding/json"
	"fmt"
	"os"
	"reflect"
	"strings"

	"github.com/gogap/errors"
	log "github.com/golang/glog"
)

var components map[string]*Component = make(map[string]*Component)

// 端点
type EndPoint struct {
	Url    string
	MQType string
	mq
}

// 组件
type Component struct {
	Name        string
	Description string
	in          EndPoint
	app         *App // 当这是一个入口组件...

	outs    map[string]*EndPoint
	handler ComponentHandler
}

type ComponentHandler func(*Payload) (result interface{}, err error)
type ComponentHandlers map[string]ComponentHandler

func BuildComFromConfig(fileName string) {
	var conf struct {
		Components []struct {
			Name        string `json:"name"`
			Description string `json:"description"`
			Type        string `json:"type"`
			In          string `json:"in"`
		} `json:"components"`
	}

	r, err := os.Open(fileName)
	if err != nil {
		panic(err)
	}
	defer r.Close()

	if err = json.NewDecoder(r).Decode(&conf); err != nil {
		panic(err)
	}

	for i := 0; i < len(conf.Components); i++ {
		_, err := NewComponent(conf.Components[i].Name, conf.Components[i].Description, conf.Components[i].Type, conf.Components[i].In)
		if err != nil {
			panic(err)
		}
	}
}

func NewComponent(name, description, mqtype, in string) (*Component, error) {
	sname, smqtype, sin := strings.TrimSpace(name), strings.TrimSpace(mqtype), strings.TrimSpace(in)
	if sname == "" {
		return nil, fmt.Errorf("Component's name empty ERROR")
	}
	if sin == "" {
		return nil, fmt.Errorf("Component's addr empty ERROR")
	}
	if smqtype == "" {
		return nil, fmt.Errorf("Component's mq typpe empty ERROR")
	}

	com := &Component{
		Name:        sname,
		Description: description,
		in:          EndPoint{Url: sin, MQType: smqtype, mq: nil},
		app:         nil,
		outs:        make(map[string]*EndPoint),
		handler:     nil}

	components[com.Name] = com

	return com, nil
}

func GetComponentByName(name string) *Component {
	if component, ok := components[name]; ok {
		return component
	}
	return nil
}

func SetHandlers(handlers ComponentHandlers) {
	for name, handler := range handlers {
		if component := GetComponentByName(name); component != nil {
			component.SetHandler(handler)
		} else {
			panic(fmt.Errorf("component of %s not exist", name))
		}
	}
}

func (p *Component) SetHandler(handler ComponentHandler) {
	if handler == nil {
		panic("handler could not be nil")
	}
	p.handler = handler
}

func (p *Component) GetOutPoint(url string) *EndPoint {
	surl := strings.TrimSpace(url)
	if surl == "" {
		return nil
	}

	if _, ok := p.outs[surl]; !ok {
		p.outs[surl] = &EndPoint{Url: surl, MQType: p.in.MQType, mq: nil}
	}

	return p.outs[surl]
}

func (p *Component) Run() (err error) {
	log.Infoln("Component Run...", p.Name)

	// 创建MQ
	p.in.mq, err = NewMq(p.in.MQType, p.in.Url)
	if err != nil {
		return
	}

	// MQ 准备
	err = p.in.mq.Ready()
	if err != nil {
		return
	}

	// 开始监听
	go p.recvMonitor()

	return nil
}

func (p *Component) recvMonitor() {
	for {
		msg, err := p.in.mq.RecvMessage()
		if err != nil {
			log.Errorln(p.Name, "Error receiving message:", err.Error())
			continue
		}
		log.Infoln(p.Name, "Recv:", string(msg))

		comsg := new(ComponentMessage)
		if err := comsg.FromJson(msg); err != nil {
			log.Errorln(p.Name, "Msg's format error:", err.Error(), string(msg))
			continue
		}

		go p.SendMsg(comsg)
	}
}

func (p *Component) SendMsg(comsg *ComponentMessage) {
	// 就是打日志用的
	msg, _ := comsg.Serialize()
	smsg := string(msg)

	// 更新调用链
	comsg.chain = append(comsg.chain, p.in.Url)

	// deal path
	next := comsg.TopGraph()
	if next == p.in.Url {
		// 正常流程
		next = comsg.PopGraph()
		if next == "" {
			log.Warningln("next is nil. send to entrance:", smsg)
			next = comsg.entrance
		}

		// call worker
		var ret interface{}
		var err error
		if p.handler != nil {
			log.Infoln(p.Name, "Call handler")
			ret, err = p.handler(comsg.Payload)
			if err != nil {
				// 业务处理错误, 发给入口
				log.Errorln("worker error, send to entrance:", smsg, err.Error())
				if errors.IsErrCode(err) == false {
					comsg.Payload.Code = 500
					comsg.Payload.Message = err.Error()
				} else {
					comsg.Payload.Code = err.(errors.ErrCode).Code()
					comsg.Payload.Message = err.(errors.ErrCode).Error()
				}
				next = comsg.entrance
				comsg.Payload.Result = nil
			} else {
				log.Infoln(p.Name, "Call handler ok")
				if ret != nil {
					if _, ok := ret.(Payload); ok {
						panic(fmt.Errorf("%s. worker return can't be Payload or *Payload", p.Name))
					}
					if _, ok := ret.(*Payload); ok {
						panic(fmt.Errorf("%s. worker return can't be Payload or *Payload", p.Name))
					}
					if reflect.TypeOf(ret).Kind() != reflect.Ptr {
						panic(fmt.Errorf("%s. worker return must be Ptr", p.Name))
					}
				}
				comsg.Payload.Result = ret
			}
		}

		// 正常发到下一站
		log.Infoln("sendToNext:", smsg)
		msg, _ := comsg.Serialize()
		if _, err = p.sendToNext(next, msg); err != nil {
			log.Errorf(p.Name, "sendToNext error: ", smsg)
		}
	} else if next == "" {
		// 消息流出错了或是已经走到了入口
		msg, _ := comsg.Serialize()
		if p.app != nil {
			log.Infoln(p.Name, "Msg to entrance:", smsg)
			if err := p.app.recvMsg(comsg); err != nil {
				log.Errorln(p.Name, "msg to entrance err:", err.Error())
			}
		} else {
			// send to entrance
			log.Errorln("msg's next null, send to entrance", smsg)
			_, err := p.sendToNext(comsg.entrance, msg)
			if err != nil {
				log.Errorln(p.Name, "msg's next null, send to entrance ERR", smsg)
			}
		}
	} else if next != p.in.Url {
		// 发给正确的站点
		msg, _ := comsg.Serialize()
		log.Warningln(p.Name, "msg's real next is:", next)
		_, err := p.sendToNext(next, msg)
		if err != nil {
			log.Errorln(p.Name, "send to real next ERR: ", string(msg))
		}
	}
}

func (p *Component) sendToNext(url string, msg []byte) (total int, err error) {
	if url == "" {
		return 0, fmt.Errorf("sendTo nil url")
	}

	if _, ok := p.outs[url]; ok == false {
		mqtmp, err := NewMq(p.in.MQType, url)
		if err != nil {
			return 0, err
		}
		p.outs[url] = &EndPoint{Url: url, MQType: p.in.MQType, mq: mqtmp}
	}

	log.Infoln(p.Name, "sendToNext:", url, string(msg))
	total, err = p.outs[url].mq.SendToNext(msg)

	return
}
<commit_msg>worker return must a ptr<commit_after>/*
 组件
*/
package casper

import (
	"encoding/json"
	"fmt"
	"os"
	"reflect"
	"strings"

	"github.com/gogap/errors"
	log "github.com/golang/glog"
)

var components map[string]*Component = make(map[string]*Component)

// 端点
type EndPoint struct {
	Url    string
	MQType string
	mq
}

// 组件
type Component struct {
	Name        string
	Description string
	in          EndPoint
	app         *App // 当这是一个入口组件...

	outs    map[string]*EndPoint
	handler ComponentHandler
}

type ComponentHandler func(*Payload) (result interface{}, err error)
type ComponentHandlers map[string]ComponentHandler

func BuildComFromConfig(fileName string) {
	var conf struct {
		Components []struct {
			Name        string `json:"name"`
			Description string `json:"description"`
			Type        string `json:"type"`
			In          string `json:"in"`
		} `json:"components"`
	}

	r, err := os.Open(fileName)
	if err != nil {
		panic(err)
	}
	defer r.Close()

	if err = json.NewDecoder(r).Decode(&conf); err != nil {
		panic(err)
	}

	for i := 0; i < len(conf.Components); i++ {
		_, err := NewComponent(conf.Components[i].Name, conf.Components[i].Description, conf.Components[i].Type, conf.Components[i].In)
		if err != nil {
			panic(err)
		}
	}
}

func NewComponent(name, description, mqtype, in string) (*Component, error) {
	sname, smqtype, sin := strings.TrimSpace(name), strings.TrimSpace(mqtype), strings.TrimSpace(in)
	if sname == "" {
		return nil, fmt.Errorf("Component's name empty ERROR")
	}
	if sin == "" {
		return nil, fmt.Errorf("Component's addr empty ERROR")
	}
	if smqtype == "" {
		return nil, fmt.Errorf("Component's mq typpe empty ERROR")
	}

	com := &Component{
		Name:        sname,
		Description: description,
		in:          EndPoint{Url: sin, MQType: smqtype, mq: nil},
		app:         nil,
		outs:        make(map[string]*EndPoint),
		handler:     nil}

	components[com.Name] = com

	return com, nil
}

func GetComponentByName(name string) *Component {
	if component, ok := components[name]; ok {
		return component
	}
	return nil
}

func SetHandlers(handlers ComponentHandlers) {
	for name, handler := range handlers {
		if component := GetComponentByName(name); component != nil {
			component.SetHandler(handler)
		} else {
			panic(fmt.Errorf("component of %s not exist", name))
		}
	}
}

func (p *Component) SetHandler(handler ComponentHandler) {
	if handler == nil {
		panic("handler could not be nil")
	}
	p.handler = handler
}

func (p *Component) GetOutPoint(url string) *EndPoint {
	surl := strings.TrimSpace(url)
	if surl == "" {
		return nil
	}

	if _, ok := p.outs[surl]; !ok {
		p.outs[surl] = &EndPoint{Url: surl, MQType: p.in.MQType, mq: nil}
	}

	return p.outs[surl]
}

func (p *Component) Run() (err error) {
	log.Infoln("Component Run...", p.Name)

	// 创建MQ
	p.in.mq, err = NewMq(p.in.MQType, p.in.Url)
	if err != nil {
		return
	}

	// MQ 准备
	err = p.in.mq.Ready()
	if err != nil {
		return
	}

	// 开始监听
	go p.recvMonitor()

	return nil
}

func (p *Component) recvMonitor() {
	for {
		msg, err := p.in.mq.RecvMessage()
		if err != nil {
			log.Errorln(p.Name, "Error receiving message:", err.Error())
			continue
		}
		log.Infoln(p.Name, "Recv:", string(msg))

		comsg := new(ComponentMessage)
		if err := comsg.FromJson(msg); err != nil {
			log.Errorln(p.Name, "Msg's format error:", err.Error(), string(msg))
			continue
		}

		go p.SendMsg(comsg)
	}
}

func (p *Component) SendMsg(comsg *ComponentMessage) {
	// 就是打日志用的
	msg, _ := comsg.Serialize()
	smsg := string(msg)

	// 更新调用链
	comsg.chain = append(comsg.chain, p.in.Url)

	// deal path
	next := comsg.TopGraph()
	if next == p.in.Url {
		// 正常流程
		next = comsg.PopGraph()
		if next == "" {
			log.Warningln("next is nil. send to entrance:", smsg)
			next = comsg.entrance
		}

		// call worker
		var ret interface{}
		var err error
		if p.handler != nil {
			log.Infoln(p.Name, "Call handler")
			ret, err = p.handler(comsg.Payload)
			if err != nil {
				// 业务处理错误, 发给入口
				log.Errorln("worker error, send to entrance:", smsg, err.Error())
				if errors.IsErrCode(err) == false {
					comsg.Payload.Code = 500
					comsg.Payload.Message = err.Error()
				} else {
					comsg.Payload.Code = err.(errors.ErrCode).Code()
					comsg.Payload.Message = err.(errors.ErrCode).Error()
				}
				next = comsg.entrance
				comsg.Payload.Result = nil
			} else {
				log.Infoln(p.Name, "Call handler ok")
				comsg.Payload.Result = ret
				if ret != nil {
					_, ok := ret.(Payload)
					_, ok1 := ret.(*Payload)
					if ok || ok1 {
						comsg.Payload.Code = 500
						comsg.Payload.Message = "业务返回数据类型错误"
						comsg.Payload.Result = nil
						log.Errorf("%s. worker return can't be Payload or *Payload", p.Name)
					}
					if reflect.TypeOf(ret).Kind() != reflect.Ptr {
						log.Errorf("%s. worker return must be Ptr", p.Name)
						comsg.Payload.Code = 500
						comsg.Payload.Message = "业务返回数据类型错误"
						comsg.Payload.Result = nil
					}
				}
			}
		}

		// 正常发到下一站
		log.Infoln("sendToNext:", smsg)
		msg, _ := comsg.Serialize()
		if _, err = p.sendToNext(next, msg); err != nil {
			log.Errorf(p.Name, "sendToNext error: ", smsg)
		}
	} else if next == "" {
		// 消息流出错了或是已经走到了入口
		msg, _ := comsg.Serialize()
		if p.app != nil {
			log.Infoln(p.Name, "Msg to entrance:", smsg)
			if err := p.app.recvMsg(comsg); err != nil {
				log.Errorln(p.Name, "msg to entrance err:", err.Error())
			}
		} else {
			// send to entrance
			log.Errorln("msg's next null, send to entrance", smsg)
			_, err := p.sendToNext(comsg.entrance, msg)
			if err != nil {
				log.Errorln(p.Name, "msg's next null, send to entrance ERR", smsg)
			}
		}
	} else if next != p.in.Url {
		// 发给正确的站点
		msg, _ := comsg.Serialize()
		log.Warningln(p.Name, "msg's real next is:", next)
		_, err := p.sendToNext(next, msg)
		if err != nil {
			log.Errorln(p.Name, "send to real next ERR: ", string(msg))
		}
	}
}

func (p *Component) sendToNext(url string, msg []byte) (total int, err error) {
	if url == "" {
		return 0, fmt.Errorf("sendTo nil url")
	}

	if _, ok := p.outs[url]; ok == false {
		mqtmp, err := NewMq(p.in.MQType, url)
		if err != nil {
			return 0, err
		}
		p.outs[url] = &EndPoint{Url: url, MQType: p.in.MQType, mq: mqtmp}
	}

	log.Infoln(p.Name, "sendToNext:", url, string(msg))
	total, err = p.outs[url].mq.SendToNext(msg)

	return
}
<|endoftext|>


<commit_before>package sms // import "github.com/mndrix/sms-over-xmpp"

import (
	"encoding/xml"
	"fmt"
	"log"
	"net/http"
	"os"

	xco "github.com/mndrix/go-xco"
	"github.com/pkg/errors"
)

func Main(config Config) {
	sc := &Component{config}

	xmppErr := make(chan error)
	go sc.runXmppComponent(xmppErr)

	httpErr := make(chan error)
	go sc.runHttpServer(httpErr)

	select {
	case err := <-httpErr:
		log.Printf("ERROR HTTP: %s", err)
	case err := <-xmppErr:
		log.Printf("ERROR XMPP: %s", err)
	}
}

func (sc *Component) runHttpServer(errCh chan<- error) {
	config := sc.config
	addr := fmt.Sprintf("%s:%d", config.HttpHost(), config.HttpPort())
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		msgSid := r.FormValue("MessageSid")
		log.Printf("%s %s (%s)", r.Method, r.URL.Path, msgSid)

		// which SMS provider is applicable?
		provider, err := config.SmsProvider()
		switch err {
		case nil:
			// all is well. we'll continue below
		case ErrIgnoreMessage:
			msg := "ignored during provider selection"
			log.Println(msg)
			return
		default:
			msg := fmt.Sprintf("ERROR: choosing an SMS provider: %s", err)
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintln(w, msg)
			log.Println(msg)
			return
		}

		fromPhone, toPhone, body, err := provider.ReceiveSms(r)

		// convert author's phone number into XMPP address
		from, err := config.PhoneToAddress(fromPhone)
		switch err {
		case nil:
			// all is well. proceed
		case ErrIgnoreMessage:
			msg := "ignored based on From address"
			log.Println(msg)
			return
		default:
			msg := fmt.Sprintf("ERROR: From address %s: %s", fromPhone, err)
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintln(w, msg)
			log.Println(msg)
			return
		}

		// convert recipient's phone number into XMPP address
		to, err := config.PhoneToAddress(toPhone)
		switch err {
		case nil:
			// all is well. proceed
		case ErrIgnoreMessage:
			msg := "ignored based on To address"
			log.Println(msg)
			return
		default:
			msg := fmt.Sprintf("ERROR: To address %s: %s", toPhone, err)
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintln(w, msg)
			log.Println(msg)
			return
		}

		log.Printf("would have sent (%s -> %s): %s", &from, &to, body)
	})
	errCh <- http.ListenAndServe(addr, handler)
	close(errCh)
}

func (sc *Component) runXmppComponent(errCh chan<- error) {
	config := sc.config
	opts := xco.Options{
		Name:         config.ComponentName(),
		SharedSecret: config.SharedSecret(),
		Address:      fmt.Sprintf("%s:%d", config.XmppHost(), config.XmppPort()),
		Logger:       log.New(os.Stderr, "", log.LstdFlags),
	}
	c, err := xco.NewComponent(opts)
	if err != nil {
		panic(err)
	}

	c.MessageHandler = sc.onMessage
	c.PresenceHandler = sc.onPresence
	c.IqHandler = sc.onIq
	c.UnknownHandler = sc.onUnknown

	errCh <- c.Run()
	close(errCh)
}

// ErrIgnoreMessage should be returned to indicate that a message
// should be ignored; as if it never happened.
var ErrIgnoreMessage = errors.New("ignore this message")

// Component represents an SMS-over-XMPP component
type Component struct {
	config Config
}

func (sc *Component) onMessage(c *xco.Component, m *xco.Message) error {
	log.Printf("Message: %+v", m)
	if m.Body == "" {
		log.Printf("  ignoring message with empty body")
		return nil
	}

	// convert recipient address into a phone number
	toPhone, err := sc.config.AddressToPhone(m.To)
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		return nil
	default:
		return errors.Wrap(err, "converting 'to' address to phone")
	}

	// convert author's address into a phone number
	fromPhone, err := sc.config.AddressToPhone(m.From)
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		return nil
	default:
		return errors.Wrap(err, "converting 'from' address to phone")
	}

	// choose an SMS provider
	provider, err := sc.config.SmsProvider()
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		return nil
	default:
		return errors.Wrap(err, "choosing an SMS provider")
	}

	// send the message
	err = provider.SendSms(fromPhone, toPhone, m.Body)
	return errors.Wrap(err, "sending SMS")
}

func (sc *Component) onPresence(c *xco.Component, p *xco.Presence) error {
	log.Printf("Presence: %+v", p)
	return nil
}

func (sc *Component) onIq(c *xco.Component, iq *xco.Iq) error {
	log.Printf("Iq: %+v", iq)
	return nil
}

func (sc *Component) onUnknown(c *xco.Component, x *xml.StartElement) error {
	log.Printf("Unknown: %+v", x)
	return nil
}
<commit_msg>Move HTTP handler to a method<commit_after>package sms // import "github.com/mndrix/sms-over-xmpp"

import (
	"encoding/xml"
	"fmt"
	"log"
	"net/http"
	"os"

	xco "github.com/mndrix/go-xco"
	"github.com/pkg/errors"
)

func Main(config Config) {
	sc := &Component{config}

	xmppErr := make(chan error)
	go sc.runXmppComponent(xmppErr)

	httpErr := make(chan error)
	go sc.runHttpServer(httpErr)

	select {
	case err := <-httpErr:
		log.Printf("ERROR HTTP: %s", err)
	case err := <-xmppErr:
		log.Printf("ERROR XMPP: %s", err)
	}
}

func (sc *Component) runHttpServer(errCh chan<- error) {
	config := sc.config
	addr := fmt.Sprintf("%s:%d", config.HttpHost(), config.HttpPort())
	errCh <- http.ListenAndServe(addr, sc)
	close(errCh)
}

func (sc *Component) runXmppComponent(errCh chan<- error) {
	config := sc.config
	opts := xco.Options{
		Name:         config.ComponentName(),
		SharedSecret: config.SharedSecret(),
		Address:      fmt.Sprintf("%s:%d", config.XmppHost(), config.XmppPort()),
		Logger:       log.New(os.Stderr, "", log.LstdFlags),
	}
	c, err := xco.NewComponent(opts)
	if err != nil {
		panic(err)
	}

	c.MessageHandler = sc.onMessage
	c.PresenceHandler = sc.onPresence
	c.IqHandler = sc.onIq
	c.UnknownHandler = sc.onUnknown

	errCh <- c.Run()
	close(errCh)
}

// ErrIgnoreMessage should be returned to indicate that a message
// should be ignored; as if it never happened.
var ErrIgnoreMessage = errors.New("ignore this message")

// Component represents an SMS-over-XMPP component
type Component struct {
	config Config
}

func (sc *Component) onMessage(c *xco.Component, m *xco.Message) error {
	log.Printf("Message: %+v", m)
	if m.Body == "" {
		log.Printf("  ignoring message with empty body")
		return nil
	}

	// convert recipient address into a phone number
	toPhone, err := sc.config.AddressToPhone(m.To)
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		return nil
	default:
		return errors.Wrap(err, "converting 'to' address to phone")
	}

	// convert author's address into a phone number
	fromPhone, err := sc.config.AddressToPhone(m.From)
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		return nil
	default:
		return errors.Wrap(err, "converting 'from' address to phone")
	}

	// choose an SMS provider
	provider, err := sc.config.SmsProvider()
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		return nil
	default:
		return errors.Wrap(err, "choosing an SMS provider")
	}

	// send the message
	err = provider.SendSms(fromPhone, toPhone, m.Body)
	return errors.Wrap(err, "sending SMS")
}

func (sc *Component) onPresence(c *xco.Component, p *xco.Presence) error {
	log.Printf("Presence: %+v", p)
	return nil
}

func (sc *Component) onIq(c *xco.Component, iq *xco.Iq) error {
	log.Printf("Iq: %+v", iq)
	return nil
}

func (sc *Component) onUnknown(c *xco.Component, x *xml.StartElement) error {
	log.Printf("Unknown: %+v", x)
	return nil
}

func (sc *Component) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	msgSid := r.FormValue("MessageSid")
	log.Printf("%s %s (%s)", r.Method, r.URL.Path, msgSid)

	// which SMS provider is applicable?
	provider, err := sc.config.SmsProvider()
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		msg := "ignored during provider selection"
		log.Println(msg)
		return
	default:
		msg := fmt.Sprintf("ERROR: choosing an SMS provider: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, msg)
		log.Println(msg)
		return
	}

	fromPhone, toPhone, body, err := provider.ReceiveSms(r)

	// convert author's phone number into XMPP address
	from, err := sc.config.PhoneToAddress(fromPhone)
	switch err {
	case nil:
		// all is well. proceed
	case ErrIgnoreMessage:
		msg := "ignored based on From address"
		log.Println(msg)
		return
	default:
		msg := fmt.Sprintf("ERROR: From address %s: %s", fromPhone, err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, msg)
		log.Println(msg)
		return
	}

	// convert recipient's phone number into XMPP address
	to, err := sc.config.PhoneToAddress(toPhone)
	switch err {
	case nil:
		// all is well. proceed
	case ErrIgnoreMessage:
		msg := "ignored based on To address"
		log.Println(msg)
		return
	default:
		msg := fmt.Sprintf("ERROR: To address %s: %s", toPhone, err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, msg)
		log.Println(msg)
		return
	}

	log.Printf("would have sent (%s -> %s): %s", &from, &to, body)
}
<|endoftext|>


<commit_before>package jira

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
)

// ComponentService handles components for the JIRA instance / API.
//
// JIRA API docs: https://docs.atlassian.com/software/jira/docs/api/REST/7.10.1/#api/2/component
type ComponentService struct {
	client *Client
}

// CreateComponentOptions are passed to the ComponentService.Create function to create a new JIRA component
type CreateComponentOptions struct {
	Name         string
	Description  string
	LeadUserName string
	AssigneeType string
	Lead         *User
	Assignee     *User
	Project      string
	ProjectID    *int
}

// FullComponent is a JIRA component with all details filled in
type FullComponent struct {
	Name                string `json:"name,omitempty" structs:"name,omitempty"`
	Description         string `json:"description,omitempty" structs:"description,omitempty"`
	Lead                *User  `json:"lead,omitempty" structs:"lead,omitempty"`
	Assignee            *User  `json:"assignee,omitempty" structs:"assignee,omitempty"`
	LeadUserName        string `json:"leadUserName,omitempty" structs:"leadUserName,omitempty"`
	AssigneeType        string `json:"assigneeType,omitempty" structs:"assigneeType,omitempty"`
	RealAssigneeType    string `json:"realAssigneeType,omitempty" structs:"realAssigneeType,omitempty"`
	RealAssignee        *User  `json:"realAssignee,omitempty" structs:"realAssignee,omitempty"`
	IsAssigneeTypeValid bool   `json:"isAssigneeTypeValid,omitempty" structs:"isAssigneeTypeValid,omitempty"`
	Project             string `json:"project,omitempty" structs:"project,omitempty"`
	ProjectID           int    `json:"projectId,omitempty" structs:"projectId,omitempty"`
}

// Create creates a new JIRA component based on the given options.
func (s *ComponentService) Create(options *CreateComponentOptions) (*FullComponent, *Response, error) {
	apiEndpoint := "rest/api/2/component"
	req, err := s.client.NewRequest("POST", apiEndpoint, options)
	if err != nil {
		return nil, nil, err
	}
	resp, err := s.client.Do(req, nil)
	if err != nil {
		// incase of error return the resp for further inspection
		return nil, resp, err
	}

	component := new(FullComponent)
	defer resp.Body.Close()
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, resp, fmt.Errorf("Could not read the returned data")
	}
	err = json.Unmarshal(data, component)
	if err != nil {
		return nil, resp, fmt.Errorf("Could not unmarshall the data into struct")
	}
	return component, resp, nil
}
<commit_msg>Update CreateComponentOptions to include JSON field tags<commit_after>package jira

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
)

// ComponentService handles components for the JIRA instance / API.
//
// JIRA API docs: https://docs.atlassian.com/software/jira/docs/api/REST/7.10.1/#api/2/component
type ComponentService struct {
	client *Client
}

// CreateComponentOptions are passed to the ComponentService.Create function to create a new JIRA component
type CreateComponentOptions struct {
	Name         string `json:"name,omitempty" structs:"name,omitempty"`
	Description  string `json:"description,omitempty" structs:"description,omitempty"`
	Lead         *User  `json:"lead,omitempty" structs:"lead,omitempty"`
	LeadUserName string `json:"leadUserName,omitempty" structs:"leadUserName,omitempty"`
	AssigneeType string `json:"assigneeType,omitempty" structs:"assigneeType,omitempty"`
	Assignee     *User  `json:"assignee,omitempty" structs:"assignee,omitempty"`
	Project      string `json:"project,omitempty" structs:"project,omitempty"`
	ProjectID    int    `json:"projectId,omitempty" structs:"projectId,omitempty"`
}

// FullComponent is a JIRA component with all details filled in
type FullComponent struct {
	RealAssigneeType    string `json:"realAssigneeType,omitempty" structs:"realAssigneeType,omitempty"`
	RealAssignee        *User  `json:"realAssignee,omitempty" structs:"realAssignee,omitempty"`
	IsAssigneeTypeValid bool   `json:"isAssigneeTypeValid,omitempty" structs:"isAssigneeTypeValid,omitempty"`

	CreateComponentOptions
}

// Create creates a new JIRA component based on the given options.
func (s *ComponentService) Create(options *CreateComponentOptions) (*FullComponent, *Response, error) {
	apiEndpoint := "rest/api/2/component"
	req, err := s.client.NewRequest("POST", apiEndpoint, options)
	if err != nil {
		return nil, nil, err
	}
	resp, err := s.client.Do(req, nil)
	if err != nil {
		// incase of error return the resp for further inspection
		return nil, resp, err
	}

	component := new(FullComponent)
	defer resp.Body.Close()
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, resp, fmt.Errorf("Could not read the returned data")
	}
	err = json.Unmarshal(data, component)
	if err != nil {
		return nil, resp, fmt.Errorf("Could not unmarshall the data into struct")
	}
	return component, resp, nil
}
<|endoftext|>


<commit_before>package microsoft

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/poorny/utils/cache"
)

const (
	datamarket        = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13"
	scope             = "http://api.microsofttranslator.com"
	translateUrl      = "http://api.microsofttranslator.com/v2/Http.svc/Translate"
	translateArrayUrl = "http://api.microsofttranslator.com/V2/Http.svc/TranslateArray"
	detectArrayUrl    = "http://api.microsofttranslator.com/V2/Http.svc/DetectArray"
	grant_type        = "client_credentials"
	xmlArrayTemplate  = `<TranslateArrayRequest>
						<AppId />
						<From>%s</From>
						<Options>
							<Category xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2" ></Category>
							<ContentType xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2">text/plain</ContentType>
							<ReservedFlags xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2" />
							<State xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2"></State>
							<Uri xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2"></Uri>
							<User xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2"></User>
						</Options>
						<Texts>
							%s
						</Texts>
						<To>%s</To>
					</TranslateArrayRequest>`
	templateToTranslate    = `<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">%s</string>`
	xmlDetectArrayTemplate = `<ArrayOfstring xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">%s</ArrayOfstring>`
)

type Access interface {
	GetAccessToken() TokenResponse
}

type Translator interface {
	Translate() (string, error)
	TranslateArray() ([]string, error)
	DetectTextArray() ([]string, error)
	CheckTimeout() bool
}

type AuthRequest struct {
	ClientId     string
	ClientSecret string
}

type TextTranslate struct {
	Text  string
	Texts []string
	From  string
	To    string
	Cache bool

	TokenResponse
}

type TokenResponse struct {
	AccessToken string    `json:"access_token"`
	TokenType   string    `json:"token_type,omitempty"`
	ExpiresIn   string    `json:"expires_in"`
	Scope       string    `json:"scope"`
	Timeout     time.Time `json:"timeout"`
}

type TranslateResponse struct {
	Resp []ArrayResp `xml:"TranslateArrayResponse"`
}

type ArrayResp struct {
	Text string `xml:"TranslatedText"`
}

func GetAccessToken(access Access) TokenResponse {
	return access.GetAccessToken()
}

func TranslateText(t Translator) (string, error) {
	if t.CheckTimeout() == false {
		return t.Translate()
	}
	return "", errors.New("Access token is invalid, please get new token")
}

func TranslateTexts(t Translator) ([]string, error) {
	if t.CheckTimeout() == false {
		return t.TranslateArray()
	}
	return []string{}, errors.New("Access token is invalid, please get new token")
}

func DetectText(t Translator) ([]string, error) {
	if t.CheckTimeout() == false {
		return t.DetectTextArray()
	}
	return []string{}, errors.New("Access token is invalid, please get new token")
}

func rdbCache() *redis.Redis {
	conn := redis.Connection{"tcp", ":6379", "7"}
	rdb, err := conn.Dial()
	if err != nil {
		log.Fatal(err)
	}
	return rdb
}

// Make a POST request to `datamark` url for getting access token
func (a *AuthRequest) GetAccessToken() TokenResponse {
	client := &http.Client{}

	postValues := url.Values{}
	postValues.Add("client_id", a.ClientId)
	postValues.Add("client_secret", a.ClientSecret)
	postValues.Add("scope", scope)
	postValues.Add("grant_type", grant_type)

	req, err := http.NewRequest("POST", datamarket, strings.NewReader(postValues.Encode()))
	if err != nil {
		log.Println(err)
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}

	var tr TokenResponse
	err = json.Unmarshal(body, &tr)
	if err != nil {
		log.Println(err)
	}

	now := time.Now()
	expires_in, err := strconv.ParseInt(tr.ExpiresIn, 10, 0)
	if err != nil {
		log.Println(err)
	}

	exp_time := now.Add(time.Duration(expires_in) * time.Second)
	// 10 min
	tr.Timeout = exp_time
	return tr
}

// Return `t.Text` in `t.From` language translated for `t.To` language
func (t *TextTranslate) Translate() (string, error) {

	if t.Cache == true {
		rdb := rdbCache()
		exists, _ := rdb.Exists(t.Text)
		if exists == false {
			result, _ := rdb.Get(t.Text)
			rdb.Conn.Close()
			return result, nil
		}
	}
	textEncode := url.Values{}
	textEncode.Add("text", t.Text)
	text := textEncode.Encode()

	url := fmt.Sprintf("%s?%s&from=%s&to=%s", translateUrl, text, t.From, t.To)

	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		log.Println(err)
	}
	auth_token := fmt.Sprintf("Bearer %s", t.TokenResponse.AccessToken)
	req.Header.Add("Authorization", auth_token)

	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}

	type Text struct {
		T string `xml:",chardata"`
	}

	var obj Text
	err = xml.Unmarshal(body, &obj)
	if t.Cache == true {
		rdb := rdbCache()
		rdb.Set(t.Text, obj.T)
		rdb.Conn.Close()
	}

	return obj.T, nil
}

// Return `t.Texts` array in `t.From` language translated for `t.To` language
func (t *TextTranslate) TranslateArray() ([]string, error) {
	response := []string{}
	toTranslate := make([]string, len(t.Texts))

	// Simulate possible indexes of array response from microsoft.
	var notCached map[string]int
	count := 0

	if t.Cache == true {
		rdb := rdbCache()
		for _, tx := range t.Texts {
			exs, _ := rdb.Exists(tx)

			if exs == true {
				res, err := rdb.Get(tx)
				if err != nil {
					log.Fatal(err)
				}
				response = append(response, res)
			} else {
				ts := fmt.Sprintf(templateToTranslate, tx)
				toTranslate = append(toTranslate, ts)
				notCached[tx] = count
				count++
			}
		}
		rdb.Conn.Close()
	} else {
		for _, text := range t.Texts {
			tx := fmt.Sprintf(templateToTranslate, text)
			toTranslate = append(toTranslate, tx)
		}
	}

	if len(response) == len(t.Texts) {
		return response, nil
	}

	textToTranslate := strings.Join(toTranslate, "\n")
	bodyReq := fmt.Sprintf(xmlArrayTemplate, t.From, textToTranslate, t.To)

	client := &http.Client{}
	req, err := http.NewRequest("POST", translateArrayUrl, strings.NewReader(bodyReq))
	if err != nil {
		log.Println(err)
	}

	auth_token := fmt.Sprintf("Bearer %s", t.TokenResponse.AccessToken)
	req.Header.Add("Authorization", auth_token)
	req.Header.Add("Content-Type", "text/xml")

	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}

	var obj TranslateResponse
	err = xml.Unmarshal(body, &obj)
	if err != nil {
		log.Println(err)
	}

	for _, r := range obj.Resp {
		response = append(response, r.Text)
	}

	if t.Cache == true {
		rdb := rdbCache()
		for key, indx := range notCached {
			err = rdb.Set(key, obj.Resp[indx].Text)
			if err != nil {
				log.Println(err)
			}
		}
		rdb.Conn.Close()
	}

	return response, nil
}

// Detects the language of the text passed in the array `t.Texts`
func (t *TextTranslate) DetectTextArray() ([]string, error) {
	response := []string{}
	toTranslate := make([]string, len(t.Texts))

	for _, text := range t.Texts {
		tx := fmt.Sprintf(templateToTranslate, text)
		toTranslate = append(toTranslate, tx)
	}
	textToTranslate := strings.Join(toTranslate, "\n")
	bodyReq := fmt.Sprintf(xmlDetectArrayTemplate, textToTranslate)

	client := &http.Client{}
	req, err := http.NewRequest("POST", detectArrayUrl, strings.NewReader(bodyReq))
	if err != nil {
		log.Println(err)
	}

	auth_token := fmt.Sprintf("Bearer %s", t.TokenResponse.AccessToken)
	req.Header.Add("Authorization", auth_token)
	req.Header.Add("Content-Type", "text/xml")

	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}
	type LangArray struct {
		Array []string `xml:"string"`
	}

	var obj LangArray
	err = xml.Unmarshal(body, &obj)
	if err != nil {
		log.Println(err)
	}

	response = append(response, obj.Array...)
	return response, nil
}

// Verify if access token is valid
func (t *TokenResponse) CheckTimeout() bool {
	if time.Since(t.Timeout) > 0 {
		return true
	}
	return false
}
<commit_msg>change variables names<commit_after>package microsoft

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/poorny/utils/cache"
)

const (
	datamarket        = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13"
	scope             = "http://api.microsofttranslator.com"
	translateURL      = "http://api.microsofttranslator.com/v2/Http.svc/Translate"
	translateArrayURL = "http://api.microsofttranslator.com/V2/Http.svc/TranslateArray"
	detectArrayURL    = "http://api.microsofttranslator.com/V2/Http.svc/DetectArray"
	grantType         = "client_credentials"
	xmlArrayTemplate  = `<TranslateArrayRequest>
						<AppId />
						<From>%s</From>
						<Options>
							<Category xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2" ></Category>
							<ContentType xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2">text/plain</ContentType>
							<ReservedFlags xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2" />
							<State xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2"></State>
							<Uri xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2"></Uri>
							<User xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2"></User>
						</Options>
						<Texts>
							%s
						</Texts>
						<To>%s</To>
					</TranslateArrayRequest>`
	templateToTranslate    = `<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">%s</string>`
	xmlDetectArrayTemplate = `<ArrayOfstring xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">%s</ArrayOfstring>`
)

type Access interface {
	GetAccessToken() TokenResponse
}

type Translator interface {
	Translate() (string, error)
	TranslateArray() ([]string, error)
	DetectTextArray() ([]string, error)
	CheckTimeout() bool
}

type AuthRequest struct {
	ClientID     string
	ClientSecret string
}

type TextTranslate struct {
	Text  string
	Texts []string
	From  string
	To    string
	Cache bool

	TokenResponse
}

type TokenResponse struct {
	AccessToken string    `json:"access_token"`
	TokenType   string    `json:"token_type,omitempty"`
	ExpiresIn   string    `json:"expires_in"`
	Scope       string    `json:"scope"`
	Timeout     time.Time `json:"timeout"`
}

type TranslateResponse struct {
	Resp []ArrayResp `xml:"TranslateArrayResponse"`
}

type ArrayResp struct {
	Text string `xml:"TranslatedText"`
}

func GetAccessToken(access Access) TokenResponse {
	return access.GetAccessToken()
}

func TranslateText(t Translator) (string, error) {
	if t.CheckTimeout() == false {
		return t.Translate()
	}
	return "", errors.New("Access token is invalid, please get new token")
}

func TranslateTexts(t Translator) ([]string, error) {
	if t.CheckTimeout() == false {
		return t.TranslateArray()
	}
	return []string{}, errors.New("Access token is invalid, please get new token")
}

func DetectText(t Translator) ([]string, error) {
	if t.CheckTimeout() == false {
		return t.DetectTextArray()
	}
	return []string{}, errors.New("Access token is invalid, please get new token")
}

func rdbCache() *redis.Redis {
	conn := redis.Connection{"tcp", ":6379", "7"}
	rdb, err := conn.Dial()
	if err != nil {
		log.Fatal(err)
	}
	return rdb
}

// Make a POST request to `datamark` url for getting access token
func (a *AuthRequest) GetAccessToken() TokenResponse {
	client := &http.Client{}

	postValues := url.Values{}
	postValues.Add("client_id", a.ClientID)
	postValues.Add("client_secret", a.ClientSecret)
	postValues.Add("scope", scope)
	postValues.Add("grant_type", grantType)

	req, err := http.NewRequest("POST", datamarket, strings.NewReader(postValues.Encode()))
	if err != nil {
		log.Println(err)
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}

	var tr TokenResponse
	err = json.Unmarshal(body, &tr)
	if err != nil {
		log.Println(err)
	}

	now := time.Now()
	expiresIn, err := strconv.ParseInt(tr.ExpiresIn, 10, 0)
	if err != nil {
		log.Println(err)
	}

	expTime := now.Add(time.Duration(expiresIn) * time.Second)
	// 10 min
	tr.Timeout = expTime
	return tr
}

// Return `t.Text` in `t.From` language translated for `t.To` language
func (t *TextTranslate) Translate() (string, error) {

	if t.Cache == true {
		rdb := rdbCache()
		exists, _ := rdb.Exists(t.Text)
		if exists == false {
			result, _ := rdb.Get(t.Text)
			rdb.Conn.Close()
			return result, nil
		}
	}
	textEncode := url.Values{}
	textEncode.Add("text", t.Text)
	text := textEncode.Encode()

	url := fmt.Sprintf("%s?%s&from=%s&to=%s", translateURL, text, t.From, t.To)

	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		log.Println(err)
	}
	authToken := fmt.Sprintf("Bearer %s", t.TokenResponse.AccessToken)
	req.Header.Add("Authorization", authToken)

	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}

	type Text struct {
		T string `xml:",chardata"`
	}

	var obj Text
	err = xml.Unmarshal(body, &obj)
	if t.Cache == true {
		rdb := rdbCache()
		rdb.Set(t.Text, obj.T)
		rdb.Conn.Close()
	}

	return obj.T, nil
}

// Return `t.Texts` array in `t.From` language translated for `t.To` language
func (t *TextTranslate) TranslateArray() ([]string, error) {
	response := []string{}
	toTranslate := make([]string, len(t.Texts))

	// Simulate possible indexes of array response from microsoft.
	var notCached map[string]int
	count := 0

	if t.Cache == true {
		rdb := rdbCache()
		for _, tx := range t.Texts {
			exs, _ := rdb.Exists(tx)

			if exs == true {
				res, err := rdb.Get(tx)
				if err != nil {
					log.Fatal(err)
				}
				response = append(response, res)
			} else {
				ts := fmt.Sprintf(templateToTranslate, tx)
				toTranslate = append(toTranslate, ts)
				notCached[tx] = count
				count++
			}
		}
		rdb.Conn.Close()
	} else {
		for _, text := range t.Texts {
			tx := fmt.Sprintf(templateToTranslate, text)
			toTranslate = append(toTranslate, tx)
		}
	}

	if len(response) == len(t.Texts) {
		return response, nil
	}

	textToTranslate := strings.Join(toTranslate, "\n")
	bodyReq := fmt.Sprintf(xmlArrayTemplate, t.From, textToTranslate, t.To)

	client := &http.Client{}
	req, err := http.NewRequest("POST", translateArrayURL, strings.NewReader(bodyReq))
	if err != nil {
		log.Println(err)
	}

	authToken := fmt.Sprintf("Bearer %s", t.TokenResponse.AccessToken)
	req.Header.Add("Authorization", authToken)
	req.Header.Add("Content-Type", "text/xml")

	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}

	var obj TranslateResponse
	err = xml.Unmarshal(body, &obj)
	if err != nil {
		log.Println(err)
	}

	for _, r := range obj.Resp {
		response = append(response, r.Text)
	}

	if t.Cache == true {
		rdb := rdbCache()
		for key, indx := range notCached {
			err = rdb.Set(key, obj.Resp[indx].Text)
			if err != nil {
				log.Println(err)
			}
		}
		rdb.Conn.Close()
	}

	return response, nil
}

// Detects the language of the text passed in the array `t.Texts`
func (t *TextTranslate) DetectTextArray() ([]string, error) {
	response := []string{}
	toTranslate := make([]string, len(t.Texts))

	for _, text := range t.Texts {
		tx := fmt.Sprintf(templateToTranslate, text)
		toTranslate = append(toTranslate, tx)
	}
	textToTranslate := strings.Join(toTranslate, "\n")
	bodyReq := fmt.Sprintf(xmlDetectArrayTemplate, textToTranslate)

	client := &http.Client{}
	req, err := http.NewRequest("POST", detectArrayURL, strings.NewReader(bodyReq))
	if err != nil {
		log.Println(err)
	}

	authToken := fmt.Sprintf("Bearer %s", t.TokenResponse.AccessToken)
	req.Header.Add("Authorization", authToken)
	req.Header.Add("Content-Type", "text/xml")

	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}
	type LangArray struct {
		Array []string `xml:"string"`
	}

	var obj LangArray
	err = xml.Unmarshal(body, &obj)
	if err != nil {
		log.Println(err)
	}

	response = append(response, obj.Array...)
	return response, nil
}

// Verify if access token is valid
func (t *TokenResponse) CheckTimeout() bool {
	if time.Since(t.Timeout) > 0 {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>// Copyright 2014 The Serviced Authors.
// 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 cmd

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"syscall"

	"golang.org/x/crypto/ssh/terminal"
)

func remove(index int, list ...interface{}) []interface{} {
	var (
		left  []interface{}
		right []interface{}
	)

	switch {
	case index < 0 || index > len(list):
		panic("index out of bounds")
	case index+1 < len(list):
		right = list[index+1:]
		fallthrough
	default:
		left = list[:index]
	}

	return append(left, right...)
}

var editors = []string{"vim", "vi", "nano"}

func findEditor(editor string) (string, error) {
	if editor != "" {
		path, err := exec.LookPath(editor)
		if err != nil {
			return "", fmt.Errorf("editor (%s) not found: %s", editor, err)
		}
		return path, nil
	}
	for _, e := range editors {
		path, err := exec.LookPath(e)
		if err == nil {
			return path, nil
		}
	}
	return "", fmt.Errorf("no editor found")
}

func openEditor(data []byte, name, editor string) (reader io.Reader, err error) {
	if terminal.IsTerminal(syscall.Stdin) {
		editor, err := findEditor(editor)
		if err != nil {
			return nil, err
		}

		f, err := ioutil.TempFile("", name+"_")
		if err != nil {
			return nil, fmt.Errorf("could not open tempfile: %s", err)
		}
		defer os.Remove(f.Name())
		defer f.Close()

		if _, err := f.Write(data); err != nil {
			return nil, fmt.Errorf("could not write tempfile: %s", err)
		}

		e := exec.Command(editor, f.Name())
		e.Stdin = os.Stdin
		e.Stdout = os.Stdout
		e.Stderr = os.Stderr

		if err := e.Run(); err != nil {
			return nil, fmt.Errorf("received error from editor: %s (%s)", err, editor)
		}
		if _, err := f.Seek(0, 0); err != nil {
			return nil, fmt.Errorf("could not seek file: %s", err)
		}

		data, err := ioutil.ReadAll(f)
		if err != nil {
			return nil, fmt.Errorf("could not read file: %s", err)
		}
		reader = bytes.NewReader(data)
	} else {
		if _, err := os.Stdout.Write(data); err != nil {
			return nil, fmt.Errorf("could not write to stdout: %s", err)
		}
		reader = os.Stdin
	}

	return reader, nil
}
<commit_msg>allow scripts to edit services using EDITOR<commit_after>// Copyright 2014 The Serviced Authors.
// 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 cmd

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"syscall"

	"golang.org/x/crypto/ssh/terminal"
)

func remove(index int, list ...interface{}) []interface{} {
	var (
		left  []interface{}
		right []interface{}
	)

	switch {
	case index < 0 || index > len(list):
		panic("index out of bounds")
	case index+1 < len(list):
		right = list[index+1:]
		fallthrough
	default:
		left = list[:index]
	}

	return append(left, right...)
}

var editors = []string{"vim", "vi", "nano"}

func findEditor(editor string) (string, error) {
	if editor != "" {
		path, err := exec.LookPath(editor)
		if err != nil {
			return "", fmt.Errorf("editor (%s) not found: %s", editor, err)
		}
		return path, nil
	}
	for _, e := range editors {
		path, err := exec.LookPath(e)
		if err == nil {
			return path, nil
		}
	}
	return "", fmt.Errorf("no editor found")
}

func openEditor(data []byte, name, editor string) (reader io.Reader, err error) {
	if terminal.IsTerminal(syscall.Stdin) {
		editor, err := findEditor(editor)
		if err != nil {
			return nil, err
		}

		f, err := ioutil.TempFile("", name+"_")
		if err != nil {
			return nil, fmt.Errorf("could not open tempfile: %s", err)
		}
		defer os.Remove(f.Name())
		defer f.Close()

		if _, err := f.Write(data); err != nil {
			return nil, fmt.Errorf("could not write tempfile: %s", err)
		}

		e := exec.Command(editor, f.Name())
		e.Stdin = os.Stdin
		e.Stdout = os.Stdout
		e.Stderr = os.Stderr

		if err := e.Run(); err != nil {
			return nil, fmt.Errorf("received error from editor: %s (%s)", err, editor)
		}
		if _, err := f.Seek(0, 0); err != nil {
			return nil, fmt.Errorf("could not seek file: %s", err)
		}

		data, err := ioutil.ReadFile(f.Name())
		if err != nil {
			return nil, fmt.Errorf("could not read file: %s", err)
		}

		reader = bytes.NewReader(data)
	} else {
		if _, err := os.Stdout.Write(data); err != nil {
			return nil, fmt.Errorf("could not write to stdout: %s", err)
		}
		reader = os.Stdin
	}

	return reader, nil
}
<|endoftext|>