-
Notifications
You must be signed in to change notification settings - Fork 4
/
cmd.go
190 lines (177 loc) · 5.14 KB
/
cmd.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
package main
import (
"bufio"
"errors"
"flag"
"fmt"
"io"
"os"
"strings"
)
const (
tagPrefix = "@" // Prefix for tags passed by user
SimpleNoteKeyLength = 32 // Length of note keys in SimpleNote
maxStdinLen = 1024 * 1024
)
var (
// Actions available for the user. Keys represent name of actions, values, whether action requires note key parameter.
CustomActions = &map[string]bool{
"version": false,
"list": false,
"delete": true,
"edit": true,
"get": true,
}
)
// cmmandLineParser contains both user parameters and configuration file.
type commandLineParser struct {
Params *CommandLineParams
config *UserConfigFile
}
// Command line params represents all actions available to the user from command line.
type CommandLineParams struct {
Content string // Content of the note to be saved
Tags []string // List of tags to be used with requests
Action string // Action represents custom action performed by user
Key string // For some actions Note key is required
Flags map[string]string // Flags are additional params passed with some commands
Piped bool
}
// Interface implementing methods used for command line interaction.
type CommandLineParser interface {
Grab() (*CommandLineParams, error)
getTags([]string) []string
getAction([]string) ([]string, error)
getFlags([]string) []string
}
// NewCommandLineParser returns new command parser interface.
func newCommandLineParser(config MainConfig) (c CommandLineParser) {
return &commandLineParser{
Params: &CommandLineParams{
Flags: make(map[string]string),
},
config: config.GetUserConfig(),
}
}
// Read retrieves all parameters passed from command line.
func (c *commandLineParser) Grab() (params *CommandLineParams, err error) {
args := os.Args[1:]
err = c.parse(args)
return c.Params, err
}
//Get flags retrieves all flags passed by the user.
func (c *commandLineParser) getFlags(args []string) []string {
var flagListItemCount int
var flagListShowDeleted, flagDeletePermanently bool
cmdFlagSet := flag.NewFlagSet("", flag.ExitOnError)
cmdFlagSet.IntVar(&flagListItemCount, "n", -1, "Number of items to show with list command.")
cmdFlagSet.BoolVar(&flagListShowDeleted, "deleted", false, "Whether to show deleted items with list command.")
cmdFlagSet.BoolVar(&flagDeletePermanently, "permanently", false, "If true will permanently delete the note instead of moving it to trash.")
cmdFlagSet.Parse(args)
c.Params.Flags["n"] = ConvertToString(flagListItemCount)
c.Params.Flags["deleted"] = ConvertToString(flagListShowDeleted)
c.Params.Flags["permanently"] = ConvertToString(flagDeletePermanently)
// Return all remaining arguments
return cmdFlagSet.Args()
}
// Check if arguments have any tags defined if so pop it from the list and save.
// Tags are always the first parameter or after keyword.
func (c *commandLineParser) getTags(args []string) []string {
if len(args) > 0 {
for i, arg := range args {
if strings.HasPrefix(arg, tagPrefix) {
c.Params.Tags = append(c.Params.Tags, strings.TrimPrefix(arg, tagPrefix))
// Meaning user passed only tags
if i == len(args)-1 {
return []string{}
}
} else {
return args[i:]
}
}
}
return args
}
// GetAction retrieves custom user action from command line.
func (c *commandLineParser) getAction(args []string) ([]string, error) {
if len(args) == 0 {
return args, nil
}
for action, requiresKey := range *CustomActions {
if action == args[0] {
c.Params.Action = action
if requiresKey {
if len(args) < 2 {
return nil, errors.New("Missing note key parameter.")
}
if len(args[1]) != SimpleNoteKeyLength {
return nil, errors.New("Invalid identifier passed")
}
c.Params.Key = strings.TrimSpace(args[1])
return args[2:], nil
}
return args[1:], nil
}
}
return args, nil
}
// GetStdin retrieves STDIN string if available
func (c *commandLineParser) getStdin() (in string, err error) {
if c.Params.Piped {
reader := bufio.NewReader(os.Stdin)
buf := make([]byte, maxStdinLen)
for {
n, err := reader.Read(buf[:cap(buf)])
if err != nil {
fmt.Println(err.Error())
}
buf = buf[:n]
if n == 0 {
if err != nil {
continue
}
if err == io.EOF {
break
}
return "", err
}
if err != nil && err != io.EOF {
return "", err
}
return string(buf), nil
}
}
return "", nil
}
// Parse retrieves content of a note to be saved.
func (c *commandLineParser) parse(args []string) (err error) {
stat, _ := os.Stdin.Stat()
c.Params.Piped = ((stat.Mode() & os.ModeCharDevice) == 0)
actionless, err := c.getAction(args)
if err != nil {
return err
}
tagless := c.getTags(actionless)
flagless := c.getFlags(tagless)
// If action is defined we don't need any content passed
if c.Params.Piped {
content, err := c.getStdin()
if err != nil {
return err
}
c.Params.Content = content
} else if c.Params.Action == "" {
if len(flagless) > 0 {
c.Params.Content = strings.Join(flagless, " ")
} else {
content, err := WriteToFile("")
if err != nil {
return err
}
c.Params.Content = strings.TrimSpace(content)
}
} else {
return
}
return
}