mirror of
https://github.com/mcuadros/ascode
synced 2024-05-09 00:56:14 +02:00
226 lines
5.1 KiB
Go
226 lines
5.1 KiB
Go
package doc
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/b5/outline/lib"
|
|
"github.com/go-git/go-git/v5"
|
|
"github.com/go-git/go-git/v5/plumbing/object"
|
|
"github.com/go-git/go-git/v5/storage/memory"
|
|
"github.com/hashicorp/terraform/configs/configschema"
|
|
"github.com/hashicorp/terraform/providers"
|
|
"github.com/mcuadros/ascode/starlark/types"
|
|
)
|
|
|
|
type ResourceDocumentation struct {
|
|
Name string
|
|
Type string
|
|
Attribs map[string]string
|
|
Blocks map[string]map[string]string
|
|
}
|
|
|
|
func NewResourceDocumentation(typ, name string) *ResourceDocumentation {
|
|
return &ResourceDocumentation{
|
|
Name: name,
|
|
Type: typ,
|
|
Attribs: make(map[string]string, 0),
|
|
Blocks: make(map[string]map[string]string, 0),
|
|
}
|
|
}
|
|
|
|
var re = regexp.MustCompile(`\*[. ][\x60](.*)[\x60].*\) (.*)`)
|
|
|
|
// https://regex101.com/r/hINfBI/2
|
|
var blockRe = regexp.MustCompile(`^[^\*].*\x60(.*)\x60 block(?:s?)`)
|
|
|
|
func (r *ResourceDocumentation) Decode(doc io.Reader) error {
|
|
var block string
|
|
|
|
buf := bufio.NewReader(doc)
|
|
for {
|
|
line, err := buf.ReadString('\n')
|
|
if err == io.EOF {
|
|
return nil
|
|
}
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
line = strings.TrimSpace(line)
|
|
if len(line) == 0 {
|
|
continue
|
|
}
|
|
|
|
parts := re.FindStringSubmatch(line)
|
|
if len(parts) == 3 {
|
|
if block == "" {
|
|
r.AddAttrib(parts[1], parts[2])
|
|
continue
|
|
}
|
|
|
|
r.AddBlockAttrib(block, parts[1], parts[2])
|
|
continue
|
|
}
|
|
|
|
parts = blockRe.FindStringSubmatch(line)
|
|
if len(parts) == 2 {
|
|
block = parts[1]
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *ResourceDocumentation) AddAttrib(name, desc string) {
|
|
r.Attribs[name] = desc
|
|
}
|
|
|
|
func (r *ResourceDocumentation) AddBlockAttrib(block, name, desc string) {
|
|
if _, ok := r.Blocks[block]; !ok {
|
|
r.Blocks[block] = make(map[string]string, 0)
|
|
}
|
|
|
|
r.Blocks[block][name] = desc
|
|
}
|
|
|
|
type Documentation struct {
|
|
name string
|
|
repository *git.Repository
|
|
head *object.Commit
|
|
resources map[string]map[string]string
|
|
}
|
|
|
|
func NewDocumentation(name string) (*Documentation, error) {
|
|
d := &Documentation{
|
|
name: name,
|
|
resources: make(map[string]map[string]string, 0),
|
|
}
|
|
|
|
return d, d.initRepository()
|
|
}
|
|
|
|
func (d *Documentation) initRepository() error {
|
|
storer := memory.NewStorage()
|
|
|
|
var err error
|
|
d.repository, err = git.Clone(storer, nil, &git.CloneOptions{
|
|
URL: fmt.Sprintf("https://github.com/terraform-providers/terraform-provider-%s.git", d.name),
|
|
Depth: 1,
|
|
|
|
// as git does, when you make a clone, pull or some other operations the
|
|
// server sends information via the sideband, this information can being
|
|
// collected provinding a io.Writer to the CloneOptions options
|
|
Progress: os.Stdout,
|
|
})
|
|
|
|
h, err := d.repository.Head()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
d.head, err = d.repository.CommitObject(h.Hash())
|
|
return err
|
|
}
|
|
|
|
func (d *Documentation) Resource(typ, name string) (*ResourceDocumentation, error) {
|
|
parts := strings.SplitN(name, "_", 2)
|
|
name = parts[1]
|
|
|
|
filename := fmt.Sprintf("website/docs/%s/%s.html.md", typ, name)
|
|
|
|
file, err := d.head.File(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
r, err := file.Reader()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resource := NewResourceDocumentation(typ, name)
|
|
return resource, resource.Decode(r)
|
|
}
|
|
|
|
func (d *Documentation) Do(name string, schema providers.GetSchemaResponse) *lib.Doc {
|
|
doc := &lib.Doc{}
|
|
doc.Name = name
|
|
doc.Path = name
|
|
|
|
for name, schema := range schema.DataSources {
|
|
doc.Types = append(doc.Types, d.schemaToDoc(name, &schema)...)
|
|
}
|
|
|
|
return doc
|
|
}
|
|
|
|
func (d *Documentation) schemaToDoc(resource string, s *providers.Schema) []*lib.Type {
|
|
rd, err := d.Resource("d", resource)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
fmt.Println(resource, rd)
|
|
typ := &lib.Type{}
|
|
typ.Name = resource
|
|
|
|
for name, attr := range s.Block.Attributes {
|
|
typ.Fields = append(typ.Fields, d.attributeToField(rd.Attribs, name, attr))
|
|
}
|
|
|
|
types := []*lib.Type{typ}
|
|
|
|
for name, block := range s.Block.BlockTypes {
|
|
types = append(types, d.blockToType(rd, resource, name, block))
|
|
typ.Fields = append(typ.Fields, d.blockToField(rd, resource, name, block))
|
|
}
|
|
|
|
return types
|
|
}
|
|
|
|
func (d *Documentation) blockToType(rd *ResourceDocumentation, resource, name string, block *configschema.NestedBlock) *lib.Type {
|
|
typ := &lib.Type{}
|
|
typ.Name = fmt.Sprintf("%s.%s", resource, name)
|
|
|
|
for n, attr := range block.Attributes {
|
|
//fmt.Println(rd.Blocks[name])
|
|
|
|
typ.Fields = append(typ.Fields, d.attributeToField(rd.Blocks[name], n, attr))
|
|
}
|
|
|
|
return typ
|
|
}
|
|
|
|
func (d *Documentation) blockToField(rd *ResourceDocumentation, resource, name string, block *configschema.NestedBlock) *lib.Field {
|
|
field := &lib.Field{}
|
|
nested := fmt.Sprintf("%s.%s", resource, name)
|
|
|
|
field.Name = name
|
|
if block.MaxItems != 1 {
|
|
field.Type = fmt.Sprintf("collection<%s>", nested)
|
|
} else {
|
|
field.Type = nested
|
|
}
|
|
|
|
return field
|
|
}
|
|
|
|
func (d *Documentation) attributeToField(doc map[string]string, name string, attr *configschema.Attribute) *lib.Field {
|
|
field := &lib.Field{}
|
|
field.Name = name
|
|
field.Description, _ = doc[name]
|
|
if attr.Computed && !attr.Optional {
|
|
field.Type = "computed"
|
|
} else {
|
|
field.Type = types.MustTypeFromCty(attr.Type).Starlark()
|
|
}
|
|
|
|
return field
|
|
}
|