Sfoglia il codice sorgente

Implements constructors for Bugs and path to human readable parsing

arianagiroux 3 settimane fa
parent
commit
ba91ee2e8b
7 ha cambiato i file con 232 aggiunte e 150 eliminazioni
  1. 3 6
      Readme.md
  2. 124 22
      bug.go
  3. 85 7
      bug_test.go
  4. 11 1
      cmd/main.go
  5. 2 54
      io.go
  6. 0 59
      io_test.go
  7. 7 1
      tui.go

+ 3 - 6
Readme.md

@@ -3,11 +3,8 @@
 
 ## TODO
 
-- `bug.go:func (b *Bug) parseHumanToMachine() string { return "" } // TODO: implement!`
-- `bug.go:func (b *Bug) parseMachineToHuman() string { return "" } // TODO: implement!`
-- `cmd/main.go:// TODO implement cli argv defs`
-- `cmd/main.go:	bug, _ := buggo.LoadBug("tests/bugs/test-1") // TODO set via argv`
-- `io.go:func (b Bug) WriteBug() (success bool, err error) { return false, nil } // TODO: implement`
-- `io.go:func (b Bug) DeleteBug() (success bool, err error) { return false, nil } // TODO: implement`
+- `bug.go:func (b Bug) New(title string, status Field, tags VariadicField, blockedby VariadicField, path string) (bug Bug, err error) { // TODO Implement`
+- `io.go:func WriteBug(bug Bug) (success bool, err error) { return false, nil } // TODO: implement`
+- `io.go:func DeleteBug(bug Bug) (success bool, err error) { return false, nil } // TODO: implement`
 - `tui.go:// TODO Implement Bubbletea`
 - `tui.go:// TODO Replace bug.go implementation of [Bug.View]`

+ 124 - 22
bug.go

@@ -3,33 +3,29 @@ package buggo
 
 import (
 	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
 
 	"github.com/charmbracelet/lipgloss"
 )
 
-type Bug 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
-}
+// Bug Definitions ------------------------------------------------------------
+// ----------------------------------------------------------------------------
 
-// A struct representing data that is tied to data on disk
-type Field struct {
-	Path string
-	Data string
-}
+type Bug 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
 
-// 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
+	// machineTitle string        // The machine parseable bug title
 }
 
 // Renders a bug as text
+// DEPRECATED
 func (b Bug) View() string {
 	headerStyle := lipgloss.NewStyle().
 		Width(120).
@@ -64,8 +60,114 @@ func (b Bug) View() string {
 	)
 }
 
-// Parses human readable titles as path
-func (b *Bug) parseHumanToMachine() string { return "" } // TODO: implement!
+// Constrcutor for Bugs
+func (b Bug) New(title string, status Field, tags VariadicField, blockedby VariadicField, path string) (bug Bug, err error) { // TODO Implement
+	return Bug{}, err
+}
+
+// Constrcutor for Bugs that loads relevant data from disk
+func (b Bug) NewFromPath(path string) (bug Bug, 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 Bug{}, 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 Bug{
+		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
+		}
+		vf.Fields = append(vf.Fields, Field{Data: data, Path: file.Name()})
+	}
+
+	return vf, err
+}
 
-// Parses machine parseable titles as human readable
-func (b *Bug) parseMachineToHuman() string { return "" } // TODO: implement!
+// 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
+}

+ 85 - 7
bug_test.go

