Documentation ¶
Overview ¶
Package module defines the Version type along with support code.
WARNING: THIS PACKAGE IS EXPERIMENTAL. ITS API MAY CHANGE AT ANY TIME.
The Version type holds a pair of module path and version. The module path conforms to the checks implemented by Check.
Escaped Paths ¶
Module versions appear as substrings of file system paths (as stored by the modcache package). In general we cannot rely on file systems to be case-sensitive. Although module paths cannot currently contain upper case characters because OCI registries forbid that, versions can. That is, we cannot rely on the file system to keep foo.com/v@v1.0.0-PRE and foo.com/v@v1.0.0-PRE separate. Windows and macOS don't. Instead, we must never require two different casings of a file path.
One possibility would be to make the escaped form be the lowercase hexadecimal encoding of the actual path bytes. This would avoid ever needing different casings of a file path, but it would be fairly illegible to most programmers when those paths appeared in the file system (including in file paths in compiler errors and stack traces) in web server logs, and so on. Instead, we want a safe escaped form that leaves most paths unaltered.
The safe escaped form is to replace every uppercase letter with an exclamation mark followed by the letter's lowercase equivalent.
For example,
foo.com/v@v1.0.0-PRE -> foo.com/v@v1.0.0-!p!r!e
Versions that avoid upper-case letters are left unchanged. Note that because import paths are ASCII-only and avoid various problematic punctuation (like : < and >), the escaped form is also ASCII-only and avoids the same problematic punctuation.
Neither versions nor module paths allow exclamation marks, so there is no need to define how to escape a literal !.
Unicode Restrictions ¶
Today, paths are disallowed from using Unicode.
Although paths are currently disallowed from using Unicode, we would like at some point to allow Unicode letters as well, to assume that file systems and URLs are Unicode-safe (storing UTF-8), and apply the !-for-uppercase convention for escaping them in the file system. But there are at least two subtle considerations.
First, note that not all case-fold equivalent distinct runes form an upper/lower pair. For example, U+004B ('K'), U+006B ('k'), and U+212A ('K' for Kelvin) are three distinct runes that case-fold to each other. When we do add Unicode letters, we must not assume that upper/lower are the only case-equivalent pairs. Perhaps the Kelvin symbol would be disallowed entirely, for example. Or perhaps it would escape as "!!k", or perhaps as "(212A)".
Second, it would be nice to allow Unicode marks as well as letters, but marks include combining marks, and then we must deal not only with case folding but also normalization: both U+00E9 ('é') and U+0065 U+0301 ('e' followed by combining acute accent) look the same on the page and are treated by some file systems as the same path. If we do allow Unicode marks in paths, there must be some kind of normalization to allow only one canonical encoding of any character used in an import path.
Index ¶
- func Check(path, version string) error
- func CheckFilePath(path string) error
- func CheckImportPath(path string) error
- func CheckPath(mpath string) (err error)
- func CheckPathMajor(v, pathMajor string) error
- func CheckPathWithoutVersion(basePath string) (err error)
- func EscapePath(path string) (escaped string, err error)
- func EscapeVersion(v string) (escaped string, err error)
- func OSDirFS(p string) fs.FS
- func Sort(list []Version)
- func SplitPathVersion(path string) (prefix, version string, ok bool)
- type ImportPath
- type InvalidPathError
- type InvalidVersionError
- type ModuleError
- type OSRootFS
- type ReadCUEFS
- type SourceLoc
- type Version
- type Versions
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Check ¶
Check checks that a given module path, version pair is valid. In addition to the path being a valid module path and the version being a valid semantic version, the two must correspond. For example, the path "foo.com/bar@v2" only corresponds to semantic versions beginning with "v2.".
func CheckFilePath ¶
CheckFilePath checks that a slash-separated file path is valid. The definition of a valid file path is the same as the definition of a valid import path except that the set of allowed characters is larger: all Unicode letters, ASCII digits, the ASCII space character (U+0020), and the ASCII punctuation characters “!#$%&()+,-.=@[]^_{}~”. (The excluded punctuation characters, " * < > ? ` ' | / \ and :, have special meanings in certain shells or operating systems.)
CheckFilePath may be less restrictive in the future, but see the top-level package documentation for additional information about subtleties of Unicode.
func CheckImportPath ¶
CheckImportPath checks that an import path is valid.
A valid import path consists of one or more valid path elements separated by slashes (U+002F), optionally followed by an @vN (major version) qualifier. The path part must not begin with nor end in a slash.
A valid path element is a non-empty string made up of lower case ASCII letters, ASCII digits, and limited ASCII punctuation: - . and _ Punctuation characters may not be adjacent and must be between non-punctuation characters.
The element prefix up to the first dot must not be a reserved file name on Windows, regardless of case (CON, com1, NuL, and so on).
func CheckPath ¶
CheckPath checks that a module path is valid. A valid module path is a valid import path, as checked by CheckImportPath, with three additional constraints.
First, the leading path element (up to the first slash, if any), by convention a domain name, must contain only lower-case ASCII letters, ASCII digits, dots (U+002E), and dashes (U+002D); it must contain at least one dot and cannot start with a dash.
Second, there may be a final major version of the form @vN where N looks numeric (ASCII digits) and must not begin with a leading zero. Without such a major version, the major version is assumed to be v0.
Third, no path element may begin with a dot.
func CheckPathMajor ¶
CheckPathMajor returns a non-nil error if the semantic version v does not match the path major version pathMajor.
func CheckPathWithoutVersion ¶
CheckPathWithoutVersion is like CheckPath except that it expects a module path without a major version.
func EscapePath ¶
EscapePath returns the escaped form of the given module path (without the major version suffix). It fails if the module path is invalid.
func EscapeVersion ¶
EscapeVersion returns the escaped form of the given module version. Versions must be in (possibly non-canonical) semver form and must be valid file names and not contain exclamation marks.
func Sort ¶
func Sort(list []Version)
Sort sorts the list by Path, breaking ties by comparing Version fields. The Version fields are interpreted as semantic versions (using semver.Compare) optionally followed by a tie-breaking suffix introduced by a slash character, like in "v0.0.1/module.cue".
func SplitPathVersion ¶
SplitPathVersion returns a prefix and version suffix such that prefix+"@"+version == path. SplitPathVersion returns with ok=false when presented with a path with an invalid version suffix.
For example, SplitPathVersion("foo.com/bar@v0.1") returns ("foo.com/bar", "v0.1", true).
Types ¶
type ImportPath ¶
type ImportPath struct { // Path holds the base package/directory path, similar // to that returned by [Version.BasePath]. Path string // Version holds the version of the import // or empty if not present. Note: in general this // will contain a major version only, but there's no // guarantee of that. Version string // Qualifier holds the package qualifier within the path. // This will be derived from the last component of Path // if it wasn't explicitly present in the import path. // This is not guaranteed to be a valid CUE identifier. Qualifier string // ExplicitQualifier holds whether the qualifier will // always be added regardless of whether it matches // the final path element. ExplicitQualifier bool }
ImportPath holds the various components of an import path.
func ParseImportPath ¶
func ParseImportPath(p string) ImportPath
ParseImportPath returns the various components of an import path. It does not check the result for validity.
func (ImportPath) Canonical ¶
func (parts ImportPath) Canonical() ImportPath
Canonical returns the canonical form of the import path. Specifically, it will only include the package qualifier if it's different from the last component of parts.Path.
func (ImportPath) String ¶
func (parts ImportPath) String() string
func (ImportPath) Unqualified ¶
func (parts ImportPath) Unqualified() ImportPath
Unqualified returns the import path without any package qualifier.
type InvalidPathError ¶
An InvalidPathError indicates a module, import, or file path doesn't satisfy all naming constraints. See CheckPath, CheckImportPath, and CheckFilePath for specific restrictions.
func (*InvalidPathError) Error ¶
func (e *InvalidPathError) Error() string
func (*InvalidPathError) Unwrap ¶
func (e *InvalidPathError) Unwrap() error
type InvalidVersionError ¶
An InvalidVersionError indicates an error specific to a version, with the module path unknown or specified externally.
A ModuleError may wrap an InvalidVersionError, but an InvalidVersionError must not wrap a ModuleError.
func (*InvalidVersionError) Error ¶
func (e *InvalidVersionError) Error() string
func (*InvalidVersionError) Unwrap ¶
func (e *InvalidVersionError) Unwrap() error
type ModuleError ¶
A ModuleError indicates an error specific to a module.
func (*ModuleError) Error ¶
func (e *ModuleError) Error() string
func (*ModuleError) Unwrap ¶
func (e *ModuleError) Unwrap() error
type OSRootFS ¶
type OSRootFS interface { fs.FS // OSRoot returns the root directory of the FS // as an OS file path. If it wasn't possible to do that, // it returns the empty string. OSRoot() string }
OSRootFS can be implemented by an fs.FS implementation to return its root directory as an OS file path.
type ReadCUEFS ¶ added in v0.10.0
type ReadCUEFS interface { fs.FS // ReadCUEFile reads CUE syntax from the given path. // // If this method is implemented, but the implementation // does not support reading CUE files, // it should return [errors.ErrUnsupported]. ReadCUEFile(path string) (*ast.File, error) }
ReadCUE can be implemented by an fs.FS to provide an optimized (cached) way of reading and parsing CUE syntax.
type SourceLoc ¶
type SourceLoc struct { // FS is the filesystem containing the source. FS fs.FS // Dir is the directory within the above filesystem. Dir string }
SourceLoc represents the location of some CUE source code.
type Version ¶
type Version struct {
// contains filtered or unexported fields
}
A Version (for clients, a module.Version) is defined by a module path and version pair. These are stored in their plain (unescaped) form. This type is comparable.
func MustNewVersion ¶
func MustParseVersion ¶
func NewVersion ¶
NewVersion forms a Version from the given path and version. The version must be canonical, empty or "none". If the path doesn't have a major version suffix, one will be added if the version isn't empty; if the version is empty, it's an error.
As a special case, the path "local" is used to mean all packages held in the gen, pkg and usr directories.
func ParseVersion ¶
ParseVersion parses a $module@$version string into a Version. The version must be canonical (i.e. it can't be just a major version).
func (Version) IsCanonical ¶
IsCanonical reports whether m is valid and has a canonical semver version.
func (Version) Path ¶
Path returns the module path part of the Version, which always includes the major version suffix unless a module path, like "github.com/foo/bar@v0". Note that in general the path should include the major version suffix even though it's implied from the version. The Canonical method can be used to add the major version suffix if not present. The BasePath method can be used to obtain the path without the suffix.
type Versions ¶
type Versions struct{}
Versions implements mvs.Versions[Version].
func (Versions) Max ¶
Max implements mvs.Reqs.Max.
It is consistent with semver.Compare except that as a special case, the version "" is considered higher than all other versions. The main module (also known as the target) has no version and must be chosen over other versions of the same module in the module dependency graph.
See [mvs.Reqs] for more detail.