<commit_before>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package app does all of the work necessary to create a Kubernetes
// APIServer by binding together the API, master and APIServer infrastructure.
// It can be configured and called directly or via the hyperkube cache.
package app

import (
	"strings"
	"time"

	"github.com/golang/glog"
	"github.com/pborman/uuid"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	"k8s.io/kubernetes/federation/cmd/federation-apiserver/app/options"
	"k8s.io/kubernetes/pkg/admission"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/apis/rbac"
	"k8s.io/kubernetes/pkg/apiserver/authenticator"
	authorizerunion "k8s.io/kubernetes/pkg/auth/authorizer/union"
	"k8s.io/kubernetes/pkg/auth/user"
	"k8s.io/kubernetes/pkg/controller/informers"
	"k8s.io/kubernetes/pkg/generated/openapi"
	"k8s.io/kubernetes/pkg/genericapiserver"
	"k8s.io/kubernetes/pkg/genericapiserver/authorizer"
	genericoptions "k8s.io/kubernetes/pkg/genericapiserver/options"
	genericvalidation "k8s.io/kubernetes/pkg/genericapiserver/validation"
	"k8s.io/kubernetes/pkg/registry/cachesize"
	"k8s.io/kubernetes/pkg/registry/generic"
	"k8s.io/kubernetes/pkg/registry/generic/registry"
	"k8s.io/kubernetes/pkg/registry/rbac/clusterrole"
	clusterroleetcd "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/etcd"
	"k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding"
	clusterrolebindingetcd "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/etcd"
	"k8s.io/kubernetes/pkg/registry/rbac/role"
	roleetcd "k8s.io/kubernetes/pkg/registry/rbac/role/etcd"
	"k8s.io/kubernetes/pkg/registry/rbac/rolebinding"
	rolebindingetcd "k8s.io/kubernetes/pkg/registry/rbac/rolebinding/etcd"
	"k8s.io/kubernetes/pkg/routes"
	"k8s.io/kubernetes/pkg/util/wait"
	authenticatorunion "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/union"
)

// NewAPIServerCommand creates a *cobra.Command object with default parameters
func NewAPIServerCommand() *cobra.Command {
	s := options.NewServerRunOptions()
	s.AddFlags(pflag.CommandLine)
	cmd := &cobra.Command{
		Use: "federation-apiserver",
		Long: `The Kubernetes federation API server validates and configures data
for the api objects which include pods, services, replicationcontrollers, and
others. The API Server services REST operations and provides the frontend to the
cluster's shared state through which all other components interact.`,
		Run: func(cmd *cobra.Command, args []string) {
		},
	}
	return cmd
}

// Run runs the specified APIServer.  This should never exit.
func Run(s *options.ServerRunOptions) error {
	genericvalidation.VerifyEtcdServersList(s.ServerRunOptions)
	genericapiserver.DefaultAndValidateRunOptions(s.ServerRunOptions)

	// TODO: register cluster federation resources here.
	resourceConfig := genericapiserver.NewResourceConfig()

	storageGroupsToEncodingVersion, err := s.StorageGroupsToEncodingVersion()
	if err != nil {
		glog.Fatalf("error generating storage version map: %s", err)
	}
	storageFactory, err := genericapiserver.BuildDefaultStorageFactory(
		s.StorageConfig, s.DefaultStorageMediaType, api.Codecs,
		genericapiserver.NewDefaultResourceEncodingConfig(), storageGroupsToEncodingVersion,
		[]unversioned.GroupVersionResource{}, resourceConfig, s.RuntimeConfig)
	if err != nil {
		glog.Fatalf("error in initializing storage factory: %s", err)
	}

	for _, override := range s.EtcdServersOverrides {
		tokens := strings.Split(override, "#")
		if len(tokens) != 2 {
			glog.Errorf("invalid value of etcd server overrides: %s", override)
			continue
		}

		apiresource := strings.Split(tokens[0], "/")
		if len(apiresource) != 2 {
			glog.Errorf("invalid resource definition: %s", tokens[0])
			continue
		}
		group := apiresource[0]
		resource := apiresource[1]
		groupResource := unversioned.GroupResource{Group: group, Resource: resource}

		servers := strings.Split(tokens[1], ";")
		storageFactory.SetEtcdLocation(groupResource, servers)
	}

	apiAuthenticator, err := authenticator.New(authenticator.AuthenticatorConfig{
		Anonymous:         s.AnonymousAuth,
		AnyToken:          s.EnableAnyToken,
		BasicAuthFile:     s.BasicAuthFile,
		ClientCAFile:      s.ClientCAFile,
		TokenAuthFile:     s.TokenAuthFile,
		OIDCIssuerURL:     s.OIDCIssuerURL,
		OIDCClientID:      s.OIDCClientID,
		OIDCCAFile:        s.OIDCCAFile,
		OIDCUsernameClaim: s.OIDCUsernameClaim,
		OIDCGroupsClaim:   s.OIDCGroupsClaim,
		KeystoneURL:       s.KeystoneURL,
	})
	if err != nil {
		glog.Fatalf("Invalid Authentication Config: %v", err)
	}

	authorizationModeNames := strings.Split(s.AuthorizationMode, ",")

	modeEnabled := func(mode string) bool {
		for _, m := range authorizationModeNames {
			if m == mode {
				return true
			}
		}
		return false
	}

	authorizationConfig := authorizer.AuthorizationConfig{
		PolicyFile:                  s.AuthorizationPolicyFile,
		WebhookConfigFile:           s.AuthorizationWebhookConfigFile,
		WebhookCacheAuthorizedTTL:   s.AuthorizationWebhookCacheAuthorizedTTL,
		WebhookCacheUnauthorizedTTL: s.AuthorizationWebhookCacheUnauthorizedTTL,
		RBACSuperUser:               s.AuthorizationRBACSuperUser,
	}
	if modeEnabled(genericoptions.ModeRBAC) {
		mustGetRESTOptions := func(resource string) generic.RESTOptions {
			config, err := storageFactory.NewConfig(rbac.Resource(resource))
			if err != nil {
				glog.Fatalf("Unable to get %s storage: %v", resource, err)
			}
			return generic.RESTOptions{StorageConfig: config, Decorator: generic.UndecoratedStorage, ResourcePrefix: storageFactory.ResourcePrefix(rbac.Resource(resource))}
		}

		// For initial bootstrapping go directly to etcd to avoid privillege escalation check.
		authorizationConfig.RBACRoleRegistry = role.NewRegistry(roleetcd.NewREST(mustGetRESTOptions("roles")))
		authorizationConfig.RBACRoleBindingRegistry = rolebinding.NewRegistry(rolebindingetcd.NewREST(mustGetRESTOptions("rolebindings")))
		authorizationConfig.RBACClusterRoleRegistry = clusterrole.NewRegistry(clusterroleetcd.NewREST(mustGetRESTOptions("clusterroles")))
		authorizationConfig.RBACClusterRoleBindingRegistry = clusterrolebinding.NewRegistry(clusterrolebindingetcd.NewREST(mustGetRESTOptions("clusterrolebindings")))
	}

	apiAuthorizer, err := authorizer.NewAuthorizerFromAuthorizationConfig(authorizationModeNames, authorizationConfig)
	if err != nil {
		glog.Fatalf("Invalid Authorization Config: %v", err)
	}

	admissionControlPluginNames := strings.Split(s.AdmissionControl, ",")
	privilegedLoopbackToken := uuid.NewRandom().String()

	selfClientConfig, err := s.NewSelfClientConfig(privilegedLoopbackToken)
	if err != nil {
		glog.Fatalf("Failed to create clientset: %v", err)
	}
	client, err := s.NewSelfClient(privilegedLoopbackToken)
	if err != nil {
		glog.Errorf("Failed to create clientset: %v", err)
	}

	// TODO(dims): We probably need to add an option "EnableLoopbackToken"
	if apiAuthenticator != nil {
		var uid = uuid.NewRandom().String()
		tokens := make(map[string]*user.DefaultInfo)
		tokens[privilegedLoopbackToken] = &user.DefaultInfo{
			Name:   user.APIServerUser,
			UID:    uid,
			Groups: []string{user.SystemPrivilegedGroup},
		}

		tokenAuthenticator := authenticator.NewAuthenticatorFromTokens(tokens)
		apiAuthenticator = authenticatorunion.New(tokenAuthenticator, apiAuthenticator)

		tokenAuthorizer := authorizer.NewPrivilegedGroups(user.SystemPrivilegedGroup)
		apiAuthorizer = authorizerunion.New(tokenAuthorizer, apiAuthorizer)
	}

	sharedInformers := informers.NewSharedInformerFactory(client, 10*time.Minute)
	pluginInitializer := admission.NewPluginInitializer(sharedInformers)

	admissionController, err := admission.NewFromPlugins(client, admissionControlPluginNames, s.AdmissionControlConfigFile, pluginInitializer)
	if err != nil {
		glog.Fatalf("Failed to initialize plugins: %v", err)
	}
	genericConfig := genericapiserver.NewConfig(s.ServerRunOptions)
	// TODO: Move the following to generic api server as well.
	genericConfig.LoopbackClientConfig = selfClientConfig
	genericConfig.Authenticator = apiAuthenticator
	genericConfig.SupportsBasicAuth = len(s.BasicAuthFile) > 0
	genericConfig.Authorizer = apiAuthorizer
	genericConfig.AuthorizerRBACSuperUser = s.AuthorizationRBACSuperUser
	genericConfig.AdmissionControl = admissionController
	genericConfig.APIResourceConfigSource = storageFactory.APIResourceConfigSource
	genericConfig.MasterServiceNamespace = s.MasterServiceNamespace
	genericConfig.Serializer = api.Codecs
	genericConfig.OpenAPIDefinitions = openapi.OpenAPIDefinitions
	genericConfig.EnableOpenAPISupport = true

	// TODO: Move this to generic api server (Need to move the command line flag).
	if s.EnableWatchCache {
		cachesize.InitializeWatchCacheSizes(s.TargetRAMMB)
		cachesize.SetWatchCacheSizes(s.WatchCacheSizes)
	}

	m, err := genericConfig.Complete().New()
	if err != nil {
		return err
	}

	routes.UIRedirect{}.Install(m.Mux, m.HandlerContainer)
	routes.Logs{}.Install(m.Mux, m.HandlerContainer)

	restOptionsFactory := restOptionsFactory{
		storageFactory:          storageFactory,
		deleteCollectionWorkers: s.DeleteCollectionWorkers,
	}
	if s.EnableWatchCache {
		restOptionsFactory.storageDecorator = registry.StorageWithCacher
	} else {
		restOptionsFactory.storageDecorator = generic.UndecoratedStorage
	}

	installFederationAPIs(m, restOptionsFactory)
	installCoreAPIs(s, m, restOptionsFactory)
	installExtensionsAPIs(m, restOptionsFactory)

	sharedInformers.Start(wait.NeverStop)
	m.Run(s.ServerRunOptions)
	return nil
}

type restOptionsFactory struct {
	storageFactory          genericapiserver.StorageFactory
	storageDecorator        generic.StorageDecorator
	deleteCollectionWorkers int
}

func (f restOptionsFactory) NewFor(resource unversioned.GroupResource) generic.RESTOptions {
	config, err := f.storageFactory.NewConfig(resource)
	if err != nil {
		glog.Fatalf("Unable to find storage config for %v, due to %v", resource, err.Error())
	}
	return generic.RESTOptions{
		StorageConfig:           config,
		Decorator:               f.storageDecorator,
		DeleteCollectionWorkers: f.deleteCollectionWorkers,
		ResourcePrefix:          f.storageFactory.ResourcePrefix(resource),
	}
}
<commit_msg>stop plumbing options to start<commit_after>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package app does all of the work necessary to create a Kubernetes
// APIServer by binding together the API, master and APIServer infrastructure.
// It can be configured and called directly or via the hyperkube cache.
package app

import (
	"strings"
	"time"

	"github.com/golang/glog"
	"github.com/pborman/uuid"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	"k8s.io/kubernetes/federation/cmd/federation-apiserver/app/options"
	"k8s.io/kubernetes/pkg/admission"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/apis/rbac"
	"k8s.io/kubernetes/pkg/apiserver/authenticator"
	authorizerunion "k8s.io/kubernetes/pkg/auth/authorizer/union"
	"k8s.io/kubernetes/pkg/auth/user"
	"k8s.io/kubernetes/pkg/controller/informers"
	"k8s.io/kubernetes/pkg/generated/openapi"
	"k8s.io/kubernetes/pkg/genericapiserver"
	"k8s.io/kubernetes/pkg/genericapiserver/authorizer"
	genericoptions "k8s.io/kubernetes/pkg/genericapiserver/options"
	genericvalidation "k8s.io/kubernetes/pkg/genericapiserver/validation"
	"k8s.io/kubernetes/pkg/registry/cachesize"
	"k8s.io/kubernetes/pkg/registry/generic"
	"k8s.io/kubernetes/pkg/registry/generic/registry"
	"k8s.io/kubernetes/pkg/registry/rbac/clusterrole"
	clusterroleetcd "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/etcd"
	"k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding"
	clusterrolebindingetcd "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/etcd"
	"k8s.io/kubernetes/pkg/registry/rbac/role"
	roleetcd "k8s.io/kubernetes/pkg/registry/rbac/role/etcd"
	"k8s.io/kubernetes/pkg/registry/rbac/rolebinding"
	rolebindingetcd "k8s.io/kubernetes/pkg/registry/rbac/rolebinding/etcd"
	"k8s.io/kubernetes/pkg/routes"
	"k8s.io/kubernetes/pkg/util/wait"
	authenticatorunion "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/union"
)

// NewAPIServerCommand creates a *cobra.Command object with default parameters
func NewAPIServerCommand() *cobra.Command {
	s := options.NewServerRunOptions()
	s.AddFlags(pflag.CommandLine)
	cmd := &cobra.Command{
		Use: "federation-apiserver",
		Long: `The Kubernetes federation API server validates and configures data
for the api objects which include pods, services, replicationcontrollers, and
others. The API Server services REST operations and provides the frontend to the
cluster's shared state through which all other components interact.`,
		Run: func(cmd *cobra.Command, args []string) {
		},
	}
	return cmd
}

