<commit_before>package server

import (
	"sync/atomic"
	"time"

	"github.com/go-kit/kit/log"
	"github.com/pkg/errors"

	"github.com/weaveworks/flux"
	"github.com/weaveworks/flux/history"
	"github.com/weaveworks/flux/instance"
	"github.com/weaveworks/flux/job"
	"github.com/weaveworks/flux/notifications"
	"github.com/weaveworks/flux/policy"
	"github.com/weaveworks/flux/remote"
	"github.com/weaveworks/flux/ssh"
	"github.com/weaveworks/flux/update"
)

const (
	serviceAutomated   = "Automation enabled."
	serviceDeautomated = "Automation disabled."

	serviceLocked   = "Service locked."
	serviceUnlocked = "Service unlocked."
)

type Server struct {
	version     string
	instancer   instance.Instancer
	config      instance.DB
	messageBus  remote.MessageBus
	logger      log.Logger
	maxPlatform chan struct{} // semaphore for concurrent calls to the platform
	connected   int32
}

func New(
	version string,
	instancer instance.Instancer,
	config instance.DB,
	messageBus remote.MessageBus,
	logger log.Logger,
) *Server {
	connectedDaemons.Set(0)
	return &Server{
		version:     version,
		instancer:   instancer,
		config:      config,
		messageBus:  messageBus,
		logger:      logger,
		maxPlatform: make(chan struct{}, 8),
	}
}

func (s *Server) Status(instID flux.InstanceID) (res flux.Status, err error) {
	inst, err := s.instancer.Get(instID)
	if err != nil {
		return res, errors.Wrapf(err, "getting instance")
	}

	res.Fluxsvc = flux.FluxsvcStatus{Version: s.version}
	res.Fluxd.Version, err = inst.Platform.Version()
	res.Fluxd.Connected = (err == nil)
	_, err = inst.Platform.SyncStatus("HEAD")
	if err != nil {
		res.Git.Error = err.Error()
	} else {
		res.Git.Configured = true
	}

	return res, nil
}

func (s *Server) ListServices(instID flux.InstanceID, namespace string) (res []flux.ServiceStatus, err error) {
	inst, err := s.instancer.Get(instID)
	if err != nil {
		return nil, errors.Wrapf(err, "getting instance")
	}

	services, err := inst.Platform.ListServices(namespace)
	if err != nil {
		return nil, errors.Wrap(err, "getting services from platform")
	}
	return services, nil
}

func (s *Server) ListImages(instID flux.InstanceID, spec update.ServiceSpec) (res []flux.ImageStatus, err error) {
	inst, err := s.instancer.Get(instID)
	if err != nil {
		return nil, errors.Wrapf(err, "getting instance "+string(instID))
	}
	return inst.Platform.ListImages(spec)
}

func (s *Server) UpdateImages(instID flux.InstanceID, spec update.ReleaseSpec, cause update.Cause) (job.ID, error) {
	inst, err := s.instancer.Get(instID)
	if err != nil {
		return "", errors.Wrapf(err, "getting instance "+string(instID))
	}
	return inst.Platform.UpdateManifests(update.Spec{Type: update.Images, Cause: cause, Spec: spec})
}

func (s *Server) UpdatePolicies(instID flux.InstanceID, updates policy.Updates, cause update.Cause) (job.ID, error) {
	inst, err := s.instancer.Get(instID)
	if err != nil {
		return "", errors.Wrapf(err, "getting instance "+string(instID))
	}

	return inst.Platform.UpdateManifests(update.Spec{Type: update.Policy, Cause: cause, Spec: updates})
}

func (s *Server) SyncNotify(instID flux.InstanceID) (err error) {
	inst, err := s.instancer.Get(instID)
	if err != nil {
		return errors.Wrapf(err, "getting instance "+string(instID))
	}
	return inst.Platform.SyncNotify()
}

func (s *Server) JobStatus(instID flux.InstanceID, jobID job.ID) (res job.Status, err error) {
	inst, err := s.instancer.Get(instID)
	if err != nil {
		return job.Status{}, errors.Wrapf(err, "getting instance "+string(instID))
	}

	return inst.Platform.JobStatus(jobID)
}

func (s *Server) SyncStatus(instID flux.InstanceID, ref string) (res []string, err error) {
	inst, err := s.instancer.Get(instID)
	if err != nil {
		return nil, errors.Wrapf(err, "getting instance "+string(instID))
	}

	return inst.Platform.SyncStatus(ref)
}

// LogEvent receives events from fluxd and pushes events to the history
// db and a slack notification
func (s *Server) LogEvent(instID flux.InstanceID, e history.Event) error {
	helper, err := s.instancer.Get(instID)
	if err != nil {
		return errors.Wrapf(err, "getting instance")
	}

	// Log event in history first. This is less likely to fail
	err = helper.LogEvent(e)
	if err != nil {
		return errors.Wrapf(err, "logging event")
	}

	// If this is a release
	if e.Type == history.EventRelease {
		cfg, err := helper.Config.Get()
		if err != nil {
			return errors.Wrapf(err, "getting config")
		}
		releaseMeta := e.Metadata.(*history.ReleaseEventMetadata)
		err = notifications.Release(cfg, releaseMeta, releaseMeta.Error)
		if err != nil {
			return errors.Wrapf(err, "notifying slack")
		}
	}
	return nil
}

func (s *Server) History(inst flux.InstanceID, spec update.ServiceSpec, before time.Time, limit int64) (res []history.Entry, err error) {
	helper, err := s.instancer.Get(inst)
	if err != nil {
		return nil, errors.Wrapf(err, "getting instance")
	}

	var events []history.Event
	if spec == update.ServiceSpecAll {
		events, err = helper.AllEvents(before, limit)
		if err != nil {
			return nil, errors.Wrap(err, "fetching all history events")
		}
	} else {
		id, err := flux.ParseServiceID(string(spec))
		if err != nil {
			return nil, errors.Wrapf(err, "parsing service ID from spec %s", spec)
		}

		events, err = helper.EventsForService(id, before, limit)
		if err != nil {
			return nil, errors.Wrapf(err, "fetching history events for %s", id)
		}
	}

	res = make([]history.Entry, len(events))
	for i, event := range events {
		res[i] = history.Entry{
			Stamp: &events[i].StartedAt,
			Type:  "v0",
			Data:  event.String(),
			Event: &events[i],
		}
	}

	return res, nil
}

func (s *Server) GetConfig(instID flux.InstanceID, fingerprint string) (flux.InstanceConfig, error) {
	fullConfig, err := s.config.GetConfig(instID)
	if err != nil {
		return flux.InstanceConfig{}, err
	}

	config := flux.InstanceConfig(fullConfig.Settings)

	return config, nil
}

func (s *Server) SetConfig(instID flux.InstanceID, updates flux.UnsafeInstanceConfig) error {
	return s.config.UpdateConfig(instID, applyConfigUpdates(updates))
}

func (s *Server) PatchConfig(instID flux.InstanceID, patch flux.ConfigPatch) error {
	fullConfig, err := s.config.GetConfig(instID)
	if err != nil {
		return errors.Wrap(err, "unable to get config")
	}

	patchedConfig, err := fullConfig.Settings.Patch(patch)
	if err != nil {
		return errors.Wrap(err, "unable to apply patch")
	}

	return s.config.UpdateConfig(instID, applyConfigUpdates(patchedConfig))
}

func applyConfigUpdates(updates flux.UnsafeInstanceConfig) instance.UpdateFunc {
	return func(config instance.Config) (instance.Config, error) {
		config.Settings = updates
		return config, nil
	}
}

func (s *Server) PublicSSHKey(instID flux.InstanceID, regenerate bool) (ssh.PublicKey, error) {
	inst, err := s.instancer.Get(instID)
	if err != nil {
		return ssh.PublicKey{}, errors.Wrapf(err, "getting instance "+string(instID))
	}

	return inst.Platform.PublicSSHKey(regenerate)
}

// RegisterDaemon handles a daemon connection. It blocks until the
// daemon is disconnected.
//
// There are two conditions where we need to close and cleanup: either
// the server has initiated a close (due to another client showing up,
// say) or the client has disconnected.
//
// If the server has initiated a close, we should close the other
// client's respective blocking goroutine.
//
// If the client has disconnected, there is no way to detect this in
// go, aside from just trying to connection. Therefore, the server
// will get an error when we try to use the client. We rely on that to
// break us out of this method.
func (s *Server) RegisterDaemon(instID flux.InstanceID, platform remote.Platform) (err error) {
	defer func() {
		if err != nil {
			s.logger.Log("method", "RegisterDaemon", "err", err)
		}
		connectedDaemons.Set(float64(atomic.AddInt32(&s.connected, -1)))
	}()
	connectedDaemons.Set(float64(atomic.AddInt32(&s.connected, 1)))

	// Register the daemon with our message bus, waiting for it to be
	// closed. NB we cannot in general expect there to be a
	// configuration record for this instance; it may be connecting
	// before there is configuration supplied.
	done := make(chan error)
	s.messageBus.Subscribe(instID, s.instrumentPlatform(instID, platform), done)
	err = <-done
	return err
}

func (s *Server) Export(instID flux.InstanceID) (res []byte, err error) {
	inst, err := s.instancer.Get(instID)
	if err != nil {
		return res, errors.Wrapf(err, "getting instance")
	}

	res, err = inst.Platform.Export()
	if err != nil {
		return res, errors.Wrapf(err, "exporting %s", instID)
	}

	return res, nil
}

func (s *Server) instrumentPlatform(instID flux.InstanceID, p remote.Platform) remote.Platform {
	return &remote.ErrorLoggingPlatform{
		remote.Instrument(p),
		log.NewContext(s.logger).With("instanceID", instID),
	}
}

func (s *Server) IsDaemonConnected(instID flux.InstanceID) error {
	return s.messageBus.Ping(instID)
}
<commit_msg>Remove unused consts<commit_after>package server

import (
	"sync/atomic"
	"time"

	"github.com/go-kit/kit/log"
	"github.com/pkg/errors"

	"github.com/weaveworks/flux"
	"github.com/weaveworks/flux/history"
	"github.com/weaveworks/flux/instance"
	"github.com/weaveworks/flux/job"
	"github.com/weaveworks/flux/notifications"
	"github.com/weaveworks/flux/policy"
	"github.com/weaveworks/flux/remote"
	"github.com/weaveworks/flux/ssh"
	"github.com/weaveworks/flux/update"
)

type Server struct {
	version     string
	instancer   instance.Instancer
	config      instance.DB
	messageBus  remote.MessageBus
	logger      log.Logger
	maxPlatform chan struct{} // semaphore for concurrent calls to the platform
	connected   int32
}

func New(
	version string,
	instancer instance.Instancer,
	config instance.DB,
	messageBus remote.MessageBus,
	logger log.Logger,
) *Server {
	connectedDaemons.Set(0)
	return &Server{
		version:     version,
		instancer:   instancer,
		config:      config,
		messageBus:  messageBus,
		logger:      logger,
		maxPlatform: make(chan struct{}, 8),
	}
}

func (s *Server) Status(instID flux.InstanceID) (res flux.Status, err error) {
	inst, err := s.instancer.Get(instID)
	if err != nil {
		return res, errors.Wrapf(err, "getting instance")
	}

	res.Fluxsvc = flux.FluxsvcStatus{Version: s.version}
	res.Fluxd.Version, err = inst.Platform.Version()
	res.Fluxd.Connected = (err == nil)
	_, err = inst.Platform.SyncStatus("HEAD")
	if err != nil {
		res.Git.Error = err.Error()
	} else {
		res.Git.Configured = true
	}

	return res, nil
}

func (s *Server) ListServices(instID flux.InstanceID, namespace string) (res []flux.ServiceStatus, err error) {
	inst, err := s.instancer.Get(instID)
	if err != nil {
		return nil, errors.Wrapf(err, "getting instance")
	}

	services, err := inst.Platform.ListServices(namespace)
	if err != nil {
		return nil, errors.Wrap(err, "getting services from platform")
	}
	return services, nil
}

