17
17
ErrNotParsed = errors .New ("not a parsed value" )
18
18
)
19
19
20
+ type options struct {
21
+ errorOnMissingEnv bool
22
+ noTrimSpaces bool
23
+ }
24
+
25
+ type option func () optionFunc
26
+
27
+ type optionFunc func (* options )
28
+
20
29
// ParsePath parses a URL with schemes file://, env://, or any other. Depending
21
30
// on the scheme it will return specific types of data:
22
31
//
@@ -34,35 +43,82 @@ var (
34
43
// step that errored or something else (such as a file not found). This is
35
44
// useful to attempt to read a non-URL string from some resource, but where the
36
45
// original input may simply be a valid string of that type.
37
- func ParsePath (path string ) (string , error ) {
38
- return parsePath (path , false )
46
+ func ParsePath (path string , options ... option ) (string , error ) {
47
+ return parsePath (path , false , options )
39
48
}
40
49
41
50
// MustParsePath behaves like ParsePath but will return ErrNotAUrl if the value
42
51
// is not a URL with a scheme that can be parsed by this function.
43
- func MustParsePath (path string ) (string , error ) {
44
- return parsePath (path , true )
52
+ func MustParsePath (path string , options ... option ) (string , error ) {
53
+ return parsePath (path , true , options )
45
54
}
46
55
47
- func parsePath (path string , mustParse bool ) (string , error ) {
48
- path = strings .TrimSpace (path )
49
- parsed , err := url .Parse (path )
56
+ func parsePath (path string , mustParse bool , passedOptions []option ) (string , error ) {
57
+ var opts options
58
+ for _ , o := range passedOptions {
59
+ of := o ()
60
+ of (& opts )
61
+ }
62
+
63
+ trimmedPath := strings .TrimSpace (path )
64
+ parsed , err := url .Parse (trimmedPath )
50
65
if err != nil {
51
- return path , fmt .Errorf ("error parsing url (%q): %w" , err .Error (), ErrNotAUrl )
66
+ err = fmt .Errorf ("error parsing url (%q): %w" , err .Error (), ErrNotAUrl )
67
+ if opts .noTrimSpaces {
68
+ return path , err
69
+ }
70
+ return trimmedPath , err
52
71
}
53
72
switch parsed .Scheme {
54
73
case "file" :
55
- contents , err := ioutil .ReadFile (strings .TrimPrefix (path , "file://" ))
74
+ contents , err := ioutil .ReadFile (strings .TrimPrefix (trimmedPath , "file://" ))
56
75
if err != nil {
57
- return path , fmt .Errorf ("error reading file at %s: %w" , path , err )
76
+ return trimmedPath , fmt .Errorf ("error reading file at %s: %w" , trimmedPath , err )
77
+ }
78
+ if opts .noTrimSpaces {
79
+ return string (contents ), nil
58
80
}
59
81
return strings .TrimSpace (string (contents )), nil
60
82
case "env" :
61
- return strings .TrimSpace (os .Getenv (strings .TrimPrefix (path , "env://" ))), nil
83
+ envKey := strings .TrimPrefix (trimmedPath , "env://" )
84
+ envVal , ok := os .LookupEnv (envKey )
85
+ if opts .errorOnMissingEnv && ! ok {
86
+ return "" , fmt .Errorf ("environment variable %s unset" , envKey )
87
+ }
88
+ if opts .noTrimSpaces {
89
+ return envVal , nil
90
+ }
91
+ return strings .TrimSpace (envVal ), nil
92
+ case "string" :
93
+ // Meant if there is a need to provide a string literal that is prefixed by one of these URL schemes but want to "escape" it,
94
+ // e.g. "string://env://foo", in order to get the value "env://foo"
95
+ val := strings .TrimPrefix (trimmedPath , "string://" )
96
+ if opts .noTrimSpaces {
97
+ return val , nil
98
+ }
99
+ return strings .TrimSpace (val ), nil
62
100
default :
63
101
if mustParse {
64
102
return "" , ErrNotParsed
65
103
}
66
104
return path , nil
67
105
}
68
106
}
107
+
108
+ // When true, values returned from ParsePath won't have leading/trailing spaces trimmed.
109
+ func WithNoTrimSpaces (noTrim bool ) option {
110
+ return func () optionFunc {
111
+ return optionFunc (func (o * options ) {
112
+ o .noTrimSpaces = noTrim
113
+ })
114
+ }
115
+ }
116
+
117
+ // When true, if an environment variable is unset, an error will be returned rather than the empty string.
118
+ func WithErrorOnMissingEnv (errorOnMissingEnv bool ) option {
119
+ return func () optionFunc {
120
+ return optionFunc (func (o * options ) {
121
+ o .errorOnMissingEnv = errorOnMissingEnv
122
+ })
123
+ }
124
+ }
0 commit comments