<commit_before>package main

import (
	"container/list"
	"fmt"
	"math"
	"os"
)

const (
	NilLinePanicString = "current EditBuffer line is nil"
	CacheMax           = 5
)

// XXX once a better drawing interface is figured out, there should be an lru
// cache of computed lines.  the way things are mapped now (rangeless) doesn't
// really let me cache the way i want to.

// XXX editbuffers are editable text buffers that happen to also be a screen.

// todo: lockable
type EditBuffer struct {
	fi     *os.FileInfo /* EditBuffer info. */
	name   string
	dirty  bool
	rdonly bool
	lines  *list.List    /* List of lines in the file. */
	line   *list.Element /* Current line in the file. */
	edits  *list.List    /* Edit history. */

	anchor         *list.Element // first line to draw
	view           *Screen       // view this buffer draws to
	curs_x, curs_y int           // cursor position
}

func NewEditBuffer(name string, view *Screen) *EditBuffer {

	f := new(EditBuffer)
	f.name = name
	// XXX it would be better to init this to nil and add the line from another
	// place so that the file loader can use this
	f.lines = list.New()
	// b.InsertLine(NewLine([]byte("")))
	f.anchor = f.line
	f.dirty = false

	f.view = view
	return f
}

func (b *EditBuffer) Insert(ch byte) {
	if l, ok := b.line.Value.(*EditLine); ok {
		l.insertCharacter(ch)
	}
	b.dirty = true
	for b.ScreenRange(b.anchor, b.line) > b.view.Rows-1 && b.anchor != b.line {
		b.anchor = b.anchor.Next()
	}
	b.Map()
}

func (b *EditBuffer) BackSpace() {
	if b.line == nil {
		Beep()
		return
	}

	if l, ok := b.line.Value.(*EditLine); ok {
		if (l.cursor == 0 && !l.hasNewLine) || (l.cursor == 1 && l.hasNewLine) {
			if l.DisplayLength() != 0 && b.line.Prev() != nil {
				// combine this line and the previous
			} else {

				if b.line.Prev() != nil {
					b.DeleteCurrLine()
				} else {
					Beep()
				}
			}
		} else {
			l.backspace()
		}
	}
	for b.ScreenRange(b.anchor, b.line) > b.view.Rows-1 && b.anchor != b.line {
		b.anchor = b.anchor.Next()
	}
	b.Map()
}

func (b *EditBuffer) MoveLeft() {
	b.MoveCursor(-1)
}

func (b *EditBuffer) MoveRight() {
	b.MoveCursor(1)
}

func (b *EditBuffer) MoveCursor(d int) {
	if l, ok := b.line.Value.(*EditLine); ok && l.moveCursor(l.cursor+d) < 0 {
		Beep()
	}
}

func (b *EditBuffer) ScreenRange(a, l *list.Element) int {
	// aln, bln := a.Value.(*EditLine), l.Value.(*EditLine)
	cnt := 0
	for c := a; c != nil && c != l.Next(); c = c.Next() {
		// XXX hacks on hacks
		cnt += b.ScreenLines(c.Value.(*EditLine))
	}
	return cnt
}

func (b *EditBuffer) MoveDown() {
	if b.line == nil {
		panic(NilLinePanicString)
	}

	if n := b.line.Next(); n != nil {
		ln := n.Value.(*EditLine)
		if ln.moveCursor(b.line.Value.(*EditLine).cursor) < 0 {
			ln.moveCursor(ln.cursorMax())
		}
		b.line = n
		// We are now at line n.  We need to adjust anchor properly so that we can
		// remap the buffer.
		for b.ScreenRange(b.anchor, b.line) > b.view.Rows-1 && b.anchor != b.line {
			b.anchor = b.anchor.Next()
		}
		curr.Map()
	} else {
		Beep()
	}
}

func (b *EditBuffer) MoveUp() {
	if b.line == nil {
		panic(NilLinePanicString)
	}

	if p := b.line.Prev(); p != nil {
		lp := p.Value.(*EditLine)
		if lp.moveCursor(b.line.Value.(*EditLine).cursor) < 0 {
			lp.moveCursor(lp.cursorMax())
		}
		b.line = p
		// We are now at line p.  We need to adjust the anchor in case we've moved
		// above it.  If we have, there is a need for an entire screen remap :(
		for b.line.Value.(*EditLine).lno < b.anchor.Value.(*EditLine).lno && b.anchor != b.line {
			b.anchor = b.anchor.Prev()
		}
		curr.Map()
	} else {
		Beep()
	}
}

func (b *EditBuffer) DeleteSpan(p, l int) {
	if ln, ok := b.line.Value.(*EditLine); ok {
		ln.delete(p, l)
		b.dirty = true
	}
}

func (b *EditBuffer) FirstLine() {
	b.line = b.lines.Front()
}

// Insert a new line after line
// XXX this doesn't really work as desired (can't insert at 0, for instance)
func (b *EditBuffer) InsertLine(line *EditLine) {
	if b.line == nil {
		b.line = b.lines.PushFront(line)
		l := b.line.Value.(*EditLine)
		l.lno = 1
		b.anchor = b.line
	} else {
		e := b.lines.InsertAfter(line, b.line)
		l := e.Value.(*EditLine)
		l.lno = e.Prev().Value.(*EditLine).lno + 1
		for p := e.Next(); p != nil; p = p.Next() {
			p.Value.(*EditLine).lno++
		}
	}
	b.dirty = true
	b.MoveDown() // does the mapping
}

func (b *EditBuffer) AppendLine() {
	b.InsertLine(NewLine([]byte("")))
}

func (b *EditBuffer) NewLine(dlm byte) {
	if l, ok := b.line.Value.(*EditLine); ok {
		newbuf := l.raw()[l.cursor:]
		l.insertCharacter(dlm)
		l.ClearAfterCursor()
		l.size -= len(newbuf)
		b.InsertLine(NewLine(newbuf))
	}
}

func (b *EditBuffer) DeleteCurrLine() {
	if b.line.Prev() != nil {
		rm := b.line
		b.MoveUp()
		b.lines.Remove(rm)
	} else if b.line.Next() != nil {
		rm := b.line
		b.MoveDown()
		b.lines.Remove(rm)
	} else {
		return
	}
	b.dirty = true
	// moveup and movedown take care of mapping
}

func (b *EditBuffer) LnoOffset() int {
	// if we show line numbers, we reserve at least 3 columns.
	if optLineNo {
		if dg := math.Log10(float64(b.lines.Len())) + 1; dg > 2 {
			return int(dg) + 1
		} else {
			return 3
		}
	}
	return 0
}

func (b *EditBuffer) ScreenLines(ln *EditLine) int {
	offset := b.LnoOffset()
	actual := b.view.Cols - offset
	// XXX this is an example of why displaylength needs to be fixed.
	sz := ln.DisplayLength()
	if sz == 0 && ln.hasNewLine {
		sz = 1
	}

	l := int(math.Ceil(float64(sz) / float64(actual)))
	if l == 0 {
		l = 1
	}
	return l
}

// Maps every visible line to a position on the screen.
func (b *EditBuffer) Map() int {
	offset := b.LnoOffset()
	i := 0
	for l := b.anchor; l != nil && i < b.view.Rows-1; l = l.Next() {
		ln := l.Value.(*EditLine)
		cnt := b.ScreenLines(ln)
		wrap := 0
		raw := ln.raw()
		for lmt := i + cnt; i < lmt; i++ {
			str := make([]byte, b.view.Cols)
			// XXX this is the first part of the line, the optional
			// line number.  if this isn't the first screen line of
			// a line (for a wrapped line), then just draw empty
			// space if line numbers are on
			if wrap == 0 {
				lno := []byte(fmt.Sprintf("%*d ", offset-1, ln.lno))
				copy(str[0:offset], lno)
			} else {
				for j := 0; j < offset; j++ {
					str[j] = ' '
				}
			}

			// XXX the second part of the line, which shows actual
			// text that the user is viewing
			actual := b.view.Cols - offset
			start := actual * wrap
			end := start + actual - 1
			if end >= ln.DisplayLength() {
				end = ln.DisplayLength()
			}
			copy(str[offset:], raw[start:end])

			if b.line == l && (ln.cursor >= start || ln.cursor <= end) {
				b.curs_y = i
				b.curs_x = b.LnoOffset() + (ln.cursor - start)
			}
			b.view.Lines[i] = string(str)
			wrap++
		}
	}
	for ; i < b.view.Rows-1; i++ {
		b.view.Lines[i] = NaL
	}
	return 0
}

func (b *EditBuffer) CursorCoord() (int, int) {
	return b.curs_y, b.curs_x
}

func (b *EditBuffer) Lines() *list.List {
	return b.lines
}
<commit_msg>map on move<commit_after>package main

import (
	"container/list"
	"fmt"
	"math"
	"os"
)

const (
	NilLinePanicString = "current EditBuffer line is nil"
	CacheMax           = 5
)

// XXX once a better drawing interface is figured out, there should be an lru
// cache of computed lines.  the way things are mapped now (rangeless) doesn't
// really let me cache the way i want to.

// XXX editbuffers are editable text buffers that happen to also be a screen.

// todo: lockable
type EditBuffer struct {
	fi     *os.FileInfo /* EditBuffer info. */
	name   string
	dirty  bool
	rdonly bool
	lines  *list.List    /* List of lines in the file. */
	line   *list.Element /* Current line in the file. */
	edits  *list.List    /* Edit history. */

	anchor         *list.Element // first line to draw
	view           *Screen       // view this buffer draws to
	curs_x, curs_y int           // cursor position
}

func NewEditBuffer(name string, view *Screen) *EditBuffer {

	f := new(EditBuffer)
	f.name = name
	// XXX it would be better to init this to nil and add the line from another
	// place so that the file loader can use this
	f.lines = list.New()
	// b.InsertLine(NewLine([]byte("")))
	f.anchor = f.line
	f.dirty = false

	f.view = view
	return f
}

func (b *EditBuffer) Insert(ch byte) {
	if l, ok := b.line.Value.(*EditLine); ok {
		l.insertCharacter(ch)
	}
	b.dirty = true
	for b.ScreenRange(b.anchor, b.line) > b.view.Rows-1 && b.anchor != b.line {
		b.anchor = b.anchor.Next()
	}
	b.Map()
}

func (b *EditBuffer) BackSpace() {
	if b.line == nil {
		Beep()
		return
	}

	if l, ok := b.line.Value.(*EditLine); ok {
		if (l.cursor == 0 && !l.hasNewLine) || (l.cursor == 1 && l.hasNewLine) {
			if l.DisplayLength() != 0 && b.line.Prev() != nil {
				// combine this line and the previous
			} else {

				if b.line.Prev() != nil {
					b.DeleteCurrLine()
				} else {
					Beep()
				}
			}
		} else {
			l.backspace()
		}
	}
	for b.ScreenRange(b.anchor, b.line) > b.view.Rows-1 && b.anchor != b.line {
		b.anchor = b.anchor.Next()
	}
	b.Map()
}

func (b *EditBuffer) MoveLeft() {
	b.MoveCursor(-1)
	b.Map()
}

func (b *EditBuffer) MoveRight() {
	b.MoveCursor(1)
	b.Map()
}

func (b *EditBuffer) MoveCursor(d int) {
	if l, ok := b.line.Value.(*EditLine); ok && l.moveCursor(l.cursor+d) < 0 {
		Beep()
	}
}

func (b *EditBuffer) ScreenRange(a, l *list.Element) int {
	// aln, bln := a.Value.(*EditLine), l.Value.(*EditLine)
	cnt := 0
	for c := a; c != nil && c != l.Next(); c = c.Next() {
		// XXX hacks on hacks
		cnt += b.ScreenLines(c.Value.(*EditLine))
	}
	return cnt
}

func (b *EditBuffer) MoveDown() {
	if b.line == nil {
		panic(NilLinePanicString)
	}

	if n := b.line.Next(); n != nil {
		ln := n.Value.(*EditLine)
		if ln.moveCursor(b.line.Value.(*EditLine).cursor) < 0 {
			ln.moveCursor(ln.cursorMax())
		}
		b.line = n
		// We are now at line n.  We need to adjust anchor properly so that we can
		// remap the buffer.
		for b.ScreenRange(b.anchor, b.line) > b.view.Rows-1 && b.anchor != b.line {
			b.anchor = b.anchor.Next()
		}
		curr.Map()
	} else {
		Beep()
	}
}

func (b *EditBuffer) MoveUp() {
	if b.line == nil {
		panic(NilLinePanicString)
	}

	if p := b.line.Prev(); p != nil {
		lp := p.Value.(*EditLine)
		if lp.moveCursor(b.line.Value.(*EditLine).cursor) < 0 {
			lp.moveCursor(lp.cursorMax())
		}
		b.line = p
		// We are now at line p.  We need to adjust the anchor in case we've moved
		// above it.  If we have, there is a need for an entire screen remap :(
		for b.line.Value.(*EditLine).lno < b.anchor.Value.(*EditLine).lno && b.anchor != b.line {
			b.anchor = b.anchor.Prev()
		}
		curr.Map()
	} else {
		Beep()
	}
}

