Skip to content

Commit

Permalink
Add MustParsePath (#42)
Browse files Browse the repository at this point in the history
* Add MustParsePath

* Use a separate error for clarity

* Address review feedback
  • Loading branch information
jefferai committed Aug 5, 2022
1 parent bf6d78e commit 64b82fc
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 5 deletions.
27 changes: 23 additions & 4 deletions parseutil/parsepath.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import (
"strings"
)

var ErrNotAUrl = errors.New("not a url")
var (
ErrNotAUrl = errors.New("not a url")
ErrNotParsed = errors.New("not a parsed value")
)

// ParsePath parses a URL with schemes file://, env://, or any other. Depending
// on the scheme it will return specific types of data:
Expand All @@ -18,14 +21,27 @@ var ErrNotAUrl = errors.New("not a url")
//
// * env:// will return a string with the env var's contents
//
// * Anything else will return the string as it was
// * Anything else will return the string as it was. Functionally this means
// anything for which Go's `url.Parse` function does not throw an error. If you
// want to ensure that this function errors if a known scheme is not found, use
// MustParsePath.
//
// On error, we return the original string along with the error. The caller can
// switch on errors.Is(err, ErrNotAUrl) to understand whether it was the parsing
// step that errored or something else (such as a file not found). This is
// useful to attempt to read a non-URL string from some resource, but where the
// original input may simply be a valid string of that type.
func ParsePath(path string) (string, error) {
return parsePath(path, false)
}

// MustParsePath behaves like ParsePath but will return ErrNotAUrl if the value
// is not a URL with a scheme that can be parsed by this function.
func MustParsePath(path string) (string, error) {
return parsePath(path, true)
}

func parsePath(path string, mustParse bool) (string, error) {
path = strings.TrimSpace(path)
parsed, err := url.Parse(path)
if err != nil {
Expand All @@ -40,7 +56,10 @@ func ParsePath(path string) (string, error) {
return strings.TrimSpace(string(contents)), nil
case "env":
return strings.TrimSpace(os.Getenv(strings.TrimPrefix(path, "env://"))), nil
default:
if mustParse {
return "", ErrNotParsed
}
return path, nil
}

return path, nil
}
36 changes: 35 additions & 1 deletion parseutil/parsepath_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,44 @@ func TestParsePath(t *testing.T) {
inPath string
outStr string
notAUrl bool
must bool
notParsed bool
expErrorContains string
}{
{
name: "file",
inPath: fmt.Sprintf("file://%s", file.Name()),
outStr: "foo",
},
{
name: "file-mustparse",
inPath: fmt.Sprintf("file://%s", file.Name()),
outStr: "foo",
must: true,
},
{
name: "env",
inPath: "env://PATHTEST",
outStr: "bar",
},
{
name: "env-mustparse",
inPath: "env://PATHTEST",
outStr: "bar",
must: true,
},
{
name: "plain",
inPath: "zipzap",
outStr: "zipzap",
},
{
name: "plain-mustparse",
inPath: "zipzap",
outStr: "zipzap",
must: true,
notParsed: true,
},
{
name: "no file",
inPath: "file:///dev/nullface",
Expand All @@ -60,7 +81,14 @@ func TestParsePath(t *testing.T) {
for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
out, err := ParsePath(tt.inPath)
var out string
var err error
switch tt.must {
case false:
out, err = ParsePath(tt.inPath)
default:
out, err = MustParsePath(tt.inPath)
}
if tt.expErrorContains != "" {
require.Error(err)
assert.Contains(err.Error(), tt.expErrorContains)
Expand All @@ -72,6 +100,12 @@ func TestParsePath(t *testing.T) {
assert.Equal(tt.inPath, out)
return
}
if tt.notParsed {
require.Error(err)
assert.True(errors.Is(err, ErrNotParsed))
assert.Empty(out)
return
}
require.NoError(err)
assert.Equal(tt.outStr, out)
})
Expand Down

0 comments on commit 64b82fc

Please sign in to comment.