<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package simplepush

import (
	"code.google.com/p/go.net/websocket"
	"mozilla.org/simplepush/sperrors"
	"mozilla.org/util"

	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"time"
)

var MissingChannelErr = errors.New("Missing channelID")

//    -- Workers
//      these write back to the websocket.

type Worker struct {
	log   *util.HekaLogger
	state int
}

const (
	INACTIVE = 0
	ACTIVE   = 1
)

const (
    UAID_MAX_LEN = 100
    CHID_MAX_LEN = 100
)

func NewWorker(config util.JsMap) *Worker {
	return &Worker{log: util.NewHekaLogger(config), state: INACTIVE}
}

func (self *Worker) sniffer(sock PushWS, in chan util.JsMap) {
	// Sniff the websocket for incoming data.
	// Reading from the websocket is a blocking operation, and we also
	// need to write out when an even occurs. This isolates the incoming
	// reads to a separate go process.
	var socket = sock.Socket
	for {
		var raw []byte
		var buffer util.JsMap
		err := websocket.Message.Receive(socket, &raw)
		if err != nil {
			self.log.Error("worker",
				fmt.Sprintf("Websocket Error %s", err), nil)
			break
		}
		if len(raw) > 0 {
			self.log.Info("worker",
				fmt.Sprintf("Socket received %s", string(raw)), nil)
			err := json.Unmarshal(raw, &buffer)
			if err != nil {
				self.log.Error("worker",
					"Unparsable data", util.JsMap{"raw": raw})
				break
			}
			self.log.Info("worker",
				fmt.Sprintf("Socket sending %s", buffer), nil)
			// Only do something if there's something to do.
			in <- buffer
		}
	}
	// Clean up the server side (This will delete records associated
	// with the UAID.
	sock.Scmd <- PushCommand{Command: DIE, Arguments: nil}
	socket.Close()
}

// standardize the error reporting back to the client.
func (self *Worker) handleError(sock PushWS, message util.JsMap, err error) (ret error) {
	self.log.Info("worker", fmt.Sprintf("Sending error %s", err), nil)
	message["status"] = sperrors.ErrToStatus(err)
	message["error"] = err.Error()
	return websocket.JSON.Send(sock.Socket, message)
}

// General workhorse loop for the websocket handler.
func (self *Worker) Run(sock PushWS) {
	var err error

	// Instantiate a websocket reader, a blocking operation
	// (Remember, we need to be able to write out PUSH events
	// as they happen.)
	in := make(chan util.JsMap)
	go self.sniffer(sock, in)

	for {
		select {
		case cmd := <-sock.Ccmd:
			// A new Push has happened. Flush out the data to the
			// device (and potentially remotely wake it if that fails)
			self.log.Info("worker",
				fmt.Sprintf("Client Run cmd: %d", cmd.Command),
				nil)
			if cmd.Command == FLUSH {
				self.log.Info("worker",
					fmt.Sprintf("Flushing... %s", sock.Uaid), nil)
				self.Flush(sock, time.Now().UTC().Unix())
				// additional non-client commands are TBD.
			}
		case buffer := <-in:
			defer func(sock PushWS) {
				if r := recover(); r != nil {
					sock.Logger.Error("worker", r.(error).Error(), nil)
				}
				sock.Scmd <- PushCommand{Command: DIE, Arguments: nil}
				sock.Socket.Close()
				return
			}(sock)

			self.log.Info("worker",
				fmt.Sprintf("Client Read buffer, %s \n", buffer), nil)
			// process the client commands
			if _, ok := buffer["messageType"]; !ok {
				self.log.Info("worker", "Invalid message",
					util.JsMap{"reason": "Missing messageType",
						"data": buffer})
				self.handleError(sock,
					util.JsMap{},
					sperrors.UnknownCommandError)
				break
			}
			switch strings.ToLower(buffer["messageType"].(string)) {
			case "hello":
				err = self.Hello(&sock, buffer)
			case "ack":
				err = self.Ack(sock, buffer)
			case "register":
				err = self.Register(sock, buffer)
                self.log.Info("worker", fmt.Sprintf("Reg result:: %s", err), nil)
			case "unregister":
				err = self.Unregister(sock, buffer)
			case "ping":
				err = self.Ping(sock, buffer)
			default:
				self.log.Warn("worker",
					fmt.Sprintf("I have no idea what [%s] is.", buffer),
					nil)
				err = sperrors.UnknownCommandError
			}
			if err != nil {
				self.handleError(sock, buffer, err)
				break
			}
		}
	}
	sock.Scmd <- PushCommand{Command: DIE, Arguments: nil}
	sock.Socket.Close()
}

// Associate the UAID for this socket connection (and flush any data that
// may be pending for the connection)
func (self *Worker) Hello(sock *PushWS, buffer interface{}) (err error) {
	// register the UAID
    defer func() {
        if r := recover(); r != nil {
            self.log.Error("worker",
                fmt.Sprintf("Unhandled error in Hello: %s", r),
                nil )
            err = sperrors.InvalidDataError
            }
    }()

	data := buffer.(util.JsMap)
	if _, ok := data["uaid"]; !ok {
		// Must include "uaid" (even if blank)
		return sperrors.MissingDataError
	}
	if data["channelIDs"] == nil {
		// Must include "channelIDs" (even if empty)
		return sperrors.MissingDataError
	}
	if len(sock.Uaid) > 0 && len(data["uaid"].(string)) > 0 && sock.Uaid != data["uaid"].(string) {
		// if there's already a Uaid for this channel, don't accept a new one
		return sperrors.InvalidCommandError
	}
	if len(sock.Uaid) == 0 {
		// if there's no UAID for the socket, accept or create a new one.
		sock.Uaid = data["uaid"].(string)
		if len(sock.Uaid) > UAID_MAX_LEN {
			return sperrors.InvalidDataError
		}
		if len(sock.Uaid) == 0 {
			sock.Uaid, _ = GenUUID4()
		}
	}
	// register the sockets (NOOP)
	// register any proprietary connection requirements
	// alert the master of the new UAID.
	cmd := PushCommand{Command: HELLO,
		Arguments: util.JsMap{
			"uaid":  sock.Uaid,
			"chids": data["channelIDs"]}}
	// blocking call back to the boss.
	sock.Scmd <- cmd
	result := <-sock.Scmd
	if err = sock.Store.SetUAIDHost(sock.Uaid); err != nil {
		return err
	}

	self.log.Info("worker", "sending HELLO response....",
		util.JsMap{"uaid": sock.Uaid})
	websocket.JSON.Send(sock.Socket, util.JsMap{
		"messageType": data["messageType"],
		"status":      result.Command,
		"uaid":        sock.Uaid})
	self.state = ACTIVE
	if err == nil {
		// Get the lastAccessed time from wherever
		self.Flush(*sock, 0)
	}
	return err
}

// Clear the data that the client stated it received, then re-flush any
// records (including new data)
func (self *Worker) Ack(sock PushWS, buffer interface{}) (err error) {
    defer func() {
        if r := recover(); r != nil {
            self.log.Error("worker",
                fmt.Sprintf("Unhandled error in Ack: %s", r),
                nil )
            err = sperrors.InvalidDataError
            }
    }()

	if sock.Uaid == "" {
		return sperrors.InvalidCommandError
	}
	data := buffer.(util.JsMap)
	if data["updates"] == nil {
		return sperrors.MissingDataError
	}
	err = sock.Store.Ack(sock.Uaid, data)
	// Get the lastAccessed time from wherever.
	if err == nil {
        websocket.JSON.Send(sock.Socket, util.JsMap{
            "messageType": data["messageType"],
            "status": 200})
		self.Flush(sock, 0)
		return nil
	}
	self.log.Info("worker", fmt.Sprintf("Flushed ACK returning %s", err), nil)
	return err
}

// Register a new ChannelID. Optionally, encrypt the endpoint.
func (self *Worker) Register(sock PushWS, buffer interface{}) (err error) {
    defer func() {
        if r := recover(); r != nil {
            self.log.Error("worker",
                fmt.Sprintf("Unhandled error in Register: %s", r),
                nil )
            err = sperrors.InvalidDataError
            }
    }()

	if sock.Uaid == "" {
		return sperrors.InvalidCommandError
	}
	data := buffer.(util.JsMap)
	if data["channelID"] == nil {
		return sperrors.MissingDataError
	}
	appid := data["channelID"].(string)
	if len(appid) > CHID_MAX_LEN {
		return sperrors.InvalidDataError
	}
	err = sock.Store.RegisterAppID(sock.Uaid, appid, "")
	if err != nil {
		self.log.Error("worker",
			fmt.Sprintf("ERROR: RegisterAppID failed %s", err),
			nil)
		return err
	}
	// have the server generate the callback URL.
	cmd := PushCommand{Command: REGIS,
		Arguments: data}
	sock.Scmd <- cmd
	result := <-sock.Scmd
	self.log.Debug("worker", fmt.Sprintf("Server returned %s", result), nil)
	endpoint := result.Arguments.(util.JsMap)["pushEndpoint"].(string)
	// return the info back to the socket
	self.log.Info("worker", "Sending REGIS response....", nil)
	websocket.JSON.Send(sock.Socket, util.JsMap{
		"messageType":  data["messageType"],
		"status":       result.Command,
		"pushEndpoint": endpoint})
	return err
}

// Unregister a ChannelID.
func (self *Worker) Unregister(sock PushWS, buffer interface{}) (err error) {
    defer func() {
        if r := recover(); r != nil {
            self.log.Error("worker",
                fmt.Sprintf("Unhandled error in Unregister: %s", r),
                nil )
            err = sperrors.InvalidDataError
            }
    }()
	if sock.Uaid == "" {
		return sperrors.InvalidCommandError
	}
	data := buffer.(util.JsMap)
	if data["channelID"] == nil {
		return sperrors.MissingDataError
	}
	appid := data["channelID"].(string)
    // Always return success for an UNREG.
	sock.Store.DeleteAppID(sock.Uaid, appid, false)
	self.log.Info("worker", "Sending UNREG response ..", nil)
	websocket.JSON.Send(sock.Socket, util.JsMap{
		"messageType": data["messageType"],
		"status":      200,
		"channelID":   appid})
	return err
}

// Dump any records associated with the UAID.
func (self *Worker) Flush(sock PushWS, lastAccessed int64) {
	// flush pending data back to Client
	messageType := "notification"
	timer := time.Now()
	defer func(timer time.Time, sock PushWS) {
		sock.Logger.Info("timer",
			"Client flush completed",
			util.JsMap{"duration": time.Now().Sub(timer).Nanoseconds(),
				"uaid": sock.Uaid})
	}(timer, sock)
	if sock.Uaid == "" {
		self.log.Error("worker", "Undefined UAID for socket. Aborting.", nil)
		// Have the server clean up records associated with this UAID.
		// (Probably "none", but still good for housekeeping)
		sock.Scmd <- PushCommand{Command: DIE, Arguments: nil}
		sock.Socket.Close()
	}
	// Fetch the pending updates from #storage
	updates, err := sock.Store.GetUpdates(sock.Uaid, lastAccessed)
	if err != nil {
		self.handleError(sock, util.JsMap{"messageType": messageType}, err)
		return
	}
	if updates == nil {
		return
	}
	updates["messageType"] = messageType
	self.log.Info("worker", "Flushing data back to socket", updates)
	websocket.JSON.Send(sock.Socket, updates)
}

func (self *Worker) Ping(sock PushWS, buffer interface{}) (err error) {
	data := buffer.(util.JsMap)
	websocket.JSON.Send(sock.Socket, util.JsMap{
		"messageType": data["messageType"],
		"status":      200})
	return nil
}

// o4fs
// vim: set tabstab=4 softtabstop=4 shiftwidth=4 noexpandtab
<commit_msg>Issue #24 - handle empty JSON buffer as "ping"<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package simplepush

import (
	"code.google.com/p/go.net/websocket"
	"mozilla.org/simplepush/sperrors"
	"mozilla.org/util"

	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"time"
)

var MissingChannelErr = errors.New("Missing channelID")

//    -- Workers
//      these write back to the websocket.

type Worker struct {
	log   *util.HekaLogger
	state int
}

const (
	INACTIVE = 0
	ACTIVE   = 1
)

const (
	UAID_MAX_LEN = 100
	CHID_MAX_LEN = 100
)

func NewWorker(config util.JsMap) *Worker {
	return &Worker{log: util.NewHekaLogger(config), state: INACTIVE}
}

