--- /dev/null
+package getter
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "runtime"
+)
+
+// FileDetector implements Detector to detect file paths.
+type FileDetector struct{}
+
+func (d *FileDetector) Detect(src, pwd string) (string, bool, error) {
+ if len(src) == 0 {
+ return "", false, nil
+ }
+
+ if !filepath.IsAbs(src) {
+ if pwd == "" {
+ return "", true, fmt.Errorf(
+ "relative paths require a module with a pwd")
+ }
+
+ // Stat the pwd to determine if its a symbolic link. If it is,
+ // then the pwd becomes the original directory. Otherwise,
+ // `filepath.Join` below does some weird stuff.
+ //
+ // We just ignore if the pwd doesn't exist. That error will be
+ // caught later when we try to use the URL.
+ if fi, err := os.Lstat(pwd); !os.IsNotExist(err) {
+ if err != nil {
+ return "", true, err
+ }
+ if fi.Mode()&os.ModeSymlink != 0 {
+ pwd, err = os.Readlink(pwd)
+ if err != nil {
+ return "", true, err
+ }
+
+ // The symlink itself might be a relative path, so we have to
+ // resolve this to have a correctly rooted URL.
+ pwd, err = filepath.Abs(pwd)
+ if err != nil {
+ return "", true, err
+ }
+ }
+ }
+
+ src = filepath.Join(pwd, src)
+ }
+
+ return fmtFileURL(src), true, nil
+}
+
+func fmtFileURL(path string) string {
+ if runtime.GOOS == "windows" {
+ // Make sure we're using "/" on Windows. URLs are "/"-based.
+ path = filepath.ToSlash(path)
+ return fmt.Sprintf("file://%s", path)
+ }
+
+ // Make sure that we don't start with "/" since we add that below.
+ if path[0] == '/' {
+ path = path[1:]
+ }
+ return fmt.Sprintf("file:///%s", path)
+}