123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536(*{{{ Copyright (c) 2015 David Sheets <sheets@alum.mit.edu>
*
* 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.
*
}}}*)openSexplib0.Sexp_conv(* From <https://tools.ietf.org/html/rfc5988> *)moduleRel=structtypet=|ExtensionofUri_sexp.t|Alternate|Appendix|Bookmark|Chapter|Contents|Copyright|Current|Described_by|Edit|Edit_media|Enclosure|First|Glossary|Help|Hub|Index|Last|Latest_version|License|Next|Next_archive|Payment|Predecessor_version|Prev|Prev_archive|Related|Replies|Section|Self|Service|Start|Stylesheet|Subsection|Successor_version|Up|Version_history|Via|Working_copy|Working_copy_of[@@derivingsexp]letextensionuri=Extensionuriletalternate=Alternateletappendix=Appendixletbookmark=Bookmarkletchapter=Chapterletcontents=Contentsletcopyright=Copyrightletcurrent=Currentletdescribed_by=Described_byletedit=Editletedit_media=Edit_medialetenclosure=Enclosureletfirst=Firstletglossary=Glossarylethelp=Helplethub=Hubletindex=Indexletlast=Lastletlatest_version=Latest_versionletlicense=Licenseletnext=Nextletnext_archive=Next_archiveletpayment=Paymentletpredecessor_version=Predecessor_versionletprev=Prevletprev_archive=Prev_archiveletrelated=Relatedletreplies=Repliesletsection=Sectionletself=Selfletservice=Serviceletstart=Startletstylesheet=Stylesheetletsubsection=Subsectionletsuccessor_version=Successor_versionletup=Upletversion_history=Version_historyletvia=Vialetworking_copy=Working_copyletworking_copy_of=Working_copy_ofendmoduleLanguage=structtypet=string[@@derivingsexp]letto_stringx=xletof_stringx=xendmoduleCharset=structtypet=string[@@derivingsexp]letto_stringx=xletof_stringx=xendmoduleExt=structtype'at={charset:Charset.t;language:Language.t;value:'a}[@@derivingsexp]letcharsett=t.charsetletlanguaget=t.languageletvaluet=t.valueletmake?(charset="")?(language="")value={charset;language;value}letmapfx={xwithvalue=fx.value}endmoduleArc=structtypet={reverse:bool;relation:Rel.tlist;hreflang:stringoption;media:stringoption;title:stringoption;title_ext:stringExt.toption;media_type:(string*string)option;extensions:(string*string)list;extension_exts:(string*stringExt.t)list;}[@@derivingsexp]letempty={reverse=false;relation=[];hreflang=None;media=None;title=None;title_ext=None;media_type=None;extensions=[];extension_exts=[];}endtypet={context:Uri_sexp.t;arc:Arc.t;target:Uri_sexp.t}[@@derivingsexp](* TODO: this could be replaced with empty t/arc fupdate *)typeparam=|RelofRel.tlist|AnchorofUri.t|RevofRel.tlist|HreflangofLanguage.t|Mediaofstring|Titleofstring|StarofparamExt.t|Typeof(string*string)|Link_extensionofstring*stringletuntilsstartcl=letnextl=List.map(func->letpattern=String.make1cinStringext.find_from~starts~pattern)clinletmin=List.fold_left(funmin_opti_opt->match(min_opt,i_opt)with|None,None->None|Somei,None|None,Somei->Somei|Somei,Somej->Some(minij))Nonenextlinmatchminwith|None->(Stringext.string_aftersstart,String.lengths)|Somei->(String.subsstart(i-start),i)letstring_of_rel=Rel.(function|Alternate->"alternate"|Appendix->"appendix"|Bookmark->"bookmark"|Chapter->"chapter"|Contents->"contents"|Copyright->"copyright"|Current->"current"|Described_by->"describedby"|Edit->"edit"|Edit_media->"edit-media"|Enclosure->"enclosure"|First->"first"|Glossary->"glossary"|Help->"help"|Hub->"hub"|Index->"index"|Last->"last"|Latest_version->"latest-version"|License->"license"|Next->"next"|Next_archive->"next-archive"|Payment->"payment"|Predecessor_version->"predecessor-version"|Prev->"prev"|Prev_archive->"prev-archive"|Related->"related"|Replies->"replies"|Section->"section"|Self->"self"|Service->"service"|Start->"start"|Stylesheet->"stylesheet"|Subsection->"subsection"|Successor_version->"successor-version"|Up->"up"|Version_history->"version-history"|Via->"via"|Working_copy->"working-copy"|Working_copy_of->"working-copy-of"|Extensionuri->Uri.to_stringuri)letrel_of_strings=Rel.(tryignore(String.indexs':');Extension(Uri.of_strings)withNot_found->(matchswith|"alternate"->Alternate|"appendix"->Appendix|"bookmark"->Bookmark|"chapter"->Chapter|"contents"->Contents|"copyright"->Copyright|"current"->Current|"describedby"->Described_by|"edit"->Edit|"edit-media"->Edit_media|"enclosure"->Enclosure|"first"->First|"glossary"->Glossary|"help"->Help|"hub"->Hub|"index"->Index|"last"->Last|"latest-version"->Latest_version|"license"->License|"next"->Next|"next-archive"->Next_archive|"payment"->Payment|"predecessor-version"->Predecessor_version|"prev"|"previous"->Prev|"prev-archive"->Prev_archive|"related"->Related|"replies"->Replies|"section"->Section|"self"->Self|"service"->Service|"start"->Start|"stylesheet"->Stylesheet|"subsection"->Subsection|"successor-version"->Successor_version|"up"->Up|"version-history"->Version_history|"via"->Via|"working-copy"->Working_copy|"working-copy-of"->Working_copy_of|_->Extension(Uri.of_strings)))letquoted_string_of_stringsq=letrecfirst_quoteq=matchs.[q]with|' '->first_quote(q+1)|'"'->(letq=q+1inmatchStringext.find_from~start:qs~pattern:"\""with|None->(Stringext.string_aftersq,String.lengths)|Someq'->(String.subsq(q'-q),q'+1))|_->untilsq[';';',']infirst_quoteqletrels_of_string_sq=letqs,i=quoted_string_of_stringsqinletrels=Stringext.splitqs~on:' 'in(List.maprel_of_string(List.filter(funs->String.lengths>0)rels),i)letrels_of_stringsi=match(Stringext.find_from~start:is~pattern:"\"",untilsi[';';','])with|Someq,(_,d)whenq<d->rels_of_string_sq|_,(s,d)->([rel_of_strings],d)letanchor_of_stringsi=letqs,i=quoted_string_of_stringsiin(Uri.of_stringqs,i)letstar_of_stringsi=matchStringext.find_from~start:is~pattern:"'"with|None->lets,i=quoted_string_of_stringsiin("","",s,i)|Somea->(letcharset=String.subsi(a-i)inleti=a+1inmatchStringext.find_from~start:is~pattern:"'"with|None->lets,i=quoted_string_of_stringsiin(charset,"",s,i)|Somea->letlanguage=String.subsi(a-i)inleti=a+1inlets,i=quoted_string_of_stringsiin(charset,language,s,i))letmedia_type_of_stringsi=letmt,i=quoted_string_of_stringsiinmatchStringext.split~max:2mt~on:'/'with|[]->(("",""),i)|[t]->((t,""),i)|t::st::_->((t,st),i)letrecparams_of_stringsips=let_,d=untilsi[';';',']inifd=String.lengthsthen(ps,None)elseifs.[d]=','then(ps,Somed)elseleti=d+1inletparam,i=untilsi['=']inleti=i+1inmatchString.trimparamwith|"rel"->letrels,i=rels_of_stringsiinparams_of_stringsi(Relrels::ps)|"anchor"->leturi,i=anchor_of_stringsiinparams_of_stringsi(Anchoruri::ps)|"rev"->letrels,i=rels_of_stringsiinparams_of_stringsi(Revrels::ps)|"hreflang"->lethreflang,i=untilsi[',';';']inparams_of_stringsi(Hreflanghreflang::ps)|"media"->letmedia,i=quoted_string_of_stringsiinparams_of_stringsi(Mediamedia::ps)|"title"->lettitle,i=quoted_string_of_stringsiinparams_of_stringsi(Titletitle::ps)|"title*"->letcharset,language,v,i=star_of_stringsiinparams_of_stringsi(Star{Ext.charset;language;value=Titlev}::ps)|"type"->letmedia_type,i=media_type_of_stringsiinparams_of_stringsi(Typemedia_type::ps)|otherwhenString.lengthother=0->lets,i=quoted_string_of_stringsiinparams_of_stringsi(Link_extension("",s)::ps)|other->letlast=String.lengthother-1inifother.[last]='*'thenletmain=String.subother0lastinletcharset,language,v,i=star_of_stringsiinparams_of_stringsi(Star{Ext.charset;language;value=Link_extension(main,v)}::ps)elseletv,i=quoted_string_of_stringsiinparams_of_stringsi(Link_extension(other,v)::ps)letrecfind_or_defaultfd=function|[]->d|h::t->(matchfhwithNone->find_or_defaultfdt|Somev->v)letarc_of_relation_params?(reverse=false)relationparams=letextensions,extension_exts=List.fold_left(fun(x,xx)->function|Link_extension(k,v)->((k,v)::x,xx)|Star{Ext.charset;language;value=Link_extension(k,value)}->(x,(k,{Ext.charset;language;value})::xx)|_->(x,xx))([],[])paramsin{Arc.reverse;relation;hreflang=find_or_default(functionHreflangl->Some(Somel)|_->None)Noneparams;media=find_or_default(functionMediam->Some(Somem)|_->None)Noneparams;title=find_or_default(functionTitlet->Some(Somet)|_->None)Noneparams;title_ext=find_or_default(function|Star{Ext.charset;language;value=Titlet}->Some(Some{Ext.charset;language;value=t})|_->None)Noneparams;media_type=find_or_default(functionTypemt->Some(Somemt)|_->None)Noneparams;extensions;extension_exts;}letempty={context=Uri.of_string"";arc=Arc.empty;target=Uri.of_string""}letrecunfoldsliststart=matchStringext.find_from~starts~pattern:"<"with|None->list|Somei->(leturi_ref,i=untils(i+1)['>']inleti=i+1inlettarget=Uri.of_stringuri_refinletparams,c_opt=params_of_stringsi[]inletparams=List.revparamsinletcontext=find_or_default(functionAnchoruri->Someuri|_->None)(Uri.of_string"")paramsinletlink=matchfind_or_default(functionRelrels->Somerels|_->None)[]paramswith|_::_asrelation->letarc=arc_of_relation_paramsrelationparamsin{context;arc;target}|[]->(matchfind_or_default(functionRevrels->Somerels|_->None)[]paramswith|[]->letarc=arc_of_relation_params[]paramsin{context;arc;target}|rev->letarc=arc_of_relation_params~reverse:truerevparamsin{context=target;arc;target=context})inletlist=link::listinmatchc_optwithNone->list|Somec->unfoldslistc)letof_strings=List.rev(unfolds[]0)openPrintfletarc_to_stringcontextarc=Arc.(letattrs=matcharc.relationwith|[]->[]|rels->[sprintf"%s=\"%s\""(ifarc.reversethen"rev"else"rel")(String.concat" "(List.mapstring_of_relrels));]inletattrs=matcharc.hreflangwith|None->attrs|Somes->("hreflang="^s)::attrsinletattrs=matcharc.mediawith|None->attrs|Somes->sprintf"media=\"%s\""s::attrsinletattrs=matcharc.titlewith|None->attrs|Somes->sprintf"title=%S"s::attrs(* TODO: this isn't quite right...*)inletattrs=matcharc.title_extwith|None->attrs|Some{Ext.charset;language;value}->sprintf"title*=%s'%s'%s"charsetlanguagevalue::attrsinletattrs=matcharc.media_typewith|None->attrs|Some(typ,sub)->sprintf"type=%s/%s"typsub::attrsinletattrs=List.map(fun(k,v)->sprintf"%s=%S"kv)arc.extensions@attrsinletattrs=List.map(fun(k,{Ext.charset;language;value})->sprintf"%s=%s'%s'%s"kcharsetlanguagevalue)arc.extension_exts@attrsinletattrs=ifcontext=Uri.of_string""thenattrselsesprintf"anchor=\"%s\""(Uri.to_stringcontext)::attrsinString.concat"; "attrs)letto_string{context;arc;target}=sprintf"<%s>; %s"(Uri.to_stringtarget)(arc_to_stringcontextarc)