mirror of
https://github.com/yusing/godoxy.git
synced 2026-03-29 13:21:55 +02:00
added golangci-linting, refactor, simplified error msgs and fixed some error handling
This commit is contained in:
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
// Recursively lists all files in a directory until `maxDepth` is reached
|
||||
// Returns a slice of file paths relative to `dir`
|
||||
// Returns a slice of file paths relative to `dir`.
|
||||
func ListFiles(dir string, maxDepth int) ([]string, error) {
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
|
||||
@@ -4,9 +4,8 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/puzpuzpuz/xsync/v3"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Map[KT comparable, VT any] struct {
|
||||
@@ -25,6 +24,17 @@ func NewMapFrom[KT comparable, VT any](m map[KT]VT) (res Map[KT, VT]) {
|
||||
return
|
||||
}
|
||||
|
||||
// MapFind iterates over the map and returns the first value
|
||||
// that satisfies the given criteria. The iteration is stopped
|
||||
// once a value is found. If no value satisfies the criteria,
|
||||
// the function returns the zero value of CT.
|
||||
//
|
||||
// The criteria function takes a value of type VT and returns a
|
||||
// value of type CT and a boolean indicating whether the value
|
||||
// satisfies the criteria. The boolean value is used to determine
|
||||
// whether the iteration should be stopped.
|
||||
//
|
||||
// The function is safe for concurrent use.
|
||||
func MapFind[KT comparable, VT, CT any](m Map[KT, VT], criteria func(VT) (CT, bool)) (_ CT) {
|
||||
result := make(chan CT, 1)
|
||||
|
||||
@@ -49,13 +59,15 @@ func MapFind[KT comparable, VT, CT any](m Map[KT, VT], criteria func(VT) (CT, bo
|
||||
}
|
||||
}
|
||||
|
||||
// MergeFrom add contents from another `Map`, ignore duplicated keys
|
||||
// MergeFrom merges the contents of another Map into this one, ignoring duplicated keys.
|
||||
//
|
||||
// Parameters:
|
||||
// - other: `Map` of values to add from
|
||||
//
|
||||
// Return:
|
||||
// - Map: a `Map` of duplicated keys-value pairs
|
||||
// other: Map of values to add from
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// Map of duplicated keys-value pairs
|
||||
func (m Map[KT, VT]) MergeFrom(other Map[KT, VT]) Map[KT, VT] {
|
||||
dups := NewMapOf[KT, VT]()
|
||||
|
||||
@@ -70,6 +82,15 @@ func (m Map[KT, VT]) MergeFrom(other Map[KT, VT]) Map[KT, VT] {
|
||||
return dups
|
||||
}
|
||||
|
||||
// RangeAll calls the given function for each key-value pair in the map.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// do: function to call for each key-value pair
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// nothing
|
||||
func (m Map[KT, VT]) RangeAll(do func(k KT, v VT)) {
|
||||
m.Range(func(k KT, v VT) bool {
|
||||
do(k, v)
|
||||
@@ -77,6 +98,16 @@ func (m Map[KT, VT]) RangeAll(do func(k KT, v VT)) {
|
||||
})
|
||||
}
|
||||
|
||||
// RangeAllParallel calls the given function for each key-value pair in the map,
|
||||
// in parallel. The map is not safe for modification from within the function.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// do: function to call for each key-value pair
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// nothing
|
||||
func (m Map[KT, VT]) RangeAllParallel(do func(k KT, v VT)) {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(m.Size())
|
||||
@@ -91,6 +122,15 @@ func (m Map[KT, VT]) RangeAllParallel(do func(k KT, v VT)) {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// RemoveAll removes all key-value pairs from the map where the value matches the given criteria.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// criteria: function to determine whether a value should be removed
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// nothing
|
||||
func (m Map[KT, VT]) RemoveAll(criteria func(VT) bool) {
|
||||
m.Range(func(k KT, v VT) bool {
|
||||
if criteria(v) {
|
||||
@@ -105,6 +145,17 @@ func (m Map[KT, VT]) Has(k KT) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// UnmarshalFromYAML unmarshals a yaml byte slice into the map.
|
||||
//
|
||||
// It overwrites all existing key-value pairs in the map.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// data: yaml byte slice to unmarshal
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// error: if the unmarshaling fails
|
||||
func (m Map[KT, VT]) UnmarshalFromYAML(data []byte) E.NestedError {
|
||||
if m.Size() != 0 {
|
||||
return E.FailedWhy("unmarshal from yaml", "map is not empty")
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
)
|
||||
|
||||
// TODO: move to "utils/io"
|
||||
// TODO: move to "utils/io".
|
||||
type (
|
||||
FileReader struct {
|
||||
Path string
|
||||
@@ -108,7 +108,7 @@ func Copy2(ctx context.Context, dst io.Writer, src io.Reader) error {
|
||||
return Copy(&ContextWriter{ctx: ctx, Writer: dst}, &ContextReader{ctx: ctx, Reader: src})
|
||||
}
|
||||
|
||||
func LoadJson[T any](path string, pointer *T) E.NestedError {
|
||||
func LoadJSON[T any](path string, pointer *T) E.NestedError {
|
||||
data, err := E.Check(os.ReadFile(path))
|
||||
if err.HasError() {
|
||||
return err
|
||||
@@ -116,7 +116,7 @@ func LoadJson[T any](path string, pointer *T) E.NestedError {
|
||||
return E.From(json.Unmarshal(data, pointer))
|
||||
}
|
||||
|
||||
func SaveJson[T any](path string, pointer *T, perm os.FileMode) E.NestedError {
|
||||
func SaveJSON[T any](path string, pointer *T, perm os.FileMode) E.NestedError {
|
||||
data, err := E.Check(json.Marshal(pointer))
|
||||
if err.HasError() {
|
||||
return err
|
||||
|
||||
@@ -3,6 +3,7 @@ package utils
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
@@ -14,10 +15,12 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type SerializedObject = map[string]any
|
||||
type Converter interface {
|
||||
ConvertFrom(value any) (any, E.NestedError)
|
||||
}
|
||||
type (
|
||||
SerializedObject = map[string]any
|
||||
Converter interface {
|
||||
ConvertFrom(value any) (any, E.NestedError)
|
||||
}
|
||||
)
|
||||
|
||||
func ValidateYaml(schema *jsonschema.Schema, data []byte) E.NestedError {
|
||||
var i any
|
||||
@@ -37,11 +40,16 @@ func ValidateYaml(schema *jsonschema.Schema, data []byte) E.NestedError {
|
||||
return nil
|
||||
}
|
||||
|
||||
errors := E.NewBuilder("yaml validation error")
|
||||
for _, e := range err.(*jsonschema.ValidationError).Causes {
|
||||
errors.AddE(e)
|
||||
var valErr *jsonschema.ValidationError
|
||||
if !errors.As(err, &valErr) {
|
||||
return E.UnexpectedError(err)
|
||||
}
|
||||
return errors.Build()
|
||||
|
||||
b := E.NewBuilder("yaml validation error")
|
||||
for _, e := range valErr.Causes {
|
||||
b.AddE(e)
|
||||
}
|
||||
return b.Build()
|
||||
}
|
||||
|
||||
// Serialize converts the given data into a map[string]any representation.
|
||||
@@ -80,7 +88,7 @@ func Serialize(data any) (SerializedObject, E.NestedError) {
|
||||
result[key.String()] = value.MapIndex(key).Interface()
|
||||
}
|
||||
case reflect.Struct:
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
for i := range value.NumField() {
|
||||
field := value.Type().Field(i)
|
||||
if !field.IsExported() {
|
||||
continue
|
||||
@@ -91,9 +99,10 @@ func Serialize(data any) (SerializedObject, E.NestedError) {
|
||||
}
|
||||
|
||||
// If the json tag is not empty, use it as the key
|
||||
if jsonTag != "" {
|
||||
switch {
|
||||
case jsonTag != "":
|
||||
result[jsonTag] = value.Field(i).Interface()
|
||||
} else if field.Anonymous {
|
||||
case field.Anonymous:
|
||||
// If the field is an embedded struct, add its fields to the result
|
||||
fieldMap, err := Serialize(value.Field(i).Interface())
|
||||
if err != nil {
|
||||
@@ -102,7 +111,7 @@ func Serialize(data any) (SerializedObject, E.NestedError) {
|
||||
for k, v := range fieldMap {
|
||||
result[k] = v
|
||||
}
|
||||
} else {
|
||||
default:
|
||||
result[field.Name] = value.Field(i).Interface()
|
||||
}
|
||||
}
|
||||
@@ -147,7 +156,8 @@ func Deserialize(src SerializedObject, dst any) E.NestedError {
|
||||
|
||||
// TODO: use E.Builder to collect errors from all fields
|
||||
|
||||
if dstV.Kind() == reflect.Struct {
|
||||
switch dstV.Kind() {
|
||||
case reflect.Struct:
|
||||
mapping := make(map[string]reflect.Value)
|
||||
for _, field := range reflect.VisibleFields(dstT) {
|
||||
mapping[ToLowerNoSnake(field.Name)] = dstV.FieldByName(field.Name)
|
||||
@@ -162,7 +172,7 @@ func Deserialize(src SerializedObject, dst any) E.NestedError {
|
||||
return E.Unexpected("field", k).Subjectf("%T", dst)
|
||||
}
|
||||
}
|
||||
} else if dstV.Kind() == reflect.Map && dstT.Key().Kind() == reflect.String {
|
||||
case reflect.Map:
|
||||
if dstV.IsNil() {
|
||||
dstV.Set(reflect.MakeMap(dstT))
|
||||
}
|
||||
@@ -174,8 +184,7 @@ func Deserialize(src SerializedObject, dst any) E.NestedError {
|
||||
}
|
||||
dstV.SetMapIndex(reflect.ValueOf(ToLowerNoSnake(k)), tmp)
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
default:
|
||||
return E.Unsupported("target type", fmt.Sprintf("%T", dst))
|
||||
}
|
||||
|
||||
@@ -362,7 +371,7 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr E.N
|
||||
return true, Convert(reflect.ValueOf(tmp), dst)
|
||||
}
|
||||
|
||||
func DeserializeJson(j map[string]string, target any) E.NestedError {
|
||||
func DeserializeJSON(j map[string]string, target any) E.NestedError {
|
||||
data, err := E.Check(json.Marshal(j))
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// TODO: support other languages
|
||||
// TODO: support other languages.
|
||||
var titleCaser = cases.Title(language.AmericanEnglish)
|
||||
|
||||
func CommaSeperatedList(s string) []string {
|
||||
@@ -31,3 +31,7 @@ func ExtractPort(fullURL string) (int, error) {
|
||||
}
|
||||
return strconv.Atoi(url.Port())
|
||||
}
|
||||
|
||||
func PortString(port uint16) string {
|
||||
return strconv.FormatUint(uint64(port), 10)
|
||||
}
|
||||
|
||||
@@ -92,7 +92,6 @@ func ExpectType[T any](t *testing.T, got any) (_ T) {
|
||||
_, ok := got.(T)
|
||||
if !ok {
|
||||
t.Fatalf("expected type %s, got %s", tExpect, reflect.TypeOf(got).Elem())
|
||||
t.FailNow()
|
||||
return
|
||||
}
|
||||
return got.(T)
|
||||
|
||||
Reference in New Issue
Block a user