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

package casbin

// GetRolesForUser gets the roles that a user has.
func (e *SyncedEnforcer) GetRolesForUser(name string) ([]string, error) {
	e.m.RLock()
	defer e.m.RUnlock()
	return e.Enforcer.GetRolesForUser(name)
}

// GetUsersForRole gets the users that has a role.
func (e *SyncedEnforcer) GetUsersForRole(name string) ([]string, error) {
	e.m.RLock()
	defer e.m.RUnlock()
	return e.Enforcer.GetUsersForRole(name)
}

// HasRoleForUser determines whether a user has a role.
func (e *SyncedEnforcer) HasRoleForUser(name string, role string) (bool, error) {
	e.m.RLock()
	defer e.m.RUnlock()
	return e.Enforcer.HasRoleForUser(name, role)
}

// AddRoleForUser adds a role for a user.
// Returns false if the user already has the role (aka not affected).
func (e *SyncedEnforcer) AddRoleForUser(user string, role string) (bool, error) {
	e.m.Lock()
	defer e.m.Unlock()
	return e.Enforcer.AddRoleForUser(user, role)
}

// DeleteRoleForUser deletes a role for a user.
// Returns false if the user does not have the role (aka not affected).
func (e *SyncedEnforcer) DeleteRoleForUser(user string, role string) (bool, error) {
	e.m.Lock()
	defer e.m.Unlock()
	return e.Enforcer.DeleteRoleForUser(user, role)
}

// DeleteRolesForUser deletes all roles for a user.
// Returns false if the user does not have any roles (aka not affected).
func (e *SyncedEnforcer) DeleteRolesForUser(user string) (bool, error) {
	e.m.Lock()
	defer e.m.Unlock()
	return e.Enforcer.DeleteRolesForUser(user)
}

// DeleteUser deletes a user.
// Returns false if the user does not exist (aka not affected).
func (e *SyncedEnforcer) DeleteUser(user string) (bool, error) {
	e.m.Lock()
	defer e.m.Unlock()
	return e.Enforcer.DeleteUser(user)
}

// DeleteRole deletes a role.
func (e *SyncedEnforcer) DeleteRole(role string) {
	e.m.Lock()
	defer e.m.Unlock()
	e.Enforcer.DeleteRole(role)
}

// DeletePermission deletes a permission.
// Returns false if the permission does not exist (aka not affected).
func (e *SyncedEnforcer) DeletePermission(permission ...string) (bool, error) {
	e.m.Lock()
	defer e.m.Unlock()
	return e.Enforcer.DeletePermission(permission...)
}

// AddPermissionForUser adds a permission for a user or role.
// Returns false if the user or role already has the permission (aka not affected).
func (e *SyncedEnforcer) AddPermissionForUser(user string, permission ...string) (bool, error) {
	e.m.Lock()
	defer e.m.Unlock()
	return e.Enforcer.AddPermissionForUser(user, permission...)
}

// DeletePermissionForUser deletes a permission for a user or role.
// Returns false if the user or role does not have the permission (aka not affected).
func (e *SyncedEnforcer) DeletePermissionForUser(user string, permission ...string) (bool, error) {
	e.m.Lock()
	defer e.m.Unlock()
	return e.Enforcer.DeletePermissionForUser(user, permission...)
}

// DeletePermissionsForUser deletes permissions for a user or role.
// Returns false if the user or role does not have any permissions (aka not affected).
func (e *SyncedEnforcer) DeletePermissionsForUser(user string) (bool, error) {
	e.m.Lock()
	defer e.m.Unlock()
	return e.Enforcer.DeletePermissionsForUser(user)
}

// GetPermissionsForUser gets permissions for a user or role.
func (e *SyncedEnforcer) GetPermissionsForUser(user string) [][]string {
	e.m.RLock()
	defer e.m.RUnlock()
	return e.Enforcer.GetPermissionsForUser(user)
}

// HasPermissionForUser determines whether a user has a permission.
func (e *SyncedEnforcer) HasPermissionForUser(user string, permission ...string) bool {
	e.m.RLock()
	defer e.m.RUnlock()
	return e.Enforcer.HasPermissionForUser(user, permission...)
}
<commit_msg>Fix the bug that SyncedEnforcer DeleteRole() doesn't return error<commit_after>// Copyright 2017 The casbin Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package casbin

// GetRolesForUser gets the roles that a user has.
func (e *SyncedEnforcer) GetRolesForUser(name string) ([]string, error) {
	e.m.RLock()
	defer e.m.RUnlock()
	return e.Enforcer.GetRolesForUser(name)
}

// GetUsersForRole gets the users that has a role.
func (e *SyncedEnforcer) GetUsersForRole(name string) ([]string, error) {
	e.m.RLock()
	defer e.m.RUnlock()
	return e.Enforcer.GetUsersForRole(name)
}

// HasRoleForUser determines whether a user has a role.
func (e *SyncedEnforcer) HasRoleForUser(name string, role string) (bool, error) {
	e.m.RLock()
	defer e.m.RUnlock()
	return e.Enforcer.HasRoleForUser(name, role)
}

// AddRoleForUser adds a role for a user.
// Returns false if the user already has the role (aka not affected).
func (e *SyncedEnforcer) AddRoleForUser(user string, role string) (bool, error) {
	e.m.Lock()
	defer e.m.Unlock()
	return e.Enforcer.AddRoleForUser(user, role)
}

// DeleteRoleForUser deletes a role for a user.
// Returns false if the user does not have the role (aka not affected).
func (e *SyncedEnforcer) DeleteRoleForUser(user string, role string) (bool, error) {
	e.m.Lock()
	defer e.m.Unlock()
	return e.Enforcer.DeleteRoleForUser(user, role)
}

// DeleteRolesForUser deletes all roles for a user.
// Returns false if the user does not have any roles (aka not affected).
func (e *SyncedEnforcer) DeleteRolesForUser(user string) (bool, error) {
	e.m.Lock()
	defer e.m.Unlock()
	return e.Enforcer.DeleteRolesForUser(user)
}

// DeleteUser deletes a user.
// Returns false if the user does not exist (aka not affected).
func (e *SyncedEnforcer) DeleteUser(user string) (bool, error) {
	e.m.Lock()
	defer e.m.Unlock()
	return e.Enforcer.DeleteUser(user)
}

// DeleteRole deletes a role.
// Returns false if the role does not exist (aka not affected).
func (e *SyncedEnforcer) DeleteRole(role string) (bool, error) {
	e.m.Lock()
	defer e.m.Unlock()
	return e.Enforcer.DeleteRole(role)
}

// DeletePermission deletes a permission.
// Returns false if the permission does not exist (aka not affected).
func (e *SyncedEnforcer) DeletePermission(permission ...string) (bool, error) {
	e.m.Lock()
	defer e.m.Unlock()
	return e.Enforcer.DeletePermission(permission...)
}

// AddPermissionForUser adds a permission for a user or role.
// Returns false if the user or role already has the permission (aka not affected).
func (e *SyncedEnforcer) AddPermissionForUser(user string, permission ...string) (bool, error) {
	e.m.Lock()
	defer e.m.Unlock()
	return e.Enforcer.AddPermissionForUser(user, permission...)
}

// DeletePermissionForUser deletes a permission for a user or role.
// Returns false if the user or role does not have the permission (aka not affected).
func (e *SyncedEnforcer) DeletePermissionForUser(user string, permission ...string) (bool, error) {
	e.m.Lock()
	defer e.m.Unlock()
	return e.Enforcer.DeletePermissionForUser(user, permission...)
}

// DeletePermissionsForUser deletes permissions for a user or role.
// Returns false if the user or role does not have any permissions (aka not affected).
func (e *SyncedEnforcer) DeletePermissionsForUser(user string) (bool, error) {
	e.m.Lock()
	defer e.m.Unlock()
	return e.Enforcer.DeletePermissionsForUser(user)
}

// GetPermissionsForUser gets permissions for a user or role.
func (e *SyncedEnforcer) GetPermissionsForUser(user string) [][]string {
	e.m.RLock()
	defer e.m.RUnlock()
	return e.Enforcer.GetPermissionsForUser(user)
}

// HasPermissionForUser determines whether a user has a permission.
func (e *SyncedEnforcer) HasPermissionForUser(user string, permission ...string) bool {
	e.m.RLock()
	defer e.m.RUnlock()
	return e.Enforcer.HasPermissionForUser(user, permission...)
}
<|endoftext|>


<commit_before>// Package api contains the fundamental data types used by Go-NEB.
//
// Most HTTP API calls and/or config file sections are just ways of representing these
// data types.
//
// See also
//
// Package "api.handlers" for information on the HTTP API calls.
package api

import (
	"encoding/json"
	"errors"
	"net/url"
)

// ConfigureAuthRealmRequest is a request to /configureAuthRealm
type ConfigureAuthRealmRequest struct {
	// An arbitrary unique identifier for this auth realm. This can be anything.
	// Using the same ID will REPLACE the entire AuthRealm with the new information.
	ID string
	// The type of auth realm. This determines which code is loaded to execute the
	// auth realm. It must be a known type.
	Type string
	// AuthRealm specific config information. See the docs for the auth realm you're interested in.
	Config json.RawMessage
}

// RequestAuthSessionRequest is a request to /requestAuthSession
type RequestAuthSessionRequest struct {
	// The realm ID to request a new auth session on. The realm MUST already exist.
	RealmID string
	// The user ID of user requesting the auth session. If the auth is successful,
	// this user ID will be associated with the third-party credentials obtained.
	UserID string
	// AuthRealm specific config information. See the docs for the auth realm you're interested in.
	Config json.RawMessage
}

// ConfigureServiceRequest is a request to /configureService
type ConfigureServiceRequest struct {
	// An arbitrary unique identifier for this service. This can be anything.
	// Using the same ID will REPLACE the entire Service with the new information.
	ID string
	// The type of service. This determines which code is loaded to execute the
	// service. It must be a known type.
	Type string
	// The user ID of the configured client who will be controlled by this service.
	// The user MUST already be configured.
	UserID string
	// Service-specific config information. See the docs for the service you're interested in.
	Config json.RawMessage
}

// A ClientConfig contains the configuration information for a matrix client so that
// Go-NEB can drive it. It forms the HTTP body to /configureClient requests.
type ClientConfig struct {
	// The matrix User ID to connect with. E.g. @alice:matrix.org
	UserID string
	// A URL with the host and port of the matrix server. E.g. https://matrix.org:8448
	HomeserverURL string
	// The matrix access token to authenticate the requests with.
	AccessToken string
	// True to start a sync stream for this user. If false, no /sync goroutine will be
	// created and this client will be unable to receive new events from Matrix. For services
	// which only SEND events into Matrix, it may be desirable to set Sync to false to reduce the
	// number of goroutines Go-NEB has to maintain. For services which respond to !commands,
	// Sync MUST be set to true in order to receive those commands.
	Sync bool
	// True to automatically join every room this client is invited to.
	// This is desirable for services which have !commands as that means anyone can pull the bot
	// into the room. It is up to the service to decide which, if any, users to respond to however.
	AutoJoinRooms bool
	// The desired display name for this client.
	// This does not automatically set the display name for this client. See /configureClient.
	DisplayName string
}

// SessionRequests are usually multi-stage things so this type only exists for the config form
// for use with ConfigFile.
type SessionRequest struct {
	SessionID string
	RealmID   string
	UserID    string
	Config    json.RawMessage
}

// ConfigFile represents config.sample.yaml
type ConfigFile struct {
	Clients  []ClientConfig
	Realms   []ConfigureAuthRealmRequest
	Services []ConfigureServiceRequest
	Sessions []SessionRequest
}

// Check validates the /configureService request
func (c *ConfigureServiceRequest) Check() error {
	if c.ID == "" || c.Type == "" || c.UserID == "" || c.Config == nil {
		return errors.New(`Must supply an "ID", a "Type", a "UserID" and a "Config"`)
	}
	return nil
}

// Check validates the /configureAuthRealm request
func (c *ConfigureAuthRealmRequest) Check() error {
	if c.ID == "" || c.Type == "" || c.Config == nil {
		return errors.New(`Must supply a "ID", a "Type" and a "Config"`)
	}
	return nil
}

