cuelang.org/go@v0.10.1/mod/module/escape.go (about) 1 package module 2 3 import ( 4 "fmt" 5 "strings" 6 "unicode/utf8" 7 8 "cuelang.org/go/internal/mod/semver" 9 ) 10 11 // EscapePath returns the escaped form of the given module path 12 // (without the major version suffix). 13 // It fails if the module path is invalid. 14 func EscapePath(path string) (escaped string, err error) { 15 if err := CheckPathWithoutVersion(path); err != nil { 16 return "", err 17 } 18 // Technically there's no need to escape capital letters because CheckPath 19 // doesn't allow them, but let's be defensive. 20 return escapeString(path) 21 } 22 23 // EscapeVersion returns the escaped form of the given module version. 24 // Versions must be in (possibly non-canonical) semver form and must be valid file names 25 // and not contain exclamation marks. 26 func EscapeVersion(v string) (escaped string, err error) { 27 if !semver.IsValid(v) { 28 return "", &InvalidVersionError{ 29 Version: v, 30 Err: fmt.Errorf("version is not in semver syntax"), 31 } 32 } 33 if err := checkElem(v, filePath); err != nil || strings.Contains(v, "!") { 34 return "", &InvalidVersionError{ 35 Version: v, 36 Err: fmt.Errorf("disallowed version string"), 37 } 38 } 39 return escapeString(v) 40 } 41 42 func escapeString(s string) (escaped string, err error) { 43 haveUpper := false 44 for _, r := range s { 45 if r == '!' || r >= utf8.RuneSelf { 46 // This should be disallowed by CheckPath, but diagnose anyway. 47 // The correctness of the escaping loop below depends on it. 48 return "", fmt.Errorf("internal error: inconsistency in EscapePath") 49 } 50 if 'A' <= r && r <= 'Z' { 51 haveUpper = true 52 } 53 } 54 55 if !haveUpper { 56 return s, nil 57 } 58 59 var buf []byte 60 for _, r := range s { 61 if 'A' <= r && r <= 'Z' { 62 buf = append(buf, '!', byte(r+'a'-'A')) 63 } else { 64 buf = append(buf, byte(r)) 65 } 66 } 67 return string(buf), nil 68 }