diff --git a/hooks.go b/hooks.go index 8707d0e..bbe12a8 100644 --- a/hooks.go +++ b/hooks.go @@ -16,9 +16,6 @@ package trakr import "github.com/jzelinskie/trakr/bittorrent" -// HookConstructor is a function used to create a new instance of a Hook. -type HookConstructor func(interface{}) (Hook, error) - // Hook abstracts the concept of anything that needs to interact with a // BitTorrent client's request and response to a BitTorrent tracker. type Hook interface { @@ -26,6 +23,9 @@ type Hook interface { HandleScrape(context.Context, bittorrent.ScrapeRequest, bittorrent.ScrapeResponse) error } +// HookConstructor is a function used to create a new instance of a Hook. +type HookConstructor func(interface{}) (Hook, error) + var preHooks = make(map[string]HookConstructor) // RegisterPreHook makes a HookConstructor available by the provided name. @@ -44,7 +44,7 @@ func RegisterPreHook(name string, con HookConstructor) { // NewPreHook creates an instance of the given PreHook by name. func NewPreHook(name string, config interface{}) (Hook, error) { - con := preHooks[name] + con, ok := preHooks[name] if !ok { return nil, fmt.Errorf("trakr: unknown PreHook %q (forgotten import?)", name) } @@ -69,7 +69,7 @@ func RegisterPostHook(name string, con HookConstructor) { // NewPostHook creates an instance of the given PostHook by name. func NewPostHook(name string, config interface{}) (Hook, error) { - con := preHooks[name] + con, ok := preHooks[name] if !ok { return nil, fmt.Errorf("trakr: unknown PostHook %q (forgotten import?)", name) } diff --git a/storage.go b/storage.go new file mode 100644 index 0000000..8d7056c --- /dev/null +++ b/storage.go @@ -0,0 +1,97 @@ +package trakr + +import ( + "fmt" + "time" + + "github.com/jzelinskie/trakr/bittorrent" + "github.com/jzelinskie/trakr/stopper" +) + +// ErrResourceDoesNotExist is the error returned by all delete methods in the +// store if the requested resource does not exist. +var ErrResourceDoesNotExist = bittorrent.ClientError(errors.New("resource does not exist")) + +// PeerStore is an interface that abstracts the interactions of storing and +// manipulating Peers such that it can be implemented for various data stores. +type PeerStore interface { + // PutSeeder adds a Seeder to the Swarm identified by the provided infoHash. + PutSeeder(infoHash bittorrent.InfoHash, p bittorrent.Peer) error + + // DeleteSeeder removes a Seeder from the Swarm identified by the provided + // infoHash. + // + // If the Swarm or Peer does not exist, this function should return + // ErrResourceDoesNotExist. + DeleteSeeder(infoHash bittorrent.InfoHash, p bittorrent.Peer) error + + // PutLeecher adds a Leecher to the Swarm identified by the provided + // infoHash. + PutLeecher(infoHash bittorrent.InfoHash, p bittorrent.Peer) error + + // DeleteLeecher removes a Leecher from the Swarm identified by the provided + // infoHash. + // + // If the Swarm or Peer does not exist, this function should return + // ErrResourceDoesNotExist. + DeleteLeecher(infoHash bittorrent.InfoHash, p bittorrent.Peer) error + + // GraduateLeecher promotes a Leecher to a Seeder in the Swarm identified by + // the provided infoHash. + // + // If the given Peer is not present as a Leecher, add the Peer as a Seeder + // and return no error. + GraduateLeecher(infoHash bittorrent.InfoHash, p bittorrent.Peer) error + + // AnnouncePeers is a best effort attempt to return Peers from the Swarm + // identified by the provided infoHash. The returned Peers are required to be + // either all IPv4 or all IPv6. + // + // The returned Peers should strive be: + // - as close to length equal to numWant as possible without going over + // - all IPv4 or all IPv6 depending on the provided ipv6 boolean + // - if seeder is true, should ideally return more leechers than seeders + // - if seeder is false, should ideally return more seeders than leechers + AnnouncePeers(infoHash bittorrent.InfoHash, seeder bool, numWant int, ipv6 bool) (peers []bittorrent.Peer, err error) + + // CollectGarbage deletes all Peers from the PeerStore which are older than + // the cutoff time. This function must be able to execute while other methods + // on this interface are being executed in parallel. + CollectGarbage(cutoff time.Time) error + + // Stopper is an interface that expects a Stop method to stops the PeerStore. + // For more details see the documentation in the stopper package. + stopper.Stopper +} + +// PeerStoreConstructor is a function used to create a new instance of a +// PeerStore. +type PeerStoreConstructor func(interface{}) (PeerStore, error) + +var peerStores = make(map[string]PeerStoreConstructors) + +// RegisterPeerStore makes a PeerStoreConstructor available by the provided +// name. +// +// If this function is called twice with the same name or if the +// PeerStoreConstructor is nil, it panics. +func RegisterPeerStore(name string, con PeerStoreConstructor) { + if con == nil { + panic("trakr: could not register nil PeerStoreConstructor") + } + + if _, dup := peerStore[name]; dup { + panic("trakr: could not register duplicate PeerStoreConstructor: " + name) + } + + peerStores[name] = con +} + +// NewPeerStore creates an instance of the given PeerStore by name. +func NewPeerStore(name, config interface{}) (PeerStore, error) { + con, ok := peerStores[name] + if !ok { + return nil, fmt.Errorf("trakr: unknown PeerStore %q (forgotten import?)", name) + } + return con(config) +}