113 lines
2.6 KiB
Go
113 lines
2.6 KiB
Go
package loader
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"galaxy/connector"
|
|
mc "galaxy/model/client"
|
|
"galaxy/storage"
|
|
"plugin"
|
|
"time"
|
|
|
|
"fyne.io/fyne/v2"
|
|
)
|
|
|
|
type ClientInit func(context.Context, storage.UIStorage, connector.UIConnector, fyne.App) (mc.Client, error)
|
|
|
|
type loader struct {
|
|
s storage.Storage
|
|
conn connector.Connector
|
|
cli mc.Client
|
|
}
|
|
|
|
const (
|
|
clientLibraryFile = "client"
|
|
)
|
|
|
|
var (
|
|
checkConnectionTimeout = time.Second * 5
|
|
checkVersionTimeout = time.Minute * 60
|
|
)
|
|
|
|
func NewLoader(ctx context.Context, s storage.Storage, conn connector.Connector, app fyne.App) (*loader, error) {
|
|
cli, err := loadClientPlugin(ctx, s, conn, app, "./client.so", "Factory")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
l := &loader{
|
|
conn: conn,
|
|
cli: cli,
|
|
s: s,
|
|
}
|
|
return l, nil
|
|
}
|
|
|
|
func (l *loader) Run(ctx context.Context) error {
|
|
final := make(chan struct{}, 1)
|
|
if l.conn != nil {
|
|
go l.backgroundLoop(ctx, final)
|
|
defer func() { final <- struct{}{} }()
|
|
}
|
|
if err := l.cli.Run(); err != nil {
|
|
return err
|
|
}
|
|
final <- struct{}{}
|
|
return nil
|
|
}
|
|
|
|
func (l *loader) backgroundLoop(ctx context.Context, final <-chan struct{}) {
|
|
checkConnTimer := time.NewTimer(checkConnectionTimeout)
|
|
checkVersionTimer := time.NewTimer(checkVersionTimeout)
|
|
defer func() {
|
|
checkConnTimer.Stop()
|
|
checkVersionTimer.Stop()
|
|
}()
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
l.cli.Shutdown()
|
|
return
|
|
case <-final:
|
|
return
|
|
case <-checkConnTimer.C:
|
|
isGood := l.conn.CheckConnection()
|
|
l.cli.OnConnection(isGood)
|
|
checkConnTimer.Reset(checkConnectionTimeout)
|
|
case <-checkVersionTimer.C:
|
|
versions, err := l.conn.CheckVersion()
|
|
if err != nil {
|
|
// propagate error to the UI
|
|
} else if latest, ok := latestVersion(versions); ok {
|
|
l.downloadVersion(latest)
|
|
}
|
|
checkVersionTimer.Reset(checkVersionTimeout)
|
|
}
|
|
}
|
|
}
|
|
|
|
// loadClientPlugin loads a Client implementation from a shared object (.so) file at the specified path.
|
|
// It calls the constructor function by name, passing the necessary dependencies, and returns the initialized Client.
|
|
func loadClientPlugin(ctx context.Context, s storage.UIStorage, conn connector.UIConnector, app fyne.App, path, name string) (mc.Client, error) {
|
|
if path == "" {
|
|
return nil, errors.New("no plugin path given")
|
|
}
|
|
|
|
plug, err := plugin.Open(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("open plugin %q: %w", path, err)
|
|
}
|
|
|
|
sym, err := plug.Lookup(name)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("lookup symbol %q: %w", name, err)
|
|
}
|
|
|
|
initializerPtr, ok := sym.(*ClientInit)
|
|
if !ok {
|
|
return nil, fmt.Errorf("unexpected type %T; want %T", sym, initializerPtr)
|
|
}
|
|
|
|
return (*initializerPtr)(ctx, s, conn, app)
|
|
}
|