mirror of
https://github.com/terraform-docs/terraform-docs.git
synced 2026-03-27 12:58:35 +07:00
feat: Add support for XML renderer (#198)
* Add support for XML renderer * formatting * update docs
This commit is contained in:
@@ -2,8 +2,10 @@ package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"go/types"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -21,10 +23,9 @@ type Value interface {
|
||||
HasDefault() bool
|
||||
}
|
||||
|
||||
// ValueOf returns actual value of a variable
|
||||
// casted to 'Value' interface. This is done
|
||||
// to be able to attach specific marshaller func
|
||||
// to the type (if such a custom function was needed)
|
||||
// ValueOf returns actual value of a variable casted to 'Default' interface.
|
||||
// This is done to be able to attach specific marshaller func to the type
|
||||
// (if such a custom function was needed)
|
||||
func ValueOf(v interface{}) Value {
|
||||
if v == nil {
|
||||
return new(Nil)
|
||||
@@ -50,9 +51,8 @@ func ValueOf(v interface{}) Value {
|
||||
return new(Nil)
|
||||
}
|
||||
|
||||
// TypeOf returns Terraform type of a value
|
||||
// based on provided type by terraform-inspect
|
||||
// or by looking the underlying type of the value
|
||||
// TypeOf returns Terraform type of a value based on provided type by
|
||||
// terraform-inspect or by looking the underlying type of the value
|
||||
func TypeOf(t string, v interface{}) String {
|
||||
if t != "" {
|
||||
return String(t)
|
||||
@@ -74,9 +74,7 @@ func TypeOf(t string, v interface{}) String {
|
||||
return String("any")
|
||||
}
|
||||
|
||||
// Nil represents a 'nil' value which is
|
||||
// marshaled to `null` when empty for JSON
|
||||
// and YAML
|
||||
// Nil represents a 'nil' value which is marshaled to `null` when empty for JSON and YAML
|
||||
type Nil types.Nil
|
||||
|
||||
// HasDefault return false for Nil, because there's no value set for the variable
|
||||
@@ -84,21 +82,24 @@ func (n Nil) HasDefault() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// MarshalJSON custom marshal function which
|
||||
// sets the value to literal `null`
|
||||
// MarshalJSON custom marshal function which sets the value to literal `null`
|
||||
func (n Nil) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`null`), nil
|
||||
}
|
||||
|
||||
// MarshalYAML custom marshal function which
|
||||
// sets the value to literal `null`
|
||||
// MarshalXML custom marshal function which adds property 'xsi:nil="true"' to a tag
|
||||
// of a 'nil' item
|
||||
func (n Nil) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "xsi:nil"}, Value: "true"})
|
||||
return e.EncodeElement(``, start)
|
||||
}
|
||||
|
||||
// MarshalYAML custom marshal function which sets the value to literal `null`
|
||||
func (n Nil) MarshalYAML() (interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// String represents a 'string' value which is
|
||||
// marshaled to `null` when empty for JSON and
|
||||
// YAML
|
||||
// String represents a 'string' value which is marshaled to `null` when empty for JSON and YAML
|
||||
type String string
|
||||
|
||||
// String returns s as an actual string value
|
||||
@@ -116,8 +117,7 @@ func (s String) HasDefault() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// MarshalJSON custom marshal function which
|
||||
// sets the value to literal `null` when empty
|
||||
// MarshalJSON custom marshal function which sets the value to literal `null` when empty
|
||||
func (s String) MarshalJSON() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
if len(s.String()) == 0 {
|
||||
@@ -131,8 +131,17 @@ func (s String) MarshalJSON() ([]byte, error) {
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// MarshalYAML custom marshal function which
|
||||
// sets the value to literal `null` when empty
|
||||
// MarshalXML custom marshal function which adds property 'xsi:nil="true"' to a tag
|
||||
// if the underlying item is 'nil'
|
||||
func (s String) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
if string(s) == "" {
|
||||
start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "xsi:nil"}, Value: "true"})
|
||||
return e.EncodeElement(``, start)
|
||||
}
|
||||
return e.EncodeElement(string(s), start)
|
||||
}
|
||||
|
||||
// MarshalYAML custom marshal function which sets the value to literal `null` when empty
|
||||
func (s String) MarshalYAML() (interface{}, error) {
|
||||
if len(s.String()) == 0 {
|
||||
return nil, nil
|
||||
@@ -140,8 +149,7 @@ func (s String) MarshalYAML() (interface{}, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Empty represents an empty 'string' which is
|
||||
// marshaled to `""` in JSON and YAML
|
||||
// Empty represents an empty 'string' which is marshaled to `""` in JSON and YAML
|
||||
type Empty string
|
||||
|
||||
// nolint
|
||||
@@ -154,14 +162,12 @@ func (e Empty) HasDefault() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// MarshalJSON custom marshal function which
|
||||
// sets the value to `""`
|
||||
// MarshalJSON custom marshal function which sets the value to `""`
|
||||
func (e Empty) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`""`), nil
|
||||
}
|
||||
|
||||
// Number represents a 'number' value which is
|
||||
// marshaled to `null` when emty in JSON and YAML
|
||||
// Number represents a 'number' value which is marshaled to `null` when emty in JSON and YAML
|
||||
type Number float64
|
||||
|
||||
// nolint
|
||||
@@ -204,6 +210,27 @@ func (l List) HasDefault() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type xmllistentry struct {
|
||||
XMLName xml.Name
|
||||
Value interface{} `xml:",chardata"`
|
||||
}
|
||||
|
||||
// MarshalXML custom marshal function which wraps list items in '<default></default>'
|
||||
// tag and each items of the list will be wrapped in a '<item></item>' tag
|
||||
func (l List) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
if len(l) == 0 {
|
||||
return e.EncodeElement(``, start)
|
||||
}
|
||||
err := e.EncodeToken(start)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, i := range l {
|
||||
e.Encode(xmllistentry{XMLName: xml.Name{Local: "item"}, Value: i}) //nolint: errcheck
|
||||
}
|
||||
return e.EncodeToken(start.End())
|
||||
}
|
||||
|
||||
// Map represents a 'map' of values
|
||||
type Map map[string]interface{}
|
||||
|
||||
@@ -220,3 +247,64 @@ func (m Map) underlying() map[string]interface{} {
|
||||
func (m Map) HasDefault() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type xmlmapentry struct {
|
||||
XMLName xml.Name
|
||||
Value interface{} `xml:",chardata"`
|
||||
}
|
||||
|
||||
type sortmapkeys []string
|
||||
|
||||
func (s sortmapkeys) Len() int { return len(s) }
|
||||
func (s sortmapkeys) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s sortmapkeys) Less(i, j int) bool { return s[i] < s[j] }
|
||||
|
||||
// MarshalXML custom marshal function which converts map to its literal
|
||||
// XML representation. For example:
|
||||
//
|
||||
// m := Map{
|
||||
// "a": 1,
|
||||
// "b": 2,
|
||||
// "c": 3,
|
||||
// }
|
||||
//
|
||||
// type foo struct {
|
||||
// Value Map `xml:"value"`
|
||||
// }
|
||||
//
|
||||
// will get marshaled to:
|
||||
//
|
||||
// <foo>
|
||||
// <value>
|
||||
// <a>1</a>
|
||||
// <b>2</b>
|
||||
// <c>3</c>
|
||||
// </value>
|
||||
// </foo>
|
||||
func (m Map) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
if len(m) == 0 {
|
||||
return e.EncodeElement(``, start)
|
||||
}
|
||||
err := e.EncodeToken(start)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
keys := make([]string, 0)
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Sort(sortmapkeys(keys))
|
||||
for _, k := range keys {
|
||||
switch reflect.TypeOf(m[k]).Kind() {
|
||||
case reflect.Map:
|
||||
is := xml.StartElement{Name: xml.Name{Local: k}}
|
||||
Map(m[k].(map[string]interface{})).MarshalXML(e, is) //nolint: errcheck
|
||||
case reflect.Slice:
|
||||
is := xml.StartElement{Name: xml.Name{Local: k}}
|
||||
List(m[k].([]interface{})).MarshalXML(e, is) //nolint: errcheck
|
||||
default:
|
||||
e.Encode(xmlmapentry{XMLName: xml.Name{Local: k}, Value: m[k]}) //nolint: errcheck
|
||||
}
|
||||
}
|
||||
return e.EncodeToken(start.End())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user