From 2c52f07ca4d7bea3f1056576bfd2f7eea21c100d Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Thu, 31 May 2018 15:07:10 +0100 Subject: [PATCH] Simple utility for calculating the directory for go repos to go --- .gitignore | 3 ++ gosrcdir.go | 118 +++++++++++++++++++++++++++++++++++++++++++++++ gosrcdir_test.go | 82 ++++++++++++++++++++++++++++++++ 3 files changed, 203 insertions(+) create mode 100644 gosrcdir.go create mode 100644 gosrcdir_test.go diff --git a/.gitignore b/.gitignore index f1c181e..035d242 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out + +# Tag file +tags diff --git a/gosrcdir.go b/gosrcdir.go new file mode 100644 index 0000000..0d609a2 --- /dev/null +++ b/gosrcdir.go @@ -0,0 +1,118 @@ +package main + +import ( + "errors" + "fmt" + "go/build" + "net/url" + "os" + "path" + "strings" +) + +// parseStandardURL parses a standard URI formatted git URL +func parseStandardURL(repo string) (pathParts []string, err error) { + parsedURL, err := url.Parse(repo) + if err != nil { + return + } + + if parsedURL.Host == "" { + err = errors.New("Missing host part") + return + } + + pathParts = append(pathParts, parsedURL.Host) + + for _, pathPart := range strings.Split(parsedURL.Path, "/") { + if pathPart == "" { + continue + } + pathParts = append(pathParts, pathPart) + } + + return +} + +// parseWeirdGitURL tries to make sense of a user@host:path format git URL +func parseWeirdGitURL(repo string) (pathParts []string, err error) { + + hostIndex := strings.Index(repo, "@") + 1 + pathIndex := strings.Index(repo, ":") + 1 + repoPath := strings.Split(string(repo[pathIndex:]), "/") + + // If there is no : then this is wrong + if pathIndex == 0 || len(repoPath) == 0 { + err = errors.New("Missing path part") + return + } + + // if index of @ is -1 then host is the first thing + // if @ is after : then it's part of the path + if hostIndex > pathIndex { + hostIndex = 0 + } + + host := string(repo[hostIndex : pathIndex-1]) + pathParts = append(pathParts, host) + + for _, pathPart := range repoPath { + if pathPart == "" { + err = fmt.Errorf("Blank path segment") + return + } + pathParts = append(pathParts, pathPart) + } + + return +} + +// calculateSourcePath works out the local filesystem path to directory above a given repo +func calculateSourcePath(goPath string, repo string) (repoPath string, err error) { + pathParts, err := parseStandardURL(repo) + if err != nil { + pathParts, err = parseWeirdGitURL(repo) + if err != nil { + return + } + } + + if len(pathParts) < 2 { + err = errors.New("Host and path required") + return + } + + pathParts = append([]string{goPath, "src"}, pathParts[:len(pathParts)-1]...) + repoPath = path.Join(pathParts...) + return +} + +func getGoPath() string { + goPath := os.Getenv("GOPATH") + if goPath != "" { + return goPath + } + return build.Default.GOPATH +} + +func main() { + args := os.Args[1:] + + // Require at least one repo + if len(args) == 0 { + os.Exit(1) + } + + // Work out where GOPATH really is + goPath := getGoPath() + + // Calculate path for all repos + for _, repo := range args { + repoPath, err := calculateSourcePath(goPath, repo) + if err != nil { + fmt.Fprintf(os.Stderr, "Cannot parse repo URL %s: %s\n", repo, err) + os.Exit(1) + } + fmt.Println(repoPath) + } +} diff --git a/gosrcdir_test.go b/gosrcdir_test.go new file mode 100644 index 0000000..2a6134e --- /dev/null +++ b/gosrcdir_test.go @@ -0,0 +1,82 @@ +package main + +import ( + "reflect" + "testing" +) + +type ParseTest struct { + Name string + Repo string +} + +func TestParsesStandardURL(t *testing.T) { + for _, test := range []ParseTest{ + {"HTTPS", "https://user@host.com/path/to/repo"}, + {"SSH", "ssh://user@host.com/path/to/repo"}, + {"Empty path elements", "https://user@host.com//path//to//repo"}, + } { + repoPath, err := parseStandardURL(test.Repo) + if err != nil { + t.Errorf("%s: Unexpected error %q", test.Name, err) + } + + expectedPath := []string{"host.com", "path", "to", "repo"} + if !reflect.DeepEqual(repoPath, expectedPath) { + t.Errorf("%s: Expected %q, got %q", test.Name, expectedPath, repoPath) + } + } +} + +func TestParsesWeirdGitURL(t *testing.T) { + for _, test := range []ParseTest{ + {"with user", "user@host.com:path/to/repo"}, + {"without user", "host.com:path/to/repo"}, + } { + repoPath, err := parseWeirdGitURL(test.Repo) + if err != nil { + t.Errorf("%s: Unexpected error %q", test.Name, err) + } + + expectedPath := []string{"host.com", "path", "to", "repo"} + if !reflect.DeepEqual(repoPath, expectedPath) { + t.Errorf("%s: Expected %q, got %q", test.Name, expectedPath, repoPath) + } + } +} + +func TestIsIntolerantToEmptyPathElementsInWeirdURL(t *testing.T) { + _, err := parseWeirdGitURL("user@host.com:path/to//repo") + if err == nil { + t.Errorf("Expected error, got %q", err) + } +} + +func TestSourcePath(t *testing.T) { + for _, test := range []ParseTest{ + {"Standard URL", "https://user@host.com/path/to/repo"}, + {"Weird URL", "user@host.com:path/to/repo"}, + } { + repoPath, err := calculateSourcePath("/home/user", test.Repo) + if err != nil { + t.Errorf("%s: Unexpected error %q", test.Name, err) + } + + expectedPath := "/home/user/src/host.com/path/to" + if repoPath != expectedPath { + t.Errorf("%sExpected %q, got %q", test.Name, expectedPath, repoPath) + } + } +} + +func TestSourcePathErrorsIfMissingHostOrPath(t *testing.T) { + for _, test := range []ParseTest{ + {"Standard URL", "https://user@host.com/"}, + {"Weird URL", "user@host.com:"}, + } { + _, err := calculateSourcePath("/home/user", "user@host.com:") + if err == nil { + t.Errorf("%s: Expected error, got %q", test.Name, err) + } + } +}