Artifact cffc8f3b7ebeeb89ecf487a449dd4095479b292e3b463055a73a4e0db313eec3:
- File
internal/ops/docs.go
— part of check-in
[fb2f393a1e]
at
2026-08-15 23:02:23
on branch trunk
— docs: API reference generated from the registry; staleness gate
quarry docs + make docs; TestDocsCurrent fails when docs/api.md drifts (0003: documentation is a phase exit criterion). (user: dmestas, size: 2932) [annotate] [blame] [check-ins using]
// docs.go — the human-readable API reference, generated from the same // registry the router and spec serve (0003: a docs page generated from // the spec is each phase's exit criterion; the registry is the source // of truth, never the document). package ops import ( "fmt" "reflect" "sort" "strings" ) func (a Auth) String() string { switch a { case Token: return "token" case Admin: return "admin" } return "anonymous" } // Docs renders the registry as markdown. Deterministic: ops sort by // name, fields keep struct order. Regenerate with `make docs`. func Docs(r *Registry, title string) []byte { var b strings.Builder fmt.Fprintf(&b, "# %s API\n\n", title) b.WriteString("Generated from the op registry by `quarry docs` — edit the ops, not this file; regenerate with `make docs`.\n") b.WriteString("Every op also appears in the OpenAPI document (`quarry spec`). Query inputs ride the query string; the same names work as JSON.\n") ds := r.All() sort.Slice(ds, func(i, j int) bool { return ds[i].Name < ds[j].Name }) for _, d := range ds { fmt.Fprintf(&b, "\n## %s\n\n", d.Name) fmt.Fprintf(&b, "`%s %s` — %s, %s\n\n", d.Method, d.Pattern, d.Kind, d.Auth) if d.Doc != "" { fmt.Fprintf(&b, "%s\n\n", d.Doc) } if fields := exportedFields(d.In); len(fields) > 0 { b.WriteString("Input:\n\n") b.WriteString("| field | type | rules |\n|---|---|---|\n") for _, f := range fields { fmt.Fprintf(&b, "| `%s` | %s | %s |\n", jsonName(f), typeName(f.Type), rulesOf(f)) } b.WriteString("\n") } if fields := exportedFields(d.Out); len(fields) > 0 { b.WriteString("Output fields:\n\n") for _, f := range fields { fmt.Fprintf(&b, "- `%s` (%s)\n", jsonName(f), typeName(f.Type)) } } } return []byte(b.String()) } func jsonName(f reflect.StructField) string { name, _, _ := strings.Cut(f.Tag.Get("json"), ",") if name == "" { return f.Name } return name } func rulesOf(f reflect.StructField) string { tag := f.Tag.Get("validate") if tag == "" { return "—" } // A bare | would split the markdown table cell. return "`" + strings.ReplaceAll(tag, "|", `\|`) + "`" } // typeName renders a field type the way the JSON reads. func typeName(t reflect.Type) string { switch t.Kind() { case reflect.String: return "string" case reflect.Bool: return "boolean" case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return "integer" case reflect.Float32, reflect.Float64: return "number" case reflect.Slice: return "array of " + typeName(t.Elem()) case reflect.Struct: var parts []string for _, f := range exportedFields(t) { parts = append(parts, jsonName(f)+": "+typeName(f.Type)) } return "object {" + strings.Join(parts, ", ") + "}" case reflect.Pointer: return typeName(t.Elem()) } return t.Kind().String() }