Browse Source

Add godoc to plugin subpackage

Tyler Sommer 7 months ago
Signed by: tyler-sommer GPG Key ID: C09C010500DBD008
1 changed files with 53 additions and 12 deletions
  1. +53

+ 53
- 12
plugin/plugin.go View File

@ -8,57 +8,80 @@ import (
// A Plugin is the basic interface that all squircy3 plugins must implement.
type Plugin interface {
// Name returns the unique name for the Plugin.
Name() string
type InitHandler interface {
// PluginInitHandler is implemented by types that want to be notified when
// another plugin is initialized.
type PluginInitHandler interface {
// HandlePluginInit is called after each other Plugin is initialized.
// Plugin initialization occurs when the Configure method is called on
// a plugin.Manager.
// ShutdownHandler is implemented by types that want to perform some action
// when the application is shutting down.
type ShutdownHandler interface {
// HandleShutdown is called when the application begins shutting down.
// All ShutdownHandlers are invoked concurrently, but each has a limited
// amount of time to gracefully clean up before the application forcefully
// exits.
// An Initializer is a type that can create a Plugin implementation.
type Initializer interface {
// Initialize creates the corresponding Plugin for this Initializer.
Initialize(*Manager) (Plugin, error)
// An InitializerFunc is a function that implements Initializer.
type InitializerFunc func(*Manager) (Plugin, error)
func (f InitializerFunc) Initialize(m *Manager) (Plugin, error) {
return f(m)
// InitializeFromFile returns an Initializer that will attempt to load the
// specified plugin shared library from the filesystem.
// Shared library plugins must be built as a `main` package and must have
// an "Initialize" function defined at the package level. The "Initialize"
// function must be compatible with the Initialize method on the Initializer
// interface. That is, it must have the signature:
// func Initialize(*Manager) (Plugin, error)
func InitializeFromFile(p string) Initializer {
return InitializerFunc(func(m *Manager) (Plugin, error) {
pl, err := plugin.Open(p)
if err != nil {
return nil, errors.Wrapf(err, "unable to open plugin (%s)", p)
return nil, errors.Wrapf(err, "unable (%s) to open plugin", p)
in, err := pl.Lookup("Initialize")
if err != nil {
return nil, errors.Wrapf(err, "plugin does not export Initialize (%s)", p)
return nil, errors.Wrapf(err, "plugin (%s) does not export Initialize", p)
fn, ok := in.(func(*Manager) (Plugin, error))
if !ok {
err := errors.Errorf("plugin has invalid type for Initialize (%s): expected func(*plugin.Manager) (plugin.Plugin, error)", p)
return nil, err
return nil, errors.Errorf("plugin (%s) has invalid type for Initialize: expected func(*plugin.Manager) (plugin.Plugin, error), got %T", p, in)
plg, err := fn(m)
if err != nil {
return nil, errors.Wrapf(err, "plugin init failed (%s)", p)
return nil, errors.Wrapf(err, "plugin (%s) init failed", p)
return plg, nil
// A Manager controls the loading and configuration of plugins.
type Manager struct {
plugins []Initializer
loaded map[string]Plugin
onInit []InitHandler
onInit []PluginInitHandler
onShutdown []ShutdownHandler
mu sync.RWMutex
@ -75,18 +98,25 @@ func NewManager(plugins ...string) *Manager {
func (m *Manager) OnPluginInit(h InitHandler) {
// OnPluginInit adds the given PluginInitHandler to be called when a plugin
// is initialized.
func (m *Manager) OnPluginInit(h PluginInitHandler) {
m.onInit = append(m.onInit, h)
// OnShutdown adds the given ShutdownHandler to be called when the appliation
// is shutting down.
func (m *Manager) OnShutdown(h ShutdownHandler) {
m.onShutdown = append(m.onShutdown, h)
// Shutdown begins the shut down process.
// Each ShutdownHandler is called concurrently and this method returns after
// all handlers have completed.
func (m *Manager) Shutdown() {
hs := make([]ShutdownHandler, len(m.onShutdown))
@ -103,6 +133,7 @@ func (m *Manager) Shutdown() {
// Loaded returns a list of plugins currently loaded.
func (m *Manager) Loaded() []string {
@ -113,6 +144,7 @@ func (m *Manager) Loaded() []string {
return ns
// Lookup returns the given plugin by name, or an error if it isn't loaded.
func (m *Manager) Lookup(name string) (Plugin, error) {
@ -122,16 +154,22 @@ func (m *Manager) Lookup(name string) (Plugin, error) {
return nil, errors.Errorf("no plugin named %s", name)
// Register adds a plugin Initializer to the Manager.
// Invoke all the registered Initializers by calling Configure on the Manager.
func (m *Manager) Register(initfn Initializer) {
m.plugins = append(m.plugins, initfn)
// RegisterFunc is a shorthand method to register an InitializerFunc without
// extra type assertions.
func (m *Manager) RegisterFunc(initfn func(m *Manager) (Plugin, error)) {
// Configure attempts to load and configure all registered plugins.
// An error will be returned for each failed initialization.
func (m *Manager) Configure() []error {
var errs []error
@ -148,13 +186,13 @@ func (m *Manager) Configure() []error {
// get a fresh copy of init handlers before each init;
// plugins may add handlers in this loop and those should be accounted
// for on subsequent inits.
inits := append([]InitHandler{}, m.onInit...)
inits := append([]PluginInitHandler{}, m.onInit...)
// Manager should be unlocked while the plugin initializes; the plugin
// is free to use the Manager itself during init.
plg, err := p.Initialize(m)
if err != nil {
errs = append(errs, errors.Wrap(err, "plugin init failed"))
errs = append(errs, errors.Wrapf(err, "plugin (unknown) init failed"))
pn := plg.Name()
@ -163,7 +201,7 @@ func (m *Manager) Configure() []error {
if !ok {
// not already loaded, add it
m.loaded[pn] = plg
if ih, ok := plg.(InitHandler); ok {
if ih, ok := plg.(PluginInitHandler); ok {
m.onInit = append(m.onInit, ih)
if sh, ok := plg.(ShutdownHandler); ok {
@ -174,7 +212,7 @@ func (m *Manager) Configure() []error {
if ok {
// plugin was already loaded
errs = append(errs, errors.Errorf("plugin already loaded %s", pn))
errs = append(errs, errors.Errorf("plugin (%s) already loaded", pn))
// run other plugin init handlers
@ -185,6 +223,9 @@ func (m *Manager) Configure() []error {
return errs
// Main is a helper function that can be used to provide a consistent main
// func. Plugins aren't normally executed anyway, but Go requires that every
// "main" package have a "main" func.
func Main(pluginName string) {
fmt.Println(pluginName, "- a plugin for squircy3")