<commit_before>// Copyright © 2009-2010 Esko Luontola <www.orfjackal.net>
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0

package examples

import (
	"gospec"
	. "gospec"
	"strings"
)

func ExecutionModelSpec(c gospec.Context) {

	// "Before block", for example common variables for use in all specs.
	commonVariable := ""

	c.Specify("The following child specs modify the same variable", func() {

		// "Before block", for example initialization for this group of specs.
		commonVariable += "x"

		// All sibling specs (specs which are declared within a common parent)
		// are fully isolated from each other. The following three siblings are
		// executed concurrently, each in its own goroutine, and each of them
		// has its own copy of the local variables declared in its parent specs.
		c.Specify("I modify it, but none of my siblings will know it", func() {
			commonVariable += "1"
		})
		c.Specify("Also I modify it, but none of my siblings will know it", func() {
			commonVariable += "2"
		})
		c.Specify("Also I modify it, but none of my siblings will know it", func() {
			commonVariable += "3"
		})

		// "After block", for example tear down of changes to the file system.
		commonVariable += "y"

		// Depending on which of the previous siblings was executed this time,
		// there are three possible values for the variable:
		c.Expect(commonVariable, Satisfies, commonVariable == "x1y" ||
		                                    commonVariable == "x2y" ||
		                                    commonVariable == "x3y")
	})

	c.Specify("You can nest", func() {
		c.Specify("as many specs", func() {
			c.Specify("as you wish.", func() {
				c.Specify("GoSpec does not impose artificial limits, "+
				          "so you can organize your specs freely.", func() {
				})
			})
		})
	})

	c.Specify("The distinction between 'Expect' and 'Assume'", func() {
		// When we have non-trivial test setup code, then it is often useful to
		// explicitly state our assumptions about the state of the system under
		// test, before the body of the test is executed.
		//
		// Otherwise it could happen that the test passes even though the code
		// is broken, or then we get lots of unhelpful error messages from the
		// body of the test, even though the bug was in the test setup.
		//
		// For this use case, GoSpec provides 'Assume' in addition to 'Expect'.
		// Use 'Assume' when the test assumes the correct functionin of some
		// behaviour which is not the focus of the current test:
		//
		// - When an 'Expect' fails, then the child specs are executed normally.
		//
		// - When an 'Assume' fails, then the child specs are NOT executed. This
		//   helps to prevent lots of false alarms from the child specs, when
		//   the real problem was in the test setup.

		// Some very complex test setup code
		input := ""
		for ch := 'a'; ch <= 'c'; ch++ {
			input += string(ch)
		}

		// Uncomment this line to add a bug into the test setup:
		//input += " bug"

		// Uncomment one of the following asserts to see their difference:
		//c.Expect(input, Equals, "abc")
		//c.Assume(input, Equals, "abc")

		c.Specify("When a string is made all uppercase", func() {
			result := strings.ToUpper(input)

			c.Specify("it is all uppercase", func() {
				c.Expect(result, Equals, "ABC")
			})
			c.Specify("its length is not changed", func() {
				c.Expect(len(result), Equals, 3)
			})
		})
	})
}
<commit_msg>Fixed typo<commit_after>// Copyright © 2009-2010 Esko Luontola <www.orfjackal.net>
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0

package examples

import (
	"gospec"
	. "gospec"
	"strings"
)

func ExecutionModelSpec(c gospec.Context) {

	// "Before block", for example common variables for use in all specs.
	commonVariable := ""

	c.Specify("The following child specs modify the same variable", func() {

		// "Before block", for example initialization for this group of specs.
		commonVariable += "x"

		// All sibling specs (specs which are declared within a common parent)
		// are fully isolated from each other. The following three siblings are
		// executed concurrently, each in its own goroutine, and each of them
		// has its own copy of the local variables declared in its parent specs.
		c.Specify("I modify it, but none of my siblings will know it", func() {
			commonVariable += "1"
		})
		c.Specify("Also I modify it, but none of my siblings will know it", func() {
			commonVariable += "2"
		})
		c.Specify("Also I modify it, but none of my siblings will know it", func() {
			commonVariable += "3"
		})

		// "After block", for example tear down of changes to the file system.
		commonVariable += "y"

		// Depending on which of the previous siblings was executed this time,
		// there are three possible values for the variable:
		c.Expect(commonVariable, Satisfies, commonVariable == "x1y" ||
		                                    commonVariable == "x2y" ||
		                                    commonVariable == "x3y")
	})

	c.Specify("You can nest", func() {
		c.Specify("as many specs", func() {
			c.Specify("as you wish.", func() {
				c.Specify("GoSpec does not impose artificial limits, "+
				          "so you can organize your specs freely.", func() {
				})
			})
		})
	})

	c.Specify("The distinction between 'Expect' and 'Assume'", func() {
		// When we have non-trivial test setup code, then it is often useful to
		// explicitly state our assumptions about the state of the system under
		// test, before the body of the test is executed.
		//
		// Otherwise it could happen that the test passes even though the code
		// is broken, or then we get lots of unhelpful error messages from the
		// body of the test, even though the bug was in the test setup.
		//
		// For this use case, GoSpec provides 'Assume' in addition to 'Expect'.
		// Use 'Assume' when the test assumes the correct functioning of some
		// behaviour which is not the focus of the current test:
		//
		// - When an 'Expect' fails, then the child specs are executed normally.
		//
		// - When an 'Assume' fails, then the child specs are NOT executed. This
		//   helps to prevent lots of false alarms from the child specs, when
		//   the real problem was in the test setup.

		// Some very complex test setup code
		input := ""
		for ch := 'a'; ch <= 'c'; ch++ {
			input += string(ch)
		}

		// Uncomment this line to add a bug into the test setup:
		//input += " bug"

		// Uncomment one of the following asserts to see their difference:
		//c.Expect(input, Equals, "abc")
		//c.Assume(input, Equals, "abc")

		c.Specify("When a string is made all uppercase", func() {
			result := strings.ToUpper(input)

			c.Specify("it is all uppercase", func() {
				c.Expect(result, Equals, "ABC")
			})
			c.Specify("its length is not changed", func() {
				c.Expect(len(result), Equals, 3)
			})
		})
	})
}
<|endoftext|>


<commit_before>package in

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/h2non/filetype"
)

const (
	mimeTypeZip  = "application/zip"
	mimeTypeTar  = "application/x-tar"
	mimeTypeGzip = "application/gzip"
)

func isSupportedMimeType(mimeType string) bool {
	return mimeType == mimeTypeZip ||
		mimeType == mimeTypeTar ||
		mimeType == mimeTypeGzip
}

func getMimeType(path string) (string, error) {
	f, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer f.Close()

	bs, err := bufio.NewReader(f).Peek(512)
	if err != nil && err != io.EOF {
		return "", err
	}

	kind, err := filetype.Match(bs)
	if err != nil {
		return "", err
	}

	return kind.MIME.Value, nil
}

func unpack(mimeType, sourcePath string) error {
	for mimeType == mimeTypeGzip {
		var err error
		sourcePath, err = unpackGzip(sourcePath)
		if err != nil {
			return err
		}

		mimeType, err = getMimeType(sourcePath)
		if err != nil {
			return err
		}
	}

	destinationDir := filepath.Dir(sourcePath)

	switch mimeType {
	case mimeTypeZip:
		return unpackZip(sourcePath, destinationDir)
	case mimeTypeTar:
		return unpackTar(sourcePath, destinationDir)
	}

	return nil
}

func unpackZip(sourcePath, destinationDir string) error {
	cmd := exec.Command("unzip", "-P", "", "-d", destinationDir, sourcePath)

	return cmd.Run()
}

func unpackGzip(sourcePath string) (string, error) {
	cmd := exec.Command("gunzip", sourcePath)
	err := cmd.Run()
	if err != nil {
		return "", err
	}

	destinationDir := filepath.Dir(sourcePath)
	fileInfos, err := ioutil.ReadDir(destinationDir)
	if err != nil {
		return "", fmt.Errorf("failed to read dir: %s", err)
	}
	if len(fileInfos) != 1 {
		return "", fmt.Errorf("%d files found after gunzip; expected 1", len(fileInfos))
	}

	return filepath.Join(destinationDir, fileInfos[0].Name()), err
}

func unpackTar(sourcePath, destinationDir string) error {
	cmd := exec.Command("tar", "xf", sourcePath, "-C", destinationDir)

	return cmd.Run()
}
<commit_msg>Restore previous gunzip logic to preserve original filename<commit_after>package in

import (
	"bufio"
	"compress/gzip"
	"io"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/h2non/filetype"
)

const (
	mimeTypeZip  = "application/zip"
	mimeTypeTar  = "application/x-tar"
	mimeTypeGzip = "application/gzip"
)

func isSupportedMimeType(mimeType string) bool {
	return mimeType == mimeTypeZip ||
		mimeType == mimeTypeTar ||
		mimeType == mimeTypeGzip
}

func getMimeType(path string) (string, error) {
	f, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer f.Close()

	bs, err := bufio.NewReader(f).Peek(512)
	if err != nil && err != io.EOF {
		return "", err
	}

	kind, err := filetype.Match(bs)
	if err != nil {
		return "", err
	}

	return kind.MIME.Value, nil
}

func unpack(mimeType, sourcePath string) error {
	for mimeType == mimeTypeGzip {
		var err error
		sourcePath, err = unpackGzip(sourcePath)
		if err != nil {
			return err
		}

		mimeType, err = getMimeType(sourcePath)
		if err != nil {
			return err
		}
	}

	destinationDir := filepath.Dir(sourcePath)

	switch mimeType {
	case mimeTypeZip:
		return unpackZip(sourcePath, destinationDir)
	case mimeTypeTar:
		return unpackTar(sourcePath, destinationDir)
	}

	return nil
}

func unpackZip(sourcePath, destinationDir string) error {
	cmd := exec.Command("unzip", "-P", "", "-d", destinationDir, sourcePath)

	return cmd.Run()
}

func unpackGzip(sourcePath string) (string, error) {
	reader, err := os.Open(sourcePath)
	if err != nil {
		return "", err
	}
	defer reader.Close()

	archive, err := gzip.NewReader(reader)
	if err != nil {
		return "", err
	}
	defer archive.Close()

	var destinationPath string
	if archive.Name != "" {
		destinationPath = filepath.Join(filepath.Dir(sourcePath), archive.Name)
	} else {
		destinationPath = sourcePath + ".uncompressed"
	}

	writer, err := os.Create(destinationPath)
	if err != nil {
		return "", err
	}
	defer writer.Close()

	_, err = io.Copy(writer, archive)
	return destinationPath, err
}

func unpackTar(sourcePath, destinationDir string) error {
	cmd := exec.Command("tar", "xf", sourcePath, "-C", destinationDir)

	return cmd.Run()
}
<|endoftext|>


<commit_before>package main

import (
	glmenu "github.com/4ydx/glmenu"
	glfw "github.com/go-gl/glfw3"
	"github.com/go-gl/mathgl/mgl32"
)

func mainMenuInit(window *glfw.Window) (err error) {
	fontScale := int32(25)
	width, height := window.GetSize()
	mainMenu.Load(float32(width), float32(height), fontScale)
	mainMenu.Font.SetTextLowerBound(0.3)
	mainMenu.ResizeWindow(float32(width), float32(height))
	mainMenu.Background = mgl32.Vec4{0, 0, .20, 0}

	//2DO: sounds

	// start
	var label glmenu.Label
	mainMenu.AddLabel(&label, "Start")

	label.AddShadow(1.5, 0, 0, 0)
	label.Text.SetColor(0, 250.0/255.0, 154.0/255.0, 1)
	label.OnClick = func(label *glmenu.Label, xPos, yPos float64) (err error) {
		mainMenu.Toggle()
		return
	}
	label.OnHover = func(label *glmenu.Label, xPos, yPos float64) (err error) {
		label.Text.AddScale(mainMenu.TextScaleRate)
		return
	}
	label.OnNotHover = func(label *glmenu.Label) (err error) {
		label.Text.AddScale(-mainMenu.TextScaleRate)
		return
	}

	// options
	var label2 glmenu.Label
	mainMenu.AddLabel(&label2, "Options")

	label2.AddShadow(1.5, 0, 0, 0)
	label2.Text.SetColor(0.5, 0.5, 0.5, 1)
	label2.OnClick = func(label *glmenu.Label, xPos, yPos float64) (err error) {
		mainMenu.Toggle()
		optionMenu.Toggle()
		return
	}
	label2.OnHover = func(label *glmenu.Label, xPos, yPos float64) (err error) {
		label.Text.AddScale(mainMenu.TextScaleRate)
		return
	}
	label2.OnNotHover = func(label *glmenu.Label) (err error) {
		label.Text.AddScale(-mainMenu.TextScaleRate)
		return
	}

	// quit
	var label3 glmenu.Label
	mainMenu.AddLabel(&label3, "Quit")

	label3.AddShadow(1.5, 0, 0, 0)
	label3.Text.SetColor(0.5, 0.5, 0.5, 1)
	label3.OnClick = func(label *glmenu.Label, xPos, yPos float64) (err error) {
		window.SetShouldClose(true)
		return
	}
	label3.OnHover = func(label *glmenu.Label, xPos, yPos float64) (err error) {
		label.Text.AddScale(mainMenu.TextScaleRate)
		return
	}
	label3.OnNotHover = func(label *glmenu.Label) (err error) {
		label.Text.AddScale(-mainMenu.TextScaleRate)
		return
	}

	// simple centering of values
	totalHeight := label.Text.X2.Y - label.Text.X1.Y + label2.Text.X2.Y - label2.Text.X1.Y + label2.Text.X2.Y - label2.Text.X1.Y
	label.Text.SetPosition(0, totalHeight/2)
	label.UpdateShadow(1.5, 0, 0, 0)
	label3.Text.SetPosition(0, -totalHeight/2)
	label3.UpdateShadow(1.5, 0, 0, 0)

	return
}
<commit_msg>Change color on hover.<commit_after>package main

