aboutsummaryrefslogtreecommitdiffstats
path: root/ship.go
blob: b5aa822cec7cd440f9899ab7ad87e3c7a49b977c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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 ShippingAddress struct {
	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"
)