Files
nostrlib/nip34/gitnaturalapi/refs.go
T

121 lines
2.5 KiB
Go

package gitnaturalapi
import (
"fmt"
"io"
"net/http"
"strconv"
"strings"
"sync"
)
type InfoRefsUploadPackResponse struct {
Refs map[string]string
Capabilities []string
Symrefs map[string]string
}
var capabilitiesCache sync.Map
func GetCapabilities(url string, existingInfo *InfoRefsUploadPackResponse) ([]string, error) {
if existingInfo != nil {
capabilitiesCache.Store(url, existingInfo.Capabilities)
return existingInfo.Capabilities, nil
}
if cached, ok := capabilitiesCache.Load(url); ok {
return cached.([]string), nil
}
info, err := GetInfoRefs(url)
if err != nil {
return nil, err
}
capabilitiesCache.Store(url, info.Capabilities)
return info.Capabilities, nil
}
func GetInfoRefs(url string) (*InfoRefsUploadPackResponse, error) {
resp, err := http.Get(url + "/info/refs?service=git-upload-pack")
if err != nil {
return nil, fmt.Errorf("failed to fetch info/refs: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read info/refs response: %w", err)
}
response := string(body)
result := &InfoRefsUploadPackResponse{
Refs: make(map[string]string),
Symrefs: make(map[string]string),
}
lines := strings.Split(response, "\n")
firstRef := true
for _, line := range lines {
if len(line) == 0 {
continue
}
if strings.HasPrefix(line, "0000") {
line = line[4:]
}
if len(line) < 4 {
continue
}
length, err := strconv.ParseInt(line[:4], 16, 32)
if err != nil {
continue
}
endIdx := int(length)
if endIdx > len(line) {
endIdx = len(line)
}
if endIdx <= 4 {
continue
}
content := line[4:endIdx]
if firstRef && strings.HasPrefix(content, "# service=") {
firstRef = false
continue
}
if !strings.Contains(content, " ") {
continue
}
parts := strings.SplitN(content, " ", 2)
hash := parts[0]
refAndCaps := parts[1]
if strings.Contains(refAndCaps, "\x00") {
nulParts := strings.SplitN(refAndCaps, "\x00", 2)
ref := strings.TrimSpace(nulParts[0])
result.Refs[ref] = hash
caps := strings.Fields(nulParts[1])
result.Capabilities = caps
for _, cap := range caps {
if strings.HasPrefix(cap, "symref=") {
symrefData := cap[7:]
colonIdx := strings.Index(symrefData, ":")
if colonIdx != -1 {
result.Symrefs[symrefData[:colonIdx]] = symrefData[colonIdx+1:]
}
}
}
} else {
result.Refs[strings.TrimSpace(refAndCaps)] = hash
}
}
return result, nil
}