<commit_before>/*
Copyright 2017 xgfone

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 config

import (
	"fmt"
	"reflect"
)

// Opt stands for an opt value.
type Opt interface {
	// Name returns the name of the option.
	// It's necessary and must not be empty.
	Name() string

	// Short returns the short name of the option.
	// It's optional. If having no short name, it should return "".
	Short() string

	// Help returns the help or usage information.
	// If having no help doc, it should return "".
	Help() string

	// Default returns the default value.
	// If having no default value, it should return nil.
	Default() interface{}

	// IsBool returns true if the option is bool type. Or return false.
	IsBool() bool

	// Parse parses the argument to the type of this option.
	// If failed to parse, it should return an error to explain the reason.
	Parse(interface{}) (interface{}, error)
}

type optType int

func (ot optType) String() string {
	return optTypeMap[ot]
}

const (
	noneType optType = iota
	boolType
	stringType
	intType
	int8Type
	int16Type
	int32Type
	int64Type
	uintType
	uint8Type
	uint16Type
	uint32Type
	uint64Type
	float32Type
	float64Type

	stringsType
	intsType
	int64sType
	uintsType
	uint64sType
	float64sType
)

var optTypeMap = map[optType]string{
	noneType:    "none",
	boolType:    "bool",
	stringType:  "string",
	intType:     "int",
	int8Type:    "int8",
	int16Type:   "int16",
	int32Type:   "int32",
	int64Type:   "int64",
	uintType:    "uint",
	uint8Type:   "uint8",
	uint16Type:  "uint16",
	uint32Type:  "uint32",
	uint64Type:  "uint64",
	float32Type: "float32",
	float64Type: "float64",

	stringsType:  "[]string",
	intsType:     "[]int",
	int64sType:   "[]int64",
	uintsType:    "[]uint",
	uint64sType:  "[]uint64",
	float64sType: "[]float64",
}

var kind2optType = map[reflect.Kind]optType{
	reflect.Bool:    boolType,
	reflect.String:  stringType,
	reflect.Int:     intType,
	reflect.Int8:    int8Type,
	reflect.Int16:   int16Type,
	reflect.Int32:   int32Type,
	reflect.Int64:   int64Type,
	reflect.Uint:    uintType,
	reflect.Uint8:   uint8Type,
	reflect.Uint16:  uint16Type,
	reflect.Uint32:  uint32Type,
	reflect.Uint64:  uint64Type,
	reflect.Float32: float32Type,
	reflect.Float64: float64Type,
}

func getOptType(v reflect.Value) optType {
	if t, ok := kind2optType[v.Kind()]; ok {
		return t
	}

	switch v.Interface().(type) {
	case []string:
		return stringsType
	case []int:
		return intsType
	case []int64:
		return int64sType
	case []uint:
		return uintsType
	case []uint64:
		return uint64sType
	case []float64:
		return float64sType
	default:
		panic(fmt.Errorf("doesn't support the type %s", v.Type().Name()))
	}
}

type baseOpt struct {
	name     string
	help     string
	short    string
	_default interface{}

	_type      optType
	validators []Validator
}

var _ ValidatorChainOpt = baseOpt{}

func newBaseOpt(short, name string, _default interface{}, help string,
	optType optType) baseOpt {
	o := baseOpt{
		short:    short,
		name:     name,
		help:     help,
		_default: _default,
		_type:    optType,
	}
	o.Default()
	return o
}

// SetValidators resets the validator chain.
func (o baseOpt) SetValidators(vs []Validator) ValidatorChainOpt {
	o.validators = vs
	return o
}

// AddValidators adds some new validators into the validator chain.
func (o baseOpt) AddValidators(vs ...Validator) ValidatorChainOpt {
	for _, v := range vs {
		o.validators = append(o.validators, v)
	}
	return o
}

// GetValidators returns the validator chain<commit_msg>Optimize AddValidators<commit_after>/*
Copyright 2017 xgfone

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 config

import (
	"fmt"
	"reflect"
)

// Opt stands for an opt value.
type Opt interface {
	// Name returns the name of the option.
	// It's necessary and must not be empty.
	Name() string

	// Short returns the short name of the option.
	// It's optional. If having no short name, it should return "".
	Short() string

	// Help returns the help or usage information.
	// If having no help doc, it should return "".
	Help() string

	// Default returns the default value.
	// If having no default value, it should return nil.
	Default() interface{}

	// IsBool returns true if the option is bool type. Or return false.
	IsBool() bool

	// Parse parses the argument to the type of this option.
	// If failed to parse, it should return an error to explain the reason.
	Parse(interface{}) (interface{}, error)
}

type optType int

func (ot optType) String() string {
	return optTypeMap[ot]
}

const (
	noneType optType = iota
	boolType
	stringType
	intType
	int8Type
	int16Type
	int32Type
	int64Type
	uintType
	uint8Type
	uint16Type
	uint32Type
	uint64Type
	float32Type
	float64Type

	stringsType
	intsType
	int64sType
	uintsType
	uint64sType
	float64sType
)

var optTypeMap = map[optType]string{
	noneType:    "none",
	boolType:    "bool",
	stringType:  "string",
	intType:     "int",
	int8Type:    "int8",
	int16Type:   "int16",
	int32Type:   "int32",
	int64Type:   "int64",
	uintType:    "uint",
	uint8Type:   "uint8",
	uint16Type:  "uint16",
	uint32Type:  "uint32",
	uint64Type:  "uint64",
	float32Type: "float32",
	float64Type: "float64",

	stringsType:  "[]string",
	intsType:     "[]int",
	int64sType:   "[]int64",
	uintsType:    "[]uint",
	uint64sType:  "[]uint64",
	float64sType: "[]float64",
}

var kind2optType = map[reflect.Kind]optType{
	reflect.Bool:    boolType,
	reflect.String:  stringType,
	reflect.Int:     intType,
	reflect.Int8:    int8Type,
	reflect.Int16:   int16Type,
	reflect.Int32:   int32Type,
	reflect.Int64:   int64Type,
	reflect.Uint:    uintType,
	reflect.Uint8:   uint8Type,
	reflect.Uint16:  uint16Type,
	reflect.Uint32:  uint32Type,
	reflect.Uint64:  uint64Type,
	reflect.Float32: float32Type,
	reflect.Float64: float64Type,
}

func getOptType(v reflect.Value) optType {
	if t, ok := kind2optType[v.Kind()]; ok {
		return t
	}

	switch v.Interface().(type) {
	case []string:
		return stringsType
	case []int:
		return intsType
	case []int64:
		return int64sType
	case []uint:
		return uintsType
	case []uint64:
		return uint64sType
	case []float64:
		return float64sType
	default:
		panic(fmt.Errorf("doesn't support the type %s", v.Type().Name()))
	}
}

type baseOpt struct {
	name     string
	help     string
	short    string
	_default interface{}

	_type      optType
	validators []Validator
}

var _ ValidatorChainOpt = baseOpt{}

func newBaseOpt(short, name string, _default interface{}, help string,
	optType optType) baseOpt {
	o := baseOpt{
		short:    short,
		name:     name,
		help:     help,
		_default: _default,
		_type:    optType,
	}
	o.Default()
	return o
}

// SetValidators resets the validator chain.
func (o baseOpt) SetValidators(vs []Validator) ValidatorChainOpt {
	o.validators = vs
	return o
}

// AddValidators adds some new validators into the validator chain.
func (o baseOpt) AddValidators(vs ...Validator) ValidatorChainOpt {
	if len(o.validators) == 0 {
		o.validators = vs
	} else {
		for _, v := range vs {
			o.validators = append(o.validators, v)
		}
	}

	return o
}

// GetValidators returns the validator chain
{'fraction_non_alphanumeric': 0.1006811145510836, 'fraction_numerical': 0.032321981424148605, 'mean_word_length': 3.6980802792321117, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4270712', 'n_tokens_mistral': 2939, 'n_tokens_neox': 2741, 'n_words': 900}