refactor(render): filewriting in render pkg

This commit is contained in:
sh0rez 2020-05-12 15:23:24 +02:00
parent f284bff4e5
commit 14e9fc8f3f
No known key found for this signature in database
GPG key ID: 87C71DF9F8181FF1
7 changed files with 68 additions and 285 deletions

View file

@ -1,234 +0,0 @@
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
}

31
main.go
View file

@ -1,10 +1,9 @@
package main package main
import ( import (
"encoding/json"
"fmt" "fmt"
"io/ioutil"
"log" "log"
"path/filepath"
"github.com/go-clix/cli" "github.com/go-clix/cli"
@ -20,19 +19,27 @@ func main() {
Short: "Utility to parse and transform Jsonnet code that uses the docsonnet extension", Short: "Utility to parse and transform Jsonnet code that uses the docsonnet extension",
} }
output := root.Flags().StringP("output", "o", "docs", "directory to write the .md files to") dir := root.Flags().StringP("output", "o", "docs", "directory to write the .md files to")
root.Run = func(cmd *cli.Command, args []string) error { outputMd := root.Flags().Bool("md", true, "render as markdown files")
pkg, err := docsonnet.Load(args[0]) outputJSON := root.Flags().Bool("json", false, "print loaded docsonnet as JSON")
if err != nil {
return err
}
data := render.Render(*pkg) root.Run = func(cmd *cli.Command, args []string) error {
for k, v := range data { file := args[0]
fmt.Println(k)
if err := ioutil.WriteFile(filepath.Join(*output, k), []byte(v), 0644); err != nil { switch {
case *outputJSON:
model, err := docsonnet.Load(file)
if err != nil {
return err return err
} }
data, err := json.MarshalIndent(model, "", " ")
if err != nil {
return err
}
fmt.Println(string(data))
case *outputMd:
return render.To(file, *dir)
} }
return nil return nil

View file

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"log" "log"
"strings" "strings"
"time"
) )
// load docsonnet // load docsonnet
@ -13,10 +12,7 @@ import (
// - only map[string]interface{} and fields // - only map[string]interface{} and fields
// - fields (#...) coming first // - fields (#...) coming first
func fastLoad(d DS) Package { func fastLoad(d DS) Package {
start := time.Now()
pkg := d.Package() pkg := d.Package()
fmt.Println("load", pkg.Name)
pkg.API = make(Fields) pkg.API = make(Fields)
pkg.Sub = make(map[string]Package) pkg.Sub = make(map[string]Package)
@ -51,7 +47,6 @@ func fastLoad(d DS) Package {
} }
} }
fmt.Println("done load", pkg.Name, time.Since(start))
return pkg return pkg
} }

View file

@ -33,13 +33,13 @@ func Load(filename string) (*Package, error) {
// invoke load.libsonnet // invoke load.libsonnet
vm.ExtCode("main", fmt.Sprintf(`(import "%s")`, filename)) vm.ExtCode("main", fmt.Sprintf(`(import "%s")`, filename))
log.Println("evaluating Jsonnet") log.Println("Evaluating Jsonnet")
data, err := vm.EvaluateSnippet("load.libsonnet", string(load)) data, err := vm.EvaluateSnippet("load.libsonnet", string(load))
if err != nil { if err != nil {
return nil, err return nil, err
} }
log.Println("parsing result") log.Println("Transforming result")
// parse the result // parse the result
var d DS var d DS
if err := json.Unmarshal([]byte(data), &d); err != nil { if err := json.Unmarshal([]byte(data), &d); err != nil {

32
pkg/render/fs.go Normal file
View file

@ -0,0 +1,32 @@
package render
import (
"io/ioutil"
"log"
"os"
"path/filepath"
"github.com/sh0rez/docsonnet/pkg/docsonnet"
)
func To(api, out string) error {
pkg, err := docsonnet.Load(api)
if err != nil {
return err
}
if err := os.MkdirAll(out, os.ModePerm); err != nil {
return err
}
log.Println("Rendering .md files")
data := Render(*pkg)
for k, v := range data {
if err := ioutil.WriteFile(filepath.Join(out, k), []byte(v), 0644); err != nil {
return err
}
}
log.Printf("Success! Rendered %v packages from '%s' to '%s'", len(data), api, out)
return nil
}

View file

@ -1,28 +0,0 @@
package render
import (
"github.com/sh0rez/docsonnet/pkg/docsonnet"
)
func Paths(pkg docsonnet.Package) map[string]docsonnet.Package {
p := paths(pkg)
return p
}
func paths(pkg docsonnet.Package) map[string]docsonnet.Package {
pkgs := make(map[string]docsonnet.Package)
pkgs[pkg.Name+".md"] = pkg
if len(pkg.Sub) == 0 {
return pkgs
}
for _, sub := range pkg.Sub {
for k, v := range paths(sub) {
v.Name = pkg.Name + "/" + k
pkgs[pkg.Name+"-"+k] = v
}
}
return pkgs
}

View file

@ -15,7 +15,6 @@ func Render(pkg docsonnet.Package) map[string]string {
} }
func render(pkg docsonnet.Package, parents []string, root bool) map[string]string { func render(pkg docsonnet.Package, parents []string, root bool) map[string]string {
fmt.Println("render", pkg.Name)
link := "/" + strings.Join(append(parents, pkg.Name), "/") link := "/" + strings.Join(append(parents, pkg.Name), "/")
if root { if root {
link = "/" link = "/"
@ -27,20 +26,32 @@ func render(pkg docsonnet.Package, parents []string, root bool) map[string]strin
"permalink": link, "permalink": link,
}), }),
md.Headline(1, "package "+pkg.Name), md.Headline(1, "package "+pkg.Name),
md.CodeBlock("jsonnet", fmt.Sprintf(`local %s = import "%s"`, pkg.Name, pkg.Import)),
md.Text(pkg.Help),
} }
if pkg.Import != "" {
elems = append(elems, md.CodeBlock("jsonnet", fmt.Sprintf(`local %s = import "%s"`, pkg.Name, pkg.Import)))
}
elems = append(elems, md.Text(pkg.Help))
if len(pkg.Sub) > 0 { if len(pkg.Sub) > 0 {
elems = append(elems, md.Headline(2, "Subpackages")) elems = append(elems, md.Headline(2, "Subpackages"))
var items []md.Elem
keys := make([]string, 0, len(pkg.Sub))
for _, s := range pkg.Sub { for _, s := range pkg.Sub {
keys = append(keys, s.Name)
}
sort.Strings(keys)
var items []md.Elem
for _, k := range keys {
s := pkg.Sub[k]
link := strings.Join(append(parents, pkg.Name, s.Name), "-") link := strings.Join(append(parents, pkg.Name, s.Name), "-")
if root { if root {
link = strings.Join(append(parents, s.Name), "-") link = strings.Join(append(parents, s.Name), "-")
} }
items = append(items, md.Link(md.Text(s.Name), link+".md")) items = append(items, md.Link(md.Text(s.Name), link+".md"))
} }
elems = append(elems, md.List(items...)) elems = append(elems, md.List(items...))
} }