-module(yum@yaml@node).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/yum/yaml/node.gleam").
-export([new/3, synthetic_span/0, synthetic/1, kind/1, kind_name/1, span/1, style/1, tag/1, anchor/1, alias/1, with_tag/2, with_anchor/2, with_alias/2, as_mapping/1, get_keys/1, get_values/1, as_sequence/1, as_string/1, as_bool/1, as_int/1, as_float/1, as_null/1, get_index/2, get_key/2, get/2]).
-export_type([node_/0, kind/0, kind_name/0, access_error/0, style/0, span/0, position/0, path_segment/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(
" Inspect and query YAML nodes.\n"
"\n"
" A [`Node`](#Node) is one value inside a YAML document: a scalar, sequence,\n"
" or mapping. This module provides accessors for the node kind, source span,\n"
" source style, tags, anchors, aliases, typed scalar values, and nested path\n"
" lookup.\n"
"\n"
" Nodes are used by both raw and resolved YAML documents. Parsing creates\n"
" nodes with the structure and metadata found in the source. Resolving\n"
" validates YAML-level metadata such as aliases and tags, and may expand or\n"
" compose parts of the tree, but a resolved document still contains nodes.\n"
"\n"
" For example, [`tag`](#tag), [`anchor`](#anchor), and [`alias`](#alias)\n"
" expose YAML metadata attached to a node. That metadata is not the same as\n"
" the node's semantic [`Kind`](#Kind). Nodes created with\n"
" [`yum/yaml/builder`](./builder.html) are synthetic and use\n"
" [`synthetic_span`](#synthetic_span).\n"
).
-opaque node_() :: {node,
kind(),
span(),
style(),
gleam@option:option(binary()),
gleam@option:option(binary()),
gleam@option:option(binary())}.
-type kind() :: null |
{bool, boolean()} |
{int, integer()} |
{float, float()} |
pos_inf |
neg_inf |
nan |
{string, binary()} |
{sequence, list(node_())} |
{mapping, list({node_(), node_()})}.
-type kind_name() :: null_kind |
bool_kind |
int_kind |
float_kind |
pos_inf_kind |
neg_inf_kind |
nan_kind |
string_kind |
sequence_kind |
mapping_kind.
-type access_error() :: {expected_kind, kind_name(), kind_name(), span()}.
-type style() :: plain_scalar |
single_quoted_scalar |
double_quoted_scalar |
literal_block_scalar |
folded_block_scalar |
block_sequence |
flow_sequence |
block_mapping |
flow_mapping |
synthetic.
-type span() :: {span, position(), position()}.
-type position() :: {position, integer(), integer()}.
-type path_segment() :: {key, binary()} | {index, integer()}.
-file("src/yum/yaml/node.gleam", 258).
?DOC(
" Creates a YAML node with explicit metadata.\n"
"\n"
" Most callers should prefer [`yum/yaml.parse`](../yaml.html#parse) plus\n"
" [`yum/yaml.root`](../yaml.html#root) for parsed input or\n"
" [`yum/yaml/builder`](./builder.html) for generated YAML. This constructor is\n"
" public for tools that need to synthesize nodes while preserving their own\n"
" source metadata.\n"
).
-spec new(kind(), span(), style()) -> node_().
new(Kind, Span, Style) ->
{node, Kind, Span, Style, none, none, none}.
-file("src/yum/yaml/node.gleam", 271).
?DOC(" Returns the placeholder span used for generated nodes with no source.\n").
-spec synthetic_span() -> span().
synthetic_span() ->
{span, {position, 0, 0}, {position, 0, 0}}.
-file("src/yum/yaml/node.gleam", 265).
?DOC(
" Creates a generated node with no original source location.\n"
"\n"
" This is the lower-level constructor used by [`yum/yaml/builder`](./builder.html).\n"
).
-spec synthetic(kind()) -> node_().
synthetic(Kind) ->
new(Kind, synthetic_span(), synthetic).
-file("src/yum/yaml/node.gleam", 279).
?DOC(
" Returns the semantic kind and value of a node.\n"
"\n"
" This is the broadest accessor. Prefer the stricter `as_*` functions when a\n"
" caller expects one specific YAML kind and wants a typed error for mismatches.\n"
).
-spec kind(node_()) -> kind().
kind(Node) ->
erlang:element(2, Node).
-file("src/yum/yaml/node.gleam", 566).
-spec kind_name_of(kind()) -> kind_name().
kind_name_of(Kind) ->
case Kind of
null ->
null_kind;
{bool, _} ->
bool_kind;
{int, _} ->
int_kind;
{float, _} ->
float_kind;
pos_inf ->
pos_inf_kind;
neg_inf ->
neg_inf_kind;
nan ->
nan_kind;
{string, _} ->
string_kind;
{sequence, _} ->
sequence_kind;
{mapping, _} ->
mapping_kind
end.
-file("src/yum/yaml/node.gleam", 285).
?DOC(" Returns the node kind without its associated value.\n").
-spec kind_name(node_()) -> kind_name().
kind_name(Node) ->
_pipe = erlang:element(2, Node),
kind_name_of(_pipe).
-file("src/yum/yaml/node.gleam", 294).
?DOC(
" Returns the source span for a node.\n"
"\n"
" Parsed nodes use 1-based row and column positions. Synthetic nodes created\n"
" with [`yum/yaml/builder`](./builder.html) use [`synthetic_span`](#synthetic_span).\n"
).
-spec span(node_()) -> span().
span(Node) ->
erlang:element(3, Node).
-file("src/yum/yaml/node.gleam", 302).
?DOC(
" Returns the source style used to write a node.\n"
"\n"
" Style describes presentation, such as plain versus quoted scalars or block\n"
" versus flow collections. It does not change the node's semantic kind.\n"
).
-spec style(node_()) -> style().
style(Node) ->
erlang:element(4, Node).
-file("src/yum/yaml/node.gleam", 332).
?DOC(
" Returns the YAML tag attached to a node, if one was written or added.\n"
"\n"
" Tags are metadata, not value casts. A tagged scalar keeps its parsed\n"
" [`Kind`](#Kind); for example `!!str 123` is still parsed as an integer today,\n"
" while the tag is available through this function.\n"
"\n"
" On raw YAML, this returns the tag form captured by the parser. On resolved\n"
" YAML, tag handles are expanded where possible. For example:\n"
"\n"
" ```gleam\n"
" import gleam/option\n"
" import yum/yaml\n"
" import yum/yaml/node\n"
"\n"
" pub fn example() {\n"
" let assert Ok(document) = yaml.parse(\"value: !!str 123\")\n"
" let assert option.Some(value) = document |> yaml.get([node.Key(\"value\")])\n"
"\n"
" assert node.tag(value) == option.Some(\"!str\")\n"
"\n"
" let assert Ok(document) = yaml.resolve(document)\n"
" let assert option.Some(value) = document |> yaml.get([node.Key(\"value\")])\n"
"\n"
" assert node.tag(value) == option.Some(\"tag:yaml.org,2002:str\")\n"
" }\n"
" ```\n"
).
-spec tag(node_()) -> gleam@option:option(binary()).
tag(Node) ->
erlang:element(5, Node).
-file("src/yum/yaml/node.gleam", 341).
?DOC(
" Returns the YAML anchor name attached to a node, if one was written or added.\n"
"\n"
" For source YAML like `defaults: &base { retries: 1 }`, the value node under\n"
" `defaults` has anchor `base`. Resolving validates duplicate anchors but does\n"
" not remove anchor metadata from nodes.\n"
).
-spec anchor(node_()) -> gleam@option:option(binary()).
anchor(Node) ->
erlang:element(6, Node).
-file("src/yum/yaml/node.gleam", 350).
?DOC(
" Returns the YAML alias name attached to a node, if one was written or added.\n"
"\n"
" For source YAML like `copy: *base`, the value node under `copy` has alias\n"
" `base`. Resolving checks that aliases refer to anchors seen earlier in the\n"
" document, but alias metadata can still be inspected on the node.\n"
).
-spec alias(node_()) -> gleam@option:option(binary()).
alias(Node) ->
erlang:element(7, Node).
-file("src/yum/yaml/node.gleam", 359).
?DOC(
" Returns a copy of the node with tag metadata.\n"
"\n"
" This function only sets metadata. It does not validate tag syntax or change\n"
" the node's [`Kind`](#Kind).\n"
).
-spec with_tag(node_(), binary()) -> node_().
with_tag(Node, Tag) ->
{node,
erlang:element(2, Node),
erlang:element(3, Node),
erlang:element(4, Node),
{some, Tag},
erlang:element(6, Node),
erlang:element(7, Node)}.
-file("src/yum/yaml/node.gleam", 368).
?DOC(
" Returns a copy of the node with anchor metadata.\n"
"\n"
" This function only sets metadata. Use [`yum/yaml.resolve`](../yaml.html#resolve)\n"
" to validate anchor and alias relationships.\n"
).
-spec with_anchor(node_(), binary()) -> node_().
with_anchor(Node, Anchor) ->
{node,
erlang:element(2, Node),
erlang:element(3, Node),
erlang:element(4, Node),
erlang:element(5, Node),
{some, Anchor},
erlang:element(7, Node)}.
-file("src/yum/yaml/node.gleam", 377).
?DOC(
" Returns a copy of the node with alias metadata.\n"
"\n"
" This function only sets metadata. Use [`yum/yaml.resolve`](../yaml.html#resolve)\n"
" to check whether the alias refers to a known anchor.\n"
).
-spec with_alias(node_(), binary()) -> node_().
with_alias(Node, Alias) ->
{node,
erlang:element(2, Node),
erlang:element(3, Node),
erlang:element(4, Node),
erlang:element(5, Node),
erlang:element(6, Node),
{some, Alias}}.
-file("src/yum/yaml/node.gleam", 562).
-spec expected(node_(), kind_name()) -> access_error().
expected(Node, Kind) ->
{expected_kind, Kind, kind_name(Node), erlang:element(3, Node)}.
-file("src/yum/yaml/node.gleam", 384).
?DOC(
" Returns mapping entries when the node is a mapping.\n"
"\n"
" Returns [`ExpectedKind`](#AccessError) when the node is not a mapping.\n"
).
-spec as_mapping(node_()) -> {ok, list({node_(), node_()})} |
{error, access_error()}.
as_mapping(Node) ->
case erlang:element(2, Node) of
{mapping, Entries} ->
{ok, Entries};
_ ->
{error, expected(Node, mapping_kind)}
end.
-file("src/yum/yaml/node.gleam", 396).
?DOC(
" Returns all keys from a mapping node.\n"
"\n"
" The keys are returned as nodes because YAML mappings can use scalar,\n"
" sequence, or mapping nodes as keys. Returns [`ExpectedKind`](#AccessError)\n"
" when the node is not a mapping.\n"
).
-spec get_keys(node_()) -> {ok, list(node_())} | {error, access_error()}.
get_keys(Node) ->
_pipe = Node,
_pipe@1 = as_mapping(_pipe),
gleam@result:map(
_pipe@1,
fun(_capture) ->
gleam@list:map(
_capture,
fun(Entry) ->
{Key, _} = Entry,
Key
end
)
end
).
-file("src/yum/yaml/node.gleam", 411).
?DOC(
" Returns all values from a mapping node.\n"
"\n"
" Values are returned in source order. Returns [`ExpectedKind`](#AccessError)\n"
" when the node is not a mapping.\n"
).
-spec get_values(node_()) -> {ok, list(node_())} | {error, access_error()}.
get_values(Node) ->
_pipe = Node,
_pipe@1 = as_mapping(_pipe),
gleam@result:map(
_pipe@1,
fun(Node@1) ->
gleam@list:map(
Node@1,
fun(Entry) ->
{_, Value} = Entry,
Value
end
)
end
).
-file("src/yum/yaml/node.gleam", 425).
?DOC(
" Returns sequence entries when the node is a sequence.\n"
"\n"
" Returns [`ExpectedKind`](#AccessError) when the node is not a sequence.\n"
).
-spec as_sequence(node_()) -> {ok, list(node_())} | {error, access_error()}.
as_sequence(Node) ->
case erlang:element(2, Node) of
{sequence, Entries} ->
{ok, Entries};
_ ->
{error, expected(Node, sequence_kind)}
end.
-file("src/yum/yaml/node.gleam", 435).
?DOC(
" Returns the string value when the node is a string.\n"
"\n"
" Returns [`ExpectedKind`](#AccessError) when the node is not a string.\n"
).
-spec as_string(node_()) -> {ok, binary()} | {error, access_error()}.
as_string(Node) ->
case erlang:element(2, Node) of
{string, Value} ->
{ok, Value};
_ ->
{error, expected(Node, string_kind)}
end.
-file("src/yum/yaml/node.gleam", 445).
?DOC(
" Returns the boolean value when the node is a boolean.\n"
"\n"
" Returns [`ExpectedKind`](#AccessError) when the node is not a boolean.\n"
).
-spec as_bool(node_()) -> {ok, boolean()} | {error, access_error()}.
as_bool(Node) ->
case erlang:element(2, Node) of
{bool, Value} ->
{ok, Value};
_ ->
{error, expected(Node, bool_kind)}
end.
-file("src/yum/yaml/node.gleam", 455).
?DOC(
" Returns the integer value when the node is an integer.\n"
"\n"
" Returns [`ExpectedKind`](#AccessError) when the node is not an integer.\n"
).
-spec as_int(node_()) -> {ok, integer()} | {error, access_error()}.
as_int(Node) ->
case erlang:element(2, Node) of
{int, Value} ->
{ok, Value};
_ ->
{error, expected(Node, int_kind)}
end.
-file("src/yum/yaml/node.gleam", 467).
?DOC(
" Returns the finite float value when the node is a float.\n"
"\n"
" Returns [`ExpectedKind`](#AccessError) when the node is not a finite float.\n"
" Special float values have separate kinds: [`PosInf`](#Kind),\n"
" [`NegInf`](#Kind), and [`Nan`](#Kind).\n"
).
-spec as_float(node_()) -> {ok, float()} | {error, access_error()}.
as_float(Node) ->
case erlang:element(2, Node) of
{float, Value} ->
{ok, Value};
_ ->
{error, expected(Node, float_kind)}
end.
-file("src/yum/yaml/node.gleam", 477).
?DOC(
" Returns `Nil` when the node is null.\n"
"\n"
" Returns [`ExpectedKind`](#AccessError) when the node is not null.\n"
).
-spec as_null(node_()) -> {ok, nil} | {error, access_error()}.
as_null(Node) ->
case erlang:element(2, Node) of
null ->
{ok, nil};
_ ->
{error, expected(Node, null_kind)}
end.
-file("src/yum/yaml/node.gleam", 546).
?DOC(
" Returns a sequence item by zero-based index.\n"
"\n"
" Returns `None` when the node is not a sequence or when the index is out of\n"
" bounds. Negative indexes always return `None`.\n"
).
-spec get_index(node_(), integer()) -> gleam@option:option(node_()).
get_index(Node, Index) ->
case Index < 0 of
true ->
none;
false ->
case as_sequence(Node) of
{ok, Entries} ->
_pipe = Entries,
_pipe@1 = gleam@list:drop(_pipe, Index),
_pipe@2 = gleam@list:first(_pipe@1),
gleam@option:from_result(_pipe@2);
{error, _} ->
none
end
end.
-file("src/yum/yaml/node.gleam", 526).
?DOC(
" Returns a mapping value by string key.\n"
"\n"
" Only string keys are matched. Returns `None` when the node is not a mapping,\n"
" when the key is not present, or when a mapping entry uses a non-string key.\n"
).
-spec get_key(node_(), binary()) -> gleam@option:option(node_()).
get_key(Node, Key) ->
case as_mapping(Node) of
{ok, Entries} ->
_pipe = Entries,
_pipe@1 = gleam@list:find_map(
_pipe,
fun(Entry) ->
{Entry_key, Value} = Entry,
case as_string(Entry_key) of
{ok, Entry_key@1} when Entry_key@1 =:= Key ->
{ok, Value};
_ ->
{error, nil}
end
end
),
gleam@option:from_result(_pipe@1);
{error, _} ->
none
end.
-file("src/yum/yaml/node.gleam", 510).
?DOC(
" Returns a nested node by following mapping keys and sequence indexes.\n"
"\n"
" This is a convenience wrapper around [`get_key`](#get_key) and\n"
" [`get_index`](#get_index). It returns `None` when any path segment does not\n"
" match the current node.\n"
"\n"
" ```gleam\n"
" import gleam/option\n"
" import yum/yaml\n"
" import yum/yaml/node\n"
"\n"
" pub fn example() {\n"
" let assert Ok(document) = yaml.parse(\"jobs:\\n test:\\n run: gleam test\")\n"
" let root = yaml.root(document)\n"
"\n"
" let run = root |> node.get([\n"
" node.Key(\"jobs\"),\n"
" node.Key(\"test\"),\n"
" node.Key(\"run\"),\n"
" ])\n"
"\n"
" let value = run |> option.map(node.as_string)\n"
"\n"
" assert value == option.Some(Ok(\"gleam test\"))\n"
" }\n"
" ```\n"
).
-spec get(node_(), list(path_segment())) -> gleam@option:option(node_()).
get(Node, Path) ->
case Path of
[] ->
{some, Node};
[{key, Key} | Rest] ->
_pipe = get_key(Node, Key),
gleam@option:then(_pipe, fun(_capture) -> get(_capture, Rest) end);
[{index, Index} | Rest@1] ->
_pipe@1 = get_index(Node, Index),
gleam@option:then(
_pipe@1,
fun(_capture@1) -> get(_capture@1, Rest@1) end
)
end.