<commit_before>/*
Package plugins provides support for creating plugin-based applications.

Following workflow is supposed:
	1) main application creates RPC Host service;
	2) plugin starts and connects to the RPC Host:
		2.1) plugin informs RPC Host about service, provided by that plugin and requests port to serve on;
  		2.2) RPC Host checks if such service is not provided by other connected plugin and generates port for plugin to serve on;
		2.3) plugin starts RPC server on port, provided by RPC Host, and informs RPC Host about it;
		2.4) RPC Host registers plugin service;
	3) when plugin terminates, it informs RPC Host; RPC Host unregisters plugin service;
	4) when RPC Host terminates, it informs all connected plugins; plugins terminates;
	5) when main application need to invoke some service method, RPC Host is used for dispatch it;

For handling remote calls net/rpc package usage is supposed.
A plugin must register object(s) that will be used for handling remote calls.
*/
package plugins

import (
	"fmt"
	"log"
	"time"
	"errors"
	"os"
	"os/signal"
	"syscall"
	"net"
	"net/rpc"
)

// Options for RPC client/server.
type Options struct {
	ipAddr	string
	Port	int				// network port for serving connection or connecting to the remote server
	Log		*log.Logger		// a object for logging operations
}

func (o *Options) listenAddr() string {
	return fmt.Sprintf(":%d", o.Port)
}

func (o *Options) dialAddr() string {
	return fmt.Sprintf("%s:%d", o.ipAddr, o.Port)
}

func (o *Options) applyDefaults(name string) {
	if len(o.ipAddr) == 0 {
		o.ipAddr = "127.0.0.1"
	}
	if o.Port <= 0 {
		o.Port = 4000
	}
	if o.Log == nil {
		o.Log = log.New(os.Stdout, fmt.Sprintf("[%s] ", name), 0)
	}
}

func prepareOptions(name string, options ...*Options) Options {
	var opt Options
	if len(options) > 0 {
		opt = *options[0]
	}
	opt.applyDefaults(name)
	return opt
}

// PluginInfo describes a plugin.
type PluginInfo struct {
	Name			string		// Human-readable name of plugin
	ServiceName		string		// Name of service provided by plugin
	Port			int			// Port plugin serves on
}

// rpcServer represents an RPC Server.
type rpcServer interface {
	Serve()
	onServe(func())
	onStop(func())
}

type rpcSrv struct {
	listener	net.Listener
	log			*log.Logger
	running		bool
	sChan		chan os.Signal
	serve		[]func()
	stop		[]func()
}

func (s *rpcSrv) Serve() {
	if !s.running {
		s.running = true
		go func() {
			time.Sleep(500 * time.Millisecond)
			for _, f := range s.serve {
				f()
			}
		}()

		for {
			if conn, err := s.listener.Accept(); err != nil {
				s.log.Fatal("accept error: " + err.Error())         //!!  panic???
			} else {
				go rpc.ServeConn(conn)
			}
		}
	}
};

func (s *rpcSrv) onServe(h func()) {
	s.serve = append(s.serve, h)
}

func (s *rpcSrv) onStop(h func()) {
	s.stop = append(s.stop, h)
}

func (s *rpcSrv) handleStop() {
	for _, f := range s.stop {
		f()
	}
	os.Exit(0);
}

// newRPCServer creates a new rpcServer instance.
func newRPCServer(opt *Options, rpcServices ...interface{}) (rpcServer, error) {
	err := registerRPCServices(rpcServices...)
	if err == nil {
		opt.applyDefaults("RPC Server")
		listener, err := net.Listen("tcp", opt.listenAddr())
		if err == nil {
			opt.Log.Printf("Listening on %s\n", opt.listenAddr())
			srv := &rpcSrv {
				listener: listener,
				log: opt.Log,
				sChan: make(chan os.Signal, 1),
				serve: make([]func(), 0),
				stop: make([]func(), 0)}
			signal.Notify(srv.sChan, os.Interrupt, os.Kill, syscall.SIGTERM)
			go func() {
				<- srv.sChan
				if srv.running {
					srv.handleStop()
				}
			}()
			return srv, nil
		}
	}
	return nil, err;
}

func registerRPCServices(rpcServices ...interface{}) error {
	for _, rpcService := range rpcServices {
		err := rpc.Register(rpcService)
		if err != nil {
			return err
		}
	}
	return nil
}

// rpcClient represents an RPC Client.
type rpcClient interface {
	Call(serviceMethod string, args interface{}, reply interface{}) error
	Close() error
}

type rpcClnt struct {
	clnt	*rpc.Client
	log		*log.Logger
}

func (c *rpcClnt) Call(serviceMethod string, args interface{}, reply interface{}) error {
	return c.clnt.Call(serviceMethod, args, reply)
}

func (c *rpcClnt) Close() error {
	return c.clnt.Close()
}

// newRPCClient creates a new rpcClient instance.
func newRPCClient(name string, options ...*Options) (rpcClient, error) {
	opt := prepareOptions(fmt.Sprintf("plugin %s", name), options...)
	clnt, err := rpc.Dial("tcp", opt.dialAddr())
	if err == nil {
		return &rpcClnt{clnt: clnt, log: opt.Log}, nil
	}
	return nil, err;

}

// PluginNotifyFunc is a callback function to recieve notifications
type PluginNotifyFunc func(p PluginInfo)

// A Host is a manager that plugins connects to.
// Host uses connected plugins for handling main application calls.
type Host interface {
	Serve()
	Services() []string
	OnConnectPlugin(f PluginNotifyFunc)
	OnDisconnectPlugin(f PluginNotifyFunc)
	Call(serviceName string, serviceMethod string, args interface{}, reply interface{}) error
}

// ErrServiceNotRegistered returns when trying to access non-registered service
var ErrServiceNotRegistered = errors.New("service is not registered")

// ErrServiceAlreadyRegistered throws when trying to register already registered service
var ErrServiceAlreadyRegistered = errors.New("service is already registered")

type connectedPlugin struct {
	info	*PluginInfo
	clnt	rpcClient
}

func (c *connectedPlugin) Call(serviceMethod string, args interface{}, reply interface{}) error {
	return c.clnt.Call(serviceMethod, args, reply)
}

type host struct {
	srv				rpcServer
	log				*log.Logger
	nextPort		int					// free network port to connect
	availPorts		[]int				// network ports that are returned by disconnected plugins
	plugins			[]*connectedPlugin
	onConnect		[]PluginNotifyFunc
	onDisconnect	[]PluginNotifyFunc
}

func (h *host) Serve() {
	go h.srv.Serve()
}

func (h *host) Services() []string {
	rslt := make([]string, len(h.plugins))
	for i, cp := range h.plugins {
		rslt[i] = cp.info.ServiceName
	}
	return rslt
}

func (h *host) OnConnectPlugin(f PluginNotifyFunc) {
	h.onConnect = append(h.onConnect, f)
}

func (h *host) OnDisconnectPlugin(f PluginNotifyFunc) {
	h.onDisconnect = append(h.onDisconnect, f)
}

func (h *host) Call(serviceName string, serviceMethod string, args interface{}, reply interface{}) error {
	var err error
	if i := h.indexOf(serviceName); i>=0 {
		if err = h.plugins[i].Call(serviceMethod, args, reply); err == rpc.ErrShutdown {
			h.removePlugin(serviceName)
			err = ErrServiceNotRegistered
		}
		return err
	}
	return ErrServiceNotRegistered
}

func (h *host) getNextPort() int {
	var rslt int
	if len(h.availPorts) > 0 {
		i := len(h.availPorts) - 1
		rslt, h.availPorts = h.availPorts[i], h.availPorts[:i]
	} else {
		rslt = h.nextPort
		h.nextPort++
	}
	return rslt
}

func (h *host) appendPlugin(cp *connectedPlugin) {
	if i := h.indexOf(cp.info.ServiceName); i<0 {
		h.plugins = append(h.plugins, cp)
		for _, f := range h.onConnect {
			f(*cp.info)
		}
	}
}

func (h *host) removePlugin(serviceName string) {
	if i := h.indexOf(serviceName); i>=0 {
		cp := h.plugins[i]
		var dummy int
		cp.Call("RPCPlugin.Terminate", dummy, &dummy)
		h.log.Printf("Plugin disconnected: \"%s\"", cp.info.Name)
		for _, f := range h.onDisconnect {
			f(*cp.info)
		}
		h.availPorts = append(h.availPorts, cp.info.Port)
		h.plugins = append(h.plugins[:i], h.plugins[i+1:]...)
	}
}

func (h *host) onStop() {
	for len(h.plugins)>0 {
		h.removePlugin(h.plugins[0].info.ServiceName)
	}
}

func (h *host) indexOf(serviceName string) int {
	for i, cp := range h.plugins {
		if cp.info.ServiceName == serviceName {
			return i
		}
	}
	return -1
}

// NewHost creates a new Host instance.
func NewHost(name string, options ...*Options) (Host, error) {
	opt := prepareOptions(name, options...)
	h := &host{
		log: opt.Log,
		nextPort: opt.Port+1,
		availPorts: make([]int, 0),
		plugins: make([]*connectedPlugin, 0),
		onConnect: make([]PluginNotifyFunc, 0),
		onDisconnect: make([]PluginNotifyFunc, 0)}
	srv, err := newRPCServer(&opt, &RPCHost{host: h})
	if err == nil {
		h.srv = srv
		srv.onStop(h.onStop)
		return h, nil
	}
	opt.Log.Println(err)
	return nil, err
}

// RPCHost implements Host RPC service. Handles plugins requests.
type RPCHost struct {
	host *host
}

// GetPort generates port for plugin to serve on.
func (rh *RPCHost) GetPort(info *PluginInfo, port *int) error {
	err := rh.mustBeDisconnected(info)
	if err == nil {
		*port = rh.host.getNextPort()
	}
	return err
}

// ConnectPlugin handles plugin connection to host.
func (rh *RPCHost) ConnectPlugin(info *PluginInfo, _ *int) error {
	err := rh.mustBeDisconnected(info)
	if err == nil {
		opt := prepareOptions(info.Name, &Options{Port: info.Port, Log: rh.host.log})
		if clnt, err := newRPCClient(info.Name, &opt); err == nil {
			cp := &connectedPlugin{info: info, clnt: clnt}
			rh.host.appendPlugin(cp)
			rh.host.log.Printf("Plugin connected: \"%s\", handling \"%s\", serves at %s", cp.info.Name, cp.info.ServiceName, opt.dialAddr())
			return nil
		}
	}
	return err
}

// DisconnectPlugin handles plugin disconnection from host.
func (rh *RPCHost) DisconnectPlugin(info *PluginInfo, _ *int) error {
	err := rh.mustBeConnected(info)
	if err == nil {
		rh.host.removePlugin(info.ServiceName)
	}
	return err
}

func (rh *RPCHost) mustBeDisconnected(info *PluginInfo) error {
	if rh.host.indexOf(info.ServiceName)>=0 {
		return ErrServiceAlreadyRegistered
	}
	return nil
}

func (rh *RPCHost) mustBeConnected(info *PluginInfo) error {
	if rh.host.indexOf(info.ServiceName)<0 {
		return ErrServiceNotRegistered
	}
	return nil
}

// Plugin is used for serving requests from main application.
type Plugin interface {
	Serve()
	Call(serviceMethod string, args interface{}, reply interface{}) error
}

