-module(qrkit).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/qrkit.gleam").
-export([package_version/0, new/1, with_ecc/2, with_exact_version/2, with_min_version/2, with_eci/2, with_symbol/2, with_mode_preference/2, build/1, encode/1, encode_split_with/3, encode_split/2, version/1, size/1, width/1, height/1, symbol_size/1, error_correction/1, error_correction_designator/1, symbol/1, mask/1, rows/1, module_at/3]).
-export_type([builder/0, qr_code/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(" Public API for the qrkit package.\n").
-opaque builder() :: {builder,
binary(),
qrkit@types:error_correction(),
gleam@option:option(integer()),
gleam@option:option(integer()),
qrkit@types:symbol(),
qrkit@types:mode_preference()}.
-opaque qr_code() :: {qr_code,
integer(),
integer(),
integer(),
qrkit@types:error_correction(),
qrkit@types:symbol(),
integer(),
list(list(boolean()))}.
-file("src/qrkit.gleam", 67).
?DOC(" The package version.\n").
-spec package_version() -> binary().
package_version() ->
<<"0.1.0"/utf8>>.
-file("src/qrkit.gleam", 72).
?DOC(" Create a new builder from input text.\n").
-spec new(binary()) -> builder().
new(Data) ->
{builder, Data, medium, none, none, standard, auto}.
-file("src/qrkit.gleam", 82).
?DOC(" Set the desired error correction level.\n").
-spec with_ecc(builder(), qrkit@types:error_correction()) -> builder().
with_ecc(Builder, Ecc) ->
{builder, Data, _, Min_version, Eci, Symbol, Preference} = Builder,
{builder, Data, Ecc, Min_version, Eci, Symbol, Preference}.
-file("src/qrkit.gleam", 98).
?DOC(
" Pin the symbol version exactly.\n"
"\n"
" The value is interpreted relative to the active symbol family: Standard QR\n"
" accepts 1..40, Micro QR accepts 1..4 (M1..M4), and rMQR accepts 1..32\n"
" (R7x43..R17x139).\n"
"\n"
" If the payload, mode, or ECC level cannot be satisfied at the requested\n"
" version, `build` returns `Error(DataExceedsCapacity)` or\n"
" `Error(IncompatibleOptions)` instead of bumping to a larger version.\n"
" When no exact version is configured, the encoder selects the smallest\n"
" version that fits the payload.\n"
).
-spec with_exact_version(builder(), integer()) -> builder().
with_exact_version(Builder, Version) ->
{builder, Data, Ecc, _, Eci, Symbol, Preference} = Builder,
{builder, Data, Ecc, {some, Version}, Eci, Symbol, Preference}.
-file("src/qrkit.gleam", 107).
?DOC(
" Compatibility alias for [`with_exact_version`](#with_exact_version).\n"
"\n"
" Despite the historical name, this pins the symbol version exactly rather\n"
" than setting a lower bound. New code should prefer `with_exact_version`.\n"
).
-spec with_min_version(builder(), integer()) -> builder().
with_min_version(Builder, Min_version) ->
with_exact_version(Builder, Min_version).
-file("src/qrkit.gleam", 116).
?DOC(
" Add an optional ECI assignment designator before the data segments.\n"
"\n"
" ECI is only supported for Standard QR. Valid designators are in the range\n"
" 0..999999; invalid values surface as `Error(InvalidEciDesignator(..))`\n"
" during `build`.\n"
).
-spec with_eci(builder(), integer()) -> builder().
with_eci(Builder, Designator) ->
{builder, Data, Ecc, Min_version, _, Symbol, Preference} = Builder,
{builder, Data, Ecc, Min_version, {some, Designator}, Symbol, Preference}.
-file("src/qrkit.gleam", 122).
?DOC(" Select the symbol family.\n").
-spec with_symbol(builder(), qrkit@types:symbol()) -> builder().
with_symbol(Builder, Symbol) ->
{builder, Data, Ecc, Min_version, Eci, _, Preference} = Builder,
{builder, Data, Ecc, Min_version, Eci, Symbol, Preference}.
-file("src/qrkit.gleam", 128).
?DOC(" Change the mode optimisation strategy.\n").
-spec with_mode_preference(builder(), qrkit@types:mode_preference()) -> builder().
with_mode_preference(Builder, Preference) ->
{builder, Data, Ecc, Min_version, Eci, Symbol, _} = Builder,
{builder, Data, Ecc, Min_version, Eci, Symbol, Preference}.
-file("src/qrkit.gleam", 204).
-spec validate_min_version(gleam@option:option(integer()), qrkit@types:symbol()) -> {ok,
nil} |
{error, qrkit@error:encode_error()}.
validate_min_version(Min_version, Symbol) ->
case Min_version of
none ->
{ok, nil};
{some, Value} ->
Upper = case Symbol of
standard ->
40;
micro ->
4;
rectangular ->
32
end,
case (Value < 1) orelse (Value > Upper) of
true ->
{error, {invalid_version, Value}};
false ->
{ok, nil}
end
end.
-file("src/qrkit.gleam", 224).
-spec validate_eci(gleam@option:option(integer()), qrkit@types:symbol()) -> {ok,
nil} |
{error, qrkit@error:encode_error()}.
validate_eci(Eci, Symbol) ->
case Eci of
none ->
{ok, nil};
{some, Designator} ->
case (Designator < 0) orelse (Designator > 999999) of
true ->
{error, {invalid_eci_designator, Designator}};
false ->
case Symbol =:= standard of
true ->
{ok, nil};
false ->
{error,
{incompatible_options,
<<"ECI is only supported for Standard QR"/utf8>>}}
end
end
end.
-file("src/qrkit.gleam", 193).
-spec validate_builder_options(
gleam@option:option(integer()),
gleam@option:option(integer()),
qrkit@types:symbol()
) -> {ok, nil} | {error, qrkit@error:encode_error()}.
validate_builder_options(Min_version, Eci, Symbol) ->
case validate_min_version(Min_version, Symbol) of
{error, Error} ->
{error, Error};
{ok, nil} ->
validate_eci(Eci, Symbol)
end.
-file("src/qrkit.gleam", 137).
?DOC(" Build a QR code from the accumulated builder configuration.\n").
-spec build(builder()) -> {ok, qr_code()} | {error, qrkit@error:encode_error()}.
build(Builder) ->
{builder, Data, Ecc, Min_version, Eci, Symbol, Preference} = Builder,
case Data =:= <<""/utf8>> of
true ->
{error, empty_input};
false ->
case validate_builder_options(Min_version, Eci, Symbol) of
{error, Error} ->
{error, Error};
{ok, nil} ->
case Symbol of
standard ->
case qrkit@internal@standard:encode(
Data,
Ecc,
Min_version,
Eci,
Preference
) of
{ok, Encoded} ->
{ok,
{qr_code,
qrkit@internal@standard:version(
Encoded
),
qrkit@internal@standard:width(
Encoded
),
qrkit@internal@standard:height(
Encoded
),
Ecc,
Symbol,
qrkit@internal@standard:mask(
Encoded
),
qrkit@internal@standard:rows(
Encoded
)}};
{error, Encode_error} ->
{error, Encode_error}
end;
micro ->
case qrkit@internal@micro:encode(
Data,
Ecc,
Min_version,
Preference
) of
{ok, Encoded@1} ->
{ok,
{qr_code,
qrkit@internal@micro:version(
Encoded@1
),
qrkit@internal@micro:width(
Encoded@1
),
qrkit@internal@micro:height(
Encoded@1
),
Ecc,
Symbol,
qrkit@internal@micro:mask(Encoded@1),
qrkit@internal@micro:rows(Encoded@1)}};
{error, Encode_error@1} ->
{error, Encode_error@1}
end;
rectangular ->
case qrkit@internal@rmqr:encode(
Data,
Ecc,
Min_version,
Preference
) of
{ok, Encoded@2} ->
{ok,
{qr_code,
qrkit@internal@rmqr:version(
Encoded@2
),
qrkit@internal@rmqr:width(Encoded@2),
qrkit@internal@rmqr:height(
Encoded@2
),
Ecc,
Symbol,
qrkit@internal@rmqr:mask(Encoded@2),
qrkit@internal@rmqr:rows(Encoded@2)}};
{error, Encode_error@2} ->
{error, Encode_error@2}
end
end
end
end.
-file("src/qrkit.gleam", 77).
?DOC(" Encode input text using the default builder configuration.\n").
-spec encode(binary()) -> {ok, qr_code()} | {error, qrkit@error:encode_error()}.
encode(Data) ->
_pipe = new(Data),
build(_pipe).
-file("src/qrkit.gleam", 257).
?DOC(" Same as `encode_split` but with a caller-chosen error correction level.\n").
-spec encode_split_with(binary(), integer(), qrkit@types:error_correction()) -> {ok,
list(qr_code())} |
{error, qrkit@error:encode_error()}.
encode_split_with(Data, Max_version, Ecc) ->
case qrkit@internal@structured_append:encode(Data, Max_version, Ecc) of
{error, Error} ->
{error, Error};
{ok, Encodes} ->
{ok,
gleam@list:map(
Encodes,
fun(Encoded) ->
{qr_code,
qrkit@internal@standard:version(Encoded),
qrkit@internal@standard:width(Encoded),
qrkit@internal@standard:height(Encoded),
Ecc,
standard,
qrkit@internal@standard:mask(Encoded),
qrkit@internal@standard:rows(Encoded)}
end
)}
end.
-file("src/qrkit.gleam", 249).
?DOC(
" Split data into multiple symbols using Structured Append (ISO/IEC 18004 §8.2).\n"
"\n"
" Each returned QR carries the 20-bit Structured Append header so a compliant\n"
" reader can reassemble the original message. When `data` fits in a single QR\n"
" at `max_version`, the returned list contains exactly one symbol with no SA\n"
" header. Uses the Medium error correction level — call `encode_split_with`\n"
" for a different level.\n"
).
-spec encode_split(binary(), integer()) -> {ok, list(qr_code())} |
{error, qrkit@error:encode_error()}.
encode_split(Data, Max_version) ->
encode_split_with(Data, Max_version, medium).
-file("src/qrkit.gleam", 283).
?DOC(
" Return the symbol version number. Standard QR returns 1..40, Micro QR 1..4\n"
" (M1..M4), and rMQR 1..32 (R7x43..R17x139).\n"
).
-spec version(qr_code()) -> integer().
version(Qr) ->
{qr_code, Version, _, _, _, _, _, _} = Qr,
Version.
-file("src/qrkit.gleam", 290).
?DOC(
" Return the symbol side length in modules. For non-square rMQR symbols this\n"
" is the width; use `width` and `height` for the explicit dimensions.\n"
).
-spec size(qr_code()) -> integer().
size(Qr) ->
{qr_code, _, Width, _, _, _, _, _} = Qr,
Width.
-file("src/qrkit.gleam", 296).
?DOC(" Return the symbol width in modules.\n").
-spec width(qr_code()) -> integer().
width(Qr) ->
{qr_code, _, Width, _, _, _, _, _} = Qr,
Width.
-file("src/qrkit.gleam", 302).
?DOC(" Return the symbol height in modules.\n").
-spec height(qr_code()) -> integer().
height(Qr) ->
{qr_code, _, _, Height, _, _, _, _} = Qr,
Height.
-file("src/qrkit.gleam", 308).
?DOC(" Return the symbol dimensions in modules.\n").
-spec symbol_size(qr_code()) -> {integer(), integer()}.
symbol_size(Qr) ->
{width(Qr), height(Qr)}.
-file("src/qrkit.gleam", 313).
?DOC(" Return the error correction level used by this symbol.\n").
-spec error_correction(qr_code()) -> qrkit@types:error_correction().
error_correction(Qr) ->
{qr_code, _, _, _, Ecc, _, _, _} = Qr,
Ecc.
-file("src/qrkit.gleam", 319).
?DOC(" Return the canonical single-letter ECC designator.\n").
-spec error_correction_designator(qrkit@types:error_correction()) -> binary().
error_correction_designator(Ecc) ->
case Ecc of
low ->
<<"L"/utf8>>;
medium ->
<<"M"/utf8>>;
quartile ->
<<"Q"/utf8>>;
high ->
<<"H"/utf8>>
end.
-file("src/qrkit.gleam", 329).
?DOC(" Return the symbol family used by this QR code.\n").
-spec symbol(qr_code()) -> qrkit@types:symbol().
symbol(Qr) ->
{qr_code, _, _, _, _, Symbol, _, _} = Qr,
Symbol.
-file("src/qrkit.gleam", 336).
?DOC(
" Return the mask pattern that was applied. Standard QR returns 0..7,\n"
" Micro QR 0..3, and rMQR always 4 (rMQR uses a single fixed mask).\n"
).
-spec mask(qr_code()) -> integer().
mask(Qr) ->
{qr_code, _, _, _, _, _, Mask, _} = Qr,
Mask.
-file("src/qrkit.gleam", 357).
?DOC(" Return the symbol matrix as rows of booleans.\n").
-spec rows(qr_code()) -> list(list(boolean())).
rows(Qr) ->
{qr_code, _, _, _, _, _, _, Rows} = Qr,
Rows.
-file("src/qrkit.gleam", 345).
?DOC(
" Return a single module from the symbol matrix.\n"
"\n"
" Returns `Error(ModuleOutOfBounds(..))` when `x` or `y` fall outside the\n"
" matrix dimensions.\n"
).
-spec module_at(qr_code(), integer(), integer()) -> {ok, boolean()} |
{error, qrkit@error:matrix_access_error()}.
module_at(Qr, X, Y) ->
case begin
_pipe = rows(Qr),
qrkit@internal@util:at(_pipe, Y)
end of
{ok, Row} ->
case qrkit@internal@util:at(Row, X) of
{ok, Value} ->
{ok, Value};
{error, _} ->
{error, {module_out_of_bounds, X, Y, width(Qr), height(Qr)}}
end;
{error, _} ->
{error, {module_out_of_bounds, X, Y, width(Qr), height(Qr)}}
end.