Files
simplegit/common/repolayout.go
T
2026-07-17 05:55:31 -04:00

126 lines
2.7 KiB
Go

package common
import (
"errors"
"fmt"
"net/url"
"os"
"path/filepath"
"strings"
)
func Validate(name string) error {
if name == "" {
return errors.New("name cannot be empty")
}
if strings.HasPrefix(name, "/") || strings.Contains(name, "\\") {
return errors.New("name must be a relative unix-style path")
}
if !strings.HasSuffix(name, ".git") {
return errors.New("name must end with .git")
}
parts := strings.Split(name, "/")
for _, part := range parts {
if part == "" || part == "." || part == ".." {
return errors.New("name contains an invalid path segment")
}
}
return nil
}
func Resolve(root, name string) (string, error) {
if err := Validate(name); err != nil {
return "", err
}
full, err := filepath.Abs(filepath.Join(root, name))
if err != nil {
return "", err
}
absRoot, err := filepath.Abs(root)
if err != nil {
return "", err
}
if full != absRoot && !strings.HasPrefix(full, absRoot+string(os.PathSeparator)) {
return "", errors.New("repository path escapes root directory")
}
return full, nil
}
func SplitRepo(repo string) (owner, name string, err error) {
s := strings.TrimSuffix(repo, ".git")
i := strings.LastIndex(s, "/")
if i < 0 {
return "", "", errors.New("repository path must be owner/name.git")
}
return s[:i], s[i+1:], nil
}
func ParseDBURI(root, uri string) (driver, dsn string, uriNew string, err error) {
if uri == "" {
return "", "", "", fmt.Errorf("database URI is empty")
}
scheme, rest, ok := splitScheme(uri)
if !ok {
return "", "", "", fmt.Errorf("database URI %q has no scheme (want sqlite:// or postgres://)", uri)
}
switch scheme {
case "sqlite", "sqlite3":
path, query, _ := strings.Cut(strings.TrimPrefix(rest, "//"), "?")
if path == "" {
return "", "", "", fmt.Errorf("sqlite URI %q has no file path", uri)
}
uriNew := "sqlite3://" + filepath.Join(root, path)
return "sqlite3", sqliteDSN(filepath.Join(root, path), query), uriNew, nil
case "postgres", "postgresql":
return "postgres", uri, uri, nil
default:
return "", "", "", fmt.Errorf("unsupported database URI scheme %q (want sqlite:// or postgres://)", scheme)
}
}
func sqliteDSN(path, query string) string {
v, _ := url.ParseQuery(query)
if len(v["_pragma"]) == 0 {
v.Add("_pragma", "journal_mode(WAL)")
v.Add("_pragma", "busy_timeout(5000)")
v.Add("_pragma", "foreign_keys(ON)")
}
v.Add("_txlock", "immediate")
return "file:" + path + "?" + v.Encode()
}
func sqlitePath(dsn string) string {
s := strings.TrimPrefix(dsn, "file:")
if i := strings.Index(s, "?"); i >= 0 {
s = s[:i]
}
return s
}
func splitScheme(uri string) (scheme, rest string, ok bool) {
i := strings.Index(uri, ":")
if i < 0 {
return "", uri, false
}
return uri[:i], uri[i+1:], true
}