Current section
Files
Jump to
Current section
Files
src/taffy@native.erl
-module(taffy@native).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/taffy/native.gleam").
-export([parse/1, parse_all/1]).
-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(
" Native YAML parsing backend wrapping the `fast_yaml` C NIF (libyaml).\n"
" Erlang target only — requires `fast_yaml` in your dependencies.\n"
" Output is the same `YamlValue` shape as the pure-Gleam parser but\n"
" ~3-7× faster on large documents.\n"
"\n"
" ```gleam\n"
" import taffy/native\n"
"\n"
" let assert Ok(value) = native.parse(\"name: John\\nage: 30\")\n"
" ```\n"
"\n"
" One known divergence from the pure parser: `fast_yaml` represents both\n"
" `{}` and `[]` as the empty Erlang list at the term level, so the\n"
" distinction can't be recovered from the decoded value alone. Taffy\n"
" resolves the ambiguity in favour of `Mapping([])` since `{}` is the\n"
" more common author intent; users who need `Sequence([])` for `[]`\n"
" should use the pure parser.\n"
).
-file("src/taffy/native.gleam", 64).
-spec format_error(gleam@dynamic:dynamic_()) -> taffy@parser@types:parse_error().
format_error(Err) ->
{parse_error,
<<"fast_yaml decode error: "/utf8, (gleam@string:inspect(Err))/binary>>,
0}.
-file("src/taffy/native.gleam", 103).
-spec try_as_string(gleam@dynamic:dynamic_()) -> gleam@option:option(taffy@value:yaml_value()).
try_as_string(Val) ->
_pipe = taffy_ffi:to_string(Val),
_pipe@1 = gleam@result:map(_pipe, fun taffy@parser@scalar:parse_scalar/1),
gleam@option:from_result(_pipe@1).
-file("src/taffy/native.gleam", 85).
-spec try_as_int(gleam@dynamic:dynamic_()) -> gleam@option:option(taffy@value:yaml_value()).
try_as_int(Val) ->
_pipe = taffy_ffi:to_int(Val),
_pipe@1 = gleam@result:map(_pipe, fun(Field@0) -> {int, Field@0} end),
gleam@option:from_result(_pipe@1).
-file("src/taffy/native.gleam", 89).
-spec try_as_float(gleam@dynamic:dynamic_()) -> gleam@option:option(taffy@value:yaml_value()).
try_as_float(Val) ->
_pipe = taffy_ffi:to_float(Val),
_pipe@1 = gleam@result:map(_pipe, fun(Field@0) -> {float, Field@0} end),
gleam@option:from_result(_pipe@1).
-file("src/taffy/native.gleam", 97).
-spec try_as_sequence(gleam@dynamic:dynamic_()) -> gleam@option:option(taffy@value:yaml_value()).
try_as_sequence(Val) ->
_pipe = taffy_ffi:to_list(Val),
_pipe@1 = gleam@result:map(
_pipe,
fun(Items) -> {sequence, gleam@list:map(Items, fun convert_value/1)} end
),
gleam@option:from_result(_pipe@1).
-file("src/taffy/native.gleam", 75).
-spec convert_value(gleam@dynamic:dynamic_()) -> taffy@value:yaml_value().
convert_value(Val) ->
_pipe = try_as_int(Val),
_pipe@1 = gleam@option:lazy_or(_pipe, fun() -> try_as_float(Val) end),
_pipe@2 = gleam@option:lazy_or(_pipe@1, fun() -> try_as_mapping(Val) end),
_pipe@3 = gleam@option:lazy_or(_pipe@2, fun() -> try_as_sequence(Val) end),
_pipe@4 = gleam@option:lazy_or(_pipe@3, fun() -> try_as_string(Val) end),
gleam@option:unwrap(_pipe@4, null).
-file("src/taffy/native.gleam", 68).
-spec decode_documents(gleam@dynamic:dynamic_()) -> list(taffy@value:yaml_value()).
decode_documents(Docs) ->
case taffy_ffi:to_list(Docs) of
{ok, Items} ->
gleam@list:map(Items, fun convert_value/1);
{error, _} ->
[convert_value(Docs)]
end.
-file("src/taffy/native.gleam", 32).
?DOC(
" Parse a single document via `fast_yaml`. The error type matches the\n"
" pure parser's so callers can swap backends without changing their\n"
" error-handling. `pos` is always 0 here — `fast_yaml` doesn't expose a\n"
" position on its error path.\n"
).
-spec parse(binary()) -> {ok, taffy@value:yaml_value()} |
{error, taffy@parser@types:parse_error()}.
parse(Input) ->
application:ensure_all_started(erlang:binary_to_atom(<<"fast_yaml"/utf8>>)),
case fast_yaml:decode(Input) of
{ok, Docs} ->
case decode_documents(Docs) of
[First | _] ->
{ok, First};
[] ->
{ok, null}
end;
{error, Err} ->
{error, format_error(Err)}
end.
-file("src/taffy/native.gleam", 47).
?DOC(
" Parse a multi-document stream via `fast_yaml`. Same error semantics\n"
" as `parse`.\n"
).
-spec parse_all(binary()) -> {ok, list(taffy@value:yaml_value())} |
{error, taffy@parser@types:parse_error()}.
parse_all(Input) ->
application:ensure_all_started(erlang:binary_to_atom(<<"fast_yaml"/utf8>>)),
case fast_yaml:decode(Input) of
{ok, Docs} ->
{ok, decode_documents(Docs)};
{error, Err} ->
{error, format_error(Err)}
end.
-file("src/taffy/native.gleam", 125).
-spec decode_pair(gleam@dynamic:dynamic_()) -> {ok,
{binary(), taffy@value:yaml_value()}} |
{error, nil}.
decode_pair(Item) ->
gleam@result:'try'(
taffy_ffi:decode_tuple2(Item),
fun(_use0) ->
{Key, Raw_val} = _use0,
gleam@result:'try'(
taffy_ffi:to_string(Key),
fun(K) -> {ok, {K, convert_value(Raw_val)}} end
)
end
).
-file("src/taffy/native.gleam", 109).
-spec try_proplist(gleam@dynamic:dynamic_()) -> gleam@option:option(list({binary(),
taffy@value:yaml_value()})).
try_proplist(Val) ->
gleam@option:then(
begin
_pipe = taffy_ffi:to_list(Val),
gleam@option:from_result(_pipe)
end,
fun(Items) -> case Items of
[] ->
{some, []};
_ ->
case gleam@list:all(Items, fun taffy_ffi:is_tuple2/1) of
true ->
{some,
gleam@list:filter_map(Items, fun decode_pair/1)};
false ->
none
end
end end
).
-file("src/taffy/native.gleam", 93).
-spec try_as_mapping(gleam@dynamic:dynamic_()) -> gleam@option:option(taffy@value:yaml_value()).
try_as_mapping(Val) ->
_pipe = try_proplist(Val),
gleam@option:map(_pipe, fun(Field@0) -> {mapping, Field@0} end).