Skip to content

Commit

Permalink
ISBN checker is added. Fixes #27 (#100)
Browse files Browse the repository at this point in the history
# Describe Request

ISBN checker is added. 

Fixes #27

# Change Type

Checker added.
  • Loading branch information
cinar committed Jun 24, 2023
1 parent a125ee1 commit fbabb71
Show file tree
Hide file tree
Showing 6 changed files with 339 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ This package currently provides the following checkers:
- [ip](doc/checkers/ip.md) checks if the given value is an IP address.
- [ipv4](doc/checkers/ipv4.md) checks if the given value is an IPv4 address.
- [ipv6](doc/checkers/ipv6.md) checks if the given value is an IPv6 address.
- [isbn](doc/checkers/isbn.md) checks if the given value is a valid ISBN number.
- [luhn](doc/checkers/luhn.md) checks if the given number is valid based on the Luhn algorithm.
- [mac](doc/checkers/mac.md) checks if the given value is a valid an IEEE 802 MAC-48, EUI-48, EUI-64, or a 20-octet IP over InfiniBand link-layer address.
- [max](doc/checkers/max.md) checks if the given value is less than the given maximum.
Expand Down
1 change: 1 addition & 0 deletions checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ var makers = map[string]MakeFunc{
CheckerIP: makeIP,
CheckerIPV4: makeIPV4,
CheckerIPV6: makeIPV6,
CheckerISBN: makeISBN,
CheckerLuhn: makeLuhn,
CheckerMac: makeMac,
CheckerMax: makeMax,
Expand Down
52 changes: 52 additions & 0 deletions doc/checkers/isbn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# ISBN Checker

An [ISBN (International Standard Book Number)](https://en.wikipedia.org/wiki/International_Standard_Book_Number) is a 10 or 13 digit number that is used to identify a book.

The `isbn` checker checks if the given value is a valid ISBN. If the given value is not a valid ISBN, the checker will return the `NOT_ISBN` result. Here is an example:

```golang
type Book struct {
ISBN string `checkers:"isbn"`
}

book := &Book{
ISBN: "1430248270",
}

_, valid := checker.Check(book)
if !valid {
// Send the mistakes back to the user
}
```

The `isbn` checker can also be configured to check for a 10 digit or a 13 digit number. Here is an example:

```golang
type Book struct {
ISBN string `checkers:"isbn:13"`
}

book := &Book{
ISBN: "9781430248279",
}

_, valid := checker.Check(book)
if !valid {
// Send the mistakes back to the user
}
```

In your custom checkers, you can call the `isbn` checker functions below to validate the user input.

- [`IsISBN`](https://pkg.go.dev/github.com/cinar/checker#IsISBN) checks if the given value is a valid ISBN number.
- [`IsISBN10`](https://pkg.go.dev/github.com/cinar/checker#IsISBN10) checks if the given value is a valid ISBN 10 number.
- [`IsISBN13`](https://pkg.go.dev/github.com/cinar/checker#IsISBN13) checks if the given value is a valid ISBN 13 number.

Here is an example:

```golang
result := checker.IsISBN("1430248270")
if result != checker.ResultValid {
// Send the mistakes back to the user
}
```
4 changes: 2 additions & 2 deletions doc/checkers/url.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# URL Checker

The ```url``` checker checks if the given value is a valid URL. If the given value is not a valid URL, the checker will return the ```NOT_URL``` result. The checker uses [ParseRequestURI](https://pkg.go.dev/net/url#ParseRequestURI) function to parse the URL, and then checks if the schema or the host are both set.
The `url` checker checks if the given value is a valid URL. If the given value is not a valid URL, the checker will return the `NOT_URL` result. The checker uses [ParseRequestURI](https://pkg.go.dev/net/url#ParseRequestURI) function to parse the URL, and then checks if the schema or the host are both set.

Here is an example:

Expand All @@ -19,7 +19,7 @@ if !valid {
}
```

In your custom checkers, you can call the ```url``` checker function ```IsURL``` to validate the user input. Here is an example:
In your custom checkers, you can call the `url` checker function [`IsURL`](https://pkg.go.dev/github.com/cinar/checker#IsURL) to validate the user input. Here is an example:

```golang
result := checker.IsURL("https://zdo.com")
Expand Down
121 changes: 121 additions & 0 deletions isbn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package checker

import (
"reflect"
"strings"
)

// Program to check for ISBN
// https://www.geeksforgeeks.org/program-check-isbn/

// How to Verify an ISBN
// https://www.instructables.com/How-to-verify-a-ISBN/

// CheckerISBN is the name of the checker.
const CheckerISBN = "isbn"

// ResultNotISBN indicates that the given value is not a valid ISBN.
const ResultNotISBN = "NOT_ISBN"

// IsISBN10 checks if the given value is a valid ISBN-10 number.
func IsISBN10(value string) Result {
value = strings.ReplaceAll(value, "-", "")

if len(value) != 10 {
return ResultNotISBN
}

digits := []rune(value)
sum := 0

for i, e := 0, len(digits); i < e; i++ {
n := isbnDigitToInt(digits[i])
sum += n * (e - i)
}

if sum%11 != 0 {
return ResultNotISBN
}

return ResultValid
}

// IsISBN13 checks if the given value is a valid ISBN-13 number.
func IsISBN13(value string) Result {
value = strings.ReplaceAll(value, "-", "")

if len(value) != 13 {
return ResultNotISBN
}

digits := []rune(value)
sum := 0

for i, d := range digits {
n := isbnDigitToInt(d)
if i%2 != 0 {
n *= 3
}

sum += n
}

if sum%10 != 0 {
return ResultNotISBN
}

return ResultValid
}

// IsISBN checks if the given value is a valid ISBN number.
func IsISBN(value string) Result {
value = strings.ReplaceAll(value, "-", "")

if len(value) == 10 {
return IsISBN10(value)
} else if len(value) == 13 {
return IsISBN13(value)
}

return ResultNotISBN
}

// isbnDigitToInt returns the integer value of given ISBN digit.
func isbnDigitToInt(r rune) int {
if r == 'X' {
return 10
}

return int(r - '0')
}

// makeISBN makes a checker function for the URL checker.
func makeISBN(config string) CheckFunc {
if config != "" && config != "10" && config != "13" {
panic("invalid format")
}

return func(value, parent reflect.Value) Result {
return checkISBN(value, parent, config)
}
}

// checkISBN checks if the given value is a valid ISBN number.
func checkISBN(value, _ reflect.Value, mode string) Result {
if value.Kind() != reflect.String {
panic("string expected")
}

number := value.String()

switch mode {
case "10":
return IsISBN10(number)

case "13":
return IsISBN13(number)

default:
return IsISBN(number)
}
}
162 changes: 162 additions & 0 deletions isbn_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package checker_test

import (
"testing"

"github.com/cinar/checker"
)

func TestIsISBN10Valid(t *testing.T) {
result := checker.IsISBN10("1430248270")
if result != checker.ResultValid {
t.Fail()
}
}

func TestIsISBN10ValidX(t *testing.T) {
result := checker.IsISBN10("007462542X")
if result != checker.ResultValid {
t.Fail()
}
}

func TestIsISBN10ValidWithDashes(t *testing.T) {
result := checker.IsISBN10("1-4302-4827-0")
if result != checker.ResultValid {
t.Fail()
}
}

func TestIsISBN10InvalidLength(t *testing.T) {
result := checker.IsISBN10("143024827")
if result != checker.ResultNotISBN {
t.Fail()
}
}

func TestIsISBN10InvalidCheck(t *testing.T) {
result := checker.IsISBN10("1430248272")
if result != checker.ResultNotISBN {
t.Fail()
}
}

func TestIsISBN13Valid(t *testing.T) {
result := checker.IsISBN13("9781430248279")
if result != checker.ResultValid {
t.Fail()
}
}

func TestIsISBN13ValidWithDashes(t *testing.T) {
result := checker.IsISBN13("978-1-4302-4827-9")
if result != checker.ResultValid {
t.Fail()
}
}

func TestIsISBN13InvalidLength(t *testing.T) {
result := checker.IsISBN13("978143024827")
if result != checker.ResultNotISBN {
t.Fail()
}
}

func TestIsISBN13InvalidCheck(t *testing.T) {
result := checker.IsISBN13("9781430248272")
if result != checker.ResultNotISBN {
t.Fail()
}
}

func TestIsISBNValid10(t *testing.T) {
result := checker.IsISBN("1430248270")
if result != checker.ResultValid {
t.Fail()
}
}

func TestIsISBNValid13(t *testing.T) {
result := checker.IsISBN("9781430248279")
if result != checker.ResultValid {
t.Fail()
}
}

func TestIsISBNInvalidLenght(t *testing.T) {
result := checker.IsISBN("978143024827")
if result != checker.ResultNotISBN {
t.Fail()
}
}

func TestCheckISBNNonString(t *testing.T) {
defer checker.FailIfNoPanic(t)

type Book struct {
ISBN int `checkers:"isbn"`
}

book := &Book{}

checker.Check(book)
}

func TestCheckISBNValid(t *testing.T) {
type Book struct {
ISBN string `checkers:"isbn"`
}

book := &Book{
ISBN: "1430248270",
}

_, valid := checker.Check(book)
if !valid {
t.Fail()
}
}

func TestCheckISBNInvalid(t *testing.T) {
defer checker.FailIfNoPanic(t)

type Book struct {
ISBN string `checkers:"isbn:20"`
}

book := &Book{
ISBN: "1430248270",
}

checker.Check(book)
}

func TestCheckISBNValid10(t *testing.T) {
type Book struct {
ISBN string `checkers:"isbn:10"`
}

book := &Book{
ISBN: "1430248270",
}

_, valid := checker.Check(book)
if !valid {
t.Fail()
}
}

func TestCheckISBNValid13(t *testing.T) {
type Book struct {
ISBN string `checkers:"isbn:13"`
}

book := &Book{
ISBN: "9781430248279",
}

_, valid := checker.Check(book)
if !valid {
t.Fail()
}
}

0 comments on commit fbabb71

Please sign in to comment.