12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016let(<.>)fgx=f(gx)moduleAdvertised_refs=structtype('uid,'reference)t={shallows:'uidlist;refs:('uid*'reference*bool)list;capabilities:Capability.tlist;version:int;}letequal_shallows~uid:equal_uidl0l1=ifList.lengthl0<>List.lengthl1thenfalseelseList.for_all(funuid0->List.exists(equal_uiduid0)l1)l0letequal_advertised_refs~uid:equal_uid~reference:equal_referencel0l1=ifList.lengthl0<>List.lengthl1thenfalseelseList.for_all(fun(uid0,ref0,peeled0)->List.exists(fun(uid1,ref1,peeled1)->equal_uiduid0uid1&&equal_referenceref0ref1&&peeled0=peeled1)l1)l0letequal_capabilitiesl0l1=ifList.lengthl0<>List.lengthl1thenfalseelseList.for_all(func0->List.exists(Capability.equalc0)l1)l0letequal~uid:equal_uid~reference:equal_referenceab=equal_shallows~uid:equal_uida.shallowsb.shallows&&equal_advertised_refs~uid:equal_uid~reference:equal_referencea.refsb.refs&&equal_capabilitiesa.capabilitiesb.capabilities&&a.version=b.versionlethead{refs;_}=tryletuid,_,_=List.find(function_,"HEAD",false->true|_->false)refsinSomeuidwith_exn->Noneletreference~equal?(peeled=false)refname{refs;_}=tryletuid,_,_=List.find(fun(_,refname',peeled')->equalrefnamerefname'&&peeled=peeled')refsinSomeuidwith_exn->Noneletreferences~equal?(peeled=false)refnames{refs;_}=letfoldacc(uid,refname',peeled')=ifList.exists(equalrefname')refnames&&peeled=peeled'thenuid::accelseaccinList.fold_leftfold[]refsletrefs{refs;_}=refsletcapabilities{capabilities;_}=capabilitiesletmap~fuid~fref{shallows;refs;capabilities;version}=letshallows=List.mapfuidshallowsinletrefs=List.map(fun(uid,ref,peeled)->fuiduid,frefref,peeled)refsin{shallows;refs;capabilities;version}letppppf{shallows;refs;capabilities;version}=Fmt.pfppf"version %d@ "version;matchrefswith|[]->Fmt.pfppf"0 capabilities^{}@ ";Fmt.pfppf"%a@,"Fmt.(Dump.listCapability.pp)capabilities;List.iter(Fmt.pfppf"shallow %s@ ")shallows|head::refs->letpp_refppf(uid,refname,peeled)=ifpeeledthenFmt.pfppf"%s %s^{}"uidrefnameelseFmt.pfppf"%s %s"uidrefnameinFmt.pfppf"%a@ "pp_refhead;Fmt.pfppf"%a@ "Fmt.(Dump.listCapability.pp)capabilities;List.iter(Fmt.pfppf"%a@ "pp_ref)refs;List.iter(Fmt.pfppf"shallow %s@ ")shallowsletv1?(shallows=[])?(capabilities=[])refs={shallows;capabilities;refs;version=1}endmoduleProto_request=structtypet={path:string;host:[`AddrofIpaddr.t|`Domainof[`host]Domain_name.t]*intoption;version:int;request_command:[`Upload_pack|`Receive_pack|`Upload_archive];}letupload_pack~host?port?(version=2)path=lethost=host,portin{request_command=`Upload_pack;host;version;path}letreceive_pack~host?port?(version=1)path=lethost=host,portin{request_command=`Receive_pack;host;version;path}letppppf{path;host;request_command;version}=letpp_request_commandppf=function|`Upload_pack->Fmt.pfppf"git-upload-pack"|`Receive_pack->Fmt.pfppf"git-receive-pack"|`Upload_archive->Fmt.pfppf"git-upload-archive"inletpp_hostppf=function|`Domainhost,Someport->Fmt.pfppf"%a:%d"Domain_name.pphostport|`Domainhost,None->Fmt.pfppf"%a"Domain_name.pphost|`Addrv,Someport->Fmt.pfppf"%a:%d"Ipaddr.ppvport|`Addrv,None->Ipaddr.ppppfvinFmt.pfppf"%a %s %a %a"pp_request_commandrequest_commandpathFmt.(prefix(conststring" host=")pp_host)hostFmt.(prefix(conststring" version=")int)versionendmoduleWant=structtype('uid,'reference)t={wants:'uid*'uidlist;shallows:'uidlist;deepen:[`Depthofint|`Timestampofint64|`Notof'reference]option;filter:Filter.toption;capabilities:Capability.tlist;}letwant~capabilities?deepen?filter?(shallows=[])?(others=[])hash={wants=hash,others;shallows;deepen;filter;capabilities}endmoduleResult=structtype'uidt=NAK|ACKof'uidletppppf=function|NAK->Fmt.pfppf"NAK"|ACKcommon->Fmt.pfppf"ACK %s"commonendmoduleNegotiation=structtype'uidt=|ACKof'uid|ACK_continueof'uid|ACK_readyof'uid|ACK_commonof'uid|NAKletis_common=functionACK_common_->true|_->falseletis_ready=functionACK_ready_->true|_->falseletis_nak=functionNAK->true|_->falseletppppf=function|ACKuid->Fmt.pfppf"ACK %s"uid|ACK_continueuid->Fmt.pfppf"ACK %s continue"uid|ACK_readyuid->Fmt.pfppf"ACK %s ready"uid|ACK_commonuid->Fmt.pfppf"ACK %s common"uid|NAK->Fmt.pfppf"NAK"letmap~f=function|ACKuid->ACK(fuid)|ACK_continueuid->ACK_continue(fuid)|ACK_readyuid->ACK_ready(fuid)|ACK_commonuid->ACK_common(fuid)|NAK->NAKendmoduleCommands=structtype('uid,'ref)command=|Createof'uid*'ref|Deleteof'uid*'ref|Updateof'uid*'uid*'refletmap_command~fuid~fref=function|Create(uid,ref)->Create(fuiduid,frefref)|Delete(uid,ref)->Delete(fuiduid,frefref)|Update(a,b,ref)->Update(fuida,fuidb,frefref)type('uid,'ref)t={capabilities:Capability.tlist;commands:('uid,'ref)command*('uid,'ref)commandlist;}letcreateuidreference=Create(uid,reference)letdeleteuidreference=Delete(uid,reference)letupdateabreference=Update(a,b,reference)letv~capabilities?(others=[])command={capabilities;commands=command,others}letcommands{commands=command,others;_}=command::othersletmap~fuid~fref{commands=command,others;capabilities}=letcommand=map_command~fuid~frefcommandinletothers=List.map(map_command~fuid~fref)othersin{commands=command,others;capabilities}endmoduleShallow=structtype'uidt=Shallowof'uid|Unshallowof'uidletmap~f=function|Shallowv->Shallow(fv)|Unshallowv->Unshallow(fv)endmoduleStatus=structtype'reft={result:(unit,string)result;commands:('ref,'ref*string)resultlist;}letppppf{result;commands}=letok_with_refppfref=Fmt.pfppf"%s:ok"refinleterror_with_refppf(ref,err)=Fmt.pfppf"%s:%s"referrinFmt.pfppf"{ @[<hov>result= %a;@ commands= @[<hov>%a@];@] }"Fmt.(Dump.result~ok:(conststring"done")~error:string)resultFmt.(Dump.list(Dump.result~ok:ok_with_ref~error:error_with_ref))commandsletto_result{result;_}=resultletmap~f{result;commands}=letcommands=letfold=function|Okref->Ok(fref)|Error(ref,err)->Error(fref,err)inList.mapfoldcommandsin{result;commands}letv?(err="An error occurred")cmds=letcommands=letmap=function|Ok(Commands.Create(_,ref))|Ok(Commands.Delete(_,ref))|Ok(Commands.Update(_,_,ref))->Okref|Error((Commands.Create(_,ref)|Commands.Delete(_,ref)|Commands.Update(_,_,ref)),err)->Error(ref,err)inList.mapmapcmdsinifList.existsRresult.R.is_errorcommandsthen{result=Errorerr;commands}else{result=Ok();commands}endmoduleDecoder=structopenAstringopenPkt_line.Decodertypenonrecerror=[error|`Invalid_advertised_refofstring|`Invalid_shallowofstring|`Invalid_negotiation_resultofstring|`Invalid_side_bandofstring|`Invalid_ackofstring|`Invalid_resultofstring|`Invalid_command_resultofstring|`Unexpected_flush|`Invalid_pkt_line]letpp_errorppf=function|#Pkt_line.Decoder.erroraserr->Pkt_line.Decoder.pp_errorppferr|`Invalid_advertised_refraw->Fmt.pfppf"Invalid advertised refererence (%S)"raw|`Invalid_shallowraw->Fmt.pfppf"Invalid shallow (%S)"raw|`Invalid_negotiation_resultraw->Fmt.pfppf"Invalid negotiation result (%S)"raw|`Invalid_side_bandraw->Fmt.pfppf"Invalid side-band (%S)"raw|`Invalid_ackraw->Fmt.pfppf"Invalid ack (%S)"raw|`Invalid_resultraw->Fmt.pfppf"Invalid result (%S)"raw|`Invalid_command_resultraw->Fmt.pfppf"Invalid result command (%S)"raw|`Unexpected_flush->Fmt.stringppf"Unexpected flush"letrecprompt_pkt?strictkdecoder=ifat_least_one_pktdecoderthenkdecoderelseprompt?strict(prompt_pkt?strictk)decoderletis_new_line=function'\n'->true|_->falseletpeek_pkt?(trim=true)decoder=letbuf,off,len=peek_pktdecoderinletbuf=Bytes.to_stringbufinletres=String.Sub.vbuf~start:off~stop:(off+len)iniftrimthenString.Sub.trim~drop:is_new_linereselseresletis_zero=function'0'->true|_->falseletv_zero=String.Sub.of_string"\000"letv_space=String.Sub.of_string" "letv_peeled=String.Sub.of_string"^{}"letv_shallow=String.Sub.of_string"shallow"letv_unshallow=String.Sub.of_string"unshallow"letv_version=String.Sub.of_string"version"letv_nak=String.Sub.of_string"NAK"letv_ack=String.Sub.of_string"ACK"letv_ok=String.Sub.of_string"ok"letv_ng=String.Sub.of_string"ng"letdecode_advertised_refsdecoder=letdecode_shallowsadvertised_refsdecoder=letrecgoshallowsdecoder=letv=peek_pktdecoderinifString.Sub.is_emptyvthen(junk_pktdecoder;return{advertised_refswithAdvertised_refs.shallows}decoder)elsematchString.Sub.cut~sep:v_spacevwith|Some(_,uid)->letuid=String.Sub.to_stringuidinjunk_pktdecoder;letkdecoder=go(uid::shallows)decoderinprompt_pktkdecoder|None->faildecoder(`Invalid_shallow(String.Sub.to_stringv))ingo[]decoderin(* obj-id refname *)letdecode_others_refs~version~head~capabilitiesdecoder=letrecgorefsdecoder=letv=peek_pktdecoderinifString.Sub.is_emptyvthen(junk_pktdecoder;return{Advertised_refs.capabilities;refs=List.revrefs;version;shallows=[];}decoder)elseifString.Sub.is_prefix~affix:v_shallowvthendecode_shallows{Advertised_refs.capabilities;refs=List.revrefs;version;shallows=[];}decoderelsematchString.Sub.cut~sep:v_spacevwith|Some(uid,reference)->letuid=String.Sub.to_stringuidinletreference,peeled=matchString.Sub.cut~rev:true~sep:v_peeledreferencewith|Some(reference,_)->String.Sub.to_stringreference,true|None->String.Sub.to_stringreference,falseinletkdecoder=go((uid,reference,peeled)::refs)decoderinjunk_pktdecoder;prompt_pktkdecoder|None->faildecoder(`Invalid_advertised_ref(String.Sub.to_stringv))ingo[head]decoderin(* zero-id capabilities^{}\000capabilities *)letdecode_no_ref~versionvdecoder=let_,rest=Option.get(String.Sub.cut~sep:v_spacev)inmatchString.Sub.cut~sep:v_zerorestwith|Some(_,capabilities)->letcapabilities=String.Sub.fieldscapabilitiesinletcapabilities=List.map(Capability.of_string<.>String.Sub.to_string)capabilitiesinjunk_pktdecoder;return{Advertised_refs.capabilities;refs=[];version;shallows=[]}decoder|None->faildecoder(`Invalid_advertised_ref(String.Sub.to_stringv))in(* obj-id HEAD\000capabilities *)letdecode_first_ref~versionvdecoder=letuid,rest=Option.get(String.Sub.cut~sep:v_spacev)inmatchString.Sub.cut~sep:v_zerorestwith|Some(head,capabilities)->letuid=String.Sub.to_stringuidinletcapabilities=String.Sub.fieldscapabilitiesinletcapabilities=List.map(Capability.of_string<.>String.Sub.to_string)capabilitiesinletpeeled=String.Sub.is_suffix~affix:v_peeledheadinlethead=ifpeeledthenString.Sub.with_range~len:(String.Sub.lengthhead-3)headelseheadinlethead=String.Sub.to_stringheadinjunk_pktdecoder;letkdecoder=decode_others_refs~version~head:(uid,head,peeled)~capabilitiesdecoderinprompt_pktkdecoder|None->faildecoder(`Invalid_advertised_ref(String.Sub.to_stringv))in(* zero-id capabilities^{}\000capabilities
| obj-id HEAD\000capabilities *)letdecode_refs?(version=1)decoder=letv=peek_pktdecoderinmatchString.Sub.cut~sep:v_spacevwith|Some(uid,_)->ifString.Sub.for_allis_zerouidthendecode_no_ref~versionvdecoderelsedecode_first_ref~versionvdecoder|None->(* XXX(dinosaure): see [empty_clone]. *)junk_pktdecoder;return{Advertised_refs.shallows=[];Advertised_refs.refs=[];Advertised_refs.capabilities=[];Advertised_refs.version=1;}decoderin(* version (1|2) *)letdecode_versiondecoder=letv=peek_pktdecoderinifString.Sub.is_prefix~affix:v_versionvthenmatchString.Sub.cut~sep:v_spacevwith|Some(_,version)->letversion=int_of_string(String.Sub.to_stringversion)injunk_pktdecoder;prompt_pkt(decode_refs~version)decoder|None->decode_refs~version:1decoderelsedecode_refsdecoderin(* only for HTTP *)letrecdecode_comment?(comment=false)decoder=letv=peek_pktdecoderinmatchString.Sub.headvwith|Some'#'->junk_pktdecoder;prompt_pkt(decode_comment~comment:true)decoder|Some_->decode_versiondecoder|None->(* XXX(dinosaure): HTTP starts with a comment AND [0000]. We must
* consume it to correctly parse advertised refs then. However,
* for an empty clone (over TCP), we must not consume it. *)ifcommentthenjunk_pktdecoder;prompt_pktdecode_versiondecoderinprompt_pktdecode_commentdecoderletdecode_resultdecoder=letkdecoder=letv=peek_pktdecoderinifString.Sub.equal_bytesvv_nakthen(junk_pktdecoder;returnResult.NAKdecoder)elsematchString.Sub.cut~sep:v_spacevwith|Some(_,common)->letcommon=String.Sub.to_stringcommoninjunk_pktdecoder;return(Result.ACKcommon)decoder|None->faildecoder(`Invalid_negotiation_result(String.Sub.to_stringv))inprompt_pktkdecoderletdecode_packet~trimdecoder=letkdecoder=letv=peek_pkt~trimdecoderinletr=String.Sub.to_stringvinjunk_pktdecoder;returnrdecoderinprompt_pktkdecoderletprompt_pack_without_sidebandkcontinuekeofdecoder=ifdecoder.pos>0then(letrest=decoder.max-decoder.posinBytes.unsafe_blitdecoder.bufferdecoder.posdecoder.buffer0rest;decoder.max<-rest;decoder.pos<-0);letrecgooff=ifoff=Bytes.lengthdecoder.buffer&&decoder.pos>0thenError{error=`No_enough_space;buffer=decoder.buffer;committed=decoder.pos;}elseifoff-decoder.pos>0then(decoder.max<-off;safekcontinuedecoder)elseRead{buffer=decoder.buffer;off;len=Bytes.lengthdecoder.buffer-off;continue=(funlen->go(off+len));eof=keofdecoder;}ingodecoder.maxletpeek_pack_without_sideband(decoder:decoder)=letpayload=Bytes.sub_stringdecoder.bufferdecoder.pos(decoder.max-decoder.pos)inpayload,0,decoder.max-decoder.posletjunk_pack_without_sideband(decoder:decoder)=decoder.pos<-decoder.maxletdecode_pack?(side_band=false)~push_pack~push_stdout~push_stderrdecoder=letwith_side_banddecoder=letv=peek_pkt~trim:falsedecoderinmatchString.Sub.headvwith|Some'\001'->letoff=String.Sub.start_posv+1inletlen=String.Sub.stop_posv-offinletbuf=String.Sub.base_stringvinpush_pack(buf,off,len);junk_pktdecoder;returntruedecoder|Some'\002'->lettail=String.Sub.to_string(String.Sub.tailv)(* copy *)inpush_stdouttail;junk_pktdecoder;returntruedecoder|Some'\003'->lettail=String.Sub.to_string(String.Sub.tailv)(* copy *)inpush_stderrtail;junk_pktdecoder;returntruedecoder|Some_->faildecoder(`Invalid_side_band(String.Sub.to_stringv))|None->returnfalsedecoderinletend_of_packdecoder()=returnfalsedecoderinletwithout_side_banddecoder=letbuf,off,len=peek_pack_without_sidebanddecoderinpush_pack(buf,off,len);junk_pack_without_sidebanddecoder;returntruedecoderinifside_bandthenprompt_pkt~strict:truewith_side_banddecoderelseprompt_pack_without_sidebandwithout_side_bandend_of_packdecoderletdecode_shallowsdecoder=letrecgoaccdecoder=letv=peek_pktdecoderinifString.Sub.lengthv=0then(junk_pktdecoder;return(List.revacc)decoder)elseifString.Sub.is_prefix~affix:v_shallowv||String.Sub.is_prefix~affix:v_unshallowvthenmatchString.Sub.cut~sep:v_spacevwith|Some(v,uid)->letuid=String.Sub.to_stringuidinifString.Sub.equal_bytesvv_shallowthen(junk_pktdecoder;prompt_pkt(go(Shallow.Shallowuid::acc))decoder)else(junk_pktdecoder;prompt_pkt(go(Shallow.Unshallowuid::acc))decoder)|_->return(List.revacc)decoderelsereturn(List.revacc)decoderinprompt_pkt(go[])decoderletdecode_negotiationdecoder=letkdecoder=letpkt=peek_pktdecoderinifString.Sub.equal_bytespktv_nakthen(junk_pktdecoder;returnNegotiation.NAKdecoder)elseifString.Sub.is_prefix~affix:v_ackpktthenmatchString.Sub.cuts~sep:v_spacepktwith|[_;uid]->letuid=String.Sub.to_stringuidinjunk_pktdecoder;return(Negotiation.ACKuid)decoder|[_;uid;v]->(letuid=String.Sub.to_stringuidinmatchletv=String.Sub.to_stringvinjunk_pktdecoder;vwith|"continue"->return(Negotiation.ACK_continueuid)decoder|"ready"->return(Negotiation.ACK_readyuid)decoder|"common"->return(Negotiation.ACK_commonuid)decoder|_->faildecoder(`Invalid_ack(String.Sub.to_stringpkt)))|_->faildecoder(`Invalid_ack(String.Sub.to_stringpkt))elseassertfalseinprompt_pktkdecoderletrecbindx~f=matchxwith|Donev->fv|Read{buffer;off;len;continue;eof}->letcontinuelen=bind(continuelen)~finleteof()=bind(eof())~finRead{buffer;off;len;continue;eof}|Error_aserr->errlet(>>=)xf=bindx~fletdecode_statusdecoder=letcommandpkt=matchString.Sub.cuts~sep:v_spacepktwith|res::reference::rest->(matchString.Sub.to_stringreswith|"ok"->Stdlib.Ok(Stdlib.Ok(String.Sub.to_stringreference))|"ng"->leterr=String.Sub.(to_string(concat~sep:v_spacerest))inletreference=String.Sub.to_stringreferenceinStdlib.Ok(Stdlib.Error(reference,err))|_->Stdlib.Error(`Invalid_command_result(String.Sub.to_stringpkt)))|_->Stdlib.Error(`Invalid_command_result(String.Sub.to_stringpkt))inletcommandsdecoder=letrecgoaccdecoder=letpkt=peek_pktdecoderinifString.Sub.lengthpkt=0thenreturn(List.revacc)decoderelseifString.Sub.is_prefix~affix:v_okpkt||String.Sub.is_prefix~affix:v_ngpktthenmatchcommandpktwith|Okx->junk_pktdecoder;prompt_pkt(go(x::acc))decoder|Errorerr->faildecodererrelsefaildecoder(`Invalid_command_result(String.Sub.to_stringpkt))inprompt_pkt(go[])decoderinletresultdecoder=letpkt=peek_pktdecoderinmatchString.Sub.cut~sep:v_spacepktwith|None->faildecoder(`Invalid_result(String.Sub.to_stringpkt))|Some(_unpack,res)->(matchString.Sub.(to_string(trimres))with|"ok"->junk_pktdecoder;return(Stdlib.Ok())decoder|err->junk_pktdecoder;return(Stdlib.Errorerr)decoder)inprompt_pktresultdecoder>>=funresult->prompt_pktcommandsdecoder>>=funcommands->return{Status.result;Status.commands}decoder(* XXX(dinosaure): even if we handle with and without sideband, currently the
default [decode_status] parse a sideband. On the Irmin side, sideband is
used in any case but we should improve [protocol.ml] and pass true
[sideband] value. *)letdecode_status?(sideband=true)decoder=letwith_sidebanddecoder=letpkt=peek_pktdecoderinmatchString.Sub.headpktwith|Some'\001'->letstr=String.Sub.(to_string(tailpkt))inletdecoder'=decoder_fromstrindecode_statusdecoder'>>=funres->junk_pktdecoder;prompt_pkt(returnres)decoder|Some_->assertfalse(* TODO *)|None->junk_pktdecoder;return{Status.result=Ok();Status.commands=[]}decoderinletwithout_sidebanddecoder=letpkt=peek_pktdecoderinifString.Sub.lengthpkt<>0thenfaildecoder(`Invalid_command_result(String.Sub.to_stringpkt))else(junk_pktdecoder;return{Status.result=Ok();Status.commands=[]}decoder)inifsidebandthenprompt_pktwith_sidebanddecoderelseprompt_pktwithout_sidebanddecoderendmoduleEncoder=structopenPkt_line.Encodertypenonrecerror=errorletpp_error=pp_errorletwrite_spaceencoder=writeencoder" "letwrite_zeroencoder=writeencoder"\000"letwrite_new_lineencoder=writeencoder"\n"letdelayed_write_pktk0k1encoder=letpos=encoder.posinencoder.pos<-encoder.pos+4;k0encoder;(* XXX(dinosaure): or [encoder.pos <- encoder.pos + 4]? *)letlen=encoder.pos-posinBytes.blit_string(Fmt.str"%04X"len)0encoder.payloadpos4;flushk1encoderletkdone_encoder=Doneletkflushencoder=writeencoder"0000";flushkdoneencoderletencode_flushencoder=kflushencoderletencode_proto_requestencoder{Proto_request.path;host;version;request_command}=letwrite_request_commandencoder=function|`Upload_pack->writeencoder"git-upload-pack"|`Receive_pack->writeencoder"git-receive-pack"|`Upload_archive->writeencoder"git-upload-archive"inletwrite_versionencoderversion=letversion=Fmt.str"version=%d"versioninwriteencoderversioninletpp_hostppf=function|`Domainv->Domain_name.ppppfv|`Addrv->Ipaddr.ppppfvinletwrite_hostencoder=function|host,Someport->lethost=Fmt.str"host=%a:%d"pp_hosthostportinwriteencoderhost|host,None->lethost=Fmt.str"host=%a"pp_hosthostinwriteencoderhostinletkencoder=write_request_commandencoderrequest_command;write_spaceencoder;writeencoderpath;write_zeroencoder;write_hostencoderhost;write_zeroencoder;ifversion>1then(write_zeroencoder;write_versionencoderversion;write_zeroencoder)indelayed_write_pktkkdoneencoderletencode_wantencoder{Want.capabilities;wants=first,others;shallows;deepen;filter}=letfilterencoder=matchfilterwithSome_->.|None->encode_flushencoderinletdeepenencoder=matchdeepenwith|None->filterencoder|Some(`Depthdepth)->letdepthencoder=writeencoder"deepen";write_spaceencoder;writeencoder(string_of_intdepth)indelayed_write_pktdepthfilterencoder|Some(`Timestamptimestamp)->lettimestampencoder=writeencoder"deepen-since";write_spaceencoder;writeencoder(Int64.to_stringtimestamp)indelayed_write_pkttimestampfilterencoder|Some(`Notreference)->letnotencoder=writeencoder"deepen-not";write_spaceencoder;writeencoderreferenceindelayed_write_pktnotfilterencoderinletshallowsencoder=letshallowhashencoder=writeencoder"shallow";write_spaceencoder;writeencoderhashinletrecgoshallowsencoder=matchshallowswith|[]->deepenencoder|head::tail->delayed_write_pkt(shallowhead)(gotail)encoderingoshallowsencoderinletothersencoder=letwanthashencoder=writeencoder"want";write_spaceencoder;writeencoderhashinletrecgoothersencoder=matchotherswith|[]->shallowsencoder|head::tail->delayed_write_pkt(wanthead)(gotail)encoderingoothersencoderinletfirstencoder=writeencoder"want";write_spaceencoder;writeencoderfirst;letrecgo=function|[]->()|[capability]->writeencoder(Capability.to_stringcapability)|head::tail->writeencoder(Capability.to_stringhead);write_spaceencoder;gotailinifList.lengthcapabilities>0then(write_spaceencoder;gocapabilities);write_new_lineencoderindelayed_write_pktfirstothersencoderletencode_doneencoder=letkencoder=writeencoder"done";write_new_lineencoderindelayed_write_pktkkdoneencoderletunsafe_encode_packet({pos;payload;_}asencoder)~packet=encoder.pos<-pos+4;writeencoderpacket;letlen=encoder.pos-posinBytes.blit_string(Fmt.str"%04X"len)0payloadpos4letwrite_commandencoder=function|Commands.Create(uid,r)->letzero_id=String.make(String.lengthuid)'0'inwriteencoderzero_id;write_spaceencoder;writeencoderuid;write_spaceencoder;writeencoderr|Commands.Delete(uid,r)->letzero_id=String.make(String.lengthuid)'0'inwriteencoderuid;write_spaceencoder;writeencoderzero_id;write_spaceencoder;writeencoderr|Commands.Update(a,b,r)->writeencodera;write_spaceencoder;writeencoderb;write_spaceencoder;writeencoderrletencode_commandsencoder{Commands.capabilities;commands=first,others}=letothersencoder=letcommandcencoder=write_commandencodercinletrecgoothersencoder=matchotherswith|[]->kflushencoder|head::tail->delayed_write_pkt(commandhead)(gotail)encoderingoothersencoderinletfirstencoder=write_commandencoderfirst;letrecgo=function|[]->()|[capability]->writeencoder(Capability.to_stringcapability)|head::tail->writeencoder(Capability.to_stringhead);write_spaceencoder;gotailinwrite_zeroencoder;ifList.lengthcapabilities>0thengocapabilitiesindelayed_write_pktfirstothersencoderletencode_advertised_refsencoderadvertised_refs=letencode_shallowsshallowsencoder=letencode_shallowshallowencoder=writeencoder"shallow";write_spaceencoder;writeencodershallowinletrecgoshallowsencoder=matchshallowswith|[]->kflushencoder|hd::tl->delayed_write_pkt(encode_shallowhd)(gotl)encoderingoshallowsencoderinletencode_others_refsothersencoder=letencode_advertised_refuidrefnamepeeledencoder=writeencoderuid;write_spaceencoder;writeencoderrefname;ifpeeledthenwriteencoder"^{}"inletrecgoothersencoder=matchotherswith|[]->encode_shallowsadvertised_refs.Advertised_refs.shallowsencoder|(uid,refname,peeled)::rest->delayed_write_pkt(encode_advertised_refuidrefnamepeeled)(gorest)encoderingoothersencoderinletencode_first_ref(uid,refname,peeled)encoder=writeencoderuid;write_spaceencoder;writeencoderrefname;ifpeeledthenwriteencoder"^{}";write_zeroencoder;letrecgo=function|[]->()|[capability]->writeencoder(Capability.to_stringcapability)|head::tail->writeencoder(Capability.to_stringhead);write_spaceencoder;gotailingoadvertised_refs.Advertised_refs.capabilitiesinletencode_no_refsencoder=letcapabilities="capabilities^{}"andzero_uid=String.make40'0'inwriteencoderzero_uid;write_spaceencoder;writeencodercapabilities;write_zeroencoder;letrecgo=function|[]->()|[capability]->writeencoder(Capability.to_stringcapability)|head::tail->writeencoder(Capability.to_stringhead);write_spaceencoder;gotailingoadvertised_refs.Advertised_refs.capabilitiesinmatchadvertised_refs.Advertised_refs.refswith|(uid,refname,peeled)::others->delayed_write_pkt(encode_first_ref(uid,refname,peeled))(encode_others_refsothers)encoder|[]->delayed_write_pktencode_no_refs(encode_shallowsadvertised_refs.Advertised_refs.shallows)encoder(* TODO(dinosaure): handle HTTP/stateless and side-band. *)letencode_pack?side_band:(_=false)?stateless:(_=false)encoderpayload=letrecgobufferoffmaxencoder=ifmax=0thenflushkdoneencoderelseletlen=minmax(Bytes.lengthencoder.payload-encoder.pos)inBytes.blit_stringpayloadoffencoder.payloadencoder.poslen;encoder.pos<-encoder.pos+len;flush(gobuffer(off+len)(max-len))encoderingopayload0(String.lengthpayload)encoderend