Skip to content

Commit

Permalink
feat: display usages in CLI and in README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
moul committed Sep 9, 2020
1 parent 532010f commit 2efb668
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 30 deletions.
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@ generate: install
echo 'foo@bar:~$$ generate-fake-data | prefix' > .tmp/default.txt
generate-fake-data --seed=42 --lines=10 --dict=lorem-ipsum --no-stderr | prefix >> .tmp/default.txt

echo 'foo@bar:~$$ generate-fake-data | prefix -format="#{{.LineNumber3}} {{.Uptime}} {{.Duration}} | "' > .tmp/example-1.txt
generate-fake-data --seed=42 --lines=10 --dict=lorem-ipsum --no-stderr | prefix --format="#{{.LineNumber3}} {{.Uptime}} {{.Duration}} | " >> .tmp/example-1.txt
echo 'foo@bar:~$$ generate-fake-data | prefix -format="#{{.LineNumber3}} {{.ShortUptime}} {{.ShortDuration}} | "' > .tmp/example-1.txt
generate-fake-data --seed=42 --lines=10 --dict=lorem-ipsum --no-stderr | prefix --format="#{{.LineNumber3}} {{.ShortUptime}} {{.ShortDuration}} | " >> .tmp/example-1.txt

echo 'foo@bar:~$$ generate-fake-data | prefix -format="{{.LineNumber3}} "' > .tmp/example-2.txt
generate-fake-data --seed=42 --lines=10 --dict=lorem-ipsum --no-stderr --sleep-max=0 | prefix --format="{{.LineNumber3}} " >> .tmp/example-2.txt

echo 'foo@bar:~$$ generate-fake-data | prefix -format=">>> "' > .tmp/example-3.txt
generate-fake-data --seed=42 --lines=10 --dict=lorem-ipsum --no-stderr --sleep-max=0 | prefix --format=">>> " >> .tmp/example-3.txt

echo 'foo@bar:~$$ generate-fake-data | prefix -format="{{SLOW_LINES}} up={{.ShortUptime}} | "' > .tmp/example-4.txt
generate-fake-data --seed=4242 --lines=10 --sleep-max=1.5s --dict=lorem-ipsum --no-stderr | prefix --format="{{SLOW_LINES}} up={{.ShortUptime}} | " >> .tmp/example-4.txt

embedmd -w README.md
#rm -rf .tmp
76 changes: 62 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,61 @@

## Usage

TODO
[embedmd]:# (.tmp/usage.txt console)
```console
foo@bar:~$ prefix -h
Usage of prefix:
USAGE
prefix [flags] file

FLAGS
-format string
format string (default "{{.LineNumber3}} upt={{.Uptime}} dur={{.Duration}} ")
format string (default "{{DEFAULT}} ")

SYNTAX
{{.Duration | short_duration}} {{.Duration}} displayed in a pretty & short format (len<=7)
{{.Duration}} time since previous line was started
{{.Format}} the value you set with -format
{{.LineNumber3}} alias for {{printf "%-3d" .LineNumber}}
{{.LineNumber4}} alias for {{printf "%-4d" .LineNumber}}
{{.LineNumber5}} alias for {{printf "%-5d" .LineNumber}}
{{.LineNumber}} display line number
{{.ShortDuration}} alias for {{.Duration | short_duration}}
{{.ShortUptime}} alias for {{.Uptime | short_duration}}
{{.Uptime | short_duration}} {{.Uptime}} displayed in a pretty & short format (len<=7)
{{.Uptime}} time since the the prefixer was initialized
{{env "USER"}} replace with content of the $USER env var
{{now | unixEpoch}} current timestamp
{{now}} current date (format: 2006-01-02 15:04:05.999999999 -0700 MST)
{{uuidv4}} UUID of the v4 (randomly generated) type

the following helpers are also available:
- from the text/template library https://golang.org/pkg/text/template/
- from the sprig project https://github.com/masterminds/sprig#usage

PRESETS
{{DEFAULT}} {{.LineNumber3}} up={{.ShortUptime}} d={{.ShortDuration}} |
{{SLOW_LINES}} {{if (gt .Duration 1000000000)}}SLOW{{else}} {{end}} {{.Duration | short_duration}}

EXAMPLES
prefix apache.log
prefix -format=">>>" apache.log
tail -f apache.log | prefix -
my-cool-program 2>&1 | prefix -format="#{{.LineNumber5}} " -
```

