list.go

  1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
  2//
  3// SPDX-License-Identifier: AGPL-3.0-or-later
  4
  5package area
  6
  7import (
  8	"encoding/json"
  9	"fmt"
 10
 11	"git.secluded.site/lune/internal/config"
 12	"git.secluded.site/lune/internal/ui"
 13	"github.com/charmbracelet/lipgloss"
 14	"github.com/charmbracelet/lipgloss/table"
 15	"github.com/spf13/cobra"
 16)
 17
 18// ListCmd lists configured areas and their goals.
 19var ListCmd = &cobra.Command{
 20	Use:   "list",
 21	Short: "List configured areas",
 22	Long: `List areas configured in lune.
 23
 24Areas are copied from the Lunatask desktop app during 'lune init'.
 25Each area may have goals associated with it.`,
 26	RunE: runList,
 27}
 28
 29func init() {
 30	ListCmd.Flags().Bool("json", false, "Output as JSON")
 31}
 32
 33func runList(cmd *cobra.Command, _ []string) error {
 34	cfg, err := config.Load()
 35	if err != nil {
 36		return err
 37	}
 38
 39	if len(cfg.Areas) == 0 {
 40		fmt.Fprintln(cmd.OutOrStdout(), "No areas configured")
 41
 42		return nil
 43	}
 44
 45	jsonFlag, _ := cmd.Flags().GetBool("json")
 46	if jsonFlag {
 47		return outputJSON(cmd, cfg.Areas)
 48	}
 49
 50	return outputTable(cmd, cfg.Areas)
 51}
 52
 53func outputJSON(cmd *cobra.Command, areas []config.Area) error {
 54	enc := json.NewEncoder(cmd.OutOrStdout())
 55	enc.SetIndent("", "  ")
 56
 57	if err := enc.Encode(areas); err != nil {
 58		return fmt.Errorf("encoding JSON: %w", err)
 59	}
 60
 61	return nil
 62}
 63
 64func outputTable(cmd *cobra.Command, areas []config.Area) error {
 65	rows := make([][]string, 0, len(areas))
 66	truncated := false
 67
 68	for _, area := range areas {
 69		goals, wasTruncated := formatGoals(area.Goals)
 70		if wasTruncated {
 71			truncated = true
 72		}
 73
 74		rows = append(rows, []string{area.Key, area.Name, goals})
 75	}
 76
 77	tbl := table.New().
 78		Headers("KEY", "NAME", "GOALS").
 79		Rows(rows...).
 80		StyleFunc(func(row, col int) lipgloss.Style {
 81			if row == table.HeaderRow {
 82				return ui.TableHeaderStyle()
 83			}
 84
 85			return lipgloss.NewStyle()
 86		}).
 87		Border(ui.TableBorder())
 88
 89	fmt.Fprintln(cmd.OutOrStdout(), tbl.Render())
 90
 91	if truncated {
 92		fmt.Fprintln(cmd.OutOrStdout(), ui.Hint.Render("(+N) = more goals; run 'lune goal list -a AREA' to see all"))
 93	}
 94
 95	return nil
 96}
 97
 98func formatGoals(goals []config.Goal) (string, bool) {
 99	if len(goals) == 0 {
100		return "-", false
101	}
102
103	keys := make([]string, len(goals))
104	for i, g := range goals {
105		keys[i] = g.Key
106	}
107
108	if len(keys) <= 3 {
109		return joinKeys(keys), false
110	}
111
112	return joinKeys(keys[:3]) + fmt.Sprintf(" (+%d)", len(keys)-3), true
113}
114
115func joinKeys(keys []string) string {
116	result := ""
117
118	for i, k := range keys {
119		if i > 0 {
120			result += ", "
121		}
122
123		result += k
124	}
125
126	return result
127}