jsonschema: unexport ForType (#71)

There's no evidence that it's needed.

Also, improve doc and error messages.
This commit is contained in:
Jonathan Amsterdam 2025-06-30 14:44:40 -04:00 • committed by GitHub
commit 2facfc6ffe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 25 additions and 26 deletions

View file

@ -40,7 +40,7 @@ or as a value of this type:
# Inference # Inference
The [For] and [ForType] functions return a [Schema] describing the given Go type. The [For] function returns a [Schema] describing the given Go type.
The type cannot contain any function or channel types, and any map types must have a string key. The type cannot contain any function or channel types, and any map types must have a string key.
For example, calling For on the above Player type results in this schema: For example, calling For on the above Player type results in this schema:

View file

@ -15,40 +15,39 @@ import (
// For constructs a JSON schema object for the given type argument. // For constructs a JSON schema object for the given type argument.
// //
// It is a convenience for ForType.
func For[T any]() (*Schema, error) {
return ForType(reflect.TypeFor[T]())
}
// ForType constructs a JSON schema object for the given type.
// It translates Go types into compatible JSON schema types, as follows: // It translates Go types into compatible JSON schema types, as follows:
// - strings have schema type "string" // - Strings have schema type "string".
// - bools have schema type "boolean" // - Bools have schema type "boolean".
// - signed and unsigned integer types have schema type "integer" // - Signed and unsigned integer types have schema type "integer".
// - floating point types have schema type "number" // - Floating point types have schema type "number".
// - slices and arrays have schema type "array", and a corresponding schema // - Slices and arrays have schema type "array", and a corresponding schema
// for items // for items.
// - maps with string key have schema type "object", and corresponding // - Maps with string key have schema type "object", and corresponding
// schema for additionalProperties // schema for additionalProperties.
// - structs have schema type "object", and disallow additionalProperties. // - Structs have schema type "object", and disallow additionalProperties.
// Their properties are derived from exported struct fields, using the // Their properties are derived from exported struct fields, using the
// struct field json name. Fields that are marked "omitempty" are // struct field JSON name. Fields that are marked "omitempty" are
// considered optional; all other fields become required properties. // considered optional; all other fields become required properties.
// //
// It returns an error if t contains (possibly recursively) any of the following Go // For returns an error if t contains (possibly recursively) any of the following Go
// types, as they are incompatible with the JSON schema spec. // types, as they are incompatible with the JSON schema spec.
// - maps with key other than 'string' // - maps with key other than 'string'
// - function types // - function types
// - complex numbers // - complex numbers
// - unsafe pointers // - unsafe pointers
// //
// The cannot be any cycles in the types. // The types must not have cycles.
// TODO(rfindley): we could perhaps just skip these incompatible fields. func For[T any]() (*Schema, error) {
func ForType(t reflect.Type) (*Schema, error) { // TODO: consider skipping incompatible fields, instead of failing.
return typeSchema(t) s, err := forType(reflect.TypeFor[T]())
if err != nil {
var z T
return nil, fmt.Errorf("For[%T](): %w", z, err)
}
return s, nil
} }
func typeSchema(t reflect.Type) (*Schema, error) { func forType(t reflect.Type) (*Schema, error) {
// Follow pointers: the schema for *T is almost the same as for T, except that // Follow pointers: the schema for *T is almost the same as for T, except that
// an explicit JSON "null" is allowed for the pointer. // an explicit JSON "null" is allowed for the pointer.
allowNull := false allowNull := false
@ -82,14 +81,14 @@ func typeSchema(t reflect.Type) (*Schema, error) {
return nil, fmt.Errorf("unsupported map key type %v", t.Key().Kind()) return nil, fmt.Errorf("unsupported map key type %v", t.Key().Kind())
} }
s.Type = "object" s.Type = "object"
s.AdditionalProperties, err = typeSchema(t.Elem()) s.AdditionalProperties, err = forType(t.Elem())
if err != nil { if err != nil {
return nil, fmt.Errorf("computing map value schema: %v", err) return nil, fmt.Errorf("computing map value schema: %v", err)
} }
case reflect.Slice, reflect.Array: case reflect.Slice, reflect.Array:
s.Type = "array" s.Type = "array"
s.Items, err = typeSchema(t.Elem()) s.Items, err = forType(t.Elem())
if err != nil { if err != nil {
return nil, fmt.Errorf("computing element schema: %v", err) return nil, fmt.Errorf("computing element schema: %v", err)
} }
@ -115,7 +114,7 @@ func typeSchema(t reflect.Type) (*Schema, error) {
if s.Properties == nil { if s.Properties == nil {
s.Properties = make(map[string]*Schema) s.Properties = make(map[string]*Schema)
} }
s.Properties[info.Name], err = typeSchema(field.Type) s.Properties[info.Name], err = forType(field.Type)
if err != nil { if err != nil {
return nil, err return nil, err
} }