diff --git a/jsonschema/doc.go b/jsonschema/doc.go index a53df32d..f25b000a 100644 --- a/jsonschema/doc.go +++ b/jsonschema/doc.go @@ -40,7 +40,7 @@ or as a value of this type: # 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. For example, calling For on the above Player type results in this schema: diff --git a/jsonschema/infer.go b/jsonschema/infer.go index 20ec1345..1334bdf1 100644 --- a/jsonschema/infer.go +++ b/jsonschema/infer.go @@ -15,40 +15,39 @@ import ( // 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: -// - strings have schema type "string" -// - bools have schema type "boolean" -// - signed and unsigned integer types have schema type "integer" -// - floating point types have schema type "number" -// - slices and arrays have schema type "array", and a corresponding schema -// for items -// - maps with string key have schema type "object", and corresponding -// schema for additionalProperties -// - structs have schema type "object", and disallow additionalProperties. +// - Strings have schema type "string". +// - Bools have schema type "boolean". +// - Signed and unsigned integer types have schema type "integer". +// - Floating point types have schema type "number". +// - Slices and arrays have schema type "array", and a corresponding schema +// for items. +// - Maps with string key have schema type "object", and corresponding +// schema for additionalProperties. +// - Structs have schema type "object", and disallow additionalProperties. // 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. // -// 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. // - maps with key other than 'string' // - function types // - complex numbers // - unsafe pointers // -// The cannot be any cycles in the types. -// TODO(rfindley): we could perhaps just skip these incompatible fields. -func ForType(t reflect.Type) (*Schema, error) { - return typeSchema(t) +// The types must not have cycles. +func For[T any]() (*Schema, error) { + // TODO: consider skipping incompatible fields, instead of failing. + 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 // an explicit JSON "null" is allowed for the pointer. 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()) } s.Type = "object" - s.AdditionalProperties, err = typeSchema(t.Elem()) + s.AdditionalProperties, err = forType(t.Elem()) if err != nil { return nil, fmt.Errorf("computing map value schema: %v", err) } case reflect.Slice, reflect.Array: s.Type = "array" - s.Items, err = typeSchema(t.Elem()) + s.Items, err = forType(t.Elem()) if err != nil { return nil, fmt.Errorf("computing element schema: %v", err) } @@ -115,7 +114,7 @@ func typeSchema(t reflect.Type) (*Schema, error) { if s.Properties == nil { 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 { return nil, err }