Skip to content

Commit 5ced646

Browse files
authored
Merge pull request #58 from moul/dev/moul/expandpath
feat: add u.ExpandPath
2 parents cb60818 + 359601b commit 5ced646

5 files changed

Lines changed: 194 additions & 102 deletions

File tree

README.md

Lines changed: 59 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,12 @@ func ExecStandaloneOutputs(cmd *exec.Cmd) ([]byte, []byte, error)
6969
ExecStandaloneOutputs runs the command and returns its standard output and
7070
standard error.
7171
72-
func ExpandUser(path string) (string, error)
72+
func ExpandPath(path string) (string, error)
73+
ExpandPath performs various expansions on a given path.
74+
75+
- Replaces ~/ with $HOME/. - Returns absolute path. - Expands env vars.
76+
TODO: - Follow symlinks.
77+
7378
func FanIn(chans ...<-chan interface{}) <-chan interface{}
7479
FanIn merges multiple input chans events into one.
7580
@@ -100,8 +105,8 @@ func MustCaptureStdoutAndStderr() func() string
100105
MustCaptureStdoutAndStderr wraps CaptureStdoutAndStderr and panics if
101106
initialization fails.
102107
103-
func MustExpandUser(path string) string
104-
MustExpandUser wraps ExpandUser and panics if initialization fails.
108+
func MustExpandPath(path string) string
109+
MustExpandPath wraps ExpandPath and panics if initialization fails.
105110
106111
func MustTempFileName(dir, pattern string) string
107112
MustTempFileName wraps TempFileName and panics if initialization fails.
@@ -212,57 +217,57 @@ $ go get moul.io/u
212217
```txt
213218
benchmark iter time/iter
214219
--------- ---- ---------
215-
BenchmarkUnzip-12 3795 281158.00 ns/op
216-
BenchmarkUnzipBytes-12 3854 270661.00 ns/op
217-
BenchmarkB64Encode/1-12 14475339 78.53 ns/op
218-
BenchmarkB64Encode/1-parallel-12 95066647 11.42 ns/op
219-
BenchmarkB64Encode/1000-12 450759 5188.00 ns/op
220-
BenchmarkB64Encode/1000-parallel-12 1379708 870.70 ns/op
221-
BenchmarkB64Encode/1000000-12 279 4530342.00 ns/op
222-
BenchmarkB64Encode/1000000-parallel-12 2029 518956.00 ns/op
223-
BenchmarkB64Decode/1000-12 403045 4057.00 ns/op
224-
BenchmarkB64Decode/1000-parallel-12 1795483 690.40 ns/op
225-
BenchmarkB64Decode/10000-12 30897 37342.00 ns/op
226-
BenchmarkB64Decode/10000-parallel-12 193408 7130.00 ns/op
227-
BenchmarkB64Decode/100000-12 5318 344592.00 ns/op
228-
BenchmarkB64Decode/100000-parallel-12 24860 47960.00 ns/op
229-
BenchmarkIsBinary/small-valid-12 134240438 8.63 ns/op
230-
BenchmarkIsBinary/small-valid-parallel-12 819188630 1.35 ns/op
231-
BenchmarkIsBinary/long-valid-12 5294448 225.10 ns/op
232-
BenchmarkIsBinary/long-valid-parallel-12 27616174 36.47 ns/op
233-
BenchmarkIsBinary/small-invalid-12 141702962 8.35 ns/op
234-
BenchmarkIsBinary/small-invalid-parallel-12 746452850 1.40 ns/op
235-
BenchmarkCommandExists/go-12 140930 8908.00 ns/op
236-
BenchmarkCommandExists/go-parallel-12 947446 1299.00 ns/op
237-
BenchmarkCommandExists/asddsa-12 23431 51981.00 ns/op
238-
BenchmarkCommandExists/asddsa-parallel-12 155037 6934.00 ns/op
239-
BenchmarkSafeExec-12 679 2368421.00 ns/op
240-
BenchmarkCombineFuncs-12 6238905 202.40 ns/op
241-
BenchmarkFuture-12 1468946 802.00 ns/op
242-
BenchmarkRandomLetters/1000-12 324505 4102.00 ns/op
243-
BenchmarkRandomLetters/1000-parallel-12 33856 35397.00 ns/op
244-
BenchmarkRandomLetters/10000-12 29956 39849.00 ns/op
245-
BenchmarkRandomLetters/10000-parallel-12 3334 357897.00 ns/op
246-
BenchmarkRandomLetters/100000-12 2995 394264.00 ns/op
247-
BenchmarkRandomLetters/100000-parallel-12 349 3509818.00 ns/op
248-
BenchmarkUniqueStrings/slice1-12 849084 1424.00 ns/op
249-
BenchmarkUniqueStrings/slice1-parallel-12 5389947 220.00 ns/op
250-
BenchmarkUniqueStrings/slice2-12 9249 251324.00 ns/op
251-
BenchmarkUniqueStrings/slice2-parallel-12 27469 43579.00 ns/op
252-
BenchmarkUniqueInts/slice1-12 1474201 789.60 ns/op
253-
BenchmarkUniqueInts/slice1-parallel-12 9390207 126.50 ns/op
254-
BenchmarkUniqueInts/slice2-12 10000 154489.00 ns/op
255-
BenchmarkUniqueInts/slice2-parallel-12 46874 25296.00 ns/op
256-
BenchmarkUniqueInterfaces/slice1-12 932260 1724.00 ns/op
257-
BenchmarkUniqueInterfaces/slice1-parallel-12 4381582 255.00 ns/op
258-
BenchmarkUniqueInterfaces/slice2-12 2624 563016.00 ns/op
259-
BenchmarkUniqueInterfaces/slice2-parallel-12 10660 111471.00 ns/op
260-
BenchmarkShortDuration/Simple-12 13368848 93.61 ns/op
261-
BenchmarkShortDuration/Simple-parallel-12 84225661 13.74 ns/op
262-
BenchmarkShortDuration/Complex-12 2778331 462.80 ns/op
263-
BenchmarkShortDuration/Complex-parallel-12 13124893 81.04 ns/op
264-
BenchmarkBoolPtr/serial-12 1000000000 0.41 ns/op
265-
BenchmarkBoolPtr/parallel-12 1000000000 0.30 ns/op
220+
BenchmarkUnzip-12 3919 262998.00 ns/op
221+
BenchmarkUnzipBytes-12 4461 249418.00 ns/op
222+
BenchmarkB64Encode/1-12 15309999 82.28 ns/op
223+
BenchmarkB64Encode/1-parallel-12 66409862 15.98 ns/op
224+
BenchmarkB64Encode/1000-12 356790 4955.00 ns/op
225+
BenchmarkB64Encode/1000-parallel-12 1349776 839.50 ns/op
226+
BenchmarkB64Encode/1000000-12 296 4421480.00 ns/op
227+
BenchmarkB64Encode/1000000-parallel-12 1935 562698.00 ns/op
228+
BenchmarkB64Decode/1000-12 358122 3997.00 ns/op
229+
BenchmarkB64Decode/1000-parallel-12 1674694 688.20 ns/op
230+
BenchmarkB64Decode/10000-12 35721 36986.00 ns/op
231+
BenchmarkB64Decode/10000-parallel-12 186091 5815.00 ns/op
232+
BenchmarkB64Decode/100000-12 5961 342365.00 ns/op
233+
BenchmarkB64Decode/100000-parallel-12 25243 46691.00 ns/op
234+
BenchmarkIsBinary/small-valid-12 130751559 9.20 ns/op
235+
BenchmarkIsBinary/small-valid-parallel-12 721446243 1.43 ns/op
236+
BenchmarkIsBinary/long-valid-12 5305724 232.00 ns/op
237+
BenchmarkIsBinary/long-valid-parallel-12 25722481 39.80 ns/op
238+
BenchmarkIsBinary/small-invalid-12 135880948 8.99 ns/op
239+
BenchmarkIsBinary/small-invalid-parallel-12 763902718 1.55 ns/op
240+
BenchmarkCommandExists/go-12 111031 10957.00 ns/op
241+
BenchmarkCommandExists/go-parallel-12 718045 1587.00 ns/op
242+
BenchmarkCommandExists/asddsa-12 24708 57858.00 ns/op
243+
BenchmarkCommandExists/asddsa-parallel-12 130980 8246.00 ns/op
244+
BenchmarkSafeExec-12 690 2308535.00 ns/op
245+
BenchmarkCombineFuncs-12 5793744 203.80 ns/op
246+
BenchmarkFuture-12 1492561 789.80 ns/op
247+
BenchmarkRandomLetters/1000-12 326869 4196.00 ns/op
248+
BenchmarkRandomLetters/1000-parallel-12 37795 30892.00 ns/op
249+
BenchmarkRandomLetters/10000-12 29838 41700.00 ns/op
250+
BenchmarkRandomLetters/10000-parallel-12 3884 305148.00 ns/op
251+
BenchmarkRandomLetters/100000-12 2571 400259.00 ns/op
252+
BenchmarkRandomLetters/100000-parallel-12 386 2972860.00 ns/op
253+
BenchmarkUniqueStrings/slice1-12 942453 1435.00 ns/op
254+
BenchmarkUniqueStrings/slice1-parallel-12 4134234 273.10 ns/op
255+
BenchmarkUniqueStrings/slice2-12 8259 261283.00 ns/op
256+
BenchmarkUniqueStrings/slice2-parallel-12 21670 56012.00 ns/op
257+
BenchmarkUniqueInts/slice1-12 1523756 843.80 ns/op
258+
BenchmarkUniqueInts/slice1-parallel-12 7665976 133.00 ns/op
259+
BenchmarkUniqueInts/slice2-12 10000 159122.00 ns/op
260+
BenchmarkUniqueInts/slice2-parallel-12 61369 17542.00 ns/op
261+
BenchmarkUniqueInterfaces/slice1-12 1000000 1743.00 ns/op
262+
BenchmarkUniqueInterfaces/slice1-parallel-12 3386178 337.70 ns/op
263+
BenchmarkUniqueInterfaces/slice2-12 2461 585875.00 ns/op
264+
BenchmarkUniqueInterfaces/slice2-parallel-12 8068 131634.00 ns/op
265+
BenchmarkShortDuration/Simple-12 14830498 92.78 ns/op
266+
BenchmarkShortDuration/Simple-parallel-12 61034352 17.06 ns/op
267+
BenchmarkShortDuration/Complex-12 2587477 504.30 ns/op
268+
BenchmarkShortDuration/Complex-parallel-12 8923653 123.70 ns/op
269+
BenchmarkBoolPtr/serial-12 1000000000 0.43 ns/op
270+
BenchmarkBoolPtr/parallel-12 1000000000 0.33 ns/op
266271
```
267272