import (
	glmenu "github.com/4ydx/glmenu"
	glfw "github.com/go-gl/glfw3"
	"github.com/go-gl/mathgl/mgl32"
)

func mainMenuInit(window *glfw.Window) (err error) {
	fontScale := int32(25)
	width, height := window.GetSize()
	mainMenu.Load(float32(width), float32(height), fontScale)
	mainMenu.Font.SetTextLowerBound(0.3)
	mainMenu.ResizeWindow(float32(width), float32(height))
	mainMenu.Background = mgl32.Vec4{0, 0, .20, 0}

	//2DO: sounds

	// start
	var label glmenu.Label
	mainMenu.AddLabel(&label, "Start")

	label.Text.SetColor(0.5, 0.5, 0.5, 1)
	label.OnClick = func(label *glmenu.Label, xPos, yPos float64) (err error) {
		mainMenu.Toggle()
		return
	}
	label.OnHover = func(label *glmenu.Label, xPos, yPos float64) (err error) {
		label.Text.SetColor(0, 250.0/255.0, 154.0/255.0, 1)
		label.Text.AddScale(mainMenu.TextScaleRate)
		return
	}
	label.OnNotHover = func(label *glmenu.Label) (err error) {
		label.Text.SetColor(0.5, 0.5, 0.5, 1)
		label.Text.AddScale(-mainMenu.TextScaleRate)
		return
	}

	// options
	var label2 glmenu.Label
	mainMenu.AddLabel(&label2, "Options")

	label2.Text.SetColor(0.5, 0.5, 0.5, 1)
	label2.OnClick = func(label *glmenu.Label, xPos, yPos float64) (err error) {
		mainMenu.Toggle()
		optionMenu.Toggle()
		return
	}
	label2.OnHover = func(label *glmenu.Label, xPos, yPos float64) (err error) {
		label.Text.SetColor(0, 250.0/255.0, 154.0/255.0, 1)
		label.Text.AddScale(mainMenu.TextScaleRate)
		return
	}
	label2.OnNotHover = func(label *glmenu.Label) (err error) {
		label.Text.SetColor(0.5, 0.5, 0.5, 1)
		label.Text.AddScale(-mainMenu.TextScaleRate)
		return
	}

	// quit
	var label3 glmenu.Label
	mainMenu.AddLabel(&label3, "Quit")

	label3.Text.SetColor(0.5, 0.5, 0.5, 1)
	label3.OnClick = func(label *glmenu.Label, xPos, yPos float64) (err error) {
		window.SetShouldClose(true)
		return
	}
	label3.OnHover = func(label *glmenu.Label, xPos, yPos float64) (err error) {
		label.Text.SetColor(0, 250.0/255.0, 154.0/255.0, 1)
		label.Text.AddScale(mainMenu.TextScaleRate)
		return
	}
	label3.OnNotHover = func(label *glmenu.Label) (err error) {
		label.Text.SetColor(0.5, 0.5, 0.5, 1)
		label.Text.AddScale(-mainMenu.TextScaleRate)
		return
	}

	// simple centering of values
	totalHeight := label.Text.X2.Y - label.Text.X1.Y + label2.Text.X2.Y - label2.Text.X1.Y + label2.Text.X2.Y - label2.Text.X1.Y
	label.Text.SetPosition(0, totalHeight/2)
	label3.Text.SetPosition(0, -totalHeight/2)

	return
}
<|endoftext|>


<commit_before>package sesstype

import (
	"fmt"
	"io"
	"log"

	"github.com/nickng/cfsm"
)

// STOP is the 'close' message.
const STOP = "STOP"

// CFSMs captures a CFSM system syserated from a Session.
type CFSMs struct {
	Sys    *cfsm.System
	Chans  map[Role]*cfsm.CFSM
	Roles  map[Role]*cfsm.CFSM
	States map[*cfsm.CFSM]map[string]*cfsm.State
}

func NewCFSMs(s *Session) *CFSMs {
	sys := &CFSMs{
		Sys:    cfsm.NewSystem(),
		Chans:  make(map[Role]*cfsm.CFSM),
		Roles:  make(map[Role]*cfsm.CFSM),
		States: make(map[*cfsm.CFSM]map[string]*cfsm.State),
	}
	for _, c := range s.Chans {
		m := sys.Sys.NewMachine()
		m.Comment = c.Name()
		sys.Chans[c] = m
		defer sys.chanToMachine(c, c.Type().String(), m)
	}
	for role, root := range s.Types {
		m := sys.Sys.NewMachine()
		m.Comment = role.Name()
		sys.Roles[role] = m
		sys.States[m] = make(map[string]*cfsm.State)
		sys.rootToMachine(role, root, m)
		if m.IsEmpty() {
			log.Println("Machine", m.ID, "is empty")
			sys.Sys.RemoveMachine(m.ID)
			delete(sys.Roles, role)
			delete(sys.States, m)
		}
	}
	return sys
}

// WriteTo implementers io.WriterTo interface.
func (sys *CFSMs) WriteTo(w io.Writer) (int64, error) {
	n, err := w.Write([]byte(sys.Sys.String()))
	return int64(n), err
}

// PrintSummary shows the statistics of the CFSM syseration.
func (sys *CFSMs) PrintSummary() {
	fmt.Printf("Total of %d CFSMs (%d are channels)\n",
		len(sys.Roles)+len(sys.Chans), len(sys.Chans))
	for r, m := range sys.Chans {
		fmt.Printf("\t%d\t= %s (channel)\n", m.ID, r.Name())
	}
	for r, m := range sys.Roles {
		fmt.Printf("\t%d\t= %s\n", m.ID, r.Name())
	}
}

func (sys *CFSMs) rootToMachine(role Role, root Node, m *cfsm.CFSM) {
	q0 := m.NewState()
	sys.nodeToMachine(role, root, q0, m)
	m.Start = q0
}

func (sys *CFSMs) nodeToMachine(role Role, node Node, q0 *cfsm.State, m *cfsm.CFSM) {
	switch node := node.(type) {
	case *SendNode:
		to, ok := sys.Chans[node.To()]
		if !ok {
			log.Fatal("Cannot Send to unknown channel", node.To().Name())
		}
		tr := cfsm.NewSend(to, node.To().Type().String())
		qSent := m.NewState()
		for _, c := range node.Children() {
			sys.nodeToMachine(role, c, qSent, m)
		}
		tr.SetNext(qSent)
		q0.AddTransition(tr)

	case *RecvNode:
		from, ok := sys.Chans[node.From()]
		if !ok {
			log.Fatal("Cannot Recv from unknown channel", node.From().Name())
		}
		msg := node.From().Type().String()
		if node.Stop() {
			msg = STOP
		}
		tr := cfsm.NewRecv(from, msg)
		qRcvd := m.NewState()
		for _, c := range node.Children() {
			sys.nodeToMachine(role, c, qRcvd, m)
		}
		tr.SetNext(qRcvd)
		q0.AddTransition(tr)

	case *EndNode:
		ch, ok := sys.Chans[node.Chan()]
		if !ok {
			log.Fatal("Cannot Close unknown channel", node.Chan().Name())
		}
		tr := cfsm.NewSend(ch, STOP)
		qEnd := m.NewState()
		for _, c := range node.Children() {
			sys.nodeToMachine(role, c, qEnd, m)
		}
		tr.SetNext(qEnd)
		q0.AddTransition(tr)

	case *NewChanNode, *EmptyBodyNode: // Skip
		for _, c := range node.Children() {
			sys.nodeToMachine(role, c, q0, m)
		}

	case *LabelNode:
		sys.States[m][node.Name()] = q0
		for _, c := range node.Children() {
			sys.nodeToMachine(role, c, q0, m)
		}

	case *GotoNode:
		qTarget := sys.States[m][node.Name()]
		for _, c := range node.Children() {
			sys.nodeToMachine(role, c, qTarget, m)
		}

	default:
		log.Fatalf("Unhandled node type %T", node)
	}
}

func (sys *CFSMs) chanToMachine(ch Role, T string, m *cfsm.CFSM) {
	q0 := m.NewState()
	qEnd := m.NewState()
	for _, machine := range sys.Roles {
		q1 := m.NewState()
		// q0 -- Recv --> q1
		tr0 := cfsm.NewRecv(machine, T)
		tr0.SetNext(q1)
		q0.AddTransition(tr0)
		// q1 -- Send --> q0
		for _, machine2 := range sys.Roles {
			if machine.ID != machine2.ID {
				tr1 := cfsm.NewSend(machine2, T)
				tr1.SetNext(q0)
				q1.AddTransition(tr1)
			}
		}
		// q0 -- STOP --> qEnd (same qEnd)
		tr2 := cfsm.NewRecv(machine, STOP)
		tr2.SetNext(qEnd)
		qEnd.AddTransition(tr2)
		// qEnd -- STOP --> qEnd
		for _, machine2 := range sys.Roles {
			if machine.ID != machine2.ID {
				tr3 := cfsm.NewSend(machine2, STOP)
				tr3.SetNext(qEnd)
				qEnd.AddTransition(tr3)
			}
		}
	}
	m.Start = q0
}
<commit_msg>Detect and handle self-loop in CFSM. Closes #20.<commit_after>package sesstype

import (
	"fmt"
	"io"
	"log"

	"github.com/nickng/cfsm"
)

// STOP is the 'close' message.
const STOP = "STOP"

// CFSMs captures a CFSM system syserated from a Session.
type CFSMs struct {
	Sys    *cfsm.System
	Chans  map[Role]*cfsm.CFSM
	Roles  map[Role]*cfsm.CFSM
	States map[*cfsm.CFSM]map[string]*cfsm.State
}

func NewCFSMs(s *Session) *CFSMs {
	sys := &CFSMs{
		Sys:    cfsm.NewSystem(),
		Chans:  make(map[Role]*cfsm.CFSM),
		Roles:  make(map[Role]*cfsm.CFSM),
		States: make(map[*cfsm.CFSM]map[string]*cfsm.State),
	}
	for _, c := range s.Chans {
		m := sys.Sys.NewMachine()
		m.Comment = c.Name()
		sys.Chans[c] = m
		defer sys.chanToMachine(c, c.Type().String(), m)
	}
	for role, root := range s.Types {
		m := sys.Sys.NewMachine()
		m.Comment = role.Name()
		sys.Roles[role] = m
		sys.States[m] = make(map[string]*cfsm.State)
		sys.rootToMachine(role, root, m)
		if m.IsEmpty() {
			log.Println("Machine", m.ID, "is empty")
			sys.Sys.RemoveMachine(m.ID)
			delete(sys.Roles, role)
			delete(sys.States, m)
		}
	}
	return sys
}

// WriteTo implementers io.WriterTo interface.
func (sys *CFSMs) WriteTo(w io.Writer) (int64, error) {
	n, err := w.Write([]byte(sys.Sys.String()))
	return int64(n), err
}

// PrintSummary shows the statistics of the CFSM syseration.
func (sys *CFSMs) PrintSummary() {
	fmt.Printf("Total of %d CFSMs (%d are channels)\n",
		len(sys.Roles)+len(sys.Chans), len(sys.Chans))
	for r, m := range sys.Chans {
		fmt.Printf("\t%d\t= %s (channel)\n", m.ID, r.Name())
	}
	for r, m := range sys.Roles {
		fmt.Printf("\t%d\t= %s\n", m.ID, r.Name())
	}
}

func (sys *CFSMs) rootToMachine(role Role, root Node, m *cfsm.CFSM) {
	q0 := m.NewState()
	sys.nodeToMachine(role, root, q0, m)
	m.Start = q0
}