// Run runs the specified APIServer.  This should never exit.
func Run(s *options.ServerRunOptions) error {
	genericvalidation.VerifyEtcdServersList(s.ServerRunOptions)
	genericapiserver.DefaultAndValidateRunOptions(s.ServerRunOptions)

	// TODO: register cluster federation resources here.
	resourceConfig := genericapiserver.NewResourceConfig()

	storageGroupsToEncodingVersion, err := s.StorageGroupsToEncodingVersion()
	if err != nil {
		glog.Fatalf("error generating storage version map: %s", err)
	}
	storageFactory, err := genericapiserver.BuildDefaultStorageFactory(
		s.StorageConfig, s.DefaultStorageMediaType, api.Codecs,
		genericapiserver.NewDefaultResourceEncodingConfig(), storageGroupsToEncodingVersion,
		[]unversioned.GroupVersionResource{}, resourceConfig, s.RuntimeConfig)
	if err != nil {
		glog.Fatalf("error in initializing storage factory: %s", err)
	}

	for _, override := range s.EtcdServersOverrides {
		tokens := strings.Split(override, "#")
		if len(tokens) != 2 {
			glog.Errorf("invalid value of etcd server overrides: %s", override)
			continue
		}

		apiresource := strings.Split(tokens[0], "/")
		if len(apiresource) != 2 {
			glog.Errorf("invalid resource definition: %s", tokens[0])
			continue
		}
		group := apiresource[0]
		resource := apiresource[1]
		groupResource := unversioned.GroupResource{Group: group, Resource: resource}

		servers := strings.Split(tokens[1], ";")
		storageFactory.SetEtcdLocation(groupResource, servers)
	}

	apiAuthenticator, err := authenticator.New(authenticator.AuthenticatorConfig{
		Anonymous:         s.AnonymousAuth,
		AnyToken:          s.EnableAnyToken,
		BasicAuthFile:     s.BasicAuthFile,
		ClientCAFile:      s.ClientCAFile,
		TokenAuthFile:     s.TokenAuthFile,
		OIDCIssuerURL:     s.OIDCIssuerURL,
		OIDCClientID:      s.OIDCClientID,
		OIDCCAFile:        s.OIDCCAFile,
		OIDCUsernameClaim: s.OIDCUsernameClaim,
		OIDCGroupsClaim:   s.OIDCGroupsClaim,
		KeystoneURL:       s.KeystoneURL,
	})
	if err != nil {
		glog.Fatalf("Invalid Authentication Config: %v", err)
	}

	authorizationModeNames := strings.Split(s.AuthorizationMode, ",")

	modeEnabled := func(mode string) bool {
		for _, m := range authorizationModeNames {
			if m == mode {
				return true
			}
		}
		return false
	}

	authorizationConfig := authorizer.AuthorizationConfig{
		PolicyFile:                  s.AuthorizationPolicyFile,
		WebhookConfigFile:           s.AuthorizationWebhookConfigFile,
		WebhookCacheAuthorizedTTL:   s.AuthorizationWebhookCacheAuthorizedTTL,
		WebhookCacheUnauthorizedTTL: s.AuthorizationWebhookCacheUnauthorizedTTL,
		RBACSuperUser:               s.AuthorizationRBACSuperUser,
	}
	if modeEnabled(genericoptions.ModeRBAC) {
		mustGetRESTOptions := func(resource string) generic.RESTOptions {
			config, err := storageFactory.NewConfig(rbac.Resource(resource))
			if err != nil {
				glog.Fatalf("Unable to get %s storage: %v", resource, err)
			}
			return generic.RESTOptions{StorageConfig: config, Decorator: generic.UndecoratedStorage, ResourcePrefix: storageFactory.ResourcePrefix(rbac.Resource(resource))}
		}

		// For initial bootstrapping go directly to etcd to avoid privillege escalation check.
		authorizationConfig.RBACRoleRegistry = role.NewRegistry(roleetcd.NewREST(mustGetRESTOptions("roles")))
		authorizationConfig.RBACRoleBindingRegistry = rolebinding.NewRegistry(rolebindingetcd.NewREST(mustGetRESTOptions("rolebindings")))
		authorizationConfig.RBACClusterRoleRegistry = clusterrole.NewRegistry(clusterroleetcd.NewREST(mustGetRESTOptions("clusterroles")))
		authorizationConfig.RBACClusterRoleBindingRegistry = clusterrolebinding.NewRegistry(clusterrolebindingetcd.NewREST(mustGetRESTOptions("clusterrolebindings")))
	}

	apiAuthorizer, err := authorizer.NewAuthorizerFromAuthorizationConfig(authorizationModeNames, authorizationConfig)
	if err != nil {
		glog.Fatalf("Invalid Authorization Config: %v", err)
	}

	admissionControlPluginNames := strings.Split(s.AdmissionControl, ",")
	privilegedLoopbackToken := uuid.NewRandom().String()

	selfClientConfig, err := s.NewSelfClientConfig(privilegedLoopbackToken)
	if err != nil {
		glog.Fatalf("Failed to create clientset: %v", err)
	}
	client, err := s.NewSelfClient(privilegedLoopbackToken)
	if err != nil {
		glog.Errorf("Failed to create clientset: %v", err)
	}

	// TODO(dims): We probably need to add an option "EnableLoopbackToken"
	if apiAuthenticator != nil {
		var uid = uuid.NewRandom().String()
		tokens := make(map[string]*user.DefaultInfo)
		tokens[privilegedLoopbackToken] = &user.DefaultInfo{
			Name:   user.APIServerUser,
			UID:    uid,
			Groups: []string{user.SystemPrivilegedGroup},
		}

		tokenAuthenticator := authenticator.NewAuthenticatorFromTokens(tokens)
		apiAuthenticator = authenticatorunion.New(tokenAuthenticator, apiAuthenticator)

		tokenAuthorizer := authorizer.NewPrivilegedGroups(user.SystemPrivilegedGroup)
		apiAuthorizer = authorizerunion.New(tokenAuthorizer, apiAuthorizer)
	}

	sharedInformers := informers.NewSharedInformerFactory(client, 10*time.Minute)
	pluginInitializer := admission.NewPluginInitializer(sharedInformers)

	admissionController, err := admission.NewFromPlugins(client, admissionControlPluginNames, s.AdmissionControlConfigFile, pluginInitializer)
	if err != nil {
		glog.Fatalf("Failed to initialize plugins: %v", err)
	}
	genericConfig := genericapiserver.NewConfig(s.ServerRunOptions)
	// TODO: Move the following to generic api server as well.
	genericConfig.LoopbackClientConfig = selfClientConfig
	genericConfig.Authenticator = apiAuthenticator
	genericConfig.SupportsBasicAuth = len(s.BasicAuthFile) > 0
	genericConfig.Authorizer = apiAuthorizer
	genericConfig.AuthorizerRBACSuperUser = s.AuthorizationRBACSuperUser
	genericConfig.AdmissionControl = admissionController
	genericConfig.APIResourceConfigSource = storageFactory.APIResourceConfigSource
	genericConfig.MasterServiceNamespace = s.MasterServiceNamespace
	genericConfig.Serializer = api.Codecs
	genericConfig.OpenAPIDefinitions = openapi.OpenAPIDefinitions
	genericConfig.EnableOpenAPISupport = true

	// TODO: Move this to generic api server (Need to move the command line flag).
	if s.EnableWatchCache {
		cachesize.InitializeWatchCacheSizes(s.TargetRAMMB)
		cachesize.SetWatchCacheSizes(s.WatchCacheSizes)
	}

	m, err := genericConfig.Complete().New()
	if err != nil {
		return err
	}

	routes.UIRedirect{}.Install(m.Mux, m.HandlerContainer)
	routes.Logs{}.Install(m.Mux, m.HandlerContainer)

	restOptionsFactory := restOptionsFactory{
		storageFactory:          storageFactory,
		deleteCollectionWorkers: s.DeleteCollectionWorkers,
	}
	if s.EnableWatchCache {
		restOptionsFactory.storageDecorator = registry.StorageWithCacher
	} else {
		restOptionsFactory.storageDecorator = generic.UndecoratedStorage
	}

	installFederationAPIs(m, restOptionsFactory)
	installCoreAPIs(s, m, restOptionsFactory)
	installExtensionsAPIs(m, restOptionsFactory)

	sharedInformers.Start(wait.NeverStop)
	m.Run()
	return nil
}

type restOptionsFactory struct {
	storageFactory          genericapiserver.StorageFactory
	storageDecorator        generic.StorageDecorator
	deleteCollectionWorkers int
}

func (f restOptionsFactory) NewFor(resource unversioned.GroupResource) generic.RESTOptions {
	config, err := f.storageFactory.NewConfig(resource)
	if err != nil {
		glog.Fatalf("Unable to find storage config for %v, due to %v", resource, err.Error())
	}
	return generic.RESTOptions{
		StorageConfig:           config,
		Decorator:               f.storageDecorator,
		DeleteCollectionWorkers: f.deleteCollectionWorkers,
		ResourcePrefix:          f.storageFactory.ResourcePrefix(resource),
	}
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/koding/bongo"
)

type Channel struct {
	// unique identifier of the channel
	Id int64 `json:"id"`

	// Name of the channel
	Name string `json:"name"                   sql:"NOT NULL;TYPE:VARCHAR(200);"`

	// Creator of the channel
	CreatorId int64 `json:"creatorId"          sql:"NOT NULL"`

	// Name of the group which channel is belong to
	GroupName string `json:"groupName"         sql:"NOT NULL;TYPE:VARCHAR(200);"`

	// Purpose of the channel
	Purpose string `json:"purpose"`

	// Secret key of the channel for event propagation purposes
	// we can put this key into another table?
	SecretKey string `json:"secretKey"`

	// Type of the channel
	TypeConstant string `json:"typeConstant"   sql:"NOT NULL"`

	// Privacy constant of the channel
	Privacy string `json:"privacy"             sql:"NOT NULL"`

	// Creation date of the channel
	CreatedAt time.Time `json:"createdAt"      sql:"NOT NULL"`

	// Modification date of the channel
	UpdatedAt time.Time `json:"updatedAt"      sql:"NOT NULL"`
}

// to-do check for allowed channels
const (
	// TYPES
	Channel_TYPE_GROUP         = "group"
	Channel_TYPE_TOPIC         = "topic"
	Channel_TYPE_FOLLOWINGFEED = "followingfeed"
	Channel_TYPE_FOLLOWERS     = "followers"
	Channel_TYPE_CHAT          = "chat"
	// Privacy
	Channel_TYPE_PUBLIC  = "public"
	Channel_TYPE_PRIVATE = "private"
	// Koding Group Name
	Channel_KODING_NAME = "koding"
)

func NewChannel() *Channel {
	return &Channel{
		Name:         "koding",
		CreatorId:    123,
		GroupName:    Channel_KODING_NAME,
		Purpose:      "string",
		SecretKey:    "string",
		TypeConstant: Channel_TYPE_GROUP,
		Privacy:      Channel_TYPE_PRIVATE,
	}
}

func (c *Channel) BeforeCreate() {
	c.CreatedAt = time.Now()
	c.UpdatedAt = time.Now()
}

func (c *Channel) BeforeUpdate() {
	c.UpdatedAt = time.Now()
}

func (c *Channel) GetId() int64 {
	return c.Id
}

func (c *Channel) TableName() string {
	return "channel"
}

func (c *Channel) Fetch() error {
	return bongo.B.Fetch(c)
}

func (c *Channel) AfterCreate() {
	bongo.B.AfterCreate(c)
}

func (c *Channel) AfterUpdate() {
	bongo.B.AfterUpdate(c)
}

func (c *Channel) AfterDelete() {
	bongo.B.AfterDelete(c)
}

func (c *Channel) Update() error {
	if c.Name == "" || c.GroupName == "" {
		return fmt.Errorf("Validation failed %s - %s", c.Name, c.GroupName)
	}

	return bongo.B.Update(c)
}

func (c *Channel) Create() error {
	if c.Name == "" || c.GroupName == "" || c.TypeConstant == "" {
		return fmt.Errorf("Validation failed %s - %s", c.Name, c.GroupName)
	}

	// golang returns -1 if item not in the string
	if strings.Index(c.Name, " ") > -1 {
		return fmt.Errorf("Channel name %q has empty space in it", c.Name)
	}

	selector := map[string]interface{}{
		"name":       c.Name,
		"group_name": c.GroupName,
	}

	// if err is nil
	// it means we already have that channel
	err := c.One(selector)
	if err == nil {
		return fmt.Errorf("Channel %s is already created before for %s group", c.Name, c.GroupName)
	}

	if err != gorm.RecordNotFound {
		return err
	}

	return bongo.B.Create(c)
}

func (c *Channel) Delete() error {
	return bongo.B.Delete(c)
}

func (c *Channel) One(selector map[string]interface{}) error {
	return bongo.B.One(c, c, selector)
}

func (c *Channel) FetchByIds(ids []int64) ([]Channel, error) {
	var channels []Channel

	if len(ids) == 0 {
		return channels, nil
	}

	if err := bongo.B.FetchByIds(c, &channels, ids); err != nil {
		return nil, err
	}
	return channels, nil
}

func (c *Channel) AddParticipant(participantId int64) (*ChannelParticipant, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cp := NewChannelParticipant()
	cp.ChannelId = c.Id
	cp.AccountId = participantId

	err := cp.FetchParticipant()
	if err != nil && err != gorm.RecordNotFound {
		return nil, err
	}

	// if we have this record in DB
	if cp.Id != 0 {
		// if status is not active
		if cp.Status == ChannelParticipant_STATUS_ACTIVE {
			return nil, errors.New(fmt.Sprintf("Account %s is already a participant of channel %s", cp.AccountId, cp.ChannelId))
		}
		cp.Status = ChannelParticipant_STATUS_ACTIVE
		if err := cp.Update(); err != nil {
			return nil, err
		}
		return cp, nil
	}

	cp.Status = ChannelParticipant_STATUS_ACTIVE

	if err := cp.Create(); err != nil {
		return nil, err
	}

	return cp, nil
}

