Created
May 27, 2024 09:13
-
-
Save Splizard/1d75d2efa397dcf9d0e2389afc805f3d to your computer and use it in GitHub Desktop.
Self-hosted runtime API documentation in Go.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"reflect" | |
"runtime" | |
"strings" | |
"runtime.link/api" | |
"runtime.link/api/rest" | |
) | |
type API struct { | |
api.Specification | |
Echo func(Message) Message `rest:"POST /echo" | |
echos back the input string.` | |
GOOS func() string `rest:"GET /goos" | |
returns the operating system of the server.` | |
Docs func() []Endpoint `rest:"GET /docs" | |
returns self-hosted documentation for the API` | |
} | |
type Message struct { | |
Text string `json:"text" | |
content of the message, should be human readable.` | |
} | |
type Endpoint struct { | |
Method string `json:"method" | |
is the HTTP method of the endpoint` | |
Path string `json:"path" | |
is the URL path of the endpoint` | |
Docs string `json:"docs" | |
is the human readable description of the endpoint` | |
Request any `json:"request" | |
that each endpoint accepts, including their data type and whether they are required or optional` | |
Response any `json:"response" | |
that represents the data type of the response` | |
} | |
func makeDocumentationFor(rtype reflect.Type, docs string) any { | |
switch rtype.Kind() { | |
case reflect.Struct: | |
var fields = make(map[string]any) | |
for i := 0; i < rtype.NumField(); i++ { | |
field := rtype.Field(i) | |
jsonName, _, _ := strings.Cut(string(field.Tag.Get("json")), ",") | |
_, docs, _ := strings.Cut(string(field.Tag), "\n") | |
docs = strings.Replace(docs, "\t", "", -1) | |
fields[jsonName] = makeDocumentationFor(field.Type, docs) | |
} | |
return fields | |
case reflect.Slice: | |
return []any{makeDocumentationFor(rtype.Elem(), docs)} | |
default: | |
return rtype.Kind().String() + " - " + docs | |
} | |
} | |
func main() { | |
rest.ListenAndServe(":8080", nil, API{ | |
Echo: func(msg Message) Message { return msg }, | |
GOOS: func() string { return runtime.GOOS }, | |
Docs: func() []Endpoint { | |
spec := api.StructureOf(API{}) | |
docs := make([]Endpoint, 0, len(spec.Functions)) | |
for _, fn := range spec.Functions { | |
tag := fn.Tags.Get("rest") | |
method, path, _ := strings.Cut(tag, " ") | |
var endpoint = Endpoint{ | |
Method: method, | |
Path: path, | |
Docs: fn.Docs, | |
} | |
if fn.Type.NumIn() > 0 { | |
endpoint.Request = makeDocumentationFor(fn.Type.In(0), "request") | |
} | |
if fn.Type.NumOut() > 0 { | |
endpoint.Response = makeDocumentationFor(fn.Type.Out(0), "response") | |
} | |
docs = append(docs, endpoint) | |
} | |
return docs | |
}, | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment