1package app
2
3import (
4 "context"
5 "database/sql"
6 "maps"
7 "sync"
8 "time"
9
10 "github.com/kujtimiihoxha/termai/internal/db"
11 "github.com/kujtimiihoxha/termai/internal/history"
12 "github.com/kujtimiihoxha/termai/internal/logging"
13 "github.com/kujtimiihoxha/termai/internal/lsp"
14 "github.com/kujtimiihoxha/termai/internal/message"
15 "github.com/kujtimiihoxha/termai/internal/permission"
16 "github.com/kujtimiihoxha/termai/internal/session"
17)
18
19type App struct {
20 Sessions session.Service
21 Messages message.Service
22 Files history.Service
23 Permissions permission.Service
24
25 LSPClients map[string]*lsp.Client
26
27 clientsMutex sync.RWMutex
28
29 watcherCancelFuncs []context.CancelFunc
30 cancelFuncsMutex sync.Mutex
31 watcherWG sync.WaitGroup
32}
33
34func New(ctx context.Context, conn *sql.DB) *App {
35 q := db.New(conn)
36 sessions := session.NewService(q)
37 messages := message.NewService(q)
38 files := history.NewService(q)
39
40 app := &App{
41 Sessions: sessions,
42 Messages: messages,
43 Files: files,
44 Permissions: permission.NewPermissionService(),
45 LSPClients: make(map[string]*lsp.Client),
46 }
47
48 app.initLSPClients(ctx)
49
50 return app
51}
52
53// Shutdown performs a clean shutdown of the application
54func (app *App) Shutdown() {
55 // Cancel all watcher goroutines
56 app.cancelFuncsMutex.Lock()
57 for _, cancel := range app.watcherCancelFuncs {
58 cancel()
59 }
60 app.cancelFuncsMutex.Unlock()
61 app.watcherWG.Wait()
62
63 // Perform additional cleanup for LSP clients
64 app.clientsMutex.RLock()
65 clients := make(map[string]*lsp.Client, len(app.LSPClients))
66 maps.Copy(clients, app.LSPClients)
67 app.clientsMutex.RUnlock()
68
69 for name, client := range clients {
70 shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
71 if err := client.Shutdown(shutdownCtx); err != nil {
72 logging.Error("Failed to shutdown LSP client", "name", name, "error", err)
73 }
74 cancel()
75 }
76}