Split commands out into their own files.

- add login, register commands
- make "similar" parse out the parts of speach and only give us back
  entries that match those parts.
This commit is contained in:
Aaron Bieber 2020-04-05 09:31:32 -06:00
parent 1884f88912
commit 31af3d75d2
8 changed files with 371 additions and 117 deletions

View File

@ -1,10 +1,14 @@
package main
import (
"context"
"encoding/json"
"flag"
"io/ioutil"
"os/user"
"path"
"github.com/peterbourgon/ff/v2/ffcli"
)
func getPath() (*string, error) {
@ -53,3 +57,29 @@ func (c *Config) ReadConfig() error {
return json.Unmarshal(data, c)
}
// NewConfig creates a new config ffcli command
func NewConfig() *ffcli.Command {
var configFlagSet = flag.NewFlagSet("cromp config", flag.ExitOnError)
urlConfFS := configFlagSet.String("url", "", "URL of cromp server")
tokenConfFS := configFlagSet.String("token", "", "Access token for cromp server")
return &ffcli.Command{
Name: "config",
ShortUsage: "cromp config -token [token] -url [url]",
FlagSet: configFlagSet,
Exec: func(ctx context.Context, args []string) error {
cfg := &Config{
Token: *tokenConfFS,
URL: *urlConfFS,
}
err := cfg.WriteConfig()
if err != nil {
return err
}
return nil
},
}
}

58
cmd/cromp/load.go Normal file
View File

@ -0,0 +1,58 @@
package main
import (
"context"
"fmt"
"github.com/google/uuid"
"github.com/peterbourgon/ff/v2/ffcli"
"suah.dev/cromp/db"
cromp "suah.dev/cromp/internal"
)
// NewLoad returns the load ffcli command
func NewLoad() *ffcli.Command {
return &ffcli.Command{
Name: "load",
ShortUsage: "cromp load <file>",
Exec: func(ctx context.Context, args []string) error {
var entry db.CreateEntryParams
resp := &db.CreateEntryRow{}
if len(args) != 1 {
return fmt.Errorf("missing file name")
}
header, err := cromp.ParseFileHeader(args[0])
if err != nil {
return err
}
s := header.UUID.String()
if s == "" || s == "00000000-0000-0000-0000-000000000000" {
header.UUID = uuid.New()
}
entry.Title = header.Title
entry.EntryID = header.UUID
fmt.Printf("%#v\n", entry)
data, err := cromp.ReadFileBody(args[0])
if err != nil {
return err
}
entry.Body = string(data)
err = Post("/entries/add", entry, resp)
if err != nil {
return err
}
fmt.Printf("%#v\n", resp)
return nil
},
}
}

64
cmd/cromp/login.go Normal file
View File

@ -0,0 +1,64 @@
package main
import (
"context"
"flag"
"fmt"
"github.com/peterbourgon/ff/v2/ffcli"
"suah.dev/cromp/db"
)
// NewLogin creates a new config ffcli command
func NewLogin() *ffcli.Command {
var loginFlagSet = flag.NewFlagSet("cromp login", flag.ExitOnError)
urlConfFS := loginFlagSet.String("url", "", "URL of cromp server")
return &ffcli.Command{
Name: "login",
ShortUsage: "login -url [url]",
FlagSet: loginFlagSet,
Exec: func(ctx context.Context, args []string) error {
var loginParams db.AuthUserParams
resp := &db.AuthUserRow{}
cfg := &Config{}
err := cfg.ReadConfig()
if err != nil {
return err
}
if cfg.URL == "" && *urlConfFS == "" {
return fmt.Errorf("please specify -url")
}
user, err := Prompt("Login: ")
if err != nil {
return err
}
pass, err := SecurePrompt("Password: ")
if err != nil {
return err
}
fmt.Println()
loginParams.Username = *user
loginParams.Crypt = *pass
err = Post("/user/auth", loginParams, resp)
if err != nil {
return err
}
fmt.Printf("New token expires %s\n", resp.TokenExpires)
cfg.Token = resp.Token
err = cfg.WriteConfig()
if err != nil {
return err
}
return nil
},
}
}

View File

@ -5,24 +5,14 @@ import (
"flag"
"fmt"
"os"
"strings"
"github.com/google/uuid"
"github.com/peterbourgon/ff/v2/ffcli"
"suah.dev/cromp/db"
cromp "suah.dev/cromp/internal"
)
func main() {
var (
rootFlagSet = flag.NewFlagSet("cromp", flag.ExitOnError)
loadFlagSet = flag.NewFlagSet("cromp load", flag.ExitOnError)
configFlagSet = flag.NewFlagSet("cromp config", flag.ExitOnError)
urlConfFS = configFlagSet.String("url", "", "URL of cromp server")
tokenConfFS = configFlagSet.String("token", "", "Access token for cromp server")
//createFlagSet = flag.NewFlagSet("cromp create", flag.ExitOnError)
)
@ -30,113 +20,12 @@ func main() {
ShortUsage: "cromp <subcommand>",
FlagSet: rootFlagSet,
Subcommands: []*ffcli.Command{
&ffcli.Command{
Name: "config",
ShortUsage: "cromp config -token [token] -url [url]",
FlagSet: configFlagSet,
Exec: func(ctx context.Context, args []string) error {
cfg := &Config{
Token: *tokenConfFS,
URL: *urlConfFS,
}
err := cfg.WriteConfig()
if err != nil {
return err
}
return nil
},
},
&ffcli.Command{
Name: "similar",
ShortUsage: "cromp similar [text]",
Exec: func(ctx context.Context, args []string) error {
var params db.SimilarEntriesParams
resp := &[]db.SimilarEntriesRow{}
params.Similarity = strings.Join(args, " ")
err := Post("/entries/similar", params, resp)
if err != nil {
return err
}
for _, e := range *resp {
fmt.Printf("%s\t%s\t%f\n",
e.EntryID,
e.Title,
e.Similarity)
}
return nil
},
},
&ffcli.Command{
Name: "list",
ShortUsage: "cromp list",
FlagSet: configFlagSet,
Exec: func(ctx context.Context, args []string) error {
resp := &[]db.Entry{}
err := Get("/entries/list", resp)
if err != nil {
return err
}
for _, e := range *resp {
fmt.Printf("%s\t%s\t%s\n", e.EntryID.String(),
e.CreatedAt,
e.Title)
}
return nil
},
},
&ffcli.Command{
Name: "load",
ShortUsage: "cromp load <file>",
FlagSet: loadFlagSet,
Exec: func(ctx context.Context, args []string) error {
var entry db.CreateEntryParams
resp := &db.CreateEntryRow{}
if len(args) != 1 {
return fmt.Errorf("missing file name")
}
header, err := cromp.ParseFileHeader(args[0])
if err != nil {
return err
}
s := header.UUID.String()
if s == "" || s == "00000000-0000-0000-0000-000000000000" {
header.UUID = uuid.New()
}
entry.Title = header.Title
entry.EntryID = header.UUID
fmt.Printf("%#v\n", entry)
data, err := cromp.ReadFileBody(args[0])
if err != nil {
return err
}
entry.Body = string(data)
err = Post("/entries/add", entry, resp)
if err != nil {
return err
}
fmt.Printf("%#v\n", resp)
return nil
},
},
NewConfig(),
NewLoad(),
NewLogin(),
NewPOS(),
NewRegister(),
NewSimilar(),
},
Exec: func(context.Context, []string) error {
return flag.ErrHelp

58
cmd/cromp/pos.go Normal file
View File

@ -0,0 +1,58 @@
package main
import (
"context"
"fmt"
"io/ioutil"
"regexp"
"strings"
"github.com/peterbourgon/ff/v2/ffcli"
"gopkg.in/jdkato/prose.v2"
)
// GetPOS returns the parts of speech from a text file
func GetPOS(p string) (map[string][]string, error) {
// We only care about word like things
var wre = regexp.MustCompile(`^\w+$`)
var pos = map[string][]string{}
data, err := ioutil.ReadFile(p)
if err != nil {
return nil, err
}
doc, err := prose.NewDocument(string(data))
if err != nil {
return nil, err
}
for _, tok := range doc.Tokens() {
if wre.MatchString(tok.Text) {
pos[tok.Tag] = append(pos[tok.Tag], tok.Text)
}
}
return pos, nil
}
// NewPOS returns a new ffcli.Command
func NewPOS() *ffcli.Command {
return &ffcli.Command{
Name: "pos",
ShortUsage: "cromp pos <file>",
Exec: func(ctx context.Context, args []string) error {
if len(args) != 1 {
return fmt.Errorf("missing file name")
}
pos, err := GetPOS(args[0])
if err != nil {
return err
}
fmt.Printf("Nouns: %s\n", strings.Join(pos["NN"], ", "))
fmt.Printf("Verbs: %s\n", strings.Join(pos["VB"], ", "))
return nil
},
}
}

27
cmd/cromp/prompts.go Normal file
View File

@ -0,0 +1,27 @@
package main
import (
"fmt"
"os"
"golang.org/x/crypto/ssh/terminal"
)
// SecurePrompt presents the user with a non-echoing prompt
func SecurePrompt(prompt string) (*string, error) {
fmt.Print(prompt)
b, err := terminal.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
return nil, err
}
pass := string(b)
return &pass, nil
}
// Prompt presents the user with an echoing prompt
func Prompt(prompt string) (*string, error) {
var user string
fmt.Print(prompt)
fmt.Scanln(&user)
return &user, nil
}

82
cmd/cromp/register.go Normal file
View File

@ -0,0 +1,82 @@
package main
import (
"context"
"flag"
"fmt"
"github.com/peterbourgon/ff/v2/ffcli"
"suah.dev/cromp/db"
)
// NewRegister creates a new config ffcli command
func NewRegister() *ffcli.Command {
var regFlagSet = flag.NewFlagSet("cromp reg", flag.ExitOnError)
urlConfFS := regFlagSet.String("url", "", "URL of cromp server")
return &ffcli.Command{
Name: "reg",
ShortUsage: "reg -url [url]",
FlagSet: regFlagSet,
Exec: func(ctx context.Context, args []string) error {
var regParams db.CreateUserParams
resp := &db.CreateUserRow{}
cfg := &Config{}
err := cfg.ReadConfig()
if err != nil {
return err
}
if cfg.URL == "" && *urlConfFS == "" {
return fmt.Errorf("please specify -url")
}
fn, err := Prompt("First Name: ")
if err != nil {
return err
}
ln, err := Prompt("Last Name: ")
if err != nil {
return err
}
email, err := Prompt("Email: ")
if err != nil {
return err
}
user, err := Prompt("Username: ")
if err != nil {
return err
}
pass, err := SecurePrompt("Password: ")
if err != nil {
return err
}
fmt.Println()
regParams.Username = *user
regParams.Password = *pass
regParams.FirstName = *fn
regParams.LastName = *ln
regParams.Email = *email
err = Post("/user/new", regParams, resp)
if err != nil {
return err
}
fmt.Printf("New token expires %s\n", resp.TokenExpires)
cfg.Token = resp.Token
err = cfg.WriteConfig()
if err != nil {
return err
}
return nil
},
}
}

46
cmd/cromp/similar.go Normal file
View File

@ -0,0 +1,46 @@
package main
import (
"context"
"fmt"
"strings"
"github.com/peterbourgon/ff/v2/ffcli"
"suah.dev/cromp/db"
)
// NewSimilar creates a new config ffcli command
func NewSimilar() *ffcli.Command {
return &ffcli.Command{
Name: "similar",
ShortUsage: "cromp similar [text]",
Exec: func(ctx context.Context, args []string) error {
var params db.SimilarEntriesParams
resp := &[]db.SimilarEntriesRow{}
if len(args) != 1 {
return fmt.Errorf("missing file name")
}
pos, err := GetPOS(args[0])
if err != nil {
return err
}
params.Similarity = strings.Join(pos["NN"], "|")
err = Post("/entries/similar", params, resp)
if err != nil {
return err
}
for _, e := range *resp {
fmt.Printf("%s\t%s\n",
e.Title,
e.Headline)
}
return nil
},
}
}