Current section
Files
Jump to
Current section
Files
lib/isbn.ex
defmodule ISBN do
@moduledoc """
Documentation for ISBN.
"""
@isbn13_multipliers [1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3]
@isbn10_multipliers [1, 2, 3, 4, 5, 6, 7, 8, 9]
@doc """
Checks if ISBN is valid.
## Examples
iex> ISBN.valid?("9971502100")
true
"""
def valid?(isbn) when not is_binary(isbn), do: false
# def valid?(isbn) when not match?(isbn, ~r/^[[:alnum:]]+$/), do: false
def valid?(isbn) do
last_digit = isbn |> String.trim() |> String.last()
digits =
isbn
|> String.replace("-", "")
|> String.replace(" ", "")
|> String.slice(0..-2)
|> String.codepoints()
|> Enum.map(&String.to_integer/1)
reveal_verifier(digits) == last_digit
end
@doc """
Returns ISBN formatted.
## Examples
iex> ISBN.format("9992158107")
"99-9215-810-7"
iex> ISBN.format("9992158106")
nil
"""
def format(isbn) when not is_binary(isbn), do: nil
def format(isbn) do
case valid?(isbn) do
true ->
isbn
|> String.trim()
|> String.replace("-", "")
|> String.replace(" ", "")
|> String.codepoints()
|> do_format()
_ ->
nil
end
end
defp do_format(digits) when length(digits) == 10 do
digits
|> List.insert_at(2, "-")
|> List.insert_at(7, "-")
|> List.insert_at(11, "-")
|> Enum.join()
end
defp do_format(digits) when length(digits) == 13 do
digits
|> List.insert_at(3, "-")
|> List.insert_at(6, "-")
|> List.insert_at(11, "-")
|> List.insert_at(15, "-")
|> Enum.join()
end
defp do_format(_isbn), do: nil
defp reveal_verifier(digits) when length(digits) == 9 do
acc = calculate(digits, @isbn10_multipliers)
rem = rem(acc, 11)
if rem == 10, do: "X", else: Integer.to_string(rem)
end
defp reveal_verifier(digits) when length(digits) == 12 do
acc = calculate(digits, @isbn13_multipliers)
rem = rem(acc, 10)
if rem == 0, do: "0", else: Integer.to_string(10 - rem)
end
defp reveal_verifier(_isbn), do: nil
defp calculate(digits, multipliers) do
multipliers
|> Enum.zip(digits)
|> Enum.reduce(0, fn {multiplier, digit}, acc ->
acc + multiplier * digit
end)
end
end