func (self *Worker) sniffer(sock PushWS, in chan util.JsMap) {
	// Sniff the websocket for incoming data.
	// Reading from the websocket is a blocking operation, and we also
	// need to write out when an even occurs. This isolates the incoming
	// reads to a separate go process.
	var socket = sock.Socket
	for {
		var raw []byte
		var buffer util.JsMap
		err := websocket.Message.Receive(socket, &raw)
		if err != nil {
			self.log.Error("worker",
				fmt.Sprintf("Websocket Error %s", err), nil)
			break
		}
		if len(raw) > 0 {
			self.log.Info("worker",
				fmt.Sprintf("Socket received %s", string(raw)), nil)
			err := json.Unmarshal(raw, &buffer)
			if err != nil {
				self.log.Error("worker",
					"Unparsable data", util.JsMap{"raw": raw})
				break
			}
			self.log.Info("worker",
				fmt.Sprintf("Socket sending %s", buffer), nil)
			// Only do something if there's something to do.
			in <- buffer
		}
	}
	// Clean up the server side (This will delete records associated
	// with the UAID.
	sock.Scmd <- PushCommand{Command: DIE, Arguments: nil}
	socket.Close()
}

// standardize the error reporting back to the client.
func (self *Worker) handleError(sock PushWS, message util.JsMap, err error) (ret error) {
	self.log.Info("worker", fmt.Sprintf("Sending error %s", err), nil)
	message["status"] = sperrors.ErrToStatus(err)
	message["error"] = err.Error()
	return websocket.JSON.Send(sock.Socket, message)
}

// General workhorse loop for the websocket handler.
func (self *Worker) Run(sock PushWS) {
	var err error

	// Instantiate a websocket reader, a blocking operation
	// (Remember, we need to be able to write out PUSH events
	// as they happen.)
	in := make(chan util.JsMap)
	go self.sniffer(sock, in)

	for {
		select {
		case cmd := <-sock.Ccmd:
			// A new Push has happened. Flush out the data to the
			// device (and potentially remotely wake it if that fails)
			self.log.Info("worker",
				fmt.Sprintf("Client Run cmd: %d", cmd.Command),
				nil)
			if cmd.Command == FLUSH {
				self.log.Info("worker",
					fmt.Sprintf("Flushing... %s", sock.Uaid), nil)
				self.Flush(sock, time.Now().UTC().Unix())
				// additional non-client commands are TBD.
			}
		case buffer := <-in:
			defer func(sock PushWS) {
				if r := recover(); r != nil {
					sock.Logger.Error("worker", r.(error).Error(), nil)
				}
				sock.Scmd <- PushCommand{Command: DIE, Arguments: nil}
				sock.Socket.Close()
				return
			}(sock)

			self.log.Info("worker",
				fmt.Sprintf("Client Read buffer, %s %d\n", buffer,
					len(buffer)), nil)
			if len(buffer) == 0 {
                // Empty buffers are "pings"
				buffer["messageType"] = "ping"
			}
			// process the client commands
			if _, ok := buffer["messageType"]; !ok {
				self.log.Info("worker", "Invalid message",
					util.JsMap{"reason": "Missing messageType",
						"data": buffer})
				self.handleError(sock,
					util.JsMap{},
					sperrors.UnknownCommandError)
				break
			}
			switch strings.ToLower(buffer["messageType"].(string)) {
			case "hello":
				err = self.Hello(&sock, buffer)
			case "ack":
				err = self.Ack(sock, buffer)
			case "register":
				err = self.Register(sock, buffer)
				self.log.Info("worker", fmt.Sprintf("Reg result:: %s", err), nil)
			case "unregister":
				err = self.Unregister(sock, buffer)
			case "ping":
				err = self.Ping(sock, buffer)
			default:
				self.log.Warn("worker",
					fmt.Sprintf("I have no idea what [%s] is.", buffer),
					nil)
				err = sperrors.UnknownCommandError
			}
			if err != nil {
				self.handleError(sock, buffer, err)
				break
			}
		}
	}
	sock.Scmd <- PushCommand{Command: DIE, Arguments: nil}
	sock.Socket.Close()
}

// Associate the UAID for this socket connection (and flush any data that
// may be pending for the connection)
func (self *Worker) Hello(sock *PushWS, buffer interface{}) (err error) {
	// register the UAID
	defer func() {
		if r := recover(); r != nil {
			self.log.Error("worker",
				fmt.Sprintf("Unhandled error in Hello: %s", r),
				nil)
			err = sperrors.InvalidDataError
		}
	}()

	data := buffer.(util.JsMap)
	if _, ok := data["uaid"]; !ok {
		// Must include "uaid" (even if blank)
		return sperrors.MissingDataError
	}
	if data["channelIDs"] == nil {
		// Must include "channelIDs" (even if empty)
		return sperrors.MissingDataError
	}
	if len(sock.Uaid) > 0 && len(data["uaid"].(string)) > 0 && sock.Uaid != data["uaid"].(string) {
		// if there's already a Uaid for this channel, don't accept a new one
		return sperrors.InvalidCommandError
	}
	if len(sock.Uaid) == 0 {
		// if there's no UAID for the socket, accept or create a new one.
		sock.Uaid = data["uaid"].(string)
		if len(sock.Uaid) > UAID_MAX_LEN {
			return sperrors.InvalidDataError
		}
		if len(sock.Uaid) == 0 {
			sock.Uaid, _ = GenUUID4()
		}
	}
	// register the sockets (NOOP)
	// register any proprietary connection requirements
	// alert the master of the new UAID.
	cmd := PushCommand{Command: HELLO,
		Arguments: util.JsMap{
			"uaid":  sock.Uaid,
			"chids": data["channelIDs"]}}
	// blocking call back to the boss.
	sock.Scmd <- cmd
	result := <-sock.Scmd
	if err = sock.Store.SetUAIDHost(sock.Uaid); err != nil {
		return err
	}

	self.log.Info("worker", "sending HELLO response....",
		util.JsMap{"uaid": sock.Uaid})
	websocket.JSON.Send(sock.Socket, util.JsMap{
		"messageType": data["messageType"],
		"status":      result.Command,
		"uaid":        sock.Uaid})
	self.state = ACTIVE
	if err == nil {
		// Get the lastAccessed time from wherever
		self.Flush(*sock, 0)
	}
	return err
}

// Clear the data that the client stated it received, then re-flush any
// records (including new data)
func (self *Worker) Ack(sock PushWS, buffer interface{}) (err error) {
	defer func() {
		if r := recover(); r != nil {
			self.log.Error("worker",
				fmt.Sprintf("Unhandled error in Ack: %s", r),
				nil)
			err = sperrors.InvalidDataError
		}
	}()

	if sock.Uaid == "" {
		return sperrors.InvalidCommandError
	}
	data := buffer.(util.JsMap)
	if data["updates"] == nil {
		return sperrors.MissingDataError
	}
	err = sock.Store.Ack(sock.Uaid, data)
	// Get the lastAccessed time from wherever.
	if err == nil {
		websocket.JSON.Send(sock.Socket, util.JsMap{
			"messageType": data["messageType"],
			"status":      200})
		self.Flush(sock, 0)
		return nil
	}
	self.log.Info("worker", fmt.Sprintf("Flushed ACK returning %s", err), nil)
	return err
}

// Register a new ChannelID. Optionally, encrypt the endpoint.
func (self *Worker) Register(sock PushWS, buffer interface{}) (err error) {
	defer func() {
		if r := recover(); r != nil {
			self.log.Error("worker",
				fmt.Sprintf("Unhandled error in Register: %s", r),
				nil)
			err = sperrors.InvalidDataError
		}
	}()

	if sock.Uaid == "" {
		return sperrors.InvalidCommandError
	}
	data := buffer.(util.JsMap)
	if data["channelID"] == nil {
		return sperrors.MissingDataError
	}
	appid := data["channelID"].(string)
	if len(appid) > CHID_MAX_LEN {
		return sperrors.InvalidDataError
	}
	err = sock.Store.RegisterAppID(sock.Uaid, appid, "")
	if err != nil {
		self.log.Error("worker",
			fmt.Sprintf("ERROR: RegisterAppID failed %s", err),
			nil)
		return err
	}
	// have the server generate the callback URL.
	cmd := PushCommand{Command: REGIS,
		Arguments: data}
	sock.Scmd <- cmd
	result := <-sock.Scmd
	self.log.Debug("worker", fmt.Sprintf("Server returned %s", result), nil)
	endpoint := result.Arguments.(util.JsMap)["pushEndpoint"].(string)
	// return the info back to the socket
	self.log.Info("worker", "Sending REGIS response....", nil)
	websocket.JSON.Send(sock.Socket, util.JsMap{
		"messageType":  data["messageType"],
		"status":       result.Command,
		"pushEndpoint": endpoint})
	return err
}

// Unregister a ChannelID.
func (self *Worker) Unregister(sock PushWS, buffer interface{}) (err error) {
	defer func() {
		if r := recover(); r != nil {
			self.log.Error("worker",
				fmt.Sprintf("Unhandled error in Unregister: %s", r),
				nil)
			err = sperrors.InvalidDataError
		}
	}()
	if sock.Uaid == "" {
		return sperrors.InvalidCommandError
	}
	data := buffer.(util.JsMap)
	if data["channelID"] == nil {
		return sperrors.MissingDataError
	}
	appid := data["channelID"].(string)
	// Always return success for an UNREG.
	sock.Store.DeleteAppID(sock.Uaid, appid, false)
	self.log.Info("worker", "Sending UNREG response ..", nil)
	websocket.JSON.Send(sock.Socket, util.JsMap{
		"messageType": data["messageType"],
		"status":      200,
		"channelID":   appid})
	return err
}

// Dump any records associated with the UAID.
func (self *Worker) Flush(sock PushWS, lastAccessed int64) {
	// flush pending data back to Client
	messageType := "notification"
	timer := time.Now()
	defer func(timer time.Time, sock PushWS) {
		sock.Logger.Info("timer",
			"Client flush completed",
			util.JsMap{"duration": time.Now().Sub(timer).Nanoseconds(),
				"uaid": sock.Uaid})
	}(timer, sock)
	if sock.Uaid == "" {
		self.log.Error("worker", "Undefined UAID for socket. Aborting.", nil)
		// Have the server clean up records associated with this UAID.
		// (Probably "none", but still good for housekeeping)
		sock.Scmd <- PushCommand{Command: DIE, Arguments: nil}
		sock.Socket.Close()
	}
	// Fetch the pending updates from #storage
	updates, err := sock.Store.GetUpdates(sock.Uaid, lastAccessed)
	if err != nil {
		self.handleError(sock, util.JsMap{"messageType": messageType}, err)
		return
	}
	if updates == nil {
		return
	}
	updates["messageType"] = messageType
	self.log.Info("worker", "Flushing data back to socket", updates)
	websocket.JSON.Send(sock.Socket, updates)
}

func (self *Worker) Ping(sock PushWS, buffer interface{}) (err error) {
	data := buffer.(util.JsMap)
	websocket.JSON.Send(sock.Socket, util.JsMap{
		"messageType": data["messageType"],
		"status":      200})
	return nil
}

// o4fs
// vim: set tabstab=4 softtabstop=4 shiftwidth=4 noexpandtab
<|endoftext|>


<commit_before>package booklit

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/agext/levenshtein"
	"github.com/vito/booklit/ast"
)

type Section struct {
	Path string

	Title Content
	Body  Content

	PrimaryTag Tag
	Tags       []Tag

	Parent   *Section
	Children []*Section

	Style    string
	Partials Partials

	SplitSections        bool
	PreventSplitSections bool

	ResetDepth bool

	OmitChildrenFromTableOfContents bool

	Processor       SectionProcessor
	PluginFactories []PluginFactory
	Plugins         []Plugin

	Location       ast.Location
	InvokeLocation ast.Location
}

type Partials map[string]Content

type SectionProcessor interface {
	EvaluateFile(*Section, string, []PluginFactory) (*Section, error)
	EvaluateNode(*Section, ast.Node, []PluginFactory) (*Section, error)
}

type Tag struct {
	Name  string
	Title Content

	Section  *Section
	Location ast.Location
	Anchor   string

	Content Content
}

func (con *Section) String() string {
	return fmt.Sprintf("{section (%s): %s}", con.Path, con.Title)
}

