diff --git a/lib/qr_code/format_version.ex b/lib/qr_code/format_version.ex index 508673b..5744141 100644 --- a/lib/qr_code/format_version.ex +++ b/lib/qr_code/format_version.ex @@ -93,7 +93,7 @@ defmodule QRCode.FormatVersion do 40 => [[1, 0, 0], [1, 0, 1], [1, 0, 0], [0, 1, 1], [0, 0, 0], [1, 0, 1]] } - @spec put_information(QR.t()) :: Result.t(String.t(), QR.t()) + @spec put_information(QR.t()) :: {:ok, QR.t()} | {:error, String.t()} def put_information( %QR{matrix: matrix, version: version, ecc_level: ecc_level, mask_num: mask_num} = qr ) @@ -105,7 +105,7 @@ defmodule QRCode.FormatVersion do end @spec set_format_info(Matrix.t(), QR.level(), QR.mask_num(), QR.version()) :: - Result.t(String.t(), Matrix.t()) + {:ok, Matrix.t()} | {:error, String.t()} def set_format_info(matrix, table_level, mask_num, version) do {row_left, row_right, col_top, col_bottom} = information_string(table_level, mask_num) @@ -116,7 +116,7 @@ defmodule QRCode.FormatVersion do |> Result.and_then(&Matrix.update_col(&1, col_bottom, {4 * version + 10, 8})) end - @spec set_version_info(Matrix.t(), QR.version()) :: Result.t(String.t(), Matrix.t()) + @spec set_version_info(Matrix.t(), QR.version()) :: {:ok, Matrix.t()} | {:error, String.t()} def set_version_info(matrix, version) when version < 7 do Result.ok(matrix) end diff --git a/lib/qr_code/mode.ex b/lib/qr_code/mode.ex index e7c9cd8..a4ce137 100644 --- a/lib/qr_code/mode.ex +++ b/lib/qr_code/mode.ex @@ -6,7 +6,7 @@ defmodule QRCode.Mode do alias QRCode.QR import QRCode.QR, only: [mode: 1] - @spec select(QR.t(), QR.mode()) :: Result.t(String.t(), QR.t()) + @spec select(QR.t(), QR.mode()) :: {:ok, QR.t()} | {:error, String.t()} def select(qr, mode) when mode(mode) do qr |> put_mode(mode) diff --git a/lib/qr_code/placement.ex b/lib/qr_code/placement.ex index d12bde2..b51932c 100644 --- a/lib/qr_code/placement.ex +++ b/lib/qr_code/placement.ex @@ -71,7 +71,7 @@ defmodule QRCode.Placement do @correct_separator Vector.row(8) - @spec put_patterns(QR.t()) :: Result.t(String.t(), QR.t()) + @spec put_patterns(QR.t()) :: {:ok, QR.t()} | {:error, String.t()} def put_patterns(%QR{version: version, message: message} = qr) when version(version) do size = (version - 1) * 4 + 21 @@ -87,7 +87,7 @@ defmodule QRCode.Placement do |> Result.map(fn matrix -> %{qr | matrix: matrix} end) end - @spec replace_placeholders(QR.t()) :: Result.t(String.t(), QR.t()) + @spec replace_placeholders(QR.t()) :: {:ok, QR.t()} | {:error, String.t()} def replace_placeholders(%QR{matrix: matrix, version: version} = qr) when version(version) do matrix |> add_finders(version) @@ -99,7 +99,8 @@ defmodule QRCode.Placement do |> Result.map(fn matrix -> %{qr | matrix: matrix} end) end - @spec add_finders(Matrix.t(), QR.version(), Matrix.t()) :: Result.t(String.t(), Matrix.t()) + @spec add_finders(Matrix.t(), QR.version(), Matrix.t()) :: + {:ok, Matrix.t()} | {:error, String.t()} def add_finders(matrix, version, finder \\ @correct_finder) do matrix |> Matrix.update(finder, {0, 0}) @@ -107,7 +108,8 @@ defmodule QRCode.Placement do |> Result.and_then(&Matrix.update(&1, finder, {4 * version + 10, 0})) end - @spec add_separators(Matrix.t(), QR.version(), Vector.t()) :: Result.t(String.t(), Matrix.t()) + @spec add_separators(Matrix.t(), QR.version(), Vector.t()) :: + {:ok, Matrix.t()} | {:error, String.t()} def add_separators(matrix, version, row \\ @correct_separator) do col = Vector.transpose(row) @@ -121,7 +123,7 @@ defmodule QRCode.Placement do end @spec add_reserved_areas(Matrix.t(), QR.version(), non_neg_integer()) :: - Result.t(String.t(), Matrix.t()) + {:ok, Matrix.t()} | {:error, String.t()} def add_reserved_areas(matrix, version, val \\ 0) def add_reserved_areas(matrix, version, val) when version < 7 do @@ -134,7 +136,7 @@ defmodule QRCode.Placement do |> add_reserve_via(version, val) end - @spec add_timings(Matrix.t(), QR.version()) :: Result.t(String.t(), Matrix.t()) + @spec add_timings(Matrix.t(), QR.version()) :: {:ok, Matrix.t()} | {:error, String.t()} def add_timings(matrix, version) do row = get_timing_row(version) @@ -142,7 +144,8 @@ defmodule QRCode.Placement do |> Result.and_then_x(&Matrix.update_col(&1, Vector.transpose(&2), {8, 6})) end - @spec add_timings(Matrix.t(), QR.version(), pos_integer()) :: Result.t(String.t(), Matrix.t()) + @spec add_timings(Matrix.t(), QR.version(), pos_integer()) :: + {:ok, Matrix.t()} | {:error, String.t()} def add_timings(matrix, version, val) do size = 4 * version + 1 row = size |> Vector.row(val) @@ -152,7 +155,8 @@ defmodule QRCode.Placement do |> Result.and_then(&Matrix.update_col(&1, Vector.transpose(row), {8, 6})) end - @spec add_alignments(Matrix.t(), QR.version(), Matrix.t()) :: Result.t(String.t(), Matrix.t()) + @spec add_alignments(Matrix.t(), QR.version(), Matrix.t()) :: + {:ok, Matrix.t()} | {:error, String.t()} def add_alignments(matrix, version, alignment \\ @correct_alignment) def add_alignments(matrix, 1, _alignment), do: Result.ok(matrix) @@ -174,7 +178,7 @@ defmodule QRCode.Placement do end @spec add_dark_module(Matrix.t(), QR.version(), pos_integer()) :: - Result.t(String.t(), Matrix.t()) + {:ok, Matrix.t()} | {:error, String.t()} def add_dark_module(matrix, version, val \\ 1) do Matrix.update_element(matrix, val, {4 * version + 9, 8}) end diff --git a/lib/qr_code/qr.ex b/lib/qr_code/qr.ex index c119fdd..591f9a1 100644 --- a/lib/qr_code/qr.ex +++ b/lib/qr_code/qr.ex @@ -107,13 +107,13 @@ defmodule QRCode.QR do For saving QR code to svg file, you have to render it first and then save it: - iex> qr = QRCode.QR.create("Hello World", :high) + iex> {:ok, qr} = QRCode.QR.create("Hello World", :high) iex> qr |> QRCode.render() |> QRCode.save("hello.svg") {:ok, "hello.svg"} The svg file will be saved into your project directory. """ - @spec create(String.t(), level(), mode()) :: Result.t(String.t(), t()) + @spec create(String.t(), level(), mode()) :: {:ok, t()} | {:error, String.t()} def create(orig, level \\ :low, mode \\ :byte) when level(level) and mode(mode) do %__MODULE__{orig: orig, ecc_level: level} |> QRCode.Mode.select(mode) diff --git a/lib/qr_code/render.ex b/lib/qr_code/render.ex index f4cbd75..b06113e 100644 --- a/lib/qr_code/render.ex +++ b/lib/qr_code/render.ex @@ -3,12 +3,15 @@ defmodule QRCode.Render do Render common module. """ + @type image_format() :: :png | :svg + + alias QRCode.QR alias QRCode.Render.{PngSettings, SvgSettings} @doc """ Render QR matrix to `svg` or `png` binary representation with default settings. """ - @spec render(Result.t(String.t(), binary()), :png | :svg) :: Result.t(String.t(), binary()) + @spec render(QR.t(), image_format()) :: String.t() def render(qr, :svg) do render(qr, :svg, %SvgSettings{}) end @@ -44,8 +47,7 @@ defmodule QRCode.Render do | qrcode_color | string or {r, g, b} | "#000000" | sets color of QR | ``` """ - @spec render(Result.t(String.t(), binary()), atom(), SvgSettings.t() | PngSettings.t()) :: - Result.t(String.t(), binary()) + @spec render(QR.t(), image_format(), SvgSettings.t() | PngSettings.t()) :: String.t() def render(qr, :svg, settings) do QRCode.Render.Svg.create(qr, settings) end @@ -68,21 +70,17 @@ defmodule QRCode.Render do QR code ``` """ - @spec to_base64(Result.t(String.t(), binary())) :: Result.t(String.t(), binary()) - def to_base64({:ok, rendered_qr_matrix}) do - rendered_qr_matrix - |> Base.encode64() - |> Result.ok() + @spec to_base64(String.t()) :: String.t() + def to_base64(rendered_qr_matrix) do + Base.encode64(rendered_qr_matrix) end - def to_base64(error), do: error - @doc """ Saves rendered QR code to `svg` or `png` file. See a few examples below: ```elixir iex> "Hello World" - |> QRCode.create(:high) + |> QRCode.create!(:high) |> QRCode.render(:svg) |> QRCode.save("/path/to/hello.svg") {:ok, "/path/to/hello.svg"} @@ -91,21 +89,18 @@ defmodule QRCode.Render do ```elixir iex> png_settings = %QRCode.Render.PngSettings{qrcode_color: {17, 170, 136}} iex> "Hello World" - |> QRCode.create(:high) + |> QRCode.create!(:high) |> QRCode.render(:png, png_settings) |> QRCode.save("/tmp/to/hello.png") {:ok, "/path/to/hello.png"} ``` ![QR code color](docs/qrcode_color.png) """ - @spec save(Result.t(any(), binary()), Path.t()) :: - Result.t(String.t() | File.posix() | :badarg | :terminated, Path.t()) - def save({:ok, rendered_qr_matrix}, path_with_file_name) do + @spec save(String.t(), Path.t()) :: {:ok, Path.t()} | {:error, File.posix()} + def save(rendered_qr_matrix, path_with_file_name) do case File.write(path_with_file_name, rendered_qr_matrix) do :ok -> {:ok, path_with_file_name} err -> err end end - - def save(error, _path_with_file_name), do: error end diff --git a/lib/qr_code/render/png.ex b/lib/qr_code/render/png.ex index 22860fa..cc1617f 100644 --- a/lib/qr_code/render/png.ex +++ b/lib/qr_code/render/png.ex @@ -10,15 +10,11 @@ defmodule QRCode.Render.Png do @doc """ Create Png image from QR matrix as binary. """ - @spec create(Result.t(String.t(), QR.t()), PngSettings.t()) :: Result.t(String.t(), binary()) - def create({:ok, %QR{matrix: matrix}}, settings) do - matrix - |> create_png(settings) - |> Result.ok() + @spec create(QR.t(), PngSettings.t()) :: String.t() + def create(%QR{matrix: matrix}, settings) do + create_png(matrix, settings) end - def create(error, _settings), do: error - # Private defp create_png( diff --git a/lib/qr_code/render/svg.ex b/lib/qr_code/render/svg.ex index 6406a6b..780d6d2 100644 --- a/lib/qr_code/render/svg.ex +++ b/lib/qr_code/render/svg.ex @@ -27,23 +27,15 @@ defmodule QRCode.Render.Svg do Create Svg structure from QR matrix as binary. This binary contains svg attributes and svg elements. """ - @spec create(Result.t(String.t(), QR.t()), SvgSettings.t()) :: Result.t(String.t(), binary()) - def create({:ok, %QR{matrix: matrix}}, settings) do - matrix - |> create_svg(settings) - |> Result.ok() - end - - def create(error, _settings), do: error - - # Private - - defp create_svg(matrix, settings) do + @spec create(QR.t(), SvgSettings.t()) :: String.t() + def create(%QR{matrix: matrix}, settings) do matrix |> construct_body(%__MODULE__{}, settings) |> construct_svg(settings) end + # Private + defp construct_body(matrix, svg, %SvgSettings{scale: scale}) do {rank_matrix, _} = Matrix.size(matrix) diff --git a/test/png_test.exs b/test/png_test.exs index c3d6911..daf77ec 100644 --- a/test/png_test.exs +++ b/test/png_test.exs @@ -11,7 +11,7 @@ defmodule PngTest do describe "Png" do setup do @text - |> QRCode.create() + |> QRCode.create!() |> QRCode.render(:png) |> QRCode.save(@dst_to_file) @@ -20,22 +20,14 @@ defmodule PngTest do end) end - test "render should fail with error" do - rv = - Result.error("Error") - |> QRCode.render(:png) - - assert rv == {:error, "Error"} - end - test "should save qr code to png file" do assert File.exists?(@dst_to_file) end test "should create png from qr matrix" do - {:ok, expected} = + expected = @text - |> QRCode.create() + |> QRCode.create!() |> QRCode.render(:png) rv = @@ -46,35 +38,33 @@ defmodule PngTest do end test "should encode png binary to base64" do - {:ok, expected} = + rendered_qr = @text - |> QRCode.create() + |> QRCode.create!() |> QRCode.render(:png) - {:ok, rv} = - @text - |> QRCode.create() - |> QRCode.render(:png) + rv = + rendered_qr |> QRCode.to_base64() - |> Result.and_then(&Base.decode64/1) + |> Base.decode64!() - assert expected == rv + assert rv == rendered_qr end test "file should contain different qr code color than black" do expected = @text - |> QRCode.create() + |> QRCode.create!() |> QRCode.render( :png, %PngSettings{qrcode_color: {17, 170, 136}} ) - QRCode.save(expected, @dst_to_file) + {:ok, _} = QRCode.save(expected, @dst_to_file) rv = @dst_to_file - |> File.read() + |> File.read!() assert expected == rv end diff --git a/test/qr_code_test.exs b/test/qr_code_test.exs index b8922a7..d922995 100644 --- a/test/qr_code_test.exs +++ b/test/qr_code_test.exs @@ -1,9 +1,7 @@ defmodule QRCodeTest do @moduledoc false - use ExUnit.Case, async: true - - @dst_to_file "/tmp/hello.svg" + use ExUnit.Case describe "QRCode" do test "should create! QR" do @@ -24,27 +22,36 @@ defmodule QRCodeTest do test "should fail to save qr code bc of wrong file name" do rv = "text" - |> QRCode.create() + |> QRCode.create!() |> QRCode.render() |> QRCode.save("/") assert rv == {:error, :eisdir} end - test "should fail to save qr code to file" do - rv = - Result.error("Error") - |> QRCode.save(@dst_to_file) - - assert rv == {:error, "Error"} - end - - test "should fail to encode qr to base 64" do - rv = - Result.error("Error") - |> QRCode.to_base64() - - assert rv == {:error, "Error"} + test "should raise error when embedded image has not supported mime type" do + assert_raise ArgumentError, "Bad embedded image format!", fn -> + wrong_img_format = "/tmp/embedded_img.xxx" + text = "HELLO WOLRD" + + on_exit(fn -> + :ok = File.rm(wrong_img_format) + end) + + {:ok, _} = + text + |> QRCode.create!() + |> QRCode.render(:png) + |> QRCode.save(wrong_img_format) + + settings = %QRCode.Render.SvgSettings{ + image: {wrong_img_format, 100} + } + + text + |> QRCode.create!() + |> QRCode.render(:svg, settings) + end end end end diff --git a/test/qr_test.exs b/test/qr_test.exs index e67d214..6c3b6b9 100644 --- a/test/qr_test.exs +++ b/test/qr_test.exs @@ -10,28 +10,4 @@ defmodule QRTest do 5000 |> :crypto.strong_rand_bytes() |> QR.create!() end end - - test "should raise error when embedded image has not supported mime type" do - assert_raise ArgumentError, "Bad embedded image format!", fn -> - wrong_img_format = "/tmp/embedded_img.xxx" - text = "HELLO WOLRD" - - on_exit(fn -> - :ok = File.rm(wrong_img_format) - end) - - text - |> QRCode.create() - |> QRCode.render(:png) - |> QRCode.save(wrong_img_format) - - settings = %QRCode.Render.SvgSettings{ - image: {wrong_img_format, 100} - } - - text - |> QRCode.create() - |> QRCode.render(:svg, settings) - end - end end diff --git a/test/svg_test.exs b/test/svg_test.exs index 6509f67..21c0f99 100644 --- a/test/svg_test.exs +++ b/test/svg_test.exs @@ -15,7 +15,7 @@ defmodule SvgTest do describe "Svg" do setup do @text - |> QRCode.create() + |> QRCode.create!() |> QRCode.render(:svg, %SvgSettings{structure: :readable}) |> QRCode.save(@dst_to_file) @@ -24,22 +24,14 @@ defmodule SvgTest do end) end - test "render should fail with error" do - rv = - Result.error("Error") - |> QRCode.render() - - assert rv == {:error, "Error"} - end - test "should save qr code to svg file" do assert File.exists?(@dst_to_file) end test "should create svg from qr matrix" do - {:ok, expected} = + expected = @text - |> QRCode.create() + |> QRCode.create!() |> QRCode.render(:svg, %SvgSettings{structure: :readable}) rv = @@ -50,19 +42,17 @@ defmodule SvgTest do end test "should encode svg binary to base64" do - {:ok, expected} = + rendered_qr = @text - |> QRCode.create() - |> QRCode.render() + |> QRCode.create!() + |> QRCode.render(:svg) - {:ok, rv} = - @text - |> QRCode.create() - |> QRCode.render() + rv = + rendered_qr |> QRCode.to_base64() - |> Result.and_then(&Base.decode64/1) + |> Base.decode64!() - assert expected == rv + assert rv == rendered_qr end test "file should contain xmlns and xlink attributes" do @@ -86,13 +76,14 @@ defmodule SvgTest do end test "file should contain background opacity" do - @text - |> QRCode.create() - |> QRCode.render( - :svg, - %SvgSettings{background_opacity: 0, structure: :readable} - ) - |> QRCode.save(@dst_to_file) + {:ok, _} = + @text + |> QRCode.create!() + |> QRCode.render( + :svg, + %SvgSettings{background_opacity: 0, structure: :readable} + ) + |> QRCode.save(@dst_to_file) rv = @dst_to_file @@ -110,20 +101,22 @@ defmodule SvgTest do :ok = File.rm(png_image) end) - @text - |> QRCode.create() - |> QRCode.render(:png) - |> QRCode.save(png_image) + {:ok, _} = + @text + |> QRCode.create!() + |> QRCode.render(:png) + |> QRCode.save(png_image) settings = %SvgSettings{ image: {png_image, 100}, structure: :readable } - @text - |> QRCode.create() - |> QRCode.render(:svg, settings) - |> QRCode.save(@dst_to_file) + {:ok, _} = + @text + |> QRCode.create!() + |> QRCode.render(:svg, settings) + |> QRCode.save(@dst_to_file) rv = @dst_to_file @@ -135,13 +128,14 @@ defmodule SvgTest do end test "file should contain different qr code color than black" do - @text - |> QRCode.create() - |> QRCode.render( - :svg, - %SvgSettings{qrcode_color: {17, 170, 136}, structure: :readable} - ) - |> QRCode.save(@dst_to_file) + {:ok, _} = + @text + |> QRCode.create!() + |> QRCode.render( + :svg, + %SvgSettings{qrcode_color: {17, 170, 136}, structure: :readable} + ) + |> QRCode.save(@dst_to_file) rv = @dst_to_file