list.go

  1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
  2//
  3// SPDX-License-Identifier: AGPL-3.0-or-later
  4
  5package goal
  6
  7import (
  8	"encoding/json"
  9	"fmt"
 10
 11	"git.secluded.site/lune/internal/completion"
 12	"git.secluded.site/lune/internal/config"
 13	"git.secluded.site/lune/internal/ui"
 14	"github.com/charmbracelet/lipgloss"
 15	"github.com/charmbracelet/lipgloss/table"
 16	"github.com/spf13/cobra"
 17)
 18
 19// ListCmd lists configured goals.
 20var ListCmd = &cobra.Command{
 21	Use:   "list",
 22	Short: "List configured goals",
 23	Long: `List goals configured in lune.
 24
 25By default, shows goals from the default area (if configured).
 26Use --area to specify a different area, or --all to show all goals.`,
 27	RunE: runList,
 28}
 29
 30func init() {
 31	ListCmd.Flags().StringP("area", "a", "", "Filter by area key")
 32	ListCmd.Flags().Bool("all", false, "Show goals from all areas")
 33	ListCmd.Flags().Bool("json", false, "Output as JSON")
 34
 35	_ = ListCmd.RegisterFlagCompletionFunc("area", completion.Areas)
 36}
 37
 38func runList(cmd *cobra.Command, _ []string) error {
 39	cfg, err := config.Load()
 40	if err != nil {
 41		fmt.Fprintln(cmd.ErrOrStderr(), ui.Error.Render("Config not found; run 'lune init' to configure areas"))
 42
 43		return err
 44	}
 45
 46	areaKey, _ := cmd.Flags().GetString("area")
 47	showAll, _ := cmd.Flags().GetBool("all")
 48	usingDefault := areaKey == "" && !showAll && cfg.Defaults.Area != ""
 49
 50	areas := filterAreas(cfg, areaKey, showAll)
 51	if len(areas) == 0 {
 52		fmt.Fprintln(cmd.OutOrStdout(), "No areas found")
 53
 54		return nil
 55	}
 56
 57	goals := collectGoals(areas)
 58	if len(goals) == 0 {
 59		if usingDefault {
 60			fmt.Fprintf(cmd.OutOrStdout(), "No goals configured in %s area\n", cfg.Defaults.Area)
 61		} else {
 62			fmt.Fprintln(cmd.OutOrStdout(), "No goals configured")
 63		}
 64
 65		return nil
 66	}
 67
 68	jsonFlag, _ := cmd.Flags().GetBool("json")
 69	if jsonFlag {
 70		return outputJSON(cmd, goals)
 71	}
 72
 73	return outputTable(cmd, goals)
 74}
 75
 76type goalWithArea struct {
 77	Key     string `json:"key"`
 78	Name    string `json:"name"`
 79	ID      string `json:"id"`
 80	AreaKey string `json:"area_key"`
 81}
 82
 83func filterAreas(cfg *config.Config, areaKey string, showAll bool) []config.Area {
 84	if showAll {
 85		return cfg.Areas
 86	}
 87
 88	if areaKey == "" {
 89		areaKey = cfg.Defaults.Area
 90	}
 91
 92	if areaKey == "" {
 93		return cfg.Areas
 94	}
 95
 96	area := cfg.AreaByKey(areaKey)
 97	if area == nil {
 98		return nil
 99	}
100
101	return []config.Area{*area}
102}
103
104func collectGoals(areas []config.Area) []goalWithArea {
105	var goals []goalWithArea
106
107	for _, area := range areas {
108		for _, goal := range area.Goals {
109			goals = append(goals, goalWithArea{
110				Key:     goal.Key,
111				Name:    goal.Name,
112				ID:      goal.ID,
113				AreaKey: area.Key,
114			})
115		}
116	}
117
118	return goals
119}
120
121func outputJSON(cmd *cobra.Command, goals []goalWithArea) error {
122	enc := json.NewEncoder(cmd.OutOrStdout())
123	enc.SetIndent("", "  ")
124
125	if err := enc.Encode(goals); err != nil {
126		return fmt.Errorf("encoding JSON: %w", err)
127	}
128
129	return nil
130}
131
132func outputTable(cmd *cobra.Command, goals []goalWithArea) error {
133	rows := make([][]string, 0, len(goals))
134
135	for _, goal := range goals {
136		rows = append(rows, []string{goal.Key, goal.Name, goal.AreaKey})
137	}
138
139	tbl := table.New().
140		Headers("KEY", "NAME", "AREA").
141		Rows(rows...).
142		StyleFunc(func(row, col int) lipgloss.Style {
143			if row == table.HeaderRow {
144				return ui.Bold
145			}
146
147			return lipgloss.NewStyle()
148		}).
149		Border(lipgloss.HiddenBorder())
150
151	fmt.Fprintln(cmd.OutOrStdout(), tbl.Render())
152
153	return nil
154}