Permalink
Browse files

fix #50: add new options for urls: --decode, --no-color, --no-error, …

…--no-redirect
  • Loading branch information...
kamilsk committed May 7, 2018
1 parent 711c07c commit 15b617629bbc8dc7f19a65297549f462d19d3cb9
Showing with 97 additions and 33 deletions.
  1. +11 βˆ’1 cmd/urls.go
  2. +86 βˆ’32 http/availability/printer.go
@@ -37,12 +37,22 @@ var urlsCmd = &cobra.Command{
Fill()
stop()
return availability.
NewPrinter(availability.OutputForPrinting(cmd.OutOrStdout())).
NewPrinter(
availability.ColorizeOutput(!asBool(cmd.Flag("no-color").Value)),
availability.DecodeOutput(asBool(cmd.Flag("decode").Value)),
availability.HideError(asBool(cmd.Flag("no-error").Value)),
availability.HideRedirect(asBool(cmd.Flag("no-redirect").Value)),
availability.OutputForPrinting(cmd.OutOrStdout()),
).
For(report).
Print()
},
}

func init() {
urlsCmd.Flags().BoolP("decode", "d", false, "decode URLs")
urlsCmd.Flags().Bool("no-color", false, "disable colorized output")
urlsCmd.Flags().Bool("no-error", false, "do not show URL's error")
urlsCmd.Flags().Bool("no-redirect", false, "do not show URL's redirect")
urlsCmd.Flags().BoolP("verbose", "v", false, "turn on verbose mode")
}
@@ -5,6 +5,7 @@ import (
"fmt"
"io"
"io/ioutil"
"net/url"
"os"
"sort"
"text/template"
@@ -20,26 +21,71 @@ const (
danger = "danger"
)

var colors = map[string]*color.Color{
shaded: color.New(color.FgHiBlack),
success: color.New(color.FgWhite),
warning: color.New(color.FgYellow),
danger: color.New(color.FgRed, color.Bold),
}

var entry = template.Must(template.New("entry").Parse(
"[{{ .StatusCode }}] {{ .Location }}{{ with .Error }} -> ({{ . }}){{ end }}{{ with .Redirect }} -> {{ . }}{{ end }}",
))
var base = template.Must(template.New("entry").Parse(`
{{- define "error" }}{{ with .Error }} -> ({{ . }}){{ end }}{{ end -}}
{{- define "redirect" }}{{ with .Redirect }} -> {{ . }}{{ end }}{{ end -}}
[{{ .StatusCode }}] {{ .Location }}{{ template "error" . }}{{ template "redirect" . -}}
`))

// NewPrinter returns configured printer instance.
func NewPrinter(options ...func(*Printer)) *Printer {
p := &Printer{}
p := &Printer{
tpl: template.Must(base.Clone()),
decoder: func(origin string) string { return origin },
}
for _, f := range options {
f(p)
}
return p
}

// ColorizeOutput sets the ink for the printer.
func ColorizeOutput(enabled bool) func(*Printer) {
return func(p *Printer) {
if enabled {
p.ink = map[string]*color.Color{
shaded: color.New(color.FgHiBlack),
success: color.New(color.FgWhite),
warning: color.New(color.FgYellow),
danger: color.New(color.FgRed, color.Bold),
}
}
}
}

// DecodeOutput sets `net/url.PathUnescape` as a decoder.
func DecodeOutput(enabled bool) func(*Printer) {
return func(p *Printer) {
if enabled {
p.decoder = func(origin string) string {
decoded, err := url.PathUnescape(origin)
if err != nil {
return origin
}
return decoded
}
}
}
}

// HideError prevents URL's error output.
func HideError(disabled bool) func(*Printer) {
return func(p *Printer) {
if disabled {
p.tpl.New("error").Parse("{{ with .Error }}{{/* ignore */}}{{ end }}")
}
}
}

// HideRedirect prevents URL's redirect output.
func HideRedirect(disabled bool) func(*Printer) {
return func(p *Printer) {
if disabled {
p.tpl.New("redirect").Parse("{{ with .Redirect }}{{/* ignore */}}{{ end }}")
}
}
}

// OutputForPrinting sets up printer output.
func OutputForPrinting(output io.Writer) func(*Printer) {
return func(p *Printer) {
@@ -54,8 +100,11 @@ type Reporter interface {

// Printer represents a printer.
type Printer struct {
output io.Writer
report Reporter
tpl *template.Template
output io.Writer
ink map[string]*color.Color
decoder func(string) string
report Reporter
}

// For prepares printer for passed report provider.
@@ -74,9 +123,9 @@ func (p *Printer) Print() error {
}
for site := range p.report.Sites() {
if site.Error != nil {
critical().Fprintf(w, "report %q has error %q\n", site.Name, site.Error)
p.critical().Fprintf(w, "report %q has error %q\n", site.Name, site.Error)
if stack := errors.StackTrace(site.Error); stack != nil {
critical().Fprintf(ioutil.Discard, "stack trace: %#+v\n", stack) // for future
p.critical().Fprintf(ioutil.Discard, "stack trace: %#+v\n", stack) // for future
}
continue
}
@@ -85,63 +134,68 @@ func (p *Printer) Print() error {
last := len(page.Links) - 1
{
buf.Reset()
entry.Execute(buf, page)
p.tpl.Execute(buf, page)
}
colorize(page.Link).Fprintf(w, "%s\n", buf.String())
p.typewriter(page.Link).Fprintf(w, "%s\n", p.decoder(buf.String()))
sort.Sort(linksByStatusCode(page.Links))
for i, link := range page.Links {
{
buf.Reset()
entry.Execute(buf, link)
p.tpl.Execute(buf, link)
}
if i == last {
colorize(&link).Fprintf(w, " └───%s\n", buf.String())
p.typewriter(&link).Fprintf(w, " └───%s\n", p.decoder(buf.String()))
continue
}
colorize(&link).Fprintf(w, " β”œβ”€β”€β”€%s\n", buf.String())
p.typewriter(&link).Fprintf(w, " β”œβ”€β”€β”€%s\n", p.decoder(buf.String()))
}
}
if len(site.Problems) > 0 {
critical().Fprintf(w, "found problems on the site %q\n", site.Name)
p.critical().Fprintf(w, "found problems on the site %q\n", site.Name)
for i, problem := range site.Problems {
critical().Fprintf(w, "- [%d] %s `%+v`\n", i, problem.Message, problem.Context)
p.critical().Fprintf(w, "- [%d] %s `%+v`\n", i, problem.Message, problem.Context)
}
}
}
return nil
}

func (p *Printer) critical() typewriter {
return p.typewriter(nil)
}

func (p *Printer) outOrStdout() io.Writer {
if p.output != nil {
return p.output
}
return os.Stdout
}

func colorize(link *Link) typewriter {
var tw typewriter
func (p *Printer) typewriter(link *Link) typewriter {
var (
tw typewriter
ok bool
)
switch {
case link == nil:
tw, _ = colors[danger]
tw, ok = p.ink[danger]
case link.StatusCode >= 200 && link.StatusCode < 300:
if link.Internal {
tw, _ = colors[shaded]
tw, ok = p.ink[shaded]
break
}
tw, _ = colors[success]
tw, ok = p.ink[success]
case link.StatusCode >= 300 && link.StatusCode < 400:
tw, _ = colors[warning]
tw, ok = p.ink[warning]
case link.StatusCode >= 400:
tw, _ = colors[danger]
tw, ok = p.ink[danger]
}
if tw == nil {
if !ok || tw == nil {
tw = typewriterFunc(fmt.Fprintf)
}
return tw
}

func critical() typewriter { return colorize(nil) }

type typewriter interface {
Fprintf(io.Writer, string, ...interface{}) (int, error)
}

0 comments on commit 15b6176

Please sign in to comment.