<commit_before>package gucumber

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"

	"github.com/lsegal/gucumber/gherkin"
	"github.com/shiena/ansicolor"
)

const (
	clrWhite  = "0"
	clrRed    = "31"
	clrGreen  = "32"
	clrYellow = "33"
	clrCyan   = "36"

	txtUnmatchInt   = `(\d+)`
	txtUnmatchFloat = `(-?\d+(?:\.\d+)?)`
	txtUnmatchStr   = `"(.+?)"`
)

var (
	reUnmatchInt   = regexp.MustCompile(txtUnmatchInt)
	reUnmatchFloat = regexp.MustCompile(txtUnmatchFloat)
	reUnmatchStr   = regexp.MustCompile(`(<|").+?("|>)`)
	reOutlineVal   = regexp.MustCompile(`<(.+?)>`)
)

type Runner struct {
	*Context
	Features  []*gherkin.Feature
	Results   []RunnerResult
	Unmatched []*gherkin.Step
	FailCount int
	SkipCount int
}

type RunnerResult struct {
	*TestingT
	*gherkin.Feature
	*gherkin.Scenario
}

func (c *Context) RunDir(dir string) (*Runner, error) {
	g, _ := filepath.Glob(filepath.Join(dir, "*.feature"))
	g2, _ := filepath.Glob(filepath.Join(dir, "**", "*.feature"))
	g = append(g, g2...)

	runner, err := c.RunFiles(g)
	if err != nil {
		panic(err)
	}

	if len(runner.Unmatched) > 0 {
		fmt.Println("Some steps were missing, you can add them by using the following step definition stubs: ")
		fmt.Println("")
		fmt.Print(runner.MissingMatcherStubs())
	}

	os.Exit(runner.FailCount)
	return runner, err
}

func (c *Context) RunFiles(featureFiles []string) (*Runner, error) {
	r := Runner{
		Context:   c,
		Features:  []*gherkin.Feature{},
		Results:   []RunnerResult{},
		Unmatched: []*gherkin.Step{},
	}

	for _, file := range featureFiles {
		fd, err := os.Open(file)
		if err != nil {
			return nil, err
		}
		defer fd.Close()

		b, err := ioutil.ReadAll(fd)
		if err != nil {
			return nil, err
		}

		fs, err := gherkin.ParseFilename(string(b), file)
		if err != nil {
			return nil, err
		}

		for _, f := range fs {
			r.Features = append(r.Features, &f)
		}
	}

	r.run()
	return &r, nil
}

func (c *Runner) MissingMatcherStubs() string {
	var buf bytes.Buffer
	matches := map[string]bool{}

	buf.WriteString(`import . "github.com/lsegal/gucumber"` + "\n\n")
	buf.WriteString("func init() {\n")

	for _, m := range c.Unmatched {
		numInts, numFloats, numStrs := 1, 1, 1
		str, args := m.Text, []string{}
		str = reUnmatchInt.ReplaceAllStringFunc(str, func(s string) string {
			args = append(args, fmt.Sprintf("i%d int", numInts))
			numInts++
			return txtUnmatchInt
		})
		str = reUnmatchFloat.ReplaceAllStringFunc(str, func(s string) string {
			args = append(args, fmt.Sprintf("s%d float64", numFloats))
			numFloats++
			return txtUnmatchFloat
		})
		str = reUnmatchStr.ReplaceAllStringFunc(str, func(s string) string {
			args = append(args, fmt.Sprintf("s%d string", numStrs))
			numStrs++
			return txtUnmatchStr
		})

		if m.Argument.IsTabular() {
			args = append(args, "table [][]string")
		} else {
			args = append(args, "data string")
		}

		// Don't duplicate matchers. This is mostly for scenario outlines.
		if matches[str] {
			continue
		}
		matches[str] = true

		fmt.Fprintf(&buf, "\t%s(`^%s$`, func(%s) {\n\t\tT.Skip() // pending\n\t})\n\n",
			m.Type, str, strings.Join(args, ", "))
	}

	buf.WriteString("}\n")
	return buf.String()
}

func (c *Runner) run() {
	if c.BeforeAllFilter != nil {
		c.BeforeAllFilter()
	}
	for _, f := range c.Features {
		c.runFeature(f)
	}
	if c.AfterAllFilter != nil {
		c.AfterAllFilter()
	}

	c.line("0;1", "Finished (%d passed, %d failed, %d skipped).\n",
		len(c.Results)-c.FailCount-c.SkipCount, c.FailCount, c.SkipCount)
}

func (c *Runner) runFeature(f *gherkin.Feature) {
	if !f.FilterMatched(c.Filters...) {
		result := false

		// if any scenarios match, we will run those
		for _, s := range f.Scenarios {
			if s.FilterMatched(c.Filters...) {
				result = true
				break
			}
		}

		if !result {
			return
		}
	}

	for k, fn := range c.BeforeFilters {
		if f.FilterMatched(strings.Split(k, "|")...) {
			fn()
		}
	}

	if len(f.Tags) > 0 {
		c.line(clrCyan, strings.Join([]string(f.Tags), " "))
	}
	c.line("0;1", "Feature: %s", f.Title)

	if f.Background.Steps != nil {
		c.runScenario("Background", f, &f.Background, false)
	}

	for _, s := range f.Scenarios {
		c.runScenario("Scenario", f, &s, false)
	}

	for k, fn := range c.AfterFilters {
		if f.FilterMatched(strings.Split(k, "|")...) {
			fn()
		}
	}
}

func (c *Runner) runScenario(title string, f *gherkin.Feature, s *gherkin.Scenario, isExample bool) {
	if !s.FilterMatched(c.Filters...) {
		return
	}

	for k, fn := range c.BeforeFilters {
		if s.FilterMatched(strings.Split(k, "|")...) {
			fn()
		}
	}

	if s.Examples != "" { // run scenario outline data
		exrows := strings.Split(string(s.Examples), "\n")

		c.line(clrCyan, "  "+strings.Join([]string(s.Tags), " "))
		c.fileLine("0;1", "  %s Outline: %s", s.Filename, s.Line, s.LongestLine()+1,
			title, s.Title)

		for _, step := range s.Steps {
			c.fileLine("0;0", "    %s %s", step.Filename, step.Line,
				s.LongestLine()+1, step.Type, step.Text)
		}

		c.line(clrWhite, "")
		c.line("0;1", "  Examples:")
		c.line(clrCyan, "    %s", exrows[0])

		tab := s.Examples.ToTable()
		tabmap := tab.ToMap()
		for i, rows := 1, len(tab); i < rows; i++ {
			other := gherkin.Scenario{
				Filename: s.Filename,
				Line:     s.Line,
				Title:    s.Title,
				Examples: gherkin.StringData(""),
				Steps:    []gherkin.Step{},
			}

			for _, step := range s.Steps {
				step.Text = reOutlineVal.ReplaceAllStringFunc(step.Text, func(t string) string {
					return tabmap[t[1:len(t)-1]][i-1]
				})
				other.Steps = append(other.Steps, step)
			}

			fc := c.FailCount
			clr := clrGreen
			c.runScenario(title, f, &other, true)

			if fc != c.FailCount {
				clr = clrRed
			}

			c.line(clr, "    %s", exrows[i])
		}
		c.line(clrWhite, "")

		for k, fn := range c.AfterFilters {
			if s.FilterMatched(strings.Split(k, "|")...) {
				fn()
			}
		}

		return
	}

	t := &TestingT{}
	skipping := false
	clr := clrGreen

	if !isExample {
		if len(s.Tags) > 0 {
			c.line(clrCyan, "  "+strings.Join([]string(s.Tags), " "))
		}
		c.fileLine("0;1", "  %s: %s", s.Filename, s.Line, s.LongestLine(),
			title, s.Title)
	}

	for _, step := range s.Steps {
		errCount := len(t.errors)
		found := false
		if !skipping {
			done := make(chan bool)
			go func() {
				defer func() {
					c.Results = append(c.Results, RunnerResult{t, f, s})

					if t.Skipped() {
						c.SkipCount++
						skipping = true
						clr = clrYellow
					} else if t.Failed() {
						c.FailCount++
						clr = clrRed
					}

					done <- true
				}()

				f, err := c.Execute(t, step.Text, string(step.Argument))
				if err != nil {
					t.Error(err)
				}
				found = f

				if !f {
					t.Skip("no match function for step")
				}
			}()
			<-done
		}

		if skipping && !found {
			cstep := step
			c.Unmatched = append(c.Unmatched, &cstep)
		}

		if !isExample {
			c.fileLine(clr, "    %s %s", step.Filename, step.Line,
				s.LongestLine(), step.Type, step.Text)
			if len(step.Argument) > 0 {
				if !step.Argument.IsTabular() {
					c.line(clrWhite, `      """`)
				}
				for _, l := range strings.Split(string(step.Argument), "\n") {
					c.line(clrWhite, "      %s", l)
				}
				if !step.Argument.IsTabular() {
					c.line(clrWhite, `      """`)
				}
			}
		}

		if len(t.errors) > errCount {
			c.line(clrRed, "\n"+t.errors[len(t.errors)-1].message)
		}
	}
	if !isExample {
		c.line(clrWhite, "")
	}

	for k, fn := range c.AfterFilters {
		if s.FilterMatched(strings.Split(k, "|")...) {
			fn()
		}
	}
}

var writer = ansicolor.NewAnsiColorWriter(os.Stdout)

func (c *Runner) line(clr, text string, args ...interface{}) {
	fmt.Fprintf(writer, "\033[%sm%s\033[0;0m\n", clr, fmt.Sprintf(text, args...))
}

func (c *Runner) fileLine(clr, text, filename string, line int, max int, args ...interface{}) {
	space, str := "", fmt.Sprintf(text, args...)
	if l := max + 5 - len(str); l > 0 {
		space = strings.Repeat(" ", l)
	}
	comment := fmt.Sprintf("%s \033[39;0m# %s:%d", space, filename, line)
	c.line(clr, "%s%s", str, comment)
}

type Tester interface {
	Errorf(format string, args ...interface{})
}

type TestingT struct {
	skipped bool
	errors  []TestError
}

type TestError struct {
	message string
	stack   []byte
}

func (t *TestingT) Errorf(format string, args ...interface{}) {
	var buf bytes.Buffer

	str := fmt.Sprintf(format, args...)
	sbuf := make([]byte, 8192)
	for {
		size := runtime.Stack(sbuf, false)
		if size < len(sbuf) {
			break
		}
		buf.Write(sbuf[0:size])
	}

	t.errors = append(t.errors, TestError{message: str, stack: buf.Bytes()})
}

func (t *TestingT) Skip(args ...interface{}) {
	t.skipped = true
}

func (t *TestingT) Skipped() bool {
	return t.skipped
}

func (t *TestingT) Failed() bool {
	return len(t.errors) > 0
}

func (t *TestingT) Error(err error) {
	t.errors = append(t.errors, TestError{message: err.Error()})
}
<commit_msg>Add Skip() to Tester interface<commit_after>package gucumber

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"

	"github.com/lsegal/gucumber/gherkin"
	"github.com/shiena/ansicolor"
)

const (
	clrWhite  = "0"
	clrRed    = "31"
	clrGreen  = "32"
	clrYellow = "33"
	clrCyan   = "36"

	txtUnmatchInt   = `(\d+)`
	txtUnmatchFloat = `(-?\d+(?:\.\d+)?)`
	txtUnmatchStr   = `"(.+?)"`
)

var (
	reUnmatchInt   = regexp.MustCompile(txtUnmatchInt)
	reUnmatchFloat = regexp.MustCompile(txtUnmatchFloat)
	reUnmatchStr   = regexp.MustCompile(`(<|").+?("|>)`)
	reOutlineVal   = regexp.MustCompile(`<(.+?)>`)
)

type Runner struct {
	*Context
	Features  []*gherkin.Feature
	Results   []RunnerResult
	Unmatched []*gherkin.Step
	FailCount int
	SkipCount int
}

type RunnerResult struct {
	*TestingT
	*gherkin.Feature
	*gherkin.Scenario
}