func (sys *CFSMs) nodeToMachine(role Role, node Node, q0 *cfsm.State, m *cfsm.CFSM) {
	switch node := node.(type) {
	case *SendNode:
		to, ok := sys.Chans[node.To()]
		if !ok {
			log.Fatal("Cannot Send to unknown channel", node.To().Name())
		}
		tr := cfsm.NewSend(to, node.To().Type().String())
		var qSent *cfsm.State
		if sys.isSelfLoop(m, q0, node) {
			qSent = q0
		} else {
			qSent = m.NewState()
			for _, c := range node.Children() {
				sys.nodeToMachine(role, c, qSent, m)
			}
		}
		tr.SetNext(qSent)
		q0.AddTransition(tr)

	case *RecvNode:
		from, ok := sys.Chans[node.From()]
		if !ok {
			log.Fatal("Cannot Recv from unknown channel", node.From().Name())
		}
		msg := node.From().Type().String()
		if node.Stop() {
			msg = STOP
		}
		tr := cfsm.NewRecv(from, msg)
		var qRcvd *cfsm.State
		if sys.isSelfLoop(m, q0, node) {
			qRcvd = q0
		} else {
			qRcvd = m.NewState()
			for _, c := range node.Children() {
				sys.nodeToMachine(role, c, qRcvd, m)
			}
		}
		tr.SetNext(qRcvd)
		q0.AddTransition(tr)

	case *EndNode:
		ch, ok := sys.Chans[node.Chan()]
		if !ok {
			log.Fatal("Cannot Close unknown channel", node.Chan().Name())
		}
		tr := cfsm.NewSend(ch, STOP)
		qEnd := m.NewState()
		for _, c := range node.Children() {
			sys.nodeToMachine(role, c, qEnd, m)
		}
		tr.SetNext(qEnd)
		q0.AddTransition(tr)

	case *NewChanNode, *EmptyBodyNode: // Skip
		for _, c := range node.Children() {
			sys.nodeToMachine(role, c, q0, m)
		}

	case *LabelNode:
		sys.States[m][node.Name()] = q0
		for _, c := range node.Children() {
			sys.nodeToMachine(role, c, q0, m)
		}

	case *GotoNode:
		qTarget := sys.States[m][node.Name()]
		for _, c := range node.Children() {
			sys.nodeToMachine(role, c, qTarget, m)
		}

	default:
		log.Fatalf("Unhandled node type %T", node)
	}
}

func (sys *CFSMs) chanToMachine(ch Role, T string, m *cfsm.CFSM) {
	q0 := m.NewState()
	qEnd := m.NewState()
	for _, machine := range sys.Roles {
		q1 := m.NewState()
		// q0 -- Recv --> q1
		tr0 := cfsm.NewRecv(machine, T)
		tr0.SetNext(q1)
		q0.AddTransition(tr0)
		// q1 -- Send --> q0
		for _, machine2 := range sys.Roles {
			if machine.ID != machine2.ID {
				tr1 := cfsm.NewSend(machine2, T)
				tr1.SetNext(q0)
				q1.AddTransition(tr1)
			}
		}
		// q0 -- STOP --> qEnd (same qEnd)
		tr2 := cfsm.NewRecv(machine, STOP)
		tr2.SetNext(qEnd)
		qEnd.AddTransition(tr2)
		// qEnd -- STOP --> qEnd
		for _, machine2 := range sys.Roles {
			if machine.ID != machine2.ID {
				tr3 := cfsm.NewSend(machine2, STOP)
				tr3.SetNext(qEnd)
				qEnd.AddTransition(tr3)
			}
		}
	}
	m.Start = q0
}

// isSelfLoop returns true if the action of node is a self-loop
// i.e. the state before and after the transition is the same.
func (sys *CFSMs) isSelfLoop(m *cfsm.CFSM, q0 *cfsm.State, node Node) bool {
	if len(node.Children()) == 1 {
		if gotoNode, ok := node.Child(0).(*GotoNode); ok {
			if loopback, ok := sys.States[m][gotoNode.Name()]; ok {
				return loopback == q0
			}
		}
	}
	return false
}
<|endoftext|>


<commit_before>package source

import (
	"fmt"
	"io/ioutil"

	"github.com/pachyderm/pachyderm/src/pkg/clone"
	"github.com/pachyderm/pachyderm/src/pps"
	"github.com/pachyderm/pachyderm/src/pps/parse"
)

type sourcer struct{}

func newSourcer() *sourcer {
	return &sourcer{}
}

func (s *sourcer) GetDirPathAndPipeline(pipelineSource *pps.PipelineSource) (string, *pps.Pipeline, error) {
	return getDirPathAndPipeline(pipelineSource)
}

func getDirPathAndPipeline(pipelineSource *pps.PipelineSource) (string, *pps.Pipeline, error) {
	if pipelineSource.GithubPipelineSource != nil {
		dirPath, err := githubClone(pipelineSource.GithubPipelineSource)
		if err != nil {
			return "", nil, err
		}
		dirPath = filePath.Join(dirPath, pipelineSource.GithubPipelineSource.ContextDir)
		pipeline, err := parse.NewParser().ParsePipeline(dirPath)
		if err != nil {
			return "", nil, err
		}
		pipeline.PipelineSourceId = pipelineSource.Id
		return dirPath, pipeline, nil
	}
	return "", nil, fmt.Errorf("must specify pipeline source")
}

func githubClone(githubPipelineSource *pps.GithubPipelineSource) (string, error) {
	dirPath, err := makeTempDir()
	if err != nil {
		return "", err
	}
	if err := clone.GithubClone(
		dirPath,
		githubPipelineSource.User,
		githubPipelineSource.Repository,
		githubPipelineSource.Branch,
		githubPipelineSource.CommitId,
		githubPipelineSource.AccessToken,
	); err != nil {
		return "", err
	}
	return dirPath, nil
}

func makeTempDir() (string, error) {
	return ioutil.TempDir("", "pachyderm")
}
<commit_msg>fix compile for src/pps/source<commit_after>package source

import (
	"fmt"
	"io/ioutil"
	"path/filepath"

	"github.com/pachyderm/pachyderm/src/pkg/clone"
	"github.com/pachyderm/pachyderm/src/pps"
	"github.com/pachyderm/pachyderm/src/pps/parse"
)

type sourcer struct{}

func newSourcer() *sourcer {
	return &sourcer{}
}

func (s *sourcer) GetDirPathAndPipeline(pipelineSource *pps.PipelineSource) (string, *pps.Pipeline, error) {
	return getDirPathAndPipeline(pipelineSource)
}

func getDirPathAndPipeline(pipelineSource *pps.PipelineSource) (string, *pps.Pipeline, error) {
	if pipelineSource.GetGithubPipelineSource() != nil {
		dirPath, err := githubClone(pipelineSource.GetGithubPipelineSource())
		if err != nil {
			return "", nil, err
		}
		dirPath = filepath.Join(dirPath, pipelineSource.GetGithubPipelineSource().ContextDir)
		pipeline, err := parse.NewParser().ParsePipeline(dirPath)
		if err != nil {
			return "", nil, err
		}
		pipeline.PipelineSourceId = pipelineSource.Id
		return dirPath, pipeline, nil
	}
	return "", nil, fmt.Errorf("must specify pipeline source")
}

func githubClone(githubPipelineSource *pps.GithubPipelineSource) (string, error) {
	dirPath, err := makeTempDir()
	if err != nil {
		return "", err
	}
	if err := clone.GithubClone(
		dirPath,
		githubPipelineSource.User,
		githubPipelineSource.Repository,
		githubPipelineSource.Branch,
		githubPipelineSource.CommitId,
		githubPipelineSource.AccessToken,
	); err != nil {
		return "", err
	}
	return dirPath, nil
}

func makeTempDir() (string, error) {
	return ioutil.TempDir("", "pachyderm")
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/scottferg/Go-SDL/sdl"
)

var (
	joy []*sdl.Joystick
)

const (
	JoypadButtonA      = 1
	JoypadButtonB      = 2
	JoypadButtonStart  = 9
	JoypadButtonSelect = 8
	JoypadAxisUp       = -32768
	JoypadAxisDown     = 32767
	JoypadAxisLeft     = -32768
	JoypadAxisRight    = 32767
)

type Controller struct {
	ButtonState [16]Word
	StrobeState int
	LastWrite   Word
	LastYAxis   int
	LastXAxis   int
	BitOffset   int
}

func (c *Controller) SetJoypadAxisState(a, d int, v Word, offset int) {
	resetAxis := func(d int) {
		switch d {
		case 0:
			if c.LastYAxis != -1 {
				c.ButtonState[c.LastYAxis+offset] = 0x40
			}
		case 1:
			if c.LastXAxis != -1 {
				c.ButtonState[c.LastXAxis+offset] = 0x40
			}
		}
	}

	if a == 4 || a == 1 {
		switch d {
		case JoypadAxisUp: // Up
			resetAxis(0)
			c.ButtonState[4+offset] = v
			c.LastYAxis = 4
		case JoypadAxisDown: // Down
			resetAxis(0)
			c.ButtonState[5+offset] = v
			c.LastYAxis = 5
		default:
			resetAxis(0)
			c.LastYAxis = -1
		}
	} else if a == 3 || a == 0 {
		switch d {
		case JoypadAxisLeft: // Left
			resetAxis(1)
			c.ButtonState[6+offset] = v
			c.LastXAxis = 6
		case JoypadAxisRight: // Right
			resetAxis(1)
			c.ButtonState[7+offset] = v
			c.LastXAxis = 7
		default:
			resetAxis(1)
			c.LastXAxis = -1
		}
	}
}

func (c *Controller) SetJoypadButtonState(k int, v Word, offset int) {
	switch k {
	case JoypadButtonA: // A
		c.ButtonState[0+offset] = v
	case JoypadButtonB: // B
		c.ButtonState[1+offset] = v
	case JoypadButtonSelect: // Select
		c.ButtonState[2+offset] = v
	case JoypadButtonStart: // Start
		c.ButtonState[3+offset] = v
	}
}

func (c *Controller) SetButtonState(k sdl.KeyboardEvent, v Word, offset int) {
	switch k.Keysym.Sym {
	case sdl.K_z: // A
		c.ButtonState[0+offset] = v
	case sdl.K_x: // B
		c.ButtonState[1+offset] = v
	case sdl.K_RSHIFT: // Select
		c.ButtonState[2+offset] = v
	case sdl.K_RETURN: // Start
		c.ButtonState[3+offset] = v
	case sdl.K_m: // Start
		c.ButtonState[11] = v
	case sdl.K_UP: // Up
		c.ButtonState[4+offset] = v
	case sdl.K_DOWN: // Down
		c.ButtonState[5+offset] = v
	case sdl.K_LEFT: // Left
		c.ButtonState[6+offset] = v
	case sdl.K_RIGHT: // Right
		c.ButtonState[7+offset] = v
	}
}

func (c *Controller) AxisDown(a, d int, offset int) {
	c.SetJoypadAxisState(a, d, 0x41, offset)
}

func (c *Controller) AxisUp(a, d int, offset int) {
	c.SetJoypadAxisState(a, d, 0x40, offset)
}

func (c *Controller) ButtonDown(b int, offset int) {
	c.SetJoypadButtonState(b, 0x41, offset)
}

func (c *Controller) ButtonUp(b int, offset int) {
	c.SetJoypadButtonState(b, 0x40, offset)
}

func (c *Controller) KeyDown(e sdl.KeyboardEvent, offset int) {
	c.SetButtonState(e, 0x41, offset)
}

func (c *Controller) KeyUp(e sdl.KeyboardEvent, offset int) {
	c.SetButtonState(e, 0x40, offset)
}

func (c *Controller) Write(v Word) {
	if v == 1 {
		// 0x4016 writes manage strobe state for
		// both controllers. 0x4017 is reserved for
		// APU
		pads[0].StrobeState = 0
		pads[1].StrobeState = 0
	}

	c.LastWrite = v
}

func (c *Controller) Read() (r Word) {
	if c.StrobeState < 16 {
		r = ((c.ButtonState[c.StrobeState+8] & 1) << 1) | c.ButtonState[c.StrobeState]
	} else if c.StrobeState == 18 {
		r = 0x0
	} else if c.StrobeState == 19 {
		r = 0x0
	} else {
		r = 0x0
	}

	c.StrobeState++

	if c.StrobeState == 24 {
		c.StrobeState = 0
	}

	return
}

func (c *Controller) Init(offset int) {
	for i := 0; i < len(c.ButtonState); i++ {
		c.ButtonState[i] = 0x40
	}
}

