// Data and interface definitions for bugs // // TODO strip last newline from [issues.Issue] [issues.Field] and [issues.VariadicField] package issues import ( "os" "path/filepath" "strings" ) // Issue Definitions ------------------------------------------------------------ // ---------------------------------------------------------------------------- type Issue struct { Title string // The title of the bug in human readable format Description Field // The description of the bug Status Field // The status of the bug Tags VariadicField // A slice of VariadicFields Blockedby VariadicField // A slice of VariadicFields Path string // The path to the bug // machineTitle string // The machine parseable bug title } // Constrcutor for Issues func (i Issue) New(title string, status Field, tags VariadicField, blockedby VariadicField, path string) (issue Issue, err error) { return Issue{ Title: title, Status: status, Tags: tags, Blockedby: blockedby, Path: path, }, err } // Constrcutor for Issues that loads relevant data from disk func (i Issue) NewFromPath(path string) (bug Issue, err error) { // Required Fields description := &Field{Path: "/description"} status := &Field{Path: "/status"} requiredFields := []*Field{description, status} for _, field := range requiredFields { data, err := readPath(path + field.Path) if err != nil { return Issue{}, err } field.Data = data } // Variadic Fields tags := VariadicField{Path: "/tags"} blockers := VariadicField{Path: "/blockedby"} tags, _ = VariadicField.NewFromPath(tags, path) blockers, _ = VariadicField.NewFromPath(blockers, path) // we can ignore the errors, as loadVariadicField already gracefully handles //them. // title from path title := parsePathToHuman(path) return Issue{ Title: title, Description: *description, Status: *status, Tags: tags, Blockedby: blockers, Path: path, }, err } // Field Definitions ---------------------------------------------------------- // ---------------------------------------------------------------------------- // A struct representing data that is tied to data on disk type Field struct { Path string Data string } // Constructor for Fields func (f Field) New(data string, path string) Field { return Field{Data: data, Path: path} } // VariadicField Definitions -------------------------------------------------- // ---------------------------------------------------------------------------- // VariadicFields hold lists of Field objects. type VariadicField struct { Path string // The associated path on disk of Fields represented by Variadic Field Fields []Field // The underlying slice of Field objects } // Constructor for VariadicFields func (vf VariadicField) New(fields []Field, path string) VariadicField { return VariadicField{Fields: fields, Path: path} } // Constructor for VariadicFields that loads relevant data from disk func (vf VariadicField) NewFromPath(pathOnDisk string) (v VariadicField, err error) { rootPath := pathOnDisk + "/" + vf.Path files, err := os.ReadDir(rootPath) if err != nil { return vf, err } for _, file := range files { data, err := readPath(rootPath + "/" + file.Name()) if err != nil { return vf, err } if file.Name()[0:1] == "." { continue } vf.Fields = append(vf.Fields, Field{Data: data, Path: file.Name()}) } return vf, err } // Util Definitions ----------------------------------------------------------- // ---------------------------------------------------------------------------- // Parses human readable strings as path strings func parseHumanToPath(humanReadable string) string { var out string out = strings.ReplaceAll(humanReadable, "-", "\\replace/") out = strings.ReplaceAll(out, " ", "-") out = strings.ReplaceAll(out, "\\replace/", "--") return out } // Parses machine parseable paths as human readable strings func parsePathToHuman(path string) string { _, last := filepath.Split(filepath.Clean(path)) last = strings.ReplaceAll(last, "--", "\\replace/") last = strings.ReplaceAll(last, "-", " ") last = strings.ReplaceAll(last, "\\replace/", "-") return last }