func (c *Context) RunDir(dir string) (*Runner, error) {
	g, _ := filepath.Glob(filepath.Join(dir, "*.feature"))
	g2, _ := filepath.Glob(filepath.Join(dir, "**", "*.feature"))
	g = append(g, g2...)

	runner, err := c.RunFiles(g)
	if err != nil {
		panic(err)
	}

	if len(runner.Unmatched) > 0 {
		fmt.Println("Some steps were missing, you can add them by using the following step definition stubs: ")
		fmt.Println("")
		fmt.Print(runner.MissingMatcherStubs())
	}

	os.Exit(runner.FailCount)
	return runner, err
}

func (c *Context) RunFiles(featureFiles []string) (*Runner, error) {
	r := Runner{
		Context:   c,
		Features:  []*gherkin.Feature{},
		Results:   []RunnerResult{},
		Unmatched: []*gherkin.Step{},
	}

	for _, file := range featureFiles {
		fd, err := os.Open(file)
		if err != nil {
			return nil, err
		}
		defer fd.Close()

		b, err := ioutil.ReadAll(fd)
		if err != nil {
			return nil, err
		}

		fs, err := gherkin.ParseFilename(string(b), file)
		if err != nil {
			return nil, err
		}

		for _, f := range fs {
			r.Features = append(r.Features, &f)
		}
	}

	r.run()
	return &r, nil
}

func (c *Runner) MissingMatcherStubs() string {
	var buf bytes.Buffer
	matches := map[string]bool{}

	buf.WriteString(`import . "github.com/lsegal/gucumber"` + "\n\n")
	buf.WriteString("func init() {\n")

	for _, m := range c.Unmatched {
		numInts, numFloats, numStrs := 1, 1, 1
		str, args := m.Text, []string{}
		str = reUnmatchInt.ReplaceAllStringFunc(str, func(s string) string {
			args = append(args, fmt.Sprintf("i%d int", numInts))
			numInts++
			return txtUnmatchInt
		})
		str = reUnmatchFloat.ReplaceAllStringFunc(str, func(s string) string {
			args = append(args, fmt.Sprintf("s%d float64", numFloats))
			numFloats++
			return txtUnmatchFloat
		})
		str = reUnmatchStr.ReplaceAllStringFunc(str, func(s string) string {
			args = append(args, fmt.Sprintf("s%d string", numStrs))
			numStrs++
			return txtUnmatchStr
		})

		if m.Argument.IsTabular() {
			args = append(args, "table [][]string")
		} else {
			args = append(args, "data string")
		}

		// Don't duplicate matchers. This is mostly for scenario outlines.
		if matches[str] {
			continue
		}
		matches[str] = true

		fmt.Fprintf(&buf, "\t%s(`^%s$`, func(%s) {\n\t\tT.Skip() // pending\n\t})\n\n",
			m.Type, str, strings.Join(args, ", "))
	}

	buf.WriteString("}\n")
	return buf.String()
}

func (c *Runner) run() {
	if c.BeforeAllFilter != nil {
		c.BeforeAllFilter()
	}
	for _, f := range c.Features {
		c.runFeature(f)
	}
	if c.AfterAllFilter != nil {
		c.AfterAllFilter()
	}

	c.line("0;1", "Finished (%d passed, %d failed, %d skipped).\n",
		len(c.Results)-c.FailCount-c.SkipCount, c.FailCount, c.SkipCount)
}

func (c *Runner) runFeature(f *gherkin.Feature) {
	if !f.FilterMatched(c.Filters...) {
		result := false

		// if any scenarios match, we will run those
		for _, s := range f.Scenarios {
			if s.FilterMatched(c.Filters...) {
				result = true
				break
			}
		}

		if !result {
			return
		}
	}

	for k, fn := range c.BeforeFilters {
		if f.FilterMatched(strings.Split(k, "|")...) {
			fn()
		}
	}

	if len(f.Tags) > 0 {
		c.line(clrCyan, strings.Join([]string(f.Tags), " "))
	}
	c.line("0;1", "Feature: %s", f.Title)

	if f.Background.Steps != nil {
		c.runScenario("Background", f, &f.Background, false)
	}

	for _, s := range f.Scenarios {
		c.runScenario("Scenario", f, &s, false)
	}

	for k, fn := range c.AfterFilters {
		if f.FilterMatched(strings.Split(k, "|")...) {
			fn()
		}
	}
}

func (c *Runner) runScenario(title string, f *gherkin.Feature, s *gherkin.Scenario, isExample bool) {
	if !s.FilterMatched(c.Filters...) {
		return
	}

	for k, fn := range c.BeforeFilters {
		if s.FilterMatched(strings.Split(k, "|")...) {
			fn()
		}
	}

	if s.Examples != "" { // run scenario outline data
		exrows := strings.Split(string(s.Examples), "\n")

		c.line(clrCyan, "  "+strings.Join([]string(s.Tags), " "))
		c.fileLine("0;1", "  %s Outline: %s", s.Filename, s.Line, s.LongestLine()+1,
			title, s.Title)

		for _, step := range s.Steps {
			c.fileLine("0;0", "    %s %s", step.Filename, step.Line,
				s.LongestLine()+1, step.Type, step.Text)
		}

		c.line(clrWhite, "")
		c.line("0;1", "  Examples:")
		c.line(clrCyan, "    %s", exrows[0])

		tab := s.Examples.ToTable()
		tabmap := tab.ToMap()
		for i, rows := 1, len(tab); i < rows; i++ {
			other := gherkin.Scenario{
				Filename: s.Filename,
				Line:     s.Line,
				Title:    s.Title,
				Examples: gherkin.StringData(""),
				Steps:    []gherkin.Step{},
			}

			for _, step := range s.Steps {
				step.Text = reOutlineVal.ReplaceAllStringFunc(step.Text, func(t string) string {
					return tabmap[t[1:len(t)-1]][i-1]
				})
				other.Steps = append(other.Steps, step)
			}

			fc := c.FailCount
			clr := clrGreen
			c.runScenario(title, f, &other, true)

			if fc != c.FailCount {
				clr = clrRed
			}

			c.line(clr, "    %s", exrows[i])
		}
		c.line(clrWhite, "")

		for k, fn := range c.AfterFilters {
			if s.FilterMatched(strings.Split(k, "|")...) {
				fn()
			}
		}

		return
	}

	t := &TestingT{}
	skipping := false
	clr := clrGreen

	if !isExample {
		if len(s.Tags) > 0 {
			c.line(clrCyan, "  "+strings.Join([]string(s.Tags), " "))
		}
		c.fileLine("0;1", "  %s: %s", s.Filename, s.Line, s.LongestLine(),
			title, s.Title)
	}

	for _, step := range s.Steps {
		errCount := len(t.errors)
		found := false
		if !skipping {
			done := make(chan bool)
			go func() {
				defer func() {
					c.Results = append(c.Results, RunnerResult{t, f, s})

					if t.Skipped() {
						c.SkipCount++
						skipping = true
						clr = clrYellow
					} else if t.Failed() {
						c.FailCount++
						clr = clrRed
					}

					done <- true
				}()

				f, err := c.Execute(t, step.Text, string(step.Argument))
				if err != nil {
					t.Error(err)
				}
				found = f

				if !f {
					t.Skip("no match function for step")
				}
			}()
			<-done
		}

		if skipping && !found {
			cstep := step
			c.Unmatched = append(c.Unmatched, &cstep)
		}

		if !isExample {
			c.fileLine(clr, "    %s %s", step.Filename, step.Line,
				s.LongestLine(), step.Type, step.Text)
			if len(step.Argument) > 0 {
				if !step.Argument.IsTabular() {
					c.line(clrWhite, `      """`)
				}
				for _, l := range strings.Split(string(step.Argument), "\n") {
					c.line(clrWhite, "      %s", l)
				}
				if !step.Argument.IsTabular() {
					c.line(clrWhite, `      """`)
				}
			}
		}

		if len(t.errors) > errCount {
			c.line(clrRed, "\n"+t.errors[len(t.errors)-1].message)
		}
	}
	if !isExample {
		c.line(clrWhite, "")
	}

	for k, fn := range c.AfterFilters {
		if s.FilterMatched(strings.Split(k, "|")...) {
			fn()
		}
	}
}

var writer = ansicolor.NewAnsiColorWriter(os.Stdout)

func (c *Runner) line(clr, text string, args ...interface{}) {
	fmt.Fprintf(writer, "\033[%sm%s\033[0;0m\n", clr, fmt.Sprintf(text, args...))
}

func (c *Runner) fileLine(clr, text, filename string, line int, max int, args ...interface{}) {
	space, str := "", fmt.Sprintf(text, args...)
	if l := max + 5 - len(str); l > 0 {
		space = strings.Repeat(" ", l)
	}
	comment := fmt.Sprintf("%s \033[39;0m# %s:%d", space, filename, line)
	c.line(clr, "%s%s", str, comment)
}

type Tester interface {
	Errorf(format string, args ...interface{})
	Skip(args ...interface{})
}

type TestingT struct {
	skipped bool
	errors  []TestError
}

type TestError struct {
	message string
	stack   []byte
}

func (t *TestingT) Errorf(format string, args ...interface{}) {
	var buf bytes.Buffer

	str := fmt.Sprintf(format, args...)
	sbuf := make([]byte, 8192)
	for {
		size := runtime.Stack(sbuf, false)
		if size < len(sbuf) {
			break
		}
		buf.Write(sbuf[0:size])
	}

	t.errors = append(t.errors, TestError{message: str, stack: buf.Bytes()})
}

func (t *TestingT) Skip(args ...interface{}) {
	t.skipped = true
}

func (t *TestingT) Skipped() bool {
	return t.skipped
}

func (t *TestingT) Failed() bool {
	return len(t.errors) > 0
}

func (t *TestingT) Error(err error) {
	t.errors = append(t.errors, TestError{message: err.Error()})
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"runtime"
	"sync"
	"syscall"
	"time"
)

type PackageInfo struct {
	packageName    string
	packageVersion string
	repositoryUrl  string
	repositoryType string
}

// Flags
var (
	satisPath  string
	configPath string
	repoPath   string

	listen string
)

// Operation vars
var (
	shouldGenerateConfig bool
	shouldGenerateRepo   bool
	runningGoroutines    = 0

	pendingUpdates map[string]*PackageInfo = make(map[string]*PackageInfo)

	updateMutex sync.Mutex
	configMutex sync.RWMutex
)

func init() {
	flag.StringVar(&satisPath, "satis", "", "The path to the satis binary (required)")
	flag.StringVar(&configPath, "config", "", "The path to the satis repo configuration file (required)")
	flag.StringVar(&repoPath, "repo", "", "The path to the satis repository (required)")

	flag.StringVar(&listen, "listen", ":8080", "The address to listen on")
}

func printHelp() {
	flag.PrintDefaults()
}

// Writes the satis configuration file upon receiving a signal
func configGenerator(abortChan chan bool) {
	runningGoroutines += 1
	defer func() {
		runningGoroutines -= 1
	}()

	var config map[string]interface{}
	for {
		// check if we're shutting down
		select {
		case <-abortChan:
			return
		default:
			break
		}

		// Have we been flagged for a config rebuild?
		if !shouldGenerateConfig {
			continue
		}

		// Read the existing config
		configMutex.RLock()
		data, err := ioutil.ReadFile(configPath)
		if err != nil {
			configMutex.RUnlock()
			fmt.Fprintf(os.Stderr, "Failed to load satis config file: %s", err)
			time.Sleep(time.Second * 5)
			continue
		}
		configMutex.RUnlock()
		// Decode the config
		err = json.Unmarshal(data, &config)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to decode satis config file: %s", err)
			time.Sleep(time.Second * 5)
			continue
		}

		var repositories []map[string]interface{}
		var packages map[string]interface{}

		// Create the keys if they don't exist,
		// but assume they're the correct type if they do exist
		if tmp, ok := config["repositories"]; !ok {
			repositories = make([]map[string]interface{}, 0, 1)
		} else {
			tmp2 := tmp.([]interface{})
			repositories = make([]map[string]interface{}, len(tmp2))
			for k, repo := range tmp2 {
				repositories[k] = repo.(map[string]interface{})
			}
		}
		if tmp, ok := config["require"]; !ok {
			packages = make(map[string]interface{})
		} else {
			packages = tmp.(map[string]interface{})
		}

		// Update the config
		updateMutex.Lock()
		for _, packageInfo := range pendingUpdates {
			var repoExists = false

			// Update the repo if it already exists
			for _, repo := range repositories {
				if repo["url"] == packageInfo.repositoryUrl {
					repo["type"] = packageInfo.repositoryType
					repoExists = true
					break
				}
			}

			// Create the repo if it doesn't exist
			if !repoExists {
				repositories = append(repositories, map[string]interface{}{
					"url":  packageInfo.repositoryUrl,
					"type": packageInfo.repositoryType,
				})
			}

			// Update the package version
			packages[packageInfo.packageName] = packageInfo.packageVersion
		}
		updateMutex.Unlock()

		// Write the config changes
		config["repositories"] = repositories
		config["require"] = packages

		data, err = json.MarshalIndent(config, "", "    ")
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to encode satis config file: %s", err)
			os.Exit(1)
		}

		configMutex.Lock()
		err = ioutil.WriteFile(configPath, data, 0644)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to write satis config file: %s", err)
			continue
		}
		configMutex.Unlock()

		log.Println("Generated config file for", len(pendingUpdates), "updates")
		pendingUpdates = make(map[string]*PackageInfo)

		// Update the worker flags to trigger a build
		shouldGenerateConfig = false
		shouldGenerateRepo = true
	}
}

