mirror of
				https://github.com/containers/podman.git
				synced 2025-11-04 08:56:05 +08:00 
			
		
		
		
	the varlink doc generator was ignoring all nullable types when generating its documentation Signed-off-by: baude <bbaude@redhat.com>
		
			
				
	
	
		
			277 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			277 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package main
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"fmt"
 | 
						|
	"io/ioutil"
 | 
						|
	"os"
 | 
						|
	"sort"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/varlink/go/varlink/idl"
 | 
						|
)
 | 
						|
 | 
						|
func readFileToString(path string) (string, error) {
 | 
						|
	content, err := ioutil.ReadFile(path)
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
	return string(content), nil
 | 
						|
}
 | 
						|
 | 
						|
func exit(err error) {
 | 
						|
	fmt.Println(err.Error())
 | 
						|
	os.Exit(1)
 | 
						|
}
 | 
						|
 | 
						|
func typeToString(input *idl.Type) string {
 | 
						|
	switch input.Kind {
 | 
						|
	case idl.TypeString:
 | 
						|
		return "string"
 | 
						|
	case idl.TypeBool:
 | 
						|
		return "bool"
 | 
						|
	case idl.TypeFloat:
 | 
						|
		return "float"
 | 
						|
	case idl.TypeArray:
 | 
						|
		result := input.ElementType.Alias
 | 
						|
		if result == "" {
 | 
						|
			return fmt.Sprintf("[]%s", typeToString(input.ElementType))
 | 
						|
		}
 | 
						|
		return result
 | 
						|
	case idl.TypeAlias:
 | 
						|
		return input.Alias
 | 
						|
	case idl.TypeMap:
 | 
						|
		return "map[string]"
 | 
						|
	case idl.TypeInt:
 | 
						|
		return "int"
 | 
						|
	case idl.TypeMaybe:
 | 
						|
		return fmt.Sprintf("?%s", typeToString(input.ElementType))
 | 
						|
	}
 | 
						|
	return ""
 | 
						|
}
 | 
						|
 | 
						|