type plugin struct {
	info	*PluginInfo
	log		*log.Logger
	srv		rpcServer
	clnt	rpcClient
}

func (p *plugin) Serve() {
	p.srv.Serve()
}

func (p *plugin) Call(serviceMethod string, args interface{}, reply interface{}) error {
	return p.clnt.Call(serviceMethod, args, reply)
}

func (p *plugin) onServe() {
	var dummy int
	if err := p.Call("RPCHost.ConnectPlugin", p.info, &dummy); err != nil {
		p.log.Fatal(err)
	}
}

func (p *plugin) onStop() {
	var dummy int
	if err := p.Call("RPCHost.DisconnectPlugin", p.info, &dummy); err != nil {
		p.log.Println(err)
	}
}

// NewPlugin creates a new Plugin instance.
func NewPlugin(pluginName string, serviceName string, options ...*Options) (Plugin, error) {
	opt := prepareOptions(pluginName, options...)
	clnt, err := newRPCClient(pluginName, &opt)
	if err == nil {
		opt.Log.Printf("Connected to RPC host: %s\n", opt.dialAddr())
		info := &PluginInfo{Name: pluginName, ServiceName: serviceName}
		if err = clnt.Call("RPCHost.GetPort", info, &info.Port); err == nil {
			p := &plugin{info: info, log: opt.Log, clnt: clnt}
			srvOpt := prepareOptions(pluginName, &Options{Port: info.Port, Log: opt.Log})
			if srv, err := newRPCServer(&srvOpt, &RPCPlugin{plugin: p}); err == nil {
				p.srv = srv
				srv.onServe(p.onServe)
				srv.onStop(p.onStop)
				return p, nil
			}
		}
		clnt.Close()
	}
	opt.Log.Println(err)
	return nil, err
}

// RPCPlugin implements Plugin RPC service. Handles  RPC Host requests.
type RPCPlugin struct {
	plugin	*plugin
}

// Terminate handles RPC Host termination.
func (rp *RPCPlugin) Terminate(_ int, _ *int) error {
	rp.plugin.log.Println("Terminating by RPC host request")
	os.Exit(0)
	return nil
}
<commit_msg>concurency errors fix<commit_after>/*
Package plugins provides support for creating plugin-based applications.

Following workflow is supposed:
	1) main application creates RPC Host service;
	2) plugin starts and connects to the RPC Host:
		2.1) plugin informs RPC Host about service, provided by that plugin and requests port to serve on;
  		2.2) RPC Host checks if such service is not provided by other connected plugin and generates port for plugin to serve on;
		2.3) plugin starts RPC server on port, provided by RPC Host, and informs RPC Host about it;
		2.4) RPC Host registers plugin service;
	3) when plugin terminates, it informs RPC Host; RPC Host unregisters plugin service;
	4) when RPC Host terminates, it informs all connected plugins; plugins terminates;
	5) when main application need to invoke some service method, RPC Host is used for dispatch it;

For handling remote calls net/rpc package usage is supposed.
A plugin must register object(s) that will be used for handling remote calls.
*/
package plugins

import (
	"fmt"
	"log"
	"time"
	"errors"
	"os"
	"os/signal"
	"syscall"
	"sync"
	"net"
	"net/rpc"
)

// Options for RPC client/server.
type Options struct {
	ipAddr	string
	Port	int				// network port for serving connection or connecting to the remote server
	Log		*log.Logger		// a object for logging operations
}

func (o *Options) listenAddr() string {
	return fmt.Sprintf(":%d", o.Port)
}

func (o *Options) dialAddr() string {
	return fmt.Sprintf("%s:%d", o.ipAddr, o.Port)
}

func (o *Options) applyDefaults(name string) {
	if len(o.ipAddr) == 0 {
		o.ipAddr = "127.0.0.1"
	}
	if o.Port <= 0 {
		o.Port = 4000
	}
	if o.Log == nil {
		o.Log = log.New(os.Stdout, fmt.Sprintf("[%s] ", name), 0)
	}
}

func prepareOptions(name string, options ...*Options) Options {
	var opt Options
	if len(options) > 0 {
		opt = *options[0]
	}
	opt.applyDefaults(name)
	return opt
}

// PluginInfo describes a plugin.
type PluginInfo struct {
	Name			string		// Human-readable name of plugin
	ServiceName		string		// Name of service provided by plugin
	Port			int			// Port plugin serves on
}

// rpcServer represents an RPC Server.
type rpcServer interface {
	Serve()
	onServe(func())
	onStop(func())
}

type rpcSrv struct {
	listener	net.Listener
	log			*log.Logger
	running		bool
	sChan		chan os.Signal
	serve		[]func()
	stop		[]func()
}

func (s *rpcSrv) Serve() {
	if !s.running {
		s.running = true
		go func() {
			time.Sleep(500 * time.Millisecond)
			for _, f := range s.serve {
				f()
			}
		}()

		for {
			if conn, err := s.listener.Accept(); err != nil {
				s.log.Fatal("accept error: " + err.Error())         //!!  panic???
			} else {
				go rpc.ServeConn(conn)
			}
		}
	}
};

func (s *rpcSrv) onServe(h func()) {
	s.serve = append(s.serve, h)
}

func (s *rpcSrv) onStop(h func()) {
	s.stop = append(s.stop, h)
}

func (s *rpcSrv) handleStop() {
	for _, f := range s.stop {
		f()
	}
	os.Exit(0);
}

// newRPCServer creates a new rpcServer instance.
func newRPCServer(opt *Options, rpcServices ...interface{}) (rpcServer, error) {
	err := registerRPCServices(rpcServices...)
	if err == nil {
		opt.applyDefaults("RPC Server")
		var listener net.Listener
		if listener, err = net.Listen("tcp", opt.listenAddr()); err == nil {
			opt.Log.Printf("Listening on %s\n", opt.listenAddr())
			srv := &rpcSrv {
				listener: listener,
				log: opt.Log,
				sChan: make(chan os.Signal, 1),
				serve: make([]func(), 0),
				stop: make([]func(), 0)}
			signal.Notify(srv.sChan, os.Interrupt, os.Kill, syscall.SIGTERM)
			go func() {
				<- srv.sChan
				if srv.running {
					srv.handleStop()
				}
			}()
			return srv, nil
		}
	}
	return nil, err;
}

func registerRPCServices(rpcServices ...interface{}) error {
	for _, rpcService := range rpcServices {
		err := rpc.Register(rpcService)
		if err != nil {
			return err
		}
	}
	return nil
}

// rpcClient represents an RPC Client.
type rpcClient interface {
	Call(serviceMethod string, args interface{}, reply interface{}) error
	Close() error
}

type rpcClnt struct {
	clnt	*rpc.Client
	log		*log.Logger
}

func (c *rpcClnt) Call(serviceMethod string, args interface{}, reply interface{}) error {
	return c.clnt.Call(serviceMethod, args, reply)
}

func (c *rpcClnt) Close() error {
	return c.clnt.Close()
}

// newRPCClient creates a new rpcClient instance.
func newRPCClient(name string, options ...*Options) (rpcClient, error) {
	opt := prepareOptions(fmt.Sprintf("plugin %s", name), options...)
	clnt, err := rpc.Dial("tcp", opt.dialAddr())
	if err == nil {
		return &rpcClnt{clnt: clnt, log: opt.Log}, nil
	}
	return nil, err;

}

// PluginNotifyFunc is a callback function to recieve notifications
type PluginNotifyFunc func(PluginInfo)

// A Host is a manager that plugins connects to.
// Host uses connected plugins for handling main application calls.
type Host interface {
	Serve()
	Services() []string
	OnConnectPlugin(f PluginNotifyFunc)
	OnDisconnectPlugin(f PluginNotifyFunc)
	Call(serviceName string, serviceMethod string, args interface{}, reply interface{}) error
}

// ErrServiceNotRegistered returns when trying to access non-registered service
var ErrServiceNotRegistered = errors.New("service is not registered")

// ErrServiceAlreadyRegistered throws when trying to register already registered service
var ErrServiceAlreadyRegistered = errors.New("service is already registered")

type connectedPlugin struct {
	info	*PluginInfo
	clnt	rpcClient
}

func (c *connectedPlugin) Call(serviceMethod string, args interface{}, reply interface{}) error {
	return c.clnt.Call(serviceMethod, args, reply)
}

type host struct {
	srv				rpcServer
	log				*log.Logger
	mutex			*sync.Mutex
	nextPort		int					// free network port to connect
	availPorts		[]int				// network ports that are returned by disconnected plugins
	plugins			[]*connectedPlugin
	onConnect		[]PluginNotifyFunc
	onDisconnect	[]PluginNotifyFunc
}

func (h *host) Serve() {
	go h.srv.Serve()
}

func (h *host) Services() []string {
	rslt := make([]string, len(h.plugins))
	for i, cp := range h.plugins {
		rslt[i] = cp.info.ServiceName
	}
	return rslt
}

func (h *host) OnConnectPlugin(f PluginNotifyFunc) {
	h.mutex.Lock()
	defer h.mutex.Unlock()
	h.onConnect = append(h.onConnect, f)
}

func (h *host) OnDisconnectPlugin(f PluginNotifyFunc) {
	h.mutex.Lock()
	defer h.mutex.Unlock()
	h.onDisconnect = append(h.onDisconnect, f)
}

func (h *host) Call(serviceName string, serviceMethod string, args interface{}, reply interface{}) error {
	var err error
	if i := h.indexOf(serviceName); i>=0 {
		if err = h.plugins[i].Call(serviceMethod, args, reply); err == rpc.ErrShutdown {
			h.removePlugin(serviceName)
			err = ErrServiceNotRegistered
		}
		return err
	}
	return ErrServiceNotRegistered
}

func (h *host) getNextPort() int {
	h.mutex.Lock()
	defer h.mutex.Unlock()
	var rslt int
	if len(h.availPorts) > 0 {
		i := len(h.availPorts) - 1
		rslt, h.availPorts = h.availPorts[i], h.availPorts[:i]
	} else {
		rslt = h.nextPort
		h.nextPort++
	}
	return rslt
}

func (h *host) appendPlugin(cp *connectedPlugin) {
	h.mutex.Lock()
	defer h.mutex.Unlock()
	if i := h.indexOf(cp.info.ServiceName); i<0 {
		h.plugins = append(h.plugins, cp)
		for _, f := range h.onConnect {
			f(*cp.info)
		}
	}
}

func (h *host) removePlugin(serviceName string) {
	h.mutex.Lock()
	defer h.mutex.Unlock()
	if i := h.indexOf(serviceName); i>=0 {
		cp := h.plugins[i]
		var dummy int
		cp.Call("RPCPlugin.Terminate", dummy, &dummy)
		h.log.Printf("Plugin disconnected: \"%s\"", cp.info.Name)
		for _, f := range h.onDisconnect {
			f(*cp.info)
		}
		h.availPorts = append(h.availPorts, cp.info.Port)
		h.plugins = append(h.plugins[:i], h.plugins[i+1:]...)
	}
}

func (h *host) onStop() {
	for len(h.plugins)>0 {
		h.removePlugin(h.plugins[0].info.ServiceName)
	}
}

func (h *host) indexOf(serviceName string) int {
	for i, cp := range h.plugins {
		if cp.info.ServiceName == serviceName {
			return i
		}
	}
	return -1
}

