Implements:
- Projects are now created on collections.
- Layer-Search-Methods for tree-printing of projects
- UUIDs for projects
---
lib/collection.go | 96 ++++++++++++++++++++++++++++++++++++++++++-----
lib/container.go | 20 +++++-----
lib/project.go | 5 +--
3 files changed, 98 insertions(+), 23 deletions(-)
diff --git a/lib/collection.go b/lib/collection.go
index 37784ff..c0bc246 100644
--- a/lib/collection.go
+++ b/lib/collection.go
@@ -23,6 +23,11 @@ const (
SearchAttrTag = "tag"
)
+type ProjectSubTree struct {
+ Proj *Project
+ Subprojs []*ProjectSubTree
+}
+
type Collection struct {
Path string
PoolPath string
@@ -99,9 +104,12 @@ func getProjMeta(path string) (*Project, error) {
return proj, nil
}
+// TODO: Works only properly with minimum number = 1. Maybe 0 is better?
func buildLayerNGlob(depth uint) string {
- var i uint
- s := "/"
+ var (
+ i uint
+ s = "/"
+ )
for i = 0; i < depth; i++ {
s += "*/"
@@ -112,26 +120,91 @@ func buildLayerNGlob(depth uint) string {
}
// get subprojects N layers deep
-func (p *Project) GetSubprojectsN(depth uint) ([]*Project, error) {
- var subprojs []*Project
+func (p *Project) GetSubprojectsN(depth int) ([]*Project, error) {
+ var (
+ subprojs []*Project
+ glob = p.Path + buildLayerNGlob(uint(depth))
+ matches, err = filepath.Glob(glob)
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ for _, m := range matches {
+ foundP, err := getProjMeta(m)
+ if err != nil {
+ return nil, err
+ }
+
+ if foundP.UUID != p.UUID {
+ subprojs = append(subprojs, foundP)
+ }
+ }
+
+ return subprojs, nil
+}
+
+// calls itself recursively until all subprojects have been found.
+func (p *Project) GetSubprojectTree() ([]*ProjectSubTree, error) {
+ subprojs, err := p.GetSubprojectsN(1)
+ if len(subprojs) == 0 || err != nil {
+ return nil, err
+ }
- glob := p.Path + buildLayerNGlob(depth)
- fmt.Println("glob: ", glob)
+ var subtree []*ProjectSubTree
+
+ for _, subpr := range subprojs {
+ deeperSubs, err := subpr.GetSubprojectTree()
+ if err != nil {
+ break
+ }
+ filler := ProjectSubTree{Proj: subpr, Subprojs: deeperSubs}
+ subtree = append(subtree, &filler)
+ }
+
+ return subtree, err
+}
+
+// TODO implement this
+func (c *Collection) GetProjectTree() ([]ProjectSubTree, error) {
+ var tree []ProjectSubTree
+
+ _, err := c.AllProjects()
+ if err != nil {
+ return nil, err
+ }
+
+ return tree, nil
+}
+
+// Gets all Projects layer N deep. Can be used to get all root projects.
+func (c *Collection) AllProjectsN(depth int) ([]*Project, error) {
+ var (
+ projs []*Project
+ err error
+ )
+ if depth == 0 {
+ return nil, fmt.Errorf("Invalid depth")
+ } else if depth == -1 {
+ return c.AllProjects()
+ }
+
+ glob := c.ProjectsPath + buildLayerNGlob(uint(depth))
matches, err := filepath.Glob(glob)
if err != nil {
return nil, err
}
- fmt.Println("nr of matches: ", len(matches))
for _, m := range matches {
- p, err := getProjMeta(m)
+ foundP, err := getProjMeta(m)
if err != nil {
return nil, err
}
- subprojs = append(subprojs, p)
+
+ projs = append(projs, foundP)
}
- return subprojs, nil
+ return projs, nil
}
func (c *Collection) AllProjects() ([]*Project, error) {
@@ -140,6 +213,7 @@ func (c *Collection) AllProjects() ([]*Project, error) {
err error
)
+ // FIXME: Chdir sucks
if err = os.Chdir(c.ProjectsPath); err != nil {
return nil, err
}
@@ -208,6 +282,8 @@ func (p *Collection) FindTags(t *Item) ([]*Tag, error) {
return out, nil
}
+// TODO: warn user when there is more than 1 proj with identical name
+// if there is, than make him choose by UUID.
func (c *Collection) ProjectByTitle(title string) (*Project, error) {
// TODO: implement this more efficently
projs, err := c.AllProjects()
diff --git a/lib/container.go b/lib/container.go
index 5859a42..7a32365 100644
--- a/lib/container.go
+++ b/lib/container.go
@@ -7,6 +7,7 @@ package qm
import (
"encoding/json"
+ "github.com/google/uuid"
"os"
"path/filepath"
"time"
@@ -18,26 +19,24 @@ type Container struct {
Description string `json:"description" yaml:"description"`
CreatedAt time.Time `json:"created_at" yaml:"created_at"`
Path string `json:"path" yaml:"-"`
+ UUID uuid.UUID `json:"uuid" yaml:"uuid"`
parent string
}
-func checkParentExists(c *Collection, title string) error {
- _, err := c.ProjectByTitle(title)
- return err
-}
-
+// FIXME: this should not only work for Projects
func (c *Collection) CreateContainer(title,
- descr, type_, parent string) (*Container, error) {
+ descr, type_, parentTitle string) (*Container, error) {
var path string
slug := Slug(title)
- if parent == "" {
+ if parentTitle == "" {
path = filepath.Join(c.Path, type_, slug)
} else {
- if err := checkParentExists(c, parent); err != nil {
+ parent, err := c.ProjectByTitle(parentTitle)
+ if err != nil {
return nil, err
}
- path = filepath.Join(c.Path, type_, parent, slug)
+ path = filepath.Join(parent.Path, slug)
}
cont := &Container{
@@ -46,7 +45,8 @@ func (c *Collection) CreateContainer(title,
Description: descr,
CreatedAt: time.Now(),
Path: path,
- parent: parent,
+ UUID: uuid.New(),
+ parent: parentTitle,
}
return cont, nil
diff --git a/lib/project.go b/lib/project.go
index 946b3d0..51b33f1 100644
--- a/lib/project.go
+++ b/lib/project.go
@@ -19,10 +19,9 @@ type Project struct {
//Progress uint `json:"progress" yaml:"progress"`
}
-func (c *Collection) CreateProject(title, description,
- parent string) (*Project, error) {
+func (c *Collection) CreateProject(title, descr, parent string) (*Project, error) {
p := &Project{}
- cont, err := c.CreateContainer(title, description, "projects", parent)
+ cont, err := c.CreateContainer(title, descr, "projects", parent)
if err != nil {
return nil, err
}
--
2.30.1
Working, but incomplete first draft of proj-tree printing.
---
bin/qm-list/main.go | 118 ++++++++++++++++++++++++++++++++++++++------
1 file changed, 104 insertions(+), 14 deletions(-)
diff --git a/bin/qm-list/main.go b/bin/qm-list/main.go
index 512baca..b22e532 100644
--- a/bin/qm-list/main.go
@@ -2,51 +2,141 @@ package main
import (
"fmt"
+ "os"
qm "git.sr.ht/~imperator/quartiermeister/lib"
"github.com/integrii/flaggy"
)
-func listItems(c *qm.Collection) {
+type options struct {
+ recurse uint
+ noLines bool
+}
+
+// options should be global, otherwise get passed around 1000 times.
+var opts options
+
+func listItems(c *qm.Collection) error {
items, err := c.AllItems()
if err != nil {
- fmt.Println("error: ", err)
- return
+ return err
}
for _, i := range items {
fmt.Println(i.Title)
}
+
+ return nil
+}
+
+// creates string prefix for printing a tree
+func prefixByDepth(depth int) string {
+ var (
+ indentation string
+ prefix = "├─"
+ lengthener = "────"
+ )
+
+ if depth == 0 {
+ return ""
+ }
+
+ if opts.noLines {
+ prefix = " "
+ lengthener = " "
+ }
+
+ for i := 0; i < depth; i++ {
+ indentation += lengthener
+ }
+ prefix += indentation
+
+ return prefix
+}
+
+func decrDepth(depth *int) {
+ *depth -= 1
+}
+
+// prints passed subproject and all its subprojects recursively
+func printProjAndSubs(subproj *qm.ProjectSubTree, depth int) error {
+ defer decrDepth(&depth)
+
+ // when this function recurses, the recursors get higher and higher
+ // numbers, causing the right indentation.
+ prefix := prefixByDepth(depth)
+ depth += 1
+
+ // TODO: find out why there is a whitespace too much
+ fmt.Println(prefix, subproj.Proj.Title)
+
+ for _, proj := range subproj.Subprojs {
+ prefix = prefixByDepth(depth)
+ fmt.Println(prefix, proj.Proj.Title)
+ subprojs, err := proj.Proj.GetSubprojectTree()
+ if err != nil {
+ return err
+ }
+
+ for _, sub := range subprojs {
+ depth += 1 // ugly... recursion.
+ printProjAndSubs(sub, depth)
+ depth -= 1
+ }
+ }
+
+ return nil
}
-func listProjs(c *qm.Collection) {
- projs, err := c.AllProjects()
+// loads all projects and passes them to the print function
+func printProjFamily(c *qm.Collection) error {
+ projs, err := c.AllProjectsN(1)
if err != nil {
- fmt.Println("error: ", err)
- return
+ return err
}
for _, p := range projs {
fmt.Println(p.Title)
}
+ for _, proj := range projs {
+ tmpSubs, err := proj.GetSubprojectTree()
+ if err != nil {
+ return err
+ }
+
+ tmp := qm.ProjectSubTree{proj, tmpSubs}
+ if err = printProjAndSubs(&tmp, 0); err != nil {
+ return err
+ }
+ }
+
+ return nil
}
func main() {
- var (
- itemCmd = flaggy.NewSubcommand("items")
- projCmd = flaggy.NewSubcommand("projs")
- )
+ var err error
+ itemCmd := flaggy.NewSubcommand("items")
+ projCmd := flaggy.NewSubcommand("projs")
+ projCmd.UInt(&opts.recurse, "r", "recurse",
+ "<N> show subprojects N layers deep")
+ flaggy.Bool(&opts.noLines, "n", "no-lines", "Show no lines")
+
flaggy.AttachSubcommand(itemCmd, 1)
flaggy.AttachSubcommand(projCmd, 1)
flaggy.Parse()
c, err := qm.LoadCollection()
if err != nil {
- fmt.Println("error: ", err)
+ fmt.Fprintln(os.Stderr, "error: ", err)
return
}
if itemCmd.Used {
- listItems(c)
+ err = listItems(c)
} else if projCmd.Used {
- listProjs(c)
+ err = printProjFamily(c)
+ }
+
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
}
}
--
2.30.1