// Copyright 2021 The OPA Authors. All rights reserved. // Use of this source code is governed by an Apache2 // license that can be found in the LICENSE file. // Package strings contains helpers to perform string manipulation package strings import ( "path/filepath" "strings" "github.com/open-policy-agent/opa/internal/lcss" ) // TruncateFilePaths truncates the given file paths to conform to the given // "ideal" width and returns the shortened paths by replacing the middle parts of paths // with "...", ex: bundle1/.../a/b/policy.rego func TruncateFilePaths(maxIdealWidth, maxWidth int, path ...string) (map[string]string, int) { canShorten := make([][]byte, 0, len(path)) for _, p := range path { canShorten = append(canShorten, []byte(getPathFromFirstSeparator(p))) } // Find the longest common path segment var lcs string if len(canShorten) > 1 { lcs = string(lcss.LongestCommonSubstring(canShorten...)) } else { lcs = string(canShorten[0]) } // Don't just swap in the full LCSS, trim it down to be the least amount of // characters to reach our "ideal" width boundary giving as much // detail as possible without going too long. diff := maxIdealWidth - (maxWidth - len(lcs) + 3) if diff > 0 { if diff > len(lcs) { lcs = "" } else { // Favor data on the right hand side of the path lcs = lcs[:len(lcs)-diff] } } result := map[string]string{} for _, p := range path { result[p] = p } longestLocation := maxWidth // Swap in "..." for the longest common path, but if it makes things better if len(lcs) > 3 { for path := range result { result[path] = strings.Replace(path, lcs, "...", 1) } // Drop the overall length down to match our substitution longestLocation -= (len(lcs) - 3) } return result, longestLocation } func Truncate(str string, maxWidth int) string { if len(str) <= maxWidth { return str } return str[:maxWidth-3] + "..." } func getPathFromFirstSeparator(path string) string { s := filepath.Dir(path) s = strings.TrimPrefix(s, string(filepath.Separator)) firstSlash := strings.IndexRune(s, filepath.Separator) if firstSlash > 0 { return s[firstSlash+1:] } return s }