-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
Showing
6 changed files
with
339 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} | ||
} |