// NewHost creates a new Host instance.
func NewHost(name string, options ...*Options) (Host, error) {
	opt := prepareOptions(name, options...)
	h := &host{
		log: opt.Log,
		mutex: &sync.Mutex{},
		nextPort: opt.Port+1,
		availPorts: make([]int, 0),
		plugins: make([]*connectedPlugin, 0),
		onConnect: make([]PluginNotifyFunc, 0),
		onDisconnect: make([]PluginNotifyFunc, 0)}
	srv, err := newRPCServer(&opt, &RPCHost{host: h})
	if err == nil {
		h.srv = srv
		srv.onStop(h.onStop)
		return h, nil
	}
	opt.Log.Println(err)
	return nil, err
}

// RPCHost implements Host RPC service. Handles plugins requests.
type RPCHost struct {
	host *host
}

// GetPort generates port for plugin to serve on.
func (rh *RPCHost) GetPort(info *PluginInfo, port *int) error {
	err := rh.mustBeDisconnected(info)
	if err == nil {
		*port = rh.host.getNextPort()
	}
	return err
}

// ConnectPlugin handles plugin connection to host.
func (rh *RPCHost) ConnectPlugin(info *PluginInfo, _ *int) error {
	err := rh.mustBeDisconnected(info)
	if err == nil {
		opt := prepareOptions(info.Name, &Options{Port: info.Port, Log: rh.host.log})
		var clnt rpcClient
		if clnt, err = newRPCClient(info.Name, &opt); err == nil {
			cp := &connectedPlugin{info: info, clnt: clnt}
			rh.host.appendPlugin(cp)
			rh.host.log.Printf("Plugin connected: \"%s\", handling \"%s\", serves at %s", cp.info.Name, cp.info.ServiceName, opt.dialAddr())
			return nil
		}
	}
	return err
}

// DisconnectPlugin handles plugin disconnection from host.
func (rh *RPCHost) DisconnectPlugin(info *PluginInfo, _ *int) error {
	err := rh.mustBeConnected(info)
	if err == nil {
		rh.host.removePlugin(info.ServiceName)
	}
	return err
}

func (rh *RPCHost) mustBeDisconnected(info *PluginInfo) error {
	if rh.host.indexOf(info.ServiceName)>=0 {
		return ErrServiceAlreadyRegistered
	}
	return nil
}

func (rh *RPCHost) mustBeConnected(info *PluginInfo) error {
	if rh.host.indexOf(info.ServiceName)<0 {
		return ErrServiceNotRegistered
	}
	return nil
}

// Plugin is used for serving requests from main application.
type Plugin interface {
	Serve()
	Call(serviceMethod string, args interface{}, reply interface{}) error
}

type plugin struct {
	info	*PluginInfo
	log		*log.Logger
	srv		rpcServer
	clnt	rpcClient
}

func (p *plugin) Serve() {
	p.srv.Serve()
}

func (p *plugin) Call(serviceMethod string, args interface{}, reply interface{}) error {
	return p.clnt.Call(serviceMethod, args, reply)
}

func (p *plugin) onServe() {
	var dummy int
	if err := p.Call("RPCHost.ConnectPlugin", p.info, &dummy); err != nil {
		p.log.Fatal(err)
	}
}

func (p *plugin) onStop() {
	var dummy int
	if err := p.Call("RPCHost.DisconnectPlugin", p.info, &dummy); err != nil {
		p.log.Println(err)
	}
}

// NewPlugin creates a new Plugin instance.
func NewPlugin(pluginName string, serviceName string, options ...*Options) (Plugin, error) {
	opt := prepareOptions(pluginName, options...)
	clnt, err := newRPCClient(pluginName, &opt)
	if err == nil {
		opt.Log.Printf("Connected to RPC host: %s\n", opt.dialAddr())
		info := &PluginInfo{Name: pluginName, ServiceName: serviceName}
		if err = clnt.Call("RPCHost.GetPort", info, &info.Port); err == nil {
			p := &plugin{info: info, log: opt.Log, clnt: clnt}
			srvOpt := prepareOptions(pluginName, &Options{Port: info.Port, Log: opt.Log})
			var srv rpcServer
			if srv, err = newRPCServer(&srvOpt, &RPCPlugin{plugin: p}); err == nil {
				p.srv = srv
				srv.onServe(p.onServe)
				srv.onStop(p.onStop)
				return p, nil
			}
		}
		clnt.Close()
	}
	opt.Log.Println(err)
	return nil, err
}

// RPCPlugin implements Plugin RPC service. Handles  RPC Host requests.
type RPCPlugin struct {
	plugin	*plugin
}

