<commit_before>/*
file: nnc.go
author: alemedeiros <alexandre.n.medeiros _at_ gmail.com>

A n-sized noughts and crosses game library.

It is a generalization of noughts and crosses, with a n x n board.
To win, you have to fill a line, column or diagonal with your symbol.
*/

// Package nnc implements a n-sized noughts and crosses game.
package nnc

import "errors"

// Empty is an unplayed square;
// Cross is a 'X';
// Nought is a 'O';
const (
	Empty  byte = iota // 0
	Cross  byte = iota // X
	Nought byte = iota // O
)

// A Game is a game board, use New function to initialize a Game.
type Game struct {
	board      [][]byte
	size       int
	currPlayer byte
}

// New function Initializes a game structure with a sz-sized board.
// First player is always Cross.
func New(sz int) (g Game) {
	// Allocate a new Game structure
	g = Game{
		board:      make([][]byte, sz),
		size:       sz,
		currPlayer: Cross, // First player is Cross
	}

	for i := range g.board {
		g.board[i] = make([]byte, sz)
		for j := range g.board[i] {
			g.board[i][j] = Empty
		}
	}

	return
}

// Play function checks if the coordinates are inside the board and if it is the
// given player's turn.
//
// Return true and winner (Empty means draw) if the move ended the game.
func (g *Game) Play(x, y int, player byte) (done bool, winner byte, err error) {
	// Validation check
	if g.currPlayer != player {
		return false, Empty, errors.New("not player's turn")
	}
	if x < 0 || g.size <= x || y < 0 || g.size <= y {
		return false, Empty, errors.New("invalid position")
	}

	// Move is valid, do it!
	g.board[x][y] = player

	// Check if move ended the game
	if isDone, winner := g.isDone(); isDone {
		return true, winner, nil
	}

	// Update whose turn it is
	switch g.currPlayer {
	case Cross:
		g.currPlayer = Nought
	case Nought:
		g.currPlayer = Cross
	}

	return false, Empty, nil
}

// isDone function determines if the game is over, and if it is, its winner.
// If winner is Empty, the it was a draw.
func (g Game) isDone() (done bool, winner byte) {
	winner = Empty
	done = true

	// TODO: Check for winner

	// Check for draw
OuterFor:
	for i := range g.board {
		for _, p := range g.board[i] {
			if p == Empty {
				done = false
				break OuterFor
			}
		}
	}

	return
}

// Outcome calculates the outcome function for a player (Nought/Cross) for the
// current game.
func (g Game) Outcome(player byte) (sum int) {
	if player != Nought && player != Cross {
		return
	}

	for i, sz := 0, g.size; i < sz; i++ {
		// Lines
		linit, lsum := Empty, 0
		for j := 0; j < sz; j++ {
			// Empty squares don't change the outcome function.
			if g.board[i][j] == Empty {
				continue
			}

			// Initialize initial symbol.
			if linit == Empty {
				linit = g.board[i][j]
			}

			// Different symbols means line sum is 0.
			if g.board[i][j] != linit {
				lsum = 0
				break
			}

			if g.board[i][j] == player {
				lsum += 1 // Increment for player
			} else {
				lsum -= 1 // Decrement for opponent
			}
		}

		// Colums
		cinit, csum := Empty, 0
		for j := 0; j < sz; j++ {
			// Empty squares don't change the outcome function.
			if g.board[j][i] == Empty {
				continue
			}

			// Initialize initial symbol.
			if cinit == Empty {
				cinit = g.board[j][i]
			}

			// Different symbols means column sum is 0.
			if g.board[j][i] != cinit {
				csum = 0
				break
			}

			if g.board[j][i] == player {
				csum += 1 // Increment for player
			} else {
				csum -= 1 // Decrement for opponent
			}
		}
		sum += lsum + csum
	}

	// Diagonal
	dinit, dsum := Empty, 0
	for i, sz := 0, g.size; i < sz; i++ {

		// Empty squares don't change the outcome function.
		if g.board[i][i] == Empty {
			continue
		}

		// Initialize initial symbol.
		if dinit == Empty {
			dinit = g.board[i][i]
		}

		// Different symbols means diagonal sum is 0.
		if g.board[i][i] != dinit {
			dsum = 0
			break
		}

		if g.board[i][i] == player {
			dsum += 1 // Increment for player
		} else {
			dsum -= 1 // Decrement for opponent
		}
	}
	sum += dsum

	// Anti-Diagonal
	adinit, adsum := Empty, 0
	for i, sz := 0, g.size; i < sz; i++ {
		// Empty squares don't change the outcome function.
		if g.board[i][sz-1-i] == Empty {
			continue
		}

		// Initialize initial symbol.
		if adinit == Empty {
			adinit = g.board[i][sz-1-i]
		}

		// Different symbols means anti-diagonal sum is 0.
		if g.board[i][sz-1-i] != adinit {
			adsum = 0
			break
		}

		if g.board[i][sz-1-i] == player {
			adsum += 1 // Increment for player
		} else {
			adsum -= 1 // Decrement for opponent
		}
	}
	sum += adsum

	return
}
<commit_msg>create PlayAI function skeleton.<commit_after>/*
file: nnc.go
author: alemedeiros <alexandre.n.medeiros _at_ gmail.com>

A n-sized noughts and crosses game library.

It is a generalization of noughts and crosses, with a n x n board.
To win, you have to fill a line, column or diagonal with your symbol.
*/

// Package nnc implements a n-sized noughts and crosses game.
package nnc

import "errors"

// Empty is an unplayed square;
// Cross is a 'X';
// Nought is a 'O';
const (
	Empty  byte = iota // 0
	Cross  byte = iota // X
	Nought byte = iota // O
)

// A Game is a game board, use New function to initialize a Game.
type Game struct {
	board      [][]byte
	size       int
	currPlayer byte
}

// New function Initializes a game structure with a sz-sized board.
// First player is always Cross.
func New(sz int) (g Game) {
	// Allocate a new Game structure
	g = Game{
		board:      make([][]byte, sz),
		size:       sz,
		currPlayer: Cross, // First player is Cross
	}

	for i := range g.board {
		g.board[i] = make([]byte, sz)
		for j := range g.board[i] {
			g.board[i][j] = Empty
		}
	}

	return
}

// Play function checks if the coordinates are inside the board and if it is the
// given player's turn.
//
// Return true and winner (Empty means draw) if the move ended the game.
func (g *Game) Play(x, y int, player byte) (done bool, winner byte, err error) {
	// Validation check
	if g.currPlayer != player {
		return false, Empty, errors.New("not player's turn")
	}
	if x < 0 || g.size <= x || y < 0 || g.size <= y {
		return false, Empty, errors.New("invalid position")
	}

	// Move is valid, do it!
	g.board[x][y] = player

	// Check if move ended the game
	if isDone, winner := g.isDone(); isDone {
		return true, winner, nil
	}

	g.updateTurn()

	return false, Empty, nil
}

// PlayAI function checks if is the given player's turn, if so, it makes a move
// as that player.
//
// Return true and winner (Empty means draw) if the move ended the game.
func (g *Game) PlayAI(player byte) (done bool, winner byte, err error) {
	// Validation check
	if g.currPlayer != player {
		return false, Empty, errors.New("not player's turn")
	}

	// TODO: AI code here

	g.updateTurn()

	return false, Empty, nil
}

// updateTurn function updates whose turn it is.
//
// Assumes the turn was correctly set before call.
func (g *Game) updateTurn() error {
	switch g.currPlayer {
	case Cross:
		g.currPlayer = Nought
	case Nought:
		g.currPlayer = Cross
	default:
		return errors.New("invalid player turn value")
	}

	return nil
}

// isDone function determines if the game is over, and if it is, its winner.
// If winner is Empty, the it was a draw.
func (g Game) isDone() (done bool, winner byte) {
	winner = Empty
	done = true

	// TODO: Check for winner

	// Check for draw
outerFor:
	for i := range g.board {
		for _, p := range g.board[i] {
			if p == Empty {
				done = false
				break outerFor
			}
		}
	}

	return
}

// Outcome calculates the outcome function for a player (Nought/Cross) for the
// current game.
func (g Game) Outcome(player byte) (sum int) {
	if player != Nought && player != Cross {
		return
	}

	for i, sz := 0, g.size; i < sz; i++ {
		// Lines
		linit, lsum := Empty, 0
		for j := 0; j < sz; j++ {
			// Empty squares don't change the outcome function.
			if g.board[i][j] == Empty {
				continue
			}

			// Initialize initial symbol.
			if linit == Empty {
				linit = g.board[i][j]
			}

			// Different symbols means line sum is 0.
			if g.board[i][j] != linit {
				lsum = 0
				break
			}

			if g.board[i][j] == player {
				lsum += 1 // Increment for player
			} else {
				lsum -= 1 // Decrement for opponent
			}
		}

		// Colums
		cinit, csum := Empty, 0
		for j := 0; j < sz; j++ {
			// Empty squares don't change the outcome function.
			if g.board[j][i] == Empty {
				continue
			}

			// Initialize initial symbol.
			if cinit == Empty {
				cinit = g.board[j][i]
			}

			// Different symbols means column sum is 0.
			if g.board[j][i] != cinit {
				csum = 0
				break
			}

			if g.board[j][i] == player {
				csum += 1 // Increment for player
			} else {
				csum -= 1 // Decrement for opponent
			}
		}
		sum += lsum + csum
	}

	// Diagonal
	dinit, dsum := Empty, 0
	for i, sz := 0, g.size; i < sz; i++ {

		// Empty squares don't change the outcome function.
		if g.board[i][i] == Empty {
			continue
		}

		// Initialize initial symbol.
		if dinit == Empty {
			dinit = g.board[i][i]
		}

		// Different symbols means diagonal sum is 0.
		if g.board[i][i] != dinit {
			dsum = 0
			break
		}

		if g.board[i][i] == player {
			dsum += 1 // Increment for player
		} else {
			dsum -= 1 // Decrement for opponent
		}
	}
	sum += dsum

	// Anti-Diagonal
	adinit, adsum := Empty, 0
	for i, sz := 0, g.size; i < sz; i++ {
		// Empty squares don't change the outcome function.
		if g.board[i][sz-1-i] == Empty {
			continue
		}

		// Initialize initial symbol.
		if adinit == Empty {
			adinit = g.board[i][sz-1-i]
		}

		// Different symbols means anti-diagonal sum is 0.
		if g.board[i][sz-1-i] != adinit {
			adsum = 0
			break
		}

		if g.board[i][sz-1-i] == player {
			adsum += 1 // Increment for player
		} else {
			adsum -= 1 // Decrement for opponent
		}
	}
	sum += adsum

	return
}
<|endoftext|>


<commit_before>package rogue

import "fmt"

type Point struct {
	X uint16
	Y uint16
}

type WorldObject interface {
	Tick()
	Defaults()
}

const (
	DoingNothing = 0
	DoingSleeping
	DoingEating
	DoingDrinking
	DoingMoving
	DoingForaging
)

type Npc struct {
	Level          int
	XP             int
	Age            int
	Name           string
	Position       Point
	CurrentlyDoing int

	// the lower value, the less hungry npc is
	Hunger int
	Thirst int

	// the higher value, the faster npc get hungry
	HungerMod int
}

type Dwarf struct {
	Npc
}

type Rabbit struct {
	Npc
}

func (n *Npc) Defaults() {
	// init non-zero values
	n.Level = 1
}

func (n *Npc) hungerCap() int {
	// XXX
	return n.Level * 10
}

func (n *Npc) Tick() {
	n.Age++

	n.Hunger++

	fmt.Println("[tick]", n.Name, n.Age)

	if n.Hunger > n.hungerCap() {
		fmt.Println("HUNGRY!", n.Hunger, n.hungerCap())

		// XXX enqueue action
	}

	if n.CurrentlyDoing == DoingNothing {
		// XXX shuffle action list, so behaviour is more random
		// XXX if action in queue, make it "currently doing"
	}
}
<commit_msg>npc planned actions, wip<commit_after>package rogue

import "fmt"

type Point struct {
	X uint16
	Y uint16
}

type WorldObject interface {
	Tick()
	Defaults()
}

const (
	DoingNothing = 0
	DoingSleeping
	DoingEating
	DoingDrinking
	DoingMoving
	DoingForaging
)

type Npc struct {
	Level          int
	XP             int
	Age            int
	Name           string
	Position       Point
	CurrentlyDoing int
	PlannedActions []Action

	// the lower value, the less hungry npc is
	Hunger    int
	Thirst    int
	Tiredness int
}

type Dwarf struct {
	Npc
}

type Rabbit struct {
	Npc
}

