fixed makefile

This commit is contained in:
anthdm 2024-06-08 21:59:00 +02:00
parent 49f59f090f
commit e11eda9b3b
7 changed files with 210 additions and 30 deletions

View file

@ -8,7 +8,7 @@ endif
# re-create _templ.txt files on change, then send reload event to browser. # re-create _templ.txt files on change, then send reload event to browser.
# Default url: http://localhost:7331 # Default url: http://localhost:7331
templ: templ:
@templ generate --watch --proxy="http://localhost$(HTTP_LISTEN_ADDR)" --open-browser=false -v @go run github.com/a-h/templ/cmd/templ@latest generate --watch --proxy="http://localhost$(HTTP_LISTEN_ADDR)" --open-browser=false -v
# run air to detect any go file changes to re-build and re-run the server. # run air to detect any go file changes to re-build and re-run the server.
server: server:
@ -30,7 +30,7 @@ esbuild:
# watch for any js or css change in the assets/ folder, then reload the browser via templ proxy. # watch for any js or css change in the assets/ folder, then reload the browser via templ proxy.
sync_assets: sync_assets:
go run github.com/cosmtrek/air@v1.51.0 \ go run github.com/cosmtrek/air@v1.51.0 \
--build.cmd "templ generate --notify-proxy" \ --build.cmd "go run github.com/a-h/templ/cmd/templ@latest generate --notify-proxy" \
--build.bin "true" \ --build.bin "true" \
--build.delay "100" \ --build.delay "100" \
--build.exclude_dir "" \ --build.exclude_dir "" \

View file

@ -8,7 +8,7 @@ require (
) )
require ( require (
github.com/anthdm/gothkit v0.0.0-20240608092625-85a106b8717c github.com/anthdm/gothkit v0.0.0-20240608094934-49f59f090fb7
github.com/fatih/color v1.16.0 // indirect github.com/fatih/color v1.16.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect

View file

@ -4,6 +4,8 @@ github.com/anthdm/gothkit v0.0.0-20240608091251-8198dfa775c8 h1:2C+WGVrNvIiSLFLJ
github.com/anthdm/gothkit v0.0.0-20240608091251-8198dfa775c8/go.mod h1:/zW/YMI9hOvRTQwmpfRU+CoJ+m4LZYuzC496KFOJAM8= github.com/anthdm/gothkit v0.0.0-20240608091251-8198dfa775c8/go.mod h1:/zW/YMI9hOvRTQwmpfRU+CoJ+m4LZYuzC496KFOJAM8=
github.com/anthdm/gothkit v0.0.0-20240608092625-85a106b8717c h1:JYV/co6zKfSR4eR/MVORi6wQvva1tikbBS8Xgd2GVFc= github.com/anthdm/gothkit v0.0.0-20240608092625-85a106b8717c h1:JYV/co6zKfSR4eR/MVORi6wQvva1tikbBS8Xgd2GVFc=
github.com/anthdm/gothkit v0.0.0-20240608092625-85a106b8717c/go.mod h1:/zW/YMI9hOvRTQwmpfRU+CoJ+m4LZYuzC496KFOJAM8= github.com/anthdm/gothkit v0.0.0-20240608092625-85a106b8717c/go.mod h1:/zW/YMI9hOvRTQwmpfRU+CoJ+m4LZYuzC496KFOJAM8=
github.com/anthdm/gothkit v0.0.0-20240608094934-49f59f090fb7 h1:W2/7bSR+GBehAC1+Tg/wigCA6o4y3VAFnkLG4NypB6A=
github.com/anthdm/gothkit v0.0.0-20240608094934-49f59f090fb7/go.mod h1:/zW/YMI9hOvRTQwmpfRU+CoJ+m4LZYuzC496KFOJAM8=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=

View file

@ -46,7 +46,7 @@ html,
-o-tab-size: 4; -o-tab-size: 4;
tab-size: 4; tab-size: 4;
/* 3 */ /* 3 */
font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
/* 4 */ /* 4 */
font-feature-settings: normal; font-feature-settings: normal;
/* 5 */ /* 5 */

View file

@ -4,6 +4,8 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"regexp" "regexp"
"time"
"unicode"
) )
var ( var (
@ -11,6 +13,7 @@ var (
urlRegex = regexp.MustCompile(`^(http(s)?://)?([\da-z\.-]+)\.([a-z\.]{2,6})([/\w \.-]*)*/?$`) urlRegex = regexp.MustCompile(`^(http(s)?://)?([\da-z\.-]+)\.([a-z\.]{2,6})([/\w \.-]*)*/?$`)
) )
// RuleSet holds the state of a single rule.
type RuleSet struct { type RuleSet struct {
Name string Name string
RuleValue any RuleValue any
@ -21,6 +24,7 @@ type RuleSet struct {
ValidateFunc func(RuleSet) bool ValidateFunc func(RuleSet) bool
} }
// Message overrides the default message of a RuleSet
func (set RuleSet) Message(msg string) RuleSet { func (set RuleSet) Message(msg string) RuleSet {
set.ErrorMessage = msg set.ErrorMessage = msg
return set return set
@ -49,8 +53,64 @@ func In[T any](values []T) RuleSet {
} }
} }
func Required() RuleSet { var ContainsUpper = RuleSet{
return RuleSet{ Name: "containsUpper",
ValidateFunc: func(rule RuleSet) bool {
str, ok := rule.FieldValue.(string)
if !ok {
return false
}
for _, ch := range str {
if unicode.IsUpper(rune(ch)) {
return true
}
}
return false
},
MessageFunc: func(set RuleSet) string {
return "must contain at least 1 uppercase character"
},
}
var ContainsNumeric = RuleSet{
Name: "containsNumeric",
ValidateFunc: func(rule RuleSet) bool {
str, ok := rule.FieldValue.(string)
if !ok {
return false
}
for _, ch := range str {
if isSpecial(ch) {
return true
}
}
return false
},
MessageFunc: func(set RuleSet) string {
return "must contain at least 1 numeric character"
},
}
var ContainsSpecial = RuleSet{
Name: "containsSpecial",
ValidateFunc: func(rule RuleSet) bool {
str, ok := rule.FieldValue.(string)
if !ok {
return false
}
for _, ch := range str {
if isSpecial(ch) {
return true
}
}
return false
},
MessageFunc: func(set RuleSet) string {
return "must contain at least 1 special character"
},
}
var Required = RuleSet{
Name: "required", Name: "required",
MessageFunc: func(set RuleSet) string { MessageFunc: func(set RuleSet) string {
return "is a required field" return "is a required field"
@ -62,11 +122,9 @@ func Required() RuleSet {
} }
return len(str) > 0 return len(str) > 0
}, },
}
} }
func Url() RuleSet { var URL = RuleSet{
return RuleSet{
Name: "url", Name: "url",
MessageFunc: func(set RuleSet) string { MessageFunc: func(set RuleSet) string {
return "is not a valid url" return "is not a valid url"
@ -78,11 +136,9 @@ func Url() RuleSet {
} }
return urlRegex.MatchString(u) return urlRegex.MatchString(u)
}, },
}
} }
func Email() RuleSet { var Email = RuleSet{
return RuleSet{
Name: "email", Name: "email",
MessageFunc: func(set RuleSet) string { MessageFunc: func(set RuleSet) string {
return "is not a valid email address" return "is not a valid email address"
@ -94,6 +150,64 @@ func Email() RuleSet {
} }
return emailRegex.MatchString(email) return emailRegex.MatchString(email)
}, },
}
var Time = RuleSet{
Name: "time",
ValidateFunc: func(set RuleSet) bool {
t, ok := set.FieldValue.(time.Time)
if !ok {
return false
}
return t.After(time.Time{})
},
MessageFunc: func(set RuleSet) string {
return "is not a valid time"
},
}
func TimeAfter(t time.Time) RuleSet {
return RuleSet{
Name: "timeAfter",
ValidateFunc: func(set RuleSet) bool {
t, ok := set.FieldValue.(time.Time)
if !ok {
return false
}
return t.After(t)
},
MessageFunc: func(set RuleSet) string {
return fmt.Sprintf("is not after %v", set.FieldValue)
},
}
}
func TimeBefore(t time.Time) RuleSet {
return RuleSet{
Name: "timeBefore",
ValidateFunc: func(set RuleSet) bool {
t, ok := set.FieldValue.(time.Time)
if !ok {
return false
}
return t.Before(t)
},
MessageFunc: func(set RuleSet) string {
return fmt.Sprintf("is not before %v", set.FieldValue)
},
}
}
func EQ[T comparable](v T) RuleSet {
return RuleSet{
Name: "eq",
RuleValue: v,
ValidateFunc: func(set RuleSet) bool {
return set.FieldValue.(T) == v
},
MessageFunc: func(set RuleSet) string {
return fmt.Sprintf("should be equal to %v", v)
},
} }
} }
@ -182,3 +296,7 @@ func Min(n int) RuleSet {
}, },
} }
} }
func isSpecial(ch rune) bool {
return true
}