[embedmd]:# (.tmp/example-1.txt console)
```console
foo@bar:~$ generate-fake-data | prefix -format="#{{.LineNumber3}} {{.Uptime}} {{.Duration}} | "
#1 61.4µs 69.5µs | At illum ut est sit soluta nulla numquam.
#2 113.8ms 113.7ms | Sunt quaerat ea dolores facere deleniti culpa numquam.
#3 328.8ms 215.1ms | Distinctio maxime consequatur est qui corporis sunt officia.
#4 607.2ms 278.3ms | Et quia odit molestias voluptas porro repellendus magnam.
#5 899.5ms 292.3ms | Corporis eos rem non hic esse optio quisquam.
#6 1.1s 211.7ms | Natus earum molestias iste architecto porro et blanditiis.
#7 1.3s 238.1ms | Eum repellendus nostrum qui eius suscipit fugit quia.
#8 1.4s 50.6ms | Et nesciunt quod fuga ut vel pariatur libero.
#9 1.6s 209.7ms | Rerum omnis soluta facilis voluptatem possimus et voluptas.
#10 1.9s 274.8ms | Possimus harum voluptatibus aperiam voluptatibus qui autem quam.
foo@bar:~$ generate-fake-data | prefix -format="#{{.LineNumber3}} {{.ShortUptime}} {{.ShortDuration}} | "
#1 44.5µs 48.3µs | At illum ut est sit soluta nulla numquam.
#2 112ms 112ms | Sunt quaerat ea dolores facere deleniti culpa numquam.
#3 327ms 215ms | Distinctio maxime consequatur est qui corporis sunt officia.
#4 605.4ms 278.3ms | Et quia odit molestias voluptas porro repellendus magnam.
#5 897.7ms 292.3ms | Corporis eos rem non hic esse optio quisquam.
#6 1.1s 211.6ms | Natus earum molestias iste architecto porro et blanditiis.
#7 1.3s 238.3ms | Eum repellendus nostrum qui eius suscipit fugit quia.
#8 1.4s 50.5ms | Et nesciunt quod fuga ut vel pariatur libero.
#9 1.6s 209.9ms | Rerum omnis soluta facilis voluptatem possimus et voluptas.
#10 1.9s 274.7ms | Possimus harum voluptatibus aperiam voluptatibus qui autem quam.
```

