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}