func (n *Npc) Defaults() {
	// init non-zero values
	n.Level = 1
}

func (n *Npc) hungerCap() int {
	// XXX
	return n.Level * 10
}

func (n *Npc) tirednessCap() int {
	// XXX
	return n.Level * 10
}

func (n *Npc) Tick() {
	n.Age++

	n.Hunger++
	n.Tiredness++

	fmt.Println("[tick]", n.Name, n.Age)

	if n.Tiredness > n.tirednessCap() { // XXX make sure we dont have one such thing planned
		fmt.Println(n.Name, "is feeling tired")
		n.PlannedActions = append(n.PlannedActions, Sleep{})
	}

	if n.Hunger > n.hungerCap() {
		// fmt.Println("HUNGRY!", n.Hunger, n.hungerCap())

		// XXX enqueue action
	}

	if n.CurrentlyDoing == DoingNothing {
		// XXX shuffle action list, so behaviour is more random
		// XXX if action in queue, make it "currently doing"
	}

	fmt.Println(n.PlannedActions)
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fuse

import (
	"fmt"
	"unsafe"

	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/internal/buffer"
	"github.com/jacobsa/fuse/internal/fusekernel"
)

type internalOp struct {
}

////////////////////////////////////////////////////////////////////////
// Inodes
////////////////////////////////////////////////////////////////////////

type lookUpInodeOp struct {
	internalOp
	wrapped fuseops.LookUpInodeOp
}

func (o *lookUpInodeOp) kernelResponse(
	protocol fusekernel.Protocol) (b buffer.OutMessage) {
	size := fusekernel.EntryOutSize(protocol)
	b = buffer.NewOutMessage(size)
	out := (*fusekernel.EntryOut)(b.Grow(size))
	convertChildInodeEntry(&o.wrapped.Entry, out)

	return
}

type getInodeAttributesOp struct {
	internalOp
	wrapped fuseops.GetInodeAttributesOp
}

func (o *getInodeAttributesOp) kernelResponse() (b buffer.OutMessage) {
	size := fusekernel.AttrOutSize(o.protocol)
	b = buffer.NewOutMessage(size)
	out := (*fusekernel.AttrOut)(b.Grow(size))
	out.AttrValid, out.AttrValidNsec = convertExpirationTime(o.AttributesExpiration)
	convertAttributes(o.Inode, &o.Attributes, &out.Attr)

	return
}

type setInodeAttributesOp struct {
	internalOp
	wrapped fuseops.SetInodeAttributesOp
}

func (o *setInodeAttributesOp) kernelResponse() (b buffer.OutMessage) {
	size := fusekernel.AttrOutSize(o.protocol)
	b = buffer.NewOutMessage(size)
	out := (*fusekernel.AttrOut)(b.Grow(size))
	out.AttrValid, out.AttrValidNsec = convertExpirationTime(o.AttributesExpiration)
	convertAttributes(o.Inode, &o.Attributes, &out.Attr)

	return
}

type forgetInodeOp struct {
	internalOp
	wrapped fuseops.ForgetInodeOp
}

func (o *forgetInodeOp) kernelResponse() (b buffer.OutMessage) {
	// No response.
	return
}

////////////////////////////////////////////////////////////////////////
// Inode creation
////////////////////////////////////////////////////////////////////////

type mkDirOp struct {
	internalOp
	wrapped fuseops.MkDirOp
}

func (o *mkDirOp) kernelResponse() (b buffer.OutMessage) {
	size := fusekernel.EntryOutSize(o.protocol)
	b = buffer.NewOutMessage(size)
	out := (*fusekernel.EntryOut)(b.Grow(size))
	convertChildInodeEntry(&o.Entry, out)

	return
}

type createFileOp struct {
	internalOp
	wrapped fuseops.CreateFileOp
}

func (o *createFileOp) kernelResponse() (b buffer.OutMessage) {
	eSize := fusekernel.EntryOutSize(o.protocol)
	b = buffer.NewOutMessage(eSize + unsafe.Sizeof(fusekernel.OpenOut{}))

	e := (*fusekernel.EntryOut)(b.Grow(eSize))
	convertChildInodeEntry(&o.Entry, e)

	oo := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
	oo.Fh = uint64(o.Handle)

	return
}

type createSymlinkOp struct {
	internalOp
	wrapped fuseops.CreateSymlinkOp
}

func (o *createSymlinkOp) kernelResponse() (b buffer.OutMessage) {
	size := fusekernel.EntryOutSize(o.protocol)
	b = buffer.NewOutMessage(size)
	out := (*fusekernel.EntryOut)(b.Grow(size))
	convertChildInodeEntry(&o.Entry, out)

	return
}

////////////////////////////////////////////////////////////////////////
// Unlinking
////////////////////////////////////////////////////////////////////////

type renameOp struct {
	internalOp
	wrapped fuseops.RenameOp
}

func (o *renameOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(0)
	return
}

type rmDirOp struct {
	internalOp
	wrapped fuseops.RmDirOp
}

func (o *rmDirOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(0)
	return
}

type unlinkOp struct {
	internalOp
	wrapped fuseops.UnlinkOp
}

func (o *unlinkOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(0)
	return
}

////////////////////////////////////////////////////////////////////////
// Directory handles
////////////////////////////////////////////////////////////////////////

type openDirOp struct {
	internalOp
	wrapped fuseops.OpenDirOp
}

func (o *openDirOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.OpenOut{}))
	out := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
	out.Fh = uint64(o.Handle)

	return
}

type readDirOp struct {
	internalOp
	wrapped fuseops.ReadDirOp
}

func (o *readDirOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(uintptr(len(o.Data)))
	b.Append(o.Data)
	return
}

type releaseDirHandleOp struct {
	internalOp
	wrapped fuseops.ReleaseDirHandleOp
}

func (o *releaseDirHandleOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(0)
	return
}

////////////////////////////////////////////////////////////////////////
// File handles
////////////////////////////////////////////////////////////////////////

type openFileOp struct {
	internalOp
	wrapped fuseops.OpenFileOp
}

func (o *openFileOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.OpenOut{}))
	out := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
	out.Fh = uint64(o.Handle)

	return
}

type readFileOp struct {
	internalOp
	wrapped fuseops.ReadFileOp
}

func (o *readFileOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(uintptr(len(o.Data)))
	b.Append(o.Data)
	return
}

type writeFileOp struct {
	internalOp
	wrapped fuseops.WriteFileOp
}

func (o *writeFileOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.WriteOut{}))
	out := (*fusekernel.WriteOut)(b.Grow(unsafe.Sizeof(fusekernel.WriteOut{})))
	out.Size = uint32(len(o.Data))

	return
}

type syncFileOp struct {
	internalOp
	wrapped fuseops.SyncFileOp
}

func (o *syncFileOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(0)
	return
}

type flushFileOp struct {
	internalOp
	wrapped fuseops.FlushFileOp
}

func (o *flushFileOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(0)
	return
}

type releaseFileHandleOp struct {
	internalOp
	wrapped fuseops.ReleaseFileHandleOp
}

func (o *releaseFileHandleOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(0)
	return
}

// A sentinel used for unknown ops. The user is expected to respond with a
// non-nil error.
type unknownOp struct {
	internalOp
	opCode uint32
	inode  fuseops.InodeID
}

func (o *unknownOp) kernelResponse() (b buffer.OutMessage) {
	panic(fmt.Sprintf("Should never get here for unknown op: %s", o.ShortDesc()))
}

////////////////////////////////////////////////////////////////////////
// Reading symlinks
////////////////////////////////////////////////////////////////////////

type readSymlinkOp struct {
	internalOp
	wrapped fuseops.ReadSymlinkOp
}

func (o *readSymlinkOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(uintptr(len(o.Target)))
	b.AppendString(o.Target)
	return
}

////////////////////////////////////////////////////////////////////////
// Internal
////////////////////////////////////////////////////////////////////////

type internalStatFSOp struct {
	internalOp
}

func (o *internalStatFSOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.StatfsOut{}))
	b.Grow(unsafe.Sizeof(fusekernel.StatfsOut{}))

	return
}

type internalInterruptOp struct {
	internalOp
	FuseID uint64
}

func (o *internalInterruptOp) kernelResponse() (b buffer.OutMessage) {
	panic("Shouldn't get here.")
}

type internalInitOp struct {
	internalOp

	// In
	Kernel fusekernel.Protocol

	// Out
	Library      fusekernel.Protocol
	MaxReadahead uint32
	Flags        fusekernel.InitFlags
	MaxWrite     uint32
}

func (o *internalInitOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.InitOut{}))
	out := (*fusekernel.InitOut)(b.Grow(unsafe.Sizeof(fusekernel.InitOut{})))

	out.Major = o.Library.Major
	out.Minor = o.Library.Minor
	out.MaxReadahead = o.MaxReadahead
	out.Flags = uint32(o.Flags)
	out.MaxWrite = o.MaxWrite

	return
}
<commit_msg>Renamed internalOp to opCommon.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fuse

import (
	"fmt"
	"unsafe"

	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/internal/buffer"
	"github.com/jacobsa/fuse/internal/fusekernel"
)

type opCommon struct {
}

////////////////////////////////////////////////////////////////////////
// Inodes
////////////////////////////////////////////////////////////////////////

type lookUpInodeOp struct {
	opCommon
	wrapped fuseops.LookUpInodeOp
}

func (o *lookUpInodeOp) kernelResponse(
	protocol fusekernel.Protocol) (b buffer.OutMessage) {
	size := fusekernel.EntryOutSize(protocol)
	b = buffer.NewOutMessage(size)
	out := (*fusekernel.EntryOut)(b.Grow(size))
	convertChildInodeEntry(&o.wrapped.Entry, out)

	return
}

type getInodeAttributesOp struct {
	opCommon
	wrapped fuseops.GetInodeAttributesOp
}

func (o *getInodeAttributesOp) kernelResponse() (b buffer.OutMessage) {
	size := fusekernel.AttrOutSize(o.protocol)
	b = buffer.NewOutMessage(size)
	out := (*fusekernel.AttrOut)(b.Grow(size))
	out.AttrValid, out.AttrValidNsec = convertExpirationTime(o.AttributesExpiration)
	convertAttributes(o.Inode, &o.Attributes, &out.Attr)

	return
}

type setInodeAttributesOp struct {
	opCommon
	wrapped fuseops.SetInodeAttributesOp
}

func (o *setInodeAttributesOp) kernelResponse() (b buffer.OutMessage) {
	size := fusekernel.AttrOutSize(o.protocol)
	b = buffer.NewOutMessage(size)
	out := (*fusekernel.AttrOut)(b.Grow(size))
	out.AttrValid, out.AttrValidNsec = convertExpirationTime(o.AttributesExpiration)
	convertAttributes(o.Inode, &o.Attributes, &out.Attr)

	return
}

type forgetInodeOp struct {
	opCommon
	wrapped fuseops.ForgetInodeOp
}

func (o *forgetInodeOp) kernelResponse() (b buffer.OutMessage) {
	// No response.
	return
}

////////////////////////////////////////////////////////////////////////
// Inode creation
////////////////////////////////////////////////////////////////////////

type mkDirOp struct {
	opCommon
	wrapped fuseops.MkDirOp
}

func (o *mkDirOp) kernelResponse() (b buffer.OutMessage) {
	size := fusekernel.EntryOutSize(o.protocol)
	b = buffer.NewOutMessage(size)
	out := (*fusekernel.EntryOut)(b.Grow(size))
	convertChildInodeEntry(&o.Entry, out)

	return
}

type createFileOp struct {
	opCommon
	wrapped fuseops.CreateFileOp
}

func (o *createFileOp) kernelResponse() (b buffer.OutMessage) {
	eSize := fusekernel.EntryOutSize(o.protocol)
	b = buffer.NewOutMessage(eSize + unsafe.Sizeof(fusekernel.OpenOut{}))

	e := (*fusekernel.EntryOut)(b.Grow(eSize))
	convertChildInodeEntry(&o.Entry, e)

	oo := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
	oo.Fh = uint64(o.Handle)

	return
}

type createSymlinkOp struct {
	opCommon
	wrapped fuseops.CreateSymlinkOp
}

func (o *createSymlinkOp) kernelResponse() (b buffer.OutMessage) {
	size := fusekernel.EntryOutSize(o.protocol)
	b = buffer.NewOutMessage(size)
	out := (*fusekernel.EntryOut)(b.Grow(size))
	convertChildInodeEntry(&o.Entry, out)

	return
}

////////////////////////////////////////////////////////////////////////
// Unlinking
////////////////////////////////////////////////////////////////////////