// Generates the satis repositroy upon receiving a signal
func repoGenerator(abortChan chan bool) {
	runningGoroutines += 1
	defer func() {
		runningGoroutines -= 1
	}()

	for {
		// check if we're shutting down
		select {
		case <-abortChan:
			return
		default:
			break
		}

		// Check if we should generate the repo
		if !shouldGenerateRepo {
			continue
		}

		// Lock the config writer for an arbitrary amount of time on the off chance we want to write
		// to just as we launch satis
		configMutex.RLock()
		go func() {
			time.Sleep(time.Second * 5)
			configMutex.RUnlock()
		}()

		command := exec.Command(satisPath, "build", configPath, repoPath)
		command.Stdout = os.Stdout
		command.Stderr = os.Stderr
		command.Stdin = os.Stdin
		err := command.Run()

		if err != nil {
			log.Fatalln("failed to execute satis: %s", err)
		}

		shouldGenerateRepo = false
	}
}

func serveHttp(abortChan chan bool) {
	runningGoroutines += 1
	defer func() {
		runningGoroutines -= 1
	}()

	// Serve the repo config
	http.HandleFunc("/config.json", func(w http.ResponseWriter, r *http.Request) {
		configMutex.RLock()
		defer configMutex.RUnlock()
		http.ServeFile(w, r, configPath)
	})

	// Endpoint to force regeneration
	http.HandleFunc("/generate", func(w http.ResponseWriter, r *http.Request) {
		log.Println("HTTP triggered repo generation")

		shouldGenerateRepo = true

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(200)
		w.Write([]byte("{\"success\": true}"))
	})

	// Endpoint to register a repo update
	http.HandleFunc("/register", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Content-Type", "application/json")

		params := r.URL.Query()
		var update = &PackageInfo{
			repositoryUrl:  params.Get("repo"),
			repositoryType: params.Get("repoType"),
			packageName:    params.Get("package"),
			packageVersion: params.Get("version"),
		}

		// Basic sanity checking
		if update.packageName == "" {
			w.WriteHeader(400)
			w.Write([]byte("{\"error\": \"missing package\"}"))
			return
		}

		if update.repositoryUrl == "" {
			w.WriteHeader(400)
			w.Write([]byte("{\"error\": \"missing repo\"}"))
			return
		}

		if update.repositoryType == "" {
			w.WriteHeader(400)
			w.Write([]byte("{\"error\": \"missing repoType\"}"))
			return
		}

		if update.packageVersion == "" {
			update.packageVersion = "*"
		}

		updateMutex.Lock()
		pendingUpdates[update.packageName] = update
		updateMutex.Unlock()

		shouldGenerateRepo = true
		w.WriteHeader(200)
		w.Write([]byte("{\"success\": true}"))
	})

	// Serve the repo
	fs := http.FileServer(http.Dir(repoPath))
	http.Handle("/", fs)

	var server = http.Server{Addr: listen}
	var errorChan = make(chan error)

	go func() {
		fmt.Println("listening on", listen)
		errorChan <- server.ListenAndServe()
	}()

	select {
	case err := <-errorChan:
		log.Fatalln("HTTP listener error:", err)
		break
	case <-abortChan:
		server.Close()
		break
	}

}

func main() {
	fmt.Println("satisd - dynamic satis repository generator daemon")
	flag.Parse()

	if satisPath == "" || configPath == "" || repoPath == "" {
		printHelp()
		return
	}

	// Check that the files exist
	if _, err := os.Stat(satisPath); os.IsNotExist(err) {
		log.Fatalln("satis binary not found at", satisPath)
	}
	if _, err := os.Stat(configPath); os.IsNotExist(err) {
		log.Fatalln("satis configuration not found at", configPath)
	}

	// Perform a basic sanity check on the config
	var config map[string]interface{}
	data, err := ioutil.ReadFile(configPath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to load satis config file: %s", err)
		os.Exit(1)
	}
	err = json.Unmarshal(data, &config)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to decode satis config file: %s", err)
		os.Exit(1)
	}

	// Spin up the worker goroutines
	shutdownChannel := make(chan bool)
	go configGenerator(shutdownChannel)
	go repoGenerator(shutdownChannel)
	go serveHttp(shutdownChannel)

	// Catch signals
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	// We've got a kill signal? - do a clean shutdown
	sig := <-sigs
	fmt.Println("Received signal", sig)
	close(shutdownChannel)

	// Wait for the goroutines to exit
	for {
		if runningGoroutines == 0 {
			return
		}
		runtime.Gosched()
	}
}
<commit_msg>Stop workers running wild while waiting for work<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"runtime"
	"sync"
	"syscall"
	"time"
)

type PackageInfo struct {
	packageName    string
	packageVersion string
	repositoryUrl  string
	repositoryType string
}

// Flags
var (
	satisPath  string
	configPath string
	repoPath   string

	listen string
)

// Operation vars
var (
	shouldGenerateConfig bool
	shouldGenerateRepo   bool
	runningGoroutines    = 0

	pendingUpdates map[string]*PackageInfo = make(map[string]*PackageInfo)

	updateMutex sync.Mutex
	configMutex sync.RWMutex
)

func init() {
	flag.StringVar(&satisPath, "satis", "", "The path to the satis binary (required)")
	flag.StringVar(&configPath, "config", "", "The path to the satis repo configuration file (required)")
	flag.StringVar(&repoPath, "repo", "", "The path to the satis repository (required)")

	flag.StringVar(&listen, "listen", ":8080", "The address to listen on")
}

func printHelp() {
	flag.PrintDefaults()
}

// Writes the satis configuration file upon receiving a signal
func configGenerator(abortChan chan bool) {
	runningGoroutines += 1
	defer func() {
		runningGoroutines -= 1
	}()

	var config map[string]interface{}
	for {
		// check if we're shutting down
		select {
		case <-abortChan:
			return
		default:
			break
		}

		// Have we been flagged for a config rebuild?
		if !shouldGenerateConfig {
			time.Sleep(time.Second * 1)
			continue
		}

		// Read the existing config
		configMutex.RLock()
		data, err := ioutil.ReadFile(configPath)
		if err != nil {
			configMutex.RUnlock()
			fmt.Fprintf(os.Stderr, "Failed to load satis config file: %s", err)
			time.Sleep(time.Second * 5)
			continue
		}
		configMutex.RUnlock()
		// Decode the config
		err = json.Unmarshal(data, &config)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to decode satis config file: %s", err)
			time.Sleep(time.Second * 5)
			continue
		}

		var repositories []map[string]interface{}
		var packages map[string]interface{}

		// Create the keys if they don't exist,
		// but assume they're the correct type if they do exist
		if tmp, ok := config["repositories"]; !ok {
			repositories = make([]map[string]interface{}, 0, 1)
		} else {
			tmp2 := tmp.([]interface{})
			repositories = make([]map[string]interface{}, len(tmp2))
			for k, repo := range tmp2 {
				repositories[k] = repo.(map[string]interface{})
			}
		}
		if tmp, ok := config["require"]; !ok {
			packages = make(map[string]interface{})
		} else {
			packages = tmp.(map[string]interface{})
		}

		// Update the config
		updateMutex.Lock()
		for _, packageInfo := range pendingUpdates {
			var repoExists = false

			// Update the repo if it already exists
			for _, repo := range repositories {
				if repo["url"] == packageInfo.repositoryUrl {
					repo["type"] = packageInfo.repositoryType
					repoExists = true
					break
				}
			}

			// Create the repo if it doesn't exist
			if !repoExists {
				repositories = append(repositories, map[string]interface{}{
					"url":  packageInfo.repositoryUrl,
					"type": packageInfo.repositoryType,
				})
			}

			// Update the package version
			packages[packageInfo.packageName] = packageInfo.packageVersion
		}
		updateMutex.Unlock()

		// Write the config changes
		config["repositories"] = repositories
		config["require"] = packages

		data, err = json.MarshalIndent(config, "", "    ")
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to encode satis config file: %s", err)
			os.Exit(1)
		}

		configMutex.Lock()
		err = ioutil.WriteFile(configPath, data, 0644)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to write satis config file: %s", err)
			continue
		}
		configMutex.Unlock()

		log.Println("Generated config file for", len(pendingUpdates), "updates")
		pendingUpdates = make(map[string]*PackageInfo)

		// Update the worker flags to trigger a build
		shouldGenerateConfig = false
		shouldGenerateRepo = true
	}
}

// Generates the satis repositroy upon receiving a signal
func repoGenerator(abortChan chan bool) {
	runningGoroutines += 1
	defer func() {
		runningGoroutines -= 1
	}()

	for {
		// check if we're shutting down
		select {
		case <-abortChan:
			return
		default:
			break
		}

		// Check if we should generate the repo
		if !shouldGenerateRepo {
			time.Sleep(time.Second * 1)
			continue
		}

		// Lock the config writer for an arbitrary amount of time on the off chance we want to write
		// to just as we launch satis
		configMutex.RLock()
		go func() {
			time.Sleep(time.Second * 5)
			configMutex.RUnlock()
		}()

		command := exec.Command(satisPath, "build", configPath, repoPath)
		command.Stdout = os.Stdout
		command.Stderr = os.Stderr
		command.Stdin = os.Stdin
		err := command.Run()

		if err != nil {
			log.Fatalln("failed to execute satis: %s", err)
		}

		shouldGenerateRepo = false
	}
}

func serveHttp(abortChan chan bool) {
	runningGoroutines += 1
	defer func() {
		runningGoroutines -= 1
	}()

	// Serve the repo config
	http.HandleFunc("/config.json", func(w http.ResponseWriter, r *http.Request) {
		configMutex.RLock()
		defer configMutex.RUnlock()
		http.ServeFile(w, r, configPath)
	})

	// Endpoint to force regeneration
	http.HandleFunc("/generate", func(w http.ResponseWriter, r *http.Request) {
		log.Println("HTTP triggered repo generation")

		shouldGenerateRepo = true

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(200)
		w.Write([]byte("{\"success\": true}"))
	})

	// Endpoint to register a repo update
	http.HandleFunc("/register", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Content-Type", "application/json")

		params := r.URL.Query()
		var update = &PackageInfo{
			repositoryUrl:  params.Get("repo"),
			repositoryType: params.Get("repoType"),
			packageName:    params.Get("package"),
			packageVersion: params.Get("version"),
		}

		// Basic sanity checking
		if update.packageName == "" {
			w.WriteHeader(400)
			w.Write([]byte("{\"error\": \"missing package\"}"))
			return
		}

		if update.repositoryUrl == "" {
			w.WriteHeader(400)
			w.Write([]byte("{\"error\": \"missing repo\"}"))
			return
		}

		if update.repositoryType == "" {
			w.WriteHeader(400)
			w.Write([]byte("{\"error\": \"missing repoType\"}"))
			return
		}

		if update.packageVersion == "" {
			update.packageVersion = "*"
		}

		updateMutex.Lock()
		pendingUpdates[update.packageName] = update
		updateMutex.Unlock()

		shouldGenerateRepo = true
		w.WriteHeader(200)
		w.Write([]byte("{\"success\": true}"))
	})

	// Serve the repo
	fs := http.FileServer(http.Dir(repoPath))
	http.Handle("/", fs)

	var server = http.Server{Addr: listen}
	var errorChan = make(chan error)

	go func() {
		fmt.Println("listening on", listen)
		errorChan <- server.ListenAndServe()
	}()

	select {
	case err := <-errorChan:
		log.Fatalln("HTTP listener error:", err)
		break
	case <-abortChan:
		server.Close()
		break
	}

}

