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@json.erl
Raw

src/yog_io@json.erl

-module(yog_io@json).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/yog_io/json.gleam").
-export([default_export_options/0, export_options_with/2, error_to_string/1, to_json/2, to_json_file/3, to_d3_json/3, to_cytoscape_json/3, to_visjs_json/3, write/2, write_with/3, to_json_multi/2, to_json_file_multi/3, from_json_with/3, read_with/3, from_json/1, read/1, from_json_multi_with/3, read_multi_with/3, from_json_multi/1, read_multi/1]).
-export_type([json_format/0, json_export_options/2, json_error/0, generic_metadata/0, generic_node/1, generic_edge/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(
" JSON format for graph data exchange.\n"
"\n"
" This module provides comprehensive JSON I/O capabilities for graph data,\n"
" supporting multiple formats used by popular visualization libraries.\n"
"\n"
" ## Features\n"
"\n"
" - **Generic types** for nodes and edges\n"
" - **Reading and Writing** support for Generic format\n"
" - **Multiple JSON export formats** (D3.js, Cytoscape.js, vis.js, etc.)\n"
" - **File operations** (read/write JSON files directly)\n"
" - **Rich metadata** support (graph properties, rendering hints)\n"
" - **Format validation** and error reporting\n"
"\n"
" ## Quick Start\n"
"\n"
" ```gleam\n"
" import yog_io/json\n"
" import yog/model\n"
"\n"
" pub fn main() {\n"
" let graph =\n"
" model.new(model.Directed)\n"
" |> model.add_node(1, \"Alice\")\n"
" |> model.add_node(2, \"Bob\")\n"
" |> model.add_edge(from: 1, to: 2, with: \"follows\")\n"
"\n"
" // Export to file (simple)\n"
" let assert Ok(_) = json.write(\"graph.json\", graph)\n"
"\n"
" // Or with custom options\n"
" let assert Ok(_) = json.to_json_file(\n"
" graph,\n"
" \"graph.json\",\n"
" json.default_export_options(),\n"
" )\n"
" }\n"
" ```\n"
"\n"
" ## Format Support\n"
"\n"
" - **Generic**: Full metadata with type preservation\n"
" - **D3Force**: D3.js force-directed graphs\n"
" - **Cytoscape**: Cytoscape.js network visualization\n"
" - **VisJs**: vis.js network format\n"
" - **NetworkX**: Python NetworkX compatibility\n"
"\n"
" ## References\n"
"\n"
" - [JSON Specification](https://www.json.org/)\n"
" - [D3.js](https://d3js.org/)\n"
" - [Cytoscape.js](https://js.cytoscape.org/)\n"
" - [vis.js](https://visjs.github.io/vis-network/)\n"
).
-type json_format() :: generic | d3_force | cytoscape | vis_js | network_x.
-type json_export_options(ADHT, ADHU) :: {json_export_options,
json_format(),
boolean(),
gleam@option:option(fun((ADHT) -> gleam@json:json())),
gleam@option:option(fun((ADHU) -> gleam@json:json())),
boolean(),
gleam@option:option(gleam@dict:dict(binary(), gleam@json:json()))}.
-type json_error() :: {file_not_found, binary()} |
{invalid_json, binary()} |
{write_error, binary(), binary()} |
{read_error, binary(), binary()} |
{unsupported_format, binary()}.
-type generic_metadata() :: {generic_metadata, binary(), boolean()}.
-type generic_node(ADHV) :: {generic_node, integer(), ADHV}.
-type generic_edge(ADHW) :: {generic_edge,
gleam@option:option(integer()),
integer(),
integer(),
ADHW}.
-file("src/yog_io/json.gleam", 130).
?DOC(
" Creates default export options for String node and edge data.\n"
"\n"
" ## Default Settings\n"
"\n"
" - Format: Generic\n"
" - Include metadata: True\n"
" - Pretty print: True\n"
" - Node serializer: Converts strings to JSON strings\n"
" - Edge serializer: Converts strings to JSON strings\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let options = json.default_export_options()\n"
" json.to_json(graph, options)\n"
" ```\n"
).
-spec default_export_options() -> json_export_options(binary(), binary()).
default_export_options() ->
{json_export_options,
generic,
true,
{some, fun gleam@json:string/1},
{some, fun gleam@json:string/1},
true,
none}.
-file("src/yog_io/json.gleam", 163).
?DOC(
" Creates export options with custom serializers for generic types.\n"
"\n"
" Use this when your graph contains custom data types that need\n"
" special conversion to JSON.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" pub type Person {\n"
" Person(name: String, age: Int)\n"
" }\n"
"\n"
" let options = json.export_options_with(\n"
" node_serializer: fn(person) {\n"
" json.object([\n"
" #(\"name\", json.string(person.name)),\n"
" #(\"age\", json.int(person.age)),\n"
" ])\n"
" },\n"
" edge_serializer: fn(weight) { json.int(weight) },\n"
" )\n"
" ```\n"
).
-spec export_options_with(
fun((ADHZ) -> gleam@json:json()),
fun((ADIA) -> gleam@json:json())
) -> json_export_options(ADHZ, ADIA).
export_options_with(Node_serializer, Edge_serializer) ->
{json_export_options,
generic,
true,
{some, Node_serializer},
{some, Edge_serializer},
true,
none}.
-file("src/yog_io/json.gleam", 622).
-spec serialize_nodes_generic(
yog@model:graph(ADMP, ADMQ),
json_export_options(ADMP, ADMQ)
) -> list(gleam@json:json()).
serialize_nodes_generic(Graph, Options) ->
gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Data_json = case erlang:element(4, Options) of
{some, Serializer} ->
Serializer(Data);
none ->
gleam@json:null()
end,
Node_obj = gleam@json:object(
[{<<"id"/utf8>>, gleam@json:int(Id)},
{<<"data"/utf8>>, Data_json}]
),
[Node_obj | Acc]
end
).
-file("src/yog_io/json.gleam", 638).
-spec serialize_nodes_d3(
yog@model:graph(ADMW, ADMX),
json_export_options(ADMW, ADMX)
) -> list(gleam@json:json()).
serialize_nodes_d3(Graph, Options) ->
gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Id_str = erlang:integer_to_binary(Id),
Base = [{<<"id"/utf8>>, gleam@json:string(Id_str)}],
With_data = case erlang:element(4, Options) of
{some, Serializer} ->
[{<<"data"/utf8>>, Serializer(Data)} | Base];
none ->
Base
end,
[gleam@json:object(With_data) | Acc]
end
).
-file("src/yog_io/json.gleam", 659).
-spec serialize_nodes_cytoscape(
yog@model:graph(ADND, ADNE),
json_export_options(ADND, ADNE)
) -> list(gleam@json:json()).
serialize_nodes_cytoscape(Graph, Options) ->
gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Data_fields = [{<<"id"/utf8>>,
gleam@json:string(erlang:integer_to_binary(Id))}],
Data_obj = case erlang:element(4, Options) of
{some, Serializer} ->
gleam@json:object(
[{<<"label"/utf8>>, Serializer(Data)} | Data_fields]
);
none ->
gleam@json:object(Data_fields)
end,
Node_obj = gleam@json:object([{<<"data"/utf8>>, Data_obj}]),
[Node_obj | Acc]
end
).
-file("src/yog_io/json.gleam", 679).
-spec serialize_nodes_visjs(
yog@model:graph(ADNK, ADNL),
json_export_options(ADNK, ADNL)
) -> list(gleam@json:json()).
serialize_nodes_visjs(Graph, Options) ->
gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Base = [{<<"id"/utf8>>, gleam@json:int(Id)}],
With_label = case erlang:element(4, Options) of
{some, Serializer} ->
[{<<"label"/utf8>>, Serializer(Data)} | Base];
none ->
Base
end,
[gleam@json:object(With_label) | Acc]
end
).
-file("src/yog_io/json.gleam", 695).
-spec serialize_nodes_networkx(
yog@model:graph(ADNR, ADNS),
json_export_options(ADNR, ADNS)
) -> list(gleam@json:json()).
serialize_nodes_networkx(Graph, Options) ->
gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Base = [{<<"id"/utf8>>, gleam@json:int(Id)}],
With_data = case erlang:element(4, Options) of
{some, Serializer} ->
[{<<"data"/utf8>>, Serializer(Data)} | Base];
none ->
Base
end,
[gleam@json:object(With_data) | Acc]
end
).
-file("src/yog_io/json.gleam", 713).
-spec serialize_edges_generic(
yog@model:graph(ADNY, ADNZ),
json_export_options(ADNY, ADNZ)
) -> list(gleam@json:json()).
serialize_edges_generic(Graph, Options) ->
gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, From_id, Targets) ->
Inner_edges = gleam@dict:fold(
Targets,
[],
fun(Inner_acc, To_id, Edge_data) ->
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Inner_acc;
_ ->
Data_json = case erlang:element(5, Options) of
{some, Serializer} ->
Serializer(Edge_data);
none ->
gleam@json:null()
end,
Edge_obj = gleam@json:object(
[{<<"source"/utf8>>, gleam@json:int(From_id)},
{<<"target"/utf8>>, gleam@json:int(To_id)},
{<<"data"/utf8>>, Data_json}]
),
[Edge_obj | Inner_acc]
end
end
),
lists:append([Inner_edges, Acc])
end
).
-file("src/yog_io/json.gleam", 743).
-spec serialize_edges_d3(
yog@model:graph(ADOF, ADOG),
json_export_options(ADOF, ADOG)
) -> list(gleam@json:json()).
serialize_edges_d3(Graph, Options) ->
gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, From_id, Targets) ->
Inner_edges = gleam@dict:fold(
Targets,
[],
fun(Inner_acc, To_id, Edge_data) ->
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Inner_acc;
_ ->
Base = [{<<"source"/utf8>>,
gleam@json:string(
erlang:integer_to_binary(From_id)
)},
{<<"target"/utf8>>,
gleam@json:string(
erlang:integer_to_binary(To_id)
)}],
With_data = case erlang:element(5, Options) of
{some, Serializer} ->
[{<<"value"/utf8>>, Serializer(Edge_data)} |
Base];
none ->
Base
end,
[gleam@json:object(With_data) | Inner_acc]
end
end
),
lists:append([Inner_edges, Acc])
end
).
-file("src/yog_io/json.gleam", 555).
-spec to_d3_force_format(
yog@model:graph(ADLR, ADLS),
json_export_options(ADLR, ADLS)
) -> gleam@json:json().
to_d3_force_format(Graph, Options) ->
Nodes_json = serialize_nodes_d3(Graph, Options),
Edges_json = serialize_edges_d3(Graph, Options),
gleam@json:object(
[{<<"nodes"/utf8>>,
gleam@json:array(Nodes_json, fun gleam@function:identity/1)},
{<<"links"/utf8>>,
gleam@json:array(Edges_json, fun gleam@function:identity/1)}]
).
-file("src/yog_io/json.gleam", 771).
-spec serialize_edges_cytoscape(
yog@model:graph(ADOM, ADON),
json_export_options(ADOM, ADON)
) -> list(gleam@json:json()).
serialize_edges_cytoscape(Graph, Options) ->
gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, From_id, Targets) ->
Inner_edges = gleam@dict:fold(
Targets,
[],
fun(Inner_acc, To_id, Edge_data) ->
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Inner_acc;
_ ->
Edge_id = <<<<<<"e"/utf8,
(erlang:integer_to_binary(From_id))/binary>>/binary,
"_"/utf8>>/binary,
(erlang:integer_to_binary(To_id))/binary>>,
Data_fields = [{<<"id"/utf8>>,
gleam@json:string(Edge_id)},
{<<"source"/utf8>>,
gleam@json:string(
erlang:integer_to_binary(From_id)
)},
{<<"target"/utf8>>,
gleam@json:string(
erlang:integer_to_binary(To_id)
)}],
Data_obj = case erlang:element(5, Options) of
{some, Serializer} ->
gleam@json:object(
[{<<"weight"/utf8>>,
Serializer(Edge_data)} |
Data_fields]
);
none ->
gleam@json:object(Data_fields)
end,
Edge_obj = gleam@json:object(
[{<<"data"/utf8>>, Data_obj}]
),
[Edge_obj | Inner_acc]
end
end
),
lists:append([Inner_edges, Acc])
end
).
-file("src/yog_io/json.gleam", 568).
-spec to_cytoscape_format(
yog@model:graph(ADLX, ADLY),
json_export_options(ADLX, ADLY)
) -> gleam@json:json().
to_cytoscape_format(Graph, Options) ->
Nodes_json = serialize_nodes_cytoscape(Graph, Options),
Edges_json = serialize_edges_cytoscape(Graph, Options),
gleam@json:object(
[{<<"elements"/utf8>>,
gleam@json:object(
[{<<"nodes"/utf8>>,
gleam@json:array(
Nodes_json,
fun gleam@function:identity/1
)},
{<<"edges"/utf8>>,
gleam@json:array(
Edges_json,
fun gleam@function:identity/1
)}]
)}]
).
-file("src/yog_io/json.gleam", 807).
-spec serialize_edges_visjs(
yog@model:graph(ADOT, ADOU),
json_export_options(ADOT, ADOU)
) -> list(gleam@json:json()).
serialize_edges_visjs(Graph, Options) ->
gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, From_id, Targets) ->
Inner_edges = gleam@dict:fold(
Targets,
[],
fun(Inner_acc, To_id, Edge_data) ->
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Inner_acc;
_ ->
Base = [{<<"from"/utf8>>, gleam@json:int(From_id)},
{<<"to"/utf8>>, gleam@json:int(To_id)}],
With_data = case erlang:element(5, Options) of
{some, Serializer} ->
[{<<"label"/utf8>>, Serializer(Edge_data)} |
Base];
none ->
Base
end,
[gleam@json:object(With_data) | Inner_acc]
end
end
),
lists:append([Inner_edges, Acc])
end
).
-file("src/yog_io/json.gleam", 586).
-spec to_visjs_format(
yog@model:graph(ADMD, ADME),
json_export_options(ADMD, ADME)
) -> gleam@json:json().
to_visjs_format(Graph, Options) ->
Nodes_json = serialize_nodes_visjs(Graph, Options),
Edges_json = serialize_edges_visjs(Graph, Options),
gleam@json:object(
[{<<"nodes"/utf8>>,
gleam@json:array(Nodes_json, fun gleam@function:identity/1)},
{<<"edges"/utf8>>,
gleam@json:array(Edges_json, fun gleam@function:identity/1)}]
).
-file("src/yog_io/json.gleam", 835).
-spec serialize_edges_networkx(
yog@model:graph(ADPA, ADPB),
json_export_options(ADPA, ADPB)
) -> list(gleam@json:json()).
serialize_edges_networkx(Graph, Options) ->
gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, From_id, Targets) ->
Inner_edges = gleam@dict:fold(
Targets,
[],
fun(Inner_acc, To_id, Edge_data) ->
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Inner_acc;
_ ->
Base = [{<<"source"/utf8>>, gleam@json:int(From_id)},
{<<"target"/utf8>>, gleam@json:int(To_id)}],
With_data = case erlang:element(5, Options) of
{some, Serializer} ->
[{<<"weight"/utf8>>, Serializer(Edge_data)} |
Base];
none ->
Base
end,
[gleam@json:object(With_data) | Inner_acc]
end
end
),
lists:append([Inner_edges, Acc])
end
).
-file("src/yog_io/json.gleam", 599).
-spec to_networkx_format(
yog@model:graph(ADMJ, ADMK),
json_export_options(ADMJ, ADMK)
) -> gleam@json:json().
to_networkx_format(Graph, Options) ->
Nodes_json = serialize_nodes_networkx(Graph, Options),
Edges_json = serialize_edges_networkx(Graph, Options),
Directed = case erlang:element(2, Graph) of
directed ->
true;
undirected ->
false
end,
gleam@json:object(
[{<<"directed"/utf8>>, gleam@json:bool(Directed)},
{<<"multigraph"/utf8>>, gleam@json:bool(false)},
{<<"graph"/utf8>>, gleam@json:object([])},
{<<"nodes"/utf8>>,
gleam@json:array(Nodes_json, fun gleam@function:identity/1)},
{<<"links"/utf8>>,
gleam@json:array(Edges_json, fun gleam@function:identity/1)}]
).
-file("src/yog_io/json.gleam", 896).
-spec count_edges(yog@model:graph(any(), any())) -> integer().
count_edges(Graph) ->
case case erlang:element(2, Graph) of
undirected ->
2;
directed ->
1
end of
0 -> 0;
Gleam@denominator -> gleam@dict:fold(
erlang:element(4, Graph),
0,
fun(Acc, _, Targets) -> Acc + maps:size(Targets) end
)
div Gleam@denominator
end.
-file("src/yog_io/json.gleam", 865).
-spec build_metadata(
yog@model:graph(ADPH, ADPI),
json_export_options(ADPH, ADPI)
) -> gleam@json:json().
build_metadata(Graph, Options) ->
Graph_type = case erlang:element(2, Graph) of
directed ->
<<"directed"/utf8>>;
undirected ->
<<"undirected"/utf8>>
end,
Base_metadata = [{<<"graph_type"/utf8>>, gleam@json:string(Graph_type)},
{<<"node_count"/utf8>>,
gleam@json:int(maps:size(erlang:element(3, Graph)))},
{<<"edge_count"/utf8>>, gleam@json:int(count_edges(Graph))}],
With_custom = case erlang:element(7, Options) of
{some, Custom_metadata} ->
Custom_list = begin
_pipe = maps:to_list(Custom_metadata),
gleam@list:map(
_pipe,
fun(Pair) ->
{Key, Value} = Pair,
{Key, Value}
end
)
end,
lists:append(Base_metadata, Custom_list);
none ->
Base_metadata
end,
gleam@json:object(With_custom).
-file("src/yog_io/json.gleam", 530).
-spec to_generic_format(
yog@model:graph(ADLL, ADLM),
json_export_options(ADLL, ADLM)
) -> gleam@json:json().
to_generic_format(Graph, Options) ->
Nodes_json = serialize_nodes_generic(Graph, Options),
Edges_json = serialize_edges_generic(Graph, Options),
Base_object = [{<<"format"/utf8>>,
gleam@json:string(<<"yog-generic"/utf8>>)},
{<<"version"/utf8>>, gleam@json:string(<<"2.0"/utf8>>)},
{<<"nodes"/utf8>>,
gleam@json:array(Nodes_json, fun gleam@function:identity/1)},
{<<"edges"/utf8>>,
gleam@json:array(Edges_json, fun gleam@function:identity/1)}],
With_metadata = case erlang:element(3, Options) of
true ->
Metadata = build_metadata(Graph, Options),
[{<<"metadata"/utf8>>, Metadata} | Base_object];
false ->
Base_object
end,
gleam@json:object(With_metadata).
-file("src/yog_io/json.gleam", 916).
?DOC(
" Converts a JsonError to a human-readable string.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" case json.to_json_file(graph, \"output.json\", options) {\n"
" Ok(_) -> \"Success!\"\n"
" Error(e) -> json.error_to_string(e)\n"
" }\n"
" ```\n"
).
-spec error_to_string(json_error()) -> binary().
error_to_string(Error) ->
case Error of
{file_not_found, Path} ->
<<"File not found: "/utf8, Path/binary>>;
{invalid_json, Msg} ->
<<"Invalid JSON: "/utf8, Msg/binary>>;
{write_error, Path@1, Msg@1} ->
<<<<<<"Write error at "/utf8, Path@1/binary>>/binary, ": "/utf8>>/binary,
Msg@1/binary>>;
{read_error, Path@2, Msg@2} ->
<<<<<<"Read error at "/utf8, Path@2/binary>>/binary, ": "/utf8>>/binary,
Msg@2/binary>>;
{unsupported_format, Fmt} ->
<<"Unsupported format: "/utf8, Fmt/binary>>
end.
-file("src/yog_io/json.gleam", 937).
-spec pretty_print_loop(
list(binary()),
integer(),
list(binary()),
list(binary()),
boolean()
) -> list(binary()).
pretty_print_loop(Chars, Indent, Acc, Indent_stack, In_string) ->
case Chars of
[] ->
Acc;
[Char | Rest] ->
case In_string of
true ->
case Char of
<<"\""/utf8>> ->
case Acc of
[<<"\\"/utf8>> | _] ->
pretty_print_loop(
Rest,
Indent,
[Char | Acc],
Indent_stack,
true
);
_ ->
pretty_print_loop(
Rest,
Indent,
[Char | Acc],
Indent_stack,
false
)
end;
_ ->
pretty_print_loop(
Rest,
Indent,
[Char | Acc],
Indent_stack,
true
)
end;
false ->
case Char of
<<"{"/utf8>> ->
New_indent = Indent + 2,
Indent_str = gleam@string:repeat(
<<" "/utf8>>,
New_indent
),
New_acc = case Acc of
[] ->
[Char];
_ ->
[Indent_str, <<"\n"/utf8>>, Char | Acc]
end,
pretty_print_loop(
Rest,
New_indent,
New_acc,
[Indent_str | Indent_stack],
false
);
<<"["/utf8>> ->
New_indent = Indent + 2,
Indent_str = gleam@string:repeat(
<<" "/utf8>>,
New_indent
),
New_acc = case Acc of
[] ->
[Char];
_ ->
[Indent_str, <<"\n"/utf8>>, Char | Acc]
end,
pretty_print_loop(
Rest,
New_indent,
New_acc,
[Indent_str | Indent_stack],
false
);
<<"}"/utf8>> ->
New_indent@1 = gleam@int:max(0, Indent - 2),
New_stack = case Indent_stack of
[_ | St] ->
St;
[] ->
[]
end,
Current_indent = case New_stack of
[S | _] ->
S;
[] ->
<<""/utf8>>
end,
New_acc@1 = [Current_indent,
<<"\n"/utf8>>,
Char |
Acc],
pretty_print_loop(
Rest,
New_indent@1,
New_acc@1,
New_stack,
false
);
<<"]"/utf8>> ->
New_indent@1 = gleam@int:max(0, Indent - 2),
New_stack = case Indent_stack of
[_ | St] ->
St;
[] ->
[]
end,
Current_indent = case New_stack of
[S | _] ->
S;
[] ->
<<""/utf8>>
end,
New_acc@1 = [Current_indent,
<<"\n"/utf8>>,
Char |
Acc],
pretty_print_loop(
Rest,
New_indent@1,
New_acc@1,
New_stack,
false
);
<<","/utf8>> ->
Current_indent@1 = case Indent_stack of
[S@1 | _] ->
S@1;
[] ->
<<""/utf8>>
end,
New_acc@2 = [Current_indent@1,
<<"\n"/utf8>>,
Char |
Acc],
pretty_print_loop(
Rest,
Indent,
New_acc@2,
Indent_stack,
false
);
<<":"/utf8>> ->
pretty_print_loop(
Rest,
Indent,
[Char | Acc],
Indent_stack,
false
);
<<" "/utf8>> ->
pretty_print_loop(
Rest,
Indent,
Acc,
Indent_stack,
false
);
<<"\n"/utf8>> ->
pretty_print_loop(
Rest,
Indent,
Acc,
Indent_stack,
false
);
<<"\t"/utf8>> ->
pretty_print_loop(
Rest,
Indent,
Acc,
Indent_stack,
false
);
<<"\""/utf8>> ->
pretty_print_loop(
Rest,
Indent,
[Char | Acc],
Indent_stack,
true
);
_ ->
pretty_print_loop(
Rest,
Indent,
[Char | Acc],
Indent_stack,
false
)
end
end
end.
-file("src/yog_io/json.gleam", 931).
?DOC(" Simple JSON pretty printer that adds indentation and newlines.\n").
-spec pretty_print_json(binary()) -> binary().
pretty_print_json(Json_str) ->
Chars = gleam@string:to_graphemes(Json_str),
Result = pretty_print_loop(Chars, 0, [], [], false),
erlang:list_to_binary(lists:reverse(Result)).
-file("src/yog_io/json.gleam", 201).
?DOC(
" Converts a graph to a JSON string.\n"
"\n"
" This function serializes a graph to JSON format according to the\n"
" specified options and format preset.\n"
"\n"
" **Time Complexity:** O(V + E)\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import yog/model\n"
" import yog_io/json\n"
"\n"
" pub fn main() {\n"
" let graph =\n"
" model.new(model.Directed)\n"
" |> model.add_node(1, \"Alice\")\n"
" |> model.add_node(2, \"Bob\")\n"
" |> model.add_edge(from: 1, to: 2, with: \"follows\")\n"
"\n"
" let json_string = json.to_json(graph, json.default_export_options())\n"
" // Returns JSON string representation\n"
" }\n"
" ```\n"
).
-spec to_json(yog@model:graph(ADID, ADIE), json_export_options(ADID, ADIE)) -> binary().
to_json(Graph, Options) ->
Json_obj = case erlang:element(2, Options) of
generic ->
to_generic_format(Graph, Options);
d3_force ->
to_d3_force_format(Graph, Options);
cytoscape ->
to_cytoscape_format(Graph, Options);
vis_js ->
to_visjs_format(Graph, Options);
network_x ->
to_networkx_format(Graph, Options)
end,
case erlang:element(6, Options) of
true ->
pretty_print_json(gleam@json:to_string(Json_obj));
false ->
gleam@json:to_string(Json_obj)
end.
-file("src/yog_io/json.gleam", 232).
?DOC(
" Exports a graph to a JSON file.\n"
"\n"
" This function writes the graph to a file in the specified JSON format.\n"
" The file will be created if it doesn't exist, or overwritten if it does.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" case json.to_json_file(graph, \"output.json\", json.default_export_options()) {\n"
" Ok(_) -> io.println(\"Graph saved successfully\")\n"
" Error(json.WriteError(path, error)) -> {\n"
" io.println(\"Failed to write to \" <> path <> \": \" <> error)\n"
" }\n"
" Error(_) -> io.println(\"Unknown error\")\n"
" }\n"
" ```\n"
).
-spec to_json_file(
yog@model:graph(ADIJ, ADIK),
binary(),
json_export_options(ADIJ, ADIK)
) -> {ok, nil} | {error, json_error()}.
to_json_file(Graph, Path, Options) ->
Json_string = to_json(Graph, Options),
_pipe = simplifile:write(Path, Json_string),
gleam@result:map_error(
_pipe,
fun(Error) -> {write_error, Path, gleam@string:inspect(Error)} end
).
-file("src/yog_io/json.gleam", 253).
?DOC(
" Quick export for D3.js force-directed graphs with default settings.\n"
"\n"
" This is a convenience function that exports graphs in D3.js format\n"
" with sensible defaults.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let d3_json = json.to_d3_json(graph, json.string, json.string)\n"
" ```\n"
).
-spec to_d3_json(
yog@model:graph(ADIR, ADIS),
fun((ADIR) -> gleam@json:json()),
fun((ADIS) -> gleam@json:json())
) -> binary().
to_d3_json(Graph, Node_serializer, Edge_serializer) ->
Options = {json_export_options,
d3_force,
false,
{some, Node_serializer},
{some, Edge_serializer},
true,
none},
to_json(Graph, Options).
-file("src/yog_io/json.gleam", 271).
?DOC(" Quick export for Cytoscape.js with default settings.\n").
-spec to_cytoscape_json(
yog@model:graph(ADIV, ADIW),
fun((ADIV) -> gleam@json:json()),
fun((ADIW) -> gleam@json:json())
) -> binary().
to_cytoscape_json(Graph, Node_serializer, Edge_serializer) ->
Options = {json_export_options,
cytoscape,
false,
{some, Node_serializer},
{some, Edge_serializer},
true,
none},
to_json(Graph, Options).
-file("src/yog_io/json.gleam", 289).
?DOC(" Quick export for vis.js networks with default settings.\n").
-spec to_visjs_json(
yog@model:graph(ADIZ, ADJA),
fun((ADIZ) -> gleam@json:json()),
fun((ADJA) -> gleam@json:json())
) -> binary().
to_visjs_json(Graph, Node_serializer, Edge_serializer) ->
Options = {json_export_options,
vis_js,
false,
{some, Node_serializer},
{some, Edge_serializer},
true,
none},
to_json(Graph, Options).
-file("src/yog_io/json.gleam", 326).
?DOC(
" Writes a graph to a JSON file using default export options.\n"
"\n"
" This is a convenience function for String-based graphs that uses default\n"
" JSON export options. For more control over the output format, use\n"
" `to_json_file()` or `write_with()`.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import yog/model\n"
" import yog_io/json\n"
"\n"
" let graph =\n"
" model.new(model.Directed)\n"
" |> model.add_node(1, \"Alice\")\n"
" |> model.add_node(2, \"Bob\")\n"
" |> model.add_edge(from: 1, to: 2, with: \"follows\")\n"
"\n"
" let assert Ok(Nil) = json.write(\"graph.json\", graph)\n"
" ```\n"
).
-spec write(binary(), yog@model:graph(binary(), binary())) -> {ok, nil} |
{error, json_error()}.
write(Path, Graph) ->
to_json_file(Graph, Path, default_export_options()).
-file("src/yog_io/json.gleam", 360).
?DOC(
" Writes a graph to a JSON file with custom export options.\n"
"\n"
" This function provides full control over the JSON export format and\n"
" serialization behavior.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import gleam/json as gleam_json\n"
" import yog_io/json\n"
"\n"
" pub type Person {\n"
" Person(name: String, age: Int)\n"
" }\n"
"\n"
" let options = json.export_options_with(\n"
" node_serializer: fn(person) {\n"
" gleam_json.object([\n"
" #(\"name\", gleam_json.string(person.name)),\n"
" #(\"age\", gleam_json.int(person.age)),\n"
" ])\n"
" },\n"
" edge_serializer: fn(weight) { gleam_json.int(weight) },\n"
" )\n"
"\n"
" let assert Ok(Nil) = json.write_with(\"graph.json\", options, graph)\n"
" ```\n"
).
-spec write_with(
binary(),
json_export_options(ADJH, ADJI),
yog@model:graph(ADJH, ADJI)
) -> {ok, nil} | {error, json_error()}.
write_with(Path, Options, Graph) ->
to_json_file(Graph, Path, Options).
-file("src/yog_io/json.gleam", 1214).
-spec serialize_nodes_generic_multi(
yog@multi@model:multi_graph(ADRN, ADRO),
json_export_options(ADRN, ADRO)
) -> list(gleam@json:json()).
serialize_nodes_generic_multi(Graph, Options) ->
gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Data_json = case erlang:element(4, Options) of
{some, Serializer} ->
Serializer(Data);
none ->
gleam@json:null()
end,
Node_obj = gleam@json:object(
[{<<"id"/utf8>>, gleam@json:int(Id)},
{<<"data"/utf8>>, Data_json}]
),
[Node_obj | Acc]
end
).
-file("src/yog_io/json.gleam", 1230).
-spec serialize_nodes_d3_multi(
yog@multi@model:multi_graph(ADRU, ADRV),
json_export_options(ADRU, ADRV)
) -> list(gleam@json:json()).
serialize_nodes_d3_multi(Graph, Options) ->
gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Id_str = erlang:integer_to_binary(Id),
Base = [{<<"id"/utf8>>, gleam@json:string(Id_str)}],
With_data = case erlang:element(4, Options) of
{some, Serializer} ->
[{<<"data"/utf8>>, Serializer(Data)} | Base];
none ->
Base
end,
[gleam@json:object(With_data) | Acc]
end
).
-file("src/yog_io/json.gleam", 1250).
-spec serialize_nodes_cytoscape_multi(
yog@multi@model:multi_graph(ADSB, ADSC),
json_export_options(ADSB, ADSC)
) -> list(gleam@json:json()).
serialize_nodes_cytoscape_multi(Graph, Options) ->
gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Data_fields = [{<<"id"/utf8>>,
gleam@json:string(erlang:integer_to_binary(Id))}],
Data_obj = case erlang:element(4, Options) of
{some, Serializer} ->
gleam@json:object(
[{<<"label"/utf8>>, Serializer(Data)} | Data_fields]
);
none ->
gleam@json:object(Data_fields)
end,
Node_obj = gleam@json:object([{<<"data"/utf8>>, Data_obj}]),
[Node_obj | Acc]
end
).
-file("src/yog_io/json.gleam", 1270).
-spec serialize_nodes_visjs_multi(
yog@multi@model:multi_graph(ADSI, ADSJ),
json_export_options(ADSI, ADSJ)
) -> list(gleam@json:json()).
serialize_nodes_visjs_multi(Graph, Options) ->
gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Base = [{<<"id"/utf8>>, gleam@json:int(Id)}],
With_label = case erlang:element(4, Options) of
{some, Serializer} ->
[{<<"label"/utf8>>, Serializer(Data)} | Base];
none ->
Base
end,
[gleam@json:object(With_label) | Acc]
end
).
-file("src/yog_io/json.gleam", 1286).
-spec serialize_nodes_networkx_multi(
yog@multi@model:multi_graph(ADSP, ADSQ),
json_export_options(ADSP, ADSQ)
) -> list(gleam@json:json()).
serialize_nodes_networkx_multi(Graph, Options) ->
gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Base = [{<<"id"/utf8>>, gleam@json:int(Id)}],
With_data = case erlang:element(4, Options) of
{some, Serializer} ->
[{<<"data"/utf8>>, Serializer(Data)} | Base];
none ->
Base
end,
[gleam@json:object(With_data) | Acc]
end
).
-file("src/yog_io/json.gleam", 1304).
-spec serialize_edges_generic_multi(
yog@multi@model:multi_graph(ADSW, ADSX),
json_export_options(ADSW, ADSX)
) -> list(gleam@json:json()).
serialize_edges_generic_multi(Graph, Options) ->
gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, Edge_id, Edge_tuple) ->
{From_id, To_id, Edge_data} = Edge_tuple,
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Acc;
_ ->
Data_json = case erlang:element(5, Options) of
{some, Serializer} ->
Serializer(Edge_data);
none ->
gleam@json:null()
end,
Edge_obj = gleam@json:object(
[{<<"id"/utf8>>, gleam@json:int(Edge_id)},
{<<"source"/utf8>>, gleam@json:int(From_id)},
{<<"target"/utf8>>, gleam@json:int(To_id)},
{<<"data"/utf8>>, Data_json}]
),
[Edge_obj | Acc]
end
end
).
-file("src/yog_io/json.gleam", 1334).
-spec serialize_edges_d3_multi(
yog@multi@model:multi_graph(ADTD, ADTE),
json_export_options(ADTD, ADTE)
) -> list(gleam@json:json()).
serialize_edges_d3_multi(Graph, Options) ->
gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, Edge_id, Edge_tuple) ->
{From_id, To_id, Edge_data} = Edge_tuple,
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Acc;
_ ->
From_str = erlang:integer_to_binary(From_id),
To_str = erlang:integer_to_binary(To_id),
Base = [{<<"id"/utf8>>, gleam@json:int(Edge_id)},
{<<"source"/utf8>>, gleam@json:string(From_str)},
{<<"target"/utf8>>, gleam@json:string(To_str)}],
With_data = case erlang:element(5, Options) of
{some, Serializer} ->
[{<<"value"/utf8>>, Serializer(Edge_data)} | Base];
none ->
Base
end,
[gleam@json:object(With_data) | Acc]
end
end
).
-file("src/yog_io/json.gleam", 1147).
-spec to_d3_force_format_multi(
yog@multi@model:multi_graph(ADQP, ADQQ),
json_export_options(ADQP, ADQQ)
) -> gleam@json:json().
to_d3_force_format_multi(Graph, Options) ->
Nodes_json = serialize_nodes_d3_multi(Graph, Options),
Edges_json = serialize_edges_d3_multi(Graph, Options),
gleam@json:object(
[{<<"nodes"/utf8>>,
gleam@json:array(Nodes_json, fun gleam@function:identity/1)},
{<<"links"/utf8>>,
gleam@json:array(Edges_json, fun gleam@function:identity/1)}]
).
-file("src/yog_io/json.gleam", 1364).
-spec serialize_edges_cytoscape_multi(
yog@multi@model:multi_graph(ADTK, ADTL),
json_export_options(ADTK, ADTL)
) -> list(gleam@json:json()).
serialize_edges_cytoscape_multi(Graph, Options) ->
gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, Edge_id, Edge_tuple) ->
{From_id, To_id, Edge_data} = Edge_tuple,
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Acc;
_ ->
Edge_id_str = <<"e"/utf8,
(erlang:integer_to_binary(Edge_id))/binary>>,
Source_str = erlang:integer_to_binary(From_id),
Target_str = erlang:integer_to_binary(To_id),
Data_fields = [{<<"id"/utf8>>,
gleam@json:string(Edge_id_str)},
{<<"source"/utf8>>, gleam@json:string(Source_str)},
{<<"target"/utf8>>, gleam@json:string(Target_str)}],
Data_obj = case erlang:element(5, Options) of
{some, Serializer} ->
gleam@json:object(
[{<<"label"/utf8>>, Serializer(Edge_data)} |
Data_fields]
);
none ->
gleam@json:object(Data_fields)
end,
Edge_obj = gleam@json:object([{<<"data"/utf8>>, Data_obj}]),
[Edge_obj | Acc]
end
end
).
-file("src/yog_io/json.gleam", 1160).
-spec to_cytoscape_format_multi(
yog@multi@model:multi_graph(ADQV, ADQW),
json_export_options(ADQV, ADQW)
) -> gleam@json:json().
to_cytoscape_format_multi(Graph, Options) ->
Nodes_json = serialize_nodes_cytoscape_multi(Graph, Options),
Edges_json = serialize_edges_cytoscape_multi(Graph, Options),
gleam@json:object(
[{<<"elements"/utf8>>,
gleam@json:object(
[{<<"nodes"/utf8>>,
gleam@json:array(
Nodes_json,
fun gleam@function:identity/1
)},
{<<"edges"/utf8>>,
gleam@json:array(
Edges_json,
fun gleam@function:identity/1
)}]
)}]
).
-file("src/yog_io/json.gleam", 1399).
-spec serialize_edges_visjs_multi(
yog@multi@model:multi_graph(ADTR, ADTS),
json_export_options(ADTR, ADTS)
) -> list(gleam@json:json()).
serialize_edges_visjs_multi(Graph, Options) ->
gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, Edge_id, Edge_tuple) ->
{From_id, To_id, Edge_data} = Edge_tuple,
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Acc;
_ ->
Base = [{<<"id"/utf8>>, gleam@json:int(Edge_id)},
{<<"from"/utf8>>, gleam@json:int(From_id)},
{<<"to"/utf8>>, gleam@json:int(To_id)}],
With_label = case erlang:element(5, Options) of
{some, Serializer} ->
[{<<"label"/utf8>>, Serializer(Edge_data)} | Base];
none ->
Base
end,
[gleam@json:object(With_label) | Acc]
end
end
).
-file("src/yog_io/json.gleam", 1178).
-spec to_visjs_format_multi(
yog@multi@model:multi_graph(ADRB, ADRC),
json_export_options(ADRB, ADRC)
) -> gleam@json:json().
to_visjs_format_multi(Graph, Options) ->
Nodes_json = serialize_nodes_visjs_multi(Graph, Options),
Edges_json = serialize_edges_visjs_multi(Graph, Options),
gleam@json:object(
[{<<"nodes"/utf8>>,
gleam@json:array(Nodes_json, fun gleam@function:identity/1)},
{<<"edges"/utf8>>,
gleam@json:array(Edges_json, fun gleam@function:identity/1)}]
).
-file("src/yog_io/json.gleam", 1426).
-spec serialize_edges_networkx_multi(
yog@multi@model:multi_graph(ADTY, ADTZ),
json_export_options(ADTY, ADTZ)
) -> list(gleam@json:json()).
serialize_edges_networkx_multi(Graph, Options) ->
gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, Edge_id, Edge_tuple) ->
{From_id, To_id, Edge_data} = Edge_tuple,
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Acc;
_ ->
Base = [{<<"id"/utf8>>, gleam@json:int(Edge_id)},
{<<"source"/utf8>>, gleam@json:int(From_id)},
{<<"target"/utf8>>, gleam@json:int(To_id)}],
With_data = case erlang:element(5, Options) of
{some, Serializer} ->
[{<<"weight"/utf8>>, Serializer(Edge_data)} | Base];
none ->
Base
end,
[gleam@json:object(With_data) | Acc]
end
end
).
-file("src/yog_io/json.gleam", 1191).
-spec to_networkx_format_multi(
yog@multi@model:multi_graph(ADRH, ADRI),
json_export_options(ADRH, ADRI)
) -> gleam@json:json().
to_networkx_format_multi(Graph, Options) ->
Nodes_json = serialize_nodes_networkx_multi(Graph, Options),
Edges_json = serialize_edges_networkx_multi(Graph, Options),
Directed = case erlang:element(2, Graph) of
directed ->
true;
undirected ->
false
end,
gleam@json:object(
[{<<"directed"/utf8>>, gleam@json:bool(Directed)},
{<<"multigraph"/utf8>>, gleam@json:bool(true)},
{<<"graph"/utf8>>, gleam@json:object([])},
{<<"nodes"/utf8>>,
gleam@json:array(Nodes_json, fun gleam@function:identity/1)},
{<<"links"/utf8>>,
gleam@json:array(Edges_json, fun gleam@function:identity/1)}]
).
-file("src/yog_io/json.gleam", 1455).
-spec build_metadata_multi(
yog@multi@model:multi_graph(ADUF, ADUG),
json_export_options(ADUF, ADUG)
) -> gleam@json:json().
build_metadata_multi(Graph, Options) ->
Graph_type = case erlang:element(2, Graph) of
directed ->
<<"directed"/utf8>>;
undirected ->
<<"undirected"/utf8>>
end,
Base_metadata = [{<<"graph_type"/utf8>>, gleam@json:string(Graph_type)},
{<<"multigraph"/utf8>>, gleam@json:bool(true)},
{<<"node_count"/utf8>>,
gleam@json:int(maps:size(erlang:element(3, Graph)))},
{<<"edge_count"/utf8>>,
gleam@json:int(maps:size(erlang:element(4, Graph)))}],
With_custom = case erlang:element(7, Options) of
{some, Custom_metadata} ->
Custom_list = begin
_pipe = maps:to_list(Custom_metadata),
gleam@list:map(
_pipe,
fun(Pair) ->
{Key, Value} = Pair,
{Key, Value}
end
)
end,
lists:append(Custom_list, Base_metadata);
none ->
Base_metadata
end,
gleam@json:object(With_custom).
-file("src/yog_io/json.gleam", 1122).
-spec to_generic_format_multi(
yog@multi@model:multi_graph(ADQJ, ADQK),
json_export_options(ADQJ, ADQK)
) -> gleam@json:json().
to_generic_format_multi(Graph, Options) ->
Nodes_json = serialize_nodes_generic_multi(Graph, Options),
Edges_json = serialize_edges_generic_multi(Graph, Options),
Base_object = [{<<"format"/utf8>>,
gleam@json:string(<<"yog-generic"/utf8>>)},
{<<"version"/utf8>>, gleam@json:string(<<"2.0"/utf8>>)},
{<<"nodes"/utf8>>,
gleam@json:array(Nodes_json, fun gleam@function:identity/1)},
{<<"edges"/utf8>>,
gleam@json:array(Edges_json, fun gleam@function:identity/1)}],
With_metadata = case erlang:element(3, Options) of
true ->
Metadata = build_metadata_multi(Graph, Options),
[{<<"metadata"/utf8>>, Metadata} | Base_object];
false ->
Base_object
end,
gleam@json:object(With_metadata).
-file("src/yog_io/json.gleam", 1075).
?DOC(
" Converts a multigraph to a JSON string.\n"
"\n"
" This function serializes a multigraph to JSON format with edge IDs\n"
" to preserve parallel edges. All formats include unique edge identifiers.\n"
"\n"
" **Time Complexity:** O(V + E)\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import yog/multi/model as multi\n"
" import yog_io/json\n"
"\n"
" pub fn main() {\n"
" let graph =\n"
" multi.new(multi.Directed)\n"
" |> multi.add_node(1, \"Alice\")\n"
" |> multi.add_node(2, \"Bob\")\n"
" |> multi.add_edge(from: 1, to: 2, with: \"follows\")\n"
" |> multi.add_edge(from: 1, to: 2, with: \"mentions\")\n"
"\n"
" let json_string = json.to_json_multi(graph, json.default_export_options())\n"
" // Returns JSON with edge IDs for parallel edges\n"
" }\n"
" ```\n"
).
-spec to_json_multi(
yog@multi@model:multi_graph(ADPV, ADPW),
json_export_options(ADPV, ADPW)
) -> binary().
to_json_multi(Graph, Options) ->
Json_obj = case erlang:element(2, Options) of
generic ->
to_generic_format_multi(Graph, Options);
d3_force ->
to_d3_force_format_multi(Graph, Options);
cytoscape ->
to_cytoscape_format_multi(Graph, Options);
vis_js ->
to_visjs_format_multi(Graph, Options);
network_x ->
to_networkx_format_multi(Graph, Options)
end,
case erlang:element(6, Options) of
true ->
pretty_print_json(gleam@json:to_string(Json_obj));
false ->
gleam@json:to_string(Json_obj)
end.
-file("src/yog_io/json.gleam", 1109).
?DOC(
" Exports a multigraph to a JSON file.\n"
"\n"
" This function writes the multigraph to a file in the specified JSON format.\n"
" The file will be created if it doesn't exist, or overwritten if it does.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" case json.to_json_file_multi(graph, \"output.json\", json.default_export_options()) {\n"
" Ok(_) -> io.println(\"Multigraph saved successfully\")\n"
" Error(json.WriteError(path, error)) -> {\n"
" io.println(\"Failed to write to \" <> path <> \": \" <> error)\n"
" }\n"
" Error(_) -> io.println(\"Unknown error\")\n"
" }\n"
" ```\n"
).
-spec to_json_file_multi(
yog@multi@model:multi_graph(ADQB, ADQC),
binary(),
json_export_options(ADQB, ADQC)
) -> {ok, nil} | {error, json_error()}.
to_json_file_multi(Graph, Path, Options) ->
Json_string = to_json_multi(Graph, Options),
_pipe = simplifile:write(Path, Json_string),
gleam@result:map_error(
_pipe,
fun(Error) -> {write_error, Path, gleam@string:inspect(Error)} end
).
-file("src/yog_io/json.gleam", 1501).
-spec build_metadata_decoder() -> gleam@dynamic@decode:decoder(generic_metadata()).
build_metadata_decoder() ->
gleam@dynamic@decode:field(
<<"graph_type"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Graph_type) ->
gleam@dynamic@decode:optional_field(
<<"multigraph"/utf8>>,
false,
{decoder, fun gleam@dynamic@decode:decode_bool/1},
fun(Multigraph) ->
gleam@dynamic@decode:success(
{generic_metadata, Graph_type, Multigraph}
)
end
)
end
).
-file("src/yog_io/json.gleam", 1507).
-spec decode_node(gleam@dynamic@decode:decoder(ADUM)) -> gleam@dynamic@decode:decoder(generic_node(ADUM)).
decode_node(Data_decoder) ->
gleam@dynamic@decode:field(
<<"id"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Id) ->
gleam@dynamic@decode:field(
<<"data"/utf8>>,
Data_decoder,
fun(Data) ->
gleam@dynamic@decode:success({generic_node, Id, Data})
end
)
end
).
-file("src/yog_io/json.gleam", 1515).
-spec decode_edge(gleam@dynamic@decode:decoder(ADUQ)) -> gleam@dynamic@decode:decoder(generic_edge(ADUQ)).
decode_edge(Data_decoder) ->
gleam@dynamic@decode:optional_field(
<<"id"/utf8>>,
none,
gleam@dynamic@decode:optional(
{decoder, fun gleam@dynamic@decode:decode_int/1}
),
fun(Id) ->
gleam@dynamic@decode:field(
<<"source"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Source) ->
gleam@dynamic@decode:field(
<<"target"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Target) ->
gleam@dynamic@decode:field(
<<"data"/utf8>>,
Data_decoder,
fun(Data) ->
gleam@dynamic@decode:success(
{generic_edge, Id, Source, Target, Data}
)
end
)
end
)
end
)
end
).
-file("src/yog_io/json.gleam", 429).
?DOC(
" Parses a graph from a JSON string with custom data decoders.\n"
"\n"
" This function supports the standard `yog-generic` JSON format.\n"
).
-spec from_json_with(
binary(),
gleam@dynamic@decode:decoder(ADKV),
gleam@dynamic@decode:decoder(ADKX)
) -> {ok, yog@model:graph(ADKV, ADKX)} | {error, json_error()}.
from_json_with(Json_string, Node_data_decoder, Edge_data_decoder) ->
Main_decoder = begin
gleam@dynamic@decode:field(
<<"format"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Format) -> case Format of
<<"yog-generic"/utf8>> ->
gleam@dynamic@decode:field(
<<"metadata"/utf8>>,
build_metadata_decoder(),
fun(Meta) ->
gleam@dynamic@decode:field(
<<"nodes"/utf8>>,
gleam@dynamic@decode:list(
decode_node(Node_data_decoder)
),
fun(Nodes) ->
gleam@dynamic@decode:field(
<<"edges"/utf8>>,
gleam@dynamic@decode:list(
decode_edge(Edge_data_decoder)
),
fun(Edges) ->
gleam@dynamic@decode:success(
{Meta, Nodes, Edges}
)
end
)
end
)
end
);
Fmt ->
gleam@dynamic@decode:failure(
{{generic_metadata, <<""/utf8>>, false}, [], []},
Fmt
)
end end
)
end,
_pipe = gleam@json:parse(Json_string, Main_decoder),
_pipe@1 = gleam@result:map_error(
_pipe,
fun(Err) -> {invalid_json, gleam@string:inspect(Err)} end
),
gleam@result:'try'(
_pipe@1,
fun(Data) ->
{Meta@1, Nodes@1, Edges@1} = Data,
Kind = case erlang:element(2, Meta@1) of
<<"undirected"/utf8>> ->
undirected;
_ ->
directed
end,
Graph = gleam@list:fold(
Nodes@1,
yog@model:new(Kind),
fun(G, N) ->
yog@model:add_node(
G,
erlang:element(2, N),
erlang:element(3, N)
)
end
),
gleam@list:try_fold(
Edges@1,
Graph,
fun(G@1, E) ->
_pipe@2 = yog@model:add_edge(
G@1,
erlang:element(3, E),
erlang:element(4, E),
erlang:element(5, E)
),
gleam@result:map_error(
_pipe@2,
fun(Err@1) ->
{invalid_json,
<<"Edge creation error: "/utf8, Err@1/binary>>}
end
)
end
)
end
).
-file("src/yog_io/json.gleam", 391).
?DOC(" Reads a graph from a JSON file with custom data decoders.\n").
-spec read_with(
binary(),
gleam@dynamic@decode:decoder(ADJX),
gleam@dynamic@decode:decoder(ADJZ)
) -> {ok, yog@model:graph(ADJX, ADJZ)} | {error, json_error()}.
read_with(Path, Node_decoder, Edge_decoder) ->
_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(S) -> from_json_with(S, Node_decoder, Edge_decoder) end
).
-file("src/yog_io/json.gleam", 413).
?DOC(" Parses a graph from a JSON string representation.\n").
-spec from_json(binary()) -> {ok, yog@model:graph(binary(), binary())} |
{error, json_error()}.
from_json(Json_string) ->
from_json_with(
Json_string,
{decoder, fun gleam@dynamic@decode:decode_string/1},
{decoder, fun gleam@dynamic@decode:decode_string/1}
).
-file("src/yog_io/json.gleam", 375).
?DOC(
" Reads a graph from a JSON file using default options.\n"
"\n"
" This currently supports only the `Generic` format (`yog-generic`).\n"
).
-spec read(binary()) -> {ok, yog@model:graph(binary(), binary())} |
{error, json_error()}.
read(Path) ->
_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 from_json/1).
-file("src/yog_io/json.gleam", 479).
?DOC(
" Parses a multigraph from a JSON string with custom data decoders.\n"
"\n"
" This function supports the standard `yog-generic` JSON format.\n"
).
-spec from_json_multi_with(
binary(),
gleam@dynamic@decode:decoder(ADLD),
gleam@dynamic@decode:decoder(ADLF)
) -> {ok, yog@multi@model:multi_graph(ADLD, ADLF)} | {error, json_error()}.
from_json_multi_with(Json_string, Node_data_decoder, Edge_data_decoder) ->
Main_decoder = begin
gleam@dynamic@decode:field(
<<"format"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Format) -> case Format of
<<"yog-generic"/utf8>> ->
gleam@dynamic@decode:field(
<<"metadata"/utf8>>,
build_metadata_decoder(),
fun(Meta) ->
gleam@dynamic@decode:field(
<<"nodes"/utf8>>,
gleam@dynamic@decode:list(
decode_node(Node_data_decoder)
),
fun(Nodes) ->
gleam@dynamic@decode:field(
<<"edges"/utf8>>,
gleam@dynamic@decode:list(
decode_edge(Edge_data_decoder)
),
fun(Edges) ->
gleam@dynamic@decode:success(
{Meta, Nodes, Edges}
)
end
)
end
)
end
);
Fmt ->
gleam@dynamic@decode:failure(
{{generic_metadata, <<""/utf8>>, false}, [], []},
Fmt
)
end end
)
end,
_pipe = gleam@json:parse(Json_string, Main_decoder),
_pipe@1 = gleam@result:map_error(
_pipe,
fun(Err) -> {invalid_json, gleam@string:inspect(Err)} end
),
gleam@result:'try'(
_pipe@1,
fun(Data) ->
{Meta@1, Nodes@1, Edges@1} = Data,
Kind = case erlang:element(2, Meta@1) of
<<"undirected"/utf8>> ->
undirected;
_ ->
directed
end,
Graph = gleam@list:fold(
Nodes@1,
yog@multi@model:new(Kind),
fun(G, N) ->
yog@multi@model:add_node(
G,
erlang:element(2, N),
erlang:element(3, N)
)
end
),
{Final_graph, _} = gleam@list:fold(
Edges@1,
{Graph, 0},
fun(Acc, E) ->
{G@1, Next_id} = Acc,
{New_g, _} = yog@multi@model:add_edge(
G@1,
erlang:element(3, E),
erlang:element(4, E),
erlang:element(5, E)
),
{New_g, Next_id + 1}
end
),
{ok, Final_graph}
end
).
-file("src/yog_io/json.gleam", 402).
?DOC(" Reads a multigraph from a JSON file with custom data decoders.\n").
-spec read_multi_with(
binary(),
gleam@dynamic@decode:decoder(ADKF),
gleam@dynamic@decode:decoder(ADKH)
) -> {ok, yog@multi@model:multi_graph(ADKF, ADKH)} | {error, json_error()}.
read_multi_with(Path, Node_decoder, Edge_decoder) ->
_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(S) -> from_json_multi_with(S, Node_decoder, Edge_decoder) end
).
-file("src/yog_io/json.gleam", 420).
?DOC(" Parses a multigraph from a JSON string representation.\n").
-spec from_json_multi(binary()) -> {ok,
yog@multi@model:multi_graph(binary(), binary())} |
{error, json_error()}.
from_json_multi(Json_string) ->
from_json_multi_with(
Json_string,
{decoder, fun gleam@dynamic@decode:decode_string/1},
{decoder, fun gleam@dynamic@decode:decode_string/1}
).
-file("src/yog_io/json.gleam", 382).
?DOC(" Reads a multigraph from a JSON file using default options.\n").
-spec read_multi(binary()) -> {ok,
yog@multi@model:multi_graph(binary(), binary())} |
{error, json_error()}.
read_multi(Path) ->
_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 from_json_multi/1).