type renameOp struct {
	opCommon
	wrapped fuseops.RenameOp
}

func (o *renameOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(0)
	return
}

type rmDirOp struct {
	opCommon
	wrapped fuseops.RmDirOp
}

func (o *rmDirOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(0)
	return
}

type unlinkOp struct {
	opCommon
	wrapped fuseops.UnlinkOp
}

func (o *unlinkOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(0)
	return
}

////////////////////////////////////////////////////////////////////////
// Directory handles
////////////////////////////////////////////////////////////////////////

type openDirOp struct {
	opCommon
	wrapped fuseops.OpenDirOp
}

func (o *openDirOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.OpenOut{}))
	out := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
	out.Fh = uint64(o.Handle)

	return
}

type readDirOp struct {
	opCommon
	wrapped fuseops.ReadDirOp
}

func (o *readDirOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(uintptr(len(o.Data)))
	b.Append(o.Data)
	return
}

type releaseDirHandleOp struct {
	opCommon
	wrapped fuseops.ReleaseDirHandleOp
}

func (o *releaseDirHandleOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(0)
	return
}

////////////////////////////////////////////////////////////////////////
// File handles
////////////////////////////////////////////////////////////////////////

type openFileOp struct {
	opCommon
	wrapped fuseops.OpenFileOp
}

func (o *openFileOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.OpenOut{}))
	out := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
	out.Fh = uint64(o.Handle)

	return
}

type readFileOp struct {
	opCommon
	wrapped fuseops.ReadFileOp
}

func (o *readFileOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(uintptr(len(o.Data)))
	b.Append(o.Data)
	return
}

type writeFileOp struct {
	opCommon
	wrapped fuseops.WriteFileOp
}

func (o *writeFileOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.WriteOut{}))
	out := (*fusekernel.WriteOut)(b.Grow(unsafe.Sizeof(fusekernel.WriteOut{})))
	out.Size = uint32(len(o.Data))

	return
}

type syncFileOp struct {
	opCommon
	wrapped fuseops.SyncFileOp
}

func (o *syncFileOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(0)
	return
}

type flushFileOp struct {
	opCommon
	wrapped fuseops.FlushFileOp
}

func (o *flushFileOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(0)
	return
}

type releaseFileHandleOp struct {
	opCommon
	wrapped fuseops.ReleaseFileHandleOp
}

func (o *releaseFileHandleOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(0)
	return
}

// A sentinel used for unknown ops. The user is expected to respond with a
// non-nil error.
type unknownOp struct {
	opCommon
	opCode uint32
	inode  fuseops.InodeID
}

func (o *unknownOp) kernelResponse() (b buffer.OutMessage) {
	panic(fmt.Sprintf("Should never get here for unknown op: %s", o.ShortDesc()))
}

////////////////////////////////////////////////////////////////////////
// Reading symlinks
////////////////////////////////////////////////////////////////////////

type readSymlinkOp struct {
	opCommon
	wrapped fuseops.ReadSymlinkOp
}

func (o *readSymlinkOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(uintptr(len(o.Target)))
	b.AppendString(o.Target)
	return
}

////////////////////////////////////////////////////////////////////////
// Internal
////////////////////////////////////////////////////////////////////////

type internalStatFSOp struct {
	opCommon
}

func (o *internalStatFSOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.StatfsOut{}))
	b.Grow(unsafe.Sizeof(fusekernel.StatfsOut{}))

	return
}

type internalInterruptOp struct {
	opCommon
	FuseID uint64
}

func (o *internalInterruptOp) kernelResponse() (b buffer.OutMessage) {
	panic("Shouldn't get here.")
}

type internalInitOp struct {
	opCommon

	// In
	Kernel fusekernel.Protocol

	// Out
	Library      fusekernel.Protocol
	MaxReadahead uint32
	Flags        fusekernel.InitFlags
	MaxWrite     uint32
}

func (o *internalInitOp) kernelResponse() (b buffer.OutMessage) {
	b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.InitOut{}))
	out := (*fusekernel.InitOut)(b.Grow(unsafe.Sizeof(fusekernel.InitOut{})))

	out.Major = o.Library.Major
	out.Minor = o.Library.Minor
	out.MaxReadahead = o.MaxReadahead
	out.Flags = uint32(o.Flags)
	out.MaxWrite = o.MaxWrite

	return
}
<|endoftext|>


<commit_before>package p2mfmt

import (
	"bytes"
	"regexp"
	"strings"
)

//
func Convert(pukiText string) (string, []string) {
	splitedWiki := strings.Split(
		strings.Replace(pukiText, "\r\n", "\n", -1),
		"\n",
	)

	var b bytes.Buffer
	var table Table

	categories := []string{}

	for _, v := range splitedWiki {
		v = regexp.MustCompile(`#navi\(.*\)`).ReplaceAllStringFunc(
			v,
			func(s string) string {
				s = strings.NewReplacer("#navi(", "", ")", "").Replace(s)
				categories = append(categories, s)

				return ""
			},
		)

		v = regexp.MustCompile(`#comment\(.*\)`).ReplaceAllStringFunc(
			v,
			func(s string) string {
				return "\n"
			},
		)

		if v != "" && v[0:1] == "|" && v[len(v)-1:] == "|" {
			table.ParseRow(v)
		} else {
			if table.Cells != nil {
				b.WriteString(table.String() + "\n")
				table.Cells = nil
			}
			b.WriteString(ConvertLine(v) + "\n")
		}
	}

	result := string(b.Bytes())

	if strings.Contains(result, "<ref>") {
		result += "\n== 備註 ==\n<references/>\n\n"
	}

	return result, categories
}

