diff --git a/browser.go b/browser.go index d7969d7..89a07bf 100644 --- a/browser.go +++ b/browser.go @@ -18,18 +18,50 @@ var Stdout io.Writer = os.Stdout // Stderr is the io.Writer to which executed commands write standard error. var Stderr io.Writer = os.Stderr +// Opener allows customizing browser opening behavior. +type Opener struct { + // Stdout is the io.Writer to which executed commands write standard output. + // If nil, os.Stdout is used. + Stdout io.Writer + + // Stderr is the io.Writer to which executed commands write standard error. + // If nil, os.Stderr is used. + Stderr io.Writer +} + +func (o *Opener) stdout() io.Writer { + if o.Stdout != nil { + return o.Stdout + } + return Stdout +} + +func (o *Opener) stderr() io.Writer { + if o.Stderr != nil { + return o.Stderr + } + return Stderr +} + +func (o *Opener) runCmd(prog string, args ...string) error { + cmd := exec.Command(prog, args...) + cmd.Stdout = o.stdout() + cmd.Stderr = o.stderr() + return cmd.Run() +} + // OpenFile opens new browser window for the file path. -func OpenFile(path string) error { +func (o *Opener) OpenFile(path string) error { path, err := filepath.Abs(path) if err != nil { return err } - return OpenURL("file://" + path) + return o.OpenURL("file://" + path) } // OpenReader consumes the contents of r and presents the // results in a new browser window. -func OpenReader(r io.Reader) error { +func (o *Opener) OpenReader(r io.Reader) error { f, err := ioutil.TempFile("", "browser.*.html") if err != nil { return fmt.Errorf("browser: could not create temporary file: %v", err) @@ -41,17 +73,35 @@ func OpenReader(r io.Reader) error { if err := f.Close(); err != nil { return fmt.Errorf("browser: caching temporary file failed: %v", err) } - return OpenFile(f.Name()) + return o.OpenFile(f.Name()) } // OpenURL opens a new browser window pointing to url. -func OpenURL(url string) error { - return openBrowser(url) +func (o *Opener) OpenURL(url string) error { + return o.openBrowser(url) } -func runCmd(prog string, args ...string) error { - cmd := exec.Command(prog, args...) - cmd.Stdout = Stdout - cmd.Stderr = Stderr - return cmd.Run() +// defaultOpener returns an Opener configured with the package-level Stdout/Stderr. +// This is done as a function to always grab the latest values of Stdout/Stderr. +func defaultOpener() *Opener { + return &Opener{ + Stdout: Stdout, + Stderr: Stderr, + } +} + +// OpenFile opens new browser window for the file path. +func OpenFile(path string) error { + return defaultOpener().OpenFile(path) +} + +// OpenReader consumes the contents of r and presents the +// results in a new browser window. +func OpenReader(r io.Reader) error { + return defaultOpener().OpenReader(r) +} + +// OpenURL opens a new browser window pointing to url. +func OpenURL(url string) error { + return defaultOpener().OpenURL(url) } diff --git a/browser_darwin.go b/browser_darwin.go index 8507cf7..2ea95f2 100644 --- a/browser_darwin.go +++ b/browser_darwin.go @@ -1,5 +1,5 @@ package browser -func openBrowser(url string) error { - return runCmd("open", url) +func (o *Opener) openBrowser(url string) error { + return o.runCmd("open", url) } diff --git a/browser_freebsd.go b/browser_freebsd.go index 4fc7ff0..48b7f62 100644 --- a/browser_freebsd.go +++ b/browser_freebsd.go @@ -5,8 +5,8 @@ import ( "os/exec" ) -func openBrowser(url string) error { - err := runCmd("xdg-open", url) +func (o *Opener) openBrowser(url string) error { + err := o.runCmd("xdg-open", url) if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound { return errors.New("xdg-open: command not found - install xdg-utils from ports(8)") } diff --git a/browser_linux.go b/browser_linux.go index d26cddd..42f3b6f 100644 --- a/browser_linux.go +++ b/browser_linux.go @@ -5,7 +5,7 @@ import ( "strings" ) -func openBrowser(url string) error { +func (o *Opener) openBrowser(url string) error { providers := []string{"xdg-open", "x-www-browser", "www-browser"} // There are multiple possible providers to open a browser on linux @@ -13,7 +13,7 @@ func openBrowser(url string) error { // Look for one that exists and run it for _, provider := range providers { if _, err := exec.LookPath(provider); err == nil { - return runCmd(provider, url) + return o.runCmd(provider, url) } } diff --git a/browser_netbsd.go b/browser_netbsd.go index 65a5e5a..db5c9a9 100644 --- a/browser_netbsd.go +++ b/browser_netbsd.go @@ -5,8 +5,8 @@ import ( "os/exec" ) -func openBrowser(url string) error { - err := runCmd("xdg-open", url) +func (o *Opener) openBrowser(url string) error { + err := o.runCmd("xdg-open", url) if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound { return errors.New("xdg-open: command not found - install xdg-utils from pkgsrc(7)") } diff --git a/browser_openbsd.go b/browser_openbsd.go index 4fc7ff0..48b7f62 100644 --- a/browser_openbsd.go +++ b/browser_openbsd.go @@ -5,8 +5,8 @@ import ( "os/exec" ) -func openBrowser(url string) error { - err := runCmd("xdg-open", url) +func (o *Opener) openBrowser(url string) error { + err := o.runCmd("xdg-open", url) if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound { return errors.New("xdg-open: command not found - install xdg-utils from ports(8)") } diff --git a/browser_test.go b/browser_test.go new file mode 100644 index 0000000..6ffbadf --- /dev/null +++ b/browser_test.go @@ -0,0 +1,19 @@ +package browser + +import ( + "bytes" + "path/filepath" + "testing" +) + +func TestErrorRedirect(t *testing.T) { + stderr := new(bytes.Buffer) + o := &Opener{ + Stderr: stderr, + } + + _ = o.OpenFile(filepath.Join(t.TempDir(), "nonexistentfile.html")) + if stderr.Len() == 0 { + t.Errorf("expected stderr to contain error message, got empty") + } +} diff --git a/browser_unsupported.go b/browser_unsupported.go index 7c5c17d..29d9a9f 100644 --- a/browser_unsupported.go +++ b/browser_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux && !windows && !darwin && !openbsd && !freebsd && !netbsd // +build !linux,!windows,!darwin,!openbsd,!freebsd,!netbsd package browser @@ -7,6 +8,6 @@ import ( "runtime" ) -func openBrowser(url string) error { +func (o *Opener) openBrowser(url string) error { return fmt.Errorf("openBrowser: unsupported operating system: %v", runtime.GOOS) } diff --git a/browser_windows.go b/browser_windows.go index 63e1929..eac213f 100644 --- a/browser_windows.go +++ b/browser_windows.go @@ -2,6 +2,6 @@ package browser import "golang.org/x/sys/windows" -func openBrowser(url string) error { +func (o *Opener) openBrowser(url string) error { return windows.ShellExecute(0, nil, windows.StringToUTF16Ptr(url), nil, nil, windows.SW_SHOWNORMAL) }