// Terminate handles RPC Host termination.
func (rp *RPCPlugin) Terminate(_ int, _ *int) error {
	rp.plugin.log.Println("Terminating by RPC host request")
	os.Exit(0)
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package archive

import (
	"compress/gzip"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/juju/errors"
	"github.com/juju/utils/tar"

	"github.com/juju/juju/state/backups/metadata"
)

// Workspace is a wrapper around backup archive info that has a concrete
// root directory and an archive unpacked in it.
type Workspace struct {
	Archive
}

func newWorkspace() (*Workspace, error) {
	dirName, err := ioutil.TempDir("", "juju-backups-")
	if err != nil {
		return nil, errors.Annotate(err, "while creating workspace dir")
	}

	// Populate the workspace info.
	ws := Workspace{
		Archive: Archive{
			UnpackedRootDir: dirName,
		},
	}
	return &ws, nil
}

// NewWorkspace returns a new workspace with the compressed archive
// file unpacked into the workspace dir.
func NewWorkspace(archiveFile io.Reader) (*Workspace, error) {
	ws, err := newWorkspace()
	if err != nil {
		return nil, errors.Trace(err)
	}

	// Unpack the archive.
	tarFile, err := gzip.NewReader(archiveFile)
	if err != nil {
		return ws, errors.Annotate(err, "while uncompressing archive file")
	}
	if err := tar.UntarFiles(tarFile, dirName); err != nil {
		return ws, errors.Annotate(err, "while extracting files from archive")
	}

	return ws, nil
}

// Close cleans up the workspace dir.
func (ws *Workspace) Close() error {
	err := os.RemoveAll(ws.UnpackedRootDir)
	return errors.Trace(err)
}

// UnpackFiles unpacks the archived files bundle into the targeted dir.
func (ws *Workspace) UnpackFiles(targetRoot string) error {
	tarFile, err := os.Open(ws.FilesBundle())
	if err != nil {
		return errors.Trace(err)
	}
	defer tarFile.Close()

	if err := tar.UntarFiles(tarFile, targetRoot); err != nil {
		return errors.Annotate(err, "while unpacking system files")
	}

	return nil
}

// OpenFile returns an open ReadCloser for the corresponding file in
// the archived files bundle.
func (ws *Workspace) OpenFile(filename string) (_ io.ReadCloser, err error) {
	if filepath.IsAbs(filename) {
		return nil, errors.Errorf("filename must not be relative, got %q", filename)
	}

	tarFile, err := os.Open(ws.FilesBundle())
	if err != nil {
		return nil, errors.Trace(err)
	}

	_, file, err := tar.FindFile(tarFile, filename)
	if err != nil {
		tarFile.Close()
		return nil, errors.Trace(err)
	}
	return file, nil
}

// Metadata returns the metadata derived from the JSON file in the archive.
func (ws *Workspace) Metadata() (*metadata.Metadata, error) {
	metaFile, err := os.Open(ws.MetadataFile())
	if err != nil {
		return nil, errors.Trace(err)
	}
	defer metaFile.Close()

	meta, err := metadata.NewFromJSONBuffer(metaFile)
	return meta, errors.Trace(err)
}
<commit_msg>Remove superfluous blank lines.<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package archive

import (
	"compress/gzip"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/juju/errors"
	"github.com/juju/utils/tar"

	"github.com/juju/juju/state/backups/metadata"
)

// Workspace is a wrapper around backup archive info that has a concrete
// root directory and an archive unpacked in it.
type Workspace struct {
	Archive
}

func newWorkspace() (*Workspace, error) {
	dirName, err := ioutil.TempDir("", "juju-backups-")
	if err != nil {
		return nil, errors.Annotate(err, "while creating workspace dir")
	}

	// Populate the workspace info.
	ws := Workspace{
		Archive: Archive{
			UnpackedRootDir: dirName,
		},
	}
	return &ws, nil
}

// NewWorkspace returns a new workspace with the compressed archive
// file unpacked into the workspace dir.
func NewWorkspace(archiveFile io.Reader) (*Workspace, error) {
	ws, err := newWorkspace()
	if err != nil {
		return nil, errors.Trace(err)
	}

	// Unpack the archive.
	tarFile, err := gzip.NewReader(archiveFile)
	if err != nil {
		return ws, errors.Annotate(err, "while uncompressing archive file")
	}
	if err := tar.UntarFiles(tarFile, dirName); err != nil {
		return ws, errors.Annotate(err, "while extracting files from archive")
	}
	return ws, nil
}

// Close cleans up the workspace dir.
func (ws *Workspace) Close() error {
	err := os.RemoveAll(ws.UnpackedRootDir)
	return errors.Trace(err)
}

// UnpackFiles unpacks the archived files bundle into the targeted dir.
func (ws *Workspace) UnpackFiles(targetRoot string) error {
	tarFile, err := os.Open(ws.FilesBundle())
	if err != nil {
		return errors.Trace(err)
	}
	defer tarFile.Close()

	if err := tar.UntarFiles(tarFile, targetRoot); err != nil {
		return errors.Annotate(err, "while unpacking system files")
	}
	return nil
}

// OpenFile returns an open ReadCloser for the corresponding file in
// the archived files bundle.
func (ws *Workspace) OpenFile(filename string) (_ io.ReadCloser, err error) {
	if filepath.IsAbs(filename) {
		return nil, errors.Errorf("filename must not be relative, got %q", filename)
	}

	tarFile, err := os.Open(ws.FilesBundle())
	if err != nil {
		return nil, errors.Trace(err)
	}

	_, file, err := tar.FindFile(tarFile, filename)
	if err != nil {
		tarFile.Close()
		return nil, errors.Trace(err)
	}
	return file, nil
}

// Metadata returns the metadata derived from the JSON file in the archive.
func (ws *Workspace) Metadata() (*metadata.Metadata, error) {
	metaFile, err := os.Open(ws.MetadataFile())
	if err != nil {
		return nil, errors.Trace(err)
	}
	defer metaFile.Close()

	meta, err := metadata.NewFromJSONBuffer(metaFile)
	return meta, errors.Trace(err)
}
<|endoftext|>


<commit_before>package btce

import (
	"time"
	"sort"
	"strings"
	"strconv"
	"net/url"
	"io/ioutil"
	"encoding/json"
)

type btcePrivate struct {
	Client *clientTLS
}

type Info struct {
	Success int `json:"success"`
	Return InfoReturn `json:"return"`
	Error string `json:"error"`
}

type InfoReturn struct {
	Funds InfoFunds `json:"funds"`
	Rights InfoRights `json:"rights"`
	TransactionsCount int `json:"Transaction_count"`
	OpenOrders int `json:"open_orders"`
	Time int `json:"server_time"`
}

type InfoRights struct {
	Info int `json:"Info"`
	Trade int `json:"Trade"`
	Withdraw int `json:"withdraw"`
}

type InfoFunds struct {
	USD float64 `json:"usd"`
	BTC float64 `json:"btc"`
	LTC float64 `json:"ltc"`
	NMC float64 `json:"nmc"`
	RUR float64 `json:"rur"`
	EUR float64 `json:"eur"`
	NVC float64 `json:"nvc"`
	TRC float64 `json:"trc"`
	PPC float64 `json:"ppc"`
	FTC float64 `json:"ftc"`
	XPM float64 `json:"xpm"`
}

type TransHistory struct {
	Success int `json:"success"`
	Return TransHistoryReturn `json:"return"`
	Error string `json:"error"`
}

type TransHistoryReturn map[string]TransHistoryTransaction;

type TransHistoryTransaction struct {
	Type int `json:"type"`
	Amount float64 `json:"amount"`
	Currency string `json:"currency"`
	Desc string `json:"desc"`
	Status int `json:"status"`
	Timestamp int `json:"timestamp"`
}

type TradeHistory struct {
	Success int `json:"success"`
	Return TradeHistoryReturn `json:"return"`
	Error string `json:"error"`
}

type TradeHistoryReturn map[string]TradeHistoryTrade;

type TradeHistoryTrade struct {
	Pair string `json:"pair"`
	Type string `json:"type"`
	Amount float64 `json:"amount"`
	Rate float64 `json:"rate"`
	OrderID int `json:"order_id"`
	IsYourOrder int `json:"is_your_order"`
	Timestamp int `json:"timestamp"`
}

type Trade struct {
	Success int `json:"success"`
	Return TradeReturn `json:"return"`
	Error string `json:"error"`
}

type TradeReturn struct {
	Received float64 `json:"received"`
	Remains float64 `json:"remains"`
	Order int `json:"order_id"`
	Funds InfoFunds `json:"funds"`
}

type ActiveOrders struct {
	Success int `json:"success"`
	Return ActiveOrdersReturn `json:"return"`
	Error string `json:"error"`
}

type ActiveOrdersReturn map[string]ActiveOrdersOrder;

type ActiveOrdersOrder struct {
	Pair string `json:"pair"`
	Type string `json:"type"`
	Amount float64 `json:"amount"`
	Rate float64 `json:"rate"`
	OrderID int `json:"order_id"`
	TimestampCreated int `json:"timestamp_created"`
	Status int `json:"status"`
}

type CancelOrder struct {
	Success int `json:"success"`
	Return CancelOrderReturn `json:"return"`
	Error string `json:"error"`
}

type CancelOrderReturn struct {
	OrderID int `json:"order_id"`
	Funds InfoFunds `json:"funds"`
}

func NewPrivate(public string, private string) *btcePrivate {
	client := NewClient(public, private, true);
	return &btcePrivate{client};
}

func (this *btcePrivate) GetInfo() (*Info, error) {
	data := url.Values{};
	data.Add("method", "getInfo");
	data.Add("nonce", strconv.Itoa(int(time.Now().Unix())));
	response, err := this.Client.Request(data, "");
	if err != nil {
		return nil, err;
	}
	defer response.Body.Close();
	body, err := ioutil.ReadAll(response.Body);
	if err != nil {
		return nil, err;
	}
	Info := new(Info);
	err = json.Unmarshal([]byte(body), &Info);
	if err != nil {
		return nil, err;
	}
	return Info, nil;
}

func (this *btcePrivate) TransHistory(From int, Count int, FromID int, EndID int, Order string, Since string, End string) (*TransHistory, error) {
	data := url.Values{};
	data.Add("method", "TransHistory");
	data.Add("nonce", strconv.Itoa(int(time.Now().Unix())));
	data.Add("from", strconv.Itoa(From));
	data.Add("count", strconv.Itoa(Count));
	data.Add("from_id", strconv.Itoa(FromID));
	data.Add("end_id", strconv.Itoa(EndID));
	data.Add("order", Order);
	data.Add("since", Since);
	data.Add("end", End);
	response, err := this.Client.Request(data, "");
	if err != nil {
		return nil, err;
	}
	defer response.Body.Close();
	body, err := ioutil.ReadAll(response.Body);
	if err != nil {
		return nil, err;
	}
	TransHistory := new(TransHistory);
	err = json.Unmarshal([]byte(body), &TransHistory);
	if err != nil {
		return nil, err;
	}
	return TransHistory, nil;
}

func (this *btcePrivate) TradeHistory(From int, Count int, FromID int, EndID int, Order string, Since string, End string, Pair string) (*TradeHistory, error) {
	data := url.Values{};
	data.Add("method", "TradeHistory");
	data.Add("nonce", strconv.Itoa(int(time.Now().Unix())));
	data.Add("from", strconv.Itoa(From));
	data.Add("count", strconv.Itoa(Count));
	data.Add("from_id", strconv.Itoa(FromID));
	data.Add("end_id", strconv.Itoa(EndID));
	data.Add("order", Order);
	data.Add("since", Since);
	data.Add("end", End);
	data.Add("pair", Pair);
	response, err := this.Client.Request(data, "");
	if err != nil {
		return nil, err;
	}
	defer response.Body.Close();
	body, err := ioutil.ReadAll(response.Body);
	if err != nil {
		return nil, err;
	}
	TradeHistory := new(TradeHistory);
	err = json.Unmarshal([]byte(body), &TradeHistory);
	if err != nil {
		return nil, err;
	}
	return TradeHistory, nil;
}

type ByTradeTimestamp []TradeHistoryTrade;

func (this ByTradeTimestamp) Len() int { return len(this); }
func (this ByTradeTimestamp) Swap(i, j int) { this[i], this[j] = this[j], this[i]; }
func (this ByTradeTimestamp) Less(i, j int) bool { return this[i].Timestamp > this[j].Timestamp; }

func (this *btcePrivate) OrderedTradeHistory(From int, Count int, FromID int, EndID int, Order string, Since string, End string, Pair string) (*TradeHistory, error) {
	tradeHistory, err := this.TradeHistory(From, Count, FromID, EndID, Order, Since, End, Pair);
	if err != nil {
		return nil, err;
	}
	temp := make([]TradeHistoryTrade, 0);
	for _, value := range tradeHistory.Return {
		temp = append(temp, value);
	}
	sort.Sort(ByTradeTimestamp(temp));
	tradeHistory.Return = map[string]TradeHistoryTrade{};
	for key, value := range temp {
		tradeHistory.Return[strconv.Itoa(key)] = value;
	}
	return tradeHistory, nil;
}

func (this *btcePrivate) LastTrade(Pair string) (*TradeHistoryTrade, error) {
	tradeHistory, err := this.OrderedTradeHistory(0, 1000, 0, 0, "ASC", "", "", Pair);
	if err != nil {
		return nil, err;
	}
	lastTrade := new(TradeHistoryTrade);
	*lastTrade = tradeHistory.Return["0"]
	return lastTrade, nil;
}

func (this *btcePrivate) ActiveOrders(Pair string) (*ActiveOrders, error) {
	data := url.Values{};
	data.Add("method", "Trade");
	data.Add("nonce", strconv.Itoa(int(time.Now().Unix())));
	data.Add("pair", Pair);
	response, err := this.Client.Request(data, "");
	if err != nil {
		return nil, err;
	}
	defer response.Body.Close();
	body, err := ioutil.ReadAll(response.Body);
	if err != nil {
		return nil, err;
	}
	ActiveOrders := new(ActiveOrders);
	err = json.Unmarshal([]byte(body), &ActiveOrders);
	if err != nil {
		return nil, err;
	}
	return ActiveOrders, nil;

}

type ByActiveOrdersTimestampCreated []ActiveOrdersOrder;

func (this ByActiveOrdersTimestampCreated) Len() int { return len(this); }
func (this ByActiveOrdersTimestampCreated) Swap(i, j int) { this[i], this[j] = this[j], this[i]; }
func (this ByActiveOrdersTimestampCreated) Less(i, j int) bool { return this[i].TimestampCreated > this[j].TimestampCreated; }

func (this *btcePrivate) OrderedActiveOrders(Pair string) (*ActiveOrders, error) {
	activeOrders, err := this.ActiveOrders(Pair);
	if err != nil {
		return nil, err;
	}
	temp := make([]ActiveOrdersOrder, 0);
	for _, value := range activeOrders.Return {
		temp = append(temp, value);
	}
	sort.Sort(ByActiveOrdersTimestampCreated(temp));
	activeOrders.Return = map[string]ActiveOrdersOrder{};
	for key, value := range temp {
		activeOrders.Return[strconv.Itoa(key)] = value;
	}
	return activeOrders, nil;
}

func (this *btcePrivate) Balances(Pair string) ([]float64, error) {
	info, err := this.GetInfo();
	if err != nil {
		return nil, err;
	}
	newPair := strings.Split(strings.ToUpper(Pair), "_");
	balances := make([]float64, 2);
	for i := 0; i < 2; i++ {
		if newPair[i] == "USD" {
			balances[i] = info.Return.Funds.USD;
		} else if newPair[i] == "BTC" {
			balances[i] = info.Return.Funds.BTC;
		} else if newPair[i] == "LTC" {
			balances[i] = info.Return.Funds.LTC;
		} else if newPair[i] == "NMC" {
			balances[i] = info.Return.Funds.NMC;
		} else if newPair[i] == "RUR" {
			balances[i] = info.Return.Funds.RUR;
		} else if newPair[i] == "EUR" {
			balances[i] = info.Return.Funds.EUR;
		} else if newPair[i] == "NVC" {
			balances[i] = info.Return.Funds.NVC;
		} else if newPair[i] == "TRC" {
			balances[i] = info.Return.Funds.TRC;
		} else if newPair[i] == "PPC" {
			balances[i] = info.Return.Funds.PPC;
		} else if newPair[i] == "FTC" {
			balances[i] = info.Return.Funds.FTC;
		} else if newPair[i] == "XPM" {
			balances[i] = info.Return.Funds.XPM;
		}
	}
	return balances, nil;
}

func (this *btcePrivate) Trade(Pair string, Type string, Rate float64, Amount float64) (*Trade, error) {
	data := url.Values{};
	data.Add("method", "Trade");
	data.Add("nonce", strconv.Itoa(int(time.Now().Unix())));
	data.Add("pair", Pair);
	data.Add("type", Type);
	Rate, _  = strconv.ParseFloat(strconv.FormatFloat(Rate, 'f', 6, 64), 64);
	data.Add("rate", fmt.Sprintf("%g", Rate));
	Amount, _ = strconv.ParseFloat(strconv.FormatFloat(Amount, 'f', 8, 64), 64);
	data.Add("amount", fmt.Sprintf("%g", Amount));
	response, err := this.Client.Request(data, "");
	if err != nil {
		return nil, err;
	}
	defer response.Body.Close();
	body, err := ioutil.ReadAll(response.Body);
	if err != nil {
		return nil, err;
	}
	Trade := new(Trade);
	err = json.Unmarshal([]byte(body), &Trade);
	if err != nil {
		return nil, err;
	}
	return Trade, nil;
}

func (this *btcePrivate) CancelOrder(OrderID int) (*CancelOrder, error) {
	data := url.Values{};
	data.Add("method", "CancelOrder");
	data.Add("nonce", strconv.Itoa(int(time.Now().Unix())));
	data.Add("order_id", strconv.Itoa(OrderID));
	response, err := this.Client.Request(data, "");
	if err != nil {
		return nil, err;
	}
	defer response.Body.Close();
	body, err := ioutil.ReadAll(response.Body);
	if err != nil {
		return nil, err;
	}
	CancelOrder := new(CancelOrder);
	err = json.Unmarshal([]byte(body), &CancelOrder);
	if err != nil {
		return nil, err;
	}
	return CancelOrder, nil;

}
<commit_msg>fix fmt not imported<commit_after>package btce

import (
	"fmt"
	"time"
	"sort"
	"strings"
	"strconv"
	"net/url"
	"io/ioutil"
	"encoding/json"
)

type btcePrivate struct {
	Client *clientTLS
}

type Info struct {
	Success int `json:"success"`
	Return InfoReturn `json:"return"`
	Error string `json:"error"`
}

type InfoReturn struct {
	Funds InfoFunds `json:"funds"`
	Rights InfoRights `json:"rights"`
	TransactionsCount int `json:"Transaction_count"`
	OpenOrders int `json:"open_orders"`
	Time int `json:"server_time"`
}

type InfoRights struct {
	Info int `json:"Info"`
	Trade int `json:"Trade"`
	Withdraw int `json:"withdraw"`
}

type InfoFunds struct {
	USD float64 `json:"usd"`
	BTC float64 `json:"btc"`
	LTC float64 `json:"ltc"`
	NMC float64 `json:"nmc"`
	RUR float64 `json:"rur"`
	EUR float64 `json:"eur"`
	NVC float64 `json:"nvc"`
	TRC float64 `json:"trc"`
	PPC float64 `json:"ppc"`
	FTC float64 `json:"ftc"`
	XPM float64 `json:"xpm"`
}

type TransHistory struct {
	Success int `json:"success"`
	Return TransHistoryReturn `json:"return"`
	Error string `json:"error"`
}

type TransHistoryReturn map[string]TransHistoryTransaction;

type TransHistoryTransaction struct {
	Type int `json:"type"`
	Amount float64 `json:"amount"`
	Currency string `json:"currency"`
	Desc string `json:"desc"`
	Status int `json:"status"`
	Timestamp int `json:"timestamp"`
}

type TradeHistory struct {
	Success int `json:"success"`
	Return TradeHistoryReturn `json:"return"`
	Error string `json:"error"`
}

type TradeHistoryReturn map[string]TradeHistoryTrade;

type TradeHistoryTrade struct {
	Pair string `json:"pair"`
	Type string `json:"type"`
	Amount float64 `json:"amount"`
	Rate float64 `json:"rate"`
	OrderID int `json:"order_id"`
	IsYourOrder int `json:"is_your_order"`
	Timestamp int `json:"timestamp"`
}

type Trade struct {
	Success int `json:"success"`
	Return TradeReturn `json:"return"`
	Error string `json:"error"`
}

type TradeReturn struct {
	Received float64 `json:"received"`
	Remains float64 `json:"remains"`
	Order int `json:"order_id"`
	Funds InfoFunds `json:"funds"`
}

type ActiveOrders struct {
	Success int `json:"success"`
	Return ActiveOrdersReturn `json:"return"`
	Error string `json:"error"`
}

type ActiveOrdersReturn map[string]ActiveOrdersOrder;

type ActiveOrdersOrder struct {
	Pair string `json:"pair"`
	Type string `json:"type"`
	Amount float64 `json:"amount"`
	Rate float64 `json:"rate"`
	OrderID int `json:"order_id"`
	TimestampCreated int `json:"timestamp_created"`
	Status int `json:"status"`
}

type CancelOrder struct {
	Success int `json:"success"`
	Return CancelOrderReturn `json:"return"`
	Error string `json:"error"`
}

type CancelOrderReturn struct {
	OrderID int `json:"order_id"`
	Funds InfoFunds `json:"funds"`
}

func NewPrivate(public string, private string) *btcePrivate {
	client := NewClient(public, private, true);
	return &btcePrivate{client};
}

func (this *btcePrivate) GetInfo() (*Info, error) {
	data := url.Values{};
	data.Add("method", "getInfo");
	data.Add("nonce", strconv.Itoa(int(time.Now().Unix())));
	response, err := this.Client.Request(data, "");
	if err != nil {
		return nil, err;
	}
	defer response.Body.Close();
	body, err := ioutil.ReadAll(response.Body);
	if err != nil {
		return nil, err;
	}
	Info := new(Info);
	err = json.Unmarshal([]byte(body), &Info);
	if err != nil {
		return nil, err;
	}
	return Info, nil;
}

func (this *btcePrivate) TransHistory(From int, Count int, FromID int, EndID int, Order string, Since string, End string) (*TransHistory, error) {
	data := url.Values{};
	data.Add("method", "TransHistory");
	data.Add("nonce", strconv.Itoa(int(time.Now().Unix())));
	data.Add("from", strconv.Itoa(From));
	data.Add("count", strconv.Itoa(Count));
	data.Add("from_id", strconv.Itoa(FromID));
	data.Add("end_id", strconv.Itoa(EndID));
	data.Add("order", Order);
	data.Add("since", Since);
	data.Add("end", End);
	response, err := this.Client.Request(data, "");
	if err != nil {
		return nil, err;
	}
	defer response.Body.Close();
	body, err := ioutil.ReadAll(response.Body);
	if err != nil {
		return nil, err;
	}
	TransHistory := new(TransHistory);
	err = json.Unmarshal([]byte(body), &TransHistory);
	if err != nil {
		return nil, err;
	}
	return TransHistory, nil;
}

func (this *btcePrivate) TradeHistory(From int, Count int, FromID int, EndID int, Order string, Since string, End string, Pair string) (*TradeHistory, error) {
	data := url.Values{};
	data.Add("method", "TradeHistory");
	data.Add("nonce", strconv.Itoa(int(time.Now().Unix())));
	data.Add("from", strconv.Itoa(From));
	data.Add("count", strconv.Itoa(Count));
	data.Add("from_id", strconv.Itoa(FromID));
	data.Add("end_id", strconv.Itoa(EndID));
	data.Add("order", Order);
	data.Add("since", Since);
	data.Add("end", End);
	data.Add("pair", Pair);
	response, err := this.Client.Request(data, "");
	if err != nil {
		return nil, err;
	}
	defer response.Body.Close();
	body, err := ioutil.ReadAll(response.Body);
	if err != nil {
		return nil, err;
	}
	TradeHistory := new(TradeHistory);
	err = json.Unmarshal([]byte(body), &TradeHistory);
	if err != nil {
		return nil, err;
	}
	return TradeHistory, nil;
}

type ByTradeTimestamp []TradeHistoryTrade;

func (this ByTradeTimestamp) Len() int { return len(this); }
func (this ByTradeTimestamp) Swap(i, j int) { this[i], this[j] = this[j], this[i]; }
func (this ByTradeTimestamp) Less(i, j int) bool { return this[i].Timestamp > this[j].Timestamp; }

func (this *btcePrivate) OrderedTradeHistory(From int, Count int, FromID int, EndID int, Order string, Since string, End string, Pair string) (*TradeHistory, error) {
	tradeHistory, err := this.TradeHistory(From, Count, FromID, EndID, Order, Since, End, Pair);
	if err != nil {
		return nil, err;
	}
	temp := make([]TradeHistoryTrade, 0);
	for _, value := range tradeHistory.Return {
		temp = append(temp, value);
	}
	sort.Sort(ByTradeTimestamp(temp));
	tradeHistory.Return = map[string]TradeHistoryTrade{};
	for key, value := range temp {
		tradeHistory.Return[strconv.Itoa(key)] = value;
	}
	return tradeHistory, nil;
}

func (this *btcePrivate) LastTrade(Pair string) (*TradeHistoryTrade, error) {
	tradeHistory, err := this.OrderedTradeHistory(0, 1000, 0, 0, "ASC", "", "", Pair);
	if err != nil {
		return nil, err;
	}
	lastTrade := new(TradeHistoryTrade);
	*lastTrade = tradeHistory.Return["0"]
	return lastTrade, nil;
}

func (this *btcePrivate) ActiveOrders(Pair string) (*ActiveOrders, error) {
	data := url.Values{};
	data.Add("method", "Trade");
	data.Add("nonce", strconv.Itoa(int(time.Now().Unix())));
	data.Add("pair", Pair);
	response, err := this.Client.Request(data, "");
	if err != nil {
		return nil, err;
	}
	defer response.Body.Close();
	body, err := ioutil.ReadAll(response.Body);
	if err != nil {
		return nil, err;
	}
	ActiveOrders := new(ActiveOrders);
	err = json.Unmarshal([]byte(body), &ActiveOrders);
	if err != nil {
		return nil, err;
	}
	return ActiveOrders, nil;

}

type ByActiveOrdersTimestampCreated []ActiveOrdersOrder;

func (this ByActiveOrdersTimestampCreated) Len() int { return len(this); }
func (this ByActiveOrdersTimestampCreated) Swap(i, j int) { this[i], this[j] = this[j], this[i]; }
func (this ByActiveOrdersTimestampCreated) Less(i, j int) bool { return this[i].TimestampCreated > this[j].TimestampCreated; }

func (this *btcePrivate) OrderedActiveOrders(Pair string) (*ActiveOrders, error) {
	activeOrders, err := this.ActiveOrders(Pair);
	if err != nil {
		return nil, err;
	}
	temp := make([]ActiveOrdersOrder, 0);
	for _, value := range activeOrders.Return {
		temp = append(temp, value);
	}
	sort.Sort(ByActiveOrdersTimestampCreated(temp));
	activeOrders.Return = map[string]ActiveOrdersOrder{};
	for key, value := range temp {
		activeOrders.Return[strconv.Itoa(key)] = value;
	}
	return activeOrders, nil;
}

func (this *btcePrivate) Balances(Pair string) ([]float64, error) {
	info, err := this.GetInfo();
	if err != nil {
		return nil, err;
	}
	newPair := strings.Split(strings.ToUpper(Pair), "_");
	balances := make([]float64, 2);
	for i := 0; i < 2; i++ {
		if newPair[i] == "USD" {
			balances[i] = info.Return.Funds.USD;
		} else if newPair[i] == "BTC" {
			balances[i] = info.Return.Funds.BTC;
		} else if newPair[i] == "LTC" {
			balances[i] = info.Return.Funds.LTC;
		} else if newPair[i] == "NMC" {
			balances[i] = info.Return.Funds.NMC;
		} else if newPair[i] == "RUR" {
			balances[i] = info.Return.Funds.RUR;
		} else if newPair[i] == "EUR" {
			balances[i] = info.Return.Funds.EUR;
		} else if newPair[i] == "NVC" {
			balances[i] = info.Return.Funds.NVC;
		} else if newPair[i] == "TRC" {
			balances[i] = info.Return.Funds.TRC;
		} else if newPair[i] == "PPC" {
			balances[i] = info.Return.Funds.PPC;
		} else if newPair[i] == "FTC" {
			balances[i] = info.Return.Funds.FTC;
		} else if newPair[i] == "XPM" {
			balances[i] = info.Return.Funds.XPM;
		}
	}
	return balances, nil;
}

func (this *btcePrivate) Trade(Pair string, Type string, Rate float64, Amount float64) (*Trade, error) {
	data := url.Values{};
	data.Add("method", "Trade");
	data.Add("nonce", strconv.Itoa(int(time.Now().Unix())));
	data.Add("pair", Pair);
	data.Add("type", Type);
	Rate, _  = strconv.ParseFloat(strconv.FormatFloat(Rate, 'f', 6, 64), 64);
	data.Add("rate", fmt.Sprintf("%g", Rate));
	Amount, _ = strconv.ParseFloat(strconv.FormatFloat(Amount, 'f', 8, 64), 64);
	data.Add("amount", fmt.Sprintf("%g", Amount));
	response, err := this.Client.Request(data, "");
	if err != nil {
		return nil, err;
	}
	defer response.Body.Close();
	body, err := ioutil.ReadAll(response.Body);
	if err != nil {
		return nil, err;
	}
	Trade := new(Trade);
	err = json.Unmarshal([]byte(body), &Trade);
	if err != nil {
		return nil, err;
	}
	return Trade, nil;
}

func (this *btcePrivate) CancelOrder(OrderID int) (*CancelOrder, error) {
	data := url.Values{};
	data.Add("method", "CancelOrder");
	data.Add("nonce", strconv.Itoa(int(time.Now().Unix())));
	data.Add("order_id", strconv.Itoa(OrderID));
	response, err := this.Client.Request(data, "");
	if err != nil {
		return nil, err;
	}
	defer response.Body.Close();
	body, err := ioutil.ReadAll(response.Body);
	if err != nil {
		return nil, err;
	}
	CancelOrder := new(CancelOrder);
	err = json.Unmarshal([]byte(body), &CancelOrder);
	if err != nil {
		return nil, err;
	}
	return CancelOrder, nil;

}
<|endoftext|>


<commit_before>package genetics

import (
	"github.com/yaricom/goNEAT/neat/network"
	"fmt"
)

// Organisms are Genomes and Networks with fitness information,
// i.e. the genotype and phenotype together.
type Organism struct {
	// A measure of fitness for the Organism
	Fitness                   float64
	// A fitness measure that won't change during adjustments
	OriginalFitness           float64

	// Win marker (if needed for a particular task)
	IsWinner                  bool

	// The Organism's phenotype
	Net                       *network.Network
	// The Organism's genotype
	GNome                     *Genome
	// The Species of the Organism
	SpeciesOf                 *Species

	// Number of children this Organism may have
	ExpectedOffspring         float64
	// Tells which generation this Organism is from
	Generation                int

	// Marker for destruction of inferior Organisms
	ToEliminate               bool
	// Marks the species champion
	IsChampion                bool

	// Number of reserved offspring for a population leader
	SuperChampOffspring       int
	// Marks the best in population
	IsPopulationChampion      bool
	// Marks the duplicate child of a champion (for tracking purposes)
	IsPopulationChampionChild bool

	// DEBUG variable - highest fitness of champ
	highestFitness            float64

	// Track its origin - for debugging or analysis - we can tell how the organism was born
	mutationStructBaby        bool
	mateBaby                  bool

	// Used just for reporting purposes
	error float64
}

// Creates new organism with specified genome, fitness and given generation number
func NewOrganism(fit float64, g *Genome, generation int) *Organism {
	return &Organism{
		Fitness:fit,
		GNome:g,
		Net:g.Phenotype, // TODO g.genessis(g.Id)
		Generation:generation,
	}
}

// Regenerate the network based on a change in the genotype
func (o *Organism) UpdatePhenotype() {
	// First, delete the old phenotype (net)
	o.Net = nil

	// Now, recreate the phenotype off the new genotype
	o.Net = o.GNome.genesis(o.GNome.Id)
}

func (o *Organism) String() string {
	champStr := ""
	if o.IsChampion {
		champStr = " - CHAMPION - "
	}
	eliminStr := ""
	if o.ToEliminate {
		eliminStr = " - TO BE ELIMINATED - "
	}
	return fmt.Sprintf("[Organism generation: %d, fitness: %.3f, original fitness: %.3f%s%s]",
		o.Generation, o.Fitness, o.OriginalFitness, champStr, eliminStr)
}

// ByFitness implements sort.Interface for []Organism based on the Fitness field in descending order.
type ByFitness []*Organism
func (f ByFitness) Len() int {
	return len(f)
}
func (f ByFitness) Swap(i, j int) {
	f[i], f[j] = f[j], f[i]
}
func (f ByFitness) Less(i, j int) bool {
	return f[i].Fitness > f[j].Fitness
}
<commit_msg>Fixed new organism creation by genesis of new network per organism<commit_after>package genetics

import (
	"github.com/yaricom/goNEAT/neat/network"
	"fmt"
)

// Organisms are Genomes and Networks with fitness information,
// i.e. the genotype and phenotype together.
type Organism struct {
	// A measure of fitness for the Organism
	Fitness                   float64
	// A fitness measure that won't change during adjustments
	OriginalFitness           float64

	// Win marker (if needed for a particular task)
	IsWinner                  bool

	// The Organism's phenotype
	Net                       *network.Network
	// The Organism's genotype
	GNome                     *Genome
	// The Species of the Organism
	SpeciesOf                 *Species

	// Number of children this Organism may have
	ExpectedOffspring         float64
	// Tells which generation this Organism is from
	Generation                int

	// Marker for destruction of inferior Organisms
	ToEliminate               bool
	// Marks the species champion
	IsChampion                bool

	// Number of reserved offspring for a population leader
	SuperChampOffspring       int
	// Marks the best in population
	IsPopulationChampion      bool
	// Marks the duplicate child of a champion (for tracking purposes)
	IsPopulationChampionChild bool

	// DEBUG variable - highest fitness of champ
	highestFitness            float64

	// Track its origin - for debugging or analysis - we can tell how the organism was born
	mutationStructBaby        bool
	mateBaby                  bool

	// Used just for reporting purposes
	error float64
}

// Creates new organism with specified genome, fitness and given generation number
func NewOrganism(fit float64, g *Genome, generation int) *Organism {
	return &Organism{
		Fitness:fit,
		GNome:g,
		Net:g.genesis(g.Id),
		Generation:generation,
	}
}

// Regenerate the network based on a change in the genotype
func (o *Organism) UpdatePhenotype() {
	// First, delete the old phenotype (net)
	o.Net = nil

	// Now, recreate the phenotype off the new genotype
	o.Net = o.GNome.genesis(o.GNome.Id)
}

func (o *Organism) String() string {
	champStr := ""
	if o.IsChampion {
		champStr = " - CHAMPION - "
	}
	eliminStr := ""
	if o.ToEliminate {
		eliminStr = " - TO BE ELIMINATED - "
	}
	return fmt.Sprintf("[Organism generation: %d, fitness: %.3f, original fitness: %.3f%s%s]",
		o.Generation, o.Fitness, o.OriginalFitness, champStr, eliminStr)
}

// ByFitness implements sort.Interface for []Organism based on the Fitness field in descending order.
type ByFitness []*Organism
func (f ByFitness) Len() int {
	return len(f)
}
func (f ByFitness) Swap(i, j int) {
	f[i], f[j] = f[j], f[i]
}
func (f ByFitness) Less(i, j int) bool {
	return f[i].Fitness > f[j].Fitness
}
<|endoftext|>


<commit_before>package stringsutil

import (
	"regexp"
	"strings"
	"unicode"
	"unicode/utf8"
)

// PadLeft prepends a string to a base string until the string
// length is greater or equal to the desired length.
func PadLeft(str string, pad string, length int) string {
	for {
		str = pad + str
		if len(str) >= length {
			return str[0:length]
		}
	}
}

// PadRight appends a string to a base string until the string
// length is greater or equal to the desired length.
func PadRight(str string, pad string, length int) string {
	for {
		str += pad
		if len(str) > length {
			return str[0:length]
		}
	}
}

// ToLowerFirst lower cases the first letter in the string
func ToLowerFirst(s string) string {
	if s == "" {
		return ""
	}
	r, n := utf8.DecodeRuneInString(s)
	return string(unicode.ToLower(r)) + s[n:]
}

// ToUpperFirst upper cases the first letter in the string
func ToUpperFirst(s string) string {
	if s == "" {
		return ""
	}
	r, n := utf8.DecodeRuneInString(s)
	return string(unicode.ToUpper(r)) + s[n:]
}

// CondenseString trims whitespace at the ends of the string
// as well as in between.
func CondenseString(content string, join_lines bool) string {
	if join_lines {
		content = regexp.MustCompile(`\n`).ReplaceAllString(content, " ")
	}
	//rx_beg := regexp.MustCompile(`^\s+`)
	//rx_end := regexp.MustCompile(`\s+$`)
	//rx_mid := regexp.MustCompile(`\n[\s\t\r]*\n`)
	//rx_pre := regexp.MustCompile(`\n[\s\t\r]*`)
	//rx_spc := regexp.MustCompile(`\s+`)
	// Begining
	content = regexp.MustCompile(`^\s+`).ReplaceAllString(content, "")
	// End
	content = regexp.MustCompile(`\s+$`).ReplaceAllString(content, "")
	// Middle
	content = regexp.MustCompile(`\n[\s\t\r]*\n`).ReplaceAllString(content, "\n")
	// Indentation
	content = regexp.MustCompile(`\n[\s\t\r]*`).ReplaceAllString(content, "\n")
	// Collapse
	content = regexp.MustCompile(`\s+`).ReplaceAllString(content, " ")
	return strings.TrimSpace(content)
}

func TrimSentenceLength(sentenceInput string, maxLength int) string {
	if len(sentenceInput) <= maxLength {
		return sentenceInput
	}
	sentenceLen := string(sentenceInput[0:maxLength]) // first350 := string(s[0:350])
	rx_end := regexp.MustCompile(`[[:punct:]][^[[:punct:]]]*$`)
	sentencePunct := rx_end.ReplaceAllString(sentenceLen, "")
	if len(sentencePunct) >= 2 {
		return sentencePunct
	}
	return sentenceLen
}
<commit_msg>fix spelling<commit_after>package stringsutil

import (
	"regexp"
	"strings"
	"unicode"
	"unicode/utf8"
)

// PadLeft prepends a string to a base string until the string
// length is greater or equal to the desired length.
func PadLeft(str string, pad string, length int) string {
	for {
		str = pad + str
		if len(str) >= length {
			return str[0:length]
		}
	}
}

// PadRight appends a string to a base string until the string
// length is greater or equal to the desired length.
func PadRight(str string, pad string, length int) string {
	for {
		str += pad
		if len(str) > length {
			return str[0:length]
		}
	}
}

// ToLowerFirst lower cases the first letter in the string
func ToLowerFirst(s string) string {
	if s == "" {
		return ""
	}
	r, n := utf8.DecodeRuneInString(s)
	return string(unicode.ToLower(r)) + s[n:]
}

// ToUpperFirst upper cases the first letter in the string
func ToUpperFirst(s string) string {
	if s == "" {
		return ""
	}
	r, n := utf8.DecodeRuneInString(s)
	return string(unicode.ToUpper(r)) + s[n:]
}

// CondenseString trims whitespace at the ends of the string
// as well as in between.
func CondenseString(content string, join_lines bool) string {
	if join_lines {
		content = regexp.MustCompile(`\n`).ReplaceAllString(content, " ")
	}
	//rx_beg := regexp.MustCompile(`^\s+`)
	//rx_end := regexp.MustCompile(`\s+$`)
	//rx_mid := regexp.MustCompile(`\n[\s\t\r]*\n`)
	//rx_pre := regexp.MustCompile(`\n[\s\t\r]*`)
	//rx_spc := regexp.MustCompile(`\s+`)
	// Beginning
	content = regexp.MustCompile(`^\s+`).ReplaceAllString(content, "")
	// End
	content = regexp.MustCompile(`\s+$`).ReplaceAllString(content, "")
	// Middle
	content = regexp.MustCompile(`\n[\s\t\r]*\n`).ReplaceAllString(content, "\n")
	// Indentation
	content = regexp.MustCompile(`\n[\s\t\r]*`).ReplaceAllString(content, "\n")
	// Collapse
	content = regexp.MustCompile(`\s+`).ReplaceAllString(content, " ")
	return strings.TrimSpace(content)
}

func TrimSentenceLength(sentenceInput string, maxLength int) string {
	if len(sentenceInput) <= maxLength {
		return sentenceInput
	}
	sentenceLen := string(sentenceInput[0:maxLength]) // first350 := string(s[0:350])
	rx_end := regexp.MustCompile(`[[:punct:]][^[[:punct:]]]*$`)
	sentencePunct := rx_end.ReplaceAllString(sentenceLen, "")
	if len(sentencePunct) >= 2 {
		return sentencePunct
	}
	return sentenceLen
}
<|endoftext|>


<commit_before>// Package strumt provides a way to defines scenarios for prompting
// informations on command line
package strumt

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strings"
)

