diff --git a/internal/resource/id_monotonic.go b/internal/resource/id_monotonic.go index 20f7f5c..d84d896 100644 --- a/internal/resource/id_monotonic.go +++ b/internal/resource/id_monotonic.go @@ -11,7 +11,8 @@ var ( mu sync.Mutex ) -// MonotonicID is a unique identifier for a pug resource. +// MonotonicID is an identifier based on an ever-increasing serial number, and a +// kind to differentiate it from other kinds of identifiers. type MonotonicID struct { Serial uint Kind Kind @@ -30,12 +31,7 @@ func NewMonotonicID(kind Kind) MonotonicID { } } -// String provides a human readable description. +// String provides a human readable representation of the identifier. func (id MonotonicID) String() string { return fmt.Sprintf("#%d", id.Serial) } - -// GetID implements Identifiable -func (id MonotonicID) GetID() ID { - return id -} diff --git a/internal/state/resource.go b/internal/state/resource.go index f4052c5..6a7d62f 100644 --- a/internal/state/resource.go +++ b/internal/state/resource.go @@ -8,21 +8,16 @@ import ( // Resource is a pug state resource. type Resource struct { - resource.MonotonicID - + ID resource.MonotonicID WorkspaceID resource.ID Address ResourceAddress Attributes map[string]any Tainted bool } -func (r *Resource) String() string { - return string(r.Address) -} - func newResource(workspaceID resource.ID, addr ResourceAddress, attrs json.RawMessage) (*Resource, error) { res := &Resource{ - MonotonicID: resource.NewMonotonicID(resource.StateResource), + ID: resource.NewMonotonicID(resource.StateResource), WorkspaceID: workspaceID, Address: addr, } @@ -32,4 +27,10 @@ func newResource(workspaceID resource.ID, addr ResourceAddress, attrs json.RawMe return res, nil } +func (r *Resource) String() string { + return string(r.Address) +} + +func (r *Resource) GetID() resource.ID { return r.ID } + type ResourceAddress string diff --git a/internal/state/service.go b/internal/state/service.go index fb00e7c..1acc59d 100644 --- a/internal/state/service.go +++ b/internal/state/service.go @@ -54,7 +54,7 @@ func (s *Service) Get(workspaceID resource.ID) (*State, error) { func (s *Service) GetResource(resourceID resource.ID) (*Resource, error) { for _, state := range s.cache.List() { for _, res := range state.Resources { - if res.MonotonicID == resourceID { + if res.ID == resourceID { return res, nil } } diff --git a/internal/state/state.go b/internal/state/state.go index 5fbc520..fc19592 100644 --- a/internal/state/state.go +++ b/internal/state/state.go @@ -12,8 +12,7 @@ import ( ) type State struct { - resource.MonotonicID - + ID resource.MonotonicID WorkspaceID resource.ID Resources map[ResourceAddress]*Resource Serial int64 @@ -24,7 +23,7 @@ type State struct { func newState(workspaceID resource.ID, r io.Reader) (*State, error) { // Default to a serial of -1 to indicate that there is no state yet. state := &State{ - MonotonicID: resource.NewMonotonicID(resource.State), + ID: resource.NewMonotonicID(resource.State), WorkspaceID: workspaceID, Serial: -1, } @@ -88,6 +87,8 @@ func newState(workspaceID resource.ID, r io.Reader) (*State, error) { return state, nil } +func (s *State) GetID() resource.ID { return s.ID } + func (s *State) LogValue() slog.Value { return slog.GroupValue( slog.Int("resources", len(s.Resources)), diff --git a/internal/task/group.go b/internal/task/group.go index ff2a85b..d06a83c 100644 --- a/internal/task/group.go +++ b/internal/task/group.go @@ -10,8 +10,7 @@ import ( ) type Group struct { - resource.MonotonicID - + ID resource.MonotonicID Created time.Time Command string Tasks []*Task @@ -23,8 +22,8 @@ func newGroup(service *Service, specs ...Spec) (*Group, error) { return nil, errors.New("no specs provided") } g := &Group{ - MonotonicID: resource.NewMonotonicID(resource.TaskGroup), - Created: time.Now(), + ID: resource.NewMonotonicID(resource.TaskGroup), + Created: time.Now(), } // Validate specifications. There are some settings that are incompatible // with one another within a task group. @@ -84,7 +83,8 @@ func newGroup(service *Service, specs ...Spec) (*Group, error) { return g, nil } -func (g *Group) String() string { return g.Command } +func (g *Group) String() string { return g.Command } +func (g *Group) GetID() resource.ID { return g.ID } func (g *Group) IncludesTask(taskID resource.MonotonicID) bool { return slices.ContainsFunc(g.Tasks, func(tgt *Task) bool { diff --git a/internal/task/service.go b/internal/task/service.go index d15eab6..1304e6f 100644 --- a/internal/task/service.go +++ b/internal/task/service.go @@ -109,7 +109,7 @@ func (s *Service) CreateGroup(specs ...Spec) (*Group, error) { // AddGroup adds a task group to the DB. func (s *Service) AddGroup(group *Group) { - s.groups.Add(group.MonotonicID, group) + s.groups.Add(group.ID, group) } // Enqueue moves the task onto the global queue for processing. diff --git a/internal/tui/helpers.go b/internal/tui/helpers.go index 8f85d21..bb56d3a 100644 --- a/internal/tui/helpers.go +++ b/internal/tui/helpers.go @@ -360,7 +360,7 @@ func (h *Helpers) createTaskGroup(specs ...task.Spec) tea.Msg { if err != nil { return ReportError(fmt.Errorf("creating task group: %w", err)) } - return NewNavigationMsg(TaskGroupKind, WithParent(group.MonotonicID)) + return NewNavigationMsg(TaskGroupKind, WithParent(group.ID)) } func (h *Helpers) Move(workspaceID resource.ID, from state.ResourceAddress) tea.Cmd { diff --git a/internal/tui/table/table_test.go b/internal/tui/table/table_test.go index cd97880..7528366 100644 --- a/internal/tui/table/table_test.go +++ b/internal/tui/table/table_test.go @@ -11,27 +11,28 @@ import ( ) var ( - resource0 = testResource{n: 0, MonotonicID: resource.NewMonotonicID(resource.Workspace)} - resource1 = testResource{n: 1, MonotonicID: resource.NewMonotonicID(resource.Workspace)} - resource2 = testResource{n: 2, MonotonicID: resource.NewMonotonicID(resource.Workspace)} - resource3 = testResource{n: 3, MonotonicID: resource.NewMonotonicID(resource.Workspace)} - resource4 = testResource{n: 4, MonotonicID: resource.NewMonotonicID(resource.Workspace)} - resource5 = testResource{n: 5, MonotonicID: resource.NewMonotonicID(resource.Workspace)} + resource0 = testResource{n: 0, ID: resource.NewMonotonicID(resource.Workspace)} + resource1 = testResource{n: 1, ID: resource.NewMonotonicID(resource.Workspace)} + resource2 = testResource{n: 2, ID: resource.NewMonotonicID(resource.Workspace)} + resource3 = testResource{n: 3, ID: resource.NewMonotonicID(resource.Workspace)} + resource4 = testResource{n: 4, ID: resource.NewMonotonicID(resource.Workspace)} + resource5 = testResource{n: 5, ID: resource.NewMonotonicID(resource.Workspace)} ) type testResource struct { - resource.MonotonicID - - n int + ID resource.MonotonicID + n int } +func (r *testResource) GetID() resource.ID { return r.ID } + // setupTest sets up a table test with several rows. Each row is keyed with an // int, and the row item is an int corresponding to the key, for ease of // testing. The rows are sorted from lowest int to highest int. -func setupTest() Model[testResource] { - renderer := func(v testResource) RenderedRow { return nil } +func setupTest() Model[*testResource] { + renderer := func(v *testResource) RenderedRow { return nil } tbl := New(nil, renderer, 0, 0, - WithSortFunc(func(i, j testResource) int { + WithSortFunc(func(i, j *testResource) int { if i.n < j.n { return -1 } @@ -39,12 +40,12 @@ func setupTest() Model[testResource] { }), ) tbl.SetItems( - resource0, - resource1, - resource2, - resource3, - resource4, - resource5, + &resource0, + &resource1, + &resource2, + &resource3, + &resource4, + &resource5, ) return tbl } @@ -55,7 +56,7 @@ func TestTable_CurrentRow(t *testing.T) { got, ok := tbl.CurrentRow() require.True(t, ok) - assert.Equal(t, resource0, got) + assert.Equal(t, &resource0, got) } func TestTable_ToggleSelection(t *testing.T) { @@ -64,7 +65,7 @@ func TestTable_ToggleSelection(t *testing.T) { tbl.ToggleSelection() assert.Len(t, tbl.selected, 1) - assert.Equal(t, resource0, tbl.selected[resource0.MonotonicID]) + assert.Equal(t, &resource0, tbl.selected[resource0.ID]) } func TestTable_SelectRange(t *testing.T) { @@ -86,32 +87,32 @@ func TestTable_SelectRange(t *testing.T) { }, { name: "select no range when cursor is on the only selected row", - selected: []resource.ID{resource0.MonotonicID}, - want: []resource.ID{resource0.MonotonicID}, + selected: []resource.ID{resource0.ID}, + want: []resource.ID{resource0.ID}, }, { name: "select all rows between selected top row and cursor on last row", - selected: []resource.ID{resource0.MonotonicID}, // first row - cursor: 5, // last row - want: []resource.ID{resource0.MonotonicID, resource1.MonotonicID, resource2.MonotonicID, resource3.MonotonicID, resource4.MonotonicID, resource5.MonotonicID}, + selected: []resource.ID{resource0.ID}, // first row + cursor: 5, // last row + want: []resource.ID{resource0.ID, resource1.ID, resource2.ID, resource3.ID, resource4.ID, resource5.ID}, }, { name: "select rows between selected top row and cursor in third row", - selected: []resource.ID{resource0.MonotonicID}, // first row - cursor: 2, // third row - want: []resource.ID{resource0.MonotonicID, resource1.MonotonicID, resource2.MonotonicID}, + selected: []resource.ID{resource0.ID}, // first row + cursor: 2, // third row + want: []resource.ID{resource0.ID, resource1.ID, resource2.ID}, }, { name: "select rows between selected top row and cursor in third row, ignoring selected last row", - selected: []resource.ID{resource0.MonotonicID, resource5.MonotonicID}, // first and last row - cursor: 2, // third row - want: []resource.ID{resource0.MonotonicID, resource1.MonotonicID, resource2.MonotonicID, resource5.MonotonicID}, + selected: []resource.ID{resource0.ID, resource5.ID}, // first and last row + cursor: 2, // third row + want: []resource.ID{resource0.ID, resource1.ID, resource2.ID, resource5.ID}, }, { name: "select rows between cursor in third row and selected last row", - selected: []resource.ID{resource5.MonotonicID}, // last row - cursor: 2, // third row - want: []resource.ID{resource2.MonotonicID, resource3.MonotonicID, resource4.MonotonicID, resource5.MonotonicID}, + selected: []resource.ID{resource5.ID}, // last row + cursor: 2, // third row + want: []resource.ID{resource2.ID, resource3.ID, resource4.ID, resource5.ID}, }, } for _, tt := range tests { diff --git a/internal/tui/task/group_list.go b/internal/tui/task/group_list.go index fd160a1..cdc9ecb 100644 --- a/internal/tui/task/group_list.go +++ b/internal/tui/task/group_list.go @@ -40,7 +40,7 @@ func (m *GroupListMaker) Make(_ resource.ID, width, height int) (tui.ChildModel, renderer := func(g *task.Group) table.RenderedRow { row := table.RenderedRow{ commandColumn.Key: g.Command, - taskGroupID.Key: g.MonotonicID.String(), + taskGroupID.Key: g.ID.String(), taskGroupCount.Key: m.Helpers.GroupReport(g, true), ageColumn.Key: tui.Ago(time.Now(), g.Created), } @@ -83,7 +83,7 @@ func (m *groupList) Update(msg tea.Msg) tea.Cmd { switch { case key.Matches(msg, groupListKeys.Enter): if row, ok := m.table.CurrentRow(); ok { - return tui.NavigateTo(tui.TaskGroupKind, tui.WithParent(row.MonotonicID)) + return tui.NavigateTo(tui.TaskGroupKind, tui.WithParent(row.ID)) } } } diff --git a/internal/tui/workspace/resource_list.go b/internal/tui/workspace/resource_list.go index 5b4508d..312ea08 100644 --- a/internal/tui/workspace/resource_list.go +++ b/internal/tui/workspace/resource_list.go @@ -124,7 +124,7 @@ func (m *resourceList) Update(msg tea.Msg) tea.Cmd { switch { case key.Matches(msg, resourcesKeys.Enter): if row, ok := m.CurrentRow(); ok { - return tui.NavigateTo(tui.ResourceKind, tui.WithParent(row.MonotonicID)) + return tui.NavigateTo(tui.ResourceKind, tui.WithParent(row.ID)) } case key.Matches(msg, resourcesKeys.Reload): if m.reloading {