Skip to content

Commit

Permalink
feat: grepable output (#72)
Browse files Browse the repository at this point in the history
Co-authored-by: DnFreddie <[email protected]>
  • Loading branch information
DnFreddie and DnFreddie authored Nov 19, 2024
1 parent 611f84c commit c344987
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 34 deletions.
3 changes: 3 additions & 0 deletions internal/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ type Options struct {
// Output flags.
// Output in JSON format.
JSONOutput bool
// Output in Grepable format.
GrepFormat bool
// Disable color output.
NoColor bool
// Enable debugging.
Expand All @@ -47,6 +49,7 @@ func (opts *Options) Parse() {
)
flag.StringVar(&opts.DNSResolver, "dr", "", "DNS resolution server")
flag.BoolVar(&opts.JSONOutput, "j", false, "Output in JSON format")
flag.BoolVar(&opts.GrepFormat, "g", false, "Output in Grepable format")
flag.BoolVar(&opts.NoColor, "nc", false, "Disable color output")
flag.BoolVar(&opts.Debug, "dbg", false, "Verbose output")
flag.BoolVar(&opts.Help, "h", false, "Show app documentation")
Expand Down
80 changes: 79 additions & 1 deletion internal/report.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
package internal

import (
"encoding/json"
"fmt"
"time"

"github.com/fatih/color"
)

// Report is the result of a connection attempt.
//
// Only one of the properties 'Response' or 'Error' is set.

type Format int

const (
JSONFormat Format = iota
HumanFormat
GrepFormat
)

type Report struct {
// Protocol used to connect to.
ProtocolID string `json:"protocol"`
Expand All @@ -17,5 +30,70 @@ type Report struct {
// Network error.
Error error `json:"error,omitempty"`
// Extra information. Depends on the protocol.
Extra string `json:"extra,omitempty"`
Extra string `json:"extra,omitempty"`
Format Format
}

func (r *Report) NewLine(f Format) (string, error) {
switch f {
case HumanFormat:
return r.newLineHuman(), nil
case JSONFormat:
line, err := r.newLineJSON()
if err != nil {
return "", fmt.Errorf("error generating JSON report: %w", err)
}
return line, nil
case GrepFormat:
return r.newLineGrep(), nil
default:
return "", fmt.Errorf("unsupported format: %v", f)
}
}

func (r *Report) newLineJSON() (string, error) {

reportJSON, err := json.Marshal(r)
if err != nil {
return "", fmt.Errorf("marshaling report: %w", err)

}
return string(reportJSON), nil
}

var (
green = color.New(color.FgGreen).SprintFunc()
red = color.New(color.FgRed).SprintFunc()
bold = color.New(color.Bold).SprintFunc()
faint = color.New(color.Faint).SprintFunc()
)

func (r *Report) newLineHuman() string {
line := fmt.Sprintf("%-15s %-14s %s", bold(r.ProtocolID), r.Time, r.RHost)
suffix := r.Extra
prefix := green("✔")
if r.Error != nil {
prefix = red("✘")
suffix = r.Error.Error()
}
suffix = fmt.Sprintf("(%s)", suffix)

return fmt.Sprintf("%s %s %s", prefix, line, faint(suffix))
}

// Output: HTTP/1.1 2024-11-18T15:00:00Z 192.168.1.1 success Request
// processed successfully
func (r *Report) newLineGrep() string {
status := "success"
if r.Error != nil {
status = "failure"
}
line := fmt.Sprintf("%s\t%s\t%s\t%s\t%s",
r.ProtocolID,
r.Time,
r.RHost,
status,
r.Extra,
)
return line
}
50 changes: 17 additions & 33 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package main

import (
"context"
"encoding/json"
"flag"
"fmt"
"log/slog"
Expand Down Expand Up @@ -33,8 +32,6 @@ const (
`
)

// TODO(#39): STDIN piped input.

func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
Expand Down Expand Up @@ -106,17 +103,24 @@ func main() {
logger.Debug("Listening for reports ...")
for report := range probe.ReportCh {
logger.Debug("New report", "report", *report)
var line string
if opts.JSONOutput {
reportJSON, err := json.Marshal(report)
if err != nil {
fatal(fmt.Errorf("marshaling report: %w", err))
}
line = string(reportJSON)
} else {
line = reportToLine(report)

var format internal.Format
switch {
case opts.JSONOutput:
format = internal.JSONFormat
case opts.GrepFormat:
format = internal.GrepFormat
default:
format = internal.HumanFormat
}
fmt.Println(line)
repLine, err := report.NewLine(format)

if err != nil {
fatal(err)
}

fmt.Println(repLine)

if report.Error == nil {
if opts.Stop {
logger.Debug("Stopping after first successful request")
Expand All @@ -137,23 +141,3 @@ func fatal(err error) {
fmt.Fprintf(os.Stderr, "%s: %s\n", appName, err)
os.Exit(1)
}

// Returns a human-readable representation of the report.
func reportToLine(r *internal.Report) string {
line := fmt.Sprintf("%-15s %-14s %s", bold(r.ProtocolID), r.Time, r.RHost)
suffix := r.Extra
prefix := green("✔")
if r.Error != nil {
prefix = red("✘")
suffix = r.Error.Error()
}
suffix = fmt.Sprintf("(%s)", suffix)
return fmt.Sprintf("%s %s %s", prefix, line, faint(suffix))
}

var (
green = color.New(color.FgGreen).SprintFunc()
red = color.New(color.FgRed).SprintFunc()
bold = color.New(color.Bold).SprintFunc()
faint = color.New(color.Faint).SprintFunc()
)

0 comments on commit c344987

Please sign in to comment.