// This implements the missinggo resource.Provider interface over a Possum handle. package possumResource import ( "fmt" "github.com/anacrolix/missinggo/v2/resource" possum "github.com/anacrolix/possum/go" "io" "io/fs" "os" ) type Provider struct { Handle *possum.Handle } func (p Provider) NewInstance(s string) (resource.Instance, error) { return &instance{ key: s, handle: p.Handle, }, nil } func (p Provider) DeletePrefix(prefix string) error { return p.Handle.DeletePrefix([]byte(prefix)) } var _ resource.Provider = Provider{} type instance struct { key string handle *possum.Handle } func (i *instance) Get() (_ io.ReadCloser, err error) { possumReader, err := i.handle.NewReader() if err != nil { return } defer func() { if err != nil { possumReader.End() } }() v, err := possumReader.Add(i.key) if err != nil { return } err = possumReader.Begin() if err != nil { return } retReader := io.NewSectionReader(v, 0, v.Stat().Size()) return struct { io.Reader io.Closer }{ retReader, possumReader, }, nil } func (i *instance) Put(reader io.Reader) (err error) { w, err := i.handle.NewWriter() if err != nil { err = fmt.Errorf("creating new possum writer: %w", err) return } defer func() { // TODO: There's no Writer.Drop. commitErr := w.Commit() if err == nil { err = commitErr } }() vw, err := w.StartNewValue() if err != nil { return } f, err := vw.NewFile(i.key) if err != nil { return } _, err = io.Copy(f, reader) f.Close() if err == nil { err = w.Stage([]byte(i.key), vw) } // TODO: Committing here since we only staged one thing and if it failed, we should be // committing nothing. There's no way to drop a Writer at the time of writing. return } func (i *instance) Stat() (fi os.FileInfo, err error) { fi, ok := i.handle.SingleStat(i.key) if !ok { err = fs.ErrNotExist return } return } func (i *instance) ReadAt(p []byte, off int64) (n int, err error) { return i.handle.SingleReadAt(i.key, off, p) } func (i *instance) WriteAt([]byte, int64) (int, error) { //TODO implement me panic("implement me") } func (i *instance) Delete() (err error) { fi, err := i.handle.SingleDelete(i.key) if err != nil { return } if !fi.Ok { err = fs.ErrNotExist } return } var _ interface { resource.Instance resource.DirInstance } = (*instance)(nil) func (i *instance) Readdirnames() (names []string, err error) { subKeys, err := i.handle.ListKeys(i.key + "/") if err != nil { return } // For now let's just return all the keys. If they have slashes, it might not be what the caller // expects. names = subKeys return }