func (s *Server) ListImages(instID flux.InstanceID, spec update.ServiceSpec) (res []flux.ImageStatus, err error) {
	inst, err := s.instancer.Get(instID)
	if err != nil {
		return nil, errors.Wrapf(err, "getting instance "+string(instID))
	}
	return inst.Platform.ListImages(spec)
}

func (s *Server) UpdateImages(instID flux.InstanceID, spec update.ReleaseSpec, cause update.Cause) (job.ID, error) {
	inst, err := s.instancer.Get(instID)
	if err != nil {
		return "", errors.Wrapf(err, "getting instance "+string(instID))
	}
	return inst.Platform.UpdateManifests(update.Spec{Type: update.Images, Cause: cause, Spec: spec})
}

func (s *Server) UpdatePolicies(instID flux.InstanceID, updates policy.Updates, cause update.Cause) (job.ID, error) {
	inst, err := s.instancer.Get(instID)
	if err != nil {
		return "", errors.Wrapf(err, "getting instance "+string(instID))
	}

	return inst.Platform.UpdateManifests(update.Spec{Type: update.Policy, Cause: cause, Spec: updates})
}

func (s *Server) SyncNotify(instID flux.InstanceID) (err error) {
	inst, err := s.instancer.Get(instID)
	if err != nil {
		return errors.Wrapf(err, "getting instance "+string(instID))
	}
	return inst.Platform.SyncNotify()
}

func (s *Server) JobStatus(instID flux.InstanceID, jobID job.ID) (res job.Status, err error) {
	inst, err := s.instancer.Get(instID)
	if err != nil {
		return job.Status{}, errors.Wrapf(err, "getting instance "+string(instID))
	}

	return inst.Platform.JobStatus(jobID)
}

func (s *Server) SyncStatus(instID flux.InstanceID, ref string) (res []string, err error) {
	inst, err := s.instancer.Get(instID)
	if err != nil {
		return nil, errors.Wrapf(err, "getting instance "+string(instID))
	}

	return inst.Platform.SyncStatus(ref)
}

// LogEvent receives events from fluxd and pushes events to the history
// db and a slack notification
func (s *Server) LogEvent(instID flux.InstanceID, e history.Event) error {
	helper, err := s.instancer.Get(instID)
	if err != nil {
		return errors.Wrapf(err, "getting instance")
	}

	// Log event in history first. This is less likely to fail
	err = helper.LogEvent(e)
	if err != nil {
		return errors.Wrapf(err, "logging event")
	}

	// If this is a release
	if e.Type == history.EventRelease {
		cfg, err := helper.Config.Get()
		if err != nil {
			return errors.Wrapf(err, "getting config")
		}
		releaseMeta := e.Metadata.(*history.ReleaseEventMetadata)
		err = notifications.Release(cfg, releaseMeta, releaseMeta.Error)
		if err != nil {
			return errors.Wrapf(err, "notifying slack")
		}
	}
	return nil
}

func (s *Server) History(inst flux.InstanceID, spec update.ServiceSpec, before time.Time, limit int64) (res []history.Entry, err error) {
	helper, err := s.instancer.Get(inst)
	if err != nil {
		return nil, errors.Wrapf(err, "getting instance")
	}

	var events []history.Event
	if spec == update.ServiceSpecAll {
		events, err = helper.AllEvents(before, limit)
		if err != nil {
			return nil, errors.Wrap(err, "fetching all history events")
		}
	} else {
		id, err := flux.ParseServiceID(string(spec))
		if err != nil {
			return nil, errors.Wrapf(err, "parsing service ID from spec %s", spec)
		}

		events, err = helper.EventsForService(id, before, limit)
		if err != nil {
			return nil, errors.Wrapf(err, "fetching history events for %s", id)
		}
	}

	res = make([]history.Entry, len(events))
	for i, event := range events {
		res[i] = history.Entry{
			Stamp: &events[i].StartedAt,
			Type:  "v0",
			Data:  event.String(),
			Event: &events[i],
		}
	}

	return res, nil
}

func (s *Server) GetConfig(instID flux.InstanceID, fingerprint string) (flux.InstanceConfig, error) {
	fullConfig, err := s.config.GetConfig(instID)
	if err != nil {
		return flux.InstanceConfig{}, err
	}

	config := flux.InstanceConfig(fullConfig.Settings)

	return config, nil
}

func (s *Server) SetConfig(instID flux.InstanceID, updates flux.UnsafeInstanceConfig) error {
	return s.config.UpdateConfig(instID, applyConfigUpdates(updates))
}

func (s *Server) PatchConfig(instID flux.InstanceID, patch flux.ConfigPatch) error {
	fullConfig, err := s.config.GetConfig(instID)
	if err != nil {
		return errors.Wrap(err, "unable to get config")
	}

	patchedConfig, err := fullConfig.Settings.Patch(patch)
	if err != nil {
		return errors.Wrap(err, "unable to apply patch")
	}

	return s.config.UpdateConfig(instID, applyConfigUpdates(patchedConfig))
}

func applyConfigUpdates(updates flux.UnsafeInstanceConfig) instance.UpdateFunc {
	return func(config instance.Config) (instance.Config, error) {
		config.Settings = updates
		return config, nil
	}
}

func (s *Server) PublicSSHKey(instID flux.InstanceID, regenerate bool) (ssh.PublicKey, error) {
	inst, err := s.instancer.Get(instID)
	if err != nil {
		return ssh.PublicKey{}, errors.Wrapf(err, "getting instance "+string(instID))
	}

	return inst.Platform.PublicSSHKey(regenerate)
}

// RegisterDaemon handles a daemon connection. It blocks until the
// daemon is disconnected.
//
// There are two conditions where we need to close and cleanup: either
// the server has initiated a close (due to another client showing up,
// say) or the client has disconnected.
//
// If the server has initiated a close, we should close the other
// client's respective blocking goroutine.
//
// If the client has disconnected, there is no way to detect this in
// go, aside from just trying to connection. Therefore, the server
// will get an error when we try to use the client. We rely on that to
// break us out of this method.
func (s *Server) RegisterDaemon(instID flux.InstanceID, platform remote.Platform) (err error) {
	defer func() {
		if err != nil {
			s.logger.Log("method", "RegisterDaemon", "err", err)
		}
		connectedDaemons.Set(float64(atomic.AddInt32(&s.connected, -1)))
	}()
	connectedDaemons.Set(float64(atomic.AddInt32(&s.connected, 1)))

	// Register the daemon with our message bus, waiting for it to be
	// closed. NB we cannot in general expect there to be a
	// configuration record for this instance; it may be connecting
	// before there is configuration supplied.
	done := make(chan error)
	s.messageBus.Subscribe(instID, s.instrumentPlatform(instID, platform), done)
	err = <-done
	return err
}

func (s *Server) Export(instID flux.InstanceID) (res []byte, err error) {
	inst, err := s.instancer.Get(instID)
	if err != nil {
		return res, errors.Wrapf(err, "getting instance")
	}

	res, err = inst.Platform.Export()
	if err != nil {
		return res, errors.Wrapf(err, "exporting %s", instID)
	}

	return res, nil
}

func (s *Server) instrumentPlatform(instID flux.InstanceID, p remote.Platform) remote.Platform {
	return &remote.ErrorLoggingPlatform{
		remote.Instrument(p),
		log.NewContext(s.logger).With("instanceID", instID),
	}
}

func (s *Server) IsDaemonConnected(instID flux.InstanceID) error {
	return s.messageBus.Ping(instID)
}
<|endoftext|>


<commit_before>package server

import (
	"net"
	"os"
	"os/signal"
	"path"
	"path/filepath"
	"syscall"
)

func (s *Server) Bootstrap(basepath string) error {
	var err error
	s.basepath = basepath
	s.modpath = path.Join(basepath, "modules")
	s.socketpath = path.Join(basepath, "server.sock")
	err = s.FindModules()
	if err != nil {
		return err
	}
	err = s.OpenSocket()
	if err != nil {
		return err
	}
	return nil
}

func (s *Server) FindModules() error {
	var err error
	s.Modules, err = filepath.Glob(path.Join(s.modpath, "*"))
	if err != nil {
		return err
	}
	return nil
}

func (s *Server) OpenSocket() error {
	s.SocketAddress = new(net.UnixAddr)
	s.SocketAddress.Name = s.socketpath
	s.SocketAddress.Net = "unix"
	socket, err := net.ListenUnix("unix", s.SocketAddress)
	if err != nil {
		return err
	}
	s.Socket = socket
	return nil
}

func (s *Server) CloseSocket() error {
	if s.Socket != nil {
		return s.Socket.Close()
	}
	return nil
}

func (s *Server) Run() error {
	var signalIn os.Signal
	var endSignal chan os.Signal
	endSignal = make(chan os.Signal)
	signal.Notify(endSignal, syscall.SIGINT, syscall.SIGQUIT)
	for {
		select {
		case signalIn = <-endSignal:
			println(signalIn.String())
			s.Shutdown()
			return nil
		}
	}
	return nil
}

func (s *Server) Shutdown() {
	s.CloseSocket()
	println("closing")
}
<commit_msg>better signal handling<commit_after>package server

import (
	"net"
	"os"
	"os/signal"
	"path"
	"path/filepath"
	"syscall"
)

func (s *Server) Bootstrap(basepath string) error {
	var err error
	s.basepath = basepath
	s.modpath = path.Join(basepath, "modules")
	s.socketpath = path.Join(basepath, "server.sock")
	err = s.FindModules()
	if err != nil {
		return err
	}
	err = s.OpenSocket()
	if err != nil {
		return err
	}
	return nil
}

func (s *Server) FindModules() error {
	var err error
	s.Modules, err = filepath.Glob(path.Join(s.modpath, "*"))
	if err != nil {
		return err
	}
	return nil
}

func (s *Server) OpenSocket() error {
	s.SocketAddress = new(net.UnixAddr)
	s.SocketAddress.Name = s.socketpath
	s.SocketAddress.Net = "unix"
	socket, err := net.ListenUnix("unix", s.SocketAddress)
	if err != nil {
		return err
	}
	s.Socket = socket
	return nil
}

func (s *Server) CloseSocket() error {
	if s.Socket != nil {
		return s.Socket.Close()
	}
	return nil
}

func (s *Server) Run() error {
	endSignal := make(chan os.Signal)
	signal.Notify(endSignal, syscall.SIGINT, syscall.SIGQUIT)
	for {
		select {
		case <-endSignal:
			s.Shutdown()
			return nil
		}
	}
	return nil
}

func (s *Server) Shutdown() {
	s.CloseSocket()
	println("closing")
}
<|endoftext|>


<commit_before>package server

import (
    "github.com/kataras/iris"
    "github.com/BluePecker/JwtAuth/server/router"
    "fmt"
    "github.com/Sirupsen/logrus"
    //"github.com/kataras/iris/middleware/logger"
)

type TLS struct {
    Cert string
    Key  string
}

type Options struct {
    Host string
    Port int
    Tls  *TLS
}

type Server struct {
    app *iris.Application
}

func (s *Server) initHttpApp() {
    if s.app == nil {
        s.app = iris.New()

        s.app.Logger() = logrus.StandardLogger()
        //customLogger := logger.New(logger.Config{
        //    // Status displays status code
        //    Status: true,
        //    // IP displays request's remote address
        //    IP: true,
        //    // Method displays the http method
        //    Method: true,
        //    // Path displays the request path
        //    Path: true,
        //})
        //s.app.Use(customLogger)
    }
}

