func parseStructTag(tag string) map[string]string {
	re := regexp.MustCompile("^([^:\"]+):\"([^\"]*)\" *(.*)")
	result := map[string]string{}
	if tag[0] != '`' || tag[len(tag)-1] != '`' {
		panic(fmt.Sprintf("Expected tag to begin and end with `, got: \"%s\"", tag))
	}
	parseStructTagInner(re, result, tag[1:len(tag)-1])
	return result
}
parseStructTag parses a string like

`config:"ReadTimeoutSec" default:"3" metric:"sec"`

into a map of tag names and values
package main
import (
	"fmt"
	"go/ast"
	"go/token"
	"regexp"
)
type GollumPlugin struct {
	Pkg  string
	Name string
	Comment string
	Embeds []typeEmbed
	StructTagParams DefinitionList
}
func findGollumPlugins(pkgRoot *ast.Package) []GollumPlugin {
	tree := NewTree(pkgRoot)
	pattern := PatternNode{
		Comparison: "*ast.GenDecl",
		Children: []PatternNode{
			{
				Comparison: "*ast.CommentGroup",
			},
			{
				Comparison: "*ast.TypeSpec",
				Children: []PatternNode{
					{
						Comparison: "*ast.Ident",
						Callback: func(astNode ast.Node) bool {
							return ast.IsExported(astNode.(*ast.Ident).Name)
						},
					},
					{
						Comparison: "*ast.StructType",
						Children: []PatternNode{
							{
								Comparison: "*ast.FieldList",
							},
						},
					},
				},
			},
		},
	}
	results := []GollumPlugin{}
	for _, genDecl := range tree.Search(pattern) {
		pst := GollumPlugin{
			Pkg: pkgRoot.Name,
			Name:    genDecl.Children[1].Children[0].AstNode.(*ast.Ident).Name,
			Comment: genDecl.Children[0].AstNode.(*ast.CommentGroup).Text(),
			Embeds: getGollumPluginEmbedList(pkgRoot.Name,
				genDecl.Children[1].Children[1].Children[0].AstNode.(*ast.FieldList),
			),
			StructTagParams: getGollumPluginConfigParams(
				genDecl.Children[1].Children[1].Children[0].AstNode.(*ast.FieldList),
			),
		}
		results = append(results, pst)
	}
	return results
}
func getGollumPluginEmbedList(packageName string, fieldList *ast.FieldList) []typeEmbed {
	results := []typeEmbed{}
	for _, field := range fieldList.List {
		if field.Tag == nil ||
			field.Tag.Kind != token.STRING ||
			field.Tag.Value != embedTag {
			continue
		}
		switch t := field.Type.(type) {
		case *ast.Ident:
			results = append(results, typeEmbed{
				pkg:  packageName,
				name: field.Type.(*ast.Ident).Name,
			})
		case *ast.SelectorExpr:
			sel := field.Type.(*ast.SelectorExpr)
			results = append(results, typeEmbed{
				pkg:  sel.X.(*ast.Ident).Name,
				name: sel.Sel.Name,
			})
		default:
			_ = t
			fmt.Printf("WARNING: struct tag %s in unexpected location\n", embedTag)
			continue
		}
	}
	return results
}
func getGollumPluginConfigParams(fieldList *ast.FieldList) DefinitionList {
	results := DefinitionList{}
	for _, field := range fieldList.List {
		if field.Tag == nil || field.Tag.Kind != token.STRING {
			continue
		}
		tags := parseStructTag(field.Tag.Value)
		if paramName, found := tags["config"]; found {
			results.add(&Definition{
				name: paramName,
				desc: "(no documentation available)",
				dfl:  tags["default"],
				unit: tags["metric"],
			})
		}
	}
	return results
}