func (con *Section) FilePath() string {
	if con.Path != "" {
		return con.Path
	}

	if con.Parent != nil {
		return con.Parent.FilePath()
	}

	return ""
}

func (con *Section) IsFlow() bool {
	return false
}

func (con *Section) Visit(visitor Visitor) error {
	return visitor.VisitSection(con)
}

func (con *Section) SetTitle(title Content, loc ast.Location, tags ...string) {
	if len(tags) == 0 {
		tags = []string{con.defaultTag(title)}
	}

	con.Tags = []Tag{}
	for _, name := range tags {
		con.SetTag(name, title, loc)
	}

	con.Title = title
	con.PrimaryTag = con.Tags[0]
}

func (con *Section) SetTag(name string, title Content, loc ast.Location, optionalAnchor ...string) {
	anchor := ""
	if len(optionalAnchor) > 0 {
		anchor = optionalAnchor[0]
	}

	con.Tags = append(con.Tags, Tag{
		Section:  con,
		Location: loc,

		Name:   name,
		Title:  title,
		Anchor: anchor,
	})
}

func (con *Section) SetTagAnchored(name string, title Content, loc ast.Location, content Content, anchor string) {
	con.Tags = append(con.Tags, Tag{
		Section:  con,
		Location: loc,

		Name:   name,
		Title:  title,
		Anchor: anchor,

		Content: content,
	})
}

func (con *Section) Number() string {
	if con.Parent == nil {
		return ""
	}

	parentNumber := con.Parent.Number()
	selfIndex := 1
	for _, child := range con.Parent.Children {
		if child == con {
			break
		}

		selfIndex++
	}

	if parentNumber == "" {
		return fmt.Sprintf("%d", selfIndex)
	}

	return fmt.Sprintf("%s.%d", parentNumber, selfIndex)
}

func (con *Section) HasAnchors() bool {
	for _, tag := range con.Tags {
		if tag.Anchor != "" {
			return true
		}
	}

	if con.SplitSections {
		return false
	}

	for _, child := range con.Children {
		if child.HasAnchors() {
			return true
		}
	}

	return false
}

func (con *Section) AnchorTags() []Tag {
	tags := []Tag{}

	for _, tag := range con.Tags {
		if tag.Anchor == "" {
			continue
		}

		tags = append(tags, tag)
	}

	return tags
}

func (con *Section) Top() *Section {
	if con.Parent != nil {
		return con.Parent.Top()
	}

	return con
}

func (con *Section) Contains(sub *Section) bool {
	if con == sub {
		return true
	}

	for _, child := range con.Children {
		if child.Contains(sub) {
			return true
		}
	}

	return false
}

func (con *Section) IsOrHasChild(sub *Section) bool {
	if con == sub {
		return true
	}

	for _, child := range con.Children {
		if child == sub {
			return true
		}
	}

	return false
}

func (con *Section) Prev() *Section {
	if con.Parent == nil {
		return nil
	}

	var lastChild *Section
	for _, child := range con.Parent.Children {
		if lastChild != nil && child == con {
			return lastChild
		}

		lastChild = child
	}

	return con.Parent
}

func (con *Section) Next() *Section {
	if con.SplitSections {
		if len(con.Children) > 0 {
			return con.Children[0]
		}
	}

	return con.NextSibling()
}

func (con *Section) NextSibling() *Section {
	if con.Parent == nil {
		return nil
	}

	var sawSelf bool
	for _, child := range con.Parent.Children {
		if sawSelf {
			return child
		}

		if child == con {
			sawSelf = true
		}
	}

	return con.Parent.NextSibling()
}

func (con *Section) FindTag(tagName string) []Tag {
	return con.filterTags(true, nil, func(other string) bool {
		return other == tagName
	})
}

func (con *Section) SimilarTags(tagName string) []Tag {
	return con.filterTags(true, nil, func(other string) bool {
		return levenshtein.Match(tagName, other, nil) > 0.5
	})
}

func (con *Section) SetPartial(name string, value Content) {
	if con.Partials == nil {
		con.Partials = Partials{}
	}

	con.Partials[name] = value
}

func (con *Section) Partial(name string) Content {
	return con.Partials[name]
}

func (con *Section) UsePlugin(pf PluginFactory) {
	con.PluginFactories = append(con.PluginFactories, pf)
	con.Plugins = append(con.Plugins, pf(con))
}

func (con *Section) Depth() int {
	if con.Parent == nil {
		return 0
	}

	return con.Parent.Depth() + 1
}

func (con *Section) PageDepth() int {
	if con.Parent == nil || con.Parent.ResetDepth {
		return 0
	}

	return con.Parent.PageDepth() + 1
}

func (con *Section) SplitSectionsPrevented() bool {
	if con.PreventSplitSections {
		return true
	}

	if con.Parent != nil && con.Parent.SplitSectionsPrevented() {
		return true
	}

	return false
}

func (con *Section) filterTags(up bool, exclude *Section, match func(string) bool) []Tag {
	tags := []Tag{}

	if match(con.Title.String()) {
		tags = append(tags, con.PrimaryTag)
	}

	for _, t := range con.Tags {
		if match(t.Name) {
			tags = append(tags, t)
		}
	}

	for _, sub := range con.Children {
		if sub != exclude {
			tags = append(tags, sub.filterTags(false, nil, match)...)
		}
	}

	if up && con.Parent != nil {
		tags = append(tags, con.Parent.filterTags(true, con, match)...)
	}

	return tags
}

var whitespaceRegexp = regexp.MustCompile(`\s+`)
var specialCharsRegexp = regexp.MustCompile(`[^[:alnum:]_\-]`)

func (con *Section) defaultTag(title Content) string {
	return strings.ToLower(
		specialCharsRegexp.ReplaceAllString(
			whitespaceRegexp.ReplaceAllString(
				strings.Replace(
					StripAux(title).String(),
					" & ",
					" and ",
					-1,
				),
				"-",
			),
			"",
		),
	)
}
<commit_msg>Revert "Revert "revert this: adding function with no test coverage""<commit_after>package booklit

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/agext/levenshtein"
	"github.com/vito/booklit/ast"
)

type Section struct {
	Path string

	Title Content
	Body  Content

	PrimaryTag Tag
	Tags       []Tag

	Parent   *Section
	Children []*Section

	Style    string
	Partials Partials

	SplitSections        bool
	PreventSplitSections bool

	ResetDepth bool

	OmitChildrenFromTableOfContents bool

	Processor       SectionProcessor
	PluginFactories []PluginFactory
	Plugins         []Plugin

	Location       ast.Location
	InvokeLocation ast.Location
}

type Partials map[string]Content

type SectionProcessor interface {
	EvaluateFile(*Section, string, []PluginFactory) (*Section, error)
	EvaluateNode(*Section, ast.Node, []PluginFactory) (*Section, error)
}

type Tag struct {
	Name  string
	Title Content

	Section  *Section
	Location ast.Location
	Anchor   string

	Content Content
}

func (con *Section) String() string {
	return fmt.Sprintf("{section (%s): %s}", con.Path, con.Title)
}

func (con *Section) FilePath() string {
	if con.Path != "" {
		return con.Path
	}

	if con.Parent != nil {
		return con.Parent.FilePath()
	}

	return ""
}

func (con *Section) IsFlow() bool {
	return false
}

func (con *Section) Visit(visitor Visitor) error {
	return visitor.VisitSection(con)
}

func (con *Section) SetTitle(title Content, loc ast.Location, tags ...string) {
	if len(tags) == 0 {
		tags = []string{con.defaultTag(title)}
	}

	con.Tags = []Tag{}
	for _, name := range tags {
		con.SetTag(name, title, loc)
	}

	con.Title = title
	con.PrimaryTag = con.Tags[0]
}

func (con *Section) SetTag(name string, title Content, loc ast.Location, optionalAnchor ...string) {
	anchor := ""
	if len(optionalAnchor) > 0 {
		anchor = optionalAnchor[0]
	}

	con.Tags = append(con.Tags, Tag{
		Section:  con,
		Location: loc,

		Name:   name,
		Title:  title,
		Anchor: anchor,
	})
}

func (con *Section) SetTagAnchored(name string, title Content, loc ast.Location, content Content, anchor string) {
	con.Tags = append(con.Tags, Tag{
		Section:  con,
		Location: loc,

		Name:   name,
		Title:  title,
		Anchor: anchor,

		Content: content,
	})
}

func (con *Section) Number() string {
	if con.Parent == nil {
		return ""
	}

	parentNumber := con.Parent.Number()
	selfIndex := 1
	for _, child := range con.Parent.Children {
		if child == con {
			break
		}

		selfIndex++
	}

	if parentNumber == "" {
		return fmt.Sprintf("%d", selfIndex)
	}

	return fmt.Sprintf("%s.%d", parentNumber, selfIndex)
}

func (con *Section) HasAnchors() bool {
	for _, tag := range con.Tags {
		if tag.Anchor != "" {
			return true
		}
	}

	if con.SplitSections {
		return false
	}

	for _, child := range con.Children {
		if child.HasAnchors() {
			return true
		}
	}

	return false
}

func (con *Section) AnchorTags() []Tag {
	tags := []Tag{}

	for _, tag := range con.Tags {
		if tag.Anchor == "" {
			continue
		}

		tags = append(tags, tag)
	}

	return tags
}

func (con *Section) Top() *Section {
	if con.Parent != nil {
		return con.Parent.Top()
	}

	return con
}

func (con *Section) Contains(sub *Section) bool {
	if con == sub {
		return true
	}

	for _, child := range con.Children {
		if child.Contains(sub) {
			return true
		}
	}

	return false
}

func (con *Section) IsOrHasChild(sub *Section) bool {
	if con == sub {
		return true
	}

	for _, child := range con.Children {
		if child == sub {
			return true
		}
	}

	return false
}

func (con *Section) Prev() *Section {
	if con.Parent == nil {
		return nil
	}

	var lastChild *Section
	for _, child := range con.Parent.Children {
		if lastChild != nil && child == con {
			return lastChild
		}

		lastChild = child
	}

	return con.Parent
}

func (con *Section) Next() *Section {
	if con.SplitSections {
		if len(con.Children) > 0 {
			return con.Children[0]
		}
	}

	return con.NextSibling()
}

func (con *Section) NextSibling() *Section {
	if con.Parent == nil {
		return nil
	}

	var sawSelf bool
	for _, child := range con.Parent.Children {
		if sawSelf {
			return child
		}

		if child == con {
			sawSelf = true
		}
	}

	return con.Parent.NextSibling()
}

func (con *Section) FindTag(tagName string) []Tag {
	return con.filterTags(true, nil, func(other string) bool {
		return other == tagName
	})
}

func (con *Section) SimilarTags(tagName string) []Tag {
	return con.filterTags(true, nil, func(other string) bool {
		return levenshtein.Match(tagName, other, nil) > 0.5
	})
}

func (con *Section) SetPartial(name string, value Content) {
	if con.Partials == nil {
		con.Partials = Partials{}
	}

	con.Partials[name] = value
}

func (con *Section) Partial(name string) Content {
	return con.Partials[name]
}

func (con *Section) UsePlugin(pf PluginFactory) {
	con.PluginFactories = append(con.PluginFactories, pf)
	con.Plugins = append(con.Plugins, pf(con))
}

func (con *Section) Depth() int {
	if con.Parent == nil {
		return 0
	}

	return con.Parent.Depth() + 1
}

func (con *Section) DepthPlusOne() int {
	if con.Parent == nil {
		return 1
	}

	return con.Parent.Depth() + 1
}

func (con *Section) PageDepth() int {
	if con.Parent == nil || con.Parent.ResetDepth {
		return 0
	}

	return con.Parent.PageDepth() + 1
}

func (con *Section) SplitSectionsPrevented() bool {
	if con.PreventSplitSections {
		return true
	}

	if con.Parent != nil && con.Parent.SplitSectionsPrevented() {
		return true
	}

	return false
}

func (con *Section) filterTags(up bool, exclude *Section, match func(string) bool) []Tag {
	tags := []Tag{}

	if match(con.Title.String()) {
		tags = append(tags, con.PrimaryTag)
	}

	for _, t := range con.Tags {
		if match(t.Name) {
			tags = append(tags, t)
		}
	}

	for _, sub := range con.Children {
		if sub != exclude {
			tags = append(tags, sub.filterTags(false, nil, match)...)
		}
	}

	if up && con.Parent != nil {
		tags = append(tags, con.Parent.filterTags(true, con, match)...)
	}

	return tags
}