// Check validates the session config request
func (c *SessionRequest) Check() error {
	if c.SessionID == "" || c.UserID == "" || c.RealmID == "" || c.Config == nil {
		return errors.New(`Must supply a "SessionID", a "RealmID", a "UserID" and a "Config"`)
	}
	return nil
}

// Check that the client has supplied the correct fields.
func (c *ClientConfig) Check() error {
	if c.UserID == "" || c.HomeserverURL == "" || c.AccessToken == "" {
		return errors.New(`Must supply a "UserID", a "HomeserverURL", and an "AccessToken"`)
	}
	if _, err := url.Parse(c.HomeserverURL); err != nil {
		return err
	}
	return nil
}

// Check that the request is valid.
func (r *RequestAuthSessionRequest) Check() error {
	if r.UserID == "" || r.RealmID == "" || r.Config == nil {
		return errors.New(`Must supply a "UserID", a "RealmID" and a "Config"`)
	}
	return nil
}
<commit_msg>Review comments<commit_after>// Package api contains the fundamental data types used by Go-NEB.
//
// Most HTTP API calls and/or config file sections are just ways of representing these
// data types.
//
// See also
//
// Package "api.handlers" for information on the HTTP API calls.
package api

import (
	"encoding/json"
	"errors"
	"net/url"
)

// ConfigureAuthRealmRequest is a request to /configureAuthRealm
type ConfigureAuthRealmRequest struct {
	// An arbitrary unique identifier for this auth realm. This can be anything.
	// Using an existing ID will REPLACE the entire existing AuthRealm with the new information.
	ID string
	// The type of auth realm. This determines which code is loaded to execute the
	// auth realm. It must be a known type. E.g. "github".
	Type string
	// AuthRealm specific config information. See the docs for the auth realm you're interested in.
	Config json.RawMessage
}

// RequestAuthSessionRequest is a request to /requestAuthSession
type RequestAuthSessionRequest struct {
	// The realm ID to request a new auth session on. The realm MUST already exist.
	RealmID string
	// The Matrix user ID requesting the auth session. If the auth is successful,
	// this user ID will be associated with the third-party credentials obtained.
	UserID string
	// AuthRealm specific config information. See the docs for the auth realm you're interested in.
	Config json.RawMessage
}

// ConfigureServiceRequest is a request to /configureService
type ConfigureServiceRequest struct {
	// An arbitrary unique identifier for this service. This can be anything.
	// Using an existing ID will REPLACE the entire Service with the new information.
	ID string
	// The type of service. This determines which code is loaded to execute the
	// service. It must be a known type, e.g. "github".
	Type string
	// The user ID of the configured client that this service will use to communicate with Matrix.
	// The user MUST already be configured.
	UserID string
	// Service-specific config information. See the docs for the service you're interested in.
	Config json.RawMessage
}

// A ClientConfig contains the configuration information for a matrix client so that
// Go-NEB can drive it. It forms the HTTP body to /configureClient requests.
type ClientConfig struct {
	// The matrix User ID to connect with. E.g. @alice:matrix.org
	UserID string
	// A URL with the host and port of the matrix server. E.g. https://matrix.org:8448
	HomeserverURL string
	// The matrix access token to authenticate the requests with.
	AccessToken string
	// True to start a sync stream for this user. If false, no /sync goroutine will be
	// created and this client won't listen for new events from Matrix. For services
	// which only SEND events into Matrix, it may be desirable to set Sync to false to reduce the
	// number of goroutines Go-NEB has to maintain. For services which respond to !commands,
	// Sync MUST be set to true in order to receive those commands.
	Sync bool
	// True to automatically join every room this client is invited to.
	// This is desirable for services which have !commands as that means anyone can pull the bot
	// into the room. It is up to the service to decide which, if any, users to respond to however.
	AutoJoinRooms bool
	// The desired display name for this client.
	// This does not automatically set the display name for this client. See /configureClient.
	DisplayName string
}

// Sessions contain the complete auth session information for a given user on a given realm.
// They are created for use with ConfigFile.
type Session struct {
	SessionID string
	RealmID   string
	UserID    string
	Config    json.RawMessage
}

// ConfigFile represents config.sample.yaml
type ConfigFile struct {
	Clients  []ClientConfig
	Realms   []ConfigureAuthRealmRequest
	Services []ConfigureServiceRequest
	Sessions []Session
}

// Check validates the /configureService request
func (c *ConfigureServiceRequest) Check() error {
	if c.ID == "" || c.Type == "" || c.UserID == "" || c.Config == nil {
		return errors.New(`Must supply an "ID", a "Type", a "UserID" and a "Config"`)
	}
	return nil
}

// Check validates the /configureAuthRealm request
func (c *ConfigureAuthRealmRequest) Check() error {
	if c.ID == "" || c.Type == "" || c.Config == nil {
		return errors.New(`Must supply a "ID", a "Type" and a "Config"`)
	}
	return nil
}

// Check validates the session config request
func (c *Session) Check() error {
	if c.SessionID == "" || c.UserID == "" || c.RealmID == "" || c.Config == nil {
		return errors.New(`Must supply a "SessionID", a "RealmID", a "UserID" and a "Config"`)
	}
	return nil
}

// Check that the client has supplied the correct fields.
func (c *ClientConfig) Check() error {
	if c.UserID == "" || c.HomeserverURL == "" || c.AccessToken == "" {
		return errors.New(`Must supply a "UserID", a "HomeserverURL", and an "AccessToken"`)
	}
	if _, err := url.Parse(c.HomeserverURL); err != nil {
		return err
	}
	return nil
}

// Check that the request is valid.
func (r *RequestAuthSessionRequest) Check() error {
	if r.UserID == "" || r.RealmID == "" || r.Config == nil {
		return errors.New(`Must supply a "UserID", a "RealmID" and a "Config"`)
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"os"
	"sort"
	"strings"
	"sync"
	"text/template"

	"github.com/anaminus/drill"
	"github.com/anaminus/drill/filesys"
	"github.com/anaminus/rbxmk/fragments"
	"github.com/anaminus/rbxmk/rbxmk/htmldrill"
	"github.com/anaminus/rbxmk/rbxmk/term"
	terminal "golang.org/x/term"
)

// Language determines the language of documentation text.
var Language = "en-us"

// panicLanguage writes the languages that have been embedded, then panics.
func panicLanguage() {
	langs := make([]string, 0, len(fragments.Languages))
	for lang := range fragments.Languages {
		langs = append(langs, lang)
	}
	sort.Strings(langs)
	if len(langs) == 0 {
		fmt.Fprintln(os.Stderr, "no languages are embedded")
	} else {
		fmt.Fprintln(os.Stderr, "the following languages are embedded:")
		for _, lang := range langs {
			fmt.Fprintf(os.Stderr, "\t%s\n", lang)
		}
	}
	panic(fmt.Sprintf("unsupported language %q", Language))
}

func initDocs() drill.Node {
	termWidth, _, _ := terminal.GetSize(int(os.Stdout.Fd()))
	lang, ok := fragments.Languages[Language]
	if !ok {
		panicLanguage()
	}
	f, err := filesys.NewFS(lang, filesys.Handlers{
		{Pattern: "*.html", Func: htmldrill.NewHandler(
			htmldrill.WithRenderer(term.Renderer{Width: termWidth, TabSize: 4}.Render),
		)},
	})
	if err != nil {
		panic(err)
	}
	node := f.UnorderedChild(Language)
	if node == nil {
		panicLanguage()
	}
	return node
}

var docfs = initDocs()
var docMut sync.RWMutex
var docCache = map[string]*htmldrill.Node{}
var docFailed = map[string]struct{}{}
var docSeen = map[string]struct{}{}

// DocFragments returns a list of requested fragments.
func DocFragments() []string {
	docMut.RLock()
	defer docMut.RUnlock()
	frags := make([]string, 0, len(docSeen))
	for frag := range docSeen {
		frags = append(frags, frag)
	}
	sort.Strings(frags)
	return frags
}

// UnresolvedFragments writes to stderr a list of fragment references that
// failed to resolve. Panics if any references failed.
func UnresolvedFragments() {
	if len(docFailed) == 0 {
		return
	}
	refs := make([]string, 0, len(docFailed))
	for ref := range docFailed {
		refs = append(refs, ref)
	}
	sort.Strings(refs)
	var s strings.Builder
	fmt.Fprintf(&s, "unresolved fragments for %q:\n", Language)
	for _, ref := range refs {
		fmt.Fprintf(&s, "\t%s\n", ref)
	}
	fmt.Fprintf(&s, "\nversion: %s", VersionString())
	panic(s.String())
}

// parseFragRef receives a fragment reference and converts it to a file path.
//
// A fragment reference is like a regular file path, except that filesep is the
// separator that descends into a file. After the first occurrence of filesep,
// the preceding element is appended with the given suffix, and then descending
// continues.
//
// For example, with ".md" and ":":
//
//     libraries/roblox/types/Region3:Properties/CFrame/Description
//
// is split into the following elements:
//
//     libraries, roblox, types, Region3.md, Properties, CFrame, Description
//
// The file portion of the fragment reference is converted to lowercase.
//
// If no separator was found in the reference, then the final element will have
// suffix appended unless dir is true.
//
// infile returns whether the reference drilled into a file.
func parseFragRef(s, suffix string, filesep rune, dir bool) (items []string, infile bool) {
	if s == "" {
		return []string{}, false
	}
	i := strings.IndexRune(s, filesep)
	if i < 0 {
		if dir {
			return strings.Split(strings.ToLower(s), "/"), false
		} else {
			return strings.Split(strings.ToLower(s)+suffix, "/"), false
		}
	}
	items = make([]string, 0, strings.Count(s, "/")+1)
	items = append(items, strings.Split(strings.ToLower(s[:i])+suffix, "/")...)
	items = append(items, strings.Split(s[i+1:], "/")...)
	return items, true
}

type FuncMap = template.FuncMap

var docTmplFuncs = FuncMap{
	// List of top-level fragment topics.
	"Topics": func() string {
		return "\n\t" + strings.Join(ListFragments(""), "\n\t")
	},
}

func executeDocTmpl(fragref, tmplText string, data interface{}, funcs FuncMap) string {
	t := template.New("root")
	t.Funcs(docTmplFuncs)
	t.Funcs(funcs)
	t, err := t.Parse(tmplText)
	if err != nil {
		panic(fmt.Errorf("parse %q: %w", fragref, err))
	}
	var buf bytes.Buffer
	err = t.Execute(&buf, data)
	if err != nil {
		panic(fmt.Errorf("execute %q: %w", fragref, err))
	}
	return strings.TrimSpace(buf.String())
}

type FragOptions struct {
	// Data included with the executed template.
	TmplData interface{}
	// Functions included with the executed template.
	TmplFuncs FuncMap
	// Renderer used if a node is htmldrill.Node.
	Renderer htmldrill.Renderer
}

// Doc returns the content of the fragment referred to by fragref. The given
// path is marked to be returned by DocFragments. If no content was found, then
// a string indicating an unresolved reference is returned.
//
// Doc should only be used to capture additional fragment references.
// ResolveFragment can be used to resolve a reference without marking it.
//
// The content of the fragment executed as a template with docTmplFuncs included
// as functions.
func Doc(fragref string) string {
	return DocWith(fragref, FragOptions{})
}

// DocWith is like Doc, but with configurable options.
func DocWith(fragref string, opt FragOptions) string {
	docMut.Lock()
	defer docMut.Unlock()
	docSeen[fragref] = struct{}{}
	node, _ := resolveFragmentNode(fragref, false)
	if node == nil {
		docFailed[fragref] = struct{}{}
		return "{" + Language + ":" + fragref + "}"
	}
	if opt.Renderer != nil {
		if n, ok := node.(*htmldrill.Node); ok {
			node = n.WithRenderer(opt.Renderer)
		}
	}
	tmplText := strings.TrimSpace(node.Fragment())
	return executeDocTmpl(fragref, tmplText, opt.TmplData, opt.TmplFuncs)
}

// DocFlag returns the content for a flag by configuring the renderer with 0
// width, so that it can be properly formatted by the usage template.
func DocFlag(fragref string) string {
	return DocWith(fragref, FragOptions{Renderer: term.Renderer{Width: 0, TabSize: 4}.Render})
}

// ResolveFragment returns the content of the fragment referred to by fragref.
// Returns an empty string if no content was found.
//
// The content of the fragment executed as a template with docTmplFuncs included
// as functions.
func ResolveFragment(fragref string) string {
	return ResolveFragmentWith(fragref, FragOptions{})
}

// ResolveFragmentWith is like ResolveFragment, but with configurable options.
func ResolveFragmentWith(fragref string, opt FragOptions) string {
	docMut.Lock()
	defer docMut.Unlock()
	node, _ := resolveFragmentNode(fragref, false)
	if node == nil {
		return ""
	}
	if opt.Renderer != nil {
		if n, ok := node.(*htmldrill.Node); ok {
			node = n.WithRenderer(opt.Renderer)
		}
	}
	tmplText := strings.TrimSpace(node.Fragment())
	return executeDocTmpl(fragref, tmplText, opt.TmplData, opt.TmplFuncs)
}

const FragSep = ':'
const FragSuffix = ".html"

func ListFragments(fragref string) []string {
	frags := map[string]struct{}{}
	if fragref == "" {
		node, _ := resolveFragmentNode("", false)
		switch node := node.(type) {
		case drill.UnorderedBranch:
			children := node.UnorderedChildren()
			for name := range children {
				if name != "" {
					name = strings.TrimSuffix(name, FragSuffix)
					frags[name] = struct{}{}
				}
			}
		}
	} else {
		node, infile := resolveFragmentNode(fragref, false)
		switch node := node.(type) {
		case drill.UnorderedBranch:
			children := node.UnorderedChildren()
			for name := range children {
				if name != "" {
					if infile {
						name = strings.TrimSuffix(name, FragSuffix)
						frags["/"+name] = struct{}{}
					} else {
						frags[string(FragSep)+name] = struct{}{}
					}
				}
			}
		}
		if node == nil || !infile {
			node, infile := resolveFragmentNode(fragref, true)
			switch node := node.(type) {
			case drill.UnorderedBranch:
				children := node.UnorderedChildren()
				for name := range children {
					if name != "" {
						if !infile {
							name = strings.TrimSuffix(name, FragSuffix)
						}
						frags["/"+name] = struct{}{}
					}
				}
			}
		}
	}
	list := make([]string, 0, len(frags))
	for frag := range frags {
		list = append(list, frag)
	}
	sort.Strings(list)
	return list
}

// resolveFragmentNode resolves a fragment reference into a drill Node by
// walking through the components of the reference.
func resolveFragmentNode(fragref string, dir bool) (n drill.Node, infile bool) {
	n = docfs
	var path string
	names, infile := parseFragRef(fragref, FragSuffix, FragSep, dir)
	for _, name := range names {
		if name == "" {
			return nil, false
		}
		path += "/" + name
		if node, ok := docCache[path]; ok {
			n = node
		} else {
			switch v := n.(type) {
			case drill.UnorderedBranch:
				n = v.UnorderedChild(name)
			default:
				return nil, false
			}
			if node, ok := n.(*htmldrill.Node); ok {
				docCache[path] = node
			}
		}
	}
	return n, infile
}

// ErrorFrag returns an error according to the fragment section of the given
// name. The result is passed to fmt.Errorf with args.
func ErrorFrag(name string, args ...interface{}) error {
	format := ResolveFragment("Errors:" + name)
	return fmt.Errorf(strings.TrimSpace(format), args...)
}

// FormatFrag returns a formatted string according to the fragment of the given
// reference. The result is passed to fmt.Sprintf with args.
func FormatFrag(fragref string, args ...interface{}) string {
	format := ResolveFragment(fragref)
	return fmt.Sprintf(strings.TrimSpace(format), args...)
}
<commit_msg>Refactor template processing.<commit_after>package main

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"sort"
	"strings"
	"sync"
	"text/template"

	"github.com/PuerkitoBio/goquery"
	"github.com/anaminus/drill"
	"github.com/anaminus/drill/filesys"
	"github.com/anaminus/rbxmk/fragments"
	"github.com/anaminus/rbxmk/rbxmk/htmldrill"
	"github.com/anaminus/rbxmk/rbxmk/term"
	"golang.org/x/net/html"
	terminal "golang.org/x/term"
)

