package agent import ( "fmt" "strconv" "strings" ) // PRRef identifies a single pull request by repository and number. type PRRef struct { Owner string Repo string Number int } // String renders the ref in canonical owner/repo#N form. func (r PRRef) String() string { return fmt.Sprintf("%s/%s#%d", r.Owner, r.Repo, r.Number) } // RepoPath returns the "owner/repo" portion used in Gitea API URLs. func (r PRRef) RepoPath() string { return r.Owner + "/" + r.Repo } // ParsePRRef parses "owner/repo#N" or "owner/repo:N" into a PRRef. func ParsePRRef(s string) (PRRef, error) { s = strings.TrimSpace(s) sep := strings.IndexAny(s, "#:") if sep < 0 { return PRRef{}, fmt.Errorf("invalid PR reference %q: expected owner/repo#N or owner/repo:N", s) } repoPart := s[:sep] numPart := s[sep+1:] owner, repo, ok := strings.Cut(repoPart, "/") if !ok || owner == "" || repo == "" { return PRRef{}, fmt.Errorf("invalid PR reference %q: repo must be owner/repo", s) } if strings.Contains(repo, "/") { return PRRef{}, fmt.Errorf("invalid PR reference %q: repo must be owner/repo", s) } n, err := strconv.Atoi(numPart) if err != nil || n <= 0 { return PRRef{}, fmt.Errorf("invalid PR reference %q: PR number must be a positive integer", s) } return PRRef{Owner: owner, Repo: repo, Number: n}, nil } // ParseRepo validates and splits an "owner/repo" string. func ParseRepo(s string) (owner, repo string, err error) { s = strings.TrimSpace(s) owner, repo, ok := strings.Cut(s, "/") if !ok || owner == "" || repo == "" || strings.Contains(repo, "/") { return "", "", fmt.Errorf("invalid repo %q: expected owner/repo", s) } return owner, repo, nil }