From 4165caa02e39328df732c45a573fe66d56569c1d Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Tue, 21 Mar 2023 08:55:55 +1100 Subject: [PATCH] Add `Exported() []string` to plugin API --- v3/examples/plugins/plugins/hashes/plugin.go | 6 ++++ v3/examples/plugins/store.json | 2 +- v3/pkg/application/bindings.go | 8 +++++ v3/pkg/application/plugins.go | 2 ++ v3/plugins/README.md | 29 ++++++++++++++++ v3/plugins/browser/plugin.go | 7 ++++ v3/plugins/kvstore/kvstore.go | 35 +++++++++++++++++--- 7 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 v3/plugins/README.md diff --git a/v3/examples/plugins/plugins/hashes/plugin.go b/v3/examples/plugins/plugins/hashes/plugin.go index 861b127aa..94b5fcee5 100644 --- a/v3/examples/plugins/plugins/hashes/plugin.go +++ b/v3/examples/plugins/plugins/hashes/plugin.go @@ -26,6 +26,12 @@ func (r *Plugin) Init(_ *application.App) error { return nil } +func (r *Plugin) Exported() []string { + return []string{ + "Generate", + } +} + // ---------------- Plugin Methods ---------------- type Hashes struct { diff --git a/v3/examples/plugins/store.json b/v3/examples/plugins/store.json index 6a107caa1..948bb52b0 100644 --- a/v3/examples/plugins/store.json +++ b/v3/examples/plugins/store.json @@ -1 +1 @@ -{"url":null,"url2":"https://reddit.com"} \ No newline at end of file +{"url2":"https://reddit.com"} \ No newline at end of file diff --git a/v3/pkg/application/bindings.go b/v3/pkg/application/bindings.go index 3e97ee28a..5a94ee9c9 100644 --- a/v3/pkg/application/bindings.go +++ b/v3/pkg/application/bindings.go @@ -24,6 +24,7 @@ var reservedPluginMethods = []string{ "Name", "Init", "Shutdown", + "Exported", } // Parameter defines a Go method parameter @@ -113,10 +114,17 @@ func (b *Bindings) AddPlugins(plugins map[string]Plugin) error { return fmt.Errorf("cannot add plugin '%s' to app: %s", pluginID, err.Error()) } + exportedMethods := plugin.Exported() + for _, method := range methods { + // Do not expose reserved methods if lo.Contains(reservedPluginMethods, method.Name) { continue } + // Do not expose methods that are not in the exported list + if !lo.Contains(exportedMethods, method.Name) { + continue + } packageName := "wails-plugins" structName := pluginID methodName := method.Name diff --git a/v3/pkg/application/plugins.go b/v3/pkg/application/plugins.go index d33feb25e..5312796a2 100644 --- a/v3/pkg/application/plugins.go +++ b/v3/pkg/application/plugins.go @@ -4,6 +4,8 @@ type Plugin interface { Name() string Init(app *App) error Shutdown() + // Exported is a list of method names that should be exposed to the frontend + Exported() []string } type PluginManager struct { diff --git a/v3/plugins/README.md b/v3/plugins/README.md new file mode 100644 index 000000000..2dae437a2 --- /dev/null +++ b/v3/plugins/README.md @@ -0,0 +1,29 @@ +# Plugins + +Plugins are a way to extend the functionality of your Wails application. + +## Creating a plugin + +Plugins are standard Go structure that adhere to the following interface: + +```go +type Plugin interface { + Name() string + Init(*application.App) error + Shutdown() + Exported() []string +} +``` + +The `Name()` method returns the name of the plugin. This is used for logging purposes. + +The `Init(*application.App) error` method is called when the plugin is loaded. The `*application.App` +parameter is the application that the plugin is being loaded into. Any errors will prevent +the application from starting. + +The `Shutdown()` method is called when the application is shutting down. + +The `Exported()` method returns a list of exported functions that can be called from +the frontend. These method names must exactly match the names of the methods exported +by the plugin. + diff --git a/v3/plugins/browser/plugin.go b/v3/plugins/browser/plugin.go index dc0dfa164..ab408c041 100644 --- a/v3/plugins/browser/plugin.go +++ b/v3/plugins/browser/plugin.go @@ -26,6 +26,13 @@ func (p *Plugin) Init(_ *application.App) error { return nil } +func (p *Plugin) Exported() []string { + return []string{ + "OpenURL", + "OpenFile", + } +} + // ---------------- Plugin Methods ---------------- func (p *Plugin) OpenURL(url string) error { diff --git a/v3/plugins/kvstore/kvstore.go b/v3/plugins/kvstore/kvstore.go index 2e345b157..2f3b4b522 100644 --- a/v3/plugins/kvstore/kvstore.go +++ b/v3/plugins/kvstore/kvstore.go @@ -3,6 +3,7 @@ package kvstore import ( "encoding/json" "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/logger" "io" "os" "sync" @@ -14,6 +15,7 @@ type KeyValueStore struct { data map[string]any unsaved bool lock sync.RWMutex + app *application.App } type Config struct { @@ -47,7 +49,8 @@ func (kvs *KeyValueStore) Name() string { // Init is called when the plugin is loaded. It is passed the application.App // instance. This is where you should do any setup. -func (kvs *KeyValueStore) Init(_ *application.App) error { +func (kvs *KeyValueStore) Init(app *application.App) error { + kvs.app = app err := kvs.open(kvs.config.Filename) if err != nil { return err @@ -56,9 +59,17 @@ func (kvs *KeyValueStore) Init(_ *application.App) error { return nil } +func (kvs *KeyValueStore) Exported() []string { + return []string{ + "Set", + "Get", + "Save", + } +} + // ---------------- Plugin Methods ---------------- -func (kvs *KeyValueStore) open(filename string) error { +func (kvs *KeyValueStore) open(filename string) (err error) { kvs.filename = filename kvs.data = make(map[string]any) @@ -69,7 +80,19 @@ func (kvs *KeyValueStore) open(filename string) error { } return err } - defer file.Close() + defer func() { + err2 := file.Close() + if err2 != nil { + if err == nil { + err = err2 + } else { + kvs.app.Log(&logger.Message{ + Level: "error", + Message: err.Error(), + }) + } + } + }() bytes, err := io.ReadAll(file) if err != nil { @@ -120,7 +143,11 @@ func (kvs *KeyValueStore) Get(key string) any { // Set sets the value for the given key. If AutoSave is true, the store is saved to disk. func (kvs *KeyValueStore) Set(key string, value any) error { kvs.lock.Lock() - kvs.data[key] = value + if value == nil { + delete(kvs.data, key) + } else { + kvs.data[key] = value + } kvs.lock.Unlock() if kvs.config.AutoSave { err := kvs.Save()