func (c *Channel) RemoveParticipant(participantId int64) error {
	if c.Id == 0 {
		return errors.New("Channel Id is not set")
	}

	cp := NewChannelParticipant()
	cp.ChannelId = c.Id
	cp.AccountId = participantId

	err := cp.FetchParticipant()
	// if user is not in this channel, do nothing
	if err == gorm.RecordNotFound {
		return nil
	}

	if err != nil {
		return err
	}

	if cp.Status == ChannelParticipant_STATUS_LEFT {
		return nil
	}

	cp.Status = ChannelParticipant_STATUS_LEFT
	if err := cp.Update(); err != nil {
		return err
	}

	return nil
}

func (c *Channel) FetchParticipantIds() ([]int64, error) {
	var participantIds []int64

	if c.Id == 0 {
		return participantIds, errors.New("Channel Id is not set")
	}

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id": c.Id,
			"status":     ChannelParticipant_STATUS_ACTIVE,
		},
		Pluck: "account_id",
	}

	cp := NewChannelParticipant()
	err := cp.Some(&participantIds, query)
	if err != nil {
		return nil, err
	}

	return participantIds, nil
}

func (c *Channel) AddMessage(messageId int64) (*ChannelMessageList, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cml := NewChannelMessageList()
	cml.ChannelId = c.Id
	cml.MessageId = messageId

	if err := cml.Create(); err != nil {
		return nil, err
	}

	return cml, nil
}

func (c *Channel) List(q *Query) ([]Channel, error) {

	var channels []Channel

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"group_name": q.GroupName,
		},
	}

	err := bongo.B.Some(c, &channels, query)
	if err != nil {
		return nil, err
	}

	return channels, nil
}
<commit_msg>Social: change status to status constant<commit_after>package models

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/koding/bongo"
)

type Channel struct {
	// unique identifier of the channel
	Id int64 `json:"id"`

	// Name of the channel
	Name string `json:"name"                         sql:"NOT NULL;TYPE:VARCHAR(200);"`

	// Creator of the channel
	CreatorId int64 `json:"creatorId"                sql:"NOT NULL"`

	// Name of the group which channel is belong to
	GroupName string `json:"groupName"               sql:"NOT NULL;TYPE:VARCHAR(200);"`

	// Purpose of the channel
	Purpose string `json:"purpose"`

	// Secret key of the channel for event propagation purposes
	// we can put this key into another table?
	SecretKey string `json:"secretKey"`

	// Type of the channel
	TypeConstant string `json:"typeConstant"         sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// Privacy constant of the channel
	PrivacyConstant string `json:"privacyConstant"   sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// Creation date of the channel
	CreatedAt time.Time `json:"createdAt"            sql:"NOT NULL"`

	// Modification date of the channel
	UpdatedAt time.Time `json:"updatedAt"            sql:"NOT NULL"`
}

// to-do check for allowed channels
const (
	// TYPES
	Channel_TYPE_GROUP         = "group"
	Channel_TYPE_TOPIC         = "topic"
	Channel_TYPE_FOLLOWINGFEED = "followingfeed"
	Channel_TYPE_FOLLOWERS     = "followers"
	Channel_TYPE_CHAT          = "chat"
	// Privacy
	Channel_TYPE_PUBLIC  = "public"
	Channel_TYPE_PRIVATE = "private"
	// Koding Group Name
	Channel_KODING_NAME = "koding"
)

func NewChannel() *Channel {
	return &Channel{
		Name:            "koding",
		CreatorId:       123,
		GroupName:       Channel_KODING_NAME,
		Purpose:         "string",
		SecretKey:       "string",
		TypeConstant:    Channel_TYPE_GROUP,
		PrivacyConstant: Channel_TYPE_PRIVATE,
	}
}

func (c *Channel) BeforeCreate() {
	c.CreatedAt = time.Now()
	c.UpdatedAt = time.Now()
}

func (c *Channel) BeforeUpdate() {
	c.UpdatedAt = time.Now()
}

func (c *Channel) GetId() int64 {
	return c.Id
}

func (c *Channel) TableName() string {
	return "channel"
}

func (c *Channel) Fetch() error {
	return bongo.B.Fetch(c)
}

func (c *Channel) AfterCreate() {
	bongo.B.AfterCreate(c)
}

func (c *Channel) AfterUpdate() {
	bongo.B.AfterUpdate(c)
}

func (c *Channel) AfterDelete() {
	bongo.B.AfterDelete(c)
}

func (c *Channel) Update() error {
	if c.Name == "" || c.GroupName == "" {
		return fmt.Errorf("Validation failed %s - %s", c.Name, c.GroupName)
	}

	return bongo.B.Update(c)
}

func (c *Channel) Create() error {
	if c.Name == "" || c.GroupName == "" || c.TypeConstant == "" {
		return fmt.Errorf("Validation failed %s - %s", c.Name, c.GroupName)
	}

	// golang returns -1 if item not in the string
	if strings.Index(c.Name, " ") > -1 {
		return fmt.Errorf("Channel name %q has empty space in it", c.Name)
	}

	selector := map[string]interface{}{
		"name":       c.Name,
		"group_name": c.GroupName,
	}

	// if err is nil
	// it means we already have that channel
	err := c.One(selector)
	if err == nil {
		return fmt.Errorf("Channel %s is already created before for %s group", c.Name, c.GroupName)
	}

	if err != gorm.RecordNotFound {
		return err
	}

	return bongo.B.Create(c)
}

func (c *Channel) Delete() error {
	return bongo.B.Delete(c)
}

func (c *Channel) One(selector map[string]interface{}) error {
	return bongo.B.One(c, c, selector)
}

func (c *Channel) FetchByIds(ids []int64) ([]Channel, error) {
	var channels []Channel

	if len(ids) == 0 {
		return channels, nil
	}

	if err := bongo.B.FetchByIds(c, &channels, ids); err != nil {
		return nil, err
	}
	return channels, nil
}

func (c *Channel) AddParticipant(participantId int64) (*ChannelParticipant, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cp := NewChannelParticipant()
	cp.ChannelId = c.Id
	cp.AccountId = participantId

	err := cp.FetchParticipant()
	if err != nil && err != gorm.RecordNotFound {
		return nil, err
	}

	// if we have this record in DB
	if cp.Id != 0 {
		// if status is not active
		if cp.StatusConstant == ChannelParticipant_STATUS_ACTIVE {
			return nil, errors.New(fmt.Sprintf("Account %s is already a participant of channel %s", cp.AccountId, cp.ChannelId))
		}
		cp.StatusConstant = ChannelParticipant_STATUS_ACTIVE
		if err := cp.Update(); err != nil {
			return nil, err
		}
		return cp, nil
	}

	cp.StatusConstant = ChannelParticipant_STATUS_ACTIVE

	if err := cp.Create(); err != nil {
		return nil, err
	}

	return cp, nil
}

func (c *Channel) RemoveParticipant(participantId int64) error {
	if c.Id == 0 {
		return errors.New("Channel Id is not set")
	}

	cp := NewChannelParticipant()
	cp.ChannelId = c.Id
	cp.AccountId = participantId

	err := cp.FetchParticipant()
	// if user is not in this channel, do nothing
	if err == gorm.RecordNotFound {
		return nil
	}

	if err != nil {
		return err
	}

	if cp.StatusConstant == ChannelParticipant_STATUS_LEFT {
		return nil
	}

	cp.StatusConstant = ChannelParticipant_STATUS_LEFT
	if err := cp.Update(); err != nil {
		return err
	}

	return nil
}

func (c *Channel) FetchParticipantIds() ([]int64, error) {
	var participantIds []int64

	if c.Id == 0 {
		return participantIds, errors.New("Channel Id is not set")
	}

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id":      c.Id,
			"status_constant": ChannelParticipant_STATUS_ACTIVE,
		},
		Pluck: "account_id",
	}

	cp := NewChannelParticipant()
	err := cp.Some(&participantIds, query)
	if err != nil {
		return nil, err
	}

	return participantIds, nil
}

func (c *Channel) AddMessage(messageId int64) (*ChannelMessageList, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cml := NewChannelMessageList()
	cml.ChannelId = c.Id
	cml.MessageId = messageId

	if err := cml.Create(); err != nil {
		return nil, err
	}

	return cml, nil
}

func (c *Channel) List(q *Query) ([]Channel, error) {

	var channels []Channel

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"group_name": q.GroupName,
		},
	}

	err := bongo.B.Some(c, &channels, query)
	if err != nil {
		return nil, err
	}

	return channels, nil
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/koding/bongo"
)

type Channel struct {
	// unique identifier of the channel
	Id int64 `json:"id"`

	// Name of the channel
	Name string `json:"name"                         sql:"NOT NULL;TYPE:VARCHAR(200);"`

	// Creator of the channel
	CreatorId int64 `json:"creatorId"                sql:"NOT NULL"`

	// Name of the group which channel is belong to
	GroupName string `json:"groupName"               sql:"NOT NULL;TYPE:VARCHAR(200);"`

	// Purpose of the channel
	Purpose string `json:"purpose"`

	// Secret key of the channel for event propagation purposes
	// we can put this key into another table?
	SecretKey string `json:"-"`

	// Type of the channel
	TypeConstant string `json:"typeConstant"         sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// Privacy constant of the channel
	PrivacyConstant string `json:"privacyConstant"   sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// Creation date of the channel
	CreatedAt time.Time `json:"createdAt"            sql:"NOT NULL"`

	// Modification date of the channel
	UpdatedAt time.Time `json:"updatedAt"            sql:"NOT NULL"`

	// Deletion date of the channel
	DeletedAt time.Time `json:"deletedAt"`
}

// to-do check for allowed channels
const (
	// TYPES
	Channel_TYPE_GROUP           = "group"
	Channel_TYPE_TOPIC           = "topic"
	Channel_TYPE_FOLLOWINGFEED   = "followingfeed"
	Channel_TYPE_FOLLOWERS       = "followers"
	Channel_TYPE_CHAT            = "chat"
	Channel_TYPE_PINNED_ACTIVITY = "pinnedActivity"
	Channel_TYPE_PRIVATE_MESSAGE = "privateMessage"
	Channel_TYPE_DEFAULT         = "default"
	// Privacy
	Channel_PRIVACY_PUBLIC  = "public"
	Channel_PRIVACY_PRIVATE = "private"
	// Koding Group Name
	Channel_KODING_NAME = "koding"
)

func NewChannel() *Channel {
	return &Channel{
		Name:            "Channel" + RandomName(),
		CreatorId:       0,
		GroupName:       Channel_KODING_NAME,
		Purpose:         "",
		SecretKey:       "",
		TypeConstant:    Channel_TYPE_DEFAULT,
		PrivacyConstant: Channel_PRIVACY_PRIVATE,
	}
}

func NewPrivateMessageChannel(creatorId int64, groupName string) *Channel {
	c := NewChannel()
	c.GroupName = groupName
	c.CreatorId = creatorId
	c.Name = RandomName()
	c.TypeConstant = Channel_TYPE_PRIVATE_MESSAGE
	c.PrivacyConstant = Channel_PRIVACY_PRIVATE
	c.Purpose = ""
	return c
}

func (c *Channel) BeforeCreate() {
	c.CreatedAt = time.Now().UTC()
	c.UpdatedAt = time.Now().UTC()
	c.DeletedAt = ZeroDate()
}

func (c *Channel) BeforeUpdate() {
	c.UpdatedAt = time.Now()
}

func (c *Channel) GetId() int64 {
	return c.Id
}

func (c Channel) TableName() string {
	return "api.channel"
}

func (c *Channel) AfterCreate() {
	bongo.B.AfterCreate(c)
}

func (c *Channel) AfterUpdate() {
	bongo.B.AfterUpdate(c)
}

func (c *Channel) AfterDelete() {
	bongo.B.AfterDelete(c)
}

func (c *Channel) Update() error {
	if c.Name == "" || c.GroupName == "" {
		return fmt.Errorf("Validation failed %s - %s", c.Name, c.GroupName)
	}

	return bongo.B.Update(c)
}

func (c *Channel) Create() error {
	if c.Name == "" || c.GroupName == "" || c.TypeConstant == "" {
		return fmt.Errorf("Validation failed %s - %s -%s", c.Name, c.GroupName, c.TypeConstant)
	}

	// golang returns -1 if item not in the string
	if strings.Index(c.Name, " ") > -1 {
		return fmt.Errorf("Channel name %q has empty space in it", c.Name)
	}

	if c.TypeConstant == Channel_TYPE_GROUP /* we can add more types here */ {
		selector := map[string]interface{}{
			"group_name":    c.GroupName,
			"type_constant": c.TypeConstant,
		}

		// if err is nil
		// it means we already have that channel
		err := c.One(bongo.NewQS(selector))
		if err == nil {
			return nil
			// return fmt.Errorf("%s typed channel is already created before for %s group", c.TypeConstant, c.GroupName)
		}

		if err != gorm.RecordNotFound {
			return err
		}

	}

	return bongo.B.Create(c)
}

func (c *Channel) Delete() error {
	return bongo.B.Delete(c)
}

func (c *Channel) ById(id int64) error {
	return bongo.B.ById(c, id)
}

func (c *Channel) One(q *bongo.Query) error {
	return bongo.B.One(c, c, q)
}

func (c *Channel) Some(data interface{}, q *bongo.Query) error {
	return bongo.B.Some(c, data, q)
}

func (c *Channel) FetchByIds(ids []int64) ([]Channel, error) {
	var channels []Channel

	if len(ids) == 0 {
		return channels, nil
	}

	if err := bongo.B.FetchByIds(c, &channels, ids); err != nil {
		return nil, err
	}
	return channels, nil
}