// Step represents a scenario step which is
// the result of a prompt execution. We store
// the prompt string, inputs that the user has given,
// and the prompt error if one occurred
type Step struct {
	prompt string
	inputs []string
	err    error
}

// PromptString returns the prompt string displayed by the prompt
func (s Step) PromptString() string {
	return s.prompt
}

// Inputs retrieves all inputs given by user
func (s Step) Inputs() []string {
	return s.inputs
}

// Error returns the error triggered by a prompt
func (s Step) Error() error {
	return s.err
}

// NewPrompts creates a new prompt from stdin and stdout
func NewPrompts() Prompts {
	return Prompts{reader: bufio.NewReader(os.Stdin), writer: os.Stdout, prompts: map[string]Prompter{}}
}

// NewPromptsFromReaderAndWriter creates a new prompt from a given reader and writer, useful for testing purpose
func NewPromptsFromReaderAndWriter(reader io.Reader, writer io.Writer) Prompts {
	return Prompts{reader: bufio.NewReader(reader), writer: writer, prompts: map[string]Prompter{}}
}

// Prompts stores all defined prompts and current running prompt
type Prompts struct {
	currentPrompt Prompter
	prompts       map[string]Prompter
	reader        *bufio.Reader
	writer        io.Writer
	scenario      []Step
}

