Current section
Files
Jump to
Current section
Files
src/yog_io@list.erl
-module(yog_io@list).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/yog_io/list.gleam").
-export([default_options/0, from_string/3, read_with/3, read/2, serialize/2, write_with/3, write/2]).
-export_type([list_error/0, list_options/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(
" Adjacency List format support.\n"
"\n"
" This module provides functions to serialize and deserialize graphs in\n"
" adjacency list format (`.list`). This format is simple and human-readable,\n"
" where each line represents a node and its successors.\n"
"\n"
" ## Format Overview\n"
"\n"
" Each line starts with a node ID, followed by a delimiter (default: `:`),\n"
" and then a space-separated list of successor node IDs.\n"
"\n"
" For weighted graphs, successors are represented as `id,weight`.\n"
"\n"
" ## Example (Unweighted)\n"
"\n"
" ```\n"
" 1: 2 3\n"
" 2: 3\n"
" 3:\n"
" ```\n"
"\n"
" ## Example (Weighted)\n"
"\n"
" ```\n"
" 1: 2,0.5 3,1.2\n"
" 2: 3,0.8\n"
" 3:\n"
" ```\n"
"\n"
" ## Usage\n"
"\n"
" ```gleam\n"
" import yog/model.{Directed}\n"
" import yog_io/list as list_io\n"
"\n"
" pub fn main() {\n"
" let assert Ok(graph) = list_io.read(\"graph.list\", Directed)\n"
" }\n"
" ```\n"
).
-type list_error() :: {read_error, binary(), binary()} |
{write_error, binary(), binary()} |
{parse_error, integer(), binary()}.
-type list_options() :: {list_options, boolean(), binary()}.
-file("src/yog_io/list.gleam", 71).
?DOC(" Returns default options for unweighted adjacency lists\n").
-spec default_options() -> list_options().
default_options() ->
{list_options, false, <<":"/utf8>>}.
-file("src/yog_io/list.gleam", 141).
-spec parse_id(binary()) -> integer().
parse_id(Str) ->
case gleam_stdlib:parse_int(gleam@string:trim(Str)) of
{ok, I} ->
I;
{error, nil} ->
0
end.
-file("src/yog_io/list.gleam", 162).
-spec parse_float(binary()) -> float().
parse_float(Str) ->
Trimmed = gleam@string:trim(Str),
case gleam_stdlib:parse_float(Trimmed) of
{ok, F} ->
F;
{error, nil} ->
case gleam_stdlib:parse_int(Trimmed) of
{ok, I} ->
erlang:float(I);
{error, nil} ->
1.0
end
end.
-file("src/yog_io/list.gleam", 148).
-spec parse_neighbor(binary(), boolean()) -> {integer(), float()}.
parse_neighbor(Str, Weighted) ->
case Weighted of
true ->
case gleam@string:split_once(Str, <<","/utf8>>) of
{ok, {Id_str, Weight_str}} ->
{parse_id(Id_str), parse_float(Weight_str)};
{error, nil} ->
{parse_id(Str), 1.0}
end;
false ->
{parse_id(Str), 1.0}
end.
-file("src/yog_io/list.gleam", 95).
?DOC(" Parses an adjacency list from a string into a graph.\n").
-spec from_string(binary(), yog@model:graph_type(), list_options()) -> {ok,
yog@model:graph(nil, float())} |
{error, list_error()}.
from_string(Content, Graph_type, Options) ->
Lines = gleam@string:split(Content, <<"\n"/utf8>>),
Initial_graph = yog@model:new(Graph_type),
gleam@list:index_fold(
Lines,
{ok, Initial_graph},
fun(Res_graph, Line, Idx) ->
gleam@result:'try'(
Res_graph,
fun(Graph) ->
Line@1 = gleam@string:trim(Line),
Line_no = Idx + 1,
case (Line@1 =:= <<""/utf8>>) orelse gleam_stdlib:string_starts_with(
Line@1,
<<"#"/utf8>>
) of
true ->
{ok, Graph};
false ->
case gleam@string:split_once(
Line@1,
erlang:element(3, Options)
) of
{ok, {Node_id_str, Neighbors_str}} ->
Node_id = parse_id(Node_id_str),
Graph@1 = yog@model:add_node(
Graph,
Node_id,
nil
),
Neighbor_parts = begin
_pipe = gleam@string:split(
gleam@string:trim(Neighbors_str),
<<" "/utf8>>
),
gleam@list:filter(
_pipe,
fun(S) -> S /= <<""/utf8>> end
)
end,
gleam@list:try_fold(
Neighbor_parts,
Graph@1,
fun(G, Part) ->
{Target_id, Weight} = parse_neighbor(
Part,
erlang:element(2, Options)
),
G@1 = case gleam@dict:has_key(
erlang:element(3, G),
Target_id
) of
true ->
G;
false ->
yog@model:add_node(
G,
Target_id,
nil
)
end,
_pipe@1 = yog@model:add_edge(
G@1,
Node_id,
Target_id,
Weight
),
gleam@result:replace_error(
_pipe@1,
{parse_error, Line_no, Line@1}
)
end
);
{error, nil} ->
Node_id@1 = parse_id(Line@1),
{ok,
yog@model:add_node(
Graph,
Node_id@1,
nil
)}
end
end
end
)
end
).
-file("src/yog_io/list.gleam", 84).
?DOC(" Reads a graph from an adjacency list file with custom options.\n").
-spec read_with(binary(), yog@model:graph_type(), list_options()) -> {ok,
yog@model:graph(nil, float())} |
{error, list_error()}.
read_with(Path, Graph_type, Options) ->
_pipe = simplifile:read(Path),
_pipe@1 = gleam@result:map_error(
_pipe,
fun(Err) -> {read_error, Path, gleam@string:inspect(Err)} end
),
gleam@result:'try'(
_pipe@1,
fun(Content) -> from_string(Content, Graph_type, Options) end
).
-file("src/yog_io/list.gleam", 76).
?DOC(" Reads a graph from an adjacency list file.\n").
-spec read(binary(), yog@model:graph_type()) -> {ok,
yog@model:graph(nil, float())} |
{error, list_error()}.
read(Path, Graph_type) ->
read_with(Path, Graph_type, default_options()).
-file("src/yog_io/list.gleam", 192).
?DOC(" Converts a graph to an adjacency list string.\n").
-spec serialize(yog@model:graph(any(), float()), list_options()) -> binary().
serialize(Graph, Options) ->
Nodes = begin
_pipe = maps:keys(erlang:element(3, Graph)),
gleam@list:sort(_pipe, fun gleam@int:compare/2)
end,
_pipe@2 = gleam@list:map(
Nodes,
fun(Node_id) ->
Neighbors = begin
_pipe@1 = yog@model:successors(Graph, Node_id),
gleam@list:sort(
_pipe@1,
fun(A, B) ->
gleam@int:compare(
erlang:element(1, A),
erlang:element(1, B)
)
end
)
end,
Neighbor_strs = gleam@list:map(
Neighbors,
fun(Neighbor) ->
{Target_id, Weight} = Neighbor,
case erlang:element(2, Options) of
true ->
<<<<(erlang:integer_to_binary(Target_id))/binary,
","/utf8>>/binary,
(gleam_stdlib:float_to_string(Weight))/binary>>;
false ->
erlang:integer_to_binary(Target_id)
end
end
),
Neighbor_str = case Neighbor_strs of
[] ->
<<""/utf8>>;
_ ->
<<" "/utf8,
(gleam@string:join(Neighbor_strs, <<" "/utf8>>))/binary>>
end,
<<<<(erlang:integer_to_binary(Node_id))/binary,
(erlang:element(3, Options))/binary>>/binary,
Neighbor_str/binary>>
end
),
gleam@string:join(_pipe@2, <<"\n"/utf8>>).
-file("src/yog_io/list.gleam", 181).
?DOC(" Writes a graph to an adjacency list file with custom options.\n").
-spec write_with(binary(), yog@model:graph(any(), float()), list_options()) -> {ok,
nil} |
{error, list_error()}.
write_with(Path, Graph, Options) ->
Content = serialize(Graph, Options),
_pipe = simplifile:write(Path, Content),
gleam@result:map_error(
_pipe,
fun(Err) -> {write_error, Path, gleam@string:inspect(Err)} end
).
-file("src/yog_io/list.gleam", 176).
?DOC(" Writes a graph to an adjacency list file.\n").
-spec write(binary(), yog@model:graph(any(), float())) -> {ok, nil} |
{error, list_error()}.
write(Path, Graph) ->
write_with(Path, Graph, default_options()).