Skip to content

Commit d49e5a7

Browse files
committed
Add .cd helper command and .ls helper command
Not use fmt.Errorf
1 parent 955adff commit d49e5a7

File tree

11 files changed

+186
-23
lines changed

11 files changed

+186
-23
lines changed

.octocov.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# generated by octocov init
22
coverage:
33
if: true
4-
acceptable: 83%
4+
acceptable: 80%
55
exclude:
66
- "**/interactor/mock/*.go"
77
- "**/infrastructure/mock/*.go"

README.md

+7-2
Original file line numberDiff line numberDiff line change
@@ -102,20 +102,22 @@ sqly v0.10.0
102102
enter "SQL query" or "sqly command that begins with a dot".
103103
.help print usage, .exit exit sqly.
104104

105-
sqly (mode: table) >
105+
sqly:~/github/github.com/nao1215/sqly(table)$
106106
```
107107

108108
The sqly shell functions similarly to a common SQL client (e.g., `sqlite3` command or `mysql` command). The sqly shell has helper commands that begin with a dot. The sqly-shell also supports command history, and input completion.
109109

110110
The sqly-shell has the following helper commands:
111111

112112
```shell
113-
sqly (mode: table) > .help
113+
sqly:~/github/github.com/nao1215/sqly(table)$ .help
114+
.cd: change directory
114115
.dump: dump db table to file in a format according to output mode (default: csv)
115116
.exit: exit sqly
116117
.header: print table header
117118
.help: print help message
118119
.import: import file(s)
120+
.ls: print directory contents
119121
.mode: change output mode
120122
.pwd: print current working directory
121123
.tables: print tables
@@ -151,6 +153,9 @@ $ sqly --sql "SELECT * FROM user" --output=test.csv testdata/user.csv
151153
|Ctrl + K |Cut the line after the cursor to the clipboard|
152154
|Ctrl + U |Cut the line before the cursor to the clipboard|
153155
|Ctrl + L |Clear the screen|
156+
|TAB |Completion|
157+
||Previous command|
158+
||Next command|
154159

155160
## Benchmark
156161
CPU: AMD Ryzen 5 3400G with Radeon Vega Graphics

di/wire_gen.go

+6-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

doc/pages/markdown/sqly_helper_command.md

