123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232(*-------------------------------------------------------------------------
* Copyright (c) 2021 Bikal Gurung. All rights reserved.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
*-------------------------------------------------------------------------*)openPpxlibmoduleAst_builder=Ast_builder.Defaultlet(let*)rf=Result.bindrflet(>>=)=(let*)letfindifl=letrecloopn=function|[]->None|x::t->iffxthenSomenelseloop(n+1)tinloop0lletsplit_onfl=matchfindiflwith|Somen->(List.filteri(funi_->i<n)l,List.filteri(funi_->i>n)l)|None->(l,[])letcapitalizeds=Char.(uppercase_asciis.[0]|>equals.[0])letvalidate_path_tokenstokens=letvalidate_starttokens=matchList.hdtokenswith|""->Ok(List.tltokens)|_|(exception_)->Error"Uri path specification must start with '/'"inletvalidate_end_slashpath=let_,l2=split_on(funx->String.equal""x)pathinifList.lengthl2>0thenError"Invalid uri path specification. No tokens allowed after trailing '/' \
token"elseOkpathinletvalidate_restpath=let_,l2=split_on(funx->String.equal"**"x)pathinifList.lengthl2>0thenError"Invalid uri path specification. No tokens allowed after full rest \
(**) token"elseOkpathinvalidate_starttokens>>=validate_end_slash>>=validate_restletpath_tokensuri=Uri.pathuri|>String.split_on_char'/'|>validate_path_tokensletquery_tokensuri=letexceptionEofstringintryUri.queryuri|>List.map(fun(k,v)->ifList.lengthv!=1thenraise(E(Printf.sprintf"Invalid query specification for key: %s"k))else(k,List.hdv))|>Result.okwithEmsg->Errormsgletrequest_target_tokenstarget=lettarget=String.trimtargetinifString.lengthtarget>0thenleturi=Uri.of_stringtargetinlet*path_components=path_tokensuriinlet*query_components=query_tokensuriinOk(path_components,query_components)elseError"Empty uri path specification"letmake_methods:loc:location->string->expression=fun~locmethods_str->String.split_on_char','methods_str|>List.filter_map(funs->lets=String.trimsinifString.lengths>0thenSomeselseNone)|>List.rev|>List.fold_left(funexprmethod'->letmethod'=Ast_builder.estring~locmethod'in[%exprWtr.method'[%emethod']::[%eexpr]])[%expr[]]letrecmake_query~locquery_tokens=matchquery_tokenswith|[]->[%exprWtr.Private.nil]|(name,"*")::query_tokens->[%exprWtr.Private.(query_arg[%eAst_builder.estring~locname]string[%emake_query~locquery_tokens])]|(name,query_token)::query_tokenswhenChar.equalquery_token.[0]':'->(letquery_token=String.subquery_token1(String.lengthquery_token-1)inletname_expr=Ast_builder.estring~locnameinmatchquery_tokenwith|"int"->[%exprWtr.Private.(query_arg[%ename_expr]int[%emake_query~locquery_tokens])]|"int32"->[%exprWtr.Private.(query_arg[%ename_expr]int32[%emake_query~locquery_tokens])]|"int64"->[%exprWtr.Private.(query_arg[%ename_expr]int64[%emake_query~locquery_tokens])]|"float"->[%exprWtr.Private.(query_arg[%ename_expr]float[%emake_query~locquery_tokens])]|"string"->[%exprWtr.Private.(query_arg[%ename_expr]string[%emake_query~locquery_tokens])]|"bool"->[%exprWtr.Private.(query_arg[%ename_expr]bool[%emake_query~locquery_tokens])]|custom_argwhencapitalizedcustom_arg->letlongident_loc={txt=Longident.parse(custom_arg^".t");loc}in[%exprWtr.Private.query_arg[%ename_expr][%eAst_builder.pexp_ident~loclongident_loc][%emake_query~locquery_tokens]]|x->Location.raise_errorf~loc"wtr: Invalid query component '%s'"x)|(name,query_token)::query_tokens->[%exprWtr.Private.query_exact[%eAst_builder.estring~locname][%eAst_builder.estring~locquery_token][%emake_query~locquery_tokens]]letrecmake_request_target~locquery_tokenspath_tokens=matchpath_tokenswith|[]->make_query~locquery_tokens|[""]->[%exprWtr.Private.slash]|["**"]->[%exprWtr.Private.rest]|"*"::path_tokens->[%exprWtr.Private.(argstring[%emake_request_target~locquery_tokenspath_tokens])]|path_token::path_tokenswhenChar.equalpath_token.[0]':'->(letpath_token=String.subpath_token1(String.lengthpath_token-1)inmatchpath_tokenwith|"int"->[%exprWtr.Private.(argint[%emake_request_target~locquery_tokenspath_tokens])]|"int32"->[%exprWtr.Private.(argint32[%emake_request_target~locquery_tokenspath_tokens])]|"int64"->[%exprWtr.Private.(argint64[%emake_request_target~locquery_tokenspath_tokens])]|"float"->[%exprWtr.Private.(argfloat[%emake_request_target~locquery_tokenspath_tokens])]|"string"->[%exprWtr.Private.(argstring[%emake_request_target~locquery_tokenspath_tokens])]|"bool"->[%exprWtr.Private.(argbool[%emake_request_target~locquery_tokenspath_tokens])]|custom_argwhencapitalizedcustom_arg->letlongident_loc={txt=Longident.parse(custom_arg^".t");loc}in[%exprWtr.Private.arg[%eAst_builder.pexp_ident~loclongident_loc][%emake_request_target~locquery_tokenspath_tokens]]|x->Location.raise_errorf~loc"wtr: Invalid path component '%s'."x)|path_token::path_tokens->[%exprWtr.Private.exact[%eAst_builder.estring~locpath_token][%emake_request_target~locquery_tokenspath_tokens]]letmake_routes~loc~path:_wtr=letwtr=String.trimwtrinletmethods,uri=lettokens=String.split_on_char';'wtr|>List.mapString.trim|>List.filter(funs->not(String.equal""s))inletlen=List.lengthtokensiniflen>2thenLocation.raise_errorf~loc"Invalid wtr: %s. Valid wtr is: [HTTP methods separated by comma (,)] \
; [URI]"wtrelseiflen=2then(List.nthtokens0,List.nthtokens1)(* Default method is `GET *)else("get",List.nthtokens0)inmatchrequest_target_tokensuriwith|Ok(path_tokens,query_tokens)->letmethods'=make_methods~locmethodsinleturi=make_request_target~locquery_tokenspath_tokensin[%exprWtr.routes[%emethods'][%euri]]|Errormsg->Location.raise_errorf~loc"wtr: %s"msgletroutes_ppx_name="routes"letroutes_ppx=Extension.declareroutes_ppx_nameExtension.Context.ExpressionAst_pattern.(single_expr_payload(estring__))make_routeslet()=Driver.register_transformationroutes_ppx_name~extensions:[routes_ppx]