forked from security/hakurei
internal/store: expose save via handle
The handle is otherwise inaccessible without the compat interface. This change also moves compatibility methods to separate adapter structs to avoid inadvertently using them. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
@@ -22,20 +22,20 @@ type Compat interface {
|
|||||||
List() (identities []int, err error)
|
List() (identities []int, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) Do(identity int, f func(c Cursor)) (bool, error) {
|
|
||||||
if h, err := s.Handle(identity); err != nil {
|
|
||||||
return false, err
|
|
||||||
} else {
|
|
||||||
return h.do(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// storeAdapter satisfies [Compat] via [Store].
|
// storeAdapter satisfies [Compat] via [Store].
|
||||||
type storeAdapter struct {
|
type storeAdapter struct {
|
||||||
msg message.Msg
|
msg message.Msg
|
||||||
*Store
|
*Store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s storeAdapter) Do(identity int, f func(c Cursor)) (bool, error) {
|
||||||
|
if h, err := s.Handle(identity); err != nil {
|
||||||
|
return false, err
|
||||||
|
} else {
|
||||||
|
return handleAdapter{h}.do(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s storeAdapter) List() ([]int, error) {
|
func (s storeAdapter) List() ([]int, error) {
|
||||||
segments, n, err := s.Segments()
|
segments, n, err := s.Segments()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -71,8 +71,11 @@ type Cursor interface {
|
|||||||
Len() (int, error)
|
Len() (int, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleAdapter satisfies [Cursor] via [Handle].
|
||||||
|
type handleAdapter struct{ *Handle }
|
||||||
|
|
||||||
// do implements [Compat.Do] on [Handle].
|
// do implements [Compat.Do] on [Handle].
|
||||||
func (h *Handle) do(f func(c Cursor)) (bool, error) {
|
func (h handleAdapter) do(f func(c Cursor)) (bool, error) {
|
||||||
if unlock, err := h.Lock(); err != nil {
|
if unlock, err := h.Lock(); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
} else {
|
} else {
|
||||||
@@ -85,15 +88,13 @@ func (h *Handle) do(f func(c Cursor)) (bool, error) {
|
|||||||
|
|
||||||
/* these compatibility methods must only be called while fileMu is held */
|
/* these compatibility methods must only be called while fileMu is held */
|
||||||
|
|
||||||
func (h *Handle) Save(state *hst.State) error {
|
func (h handleAdapter) Save(state *hst.State) error { _, err := h.Handle.Save(state); return err }
|
||||||
return (&EntryHandle{nil, h.Path.Append(state.ID.String()), state.ID}).Save(state)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handle) Destroy(id hst.ID) error {
|
func (h handleAdapter) Destroy(id hst.ID) error {
|
||||||
return (&EntryHandle{nil, h.Path.Append(id.String()), id}).Destroy()
|
return (&EntryHandle{nil, h.Path.Append(id.String()), id}).Destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) Load() (map[hst.ID]*hst.State, error) {
|
func (h handleAdapter) Load() (map[hst.ID]*hst.State, error) {
|
||||||
entries, n, err := h.Entries()
|
entries, n, err := h.Entries()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -114,7 +115,7 @@ func (h *Handle) Load() (map[hst.ID]*hst.State, error) {
|
|||||||
return r, err
|
return r, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) Len() (int, error) {
|
func (h handleAdapter) Len() (int, error) {
|
||||||
entries, _, err := h.Entries()
|
entries, _, err := h.Entries()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
|
|||||||
@@ -54,11 +54,11 @@ func (eh *EntryHandle) Destroy() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save encodes [hst.State] and writes it to the underlying file.
|
// save encodes [hst.State] and writes it to the underlying file.
|
||||||
// An error is returned if a file already exists with the same identifier.
|
// An error is returned if a file already exists with the same identifier.
|
||||||
// Save does not validate the embedded [hst.Config].
|
// save does not validate the embedded [hst.Config].
|
||||||
// A non-nil error returned by Save is of type [hst.AppError].
|
// A non-nil error returned by save is of type [hst.AppError].
|
||||||
func (eh *EntryHandle) Save(state *hst.State) error {
|
func (eh *EntryHandle) save(state *hst.State) error {
|
||||||
f, err := eh.open(os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
|
f, err := eh.open(os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -123,6 +123,16 @@ func (h *Handle) Lock() (unlock func(), err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save attempts to save [hst.State] as a segment entry, and returns its [EntryHandle].
|
||||||
|
// Must be called while holding [Handle.Lock].
|
||||||
|
// An error is returned if an entry already exists with the same identifier.
|
||||||
|
// Save does not validate the embedded [hst.Config].
|
||||||
|
// A non-nil error returned by Save is of type [hst.AppError].
|
||||||
|
func (h *Handle) Save(state *hst.State) (*EntryHandle, error) {
|
||||||
|
eh := EntryHandle{nil, h.Path.Append(state.ID.String()), state.ID}
|
||||||
|
return &eh, eh.save(state)
|
||||||
|
}
|
||||||
|
|
||||||
// Entries returns an iterator over all [EntryHandle] held in this segment.
|
// Entries returns an iterator over all [EntryHandle] held in this segment.
|
||||||
// Must be called while holding [Handle.Lock].
|
// Must be called while holding [Handle.Lock].
|
||||||
// A non-nil error attached to a [EntryHandle] indicates a malformed identifier and is of type [hst.AppError].
|
// A non-nil error attached to a [EntryHandle] indicates a malformed identifier and is of type [hst.AppError].
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ func newHandle(base *check.Absolute, identity int) *store.Handle
|
|||||||
//go:linkname open hakurei.app/internal/store.(*EntryHandle).open
|
//go:linkname open hakurei.app/internal/store.(*EntryHandle).open
|
||||||
func open(eh *store.EntryHandle, flag int, perm os.FileMode) (*os.File, error)
|
func open(eh *store.EntryHandle, flag int, perm os.FileMode) (*os.File, error)
|
||||||
|
|
||||||
|
//go:linkname save hakurei.app/internal/store.(*EntryHandle).save
|
||||||
|
func save(eh *store.EntryHandle, state *hst.State) error
|
||||||
|
|
||||||
func TestStateEntryHandle(t *testing.T) {
|
func TestStateEntryHandle(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -44,7 +47,7 @@ func TestStateEntryHandle(t *testing.T) {
|
|||||||
if err := eh.Destroy(); !reflect.DeepEqual(err, wantErr()) {
|
if err := eh.Destroy(); !reflect.DeepEqual(err, wantErr()) {
|
||||||
t.Errorf("destroy: error = %v, want %v", err, wantErr())
|
t.Errorf("destroy: error = %v, want %v", err, wantErr())
|
||||||
}
|
}
|
||||||
if err := eh.Save(nil); !reflect.DeepEqual(err, wantErr()) {
|
if err := save(&eh, nil); !reflect.DeepEqual(err, wantErr()) {
|
||||||
t.Errorf("save: error = %v, want %v", err, wantErr())
|
t.Errorf("save: error = %v, want %v", err, wantErr())
|
||||||
}
|
}
|
||||||
if _, err := eh.Load(nil); !reflect.DeepEqual(err, wantErr()) {
|
if _, err := eh.Load(nil); !reflect.DeepEqual(err, wantErr()) {
|
||||||
@@ -90,7 +93,7 @@ func TestStateEntryHandle(t *testing.T) {
|
|||||||
eh := store.EntryHandle{Pathname: check.MustAbs(t.TempDir()).Append("entry"),
|
eh := store.EntryHandle{Pathname: check.MustAbs(t.TempDir()).Append("entry"),
|
||||||
ID: newTemplateState().ID}
|
ID: newTemplateState().ID}
|
||||||
|
|
||||||
if err := eh.Save(newTemplateState()); err != nil {
|
if err := save(&eh, newTemplateState()); err != nil {
|
||||||
t.Fatalf("save: error = %v", err)
|
t.Fatalf("save: error = %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user