[embedmd]:# (.tmp/example-2.txt console)
Expand Down Expand Up @@ -73,6 +106,21 @@ foo@bar:~$ generate-fake-data | prefix -format=">>> "
>>> Rerum omnis soluta facilis voluptatem possimus et voluptas.
```

[embedmd]:# (.tmp/example-4.txt console)
```console
foo@bar:~$ generate-fake-data | prefix -format="{{SLOW_LINES}} up={{.ShortUptime}} | "
109.3µs up=129.7µs | Rerum natus quo quo explicabo tempore et delectus.
SLOW 1s up=1s | Dolor blanditiis voluptas dolorum sint laudantium eveniet amet.
SLOW 1.3s up=2.4s | Qui asperiores molestiae est quia est eum omnis.
SLOW 1.3s up=3.6s | Illum explicabo aut illum iste pariatur aut laudantium.
982.1ms up=4.6s | Quibusdam asperiores consequatur est dolores quas dolor ipsam.
185.4ms up=4.8s | Possimus qui non rem qui cum sit temporibus.
167.5ms up=5s | Ea debitis sit deleniti cum ut adipisci in.
520.5ms up=5.5s | Eveniet molestias voluptatem voluptatem deserunt nisi tempora iusto.
215.1ms up=5.7s | Fugiat minus quam eos voluptatem labore sit velit.
SLOW 1s up=6.7s | Enim aut autem tenetur fugit minima quo atque.
```

## Install

### Using go
Expand Down
67 changes: 65 additions & 2 deletions cmd/prefix/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"log"
"os"
"sort"

"moul.io/prefix"
)
Expand All @@ -20,9 +21,14 @@ func main() {
}
}

var (
flags = flag.NewFlagSet("prefix", flag.ExitOnError)
format = flags.String("format", "{{DEFAULT}} ", "format string")
)

func run(args []string) error {
flags := flag.NewFlagSet("prefix", flag.ExitOnError)
format := flags.String("format", prefix.DefaultFormat, "format string")
flags.Usage = usage

if err := flags.Parse(args[1:]); err != nil {
return err
}
Expand Down Expand Up @@ -64,3 +70,60 @@ func run(args []string) error {
}
return nil
}

func usage() {
// usage
{
fmt.Fprintln(os.Stderr, `USAGE`)
fmt.Fprintln(os.Stderr, ` prefix [flags] file`)
fmt.Fprintln(os.Stderr)
}

// flags
{
fmt.Fprintln(os.Stderr, `FLAGS`)
flags.PrintDefaults()
fmt.Fprintln(os.Stderr)
}

// syntax
{
fmt.Fprintln(os.Stderr, `SYNTAX`)
keys := []string{}
for key := range prefix.AvailablePatterns {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
fmt.Fprintf(os.Stderr, " %-35s %s\n", key, prefix.AvailablePatterns[key])
}
fmt.Fprintln(os.Stderr)
fmt.Fprintln(os.Stderr, " the following helpers are also available:")
fmt.Fprintln(os.Stderr, " - from the text/template library https://golang.org/pkg/text/template/")
fmt.Fprintln(os.Stderr, " - from the sprig project https://github.com/masterminds/sprig#usage")
fmt.Fprintln(os.Stderr)
}

// presets
{
fmt.Fprintln(os.Stderr, `PRESETS`)
keys := []string{}
for key := range prefix.AvailablePresets {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
fmt.Fprintf(os.Stderr, " %-20s %s\n", key, prefix.AvailablePresets[key])
}
fmt.Fprintln(os.Stderr)
}

// examples
{
fmt.Fprintln(os.Stderr, `EXAMPLES`)
fmt.Fprintln(os.Stderr, ` prefix apache.log`)
fmt.Fprintln(os.Stderr, ` prefix -format=">>>" apache.log`)
fmt.Fprintln(os.Stderr, ` tail -f apache.log | prefix -`)
fmt.Fprintln(os.Stderr, ` my-cool-program 2>&1 | prefix -format="#{{.LineNumber5}} " -`)
}
}
2 changes: 1 addition & 1 deletion cmd/prefix/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func TestRun(t *testing.T) {
closer := u.MustCaptureStdoutAndStderr()

// simulate CLI call
err := run([]string{"prefix", "-format", "{{.LineNumber3}} ", f.Name()})
err := run([]string{"prefix", "-format", "{{DEFAULT}} ", f.Name()})
if err != nil {
t.Fatalf("err should be nil: %v", err)
}
Expand Down
72 changes: 61 additions & 11 deletions prefix.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package prefix
import (
"bytes"
"fmt"
"strings"
"text/template"
"time"

Expand All @@ -12,7 +13,34 @@ import (

/// Public API

const DefaultFormat = `{{.LineNumber3}} upt={{.Uptime}} dur={{.Duration}} `
// AvailablePatterns is the list of available patterns to be used by the user.
//
// This variable is only used to generate usage.
var AvailablePatterns = map[string]string{
`{{.LineNumber}}`: `display line number`,
`{{.LineNumber3}}`: `alias for {{printf "%-3d" .LineNumber}}`,
`{{.LineNumber4}}`: `alias for {{printf "%-4d" .LineNumber}}`,
`{{.LineNumber5}}`: `alias for {{printf "%-5d" .LineNumber}}`,
`{{.Format}}`: `the value you set with -format`,
`{{.Uptime}}`: `time since the the prefixer was initialized`,
`{{.Duration}}`: `time since previous line was started`,
`{{.Uptime | short_duration}}`: `{{.Uptime}} displayed in a pretty & short format (len<=7)`,
`{{.Duration | short_duration}}`: `{{.Duration}} displayed in a pretty & short format (len<=7)`,
`{{now}}`: `current date (format: 2006-01-02 15:04:05.999999999 -0700 MST)`,
`{{now | unixEpoch}}`: `current timestamp`,
`{{uuidv4}}`: `UUID of the v4 (randomly generated) type`,
`{{env "USER"}}`: `replace with content of the $USER env var`,
`{{.ShortDuration}}`: `alias for {{.Duration | short_duration}}`,
`{{.ShortUptime}}`: `alias for {{.Uptime | short_duration}}`,
}

// AvailablePresets is the list of available presets.
//
// Those presets are automatically replaced during the initialization of the prefixer.
var AvailablePresets = map[string]string{
"{{DEFAULT}}": `{{.LineNumber3}} up={{.ShortUptime}} d={{.ShortDuration}} |`,
"{{SLOW_LINES}}": `{{if (gt .Duration 1000000000)}}SLOW{{else}} {{end}} {{.Duration | short_duration}} `,
}

type LinePrefixer interface {
PrefixLine(string) string
Expand All @@ -29,14 +57,36 @@ type linePrefixer struct {
lastTime time.Time // used by {{.Duration}}
}

func (p *linePrefixer) String() string {
return fmt.Sprintf("LinePrefixer{%q}", p.Format)
}

// New returns an initialized LinePrefixer.
func New(format string) LinePrefixer {
if format == "" {
format = AvailablePresets["{{DEFAULT}} "]
}

// apply presets
for {
before := format
for k, v := range AvailablePresets {
format = strings.ReplaceAll(format, k, v)
}
if before == format {
break
}
}

// build funcmap
funcMap := template.FuncMap{}
for k, v := range sprig.FuncMap() {
funcMap[k] = v
}
funcMap["short_duration"] = shortDuration

return &linePrefixer{
Format: DefaultFormat,
Format: format,
LineNumber: 0,

t: template.Must(template.New("").Funcs(funcMap).Parse(format)),
Expand All @@ -55,12 +105,12 @@ func (p *linePrefixer) PrefixLine(line string) string {

/// Template helpers

func (p *linePrefixer) LineNumber3() string { return fmt.Sprintf("%-3d", p.LineNumber) }
func (p *linePrefixer) LineNumber4() string { return fmt.Sprintf("%-4d", p.LineNumber) }
func (p *linePrefixer) LineNumber5() string { return fmt.Sprintf("%-5d", p.LineNumber) }
func (p *linePrefixer) Uptime() string {
return fmt.Sprintf("%-7s", u.ShortDuration(time.Since(p.startedAt)))
}
func (p *linePrefixer) Duration() string {
return fmt.Sprintf("%-7s", u.ShortDuration(time.Since(p.lastTime)))
}
func (p *linePrefixer) LineNumber3() string { return fmt.Sprintf("%-3d", p.LineNumber) }
func (p *linePrefixer) LineNumber4() string { return fmt.Sprintf("%-4d", p.LineNumber) }
func (p *linePrefixer) LineNumber5() string { return fmt.Sprintf("%-5d", p.LineNumber) }
func (p *linePrefixer) Uptime() time.Duration { return time.Since(p.startedAt) }
func (p *linePrefixer) Duration() time.Duration { return time.Since(p.lastTime) }
func (p *linePrefixer) ShortDuration() string { return shortDuration(p.Duration()) }
func (p *linePrefixer) ShortUptime() string { return shortDuration(p.Uptime()) }

func shortDuration(d time.Duration) string { return fmt.Sprintf("%-7s", u.ShortDuration(d)) }
19 changes: 19 additions & 0 deletions prefix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package prefix_test

import (
"testing"

"moul.io/prefix"
)

// you can find integration tests in the cmd/prefix/main_test.go file

func TestAvailablePatterns(t *testing.T) {
// this test is dummy, it just checks that everything runs without panicking
for pattern := range prefix.AvailablePatterns {
prefixer := prefix.New(pattern)
prefixer.PrefixLine("first")
prefixer.PrefixLine("second")
prefixer.PrefixLine("third")
}
}

0 comments on commit 2efb668

Please sign in to comment.