docsonnet/fast/main.go

235 lines
4.8 KiB
Go
Raw Normal View History

package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"path/filepath"
"strings"
"time"
"github.com/google/go-jsonnet"
"github.com/sh0rez/docsonnet/pkg/docsonnet"
"github.com/sh0rez/docsonnet/pkg/render"
)
func main() {
data, err := eval()
if err != nil {
log.Fatalln(err)
}
// fmt.Println(string(data))
var d DS
if err := json.Unmarshal(data, &d); err != nil {
log.Fatalln(err)
}
pkg := load(d)
fmt.Println("render")
res := render.Render(pkg)
for k, v := range res {
fmt.Println(k)
if err := ioutil.WriteFile(filepath.Join("docs", k), []byte(v), 0644); err != nil {
log.Fatalln(err)
}
}
}
// load docsonnet
//
// Data assumptions:
// - only map[string]interface{} and docsonnet fields
// - docsonnet fields (#...) coming first
func load(d DS) docsonnet.Package {
start := time.Now()
pkg := d.Package()
fmt.Println("load", pkg.Name)
pkg.API = make(docsonnet.Fields)
pkg.Sub = make(map[string]docsonnet.Package)
for k, v := range d {
if k == "#" {
continue
}
f := v.(map[string]interface{})
// docsonnet field
name := strings.TrimPrefix(k, "#")
if strings.HasPrefix(k, "#") {
pkg.API[name] = loadField(name, f, d)
continue
}
// non-docsonnet
// subpackage?
if _, ok := f["#"]; ok {
p := load(DS(f))
pkg.Sub[p.Name] = p
continue
}
// non-annotated nested?
// try to load, but skip when already loaded as annotated above
if nested, ok := loadNested(name, f); ok && !fieldsHas(pkg.API, name) {
pkg.API[name] = *nested
continue
}
}
fmt.Println("done load", pkg.Name, time.Since(start))
return pkg
}
func fieldsHas(f docsonnet.Fields, key string) bool {
_, b := f[key]
return b
}
func loadNested(name string, msi map[string]interface{}) (*docsonnet.Field, bool) {
out := docsonnet.Object{
Name: name,
Fields: make(docsonnet.Fields),
}
ok := false
for k, v := range msi {
f := v.(map[string]interface{})
n := strings.TrimPrefix(k, "#")
if !strings.HasPrefix(k, "#") {
if l, ok := loadNested(k, f); ok {
out.Fields[n] = *l
}
continue
}
ok = true
l := loadField(n, f, msi)
out.Fields[n] = l
}
if !ok {
return nil, false
}
return &docsonnet.Field{Object: &out}, true
}
func loadField(name string, field map[string]interface{}, parent map[string]interface{}) docsonnet.Field {
if ifn, ok := field["function"]; ok {
return loadFn(name, ifn.(map[string]interface{}))
}
if iobj, ok := field["object"]; ok {
return loadObj(name, iobj.(map[string]interface{}), parent)
}
panic("docsonnet field lacking {function | object}")
}
func loadFn(name string, msi map[string]interface{}) docsonnet.Field {
fn := docsonnet.Function{
Name: name,
Help: msi["help"].(string),
}
if args, ok := msi["args"]; ok {
fn.Args = loadArgs(args.([]interface{}))
}
return docsonnet.Field{Function: &fn}
}
func loadArgs(is []interface{}) []docsonnet.Argument {
args := make([]docsonnet.Argument, len(is))
for i := range is {
arg := is[i].(map[string]interface{})
args[i] = docsonnet.Argument{
Name: arg["name"].(string),
Type: docsonnet.Type(arg["type"].(string)),
Default: arg["default"],
}
}
return args
}
func fieldNames(msi map[string]interface{}) []string {
out := make([]string, 0, len(msi))
for k := range msi {
out = append(out, k)
}
return out
}
func loadObj(name string, msi map[string]interface{}, parent map[string]interface{}) docsonnet.Field {
obj := docsonnet.Object{
Name: name,
Help: msi["help"].(string),
Fields: make(docsonnet.Fields),
}
// look for children in same key without #
var iChilds interface{}
var ok bool
if iChilds, ok = parent[name]; !ok {
fmt.Println("aborting, no", name, strings.Join(fieldNames(parent), ", "))
return docsonnet.Field{Object: &obj}
}
childs := iChilds.(map[string]interface{})
for k, v := range childs {
name := strings.TrimPrefix(k, "#")
f := v.(map[string]interface{})
if !strings.HasPrefix(k, "#") {
if l, ok := loadNested(k, f); ok {
obj.Fields[name] = *l
}
continue
}
obj.Fields[name] = loadField(name, f, childs)
}
return docsonnet.Field{Object: &obj}
}
type DS map[string]interface{}
func (d DS) Package() docsonnet.Package {
hash, ok := d["#"]
if !ok {
log.Fatalln("Package declaration missing")
}
pkg := hash.(map[string]interface{})
return docsonnet.Package{
Help: pkg["help"].(string),
Name: pkg["name"].(string),
Import: pkg["import"].(string),
}
}
func eval() ([]byte, error) {
fmt.Println("eval start")
start := time.Now()
vm := jsonnet.MakeVM()
vm.Importer(&jsonnet.FileImporter{JPaths: []string{".."}})
data, err := ioutil.ReadFile("fast.libsonnet")
if err != nil {
return nil, err
}
out, err := vm.EvaluateSnippet("fast.libsonnet", string(data))
if err != nil {
return nil, err
}
fmt.Println("eval:", time.Since(start))
return []byte(out), nil
}