// Copyright IBM Corp. 2014, 2026 // SPDX-License-Identifier: BUSL-1.1 package moduleaddrs import ( "fmt" "path" "path/filepath" "strings" ) // SplitPackageSubdir detects whether the given address string has a // subdirectory portion, and if so returns a non-empty subDir string // along with the trimmed package address. // // If the given string doesn't have a subdirectory portion then it'll // just be returned verbatim in packageAddr, with an empty subDir value. // // Although the rest of this package is focused only on direct remote // module packages, this particular function and its companion // ExpandSubdirGlobs are both also relevant for registry-based module // addresses, because a registry translates such an address into a // remote module package address and thus can contribute its own // additions to the final subdirectory selection. func SplitPackageSubdir(given string) (packageAddr, subDir string) { packageAddr, subDir = splitPackageSubdirRaw(given) if subDir != "" { subDir = path.Clean(subDir) } return packageAddr, subDir } func splitPackageSubdirRaw(src string) (packageAddr, subDir string) { // URL might contains another url in query parameters stop := len(src) if idx := strings.Index(src, "?"); idx > -1 { stop = idx } // Calculate an offset to avoid accidentally marking the scheme // as the dir. var offset int if idx := strings.Index(src[:stop], "://"); idx > -1 { offset = idx + 3 } // First see if we even have an explicit subdir idx := strings.Index(src[offset:stop], "//") if idx == -1 { return src, "" } idx += offset subdir := src[idx+2:] src = src[:idx] // Next, check if we have query parameters and push them onto the // URL. if idx = strings.Index(subdir, "?"); idx > -1 { query := subdir[idx:] subdir = subdir[:idx] src += query } return src, subdir } // ExpandSubdirGlobs handles a subdir string that might contain glob syntax, // turning it into a concrete subdirectory path by referring to the actual // files on disk in the given directory which we assume contains the content // of whichever package this is a subdirectory glob for. // // Subdir globs are used, for example, when a module registry wants to specify // to select the contents of the single directory at the root of a conventional // tar archive but it doesn't actually know the exact name of that directory. // In that case it might specify a subdir of just "*", which this function // will then expand into the single subdirectory found inside instDir, or // return an error if the result would be ambiguous. func ExpandSubdirGlobs(instDir string, subDir string) (string, error) { pattern := filepath.Join(instDir, subDir) matches, err := filepath.Glob(pattern) if err != nil { return "", err } if len(matches) == 0 { return "", fmt.Errorf("subdir %q not found", subDir) } if len(matches) > 1 { return "", fmt.Errorf("subdir %q matches multiple paths", subDir) } return matches[0], nil }