diff --git a/actions.go b/actions.go index ba110b8..c1bbb65 100644 --- a/actions.go +++ b/actions.go @@ -2,9 +2,9 @@ package forge import ( "context" - "fmt" "iter" + core "dappco.re/go/core" "dappco.re/go/core/forge/types" ) @@ -21,82 +21,82 @@ func newActionsService(c *Client) *ActionsService { // ListRepoSecrets returns all secrets for a repository. func (s *ActionsService) ListRepoSecrets(ctx context.Context, owner, repo string) ([]types.Secret, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/actions/secrets", owner, repo) + path := core.Sprintf("/api/v1/repos/%s/%s/actions/secrets", owner, repo) return ListAll[types.Secret](ctx, s.client, path, nil) } // IterRepoSecrets returns an iterator over all secrets for a repository. func (s *ActionsService) IterRepoSecrets(ctx context.Context, owner, repo string) iter.Seq2[types.Secret, error] { - path := fmt.Sprintf("/api/v1/repos/%s/%s/actions/secrets", owner, repo) + path := core.Sprintf("/api/v1/repos/%s/%s/actions/secrets", owner, repo) return ListIter[types.Secret](ctx, s.client, path, nil) } // CreateRepoSecret creates or updates a secret in a repository. // Forgejo expects a PUT with {"data": "secret-value"} body. func (s *ActionsService) CreateRepoSecret(ctx context.Context, owner, repo, name string, data string) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/actions/secrets/%s", owner, repo, name) + path := core.Sprintf("/api/v1/repos/%s/%s/actions/secrets/%s", owner, repo, name) body := map[string]string{"data": data} return s.client.Put(ctx, path, body, nil) } // DeleteRepoSecret removes a secret from a repository. func (s *ActionsService) DeleteRepoSecret(ctx context.Context, owner, repo, name string) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/actions/secrets/%s", owner, repo, name) + path := core.Sprintf("/api/v1/repos/%s/%s/actions/secrets/%s", owner, repo, name) return s.client.Delete(ctx, path) } // ListRepoVariables returns all action variables for a repository. func (s *ActionsService) ListRepoVariables(ctx context.Context, owner, repo string) ([]types.ActionVariable, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/actions/variables", owner, repo) + path := core.Sprintf("/api/v1/repos/%s/%s/actions/variables", owner, repo) return ListAll[types.ActionVariable](ctx, s.client, path, nil) } // IterRepoVariables returns an iterator over all action variables for a repository. func (s *ActionsService) IterRepoVariables(ctx context.Context, owner, repo string) iter.Seq2[types.ActionVariable, error] { - path := fmt.Sprintf("/api/v1/repos/%s/%s/actions/variables", owner, repo) + path := core.Sprintf("/api/v1/repos/%s/%s/actions/variables", owner, repo) return ListIter[types.ActionVariable](ctx, s.client, path, nil) } // CreateRepoVariable creates a new action variable in a repository. // Forgejo expects a POST with {"value": "var-value"} body. func (s *ActionsService) CreateRepoVariable(ctx context.Context, owner, repo, name, value string) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/actions/variables/%s", owner, repo, name) + path := core.Sprintf("/api/v1/repos/%s/%s/actions/variables/%s", owner, repo, name) body := types.CreateVariableOption{Value: value} return s.client.Post(ctx, path, body, nil) } // DeleteRepoVariable removes an action variable from a repository. func (s *ActionsService) DeleteRepoVariable(ctx context.Context, owner, repo, name string) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/actions/variables/%s", owner, repo, name) + path := core.Sprintf("/api/v1/repos/%s/%s/actions/variables/%s", owner, repo, name) return s.client.Delete(ctx, path) } // ListOrgSecrets returns all secrets for an organisation. func (s *ActionsService) ListOrgSecrets(ctx context.Context, org string) ([]types.Secret, error) { - path := fmt.Sprintf("/api/v1/orgs/%s/actions/secrets", org) + path := core.Sprintf("/api/v1/orgs/%s/actions/secrets", org) return ListAll[types.Secret](ctx, s.client, path, nil) } // IterOrgSecrets returns an iterator over all secrets for an organisation. func (s *ActionsService) IterOrgSecrets(ctx context.Context, org string) iter.Seq2[types.Secret, error] { - path := fmt.Sprintf("/api/v1/orgs/%s/actions/secrets", org) + path := core.Sprintf("/api/v1/orgs/%s/actions/secrets", org) return ListIter[types.Secret](ctx, s.client, path, nil) } // ListOrgVariables returns all action variables for an organisation. func (s *ActionsService) ListOrgVariables(ctx context.Context, org string) ([]types.ActionVariable, error) { - path := fmt.Sprintf("/api/v1/orgs/%s/actions/variables", org) + path := core.Sprintf("/api/v1/orgs/%s/actions/variables", org) return ListAll[types.ActionVariable](ctx, s.client, path, nil) } // IterOrgVariables returns an iterator over all action variables for an organisation. func (s *ActionsService) IterOrgVariables(ctx context.Context, org string) iter.Seq2[types.ActionVariable, error] { - path := fmt.Sprintf("/api/v1/orgs/%s/actions/variables", org) + path := core.Sprintf("/api/v1/orgs/%s/actions/variables", org) return ListIter[types.ActionVariable](ctx, s.client, path, nil) } // DispatchWorkflow triggers a workflow run. func (s *ActionsService) DispatchWorkflow(ctx context.Context, owner, repo, workflow string, opts map[string]any) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/actions/workflows/%s/dispatches", owner, repo, workflow) + path := core.Sprintf("/api/v1/repos/%s/%s/actions/workflows/%s/dispatches", owner, repo, workflow) return s.client.Post(ctx, path, opts, nil) } diff --git a/branches.go b/branches.go index 66af9a4..010b5e8 100644 --- a/branches.go +++ b/branches.go @@ -2,9 +2,9 @@ package forge import ( "context" - "fmt" "iter" + core "dappco.re/go/core" "dappco.re/go/core/forge/types" ) @@ -23,19 +23,19 @@ func newBranchService(c *Client) *BranchService { // ListBranchProtections returns all branch protections for a repository. func (s *BranchService) ListBranchProtections(ctx context.Context, owner, repo string) ([]types.BranchProtection, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/branch_protections", owner, repo) + path := core.Sprintf("/api/v1/repos/%s/%s/branch_protections", owner, repo) return ListAll[types.BranchProtection](ctx, s.client, path, nil) } // IterBranchProtections returns an iterator over all branch protections for a repository. func (s *BranchService) IterBranchProtections(ctx context.Context, owner, repo string) iter.Seq2[types.BranchProtection, error] { - path := fmt.Sprintf("/api/v1/repos/%s/%s/branch_protections", owner, repo) + path := core.Sprintf("/api/v1/repos/%s/%s/branch_protections", owner, repo) return ListIter[types.BranchProtection](ctx, s.client, path, nil) } // GetBranchProtection returns a single branch protection by name. func (s *BranchService) GetBranchProtection(ctx context.Context, owner, repo, name string) (*types.BranchProtection, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/branch_protections/%s", owner, repo, name) + path := core.Sprintf("/api/v1/repos/%s/%s/branch_protections/%s", owner, repo, name) var out types.BranchProtection if err := s.client.Get(ctx, path, &out); err != nil { return nil, err @@ -45,7 +45,7 @@ func (s *BranchService) GetBranchProtection(ctx context.Context, owner, repo, na // CreateBranchProtection creates a new branch protection rule. func (s *BranchService) CreateBranchProtection(ctx context.Context, owner, repo string, opts *types.CreateBranchProtectionOption) (*types.BranchProtection, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/branch_protections", owner, repo) + path := core.Sprintf("/api/v1/repos/%s/%s/branch_protections", owner, repo) var out types.BranchProtection if err := s.client.Post(ctx, path, opts, &out); err != nil { return nil, err @@ -55,7 +55,7 @@ func (s *BranchService) CreateBranchProtection(ctx context.Context, owner, repo // EditBranchProtection updates an existing branch protection rule. func (s *BranchService) EditBranchProtection(ctx context.Context, owner, repo, name string, opts *types.EditBranchProtectionOption) (*types.BranchProtection, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/branch_protections/%s", owner, repo, name) + path := core.Sprintf("/api/v1/repos/%s/%s/branch_protections/%s", owner, repo, name) var out types.BranchProtection if err := s.client.Patch(ctx, path, opts, &out); err != nil { return nil, err @@ -65,6 +65,6 @@ func (s *BranchService) EditBranchProtection(ctx context.Context, owner, repo, n // DeleteBranchProtection deletes a branch protection rule. func (s *BranchService) DeleteBranchProtection(ctx context.Context, owner, repo, name string) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/branch_protections/%s", owner, repo, name) + path := core.Sprintf("/api/v1/repos/%s/%s/branch_protections/%s", owner, repo, name) return s.client.Delete(ctx, path) } diff --git a/client.go b/client.go index cb10233..6a56f6d 100644 --- a/client.go +++ b/client.go @@ -4,13 +4,11 @@ import ( "bytes" "context" "encoding/json" - "errors" - "fmt" "io" "net/http" "strconv" - "strings" + core "dappco.re/go/core" coreerr "dappco.re/go/core/log" ) @@ -22,25 +20,25 @@ type APIError struct { } func (e *APIError) Error() string { - return fmt.Sprintf("forge: %s %d: %s", e.URL, e.StatusCode, e.Message) + return core.Sprintf("forge: %s %d: %s", e.URL, e.StatusCode, e.Message) } // IsNotFound returns true if the error is a 404 response. func IsNotFound(err error) bool { var apiErr *APIError - return errors.As(err, &apiErr) && apiErr.StatusCode == http.StatusNotFound + return core.As(err, &apiErr) && apiErr.StatusCode == http.StatusNotFound } // IsForbidden returns true if the error is a 403 response. func IsForbidden(err error) bool { var apiErr *APIError - return errors.As(err, &apiErr) && apiErr.StatusCode == http.StatusForbidden + return core.As(err, &apiErr) && apiErr.StatusCode == http.StatusForbidden } // IsConflict returns true if the error is a 409 response. func IsConflict(err error) bool { var apiErr *APIError - return errors.As(err, &apiErr) && apiErr.StatusCode == http.StatusConflict + return core.As(err, &apiErr) && apiErr.StatusCode == http.StatusConflict } // Option configures the Client. @@ -80,7 +78,7 @@ func (c *Client) RateLimit() RateLimit { // NewClient creates a new Forgejo API client. func NewClient(url, token string, opts ...Option) *Client { c := &Client{ - baseURL: strings.TrimRight(url, "/"), + baseURL: core.TrimSuffix(url, "/"), token: token, httpClient: &http.Client{ CheckRedirect: func(req *http.Request, via []*http.Request) error { @@ -139,11 +137,11 @@ func (c *Client) PostRaw(ctx context.Context, path string, body any) ([]byte, er var bodyReader io.Reader if body != nil { - data, err := json.Marshal(body) - if err != nil { - return nil, coreerr.E("Client.PostRaw", "forge: marshal body", err) + r := core.JSONMarshal(body) + if !r.OK { + return nil, coreerr.E("Client.PostRaw", "forge: marshal body", nil) } - bodyReader = bytes.NewReader(data) + bodyReader = bytes.NewReader(r.Value.([]byte)) } req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bodyReader) @@ -218,11 +216,11 @@ func (c *Client) doJSON(ctx context.Context, method, path string, body, out any) var bodyReader io.Reader if body != nil { - data, err := json.Marshal(body) - if err != nil { - return nil, coreerr.E("Client.doJSON", "forge: marshal body", err) + r := core.JSONMarshal(body) + if !r.OK { + return nil, coreerr.E("Client.doJSON", "forge: marshal body", nil) } - bodyReader = bytes.NewReader(data) + bodyReader = bytes.NewReader(r.Value.([]byte)) } req, err := http.NewRequestWithContext(ctx, method, url, bodyReader) @@ -267,7 +265,7 @@ func (c *Client) parseError(resp *http.Response, path string) error { // Read a bit of the body to see if we can get a message data, _ := io.ReadAll(io.LimitReader(resp.Body, 1024)) - _ = json.Unmarshal(data, &errBody) + core.JSONUnmarshal(data, &errBody) msg := errBody.Message if msg == "" && len(data) > 0 { diff --git a/client_test.go b/client_test.go index 1c67351..fb7707a 100644 --- a/client_test.go +++ b/client_test.go @@ -3,10 +3,11 @@ package forge import ( "context" "encoding/json" - "errors" "net/http" "net/http/httptest" "testing" + + core "dappco.re/go/core" ) func TestClient_Good_Get(t *testing.T) { @@ -91,7 +92,7 @@ func TestClient_Bad_ServerError(t *testing.T) { t.Fatal("expected error") } var apiErr *APIError - if !errors.As(err, &apiErr) { + if !core.As(err, &apiErr) { t.Fatalf("expected APIError, got %T", err) } if apiErr.StatusCode != 500 { diff --git a/cmd/forgegen/generator.go b/cmd/forgegen/generator.go index aea2da5..08fc51e 100644 --- a/cmd/forgegen/generator.go +++ b/cmd/forgegen/generator.go @@ -3,11 +3,11 @@ package main import ( "bytes" "maps" - "path/filepath" "slices" "strings" "text/template" + core "dappco.re/go/core" coreio "dappco.re/go/core/io" coreerr "dappco.re/go/core/log" ) @@ -110,7 +110,7 @@ func classifyType(name string) string { bestKey := "" bestGroup := "" for key, group := range typeGrouping { - if strings.HasPrefix(name, key) && len(key) > len(bestKey) { + if core.HasPrefix(name, key) && len(key) > len(bestKey) { bestKey = key bestGroup = group } @@ -122,10 +122,10 @@ func classifyType(name string) string { // Strip CRUD prefixes and Option suffix, then retry. base := name for _, prefix := range []string{"Create", "Edit", "Delete", "Update", "Add", "Submit", "Replace", "Set", "Transfer"} { - base = strings.TrimPrefix(base, prefix) + base = core.TrimPrefix(base, prefix) } - base = strings.TrimSuffix(base, "Option") - base = strings.TrimSuffix(base, "Options") + base = core.TrimSuffix(base, "Option") + base = core.TrimSuffix(base, "Options") if base != name && base != "" { if group, ok := typeGrouping[base]; ok { @@ -135,7 +135,7 @@ func classifyType(name string) string { bestKey = "" bestGroup = "" for key, group := range typeGrouping { - if strings.HasPrefix(base, key) && len(key) > len(bestKey) { + if core.HasPrefix(base, key) && len(key) > len(bestKey) { bestKey = key bestGroup = group } @@ -151,7 +151,7 @@ func classifyType(name string) string { // sanitiseLine collapses a multi-line string into a single line, // replacing newlines and consecutive whitespace with a single space. func sanitiseLine(s string) string { - return strings.Join(strings.Fields(s), " ") + return core.Join(" ", strings.Fields(s)...) } // enumConstName generates a Go constant name for an enum value. @@ -219,7 +219,13 @@ func Generate(types map[string]*GoType, pairs []CRUDPair, outDir string) error { // Sort types within each group for deterministic output. for file := range groups { slices.SortFunc(groups[file], func(a, b *GoType) int { - return strings.Compare(a.Name, b.Name) + if a.Name < b.Name { + return -1 + } + if a.Name > b.Name { + return 1 + } + return 0 }) } @@ -228,7 +234,7 @@ func Generate(types map[string]*GoType, pairs []CRUDPair, outDir string) error { slices.Sort(fileNames) for _, file := range fileNames { - outPath := filepath.Join(outDir, file+".go") + outPath := core.JoinPath(outDir, file+".go") if err := writeFile(outPath, groups[file]); err != nil { return coreerr.E("Generate", "write "+outPath, err) } @@ -241,7 +247,7 @@ func Generate(types map[string]*GoType, pairs []CRUDPair, outDir string) error { func writeFile(path string, types []*GoType) error { needTime := slices.ContainsFunc(types, func(gt *GoType) bool { return slices.ContainsFunc(gt.Fields, func(f GoField) bool { - return strings.Contains(f.GoType, "time.Time") + return core.Contains(f.GoType, "time.Time") }) }) diff --git a/cmd/forgegen/generator_test.go b/cmd/forgegen/generator_test.go index 9f60e45..3d63a60 100644 --- a/cmd/forgegen/generator_test.go +++ b/cmd/forgegen/generator_test.go @@ -2,10 +2,9 @@ package main import ( "os" - "path/filepath" - "strings" "testing" + core "dappco.re/go/core" coreio "dappco.re/go/core/io" ) @@ -26,7 +25,7 @@ func TestGenerate_Good_CreatesFiles(t *testing.T) { entries, _ := os.ReadDir(outDir) goFiles := 0 for _, e := range entries { - if strings.HasSuffix(e.Name(), ".go") { + if core.HasSuffix(e.Name(), ".go") { goFiles++ } } @@ -52,8 +51,8 @@ func TestGenerate_Good_ValidGoSyntax(t *testing.T) { entries, _ := os.ReadDir(outDir) var content string for _, e := range entries { - if strings.HasSuffix(e.Name(), ".go") { - content, err = coreio.Local.Read(filepath.Join(outDir, e.Name())) + if core.HasSuffix(e.Name(), ".go") { + content, err = coreio.Local.Read(core.JoinPath(outDir, e.Name())) if err == nil { break } @@ -62,10 +61,10 @@ func TestGenerate_Good_ValidGoSyntax(t *testing.T) { if err != nil || content == "" { t.Fatal("could not read any generated file") } - if !strings.Contains(content, "package types") { + if !core.Contains(content, "package types") { t.Error("missing package declaration") } - if !strings.Contains(content, "// Code generated") { + if !core.Contains(content, "// Code generated") { t.Error("missing generated comment") } } @@ -87,8 +86,8 @@ func TestGenerate_Good_RepositoryType(t *testing.T) { var content string entries, _ := os.ReadDir(outDir) for _, e := range entries { - data, _ := coreio.Local.Read(filepath.Join(outDir, e.Name())) - if strings.Contains(data, "type Repository struct") { + data, _ := coreio.Local.Read(core.JoinPath(outDir, e.Name())) + if core.Contains(data, "type Repository struct") { content = data break } @@ -107,7 +106,7 @@ func TestGenerate_Good_RepositoryType(t *testing.T) { "`json:\"private,omitempty\"`", } for _, check := range checks { - if !strings.Contains(content, check) { + if !core.Contains(content, check) { t.Errorf("missing field with tag %s", check) } } @@ -129,8 +128,8 @@ func TestGenerate_Good_TimeImport(t *testing.T) { entries, _ := os.ReadDir(outDir) for _, e := range entries { - content, _ := coreio.Local.Read(filepath.Join(outDir, e.Name())) - if strings.Contains(content, "time.Time") && !strings.Contains(content, "\"time\"") { + content, _ := coreio.Local.Read(core.JoinPath(outDir, e.Name())) + if core.Contains(content, "time.Time") && !core.Contains(content, "\"time\"") { t.Errorf("file %s uses time.Time but doesn't import time", e.Name()) } } diff --git a/cmd/forgegen/main.go b/cmd/forgegen/main.go index 856331d..b34c874 100644 --- a/cmd/forgegen/main.go +++ b/cmd/forgegen/main.go @@ -2,8 +2,9 @@ package main import ( "flag" - "fmt" "os" + + core "dappco.re/go/core" ) func main() { @@ -13,18 +14,18 @@ func main() { spec, err := LoadSpec(*specPath) if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) + core.Print(os.Stderr, "Error: %v\n", err) os.Exit(1) } types := ExtractTypes(spec) pairs := DetectCRUDPairs(spec) - fmt.Printf("Loaded %d types, %d CRUD pairs\n", len(types), len(pairs)) - fmt.Printf("Output dir: %s\n", *outDir) + core.Print(os.Stdout, "Loaded %d types, %d CRUD pairs\n", len(types), len(pairs)) + core.Print(os.Stdout, "Output dir: %s\n", *outDir) if err := Generate(types, pairs, *outDir); err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) + core.Print(os.Stderr, "Error: %v\n", err) os.Exit(1) } } diff --git a/cmd/forgegen/parser.go b/cmd/forgegen/parser.go index f32bb8e..d56de7e 100644 --- a/cmd/forgegen/parser.go +++ b/cmd/forgegen/parser.go @@ -1,11 +1,10 @@ package main import ( - "encoding/json" - "fmt" "slices" "strings" + core "dappco.re/go/core" coreio "dappco.re/go/core/io" coreerr "dappco.re/go/core/log" ) @@ -77,8 +76,9 @@ func LoadSpec(path string) (*Spec, error) { return nil, coreerr.E("LoadSpec", "read spec", err) } var spec Spec - if err := json.Unmarshal([]byte(content), &spec); err != nil { - return nil, coreerr.E("LoadSpec", "parse spec", err) + r := core.JSONUnmarshal([]byte(content), &spec) + if !r.OK { + return nil, coreerr.E("LoadSpec", "parse spec", nil) } return &spec, nil } @@ -91,7 +91,7 @@ func ExtractTypes(spec *Spec) map[string]*GoType { if len(def.Enum) > 0 { gt.IsEnum = true for _, v := range def.Enum { - gt.EnumValues = append(gt.EnumValues, fmt.Sprintf("%v", v)) + gt.EnumValues = append(gt.EnumValues, core.Sprintf("%v", v)) } slices.Sort(gt.EnumValues) result[name] = gt @@ -116,7 +116,13 @@ func ExtractTypes(spec *Spec) map[string]*GoType { gt.Fields = append(gt.Fields, gf) } slices.SortFunc(gt.Fields, func(a, b GoField) int { - return strings.Compare(a.GoName, b.GoName) + if a.GoName < b.GoName { + return -1 + } + if a.GoName > b.GoName { + return 1 + } + return 0 }) result[name] = gt } @@ -128,11 +134,11 @@ func ExtractTypes(spec *Spec) map[string]*GoType { func DetectCRUDPairs(spec *Spec) []CRUDPair { var pairs []CRUDPair for name := range spec.Definitions { - if !strings.HasPrefix(name, "Create") || !strings.HasSuffix(name, "Option") { + if !core.HasPrefix(name, "Create") || !core.HasSuffix(name, "Option") { continue } - inner := strings.TrimPrefix(name, "Create") - inner = strings.TrimSuffix(inner, "Option") + inner := core.TrimPrefix(name, "Create") + inner = core.TrimSuffix(inner, "Option") editName := "Edit" + inner + "Option" pair := CRUDPair{Base: inner, Create: name} if _, ok := spec.Definitions[editName]; ok { @@ -141,7 +147,13 @@ func DetectCRUDPairs(spec *Spec) []CRUDPair { pairs = append(pairs, pair) } slices.SortFunc(pairs, func(a, b CRUDPair) int { - return strings.Compare(a.Base, b.Base) + if a.Base < b.Base { + return -1 + } + if a.Base > b.Base { + return 1 + } + return 0 }) return pairs } @@ -149,7 +161,7 @@ func DetectCRUDPairs(spec *Spec) []CRUDPair { // resolveGoType maps a swagger schema property to a Go type string. func resolveGoType(prop SchemaProperty) string { if prop.Ref != "" { - parts := strings.Split(prop.Ref, "/") + parts := core.Split(prop.Ref, "/") return "*" + parts[len(parts)-1] } switch prop.Type { @@ -202,13 +214,13 @@ func pascalCase(s string) string { if len(p) == 0 { continue } - upper := strings.ToUpper(p) + upper := core.Upper(p) switch upper { case "ID", "URL", "HTML", "SSH", "HTTP", "HTTPS", "API", "URI", "GPG", "IP", "CSS", "JS": parts = append(parts, upper) default: - parts = append(parts, strings.ToUpper(p[:1])+p[1:]) + parts = append(parts, core.Upper(p[:1])+p[1:]) } } - return strings.Join(parts, "") + return core.Join("", parts...) } diff --git a/commits.go b/commits.go index 27b501e..f6540b9 100644 --- a/commits.go +++ b/commits.go @@ -2,9 +2,9 @@ package forge import ( "context" - "fmt" "iter" + core "dappco.re/go/core" "dappco.re/go/core/forge/types" ) @@ -51,7 +51,7 @@ func (s *CommitService) Get(ctx context.Context, params Params) (*types.Commit, // GetCombinedStatus returns the combined status for a given ref (branch, tag, or SHA). func (s *CommitService) GetCombinedStatus(ctx context.Context, owner, repo, ref string) (*types.CombinedStatus, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/statuses/%s", owner, repo, ref) + path := core.Sprintf("/api/v1/repos/%s/%s/statuses/%s", owner, repo, ref) var out types.CombinedStatus if err := s.client.Get(ctx, path, &out); err != nil { return nil, err @@ -61,7 +61,7 @@ func (s *CommitService) GetCombinedStatus(ctx context.Context, owner, repo, ref // ListStatuses returns all commit statuses for a given ref. func (s *CommitService) ListStatuses(ctx context.Context, owner, repo, ref string) ([]types.CommitStatus, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/commits/%s/statuses", owner, repo, ref) + path := core.Sprintf("/api/v1/repos/%s/%s/commits/%s/statuses", owner, repo, ref) var out []types.CommitStatus if err := s.client.Get(ctx, path, &out); err != nil { return nil, err @@ -71,7 +71,7 @@ func (s *CommitService) ListStatuses(ctx context.Context, owner, repo, ref strin // CreateStatus creates a new commit status for the given SHA. func (s *CommitService) CreateStatus(ctx context.Context, owner, repo, sha string, opts *types.CreateStatusOption) (*types.CommitStatus, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/statuses/%s", owner, repo, sha) + path := core.Sprintf("/api/v1/repos/%s/%s/statuses/%s", owner, repo, sha) var out types.CommitStatus if err := s.client.Post(ctx, path, opts, &out); err != nil { return nil, err @@ -81,7 +81,7 @@ func (s *CommitService) CreateStatus(ctx context.Context, owner, repo, sha strin // GetNote returns the git note for a given commit SHA. func (s *CommitService) GetNote(ctx context.Context, owner, repo, sha string) (*types.Note, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/git/notes/%s", owner, repo, sha) + path := core.Sprintf("/api/v1/repos/%s/%s/git/notes/%s", owner, repo, sha) var out types.Note if err := s.client.Get(ctx, path, &out); err != nil { return nil, err diff --git a/contents.go b/contents.go index 8a6f48e..a4b3775 100644 --- a/contents.go +++ b/contents.go @@ -2,8 +2,8 @@ package forge import ( "context" - "fmt" + core "dappco.re/go/core" "dappco.re/go/core/forge/types" ) @@ -19,7 +19,7 @@ func newContentService(c *Client) *ContentService { // GetFile returns metadata and content for a file in a repository. func (s *ContentService) GetFile(ctx context.Context, owner, repo, filepath string) (*types.ContentsResponse, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", owner, repo, filepath) + path := core.Sprintf("/api/v1/repos/%s/%s/contents/%s", owner, repo, filepath) var out types.ContentsResponse if err := s.client.Get(ctx, path, &out); err != nil { return nil, err @@ -29,7 +29,7 @@ func (s *ContentService) GetFile(ctx context.Context, owner, repo, filepath stri // CreateFile creates a new file in a repository. func (s *ContentService) CreateFile(ctx context.Context, owner, repo, filepath string, opts *types.CreateFileOptions) (*types.FileResponse, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", owner, repo, filepath) + path := core.Sprintf("/api/v1/repos/%s/%s/contents/%s", owner, repo, filepath) var out types.FileResponse if err := s.client.Post(ctx, path, opts, &out); err != nil { return nil, err @@ -39,7 +39,7 @@ func (s *ContentService) CreateFile(ctx context.Context, owner, repo, filepath s // UpdateFile updates an existing file in a repository. func (s *ContentService) UpdateFile(ctx context.Context, owner, repo, filepath string, opts *types.UpdateFileOptions) (*types.FileResponse, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", owner, repo, filepath) + path := core.Sprintf("/api/v1/repos/%s/%s/contents/%s", owner, repo, filepath) var out types.FileResponse if err := s.client.Put(ctx, path, opts, &out); err != nil { return nil, err @@ -49,12 +49,12 @@ func (s *ContentService) UpdateFile(ctx context.Context, owner, repo, filepath s // DeleteFile deletes a file from a repository. Uses DELETE with a JSON body. func (s *ContentService) DeleteFile(ctx context.Context, owner, repo, filepath string, opts *types.DeleteFileOptions) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", owner, repo, filepath) + path := core.Sprintf("/api/v1/repos/%s/%s/contents/%s", owner, repo, filepath) return s.client.DeleteWithBody(ctx, path, opts) } // GetRawFile returns the raw file content as bytes. func (s *ContentService) GetRawFile(ctx context.Context, owner, repo, filepath string) ([]byte, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/raw/%s", owner, repo, filepath) + path := core.Sprintf("/api/v1/repos/%s/%s/raw/%s", owner, repo, filepath) return s.client.GetRaw(ctx, path) } diff --git a/go.mod b/go.mod index f973b82..bf0625a 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module dappco.re/go/core/forge go 1.26.0 require ( + dappco.re/go/core v0.8.0-alpha.1 dappco.re/go/core/io v0.2.0 dappco.re/go/core/log v0.1.0 ) diff --git a/go.sum b/go.sum index 76d01ec..5a2e71c 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +dappco.re/go/core v0.8.0-alpha.1 h1:gj7+Scv+L63Z7wMxbJYHhaRFkHJo2u4MMPuUSv/Dhtk= +dappco.re/go/core v0.8.0-alpha.1/go.mod h1:f2/tBZ3+3IqDrg2F5F598llv0nmb/4gJVCFzM5geE4A= dappco.re/go/core/io v0.2.0 h1:zuudgIiTsQQ5ipVt97saWdGLROovbEB/zdVyy9/l+I4= dappco.re/go/core/io v0.2.0/go.mod h1:1QnQV6X9LNgFKfm8SkOtR9LLaj3bDcsOIeJOOyjbL5E= dappco.re/go/core/log v0.1.0 h1:pa71Vq2TD2aoEUQWFKwNcaJ3GBY8HbaNGqtE688Unyc= diff --git a/issues.go b/issues.go index a6ab01d..8779495 100644 --- a/issues.go +++ b/issues.go @@ -2,9 +2,9 @@ package forge import ( "context" - "fmt" "iter" + core "dappco.re/go/core" "dappco.re/go/core/forge/types" ) @@ -23,77 +23,77 @@ func newIssueService(c *Client) *IssueService { // Pin pins an issue. func (s *IssueService) Pin(ctx context.Context, owner, repo string, index int64) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/pin", owner, repo, index) + path := core.Sprintf("/api/v1/repos/%s/%s/issues/%d/pin", owner, repo, index) return s.client.Post(ctx, path, nil, nil) } // Unpin unpins an issue. func (s *IssueService) Unpin(ctx context.Context, owner, repo string, index int64) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/pin", owner, repo, index) + path := core.Sprintf("/api/v1/repos/%s/%s/issues/%d/pin", owner, repo, index) return s.client.Delete(ctx, path) } // SetDeadline sets or updates the deadline on an issue. func (s *IssueService) SetDeadline(ctx context.Context, owner, repo string, index int64, deadline string) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/deadline", owner, repo, index) + path := core.Sprintf("/api/v1/repos/%s/%s/issues/%d/deadline", owner, repo, index) body := map[string]string{"due_date": deadline} return s.client.Post(ctx, path, body, nil) } // AddReaction adds a reaction to an issue. func (s *IssueService) AddReaction(ctx context.Context, owner, repo string, index int64, reaction string) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/reactions", owner, repo, index) + path := core.Sprintf("/api/v1/repos/%s/%s/issues/%d/reactions", owner, repo, index) body := map[string]string{"content": reaction} return s.client.Post(ctx, path, body, nil) } // DeleteReaction removes a reaction from an issue. func (s *IssueService) DeleteReaction(ctx context.Context, owner, repo string, index int64, reaction string) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/reactions", owner, repo, index) + path := core.Sprintf("/api/v1/repos/%s/%s/issues/%d/reactions", owner, repo, index) body := map[string]string{"content": reaction} return s.client.DeleteWithBody(ctx, path, body) } // StartStopwatch starts the stopwatch on an issue. func (s *IssueService) StartStopwatch(ctx context.Context, owner, repo string, index int64) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/stopwatch/start", owner, repo, index) + path := core.Sprintf("/api/v1/repos/%s/%s/issues/%d/stopwatch/start", owner, repo, index) return s.client.Post(ctx, path, nil, nil) } // StopStopwatch stops the stopwatch on an issue. func (s *IssueService) StopStopwatch(ctx context.Context, owner, repo string, index int64) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/stopwatch/stop", owner, repo, index) + path := core.Sprintf("/api/v1/repos/%s/%s/issues/%d/stopwatch/stop", owner, repo, index) return s.client.Post(ctx, path, nil, nil) } // AddLabels adds labels to an issue. func (s *IssueService) AddLabels(ctx context.Context, owner, repo string, index int64, labelIDs []int64) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels", owner, repo, index) + path := core.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels", owner, repo, index) body := types.IssueLabelsOption{Labels: toAnySlice(labelIDs)} return s.client.Post(ctx, path, body, nil) } // RemoveLabel removes a single label from an issue. func (s *IssueService) RemoveLabel(ctx context.Context, owner, repo string, index int64, labelID int64) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels/%d", owner, repo, index, labelID) + path := core.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels/%d", owner, repo, index, labelID) return s.client.Delete(ctx, path) } // ListComments returns all comments on an issue. func (s *IssueService) ListComments(ctx context.Context, owner, repo string, index int64) ([]types.Comment, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/comments", owner, repo, index) + path := core.Sprintf("/api/v1/repos/%s/%s/issues/%d/comments", owner, repo, index) return ListAll[types.Comment](ctx, s.client, path, nil) } // IterComments returns an iterator over all comments on an issue. func (s *IssueService) IterComments(ctx context.Context, owner, repo string, index int64) iter.Seq2[types.Comment, error] { - path := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/comments", owner, repo, index) + path := core.Sprintf("/api/v1/repos/%s/%s/issues/%d/comments", owner, repo, index) return ListIter[types.Comment](ctx, s.client, path, nil) } // CreateComment creates a comment on an issue. func (s *IssueService) CreateComment(ctx context.Context, owner, repo string, index int64, body string) (*types.Comment, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/comments", owner, repo, index) + path := core.Sprintf("/api/v1/repos/%s/%s/issues/%d/comments", owner, repo, index) opts := types.CreateIssueCommentOption{Body: body} var out types.Comment if err := s.client.Post(ctx, path, opts, &out); err != nil { diff --git a/labels.go b/labels.go index acb1146..d4efe7f 100644 --- a/labels.go +++ b/labels.go @@ -2,9 +2,9 @@ package forge import ( "context" - "fmt" "iter" + core "dappco.re/go/core" "dappco.re/go/core/forge/types" ) @@ -20,19 +20,19 @@ func newLabelService(c *Client) *LabelService { // ListRepoLabels returns all labels for a repository. func (s *LabelService) ListRepoLabels(ctx context.Context, owner, repo string) ([]types.Label, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/labels", owner, repo) + path := core.Sprintf("/api/v1/repos/%s/%s/labels", owner, repo) return ListAll[types.Label](ctx, s.client, path, nil) } // IterRepoLabels returns an iterator over all labels for a repository. func (s *LabelService) IterRepoLabels(ctx context.Context, owner, repo string) iter.Seq2[types.Label, error] { - path := fmt.Sprintf("/api/v1/repos/%s/%s/labels", owner, repo) + path := core.Sprintf("/api/v1/repos/%s/%s/labels", owner, repo) return ListIter[types.Label](ctx, s.client, path, nil) } // GetRepoLabel returns a single label by ID. func (s *LabelService) GetRepoLabel(ctx context.Context, owner, repo string, id int64) (*types.Label, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/labels/%d", owner, repo, id) + path := core.Sprintf("/api/v1/repos/%s/%s/labels/%d", owner, repo, id) var out types.Label if err := s.client.Get(ctx, path, &out); err != nil { return nil, err @@ -42,7 +42,7 @@ func (s *LabelService) GetRepoLabel(ctx context.Context, owner, repo string, id // CreateRepoLabel creates a new label in a repository. func (s *LabelService) CreateRepoLabel(ctx context.Context, owner, repo string, opts *types.CreateLabelOption) (*types.Label, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/labels", owner, repo) + path := core.Sprintf("/api/v1/repos/%s/%s/labels", owner, repo) var out types.Label if err := s.client.Post(ctx, path, opts, &out); err != nil { return nil, err @@ -52,7 +52,7 @@ func (s *LabelService) CreateRepoLabel(ctx context.Context, owner, repo string, // EditRepoLabel updates an existing label in a repository. func (s *LabelService) EditRepoLabel(ctx context.Context, owner, repo string, id int64, opts *types.EditLabelOption) (*types.Label, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/labels/%d", owner, repo, id) + path := core.Sprintf("/api/v1/repos/%s/%s/labels/%d", owner, repo, id) var out types.Label if err := s.client.Patch(ctx, path, opts, &out); err != nil { return nil, err @@ -62,25 +62,25 @@ func (s *LabelService) EditRepoLabel(ctx context.Context, owner, repo string, id // DeleteRepoLabel deletes a label from a repository. func (s *LabelService) DeleteRepoLabel(ctx context.Context, owner, repo string, id int64) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/labels/%d", owner, repo, id) + path := core.Sprintf("/api/v1/repos/%s/%s/labels/%d", owner, repo, id) return s.client.Delete(ctx, path) } // ListOrgLabels returns all labels for an organisation. func (s *LabelService) ListOrgLabels(ctx context.Context, org string) ([]types.Label, error) { - path := fmt.Sprintf("/api/v1/orgs/%s/labels", org) + path := core.Sprintf("/api/v1/orgs/%s/labels", org) return ListAll[types.Label](ctx, s.client, path, nil) } // IterOrgLabels returns an iterator over all labels for an organisation. func (s *LabelService) IterOrgLabels(ctx context.Context, org string) iter.Seq2[types.Label, error] { - path := fmt.Sprintf("/api/v1/orgs/%s/labels", org) + path := core.Sprintf("/api/v1/orgs/%s/labels", org) return ListIter[types.Label](ctx, s.client, path, nil) } // CreateOrgLabel creates a new label in an organisation. func (s *LabelService) CreateOrgLabel(ctx context.Context, org string, opts *types.CreateLabelOption) (*types.Label, error) { - path := fmt.Sprintf("/api/v1/orgs/%s/labels", org) + path := core.Sprintf("/api/v1/orgs/%s/labels", org) var out types.Label if err := s.client.Post(ctx, path, opts, &out); err != nil { return nil, err diff --git a/milestones.go b/milestones.go index fa2dfd8..767cb2f 100644 --- a/milestones.go +++ b/milestones.go @@ -2,8 +2,8 @@ package forge import ( "context" - "fmt" + core "dappco.re/go/core" "dappco.re/go/core/forge/types" ) @@ -18,13 +18,13 @@ func newMilestoneService(c *Client) *MilestoneService { // ListAll returns all milestones for a repository. func (s *MilestoneService) ListAll(ctx context.Context, params Params) ([]types.Milestone, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/milestones", params["owner"], params["repo"]) + path := core.Sprintf("/api/v1/repos/%s/%s/milestones", params["owner"], params["repo"]) return ListAll[types.Milestone](ctx, s.client, path, nil) } // Get returns a single milestone by ID. func (s *MilestoneService) Get(ctx context.Context, owner, repo string, id int64) (*types.Milestone, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/milestones/%d", owner, repo, id) + path := core.Sprintf("/api/v1/repos/%s/%s/milestones/%d", owner, repo, id) var out types.Milestone if err := s.client.Get(ctx, path, &out); err != nil { return nil, err @@ -34,7 +34,7 @@ func (s *MilestoneService) Get(ctx context.Context, owner, repo string, id int64 // Create creates a new milestone. func (s *MilestoneService) Create(ctx context.Context, owner, repo string, opts *types.CreateMilestoneOption) (*types.Milestone, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/milestones", owner, repo) + path := core.Sprintf("/api/v1/repos/%s/%s/milestones", owner, repo) var out types.Milestone if err := s.client.Post(ctx, path, opts, &out); err != nil { return nil, err diff --git a/misc.go b/misc.go index b05526a..70a1770 100644 --- a/misc.go +++ b/misc.go @@ -2,8 +2,8 @@ package forge import ( "context" - "fmt" + core "dappco.re/go/core" "dappco.re/go/core/forge/types" ) @@ -41,7 +41,7 @@ func (s *MiscService) ListLicenses(ctx context.Context) ([]types.LicensesTemplat // GetLicense returns a single licence template by name. func (s *MiscService) GetLicense(ctx context.Context, name string) (*types.LicenseTemplateInfo, error) { - path := fmt.Sprintf("/api/v1/licenses/%s", name) + path := core.Sprintf("/api/v1/licenses/%s", name) var out types.LicenseTemplateInfo if err := s.client.Get(ctx, path, &out); err != nil { return nil, err @@ -60,7 +60,7 @@ func (s *MiscService) ListGitignoreTemplates(ctx context.Context) ([]string, err // GetGitignoreTemplate returns a single gitignore template by name. func (s *MiscService) GetGitignoreTemplate(ctx context.Context, name string) (*types.GitignoreTemplateInfo, error) { - path := fmt.Sprintf("/api/v1/gitignore/templates/%s", name) + path := core.Sprintf("/api/v1/gitignore/templates/%s", name) var out types.GitignoreTemplateInfo if err := s.client.Get(ctx, path, &out); err != nil { return nil, err diff --git a/notifications.go b/notifications.go index e3b8af2..465fc3b 100644 --- a/notifications.go +++ b/notifications.go @@ -2,9 +2,9 @@ package forge import ( "context" - "fmt" "iter" + core "dappco.re/go/core" "dappco.re/go/core/forge/types" ) @@ -30,13 +30,13 @@ func (s *NotificationService) Iter(ctx context.Context) iter.Seq2[types.Notifica // ListRepo returns all notifications for a specific repository. func (s *NotificationService) ListRepo(ctx context.Context, owner, repo string) ([]types.NotificationThread, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/notifications", owner, repo) + path := core.Sprintf("/api/v1/repos/%s/%s/notifications", owner, repo) return ListAll[types.NotificationThread](ctx, s.client, path, nil) } // IterRepo returns an iterator over all notifications for a specific repository. func (s *NotificationService) IterRepo(ctx context.Context, owner, repo string) iter.Seq2[types.NotificationThread, error] { - path := fmt.Sprintf("/api/v1/repos/%s/%s/notifications", owner, repo) + path := core.Sprintf("/api/v1/repos/%s/%s/notifications", owner, repo) return ListIter[types.NotificationThread](ctx, s.client, path, nil) } @@ -47,7 +47,7 @@ func (s *NotificationService) MarkRead(ctx context.Context) error { // GetThread returns a single notification thread by ID. func (s *NotificationService) GetThread(ctx context.Context, id int64) (*types.NotificationThread, error) { - path := fmt.Sprintf("/api/v1/notifications/threads/%d", id) + path := core.Sprintf("/api/v1/notifications/threads/%d", id) var out types.NotificationThread if err := s.client.Get(ctx, path, &out); err != nil { return nil, err @@ -57,6 +57,6 @@ func (s *NotificationService) GetThread(ctx context.Context, id int64) (*types.N // MarkThreadRead marks a single notification thread as read. func (s *NotificationService) MarkThreadRead(ctx context.Context, id int64) error { - path := fmt.Sprintf("/api/v1/notifications/threads/%d", id) + path := core.Sprintf("/api/v1/notifications/threads/%d", id) return s.client.Patch(ctx, path, nil, nil) } diff --git a/orgs.go b/orgs.go index 36d4dc2..18fa762 100644 --- a/orgs.go +++ b/orgs.go @@ -2,9 +2,9 @@ package forge import ( "context" - "fmt" "iter" + core "dappco.re/go/core" "dappco.re/go/core/forge/types" ) @@ -23,37 +23,37 @@ func newOrgService(c *Client) *OrgService { // ListMembers returns all members of an organisation. func (s *OrgService) ListMembers(ctx context.Context, org string) ([]types.User, error) { - path := fmt.Sprintf("/api/v1/orgs/%s/members", org) + path := core.Sprintf("/api/v1/orgs/%s/members", org) return ListAll[types.User](ctx, s.client, path, nil) } // IterMembers returns an iterator over all members of an organisation. func (s *OrgService) IterMembers(ctx context.Context, org string) iter.Seq2[types.User, error] { - path := fmt.Sprintf("/api/v1/orgs/%s/members", org) + path := core.Sprintf("/api/v1/orgs/%s/members", org) return ListIter[types.User](ctx, s.client, path, nil) } // AddMember adds a user to an organisation. func (s *OrgService) AddMember(ctx context.Context, org, username string) error { - path := fmt.Sprintf("/api/v1/orgs/%s/members/%s", org, username) + path := core.Sprintf("/api/v1/orgs/%s/members/%s", org, username) return s.client.Put(ctx, path, nil, nil) } // RemoveMember removes a user from an organisation. func (s *OrgService) RemoveMember(ctx context.Context, org, username string) error { - path := fmt.Sprintf("/api/v1/orgs/%s/members/%s", org, username) + path := core.Sprintf("/api/v1/orgs/%s/members/%s", org, username) return s.client.Delete(ctx, path) } // ListUserOrgs returns all organisations for a user. func (s *OrgService) ListUserOrgs(ctx context.Context, username string) ([]types.Organization, error) { - path := fmt.Sprintf("/api/v1/users/%s/orgs", username) + path := core.Sprintf("/api/v1/users/%s/orgs", username) return ListAll[types.Organization](ctx, s.client, path, nil) } // IterUserOrgs returns an iterator over all organisations for a user. func (s *OrgService) IterUserOrgs(ctx context.Context, username string) iter.Seq2[types.Organization, error] { - path := fmt.Sprintf("/api/v1/users/%s/orgs", username) + path := core.Sprintf("/api/v1/users/%s/orgs", username) return ListIter[types.Organization](ctx, s.client, path, nil) } diff --git a/packages.go b/packages.go index ee724ea..bade2e3 100644 --- a/packages.go +++ b/packages.go @@ -2,9 +2,9 @@ package forge import ( "context" - "fmt" "iter" + core "dappco.re/go/core" "dappco.re/go/core/forge/types" ) @@ -20,19 +20,19 @@ func newPackageService(c *Client) *PackageService { // List returns all packages for a given owner. func (s *PackageService) List(ctx context.Context, owner string) ([]types.Package, error) { - path := fmt.Sprintf("/api/v1/packages/%s", owner) + path := core.Sprintf("/api/v1/packages/%s", owner) return ListAll[types.Package](ctx, s.client, path, nil) } // Iter returns an iterator over all packages for a given owner. func (s *PackageService) Iter(ctx context.Context, owner string) iter.Seq2[types.Package, error] { - path := fmt.Sprintf("/api/v1/packages/%s", owner) + path := core.Sprintf("/api/v1/packages/%s", owner) return ListIter[types.Package](ctx, s.client, path, nil) } // Get returns a single package by owner, type, name, and version. func (s *PackageService) Get(ctx context.Context, owner, pkgType, name, version string) (*types.Package, error) { - path := fmt.Sprintf("/api/v1/packages/%s/%s/%s/%s", owner, pkgType, name, version) + path := core.Sprintf("/api/v1/packages/%s/%s/%s/%s", owner, pkgType, name, version) var out types.Package if err := s.client.Get(ctx, path, &out); err != nil { return nil, err @@ -42,18 +42,18 @@ func (s *PackageService) Get(ctx context.Context, owner, pkgType, name, version // Delete removes a package by owner, type, name, and version. func (s *PackageService) Delete(ctx context.Context, owner, pkgType, name, version string) error { - path := fmt.Sprintf("/api/v1/packages/%s/%s/%s/%s", owner, pkgType, name, version) + path := core.Sprintf("/api/v1/packages/%s/%s/%s/%s", owner, pkgType, name, version) return s.client.Delete(ctx, path) } // ListFiles returns all files for a specific package version. func (s *PackageService) ListFiles(ctx context.Context, owner, pkgType, name, version string) ([]types.PackageFile, error) { - path := fmt.Sprintf("/api/v1/packages/%s/%s/%s/%s/files", owner, pkgType, name, version) + path := core.Sprintf("/api/v1/packages/%s/%s/%s/%s/files", owner, pkgType, name, version) return ListAll[types.PackageFile](ctx, s.client, path, nil) } // IterFiles returns an iterator over all files for a specific package version. func (s *PackageService) IterFiles(ctx context.Context, owner, pkgType, name, version string) iter.Seq2[types.PackageFile, error] { - path := fmt.Sprintf("/api/v1/packages/%s/%s/%s/%s/files", owner, pkgType, name, version) + path := core.Sprintf("/api/v1/packages/%s/%s/%s/%s/files", owner, pkgType, name, version) return ListIter[types.PackageFile](ctx, s.client, path, nil) } diff --git a/params.go b/params.go index ce20aaf..a6306d5 100644 --- a/params.go +++ b/params.go @@ -2,7 +2,8 @@ package forge import ( "net/url" - "strings" + + core "dappco.re/go/core" ) // Params maps path variable names to values. @@ -12,7 +13,7 @@ type Params map[string]string // ResolvePath substitutes {placeholders} in path with values from params. func ResolvePath(path string, params Params) string { for k, v := range params { - path = strings.ReplaceAll(path, "{"+k+"}", url.PathEscape(v)) + path = core.Replace(path, "{"+k+"}", url.PathEscape(v)) } return path } diff --git a/pulls.go b/pulls.go index 408f438..3530884 100644 --- a/pulls.go +++ b/pulls.go @@ -2,9 +2,9 @@ package forge import ( "context" - "fmt" "iter" + core "dappco.re/go/core" "dappco.re/go/core/forge/types" ) @@ -23,32 +23,32 @@ func newPullService(c *Client) *PullService { // Merge merges a pull request. Method is one of "merge", "rebase", "rebase-merge", "squash", "fast-forward-only", "manually-merged". func (s *PullService) Merge(ctx context.Context, owner, repo string, index int64, method string) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge", owner, repo, index) + path := core.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge", owner, repo, index) body := map[string]string{"Do": method} return s.client.Post(ctx, path, body, nil) } // Update updates a pull request branch with the base branch. func (s *PullService) Update(ctx context.Context, owner, repo string, index int64) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/update", owner, repo, index) + path := core.Sprintf("/api/v1/repos/%s/%s/pulls/%d/update", owner, repo, index) return s.client.Post(ctx, path, nil, nil) } // ListReviews returns all reviews on a pull request. func (s *PullService) ListReviews(ctx context.Context, owner, repo string, index int64) ([]types.PullReview, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", owner, repo, index) + path := core.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", owner, repo, index) return ListAll[types.PullReview](ctx, s.client, path, nil) } // IterReviews returns an iterator over all reviews on a pull request. func (s *PullService) IterReviews(ctx context.Context, owner, repo string, index int64) iter.Seq2[types.PullReview, error] { - path := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", owner, repo, index) + path := core.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", owner, repo, index) return ListIter[types.PullReview](ctx, s.client, path, nil) } // SubmitReview creates a new review on a pull request. func (s *PullService) SubmitReview(ctx context.Context, owner, repo string, index int64, review map[string]any) (*types.PullReview, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", owner, repo, index) + path := core.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", owner, repo, index) var out types.PullReview if err := s.client.Post(ctx, path, review, &out); err != nil { return nil, err @@ -58,13 +58,13 @@ func (s *PullService) SubmitReview(ctx context.Context, owner, repo string, inde // DismissReview dismisses a pull request review. func (s *PullService) DismissReview(ctx context.Context, owner, repo string, index, reviewID int64, msg string) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews/%d/dismissals", owner, repo, index, reviewID) + path := core.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews/%d/dismissals", owner, repo, index, reviewID) body := map[string]string{"message": msg} return s.client.Post(ctx, path, body, nil) } // UndismissReview undismisses a pull request review. func (s *PullService) UndismissReview(ctx context.Context, owner, repo string, index, reviewID int64) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews/%d/undismissals", owner, repo, index, reviewID) + path := core.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews/%d/undismissals", owner, repo, index, reviewID) return s.client.Post(ctx, path, nil, nil) } diff --git a/releases.go b/releases.go index ae32d49..3e8a227 100644 --- a/releases.go +++ b/releases.go @@ -2,9 +2,9 @@ package forge import ( "context" - "fmt" "iter" + core "dappco.re/go/core" "dappco.re/go/core/forge/types" ) @@ -23,7 +23,7 @@ func newReleaseService(c *Client) *ReleaseService { // GetByTag returns a release by its tag name. func (s *ReleaseService) GetByTag(ctx context.Context, owner, repo, tag string) (*types.Release, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s", owner, repo, tag) + path := core.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s", owner, repo, tag) var out types.Release if err := s.client.Get(ctx, path, &out); err != nil { return nil, err @@ -33,25 +33,25 @@ func (s *ReleaseService) GetByTag(ctx context.Context, owner, repo, tag string) // DeleteByTag deletes a release by its tag name. func (s *ReleaseService) DeleteByTag(ctx context.Context, owner, repo, tag string) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s", owner, repo, tag) + path := core.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s", owner, repo, tag) return s.client.Delete(ctx, path) } // ListAssets returns all assets for a release. func (s *ReleaseService) ListAssets(ctx context.Context, owner, repo string, releaseID int64) ([]types.Attachment, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/releases/%d/assets", owner, repo, releaseID) + path := core.Sprintf("/api/v1/repos/%s/%s/releases/%d/assets", owner, repo, releaseID) return ListAll[types.Attachment](ctx, s.client, path, nil) } // IterAssets returns an iterator over all assets for a release. func (s *ReleaseService) IterAssets(ctx context.Context, owner, repo string, releaseID int64) iter.Seq2[types.Attachment, error] { - path := fmt.Sprintf("/api/v1/repos/%s/%s/releases/%d/assets", owner, repo, releaseID) + path := core.Sprintf("/api/v1/repos/%s/%s/releases/%d/assets", owner, repo, releaseID) return ListIter[types.Attachment](ctx, s.client, path, nil) } // GetAsset returns a single asset for a release. func (s *ReleaseService) GetAsset(ctx context.Context, owner, repo string, releaseID, assetID int64) (*types.Attachment, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/releases/%d/assets/%d", owner, repo, releaseID, assetID) + path := core.Sprintf("/api/v1/repos/%s/%s/releases/%d/assets/%d", owner, repo, releaseID, assetID) var out types.Attachment if err := s.client.Get(ctx, path, &out); err != nil { return nil, err @@ -61,6 +61,6 @@ func (s *ReleaseService) GetAsset(ctx context.Context, owner, repo string, relea // DeleteAsset deletes a single asset from a release. func (s *ReleaseService) DeleteAsset(ctx context.Context, owner, repo string, releaseID, assetID int64) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/releases/%d/assets/%d", owner, repo, releaseID, assetID) + path := core.Sprintf("/api/v1/repos/%s/%s/releases/%d/assets/%d", owner, repo, releaseID, assetID) return s.client.Delete(ctx, path) } diff --git a/resource.go b/resource.go index 3ee5d45..cfd68b4 100644 --- a/resource.go +++ b/resource.go @@ -3,7 +3,8 @@ package forge import ( "context" "iter" - "strings" + + core "dappco.re/go/core" ) // Resource provides generic CRUD operations for a Forgejo API resource. @@ -21,10 +22,11 @@ func NewResource[T any, C any, U any](c *Client, path string) *Resource[T, C, U] collection := path // Strip last segment if it's a pure placeholder like /{index} // Don't strip if mixed like /repos or /{org}/repos - if i := strings.LastIndex(path, "/"); i >= 0 { - lastSeg := path[i+1:] - if strings.HasPrefix(lastSeg, "{") && strings.HasSuffix(lastSeg, "}") { - collection = path[:i] + parts := core.Split(path, "/") + if len(parts) > 0 { + lastSeg := parts[len(parts)-1] + if core.HasPrefix(lastSeg, "{") && core.HasSuffix(lastSeg, "}") { + collection = path[:len(path)-len(lastSeg)-1] } } return &Resource[T, C, U]{client: c, path: path, collection: collection} diff --git a/teams.go b/teams.go index 32470ec..b1e1bf3 100644 --- a/teams.go +++ b/teams.go @@ -2,9 +2,9 @@ package forge import ( "context" - "fmt" "iter" + core "dappco.re/go/core" "dappco.re/go/core/forge/types" ) @@ -23,60 +23,60 @@ func newTeamService(c *Client) *TeamService { // ListMembers returns all members of a team. func (s *TeamService) ListMembers(ctx context.Context, teamID int64) ([]types.User, error) { - path := fmt.Sprintf("/api/v1/teams/%d/members", teamID) + path := core.Sprintf("/api/v1/teams/%d/members", teamID) return ListAll[types.User](ctx, s.client, path, nil) } // IterMembers returns an iterator over all members of a team. func (s *TeamService) IterMembers(ctx context.Context, teamID int64) iter.Seq2[types.User, error] { - path := fmt.Sprintf("/api/v1/teams/%d/members", teamID) + path := core.Sprintf("/api/v1/teams/%d/members", teamID) return ListIter[types.User](ctx, s.client, path, nil) } // AddMember adds a user to a team. func (s *TeamService) AddMember(ctx context.Context, teamID int64, username string) error { - path := fmt.Sprintf("/api/v1/teams/%d/members/%s", teamID, username) + path := core.Sprintf("/api/v1/teams/%d/members/%s", teamID, username) return s.client.Put(ctx, path, nil, nil) } // RemoveMember removes a user from a team. func (s *TeamService) RemoveMember(ctx context.Context, teamID int64, username string) error { - path := fmt.Sprintf("/api/v1/teams/%d/members/%s", teamID, username) + path := core.Sprintf("/api/v1/teams/%d/members/%s", teamID, username) return s.client.Delete(ctx, path) } // ListRepos returns all repositories managed by a team. func (s *TeamService) ListRepos(ctx context.Context, teamID int64) ([]types.Repository, error) { - path := fmt.Sprintf("/api/v1/teams/%d/repos", teamID) + path := core.Sprintf("/api/v1/teams/%d/repos", teamID) return ListAll[types.Repository](ctx, s.client, path, nil) } // IterRepos returns an iterator over all repositories managed by a team. func (s *TeamService) IterRepos(ctx context.Context, teamID int64) iter.Seq2[types.Repository, error] { - path := fmt.Sprintf("/api/v1/teams/%d/repos", teamID) + path := core.Sprintf("/api/v1/teams/%d/repos", teamID) return ListIter[types.Repository](ctx, s.client, path, nil) } // AddRepo adds a repository to a team. func (s *TeamService) AddRepo(ctx context.Context, teamID int64, org, repo string) error { - path := fmt.Sprintf("/api/v1/teams/%d/repos/%s/%s", teamID, org, repo) + path := core.Sprintf("/api/v1/teams/%d/repos/%s/%s", teamID, org, repo) return s.client.Put(ctx, path, nil, nil) } // RemoveRepo removes a repository from a team. func (s *TeamService) RemoveRepo(ctx context.Context, teamID int64, org, repo string) error { - path := fmt.Sprintf("/api/v1/teams/%d/repos/%s/%s", teamID, org, repo) + path := core.Sprintf("/api/v1/teams/%d/repos/%s/%s", teamID, org, repo) return s.client.Delete(ctx, path) } // ListOrgTeams returns all teams in an organisation. func (s *TeamService) ListOrgTeams(ctx context.Context, org string) ([]types.Team, error) { - path := fmt.Sprintf("/api/v1/orgs/%s/teams", org) + path := core.Sprintf("/api/v1/orgs/%s/teams", org) return ListAll[types.Team](ctx, s.client, path, nil) } // IterOrgTeams returns an iterator over all teams in an organisation. func (s *TeamService) IterOrgTeams(ctx context.Context, org string) iter.Seq2[types.Team, error] { - path := fmt.Sprintf("/api/v1/orgs/%s/teams", org) + path := core.Sprintf("/api/v1/orgs/%s/teams", org) return ListIter[types.Team](ctx, s.client, path, nil) } diff --git a/users.go b/users.go index 2aba489..bc55a2a 100644 --- a/users.go +++ b/users.go @@ -2,9 +2,9 @@ package forge import ( "context" - "fmt" "iter" + core "dappco.re/go/core" "dappco.re/go/core/forge/types" ) @@ -32,60 +32,60 @@ func (s *UserService) GetCurrent(ctx context.Context) (*types.User, error) { // ListFollowers returns all followers of a user. func (s *UserService) ListFollowers(ctx context.Context, username string) ([]types.User, error) { - path := fmt.Sprintf("/api/v1/users/%s/followers", username) + path := core.Sprintf("/api/v1/users/%s/followers", username) return ListAll[types.User](ctx, s.client, path, nil) } // IterFollowers returns an iterator over all followers of a user. func (s *UserService) IterFollowers(ctx context.Context, username string) iter.Seq2[types.User, error] { - path := fmt.Sprintf("/api/v1/users/%s/followers", username) + path := core.Sprintf("/api/v1/users/%s/followers", username) return ListIter[types.User](ctx, s.client, path, nil) } // ListFollowing returns all users that a user is following. func (s *UserService) ListFollowing(ctx context.Context, username string) ([]types.User, error) { - path := fmt.Sprintf("/api/v1/users/%s/following", username) + path := core.Sprintf("/api/v1/users/%s/following", username) return ListAll[types.User](ctx, s.client, path, nil) } // IterFollowing returns an iterator over all users that a user is following. func (s *UserService) IterFollowing(ctx context.Context, username string) iter.Seq2[types.User, error] { - path := fmt.Sprintf("/api/v1/users/%s/following", username) + path := core.Sprintf("/api/v1/users/%s/following", username) return ListIter[types.User](ctx, s.client, path, nil) } // Follow follows a user as the authenticated user. func (s *UserService) Follow(ctx context.Context, username string) error { - path := fmt.Sprintf("/api/v1/user/following/%s", username) + path := core.Sprintf("/api/v1/user/following/%s", username) return s.client.Put(ctx, path, nil, nil) } // Unfollow unfollows a user as the authenticated user. func (s *UserService) Unfollow(ctx context.Context, username string) error { - path := fmt.Sprintf("/api/v1/user/following/%s", username) + path := core.Sprintf("/api/v1/user/following/%s", username) return s.client.Delete(ctx, path) } // ListStarred returns all repositories starred by a user. func (s *UserService) ListStarred(ctx context.Context, username string) ([]types.Repository, error) { - path := fmt.Sprintf("/api/v1/users/%s/starred", username) + path := core.Sprintf("/api/v1/users/%s/starred", username) return ListAll[types.Repository](ctx, s.client, path, nil) } // IterStarred returns an iterator over all repositories starred by a user. func (s *UserService) IterStarred(ctx context.Context, username string) iter.Seq2[types.Repository, error] { - path := fmt.Sprintf("/api/v1/users/%s/starred", username) + path := core.Sprintf("/api/v1/users/%s/starred", username) return ListIter[types.Repository](ctx, s.client, path, nil) } // Star stars a repository as the authenticated user. func (s *UserService) Star(ctx context.Context, owner, repo string) error { - path := fmt.Sprintf("/api/v1/user/starred/%s/%s", owner, repo) + path := core.Sprintf("/api/v1/user/starred/%s/%s", owner, repo) return s.client.Put(ctx, path, nil, nil) } // Unstar unstars a repository as the authenticated user. func (s *UserService) Unstar(ctx context.Context, owner, repo string) error { - path := fmt.Sprintf("/api/v1/user/starred/%s/%s", owner, repo) + path := core.Sprintf("/api/v1/user/starred/%s/%s", owner, repo) return s.client.Delete(ctx, path) } diff --git a/webhooks.go b/webhooks.go index b814b28..eaa51d0 100644 --- a/webhooks.go +++ b/webhooks.go @@ -2,9 +2,9 @@ package forge import ( "context" - "fmt" "iter" + core "dappco.re/go/core" "dappco.re/go/core/forge/types" ) @@ -24,18 +24,18 @@ func newWebhookService(c *Client) *WebhookService { // TestHook triggers a test delivery for a webhook. func (s *WebhookService) TestHook(ctx context.Context, owner, repo string, id int64) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/hooks/%d/tests", owner, repo, id) + path := core.Sprintf("/api/v1/repos/%s/%s/hooks/%d/tests", owner, repo, id) return s.client.Post(ctx, path, nil, nil) } // ListOrgHooks returns all webhooks for an organisation. func (s *WebhookService) ListOrgHooks(ctx context.Context, org string) ([]types.Hook, error) { - path := fmt.Sprintf("/api/v1/orgs/%s/hooks", org) + path := core.Sprintf("/api/v1/orgs/%s/hooks", org) return ListAll[types.Hook](ctx, s.client, path, nil) } // IterOrgHooks returns an iterator over all webhooks for an organisation. func (s *WebhookService) IterOrgHooks(ctx context.Context, org string) iter.Seq2[types.Hook, error] { - path := fmt.Sprintf("/api/v1/orgs/%s/hooks", org) + path := core.Sprintf("/api/v1/orgs/%s/hooks", org) return ListIter[types.Hook](ctx, s.client, path, nil) } diff --git a/wiki.go b/wiki.go index 898c5ca..7681ada 100644 --- a/wiki.go +++ b/wiki.go @@ -2,8 +2,8 @@ package forge import ( "context" - "fmt" + core "dappco.re/go/core" "dappco.re/go/core/forge/types" ) @@ -19,7 +19,7 @@ func newWikiService(c *Client) *WikiService { // ListPages returns all wiki page metadata for a repository. func (s *WikiService) ListPages(ctx context.Context, owner, repo string) ([]types.WikiPageMetaData, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/pages", owner, repo) + path := core.Sprintf("/api/v1/repos/%s/%s/wiki/pages", owner, repo) var out []types.WikiPageMetaData if err := s.client.Get(ctx, path, &out); err != nil { return nil, err @@ -29,7 +29,7 @@ func (s *WikiService) ListPages(ctx context.Context, owner, repo string) ([]type // GetPage returns a single wiki page by name. func (s *WikiService) GetPage(ctx context.Context, owner, repo, pageName string) (*types.WikiPage, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/page/%s", owner, repo, pageName) + path := core.Sprintf("/api/v1/repos/%s/%s/wiki/page/%s", owner, repo, pageName) var out types.WikiPage if err := s.client.Get(ctx, path, &out); err != nil { return nil, err @@ -39,7 +39,7 @@ func (s *WikiService) GetPage(ctx context.Context, owner, repo, pageName string) // CreatePage creates a new wiki page. func (s *WikiService) CreatePage(ctx context.Context, owner, repo string, opts *types.CreateWikiPageOptions) (*types.WikiPage, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/new", owner, repo) + path := core.Sprintf("/api/v1/repos/%s/%s/wiki/new", owner, repo) var out types.WikiPage if err := s.client.Post(ctx, path, opts, &out); err != nil { return nil, err @@ -49,7 +49,7 @@ func (s *WikiService) CreatePage(ctx context.Context, owner, repo string, opts * // EditPage updates an existing wiki page. func (s *WikiService) EditPage(ctx context.Context, owner, repo, pageName string, opts *types.CreateWikiPageOptions) (*types.WikiPage, error) { - path := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/page/%s", owner, repo, pageName) + path := core.Sprintf("/api/v1/repos/%s/%s/wiki/page/%s", owner, repo, pageName) var out types.WikiPage if err := s.client.Patch(ctx, path, opts, &out); err != nil { return nil, err @@ -59,6 +59,6 @@ func (s *WikiService) EditPage(ctx context.Context, owner, repo, pageName string // DeletePage removes a wiki page. func (s *WikiService) DeletePage(ctx context.Context, owner, repo, pageName string) error { - path := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/page/%s", owner, repo, pageName) + path := core.Sprintf("/api/v1/repos/%s/%s/wiki/page/%s", owner, repo, pageName) return s.client.Delete(ctx, path) }