strumt

package module
v2.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 27, 2023 License: MIT Imports: 5 Imported by: 1

README

Strumt codecov Go Report Card GoDoc GitHub tag

Strumt is a library to create prompt chain. It provides multiline prompt, input validation, retry on error, ability to create typesafe prompt, ability to customize prompt and error display, a recording of prompt session and the ability to easily test your prompt scenario.

Example

Checkout godoc to have more examples : https://godoc.org/github.com/antham/strumt


asciicast

package main

import (
    "bufio"
    "fmt"
    "os"
    "strconv"

    "github.com/antham/strumt"
)

func main() {
    user := User{}

    p := strumt.NewPromptsFromReaderAndWriter(bufio.NewReader(os.Stdin), os.Stdout)
    p.AddLinePrompter(&StringPrompt{&user.FirstName, "Enter your first name", "userName", "lastName", "userName"})
    p.AddLinePrompter(&StringPrompt{&user.LastName, "Enter your last name", "lastName", "age", "lastName"})
    p.AddLinePrompter(&IntPrompt{&user.Age, "Enter your age", "age", "", "age"})
    p.SetFirst("userName")
    p.Run()

    for _, step := range p.Scenario() {
        fmt.Println(step.PromptString())
        fmt.Println(step.Inputs()[0])

        if step.Error() != nil {
            fmt.Println(step.Error())
        }
    }

    fmt.Println()
    fmt.Printf("User datas : %#v", user)

}

type StringPrompt struct {
    store             *string
    prompt            string
    currentID         string
    nextPrompt        string
    nextPromptOnError string
}

func (s *StringPrompt) ID() string {
	return s.currentID
}

func (s *StringPrompt) PromptString() string {
    return s.prompt
}

func (s *StringPrompt) Parse(value string) error {
    if value == "" {
        return fmt.Errorf("Empty value given")
    }

    *(s.store) = value

    return nil
}

func (s *StringPrompt) NextOnSuccess(value string) string {
    return s.nextPrompt
}

func (s *StringPrompt) NextOnError(err error) string {
    return s.nextPromptOnError
}

type IntPrompt struct {
    store             *int
    prompt            string
    currentID         string
    nextPrompt        string
    nextPromptOnError string
}

func (i *IntPrompt) ID() string {
	return i.currentID
}

func (i *IntPrompt) PromptString() string {
    return i.prompt
}

func (i *IntPrompt) Parse(value string) error {
    age, err := strconv.Atoi(value)

    if err != nil {
        return fmt.Errorf("%s is not a valid number", value)
    }

    if age <= 0 {
        return fmt.Errorf("Give a valid age")
    }

    *(i.store) = age

    return nil
}

func (i *IntPrompt) NextOnSuccess(value string) string {
    return i.nextPrompt
}

func (i *IntPrompt) NextOnError(err error) string {
    return i.nextPromptOnError
}

type User struct {
    FirstName string
    LastName  string
    Age       int
}

Documentation

Overview

Package strumt provides a way to defines scenarios for prompting informations on command line

Example
package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"strconv"

	"github.com/antham/strumt/v2"
)

func main() {
	user := User{}

	buf := "\nBrad\n\nBlanton\nwhatever\n0\n31\n"

	p := strumt.NewPromptsFromReaderAndWriter(bytes.NewBufferString(buf), ioutil.Discard)
	p.AddLinePrompter(&StringPrompt{&user.FirstName, "Enter your first name", "userName", "lastName", "userName"})
	p.AddLinePrompter(&StringPrompt{&user.LastName, "Enter your last name", "lastName", "age", "lastName"})
	p.AddLinePrompter(&IntPrompt{&user.Age, "Enter your age", "age", "", "age"})
	p.SetFirst("userName")
	p.Run()

	for _, step := range p.Scenario() {
		fmt.Println(step.PromptString())
		fmt.Println(step.Inputs()[0])

		if step.Error() != nil {
			fmt.Println(step.Error())
		}
	}

	fmt.Println()
	fmt.Printf("User datas : %#v", user)

}