func (s *Server) Accept(options Options) {
    s.initHttpApp()

    if options.Tls != nil {
        //是否将80端口的请求转发到443
        //target, _ := url.Parse("https://127.0.0.1:443")
        //go host.NewProxy("127.0.0.1:80", target).ListenAndServe()
        var addr string = fmt.Sprintf("%s:%d", options.Host, options.Port)
        err := s.app.Run(iris.TLS(addr, options.Tls.Cert, options.Tls.Key))
        if err != nil {
            logrus.Error(err)
        }

    } else {
        var addr string = fmt.Sprintf("%s:%d", options.Host, options.Port)
        err := s.app.Run(iris.Addr(addr))
        if err != nil {
            logrus.Error(err)
        }
    }
}

func (s *Server) AddRouter(routers... router.Router) {
    s.initHttpApp()

    for _, item := range routers {
        item.Routes(s.app)
    }
}<commit_msg>debug<commit_after>package server

import (
    "github.com/kataras/iris"
    "github.com/BluePecker/JwtAuth/server/router"
    "fmt"
    "github.com/Sirupsen/logrus"
    "github.com/kataras/iris/middleware/logger"
)

type TLS struct {
    Cert string
    Key  string
}

type Options struct {
    Host string
    Port int
    Tls  *TLS
}

type Server struct {
    app *iris.Application
}

func (s *Server) initHttpApp() {
    if s.app == nil {
        s.app = iris.New()

        s.app.Logger() = logrus.StandardLogger()
        customLogger := logger.New(logger.Config{
            // Status displays status code
            Status: true,
            // IP displays request's remote address
            IP: true,
            // Method displays the http method
            Method: true,
            // Path displays the request path
            Path: true,
        })
        s.app.Use(customLogger)
    }
}

func (s *Server) Accept(options Options) {
    s.initHttpApp()

    if options.Tls != nil {
        //是否将80端口的请求转发到443
        //target, _ := url.Parse("https://127.0.0.1:443")
        //go host.NewProxy("127.0.0.1:80", target).ListenAndServe()
        var addr string = fmt.Sprintf("%s:%d", options.Host, options.Port)
        err := s.app.Run(iris.TLS(addr, options.Tls.Cert, options.Tls.Key))
        if err != nil {
            logrus.Error(err)
        }

    } else {
        var addr string = fmt.Sprintf("%s:%d", options.Host, options.Port)
        err := s.app.Run(iris.Addr(addr))
        if err != nil {
            logrus.Error(err)
        }
    }
}

func (s *Server) AddRouter(routers... router.Router) {
    s.initHttpApp()

    for _, item := range routers {
        item.Routes(s.app)
    }
}<|endoftext|>


<commit_before>package endly

import (
	"fmt"
	"github.com/pkg/errors"
	"github.com/viant/endly/common"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/storage"
	"net/url"
)

const BuildServiceId = "build"

type OperatingSystemDeployment struct {
	OsTarget *OperatingSystemTarget
	Config   *DeploymentConfig
}

type BuildGoal struct {
	Name                string
	Command             *ManagedCommand
	Transfers           *TransfersRequest
	VerificationCommand *ManagedCommand
}

type BuildMeta struct {
	Sdk              string
	SdkVersion       string
	Name             string
	Goals            []*BuildGoal
	goalsIndex       map[string]*BuildGoal
	BuildDeployments []*OperatingSystemDeployment //defines deployment of the build app itself, i.e how to get maven installed
}

func (m *BuildMeta) Validate() error {
	if m.Name == "" {
		return fmt.Errorf("MetaBuild.Names %v", m.Name)

	}
	if len(m.Goals) == 0 {
		return fmt.Errorf("MetaBuild.Goals were empty %v", m.Name)
	}
	return nil
}

func (m *BuildMeta) Match(operatingSystem *OperatingSystem, version string) *OperatingSystemDeployment {
	for _, candidate := range m.BuildDeployments {
		osTarget := candidate.OsTarget
		if version != "" {
			if candidate.Config.Transfer.Target.Version != version {
				continue
			}
		}
		if operatingSystem.Matches(osTarget) {
			return candidate
		}
	}
	return nil
}

type BuildSpec struct {
	Name       string //build name  like go, mvn, node, yarn
	Version    string
	Goal       string //actual build target, like clean, test
	Args       string // additional build arguments , that can be expanded with $build.args
	Sdk        string
	SdkVersion string
}

type BuildRequest struct {
	BuildSpec *BuildSpec //build specification
	Target    *Resource  //path to application to be build, Note that command may use $build.target variable. that expands to Target URL path
}

type BuildResponse struct {
	SdkResponse *SdkSetResponse
	CommandInfo *CommandInfo
}

type BuildRegisterMetaRequest struct {
	Meta *BuildMeta
}

type BuildLoadMetaRequest struct {
	Resource *Resource
}

type BuildLoadMetaResponse struct {
	Loaded map[string]*BuildMeta //url to size
}

type BuildService struct {
	*AbstractService
	registry BuildMetaRegistry
}

func (s *BuildService) build(context *Context, request *BuildRequest) (interface{}, error) {
	var result = &BuildResponse{}
	state := context.State()
	target, err := context.ExpandResource(request.Target)
	if err != nil {
		return nil, err
	}
	buildSpec := request.BuildSpec

	if buildSpec == nil {
		return nil, fmt.Errorf("BuildSpec was empty")
	}
	buildMeta, has := s.registry[buildSpec.Name]
	if !has {
		return nil, fmt.Errorf("Failed to lookup build: %v", buildSpec.Name)
	}

	goal, has := buildMeta.goalsIndex[buildSpec.Goal]
	if !has {
		return nil, fmt.Errorf("Failed to lookup build %v goal: %v", buildSpec.Name, buildSpec.Goal)
	}

	buildState, err := newBuildState(buildSpec, target.ParsedURL, request, context)
	if err != nil {
		return nil, err
	}
	if buildMeta.Sdk != "" {
		state.Put("build", buildState)
		sdkService, err := context.Service(SdkServiceId)
		if err != nil {
			return nil, err
		}
		serviceResponse := sdkService.Run(context, &SdkSetRequest{Target: request.Target,
			Sdk:     context.Expand(buildMeta.Sdk),
			Version: context.Expand(buildMeta.SdkVersion),
		})
		if serviceResponse.Error != "" {
			return nil, errors.New(serviceResponse.Error)
		}
		result.SdkResponse, _ = serviceResponse.Response.(*SdkSetResponse)
	}

	execService, err := context.Service(ExecServiceId)
	if err != nil {
		return nil, err
	}
	state.Put("build", buildState)
	response := execService.Run(context, &OpenSession{
		Target: target,
	})

	if response.Error != "" {
		return nil, errors.New(response.Error)
	}

	operatingSystem := context.OperatingSystem(target.Session())
	buildDeployment := buildMeta.Match(operatingSystem, buildSpec.Version)
	if buildDeployment == nil {
		return nil, fmt.Errorf("Failed to find a build for provided operating system: %v %v", operatingSystem.Name, operatingSystem.Version)
	}

	deploymentService, err := context.Service(DeploymentServiceId)

	if err != nil {
		return nil, err
	}

	response = deploymentService.Run(context, buildDeployment.Config)
	if response.Error != "" {
		return nil, errors.New(response.Error)

	}

	commandInfo, err := context.Execute(target, goal.Command)
	if err != nil {
		return nil, err
	}
	result.CommandInfo = commandInfo
	if goal.Transfers != nil {
		_, err = context.Transfer(goal.Transfers.Transfers...)
		if err != nil {
			return nil, err
		}
	}
	if goal.VerificationCommand != nil {
		_, err = context.Execute(target, goal.VerificationCommand)
		if err != nil {
			return nil, err
		}
	}
	return result, nil
}
func newBuildState(buildSepc *BuildSpec, parsedUrl *url.URL, request *BuildRequest, context *Context) (common.Map, error) {
	target, err := context.ExpandResource(request.Target)
	if err != nil {
		return nil, err
	}
	build := common.NewMap()
	build.Put("args", buildSepc.Args)
	build.Put("target", parsedUrl.Path)
	build.Put("host", parsedUrl.Host)
	build.Put("credential", target.Credential)
	build.Put("sdk", buildSepc.Sdk)
	build.Put("sdkVersion", buildSepc.SdkVersion)
	return build, nil
}

func (s *BuildService) Run(context *Context, request interface{}) *ServiceResponse {
	var response = &ServiceResponse{
		Status: "ok",
	}
	var err error
	switch actualRequest := request.(type) {
	case *BuildRequest:
		response.Response, err = s.build(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to build: %v %v", actualRequest.Target.URL, err)
		}
	case *BuildRegisterMetaRequest:
		s.registry.Register(actualRequest.Meta)

	case *BuildLoadMetaRequest:
		s.load(context, actualRequest)

	default:
		response.Error = fmt.Sprintf("Unsupported request type: %T", request)
	}
	if response.Error != "" {
		response.Status = "error"
	}
	return response
}

func (s *BuildService) load(context *Context, request *BuildLoadMetaRequest) (*BuildLoadMetaResponse, error) {
	var result = &BuildLoadMetaResponse{
		Loaded: make(map[string]*BuildMeta),
	}
	target, err := context.ExpandResource(request.Resource)
	if err != nil {
		return nil, err
	}

	service, err := storage.NewServiceForURL(target.URL, "")
	if err != nil {
		return nil, err
	}
	objects, err := service.List(target.URL)
	if err != nil {
		return nil, err
	}

	for _, object := range objects {
		reader, err := service.Download(object)
		if err != nil {
			return nil, err
		}
		var buildMeta = &BuildMeta{}
		err = toolbox.NewJSONDecoderFactory().Create(reader).Decode(buildMeta)
		if err != nil {
			return nil, err
		}
		err = s.registry.Register(buildMeta)
		if err != nil {
			return nil, err
		}
		result.Loaded[object.URL()] = buildMeta
	}
	return result, nil
}

func (s *BuildService) NewRequest(action string) (interface{}, error) {
	switch action {
	case "build":
		return &BuildRequest{}, nil
	case "load":
		return &BuildLoadMetaRequest{}, nil
	case "register":
		return &BuildRegisterMetaRequest{}, nil
	}
	return s.AbstractService.NewRequest(action)

}

func NewBuildService() Service {
	var result = &BuildService{
		registry:        make(map[string]*BuildMeta),
		AbstractService: NewAbstractService(BuildServiceId),
	}
	result.AbstractService.Service = result
	return result
}

type BuildMetaRegistry map[string]*BuildMeta

func indexBuildGoals(goals []*BuildGoal, index map[string]*BuildGoal) {
	if len(goals) == 0 {
		return
	}
	for _, goal := range goals {
		index[goal.Name] = goal
	}
}

func (r *BuildMetaRegistry) Register(meta *BuildMeta) error {
	err := meta.Validate()
	if err != nil {
		return nil
	}
	meta.goalsIndex = make(map[string]*BuildGoal)
	indexBuildGoals(meta.Goals, meta.goalsIndex)
	(*r)[meta.Name] = meta
	return nil
}
<commit_msg>updated transfer request<commit_after>package endly

import (
	"fmt"
	"github.com/pkg/errors"
	"github.com/viant/endly/common"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/storage"
	"net/url"
)

const BuildServiceId = "build"

type OperatingSystemDeployment struct {
	OsTarget *OperatingSystemTarget
	Config   *DeploymentConfig
}

type BuildGoal struct {
	Name                string
	Command             *ManagedCommand
	Transfers           *TransferCopyRequest
	VerificationCommand *ManagedCommand
}

type BuildMeta struct {
	Sdk              string
	SdkVersion       string
	Name             string
	Goals            []*BuildGoal
	goalsIndex       map[string]*BuildGoal
	BuildDeployments []*OperatingSystemDeployment //defines deployment of the build app itself, i.e how to get maven installed
}

func (m *BuildMeta) Validate() error {
	if m.Name == "" {
		return fmt.Errorf("MetaBuild.Names %v", m.Name)

	}
	if len(m.Goals) == 0 {
		return fmt.Errorf("MetaBuild.Goals were empty %v", m.Name)
	}
	return nil
}