func main() {
	fmt.Println("satisd - dynamic satis repository generator daemon")
	flag.Parse()

	if satisPath == "" || configPath == "" || repoPath == "" {
		printHelp()
		return
	}

	// Check that the files exist
	if _, err := os.Stat(satisPath); os.IsNotExist(err) {
		log.Fatalln("satis binary not found at", satisPath)
	}
	if _, err := os.Stat(configPath); os.IsNotExist(err) {
		log.Fatalln("satis configuration not found at", configPath)
	}

	// Perform a basic sanity check on the config
	var config map[string]interface{}
	data, err := ioutil.ReadFile(configPath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to load satis config file: %s", err)
		os.Exit(1)
	}
	err = json.Unmarshal(data, &config)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to decode satis config file: %s", err)
		os.Exit(1)
	}

	// Spin up the worker goroutines
	shutdownChannel := make(chan bool)
	go configGenerator(shutdownChannel)
	go repoGenerator(shutdownChannel)
	go serveHttp(shutdownChannel)

	// Catch signals
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	// We've got a kill signal? - do a clean shutdown
	sig := <-sigs
	fmt.Println("Received signal", sig)
	close(shutdownChannel)

	// Wait for the goroutines to exit
	for {
		if runningGoroutines == 0 {
			return
		}
		runtime.Gosched()
	}
}
<|endoftext|>


<commit_before>package graphql

import (
	"fmt"

	"github.com/golang/sync/syncmap"
	"github.com/sprucehealth/graphql/gqlerrors"
)

type SchemaConfig struct {
	Query        *Object
	Mutation     *Object
	Subscription *Object
	Types        []Type
	Directives   []*Directive
}

type TypeMap map[string]Type

// Schema Definition
// A Schema is created by supplying the root types of each type of operation,
// query, mutation (optional) and subscription (optional). A schema definition is then supplied to the
// validator and executor.
// Example:
//     myAppSchema, err := NewSchema(SchemaConfig({
//       Query: MyAppQueryRootType,
//       Mutation: MyAppMutationRootType,
//       Subscription: MyAppSubscriptionRootType,
//     });
// Note: If an array of `directives` are provided to GraphQLSchema, that will be
// the exact list of directives represented and allowed. If `directives` is not
// provided then a default set of the specified directives (e.g. @include and
// @skip) will be used. If you wish to provide *additional* directives to these
// specified directives, you must explicitly declare them. Example:
//
//     const MyAppSchema = new GraphQLSchema({
//       ...
//       directives: specifiedDirectives.concat([ myCustomDirective ]),
//     })
type Schema struct {
	typeMap    TypeMap
	directives []*Directive

	queryType        *Object
	mutationType     *Object
	subscriptionType *Object
	implementations  map[string][]*Object
	possibleTypeMap  *syncmap.Map // abstract type name -> map[string]struct{}
}

func NewSchema(config SchemaConfig) (Schema, error) {
	schema := Schema{
		possibleTypeMap: &syncmap.Map{},
	}

	if config.Query == nil {
		return schema, gqlerrors.NewFormattedError("Schema query must be Object Type but got: nil.")
	}

	// if schema config contains error at creation time, return those errors
	if config.Query != nil && config.Query.Error() != nil {
		return schema, config.Query.Error()
	}
	if config.Mutation != nil && config.Mutation.Error() != nil {
		return schema, config.Mutation.Error()
	}

	schema.queryType = config.Query
	schema.mutationType = config.Mutation
	schema.subscriptionType = config.Subscription

	// Provide specified directives (e.g. @include and @skip) by default.
	schema.directives = config.Directives
	if len(schema.directives) == 0 {
		schema.directives = SpecifiedDirectives
	}
	// Ensure directive definitions are error-free
	for _, dir := range schema.directives {
		if dir.err != nil {
			return schema, dir.err
		}
	}

	// Build type map now to detect any errors within this schema.
	typeMap := TypeMap{}
	initialTypes := make([]Type, 0, 4+len(config.Types))
	if schema.QueryType() != nil {
		initialTypes = append(initialTypes, schema.QueryType())
	}
	if schema.MutationType() != nil {
		initialTypes = append(initialTypes, schema.MutationType())
	}
	if schema.SubscriptionType() != nil {
		initialTypes = append(initialTypes, schema.SubscriptionType())
	}
	if SchemaType != nil {
		initialTypes = append(initialTypes, SchemaType)
	}

	for _, ttype := range config.Types {
		// assume that user will never add a nil object to config
		initialTypes = append(initialTypes, ttype)
	}

	for _, ttype := range initialTypes {
		if ttype.Error() != nil {
			return schema, ttype.Error()
		}
		var err error
		typeMap, err = typeMapReducer(&schema, typeMap, ttype)
		if err != nil {
			return schema, err
		}
	}

	schema.typeMap = typeMap

	// Keep track of all implementations by interface name.
	if schema.implementations == nil {
		schema.implementations = map[string][]*Object{}
	}
	for _, ttype := range schema.typeMap {
		if ttype, ok := ttype.(*Object); ok {
			for _, iface := range ttype.Interfaces() {
				schema.implementations[iface.Name()] = append(schema.implementations[iface.Name()], ttype)
			}
		}
	}

	// Enforce correct interface implementations
	for _, ttype := range schema.typeMap {
		if ttype, ok := ttype.(*Object); ok {
			for _, iface := range ttype.Interfaces() {
				err := assertObjectImplementsInterface(&schema, ttype, iface)
				if err != nil {
					return schema, err
				}
			}
		}
	}

	return schema, nil
}

func (gq *Schema) QueryType() *Object {
	return gq.queryType
}

func (gq *Schema) MutationType() *Object {
	return gq.mutationType
}

func (gq *Schema) SubscriptionType() *Object {
	return gq.subscriptionType
}

func (gq *Schema) Directives() []*Directive {
	return gq.directives
}

func (gq *Schema) Directive(name string) *Directive {
	for _, directive := range gq.Directives() {
		if directive.Name == name {
			return directive
		}
	}
	return nil
}

func (gq *Schema) TypeMap() TypeMap {
	return gq.typeMap
}

func (gq *Schema) Type(name string) Type {
	return gq.TypeMap()[name]
}

func (gq *Schema) PossibleTypes(abstractType Abstract) []*Object {
	switch abstractType := abstractType.(type) {
	case *Union:
		return abstractType.Types()
	case *Interface:
		if impls, ok := gq.implementations[abstractType.Name()]; ok {
			return impls
		}
	}
	return []*Object{}
}
func (gq *Schema) IsPossibleType(abstractType Abstract, possibleType *Object) bool {
	name := abstractType.Name()
	typeMapVal, _ := gq.possibleTypeMap.Load(name)
	typeMap, ok := typeMapVal.(map[string]struct{})

	if !ok {
		possibleTypes := gq.PossibleTypes(abstractType)
		typeMap = make(map[string]struct{}, len(possibleTypes))
		for _, possibleType := range possibleTypes {
			typeMap[possibleType.Name()] = struct{}{}
		}
		gq.possibleTypeMap.Store(name, typeMap)
	}

	_, isPossible := typeMap[possibleType.Name()]
	return isPossible
}
func typeMapReducer(schema *Schema, typeMap TypeMap, objectType Type) (TypeMap, error) {
	var err error
	if objectType == nil || objectType.Name() == "" {
		return typeMap, nil
	}

	switch objectType := objectType.(type) {
	case *List:
		if objectType.OfType != nil {
			return typeMapReducer(schema, typeMap, objectType.OfType)
		}
	case *NonNull:
		if objectType.OfType != nil {
			return typeMapReducer(schema, typeMap, objectType.OfType)
		}
	case *Object:
		if objectType.Error() != nil {
			return typeMap, objectType.Error()
		}
	}

	if mappedObjectType, ok := typeMap[objectType.Name()]; ok {
		if mappedObjectType != objectType {
			return typeMap, gqlerrors.NewFormattedError(fmt.Sprintf(`Schema must contain unique named types but contains multiple types named "%v".`, objectType.Name()))
		}
		return typeMap, nil
	}
	if objectType.Name() == "" {
		return typeMap, nil
	}

	typeMap[objectType.Name()] = objectType

	switch objectType := objectType.(type) {
	case *Union:
		types := schema.PossibleTypes(objectType)
		if objectType.Error() != nil {
			return typeMap, objectType.Error()
		}
		for _, innerObjectType := range types {
			if innerObjectType.Error() != nil {
				return typeMap, innerObjectType.Error()
			}
			typeMap, err = typeMapReducer(schema, typeMap, innerObjectType)
			if err != nil {
				return typeMap, err
			}
		}
	case *Interface:
		types := schema.PossibleTypes(objectType)
		if objectType.err != nil {
			return typeMap, objectType.err
		}
		for _, innerObjectType := range types {
			if innerObjectType.Error() != nil {
				return typeMap, innerObjectType.Error()
			}
			typeMap, err = typeMapReducer(schema, typeMap, innerObjectType)
			if err != nil {
				return typeMap, err
			}
		}
	case *Object:
		interfaces := objectType.Interfaces()
		if objectType.Error() != nil {
			return typeMap, objectType.Error()
		}
		for _, innerObjectType := range interfaces {
			if innerObjectType.Error() != nil {
				return typeMap, innerObjectType.Error()
			}
			typeMap, err = typeMapReducer(schema, typeMap, innerObjectType)
			if err != nil {
				return typeMap, err
			}
		}
	}

	switch objectType := objectType.(type) {
	case *Object:
		fieldMap := objectType.Fields()
		if objectType.Error() != nil {
			return typeMap, objectType.Error()
		}
		for _, field := range fieldMap {
			for _, arg := range field.Args {
				typeMap, err = typeMapReducer(schema, typeMap, arg.Type)
				if err != nil {
					return typeMap, err
				}
			}
			typeMap, err = typeMapReducer(schema, typeMap, field.Type)
			if err != nil {
				return typeMap, err
			}
		}
	case *Interface:
		fieldMap := objectType.Fields()
		if objectType.Error() != nil {
			return typeMap, objectType.Error()
		}
		for _, field := range fieldMap {
			for _, arg := range field.Args {
				typeMap, err = typeMapReducer(schema, typeMap, arg.Type)
				if err != nil {
					return typeMap, err
				}
			}
			typeMap, err = typeMapReducer(schema, typeMap, field.Type)
			if err != nil {
				return typeMap, err
			}
		}
	case *InputObject:
		fieldMap := objectType.Fields()
		if objectType.err != nil {
			return typeMap, objectType.err
		}
		for _, field := range fieldMap {
			typeMap, err = typeMapReducer(schema, typeMap, field.Type)
			if err != nil {
				return typeMap, err
			}
		}
	}
	return typeMap, nil
}

func assertObjectImplementsInterface(schema *Schema, object *Object, iface *Interface) error {
	objectFieldMap := object.Fields()
	ifaceFieldMap := iface.Fields()

	// Assert each interface field is implemented.
	for fieldName := range ifaceFieldMap {
		objectField := objectFieldMap[fieldName]
		ifaceField := ifaceFieldMap[fieldName]

		// Assert interface field exists on object.
		if objectField == nil {
			return gqlerrors.NewFormattedError(fmt.Sprintf(`"%v" expects field "%v" but "%v" does not provide it.`, iface, fieldName, object))
		}

		// Assert interface field type matches object field type.
		if !isTypeSubTypeOf(schema, objectField.Type, ifaceField.Type) {
			return gqlerrors.NewFormattedError(fmt.Sprintf(`%v.%v expects type "%v" but %v.%v provides type "%v".`,
				iface, fieldName, ifaceField.Type,
				object, fieldName, objectField.Type))
		}

		// Assert each interface field arg is implemented.
		for _, ifaceArg := range ifaceField.Args {
			argName := ifaceArg.PrivateName
			var objectArg *Argument
			for _, arg := range objectField.Args {
				if arg.PrivateName == argName {
					objectArg = arg
					break
				}
			}
			// Assert interface field arg exists on object field.
			if objectArg == nil {
				return gqlerrors.NewFormattedError(fmt.Sprintf(`%v.%v expects argument "%v" but %v.%v does not provide it.`,
					iface, fieldName, argName,
					object, fieldName))
			}

			// Assert interface field arg type matches object field arg type.
			if !isEqualType(ifaceArg.Type, objectArg.Type) {
				return gqlerrors.NewFormattedError(fmt.Sprintf(
					`%v.%v(%v:) expects type "%v" `+
						`but %v.%v(%v:) provides `+
						`type "%v".`,
					iface, fieldName, argName, ifaceArg.Type,
					object, fieldName, argName, objectArg.Type))
			}
		}
		// Assert additional arguments must not be required.
		for _, objectArg := range objectField.Args {
			argName := objectArg.PrivateName
			var ifaceArg *Argument
			for _, arg := range ifaceField.Args {
				if arg.PrivateName == argName {
					ifaceArg = arg
					break
				}
			}
			if ifaceArg == nil {
				_, ok := objectArg.Type.(*NonNull)
				if ok {
					return gqlerrors.NewFormattedError(
						fmt.Sprintf(`%v.%v(%v:) is of required type "%v" but is not also provided by the interface %v.%v.`,
							object, fieldName, argName, objectArg.Type, iface, fieldName))
				}
			}
		}
	}
	return nil
}

