<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 defaultrolemanager

import (
	"fmt"
	"strings"
	"sync"

	"github.com/casbin/casbin/v2/errors"
	"github.com/casbin/casbin/v2/log"
	"github.com/casbin/casbin/v2/util"
)

const defaultDomain string = ""
const defaultSeparator = "::"

type MatchingFunc func(arg1, arg2 string) bool

// RoleManager provides a default implementation for the RoleManager interface
type RoleManager struct {
	roles              *Roles
	domains            map[string]struct{}
	maxHierarchyLevel  int
	hasPattern         bool
	matchingFunc       MatchingFunc
	hasDomainPattern   bool
	domainMatchingFunc MatchingFunc

	logger log.Logger
}

// NewRoleManager is the constructor for creating an instance of the
// default RoleManager implementation.
func NewRoleManager(maxHierarchyLevel int) *RoleManager {
	rm := RoleManager{}
	rm.roles = &Roles{sync.Map{}}
	rm.domains = make(map[string]struct{})
	rm.maxHierarchyLevel = maxHierarchyLevel
	rm.hasPattern = false
	rm.hasDomainPattern = false

	rm.SetLogger(&log.DefaultLogger{})

	return &rm
}

// AddMatchingFunc support use pattern in g
func (rm *RoleManager) AddMatchingFunc(name string, fn MatchingFunc) {
	rm.hasPattern = true
	rm.matchingFunc = fn
}

// AddDomainMatchingFunc support use domain pattern in g
func (rm *RoleManager) AddDomainMatchingFunc(name string, fn MatchingFunc) {
	rm.hasDomainPattern = true
	rm.domainMatchingFunc = fn
}

// SetLogger sets role manager's logger.
func (rm *RoleManager) SetLogger(logger log.Logger) {
	rm.logger = logger
}

// Clear clears all stored data and resets the role manager to the initial state.
func (rm *RoleManager) Clear() error {
	rm.roles = &Roles{sync.Map{}}
	rm.domains = make(map[string]struct{})
	return nil
}

// AddLink adds the inheritance link between role: name1 and role: name2.
// aka role: name1 inherits role: name2.
func (rm *RoleManager) AddLink(name1 string, name2 string, domain ...string) error {
	switch len(domain) {
	case 0:
		domain = []string{defaultDomain}
		fallthrough
	case 1:
		rm.domains[domain[0]] = struct{}{}
		patternDomain := rm.getPatternDomain(domain[0])

		for _, domain := range patternDomain {
			name1WithDomain := getNameWithDomain(domain, name1)
			name2WithDomain := getNameWithDomain(domain, name2)

			role1 := rm.roles.createRole(name1WithDomain)
			role2 := rm.roles.createRole(name2WithDomain)
			role1.addRole(role2)

			if rm.hasPattern {
				rm.roles.Range(func(key, value interface{}) bool {
					domainPattern, namePattern := getNameAndDomain(key.(string))
					if rm.hasDomainPattern {
						if !rm.domainMatchingFunc(domainPattern, domain) {
							return true
						}
					} else {
						if domainPattern != domain {
							return true
						}
					}
					if rm.matchingFunc(namePattern, name1) && name1 != namePattern && name2 != namePattern {
						valueRole, _ := rm.roles.LoadOrStore(key.(string), newRole(key.(string)))
						valueRole.(*Role).addRole(role1)
					}
					if rm.matchingFunc(namePattern, name2) && name2 != namePattern && name1 != namePattern {
						role2.addRole(value.(*Role))
					}
					if rm.matchingFunc(name1, namePattern) && name1 != namePattern && name2 != namePattern {
						valueRole, _ := rm.roles.LoadOrStore(key.(string), newRole(key.(string)))
						valueRole.(*Role).addRole(role1)
					}
					if rm.matchingFunc(name2, namePattern) && name2 != namePattern && name1 != namePattern {
						role2.addRole(value.(*Role))
					}
					return true
				})
			}
		}
		return nil
	default:
		return errors.ERR_DOMAIN_PARAMETER
	}

}

// DeleteLink deletes the inheritance link between role: name1 and role: name2.
// aka role: name1 does not inherit role: name2 any more.
func (rm *RoleManager) DeleteLink(name1 string, name2 string, domain ...string) error {
	switch len(domain) {
	case 0:
		domain = []string{defaultDomain}
		fallthrough
	case 1:
		name1WithDomain := getNameWithDomain(domain[0], name1)
		name2WithDomain := getNameWithDomain(domain[0], name2)

		_, ok1 := rm.roles.Load(name1WithDomain)
		_, ok2 := rm.roles.Load(name2WithDomain)
		if !ok1 || !ok2 {
			return errors.ERR_NAMES12_NOT_FOUND
		}

		role1 := rm.roles.createRole(name1WithDomain)
		role2 := rm.roles.createRole(name2WithDomain)
		role1.deleteRole(role2)
		return nil
	default:
		return errors.ERR_DOMAIN_PARAMETER
	}
}

func (rm *RoleManager) getPatternDomain(domain string) []string {
	matchedDomains := []string{domain}
	if rm.hasDomainPattern {
		for domainPattern := range rm.domains {
			if domain != domainPattern && rm.domainMatchingFunc(domain, domainPattern) {
				matchedDomains = append(matchedDomains, domainPattern)
			}
		}
	}
	return matchedDomains
}

// HasLink determines whether role: name1 inherits role: name2.
func (rm *RoleManager) HasLink(name1 string, name2 string, domain ...string) (bool, error) {
	switch len(domain) {
	case 0:
		domain = []string{defaultDomain}
		fallthrough
	case 1:
		if name1 == name2 {
			return true, nil
		}

		matchedDomain := rm.getPatternDomain(domain[0])

		for _, domain := range matchedDomain {
			if !rm.roles.hasRole(domain, name1, rm.matchingFunc) || !rm.roles.hasRole(domain, name2, rm.matchingFunc) {
				continue
			}

			name1WithDomain := getNameWithDomain(domain, name1)
			name2WithDomain := getNameWithDomain(domain, name2)

			if rm.hasPattern {
				flag := false
				rm.roles.Range(func(key, value interface{}) bool {
					nameWithDomain := key.(string)
					keyDomain, name := getNameAndDomain(nameWithDomain)
					if rm.hasDomainPattern {
						if !rm.domainMatchingFunc(domain, keyDomain) {
							return true
						}
					} else if domain != keyDomain {
						return true
					}
					if rm.matchingFunc(name1, name) && value.(*Role).hasRoleWithMatchingFunc(name2, rm.maxHierarchyLevel, rm.matchingFunc) {
						flag = true
						return false
					}
					return true
				})
				if flag {
					return true, nil
				}
			} else {
				role1 := rm.roles.createRole(name1WithDomain)
				result := role1.hasRole(name2WithDomain, rm.maxHierarchyLevel)
				if result {
					return true, nil
				}
			}
		}
		return false, nil
	default:
		return false, errors.ERR_DOMAIN_PARAMETER
	}
}

// GetRoles gets the roles that a subject inherits.
func (rm *RoleManager) GetRoles(name string, domain ...string) ([]string, error) {
	switch len(domain) {
	case 0:
		domain = []string{defaultDomain}
		fallthrough
	case 1:
		patternDomain := rm.getPatternDomain(domain[0])

		var gottenRoles []string
		for _, domain := range patternDomain {
			nameWithDomain := getNameWithDomain(domain, name)
			if !rm.roles.hasRole(domain, name, rm.matchingFunc) {
				continue
			}
			gottenRoles = append(gottenRoles, rm.roles.createRole(nameWithDomain).getRoles()...)
		}
		gottenRoles = util.RemoveDuplicateElement(gottenRoles)
		return gottenRoles, nil
	default:
		return nil, errors.ERR_DOMAIN_PARAMETER
	}
}

// GetUsers gets the users that inherits a subject.
// domain is an unreferenced parameter here, may be used in other implementations.
func (rm *RoleManager) GetUsers(name string, domain ...string) ([]string, error) {
	switch len(domain) {
	case 0:
		domain = []string{defaultDomain}
		fallthrough
	case 1:
		patternDomain := rm.getPatternDomain(domain[0])

		var names []string
		for _, domain := range patternDomain {
			nameWithDomain := getNameWithDomain(domain, name)
			if !rm.roles.hasRole(domain, name, rm.domainMatchingFunc) {
				return nil, errors.ERR_NAME_NOT_FOUND
			}

			rm.roles.Range(func(_, value interface{}) bool {
				role := value.(*Role)
				if role.hasDirectRole(nameWithDomain) {
					_, roleName := getNameAndDomain(role.nameWithDomain)
					names = append(names, roleName)
				}
				return true
			})
		}

		return names, nil
	default:
		return nil, errors.ERR_DOMAIN_PARAMETER
	}
}

// PrintRoles prints all the roles to log.
func (rm *RoleManager) PrintRoles() error {
	if !(rm.logger).IsEnabled() {
		return nil
	}

	var roles []string
	rm.roles.Range(func(_, value interface{}) bool {
		if text := value.(*Role).toString(); text != "" {
			roles = append(roles, text)
		}
		return true
	})

	rm.logger.LogRole(roles)
	return nil
}

// GetDomains gets domains that a user has
func (rm *RoleManager) GetDomains(name string) ([]string, error) {
	var domains []string
	for domain := range rm.domains {
		if rm.hasAnyRole(name, domain) {
			domains = append(domains, domain)
		}
	}
	domains = util.RemoveDuplicateElement(domains)
	return domains, nil
}

func (rm *RoleManager) hasAnyRole(name string, domain string) bool {
	patternDomain := rm.getPatternDomain(domain)
	for _, domain := range patternDomain {
		if rm.roles.hasRole(domain, name, rm.matchingFunc) {
			return true
		}
	}
	return false
}

// Roles represents all roles in a domain
type Roles struct {
	sync.Map
}

func (roles *Roles) hasRole(domain, name string, matchingFunc MatchingFunc) bool {
	var ok bool
	if matchingFunc != nil {
		roles.Range(func(key, value interface{}) bool {
			domainPattern, namePattern := getNameAndDomain(key.(string))
			if domainPattern == domain && matchingFunc(name, namePattern) {
				ok = true
			}
			return true
		})
	} else {
		_, ok = roles.Load(getNameWithDomain(domain, name))
	}

	return ok
}

func (roles *Roles) createRole(name string) *Role {
	role, _ := roles.LoadOrStore(name, newRole(name))
	return role.(*Role)
}

// Role represents the data structure for a role in RBAC.
type Role struct {
	nameWithDomain string
	roles          []*Role
}

func newRole(name string) *Role {
	r := Role{}
	r.nameWithDomain = name
	return &r
}

func (r *Role) addRole(role *Role) {
	// determine whether this role has been added
	for _, rr := range r.roles {
		if rr.nameWithDomain == role.nameWithDomain {
			return
		}
	}

	r.roles = append(r.roles, role)
}

func (r *Role) deleteRole(role *Role) {
	for i, rr := range r.roles {
		if rr.nameWithDomain == role.nameWithDomain {
			r.roles = append(r.roles[:i], r.roles[i+1:]...)
			return
		}
	}
}

func (r *Role) hasRole(nameWithDomain string, hierarchyLevel int) bool {
	if r.hasDirectRole(nameWithDomain) {
		return true
	}

	if hierarchyLevel <= 0 {
		return false
	}

	for _, role := range r.roles {
		if role.hasRole(nameWithDomain, hierarchyLevel-1) {
			return true
		}
	}
	return false
}

