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 &copy
   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 &copy
   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