// Fragments are formatted as HTML templates. They are parsed and rendered as
// regular HTML by the drill, but this is lossless enough that that template
// directives survive the process.
//
// More technically, a particular fragment file is a concatenation of a number
// of independently evaluated template fragments. That is, the data in one
// section does not necessarily correspond to the data in another section.
//
// The resulting render is a HTML template, ready for evaluation. Data produced
// by the execution are expected to be formatted in HTML.
//
// The final result of the template is regular HTML. This result is passed to a
// configured renderer that converts it to a finalized format.

// Language determines the language of documentation text.
var Language = "en-us"

// panicLanguage writes the languages that have been embedded, then panics.
func panicLanguage() {
	langs := make([]string, 0, len(fragments.Languages))
	for lang := range fragments.Languages {
		langs = append(langs, lang)
	}
	sort.Strings(langs)
	if len(langs) == 0 {
		fmt.Fprintln(os.Stderr, "no languages are embedded")
	} else {
		fmt.Fprintln(os.Stderr, "the following languages are embedded:")
		for _, lang := range langs {
			fmt.Fprintf(os.Stderr, "\t%s\n", lang)
		}
	}
	panic(fmt.Sprintf("unsupported language %q", Language))
}

func initDocs() drill.Node {
	lang, ok := fragments.Languages[Language]
	if !ok {
		panicLanguage()
	}
	f, err := filesys.NewFS(lang, filesys.Handlers{
		{Pattern: "*.html", Func: htmldrill.NewHandler()},
	})
	if err != nil {
		panic(err)
	}
	node := f.UnorderedChild(Language)
	if node == nil {
		panicLanguage()
	}
	return node
}

var docfs = initDocs()
var docMut sync.RWMutex
var docCache = map[string]*htmldrill.Node{}
var docFailed = map[string]struct{}{}
var docSeen = map[string]struct{}{}

// DocFragments returns a list of requested fragments.
func DocFragments() []string {
	docMut.RLock()
	defer docMut.RUnlock()
	frags := make([]string, 0, len(docSeen))
	for frag := range docSeen {
		frags = append(frags, frag)
	}
	sort.Strings(frags)
	return frags
}

// UnresolvedFragments writes to stderr a list of fragment references that
// failed to resolve. Panics if any references failed.
func UnresolvedFragments() {
	if len(docFailed) == 0 {
		return
	}
	refs := make([]string, 0, len(docFailed))
	for ref := range docFailed {
		refs = append(refs, ref)
	}
	sort.Strings(refs)
	var s strings.Builder
	fmt.Fprintf(&s, "unresolved fragments for %q:\n", Language)
	for _, ref := range refs {
		fmt.Fprintf(&s, "\t%s\n", ref)
	}
	fmt.Fprintf(&s, "\nversion: %s", VersionString())
	panic(s.String())
}

// parseFragRef receives a fragment reference and converts it to a file path.
//
// A fragment reference is like a regular file path, except that filesep is the
// separator that descends into a file. After the first occurrence of filesep,
// the preceding element is appended with the given suffix, and then descending
// continues.
//
// For example, with ".md" and ":":
//
//     libraries/roblox/types/Region3:Properties/CFrame/Description
//
// is split into the following elements:
//
//     libraries, roblox, types, Region3.md, Properties, CFrame, Description
//
// The file portion of the fragment reference is converted to lowercase.
//
// If no separator was found in the reference, then the final element will have
// suffix appended unless dir is true.
//
// infile returns whether the reference drilled into a file.
func parseFragRef(s, suffix string, filesep rune, dir bool) (items []string, infile bool) {
	if s == "" {
		return []string{}, false
	}
	i := strings.IndexRune(s, filesep)
	if i < 0 {
		if dir {
			return strings.Split(strings.ToLower(s), "/"), false
		} else {
			return strings.Split(strings.ToLower(s)+suffix, "/"), false
		}
	}
	items = make([]string, 0, strings.Count(s, "/")+1)
	items = append(items, strings.Split(strings.ToLower(s[:i])+suffix, "/")...)
	items = append(items, strings.Split(s[i+1:], "/")...)
	return items, true
}

type Renderer = func(w io.Writer, s *goquery.Selection) error

type FuncMap = template.FuncMap

var docTmplFuncs = FuncMap{
	// List of top-level fragment topics.
	"Topics": func() string {
		return "\n\t" + strings.Join(ListFragments(""), "\n\t")
	},
}

type FragOptions struct {
	// Data included with the executed template.
	TmplData interface{}
	// Functions included with the executed template.
	TmplFuncs FuncMap
	// Renderer used if a node is htmldrill.Node.
	Renderer Renderer
}

// ExecuteFragTmpl renders converts the result of node, in template format, to a
// final rendering.
func ExecuteFragTmpl(fragref string, node drill.Node, opt FragOptions) string {
	// Parse template.
	tmplText := strings.TrimSpace(node.Fragment())
	t := template.New("root")
	t.Funcs(docTmplFuncs)
	t.Funcs(opt.TmplFuncs)
	t, err := t.Parse(tmplText)
	if err != nil {
		panic(fmt.Errorf("parse template %q: %w", fragref, err))
	}

	// Execute template.
	var buf bytes.Buffer
	err = t.Execute(&buf, opt.TmplData)
	if err != nil {
		panic(fmt.Errorf("execute template %q: %w", fragref, err))
	}

	// If no renderer, return directly as HTML.
	if opt.Renderer == nil {
		return strings.TrimSpace(buf.String())
	}

	// Parse HTML.
	root, err := html.Parse(&buf)
	if err != nil {
		panic(fmt.Errorf("parse HTML %q: %w", fragref, err))
	}
	doc := goquery.NewDocumentFromNode(root)

	// Render HTML.
	buf.Reset()
	if err := opt.Renderer(&buf, doc.Selection); err != nil {
		panic(fmt.Errorf("render HTML %q: %w", fragref, err))
	}
	return strings.TrimSpace(buf.String())
}

// DocWith is like Doc, but with configurable options.
func DocWith(fragref string, opt FragOptions) string {
	docMut.Lock()
	defer docMut.Unlock()
	docSeen[fragref] = struct{}{}
	node, _ := resolveFragmentNode(fragref, false)
	if node == nil {
		docFailed[fragref] = struct{}{}
		return "{" + Language + ":" + fragref + "}"
	}
	return ExecuteFragTmpl(fragref, node, opt)
}

// Doc returns the content of the fragment referred to by fragref. The given
// path is marked to be returned by DocFragments. If no content was found, then
// a string indicating an unresolved reference is returned.
//
// Doc should be used only to process descriptions for command-line elements.
// Descriptions are rendered in a format suitable for the terminal.
// ResolveFragment can be used to resolve a reference without marking it.
func Doc(fragref string) string {
	termWidth, _, _ := terminal.GetSize(int(os.Stdout.Fd()))
	return DocWith(fragref, FragOptions{
		Renderer: term.Renderer{Width: termWidth, TabSize: 4}.Render,
	})
}

// DocFlag returns the content for a flag by configuring the renderer with 0
// width, so that it can be properly formatted by the usage template.
func DocFlag(fragref string) string {
	return DocWith(fragref, FragOptions{
		Renderer: term.Renderer{Width: 0, TabSize: 4}.Render,
	})
}

// ResolveFragmentWith is like ResolveFragment, but with configurable options.
func ResolveFragmentWith(fragref string, opt FragOptions) string {
	docMut.Lock()
	defer docMut.Unlock()
	node, _ := resolveFragmentNode(fragref, false)
	if node == nil {
		return ""
	}
	return ExecuteFragTmpl(fragref, node, opt)
}

// ResolveFragment returns the content of the fragment referred to by fragref.
// Returns an empty string if no content was found.
func ResolveFragment(fragref string) string {
	return ResolveFragmentWith(fragref, FragOptions{})
}

const FragSep = ':'
const FragSuffix = ".html"

