package repl
import (
"bufio"
"encoding/json"
"errors"
"fmt"
"os"
"os/user"
"regexp"
"strings"
"time"
"github.com/reeflective/console"
"github.com/reeflective/readline"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
amhelp "github.com/pancsta/asyncmachine-go/pkg/helpers"
am "github.com/pancsta/asyncmachine-go/pkg/machine"
"github.com/pancsta/asyncmachine-go/pkg/rpc"
"github.com/pancsta/asyncmachine-go/tools/repl/states"
)
var (
historyPath = ""
ss = states .ReplStates
)
type completionFunc func (
cmd *cobra .Command , args []string , toComplete string ,
) ([]string , cobra .ShellCompDirective )
func init() {
usr , _ := user .Current ()
dir := usr .HomeDir
historyPath = dir + "/.arpc_history"
}
var ErrSyntax = errors .New ("syntax error" )
func AddErrSyntax (
event *am .Event , mach *am .Machine , err error , args am .A ,
) error {
err = fmt .Errorf ("%w: %w" , ErrSyntax , err )
mach .EvAddErrState (event , ss .ErrSyntax , err , args )
return err
}
const APrefix = "am_repl"
type A struct {
Id string `log:"id"`
Addrs []string `log:"addr"`
Lines []string
States []string `log:"states"`
MachId string `log:"mach"`
MachIds []string
MutArgs [2 ][]string
CliArgs []string
RpcCh chan <- []*rpc .Client
ListFilters *ListFilters
}
type ListFilters struct {
IdExact string
IdSubstr string
IdRegexp *regexp .Regexp
IdPrefix string
IdSuffix string
Parent string
MtimeMin uint64
MtimeMax uint64
MtimeStates S
StatesActive S
StatesInactive S
Limit int
StartIdx int
NoSchema bool
SkipDisconn bool
}
func ParseArgs (args am .A ) *A {
if a , _ := args [APrefix ].(*A ); a != nil {
return a
}
return &A {}
}
func Pass (args *A ) am .A {
return am .A {APrefix : args }
}
func LogArgs (args am .A ) map [string ]string {
a1 := rpc .ParseArgs (args )
a2 := ParseArgs (args )
if a1 == nil && a2 == nil {
return nil
}
return am .AMerge (amhelp .ArgsToLogMap (a1 , 0 ), amhelp .ArgsToLogMap (a2 , 0 ))
}
func completionsNarrowDown(
toComplete string , resources []string ,
) ([]string , cobra .ShellCompDirective ) {
if toComplete != "" {
filtered := []string {}
for _ , resource := range resources {
lr := strings .ToLower (resource )
lc := strings .ToLower (toComplete )
if strings .HasPrefix (lr , lc ) {
filtered = append (filtered , resource )
}
}
return filtered , cobra .ShellCompDirectiveNoFileComp
}
return resources , cobra .ShellCompDirectiveNoFileComp
}
var (
errOpenHistoryFile = errors .New ("failed to open history file" )
errNegativeIndex = errors .New (
"cannot use a negative index when requesting historic commands" )
errOutOfRangeIndex = errors .New (
"index requested greater than number of items in history" )
)
type History struct {
file string
lines []HistoryItem
}
type HistoryItem struct {
Index int
DateTime time .Time
Block string
}
func historyFromFile(file string ) (readline .History , error ) {
var err error
hist := new (History )
hist .file = file
hist .lines , err = openHist (file )
return hist , err
}
func openHist(filename string ) (list []HistoryItem , err error ) {
file , err := os .Open (filename )
if err != nil {
return list , fmt .Errorf ("error opening history file: %s" , err .Error())
}
scanner := bufio .NewScanner (file )
for scanner .Scan () {
var item HistoryItem
err := json .Unmarshal (scanner .Bytes (), &item )
if err != nil || len (item .Block ) == 0 {
continue
}
item .Index = len (list )
list = append (list , item )
}
file .Close ()
return list , nil
}
func (h *History ) Write (s string ) (int , error ) {
block := strings .TrimSpace (s )
if block == "" {
return 0 , nil
}
item := HistoryItem {
DateTime : time .Now (),
Block : block ,
Index : len (h .lines ),
}
if len (h .lines ) == 0 || h .lines [len (h .lines )-1 ].Block != block {
h .lines = append (h .lines , item )
}
line := struct {
DateTime time .Time `json:"datetime"`
Block string `json:"block"`
}{
Block : block ,
DateTime : item .DateTime ,
}
data , err := json .Marshal (line )
if err != nil {
return h .Len (), err
}
f , err := os .OpenFile (h .file , os .O_CREATE |os .O_APPEND |os .O_WRONLY , 0o666 )
if err != nil {
return 0 , fmt .Errorf ("%w: %s" , errOpenHistoryFile , err .Error())
}
_, _ = f .Write (append (data , '\n' ))
f .Close ()
return h .Len (), nil
}
func (h *History ) GetLine (pos int ) (string , error ) {
if pos < 0 {
return "" , errNegativeIndex
}
if pos < len (h .lines ) {
return h .lines [pos ].Block , nil
}
return "" , errOutOfRangeIndex
}
func (h *History ) Len () int {
return len (h .lines )
}
func (h *History ) Dump () interface {} {
return h .lines
}
func setupPrompt(m *console .Menu ) {
p := m .Prompt ()
p .Primary = func () string {
return "\x1b[33marpc>\x1b[0m "
}
}
func listCmdFlags(cmd *cobra .Command ) []string {
flags := []string {}
cmd .Flags ().VisitAll (func (flag *pflag .Flag ) {
if flag .Name == "help" || flag .Name == "completion" {
return
}
flags = append (flags , "--" +flag .Name )
})
return flags
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .