diff options
| author | Sam Anthony <sam@samanthony.xyz> | 2026-05-11 16:16:24 -0400 |
|---|---|---|
| committer | Sam Anthony <sam@samanthony.xyz> | 2026-05-11 16:16:24 -0400 |
| commit | af2de318402df1fd8d33192d71613c21c4ee96bf (patch) | |
| tree | 473c33981032bc73f5a5f4282ef8b1292333ce28 /ship.go | |
| parent | fcfe73600c8548d45820176d58f1c8cfc2327810 (diff) | |
| download | lulu-af2de318402df1fd8d33192d71613c21c4ee96bf.zip | |
implement POST /print-job-cost-calculations
Diffstat (limited to 'ship.go')
| -rw-r--r-- | ship.go | 119 |
1 files changed, 109 insertions, 10 deletions
@@ -1,6 +1,11 @@ package lulu -//go:generate go run github.com/yawnak/string-enumer -t ShippingLevel --text -o ./ship_gen.go . +import ( + "encoding/json" + "fmt" +) + +//go:generate go run github.com/yawnak/string-enumer -t ShippingLevel -t Title --text -o ./ship_gen.go . // ShippingLevel is the quality/speed with which a package is shipped. type ShippingLevel string @@ -14,13 +19,107 @@ const ( ) type ShippingAddress struct { - Name string `json:"name"` - Phone string `json:"phone_number"` - CountryCode string `json:"country_code"` - PostCode string `json:"postcode"` - StateCode string `json:"state_code"` - City string `json:"city"` - Street1 string `json:"street1"` - Street2 string `json:"street2"` - IsBusiness bool `json:"is_business"` + Country string // ISO 3166-2 country code + State string // 2 or 3 letter state codes (officially called ISO-3166-2 subdivision codes). They are required for some countries (e.g. US, MX, CA, AU) + City string + Street1 string // First address line + Street2 string // Second address line + PostCode string // Required for most countries + IsBusiness bool // Only relevant for US addresses. Some US carriers don't deliver to business-addresses on Saturday. + Name string // Full name of the person, including first and last name. + Title Title + Organization string // Name of an organization. Required if no person name is given. + Email string // Shipping carriers require an email address for notifications or handling delivery issues. If no email is given, the default email in the user profile will be used. + Phone string // Shipping carriers require a phone number for handling delivery issues. If no phone number is given, the default in the API user profile will be used. + TaxId string // The recipient’s tax identification number. Required for shipping addresses to Brazil, Chile, and Mexico. +} + +// They use "country" in some places, "country_code" in others, etc. +// This is the union of all of them. +type shippingAddress struct { + Country string `json:"country"` + CountryCode string `json:"country_code"` + State string `json:"state"` + StateCode string `json:"state_code"` + City string `json:"city"` + Street1 string `json:"street1"` + Street2 string `json:"street2"` + PostCode string `json:"postcode"` + IsBusiness bool `json:"is_business"` + Name string `json:"name"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + Title Title `json:"title"` + Organization string `json:"organization"` + Email string `json:"email"` + Phone string `json:"phone_number"` + TaxId string `json:"recipient_tax_id"` +} + +func (a *ShippingAddress) UnmarshalJSON(data []byte) error { + s := string(data) + var alias shippingAddress + if err := json.Unmarshal(data, &alias); err != nil { + return err + } + + if country, both := either(alias.Country, alias.CountryCode); !both { + a.Country = country + } else { + return fmt.Errorf(`address contains both "country" and "country_code": %q`, s) + } + + if state, both := either(alias.State, alias.StateCode); !both { + a.State = state + } else { + return fmt.Errorf(`address contains both "state" and "state_code": %q`, s) + } + + a.City = alias.City + a.Street1 = alias.Street1 + a.Street2 = alias.Street2 + a.PostCode = alias.PostCode + a.IsBusiness = alias.IsBusiness + + hasName := alias.Name != "" + hasFirst := alias.FirstName != "" + hasLast := alias.LastName != "" + if hasName && (hasFirst || hasLast) { + return fmt.Errorf(`address contains both "name" and {"first_name", "last_name"}: %q`, s) + } else if hasName { + a.Name = alias.Name + } else if hasFirst && hasLast { + a.Name = fmt.Sprintf("%s %s", alias.FirstName, alias.LastName) + } else if hasLast { + a.Name = alias.LastName + } else if hasFirst { + a.Name = alias.FirstName + } + + a.Title = alias.Title + a.Organization = alias.Organization + a.Email = alias.Email + a.Phone = alias.Phone + a.TaxId = alias.TaxId + + return nil +} + +func either(a, b string) (string, bool) { + if a != "" && b != "" { + return a, true + } else if a != "" { + return a, false + } + return b, false } + +type Title string + +const ( + Mr Title = "MR" + Miss Title = "MISS" + Mrs Title = "MRS" + Ms Title = "MS" + Dr Title = "DR" +) |