func (m *BuildMeta) Match(operatingSystem *OperatingSystem, version string) *OperatingSystemDeployment {
	for _, candidate := range m.BuildDeployments {
		osTarget := candidate.OsTarget
		if version != "" {
			if candidate.Config.Transfer.Target.Version != version {
				continue
			}
		}
		if operatingSystem.Matches(osTarget) {
			return candidate
		}
	}
	return nil
}

type BuildSpec struct {
	Name       string //build name  like go, mvn, node, yarn
	Version    string
	Goal       string //actual build target, like clean, test
	Args       string // additional build arguments , that can be expanded with $build.args
	Sdk        string
	SdkVersion string
}

type BuildRequest struct {
	BuildSpec *BuildSpec //build specification
	Target    *Resource  //path to application to be build, Note that command may use $build.target variable. that expands to Target URL path
}

type BuildResponse struct {
	SdkResponse *SdkSetResponse
	CommandInfo *CommandInfo
}

type BuildRegisterMetaRequest struct {
	Meta *BuildMeta
}

type BuildLoadMetaRequest struct {
	Resource *Resource
}

type BuildLoadMetaResponse struct {
	Loaded map[string]*BuildMeta //url to size
}

type BuildService struct {
	*AbstractService
	registry BuildMetaRegistry
}

func (s *BuildService) build(context *Context, request *BuildRequest) (interface{}, error) {
	var result = &BuildResponse{}
	state := context.State()
	target, err := context.ExpandResource(request.Target)
	if err != nil {
		return nil, err
	}
	buildSpec := request.BuildSpec

	if buildSpec == nil {
		return nil, fmt.Errorf("BuildSpec was empty")
	}
	buildMeta, has := s.registry[buildSpec.Name]
	if !has {
		return nil, fmt.Errorf("Failed to lookup build: %v", buildSpec.Name)
	}

	goal, has := buildMeta.goalsIndex[buildSpec.Goal]
	if !has {
		return nil, fmt.Errorf("Failed to lookup build %v goal: %v", buildSpec.Name, buildSpec.Goal)
	}

	buildState, err := newBuildState(buildSpec, target.ParsedURL, request, context)
	if err != nil {
		return nil, err
	}
	if buildMeta.Sdk != "" {
		state.Put("build", buildState)
		sdkService, err := context.Service(SdkServiceId)
		if err != nil {
			return nil, err
		}
		serviceResponse := sdkService.Run(context, &SdkSetRequest{Target: request.Target,
			Sdk:     context.Expand(buildMeta.Sdk),
			Version: context.Expand(buildMeta.SdkVersion),
		})
		if serviceResponse.Error != "" {
			return nil, errors.New(serviceResponse.Error)
		}
		result.SdkResponse, _ = serviceResponse.Response.(*SdkSetResponse)
	}

	execService, err := context.Service(ExecServiceId)
	if err != nil {
		return nil, err
	}
	state.Put("build", buildState)
	response := execService.Run(context, &OpenSession{
		Target: target,
	})

	if response.Error != "" {
		return nil, errors.New(response.Error)
	}

	operatingSystem := context.OperatingSystem(target.Session())
	buildDeployment := buildMeta.Match(operatingSystem, buildSpec.Version)
	if buildDeployment == nil {
		return nil, fmt.Errorf("Failed to find a build for provided operating system: %v %v", operatingSystem.Name, operatingSystem.Version)
	}

	deploymentService, err := context.Service(DeploymentServiceId)

	if err != nil {
		return nil, err
	}

	response = deploymentService.Run(context, buildDeployment.Config)
	if response.Error != "" {
		return nil, errors.New(response.Error)

	}

	commandInfo, err := context.Execute(target, goal.Command)
	if err != nil {
		return nil, err
	}
	result.CommandInfo = commandInfo
	if goal.Transfers != nil {
		_, err = context.Transfer(goal.Transfers.Transfers...)
		if err != nil {
			return nil, err
		}
	}
	if goal.VerificationCommand != nil {
		_, err = context.Execute(target, goal.VerificationCommand)
		if err != nil {
			return nil, err
		}
	}
	return result, nil
}
func newBuildState(buildSepc *BuildSpec, parsedUrl *url.URL, request *BuildRequest, context *Context) (common.Map, error) {
	target, err := context.ExpandResource(request.Target)
	if err != nil {
		return nil, err
	}
	build := common.NewMap()
	build.Put("args", buildSepc.Args)
	build.Put("target", parsedUrl.Path)
	build.Put("host", parsedUrl.Host)
	build.Put("credential", target.Credential)
	build.Put("sdk", buildSepc.Sdk)
	build.Put("sdkVersion", buildSepc.SdkVersion)
	return build, nil
}

func (s *BuildService) Run(context *Context, request interface{}) *ServiceResponse {
	var response = &ServiceResponse{
		Status: "ok",
	}
	var err error
	switch actualRequest := request.(type) {
	case *BuildRequest:
		response.Response, err = s.build(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to build: %v %v", actualRequest.Target.URL, err)
		}
	case *BuildRegisterMetaRequest:
		s.registry.Register(actualRequest.Meta)

	case *BuildLoadMetaRequest:
		s.load(context, actualRequest)

	default:
		response.Error = fmt.Sprintf("Unsupported request type: %T", request)
	}
	if response.Error != "" {
		response.Status = "error"
	}
	return response
}

func (s *BuildService) load(context *Context, request *BuildLoadMetaRequest) (*BuildLoadMetaResponse, error) {
	var result = &BuildLoadMetaResponse{
		Loaded: make(map[string]*BuildMeta),
	}
	target, err := context.ExpandResource(request.Resource)
	if err != nil {
		return nil, err
	}

	service, err := storage.NewServiceForURL(target.URL, "")
	if err != nil {
		return nil, err
	}
	objects, err := service.List(target.URL)
	if err != nil {
		return nil, err
	}

	for _, object := range objects {
		reader, err := service.Download(object)
		if err != nil {
			return nil, err
		}
		var buildMeta = &BuildMeta{}
		err = toolbox.NewJSONDecoderFactory().Create(reader).Decode(buildMeta)
		if err != nil {
			return nil, err
		}
		err = s.registry.Register(buildMeta)
		if err != nil {
			return nil, err
		}
		result.Loaded[object.URL()] = buildMeta
	}
	return result, nil
}

func (s *BuildService) NewRequest(action string) (interface{}, error) {
	switch action {
	case "build":
		return &BuildRequest{}, nil
	case "load":
		return &BuildLoadMetaRequest{}, nil
	case "register":
		return &BuildRegisterMetaRequest{}, nil
	}
	return s.AbstractService.NewRequest(action)

}

func NewBuildService() Service {
	var result = &BuildService{
		registry:        make(map[string]*BuildMeta),
		AbstractService: NewAbstractService(BuildServiceId),
	}
	result.AbstractService.Service = result
	return result
}

type BuildMetaRegistry map[string]*BuildMeta

func indexBuildGoals(goals []*BuildGoal, index map[string]*BuildGoal) {
	if len(goals) == 0 {
		return
	}
	for _, goal := range goals {
		index[goal.Name] = goal
	}
}

func (r *BuildMetaRegistry) Register(meta *BuildMeta) error {
	err := meta.Validate()
	if err != nil {
		return nil
	}
	meta.goalsIndex = make(map[string]*BuildGoal)
	indexBuildGoals(meta.Goals, meta.goalsIndex)
	(*r)[meta.Name] = meta
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
// Use of this source code is governed by a MIT-license.
// See http://olivere.mit-license.org/license.txt for details.

package elastic

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/url"

	"gopkg.in/olivere/elastic.v3/uritemplates"
)

// BulkService allows for batching bulk requests and sending them to
// Elasticsearch in one roundtrip. Use the Add method with BulkIndexRequest,
// BulkUpdateRequest, and BulkDeleteRequest to add bulk requests to a batch,
// then use Do to send them to Elasticsearch.
//
// BulkService will be reset after each Do call. In other words, you can
// reuse BulkService to send many batches. You do not have to create a new
// BulkService for each batch.
//
// See https://www.elastic.co/guide/en/elasticsearch/reference/2.x/docs-bulk.html
// for more details.
type BulkService struct {
	client *Client

	index    string
	_type    string
	requests []BulkableRequest
	//replicationType string
	//consistencyLevel string
	timeout string
	refresh *bool
	pretty  bool

	sizeInBytes int64
}

// NewBulkService initializes a new BulkService.
func NewBulkService(client *Client) *BulkService {
	builder := &BulkService{
		client:   client,
		requests: make([]BulkableRequest, 0),
	}
	return builder
}

func (s *BulkService) reset() {
	s.requests = make([]BulkableRequest, 0)
	s.sizeInBytes = 0
}

// Index specifies the index to use for all batches. You may also leave
// this blank and specify the index in the individual bulk requests.
func (s *BulkService) Index(index string) *BulkService {
	s.index = index
	return s
}

// Type specifies the type to use for all batches. You may also leave
// this blank and specify the type in the individual bulk requests.
func (s *BulkService) Type(_type string) *BulkService {
	s._type = _type
	return s
}

// Timeout is a global timeout for processing bulk requests. This is a
// server-side timeout, i.e. it tells Elasticsearch the time after which
// it should stop processing.
func (s *BulkService) Timeout(timeout string) *BulkService {
	s.timeout = timeout
	return s
}

// Refresh, when set to true, tells Elasticsearch to make the bulk requests
// available to search immediately after being processed. Normally, this
// only happens after a specified refresh interval.
func (s *BulkService) Refresh(refresh bool) *BulkService {
	s.refresh = &refresh
	return s
}

// Pretty tells Elasticsearch whether to return a formatted JSON response.
func (s *BulkService) Pretty(pretty bool) *BulkService {
	s.pretty = pretty
	return s
}

// Add adds bulkable requests, i.e. BulkIndexRequest, BulkUpdateRequest,
// and/or BulkDeleteRequest.
func (s *BulkService) Add(requests ...BulkableRequest) *BulkService {
	for _, r := range requests {
		s.requests = append(s.requests, r)
		s.sizeInBytes += s.estimateSizeInBytes(r)
	}
	return s
}

// EstimatedSizeInBytes returns the estimated size of all bulkable
// requests added via Add.
func (s *BulkService) EstimatedSizeInBytes() int64 {
	// var size int64
	// for _, r := range s.requests {
	// 	size += s.estimateSizeInBytes(r)
	// }
	// return size
	return s.sizeInBytes
}

// estimateSizeInBytes returns the estimates size of the given
// bulkable request, i.e. BulkIndexRequest, BulkUpdateRequest, and
// BulkDeleteRequest.
func (s *BulkService) estimateSizeInBytes(r BulkableRequest) int64 {
	lines, _ := r.Source()
	size := 0
	for _, line := range lines {
		size += len(line)
	}
	// +1 for the \n
	return int64(1 + size)
}

// NumberOfActions returns the number of bulkable requests that need to
// be sent to Elasticsearch on the next batch.
func (s *BulkService) NumberOfActions() int {
	return len(s.requests)
}

func (s *BulkService) bodyAsString() (string, error) {
	buf := bytes.NewBufferString("")

	for _, req := range s.requests {
		source, err := req.Source()
		if err != nil {
			return "", err
		}
		for _, line := range source {
			_, err := buf.WriteString(fmt.Sprintf("%s\n", line))
			if err != nil {
				return "", nil
			}
		}
	}

	return buf.String(), nil
}