func (p *Prompts) parse() ([]string, Prompter, error) {
	var nextPrompt Prompter
	var inputs []string
	var err error

	switch prompt := p.currentPrompt.(type) {
	case LinePrompter:
		var input string

		input, err = parseLine(p.reader, prompt)

		if prompt.NextOnSuccess(input) != "" {
			nextPrompt = p.prompts[prompt.NextOnSuccess(input)]
		}

		inputs = append(inputs, input)
	case MultilinePrompter:
		inputs, err = parseMultipleLine(p.reader, prompt)

		if prompt.NextOnSuccess(inputs) != "" {
			nextPrompt = p.prompts[prompt.NextOnSuccess(inputs)]
		}
	}

	if err != nil {
		nextPrompt = p.prompts[p.currentPrompt.NextOnError(err)]
	}

	return inputs, nextPrompt, err
}

func (p *Prompts) appendScenario(promptString string, inputs []string, err error) {
	p.scenario = append(
		p.scenario,
		Step{
			promptString,
			inputs,
			err,
		},
	)
}

// AddLinePrompter add a new LinePrompter mapped to a given id
func (p *Prompts) AddLinePrompter(prompt LinePrompter) {
	p.prompts[prompt.ID()] = prompt
}

// AddMultilinePrompter add a new MultilinePrompter mapped to a given id
func (p *Prompts) AddMultilinePrompter(prompt MultilinePrompter) {
	p.prompts[prompt.ID()] = prompt
}

