Skip to content

Commit

Permalink
feat: treat encoding.TextUnmarshaler as string in schema
Browse files Browse the repository at this point in the history
  • Loading branch information
danielgtaylor committed Aug 19, 2024
1 parent 853cd70 commit a63c9cb
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 1 deletion.
6 changes: 6 additions & 0 deletions registry.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package huma

import (
"encoding"
"encoding/json"
"fmt"
"reflect"
Expand Down Expand Up @@ -92,6 +93,11 @@ func (r *mapRegistry) Schema(t reflect.Type, allowRef bool, hint string) *Schema
// Special case: type provides its own schema
getsRef = false
}
if _, ok := v.(encoding.TextUnmarshaler); ok {
// Special case: type can be unmarshalled from text so will be a `string`
// and doesn't need a ref. This simplifies the schema a little bit.
getsRef = false
}

name := r.namer(origType, hint)

Expand Down
11 changes: 10 additions & 1 deletion schema.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package huma

import (
"encoding"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -686,7 +687,7 @@ func schemaFromType(r Registry, t reflect.Type) *Schema {
return custom
}

// Handle special cases.
// Handle special cases for known stdlib types.
switch t {
case timeType:
return &Schema{Type: TypeString, Nullable: isPointer, Format: "date-time"}
Expand All @@ -700,6 +701,14 @@ func schemaFromType(r Registry, t reflect.Type) *Schema {
return &Schema{}
}

if _, ok := v.(encoding.TextUnmarshaler); ok {
// Special case: types that implement encoding.TextUnmarshaler are able to
// be loaded from plain text, and so should be treated as strings.
// This behavior can be overidden by implementing `huma.SchemaProvider`
// and returning a custom schema.
return &Schema{Type: TypeString, Nullable: isPointer}
}

minZero := 0.0
switch t.Kind() {
case reflect.Bool:
Expand Down
29 changes: 29 additions & 0 deletions schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package huma_test

import (
"bytes"
"encoding"
"encoding/json"
"math/bits"
"net"
Expand Down Expand Up @@ -1172,6 +1173,34 @@ func TestSchemaGenericNamingFromModule(t *testing.T) {
}`, string(b))
}

type MyDate time.Time

func (d *MyDate) UnmarshalText(data []byte) error {
t, err := time.Parse(time.RFC3339, string(data))
if err != nil {
return err
}
*d = MyDate(t)
return nil
}

var _ encoding.TextUnmarshaler = (*MyDate)(nil)

func TestCustomDateType(t *testing.T) {
type O struct {
Date MyDate `json:"date"`
}

var o O
err := json.Unmarshal([]byte(`{"date": "2022-01-01T00:00:00Z"}`), &o)
require.NoError(t, err)
assert.Equal(t, MyDate(time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)), o.Date)

r := huma.NewMapRegistry("#/components/schemas/", huma.DefaultSchemaNamer)
s := r.Schema(reflect.TypeOf(o), false, "")
assert.Equal(t, "string", s.Properties["date"].Type)
}

type OmittableNullable[T any] struct {
Sent bool
Null bool
Expand Down

0 comments on commit a63c9cb

Please sign in to comment.