// Do sends the batched requests to Elasticsearch. Note that, when successful,
// you can reuse the BulkService for the next batch as the list of bulk
// requests is cleared on success.
func (s *BulkService) Do() (*BulkResponse, error) {
	// No actions?
	if s.NumberOfActions() == 0 {
		return nil, errors.New("elastic: No bulk actions to commit")
	}

	// Get body
	body, err := s.bodyAsString()
	if err != nil {
		return nil, err
	}

	// Build url
	path := "/"
	if s.index != "" {
		index, err := uritemplates.Expand("{index}", map[string]string{
			"index": s.index,
		})
		if err != nil {
			return nil, err
		}
		path += index + "/"
	}
	if s._type != "" {
		typ, err := uritemplates.Expand("{type}", map[string]string{
			"type": s._type,
		})
		if err != nil {
			return nil, err
		}
		path += typ + "/"
	}
	path += "_bulk"

	// Parameters
	params := make(url.Values)
	if s.pretty {
		params.Set("pretty", fmt.Sprintf("%v", s.pretty))
	}
	if s.refresh != nil {
		params.Set("refresh", fmt.Sprintf("%v", *s.refresh))
	}
	if s.timeout != "" {
		params.Set("timeout", s.timeout)
	}

	// Get response
	res, err := s.client.PerformRequest("POST", path, params, body)
	if err != nil {
		return nil, err
	}

	// Return results
	ret := new(BulkResponse)
	if err := json.Unmarshal(res.Body, ret); err != nil {
		return nil, err
	}

	// Reset so the request can be reused
	s.reset()

	return ret, nil
}

// BulkResponse is a response to a bulk execution.
//
// Example:
// {
//   "took":3,
//   "errors":false,
//   "items":[{
//     "index":{
//       "_index":"index1",
//       "_type":"tweet",
//       "_id":"1",
//       "_version":3,
//       "status":201
//     }
//   },{
//     "index":{
//       "_index":"index2",
//       "_type":"tweet",
//       "_id":"2",
//       "_version":3,
//       "status":200
//     }
//   },{
//     "delete":{
//       "_index":"index1",
//       "_type":"tweet",
//       "_id":"1",
//       "_version":4,
//       "status":200,
//       "found":true
//     }
//   },{
//     "update":{
//       "_index":"index2",
//       "_type":"tweet",
//       "_id":"2",
//       "_version":4,
//       "status":200
//     }
//   }]
// }
type BulkResponse struct {
	Took   int                            `json:"took,omitempty"`
	Errors bool                           `json:"errors,omitempty"`
	Items  []map[string]*BulkResponseItem `json:"items,omitempty"`
}

// BulkResponseItem is the result of a single bulk request.
type BulkResponseItem struct {
	Index   string        `json:"_index,omitempty"`
	Type    string        `json:"_type,omitempty"`
	Id      string        `json:"_id,omitempty"`
	Version int           `json:"_version,omitempty"`
	Status  int           `json:"status,omitempty"`
	Found   bool          `json:"found,omitempty"`
	Error   *ErrorDetails `json:"error,omitempty"`
}

// Indexed returns all bulk request results of "index" actions.
func (r *BulkResponse) Indexed() []*BulkResponseItem {
	return r.ByAction("index")
}

// Created returns all bulk request results of "create" actions.
func (r *BulkResponse) Created() []*BulkResponseItem {
	return r.ByAction("create")
}

// Updated returns all bulk request results of "update" actions.
func (r *BulkResponse) Updated() []*BulkResponseItem {
	return r.ByAction("update")
}

// Deleted returns all bulk request results of "delete" actions.
func (r *BulkResponse) Deleted() []*BulkResponseItem {
	return r.ByAction("delete")
}

// ByAction returns all bulk request results of a certain action,
// e.g. "index" or "delete".
func (r *BulkResponse) ByAction(action string) []*BulkResponseItem {
	if r.Items == nil {
		return nil
	}
	items := make([]*BulkResponseItem, 0)
	for _, item := range r.Items {
		if result, found := item[action]; found {
			items = append(items, result)
		}
	}
	return items
}

// ById returns all bulk request results of a given document id,
// regardless of the action ("index", "delete" etc.).
func (r *BulkResponse) ById(id string) []*BulkResponseItem {
	if r.Items == nil {
		return nil
	}
	items := make([]*BulkResponseItem, 0)
	for _, item := range r.Items {
		for _, result := range item {
			if result.Id == id {
				items = append(items, result)
			}
		}
	}
	return items
}

// Failed returns those items of a bulk response that have errors,
// i.e. those that don't have a status code between 200 and 299.
func (r *BulkResponse) Failed() []*BulkResponseItem {
	if r.Items == nil {
		return nil
	}
	errors := make([]*BulkResponseItem, 0)
	for _, item := range r.Items {
		for _, result := range item {
			if !(result.Status >= 200 && result.Status <= 299) {
				errors = append(errors, result)
			}
		}
	}
	return errors
}

// Succeeded returns those items of a bulk response that have no errors,
// i.e. those have a status code between 200 and 299.
func (r *BulkResponse) Succeeded() []*BulkResponseItem {
	if r.Items == nil {
		return nil
	}
	succeeded := make([]*BulkResponseItem, 0)
	for _, item := range r.Items {
		for _, result := range item {
			if result.Status >= 200 && result.Status <= 299 {
				succeeded = append(succeeded, result)
			}
		}
	}
	return succeeded
}
<commit_msg>Count 1 newline per line<commit_after>// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
// Use of this source code is governed by a MIT-license.
// See http://olivere.mit-license.org/license.txt for details.

package elastic

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/url"

	"gopkg.in/olivere/elastic.v3/uritemplates"
)

// BulkService allows for batching bulk requests and sending them to
// Elasticsearch in one roundtrip. Use the Add method with BulkIndexRequest,
// BulkUpdateRequest, and BulkDeleteRequest to add bulk requests to a batch,
// then use Do to send them to Elasticsearch.
//
// BulkService will be reset after each Do call. In other words, you can
// reuse BulkService to send many batches. You do not have to create a new
// BulkService for each batch.
//
// See https://www.elastic.co/guide/en/elasticsearch/reference/2.x/docs-bulk.html
// for more details.
type BulkService struct {
	client *Client

	index    string
	_type    string
	requests []BulkableRequest
	//replicationType string
	//consistencyLevel string
	timeout string
	refresh *bool
	pretty  bool

	sizeInBytes int64
}

// NewBulkService initializes a new BulkService.
func NewBulkService(client *Client) *BulkService {
	builder := &BulkService{
		client:   client,
		requests: make([]BulkableRequest, 0),
	}
	return builder
}

func (s *BulkService) reset() {
	s.requests = make([]BulkableRequest, 0)
	s.sizeInBytes = 0
}

// Index specifies the index to use for all batches. You may also leave
// this blank and specify the index in the individual bulk requests.
func (s *BulkService) Index(index string) *BulkService {
	s.index = index
	return s
}

// Type specifies the type to use for all batches. You may also leave
// this blank and specify the type in the individual bulk requests.
func (s *BulkService) Type(_type string) *BulkService {
	s._type = _type
	return s
}

// Timeout is a global timeout for processing bulk requests. This is a
// server-side timeout, i.e. it tells Elasticsearch the time after which
// it should stop processing.
func (s *BulkService) Timeout(timeout string) *BulkService {
	s.timeout = timeout
	return s
}

// Refresh, when set to true, tells Elasticsearch to make the bulk requests
// available to search immediately after being processed. Normally, this
// only happens after a specified refresh interval.
func (s *BulkService) Refresh(refresh bool) *BulkService {
	s.refresh = &refresh
	return s
}

// Pretty tells Elasticsearch whether to return a formatted JSON response.
func (s *BulkService) Pretty(pretty bool) *BulkService {
	s.pretty = pretty
	return s
}

// Add adds bulkable requests, i.e. BulkIndexRequest, BulkUpdateRequest,
// and/or BulkDeleteRequest.
func (s *BulkService) Add(requests ...BulkableRequest) *BulkService {
	for _, r := range requests {
		s.requests = append(s.requests, r)
		s.sizeInBytes += s.estimateSizeInBytes(r)
	}
	return s
}

// EstimatedSizeInBytes returns the estimated size of all bulkable
// requests added via Add.
func (s *BulkService) EstimatedSizeInBytes() int64 {
	// var size int64
	// for _, r := range s.requests {
	// 	size += s.estimateSizeInBytes(r)
	// }
	// return size
	return s.sizeInBytes
}

// estimateSizeInBytes returns the estimates size of the given
// bulkable request, i.e. BulkIndexRequest, BulkUpdateRequest, and
// BulkDeleteRequest.
func (s *BulkService) estimateSizeInBytes(r BulkableRequest) int64 {
	lines, _ := r.Source()
	size := 0
	for _, line := range lines {
		// +1 for the \n
		size += len(line) + 1
	}
	return int64(size)
}

// NumberOfActions returns the number of bulkable requests that need to
// be sent to Elasticsearch on the next batch.
func (s *BulkService) NumberOfActions() int {
	return len(s.requests)
}

func (s *BulkService) bodyAsString() (string, error) {
	buf := bytes.NewBufferString("")

	for _, req := range s.requests {
		source, err := req.Source()
		if err != nil {
			return "", err
		}
		for _, line := range source {
			_, err := buf.WriteString(fmt.Sprintf("%s\n", line))
			if err != nil {
				return "", nil
			}
		}
	}

	return buf.String(), nil
}

// Do sends the batched requests to Elasticsearch. Note that, when successful,
// you can reuse the BulkService for the next batch as the list of bulk
// requests is cleared on success.
func (s *BulkService) Do() (*BulkResponse, error) {
	// No actions?
	if s.NumberOfActions() == 0 {
		return nil, errors.New("elastic: No bulk actions to commit")
	}

	// Get body
	body, err := s.bodyAsString()
	if err != nil {
		return nil, err
	}

	// Build url
	path := "/"
	if s.index != "" {
		index, err := uritemplates.Expand("{index}", map[string]string{
			"index": s.index,
		})
		if err != nil {
			return nil, err
		}
		path += index + "/"
	}
	if s._type != "" {
		typ, err := uritemplates.Expand("{type}", map[string]string{
			"type": s._type,
		})
		if err != nil {
			return nil, err
		}
		path += typ + "/"
	}
	path += "_bulk"

	// Parameters
	params := make(url.Values)
	if s.pretty {
		params.Set("pretty", fmt.Sprintf("%v", s.pretty))
	}
	if s.refresh != nil {
		params.Set("refresh", fmt.Sprintf("%v", *s.refresh))
	}
	if s.timeout != "" {
		params.Set("timeout", s.timeout)
	}

	// Get response
	res, err := s.client.PerformRequest("POST", path, params, body)
	if err != nil {
		return nil, err
	}

	// Return results
	ret := new(BulkResponse)
	if err := json.Unmarshal(res.Body, ret); err != nil {
		return nil, err
	}

	// Reset so the request can be reused
	s.reset()

	return ret, nil
}

// BulkResponse is a response to a bulk execution.
//
// Example:
// {
//   "took":3,
//   "errors":false,
//   "items":[{
//     "index":{
//       "_index":"index1",
//       "_type":"tweet",
//       "_id":"1",
//       "_version":3,
//       "status":201
//     }
//   },{
//     "index":{
//       "_index":"index2",
//       "_type":"tweet",
//       "_id":"2",
//       "_version":3,
//       "status":200
//     }
//   },{
//     "delete":{
//       "_index":"index1",
//       "_type":"tweet",
//       "_id":"1",
//       "_version":4,
//       "status":200,
//       "found":true
//     }
//   },{
//     "update":{
//       "_index":"index2",
//       "_type":"tweet",
//       "_id":"2",
//       "_version":4,
//       "status":200
//     }
//   }]
// }
type BulkResponse struct {
	Took   int                            `json:"took,omitempty"`
	Errors bool                           `json:"errors,omitempty"`
	Items  []map[string]*BulkResponseItem `json:"items,omitempty"`
}

// BulkResponseItem is the result of a single bulk request.
type BulkResponseItem struct {
	Index   string        `json:"_index,omitempty"`
	Type    string        `json:"_type,omitempty"`
	Id      string        `json:"_id,omitempty"`
	Version int           `json:"_version,omitempty"`
	Status  int           `json:"status,omitempty"`
	Found   bool          `json:"found,omitempty"`
	Error   *ErrorDetails `json:"error,omitempty"`
}