func ReadInput(r chan [2]int, i chan int) {
	for {
		select {
		case ev := <-sdl.Events:
			switch e := ev.(type) {
			case sdl.ResizeEvent:
				r <- [2]int{int(e.W), int(e.H)}
			case sdl.QuitEvent:
				running = false
				video.Close()
			case sdl.JoyAxisEvent:
				j := int(e.Which)

				index := j
				var offset int
				if j > 1 {
					offset = 8
					index = j%2
				}

				switch e.Value {
				// Same values for left/right
				case JoypadAxisUp:
					fallthrough
				case JoypadAxisDown:
					pads[index].AxisDown(int(e.Axis), int(e.Value), offset)
				default:
					pads[index].AxisUp(int(e.Axis), int(e.Value), offset)
				}
			case sdl.JoyButtonEvent:
				j := int(e.Which)

				index := j
				var offset int
				if j > 1 {
					offset = 8
					index = j%2
				}

				switch joy[j].GetButton(int(e.Button)) {
				case 1:
					pads[index].ButtonDown(int(e.Button), offset)
				case 0:
					pads[index].ButtonUp(int(e.Button), offset)
				}
			case sdl.KeyboardEvent:
				switch e.Keysym.Sym {
				case sdl.K_ESCAPE:
					running = false
				case sdl.K_r:
					// Trigger reset interrupt
					if e.Type == sdl.KEYDOWN {
						cpu.RequestInterrupt(InterruptReset)
					}
				case sdl.K_l:
					if e.Type == sdl.KEYDOWN {
						i <- LoadState
					}
				case sdl.K_p:
					if e.Type == sdl.KEYDOWN {
						// Enable/disable scanline sprite limiter flag
						ppu.SpriteLimitEnabled = !ppu.SpriteLimitEnabled
					}
				case sdl.K_s:
					if e.Type == sdl.KEYDOWN {
						i <- SaveState
					}
				case sdl.K_o:
					if e.Type == sdl.KEYDOWN {
						ppu.OverscanEnabled = !ppu.OverscanEnabled
					}
				case sdl.K_i:
					if e.Type == sdl.KEYDOWN {
						audioEnabled = !audioEnabled
					}
				case sdl.K_1:
					if e.Type == sdl.KEYDOWN {
						r <- [2]int{256, 240}
					}
				case sdl.K_2:
					if e.Type == sdl.KEYDOWN {
						r <- [2]int{512, 480}
					}
				case sdl.K_3:
					if e.Type == sdl.KEYDOWN {
						r <- [2]int{768, 720}
					}
				case sdl.K_4:
					if e.Type == sdl.KEYDOWN {
						r <- [2]int{1024, 960}
					}
				}

				switch e.Type {
				case sdl.KEYDOWN:
					pads[0].KeyDown(e, 0)
				case sdl.KEYUP:
					pads[0].KeyUp(e, 0)
				}
			}
		}
	}
}
<commit_msg>Don't let joypads on shared registers conflict with each others state<commit_after>package main

import (
	"github.com/scottferg/Go-SDL/sdl"
)

var (
	joy []*sdl.Joystick
)

const (
	JoypadButtonA      = 1
	JoypadButtonB      = 2
	JoypadButtonStart  = 9
	JoypadButtonSelect = 8
	JoypadAxisUp       = -32768
	JoypadAxisDown     = 32767
	JoypadAxisLeft     = -32768
	JoypadAxisRight    = 32767
)

type Controller struct {
	ButtonState [16]Word
	StrobeState int
	LastWrite   Word
	LastYAxis   [2]int
	LastXAxis   [2]int
}

func (c *Controller) SetJoypadAxisState(a, d int, v Word, offset int) {
	resetAxis := func(d, i int) {
		switch d {
		case 0:
			if c.LastYAxis[i] != -1 {
				c.ButtonState[c.LastYAxis[i]] = 0x40
			}
		case 1:
			if c.LastXAxis[i] != -1 {
				c.ButtonState[c.LastXAxis[i]] = 0x40
			}
		}
	}

	index := 0
	if offset > 0 {
		index = 1
	}

	if a == 4 || a == 1 {
		switch d {
		case JoypadAxisUp: // Up
			resetAxis(0, index)
			c.ButtonState[4+offset] = v
			c.LastYAxis[index] = 4+offset
		case JoypadAxisDown: // Down
			resetAxis(0, index)
			c.ButtonState[5+offset] = v
			c.LastYAxis[index] = 5+offset
		default:
			resetAxis(0, index)
			c.LastYAxis[index] = -1
		}
	} else if a == 3 || a == 0 {
		switch d {
		case JoypadAxisLeft: // Left
			resetAxis(1, index)
			c.ButtonState[6+offset] = v
			c.LastXAxis[index] = 6+offset
		case JoypadAxisRight: // Right
			resetAxis(1, index)
			c.ButtonState[7+offset] = v
			c.LastXAxis[index] = 7+offset
		default:
			resetAxis(1, index)
			c.LastXAxis[index] = -1
		}
	}
}

func (c *Controller) SetJoypadButtonState(k int, v Word, offset int) {
	switch k {
	case JoypadButtonA: // A
		c.ButtonState[0+offset] = v
	case JoypadButtonB: // B
		c.ButtonState[1+offset] = v
	case JoypadButtonSelect: // Select
		c.ButtonState[2+offset] = v
	case JoypadButtonStart: // Start
		c.ButtonState[3+offset] = v
	}
}

func (c *Controller) SetButtonState(k sdl.KeyboardEvent, v Word, offset int) {
	switch k.Keysym.Sym {
	case sdl.K_z: // A
		c.ButtonState[0+offset] = v
	case sdl.K_x: // B
		c.ButtonState[1+offset] = v
	case sdl.K_RSHIFT: // Select
		c.ButtonState[2+offset] = v
	case sdl.K_RETURN: // Start
		c.ButtonState[3+offset] = v
	case sdl.K_UP: // Up
		c.ButtonState[4+offset] = v
	case sdl.K_DOWN: // Down
		c.ButtonState[5+offset] = v
	case sdl.K_LEFT: // Left
		c.ButtonState[6+offset] = v
	case sdl.K_RIGHT: // Right
		c.ButtonState[7+offset] = v
	}
}

func (c *Controller) AxisDown(a, d int, offset int) {
	c.SetJoypadAxisState(a, d, 0x41, offset)
}

func (c *Controller) AxisUp(a, d int, offset int) {
	c.SetJoypadAxisState(a, d, 0x40, offset)
}

func (c *Controller) ButtonDown(b int, offset int) {
	c.SetJoypadButtonState(b, 0x41, offset)
}

func (c *Controller) ButtonUp(b int, offset int) {
	c.SetJoypadButtonState(b, 0x40, offset)
}

func (c *Controller) KeyDown(e sdl.KeyboardEvent, offset int) {
	c.SetButtonState(e, 0x41, offset)
}

func (c *Controller) KeyUp(e sdl.KeyboardEvent, offset int) {
	c.SetButtonState(e, 0x40, offset)
}

func (c *Controller) Write(v Word) {
	if v == 0 && c.LastWrite == 1 {
		// 0x4016 writes manage strobe state for
		// both controllers. 0x4017 is reserved for
		// APU
		pads[0].StrobeState = 0
		pads[1].StrobeState = 0
	}

	c.LastWrite = v
}

func (c *Controller) Read() (r Word) {
	if c.StrobeState < 8 {
		r = ((c.ButtonState[c.StrobeState+8] & 1) << 1) | c.ButtonState[c.StrobeState]
	} else if c.StrobeState == 18 {
		r = 0x0
	} else if c.StrobeState == 19 {
		r = 0x0
	} else {
		r = 0x0
	}

	c.StrobeState++

	if c.StrobeState == 24 {
		c.StrobeState = 0
	}

	return
}

func (c *Controller) Init(offset int) {
	for i := 0; i < len(c.ButtonState); i++ {
		c.ButtonState[i] = 0x40
	}
}

func ReadInput(r chan [2]int, i chan int) {
	for {
		select {
		case ev := <-sdl.Events:
			switch e := ev.(type) {
			case sdl.ResizeEvent:
				r <- [2]int{int(e.W), int(e.H)}
			case sdl.QuitEvent:
				running = false
				video.Close()
			case sdl.JoyAxisEvent:
				j := int(e.Which)

				index := j
				var offset int
				if j > 1 {
					offset = 8
					index = j % 2
				}

				switch e.Value {
				// Same values for left/right
				case JoypadAxisUp:
					fallthrough
				case JoypadAxisDown:
					pads[index].AxisDown(int(e.Axis), int(e.Value), offset)
				default:
					pads[index].AxisUp(int(e.Axis), int(e.Value), offset)
				}
			case sdl.JoyButtonEvent:
				j := int(e.Which)

				index := j
				var offset int
				if j > 1 {
					offset = 8
					index = j % 2
				}

				switch joy[j].GetButton(int(e.Button)) {
				case 1:
					pads[index].ButtonDown(int(e.Button), offset)
				case 0:
					pads[index].ButtonUp(int(e.Button), offset)
				}
			case sdl.KeyboardEvent:
				switch e.Keysym.Sym {
				case sdl.K_ESCAPE:
					running = false
				case sdl.K_r:
					// Trigger reset interrupt
					if e.Type == sdl.KEYDOWN {
						cpu.RequestInterrupt(InterruptReset)
					}
				case sdl.K_l:
					if e.Type == sdl.KEYDOWN {
						i <- LoadState
					}
				case sdl.K_p:
					if e.Type == sdl.KEYDOWN {
						// Enable/disable scanline sprite limiter flag
						ppu.SpriteLimitEnabled = !ppu.SpriteLimitEnabled
					}
				case sdl.K_s:
					if e.Type == sdl.KEYDOWN {
						i <- SaveState
					}
				case sdl.K_o:
					if e.Type == sdl.KEYDOWN {
						ppu.OverscanEnabled = !ppu.OverscanEnabled
					}
				case sdl.K_i:
					if e.Type == sdl.KEYDOWN {
						audioEnabled = !audioEnabled
					}
				case sdl.K_1:
					if e.Type == sdl.KEYDOWN {
						r <- [2]int{256, 240}
					}
				case sdl.K_2:
					if e.Type == sdl.KEYDOWN {
						r <- [2]int{512, 480}
					}
				case sdl.K_3:
					if e.Type == sdl.KEYDOWN {
						r <- [2]int{768, 720}
					}
				case sdl.K_4:
					if e.Type == sdl.KEYDOWN {
						r <- [2]int{1024, 960}
					}
				}

				switch e.Type {
				case sdl.KEYDOWN:
					pads[0].KeyDown(e, 0)
				case sdl.KEYUP:
					pads[0].KeyUp(e, 0)
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package access

import (
	"fmt"
	"net/http"
	"strings"
	"regexp"

	"github.com/Sirupsen/logrus"
	"github.com/shipyard/shipyard/auth"
	"github.com/shipyard/shipyard/controller/manager"
)

var (
	logger = logrus.New()
)

func defaultDeniedHandler(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "access denied", http.StatusForbidden)
}

type AccessRequired struct {
	deniedHandler http.Handler
	manager       manager.Manager
	acls          []*auth.ACL
}

func NewAccessRequired(m manager.Manager) *AccessRequired {
	acls := auth.DefaultACLs()
	a := &AccessRequired{
		deniedHandler: http.HandlerFunc(defaultDeniedHandler),
		manager:       m,
		acls:          acls,
	}
	return a
}

func (a *AccessRequired) Handler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		err := a.handleRequest(w, r)
		if err != nil {
			logger.Warnf("unauthorized request for %s from %s", r.URL.Path, r.RemoteAddr)
			return
		}
		h.ServeHTTP(w, r)
	})
}

func (a *AccessRequired) handleRequest(w http.ResponseWriter, r *http.Request) error {
	valid := false
	authHeader := r.Header.Get("X-Access-Token")
	parts := strings.Split(authHeader, ":")
	if len(parts) == 2 {
		// validate
		u := parts[0]
		token := parts[1]
		if err := a.manager.VerifyAuthToken(u, token); err == nil {
			acct, err := a.manager.Account(u)
			if err != nil {
				return err
			}
			// check role
			valid = a.checkAccess(acct, r.URL.Path, r.Method)
		}
	} else { // only check access for users; not service keys
		valid = true
	}

	if !valid {
		a.deniedHandler.ServeHTTP(w, r)
		return fmt.Errorf("access denied %s", r.RemoteAddr)
	}

	return nil
}

func (a *AccessRequired) checkRule(rule *auth.AccessRule, path, method string) bool {
	// check wildcard
	if rule.Path == "*" {
		return true
	}

	// check path
	if strings.HasPrefix(path, rule.Path) {
		// check method
		for _, m := range rule.Methods {
			if m == method {
				return true
			}
		}
	}

	return false
}

func (a *AccessRequired) checkRole(role string, path, method string) bool {
	for _, acl := range a.acls {
		// find role
		if acl.RoleName == role {
			for _, rule := range acl.Rules {
				if a.checkRule(rule, path, method) {
					return true
				}
			}
		}
	}

	return false
}

func (a *AccessRequired) checkOwnership(acct *auth.Account, path string, method string) bool {
	// we allow every login user create containers
	if path == "/containers/create" {
		return true
	}

	// check roles
	container_pattern := regexp.Compile("/containers/([0-9a-z]{64})(/(.+))?")
	if container_pattern == nil {
		return false
	}

	// check if it is an container operation
	matches := container_pattern.FindStringSubmatch(path)
	if matches == nil || len(matches) < 4 {
		return false
	}

	// get the container id
	containerId := matches[1]

	// get container info from client
	containerInfo,err := a.manager.Container(containerId)
	if err != nil {
		return false
	}

	// check the owner of the container
	if containerInfo.Config.Labels["owner"] == acct.Username {
		return true
	}

	return false
}

func (a *AccessRequired) checkAccess(acct *auth.Account, path string, method string) bool {

	fmt.Println("User "+acct.Username+" "+method+" path:"+path)
	if a.checkOwnership(acct, path, method) {
		return true
	}

	// check roles
	for _, role := range acct.Roles {
		// check acls
		if a.checkRole(role, path, method) {
			return true
		}
	}

	return false
}

func (a *AccessRequired) HandlerFuncWithNext(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	err := a.handleRequest(w, r)
	session, _ := a.manager.Store().Get(r, a.manager.StoreKey())
	username := session.Values["username"]
	if err != nil {
		logger.Warnf("access denied for %s to %s from %s", username, r.URL.Path, r.RemoteAddr)
		return
	}

	if next != nil {
		next(w, r)
	}
}
<commit_msg>add container ownership<commit_after>package access

import (
	"fmt"
	"net/http"
	"strings"
	"regexp"

	"github.com/Sirupsen/logrus"
	"github.com/shipyard/shipyard/auth"
	"github.com/shipyard/shipyard/controller/manager"
)

var (
	logger = logrus.New()
)

func defaultDeniedHandler(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "access denied", http.StatusForbidden)
}