func (b *EditBuffer) DeleteSpan(p, l int) {
	if ln, ok := b.line.Value.(*EditLine); ok {
		ln.delete(p, l)
		b.dirty = true
	}
}

func (b *EditBuffer) FirstLine() {
	b.line = b.lines.Front()
}

// Insert a new line after line
// XXX this doesn't really work as desired (can't insert at 0, for instance)
func (b *EditBuffer) InsertLine(line *EditLine) {
	if b.line == nil {
		b.line = b.lines.PushFront(line)
		l := b.line.Value.(*EditLine)
		l.lno = 1
		b.anchor = b.line
	} else {
		e := b.lines.InsertAfter(line, b.line)
		l := e.Value.(*EditLine)
		l.lno = e.Prev().Value.(*EditLine).lno + 1
		for p := e.Next(); p != nil; p = p.Next() {
			p.Value.(*EditLine).lno++
		}
	}
	b.dirty = true
	b.MoveDown() // does the mapping
}

func (b *EditBuffer) AppendLine() {
	b.InsertLine(NewLine([]byte("")))
}

func (b *EditBuffer) NewLine(dlm byte) {
	if l, ok := b.line.Value.(*EditLine); ok {
		newbuf := l.raw()[l.cursor:]
		l.insertCharacter(dlm)
		l.ClearAfterCursor()
		l.size -= len(newbuf)
		b.InsertLine(NewLine(newbuf))
	}
}

func (b *EditBuffer) DeleteCurrLine() {
	if b.line.Prev() != nil {
		rm := b.line
		b.MoveUp()
		b.lines.Remove(rm)
	} else if b.line.Next() != nil {
		rm := b.line
		b.MoveDown()
		b.lines.Remove(rm)
	} else {
		return
	}
	b.dirty = true
	// moveup and movedown take care of mapping
}

func (b *EditBuffer) LnoOffset() int {
	// if we show line numbers, we reserve at least 3 columns.
	if optLineNo {
		if dg := math.Log10(float64(b.lines.Len())) + 1; dg > 2 {
			return int(dg) + 1
		} else {
			return 3
		}
	}
	return 0
}

func (b *EditBuffer) ScreenLines(ln *EditLine) int {
	offset := b.LnoOffset()
	actual := b.view.Cols - offset
	// XXX this is an example of why displaylength needs to be fixed.
	sz := ln.DisplayLength()
	if sz == 0 && ln.hasNewLine {
		sz = 1
	}

	l := int(math.Ceil(float64(sz) / float64(actual)))
	if l == 0 {
		l = 1
	}
	return l
}

// Maps every visible line to a position on the screen.
func (b *EditBuffer) Map() int {
	offset := b.LnoOffset()
	i := 0
	for l := b.anchor; l != nil && i < b.view.Rows-1; l = l.Next() {
		ln := l.Value.(*EditLine)
		cnt := b.ScreenLines(ln)
		wrap := 0
		raw := ln.raw()
		for lmt := i + cnt; i < lmt; i++ {
			str := make([]byte, b.view.Cols)
			// XXX this is the first part of the line, the optional
			// line number.  if this isn't the first screen line of
			// a line (for a wrapped line), then just draw empty
			// space if line numbers are on
			if wrap == 0 {
				lno := []byte(fmt.Sprintf("%*d ", offset-1, ln.lno))
				copy(str[0:offset], lno)
			} else {
				for j := 0; j < offset; j++ {
					str[j] = ' '
				}
			}

			// XXX the second part of the line, which shows actual
			// text that the user is viewing
			actual := b.view.Cols - offset
			start := actual * wrap
			end := start + actual - 1
			if end >= ln.DisplayLength() {
				end = ln.DisplayLength()
			}
			copy(str[offset:], raw[start:end])

			if b.line == l && (ln.cursor >= start || ln.cursor <= end) {
				b.curs_y = i
				b.curs_x = b.LnoOffset() + (ln.cursor - start)
			}
			b.view.Lines[i] = string(str)
			wrap++
		}
	}
	for ; i < b.view.Rows-1; i++ {
		b.view.Lines[i] = NaL
	}
	return 0
}

func (b *EditBuffer) CursorCoord() (int, int) {
	return b.curs_y, b.curs_x
}

func (b *EditBuffer) Lines() *list.List {
	return b.lines
}
<|endoftext|>


<commit_before>package authmethodupdate

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"strings"
	"time"

	"github.com/hashicorp/consul/api"
	"github.com/hashicorp/consul/command/acl/authmethod"
	"github.com/hashicorp/consul/command/flags"
	"github.com/hashicorp/consul/command/helpers"
	"github.com/mitchellh/cli"
)

func New(ui cli.Ui) *cmd {
	c := &cmd{UI: ui}
	c.init()
	return c
}

type cmd struct {
	UI    cli.Ui
	flags *flag.FlagSet
	http  *flags.HTTPFlags
	help  string

	name string

	displayName   string
	description   string
	maxTokenTTL   time.Duration
	tokenLocality string
	config        string

	k8sHost              string
	k8sCACert            string
	k8sServiceAccountJWT string

	noMerge  bool
	showMeta bool
	format   string

	testStdin io.Reader

	enterpriseCmd
}

func (c *cmd) init() {
	c.flags = flag.NewFlagSet("", flag.ContinueOnError)

	c.flags.BoolVar(
		&c.showMeta,
		"meta",
		false,
		"Indicates that auth method metadata such "+
			"as the raft indices should be shown for each entry.",
	)

	c.flags.StringVar(
		&c.name,
		"name",
		"",
		"The auth method name.",
	)

	c.flags.StringVar(
		&c.displayName,
		"display-name",
		"",
		"An optional name to use instead of the name when displaying this auth method in a UI.",
	)

	c.flags.StringVar(
		&c.description,
		"description",
		"",
		"A description of the auth method.",
	)

	c.flags.DurationVar(
		&c.maxTokenTTL,
		"max-token-ttl",
		0,
		"Duration of time all tokens created by this auth method should be valid for",
	)
	c.flags.StringVar(
		&c.tokenLocality,
		"token-locality",
		"",
		"Defines the kind of token that this auth method should produce. "+
			"This can be either 'local' or 'global'. If empty the value of 'local' is assumed.",
	)

	c.flags.StringVar(
		&c.config,
		"config",
		"",
		"The configuration for the auth method. Must be JSON. The config is updated as one field"+
			"May be prefixed with '@' to indicate that the value is a file path to load the config from. "+
			"'-' may also be given to indicate that the config are available on stdin. ",
	)

	c.flags.StringVar(
		&c.k8sHost,
		"kubernetes-host",
		"",
		"Address of the Kubernetes API server. This flag is required for type=kubernetes.",
	)
	c.flags.StringVar(
		&c.k8sCACert,
		"kubernetes-ca-cert",
		"",
		"PEM encoded CA cert for use by the TLS client used to talk with the "+
			"Kubernetes API. May be prefixed with '@' to indicate that the "+
			"value is a file path to load the cert from. "+
			"This flag is required for type=kubernetes.",
	)
	c.flags.StringVar(
		&c.k8sServiceAccountJWT,
		"kubernetes-service-account-jwt",
		"",
		"A Kubernetes service account JWT used to access the TokenReview API to "+
			"validate other JWTs during login. "+
			"This flag is required for type=kubernetes.",
	)

	c.flags.BoolVar(&c.noMerge, "no-merge", false, "Do not merge the current auth method "+
		"information with what is provided to the command. Instead overwrite all fields "+
		"with the exception of the name which is immutable.")

	c.flags.StringVar(
		&c.format,
		"format",
		authmethod.PrettyFormat,
		fmt.Sprintf("Output format {%s}", strings.Join(authmethod.GetSupportedFormats(), "|")),
	)

	c.initEnterpriseFlags()

	c.http = &flags.HTTPFlags{}
	flags.Merge(c.flags, c.http.ClientFlags())
	flags.Merge(c.flags, c.http.ServerFlags())
	flags.Merge(c.flags, c.http.NamespaceFlags())
	c.help = flags.Usage(help, c.flags)
}

func (c *cmd) Run(args []string) int {
	if err := c.flags.Parse(args); err != nil {
		return 1
	}

	if c.name == "" {
		c.UI.Error(fmt.Sprintf("Cannot update an auth method without specifying the -name parameter"))
		return 1
	}

	client, err := c.http.APIClient()
	if err != nil {
		c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
		return 1
	}

	// Regardless of merge, we need to fetch the prior immutable fields first.
	currentAuthMethod, _, err := client.ACL().AuthMethodRead(c.name, nil)
	if err != nil {
		c.UI.Error(fmt.Sprintf("Error when retrieving current auth method: %v", err))
		return 1
	} else if currentAuthMethod == nil {
		c.UI.Error(fmt.Sprintf("Auth method not found with name %q", c.name))
		return 1
	}

	if c.k8sCACert != "" {
		c.k8sCACert, err = helpers.LoadDataSource(c.k8sCACert, c.testStdin)
		if err != nil {
			c.UI.Error(fmt.Sprintf("Invalid '-kubernetes-ca-cert' value: %v", err))
			return 1
		} else if c.k8sCACert == "" {
			c.UI.Error(fmt.Sprintf("Kubernetes CA Cert is empty"))
			return 1
		}
	}

	var method *api.ACLAuthMethod
	if c.noMerge {
		method = &api.ACLAuthMethod{
			Name:          currentAuthMethod.Name,
			Type:          currentAuthMethod.Type,
			DisplayName:   c.displayName,
			Description:   c.description,
			TokenLocality: c.tokenLocality,
		}
		if c.maxTokenTTL > 0 {
			method.MaxTokenTTL = c.maxTokenTTL
		}

		if err := c.enterprisePopulateAuthMethod(method); err != nil {
			c.UI.Error(err.Error())
			return 1
		}

		if c.config != "" {
			if c.k8sHost != "" || c.k8sCACert != "" || c.k8sServiceAccountJWT != "" {
				c.UI.Error(fmt.Sprintf("Cannot use command line arguments with '-config' flag"))
				return 1
			}
			data, err := helpers.LoadDataSource(c.config, c.testStdin)
			if err != nil {
				c.UI.Error(fmt.Sprintf("Error loading configuration file: %v", err))
				return 1
			}
			if err := json.Unmarshal([]byte(data), &method.Config); err != nil {
				c.UI.Error(fmt.Sprintf("Error parsing JSON for auth method config: %v", err))
				return 1
			}
		}

		if currentAuthMethod.Type == "kubernetes" {
			if c.k8sHost == "" {
				c.UI.Error(fmt.Sprintf("Missing required '-kubernetes-host' flag"))
				return 1
			} else if c.k8sCACert == "" {
				c.UI.Error(fmt.Sprintf("Missing required '-kubernetes-ca-cert' flag"))
				return 1
			} else if c.k8sServiceAccountJWT == "" {
				c.UI.Error(fmt.Sprintf("Missing required '-kubernetes-service-account-jwt' flag"))
				return 1
			}

			method.Config = map[string]interface{}{
				"Host":              c.k8sHost,
				"CACert":            c.k8sCACert,
				"ServiceAccountJWT": c.k8sServiceAccountJWT,
			}
		}
	} else {
		methodCopy := *currentAuthMethod
		method = &methodCopy
		if c.description != "" {
			method.Description = c.description
		}
		if c.displayName != "" {
			method.DisplayName = c.displayName
		}
		if c.maxTokenTTL > 0 {
			method.MaxTokenTTL = c.maxTokenTTL
		}
		if c.tokenLocality != "" {
			method.TokenLocality = c.tokenLocality
		}
		if err := c.enterprisePopulateAuthMethod(method); err != nil {
			c.UI.Error(err.Error())
			return 1
		}
		if c.config != "" {
			if c.k8sHost != "" || c.k8sCACert != "" || c.k8sServiceAccountJWT != "" {
				c.UI.Error(fmt.Sprintf("Cannot use command line arguments with '-config' flag"))
				return 1
			}
			data, err := helpers.LoadDataSource(c.config, c.testStdin)
			if err != nil {
				c.UI.Error(fmt.Sprintf("Error loading configuration file: %v", err))
				return 1
			}
			// Don't attempt a deep merge.
			method.Config = make(map[string]interface{})
			if err := json.Unmarshal([]byte(data), &method.Config); err != nil {
				c.UI.Error(fmt.Sprintf("Error parsing JSON for auth method config: %v", err))
				return 1
			}
		}
		if method.Config == nil {
			method.Config = make(map[string]interface{})
		}
		if currentAuthMethod.Type == "kubernetes" {
			if c.k8sHost != "" {
				method.Config["Host"] = c.k8sHost
			}
			if c.k8sCACert != "" {
				method.Config["CACert"] = c.k8sCACert
			}
			if c.k8sServiceAccountJWT != "" {
				method.Config["ServiceAccountJWT"] = c.k8sServiceAccountJWT
			}
		}
	}

	method, _, err = client.ACL().AuthMethodUpdate(method, nil)
	if err != nil {
		c.UI.Error(fmt.Sprintf("Error updating auth method %q: %v", c.name, err))
		return 1
	}

	formatter, err := authmethod.NewFormatter(c.format, c.showMeta)
	if err != nil {
		c.UI.Error(err.Error())
		return 1
	}

	out, err := formatter.FormatAuthMethod(method)
	if err != nil {
		c.UI.Error(err.Error())
		return 1
	}
	if out != "" {
		c.UI.Info(out)
	}

	return 0
}

