@@ -24,6 +24,9 @@ type AppServiceInterface interface {
2424 GetLayout () ui.LayoutInterface
2525 Boot () (err error )
2626 BuildApp ()
27+ SetBrewfilePath (path string )
28+ IsBrewfileMode () bool
29+ GetBrewfilePackages () * []models.Package
2730}
2831
2932// AppService manages the application state, Homebrew integration, and UI components.
@@ -40,6 +43,10 @@ type AppService struct {
4043 showOnlyCasks bool
4144 brewVersion string
4245
46+ // Brewfile support
47+ brewfilePath string
48+ brewfilePackages * []models.Package
49+
4350 brewService BrewServiceInterface
4451 selfUpdateService SelfUpdateServiceInterface
4552 ioService IOServiceInterface
@@ -63,6 +70,9 @@ var NewAppService = func() AppServiceInterface {
6370 showOnlyLeaves : false ,
6471 showOnlyCasks : false ,
6572 brewVersion : "-" ,
73+
74+ brewfilePath : "" ,
75+ brewfilePackages : new ([]models.Package ),
6676 }
6777
6878 // Initialize services
@@ -73,8 +83,11 @@ var NewAppService = func() AppServiceInterface {
7383 return s
7484}
7585
76- func (s * AppService ) GetApp () * tview.Application { return s .app }
77- func (s * AppService ) GetLayout () ui.LayoutInterface { return s .layout }
86+ func (s * AppService ) GetApp () * tview.Application { return s .app }
87+ func (s * AppService ) GetLayout () ui.LayoutInterface { return s .layout }
88+ func (s * AppService ) SetBrewfilePath (path string ) { s .brewfilePath = path }
89+ func (s * AppService ) IsBrewfileMode () bool { return s .brewfilePath != "" }
90+ func (s * AppService ) GetBrewfilePackages () * []models.Package { return s .brewfilePackages }
7891
7992// Boot initializes the application by setting up Homebrew and loading formulae data.
8093func (s * AppService ) Boot () (err error ) {
@@ -91,6 +104,42 @@ func (s *AppService) Boot() (err error) {
91104 // Initialize packages and filteredPackages
92105 s .packages = s .brewService .GetPackages ()
93106 * s .filteredPackages = * s .packages
107+
108+ // If Brewfile is specified, parse it and filter packages
109+ if s .IsBrewfileMode () {
110+ if err = s .loadBrewfilePackages (); err != nil {
111+ return fmt .Errorf ("failed to load Brewfile: %v" , err )
112+ }
113+ }
114+
115+ return nil
116+ }
117+
118+ // loadBrewfilePackages parses the Brewfile and creates a filtered package list
119+ func (s * AppService ) loadBrewfilePackages () error {
120+ entries , err := s .brewService .ParseBrewfile (s .brewfilePath )
121+ if err != nil {
122+ return err
123+ }
124+
125+ // Create a map for quick lookup
126+ packageMap := make (map [string ]models.PackageType )
127+ for _ , entry := range entries {
128+ if entry .IsCask {
129+ packageMap [entry .Name ] = models .PackageTypeCask
130+ } else {
131+ packageMap [entry .Name ] = models .PackageTypeFormula
132+ }
133+ }
134+
135+ // Filter packages to only include those in the Brewfile
136+ * s .brewfilePackages = []models.Package {}
137+ for _ , pkg := range * s .packages {
138+ if pkgType , exists := packageMap [pkg .Name ]; exists && pkgType == pkg .Type {
139+ * s .brewfilePackages = append (* s .brewfilePackages , pkg )
140+ }
141+ }
142+
94143 return nil
95144}
96145
@@ -112,41 +161,51 @@ func (s *AppService) search(searchText string, scrollToTop bool) {
112161 uniquePackages := make (map [string ]bool )
113162
114163 // Determine the source list based on the current filter state
164+ // If Brewfile mode is active, use brewfilePackages as the base source
115165 sourceList := s .packages
166+ if s .IsBrewfileMode () {
167+ sourceList = s .brewfilePackages
168+ }
169+
170+ // Apply filters on the base source list (either all packages or Brewfile packages)
116171 if s .showOnlyInstalled && ! s .showOnlyOutdated {
117- sourceList = & []models.Package {}
118- for _ , info := range * s . packages {
172+ filteredSource : = & []models.Package {}
173+ for _ , info := range * sourceList {
119174 if info .LocallyInstalled {
120- * sourceList = append (* sourceList , info )
175+ * filteredSource = append (* filteredSource , info )
121176 }
122177 }
178+ sourceList = filteredSource
123179 }
124180
125181 if s .showOnlyOutdated {
126- sourceList = & []models.Package {}
127- for _ , info := range * s . packages {
182+ filteredSource : = & []models.Package {}
183+ for _ , info := range * sourceList {
128184 if info .LocallyInstalled && info .Outdated {
129- * sourceList = append (* sourceList , info )
185+ * filteredSource = append (* filteredSource , info )
130186 }
131187 }
188+ sourceList = filteredSource
132189 }
133190
134191 if s .showOnlyLeaves {
135- sourceList = & []models.Package {}
136- for _ , info := range * s . packages {
192+ filteredSource : = & []models.Package {}
193+ for _ , info := range * sourceList {
137194 if info .LocallyInstalled && info .InstalledOnRequest {
138- * sourceList = append (* sourceList , info )
195+ * filteredSource = append (* filteredSource , info )
139196 }
140197 }
198+ sourceList = filteredSource
141199 }
142200
143201 if s .showOnlyCasks {
144- sourceList = & []models.Package {}
145- for _ , info := range * s . packages {
202+ filteredSource : = & []models.Package {}
203+ for _ , info := range * sourceList {
146204 if info .Type == models .PackageTypeCask {
147- * sourceList = append (* sourceList , info )
205+ * filteredSource = append (* filteredSource , info )
148206 }
149207 }
208+ sourceList = filteredSource
150209 }
151210
152211 if searchText == "" {
@@ -185,7 +244,14 @@ func (s *AppService) search(searchText string, scrollToTop bool) {
185244func (s * AppService ) forceRefreshResults () {
186245 _ = s .brewService .SetupData (true )
187246 s .packages = s .brewService .GetPackages ()
188- * s .filteredPackages = * s .packages
247+
248+ // If in Brewfile mode, reload the filtered packages
249+ if s .IsBrewfileMode () {
250+ _ = s .loadBrewfilePackages ()
251+ * s .filteredPackages = * s .brewfilePackages
252+ } else {
253+ * s .filteredPackages = * s .packages
254+ }
189255
190256 s .app .QueueUpdateDraw (func () {
191257 s .search (s .layout .GetSearch ().Field ().GetText (), false )
@@ -251,7 +317,15 @@ func (s *AppService) setResults(data *[]models.Package, scrollToTop bool) {
251317func (s * AppService ) BuildApp () {
252318 // Build the layout
253319 s .layout .Setup ()
254- s .layout .GetHeader ().Update (AppName , AppVersion , s .brewVersion )
320+
321+ // Update header and enable Brewfile mode features if needed
322+ headerName := AppName
323+ if s .IsBrewfileMode () {
324+ headerName = fmt .Sprintf ("%s [Brewfile Mode]" , AppName )
325+ s .layout .GetSearch ().Field ().SetLabel ("Search (Brewfile): " )
326+ s .ioService .EnableBrewfileMode () // Add Install All action
327+ }
328+ s .layout .GetHeader ().Update (headerName , AppVersion , s .brewVersion )
255329
256330 // Evaluate if there is a new version available
257331 // This is done in a goroutine to avoid blocking the UI during startup
@@ -264,7 +338,11 @@ func (s *AppService) BuildApp() {
264338 if latestVersion , err := s .selfUpdateService .CheckForUpdates (ctx ); err == nil && latestVersion != AppVersion {
265339 s .app .QueueUpdateDraw (func () {
266340 AppVersion = fmt .Sprintf ("%s ([orange]New Version Available: %s[-])" , AppVersion , latestVersion )
267- s .layout .GetHeader ().Update (AppName , AppVersion , s .brewVersion )
341+ headerName := AppName
342+ if s .IsBrewfileMode () {
343+ headerName = fmt .Sprintf ("%s [Brewfile Mode]" , AppName )
344+ }
345+ s .layout .GetHeader ().Update (headerName , AppVersion , s .brewVersion )
268346 })
269347 }
270348 }()
@@ -295,6 +373,13 @@ func (s *AppService) BuildApp() {
295373 s .app .SetRoot (s .layout .Root (), true )
296374 s .app .SetFocus (s .layout .GetTable ().View ())
297375
298- go s .updateHomeBrew () // Update Async the Homebrew formulae
299- s .setResults (s .packages , true ) // Set the results
376+ go s .updateHomeBrew () // Update Async the Homebrew formulae
377+
378+ // Set initial results based on mode
379+ if s .IsBrewfileMode () {
380+ * s .filteredPackages = * s .brewfilePackages // Sync filteredPackages
381+ s .setResults (s .brewfilePackages , true ) // Show only Brewfile packages
382+ } else {
383+ s .setResults (s .packages , true ) // Show all packages
384+ }
300385}
0 commit comments