github.com/mineiros-io/terradoc@v0.0.9-0.20220711062319-018bd4ae81f5/internal/renderers/markdown/writer.go (about)

     1  package markdown
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"text/template"
     7  
     8  	"github.com/mineiros-io/terradoc"
     9  	"github.com/mineiros-io/terradoc/internal/entities"
    10  	"github.com/mineiros-io/terradoc/internal/renderers"
    11  )
    12  
    13  const (
    14  	templateName = "README.md"
    15  
    16  	sectionTemplateName         = "section"
    17  	referencesTemplateName      = "references"
    18  	variableTemplateName        = "variable"
    19  	attributeTemplateName       = "attribute"
    20  	typeDescriptionTemplateName = "typeDescription"
    21  	headerTemplateName          = "header"
    22  	tocTemplateName             = "toc"
    23  	outputTemplateName          = "output"
    24  
    25  	varNestingLevel = 0
    26  )
    27  
    28  type markdownWriter struct {
    29  	writer io.Writer
    30  	templ  *template.Template
    31  }
    32  
    33  func newMarkdownWriter(writer io.Writer) (*markdownWriter, error) {
    34  	const templatesPath = "templates/markdown/*"
    35  
    36  	t, err := template.New(templateName).Funcs(renderers.TemplatesFuncMap).ParseFS(terradoc.TemplateFS, templatesPath)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  
    41  	return &markdownWriter{writer: writer, templ: t}, nil
    42  }
    43  
    44  func (mw *markdownWriter) writeDefinition(definition entities.Doc) error {
    45  	if err := mw.writeHeader(definition.Header); err != nil {
    46  		return err
    47  	}
    48  
    49  	if err := mw.writeSections(definition.Sections); err != nil {
    50  		return err
    51  	}
    52  
    53  	return mw.writeReferences(definition.References)
    54  }
    55  
    56  func (mw *markdownWriter) writeHeader(header entities.Header) error {
    57  	// Prevent empty header from being rendered
    58  	if len(header.Badges) > 0 || header.Image != "" {
    59  		return mw.writeTemplate(headerTemplateName, header)
    60  	}
    61  
    62  	return nil
    63  }
    64  
    65  func (mw *markdownWriter) writeReferences(references []entities.Reference) error {
    66  	if len(references) == 0 {
    67  		return nil
    68  	}
    69  
    70  	return mw.writeTemplate(referencesTemplateName, references)
    71  }
    72  
    73  func (mw *markdownWriter) writeSections(sections []entities.Section) error {
    74  	for _, section := range sections {
    75  		if err := mw.writeSection(section); err != nil {
    76  			return err
    77  		}
    78  	}
    79  
    80  	return nil
    81  }
    82  
    83  func (mw *markdownWriter) writeSection(section entities.Section) error {
    84  	if err := mw.writeTemplate(sectionTemplateName, section); err != nil {
    85  		return err
    86  	}
    87  
    88  	if section.TOC {
    89  		if err := mw.writeTOC(section.SubSections); err != nil {
    90  			return err
    91  		}
    92  	}
    93  
    94  	if err := mw.writeVariables(section.Variables); err != nil {
    95  		return err
    96  	}
    97  
    98  	if err := mw.writeOutputs(section.Outputs); err != nil {
    99  		return err
   100  	}
   101  
   102  	return mw.writeSections(section.SubSections)
   103  }
   104  
   105  func (mw *markdownWriter) writeTemplate(templateName string, v interface{}) error {
   106  	return mw.templ.ExecuteTemplate(mw.writer, templateName, v)
   107  }
   108  
   109  func (mw *markdownWriter) writeVariables(variables []entities.Variable) error {
   110  	for _, variable := range variables {
   111  		if err := mw.writeVariable(variable); err != nil {
   112  			return err
   113  		}
   114  	}
   115  
   116  	return nil
   117  }
   118  
   119  func (mw *markdownWriter) writeVariable(variable entities.Variable) error {
   120  	if err := mw.writeTemplate(variableTemplateName, variable); err != nil {
   121  		return err
   122  	}
   123  
   124  	if len(variable.Attributes) > 0 {
   125  		if err := mw.writeType(variable.Type, varNestingLevel); err != nil {
   126  			return err
   127  		}
   128  
   129  		return mw.writeAttributes(variable.Attributes, variable.Name)
   130  	}
   131  
   132  	return nil
   133  }
   134  
   135  func (mw *markdownWriter) writeType(typeDefinition entities.Type, nestingLevel int) error {
   136  	type typeRenderer struct {
   137  		entities.Type
   138  		IndentLevel int
   139  	}
   140  
   141  	indentLevel := renderers.GetIndent(nestingLevel)
   142  
   143  	return mw.writeTemplate(
   144  		typeDescriptionTemplateName,
   145  		&typeRenderer{
   146  			Type:        typeDefinition,
   147  			IndentLevel: indentLevel,
   148  		},
   149  	)
   150  }
   151  
   152  func (mw *markdownWriter) writeAttributes(attributes []entities.Attribute, parentName string) error {
   153  	for _, attribute := range attributes {
   154  		if err := mw.writeAttribute(attribute, parentName); err != nil {
   155  			return err
   156  		}
   157  	}
   158  
   159  	return nil
   160  }
   161  
   162  func (mw *markdownWriter) writeAttribute(attribute entities.Attribute, parentName string) error {
   163  	type attributeRenderer struct {
   164  		entities.Attribute
   165  		ParentName string
   166  	}
   167  
   168  	attrRenderer := attributeRenderer{Attribute: attribute, ParentName: parentName}
   169  
   170  	if err := mw.writeTemplate(attributeTemplateName, attrRenderer); err != nil {
   171  		return err
   172  	}
   173  
   174  	if len(attribute.Attributes) > 0 {
   175  		if err := mw.writeType(attribute.Type, attribute.Level); err != nil {
   176  			return err
   177  		}
   178  
   179  		nestedParentName := fmt.Sprintf("%s-%s", parentName, attribute.Name)
   180  
   181  		return mw.writeAttributes(attribute.Attributes, nestedParentName)
   182  	}
   183  
   184  	return nil
   185  }
   186  
   187  type tocItemRenderer struct {
   188  	Label       string
   189  	IndentLevel int
   190  }
   191  
   192  func (mw *markdownWriter) writeTOC(sections []entities.Section) error {
   193  	items := fetchTOCItems(sections, 0)
   194  
   195  	return mw.writeTemplate(tocTemplateName, items)
   196  }
   197  
   198  func fetchTOCItems(sections []entities.Section, level int) (items []tocItemRenderer) {
   199  	for _, section := range sections {
   200  
   201  		tocItem := tocItemRenderer{Label: section.Title, IndentLevel: level}
   202  		items = append(items, tocItem)
   203  
   204  		nestedItems := fetchTOCItems(section.SubSections, level+2)
   205  
   206  		items = append(items, nestedItems...)
   207  	}
   208  
   209  	return items
   210  }
   211  
   212  func (mw *markdownWriter) writeOutputs(outputs []entities.Output) error {
   213  	for _, output := range outputs {
   214  		if err := mw.writeOutput(output); err != nil {
   215  			return err
   216  		}
   217  	}
   218  
   219  	return nil
   220  }
   221  
   222  func (mw *markdownWriter) writeOutput(output entities.Output) error {
   223  	if err := mw.writeTemplate(outputTemplateName, output); err != nil {
   224  		return err
   225  	}
   226  
   227  	return nil
   228  }