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 := apiClient.ListPeople(cmd.Context(), opts)
 49	if err != nil {
 50		return err
 51	}
 52
 53	relFilter, _ := cmd.Flags().GetString("relationship")
 54	if relFilter != "" {
 55		people = filterByRelationship(people, relFilter)
 56	}
 57
 58	if len(people) == 0 {
 59		fmt.Fprintln(cmd.OutOrStdout(), "No people found")
 60
 61		return nil
 62	}
 63
 64	if mustGetBoolFlag(cmd, "json") {
 65		return outputJSON(cmd, people)
 66	}
 67
 68	return outputTable(cmd, people)
 69}
 70
 71func buildListOptions(cmd *cobra.Command) *lunatask.ListPeopleOptions {
 72	source, _ := cmd.Flags().GetString("source")
 73	sourceID, _ := cmd.Flags().GetString("source-id")
 74
 75	if source == "" && sourceID == "" {
 76		return nil
 77	}
 78
 79	opts := &lunatask.ListPeopleOptions{}
 80	if source != "" {
 81		opts.Source = &source
 82	}
 83
 84	if sourceID != "" {
 85		opts.SourceID = &sourceID
 86	}
 87
 88	return opts
 89}
 90
 91func filterByRelationship(people []lunatask.Person, rel string) []lunatask.Person {
 92	filtered := make([]lunatask.Person, 0, len(people))
 93
 94	for _, person := range people {
 95		if person.RelationshipStrength != nil && string(*person.RelationshipStrength) == rel {
 96			filtered = append(filtered, person)
 97		}
 98	}
 99
100	return filtered
101}
102
103func mustGetBoolFlag(cmd *cobra.Command, name string) bool {
104	f := cmd.Flags().Lookup(name)
105	if f == nil {
106		panic("flag not defined: " + name)
107	}
108
109	return f.Value.String() == "true"
110}
111
112func outputJSON(cmd *cobra.Command, people []lunatask.Person) error {
113	enc := json.NewEncoder(cmd.OutOrStdout())
114	enc.SetIndent("", "  ")
115
116	if err := enc.Encode(people); err != nil {
117		return fmt.Errorf("encoding JSON: %w", err)
118	}
119
120	return nil
121}
122
123func outputTable(cmd *cobra.Command, people []lunatask.Person) error {
124	rows := make([][]string, 0, len(people))
125
126	for _, person := range people {
127		rel := "-"
128		if person.RelationshipStrength != nil {
129			rel = string(*person.RelationshipStrength)
130		}
131
132		created := ui.FormatDate(person.CreatedAt)
133
134		rows = append(rows, []string{person.ID, rel, created})
135	}
136
137	tbl := table.New().
138		Headers("ID", "RELATIONSHIP", "CREATED").
139		Rows(rows...).
140		StyleFunc(func(row, col int) lipgloss.Style {
141			if row == table.HeaderRow {
142				return ui.Bold
143			}
144
145			return lipgloss.NewStyle()
146		}).
147		Border(lipgloss.HiddenBorder())
148
149	fmt.Fprintln(cmd.OutOrStdout(), tbl.Render())
150
151	return nil
152}