// 轉換任意時候均可轉換的內容
// 例如換行符號、引用/註解、圖片……等
func ConvertLine(wikiText string) string {
	if wikiText == "" {
		return ""
	}

	// 轉換標題
	if wikiText[0:1] == "*" {
		noneTitleIndexes := regexp.MustCompile(`[^\* ]`).FindStringIndex(wikiText)
		if noneTitleIndexes != nil {
			title := wikiText[noneTitleIndexes[0]:]
			title = regexp.MustCompile(`\[#[a-zA-Z0-9]*\]`).ReplaceAllLiteralString(title, "")

			h := wikiText[0:noneTitleIndexes[0]]
			h = strings.Replace(h, "*", "=", -1)
			wikiText = h + title + h
		}
	}
	// 轉換引用

	// 轉換清單
	if wikiText[0:1] == "-" {
		noneListIndexes := regexp.MustCompile(`[^\- ]`).FindStringIndex(wikiText)
		if noneListIndexes != nil {
			l := strings.Trim(wikiText[0:noneListIndexes[0]], " \t")
			if len(l) != 0 {
				l = strings.Repeat(":", len(l)-1) + "*"
			}

			wikiText = l + " " + wikiText[noneListIndexes[0]:]
		}
	}

	//
	// 轉換超連結
	// 一般超連結可不用轉換，但是連結文字和頁面標題不同時兩者格式不同
	// puki wiki 格式是 [[連結文字>頁面標題]]
	// media wiki 則是 [[頁面標題|連結文字]]
	//
	wikiText = regexp.MustCompile(`\[\[[^\>]*>[^\>]*\]\]`).ReplaceAllStringFunc(
		wikiText,
		func(s string) string {
			s = strings.NewReplacer("[[", "", "]]", "").Replace(s)
			vars := strings.Split(s, ">")

			pageTitle := vars[1]
			linkText := vars[0]

			if strings.Contains(pageTitle, "://") {
				// 此為超連結
				return `[` + pageTitle + " " + linkText + `]`
			}

			return `[[` + pageTitle + "|" + linkText + `]]`
		},
	)

	// 轉換圖片
	imgReg := regexp.MustCompile(`#imgr\([\.A-Za-z0-9\/,%]*\)`)
	wikiText = imgReg.ReplaceAllStringFunc(wikiText, func(s string) string {
		s = strings.NewReplacer("#imgr(", "", ")", "").Replace(s)

		imgVars := strings.Split(s, ",")

		img := Image{}

		for i, v := range imgVars {
			if i == 0 {
				img.Url = v
			} else if regexp.MustCompile("[0-9]").MatchString(v) {
				img.Size = v
			} else if v == "around" {
				img.Format = "thumb"
			} else if v == "center" || v == "right" || v == "left" || v == "none" {
				img.Align = v
			}
		}

		return img.String()
	})

	// 轉換刪除線
	delIndex := -1
	wikiText = regexp.MustCompile(`%%`).ReplaceAllStringFunc(
		wikiText,
		func(s string) string {
			delIndex++
			if delIndex%2 == 0 {
				return ""
			} else {
				return ""
			}

		},
	)

	// 轉換 換行符號
	wikiText = strings.Replace(wikiText, "&br;", "
", -1)
	wikiText = strings.Replace(wikiText, "~\n", "
\n", -1)
	if wikiText[len(wikiText):] == "~" {
		wikiText = wikiText[0:len(wikiText)-1] + "\n\n"
	}

	// 轉換 引用/ 註解 （<ref>）
	wikiText = strings.Replace(wikiText, "((", "<ref>", -1)
	wikiText = strings.Replace(wikiText, "))", "</ref>", -1)

	// 轉換粗斜體
	wikiText = strings.Replace(wikiText, "''", "", -1)
	wikiText = strings.Replace(wikiText, "'''", "", -1)
	wikiText = strings.Replace(wikiText, "", "'''", -1)
	wikiText = strings.Replace(wikiText, "", "''", -1)
	// TODO 處理顏色、背景、置左/中/右

	// 轉換回復的時間格式
	// pukiwiki 在進行回覆的時候，格式如下
	//
	// - 我的回覆 -- &new{2009-01-26 (月) 01:02:59};
	//
	// 前面已經轉換 list 了，這邊是將 &new{2009-01-26 (月) 01:02:59}; 轉換成
	//
	// <time>2009-01-26 (月) 01:02:59</time>
	//

	wikiText = regexp.MustCompile("&new{.*};").ReplaceAllStringFunc(
		wikiText,
		func(s string) string {
			return "<time>" + s[5:len(s)-2] + "</time>"
		},
	)

	// 轉換註解
	if strings.Contains(wikiText, "//") {
		s := strings.Split(wikiText, "//")
		hasComment := false

		for i, v := range s {
			if len(v) > 0 && v[len(v)-1:] == ":" {
				s[i] += "//"
			} else if i != len(s)-1 {
				s[i] += ""
		}
	}

	return wikiText
}
<commit_msg>修正超連結的 RegExp<commit_after>package p2mfmt

import (
	"bytes"
	"regexp"
	"strings"
)

//
func Convert(pukiText string) (string, []string) {
	splitedWiki := strings.Split(
		strings.Replace(pukiText, "\r\n", "\n", -1),
		"\n",
	)

	var b bytes.Buffer
	var table Table

	categories := []string{}

	for _, v := range splitedWiki {
		v = regexp.MustCompile(`#navi\(.*\)`).ReplaceAllStringFunc(
			v,
			func(s string) string {
				s = strings.NewReplacer("#navi(", "", ")", "").Replace(s)
				categories = append(categories, s)

				return ""
			},
		)

		v = regexp.MustCompile(`#comment\(.*\)`).ReplaceAllStringFunc(
			v,
			func(s string) string {
				return "\n"
			},
		)

		if v != "" && v[0:1] == "|" && v[len(v)-1:] == "|" {
			table.ParseRow(v)
		} else {
			if table.Cells != nil {
				b.WriteString(table.String() + "\n")
				table.Cells = nil
			}
			b.WriteString(ConvertLine(v) + "\n")
		}
	}

	result := string(b.Bytes())

	if strings.Contains(result, "<ref>") {
		result += "\n== 備註 ==\n<references/>\n\n"
	}

	return result, categories
}

// 轉換任意時候均可轉換的內容
// 例如換行符號、引用/註解、圖片……等
func ConvertLine(wikiText string) string {
	if wikiText == "" {
		return ""
	}

	// 轉換標題
	if wikiText[0:1] == "*" {
		noneTitleIndexes := regexp.MustCompile(`[^\* ]`).FindStringIndex(wikiText)
		if noneTitleIndexes != nil {
			title := wikiText[noneTitleIndexes[0]:]
			title = regexp.MustCompile(`\[#[a-zA-Z0-9]*\]`).ReplaceAllLiteralString(title, "")

			h := wikiText[0:noneTitleIndexes[0]]
			h = strings.Replace(h, "*", "=", -1)
			wikiText = h + title + h
		}
	}
	// 轉換引用

	// 轉換清單
	if wikiText[0:1] == "-" {
		noneListIndexes := regexp.MustCompile(`[^\- ]`).FindStringIndex(wikiText)
		if noneListIndexes != nil {
			l := strings.Trim(wikiText[0:noneListIndexes[0]], " \t")
			if len(l) != 0 {
				l = strings.Repeat(":", len(l)-1) + "*"
			}

			wikiText = l + " " + wikiText[noneListIndexes[0]:]
		}
	}

	//
	// 轉換超連結
	// 一般超連結可不用轉換，但是連結文字和頁面標題不同時兩者格式不同
	// puki wiki 格式是 [[連結文字>頁面標題]]
	// media wiki 則是 [[頁面標題|連結文字]]
	//
	wikiText = regexp.MustCompile(`\[\[[^\>\[\]]*>[^\>\[\]]*\]\]`).ReplaceAllStringFunc(
		wikiText,
		func(s string) string {
			s = strings.NewReplacer("[[", "", "]]", "").Replace(s)
			vars := strings.Split(s, ">")

			pageTitle := vars[1]
			linkText := vars[0]

			if strings.Contains(pageTitle, "://") {
				// 此為超連結
				return `[` + pageTitle + " " + linkText + `]`
			}

			return `[[` + pageTitle + "|" + linkText + `]]`
		},
	)

	// 轉換圖片
	imgReg := regexp.MustCompile(`#imgr\([\.A-Za-z0-9\/,%]*\)`)
	wikiText = imgReg.ReplaceAllStringFunc(wikiText, func(s string) string {
		s = strings.NewReplacer("#imgr(", "", ")", "").Replace(s)

		imgVars := strings.Split(s, ",")

		img := Image{}

		for i, v := range imgVars {
			if i == 0 {
				img.Url = v
			} else if regexp.MustCompile("[0-9]").MatchString(v) {
				img.Size = v
			} else if v == "around" {
				img.Format = "thumb"
			} else if v == "center" || v == "right" || v == "left" || v == "none" {
				img.Align = v
			}
		}

		return img.String()
	})

	// 轉換刪除線
	delIndex := -1
	wikiText = regexp.MustCompile(`%%`).ReplaceAllStringFunc(
		wikiText,
		func(s string) string {
			delIndex++
			if delIndex%2 == 0 {
				return ""
			} else {
				return ""
			}

		},
	)

	// 轉換 換行符號
	wikiText = strings.Replace(wikiText, "&br;", "
", -1)
	wikiText = strings.Replace(wikiText, "~\n", "
\n", -1)
	if wikiText[len(wikiText):] == "~" {
		wikiText = wikiText[0:len(wikiText)-1] + "\n\n"
	}

	// 轉換 引用/ 註解 （<ref>）
	wikiText = strings.Replace(wikiText, "((", "<ref>", -1)
	wikiText = strings.Replace(wikiText, "))", "</ref>", -1)

	// 轉換粗斜體
	wikiText = strings.Replace(wikiText, "''", "", -1)
	wikiText = strings.Replace(wikiText, "'''", "", -1)
	wikiText = strings.Replace(wikiText, "", "'''", -1)
	wikiText = strings.Replace(wikiText, "", "''", -1)
	// TODO 處理顏色、背景、置左/中/右

	// 轉換回復的時間格式
	// pukiwiki 在進行回覆的時候，格式如下
	//
	// - 我的回覆 -- &new{2009-01-26 (月) 01:02:59};
	//
	// 前面已經轉換 list 了，這邊是將 &new{2009-01-26 (月) 01:02:59}; 轉換成
	//
	// <time>2009-01-26 (月) 01:02:59</time>
	//

	wikiText = regexp.MustCompile("&new{.*};").ReplaceAllStringFunc(
		wikiText,
		func(s string) string {
			return "<time>" + s[5:len(s)-2] + "</time>"
		},
	)

	// 轉換註解
	if strings.Contains(wikiText, "//") {
		s := strings.Split(wikiText, "//")
		hasComment := false

		for i, v := range s {
			if len(v) > 0 && v[len(v)-1:] == ":" {
				s[i] += "//"
			} else if i != len(s)-1 {
				s[i] += ""
		}
	}

	return wikiText
}
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"log"

	"golang.org/x/text/encoding/unicode/utf32"
)

//PetType :
type PetType int

var BrownEmoji string

const (
	//Dog :
	Dog PetType = iota
	//Cat :
	Cat
)

func init() {
	var err error
	utf32BEIB := utf32.UTF32(utf32.BigEndian, utf32.IgnoreBOM)
	dec := utf32BEIB.NewDecoder()
	BrownEmoji, err = dec.String("\x00\x10\x00\x84")
	if err != nil {
		log.Print(err)
	}

}

//Pet :
type Pet struct {
	ID              string `json:"_id"`
	Name            string `json:"Name"`
	Sex             string `json:"Sex"`
	Type            string `json:"Type"`
	Build           string `json:"Build"`
	Age             string `json:"Age"`
	Variety         string `json:"Variety"`
	Reason          string `json:"Reason"`
	AcceptNum       string `json:"AcceptNum"`
	ChipNum         string `json:"ChipNum"`
	IsSterilization string `json:"IsSterilization"`
	HairType        string `json:"HairType"`
	Note            string `json:"Note"`
	Resettlement    string `json:"Resettlement"`
	Phone           string `json:"Phone"`
	Email           string `json:"Email"`
	ChildreAnlong   string `json:"ChildreAnlong"`
	AnimalAnlong    string `json:"AnimalAnlong"`
	Bodyweight      string `json:"Bodyweight"`
	ImageName       string `json:"ImageName"`
}

//PetType :
func (p *Pet) PetType() PetType {
	var retType PetType
	switch p.Variety {
	case "犬":
		retType = Dog
	case "貓":
		retType = Cat
	}

	return retType
}

//DisplayPet : Display single pet on chatbot
func (p *Pet) DisplayPet() string {
	if len(p.ImageName) > 0 {
		p.ImageName = getSecureImageAddress(p.ImageName)
		log.Println("img:", p.ImageName)
	}

	return fmt.Sprintf("%s 您好 \n 動物品種: %s \n 毛色: %s \n 體型: %s \n 性別: %s \n 名為: %s \n 公告收容所: %s \n 領養電話為: %s  \n 圖片位置: %s", BrownEmoji, p.Variety, p.HairType, p.Type, p.Sex, p.Name, p.Resettlement, p.Phone, p.ImageName)
}
<commit_msg>add emoji<commit_after>// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"log"
)

//PetType :
type PetType int

const (
	//Dog :
	Dog PetType = iota
	//Cat :
	Cat
)

//Pet :
type Pet struct {
	ID              string `json:"_id"`
	Name            string `json:"Name"`
	Sex             string `json:"Sex"`
	Type            string `json:"Type"`
	Build           string `json:"Build"`
	Age             string `json:"Age"`
	Variety         string `json:"Variety"`
	Reason          string `json:"Reason"`
	AcceptNum       string `json:"AcceptNum"`
	ChipNum         string `json:"ChipNum"`
	IsSterilization string `json:"IsSterilization"`
	HairType        string `json:"HairType"`
	Note            string `json:"Note"`
	Resettlement    string `json:"Resettlement"`
	Phone           string `json:"Phone"`
	Email           string `json:"Email"`
	ChildreAnlong   string `json:"ChildreAnlong"`
	AnimalAnlong    string `json:"AnimalAnlong"`
	Bodyweight      string `json:"Bodyweight"`
	ImageName       string `json:"ImageName"`
}

//PetType :
func (p *Pet) PetType() PetType {
	var retType PetType
	switch p.Variety {
	case "犬":
		retType = Dog
	case "貓":
		retType = Cat
	}

	return retType
}

//DisplayPet : Display single pet on chatbot
func (p *Pet) DisplayPet() string {
	if len(p.ImageName) > 0 {
		p.ImageName = getSecureImageAddress(p.ImageName)
		log.Println("img:", p.ImageName)
	}

	return fmt.Sprintf("$ 您好 \n 動物品種: %s \n 毛色: %s \n 體型: %s \n 性別: %s \n 名為: %s \n 公告收容所: %s \n 領養電話為: %s  \n 圖片位置: %s", p.Variety, p.HairType, p.Type, p.Sex, p.Name, p.Resettlement, p.Phone, p.ImageName)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/user"
	"regexp"

	"github.com/imwally/pinboard"
)

var (
	options   = flag.NewFlagSet("", flag.ExitOnError)
	privFlag  = options.Bool("private", false, "private bookmark")
	readFlag  = options.Bool("readlater", false, "read later bookmark")
	longFlag  = options.Bool("l", false, "display long format")
	extFlag   = options.String("text", "", "longer description of bookmark")
	tagFlag   = options.String("tag", "", "tags for bookmark")
	titleFlag = options.String("title", "", "title of the bookmark")

	token string
)

var usage = `Usage: pin
  pin rm  URL
  pin add URL [-title title] [OPTIONS]
  pin ls [OPTIONS]

Options:
  -tag        space delimited tags
  -private    mark bookmark as private
  -readlater  mark bookmark as read later
  -text       longer description of bookmark
  -l          long format for ls
`

// Number of bookmarks to display.
const COUNT int = 50

// Piped is a helper function to check for piped input. It will return
// input, true if data was piped.
func Piped() (string, bool) {
	fi, err := os.Stdin.Stat()
	if err != nil {
		fmt.Fprintf(os.Stderr, "pin: %s", err)
		return "", false
	}

	isPipe := (fi.Mode() & os.ModeNamedPipe) == os.ModeNamedPipe
	if isPipe {
		read := bufio.NewReader(os.Stdin)
		line, _, err := read.ReadLine()
		if err != nil {
			fmt.Println(err)
		}
		return string(line), true
	}

	return "", false
}

// PageTitle returns the title from an HTML page.
func PageTitle(url string) (title string, err error) {
	resp, err := http.Get(url)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

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

	re := regexp.MustCompile("<title>(.*?)</title>")

	return string(re.FindSubmatch(body)[1]), nil
}

// Add checks flag values and encodes the GET URL for adding a bookmark.
func Add(p pinboard.Post) {

	var args []string

	// Check if URL is piped in or first argument. Optional tags
	// should follow the URL.
	if url, ok := Piped(); ok {
		p.URL = url
		args = flag.Args()[1:]
	} else {
		p.URL = flag.Args()[1]
		args = flag.Args()[2:]
	}

	// Parse flags after the URL.
	options.Parse(args)

	title, err := PageTitle(p.URL)
	if err != nil {
		fmt.Fprintf(os.Stderr, "pin: couldn't get title: %s\n", err)
		return
	} else {
		p.Description = title
	}

	if *privFlag {
		p.Shared = "no"
	}

	if *readFlag {
		p.Toread = "yes"
	}

	p.Extended = *extFlag
	p.Tags = *tagFlag

	p.Encode()
	err = p.Add()
	if err != nil {
		fmt.Println(err)
	}
}

// Delete will delete the URL specified.
func Delete(p pinboard.Post) {

	// Check if URL is piped in or first argument.
	if url, ok := Piped(); ok {
		p.URL = url
	} else {
		p.URL = flag.Args()[1]
	}

	p.Encode()
	err := p.Delete()
	if err != nil {
		fmt.Println(err)
	}
}

// Show will list the most recent bookmarks. The -tag and -readlater
// flags can be used to filter results.
func Show(p pinboard.Post) {

	args := flag.Args()[1:]
	options.Parse(args)

	if *tagFlag != "" {
		p.Tag = *tagFlag
	}
	if *readFlag {
		p.Toread = "yes"
	}

	p.Count = COUNT
	p.Encode()

	recent := p.ShowRecent()

	if *longFlag {
		for _, v := range recent.Posts {
			var shared, unread string
			if v.Shared == "no" {
				shared = "[*]"
			}
			if v.Toread == "yes" {
				unread = "[#]"
			}
			fmt.Println(unread + shared + v.Description)
			fmt.Println(v.Href)
			if v.Extended != "" {
				fmt.Println(v.Extended)
			}
			fmt.Println(v.Tags, "\n")
		}
	} else {
		for _, v := range recent.Posts {
			fmt.Println(v.Href)
		}
	}
}

// runCmd takes a command string, initialises a new pinboard post and
// runs the command.
func runCmd(cmd string) {
	var p pinboard.Post
	p.Token = token

	if cmd == "help" {
		fmt.Printf("%s", usage)
	}

	if cmd == "ls" {
		Show(p)
	}

	if cmd == "add" {
		Add(p)
	}

	if cmd == "rm" {
		Delete(p)
	}
}

// start takes a slice of commands, parses flag arguments and runs the
// command if it's found.
func start(cmds []string) {
	flag.Parse()
	if flag.NArg() < 1 {
		fmt.Fprintf(os.Stderr, "pin: no command is given.\n")
		return
	}

	cmdName := flag.Arg(0)

	var found bool
	for _, cmd := range cmds {
		if cmdName == cmd {
			runCmd(cmd)
			return
		}
	}

	if !found {
		fmt.Fprintf(os.Stderr, "pin: command %s not found.\n", cmdName)
		return
	}
}

// TokenIsSet will check to make sure an authentication token is set before
// making any API calls.
func TokenIsSet() bool {
	if token == "" {
		return false
	}
	return true
}

func init() {
	u, err := user.Current()
	if err != nil {
		fmt.Println(err)
	}

	content, err := ioutil.ReadFile(u.HomeDir + "/.pinboard")
	if err != nil {
		fmt.Println("No authorization token found. Please add your authorization token to ~/.pinboard")
	}

	token = string(content)
}

func main() {
	if !TokenIsSet() {
		return
	}

	cmds := []string{"help", "add", "rm", "ls"}

	start(cmds)
}
<commit_msg>Cleaner unescaped titles.<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"html"
	"io/ioutil"
	"net/http"
	"os"
	"os/user"
	"regexp"

	"github.com/imwally/pinboard"
)

var (
	options   = flag.NewFlagSet("", flag.ExitOnError)
	privFlag  = options.Bool("private", false, "private bookmark")
	readFlag  = options.Bool("readlater", false, "read later bookmark")
	longFlag  = options.Bool("l", false, "display long format")
	extFlag   = options.String("text", "", "longer description of bookmark")
	tagFlag   = options.String("tag", "", "tags for bookmark")
	titleFlag = options.String("title", "", "title of the bookmark")

	token string
)

var usage = `Usage: pin
  pin rm  URL
  pin add URL [-title title] [OPTIONS]
  pin ls [OPTIONS]

Options:
  -tag        space delimited tags
  -private    mark bookmark as private
  -readlater  mark bookmark as read later
  -text       longer description of bookmark
  -l          long format for ls
`

// Number of bookmarks to display.
const COUNT int = 50

// Piped is a helper function to check for piped input. It will return
// input, true if data was piped.
func Piped() (string, bool) {
	fi, err := os.Stdin.Stat()
	if err != nil {
		fmt.Fprintf(os.Stderr, "pin: %s", err)
		return "", false
	}

	isPipe := (fi.Mode() & os.ModeNamedPipe) == os.ModeNamedPipe
	if isPipe {
		read := bufio.NewReader(os.Stdin)
		line, _, err := read.ReadLine()
		if err != nil {
			fmt.Println(err)
		}
		return string(line), true
	}

	return "", false
}

// PageTitle returns the title from an HTML page.
func PageTitle(url string) (title string, err error) {
	resp, err := http.Get(url)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

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

	re := regexp.MustCompile("<title>(.*?)</title>")

	return html.UnescapeString(string(re.FindSubmatch(body)[1])), nil
}

// Add checks flag values and encodes the GET URL for adding a bookmark.
func Add(p pinboard.Post) {

	var args []string

	// Check if URL is piped in or first argument. Optional tags
	// should follow the URL.
	if url, ok := Piped(); ok {
		p.URL = url
		args = flag.Args()[1:]
	} else {
		p.URL = flag.Args()[1]
		args = flag.Args()[2:]
	}

	// Parse flags after the URL.
	options.Parse(args)

	title, err := PageTitle(p.URL)
	if err != nil {
		fmt.Fprintf(os.Stderr, "pin: couldn't get title: %s\n", err)
		return
	} else {
		p.Description = title
	}

	if *privFlag {
		p.Shared = "no"
	}

	if *readFlag {
		p.Toread = "yes"
	}

	p.Extended = *extFlag
	p.Tags = *tagFlag

	p.Encode()
	err = p.Add()
	if err != nil {
		fmt.Println(err)
	}
}

// Delete will delete the URL specified.
func Delete(p pinboard.Post) {

	// Check if URL is piped in or first argument.
	if url, ok := Piped(); ok {
		p.URL = url
	} else {
		p.URL = flag.Args()[1]
	}

	p.Encode()
	err := p.Delete()
	if err != nil {
		fmt.Println(err)
	}
}

// Show will list the most recent bookmarks. The -tag and -readlater
// flags can be used to filter results.
func Show(p pinboard.Post) {

	args := flag.Args()[1:]
	options.Parse(args)

	if *tagFlag != "" {
		p.Tag = *tagFlag
	}
	if *readFlag {
		p.Toread = "yes"
	}

	p.Count = COUNT
	p.Encode()

	recent := p.ShowRecent()

	if *longFlag {
		for _, v := range recent.Posts {
			var shared, unread string
			if v.Shared == "no" {
				shared = "[*]"
			}
			if v.Toread == "yes" {
				unread = "[#]"
			}
			fmt.Println(unread + shared + v.Description)
			fmt.Println(v.Href)
			if v.Extended != "" {
				fmt.Println(v.Extended)
			}
			fmt.Println(v.Tags, "\n")
		}
	} else {
		for _, v := range recent.Posts {
			fmt.Println(v.Href)
		}
	}
}

// runCmd takes a command string, initialises a new pinboard post and
// runs the command.
func runCmd(cmd string) {
	var p pinboard.Post
	p.Token = token

	if cmd == "help" {
		fmt.Printf("%s", usage)
	}

	if cmd == "ls" {
		Show(p)
	}

	if cmd == "add" {
		Add(p)
	}

	if cmd == "rm" {
		Delete(p)
	}
}

// start takes a slice of commands, parses flag arguments and runs the
// command if it's found.
func start(cmds []string) {
	flag.Parse()
	if flag.NArg() < 1 {
		fmt.Fprintf(os.Stderr, "pin: no command is given.\n")
		return
	}

	cmdName := flag.Arg(0)

	var found bool
	for _, cmd := range cmds {
		if cmdName == cmd {
			runCmd(cmd)
			return
		}
	}

	if !found {
		fmt.Fprintf(os.Stderr, "pin: command %s not found.\n", cmdName)
		return
	}
}

// TokenIsSet will check to make sure an authentication token is set before
// making any API calls.
func TokenIsSet() bool {
	if token == "" {
		return false
	}
	return true
}

func init() {
	u, err := user.Current()
	if err != nil {
		fmt.Println(err)
	}

	content, err := ioutil.ReadFile(u.HomeDir + "/.pinboard")
	if err != nil {
		fmt.Println("No authorization token found. Please add your authorization token to ~/.pinboard")
	}

	token = string(content)
}

func main() {
	if !TokenIsSet() {
		return
	}

	cmds := []string{"help", "add", "rm", "ls"}

	start(cmds)
}
<|endoftext|>


<commit_before>// +build !cairo

package main

import (
	"bytes"
	"image/color"
	"math"
	"net/http"
	"strconv"
	"time"

	"github.com/gogo/protobuf/proto"
	"github.com/gonum/plot"
	"github.com/gonum/plot/plotter"
	"github.com/gonum/plot/plotutil"
	"github.com/gonum/plot/vg"
	vgdraw "github.com/gonum/plot/vg/draw"
)

func marshalPNG(r *http.Request, results []*metricData) []byte {
	p, err := plot.New()
	if err != nil {
		panic(err)
	}

	// set bg/fg colors
	bgcolor := string2Color(getString(r.FormValue("bgcolor"), "black"))
	p.BackgroundColor = bgcolor

	fgcolorstr := getString(r.FormValue("fgcolor"), "white")
	fgcolor := string2Color(fgcolorstr)
	p.Title.Color = fgcolor
	p.X.LineStyle.Color = fgcolor
	p.Y.LineStyle.Color = fgcolor
	p.X.Tick.LineStyle.Color = fgcolor
	p.Y.Tick.LineStyle.Color = fgcolor
	p.X.Tick.Label.Color = fgcolor
	p.Y.Tick.Label.Color = fgcolor
	p.X.Label.Color = fgcolor
	p.Y.Label.Color = fgcolor
	p.Legend.Color = fgcolor

	// set grid
	grid := plotter.NewGrid()
	grid.Vertical.Color = fgcolor
	grid.Horizontal.Color = fgcolor
	p.Add(grid)

	// line mode (ikruglow) TODO check values
	lineMode := getString(r.FormValue("lineMode"), "slope")

	// width and height
	width := getFloat64(r.FormValue("width"), 330)
	height := getFloat64(r.FormValue("height"), 250)

	// need different timeMarker's based on step size
	p.Title.Text = r.FormValue("title")
	if len(results) > 0 {
		p.X.Tick.Marker = NewTimeMarker(results[0].GetStepTime())
	}

	hideLegend := getBool(r.FormValue("hideLegend"), false)

	graphOnly := getBool(r.FormValue("graphOnly"), false)
	if graphOnly {
		p.HideAxes()
	}

	if len(results) == 1 && results[0].color == "" {
		results[0].color = fgcolorstr
	}

	var lines []plot.Plotter
	for i, r := range results {
		if r == nil {
			continue
		}
		l := NewResponsePlotter(r)

		l.LineStyle.Color = fgcolor

		// consolidate datapoints
		l.maybeConsolidateData(width)

		if r.drawAsInfinite {
			l.lineMode = "drawAsInfinite"
		} else {
			l.lineMode = lineMode
		}

		if r.color != "" {
			l.Color = string2Color(r.color)
		} else {
			l.Color = plotutil.Color(i)
		}

		lines = append(lines, l)

		if !graphOnly && !hideLegend {
			p.Legend.Add(r.GetName(), l)
		}
	}

	p.Add(lines...)

	p.Y.Max *= 1.05
	p.Y.Min *= 0.95

	writerTo, err := p.WriterTo(vg.Points(width), vg.Points(height), "png")
	var buffer bytes.Buffer
	if _, err := writerTo.WriteTo(&buffer); err != nil {
		panic(err)
	}

	return buffer.Bytes()
}

type TimeMarker struct {
	format string
}

func NewTimeMarker(step int32) TimeMarker {
	var format string

	// heuristic yoinked from graphite, more or less
	switch {
	case step < 5:
		format = "15:04:05"
	case step < 60:
		format = "15:04"
	case step < 100:
		format = "Mon 3PM"
	case step < 255:
		format = "01/02 3PM"
	case step < 32000:
		format = "01/02"
	case step < 120000:
		format = "01/02 2006"
	}

	return TimeMarker{format}
}

func (tm TimeMarker) Ticks(min, max float64) []plot.Tick {
	ticks := []plot.Tick{
		plot.Tick{
			Value: min,
		},
		plot.Tick{
			Value: max,
		},
	}

	for i, t := range ticks {
		if !t.IsMinor() {
			t0 := time.Unix(int64(t.Value), 0)
			ticks[i].Label = t0.Format(tm.format)
		}
	}

	return ticks
}

func string2Color(clr string) color.Color {
	if c, ok := colors[clr]; ok {
		return c
	}
	return hexToColor(clr)
}

// https://code.google.com/p/sadbox/source/browse/color/hex.go
// hexToColor converts an Hex string to a RGB triple.
func hexToColor(h string) color.Color {
	var r, g, b uint8
	if len(h) > 0 && h[0] == '#' {
		h = h[1:]
	}

	if len(h) == 3 {
		h = h[:1] + h[:1] + h[1:2] + h[1:2] + h[2:] + h[2:]
	}

	if len(h) == 6 {
		if rgb, err := strconv.ParseUint(h, 16, 32); err == nil {
			r = uint8(rgb >> 16)
			g = uint8((rgb >> 8) & 0xFF)
			b = uint8(rgb & 0xFF)
		}
	}

	return color.RGBA{r, g, b, 255}
}

type ResponsePlotter struct {
	Response *metricData
	vgdraw.LineStyle
	lineMode string
}

func NewResponsePlotter(r *metricData) *ResponsePlotter {
	return &ResponsePlotter{
		Response:  r,
		LineStyle: plotter.DefaultLineStyle,
	}
}

// Plot draws the Line, implementing the plot.Plotter interface.
func (rp *ResponsePlotter) Plot(canvas vgdraw.Canvas, plt *plot.Plot) {
	trX, trY := plt.Transforms(&canvas)

	start := float64(rp.Response.GetStartTime())
	step := float64(rp.Response.GetStepTime())
	absent := rp.Response.IsAbsent

	lines := make([][]vgdraw.Point, 1)
	lines[0] = make([]vgdraw.Point, 0, len(rp.Response.Values))

	/* ikruglov
	 * swithing between lineMode and looping inside
	 * is more branch-prediction friendly i.e. potentially faster */
	switch rp.lineMode {
	case "slope":
		currentLine := 0
		lastAbsent := false
		for i, v := range rp.Response.Values {
			if absent[i] {
				lastAbsent = true
			} else if lastAbsent {
				currentLine++
				lines = append(lines, make([]vgdraw.Point, 1))
				lines[currentLine][0] = vgdraw.Point{X: trX(start + float64(i)*step), Y: trY(v)}
				lastAbsent = false
			} else {
				lines[currentLine] = append(lines[currentLine], vgdraw.Point{X: trX(start + float64(i)*step), Y: trY(v)})
			}
		}

	case "connected":
		for i, v := range rp.Response.Values {
			if absent[i] {
				continue
			}

			lines[0] = append(lines[0], vgdraw.Point{X: trX(start + float64(i)*step), Y: trY(v)})
		}

	case "drawAsInfinite":
		for i, v := range rp.Response.Values {
			if !absent[i] && v > 0 {
				infiniteLine := []vgdraw.Point{
					vgdraw.Point{X: trX(start + float64(i)*step), Y: canvas.Y(1)},
					vgdraw.Point{X: trX(start + float64(i)*step), Y: canvas.Y(0)},
				}
				lines = append(lines, infiniteLine)
			}
		}

	//case "staircase": // TODO
	default:
		panic("Unimplemented " + rp.lineMode)
	}

	canvas.StrokeLines(rp.LineStyle, lines...)
}

func (rp *ResponsePlotter) Thumbnail(canvas *vgdraw.Canvas) {
	l := plotter.Line{LineStyle: rp.LineStyle}
	l.Thumbnail(canvas)
}

func (rp *ResponsePlotter) DataRange() (xmin, xmax, ymin, ymax float64) {
	ymin = math.Inf(1)
	ymax = math.Inf(-1)
	absent := rp.Response.IsAbsent

	xmin = float64(rp.Response.GetStartTime())
	xmax = float64(rp.Response.GetStopTime())

	// same as rp.lineMode == "drawAsInfinite"
	if rp.Response.drawAsInfinite {
		ymin = 0
		ymax = 1
		return
	}

	for i, v := range rp.Response.Values {
		if absent[i] {
			continue
		}
		ymin = math.Min(ymin, v)
		ymax = math.Max(ymax, v)
	}
	return
}

func (rp *ResponsePlotter) maybeConsolidateData(numberOfPixels float64) {
	// idealy numberOfPixels should be size in pixels of char ares,
	// not char areay with Y axis and its label

	numberOfDataPoints := len(rp.Response.Values)
	pointsPerPixel := int(math.Ceil(float64(numberOfDataPoints) / numberOfPixels))

	if pointsPerPixel <= 1 {
		return
	}

	newNumberOfDataPoints := (numberOfDataPoints / pointsPerPixel) + 1
	values := make([]float64, newNumberOfDataPoints)
	absent := make([]bool, newNumberOfDataPoints)

	k := 0
	step := pointsPerPixel
	for i := 0; i < numberOfDataPoints; i += step {
		if i+step < numberOfDataPoints {
			values[k], absent[k] = consolidateAvg(rp.Response.Values[i:i+step], rp.Response.IsAbsent[i:i+step])
		} else {
			values[k], absent[k] = consolidateAvg(rp.Response.Values[i:], rp.Response.IsAbsent[i:])
		}

		k++
	}

	stepTime := rp.Response.GetStepTime()
	stepTime *= int32(pointsPerPixel)

	rp.Response.Values = values[:k]
	rp.Response.IsAbsent = absent[:k]
	rp.Response.StepTime = proto.Int32(stepTime)
}

func consolidateAvg(v []float64, a []bool) (float64, bool) {
	cnt := len(v)
	if cnt == 0 {
		return 0.0, true
	}

	abs := true
	var val float64
	var elts int

	for i := 0; i < cnt; i++ {
		if !a[i] {
			abs = false
			val += v[i]
			elts++
		}
	}

	// Don't need to check for divide-by-zero. We'll just return NaN and the point will be marked absent.
	return val / float64(elts), abs
}
<commit_msg>Remove ancient, broken PNG rendering backend.<commit_after>// +build !cairo

package main

import "net/http"

func marshalPNG(r *http.Request, results []*metricData) []byte {
	return nil
}
<|endoftext|>


<commit_before>package rcf

import (
	"bytes"
	"encoding/binary"
	"encoding/gob"
	"fmt"
	"github.com/golang/snappy"
	"io"
	"os"
	"reflect"
	"runtime"
	"sync"
)

type File struct {
	sync.Mutex
	file         *os.File
	path         string
	colSets      [][]string
	colSetsFn    func(int) interface{}
	validateOnce sync.Once
}

func (f *File) Sync() error {
	f.Lock()
	defer f.Unlock()
	return f.file.Sync()
}

func (f *File) Close() error {
	return f.file.Close()
}

func New(path string, colSetsFn func(int) interface{}) (*File, error) {
	file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		return nil, makeErr(err, "open file")
	}
	n := 0
	colSets := [][]string{}
	for {
		v := colSetsFn(n)
		if v == nil {
			break
		}
		gob.Register(v)
		t := reflect.TypeOf(v).Elem()
		set := []string{}
		for i, max := 0, t.NumField(); i < max; i++ {
			set = append(set, t.Field(i).Name)
		}
		colSets = append(colSets, set)
		n++
	}
	ret := &File{
		file:      file,
		path:      path,
		colSets:   colSets,
		colSetsFn: colSetsFn,
	}
	return ret, nil
}

func (f *File) validate() (err error) {
	f.validateOnce.Do(func() {
	read:
		// read number of sets
		var numSets uint8
		err = binary.Read(f.file, binary.LittleEndian, &numSets)
		if err == io.EOF { // no more
			return
		}
		if err != nil {
			err = makeErr(err, "read number of column sets")
			return
		}
		// read meta length
		var sum, l uint32
		err = binary.Read(f.file, binary.LittleEndian, &l)
		if err != nil {
			err = makeErr(err, "read meta length")
			return
		}
		sum += l
		// read sets length
		for i, max := 0, int(numSets); i < max; i++ {
			err = binary.Read(f.file, binary.LittleEndian, &l)
			if err != nil {
				err = makeErr(err, "read column set length")
				return
			}
			sum += l
		}
		_, err = f.file.Seek(int64(sum), os.SEEK_CUR)
		if err != nil {
			err = makeErr(err, "validate seek")
			return
		}
		goto read
		return
	})
	return
}

func encode(o interface{}) (bs []byte, err error) {
	buf := new(bytes.Buffer)
	w := snappy.NewWriter(buf)
	err = gob.NewEncoder(w).Encode(o)
	if err != nil {
		return nil, err
	}
	err = w.Close()
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func decode(bs []byte, target interface{}) (err error) {
	r := snappy.NewReader(bytes.NewReader(bs))
	return gob.NewDecoder(r).Decode(target)
}

func (f *File) Append(rows, meta interface{}) error {
	f.validate()
	// encode meta
	metaBin, err := encode(meta)
	if err != nil {
		return makeErr(err, "encode meta")
	}
	// column slices
	rowsValue := reflect.ValueOf(rows)
	if rowsValue.Type().Kind() != reflect.Slice {
		return makeErr(nil, "rows is not slice")
	}
	columns := make(map[string]reflect.Value)
	for i, l := 0, rowsValue.Len(); i < l; i++ {
		row := rowsValue.Index(i)
		// make colums slices
		if i == 0 {
			for _, set := range f.colSets {
				for _, col := range set {
					if _, ok := columns[col]; ok {
						continue
					}
					columns[col] = reflect.MakeSlice(reflect.SliceOf(row.FieldByName(col).Type()), 0, 0)
				}
			}
		}
		// append colum values
		for name, col := range columns {
			columns[name] = reflect.Append(col, row.FieldByName(name))
		}
	}
	// column sets
	var bins [][]byte
	for n, set := range f.colSets {
		var v interface{} = f.colSetsFn(n)
		s := reflect.ValueOf(v)
		if s.Kind() == reflect.Ptr {
			s = s.Elem()
		}
		for _, col := range set {
			field := s.FieldByName(col)
			if !field.IsValid() {
				return makeErr(nil, fmt.Sprintf("no %s field in colun set %d", col, n))
			}
			column := columns[col]
			if column.IsValid() { // if len(rows) == 0, this would be a nil slice
				field.Set(column)
			}
		}
		//t0 := time.Now()
		bin, err := encode(&v)
		if err != nil {
			return makeErr(err, "encode column set")
		}
		bins = append(bins, bin)
	}
	// write header
	if len(bins) > 255 {
		return makeErr(nil, "more than 255 column sets")
	}
	f.Lock()
	defer f.Unlock()
	err = binary.Write(f.file, binary.LittleEndian, uint8(len(bins)))
	if err != nil {
		return makeErr(err, "write length length")
	}
	err = binary.Write(f.file, binary.LittleEndian, uint32(len(metaBin)))
	if err != nil {
		return makeErr(err, "write meta length")
	}
	for _, bin := range bins {
		err = binary.Write(f.file, binary.LittleEndian, uint32(len(bin)))
		if err != nil {
			return makeErr(err, "write column set length")
		}
	}
	// write encoded
	_, err = f.file.Write(metaBin)
	if err != nil {
		return makeErr(err, "write meta")
	}
	for _, bin := range bins {
		_, err = f.file.Write(bin)
		if err != nil {
			return makeErr(err, "write column set")
		}
	}
	return nil
}

func (f *File) IterMetas(fn interface{}) error {
	f.Sync()
	file, err := os.Open(f.path)
	if err != nil {
		return makeErr(err, "open file")
	}
	defer file.Close()

	fnValue := reflect.ValueOf(fn)
	fnType := fnValue.Type()
	metaType := fnType.In(0)

	wg := new(sync.WaitGroup)
	fns := make(chan func(), 2048)
	fns2 := make(chan func(), 2048)
	done := make(chan struct{})
	closeDoneOnce := new(sync.Once)
	shut := func() {
		closeDoneOnce.Do(func() {
			close(done)
		})
	}

	var e error
	setErrOnce := new(sync.Once)
	setErr := func(err error) {
		setErrOnce.Do(func() {
			e = err
		})
	}

	go func() {
		for {
			meta := reflect.New(metaType)

			// read number of sets
			var numSets uint8
			err = binary.Read(file, binary.LittleEndian, &numSets)
			if err == io.EOF { // no more
				break
			}
			if err != nil {
				setErr(makeErr(err, "read number of column sets"))
				shut()
				return
			}

			// read meta length
			var metaLength uint32
			err = binary.Read(file, binary.LittleEndian, &metaLength)
			if err != nil {
				setErr(makeErr(err, "read meta length"))
				shut()
				return
			}

			// read sets length
			var sum, l uint32
			for i, max := 0, int(numSets); i < max; i++ {
				err = binary.Read(file, binary.LittleEndian, &l)
				if err != nil {
					setErr(makeErr(err, "read column set length"))
					shut()
					return
				}
				sum += l
			}

			// read meta
			bs := make([]byte, metaLength)
			_, err = io.ReadFull(file, bs)
			if err != nil {
				setErr(makeErr(err, "read meta"))
				shut()
				return
			}
			wg.Add(1)

			select {
			case fns <- func() {
				// decode meta
				err = decode(bs, meta.Interface())
				if err != nil {
					setErr(makeErr(err, "decode meta"))
					shut()
					return
				}
				// callback
				select {
				case fns2 <- func() {
					if !fnValue.Call([]reflect.Value{meta.Elem()})[0].Bool() {
						shut()
						return
					}
					wg.Done()
				}:
				case <-done:
					return
				}
			}:
			case <-done:
				return
			}

			// skip sets
			_, err = file.Seek(int64(sum), os.SEEK_CUR)
			if err != nil {
				setErr(makeErr(err, "skip column sets"))
				shut()
				return
			}

		}
		wg.Wait()
		shut()
	}()

	go func() {
		ncpu := runtime.NumCPU()
		for i := 0; i < ncpu; i++ {
			go func() {
				for {
					select {
					case fn := <-fns:
						fn()
					case <-done:
						return
					}
				}
			}()
		}
	}()

loop:
	for {
		select {
		case fn := <-fns2:
			fn()
		case <-done:
			break loop
		}
	}

	return e
}

func (f *File) Iter(cols []string, cb func(columns ...interface{}) bool) error {
	f.Sync()
	file, err := os.Open(f.path)
	if err != nil {
		return makeErr(err, "open file")
	}
	defer file.Close()

	// determine which set to decode and which column to collect
	toCollect := make([][]bool, 0)
	for _, set := range f.colSets {
		c := []bool{}
		for _, col := range set {
			in := false
			for _, column := range cols {
				if column == col {
					in = true
					break
				}
			}
			c = append(c, in)
		}
		toCollect = append(toCollect, c)
	}
	toDecode := make([]bool, len(toCollect))
	for n, c := range toCollect {
		for _, b := range c {
			if b {
				toDecode[n] = true
				break
			}
		}
	}

	var e error
	setErrOnce := new(sync.Once)
	setErr := func(err error) {
		setErrOnce.Do(func() {
			e = err
		})
	}

	wg := new(sync.WaitGroup)
	fns := make(chan func(), 2048)
	fns2 := make(chan func(), 2048)
	done := make(chan struct{})
	closeDoneOnce := new(sync.Once)
	shutdown := func() {
		closeDoneOnce.Do(func() {
			close(done)
		})
	}

	// read bytes
	go func() {
		for {
			// read number of sets
			var numSets uint8
			err := binary.Read(file, binary.LittleEndian, &numSets)
			if err == io.EOF { // no more
				break
			}
			if err != nil {
				setErr(makeErr(err, "read number of column sets"))
				shutdown()
				return
			}
			// read meta length
			var metaLength uint32
			err = binary.Read(file, binary.LittleEndian, &metaLength)
			if err != nil {
				setErr(makeErr(err, "read meta length"))
				shutdown()
				return
			}
			// read sets length
			var lens []uint32
			var l uint32
			for i, max := 0, int(numSets); i < max; i++ {
				err = binary.Read(file, binary.LittleEndian, &l)
				if err != nil {
					setErr(makeErr(err, "read column set length"))
					shutdown()
					return
				}
				lens = append(lens, l)
			}
			// skip meta
			_, err = file.Seek(int64(metaLength), os.SEEK_CUR)
			if err != nil {
				setErr(makeErr(err, "skip meta"))
				shutdown()
				return
			}
			// read bytes
			var bss [][]byte
			for n, l := range lens {
				if toDecode[n] { // decode
					bs := make([]byte, l)
					_, err = io.ReadFull(file, bs)
					if err != nil {
						setErr(makeErr(err, "read column set"))
						shutdown()
						return
					}
					bss = append(bss, bs)
				} else { // skip
					_, err = file.Seek(int64(l), os.SEEK_CUR)
					if err != nil {
						setErr(makeErr(err, "skip column set"))
						shutdown()
						return
					}
					bss = append(bss, nil)
				}
			}

			wg.Add(1)
			select {
			case fns <- func() {
				var columns []interface{}
				for n, bs := range bss {
					if bs == nil {
						continue
					}
					s := f.colSetsFn(n)
					err := decode(bs, &s)
					if err != nil {
						setErr(makeErr(err, "decode column set"))
						shutdown()
						return
					}
					sValue := reflect.ValueOf(s).Elem()
					for nfield, b := range toCollect[n] {
						if b {
							columns = append(columns, sValue.Field(nfield).Interface())
						}
					}
				}

				select {
				case fns2 <- func() {
					if !cb(columns...) {
						shutdown()
						return
					}
					wg.Done()
				}:
				case <-done:
					return
				}

			}:
			case <-done:
				return
			}

		}
		wg.Wait()
		shutdown()
	}()

	go func() {
		ncpu := runtime.NumCPU()
		for i := 0; i < ncpu; i++ {
			go func() {
				for {
					select {
					case fn := <-fns:
						fn()
					case <-done:
						return
					}
				}
			}()
		}
	}()

loop:
	for {
		select {
		case fn := <-fns2:
			fn()
		case <-done:
			break loop
		}
	}

	return e
}
<commit_msg>use github.com/reusee/pipeline<commit_after>package rcf

import (
	"bytes"
	"encoding/binary"
	"encoding/gob"
	"fmt"
	"github.com/golang/snappy"
	"github.com/reusee/pipeline"
	"io"
	"os"
	"reflect"
	"runtime"
	"sync"
)

type File struct {
	sync.Mutex
	file         *os.File
	path         string
	colSets      [][]string
	colSetsFn    func(int) interface{}
	validateOnce sync.Once
}

func (f *File) Sync() error {
	f.Lock()
	defer f.Unlock()
	return f.file.Sync()
}

func (f *File) Close() error {
	return f.file.Close()
}

func New(path string, colSetsFn func(int) interface{}) (*File, error) {
	file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		return nil, makeErr(err, "open file")
	}
	n := 0
	colSets := [][]string{}
	for {
		v := colSetsFn(n)
		if v == nil {
			break
		}
		gob.Register(v)
		t := reflect.TypeOf(v).Elem()
		set := []string{}
		for i, max := 0, t.NumField(); i < max; i++ {
			set = append(set, t.Field(i).Name)
		}
		colSets = append(colSets, set)
		n++
	}
	ret := &File{
		file:      file,
		path:      path,
		colSets:   colSets,
		colSetsFn: colSetsFn,
	}
	return ret, nil
}

func (f *File) validate() (err error) {
	f.validateOnce.Do(func() {
	read:
		// read number of sets
		var numSets uint8
		err = binary.Read(f.file, binary.LittleEndian, &numSets)
		if err == io.EOF { // no more
			return
		}
		if err != nil {
			err = makeErr(err, "read number of column sets")
			return
		}
		// read meta length
		var sum, l uint32
		err = binary.Read(f.file, binary.LittleEndian, &l)
		if err != nil {
			err = makeErr(err, "read meta length")
			return
		}
		sum += l
		// read sets length
		for i, max := 0, int(numSets); i < max; i++ {
			err = binary.Read(f.file, binary.LittleEndian, &l)
			if err != nil {
				err = makeErr(err, "read column set length")
				return
			}
			sum += l
		}
		_, err = f.file.Seek(int64(sum), os.SEEK_CUR)
		if err != nil {
			err = makeErr(err, "validate seek")
			return
		}
		goto read
		return
	})
	return
}

func encode(o interface{}) (bs []byte, err error) {
	buf := new(bytes.Buffer)
	w := snappy.NewWriter(buf)
	err = gob.NewEncoder(w).Encode(o)
	if err != nil {
		return nil, err
	}
	err = w.Close()
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func decode(bs []byte, target interface{}) (err error) {
	r := snappy.NewReader(bytes.NewReader(bs))
	return gob.NewDecoder(r).Decode(target)
}

func (f *File) Append(rows, meta interface{}) error {
	f.validate()
	// encode meta
	metaBin, err := encode(meta)
	if err != nil {
		return makeErr(err, "encode meta")
	}
	// column slices
	rowsValue := reflect.ValueOf(rows)
	if rowsValue.Type().Kind() != reflect.Slice {
		return makeErr(nil, "rows is not slice")
	}
	columns := make(map[string]reflect.Value)
	for i, l := 0, rowsValue.Len(); i < l; i++ {
		row := rowsValue.Index(i)
		// make colums slices
		if i == 0 {
			for _, set := range f.colSets {
				for _, col := range set {
					if _, ok := columns[col]; ok {
						continue
					}
					columns[col] = reflect.MakeSlice(reflect.SliceOf(row.FieldByName(col).Type()), 0, 0)
				}
			}
		}
		// append colum values
		for name, col := range columns {
			columns[name] = reflect.Append(col, row.FieldByName(name))
		}
	}
	// column sets
	var bins [][]byte
	for n, set := range f.colSets {
		var v interface{} = f.colSetsFn(n)
		s := reflect.ValueOf(v)
		if s.Kind() == reflect.Ptr {
			s = s.Elem()
		}
		for _, col := range set {
			field := s.FieldByName(col)
			if !field.IsValid() {
				return makeErr(nil, fmt.Sprintf("no %s field in colun set %d", col, n))
			}
			column := columns[col]
			if column.IsValid() { // if len(rows) == 0, this would be a nil slice
				field.Set(column)
			}
		}
		//t0 := time.Now()
		bin, err := encode(&v)
		if err != nil {
			return makeErr(err, "encode column set")
		}
		bins = append(bins, bin)
	}
	// write header
	if len(bins) > 255 {
		return makeErr(nil, "more than 255 column sets")
	}
	f.Lock()
	defer f.Unlock()
	err = binary.Write(f.file, binary.LittleEndian, uint8(len(bins)))
	if err != nil {
		return makeErr(err, "write length length")
	}
	err = binary.Write(f.file, binary.LittleEndian, uint32(len(metaBin)))
	if err != nil {
		return makeErr(err, "write meta length")
	}
	for _, bin := range bins {
		err = binary.Write(f.file, binary.LittleEndian, uint32(len(bin)))
		if err != nil {
			return makeErr(err, "write column set length")
		}
	}
	// write encoded
	_, err = f.file.Write(metaBin)
	if err != nil {
		return makeErr(err, "write meta")
	}
	for _, bin := range bins {
		_, err = f.file.Write(bin)
		if err != nil {
			return makeErr(err, "write column set")
		}
	}
	return nil
}

func (f *File) IterMetas(fn interface{}) error {
	f.Sync()
	file, err := os.Open(f.path)
	if err != nil {
		return makeErr(err, "open file")
	}
	defer file.Close()

	fnValue := reflect.ValueOf(fn)
	fnType := fnValue.Type()
	metaType := fnType.In(0)

	line := pipeline.NewPipeline()
	p1 := line.NewPipe(2048)
	p2 := line.NewPipe(2048)

	go func() {
		for {
			meta := reflect.New(metaType)

			// read number of sets
			var numSets uint8
			err = binary.Read(file, binary.LittleEndian, &numSets)
			if err == io.EOF { // no more
				break
			}
			if err != nil {
				line.Error(makeErr(err, "read number of column sets"))
				return
			}

			// read meta length
			var metaLength uint32
			err = binary.Read(file, binary.LittleEndian, &metaLength)
			if err != nil {
				line.Error(makeErr(err, "read meta length"))
				return
			}

			// read sets length
			var sum, l uint32
			for i, max := 0, int(numSets); i < max; i++ {
				err = binary.Read(file, binary.LittleEndian, &l)
				if err != nil {
					line.Error(makeErr(err, "read column set length"))
					return
				}
				sum += l
			}

			// read meta
			bs := make([]byte, metaLength)
			_, err = io.ReadFull(file, bs)
			if err != nil {
				line.Error(makeErr(err, "read meta"))
				return
			}
			line.Add()

			if !p1.Do(func() {
				// decode meta
				err = decode(bs, meta.Interface())
				if err != nil {
					line.Error(makeErr(err, "decode meta"))
					return
				}
				// callback
				if !p2.Do(func() {
					if !fnValue.Call([]reflect.Value{meta.Elem()})[0].Bool() {
						line.Close()
						return
					}
					line.Done()
				}) {
					return
				}
			}) {
				return
			}

			// skip sets
			_, err = file.Seek(int64(sum), os.SEEK_CUR)
			if err != nil {
				line.Error(makeErr(err, "skip column sets"))
				return
			}

		}
		line.Wait()
		line.Close()
	}()

	go p1.ParallelProcess(runtime.NumCPU())
	p2.Process()

	return line.Err
}

func (f *File) Iter(cols []string, cb func(columns ...interface{}) bool) error {
	f.Sync()
	file, err := os.Open(f.path)
	if err != nil {
		return makeErr(err, "open file")
	}
	defer file.Close()

	// determine which set to decode and which column to collect
	toCollect := make([][]bool, 0)
	for _, set := range f.colSets {
		c := []bool{}
		for _, col := range set {
			in := false
			for _, column := range cols {
				if column == col {
					in = true
					break
				}
			}
			c = append(c, in)
		}
		toCollect = append(toCollect, c)
	}
	toDecode := make([]bool, len(toCollect))
	for n, c := range toCollect {
		for _, b := range c {
			if b {
				toDecode[n] = true
				break
			}
		}
	}

	line := pipeline.NewPipeline()
	p1 := line.NewPipe(2048)
	p2 := line.NewPipe(2048)

	// read bytes
	go func() {
		for {
			// read number of sets
			var numSets uint8
			err := binary.Read(file, binary.LittleEndian, &numSets)
			if err == io.EOF { // no more
				break
			}
			if err != nil {
				line.Error(makeErr(err, "read number of column sets"))
				return
			}
			// read meta length
			var metaLength uint32
			err = binary.Read(file, binary.LittleEndian, &metaLength)
			if err != nil {
				line.Error(makeErr(err, "read meta length"))
				return
			}
			// read sets length
			var lens []uint32
			var l uint32
			for i, max := 0, int(numSets); i < max; i++ {
				err = binary.Read(file, binary.LittleEndian, &l)
				if err != nil {
					line.Error(makeErr(err, "read column set length"))
					return
				}
				lens = append(lens, l)
			}
			// skip meta
			_, err = file.Seek(int64(metaLength), os.SEEK_CUR)
			if err != nil {
				line.Error(makeErr(err, "skip meta"))
				return
			}
			// read bytes
			var bss [][]byte
			for n, l := range lens {
				if toDecode[n] { // decode
					bs := make([]byte, l)
					_, err = io.ReadFull(file, bs)
					if err != nil {
						line.Error(makeErr(err, "read column set"))
						return
					}
					bss = append(bss, bs)
				} else { // skip
					_, err = file.Seek(int64(l), os.SEEK_CUR)
					if err != nil {
						line.Error(makeErr(err, "skip column set"))
						return
					}
					bss = append(bss, nil)
				}
			}

			line.Add()
			if !p1.Do(func() {
				var columns []interface{}
				for n, bs := range bss {
					if bs == nil {
						continue
					}
					s := f.colSetsFn(n)
					err := decode(bs, &s)
					if err != nil {
						line.Error(makeErr(err, "decode column set"))
						return
					}
					sValue := reflect.ValueOf(s).Elem()
					for nfield, b := range toCollect[n] {
						if b {
							columns = append(columns, sValue.Field(nfield).Interface())
						}
					}
				}

				if !p2.Do(func() {
					if !cb(columns...) {
						line.Close()
						return
					}
					line.Done()
				}) {
					return
				}

			}) {
				return
			}

		}
		line.Wait()
		line.Close()
	}()

	go p1.ParallelProcess(runtime.NumCPU())
	p2.Process()

	return line.Err
}
<|endoftext|>


<commit_before>package main

/*
loadTinzenite loads an existing Tinzenite directory and runs it.
*/import (
	"fmt"
	"github.com/tinzenite/bootstrap"
	"github.com/tinzenite/core"
	"github.com/tinzenite/shared"
	"log"
	"os"
	"os/signal"
	"time"
)

func bootstrapTinzenite(path string) {
	var boot *bootstrap.Bootstrap
	var err error
	done := make(chan bool)
	if shared.IsTinzenite(path) {
		boot, err = bootstrap.Load(path, func() {
			done <- true
		})
		if err != nil {
			logMain("Bootstrap load error:", err.Error())
			return
		}
	} else {
		// ask whether this is supposed to be a trusted peer
		question := shared.CreateYesNo("Is this a TRUSTED peer?")
		trusted := question.Ask() > 0
		// get peer name
		peerName := shared.GetString("Enter the peer name for this Tinzenite directory:")
		// get address to connect to BEFORE starting boot to avoid terminal clutter
		address := shared.GetString("Please enter the address of the peer to connect to:")
		// build object
		boot, err = bootstrap.Create(path, peerName, trusted, func() {
			done <- true
		})
		if err != nil {
			logMain("Bootstrap create error:", err.Error())
			return
		}
		// connect to:
		err = boot.Start(address)
		if err != nil {
			logMain("Bootstrap start error:", err.Error())
			// return because we don't want to store a faulty bootstrap
			return
		}
		// if everything ok, try storing this bootstrap
		err = boot.Store()
		if err != nil {
			logMain("Bootstrap store error:", err.Error())
		}
	}
	// print information
	address, _ := boot.Address()
	fmt.Printf("Bootstrapping.\nID: %s\n", address)
	// wait for successful bootstrap
	<-done
	log.Println("Closing Bootstrap.")
	// this is required before closing boot because ToxCore may still need to
	// notify the other client that the file transfers are complete - this can
	// take a few iterations, so we delay for a second to give it time to do that.
	<-time.Tick(1 * time.Second)
	// manually close boot if we're done! It won't close itself!
	boot.Close()
	// continue to executing the directory
	fmt.Println("Bootstrapping was successful. Loading Tinzenite.")
	// load tinzenite with password
	password := getPassword()
	loadTinzenite(path, password)
}

func createTinzenite(path string) {
	if shared.IsTinzenite(path) {
		logMain("Directory is already a valid Tinzenite directory!")
		return
	}
	// get options
	peerName := shared.GetString("Enter the peer name for this Tinzenite directory:")
	userName := shared.GetString("Enter your username:")
	password := shared.GetString("Enter a password for this Tinzenite network:")
	relPath := shared.CreatePathRoot(path)
	tinzenite, err := core.CreateTinzenite(relPath.LastElement(), relPath.FullPath(), peerName, userName, password)
	if err != nil {
		logMain("Creation error:", err.Error())
		return
	}
	err = tinzenite.SyncLocal()
	if err != nil {
		logMain("Initial model sync error:", err.Error())
	}
	// run tinzenite until killed
	runTinzenite(tinzenite)
}

func loadTinzenite(path, password string) {
	if !shared.IsTinzenite(path) {
		logMain("Directory is not a valid Tinzenite directory!")
		return
	}
	tinzenite, err := core.LoadTinzenite(path, password)
	if err != nil {
		// TODO catch wrong password and allow retry
		logMain("Loading error:", err.Error())
		return
	}
	err = tinzenite.SyncLocal()
	if err != nil {
		logMain("Initial model sync error:", err.Error())
	}
	// run tinzenite until killed
	runTinzenite(tinzenite)
}

/*
runTinzenite runs the given Tinzenite instance.
*/
func runTinzenite(t *core.Tinzenite) {
	// do this here so that it is guaranteed to be set
	t.RegisterPeerValidation(func(address string, wantsTrust bool) {
		var allow bool
		if wantsTrust {
			question := shared.CreateYesNo("Add peer " + address[:8] + " as TRUSTED peer?")
			allow = question.Ask() > 0
		} else {
			question := shared.CreateYesNo("Add peer " + address[:8] + " as ENCRYPTED peer?")
			allow = question.Ask() > 0
		}
		if !allow {
			log.Println("Tin: will not add peer, as requested.")
			return
		}
		// allow peer
		err := t.AllowPeer(address)
		if err != nil {
			log.Println("Tinzenite: failed to allow peer:", err)
		}
		log.Println("Tin: will allow peer, as requested.")
	})
	// print important info
	address, _ := t.Address()
	fmt.Printf("Running peer <%s>.\nID: %s\n", t.Name(), address)
	// run update and sync in intervalls
	var counter int
	// only build this once instead of every time
	tickSpan := time.Duration(10) * time.Second
	ticker := time.Tick(tickSpan)
	// prepare quitting via ctrl-c
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	// loop until close
	for {
		select {
		case <-ticker:
			if counter >= 5 {
				// TODO remove once Merge bug is fixed
				log.Println("DEBUG: Model sync ---------------------------")
				counter = 0
				err := t.SyncRemote()
				if err != nil {
					logMain("SyncRemote error:", err.Error())
				}
				continue
			}
			// log.Println("DEBUG: Update")
			counter++
			err := t.SyncLocal()
			if err != nil {
				logMain("SyncLocal error:", err.Error())
			}
		case <-c:
			// on interrupt close tinzenite
			t.Close()
			return
		} // select
	} // for
}
<commit_msg>dont run tinzenite if encrypted peer<commit_after>package main

/*
loadTinzenite loads an existing Tinzenite directory and runs it.
*/import (
	"fmt"
	"github.com/tinzenite/bootstrap"
	"github.com/tinzenite/core"
	"github.com/tinzenite/shared"
	"log"
	"os"
	"os/signal"
	"time"
)

func bootstrapTinzenite(path string) {
	var boot *bootstrap.Bootstrap
	var err error
	done := make(chan bool)
	if shared.IsTinzenite(path) {
		boot, err = bootstrap.Load(path, func() {
			done <- true
		})
		if err != nil {
			logMain("Bootstrap load error:", err.Error())
			return
		}
	} else {
		// ask whether this is supposed to be a trusted peer
		question := shared.CreateYesNo("Is this a TRUSTED peer?")
		trusted := question.Ask() > 0
		// get peer name
		peerName := shared.GetString("Enter the peer name for this Tinzenite directory:")
		// get address to connect to BEFORE starting boot to avoid terminal clutter
		address := shared.GetString("Please enter the address of the peer to connect to:")
		// build object
		boot, err = bootstrap.Create(path, peerName, trusted, func() {
			done <- true
		})
		if err != nil {
			logMain("Bootstrap create error:", err.Error())
			return
		}
		// connect to:
		err = boot.Start(address)
		if err != nil {
			logMain("Bootstrap start error:", err.Error())
			// return because we don't want to store a faulty bootstrap
			return
		}
		// if everything ok, try storing this bootstrap
		err = boot.Store()
		if err != nil {
			logMain("Bootstrap store error:", err.Error())
		}
	}
	// print information
	address, _ := boot.Address()
	fmt.Printf("Bootstrapping.\nID: %s\n", address)
	// wait for successful bootstrap
	<-done
	log.Println("Closing Bootstrap.")
	// this is required before closing boot because ToxCore may still need to
	// notify the other client that the file transfers are complete - this can
	// take a few iterations, so we delay for a second to give it time to do that.
	<-time.Tick(1 * time.Second)
	// manually close boot if we're done! It won't close itself!
	boot.Close()
	// IF w bootstrapped an encrypted peer, write that to the log and quit.
	if !boot.IsTrusted() {
		fmt.Println("Bootstrapping was successful. Run server to start encrypted peer.")
		return
	}
	// continue to executing the directory
	fmt.Println("Bootstrapping was successful. Loading Tinzenite.")
	// load tinzenite with password
	password := getPassword()
	loadTinzenite(path, password)
}

func createTinzenite(path string) {
	if shared.IsTinzenite(path) {
		logMain("Directory is already a valid Tinzenite directory!")
		return
	}
	// get options
	peerName := shared.GetString("Enter the peer name for this Tinzenite directory:")
	userName := shared.GetString("Enter your username:")
	password := shared.GetString("Enter a password for this Tinzenite network:")
	relPath := shared.CreatePathRoot(path)
	tinzenite, err := core.CreateTinzenite(relPath.LastElement(), relPath.FullPath(), peerName, userName, password)
	if err != nil {
		logMain("Creation error:", err.Error())
		return
	}
	err = tinzenite.SyncLocal()
	if err != nil {
		logMain("Initial model sync error:", err.Error())
	}
	// run tinzenite until killed
	runTinzenite(tinzenite)
}

func loadTinzenite(path, password string) {
	if !shared.IsTinzenite(path) {
		logMain("Directory is not a valid Tinzenite directory!")
		return
	}
	tinzenite, err := core.LoadTinzenite(path, password)
	if err != nil {
		// TODO catch wrong password and allow retry
		logMain("Loading error:", err.Error())
		return
	}
	err = tinzenite.SyncLocal()
	if err != nil {
		logMain("Initial model sync error:", err.Error())
	}
	// run tinzenite until killed
	runTinzenite(tinzenite)
}

/*
runTinzenite runs the given Tinzenite instance.
*/
func runTinzenite(t *core.Tinzenite) {
	// do this here so that it is guaranteed to be set
	t.RegisterPeerValidation(func(address string, wantsTrust bool) {
		var allow bool
		if wantsTrust {
			question := shared.CreateYesNo("Add peer " + address[:8] + " as TRUSTED peer?")
			allow = question.Ask() > 0
		} else {
			question := shared.CreateYesNo("Add peer " + address[:8] + " as ENCRYPTED peer?")
			allow = question.Ask() > 0
		}
		if !allow {
			log.Println("Tin: will not add peer, as requested.")
			return
		}
		// allow peer
		err := t.AllowPeer(address)
		if err != nil {
			log.Println("Tinzenite: failed to allow peer:", err)
		}
		log.Println("Tin: will allow peer, as requested.")
	})
	// print important info
	address, _ := t.Address()
	fmt.Printf("Running peer <%s>.\nID: %s\n", t.Name(), address)
	// run update and sync in intervalls
	var counter int
	// only build this once instead of every time
	tickSpan := time.Duration(10) * time.Second
	ticker := time.Tick(tickSpan)
	// prepare quitting via ctrl-c
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	// loop until close
	for {
		select {
		case <-ticker:
			if counter >= 5 {
				// TODO remove once Merge bug is fixed
				log.Println("DEBUG: Model sync ---------------------------")
				counter = 0
				err := t.SyncRemote()
				if err != nil {
					logMain("SyncRemote error:", err.Error())
				}
				continue
			}
			// log.Println("DEBUG: Update")
			counter++
			err := t.SyncLocal()
			if err != nil {
				logMain("SyncLocal error:", err.Error())
			}
		case <-c:
			// on interrupt close tinzenite
			t.Close()
			return
		} // select
	} // for
}
<|endoftext|>