// SetFirst defines from which prompt the prompt sequence has to start
func (p *Prompts) SetFirst(id string) {
	p.currentPrompt = p.prompts[id]
}

// Scenario retrieves all steps done during a prompt sequence
func (p *Prompts) Scenario() []Step {
	return p.scenario
}

// Run executes a prompt sequence
func (p *Prompts) Run() {
	p.scenario = []Step{}

	for {
		prompt := p.currentPrompt
		renderPrompt(p.writer, prompt)

		inputs, nextPrompt, err := p.parse()

		if err != nil {
			renderError(p.writer, prompt, err)
		}

		p.appendScenario(prompt.PromptString(), inputs, err)

		if nextPrompt == nil {
			return
		}

		renderSeparator(p.writer, prompt)

		p.currentPrompt = nextPrompt
	}
}

func isMultilineEnd(reader io.ByteScanner) (bool, error) {
	bn, err := reader.ReadByte()

	if err == io.EOF {
		return true, nil
	}

	if bn == '\n' {
		return true, nil
	}

	if err := reader.UnreadByte(); err != nil {
		return false, err
	}

	return false, nil
}

func parseMultipleLine(reader *bufio.Reader, prompt MultilinePrompter) ([]string, error) {
	inputs := []string{}

	for {
		input, err := reader.ReadString('\n')
		input = strings.TrimRight(input, "\n")

		if err != nil {
			return []string{}, err
		}

		inputs = append(inputs, input)

		end, err := isMultilineEnd(reader)

		if err != nil {
			return []string{}, err
		}

		if end {
			break
		}
	}

	return inputs, prompt.Parse(inputs)
}

func parseLine(reader *bufio.Reader, prompt LinePrompter) (string, error) {
	input, err := reader.ReadString('\n')
	input = strings.TrimRight(input, "\n")

	if err != nil {
		return "", err
	}

	return input, prompt.Parse(input)
}

func renderPrompt(writer io.Writer, prompt Prompter) {
	switch pr := prompt.(type) {
	case PromptRenderer:
		pr.PrintPrompt(writer, prompt.PromptString())
	default:
		fmt.Fprintf(writer, "%s\n", prompt.PromptString())
	}
}

func renderError(writer io.Writer, prompt Prompter, err error) {
	switch pr := prompt.(type) {
	case ErrorRenderer:
		pr.PrintError(writer, err)
	default:
		fmt.Fprintf(writer, "%s\n", err.Error())
	}
}

func renderSeparator(writer io.Writer, prompt Prompter) {
	switch pr := prompt.(type) {
	case SeparatorRenderer:
		pr.PrintSeparator(writer)
	default:
		fmt.Fprintf(writer, "\n")
	}
}
<commit_msg>Update documentation<commit_after>// Package strumt provides a way to defines scenarios for prompting
// informations on command line
package strumt

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strings"
)

// Step represents a scenario step which is
// the result of a prompt execution. We store
// the prompt string displayed on the screen, inputs that the user has given,
// and the prompt error if one occurred
type Step struct {
	prompt string
	inputs []string
	err    error
}

// PromptString returns the prompt string displayed by the prompt on the screen
func (s Step) PromptString() string {
	return s.prompt
}

// Inputs retrieves all inputs submitted by the user
func (s Step) Inputs() []string {
	return s.inputs
}

// Error returns the error triggered by a prompt
func (s Step) Error() error {
	return s.err
}

// NewPrompts creates a new prompt from stdin and stdout
func NewPrompts() Prompts {
	return Prompts{reader: bufio.NewReader(os.Stdin), writer: os.Stdout, prompts: map[string]Prompter{}}
}

// NewPromptsFromReaderAndWriter creates a new prompt from a given reader and writer, useful for testing purpose
func NewPromptsFromReaderAndWriter(reader io.Reader, writer io.Writer) Prompts {
	return Prompts{reader: bufio.NewReader(reader), writer: writer, prompts: map[string]Prompter{}}
}

// Prompts is the main structure that handle all defined prompts
// and the logic to run and switch form one prompt to another.
// It keeps a record as well, of all user actions under a scenario
// entry
type Prompts struct {
	currentPrompt Prompter
	prompts       map[string]Prompter
	reader        *bufio.Reader
	writer        io.Writer
	scenario      []Step
}

func (p *Prompts) parse() ([]string, Prompter, error) {
	var nextPrompt Prompter
	var inputs []string
	var err error

	switch prompt := p.currentPrompt.(type) {
	case LinePrompter:
		var input string

		input, err = parseLine(p.reader, prompt)

		if prompt.NextOnSuccess(input) != "" {
			nextPrompt = p.prompts[prompt.NextOnSuccess(input)]
		}

		inputs = append(inputs, input)
	case MultilinePrompter:
		inputs, err = parseMultipleLine(p.reader, prompt)

		if prompt.NextOnSuccess(inputs) != "" {
			nextPrompt = p.prompts[prompt.NextOnSuccess(inputs)]
		}
	}

	if err != nil {
		nextPrompt = p.prompts[p.currentPrompt.NextOnError(err)]
	}

	return inputs, nextPrompt, err
}

func (p *Prompts) appendScenario(promptString string, inputs []string, err error) {
	p.scenario = append(
		p.scenario,
		Step{
			promptString,
			inputs,
			err,
		},
	)
}

// AddLinePrompter adds a new LinePrompter using the internal id as a reference
func (p *Prompts) AddLinePrompter(prompt LinePrompter) {
	p.prompts[prompt.ID()] = prompt
}

// AddMultilinePrompter adds a new MultilinePrompter using the internal id as a reference
func (p *Prompts) AddMultilinePrompter(prompt MultilinePrompter) {
	p.prompts[prompt.ID()] = prompt
}

// SetFirst defines from which prompt the prompt sequence has to start
func (p *Prompts) SetFirst(id string) {
	p.currentPrompt = p.prompts[id]
}

// Scenario retrieves all steps done during a prompt sequence
func (p *Prompts) Scenario() []Step {
	return p.scenario
}

// Run executes a prompt sequence
func (p *Prompts) Run() {
	p.scenario = []Step{}

	for {
		prompt := p.currentPrompt
		renderPrompt(p.writer, prompt)

		inputs, nextPrompt, err := p.parse()

		if err != nil {
			renderError(p.writer, prompt, err)
		}

		p.appendScenario(prompt.PromptString(), inputs, err)

		if nextPrompt == nil {
			return
		}

		renderSeparator(p.writer, prompt)

		p.currentPrompt = nextPrompt
	}
}

func isMultilineEnd(reader io.ByteScanner) (bool, error) {
	bn, err := reader.ReadByte()

	if err == io.EOF {
		return true, nil
	}

	if bn == '\n' {
		return true, nil
	}

	if err := reader.UnreadByte(); err != nil {
		return false, err
	}

	return false, nil
}

func parseMultipleLine(reader *bufio.Reader, prompt MultilinePrompter) ([]string, error) {
	inputs := []string{}

	for {
		input, err := reader.ReadString('\n')
		input = strings.TrimRight(input, "\n")

		if err != nil {
			return []string{}, err
		}

		inputs = append(inputs, input)

		end, err := isMultilineEnd(reader)

		if err != nil {
			return []string{}, err
		}

		if end {
			break
		}
	}

	return inputs, prompt.Parse(inputs)
}

func parseLine(reader *bufio.Reader, prompt LinePrompter) (string, error) {
	input, err := reader.ReadString('\n')
	input = strings.TrimRight(input, "\n")

	if err != nil {
		return "", err
	}

	return input, prompt.Parse(input)
}