func (c *cmd) Synopsis() string {
	return synopsis
}

func (c *cmd) Help() string {
	return flags.Usage(c.help, nil)
}

const synopsis = "Update an ACL auth method"
const help = `
Usage: consul acl auth-method update -name NAME [options]

  Updates an auth method. By default it will merge the auth method
  information with its current state so that you do not have to provide all
  parameters. This behavior can be disabled by passing -no-merge.

  Update all editable fields of the auth method:

    $ consul acl auth-method update -name "my-k8s" \
                            -description "new description" \
                            -kubernetes-host "https://new-apiserver.example.com:8443" \
                            -kubernetes-ca-cert @/path/to/new-kube.ca.crt \
                            -kubernetes-service-account-jwt "NEW_JWT_CONTENTS"
<commit_msg>add tailing backtick<commit_after>package authmethodupdate

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"strings"
	"time"

	"github.com/hashicorp/consul/api"
	"github.com/hashicorp/consul/command/acl/authmethod"
	"github.com/hashicorp/consul/command/flags"
	"github.com/hashicorp/consul/command/helpers"
	"github.com/mitchellh/cli"
)

func New(ui cli.Ui) *cmd {
	c := &cmd{UI: ui}
	c.init()
	return c
}

type cmd struct {
	UI    cli.Ui
	flags *flag.FlagSet
	http  *flags.HTTPFlags
	help  string

	name string

	displayName   string
	description   string
	maxTokenTTL   time.Duration
	tokenLocality string
	config        string

	k8sHost              string
	k8sCACert            string
	k8sServiceAccountJWT string

	noMerge  bool
	showMeta bool
	format   string

	testStdin io.Reader

	enterpriseCmd
}

func (c *cmd) init() {
	c.flags = flag.NewFlagSet("", flag.ContinueOnError)

	c.flags.BoolVar(
		&c.showMeta,
		"meta",
		false,
		"Indicates that auth method metadata such "+
			"as the raft indices should be shown for each entry.",
	)

	c.flags.StringVar(
		&c.name,
		"name",
		"",
		"The auth method name.",
	)

	c.flags.StringVar(
		&c.displayName,
		"display-name",
		"",
		"An optional name to use instead of the name when displaying this auth method in a UI.",
	)

	c.flags.StringVar(
		&c.description,
		"description",
		"",
		"A description of the auth method.",
	)

	c.flags.DurationVar(
		&c.maxTokenTTL,
		"max-token-ttl",
		0,
		"Duration of time all tokens created by this auth method should be valid for",
	)
	c.flags.StringVar(
		&c.tokenLocality,
		"token-locality",
		"",
		"Defines the kind of token that this auth method should produce. "+
			"This can be either 'local' or 'global'. If empty the value of 'local' is assumed.",
	)

	c.flags.StringVar(
		&c.config,
		"config",
		"",
		"The configuration for the auth method. Must be JSON. The config is updated as one field"+
			"May be prefixed with '@' to indicate that the value is a file path to load the config from. "+
			"'-' may also be given to indicate that the config are available on stdin. ",
	)

	c.flags.StringVar(
		&c.k8sHost,
		"kubernetes-host",
		"",
		"Address of the Kubernetes API server. This flag is required for type=kubernetes.",
	)
	c.flags.StringVar(
		&c.k8sCACert,
		"kubernetes-ca-cert",
		"",
		"PEM encoded CA cert for use by the TLS client used to talk with the "+
			"Kubernetes API. May be prefixed with '@' to indicate that the "+
			"value is a file path to load the cert from. "+
			"This flag is required for type=kubernetes.",
	)
	c.flags.StringVar(
		&c.k8sServiceAccountJWT,
		"kubernetes-service-account-jwt",
		"",
		"A Kubernetes service account JWT used to access the TokenReview API to "+
			"validate other JWTs during login. "+
			"This flag is required for type=kubernetes.",
	)

	c.flags.BoolVar(&c.noMerge, "no-merge", false, "Do not merge the current auth method "+
		"information with what is provided to the command. Instead overwrite all fields "+
		"with the exception of the name which is immutable.")

	c.flags.StringVar(
		&c.format,
		"format",
		authmethod.PrettyFormat,
		fmt.Sprintf("Output format {%s}", strings.Join(authmethod.GetSupportedFormats(), "|")),
	)

	c.initEnterpriseFlags()

	c.http = &flags.HTTPFlags{}
	flags.Merge(c.flags, c.http.ClientFlags())
	flags.Merge(c.flags, c.http.ServerFlags())
	flags.Merge(c.flags, c.http.NamespaceFlags())
	c.help = flags.Usage(help, c.flags)
}

func (c *cmd) Run(args []string) int {
	if err := c.flags.Parse(args); err != nil {
		return 1
	}

	if c.name == "" {
		c.UI.Error(fmt.Sprintf("Cannot update an auth method without specifying the -name parameter"))
		return 1
	}

	client, err := c.http.APIClient()
	if err != nil {
		c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
		return 1
	}

	// Regardless of merge, we need to fetch the prior immutable fields first.
	currentAuthMethod, _, err := client.ACL().AuthMethodRead(c.name, nil)
	if err != nil {
		c.UI.Error(fmt.Sprintf("Error when retrieving current auth method: %v", err))
		return 1
	} else if currentAuthMethod == nil {
		c.UI.Error(fmt.Sprintf("Auth method not found with name %q", c.name))
		return 1
	}

	if c.k8sCACert != "" {
		c.k8sCACert, err = helpers.LoadDataSource(c.k8sCACert, c.testStdin)
		if err != nil {
			c.UI.Error(fmt.Sprintf("Invalid '-kubernetes-ca-cert' value: %v", err))
			return 1
		} else if c.k8sCACert == "" {
			c.UI.Error(fmt.Sprintf("Kubernetes CA Cert is empty"))
			return 1
		}
	}

	var method *api.ACLAuthMethod
	if c.noMerge {
		method = &api.ACLAuthMethod{
			Name:          currentAuthMethod.Name,
			Type:          currentAuthMethod.Type,
			DisplayName:   c.displayName,
			Description:   c.description,
			TokenLocality: c.tokenLocality,
		}
		if c.maxTokenTTL > 0 {
			method.MaxTokenTTL = c.maxTokenTTL
		}

		if err := c.enterprisePopulateAuthMethod(method); err != nil {
			c.UI.Error(err.Error())
			return 1
		}

		if c.config != "" {
			if c.k8sHost != "" || c.k8sCACert != "" || c.k8sServiceAccountJWT != "" {
				c.UI.Error(fmt.Sprintf("Cannot use command line arguments with '-config' flag"))
				return 1
			}
			data, err := helpers.LoadDataSource(c.config, c.testStdin)
			if err != nil {
				c.UI.Error(fmt.Sprintf("Error loading configuration file: %v", err))
				return 1
			}
			if err := json.Unmarshal([]byte(data), &method.Config); err != nil {
				c.UI.Error(fmt.Sprintf("Error parsing JSON for auth method config: %v", err))
				return 1
			}
		}

		if currentAuthMethod.Type == "kubernetes" {
			if c.k8sHost == "" {
				c.UI.Error(fmt.Sprintf("Missing required '-kubernetes-host' flag"))
				return 1
			} else if c.k8sCACert == "" {
				c.UI.Error(fmt.Sprintf("Missing required '-kubernetes-ca-cert' flag"))
				return 1
			} else if c.k8sServiceAccountJWT == "" {
				c.UI.Error(fmt.Sprintf("Missing required '-kubernetes-service-account-jwt' flag"))
				return 1
			}

			method.Config = map[string]interface{}{
				"Host":              c.k8sHost,
				"CACert":            c.k8sCACert,
				"ServiceAccountJWT": c.k8sServiceAccountJWT,
			}
		}
	} else {
		methodCopy := *currentAuthMethod
		method = &methodCopy
		if c.description != "" {
			method.Description = c.description
		}
		if c.displayName != "" {
			method.DisplayName = c.displayName
		}
		if c.maxTokenTTL > 0 {
			method.MaxTokenTTL = c.maxTokenTTL
		}
		if c.tokenLocality != "" {
			method.TokenLocality = c.tokenLocality
		}
		if err := c.enterprisePopulateAuthMethod(method); err != nil {
			c.UI.Error(err.Error())
			return 1
		}
		if c.config != "" {
			if c.k8sHost != "" || c.k8sCACert != "" || c.k8sServiceAccountJWT != "" {
				c.UI.Error(fmt.Sprintf("Cannot use command line arguments with '-config' flag"))
				return 1
			}
			data, err := helpers.LoadDataSource(c.config, c.testStdin)
			if err != nil {
				c.UI.Error(fmt.Sprintf("Error loading configuration file: %v", err))
				return 1
			}
			// Don't attempt a deep merge.
			method.Config = make(map[string]interface{})
			if err := json.Unmarshal([]byte(data), &method.Config); err != nil {
				c.UI.Error(fmt.Sprintf("Error parsing JSON for auth method config: %v", err))
				return 1
			}
		}
		if method.Config == nil {
			method.Config = make(map[string]interface{})
		}
		if currentAuthMethod.Type == "kubernetes" {
			if c.k8sHost != "" {
				method.Config["Host"] = c.k8sHost
			}
			if c.k8sCACert != "" {
				method.Config["CACert"] = c.k8sCACert
			}
			if c.k8sServiceAccountJWT != "" {
				method.Config["ServiceAccountJWT"] = c.k8sServiceAccountJWT
			}
		}
	}

	method, _, err = client.ACL().AuthMethodUpdate(method, nil)
	if err != nil {
		c.UI.Error(fmt.Sprintf("Error updating auth method %q: %v", c.name, err))
		return 1
	}

	formatter, err := authmethod.NewFormatter(c.format, c.showMeta)
	if err != nil {
		c.UI.Error(err.Error())
		return 1
	}

	out, err := formatter.FormatAuthMethod(method)
	if err != nil {
		c.UI.Error(err.Error())
		return 1
	}
	if out != "" {
		c.UI.Info(out)
	}

	return 0
}

func (c *cmd) Synopsis() string {
	return synopsis
}

func (c *cmd) Help() string {
	return flags.Usage(c.help, nil)
}

const synopsis = "Update an ACL auth method"
const help = `
Usage: consul acl auth-method update -name NAME [options]

  Updates an auth method. By default it will merge the auth method
  information with its current state so that you do not have to provide all
  parameters. This behavior can be disabled by passing -no-merge.

  Update all editable fields of the auth method:

    $ consul acl auth-method update -name "my-k8s" \
                            -description "new description" \
                            -kubernetes-host "https://new-apiserver.example.com:8443" \
                            -kubernetes-ca-cert @/path/to/new-kube.ca.crt \
                            -kubernetes-service-account-jwt "NEW_JWT_CONTENTS"