func (r *Role) hasRoleWithMatchingFunc(name string, hierarchyLevel int, matchingFunc MatchingFunc) bool {
	if r.hasDirectRoleWithMatchingFunc(name, matchingFunc) {
		return true
	}

	if hierarchyLevel <= 0 {
		return false
	}

	for _, role := range r.roles {
		if role.hasRoleWithMatchingFunc(name, hierarchyLevel-1, matchingFunc) {
			return true
		}
	}
	return false
}

func (r *Role) hasDirectRole(nameWithDomain string) bool {
	for _, role := range r.roles {
		if role.nameWithDomain == nameWithDomain {
			return true
		}
	}

	return false
}

func (r *Role) hasDirectRoleWithMatchingFunc(name string, matchingFunc MatchingFunc) bool {
	for _, role := range r.roles {
		_, roleName := getNameAndDomain(role.nameWithDomain)
		if roleName == name || (matchingFunc(name, roleName) && name != roleName) {
			return true
		}
	}

	return false
}

func (r *Role) toString() string {
	if len(r.roles) == 0 {
		return ""
	}

	var sb strings.Builder
	sb.WriteString(r.nameWithDomain)
	sb.WriteString(" < ")
	if len(r.roles) != 1 {
		sb.WriteString("(")
	}

	for i, role := range r.roles {
		if i == 0 {
			sb.WriteString(role.nameWithDomain)
		} else {
			sb.WriteString(", ")
			sb.WriteString(role.nameWithDomain)
		}
	}

	if len(r.roles) != 1 {
		sb.WriteString(")")
	}

	return sb.String()
}

func (r *Role) getRoles() []string {
	names := []string{}
	for _, role := range r.roles {
		_, roleName := getNameAndDomain(role.nameWithDomain)
		names = append(names, roleName)
	}
	return names
}

func getNameWithDomain(domain, name string) string {
	if domain == "" {
		return name
	}
	return fmt.Sprintf("%s%s%s", domain, defaultSeparator, name)
}

func getNameAndDomain(domainAndName string) (string, string) {
	t := strings.Split(domainAndName, defaultSeparator)
	if len(t) == 1 {
		return defaultDomain, t[0]
	}
	return t[0], t[1]
}
<commit_msg>fix: error parameter in GetUsers at default role manager<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 defaultrolemanager

import (
	"fmt"
	"strings"
	"sync"

	"github.com/casbin/casbin/v2/errors"
	"github.com/casbin/casbin/v2/log"
	"github.com/casbin/casbin/v2/util"
)

const defaultDomain string = ""
const defaultSeparator = "::"

type MatchingFunc func(arg1, arg2 string) bool

// RoleManager provides a default implementation for the RoleManager interface
type RoleManager struct {
	roles              *Roles
	domains            map[string]struct{}
	maxHierarchyLevel  int
	hasPattern         bool
	matchingFunc       MatchingFunc
	hasDomainPattern   bool
	domainMatchingFunc MatchingFunc

	logger log.Logger
}

// NewRoleManager is the constructor for creating an instance of the
// default RoleManager implementation.
func NewRoleManager(maxHierarchyLevel int) *RoleManager {
	rm := RoleManager{}
	rm.roles = &Roles{sync.Map{}}
	rm.domains = make(map[string]struct{})
	rm.maxHierarchyLevel = maxHierarchyLevel
	rm.hasPattern = false
	rm.hasDomainPattern = false

	rm.SetLogger(&log.DefaultLogger{})

	return &rm
}

// AddMatchingFunc support use pattern in g
func (rm *RoleManager) AddMatchingFunc(name string, fn MatchingFunc) {
	rm.hasPattern = true
	rm.matchingFunc = fn
}

// AddDomainMatchingFunc support use domain pattern in g
func (rm *RoleManager) AddDomainMatchingFunc(name string, fn MatchingFunc) {
	rm.hasDomainPattern = true
	rm.domainMatchingFunc = fn
}

// SetLogger sets role manager's logger.
func (rm *RoleManager) SetLogger(logger log.Logger) {
	rm.logger = logger
}

// Clear clears all stored data and resets the role manager to the initial state.
func (rm *RoleManager) Clear() error {
	rm.roles = &Roles{sync.Map{}}
	rm.domains = make(map[string]struct{})
	return nil
}

// AddLink adds the inheritance link between role: name1 and role: name2.
// aka role: name1 inherits role: name2.
func (rm *RoleManager) AddLink(name1 string, name2 string, domain ...string) error {
	switch len(domain) {
	case 0:
		domain = []string{defaultDomain}
		fallthrough
	case 1:
		rm.domains[domain[0]] = struct{}{}
		patternDomain := rm.getPatternDomain(domain[0])

		for _, domain := range patternDomain {
			name1WithDomain := getNameWithDomain(domain, name1)
			name2WithDomain := getNameWithDomain(domain, name2)

			role1 := rm.roles.createRole(name1WithDomain)
			role2 := rm.roles.createRole(name2WithDomain)
			role1.addRole(role2)

			if rm.hasPattern {
				rm.roles.Range(func(key, value interface{}) bool {
					domainPattern, namePattern := getNameAndDomain(key.(string))
					if rm.hasDomainPattern {
						if !rm.domainMatchingFunc(domainPattern, domain) {
							return true
						}
					} else {
						if domainPattern != domain {
							return true
						}
					}
					if rm.matchingFunc(namePattern, name1) && name1 != namePattern && name2 != namePattern {
						valueRole, _ := rm.roles.LoadOrStore(key.(string), newRole(key.(string)))
						valueRole.(*Role).addRole(role1)
					}
					if rm.matchingFunc(namePattern, name2) && name2 != namePattern && name1 != namePattern {
						role2.addRole(value.(*Role))
					}
					if rm.matchingFunc(name1, namePattern) && name1 != namePattern && name2 != namePattern {
						valueRole, _ := rm.roles.LoadOrStore(key.(string), newRole(key.(string)))
						valueRole.(*Role).addRole(role1)
					}
					if rm.matchingFunc(name2, namePattern) && name2 != namePattern && name1 != namePattern {
						role2.addRole(value.(*Role))
					}
					return true
				})
			}
		}
		return nil
	default:
		return errors.ERR_DOMAIN_PARAMETER
	}

}

// DeleteLink deletes the inheritance link between role: name1 and role: name2.
// aka role: name1 does not inherit role: name2 any more.
func (rm *RoleManager) DeleteLink(name1 string, name2 string, domain ...string) error {
	switch len(domain) {
	case 0:
		domain = []string{defaultDomain}
		fallthrough
	case 1:
		name1WithDomain := getNameWithDomain(domain[0], name1)
		name2WithDomain := getNameWithDomain(domain[0], name2)

		_, ok1 := rm.roles.Load(name1WithDomain)
		_, ok2 := rm.roles.Load(name2WithDomain)
		if !ok1 || !ok2 {
			return errors.ERR_NAMES12_NOT_FOUND
		}

		role1 := rm.roles.createRole(name1WithDomain)
		role2 := rm.roles.createRole(name2WithDomain)
		role1.deleteRole(role2)
		return nil
	default:
		return errors.ERR_DOMAIN_PARAMETER
	}
}

func (rm *RoleManager) getPatternDomain(domain string) []string {
	matchedDomains := []string{domain}
	if rm.hasDomainPattern {
		for domainPattern := range rm.domains {
			if domain != domainPattern && rm.domainMatchingFunc(domain, domainPattern) {
				matchedDomains = append(matchedDomains, domainPattern)
			}
		}
	}
	return matchedDomains
}

// HasLink determines whether role: name1 inherits role: name2.
func (rm *RoleManager) HasLink(name1 string, name2 string, domain ...string) (bool, error) {
	switch len(domain) {
	case 0:
		domain = []string{defaultDomain}
		fallthrough
	case 1:
		if name1 == name2 {
			return true, nil
		}

		matchedDomain := rm.getPatternDomain(domain[0])

		for _, domain := range matchedDomain {
			if !rm.roles.hasRole(domain, name1, rm.matchingFunc) || !rm.roles.hasRole(domain, name2, rm.matchingFunc) {
				continue
			}

			name1WithDomain := getNameWithDomain(domain, name1)
			name2WithDomain := getNameWithDomain(domain, name2)

			if rm.hasPattern {
				flag := false
				rm.roles.Range(func(key, value interface{}) bool {
					nameWithDomain := key.(string)
					keyDomain, name := getNameAndDomain(nameWithDomain)
					if rm.hasDomainPattern {
						if !rm.domainMatchingFunc(domain, keyDomain) {
							return true
						}
					} else if domain != keyDomain {
						return true
					}
					if rm.matchingFunc(name1, name) && value.(*Role).hasRoleWithMatchingFunc(name2, rm.maxHierarchyLevel, rm.matchingFunc) {
						flag = true
						return false
					}
					return true
				})
				if flag {
					return true, nil
				}
			} else {
				role1 := rm.roles.createRole(name1WithDomain)
				result := role1.hasRole(name2WithDomain, rm.maxHierarchyLevel)
				if result {
					return true, nil
				}
			}
		}
		return false, nil
	default:
		return false, errors.ERR_DOMAIN_PARAMETER
	}
}

// GetRoles gets the roles that a subject inherits.
func (rm *RoleManager) GetRoles(name string, domain ...string) ([]string, error) {
	switch len(domain) {
	case 0:
		domain = []string{defaultDomain}
		fallthrough
	case 1:
		patternDomain := rm.getPatternDomain(domain[0])

		var gottenRoles []string
		for _, domain := range patternDomain {
			nameWithDomain := getNameWithDomain(domain, name)
			if !rm.roles.hasRole(domain, name, rm.matchingFunc) {
				continue
			}
			gottenRoles = append(gottenRoles, rm.roles.createRole(nameWithDomain).getRoles()...)
		}
		gottenRoles = util.RemoveDuplicateElement(gottenRoles)
		return gottenRoles, nil
	default:
		return nil, errors.ERR_DOMAIN_PARAMETER
	}
}

// GetUsers gets the users that inherits a subject.
// domain is an unreferenced parameter here, may be used in other implementations.
func (rm *RoleManager) GetUsers(name string, domain ...string) ([]string, error) {
	switch len(domain) {
	case 0:
		domain = []string{defaultDomain}
		fallthrough
	case 1:
		patternDomain := rm.getPatternDomain(domain[0])

		var names []string
		for _, domain := range patternDomain {
			nameWithDomain := getNameWithDomain(domain, name)
			if !rm.roles.hasRole(domain, name, rm.matchingFunc) {
				return nil, errors.ERR_NAME_NOT_FOUND
			}

			rm.roles.Range(func(_, value interface{}) bool {
				role := value.(*Role)
				if role.hasDirectRole(nameWithDomain) {
					_, roleName := getNameAndDomain(role.nameWithDomain)
					names = append(names, roleName)
				}
				return true
			})
		}

		return names, nil
	default:
		return nil, errors.ERR_DOMAIN_PARAMETER
	}
}

// PrintRoles prints all the roles to log.
func (rm *RoleManager) PrintRoles() error {
	if !(rm.logger).IsEnabled() {
		return nil
	}

	var roles []string
	rm.roles.Range(func(_, value interface{}) bool {
		if text := value.(*Role).toString(); text != "" {
			roles = append(roles, text)
		}
		return true
	})

	rm.logger.LogRole(roles)
	return nil
}

// GetDomains gets domains that a user has
func (rm *RoleManager) GetDomains(name string) ([]string, error) {
	var domains []string
	for domain := range rm.domains {
		if rm.hasAnyRole(name, domain) {
			domains = append(domains, domain)
		}
	}
	domains = util.RemoveDuplicateElement(domains)
	return domains, nil
}

