9 "github.com/charmbracelet/crush/internal/db"
10 "github.com/charmbracelet/crush/internal/pubsub"
11 "github.com/google/uuid"
28type Service interface {
29 pubsub.Suscriber[File]
30 Create(ctx context.Context, sessionID, path, content string) (File, error)
31 CreateVersion(ctx context.Context, sessionID, path, content string) (File, error)
32 Get(ctx context.Context, id string) (File, error)
33 GetByPathAndSession(ctx context.Context, path, sessionID string) (File, error)
34 ListBySession(ctx context.Context, sessionID string) ([]File, error)
35 ListLatestSessionFiles(ctx context.Context, sessionID string) ([]File, error)
36 Delete(ctx context.Context, id string) error
37 DeleteSessionFiles(ctx context.Context, sessionID string) error
46func NewService(q *db.Queries, db *sql.DB) Service {
48 Broker: pubsub.NewBroker[File](),
54func (s *service) Create(ctx context.Context, sessionID, path, content string) (File, error) {
55 return s.createWithVersion(ctx, sessionID, path, content, InitialVersion)
58func (s *service) CreateVersion(ctx context.Context, sessionID, path, content string) (File, error) {
59 // Get the latest version for this path
60 files, err := s.q.ListFilesByPath(ctx, path)
66 // No previous versions, create initial
67 return s.Create(ctx, sessionID, path, content)
70 // Get the latest version
71 latestFile := files[0] // Files are ordered by version DESC, created_at DESC
72 nextVersion := latestFile.Version + 1
74 return s.createWithVersion(ctx, sessionID, path, content, nextVersion)
77func (s *service) createWithVersion(ctx context.Context, sessionID, path, content string, version int64) (File, error) {
78 // Maximum number of retries for transaction conflicts
83 // Retry loop for transaction conflicts
84 for attempt := range maxRetries {
85 // Start a transaction
86 tx, txErr := s.db.BeginTx(ctx, nil)
88 return File{}, fmt.Errorf("failed to begin transaction: %w", txErr)
91 // Create a new queries instance with the transaction
94 // Try to create the file within the transaction
95 dbFile, txErr := qtx.CreateFile(ctx, db.CreateFileParams{
96 ID: uuid.New().String(),
103 // Rollback the transaction
106 // Check if this is a uniqueness constraint violation
107 if strings.Contains(txErr.Error(), "UNIQUE constraint failed") {
108 if attempt < maxRetries-1 {
109 // If we have retries left, increment version and try again
117 // Commit the transaction
118 if txErr = tx.Commit(); txErr != nil {
119 return File{}, fmt.Errorf("failed to commit transaction: %w", txErr)
122 file = s.fromDBItem(dbFile)
123 s.Publish(pubsub.CreatedEvent, file)
130func (s *service) Get(ctx context.Context, id string) (File, error) {
131 dbFile, err := s.q.GetFile(ctx, id)
135 return s.fromDBItem(dbFile), nil
138func (s *service) GetByPathAndSession(ctx context.Context, path, sessionID string) (File, error) {
139 dbFile, err := s.q.GetFileByPathAndSession(ctx, db.GetFileByPathAndSessionParams{
141 SessionID: sessionID,
146 return s.fromDBItem(dbFile), nil
149func (s *service) ListBySession(ctx context.Context, sessionID string) ([]File, error) {
150 dbFiles, err := s.q.ListFilesBySession(ctx, sessionID)
154 files := make([]File, len(dbFiles))
155 for i, dbFile := range dbFiles {
156 files[i] = s.fromDBItem(dbFile)
161func (s *service) ListLatestSessionFiles(ctx context.Context, sessionID string) ([]File, error) {
162 dbFiles, err := s.q.ListLatestSessionFiles(ctx, sessionID)
166 files := make([]File, len(dbFiles))
167 for i, dbFile := range dbFiles {
168 files[i] = s.fromDBItem(dbFile)
173func (s *service) Delete(ctx context.Context, id string) error {
174 file, err := s.Get(ctx, id)
178 err = s.q.DeleteFile(ctx, id)
182 s.Publish(pubsub.DeletedEvent, file)
186func (s *service) DeleteSessionFiles(ctx context.Context, sessionID string) error {
187 files, err := s.ListBySession(ctx, sessionID)
191 for _, file := range files {
192 err = s.Delete(ctx, file.ID)
200func (s *service) fromDBItem(item db.File) File {
203 SessionID: item.SessionID,
205 Content: item.Content,
206 Version: item.Version,
207 CreatedAt: item.CreatedAt,
208 UpdatedAt: item.UpdatedAt,
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list