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}