var whitespaceRegexp = regexp.MustCompile(`\s+`)
var specialCharsRegexp = regexp.MustCompile(`[^[:alnum:]_\-]`)

func (con *Section) defaultTag(title Content) string {
	return strings.ToLower(
		specialCharsRegexp.ReplaceAllString(
			whitespaceRegexp.ReplaceAllString(
				strings.Replace(
					StripAux(title).String(),
					" & ",
					" and ",
					-1,
				),
				"-",
			),
			"",
		),
	)
}
<|endoftext|>


<commit_before>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package precis

import "testing"

type testCase struct {
	input, output string
	isErr         bool
}

var testCases = []struct {
	name  string
	p     *Profile
	cases []testCase
}{
	{"Nickname", Nickname, []testCase{
		{"  Swan  of   Avon   ", "Swan of Avon", false},
		{"", "", true},
		{" ", "", true},
		{"  ", "", true},
		{"a\u00A0a\u1680a\u2000a\u2001a\u2002a\u2003a\u2004a\u2005a\u2006a\u2007a\u2008a\u2009a\u200Aa\u202Fa\u205Fa\u3000a", "a a a a a a a a a a a a a a a a a", false},
		{"Foo", "Foo", false},
		{"foo", "foo", false},
		{"Foo Bar", "Foo Bar", false},
		{"foo bar", "foo bar", false},
		{"\u03C3", "\u03C3", false},
		// Greek final sigma is left as is (do not fold!)
		{"\u03C2", "\u03C2", false},
		{"\u265A", "♚", false},
		{"Richard \u2163", "Richard IV", false},
		{"\u212B", "Å", false},
		{"\uFB00", "ff", false}, // because of NFKC
		{"שa", "שa", false},     // no bidi rule
		{"동일조건변경허락", "동일조건변경허락", false},
	}},
	{"OpaqueString", OpaqueString, []testCase{
		{"  Swan  of   Avon   ", "  Swan  of   Avon   ", false},
		{"", "", true},
		{" ", " ", false},
		{"  ", "  ", false},
		{"a\u00A0a\u1680a\u2000a\u2001a\u2002a\u2003a\u2004a\u2005a\u2006a\u2007a\u2008a\u2009a\u200Aa\u202Fa\u205Fa\u3000a", "a a a a a a a a a a a a a a a a a", false},
		{"Foo", "Foo", false},
		{"foo", "foo", false},
		{"Foo Bar", "Foo Bar", false},
		{"foo bar", "foo bar", false},
		{"\u03C3", "\u03C3", false},
		{"Richard \u2163", "Richard \u2163", false},
		{"\u212B", "Å", false},
		{"Jack of \u2666s", "Jack of \u2666s", false},
		{"my cat is a \u0009by", "", true},
		{"·", "", true}, // Middle dot
		{"͵", "", true}, // Keraia
		{"׳", "", true},
		{"׳ה", "", true},
		{"a׳b", "", true},
		// TODO: Requires context rule to work.
		// {OpaqueString, "ש׳", "ש", false},  // U+05e9 U+05f3
		{"שa", "שa", false}, // no bidi rule

		// Katakana Middle Dot
		{"abc・def", "", true},
		// TODO: These require context rules to work.
		// {OpaqueString, "aヅc・def", "", false},
		// {OpaqueString, "abc・dぶf", "", false},
		// {OpaqueString, "⺐bc・def", "", false},

		// Arabic Indic Digit
		// TODO: These require context rules to work.
		// {OpaqueString, "١٢٣٤٥", "١٢٣٤٥", false},
		// {OpaqueString, "۱۲۳۴۵", "۱۲۳۴۵", false},
		{"١٢٣٤٥۶", "", true},
		{"۱۲۳۴۵٦", "", true},
	}},
	{"UsernameCaseMapped", UsernameCaseMapped, []testCase{
		// TODO: Should this work?
		// {UsernameCaseMapped, "", "", true},
		{"juliet@example.com", "juliet@example.com", false},
		{"fussball", "fussball", false},
		{"fu\u00DFball", "fussball", false},
		{"\u03C0", "\u03C0", false},
		{"\u03A3", "\u03C3", false},
		{"\u03C3", "\u03C3", false},
		{"\u03C2", "\u03C3", false},
		{"\u0049", "\u0069", false},
		{"\u0049", "\u0069", false},
		{"\u03D2", "\u03C5", true},
		{"\u03B0", "\u03B0", false},
		{"foo bar", "", true},
		{"♚", "", true},
		{"\u007E", "", true}, // disallowed by bidi rule
		{"a", "a", false},
		{"!", "", true}, // disallowed by bidi rule
		{"²", "", true},
		{"\t", "", true},
		{"\n", "", true},
		{"\u26D6", "", true},
		{"\u26FF", "", true},
		{"\uFB00", "ff", false}, // Side effect of case folding.
		{"\u1680", "", true},
		{" ", "", true},
		{"  ", "", true},
		{"\u01C5", "", true},
		{"\u16EE", "", true},         // Nl RUNIC ARLAUG SYMBOL
		{"\u0488", "", true},         // Me COMBINING CYRILLIC HUNDRED THOUSANDS SIGN
		{"\u212B", "\u00e5", false},  // Angstrom sign, NFC -> U+00E5
		{"A\u030A", "å", false},      // A + ring
		{"\u00C5", "å", false},       // A with ring
		{"\u00E7", "ç", false},       // c cedille
		{"\u0063\u0327", "ç", false}, // c + cedille
		{"\u0158", "ř", false},
		{"\u0052\u030C", "ř", false},

		{"\u1E61", "\u1E61", false}, // LATIN SMALL LETTER S WITH DOT ABOVE
		// U+1e9B: case folded.
		{"ẛ", "\u1E61", false}, // LATIN SMALL LETTER LONG S WITH DOT ABOVE

		// Confusable characters ARE allowed and should NOT be mapped.
		{"\u0410", "\u0430", false}, // CYRILLIC CAPITAL LETTER A

		// Full width should be mapped to the canonical decomposition.
		{"ＡＢ", "ab", false},
		{"שc", "שc", true}, // bidi rule

	}},
	{"UsernameCasePreserved", UsernameCasePreserved, []testCase{
		{"ABC", "ABC", false},
		{"ＡＢ", "AB", false},
		{"שc", "שc", true}, // bidi rule
		{"\uFB00", "", true},
		{"\u212B", "\u00c5", false}, // Angstrom sign, NFC -> U+00E5
		{"ẛ", "", true},             // LATIN SMALL LETTER LONG S WITH DOT ABOVE
	}},
}

func TestString(t *testing.T) {
	doTests(t, func(t *testing.T, p *Profile, tc testCase) {
		if e, err := p.String(tc.input); (tc.isErr && err == nil) ||
			!tc.isErr && (err != nil || e != tc.output) {
			t.Errorf("got %+q (err: %v); want %+q (ok: %v)", e, err, tc.output, !tc.isErr)
		}
	})
}

func TestBytes(t *testing.T) {
	doTests(t, func(t *testing.T, p *Profile, tc testCase) {
		if e, err := p.Bytes([]byte(tc.input)); (tc.isErr && err == nil) ||
			!tc.isErr && (err != nil || string(e) != tc.output) {
			t.Errorf("got %+q (err: %v); want %+q (ok: %v)", string(e), err, tc.output, !tc.isErr)
		}
	})
}

func TestAppend(t *testing.T) {
	doTests(t, func(t *testing.T, p *Profile, tc testCase) {
		if e, err := p.Append(nil, []byte(tc.input)); (tc.isErr && err == nil) ||
			!tc.isErr && (err != nil || string(e) != tc.output) {
			t.Errorf("got %+q (err: %v); want %+q (ok: %v)", string(e), err, tc.output, !tc.isErr)
		}
	})
}
<commit_msg>secure/precis: simplified tests<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package precis

import (
	"testing"

	"golang.org/x/text/secure/bidirule"
)

type testCase struct {
	input  string
	output string
	err    error
}

var testCases = []struct {
	name  string
	p     *Profile
	cases []testCase
}{
	{"Nickname", Nickname, []testCase{
		{"  Swan  of   Avon   ", "Swan of Avon", nil},
		{"", "", errEmptyString},
		{" ", "", errEmptyString},
		{"  ", "", errEmptyString},
		{"a\u00A0a\u1680a\u2000a\u2001a\u2002a\u2003a\u2004a\u2005a\u2006a\u2007a\u2008a\u2009a\u200Aa\u202Fa\u205Fa\u3000a", "a a a a a a a a a a a a a a a a a", nil},
		{"Foo", "Foo", nil},
		{"foo", "foo", nil},
		{"Foo Bar", "Foo Bar", nil},
		{"foo bar", "foo bar", nil},
		{"\u03C3", "\u03C3", nil},
		// Greek final sigma is left as is (do not fold!)
		{"\u03C2", "\u03C2", nil},
		{"\u265A", "♚", nil},
		{"Richard \u2163", "Richard IV", nil},
		{"\u212B", "Å", nil},
		{"\uFB00", "ff", nil}, // because of NFKC
		{"שa", "שa", nil},     // no bidi rule
		{"동일조건변경허락", "동일조건변경허락", nil},
	}},
	{"OpaqueString", OpaqueString, []testCase{
		{"  Swan  of   Avon   ", "  Swan  of   Avon   ", nil},
		{"", "", errEmptyString},
		{" ", " ", nil},
		{"  ", "  ", nil},
		{"a\u00A0a\u1680a\u2000a\u2001a\u2002a\u2003a\u2004a\u2005a\u2006a\u2007a\u2008a\u2009a\u200Aa\u202Fa\u205Fa\u3000a", "a a a a a a a a a a a a a a a a a", nil},
		{"Foo", "Foo", nil},
		{"foo", "foo", nil},
		{"Foo Bar", "Foo Bar", nil},
		{"foo bar", "foo bar", nil},
		{"\u03C3", "\u03C3", nil},
		{"Richard \u2163", "Richard \u2163", nil},
		{"\u212B", "Å", nil},
		{"Jack of \u2666s", "Jack of \u2666s", nil},
		{"my cat is a \u0009by", "", errDisallowedRune},
		{"·", "", errDisallowedRune}, // Middle dot
		{"͵", "", errDisallowedRune}, // Keraia
		{"׳", "", errDisallowedRune},
		{"׳ה", "", errDisallowedRune},
		{"a׳b", "", errDisallowedRune},
		// TODO: Requires context rule to work.
		// {OpaqueString, "ש׳", "ש", nil},  // U+05e9 U+05f3
		{"שa", "שa", nil}, // no bidi rule

		// Katakana Middle Dot
		{"abc・def", "", errDisallowedRune},
		// TODO: These require context rules to work.
		// {OpaqueString, "aヅc・def", "", nil},
		// {OpaqueString, "abc・dぶf", "", nil},
		// {OpaqueString, "⺐bc・def", "", nil},

		// Arabic Indic Digit
		// TODO: These require context rules to work.
		// {OpaqueString, "١٢٣٤٥", "١٢٣٤٥", nil},
		// {OpaqueString, "۱۲۳۴۵", "۱۲۳۴۵", nil},
		{"١٢٣٤٥۶", "", errDisallowedRune},
		{"۱۲۳۴۵٦", "", errDisallowedRune},
	}},
	{"UsernameCaseMapped", UsernameCaseMapped, []testCase{
		// TODO: Should this work?
		// {UsernameCaseMapped, "", "", errDisallowedRune},
		{"juliet@example.com", "juliet@example.com", nil},
		{"fussball", "fussball", nil},
		{"fu\u00DFball", "fussball", nil},
		{"\u03C0", "\u03C0", nil},
		{"\u03A3", "\u03C3", nil},
		{"\u03C3", "\u03C3", nil},
		{"\u03C2", "\u03C3", nil},
		{"\u0049", "\u0069", nil},
		{"\u0049", "\u0069", nil},
		{"\u03D2", "", errDisallowedRune},
		{"\u03B0", "\u03B0", nil},
		{"foo bar", "", bidirule.ErrInvalid},
		{"♚", "", bidirule.ErrInvalid},
		{"\u007E", "", bidirule.ErrInvalid}, // disallowed by bidi rule
		{"a", "a", nil},
		{"!", "", bidirule.ErrInvalid}, // disallowed by bidi rule
		{"²", "", bidirule.ErrInvalid},
		{"\t", "", bidirule.ErrInvalid},
		{"\n", "", bidirule.ErrInvalid},
		{"\u26D6", "", bidirule.ErrInvalid},
		{"\u26FF", "", bidirule.ErrInvalid},
		{"\uFB00", "ff", nil}, // Side effect of case folding.
		{"\u1680", "", bidirule.ErrInvalid},
		{" ", "", bidirule.ErrInvalid},
		{"  ", "", bidirule.ErrInvalid},
		{"\u01C5", "", errDisallowedRune},
		{"\u16EE", "", errDisallowedRune},   // Nl RUNIC ARLAUG SYMBOL
		{"\u0488", "", bidirule.ErrInvalid}, // Me COMBINING CYRILLIC HUNDRED THOUSANDS SIGN
		{"\u212B", "\u00e5", nil},           // Angstrom sign, NFC -> U+00E5
		{"A\u030A", "å", nil},               // A + ring
		{"\u00C5", "å", nil},                // A with ring
		{"\u00E7", "ç", nil},                // c cedille
		{"\u0063\u0327", "ç", nil},          // c + cedille
		{"\u0158", "ř", nil},
		{"\u0052\u030C", "ř", nil},

		{"\u1E61", "\u1E61", nil}, // LATIN SMALL LETTER S WITH DOT ABOVE
		// U+1e9B: case folded.
		{"ẛ", "\u1E61", nil}, // LATIN SMALL LETTER LONG S WITH DOT ABOVE

		// Confusable characters ARE allowed and should NOT be mapped.
		{"\u0410", "\u0430", nil}, // CYRILLIC CAPITAL LETTER A

		// Full width should be mapped to the canonical decomposition.
		{"ＡＢ", "ab", nil},
		{"שc", "", bidirule.ErrInvalid}, // bidi rule

	}},
	{"UsernameCasePreserved", UsernameCasePreserved, []testCase{
		{"ABC", "ABC", nil},
		{"ＡＢ", "AB", nil},
		{"שc", "", bidirule.ErrInvalid}, // bidi rule
		{"\uFB00", "", errDisallowedRune},
		{"\u212B", "\u00c5", nil},    // Angstrom sign, NFC -> U+00E5
		{"ẛ", "", errDisallowedRune}, // LATIN SMALL LETTER LONG S WITH DOT ABOVE
	}},
}

