-
Notifications
You must be signed in to change notification settings - Fork 0
/
nextone.go
185 lines (159 loc) · 3.43 KB
/
nextone.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
package main
import (
"encoding/json"
"flag"
"fmt"
"io"
"log"
"os"
"os/signal"
"os/user"
"path/filepath"
"sort"
"strings"
"syscall"
"github.com/mattn/go-colorable"
"github.com/mgutz/ansi"
"github.com/peterh/liner"
)
// DbFileName is the default file name find in user home
const DbFileName = "db.task"
// HistoryFileName is the name of the nextone cmd line history
const HistoryFileName = ".nextone_history"
// DbEnvPath is an environment variable that helps configure db path
const DbEnvPath = "NEXTONE_DB_PATH"
var dbFlag = flag.String("db", "", "Db path.")
var dbPath string
func main() {
// Ensure that we have an ansi enabled terminal
ansi.DisableColors(false)
stdout := colorable.NewColorableStdout()
flag.Parse()
u, err := user.Current()
if *dbFlag == "" {
// open db.task file in user home
dbPath = os.Getenv(DbEnvPath)
if dbPath == "" {
if err != nil {
fmt.Print(err)
return
}
dbPath = filepath.Join(u.HomeDir, DbFileName)
}
} else {
dbPath = *dbFlag
}
// Open file database
db, err := openDatabase(dbPath)
if err != nil {
fmt.Println(err)
return
}
// sort task
sort.Sort(TaskByTime(db.Tasks))
historyPath := filepath.Join(u.HomeDir, HistoryFileName)
interactive(stdout, db, historyPath)
}
func openDatabase(dbPath string) (*JSONDb, error) {
var db JSONDb
// open db file
f, err := os.Open(dbPath)
if err != nil {
return nil, err
}
decoder := json.NewDecoder(f)
defer f.Close()
err = decoder.Decode(&db)
if err != nil {
return nil, err
}
for _, task := range db.Tasks {
if task.ID >= db.IDGen {
db.IDGen = task.ID
}
}
return &db, nil
}
func backupDatabase(dbPath string, suffix string) (err error) {
// open db file
in, err := os.Open(dbPath)
if err != nil {
return err
}
defer in.Close()
// open db bakup file
out, err := os.Create(dbPath + suffix)
if err != nil {
return err
}
defer out.Close()
if _, err := io.Copy(out, in); err != nil {
return err
}
return nil
}
func saveDatabase(dbPath string, db *JSONDb) (err error) {
// open db file
f, err := os.Create(dbPath)
if err != nil {
return err
}
result, err := json.MarshalIndent(db, "", " ")
if err != nil {
return err
}
fmt.Fprintln(f, string(result))
return nil
}
func interactive(stdout io.Writer, db *JSONDb, historyPath string) {
line := liner.NewLiner()
defer line.Close()
f, err := os.Open(historyPath)
if err == nil {
defer f.Close()
line.ReadHistory(f)
}
line.SetCtrlCAborts(true)
line.SetCompleter(func(line string) (c []string) {
for _, cmd := range commands {
if strings.HasPrefix(cmd.Name, strings.ToLower(line)) {
c = append(c, cmd.Name)
}
}
return
})
saveHistory := func() {
fmt.Println("save history")
f, err := os.Create(historyPath)
if err != nil {
log.Print("Error writing history file: ", err)
} else {
defer f.Close()
line.WriteHistory(f)
}
}
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
// handle ctrl+c event here
saveHistory()
os.Exit(0)
}()
mainPrompt(line, stdout, db)
saveHistory()
}
func mainPrompt(line *liner.State, stdout io.Writer, db *JSONDb) {
if cmdLine, err := line.Prompt("nextone> "); err == nil {
if cmdLine == "quit" {
return
}
execCommand(stdout, db, line, cmdLine)
line.AppendHistory(cmdLine)
mainPrompt(line, stdout, db)
} else if err == liner.ErrPromptAborted {
log.Print("Aborted")
} else {
log.Print("Error reading line: ", err)
}
}