Skip to content

Commit

Permalink
Merge pull request #18 from onozaty/develop/v1.12.0
Browse files Browse the repository at this point in the history
Develop v1.12.0
  • Loading branch information
onozaty committed Dec 5, 2021
2 parents 3f19139 + aa9fe0a commit 886c38f
Show file tree
Hide file tree
Showing 7 changed files with 379 additions and 1 deletion.
71 changes: 71 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* [count](#count) Count the number of records.
* [exclude](#exclude) Exclude rows by included in another CSV file.
* [filter](#filter) Filter rows by condition.
* [head](#head) Show head few rows.
* [header](#header) Show header.
* [include](#include) Filter rows by included in another CSV file.
* [join](#join) Join CSV files.
Expand Down Expand Up @@ -454,6 +455,76 @@ Please refer to the following for the syntax of regular expressions.

* https://pkg.go.dev/regexp/syntax

## head

Show the first few rows.
It will be shown in table format.

### Usage

```
csvt head -i INPUT [-n NUMBER]
```

```
Usage:
csvt head [flags]
Flags:
-i, --input string Input CSV file path.
-n, --number int The number of records to show. If not specified, it will be the first 10 rows. (default 10)
-h, --help help for head
```

### Example

The contents of `input.csv`.

```
UserID,Name,Age
1,"Taro, Yamada",10
2,Hanako,21
3,Smith,30
4,Jun,22
5,Kevin,10
6,Bob,
7,Jackson,51
8,Harry,22
9,Olivia,32
10,Aiko,35
11,Kaede,9
12,Sakura,12
13,Momoka,16
```

```
$ csvt head -i input.csv
+--------+--------------+-----+
| UserID | Name | Age |
+--------+--------------+-----+
| 1 | Taro, Yamada | 10 |
| 2 | Hanako | 21 |
| 3 | Smith | 30 |
| 4 | Jun | 22 |
| 5 | Kevin | 10 |
| 6 | Bob | |
| 7 | Jackson | 51 |
| 8 | Harry | 22 |
| 9 | Olivia | 32 |
| 10 | Aiko | 35 |
+--------+--------------+-----+
```

```
$ csvt head -i input.csv -n 2
+--------+--------------+-----+
| UserID | Name | Age |
+--------+--------------+-----+
| 1 | Taro, Yamada | 10 |
| 2 | Hanako | 21 |
+--------+--------------+-----+
```

## header

Show the header of CSV file.
Expand Down
89 changes: 89 additions & 0 deletions cmd/head.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package cmd

import (
"fmt"
"io"

"github.com/olekukonko/tablewriter"
"github.com/onozaty/csvt/csv"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)

func newHeadCmd() *cobra.Command {

headCmd := &cobra.Command{
Use: "head",
Short: "Show head few rows",
RunE: func(cmd *cobra.Command, args []string) error {

format, err := getFlagBaseCsvFormat(cmd.Flags())
if err != nil {
return err
}

inputPath, _ := cmd.Flags().GetString("input")
number, _ := cmd.Flags().GetInt("number")

// 表示件数は1以上
if number <= 0 {
return fmt.Errorf("number must be greater than or equal to 1")
}

// 引数の解析に成功した時点で、エラーが起きてもUsageは表示しない
cmd.SilenceUsage = true

return runHead(
format,
inputPath,
number,
cmd.OutOrStdout())
},
}

headCmd.Flags().StringP("input", "i", "", "Input CSV file path.")
headCmd.MarkFlagRequired("input")
headCmd.Flags().IntP("number", "n", 10, "The number of records to show. If not specified, it will be the first 10 rows.")

return headCmd
}

func runHead(format csv.Format, inputPath string, number int, writer io.Writer) error {

reader, close, err := setupInput(inputPath, format)
if err != nil {
return err
}
defer close()

return head(reader, number, writer)
}

func head(reader csv.CsvReader, number int, writer io.Writer) error {

columnNames, err := reader.Read()
if err != nil {
return errors.Wrap(err, "failed to read the input CSV file")
}

table := tablewriter.NewWriter(writer)
table.SetAutoFormatHeaders(false)
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.SetAlignment(tablewriter.ALIGN_LEFT)
table.SetHeader(columnNames)

for i := 0; i < number; i++ {
row, err := reader.Read()
if err == io.EOF {
break
}
if err != nil {
return errors.Wrap(err, "failed to read the input CSV file")
}

table.Append(row)
}

table.Render()
return nil
}
212 changes: 212 additions & 0 deletions cmd/head_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
package cmd

import (
"bytes"
"os"
"testing"
)

func TestHeadCmd(t *testing.T) {

s := `ID,Name,Company ID
1,Yamada,1
2,Ichikawa,
3,"Hanako, Sato",3
4,Otani,3
5,,2
6,Suzuki,1
7,Jane,2
8,Michel,3
9,1,3
10,Ken,3
11,Suzuki,2
12,Z,
`
f := createTempFile(t, s)
defer os.Remove(f)

rootCmd := newRootCmd()
rootCmd.SetArgs([]string{
"head",
"-i", f,
})

buf := new(bytes.Buffer)
rootCmd.SetOutput(buf)

err := rootCmd.Execute()
if err != nil {
t.Fatal("failed test\n", err)
}

result := buf.String()

except := `+----+--------------+------------+
| ID | Name | Company ID |
+----+--------------+------------+
| 1 | Yamada | 1 |
| 2 | Ichikawa | |
| 3 | Hanako, Sato | 3 |
| 4 | Otani | 3 |
| 5 | | 2 |
| 6 | Suzuki | 1 |
| 7 | Jane | 2 |
| 8 | Michel | 3 |
| 9 | 1 | 3 |
| 10 | Ken | 3 |
+----+--------------+------------+
`
if result != except {
t.Fatal("failed test\n", result)
}
}

func TestHeadCmd_number(t *testing.T) {

s := `ID,Name,Company ID
1,Yamada,1
2,Ichikawa,
3,"Hanako, Sato",3
4,Otani,3
5,,2
`
f := createTempFile(t, s)
defer os.Remove(f)

rootCmd := newRootCmd()
rootCmd.SetArgs([]string{
"head",
"-i", f,
"-n", "3",
})

buf := new(bytes.Buffer)
rootCmd.SetOutput(buf)

err := rootCmd.Execute()
if err != nil {
t.Fatal("failed test\n", err)
}

result := buf.String()

except := `+----+--------------+------------+
| ID | Name | Company ID |
+----+--------------+------------+
| 1 | Yamada | 1 |
| 2 | Ichikawa | |
| 3 | Hanako, Sato | 3 |
+----+--------------+------------+
`
if result != except {
t.Fatal("failed test\n", result)
}
}

func TestHeadCmd_less(t *testing.T) {

s := `ID,Name,Company ID
1,Yamada,1
2,Ichikawa,
`
f := createTempFile(t, s)
defer os.Remove(f)

rootCmd := newRootCmd()
rootCmd.SetArgs([]string{
"head",
"-i", f,
})

buf := new(bytes.Buffer)
rootCmd.SetOutput(buf)

err := rootCmd.Execute()
if err != nil {
t.Fatal("failed test\n", err)
}

result := buf.String()

except := `+----+----------+------------+
| ID | Name | Company ID |
+----+----------+------------+
| 1 | Yamada | 1 |
| 2 | Ichikawa | |
+----+----------+------------+
`
if result != except {
t.Fatal("failed test\n", result)
}
}

func TestHeadCmd_format(t *testing.T) {

s := "col1,col2;1,a;2,b;"

f := createTempFile(t, s)
defer os.Remove(f)

rootCmd := newRootCmd()
rootCmd.SetArgs([]string{
"head",
"-i", f,
"--sep", ";",
})

buf := new(bytes.Buffer)
rootCmd.SetOutput(buf)

err := rootCmd.Execute()
if err != nil {
t.Fatal("failed test\n", err)
}

result := buf.String()

except := `+------+------+
| col1 | col2 |
+------+------+
| 1 | a |
| 2 | b |
+------+------+
`
if result != except {
t.Fatal("failed test\n", result)
}
}

func TestHeadCmd_invalidNumber(t *testing.T) {

f := createTempFile(t, "")
defer os.Remove(f)

rootCmd := newRootCmd()
rootCmd.SetArgs([]string{
"head",
"-i", f,
"-n", "0",
})

err := rootCmd.Execute()
if err == nil || err.Error() != "number must be greater than or equal to 1" {
t.Fatal("failed test\n", err)
}
}

func TestHeadCmd_empty(t *testing.T) {

f := createTempFile(t, "")
defer os.Remove(f)

rootCmd := newRootCmd()
rootCmd.SetArgs([]string{
"head",
"-i", f,
})

err := rootCmd.Execute()
if err == nil || err.Error() != "failed to read the input CSV file: EOF" {
t.Fatal("failed test\n", err)
}
}
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func newRootCmd() *cobra.Command {
rootCmd.AddCommand(newAddCmd())
rootCmd.AddCommand(newSortCmd())
rootCmd.AddCommand(newSplitCmd())
rootCmd.AddCommand(newHeadCmd())

for _, c := range rootCmd.Commands() {
// フラグ以外は受け付けないように
Expand Down
Loading

0 comments on commit 886c38f

Please sign in to comment.