-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
dapeng
committed
Dec 17, 2024
1 parent
671e8ef
commit 70d848d
Showing
226 changed files
with
5,805 additions
and
17,377 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package gone | ||
|
||
import ( | ||
"reflect" | ||
"sort" | ||
) | ||
|
||
// coffin represents a component container in the gone framework | ||
type coffin struct { | ||
name string | ||
goner any | ||
|
||
order int | ||
onlyForName bool | ||
forceReplace bool | ||
|
||
defaultTypeMap map[reflect.Type]bool | ||
lazyFill bool | ||
needInitBeforeUse bool | ||
isFill bool | ||
isInit bool | ||
} | ||
|
||
func newCoffin(goner any) *coffin { | ||
_, needInitBeforeUse := goner.(Initiator) | ||
if !needInitBeforeUse { | ||
_, needInitBeforeUse = goner.(InitiatorNoError) | ||
} | ||
if !needInitBeforeUse { | ||
_, needInitBeforeUse = goner.(NamedProvider) | ||
} | ||
|
||
return &coffin{ | ||
goner: goner, | ||
defaultTypeMap: make(map[reflect.Type]bool), | ||
needInitBeforeUse: needInitBeforeUse, | ||
} | ||
} | ||
|
||
func (c *coffin) isDefault(t reflect.Type) bool { | ||
return c.defaultTypeMap[t] | ||
} | ||
|
||
// coffinList is a slice of coffin pointers that implements sort.Interface | ||
type coffinList []*coffin | ||
|
||
func (c coffinList) Len() int { | ||
return len(c) | ||
} | ||
|
||
func (c coffinList) Less(i, j int) bool { | ||
return c[i].order < c[j].order | ||
} | ||
|
||
func (c coffinList) Swap(i, j int) { | ||
c[i], c[j] = c[j], c[i] | ||
} | ||
|
||
// SortCoffins sorts a slice of coffins by their order | ||
func SortCoffins(coffins []*coffin) { | ||
sort.Sort(coffinList(coffins)) | ||
} | ||
|
||
func isInitiator(co *coffin) bool { | ||
if _, ok := co.goner.(Initiator); ok { | ||
return true | ||
} | ||
if _, ok := co.goner.(InitiatorNoError); ok { | ||
return true | ||
} | ||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
package gone | ||
|
||
import ( | ||
"reflect" | ||
"testing" | ||
) | ||
|
||
func TestCoffinListSort(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
coffins []*coffin | ||
expected []*coffin | ||
}{ | ||
{ | ||
name: "Sort by order - ascending", | ||
coffins: []*coffin{ | ||
{name: "C", order: 3}, | ||
{name: "A", order: 1}, | ||
{name: "B", order: 2}, | ||
}, | ||
expected: []*coffin{ | ||
{name: "A", order: 1}, | ||
{name: "B", order: 2}, | ||
{name: "C", order: 3}, | ||
}, | ||
}, | ||
{ | ||
name: "Sort with same orders", | ||
coffins: []*coffin{ | ||
{name: "A", order: 1}, | ||
{name: "B", order: 1}, | ||
{name: "C", order: 1}, | ||
}, | ||
expected: []*coffin{ | ||
{name: "A", order: 1}, | ||
{name: "B", order: 1}, | ||
{name: "C", order: 1}, | ||
}, | ||
}, | ||
{ | ||
name: "Sort with negative orders", | ||
coffins: []*coffin{ | ||
{name: "C", order: 1}, | ||
{name: "A", order: -2}, | ||
{name: "B", order: -1}, | ||
}, | ||
expected: []*coffin{ | ||
{name: "A", order: -2}, | ||
{name: "B", order: -1}, | ||
{name: "C", order: 1}, | ||
}, | ||
}, | ||
{ | ||
name: "Empty slice", | ||
coffins: []*coffin{}, | ||
expected: []*coffin{}, | ||
}, | ||
{ | ||
name: "Single element", | ||
coffins: []*coffin{ | ||
{name: "A", order: 1}, | ||
}, | ||
expected: []*coffin{ | ||
{name: "A", order: 1}, | ||
}, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
// Test Sort method | ||
SortCoffins(tt.coffins) | ||
|
||
// Check if the result matches expected | ||
if !reflect.DeepEqual(tt.coffins, tt.expected) { | ||
t.Errorf("SortCoffins() got = %v, want %v", formatCoffins(tt.coffins), formatCoffins(tt.expected)) | ||
} | ||
|
||
// Verify that the result is actually sorted | ||
for i := 1; i < len(tt.coffins); i++ { | ||
if tt.coffins[i-1].order > tt.coffins[i].order { | ||
t.Errorf("SortCoffins() result is not sorted at index %d: %v", i, formatCoffins(tt.coffins)) | ||
} | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestCoffinListInterface(t *testing.T) { | ||
list := coffinList{ | ||
{name: "B", order: 2}, | ||
{name: "A", order: 1}, | ||
{name: "C", order: 3}, | ||
} | ||
|
||
// Test Len() | ||
if got := list.Len(); got != 3 { | ||
t.Errorf("coffinList.Len() = %v, want %v", got, 3) | ||
} | ||
|
||
// Test Less() | ||
tests := []struct { | ||
i, j int | ||
expected bool | ||
}{ | ||
{0, 1, false}, // 2 > 1, should be false | ||
{1, 2, true}, // 1 < 3, should be true | ||
{0, 2, true}, // 2 < 3, should be true | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(formatLessTest(list[tt.i], list[tt.j]), func(t *testing.T) { | ||
if got := list.Less(tt.i, tt.j); got != tt.expected { | ||
t.Errorf("coffinList.Less(%v, %v) = %v, want %v", | ||
tt.i, tt.j, got, tt.expected) | ||
} | ||
}) | ||
} | ||
|
||
// Test Swap() | ||
original := make([]*coffin, len(list)) | ||
copy(original, list) | ||
|
||
list.Swap(0, 2) | ||
if list[0].order != original[2].order || list[2].order != original[0].order { | ||
t.Errorf("coffinList.Swap(0, 2) failed, got %v, want swapped %v", | ||
formatCoffins(list), formatCoffins(original)) | ||
} | ||
} | ||
|
||
// Helper function to format coffins for error messages | ||
func formatCoffins(coffins []*coffin) string { | ||
result := "[" | ||
for i, c := range coffins { | ||
if i > 0 { | ||
result += ", " | ||
} | ||
result += "{" + c.name + ":" + string(rune('0'+c.order)) + "}" | ||
} | ||
result += "]" | ||
return result | ||
} | ||
|
||
// Helper function to format Less test description | ||
func formatLessTest(a, b *coffin) string { | ||
return "Less " + a.name + "(" + string(rune('0'+a.order)) + ") " + | ||
b.name + "(" + string(rune('0'+b.order)) + ")" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
package gone | ||
|
||
import ( | ||
"github.com/gone-io/gone/internal/json" | ||
"os" | ||
"reflect" | ||
"strconv" | ||
"time" | ||
) | ||
|
||
const ConfigureName = "configure" | ||
|
||
// Configure defines the interface for configuration providers | ||
// Get retrieves a configuration value by key, storing it in v, with a default value if not found | ||
type Configure interface { | ||
Get(key string, v any, defaultVal string) error | ||
} | ||
|
||
// ConfigProvider implements a provider for injecting configuration values | ||
// It uses an underlying Configure implementation to retrieve values | ||
type ConfigProvider struct { | ||
Flag | ||
configure Configure `gone:"configure"` // The Configure implementation to use | ||
} | ||
|
||
// Name returns the provider name "config" used for registration | ||
func (s *ConfigProvider) Name() string { | ||
return "config" | ||
} | ||
|
||
func (s *ConfigProvider) Init() {} | ||
|
||
// Provide implements the provider interface to inject configuration values | ||
// Parameters: | ||
// - tagConf: The tag configuration string containing key and default value | ||
// - t: The reflect.Type of the value to provide | ||
// | ||
// Returns: | ||
// - The configured value of type t | ||
// - Error if configuration fails | ||
func (s *ConfigProvider) Provide(tagConf string, t reflect.Type) (any, error) { | ||
// Parse the tag string into a map and ordered keys | ||
m, keys := TagStringParse(tagConf) | ||
if len(keys) == 0 { | ||
return nil, NewInnerError("config-key is empty", ConfigError) | ||
} | ||
|
||
// Get the first key and its default value | ||
key := keys[0] | ||
defaultValue := m[key] | ||
if defaultValue == "" { | ||
defaultValue = m["default"] // Fallback to "default" key if no value | ||
} | ||
|
||
// Create new value of requested type and configure it | ||
value := reflect.New(t) | ||
err := s.configure.Get(key, value.Interface(), defaultValue) | ||
if err != nil { | ||
return nil, ToError(err) | ||
} | ||
return value.Elem().Interface(), nil | ||
} | ||
|
||
type EnvConfigure struct { | ||
Flag | ||
} | ||
|
||
// Get retrieves a configuration value from environment variables with fallback to default value. | ||
// Supports type conversion for various Go types including string, int, float, bool, and structs. | ||
// | ||
// Parameters: | ||
// - key: Environment variable name to look up | ||
// - v: Pointer to variable where the value will be stored | ||
// - defaultVal: Default value if environment variable is not set | ||
// | ||
// Returns error if: | ||
// - v is not a pointer | ||
// - Type conversion fails | ||
// - Unsupported type is provided | ||
func (s *EnvConfigure) Get(key string, v any, defaultVal string) error { | ||
// Get environment variable value, fallback to default if not set | ||
key = convertUppercaseCamel("GONE_" + key) | ||
env := os.Getenv(key) | ||
if env == "" { | ||
env = defaultVal | ||
} | ||
|
||
// Verify v is a pointer | ||
rv := reflect.ValueOf(v) | ||
if rv.Kind() != reflect.Ptr { | ||
return NewInnerError("Value must be a pointer", ConfigError) | ||
} | ||
|
||
// Type switch to handle different pointer types | ||
switch ptr := v.(type) { | ||
case *string: | ||
// String type needs no conversion | ||
*ptr = env | ||
case *int: | ||
// Convert string to int | ||
val, err := strconv.Atoi(env) | ||
if err != nil { | ||
return ToError(err) | ||
} | ||
*ptr = val | ||
case *int64: | ||
// Convert string to int64 | ||
val, err := strconv.ParseInt(env, 10, 64) | ||
if err != nil { | ||
return ToError(err) | ||
} | ||
*ptr = val | ||
case *float64: | ||
// Convert string to float64 | ||
val, err := strconv.ParseFloat(env, 64) | ||
if err != nil { | ||
return ToError(err) | ||
} | ||
*ptr = val | ||
case *bool: | ||
// Convert string to bool | ||
val, err := strconv.ParseBool(env) | ||
if err != nil { | ||
return ToError(err) | ||
} | ||
*ptr = val | ||
case *uint: | ||
// Convert string to uint | ||
val, err := strconv.ParseUint(env, 10, 64) | ||
if err != nil { | ||
return ToError(err) | ||
} | ||
*ptr = uint(val) | ||
case *uint64: | ||
// Convert string to uint64 | ||
val, err := strconv.ParseUint(env, 10, 64) | ||
if err != nil { | ||
return ToError(err) | ||
} | ||
*ptr = val | ||
case *time.Duration: | ||
// Convert string to time.Duration | ||
val, err := time.ParseDuration(env) | ||
if err != nil { | ||
return ToError(err) | ||
} | ||
*ptr = val | ||
default: | ||
// Handle struct types by JSON unmarshal | ||
if rv.Elem().Kind() == reflect.Struct { | ||
err := json.Unmarshal([]byte(env), v) | ||
if err != nil { | ||
return ToError(err) | ||
} | ||
return nil | ||
} | ||
return NewInnerError("Unsupported type by EnvConfigure", ConfigError) | ||
} | ||
return nil | ||
} |
Oops, something went wrong.