func ListFragments(fragref string) []string {
	frags := map[string]struct{}{}
	if fragref == "" {
		node, _ := resolveFragmentNode("", false)
		switch node := node.(type) {
		case drill.UnorderedBranch:
			children := node.UnorderedChildren()
			for name := range children {
				if name != "" {
					name = strings.TrimSuffix(name, FragSuffix)
					frags[name] = struct{}{}
				}
			}
		}
	} else {
		node, infile := resolveFragmentNode(fragref, false)
		switch node := node.(type) {
		case drill.UnorderedBranch:
			children := node.UnorderedChildren()
			for name := range children {
				if name != "" {
					if infile {
						name = strings.TrimSuffix(name, FragSuffix)
						frags["/"+name] = struct{}{}
					} else {
						frags[string(FragSep)+name] = struct{}{}
					}
				}
			}
		}
		if node == nil || !infile {
			node, infile := resolveFragmentNode(fragref, true)
			switch node := node.(type) {
			case drill.UnorderedBranch:
				children := node.UnorderedChildren()
				for name := range children {
					if name != "" {
						if !infile {
							name = strings.TrimSuffix(name, FragSuffix)
						}
						frags["/"+name] = struct{}{}
					}
				}
			}
		}
	}
	list := make([]string, 0, len(frags))
	for frag := range frags {
		list = append(list, frag)
	}
	sort.Strings(list)
	return list
}

// resolveFragmentNode resolves a fragment reference into a drill Node by
// walking through the components of the reference.
func resolveFragmentNode(fragref string, dir bool) (n drill.Node, infile bool) {
	n = docfs
	var path string
	names, infile := parseFragRef(fragref, FragSuffix, FragSep, dir)
	for _, name := range names {
		if name == "" {
			return nil, false
		}
		path += "/" + name
		if node, ok := docCache[path]; ok {
			n = node
		} else {
			switch v := n.(type) {
			case drill.UnorderedBranch:
				n = v.UnorderedChild(name)
			default:
				return nil, false
			}
			if node, ok := n.(*htmldrill.Node); ok {
				docCache[path] = node
			}
		}
	}
	return n, infile
}

// ErrorFrag returns an error according to the fragment section of the given
// name. The result is passed to fmt.Errorf with args.
func ErrorFrag(name string, args ...interface{}) error {
	format := ResolveFragment("Errors:" + name)
	return fmt.Errorf(strings.TrimSpace(format), args...)
}

// FormatFrag returns a formatted string according to the fragment of the given
// reference. The result is passed to fmt.Sprintf with args.
func FormatFrag(fragref string, args ...interface{}) string {
	format := ResolveFragment(fragref)
	return fmt.Sprintf(strings.TrimSpace(format), args...)
}
<|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 profile

import (
	"log"
	"time"
)

// Profile measurement of the time it took to any func and output log file.
// Usage: defer nvim.Profile(time.Now(), "func name")
func Start(start time.Time, name string) {
	elapsed := time.Since(start).Seconds()
	log.Printf("%s: %fsec\n", name, elapsed)
}
<commit_msg>nvim/profile: Fix profile to set NVIM_GO_DEBUG env only<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 profile

import (
	"log"
	"os"
	"time"
)

// Profile measurement of the time it took to any func and output log file.
// Usage: defer nvim.Profile(time.Now(), "func name")
func Start(start time.Time, name string) {
	elapsed := time.Since(start).Seconds()
	if os.Getenv("NVIM_GO_DEBUG") != "" {
		log.Printf("%s: %fsec\n", name, elapsed)
	}
}
<|endoftext|>


<commit_before>package readline

import (
	"bufio"
	"bytes"
	"os"
	"strings"
	"unicode"

	. "../conio"
)

var stdOut *bufio.Writer = bufio.NewWriter(os.Stdout)

var hasCache = map[rune]bool{}

func PutRune(ch rune) {
	if hasCache[ch] {
		stdOut.WriteRune(ch)
	} else {
		stdOut.Flush()
		pre_x, pre_y := GetLocate()
		stdOut.WriteRune(ch)
		stdOut.Flush()
		post_x, post_y := GetLocate()
		if post_y == pre_y && post_x > pre_x {
			hasCache[ch] = true
			SetCharWidth(ch, post_x-pre_x)
		}
	}
}

func PutRunes(ch rune, n int) {
	if n <= 0 {
		return
	}
	PutRune(ch)
	for i := 1; i < n; i++ {
		stdOut.WriteRune(ch)
	}
}

func Backspace(n int) {
	if n > 0 {
		stdOut.Flush()
		x, y := GetLocate()
		Locate(x-n, y)
	}
}

func shineCursor() {
	x, y := GetLocate()
	Locate(x, y)
}

const FORBIDDEN_WIDTH = 3 // = lastcolumn(1) and FULLWIDTHCHAR-SIZE(2)

type Buffer struct {
	Buffer     []rune
	Length     int
	Cursor     int
	Unicode    rune
	Keycode    uint16
	ShiftState uint32
	ViewStart  int
	ViewWidth  int // not includes TopColumn
	TermWidth  int // == TopColumn + ViewWidth + FORBIDDEN_WIDTH
	TopColumn  int // == width of Prompt
	Session    *LineEditor
}

func (this *Buffer) Insert(pos int, c []rune) {
	n := len(c)
	for this.Length+n >= len(this.Buffer) {
		tmp := make([]rune, len(this.Buffer)*2)
		copy(tmp, this.Buffer)
		this.Buffer = tmp
	}
	for i := this.Length - 1; i >= pos; i-- {
		this.Buffer[i+n] = this.Buffer[i]
	}
	for i := 0; i < n; i++ {
		this.Buffer[pos+i] = c[i]
	}
	this.Length += n
}

// Insert String :s at :pos
// returns
//    count of rune
func (this *Buffer) InsertString(pos int, s string) int {
	list := []rune(s)
	this.Insert(pos, list)
	return len(list)
}

func (this *Buffer) Delete(pos int, n int) int {
	if n <= 0 || this.Length < pos+n {
		return 0
	}
	delw := this.GetWidthBetween(pos, pos+n)
	for i := pos; i < this.Length-n; i++ {
		this.Buffer[i] = this.Buffer[i+n]
	}
	this.Length -= n
	return delw
}

func (this *Buffer) InsertAndRepaint(str string) {
	this.ReplaceAndRepaint(this.Cursor, str)
}

func (this *Buffer) ResetViewStart() {
	this.ViewStart = 0
	w := 0
	for i := 0; i <= this.Cursor; i++ {
		w += GetCharWidth(this.Buffer[i])
		for w >= this.ViewWidth {
			w -= GetCharWidth(this.Buffer[this.ViewStart])
			this.ViewStart++
		}
	}
}

func (this *Buffer) ReplaceAndRepaint(pos int, str string) {
	// Cursor rewind
	Backspace(this.GetWidthBetween(this.ViewStart, this.Cursor))

	// Replace Buffer
	this.Delete(pos, this.Cursor-pos)

	// Define ViewStart , Cursor
	this.Cursor = pos + this.InsertString(pos, str)
	this.ResetViewStart()

	// Repaint
	w := 0
	for i := this.ViewStart; i < this.Cursor; i++ {
		PutRune(this.Buffer[i])
		w += GetCharWidth(this.Buffer[i])
	}
	bs := 0
	for i := this.Cursor; i < this.Length; i++ {
		w1 := GetCharWidth(this.Buffer[i])
		if w+w1 >= this.ViewWidth {
			break
		}
		PutRune(this.Buffer[i])
		w += w1
		bs += w1
	}
	for w+1 < this.ViewWidth {
		PutRune(' ')
		bs++
		w++
	}
	if bs > 0 {
		Backspace(bs)
	}
}

func (this *Buffer) GetWidthBetween(from int, to int) int {
	width := 0
	for i := from; i < to; i++ {
		width += GetCharWidth(this.Buffer[i])
	}
	return width
}

func (this *Buffer) Repaint(pos int, del int) {
	bs := 0
	vp := this.GetWidthBetween(this.ViewStart, pos)

	for i := pos; i < this.Length; i++ {
		w1 := GetCharWidth(this.Buffer[i])
		if vp+w1 >= this.ViewWidth {
			break
		}
		PutRune(this.Buffer[i])
		vp += w1
		bs += w1
	}
	if del > 0 {
		for i := 0; vp+1 < this.ViewWidth && i < del; i++ {
			PutRune(' ')
			vp++
			bs++
		}
		Backspace(bs)
	} else {
		// for readline_keyfunc.go: KeyFuncInsertSelf()
		Backspace(bs + del)
	}
}

func (this *Buffer) RepaintAll() {
	this.TopColumn, _ = this.Session.Prompt(this.Session)
	this.ViewWidth = this.TermWidth - this.TopColumn - FORBIDDEN_WIDTH
	this.ResetViewStart()
	for i := this.ViewStart; i < this.Cursor; i++ {
		PutRune(this.Buffer[i])
	}
	this.Repaint(this.Cursor, 0)
}

func (this Buffer) String() string {
	var result bytes.Buffer
	for i := 0; i < this.Length; i++ {
		result.WriteRune(this.Buffer[i])
	}
	return result.String()
}

var Delimiters = "\"'"

func (this *Buffer) CurrentWordTop() (wordTop int) {
	wordTop = -1
	quotedchar := '\000'
	for i := 0; i < this.Cursor; i++ {
		if quotedchar == '\000' {
			if strings.ContainsRune(Delimiters, this.Buffer[i]) {
				quotedchar = this.Buffer[i]
			}
		} else if this.Buffer[i] == quotedchar {
			quotedchar = '\000'
		}
		if unicode.IsSpace(this.Buffer[i]) && quotedchar == '\000' {
			wordTop = -1
		} else if wordTop < 0 {
			wordTop = i
		}
	}
	if wordTop < 0 {
		return this.Cursor
	} else {
		return wordTop
	}
}