268273
## Contribute

os.go

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
package u
22

33
import (
4-
"errors"
54
"fmt"
65
"io"
76
"io/ioutil"
87
"os"
98
"os/user"
10-
"strings"
119
)
1210

1311
// TempfileWithContent creates a tempfile with specified content written in it, it also seeks the file pointer so you can read it directly.
@@ -48,35 +46,6 @@ func MustTempfileWithContent(content []byte) (*os.File, func()) {
4846
return f, cleanup
4947
}
5048

51-
func ExpandUser(path string) (string, error) {
52-
// expand variables
53-
path = os.ExpandEnv(path)
54-
55-
// replace ~ with homedir
56-
if len(path) > 1 && path[:2] == "~/" {
57-
home := os.Getenv("HOME") // *nix
58-
if home == "" {
59-
home = os.Getenv("USERPROFILE") // windows
60-
}
61-
if home == "" {
62-
return "", errors.New("user home directory not found")
63-
}
64-
65-
return strings.Replace(path, "~", home, 1), nil
66-
}
67-
68-
return path, nil
69-
}
70-
71-
// MustExpandUser wraps ExpandUser and panics if initialization fails.
72-
func MustExpandUser(path string) string {
73-
ret, err := ExpandUser(path)
74-
if err != nil {
75-
panic(err)
76-
}
77-
return ret
78-
}
79-
8049
// PathExists checks whether a path exists or not.
8150
func PathExists(path string) bool {
8251
_, err := os.Stat(path)

os_test.go

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,23 +45,6 @@ func ExampleMustTempfileWithContent() {
4545
// CCC
4646
}
4747

48-
func ExampleExpandUser() {
49-
os.Setenv("HOME", "/home/foo") // just for example
50-
ret, err := u.ExpandUser("~/hello-world.txt")
51-
if err != nil {
52-
panic(err)
53-
}
54-
fmt.Println(ret)
55-
// Output: /home/foo/hello-world.txt
56-
}
57-
58-
func ExampleMustExpandUser() {
59-
os.Setenv("HOME", "/home/foo") // just for example
60-
ret := u.MustExpandUser("~/hello-world.txt")
61-
fmt.Println(ret)
62-
// Output: /home/foo/hello-world.txt
63-
}
64-
6548
func ExamplePathExists() {
6649
file, err := ioutil.TempFile("", "bar")
6750
if err != nil {

path.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package u
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
)
10+
11+
// ExpandPath performs various expansions on a given path.
12+
//
13+
// - Replaces ~/ with $HOME/.
14+
// - Returns absolute path.
15+
// - Expands env vars.
16+
// TODO: - Follow symlinks.
17+
func ExpandPath(path string) (string, error) {
18+
// expand variables
19+
path = os.ExpandEnv(path)
20+
21+
// replace ~ with homedir
22+
if len(path) > 1 && path[:2] == "~/" {
23+
home := os.Getenv("HOME") // *nix
24+
if home == "" {
25+
home = os.Getenv("USERPROFILE") // windows
26+
}
27+
if home == "" {
28+
return "", errors.New("user home directory not found")
29+
}
30+
31+
return strings.Replace(path, "~", home, 1), nil
32+
}
33+
34+
// compute absolute path
35+
{
36+
result, err := filepath.Abs(path)
37+
if err != nil {
38+
return "", fmt.Errorf("absolute path: %q: %w", path, err)
39+
}
40+
path = result
41+
}
42+
43+
// expand env vars
44+
{
45+
path = os.ExpandEnv(path)
46+
}
47+
48+
// eval symlinks
49+
// TODO: do not fail if path does not exist
50+
/*
51+
{
52+
result, err := filepath.EvalSymlinks(path)
53+
if err != nil {
54+
return "", fmt.Errorf("eval symlinks: %q: %w", path, err)
55+
}
56+
path = result
57+
}
58+
*/
59+
60+
return path, nil
61+
}
62+
63+
// MustExpandPath wraps ExpandPath and panics if initialization fails.
64+
func MustExpandPath(path string) string {
65+
ret, err := ExpandPath(path)
66+
if err != nil {
67+
panic(err)
68+
}
69+
return ret
70+
}

path_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package u_test
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
"strings"
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
"moul.io/u"
13+
)
14+
15+
func TestExpandPath(t *testing.T) {
16+
os.Setenv("HOME", "/home/foo")
17+
os.Setenv("USER", "foo")
18+
os.Unsetenv("FOOBAR")
19+
workdir, err := os.Getwd()
20+
require.NoError(t, err)
21+
22+
tests := []struct {
23+
input string
24+
expected string
25+
shouldFails bool
26+
}{
27+
{"/home/foo", "/home/foo", false},
28+
{"/home/foo/", "/home/foo", false},
29+
{"~", filepath.Join(workdir, "~"), false},
30+
{"~/", "/home/foo/", false},
31+
{"$HOME/hello", "/home/foo/hello", false},
32+
{"/home/$USER/hello", "/home/foo/hello", false},
33+
{"/tmp/$FOOBAR/hello", "/tmp/hello", false},
34+
}
35+
for _, tc := range tests {
36+
name := strings.Replace(tc.input, "/", "-", -1)
37+
t.Run(name, func(t *testing.T) {
38+
result, err := u.ExpandPath(tc.input)
39+
if tc.shouldFails {
40+
assert.Error(t, err)
41+
assert.Empty(t, result)
42+
} else {
43+
assert.NoError(t, err)
44+
assert.Equal(t, result, tc.expected)
45+
}
46+
})
47+
}
48+
}
49+
50+
func ExampleExpandPath() {
51+
os.Setenv("HOME", "/home/foo") // just for example
52+
ret, err := u.ExpandPath("~/hello-world.txt")
53+
if err != nil {
54+
panic(err)
55+
}
56+
fmt.Println(ret)
57+
// Output: /home/foo/hello-world.txt
58+
}
59+
60+
func ExampleMustExpandPath() {
61+
os.Setenv("HOME", "/home/foo") // just for example
62+
ret := u.MustExpandPath("~/hello-world.txt")
63+
fmt.Println(ret)
64+
// Output: /home/foo/hello-world.txt
65+
}

0 commit comments

Comments
 (0)