Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 17 additions & 14 deletions generator/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ import (
"github.com/voidclancy/valk/schema"
)

var DEFAULT_FUNCS = map[string]string{
"autoincrement": "",
"cuid": "generateCUID()",
"cuid(1)": "generateCUID()",
"cuid(2)": "generateCUID2()",
"uuid": "generateUUID()",
"uuid(4)": "generateUUID()",
"uuid(7)": "generateUUID7()",
"ulid": "generateULID()",
"nanoid": "generateNanoID()",
"now": "time.Now()",
}

func capitalize(s string) string {
if s == "" {
return ""
Expand Down Expand Up @@ -69,22 +82,12 @@ func hasTimeField(m *schema.Model) bool {
return false
}
func isKnownDefaultFunc(funcName string) bool {
switch funcName {
case "autoincrement", "cuid", "uuid", "now":
return true
}
return false
_, ok := DEFAULT_FUNCS[funcName]
return ok
}

func defaultFuncCall(funcName string) string {
switch funcName {
case "cuid":
return "generateCUID()"
case "uuid":
return "generateUUID()"
case "now":
return "time.Now()"
}
return ""
return DEFAULT_FUNCS[funcName]
}
func hasStringField(m *schema.Model) bool {
for _, sf := range m.ScalarFields {
Expand Down
61 changes: 61 additions & 0 deletions generator/templates/header.gotpl
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ var _ = time.Time{}
var _ = json.RawMessage{}
var _ = strings.Join
var _ = uuid.New
var _ = uuid.NewV7
var _ = rand.Read
var _ = strconv.AppendUint

Expand Down Expand Up @@ -52,6 +53,66 @@ func generateUUID() string {
return uuid.New().String()
}

func generateUUID7() string {
id, err := uuid.NewV7()
if err != nil {
return uuid.New().String()
}
return id.String()
}

func generateCUID2() string {
now := uint64(time.Now().UnixMilli())
b := make([]byte, 12)
_, _ = rand.Read(b)
buf := make([]byte, 0, 34)
buf = strconv.AppendUint(buf, now, 36)
const chars = "0123456789abcdefghijklmnopqrstuvwxyz"
for _, v := range b {
buf = append(buf, chars[v&0x1f])
buf = append(buf, chars[(v>>5)&0x1f])
}
return string(buf)
}

func generateULID() string {
now := uint64(time.Now().UnixMilli())
b := make([]byte, 10)
_, _ = rand.Read(b)
bits := make([]byte, 16)
bits[0] = byte(now >> 40)
bits[1] = byte(now >> 32)
bits[2] = byte(now >> 24)
bits[3] = byte(now >> 16)
bits[4] = byte(now >> 8)
bits[5] = byte(now)
copy(bits[6:], b)

const crockford = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
var buf [26]byte
for i := 25; i >= 0; i-- {
bitOff := i * 5
byteIdx := bitOff / 8
byteOff := uint(bitOff % 8)
val := uint16(bits[byteIdx]) >> byteOff
if byteIdx+1 < 16 {
val |= uint16(bits[byteIdx+1]) << (8 - byteOff)
}
buf[i] = crockford[val&0x1f]
}
return string(buf[:])
}

func generateNanoID() string {
const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz-"
b := make([]byte, 21)
_, _ = rand.Read(b)
for i := range b {
b[i] = alphabet[b[i]&0x3f]
}
return string(b)
}

// FieldError represents a single validation failure on a specific field.
type FieldError struct {
Field string
Expand Down
10 changes: 10 additions & 0 deletions generator/templates/model_create.gotpl
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ func validate{{ .Model.Name }}Create(assignments []FieldAssignment) error {
errs.Add("{{ $field.Name }}", v, "range", "value is out of range for TinyInt (-128 to 127)")
}
{{- end }}
{{- else }}
if _, ok := a.Val.({{ trimPrefix $field.GoType "*" }}); !ok {
errs.Add("{{ $field.Name }}", a.Val, "type", "field {{ $field.Name }} must be of type {{ trimPrefix $field.GoType "*" }}")
}
{{- end }}
{{- else }}
if _, ok := a.Val.({{ trimPrefix $field.GoType "*" }}); !ok {
Expand All @@ -106,6 +110,12 @@ func validate{{ .Model.Name }}Create(assignments []FieldAssignment) error {
errs.Add("{{ $field.Name }}", "", "required", "field {{ $fieldName }} is required")
}
{{- end }}
{{- else }}
{{- if and (eq $field.Default nil) (not $field.Optional) (not $field.IsArray) }}
if !provided["{{ $col }}"] {
errs.Add("{{ $field.Name }}", nil, "required", "field {{ $fieldName }} is required")
}
{{- end }}
{{- end }}
{{- end }}

Expand Down
74 changes: 74 additions & 0 deletions integration/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,77 @@ model CategoryToPost {

@@id([postId, categoryId])
}

model DefaultsTest {
uuid4 String @id @default(uuid(4))
uuid7 String @default(uuid(7))
uuidNoArgs String @default(uuid())
cuid1 String @default(cuid(1))
cuid2 String @default(cuid(2))
cuidNoArgs String @default(cuid())
ulid String @default(ulid())
nanoid String @default(nanoid())
now DateTime @default(now())
}

model AllFieldsSoFar {
id Int @id @default(autoincrement())

// String
stringReq String
stringOpt String?
stringDefault String @default("default")
stringVarchar String
stringChar String

// Default function variants
cuidDefault String @default(cuid())
cuid1Default String @default(cuid(1))
cuid2Default String @default(cuid(2))
uuidDefault String @default(uuid())
uuid4Default String @default(uuid(4))
uuid7Default String @default(uuid(7))
ulidDefault String @default(ulid())
nanoidDefault String @default(nanoid())
uuidDb String

// Int
intReq Int
intOpt Int?
intDefault Int @default(42)
smallInt Int
tinyInt Int

// BigInt
bigIntReq BigInt
bigIntOpt BigInt?

// Float
floatReq Float
floatOpt Float?

// Decimal
decimalReq Decimal
decimalOpt Decimal?
decimalPrecise Decimal

// Boolean
boolReq Boolean
boolOpt Boolean?
boolDefault Boolean @default(false)

// DateTime
dateTimeReq DateTime
dateTimeOpt DateTime?
dateTimeDefault DateTime @default(now())
updatedAt DateTime @updatedAt
dateTimeTz DateTime

// Json
jsonReq Json
jsonOpt Json?

// Bytes
bytesReq Bytes
bytesOpt Bytes?
}
Loading
Loading