feat: complete contract-driven business development and MySQL integration

This commit is contained in:
2026-09-24 21:56:04 +08:00
parent f9afb8f8dd
commit f5478cdc76
108 changed files with 6557 additions and 550 deletions

174
contract/documentation.go Normal file
View File

@@ -0,0 +1,174 @@
package contract
import (
"encoding/json"
"errors"
"fmt"
"io"
"reflect"
"strings"
)
// Documentation is part of the contract compiler, so CLI and runtime agree.
func applyDocumentation(f *field, tags map[string]string) error {
s := f.value.schema
if f.maxBytes > 0 {
v := f.maxBytes
s.MaxBytes = &v
}
if f.source == "file" && f.value.typ.Kind() == reflect.Slice {
v := f.maxFiles
if v == 0 {
v = 16
}
s.MaxItems = &v
}
s.Description = tags["description"]
if raw, ok := tags["enumDescriptions"]; ok {
if err := json.Unmarshal([]byte(raw), &s.EnumDescriptions); err != nil {
return errors.New("enumDescriptions must be a JSON string array")
}
count := len(s.Enum)
if s.Nullable && count > 0 && s.Enum[count-1] == nil {
count--
}
if count == 0 || len(s.EnumDescriptions) != count {
return errors.New("enumDescriptions must describe every declared enum value")
}
for _, v := range s.EnumDescriptions {
if strings.TrimSpace(v) == "" {
return errors.New("enum description cannot be empty")
}
}
}
if raw, ok := tags["example"]; ok {
var value any
if base(f.value).typ.Kind() == reflect.String && base(f.value).scalar == nil {
value = raw
} else {
decoder := json.NewDecoder(strings.NewReader(raw))
decoder.UseNumber()
var err error
value, err = decodeValue(decoder, 0)
if err != nil {
return errors.New("invalid example JSON")
}
if _, err = decoder.Token(); err != io.EOF {
return errors.New("example requires one value")
}
}
// A schema-only custom scalar never executes project decoders.
if !documentationHasScalar(f.value) {
v := reflect.New(f.value.typ).Elem()
if err := setValue(f.value, v, value); err != nil {
return fmt.Errorf("example: %w", err)
}
if err := checkField(*f, v); err != nil {
return fmt.Errorf("example: %w", err)
}
} else if err := checkDocumentationScalarTree(f.value, value); err != nil {
return err
}
s.Example = value
}
return nil
}
func documentationHasScalar(n *node) bool {
if n.scalar != nil {
return true
}
if n.elem != nil && documentationHasScalar(n.elem) {
return true
}
for _, f := range n.fields {
if documentationHasScalar(f.value) {
return true
}
}
return false
}
// Metadata never invokes application decoders, including nested scalar types.
func checkDocumentationScalarTree(n *node, value any) error {
if value == nil {
if n.typ.Kind() == reflect.Pointer || n.typ.Kind() == reflect.Interface {
return nil
}
return errors.New("example null requires a nullable field")
}
if n.typ.Kind() == reflect.Pointer {
return checkDocumentationScalarTree(n.elem, value)
}
if n.scalar != nil {
for _, kind := range strings.Split(n.scalar.JSONInput, "|") {
if scalarAccepts(strings.TrimSpace(kind), value) {
return nil
}
}
return errors.New("example does not match scalar input")
}
if !documentationHasScalar(n) {
v := reflect.New(n.typ).Elem()
return setValue(n, v, value)
}
switch n.typ.Kind() {
case reflect.Slice:
values, ok := value.([]any)
if !ok {
return errors.New("example requires an array")
}
if n.schema.MinItems != nil && len(values) < *n.schema.MinItems || n.schema.MaxItems != nil && len(values) > *n.schema.MaxItems {
return errors.New("example violates item limits")
}
for _, v := range values {
if err := checkDocumentationScalarTree(n.elem, v); err != nil {
return err
}
}
case reflect.Map:
values, ok := value.(map[string]any)
if !ok {
return errors.New("example requires an object")
}
for _, v := range values {
if err := checkDocumentationScalarTree(n.elem, v); err != nil {
return err
}
}
case reflect.Struct:
values, ok := value.(map[string]any)
if !ok {
return errors.New("example requires an object")
}
declared := map[string]bool{}
for _, f := range n.fields {
declared[f.name] = true
v, ok := values[f.name]
if !ok {
if !f.optional && !f.hasDefault {
return errors.New("example missing field")
}
continue
}
if err := checkDocumentationScalarTree(f.value, v); err != nil {
return err
}
if !documentationHasScalar(f.value) {
typed := reflect.New(f.value.typ).Elem()
if err := setValue(f.value, typed, v); err != nil {
return err
}
if err := checkField(f, typed); err != nil {
return err
}
}
}
for name := range values {
if !declared[name] {
return errors.New("unknown example field")
}
}
}
return nil
}