master
1package main
2
3import (
4 "errors"
5 "flag"
6 "fmt"
7 "net/url"
8 "strconv"
9 "strings"
10
11 "github.com/bryfry/amazing"
12)
13
14var (
15 isbn string
16 checkNeighbors bool
17)
18
19func init() {
20 flag.StringVar(&isbn, "i", "", "isbn number to validate")
21 flag.BoolVar(&checkNeighbors, "n", false, "check neighbor isbns")
22 flag.Parse()
23}
24
25func main() {
26 fmt.Println("Checking for valid ISBN:", isbn)
27 isbn := strings.Replace(isbn, "-", "", -1)
28
29 valid, err := ValidISBN10(isbn)
30 if err != nil || !valid {
31 fmt.Println("Not Valid ISBN-10: ", err)
32 isbn, err = CreateValidISBN10(isbn)
33 if err != nil {
34 fmt.Println("Failed to create a Valid ISBN10:", err)
35 }
36 fmt.Println("Looking up expected ISBN-10:", isbn)
37 }
38 url, err := ISBN10URL(isbn)
39 if err != nil {
40 fmt.Println("Amazon Error:", err)
41 }
42 fmt.Println("ISBN-10:", isbn, url)
43
44 if checkNeighbors {
45 ISBN10Neighbors(isbn)
46 }
47
48}
49
50func ISBN10Neighbors(isbn string) {
51 fmt.Println("----- Neighbor ISBNs -----")
52 for i := 0; i < 10; i++ {
53 isbn, err := CreateValidISBN10(isbn[:8] + strconv.Itoa(i))
54 if err != nil {
55 fmt.Println("ISBN-10:", isbn, err)
56 continue
57 }
58 url, err := ISBN10URL(isbn)
59 if err != nil {
60 fmt.Println(isbn)
61 continue
62 }
63 fmt.Println(isbn, url)
64 }
65
66 fmt.Println("----- ----- ----- ----- -----")
67
68}
69
70func ISBN10URL(isbn string) (string, error) {
71 client, err := amazing.NewAmazingFromEnv("US")
72 if err != nil {
73 return "", err
74 }
75
76 params := url.Values{
77 "IdType": []string{"ISBN"},
78 "ItemId": []string{isbn},
79 "Operation": []string{"ItemLookup"},
80 "SearchIndex": []string{"All"},
81 }
82
83 result, err := client.ItemLookup(params)
84 if err != nil {
85 return "", err
86 }
87
88 if len(result.AmazonItems.Request.Errors) > 0 {
89 return "", errors.New(result.AmazonItems.Request.Errors[0].Error())
90 }
91
92 u, _ := url.Parse(result.AmazonItems.Items[0].DetailPageURL)
93 return strings.Split((u.Host + u.Path), "?")[0], nil
94}
95
96var ISBN10LengthError = errors.New("Incorrect Length")
97
98func ValidISBN10(isbn string) (bool, error) {
99 if len(isbn) != 10 {
100 return false, ISBN10LengthError
101 }
102 expected, err := CheckDigitISBN10(isbn)
103 if err != nil {
104 return false, err
105 }
106 recieved := strings.ToUpper(string(isbn[9]))
107 if expected != recieved {
108 return false, errors.New(fmt.Sprintf("Invalid check digit: expected (%s) received (%s)", expected, recieved))
109 }
110 return true, nil
111}
112
113func CheckDigitISBN10(isbn string) (string, error) {
114 if len(isbn) != 9 && len(isbn) != 10 {
115 return "", ISBN10LengthError
116 } else if len(isbn) == 10 {
117 isbn = isbn[:9] // Ignore check digit if provided
118 }
119 sum := 0
120 for i, r := range isbn {
121 d, err := isbnAtoi(string(r))
122 if err != nil {
123 return "", errors.New(fmt.Sprintf("Digit parsing failed (location %d: %s)", i, string(r)))
124 }
125 sum = sum + (10-i)*d
126 }
127 return isbnItoa(11 - (sum % 11)), nil
128}
129
130func CreateValidISBN10(isbn string) (string, error) {
131 if len(isbn) > 10 {
132 return "", ISBN10LengthError
133 }
134 cd, err := CheckDigitISBN10(isbn)
135 if err != nil {
136 return "", err
137 }
138 isbn = isbn[:9] + cd
139 return isbn, nil
140}
141
142func isbnAtoi(digit string) (int, error) {
143 if digit == "X" || digit == "x" {
144 return 10, nil
145 } else {
146 return strconv.Atoi(digit)
147 }
148}
149
150func isbnItoa(digit int) string {
151 if digit == 10 {
152 return "X"
153 }
154 if digit == 11 {
155 return "0"
156 }
157 return strconv.Itoa(digit)
158}