func (this *Buffer) CurrentWord() (string, int) {
	var buffer bytes.Buffer
	start := this.CurrentWordTop()
	for i := start; i < this.Cursor; i++ {
		buffer.WriteRune(this.Buffer[i])
	}
	return buffer.String(), start
}
<commit_msg>Backspace with ESC[mC<commit_after>package readline

import (
	"bufio"
	"bytes"
	"fmt"
	"strings"
	"unicode"

	. "../conio"
	"github.com/mattn/go-colorable"
)

var stdOut = bufio.NewWriter(colorable.NewColorableStdout())

var hasCache = map[rune]bool{}

func PutRune(ch rune) {
	if hasCache[ch] {
		stdOut.WriteRune(ch)
	} else {
		stdOut.Flush()
		pre_x, pre_y := GetLocate()
		stdOut.WriteRune(ch)
		stdOut.Flush()
		post_x, post_y := GetLocate()
		if post_y == pre_y && post_x > pre_x {
			hasCache[ch] = true
			SetCharWidth(ch, post_x-pre_x)
		}
	}
}

func PutRunes(ch rune, n int) {
	if n <= 0 {
		return
	}
	PutRune(ch)
	for i := 1; i < n; i++ {
		stdOut.WriteRune(ch)
	}
}

func Backspace(n int) {
	if n > 1 {
		fmt.Fprintf(stdOut, "\x1B[%dC", n)
	} else if n == 1 {
		fmt.Fprint(stdOut, "\b")
	}
}

func shineCursor() {
	x, y := GetLocate()
	Locate(x, y)
}

const FORBIDDEN_WIDTH = 3 // = lastcolumn(1) and FULLWIDTHCHAR-SIZE(2)

type Buffer struct {
	Buffer     []rune
	Length     int
	Cursor     int
	Unicode    rune
	Keycode    uint16
	ShiftState uint32
	ViewStart  int
	ViewWidth  int // not includes TopColumn
	TermWidth  int // == TopColumn + ViewWidth + FORBIDDEN_WIDTH
	TopColumn  int // == width of Prompt
	Session    *LineEditor
}

func (this *Buffer) Insert(pos int, c []rune) {
	n := len(c)
	for this.Length+n >= len(this.Buffer) {
		tmp := make([]rune, len(this.Buffer)*2)
		copy(tmp, this.Buffer)
		this.Buffer = tmp
	}
	for i := this.Length - 1; i >= pos; i-- {
		this.Buffer[i+n] = this.Buffer[i]
	}
	for i := 0; i < n; i++ {
		this.Buffer[pos+i] = c[i]
	}
	this.Length += n
}

// Insert String :s at :pos
// returns
//    count of rune
func (this *Buffer) InsertString(pos int, s string) int {
	list := []rune(s)
	this.Insert(pos, list)
	return len(list)
}

func (this *Buffer) Delete(pos int, n int) int {
	if n <= 0 || this.Length < pos+n {
		return 0
	}
	delw := this.GetWidthBetween(pos, pos+n)
	for i := pos; i < this.Length-n; i++ {
		this.Buffer[i] = this.Buffer[i+n]
	}
	this.Length -= n
	return delw
}

func (this *Buffer) InsertAndRepaint(str string) {
	this.ReplaceAndRepaint(this.Cursor, str)
}

func (this *Buffer) ResetViewStart() {
	this.ViewStart = 0
	w := 0
	for i := 0; i <= this.Cursor; i++ {
		w += GetCharWidth(this.Buffer[i])
		for w >= this.ViewWidth {
			w -= GetCharWidth(this.Buffer[this.ViewStart])
			this.ViewStart++
		}
	}
}

func (this *Buffer) ReplaceAndRepaint(pos int, str string) {
	// Cursor rewind
	Backspace(this.GetWidthBetween(this.ViewStart, this.Cursor))

	// Replace Buffer
	this.Delete(pos, this.Cursor-pos)

	// Define ViewStart , Cursor
	this.Cursor = pos + this.InsertString(pos, str)
	this.ResetViewStart()

	// Repaint
	w := 0
	for i := this.ViewStart; i < this.Cursor; i++ {
		PutRune(this.Buffer[i])
		w += GetCharWidth(this.Buffer[i])
	}
	bs := 0
	for i := this.Cursor; i < this.Length; i++ {
		w1 := GetCharWidth(this.Buffer[i])
		if w+w1 >= this.ViewWidth {
			break
		}
		PutRune(this.Buffer[i])
		w += w1
		bs += w1
	}
	for w+1 < this.ViewWidth {
		PutRune(' ')
		bs++
		w++
	}
	if bs > 0 {
		Backspace(bs)
	}
}

func (this *Buffer) GetWidthBetween(from int, to int) int {
	width := 0
	for i := from; i < to; i++ {
		width += GetCharWidth(this.Buffer[i])
	}
	return width
}

func (this *Buffer) Repaint(pos int, del int) {
	bs := 0
	vp := this.GetWidthBetween(this.ViewStart, pos)

	for i := pos; i < this.Length; i++ {
		w1 := GetCharWidth(this.Buffer[i])
		if vp+w1 >= this.ViewWidth {
			break
		}
		PutRune(this.Buffer[i])
		vp += w1
		bs += w1
	}
	if del > 0 {
		for i := 0; vp+1 < this.ViewWidth && i < del; i++ {
			PutRune(' ')
			vp++
			bs++
		}
		Backspace(bs)
	} else {
		// for readline_keyfunc.go: KeyFuncInsertSelf()
		Backspace(bs + del)
	}
}

func (this *Buffer) RepaintAll() {
	this.TopColumn, _ = this.Session.Prompt(this.Session)
	this.ViewWidth = this.TermWidth - this.TopColumn - FORBIDDEN_WIDTH
	this.ResetViewStart()
	for i := this.ViewStart; i < this.Cursor; i++ {
		PutRune(this.Buffer[i])
	}
	this.Repaint(this.Cursor, 0)
}

func (this Buffer) String() string {
	var result bytes.Buffer
	for i := 0; i < this.Length; i++ {
		result.WriteRune(this.Buffer[i])
	}
	return result.String()
}

var Delimiters = "\"'"

func (this *Buffer) CurrentWordTop() (wordTop int) {
	wordTop = -1
	quotedchar := '\000'
	for i := 0; i < this.Cursor; i++ {
		if quotedchar == '\000' {
			if strings.ContainsRune(Delimiters, this.Buffer[i]) {
				quotedchar = this.Buffer[i]
			}
		} else if this.Buffer[i] == quotedchar {
			quotedchar = '\000'
		}
		if unicode.IsSpace(this.Buffer[i]) && quotedchar == '\000' {
			wordTop = -1
		} else if wordTop < 0 {
			wordTop = i
		}
	}
	if wordTop < 0 {
		return this.Cursor
	} else {
		return wordTop
	}
}

func (this *Buffer) CurrentWord() (string, int) {
	var buffer bytes.Buffer
	start := this.CurrentWordTop()
	for i := start; i < this.Cursor; i++ {
		buffer.WriteRune(this.Buffer[i])
	}
	return buffer.String(), start
}
<|endoftext|>


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

package gitea

import (
	"fmt"
)

// GitEntry represents a git tree
type GitEntry struct {
	Path string `json:"path"`
	Mode string `json:"mode"`
	Type string `json:"type"`
	Size int64  `json:"size"`
	SHA  string `json:"sha"`
	URL  string `json:"url"`
}

// GitTreeResponse returns a git tree
type GitTreeResponse struct {
	SHA       string     `json:"sha"`
	URL       string     `json:"url"`
	Entries   []GitEntry `json:"tree"`
	Truncated bool       `json:"truncated"`
}

// GetTrees downloads a file of repository, ref can be branch/tag/commit.
// e.g.: ref -> master, tree -> macaron.go(no leading slash)
func (c *Client) GetTrees(user, repo, ref string) (*GitTreeResponse, error) {
	var trees GitTreeResponse
	_, err := c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/git/trees/%s", user, repo, ref), nil, nil)
	return &trees, err
}
<commit_msg>added recursive support to git trees api (#130)<commit_after>// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package gitea

import (
	"fmt"
)

// GitEntry represents a git tree
type GitEntry struct {
	Path string `json:"path"`
	Mode string `json:"mode"`
	Type string `json:"type"`
	Size int64  `json:"size"`
	SHA  string `json:"sha"`
	URL  string `json:"url"`
}

// GitTreeResponse returns a git tree
type GitTreeResponse struct {
	SHA       string     `json:"sha"`
	URL       string     `json:"url"`
	Entries   []GitEntry `json:"tree"`
	Truncated bool       `json:"truncated"`
}

// GetTrees downloads a file of repository, ref can be branch/tag/commit.
// e.g.: ref -> master, tree -> macaron.go(no leading slash)
func (c *Client) GetTrees(user, repo, ref string, recursive bool) (*GitTreeResponse, error) {
	var trees GitTreeResponse
	var path = fmt.Sprintf("/repos/%s/%s/git/trees/%s", user, repo, ref)
	if recursive {
		path += "?recursive=1"
	}
	err := c.getParsedResponse("GET", path, nil, nil, &trees)
	return &trees, err
}
<|endoftext|>


<commit_before>package common

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/request"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/packer/common/uuid"
	"github.com/hashicorp/packer/helper/communicator"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
)

type StepSecurityGroup struct {
	CommConfig            *communicator.Config
	SecurityGroupIds      []string
	VpcId                 string
	TemporarySGSourceCidr string

	createdGroupId string
}

func (s *StepSecurityGroup) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
	ec2conn := state.Get("ec2").(*ec2.EC2)
	ui := state.Get("ui").(packer.Ui)

	if len(s.SecurityGroupIds) > 0 {
		_, err := ec2conn.DescribeSecurityGroups(
			&ec2.DescribeSecurityGroupsInput{
				GroupIds: aws.StringSlice(s.SecurityGroupIds),
			},
		)
		if err != nil {
			err := fmt.Errorf("Couldn't find specified security group: %s", err)
			log.Printf("[DEBUG] %s", err.Error())
			state.Put("error", err)
			return multistep.ActionHalt
		}
		log.Printf("Using specified security groups: %v", s.SecurityGroupIds)
		state.Put("securityGroupIds", s.SecurityGroupIds)
		return multistep.ActionContinue
	}

	port := s.CommConfig.Port()
	if port == 0 {
		if s.CommConfig.Type != "none" {
			panic("port must be set to a non-zero value.")
		}
	}

	// Create the group
	groupName := fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
	ui.Say(fmt.Sprintf("Creating temporary security group for this instance: %s", groupName))
	group := &ec2.CreateSecurityGroupInput{
		GroupName:   &groupName,
		Description: aws.String("Temporary group for Packer"),
	}

	if s.VpcId != "" {
		group.VpcId = &s.VpcId
	}

	groupResp, err := ec2conn.CreateSecurityGroup(group)
	if err != nil {
		ui.Error(err.Error())
		state.Put("error", err)
		return multistep.ActionHalt
	}

	// Set the group ID so we can delete it later
	s.createdGroupId = *groupResp.GroupId

	// Authorize the SSH access for the security group
	req := &ec2.AuthorizeSecurityGroupIngressInput{
		GroupId:    groupResp.GroupId,
		IpProtocol: aws.String("tcp"),
		FromPort:   aws.Int64(int64(port)),
		ToPort:     aws.Int64(int64(port)),
		CidrIp:     aws.String(s.TemporarySGSourceCidr),
	}

	// We loop and retry this a few times because sometimes the security
	// group isn't available immediately because AWS resources are eventually
	// consistent.
	ui.Say(fmt.Sprintf(
		"Authorizing access to port %d from %s in the temporary security group...",
		port, s.TemporarySGSourceCidr))
	for i := 0; i < 5; i++ {
		_, err = ec2conn.AuthorizeSecurityGroupIngress(req)
		if err == nil {
			break
		}

		log.Printf("Error authorizing. Will sleep and retry. %s", err)
		time.Sleep((time.Duration(i) * time.Second) + 1)
	}

	if err != nil {
		err := fmt.Errorf("Error creating temporary security group: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	log.Printf("[DEBUG] Waiting for temporary security group: %s", s.createdGroupId)
	err = waitUntilSecurityGroupExists(ec2conn,
		&ec2.DescribeSecurityGroupsInput{
			GroupIds: []*string{aws.String(s.createdGroupId)},
		},
	)
	if err == nil {
		log.Printf("[DEBUG] Found security group %s", s.createdGroupId)
	} else {
		err := fmt.Errorf("Timed out waiting for security group %s: %s", s.createdGroupId, err)
		log.Printf("[DEBUG] %s", err.Error())
		state.Put("error", err)
		return multistep.ActionHalt
	}

	// Set some state data for use in future steps
	state.Put("securityGroupIds", []string{s.createdGroupId})

	return multistep.ActionContinue
}

func (s *StepSecurityGroup) Cleanup(state multistep.StateBag) {
	if s.createdGroupId == "" {
		return
	}

	ec2conn := state.Get("ec2").(*ec2.EC2)
	ui := state.Get("ui").(packer.Ui)

	ui.Say("Deleting temporary security group...")

	var err error
	for i := 0; i < 5; i++ {
		_, err = ec2conn.DeleteSecurityGroup(&ec2.DeleteSecurityGroupInput{GroupId: &s.createdGroupId})
		if err == nil {
			break
		}

		log.Printf("Error deleting security group: %s", err)
		time.Sleep(5 * time.Second)
	}

	if err != nil {
		ui.Error(fmt.Sprintf(
			"Error cleaning up security group. Please delete the group manually: %s", s.createdGroupId))
	}
}

func waitUntilSecurityGroupExists(c *ec2.EC2, input *ec2.DescribeSecurityGroupsInput) error {
	ctx := aws.BackgroundContext()
	w := request.Waiter{
		Name:        "DescribeSecurityGroups",
		MaxAttempts: 40,
		Delay:       request.ConstantWaiterDelay(5 * time.Second),
		Acceptors: []request.WaiterAcceptor{
			{
				State:    request.SuccessWaiterState,
				Matcher:  request.PathWaiterMatch,
				Argument: "length(SecurityGroups[]) > `0`",
				Expected: true,
			},
			{
				State:    request.RetryWaiterState,
				Matcher:  request.ErrorWaiterMatch,
				Argument: "",
				Expected: "InvalidGroup.NotFound",
			},
			{
				State:    request.RetryWaiterState,
				Matcher:  request.ErrorWaiterMatch,
				Argument: "",
				Expected: "InvalidSecurityGroupID.NotFound",
			},
		},
		Logger: c.Config.Logger,
		NewRequest: func(opts []request.Option) (*request.Request, error) {
			var inCpy *ec2.DescribeSecurityGroupsInput
			if input != nil {
				tmp := *input
				inCpy = &tmp
			}
			req, _ := c.DescribeSecurityGroupsRequest(inCpy)
			req.SetContext(ctx)
			req.ApplyOptions(opts...)
			return req, nil
		},
	}
	return w.WaitWithContext(ctx)
}
<commit_msg>aws: wait for temporary security group before authorizing<commit_after>package common

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/request"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/packer/common/uuid"
	"github.com/hashicorp/packer/helper/communicator"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
)

type StepSecurityGroup struct {
	CommConfig            *communicator.Config
	SecurityGroupIds      []string
	VpcId                 string
	TemporarySGSourceCidr string

	createdGroupId string
}

func (s *StepSecurityGroup) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
	ec2conn := state.Get("ec2").(*ec2.EC2)
	ui := state.Get("ui").(packer.Ui)

	if len(s.SecurityGroupIds) > 0 {
		_, err := ec2conn.DescribeSecurityGroups(
			&ec2.DescribeSecurityGroupsInput{
				GroupIds: aws.StringSlice(s.SecurityGroupIds),
			},
		)
		if err != nil {
			err := fmt.Errorf("Couldn't find specified security group: %s", err)
			log.Printf("[DEBUG] %s", err.Error())
			state.Put("error", err)
			return multistep.ActionHalt
		}
		log.Printf("Using specified security groups: %v", s.SecurityGroupIds)
		state.Put("securityGroupIds", s.SecurityGroupIds)
		return multistep.ActionContinue
	}

	port := s.CommConfig.Port()
	if port == 0 {
		if s.CommConfig.Type != "none" {
			panic("port must be set to a non-zero value.")
		}
	}

	// Create the group
	groupName := fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
	ui.Say(fmt.Sprintf("Creating temporary security group for this instance: %s", groupName))
	group := &ec2.CreateSecurityGroupInput{
		GroupName:   &groupName,
		Description: aws.String("Temporary group for Packer"),
	}

	if s.VpcId != "" {
		group.VpcId = &s.VpcId
	}

	groupResp, err := ec2conn.CreateSecurityGroup(group)
	if err != nil {
		ui.Error(err.Error())
		state.Put("error", err)
		return multistep.ActionHalt
	}

	// Set the group ID so we can delete it later
	s.createdGroupId = *groupResp.GroupId

	// Wait for the security group become available for authorizing
	log.Printf("[DEBUG] Waiting for temporary security group: %s", s.createdGroupId)
	err = waitUntilSecurityGroupExists(ec2conn,
		&ec2.DescribeSecurityGroupsInput{
			GroupIds: []*string{aws.String(s.createdGroupId)},
		},
	)
	if err == nil {
		log.Printf("[DEBUG] Found security group %s", s.createdGroupId)
	} else {
		err := fmt.Errorf("Timed out waiting for security group %s: %s", s.createdGroupId, err)
		log.Printf("[DEBUG] %s", err.Error())
		state.Put("error", err)
		return multistep.ActionHalt
	}

	// Authorize the SSH access for the security group
	groupRules := &ec2.AuthorizeSecurityGroupIngressInput{
		GroupId:    groupResp.GroupId,
		IpProtocol: aws.String("tcp"),
		FromPort:   aws.Int64(int64(port)),
		ToPort:     aws.Int64(int64(port)),
		CidrIp:     aws.String(s.TemporarySGSourceCidr),
	}

	ui.Say(fmt.Sprintf(
		"Authorizing access to port %d from %s in the temporary security group...",
		port, s.TemporarySGSourceCidr))
	_, err = ec2conn.AuthorizeSecurityGroupIngress(groupRules)
	if err != nil {
		err := fmt.Errorf("Error authorizing temporary security group: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Set some state data for use in future steps
	state.Put("securityGroupIds", []string{s.createdGroupId})

	return multistep.ActionContinue
}

func (s *StepSecurityGroup) Cleanup(state multistep.StateBag) {
	if s.createdGroupId == "" {
		return
	}

	ec2conn := state.Get("ec2").(*ec2.EC2)
	ui := state.Get("ui").(packer.Ui)

	ui.Say("Deleting temporary security group...")

	var err error
	for i := 0; i < 5; i++ {
		_, err = ec2conn.DeleteSecurityGroup(&ec2.DeleteSecurityGroupInput{GroupId: &s.createdGroupId})
		if err == nil {
			break
		}

		log.Printf("Error deleting security group: %s", err)
		time.Sleep(5 * time.Second)
	}

	if err != nil {
		ui.Error(fmt.Sprintf(
			"Error cleaning up security group. Please delete the group manually: %s", s.createdGroupId))
	}
}

func waitUntilSecurityGroupExists(c *ec2.EC2, input *ec2.DescribeSecurityGroupsInput) error {
	ctx := aws.BackgroundContext()
	w := request.Waiter{
		Name:        "DescribeSecurityGroups",
		MaxAttempts: 40,
		Delay:       request.ConstantWaiterDelay(5 * time.Second),
		Acceptors: []request.WaiterAcceptor{
			{
				State:    request.SuccessWaiterState,
				Matcher:  request.PathWaiterMatch,
				Argument: "length(SecurityGroups[]) > `0`",
				Expected: true,
			},
			{
				State:    request.RetryWaiterState,
				Matcher:  request.ErrorWaiterMatch,
				Argument: "",
				Expected: "InvalidGroup.NotFound",
			},
			{
				State:    request.RetryWaiterState,
				Matcher:  request.ErrorWaiterMatch,
				Argument: "",
				Expected: "InvalidSecurityGroupID.NotFound",
			},
		},
		Logger: c.Config.Logger,
		NewRequest: func(opts []request.Option) (*request.Request, error) {
			var inCpy *ec2.DescribeSecurityGroupsInput
			if input != nil {
				tmp := *input
				inCpy = &tmp
			}
			req, _ := c.DescribeSecurityGroupsRequest(inCpy)
			req.SetContext(ctx)
			req.ApplyOptions(opts...)
			return req, nil
		},
	}
	return w.WaitWithContext(ctx)
}
<|endoftext|>


<commit_before>package common

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"time"

	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
)

// StepOutputDir sets up the output directory by creating it if it does
// not exist, deleting it if it does exist and we're forcing, and cleaning
// it up when we're done with it.
type StepOutputDir struct {
	Force bool
	Path  string
}

func (s *StepOutputDir) Run(state multistep.StateBag) multistep.StepAction {
	ui := state.Get("ui").(packer.Ui)

	if _, err := os.Stat(s.Path); err == nil {
		if !s.Force {
			err := fmt.Errorf(
				"Output directory exists: %s\n\n"+
					"Use the force flag to delete it prior to building.",
				s.Path)
			state.Put("error", err)
			return multistep.ActionHalt
		}

		ui.Say("Deleting previous output directory...")
		os.RemoveAll(s.Path)
	}

	// Create the directory
	if err := os.MkdirAll(s.Path, 0755); err != nil {
		state.Put("error", err)
		return multistep.ActionHalt
	}

	// Make sure we can write in the directory
	f, err := os.Create(filepath.Join(s.Path, "_packer_perm_check"))
	if err != nil {
		err = fmt.Errorf("Couldn't write to output directory: %s", err)
		state.Put("error", err)
		return multistep.ActionHalt
	}
	f.Close()
	os.Remove(f.Name())

	return multistep.ActionContinue
}

func (s *StepOutputDir) Cleanup(state multistep.StateBag) {
	_, cancelled := state.GetOk(multistep.StateCancelled)
	_, halted := state.GetOk(multistep.StateHalted)

	if cancelled || halted {
		ui := state.Get("ui").(packer.Ui)

		ui.Say("Deleting output directory...")
		for i := 0; i < 5; i++ {
			err := os.RemoveAll(s.Path)
			if err == nil {
				break
			}

			log.Printf("Error removing output dir: %s", err)
			time.Sleep(2 * time.Second)
		}
	}
}
<commit_msg>builder/virtualbox: only remove output dir after check<commit_after>package common

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"time"

	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
)