// Indexed returns all bulk request results of "index" actions.
func (r *BulkResponse) Indexed() []*BulkResponseItem {
	return r.ByAction("index")
}

// Created returns all bulk request results of "create" actions.
func (r *BulkResponse) Created() []*BulkResponseItem {
	return r.ByAction("create")
}

// Updated returns all bulk request results of "update" actions.
func (r *BulkResponse) Updated() []*BulkResponseItem {
	return r.ByAction("update")
}

// Deleted returns all bulk request results of "delete" actions.
func (r *BulkResponse) Deleted() []*BulkResponseItem {
	return r.ByAction("delete")
}

// ByAction returns all bulk request results of a certain action,
// e.g. "index" or "delete".
func (r *BulkResponse) ByAction(action string) []*BulkResponseItem {
	if r.Items == nil {
		return nil
	}
	items := make([]*BulkResponseItem, 0)
	for _, item := range r.Items {
		if result, found := item[action]; found {
			items = append(items, result)
		}
	}
	return items
}

// ById returns all bulk request results of a given document id,
// regardless of the action ("index", "delete" etc.).
func (r *BulkResponse) ById(id string) []*BulkResponseItem {
	if r.Items == nil {
		return nil
	}
	items := make([]*BulkResponseItem, 0)
	for _, item := range r.Items {
		for _, result := range item {
			if result.Id == id {
				items = append(items, result)
			}
		}
	}
	return items
}

// Failed returns those items of a bulk response that have errors,
// i.e. those that don't have a status code between 200 and 299.
func (r *BulkResponse) Failed() []*BulkResponseItem {
	if r.Items == nil {
		return nil
	}
	errors := make([]*BulkResponseItem, 0)
	for _, item := range r.Items {
		for _, result := range item {
			if !(result.Status >= 200 && result.Status <= 299) {
				errors = append(errors, result)
			}
		}
	}
	return errors
}

// Succeeded returns those items of a bulk response that have no errors,
// i.e. those have a status code between 200 and 299.
func (r *BulkResponse) Succeeded() []*BulkResponseItem {
	if r.Items == nil {
		return nil
	}
	succeeded := make([]*BulkResponseItem, 0)
	for _, item := range r.Items {
		for _, result := range item {
			if result.Status >= 200 && result.Status <= 299 {
				succeeded = append(succeeded, result)
			}
		}
	}
	return succeeded
}
<|endoftext|>


<commit_before>package deck

/* Card interface defines a object that represents a card in a card game
   The implementing struct should be able to represent the Card via the String()
   function.
   Cards can be collected and shuffled in a Deck and be held by Players.

   TODO: Should implementations store information about what Player has played a Card?
*/
type Card interface {
	//PlayedBy(Player) Player
	String() string
}

// PrintCards takes a slice of Cards and returns a single string with each Card printed
// and seperated by spaces.
// For use as a helper function for structs that implement Card,
func PrintCards(stack []Card) string {
	var s string

	for _, c := range stack {
		if s != "" {
			s = s + " " + c.String()
		} else {
			s = c.String()
		}
	}
	return s
}

// CardSlice takes a varidric list of Cards and returns a slice of Cards.
// Useful for taking a slice of CardType and returning a slice of Card.
func CardSlice(cards ...Card) []Card {
	slice := make([]Card, len(cards))

	for i, c := range cards {
		slice[i] = c
	}
	return slice
}

// PopCard attempts to remove a Card from a slice of Cards, returns true if successful.
// **Not currently being used**
func popCard(c Card, s []Card) bool {
	for i, v := range s {
		if c == v {
			s = append(s[:i], s[i+1:]...)
			return true
		}
	}
	return false
}
<commit_msg>removed useless card functions<commit_after>package deck

/* Card interface defines a object that represents a card in a card game
   The implementing struct should be able to represent the Card via the String()
   function.
   Cards can be collected and shuffled in a Deck and be held by Players.

   TODO: Should implementations store information about what Player has played a Card?
*/
type Card interface {
	//PlayedBy(Player) Player
	String() string
}

// PrintCards takes a slice of Cards and returns a single string with each Card printed
// and seperated by spaces.
// For use as a helper function for structs that implement Card,
func PrintCards(stack []Card) string {
	var s string

	for _, c := range stack {
		if s != "" {
			s = s + " " + c.String()
		} else {
			s = c.String()
		}
	}
	return s
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"syscall"

	"github.com/jingweno/ccat/Godeps/_workspace/src/github.com/mattn/go-isatty"
	"fmt"
)

type CCatPrinter interface {
	Print(r io.Reader, w io.Writer) error
}

type AutoColorPrinter struct {
	ColorPalettes ColorPalettes
}

func (a AutoColorPrinter) Print(r io.Reader, w io.Writer) error {
	if isatty.IsTerminal(uintptr(syscall.Stdout)) {
		return ColorPrinter{a.ColorPalettes}.Print(r, w)
	} else {
		return PlainTextPrinter{}.Print(r, w)
	}
}

type ColorPrinter struct {
	ColorPalettes ColorPalettes
}

func (c ColorPrinter) Print(r io.Reader, w io.Writer) error {
	return CPrint(r, w, c.ColorPalettes)
}

type PlainTextPrinter struct {
}

func (p PlainTextPrinter) Print(r io.Reader, w io.Writer) error {
	_, err := io.Copy(w, r)
	return err
}

type HtmlPrinter struct {
	ColorPalettes ColorPalettes
}

func (c HtmlPrinter) Print(r io.Reader, w io.Writer) error {
	return HtmlPrint(r, w, c.ColorPalettes)
}

func CCat(fname string, p CCatPrinter, w io.Writer) error {
	var r io.Reader

	if fname == readFromStdin {
		// scanner.Scanner from text/scanner couldn't detect EOF
		// if the io.Reader is os.Stdin
		// see https://github.com/golang/go/issues/10735
		b, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			return err
		}

		r = bytes.NewReader(b)
	} else {
		file, err := os.Open(fname)
		if err != nil {
			return err
		}

		defer file.Close()

		fi, err := file.Stat()

		switch mode := fi.Mode(); {
		case mode.IsDir():
			fmt.Println(file.Name() + ": Is a directory")
			os.Exit(1)
		case mode.IsRegular():
			r = file
		}
	}

	return p.Print(r, w)
}
<commit_msg>Reworked directory check to be more concise<commit_after>package main

import (
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"syscall"

	"github.com/jingweno/ccat/Godeps/_workspace/src/github.com/mattn/go-isatty"
	"fmt"
)

type CCatPrinter interface {
	Print(r io.Reader, w io.Writer) error
}

type AutoColorPrinter struct {
	ColorPalettes ColorPalettes
}

func (a AutoColorPrinter) Print(r io.Reader, w io.Writer) error {
	if isatty.IsTerminal(uintptr(syscall.Stdout)) {
		return ColorPrinter{a.ColorPalettes}.Print(r, w)
	} else {
		return PlainTextPrinter{}.Print(r, w)
	}
}

type ColorPrinter struct {
	ColorPalettes ColorPalettes
}

func (c ColorPrinter) Print(r io.Reader, w io.Writer) error {
	return CPrint(r, w, c.ColorPalettes)
}

type PlainTextPrinter struct {
}

func (p PlainTextPrinter) Print(r io.Reader, w io.Writer) error {
	_, err := io.Copy(w, r)
	return err
}

type HtmlPrinter struct {
	ColorPalettes ColorPalettes
}

func (c HtmlPrinter) Print(r io.Reader, w io.Writer) error {
	return HtmlPrint(r, w, c.ColorPalettes)
}

func CCat(fname string, p CCatPrinter, w io.Writer) error {
	var r io.Reader

	if fname == readFromStdin {
		// scanner.Scanner from text/scanner couldn't detect EOF
		// if the io.Reader is os.Stdin
		// see https://github.com/golang/go/issues/10735
		b, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			return err
		}

		r = bytes.NewReader(b)
	} else {
		file, err := os.Open(fname)
		if err != nil {
			return err
		}

		defer file.Close()

		fi, err := file.Stat()

		if fi.Mode().IsDir() {
			return fmt.Errorf("%s is a directory", file.Name())
		}

		r = file
	}

	return p.Print(r, w)
}
<|endoftext|>


<commit_before>package xlsx

import (
	"errors"
	"fmt"
	"strconv"
)

// Sheet is a high level structure intended to provide user access to
// the contents of a particular sheet within an XLSX file.
type Sheet struct {
	Name        string
	File        *File
	Rows        []*Row
	Cols        []*Col
	MaxRow      int
	MaxCol      int
	Hidden      bool
	Selected    bool
	SheetViews  []SheetView
	SheetFormat SheetFormat
	AutoFilter  *AutoFilter
}

type SheetView struct {
	Pane *Pane
}

type Pane struct {
	XSplit      float64
	YSplit      float64
	TopLeftCell string
	ActivePane  string
	State       string // Either "split" or "frozen"
}

type SheetFormat struct {
	DefaultColWidth  float64
	DefaultRowHeight float64
	OutlineLevelCol  uint8
	OutlineLevelRow  uint8
}

type AutoFilter struct {
	TopLeftCell     string
	BottomRightCell string
}

// Add a new Row to a Sheet
func (s *Sheet) AddRow() *Row {
	row := &Row{Sheet: s}
	s.Rows = append(s.Rows, row)
	if len(s.Rows) > s.MaxRow {
		s.MaxRow = len(s.Rows)
	}
	return row
}

// Make sure we always have as many Rows as we do cells.
func (s *Sheet) maybeAddRow(rowCount int) {
	if rowCount > s.MaxRow {
		loopCnt := rowCount - s.MaxRow
		for i := 0; i < loopCnt; i++ {

			row := &Row{Sheet: s}
			s.Rows = append(s.Rows, row)
		}
		s.MaxRow = rowCount
	}
}

// Make sure we always have as many Rows as we do cells.
func (s *Sheet) Row(idx int) *Row {
	s.maybeAddRow(idx + 1)
	return s.Rows[idx]
}

// Make sure we always have as many Cols as we do cells.
func (s *Sheet) maybeAddCol(cellCount int) {
	if cellCount > s.MaxCol {
		loopCnt := cellCount - s.MaxCol
		currIndex := s.MaxCol + 1
		for i := 0; i < loopCnt; i++ {

			col := &Col{
				style:     NewStyle(),
				Min:       currIndex,
				Max:       currIndex,
				Hidden:    false,
				Collapsed: false}
			s.Cols = append(s.Cols, col)
			currIndex++
		}

		s.MaxCol = cellCount
	}
}

// Make sure we always have as many Cols as we do cells.
func (s *Sheet) Col(idx int) *Col {
	s.maybeAddCol(idx + 1)
	return s.Cols[idx]
}

// Get a Cell by passing it's cartesian coordinates (zero based) as
// row and column integer indexes.
//
// For example:
//
//    cell := sheet.Cell(0,0)
//
// ... would set the variable "cell" to contain a Cell struct
// containing the data from the field "A1" on the spreadsheet.
func (sh *Sheet) Cell(row, col int) *Cell {

	// If the user requests a row beyond what we have, then extend.
	for len(sh.Rows) <= row {
		sh.AddRow()
	}

	r := sh.Rows[row]
	for len(r.Cells) <= col {
		r.AddCell()
	}

	return r.Cells[col]
}

//Set the width of a single column or multiple columns.
func (s *Sheet) SetColWidth(startcol, endcol int, width float64) error {
	if startcol > endcol {
		return fmt.Errorf("Could not set width for range %d-%d: startcol must be less than endcol.", startcol, endcol)
	}
	end := endcol + 1
	s.maybeAddCol(end)
	for ; startcol < end; startcol++ {
		s.Cols[startcol].Width = width
	}

	return nil
}