`
<|endoftext|>


<commit_before>package dpn_test

import (
	"fmt"
	"github.com/APTrust/bagman/bagman"
	"github.com/APTrust/bagman/dpn"
//	"github.com/nu7hatch/gouuid"
	"net/http"
//	"os"
//	"path/filepath"
//	"strings"
	"testing"
	"time"
)

/*
This file contains integration that rely on a locally-running instance
of the DPN REST service. The tests will not run if runRestTests()
determines that the DPN REST server is unreachable.

The DPN-REST respository includes a file at data/integration_test_data.json
that contains the test data we're expecting to find in these tests.

See the data/README.md file in that repo for information about how to
load that test data into your DPN instance.
*/

var configFile = "dpn/dpn_config.json"
var skipRestMessagePrinted = false
var aptrustBagIdentifier = "9998e960-fc6d-44f4-9d73-9a60a8eae609"
var chronBagIdentifier = "87f2d0ac-94c8-4bb2-90e8-24c076cef9f0"

func runRestTests(t *testing.T) bool {
	config := loadConfig(t, configFile)
	_, err := http.Get(config.RestClient.LocalServiceURL)
	if err != nil {
		if skipRestMessagePrinted == false {
			skipRestMessagePrinted = true
			fmt.Printf("Skipping DPN REST integration tests: "+
				"DPN REST server is not running at %s\n",
				config.RestClient.LocalServiceURL)
		}
		return false
	}
	return true
}

func getClient(t *testing.T) (*dpn.DPNRestClient) {
	// If you want to debug, change ioutil.Discard to os.Stdout
	// to see log output from the client.
	config := loadConfig(t, configFile)
	logger := bagman.DiscardLogger("dpn_rest_client_test")
	client, err := dpn.NewDPNRestClient(
		config.RestClient.LocalServiceURL,
		config.RestClient.LocalAPIRoot,
		config.RestClient.LocalAuthToken,
		logger)
	if err != nil {
		t.Errorf("Error constructing DPN REST client: %v", err)
	}
	return client
}

func TestDPNBagGet(t *testing.T) {
	if runRestTests(t) == false {
		return
	}
	client := getClient(t)
	dpnBag, err := client.DPNBagGet(aptrustBagIdentifier)
	if err != nil {
		t.Error(err)
		return
	}
	if dpnBag.UUID != aptrustBagIdentifier {
		t.Errorf("UUID: expected '%s', got '%s'", aptrustBagIdentifier, dpnBag.UUID)
	}
	if dpnBag.LocalId != "aptrust-12345" {
		t.Errorf("LocalId: expected 'aptrust-12345', got '%s'", dpnBag.LocalId)
	}
	if dpnBag.Size != 2526492640 {
		t.Errorf("Size: expected 2526492640, got %d", dpnBag.Size)
	}
	if dpnBag.FirstVersionUUID != aptrustBagIdentifier {
		t.Errorf("FirstVersionUUID: expected '%s', got '%s'",
			aptrustBagIdentifier, dpnBag.FirstVersionUUID)
	}
	if dpnBag.BagType != "D" {
		t.Errorf("BagType: expected 'D', got '%s'", dpnBag.BagType)
	}
	if dpnBag.Version != 1 {
		t.Errorf("Version: expected 1, got %d", dpnBag.Version)
	}
	if dpnBag.IngestNode != "aptrust" {
		t.Errorf("IngestNode: expected 'aptrust', got '%s'", dpnBag.IngestNode)
	}
	if dpnBag.AdminNode != "aptrust" {
		t.Errorf("AdminNode: expected 'aptrust', got '%s'", dpnBag.AdminNode)
	}
	if dpnBag.CreatedAt.Format(time.RFC3339) != "2015-02-25T16:24:02Z" {
		t.Errorf("CreatedAt: expected '2015-02-25T16:24:02.475138Z', got '%s'",
			dpnBag.CreatedAt.Format(time.RFC3339))
	}
	if dpnBag.UpdatedAt.Format(time.RFC3339) != "2015-02-25T16:24:02Z" {
		t.Errorf("UpdatedAt: expected '2015-02-25T16:24:02.475138Z', got '%s'",
			dpnBag.UpdatedAt.Format(time.RFC3339))
	}
	if len(dpnBag.Rights) != 1 {
		t.Errorf("Rights: expected 1 item, got %d", len(dpnBag.Rights))
	}
	if dpnBag.Rights[0] != "ff297922-a5b2-4b66-9475-3ce98b074d37" {
		t.Errorf("Rights[0]: expected 'ff297922-a5b2-4b66-9475-3ce98b074d37', got '%s'",
			dpnBag.Rights[0])
	}
	if len(dpnBag.Interpretive) != 1 {
		t.Errorf("Interpretive: expected 1 item, got %d", len(dpnBag.Interpretive))
	}
	if dpnBag.Interpretive[0] != "821decbb-4063-48b1-adef-1d3906bf7b87" {
		t.Errorf("Interpretive[0]: expected '821decbb-4063-48b1-adef-1d3906bf7b87', got '%s'",
			dpnBag.Interpretive[0])
	}
	if len(dpnBag.ReplicatingNodes) != 1 {
		t.Errorf("ReplicatingNodes: expected 1 item, got %d", len(dpnBag.ReplicatingNodes))
	}
	if dpnBag.ReplicatingNodes[0] != "chron" {
		t.Errorf("ReplicatingNodes[0]: expected 'chron', got '%s'",
			dpnBag.ReplicatingNodes[0])
	}
	if len(dpnBag.Fixities) != 1 {
		t.Errorf("Fixities: expected 1 item, got %d", len(dpnBag.Fixities))
	}
	if dpnBag.Fixities[0].Algorithm != "sha256" {
		t.Errorf("Fixities[0].Algorithm: expected 'sha256', got '%s'",
			dpnBag.Fixities[0].Algorithm)
	}
	if dpnBag.Fixities[0].Digest != "tums-for-digestion" {
		t.Errorf("Fixities[0].Digest: expected 'tums-for-digestion', got '%s'",
			dpnBag.Fixities[0].Digest)
	}
	if dpnBag.Fixities[0].CreatedAt.Format(time.RFC3339) != "2015-05-01T12:32:17Z" {
		t.Errorf("Fixities[0].CreatedAt: expected '2015-05-01T12:32:17Z', got '%s'",
			dpnBag.Fixities[0].CreatedAt.Format(time.RFC3339))
	}
}
<commit_msg>Added BuildUrl test<commit_after>package dpn_test

import (
	"fmt"
	"github.com/APTrust/bagman/bagman"
	"github.com/APTrust/bagman/dpn"
//	"github.com/nu7hatch/gouuid"
	"net/http"
//	"os"
//	"path/filepath"
//	"strings"
	"testing"
	"time"
)

/*
This file contains integration that rely on a locally-running instance
of the DPN REST service. The tests will not run if runRestTests()
determines that the DPN REST server is unreachable.

The DPN-REST respository includes a file at data/integration_test_data.json
that contains the test data we're expecting to find in these tests.

See the data/README.md file in that repo for information about how to
load that test data into your DPN instance.
*/

var configFile = "dpn/dpn_config.json"
var skipRestMessagePrinted = false
var aptrustBagIdentifier = "9998e960-fc6d-44f4-9d73-9a60a8eae609"
var chronBagIdentifier = "87f2d0ac-94c8-4bb2-90e8-24c076cef9f0"

func runRestTests(t *testing.T) bool {
	config := loadConfig(t, configFile)
	_, err := http.Get(config.RestClient.LocalServiceURL)
	if err != nil {
		if skipRestMessagePrinted == false {
			skipRestMessagePrinted = true
			fmt.Printf("Skipping DPN REST integration tests: "+
				"DPN REST server is not running at %s\n",
				config.RestClient.LocalServiceURL)
		}
		return false
	}
	return true
}

func getClient(t *testing.T) (*dpn.DPNRestClient) {
	// If you want to debug, change ioutil.Discard to os.Stdout
	// to see log output from the client.
	config := loadConfig(t, configFile)
	logger := bagman.DiscardLogger("dpn_rest_client_test")
	client, err := dpn.NewDPNRestClient(
		config.RestClient.LocalServiceURL,
		config.RestClient.LocalAPIRoot,
		config.RestClient.LocalAuthToken,
		logger)
	if err != nil {
		t.Errorf("Error constructing DPN REST client: %v", err)
	}
	return client
}

func TestBuildUrl(t *testing.T) {
	config := loadConfig(t, configFile)
	client := getClient(t)
	relativeUrl := "/api-v1/popeye/olive/oyl/"
	expectedUrl := config.RestClient.LocalServiceURL + relativeUrl
	if client.BuildUrl(relativeUrl) != expectedUrl {
		t.Errorf("BuildUrl returned '%s', expected '%s'",
			client.BuildUrl(relativeUrl), expectedUrl)
	}
}

func TestDPNBagGet(t *testing.T) {
	if runRestTests(t) == false {
		return
	}
	client := getClient(t)
	dpnBag, err := client.DPNBagGet(aptrustBagIdentifier)
	if err != nil {
		t.Error(err)
		return
	}
	if dpnBag.UUID != aptrustBagIdentifier {
		t.Errorf("UUID: expected '%s', got '%s'", aptrustBagIdentifier, dpnBag.UUID)
	}
	if dpnBag.LocalId != "aptrust-12345" {
		t.Errorf("LocalId: expected 'aptrust-12345', got '%s'", dpnBag.LocalId)
	}
	if dpnBag.Size != 2526492640 {
		t.Errorf("Size: expected 2526492640, got %d", dpnBag.Size)
	}
	if dpnBag.FirstVersionUUID != aptrustBagIdentifier {
		t.Errorf("FirstVersionUUID: expected '%s', got '%s'",
			aptrustBagIdentifier, dpnBag.FirstVersionUUID)
	}
	if dpnBag.BagType != "D" {
		t.Errorf("BagType: expected 'D', got '%s'", dpnBag.BagType)
	}
	if dpnBag.Version != 1 {
		t.Errorf("Version: expected 1, got %d", dpnBag.Version)
	}
	if dpnBag.IngestNode != "aptrust" {
		t.Errorf("IngestNode: expected 'aptrust', got '%s'", dpnBag.IngestNode)
	}
	if dpnBag.AdminNode != "aptrust" {
		t.Errorf("AdminNode: expected 'aptrust', got '%s'", dpnBag.AdminNode)
	}
	if dpnBag.CreatedAt.Format(time.RFC3339) != "2015-02-25T16:24:02Z" {
		t.Errorf("CreatedAt: expected '2015-02-25T16:24:02.475138Z', got '%s'",
			dpnBag.CreatedAt.Format(time.RFC3339))
	}
	if dpnBag.UpdatedAt.Format(time.RFC3339) != "2015-02-25T16:24:02Z" {
		t.Errorf("UpdatedAt: expected '2015-02-25T16:24:02.475138Z', got '%s'",
			dpnBag.UpdatedAt.Format(time.RFC3339))
	}
	if len(dpnBag.Rights) != 1 {
		t.Errorf("Rights: expected 1 item, got %d", len(dpnBag.Rights))
	}
	if dpnBag.Rights[0] != "ff297922-a5b2-4b66-9475-3ce98b074d37" {
		t.Errorf("Rights[0]: expected 'ff297922-a5b2-4b66-9475-3ce98b074d37', got '%s'",
			dpnBag.Rights[0])
	}
	if len(dpnBag.Interpretive) != 1 {
		t.Errorf("Interpretive: expected 1 item, got %d", len(dpnBag.Interpretive))
	}
	if dpnBag.Interpretive[0] != "821decbb-4063-48b1-adef-1d3906bf7b87" {
		t.Errorf("Interpretive[0]: expected '821decbb-4063-48b1-adef-1d3906bf7b87', got '%s'",
			dpnBag.Interpretive[0])
	}
	if len(dpnBag.ReplicatingNodes) != 1 {
		t.Errorf("ReplicatingNodes: expected 1 item, got %d", len(dpnBag.ReplicatingNodes))
	}
	if dpnBag.ReplicatingNodes[0] != "chron" {
		t.Errorf("ReplicatingNodes[0]: expected 'chron', got '%s'",
			dpnBag.ReplicatingNodes[0])
	}
	if len(dpnBag.Fixities) != 1 {
		t.Errorf("Fixities: expected 1 item, got %d", len(dpnBag.Fixities))
	}
	if dpnBag.Fixities[0].Algorithm != "sha256" {
		t.Errorf("Fixities[0].Algorithm: expected 'sha256', got '%s'",
			dpnBag.Fixities[0].Algorithm)
	}
	if dpnBag.Fixities[0].Digest != "tums-for-digestion" {
		t.Errorf("Fixities[0].Digest: expected 'tums-for-digestion', got '%s'",
			dpnBag.Fixities[0].Digest)
	}
	if dpnBag.Fixities[0].CreatedAt.Format(time.RFC3339) != "2015-05-01T12:32:17Z" {
		t.Errorf("Fixities[0].CreatedAt: expected '2015-05-01T12:32:17Z', got '%s'",
			dpnBag.Fixities[0].CreatedAt.Format(time.RFC3339))
	}
}
<|endoftext|>


<commit_before>// a simple cache
package scache

import (
	"hash/fnv"
	"reflect"
	"sync"
	"sync/atomic"
	"time"
	"unsafe"
)

var (
	BUCKET_COUNT = 16 //must be a power of 2
	bucket_mask  = uint32(BUCKET_COUNT - 1)
)

type Item struct {
	Expires time.Time
	Value   interface{}
}

type Scache struct {
	gcing   uint32
	max     int32
	count   int32
	ttl     time.Duration
	buckets []*Bucket
}

type Bucket struct {
	sync.RWMutex
	count  int
	lookup map[string]*Item
}

func New(max int32, ttl time.Duration) *Scache {
	scache := &Scache{
		count:   0,
		max:     max,
		ttl:     ttl,
		buckets: make([]*Bucket, BUCKET_COUNT),
	}
	for i := 0; i < BUCKET_COUNT; i++ {
		scache.buckets[i] = &Bucket{
			lookup: make(map[string]*Item),
		}
	}
	return scache
}

func (sc *Scache) Get(key string) interface{} {
	bucket := sc.getBucket(key)
	item := bucket.Get(key)
	if item == nil {
		return nil
	}
	if item.Expires.After(time.Now()) {
		return item.Value
	}
	if bucket.Remove(key) == true {
		atomic.AddInt32(&sc.count, -1)
	}
	return nil
}

func (sc *Scache) Fetch(key string, miss func(key string) (interface{}, error)) (interface{}, error) {
	bucket := sc.getBucket(key)
	item := bucket.Get(key)
	if item != nil && item.Expires.After(time.Now()) {
		return item.Value, nil
	}
	value, err := miss(key)
	if err != nil {
		return nil, err
	}
	if value != nil {
		sc.Set(key, value)
	}
	return value, nil
}

func (sc *Scache) Set(key string, value interface{}) {
	item := &Item{
		Expires: time.Now().Add(sc.ttl),
		Value:   value,
	}
	if sc.getBucket(key).Set(key, item) == true {
		if atomic.AddInt32(&sc.count, 1) >= sc.max && atomic.CompareAndSwapUint32(&sc.gcing, 0, 1) {
			go sc.gc()
		}
	}
}

func (sc *Scache) Remove(key string) bool {
	bucket := sc.getBucket(key)
	if bucket.Remove(key) == true {
		atomic.AddInt32(&sc.count, -1)
		return true
	}
	return false
}

func (sc *Scache) gc() {
	defer atomic.StoreUint32(&sc.gcing, 0)
	freed := int32(0)
	for i := 0; i < BUCKET_COUNT; i++ {
		if sc.buckets[i].gc() {
			freed++
		}
	}
	atomic.AddInt32(&sc.count, -freed)
}

func (b *Bucket) Get(key string) *Item {
	b.RLock()
	value := b.lookup[key]
	b.RUnlock()
	return value
}

func (b *Bucket) Remove(key string) bool {
	b.Lock()
	_, exists := b.lookup[key]
	delete(b.lookup, key)
	b.Unlock()
	return exists
}

func (b *Bucket) Set(key string, item *Item) bool {
	b.Lock()
	_, exists := b.lookup[key]
	b.lookup[key] = item
	b.Unlock()
	return !exists
}

func (b *Bucket) gc() bool {
	visited, now := 0, time.Now()
	ok, oe := "", now.Add(time.Hour*8765)

	b.RLock()
	for key, item := range b.lookup {
		if item.Expires.Before(oe) {
			oe = item.Expires
			ok = key
			if oe.Before(now) {
				break
			}
		}
		if visited++; visited == 10 {
			break
		}
	}
	b.RUnlock()

	if len(ok) == 0 {
		return false
	}

	b.Lock()
	delete(b.lookup, ok)
	b.Unlock()
	return true
}

func (sc *Scache) getBucket(key string) *Bucket {
	h := fnv.New32a()
	h.Write(str2bytes(&key))
	return sc.buckets[h.Sum32()&bucket_mask]
}

func str2bytes(s *string) []byte {
	sh := (*reflect.SliceHeader)(unsafe.Pointer(s))
	sh.Len = len(*s)
	sh.Cap = sh.Len
	return *(*[]byte)(unsafe.Pointer(sh))
}
<commit_msg>cache.Clear() to remove all items<commit_after>// a simple cache
package scache

import (
	"hash/fnv"
	"reflect"
	"sync"
	"sync/atomic"
	"time"
	"unsafe"
)

var (
	BUCKET_COUNT = 16 //must be a power of 2
	bucket_mask  = uint32(BUCKET_COUNT - 1)
)

type Item struct {
	Expires time.Time
	Value   interface{}
}

type Scache struct {
	gcing   uint32
	max     int32
	count   int32
	ttl     time.Duration
	buckets []*Bucket
}

type Bucket struct {
	sync.RWMutex
	count  int
	lookup map[string]*Item
}

func New(max int32, ttl time.Duration) *Scache {
	scache := &Scache{
		count:   0,
		max:     max,
		ttl:     ttl,
		buckets: make([]*Bucket, BUCKET_COUNT),
	}
	for i := 0; i < BUCKET_COUNT; i++ {
		scache.buckets[i] = &Bucket{
			lookup: make(map[string]*Item),
		}
	}
	return scache
}

func (sc *Scache) Get(key string) interface{} {
	bucket := sc.getBucket(key)
	item := bucket.Get(key)
	if item == nil {
		return nil
	}
	if item.Expires.After(time.Now()) {
		return item.Value
	}
	if bucket.Remove(key) == true {
		atomic.AddInt32(&sc.count, -1)
	}
	return nil
}

func (sc *Scache) Fetch(key string, miss func(key string) (interface{}, error)) (interface{}, error) {
	bucket := sc.getBucket(key)
	item := bucket.Get(key)
	if item != nil && item.Expires.After(time.Now()) {
		return item.Value, nil
	}
	value, err := miss(key)
	if err != nil {
		return nil, err
	}
	if value != nil {
		sc.Set(key, value)
	}
	return value, nil
}

func (sc *Scache) Set(key string, value interface{}) {
	item := &Item{
		Expires: time.Now().Add(sc.ttl),
		Value:   value,
	}
	if sc.getBucket(key).Set(key, item) == true {
		if atomic.AddInt32(&sc.count, 1) >= sc.max && atomic.CompareAndSwapUint32(&sc.gcing, 0, 1) {
			go sc.gc()
		}
	}
}

func (sc *Scache) Remove(key string) bool {
	bucket := sc.getBucket(key)
	if bucket.Remove(key) == true {
		atomic.AddInt32(&sc.count, -1)
		return true
	}
	return false
}

func (sc *Scache) Clear() {
	for _, bucket := range sc.buckets {
		lookup := make(map[string]*Item)
		bucket.Lock()
		bucket.count = 0
		bucket.lookup = lookup
		bucket.Unlock()
	}
}

func (sc *Scache) gc() {
	defer atomic.StoreUint32(&sc.gcing, 0)
	freed := int32(0)
	for i := 0; i < BUCKET_COUNT; i++ {
		if sc.buckets[i].gc() {
			freed++
		}
	}
	atomic.AddInt32(&sc.count, -freed)
}

func (b *Bucket) Get(key string) *Item {
	b.RLock()
	value := b.lookup[key]
	b.RUnlock()
	return value
}

func (b *Bucket) Remove(key string) bool {
	b.Lock()
	_, exists := b.lookup[key]
	delete(b.lookup, key)
	b.Unlock()
	return exists
}

func (b *Bucket) Set(key string, item *Item) bool {
	b.Lock()
	_, exists := b.lookup[key]
	b.lookup[key] = item
	b.Unlock()
	return !exists
}

func (b *Bucket) gc() bool {
	visited, now := 0, time.Now()
	ok, oe := "", now.Add(time.Hour*8765)

	b.RLock()
	for key, item := range b.lookup {
		if item.Expires.Before(oe) {
			oe = item.Expires
			ok = key
			if oe.Before(now) {
				break
			}
		}
		if visited++; visited == 10 {
			break
		}
	}
	b.RUnlock()

	if len(ok) == 0 {
		return false
	}

	b.Lock()
	delete(b.lookup, ok)
	b.Unlock()
	return true
}

func (sc *Scache) getBucket(key string) *Bucket {
	h := fnv.New32a()
	h.Write(str2bytes(&key))
	return sc.buckets[h.Sum32()&bucket_mask]
}

func str2bytes(s *string) []byte {
	sh := (*reflect.SliceHeader)(unsafe.Pointer(s))
	sh.Len = len(*s)
	sh.Cap = sh.Len
	return *(*[]byte)(unsafe.Pointer(sh))
}
<|endoftext|>


<commit_before>package finalizer

import (
	"reflect"
	"strings"
	"time"

	"github.com/itrabbit/just"
)

var (
	timeType = reflect.TypeOf(time.Time{})
)

const (
	// Типы условий исключения из выдачи
	excludeCmEqual = 1
)

func isEmptyValue(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
		return v.Len() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	case reflect.Struct:
		if m := v.MethodByName("IsZero"); m.IsValid() {
			return m.Call([]reflect.Value{})[0].Bool()
		}
	}
	return false
}

func checkExportedInterface(value reflect.Value) bool {
	kind := value.Kind()
	switch kind {
	case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:
		return !value.IsNil()
	default:
		return true
	}
}

func indexOfStrings(list []string, x string) int {
	if list != nil {
		for i, item := range list {
			if strings.EqualFold(item, x) {
				return i
			}
		}
	}
	return -1
}

type fieldOptions struct {
	Name              string
	Export            string
	Omitempty         bool
	Skip              bool
	ExcludeConditions map[string]int
}

func (o *fieldOptions) SetExcludeConditions(toFieldName string, opID int) {
	if o.ExcludeConditions == nil {
		o.ExcludeConditions = make(map[string]int)
	}
	o.ExcludeConditions[toFieldName] = opID
}

func parseFieldTags(encTagName string, field *reflect.StructField, groups ...string) *fieldOptions {
	options := &fieldOptions{}
	// 1. Определяемся с параметром пропуска значения
	groupTag := strings.TrimSpace(field.Tag.Get("group"))
	if len(groupTag) > 0 {
		if items := strings.Split(groupTag, ","); len(items) > 0 {
			options.Skip = true
			if len(groups) > 0 {
				for _, item := range items {
					if i := indexOfStrings(groups, item); i >= 0 {
						options.Skip = false
						break
					}
				}
			}
		}
	}
	if options.Skip {
		return options
	}
	// 2. Находим имя и определяем возможность пропуска пустого значения
	options.Name = field.Name
	if items := strings.Split(field.Tag.Get(encTagName), ","); len(items) > 0 {
		first := strings.TrimSpace(items[0])
		if len(first) > 0 {
			options.Name = first
			if options.Name == "-" {
				options.Skip = true
				return options
			}
		}
		if len(items) > 1 {
			for i, item := range items {
				if i > 0 {
					if strings.TrimSpace(item) == "omitempty" {
						options.Omitempty = true
						break
					}
				}
			}
		}
	}
	// 3. Получаем поле для экспорта внутреннего значения, если оно указано
	options.Export = strings.TrimSpace(field.Tag.Get("export"))
	// 4. Получаем данные для условий исключения
	for _, exclude := range strings.Split(strings.TrimSpace(field.Tag.Get("exclude")), ";") {
		params := strings.Split(exclude, ":")
		if len(params) > 1 {
			switch strings.ToLower(strings.TrimSpace(params[0])) {
			case "equal":
				options.SetExcludeConditions(strings.TrimSpace(params[1]), excludeCmEqual)
				break
			default:
				break
			}
		}
	}
	return options
}

// Finalization of objects for a particular data model output to the serializer.
func Finalize(encTagName string, v interface{}, groups ...string) interface{} {
	// TODO: Оптимизировать в будущем
	val := reflect.Indirect(reflect.ValueOf(v))
	kind := val.Type().Kind()
	if kind == reflect.Array || kind == reflect.Slice {
		list := make([]interface{}, val.Len(), val.Len())
		for i := 0; i < val.Len(); i++ {
			if elem := val.Index(i); elem.IsValid() {
				list[i] = Finalize(encTagName, elem.Interface(), groups...)
				continue
			}
			list[i] = nil
		}
		return list
	} else if kind == reflect.Struct {
		t := val.Type()
		if !t.AssignableTo(timeType) && t.Name() != "Time" {
			m := make(just.H)
			// Перебираем поля и проверяем по группам
			for i := 0; i < t.NumField(); i++ {
				if field := t.Field(i); !field.Anonymous {
					if options := parseFieldTags(encTagName, &field, groups...); !options.Skip {
						fieldVal := val.Field(i)
						if options.Omitempty && isEmptyValue(fieldVal) {
							continue
						}
						if options.ExcludeConditions != nil && len(options.ExcludeConditions) > 0 {
							excluded := false
							for toFieldName, opID := range options.ExcludeConditions {
								if toFieldType, ok := t.FieldByName(toFieldName); ok && !toFieldType.Anonymous {
									// Сравнение значений
									if opID == excludeCmEqual {
										if toFieldVal := val.FieldByName(toFieldName); toFieldVal.IsValid() {
											if reflect.DeepEqual(fieldVal.Interface(), toFieldVal.Interface()) {
												excluded = true
												break
											}
										}
									}
								}
							}
							if excluded {
								continue
							}
						}
						if len(options.Export) > 0 {
							if indFieldVal := reflect.Indirect(fieldVal); indFieldVal.IsValid() {
								expKind := indFieldVal.Type().Kind()
								if expKind == reflect.Struct {
									if subField, ok := indFieldVal.Type().FieldByName(options.Export); ok {
										subFieldVal := indFieldVal.FieldByName(options.Export)
										if options.Omitempty && isEmptyValue(subFieldVal) {
											continue
										}
										if !subField.Anonymous && checkExportedInterface(subFieldVal) {
											m[options.Name] = Finalize(encTagName, subFieldVal.Interface(), groups...)
										} else if !options.Omitempty {
											m[options.Name] = nil
										}
									}
								} else if expKind == reflect.Slice || expKind == reflect.Array {
									arr := make([]interface{}, 0)
									for index := 0; index < indFieldVal.Len(); index++ {
										if itemVal := reflect.Indirect(indFieldVal.Index(index)); itemVal.Type().Kind() == reflect.Struct {
											if subField, ok := itemVal.Type().FieldByName(options.Export); ok {
												subFieldVal := itemVal.FieldByName(options.Export)
												if options.Omitempty && isEmptyValue(subFieldVal) {
													continue
												}
												if !subField.Anonymous && checkExportedInterface(subFieldVal) {
													arr = append(arr, Finalize(encTagName, subFieldVal.Interface(), groups...))
												}
											}
										}
									}
									if len(arr) > 0 || options.Omitempty {
										m[options.Name] = arr
									}
								}
							}
							continue
						}
						if checkExportedInterface(fieldVal) {
							m[options.Name] = Finalize(encTagName, fieldVal.Interface(), groups...)
						} else if !options.Omitempty {
							m[options.Name] = nil
						}
					}
				}
			}
			return m
		}
	}
	return v
}
<commit_msg>- Add bygroup tag for finalize<commit_after>package finalizer

import (
	"reflect"
	"strings"
	"time"

	"github.com/itrabbit/just"
)

var (
	timeType = reflect.TypeOf(time.Time{})
)

const (
	// Типы условий исключения из выдачи
	excludeCmEqual = 1
)

func isEmptyValue(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
		return v.Len() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	case reflect.Struct:
		if m := v.MethodByName("IsZero"); m.IsValid() {
			return m.Call([]reflect.Value{})[0].Bool()
		}
	}
	return false
}

func checkExportedInterface(value reflect.Value) bool {
	kind := value.Kind()
	switch kind {
	case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:
		return !value.IsNil()
	default:
		return true
	}
}

func indexOfStrings(list []string, x string) int {
	if list != nil {
		for i, item := range list {
			if strings.EqualFold(item, x) {
				return i
			}
		}
	}
	return -1
}

type fieldOptions struct {
	Name              string
	Group             string
	Export            string
	Omitempty         bool
	Skip              bool
	ExcludeConditions map[string]int
}

func (o *fieldOptions) SetExcludeConditions(toFieldName string, opID int) {
	if o.ExcludeConditions == nil {
		o.ExcludeConditions = make(map[string]int)
	}
	o.ExcludeConditions[toFieldName] = opID
}

func parseFieldTags(encTagName string, field *reflect.StructField, groups ...string) *fieldOptions {
	options := &fieldOptions{}
	// 1. Определяемся с параметром пропуска значения
	groupTag := strings.TrimSpace(field.Tag.Get("group"))
	if len(groupTag) > 0 {
		if items := strings.Split(groupTag, ","); len(items) > 0 {
			options.Skip = true
			if len(groups) > 0 {
				for _, item := range items {
					if i := indexOfStrings(groups, item); i >= 0 {
						options.Skip = false
						break
					}
				}
			}
		}
	}
	if options.Skip {
		return options
	}
	// 2. Находим имя и определяем возможность пропуска пустого значения
	options.Name = field.Name
	if items := strings.Split(field.Tag.Get(encTagName), ","); len(items) > 0 {
		first := strings.TrimSpace(items[0])
		if len(first) > 0 {
			options.Name = first
			if options.Name == "-" {
				options.Skip = true
				return options
			}
		}
		if len(items) > 1 {
			for i, item := range items {
				if i > 0 {
					if strings.TrimSpace(item) == "omitempty" {
						options.Omitempty = true
						break
					}
				}
			}
		}
	}
	// 3. Получаем поле для экспорта внутреннего значения, если оно указано
	options.Export = strings.TrimSpace(field.Tag.Get("export"))
	// 4. Получаем указания для группысвнутренней финализации
	options.Group = strings.TrimSpace(field.Tag.Get("bygroup"))
	// 5. Получаем данные для условий исключения
	for _, exclude := range strings.Split(strings.TrimSpace(field.Tag.Get("exclude")), ";") {
		params := strings.Split(exclude, ":")
		if len(params) > 1 {
			switch strings.ToLower(strings.TrimSpace(params[0])) {
			case "equal":
				options.SetExcludeConditions(strings.TrimSpace(params[1]), excludeCmEqual)
				break
			default:
				break
			}
		}
	}
	return options
}

// Finalization of objects for a particular data model output to the serializer.
func Finalize(encTagName string, v interface{}, groups ...string) interface{} {
	// TODO: Оптимизировать в будущем
	val := reflect.Indirect(reflect.ValueOf(v))
	kind := val.Type().Kind()
	if kind == reflect.Array || kind == reflect.Slice {
		list := make([]interface{}, val.Len(), val.Len())
		for i := 0; i < val.Len(); i++ {
			if elem := val.Index(i); elem.IsValid() {
				list[i] = Finalize(encTagName, elem.Interface(), groups...)
				continue
			}
			list[i] = nil
		}
		return list
	} else if kind == reflect.Struct {
		t := val.Type()
		if !t.AssignableTo(timeType) && t.Name() != "Time" {
			m := make(just.H)
			// Перебираем поля и проверяем по группам
			for i := 0; i < t.NumField(); i++ {
				if field := t.Field(i); !field.Anonymous {
					if options := parseFieldTags(encTagName, &field, groups...); !options.Skip {
						fieldVal := val.Field(i)
						if options.Omitempty && isEmptyValue(fieldVal) {
							continue
						}
						if options.ExcludeConditions != nil && len(options.ExcludeConditions) > 0 {
							excluded := false
							for toFieldName, opID := range options.ExcludeConditions {
								if toFieldType, ok := t.FieldByName(toFieldName); ok && !toFieldType.Anonymous {
									// Сравнение значений
									if opID == excludeCmEqual {
										if toFieldVal := val.FieldByName(toFieldName); toFieldVal.IsValid() {
											if reflect.DeepEqual(fieldVal.Interface(), toFieldVal.Interface()) {
												excluded = true
												break
											}
										}
									}
								}
							}
							if excluded {
								continue
							}
						}
						if len(options.Export) > 0 {
							if indFieldVal := reflect.Indirect(fieldVal); indFieldVal.IsValid() {
								expKind := indFieldVal.Type().Kind()
								if expKind == reflect.Struct {
									if subField, ok := indFieldVal.Type().FieldByName(options.Export); ok {
										subFieldVal := indFieldVal.FieldByName(options.Export)
										if options.Omitempty && isEmptyValue(subFieldVal) {
											continue
										}
										if !subField.Anonymous && checkExportedInterface(subFieldVal) {
											if len(options.Group) > 0 {
												m[options.Name] = Finalize(encTagName, subFieldVal.Interface(), options.Group)
											} else {
												m[options.Name] = Finalize(encTagName, subFieldVal.Interface(), groups...)
											}
										} else if !options.Omitempty {
											m[options.Name] = nil
										}
									}
								} else if expKind == reflect.Slice || expKind == reflect.Array {
									arr := make([]interface{}, 0)
									for index := 0; index < indFieldVal.Len(); index++ {
										if itemVal := reflect.Indirect(indFieldVal.Index(index)); itemVal.Type().Kind() == reflect.Struct {
											if subField, ok := itemVal.Type().FieldByName(options.Export); ok {
												subFieldVal := itemVal.FieldByName(options.Export)
												if options.Omitempty && isEmptyValue(subFieldVal) {
													continue
												}
												if !subField.Anonymous && checkExportedInterface(subFieldVal) {
													if len(options.Group) > 0 {
														arr = append(arr, Finalize(encTagName, subFieldVal.Interface(), options.Group))
													} else {
														arr = append(arr, Finalize(encTagName, subFieldVal.Interface(), groups...))
													}
												}
											}
										}
									}
									if len(arr) > 0 || options.Omitempty {
										m[options.Name] = arr
									}
								}
							}
							continue
						}
						if checkExportedInterface(fieldVal) {
							if len(options.Group) > 0 {
								m[options.Name] = Finalize(encTagName, fieldVal.Interface(), options.Group)
							} else {
								m[options.Name] = Finalize(encTagName, fieldVal.Interface(), groups...)
							}
						} else if !options.Omitempty {
							m[options.Name] = nil
						}
					}
				}
			}
			return m
		}
	}
	return v
}
<|endoftext|>


<commit_before>package composition

import (
	"errors"
	"github.com/stretchr/testify/assert"
	"testing"
)

func Test_ContentMerge_PositiveCase(t *testing.T) {
	a := assert.New(t)

	expected := `<!DOCTYPE html>
