123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103(*
* Copyright (c) 2021 Tarides <contact@tarides.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*)openPpxlibletrewriter_name="ppx_irmin.internal"(* Provides a PPX wrapper around the Logs library that attaches source code
postitions to log lines via Logs' tags system.
Input: [%log(s?).<level> <fmt_string> <args...>]
Output: Log(s?).(<level>) (fun f -> f <fmt_string> <args...> ~tags:(...))
(The extension node payload can also be written in the standard CPSed form,
for instance in order to perform computation before constructing the log
line.)
*)moduleSource=structtypet=|Logs(** default (source-less) logging functions *)|Log(** referencing a "Log" module, specifying a particular source *)letto_string=functionLogs->"logs"|Log->"log"endletlevel_to_function_name:Logs.level->string=function|App->"app"|Error->"err"|Warning->"warn"|Info->"info"|Debug->"debug"letlog_function~loc(source:Source.t)(level:Logs.level)=letprefix=matchsourcewithLogs->"Logs."|Log->"Log."inAst_builder.Default.evar~loc(prefix^level_to_function_namelevel)lettags~loc=[%exprLogs.Tag.addPpx_irmin_internal_lib.Source_code_position.tag__POS__Logs.Tag.empty]letexpansion_functionsourcelevel~loc~path:_payload=letlog_fn=log_function~locsourcelevelinletopenAst_builder.Defaultinmatchpayloadwith|[%exprfun[%p?_]->[%e?_]]->(* Payload is already in CPS-ed form: we just need to attach the tags. *)[%expr[%elog_fn](funf->([%epayload]:(?header:string->(_,_,_,_)format4->_)->_)(f~tags:[%etags~loc]))]|_->(* The user hasn't wrapped the payload in [fun f -> ...; f ...], so we
should attempt to do so. This requires re-interpreting top-level
[Pexp_apply] nodes in the AST, for example:
> [%log.debug "fmt_string" ...args]
This parses ["fmt_string"] as a _function_, but it's going to become
the first argument of a function [debug]. *)letinput_args=matchpayloadwith|{pexp_desc=Pexp_constant(Pconst_string_);_}->[(Nolabel,payload)](* Special case for ( @@ ), e.g. [%log.err "%d" @@ 1 + 2] *)|[%expr[%e?fmt]@@[%e?args]]->[(Nolabel,fmt);(Nolabel,args)]|{pexp_desc=Pexp_apply(fmt,args);_}->(Nolabel,fmt)::args|_->Location.raise_errorf~loc"%s: invalid payload"rewriter_nameinletargs=input_args@[(Labelled"tags",tags~loc)]in[%expr[%elog_fn](funf->[%epexp_apply~loc[%exprf]args])]let(let*)xf=List.concat_mapfxletrules=let*source=[Source.Logs;Log]inlet*level=[Logs.App;Error;Warning;Info;Debug]inletextension_name=Format.sprintf"irmin.%s.%s"(Source.to_stringsource)(level_to_function_namelevel)in[Extension.declareextension_nameExtension.Context.expressionAst_pattern.(single_expr_payload__)(expansion_functionsourcelevel)|>Context_free.Rule.extension;]let()=Driver.register_transformation~rulesrewriter_name