func (rm *RoleManager) hasAnyRole(name string, domain string) bool {
	patternDomain := rm.getPatternDomain(domain)
	for _, domain := range patternDomain {
		if rm.roles.hasRole(domain, name, rm.matchingFunc) {
			return true
		}
	}
	return false
}

// Roles represents all roles in a domain
type Roles struct {
	sync.Map
}

func (roles *Roles) hasRole(domain, name string, matchingFunc MatchingFunc) bool {
	var ok bool
	if matchingFunc != nil {
		roles.Range(func(key, value interface{}) bool {
			domainPattern, namePattern := getNameAndDomain(key.(string))
			if domainPattern == domain && matchingFunc(name, namePattern) {
				ok = true
			}
			return true
		})
	} else {
		_, ok = roles.Load(getNameWithDomain(domain, name))
	}

	return ok
}

func (roles *Roles) createRole(name string) *Role {
	role, _ := roles.LoadOrStore(name, newRole(name))
	return role.(*Role)
}

// Role represents the data structure for a role in RBAC.
type Role struct {
	nameWithDomain string
	roles          []*Role
}

func newRole(name string) *Role {
	r := Role{}
	r.nameWithDomain = name
	return &r
}

func (r *Role) addRole(role *Role) {
	// determine whether this role has been added
	for _, rr := range r.roles {
		if rr.nameWithDomain == role.nameWithDomain {
			return
		}
	}

	r.roles = append(r.roles, role)
}

func (r *Role) deleteRole(role *Role) {
	for i, rr := range r.roles {
		if rr.nameWithDomain == role.nameWithDomain {
			r.roles = append(r.roles[:i], r.roles[i+1:]...)
			return
		}
	}
}

func (r *Role) hasRole(nameWithDomain string, hierarchyLevel int) bool {
	if r.hasDirectRole(nameWithDomain) {
		return true
	}

	if hierarchyLevel <= 0 {
		return false
	}

	for _, role := range r.roles {
		if role.hasRole(nameWithDomain, hierarchyLevel-1) {
			return true
		}
	}
	return false
}

func (r *Role) hasRoleWithMatchingFunc(name string, hierarchyLevel int, matchingFunc MatchingFunc) bool {
	if r.hasDirectRoleWithMatchingFunc(name, matchingFunc) {
		return true
	}

	if hierarchyLevel <= 0 {
		return false
	}

	for _, role := range r.roles {
		if role.hasRoleWithMatchingFunc(name, hierarchyLevel-1, matchingFunc) {
			return true
		}
	}
	return false
}

func (r *Role) hasDirectRole(nameWithDomain string) bool {
	for _, role := range r.roles {
		if role.nameWithDomain == nameWithDomain {
			return true
		}
	}

	return false
}

func (r *Role) hasDirectRoleWithMatchingFunc(name string, matchingFunc MatchingFunc) bool {
	for _, role := range r.roles {
		_, roleName := getNameAndDomain(role.nameWithDomain)
		if roleName == name || (matchingFunc(name, roleName) && name != roleName) {
			return true
		}
	}

	return false
}

func (r *Role) toString() string {
	if len(r.roles) == 0 {
		return ""
	}

	var sb strings.Builder
	sb.WriteString(r.nameWithDomain)
	sb.WriteString(" < ")
	if len(r.roles) != 1 {
		sb.WriteString("(")
	}

	for i, role := range r.roles {
		if i == 0 {
			sb.WriteString(role.nameWithDomain)
		} else {
			sb.WriteString(", ")
			sb.WriteString(role.nameWithDomain)
		}
	}

	if len(r.roles) != 1 {
		sb.WriteString(")")
	}

	return sb.String()
}

func (r *Role) getRoles() []string {
	names := []string{}
	for _, role := range r.roles {
		_, roleName := getNameAndDomain(role.nameWithDomain)
		names = append(names, roleName)
	}
	return names
}

func getNameWithDomain(domain, name string) string {
	if domain == "" {
		return name
	}
	return fmt.Sprintf("%s%s%s", domain, defaultSeparator, name)
}

func getNameAndDomain(domainAndName string) (string, string) {
	t := strings.Split(domainAndName, defaultSeparator)
	if len(t) == 1 {
		return defaultDomain, t[0]
	}
	return t[0], t[1]
}
<|endoftext|>


<commit_before>// use build constraints to work around http://code.google.com/p/go/issues/detail?id=4210
// +build heroku

// note: need at least one blank line after build constraint
package main

import _ "bitbucket.org/liamstask/goose/cmd/goose"
<commit_msg>Delete install_goose.go<commit_after><|endoftext|>


<commit_before>package tview

import (
	"github.com/gdamore/tcell/v2"
)

// Checkbox implements a simple box for boolean values which can be checked and
// unchecked.
//
// See https://github.com/rivo/tview/wiki/Checkbox for an example.
type Checkbox struct {
	*Box

	// Whether or not this box is checked.
	checked bool

	// The text to be displayed before the input area.
	label string

	// The screen width of the label area. A value of 0 means use the width of
	// the label text.
	labelWidth int

	// The label color.
	labelColor tcell.Color

	// The background color of the input area.
	fieldBackgroundColor tcell.Color

	// The text color of the input area.
	fieldTextColor tcell.Color

	// An optional function which is called when the user changes the checked
	// state of this checkbox.
	changed func(checked bool)

	// An optional function which is called when the user indicated that they
	// are done entering text. The key which was pressed is provided (tab,
	// shift-tab, or escape).
	done func(tcell.Key)

	// A callback function set by the Form class and called when the user leaves
	// this form item.
	finished func(tcell.Key)
}

// NewCheckbox returns a new input field.
func NewCheckbox() *Checkbox {
	return &Checkbox{
		Box:                  NewBox(),
		labelColor:           Styles.SecondaryTextColor,
		fieldBackgroundColor: Styles.ContrastBackgroundColor,
		fieldTextColor:       Styles.PrimaryTextColor,
	}
}

// SetChecked sets the state of the checkbox.
func (c *Checkbox) SetChecked(checked bool) *Checkbox {
	c.checked = checked
	return c
}

// IsChecked returns whether or not the box is checked.
func (c *Checkbox) IsChecked() bool {
	return c.checked
}

// SetLabel sets the text to be displayed before the input area.
func (c *Checkbox) SetLabel(label string) *Checkbox {
	c.label = label
	return c
}

// GetLabel returns the text to be displayed before the input area.
func (c *Checkbox) GetLabel() string {
	return c.label
}

// SetLabelWidth sets the screen width of the label. A value of 0 will cause the
// primitive to use the width of the label string.
func (c *Checkbox) SetLabelWidth(width int) *Checkbox {
	c.labelWidth = width
	return c
}

// SetLabelColor sets the color of the label.
func (c *Checkbox) SetLabelColor(color tcell.Color) *Checkbox {
	c.labelColor = color
	return c
}

// SetFieldBackgroundColor sets the background color of the input area.
func (c *Checkbox) SetFieldBackgroundColor(color tcell.Color) *Checkbox {
	c.fieldBackgroundColor = color
	return c
}

// SetFieldTextColor sets the text color of the input area.
func (c *Checkbox) SetFieldTextColor(color tcell.Color) *Checkbox {
	c.fieldTextColor = color
	return c
}

// SetFormAttributes sets attributes shared by all form items.
func (c *Checkbox) SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem {
	c.labelWidth = labelWidth
	c.labelColor = labelColor
	c.backgroundColor = bgColor
	c.fieldTextColor = fieldTextColor
	c.fieldBackgroundColor = fieldBgColor
	return c
}

// GetFieldWidth returns this primitive's field width.
func (c *Checkbox) GetFieldWidth() int {
	return 1
}

// SetChangedFunc sets a handler which is called when the checked state of this
// checkbox was changed by the user. The handler function receives the new
// state.
func (c *Checkbox) SetChangedFunc(handler func(checked bool)) *Checkbox {
	c.changed = handler
	return c
}

// SetDoneFunc sets a handler which is called when the user is done using the
// checkbox. The callback function is provided with the key that was pressed,
// which is one of the following:
//
//   - KeyEscape: Abort text input.
//   - KeyTab: Move to the next field.
//   - KeyBacktab: Move to the previous field.
func (c *Checkbox) SetDoneFunc(handler func(key tcell.Key)) *Checkbox {
	c.done = handler
	return c
}

// SetFinishedFunc sets a callback invoked when the user leaves this form item.
func (c *Checkbox) SetFinishedFunc(handler func(key tcell.Key)) FormItem {
	c.finished = handler
	return c
}

// Draw draws this primitive onto the screen.
func (c *Checkbox) Draw(screen tcell.Screen) {
	c.Box.DrawForSubclass(screen, c)

	// Prepare
	x, y, width, height := c.GetInnerRect()
	rightLimit := x + width
	if height < 1 || rightLimit <= x {
		return
	}

	// Draw label.
	if c.labelWidth > 0 {
		labelWidth := c.labelWidth
		if labelWidth > rightLimit-x {
			labelWidth = rightLimit - x
		}
		Print(screen, c.label, x, y, labelWidth, AlignLeft, c.labelColor)
		x += labelWidth
	} else {
		_, drawnWidth := Print(screen, c.label, x, y, rightLimit-x, AlignLeft, c.labelColor)
		x += drawnWidth
	}

	// Draw checkbox.
	fieldStyle := tcell.StyleDefault.Background(c.fieldBackgroundColor).Foreground(c.fieldTextColor)
	if c.HasFocus() {
		fieldStyle = fieldStyle.Background(c.fieldTextColor).Foreground(c.fieldBackgroundColor)
	}
	checkedRune := 'X'
	if !c.checked {
		checkedRune = ' '
	}
	screen.SetContent(x, y, checkedRune, nil, fieldStyle)
}

// InputHandler returns the handler for this primitive.
func (c *Checkbox) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
	return c.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
		// Process key event.
		switch key := event.Key(); key {
		case tcell.KeyRune, tcell.KeyEnter: // Check.
			if key == tcell.KeyRune && event.Rune() != ' ' {
				break
			}
			c.checked = !c.checked
			if c.changed != nil {
				c.changed(c.checked)
			}
		case tcell.KeyTab, tcell.KeyBacktab, tcell.KeyEscape: // We're done.
			if c.done != nil {
				c.done(key)
			}
			if c.finished != nil {
				c.finished(key)
			}
		}
	})
}

// MouseHandler returns the mouse handler for this primitive.
func (c *Checkbox) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
	return c.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
		x, y := event.Position()
		_, rectY, _, _ := c.GetInnerRect()
		if !c.InRect(x, y) {
			return false, nil
		}

		// Process mouse event.
		if action == MouseLeftClick && y == rectY {
			setFocus(c)
			c.checked = !c.checked
			if c.changed != nil {
				c.changed(c.checked)
			}
			consumed = true
		}

		return
	})
}
<commit_msg>Customization of strings displayed when a checkbox is checked. Resolves #518<commit_after>package tview

import (
	"strings"

	"github.com/gdamore/tcell/v2"
)

// Checkbox implements a simple box for boolean values which can be checked and
// unchecked.
//
// See https://github.com/rivo/tview/wiki/Checkbox for an example.
type Checkbox struct {
	*Box

	// Whether or not this box is checked.
	checked bool

	// The text to be displayed before the input area.
	label string

	// The screen width of the label area. A value of 0 means use the width of
	// the label text.
	labelWidth int

	// The label color.
	labelColor tcell.Color

	// The background color of the input area.
	fieldBackgroundColor tcell.Color

	// The text color of the input area.
	fieldTextColor tcell.Color

	// The string use to display a checked box.
	checkedString string

	// An optional function which is called when the user changes the checked
	// state of this checkbox.
	changed func(checked bool)

	// An optional function which is called when the user indicated that they
	// are done entering text. The key which was pressed is provided (tab,
	// shift-tab, or escape).
	done func(tcell.Key)

	// A callback function set by the Form class and called when the user leaves
	// this form item.
	finished func(tcell.Key)
}