func (c *Channel) AddParticipant(participantId int64) (*ChannelParticipant, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cp := NewChannelParticipant()
	cp.ChannelId = c.Id
	cp.AccountId = participantId

	err := cp.FetchParticipant()
	if err != nil && err != gorm.RecordNotFound {
		return nil, err
	}

	// if we have this record in DB
	if cp.Id != 0 {
		// if status is not active
		if cp.StatusConstant == ChannelParticipant_STATUS_ACTIVE {
			return nil, errors.New(fmt.Sprintf("Account %d is already a participant of channel %d", cp.AccountId, cp.ChannelId))
		}
		cp.StatusConstant = ChannelParticipant_STATUS_ACTIVE
		if err := cp.Update(); err != nil {
			return nil, err
		}
		return cp, nil
	}

	cp.StatusConstant = ChannelParticipant_STATUS_ACTIVE

	if err := cp.Create(); err != nil {
		return nil, err
	}

	return cp, nil
}

func (c *Channel) RemoveParticipant(participantId int64) error {
	if c.Id == 0 {
		return errors.New("Channel Id is not set")
	}

	cp := NewChannelParticipant()
	cp.ChannelId = c.Id
	cp.AccountId = participantId

	err := cp.FetchParticipant()
	// if user is not in this channel, do nothing
	if err == gorm.RecordNotFound {
		return nil
	}

	if err != nil {
		return err
	}

	if cp.StatusConstant == ChannelParticipant_STATUS_LEFT {
		return nil
	}

	cp.StatusConstant = ChannelParticipant_STATUS_LEFT
	if err := cp.Update(); err != nil {
		return err
	}

	return nil
}

func (c *Channel) FetchParticipantIds() ([]int64, error) {
	var participantIds []int64

	if c.Id == 0 {
		return participantIds, errors.New("Channel Id is not set")
	}

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id":      c.Id,
			"status_constant": ChannelParticipant_STATUS_ACTIVE,
		},
		Pluck: "account_id",
	}

	cp := NewChannelParticipant()
	err := cp.Some(&participantIds, query)
	if err != nil {
		return nil, err
	}

	return participantIds, nil
}

func (c *Channel) AddMessage(messageId int64) (*ChannelMessageList, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cml := NewChannelMessageList()

	selector := map[string]interface{}{
		"channel_id": c.Id,
		"message_id": messageId,
	}
	err := cml.One(bongo.NewQS(selector))
	if err == nil {
		return nil, errors.New("Message is already in the channel")
	}

	// silence record not found err
	if err != gorm.RecordNotFound {
		return nil, err
	}

	cml.ChannelId = c.Id
	cml.MessageId = messageId

	if err := cml.Create(); err != nil {
		return nil, err
	}

	return cml, nil
}

func (c *Channel) RemoveMessage(messageId int64) (*ChannelMessageList, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cml := NewChannelMessageList()
	selector := map[string]interface{}{
		"channel_id": c.Id,
		"message_id": messageId,
	}
	err := cml.One(bongo.NewQS(selector))
	// one returns error when record not found case
	// but we dont care if it is not there tho
	if err != nil {
		return nil, err
	}

	if err := cml.Delete(); err != nil {
		return nil, err
	}

	return cml, nil
}

func (c *Channel) Search(q *Query) ([]Channel, error) {

	if q.GroupName == "" {
		return nil, fmt.Errorf("Query doesnt have any Group info %+v", q)
	}

	var channels []Channel

	query := bongo.B.DB.Table(c.TableName()).Limit(q.Limit)

	query = query.Where("type_constant = ?", Channel_TYPE_TOPIC)
	query = query.Where("group_name = ?", q.GroupName)
	query = query.Where("name like ?", q.Name+"%")

	if err := query.Find(&channels).Error; err != nil {
		return nil, err
	}

	return channels, nil
}

func (c *Channel) List(q *Query) ([]Channel, error) {

	if q.GroupName == "" {
		return nil, fmt.Errorf("Query doesnt have any Group info %+v", q)
	}

	var channels []Channel

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"group_name": q.GroupName,
		},
	}

	if q.Type != "" {
		query.Selector["type_constant"] = q.Type
	}

	err := c.Some(&channels, query)
	if err != nil {
		return nil, err
	}

	return channels, nil
}

func (c *Channel) FetchLastMessage() (*ChannelMessage, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cml := NewChannelMessageList()
	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id": c.Id,
		},
		Sort: map[string]string{
			"added_at": "DESC",
		},
		Limit: 1,
		Pluck: "message_id",
	}

	var messageIds []int64
	err := cml.Some(&messageIds, query)
	if err != nil {
		return nil, err
	}

	if messageIds == nil || len(messageIds) == 0 {
		return nil, nil
	}

	cm := NewChannelMessage()
	if err := cm.ById(messageIds[0]); err != nil {
		return nil, err
	}

	return cm, nil
}
<commit_msg>Social: add privacy constant<commit_after>package models

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/koding/bongo"
)

type Channel struct {
	// unique identifier of the channel
	Id int64 `json:"id"`

	// Name of the channel
	Name string `json:"name"                         sql:"NOT NULL;TYPE:VARCHAR(200);"`

	// Creator of the channel
	CreatorId int64 `json:"creatorId"                sql:"NOT NULL"`

	// Name of the group which channel is belong to
	GroupName string `json:"groupName"               sql:"NOT NULL;TYPE:VARCHAR(200);"`

	// Purpose of the channel
	Purpose string `json:"purpose"`

	// Secret key of the channel for event propagation purposes
	// we can put this key into another table?
	SecretKey string `json:"-"`

	// Type of the channel
	TypeConstant string `json:"typeConstant"         sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// Privacy constant of the channel
	PrivacyConstant string `json:"privacyConstant"   sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// Creation date of the channel
	CreatedAt time.Time `json:"createdAt"            sql:"NOT NULL"`

	// Modification date of the channel
	UpdatedAt time.Time `json:"updatedAt"            sql:"NOT NULL"`

	// Deletion date of the channel
	DeletedAt time.Time `json:"deletedAt"`
}

// to-do check for allowed channels
const (
	// TYPES
	Channel_TYPE_GROUP           = "group"
	Channel_TYPE_TOPIC           = "topic"
	Channel_TYPE_FOLLOWINGFEED   = "followingfeed"
	Channel_TYPE_FOLLOWERS       = "followers"
	Channel_TYPE_CHAT            = "chat"
	Channel_TYPE_PINNED_ACTIVITY = "pinnedActivity"
	Channel_TYPE_PRIVATE_MESSAGE = "privateMessage"
	Channel_TYPE_DEFAULT         = "default"
	// Privacy
	Channel_PRIVACY_PUBLIC  = "public"
	Channel_PRIVACY_PRIVATE = "private"
	// Koding Group Name
	Channel_KODING_NAME = "koding"
)

func NewChannel() *Channel {
	return &Channel{
		Name:            "Channel" + RandomName(),
		CreatorId:       0,
		GroupName:       Channel_KODING_NAME,
		Purpose:         "",
		SecretKey:       "",
		TypeConstant:    Channel_TYPE_DEFAULT,
		PrivacyConstant: Channel_PRIVACY_PRIVATE,
	}
}

func NewPrivateMessageChannel(creatorId int64, groupName string) *Channel {
	c := NewChannel()
	c.GroupName = groupName
	c.CreatorId = creatorId
	c.Name = RandomName()
	c.TypeConstant = Channel_TYPE_PRIVATE_MESSAGE
	c.PrivacyConstant = Channel_PRIVACY_PRIVATE
	c.Purpose = ""
	return c
}

func (c *Channel) BeforeCreate() {
	c.CreatedAt = time.Now().UTC()
	c.UpdatedAt = time.Now().UTC()
	c.DeletedAt = ZeroDate()
}

func (c *Channel) BeforeUpdate() {
	c.UpdatedAt = time.Now()
}

func (c *Channel) GetId() int64 {
	return c.Id
}

func (c Channel) TableName() string {
	return "api.channel"
}

func (c *Channel) AfterCreate() {
	bongo.B.AfterCreate(c)
}

func (c *Channel) AfterUpdate() {
	bongo.B.AfterUpdate(c)
}

func (c *Channel) AfterDelete() {
	bongo.B.AfterDelete(c)
}

func (c *Channel) Update() error {
	if c.Name == "" || c.GroupName == "" {
		return fmt.Errorf("Validation failed %s - %s", c.Name, c.GroupName)
	}

	return bongo.B.Update(c)
}

func (c *Channel) Create() error {
	if c.Name == "" || c.GroupName == "" || c.TypeConstant == "" {
		return fmt.Errorf("Validation failed %s - %s -%s", c.Name, c.GroupName, c.TypeConstant)
	}

	// golang returns -1 if item not in the string
	if strings.Index(c.Name, " ") > -1 {
		return fmt.Errorf("Channel name %q has empty space in it", c.Name)
	}

	if c.TypeConstant == Channel_TYPE_GROUP /* we can add more types here */ {
		selector := map[string]interface{}{
			"group_name":    c.GroupName,
			"type_constant": c.TypeConstant,
		}

		// if err is nil
		// it means we already have that channel
		err := c.One(bongo.NewQS(selector))
		if err == nil {
			return nil
			// return fmt.Errorf("%s typed channel is already created before for %s group", c.TypeConstant, c.GroupName)
		}

		if err != gorm.RecordNotFound {
			return err
		}

	}

	return bongo.B.Create(c)
}

func (c *Channel) Delete() error {
	return bongo.B.Delete(c)
}

func (c *Channel) ById(id int64) error {
	return bongo.B.ById(c, id)
}

func (c *Channel) One(q *bongo.Query) error {
	return bongo.B.One(c, c, q)
}

func (c *Channel) Some(data interface{}, q *bongo.Query) error {
	return bongo.B.Some(c, data, q)
}

func (c *Channel) FetchByIds(ids []int64) ([]Channel, error) {
	var channels []Channel

	if len(ids) == 0 {
		return channels, nil
	}

	if err := bongo.B.FetchByIds(c, &channels, ids); err != nil {
		return nil, err
	}
	return channels, nil
}

func (c *Channel) AddParticipant(participantId int64) (*ChannelParticipant, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cp := NewChannelParticipant()
	cp.ChannelId = c.Id
	cp.AccountId = participantId

	err := cp.FetchParticipant()
	if err != nil && err != gorm.RecordNotFound {
		return nil, err
	}

	// if we have this record in DB
	if cp.Id != 0 {
		// if status is not active
		if cp.StatusConstant == ChannelParticipant_STATUS_ACTIVE {
			return nil, errors.New(fmt.Sprintf("Account %d is already a participant of channel %d", cp.AccountId, cp.ChannelId))
		}
		cp.StatusConstant = ChannelParticipant_STATUS_ACTIVE
		if err := cp.Update(); err != nil {
			return nil, err
		}
		return cp, nil
	}

	cp.StatusConstant = ChannelParticipant_STATUS_ACTIVE

	if err := cp.Create(); err != nil {
		return nil, err
	}

	return cp, nil
}

func (c *Channel) RemoveParticipant(participantId int64) error {
	if c.Id == 0 {
		return errors.New("Channel Id is not set")
	}

	cp := NewChannelParticipant()
	cp.ChannelId = c.Id
	cp.AccountId = participantId

	err := cp.FetchParticipant()
	// if user is not in this channel, do nothing
	if err == gorm.RecordNotFound {
		return nil
	}

	if err != nil {
		return err
	}

	if cp.StatusConstant == ChannelParticipant_STATUS_LEFT {
		return nil
	}

	cp.StatusConstant = ChannelParticipant_STATUS_LEFT
	if err := cp.Update(); err != nil {
		return err
	}

	return nil
}

func (c *Channel) FetchParticipantIds() ([]int64, error) {
	var participantIds []int64

	if c.Id == 0 {
		return participantIds, errors.New("Channel Id is not set")
	}

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id":      c.Id,
			"status_constant": ChannelParticipant_STATUS_ACTIVE,
		},
		Pluck: "account_id",
	}

	cp := NewChannelParticipant()
	err := cp.Some(&participantIds, query)
	if err != nil {
		return nil, err
	}

	return participantIds, nil
}

func (c *Channel) AddMessage(messageId int64) (*ChannelMessageList, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cml := NewChannelMessageList()

	selector := map[string]interface{}{
		"channel_id": c.Id,
		"message_id": messageId,
	}
	err := cml.One(bongo.NewQS(selector))
	if err == nil {
		return nil, errors.New("Message is already in the channel")
	}

	// silence record not found err
	if err != gorm.RecordNotFound {
		return nil, err
	}

	cml.ChannelId = c.Id
	cml.MessageId = messageId

	if err := cml.Create(); err != nil {
		return nil, err
	}

	return cml, nil
}

func (c *Channel) RemoveMessage(messageId int64) (*ChannelMessageList, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cml := NewChannelMessageList()
	selector := map[string]interface{}{
		"channel_id": c.Id,
		"message_id": messageId,
	}
	err := cml.One(bongo.NewQS(selector))
	// one returns error when record not found case
	// but we dont care if it is not there tho
	if err != nil {
		return nil, err
	}

	if err := cml.Delete(); err != nil {
		return nil, err
	}

	return cml, nil
}

func (c *Channel) Search(q *Query) ([]Channel, error) {

	if q.GroupName == "" {
		return nil, fmt.Errorf("Query doesnt have any Group info %+v", q)
	}

	var channels []Channel

	query := bongo.B.DB.Table(c.TableName()).Limit(q.Limit)

	query = query.Where("type_constant = ?", q.Type)
	query = query.Where("privacy_constant = ?", Channel_PRIVACY_PUBLIC)
	query = query.Where("group_name = ?", q.GroupName)
	query = query.Where("name like ?", q.Name+"%")

	if err := query.Find(&channels).Error; err != nil {
		return nil, err
	}

	return channels, nil
}

func (c *Channel) List(q *Query) ([]Channel, error) {

	if q.GroupName == "" {
		return nil, fmt.Errorf("Query doesnt have any Group info %+v", q)
	}

	var channels []Channel

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"group_name": q.GroupName,
		},
	}

	if q.Type != "" {
		query.Selector["type_constant"] = q.Type
	}

	err := c.Some(&channels, query)
	if err != nil {
		return nil, err
	}

	return channels, nil
}

