We were ignoring quite a few error cases, and had one case where we weren't actually updating state where we wanted to. Unfortunately, if the linter doesn't pass, nobody has any reason to actually check lint failures in CI. There are three remaining XXXs marked in the code for lint.
380 lines
9.1 KiB
Go
380 lines
9.1 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"reflect"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
"unicode"
|
|
|
|
"github.com/filecoin-project/go-address"
|
|
"github.com/filecoin-project/go-bitfield"
|
|
"github.com/filecoin-project/go-jsonrpc/auth"
|
|
"github.com/filecoin-project/lotus/api"
|
|
"github.com/filecoin-project/lotus/api/apistruct"
|
|
"github.com/filecoin-project/lotus/build"
|
|
"github.com/filecoin-project/lotus/chain/types"
|
|
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
|
"github.com/filecoin-project/specs-actors/actors/abi"
|
|
"github.com/filecoin-project/specs-actors/actors/crypto"
|
|
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
|
|
"github.com/ipfs/go-cid"
|
|
"github.com/ipfs/go-filestore"
|
|
"github.com/libp2p/go-libp2p-core/network"
|
|
"github.com/libp2p/go-libp2p-core/peer"
|
|
"github.com/multiformats/go-multiaddr"
|
|
)
|
|
|
|
var ExampleValues = map[reflect.Type]interface{}{
|
|
reflect.TypeOf(auth.Permission("")): auth.Permission("write"),
|
|
reflect.TypeOf(""): "string value",
|
|
reflect.TypeOf(uint64(42)): uint64(42),
|
|
reflect.TypeOf(byte(7)): byte(7),
|
|
reflect.TypeOf([]byte{}): []byte("byte array"),
|
|
}
|
|
|
|
func addExample(v interface{}) {
|
|
ExampleValues[reflect.TypeOf(v)] = v
|
|
}
|
|
|
|
func init() {
|
|
c, err := cid.Decode("bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
ExampleValues[reflect.TypeOf(c)] = c
|
|
|
|
c2, err := cid.Decode("bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
tsk := types.NewTipSetKey(c, c2)
|
|
|
|
ExampleValues[reflect.TypeOf(tsk)] = tsk
|
|
|
|
addr, err := address.NewIDAddress(1234)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
ExampleValues[reflect.TypeOf(addr)] = addr
|
|
|
|
pid, err := peer.Decode("12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
addExample(pid)
|
|
|
|
addExample(bitfield.NewFromSet([]uint64{5}))
|
|
addExample(abi.RegisteredSealProof_StackedDrg32GiBV1)
|
|
addExample(abi.RegisteredPoStProof_StackedDrgWindow32GiBV1)
|
|
addExample(abi.ChainEpoch(10101))
|
|
addExample(crypto.SigTypeBLS)
|
|
addExample(int64(9))
|
|
addExample(12.3)
|
|
addExample(123)
|
|
addExample(uintptr(0))
|
|
addExample(abi.MethodNum(1))
|
|
addExample(exitcode.ExitCode(0))
|
|
addExample(crypto.DomainSeparationTag_ElectionProofProduction)
|
|
addExample(true)
|
|
addExample(abi.UnpaddedPieceSize(1024))
|
|
addExample(abi.UnpaddedPieceSize(1024).Padded())
|
|
addExample(abi.DealID(5432))
|
|
addExample(filestore.StatusFileChanged)
|
|
addExample(abi.SectorNumber(9))
|
|
addExample(abi.SectorSize(32 * 1024 * 1024 * 1024))
|
|
addExample(api.MpoolChange(0))
|
|
addExample(network.Connected)
|
|
addExample(dtypes.NetworkName("lotus"))
|
|
addExample(api.SyncStateStage(1))
|
|
addExample(build.APIVersion)
|
|
addExample(api.PCHInbound)
|
|
addExample(time.Minute)
|
|
addExample(&types.ExecutionTrace{
|
|
Msg: exampleValue(reflect.TypeOf(&types.Message{}), nil).(*types.Message),
|
|
MsgRct: exampleValue(reflect.TypeOf(&types.MessageReceipt{}), nil).(*types.MessageReceipt),
|
|
})
|
|
addExample(map[string]types.Actor{
|
|
"t01236": exampleValue(reflect.TypeOf(types.Actor{}), nil).(types.Actor),
|
|
})
|
|
addExample(map[string]api.MarketDeal{
|
|
"t026363": exampleValue(reflect.TypeOf(api.MarketDeal{}), nil).(api.MarketDeal),
|
|
})
|
|
addExample(map[string]api.MarketBalance{
|
|
"t026363": exampleValue(reflect.TypeOf(api.MarketBalance{}), nil).(api.MarketBalance),
|
|
})
|
|
|
|
maddr, err := multiaddr.NewMultiaddr("/ip4/52.36.61.156/tcp/1347/p2p/12D3KooWFETiESTf1v4PGUvtnxMAcEFMzLZbJGg4tjWfGEimYior")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// because reflect.TypeOf(maddr) returns the concrete type...
|
|
ExampleValues[reflect.TypeOf(struct{ A multiaddr.Multiaddr }{}).Field(0).Type] = maddr
|
|
|
|
}
|
|
|
|
func exampleValue(t, parent reflect.Type) interface{} {
|
|
v, ok := ExampleValues[t]
|
|
if ok {
|
|
return v
|
|
}
|
|
|
|
switch t.Kind() {
|
|
case reflect.Slice:
|
|
out := reflect.New(t).Elem()
|
|
reflect.Append(out, reflect.ValueOf(exampleValue(t.Elem(), t)))
|
|
return out.Interface()
|
|
case reflect.Chan:
|
|
return exampleValue(t.Elem(), nil)
|
|
case reflect.Struct:
|
|
es := exampleStruct(t, parent)
|
|
v := reflect.ValueOf(es).Elem().Interface()
|
|
ExampleValues[t] = v
|
|
return v
|
|
case reflect.Array:
|
|
out := reflect.New(t).Elem()
|
|
for i := 0; i < t.Len(); i++ {
|
|
out.Index(i).Set(reflect.ValueOf(exampleValue(t.Elem(), t)))
|
|
}
|
|
return out.Interface()
|
|
|
|
case reflect.Ptr:
|
|
if t.Elem().Kind() == reflect.Struct {
|
|
es := exampleStruct(t.Elem(), t)
|
|
//ExampleValues[t] = es
|
|
return es
|
|
}
|
|
case reflect.Interface:
|
|
return struct{}{}
|
|
}
|
|
|
|
panic(fmt.Sprintf("No example value for type: %s", t))
|
|
}
|
|
|
|
func exampleStruct(t, parent reflect.Type) interface{} {
|
|
ns := reflect.New(t)
|
|
for i := 0; i < t.NumField(); i++ {
|
|
f := t.Field(i)
|
|
if f.Type == parent {
|
|
continue
|
|
}
|
|
if strings.Title(f.Name) == f.Name {
|
|
ns.Elem().Field(i).Set(reflect.ValueOf(exampleValue(f.Type, t)))
|
|
}
|
|
}
|
|
|
|
return ns.Interface()
|
|
}
|
|
|
|
type Visitor struct {
|
|
Methods map[string]ast.Node
|
|
}
|
|
|
|
func (v *Visitor) Visit(node ast.Node) ast.Visitor {
|
|
st, ok := node.(*ast.TypeSpec)
|
|
if !ok {
|
|
return v
|
|
}
|
|
|
|
if st.Name.Name != "FullNode" {
|
|
return nil
|
|
}
|
|
|
|
iface := st.Type.(*ast.InterfaceType)
|
|
for _, m := range iface.Methods.List {
|
|
if len(m.Names) > 0 {
|
|
v.Methods[m.Names[0].Name] = m
|
|
}
|
|
}
|
|
|
|
return v
|
|
}
|
|
|
|
const noComment = "There are not yet any comments for this method."
|
|
|
|
func parseApiASTInfo() (map[string]string, map[string]string) { //nolint:golint
|
|
fset := token.NewFileSet()
|
|
pkgs, err := parser.ParseDir(fset, "./api", nil, parser.AllErrors|parser.ParseComments)
|
|
if err != nil {
|
|
fmt.Println("parse error: ", err)
|
|
}
|
|
|
|
ap := pkgs["api"]
|
|
|
|
f := ap.Files["api/api_full.go"]
|
|
|
|
cmap := ast.NewCommentMap(fset, f, f.Comments)
|
|
|
|
v := &Visitor{make(map[string]ast.Node)}
|
|
ast.Walk(v, pkgs["api"])
|
|
|
|
groupDocs := make(map[string]string)
|
|
out := make(map[string]string)
|
|
for mn, node := range v.Methods {
|
|
cs := cmap.Filter(node).Comments()
|
|
if len(cs) == 0 {
|
|
out[mn] = noComment
|
|
} else {
|
|
for _, c := range cs {
|
|
if strings.HasPrefix(c.Text(), "MethodGroup:") {
|
|
parts := strings.Split(c.Text(), "\n")
|
|
groupName := strings.TrimSpace(parts[0][12:])
|
|
comment := strings.Join(parts[1:], "\n")
|
|
groupDocs[groupName] = comment
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
last := cs[len(cs)-1].Text()
|
|
if !strings.HasPrefix(last, "MethodGroup:") {
|
|
out[mn] = last
|
|
} else {
|
|
out[mn] = noComment
|
|
}
|
|
}
|
|
}
|
|
return out, groupDocs
|
|
}
|
|
|
|
type MethodGroup struct {
|
|
GroupName string
|
|
Header string
|
|
Methods []*Method
|
|
}
|
|
|
|
type Method struct {
|
|
Comment string
|
|
Name string
|
|
InputExample string
|
|
ResponseExample string
|
|
}
|
|
|
|
func methodGroupFromName(mn string) string {
|
|
i := strings.IndexFunc(mn[1:], func(r rune) bool {
|
|
return unicode.IsUpper(r)
|
|
})
|
|
if i < 0 {
|
|
return ""
|
|
}
|
|
return mn[:i+1]
|
|
}
|
|
|
|
func main() {
|
|
|
|
comments, groupComments := parseApiASTInfo()
|
|
|
|
groups := make(map[string]*MethodGroup)
|
|
|
|
var api struct{ api.FullNode }
|
|
t := reflect.TypeOf(api)
|
|
for i := 0; i < t.NumMethod(); i++ {
|
|
m := t.Method(i)
|
|
|
|
groupName := methodGroupFromName(m.Name)
|
|
|
|
g, ok := groups[groupName]
|
|
if !ok {
|
|
g = new(MethodGroup)
|
|
g.Header = groupComments[groupName]
|
|
g.GroupName = groupName
|
|
groups[groupName] = g
|
|
}
|
|
|
|
var args []interface{}
|
|
ft := m.Func.Type()
|
|
for j := 2; j < ft.NumIn(); j++ {
|
|
inp := ft.In(j)
|
|
args = append(args, exampleValue(inp, nil))
|
|
}
|
|
|
|
v, err := json.MarshalIndent(args, "", " ")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
outv := exampleValue(ft.Out(0), nil)
|
|
|
|
ov, err := json.MarshalIndent(outv, "", " ")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
g.Methods = append(g.Methods, &Method{
|
|
Name: m.Name,
|
|
Comment: comments[m.Name],
|
|
InputExample: string(v),
|
|
ResponseExample: string(ov),
|
|
})
|
|
}
|
|
|
|
var groupslice []*MethodGroup
|
|
for _, g := range groups {
|
|
groupslice = append(groupslice, g)
|
|
}
|
|
|
|
sort.Slice(groupslice, func(i, j int) bool {
|
|
return groupslice[i].GroupName < groupslice[j].GroupName
|
|
})
|
|
|
|
fmt.Printf("# Groups\n")
|
|
|
|
for _, g := range groupslice {
|
|
fmt.Printf("* [%s](#%s)\n", g.GroupName, g.GroupName)
|
|
for _, method := range g.Methods {
|
|
fmt.Printf(" * [%s](#%s)\n", method.Name, method.Name)
|
|
}
|
|
}
|
|
|
|
permStruct := reflect.TypeOf(apistruct.FullNodeStruct{}.Internal)
|
|
commonPermStruct := reflect.TypeOf(apistruct.CommonStruct{}.Internal)
|
|
|
|
for _, g := range groupslice {
|
|
g := g
|
|
fmt.Printf("## %s\n", g.GroupName)
|
|
fmt.Printf("%s\n\n", g.Header)
|
|
|
|
sort.Slice(g.Methods, func(i, j int) bool {
|
|
return g.Methods[i].Name < g.Methods[j].Name
|
|
})
|
|
|
|
for _, m := range g.Methods {
|
|
fmt.Printf("### %s\n", m.Name)
|
|
fmt.Printf("%s\n\n", m.Comment)
|
|
|
|
meth, ok := permStruct.FieldByName(m.Name)
|
|
if !ok {
|
|
meth, ok = commonPermStruct.FieldByName(m.Name)
|
|
if !ok {
|
|
panic("no perms for method: " + m.Name)
|
|
}
|
|
}
|
|
|
|
perms := meth.Tag.Get("perm")
|
|
|
|
fmt.Printf("Perms: %s\n\n", perms)
|
|
|
|
if strings.Count(m.InputExample, "\n") > 0 {
|
|
fmt.Printf("Inputs:\n```json\n%s\n```\n\n", m.InputExample)
|
|
} else {
|
|
fmt.Printf("Inputs: `%s`\n\n", m.InputExample)
|
|
}
|
|
|
|
if strings.Count(m.ResponseExample, "\n") > 0 {
|
|
fmt.Printf("Response:\n```json\n%s\n```\n\n", m.ResponseExample)
|
|
} else {
|
|
fmt.Printf("Response: `%s`\n\n", m.ResponseExample)
|
|
}
|
|
}
|
|
}
|
|
}
|