// NewCheckbox returns a new input field.
func NewCheckbox() *Checkbox {
	return &Checkbox{
		Box:                  NewBox(),
		labelColor:           Styles.SecondaryTextColor,
		fieldBackgroundColor: Styles.ContrastBackgroundColor,
		fieldTextColor:       Styles.PrimaryTextColor,
		checkedString:        "X",
	}
}

// SetChecked sets the state of the checkbox.
func (c *Checkbox) SetChecked(checked bool) *Checkbox {
	c.checked = checked
	return c
}

// IsChecked returns whether or not the box is checked.
func (c *Checkbox) IsChecked() bool {
	return c.checked
}

// SetLabel sets the text to be displayed before the input area.
func (c *Checkbox) SetLabel(label string) *Checkbox {
	c.label = label
	return c
}

// GetLabel returns the text to be displayed before the input area.
func (c *Checkbox) GetLabel() string {
	return c.label
}

// SetLabelWidth sets the screen width of the label. A value of 0 will cause the
// primitive to use the width of the label string.
func (c *Checkbox) SetLabelWidth(width int) *Checkbox {
	c.labelWidth = width
	return c
}

// SetLabelColor sets the color of the label.
func (c *Checkbox) SetLabelColor(color tcell.Color) *Checkbox {
	c.labelColor = color
	return c
}

// SetFieldBackgroundColor sets the background color of the input area.
func (c *Checkbox) SetFieldBackgroundColor(color tcell.Color) *Checkbox {
	c.fieldBackgroundColor = color
	return c
}

// SetFieldTextColor sets the text color of the input area.
func (c *Checkbox) SetFieldTextColor(color tcell.Color) *Checkbox {
	c.fieldTextColor = color
	return c
}

// SetCheckedString sets the string to be displayed when the checkbox is
// checked (defaults to "X").
func (c *Checkbox) SetCheckedString(checked string) *Checkbox {
	c.checkedString = checked
	return c
}

// SetFormAttributes sets attributes shared by all form items.
func (c *Checkbox) SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem {
	c.labelWidth = labelWidth
	c.labelColor = labelColor
	c.backgroundColor = bgColor
	c.fieldTextColor = fieldTextColor
	c.fieldBackgroundColor = fieldBgColor
	return c
}

// GetFieldWidth returns this primitive's field width.
func (c *Checkbox) GetFieldWidth() int {
	return 1
}

// SetChangedFunc sets a handler which is called when the checked state of this
// checkbox was changed by the user. The handler function receives the new
// state.
func (c *Checkbox) SetChangedFunc(handler func(checked bool)) *Checkbox {
	c.changed = handler
	return c
}

// SetDoneFunc sets a handler which is called when the user is done using the
// checkbox. The callback function is provided with the key that was pressed,
// which is one of the following:
//
//   - KeyEscape: Abort text input.
//   - KeyTab: Move to the next field.
//   - KeyBacktab: Move to the previous field.
func (c *Checkbox) SetDoneFunc(handler func(key tcell.Key)) *Checkbox {
	c.done = handler
	return c
}

// SetFinishedFunc sets a callback invoked when the user leaves this form item.
func (c *Checkbox) SetFinishedFunc(handler func(key tcell.Key)) FormItem {
	c.finished = handler
	return c
}

// Draw draws this primitive onto the screen.
func (c *Checkbox) Draw(screen tcell.Screen) {
	c.Box.DrawForSubclass(screen, c)

	// Prepare
	x, y, width, height := c.GetInnerRect()
	rightLimit := x + width
	if height < 1 || rightLimit <= x {
		return
	}

	// Draw label.
	if c.labelWidth > 0 {
		labelWidth := c.labelWidth
		if labelWidth > rightLimit-x {
			labelWidth = rightLimit - x
		}
		Print(screen, c.label, x, y, labelWidth, AlignLeft, c.labelColor)
		x += labelWidth
	} else {
		_, drawnWidth := Print(screen, c.label, x, y, rightLimit-x, AlignLeft, c.labelColor)
		x += drawnWidth
	}

	// Draw checkbox.
	fieldStyle := tcell.StyleDefault.Background(c.fieldBackgroundColor).Foreground(c.fieldTextColor)
	if c.HasFocus() {
		fieldStyle = fieldStyle.Background(c.fieldTextColor).Foreground(c.fieldBackgroundColor)
	}
	checkboxWidth := stringWidth(c.checkedString)
	checkedString := c.checkedString
	if !c.checked {
		checkedString = strings.Repeat(" ", checkboxWidth)
	}
	printWithStyle(screen, checkedString, x, y, checkboxWidth, AlignLeft, fieldStyle)
}

// InputHandler returns the handler for this primitive.
func (c *Checkbox) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
	return c.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
		// Process key event.
		switch key := event.Key(); key {
		case tcell.KeyRune, tcell.KeyEnter: // Check.
			if key == tcell.KeyRune && event.Rune() != ' ' {
				break
			}
			c.checked = !c.checked
			if c.changed != nil {
				c.changed(c.checked)
			}
		case tcell.KeyTab, tcell.KeyBacktab, tcell.KeyEscape: // We're done.
			if c.done != nil {
				c.done(key)
			}
			if c.finished != nil {
				c.finished(key)
			}
		}
	})
}

// MouseHandler returns the mouse handler for this primitive.
func (c *Checkbox) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
	return c.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
		x, y := event.Position()
		_, rectY, _, _ := c.GetInnerRect()
		if !c.InRect(x, y) {
			return false, nil
		}

		// Process mouse event.
		if action == MouseLeftClick && y == rectY {
			setFocus(c)
			c.checked = !c.checked
			if c.changed != nil {
				c.changed(c.checked)
			}
			consumed = true
		}

		return
	})
}
<|endoftext|>


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

package sort_test

import (
	"fmt"
	"sort"
)

type Person struct {
	Name string
	Age  int
}

func (p Person) String() string {
	return fmt.Sprintf("%s: %d", p.Name, p.Age)
}

// ByAge implements sort.Interface for []Person based on
// the Age field.
type ByAge []*Person

func (a ByAge) Len() int           { return len(a) }
func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }

func ExampleInterface() {
	people := []*Person{
		&Person{Name: "Bob", Age: 31},
		&Person{Name: "John", Age: 42},
		&Person{Name: "Michael", Age: 17},
		&Person{Name: "Jenny", Age: 26},
	}

	fmt.Println(people)
	sort.Sort(ByAge(people))
	fmt.Println(people)

	// Output:
	// [Bob: 31 John: 42 Michael: 17 Jenny: 26]
	// [Michael: 17 Jenny: 26 Bob: 31 John: 42]
}
<commit_msg>sort: move example to package level and simplify further<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sort_test

import (
	"fmt"
	"sort"
)

type Person struct {
	Name string
	Age  int
}

func (p Person) String() string {
	return fmt.Sprintf("%s: %d", p.Name, p.Age)
}

// ByAge implements sort.Interface for []Person based on
// the Age field.
type ByAge []Person

func (a ByAge) Len() int           { return len(a) }
func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }

func Example() {
	people := []Person{
		{"Bob", 31},
		{"John", 42},
		{"Michael", 17},
		{"Jenny", 26},
	}

	fmt.Println(people)
	sort.Sort(ByAge(people))
	fmt.Println(people)

	// Output:
	// [Bob: 31 John: 42 Michael: 17 Jenny: 26]
	// [Michael: 17 Jenny: 26 Bob: 31 John: 42]
}
<|endoftext|>


<commit_before>/*
   Copyright (c) 2014, Percona LLC and/or its affiliates. All rights reserved.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package instance

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/percona/cloud-protocol/proto"
	"github.com/percona/cloud-tools/pct"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"sync"
)

type Repo struct {
	logger    *pct.Logger
	configDir string
	it        map[string]interface{}
	mux       *sync.RWMutex
}

func NewRepo(logger *pct.Logger, configDir string) *Repo {
	m := &Repo{
		logger:    logger,
		configDir: configDir,
		// --
		it:  make(map[string]interface{}),
		mux: &sync.RWMutex{},
	}
	return m
}

func (r *Repo) Init() error {
	for service, _ := range proto.ExternalService {
		if err := r.loadInstances(service); err != nil {
			return err
		}
	}
	return nil
}

func (r *Repo) loadInstances(service string) error {
	files, err := filepath.Glob(r.configDir + "/" + service + "-*.conf")
	if err != nil {
		return err
	}

	for _, file := range files {
		r.logger.Debug("Reading " + file)

		// 0       1
		// service-id
		part := strings.Split(strings.TrimSuffix(filepath.Base(file), ".conf"), "-")
		if len(part) != 2 {
			return errors.New("Invalid instance file name: " + file)
		}
		service := part[0]
		id, err := strconv.ParseUint(part[1], 10, 32)
		if err != nil {
			return err
		}
		if !valid(service, uint(id)) {
			return pct.InvalidServiceInstanceError{Service: service, Id: uint(id)}
		}

		data, err := ioutil.ReadFile(file)
		if err != nil {
			return errors.New(file + ":" + err.Error())
		}

		if err := r.Add(service, uint(id), data, false); err != nil {
			return errors.New(file + ":" + err.Error())
		}

		r.logger.Info("Loaded " + file)
	}
	return nil
}

func (r *Repo) Add(service string, id uint, data []byte, writeToDisk bool) error {
	if !valid(service, id) {
		return pct.InvalidServiceInstanceError{Service: service, Id: id}
	}

	var info interface{}
	switch service {
	case "server":
		it := &proto.ServerInstance{}
		if err := json.Unmarshal(data, it); err != nil {
			return errors.New("instance.Repo:json.Unmarshal:" + err.Error())
		}
		info = it
	case "mysql":
		it := &proto.MySQLInstance{}
		if err := json.Unmarshal(data, it); err != nil {
			return errors.New("instance.Repo:json.Unmarshal:" + err.Error())
		}
		info = it
	default:
		return errors.New(fmt.Sprintf("Invalid service name: %s", service))
	}

	r.mux.Lock()
	defer r.mux.Unlock()

	name := r.Name(service, id)
	if _, ok := r.it[name]; ok {
		return pct.DuplicateServiceInstanceError{Service: service, Id: id}
	}

	if writeToDisk {
		if err := pct.Basedir.WriteConfig(name, info); err != nil {
			return err
		}
	}

	r.it[name] = info
	return nil
}

func (r *Repo) Get(service string, id uint, info interface{}) error {
	if reflect.ValueOf(info).Kind() != reflect.Ptr {
		log.Fatal("info arg is not a pointer; need &T{}")
	}

	if !valid(service, id) {
		return pct.InvalidServiceInstanceError{Service: service, Id: id}
	}

	r.mux.Lock()
	defer r.mux.Unlock()

	name := r.Name(service, id)
	it, ok := r.it[name]
	if !ok {
		return pct.UnknownServiceInstanceError{Service: service, Id: id}
	}

	/**
	 * Yes, we need reflection because "everything in Go is passed by value"
	 * (http://golang.org/doc/faq#Pointers).  When the caller passes a pointer
	 * to a struct (*T) as an interface{} arg, the function receives a new
	 * interface that contains a pointer to the struct.  Therefore, setting
	 * info = it only sets the new interface, not the underlying struct.
	 * The only way to access and change the underlying struct of an interface
	 * is with reflection.  The nex two lines might not make any sense until
	 * you grok reflection; I leave that to you.
	 */
	infoVal := reflect.ValueOf(info).Elem()
	infoVal.Set(reflect.ValueOf(it).Elem())

	return nil
}

