Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file apath.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203openAcommonopenAtypesletslash_sub=Astring.Sub.v"/"letwildcard2_sub=Astring.Sub.v"**"letremove_useless_slashess=letopenAstring.Subincuts~sep:slash_sub(vs)|>(function|[]->[]|[e;e']whenis_emptye&&is_emptye'->[empty;empty]|x::l->x::List.filter(funs->not(is_emptys))l)|>concat~sep:slash_sub|>to_stringmodulePath=structtypet=string(* from https://tools.ietf.org/html/rfc3986#appendix-B :
the path of a URI contains any char but '?' or '#'.
We add '*', '[' and ']' as forbidden char *)letis_valids=letrecis_valid_reci=ifi>=Astring.lengthsthentrueelsematchAstring.unsafe_getsiwith|'?'->false(* ? not allowed *)|'#'->false(* # not allowed *)|'['->false(* [ not allowed *)|']'->false(* ] not allowed *)|'*'->false(* * not allowed *)|_->is_valid_rec(i+1)inis_valid_rec0letof_string_opts=lets=Astring.trimsinifis_validsthenSome(remove_useless_slashess)elseNoneletof_strings=Option.get_or_else(of_string_opts)(fun()->raise(Exception(`InvalidFormat(`Msgs))))letto_strings=sletlength=Astring.lengthletcompare=Astring.compareletequal=Astring.equalletis_relativep=iflengthp=0thentrueelseAstring.unsafe_getp0<>'/'letadd_prefix~prefixp=remove_useless_slashes@@(to_stringprefix)^"/"^pletis_prefix~affixpath=Astring.is_prefix~affix:(to_stringaffix)(to_stringpath)letremove_prefixlengthpath=Astring.afterlength(to_stringpath)|>of_stringend[@@derivingshow]modulePathExpr=structtypet=string(* from https://tools.ietf.org/html/rfc3986#appendix-B except that:
- we don't allow '[' or ']' in the path part
*)letis_valids=letrecis_valid_reci=ifi>=String.lengthsthentrueelsematchString.unsafe_getsiwith|'?'->false(* ? not allowed *)|'#'->false(* # not allowed *)|'['->false(* [ not allowed *)|']'->false(* ] not allowed *)|'/'->is_valid_rec(i+1)|'*'->(ifi+2>=String.lengthsthentrueelsematch(String.unsafe_gets(i+1),String.unsafe_gets(i+2))with|('*','/')->is_valid_rec(i+1)|('*',_)->false(* **a and *** not allowed *)|_->is_valid_rec(i+1))|_->(ifi+2>=String.lengthsthentrueelsematch(String.unsafe_gets(i+1),String.unsafe_gets(i+2))with|('*','*')->false(* a** not allowed *)|_->is_valid_rec(i+1))inis_valid_rec0letof_string_opts=lets=Astring.trimsinifis_validsthenSome(remove_useless_slashess)elseNoneletof_strings=Option.get_or_else(of_string_opts)(fun()->raise(Exception(`InvalidFormat(`Msgs))))letto_stringe=eletof_pathp=of_string@@Path.to_stringpletlength=Astring.lengthletcompare=Astring.compareletequal=Astring.equalletis_relativee=iflengthe=0thentrueelseAstring.unsafe_gete0<>'/'letget_prefixe:Path.t=matchAstring.find(func->c='*')ewith|Somei->Path.of_string@@Astring.beforeie|None->Path.of_stringeletadd_prefix~prefixe=remove_useless_slashes@@(Path.to_stringprefix)^"/"^eletremove_prefixlengthe=Astring.afterlength(to_stringe)|>of_stringletis_uniquee=not@@Astring.contains'*'eletas_unique_pathe=ifis_uniqueethenSome(Path.of_stringe)elseNonetype'aelement=|Someof'a|Wildcard|Noneletget_charsi=letopenAstring.Subinifi>=lengthsthenNoneelsematchunsafe_getsiwith|'*'->Wildcard|c->Somecletget_chunkli=matchList.nth_optliwith|Somesub->ifAstring.Sub.equal_bytessubwildcard2_subthenWildcardelseSomesub|None->Noneletintersect?(allow_empty=true)l1l2getelem_intersect=ifnotallow_empty&&getl10=Nonethengetl20=Noneelseifnotallow_empty&&getl20=Nonethengetl10=Noneelseletrecintersect_fromi1i2=match(getl1i1,getl2i2)with|(None,None)->true|(Wildcard,None)->intersect_from(i1+1)i2|(None,Wildcard)->intersect_fromi1(i2+1)|(Wildcard,_)->ifintersect_from(i1+1)i2thentrueelseintersect_fromi1(i2+1)|(_,Wildcard)->ifintersect_fromi1(i2+1)thentrueelseintersect_from(i1+1)i2|(None,_)->false|(_,None)->false|(Somee1,Somee2)->ifelem_intersecte1e2thenintersect_from(i1+1)(i2+1)elsefalseinintersect_from00letincludessublgetelem_includes=letrecincludes_fromsubii=match(getli,getsubsubi)with|(None,None)->true|(Wildcard,None)->includes_fromsubi(i+1)|(Wildcard,_)->ifincludes_fromsubi(i+1)thentrueelseincludes_from(subi+1)i|(_,Wildcard)->false|(None,_)->false|(_,None)->false|(Someec,Somesubc)->ifelem_includessubcecthenincludes_from(subi+1)(i+1)elsefalseinincludes_from00letchunk_expr_intersecte1e2=intersecte1e2get_charChar.equal~allow_empty:falseletintersecte1e2=letopenAstring.Subinlete1_chunks=cuts~sep:slash_sub(ve1)inlete2_chunks=cuts~sep:slash_sub(ve2)inintersecte1_chunkse2_chunksget_chunkchunk_expr_intersectletis_matching_path=intersectletchunk_expr_includessubce=includessubceget_charChar.equalletincludes~subexpre=letopenAstring.Subinletsub_chunks=cuts~sep:slash_sub(vsubexpr)inlete_chunks=cuts~sep:slash_sub(ve)inincludessub_chunkse_chunksget_chunkchunk_expr_includesletreclongest_matching_partpathe=iflengthe=0||is_matching_pathpathetheneelsematchAstring.find~rev:true(func->c='/')ewith|None->""|Somei->longest_matching_partpath(Astring.beforeie)letremaining_after_matchpathe:toption=letprefix=longest_matching_partpatheinmatchlength@@longest_matching_partpathewith|0->None|i->letremain=Astring.afterieinSome(ifAstring.is_suffix~affix:"**"prefixthen"/**"^remainelseremain)end[@@derivingshow]