// StepOutputDir sets up the output directory by creating it if it does
// not exist, deleting it if it does exist and we're forcing, and cleaning
// it up when we're done with it.
type StepOutputDir struct {
	Force bool
	Path  string

	cleanup bool
}

func (s *StepOutputDir) Run(state multistep.StateBag) multistep.StepAction {
	ui := state.Get("ui").(packer.Ui)

	if _, err := os.Stat(s.Path); err == nil {
		if !s.Force {
			err := fmt.Errorf(
				"Output directory exists: %s\n\n"+
					"Use the force flag to delete it prior to building.",
				s.Path)
			state.Put("error", err)
			return multistep.ActionHalt
		}

		ui.Say("Deleting previous output directory...")
		os.RemoveAll(s.Path)
	}

	// Enable cleanup
	s.cleanup = true

	// Create the directory
	if err := os.MkdirAll(s.Path, 0755); err != nil {
		state.Put("error", err)
		return multistep.ActionHalt
	}

	// Make sure we can write in the directory
	f, err := os.Create(filepath.Join(s.Path, "_packer_perm_check"))
	if err != nil {
		err = fmt.Errorf("Couldn't write to output directory: %s", err)
		state.Put("error", err)
		return multistep.ActionHalt
	}
	f.Close()
	os.Remove(f.Name())

	return multistep.ActionContinue
}

func (s *StepOutputDir) Cleanup(state multistep.StateBag) {
	if !s.cleanup {
		return
	}

	_, cancelled := state.GetOk(multistep.StateCancelled)
	_, halted := state.GetOk(multistep.StateHalted)

	if cancelled || halted {
		ui := state.Get("ui").(packer.Ui)

		ui.Say("Deleting output directory...")
		for i := 0; i < 5; i++ {
			err := os.RemoveAll(s.Path)
			if err == nil {
				break
			}

			log.Printf("Error removing output dir: %s", err)
			time.Sleep(2 * time.Second)
		}
	}
}
<|endoftext|>


<commit_before>package consumer

import (
	"container/list"
	"encoding/json"
	"flume-log-sdk/config"
	"flume-log-sdk/consumer/client"
	"flume-log-sdk/consumer/pool"
	"flume-log-sdk/rpc/flume"
	"fmt"
	"github.com/momotech/GoRedis/libs/stdlog"
	"log"
	"math/rand"
	"sync"
	"sync/atomic"
	"time"
)

type counter struct {
	lastSuccValue int64

	currSuccValue int64

	lastFailValue int64

	currFailValue int64
}

const (
	batchSize = 1000
	sendbuff  = 100000
)

//生成对象池用于缓存 thriftEvent对象
var objpool *sync.Pool
var eventPool *sync.Pool

func init() {
	objpool = &sync.Pool{}
	objpool.New = func() interface{} {
		//创建生成thriftevent的
		return client.NewFlumeEvent()
	}

	eventPool = &sync.Pool{}
	eventPool.New = func() interface{} {
		return make([]flume.ThriftFlumeEvent, batchSize, batchSize)
	}
}

// 用于向flume中作为sink 通过thrift客户端写入日志
type SourceServer struct {
	flumeClientPool *list.List
	isStop          bool
	monitorCount    counter
	business        string
	batchSize       int
	buffChannel     chan *flume.ThriftFlumeEvent
	sourceLog       stdlog.Logger
}

func newSourceServer(business string, flumePool *list.List, sourceLog stdlog.Logger) (server *SourceServer) {
	buffChannel := make(chan *flume.ThriftFlumeEvent, sendbuff)
	sourceServer := &SourceServer{
		business:        business,
		flumeClientPool: flumePool,
		batchSize:       batchSize,
		buffChannel:     buffChannel,
		sourceLog:       sourceLog}
	return sourceServer
}

func (self *SourceServer) monitor() (succ, fail int64, bufferSize int) {
	currSucc := self.monitorCount.currSuccValue
	currFail := self.monitorCount.currFailValue
	succ = (currSucc - self.monitorCount.lastSuccValue)
	fail = (currFail - self.monitorCount.lastFailValue)
	self.monitorCount.lastSuccValue = currSucc
	self.monitorCount.lastFailValue = currFail

	//自己的Buffer大小
	bufferSize = len(self.buffChannel)
	return
}