func TestString(t *testing.T) {
	doTests(t, func(t *testing.T, p *Profile, tc testCase) {
		if e, err := p.String(tc.input); tc.err != err || e != tc.output {
			t.Errorf("got %+q (err: %v); want %+q (err: %v)", e, err, tc.output, tc.err)
		}
	})
}

func TestBytes(t *testing.T) {
	doTests(t, func(t *testing.T, p *Profile, tc testCase) {
		if e, err := p.Bytes([]byte(tc.input)); tc.err != err || string(e) != tc.output {
			t.Errorf("got %+q (err: %v); want %+q (err: %v)", string(e), err, tc.output, tc.err)
		}
	})
}

func TestAppend(t *testing.T) {
	doTests(t, func(t *testing.T, p *Profile, tc testCase) {
		if e, err := p.Append(nil, []byte(tc.input)); tc.err != err || string(e) != tc.output {
			t.Errorf("got %+q (err: %v); want %+q (err: %v)", string(e), err, tc.output, tc.err)
		}
	})
}
<|endoftext|>


<commit_before>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package sensors provides sensor events from various movement and position sensors.
package sensors

import "time"

// Accelerometer represents an accelerometer sensor.
type Accelerometer struct {
	s interface{}
}

// StartAccelerometer starts a new accelerometer.
// Once the accelerometer is no longer in use, it should be stopped
// and its resources should be released by calling Close.
// Delay determines the wait-time to read the next sample from the sensor.
// Its lower limit is bound by the sensor's output bandwidth.
func StartAccelerometer(delay time.Duration) (*Accelerometer, error) {
	s, err := startAccelerometer(delay.Nanoseconds() * 1000)
	if err != nil {
		return nil, err
	}
	return &Accelerometer{s: s}, nil
}

// Read reads new events from the accelerometer event queue.
// It will block until len(e) events are retrieved.
// e is a series of 3-vectors. Each vector contains the acceleration
// force in m/s2 that is applied to the device in x, y and z axes.
//
// 	e[i][0]: acceleration force in x-axis
// 	e[i][1]: acceleration force in y-axis
// 	e[i][2]: acceleration force in z-axis
//
// A call to StartAccelerometer is mandatory to start the accelerometer
// sensor and initialize its event queue.
//
// You have to call this function from the same OS thread that the
// accelerometer has been started. Use runtime.LockOSThread to lock the
// current goroutine to a particular OS thread.
func (a *Accelerometer) Read(e [][]float64) (n int, err error) {
	return readAccelerometer(a.s, e)
}

// Close stops the accelerometer and frees the related resources.
func (a *Accelerometer) Close() error {
	return closeAccelerometer(a.s)
}

// Type of the network that is currently in use.
const (
	TypeWiFi = iota
	TypeMobile
	TypeOther
)

// Connectivity status.
const (
	StatusUnknown = iota
	StatusConnecting
	StatusConnected
	StatusDisconnecting
)

// Connectivity returns the type and the status of the network that is
// currently in use.
func Connectivity() (networkType int, status int) {
	panic("not yet implemented")
}
<commit_msg>remove connectivity temporarily<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package sensors provides sensor events from various movement and position sensors.
package sensors

import "time"

// Accelerometer represents an accelerometer sensor.
type Accelerometer struct {
	s interface{}
}

// StartAccelerometer starts a new accelerometer.
// Once the accelerometer is no longer in use, it should be stopped
// and its resources should be released by calling Close.
// Delay determines the wait-time to read the next sample from the sensor.
// Its lower limit is bound by the sensor's output bandwidth.
func StartAccelerometer(delay time.Duration) (*Accelerometer, error) {
	s, err := startAccelerometer(delay.Nanoseconds() * 1000)
	if err != nil {
		return nil, err
	}
	return &Accelerometer{s: s}, nil
}

// Read reads new events from the accelerometer event queue.
// It will block until len(e) events are retrieved.
// e is a series of 3-vectors. Each vector contains the acceleration
// force in m/s2 that is applied to the device in x, y and z axes.
//
// 	e[i][0]: acceleration force in x-axis
// 	e[i][1]: acceleration force in y-axis
// 	e[i][2]: acceleration force in z-axis
//
// A call to StartAccelerometer is mandatory to start the accelerometer
// sensor and initialize its event queue.
//
// You have to call this function from the same OS thread that the
// accelerometer has been started. Use runtime.LockOSThread to lock the
// current goroutine to a particular OS thread.
func (a *Accelerometer) Read(e [][]float64) (n int, err error) {
	return readAccelerometer(a.s, e)
}

// Close stops the accelerometer and frees the related resources.
func (a *Accelerometer) Close() error {
	return closeAccelerometer(a.s)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math/rand"
	"net"
	"net/http"
	"time"
)

func newMainServer(backends []backend) *http.Server {
	tr := &http.Transport{
		ResponseHeaderTimeout: 2 * time.Second,
	}
	client := &http.Client{Transport: tr}

	serveMux := http.NewServeMux()
	serveMux.Handle("/", requestID(instrument(serviceTime("frontend", mainHandler(client, filterBackends(backends, homePageServices))))))
	serveMux.Handle("/products", requestID(instrument(serviceTime("frontend", productListing(client, filterBackends(backends, productListingServices))))))
	serveMux.Handle("/products/", requestID(instrument(serviceTime("frontend", productDetail(client, filterBackends(backends, productDetailServices))))))
	serveMux.Handle("/categories", requestID(instrument(serviceTime("frontend", categoryListing(client, filterBackends(backends, categoryListingServices))))))
	serveMux.Handle("/categories/", requestID(instrument(serviceTime("frontend", categoryDetail(client, filterBackends(backends, categoryDetailServices))))))
	serveMux.Handle("/search", requestID(instrument(serviceTime("frontend", search(client, filterBackends(backends, searchServices))))))
	serveMux.Handle("/account", requestID(instrument(serviceTime("frontend", account(client, filterBackends(backends, accountServices))))))
	serveMux.Handle("/checkout", requestID(instrument(serviceTime("frontend", checkout(client, filterBackends(backends, checkoutServices))))))

	return newServer(serveMux)
}

func filterBackends(backends []backend, desired map[string]bool) []backend {
	var res []backend

	for _, b := range backends {
		if _, ok := desired[b.name]; ok {
			res = append(res, b)
		}
	}

	return res
}

func newBackends(errorChan chan<- error) []backend {
	backends := make([]backend, 10)

	for i := range backends {
		serveMux := http.NewServeMux()
		name := backendName(i)
		serveMux.Handle("/", requestID(instrument(serviceTime(name, unreliableHandler(rand.Intn(5)+1)))))
		server := newServer(serveMux)
		listener, err := newListener("0")

		if err != nil {
			panic(err)
		}

		go func() {
			errorChan <- server.Serve(listener)
		}()

		backends[i] = backend{
			server:  server,
			address: listener.Addr().String(),
			name:    name,
		}
	}

	return backends
}

func backendName(i int) string {
	nameLen := len(backendServiceNames)
	return fmt.Sprintf("%s", backendServiceNames[i%nameLen])
}

func newListener(port string) (net.Listener, error) {
	return net.Listen("tcp", "0.0.0.0:"+port)
}

func newServer(serveMux http.Handler) *http.Server {
	return &http.Server{
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
		IdleTimeout:  120 * time.Second,
		Handler:      serveMux,
	}
}
<commit_msg>Simplify code<commit_after>package main

import (
	"fmt"
	"math/rand"
	"net"
	"net/http"
	"time"
)

func newMainServer(backends []backend) *http.Server {
	tr := &http.Transport{
		ResponseHeaderTimeout: 2 * time.Second,
	}
	client := &http.Client{Transport: tr}

	serveMux := http.NewServeMux()
	serveMux.Handle("/", requestID(instrument(serviceTime("frontend", mainHandler(client, filterBackends(backends, homePageServices))))))
	serveMux.Handle("/products", requestID(instrument(serviceTime("frontend", productListing(client, filterBackends(backends, productListingServices))))))
	serveMux.Handle("/products/", requestID(instrument(serviceTime("frontend", productDetail(client, filterBackends(backends, productDetailServices))))))
	serveMux.Handle("/categories", requestID(instrument(serviceTime("frontend", categoryListing(client, filterBackends(backends, categoryListingServices))))))
	serveMux.Handle("/categories/", requestID(instrument(serviceTime("frontend", categoryDetail(client, filterBackends(backends, categoryDetailServices))))))
	serveMux.Handle("/search", requestID(instrument(serviceTime("frontend", search(client, filterBackends(backends, searchServices))))))
	serveMux.Handle("/account", requestID(instrument(serviceTime("frontend", account(client, filterBackends(backends, accountServices))))))
	serveMux.Handle("/checkout", requestID(instrument(serviceTime("frontend", checkout(client, filterBackends(backends, checkoutServices))))))

	return newServer(serveMux)
}

func filterBackends(backends []backend, desired map[string]bool) []backend {
	var res []backend

	for _, b := range backends {
		if desired[b.name] {
			res = append(res, b)
		}
	}

	return res
}

func newBackends(errorChan chan<- error) []backend {
	backends := make([]backend, 10)

	for i := range backends {
		serveMux := http.NewServeMux()
		name := backendName(i)
		serveMux.Handle("/", requestID(instrument(serviceTime(name, unreliableHandler(rand.Intn(5)+1)))))
		server := newServer(serveMux)
		listener, err := newListener("0")

		if err != nil {
			panic(err)
		}

		go func() {
			errorChan <- server.Serve(listener)
		}()

		backends[i] = backend{
			server:  server,
			address: listener.Addr().String(),
			name:    name,
		}
	}

	return backends
}

func backendName(i int) string {
	nameLen := len(backendServiceNames)
	return fmt.Sprintf("%s", backendServiceNames[i%nameLen])
}

func newListener(port string) (net.Listener, error) {
	return net.Listen("tcp", "0.0.0.0:"+port)
}

func newServer(serveMux http.Handler) *http.Server {
	return &http.Server{
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
		IdleTimeout:  120 * time.Second,
		Handler:      serveMux,
	}
}
<|endoftext|>


<commit_before>package daemon

