github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/utils/docgen/api/document.go (about) 1 package docgen 2 3 import ( 4 "fmt" 5 "sort" 6 "strings" 7 "time" 8 ) 9 10 // Document is the catalogue of config files found in the search path 11 type document struct { 12 // DocumentID is the identifier for the document and name to write the document to disk as (excluding extension) 13 DocumentID string `yaml:"DocumentID"` 14 15 // Title of the document 16 Title string `yaml:"Title"` 17 18 // CategoryID as per the Categories map (see below) 19 CategoryID string `yaml:"CategoryID"` 20 21 // Summary is a one line summary 22 Summary string `yaml:"Summary"` 23 24 // Usage - this is more intended as an API reference than example code 25 Usage string `yaml:"Usage"` 26 27 // Example code to accompany the usage reference 28 Examples string `yaml:"Examples"` 29 30 // Description is the contents of the document 31 Description string `yaml:"Description"` 32 33 // Payload is a document describing an APIs payload 34 Payload string `yaml:"Payload"` 35 36 // Flags is a map of supported flags 37 Flags map[string]string `yaml:"Flags"` 38 39 // Like flags but where parameters are numerically defined 40 Parameters []string `yaml:"Parameters"` 41 42 // Associations is for murex data-types 43 Associations AssociationValues `yaml:"Associations"` 44 45 // API hooks for murex data-types 46 Hooks map[string]string `yaml:"Hooks"` 47 48 // Detail is for misc details 49 Detail string `yaml:"Detail"` 50 51 // Synonyms or aliases (if applicable) 52 Synonyms []string `yaml:"Synonyms"` 53 54 // Related documents (these should be in the format of `Category/FileName`) 55 Related []string `yaml:"Related"` 56 57 // Date article was published 58 DateTime string `yaml:"DateTime"` 59 60 // WriteTo is the path to write to, if different from the category path 61 WriteTo string `yaml:"WriteTo"` 62 63 // Automatically pulled from file location 64 SourcePath string `yaml:"-"` 65 } 66 67 // AssociationValues are associations registered by murex data-types 68 type AssociationValues struct { 69 Extensions []string `yaml:"Extensions"` 70 Mimes []string `yaml:"Mimes"` 71 } 72 73 // Hierarchy is the ID path 74 func (d document) Hierarchy() string { 75 if strings.HasPrefix(d.DocumentID, d.CategoryID+"/") { 76 return d.DocumentID 77 } 78 return d.CategoryID + "/" + d.DocumentID 79 } 80 81 // DocumentFileName is the file name of the written documents 82 func (t templates) DocumentFileName(d *document) string { 83 return d.DocumentID + t.OutputExt 84 } 85 86 // DocumentFilePath is the file name and path to write documents to 87 func (t templates) DocumentFilePath(d *document) string { 88 path := d.WriteTo 89 if path == "" { 90 path = t.OutputPath 91 } 92 return path + t.DocumentFileName(d) 93 } 94 95 const dateTimeParse = `2006-01-02 15:04` 96 97 func (t templates) DocumentValues(d *document, docs documents, nest bool) *documentValues { 98 var ( 99 dateTime time.Time 100 err error 101 ) 102 103 if d.DateTime == "" { 104 dateTime = time.Now() 105 } else { 106 dateTime, err = time.Parse(dateTimeParse, d.DateTime) 107 if err != nil { 108 panic(fmt.Sprintf("Cannot parse DateTime as `%s` on %s: %s", dateTimeParse, d.DocumentID, err.Error())) 109 } 110 } 111 112 dv := &documentValues{ 113 ID: d.DocumentID, 114 Title: d.Title, 115 FileName: t.DocumentFileName(d), 116 FilePath: t.DocumentFilePath(d), 117 WriteTo: d.WriteTo, 118 SourcePath: d.SourcePath, 119 Hierarchy: d.Hierarchy(), 120 CategoryID: d.CategoryID, 121 CategoryTitle: t.ref.Title, 122 CategoryDescription: t.ref.Description, 123 Summary: d.Summary, 124 Description: d.Description, 125 Usage: d.Usage, 126 Payload: d.Payload, 127 Examples: d.Examples, 128 Detail: d.Detail, 129 Synonyms: d.Synonyms, 130 Parameters: d.Parameters, 131 Associations: d.Associations, 132 DateTime: dateTime, 133 } 134 135 if !nest { 136 return dv 137 } 138 139 for _, val := range d.Related { 140 var relCatID, relDocID string 141 142 if strings.Contains(val, "/") { 143 split := strings.Split(val, "/") 144 if len(split) != 2 { 145 panic("related value contains multiple slashes") 146 } 147 148 relCatID = split[0] 149 relDocID = split[1] 150 151 } else { 152 relCatID = "???" + d.CategoryID // any category 153 relDocID = val 154 } 155 156 dv.Related = append( 157 dv.Related, 158 t.DocumentValues(docs.ByID(d.DocumentID, relCatID, relDocID), docs, false), 159 ) 160 } 161 162 for flag, desc := range d.Flags { 163 dv.Flags = append(dv.Flags, &flagValues{ 164 Flag: flag, 165 Description: desc, 166 }) 167 } 168 169 for hook, comment := range d.Hooks { 170 dv.Hooks = append(dv.Hooks, &hookValues{ 171 Hook: hook, 172 Comment: comment, 173 }) 174 } 175 176 sort.Sort(dv.Flags) 177 sort.Sort(dv.Related) 178 sort.Sort(dv.Hooks) 179 sort.Strings(dv.Associations.Extensions) 180 sort.Strings(dv.Associations.Mimes) 181 182 return dv 183 } 184 185 type documentValues struct { 186 ID string 187 Title string 188 FileName string 189 FilePath string 190 WriteTo string 191 SourcePath string 192 Hierarchy string 193 CategoryID string 194 CategoryTitle string 195 CategoryDescription string 196 Summary string 197 Description string 198 Payload string 199 Usage string 200 Examples string 201 Flags sortableFlagValues 202 Hooks sortableHookValues 203 Parameters []string 204 Associations AssociationValues 205 Detail string 206 Synonyms []string 207 Related sortableDocumentValues 208 DateTime time.Time 209 } 210 211 type sortableDocumentValues []*documentValues 212 213 func (v sortableDocumentValues) Len() int { return len(v) } 214 func (v sortableDocumentValues) Less(i, j int) bool { return v[i].Title < v[j].Title } 215 func (v sortableDocumentValues) Swap(i, j int) { v[i], v[j] = v[j], v[i] } 216 217 type flagValues struct { 218 Flag string 219 Description string 220 } 221 222 type sortableDocumentDateTime []*documentValues 223 224 func (v sortableDocumentDateTime) Len() int { return len(v) } 225 func (v sortableDocumentDateTime) Less(i, j int) bool { return v[i].DateTime.After(v[j].DateTime) } 226 func (v sortableDocumentDateTime) Swap(i, j int) { v[i], v[j] = v[j], v[i] } 227 228 type sortableFlagValues []*flagValues 229 230 func (v sortableFlagValues) Len() int { return len(v) } 231 func (v sortableFlagValues) Less(i, j int) bool { return v[i].Flag < v[j].Flag } 232 func (v sortableFlagValues) Swap(i, j int) { v[i], v[j] = v[j], v[i] } 233 234 type hookValues struct { 235 Hook string 236 Comment string 237 } 238 239 type sortableHookValues []*hookValues 240 241 func (v sortableHookValues) Len() int { return len(v) } 242 func (v sortableHookValues) Less(i, j int) bool { return v[i].Hook < v[j].Hook } 243 func (v sortableHookValues) Swap(i, j int) { v[i], v[j] = v[j], v[i] } 244 245 type documents []document 246 247 func (d documents) Len() int { return len(d) } 248 func (d documents) Less(i, j int) bool { return d[i].Title < d[j].Title } 249 func (d documents) Swap(i, j int) { d[i], d[j] = d[j], d[i] } 250 251 // ByID returns a document by it's CategoryID and DocumentID 252 func (d documents) ByID(requesterID, categoryID, documentID string) *document { 253 var anyCat bool 254 255 if strings.HasPrefix(categoryID, "???") { 256 categoryID = categoryID[3:] 257 anyCat = true 258 } 259 260 for i := range d { 261 if d[i].CategoryID != categoryID { 262 continue 263 } 264 265 if d[i].DocumentID == documentID { 266 return &d[i] 267 } 268 for syn := range d[i].Synonyms { 269 if d[i].Synonyms[syn] == documentID { 270 copy := d[i] 271 title := strings.Replace(d[i].Title, d[i].DocumentID, d[i].Synonyms[syn], -1) 272 if title == d[i].Title { 273 title = d[i].Synonyms[syn] 274 } 275 copy.Title = title 276 return © 277 } 278 } 279 } 280 281 if anyCat { 282 for i := range d { 283 if d[i].DocumentID == documentID { 284 return &d[i] 285 } 286 for syn := range d[i].Synonyms { 287 if d[i].Synonyms[syn] == documentID { 288 copy := d[i] 289 title := strings.Replace(d[i].Title, d[i].DocumentID, d[i].Synonyms[syn], -1) 290 if title == d[i].Title { 291 title = d[i].Synonyms[syn] 292 } 293 copy.Title = title 294 return © 295 } 296 } 297 } 298 } 299 300 warning(requesterID, fmt.Sprintf("Cannot find document with the ID `%s/%s`", categoryID, documentID)) 301 return &document{ 302 Title: documentID, 303 DocumentID: categoryID + "/" + documentID, 304 CategoryID: categoryID, 305 } 306 } 307 308 // Documents is all of the collated documents pre-rendering 309 var Documents documents