feat(forge): add safe stringers for option types
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
b8bc948fc0
commit
bdf669db39
10 changed files with 348 additions and 0 deletions
22
admin.go
22
admin.go
|
|
@ -35,6 +35,20 @@ type AdminActionsRunListOptions struct {
|
|||
HeadSHA string
|
||||
}
|
||||
|
||||
// String returns a safe summary of the admin Actions run filters.
|
||||
func (o AdminActionsRunListOptions) String() string {
|
||||
return optionString("forge.AdminActionsRunListOptions",
|
||||
"event", o.Event,
|
||||
"branch", o.Branch,
|
||||
"status", o.Status,
|
||||
"actor", o.Actor,
|
||||
"head_sha", o.HeadSHA,
|
||||
)
|
||||
}
|
||||
|
||||
// GoString returns a safe Go-syntax summary of the admin Actions run filters.
|
||||
func (o AdminActionsRunListOptions) GoString() string { return o.String() }
|
||||
|
||||
func (o AdminActionsRunListOptions) queryParams() map[string]string {
|
||||
query := make(map[string]string, 5)
|
||||
if o.Event != "" {
|
||||
|
|
@ -67,6 +81,14 @@ type AdminUnadoptedListOptions struct {
|
|||
Pattern string
|
||||
}
|
||||
|
||||
// String returns a safe summary of the unadopted repository filters.
|
||||
func (o AdminUnadoptedListOptions) String() string {
|
||||
return optionString("forge.AdminUnadoptedListOptions", "pattern", o.Pattern)
|
||||
}
|
||||
|
||||
// GoString returns a safe Go-syntax summary of the unadopted repository filters.
|
||||
func (o AdminUnadoptedListOptions) GoString() string { return o.String() }
|
||||
|
||||
func (o AdminUnadoptedListOptions) queryParams() map[string]string {
|
||||
if o.Pattern == "" {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package forge
|
|||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestParams_String_Good(t *testing.T) {
|
||||
|
|
@ -79,3 +80,96 @@ func TestPagedResult_String_Good(t *testing.T) {
|
|||
t.Fatalf("got GoString=%q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOption_Stringers_Good(t *testing.T) {
|
||||
when := time.Date(2026, time.April, 2, 8, 3, 4, 0, time.UTC)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
got fmt.Stringer
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "AdminActionsRunListOptions",
|
||||
got: AdminActionsRunListOptions{Event: "push", Status: "success"},
|
||||
want: `forge.AdminActionsRunListOptions{event="push", status="success"}`,
|
||||
},
|
||||
{
|
||||
name: "AttachmentUploadOptions",
|
||||
got: AttachmentUploadOptions{Name: "screenshot.png", UpdatedAt: &when},
|
||||
want: `forge.AttachmentUploadOptions{name="screenshot.png", updated_at="2026-04-02T08:03:04Z"}`,
|
||||
},
|
||||
{
|
||||
name: "NotificationListOptions",
|
||||
got: NotificationListOptions{All: true, StatusTypes: []string{"unread"}, SubjectTypes: []string{"issue"}},
|
||||
want: `forge.NotificationListOptions{all=true, status_types=[]string{"unread"}, subject_types=[]string{"issue"}}`,
|
||||
},
|
||||
{
|
||||
name: "SearchIssuesOptions",
|
||||
got: SearchIssuesOptions{State: "open", PriorityRepoID: 99, Assigned: true, Query: "build"},
|
||||
want: `forge.SearchIssuesOptions{state="open", q="build", priority_repo_id=99, assigned=true}`,
|
||||
},
|
||||
{
|
||||
name: "ReleaseAttachmentUploadOptions",
|
||||
got: ReleaseAttachmentUploadOptions{Name: "release.zip"},
|
||||
want: `forge.ReleaseAttachmentUploadOptions{name="release.zip"}`,
|
||||
},
|
||||
{
|
||||
name: "UserSearchOptions",
|
||||
got: UserSearchOptions{UID: 1001},
|
||||
want: `forge.UserSearchOptions{uid=1001}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if got := tc.got.String(); got != tc.want {
|
||||
t.Fatalf("got String()=%q, want %q", got, tc.want)
|
||||
}
|
||||
if got := fmt.Sprint(tc.got); got != tc.want {
|
||||
t.Fatalf("got fmt.Sprint=%q, want %q", got, tc.want)
|
||||
}
|
||||
if got := fmt.Sprintf("%#v", tc.got); got != tc.want {
|
||||
t.Fatalf("got GoString=%q, want %q", got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOption_Stringers_Empty(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
got fmt.Stringer
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "AdminUnadoptedListOptions",
|
||||
got: AdminUnadoptedListOptions{},
|
||||
want: `forge.AdminUnadoptedListOptions{}`,
|
||||
},
|
||||
{
|
||||
name: "MilestoneListOptions",
|
||||
got: MilestoneListOptions{},
|
||||
want: `forge.MilestoneListOptions{}`,
|
||||
},
|
||||
{
|
||||
name: "UserKeyListOptions",
|
||||
got: UserKeyListOptions{},
|
||||
want: `forge.UserKeyListOptions{}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if got := tc.got.String(); got != tc.want {
|
||||
t.Fatalf("got String()=%q, want %q", got, tc.want)
|
||||
}
|
||||
if got := fmt.Sprint(tc.got); got != tc.want {
|
||||
t.Fatalf("got fmt.Sprint=%q, want %q", got, tc.want)
|
||||
}
|
||||
if got := fmt.Sprintf("%#v", tc.got); got != tc.want {
|
||||
t.Fatalf("got GoString=%q, want %q", got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
75
helpers.go
75
helpers.go
|
|
@ -1,7 +1,10 @@
|
|||
package forge
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
core "dappco.re/go/core"
|
||||
)
|
||||
|
|
@ -25,6 +28,78 @@ func pathParams(values ...string) Params {
|
|||
return params
|
||||
}
|
||||
|
||||
func optionString(typeName string, fields ...any) string {
|
||||
var b strings.Builder
|
||||
b.WriteString(typeName)
|
||||
b.WriteString("{")
|
||||
|
||||
wroteField := false
|
||||
for i := 0; i+1 < len(fields); i += 2 {
|
||||
name, _ := fields[i].(string)
|
||||
value := fields[i+1]
|
||||
if isZeroOptionValue(value) {
|
||||
continue
|
||||
}
|
||||
if wroteField {
|
||||
b.WriteString(", ")
|
||||
}
|
||||
wroteField = true
|
||||
b.WriteString(name)
|
||||
b.WriteString("=")
|
||||
b.WriteString(formatOptionValue(value))
|
||||
}
|
||||
|
||||
b.WriteString("}")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func isZeroOptionValue(v any) bool {
|
||||
switch x := v.(type) {
|
||||
case nil:
|
||||
return true
|
||||
case string:
|
||||
return x == ""
|
||||
case bool:
|
||||
return !x
|
||||
case int:
|
||||
return x == 0
|
||||
case int64:
|
||||
return x == 0
|
||||
case []string:
|
||||
return len(x) == 0
|
||||
case *time.Time:
|
||||
return x == nil
|
||||
case time.Time:
|
||||
return x.IsZero()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func formatOptionValue(v any) string {
|
||||
switch x := v.(type) {
|
||||
case string:
|
||||
return strconv.Quote(x)
|
||||
case bool:
|
||||
return strconv.FormatBool(x)
|
||||
case int:
|
||||
return strconv.Itoa(x)
|
||||
case int64:
|
||||
return strconv.FormatInt(x, 10)
|
||||
case []string:
|
||||
return fmt.Sprintf("%#v", x)
|
||||
case *time.Time:
|
||||
if x == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
return strconv.Quote(x.Format(time.RFC3339))
|
||||
case time.Time:
|
||||
return strconv.Quote(x.Format(time.RFC3339))
|
||||
default:
|
||||
return fmt.Sprintf("%#v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func lastIndexByte(s string, b byte) int {
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
if s[i] == b {
|
||||
|
|
|
|||
46
issues.go
46
issues.go
|
|
@ -31,6 +31,17 @@ type AttachmentUploadOptions struct {
|
|||
UpdatedAt *time.Time
|
||||
}
|
||||
|
||||
// String returns a safe summary of the attachment upload metadata.
|
||||
func (o AttachmentUploadOptions) String() string {
|
||||
return optionString("forge.AttachmentUploadOptions",
|
||||
"name", o.Name,
|
||||
"updated_at", o.UpdatedAt,
|
||||
)
|
||||
}
|
||||
|
||||
// GoString returns a safe Go-syntax summary of the attachment upload metadata.
|
||||
func (o AttachmentUploadOptions) GoString() string { return o.String() }
|
||||
|
||||
// RepoCommentListOptions controls filtering for repository-wide issue comment listings.
|
||||
//
|
||||
// Usage:
|
||||
|
|
@ -41,6 +52,17 @@ type RepoCommentListOptions struct {
|
|||
Before *time.Time
|
||||
}
|
||||
|
||||
// String returns a safe summary of the repository comment filters.
|
||||
func (o RepoCommentListOptions) String() string {
|
||||
return optionString("forge.RepoCommentListOptions",
|
||||
"since", o.Since,
|
||||
"before", o.Before,
|
||||
)
|
||||
}
|
||||
|
||||
// GoString returns a safe Go-syntax summary of the repository comment filters.
|
||||
func (o RepoCommentListOptions) GoString() string { return o.String() }
|
||||
|
||||
func (o RepoCommentListOptions) queryParams() map[string]string {
|
||||
query := make(map[string]string, 2)
|
||||
if o.Since != nil {
|
||||
|
|
@ -86,6 +108,30 @@ type SearchIssuesOptions struct {
|
|||
Team string
|
||||
}
|
||||
|
||||
// String returns a safe summary of the issue search filters.
|
||||
func (o SearchIssuesOptions) String() string {
|
||||
return optionString("forge.SearchIssuesOptions",
|
||||
"state", o.State,
|
||||
"labels", o.Labels,
|
||||
"milestones", o.Milestones,
|
||||
"q", o.Query,
|
||||
"priority_repo_id", o.PriorityRepoID,
|
||||
"type", o.Type,
|
||||
"since", o.Since,
|
||||
"before", o.Before,
|
||||
"assigned", o.Assigned,
|
||||
"created", o.Created,
|
||||
"mentioned", o.Mentioned,
|
||||
"review_requested", o.ReviewRequested,
|
||||
"reviewed", o.Reviewed,
|
||||
"owner", o.Owner,
|
||||
"team", o.Team,
|
||||
)
|
||||
}
|
||||
|
||||
// GoString returns a safe Go-syntax summary of the issue search filters.
|
||||
func (o SearchIssuesOptions) GoString() string { return o.String() }
|
||||
|
||||
func (o SearchIssuesOptions) queryParams() map[string]string {
|
||||
query := make(map[string]string, 12)
|
||||
if o.State != "" {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,14 @@ type MilestoneListOptions struct {
|
|||
Name string
|
||||
}
|
||||
|
||||
// String returns a safe summary of the milestone filters.
|
||||
func (o MilestoneListOptions) String() string {
|
||||
return optionString("forge.MilestoneListOptions", "state", o.State, "name", o.Name)
|
||||
}
|
||||
|
||||
// GoString returns a safe Go-syntax summary of the milestone filters.
|
||||
func (o MilestoneListOptions) GoString() string { return o.String() }
|
||||
|
||||
func (o MilestoneListOptions) queryParams() map[string]string {
|
||||
query := make(map[string]string, 2)
|
||||
if o.State != "" {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,20 @@ type NotificationListOptions struct {
|
|||
Before *time.Time
|
||||
}
|
||||
|
||||
// String returns a safe summary of the notification filters.
|
||||
func (o NotificationListOptions) String() string {
|
||||
return optionString("forge.NotificationListOptions",
|
||||
"all", o.All,
|
||||
"status_types", o.StatusTypes,
|
||||
"subject_types", o.SubjectTypes,
|
||||
"since", o.Since,
|
||||
"before", o.Before,
|
||||
)
|
||||
}
|
||||
|
||||
// GoString returns a safe Go-syntax summary of the notification filters.
|
||||
func (o NotificationListOptions) GoString() string { return o.String() }
|
||||
|
||||
func (o NotificationListOptions) addQuery(values url.Values) {
|
||||
if o.All {
|
||||
values.Set("all", "true")
|
||||
|
|
@ -69,6 +83,19 @@ type NotificationRepoMarkOptions struct {
|
|||
LastReadAt *time.Time
|
||||
}
|
||||
|
||||
// String returns a safe summary of the repository notification mark options.
|
||||
func (o NotificationRepoMarkOptions) String() string {
|
||||
return optionString("forge.NotificationRepoMarkOptions",
|
||||
"all", o.All,
|
||||
"status_types", o.StatusTypes,
|
||||
"to_status", o.ToStatus,
|
||||
"last_read_at", o.LastReadAt,
|
||||
)
|
||||
}
|
||||
|
||||
// GoString returns a safe Go-syntax summary of the repository notification mark options.
|
||||
func (o NotificationRepoMarkOptions) GoString() string { return o.String() }
|
||||
|
||||
// NotificationMarkOptions controls how authenticated-user notifications are marked.
|
||||
//
|
||||
// Usage:
|
||||
|
|
@ -81,6 +108,19 @@ type NotificationMarkOptions struct {
|
|||
LastReadAt *time.Time
|
||||
}
|
||||
|
||||
// String returns a safe summary of the authenticated-user notification mark options.
|
||||
func (o NotificationMarkOptions) String() string {
|
||||
return optionString("forge.NotificationMarkOptions",
|
||||
"all", o.All,
|
||||
"status_types", o.StatusTypes,
|
||||
"to_status", o.ToStatus,
|
||||
"last_read_at", o.LastReadAt,
|
||||
)
|
||||
}
|
||||
|
||||
// GoString returns a safe Go-syntax summary of the authenticated-user notification mark options.
|
||||
func (o NotificationMarkOptions) GoString() string { return o.String() }
|
||||
|
||||
func newNotificationService(c *Client) *NotificationService {
|
||||
return &NotificationService{client: c}
|
||||
}
|
||||
|
|
|
|||
8
orgs.go
8
orgs.go
|
|
@ -28,6 +28,14 @@ type OrgActivityFeedListOptions struct {
|
|||
Date *time.Time
|
||||
}
|
||||
|
||||
// String returns a safe summary of the organisation activity feed filters.
|
||||
func (o OrgActivityFeedListOptions) String() string {
|
||||
return optionString("forge.OrgActivityFeedListOptions", "date", o.Date)
|
||||
}
|
||||
|
||||
// GoString returns a safe Go-syntax summary of the organisation activity feed filters.
|
||||
func (o OrgActivityFeedListOptions) GoString() string { return o.String() }
|
||||
|
||||
func (o OrgActivityFeedListOptions) queryParams() map[string]string {
|
||||
if o.Date == nil {
|
||||
return nil
|
||||
|
|
|
|||
11
releases.go
11
releases.go
|
|
@ -29,6 +29,17 @@ type ReleaseAttachmentUploadOptions struct {
|
|||
ExternalURL string
|
||||
}
|
||||
|
||||
// String returns a safe summary of the release attachment upload metadata.
|
||||
func (o ReleaseAttachmentUploadOptions) String() string {
|
||||
return optionString("forge.ReleaseAttachmentUploadOptions",
|
||||
"name", o.Name,
|
||||
"external_url", o.ExternalURL,
|
||||
)
|
||||
}
|
||||
|
||||
// GoString returns a safe Go-syntax summary of the release attachment upload metadata.
|
||||
func (o ReleaseAttachmentUploadOptions) GoString() string { return o.String() }
|
||||
|
||||
func releaseAttachmentUploadQuery(opts *ReleaseAttachmentUploadOptions) map[string]string {
|
||||
if opts == nil || opts.Name == "" {
|
||||
return nil
|
||||
|
|
|
|||
28
repos.go
28
repos.go
|
|
@ -31,6 +31,14 @@ type RepoKeyListOptions struct {
|
|||
Fingerprint string
|
||||
}
|
||||
|
||||
// String returns a safe summary of the repository key filters.
|
||||
func (o RepoKeyListOptions) String() string {
|
||||
return optionString("forge.RepoKeyListOptions", "key_id", o.KeyID, "fingerprint", o.Fingerprint)
|
||||
}
|
||||
|
||||
// GoString returns a safe Go-syntax summary of the repository key filters.
|
||||
func (o RepoKeyListOptions) GoString() string { return o.String() }
|
||||
|
||||
func (o RepoKeyListOptions) queryParams() map[string]string {
|
||||
query := make(map[string]string, 2)
|
||||
if o.KeyID != 0 {
|
||||
|
|
@ -54,6 +62,14 @@ type ActivityFeedListOptions struct {
|
|||
Date *time.Time
|
||||
}
|
||||
|
||||
// String returns a safe summary of the activity feed filters.
|
||||
func (o ActivityFeedListOptions) String() string {
|
||||
return optionString("forge.ActivityFeedListOptions", "date", o.Date)
|
||||
}
|
||||
|
||||
// GoString returns a safe Go-syntax summary of the activity feed filters.
|
||||
func (o ActivityFeedListOptions) GoString() string { return o.String() }
|
||||
|
||||
func (o ActivityFeedListOptions) queryParams() map[string]string {
|
||||
if o.Date == nil {
|
||||
return nil
|
||||
|
|
@ -74,6 +90,18 @@ type RepoTimeListOptions struct {
|
|||
Before *time.Time
|
||||
}
|
||||
|
||||
// String returns a safe summary of the tracked time filters.
|
||||
func (o RepoTimeListOptions) String() string {
|
||||
return optionString("forge.RepoTimeListOptions",
|
||||
"user", o.User,
|
||||
"since", o.Since,
|
||||
"before", o.Before,
|
||||
)
|
||||
}
|
||||
|
||||
// GoString returns a safe Go-syntax summary of the tracked time filters.
|
||||
func (o RepoTimeListOptions) GoString() string { return o.String() }
|
||||
|
||||
func (o RepoTimeListOptions) queryParams() map[string]string {
|
||||
query := make(map[string]string, 3)
|
||||
if o.User != "" {
|
||||
|
|
|
|||
16
users.go
16
users.go
|
|
@ -29,6 +29,14 @@ type UserSearchOptions struct {
|
|||
UID int64
|
||||
}
|
||||
|
||||
// String returns a safe summary of the user search filters.
|
||||
func (o UserSearchOptions) String() string {
|
||||
return optionString("forge.UserSearchOptions", "uid", o.UID)
|
||||
}
|
||||
|
||||
// GoString returns a safe Go-syntax summary of the user search filters.
|
||||
func (o UserSearchOptions) GoString() string { return o.String() }
|
||||
|
||||
func (o UserSearchOptions) queryParams() map[string]string {
|
||||
if o.UID == 0 {
|
||||
return nil
|
||||
|
|
@ -47,6 +55,14 @@ type UserKeyListOptions struct {
|
|||
Fingerprint string
|
||||
}
|
||||
|
||||
// String returns a safe summary of the user key filters.
|
||||
func (o UserKeyListOptions) String() string {
|
||||
return optionString("forge.UserKeyListOptions", "fingerprint", o.Fingerprint)
|
||||
}
|
||||
|
||||
// GoString returns a safe Go-syntax summary of the user key filters.
|
||||
func (o UserKeyListOptions) GoString() string { return o.String() }
|
||||
|
||||
func (o UserKeyListOptions) queryParams() map[string]string {
|
||||
if o.Fingerprint == "" {
|
||||
return nil
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue