master
Raw Download raw file
  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}