import (
	"fmt"
	"log"
	"net"
	"os"
	"os/signal"
	"strconv"
	"syscall"
)

// Service Daemon
type ServiceDaemon struct {
	Daemon
}

func (daemon *ServiceDaemon) Manage(service Service) (string, error) {

	usage := "Usage: myservice install | remove | start | stop | status"

	// if received any kind of command, do it
	if len(os.Args) > 1 {
		command := os.Args[1]
		switch command {
		case "install":
			return daemon.Install()
		case "remove":
			return daemon.Remove()
		case "start":
			return daemon.Start()
		case "stop":
			return daemon.Stop()
		case "status":
			return daemon.Status()
		default:
			return usage, nil
		}
	}

	process := service.GetProcess()
	process()

	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt, os.Kill, syscall.SIGTERM)

	// Set up listener for defined host and port
	listener, err := net.Listen("tcp", ":"+strconv.Itoa(service.GetPort()))
	if err != nil {
		return "Possibly was a problem with the port binding", err
	}

	// set up channel on which to send accepted connections
	listen := make(chan net.Conn, 100)
	go acceptConnection(listener, listen)

	// loop work cycle with accept connections or interrupt
	// by system signal
	for {
		select {
		case conn := <-listen:
			go handleClient(conn)
		case killSignal := <-interrupt:
			log.Println("Got signal:", killSignal)
			log.Println("Stoping listening on ", listener.Addr())
			listener.Close()
			if killSignal == os.Interrupt {
				return "Daemon was interruped by system signal", nil
			}
			return "Daemon was killed", nil
		}
	}

	// never happen, but need to complete code
	return usage, nil
}

// Accept a client connection and collect it in a channel
func acceptConnection(listener net.Listener, listen chan<- net.Conn) {
	for {
		conn, err := listener.Accept()
		if err != nil {
			continue
		}
		listen <- conn
	}
}

func handleClient(client net.Conn) {
	for {
		buf := make([]byte, 4096)
		numbytes, err := client.Read(buf)
		if numbytes == 0 || err != nil {
			return
		}
		client.Write(buf)
	}
}

// Services

type Service struct {
	Name        string
	Description string
	Port        int
	Process     func()
}

func (s Service) GetName() string {
	return s.Name
}

func (s Service) GetPort() int {
	return s.Port
}

func (s Service) GetDescription() string {
	return s.Description
}

func (s Service) GetProcess() func() {
	return s.Process
}

func (service Service) Daemon() {

	srv, err := New(service.GetName(), service.GetDescription())

	if err != nil {
		fmt.Println("Error: ", err)
		os.Exit(1)
	}

	service_daemon := &ServiceDaemon{srv}
	status, err := service_daemon.Manage(service)

	if err != nil {
		fmt.Println(status, "\nError: ", err)
		os.Exit(1)
	}

	fmt.Println(status)
}
<commit_msg>rename service variable<commit_after>package daemon

import (
	"fmt"
	"log"
	"net"
	"os"
	"os/signal"
	"strconv"
	"syscall"
)

// Service Daemon
type ServiceDaemon struct {
	Daemon
}

func (daemon *ServiceDaemon) Manage(service Service) (string, error) {

	usage := "Usage: myservice install | remove | start | stop | status"

	// if received any kind of command, do it
	if len(os.Args) > 1 {
		command := os.Args[1]
		switch command {
		case "install":
			return daemon.Install()
		case "remove":
			return daemon.Remove()
		case "start":
			return daemon.Start()
		case "stop":
			return daemon.Stop()
		case "status":
			return daemon.Status()
		default:
			return usage, nil
		}
	}

	process := service.GetProcess()
	process()

	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt, os.Kill, syscall.SIGTERM)

	// Set up listener for defined host and port
	listener, err := net.Listen("tcp", ":"+strconv.Itoa(service.GetPort()))
	if err != nil {
		return "Possibly was a problem with the port binding", err
	}

	// set up channel on which to send accepted connections
	listen := make(chan net.Conn, 100)
	go acceptConnection(listener, listen)

	// loop work cycle with accept connections or interrupt
	// by system signal
	for {
		select {
		case conn := <-listen:
			go handleClient(conn)
		case killSignal := <-interrupt:
			log.Println("Got signal:", killSignal)
			log.Println("Stoping listening on ", listener.Addr())
			listener.Close()
			if killSignal == os.Interrupt {
				return "Daemon was interruped by system signal", nil
			}
			return "Daemon was killed", nil
		}
	}

	// never happen, but need to complete code
	return usage, nil
}

// Accept a client connection and collect it in a channel
func acceptConnection(listener net.Listener, listen chan<- net.Conn) {
	for {
		conn, err := listener.Accept()
		if err != nil {
			continue
		}
		listen <- conn
	}
}

func handleClient(client net.Conn) {
	for {
		buf := make([]byte, 4096)
		numbytes, err := client.Read(buf)
		if numbytes == 0 || err != nil {
			return
		}
		client.Write(buf)
	}
}

// Services

type Service struct {
	Name        string
	Description string
	Port        int
	Process     func()
}

func (service Service) GetName() string {
	return service.Name
}

func (service Service) GetPort() int {
	return service.Port
}

func (service Service) GetDescription() string {
	return service.Description
}

func (service Service) GetProcess() func() {
	return service.Process
}

func (service Service) Daemon() {

	srv, err := New(service.GetName(), service.GetDescription())

	if err != nil {
		fmt.Println("Error: ", err)
		os.Exit(1)
	}

	service_daemon := &ServiceDaemon{srv}
	status, err := service_daemon.Manage(service)

	if err != nil {
		fmt.Println(status, "\nError: ", err)
		os.Exit(1)
	}

	fmt.Println(status)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"github.com/blang/procd"
	"os"
	"path"
	"strconv"
	"strings"
)

type LaunchConfig struct {
	Name      string `json:"name"`
	Game      string `json:"game"`
	Modstring string `json:"modstring"`
	Betamod   string `json:"betamod"`
	Host      string `json:"host"`
	Port      int    `json:"port"`
	Password  string `json:"password"`
}

type Service struct {
	store   *Store
	process *procd.Process
}

func NewService(store *Store) *Service {
	return &Service{
		store: store,
	}
}

func (s *Service) Config() *Config {
	return &(s.store.Config)
}

func (s *Service) Launch(conf *LaunchConfig) error {
	if s.process != nil && s.process.State() != procd.RunStateStopped {
		return errors.New("Instance already running")
	}
	switch conf.Game {
	case "a2":
		err := s.checkArma2OA()
		if err != nil {
			return err
		}
		cwd, exe, args := s.compileParams(s.Config().Arma2OAPath, "arma2oa.exe", s.Config().Arma2Params, s.Config().Arma2Profile, conf)
		s.process = procd.NewProcess(cwd, exe, args)
		s.process.Start()
	case "a3":
		err := s.checkArma3()
		if err != nil {
			return err
		}
		cwd, exe, args := s.compileParams(s.Config().Arma3Path, "arma3.exe", s.Config().Arma3Params, s.Config().Arma3Profile, conf)
		s.process = procd.NewProcess(cwd, exe, args)
		s.process.Start()
		return errors.New("Not yet implemented")
	default:
		return errors.New("Unknown gametype")
	}
	return nil
}

func (s *Service) Shutdown() {
	if s.process != nil && s.process.State() != procd.RunStateStopped {
		s.process.Stop()
		s.process.Wait()
	}
}

func (s *Service) checkArma2OA() error {
	if s.Config().Arma2OAPath == "" {
		return errors.New("Arma2OAPath not configured")
	}
	fi, err := os.Stat(s.Config().Arma2OAPath)
	if err != nil {
		return fmt.Errorf("Arma2OA Path not exists: %q", s.Config().Arma2OAPath)
	}
	if !fi.IsDir() {
		return fmt.Errorf("Arma2OA Path is not a directory: %q", s.Config().Arma2OAPath)
	}
	exePath := path.Join(s.Config().Arma2OAPath, "arma2oa.exe")
	fi, err = os.Stat(exePath)
	if err != nil {
		return fmt.Errorf("Arma2OA Executable path not exists: %q", exePath)
	}
	if !(fi.Size() > 0) {
		return fmt.Errorf("Arma2OA Executable not exists: ", exePath)
	}

	return nil
}

func (s *Service) checkArma3() error {
	if s.Config().Arma3Path == "" {
		return errors.New("Arma3Path not configured")
	}
	fi, err := os.Stat(s.Config().Arma3Path)
	if err != nil {
		return fmt.Errorf("Arma3 Path not exists: %q", s.Config().Arma3Path)
	}
	if !fi.IsDir() {
		return fmt.Errorf("Arma3 Path is not a directory: %q", s.Config().Arma3Path)
	}
	exePath := path.Join(s.Config().Arma3Path, "arma3.exe")
	fi, err = os.Stat(exePath)
	if err != nil {
		return fmt.Errorf("Arma3 Executable path not exists: %q", exePath)
	}
	if !(fi.Size() > 0) {
		return fmt.Errorf("Arma3 Executable not exists: ", exePath)
	}

	return nil
}

func (s *Service) compileParams(cwd, exe string, addParams string, profile string, conf *LaunchConfig) (string, string, []string) {
	startPath := path.Join(cwd, exe)
	if conf.Betamod != "" {
		startPath = path.Join(cwd, conf.Betamod, exe)
	}

	args := []string{
		"-nosplash",
		"-world=empty",
		"-pause",
		"-connect=" + conf.Host,
		"-port=" + strconv.Itoa(conf.Port),
		"-password=" + conf.Password,
		"-name=" + profile,
		"-mod=" + conf.Modstring,
	}
	args = append(args, strings.Split(addParams, " ")...)
	return cwd, startPath, args
}
<commit_msg>Better support for additional startup parameters<commit_after>package main

import (
	"errors"
	"fmt"
	"github.com/blang/procd"
	"os"
	"path"
	"strconv"
	"strings"
)

type LaunchConfig struct {
	Name      string `json:"name"`
	Game      string `json:"game"`
	Modstring string `json:"modstring"`
	Betamod   string `json:"betamod"`
	Host      string `json:"host"`
	Port      int    `json:"port"`
	Password  string `json:"password"`
}

type Service struct {
	store   *Store
	process *procd.Process
}

func NewService(store *Store) *Service {
	return &Service{
		store: store,
	}
}

func (s *Service) Config() *Config {
	return &(s.store.Config)
}

func (s *Service) Launch(conf *LaunchConfig) error {
	if s.process != nil && s.process.State() != procd.RunStateStopped {
		return errors.New("Instance already running")
	}
	switch conf.Game {
	case "a2":
		err := s.checkArma2OA()
		if err != nil {
			return err
		}
		cwd, exe, args := s.compileParams(s.Config().Arma2OAPath, "arma2oa.exe", s.Config().Arma2Params, s.Config().Arma2Profile, conf)
		s.process = procd.NewProcess(cwd, exe, args)
		s.process.Start()
	case "a3":
		err := s.checkArma3()
		if err != nil {
			return err
		}
		cwd, exe, args := s.compileParams(s.Config().Arma3Path, "arma3.exe", s.Config().Arma3Params, s.Config().Arma3Profile, conf)
		s.process = procd.NewProcess(cwd, exe, args)
		s.process.Start()
		return errors.New("Not yet implemented")
	default:
		return errors.New("Unknown gametype")
	}
	return nil
}

func (s *Service) Shutdown() {
	if s.process != nil && s.process.State() != procd.RunStateStopped {
		s.process.Stop()
		s.process.Wait()
	}
}

func (s *Service) checkArma2OA() error {
	if s.Config().Arma2OAPath == "" {
		return errors.New("Arma2OAPath not configured")
	}
	fi, err := os.Stat(s.Config().Arma2OAPath)
	if err != nil {
		return fmt.Errorf("Arma2OA Path not exists: %q", s.Config().Arma2OAPath)
	}
	if !fi.IsDir() {
		return fmt.Errorf("Arma2OA Path is not a directory: %q", s.Config().Arma2OAPath)
	}
	exePath := path.Join(s.Config().Arma2OAPath, "arma2oa.exe")
	fi, err = os.Stat(exePath)
	if err != nil {
		return fmt.Errorf("Arma2OA Executable path not exists: %q", exePath)
	}
	if !(fi.Size() > 0) {
		return fmt.Errorf("Arma2OA Executable not exists: ", exePath)
	}

	return nil
}