func (r *Repo) Remove(service string, id uint) error {
	// todo: API --> agent --> side.Remove()
	// Agent should stop all services using the instance before call this.
	if !valid(service, id) {
		return pct.InvalidServiceInstanceError{Service: service, Id: id}
	}

	r.mux.Lock()
	defer r.mux.Unlock()

	name := r.Name(service, id)
	if _, ok := r.it[name]; !ok {
		return pct.UnknownServiceInstanceError{Service: service, Id: id}
	}

	file := r.configDir + "/" + name + ".conf"
	r.logger.Info("Removing", file)
	if err := os.Remove(file); err != nil {
		return err
	}

	delete(r.it, name)
	return nil
}

func valid(service string, id uint) bool {
	if _, ok := proto.ExternalService[service]; !ok {
		return false
	}
	if id == 0 {
		return false
	}
	return true
}

func (r *Repo) Name(service string, id uint) string {
	return fmt.Sprintf("%s-%d", service, id)
}

func (r *Repo) List() []string {
	instances := []string{}
	for name, _ := range r.it {
		instances = append(instances, name)
	}
	return instances
}
<commit_msg>Guard instances.List() with mutex.<commit_after>/*
   Copyright (c) 2014, Percona LLC and/or its affiliates. All rights reserved.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package instance

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/percona/cloud-protocol/proto"
	"github.com/percona/cloud-tools/pct"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"sync"
)

type Repo struct {
	logger    *pct.Logger
	configDir string
	it        map[string]interface{}
	mux       *sync.RWMutex
}

func NewRepo(logger *pct.Logger, configDir string) *Repo {
	m := &Repo{
		logger:    logger,
		configDir: configDir,
		// --
		it:  make(map[string]interface{}),
		mux: &sync.RWMutex{},
	}
	return m
}

func (r *Repo) Init() error {
	for service, _ := range proto.ExternalService {
		if err := r.loadInstances(service); err != nil {
			return err
		}
	}
	return nil
}

func (r *Repo) loadInstances(service string) error {
	files, err := filepath.Glob(r.configDir + "/" + service + "-*.conf")
	if err != nil {
		return err
	}

	for _, file := range files {
		r.logger.Debug("Reading " + file)

		// 0       1
		// service-id
		part := strings.Split(strings.TrimSuffix(filepath.Base(file), ".conf"), "-")
		if len(part) != 2 {
			return errors.New("Invalid instance file name: " + file)
		}
		service := part[0]
		id, err := strconv.ParseUint(part[1], 10, 32)
		if err != nil {
			return err
		}
		if !valid(service, uint(id)) {
			return pct.InvalidServiceInstanceError{Service: service, Id: uint(id)}
		}

		data, err := ioutil.ReadFile(file)
		if err != nil {
			return errors.New(file + ":" + err.Error())
		}

		if err := r.Add(service, uint(id), data, false); err != nil {
			return errors.New(file + ":" + err.Error())
		}

		r.logger.Info("Loaded " + file)
	}
	return nil
}

func (r *Repo) Add(service string, id uint, data []byte, writeToDisk bool) error {
	if !valid(service, id) {
		return pct.InvalidServiceInstanceError{Service: service, Id: id}
	}

	var info interface{}
	switch service {
	case "server":
		it := &proto.ServerInstance{}
		if err := json.Unmarshal(data, it); err != nil {
			return errors.New("instance.Repo:json.Unmarshal:" + err.Error())
		}
		info = it
	case "mysql":
		it := &proto.MySQLInstance{}
		if err := json.Unmarshal(data, it); err != nil {
			return errors.New("instance.Repo:json.Unmarshal:" + err.Error())
		}
		info = it
	default:
		return errors.New(fmt.Sprintf("Invalid service name: %s", service))
	}

	r.mux.Lock()
	defer r.mux.Unlock()

	name := r.Name(service, id)
	if _, ok := r.it[name]; ok {
		return pct.DuplicateServiceInstanceError{Service: service, Id: id}
	}

	if writeToDisk {
		if err := pct.Basedir.WriteConfig(name, info); err != nil {
			return err
		}
	}

	r.it[name] = info
	return nil
}

func (r *Repo) Get(service string, id uint, info interface{}) error {
	if reflect.ValueOf(info).Kind() != reflect.Ptr {
		log.Fatal("info arg is not a pointer; need &T{}")
	}

	if !valid(service, id) {
		return pct.InvalidServiceInstanceError{Service: service, Id: id}
	}

	r.mux.Lock()
	defer r.mux.Unlock()

	name := r.Name(service, id)
	it, ok := r.it[name]
	if !ok {
		return pct.UnknownServiceInstanceError{Service: service, Id: id}
	}

	/**
	 * Yes, we need reflection because "everything in Go is passed by value"
	 * (http://golang.org/doc/faq#Pointers).  When the caller passes a pointer
	 * to a struct (*T) as an interface{} arg, the function receives a new
	 * interface that contains a pointer to the struct.  Therefore, setting
	 * info = it only sets the new interface, not the underlying struct.
	 * The only way to access and change the underlying struct of an interface
	 * is with reflection.  The nex two lines might not make any sense until
	 * you grok reflection; I leave that to you.
	 */
	infoVal := reflect.ValueOf(info).Elem()
	infoVal.Set(reflect.ValueOf(it).Elem())

	return nil
}

func (r *Repo) Remove(service string, id uint) error {
	// todo: API --> agent --> side.Remove()
	// Agent should stop all services using the instance before call this.
	if !valid(service, id) {
		return pct.InvalidServiceInstanceError{Service: service, Id: id}
	}

	r.mux.Lock()
	defer r.mux.Unlock()

	name := r.Name(service, id)
	if _, ok := r.it[name]; !ok {
		return pct.UnknownServiceInstanceError{Service: service, Id: id}
	}

	file := r.configDir + "/" + name + ".conf"
	r.logger.Info("Removing", file)
	if err := os.Remove(file); err != nil {
		return err
	}

	delete(r.it, name)
	return nil
}

func valid(service string, id uint) bool {
	if _, ok := proto.ExternalService[service]; !ok {
		return false
	}
	if id == 0 {
		return false
	}
	return true
}

func (r *Repo) Name(service string, id uint) string {
	return fmt.Sprintf("%s-%d", service, id)
}

