~imperator/quartiermeister-devel

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
5 2

[PATCH 1/3] qm: improve user informing

Details
Message ID
<20210311212600.19295-1-stanner@posteo.de>
DKIM signature
pass
Download raw message
Patch: +2 -1
---
 qm | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/qm b/qm
index a3b2d6f..5e50a0c 100755
--- a/qm
+++ b/qm
@@ -16,10 +16,11 @@ main() {
    export QM_EDITOR="nvim"

    if [[ ! -d "$QM_PATH" ]]; then
        echo "\"$QM_PATH\" created."
        echo "No QM database yet. Iniatlizing..."
        mkdir -p "$QM_PATH"
        mkdir -p "$QM_PATH"/$QM_COLLECTION/pool
        mkdir -p "$QM_PATH"/$QM_COLLECTION/projects
        echo "\"$QM_PATH\" initialized."
    fi

    if which "qm-$cmd" >& /dev/null; then
-- 
2.30.1

[PATCH 2/3] lib: implement search methods and project creation

Details
Message ID
<20210311212600.19295-2-stanner@posteo.de>
In-Reply-To
<20210311212600.19295-1-stanner@posteo.de> (view parent)
DKIM signature
pass
Download raw message
Patch: +91 -19
Implements:
- Projects are now created on collections.
- Layer-Search-Methods for tree-printing of projects
- UUIDs for projects
---
Those are major changes. We will have to find a way to use Container for
all of this soonish.

 lib/collection.go | 85 +++++++++++++++++++++++++++++++++++++++++++----
 lib/container.go  | 20 +++++------
 lib/project.go    |  5 ++-
 3 files changed, 91 insertions(+), 19 deletions(-)

diff --git a/lib/collection.go b/lib/collection.go
index 37784ff..6a28d35 100644
--- a/lib/collection.go
+++ b/lib/collection.go
@@ -23,6 +23,11 @@ const (
	SearchAttrTag     = "tag"
)

type ProjectSubTreeS struct {
	Proj     *Project
	Subprojs []*ProjectSubTreeS
}

type Collection struct {
	Path         string
	PoolPath     string
@@ -99,6 +104,7 @@ 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 := "/"
@@ -112,34 +118,99 @@ func buildLayerNGlob(depth uint) string {
}

// get subprojects N layers deep
func (p *Project) GetSubprojectsN(depth uint) ([]*Project, error) {
func (p *Project) GetSubprojectsN(depth int) ([]*Project, error) {
	var subprojs []*Project

	glob := p.Path + buildLayerNGlob(depth)
	fmt.Println("glob: ", glob)
	glob := p.Path + 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)

		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() ([]*ProjectSubTreeS, error) {
	subprojs, err := p.GetSubprojectsN(1)
	if len(subprojs) == 0 || err != nil {
		return nil, err
	}

	subtree := make([]*ProjectSubTreeS, 0, 8)

	for _, subpr := range subprojs {
		deeperSubs, err := subpr.GetSubprojectTree()
		if err != nil {
			break
		}
		filler := ProjectSubTreeS{subpr, deeperSubs}
		subtree = append(subtree, &filler)
	}

	return subtree, err
}

// TODO implement this
func (c *Collection) GetProjectTree() ([]ProjectSubTreeS, error) {
	var tree []ProjectSubTreeS

	_, 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
	}

	for _, m := range matches {
		foundP, err := getProjMeta(m)
		if err != nil {
			return nil, err
		}

		projs = append(projs, foundP)
	}

	return projs, nil
}

func (c *Collection) AllProjects() ([]*Project, error) {
	var (
		projs []*Project
		err   error
	)

	// FIXME: Chdir sucks
	if err = os.Chdir(c.ProjectsPath); err != nil {
		return nil, err
	}
@@ -208,6 +279,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

[PATCH 3/3] bin: list: First version of project tree printing

Details
Message ID
<20210311212600.19295-3-stanner@posteo.de>
In-Reply-To
<20210311212600.19295-1-stanner@posteo.de> (view parent)
DKIM signature
pass
Download raw message
Patch: +100 -16
Working, but incomplete first draft of proj-tree printing.
---
Thanks for your last patch.
I have fixed the global variable and use defer instead.
As for the recursion: Lots of work, we'll keep it, but feel free to
offer a patch adjusting everything in a way you viel as appropriate, and
we can have a discussion about this.
But this code will probably change very quickly anyways.
 bin/qm-list/main.go | 116 ++++++++++++++++++++++++++++++++++++++------
 1 file changed, 100 insertions(+), 16 deletions(-)

diff --git a/bin/qm-list/main.go b/bin/qm-list/main.go
index 512baca..6f99180 100644
--- a/bin/qm-list/main.go
@@ -2,51 +2,135 @@ 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 {
	if depth == 0 {
		return ""
	}
	prefix := "├─"
	lengthener := "────"
	if opts.noLines {
		prefix = "  "
		lengthener = "    "
	}

	var indentation string
	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.ProjectSubTreeS, 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.ProjectSubTreeS{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
Details
Message ID
<YEsanFFHeHSSw4dP@alderaan.localdomain>
In-Reply-To
<20210311212600.19295-1-stanner@posteo.de> (view parent)
DKIM signature
pass
Download raw message
IMO this makes kind of sense. In the long run I would prefer a `qm init`
command for this. Then the user has full control and `qm` does not do
any magic without being asked.

For now it is fine for me, just quickfix the typo below. :)

PS: Sent by mutt.

On Thu, Mar 11, 2021 at 10:25:59PM +0100, Philipp Stanner wrote:
> ---
>  qm | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/qm b/qm
> index a3b2d6f..5e50a0c 100755
> --- a/qm
> +++ b/qm
> @@ -16,10 +16,11 @@ main() {
>      export QM_EDITOR="nvim"
>  
>      if [[ ! -d "$QM_PATH" ]]; then
> -        echo "\"$QM_PATH\" created."
> +        echo "No QM database yet. Iniatlizing..."

Typo.

>          mkdir -p "$QM_PATH"
>          mkdir -p "$QM_PATH"/$QM_COLLECTION/pool
>          mkdir -p "$QM_PATH"/$QM_COLLECTION/projects
> +        echo "\"$QM_PATH\" initialized."
>      fi
>  
>      if which "qm-$cmd" >& /dev/null; then
> -- 
> 2.30.1
> 

Re: [PATCH 2/3] lib: implement search methods and project creation

Details
Message ID
<YEsfSwB+5cULBJsH@alderaan.localdomain>
In-Reply-To
<20210311212600.19295-2-stanner@posteo.de> (view parent)
DKIM signature
pass
Download raw message
A few nits. Rest seems ok for my first review like this in mutt.

On Thu, Mar 11, 2021 at 10:26:01PM +0100, Philipp Stanner wrote:
> Implements:
> - Projects are now created on collections.
> - Layer-Search-Methods for tree-printing of projects
> - UUIDs for projects
> ---
> Those are major changes. We will have to find a way to use Container for
> all of this soonish.
> 
>  lib/collection.go | 85 +++++++++++++++++++++++++++++++++++++++++++----
>  lib/container.go  | 20 +++++------
>  lib/project.go    |  5 ++-
>  3 files changed, 91 insertions(+), 19 deletions(-)
> 
> diff --git a/lib/collection.go b/lib/collection.go
> index 37784ff..6a28d35 100644
> --- a/lib/collection.go
> +++ b/lib/collection.go
> @@ -23,6 +23,11 @@ const (
>  	SearchAttrTag     = "tag"
>  )
>  
> +type ProjectSubTreeS struct {
> +	Proj     *Project
> +	Subprojs []*ProjectSubTreeS
> +}
> +
>  type Collection struct {
>  	Path         string
>  	PoolPath     string
> @@ -99,6 +104,7 @@ 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 = "/"
)

> @@ -112,34 +118,99 @@ func buildLayerNGlob(depth uint) string {
>  }
>  
>  // get subprojects N layers deep
> -func (p *Project) GetSubprojectsN(depth uint) ([]*Project, error) {
> +func (p *Project) GetSubprojectsN(depth int) ([]*Project, error) {

These guys can be grouped; reduces the noise a bit:

var (
    subprojs []*Project
    glob = p.Path + buildLayerNGlob(depth)
)

fmt.Println("glob: ", glob)

>  	var subprojs []*Project
>  
> -	glob := p.Path + buildLayerNGlob(depth)
> -	fmt.Println("glob: ", glob)
> +	glob := p.Path + 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)
> +
> +		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() ([]*ProjectSubTreeS, error) {
> +	subprojs, err := p.GetSubprojectsN(1)
> +	if len(subprojs) == 0 || err != nil {
> +		return nil, err
> +	}
> +
> +	subtree := make([]*ProjectSubTreeS, 0, 8)

Append is used below. No make is required; let's offload this to go.
It has reasonable defaults.

> +
> +	for _, subpr := range subprojs {
> +		deeperSubs, err := subpr.GetSubprojectTree()
> +		if err != nil {
> +			break
> +		}
> +		filler := ProjectSubTreeS{subpr, deeperSubs}

I would use the field names to avoid bugs in the future:

filler := ProjectSubTreeS{foo: subpr, bar: deeperSubs}

> +		subtree = append(subtree, &filler)
> +	}
> +
> +	return subtree, err
> +}
> +
> +// TODO implement this
> +func (c *Collection) GetProjectTree() ([]ProjectSubTreeS, error) {
> +	var tree []ProjectSubTreeS
> +
> +	_, 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
> +	}
> +
> +	for _, m := range matches {
> +		foundP, err := getProjMeta(m)
> +		if err != nil {
> +			return nil, err
> +		}
> +
> +		projs = append(projs, foundP)
> +	}
> +
> +	return projs, nil
> +}
> +
>  func (c *Collection) AllProjects() ([]*Project, error) {
>  	var (
>  		projs []*Project
>  		err   error
>  	)
>  
> +	// FIXME: Chdir sucks
>  	if err = os.Chdir(c.ProjectsPath); err != nil {
>  		return nil, err
>  	}
> @@ -208,6 +279,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"

Please run `goimports` on this file. The imports usually are grouped
differently. Ale in nvim does this automatically for you. :)

>  	"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
> 

Re: [PATCH 3/3] bin: list: First version of project tree printing

Details
Message ID
<YEshJL8VJnQaFxZ6@alderaan.localdomain>
In-Reply-To
<20210311212600.19295-3-stanner@posteo.de> (view parent)
DKIM signature
pass
Download raw message
On Thu, Mar 11, 2021 at 10:26:03PM +0100, Philipp Stanner wrote:
> Working, but incomplete first draft of proj-tree printing.

Yeah, I would say, merge it soon and let's play around with it.

> ---
> Thanks for your last patch.
> I have fixed the global variable and use defer instead.
> As for the recursion: Lots of work, we'll keep it, but feel free to
> offer a patch adjusting everything in a way you viel as appropriate, and
> we can have a discussion about this.
> But this code will probably change very quickly anyways.
>  bin/qm-list/main.go | 116 ++++++++++++++++++++++++++++++++++++++------
>  1 file changed, 100 insertions(+), 16 deletions(-)
> 
> diff --git a/bin/qm-list/main.go b/bin/qm-list/main.go
> index 512baca..6f99180 100644
> --- a/bin/qm-list/main.go
> +++ b/bin/qm-list/main.go
> @@ -2,51 +2,135 @@ 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 {
> +	if depth == 0 {
> +		return ""
> +	}
> +	prefix := "├─"
> +	lengthener := "────"

I prefer groups because it is always a lot clearer to me. No idea why, less
visual noise maybe.

var (
    indentation string
    prefix     = "├─"
    lengthener = "────"
)

> +	if opts.noLines {
> +		prefix = "  "
> +		lengthener = "    "
> +	}
> +
> +	var indentation string
> +	for i := 0; i < depth; i++ {
> +		indentation += lengthener
> +	}
> +	prefix += indentation
> +
> +	return prefix
> +}
> +
> +func decrDepth(depth *int) {
> +	*depth -= 1

Pointer magic required?
Ah I see, it's below. Ok.

> +}
> +
> +// prints passed subproject and all its subprojects recursively
> +func printProjAndSubs(subproj *qm.ProjectSubTreeS, 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

More a FIXME than a TODO, isn't it? Never mind.

> +	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.ProjectSubTreeS{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)

Should we create a helper for this? For example `PrintError()`?

>  		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
> 
Reply to thread Export thread (mbox)