func isEqualType(typeA, typeB Type) bool {
	// Equivalent type is a valid subtype
	if typeA == typeB {
		return true
	}
	// If either type is non-null, the other must also be non-null.
	if typeA, ok := typeA.(*NonNull); ok {
		if typeB, ok := typeB.(*NonNull); ok {
			return isEqualType(typeA.OfType, typeB.OfType)
		}
	}
	// If either type is a list, the other must also be a list.
	if typeA, ok := typeA.(*List); ok {
		if typeB, ok := typeB.(*List); ok {
			return isEqualType(typeA.OfType, typeB.OfType)
		}
	}
	return typeA == typeB
}

// isTypeSubTypeOf Provided a type and a super type, return true if the first type is either
// equal or a subset of the second super type (covariant).
func isTypeSubTypeOf(schema *Schema, maybeSubType Type, superType Type) bool {
	// Equivalent type is a valid subtype
	if maybeSubType == superType {
		return true
	}

	// If superType is non-null, maybeSubType must also be nullable.
	if superType, ok := superType.(*NonNull); ok {
		if maybeSubType, ok := maybeSubType.(*NonNull); ok {
			return isTypeSubTypeOf(schema, maybeSubType.OfType, superType.OfType)
		}
		return false
	}
	if maybeSubType, ok := maybeSubType.(*NonNull); ok {
		// If superType is nullable, maybeSubType may be non-null.
		return isTypeSubTypeOf(schema, maybeSubType.OfType, superType)
	}

	// If superType type is a list, maybeSubType type must also be a list.
	if superType, ok := superType.(*List); ok {
		if maybeSubType, ok := maybeSubType.(*List); ok {
			return isTypeSubTypeOf(schema, maybeSubType.OfType, superType.OfType)
		}
		return false
	} else if _, ok := maybeSubType.(*List); ok {
		// If superType is not a list, maybeSubType must also be not a list.
		return false
	}

	// If superType type is an abstract type, maybeSubType type may be a currently
	// possible object type.
	if superType, ok := superType.(*Interface); ok {
		if maybeSubType, ok := maybeSubType.(*Object); ok && schema.IsPossibleType(superType, maybeSubType) {
			return true
		}
	}
	if superType, ok := superType.(*Union); ok {
		if maybeSubType, ok := maybeSubType.(*Object); ok && schema.IsPossibleType(superType, maybeSubType) {
			return true
		}
	}

	// Otherwise, the child type is not a valid subtype of the parent type.
	return false
}
<commit_msg>Use sync pkg instead of syncmap<commit_after>package graphql

import (
	"fmt"
	"sync"

	"github.com/sprucehealth/graphql/gqlerrors"
)

type SchemaConfig struct {
	Query        *Object
	Mutation     *Object
	Subscription *Object
	Types        []Type
	Directives   []*Directive
}

type TypeMap map[string]Type

// Schema Definition
// A Schema is created by supplying the root types of each type of operation,
// query, mutation (optional) and subscription (optional). A schema definition is then supplied to the
// validator and executor.
// Example:
//     myAppSchema, err := NewSchema(SchemaConfig({
//       Query: MyAppQueryRootType,
//       Mutation: MyAppMutationRootType,
//       Subscription: MyAppSubscriptionRootType,
//     });
// Note: If an array of `directives` are provided to GraphQLSchema, that will be
// the exact list of directives represented and allowed. If `directives` is not
// provided then a default set of the specified directives (e.g. @include and
// @skip) will be used. If you wish to provide *additional* directives to these
// specified directives, you must explicitly declare them. Example:
//
//     const MyAppSchema = new GraphQLSchema({
//       ...
//       directives: specifiedDirectives.concat([ myCustomDirective ]),
//     })
type Schema struct {
	typeMap    TypeMap
	directives []*Directive

	queryType        *Object
	mutationType     *Object
	subscriptionType *Object
	implementations  map[string][]*Object
	possibleTypeMap  *sync.Map // abstract type name -> map[string]struct{}
}

func NewSchema(config SchemaConfig) (Schema, error) {
	schema := Schema{
		possibleTypeMap: &sync.Map{},
	}

	if config.Query == nil {
		return schema, gqlerrors.NewFormattedError("Schema query must be Object Type but got: nil.")
	}

	// if schema config contains error at creation time, return those errors
	if config.Query != nil && config.Query.Error() != nil {
		return schema, config.Query.Error()
	}
	if config.Mutation != nil && config.Mutation.Error() != nil {
		return schema, config.Mutation.Error()
	}

	schema.queryType = config.Query
	schema.mutationType = config.Mutation
	schema.subscriptionType = config.Subscription

	// Provide specified directives (e.g. @include and @skip) by default.
	schema.directives = config.Directives
	if len(schema.directives) == 0 {
		schema.directives = SpecifiedDirectives
	}
	// Ensure directive definitions are error-free
	for _, dir := range schema.directives {
		if dir.err != nil {
			return schema, dir.err
		}
	}

	// Build type map now to detect any errors within this schema.
	typeMap := TypeMap{}
	initialTypes := make([]Type, 0, 4+len(config.Types))
	if schema.QueryType() != nil {
		initialTypes = append(initialTypes, schema.QueryType())
	}
	if schema.MutationType() != nil {
		initialTypes = append(initialTypes, schema.MutationType())
	}
	if schema.SubscriptionType() != nil {
		initialTypes = append(initialTypes, schema.SubscriptionType())
	}
	if SchemaType != nil {
		initialTypes = append(initialTypes, SchemaType)
	}

	for _, ttype := range config.Types {
		// assume that user will never add a nil object to config
		initialTypes = append(initialTypes, ttype)
	}

	for _, ttype := range initialTypes {
		if ttype.Error() != nil {
			return schema, ttype.Error()
		}
		var err error
		typeMap, err = typeMapReducer(&schema, typeMap, ttype)
		if err != nil {
			return schema, err
		}
	}

	schema.typeMap = typeMap

	// Keep track of all implementations by interface name.
	if schema.implementations == nil {
		schema.implementations = map[string][]*Object{}
	}
	for _, ttype := range schema.typeMap {
		if ttype, ok := ttype.(*Object); ok {
			for _, iface := range ttype.Interfaces() {
				schema.implementations[iface.Name()] = append(schema.implementations[iface.Name()], ttype)
			}
		}
	}

	// Enforce correct interface implementations
	for _, ttype := range schema.typeMap {
		if ttype, ok := ttype.(*Object); ok {
			for _, iface := range ttype.Interfaces() {
				err := assertObjectImplementsInterface(&schema, ttype, iface)
				if err != nil {
					return schema, err
				}
			}
		}
	}

	return schema, nil
}

func (gq *Schema) QueryType() *Object {
	return gq.queryType
}

func (gq *Schema) MutationType() *Object {
	return gq.mutationType
}

func (gq *Schema) SubscriptionType() *Object {
	return gq.subscriptionType
}

func (gq *Schema) Directives() []*Directive {
	return gq.directives
}

func (gq *Schema) Directive(name string) *Directive {
	for _, directive := range gq.Directives() {
		if directive.Name == name {
			return directive
		}
	}
	return nil
}

func (gq *Schema) TypeMap() TypeMap {
	return gq.typeMap
}

func (gq *Schema) Type(name string) Type {
	return gq.TypeMap()[name]
}

func (gq *Schema) PossibleTypes(abstractType Abstract) []*Object {
	switch abstractType := abstractType.(type) {
	case *Union:
		return abstractType.Types()
	case *Interface:
		if impls, ok := gq.implementations[abstractType.Name()]; ok {
			return impls
		}
	}
	return []*Object{}
}
func (gq *Schema) IsPossibleType(abstractType Abstract, possibleType *Object) bool {
	name := abstractType.Name()
	typeMapVal, _ := gq.possibleTypeMap.Load(name)
	typeMap, ok := typeMapVal.(map[string]struct{})

	if !ok {
		possibleTypes := gq.PossibleTypes(abstractType)
		typeMap = make(map[string]struct{}, len(possibleTypes))
		for _, possibleType := range possibleTypes {
			typeMap[possibleType.Name()] = struct{}{}
		}
		gq.possibleTypeMap.Store(name, typeMap)
	}

	_, isPossible := typeMap[possibleType.Name()]
	return isPossible
}
func typeMapReducer(schema *Schema, typeMap TypeMap, objectType Type) (TypeMap, error) {
	var err error
	if objectType == nil || objectType.Name() == "" {
		return typeMap, nil
	}

	switch objectType := objectType.(type) {
	case *List:
		if objectType.OfType != nil {
			return typeMapReducer(schema, typeMap, objectType.OfType)
		}
	case *NonNull:
		if objectType.OfType != nil {
			return typeMapReducer(schema, typeMap, objectType.OfType)
		}
	case *Object:
		if objectType.Error() != nil {
			return typeMap, objectType.Error()
		}
	}

	if mappedObjectType, ok := typeMap[objectType.Name()]; ok {
		if mappedObjectType != objectType {
			return typeMap, gqlerrors.NewFormattedError(fmt.Sprintf(`Schema must contain unique named types but contains multiple types named "%v".`, objectType.Name()))
		}
		return typeMap, nil
	}
	if objectType.Name() == "" {
		return typeMap, nil
	}

	typeMap[objectType.Name()] = objectType

	switch objectType := objectType.(type) {
	case *Union:
		types := schema.PossibleTypes(objectType)
		if objectType.Error() != nil {
			return typeMap, objectType.Error()
		}
		for _, innerObjectType := range types {
			if innerObjectType.Error() != nil {
				return typeMap, innerObjectType.Error()
			}
			typeMap, err = typeMapReducer(schema, typeMap, innerObjectType)
			if err != nil {
				return typeMap, err
			}
		}
	case *Interface:
		types := schema.PossibleTypes(objectType)
		if objectType.err != nil {
			return typeMap, objectType.err
		}
		for _, innerObjectType := range types {
			if innerObjectType.Error() != nil {
				return typeMap, innerObjectType.Error()
			}
			typeMap, err = typeMapReducer(schema, typeMap, innerObjectType)
			if err != nil {
				return typeMap, err
			}
		}
	case *Object:
		interfaces := objectType.Interfaces()
		if objectType.Error() != nil {
			return typeMap, objectType.Error()
		}
		for _, innerObjectType := range interfaces {
			if innerObjectType.Error() != nil {
				return typeMap, innerObjectType.Error()
			}
			typeMap, err = typeMapReducer(schema, typeMap, innerObjectType)
			if err != nil {
				return typeMap, err
			}
		}
	}

	switch objectType := objectType.(type) {
	case *Object:
		fieldMap := objectType.Fields()
		if objectType.Error() != nil {
			return typeMap, objectType.Error()
		}
		for _, field := range fieldMap {
			for _, arg := range field.Args {
				typeMap, err = typeMapReducer(schema, typeMap, arg.Type)
				if err != nil {
					return typeMap, err
				}
			}
			typeMap, err = typeMapReducer(schema, typeMap, field.Type)
			if err != nil {
				return typeMap, err
			}
		}
	case *Interface:
		fieldMap := objectType.Fields()
		if objectType.Error() != nil {
			return typeMap, objectType.Error()
		}
		for _, field := range fieldMap {
			for _, arg := range field.Args {
				typeMap, err = typeMapReducer(schema, typeMap, arg.Type)
				if err != nil {
					return typeMap, err
				}
			}
			typeMap, err = typeMapReducer(schema, typeMap, field.Type)
			if err != nil {
				return typeMap, err
			}
		}
	case *InputObject:
		fieldMap := objectType.Fields()
		if objectType.err != nil {
			return typeMap, objectType.err
		}
		for _, field := range fieldMap {
			typeMap, err = typeMapReducer(schema, typeMap, field.Type)
			if err != nil {
				return typeMap, err
			}
		}
	}
	return typeMap, nil
}