//启动pop
func (self *SourceServer) start() {

	self.isStop = false

	//创建chan ,buffer 为10
	sendbuff := make(chan []*flume.ThriftFlumeEvent, 1000)
	//启动20个go程从channel获取
	for i := 0; i < 10; i++ {
		go func(ch chan []*flume.ThriftFlumeEvent) {
			for !self.isStop {
				events := <-ch
				self.innerSend(events)
				// 归还当前的数组空间
				defer func() {
					eventPool.Put(events)
				}()
			}
		}(sendbuff)
	}

	go func() {
		//批量收集数据
		// pack := make([]*flume.ThriftFlumeEvent, 0, self.batchSize)
		item := eventPool.Get()
		pack := item.([]*flume.ThriftFlumeEvent)
		idx := 0
		for !self.isStop {
			event := <-self.buffChannel

			// if len(pack) < self.batchSize {
			// 	pack = append(pack, event)
			// 	continue
			// }
			// sendbuff <- pack[:len(pack)]
			// pack = make([]*flume.ThriftFlumeEvent, 0, self.batchSize)

			如果总数大于batchsize则提交
			if idx < self.batchSize {
				//批量提交
				pack[idx] = event
				idx++
				continue
			}

			sendbuff <- pack[:idx]
			item = eventPool.Get()
			pack = item.([]*flume.ThriftFlumeEvent)
			idx = 0

		}

		close(sendbuff)
	}()

	self.sourceLog.Printf("LOG_SOURCE|SOURCE SERVER [%s]|STARTED\n", self.business)
}

func (self *SourceServer) innerSend(events []*flume.ThriftFlumeEvent) {

	for i := 0; i < 3; i++ {

		pool := self.getFlumeClientPool()
		if nil == pool {
			continue
		}
		flumeclient, err := pool.Get(5 * time.Second)
		if nil != err || nil == flumeclient {
			self.sourceLog.Printf("LOG_SOURCE|GET FLUMECLIENT|FAIL|%s|%s|TRY:%d\n", self.business, err, i)
			continue
		}

		err = flumeclient.AppendBatch(events)
		defer func() {
			if err := recover(); nil != err {
				//回收这个坏的连接
				pool.ReleaseBroken(flumeclient)
			} else {
				pool.Release(flumeclient)
			}
		}()

		if nil != err {
			atomic.AddInt64(&self.monitorCount.currFailValue, int64(len(events)))
			self.sourceLog.Printf("LOG_SOURCE|SEND FLUME|FAIL|%s|%s|TRY:%d\n", self.business, err.Error(), i)

		} else {
			atomic.AddInt64(&self.monitorCount.currSuccValue, int64(1*self.batchSize))
			if rand.Int()%10000 == 0 {
				self.sourceLog.Printf("trace|send 2 flume succ|%s|%d\n", flumeclient.HostPort(), len(events))
			}
			break
		}

	}

	//归还event对线到池子中
	defer func() {
		for _, v := range events {
			objpool.Put(*v)
		}
	}()
}

//解析出decodecommand
func decodeCommand(resp []byte) (string, *flume.ThriftFlumeEvent) {
	var cmd config.Command
	err := json.Unmarshal(resp, &cmd)
	if nil != err {
		log.Printf("command unmarshal fail ! %T | error:%s\n", resp, err.Error())
		return "", nil
	}
	//
	momoid := cmd.Params["momoid"].(string)

	businessName := cmd.Params["businessName"].(string)

	action := cmd.Params["type"].(string)

	bodyContent := cmd.Params["body"]

	//将businessName 加入到body中
	bodyMap := bodyContent.(map[string]interface{})
	bodyMap["business_type"] = businessName

	body, err := json.Marshal(bodyContent)
	if nil != err {
		log.Printf("marshal log body fail %s", err.Error())
		return businessName, nil
	}

	//拼Body
	flumeBody := fmt.Sprintf("%s\t%s\t%s", momoid, action, string(body))
	obj := objpool.Get()
	event := client.EventFillUp(obj, businessName, action, []byte(flumeBody))
	// event := client.NewFlumeEvent(businessName, action, []byte(flumeBody))
	return businessName, event
}

func (self *SourceServer) stop() {
	self.isStop = true
	time.Sleep(5 * time.Second)

	//遍历所有的flumeclientlink，将当前Business从该链表中移除
	for v := self.flumeClientPool.Back(); nil != v; v = v.Prev() {
		v.Value.(*pool.FlumePoolLink).DetachBusiness(self.business)
	}
	close(self.buffChannel)
	self.sourceLog.Printf("LOG_SOURCE|SOURCE SERVER|[%s]|STOPPED\n", self.business)
}

func (self *SourceServer) getFlumeClientPool() *pool.FlumeClientPool {

	//采用轮训算法
	e := self.flumeClientPool.Back()
	if nil == e {
		return nil
	}
	self.flumeClientPool.MoveToFront(e)
	return e.Value.(*pool.FlumePoolLink).FlumePool

}
<commit_msg>	modified:   consumer/log_source.go<commit_after>package consumer

import (
	"container/list"
	"encoding/json"
	"flume-log-sdk/config"
	"flume-log-sdk/consumer/client"
	"flume-log-sdk/consumer/pool"
	"flume-log-sdk/rpc/flume"
	"fmt"
	"github.com/momotech/GoRedis/libs/stdlog"
	"log"
	"math/rand"
	"sync"
	"sync/atomic"
	"time"
)

type counter struct {
	lastSuccValue int64

	currSuccValue int64

	lastFailValue int64

	currFailValue int64
}

const (
	batchSize = 1000
	sendbuff  = 100000
)

//生成对象池用于缓存 thriftEvent对象
var objpool *sync.Pool
var eventPool *sync.Pool

func init() {
	objpool = &sync.Pool{}
	objpool.New = func() interface{} {
		//创建生成thriftevent的
		return client.NewFlumeEvent()
	}

	eventPool = &sync.Pool{}
	eventPool.New = func() interface{} {
		return make([]flume.ThriftFlumeEvent, batchSize, batchSize)
	}
}

// 用于向flume中作为sink 通过thrift客户端写入日志
type SourceServer struct {
	flumeClientPool *list.List
	isStop          bool
	monitorCount    counter
	business        string
	batchSize       int
	buffChannel     chan *flume.ThriftFlumeEvent
	sourceLog       stdlog.Logger
}

func newSourceServer(business string, flumePool *list.List, sourceLog stdlog.Logger) (server *SourceServer) {
	buffChannel := make(chan *flume.ThriftFlumeEvent, sendbuff)
	sourceServer := &SourceServer{
		business:        business,
		flumeClientPool: flumePool,
		batchSize:       batchSize,
		buffChannel:     buffChannel,
		sourceLog:       sourceLog}
	return sourceServer
}

func (self *SourceServer) monitor() (succ, fail int64, bufferSize int) {
	currSucc := self.monitorCount.currSuccValue
	currFail := self.monitorCount.currFailValue
	succ = (currSucc - self.monitorCount.lastSuccValue)
	fail = (currFail - self.monitorCount.lastFailValue)
	self.monitorCount.lastSuccValue = currSucc
	self.monitorCount.lastFailValue = currFail

	//自己的Buffer大小
	bufferSize = len(self.buffChannel)
	return
}

//启动pop
func (self *SourceServer) start() {

	self.isStop = false

	//创建chan ,buffer 为10
	sendbuff := make(chan []*flume.ThriftFlumeEvent, 1000)
	//启动20个go程从channel获取
	for i := 0; i < 10; i++ {
		go func(ch chan []*flume.ThriftFlumeEvent) {
			for !self.isStop {
				events := <-ch
				self.innerSend(events)
				// 归还当前的数组空间
				defer func() {
					eventPool.Put(events)
				}()
			}
		}(sendbuff)
	}

	go func() {
		//批量收集数据
		// pack := make([]*flume.ThriftFlumeEvent, 0, self.batchSize)
		item := eventPool.Get()
		pack := item.([]*flume.ThriftFlumeEvent)
		idx := 0
		for !self.isStop {
			event := <-self.buffChannel

			// if len(pack) < self.batchSize {
			// 	pack = append(pack, event)
			// 	continue
			// }
			// sendbuff <- pack[:len(pack)]
			// pack = make([]*flume.ThriftFlumeEvent, 0, self.batchSize)

			//如果总数大于batchsize则提交
			if idx < self.batchSize {
				//批量提交
				pack[idx] = event
				idx++
				continue
			}

			sendbuff <- pack[:idx]
			item = eventPool.Get()
			pack = item.([]*flume.ThriftFlumeEvent)
			idx = 0

		}

		close(sendbuff)
	}()

	self.sourceLog.Printf("LOG_SOURCE|SOURCE SERVER [%s]|STARTED\n", self.business)
}

func (self *SourceServer) innerSend(events []*flume.ThriftFlumeEvent) {

	for i := 0; i < 3; i++ {

		pool := self.getFlumeClientPool()
		if nil == pool {
			continue
		}
		flumeclient, err := pool.Get(5 * time.Second)
		if nil != err || nil == flumeclient {
			self.sourceLog.Printf("LOG_SOURCE|GET FLUMECLIENT|FAIL|%s|%s|TRY:%d\n", self.business, err, i)
			continue
		}

		err = flumeclient.AppendBatch(events)
		defer func() {
			if err := recover(); nil != err {
				//回收这个坏的连接
				pool.ReleaseBroken(flumeclient)
			} else {
				pool.Release(flumeclient)
			}
		}()

		if nil != err {
			atomic.AddInt64(&self.monitorCount.currFailValue, int64(len(events)))
			self.sourceLog.Printf("LOG_SOURCE|SEND FLUME|FAIL|%s|%s|TRY:%d\n", self.business, err.Error(), i)

		} else {
			atomic.AddInt64(&self.monitorCount.currSuccValue, int64(1*self.batchSize))
			if rand.Int()%10000 == 0 {
				self.sourceLog.Printf("trace|send 2 flume succ|%s|%d\n", flumeclient.HostPort(), len(events))
			}
			break
		}

	}

	//归还event对线到池子中
	defer func() {
		for _, v := range events {
			objpool.Put(*v)
		}
	}()
}

//解析出decodecommand
func decodeCommand(resp []byte) (string, *flume.ThriftFlumeEvent) {
	var cmd config.Command
	err := json.Unmarshal(resp, &cmd)
	if nil != err {
		log.Printf("command unmarshal fail ! %T | error:%s\n", resp, err.Error())
		return "", nil
	}
	//
	momoid := cmd.Params["momoid"].(string)

	businessName := cmd.Params["businessName"].(string)

	action := cmd.Params["type"].(string)

	bodyContent := cmd.Params["body"]

	//将businessName 加入到body中
	bodyMap := bodyContent.(map[string]interface{})
	bodyMap["business_type"] = businessName

	body, err := json.Marshal(bodyContent)
	if nil != err {
		log.Printf("marshal log body fail %s", err.Error())
		return businessName, nil
	}

	//拼Body
	flumeBody := fmt.Sprintf("%s\t%s\t%s", momoid, action, string(body))
	obj := objpool.Get()
	event := client.EventFillUp(obj, businessName, action, []byte(flumeBody))
	// event := client.NewFlumeEvent(businessName, action, []byte(flumeBody))
	return businessName, event
}

func (self *SourceServer) stop() {
	self.isStop = true
	time.Sleep(5 * time.Second)

	//遍历所有的flumeclientlink，将当前Business从该链表中移除
	for v := self.flumeClientPool.Back(); nil != v; v = v.Prev() {
		v.Value.(*pool.FlumePoolLink).DetachBusiness(self.business)
	}
	close(self.buffChannel)
	self.sourceLog.Printf("LOG_SOURCE|SOURCE SERVER|[%s]|STOPPED\n", self.business)
}

func (self *SourceServer) getFlumeClientPool() *pool.FlumeClientPool {

	//采用轮训算法
	e := self.flumeClientPool.Back()
	if nil == e {
		return nil
	}
	self.flumeClientPool.MoveToFront(e)
	return e.Value.(*pool.FlumePoolLink).FlumePool

}
<|endoftext|>


<commit_before>package classfile

import (
	"fmt"

	"github.com/zxh0/jvm.go/jvmgo/jutil/bigendian"
)

type ClassReader struct {
	data []byte
}

func newClassReader(data []byte) *ClassReader {
	return &ClassReader{data}
}

func (self *ClassReader) readUint8() uint8 {
	val := self.data[0]
	self.data = self.data[1:]
	return val
}

func (self *ClassReader) readUint16() uint16 {
	val := bigendian.Uint16(self.data)
	self.data = self.data[2:]
	return val
}

func (self *ClassReader) readUint16s() []uint16 {
	n := self.readUint16()
	s := make([]uint16, n)
	for i := range s {
		s[i] = self.readUint16()
	}
	return s
}

func (self *ClassReader) readUint32() uint32 {
	val := bigendian.Int32(self.data)
	self.data = self.data[4:]
	return uint32(val)
}
func (self *ClassReader) readInt32() int32 {
	val := bigendian.Int32(self.data)
	self.data = self.data[4:]
	return val
}

