WIP v0.5.0

This commit is contained in:
2026-02-11 17:27:05 +03:00
parent 7ac293dd38
commit f2d85b848f
32 changed files with 2721 additions and 727 deletions

130
utils/multipart.go Normal file
View File

@@ -0,0 +1,130 @@
package utils
import (
"encoding/json"
"fmt"
"io"
"mime/multipart"
"reflect"
"slices"
"strconv"
"strings"
)
func Encode[T any](w *multipart.Writer, req T) error {
v := reflect.ValueOf(req)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
return fmt.Errorf("req must be a struct")
}
t := v.Type()
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
fieldType := t.Field(i)
formTags := strings.Split(fieldType.Tag.Get("json"), ",")
fieldName := ""
if len(formTags) == 0 {
formTags = strings.Split(fieldType.Tag.Get("json"), ",")
}
if len(formTags) > 0 {
fieldName = formTags[0]
if fieldName == "-" {
continue
}
if slices.Index(formTags, "omitempty") >= 0 {
if field.IsZero() {
continue
}
}
} else {
fieldName = strings.ToLower(fieldType.Name)
}
var (
fw io.Writer
err error
)
switch field.Kind() {
case reflect.String:
if field.String() != "" {
fw, err = w.CreateFormField(fieldName)
if err == nil {
_, err = fw.Write([]byte(field.String()))
}
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
fw, err = w.CreateFormField(fieldName)
if err == nil {
_, err = fw.Write([]byte(strconv.FormatInt(field.Int(), 10)))
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
fw, err = w.CreateFormField(fieldName)
if err == nil {
_, err = fw.Write([]byte(strconv.FormatUint(field.Uint(), 10)))
}
case reflect.Float32, reflect.Float64:
fw, err = w.CreateFormField(fieldName)
if err == nil {
_, err = fw.Write([]byte(strconv.FormatFloat(field.Float(), 'f', -1, 64)))
}
case reflect.Bool:
fw, err = w.CreateFormField(fieldName)
if err == nil {
_, err = fw.Write([]byte(strconv.FormatBool(field.Bool())))
}
case reflect.Slice:
if field.Type().Elem().Kind() == reflect.Uint8 && !field.IsNil() {
filename := fieldType.Tag.Get("filename")
if filename == "" {
filename = fieldName
}
ext := ""
filename = filename + ext
fw, err = w.CreateFormFile(fieldName, filename)
if err == nil {
_, err = fw.Write(field.Bytes())
}
} else if !field.IsNil() {
// Handle slice of primitive values (as multiple form fields with the same name)
for j := 0; j < field.Len(); j++ {
elem := field.Index(j)
fw, err = w.CreateFormField(fieldName)
if err == nil {
switch elem.Kind() {
case reflect.String:
_, err = fw.Write([]byte(elem.String()))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
_, err = fw.Write([]byte(strconv.FormatInt(elem.Int(), 10)))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
_, err = fw.Write([]byte(strconv.FormatUint(elem.Uint(), 10)))
}
}
}
}
case reflect.Struct:
var jsonData []byte
jsonData, err = json.Marshal(field.Interface())
if err == nil {
fw, err = w.CreateFormField(fieldName)
if err == nil {
_, err = fw.Write(jsonData)
}
}
}
if err != nil {
return err
}
}
return nil
}

100
utils/utils.go Normal file
View File

@@ -0,0 +1,100 @@
package utils
import (
"encoding/json"
"fmt"
"os"
"strings"
"git.nix13.pw/scuroneko/slog"
)
func GetLoggerLevel() slog.LogLevel {
level := slog.FATAL
if os.Getenv("DEBUG") == "true" {
level = slog.DEBUG
}
return level
}
func Cast[A, B any](src A) (*B, error) {
m, err := StructToMap(src)
if err != nil {
return nil, err
}
out := new(B)
err = MapToStruct(m, out)
if err != nil {
return nil, err
}
return out, nil
}
// MapToStruct unsafe function
func MapToStruct(m map[string]any, s any) error {
data, err := json.Marshal(m)
if err != nil {
return err
}
err = json.Unmarshal(data, s)
return err
}
// SliceToStruct unsafe function
func SliceToStruct(sl []any, s any) error {
data, err := json.Marshal(sl)
if err != nil {
return err
}
err = json.Unmarshal(data, s)
return err
}
// AnyToStruct unsafe function
func AnyToStruct(src, dest any) error {
data, err := json.Marshal(src)
if err != nil {
return err
}
err = json.Unmarshal(data, dest)
return err
}
func MapToJson(m map[string]any) (string, error) {
data, err := json.Marshal(m)
return string(data), err
}
func StructToMap(s any) (map[string]any, error) {
data, err := json.Marshal(s)
if err != nil {
return nil, err
}
m := make(map[string]any)
err = json.Unmarshal(data, &m)
return m, err
}
func Map[T, V any](ts []T, fn func(T) V) []V {
result := make([]V, len(ts))
for i, t := range ts {
result[i] = fn(t)
}
return result
}
func EscapeMarkdown(s string) string {
s = strings.ReplaceAll(s, "_", `\_`)
s = strings.ReplaceAll(s, "*", `\*`)
s = strings.ReplaceAll(s, "[", `\[`)
return strings.ReplaceAll(s, "`", "\\`")
}
func EscapeMarkdownV2(s string) string {
symbols := []string{"_", "*", "[", "]", "(", ")", "~", "`", ">", "#", "+", "-", "=", "|", "{", "}", ".", "!"}
for _, symbol := range symbols {
s = strings.ReplaceAll(s, symbol, fmt.Sprintf("\\%s", symbol))
}
return s
}

8
utils/version.go Normal file
View File

@@ -0,0 +1,8 @@
package utils
const (
VersionString = "0.5.0"
VersionMajor = 0
VersionMinor = 5
VersionPatch = 0
)