type AccessRequired struct {
	deniedHandler http.Handler
	manager       manager.Manager
	acls          []*auth.ACL
}

func NewAccessRequired(m manager.Manager) *AccessRequired {
	acls := auth.DefaultACLs()
	a := &AccessRequired{
		deniedHandler: http.HandlerFunc(defaultDeniedHandler),
		manager:       m,
		acls:          acls,
	}
	return a
}

func (a *AccessRequired) Handler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		err := a.handleRequest(w, r)
		if err != nil {
			logger.Warnf("unauthorized request for %s from %s", r.URL.Path, r.RemoteAddr)
			return
		}
		h.ServeHTTP(w, r)
	})
}

func (a *AccessRequired) handleRequest(w http.ResponseWriter, r *http.Request) error {
	valid := false
	authHeader := r.Header.Get("X-Access-Token")
	parts := strings.Split(authHeader, ":")
	if len(parts) == 2 {
		// validate
		u := parts[0]
		token := parts[1]
		if err := a.manager.VerifyAuthToken(u, token); err == nil {
			acct, err := a.manager.Account(u)
			if err != nil {
				return err
			}
			// check role
			valid = a.checkAccess(acct, r.URL.Path, r.Method)
		}
	} else { // only check access for users; not service keys
		valid = true
	}

	if !valid {
		a.deniedHandler.ServeHTTP(w, r)
		return fmt.Errorf("access denied %s", r.RemoteAddr)
	}

	return nil
}

func (a *AccessRequired) checkRule(rule *auth.AccessRule, path, method string) bool {
	// check wildcard
	if rule.Path == "*" {
		return true
	}

	// check path
	if strings.HasPrefix(path, rule.Path) {
		// check method
		for _, m := range rule.Methods {
			if m == method {
				return true
			}
		}
	}

	return false
}

func (a *AccessRequired) checkRole(role string, path, method string) bool {
	for _, acl := range a.acls {
		// find role
		if acl.RoleName == role {
			for _, rule := range acl.Rules {
				if a.checkRule(rule, path, method) {
					return true
				}
			}
		}
	}

	return false
}

func (a *AccessRequired) checkOwnership(acct *auth.Account, path string, method string) bool {
	// we allow every login user create containers
	if path == "/containers/create" {
		return true
	}

	// check roles
	container_pattern,err := regexp.Compile("/containers/([0-9a-z]{64})(/(.+))?")
	if err != nil {
		return false
	}

	// check if it is an container operation
	matches := container_pattern.FindStringSubmatch(path)
	if matches == nil || len(matches) < 4 {
		return false
	}

	// get the container id
	containerId := matches[1]

	// get container info from client
	containerInfo,err := a.manager.Container(containerId)
	if err != nil {
		return false
	}

	// check the owner of the container
	if containerInfo.Config.Labels["owner"] == acct.Username {
		return true
	}

	return false
}

func (a *AccessRequired) checkAccess(acct *auth.Account, path string, method string) bool {

	fmt.Println("User "+acct.Username+" "+method+" path:"+path)
	if a.checkOwnership(acct, path, method) {
		return true
	}

	// check roles
	for _, role := range acct.Roles {
		// check acls
		if a.checkRole(role, path, method) {
			return true
		}
	}

	return false
}

func (a *AccessRequired) HandlerFuncWithNext(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	err := a.handleRequest(w, r)
	session, _ := a.manager.Store().Get(r, a.manager.StoreKey())
	username := session.Values["username"]
	if err != nil {
		logger.Warnf("access denied for %s to %s from %s", username, r.URL.Path, r.RemoteAddr)
		return
	}

	if next != nil {
		next(w, r)
	}
}
<|endoftext|>


<commit_before>// +build kvdb_etcd

package etcd

import (
	"fmt"
	"net"
	"net/url"
	"sync/atomic"
	"time"

	"github.com/coreos/etcd/embed"
)

const (
	// readyTimeout is the time until the embedded etcd instance should start.
	readyTimeout = 10 * time.Second

	// defaultEtcdPort is the start of the range for listening ports of
	// embedded etcd servers. Ports are monotonically increasing starting
	// from this number and are determined by the results of getFreePort().
	defaultEtcdPort = 2379

	// defaultNamespace is the namespace we'll use in our embedded etcd
	// instance. Since it is only used for testing, we'll use the namespace
	// name "test/" for this. Note that the namespace can be any string,
	// the trailing / is not required.
	defaultNamespace = "test/"
)

var (
	// lastPort is the last port determined to be free for use by a new
	// embedded etcd server. It should be used atomically.
	lastPort uint32 = defaultEtcdPort
)

// getFreePort returns the first port that is available for listening by a new
// embedded etcd server. It panics if no port is found and the maximum available
// TCP port is reached.
func getFreePort() int {
	port := atomic.AddUint32(&lastPort, 1)
	for port < 65535 {
		// If there are no errors while attempting to listen on this
		// port, close the socket and return it as available.
		addr := fmt.Sprintf("127.0.0.1:%d", port)
		l, err := net.Listen("tcp4", addr)
		if err == nil {
			err := l.Close()
			if err == nil {
				return int(port)
			}
		}
		port = atomic.AddUint32(&lastPort, 1)
	}

	// No ports available? Must be a mistake.
	panic("no ports available for listening")
}

// NewEmbeddedEtcdInstance creates an embedded etcd instance for testing,
// listening on random open ports. Returns the backend config and a cleanup
// func that will stop the etcd instance.
func NewEmbeddedEtcdInstance(path string, clientPort, peerPort uint16) (
	*Config, func(), error) {

	cfg := embed.NewConfig()
	cfg.Dir = path

	// To ensure that we can submit large transactions.
	cfg.MaxTxnOps = 8192
	cfg.MaxRequestBytes = 16384 * 1024

	// Listen on random free ports if no ports were specified.
	if clientPort == 0 {
		clientPort = uint16(getFreePort())
	}

	if peerPort == 0 {
		peerPort = uint16(getFreePort())
	}

	clientURL := fmt.Sprintf("127.0.0.1:%d", clientPort)
	peerURL := fmt.Sprintf("127.0.0.1:%d", peerPort)
	cfg.LCUrls = []url.URL{{Host: clientURL}}
	cfg.LPUrls = []url.URL{{Host: peerURL}}

	etcd, err := embed.StartEtcd(cfg)
	if err != nil {
		return nil, nil, err
	}

	select {
	case <-etcd.Server.ReadyNotify():
	case <-time.After(readyTimeout):
		etcd.Close()
		return nil, nil,
			fmt.Errorf("etcd failed to start after: %v", readyTimeout)
	}

	connConfig := &Config{
		Host:               "http://" + peerURL,
		User:               "user",
		Pass:               "pass",
		InsecureSkipVerify: true,
		Namespace:          defaultNamespace,
	}

	return connConfig, func() {
		etcd.Close()
	}, nil
}
<commit_msg>etcd: fix embbeded etcd connection cfg<commit_after>// +build kvdb_etcd

package etcd

import (
	"fmt"
	"net"
	"net/url"
	"sync/atomic"
	"time"

	"github.com/coreos/etcd/embed"
)

const (
	// readyTimeout is the time until the embedded etcd instance should start.
	readyTimeout = 10 * time.Second

	// defaultEtcdPort is the start of the range for listening ports of
	// embedded etcd servers. Ports are monotonically increasing starting
	// from this number and are determined by the results of getFreePort().
	defaultEtcdPort = 2379

	// defaultNamespace is the namespace we'll use in our embedded etcd
	// instance. Since it is only used for testing, we'll use the namespace
	// name "test/" for this. Note that the namespace can be any string,
	// the trailing / is not required.
	defaultNamespace = "test/"
)

var (
	// lastPort is the last port determined to be free for use by a new
	// embedded etcd server. It should be used atomically.
	lastPort uint32 = defaultEtcdPort
)

// getFreePort returns the first port that is available for listening by a new
// embedded etcd server. It panics if no port is found and the maximum available
// TCP port is reached.
func getFreePort() int {
	port := atomic.AddUint32(&lastPort, 1)
	for port < 65535 {
		// If there are no errors while attempting to listen on this
		// port, close the socket and return it as available.
		addr := fmt.Sprintf("127.0.0.1:%d", port)
		l, err := net.Listen("tcp4", addr)
		if err == nil {
			err := l.Close()
			if err == nil {
				return int(port)
			}
		}
		port = atomic.AddUint32(&lastPort, 1)
	}

	// No ports available? Must be a mistake.
	panic("no ports available for listening")
}

// NewEmbeddedEtcdInstance creates an embedded etcd instance for testing,
// listening on random open ports. Returns the backend config and a cleanup
// func that will stop the etcd instance.
func NewEmbeddedEtcdInstance(path string, clientPort, peerPort uint16) (
	*Config, func(), error) {

	cfg := embed.NewConfig()
	cfg.Dir = path

	// To ensure that we can submit large transactions.
	cfg.MaxTxnOps = 8192
	cfg.MaxRequestBytes = 16384 * 1024

	// Listen on random free ports if no ports were specified.
	if clientPort == 0 {
		clientPort = uint16(getFreePort())
	}

	if peerPort == 0 {
		peerPort = uint16(getFreePort())
	}

	clientURL := fmt.Sprintf("127.0.0.1:%d", clientPort)
	peerURL := fmt.Sprintf("127.0.0.1:%d", peerPort)
	cfg.LCUrls = []url.URL{{Host: clientURL}}
	cfg.LPUrls = []url.URL{{Host: peerURL}}

	etcd, err := embed.StartEtcd(cfg)
	if err != nil {
		return nil, nil, err
	}

	select {
	case <-etcd.Server.ReadyNotify():
	case <-time.After(readyTimeout):
		etcd.Close()
		return nil, nil,
			fmt.Errorf("etcd failed to start after: %v", readyTimeout)
	}

	connConfig := &Config{
		Host:               "http://" + clientURL,
		User:               "user",
		Pass:               "pass",
		InsecureSkipVerify: true,
		Namespace:          defaultNamespace,
	}

	return connConfig, func() {
		etcd.Close()
	}, nil
}
<|endoftext|>


