list.go

  1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
  2//
  3// SPDX-License-Identifier: AGPL-3.0-or-later
  4
  5package person
  6
  7import (
  8	"encoding/json"
  9	"fmt"
 10
 11	"git.secluded.site/go-lunatask"
 12	"git.secluded.site/lune/internal/client"
 13	"git.secluded.site/lune/internal/completion"
 14	"git.secluded.site/lune/internal/ui"
 15	"github.com/charmbracelet/lipgloss"
 16	"github.com/charmbracelet/lipgloss/table"
 17	"github.com/spf13/cobra"
 18)
 19
 20// ListCmd lists people. Exported for potential use by shortcuts.
 21var ListCmd = &cobra.Command{
 22	Use:   "list",
 23	Short: "List people",
 24	Long: `List people from Lunatask.
 25
 26Note: Due to end-to-end encryption, names are not available
 27through the API. Only metadata is shown.`,
 28	RunE: runList,
 29}
 30
 31func init() {
 32	ListCmd.Flags().StringP("relationship", "r", "", "Filter by relationship strength")
 33	ListCmd.Flags().String("source", "", "Filter by source")
 34	ListCmd.Flags().String("source-id", "", "Filter by source ID")
 35	ListCmd.Flags().Bool("json", false, "Output as JSON")
 36
 37	_ = ListCmd.RegisterFlagCompletionFunc("relationship", completion.Relationships)
 38}
 39
 40func runList(cmd *cobra.Command, _ []string) error {
 41	apiClient, err := client.New()
 42	if err != nil {
 43		return err
 44	}
 45
 46	opts := buildListOptions(cmd)
 47
 48	people, err := ui.Spin("Fetching people…", func() ([]lunatask.Person, error) {
 49		return apiClient.ListPeople(cmd.Context(), opts)
 50	})
 51	if err != nil {
 52		return err
 53	}
 54
 55	relFilter, _ := cmd.Flags().GetString("relationship")
 56	if relFilter != "" {
 57		people = filterByRelationship(people, relFilter)
 58	}
 59
 60	if len(people) == 0 {
 61		fmt.Fprintln(cmd.OutOrStdout(), "No people found")
 62
 63		return nil
 64	}
 65
 66	if mustGetBoolFlag(cmd, "json") {
 67		return outputJSON(cmd, people)
 68	}
 69
 70	return outputTable(cmd, people)
 71}
 72
 73func buildListOptions(cmd *cobra.Command) *lunatask.ListPeopleOptions {
 74	source, _ := cmd.Flags().GetString("source")
 75	sourceID, _ := cmd.Flags().GetString("source-id")
 76
 77	if source == "" && sourceID == "" {
 78		return nil
 79	}
 80
 81	opts := &lunatask.ListPeopleOptions{}
 82	if source != "" {
 83		opts.Source = &source
 84	}
 85
 86	if sourceID != "" {
 87		opts.SourceID = &sourceID
 88	}
 89
 90	return opts
 91}
 92
 93func filterByRelationship(people []lunatask.Person, rel string) []lunatask.Person {
 94	filtered := make([]lunatask.Person, 0, len(people))
 95
 96	for _, person := range people {
 97		if person.RelationshipStrength != nil && string(*person.RelationshipStrength) == rel {
 98			filtered = append(filtered, person)
 99		}
100	}
101
102	return filtered
103}
104
105func mustGetBoolFlag(cmd *cobra.Command, name string) bool {
106	f := cmd.Flags().Lookup(name)
107	if f == nil {
108		panic("flag not defined: " + name)
109	}
110
111	return f.Value.String() == "true"
112}
113
114func outputJSON(cmd *cobra.Command, people []lunatask.Person) error {
115	enc := json.NewEncoder(cmd.OutOrStdout())
116	enc.SetIndent("", "  ")
117
118	if err := enc.Encode(people); err != nil {
119		return fmt.Errorf("encoding JSON: %w", err)
120	}
121
122	return nil
123}
124
125func outputTable(cmd *cobra.Command, people []lunatask.Person) error {
126	rows := make([][]string, 0, len(people))
127
128	for _, person := range people {
129		rel := "-"
130		if person.RelationshipStrength != nil {
131			rel = string(*person.RelationshipStrength)
132		}
133
134		created := ui.FormatDate(person.CreatedAt)
135
136		rows = append(rows, []string{person.ID, rel, created})
137	}
138
139	tbl := table.New().
140		Headers("ID", "RELATIONSHIP", "CREATED").
141		Rows(rows...).
142		StyleFunc(func(row, col int) lipgloss.Style {
143			if row == table.HeaderRow {
144				return ui.TableHeaderStyle()
145			}
146
147			return lipgloss.NewStyle()
148		}).
149		Border(ui.TableBorder())
150
151	fmt.Fprintln(cmd.OutOrStdout(), tbl.Render())
152
153	return nil
154}