func (r *Repo) List() []string {
	r.mux.Lock()
	defer r.mux.Unlock()
	instances := []string{}
	for name, _ := range r.it {
		instances = append(instances, name)
	}
	return instances
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

/*
This test file is part of the btcchain package rather than than the
btcchain_test package so it can bridge access to the internals to properly test
cases which are either not possible or can't reliably be tested via the public
interface.  The functions are only exported while the tests are being run.
*/

package btcchain

import (
	"time"

	"github.com/conformal/btcutil"
)

// TstSetCoinbaseMaturity makes the ability to set the coinbase maturity
// available to the test package.
func TstSetCoinbaseMaturity(maturity int64) {
	coinbaseMaturity = maturity
}

// TstTimeSorter makes the internal timeSorter type available to the test
// package.
func TstTimeSorter(times []time.Time) timeSorter {
	return timeSorter(times)
}

// TstCheckSerializedHeight makes the internal checkSerializedHeight function
// available to the test package.
func TstCheckSerializedHeight(coinbaseTx *btcutil.Tx, wantHeight int64) error {
	return checkSerializedHeight(coinbaseTx, wantHeight)
}

// TstSetMaxMedianTimeEntries makes the ability to set the maximum number of
// median tiem entries available to the test package.
func TstSetMaxMedianTimeEntries(val int) {
	maxMedianTimeEntries = val
}
<commit_msg>Modify the time sorter tests to make golint happy.<commit_after>// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

/*
This test file is part of the btcchain package rather than than the
btcchain_test package so it can bridge access to the internals to properly test
cases which are either not possible or can't reliably be tested via the public
interface.  The functions are only exported while the tests are being run.
*/

package btcchain

import (
	"sort"
	"time"

	"github.com/conformal/btcutil"
)

// TstSetCoinbaseMaturity makes the ability to set the coinbase maturity
// available to the test package.
func TstSetCoinbaseMaturity(maturity int64) {
	coinbaseMaturity = maturity
}

// TstTimeSorter makes the internal timeSorter type available to the test
// package.
func TstTimeSorter(times []time.Time) sort.Interface {
	return timeSorter(times)
}

// TstCheckSerializedHeight makes the internal checkSerializedHeight function
// available to the test package.
func TstCheckSerializedHeight(coinbaseTx *btcutil.Tx, wantHeight int64) error {
	return checkSerializedHeight(coinbaseTx, wantHeight)
}

// TstSetMaxMedianTimeEntries makes the ability to set the maximum number of
// median tiem entries available to the test package.
func TstSetMaxMedianTimeEntries(val int) {
	maxMedianTimeEntries = val
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The logr 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 logr defines abstract interfaces for logging.  Packages can depend on
// these interfaces and callers can implement logging in whatever way is
// appropriate.
//
// This design derives from Dave Cheney's blog:
//     http://dave.cheney.net/2015/11/05/lets-talk-about-logging
//
// This is a BETA grade API.  Until there is a significant 2nd implementation,
// I don't really know how it will change.
//
// The logging specifically makes it non-trivial to use format strings, to encourage
// attaching structured information instead of unstructured format strings.
//
// Usage
//
// Logging is done using a Logger.  Loggers can have name prefixes and named
// values attached, so that all log messages logged with that Logger have some
// base context associated.
//
// The term "key" is used to refer to the name associated with a particular
// value, to disambiguate it from the general Logger name.
//
// For instance, suppose we're trying to reconcile the state of an object, and
// we want to log that we've made some decision.
//
// With the traditional log package, we might write:
//  log.Printf(
//      "decided to set field foo to value %q for object %s/%s",
//       targetValue, object.Namespace, object.Name)
//
// With logr's structured logging, we'd write:
//  // elsewhere in the file, set up the logger to log with the prefix of "reconcilers",
//  // and the named value target-type=Foo, for extra context.
//  log := mainLogger.WithName("reconcilers").WithValues("target-type", "Foo")
//
//  // later on...
//  log.Info("setting field foo on object", "value", targetValue, "object", object)
//
// Depending on our logging implementation, we could then make logging decisions
// based on field values (like only logging such events for objects in a certain
// namespace), or copy the structured information into a structured log store.
//
// For logging errors, Logger has a method called Error.  Suppose we wanted to
// log an error while reconciling.  With the traditional log package, we might
// write:
//   log.Errorf("unable to reconcile object %s/%s: %v", object.Namespace, object.Name, err)
//
// With logr, we'd instead write:
//   // assuming the above setup for log
//   log.Error(err, "unable to reconcile object", "object", object)
//
// This functions similarly to:
//   log.Info("unable to reconcile object", "error", err, "object", object)
//
// However, it ensures that a standard key for the error value ("error") is used
// across all error logging.  Furthermore, certain implementations may choose to
// attach additional information (such as stack traces) on calls to Error, so
// it's preferred to use Error to log errors.
//
// Parts of a log line
//
// Each log message from a Logger has four types of context:
// logger name, log verbosity, log message, and the named values.
//
// The Logger name constists of a series of name "segments" added by successive
// calls to WithName.  These name segments will be joined in some way by the
// underlying implementation.  It is strongly reccomended that name segements
// contain simple identifiers (letters, digits, and hyphen), and do not contain
// characters that could muddle the log output or confuse the joining operation
// (e.g.  whitespace, commas, periods, slashes, brackets, quotes, etc).
//
// Log verbosity represents how little a log matters.  Level zero, the default,
// matters most.  Increasing levels matter less and less.  Try to avoid lots of
// different verbosity levels, and instead provide useful keys, logger names,
// and log messages for users to filter on.  It's illegal to pass a log level
// below zero.
//
// The log message consists of a constant message attached to the the log line.
// This should generally be a simple description of what's occuring, and should
// never be a format string.
//
// Variable information can then be attached using named values (key/value
// pairs).  Keys are arbitrary strings, while values may be any Go value.
//
// Key Naming Conventions
//
// Keys are not strictly required to conform to any specification or regex, but
// it is recommended that they:
//   * be human-readable and meaningful (not auto-generated or simple ordinals)
//   * be constant (not dependent on input data)
//   * contain only printable characters
//   * not contain whitespace or punctuation
//
// These guidelines help ensure that log data is processed properly regardless
// of the log implementation.  For example, log implementations will try to
// output JSON data or will store data for later database (e.g. SQL) queries.
//
// While users are generally free to use key names of their choice, it's
// generally best to avoid using the following keys, as they're frequently used
// by implementations:
//
// - `"caller"`: the calling information (file/line) of a particular log line.
// - `"error"`: the underlying error value in the `Error` method.
// - `"level"`: the log level.
// - `"logger"`: the name of the associated logger.
// - `"msg"`: the log message.
// - `"stacktrace"`: the stack trace associated with a particular log line or
//                   error (often from the `Error` message).
// - `"ts"`: the timestamp for a log line.
//
// Implementations are encouraged to make use of these keys to represent the
// above concepts, when neccessary (for example, in a pure-JSON output form, it
// would be necessary to represent at least message and timestamp as ordinary
// named values).
package logr

// TODO: consider adding back in format strings if they're really needed
// TODO: consider other bits of zap/zapcore functionality like ObjectMarshaller (for arbitrary objects)
// TODO: consider other bits of glog functionality like Flush, InfoDepth, OutputStats

// Logger represents the ability to log messages, both errors and not.
type Logger interface {
	// Enabled tests whether this Logger is enabled.  For example, commandline
	// flags might be used to set the logging verbosity and disable some info
	// logs.
	Enabled() bool

	// Info logs a non-error message with the given key/value pairs as context.
	//
	// The msg argument should be used to add some constant description to
	// the log line.  The key/value pairs can then be used to add additional
	// variable information.  The key/value pairs should alternate string
	// keys and arbitrary values.
	Info(msg string, keysAndValues ...interface{})

	// Error logs an error, with the given message and key/value pairs as context.
	// It functions similarly to calling Info with the "error" named value, but may
	// have unique behavior, and should be preferred for logging errors (see the
	// package documentations for more information).
	//
	// The msg field should be used to add context to any underlying error,
	// while the err field should be used to attach the actual error that
	// triggered this log line, if present.
	Error(err error, msg string, keysAndValues ...interface{})

	// V returns an Logger value for a specific verbosity level, relative to
	// this Logger.  In other words, V values are additive.  V higher verbosity
	// level means a log message is less important.  It's illegal to pass a log
	// level less than zero.
	V(level int) Logger

	// WithValues adds some key-value pairs of context to a logger.
	// See Info for documentation on how key/value pairs work.
	WithValues(keysAndValues ...interface{}) Logger

	// WithName adds a new element to the logger's name.
	// Successive calls with WithName continue to append
	// suffixes to the logger's name.  It's strongly reccomended
	// that name segments contain only letters, digits, and hyphens
	// (see the package documentation for more information).
	WithName(name string) Logger
}
<commit_msg>Fix a few typos in the go doc<commit_after>/*
Copyright 2019 The logr 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 logr defines abstract interfaces for logging.  Packages can depend on
// these interfaces and callers can implement logging in whatever way is
// appropriate.
//
// This design derives from Dave Cheney's blog:
//     http://dave.cheney.net/2015/11/05/lets-talk-about-logging
//
// This is a BETA grade API.  Until there is a significant 2nd implementation,
// I don't really know how it will change.
//
// The logging specifically makes it non-trivial to use format strings, to encourage
// attaching structured information instead of unstructured format strings.
//
// Usage
//
// Logging is done using a Logger.  Loggers can have name prefixes and named
// values attached, so that all log messages logged with that Logger have some
// base context associated.
//
// The term "key" is used to refer to the name associated with a particular
// value, to disambiguate it from the general Logger name.
//
// For instance, suppose we're trying to reconcile the state of an object, and
// we want to log that we've made some decision.
//
// With the traditional log package, we might write:
//  log.Printf(
//      "decided to set field foo to value %q for object %s/%s",
//       targetValue, object.Namespace, object.Name)
//
// With logr's structured logging, we'd write:
//  // elsewhere in the file, set up the logger to log with the prefix of "reconcilers",
//  // and the named value target-type=Foo, for extra context.
//  log := mainLogger.WithName("reconcilers").WithValues("target-type", "Foo")
//
//  // later on...
//  log.Info("setting field foo on object", "value", targetValue, "object", object)
//
// Depending on our logging implementation, we could then make logging decisions
// based on field values (like only logging such events for objects in a certain
// namespace), or copy the structured information into a structured log store.
//
// For logging errors, Logger has a method called Error.  Suppose we wanted to
// log an error while reconciling.  With the traditional log package, we might
// write:
//   log.Errorf("unable to reconcile object %s/%s: %v", object.Namespace, object.Name, err)
//
// With logr, we'd instead write:
//   // assuming the above setup for log
//   log.Error(err, "unable to reconcile object", "object", object)
//
// This functions similarly to:
//   log.Info("unable to reconcile object", "error", err, "object", object)
//
// However, it ensures that a standard key for the error value ("error") is used
// across all error logging.  Furthermore, certain implementations may choose to
// attach additional information (such as stack traces) on calls to Error, so
// it's preferred to use Error to log errors.
//
// Parts of a log line
//
// Each log message from a Logger has four types of context:
// logger name, log verbosity, log message, and the named values.
//
// The Logger name consists of a series of name "segments" added by successive
// calls to WithName.  These name segments will be joined in some way by the
// underlying implementation.  It is strongly recommended that name segments
// contain simple identifiers (letters, digits, and hyphen), and do not contain
// characters that could muddle the log output or confuse the joining operation
// (e.g.  whitespace, commas, periods, slashes, brackets, quotes, etc).
//
// Log verbosity represents how little a log matters.  Level zero, the default,
// matters most.  Increasing levels matter less and less.  Try to avoid lots of
// different verbosity levels, and instead provide useful keys, logger names,
// and log messages for users to filter on.  It's illegal to pass a log level
// below zero.
//
// The log message consists of a constant message attached to the log line.
// This should generally be a simple description of what's occurring, and should
// never be a format string.
//
// Variable information can then be attached using named values (key/value
// pairs).  Keys are arbitrary strings, while values may be any Go value.
//
// Key Naming Conventions
//
// Keys are not strictly required to conform to any specification or regex, but
// it is recommended that they:
//   * be human-readable and meaningful (not auto-generated or simple ordinals)
//   * be constant (not dependent on input data)
//   * contain only printable characters
//   * not contain whitespace or punctuation
//
// These guidelines help ensure that log data is processed properly regardless
// of the log implementation.  For example, log implementations will try to
// output JSON data or will store data for later database (e.g. SQL) queries.
//
// While users are generally free to use key names of their choice, it's
// generally best to avoid using the following keys, as they're frequently used
// by implementations:
//
// - `"caller"`: the calling information (file/line) of a particular log line.
// - `"error"`: the underlying error value in the `Error` method.
// - `"level"`: the log level.
// - `"logger"`: the name of the associated logger.
// - `"msg"`: the log message.
// - `"stacktrace"`: the stack trace associated with a particular log line or
//                   error (often from the `Error` message).
// - `"ts"`: the timestamp for a log line.
//
// Implementations are encouraged to make use of these keys to represent the
// above concepts, when necessary (for example, in a pure-JSON output form, it
// would be necessary to represent at least message and timestamp as ordinary
// named values).
package logr

// TODO: consider adding back in format strings if they're really needed
// TODO: consider other bits of zap/zapcore functionality like ObjectMarshaller (for arbitrary objects)
// TODO: consider other bits of glog functionality like Flush, InfoDepth, OutputStats

// Logger represents the ability to log messages, both errors and not.
type Logger interface {
	// Enabled tests whether this Logger is enabled.  For example, commandline
	// flags might be used to set the logging verbosity and disable some info
	// logs.
	Enabled() bool

	// Info logs a non-error message with the given key/value pairs as context.
	//
	// The msg argument should be used to add some constant description to
	// the log line.  The key/value pairs can then be used to add additional
	// variable information.  The key/value pairs should alternate string
	// keys and arbitrary values.
	Info(msg string, keysAndValues ...interface{})

	// Error logs an error, with the given message and key/value pairs as context.
	// It functions similarly to calling Info with the "error" named value, but may
	// have unique behavior, and should be preferred for logging errors (see the
	// package documentations for more information).
	//
	// The msg field should be used to add context to any underlying error,
	// while the err field should be used to attach the actual error that
	// triggered this log line, if present.
	Error(err error, msg string, keysAndValues ...interface{})

	// V returns an Logger value for a specific verbosity level, relative to
	// this Logger.  In other words, V values are additive.  V higher verbosity
	// level means a log message is less important.  It's illegal to pass a log
	// level less than zero.
	V(level int) Logger

	// WithValues adds some key-value pairs of context to a logger.
	// See Info for documentation on how key/value pairs work.
	WithValues(keysAndValues ...interface{}) Logger

	// WithName adds a new element to the logger's name.
	// Successive calls with WithName continue to append
	// suffixes to the logger's name.  It's strongly recommended
	// that name segments contain only letters, digits, and hyphens
	// (see the package documentation for more information).
	WithName(name string) Logger
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: MIT

package logs

import (
	"fmt"
	"io"
	"log"
	"os"

	"github.com/issue9/logs/v2/config"
)

// 目前支持的日志类型
const (
	LevelInfo = iota
	LevelTrace
	LevelDebug
	LevelWarn
	LevelError
	LevelCritical
	levelSize
)

var levels = map[string]int{
	"info":     LevelInfo,
	"trace":    LevelTrace,
	"debug":    LevelDebug,
	"warn":     LevelWarn,
	"error":    LevelError,
	"critical": LevelCritical,
}

var defaultLogs = New()

// Logs 日志输出
type Logs struct {
	loggers []*logger
}

// New 声明 Logs 变量
func New() *Logs {
	logs := &Logs{
		loggers: make([]*logger, levelSize),
	}

	for index := range logs.loggers {
		logs.loggers[index] = newLogger("", 0)
	}

	return logs
}

// Init 从 config.Config 中初始化整个 logs 系统
func (logs *Logs) Init(cfg *config.Config) error {
	for name, c := range cfg.Items {
		index, found := levels[name]
		if !found {
			panic("未知的二级元素名称:" + name)
		}

		l, err := toWriter(name, c)
		if err != nil {
			return err
		}

		logs.loggers[index] = l.(*logger)
	}

	return nil
}

// SetOutput 设置某一个类型的输出通道
//
// 若将 w 设置为 nil 表示关闭此类型的输出。
func (logs *Logs) SetOutput(level int, w io.Writer) error {
	if level >= LevelInfo && level < levelSize {
		return logs.loggers[level].setOutput(w)
	}
	panic(fmt.Sprintf("无效的 level 值：%d", level))
}

func (logs *Logs) SetFlags(flag int) {
	for _, l := range logs.loggers {
		l.log.SetFlags(flag)
	}
}

func (logs *Logs) SetPrefix(p string) {
	for _, l := range logs.loggers {
		l.log.SetPrefix(p)
	}
}

// Flush 输出所有的缓存内容
func (logs *Logs) Flush() error {
	for _, l := range logs.loggers {
		if err := l.container.Flush(); err != nil {
			return err
		}
	}
	return nil
}

// Close 关闭所有的输出通道
//
// 若是通过 os.Exit() 退出程序的，在执行之前，
// 一定记得调用 Close() 输出可能缓存的日志内容。
func (logs *Logs) Close() error {
	for _, l := range logs.loggers {
		if err := l.container.Close(); err != nil {
			return err
		}
	}
	return nil
}

// INFO 获取 INFO 级别的 log.Logger 实例
func (logs *Logs) INFO() *log.Logger { return logs.loggers[LevelInfo].log }

// Info 相当于 INFO().Println(v...) 的简写方式
//
// Info 函数默认是带换行符的，若需要不带换行符的，请使用 DEBUG().Print() 函数代替。
// 其它相似函数也有类型功能。
func (logs *Logs) Info(v ...interface{}) {
	logs.INFO().Output(2, fmt.Sprintln(v...))
}

// Infof 相当于 INFO().Printf(format, v...) 的简写方式
func (logs *Logs) Infof(format string, v ...interface{}) {
	logs.INFO().Output(2, fmt.Sprintf(format, v...))
}

// DEBUG 获取 DEBUG 级别的 log.Logger 实例
func (logs *Logs) DEBUG() *log.Logger {
	return logs.loggers[LevelDebug].log
}

// Debug 相当于 DEBUG().Println(v...) 的简写方式
func (logs *Logs) Debug(v ...interface{}) {
	logs.DEBUG().Output(2, fmt.Sprintln(v...))
}

// Debugf 相当于 DEBUG().Printf(format, v...) 的简写方式
func (logs *Logs) Debugf(format string, v ...interface{}) {
	logs.DEBUG().Output(2, fmt.Sprintf(format, v...))
}

// TRACE 获取 TRACE 级别的 log.Logger 实例
func (logs *Logs) TRACE() *log.Logger {
	return logs.loggers[LevelTrace].log
}

// Trace 相当于 TRACE().Println(v...) 的简写方式
func (logs *Logs) Trace(v ...interface{}) {
	logs.TRACE().Output(2, fmt.Sprintln(v...))
}

// Tracef 相当于 TRACE().Printf(format, v...) 的简写方式
func (logs *Logs) Tracef(format string, v ...interface{}) {
	logs.TRACE().Output(2, fmt.Sprintf(format, v...))
}

// WARN 获取 WARN 级别的 log.Logger 实例
func (logs *Logs) WARN() *log.Logger {
	return logs.loggers[LevelWarn].log
}

// Warn 相当于 WARN().Println(v...) 的简写方式
func (logs *Logs) Warn(v ...interface{}) {
	logs.WARN().Output(2, fmt.Sprintln(v...))
}

// Warnf 相当于 WARN().Printf(format, v...) 的简写方式
func (logs *Logs) Warnf(format string, v ...interface{}) {
	logs.WARN().Output(2, fmt.Sprintf(format, v...))
}

// ERROR 获取 ERROR 级别的 log.Logger 实例
//
// 在未指定 error 级别的日志时，该实例返回一个 nil。
func (logs *Logs) ERROR() *log.Logger {
	return logs.loggers[LevelError].log
}

// Error 相当于 ERROR().Println(v...) 的简写方式
func (logs *Logs) Error(v ...interface{}) {
	logs.ERROR().Output(2, fmt.Sprintln(v...))
}

// Errorf 相当于 ERROR().Printf(format, v...) 的简写方式
func (logs *Logs) Errorf(format string, v ...interface{}) {
	logs.ERROR().Output(2, fmt.Sprintf(format, v...))
}

// CRITICAL 获取 CRITICAL 级别的 log.Logger 实例
func (logs *Logs) CRITICAL() *log.Logger {
	return logs.loggers[LevelCritical].log
}

// Critical 相当于 CRITICAL().Println(v...)的简写方式
func (logs *Logs) Critical(v ...interface{}) {
	logs.CRITICAL().Output(2, fmt.Sprintln(v...))
}

// Criticalf 相当于 CRITICAL().Printf(format, v...) 的简写方式
func (logs *Logs) Criticalf(format string, v ...interface{}) {
	logs.CRITICAL().Output(2, fmt.Sprintf(format, v...))
}

// All 向所有的日志输出内容
func (logs *Logs) All(v ...interface{}) {
	logs.all(fmt.Sprint(v...))
}

// Allf 向所有的日志输出内容
func (logs *Logs) Allf(format string, v ...interface{}) {
	logs.all(fmt.Sprintf(format, v...))
}

// Fatal 输出错误信息然后退出程序
func (logs *Logs) Fatal(code int, v ...interface{}) {
	logs.all(fmt.Sprintln(v...))
	logs.Close()
	os.Exit(code)
}

// Fatalf 输出错误信息然后退出程序
func (logs *Logs) Fatalf(code int, format string, v ...interface{}) {
	logs.all(fmt.Sprintf(format, v...))
	logs.Close()
	os.Exit(code)
}

// Panic 输出错误信息然后触发 panic
func (logs *Logs) Panic(v ...interface{}) {
	s := fmt.Sprint(v...)
	logs.all(s)
	logs.Close()
	panic(s)
}

// Panicf 输出错误信息然后触发 panic
func (logs *Logs) Panicf(format string, v ...interface{}) {
	msg := fmt.Sprintf(format, v...)
	logs.all(msg)
	logs.Close()
	panic(msg)
}

func (logs *Logs) all(msg string) {
	for _, l := range logs.loggers {
		l.log.Output(3, msg)
	}
}

// Default 返回当前模块中全局函数使用的 *Logs 对象
func Default() *Logs {
	return defaultLogs
}

// Init 从 config.Config 中初始化整个 logs 系统
func Init(cfg *config.Config) error {
	return defaultLogs.Init(cfg)
}

// Flush 输出所有的缓存内容
func Flush() error { return defaultLogs.Flush() }

// Close 关闭所有的输出通道
//
// 若是通过 os.Exit() 退出程序的，在执行之前，
// 一定记得调用 Close() 输出可能缓存的日志内容。
func Close() error { return defaultLogs.Close() }

// INFO 获取 INFO 级别的 log.Logger 实例
func INFO() *log.Logger { return defaultLogs.INFO() }

// Info 相当于 INFO().Println(v...) 的简写方式
//
// Info 函数默认是带换行符的，若需要不带换行符的，请使用 DEBUG().Print() 函数代替。
// 其它相似函数也有类型功能。
func Info(v ...interface{}) {
	defaultLogs.INFO().Output(2, fmt.Sprintln(v...))
}

// Infof 相当于 INFO().Printf(format, v...) 的简写方式
func Infof(format string, v ...interface{}) {
	defaultLogs.INFO().Output(2, fmt.Sprintf(format, v...))
}

// DEBUG 获取 DEBUG 级别的 log.Logger 实例
func DEBUG() *log.Logger {
	return defaultLogs.loggers[LevelDebug].log
}

// Debug 相当于 DEBUG().Println(v...) 的简写方式
func Debug(v ...interface{}) {
	defaultLogs.DEBUG().Output(2, fmt.Sprintln(v...))
}

// Debugf 相当于 DEBUG().Printf(format, v...) 的简写方式
func Debugf(format string, v ...interface{}) {
	defaultLogs.DEBUG().Output(2, fmt.Sprintf(format, v...))
}

// TRACE 获取 TRACE 级别的 log.Logger 实例
func TRACE() *log.Logger {
	return defaultLogs.loggers[LevelTrace].log
}

// Trace 相当于 TRACE().Println(v...) 的简写方式
func Trace(v ...interface{}) {
	defaultLogs.TRACE().Output(2, fmt.Sprintln(v...))
}

// Tracef 相当于 TRACE().Printf(format, v...) 的简写方式
func Tracef(format string, v ...interface{}) {
	defaultLogs.TRACE().Output(2, fmt.Sprintf(format, v...))
}

// WARN 获取 WARN 级别的 log.Logger 实例
func WARN() *log.Logger {
	return defaultLogs.loggers[LevelWarn].log
}

// Warn 相当于 WARN().Println(v...) 的简写方式
func Warn(v ...interface{}) {
	defaultLogs.WARN().Output(2, fmt.Sprintln(v...))
}

// Warnf 相当于 WARN().Printf(format, v...) 的简写方式
func Warnf(format string, v ...interface{}) {
	defaultLogs.WARN().Output(2, fmt.Sprintf(format, v...))
}

// ERROR 获取 ERROR 级别的 log.Logger 实例
func ERROR() *log.Logger {
	return defaultLogs.loggers[LevelError].log
}

// Error 相当于 ERROR().Println(v...) 的简写方式
func Error(v ...interface{}) {
	defaultLogs.ERROR().Output(2, fmt.Sprintln(v...))
}

// Errorf 相当于 ERROR().Printf(format, v...) 的简写方式
func Errorf(format string, v ...interface{}) {
	defaultLogs.ERROR().Output(2, fmt.Sprintf(format, v...))
}

// CRITICAL 获取 CRITICAL 级别的 log.Logger 实例
func CRITICAL() *log.Logger {
	return defaultLogs.loggers[LevelCritical].log
}

// Critical 相当于 CRITICAL().Println(v...)的简写方式
func Critical(v ...interface{}) {
	defaultLogs.CRITICAL().Output(2, fmt.Sprintln(v...))
}

// Criticalf 相当于 CRITICAL().Printf(format, v...) 的简写方式
func Criticalf(format string, v ...interface{}) {
	defaultLogs.CRITICAL().Output(2, fmt.Sprintf(format, v...))
}

// All 向所有的日志输出内容
func All(v ...interface{}) { defaultLogs.all(fmt.Sprintln(v...)) }

// Allf 向所有的日志输出内容
func Allf(format string, v ...interface{}) { defaultLogs.all(fmt.Sprintf(format, v...)) }

// Fatal 输出错误信息然后退出程序
func Fatal(code int, v ...interface{}) {
	defaultLogs.all(fmt.Sprint(v...))
	defaultLogs.Close()
	os.Exit(code)
}

// Fatalf 输出错误信息然后退出程序
func Fatalf(code int, format string, v ...interface{}) {
	defaultLogs.all(fmt.Sprintf(format, v...))
	defaultLogs.Close()
	os.Exit(code)
}

// Panic 输出错误信息然后触发 panic
func Panic(v ...interface{}) {
	s := fmt.Sprint(v...)
	defaultLogs.all(s)
	defaultLogs.Close()
	panic(s)
}

// Panicf 输出错误信息然后触发 panic
func Panicf(format string, v ...interface{}) {
	s := fmt.Sprintf(format, v...)
	defaultLogs.all(s)
	defaultLogs.Close()
	panic(s)
}
<commit_msg>refactor(writers): 所有输出函数现在返回错误信息<commit_after>// SPDX-License-Identifier: MIT

package logs

import (
	"fmt"
	"io"
	"log"
	"os"

	"github.com/issue9/logs/v2/config"
)

// 目前支持的日志类型
const (
	LevelInfo = iota
	LevelTrace
	LevelDebug
	LevelWarn
	LevelError
	LevelCritical
	levelSize
)

var levels = map[string]int{
	"info":     LevelInfo,
	"trace":    LevelTrace,
	"debug":    LevelDebug,
	"warn":     LevelWarn,
	"error":    LevelError,
	"critical": LevelCritical,
}

var defaultLogs = New()

// Logs 日志输出
type Logs struct {
	loggers []*logger
}

// New 声明 Logs 变量
func New() *Logs {
	logs := &Logs{
		loggers: make([]*logger, levelSize),
	}

	for index := range logs.loggers {
		logs.loggers[index] = newLogger("", 0)
	}

	return logs
}

// Init 从 config.Config 中初始化整个 logs 系统
func (l *Logs) Init(cfg *config.Config) error {
	for name, c := range cfg.Items {
		index, found := levels[name]
		if !found {
			panic("未知的二级元素名称:" + name)
		}

		ll, err := toWriter(name, c)
		if err != nil {
			return err
		}
		l.loggers[index] = ll.(*logger)
	}

	return nil
}

// SetOutput 设置某一个类型的输出通道
//
// 若将 w 设置为 nil 表示关闭此类型的输出。
func (l *Logs) SetOutput(level int, w io.Writer) error {
	if level >= LevelInfo && level < levelSize {
		return l.loggers[level].setOutput(w)
	}
	panic(fmt.Sprintf("无效的 level 值：%d", level))
}

func (l *Logs) SetFlags(flag int) {
	for _, l := range l.loggers {
		l.log.SetFlags(flag)
	}
}

func (l *Logs) SetPrefix(p string) {
	for _, l := range l.loggers {
		l.log.SetPrefix(p)
	}
}

// Flush 输出所有的缓存内容
func (l *Logs) Flush() error {
	for _, l := range l.loggers {
		if err := l.container.Flush(); err != nil {
			return err
		}
	}
	return nil
}

// Close 关闭所有的输出通道
//
// 若是通过 os.Exit() 退出程序的，在执行之前，
// 一定记得调用 Close() 输出可能缓存的日志内容。
func (l *Logs) Close() error {
	for _, l := range l.loggers {
		if err := l.container.Close(); err != nil {
			return err
		}
	}
	return nil
}

// INFO 获取 INFO 级别的 log.Logger 实例
func (l *Logs) INFO() *log.Logger { return l.loggers[LevelInfo].log }

// Info 相当于 INFO().Println(v...) 的简写方式
//
// Info 函数默认是带换行符的，若需要不带换行符的，请使用 DEBUG().Print() 函数代替。
// 其它相似函数也有类型功能。
func (l *Logs) Info(v ...interface{}) error {
	return l.INFO().Output(2, fmt.Sprintln(v...))
}

// Infof 相当于 INFO().Printf(format, v...) 的简写方式
func (l *Logs) Infof(format string, v ...interface{}) error {
	return l.INFO().Output(2, fmt.Sprintf(format, v...))
}

// DEBUG 获取 DEBUG 级别的 log.Logger 实例
func (l *Logs) DEBUG() *log.Logger {
	return l.loggers[LevelDebug].log
}

// Debug 相当于 DEBUG().Println(v...) 的简写方式
func (l *Logs) Debug(v ...interface{}) error {
	return l.DEBUG().Output(2, fmt.Sprintln(v...))
}

// Debugf 相当于 DEBUG().Printf(format, v...) 的简写方式
func (l *Logs) Debugf(format string, v ...interface{}) error {
	return l.DEBUG().Output(2, fmt.Sprintf(format, v...))
}

// TRACE 获取 TRACE 级别的 log.Logger 实例
func (l *Logs) TRACE() *log.Logger { return l.loggers[LevelTrace].log }

// Trace 相当于 TRACE().Println(v...) 的简写方式
func (l *Logs) Trace(v ...interface{}) error {
	return l.TRACE().Output(2, fmt.Sprintln(v...))
}

// Tracef 相当于 TRACE().Printf(format, v...) 的简写方式
func (l *Logs) Tracef(format string, v ...interface{}) error {
	return l.TRACE().Output(2, fmt.Sprintf(format, v...))
}

// WARN 获取 WARN 级别的 log.Logger 实例
func (l *Logs) WARN() *log.Logger { return l.loggers[LevelWarn].log }

// Warn 相当于 WARN().Println(v...) 的简写方式
func (l *Logs) Warn(v ...interface{}) error {
	return l.WARN().Output(2, fmt.Sprintln(v...))
}

// Warnf 相当于 WARN().Printf(format, v...) 的简写方式
func (l *Logs) Warnf(format string, v ...interface{}) error {
	return l.WARN().Output(2, fmt.Sprintf(format, v...))
}

// ERROR 获取 ERROR 级别的 log.Logger 实例
//
// 在未指定 error 级别的日志时，该实例返回一个 nil。
func (l *Logs) ERROR() *log.Logger { return l.loggers[LevelError].log }

// Error 相当于 ERROR().Println(v...) 的简写方式
func (l *Logs) Error(v ...interface{}) error {
	return l.ERROR().Output(2, fmt.Sprintln(v...))
}

// Errorf 相当于 ERROR().Printf(format, v...) 的简写方式
func (l *Logs) Errorf(format string, v ...interface{}) error {
	return l.ERROR().Output(2, fmt.Sprintf(format, v...))
}

// CRITICAL 获取 CRITICAL 级别的 log.Logger 实例
func (l *Logs) CRITICAL() *log.Logger { return l.loggers[LevelCritical].log }

// Critical 相当于 CRITICAL().Println(v...)的简写方式
func (l *Logs) Critical(v ...interface{}) error {
	return l.CRITICAL().Output(2, fmt.Sprintln(v...))
}

// Criticalf 相当于 CRITICAL().Printf(format, v...) 的简写方式
func (l *Logs) Criticalf(format string, v ...interface{}) error {
	return l.CRITICAL().Output(2, fmt.Sprintf(format, v...))
}

// All 向所有的日志输出内容
func (l *Logs) All(v ...interface{}) { l.all(fmt.Sprint(v...)) }

// Allf 向所有的日志输出内容
func (l *Logs) Allf(format string, v ...interface{}) {
	l.all(fmt.Sprintf(format, v...))
}

// Fatal 输出错误信息然后退出程序
func (l *Logs) Fatal(code int, v ...interface{}) {
	l.all(fmt.Sprintln(v...))
	l.Close()
	os.Exit(code)
}

// Fatalf 输出错误信息然后退出程序
func (l *Logs) Fatalf(code int, format string, v ...interface{}) {
	l.all(fmt.Sprintf(format, v...))
	l.Close()
	os.Exit(code)
}

// Panic 输出错误信息然后触发 panic
func (l *Logs) Panic(v ...interface{}) {
	s := fmt.Sprint(v...)
	l.all(s)
	l.Close()
	panic(s)
}

// Panicf 输出错误信息然后触发 panic
func (l *Logs) Panicf(format string, v ...interface{}) {
	msg := fmt.Sprintf(format, v...)
	l.all(msg)
	l.Close()
	panic(msg)
}

func (l *Logs) all(msg string) {
	for _, l := range l.loggers {
		l.log.Output(3, msg)
	}
}

// Default 返回当前模块中全局函数使用的 *Logs 对象
func Default() *Logs {
	return defaultLogs
}

// Init 从 config.Config 中初始化整个 logs 系统
func Init(cfg *config.Config) error {
	return Default().Init(cfg)
}

// Flush 输出所有的缓存内容
func Flush() error { return Default().Flush() }

// Close 关闭所有的输出通道
//
// 若是通过 os.Exit() 退出程序的，在执行之前，
// 一定记得调用 Close() 输出可能缓存的日志内容。
func Close() error { return Default().Close() }

// INFO 获取 INFO 级别的 log.Logger 实例
func INFO() *log.Logger { return Default().INFO() }

// Info 相当于 INFO().Println(v...) 的简写方式
//
// Info 函数默认是带换行符的，若需要不带换行符的，请使用 DEBUG().Print() 函数代替。
// 其它相似函数也有类型功能。
func Info(v ...interface{}) error { return INFO().Output(2, fmt.Sprintln(v...)) }

// Infof 相当于 INFO().Printf(format, v...) 的简写方式
func Infof(format string, v ...interface{}) error {
	return INFO().Output(2, fmt.Sprintf(format, v...))
}

// DEBUG 获取 DEBUG 级别的 log.Logger 实例
func DEBUG() *log.Logger { return Default().DEBUG() }

// Debug 相当于 DEBUG().Println(v...) 的简写方式
func Debug(v ...interface{}) error {
	return DEBUG().Output(2, fmt.Sprintln(v...))
}

// Debugf 相当于 DEBUG().Printf(format, v...) 的简写方式
func Debugf(format string, v ...interface{}) error {
	return DEBUG().Output(2, fmt.Sprintf(format, v...))
}

// TRACE 获取 TRACE 级别的 log.Logger 实例
func TRACE() *log.Logger { return Default().TRACE() }

// Trace 相当于 TRACE().Println(v...) 的简写方式
func Trace(v ...interface{}) error {
	return TRACE().Output(2, fmt.Sprintln(v...))
}

// Tracef 相当于 TRACE().Printf(format, v...) 的简写方式
func Tracef(format string, v ...interface{}) error {
	return TRACE().Output(2, fmt.Sprintf(format, v...))
}

// WARN 获取 WARN 级别的 log.Logger 实例
func WARN() *log.Logger { return Default().WARN() }

// Warn 相当于 WARN().Println(v...) 的简写方式
func Warn(v ...interface{}) error {
	return WARN().Output(2, fmt.Sprintln(v...))
}

// Warnf 相当于 WARN().Printf(format, v...) 的简写方式
func Warnf(format string, v ...interface{}) error {
	return WARN().Output(2, fmt.Sprintf(format, v...))
}

// ERROR 获取 ERROR 级别的 log.Logger 实例
func ERROR() *log.Logger { return Default().ERROR() }

// Error 相当于 ERROR().Println(v...) 的简写方式
func Error(v ...interface{}) error {
	return ERROR().Output(2, fmt.Sprintln(v...))
}

// Errorf 相当于 ERROR().Printf(format, v...) 的简写方式
func Errorf(format string, v ...interface{}) error {
	return ERROR().Output(2, fmt.Sprintf(format, v...))
}

// CRITICAL 获取 CRITICAL 级别的 log.Logger 实例
func CRITICAL() *log.Logger { return Default().CRITICAL() }

// Critical 相当于 CRITICAL().Println(v...)的简写方式
func Critical(v ...interface{}) error {
	return CRITICAL().Output(2, fmt.Sprintln(v...))
}

// Criticalf 相当于 CRITICAL().Printf(format, v...) 的简写方式
func Criticalf(format string, v ...interface{}) error {
	return CRITICAL().Output(2, fmt.Sprintf(format, v...))
}

// All 向所有的日志输出内容
func All(v ...interface{}) { Default().all(fmt.Sprintln(v...)) }

// Allf 向所有的日志输出内容
func Allf(format string, v ...interface{}) { Default().all(fmt.Sprintf(format, v...)) }

// Fatal 输出错误信息然后退出程序
func Fatal(code int, v ...interface{}) {
	Default().all(fmt.Sprint(v...))
	Default().Close()
	os.Exit(code)
}

// Fatalf 输出错误信息然后退出程序
func Fatalf(code int, format string, v ...interface{}) {
	Default().all(fmt.Sprintf(format, v...))
	Default().Close()
	os.Exit(code)
}

// Panic 输出错误信息然后触发 panic
func Panic(v ...interface{}) {
	s := fmt.Sprint(v...)
	Default().all(s)
	Default().Close()
	panic(s)
}

// Panicf 输出错误信息然后触发 panic
func Panicf(format string, v ...interface{}) {
	s := fmt.Sprintf(format, v...)
	Default().all(s)
	Default().Close()
	panic(s)
}
<|endoftext|>