package zip import ( "fmt" "reflect" "sort" "strings" ) // DocsBundle represents generated documentation pages formatted for @hanzo/docs. type DocsBundle struct { Pages map[string]string // filename (e.g. "get-validators.mdx") -> content Index string // index.mdx content } // DocsMarkdown generates @hanzo/docs compatible MDX documentation pages for all operations. // It shares the core prose extracted from doc comments across Go, Rust, and C++ SDKs. func (a *App) DocsMarkdown(title string) (*DocsBundle, error) { if title == "" { title = a.cfg.AppName if title == "" { title = "API Reference" } } bundle := &DocsBundle{ Pages: make(map[string]string), } ops := append([]*registeredOp(nil), a.Registry()...) sort.Slice(ops, func(i, j int) bool { return opName(ops[i]) < opName(ops[j]) }) var index strings.Builder index.WriteString("---\n") fmt.Fprintf(&index, "title: %q\n", title) fmt.Fprintf(&index, "description: API documentation and SDK references for %s\n", title) index.WriteString("---\n\n") fmt.Fprintf(&index, "# %s\n\n", title) index.WriteString("This API is fully self-documenting and natively supported across **Go**, **Rust**, and **C++**.\n\n") index.WriteString("## Operations\n\n") index.WriteString("| Operation | Method | Route | Description |\n") index.WriteString("| :--- | :--- | :--- | :--- |\n") for _, op := range ops { id := opName(op) doc, hasDoc := docFor(op.Pkg, op.Method, op.Path) summary := id if hasDoc && doc.Description != "" { summary = strings.Split(strings.TrimSpace(doc.Description), "\n")[0] } slug := strings.ReplaceAll(snakeCase(id), "_", "-") filename := slug + ".mdx" fmt.Fprintf(&index, "| [`%s`](./%s) | `%s` | `%s` | %s |\n", id, slug, op.Method, op.Path, summary) // Render operation MDX page var p strings.Builder p.WriteString("---\n") fmt.Fprintf(&p, "title: %q\n", id) fmt.Fprintf(&p, "description: %q\n", summary) p.WriteString("---\n\n") fmt.Fprintf(&p, "# %s\n\n", id) fmt.Fprintf(&p, "
%s %s
\n\n", op.Method, op.Path) if hasDoc && doc.Description != "" { p.WriteString("## Overview\n\n") p.WriteString(strings.TrimSpace(doc.Description)) p.WriteString("\n\n") } // Parameters table inFields := inspectFields(op.InType, docFields(hasDoc, doc)) if len(inFields) > 0 { p.WriteString("## Request Parameters\n\n") p.WriteString("| Field | Type | Required | Description |\n") p.WriteString("| :--- | :--- | :--- | :--- |\n") for _, f := range inFields { req := "No" if f.Required { req = "**Yes**" } desc := f.Doc if desc == "" { desc = "-" } fmt.Fprintf(&p, "| `%s` | `$%s` | %s | %s |\n", f.WireName, f.Type, req, desc) } p.WriteString("\n") } // Response fields table outFields := inspectFields(op.OutType, docFields(hasDoc, doc)) if len(outFields) > 0 { p.WriteString("## Response Fields\n\n") p.WriteString("| Field | Type | Description |\n") p.WriteString("| :--- | :--- | :--- |\n") for _, f := range outFields { desc := f.Doc if desc == "" { desc = "-" } fmt.Fprintf(&p, "| `%s` | `$%s` | %s |\n", f.WireName, f.Type, desc) } p.WriteString("\n") } // Examples if hasDoc && (len(doc.Example) > 0 || len(doc.Response) > 0) { p.WriteString("## Examples\n\n") if len(doc.Example) > 0 { p.WriteString("### Request Body\n\n```json\n") p.WriteString(string(doc.Example)) p.WriteString("\n```\n\n") } if len(doc.Response) > 0 { p.WriteString("### Response Body\n\n```json\n") p.WriteString(string(doc.Response)) p.WriteString("\n```\n\n") } } // Tabbed SDK invocation in Go, Rust, C++ goMethod := exportIdent(id) rustMethod := snakeCase(id) cppMethod := snakeCase(id) p.WriteString("## SDK Examples\n\n") p.WriteString("\n") // Go Tab p.WriteString(" \n```go\n") fmt.Fprintf(&p, "client, err := sdk.Dial(\"127.0.0.1:9630\")\n") fmt.Fprintf(&p, "if err != nil {\n\tlog.Fatal(err)\n}\n") if op.InType != nil && deref(op.InType).NumField() > 0 { fmt.Fprintf(&p, "res, err := client.%s(ctx, &sdk.%s{\n\t// ...\n})\n", goMethod, exportIdent(typeName(op.InType))) } else { fmt.Fprintf(&p, "res, err := client.%s(ctx)\n", goMethod) } p.WriteString("```\n \n") // Rust Tab p.WriteString(" \n```rust\n") fmt.Fprintf(&p, "let client = sdk::Client::new(\"127.0.0.1:9630\");\n") if op.InType != nil && deref(op.InType).NumField() > 0 { fmt.Fprintf(&p, "let res = client.%s(&%s { ..Default::default() }).await?;\n", rustMethod, exportIdent(typeName(op.InType))) } else { fmt.Fprintf(&p, "let res = client.%s().await?;\n", rustMethod) } p.WriteString("```\n \n") // C++ Tab p.WriteString(" \n```cpp\n") fmt.Fprintf(&p, "auto client = sdk::create_client(\"127.0.0.1:9630\");\n") if op.InType != nil && deref(op.InType).NumField() > 0 { fmt.Fprintf(&p, "%s req{};\nauto res = client->%s(req);\n", exportIdent(typeName(op.InType)), cppMethod) } else { fmt.Fprintf(&p, "auto res = client->%s();\n", cppMethod) } p.WriteString("```\n \n") p.WriteString("\n") bundle.Pages[filename] = p.String() } bundle.Index = index.String() return bundle, nil } type fieldInfo struct { Name string WireName string Type string Required bool Doc string } func inspectFields(t reflect.Type, docs map[string]string) []fieldInfo { t = deref(t) if t == nil || t.Kind() != reflect.Struct { return nil } var res []fieldInfo for i := 0; i < t.NumField(); i++ { f := t.Field(i) if !f.IsExported() { continue } wire := jsonFieldName(f) doc := docs[typeName(t)+"."+wire] required := strings.Contains(f.Tag.Get("validate"), "required") res = append(res, fieldInfo{ Name: f.Name, WireName: wire, Type: f.Type.String(), Required: required, Doc: doc, }) } return res }