2018-11-02 14:15:31 -06:00
|
|
|
// Copyright 2018 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package source
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2018-11-05 15:54:12 -07:00
|
|
|
"net/url"
|
2018-11-02 14:15:31 -06:00
|
|
|
"path/filepath"
|
2018-11-14 18:02:22 -07:00
|
|
|
"runtime"
|
2018-11-02 14:15:31 -06:00
|
|
|
"strings"
|
2018-12-12 17:16:30 -07:00
|
|
|
"unicode"
|
2018-11-02 14:15:31 -06:00
|
|
|
)
|
|
|
|
|
2018-12-12 17:16:30 -07:00
|
|
|
const fileScheme = "file"
|
|
|
|
|
|
|
|
// URI represents the full URI for a file.
|
2018-11-05 15:54:12 -07:00
|
|
|
type URI string
|
|
|
|
|
|
|
|
// Filename gets the file path for the URI.
|
2018-11-02 14:15:31 -06:00
|
|
|
// It will return an error if the uri is not valid, or if the URI was not
|
|
|
|
// a file URI
|
2018-11-05 15:54:12 -07:00
|
|
|
func (uri URI) Filename() (string, error) {
|
2018-12-12 17:16:30 -07:00
|
|
|
filename, err := filename(uri)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
2018-11-02 14:15:31 -06:00
|
|
|
}
|
2018-12-12 17:16:30 -07:00
|
|
|
return filepath.FromSlash(filename), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func filename(uri URI) (string, error) {
|
|
|
|
u, err := url.ParseRequestURI(string(uri))
|
2018-11-05 15:54:12 -07:00
|
|
|
if err != nil {
|
2018-12-12 17:16:30 -07:00
|
|
|
return "", err
|
2018-11-05 15:54:12 -07:00
|
|
|
}
|
2018-12-12 17:16:30 -07:00
|
|
|
if u.Scheme != fileScheme {
|
|
|
|
return "", fmt.Errorf("only file URIs are supported, got %v", u.Scheme)
|
|
|
|
}
|
|
|
|
if isWindowsDriveURI(u.Path) {
|
|
|
|
u.Path = u.Path[1:]
|
|
|
|
}
|
|
|
|
return u.Path, nil
|
2018-11-02 14:15:31 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// ToURI returns a protocol URI for the supplied path.
|
|
|
|
// It will always have the file scheme.
|
2018-11-05 15:54:12 -07:00
|
|
|
func ToURI(path string) URI {
|
2018-12-12 17:16:30 -07:00
|
|
|
u := toURI(path)
|
|
|
|
u.Path = filepath.ToSlash(u.Path)
|
|
|
|
return URI(u.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
func toURI(path string) *url.URL {
|
|
|
|
// Handle standard library paths that contain the literal "$GOROOT".
|
|
|
|
// TODO(rstambler): The go/packages API should allow one to determine a user's $GOROOT.
|
2018-11-14 18:02:22 -07:00
|
|
|
const prefix = "$GOROOT"
|
2018-12-18 13:46:14 -07:00
|
|
|
if len(path) >= len(prefix) && strings.EqualFold(prefix, path[:len(prefix)]) {
|
2018-11-14 18:02:22 -07:00
|
|
|
suffix := path[len(prefix):]
|
|
|
|
path = runtime.GOROOT() + suffix
|
|
|
|
}
|
2018-12-12 17:16:30 -07:00
|
|
|
if isWindowsDrivePath(path) {
|
|
|
|
path = "/" + path
|
|
|
|
}
|
|
|
|
return &url.URL{
|
|
|
|
Scheme: fileScheme,
|
|
|
|
Path: path,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// isWindowsDrivePath returns true if the file path is of the form used by
|
|
|
|
// Windows. We check if the path begins with a drive letter, followed by a ":".
|
|
|
|
func isWindowsDrivePath(path string) bool {
|
|
|
|
if len(path) < 4 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return unicode.IsLetter(rune(path[0])) && path[1] == ':'
|
|
|
|
}
|
|
|
|
|
|
|
|
// isWindowsDriveURI returns true if the file URI is of the format used by
|
|
|
|
// Windows URIs. The url.Parse package does not specially handle Windows paths
|
|
|
|
// (see https://github.com/golang/go/issues/6027). We check if the URI path has
|
|
|
|
// a drive prefix (e.g. "/C:"). If so, we trim the leading "/".
|
|
|
|
func isWindowsDriveURI(uri string) bool {
|
|
|
|
if len(uri) < 4 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return uri[0] == '/' && unicode.IsLetter(rune(uri[1])) && uri[2] == ':'
|
2018-11-02 14:15:31 -06:00
|
|
|
}
|