Current section

Files

Jump to
gleam_mongo src mongo@client.erl
Raw

src/mongo@client.erl

-module(mongo@client).
-compile([no_auto_import, nowarn_unused_vars]).
-export([collection/2, execute/2, connect/1]).
-export_type([connection_info/0, database/0, collection/0]).
-opaque connection_info() :: {connection_info,
binary(),
integer(),
binary(),
gleam@option:option({binary(), binary()}),
gleam@option:option(binary())}.
-type database() :: {database, tcp:socket(), binary()}.
-type collection() :: {collection, database(), binary()}.
-spec collection(database(), binary()) -> collection().
collection(Db, Name) ->
{collection, Db, Name}.
-spec send_cmd(tcp:socket(), binary(), bson@types:value()) -> {ok,
list({binary(), bson@types:value()})} |
{error, nil}.
send_cmd(Socket, Db, Cmd) ->
{document, Cmd@1} = case Cmd of
{document, _} -> Cmd;
_assert_fail ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"mongo/client"/utf8>>,
function => <<"send_cmd"/utf8>>,
line => 110})
end,
Cmd@2 = gleam@list:key_set(Cmd@1, <<"$db"/utf8>>, {str, Db}),
Encoded = bson:encode(Cmd@2),
Size = gleam@bit_string:byte_size(Encoded) + 21,
Packet = begin
_pipe = [<<Size:32/little, 0:32, 0:32, 2013:32/little, 0:32, 0>>,
Encoded],
gleam@bit_string:concat(_pipe)
end,
case tcp:send(Socket, Packet) of
ok ->
case tcp:'receive'(Socket) of
{ok, Response} ->
<<_:168, Rest/bitstring>> = Response,
case bson:decode(Rest) of
{ok, Result} ->
{ok, Result};
{error, nil} ->
{error, nil}
end;
{error, nil} ->
{error, nil}
end;
_ ->
{error, nil}
end.
-spec execute(collection(), bson@types:value()) -> {ok,
list({binary(), bson@types:value()})} |
{error, {integer(), binary()}}.
execute(Collection, Cmd) ->
case erlang:element(2, Collection) of
{database, Socket, Name} ->
case send_cmd(Socket, Name, Cmd) of
{ok,
[{<<"ok"/utf8>>, {double, 0.0}},
{<<"errmsg"/utf8>>, {str, Msg}},
{<<"code"/utf8>>, {integer, Code}},
{<<"codeName"/utf8>>, _}]} ->
{error, {Code, Msg}};
{ok, Result} ->
{ok, Result};
{error, nil} ->
{error, {-2, <<""/utf8>>}}
end
end.
-spec authenticate(tcp:socket(), binary(), binary(), binary()) -> {ok, nil} |
{error, nil}.
authenticate(Socket, Username, Password, Auth_source) ->
First_payload = mongo@scram:first_payload(Username),
First = mongo@scram:first_message(First_payload),
gleam@result:then(
send_cmd(Socket, Auth_source, First),
fun(Reply) ->
gleam@result:then(
mongo@scram:parse_first_reply(Reply),
fun(_use0) ->
{Server_params, Server_payload, Cid} = _use0,
gleam@result:then(
mongo@scram:second_message(
Server_params,
First_payload,
Server_payload,
Cid,
Password
),
fun(_use0@1) ->
{Second, Server_signature} = _use0@1,
gleam@result:then(
send_cmd(Socket, Auth_source, Second),
fun(Reply@1) ->
case Reply@1 of
[{<<"ok"/utf8>>, {double, 0.0}} | _] ->
{error, nil};
Reply@2 ->
mongo@scram:parse_second_reply(
Reply@2,
Server_signature
)
end
end
)
end
)
end
)
end
).
-spec parse_connection_string(binary()) -> {ok, connection_info()} |
{error, nil}.
parse_connection_string(Uri) ->
gleam@result:then(
gleam@uri:parse(Uri),
fun(Parsed) ->
case erlang:element(2, Parsed) of
{some, <<"mongodb"/utf8>>} ->
case erlang:element(4, Parsed) of
{some, <<""/utf8>>} ->
{error, nil};
{some, Host} ->
Port = gleam@option:unwrap(
erlang:element(5, Parsed),
27017
),
case erlang:element(6, Parsed) of
<<""/utf8>> ->
{error, nil};
<<"/"/utf8>> ->
{error, nil};
Path ->
[_, Db] = gleam@string:split(
Path,
<<"/"/utf8>>
),
gleam@result:then(
gleam@uri:percent_decode(Db),
fun(Db@1) ->
case erlang:element(3, Parsed) of
{some, Userinfo} ->
case gleam@string:split(
Userinfo,
<<":"/utf8>>
) of
[<<""/utf8>>, _] ->
{error, nil};
[_, <<""/utf8>>] ->
{error, nil};
[Username, Password] ->
case begin
_pipe = [Username,
Password],
gleam@list:map(
_pipe,
fun gleam@uri:percent_decode/1
)
end of
[{ok,
Username@1},
{ok,
Password@1}] ->
case erlang:element(
7,
Parsed
) of
{some,
Query} ->
gleam@result:then(
gleam@uri:parse_query(
Query
),
fun(
Opts
) ->
case gleam@list:key_find(
Opts,
<<"authSource"/utf8>>
) of
{ok,
Auth_source} ->
_pipe@1 = {connection_info,
Host,
Port,
Db@1,
{some,
{Username@1,
Password@1}},
{some,
Auth_source}},
{ok,
_pipe@1};
{error,
nil} ->
_pipe@2 = {connection_info,
Host,
Port,
Db@1,
{some,
{Username@1,
Password@1}},
none},
{ok,
_pipe@2}
end
end
);
none ->
_pipe@3 = {connection_info,
Host,
Port,
Db@1,
{some,
{Username@1,
Password@1}},
none},
{ok,
_pipe@3}
end;
_ ->
{error, nil}
end;
_ ->
{error, nil}
end;
none ->
_pipe@4 = {connection_info,
Host,
Port,
Db@1,
none,
none},
{ok, _pipe@4}
end
end
)
end;
none ->
{error, nil}
end;
{some, _} ->
{error, nil};
none ->
{error, nil}
end
end
).
-spec connect(binary()) -> {ok, database()} | {error, nil}.
connect(Uri) ->
gleam@result:then(
parse_connection_string(Uri),
fun(Info) ->
case Info of
{connection_info, Host, Port, Db, Auth, Auth_source} ->
gleam@result:then(
tcp:connect(Host, Port),
fun(Socket) ->
case Auth of
none ->
{ok, {database, Socket, Db}};
{some, {Username, Password}} ->
gleam@result:then(case Auth_source of
none ->
authenticate(
Socket,
Username,
Password,
Db
);
{some, Source} ->
authenticate(
Socket,
Username,
Password,
Source
)
end, fun(_) ->
{ok, {database, Socket, Db}}
end)
end
end
)
end
end
).