118 lines
2.5 KiB
Go
118 lines
2.5 KiB
Go
package tui
|
|
|
|
import (
|
|
"proxyctl/internal/app"
|
|
"proxyctl/internal/target"
|
|
)
|
|
|
|
type dashboardPane uint8
|
|
|
|
const (
|
|
targetListPane dashboardPane = iota
|
|
targetDetailsPane
|
|
)
|
|
|
|
// targetCatalog owns the dashboard's product-ordered Target projection. It is
|
|
// the only place where availability filtering and identity-based selection are
|
|
// combined, so rendering and input handling cannot accidentally select by a
|
|
// transient row number.
|
|
type targetCatalog struct {
|
|
items []app.TargetSnapshot
|
|
}
|
|
|
|
func newTargetCatalog(snapshot app.Snapshot, includeUnavailable bool) targetCatalog {
|
|
items := make([]app.TargetSnapshot, 0, len(snapshot.Targets))
|
|
for _, item := range snapshot.Targets {
|
|
if item.Available || includeUnavailable || item.DetectionPhase != app.TargetReady {
|
|
items = append(items, item)
|
|
}
|
|
}
|
|
return targetCatalog{items: items}
|
|
}
|
|
|
|
func (c targetCatalog) firstID() string {
|
|
if len(c.items) == 0 {
|
|
return ""
|
|
}
|
|
return c.items[0].ID
|
|
}
|
|
|
|
func (c targetCatalog) index(id string) int {
|
|
for i := range c.items {
|
|
if c.items[i].ID == id {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
func (c targetCatalog) selected(id string) (app.TargetSnapshot, bool) {
|
|
index := c.index(id)
|
|
if index < 0 {
|
|
return app.TargetSnapshot{}, false
|
|
}
|
|
return c.items[index], true
|
|
}
|
|
|
|
func (c targetCatalog) move(id string, delta int) string {
|
|
if len(c.items) == 0 {
|
|
return ""
|
|
}
|
|
index := c.index(id)
|
|
if index < 0 {
|
|
index = 0
|
|
}
|
|
index = max(0, min(len(c.items)-1, index+delta))
|
|
return c.items[index].ID
|
|
}
|
|
|
|
// retain keeps an identity when possible and otherwise chooses the product row
|
|
// nearest its former position. This is used after refresh and visibility
|
|
// changes; terminal resize never needs to touch selection at all.
|
|
func (c targetCatalog) retain(id string, formerIndex int) string {
|
|
if c.index(id) >= 0 {
|
|
return id
|
|
}
|
|
if len(c.items) == 0 {
|
|
return ""
|
|
}
|
|
return c.items[max(0, min(len(c.items)-1, formerIndex))].ID
|
|
}
|
|
|
|
type targetStateCounts struct {
|
|
enabled int
|
|
disabled int
|
|
external int
|
|
drifted int
|
|
checking int
|
|
failed int
|
|
}
|
|
|
|
func countAvailableTargetStates(snapshot app.Snapshot) targetStateCounts {
|
|
var counts targetStateCounts
|
|
for _, item := range snapshot.Targets {
|
|
switch item.DetectionPhase {
|
|
case app.TargetChecking:
|
|
counts.checking++
|
|
continue
|
|
case app.TargetDetectionFailed:
|
|
counts.failed++
|
|
continue
|
|
}
|
|
if !item.Available {
|
|
continue
|
|
}
|
|
switch item.State {
|
|
case target.Enabled:
|
|
counts.enabled++
|
|
case target.Disabled:
|
|
counts.disabled++
|
|
case target.External:
|
|
counts.external++
|
|
case target.Drifted:
|
|
counts.drifted++
|
|
}
|
|
}
|
|
return counts
|
|
}
|