lots of changes and reducing dependency imports
This commit is contained in:
@@ -26,18 +26,18 @@ func completeIssues(cmd *cobra.Command, args []string, toComplete string) ([]str
|
||||
}
|
||||
|
||||
// GetIssueCompletions fetches issues matching the toComplete string for shell completion.
|
||||
func GetIssueCompletions(ctx context.Context, toComplete string) ([]models.Issue, cobra.ShellCompDirective) {
|
||||
func GetIssueCompletions(ctx context.Context, toComplete string) ([]*models.Issue, cobra.ShellCompDirective) {
|
||||
app := AppFromContext(ctx)
|
||||
if app == nil {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
issues, err := app.Issues.AllIssues(ctx)
|
||||
issues, err := app.Issues.SearchIssues(ctx, "", models.IssueFilter{})
|
||||
if err != nil {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
var completions []models.Issue
|
||||
var completions []*models.Issue
|
||||
for _, issue := range issues {
|
||||
if strings.HasPrefix(issue.ID, toComplete) {
|
||||
completions = append(completions, issue)
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/LazyBachelor/LazyPM/internal/commands"
|
||||
"github.com/LazyBachelor/LazyPM/internal/models"
|
||||
"github.com/LazyBachelor/LazyPM/internal/utils"
|
||||
"github.com/charmbracelet/huh"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@@ -112,7 +112,7 @@ func init() {
|
||||
CreateCmd.Flags().StringVarP(&createFlags.issueType, "type", "t", "task", "Issue type(bug, feature, task)")
|
||||
CreateCmd.Flags().IntVarP(&createFlags.priority, "priority", "p", 0, "Issue priority(0-4)")
|
||||
|
||||
CreateCmd.RegisterFlagCompletionFunc("type", commands.CompletionFunc(typeOptions))
|
||||
CreateCmd.RegisterFlagCompletionFunc("status", commands.CompletionFunc(statusOptions))
|
||||
CreateCmd.RegisterFlagCompletionFunc("priority", commands.CompletionFunc(priorityRange))
|
||||
CreateCmd.RegisterFlagCompletionFunc("type", utils.CompletionFunc(typeOptions))
|
||||
CreateCmd.RegisterFlagCompletionFunc("status", utils.CompletionFunc(statusOptions))
|
||||
CreateCmd.RegisterFlagCompletionFunc("priority", utils.CompletionFunc(priorityRange))
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ package issuesCmd
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/LazyBachelor/LazyPM/internal/commands"
|
||||
"github.com/LazyBachelor/LazyPM/internal/models"
|
||||
"github.com/LazyBachelor/LazyPM/internal/utils"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -79,7 +79,7 @@ func init() {
|
||||
|
||||
ListCmd.Flags().IntVarP(&listFlags.limit, "limit", "l", 25, "Limit the number of issues returned")
|
||||
|
||||
ListCmd.RegisterFlagCompletionFunc("status", commands.CompletionFunc(statusOptions))
|
||||
ListCmd.RegisterFlagCompletionFunc("type", commands.CompletionFunc(typeOptions))
|
||||
ListCmd.RegisterFlagCompletionFunc("priority", commands.CompletionFunc(priorityRange))
|
||||
ListCmd.RegisterFlagCompletionFunc("status", utils.CompletionFunc(statusOptions))
|
||||
ListCmd.RegisterFlagCompletionFunc("type", utils.CompletionFunc(typeOptions))
|
||||
ListCmd.RegisterFlagCompletionFunc("priority", utils.CompletionFunc(priorityRange))
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/LazyBachelor/LazyPM/internal/service"
|
||||
"github.com/LazyBachelor/LazyPM/internal/models"
|
||||
"github.com/charmbracelet/fang"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@@ -14,8 +14,9 @@ type contextKey string
|
||||
|
||||
const appKey contextKey = "app"
|
||||
|
||||
// app is a package-level variable used during command setup
|
||||
var app *service.App
|
||||
type App = models.App
|
||||
|
||||
var app *App
|
||||
|
||||
// Flags struct to hold command-line flag values for issues.
|
||||
type Flags struct {
|
||||
@@ -44,14 +45,14 @@ var RootCmd = &cobra.Command{
|
||||
|
||||
// SetApp sets the app variable for use in command execution.
|
||||
// Must be called before executing any commands to ensure services are available.
|
||||
func SetApp(application *service.App) {
|
||||
func SetApp(application *App) {
|
||||
app = application
|
||||
RootCmd.Use = app.Config.RootCmd
|
||||
}
|
||||
|
||||
// AppFromContext retrieves the App from the command context
|
||||
func AppFromContext(ctx context.Context) *service.App {
|
||||
if a, ok := ctx.Value(appKey).(*service.App); ok {
|
||||
func AppFromContext(ctx context.Context) *App {
|
||||
if a, ok := ctx.Value(appKey).(*App); ok {
|
||||
return a
|
||||
}
|
||||
// Fallback to package-level app (for testing or edge cases)
|
||||
|
||||
@@ -3,8 +3,8 @@ package issuesCmd
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/LazyBachelor/LazyPM/internal/commands"
|
||||
"github.com/LazyBachelor/LazyPM/internal/models"
|
||||
"github.com/LazyBachelor/LazyPM/internal/utils"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -62,9 +62,9 @@ func init() {
|
||||
UpdateCmd.Flags().StringVarP(&updateFlags.issueType, "type", "t", "", "New issue type(bug, feature, task)")
|
||||
UpdateCmd.Flags().IntVarP(&updateFlags.priority, "priority", "p", 0, "New issue priority(0-5)")
|
||||
|
||||
UpdateCmd.RegisterFlagCompletionFunc("type", commands.CompletionFunc(typeOptions))
|
||||
UpdateCmd.RegisterFlagCompletionFunc("status", commands.CompletionFunc(statusOptions))
|
||||
UpdateCmd.RegisterFlagCompletionFunc("priority", commands.CompletionFunc(priorityRange))
|
||||
UpdateCmd.RegisterFlagCompletionFunc("type", utils.CompletionFunc(typeOptions))
|
||||
UpdateCmd.RegisterFlagCompletionFunc("status", utils.CompletionFunc(statusOptions))
|
||||
UpdateCmd.RegisterFlagCompletionFunc("priority", utils.CompletionFunc(priorityRange))
|
||||
}
|
||||
|
||||
func getUpdateValues(cmd *cobra.Command) (map[string]interface{}, error) {
|
||||
|
||||
@@ -3,10 +3,14 @@ package surveyCmd
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/LazyBachelor/LazyPM/internal/service"
|
||||
"github.com/LazyBachelor/LazyPM/internal/models"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type App = models.App
|
||||
|
||||
var app *App
|
||||
|
||||
const appKey string = "app"
|
||||
const long string = `Project Management Interface Survey
|
||||
|
||||
@@ -18,8 +22,6 @@ This survey will present you with a series of tasks to complete using various in
|
||||
Please answer the questions honestly and to the best of your ability.
|
||||
Your responses will be kept confidential and used solely for research purposes.`
|
||||
|
||||
var app *service.App
|
||||
|
||||
// RootCmd is the base command for the survey CLI.
|
||||
var RootCmd = &cobra.Command{
|
||||
Use: "survey",
|
||||
@@ -31,12 +33,12 @@ var RootCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
func SetApp(application *service.App) {
|
||||
func SetApp(application *App) {
|
||||
app = application
|
||||
}
|
||||
|
||||
func AppFromContext(ctx context.Context) *service.App {
|
||||
if a, ok := ctx.Value(appKey).(*service.App); ok {
|
||||
func AppFromContext(ctx context.Context) *App {
|
||||
if a, ok := ctx.Value(appKey).(*App); ok {
|
||||
return a
|
||||
}
|
||||
return app
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package surveyCmd
|
||||
|
||||
import (
|
||||
"github.com/LazyBachelor/LazyPM/internal/commands"
|
||||
"github.com/LazyBachelor/LazyPM/internal/utils"
|
||||
"github.com/LazyBachelor/LazyPM/pkg/task"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -20,6 +20,6 @@ var StartCmd = &cobra.Command{
|
||||
func init() {
|
||||
StartCmd.Flags().StringVarP(&Task, "task", "t", "", "Specify task.")
|
||||
StartCmd.Flags().StringVarP(&InterfaceType, "interface", "i", "", "Specify interface.")
|
||||
StartCmd.RegisterFlagCompletionFunc("task", commands.CompletionFunc(task.ListTasks()))
|
||||
StartCmd.RegisterFlagCompletionFunc("interface", commands.CompletionFunc(task.ListInterfaces()))
|
||||
StartCmd.RegisterFlagCompletionFunc("task", utils.CompletionFunc(task.ListTasks()))
|
||||
StartCmd.RegisterFlagCompletionFunc("interface", utils.CompletionFunc(task.ListInterfaces()))
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package service
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
|
||||
"github.com/LazyBachelor/LazyPM/internal/models"
|
||||
"github.com/steveyegge/beads"
|
||||
)
|
||||
|
||||
@@ -15,22 +14,19 @@ type App struct {
|
||||
|
||||
Logger *slog.Logger
|
||||
|
||||
Tasks *map[string]Tasker
|
||||
Interfaces *map[string]Interface
|
||||
|
||||
CurrentFeedback *ValidationFeedback
|
||||
}
|
||||
|
||||
type IssueService interface {
|
||||
beads.Storage
|
||||
AllIssues(ctx context.Context) ([]models.Issue, error)
|
||||
beads.Storage // Want to get rid of this dependency, but it provides a lot of useful methods that would be a pain to re-implement right now
|
||||
DeleteIssues() error
|
||||
}
|
||||
|
||||
type StatsService interface {
|
||||
Load(ctx context.Context) error
|
||||
Save(ctx context.Context) error
|
||||
GetStatistics() (models.Statistics, error)
|
||||
}
|
||||
|
||||
type ValidationFeedback struct {
|
||||
Success bool
|
||||
Message string
|
||||
GetStatistics() (Statistics, error)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package service
|
||||
package models
|
||||
|
||||
type Config struct {
|
||||
AutoInit bool
|
||||
5
internal/models/errors.go
Normal file
5
internal/models/errors.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package models
|
||||
|
||||
import "fmt"
|
||||
|
||||
var ErrUserQuit = fmt.Errorf("user quit")
|
||||
21
internal/models/interface.go
Normal file
21
internal/models/interface.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package models
|
||||
|
||||
import "context"
|
||||
|
||||
// Interface represents a user interface for interacting with the application.
|
||||
// It can be any implementation that fulfills the Run method.
|
||||
type Interface interface {
|
||||
Run(context.Context, Config) error
|
||||
}
|
||||
|
||||
// InterfaceType represents the type of user interface
|
||||
type InterfaceType string
|
||||
|
||||
// Interface represents a user interface for interacting with the application.
|
||||
// Keep lower case to avoid confusion with Go's built-in interfaces.
|
||||
const (
|
||||
InterfaceTypeCLI InterfaceType = "cli"
|
||||
InterfaceTypeTUI InterfaceType = "tui"
|
||||
InterfaceTypeWeb InterfaceType = "web"
|
||||
InterfaceTypeREPL InterfaceType = "repl"
|
||||
)
|
||||
@@ -2,19 +2,10 @@ package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type InterfaceType string
|
||||
|
||||
const (
|
||||
InterfaceTypeCLI InterfaceType = "CLI"
|
||||
InterfaceTypeWeb InterfaceType = "Web"
|
||||
)
|
||||
|
||||
type Statistics struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
ID int `json:"id"`
|
||||
StartTime time.Time `json:"start_time"`
|
||||
EndTime time.Time `json:"end_time"`
|
||||
Duration time.Duration `json:"duration"`
|
||||
|
||||
67
internal/models/task.go
Normal file
67
internal/models/task.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/charmbracelet/huh"
|
||||
)
|
||||
|
||||
type Tasker interface {
|
||||
Config() Config
|
||||
Details() TaskDetails
|
||||
Setup(context.Context) error
|
||||
Questions(InterfaceType) Questions
|
||||
Validate(context.Context) ValidationFeedback
|
||||
}
|
||||
|
||||
type ValidationFeedback struct {
|
||||
Success bool
|
||||
Message string
|
||||
Checks []Check
|
||||
}
|
||||
|
||||
type Check struct {
|
||||
Message string
|
||||
Valid bool
|
||||
}
|
||||
|
||||
type ValidatedInterface interface {
|
||||
Interface
|
||||
SetChannels(feedbackChan chan ValidationFeedback, quitChan chan bool)
|
||||
}
|
||||
|
||||
type TaskDetails struct {
|
||||
Title string
|
||||
Description string
|
||||
TimeToComplete string
|
||||
Difficulty string
|
||||
}
|
||||
|
||||
func (td TaskDetails) WithTitle(title string) TaskDetails {
|
||||
td.Title = title
|
||||
return td
|
||||
}
|
||||
|
||||
func (td TaskDetails) WithDescription(description string) TaskDetails {
|
||||
td.Description = description
|
||||
return td
|
||||
}
|
||||
|
||||
func (td TaskDetails) WithTimeToComplete(time string) TaskDetails {
|
||||
td.TimeToComplete = time
|
||||
return td
|
||||
}
|
||||
|
||||
func (td TaskDetails) WithDifficulty(difficulty string) TaskDetails {
|
||||
td.Difficulty = difficulty
|
||||
return td
|
||||
}
|
||||
|
||||
type Questions []*huh.Group
|
||||
|
||||
func (q Questions) With(group *huh.Group) Questions {
|
||||
if group == nil {
|
||||
return q
|
||||
}
|
||||
return append(q, group)
|
||||
}
|
||||
@@ -10,12 +10,13 @@ import (
|
||||
"github.com/LazyBachelor/LazyPM/internal/models"
|
||||
"github.com/LazyBachelor/LazyPM/internal/storage"
|
||||
"github.com/charmbracelet/huh"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/steveyegge/beads"
|
||||
)
|
||||
|
||||
func NewServices(ctx context.Context, config Config) (*App, func(), error) {
|
||||
type App = models.App
|
||||
type Config = models.Config
|
||||
|
||||
func NewApp(ctx context.Context, config Config) (*App, func(), error) {
|
||||
var cleanupFuncs []func()
|
||||
|
||||
if !config.AutoInit {
|
||||
@@ -31,14 +32,14 @@ func NewServices(ctx context.Context, config Config) (*App, func(), error) {
|
||||
}
|
||||
cleanupFuncs = append(cleanupFuncs, func() { store.Close() })
|
||||
|
||||
beadsSvc, err := NewBeadsService(ctx, store, config.IssuePrefix)
|
||||
beadsSvc, err := storage.NewBeadsIssueStorage(ctx, store, config.IssuePrefix)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
cleanupFuncs = append(cleanupFuncs, func() { beadsSvc.Close() })
|
||||
|
||||
statStore := storage.NewJsonStorage(config.StatisticsStoragePath, &models.Statistics{
|
||||
ID: uuid.New(),
|
||||
ID: 0,
|
||||
StartTime: time.Now(),
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package service
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -13,7 +13,7 @@ type BeadsService struct {
|
||||
beads.Storage
|
||||
}
|
||||
|
||||
func NewBeadsService(ctx context.Context, storage beads.Storage, prefix string) (*BeadsService, error) {
|
||||
func NewBeadsIssueStorage(ctx context.Context, storage beads.Storage, prefix string) (*BeadsService, error) {
|
||||
issue_prefix, err := storage.GetConfig(ctx, "issue_prefix")
|
||||
if err != nil || issue_prefix == "" {
|
||||
if err := storage.SetConfig(ctx, "issue_prefix", prefix); err != nil {
|
||||
@@ -1,8 +1,8 @@
|
||||
package commands
|
||||
package utils
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
// completionFunc returns a function that provides shell completion for the given options.
|
||||
// CompletionFunc returns a function that provides shell completion for the given options.
|
||||
func CompletionFunc(options []string) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
|
||||
return func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
|
||||
return options, cobra.ShellCompDirectiveDefault
|
||||
111
internal/utils/expect.go
Normal file
111
internal/utils/expect.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/LazyBachelor/LazyPM/internal/models"
|
||||
)
|
||||
|
||||
type ValidationFeedback = models.ValidationFeedback
|
||||
type Check = models.Check
|
||||
|
||||
func NewCheck(message string, valid bool) Check {
|
||||
return Check{
|
||||
Message: message,
|
||||
Valid: valid,
|
||||
}
|
||||
}
|
||||
|
||||
type Expector struct {
|
||||
ValidationFeedback
|
||||
}
|
||||
|
||||
func NewExpector() *Expector {
|
||||
return &Expector{
|
||||
ValidationFeedback: ValidationFeedback{
|
||||
Success: false,
|
||||
Checks: []Check{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Expector) Complete() ValidationFeedback {
|
||||
e.Success = len(e.Errors()) == 0
|
||||
return e.ValidationFeedback
|
||||
}
|
||||
|
||||
func (e *Expector) Fail(message string) ValidationFeedback {
|
||||
e.Checks = append(e.Checks, NewCheck(message, false))
|
||||
return e.ValidationFeedback
|
||||
}
|
||||
|
||||
func (e *Expector) Errors() []error {
|
||||
var errors []error
|
||||
for _, check := range e.Checks {
|
||||
if !check.Valid {
|
||||
errors = append(errors, fmt.Errorf("%s", check.Message))
|
||||
}
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
||||
func (e *Expector) Assert(condition bool, message string) *Expector {
|
||||
check := NewCheck(message, condition)
|
||||
e.Checks = append(e.Checks, check)
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Expector) Nil(value any, message string) *Expector {
|
||||
check := NewCheck(message, value == nil)
|
||||
e.Checks = append(e.Checks, check)
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Expector) NotNil(value any, message string) *Expector {
|
||||
check := NewCheck(message, value != nil)
|
||||
e.Checks = append(e.Checks, check)
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Expector) Contains(s, substr, message string) *Expector {
|
||||
check := NewCheck(message, strings.Contains(s, substr))
|
||||
e.Checks = append(e.Checks, check)
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Expector) NotContains(s, substr, message string) *Expector {
|
||||
check := NewCheck(message, !strings.Contains(s, substr))
|
||||
e.Checks = append(e.Checks, check)
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Expector) Equal(a, b any, message string) *Expector {
|
||||
check := NewCheck(message, a == b)
|
||||
e.Checks = append(e.Checks, check)
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Expector) NotEqual(a, b any, message string) *Expector {
|
||||
check := NewCheck(message, a != b)
|
||||
e.Checks = append(e.Checks, check)
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Expector) NotEmptyString(s string, message string) *Expector {
|
||||
check := NewCheck(message, s != "")
|
||||
e.Checks = append(e.Checks, check)
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Expector) EmptySlice(s []*any, message string) *Expector {
|
||||
check := NewCheck(message, len(s) == 0)
|
||||
e.Checks = append(e.Checks, check)
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Expector) NotEmptySlice(s []*any, message string) *Expector {
|
||||
check := NewCheck(message, len(s) > 0)
|
||||
e.Checks = append(e.Checks, check)
|
||||
return e
|
||||
}
|
||||
Reference in New Issue
Block a user