@@ -7,13 +7,7 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestVariadicField(t *testing.T) {
-	vf := VariadicField{}
-	vf.Fields = append(vf.Fields, Field{})
-	assert.Equal(t, len(vf.Fields), 1)
-}
-
-func TestView(t *testing.T) {
+func Test_Bug_View(t *testing.T) {
 	bug := Bug{
 		Title:       "title",
 		Description: Field{Data: "description"},
@@ -43,3 +37,87 @@ func TestView(t *testing.T) {
 	assert.True(t, strings.Contains(output, "blocker1"), output)
 	assert.True(t, strings.Contains(output, "blocker2"), output)
 }
+
+func Test_Bug_NewFromPath_err_bad_path(t *testing.T) {
+	testBug := Bug{}
+	bug, err := Bug.NewFromPath(testBug, "doesntexist")
+
+	assert.Error(t, err)
+	assert.Equal(t, testBug, bug)
+}
+
+func Test_Bug_NewFromPath_success(t *testing.T) {
+	testBug := Bug{
+		Title:       "test 1",
+		Description: Field{Data: "test description\n", Path: "/description"},
+		Status:      Field{Data: "open:test\n", Path: "/status"},
+		Path:        "tests/bugs/test-1",
+
+		Tags: VariadicField{
+			Path: "/tags", Fields: []Field{
+				{Path: "tag1"},
+				{Path: "tag2"},
+			},
+		},
+		Blockedby: VariadicField{
+			Path: "/blockedby", Fields: []Field{
+				{Path: "blocker1"},
+				{Path: "blocker2"},
+			},
+		},
+	}
+
+	bug, err := Bug.NewFromPath(Bug{}, "tests/bugs/test-1")
+	if err != nil {
+		assert.Fail(t, "should not err")
+	}
+
+	assert.Equal(t, testBug.Title, bug.Title)
+	assert.Equal(t, testBug.Description, bug.Description)
+	assert.Equal(t, testBug.Status, bug.Status)
+	assert.Equal(t, testBug.Tags, bug.Tags)
+	assert.Equal(t, testBug.Blockedby, bug.Blockedby)
+	assert.Equal(t, testBug.Path, bug.Path)
+}
+
+func Test_VariadicField_NewFromPath_fails(t *testing.T) {
+	_, err := VariadicField.NewFromPath(VariadicField{Path: "/tags"}, "dosentexist")
+	assert.Error(t, err)
+}
+
+func Test_VariadicField_NewFromPath_no_tags_dir(t *testing.T) {
+	_, err := VariadicField.NewFromPath(VariadicField{Path: "/tags"}, "tests/bugs/test-2")
+	assert.Error(t, err, "should not find tags")
+}
+
+func Test_VariadicField_NewFromPath_no_tags_in_dir(t *testing.T) {
+	vf, err := VariadicField.NewFromPath(VariadicField{Path: "/tags"}, "tests/bugs/test-3")
+	if err != nil {
+		assert.Fail(t, "should not throw error")
+	}
+	assert.Equal(t, len(vf.Fields), 0)
+}
+
+func Test_VariadicField_NewFromPath_success(t *testing.T) {
+	vf, err := VariadicField.NewFromPath(VariadicField{Path: "/tags"}, "tests/bugs/test-1")
+	if err != nil {
+		assert.Fail(t, "should not throw error")
+	}
+	assert.Equal(t, len(vf.Fields), 2)
+}
+
+func Test_parsePathToHuman(t *testing.T) {
+	testPath := "test-dir/test-1--content"
+	testParse := "test 1-content"
+	parseResult := parsePathToHuman(testPath)
+
+	assert.Equal(t, testParse, parseResult)
+}
+
+func Test_parseHumanToPath(t *testing.T) {
+	testHuman := "test 1-content"
+	testParse := "test-1--content"
+	parseResult := parseHumanToPath(testHuman)
+
+	assert.Equal(t, testParse, parseResult)
+}

+ 11 - 1
cmd/main.go

@@ -1,4 +1,14 @@
 // TODO implement cli argv defs
+//
+// TODO implement interface for browse bugs in folder
+//
+//	For example:
+//		If the user provides the path of a folder that matches spec for a bug,
+//		just display that bug. Otherwise treat the specified path as a collection
+//		of bugs.
+//
+//		See Also:
+//			- charmbracelet/bubbles directory and tree explorer
 package main
 
 import (
@@ -10,7 +20,7 @@ import (
 )
 
 func main() {
-	bug, _ := buggo.LoadBug("tests/bugs/test-1") // TODO set via argv
+	bug, _ := buggo.Bug.NewFromPath(buggo.Bug{}, "tests/bugs/test-1") // TODO set via argv
 	p := tea.NewProgram(
 		buggo.Model{Bug: bug},
 		tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer"

+ 2 - 54
io.go

@@ -18,60 +18,8 @@ func readPath(path string) (output string, err error) {
 	return output, nil
 }
 
-func loadVariadicField(pathOnDisk string, vf VariadicField) (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
-		}
-		vf.Fields = append(vf.Fields, Field{Data: data, Path: file.Name()})
-	}
-
-	return vf, err
-}
-
-// Loads a bug from disk
-func LoadBug(path string) (bug Bug, 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 Bug{}, err
-		}
-
-		field.Data = data
-	}
-
-	// Variadic Fields
-	tags := VariadicField{Path: "/tags"}
-	blockers := VariadicField{Path: "/blockedby"}
-
-	tags, _ = loadVariadicField(path, tags)
-	blockers, _ = loadVariadicField(path, blockers)
-	// we can ignore the errors, as loadVariadicField already gracefully handles
-	//them.
-
-	return Bug{
-		Description: *description,
-		Status:      *status,
-		Tags:        tags,
-		Blockedby:   blockers,
-	}, err
-}
-
 // Writes a bug to disk
-func (b Bug) WriteBug() (success bool, err error) { return false, nil } // TODO: implement
+func WriteBug(bug Bug) (success bool, err error) { return false, nil } // TODO: implement
 
 // Removes a bug from disk
-func (b Bug) DeleteBug() (success bool, err error) { return false, nil } // TODO: implement
+func DeleteBug(bug Bug) (success bool, err error) { return false, nil } // TODO: implement

+ 0 - 59
io_test.go

@@ -22,62 +22,3 @@ func Test_readPath_succeeds(t *testing.T) {
 	assert.Equal(t, data, "test description\n")
 }
 
-func TestLoadBugErr(t *testing.T) {
-	_, err := LoadBug("doesntexist")
-	assert.Error(t, err, "should throw an error")
-}
-
-func TestLoadBugSuccess(t *testing.T) {
-	bug, err := LoadBug("tests/bugs/test-1")
-	if err != nil {
-		assert.Fail(t, "function threw error, does tests/bugs/test-1 exist?")
-	}
-
-	testBug := Bug{
-		Description: Field{Data: "test description\n", Path: "/description"},
-		Status:      Field{Data: "open:test\n", Path: "/status"},
-		Tags: VariadicField{
-			Path: "/tags", Fields: []Field{
-				{Data: "tag-1", Path: "tag-1"},
-				{Data: "tag-2", Path: "tag-2"},
-			},
-		},
-		Blockedby: VariadicField{
-			Path: "/blockedby", Fields: []Field{
-				{Data: "blocker-1", Path: "blocker-1"},
-				{Data: "blocker-2", Path: "blocker-1"},
-			},
-		},
-	}
-
-	assert.Equal(t, bug.Description, testBug.Description)
-	assert.Equal(t, bug.Status, testBug.Status)
-	assert.Equal(t, len(bug.Tags.Fields), 2)
-	assert.Equal(t, len(bug.Blockedby.Fields), 2)
-}
-
-func Test_loadTags_fails(t *testing.T) {
-	_, err := loadVariadicField("dosentexist", VariadicField{Path: "/tags"})
-	assert.Error(t, err)
-}
-
-func Test_loadTags_no_tags_dir(t *testing.T) {
-	_, err := loadVariadicField("tests/bugs/test-2", VariadicField{Path: "/tags"})
-	assert.Error(t, err, "should not find tags")
-}
-
-func Test_loadTags_no_tags_in_dir(t *testing.T) {
-	vf, err := loadVariadicField("tests/bugs/test-3", VariadicField{Path: "/tags"})
-	if err != nil {
-		assert.Fail(t, "should not throw error")
-	}
-	assert.Equal(t, len(vf.Fields), 0)
-}
-
-func Test_loadTags_success(t *testing.T) {
-	vf, err := loadVariadicField("tests/bugs/test-1", VariadicField{Path: "/tags"})
-	if err != nil {
-		assert.Fail(t, "should not throw error")
-	}
-	assert.Equal(t, len(vf.Fields), 2)
-}

+ 7 - 1
tui.go

@@ -26,5 +26,11 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 }
 
 func (m Model) View() string {
-	return fmt.Sprintf("%v", m.Bug)
+	var output string
+	output = output + fmt.Sprintf("%v", m.Bug)
+	// title
+	// status
+	// variadics
+	// description
+	return output
 }