123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212(*
* Copyright (c) 2013-2017 Thomas Gazagnaire <thomas@gazagnaire.org>
*
* 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.
*)open!ImportopenPrintfopenAstringletsrc=Logs.Src.create"irmin.dot"~doc:"Irmin dot graph output"moduleLog=(valLogs.src_logsrc:Logs.LOG)moduletypeS=sigtypedbvaloutput_buffer:db->?html:bool->?depth:int->?full:bool->date:(int64->string)->Buffer.t->unitLwt.tendexceptionUtf8_failureletis_valid_utf8str=tryUutf.String.fold_utf_8(fun__->function`Malformed_->raiseUtf8_failure|_->())()str;truewithUtf8_failure->falsemoduleMake(S:Store.S)=structtypedb=S.tmoduleBranch=S.Private.BranchmoduleContents=S.Private.ContentsmoduleNode=S.Private.NodemoduleCommit=S.Private.CommitmoduleSlice=S.Private.SlicemoduleGraph=Object_graph.Make(S.Hash)(Branch.Key)letfprintf(t:db)?depth?(html=false)?full~datename=Log.debug(funf->f"fprintf depth=%s html=%b full=%s"(matchdepthwithNone->"<none>"|Somed->string_of_intd)html(matchfullwithNone->"<none>"|Someb->string_of_boolb));let*slice=S.Repo.export?full?depth(S.repot)inletvertex=Hashtbl.create102inletadd_vertexvl=Hashtbl.addvertexvlinletmem_vertexv=Hashtbl.memvertexvinletedges=ref[]inletadd_edgev1lv2=ifmem_vertexv1&&mem_vertexv2thenedges:=(v1,l,v2)::!edgesinletstring_of_keytk=lets=Type.to_stringtkinifString.lengths<=8thenselseString.with_ranges~len:8inletstring_of_contentss=lets=ifString.lengths<=10thenselseString.with_ranges~len:10inlets=ifis_valid_utf8sthenselse"<blob>"insinletlabel_of_nodek_=lets=(ifhtmlthensprintf"<div class='node'><div class='sha1'>%s</div></div>"elsefunx->x)(string_of_keyNode.Key.tk)in`Labelsinletlabel_of_stepl=letl=Type.to_stringS.Key.step_tlinlets=(ifhtmlthensprintf"<div class='path'>%s</div>"elsefunx->x)(string_of_contentsl)in`Labelsinletlabel_of_commitkc=letk=string_of_keyCommit.Key.tkinleto=Commit.Val.infocinlets=ifhtmlthensprintf"<div class='commit'>\n\
\ <div class='sha1'>%s</div>\n\
\ <div class='author'>%s</div>\n\
\ <div class='date'>%s</div>\n\
\ <div class='message'><pre>%s</pre></div>\n\
\ <div> </div>\n\
</div>"k(Info.authoro)(date(Info.dateo))(String.Ascii.escape(Info.messageo))elsesprintf"%s"kin`Labelsinletlabel_of_contentskv=letk=string_of_keyContents.Key.tkinlets=ifhtmlthensprintf"<div class='contents'>\n\
\ <div class='sha1'>%s</div>\n\
\ <div> </div>\n\
</div>"kelseletv=string_of_contents(Type.to_stringContents.Val.tv)insprintf"%s (%s)"k(String.Ascii.escape_stringv)in`Labelsinletlabel_of_tagt=lets=ifhtmlthensprintf"<div class='tag'>%s</div>"(Type.to_stringBranch.Key.tt)elseType.to_stringBranch.Key.ttin`Labelsinletcontents=ref[]inletnodes=ref[]inletcommits=ref[]inlet*()=Slice.iterslice(function|`Contentsc->contents:=c::!contents;Lwt.return_unit|`Noden->nodes:=n::!nodes;Lwt.return_unit|`Commitc->commits:=c::!commits;Lwt.return_unit)inList.iter(fun(k,c)->add_vertex(`Contentsk)[`Shape`Box;label_of_contentskc])!contents;List.iter(fun(k,t)->add_vertex(`Nodek)[`Shape`Box;`Style`Dotted;label_of_nodekt])!nodes;List.iter(fun(k,r)->add_vertex(`Commitk)[`Shape`Box;`Style`Bold;label_of_commitkr])!commits;List.iter(fun(k,t)->List.iter(fun(l,v)->matchvwith|`Contents(v,_meta)->add_edge(`Nodek)[`Style`Dotted;label_of_stepl](`Contentsv)|`Noden->add_edge(`Nodek)[`Style`Solid;label_of_stepl](`Noden))(Node.Val.listt))!nodes;List.iter(fun(k,r)->List.iter(func->add_edge(`Commitk)[`Style`Bold](`Commitc))(Commit.Val.parentsr);add_edge(`Commitk)[`Style`Dashed](`Node(Commit.Val.noder)))!commits;letbranch_t=S.Private.Repo.branch_t(S.repot)inlet*bs=Branch.listbranch_tinlet+()=Lwt_list.iter_s(funr->Branch.findbranch_tr>|=function|None->()|Somek->add_vertex(`Branchr)[`Shape`Plaintext;label_of_tagr;`Style`Filled];add_edge(`Branchr)[`Style`Bold](`Commitk))bsinletvertex=Hashtbl.fold(funkvacc->(k,v)::acc)vertex[]infunppf->Graph.outputppfvertex!edgesnameletoutput_buffert?html?depth?full~datebuf=let+fprintf=fprintft?depth?full?html~date"graph"inletppf=Format.formatter_of_bufferbufinfprintfppfend