func (self *ClassReader) readInt64() int64 {
	val := bigendian.Int64(self.data)
	self.data = self.data[8:]
	return val
}

func (self *ClassReader) readFloat32() float32 {
	val := bigendian.Float32(self.data)
	self.data = self.data[4:]
	return val
}

func (self *ClassReader) readFloat64() float64 {
	val := bigendian.Float64(self.data)
	self.data = self.data[8:]
	return val
}

func (self *ClassReader) readBytes(length uint32) []byte {
	bytes := self.data[:length]
	self.data = self.data[length:]
	return bytes
}

// todo
func (self *ClassReader) readString() string {
	length := uint32(self.readUint16())
	bytes := self.readBytes(length)
	return string(bytes)
}

// todo
// see java.io.DataInputStream.readUTF(DataInput)
func (self *ClassReader) readMUTF8() string {
	utflen := uint32(self.readUint16())
	bytearr := self.readBytes(utflen)
	chararr := make([]uint16, utflen)

	var c, char2, char3 uint16
	var count uint32 = 0
	var chararr_count uint32 = 0

	for count < utflen {
		c = uint16(bytearr[count])
		if c > 127 {
			break
		}
		count++
		chararr[chararr_count] = c
		chararr_count++
	}

	for count < utflen {
		c = uint16(bytearr[count])
		switch c >> 4 {
		case 0, 1, 2, 3, 4, 5, 6, 7:
			/* 0xxxxxxx*/
			count++
			chararr[chararr_count] = c
			chararr_count++
			break
		case 12, 13:
			/* 110x xxxx   10xx xxxx*/
			count += 2
			if count > utflen {
				panic("malformed input: partial character at end")
			}
			char2 = uint16(bytearr[count-1])
			if char2&0xC0 != 0x80 {
				panic(fmt.Sprintf("malformed input around byte %v", count))
			}
			chararr[chararr_count] = ((c & 0x1F) << 6) | (char2 & 0x3F)
			chararr_count++
			break
		case 14:
			/* 1110 xxxx  10xx xxxx  10xx xxxx*/
			count += 3
			if count > utflen {
				panic("malformed input: partial character at end")
			}
			char2 = uint16(bytearr[count-2])
			char3 = uint16(bytearr[count-1])
			if ((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80) {
				panic(fmt.Sprintf("malformed input around byte %v", (count - 1)))
			}
			chararr[chararr_count] = uint16((((c & 0x0F) << 12) |
				((char2 & 0x3F) << 6) |
				((char3 & 0x3F) << 0)))
			chararr_count++
			break
		default:
			/* 10xx xxxx,  1111 xxxx */
			panic(fmt.Sprintf("malformed input around byte %v", count))
		}
	}
	// // The number of chars produced may be less than utflen
	// return new String(chararr, 0, chararr_count);
	return ""
}
<commit_msg>simplify code<commit_after>package classfile

import (
	"fmt"

	"github.com/zxh0/jvm.go/jvmgo/jutil/bigendian"
)

type ClassReader struct {
	data []byte
}

func newClassReader(data []byte) *ClassReader {
	return &ClassReader{data}
}

func (self *ClassReader) readUint8() uint8 {
	val := self.data[0]
	self.data = self.data[1:]
	return val
}

func (self *ClassReader) readUint16() uint16 {
	val := bigendian.Uint16(self.data)
	self.data = self.data[2:]
	return val
}

func (self *ClassReader) readUint16s() []uint16 {
	n := self.readUint16()
	s := make([]uint16, n)
	for i := range s {
		s[i] = self.readUint16()
	}
	return s
}

func (self *ClassReader) readUint32() uint32 {
	val := bigendian.Int32(self.data)
	self.data = self.data[4:]
	return uint32(val)
}
func (self *ClassReader) readInt32() int32 {
	val := bigendian.Int32(self.data)
	self.data = self.data[4:]
	return val
}

func (self *ClassReader) readInt64() int64 {
	val := bigendian.Int64(self.data)
	self.data = self.data[8:]
	return val
}

func (self *ClassReader) readFloat32() float32 {
	val := bigendian.Float32(self.data)
	self.data = self.data[4:]
	return val
}

func (self *ClassReader) readFloat64() float64 {
	val := bigendian.Float64(self.data)
	self.data = self.data[8:]
	return val
}

func (self *ClassReader) readBytes(length uint32) []byte {
	bytes := self.data[:length]
	self.data = self.data[length:]
	return bytes
}

// todo
func (self *ClassReader) readString() string {
	length := uint32(self.readUint16())
	bytes := self.readBytes(length)
	return string(bytes)
}

// todo
// see java.io.DataInputStream.readUTF(DataInput)
func (self *ClassReader) readMUTF8() string {
	utflen := uint32(self.readUint16())
	bytearr := self.readBytes(utflen)
	chararr := make([]uint16, utflen)

	var c, char2, char3 uint16
	var count uint32 = 0
	var chararr_count uint32 = 0

	for count < utflen {
		c = uint16(bytearr[count])
		if c > 127 {
			break
		}
		count++
		chararr[chararr_count] = c
		chararr_count++
	}

	for count < utflen {
		c = uint16(bytearr[count])
		switch c >> 4 {
		case 0, 1, 2, 3, 4, 5, 6, 7:
			/* 0xxxxxxx*/
			count++
			chararr[chararr_count] = c
			chararr_count++
			break
		case 12, 13:
			/* 110x xxxx   10xx xxxx*/
			count += 2
			if count > utflen {
				panic("malformed input: partial character at end")
			}
			char2 = uint16(bytearr[count-1])
			if char2&0xC0 != 0x80 {
				panic(fmt.Sprintf("malformed input around byte %v", count))
			}
			chararr[chararr_count] = (c&0x1F)<<6 | char2&0x3F
			chararr_count++
			break
		case 14:
			/* 1110 xxxx  10xx xxxx  10xx xxxx*/
			count += 3
			if count > utflen {
				panic("malformed input: partial character at end")
			}
			char2 = uint16(bytearr[count-2])
			char3 = uint16(bytearr[count-1])
			if char2&0xC0 != 0x80 || char3&0xC0 != 0x80 {
				panic(fmt.Sprintf("malformed input around byte %v", (count - 1)))
			}
			chararr[chararr_count] = (c&0x0F)<<12 | (char2&0x3F)<<6 | (char3&0x3F)<<0
			chararr_count++
			break
		default:
			/* 10xx xxxx,  1111 xxxx */
			panic(fmt.Sprintf("malformed input around byte %v", count))
		}
	}
	// // The number of chars produced may be less than utflen
	// return new String(chararr, 0, chararr_count);
	return ""
}
<|endoftext|>


<commit_before>package refresh

import (
	"context"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
	"sync"
	"time"

	"github.com/fsnotify/fsnotify"
)

type Manager struct {
	*Configuration
	Logger     *Logger
	Restart    chan bool
	gil        *sync.Once
	ID         string
	context    context.Context
	cancelFunc context.CancelFunc
}

func New(c *Configuration) *Manager {
	return NewWithContext(c, context.Background())
}

func NewWithContext(c *Configuration, ctx context.Context) *Manager {
	ctx, cancelFunc := context.WithCancel(ctx)
	m := &Manager{
		Configuration: c,
		Logger:        NewLogger(c),
		Restart:       make(chan bool),
		gil:           &sync.Once{},
		ID:            ID(),
		context:       ctx,
		cancelFunc:    cancelFunc,
	}
	return m
}

func (r *Manager) Start() error {
	w := NewWatcher(r)
	w.Start()
	go r.build(fsnotify.Event{Name: ":start:"})
	go func() {
		for {
			select {
			case event := <-w.Events:
				if event.Op != fsnotify.Chmod {
					go r.build(event)
				}
				w.Remove(event.Name)
				w.Add(event.Name)
			case <-r.context.Done():
				break
			}
		}
	}()
	go func() {
		for {
			select {
			case err := <-w.Errors:
				r.Logger.Error(err)
			case <-r.context.Done():
				break
			}
		}
	}()
	r.runner()
	return nil
}

func (r *Manager) build(event fsnotify.Event) {
	r.gil.Do(func() {
		defer func() {
			r.gil = &sync.Once{}
		}()
		r.buildTransaction(func() error {
			// time.Sleep(r.BuildDelay * time.Millisecond)

			now := time.Now()
			r.Logger.Print("Rebuild on: %s", event.Name)
			cmd := exec.Command("go", "build", "-v", "-i", "-o", r.FullBuildPath())
			err := r.runAndListen(cmd)
			if err != nil {
				if strings.Contains(err.Error(), "no buildable Go source files") {
					r.cancelFunc()
					log.Fatal(err)
				}
				return err
			}

			tt := time.Since(now)
			r.Logger.Success("Building Completed (PID: %d) (Time: %s)", cmd.Process.Pid, tt)
			r.Restart <- true
			return nil
		})
	})
}

func (r *Manager) buildTransaction(fn func() error) {
	lpath := ErrorLogPath()
	err := fn()
	if err != nil {
		f, _ := os.Create(lpath)
		fmt.Fprint(f, err)
		r.Logger.Error("Error!")
		r.Logger.Error(err)
	} else {
		os.Remove(lpath)
	}
}
<commit_msg>Add a batcher to wait for changes<commit_after>package refresh

import (
	"context"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
	"sync"
	"time"

	"github.com/fsnotify/fsnotify"
)

type Manager struct {
	*Configuration
	Logger     *Logger
	Restart    chan bool
	gil        *sync.Once
	ID         string
	context    context.Context
	cancelFunc context.CancelFunc
}

func New(c *Configuration) *Manager {
	return NewWithContext(c, context.Background())
}

func NewWithContext(c *Configuration, ctx context.Context) *Manager {
	ctx, cancelFunc := context.WithCancel(ctx)
	m := &Manager{
		Configuration: c,
		Logger:        NewLogger(c),
		Restart:       make(chan bool),
		gil:           &sync.Once{},
		ID:            ID(),
		context:       ctx,
		cancelFunc:    cancelFunc,
	}
	return m
}

func (r *Manager) Start() error {
	w := NewWatcher(r)
	w.Start()
	go r.build([]fsnotify.Event{fsnotify.Event{Name: ":start:"}})
	tick := time.Tick(r.BuildDelay)
	events := make([]fsnotify.Event, 0)
	go func() {
		for {
			select {
			case event := <-w.Events:
				if event.Op != fsnotify.Chmod {
					events = append(events, event)
				}
				w.Remove(event.Name)
				w.Add(event.Name)
			case <-tick:
				if len(events) == 0 {
					continue
				}
				go r.build(events)
				events = make([]fsnotify.Event, 0)
			case <-r.context.Done():
				break
			}
		}
	}()
	go func() {
		for {
			select {
			case err := <-w.Errors:
				r.Logger.Error(err)
			case <-r.context.Done():
				break
			}
		}
	}()
	r.runner()
	return nil
}

func (r *Manager) build(events []fsnotify.Event) {
	r.gil.Do(func() {
		defer func() {
			r.gil = &sync.Once{}
		}()
		r.buildTransaction(func() error {
			// time.Sleep(r.BuildDelay * time.Millisecond)
			eventNames := make([]string, 0)
			for _, event := range events {
				eventNames = append(eventNames, event.Name)
			}

			now := time.Now()
			r.Logger.Print("Rebuild on: %s", strings.Join(eventNames, ", "))
			cmd := exec.Command("go", "build", "-v", "-i", "-o", r.FullBuildPath())
			err := r.runAndListen(cmd)
			if err != nil {
				if strings.Contains(err.Error(), "no buildable Go source files") {
					r.cancelFunc()
					log.Fatal(err)
				}
				return err
			}

			tt := time.Since(now)
			r.Logger.Success("Building Completed (PID: %d) (Time: %s)", cmd.Process.Pid, tt)
			r.Restart <- true
			return nil
		})
	})
}

func (r *Manager) buildTransaction(fn func() error) {
	lpath := ErrorLogPath()
	err := fn()
	if err != nil {
		f, _ := os.Create(lpath)
		fmt.Fprint(f, err)
		r.Logger.Error("Error!")
		r.Logger.Error(err)
	} else {
		os.Remove(lpath)
	}
}
<|endoftext|>