func typeToLink(input string) string {
 | 
						|
	switch input {
 | 
						|
	case "string":
 | 
						|
		return "https://godoc.org/builtin#string"
 | 
						|
	case "int":
 | 
						|
		return "https://godoc.org/builtin#int"
 | 
						|
	case "bool":
 | 
						|
		return "https://godoc.org/builtin#bool"
 | 
						|
	case "float":
 | 
						|
		return "https://golang.org/src/builtin/builtin.go#L58"
 | 
						|
	default:
 | 
						|
		return fmt.Sprintf("#%s", input)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type funcArgs struct {
 | 
						|
	paramName string
 | 
						|
	paramKind string
 | 
						|
}
 | 
						|
type funcDescriber struct {
 | 
						|
	Name         string
 | 
						|
	inputParams  []funcArgs
 | 
						|
	returnParams []funcArgs
 | 
						|
	doc          string
 | 
						|
}
 | 
						|
 | 
						|
type funcSorter []funcDescriber
 | 
						|
 | 
						|
func (a funcSorter) Len() int           { return len(a) }
 | 
						|
func (a funcSorter) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
 | 
						|
func (a funcSorter) Less(i, j int) bool { return a[i].Name < a[j].Name }
 | 
						|
 | 
						|
type typeAttrs struct {
 | 
						|
	Name     string
 | 
						|
	AttrType string
 | 
						|
}
 | 
						|
type typeDescriber struct {
 | 
						|
	Name  string
 | 
						|
	doc   string
 | 
						|
	Attrs []typeAttrs
 | 
						|
}
 | 
						|
 | 
						|
type typeSorter []typeDescriber
 | 
						|
 | 
						|
func (a typeSorter) Len() int           { return len(a) }
 | 
						|
func (a typeSorter) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
 | 
						|
func (a typeSorter) Less(i, j int) bool { return a[i].Name < a[j].Name }
 | 
						|
 | 
						|
type err struct {
 | 
						|
	Name string
 | 
						|
	doc  string
 | 
						|
}
 | 
						|
 | 
						|
type errorSorter []err
 | 
						|
 | 
						|
func (a errorSorter) Len() int           { return len(a) }
 | 
						|
func (a errorSorter) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
 | 
						|
func (a errorSorter) Less(i, j int) bool { return a[i].Name < a[j].Name }
 | 
						|
 | 
						|
// collects defined types in the idl
 | 
						|
func getTypes(tidl *idl.IDL) []typeDescriber {
 | 
						|
	var types []typeDescriber
 | 
						|
	for _, x := range tidl.Aliases {
 | 
						|
		i := typeDescriber{
 | 
						|
			Name: x.Name,
 | 
						|
			doc:  x.Doc,
 | 
						|
		}
 | 
						|
		ta := []typeAttrs{}
 | 
						|
		for _, y := range x.Type.Fields {
 | 
						|
			result := typeToString(y.Type)
 | 
						|
			ta = append(ta, typeAttrs{Name: y.Name, AttrType: result})
 | 
						|
		}
 | 
						|
		i.Attrs = ta
 | 
						|
		types = append(types, i)
 | 
						|
	}
 | 
						|
	return types
 | 
						|
}
 | 
						|
 | 
						|
// collects defined methods in the idl
 | 
						|
func getMethods(midl *idl.IDL) []funcDescriber {
 | 
						|
	var methods []funcDescriber
 | 
						|
	for _, t := range midl.Methods {
 | 
						|
		m := funcDescriber{
 | 
						|
			Name: t.Name,
 | 
						|
			doc:  t.Doc,
 | 
						|
		}
 | 
						|
		fa := []funcArgs{}
 | 
						|
		fo := []funcArgs{}
 | 
						|
 | 
						|
		for _, i := range t.In.Fields {
 | 
						|
			fa = append(fa, funcArgs{paramName: i.Name, paramKind: typeToString(i.Type)})
 | 
						|
 | 
						|
		}
 | 
						|
		for _, f := range t.Out.Fields {
 | 
						|
			fo = append(fo, funcArgs{paramName: f.Name, paramKind: typeToString(f.Type)})
 | 
						|
		}
 | 
						|
		m.inputParams = fa
 | 
						|
		m.returnParams = fo
 | 
						|
		methods = append(methods, m)
 | 
						|
	}
 | 
						|
	return methods
 | 
						|
}
 | 
						|
 | 
						|
// collects defined errors in the idl
 | 
						|
func getErrors(midl *idl.IDL) []err {
 | 
						|
	var errors []err
 | 
						|
	for _, e := range midl.Errors {
 | 
						|
		myError := err{
 | 
						|
			Name: e.Name,
 | 
						|
			doc:  e.Doc,
 | 
						|
		}
 | 
						|
		errors = append(errors, myError)
 | 
						|
	}
 | 
						|
	return errors
 | 
						|
}
 | 
						|
 | 
						|
// generates the index for the top of the markdown page
 | 
						|
func generateIndex(methods []funcDescriber, types []typeDescriber, errors []err, b bytes.Buffer) bytes.Buffer {
 | 
						|
	// Sort the methods, types, and errors by alphabetical order
 | 
						|
	sort.Sort(funcSorter(methods))
 | 
						|
	sort.Sort(typeSorter(types))
 | 
						|
	sort.Sort(errorSorter(errors))
 | 
						|
 | 
						|
	for _, method := range methods {
 | 
						|
		var inArgs []string
 | 
						|
		var outArgs []string
 | 
						|
		for _, inArg := range method.inputParams {
 | 
						|
			inArgs = append(inArgs, fmt.Sprintf("%s: %s", inArg.paramName, inArg.paramKind))
 | 
						|
 | 
						|
		}
 | 
						|
		for _, outArg := range method.returnParams {
 | 
						|
			outArgs = append(outArgs, fmt.Sprintf("%s", outArg.paramKind))
 | 
						|
 | 
						|
		}
 | 
						|
		b.WriteString(fmt.Sprintf("\n[func %s(%s) %s](#%s)\n", method.Name, strings.Join(inArgs, ", "), strings.Join(outArgs, ", "), method.Name))
 | 
						|
	}
 | 
						|
	b.WriteString("\n")
 | 
						|
	for _, t := range types {
 | 
						|
		b.WriteString(fmt.Sprintf("[type %s](#%s)\n\n", t.Name, t.Name))
 | 
						|
	}
 | 
						|
	for _, e := range errors {
 | 
						|
		b.WriteString(fmt.Sprintf("[error %s](#%s)\n\n", e.Name, e.Name))
 | 
						|
	}
 | 
						|
	return b
 | 
						|
}
 | 
						|
 | 
						|
// performs the output for defined methods
 | 
						|
func generateFuncDescriptions(methods []funcDescriber, b bytes.Buffer) bytes.Buffer {
 | 
						|
	for _, method := range methods {
 | 
						|
		b.WriteString(fmt.Sprintf("### <a name=\"%s\"></a>func %s\n", method.Name, method.Name))
 | 
						|
		var inArgs []string
 | 
						|
		var outArgs []string
 | 
						|
		for _, inArg := range method.inputParams {
 | 
						|
			inArgs = append(inArgs, fmt.Sprintf("%s: [%s](%s)", inArg.paramName, inArg.paramKind, typeToLink(inArg.paramKind)))
 | 
						|
		}
 | 
						|
		for _, outArg := range method.returnParams {
 | 
						|
			outArgs = append(outArgs, fmt.Sprintf("[%s](%s)", outArg.paramKind, typeToLink(outArg.paramKind)))
 | 
						|
		}
 | 
						|
		b.WriteString(fmt.Sprintf("<div style=\"background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;\">\n\nmethod %s(%s) %s</div>", method.Name, strings.Join(inArgs, ", "), strings.Join(outArgs, ", ")))
 | 
						|
		b.WriteString("\n")
 | 
						|
		b.WriteString(method.doc)
 | 
						|
		b.WriteString("\n")
 | 
						|
	}
 | 
						|
	return b
 | 
						|
}
 | 
						|
 | 
						|
// performs the output for defined types/structs
 | 
						|
func generateTypeDescriptions(types []typeDescriber, b bytes.Buffer) bytes.Buffer {
 | 
						|
	for _, t := range types {
 | 
						|
		b.WriteString(fmt.Sprintf("### <a name=\"%s\"></a>type %s\n", t.Name, t.Name))
 | 
						|
		b.WriteString(fmt.Sprintf("\n%s\n", t.doc))
 | 
						|
		for _, i := range t.Attrs {
 | 
						|
			b.WriteString(fmt.Sprintf("\n%s [%s](%s)\n", i.Name, i.AttrType, typeToLink(i.AttrType)))
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return b
 | 
						|
}
 | 
						|
 | 
						|
// performs the output for defined errors
 | 
						|
func generateErrorDescriptions(errors []err, b bytes.Buffer) bytes.Buffer {
 | 
						|
	for _, e := range errors {
 | 
						|
		b.WriteString(fmt.Sprintf("### <a name=\"%s\"></a>type %s\n", e.Name, e.Name))
 | 
						|
		b.WriteString(fmt.Sprintf("\n%s\n", e.doc))
 | 
						|
	}
 | 
						|
	return b
 | 
						|
}
 | 
						|
 | 
						|
func main() {
 | 
						|
	args := os.Args
 | 
						|
	if len(args) < 2 {
 | 
						|
		exit(fmt.Errorf("you must provide an input and output path"))
 | 
						|
	}
 | 
						|
	varlinkFile := args[1]
 | 
						|
	mdFile := args[2]
 | 
						|
 | 
						|
	varlinkInput, err := readFileToString(varlinkFile)
 | 
						|
	if err != nil {
 | 
						|
		exit(err)
 | 
						|
	}
 | 
						|
	varlinkInput = strings.TrimRight(varlinkInput, "\n")
 | 
						|
 | 
						|
	// Run the idl parser
 | 
						|
	midl, err := idl.New(varlinkInput)
 | 
						|
	if err != nil {
 | 
						|
		exit(err)
 | 
						|
	}
 | 
						|
	// Collect up the info from the idl
 | 
						|
	methods := getMethods(midl)
 | 
						|
	types := getTypes(midl)
 | 
						|
	errors := getErrors(midl)
 | 
						|
 | 
						|
	out := bytes.Buffer{}
 | 
						|
	out.WriteString(fmt.Sprintf("# %s\n", midl.Name))
 | 
						|
	out.WriteString(fmt.Sprintf("%s\n", midl.Doc))
 | 
						|
	out.WriteString("## Index\n")
 | 
						|
	out = generateIndex(methods, types, errors, out)
 | 
						|
	out.WriteString("## Methods\n")
 | 
						|
	out = generateFuncDescriptions(methods, out)
 | 
						|
	out.WriteString("## Types\n")
 | 
						|
	out = generateTypeDescriptions(types, out)
 | 
						|
	out.WriteString("## Errors\n")
 | 
						|
	out = generateErrorDescriptions(errors, out)
 | 
						|
	ioutil.WriteFile(mdFile, out.Bytes(), 0755)
 | 
						|
}
 |