Skip to content

Commit

Permalink
feat: Add head and tail
Browse files Browse the repository at this point in the history
  • Loading branch information
Allaman committed May 19, 2024
1 parent 2866537 commit 348c97f
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 1 deletion.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Commands:
hash sha256 Calculate Sha256
hash sha512 Calculate Sha512
hash md5 Calculate MD5
head Returns the first n lines
hex to Convert to hexadecimal
hex from Convert hexadecimal back
lorem words Words
Expand All @@ -48,6 +49,7 @@ Commands:
htpasswd Create a htpasswd string
reverse Reverse the input
split Split a string
tail Returns the last n lines
url encode Encode string to valid URL
url decode Decode URL to string
version Show version information
Expand Down
30 changes: 29 additions & 1 deletion cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ type CLI struct {
SHA512 sha512Cmd `cmd:"" name:"sha512" help:"Calculate Sha512"`
MD5 md5Cmd `cmd:"" name:"md5" help:"Calculate MD5"`
} `cmd:"" help:"Calculate SHA Hashsums"`
Hex struct {
Head headCmd `cmd:"" help:"Returns the first n lines"`
Hex struct {
ToHex toHexCmd `cmd:"" name:"to" help:"Convert to hexadecimal"`
FromHex fromHexCmd `cmd:"" name:"from" help:"Convert hexadecimal back"`
} `cmd:"" name:"hex" help:"Hexadeicmal converting"`
Expand All @@ -42,6 +43,7 @@ type CLI struct {
HtPassWD HtPassWDCmd `cmd:"" name:"htpasswd" help:"Create a htpasswd string"`
Reverse reverseCmd `cmd:"" help:"Reverse the input"`
Split splitCmd `cmd:"" help:"Split a string"`
Tail tailCmd `cmd:"" help:"Returns the last n lines"`
URL struct {
Encode encodeURLCmd `cmd:"" help:"Encode string to valid URL"`
Decode decodeURLCmd `cmd:"" help:"Decode URL to string"`
Expand Down Expand Up @@ -322,3 +324,29 @@ func (c *splitCmd) Run(globals *Globals) error {
printOutput(formatSplittedString(splitString(in, c.Sep)), globals.Trim)
return nil
}

type headCmd struct {
Num int `default:"1" short:"n" help:"Number of lines"`
}

func (c *headCmd) Run(globals *Globals) error {
in, err := readFromSTDIN()
if err != nil {
return err
}
printOutput(head(in, c.Num), globals.Trim)
return nil
}

type tailCmd struct {
Num int `default:"1" short:"n" help:"Number of lines"`
}

func (c *tailCmd) Run(globals *Globals) error {
in, err := readFromSTDIN()
if err != nil {
return err
}
printOutput(tail(in, c.Num), globals.Trim)
return nil
}
18 changes: 18 additions & 0 deletions head.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package main

import (
"runtime"
"strings"
)

func head(input string, n int) string {
lines := splitLines(input)
if n > len(lines) {
n = len(lines)
}
lineEnding := "\n"
if runtime.GOOS == "windows" {
lineEnding = "\r\n"
}
return strings.Join(lines[:n], lineEnding)
}
40 changes: 40 additions & 0 deletions head_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

import (
"runtime"
"strings"
"testing"
)

func TestHead(t *testing.T) {
tests := []struct {
input string
n int
expected string
}{
{"line1\nline2\nline3\nline4\nline5\n", 2, "line1\nline2"},
{"line1\nline2\nline3\nline4\nline5\n", 0, ""},
{"line1\nline2\nline3\nline4\nline5\n", 5, "line1\nline2\nline3\nline4\nline5"},
{"line1\nline2\nline3\nline4\nline5\n", 10, "line1\nline2\nline3\nline4\nline5"},
{"line1\nline2\nline3\nline4\nline5", 3, "line1\nline2\nline3"},
{"", 3, ""},
}

for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
// Determine the expected line ending based on the operating system
lineEnding := "\n"
if runtime.GOOS == "windows" {
lineEnding = "\r\n"
}

// Replace expected line endings with the appropriate ones for the OS
expected := strings.ReplaceAll(tt.expected, "\n", lineEnding)

result := head(tt.input, tt.n)
if result != expected {
t.Errorf("head(%d) = %q; want %q", tt.n, result, expected)
}
})
}
}
18 changes: 18 additions & 0 deletions tail.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package main

import (
"runtime"
"strings"
)

func tail(input string, n int) string {
lines := splitLines(input)
if n > len(lines) {
n = len(lines)
}
lineEnding := "\n"
if runtime.GOOS == "windows" {
lineEnding = "\r\n"
}
return strings.Join(lines[len(lines)-n:], lineEnding)
}
40 changes: 40 additions & 0 deletions tail_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

import (
"runtime"
"strings"
"testing"
)

func TestTail(t *testing.T) {
tests := []struct {
input string
n int
expected string
}{
{"line1\nline2\nline3\nline4\nline5\n", 2, "line4\nline5"},
{"line1\nline2\nline3\nline4\nline5\n", 0, ""},
{"line1\nline2\nline3\nline4\nline5\n", 5, "line1\nline2\nline3\nline4\nline5"},
{"line1\nline2\nline3\nline4\nline5\n", 10, "line1\nline2\nline3\nline4\nline5"},
{"line1\nline2\nline3\nline4\nline5", 3, "line3\nline4\nline5"},
{"", 3, ""},
}

for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
// Determine the expected line ending based on the operating system
lineEnding := "\n"
if runtime.GOOS == "windows" {
lineEnding = "\r\n"
}

// Replace expected line endings with the appropriate ones for the OS
expected := strings.ReplaceAll(tt.expected, "\n", lineEnding)

result := tail(tt.input, tt.n)
if result != expected {
t.Errorf("tail(%d) = %q; want %q", tt.n, result, expected)
}
})
}
}
13 changes: 13 additions & 0 deletions util.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package main

import (
"bufio"
"fmt"
"io"
"os"
"strings"
)

func readFromSTDIN() (string, error) {
Expand All @@ -22,3 +24,14 @@ func printOutput(s interface{}, trim bool) {
fmt.Println(s)
}
}

func splitLines(input string) []string {
var lines []string
scanner := bufio.NewScanner(strings.NewReader(input))
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
line := scanner.Text()
lines = append(lines, line)
}
return lines
}
21 changes: 21 additions & 0 deletions util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"io"
"os"
"reflect"
"testing"
)

Expand Down Expand Up @@ -80,3 +81,23 @@ func TestPrintOutput(t *testing.T) {
})
}
}

func TestSplitLines(t *testing.T) {
tests := []struct {
input string
expected []string
}{
{"line1\nline2\nline3\n", []string{"line1", "line2", "line3"}},
{"line1\nline2\nline3", []string{"line1", "line2", "line3"}},
// {"", []string{}}, -- TODO: this test fails probably due to reflect.DeepEqual
}

for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
result := splitLines(tt.input)
if !reflect.DeepEqual(result, tt.expected) {
t.Errorf("splitLines(%q) = %v; want %v", tt.input, result, tt.expected)
}
})
}
}

0 comments on commit 348c97f

Please sign in to comment.