add command to specify task

add runTask to run spesific task with spesific interface
This commit is contained in:
Robin Olsen
2026-02-17 20:03:43 +01:00
parent 5044b31cff
commit 0ffe4f11b3
5 changed files with 98 additions and 77 deletions

View File

@@ -3,14 +3,12 @@ package main
import ( import (
"fmt" "fmt"
"github.com/LazyBachelor/LazyPM/pkg/cli/repl"
"github.com/LazyBachelor/LazyPM/pkg/task" "github.com/LazyBachelor/LazyPM/pkg/task"
"github.com/LazyBachelor/LazyPM/pkg/tui"
"github.com/LazyBachelor/LazyPM/pkg/web"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var interfaceType string var interfaceType string
var stage int
var rootCmd = &cobra.Command{ var rootCmd = &cobra.Command{
Use: "survey", Use: "survey",
@@ -42,29 +40,37 @@ var submitCmd = &cobra.Command{
func runStartCmd(cmd *cobra.Command, args []string) error { func runStartCmd(cmd *cobra.Command, args []string) error {
interfaces := initInterfaces() interfaces := initInterfaces()
if cmd.Flags().Changed("interface") {
switch interfaceType {
case "tui":
interfaces = []task.Interface{tui.NewTui()}
case "repl":
interfaces = []task.Interface{repl.NewRepl()}
case "web":
interfaces = []task.Interface{web.NewWeb()}
default:
return fmt.Errorf("invalid interface type: %s", interfaceType)
}
}
if err := newIntroModel().Run(); err != nil {
return returnIfUserQuit(err, "failed to run intro")
}
svc, cleanup, err := initializeServices(cmd.Context()) svc, cleanup, err := initializeServices(cmd.Context())
if err != nil { if err != nil {
return returnIfUserQuit(err, "failed to initialize services") return returnIfUserQuit(err, "failed to initialize services")
} }
defer cleanup() defer cleanup()
tasks := initTasks(svc)
if cmd.Flags().Changed("interface") {
if _, ok := interfaces[interfaceType]; !ok {
return fmt.Errorf("invalid interface, valid are (tui, repl, web)")
}
interfaces = map[string]task.Interface{
interfaceType: interfaces[interfaceType],
}
}
if cmd.Flags().Changed("stage") {
if stage < 1 || stage > len(tasks) {
return fmt.Errorf("invalid stage")
}
if err := runTask(cmd.Context(), tasks[stage-1], interfaces[interfaceType]); err != nil {
return err
}
return nil
}
if err := newIntroModel().Run(); err != nil {
return returnIfUserQuit(err, "failed to run intro")
}
surveyTasks := initTasks(svc) surveyTasks := initTasks(svc)
if err := taskLoop(cmd.Context(), surveyTasks, interfaces); err != nil { if err := taskLoop(cmd.Context(), surveyTasks, interfaces); err != nil {
@@ -75,7 +81,8 @@ func runStartCmd(cmd *cobra.Command, args []string) error {
func init() { func init() {
rootCmd.CompletionOptions.DisableDefaultCmd = true rootCmd.CompletionOptions.DisableDefaultCmd = true
startCmd.Flags().StringVarP(&interfaceType, "interface", "i", "", "Specify which interface to use for the survey (tui, repl, web).") startCmd.Flags().StringVarP(&interfaceType, "interface", "i", "tui", "Specify interface.")
startCmd.Flags().IntVarP(&stage, "stage", "s", 1, "Run stage directly")
rootCmd.AddCommand(startCmd) rootCmd.AddCommand(startCmd)
rootCmd.AddCommand(submitCmd) rootCmd.AddCommand(submitCmd)
} }

View File

@@ -27,6 +27,10 @@ func initTasks(svc *service.Services) []*task.Task {
} }
} }
func initInterfaces() []task.Interface { func initInterfaces() map[string]task.Interface {
return []task.Interface{repl.NewRepl(), tui.NewTui(), web.NewWeb()} return map[string]task.Interface{
"repl": repl.NewRepl(),
"tui": tui.NewTui(),
"web": web.NewWeb(),
}
} }

View File

@@ -114,7 +114,8 @@ func (m introModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m introModel) View() string { func (m introModel) View() string {
if m.width < 55 || m.height < 16 { if m.width < 55 || m.height < 16 {
return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, "Terminal too small.") return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center,
style.TextStyle.Render("Terminal too small."))
} }
var content string var content string
@@ -127,10 +128,9 @@ func (m introModel) View() string {
return "" return ""
} }
boxWidth := min(m.width-10, 90) boxWidth := min(m.width-10, 80)
boxStyle := lipgloss.NewStyle(). boxStyle := style.BorderStyle.
Border(style.DefaultBorder).
Margin(1, 0).Padding(2, 4).Width(boxWidth) Margin(1, 0).Padding(2, 4).Width(boxWidth)
var b strings.Builder var b strings.Builder
@@ -138,7 +138,7 @@ func (m introModel) View() string {
b.WriteString(style.TitleStyle.Render(IntroTitle)) b.WriteString(style.TitleStyle.Render(IntroTitle))
b.WriteString("\n") b.WriteString("\n")
b.WriteString(boxStyle.Render(content)) b.WriteString(boxStyle.Render(style.TextStyle.Render(content)))
b.WriteString("\n") b.WriteString("\n")
helpText := "Press " + keys.Continue.Help().Key + " to continue • " + helpText := "Press " + keys.Continue.Help().Key + " to continue • " +

View File

@@ -10,60 +10,71 @@ import (
"github.com/LazyBachelor/LazyPM/pkg/task" "github.com/LazyBachelor/LazyPM/pkg/task"
) )
func taskLoop(ctx context.Context, surveyTasks []*task.Task, interfaces []task.Interface) error { func runTask(ctx context.Context, t *task.Task, i task.Interface) error {
interfaceIndex := rand.Int() % len(interfaces) t.SetInterface(i)
t.SetInterfaceType(tasks.InterfaceToType(i))
for _, t := range surveyTasks { doneChan := make(chan bool, 1)
quitChan := make(chan bool, 1)
feedbackChan := make(chan task.ValidationFeedback, 10)
t.SetInterface(interfaces[interfaceIndex]) if validated, ok := i.(task.ValidatedInterface); ok {
t.SetInterfaceType(tasks.InterfaceToType(interfaces[interfaceIndex])) validated.SetChannels(feedbackChan, quitChan)
}
doneChan := make(chan bool, 1) if err := t.Initialize(ctx); err != nil {
quitChan := make(chan bool, 1) return fmt.Errorf("failed to initialize task: %w", err)
feedbackChan := make(chan task.ValidationFeedback, 10) }
if validated, ok := interfaces[interfaceIndex].(task.ValidatedInterface); ok { if err := t.IntroduceTask(); err != nil {
validated.SetChannels(feedbackChan, quitChan) return returnIfUserQuit(err, "failed to display task introduction screen")
}
t.SetChannels(feedbackChan, doneChan, quitChan)
go t.StartValidationLoop(ctx)
interfaceDone := make(chan error, 1)
go func() {
interfaceDone <- t.StartInterface(ctx, t.Config)
}()
select {
case <-doneChan:
close(quitChan)
<-interfaceDone
fmt.Println("Task completed successfully!")
case err := <-interfaceDone:
close(quitChan)
if err != nil {
return returnIfUserQuit(err, "failed to start task interface")
} }
fmt.Println("Task incomplete - you exited early")
}
if err := t.Initialize(ctx); err != nil { if err := t.StartQuestionnaire(); err != nil {
return fmt.Errorf("failed to initialize task: %w", err) return returnIfUserQuit(err, "failed to start questionnaire")
} }
return nil
}
if err := t.IntroduceTask(); err != nil { func taskLoop(ctx context.Context, surveyTasks []*task.Task, interfaces map[string]task.Interface) error {
return returnIfUserQuit(err, "failed to display task introduction screen") var ifaceNames []string
} for name := range interfaces {
ifaceNames = append(ifaceNames, name)
}
t.SetChannels(feedbackChan, doneChan, quitChan) rand.Shuffle(len(ifaceNames), func(i, j int) {
ifaceNames[i], ifaceNames[j] = ifaceNames[j], ifaceNames[i]
})
go t.StartValidationLoop(ctx) for i, task := range surveyTasks {
idx := i % len(ifaceNames)
selected := interfaces[ifaceNames[idx]]
interfaceDone := make(chan error, 1) if err := runTask(ctx, task, selected); err != nil {
go func() { return err
interfaceDone <- t.StartInterface(ctx, t.Config)
}()
select {
case <-doneChan:
close(quitChan)
<-interfaceDone
fmt.Println("Task completed successfully!")
case err := <-interfaceDone:
close(quitChan)
if err != nil {
return returnIfUserQuit(err, "failed to start task interface")
}
fmt.Println("Task incomplete - you exited early")
}
if err := t.StartQuestionnaire(); err != nil {
return returnIfUserQuit(err, "failed to start questionnaire")
}
interfaceIndex++
if interfaceIndex >= len(interfaces) {
interfaceIndex = 0
} }
} }
return nil return nil

View File

@@ -8,7 +8,7 @@ var (
SecondaryColor = lipgloss.AdaptiveColor{Light: "#ff6f61", Dark: "#ff6347"} SecondaryColor = lipgloss.AdaptiveColor{Light: "#ff6f61", Dark: "#ff6347"}
AccentColor = lipgloss.AdaptiveColor{Light: "#6a5acd", Dark: "#9370db"} AccentColor = lipgloss.AdaptiveColor{Light: "#6a5acd", Dark: "#9370db"}
Background = lipgloss.AdaptiveColor{Light: "#ffffff", Dark: "#1e1e1e"} Background = lipgloss.AdaptiveColor{Light: "#ffffff", Dark: "#1e1e1e"}
TextColor = lipgloss.AdaptiveColor{Light: "#000000", Dark: "#ffffff"} TextColor = lipgloss.AdaptiveColor{Light: "#252525b7", Dark: "#ffffff"}
) )
var ( var (
@@ -21,8 +21,7 @@ var (
) )
var ( var (
TitleStyle = lipgloss.NewStyle().Foreground(PrimaryColor).Bold(true) TitleStyle = lipgloss.NewStyle().Foreground(PrimaryColor).Bold(true)
DescriptionStyle = lipgloss.NewStyle().Foreground(TextColor).Italic(true) TextStyle = lipgloss.NewStyle().Foreground(TextColor)
DetailStyle = lipgloss.NewStyle().Foreground(SecondaryColor) HelpStyle = lipgloss.NewStyle().Align(lipgloss.Center).Foreground(AccentColor)
HelpStyle = lipgloss.NewStyle().Align(lipgloss.Center).Foreground(AccentColor)
) )