// When merging cells, the cell may be the 'original' or the 'covered'.
// First, figure out which cells are merge starting points. Then create
// the necessary cells underlying the merge area.
// Then go through all the underlying cells and apply the appropriate
// border, based on the original cell.
func (s *Sheet) handleMerged() {
	merged := make(map[string]*Cell)

	for r, row := range s.Rows {
		for c, cell := range row.Cells {
			if cell.HMerge > 0 || cell.VMerge > 0 {
				coord := GetCellIDStringFromCoords(c, r)
				merged[coord] = cell
			}
		}
	}

	// This loop iterates over all cells that should be merged and applies the correct
	// borders to them depending on their position. If any cells required by the merge
	// are missing, they will be allocated by s.Cell().
	for key, cell := range merged {
		mainstyle := cell.GetStyle()

		top := mainstyle.Border.Top
		left := mainstyle.Border.Left
		right := mainstyle.Border.Right
		bottom := mainstyle.Border.Bottom

		// When merging cells, the upper left cell does not maintain
		// the original borders
		mainstyle.Border.Top = "none"
		mainstyle.Border.Left = "none"
		mainstyle.Border.Right = "none"
		mainstyle.Border.Bottom = "none"

		maincol, mainrow, _ := GetCoordsFromCellIDString(key)
		for rownum := 0; rownum <= cell.VMerge; rownum++ {
			for colnum := 0; colnum <= cell.HMerge; colnum++ {
				tmpcell := s.Cell(mainrow+rownum, maincol+colnum)
				style := tmpcell.GetStyle()
				style.ApplyBorder = true

				if rownum == 0 {
					style.Border.Top = top
				}

				if rownum == (cell.VMerge) {
					style.Border.Bottom = bottom
				}

				if colnum == 0 {
					style.Border.Left = left
				}

				if colnum == (cell.HMerge) {
					style.Border.Right = right
				}
			}
		}
	}
}

// Dump sheet to its XML representation, intended for internal use only
func (s *Sheet) makeXLSXSheet(refTable *RefTable, styles *xlsxStyleSheet) *xlsxWorksheet {
	worksheet := newXlsxWorksheet()
	xSheet := xlsxSheetData{}
	maxRow := 0
	maxCell := 0
	var maxLevelCol, maxLevelRow uint8

	// Scan through the sheet and see if there are any merged cells. If there
	// are, we may need to extend the size of the sheet. There needs to be
	// phantom cells underlying the area covered by the merged cell
	s.handleMerged()

	for index, sheetView := range s.SheetViews {
		if sheetView.Pane != nil {
			worksheet.SheetViews.SheetView[index].Pane = &xlsxPane{
				XSplit:      sheetView.Pane.XSplit,
				YSplit:      sheetView.Pane.YSplit,
				TopLeftCell: sheetView.Pane.TopLeftCell,
				ActivePane:  sheetView.Pane.ActivePane,
				State:       sheetView.Pane.State,
			}

		}
	}

	if s.Selected {
		worksheet.SheetViews.SheetView[0].TabSelected = true
	}

	if s.SheetFormat.DefaultRowHeight != 0 {
		worksheet.SheetFormatPr.DefaultRowHeight = s.SheetFormat.DefaultRowHeight
	}
	worksheet.SheetFormatPr.DefaultColWidth = s.SheetFormat.DefaultColWidth

	colsXfIdList := make([]int, len(s.Cols))
	worksheet.Cols = &xlsxCols{Col: []xlsxCol{}}
	for c, col := range s.Cols {
		XfId := 0
		if col.Min == 0 {
			col.Min = 1
		}
		if col.Max == 0 {
			col.Max = 1
		}
		style := col.GetStyle()
		//col's style always not nil
		if style != nil {
			xNumFmt := styles.newNumFmt(col.numFmt)
			XfId = handleStyleForXLSX(style, xNumFmt.NumFmtId, styles)
		}
		colsXfIdList[c] = XfId

		var customWidth bool
		if col.Width == 0 {
			col.Width = ColWidth
			customWidth = false

		} else {
			customWidth = true
		}
		worksheet.Cols.Col = append(worksheet.Cols.Col,
			xlsxCol{Min: col.Min,
				Max:          col.Max,
				Hidden:       col.Hidden,
				Width:        col.Width,
				CustomWidth:  customWidth,
				Collapsed:    col.Collapsed,
				OutlineLevel: col.OutlineLevel,
				Style:        XfId,
			})

		if col.OutlineLevel > maxLevelCol {
			maxLevelCol = col.OutlineLevel
		}
	}

	for r, row := range s.Rows {
		if r > maxRow {
			maxRow = r
		}
		xRow := xlsxRow{}
		xRow.R = r + 1
		if row.isCustom {
			xRow.CustomHeight = true
			xRow.Ht = fmt.Sprintf("%g", row.Height)
		}
		xRow.OutlineLevel = row.OutlineLevel
		if row.OutlineLevel > maxLevelRow {
			maxLevelRow = row.OutlineLevel
		}
		for c, cell := range row.Cells {
			XfId := colsXfIdList[c]

			// generate NumFmtId and add new NumFmt
			xNumFmt := styles.newNumFmt(cell.NumFmt)

			style := cell.style
			if style != nil {
				XfId = handleStyleForXLSX(style, xNumFmt.NumFmtId, styles)
			} else if len(cell.NumFmt) > 0 && !compareFormatString(s.Cols[c].numFmt, cell.NumFmt) {
				XfId = handleNumFmtIdForXLSX(xNumFmt.NumFmtId, styles)
			}

			if c > maxCell {
				maxCell = c
			}
			xC := xlsxC{
				S: XfId,
				R: GetCellIDStringFromCoords(c, r),
			}
			if cell.formula != "" {
				xC.F = &xlsxF{Content: cell.formula}
			}
			switch cell.cellType {
			case CellTypeInline:
				// Inline strings are turned into shared strings since they are more efficient.
				// This is what Excel does as well.
				fallthrough
			case CellTypeString:
				if len(cell.Value) > 0 {
					xC.V = strconv.Itoa(refTable.AddString(cell.Value))
				}
				xC.T = "s"
			case CellTypeNumeric:
				// Numeric is the default, so the type can be left blank
				xC.V = cell.Value
			case CellTypeBool:
				xC.V = cell.Value
				xC.T = "b"
			case CellTypeError:
				xC.V = cell.Value
				xC.T = "e"
			case CellTypeDate:
				xC.V = cell.Value
				xC.T = "d"
			case CellTypeStringFormula:
				xC.V = cell.Value
				xC.T = "str"
			default:
				panic(errors.New("unknown cell type cannot be marshaled"))
			}

			xRow.C = append(xRow.C, xC)

			if cell.HMerge > 0 || cell.VMerge > 0 {
				// r == rownum, c == colnum
				mc := xlsxMergeCell{}
				start := GetCellIDStringFromCoords(c, r)
				endCol := c + cell.HMerge
				endRow := r + cell.VMerge
				end := GetCellIDStringFromCoords(endCol, endRow)
				mc.Ref = start + ":" + end
				if worksheet.MergeCells == nil {
					worksheet.MergeCells = &xlsxMergeCells{}
				}
				worksheet.MergeCells.Cells = append(worksheet.MergeCells.Cells, mc)
			}
		}
		xSheet.Row = append(xSheet.Row, xRow)
	}

	// Update sheet format with the freshly determined max levels
	s.SheetFormat.OutlineLevelCol = maxLevelCol
	s.SheetFormat.OutlineLevelRow = maxLevelRow
	// .. and then also apply this to the xml worksheet
	worksheet.SheetFormatPr.OutlineLevelCol = s.SheetFormat.OutlineLevelCol
	worksheet.SheetFormatPr.OutlineLevelRow = s.SheetFormat.OutlineLevelRow

	if worksheet.MergeCells != nil {
		worksheet.MergeCells.Count = len(worksheet.MergeCells.Cells)
	}

	if s.AutoFilter != nil {
		worksheet.AutoFilter = &xlsxAutoFilter{Ref: fmt.Sprintf("%v:%v", s.AutoFilter.TopLeftCell, s.AutoFilter.BottomRightCell)}
	}

	worksheet.SheetData = xSheet
	dimension := xlsxDimension{}
	dimension.Ref = "A1:" + GetCellIDStringFromCoords(maxCell, maxRow)
	if dimension.Ref == "A1:A1" {
		dimension.Ref = "A1"
	}
	worksheet.Dimension = dimension
	return worksheet
}

func handleStyleForXLSX(style *Style, NumFmtId int, styles *xlsxStyleSheet) (XfId int) {
	xFont, xFill, xBorder, xCellXf := style.makeXLSXStyleElements()
	fontId := styles.addFont(xFont)
	fillId := styles.addFill(xFill)

	// HACK - adding light grey fill, as in OO and Google
	greyfill := xlsxFill{}
	greyfill.PatternFill.PatternType = "lightGray"
	styles.addFill(greyfill)

	borderId := styles.addBorder(xBorder)
	xCellXf.FontId = fontId
	xCellXf.FillId = fillId
	xCellXf.BorderId = borderId
	xCellXf.NumFmtId = NumFmtId
	// apply the numFmtId when it is not the default cellxf
	if xCellXf.NumFmtId > 0 {
		xCellXf.ApplyNumberFormat = true
	}

	xCellXf.Alignment.Horizontal = style.Alignment.Horizontal
	xCellXf.Alignment.Indent = style.Alignment.Indent
	xCellXf.Alignment.ShrinkToFit = style.Alignment.ShrinkToFit
	xCellXf.Alignment.TextRotation = style.Alignment.TextRotation
	xCellXf.Alignment.Vertical = style.Alignment.Vertical
	xCellXf.Alignment.WrapText = style.Alignment.WrapText

	XfId = styles.addCellXf(xCellXf)
	return
}

func handleNumFmtIdForXLSX(NumFmtId int, styles *xlsxStyleSheet) (XfId int) {
	xCellXf := makeXLSXCellElement()
	xCellXf.NumFmtId = NumFmtId
	if xCellXf.NumFmtId > 0 {
		xCellXf.ApplyNumberFormat = true
	}
	XfId = styles.addCellXf(xCellXf)
	return
}
<commit_msg>fix:open excel after save, excel file format error issue #432<commit_after>package xlsx

import (
	"errors"
	"fmt"
	"strconv"
)

// Sheet is a high level structure intended to provide user access to
// the contents of a particular sheet within an XLSX file.
type Sheet struct {
	Name        string
	File        *File
	Rows        []*Row
	Cols        []*Col
	MaxRow      int
	MaxCol      int
	Hidden      bool
	Selected    bool
	SheetViews  []SheetView
	SheetFormat SheetFormat
	AutoFilter  *AutoFilter
}

type SheetView struct {
	Pane *Pane
}

type Pane struct {
	XSplit      float64
	YSplit      float64
	TopLeftCell string
	ActivePane  string
	State       string // Either "split" or "frozen"
}

type SheetFormat struct {
	DefaultColWidth  float64
	DefaultRowHeight float64
	OutlineLevelCol  uint8
	OutlineLevelRow  uint8
}

type AutoFilter struct {
	TopLeftCell     string
	BottomRightCell string
}

// Add a new Row to a Sheet
func (s *Sheet) AddRow() *Row {
	row := &Row{Sheet: s}
	s.Rows = append(s.Rows, row)
	if len(s.Rows) > s.MaxRow {
		s.MaxRow = len(s.Rows)
	}
	return row
}

// Make sure we always have as many Rows as we do cells.
func (s *Sheet) maybeAddRow(rowCount int) {
	if rowCount > s.MaxRow {
		loopCnt := rowCount - s.MaxRow
		for i := 0; i < loopCnt; i++ {

			row := &Row{Sheet: s}
			s.Rows = append(s.Rows, row)
		}
		s.MaxRow = rowCount
	}
}

// Make sure we always have as many Rows as we do cells.
func (s *Sheet) Row(idx int) *Row {
	s.maybeAddRow(idx + 1)
	return s.Rows[idx]
}

