# `Exisbn`
[🔗](https://github.com/solar05/exisbn/blob/main/lib/exisbn.ex#L1)

Documentation for `Exisbn`.

# `checkdigit_correct?`

```elixir
@spec checkdigit_correct?(String.t()) :: boolean()
```

Takes an ISBN (10 or 13) and checks its validity by its check digit

## Examples

    iex> Exisbn.checkdigit_correct?("85-359-0277-5")
    true
    iex> Exisbn.checkdigit_correct?("978-5-12345-678-1")
    true
    iex> Exisbn.checkdigit_correct?("978-5-12345-678")
    false

# `correct_hyphens?`

```elixir
@spec correct_hyphens?(binary()) :: boolean()
```

Checks if an ISBN (10 or 13) code is correctly hyphenated. If ISBN incorrect, that count as no.

## Examples

    iex> Exisbn.correct_hyphens?("978-85-359-0277-8")
    true
    iex> Exisbn.correct_hyphens?("97-8853590277-8")
    false
    iex> Exisbn.correct_hyphens?("0-306-40615-2")
    true
    iex> Exisbn.correct_hyphens?("03-064-06152")
    false
    iex> Exisbn.correct_hyphens?("str")
    false

# `fetch_checkdigit`

```elixir
@spec fetch_checkdigit(String.t()) :: {:ok, String.t()} | {:error, :invalid_isbn}
```

Takes an ISBN and returns its checkdigit.

## Examples

    iex> Exisbn.fetch_checkdigit("9788535902778")
    {:ok, "8"}
    iex> Exisbn.fetch_checkdigit("2-1234-5680-2")
    {:ok, "2"}
    iex> Exisbn.fetch_checkdigit("str")
    {:error, :invalid_isbn}
    iex> Exisbn.fetch_checkdigit("887385107X")
    {:ok, "X"}

# `fetch_checkdigit!`

```elixir
@spec fetch_checkdigit!(String.t()) :: String.t()
```

Same as `fetch_checkdigit/1`, but raises exception.

## Examples

    iex> Exisbn.fetch_checkdigit!("9788535902778")
    "8"
    iex> Exisbn.fetch_checkdigit!("2-1234-5680-2")
    "2"
    iex> Exisbn.fetch_checkdigit!("str")
    ** (ArgumentError) Invalid ISBN
    iex> Exisbn.fetch_checkdigit!("887385107X")
    "X"

# `fetch_metadata`

```elixir
@spec fetch_metadata(String.t()) ::
  {:ok,
   %{
     prefix: String.t(),
     zone: String.t(),
     country_code: String.t() | nil,
     registrant: String.t(),
     publication: String.t(),
     checkdigit: String.t()
   }}
  | {:error, :invalid_isbn | :unknown_group | :unknown_publisher}
```

Returns all ISBN metadata in a single call.

Fetches the prefix, publisher zone, ISO country code, registrant element,
publication element, and check digit without redundant lookups.

Returns `{:error, :invalid_isbn}` for structurally invalid ISBNs,
`{:error, :unknown_group}` when the registration group is not in the dataset,
and `{:error, :unknown_publisher}` when the group has no publisher ranges defined.

## Examples

    iex> Exisbn.fetch_metadata("9788535902778")
    {:ok, %{checkdigit: "8", country_code: "BR", prefix: "978-85", publication: "0277", registrant: "359", zone: "Brazil"}}

    iex> Exisbn.fetch_metadata("9780306406157")
    {:ok, %{checkdigit: "7", country_code: nil, prefix: "978-0", publication: "40615", registrant: "306", zone: "English language"}}

    iex> Exisbn.fetch_metadata("str")
    {:error, :invalid_isbn}

    iex> Exisbn.fetch_metadata("9799012345674")
    {:error, :unknown_group}

# `fetch_metadata!`

```elixir
@spec fetch_metadata!(String.t()) :: map()
```

Same as `fetch_metadata/1`, but raises exception.

## Examples

    iex> meta = Exisbn.fetch_metadata!("9788535902778")
    iex> meta.zone
    "Brazil"
    iex> meta.country_code
    "BR"

    iex> Exisbn.fetch_metadata!("str")
    ** (ArgumentError) Invalid ISBN

# `fetch_prefix`

```elixir
@spec fetch_prefix(String.t()) ::
  {:ok, String.t()} | {:error, :invalid_isbn | :unknown_group}
```

Takes an ISBN and returns its prefix.

Returns `{:error, :unknown_group}` when the ISBN is structurally valid but belongs
to a registration group not present in the dataset.

## Examples

    iex> Exisbn.fetch_prefix("9788535902778")
    {:ok, "978-85"}
    iex> Exisbn.fetch_prefix("2-1234-5680-2")
    {:ok, "978-2"}
    iex> Exisbn.fetch_prefix("str")
    {:error, :invalid_isbn}
    iex> Exisbn.fetch_prefix("9799012345674")
    {:error, :unknown_group}

# `fetch_prefix!`

```elixir
@spec fetch_prefix!(String.t()) :: String.t()
```

Same as `fetch_prefix/1`, but raises exception.

## Examples

    iex> Exisbn.fetch_prefix!("9788535902778")
    "978-85"
    iex> Exisbn.fetch_prefix!("2-1234-5680-2")
    "978-2"
    iex> Exisbn.fetch_prefix!("str")
    ** (ArgumentError) Invalid ISBN

    iex> Exisbn.fetch_prefix!("9799012345674")
    ** (ArgumentError) Unknown registration group

# `fetch_publication_element`

```elixir
@spec fetch_publication_element(String.t()) ::
  {:ok, String.t()}
  | {:error, :invalid_isbn | :unknown_group | :unknown_publisher}
```

Takes an ISBN and returns its publication element.

Propagates `{:error, :unknown_group}` and `{:error, :unknown_publisher}`
from `fetch_registrant_element/1`.

## Examples

    iex> Exisbn.fetch_publication_element("978-1-86197-876-9")
    {:ok, "876"}
    iex> Exisbn.fetch_publication_element("9789529351787")
    {:ok, "5178"}
    iex> Exisbn.fetch_publication_element("str")
    {:error, :invalid_isbn}
    iex> Exisbn.fetch_publication_element("9799012345674")
    {:error, :unknown_group}
    iex> Exisbn.fetch_publication_element("9786110000000")
    {:error, :unknown_publisher}

# `fetch_publication_element!`

```elixir
@spec fetch_publication_element!(String.t()) :: String.t()
```

Same as `fetch_publication_element/1`, but raises exception.

## Examples

    iex> Exisbn.fetch_publication_element!("978-1-86197-876-9")
    "876"
    iex> Exisbn.fetch_publication_element!("9789529351787")
    "5178"
    iex> Exisbn.fetch_publication_element!("str")
    ** (ArgumentError) Invalid ISBN

    iex> Exisbn.fetch_publication_element!("9799012345674")
    ** (ArgumentError) Unknown registration group

    iex> Exisbn.fetch_publication_element!("9786110000000")
    ** (ArgumentError) Unknown publisher

# `fetch_registrant_element`

```elixir
@spec fetch_registrant_element(String.t()) ::
  {:ok, String.t()}
  | {:error, :invalid_isbn | :unknown_group | :unknown_publisher}
```

Takes an ISBN and returns its registrant element.

Returns `{:error, :unknown_group}` when the registration group is not in the dataset,
and `{:error, :unknown_publisher}` when the group has no publisher ranges defined.

## Examples

    iex> Exisbn.fetch_registrant_element("9788535902778")
    {:ok, "359"}
    iex> Exisbn.fetch_registrant_element("978-1-86197-876-9")
    {:ok, "86197"}
    iex> Exisbn.fetch_registrant_element("9789529351787")
    {:ok, "93"}
    iex> Exisbn.fetch_registrant_element("str")
    {:error, :invalid_isbn}
    iex> Exisbn.fetch_registrant_element("9799012345674")
    {:error, :unknown_group}
    iex> Exisbn.fetch_registrant_element("9786110000000")
    {:error, :unknown_publisher}

# `fetch_registrant_element!`

```elixir
@spec fetch_registrant_element!(String.t()) :: String.t()
```

Same as `fetch_registrant_element/1`, but raises exception.

## Examples

    iex> Exisbn.fetch_registrant_element!("9788535902778")
    "359"
    iex> Exisbn.fetch_registrant_element!("978-1-86197-876-9")
    "86197"
    iex> Exisbn.fetch_registrant_element!("9789529351787")
    "93"
    iex> Exisbn.fetch_registrant_element!("str")
    ** (ArgumentError) Invalid ISBN

    iex> Exisbn.fetch_registrant_element!("9799012345674")
    ** (ArgumentError) Unknown registration group

    iex> Exisbn.fetch_registrant_element!("9786110000000")
    ** (ArgumentError) Unknown publisher

# `hyphenate`

```elixir
@spec hyphenate(String.t()) ::
  {:ok, String.t()}
  | {:error, :invalid_isbn | :unknown_group | :unknown_publisher}
```

Takes an ISBN (10 or 13) and hyphenates it.

## Examples

    iex> Exisbn.hyphenate("9788535902778")
    {:ok, "978-85-359-0277-8"}
    iex> Exisbn.hyphenate("0306406152")
    {:ok, "0-306-40615-2"}
    iex> Exisbn.hyphenate("str")
    {:error, :invalid_isbn}

# `hyphenate!`

```elixir
@spec hyphenate!(String.t()) :: String.t()
```

Same as `hyphenate/1`, but raises exception.

## Examples

    iex> Exisbn.hyphenate!("9788535902778")
    "978-85-359-0277-8"
    iex> Exisbn.hyphenate!("0306406152")
    "0-306-40615-2"
    iex> Exisbn.hyphenate!("str")
    ** (ArgumentError) Invalid ISBN

# `isbn10_checkdigit`

```elixir
@spec isbn10_checkdigit(String.t()) :: {:ok, String.t()} | {:error, :invalid_isbn}
```

Takes an ISBN 10 code as string, returns its check digit.

## Examples

    iex> Exisbn.isbn10_checkdigit("85-359-0277")
    {:ok, "5"}
    iex> Exisbn.isbn10_checkdigit("5-02-013850")
    {:ok, "9"}
    iex> Exisbn.isbn10_checkdigit("0str")
    {:error, :invalid_isbn}
    iex> Exisbn.isbn10_checkdigit("887385107")
    {:ok, "X"}

# `isbn10_checkdigit!`

```elixir
@spec isbn10_checkdigit!(String.t()) :: String.t()
```

Same as `isbn10_checkdigit/1`, but raises exception.

## Examples

    iex> Exisbn.isbn10_checkdigit!("85-359-0277")
    "5"
    iex> Exisbn.isbn10_checkdigit!("5-02-013850")
    "9"
    iex> Exisbn.isbn10_checkdigit!("0str")
    ** (ArgumentError) Invalid ISBN
    iex> Exisbn.isbn10_checkdigit!("887385107")
    "X"

# `isbn10_to_13`

```elixir
@spec isbn10_to_13(String.t()) :: {:ok, String.t()} | {:error, :invalid_isbn}
```

Takes an ISBN 10 and converts it to ISBN 13.

## Examples

    iex> Exisbn.isbn10_to_13("85-359-0277-5")
    {:ok, "9788535902778"}
    iex> Exisbn.valid?("9788535902778")
    true
    iex> Exisbn.isbn10_to_13("0306406152")
    {:ok, "9780306406157"}
    iex> Exisbn.valid?("9780306406157")
    true
    iex> Exisbn.isbn10_to_13("0-19-853453123")
    {:error, :invalid_isbn}

# `isbn10_to_13!`

```elixir
@spec isbn10_to_13!(String.t()) :: String.t()
```

Same as `isbn10_to_13/1`, but raises exception.

## Examples

    iex> Exisbn.isbn10_to_13!("85-359-0277-5")
    "9788535902778"
    iex> Exisbn.valid?("9788535902778")
    true
    iex> Exisbn.isbn10_to_13!("0306406152")
    "9780306406157"
    iex> Exisbn.valid?("9780306406157")
    true
    iex> Exisbn.isbn10_to_13!("0-19-853453123")
    ** (ArgumentError) Invalid ISBN

# `isbn13_checkdigit`

```elixir
@spec isbn13_checkdigit(binary()) :: {:ok, String.t()} | {:error, :invalid_isbn}
```

Takes an ISBN 13 code as string, returns its check digit.

## Examples

    iex> Exisbn.isbn13_checkdigit("978-5-12345-678")
    {:ok, "1"}
    iex> Exisbn.isbn13_checkdigit("978-0-306-40615")
    {:ok, "7"}
    iex> Exisbn.isbn13_checkdigit("0str")
    {:error, :invalid_isbn}

# `isbn13_checkdigit!`

```elixir
@spec isbn13_checkdigit!(binary()) :: String.t()
```

Same as `isbn13_checkdigit/1`, but raises exception.

## Examples

    iex> Exisbn.isbn13_checkdigit!("978-5-12345-678")
    "1"
    iex> Exisbn.isbn13_checkdigit!("978-0-306-40615")
    "7"
    iex> Exisbn.isbn13_checkdigit!("0str")
    ** (ArgumentError) Invalid ISBN

# `isbn13_prefix_group`

```elixir
@spec isbn13_prefix_group(String.t()) :: {:ok, String.t()} | {:error, :invalid_isbn}
```

Returns the GS1 prefix group (`"978"` or `"979"`) of a valid ISBN-13.

Returns `{:error, :invalid_isbn}` if the input is not a valid ISBN-13.
Accepts hyphenated or plain ISBN-13 strings. ISBN-10 is not accepted —
use `isbn10_to_13/1` first if needed.

## Examples

    iex> Exisbn.isbn13_prefix_group("9788535902778")
    {:ok, "978"}
    iex> Exisbn.isbn13_prefix_group("9798893031355")
    {:ok, "979"}
    iex> Exisbn.isbn13_prefix_group("978-85-359-0277-8")
    {:ok, "978"}
    iex> Exisbn.isbn13_prefix_group("85-359-0277-5")
    {:error, :invalid_isbn}
    iex> Exisbn.isbn13_prefix_group("str")
    {:error, :invalid_isbn}

# `isbn13_prefix_group!`

```elixir
@spec isbn13_prefix_group!(String.t()) :: String.t()
```

Same as `isbn13_prefix_group/1`, but raises exception.

## Examples

    iex> Exisbn.isbn13_prefix_group!("9788535902778")
    "978"
    iex> Exisbn.isbn13_prefix_group!("9798893031355")
    "979"
    iex> Exisbn.isbn13_prefix_group!("str")
    ** (ArgumentError) Invalid ISBN

# `isbn13_to_10`

```elixir
@spec isbn13_to_10(String.t()) ::
  {:ok, String.t()} | {:error, :invalid_isbn | :no_isbn10_equivalent}
```

Takes an ISBN 13 and converts it to ISBN 10.

ISBNs with prefix `979` have no ISBN-10 equivalent and return
`{:error, :no_isbn10_equivalent}`.

## Examples

    iex> Exisbn.isbn13_to_10("9788535902778")
    {:ok, "8535902775"}
    iex> Exisbn.valid?("8535902775")
    true
    iex> Exisbn.isbn13_to_10("9780306406157")
    {:ok, "0306406152"}
    iex> Exisbn.valid?("0306406152")
    true
    iex> Exisbn.isbn13_to_10("str")
    {:error, :invalid_isbn}
    iex> Exisbn.isbn13_to_10("9798893031355")
    {:error, :no_isbn10_equivalent}

# `isbn13_to_10!`

```elixir
@spec isbn13_to_10!(String.t()) :: String.t()
```

Same as `isbn13_to_10/1`, but raises exception.

## Examples

    iex> Exisbn.isbn13_to_10!("9788535902778")
    "8535902775"
    iex> Exisbn.valid?("8535902775")
    true
    iex> Exisbn.isbn13_to_10!("9780306406157")
    "0306406152"
    iex> Exisbn.valid?("0306406152")
    true
    iex> Exisbn.isbn13_to_10!("str")
    ** (ArgumentError) Invalid ISBN

    iex> Exisbn.isbn13_to_10!("9798893031355")
    ** (ArgumentError) No ISBN-10 equivalent

# `isbn_type`

```elixir
@spec isbn_type(String.t()) :: :isbn10 | :isbn13 | :invalid
```

Returns the type of the ISBN: `:isbn10`, `:isbn13`, or `:invalid`.

Does not require hyphens or any particular formatting — normalization is applied first.

## Examples

    iex> Exisbn.isbn_type("978-85-359-0277-8")
    :isbn13
    iex> Exisbn.isbn_type("85-359-0277-5")
    :isbn10
    iex> Exisbn.isbn_type("invalid")
    :invalid
    iex> Exisbn.isbn_type("9788535902778")
    :isbn13

# `normalize`

```elixir
@spec normalize(String.t()) :: String.t()
```

Normalizes an ISBN string by removing separators and uppercasing.

Strips hyphens, spaces, and any non-digit characters, then upcases the
result so the check-digit `x` becomes `X`. Returns a bare digit string
(plus optional trailing `X` for ISBN-10).

This function does **not** validate the ISBN — use `valid?/1` for that.

## Examples

    iex> Exisbn.normalize("978-85-359-0277-8")
    "9788535902778"

    iex> Exisbn.normalize("85-359-0277-5")
    "8535902775"

    iex> Exisbn.normalize("978 85 359 0277 8")
    "9788535902778"

    iex> Exisbn.normalize("887385107x")
    "887385107X"

    iex> Exisbn.normalize("9788535902778")
    "9788535902778"

# `publisher_country_code`

```elixir
@spec publisher_country_code(String.t()) ::
  {:ok, String.t() | nil} | {:error, :invalid_isbn | :unknown_group}
```

Takes an ISBN and returns its ISO 3166-1 alpha-2 country code.

Returns `{:ok, nil}` for groups that span multiple countries or
language areas (e.g. English language, French language, German language,
former U.S.S.R, Caribbean Community).

## Examples

    iex> Exisbn.publisher_country_code("9788535902778")
    {:ok, "BR"}
    iex> Exisbn.publisher_country_code("9780306406157")
    {:ok, nil}
    iex> Exisbn.publisher_country_code("str")
    {:error, :invalid_isbn}

# `publisher_country_code!`

```elixir
@spec publisher_country_code!(String.t()) :: String.t() | nil
```

Same as `publisher_country_code/1`, but raises exception.

## Examples

    iex> Exisbn.publisher_country_code!("9788535902778")
    "BR"
    iex> Exisbn.publisher_country_code!("9780306406157")
    nil
    iex> Exisbn.publisher_country_code!("str")
    ** (ArgumentError) Invalid ISBN

# `publisher_zone`

```elixir
@spec publisher_zone(String.t()) ::
  {:ok, String.t()} | {:error, :invalid_isbn | :unknown_group}
```

Takes an ISBN and returns its publisher zone.

Returns `{:error, :invalid_isbn}` for structurally invalid ISBNs and
`{:error, :unknown_group}` when the registration group is not in the dataset.

## Examples

    iex> Exisbn.publisher_zone("9788535902778")
    {:ok, "Brazil"}
    iex> Exisbn.publisher_zone("2-1234-5680-2")
    {:ok, "French language"}
    iex> Exisbn.publisher_zone("str")
    {:error, :invalid_isbn}
    iex> Exisbn.publisher_zone("9799012345674")
    {:error, :unknown_group}

# `publisher_zone!`

```elixir
@spec publisher_zone!(String.t()) :: String.t()
```

Same as `publisher_zone/1`, but raises exception.

## Examples

    iex> Exisbn.publisher_zone!("9788535902778")
    "Brazil"
    iex> Exisbn.publisher_zone!("2-1234-5680-2")
    "French language"
    iex> Exisbn.publisher_zone!("str")
    ** (ArgumentError) Invalid ISBN

    iex> Exisbn.publisher_zone!("9799012345674")
    ** (ArgumentError) Unknown registration group

# `valid?`

```elixir
@spec valid?(String.t()) :: boolean()
```

Takes an ISBN (10 or 13) and checks its validity by checking the checkdigit, length and characters.

## Examples

    iex> Exisbn.valid?("978-5-12345-678-1")
    true
    iex> Exisbn.valid?("978-5-12345-678")
    false
    iex> Exisbn.valid?("85-359-0277-5")
    true
    iex> Exisbn.valid?("85-359-0277")
    false

---

*Consult [api-reference.md](api-reference.md) for complete listing*
