diff --git a/as/registry.go b/as/registry.go new file mode 100644 index 0000000..b2ea9a1 --- /dev/null +++ b/as/registry.go @@ -0,0 +1,105 @@ +package extension + +import ( + "fmt" + "sync" +) + +// The global mapping of names to specific vocabularies and the plugins +// providing their implementations. +var ( + pluginRegistry = make(map[string]Plugin) + pluginRegistryMu sync.RWMutex +) + +func RegisteredPlugins() []string { + pluginRegistryMu.RLock() + defer pluginRegistryMu.RUnlock() + s := make([]string, 0, len(pluginRegistry)) + for name, _ := range pluginRegistry { + s = append(s, name) + } + return s +} + +/* BEGIN TEST SPECIFIC STUFF */ + +func unregisterAllExtensions() { + pluginRegistryMu.Lock() + defer pluginRegistryMu.Unlock() + pluginRegistry = make(map[string]Plugin) +} + +/* BEGIN STATIC SPECIFIC STUFF */ + +func RegisterExtension(name string, plugin Plugin) { + pluginRegistryMu.Lock() + defer pluginRegistryMu.Unlock() + if plugin == nil { + panic("nil plugin passed to RegisterExtension") + } + if _, ok := pluginRegistry[name]; ok { + panic("duplicate RegisterExtension name: " + name) + } + pluginRegistry[name] = plugin +} + +/* BEGIN DYNAMIC SPECIFIC STUFF */ + +var ( + pluginProvider PluginProvider = nil + pluginProviderMu sync.RWMutex +) + +type NamedPlugin struct { + Name string + Plugin Plugin +} + +type PluginProvider interface { + Add() <-chan NamedPlugin + Remove() <-chan string + // nil errors will be sent in response to successful calls to Add or Remove + Err() chan<- error + Done() <-chan struct{} +} + +func RegisterPluginProvider(p PluginProvider) { + pluginProviderMu.Lock() + defer pluginProviderMu.Unlock() + if pluginProvider != nil { + panic("RegisterPluginProvider already called") + } + pluginProvider = p + go func() { + addCh := pluginProvider.Add() + remCh := pluginProvider.Remove() + errCh := pluginProvider.Err() + doneCh := pluginProvider.Done() + for { + select { + case namedPlugin := <-addCh: + pluginRegistryMu.Lock() // LOCK + if _, ok := pluginRegistry[namedPlugin.Name]; ok { + errCh <- fmt.Errorf("cannot add: plugin already registered: %s", namedPlugin.Name) + } else { + pluginRegistry[namedPlugin.Name] = namedPlugin.Plugin + errCh <- nil + } + pluginRegistryMu.Unlock() // UNLOCK + case removeName := <-remCh: + pluginRegistryMu.Lock() // LOCK + if _, ok := pluginRegistry[removeName]; !ok { + errCh <- fmt.Errorf("cannot remove: plugin not registered: %s", removeName) + } else { + delete(pluginRegistry, removeName) + errCh <- nil + } + pluginRegistryMu.Unlock() // UNLOCK + case <-doneCh: + close(errCh) + return + } + } + }() +} diff --git a/as/vocabulary.go b/as/vocabulary.go new file mode 100644 index 0000000..45e2ece --- /dev/null +++ b/as/vocabulary.go @@ -0,0 +1,83 @@ +// Package extension outlines the interfaces required for an ActivityStreams +// extension. +package extension + +import () + +// TODO: Figure out the new "Resolver" and "Property-Resolver" algorithms for +// the plugin architecture. Two kinds: for deserialization, and for dispatching. + +// Plugin +type Plugin interface { + NewTypeResolver() +} + +// Resolver +type Resolver interface { + Deserialize(map[string]interface{}) error +} + +type PropertyResolver interface { +} + +/* +EXISTING USAGE: + +vocab.IsActivityType() +vocab.HasTypeTombstone() +vocab.HasTypeCreate() +vocab.Serializer +vocab.Typer +vocab.ObjectType +vocab.LinkType +vocab.ActivityType +vocab.IntransitiveActivityType +vocab.CollectionType +vocab.CollectionPageType +vocab.OrderedCollectionType +vocab.OrderedCollectionPageType +vocab.FollowType +vocab.TombstoneType +vocab.Object +vocab.Create +vocab.Accept +vocab.Reject +vocab.OrderedCollection +vocab.OrderedCollectionPage +vocab.Collection +vocab.CollectionPage +vocab.Tombstone + +streams.Resolver +streams.Create +streams.Update +streams.Delete +streams.Follow +streams.Accept +streams.Reject +streams.Add +streams.Remove +streams.Like +streams.Undo +streams.Block +streams.Announce +streams.Dislike +streams.Flag +streams.Ignore +streams.Invite +streams.Join +streams.Leave +streams.Listen +streams.Move +streams.Offer +streams.Question +streams.Read +streams.TentativeAccept +streams.TentativeReject +streams.Travel +streams.View +streams.Collection +streams.OrderedCollection +streams.CollectionPage +streams.OrderedCollectionPage +*/