// Make sure we always have as many Cols as we do cells.
func (s *Sheet) maybeAddCol(cellCount int) {
	if cellCount > s.MaxCol {
		loopCnt := cellCount - s.MaxCol
		currIndex := s.MaxCol + 1
		for i := 0; i < loopCnt; i++ {

			col := &Col{
				style:     NewStyle(),
				Min:       currIndex,
				Max:       currIndex,
				Hidden:    false,
				Collapsed: false}
			s.Cols = append(s.Cols, col)
			currIndex++
		}

		s.MaxCol = cellCount
	}
}

// Make sure we always have as many Cols as we do cells.
func (s *Sheet) Col(idx int) *Col {
	s.maybeAddCol(idx + 1)
	return s.Cols[idx]
}

// Get a Cell by passing it's cartesian coordinates (zero based) as
// row and column integer indexes.
//
// For example:
//
//    cell := sheet.Cell(0,0)
//
// ... would set the variable "cell" to contain a Cell struct
// containing the data from the field "A1" on the spreadsheet.
func (sh *Sheet) Cell(row, col int) *Cell {

	// If the user requests a row beyond what we have, then extend.
	for len(sh.Rows) <= row {
		sh.AddRow()
	}

	r := sh.Rows[row]
	for len(r.Cells) <= col {
		r.AddCell()
	}

	return r.Cells[col]
}

//Set the width of a single column or multiple columns.
func (s *Sheet) SetColWidth(startcol, endcol int, width float64) error {
	if startcol > endcol {
		return fmt.Errorf("Could not set width for range %d-%d: startcol must be less than endcol.", startcol, endcol)
	}
	end := endcol + 1
	s.maybeAddCol(end)
	for ; startcol < end; startcol++ {
		s.Cols[startcol].Width = width
	}

	return nil
}

// When merging cells, the cell may be the 'original' or the 'covered'.
// First, figure out which cells are merge starting points. Then create
// the necessary cells underlying the merge area.
// Then go through all the underlying cells and apply the appropriate
// border, based on the original cell.
func (s *Sheet) handleMerged() {
	merged := make(map[string]*Cell)

	for r, row := range s.Rows {
		for c, cell := range row.Cells {
			if cell.HMerge > 0 || cell.VMerge > 0 {
				coord := GetCellIDStringFromCoords(c, r)
				merged[coord] = cell
			}
		}
	}

	// This loop iterates over all cells that should be merged and applies the correct
	// borders to them depending on their position. If any cells required by the merge
	// are missing, they will be allocated by s.Cell().
	for key, cell := range merged {
		mainstyle := cell.GetStyle()

		top := mainstyle.Border.Top
		left := mainstyle.Border.Left
		right := mainstyle.Border.Right
		bottom := mainstyle.Border.Bottom

		// When merging cells, the upper left cell does not maintain
		// the original borders
		mainstyle.Border.Top = "none"
		mainstyle.Border.Left = "none"
		mainstyle.Border.Right = "none"
		mainstyle.Border.Bottom = "none"

		maincol, mainrow, _ := GetCoordsFromCellIDString(key)
		for rownum := 0; rownum <= cell.VMerge; rownum++ {
			for colnum := 0; colnum <= cell.HMerge; colnum++ {
				tmpcell := s.Cell(mainrow+rownum, maincol+colnum)
				style := tmpcell.GetStyle()
				style.ApplyBorder = true

				if rownum == 0 {
					style.Border.Top = top
				}

				if rownum == (cell.VMerge) {
					style.Border.Bottom = bottom
				}

				if colnum == 0 {
					style.Border.Left = left
				}

				if colnum == (cell.HMerge) {
					style.Border.Right = right
				}
			}
		}
	}
}

// Dump sheet to its XML representation, intended for internal use only
func (s *Sheet) makeXLSXSheet(refTable *RefTable, styles *xlsxStyleSheet) *xlsxWorksheet {
	worksheet := newXlsxWorksheet()
	xSheet := xlsxSheetData{}
	maxRow := 0
	maxCell := 0
	var maxLevelCol, maxLevelRow uint8

	// Scan through the sheet and see if there are any merged cells. If there
	// are, we may need to extend the size of the sheet. There needs to be
	// phantom cells underlying the area covered by the merged cell
	s.handleMerged()

	for index, sheetView := range s.SheetViews {
		if sheetView.Pane != nil {
			worksheet.SheetViews.SheetView[index].Pane = &xlsxPane{
				XSplit:      sheetView.Pane.XSplit,
				YSplit:      sheetView.Pane.YSplit,
				TopLeftCell: sheetView.Pane.TopLeftCell,
				ActivePane:  sheetView.Pane.ActivePane,
				State:       sheetView.Pane.State,
			}

		}
	}

	if s.Selected {
		worksheet.SheetViews.SheetView[0].TabSelected = true
	}

	if s.SheetFormat.DefaultRowHeight != 0 {
		worksheet.SheetFormatPr.DefaultRowHeight = s.SheetFormat.DefaultRowHeight
	}
	worksheet.SheetFormatPr.DefaultColWidth = s.SheetFormat.DefaultColWidth

	colsXfIdList := make([]int, len(s.Cols))
	for c, col := range s.Cols {
		XfId := 0
		if col.Min == 0 {
			col.Min = 1
		}
		if col.Max == 0 {
			col.Max = 1
		}
		style := col.GetStyle()
		//col's style always not nil
		if style != nil {
			xNumFmt := styles.newNumFmt(col.numFmt)
			XfId = handleStyleForXLSX(style, xNumFmt.NumFmtId, styles)
		}
		colsXfIdList[c] = XfId

		var customWidth bool
		if col.Width == 0 {
			col.Width = ColWidth
			customWidth = false

		} else {
			customWidth = true
		}
		if worksheet.Cols == nil {
			worksheet.Cols = &xlsxCols{Col: []xlsxCol{}}
		}
		worksheet.Cols.Col = append(worksheet.Cols.Col,
			xlsxCol{Min: col.Min,
				Max:          col.Max,
				Hidden:       col.Hidden,
				Width:        col.Width,
				CustomWidth:  customWidth,
				Collapsed:    col.Collapsed,
				OutlineLevel: col.OutlineLevel,
				Style:        XfId,
			})

		if col.OutlineLevel > maxLevelCol {
			maxLevelCol = col.OutlineLevel
		}
	}

	for r, row := range s.Rows {
		if r > maxRow {
			maxRow = r
		}
		xRow := xlsxRow{}
		xRow.R = r + 1
		if row.isCustom {
			xRow.CustomHeight = true
			xRow.Ht = fmt.Sprintf("%g", row.Height)
		}
		xRow.OutlineLevel = row.OutlineLevel
		if row.OutlineLevel > maxLevelRow {
			maxLevelRow = row.OutlineLevel
		}
		for c, cell := range row.Cells {
			XfId := colsXfIdList[c]

			// generate NumFmtId and add new NumFmt
			xNumFmt := styles.newNumFmt(cell.NumFmt)

			style := cell.style
			if style != nil {
				XfId = handleStyleForXLSX(style, xNumFmt.NumFmtId, styles)
			} else if len(cell.NumFmt) > 0 && !compareFormatString(s.Cols[c].numFmt, cell.NumFmt) {
				XfId = handleNumFmtIdForXLSX(xNumFmt.NumFmtId, styles)
			}

			if c > maxCell {
				maxCell = c
			}
			xC := xlsxC{
				S: XfId,
				R: GetCellIDStringFromCoords(c, r),
			}
			if cell.formula != "" {
				xC.F = &xlsxF{Content: cell.formula}
			}
			switch cell.cellType {
			case CellTypeInline:
				// Inline strings are turned into shared strings since they are more efficient.
				// This is what Excel does as well.
				fallthrough
			case CellTypeString:
				if len(cell.Value) > 0 {
					xC.V = strconv.Itoa(refTable.AddString(cell.Value))
				}
				xC.T = "s"
			case CellTypeNumeric:
				// Numeric is the default, so the type can be left blank
				xC.V = cell.Value
			case CellTypeBool:
				xC.V = cell.Value
				xC.T = "b"
			case CellTypeError:
				xC.V = cell.Value
				xC.T = "e"
			case CellTypeDate:
				xC.V = cell.Value
				xC.T = "d"
			case CellTypeStringFormula:
				xC.V = cell.Value
				xC.T = "str"
			default:
				panic(errors.New("unknown cell type cannot be marshaled"))
			}

			xRow.C = append(xRow.C, xC)

			if cell.HMerge > 0 || cell.VMerge > 0 {
				// r == rownum, c == colnum
				mc := xlsxMergeCell{}
				start := GetCellIDStringFromCoords(c, r)
				endCol := c + cell.HMerge
				endRow := r + cell.VMerge
				end := GetCellIDStringFromCoords(endCol, endRow)
				mc.Ref = start + ":" + end
				if worksheet.MergeCells == nil {
					worksheet.MergeCells = &xlsxMergeCells{}
				}
				worksheet.MergeCells.Cells = append(worksheet.MergeCells.Cells, mc)
			}
		}
		xSheet.Row = append(xSheet.Row, xRow)
	}

	// Update sheet format with the freshly determined max levels
	s.SheetFormat.OutlineLevelCol = maxLevelCol
	s.SheetFormat.OutlineLevelRow = maxLevelRow
	// .. and then also apply this to the xml worksheet
	worksheet.SheetFormatPr.OutlineLevelCol = s.SheetFormat.OutlineLevelCol
	worksheet.SheetFormatPr.OutlineLevelRow = s.SheetFormat.OutlineLevelRow

	if worksheet.MergeCells != nil {
		worksheet.MergeCells.Count = len(worksheet.MergeCells.Cells)
	}

	if s.AutoFilter != nil {
		worksheet.AutoFilter = &xlsxAutoFilter{Ref: fmt.Sprintf("%v:%v", s.AutoFilter.TopLeftCell, s.AutoFilter.BottomRightCell)}
	}

	worksheet.SheetData = xSheet
	dimension := xlsxDimension{}
	dimension.Ref = "A1:" + GetCellIDStringFromCoords(maxCell, maxRow)
	if dimension.Ref == "A1:A1" {
		dimension.Ref = "A1"
	}
	worksheet.Dimension = dimension
	return worksheet
}

func handleStyleForXLSX(style *Style, NumFmtId int, styles *xlsxStyleSheet) (XfId int) {
	xFont, xFill, xBorder, xCellXf := style.makeXLSXStyleElements()
	fontId := styles.addFont(xFont)
	fillId := styles.addFill(xFill)

	// HACK - adding light grey fill, as in OO and Google
	greyfill := xlsxFill{}
	greyfill.PatternFill.PatternType = "lightGray"
	styles.addFill(greyfill)

	borderId := styles.addBorder(xBorder)
	xCellXf.FontId = fontId
	xCellXf.FillId = fillId
	xCellXf.BorderId = borderId
	xCellXf.NumFmtId = NumFmtId
	// apply the numFmtId when it is not the default cellxf
	if xCellXf.NumFmtId > 0 {
		xCellXf.ApplyNumberFormat = true
	}

	xCellXf.Alignment.Horizontal = style.Alignment.Horizontal
	xCellXf.Alignment.Indent = style.Alignment.Indent
	xCellXf.Alignment.ShrinkToFit = style.Alignment.ShrinkToFit
	xCellXf.Alignment.TextRotation = style.Alignment.TextRotation
	xCellXf.Alignment.Vertical = style.Alignment.Vertical
	xCellXf.Alignment.WrapText = style.Alignment.WrapText

	XfId = styles.addCellXf(xCellXf)
	return
}

func handleNumFmtIdForXLSX(NumFmtId int, styles *xlsxStyleSheet) (XfId int) {
	xCellXf := makeXLSXCellElement()
	xCellXf.NumFmtId = NumFmtId
	if xCellXf.NumFmtId > 0 {
		xCellXf.ApplyNumberFormat = true
	}
	XfId = styles.addCellXf(xCellXf)
	return
}
<|endoftext|>