All checks were successful
Linting and tests / Linting (push) Successful in 6s
224 lines
5.2 KiB
Go
224 lines
5.2 KiB
Go
package application
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"bunker/commons"
|
|
|
|
"fyne.io/fyne/v2"
|
|
"fyne.io/fyne/v2/app"
|
|
)
|
|
|
|
var (
|
|
errApplication = errors.New("application")
|
|
errNoMainWindow = errors.New("no main window service registered")
|
|
)
|
|
|
|
// Application is a lifecycle controlling structure for application.
|
|
type Application struct {
|
|
ctx context.Context
|
|
cancelFunc context.CancelFunc
|
|
fyneApp fyne.App
|
|
baseLogger *slog.Logger
|
|
appLogger *slog.Logger
|
|
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
|
|
}
|
|
|
|
a.appLogger.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
|
|
}
|
|
|
|
a.appLogger.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
|
|
}
|
|
|
|
a.appLogger.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
|
|
}
|
|
|
|
a.appLogger.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(a.ctx, 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.ctx, a.cancelFunc = context.WithCancel(context.Background())
|
|
|
|
a.initializeLogger()
|
|
|
|
a.fyneApp = app.NewWithID(commons.ClientAppID)
|
|
|
|
a.services = make([]Service, 0)
|
|
}
|
|
|
|
func (a *Application) launchStartupTasks() error {
|
|
for _, service := range a.services {
|
|
if strings.Contains(service.Name(), "mainwindow") {
|
|
continue
|
|
}
|
|
|
|
if !strings.Contains(service.Name(), "core/") {
|
|
continue
|
|
}
|
|
|
|
a.appLogger.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
|
|
}
|
|
|
|
a.appLogger.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 {
|
|
a.appLogger.Info("Stopping pztrn's Bunker...")
|
|
|
|
for _, service := range a.services {
|
|
if !strings.Contains(service.Name(), "features/") {
|
|
continue
|
|
}
|
|
|
|
a.appLogger.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
|
|
}
|
|
|
|
a.appLogger.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 starts application.
|
|
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
|
|
}
|