package agent import ( "fmt" "strconv" "strings" "time" ) // 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 } // ParseDurationFlag parses a duration flag value, accepting either a Go // duration string ("30s", "1h30m") or a bare integer read as seconds ("15"). // flag names the flag so the error says which value was rejected. func ParseDurationFlag(flag, value string) (time.Duration, error) { s := strings.TrimSpace(value) d, err := time.ParseDuration(s) if err != nil { n, nerr := strconv.Atoi(s) if nerr != nil { return 0, fmt.Errorf("invalid --%s value %q: want a duration such as 30s, 2m or 1h30m, or a bare number of seconds such as 15", flag, value) } d = time.Duration(n) * time.Second } if d <= 0 { return 0, fmt.Errorf("invalid --%s value %q: must be greater than zero", flag, value) } return d, 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 }