d715b053e7
Enables request-scoped cancellation, timeouts, and graceful shutdown for all database operations across API handlers, CLI commands, and TUI.
177 lines
3.6 KiB
Go
177 lines
3.6 KiB
Go
package cmd
|
||
|
||
import (
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/lerko/nib/internal/db"
|
||
"github.com/lerko/nib/internal/display"
|
||
"github.com/spf13/cobra"
|
||
)
|
||
|
||
var (
|
||
lsTag string
|
||
lsDate string
|
||
lsMonth string
|
||
lsFrom string
|
||
lsTo string
|
||
lsLimit int
|
||
lsAll bool
|
||
)
|
||
|
||
var lsCmd = &cobra.Command{
|
||
Use: "ls",
|
||
Short: "list entities in stream order",
|
||
RunE: runLs,
|
||
}
|
||
|
||
func init() {
|
||
lsCmd.Flags().StringVar(&lsTag, "tag", "", "filter by tag")
|
||
lsCmd.Flags().StringVar(&lsDate, "date", "", "filter by date (YYYY-MM-DD)")
|
||
lsCmd.Flags().StringVar(&lsMonth, "month", "", "filter by month (YYYY-MM)")
|
||
lsCmd.Flags().StringVar(&lsFrom, "from", "", "start date (YYYY-MM-DD)")
|
||
lsCmd.Flags().StringVar(&lsTo, "to", "", "end date (YYYY-MM-DD)")
|
||
lsCmd.Flags().IntVar(&lsLimit, "limit", 0, "max entities to show (default 50)")
|
||
lsCmd.Flags().BoolVar(&lsAll, "all", false, "include deleted entities")
|
||
}
|
||
|
||
func runLs(cmd *cobra.Command, _ []string) error {
|
||
store, err := openStore()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer store.Close()
|
||
|
||
p := db.DefaultListParams()
|
||
p.IncludeDeleted = lsAll
|
||
|
||
if lsTag != "" {
|
||
p.Tag = &lsTag
|
||
}
|
||
if lsLimit > 0 {
|
||
p.Limit = lsLimit
|
||
}
|
||
|
||
hasDateFilter := false
|
||
if lsDate != "" {
|
||
p.Date = &lsDate
|
||
hasDateFilter = true
|
||
}
|
||
if lsMonth != "" {
|
||
t, err := time.Parse("2006-01", lsMonth)
|
||
if err != nil {
|
||
return fmt.Errorf("bad --month format, use YYYY-MM")
|
||
}
|
||
from := t.Format("2006-01-02")
|
||
to := t.AddDate(0, 1, -1).Format("2006-01-02")
|
||
p.From = &from
|
||
p.To = &to
|
||
hasDateFilter = true
|
||
}
|
||
if lsFrom != "" {
|
||
if _, err := time.Parse("2006-01-02", lsFrom); err != nil {
|
||
return fmt.Errorf("bad --from format, use YYYY-MM-DD")
|
||
}
|
||
p.From = &lsFrom
|
||
hasDateFilter = true
|
||
}
|
||
if lsTo != "" {
|
||
if _, err := time.Parse("2006-01-02", lsTo); err != nil {
|
||
return fmt.Errorf("bad --to format, use YYYY-MM-DD")
|
||
}
|
||
p.To = &lsTo
|
||
hasDateFilter = true
|
||
}
|
||
if !hasDateFilter {
|
||
since := time.Now().UTC().Add(-48 * time.Hour)
|
||
p.Since = &since
|
||
}
|
||
|
||
entities, err := store.List(cmd.Context(), p)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
if len(entities) == 0 {
|
||
return nil
|
||
}
|
||
|
||
groups := groupByDate(entities)
|
||
for _, g := range groups {
|
||
fmt.Printf("── %s ──\n", g.label)
|
||
for _, e := range g.entities {
|
||
printEntity(e)
|
||
}
|
||
fmt.Println()
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
type dateGroup struct {
|
||
label string
|
||
entities []*db.Entity
|
||
}
|
||
|
||
func groupByDate(entities []*db.Entity) []dateGroup {
|
||
var groups []dateGroup
|
||
var current *dateGroup
|
||
|
||
for _, e := range entities {
|
||
label := formatDateLabel(e.CreatedAt)
|
||
if current == nil || current.label != label {
|
||
if current != nil {
|
||
groups = append(groups, *current)
|
||
}
|
||
current = &dateGroup{label: label}
|
||
}
|
||
current.entities = append(current.entities, e)
|
||
}
|
||
if current != nil {
|
||
groups = append(groups, *current)
|
||
}
|
||
return groups
|
||
}
|
||
|
||
func formatDateLabel(t time.Time) string {
|
||
return strings.ToLower(t.Format("Jan 2"))
|
||
}
|
||
|
||
func printEntity(e *db.Entity) {
|
||
glyph := display.DisplayGlyph(e.Glyph, e.CardType)
|
||
shortID := display.FormatID(e.ID)
|
||
|
||
label := e.Body
|
||
if e.Title != nil {
|
||
label = *e.Title
|
||
}
|
||
|
||
var line strings.Builder
|
||
fmt.Fprintf(&line, "%s %-40s", glyph, label)
|
||
|
||
if e.TimeAnchor != nil {
|
||
fmt.Fprintf(&line, " @%-5s", *e.TimeAnchor)
|
||
} else {
|
||
line.WriteString(" ")
|
||
}
|
||
|
||
var tagStr string
|
||
for _, tag := range e.Tags {
|
||
tagStr += " #" + tag
|
||
}
|
||
if tagStr != "" {
|
||
fmt.Fprintf(&line, " %-16s", strings.TrimSpace(tagStr))
|
||
} else {
|
||
line.WriteString(" ")
|
||
}
|
||
|
||
fmt.Fprintf(&line, " %s", shortID)
|
||
|
||
if e.UseCount > 0 {
|
||
fmt.Fprintf(&line, " (%d×)", e.UseCount)
|
||
}
|
||
|
||
fmt.Println(line.String())
|
||
}
|