issue.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. // Data and interface definitions for bugs
  2. package issues
  3. import (
  4. "os"
  5. "path/filepath"
  6. "strings"
  7. )
  8. // Issue Definitions ------------------------------------------------------------
  9. // ----------------------------------------------------------------------------
  10. type Issue struct {
  11. Title string // The title of the bug in human readable format
  12. Description Field // The description of the bug
  13. Status Field // The status of the bug
  14. Tags VariadicField // A slice of VariadicFields
  15. Blockedby VariadicField // A slice of VariadicFields
  16. Path string // The path to the bug
  17. // machineTitle string // The machine parseable bug title
  18. }
  19. // Constrcutor for Issues
  20. func (i Issue) New(title string, status Field, tags VariadicField, blockedby VariadicField, path string) (issue Issue, err error) {
  21. return Issue{
  22. Title: title,
  23. Status: status,
  24. Tags: tags,
  25. Blockedby: blockedby,
  26. Path: path,
  27. }, err
  28. }
  29. // Constrcutor for Issues that loads relevant data from disk
  30. func (i Issue) NewFromPath(path string) (bug Issue, err error) {
  31. // Required Fields
  32. description := &Field{Path: "/description"}
  33. status := &Field{Path: "/status"}
  34. requiredFields := []*Field{description, status}
  35. for _, field := range requiredFields {
  36. data, err := readPath(path + field.Path)
  37. if err != nil {
  38. return Issue{}, err
  39. }
  40. field.Data = strings.TrimRight(data, "\n")
  41. }
  42. // Variadic Fields
  43. tags := VariadicField{Path: "/tags"}
  44. blockers := VariadicField{Path: "/blockedby"}
  45. tags, _ = VariadicField.NewFromPath(tags, path)
  46. blockers, _ = VariadicField.NewFromPath(blockers, path)
  47. // we can ignore the errors, as loadVariadicField already gracefully handles
  48. //them.
  49. // title from path
  50. title := parsePathToHuman(path)
  51. return Issue{
  52. Title: title,
  53. Description: *description,
  54. Status: *status,
  55. Tags: tags,
  56. Blockedby: blockers,
  57. Path: path,
  58. }, err
  59. }
  60. // Field Definitions ----------------------------------------------------------
  61. // ----------------------------------------------------------------------------
  62. // A struct representing data that is tied to data on disk
  63. type Field struct {
  64. Path string
  65. Data string
  66. }
  67. // Constructor for Fields
  68. func (f Field) New(data string, path string) Field { return Field{Data: data, Path: path} }
  69. // VariadicField Definitions --------------------------------------------------
  70. // ----------------------------------------------------------------------------
  71. // VariadicFields hold lists of Field objects.
  72. type VariadicField struct {
  73. Path string // The associated path on disk of Fields represented by Variadic Field
  74. Fields []Field // The underlying slice of Field objects
  75. }
  76. // Constructor for VariadicFields
  77. func (vf VariadicField) New(fields []Field, path string) VariadicField {
  78. return VariadicField{Fields: fields, Path: path}
  79. }
  80. // Constructor for VariadicFields that loads relevant data from disk
  81. func (vf VariadicField) NewFromPath(pathOnDisk string) (v VariadicField, err error) {
  82. rootPath := pathOnDisk + "/" + vf.Path
  83. files, err := os.ReadDir(rootPath)
  84. if err != nil {
  85. return vf, err
  86. }
  87. for _, file := range files {
  88. data, err := readPath(rootPath + "/" + file.Name())
  89. if err != nil {
  90. return vf, err
  91. }
  92. if file.Name()[0:1] == "." {
  93. continue
  94. }
  95. vf.Fields = append(vf.Fields, Field{Data: strings.TrimRight(data, "\n"), Path: file.Name()})
  96. }
  97. return vf, err
  98. }
  99. // IssueCollection Definitions ------------------------------------------------
  100. // ----------------------------------------------------------------------------
  101. type IssueCollection []Issue
  102. func (ic IssueCollection) NewFromPath(path string) (collection IssueCollection, err error) {
  103. files, err := os.ReadDir(path)
  104. if err != nil {
  105. return IssueCollection{}, err
  106. }
  107. for _, file := range files {
  108. issuePath := path + "/" + file.Name()
  109. if IsIssue(issuePath) {
  110. issue, err := Issue.NewFromPath(Issue{}, issuePath)
  111. if err != nil {
  112. continue
  113. }
  114. collection = append(collection, issue)
  115. }
  116. }
  117. return collection, nil
  118. }
  119. // Util Definitions -----------------------------------------------------------
  120. // ----------------------------------------------------------------------------
  121. // Parses human readable strings as path strings
  122. func parseHumanToPath(humanReadable string) string {
  123. var out string
  124. out = strings.ReplaceAll(humanReadable, "-", "\\replace/")
  125. out = strings.ReplaceAll(out, " ", "-")
  126. out = strings.ReplaceAll(out, "\\replace/", "--")
  127. return out
  128. }
  129. // Parses machine parseable paths as human readable strings
  130. func parsePathToHuman(path string) string {
  131. _, last := filepath.Split(filepath.Clean(path))
  132. last = strings.ReplaceAll(last, "--", "\\replace/")
  133. last = strings.ReplaceAll(last, "-", " ")
  134. last = strings.ReplaceAll(last, "\\replace/", "-")
  135. return last
  136. }