defmodule Barlix.Code39 do
alias Barlix.Utils
@moduledoc """
This module implements the [Code
39](https://en.wikipedia.org/wiki/Code_39) symbology.
"""
@doc """
Encodes the given value using code 39 symbology. Only a subset of
ascii characters are supported.
## Options
* `:checksum` (boolean) - enables checksum. Defaults to `false`
"""
@spec encode(String.t() | charlist, Keyword.t()) :: {:error, binary} | {:ok, Barlix.code()}
def encode(value, options \\ []) do
Utils.normalize_string(value)
|> loop(Keyword.get(options, :checksum, false))
end
@doc """
Accepts the same arguments as `encode/2`. Returns `t:Barlix.code/0` or
raises `Barlix.Error` in case of invalid value.
"""
@spec encode!(String.t() | charlist, Keyword.t()) :: Barlix.code() | no_return
def encode!(value, options \\ []) do
case encode(value, options) do
{:ok, code} -> code
{:error, error} -> raise Barlix.Error, error
end
end
defp loop(value, use_checksum) do
with {:ok, c} <-
(if use_checksum do
checksum(value, 0)
else
{:ok, []}
end),
{:ok, encoded} <- encodings(value, start_symbol()),
encoded = [[encoded | c] | [0 | stop_symbol()]],
do: {:ok, {:D1, Utils.flatten(encoded)}}
end
defp checksum([], acc) do
c =
rem(acc, 43)
|> index_to_char
|> encoding
{:ok, [0 | c]}
end
defp checksum([h | t], acc) do
with i when is_number(i) <- char_to_index(h),
do: checksum(t, acc + i)
end
defp encodings([], acc), do: {:ok, acc}
defp encodings([h | t], acc) do
with e when is_list(e) <- encoding(h),
do: encodings(t, [acc | [0 | e]])
end
defp encoding(?0), do: [1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1]
defp encoding(?1), do: [1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1]
defp encoding(?2), do: [1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1]
defp encoding(?3), do: [1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1]
defp encoding(?4), do: [1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1]
defp encoding(?5), do: [1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1]
defp encoding(?6), do: [1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1]
defp encoding(?7), do: [1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]
defp encoding(?8), do: [1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1]
defp encoding(?9), do: [1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1]
defp encoding(?A), do: [1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1]
defp encoding(?B), do: [1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1]
defp encoding(?C), do: [1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1]
defp encoding(?D), do: [1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1]
defp encoding(?E), do: [1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1]
defp encoding(?F), do: [1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1]
defp encoding(?G), do: [1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1]
defp encoding(?H), do: [1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1]
defp encoding(?I), do: [1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1]
defp encoding(?J), do: [1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1]
defp encoding(?K), do: [1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1]
defp encoding(?L), do: [1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1]
defp encoding(?M), do: [1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1]
defp encoding(?N), do: [1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1]
defp encoding(?O), do: [1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1]
defp encoding(?P), do: [1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1]
defp encoding(?Q), do: [1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1]
defp encoding(?R), do: [1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1]
defp encoding(?S), do: [1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1]
defp encoding(?T), do: [1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1]
defp encoding(?U), do: [1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1]
defp encoding(?V), do: [1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1]
defp encoding(?W), do: [1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1]
defp encoding(?X), do: [1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1]
defp encoding(?Y), do: [1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]
defp encoding(?Z), do: [1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1]
defp encoding(?-), do: [1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1]
defp encoding(?.), do: [1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1]
defp encoding(?\s), do: [1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1]
defp encoding(?$), do: [1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1]
defp encoding(?/), do: [1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1]
defp encoding(?+), do: [1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1]
defp encoding(?%), do: [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]
defp encoding(invalid),
do: {:error, "Invalid character found #{IO.chardata_to_string([invalid])}"}
defp start_symbol, do: [1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1]
defp stop_symbol, do: start_symbol()
defp char_to_index(?0), do: 0
defp char_to_index(?1), do: 1
defp char_to_index(?2), do: 2
defp char_to_index(?3), do: 3
defp char_to_index(?4), do: 4
defp char_to_index(?5), do: 5
defp char_to_index(?6), do: 6
defp char_to_index(?7), do: 7
defp char_to_index(?8), do: 8
defp char_to_index(?9), do: 9
defp char_to_index(?A), do: 10
defp char_to_index(?B), do: 11
defp char_to_index(?C), do: 12
defp char_to_index(?D), do: 13
defp char_to_index(?E), do: 14
defp char_to_index(?F), do: 15
defp char_to_index(?G), do: 16
defp char_to_index(?H), do: 17
defp char_to_index(?I), do: 18
defp char_to_index(?J), do: 19
defp char_to_index(?K), do: 20
defp char_to_index(?L), do: 21
defp char_to_index(?M), do: 22
defp char_to_index(?N), do: 23
defp char_to_index(?O), do: 24
defp char_to_index(?P), do: 25
defp char_to_index(?Q), do: 26
defp char_to_index(?R), do: 27
defp char_to_index(?S), do: 28
defp char_to_index(?T), do: 29
defp char_to_index(?U), do: 30
defp char_to_index(?V), do: 31
defp char_to_index(?W), do: 32
defp char_to_index(?X), do: 33
defp char_to_index(?Y), do: 34
defp char_to_index(?Z), do: 35
defp char_to_index(?-), do: 36
defp char_to_index(?.), do: 37
defp char_to_index(?\s), do: 38
defp char_to_index(?$), do: 39
defp char_to_index(?/), do: 40
defp char_to_index(?+), do: 41
defp char_to_index(?%), do: 42
defp char_to_index(invalid),
do: {:error, "Invalid character found #{IO.chardata_to_string([invalid])}"}
defp index_to_char(0), do: ?0
defp index_to_char(1), do: ?1
defp index_to_char(2), do: ?2
defp index_to_char(3), do: ?3
defp index_to_char(4), do: ?4
defp index_to_char(5), do: ?5
defp index_to_char(6), do: ?6
defp index_to_char(7), do: ?7
defp index_to_char(8), do: ?8
defp index_to_char(9), do: ?9
defp index_to_char(10), do: ?A
defp index_to_char(11), do: ?B
defp index_to_char(12), do: ?C
defp index_to_char(13), do: ?D
defp index_to_char(14), do: ?E
defp index_to_char(15), do: ?F
defp index_to_char(16), do: ?G
defp index_to_char(17), do: ?H
defp index_to_char(18), do: ?I
defp index_to_char(19), do: ?J
defp index_to_char(20), do: ?K
defp index_to_char(21), do: ?L
defp index_to_char(22), do: ?M
defp index_to_char(23), do: ?N
defp index_to_char(24), do: ?O
defp index_to_char(25), do: ?P
defp index_to_char(26), do: ?Q
defp index_to_char(27), do: ?R
defp index_to_char(28), do: ?S
defp index_to_char(29), do: ?T
defp index_to_char(30), do: ?U
defp index_to_char(31), do: ?V
defp index_to_char(32), do: ?W
defp index_to_char(33), do: ?X
defp index_to_char(34), do: ?Y
defp index_to_char(35), do: ?Z
defp index_to_char(36), do: ?-
defp index_to_char(37), do: ?.
defp index_to_char(38), do: ?\s
defp index_to_char(39), do: ?$
defp index_to_char(40), do: ?/
defp index_to_char(41), do: ?+
defp index_to_char(42), do: ?%
end