better configuration strategy

This commit is contained in:
tavo 2025-07-01 00:29:14 -06:00
parent 513bcced9a
commit 3086b8fcd6
8 changed files with 38 additions and 25 deletions

View file

@ -1,4 +1,4 @@
package middleware package config
import ( import (
"log" "log"

View file

@ -25,7 +25,7 @@ var ViewMap = map[string][]string{
), ),
} }
var FuncMap = map[string]any{ var ViewFormatters = map[string]any{
"uppercase": func(s string) string { return strings.ToUpper(s) }, "uppercase": func(s string) string { return strings.ToUpper(s) },
"firstWord": func(s string) string { "firstWord": func(s string) string {
words := strings.Fields(s) words := strings.Fields(s)

View file

@ -4,6 +4,15 @@ import (
"strings" "strings"
) )
type Formatter func(string) string
var Formatters = map[string]Formatter{
"trim": strings.TrimSpace,
"lower": strings.ToLower,
"upper": strings.ToUpper,
"capitalize": capitalize,
}
func capitalize(s string) string { func capitalize(s string) string {
words := strings.Fields(s) words := strings.Fields(s)
for i, word := range words { for i, word := range words {

View file

@ -9,23 +9,6 @@ import (
"strings" "strings"
) )
type Formatter func(string) string
var Formatters = map[string]Formatter{
"trim": strings.TrimSpace,
"lower": strings.ToLower,
"upper": strings.ToUpper,
"capitalize": capitalize,
}
type Validator func(fieldName string, value any, param string) error
var Validators = map[string]Validator{
"nonzero": nonzero,
"minlen": minlen,
"email": email,
}
func FormToStruct[T any](r *http.Request) (T, error) { func FormToStruct[T any](r *http.Request) (T, error) {
var target T var target T
if err := r.ParseForm(); err != nil { if err := r.ParseForm(); err != nil {
@ -184,8 +167,8 @@ func parseValidators(tag string) []struct {
Name string Name string
Param string Param string
} }
parts := strings.SplitSeq(tag, ",") parts := strings.Split(tag, ",")
for part := range parts { for _, part := range parts {
pair := strings.SplitN(part, ":", 2) pair := strings.SplitN(part, ":", 2)
if len(pair) == 2 { if len(pair) == 2 {
result = append(result, struct{ Name, Param string }{pair[0], pair[1]}) result = append(result, struct{ Name, Param string }{pair[0], pair[1]})

View file

@ -4,20 +4,32 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"strconv" "strconv"
"strings" "net/mail"
) )
type Validator func(fieldName string, value any, param string) error
var Validators = map[string]Validator{
"nonzero": nonzero,
"minlen": minlen,
"email": email,
}
func nonzero(field string, value any, _ string) error { func nonzero(field string, value any, _ string) error {
v := reflect.ValueOf(value) v := reflect.ValueOf(value)
if v.Kind() == reflect.String && v.Len() == 0 { if v.Kind() == reflect.String && v.Len() == 0 {
return fmt.Errorf("field '%s' must not be empty", field) return fmt.Errorf("field '%s' must not be empty", field)
} }
if v.Kind() == reflect.Slice && v.Len() == 0 { if v.Kind() == reflect.Slice && v.Len() == 0 {
return fmt.Errorf("field '%s' must not be empty", field) return fmt.Errorf("field '%s' must not be empty", field)
} }
if v.Kind() >= reflect.Int && v.Kind() <= reflect.Float64 && v.IsZero() { if v.Kind() >= reflect.Int && v.Kind() <= reflect.Float64 && v.IsZero() {
return fmt.Errorf("field '%s' must be non-zero", field) return fmt.Errorf("field '%s' must be non-zero", field)
} }
return nil return nil
} }
@ -26,16 +38,24 @@ func minlen(field string, value any, param string) error {
if err != nil { if err != nil {
return fmt.Errorf("invalid minlen param for field '%s'", field) return fmt.Errorf("invalid minlen param for field '%s'", field)
} }
if str, ok := value.(string); ok && len(str) < min { if str, ok := value.(string); ok && len(str) < min {
return fmt.Errorf("field '%s' must be at least %d characters", field, min) return fmt.Errorf("field '%s' must be at least %d characters", field, min)
} }
return nil return nil
} }
func email(field string, value any, _ string) error { func email(field string, value any, _ string) error {
str, ok := value.(string) str, ok := value.(string)
if !ok || !strings.Contains(str, "@") { if !ok {
return fmt.Errorf("field '%s' must be a valid email address", field) return fmt.Errorf("field '%s' must be a valid email address", field)
} }
_, err := mail.ParseAddress(str)
if err != nil {
return fmt.Errorf("invalid email address: %v", err)
}
return nil return nil
} }

View file

@ -39,7 +39,7 @@ func init() {
} }
func main() { func main() {
err := views.Init(viewFS, config.ViewMap, config.FuncMap) err := views.Init(viewFS, config.ViewMap, config.ViewFormatters)
if err != nil { if err != nil {
log.Fatalf("failed to initialize templates: %v", err) log.Fatalf("failed to initialize templates: %v", err)
} }

View file

@ -3,6 +3,7 @@ package main
import ( import (
"net/http" "net/http"
"git.tavo.one/tavo/axiom/config"
"git.tavo.one/tavo/axiom/handlers" "git.tavo.one/tavo/axiom/handlers"
"git.tavo.one/tavo/axiom/middleware" "git.tavo.one/tavo/axiom/middleware"
) )
@ -11,7 +12,7 @@ func routes(handler *handlers.Handler) *http.ServeMux {
router := http.NewServeMux() router := http.NewServeMux()
protectedStack := middleware.Stack( protectedStack := middleware.Stack(
middleware.AuthCheck, config.AuthCheck,
) )
router.HandleFunc( router.HandleFunc(