type StringPrompt struct {
	store             *string
	prompt            string
	currentID         string
	nextPrompt        string
	nextPromptOnError string
}

func (s *StringPrompt) ID() string {
	return s.currentID
}

func (s *StringPrompt) PromptString() string {
	return s.prompt
}

func (s *StringPrompt) Parse(value string) error {
	if value == "" {
		return fmt.Errorf("Empty value given")
	}

	*(s.store) = value

	return nil
}

func (s *StringPrompt) NextOnSuccess(value string) string {
	return s.nextPrompt
}

func (s *StringPrompt) NextOnError(err error) string {
	return s.nextPromptOnError
}

type IntPrompt struct {
	store             *int
	prompt            string
	currentID         string
	nextPrompt        string
	nextPromptOnError string
}

func (i *IntPrompt) ID() string {
	return i.currentID
}

func (i *IntPrompt) PromptString() string {
	return i.prompt
}

func (i *IntPrompt) Parse(value string) error {
	age, err := strconv.Atoi(value)

	if err != nil {
		return fmt.Errorf("%s is not a valid number", value)
	}

	if age <= 0 {
		return fmt.Errorf("Give a valid age")
	}

	*(i.store) = age

	return nil
}

func (i *IntPrompt) NextOnSuccess(value string) string {
	return i.nextPrompt
}

func (i *IntPrompt) NextOnError(err error) string {
	return i.nextPromptOnError
}

type User struct {
	FirstName string
	LastName  string
	Age       int
}
Output:

Enter your first name

Empty value given
Enter your first name
Brad
Enter your last name

Empty value given
Enter your last name
Blanton
Enter your age
whatever
whatever is not a valid number
Enter your age
0
Give a valid age
Enter your age
31

User datas : strumt_test.User{FirstName:"Brad", LastName:"Blanton", Age:31}
Example (CustomizePromptOutput)
package main

import (
	"bytes"
	"fmt"
	"io"
	"strings"

	"github.com/antham/strumt/v2"
)

func main() {
	var stdout bytes.Buffer
	buf := "whatever\nyes\n"

	p := strumt.NewPromptsFromReaderAndWriter(bytes.NewBufferString(buf), &stdout)
	p.AddLinePrompter(&AreYouOkPrompt{})
	p.SetFirst("okprompt")
	p.Run()

	for {
		line, err := stdout.ReadString('\n')

		if err == io.EOF {
			break
		}

		fmt.Println(strings.TrimSpace(line))
	}
}

type AreYouOkPrompt struct {
}

func (a *AreYouOkPrompt) ID() string {
	return "okprompt"
}

func (a *AreYouOkPrompt) PromptString() string {
	return "Are you Ok ?"
}

func (a *AreYouOkPrompt) Parse(value string) error {
	if value == "yes" || value == "no" {
		return nil
	}

	return fmt.Errorf("You must answer yes or no")
}

func (a *AreYouOkPrompt) NextOnSuccess(value string) string {
	return ""
}

func (a *AreYouOkPrompt) NextOnError(err error) string {
	return "okprompt"
}

func (a *AreYouOkPrompt) PrintPrompt(w io.Writer, prompt string) {
	fmt.Fprintf(w, "==> %s\n", prompt)
}

func (a *AreYouOkPrompt) PrintError(w io.Writer, err error) {
	fmt.Fprintf(w, "An error occurred : %s", err)
}
Output:

==> Are you Ok ?
An error occurred : You must answer yes or no
==> Are you Ok ?
Example (MultilinePrompt)
package main

import (
	"bytes"
	"fmt"
	"io/ioutil"

	"github.com/antham/strumt/v2"
)

func main() {
	var datas []string
	buf := "test1\ntest2\ntest3\ntest4\n\n"

	p := strumt.NewPromptsFromReaderAndWriter(bytes.NewBufferString(buf), ioutil.Discard)
	p.AddMultilinePrompter(&SlicePrompt{&datas})
	p.SetFirst("sliceprompt")
	p.Run()

	fmt.Println(datas)
}

type SlicePrompt struct {
	datas *[]string
}

func (s *SlicePrompt) ID() string {
	return "sliceprompt"
}

