aboutsummaryrefslogtreecommitdiffstats
path: root/lulu.go
diff options
context:
space:
mode:
authorSam Anthony <sam@samanthony.xyz>2026-05-07 11:36:45 -0400
committerSam Anthony <sam@samanthony.xyz>2026-05-07 11:36:45 -0400
commit09c91d21b083abc976a4cc6439899fb00438a630 (patch)
tree63ef50d67b72f847e85877ce9910b32003b63d6c /lulu.go
parent4ee72fe36dd513bcbad227f7bdd6ed0079d2c95a (diff)
downloadlulu-09c91d21b083abc976a4cc6439899fb00438a630.zip
implement GET /cover-dimensions
Diffstat (limited to 'lulu.go')
-rw-r--r--lulu.go105
1 files changed, 85 insertions, 20 deletions
diff --git a/lulu.go b/lulu.go
index 2346749..af425f0 100644
--- a/lulu.go
+++ b/lulu.go
@@ -9,6 +9,7 @@ import (
"io"
"net/http"
"net/url"
+ "strconv"
"golang.org/x/oauth2/clientcredentials"
)
@@ -33,8 +34,8 @@ type Unit string
const (
Points Unit = "pt"
- Millimeters = "mm"
- Inches = "inch"
+ Millimeters Unit = "mm"
+ Inches Unit = "inch"
)
type ValidationStatus string
@@ -77,6 +78,43 @@ type InteriorValidationRecord struct {
ValidPkgIds []PkgId `json:"valid_pod_package_ids"`
}
+// coverDimensionsReq is the json body of a /cover-dimensions/ request.
+type coverDimensionsReq struct {
+ PkgId PkgId `json:"pod_package_id"`
+ NPages uint `json:"interior_page_count"`
+ Unit Unit `json:"unit"`
+}
+
+type CoverDimensions struct {
+ Width, Height float64
+ Unit Unit
+}
+
+func (cd *CoverDimensions) UnmarshalJSON(data []byte) error {
+ s := string(data)
+ var alias struct {
+ Width, Height string
+ Unit Unit
+ }
+ if err := json.Unmarshal(data, &alias); err != nil {
+ return fmt.Errorf("malformed %T: %q: %w", cd, s, err)
+ }
+
+ w, err := strconv.ParseFloat(alias.Width, 64)
+ if err != nil {
+ return fmt.Errorf("malformed %T.Width: %q: %w", cd, s, err)
+ }
+ h, err := strconv.ParseFloat(alias.Height, 64)
+ if err != nil {
+ return fmt.Errorf("malformed %T.Height: %q: %w", cd, s, err)
+ }
+
+ cd.Width = w
+ cd.Height = h
+ cd.Unit = alias.Unit
+ return nil
+}
+
type Client struct {
c *http.Client
}
@@ -132,20 +170,12 @@ func (c *Client) validateInterior(payload any) (uint, error) {
defer resp.Body.Close()
if resp.StatusCode != http.StatusCreated {
- body, _ := io.ReadAll(resp.Body)
- return 0, fmt.Errorf("lulu: POST %s: %s: %s", url, resp.Status, body)
+ return 0, errResp{resp}
}
- buf := new(bytes.Buffer)
- if _, err := io.Copy(buf, resp.Body); err != nil {
- return 0, fmt.Errorf("lulu: POST %s: error reading response body: %w", url, err)
- }
- dec := json.NewDecoder(buf)
var rec InteriorValidationRecord
- if err := dec.Decode(&rec); err != nil {
- return 0, fmt.Errorf("lulu: POST %s: error decoding response body %q: %w", url, buf, err)
- }
- return rec.Id, nil
+ err = decodeResponse(resp, &rec)
+ return rec.Id, err
}
// GetInteriorValidation retrieves information about an interior file
@@ -164,18 +194,53 @@ func (c *Client) GetInteriorValidation(id uint) (InteriorValidationRecord, error
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
- body, _ := io.ReadAll(resp.Body)
- return InteriorValidationRecord{}, fmt.Errorf("lulu: GET %s: %s: %s", url, resp.Status, body)
+ return InteriorValidationRecord{}, errResp{resp}
+ }
+
+ var rec InteriorValidationRecord
+ err = decodeResponse(resp, &rec)
+ return rec, err
+}
+
+// CoverDimensions calculates the required dimensions of the cover for a
+// book with the given manufacturing settings and number of pages. The
+// returned dimensions are given in the specified units of measurement.
+//
+// https://api.lulu.com/docs/#tag/Files-validation/operation/Cover-Dimensions_create
+func (c *Client) CoverDimensions(mfg PkgId, npages uint, unit Unit) (CoverDimensions, error) {
+ payload := coverDimensionsReq{mfg, npages, unit}
+ body, err := json.Marshal(payload)
+ if err != nil {
+ return CoverDimensions{}, fmt.Errorf("lulu: error encoding request body %v for %s: %w", payload, coverDimensionsPath, err)
+ }
+
+ url, err := url.JoinPath(ApiUrl, coverDimensionsPath)
+ if err != nil {
+ return CoverDimensions{}, fmt.Errorf("lulu: %w", err)
+ }
+ resp, err := c.c.Post(url, "application/json", bytes.NewBuffer(body))
+ if err != nil {
+ return CoverDimensions{}, fmt.Errorf("lulu: %w", err)
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusCreated {
+ return CoverDimensions{}, errResp{resp}
}
+ var dims CoverDimensions
+ err = decodeResponse(resp, &dims)
+ return dims, err
+}
+
+func decodeResponse(resp *http.Response, v any) error {
buf := new(bytes.Buffer)
if _, err := io.Copy(buf, resp.Body); err != nil {
- return InteriorValidationRecord{}, fmt.Errorf("lulu: GET %s: error reading response body: %w", url, err)
+ return errReadResp{resp, err}
}
dec := json.NewDecoder(buf)
- var rec InteriorValidationRecord
- if err := dec.Decode(&rec); err != nil {
- return rec, fmt.Errorf("lulu: GET %s: error decoding response body %q: %w", url, buf, err)
+ if err := dec.Decode(v); err != nil {
+ return errDecResp{resp, buf.Bytes(), err}
}
- return rec, nil
+ return nil
}