func assertObjectImplementsInterface(schema *Schema, object *Object, iface *Interface) error {
	objectFieldMap := object.Fields()
	ifaceFieldMap := iface.Fields()

	// Assert each interface field is implemented.
	for fieldName := range ifaceFieldMap {
		objectField := objectFieldMap[fieldName]
		ifaceField := ifaceFieldMap[fieldName]

		// Assert interface field exists on object.
		if objectField == nil {
			return gqlerrors.NewFormattedError(fmt.Sprintf(`"%v" expects field "%v" but "%v" does not provide it.`, iface, fieldName, object))
		}

		// Assert interface field type matches object field type.
		if !isTypeSubTypeOf(schema, objectField.Type, ifaceField.Type) {
			return gqlerrors.NewFormattedError(fmt.Sprintf(`%v.%v expects type "%v" but %v.%v provides type "%v".`,
				iface, fieldName, ifaceField.Type,
				object, fieldName, objectField.Type))
		}

		// Assert each interface field arg is implemented.
		for _, ifaceArg := range ifaceField.Args {
			argName := ifaceArg.PrivateName
			var objectArg *Argument
			for _, arg := range objectField.Args {
				if arg.PrivateName == argName {
					objectArg = arg
					break
				}
			}
			// Assert interface field arg exists on object field.
			if objectArg == nil {
				return gqlerrors.NewFormattedError(fmt.Sprintf(`%v.%v expects argument "%v" but %v.%v does not provide it.`,
					iface, fieldName, argName,
					object, fieldName))
			}

			// Assert interface field arg type matches object field arg type.
			if !isEqualType(ifaceArg.Type, objectArg.Type) {
				return gqlerrors.NewFormattedError(fmt.Sprintf(
					`%v.%v(%v:) expects type "%v" `+
						`but %v.%v(%v:) provides `+
						`type "%v".`,
					iface, fieldName, argName, ifaceArg.Type,
					object, fieldName, argName, objectArg.Type))
			}
		}
		// Assert additional arguments must not be required.
		for _, objectArg := range objectField.Args {
			argName := objectArg.PrivateName
			var ifaceArg *Argument
			for _, arg := range ifaceField.Args {
				if arg.PrivateName == argName {
					ifaceArg = arg
					break
				}
			}
			if ifaceArg == nil {
				_, ok := objectArg.Type.(*NonNull)
				if ok {
					return gqlerrors.NewFormattedError(
						fmt.Sprintf(`%v.%v(%v:) is of required type "%v" but is not also provided by the interface %v.%v.`,
							object, fieldName, argName, objectArg.Type, iface, fieldName))
				}
			}
		}
	}
	return nil
}

func isEqualType(typeA, typeB Type) bool {
	// Equivalent type is a valid subtype
	if typeA == typeB {
		return true
	}
	// If either type is non-null, the other must also be non-null.
	if typeA, ok := typeA.(*NonNull); ok {
		if typeB, ok := typeB.(*NonNull); ok {
			return isEqualType(typeA.OfType, typeB.OfType)
		}
	}
	// If either type is a list, the other must also be a list.
	if typeA, ok := typeA.(*List); ok {
		if typeB, ok := typeB.(*List); ok {
			return isEqualType(typeA.OfType, typeB.OfType)
		}
	}
	return typeA == typeB
}

// isTypeSubTypeOf Provided a type and a super type, return true if the first type is either
// equal or a subset of the second super type (covariant).
func isTypeSubTypeOf(schema *Schema, maybeSubType Type, superType Type) bool {
	// Equivalent type is a valid subtype
	if maybeSubType == superType {
		return true
	}

	// If superType is non-null, maybeSubType must also be nullable.
	if superType, ok := superType.(*NonNull); ok {
		if maybeSubType, ok := maybeSubType.(*NonNull); ok {
			return isTypeSubTypeOf(schema, maybeSubType.OfType, superType.OfType)
		}
		return false
	}
	if maybeSubType, ok := maybeSubType.(*NonNull); ok {
		// If superType is nullable, maybeSubType may be non-null.
		return isTypeSubTypeOf(schema, maybeSubType.OfType, superType)
	}

	// If superType type is a list, maybeSubType type must also be a list.
	if superType, ok := superType.(*List); ok {
		if maybeSubType, ok := maybeSubType.(*List); ok {
			return isTypeSubTypeOf(schema, maybeSubType.OfType, superType.OfType)
		}
		return false
	} else if _, ok := maybeSubType.(*List); ok {
		// If superType is not a list, maybeSubType must also be not a list.
		return false
	}

	// If superType type is an abstract type, maybeSubType type may be a currently
	// possible object type.
	if superType, ok := superType.(*Interface); ok {
		if maybeSubType, ok := maybeSubType.(*Object); ok && schema.IsPossibleType(superType, maybeSubType) {
			return true
		}
	}
	if superType, ok := superType.(*Union); ok {
		if maybeSubType, ok := maybeSubType.(*Object); ok && schema.IsPossibleType(superType, maybeSubType) {
			return true
		}
	}

	// Otherwise, the child type is not a valid subtype of the parent type.
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"container/list"
	"fmt"
	"github.com/mattn/go-runewidth"
	"github.com/nsf/termbox-go"
	"gopkg.in/sorcix/irc.v2"
	"strings"
	"unicode/utf8"
)

var buffer *list.List
var limit int

func formatmessage(msg *irc.Message) string {
	prefix := msg.Prefix
	if prefix == nil {
		prefix = &irc.Prefix{"unknown", "unknown", "unknown"}
	}
	switch msg.Command {
	case "JOIN":
		return fmt.Sprintf("%s has joined %s\n", prefix.Name, msg.Params[0])
	case "PRIVMSG":
		return fmt.Sprintf("%s ─→ %s: %s\n", prefix.Name, strings.TrimSpace(msg.Params[0]), msg.Params[1])
	case "MODE":
		return fmt.Sprintf("%s sets mode %s\n", prefix.Name, strings.Join(msg.Params[0:], " "))
	case "NOTICE":
		return fmt.Sprintf("Notice from %s to %s: %s\n", prefix.Name, msg.Params[0], msg.Params[1])
	case "001":
		fallthrough
	case "002":
		fallthrough
	case "003":
		fallthrough
	case "372":
		fallthrough
	case "375":
		fallthrough
	case "376":
		return fmt.Sprintln(msg.Params[1])
	case "QUIT":
		return fmt.Sprintf("%s has quit (%s)\n", prefix.Name, msg.Params[0])
	case "CTCP":
		if strings.HasPrefix(msg.Params[1], "ACTION") {
			return fmt.Sprintf("%s: * %s %s\n", msg.Params[0], prefix.Name, msg.Params[1][7:])
		} else {
			return fmt.Sprintf("CTCP request from %s to %s: %s\n", prefix.Name, msg.Params[0], msg.Params[1])
		}
	case "CTCPREPLY":
		return fmt.Sprintf("CTCP reply from %s to %s: %s\n", prefix.Name, msg.Params[0], msg.Params[1])
	default:
		return fmt.Sprint("RAW:", msg.String())
	}
}

/* This function should really be in termbox... */
func drawString(x, y int, str string) {
	i := 0
	for _, runeValue := range str {
		putCh(x+i, y, runeValue)
		i += runewidth.RuneWidth(runeValue)
	}
}

func printstring(str string, anchor, width int) int {
	retval := anchor - (len(str) / width)
	retval--
	y := retval
	clearLine(y, width)
	i := 0
	for _, runeValue := range str {
		if i%width == 0 && i >= width {
			y++
			clearLine(y, width)
		}
		putCh(i%width, y, runeValue)
		i += runewidth.RuneWidth(runeValue)
	}
	return retval
}

func updatescreen() {
	width, height := termbox.Size()
	anchor := height - 2
	i := buffer.Front()
	for anchor > 0 && i != nil {
		anchor = printstring(i.Value.(string), anchor, width)
		i = i.Next()
	}
	termbox.Flush()
}

func clearLine(y, width int) {
	for i := 0; i < width; i++ {
		eraseCh(i, y)
	}
}

func putCh(x, y int, ch rune) {
	termbox.SetCell(x, y, ch, termbox.ColorDefault, termbox.ColorDefault)
}

func eraseCh(x, y int) {
	putCh(x, y, ' ')
}

func GetString() string {
	width, height := termbox.Size()
	clearLine(height-1, width)
	retval := ""
	cursor := 0
	tlen := len(target) + 3
	drawString(0, height-1, fmt.Sprint(target, " >"))
	for {
		drawString(tlen, height-1, retval)
		termbox.SetCursor(tlen+cursor, height-1)
		termbox.Flush()
		ev := termbox.PollEvent()
		if ev.Ch == 0 {
			switch ev.Key {
			case termbox.KeySpace:
				retval += " "
				cursor++
			case termbox.KeyEnter:
				if len(retval) > 0 {
					termbox.HideCursor()
					return retval
				}
			case termbox.KeyDelete:
				fallthrough
			case termbox.KeyBackspace:
				if cursor > 0 {
					r, rs :=
						utf8.DecodeLastRuneInString(retval)
					retval = retval[0 : len(retval)-rs]
					eraseCh(cursor+(tlen)-runewidth.RuneWidth(r), height-1)
					cursor -= runewidth.RuneWidth(r)
				}
			}
		} else if ev.Ch > 31 {
			retval += string(ev.Ch)
			cursor += runewidth.RuneWidth(ev.Ch)
		}
	}
}

func sendtobuffer(str string) {
	buffer.PushFront(StripMircFormatting(str))
	if buffer.Len() > limit {
		buffer.Remove(buffer.Back())
	}
}

func printmsg(msg *irc.Message) {
	sendtobuffer(formatmessage(msg))
}

func initscreen() {
	termbox.Init()
}

func outputloop(client *Client, scrollback int) {
	limit = scrollback
	buffer = list.New()
	for {
		msg, err := client.Receive()
		if err != nil {
			fmt.Println("Output loop closing:", err)
			return
		}
		if msg != nil {
			if msg.Command != "PING" {
				printmsg(msg)
			}
			if msg.Command == "ERROR" {
				return
			}
		}
		updatescreen()
	}
}

func inputloop(client *Client) {
	defer termbox.Close()
	for {
		text := GetString()
		sendtobuffer(fmt.Sprint("(", target, ") ", text))
		updatescreen()
		if Command(client, text) {
			return
		}
	}
}
<commit_msg>more readable privmsg format<commit_after>package main

import (
	"container/list"
	"fmt"
	"github.com/mattn/go-runewidth"
	"github.com/nsf/termbox-go"
	"gopkg.in/sorcix/irc.v2"
	"strings"
	"unicode/utf8"
)

var buffer *list.List
var limit int

func formatmessage(msg *irc.Message) string {
	prefix := msg.Prefix
	if prefix == nil {
		prefix = &irc.Prefix{"unknown", "unknown", "unknown"}
	}
	switch msg.Command {
	case "JOIN":
		return fmt.Sprintf("%s has joined %s\n", prefix.Name, msg.Params[0])
	case "PRIVMSG":
		return fmt.Sprintf("(%s) %s: %s\n", msg.Params[0], prefix.Name, msg.Params[1])
	case "MODE":
		return fmt.Sprintf("%s sets mode %s\n", prefix.Name, strings.Join(msg.Params[0:], " "))
	case "NOTICE":
		return fmt.Sprintf("Notice from %s to %s: %s\n", prefix.Name, msg.Params[0], msg.Params[1])
	case "001":
		fallthrough
	case "002":
		fallthrough
	case "003":
		fallthrough
	case "372":
		fallthrough
	case "375":
		fallthrough
	case "376":
		return fmt.Sprintln(msg.Params[1])
	case "QUIT":
		return fmt.Sprintf("%s has quit (%s)\n", prefix.Name, msg.Params[0])
	case "CTCP":
		if strings.HasPrefix(msg.Params[1], "ACTION") {
			return fmt.Sprintf("%s: * %s %s\n", msg.Params[0], prefix.Name, msg.Params[1][7:])
		} else {
			return fmt.Sprintf("CTCP request from %s to %s: %s\n", prefix.Name, msg.Params[0], msg.Params[1])
		}
	case "CTCPREPLY":
		return fmt.Sprintf("CTCP reply from %s to %s: %s\n", prefix.Name, msg.Params[0], msg.Params[1])
	default:
		return fmt.Sprint("RAW:", msg.String())
	}
}

