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 }