func (s *Service) checkArma3() error {
	if s.Config().Arma3Path == "" {
		return errors.New("Arma3Path not configured")
	}
	fi, err := os.Stat(s.Config().Arma3Path)
	if err != nil {
		return fmt.Errorf("Arma3 Path not exists: %q", s.Config().Arma3Path)
	}
	if !fi.IsDir() {
		return fmt.Errorf("Arma3 Path is not a directory: %q", s.Config().Arma3Path)
	}
	exePath := path.Join(s.Config().Arma3Path, "arma3.exe")
	fi, err = os.Stat(exePath)
	if err != nil {
		return fmt.Errorf("Arma3 Executable path not exists: %q", exePath)
	}
	if !(fi.Size() > 0) {
		return fmt.Errorf("Arma3 Executable not exists: ", exePath)
	}

	return nil
}

func (s *Service) compileParams(cwd, exe string, addParams string, profile string, conf *LaunchConfig) (string, string, []string) {
	startPath := path.Join(cwd, exe)
	if conf.Betamod != "" {
		startPath = path.Join(cwd, conf.Betamod, exe)
	}

	args := []string{
		"-nosplash",
		"-world=empty",
		"-pause",
		"-connect=" + conf.Host,
		"-port=" + strconv.Itoa(conf.Port),
		"-password=" + conf.Password,
		"-name=" + profile,
		"-mod=" + conf.Modstring,
	}
	args = append(args, strings.Split(addParams, "|")...)
	return cwd, startPath, args
}
<|endoftext|>


<commit_before>package micro

import (
	"os"
	"os/signal"
	"syscall"

	"github.com/micro/go-micro/client"
	"github.com/micro/go-micro/context"
	"github.com/micro/go-micro/server"
)

type service struct {
	opts Options
}

func newService(opts ...Option) Service {
	options := newOptions(opts...)

	options.Client = &clientWrapper{
		options.Client,
		context.Metadata{
			HeaderPrefix + "From-Service": options.Server.Options().Name,
		},
	}

	return &service{
		opts: options,
	}
}

func (s *service) Init(opts ...Option) {
	// We might get more command flags or the action here
	// This is pretty ugly, find a better way
	options := newOptions()
	options.Cmd = s.opts.Cmd
	for _, o := range opts {
		o(&options)
	}
	s.opts.Cmd = options.Cmd

	// Initialise the command flags, overriding new service
	s.opts.Cmd.Init()

	// Update any options to override command flags
	for _, o := range opts {
		o(&s.opts)
	}
}

func (s *service) Options() Options {
	return s.opts
}

func (s *service) Client() client.Client {
	return s.opts.Client
}

func (s *service) Server() server.Server {
	return s.opts.Server
}

func (s *service) String() string {
	return "go-micro"
}

func (s *service) Start() error {
	for _, fn := range s.opts.BeforeStart {
		if err := fn(); err != nil {
			return err
		}
	}

	if err := s.opts.Server.Start(); err != nil {
		return err
	}

	if err := s.opts.Server.Register(); err != nil {
		return err
	}

	return nil
}

func (s *service) Stop() error {
	if err := s.opts.Server.Deregister(); err != nil {
		return err
	}

	if err := s.opts.Server.Stop(); err != nil {
		return err
	}

	var gerr error
	for _, fn := range s.opts.AfterStop {
		if err := fn(); err != nil {
			// should we bail if it fails?
			// other funcs will not be executed
			// seems wrong
			gerr = err
		}
	}
	return gerr
}

func (s *service) Run() error {
	if err := s.Start(); err != nil {
		return err
	}

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL)
	<-ch

	if err := s.Stop(); err != nil {
		return err
	}

	return nil
}
<commit_msg>So much to configure, this is an ugly hack<commit_after>package micro

import (
	"os"
	"os/signal"
	"syscall"

	"github.com/micro/go-micro/client"
	"github.com/micro/go-micro/cmd"
	"github.com/micro/go-micro/context"
	"github.com/micro/go-micro/server"
)

type service struct {
	opts Options
}

func newService(opts ...Option) Service {
	options := newOptions(opts...)

	options.Client = &clientWrapper{
		options.Client,
		context.Metadata{
			HeaderPrefix + "From-Service": options.Server.Options().Name,
		},
	}

	return &service{
		opts: options,
	}
}

func (s *service) Init(opts ...Option) {
	// We might get more command flags or the action here
	// This is pretty ugly, find a better way
	options := newOptions()
	options.Cmd = s.opts.Cmd
	for _, o := range opts {
		o(&options)
	}
	s.opts.Cmd = options.Cmd

	// Initialise the command flags, overriding new service
	s.opts.Cmd.Init(
		cmd.Broker(&s.opts.Broker),
		cmd.Registry(&s.opts.Registry),
		cmd.Transport(&s.opts.Transport),
		cmd.Client(&s.opts.Client),
		cmd.Server(&s.opts.Server),
	)

	// Update any options to override command flags
	for _, o := range opts {
		o(&s.opts)
	}
}

func (s *service) Options() Options {
	return s.opts
}

func (s *service) Client() client.Client {
	return s.opts.Client
}

func (s *service) Server() server.Server {
	return s.opts.Server
}

func (s *service) String() string {
	return "go-micro"
}

func (s *service) Start() error {
	for _, fn := range s.opts.BeforeStart {
		if err := fn(); err != nil {
			return err
		}
	}

	if err := s.opts.Server.Start(); err != nil {
		return err
	}

	if err := s.opts.Server.Register(); err != nil {
		return err
	}

	return nil
}

func (s *service) Stop() error {
	if err := s.opts.Server.Deregister(); err != nil {
		return err
	}

	if err := s.opts.Server.Stop(); err != nil {
		return err
	}

	var gerr error
	for _, fn := range s.opts.AfterStop {
		if err := fn(); err != nil {
			// should we bail if it fails?
			// other funcs will not be executed
			// seems wrong
			gerr = err
		}
	}
	return gerr
}

func (s *service) Run() error {
	if err := s.Start(); err != nil {
		return err
	}

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL)
	<-ch

	if err := s.Stop(); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"testing"
	"time"

	"github.com/kubernetes-incubator/cri-o/lib"
	"github.com/kubernetes-incubator/cri-o/lib/sandbox"
	"github.com/kubernetes-incubator/cri-o/oci"
	"github.com/kubernetes-incubator/cri-o/version"
	pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
)

func init() {
	rand.Seed(time.Now().UTC().UnixNano())
}

func newTestContainerServerOrFailNow(t *testing.T) (cs *lib.ContainerServer, dirsToCleanUp []string) {
	tmpdir := os.Getenv("TMPDIR")

	config := lib.DefaultConfig()
	runRoot, err := ioutil.TempDir(tmpdir, "")
	if err != nil {
		t.Fatal(err)
	}
	config.RootConfig.RunRoot = runRoot
	root, err := ioutil.TempDir(tmpdir, "")
	if err != nil {
		t.Fatal(err)
	}
	config.RootConfig.Root = root
	config.RootConfig.Storage = "vfs"
	cs, err = lib.New(config)
	if err != nil {
		t.Fatal(err)
	}
	return cs, []string{runRoot, root}
}

func newTestSandboxOrFailNow(t *testing.T) (string, *sandbox.Sandbox) {
	id := fmt.Sprintf("id-for-sandbox-%d", rand.Int())

	sb, err := sandbox.New(id, "", "", "", "", nil, nil, "", "", nil, "", "", false, false, "", "", nil)
	if err != nil {
		t.Fatal(err)
	}
	return id, sb
}

func newTestContainerOrFailNow(t *testing.T) *oci.Container {
	id := fmt.Sprintf("id-for-container-%d", rand.Int())

	c, err := oci.NewContainer(id, "", "", "", nil, nil, nil, nil, "", "", "", nil, "", false, false, false, false, false, "", time.Now(), "")
	if err != nil {
		t.Fatal(err)
	}
	return c
}

func setupServer(t *testing.T) (*Server, string, func()) {
	containerServer, fs := newTestContainerServerOrFailNow(t)
	teardown := func() {
		for _, f := range fs {
			defer os.RemoveAll(f)
		}
	}

	server := &Server{ContainerServer: containerServer}
	sandboxID, sb := newTestSandboxOrFailNow(t)
	sb.SetInfraContainer(newTestContainerOrFailNow(t))
	server.PodIDIndex().Add(sandboxID)
	server.ContainerServer.AddSandbox(sb)

	return server, sandboxID, teardown
}

func TestPodSandboxStatus(t *testing.T) {
	server, sandboxID, teardown := setupServer(t)
	defer teardown()

	t.Run("Without verbose information", func(t *testing.T) {
		resp, err := server.PodSandboxStatus(nil, &pb.PodSandboxStatusRequest{
			PodSandboxId: sandboxID,
		})
		if err != nil {
			t.Fatal(err)
		}

		if resp.Status == nil {
			t.Error("expected non nil resp.Status")
		}
		if resp.Info != nil {
			t.Error("expected nil resp.Info")
		}
	})

	t.Run("With verbose information", func(t *testing.T) {
		resp, err := server.PodSandboxStatus(nil, &pb.PodSandboxStatusRequest{
			PodSandboxId: sandboxID,
			Verbose:      true,
		})
		if err != nil {
			t.Fatal(err)
		}

		marshaledVersion := resp.Info["version"]
		var versionPayload VersionPayload
		must(t, json.Unmarshal([]byte(marshaledVersion), &versionPayload))

		if version.Version != versionPayload.Version {
			t.Errorf("expected: %s\ngot: %s", version.Version, versionPayload.Version)
		}
	})
}
<commit_msg>server/sandbox_status_test: Use background context instead of nil<commit_after>package server

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"testing"
	"time"

	"github.com/kubernetes-incubator/cri-o/lib"
	"github.com/kubernetes-incubator/cri-o/lib/sandbox"
	"github.com/kubernetes-incubator/cri-o/oci"
	"github.com/kubernetes-incubator/cri-o/version"
	pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
)

func init() {
	rand.Seed(time.Now().UTC().UnixNano())
}

func newTestContainerServerOrFailNow(t *testing.T) (cs *lib.ContainerServer, dirsToCleanUp []string) {
	tmpdir := os.Getenv("TMPDIR")

	config := lib.DefaultConfig()
	runRoot, err := ioutil.TempDir(tmpdir, "")
	if err != nil {
		t.Fatal(err)
	}
	config.RootConfig.RunRoot = runRoot
	root, err := ioutil.TempDir(tmpdir, "")
	if err != nil {
		t.Fatal(err)
	}
	config.RootConfig.Root = root
	config.RootConfig.Storage = "vfs"
	cs, err = lib.New(config)
	if err != nil {
		t.Fatal(err)
	}
	return cs, []string{runRoot, root}
}

func newTestSandboxOrFailNow(t *testing.T) (string, *sandbox.Sandbox) {
	id := fmt.Sprintf("id-for-sandbox-%d", rand.Int())

	sb, err := sandbox.New(id, "", "", "", "", nil, nil, "", "", nil, "", "", false, false, "", "", nil)
	if err != nil {
		t.Fatal(err)
	}
	return id, sb
}

func newTestContainerOrFailNow(t *testing.T) *oci.Container {
	id := fmt.Sprintf("id-for-container-%d", rand.Int())

	c, err := oci.NewContainer(id, "", "", "", nil, nil, nil, nil, "", "", "", nil, "", false, false, false, false, false, "", time.Now(), "")
	if err != nil {
		t.Fatal(err)
	}
	return c
}

func setupServer(t *testing.T) (*Server, string, func()) {
	containerServer, fs := newTestContainerServerOrFailNow(t)
	teardown := func() {
		for _, f := range fs {
			defer os.RemoveAll(f)
		}
	}

	server := &Server{ContainerServer: containerServer}
	sandboxID, sb := newTestSandboxOrFailNow(t)
	sb.SetInfraContainer(newTestContainerOrFailNow(t))
	server.PodIDIndex().Add(sandboxID)
	server.ContainerServer.AddSandbox(sb)

	return server, sandboxID, teardown
}

