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		return err
 42	}
 43
 44	areaKey, _ := cmd.Flags().GetString("area")
 45	showAll, _ := cmd.Flags().GetBool("all")
 46	usingDefault := areaKey == "" && !showAll && cfg.Defaults.Area != ""
 47
 48	areas := filterAreas(cfg, areaKey, showAll)
 49	if len(areas) == 0 {
 50		fmt.Fprintln(cmd.OutOrStdout(), "No areas found")
 51
 52		return nil
 53	}
 54
 55	goals := collectGoals(areas)
 56	if len(goals) == 0 {
 57		if usingDefault {
 58			fmt.Fprintf(cmd.OutOrStdout(), "No goals configured in %s area\n", cfg.Defaults.Area)
 59		} else {
 60			fmt.Fprintln(cmd.OutOrStdout(), "No goals configured")
 61		}
 62
 63		return nil
 64	}
 65
 66	jsonFlag, _ := cmd.Flags().GetBool("json")
 67	if jsonFlag {
 68		return outputJSON(cmd, goals)
 69	}
 70
 71	return outputTable(cmd, goals)
 72}
 73
 74type goalWithArea struct {
 75	Key     string `json:"key"`
 76	Name    string `json:"name"`
 77	ID      string `json:"id"`
 78	AreaKey string `json:"area_key"`
 79}
 80
 81func filterAreas(cfg *config.Config, areaKey string, showAll bool) []config.Area {
 82	if showAll {
 83		return cfg.Areas
 84	}
 85
 86	if areaKey == "" {
 87		areaKey = cfg.Defaults.Area
 88	}
 89
 90	if areaKey == "" {
 91		return cfg.Areas
 92	}
 93
 94	area := cfg.AreaByKey(areaKey)
 95	if area == nil {
 96		return nil
 97	}
 98
 99	return []config.Area{*area}
100}
101
102func collectGoals(areas []config.Area) []goalWithArea {
103	var goals []goalWithArea
104
105	for _, area := range areas {
106		for _, goal := range area.Goals {
107			goals = append(goals, goalWithArea{
108				Key:     goal.Key,
109				Name:    goal.Name,
110				ID:      goal.ID,
111				AreaKey: area.Key,
112			})
113		}
114	}
115
116	return goals
117}
118
119func outputJSON(cmd *cobra.Command, goals []goalWithArea) error {
120	enc := json.NewEncoder(cmd.OutOrStdout())
121	enc.SetIndent("", "  ")
122
123	if err := enc.Encode(goals); err != nil {
124		return fmt.Errorf("encoding JSON: %w", err)
125	}
126
127	return nil
128}
129
130func outputTable(cmd *cobra.Command, goals []goalWithArea) error {
131	rows := make([][]string, 0, len(goals))
132
133	for _, goal := range goals {
134		rows = append(rows, []string{goal.Key, goal.Name, goal.AreaKey})
135	}
136
137	tbl := table.New().
138		Headers("KEY", "NAME", "AREA").
139		Rows(rows...).
140		StyleFunc(func(row, col int) lipgloss.Style {
141			if row == table.HeaderRow {
142				return ui.TableHeaderStyle()
143			}
144
145			return lipgloss.NewStyle()
146		}).
147		Border(ui.TableBorder())
148
149	fmt.Fprintln(cmd.OutOrStdout(), tbl.Render())
150
151	return nil
152}