123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179(* Copyright (C) 2017--2019 Petter A. Urkedal <paurkedal@gmail.com>
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version, with the OCaml static compilation exception.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*)openPrintftypequery=Caqti_query.t=|Lofstring|Qofstring|Pofint|Sofquerylisttype('a,'b,+'m)t={id:intoption;query:Caqti_driver_info.t->query;param_type:'aCaqti_type.t;row_type:'bCaqti_type.t;row_mult:'mCaqti_mult.t;}constraint'm=[<`Zero|`One|`Many]letlast_id=ref(-1)letcreate?(oneshot=false)param_typerow_typerow_multquery=letid=ifoneshotthenNoneelse(incrlast_id;Some!last_id)in{id;query;param_type;row_type;row_mult}letparam_typerequest=request.param_typeletrow_typerequest=request.row_typeletrow_multrequest=request.row_multletquery_idrequest=request.idletqueryrequest=request.query(* Convenience *)letinvalid_arg_ffmt=ksprintfinvalid_argfmtletformat_query~envqs=letn=String.lengthqsinletrecskip_quotedj=ifj=ntheninvalid_arg_f"Caqti_request.create_p: Unmatched quote in %S"qselseifqs.[j]='\''thenifj+1<n&&qs.[j+1]='\''thenskip_quoted(j+2)elsej+1elseskip_quoted(j+1)inletrecscan_intip=ifi=nthen(i,p)else(matchqs.[i]with|'0'..'9'asch->scan_int(i+1)(p*10+Char.codech-Char.code'0')|_->(i,p))inletrecskip_end_parenj=ifj=ntheninvalid_arg_f"Unbalanced end-parenthesis in %S"qselseifqs.[j]='('thenskip_end_paren(skip_end_paren(j+1))elseifqs.[j]=')'thenj+1elseskip_end_paren(j+1)inletcheck_idrs=letl=String.lengthsinfori=0to(ifl>1&&s.[l-1]='.'thenl-2elsel-1)do(matchs.[i]with|'a'..'z'|'A'..'Z'|'0'..'9'|'_'->()|_->invalid_arg_f"Invalid character %C in identifier %S."s.[i]s)doneinletreclooppijacc=(* acc is reversed *)ifj=nthenL(String.subqsi(j-i))::accelse(matchqs.[j]with|'\''->letk=skip_quoted(j+1)inlooppikacc|'?'->ifp<0theninvalid_arg"Mixed ? and $i style parameters."elseletacc=L(String.subqsi(j-i))::accinloop(p+1)(j+1)(j+1)(Pp::acc)|'$'->ifj+1=ntheninvalid_arg"$ at end of query"elseletacc=L(String.subqsi(j-i))::accin(matchqs.[j+1]with|'$'->letacc=L"$"::accinloopp(j+2)(j+2)acc|'0'..'9'->ifp>0theninvalid_arg"Mixed ? and $i style parameters."elseletk,p'=scan_int(j+1)0inletacc=P(p'-1)::accinloop(-1)kkacc|'('->letk=skip_end_paren(j+2)inletacc=env(String.subqs(j+2)(k-j-3))::accinlooppkkacc|'.'->letacc=env"."::accinloopp(j+2)(j+2)acc|'a'..'z'->(matchString.indexqs'.'with|exceptionNot_found->invalid_arg"Unterminated '$'."|k->letidr=String.subqs(j+1)(k-j)incheck_idridr;letacc=envidr::accinloopp(k+1)(k+1)acc)|_->invalid_arg"Unescaped $ in query string.")|_->looppi(j+1)acc)in(matchloop000[]with|[]->invalid_arg"Caqti_request.create_p: Empty query string."|[frag]->frag|rev_frags->S(List.revrev_frags))letno_env__=raiseNot_foundletrecsimplify=function|L""->S[]|Sfrags->S(frags|>List.mapsimplify|>List.filter((<>)(S[])))|L_|Q_|P_asfrag->fragletcreate_p?(env=no_env)?oneshotparam_typerow_typerow_multqs=create?oneshotparam_typerow_typerow_mult(fundi->letenvk=(matchsimplify(envdik)with|exceptionNot_found->letl=String.lengthkinifl=0||k.[l-1]<>'.'theninvalid_arg_f"No expansion provided for $(%s) \
as needed by query %S."k(qsdi)elseletk'=String.subk0(l-1)in(matchsimplify(envdik')with|exceptionNot_found->invalid_arg_f"No expansion provided for $(%s) or $(%s) \
as needed by query %S."kk'(qsdi)|S[]asv->v|v->S[v;L"."])|v->v)informat_query~env(qsdi))letexec?env?oneshotptqs=create_p?env?oneshotptCaqti_type.unitCaqti_mult.zero(fun_->qs)letfind?env?oneshotptrtqs=create_p?env?oneshotptrtCaqti_mult.one(fun_->qs)letfind_opt?env?oneshotptrtqs=create_p?env?oneshotptrtCaqti_mult.zero_or_one(fun_->qs)letcollect?env?oneshotptrtqs=create_p?env?oneshotptrtCaqti_mult.zero_or_more(fun_->qs)letppppfreq=Format.fprintfppf"(%a -%s-> %a) {|%a|}"Caqti_type.ppreq.param_type(matchCaqti_mult.exposereq.row_multwith|`Zero->"!"|`One->""|`Zero_or_one->"?"|`Zero_or_more->"*")Caqti_type.ppreq.row_typeCaqti_query.pp(req.queryCaqti_driver_info.dummy)