func (s *SlicePrompt) PromptString() string {
	return "Give several input"
}

func (s *SlicePrompt) Parse(values []string) error {
	*(s.datas) = values

	return nil
}

func (s *SlicePrompt) NextOnSuccess(values []string) string {
	return ""
}

func (s *SlicePrompt) NextOnError(err error) string {
	return "sliceprompt"
}
Output:

[test1 test2 test3 test4]

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ErrorRenderer

type ErrorRenderer interface {
	PrintError(io.Writer, error)
}

ErrorRenderer can be implemented to customize the way an error returned by Parse is rendered

type LinePrompter

type LinePrompter interface {
	Prompter
	NextOnSuccess(string) string
	Parse(string) error
}

LinePrompter defines a one line prompter that will ask only for a single line user input.

NextOnSuccess must returns the ID of the next prompt to be called. To mark prompter as the last prompter, NextOnSuccess must returns an empty string

type MultilinePrompter

type MultilinePrompter interface {
	Prompter
	NextOnSuccess([]string) string
	Parse([]string) error
}

MultilinePrompter defines a mutiline prompter that will let the possibility to the user to provide several input, result is provided as an input slice.

NextOnSuccess must returns the ID of the next prompt to be called. To mark prompter as the last prompter, NextOnSuccess must returns an empty string

type PromptRenderer

type PromptRenderer interface {
	PrintPrompt(io.Writer, string)
}

PromptRenderer can be implemented to customize the way prompt is rendered, original PromptString is given as second parameter

type Prompter

type Prompter interface {
	ID() string
	PromptString() string
	NextOnError(error) string
}

Prompter defines a generic common prompt.

ID returns a string ID to identify prompter and to let other prompter call it.

PromptString returns a string to be displayed as prompt.

NextOnError is triggered when an error occurred during prompt sequence, it must returns the ID of the prompt to be called when an error occurred, most of the time it would be the ID of the current prompt to loop on it

type Prompts

type Prompts struct {
	// contains filtered or unexported fields
}

Prompts is the main structure that handle all defined prompts and the logic to run and switch from one prompt to another. It keeps a record as well, of all user actions under a scenario entry

func NewPrompts

func NewPrompts() Prompts

NewPrompts creates a new prompt from stdin and stdout

func NewPromptsFromReaderAndWriter

func NewPromptsFromReaderAndWriter(reader io.Reader, writer io.Writer) Prompts

NewPromptsFromReaderAndWriter creates a new prompt from a given reader and writer, useful for testing purpose

func (*Prompts) AddLinePrompter

func (p *Prompts) AddLinePrompter(prompt LinePrompter)

AddLinePrompter adds a new LinePrompter using the internal id as a reference

func (*Prompts) AddMultilinePrompter

func (p *Prompts) AddMultilinePrompter(prompt MultilinePrompter)

AddMultilinePrompter adds a new MultilinePrompter using the internal id as a reference

func (*Prompts) Run

func (p *Prompts) Run()

Run executes a prompt sequence

func (*Prompts) Scenario

func (p *Prompts) Scenario() []Step

Scenario retrieves all steps done during a prompt sequence

func (*Prompts) SetFirst

func (p *Prompts) SetFirst(id string)

SetFirst defines from which prompt the prompt sequence has to start

type SeparatorRenderer

type SeparatorRenderer interface {
	PrintSeparator(io.Writer)
}

SeparatorRenderer can be implemented to customize the way a prompt is separated from another. When this interface is not implemented, the default behaviour is to define a new line as separator

type Step

type Step struct {
	// contains filtered or unexported fields
}

Step represents a scenario step which is the result of a prompt execution. We store the prompt string displayed on the screen, inputs that the user has given, and the prompt error if one occurred

func (Step) Error

func (s Step) Error() error

Error returns the error triggered by a prompt

func (Step) Inputs

func (s Step) Inputs() []string

Inputs retrieves all inputs submitted by the user

func (Step) PromptString

func (s Step) PromptString() string

PromptString returns the prompt string displayed by the prompt on the screen

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL