refactor to use app package instead of service
refactor app to be composable
This commit is contained in:
66
internal/app/app.go
Normal file
66
internal/app/app.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/LazyBachelor/LazyPM/internal/models"
|
||||
"github.com/LazyBachelor/LazyPM/internal/storage"
|
||||
"github.com/steveyegge/beads"
|
||||
)
|
||||
|
||||
type App = models.App
|
||||
type Config = models.Config
|
||||
|
||||
func New(ctx context.Context, config Config, opts ...Option) (*App, func(), error) {
|
||||
b := defaultBuilder(ctx, config)
|
||||
|
||||
for _, opt := range opts {
|
||||
if err := opt(b); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if !config.AutoInit {
|
||||
if err := b.initializer.Init(config.BeadsDBPath); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if b.issueService == nil {
|
||||
sqliteStore, err := beads.NewSQLiteStorage(b.ctx, config.BeadsDBPath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
b.lifecycle.Add(func() { sqliteStore.Close() })
|
||||
|
||||
b.issueService, err = storage.NewBeadsIssueStorage(b.ctx, sqliteStore, config.IssuePrefix)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if b.statsService == nil {
|
||||
statStore := storage.NewJsonStorage(config.StatisticsStoragePath, &models.Statistics{
|
||||
ID: 0,
|
||||
StartTime: time.Now(),
|
||||
})
|
||||
|
||||
jsonStatService, err := NewStatisticsService(statStore)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
b.statsService = jsonStatService
|
||||
}
|
||||
|
||||
app := &App{
|
||||
Config: config,
|
||||
Logger: b.logger,
|
||||
|
||||
Issues: b.issueService,
|
||||
Stats: b.statsService,
|
||||
}
|
||||
|
||||
return app, b.lifecycle.Close, nil
|
||||
}
|
||||
41
internal/app/initializer.go
Normal file
41
internal/app/initializer.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/charmbracelet/huh"
|
||||
)
|
||||
|
||||
type Initializer interface {
|
||||
Init(path string) error
|
||||
}
|
||||
|
||||
type InteractiveInitializer struct{}
|
||||
|
||||
func (i InteractiveInitializer) Init(path string) error {
|
||||
_, err := os.Stat(path)
|
||||
if !os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var initialize bool
|
||||
|
||||
err = huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewConfirm().
|
||||
Title("PM is not initialized in this directory!").
|
||||
Description("Do you want to initialize it here?").
|
||||
Value(&initialize),
|
||||
)).WithTheme(huh.ThemeBase16()).WithAccessible(true).Run()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !initialize {
|
||||
return fmt.Errorf("project not initialized")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
21
internal/app/lifecycle.go
Normal file
21
internal/app/lifecycle.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package app
|
||||
|
||||
type Lifecycle struct {
|
||||
cleanups []func()
|
||||
}
|
||||
|
||||
func NewLifecycle() *Lifecycle {
|
||||
return &Lifecycle{
|
||||
cleanups: make([]func(), 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Lifecycle) Add(fn func()) {
|
||||
l.cleanups = append(l.cleanups, fn)
|
||||
}
|
||||
|
||||
func (l *Lifecycle) Close() {
|
||||
for i := len(l.cleanups) - 1; i >= 0; i-- {
|
||||
l.cleanups[i]()
|
||||
}
|
||||
}
|
||||
68
internal/app/options.go
Normal file
68
internal/app/options.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"github.com/LazyBachelor/LazyPM/internal/models"
|
||||
)
|
||||
|
||||
type Option func(*AppBuilder) error
|
||||
|
||||
type AppBuilder struct {
|
||||
config Config
|
||||
ctx context.Context
|
||||
|
||||
logger *slog.Logger
|
||||
lifecycle *Lifecycle
|
||||
initializer Initializer
|
||||
|
||||
issueService models.IssueService
|
||||
statsService models.StatsService
|
||||
}
|
||||
|
||||
func defaultBuilder(ctx context.Context, config Config) *AppBuilder {
|
||||
return &AppBuilder{
|
||||
ctx: ctx,
|
||||
config: config,
|
||||
lifecycle: NewLifecycle(),
|
||||
initializer: &InteractiveInitializer{},
|
||||
logger: slog.New(slog.NewJSONHandler(os.Stdout, nil)),
|
||||
}
|
||||
}
|
||||
|
||||
func WithLogger(logger *slog.Logger) Option {
|
||||
return func(b *AppBuilder) error {
|
||||
b.logger = logger
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithInitializer(initializer Initializer) Option {
|
||||
return func(b *AppBuilder) error {
|
||||
b.initializer = initializer
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithLifecycle(l *Lifecycle) Option {
|
||||
return func(b *AppBuilder) error {
|
||||
b.lifecycle = l
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithIssueService(svc models.IssueService) Option {
|
||||
return func(b *AppBuilder) error {
|
||||
b.issueService = svc
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithStatsService(svc models.StatsService) Option {
|
||||
return func(b *AppBuilder) error {
|
||||
b.statsService = svc
|
||||
return nil
|
||||
}
|
||||
}
|
||||
37
internal/app/statistics.go
Normal file
37
internal/app/statistics.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/LazyBachelor/LazyPM/internal/models"
|
||||
"github.com/LazyBachelor/LazyPM/internal/storage"
|
||||
)
|
||||
|
||||
type StatisticsService struct {
|
||||
storage *storage.Storage[models.Statistics]
|
||||
}
|
||||
|
||||
func NewStatisticsService(storage *storage.Storage[models.Statistics]) (*StatisticsService, error) {
|
||||
if err := storage.Init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &StatisticsService{
|
||||
storage: storage,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *StatisticsService) Load(ctx context.Context) error {
|
||||
return s.storage.Load()
|
||||
}
|
||||
|
||||
func (s *StatisticsService) Save(ctx context.Context) error {
|
||||
return s.storage.Save()
|
||||
}
|
||||
|
||||
func (s *StatisticsService) GetStatistics() (models.Statistics, error) {
|
||||
if s.storage.Data == nil {
|
||||
return models.Statistics{}, errors.New("statistics data not initialized")
|
||||
}
|
||||
return *s.storage.Data, nil
|
||||
}
|
||||
Reference in New Issue
Block a user