fixed makefile
This commit is contained in:
parent
49f59f090f
commit
e11eda9b3b
7 changed files with 210 additions and 30 deletions
|
@ -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 "" \
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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=
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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++ {
|
||||||
|
|
|
@ -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)),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue