package lulu 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 const ( Mail ShippingLevel = "MAIL" // Slowest ship method. Depending on the destination, tracking might not be available. PriorityMail ShippingLevel = "PRIORITY_MAIL" // Priority mail shipping Ground ShippingLevel = "GROUND" // Courier based shipping using ground transportation in the US. Expedited ShippingLevel = "EXPEDITED" // Expedited (2nd day) delivery via air mail or equivalent. Express ShippingLevel = "EXPRESS" // Overnight delivery. Fastest shipping available. ) type Title string const ( Mr Title = "MR" Miss Title = "MISS" Mrs Title = "MRS" Ms Title = "MS" Dr Title = "DR" ) type ShippingAddress struct { // ISO 3166-2 country code Country string `json:"country_code"` // 2 or 3 letter state code (officially called ISO-3166-2 // subdivision codes). They are required for some countries (e.g. // US, MX, CA, AU). State string `json:"state_code"` City string `json:"city"` Street1 string `json:"street1"` // First address line Street2 string `json:"street2"` // Second address line PostCode string `json:"postcode"` // Required for most countries // Only relevant for US addresses. Some US carriers don't deliver // to business-addresses on Saturday. IsBusiness bool `json:"is_business"` // Full name of the person, including first and last name. Name string `json:"name"` Title Title `json:"title"` // Name of an organization. Required if no person name is given. Organization string `json:"organization"` // 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. Email EmailAddress `json:"email"` // 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. Phone PhoneNumber `json:"phone_number"` // The recipient's tax identification number. Required for // shipping addresses to Brazil, Chile, and Mexico. TaxId string `json:"recipient_tax_id"` } func (a *ShippingAddress) UnmarshalJSON(data []byte) error { var alias extShippingAddress if err := json.Unmarshal(data, &alias); err != nil { return err } addr, err := alias.addr() if err != nil { return err } *a = addr return nil } type ShippingAddressValidation struct { Address ShippingAddress // The original address that was given to the server. Suggested ShippingAddress // The address that the server recommends. Warnings []ShippingAddressWarning } func (av *ShippingAddressValidation) UnmarshalJSON(data []byte) error { var alias extShippingAddress if err := json.Unmarshal(data, &alias); err != nil { return err } addr, err := alias.addr() if err != nil { return err } av.Address = addr av.Suggested = alias.Suggested av.Warnings = alias.Warnings return nil } type ShippingAddressWarning struct { Type string `json:"type"` // eg "validation_warning" Path string `json:"path"` // eg "external" Code string `json:"code"` // eg "REPLACED" Msg string `json:"message"` // eg "street1: Holstenstr. 40 -> Holstenstraße 40" } // extShippingAddress is the extended shipping address that is seen in // some responses. It includes "warnings" and "suggested_address". It // also unmarshals either "country" or "country_code" etc. to account for // the irregularities in the API. type extShippingAddress 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 EmailAddress `json:"email"` Phone PhoneNumber `json:"phone_number"` TaxId string `json:"recipient_tax_id"` Warnings []ShippingAddressWarning `json:"warnings"` Suggested ShippingAddress `json:"suggested_address"` } // addr takes the disjunctive union of the API's irregular fields // ("country"/"country_code", etc.) and returns only those fields of the // actual shipping address: excluding the suggested address and warnings. // It returns error if two or more of the "same field" are present, eg. // both "country" and "country_code". func (ext extShippingAddress) addr() (ShippingAddress, error) { country, both := either(ext.Country, ext.CountryCode) if both { return ShippingAddress{}, fmt.Errorf(`address contains both "country" and "country_code"`) } state, both := either(ext.State, ext.StateCode) if both { return ShippingAddress{}, fmt.Errorf(`address contains both "state" and "state_code"`) } var name string hasName := ext.Name != "" hasFirst := ext.FirstName != "" hasLast := ext.LastName != "" if hasName && (hasFirst || hasLast) { return ShippingAddress{}, fmt.Errorf(`address contains both "name" and {"first_name", "last_name"}`) } else if hasName { name = ext.Name } else if hasFirst && hasLast { name = fmt.Sprintf("%s %s", ext.FirstName, ext.LastName) } else if hasLast { name = ext.LastName } else if hasFirst { name = ext.FirstName } return ShippingAddress{ Country: country, State: state, City: ext.City, Street1: ext.Street1, Street2: ext.Street2, PostCode: ext.PostCode, IsBusiness: ext.IsBusiness, Name: name, Title: ext.Title, Organization: ext.Organization, Email: ext.Email, Phone: ext.Phone, TaxId: ext.TaxId, }, nil } func either(a, b string) (string, bool) { if a != "" && b != "" { return a, true } else if a != "" { return a, false } return b, false }