func TestPodSandboxStatus(t *testing.T) {
	ctx := context.Background()
	server, sandboxID, teardown := setupServer(t)
	defer teardown()

	t.Run("Without verbose information", func(t *testing.T) {
		resp, err := server.PodSandboxStatus(ctx, &pb.PodSandboxStatusRequest{
			PodSandboxId: sandboxID,
		})
		if err != nil {
			t.Fatal(err)
		}

		if resp.Status == nil {
			t.Error("expected non nil resp.Status")
		}
		if resp.Info != nil {
			t.Error("expected nil resp.Info")
		}
	})

	t.Run("With verbose information", func(t *testing.T) {
		resp, err := server.PodSandboxStatus(ctx, &pb.PodSandboxStatusRequest{
			PodSandboxId: sandboxID,
			Verbose:      true,
		})
		if err != nil {
			t.Fatal(err)
		}

		marshaledVersion := resp.Info["version"]
		var versionPayload VersionPayload
		must(t, json.Unmarshal([]byte(marshaledVersion), &versionPayload))

		if version.Version != versionPayload.Version {
			t.Errorf("expected: %s\ngot: %s", version.Version, versionPayload.Version)
		}
	})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"gnd.la/log"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"sync"
	"syscall"
	"time"
)

type State uint8

const (
	StateStopped State = iota
	StateStopping
	StateStarted
	StateStarting
	StateFailed
)

const (
	minTime = time.Second
)

type Service struct {
	sync.Mutex
	Config   *Config
	Cmd      *exec.Cmd
	State    State
	Started  time.Time
	Restarts int
	Err      error
	Ch       chan error
	logger   *logger
}

func newService(cfg *Config) *Service {
	return &Service{Config: cfg, Ch: make(chan error)}
}

func (s *Service) Name() string {
	return s.Config.ServiceName()
}

func (s *Service) initLogger() {
	var m monitor
	if s.logger != nil {
		s.logger.Close()
		m = s.logger.monitor
		s.logger = nil
	}
	logPath := filepath.Join(LogDir, s.Name()+".log")
	f, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		s.errorf("error opening log file %s: %s", logPath, err)
		return
	}
	s.logger = newLogger(f)
	s.logger.monitor = m
}

func (s *Service) sendErr(err error) {
	s.Ch <- err
}

func (s *Service) Start() error {
	if err := s.startService(); err != nil {
		return err
	}
	if err := s.startWatchdog(); err != nil {
		s.errorf("error starting watchdog: %s", s.Name(), err)
	}
	return nil
}

func (s *Service) Run() {
	s.Lock()
	s.State = StateStarted
	s.initLogger()
	s.Unlock()
	for {
		s.Lock()
		if s.State != StateStarted {
			s.Unlock()
			break
		}
		cmd, err := s.Config.Cmd()
		if err != nil {
			s.State = StateFailed
			s.Err = err
			s.errorf("could not initialize: %s", err)
			s.Unlock()
			break
		}
		if s.logger != nil {
			cmd.Stdout = s.logger.stdout
			cmd.Stderr = s.logger.stderr
		}
		s.Cmd = cmd
		s.Started = time.Now()
		s.infof("starting")
		if err := s.Cmd.Start(); err != nil {
			s.State = StateFailed
			s.Err = err
			s.errorf("failed to start: %s", err)
			s.Unlock()
			break
		}
		s.Unlock()
		time.AfterFunc(time.Duration(float64(minTime)*1.1), func() {
			s.Lock()
			defer s.Unlock()
			if s.Err == nil {
				s.sendErr(nil)
				s.infof("started")
			}
		})
		err = s.Cmd.Wait()
		s.Lock()
		if s.State != StateStarted {
			s.Cmd = nil
			s.sendErr(nil)
			s.Unlock()
			break
		}
		if since := time.Since(s.Started); since < minTime {
			// Consider failure
			s.Cmd = nil
			s.State = StateFailed
			s.Err = fmt.Errorf("exited too fast (%s)", since)
			s.sendErr(s.Err)
			s.Unlock()
			s.errorf(s.Err.Error())
			break
		}
		s.Restarts++
		s.Unlock()
		s.infof("exited with error %s - restarting", err)
	}
}

func (s *Service) Stop() error {
	s.stopWatchdog()
	if err := s.stopService(); err != nil {
		s.startWatchdog()
		return err
	}
	return nil
}

func (s *Service) startWatchdog() error {
	if s.Config.Watchdog != nil {
		interval := s.Config.WatchdogInterval
		if interval < 0 {
			interval = defaultWatchdogInterval
		}
		return s.Config.Watchdog.Start(s, interval)
	}
	return nil
}

func (s *Service) stopWatchdog() {
	if s.Config.Watchdog != nil {
		s.Config.Watchdog.Stop()
	}
}

func (s *Service) startService() error {
	go s.Run()
	return <-s.Ch
}

func (s *Service) stopService() error {
	s.Lock()
	s.State = StateStopping
	cmd := s.Cmd
	s.Unlock()
	if cmd == nil {
		s.Lock()
		s.State = StateStopped
		s.Unlock()
		return nil
	}
	s.infof("stopping")
	if cmd.Process != nil {
		cmd.Process.Signal(os.Signal(syscall.SIGTERM))
	}
	stopped := false
	select {
	case <-s.Ch:
		stopped = true
	case <-time.After(10 * time.Second):
		if cmd.Process != nil {
			cmd.Process.Kill()
		}
	}
	if !stopped {
		select {
		case <-s.Ch:
		case <-time.After(2 * time.Second):
			s.Lock()
			s.State = StateStarted
			s.Unlock()
			err := fmt.Errorf("could not stop, probably stuck")
			s.errorf(err.Error())
			return err
		}
	}
	s.Lock()
	s.State = StateStopped
	s.Restarts = 0
	s.Unlock()
	s.infof("stopped")
	if s.logger != nil {
		s.logger.Close()
		s.logger = nil
	}
	return nil
}

func (s *Service) log(level log.LLevel, prefix string, format string, args ...interface{}) {
	msg := fmt.Sprintf(format, args...)
	log.Logf(level, "[%s] %s", s.Name(), msg)
	if s.logger != nil {
		s.logger.Write(prefix, []byte(msg))
	}
}

func (s *Service) errorf(format string, args ...interface{}) {
	s.log(log.LError, "error", format, args...)
}

func (s *Service) infof(format string, args ...interface{}) {
	s.log(log.LInfo, "info", format, args...)
}

func (s *Service) debugf(format string, args ...interface{}) {
	s.log(log.LDebug, "debug", format, args...)
}

func (s *Service) updateConfig(cfg *Config) {
	if reflect.DeepEqual(s.Config, cfg) {
		// there were changes to the file which don't affect the conf
		return
	}
	log.Debugf("changed service %s's configuration", s.Name())
	start := false
	if s.State == StateStarted {
		start = s.Stop() == nil
	}
	s.Config = cfg
	if start {
		s.Start()
	}
}
<commit_msg>Avoid deadlock when restarting service due to failure<commit_after>package main

import (
	"fmt"
	"gnd.la/log"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"sync"
	"syscall"
	"time"
)

type State uint8

const (
	StateStopped State = iota
	StateStopping
	StateStarted
	StateStarting
	StateFailed
)

const (
	minTime = time.Second
)

type Service struct {
	sync.Mutex
	Config   *Config
	Cmd      *exec.Cmd
	State    State
	Started  time.Time
	Restarts int
	Err      error
	Ch       chan error
	logger   *logger
}

func newService(cfg *Config) *Service {
	return &Service{Config: cfg, Ch: make(chan error)}
}

func (s *Service) Name() string {
	return s.Config.ServiceName()
}

func (s *Service) initLogger() {
	var m monitor
	if s.logger != nil {
		s.logger.Close()
		m = s.logger.monitor
		s.logger = nil
	}
	logPath := filepath.Join(LogDir, s.Name()+".log")
	f, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		s.errorf("error opening log file %s: %s", logPath, err)
		return
	}
	s.logger = newLogger(f)
	s.logger.monitor = m
}

func (s *Service) mightSendErr(err error) {
	select {
	case s.Ch <- err:
	default:
	}
}

func (s *Service) Start() error {
	if err := s.startService(); err != nil {
		return err
	}
	if err := s.startWatchdog(); err != nil {
		s.errorf("error starting watchdog: %s", s.Name(), err)
	}
	return nil
}

func (s *Service) Run() {
	s.Lock()
	s.State = StateStarted
	s.initLogger()
	s.Unlock()
	for {
		s.Lock()
		if s.State != StateStarted {
			s.Unlock()
			break
		}
		cmd, err := s.Config.Cmd()
		if err != nil {
			s.State = StateFailed
			s.Err = err
			s.errorf("could not initialize: %s", err)
			s.Unlock()
			break
		}
		if s.logger != nil {
			cmd.Stdout = s.logger.stdout
			cmd.Stderr = s.logger.stderr
		}
		s.Cmd = cmd
		s.Started = time.Now()
		s.infof("starting")
		if err := s.Cmd.Start(); err != nil {
			s.State = StateFailed
			s.Err = err
			s.errorf("failed to start: %s", err)
			s.Unlock()
			break
		}
		s.Unlock()
		time.AfterFunc(time.Duration(float64(minTime)*1.1), func() {
			s.Lock()
			defer s.Unlock()
			if s.Err == nil {
				s.mightSendErr(nil)
				s.infof("started")
			}
		})
		err = s.Cmd.Wait()
		s.Lock()
		if s.State != StateStarted {
			s.Cmd = nil
			s.Ch <- err
			s.Unlock()
			break
		}
		if since := time.Since(s.Started); since < minTime {
			// Consider failure
			s.Cmd = nil
			s.State = StateFailed
			s.Err = fmt.Errorf("exited too fast (%s)", since)
			s.mightSendErr(s.Err)
			s.Unlock()
			s.errorf(s.Err.Error())
			break
		}
		s.Restarts++
		s.Unlock()
		s.infof("exited with error %s - restarting", err)
	}
}

func (s *Service) Stop() error {
	s.stopWatchdog()
	if err := s.stopService(); err != nil {
		s.startWatchdog()
		return err
	}
	return nil
}

func (s *Service) startWatchdog() error {
	if s.Config.Watchdog != nil {
		interval := s.Config.WatchdogInterval
		if interval < 0 {
			interval = defaultWatchdogInterval
		}
		return s.Config.Watchdog.Start(s, interval)
	}
	return nil
}

func (s *Service) stopWatchdog() {
	if s.Config.Watchdog != nil {
		s.Config.Watchdog.Stop()
	}
}

func (s *Service) startService() error {
	go s.Run()
	return <-s.Ch
}

func (s *Service) stopService() error {
	s.Lock()
	s.State = StateStopping
	cmd := s.Cmd
	s.Unlock()
	if cmd == nil {
		s.Lock()
		s.State = StateStopped
		s.Unlock()
		return nil
	}
	s.infof("stopping")
	if cmd.Process != nil {
		cmd.Process.Signal(os.Signal(syscall.SIGTERM))
	}
	stopped := false
	select {
	case <-s.Ch:
		stopped = true
	case <-time.After(10 * time.Second):
		if cmd.Process != nil {
			cmd.Process.Kill()
		}
	}
	if !stopped {
		select {
		case <-s.Ch:
		case <-time.After(2 * time.Second):
			s.Lock()
			s.State = StateStarted
			s.Unlock()
			err := fmt.Errorf("could not stop, probably stuck")
			s.errorf(err.Error())
			return err
		}
	}
	s.Lock()
	s.State = StateStopped
	s.Restarts = 0
	s.Unlock()
	s.infof("stopped")
	if s.logger != nil {
		s.logger.Close()
		s.logger = nil
	}
	return nil
}

func (s *Service) log(level log.LLevel, prefix string, format string, args ...interface{}) {
	msg := fmt.Sprintf(format, args...)
	log.Logf(level, "[%s] %s", s.Name(), msg)
	if s.logger != nil {
		s.logger.Write(prefix, []byte(msg))
	}
}

func (s *Service) errorf(format string, args ...interface{}) {
	s.log(log.LError, "error", format, args...)
}

func (s *Service) infof(format string, args ...interface{}) {
	s.log(log.LInfo, "info", format, args...)
}

func (s *Service) debugf(format string, args ...interface{}) {
	s.log(log.LDebug, "debug", format, args...)
}

func (s *Service) updateConfig(cfg *Config) {
	if reflect.DeepEqual(s.Config, cfg) {
		// there were changes to the file which don't affect the conf
		return
	}
	log.Debugf("changed service %s's configuration", s.Name())
	start := false
	if s.State == StateStarted {
		start = s.Stop() == nil
	}
	s.Config = cfg
	if start {
		s.Start()
	}
}
<|endoftext|>