From dc13d5d26b7784f97e955f9e584740c2d5147e8c Mon Sep 17 00:00:00 2001 From: James Shubin Date: Thu, 29 Nov 2018 15:58:52 -0500 Subject: [PATCH] util: Add some useful path parsing functions These two are useful for looking at path prefixes and rebasing the paths onto other paths. --- util/util.go | 28 ++++++++++ util/util_test.go | 131 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) diff --git a/util/util.go b/util/util.go index bb1e859b..eedadbd7 100644 --- a/util/util.go +++ b/util/util.go @@ -349,6 +349,34 @@ func FlattenListWithSplit(input []string, split []string) []string { return out } +// RemoveBasePath removes an absolute base path (directory prefix) from an +// absolute path that is any file or directory. +// Eg: RemoveBasePath("/usr/bin/foo", "/usr/") -> "bin/foo" +// Eg: RemoveBasePath("/usr/bin/project/", "/usr/") -> "bin/project/". +func RemoveBasePath(path, base string) (string, error) { + if !strings.HasSuffix(base, "/") { // should end with a slash + return "", fmt.Errorf("base is not a directory") + } + if !strings.HasPrefix(path, base) { + return "", fmt.Errorf("path does not have base prefix") + } + return strings.TrimPrefix(path, base), nil +} + +// Rebase takes an absolute base path (directory prefix) and removes it from an +// absolute path and then returns that path with a new root as an absolute path. +// Eg: Rebase("/usr/bin/foo", "/usr/", "/usr/local/") -> "/usr/local/bin/foo" +func Rebase(path, base, root string) (string, error) { + if !strings.HasSuffix(root, "/") { // should end with a slash + return "", fmt.Errorf("root is not a directory") + } + s, err := RemoveBasePath(path, base) + if err != nil { + return "", err + } + return root + s, nil +} + // TimeAfterOrBlock is aspecial version of time.After that blocks when given a // negative integer. When used in a case statement, the timer restarts on each // select call to it. diff --git a/util/util_test.go b/util/util_test.go index c34e0e15..55da6973 100644 --- a/util/util_test.go +++ b/util/util_test.go @@ -816,6 +816,137 @@ func TestUtilFlattenListWithSplit1(t *testing.T) { } } +func TestRemoveBasePath0(t *testing.T) { + // expected successes... + if s, err := RemoveBasePath("/usr/bin/foo", "/usr/"); err != nil { + t.Errorf("unexpected error: %v", err) + } else if s != "bin/foo" { + t.Errorf("unexpected string, got: %s", s) + } + if s, err := RemoveBasePath("/usr/bin/project/", "/usr/"); err != nil { + t.Errorf("unexpected error: %v", err) + } else if s != "bin/project/" { + t.Errorf("unexpected string, got: %s", s) + } + if s, err := RemoveBasePath("/", "/"); err != nil { + t.Errorf("unexpected error: %v", err) + } else if s != "" { // TODO: is this correct? + t.Errorf("unexpected string, got: %s", s) + } + if s, err := RemoveBasePath("/usr/bin/project/", "/"); err != nil { + t.Errorf("unexpected error: %v", err) + } else if s != "usr/bin/project/" { + t.Errorf("unexpected string, got: %s", s) + } + if s, err := RemoveBasePath("/usr/bin/project/", "/usr/bin/"); err != nil { + t.Errorf("unexpected error: %v", err) + } else if s != "project/" { + t.Errorf("unexpected string, got: %s", s) + } + if s, err := RemoveBasePath("/usr/bin/foo", "/usr/bin/"); err != nil { + t.Errorf("unexpected error: %v", err) + } else if s != "foo" { + t.Errorf("unexpected string, got: %s", s) + } + // allow this one, even though it's relative paths + if s, err := RemoveBasePath("usr/bin/project/", "usr/"); err != nil { + t.Errorf("unexpected error: %v", err) + } else if s != "bin/project/" { + t.Errorf("unexpected string, got: %s", s) + } + + // expected errors... + if s, err := RemoveBasePath("", ""); err == nil { + t.Errorf("expected error, got: %s", s) + } + if s, err := RemoveBasePath("", "/usr/"); err == nil { + t.Errorf("expected error, got: %s", s) + } + if s, err := RemoveBasePath("usr/bin/project/", ""); err == nil { + t.Errorf("expected error, got: %s", s) + } + if s, err := RemoveBasePath("usr/bin/project/", "/usr/"); err == nil { + t.Errorf("expected error, got: %s", s) + } + if s, err := RemoveBasePath("/usr/bin/project/", "usr/"); err == nil { + t.Errorf("expected error, got: %s", s) + } + // allow this one, even though it's relative paths + //if s, err := RemoveBasePath("usr/bin/project/", "usr/"); err == nil { + // t.Errorf("expected error, got: %s", s) + //} + if s, err := RemoveBasePath("/usr/bin/project/", "/bin/"); err == nil { + t.Errorf("expected error, got: %s", s) + } +} + +func TestRebasePath0(t *testing.T) { + // expected successes... + if s, err := Rebase("/usr/bin/foo", "/usr/", "/usr/local/"); err != nil { + t.Errorf("unexpected error: %v", err) + } else if s != "/usr/local/bin/foo" { + t.Errorf("unexpected string, got: %s", s) + } + if s, err := Rebase("/usr/bin/project/", "/usr/", "/usr/local/"); err != nil { + t.Errorf("unexpected error: %v", err) + } else if s != "/usr/local/bin/project/" { + t.Errorf("unexpected string, got: %s", s) + } + if s, err := Rebase("/", "/", "/opt/"); err != nil { + t.Errorf("unexpected error: %v", err) + } else if s != "/opt/" { // TODO: is this correct? + t.Errorf("unexpected string, got: %s", s) + } + if s, err := Rebase("/usr/bin/project/", "/", "/opt/"); err != nil { + t.Errorf("unexpected error: %v", err) + } else if s != "/opt/usr/bin/project/" { + t.Errorf("unexpected string, got: %s", s) + } + if s, err := Rebase("/usr/bin/project/", "/usr/bin/", "/opt/"); err != nil { + t.Errorf("unexpected error: %v", err) + } else if s != "/opt/project/" { + t.Errorf("unexpected string, got: %s", s) + } + if s, err := Rebase("/usr/bin/foo", "/usr/bin/", "/opt/"); err != nil { + t.Errorf("unexpected error: %v", err) + } else if s != "/opt/foo" { + t.Errorf("unexpected string, got: %s", s) + } + // allow this one, even though it's relative paths + if s, err := Rebase("usr/bin/project/", "usr/", "/opt/"); err != nil { + t.Errorf("unexpected error: %v", err) + } else if s != "/opt/bin/project/" { + t.Errorf("unexpected string, got: %s", s) + } + + // expected errors... + if s, err := Rebase("", "", "/opt/"); err == nil { + t.Errorf("expected error, got: %s", s) + } + if s, err := Rebase("", "/usr/", "/opt/"); err == nil { + t.Errorf("expected error, got: %s", s) + } + if s, err := Rebase("usr/bin/project/", "", "/opt/"); err == nil { + t.Errorf("expected error, got: %s", s) + } + if s, err := Rebase("usr/bin/project/", "/usr/", "/opt/"); err == nil { + t.Errorf("expected error, got: %s", s) + } + if s, err := Rebase("/usr/bin/project/", "usr/", "/opt/"); err == nil { + t.Errorf("expected error, got: %s", s) + } + // allow this one, even though it's relative paths + //if s, err := Rebase("usr/bin/project/", "usr/", "/opt/"); err == nil { + // t.Errorf("expected error, got: %s", s) + //} + if s, err := Rebase("/usr/bin/project/", "/bin/", "/opt/"); err == nil { + t.Errorf("expected error, got: %s", s) + } + if s, err := Rebase("/usr/bin/project", "/usr/", ""); err == nil { + t.Errorf("expected error, got: %s", s) + } +} + func TestSortedStrSliceCompare0(t *testing.T) { slice0 := []string{"foo", "bar", "baz"} slice1 := []string{"bar", "foo", "baz"}