func (c *Channel) FetchLastMessage() (*ChannelMessage, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cml := NewChannelMessageList()
	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id": c.Id,
		},
		Sort: map[string]string{
			"added_at": "DESC",
		},
		Limit: 1,
		Pluck: "message_id",
	}

	var messageIds []int64
	err := cml.Some(&messageIds, query)
	if err != nil {
		return nil, err
	}

	if messageIds == nil || len(messageIds) == 0 {
		return nil, nil
	}

	cm := NewChannelMessage()
	if err := cm.ById(messageIds[0]); err != nil {
		return nil, err
	}

	return cm, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"syscall"
)

func main() {

	initFlags()

	// check permissions
	if os.Getuid() != 0 && os.Geteuid() != 0 {
		fmt.Printf("npserver-daemon should be run as root, have uid=%d and euid=%d\n", os.Getuid(), os.Geteuid())
		os.Exit(1)
	}

	// check start || sto
	if flags.Start == flags.Stop {
		fmt.Println("need --start or --stop flag")
	}

	if flags.Start {
		startDaemon()
	}
	if flags.Stop {
		stopDaemon()
	}

	// all good :)
}

func startDaemon() {
	// setup args for daemon call
	args := []string{
		"--name=npserver",
		"--noconfig",
		"--errlog=/var/log/npserver-daemon.log",
		"--output=/var/log/npserver.log",
		fmt.Sprintf("--pidfile=%s", flags.PIDFile),
		"--unsafe",
		"--",
		"/usr/local/bin/npserver",
	}

	// append extra args to args
	args = append(args, extraArgs...)

	// start process
	proc, err := os.StartProcess("daemon", args, &os.ProcAttr{
		Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
		Sys: &syscall.SysProcAttr{
			Credential: &syscall.Credential{
				Uid: uint32(os.Geteuid()),
				Gid: uint32(os.Getegid()),
			},
		},
	})
	if err != nil {
		fmt.Printf("os/exec returned an error: '%s'\n", err)
		os.Exit(1)
	}

	// wait for daemon to be ready
	_, err = proc.Wait()
	if err != nil {
		fmt.Printf("proc.Wait() failed. %s\n", err)
		os.Exit(1)
	}
}

func stopDaemon() {
	pidFile, err := os.Open(flags.PIDFile)
	if err != nil {
		if os.IsNotExist(err) {
			fmt.Printf("it looks like npserver is not running")
			os.Exit(0)
		}
		fmt.Printf("error on opening pidfile: %s\n", err)
		os.Exit(1)
	}

	pidFileContents, err := ioutil.ReadAll(pidFile)
	pidFile.Close()
	if err != nil {
		fmt.Printf("error reading pidfile contents: %s\n", err)
		os.Exit(1)
	}

	// convert pid string to pid int
	pid, err := strconv.Atoi(string(pidFileContents))
	if err != nil {
		fmt.Printf("error parsing pidfile contents: %s\n", err)
		os.Exit(1)
	}

	// lookup process
	proc, err := os.FindProcess(pid)
	if err != nil {
		fmt.Printf("error finding process with pid %d: %s\n", pid, err)
		os.Exit(1)
	}

	// signal process to stop
	err = proc.Signal(os.Interrupt)
	if err != nil {
		fmt.Printf("error sending interrupt signal to npserver: %s\n", err)
		os.Exit(1)
	}

	// wait until process is done
	state, err := proc.Wait()
	if err != nil {
		fmt.Printf("error waiting for process to stop: %s\n", err)
		os.Exit(1)
	}
	if !state.Exited() || !state.Success() {
		fmt.Printf("npserver process exited badly")
		os.Exit(1)
	}

	// remove pid file
	err = os.Remove(flags.PIDFile)
	if err != nil {
		fmt.Printf("error removing pid file: %s\n", err)
	}
}
<commit_msg>use absolute path for `daemon`<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"syscall"
)

func main() {

	initFlags()

	// check permissions
	if os.Getuid() != 0 && os.Geteuid() != 0 {
		fmt.Printf("npserver-daemon should be run as root, have uid=%d and euid=%d\n", os.Getuid(), os.Geteuid())
		os.Exit(1)
	}

	// check start || sto
	if flags.Start == flags.Stop {
		fmt.Println("need --start or --stop flag")
	}

	if flags.Start {
		startDaemon()
	}
	if flags.Stop {
		stopDaemon()
	}

	// all good :)
}

func startDaemon() {
	// setup args for daemon call
	args := []string{
		"--name=npserver",
		"--noconfig",
		"--errlog=/var/log/npserver-daemon.log",
		"--output=/var/log/npserver.log",
		fmt.Sprintf("--pidfile=%s", flags.PIDFile),
		"--unsafe",
		"--",
		"/usr/local/bin/npserver",
	}

	// append extra args to args
	args = append(args, extraArgs...)

	// start process
	proc, err := os.StartProcess("/usr/bin/daemon", args, &os.ProcAttr{
		Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
		Sys: &syscall.SysProcAttr{
			Credential: &syscall.Credential{
				Uid: uint32(os.Geteuid()),
				Gid: uint32(os.Getegid()),
			},
		},
	})
	if err != nil {
		fmt.Printf("os/exec returned an error: '%s'\n", err)
		os.Exit(1)
	}

	// wait for daemon to be ready
	_, err = proc.Wait()
	if err != nil {
		fmt.Printf("proc.Wait() failed. %s\n", err)
		os.Exit(1)
	}
}

func stopDaemon() {
	pidFile, err := os.Open(flags.PIDFile)
	if err != nil {
		if os.IsNotExist(err) {
			fmt.Printf("it looks like npserver is not running")
			os.Exit(0)
		}
		fmt.Printf("error on opening pidfile: %s\n", err)
		os.Exit(1)
	}

	pidFileContents, err := ioutil.ReadAll(pidFile)
	pidFile.Close()
	if err != nil {
		fmt.Printf("error reading pidfile contents: %s\n", err)
		os.Exit(1)
	}

	// convert pid string to pid int
	pid, err := strconv.Atoi(string(pidFileContents))
	if err != nil {
		fmt.Printf("error parsing pidfile contents: %s\n", err)
		os.Exit(1)
	}

	// lookup process
	proc, err := os.FindProcess(pid)
	if err != nil {
		fmt.Printf("error finding process with pid %d: %s\n", pid, err)
		os.Exit(1)
	}

	// signal process to stop
	err = proc.Signal(os.Interrupt)
	if err != nil {
		fmt.Printf("error sending interrupt signal to npserver: %s\n", err)
		os.Exit(1)
	}

	// wait until process is done
	state, err := proc.Wait()
	if err != nil {
		fmt.Printf("error waiting for process to stop: %s\n", err)
		os.Exit(1)
	}
	if !state.Exited() || !state.Success() {
		fmt.Printf("npserver process exited badly")
		os.Exit(1)
	}

	// remove pid file
	err = os.Remove(flags.PIDFile)
	if err != nil {
		fmt.Printf("error removing pid file: %s\n", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"image"
	"image/color"
	"image/png"
	"math/cmplx"
	"os"
	"sync"

	"github.com/krasoffski/gomill/htcmap"
)

const (
	xmin, ymin = -2.2, -1.2
	xmax, ymax = +1.2, +1.2
)

type point struct {
	x, y int
}

type pixel struct {
	point
	c color.Color
}

func xCord(x, width, factor int) float64 {
	return float64(x)/float64(width*factor)*(xmax-xmin) + xmin
}

func yCord(y, height, factor int) float64 {
	return float64(y)/float64(height*factor)*(ymax-ymin) + ymin
}

func superSampling(p *point, width, height, factor int) color.Color {

	xCords, yCords := make([]float64, factor), make([]float64, factor)
	subPixels := make([]color.Color, factor*factor)

	// Single calculation of required coordinates for super sampling.
	for i := 0; i < factor; i++ {
		xCords[i] = xCord(p.x+i, width, factor)
		yCords[i] = yCord(p.y+i, height, factor)
	}

	// Instead of calculation coordinate only fetching required one.
	for iy := 0; iy < factor; iy++ {
		for ix := 0; ix < factor; ix++ {
			// Using one dimension array because do not care about pixel order,
			// because at the end we are calculating avarage for all sub-pixels.
			subPixels[iy*factor+ix] = mandelbrot(complex(xCords[ix], yCords[iy]))
		}
	}

	var rAvg, gAvg, bAvg float64

	// TODO: think about removing multiplication of factor for each calculation.
	factor2 := float64(factor * factor)
	for _, c := range subPixels {
		r, g, b, _ := c.RGBA()
		rAvg += float64(r) / factor2
		gAvg += float64(g) / factor2
		bAvg += float64(b) / factor2
	}
	return color.RGBA64{uint16(rAvg), uint16(gAvg), uint16(bAvg), 0xFFFF}
}

func mandelbrot(z complex128) color.Color {
	const iterations = 255
	const contrast = 15

	var v complex128
	for n := uint8(0); n < iterations; n++ {
		v = v*v + z
		vAbs := cmplx.Abs(v)
		if vAbs > 2 {
			// smooth := float64(n) + 1 - math.Log(math.Log(vAbs))/math.Log(2)
			r, g, b := htcmap.AsUInt8(float64(n*contrast), 0, iterations)
			return color.RGBA{r, g, b, 255}
		}
	}
	return color.Black
}

func compute(width, height, factor, workers int) <-chan *pixel {
	var wg sync.WaitGroup
	points := make(chan *point)
	pixels := make(chan *pixel, workers)

	go func() {
		defer close(points)

		for py := 0; py < height*factor; py += factor {
			for px := 0; px < width*factor; px += factor {
				points <- &point{px, py}
			}
		}
	}()

	for i := 0; i < workers; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()

			for {
				p, ok := <-points
				if !ok {
					return
				}
				c := superSampling(p, width, height, factor)
				pixels <- &pixel{point{p.x / factor, p.y / factor}, c}
			}
		}()
	}

	go func() {
		wg.Wait()
		close(pixels)
	}()

	return pixels
}

func main() {
	factor := flag.Int("factor", 2, "scale factor for super sampling")
	workers := flag.Int("workers", 2, "number of workers for calculation")
	width := flag.Int("width", 1536, "width of png image in pixels")
	height := flag.Int("height", 1024, "width of png image in pixels")
	flag.Parse()
	if *factor < 1 || *factor > 256 {
		fmt.Fprintf(os.Stderr, "error: invalid value '%d', [1, 255]\n", *factor)
		os.Exit(1)
	}

	if *workers < 1 || *workers > 256 {
		fmt.Fprintf(os.Stderr, "error: invalid value '%d', [1, 255]\n", *workers)
		os.Exit(1)
	}

	img := image.NewRGBA(image.Rect(0, 0, *width, *height))

	for p := range compute(*width, *height, *factor, *workers) {
		img.Set(p.x, p.y, p.c)
	}

	if err := png.Encode(os.Stdout, img); err != nil {
		fmt.Fprintf(os.Stderr, "error encoding png: %s", err)
		os.Exit(1)
	}
	if err := png.Encode(os.Stdout, img); err != nil {
		fmt.Fprintf(os.Stderr, "error encoding png: %s", err)
		os.Exit(1)
	}
}
<commit_msg>Flag doc fixes for mandebrot.<commit_after>package main

import (
	"flag"
	"fmt"
	"image"
	"image/color"
	"image/png"
	"math/cmplx"
	"os"
	"sync"

	"github.com/krasoffski/gomill/htcmap"
)

const (
	xmin, ymin = -2.2, -1.2
	xmax, ymax = +1.2, +1.2
)

type point struct {
	x, y int
}

type pixel struct {
	point
	c color.Color
}

func xCord(x, width, factor int) float64 {
	return float64(x)/float64(width*factor)*(xmax-xmin) + xmin
}

func yCord(y, height, factor int) float64 {
	return float64(y)/float64(height*factor)*(ymax-ymin) + ymin
}

func superSampling(p *point, width, height, factor int) color.Color {

	xCords, yCords := make([]float64, factor), make([]float64, factor)
	subPixels := make([]color.Color, factor*factor)

	// Single calculation of required coordinates for super sampling.
	for i := 0; i < factor; i++ {
		xCords[i] = xCord(p.x+i, width, factor)
		yCords[i] = yCord(p.y+i, height, factor)
	}

	// Instead of calculation coordinate only fetching required one.
	for iy := 0; iy < factor; iy++ {
		for ix := 0; ix < factor; ix++ {
			// Using one dimension array because do not care about pixel order,
			// because at the end we are calculating avarage for all sub-pixels.
			subPixels[iy*factor+ix] = mandelbrot(complex(xCords[ix], yCords[iy]))
		}
	}

	var rAvg, gAvg, bAvg float64

	// TODO: think about removing multiplication of factor for each calculation.
	factor2 := float64(factor * factor)
	for _, c := range subPixels {
		r, g, b, _ := c.RGBA()
		rAvg += float64(r) / factor2
		gAvg += float64(g) / factor2
		bAvg += float64(b) / factor2
	}
	return color.RGBA64{uint16(rAvg), uint16(gAvg), uint16(bAvg), 0xFFFF}
}

func mandelbrot(z complex128) color.Color {
	const iterations = 255
	const contrast = 15

	var v complex128
	for n := uint8(0); n < iterations; n++ {
		v = v*v + z
		vAbs := cmplx.Abs(v)
		if vAbs > 2 {
			// smooth := float64(n) + 1 - math.Log(math.Log(vAbs))/math.Log(2)
			r, g, b := htcmap.AsUInt8(float64(n*contrast), 0, iterations)
			return color.RGBA{r, g, b, 255}
		}
	}
	return color.Black
}