/* This function should really be in termbox... */
func drawString(x, y int, str string) {
	i := 0
	for _, runeValue := range str {
		putCh(x+i, y, runeValue)
		i += runewidth.RuneWidth(runeValue)
	}
}

func printstring(str string, anchor, width int) int {
	retval := anchor - (len(str) / width)
	retval--
	y := retval
	clearLine(y, width)
	i := 0
	for _, runeValue := range str {
		if i%width == 0 && i >= width {
			y++
			clearLine(y, width)
		}
		putCh(i%width, y, runeValue)
		i += runewidth.RuneWidth(runeValue)
	}
	return retval
}

func updatescreen() {
	width, height := termbox.Size()
	anchor := height - 2
	i := buffer.Front()
	for anchor > 0 && i != nil {
		anchor = printstring(i.Value.(string), anchor, width)
		i = i.Next()
	}
	termbox.Flush()
}

func clearLine(y, width int) {
	for i := 0; i < width; i++ {
		eraseCh(i, y)
	}
}

func putCh(x, y int, ch rune) {
	termbox.SetCell(x, y, ch, termbox.ColorDefault, termbox.ColorDefault)
}

func eraseCh(x, y int) {
	putCh(x, y, ' ')
}

func GetString() string {
	width, height := termbox.Size()
	clearLine(height-1, width)
	retval := ""
	cursor := 0
	tlen := len(target) + 3
	drawString(0, height-1, fmt.Sprint(target, " >"))
	for {
		drawString(tlen, height-1, retval)
		termbox.SetCursor(tlen+cursor, height-1)
		termbox.Flush()
		ev := termbox.PollEvent()
		if ev.Ch == 0 {
			switch ev.Key {
			case termbox.KeySpace:
				retval += " "
				cursor++
			case termbox.KeyEnter:
				if len(retval) > 0 {
					termbox.HideCursor()
					return retval
				}
			case termbox.KeyDelete:
				fallthrough
			case termbox.KeyBackspace:
				if cursor > 0 {
					r, rs :=
						utf8.DecodeLastRuneInString(retval)
					retval = retval[0 : len(retval)-rs]
					eraseCh(cursor+(tlen)-runewidth.RuneWidth(r), height-1)
					cursor -= runewidth.RuneWidth(r)
				}
			}
		} else if ev.Ch > 31 {
			retval += string(ev.Ch)
			cursor += runewidth.RuneWidth(ev.Ch)
		}
	}
}

func sendtobuffer(str string) {
	buffer.PushFront(StripMircFormatting(str))
	if buffer.Len() > limit {
		buffer.Remove(buffer.Back())
	}
}

func printmsg(msg *irc.Message) {
	sendtobuffer(formatmessage(msg))
}

func initscreen() {
	termbox.Init()
}

func outputloop(client *Client, scrollback int) {
	limit = scrollback
	buffer = list.New()
	for {
		msg, err := client.Receive()
		if err != nil {
			fmt.Println("Output loop closing:", err)
			return
		}
		if msg != nil {
			if msg.Command != "PING" {
				printmsg(msg)
			}
			if msg.Command == "ERROR" {
				return
			}
		}
		updatescreen()
	}
}

func inputloop(client *Client) {
	defer termbox.Close()
	for {
		text := GetString()
		sendtobuffer(fmt.Sprint("(", target, ") ", text))
		updatescreen()
		if Command(client, text) {
			return
		}
	}
}
<|endoftext|>


<commit_before>package infermedica

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"

	"strconv"

	"github.com/pkg/errors"
)

type SearchRes struct {
	ID    string `json:"id"`
	Label string `json:"label"`
}

type SearchType string

const (
	SearchTypeSymptom    SearchType = "symptom"
	SearchTypeRiskFactor SearchType = "risk_factor"
	SearchTypeLabTest    SearchType = "lab_test"
)

func (s SearchType) Ptr() *SearchType { return &s }
func (s SearchType) String() string   { return string(s) }

func (s *SearchType) IsValid() bool {
	_, err := SearchTypeFromString(s.String())
	if err != nil {
		return false
	}
	return true
}

func SearchTypeFromString(x string) (SearchType, error) {
	switch strings.ToLower(x) {
	case "symptom":
		return SearchTypeSymptom, nil
	case "risk_factor":
		return SearchTypeRiskFactor, nil
	case "lab_test":
		return SearchTypeLabTest, nil
	default:
		return "", fmt.Errorf("Unexpected value for search type: %q", x)
	}
}

func (a *App) Search(phrase string, sex Sex, maxResults int, st SearchType) (*[]SearchRes, error) {
	if !sex.IsValid() {
		return nil, errors.New("Unexpected value for Sex")
	}
	if !st.IsValid() {
		return nil, errors.New("Unexpected value for search type")
	}
	url := "search?phrase=" + phrase + "&sex=" + sex.String() + "&max_results=" + strconv.Itoa(maxResults) + "&type=" + st.String()
	req, err := a.prepareRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	r := []SearchRes{}
	err = json.NewDecoder(res.Body).Decode(&r)
	if err != nil {
		return nil, err
	}
	return &r, nil
}
<commit_msg>fixed problem with spaces<commit_after>package infermedica

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"strconv"

	"github.com/pkg/errors"
)

type SearchRes struct {
	ID    string `json:"id"`
	Label string `json:"label"`
}

type SearchType string

const (
	SearchTypeSymptom    SearchType = "symptom"
	SearchTypeRiskFactor SearchType = "risk_factor"
	SearchTypeLabTest    SearchType = "lab_test"
)

func (s SearchType) Ptr() *SearchType { return &s }
func (s SearchType) String() string   { return string(s) }

func (s *SearchType) IsValid() bool {
	_, err := SearchTypeFromString(s.String())
	if err != nil {
		return false
	}
	return true
}

func SearchTypeFromString(x string) (SearchType, error) {
	switch strings.ToLower(x) {
	case "symptom":
		return SearchTypeSymptom, nil
	case "risk_factor":
		return SearchTypeRiskFactor, nil
	case "lab_test":
		return SearchTypeLabTest, nil
	default:
		return "", fmt.Errorf("Unexpected value for search type: %q", x)
	}
}

func (a *App) Search(phrase string, sex Sex, maxResults int, st SearchType) (*[]SearchRes, error) {
	if !sex.IsValid() {
		return nil, errors.New("Unexpected value for Sex")
	}
	if !st.IsValid() {
		return nil, errors.New("Unexpected value for search type")
	}
	url := "search?phrase=" + url.QueryEscape(phrase) + "&sex=" + sex.String() + "&max_results=" + strconv.Itoa(maxResults) + "&type=" + st.String()
	req, err := a.prepareRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	r := []SearchRes{}
	err = json.NewDecoder(res.Body).Decode(&r)
	if err != nil {
		return nil, err
	}
	return &r, nil
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"sort"
)

const (
	evalInf = 50000
)

// SearchPosition searches a Position to the specified depth via iterative deepening
// and returns the evaluation score relative to the side to move and the search results.
func SearchPosition(ctx context.Context, pos Position, depth int) (score int, results Results) {
	for d := 1; d <= depth; d++ {
		if ctx.Err() != nil {
			return
		}
		score, results = negamax(ctx, pos, results, NewWindow(-evalInf, evalInf), d, true, make([]int, d+1))
	}
	return
}

// negamax recursively searches a Position to the specified depth and returns the evaluation score
// relative to the side to move and the search results. It employs alpha-beta pruning outside of
// the specified Window. If recommended is zero length, negamax will generate and search all
// pseudo-legal moves; if recommended moves are provided, they must all be pseudo-legal, and
// only they will be searched.
func negamax(ctx context.Context, pos Position, recommended Results, w Window, depth int, allowCutoff bool, counters []int) (bestScore int, results Results) {
	counters[0]++

	if len(recommended) == 0 {
		moves := Candidates(pos) // pseudo-legal

		if !anyLegal(pos, moves) { // checkmate or stalemate
			if IsCheck(pos) {
				bestScore = -evalInf
			}
			return
		}
		if depth == 0 {
			bestScore = Eval(pos) * evalMult(pos.ToMove)
			return
		}

		recommended = make(Results, 0, len(moves))
		for _, m := range moves {
			recommended = append(recommended, Result{move: m})
		}
	}

	results = make(Results, 0, len(recommended))
	for _, r := range recommended {
		newpos := Make(pos, r.move)
		if !IsLegal(newpos) {
			continue
		}

		score, cont := negamax(ctx, newpos, Results{}, w.Neg(), depth-1, allowCutoff, counters[1:])
		score *= -1

		// Store the score in results relative to White
		results = append(results, Result{move: r.move, score: score * evalMult(pos.ToMove), depth: depth - 1, cont: cont})

		if depth >= 3 && ctx.Err() != nil {
			break
		}

		var constrained, ok bool
		w, constrained, ok = w.Constrain(score)
		if constrained {
			// improved lower bound
		}
		if !ok && allowCutoff {
			break
		}
	}

	if pos.ToMove == White {
		// highest score first
		sort.Sort(sort.Reverse(results))
	} else {
		sort.Sort(results)
	}
	return w.alpha, results
}

// IsPseudoLegal returns whether a Move is pseudo-legal in a Position.
// A move is pseudo-legal if the square to be moved from contains the specified piece
// and the piece is capable of moving to the target square if doing so would not put the king in check.
func IsPseudoLegal(pos Position, move Move) bool {
	for _, m := range PieceMoves[move.Piece](pos) {
		if m == move {
			return true
		}
	}
	return false
}

// IsLegal returns whether a Position results from a legal move.
// A position is illegal if the king of the side that just moved is in check.
func IsLegal(pos Position) bool {
	return !IsAttacked(pos, pos.KingSquare[pos.Opp()], pos.ToMove)
}

// IsCheck returns whether the king of the side to move is in check.
func IsCheck(pos Position) bool {
	return IsAttacked(pos, pos.KingSquare[pos.ToMove], pos.Opp())
}

// IsTerminal returns whether or not a Position is checkmate or stalemate.
// A position is checkmate or stalemate if the side to move has no legal moves.
func IsTerminal(pos Position) bool { return !anyLegal(pos, Candidates(pos)) }

// anyLegal returns whether any of the given Moves are legal in the given Position.
func anyLegal(pos Position, moves []Move) bool {
	for _, m := range moves {
		if IsLegal(Make(pos, m)) {
			return true
		}
	}
	return false
}

// A Result holds the score of a searched Move (relative to White),
// the search depth, and the continuation Results of the search.
type Result struct {
	move  Move
	score int
	depth int
	cont  Results
}

// String returns a string representation of r, including its principal variation.
func (r Result) String() string {
	return fmt.Sprintf("%v (%v) %v", float64(r.score)/100, r.depth, r.PV())
}

// PV returns a string representation of r's principal variation.
func (r Result) PV() string {
	if r.depth == 0 || len(r.cont) == 0 {
		return LongAlgebraic(r.move)
	}
	return LongAlgebraic(r.move) + " " + r.cont[0].PV()
}

// A Results contains the results of a search. Results satisfies sort.Interface.
// A Results should be sorted by the function generating it before it is returned.
type Results []Result

func (r Results) Len() int      { return len(r) }
func (r Results) Swap(i, j int) { r[i], r[j] = r[j], r[i] }

// Less reports whether the Result with index i should sort before the Result with index j.
// It sorts first by depth and then by score.
func (r Results) Less(i, j int) bool {
	return r[i].depth < r[j].depth || r[i].depth == r[j].depth && r[i].score < r[j].score
}

// String returns a string representation of all Result values in r.
func (r Results) String() string {
	var s string
	for _, result := range r {
		s += fmt.Sprintf("%v\n", result.String())
	}
	return s
}