View file

@ -47,6 +47,7 @@ func Merge(schema, other Schema) Schema {
return newSchema return newSchema
} }
// Rules is a function that takes any amount of RuleSets
func Rules(rules ...RuleSet) []RuleSet { func Rules(rules ...RuleSet) []RuleSet {
ruleSets := make([]RuleSet, len(rules)) ruleSets := make([]RuleSet, len(rules))
for i := 0; i < len(ruleSets); i++ { for i := 0; i < len(ruleSets); i++ {

View file

@ -1,11 +1,70 @@
package validate package validate
import ( import (
"fmt"
"testing" "testing"
"time"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
var createdAt = time.Now()
var testSchema = Schema{
"createdAt": Rules(Time),
"startedAt": Rules(TimeBefore(time.Now())),
"deletedAt": Rules(TimeAfter(createdAt)),
"email": Rules(Email),
"url": Rules(URL),
"password": Rules(
ContainsSpecial,
ContainsUpper,
ContainsNumeric,
Min(7),
Max(50),
),
"age": Rules(GTE(18)),
"bet": Rules(GT(0), LTE(10)),
"username": Rules(Required),
}
func TestTime(t *testing.T) {
type Foo struct {
CreatedAt time.Time
}
foo := Foo{
CreatedAt: time.Now(),
}
schema := Schema{
"createdAt": Rules(Time),
}
_, ok := Validate(foo, schema)
assert.True(t, ok)
foo.CreatedAt = time.Time{}
_, ok = Validate(foo, schema)
assert.False(t, ok)
}
func TestURL(t *testing.T) {
type Foo struct {
URL string
}
foo := Foo{
URL: "not an url",
}
schema := Schema{
"url": Rules(URL),
}
errors, ok := Validate(foo, schema)
assert.False(t, ok)
foo.URL = "www.user.com"
errors, ok = Validate(foo, schema)
assert.True(t, ok)
fmt.Println(errors)
}
func TestRuleIn(t *testing.T) { func TestRuleIn(t *testing.T) {
type Foo struct { type Foo struct {
Currency string Currency string
@ -29,7 +88,7 @@ func TestValidate(t *testing.T) {
Username string Username string
} }
schema := Schema{ schema := Schema{
"email": Rules(Email()), "email": Rules(Email),
// Test both lower and uppercase // Test both lower and uppercase
"Username": Rules(Min(3), Max(10)), "Username": Rules(Min(3), Max(10)),
} }