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}