// A Window represents the bounds of a position's evaluation.
type Window struct{ alpha, beta int }

// NewWindow returns a Window with the given bounds. It panics if alpha > beta.
func NewWindow(alpha, beta int) Window {
	if alpha > beta {
		panic(fmt.Sprintf("invalid window bounds %v, %v", alpha, beta))
	}
	return Window{alpha, beta}
}

// Constrain updates the lower bound of w, if applicable, and returns the updated window,
// whether the lower bound was changed, and whether the returned Window remains valid.
func (w Window) Constrain(n int) (c Window, constrained bool, ok bool) {
	if n <= w.alpha {
		return w, false, true
	}
	return Window{n, w.beta}, true, n <= w.beta
}

// Neg returns the additive inverse of w.
func (w Window) Neg() Window { return Window{-w.beta, -w.alpha} }
<commit_msg>factor SortFor<commit_after>package main

import (
	"context"
	"fmt"
	"sort"
)

const (
	evalInf = 50000
)

// SearchPosition searches a Position to the specified depth via iterative deepening
// and returns the evaluation score relative to the side to move and the search results.
func SearchPosition(ctx context.Context, pos Position, depth int) (score int, results Results) {
	for d := 1; d <= depth; d++ {
		if ctx.Err() != nil {
			return
		}
		score, results = negamax(ctx, pos, results, NewWindow(-evalInf, evalInf), d, true, make([]int, d+1))
	}
	return
}

// negamax recursively searches a Position to the specified depth and returns the evaluation score
// relative to the side to move and the search results. It employs alpha-beta pruning outside of
// the specified Window. If recommended is zero length, negamax will generate and search all
// pseudo-legal moves; if recommended moves are provided, they must all be pseudo-legal, and
// only they will be searched.
func negamax(ctx context.Context, pos Position, recommended Results, w Window, depth int, allowCutoff bool, counters []int) (bestScore int, results Results) {
	counters[0]++

	if len(recommended) == 0 {
		moves := Candidates(pos) // pseudo-legal

		if !anyLegal(pos, moves) { // checkmate or stalemate
			if IsCheck(pos) {
				bestScore = -evalInf
			}
			return
		}
		if depth == 0 {
			bestScore = Eval(pos) * evalMult(pos.ToMove)
			return
		}

		recommended = make(Results, 0, len(moves))
		for _, m := range moves {
			recommended = append(recommended, Result{move: m})
		}
	}

	results = make(Results, 0, len(recommended))
	for _, r := range recommended {
		newpos := Make(pos, r.move)
		if !IsLegal(newpos) {
			continue
		}

		score, cont := negamax(ctx, newpos, Results{}, w.Neg(), depth-1, allowCutoff, counters[1:])
		score *= -1

		// Store the score in results relative to White
		results = append(results, Result{move: r.move, score: score * evalMult(pos.ToMove), depth: depth - 1, cont: cont})

		if depth >= 3 && ctx.Err() != nil {
			break
		}

		var constrained, ok bool
		w, constrained, ok = w.Constrain(score)
		if constrained {
			// improved lower bound
		}
		if !ok && allowCutoff {
			break
		}
	}

	results.SortFor(pos.ToMove)
	return w.alpha, results
}

// IsPseudoLegal returns whether a Move is pseudo-legal in a Position.
// A move is pseudo-legal if the square to be moved from contains the specified piece
// and the piece is capable of moving to the target square if doing so would not put the king in check.
func IsPseudoLegal(pos Position, move Move) bool {
	for _, m := range PieceMoves[move.Piece](pos) {
		if m == move {
			return true
		}
	}
	return false
}

// IsLegal returns whether a Position results from a legal move.
// A position is illegal if the king of the side that just moved is in check.
func IsLegal(pos Position) bool {
	return !IsAttacked(pos, pos.KingSquare[pos.Opp()], pos.ToMove)
}

// IsCheck returns whether the king of the side to move is in check.
func IsCheck(pos Position) bool {
	return IsAttacked(pos, pos.KingSquare[pos.ToMove], pos.Opp())
}

// IsTerminal returns whether or not a Position is checkmate or stalemate.
// A position is checkmate or stalemate if the side to move has no legal moves.
func IsTerminal(pos Position) bool { return !anyLegal(pos, Candidates(pos)) }

// anyLegal returns whether any of the given Moves are legal in the given Position.
func anyLegal(pos Position, moves []Move) bool {
	for _, m := range moves {
		if IsLegal(Make(pos, m)) {
			return true
		}
	}
	return false
}

// A Result holds the score of a searched Move (relative to White),
// the search depth, and the continuation Results of the search.
type Result struct {
	move  Move
	score int
	depth int
	cont  Results
}

// String returns a string representation of r, including its principal variation.
func (r Result) String() string {
	return fmt.Sprintf("%v (%v) %v", float64(r.score)/100, r.depth, r.PV())
}

// PV returns a string representation of r's principal variation.
func (r Result) PV() string {
	if r.depth == 0 || len(r.cont) == 0 {
		return LongAlgebraic(r.move)
	}
	return LongAlgebraic(r.move) + " " + r.cont[0].PV()
}

// A Results contains the results of a search. Results satisfies sort.Interface.
// A Results should be sorted by the function generating it before it is returned.
type Results []Result

func (r Results) Len() int      { return len(r) }
func (r Results) Swap(i, j int) { r[i], r[j] = r[j], r[i] }

// Less reports whether the Result with index i should sort before the Result with index j.
// It sorts first by depth and then by score.
func (r Results) Less(i, j int) bool {
	return r[i].depth < r[j].depth || r[i].depth == r[j].depth && r[i].score < r[j].score
}

// SortFor sorts r beginning with the best move for c.
func (r Results) SortFor(c Color) {
	if c == White {
		// highest score first
		sort.Sort(sort.Reverse(r))
	} else {
		sort.Sort(r)
	}
}

// String returns a string representation of all Result values in r.
func (r Results) String() string {
	var s string
	for _, result := range r {
		s += fmt.Sprintf("%v\n", result.String())
	}
	return s
}

// A Window represents the bounds of a position's evaluation.
type Window struct{ alpha, beta int }

// NewWindow returns a Window with the given bounds. It panics if alpha > beta.
func NewWindow(alpha, beta int) Window {
	if alpha > beta {
		panic(fmt.Sprintf("invalid window bounds %v, %v", alpha, beta))
	}
	return Window{alpha, beta}
}

// Constrain updates the lower bound of w, if applicable, and returns the updated window,
// whether the lower bound was changed, and whether the returned Window remains valid.
func (w Window) Constrain(n int) (c Window, constrained bool, ok bool) {
	if n <= w.alpha {
		return w, false, true
	}
	return Window{n, w.beta}, true, n <= w.beta
}

// Neg returns the additive inverse of w.
func (w Window) Neg() Window { return Window{-w.beta, -w.alpha} }
<|endoftext|>


<commit_before>package utils

/*
 * Copyright 2016, 2017 SUSE LLC
 *
 * 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.
 */

import (
	"fmt"
	"os"

	"golang.org/x/sys/unix"
)

// MaxSendfdLen is the maximum length of the name of a file descriptor being
// sent using SendFd. The name of the file handle returned by RecvFd will never
// be larger than this value.
const MaxNameLen = 4096

// oobSpace is the size of the oob slice required to store a single FD. Note
// that unix.UnixRights appears to make the assumption that fd is always int32,
// so sizeof(fd) = 4.
var oobSpace = unix.CmsgSpace(4)

// RecvFd waits for a file descriptor to be sent over the given AF_UNIX
// socket. The file name of the remote file descriptor will be recreated
// locally (it is sent as non-auxiliary data in the same payload).
func RecvFd(socket *os.File) (*os.File, error) {
	// For some reason, unix.Recvmsg uses the length rather than the capacity
	// when passing the msg_controllen and other attributes to recvmsg.  So we
	// have to actually set the length.
	name := make([]byte, MaxNameLen)
	oob := make([]byte, oobSpace)

	sockfd := socket.Fd()
	n, oobn, _, _, err := unix.Recvmsg(int(sockfd), name, oob, 0)
	if err != nil {
		return nil, err
	}

	if n >= MaxNameLen || oobn != oobSpace {
		return nil, fmt.Errorf("recvfd: incorrect number of bytes read (n=%d oobn=%d)", n, oobn)
	}

	// Truncate.
	name = name[:n]
	oob = oob[:oobn]

	scms, err := unix.ParseSocketControlMessage(oob)
	if err != nil {
		return nil, err
	}
	if len(scms) != 1 {
		return nil, fmt.Errorf("recvfd: number of SCMs is not 1: %d", len(scms))
	}
	scm := scms[0]

	fds, err := unix.ParseUnixRights(&scm)
	if err != nil {
		return nil, err
	}
	if len(fds) != 1 {
		return nil, fmt.Errorf("recvfd: number of fds is not 1: %d", len(fds))
	}
	fd := uintptr(fds[0])

	return os.NewFile(fd, string(name)), nil
}

// SendFd sends a file descriptor over the given AF_UNIX socket. In
// addition, the file.Name() of the given file will also be sent as
// non-auxiliary data in the same payload (allowing to send contextual
// information for a file descriptor).
func SendFd(socket *os.File, name string, fd uintptr) error {
	if len(name) >= MaxNameLen {
		return fmt.Errorf("sendfd: filename too long: %s", name)
	}
	oob := unix.UnixRights(int(fd))
	return unix.Sendmsg(int(socket.Fd()), []byte(name), oob, nil, 0)
}
<commit_msg>libcontainer/utils: introduce SendFds<commit_after>package utils

/*
 * Copyright 2016, 2017 SUSE LLC
 *
 * 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.
 */

import (
	"fmt"
	"os"

	"golang.org/x/sys/unix"
)

// MaxSendfdLen is the maximum length of the name of a file descriptor being
// sent using SendFd. The name of the file handle returned by RecvFd will never
// be larger than this value.
const MaxNameLen = 4096

// oobSpace is the size of the oob slice required to store a single FD. Note
// that unix.UnixRights appears to make the assumption that fd is always int32,
// so sizeof(fd) = 4.
var oobSpace = unix.CmsgSpace(4)

// RecvFd waits for a file descriptor to be sent over the given AF_UNIX
// socket. The file name of the remote file descriptor will be recreated
// locally (it is sent as non-auxiliary data in the same payload).
func RecvFd(socket *os.File) (*os.File, error) {
	// For some reason, unix.Recvmsg uses the length rather than the capacity
	// when passing the msg_controllen and other attributes to recvmsg.  So we
	// have to actually set the length.
	name := make([]byte, MaxNameLen)
	oob := make([]byte, oobSpace)

	sockfd := socket.Fd()
	n, oobn, _, _, err := unix.Recvmsg(int(sockfd), name, oob, 0)
	if err != nil {
		return nil, err
	}

	if n >= MaxNameLen || oobn != oobSpace {
		return nil, fmt.Errorf("recvfd: incorrect number of bytes read (n=%d oobn=%d)", n, oobn)
	}

	// Truncate.
	name = name[:n]
	oob = oob[:oobn]

	scms, err := unix.ParseSocketControlMessage(oob)
	if err != nil {
		return nil, err
	}
	if len(scms) != 1 {
		return nil, fmt.Errorf("recvfd: number of SCMs is not 1: %d", len(scms))
	}
	scm := scms[0]

	fds, err := unix.ParseUnixRights(&scm)
	if err != nil {
		return nil, err
	}
	if len(fds) != 1 {
		return nil, fmt.Errorf("recvfd: number of fds is not 1: %d", len(fds))
	}
	fd := uintptr(fds[0])

	return os.NewFile(fd, string(name)), nil
}

// SendFd sends a file descriptor over the given AF_UNIX socket. In
// addition, the file.Name() of the given file will also be sent as
// non-auxiliary data in the same payload (allowing to send contextual
// information for a file descriptor).
func SendFd(socket *os.File, name string, fd uintptr) error {
	if len(name) >= MaxNameLen {
		return fmt.Errorf("sendfd: filename too long: %s", name)
	}
	return SendFds(socket, []byte(name), int(fd))
}

// SendFds sends a list of files descriptor and msg over the given AF_UNIX socket.
func SendFds(socket *os.File, msg []byte, fds ...int) error {
	oob := unix.UnixRights(fds...)
	return unix.Sendmsg(int(socket.Fd()), msg, oob, nil, 0)
}
<|endoftext|>