func renderPrompt(writer io.Writer, prompt Prompter) {
	switch pr := prompt.(type) {
	case PromptRenderer:
		pr.PrintPrompt(writer, prompt.PromptString())
	default:
		fmt.Fprintf(writer, "%s\n", prompt.PromptString())
	}
}

func renderError(writer io.Writer, prompt Prompter, err error) {
	switch pr := prompt.(type) {
	case ErrorRenderer:
		pr.PrintError(writer, err)
	default:
		fmt.Fprintf(writer, "%s\n", err.Error())
	}
}

func renderSeparator(writer io.Writer, prompt Prompter) {
	switch pr := prompt.(type) {
	case SeparatorRenderer:
		pr.PrintSeparator(writer)
	default:
		fmt.Fprintf(writer, "\n")
	}
}
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 newtutil

import (
	"fmt"
	"io/ioutil"
	"strconv"
	"strings"

	"mynewt.apache.org/newt/newt/interfaces"
	"mynewt.apache.org/newt/newt/ycfg"
	"mynewt.apache.org/newt/util"
	"mynewt.apache.org/newt/yaml"
)

var NewtVersion Version = Version{1, 4, 9999}
var NewtVersionStr string = "Apache Newt version: 1.5.0-dev"
var NewtBlinkyTag string = "master"
var NewtNumJobs int
var NewtForce bool
var NewtAsk bool

const CORE_REPO_NAME string = "apache-mynewt-core"
const ARDUINO_ZERO_REPO_NAME string = "mynewt_arduino_zero"

type Version struct {
	Major    int64
	Minor    int64
	Revision int64
}

func ParseVersion(s string) (Version, error) {
	v := Version{}
	parseErr := util.FmtNewtError("Invalid version string: %s", s)

	parts := strings.Split(s, ".")
	if len(parts) != 3 {
		return v, parseErr
	}

	var err error

	v.Major, err = strconv.ParseInt(parts[0], 10, 64)
	if err != nil {
		return v, parseErr
	}

	v.Minor, err = strconv.ParseInt(parts[1], 10, 64)
	if err != nil {
		return v, parseErr
	}

	v.Revision, err = strconv.ParseInt(parts[2], 10, 64)
	if err != nil {
		return v, parseErr
	}

	return v, nil
}

func (v *Version) String() string {
	return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Revision)
}

func VerCmp(v1 Version, v2 Version) int64 {
	if r := v1.Major - v2.Major; r != 0 {
		return r
	}

	if r := v1.Minor - v2.Minor; r != 0 {
		return r
	}

	if r := v1.Revision - v2.Revision; r != 0 {
		return r
	}

	return 0
}

// Parses a string of the following form:
//     [@repo]<path/to/package>
//
// @return string               repo name ("" if no repo)
//         string               package name
//         error                if invalid package string
func ParsePackageString(pkgStr string) (string, string, error) {
	// remove possible trailing '/'
	pkgStr = strings.TrimSuffix(pkgStr, "/")

	if strings.HasPrefix(pkgStr, "@") {
		nameParts := strings.SplitN(pkgStr[1:], "/", 2)
		if len(nameParts) == 1 {
			return "", "", util.NewNewtError(fmt.Sprintf("Invalid package "+
				"string; contains repo but no package name: %s", pkgStr))
		} else {
			return nameParts[0], nameParts[1], nil
		}
	} else {
		return "", pkgStr, nil
	}
}

func FindRepoDesignator(s string) (int, int) {
	start := strings.Index(s, "@")
	if start == -1 {
		return -1, -1
	}

	len := strings.Index(s[start:], "/")
	if len == -1 {
		return -1, -1
	}

	return start, len
}

func ReplaceRepoDesignators(s string) (string, bool) {
	start, len := FindRepoDesignator(s)
	if start == -1 {
		return s, false
	}
	repoName := s[start+1 : start+len]

	proj := interfaces.GetProject()
	repoPath := proj.FindRepoPath(repoName)
	if repoPath == "" {
		return s, false
	}

	// Trim common project base from repo path.
	relRepoPath := strings.TrimPrefix(repoPath, proj.Path()+"/")

	return s[:start] + relRepoPath + s[start+len:], true
}

func BuildPackageString(repoName string, pkgName string) string {
	if repoName != "" {
		return "@" + repoName + "/" + pkgName
	} else {
		return pkgName
	}
}

func GeneratedPreamble() string {
	return fmt.Sprintf("/**\n * This file was generated by %s\n */\n\n",
		NewtVersionStr)
}

// Creates a temporary directory for downloading a repo.
func MakeTempRepoDir() (string, error) {
	tmpdir, err := ioutil.TempDir("", "newt-repo")
	if err != nil {
		return "", util.ChildNewtError(err)
	}

	return tmpdir, nil
}

func ReadConfigPath(path string) (ycfg.YCfg, error) {
	file, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, util.NewNewtError(fmt.Sprintf("Error reading %s: %s",
			path, err.Error()))
	}

	settings := map[string]interface{}{}
	if err := yaml.Unmarshal(file, &settings); err != nil {
		return nil, util.FmtNewtError("Failure parsing \"%s\": %s",
			path, err.Error())
	}

	return ycfg.NewYCfg(settings)
}

// Read in the configuration file specified by name, in path
// return a new viper config object if successful, and error if not
func ReadConfig(dir string, filename string) (ycfg.YCfg, error) {
	return ReadConfigPath(dir + "/" + filename + ".yml")
}

// Converts the provided YAML-configuration map to a YAML-encoded string.
func YCfgToYaml(yc ycfg.YCfg) string {
	return yaml.MapToYaml(yc.AllSettings())
}

// Keeps track of warnings that have already been reported.
// [warning-text] => struct{}
var warnings = map[string]struct{}{}

// Displays the specified warning if it has not been displayed yet.
func OneTimeWarning(text string, args ...interface{}) {
	if _, ok := warnings[text]; !ok {
		warnings[text] = struct{}{}

		body := fmt.Sprintf(text, args...)
		util.StatusMessage(util.VERBOSITY_QUIET, "WARNING: %s\n", body)
	}
}
<commit_msg>Update newt version: 1.4.9999 --> 1.5.9900<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 newtutil

import (
	"fmt"
	"io/ioutil"
	"strconv"
	"strings"

	"mynewt.apache.org/newt/newt/interfaces"
	"mynewt.apache.org/newt/newt/ycfg"
	"mynewt.apache.org/newt/util"
	"mynewt.apache.org/newt/yaml"
)

var NewtVersion Version = Version{1, 5, 9900}
var NewtVersionStr string = "Apache Newt version: 1.5.0-dev"
var NewtBlinkyTag string = "master"
var NewtNumJobs int
var NewtForce bool
var NewtAsk bool

const CORE_REPO_NAME string = "apache-mynewt-core"
const ARDUINO_ZERO_REPO_NAME string = "mynewt_arduino_zero"

type Version struct {
	Major    int64
	Minor    int64
	Revision int64
}

func ParseVersion(s string) (Version, error) {
	v := Version{}
	parseErr := util.FmtNewtError("Invalid version string: %s", s)

	parts := strings.Split(s, ".")
	if len(parts) != 3 {
		return v, parseErr
	}

	var err error

	v.Major, err = strconv.ParseInt(parts[0], 10, 64)
	if err != nil {
		return v, parseErr
	}

	v.Minor, err = strconv.ParseInt(parts[1], 10, 64)
	if err != nil {
		return v, parseErr
	}

	v.Revision, err = strconv.ParseInt(parts[2], 10, 64)
	if err != nil {
		return v, parseErr
	}

	return v, nil
}

func (v *Version) String() string {
	return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Revision)
}

func VerCmp(v1 Version, v2 Version) int64 {
	if r := v1.Major - v2.Major; r != 0 {
		return r
	}

	if r := v1.Minor - v2.Minor; r != 0 {
		return r
	}

	if r := v1.Revision - v2.Revision; r != 0 {
		return r
	}

	return 0
}

// Parses a string of the following form:
//     [@repo]<path/to/package>
//
// @return string               repo name ("" if no repo)
//         string               package name
//         error                if invalid package string
func ParsePackageString(pkgStr string) (string, string, error) {
	// remove possible trailing '/'
	pkgStr = strings.TrimSuffix(pkgStr, "/")

	if strings.HasPrefix(pkgStr, "@") {
		nameParts := strings.SplitN(pkgStr[1:], "/", 2)
		if len(nameParts) == 1 {
			return "", "", util.NewNewtError(fmt.Sprintf("Invalid package "+
				"string; contains repo but no package name: %s", pkgStr))
		} else {
			return nameParts[0], nameParts[1], nil
		}
	} else {
		return "", pkgStr, nil
	}
}

func FindRepoDesignator(s string) (int, int) {
	start := strings.Index(s, "@")
	if start == -1 {
		return -1, -1
	}

	len := strings.Index(s[start:], "/")
	if len == -1 {
		return -1, -1
	}

	return start, len
}

func ReplaceRepoDesignators(s string) (string, bool) {
	start, len := FindRepoDesignator(s)
	if start == -1 {
		return s, false
	}
	repoName := s[start+1 : start+len]

	proj := interfaces.GetProject()
	repoPath := proj.FindRepoPath(repoName)
	if repoPath == "" {
		return s, false
	}

	// Trim common project base from repo path.
	relRepoPath := strings.TrimPrefix(repoPath, proj.Path()+"/")

	return s[:start] + relRepoPath + s[start+len:], true
}

func BuildPackageString(repoName string, pkgName string) string {
	if repoName != "" {
		return "@" + repoName + "/" + pkgName
	} else {
		return pkgName
	}
}

func GeneratedPreamble() string {
	return fmt.Sprintf("/**\n * This file was generated by %s\n */\n\n",
		NewtVersionStr)
}

// Creates a temporary directory for downloading a repo.
func MakeTempRepoDir() (string, error) {
	tmpdir, err := ioutil.TempDir("", "newt-repo")
	if err != nil {
		return "", util.ChildNewtError(err)
	}

	return tmpdir, nil
}

func ReadConfigPath(path string) (ycfg.YCfg, error) {
	file, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, util.NewNewtError(fmt.Sprintf("Error reading %s: %s",
			path, err.Error()))
	}

	settings := map[string]interface{}{}
	if err := yaml.Unmarshal(file, &settings); err != nil {
		return nil, util.FmtNewtError("Failure parsing \"%s\": %s",
			path, err.Error())
	}

	return ycfg.NewYCfg(settings)
}

// Read in the configuration file specified by name, in path
// return a new viper config object if successful, and error if not
func ReadConfig(dir string, filename string) (ycfg.YCfg, error) {
	return ReadConfigPath(dir + "/" + filename + ".yml")
}

// Converts the provided YAML-configuration map to a YAML-encoded string.
func YCfgToYaml(yc ycfg.YCfg) string {
	return yaml.MapToYaml(yc.AllSettings())
}

// Keeps track of warnings that have already been reported.
// [warning-text] => struct{}
var warnings = map[string]struct{}{}

// Displays the specified warning if it has not been displayed yet.
func OneTimeWarning(text string, args ...interface{}) {
	if _, ok := warnings[text]; !ok {
		warnings[text] = struct{}{}

		body := fmt.Sprintf(text, args...)
		util.StatusMessage(util.VERBOSITY_QUIET, "WARNING: %s\n", body)
	}
}
<|endoftext|>