215 lines
5.0 KiB
Go
215 lines
5.0 KiB
Go
|
package application
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"log/slog"
|
||
|
"os"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"fyne.io/fyne/v2"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
errApplication = errors.New("application")
|
||
|
errNoMainWindow = errors.New("no main window service registered")
|
||
|
)
|
||
|
|
||
|
// Application is a lifecycle controlling structure for application.
|
||
|
type Application struct {
|
||
|
fyneApp fyne.App
|
||
|
services []Service
|
||
|
}
|
||
|
|
||
|
// New creates new instance of lifecycle controlling structure.
|
||
|
func New() *Application {
|
||
|
appl := &Application{}
|
||
|
|
||
|
appl.initialize()
|
||
|
|
||
|
return appl
|
||
|
}
|
||
|
|
||
|
func (a *Application) configure() error {
|
||
|
// First iteration - core services.
|
||
|
for _, service := range a.services {
|
||
|
if !strings.Contains(service.Name(), "core/") {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
slog.Debug("Launching configuration procedure for service", "service", service.Name())
|
||
|
|
||
|
if err := service.Configure(); err != nil {
|
||
|
return fmt.Errorf("configure service '%s': %w", service.Name(), err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Second iteration - rest of the services.
|
||
|
for _, service := range a.services {
|
||
|
if strings.Contains(service.Name(), "core/") {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
slog.Debug("Launching configuration procedure for service", "service", service.Name())
|
||
|
|
||
|
if err := service.Configure(); err != nil {
|
||
|
return fmt.Errorf("configure service '%s': %w", service.Name(), err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (a *Application) connectDependencies() error {
|
||
|
// First iteration - core services.
|
||
|
for _, service := range a.services {
|
||
|
if !strings.Contains(service.Name(), "core/") {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
slog.Debug("Connecting dependencies for service.", "service", service.Name())
|
||
|
|
||
|
if err := service.ConnectDependencies(); err != nil {
|
||
|
return fmt.Errorf("connect dependencies for service '%s': %w", service.Name(), err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Second iteration - rest of the services.
|
||
|
for _, service := range a.services {
|
||
|
if strings.Contains(service.Name(), "core/") {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
slog.Debug("Connecting dependencies for service.", "service", service.Name())
|
||
|
|
||
|
if err := service.ConnectDependencies(); err != nil {
|
||
|
return fmt.Errorf("connect dependencies for service '%s': %w", service.Name(), err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// ContextWithTimeout returns context.Context with requested timeout.
|
||
|
func (a *Application) ContextWithTimeout(timeout time.Duration) context.Context {
|
||
|
ctx, cancelFunc := context.WithTimeout(context.Background(), timeout)
|
||
|
|
||
|
// As we do not need to call cancelFunc - make linter happy.
|
||
|
// This probably will lead to context leak, so it should be investigated.
|
||
|
go func(_ context.CancelFunc) {}(cancelFunc)
|
||
|
|
||
|
return ctx
|
||
|
}
|
||
|
|
||
|
func (a *Application) initialize() {
|
||
|
a.services = make([]Service, 0)
|
||
|
|
||
|
a.initializeFyne()
|
||
|
}
|
||
|
|
||
|
func (a *Application) launchStartupTasks() error {
|
||
|
for _, service := range a.services {
|
||
|
if strings.Contains(service.Name(), "mainwindow") {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if !strings.Contains(service.Name(), "core/") {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
slog.Debug("Launching startup tasks for service.", "service", service.Name())
|
||
|
|
||
|
if err := service.LaunchStartupTasks(); err != nil {
|
||
|
return fmt.Errorf("launch startup tasks for core/%s: %w", service.Name(), err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for _, service := range a.services {
|
||
|
if strings.Contains(service.Name(), "core/") {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
slog.Debug("Launching startup tasks for service.", "service", service.Name())
|
||
|
|
||
|
if err := service.LaunchStartupTasks(); err != nil {
|
||
|
return fmt.Errorf("launch startup tasks for core/%s: %w", service.Name(), err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var mainWindowService Service
|
||
|
|
||
|
for _, srv := range a.services {
|
||
|
if srv.Name() == "core/mainwindow" {
|
||
|
mainWindowService = srv
|
||
|
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if mainWindowService == nil {
|
||
|
return fmt.Errorf("launch startup tasks: %w", errNoMainWindow)
|
||
|
}
|
||
|
|
||
|
if err := mainWindowService.LaunchStartupTasks(); err != nil {
|
||
|
return fmt.Errorf("launch startup tasks for %s: %w", mainWindowService.Name(), err)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Shutdown stops application
|
||
|
func (a *Application) Shutdown() error {
|
||
|
slog.Info("Stopping pztrn's Bunker...")
|
||
|
|
||
|
// Сначала тушим фичи.
|
||
|
for _, service := range a.services {
|
||
|
if !strings.Contains(service.Name(), "features/") {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
slog.Debug("Shutting down service.", "service", service.Name())
|
||
|
|
||
|
if err := service.Shutdown(); err != nil {
|
||
|
return fmt.Errorf("shutting down service '%s': %w", service.Name(), err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Потом тушим ядро.
|
||
|
for _, service := range a.services {
|
||
|
if !strings.Contains(service.Name(), "core/") {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
slog.Debug("Shutting down service.", "service", service.Name())
|
||
|
|
||
|
if err := service.Shutdown(); err != nil {
|
||
|
return fmt.Errorf("shutting down service '%s': %w", service.Name(), err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
os.Exit(0)
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Start запускает приложение.
|
||
|
func (a *Application) Start() error {
|
||
|
if err := a.connectDependencies(); err != nil {
|
||
|
return fmt.Errorf("%w: %w", errApplication, err)
|
||
|
}
|
||
|
|
||
|
if err := a.configure(); err != nil {
|
||
|
return fmt.Errorf("%w: %w", errApplication, err)
|
||
|
}
|
||
|
|
||
|
if err := a.launchStartupTasks(); err != nil {
|
||
|
return fmt.Errorf("%w: %w", errApplication, err)
|
||
|
}
|
||
|
|
||
|
a.fyneApp.Run()
|
||
|
|
||
|
return nil
|
||
|
}
|