<commit_before>/*
 * Copyright 2019 Dgraph Labs, Inc. and 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 cmd

import (
	"encoding/binary"
	"fmt"
	"log"
	"math/rand"
	"sync"
	"sync/atomic"
	"time"

	humanize "github.com/dustin/go-humanize"
	"github.com/spf13/cobra"

	"github.com/dgraph-io/badger/v2"
	"github.com/dgraph-io/badger/v2/options"
	"github.com/dgraph-io/badger/v2/pb"
	"github.com/dgraph-io/badger/v2/y"
)

var writeBenchCmd = &cobra.Command{
	Use:   "write",
	Short: "Writes random data to Badger to benchmark write speed.",
	Long: `
This command writes random data to Badger to benchmark write speed. Useful for testing and
performance analysis.
`,
	RunE: writeBench,
}

var (
	keySz    int
	valSz    int
	numKeys  float64
	force    bool
	sorted   bool
	showLogs bool

	sizeWritten    uint64
	entriesWritten uint64

	valueThreshold      int
	numVersions         int
	maxCacheSize        int64
	keepBlockIdxInCache bool
	keepBlocksInCache   bool
	maxBfCacheSize      int64
	vlogMaxEntries      uint32
	loadBloomsOnOpen    bool
	detectConflicts     bool
	compression         bool
	ttlDuration         uint32

	gcPeriod       string
	gcDiscardRatio float64
	gcSuccess      uint64
)

const (
	mil float64 = 1e6
)

func init() {
	benchCmd.AddCommand(writeBenchCmd)
	writeBenchCmd.Flags().IntVarP(&keySz, "key-size", "k", 32, "Size of key")
	writeBenchCmd.Flags().IntVarP(&valSz, "val-size", "v", 128, "Size of value")
	writeBenchCmd.Flags().Float64VarP(&numKeys, "keys-mil", "m", 10.0,
		"Number of keys to add in millions")
	writeBenchCmd.Flags().BoolVarP(&force, "force-compact", "f", true,
		"Force compact level 0 on close.")
	writeBenchCmd.Flags().BoolVarP(&sorted, "sorted", "s", false, "Write keys in sorted order.")
	writeBenchCmd.Flags().BoolVarP(&showLogs, "logs", "l", false, "Show Badger logs.")
	writeBenchCmd.Flags().IntVarP(&valueThreshold, "value-th", "t", 1<<10, "Value threshold")
	writeBenchCmd.Flags().IntVarP(&numVersions, "num-version", "n", 1, "Number of versions to keep")
	writeBenchCmd.Flags().Int64VarP(&maxCacheSize, "max-cache", "C", 1<<30, "Max size of cache")
	writeBenchCmd.Flags().BoolVarP(&keepBlockIdxInCache, "keep-bidx", "b", true,
		"Keep block indices in cache")
	writeBenchCmd.Flags().BoolVarP(&keepBlocksInCache, "keep-blocks", "B", true,
		"Keep blocks in cache")
	writeBenchCmd.Flags().Int64VarP(&maxBfCacheSize, "max-bf-cache", "c", 500<<20,
		"Maximum Bloom Filter Cache Size")
	writeBenchCmd.Flags().Uint32Var(&vlogMaxEntries, "vlog-maxe", 10000, "Value log Max Entries")
	writeBenchCmd.Flags().StringVarP(&encryptionKey, "encryption-key", "e", "",
		"If it is true, badger will encrypt all the data stored on the disk.")
	writeBenchCmd.Flags().StringVar(&loadingMode, "loading-mode", "mmap",
		"Mode for accessing SSTables")
	writeBenchCmd.Flags().BoolVar(&loadBloomsOnOpen, "load-blooms", false,
		"Load Bloom filter on DB open.")
	writeBenchCmd.Flags().BoolVar(&detectConflicts, "conficts", false,
		"If true, it badger will detect the conflicts")
	writeBenchCmd.Flags().BoolVar(&compression, "compression", false,
		"If true, badger will use ZSTD mode")
	writeBenchCmd.Flags().Uint32Var(&ttlDuration, "entry-ttl", 0,
		"TTL duration in seconds for the entries, 0 means without TTL")
	writeBenchCmd.Flags().StringVarP(&gcPeriod, "gc-every", "g", "5m", "GC Period.")
	writeBenchCmd.Flags().Float64VarP(&gcDiscardRatio, "gc-ratio", "r", 0.5, "GC discard ratio.")
}

func writeRandom(db *badger.DB, num uint64) error {
	value := make([]byte, valSz)
	y.Check2(rand.Read(value))

	es := uint64(keySz + valSz) // entry size is keySz + valSz
	batch := db.NewWriteBatch()
	for i := uint64(1); i <= num; i++ {
		key := make([]byte, keySz)
		y.Check2(rand.Read(key))

		e := badger.NewEntry(key, value)
		if ttlDuration != 0 {
			e.WithTTL(time.Duration(ttlDuration) * time.Second)
		}
		if err := batch.SetEntry(e); err != nil {
			return err
		}

		atomic.AddUint64(&entriesWritten, 1)
		atomic.AddUint64(&sizeWritten, es)
	}
	return batch.Flush()
}

func writeSorted(db *badger.DB, num uint64) error {
	value := make([]byte, valSz)
	y.Check2(rand.Read(value))
	es := 8 + valSz // key size is 8 bytes and value size is valSz

	writer := db.NewStreamWriter()
	if err := writer.Prepare(); err != nil {
		return err
	}

	wg := &sync.WaitGroup{}
	writeCh := make(chan *pb.KVList, 3)
	writeRange := func(start, end uint64, streamId uint32) {
		// end is not included.
		defer wg.Done()
		kvs := &pb.KVList{}
		var sz int
		for i := start; i < end; i++ {
			key := make([]byte, 8)
			binary.BigEndian.PutUint64(key, i)
			kvs.Kv = append(kvs.Kv, &pb.KV{
				Key:      key,
				Value:    value,
				Version:  1,
				StreamId: streamId,
			})

			sz += es
			atomic.AddUint64(&entriesWritten, 1)
			atomic.AddUint64(&sizeWritten, uint64(es))

			if sz >= 4<<20 { // 4 MB
				writeCh <- kvs
				kvs = &pb.KVList{}
				sz = 0
			}
		}
		writeCh <- kvs
	}

	// Let's create some streams.
	width := num / 16
	streamID := uint32(0)
	for start := uint64(0); start < num; start += width {
		end := start + width
		if end > num {
			end = num
		}
		streamID++
		wg.Add(1)
		go writeRange(start, end, streamID)
	}
	go func() {
		wg.Wait()
		close(writeCh)
	}()
	log.Printf("Max StreamId used: %d. Width: %d\n", streamID, width)
	for kvs := range writeCh {
		if err := writer.Write(kvs); err != nil {
			panic(err)
		}
	}
	log.Println("DONE streaming. Flushing...")
	return writer.Flush()
}

func writeBench(cmd *cobra.Command, args []string) error {
	var cmode options.CompressionType
	if compression {
		cmode = options.ZSTD
	} else {
		cmode = options.None
	}
	mode := getLoadingMode(loadingMode)
	opt := badger.DefaultOptions(sstDir).
		WithValueDir(vlogDir).
		WithTruncate(truncate).
		WithSyncWrites(false).
		WithCompactL0OnClose(force).
		WithValueThreshold(valueThreshold).
		WithNumVersionsToKeep(numVersions).
		WithMaxCacheSize(maxCacheSize).
		WithKeepBlockIndicesInCache(keepBlockIdxInCache).
		WithKeepBlocksInCache(keepBlocksInCache).
		WithMaxBfCacheSize(maxBfCacheSize).
		WithValueLogMaxEntries(vlogMaxEntries).
		WithTableLoadingMode(mode).
		WithEncryptionKey([]byte(encryptionKey)).
		WithLoadBloomsOnOpen(loadBloomsOnOpen).
		WithDetectConflicts(detectConflicts).
		WithCompression(cmode)

	if !showLogs {
		opt = opt.WithLogger(nil)
	}

	db, err := badger.Open(opt)
	if err != nil {
		return err
	}
	defer func() {
		start := time.Now()
		err := db.Close()
		log.Printf("DB.Close. Error: %v. Time taken to close: %s", err, time.Since(start))
	}()

	fmt.Println("*********************************************************")
	fmt.Println("Starting to benchmark Writes")
	fmt.Println("*********************************************************")

	startTime = time.Now()
	num := uint64(numKeys * mil)
	c := y.NewCloser(2)
	go reportStats(c)
	go runGC(c, db)

	if sorted {
		err = writeSorted(db, num)
	} else {
		err = writeRandom(db, num)
	}

	c.SignalAndWait()
	return err
}

func reportStats(c *y.Closer) {
	defer c.Done()

	t := time.NewTicker(time.Second)
	defer t.Stop()
	for {
		select {
		case <-c.HasBeenClosed():
			return
		case <-t.C:
			dur := time.Since(startTime)
			sz := atomic.LoadUint64(&sizeWritten)
			entries := atomic.LoadUint64(&entriesWritten)
			bytesRate := sz / uint64(dur.Seconds())
			entriesRate := entries / uint64(dur.Seconds())
			fmt.Printf("Time elapsed: %s, bytes written: %s, speed: %s/sec, "+
				"entries written: %d, speed: %d/sec, gcSuccess: %d\n", y.FixedDuration(time.Since(startTime)),
				humanize.Bytes(sz), humanize.Bytes(bytesRate), entries, entriesRate, gcSuccess)
		}
	}
}

func runGC(c *y.Closer, db *badger.DB) {
	defer c.Done()
	period, err := time.ParseDuration(gcPeriod)
	y.Check(err)
	t := time.NewTicker(period)
	defer t.Stop()
	for {
		select {
		case <-c.HasBeenClosed():
			return
		case <-t.C:
			if err := db.RunValueLogGC(gcDiscardRatio); err == nil {
				atomic.AddUint64(&gcSuccess, 1)
			} else {
				log.Printf("[GC] Failed due to following err %v", err)
			}
		}
	}
}
<commit_msg>add dropall periodically option to write benchmark (#1433)<commit_after>/*
 * Copyright 2019 Dgraph Labs, Inc. and 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 cmd

import (
	"encoding/binary"
	"fmt"
	"log"
	"math/rand"
	"sync"
	"sync/atomic"
	"time"

	humanize "github.com/dustin/go-humanize"
	"github.com/spf13/cobra"

	"github.com/dgraph-io/badger/v2"
	"github.com/dgraph-io/badger/v2/options"
	"github.com/dgraph-io/badger/v2/pb"
	"github.com/dgraph-io/badger/v2/y"
)

var writeBenchCmd = &cobra.Command{
	Use:   "write",
	Short: "Writes random data to Badger to benchmark write speed.",
	Long: `
  This command writes random data to Badger to benchmark write speed. Useful for testing and
  performance analysis.
  `,
	RunE: writeBench,
}

var (
	keySz    int
	valSz    int
	numKeys  float64
	force    bool
	sorted   bool
	showLogs bool

	sizeWritten    uint64
	entriesWritten uint64

	valueThreshold      int
	numVersions         int
	maxCacheSize        int64
	keepBlockIdxInCache bool
	keepBlocksInCache   bool
	maxBfCacheSize      int64
	vlogMaxEntries      uint32
	loadBloomsOnOpen    bool
	detectConflicts     bool
	compression         bool
	dropAllPeriod       string
	gcPeriod            string
	gcDiscardRatio      float64
	ttlDuration         uint32

	gcSuccess uint64
)

const (
	mil float64 = 1e6
)

func init() {
	benchCmd.AddCommand(writeBenchCmd)
	writeBenchCmd.Flags().IntVarP(&keySz, "key-size", "k", 32, "Size of key")
	writeBenchCmd.Flags().IntVarP(&valSz, "val-size", "v", 128, "Size of value")
	writeBenchCmd.Flags().Float64VarP(&numKeys, "keys-mil", "m", 10.0,
		"Number of keys to add in millions")
	writeBenchCmd.Flags().BoolVarP(&force, "force-compact", "f", true,
		"Force compact level 0 on close.")
	writeBenchCmd.Flags().BoolVarP(&sorted, "sorted", "s", false, "Write keys in sorted order.")
	writeBenchCmd.Flags().BoolVarP(&showLogs, "logs", "l", false, "Show Badger logs.")
	writeBenchCmd.Flags().IntVarP(&valueThreshold, "value-th", "t", 1<<10, "Value threshold")
	writeBenchCmd.Flags().IntVarP(&numVersions, "num-version", "n", 1, "Number of versions to keep")
	writeBenchCmd.Flags().Int64VarP(&maxCacheSize, "max-cache", "C", 1<<30, "Max size of cache")
	writeBenchCmd.Flags().BoolVarP(&keepBlockIdxInCache, "keep-bidx", "b", true,
		"Keep block indices in cache")
	writeBenchCmd.Flags().BoolVarP(&keepBlocksInCache, "keep-blocks", "B", true,
		"Keep blocks in cache")
	writeBenchCmd.Flags().Int64VarP(&maxBfCacheSize, "max-bf-cache", "c", 500<<20,
		"Maximum Bloom Filter Cache Size")
	writeBenchCmd.Flags().Uint32Var(&vlogMaxEntries, "vlog-maxe", 10000, "Value log Max Entries")
	writeBenchCmd.Flags().StringVarP(&encryptionKey, "encryption-key", "e", "",
		"If it is true, badger will encrypt all the data stored on the disk.")
	writeBenchCmd.Flags().StringVar(&loadingMode, "loading-mode", "mmap",
		"Mode for accessing SSTables")
	writeBenchCmd.Flags().BoolVar(&loadBloomsOnOpen, "load-blooms", false,
		"Load Bloom filter on DB open.")
	writeBenchCmd.Flags().BoolVar(&detectConflicts, "conficts", false,
		"If true, it badger will detect the conflicts")
	writeBenchCmd.Flags().BoolVar(&compression, "compression", false,
		"If true, badger will use ZSTD mode")
	writeBenchCmd.Flags().StringVar(&dropAllPeriod, "dropall", "0s",
		"Period of dropping all. If 0, doesn't drops all.")
	writeBenchCmd.Flags().Uint32Var(&ttlDuration, "entry-ttl", 0,
		"TTL duration in seconds for the entries, 0 means without TTL")
	writeBenchCmd.Flags().StringVarP(&gcPeriod, "gc-every", "g", "5m", "GC Period.")
	writeBenchCmd.Flags().Float64VarP(&gcDiscardRatio, "gc-ratio", "r", 0.5, "GC discard ratio.")
}

func writeRandom(db *badger.DB, num uint64) error {
	value := make([]byte, valSz)
	y.Check2(rand.Read(value))

	es := uint64(keySz + valSz) // entry size is keySz + valSz
	batch := db.NewWriteBatch()

	for i := uint64(1); i <= num; i++ {
		key := make([]byte, keySz)
		y.Check2(rand.Read(key))
		e := badger.NewEntry(key, value)
		if ttlDuration != 0 {
			e.WithTTL(time.Duration(ttlDuration) * time.Second)
		}
		err := batch.SetEntry(e)
		for err == badger.ErrBlockedWrites {
			time.Sleep(time.Second)
			batch = db.NewWriteBatch()
			err = batch.SetEntry(e)
		}
		if err != nil {
			panic(err)
		}

		atomic.AddUint64(&entriesWritten, 1)
		atomic.AddUint64(&sizeWritten, es)
	}
	return batch.Flush()
}

func writeSorted(db *badger.DB, num uint64) error {
	value := make([]byte, valSz)
	y.Check2(rand.Read(value))
	es := 8 + valSz // key size is 8 bytes and value size is valSz

	writer := db.NewStreamWriter()
	if err := writer.Prepare(); err != nil {
		return err
	}

	wg := &sync.WaitGroup{}
	writeCh := make(chan *pb.KVList, 3)
	writeRange := func(start, end uint64, streamId uint32) {
		// end is not included.
		defer wg.Done()
		kvs := &pb.KVList{}
		var sz int
		for i := start; i < end; i++ {
			key := make([]byte, 8)
			binary.BigEndian.PutUint64(key, i)
			kvs.Kv = append(kvs.Kv, &pb.KV{
				Key:      key,
				Value:    value,
				Version:  1,
				StreamId: streamId,
			})

			sz += es
			atomic.AddUint64(&entriesWritten, 1)
			atomic.AddUint64(&sizeWritten, uint64(es))

			if sz >= 4<<20 { // 4 MB
				writeCh <- kvs
				kvs = &pb.KVList{}
				sz = 0
			}
		}
		writeCh <- kvs
	}

	// Let's create some streams.
	width := num / 16
	streamID := uint32(0)
	for start := uint64(0); start < num; start += width {
		end := start + width
		if end > num {
			end = num
		}
		streamID++
		wg.Add(1)
		go writeRange(start, end, streamID)
	}
	go func() {
		wg.Wait()
		close(writeCh)
	}()
	log.Printf("Max StreamId used: %d. Width: %d\n", streamID, width)
	for kvs := range writeCh {
		if err := writer.Write(kvs); err != nil {
			panic(err)
		}
	}
	log.Println("DONE streaming. Flushing...")
	return writer.Flush()
}

func writeBench(cmd *cobra.Command, args []string) error {
	var cmode options.CompressionType
	if compression {
		cmode = options.ZSTD
	} else {
		cmode = options.None
	}
	mode := getLoadingMode(loadingMode)
	opt := badger.DefaultOptions(sstDir).
		WithValueDir(vlogDir).
		WithTruncate(truncate).
		WithSyncWrites(false).
		WithCompactL0OnClose(force).
		WithValueThreshold(valueThreshold).
		WithNumVersionsToKeep(numVersions).
		WithMaxCacheSize(maxCacheSize).
		WithKeepBlockIndicesInCache(keepBlockIdxInCache).
		WithKeepBlocksInCache(keepBlocksInCache).
		WithMaxBfCacheSize(maxBfCacheSize).
		WithValueLogMaxEntries(vlogMaxEntries).
		WithTableLoadingMode(mode).
		WithEncryptionKey([]byte(encryptionKey)).
		WithLoadBloomsOnOpen(loadBloomsOnOpen).
		WithDetectConflicts(detectConflicts).
		WithCompression(cmode)

	if !showLogs {
		opt = opt.WithLogger(nil)
	}

	db, err := badger.Open(opt)
	if err != nil {
		return err
	}
	defer func() {
		start := time.Now()
		err := db.Close()
		log.Printf("DB.Close. Error: %v. Time taken to close: %s", err, time.Since(start))
	}()

	fmt.Println("*********************************************************")
	fmt.Println("Starting to benchmark Writes")
	fmt.Println("*********************************************************")

	startTime = time.Now()
	num := uint64(numKeys * mil)
	c := y.NewCloser(3)
	go reportStats(c)
	go dropAll(c, db)
	go runGC(c, db)

	if sorted {
		err = writeSorted(db, num)
	} else {
		err = writeRandom(db, num)
	}

	c.SignalAndWait()
	return err
}

func reportStats(c *y.Closer) {
	defer c.Done()

	t := time.NewTicker(time.Second)
	defer t.Stop()
	for {
		select {
		case <-c.HasBeenClosed():
			return
		case <-t.C:
			dur := time.Since(startTime)
			sz := atomic.LoadUint64(&sizeWritten)
			entries := atomic.LoadUint64(&entriesWritten)
			bytesRate := sz / uint64(dur.Seconds())
			entriesRate := entries / uint64(dur.Seconds())
			fmt.Printf("Time elapsed: %s, bytes written: %s, speed: %s/sec, "+
				"entries written: %d, speed: %d/sec, gcSuccess: %d\n", y.FixedDuration(time.Since(startTime)),
				humanize.Bytes(sz), humanize.Bytes(bytesRate), entries, entriesRate, gcSuccess)
		}
	}
}

func runGC(c *y.Closer, db *badger.DB) {
	defer c.Done()
	period, err := time.ParseDuration(gcPeriod)
	y.Check(err)
	t := time.NewTicker(period)
	defer t.Stop()
	for {
		select {
		case <-c.HasBeenClosed():
			return
		case <-t.C:
			if err := db.RunValueLogGC(gcDiscardRatio); err == nil {
				atomic.AddUint64(&gcSuccess, 1)
			} else {
				log.Printf("[GC] Failed due to following err %v", err)
			}
		}
	}
}

func dropAll(c *y.Closer, db *badger.DB) {
	defer c.Done()

	dropPeriod, err := time.ParseDuration(dropAllPeriod)
	y.Check(err)
	if dropPeriod == 0 {
		return
	}

	t := time.NewTicker(dropPeriod)
	defer t.Stop()
	for {
		select {
		case <-c.HasBeenClosed():
			return
		case <-t.C:
			fmt.Println("[DropAll] Started")
			err := db.DropAll()
			for err == badger.ErrBlockedWrites {
				err = db.DropAll()
				time.Sleep(time.Millisecond * 300)
			}

			if err != nil {
				fmt.Println("[DropAll] Failed")
			} else {
				fmt.Println("[DropAll] Successful")
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/lzcqd/sedgewick/chap2_sorting/insertionsort"
	"github.com/lzcqd/sedgewick/chap2_sorting/selectionsort"
	"github.com/lzcqd/sedgewick/chap2_sorting/shellsort"
	"github.com/lzcqd/sedgewick/chap2_sorting/sortable"
	"math/rand"
	"reflect"
	"runtime"
	"sync"
	"time"
)

type floatslice []float64

func (a floatslice) Len() int           { return len(a) }
func (a floatslice) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a floatslice) Less(i, j int) bool { return a[i] < a[j] }

func timesort(sort func(sortable.Interface), to_sort []floatslice, out chan string) {
	defer close(out)

	start := time.Now()
	for _, s := range to_sort {
		sort(s)
	}

	duration := time.Since(start)
	out <- fmt.Sprintf("%s completed in %v milliseconds", runtime.FuncForPC(reflect.ValueOf(sort).Pointer()).Name(), duration/time.Millisecond)
}

func merge(cs ...chan string) <-chan string {
	var wg sync.WaitGroup
	out := make(chan string)

	output := func(c <-chan string) {
		for n := range c {
			out <- n
		}
		wg.Done()
	}

	wg.Add(len(cs))
	for _, c := range cs {
		go output(c)
	}

	go func() {
		wg.Wait()
		close(out)
	}()

	return out
}

func startsorts(sorts []func(sortable.Interface), to_sort []floatslice, timeout int) {
	outs := make([]chan string, len(sorts))
	for i := range outs {
		outs[i] = make(chan string)
	}

	out := merge(outs...)
	done := make(chan bool)
	go manageOutput(out, timeout, done)

	for i, s := range sorts {
		go timesort(s, to_sort, outs[i])
	}

	<-done
}

func manageOutput(out <-chan string, timeout int, done chan bool) {
	defer func() {
		done <- true
	}()

	quit := time.Tick(time.Duration(timeout) * time.Second)

	for {
		select {
		case s, ok := <-out:
			fmt.Println(s)
			if !ok {
				break
			}
		case <-quit:
			fmt.Println("Time out.")
			break
		}
	}
}

func getSortFunc(in string) func(sortable.Interface) {
	switch in {
	case "selectionsort":
		return selectionsort.Sort
	case "insertionsort":
		return insertionsort.Sort
	case "shellsort":
		return shellsort.Sort
	default:
		return nil
	}
}

func generateSortArray(arrayCount, elementCount int) []floatslice {
	ret := make([]floatslice, arrayCount)
	for i := range ret {
		array := make([]float64, elementCount)
		rand.Seed(int64(time.Now().Unix()))
		for j := range array {
			array[j] = rand.Float64()
		}
		ret[i] = array
	}
	return ret
}
<commit_msg>Sort compare: implement sort compare component, with concurrent sorting and timeout capability<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"github.com/lzcqd/sedgewick/chap2_sorting/insertionsort"
	"github.com/lzcqd/sedgewick/chap2_sorting/selectionsort"
	"github.com/lzcqd/sedgewick/chap2_sorting/shellsort"
	"github.com/lzcqd/sedgewick/chap2_sorting/sortable"
	"math/rand"
	"reflect"
	"runtime"
	"strings"
	"sync"
	"time"
)

type floatslice []float64

func (a floatslice) Len() int           { return len(a) }
func (a floatslice) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a floatslice) Less(i, j int) bool { return a[i] < a[j] }

func timesort(sort func(sortable.Interface), to_sort []floatslice, out chan string) {
	defer close(out)

	start := time.Now()
	for _, s := range to_sort {
		sort(s)
	}

	duration := time.Since(start)
	out <- fmt.Sprintf("%s completed in %v", runtime.FuncForPC(reflect.ValueOf(sort).Pointer()).Name(), duration)
}

func merge(cs ...chan string) <-chan string {
	var wg sync.WaitGroup
	out := make(chan string)

	output := func(c <-chan string) {
		for n := range c {
			out <- n
		}
		wg.Done()
	}

	wg.Add(len(cs))
	for _, c := range cs {
		go output(c)
	}

	go func() {
		wg.Wait()
		close(out)
	}()

	return out
}

func startsorts(sorts []func(sortable.Interface), to_sort []floatslice, timeout int) {
	outs := make([]chan string, len(sorts))
	for i := range outs {
		outs[i] = make(chan string)
	}

	out := merge(outs...)
	done := make(chan bool)
	go manageOutput(out, timeout, done)

	for i, s := range sorts {
		go timesort(s, to_sort, outs[i])
	}
	fmt.Println("sorting...")
	<-done
}

func manageOutput(out <-chan string, timeout int, done chan bool) {
	defer func() {
		done <- true
	}()

	quit := time.Tick(time.Duration(timeout) * time.Second)

	for {
		select {
		case s, ok := <-out:
			fmt.Println(s)
			if !ok {
				return
			}
		case <-quit:
			fmt.Println("Time out.")
			return
		}
	}
}

func getSortFunc(in string) (func(sortable.Interface), error) {
	switch in {
	case "selectionsort":
		return selectionsort.Sort, nil
	case "insertionsort":
		return insertionsort.Sort, nil
	case "shellsort":
		return shellsort.Sort, nil
	default:
		return nil, errors.New("fail to parse sort function")
	}
}

func generateSortArray(arrayCount, elementCount int) []floatslice {
	ret := make([]floatslice, arrayCount)
	for i := range ret {
		array := make([]float64, elementCount)
		rand.Seed(int64(time.Now().Unix()))
		for j := range array {
			array[j] = rand.Float64()
		}
		ret[i] = array
	}
	return ret
}

func main() {
	sortFuncs := flag.String("sorts", "", "Sorting functions to compare, comma separated")
	arrayCount := flag.Int("array", 1, "Number of arrays to sort, default 1")
	elementCount := flag.Int("element", 1000, "Number of random entries for each array, default 1000")
	timeout := flag.Int("timeout", 60, "Maximum time to run in seconds, default 60s")
	flag.Parse()

	funcs := strings.Split(*sortFuncs, ",")
	for i, f := range funcs {
		funcs[i] = strings.TrimSpace(f)
	}

	var sorts []func(sortable.Interface)

	for _, f := range funcs {
		s, err := getSortFunc(f)
		if err != nil {
			panic(fmt.Sprintf("Not recognised sort function: %s\n", s))
		}
		sorts = append(sorts, s)
	}

	sortArray := generateSortArray(*arrayCount, *elementCount)
	startsorts(sorts, sortArray, *timeout)
}
<|endoftext|>


<commit_before>package bradescoNetEmpresa

import (
	"errors"
	"fmt"
	"html"
	"time"

	"github.com/mundipagg/boleto-api/tmpl"
	"github.com/mundipagg/boleto-api/util"

	"github.com/PMoneda/flow"
	"github.com/mundipagg/boleto-api/config"
	"github.com/mundipagg/boleto-api/log"
	"github.com/mundipagg/boleto-api/metrics"
	"github.com/mundipagg/boleto-api/models"
	"github.com/mundipagg/boleto-api/validations"
)

type bankBradescoNetEmpresa struct {
	validate *models.Validator
	log      *log.Log
}

type barcode struct {
	bankCode      string
	currencyCode  string
	dateDueFactor string
	value         string
	agency        string
	wallet        string
	ourNumber     string
	account       string
	zero          string
}

func New() bankBradescoNetEmpresa {
	b := bankBradescoNetEmpresa{
		validate: models.NewValidator(),
		log:      log.CreateLog(),
	}
	b.validate.Push(validations.ValidateAmount)
	b.validate.Push(validations.ValidateExpireDate)
	b.validate.Push(validations.ValidateBuyerDocumentNumber)
	b.validate.Push(validations.ValidateRecipientDocumentNumber)
	b.validate.Push(validations.ValidateBuyerDocumentNumber)
	b.validate.Push(validations.ValidateRecipientDocumentNumber)

	b.validate.Push(bradescoNetEmpresaValidateAgency)
	b.validate.Push(bradescoNetEmpresaValidateAccount)
	b.validate.Push(bradescoNetEmpresaValidateWallet)
	b.validate.Push(bradescoNetEmpresaValidateAgreement)
	return b
}

func (b bankBradescoNetEmpresa) Log() *log.Log {
	return b.log
}

func (b bankBradescoNetEmpresa) RegisterBoleto(boleto *models.BoletoRequest) (models.BoletoResponse, error) {
	timing := metrics.GetTimingMetrics()
	r := flow.NewFlow()
	serviceURL := config.Get().URLBradescoNetEmpresa
	xmlResponse := getResponseBradescoNetEmpresaXml()
	jsonReponse := getResponseBradescoNetEmpresaJson()
	from := getResponseBradescoNetEmpresa()
	to := getAPIResponseBradescoNetEmpresa()

	bod := r.From("message://?source=inline", boleto, getRequestBradescoNetEmpresa(), tmpl.GetFuncMaps())
	bod.To("logseq://?type=request&url="+serviceURL, b.log)

	err := signRequest(bod)
	if err != nil {
		return models.BoletoResponse{}, err
	}

	duration := util.Duration(func() {
		bod.To(serviceURL, map[string]string{"method": "POST", "insecureSkipVerify": "true"})
	})

	timing.Push("bradesco-netempresa-register-boleto-online", duration.Seconds())

	bod.To("logseq://?type=response&url="+serviceURL, b.log)
	bod.To("transform://?format=xml", xmlResponse, jsonReponse)
	bodyTransform := fmt.Sprintf("%v", bod.GetBody())
	bodyJson := html.UnescapeString(bodyTransform)
	bod.To("set://?prop=body", bodyJson)

	ch := bod.Choice()
	ch.When(flow.Header("status").IsEqualTo("200"))
	ch.To("transform://?format=json", from, to, tmpl.GetFuncMaps())
	ch.To("unmarshall://?format=json", new(models.BoletoResponse))
	ch.Otherwise()
	ch.To("logseq://?type=response&url="+serviceURL, b.log).To("apierro://")

	switch t := bod.GetBody().(type) {
	case *models.BoletoResponse:
		if !t.HasErrors() {
			t.BarCodeNumber = getBarcode(*boleto).toString()
		}
		return *t, nil
	case error:
		return models.BoletoResponse{}, t
	}
	return models.BoletoResponse{}, models.NewInternalServerError("MP500", "Erro interno")
}

func signRequest(bod *flow.Flow) error {

	if !config.Get().MockMode {
		bodyToSign := fmt.Sprintf("%v", bod.GetBody())
		signedRequest, err := util.SignRequest(bodyToSign)
		if err != nil {
			return err
		}
		bod.To("set://?prop=body", signedRequest)
	}

	return nil
}

func (b bankBradescoNetEmpresa) ProcessBoleto(boleto *models.BoletoRequest) (models.BoletoResponse, error) {
	errs := b.ValidateBoleto(boleto)
	if len(errs) > 0 {
		return models.BoletoResponse{Errors: errs}, nil
	}
	return b.RegisterBoleto(boleto)
}

func (b bankBradescoNetEmpresa) ValidateBoleto(boleto *models.BoletoRequest) models.Errors {
	return models.Errors(b.validate.Assert(boleto))
}

func (b bankBradescoNetEmpresa) GetBankNumber() models.BankNumber {
	return models.Bradesco
}

func (b bankBradescoNetEmpresa) GetBankNameIntegration() string {
	return "BradescoNetEmpresa"
}

func getBarcode(boleto models.BoletoRequest) (bc barcode) {
	bc.bankCode = fmt.Sprintf("%d", models.BradescoShopFacil)
	bc.currencyCode = fmt.Sprintf("%d", models.Real)
	bc.account = fmt.Sprintf("%07s", boleto.Agreement.Account)
	bc.agency = fmt.Sprintf("%04s", boleto.Agreement.Agency)
	bc.dateDueFactor, _ = dateDueFactor(boleto.Title.ExpireDateTime)
	bc.ourNumber = fmt.Sprintf("%011d", boleto.Title.OurNumber)
	bc.value = fmt.Sprintf("%010d", boleto.Title.AmountInCents)
	bc.wallet = fmt.Sprintf("%02d", boleto.Agreement.Wallet)
	bc.zero = "0"
	return
}

func (bc barcode) toString() string {
	return fmt.Sprintf("%s%s%s%s%s%s%s%s%s%s", bc.bankCode, bc.currencyCode, bc.calcCheckDigit(), bc.dateDueFactor, bc.value, bc.agency, bc.wallet, bc.ourNumber, bc.account, bc.zero)
}

func (bc barcode) calcCheckDigit() string {
	prevCode := fmt.Sprintf("%s%s%s%s%s%s%s%s%s", bc.bankCode, bc.currencyCode, bc.dateDueFactor, bc.value, bc.agency, bc.wallet, bc.ourNumber, bc.account, bc.zero)
	return util.BarcodeDv(prevCode)
}

func dateDueFactor(dateDue time.Time) (string, error) {
	var dateDueFixed = time.Date(1997, 10, 7, 0, 0, 0, 0, time.UTC)
	dif := dateDue.Sub(dateDueFixed)
	factor := int(dif.Hours() / 24)
	if factor <= 0 {
		return "", errors.New("DateDue must be in the future")
	}
	return fmt.Sprintf("%04d", factor), nil
}
<commit_msg>:bug: Fix models to genarated barcode<commit_after>package bradescoNetEmpresa

import (
	"errors"
	"fmt"
	"html"
	"time"

	"github.com/mundipagg/boleto-api/tmpl"
	"github.com/mundipagg/boleto-api/util"

	"github.com/PMoneda/flow"
	"github.com/mundipagg/boleto-api/config"
	"github.com/mundipagg/boleto-api/log"
	"github.com/mundipagg/boleto-api/metrics"
	"github.com/mundipagg/boleto-api/models"
	"github.com/mundipagg/boleto-api/validations"
)

type bankBradescoNetEmpresa struct {
	validate *models.Validator
	log      *log.Log
}

type barcode struct {
	bankCode      string
	currencyCode  string
	dateDueFactor string
	value         string
	agency        string
	wallet        string
	ourNumber     string
	account       string
	zero          string
}

func New() bankBradescoNetEmpresa {
	b := bankBradescoNetEmpresa{
		validate: models.NewValidator(),
		log:      log.CreateLog(),
	}
	b.validate.Push(validations.ValidateAmount)
	b.validate.Push(validations.ValidateExpireDate)
	b.validate.Push(validations.ValidateBuyerDocumentNumber)
	b.validate.Push(validations.ValidateRecipientDocumentNumber)
	b.validate.Push(validations.ValidateBuyerDocumentNumber)
	b.validate.Push(validations.ValidateRecipientDocumentNumber)

	b.validate.Push(bradescoNetEmpresaValidateAgency)
	b.validate.Push(bradescoNetEmpresaValidateAccount)
	b.validate.Push(bradescoNetEmpresaValidateWallet)
	b.validate.Push(bradescoNetEmpresaValidateAgreement)
	return b
}

func (b bankBradescoNetEmpresa) Log() *log.Log {
	return b.log
}

func (b bankBradescoNetEmpresa) RegisterBoleto(boleto *models.BoletoRequest) (models.BoletoResponse, error) {
	timing := metrics.GetTimingMetrics()
	r := flow.NewFlow()
	serviceURL := config.Get().URLBradescoNetEmpresa
	xmlResponse := getResponseBradescoNetEmpresaXml()
	jsonReponse := getResponseBradescoNetEmpresaJson()
	from := getResponseBradescoNetEmpresa()
	to := getAPIResponseBradescoNetEmpresa()

	bod := r.From("message://?source=inline", boleto, getRequestBradescoNetEmpresa(), tmpl.GetFuncMaps())
	bod.To("logseq://?type=request&url="+serviceURL, b.log)

	err := signRequest(bod)
	if err != nil {
		return models.BoletoResponse{}, err
	}

	duration := util.Duration(func() {
		bod.To(serviceURL, map[string]string{"method": "POST", "insecureSkipVerify": "true"})
	})

	timing.Push("bradesco-netempresa-register-boleto-online", duration.Seconds())

	bod.To("logseq://?type=response&url="+serviceURL, b.log)
	bod.To("transform://?format=xml", xmlResponse, jsonReponse)
	bodyTransform := fmt.Sprintf("%v", bod.GetBody())
	bodyJson := html.UnescapeString(bodyTransform)
	bod.To("set://?prop=body", bodyJson)

	ch := bod.Choice()
	ch.When(flow.Header("status").IsEqualTo("200"))
	ch.To("transform://?format=json", from, to, tmpl.GetFuncMaps())
	ch.To("unmarshall://?format=json", new(models.BoletoResponse))
	ch.Otherwise()
	ch.To("logseq://?type=response&url="+serviceURL, b.log).To("apierro://")

	switch t := bod.GetBody().(type) {
	case *models.BoletoResponse:
		if !t.HasErrors() {
			t.BarCodeNumber = getBarcode(*boleto).toString()
		}
		return *t, nil
	case error:
		return models.BoletoResponse{}, t
	}
	return models.BoletoResponse{}, models.NewInternalServerError("MP500", "Erro interno")
}

func signRequest(bod *flow.Flow) error {

	if !config.Get().MockMode {
		bodyToSign := fmt.Sprintf("%v", bod.GetBody())
		signedRequest, err := util.SignRequest(bodyToSign)
		if err != nil {
			return err
		}
		bod.To("set://?prop=body", signedRequest)
	}

	return nil
}

func (b bankBradescoNetEmpresa) ProcessBoleto(boleto *models.BoletoRequest) (models.BoletoResponse, error) {
	errs := b.ValidateBoleto(boleto)
	if len(errs) > 0 {
		return models.BoletoResponse{Errors: errs}, nil
	}
	return b.RegisterBoleto(boleto)
}

func (b bankBradescoNetEmpresa) ValidateBoleto(boleto *models.BoletoRequest) models.Errors {
	return models.Errors(b.validate.Assert(boleto))
}

func (b bankBradescoNetEmpresa) GetBankNumber() models.BankNumber {
	return models.Bradesco
}

func (b bankBradescoNetEmpresa) GetBankNameIntegration() string {
	return "BradescoNetEmpresa"
}

func getBarcode(boleto models.BoletoRequest) (bc barcode) {
	bc.bankCode = fmt.Sprintf("%d", models.Bradesco)
	bc.currencyCode = fmt.Sprintf("%d", models.Real)
	bc.account = fmt.Sprintf("%07s", boleto.Agreement.Account)
	bc.agency = fmt.Sprintf("%04s", boleto.Agreement.Agency)
	bc.dateDueFactor, _ = dateDueFactor(boleto.Title.ExpireDateTime)
	bc.ourNumber = fmt.Sprintf("%011d", boleto.Title.OurNumber)
	bc.value = fmt.Sprintf("%010d", boleto.Title.AmountInCents)
	bc.wallet = fmt.Sprintf("%02d", boleto.Agreement.Wallet)
	bc.zero = "0"
	return
}

func (bc barcode) toString() string {
	return fmt.Sprintf("%s%s%s%s%s%s%s%s%s%s", bc.bankCode, bc.currencyCode, bc.calcCheckDigit(), bc.dateDueFactor, bc.value, bc.agency, bc.wallet, bc.ourNumber, bc.account, bc.zero)
}

func (bc barcode) calcCheckDigit() string {
	prevCode := fmt.Sprintf("%s%s%s%s%s%s%s%s%s", bc.bankCode, bc.currencyCode, bc.dateDueFactor, bc.value, bc.agency, bc.wallet, bc.ourNumber, bc.account, bc.zero)
	return util.BarcodeDv(prevCode)
}

func dateDueFactor(dateDue time.Time) (string, error) {
	var dateDueFixed = time.Date(1997, 10, 7, 0, 0, 0, 0, time.UTC)
	dif := dateDue.Sub(dateDueFixed)
	factor := int(dif.Hours() / 24)
	if factor <= 0 {
		return "", errors.New("DateDue must be in the future")
	}
	return fmt.Sprintf("%04d", factor), nil
}
<|endoftext|>