1// Package filetracker provides functionality to track file reads in sessions.
2package filetracker
3
4import (
5 "context"
6 "log/slog"
7 "os"
8 "path/filepath"
9 "time"
10
11 "github.com/charmbracelet/crush/internal/db"
12)
13
14// Service defines the interface for tracking file reads in sessions.
15type Service interface {
16 // RecordRead records when a file was read.
17 RecordRead(ctx context.Context, sessionID, path string)
18
19 // LastReadTime returns when a file was last read.
20 // Returns zero time if never read.
21 LastReadTime(ctx context.Context, sessionID, path string) time.Time
22}
23
24type service struct {
25 q *db.Queries
26}
27
28// NewService creates a new file tracker service.
29func NewService(q *db.Queries) Service {
30 return &service{q: q}
31}
32
33// RecordRead records when a file was read.
34func (s *service) RecordRead(ctx context.Context, sessionID, path string) {
35 if err := s.q.RecordFileRead(ctx, db.RecordFileReadParams{
36 SessionID: sessionID,
37 Path: relpath(path),
38 }); err != nil {
39 slog.Error("Error recording file read", "error", err, "file", path)
40 }
41}
42
43// LastReadTime returns when a file was last read.
44// Returns zero time if never read.
45func (s *service) LastReadTime(ctx context.Context, sessionID, path string) time.Time {
46 readFile, err := s.q.GetFileRead(ctx, db.GetFileReadParams{
47 SessionID: sessionID,
48 Path: relpath(path),
49 })
50 if err != nil {
51 return time.Time{}
52 }
53
54 return time.Unix(readFile.ReadAt, 0)
55}
56
57func relpath(path string) string {
58 path = filepath.Clean(path)
59 basepath, err := os.Getwd()
60 if err != nil {
61 slog.Warn("Error getting basepath", "error", err)
62 return path
63 }
64 relpath, err := filepath.Rel(basepath, path)
65 if err != nil {
66 slog.Warn("Error getting relpath", "error", err)
67 return path
68 }
69 return relpath
70}