Current section

Files

Jump to
squirrel src squirrel@internal@query.erl
Raw

src/squirrel@internal@query.erl

-module(squirrel@internal@query).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([add_types/3, from_file/1, generate_code/2]).
-export_type([untyped_query/0, typed_query/0, code_gen_state/0]).
-type untyped_query() :: {untyped_query,
binary(),
integer(),
squirrel@internal@gleam:value_identifier(),
list(binary()),
binary()}.
-type typed_query() :: {typed_query,
binary(),
integer(),
squirrel@internal@gleam:value_identifier(),
list(binary()),
binary(),
list(squirrel@internal@gleam:type()),
list(squirrel@internal@gleam:field())}.
-type code_gen_state() :: {code_gen_state,
gleam@dict:dict(binary(), gleam@set:set(binary())),
boolean()}.
-spec add_types(
untyped_query(),
list(squirrel@internal@gleam:type()),
list(squirrel@internal@gleam:field())
) -> typed_query().
add_types(Query, Params, Returns) ->
{untyped_query, File, Starting_line, Name, Comment, Content} = Query,
{typed_query, File, Starting_line, Name, Comment, Content, Params, Returns}.
-spec do_take_comment(binary(), list(binary())) -> list(binary()).
do_take_comment(Query, Lines) ->
case gleam@string:trim_left(Query) of
<<"--"/utf8, Rest/binary>> ->
case gleam@string:split_once(Rest, <<"\n"/utf8>>) of
{ok, {Line, Rest@1}} ->
do_take_comment(Rest@1, [gleam@string:trim(Line) | Lines]);
_ ->
do_take_comment(
<<""/utf8>>,
[gleam@string:trim(Rest) | Lines]
)
end;
_ ->
lists:reverse(Lines)
end.
-spec take_comment(binary()) -> list(binary()).
take_comment(Query) ->
do_take_comment(Query, []).
-spec from_file(binary()) -> {ok, untyped_query()} |
{error, squirrel@internal@error:error()}.
from_file(File) ->
Read_file = begin
_pipe = simplifile:read(File),
gleam@result:map_error(
_pipe,
fun(_capture) -> {cannot_read_file, File, _capture} end
)
end,
gleam@result:'try'(
Read_file,
fun(Content) ->
File_name = begin
_pipe@1 = filepath:base_name(File),
filepath:strip_extension(_pipe@1)
end,
Name = begin
_pipe@2 = squirrel@internal@gleam:identifier(File_name),
gleam@result:map_error(
_pipe@2,
fun(_capture@1) ->
{query_file_has_invalid_name,
File,
begin
_pipe@3 = squirrel@internal@gleam:similar_identifier_string(
File_name
),
gleam@option:from_result(_pipe@3)
end,
_capture@1}
end
)
end,
gleam@result:'try'(
Name,
fun(Name@1) ->
{ok,
{untyped_query,
File,
1,
Name@1,
take_comment(Content),
Content}}
end
)
end
).
-spec function_doc(binary(), typed_query()) -> binary().
function_doc(Version, Query) ->
{typed_query, File, _, Name, Comment, _, _, _} = Query,
Function_name = squirrel@internal@gleam:identifier_to_string(Name),
Base = case Comment of
[] ->
<<<<<<<<"/// Runs the `"/utf8, Function_name/binary>>/binary,
"` query
/// defined in `"/utf8>>/binary,
File/binary>>/binary,
"`."/utf8>>;
[_ | _] ->
_pipe = gleam@list:map(
Comment,
fun(_capture) ->
gleam@string:append(<<"/// "/utf8>>, _capture)
end
),
gleam@string:join(_pipe, <<"\n"/utf8>>)
end,
<<<<<<Base/binary,
"
///
/// > 🐿️ This function was generated automatically using "/utf8>>/binary,
Version/binary>>/binary,
" of
/// > the [squirrel package](https://github.com/giacomocavalieri/squirrel).
///"/utf8>>.
-spec string_doc(binary()) -> glam@doc:document().
string_doc(Content) ->
Escaped_string = begin
_pipe = Content,
_pipe@1 = gleam@string:replace(_pipe, <<"\\"/utf8>>, <<"\\\\"/utf8>>),
_pipe@2 = gleam@string:replace(_pipe@1, <<"\""/utf8>>, <<"\\\""/utf8>>),
glam@doc:from_string(_pipe@2)
end,
_pipe@3 = [glam@doc:from_string(<<"\""/utf8>>),
Escaped_string,
glam@doc:from_string(<<"\""/utf8>>)],
glam@doc:concat(_pipe@3).
-spec import_module(code_gen_state(), binary()) -> code_gen_state().
import_module(State, Name) ->
Imports = case gleam@dict:has_key(erlang:element(2, State), Name) of
false ->
gleam@dict:insert(erlang:element(2, State), Name, gleam@set:new());
true ->
erlang:element(2, State)
end,
erlang:setelement(2, State, Imports).
-spec default_codegen_state() -> code_gen_state().
default_codegen_state() ->
_pipe = {code_gen_state, gleam@dict:new(), false},
_pipe@1 = import_module(_pipe, <<"decode"/utf8>>),
import_module(_pipe@1, <<"gleam/pgo"/utf8>>).
-spec import_qualified(code_gen_state(), binary(), binary()) -> code_gen_state().
import_qualified(State, Module, Imported) ->
Imports = gleam@dict:upsert(
erlang:element(2, State),
Module,
fun(Imported_values) -> case Imported_values of
{some, Imported_values@1} ->
gleam@set:insert(Imported_values@1, Imported);
none ->
gleam@set:from_list([Imported])
end end
),
erlang:setelement(2, State, Imports).
-spec imports_doc(gleam@dict:dict(binary(), gleam@set:set(binary()))) -> glam@doc:document().
imports_doc(Imports) ->
Sorted_imports = begin
_pipe = maps:to_list(Imports),
gleam@list:sort(
_pipe,
fun(One, Other) ->
gleam@string:compare(
erlang:element(1, One),
erlang:element(1, Other)
)
end
)
end,
_pipe@9 = (gleam@list:map(
Sorted_imports,
fun(_use0) ->
{Module, Imported_values} = _use0,
Import_line = glam@doc:from_string(
<<"import "/utf8, Module/binary>>
),
gleam@bool:guard(
gleam@set:is_empty(Imported_values),
Import_line,
fun() ->
Imported_values@1 = begin
_pipe@1 = gleam@set:to_list(Imported_values),
_pipe@2 = gleam@list:sort(
_pipe@1,
fun gleam@string:compare/2
),
_pipe@3 = gleam@list:map(
_pipe@2,
fun glam@doc:from_string/1
),
_pipe@4 = glam@doc:join(
_pipe@3,
glam@doc:break(<<", "/utf8>>, <<","/utf8>>)
),
glam@doc:group(_pipe@4)
end,
_pipe@8 = [Import_line,
glam@doc:from_string(<<".{"/utf8>>),
begin
_pipe@5 = [{break, <<""/utf8>>, <<""/utf8>>},
Imported_values@1],
_pipe@6 = glam@doc:concat(_pipe@5),
_pipe@7 = glam@doc:group(_pipe@6),
glam@doc:nest(_pipe@7, 2)
end,
{break, <<""/utf8>>, <<""/utf8>>},
glam@doc:from_string(<<"}"/utf8>>)],
glam@doc:concat(_pipe@8)
end
)
end
)),
glam@doc:join(_pipe@9, {line, 1}).
-spec block(list(glam@doc:document())) -> glam@doc:document().
block(Body) ->
_pipe@2 = [glam@doc:from_string(<<"{"/utf8>>),
begin
_pipe = [{line, 1} | Body],
_pipe@1 = glam@doc:concat(_pipe),
glam@doc:nest(_pipe@1, 2)
end,
{line, 1},
glam@doc:from_string(<<"}"/utf8>>)],
_pipe@3 = glam@doc:concat(_pipe@2),
glam@doc:force_break(_pipe@3).
-spec call_block(binary(), list(glam@doc:document())) -> glam@doc:document().
call_block(Function, Body) ->
_pipe = [glam@doc:from_string(<<Function/binary, "("/utf8>>),
block(Body),
glam@doc:from_string(<<")"/utf8>>)],
_pipe@1 = glam@doc:concat(_pipe),
glam@doc:group(_pipe@1).
-spec var_doc(binary(), glam@doc:document()) -> glam@doc:document().
var_doc(Name, Body) ->
_pipe@3 = [glam@doc:from_string(
<<<<"let "/utf8, Name/binary>>/binary, " ="/utf8>>
),
begin
_pipe = [{break, <<" "/utf8>>, <<""/utf8>>}, Body],
_pipe@1 = glam@doc:concat(_pipe),
_pipe@2 = glam@doc:group(_pipe@1),
glam@doc:nest(_pipe@2, 2)
end],
glam@doc:concat(_pipe@3).
-spec comma_list(binary(), list(glam@doc:document()), binary()) -> glam@doc:document().
comma_list(Open, Content, Close) ->
_pipe@3 = [glam@doc:from_string(Open),
begin
_pipe = [{break, <<""/utf8>>, <<""/utf8>>},
glam@doc:join(
Content,
glam@doc:break(<<", "/utf8>>, <<","/utf8>>)
)],
_pipe@1 = glam@doc:concat(_pipe),
_pipe@2 = glam@doc:group(_pipe@1),
glam@doc:nest(_pipe@2, 2)
end,
glam@doc:break(<<""/utf8>>, <<","/utf8>>),
glam@doc:from_string(Close)],
_pipe@4 = glam@doc:concat(_pipe@3),
glam@doc:group(_pipe@4).
-spec call_doc(binary(), list(glam@doc:document())) -> glam@doc:document().
call_doc(Function, Args) ->
_pipe = [glam@doc:from_string(Function),
comma_list(<<"("/utf8>>, Args, <<")"/utf8>>)],
_pipe@1 = glam@doc:concat(_pipe),
glam@doc:group(_pipe@1).
-spec gleam_type_to_decoder(code_gen_state(), squirrel@internal@gleam:type()) -> {code_gen_state(),
glam@doc:document()}.
gleam_type_to_decoder(State, Type_) ->
case Type_ of
uuid ->
State@1 = begin
_pipe = erlang:setelement(3, State, true),
import_module(_pipe, <<"youid/uuid"/utf8>>)
end,
{State@1, glam@doc:from_string(<<"uuid_decoder()"/utf8>>)};
{list, Type_@1} ->
{State@2, Inner_decoder} = gleam_type_to_decoder(State, Type_@1),
{State@2, call_doc(<<"decode.list"/utf8>>, [Inner_decoder])};
{option, Type_@2} ->
{State@3, Inner_decoder@1} = gleam_type_to_decoder(State, Type_@2),
{State@3, call_doc(<<"decode.optional"/utf8>>, [Inner_decoder@1])};
int ->
{State, glam@doc:from_string(<<"decode.int"/utf8>>)};
float ->
{State, glam@doc:from_string(<<"decode.float"/utf8>>)};
bool ->
{State, glam@doc:from_string(<<"decode.bool"/utf8>>)};
string ->
{State, glam@doc:from_string(<<"decode.string"/utf8>>)};
bit_array ->
{State, glam@doc:from_string(<<"decode.bit_array"/utf8>>)};
json ->
{State, glam@doc:from_string(<<"decode.string"/utf8>>)}
end.
-spec gleam_type_to_field_type(code_gen_state(), squirrel@internal@gleam:type()) -> {code_gen_state(),
glam@doc:document()}.
gleam_type_to_field_type(State, Type_) ->
case Type_ of
{list, Type_@1} ->
{State@1, Inner_type} = gleam_type_to_field_type(State, Type_@1),
{State@1, call_doc(<<"List"/utf8>>, [Inner_type])};
{option, Type_@2} ->
State@2 = begin
_pipe = State,
import_qualified(
_pipe,
<<"gleam/option"/utf8>>,
<<"type Option"/utf8>>
)
end,
{State@3, Inner_type@1} = gleam_type_to_field_type(State@2, Type_@2),
{State@3, call_doc(<<"Option"/utf8>>, [Inner_type@1])};
uuid ->
{begin
_pipe@1 = State,
import_qualified(
_pipe@1,
<<"youid/uuid"/utf8>>,
<<"type Uuid"/utf8>>
)
end,
glam@doc:from_string(<<"Uuid"/utf8>>)};
int ->
{State, glam@doc:from_string(<<"Int"/utf8>>)};
float ->
{State, glam@doc:from_string(<<"Float"/utf8>>)};
bool ->
{State, glam@doc:from_string(<<"Bool"/utf8>>)};
string ->
{State, glam@doc:from_string(<<"String"/utf8>>)};
json ->
{State, glam@doc:from_string(<<"String"/utf8>>)};
bit_array ->
{State, glam@doc:from_string(<<"BitArray"/utf8>>)}
end.
-spec record_doc(code_gen_state(), binary(), binary(), typed_query()) -> {ok,
{code_gen_state(), glam@doc:document()}} |
{error, nil}.
record_doc(State, Version, Type_name, Query) ->
{typed_query, File, _, Name, _, _, _, Returns} = Query,
gleam@bool:guard(
Returns =:= [],
{error, nil},
fun() ->
Function_name = squirrel@internal@gleam:identifier_to_string(Name),
Record_doc = <<<<<<<<<<<<"/// A row you get from running the `"/utf8,
Function_name/binary>>/binary,
"` query
/// defined in `"/utf8>>/binary,
File/binary>>/binary,
"`.
///
/// > 🐿️ This type definition was generated automatically using "/utf8>>/binary,
Version/binary>>/binary,
" of the
/// > [squirrel package](https://github.com/giacomocavalieri/squirrel).
///"/utf8>>,
{State@3, Fields@1} = (gleam@list:fold(
Returns,
{State, []},
fun(_use0, Field) ->
{State@1, Fields} = _use0,
Label = glam@doc:from_string(
<<(squirrel@internal@gleam:identifier_to_string(
erlang:element(2, Field)
))/binary,
": "/utf8>>
),
{State@2, Field_type} = gleam_type_to_field_type(
State@1,
erlang:element(3, Field)
),
Field@1 = begin
_pipe = [Label, Field_type],
_pipe@1 = glam@doc:concat(_pipe),
glam@doc:group(_pipe@1)
end,
{State@2, [Field@1 | Fields]}
end
)),
Fields@2 = lists:reverse(Fields@1),
Result = begin
_pipe@6 = [glam@doc:from_string(Record_doc),
{line, 1},
begin
_pipe@4 = [glam@doc:from_string(
<<<<"pub type "/utf8, Type_name/binary>>/binary,
" {"/utf8>>
),
begin
_pipe@2 = [{line, 1},
call_doc(Type_name, Fields@2)],
_pipe@3 = glam@doc:concat(_pipe@2),
glam@doc:nest(_pipe@3, 2)
end,
{line, 1},
glam@doc:from_string(<<"}"/utf8>>)],
_pipe@5 = glam@doc:concat(_pipe@4),
glam@doc:group(_pipe@5)
end],
glam@doc:concat(_pipe@6)
end,
{ok, {State@3, Result}}
end
).
-spec pipe_call_doc(binary(), glam@doc:document(), list(glam@doc:document())) -> glam@doc:document().
pipe_call_doc(Function, First, Rest) ->
_pipe = [First, {line, 1}, call_doc(<<"|> "/utf8, Function/binary>>, Rest)],
glam@doc:concat(_pipe).
-spec fun_doc(binary(), list(binary()), list(glam@doc:document())) -> glam@doc:document().
fun_doc(Name, Args, Body) ->
Args@1 = gleam@list:map(Args, fun glam@doc:from_string/1),
_pipe@1 = [glam@doc:from_string(<<"pub fn "/utf8, Name/binary>>),
comma_list(<<"("/utf8>>, Args@1, <<") "/utf8>>),
block(
[begin
_pipe = Body,
glam@doc:join(_pipe, glam@doc:lines(2))
end]
),
{line, 1}],
_pipe@2 = glam@doc:concat(_pipe@1),
glam@doc:group(_pipe@2).
-spec fn_doc(list(binary()), glam@doc:document()) -> glam@doc:document().
fn_doc(Args, Body) ->
_pipe@2 = [glam@doc:from_string(<<"fn"/utf8>>),
comma_list(
<<"("/utf8>>,
gleam@list:map(Args, fun glam@doc:from_string/1),
<<") {"/utf8>>
),
begin
_pipe = [{break, <<" "/utf8>>, <<""/utf8>>}, Body],
_pipe@1 = glam@doc:concat(_pipe),
glam@doc:nest(_pipe@1, 2)
end,
{break, <<" "/utf8>>, <<""/utf8>>},
glam@doc:from_string(<<"}"/utf8>>)],
_pipe@3 = glam@doc:concat(_pipe@2),
glam@doc:group(_pipe@3).
-spec gleam_type_to_encoder(
code_gen_state(),
squirrel@internal@gleam:type(),
binary()
) -> {code_gen_state(), glam@doc:document()}.
gleam_type_to_encoder(State, Type_, Name) ->
Name@1 = glam@doc:from_string(Name),
case Type_ of
{list, Type_@1} ->
State@1 = begin
_pipe = State,
import_module(_pipe, <<"gleam/list"/utf8>>)
end,
{State@2, Inner_encoder} = gleam_type_to_encoder(
State@1,
Type_@1,
<<"value"/utf8>>
),
Map_fn = fn_doc([<<"value"/utf8>>], Inner_encoder),
Doc = call_doc(
<<"pgo.array"/utf8>>,
[call_doc(<<"list.map"/utf8>>, [Name@1, Map_fn])]
),
{State@2, Doc};
{option, Type_@2} ->
{State@3, Inner_encoder@1} = gleam_type_to_encoder(
State,
Type_@2,
<<"value"/utf8>>
),
Doc@1 = call_doc(
<<"pgo.nullable"/utf8>>,
[fn_doc([<<"value"/utf8>>], Inner_encoder@1), Name@1]
),
{State@3, Doc@1};
uuid ->
State@4 = begin
_pipe@1 = State,
import_module(_pipe@1, <<"youid/uuid"/utf8>>)
end,
Doc@2 = call_doc(
<<"pgo.text"/utf8>>,
[call_doc(<<"uuid.to_string"/utf8>>, [Name@1])]
),
{State@4, Doc@2};
json ->
State@5 = begin
_pipe@2 = State,
import_module(_pipe@2, <<"gleam/json"/utf8>>)
end,
Doc@3 = call_doc(
<<"pgo.text"/utf8>>,
[call_doc(<<"json.to_string"/utf8>>, [Name@1])]
),
{State@5, Doc@3};
int ->
{State, call_doc(<<"pgo.int"/utf8>>, [Name@1])};
float ->
{State, call_doc(<<"pgo.float"/utf8>>, [Name@1])};
bool ->
{State, call_doc(<<"pgo.bool"/utf8>>, [Name@1])};
string ->
{State, call_doc(<<"pgo.text"/utf8>>, [Name@1])};
bit_array ->
{State, call_doc(<<"pgo.bytea"/utf8>>, [Name@1])}
end.
-spec list_doc(list(glam@doc:document())) -> glam@doc:document().
list_doc(Elems) ->
comma_list(<<"["/utf8>>, Elems, <<"]"/utf8>>).
-spec decoder_doc(
code_gen_state(),
binary(),
list(squirrel@internal@gleam:field())
) -> {code_gen_state(), glam@doc:document()}.
decoder_doc(State, Constructor, Returns) ->
Fallback = {State,
glam@doc:from_string(
<<"decode.map(decode.dynamic, fn(_) { Nil })"/utf8>>
)},
gleam@bool:guard(
Returns =:= [],
Fallback,
fun() ->
{State@3, Parameters@2, Labelled_names@2, Pipes@2} = begin
Acc = {State, [], [], []},
gleam@list:index_fold(
Returns,
Acc,
fun(Acc@1, Field, I) ->
{State@1, Parameters, Labelled_names, Pipes} = Acc@1,
Label = squirrel@internal@gleam:identifier_to_string(
erlang:element(2, Field)
),
Param = glam@doc:from_string(
<<<<"use "/utf8, Label/binary>>/binary,
" <- decode.parameter"/utf8>>
),
Parameters@1 = [Param | Parameters],
Labelled_name = glam@doc:from_string(
<<<<Label/binary, ": "/utf8>>/binary, Label/binary>>
),
Labelled_names@1 = [Labelled_name | Labelled_names],
Position = begin
_pipe = gleam@int:to_string(I),
glam@doc:from_string(_pipe)
end,
{State@2, Decoder} = gleam_type_to_decoder(
State@1,
erlang:element(3, Field)
),
Pipe = call_doc(
<<"|> decode.field"/utf8>>,
[Position, Decoder]
),
Pipes@1 = [Pipe | Pipes],
{State@2, Parameters@1, Labelled_names@1, Pipes@1}
end
)
end,
Parameters@3 = lists:reverse(Parameters@2),
Pipes@3 = lists:reverse(Pipes@2),
Labelled_names@3 = lists:reverse(Labelled_names@2),
Doc = begin
_pipe@1 = [call_block(
<<"decode.into"/utf8>>,
[glam@doc:join(Parameters@3, {line, 1}),
{line, 1},
call_doc(Constructor, Labelled_names@3)]
),
{line, 1},
glam@doc:join(Pipes@3, {line, 1})],
_pipe@2 = glam@doc:concat(_pipe@1),
glam@doc:group(_pipe@2)
end,
{State@3, Doc}
end
).
-spec query_doc(code_gen_state(), binary(), typed_query()) -> {code_gen_state(),
glam@doc:document()}.
query_doc(State, Version, Query) ->
{typed_query, _, _, Name, _, Content, Params, Returns} = Query,
Constructor_name = <<(squirrel@internal@gleam:identifier_to_type_name(Name))/binary,
"Row"/utf8>>,
Record_result = record_doc(State, Version, Constructor_name, Query),
{State@2, Record@1} = case Record_result of
{ok, {State@1, Record}} ->
{State@1, glam@doc:append(Record, glam@doc:lines(2))};
{error, _} ->
{State, {concat, []}}
end,
{State@5, Inputs@1, Encoders@1} = begin
Acc = {State@2, [], []},
gleam@list:index_fold(
Params,
Acc,
fun(Acc@1, Param, I) ->
{State@3, Inputs, Encoders} = Acc@1,
Input = <<"arg_"/utf8, (gleam@int:to_string(I + 1))/binary>>,
{State@4, Encoder} = gleam_type_to_encoder(
State@3,
Param,
Input
),
{State@4, [Input | Inputs], [Encoder | Encoders]}
end
)
end,
Inputs@2 = lists:reverse(Inputs@1),
Encoders@2 = lists:reverse(Encoders@1),
{State@6, Decoder} = decoder_doc(State@5, Constructor_name, Returns),
Code = begin
_pipe = [Record@1,
glam@doc:from_string(function_doc(Version, Query)),
{line, 1},
fun_doc(
squirrel@internal@gleam:identifier_to_string(Name),
[<<"db"/utf8>> | Inputs@2],
[var_doc(<<"decoder"/utf8>>, Decoder),
pipe_call_doc(
<<"pgo.execute"/utf8>>,
string_doc(Content),
[glam@doc:from_string(<<"db"/utf8>>),
list_doc(Encoders@2),
glam@doc:from_string(
<<"decode.from(decoder, _)"/utf8>>
)]
)]
)],
glam@doc:concat(_pipe)
end,
{State@6, Code}.
-spec generate_code(list(typed_query()), binary()) -> binary().
generate_code(Queries, Version) ->
{State@3, Docs@1} = begin
State = default_codegen_state(),
gleam@list:fold(
Queries,
{State, []},
fun(_use0, Query) ->
{State@1, Docs} = _use0,
{State@2, Doc} = query_doc(State@1, Version, Query),
{State@2, [Doc | Docs]}
end
)
end,
Docs@2 = lists:reverse(Docs@1),
{code_gen_state, Imports, Needs_uuid_decoder} = State@3,
Utils = case Needs_uuid_decoder of
true ->
[glam@doc:from_string(
<<"/// A decoder to decode `Uuid`s coming from a Postgres query.
///
fn uuid_decoder() {
decode.then(decode.bit_array, fn(uuid) {
case uuid.from_bit_array(uuid) {
Ok(uuid) -> decode.into(uuid)
Error(_) -> decode.fail(\"uuid\")
}
})
}"/utf8>>
)];
false ->
[]
end,
_pipe@1 = case Utils of
[] ->
[imports_doc(Imports) | Docs@2];
[_ | _] ->
Utils_comment = gleam@string:pad_right(
<<"// --- UTILS "/utf8>>,
80,
<<"-"/utf8>>
),
_pipe = [imports_doc(Imports) | Docs@2],
lists:append(_pipe, [glam@doc:from_string(Utils_comment) | Utils])
end,
_pipe@2 = glam@doc:join(_pipe@1, glam@doc:lines(2)),
glam@doc:to_string(_pipe@2, 80).