Files
grafana/apps/provisioning/pkg/safepath/path.go
Roberto Jiménez Sánchez 93a35fc7be Provisioning: Move apifmt, loki and safepath to provisioning app (#110226)
* Move apifmt

* Move safepath

* Move Loki package

* Regenerate Loki mock

* Missing file for Loki
2025-08-27 13:26:48 -05:00

80 lines
1.9 KiB
Go

package safepath
import (
"os"
"path"
"strings"
)
// TODO: explore if we want to use our own type for safepath
// to make it clearer that this is a safe path and not a regular path
// osSeparator is declared as a var here only to ensure we can change it in tests.
var osSeparator = os.PathSeparator
// Performs a [path.Clean] on the path, as well as replacing its OS separators.
//
// This replaces the OS separator with a slash.
// All OSes we target (Linux, macOS, and Windows) support forward-slashes in path traversals, as such it's simpler to use the same character everywhere.
// BSDs do as well (even though they're not a target as of writing).
//
// The output of a root path (i.e. absolute root or relative current dir) is always "" (empty string).
func Clean(p string) string {
if osSeparator != '/' {
p = strings.ReplaceAll(p, string(osSeparator), "/")
}
cleaned := path.Clean(p)
if cleaned == "." || cleaned == "/" {
return ""
}
return cleaned
}
// Join is like path.Join but preserves trailing slashes from the last element
func Join(elem ...string) string {
if len(elem) == 0 {
return ""
}
joined := path.Join(elem...)
// Preserve trailing slash if the last element had one
if strings.HasSuffix(elem[len(elem)-1], "/") {
return joined + "/"
}
return joined
}
// Base returns the last element of the path.
func Base(p string) string {
b := path.Base(p)
if b == "." || b == "/" {
return ""
}
return b
}
// RemoveExt returns the path without the extension.
// It should not remove the dot if the filename is e.g. `.gitignore`
func RemoveExt(p string) string {
// Special case: if the file starts with a dot and has no other dots,
// it's a hidden file and should not have its "extension" removed
base := Base(p)
if strings.HasPrefix(base, ".") && strings.Count(base, ".") == 1 {
return p
}
ext := path.Ext(p)
if ext == "" {
return p
}
return p[0 : len(p)-len(ext)]
}
func IsAbs(p string) bool {
return path.IsAbs(p)
}