<html>
  <head>
    <page1-head/>
    <page2-head/>
    <page3-head/>
  </head>
  <body a="b" foo="bar">
    <page1-body-main>
      <page2-body-a/>
      <page2-body-b/>
      <page3-body-a/>
    </page1-body-main>
    <page1-tail/>
    <page2-tail/>
  </body>
</html>
`

	body := StringFragment(
		`<page1-body-main>
      §[> page2-a]§
      §[> example.com#page2-b]§
      §[> page3-a]§
    </page1-body-main>
`)

	cm := NewContentMerge(nil)

	cm.AddContent(&MemoryContent{
		name:           "example.com",
		head:           StringFragment("<page1-head/>\n"),
		bodyAttributes: StringFragment(`a="b"`),
		tail:           StringFragment("    <page1-tail/>\n"),
		body:           map[string]Fragment{"": body},
	}, 0)

	page2 := NewMemoryContent()
	page2.name = "example.com"
	page2.head = StringFragment("    <page2-head/>\n")
	page2.bodyAttributes = StringFragment(`foo="bar"`)
	page2.tail = StringFragment("    <page2-tail/>")
	page2.body["page2-a"] = StringFragment("<page2-body-a/>")
	page2.body["page2-b"] = StringFragment("<page2-body-b/>")

	page3 := NewMemoryContent()
	page3.name = "example.com"
	page3.head = StringFragment("    <page3-head/>")
	page3.body["page3-a"] = StringFragment("<page3-body-a/>")

	cm.AddContent(page2, 0)
	cm.AddContent(page3, 0)

	html, err := cm.GetHtml()
	a.NoError(err)
	a.Equal(expected, string(html))
}

func Test_ContentMerge_BodyCompositionWithExplicitNames(t *testing.T) {
	a := assert.New(t)

	expected := `<!DOCTYPE html>
<html>
  <head>

  </head>
  <body>
    <page1-body-main>
      <page2-body-a/>
      <page2-body-b/>
      <page3-body-a/>
    </page1-body-main>
  </body>
</html>
`

	page1 := NewMemoryContent()
	page1.name = LayoutFragmentName
	page1.body[""] = StringFragment(
		`<page1-body-main>
      §[> page2-a]§
      §[> example.com#page2-b]§
      §[> page3-a]§
    </page1-body-main>`)

	page2 := NewMemoryContent()
	page2.name = "example.com"
	page2.body["page2-a"] = StringFragment("<page2-body-a/>")
	page2.body["page2-b"] = StringFragment("<page2-body-b/>")

	page3 := NewMemoryContent()
	page3.name = "example.com"
	page3.body["page3-a"] = StringFragment("<page3-body-a/>")

	cm := NewContentMerge(nil)
	cm.AddContent(page1, 0)
	cm.AddContent(page2, 0)
	cm.AddContent(page3, 0)

	html, err := cm.GetHtml()
	a.NoError(err)
	a.Equal(expected, string(html))
}

func Test_ContentMerge_LookupByDifferentFragmentNames(t *testing.T) {
	a := assert.New(t)

	fragmentA := StringFragment("a")
	fragmentB := StringFragment("b")

	c := NewMemoryContent()
	c.name = "main"
	c.body[""] = fragmentA
	c.body["b"] = fragmentB

	cm := NewContentMerge(nil)
	cm.AddContent(c, 0)

	// fragment a
	f, exist := cm.GetBodyFragmentByName("")
	a.True(exist)
	a.Equal(fragmentA, f)

	f, exist = cm.GetBodyFragmentByName("main")
	a.True(exist)
	a.Equal(fragmentA, f)

	f, exist = cm.GetBodyFragmentByName("main#")
	a.True(exist)
	a.Equal(fragmentA, f)

	f, exist = cm.GetBodyFragmentByName("#")
	a.True(exist)
	a.Equal(fragmentA, f)

	// fragment b
	f, exist = cm.GetBodyFragmentByName("b")
	a.True(exist)
	a.Equal(fragmentB, f)

	f, exist = cm.GetBodyFragmentByName("main#b")
	a.True(exist)
	a.Equal(fragmentB, f)

	f, exist = cm.GetBodyFragmentByName("#b")
	a.True(exist)
	a.Equal(fragmentB, f)
}

func Test_GenerateMissingFragmentString(t *testing.T) {
	body := map[string]Fragment{
		"footer": nil,
		"header": nil,
		"":       nil,
	}
	fragmentName := "body"
	fragmentString := generateMissingFragmentString(body, fragmentName)

	a := assert.New(t)
	a.Contains(fragmentString, "Fragment does not exist: body.")
	a.Contains(fragmentString, "footer")
	a.Contains(fragmentString, "header")

}

func Test_ContentMerge_MainFragmentDoesNotExist(t *testing.T) {
	a := assert.New(t)
	cm := NewContentMerge(nil)
	_, err := cm.GetHtml()
	a.Error(err)
	a.Equal("Fragment does not exist: . Existing fragments: ", err.Error())
}

type closedWriterMock struct {
}

func (buff closedWriterMock) Write(b []byte) (int, error) {
	return 0, errors.New("writer closed")
}

func asFetchResult(c Content) *FetchResult {
	return &FetchResult{Content: c, Def: &FetchDefinition{URL: c.Name()}}
}
<commit_msg>overworked tests<commit_after>package composition

import (
	"errors"
	"github.com/stretchr/testify/assert"
	"testing"
)

func Test_ContentMerge_PositiveCase(t *testing.T) {
	a := assert.New(t)

	expected := `<!DOCTYPE html>
<html>
  <head>
    <page1-head/>
    <page2-head/>
    <page3-head/>
  </head>
  <body a="b" foo="bar">
    <page1-body-main>
      <page2-body-a/>
      <page2-body-b/>
      <page3-body-a/>
    </page1-body-main>
    <page1-tail/>
    <page2-tail/>
  </body>
</html>
`

	body := StringFragment(
		`<page1-body-main>
      §[> page2-a]§
      §[> example.com#page2-b]§
      §[> page3]§
    </page1-body-main>
`)

	cm := NewContentMerge(nil)

	cm.AddContent(&MemoryContent{
		name:           LayoutFragmentName,
		head:           StringFragment("<page1-head/>\n"),
		bodyAttributes: StringFragment(`a="b"`),
		tail:           StringFragment("    <page1-tail/>\n"),
		body:           map[string]Fragment{"": body},
	}, 0)

	cm.AddContent(&MemoryContent{
		name:           "example.com",
		head:           StringFragment("    <page2-head/>\n"),
		bodyAttributes: StringFragment(`foo="bar"`),
		tail:           StringFragment("    <page2-tail/>"),
		body: map[string]Fragment{
			"page2-a": StringFragment("<page2-body-a/>"),
			"page2-b": StringFragment("<page2-body-b/>"),
		}}, 0)

	cm.AddContent(&MemoryContent{
		name: "page3",
		head: StringFragment("    <page3-head/>"),
		body: map[string]Fragment{
			"": StringFragment("<page3-body-a/>"),
		}}, 0)

	html, err := cm.GetHtml()
	a.NoError(err)
	a.Equal(expected, string(html))
}

func Test_ContentMerge_BodyCompositionWithExplicitNames(t *testing.T) {
	a := assert.New(t)

	expected := `<!DOCTYPE html>
<html>
  <head>

  </head>
  <body>
    <page1-body-main>
      <page2-body-a/>
      <page2-body-b/>
      <page3-body-a/>
    </page1-body-main>
  </body>
</html>
`

	cm := NewContentMerge(nil)

	cm.AddContent(&MemoryContent{
		name: LayoutFragmentName,
		body: map[string]Fragment{
			"": StringFragment(
				`<page1-body-main>
      §[> page2-a]§
      §[> example1.com#page2-b]§
      §[> page3-a]§
    </page1-body-main>`)}}, 0)

	cm.AddContent(&MemoryContent{
		name: "example1.com",
		body: map[string]Fragment{
			"page2-a": StringFragment("<page2-body-a/>"),
			"page2-b": StringFragment("<page2-body-b/>"),
		}}, 0)

	cm.AddContent(&MemoryContent{
		name: "example2.com",
		body: map[string]Fragment{
			"page3-a": StringFragment("<page3-body-a/>"),
		}}, 0)

	html, err := cm.GetHtml()
	a.NoError(err)
	a.Equal(expected, string(html))
}

func Test_ContentMerge_LookupByDifferentFragmentNames(t *testing.T) {
	a := assert.New(t)

	fragmentA := StringFragment("a")
	fragmentB := StringFragment("b")

	cm := NewContentMerge(nil)
	cm.AddContent(&MemoryContent{
		name: "main",
		body: map[string]Fragment{
			"":  fragmentA,
			"b": fragmentB,
		}}, 0)

	// fragment a
	f, exist := cm.GetBodyFragmentByName("")
	a.True(exist)
	a.Equal(fragmentA, f)

	f, exist = cm.GetBodyFragmentByName("main")
	a.True(exist)
	a.Equal(fragmentA, f)

	f, exist = cm.GetBodyFragmentByName("main#")
	a.True(exist)
	a.Equal(fragmentA, f)

	f, exist = cm.GetBodyFragmentByName("#")
	a.True(exist)
	a.Equal(fragmentA, f)

	// fragment b
	f, exist = cm.GetBodyFragmentByName("b")
	a.True(exist)
	a.Equal(fragmentB, f)

	f, exist = cm.GetBodyFragmentByName("main#b")
	a.True(exist)
	a.Equal(fragmentB, f)

	f, exist = cm.GetBodyFragmentByName("#b")
	a.True(exist)
	a.Equal(fragmentB, f)
}

func Test_GenerateMissingFragmentString(t *testing.T) {
	body := map[string]Fragment{
		"footer": nil,
		"header": nil,
		"":       nil,
	}
	fragmentName := "body"
	fragmentString := generateMissingFragmentString(body, fragmentName)

	a := assert.New(t)
	a.Contains(fragmentString, "Fragment does not exist: body.")
	a.Contains(fragmentString, "footer")
	a.Contains(fragmentString, "header")

}

func Test_ContentMerge_MainFragmentDoesNotExist(t *testing.T) {
	a := assert.New(t)
	cm := NewContentMerge(nil)
	_, err := cm.GetHtml()
	a.Error(err)
	a.Equal("Fragment does not exist: . Existing fragments: ", err.Error())
}

type closedWriterMock struct {
}

func (buff closedWriterMock) Write(b []byte) (int, error) {
	return 0, errors.New("writer closed")
}

func asFetchResult(c Content) *FetchResult {
	return &FetchResult{Content: c, Def: &FetchDefinition{URL: c.Name()}}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"time"

	"github.com/justincampbell/forecast/v2"
)

var conditionIcons = map[string]string{
	"clear-day":           "☀",
	"clear-night":         "🌙",
	"cloudy":              "☁",
	"fog":                 "fog",
	"partly-cloudy-day":   "⛅",
	"partly-cloudy-night": "🌙",
	"rain":                "☔",
	"sleet":               "❄ ☔",
	"snow":                "❄",
	"wind":                "🍃",
}

var maxCacheAge, _ = time.ParseDuration("1h")
var cache_file = path.Join(os.TempDir(), "emoji-weather.json")

func main() {
	var json []byte
	var err error

	if isCacheStale(cache_file) {
		json, err = getForecast()
		check(err)

		err = writeCache(cache_file, json)
		check(err)
	} else {
		json, err = ioutil.ReadFile(cache_file)
		check(err)
	}

	fmt.Println(formatConditions(extractConditionFromJSON(json)))
}

func isCacheStale(cache_file string) bool {
	stat, err := os.Stat(cache_file)

	return os.IsNotExist(err) || time.Since(stat.ModTime()) > maxCacheAge
}

func getForecast() (json []byte, err error) {
	key := os.Getenv("FORECAST_IO_API_KEY")

	if key == "" {
		log.Fatal("Please set FORECAST_IO_API_KEY to your forecast.io API key")
	}

	lat := "39.95"
	long := "-75.1667"

	res, err := forecast.GetResponse(key, lat, long, "now", "us")
	if err != nil {
		return nil, err
	}

	json, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	return json, nil
}

func writeCache(cache_file string, json []byte) (err error) {
	return ioutil.WriteFile(cache_file, json, 0644)
}

func formatConditions(condition string) (icon string) {
	icon, ok := conditionIcons[condition]
	if !ok {
		icon = condition
	}
	return
}

func extractConditionFromJSON(jsonBlob []byte) (condition string) {
	f, err := forecast.FromJSON(jsonBlob)
	if err != nil {
		return "❗"
	}

	return f.Currently.Icon
}

func check(err error) {
	if err != nil {
		panic(err)
	}
}
<commit_msg>Allow coordinates to be passed in with -coordinate<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"strings"
	"time"

	"github.com/justincampbell/forecast/v2"
)

var conditionIcons = map[string]string{
	"clear-day":           "☀",
	"clear-night":         "🌙",
	"cloudy":              "☁",
	"fog":                 "fog",
	"partly-cloudy-day":   "⛅",
	"partly-cloudy-night": "🌙",
	"rain":                "☔",
	"sleet":               "❄ ☔",
	"snow":                "❄",
	"wind":                "🍃",
}

var maxCacheAge, _ = time.ParseDuration("1h")
var cache_file = path.Join(os.TempDir(), "emoji-weather.json")

func main() {
	coordinates := flag.String("coordinates", "39.95,-75.1667", "the coordinates, expressed as latitude,longitude")
	flag.Parse()

	coordinateParts := strings.Split(*coordinates, ",")
	var latitude string
	var longitude string

	if len(coordinateParts) != 2 {
		exitWith("You must specify latitude and longitude like so: 39.95,-75.1667", 1)
	} else {
		latitude = coordinateParts[0]
		longitude = coordinateParts[1]
	}

	var json []byte
	var err error

	if isCacheStale(cache_file) {
		json, err = getForecast(latitude, longitude)
		check(err)

		err = writeCache(cache_file, json)
		check(err)
	} else {
		json, err = ioutil.ReadFile(cache_file)
		check(err)
	}

	fmt.Println(formatConditions(extractConditionFromJSON(json)))
}

func isCacheStale(cache_file string) bool {
	stat, err := os.Stat(cache_file)

	return os.IsNotExist(err) || time.Since(stat.ModTime()) > maxCacheAge
}

func getForecast(latitude string, longitude string) (json []byte, err error) {
	key := os.Getenv("FORECAST_IO_API_KEY")

	if key == "" {
		log.Fatal("Please set FORECAST_IO_API_KEY to your forecast.io API key")
	}

	res, err := forecast.GetResponse(key, latitude, longitude, "now", "us")
	if err != nil {
		return nil, err
	}

	json, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	return json, nil
}

func writeCache(cache_file string, json []byte) (err error) {
	return ioutil.WriteFile(cache_file, json, 0644)
}

func formatConditions(condition string) (icon string) {
	icon, ok := conditionIcons[condition]
	if !ok {
		icon = condition
	}
	return
}

func extractConditionFromJSON(jsonBlob []byte) (condition string) {
	f, err := forecast.FromJSON(jsonBlob)
	if err != nil {
		return "❗"
	}

	return f.Currently.Icon
}

func exitWith(message interface{}, status int) {
	fmt.Printf("%v\n", message)
	os.Exit(status)
}

func check(err error) {
	if err != nil {
		exitWith(err, 1)
	}
}
<|endoftext|>


<commit_before>package vlc

// #cgo LDFLAGS: -lvlc
// #include <vlc/vlc.h>
// #include <stdlib.h>
import "C"
import (
	"errors"
	"unsafe"
)

// Player is a media player used to play a single media file.
// For playing media lists (playlists) use ListPlayer instead.
type Player struct {
	player *C.libvlc_media_player_t
}

// NewPlayer creates an instance of a single-media player.
func NewPlayer() (*Player, error) {
	if instance == nil {
		return nil, errors.New("Module must be initialized first")
	}

	if player := C.libvlc_media_player_new(instance); player != nil {
		return &Player{player: player}, nil
	}

	return nil, getError()
}

// Release destroys the media player instance.
func (p *Player) Release() error {
	if p.player == nil {
		return nil
	}

	C.libvlc_media_player_release(p.player)
	p.player = nil

	return getError()
}

// Play plays the current media.
func (p *Player) Play() error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}
	if p.IsPlaying() {
		return nil
	}

	if C.libvlc_media_player_play(p.player) < 0 {
		return getError()
	}

	return nil
}

// IsPlaying returns a boolean value specifying if the player is currently
// playing.
func (p *Player) IsPlaying() bool {
	if p.player == nil {
		return false
	}

	return C.libvlc_media_player_is_playing(p.player) != 0
}

// Stop cancels the currently playing media, if there is one.
func (p *Player) Stop() error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}
	if !p.IsPlaying() {
		return nil
	}

	C.libvlc_media_player_stop(p.player)
	return getError()
}

// SetPause sets the pause state of the media player.
// Pass in true to pause the current media, or false to resume it.
func (p *Player) SetPause(pause bool) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_media_player_set_pause(p.player, C.int(boolToInt(pause)))
	return getError()
}

// TogglePause pauses/resumes the player.
// Calling this method has no effect if there is no media.
func (p *Player) TogglePause() error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_media_player_pause(p.player)
	return getError()
}

// SetFullScreen sets the fullscreen state of the media player.
// Pass in true to enable fullscreen, or false to disable it.
func (p *Player) SetFullScreen(fullscreen bool) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_set_fullscreen(p.player, C.int(boolToInt(fullscreen)))
	return getError()
}

// ToggleFullScreen toggles the fullscreen status of the player,
// on non-embedded video outputs.
func (p *Player) ToggleFullScreen() error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_toggle_fullscreen(p.player)
	return getError()
}

// IsFullScreen gets the fullscreen status of the current player.
func (p *Player) IsFullScreen() (bool, error) {
	if p.player == nil {
		return false, errors.New("A player must be initialized first")
	}

	return (C.libvlc_get_fullscreen(p.player) != C.int(0)), getError()
}

// Volume returns the volume of the player.
func (p *Player) Volume() (int, error) {
	if p.player == nil {
		return 0, errors.New("A player must be initialized first")
	}

	return int(C.libvlc_audio_get_volume(p.player)), getError()
}

// SetVolume sets the volume of the player.
func (p *Player) SetVolume(volume int) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_audio_set_volume(p.player, C.int(volume))
	return getError()
}

// Media returns the current media of the player, if one exists.
func (p *Player) Media() (*Media, error) {
	if p.player == nil {
		return nil, errors.New("A player must be initialized first")
	}

	media := C.libvlc_media_player_get_media(p.player)
	if media == nil {
		return nil, nil
	}

	// This call will not release the media. Instead, it will decrement
	// the reference count increased by libvlc_media_player_get_media.
	C.libvlc_media_release(media)

	return &Media{media}, nil
}

// SetMedia sets the provided media as the current media of the player.
func (p *Player) SetMedia(m *Media) error {
	return p.setMedia(m)
}

// LoadMediaFromPath loads the media from the specified path and adds it as the
// current media of the player.
func (p *Player) LoadMediaFromPath(path string) (*Media, error) {
	return p.loadMedia(path, true)
}

// LoadMediaFromURL loads the media from the specified URL and adds it as the
// current media of the player.
func (p *Player) LoadMediaFromURL(url string) (*Media, error) {
	return p.loadMedia(url, false)
}

// SetAudioOutput selects an audio output module.
// Any change will take be effect only after playback is stopped and restarted.
// Audio output cannot be changed while playing.
func (p *Player) SetAudioOutput(output string) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	cOutput := C.CString(output)
	defer C.free(unsafe.Pointer(cOutput))

	if C.libvlc_audio_output_set(p.player, cOutput) != 0 {
		return getError()
	}

	return nil
}

// MediaLength returns media length in milliseconds.
func (p *Player) MediaLength() (int, error) {
	if p.player == nil {
		return 0, errors.New("A player must be initialized first")
	}

	return int(C.libvlc_media_player_get_length(p.player)), getError()
}

// MediaState returns the state of the current media.
func (p *Player) MediaState() (MediaState, error) {
	if p.player == nil {
		return 0, errors.New("A player must be initialized first")
	}

	state := int(C.libvlc_media_player_get_state(p.player))
	return MediaState(state), getError()
}

// MediaPosition returns media position as a
// float percentage between 0.0 and 1.0.
func (p *Player) MediaPosition() (float32, error) {
	if p.player == nil {
		return 0, errors.New("A player must be initialized first")
	}

	return float32(C.libvlc_media_player_get_position(p.player)), getError()
}

// SetMediaPosition sets media position as percentage between 0.0 and 1.0.
// Some formats and protocols do not support this.
func (p *Player) SetMediaPosition(pos float32) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_media_player_set_position(p.player, C.float(pos))
	return getError()
}

// MediaTime returns media time in milliseconds.
func (p *Player) MediaTime() (int, error) {
	if p.player == nil {
		return 0, errors.New("A player must be initialized first")
	}

	return int(C.libvlc_media_player_get_time(p.player)), getError()
}

// SetMediaTime sets the media time in milliseconds.
// Some formats and protocals do not support this.
func (p *Player) SetMediaTime(t int) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_media_player_set_time(p.player, C.libvlc_time_t(int64(t)))
	return getError()
}

// WillPlay returns true if the current media is not in a
// finished or error state.
func (p *Player) WillPlay() bool {
	if p.player == nil {
		return false
	}

	return C.libvlc_media_player_will_play(p.player) != 0
}

func (p *Player) loadMedia(path string, local bool) (*Media, error) {
	m, err := newMedia(path, local)
	if err != nil {
		return nil, err
	}

	if err = p.setMedia(m); err != nil {
		m.Release()
		return nil, err
	}

	return m, nil
}

func (p *Player) setMedia(m *Media) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}
	if m.media == nil {
		return errors.New("The media must be initialized first")
	}

	C.libvlc_media_player_set_media(p.player, m.media)
	return getError()
}
<commit_msg>Add libvlc_media_player_set_xwindow<commit_after>package vlc

// #cgo LDFLAGS: -lvlc
// #include <vlc/vlc.h>
// #include <stdlib.h>
import "C"
import (
	"errors"
	"unsafe"
)

// Player is a media player used to play a single media file.
// For playing media lists (playlists) use ListPlayer instead.
type Player struct {
	player *C.libvlc_media_player_t
}

// NewPlayer creates an instance of a single-media player.
func NewPlayer() (*Player, error) {
	if instance == nil {
		return nil, errors.New("Module must be initialized first")
	}

	if player := C.libvlc_media_player_new(instance); player != nil {
		return &Player{player: player}, nil
	}

	return nil, getError()
}

// Release destroys the media player instance.
func (p *Player) Release() error {
	if p.player == nil {
		return nil
	}

	C.libvlc_media_player_release(p.player)
	p.player = nil

	return getError()
}

// Play plays the current media.
func (p *Player) Play() error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}
	if p.IsPlaying() {
		return nil
	}

	if C.libvlc_media_player_play(p.player) < 0 {
		return getError()
	}

	return nil
}

// IsPlaying returns a boolean value specifying if the player is currently
// playing.
func (p *Player) IsPlaying() bool {
	if p.player == nil {
		return false
	}

	return C.libvlc_media_player_is_playing(p.player) != 0
}

// Stop cancels the currently playing media, if there is one.
func (p *Player) Stop() error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}
	if !p.IsPlaying() {
		return nil
	}

	C.libvlc_media_player_stop(p.player)
	return getError()
}

// SetPause sets the pause state of the media player.
// Pass in true to pause the current media, or false to resume it.
func (p *Player) SetPause(pause bool) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_media_player_set_pause(p.player, C.int(boolToInt(pause)))
	return getError()
}

// TogglePause pauses/resumes the player.
// Calling this method has no effect if there is no media.
func (p *Player) TogglePause() error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_media_player_pause(p.player)
	return getError()
}

// SetFullScreen sets the fullscreen state of the media player.
// Pass in true to enable fullscreen, or false to disable it.
func (p *Player) SetFullScreen(fullscreen bool) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_set_fullscreen(p.player, C.int(boolToInt(fullscreen)))
	return getError()
}

// ToggleFullScreen toggles the fullscreen status of the player,
// on non-embedded video outputs.
func (p *Player) ToggleFullScreen() error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_toggle_fullscreen(p.player)
	return getError()
}

// IsFullScreen gets the fullscreen status of the current player.
func (p *Player) IsFullScreen() (bool, error) {
	if p.player == nil {
		return false, errors.New("A player must be initialized first")
	}

	return (C.libvlc_get_fullscreen(p.player) != C.int(0)), getError()
}

// Volume returns the volume of the player.
func (p *Player) Volume() (int, error) {
	if p.player == nil {
		return 0, errors.New("A player must be initialized first")
	}

	return int(C.libvlc_audio_get_volume(p.player)), getError()
}

// SetVolume sets the volume of the player.
func (p *Player) SetVolume(volume int) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_audio_set_volume(p.player, C.int(volume))
	return getError()
}

// Media returns the current media of the player, if one exists.
func (p *Player) Media() (*Media, error) {
	if p.player == nil {
		return nil, errors.New("A player must be initialized first")
	}

	media := C.libvlc_media_player_get_media(p.player)
	if media == nil {
		return nil, nil
	}

	// This call will not release the media. Instead, it will decrement
	// the reference count increased by libvlc_media_player_get_media.
	C.libvlc_media_release(media)

	return &Media{media}, nil
}

// SetMedia sets the provided media as the current media of the player.
func (p *Player) SetMedia(m *Media) error {
	return p.setMedia(m)
}

// LoadMediaFromPath loads the media from the specified path and adds it as the
// current media of the player.
func (p *Player) LoadMediaFromPath(path string) (*Media, error) {
	return p.loadMedia(path, true)
}

// LoadMediaFromURL loads the media from the specified URL and adds it as the
// current media of the player.
func (p *Player) LoadMediaFromURL(url string) (*Media, error) {
	return p.loadMedia(url, false)
}

// SetAudioOutput selects an audio output module.
// Any change will take be effect only after playback is stopped and restarted.
// Audio output cannot be changed while playing.
func (p *Player) SetAudioOutput(output string) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	cOutput := C.CString(output)
	defer C.free(unsafe.Pointer(cOutput))

	if C.libvlc_audio_output_set(p.player, cOutput) != 0 {
		return getError()
	}

	return nil
}

// MediaLength returns media length in milliseconds.
func (p *Player) MediaLength() (int, error) {
	if p.player == nil {
		return 0, errors.New("A player must be initialized first")
	}

	return int(C.libvlc_media_player_get_length(p.player)), getError()
}

// MediaState returns the state of the current media.
func (p *Player) MediaState() (MediaState, error) {
	if p.player == nil {
		return 0, errors.New("A player must be initialized first")
	}

	state := int(C.libvlc_media_player_get_state(p.player))
	return MediaState(state), getError()
}

// MediaPosition returns media position as a
// float percentage between 0.0 and 1.0.
func (p *Player) MediaPosition() (float32, error) {
	if p.player == nil {
		return 0, errors.New("A player must be initialized first")
	}

	return float32(C.libvlc_media_player_get_position(p.player)), getError()
}

// SetMediaPosition sets media position as percentage between 0.0 and 1.0.
// Some formats and protocols do not support this.
func (p *Player) SetMediaPosition(pos float32) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_media_player_set_position(p.player, C.float(pos))
	return getError()
}

// MediaTime returns media time in milliseconds.
func (p *Player) MediaTime() (int, error) {
	if p.player == nil {
		return 0, errors.New("A player must be initialized first")
	}

	return int(C.libvlc_media_player_get_time(p.player)), getError()
}

// SetMediaTime sets the media time in milliseconds.
// Some formats and protocals do not support this.
func (p *Player) SetMediaTime(t int) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_media_player_set_time(p.player, C.libvlc_time_t(int64(t)))
	return getError()
}

// WillPlay returns true if the current media is not in a
// finished or error state.
func (p *Player) WillPlay() bool {
	if p.player == nil {
		return false
	}

	return C.libvlc_media_player_will_play(p.player) != 0
}

func (p *Player) loadMedia(path string, local bool) (*Media, error) {
	m, err := newMedia(path, local)
	if err != nil {
		return nil, err
	}

	if err = p.setMedia(m); err != nil {
		m.Release()
		return nil, err
	}

	return m, nil
}

func (p *Player) setMedia(m *Media) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}
	if m.media == nil {
		return errors.New("The media must be initialized first")
	}

	C.libvlc_media_player_set_media(p.player, m.media)
	return getError()
}

func (p *Player) setXWindow(uint32 XID) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}
	C.libvlc_media_player_set_xwindow(XID)
	return getError()
}
<|endoftext|>


<commit_before>package engine

import (
	"math/rand"
	"sort"
	"time"
)

type Dice struct {
	randSeed int64
	dice     []int
}

func NewDice(count int) *Dice {
	return &Dice{
		randSeed: time.Now().UnixNano(),
		dice:     make([]int, count),
	}
}

func (d *Dice) SetSeed(seed int64) {
	d.randSeed = seed
}

func (d *Dice) Roll() {
	r := rand.New(rand.NewSource(d.randSeed))
	for i, _ := range d.dice {
		d.dice[i] = r.Intn(5) + 1
	}
}

func (d *Dice) Score() []int {
	sort.Sort(sort.Reverse(sort.IntSlice(d.dice)))
	return d.dice
}

type Player struct {
	dice Dice
}

func (p *Player) RollDice() {

}
<commit_msg>Linting engine stuff<commit_after>package engine

import (
	"math/rand"
	"sort"
	"time"
)

// Dice are the group of one or more Die's
type Dice struct {
	randSeed int64
	dice     []int
}

//NewDice returns a new set of Dice, with (count) number of Die's
func NewDice(count int) *Dice {
	return &Dice{
		randSeed: time.Now().UnixNano(),
		dice:     make([]int, count),
	}
}

// SetSeed sets the RNG seed that determines how the bones fall
func (d *Dice) SetSeed(seed int64) {
	d.randSeed = seed
}

// Roll makes them bones roll
func (d *Dice) Roll() {
	r := rand.New(rand.NewSource(d.randSeed))
	for i := range d.dice {
		d.dice[i] = r.Intn(5) + 1
	}
}

//Score the Dice
func (d *Dice) Score() []int {
	sort.Sort(sort.Reverse(sort.IntSlice(d.dice)))
	return d.dice
}

//Player No prizes
type Player struct {
	dice Dice
	name string
}
<|endoftext|>