Packages

Comprehensive graph file format I/O for the yog graph library - Support for GraphML, GDF, JSON (D3, Cytoscape, VisJs), TGF, LEDA, Pajek, Adjacency List/Matrix, and Matrix Market

Current section

Files

Jump to
yog_io src yog_io@leda.erl
Raw

src/yog_io@leda.erl

-module(yog_io@leda).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/yog_io/leda.gleam").
-export([default_options/0, options_with/4, serialize_with/2, serialize/1, to_string/1, write/2, write_with/3, parse_with/3, parse/1, read/1, read_with/3]).
-export_type([leda_options/2, leda_type/0, leda_error/0, leda_result/2]).
-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(
" LEDA (Library of Efficient Data types and Algorithms) graph format support.\n"
"\n"
" Provides functions to serialize and deserialize graphs in the LEDA format,\n"
" a text-based format used by the LEDA library and compatible with NetworkX.\n"
"\n"
" ## Format Overview\n"
"\n"
" LEDA files have a structured text format with distinct sections:\n"
" - **Header**: `LEDA.GRAPH`\n"
" - **Type declarations**: Node type and edge type\n"
" - **Direction**: `-1` for directed, `-2` for undirected\n"
" - **Nodes**: Count followed by node data lines\n"
" - **Edges**: Count followed by edge data lines\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import yog/model.{Directed}\n"
" import yog_io/leda\n"
"\n"
" // Create a simple graph\n"
" let graph =\n"
" model.new(Directed)\n"
" |> model.add_node(1, \"Alice\")\n"
" |> model.add_node(2, \"Bob\")\n"
"\n"
" let assert Ok(graph) = model.add_edge(graph, from: 1, to: 2, with: \"5\")\n"
"\n"
" // Serialize to LEDA\n"
" let leda_string = leda.serialize(graph)\n"
"\n"
" // Write to file\n"
" let assert Ok(Nil) = leda.write(\"graph.gw\", graph)\n"
" ```\n"
"\n"
" ## Output Format\n"
"\n"
" ```\n"
" LEDA.GRAPH\n"
" string\n"
" string\n"
" -1\n"
" 2\n"
" |{Alice}|\n"
" |{Bob}|\n"
" 1\n"
" 1 2 0 |{5}|\n"
" ```\n"
"\n"
" ## Characteristics\n"
"\n"
" - **Type-aware**: Supports typed node and edge attributes\n"
" - **1-indexed**: Node numbering starts at 1 (not 0)\n"
" - **Reversal edges**: Undirected graphs use reversal edge indices\n"
" - **Research compatible**: Used in academic graph algorithms\n"
"\n"
" ## Parsing Behavior\n"
"\n"
" When parsing LEDA files, the following behaviors apply:\n"
"\n"
" - **1-indexed nodes**: LEDA format uses 1-based indexing. Node ID 1 refers\n"
" to the first node in the node section, ID 2 to the second node, etc.\n"
" The parser preserves these IDs when creating the graph.\n"
"\n"
" - **Sequential order**: Nodes must appear in sequential order in the file.\n"
" The nth node in the node section receives LEDA ID n.\n"
"\n"
" - **Strict node references**: Edges must reference node IDs that exist in\n"
" the node section. Unlike TGF, LEDA does not auto-create missing nodes.\n"
" Edges with invalid node IDs are skipped and added to warnings.\n"
"\n"
" - **Reversal edges**: The third field in edge lines (rev_edge) indicates\n"
" the index of the reverse edge for undirected graphs. Currently, the parser\n"
" accepts but does not actively use this field.\n"
"\n"
" - **Type declarations**: The parser accepts any string in the node/edge type\n"
" declarations (lines 2-3) but currently treats all data as strings. Type\n"
" validation is not enforced.\n"
"\n"
" - **Whitespace handling**: Multiple consecutive spaces in data values are\n"
" collapsed to single spaces. Leading and trailing whitespace is trimmed.\n"
"\n"
" - **Malformed lines**: Lines that cannot be parsed are skipped and collected\n"
" as warnings in the `LedaResult`, rather than causing the parse to fail.\n"
"\n"
" ## References\n"
"\n"
" - [LEDA Library](https://www.algorithmic-solutions.com/leda/)\n"
" - [NetworkX LEDA Reader](https://networkx.org/documentation/stable/reference/readwrite/leda.html)\n"
).
-type leda_options(AFDF, AFDG) :: {leda_options,
fun((AFDF) -> binary()),
fun((AFDG) -> binary()),
fun((binary()) -> AFDF),
fun((binary()) -> AFDG)}.
-type leda_type() :: void_type |
string_type |
int_type |
double_type |
{custom_type, binary()}.
-type leda_error() :: empty_input |
invalid_header |
{invalid_node_type, integer(), binary()} |
{invalid_edge_type, integer(), binary()} |
{invalid_direction, integer(), binary()} |
{invalid_node_count, integer(), binary()} |
{invalid_edge_count, integer(), binary()} |
{invalid_node_data, integer(), binary()} |
{invalid_edge_data, integer(), binary()} |
{invalid_node_id, integer(), binary()} |
{node_id_out_of_range, integer(), integer(), integer()} |
{read_error, binary(), binary()} |
{write_error, binary(), binary()}.
-type leda_result(AFDH, AFDI) :: {leda_result,
yog@model:graph(AFDH, AFDI),
list({integer(), binary()})}.
-file("src/yog_io/leda.gleam", 186).
?DOC(
" Default LEDA options for String node and edge data.\n"
"\n"
" Uses identity functions for serialization/deserialization.\n"
).
-spec default_options() -> leda_options(binary(), binary()).
default_options() ->
{leda_options,
fun(S) -> S end,
fun(S@1) -> S@1 end,
fun(S@2) -> S@2 end,
fun(S@3) -> S@3 end}.
-file("src/yog_io/leda.gleam", 209).
?DOC(
" Creates LEDA options with custom serializers.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let options = leda.options_with(\n"
" node_serializer: fn(p) { p.name },\n"
" edge_serializer: fn(w) { int.to_string(w) },\n"
" node_deserializer: fn(s) { Person(s, 0) },\n"
" edge_deserializer: fn(s) {\n"
" case int.parse(s) { Ok(n) -> n Error(_) -> 0 }\n"
" },\n"
" )\n"
" ```\n"
).
-spec options_with(
fun((AFDL) -> binary()),
fun((AFDM) -> binary()),
fun((binary()) -> AFDL),
fun((binary()) -> AFDM)
) -> leda_options(AFDL, AFDM).
options_with(
Node_serializer,
Edge_serializer,
Node_deserializer,
Edge_deserializer
) ->
{leda_options,
Node_serializer,
Edge_serializer,
Node_deserializer,
Edge_deserializer}.
-file("src/yog_io/leda.gleam", 354).
-spec collect_undirected_edges(list({integer(), integer(), AFEA})) -> list({integer(),
integer(),
integer(),
AFEA}).
collect_undirected_edges(Edges) ->
Unique_edges = gleam@list:filter(
Edges,
fun(Edge) ->
{Src, Dst, _} = Edge,
Src =< Dst
end
),
gleam@list:index_map(
Unique_edges,
fun(Edge@1, _) ->
{Src@1, Dst@1, Data} = Edge@1,
{Src@1, Dst@1, 0, Data}
end
).
-file("src/yog_io/leda.gleam", 319).
?DOC(
" Collects edges with their 1-indexed positions for LEDA format.\n"
"\n"
" For directed graphs, rev_edge is always 0.\n"
" For undirected graphs, we need to track reverse edge pairs.\n"
).
-spec collect_edges_with_indices(yog@model:graph(any(), AFDW)) -> list({integer(),
integer(),
integer(),
AFDW}).
collect_edges_with_indices(Graph) ->
All_edges = gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, Src_id, Targets) ->
gleam@dict:fold(
Targets,
Acc,
fun(Inner_acc, Dst_id, Data) ->
[{Src_id, Dst_id, Data} | Inner_acc]
end
)
end
),
Sorted_edges = gleam@list:sort(
All_edges,
fun(A, B) ->
case gleam@int:compare(erlang:element(1, A), erlang:element(1, B)) of
eq ->
gleam@int:compare(
erlang:element(2, A),
erlang:element(2, B)
);
Ord ->
Ord
end
end
),
case erlang:element(2, Graph) of
directed ->
gleam@list:map(
Sorted_edges,
fun(Edge) ->
{Src, Dst, Data@1} = Edge,
{Src, Dst, 0, Data@1}
end
);
undirected ->
collect_undirected_edges(Sorted_edges)
end.
-file("src/yog_io/leda.gleam", 255).
?DOC(
" Serializes a graph to LEDA format with custom options.\n"
"\n"
" **Time Complexity:** O(V + E)\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import yog/model.{Directed}\n"
" import yog_io/leda\n"
"\n"
" type Person {\n"
" Person(name: String, age: Int)\n"
" }\n"
"\n"
" let graph =\n"
" model.new(Directed)\n"
" |> model.add_node(1, Person(\"Alice\", 30))\n"
" |> model.add_node(2, Person(\"Bob\", 25))\n"
"\n"
" let options = leda.options_with(\n"
" node_serializer: fn(p) { p.name },\n"
" edge_serializer: fn(w) { w },\n"
" node_deserializer: fn(s) { Person(s, 0) },\n"
" edge_deserializer: fn(s) { s },\n"
" )\n"
"\n"
" let leda_string = leda.serialize_with(options, graph)\n"
" ```\n"
).
-spec serialize_with(leda_options(AFDP, AFDQ), yog@model:graph(AFDP, AFDQ)) -> binary().
serialize_with(Options, Graph) ->
Builder = gleam@string_tree:new(),
Builder@1 = gleam@string_tree:append(Builder, <<"LEDA.GRAPH\n"/utf8>>),
Builder@2 = gleam@string_tree:append(Builder@1, <<"string\n"/utf8>>),
Builder@3 = gleam@string_tree:append(Builder@2, <<"string\n"/utf8>>),
Builder@4 = case erlang:element(2, Graph) of
directed ->
gleam@string_tree:append(Builder@3, <<"-1\n"/utf8>>);
undirected ->
gleam@string_tree:append(Builder@3, <<"-2\n"/utf8>>)
end,
Sorted_nodes = begin
_pipe = maps:to_list(erlang:element(3, Graph)),
gleam@list:sort(
_pipe,
fun(A, B) ->
gleam@int:compare(erlang:element(1, A), erlang:element(1, B))
end
)
end,
Node_count = erlang:length(Sorted_nodes),
Builder@5 = gleam@string_tree:append(
Builder@4,
<<(erlang:integer_to_binary(Node_count))/binary, "\n"/utf8>>
),
Builder@6 = gleam@list:fold(
Sorted_nodes,
Builder@5,
fun(B@1, Entry) ->
{_, Data} = Entry,
Serialized = (erlang:element(2, Options))(Data),
gleam@string_tree:append(
B@1,
<<<<"|{"/utf8, Serialized/binary>>/binary, "}|\n"/utf8>>
)
end
),
Edges_with_indices = collect_edges_with_indices(Graph),
Edge_count = erlang:length(Edges_with_indices),
Builder@7 = gleam@string_tree:append(
Builder@6,
<<(erlang:integer_to_binary(Edge_count))/binary, "\n"/utf8>>
),
_pipe@1 = gleam@list:fold(
Edges_with_indices,
Builder@7,
fun(B@2, Entry@1) ->
{Src_1idx, Dst_1idx, Rev_edge, Data@1} = Entry@1,
Serialized@1 = (erlang:element(3, Options))(Data@1),
Line = <<<<<<<<<<<<<<(erlang:integer_to_binary(Src_1idx))/binary,
" "/utf8>>/binary,
(erlang:integer_to_binary(Dst_1idx))/binary>>/binary,
" "/utf8>>/binary,
(erlang:integer_to_binary(Rev_edge))/binary>>/binary,
" |{"/utf8>>/binary,
Serialized@1/binary>>/binary,
"}|\n"/utf8>>,
gleam@string_tree:append(B@2, Line)
end
),
unicode:characters_to_binary(_pipe@1).
-file("src/yog_io/leda.gleam", 386).
?DOC(
" Serializes a graph to LEDA format.\n"
"\n"
" Convenience function for graphs with String node and edge data.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let leda_string = leda.serialize(graph)\n"
" ```\n"
).
-spec serialize(yog@model:graph(binary(), binary())) -> binary().
serialize(Graph) ->
serialize_with(default_options(), Graph).
-file("src/yog_io/leda.gleam", 393).
?DOC(
" Converts a graph to a LEDA string.\n"
"\n"
" Alias for `serialize` for consistency with other modules.\n"
).
-spec to_string(yog@model:graph(binary(), binary())) -> binary().
to_string(Graph) ->
serialize(Graph).
-file("src/yog_io/leda.gleam", 408).
?DOC(
" Writes a graph to a LEDA file.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let assert Ok(Nil) = leda.write(\"graph.gw\", graph)\n"
" ```\n"
).
-spec write(binary(), yog@model:graph(binary(), binary())) -> {ok, nil} |
{error, simplifile:file_error()}.
write(Path, Graph) ->
Content = serialize(Graph),
simplifile:write(Path, Content).
-file("src/yog_io/leda.gleam", 430).
?DOC(
" Writes a graph to a LEDA file with custom options.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let options = leda.options_with(\n"
" node_serializer: fn(p) { p.name },\n"
" edge_serializer: fn(w) { int.to_string(w) },\n"
" node_deserializer: fn(s) { Person(s, 0) },\n"
" edge_deserializer: fn(s) { case int.parse(s) { Ok(n) -> n Error(_) -> 0 } },\n"
" )\n"
"\n"
" let assert Ok(Nil) = leda.write_with(\"graph.gw\", options, graph)\n"
" ```\n"
).
-spec write_with(
binary(),
leda_options(AFEL, AFEM),
yog@model:graph(AFEL, AFEM)
) -> {ok, nil} | {error, simplifile:file_error()}.
write_with(Path, Options, Graph) ->
Content = serialize_with(Options, Graph),
simplifile:write(Path, Content).
-file("src/yog_io/leda.gleam", 601).
?DOC(" Extract value from |{...}| format.\n").
-spec extract_leda_value(binary()) -> {ok, binary()} | {error, nil}.
extract_leda_value(Line) ->
Trimmed = gleam@string:trim(Line),
case {gleam_stdlib:string_starts_with(Trimmed, <<"|{"/utf8>>),
gleam_stdlib:string_ends_with(Trimmed, <<"}|"/utf8>>)} of
{true, true} ->
Inner = gleam@string:slice(Trimmed, 2, string:length(Trimmed) - 4),
{ok, Inner};
{_, _} ->
{ok, Trimmed}
end.
-file("src/yog_io/leda.gleam", 551).
-spec parse_node_data(
list({integer(), binary()}),
integer(),
fun((binary()) -> AFFV),
list({integer(), AFFV}),
list({integer(), binary()})
) -> {ok,
{list({integer(), binary()}),
list({integer(), AFFV}),
list({integer(), binary()})}} |
{error, leda_error()}.
parse_node_data(Lines, Count, Node_parser, Acc, Warnings) ->
case Count of
0 ->
{ok, {Lines, lists:reverse(Acc), lists:reverse(Warnings)}};
_ ->
case Lines of
[] ->
Last_line = case Acc of
[{Ln, _} | _] ->
Ln;
[] ->
5
end,
{error,
{invalid_node_data,
Last_line + 1,
<<"unexpected end of input"/utf8>>}};
[{Line_num, Line} | Rest] ->
case extract_leda_value(Line) of
{ok, Value} ->
Data = Node_parser(Value),
parse_node_data(
Rest,
Count - 1,
Node_parser,
[{Line_num, Data} | Acc],
Warnings
);
{error, _} ->
parse_node_data(
Rest,
Count - 1,
Node_parser,
Acc,
[{Line_num, Line} | Warnings]
)
end
end
end.
-file("src/yog_io/leda.gleam", 616).
-spec create_graph_with_nodes(yog@model:graph_type(), list({integer(), AFGF})) -> yog@model:graph(AFGF, any()).
create_graph_with_nodes(Gtype, Nodes) ->
gleam@list:index_fold(
Nodes,
yog@model:new(Gtype),
fun(Graph, Pair, Idx) ->
{_, Data} = Pair,
Leda_id = Idx + 1,
yog@model:add_node(Graph, Leda_id, Data)
end
).
-file("src/yog_io/leda.gleam", 702).
-spec parse_edge_line(binary(), fun((binary()) -> AFHG)) -> {ok,
{integer(), integer(), integer(), AFHG}} |
{error, nil}.
parse_edge_line(Line, Edge_parser) ->
Parts = begin
_pipe = Line,
_pipe@1 = gleam@string:split(_pipe, <<" "/utf8>>),
gleam@list:filter(
_pipe@1,
fun(S) -> gleam@string:trim(S) /= <<""/utf8>> end
)
end,
case Parts of
[Src_str, Dst_str, Rev_str | Rest] ->
case {gleam_stdlib:parse_int(gleam@string:trim(Src_str)),
gleam_stdlib:parse_int(gleam@string:trim(Dst_str))} of
{{ok, Src}, {ok, Dst}} ->
Rev = case gleam_stdlib:parse_int(
gleam@string:trim(Rev_str)
) of
{ok, R} ->
R;
{error, _} ->
0
end,
Data_str = case Rest of
[] ->
<<""/utf8>>;
_ ->
gleam@string:join(Rest, <<" "/utf8>>)
end,
case extract_leda_value(Data_str) of
{ok, Value} ->
Data = Edge_parser(Value),
{ok, {Src, Dst, Rev, Data}};
{error, _} ->
{error, nil}
end;
{_, _} ->
{error, nil}
end;
_ ->
{error, nil}
end.
-file("src/yog_io/leda.gleam", 648).
-spec do_parse_edges(
list({integer(), binary()}),
integer(),
fun((binary()) -> AFGW),
yog@model:graph(AFGX, AFGW),
list({integer(), binary()})
) -> {ok, {yog@model:graph(AFGX, AFGW), list({integer(), binary()})}} |
{error, leda_error()}.
do_parse_edges(Lines, Count, Edge_parser, Graph, Warnings) ->
case Count of
0 ->
{ok, {Graph, lists:reverse(Warnings)}};
_ ->
case Lines of
[] ->
Node_count = maps:size(erlang:element(3, Graph)),
Expected_line = (5 + Node_count) + 1,
{error,
{invalid_edge_data,
Expected_line + 1,
<<"unexpected end of input"/utf8>>}};
[{Line_num, Line} | Rest] ->
case parse_edge_line(Line, Edge_parser) of
{ok, {Src, Dst, _, Data}} ->
case yog@model:add_edge(Graph, Src, Dst, Data) of
{ok, New_graph} ->
do_parse_edges(
Rest,
Count - 1,
Edge_parser,
New_graph,
Warnings
);
{error, _} ->
do_parse_edges(
Rest,
Count - 1,
Edge_parser,
Graph,
[{Line_num, Line} | Warnings]
)
end;
{error, _} ->
do_parse_edges(
Rest,
Count - 1,
Edge_parser,
Graph,
[{Line_num, Line} | Warnings]
)
end
end
end.
-file("src/yog_io/leda.gleam", 630).
-spec parse_edges(
list({integer(), binary()}),
fun((binary()) -> AFGL),
yog@model:graph(AFGM, AFGL),
list({integer(), binary()})
) -> {ok, {yog@model:graph(AFGM, AFGL), list({integer(), binary()})}} |
{error, leda_error()}.
parse_edges(Lines, Edge_parser, Graph, Warnings) ->
case Lines of
[] ->
{ok, {Graph, lists:reverse(Warnings)}};
[{Line_num, Count_str} | Rest] ->
case gleam_stdlib:parse_int(Count_str) of
{error, _} ->
{error, {invalid_edge_count, Line_num, Count_str}};
{ok, Edge_count} ->
do_parse_edges(Rest, Edge_count, Edge_parser, Graph, [])
end
end.
-file("src/yog_io/leda.gleam", 517).
-spec do_parse_nodes(
list({integer(), binary()}),
yog@model:graph_type(),
fun((binary()) -> AFFO),
fun((binary()) -> AFFP)
) -> {ok, leda_result(AFFO, AFFP)} | {error, leda_error()}.
do_parse_nodes(Lines, Gtype, Node_parser, Edge_parser) ->
case Lines of
[{_, Count_str} | Rest] ->
case gleam_stdlib:parse_int(Count_str) of
{error, _} ->
{error, {invalid_node_count, 4, Count_str}};
{ok, Node_count} ->
case parse_node_data(Rest, Node_count, Node_parser, [], []) of
{error, E} ->
{error, E};
{ok, {Remaining_lines, Nodes, Warnings}} ->
Graph = create_graph_with_nodes(Gtype, Nodes),
case parse_edges(
Remaining_lines,
Edge_parser,
Graph,
[]
) of
{error, E@1} ->
{error, E@1};
{ok, {Final_graph, Edge_warnings}} ->
All_warnings = lists:append(
Warnings,
Edge_warnings
),
{ok,
{leda_result, Final_graph, All_warnings}}
end
end
end;
_ ->
{error, {invalid_node_count, 4, <<"missing"/utf8>>}}
end.
-file("src/yog_io/leda.gleam", 499).
-spec do_parse_types(
list({integer(), binary()}),
fun((binary()) -> AFFH),
fun((binary()) -> AFFI)
) -> {ok, leda_result(AFFH, AFFI)} | {error, leda_error()}.
do_parse_types(Lines, Node_parser, Edge_parser) ->
case Lines of
[{_, _}, {_, _}, {Line_num, Dir_content} | Rest] ->
case Dir_content of
<<"-1"/utf8>> ->
do_parse_nodes(Rest, directed, Node_parser, Edge_parser);
<<"-2"/utf8>> ->
do_parse_nodes(Rest, undirected, Node_parser, Edge_parser);
_ ->
{error, {invalid_direction, Line_num, Dir_content}}
end;
_ ->
{error, {invalid_direction, 3, <<"missing"/utf8>>}}
end.
-file("src/yog_io/leda.gleam", 485).
-spec do_parse(
list({integer(), binary()}),
fun((binary()) -> AFFA),
fun((binary()) -> AFFB)
) -> {ok, leda_result(AFFA, AFFB)} | {error, leda_error()}.
do_parse(Lines, Node_parser, Edge_parser) ->
case Lines of
[{_, <<"LEDA.GRAPH"/utf8>>} | Rest] ->
do_parse_types(Rest, Node_parser, Edge_parser);
[{_, _} | _] ->
{error, invalid_header};
_ ->
{error, invalid_header}
end.
-file("src/yog_io/leda.gleam", 464).
?DOC(
" Parses a LEDA string into a graph with custom options.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let leda_string = \"LEDA.GRAPH\\nstring\\nstring\\n-1\\n2\\n|{Alice}|\\n|{Bob}|\\n1\\n1 2 0 |{follows}|\"\n"
"\n"
" let result = leda.parse_with(\n"
" leda_string,\n"
" node_parser: fn(s) { s },\n"
" edge_parser: fn(s) { s },\n"
" )\n"
"\n"
" case result {\n"
" Ok(leda.LedaResult(graph, warnings)) -> {\n"
" // Use the graph\n"
" process_graph(graph)\n"
" }\n"
" Error(e) -> handle_error(e)\n"
" }\n"
" ```\n"
).
-spec parse_with(binary(), fun((binary()) -> AFET), fun((binary()) -> AFEU)) -> {ok,
leda_result(AFET, AFEU)} |
{error, leda_error()}.
parse_with(Input, Node_parser, Edge_parser) ->
Lines = begin
_pipe = Input,
_pipe@1 = gleam@string:split(_pipe, <<"\n"/utf8>>),
_pipe@2 = gleam@list:index_map(
_pipe@1,
fun(Line, Idx) -> {Idx + 1, gleam@string:trim(Line)} end
),
gleam@list:filter(
_pipe@2,
fun(Pair) ->
{_, Content} = Pair,
Content /= <<""/utf8>>
end
)
end,
case Lines of
[] ->
{error, empty_input};
_ ->
do_parse(Lines, Node_parser, Edge_parser)
end.
-file("src/yog_io/leda.gleam", 758).
?DOC(
" Parses a LEDA string into a graph with String labels.\n"
"\n"
" Convenience function for the common case where both node and edge data\n"
" are just Strings.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let leda_string = \"LEDA.GRAPH\\nstring\\nstring\\n-1\\n2\\n|{Alice}|\\n|{Bob}|\\n1\\n1 2 0 |{follows}|\"\n"
"\n"
" case leda.parse(leda_string) {\n"
" Ok(result) -> {\n"
" // result.graph is Graph(String, String)\n"
" process_graph(result.graph)\n"
" }\n"
" Error(e) -> handle_error(e)\n"
" }\n"
" ```\n"
).
-spec parse(binary()) -> {ok, leda_result(binary(), binary())} |
{error, leda_error()}.
parse(Input) ->
parse_with(Input, fun(S) -> S end, fun(S@1) -> S@1 end).
-file("src/yog_io/leda.gleam", 783).
?DOC(
" Reads a graph from a LEDA file.\n"
"\n"
" Convenience function that reads node and edge data as strings.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let result = leda.read(\"graph.gw\")\n"
"\n"
" case result {\n"
" Ok(leda.LedaResult(graph, warnings)) -> {\n"
" // Use the graph\n"
" process_graph(graph)\n"
" }\n"
" Error(e) -> handle_error(e)\n"
" }\n"
" ```\n"
).
-spec read(binary()) -> {ok, leda_result(binary(), binary())} |
{error, leda_error()}.
read(Path) ->
case simplifile:read(Path) of
{ok, Content} ->
parse(Content);
{error, E} ->
{error, {read_error, Path, simplifile:describe_error(E)}}
end.
-file("src/yog_io/leda.gleam", 801).
?DOC(
" Reads a graph from a LEDA file with custom parsers.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let result = leda.read_with(\n"
" \"graph.gw\",\n"
" node_parser: fn(s) { Person(s, 0) },\n"
" edge_parser: fn(s) { case int.parse(s) { Ok(n) -> n Error(_) -> 0 } },\n"
" )\n"
" ```\n"
).
-spec read_with(binary(), fun((binary()) -> AFHR), fun((binary()) -> AFHS)) -> {ok,
leda_result(AFHR, AFHS)} |
{error, leda_error()}.
read_with(Path, Node_parser, Edge_parser) ->
case simplifile:read(Path) of
{ok, Content} ->
parse_with(Content, Node_parser, Edge_parser);
{error, E} ->
{error, {read_error, Path, simplifile:describe_error(E)}}
end.