+39-8
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,31 @@ The sqly shell functions similarly to a common SQL client (e.g., `sqlite3` comma
44
The sqly-shell has the following helper commands:
55

66
```shell
7-
sqly (mode: table) > .help
7+
sqly:~/github/github.com/nao1215/sqly(table)$ .help
8+
.cd: change directory
89
.dump: dump db table to file in a format according to output mode (default: csv)
910
.exit: exit sqly
1011
.header: print table header
1112
.help: print help message
1213
.import: import file(s)
14+
.ls: print directory contents
1315
.mode: change output mode
1416
.pwd: print current working directory
1517
.tables: print tables
1618
```
1719

20+
### cd command
21+
22+
```shell
23+
sqly:~/github/github.com/nao1215/sqly(table)$ .cd
24+
sqly:~(table)$ .cd Desktop
25+
sqly:Desktop(table)$
26+
```
27+
1828
### dump command
1929

2030
```shell
21-
sqly (mode: table) > .dump
31+
sqly:~/github/github.com/nao1215/sqly(table)$ .dump
2232
[Usage]
2333
.dump TABLE_NAME FILE_PATH
2434
[Note]
@@ -29,23 +39,23 @@ sqly (mode: table) > .dump
2939
### exit command
3040

3141
```shell
32-
sqly (mode: table) > .exit
42+
sqly:~/github/github.com/nao1215/sqly(table)$.exit
3343

3444
# the sqly shell is closed
3545
```
3646

3747
### header command
3848

3949
```shell
40-
sqly (mode: table) > .header
50+
sqly:~/github/github.com/nao1215/sqly(table)$ .header
4151
[Usage]
4252
.header TABLE_NAME
4353
```
4454

4555
### import command
4656

4757
```shell
48-
sqly (mode: table) > .import
58+
sqly:~/github/github.com/nao1215/sqly(table)$ .import
4959
[Usage]
5060
.import FILE_PATH(S) [--sheet=SHEET_NAME]
5161

@@ -55,10 +65,21 @@ sqly (mode: table) > .import
5565
- If import an Excel file, specify the sheet name with --sheet
5666
```
5767

68+
### ls command
69+
70+
ls command call the `ls` command or `dir` command in the shell.
71+
72+
```shell
73+
sqly:~/github/github.com/nao1215/sqly/di(table)$ .ls
74+
合計 8
75+
-rw-rw-r-- 1 nao nao 661 2月 3 13:09 wire.go
76+
-rw-rw-r-- 1 nao nao 2292 2月 7 10:40 wire_gen.go
77+
```
78+
5879
### mode command
5980

6081
```shell
61-
sqly (mode: table) > .mode
82+
sqly:~/github/github.com/nao1215/sqly(table)$ .mode
6283
[Usage]
6384
.mode OUTPUT_MODE ※ current mode=table
6485
[Output mode list]
@@ -74,13 +95,23 @@ sqly (mode: table) > .mode
7495
### pwd command
7596

7697
```shell
77-
sqly (mode: table) > .pwd
98+
sqly:~/github/github.com/nao1215/sqly(table)$ .pwd
7899
/home/nao
79100
```
80101

81102
### tables command
82103

83104
```shell
84-
sqly (mode: table) > .tables
105+
sqly:~/github/github.com/nao1215/sqly(table)$ .tables
85106
there is no table. use .import for importing file
107+
108+
sqly:~/github/github.com/nao1215/sqly(table)$ .import actor.csv
109+
sqly:~/github/github.com/nao1215/sqly(table)$ .import numeric.csv
110+
sqly:~/github/github.com/nao1215/sqly(table)$ .tables
111+
+------------+
112+
| TABLE NAME |
113+
+------------+
114+
| actor |
115+
| numeric |
116+
+------------+
86117
```

doc/pages/markdown/sqly_shell.md

+7-7
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,22 @@ The sqly-shell also supports command history, and input completion.
55

66
```shell
77
$ sqly
8-
sqly v0.10.0
8+
sqly v0.11.0
99

1010
enter "SQL query" or "sqly command that begins with a dot".
1111
.help print usage, .exit exit sqly.
1212

13-
sqly (mode: table) > .import actor.csv
14-
sqly (mode: table) > .import numeric.csv
15-
sqly (mode: table) > .tables
13+
sqly:~/github/github.com/nao1215/sqly(table)$ .import actor.csv
14+
sqly:~/github/github.com/nao1215/sqly(table)$ .import numeric.csv
15+
sqly:~/github/github.com/nao1215/sqly(table)$ .tables
1616
+------------+
1717
| TABLE NAME |
1818
+------------+
1919
| actor |
2020
| numeric |
2121
+------------+
2222

23-
sqly (mode: table) > SELECT actor, best_movie FROM actor LIMIT 3
23+
sqly:~/github/github.com/nao1215/sqly(table)$ SELECT actor, best_movie FROM actor LIMIT 3
2424
+-------------------+------------------------------+
2525
| actor | best_movie |
2626
+-------------------+------------------------------+
@@ -29,10 +29,10 @@ sqly (mode: table) > SELECT actor, best_movie FROM actor LIMIT 3
2929
| Morgan Freeman | The Dark Knight |
3030
+-------------------+------------------------------+
3131

32-
sqly (mode: table) > .mode ltsv
32+
sqly:~/github/github.com/nao1215/sqly(table)$ .mode ltsv
3333
Change output mode from table to ltsv
3434

35-
sqly (mode: ltsv) > SELECT actor, best_movie FROM actor LIMIT 3
35+
sqly:~/github/github.com/nao1215/sqly(ltsv)$ SELECT actor, best_movie FROM actor LIMIT 3
3636
actor:Harrison Ford best_movie:Star Wars: The Force Awakens
3737
actor:Samuel L. Jackson best_movie:The Avengers
3838
actor:Morgan Freeman best_movie:The Dark Knight

shell/cd.go

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package shell
2+
3+
import (
4+
"context"
5+
"errors"
6+
"os"
7+
)
8+
9+
// cdCommand change directory.
10+
// If there is no argument, change to the home directory.
11+
// If there is one argument, change to the specified directory.
12+
// If there are multiple arguments, return an error.
13+
func (c CommandList) cdCommand(_ context.Context, s *Shell, argv []string) error {
14+
if len(argv) == 0 {
15+
home := os.Getenv("HOME")
16+
if err := os.Chdir(home); err != nil {
17+
return err
18+
}
19+
s.state.cwd = home
20+
return nil
21+
}
22+
if len(argv) > 1 {
23+
return errors.New("too many arguments")
24+
}
25+
26+
if err := os.Chdir(argv[0]); err != nil {
27+
return err
28+
}
29+
s.state.cwd = argv[0]
30+
return nil
31+
}

shell/command.go

+2
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,13 @@ type CommandList map[string]command
2626
// NewCommands return *CommandList that set sqly helper commands.
2727
func NewCommands() CommandList {
2828
c := CommandList{}
29+
c[".cd"] = command{execute: c.cdCommand, name: ".cd", description: "change directory"}
2930
c[".dump"] = command{execute: c.dumpCommand, name: ".dump", description: "dump db table to file in a format according to output mode (default: csv)"}
3031
c[".exit"] = command{execute: c.exitCommand, name: ".exit", description: "exit sqly"}
3132
c[".header"] = command{execute: c.headerCommand, name: ".header", description: "print table header"}
3233
c[".help"] = command{execute: c.helpCommand, name: ".help", description: "print help message"}
3334
c[".import"] = command{execute: c.importCommand, name: ".import", description: "import file(s)"}
35+
c[".ls"] = command{execute: c.lsCommand, name: ".ls", description: "print directory contents"}
3436
c[".mode"] = command{execute: c.modeCommand, name: ".mode", description: "change output mode"}
3537
c[".tables"] = command{execute: c.tablesCommand, name: ".tables", description: "print tables"}
3638
c[".pwd"] = command{execute: c.pwdCommand, name: ".pwd", description: "print current working directory"}

shell/ls.go

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package shell
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"os"
8+
"os/exec"
9+
"runtime"
10+
)
11+
12+
// lsCommand list files and directories.
13+
// If there is no argument, list the files and directories in the current directory.
14+
// If there is one argument, list the files and directories in the specified directory.
15+
// If there are multiple arguments, return an error.
16+
func (c CommandList) lsCommand(_ context.Context, _ *Shell, argv []string) error {
17+
path, err := func() (string, error) {
18+
if len(argv) == 0 {
19+
return ".", nil
20+
}
21+
if len(argv) > 1 {
22+
return "", errors.New("too many arguments")
23+
}
24+
return argv[0], nil
25+
}()
26+
if err != nil {
27+
return err
28+
}
29+
30+
if err := func() error {
31+
if _, err := os.Stat(path); os.IsNotExist(err) {
32+
return fmt.Errorf("no such file or directory: %s", path)
33+
}
34+
35+
var cmd *exec.Cmd
36+
if runtime.GOOS == "windows" {
37+
cmd = exec.Command("cmd", "/c", "dir", "/q", path)
38+
} else {
39+
cmd = exec.Command("ls", "-l", path)
40+
}
41+
cmd.Stdout = os.Stdout
42+
cmd.Stderr = os.Stderr
43+
return cmd.Run()
44+
}(); err != nil {
45+
return err
46+
}
47+
return nil
48+
}

shell/shell.go

+37-3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,34 @@ type Shell struct {
3434
config *config.Config
3535
commands CommandList
3636
usecases Usecases
37+
state *state
38+
}
39+
40+
// state is shell state.
41+
type state struct {
42+
// cwd is current working directory.
43+
cwd string
44+
}
45+
46+
// newState return *state.
47+
func newState() (*state, error) {
48+
dir, err := os.Getwd()
49+
if err != nil {
50+
return nil, err
51+
}
52+
return &state{
53+
cwd: dir,
54+
}, nil
55+
}
56+
57+
// shortCWD return short current working directory.
58+
// If current working directory is home directory, return "~".
59+
func (s *state) shortCWD() string {
60+
home := os.Getenv("HOME")
61+
if s.cwd == home {
62+
return "~"
63+
}
64+
return strings.Replace(s.cwd, home, "~", 1)
3765
}
3866

3967
// NewShell return *Shell.
@@ -42,13 +70,18 @@ func NewShell(
4270
cfg *config.Config,
4371
cmds CommandList,
4472
usecases Usecases,
45-
) *Shell {
73+
) (*Shell, error) {
74+
state, err := newState()
75+
if err != nil {
76+
return nil, err
77+
}
4678
return &Shell{
4779
argument: arg,
4880
config: cfg,
4981
commands: cmds,
5082
usecases: usecases,
51-
}
83+
state: state,
84+
}, nil
5285
}
5386

5487
// Run start sqly shell.
@@ -138,7 +171,7 @@ func (s *Shell) prompt(ctx context.Context) (string, error) {
138171

139172
return prompt.Input(
140173
func() string {
141-
return fmt.Sprintf("sqly (mode: %s) > ", s.argument.Output.Mode)
174+
return fmt.Sprintf("sqly:%s(%s)$ ", s.state.shortCWD(), s.argument.Output.Mode)
142175
}(),
143176
func(d prompt.Document) []prompt.Suggest {
144177
return s.completer(ctx, d)
@@ -190,6 +223,7 @@ func (s *Shell) completer(ctx context.Context, d prompt.Document) []prompt.Sugge
190223
{Text: "tsv", Description: "sqly command argument: tsv output format"},
191224
{Text: "ltsv", Description: "sqly command argument: ltsv output format"},
192225
{Text: "json", Description: "sqly command argument: json output format"},
226+
{Text: "excel", Description: "sqly command argument: excel output format"},
193227
}
194228

195229
for _, v := range s.commands {

shell/shell_test.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,12 @@ func newShell(t *testing.T, args []string) (*Shell, func(), error) {
791791
historyRepository := persistence.NewHistoryRepository(historyDB)
792792
historyInteractor := interactor.NewHistoryInteractor(historyRepository)
793793
usecases := NewUsecases(csvInteractor, tsvInteractor, ltsvInteractor, jsonInteractor, sqLite3Interactor, historyInteractor, excelInteractor)
794-
shellShell := NewShell(arg, configConfig, commandList, usecases)
794+
shellShell, err := NewShell(arg, configConfig, commandList, usecases)
795+
if err != nil {
796+
cleanup2()
797+
cleanup()
798+
return nil, nil, err
799+
}
795800
return shellShell, func() {
796801
cleanup2()
797802
cleanup()

0 commit comments

Comments
 (0)