func compute(width, height, factor, workers int) <-chan *pixel {
	var wg sync.WaitGroup
	points := make(chan *point)
	pixels := make(chan *pixel, workers)

	go func() {
		defer close(points)

		for py := 0; py < height*factor; py += factor {
			for px := 0; px < width*factor; px += factor {
				points <- &point{px, py}
			}
		}
	}()

	for i := 0; i < workers; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()

			for {
				p, ok := <-points
				if !ok {
					return
				}
				c := superSampling(p, width, height, factor)
				pixels <- &pixel{point{p.x / factor, p.y / factor}, c}
			}
		}()
	}

	go func() {
		wg.Wait()
		close(pixels)
	}()

	return pixels
}

func main() {
	factor := flag.Int("factor", 2, "scale factor for super sampling")
	workers := flag.Int("workers", 2, "number of workers for calculation")
	width := flag.Int("width", 1536, "width of png image in pixels, WxH=3x2")
	height := flag.Int("height", 1024, "height of png image in pixels, WxH=3x2")
	flag.Parse()
	if *factor < 1 || *factor > 256 {
		fmt.Fprintf(os.Stderr, "error: invalid value '%d', [1, 255]\n", *factor)
		os.Exit(1)
	}

	if *workers < 1 || *workers > 256 {
		fmt.Fprintf(os.Stderr, "error: invalid value '%d', [1, 255]\n", *workers)
		os.Exit(1)
	}

	img := image.NewRGBA(image.Rect(0, 0, *width, *height))

	for p := range compute(*width, *height, *factor, *workers) {
		img.Set(p.x, p.y, p.c)
	}

	if err := png.Encode(os.Stdout, img); err != nil {
		fmt.Fprintf(os.Stderr, "error encoding png: %s", err)
		os.Exit(1)
	}
	if err := png.Encode(os.Stdout, img); err != nil {
		fmt.Fprintf(os.Stderr, "error encoding png: %s", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package plugins

import (
	"errors"
	"fmt"
	"github.com/moriyoshi/ik"
	"github.com/ugorji/go/codec"
	"io"
	"net"
	"reflect"
	"strconv"
	"sync/atomic"
)

type forwardClient struct {
	input  *ForwardInput
	logger ik.Logger
	conn   net.Conn
	codec  *codec.MsgpackHandle
	enc    *codec.Encoder
	dec    *codec.Decoder
}

type ForwardInput struct {
	factory  *ForwardInputFactory
	port     ik.Port
	logger   ik.Logger
	bind     string
	listener net.Listener
	codec    *codec.MsgpackHandle
	clients  map[net.Conn]*forwardClient
	entries  int64
}

type EntryCountTopic struct{}

type ConnectionCountTopic struct{}

type ForwardInputFactory struct {
}

func coerceInPlace(data map[string]interface{}) {
	for k, v := range data {
		switch v_ := v.(type) {
		case []byte:
			data[k] = string(v_) // XXX: byte => rune
		case map[string]interface{}:
			coerceInPlace(v_)
		}
	}
}

func decodeRecordSet(tag []byte, entries []interface{}) (ik.FluentRecordSet, error) {
	records := make([]ik.TinyFluentRecord, len(entries))
	for i, _entry := range entries {
		entry, ok := _entry.([]interface{})
		if !ok {
			return ik.FluentRecordSet{}, errors.New("Failed to decode recordSet")
		}
		timestamp, ok := entry[0].(uint64)
		if !ok {
			return ik.FluentRecordSet{}, errors.New("Failed to decode timestamp field")
		}
		data, ok := entry[1].(map[string]interface{})
		if !ok {
			return ik.FluentRecordSet{}, errors.New("Failed to decode data field")
		}
		coerceInPlace(data)
		records[i] = ik.TinyFluentRecord{
			Timestamp: timestamp,
			Data:      data,
		}
	}
	return ik.FluentRecordSet{
		Tag:     string(tag), // XXX: byte => rune
		Records: records,
	}, nil
}

func (c *forwardClient) decodeEntries() ([]ik.FluentRecordSet, error) {
	v := []interface{}{nil, nil, nil}
	err := c.dec.Decode(&v)
	if err != nil {
		return nil, err
	}
	tag, ok := v[0].([]byte)
	if !ok {
		return nil, errors.New("Failed to decode tag field")
	}

	var retval []ik.FluentRecordSet
	switch timestamp_or_entries := v[1].(type) {
	case uint64:
		timestamp := timestamp_or_entries
		data, ok := v[2].(map[string]interface{})
		if !ok {
			return nil, errors.New("Failed to decode data field")
		}
		coerceInPlace(data)
		retval = []ik.FluentRecordSet{
			{
				Tag: string(tag), // XXX: byte => rune
				Records: []ik.TinyFluentRecord{
					{
						Timestamp: timestamp,
						Data:      data,
					},
				},
			},
		}
	case float64:
		timestamp := uint64(timestamp_or_entries)
		data, ok := v[2].(map[string]interface{})
		if !ok {
			return nil, errors.New("Failed to decode data field")
		}
		retval = []ik.FluentRecordSet{
			{
				Tag: string(tag), // XXX: byte => rune
				Records: []ik.TinyFluentRecord{
					{
						Timestamp: timestamp,
						Data:      data,
					},
				},
			},
		}
	case []interface{}:
		if !ok {
			return nil, errors.New("Unexpected payload format")
		}
		recordSet, err := decodeRecordSet(tag, timestamp_or_entries)
		if err != nil {
			return nil, err
		}
		retval = []ik.FluentRecordSet{recordSet}
	case []byte:
		entries := make([]interface{}, 0)
		err := codec.NewDecoderBytes(timestamp_or_entries, c.codec).Decode(&entries)
		if err != nil {
			return nil, err
		}
		recordSet, err := decodeRecordSet(tag, entries)
		if err != nil {
			return nil, err
		}
		retval = []ik.FluentRecordSet{recordSet}
	default:
		return nil, errors.New(fmt.Sprintf("Unknown type: %t", timestamp_or_entries))
	}
	atomic.AddInt64(&c.input.entries, int64(len(retval)))
	return retval, nil
}

func handleInner(c *forwardClient) bool {
	recordSets, err := c.decodeEntries()
	defer func() {
		if len(recordSets) > 0 {
			err_ := c.input.Port().Emit(recordSets)
			if err_ != nil {
				c.logger.Error("%s", err_.Error())
			}
		}
	}()
	if err == nil {
		return true
	}

	err_, ok := err.(net.Error)
	if ok {
		if err_.Temporary() {
			c.logger.Warning("Temporary failure: %s", err_.Error())
			return true
		}
	}
	if err == io.EOF {
		c.logger.Info("Client %s closed the connection", c.conn.RemoteAddr().String())
	} else {
		c.logger.Error("%s", err.Error())
	}
	return false
}

func (c *forwardClient) handle() {
	for handleInner(c) {
	}
	err := c.conn.Close()
	if err != nil {
		c.logger.Warning("%s", err.Error())
	}
	c.input.markDischarged(c)
}

func newForwardClient(input *ForwardInput, logger ik.Logger, conn net.Conn, _codec *codec.MsgpackHandle) *forwardClient {
	c := &forwardClient{
		input:  input,
		logger: logger,
		conn:   conn,
		codec:  _codec,
		enc:    codec.NewEncoder(conn, _codec),
		dec:    codec.NewDecoder(conn, _codec),
	}
	input.markCharged(c)
	return c
}

func (input *ForwardInput) Factory() ik.Plugin {
	return input.factory
}

func (input *ForwardInput) Port() ik.Port {
	return input.port
}

func (input *ForwardInput) Run() error {
	conn, err := input.listener.Accept()
	if err != nil {
		input.logger.Warning("%s", err.Error())
		return err
	}
	go newForwardClient(input, input.logger, conn, input.codec).handle()
	return ik.Continue
}

func (input *ForwardInput) Shutdown() error {
	for conn, _ := range input.clients {
		err := conn.Close()
		if err != nil {
			input.logger.Warning("Error during closing connection: %s", err.Error())
		}
	}
	return input.listener.Close()
}

func (input *ForwardInput) Dispose() {
	input.Shutdown()
}

func (input *ForwardInput) markCharged(c *forwardClient) {
	input.clients[c.conn] = c
}

func (input *ForwardInput) markDischarged(c *forwardClient) {
	delete(input.clients, c.conn)
}

func newForwardInput(factory *ForwardInputFactory, logger ik.Logger, engine ik.Engine, bind string, port ik.Port) (*ForwardInput, error) {
	_codec := codec.MsgpackHandle{}
	_codec.MapType = reflect.TypeOf(map[string]interface{}(nil))
	_codec.RawToString = false
	listener, err := net.Listen("tcp", bind)
	if err != nil {
		logger.Warning("%s", err.Error())
		return nil, err
	}
	return &ForwardInput{
		factory:  factory,
		port:     port,
		logger:   logger,
		bind:     bind,
		listener: listener,
		codec:    &_codec,
		clients:  make(map[net.Conn]*forwardClient),
		entries:  0,
	}, nil
}

func (factory *ForwardInputFactory) Name() string {
	return "forward"
}

func (factory *ForwardInputFactory) New(engine ik.Engine, config *ik.ConfigElement) (ik.Input, error) {
	listen, ok := config.Attrs["listen"]
	if !ok {
		listen = ""
	}
	netPort, ok := config.Attrs["port"]
	if !ok {
		netPort = "24224"
	}
	bind := listen + ":" + netPort
	return newForwardInput(factory, engine.Logger(), engine, bind, engine.DefaultPort())
}

func (factory *ForwardInputFactory) BindScorekeeper(scorekeeper *ik.Scorekeeper) {
	scorekeeper.AddTopic(ik.ScorekeeperTopic{
		Plugin:      factory,
		Name:        "entries",
		DisplayName: "Total number of entries",
		Description: "Total number of entries received so far",
		Fetcher:     &EntryCountTopic{},
	})
	scorekeeper.AddTopic(ik.ScorekeeperTopic{
		Plugin:      factory,
		Name:        "connections",
		DisplayName: "Connections",
		Description: "Number of connections currently handled",
		Fetcher:     &ConnectionCountTopic{},
	})
}

func (topic *EntryCountTopic) Markup(input_ ik.PluginInstance) (ik.Markup, error) {
	text, err := topic.PlainText(input_)
	if err != nil {
		return ik.Markup{}, err
	}
	return ik.Markup{[]ik.MarkupChunk{{Text: text}}}, nil
}

func (topic *EntryCountTopic) PlainText(input_ ik.PluginInstance) (string, error) {
	input := input_.(*ForwardInput)
	return strconv.FormatInt(input.entries, 10), nil
}

func (topic *ConnectionCountTopic) Markup(input_ ik.PluginInstance) (ik.Markup, error) {
	text, err := topic.PlainText(input_)
	if err != nil {
		return ik.Markup{}, err
	}
	return ik.Markup{[]ik.MarkupChunk{{Text: text}}}, nil
}

func (topic *ConnectionCountTopic) PlainText(input_ ik.PluginInstance) (string, error) {
	input := input_.(*ForwardInput)
	return strconv.Itoa(len(input.clients)), nil // XXX: race
}

var _ = AddPlugin(&ForwardInputFactory{})
<commit_msg>Details on ill-formated payload<commit_after>package plugins

import (
	"errors"
	"fmt"
	"github.com/moriyoshi/ik"
	"github.com/ugorji/go/codec"
	"io"
	"net"
	"reflect"
	"strconv"
	"sync/atomic"
)

type forwardClient struct {
	input  *ForwardInput
	logger ik.Logger
	conn   net.Conn
	codec  *codec.MsgpackHandle
	enc    *codec.Encoder
	dec    *codec.Decoder
}

type ForwardInput struct {
	factory  *ForwardInputFactory
	port     ik.Port
	logger   ik.Logger
	bind     string
	listener net.Listener
	codec    *codec.MsgpackHandle
	clients  map[net.Conn]*forwardClient
	entries  int64
}

type EntryCountTopic struct{}

type ConnectionCountTopic struct{}

type ForwardInputFactory struct {
}

func coerceInPlace(data map[string]interface{}) {
	for k, v := range data {
		switch v_ := v.(type) {
		case []byte:
			data[k] = string(v_) // XXX: byte => rune
		case map[string]interface{}:
			coerceInPlace(v_)
		}
	}
}

func decodeRecordSet(tag []byte, entries []interface{}) (ik.FluentRecordSet, error) {
	records := make([]ik.TinyFluentRecord, len(entries))
	for i, _entry := range entries {
		entry, ok := _entry.([]interface{})
		if !ok {
			return ik.FluentRecordSet{}, errors.New("Failed to decode recordSet")
		}
		timestamp, ok := entry[0].(uint64)
		if !ok {
			return ik.FluentRecordSet{}, errors.New("Failed to decode timestamp field")
		}
		data, ok := entry[1].(map[string]interface{})
		if !ok {
			return ik.FluentRecordSet{}, errors.New(fmt.Sprintf("Failed to decode data field", entry[1]))
		}
		coerceInPlace(data)
		records[i] = ik.TinyFluentRecord{
			Timestamp: timestamp,
			Data:      data,
		}
	}
	return ik.FluentRecordSet{
		Tag:     string(tag), // XXX: byte => rune
		Records: records,
	}, nil
}

func (c *forwardClient) decodeEntries() ([]ik.FluentRecordSet, error) {
	v := []interface{}{nil, nil, nil}
	err := c.dec.Decode(&v)
	if err != nil {
		return nil, err
	}
	tag, ok := v[0].([]byte)
	if !ok {
		return nil, errors.New("Failed to decode tag field")
	}

	var retval []ik.FluentRecordSet
	switch timestamp_or_entries := v[1].(type) {
	case uint64:
		timestamp := timestamp_or_entries
		data, ok := v[2].(map[string]interface{})
		if !ok {
			return nil, errors.New(fmt.Sprintf("Failed to decode data field (got %t)", v[2]))
		}
		coerceInPlace(data)
		retval = []ik.FluentRecordSet{
			{
				Tag: string(tag), // XXX: byte => rune
				Records: []ik.TinyFluentRecord{
					{
						Timestamp: timestamp,
						Data:      data,
					},
				},
			},
		}
	case float64:
		timestamp := uint64(timestamp_or_entries)
		data, ok := v[2].(map[string]interface{})
		if !ok {
			return nil, errors.New(fmt.Sprintf("Failed to decode data field (got %t)", v[2]))
		}
		retval = []ik.FluentRecordSet{
			{
				Tag: string(tag), // XXX: byte => rune
				Records: []ik.TinyFluentRecord{
					{
						Timestamp: timestamp,
						Data:      data,
					},
				},
			},
		}
	case []interface{}:
		if !ok {
			return nil, errors.New("Unexpected payload format")
		}
		recordSet, err := decodeRecordSet(tag, timestamp_or_entries)
		if err != nil {
			return nil, err
		}
		retval = []ik.FluentRecordSet{recordSet}
	case []byte:
		entries := make([]interface{}, 0)
		err := codec.NewDecoderBytes(timestamp_or_entries, c.codec).Decode(&entries)
		if err != nil {
			return nil, err
		}
		recordSet, err := decodeRecordSet(tag, entries)
		if err != nil {
			return nil, err
		}
		retval = []ik.FluentRecordSet{recordSet}
	default:
		return nil, errors.New(fmt.Sprintf("Unknown type: %t", timestamp_or_entries))
	}
	atomic.AddInt64(&c.input.entries, int64(len(retval)))
	return retval, nil
}

func handleInner(c *forwardClient) bool {
	recordSets, err := c.decodeEntries()
	defer func() {
		if len(recordSets) > 0 {
			err_ := c.input.Port().Emit(recordSets)
			if err_ != nil {
				c.logger.Error("%s", err_.Error())
			}
		}
	}()
	if err == nil {
		return true
	}

	err_, ok := err.(net.Error)
	if ok {
		if err_.Temporary() {
			c.logger.Warning("Temporary failure: %s", err_.Error())
			return true
		}
	}
	if err == io.EOF {
		c.logger.Info("Client %s closed the connection", c.conn.RemoteAddr().String())
	} else {
		c.logger.Error("%s", err.Error())
	}
	return false
}

func (c *forwardClient) handle() {
	for handleInner(c) {
	}
	err := c.conn.Close()
	if err != nil {
		c.logger.Warning("%s", err.Error())
	}
	c.input.markDischarged(c)
}

func newForwardClient(input *ForwardInput, logger ik.Logger, conn net.Conn, _codec *codec.MsgpackHandle) *forwardClient {
	c := &forwardClient{
		input:  input,
		logger: logger,
		conn:   conn,
		codec:  _codec,
		enc:    codec.NewEncoder(conn, _codec),
		dec:    codec.NewDecoder(conn, _codec),
	}
	input.markCharged(c)
	return c
}

func (input *ForwardInput) Factory() ik.Plugin {
	return input.factory
}

func (input *ForwardInput) Port() ik.Port {
	return input.port
}

func (input *ForwardInput) Run() error {
	conn, err := input.listener.Accept()
	if err != nil {
		input.logger.Warning("%s", err.Error())
		return err
	}
	go newForwardClient(input, input.logger, conn, input.codec).handle()
	return ik.Continue
}

func (input *ForwardInput) Shutdown() error {
	for conn, _ := range input.clients {
		err := conn.Close()
		if err != nil {
			input.logger.Warning("Error during closing connection: %s", err.Error())
		}
	}
	return input.listener.Close()
}

func (input *ForwardInput) Dispose() {
	input.Shutdown()
}

func (input *ForwardInput) markCharged(c *forwardClient) {
	input.clients[c.conn] = c
}

func (input *ForwardInput) markDischarged(c *forwardClient) {
	delete(input.clients, c.conn)
}

func newForwardInput(factory *ForwardInputFactory, logger ik.Logger, engine ik.Engine, bind string, port ik.Port) (*ForwardInput, error) {
	_codec := codec.MsgpackHandle{}
	_codec.MapType = reflect.TypeOf(map[string]interface{}(nil))
	_codec.RawToString = false
	listener, err := net.Listen("tcp", bind)
	if err != nil {
		logger.Warning("%s", err.Error())
		return nil, err
	}
	return &ForwardInput{
		factory:  factory,
		port:     port,
		logger:   logger,
		bind:     bind,
		listener: listener,
		codec:    &_codec,
		clients:  make(map[net.Conn]*forwardClient),
		entries:  0,
	}, nil
}

func (factory *ForwardInputFactory) Name() string {
	return "forward"
}

func (factory *ForwardInputFactory) New(engine ik.Engine, config *ik.ConfigElement) (ik.Input, error) {
	listen, ok := config.Attrs["listen"]
	if !ok {
		listen = ""
	}
	netPort, ok := config.Attrs["port"]
	if !ok {
		netPort = "24224"
	}
	bind := listen + ":" + netPort
	return newForwardInput(factory, engine.Logger(), engine, bind, engine.DefaultPort())
}

func (factory *ForwardInputFactory) BindScorekeeper(scorekeeper *ik.Scorekeeper) {
	scorekeeper.AddTopic(ik.ScorekeeperTopic{
		Plugin:      factory,
		Name:        "entries",
		DisplayName: "Total number of entries",
		Description: "Total number of entries received so far",
		Fetcher:     &EntryCountTopic{},
	})
	scorekeeper.AddTopic(ik.ScorekeeperTopic{
		Plugin:      factory,
		Name:        "connections",
		DisplayName: "Connections",
		Description: "Number of connections currently handled",
		Fetcher:     &ConnectionCountTopic{},
	})
}

func (topic *EntryCountTopic) Markup(input_ ik.PluginInstance) (ik.Markup, error) {
	text, err := topic.PlainText(input_)
	if err != nil {
		return ik.Markup{}, err
	}
	return ik.Markup{[]ik.MarkupChunk{{Text: text}}}, nil
}

func (topic *EntryCountTopic) PlainText(input_ ik.PluginInstance) (string, error) {
	input := input_.(*ForwardInput)
	return strconv.FormatInt(input.entries, 10), nil
}

func (topic *ConnectionCountTopic) Markup(input_ ik.PluginInstance) (ik.Markup, error) {
	text, err := topic.PlainText(input_)
	if err != nil {
		return ik.Markup{}, err
	}
	return ik.Markup{[]ik.MarkupChunk{{Text: text}}}, nil
}

func (topic *ConnectionCountTopic) PlainText(input_ ik.PluginInstance) (string, error) {
	input := input_.(*ForwardInput)
	return strconv.Itoa(len(input.clients)), nil // XXX: race
}

var _ = AddPlugin(&ForwardInputFactory{})
<|endoftext|>


<commit_before>// Package boolmap creates a map of bools using bytes for efficiency (needs benchmarking for memory)
package boolmap

// Map is the default boolmap
type Map struct {
	data map[uint64]byte
}

// NewMap returns a new, initialised Map
func NewMap() Map {
	return Map{make(map[uint64]byte)}
}

// Get returns a bool for the specified position
func (m *Map) Get(p uint64) bool {
	return m.data[p>>3]&(1<<(p&7)) != 0
}

// Set sets a bool at the specified position
func (m *Map) Set(p uint64, d bool) {
	shift := byte(1 << (p & 7))
	pos := p >> 3
	var (
		c  byte
		ok bool
	)
	if c, ok = m.data[pos]; !ok && !d {
		return
	}
	if d {
		c |= shift
	} else {
		c &^= shift
	}
	if c == 0 {
		delete(m.data, pos)
	} else {
		m.data[pos] = c
	}
}

// Slice is a slice of bytes representing bools
type Slice struct {
	data []byte
}

// NewSlice returnns a new, initialised Slice
func NewSlice() *Slice {
	return &Slice{make([]byte, 1)}
}

// Get returns a bool for the specified position
func (s *Slice) Get(p uint) bool {
	pos := p >> 3
	if pos > uint(len(s.data)) {
		return false
	}
	return s.data[pos]&(1<<(p&7)) != 0
}

// Set sets a bool at the specified position
func (s *Slice) Set(p uint, d bool) {
	pos := p >> 3
	if pos >= uint(len(s.data)) {
		if pos < uint(cap(s.data)) {
			s.data = s.data[:pos]
		} else {
			var newData []byte
			if pos < 512 {
				newData = make([]byte, pos<<1)
			} else {
				newData = make([]byte, pos+(pos>>2))
			}
			copy(newData, s.data)
			s.data = newData
		}
	}
	shift := byte(1 << (p & 7))
	if d {
		s.data[pos] |= shift
	} else {
		s.data[pos] &^= shift
	}
}
<commit_msg>Small efficiency change<commit_after>// Package boolmap creates a map of bools using bytes for efficiency (needs benchmarking for memory)
package boolmap

// Map is the default boolmap
type Map struct {
	data map[uint64]byte
}

// NewMap returns a new, initialised Map
func NewMap() Map {
	return Map{make(map[uint64]byte)}
}

// Get returns a bool for the specified position
func (m *Map) Get(p uint64) bool {
	return m.data[p>>3]&(1<<(p&7)) != 0
}

// Set sets a bool at the specified position
func (m *Map) Set(p uint64, d bool) {
	shift := byte(1 << (p & 7))
	pos := p >> 3
	var (
		c  byte
		ok bool
	)
	if c, ok = m.data[pos]; !ok && !d {
		return
	}
	if d {
		c |= shift
	} else {
		c &^= shift
	}
	if c == 0 {
		delete(m.data, pos)
	} else {
		m.data[pos] = c
	}
}

// Slice is a slice of bytes representing bools
type Slice struct {
	data []byte
}

// NewSlice returnns a new, initialised Slice
func NewSlice() *Slice {
	return &Slice{make([]byte, 1)}
}

func NewSliceSize(size uint) *Slice {
	return &Slice{make([]byte, size)}
}

// Get returns a bool for the specified position
func (s *Slice) Get(p uint) bool {
	pos := p >> 3
	if pos > uint(len(s.data)) {
		return false
	}
	return s.data[pos]&(1<<(p&7)) != 0
}

// Set sets a bool at the specified position
func (s *Slice) Set(p uint, d bool) {
	pos := p >> 3
	if pos >= uint(len(s.data)) {
		if !d {
			return
		}
		if pos < uint(cap(s.data)) {
			s.data = s.data[:cap(s.data)]
		} else {
			var newData []byte
			if pos < 512 {
				newData = make([]byte, pos<<1)
			} else {
				newData = make([]byte, pos+(pos>>2))
			}
			copy(newData, s.data)
			s.data = newData
		}
	}
	shift := byte(1 << (p & 7))
	if d {
		s.data[pos] |= shift
	} else {
		s.data[pos] &^= shift
	}
}
<|endoftext|>


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

package terminal

import (
	"fmt"
	"strings"
	"sync"

	"nvim-go/config"
	"nvim-go/nvim"
	"nvim-go/pathutil"

	"github.com/garyburd/neovim-go/vim"
	"github.com/juju/errors"
)

var pkgTerminal = "GoTerminal"

var bufName = "__GO_TERMINAL__"

// Terminal represents a Neovim terminal.
type Terminal struct {
	v *vim.Vim
	p *vim.Pipeline

	cmd  []string
	mode string
	// Name terminal buffer name.
	Name string
	// Dir specifies the working directory of the command on terminal.
	Dir string
	// Size open the terminal window size.
	Size int

	cw vim.Window

	*nvim.Buf
}

// NewTerminal return the Neovim terminal buffer.
func NewTerminal(vim *vim.Vim, name string, command []string, mode string) *Terminal {
	return &Terminal{
		v:    vim,
		p:    vim.NewPipeline(),
		Name: name,
		cmd:  command,
		mode: mode,
	}
}

// Create creats the new Neovim terminal buffer.
func (t *Terminal) Create() (err error) {
	t.cw, err = t.v.CurrentWindow()
	if err != nil {
		return err
	}

	switch {
	case t.mode == "split":
		t.Size = int(config.TerminalHeight)
	case t.mode == "vsplit":
		t.Size = int(config.TerminalWidth)
	default:
		err := errors.Errorf("%s mode is not supported", t.mode)
		return nvim.ErrorWrap(t.v, errors.Annotate(err, pkgTerminal))
	}

	option := t.setTerminalOption()
	name := fmt.Sprintf("| terminal %s", strings.Join(t.cmd, " "))
	mode := fmt.Sprintf("%s %d%s", config.TerminalPosition, t.Size, t.mode)
	t.Buf = nvim.NewBuffer(t.v, name, nvim.FiletypeTerminal, mode, option)
	t.Buf.Name = t.Name
	t.Buf.UpdateSyntax(nvim.FiletypeTerminal)

	// Get terminal buffer and windows information.
	t.p.CurrentBuffer(&t.Buffer)
	t.p.CurrentWindow(&t.Window)
	if err := t.p.Wait(); err != nil {
		return err
	}
	defer t.switchFocus()()

	// Cleanup cursor highlighting
	// TODO(zchee): Can use p.ClearBufferHighlight?
	t.p.Command("highlight TermCursor gui=NONE guifg=NONE guibg=NONE")
	t.p.Command("highlight TermCursorNC gui=NONE guifg=NONE guibg=NONE")

	// Set autoclose buffer if the current buffer is only terminal
	// TODO(zchee): convert to rpc way
	t.p.Command("autocmd WinEnter <buffer> if winnr('$') == 1 | quit | endif")

	return t.p.Wait()
}

// Run runs the command in the terminal buffer.
func (t *Terminal) Run(cmd []string) error {
	if t.Dir != "" {
		defer pathutil.Chdir(t.v, t.Dir)()
	}

	if t.Buf != nil && nvim.IsBufferValid(t.v, t.Buffer) {
		defer t.switchFocus()()

		t.v.SetBufferOption(t.Buffer, nvim.BufOptionModified, false)
		t.v.Call("termopen", nil, cmd)
		t.v.SetBufferName(t.Buffer, t.Buf.Name)
	} else {
		t.Create()
	}
	// Workaround for "autocmd BufEnter term://* startinsert"
	if config.TerminalStartInsert {
		t.v.Command("stopinsert")
	}

	return nil
}

// TODO(zchee): flashing when switch the window.
func (t *Terminal) switchFocus() func() {
	t.v.SetCurrentWindow(t.Window)

	return func() {
		t.v.SetCurrentWindow(t.cw)
	}
}

// chdir changes vim current working directory.
// The returned function restores working directory to `getcwd()` result path
// and unlocks the mutex.
func chdir(v *vim.Vim, dir string) func() {
	var (
		m   sync.Mutex
		cwd interface{}
	)
	m.Lock()
	if err := v.Eval("getcwd()", &cwd); err != nil {
		nvim.Echoerr(v, "GoTerminal: %v", err)
	}
	v.ChangeDirectory(dir)
	return func() {
		v.ChangeDirectory(cwd.(string))
		m.Unlock()
	}
}

func (t *Terminal) setTerminalOption() map[nvim.NvimOption]map[string]interface{} {
	option := make(map[nvim.NvimOption]map[string]interface{})
	bufoption := make(map[string]interface{})
	bufvar := make(map[string]interface{})
	windowoption := make(map[string]interface{})

	bufoption[nvim.BufOptionBufhidden] = nvim.BufhiddenDelete
	bufoption[nvim.BufOptionBuflisted] = false
	bufoption[nvim.BufOptionBuftype] = nvim.BuftypeNofile
	bufoption[nvim.BufOptionFiletype] = nvim.FiletypeTerminal
	bufoption[nvim.BufOptionModifiable] = false
	bufoption[nvim.BufOptionSwapfile] = false

	bufvar[nvim.BufVarColorcolumn] = ""

	windowoption[nvim.WinOptionList] = false
	windowoption[nvim.WinOptionNumber] = false
	windowoption[nvim.WinOptionRelativenumber] = false
	windowoption[nvim.WinOptionWinfixheight] = true

	option[nvim.BufferOption] = bufoption
	option[nvim.BufferVar] = bufvar
	option[nvim.WindowOption] = windowoption

	return option
}
<commit_msg>nvim/terminal: Fix NewBuffer and Add nvim.Buf.Create<commit_after>// Copyright 2016 Koichi Shiraishi. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package terminal

import (
	"fmt"
	"strings"
	"sync"

	"nvim-go/config"
	"nvim-go/nvim"
	"nvim-go/pathutil"

	"github.com/garyburd/neovim-go/vim"
	"github.com/juju/errors"
)

var pkgTerminal = "GoTerminal"

var bufName = "__GO_TERMINAL__"

// Terminal represents a Neovim terminal.
type Terminal struct {
	v *vim.Vim
	p *vim.Pipeline

	cmd  []string
	mode string
	// Name terminal buffer name.
	Name string
	// Dir specifies the working directory of the command on terminal.
	Dir string
	// Size open the terminal window size.
	Size int

	cw vim.Window

	*nvim.Buf
}

// NewTerminal return the Neovim terminal buffer.
func NewTerminal(vim *vim.Vim, name string, command []string, mode string) *Terminal {
	return &Terminal{
		v:    vim,
		p:    vim.NewPipeline(),
		Name: name,
		cmd:  command,
		mode: mode,
	}
}

// Create creats the new Neovim terminal buffer.
func (t *Terminal) Create() (err error) {
	t.cw, err = t.v.CurrentWindow()
	if err != nil {
		return err
	}

	switch {
	case t.mode == "split":
		t.Size = int(config.TerminalHeight)
	case t.mode == "vsplit":
		t.Size = int(config.TerminalWidth)
	default:
		err := errors.Errorf("%s mode is not supported", t.mode)
		return nvim.ErrorWrap(t.v, errors.Annotate(err, pkgTerminal))
	}

	option := t.setTerminalOption()
	name := fmt.Sprintf("| terminal %s", strings.Join(t.cmd, " "))
	mode := fmt.Sprintf("%s %d%s", config.TerminalPosition, t.Size, t.mode)
	t.Buf = nvim.NewBuffer(t.v)
	t.Buf.Create(name, nvim.FiletypeTerminal, mode, option)
	t.Buf.Name = t.Name
	t.Buf.UpdateSyntax(nvim.FiletypeTerminal)

	// Get terminal buffer and windows information.
	t.p.CurrentBuffer(&t.Buffer)
	t.p.CurrentWindow(&t.Window)
	if err := t.p.Wait(); err != nil {
		return err
	}
	defer t.switchFocus()()

	// Cleanup cursor highlighting
	// TODO(zchee): Can use p.ClearBufferHighlight?
	t.p.Command("highlight TermCursor gui=NONE guifg=NONE guibg=NONE")
	t.p.Command("highlight TermCursorNC gui=NONE guifg=NONE guibg=NONE")

	// Set autoclose buffer if the current buffer is only terminal
	// TODO(zchee): convert to rpc way
	t.p.Command("autocmd WinEnter <buffer> if winnr('$') == 1 | quit | endif")

	return t.p.Wait()
}

// Run runs the command in the terminal buffer.
func (t *Terminal) Run(cmd []string) error {
	if t.Dir != "" {
		defer pathutil.Chdir(t.v, t.Dir)()
	}

	if t.Buf != nil && nvim.IsBufferValid(t.v, t.Buffer) {
		defer t.switchFocus()()

		t.v.SetBufferOption(t.Buffer, nvim.BufOptionModified, false)
		t.v.Call("termopen", nil, cmd)
		t.v.SetBufferName(t.Buffer, t.Buf.Name)
	} else {
		t.Create()
	}
	// Workaround for "autocmd BufEnter term://* startinsert"
	if config.TerminalStartInsert {
		t.v.Command("stopinsert")
	}

	return nil
}

// TODO(zchee): flashing when switch the window.
func (t *Terminal) switchFocus() func() {
	t.v.SetCurrentWindow(t.Window)

	return func() {
		t.v.SetCurrentWindow(t.cw)
	}
}

// chdir changes vim current working directory.
// The returned function restores working directory to `getcwd()` result path
// and unlocks the mutex.
func chdir(v *vim.Vim, dir string) func() {
	var (
		m   sync.Mutex
		cwd interface{}
	)
	m.Lock()
	if err := v.Eval("getcwd()", &cwd); err != nil {
		nvim.Echoerr(v, "GoTerminal: %v", err)
	}
	v.ChangeDirectory(dir)
	return func() {
		v.ChangeDirectory(cwd.(string))
		m.Unlock()
	}
}

func (t *Terminal) setTerminalOption() map[nvim.NvimOption]map[string]interface{} {
	option := make(map[nvim.NvimOption]map[string]interface{})
	bufoption := make(map[string]interface{})
	bufvar := make(map[string]interface{})
	windowoption := make(map[string]interface{})

	bufoption[nvim.BufOptionBufhidden] = nvim.BufhiddenDelete
	bufoption[nvim.BufOptionBuflisted] = false
	bufoption[nvim.BufOptionBuftype] = nvim.BuftypeNofile
	bufoption[nvim.BufOptionFiletype] = nvim.FiletypeTerminal
	bufoption[nvim.BufOptionModifiable] = false
	bufoption[nvim.BufOptionSwapfile] = false

	bufvar[nvim.BufVarColorcolumn] = ""

	windowoption[nvim.WinOptionList] = false
	windowoption[nvim.WinOptionNumber] = false
	windowoption[nvim.WinOptionRelativenumber] = false
	windowoption[nvim.WinOptionWinfixheight] = true

	option[nvim.BufferOption] = bufoption
	option[nvim.BufferVar] = bufvar
	option[nvim.WindowOption] = windowoption

	return option
}
<|endoftext|>


<commit_before>package stub

import (
	"errors"
	"fmt"
	"os"
	"strconv"
	"strings"
)

//InteractiveOptions renders an interactve prompt at out, in to generate an
//Options from the user. If in or out are nil, StdIn or StdOut will be used
//implicitly.
func InteractiveOptions(in, out *os.File, gameName string) *Options {

	if in == nil {
		in = os.Stdin
	}

	if out == nil {
		out = os.Stdout
	}

	result := &Options{
		Name: gameName,
	}

	if displayName := getString(out, in, "What is the human-readable display name for this game? (e.g. 'Checkers', 'Tic Tac Toe')", result.Name); displayName != result.Name {
		result.DisplayName = displayName
	}

	numPlayersString := getString(out, in, "Range of valid players counts", "2-4")

	min, max, defaultNum, err := parseNumPlayers(numPlayersString)

	if err != nil {
		fmt.Println("That value is not valid: " + err.Error())
		return nil
	}

	result.MinNumPlayers = min
	result.MaxNumPlayers = max
	result.DefaultNumPlayers = defaultNum

	extras := getBool(out, in, "Generate useful extras like a test, CurrentPhase, and CurrentPlayer?", true)

	if !extras {
		result.SuppressExtras()
	}

	client := getBool(out, in, "Generate stub client renderers?", true)

	if !client {
		result.SuppressClient()
	}

	tutorial := getBool(out, in, "Generate extra tutorial content that demonstrate how to wire up more things?", false)

	if tutorial {
		result.EnableTutorials()
	}

	return result
}

func parseNumPlayers(in string) (min, max, defaultNum int, err error) {

	parts := strings.Split(in, "-")

	if len(parts) == 1 {
		return 0, 0, 0, errors.New("Invalid string, no '-'")
	}

	if len(parts) > 2 {
		return 0, 0, 0, errors.New("Too many '-'")
	}

	min, err = strconv.Atoi(strings.TrimSpace(parts[0]))

	if err != nil {
		return 0, 0, 0, errors.New("Min value is not an int: " + err.Error())
	}

	max, err = strconv.Atoi(strings.TrimSpace(parts[1]))

	if err != nil {
		return 0, 0, 0, errors.New("Max value is not an int: " + err.Error())
	}

	return min, max, min, nil

}

func getString(out, in *os.File, prompt, defaultValue string) string {

	if defaultValue != "" {
		prompt += " [" + defaultValue + "]"
	}

	prompt += ": "
	fmt.Fprint(out, prompt)
	var response string
	fmt.Fscanln(in, &response)

	response = strings.TrimSpace(response)

	if defaultValue != "" && response == "" {
		return defaultValue
	}

	return response
}

func getBool(out, in *os.File, message string, defaultVal bool) bool {
	if defaultVal {
		message += " [Y/n]"
	} else {
		message += " [y/N]"
	}

	response := getString(out, in, message, "")

	yesResponses := []string{"Yes", "Y", "yes", "y"}
	noResponses := []string{"No", "N", "no", "n"}

	if defaultVal {
		for _, responseToTest := range noResponses {
			if response == responseToTest {
				return false
			}
		}
		return true
	} else {
		for _, responseToTest := range yesResponses {
			if response == responseToTest {
				return true
			}
		}
	}
	return false
}
<commit_msg>Ask for description interactively. Part of #663.<commit_after>package stub

import (
	"errors"
	"fmt"
	"os"
	"strconv"
	"strings"
)

//InteractiveOptions renders an interactve prompt at out, in to generate an
//Options from the user. If in or out are nil, StdIn or StdOut will be used
//implicitly.
func InteractiveOptions(in, out *os.File, gameName string) *Options {

	if in == nil {
		in = os.Stdin
	}

	if out == nil {
		out = os.Stdout
	}

	result := &Options{
		Name: gameName,
	}

	if displayName := getString(out, in, "What is the human-readable display name for this game? (e.g. 'Checkers', 'Tic Tac Toe')", result.Name); displayName != result.Name {
		result.DisplayName = displayName
	}

	if description := getString(out, in, "What is the description of the game?", ""); description != "" {
		result.Description = description
	}

	numPlayersString := getString(out, in, "Range of valid players counts", "2-4")

	min, max, defaultNum, err := parseNumPlayers(numPlayersString)

	if err != nil {
		fmt.Println("That value is not valid: " + err.Error())
		return nil
	}

	result.MinNumPlayers = min
	result.MaxNumPlayers = max
	result.DefaultNumPlayers = defaultNum

	extras := getBool(out, in, "Generate useful extras like a test, CurrentPhase, and CurrentPlayer?", true)

	if !extras {
		result.SuppressExtras()
	}

	client := getBool(out, in, "Generate stub client renderers?", true)

	if !client {
		result.SuppressClient()
	}

	tutorial := getBool(out, in, "Generate extra tutorial content that demonstrate how to wire up more things?", false)

	if tutorial {
		result.EnableTutorials()
	}

	return result
}

func parseNumPlayers(in string) (min, max, defaultNum int, err error) {

	parts := strings.Split(in, "-")

	if len(parts) == 1 {
		return 0, 0, 0, errors.New("Invalid string, no '-'")
	}

	if len(parts) > 2 {
		return 0, 0, 0, errors.New("Too many '-'")
	}

	min, err = strconv.Atoi(strings.TrimSpace(parts[0]))

	if err != nil {
		return 0, 0, 0, errors.New("Min value is not an int: " + err.Error())
	}

	max, err = strconv.Atoi(strings.TrimSpace(parts[1]))

	if err != nil {
		return 0, 0, 0, errors.New("Max value is not an int: " + err.Error())
	}

	return min, max, min, nil

}

func getString(out, in *os.File, prompt, defaultValue string) string {

	if defaultValue != "" {
		prompt += " [" + defaultValue + "]"
	}

	prompt += ": "
	fmt.Fprint(out, prompt)
	var response string
	fmt.Fscanln(in, &response)

	response = strings.TrimSpace(response)

	if defaultValue != "" && response == "" {
		return defaultValue
	}

	return response
}

func getBool(out, in *os.File, message string, defaultVal bool) bool {
	if defaultVal {
		message += " [Y/n]"
	} else {
		message += " [y/N]"
	}

	response := getString(out, in, message, "")

	yesResponses := []string{"Yes", "Y", "yes", "y"}
	noResponses := []string{"No", "N", "no", "n"}

	if defaultVal {
		for _, responseToTest := range noResponses {
			if response == responseToTest {
				return false
			}
		}
		return true
	} else {
		for _, responseToTest := range yesResponses {
			if response == responseToTest {
				return true
			}
		}
	}
	return false
}
<|endoftext|>