pax_global_header00006660000000000000000000000064142540747340014524gustar00rootroot0000000000000052 comment=1eff590933290be2b565306c89a28b2537a0659f swhid_core-0.1/000077500000000000000000000000001425407473400135125ustar00rootroot00000000000000swhid_core-0.1/.github/000077500000000000000000000000001425407473400150525ustar00rootroot00000000000000swhid_core-0.1/.github/workflows/000077500000000000000000000000001425407473400171075ustar00rootroot00000000000000swhid_core-0.1/.github/workflows/build.yml000066400000000000000000000016371425407473400207400ustar00rootroot00000000000000name: build on: pull_request: branches: - main push: branches: - main jobs: build: strategy: fail-fast: false matrix: os: - macos-latest - ubuntu-latest - windows-latest ocaml-compiler: - 4.14.0 include: - os: ubuntu-latest ocaml-compiler: 4.03.0 runs-on: ${{ matrix.os }} steps: - name: checkout uses: actions/checkout@v2 - name: setup-ocaml uses: avsm/setup-ocaml@v2 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} - name: setup run: | opam pin add . -y --no-action opam depext -y swhid_core opam install -y ./*.opam --deps-only --with-test opam upgrade --fixup - name: build run: opam exec -- dune build @install - name: test run: opam exec -- dune runtest swhid_core-0.1/.github/workflows/deploy.yml000066400000000000000000000016521425407473400211320ustar00rootroot00000000000000name: deploy on: push: branches: - main jobs: deploy: runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v2 with: submodules: recursive persist-credentials: false - name: setup-ocaml uses: avsm/setup-ocaml@v2 with: ocaml-compiler: 4.12.0 - name: setup-deploy run: | opam pin add . -y --no-action opam depext -y swhid_core opam install -y ./*.opam --deps-only --with-test --with-doc opam upgrade --fixup touch doc/.nojekyll - name: api run: | opam exec -- dune build @doc mv _build/default/_doc/_html/ doc/api - name: deploy uses: JamesIves/github-pages-deploy-action@3.6.2 with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} BRANCH: gh-pages FOLDER: doc/ CLEAN: true swhid_core-0.1/.gitignore000066400000000000000000000000071425407473400154770ustar00rootroot00000000000000_build swhid_core-0.1/.ocamlformat000066400000000000000000000017151425407473400160230ustar00rootroot00000000000000version=0.21.0 assignment-operator=end-line break-cases=fit break-fun-decl=wrap break-fun-sig=wrap break-infix=wrap break-infix-before-func=false break-separators=before break-sequences=true cases-exp-indent=2 cases-matching-exp-indent=normal doc-comments=before doc-comments-padding=2 doc-comments-tag-only=default dock-collection-brackets=false exp-grouping=preserve field-space=loose if-then-else=compact indicate-multiline-delimiters=space indicate-nested-or-patterns=unsafe-no infix-precedence=indent leading-nested-match-parens=false let-and=sparse let-binding-spacing=compact let-module=compact margin=80 max-indent=68 module-item-spacing=sparse ocp-indent-compat=false parens-ite=false parens-tuple=always parse-docstrings=true sequence-blank-line=preserve-one sequence-style=terminator single-case=compact space-around-arrays=true space-around-lists=true space-around-records=true space-around-variants=true type-decl=sparse wrap-comments=false wrap-fun-args=true swhid_core-0.1/CHANGES.md000066400000000000000000000000451425407473400151030ustar00rootroot00000000000000## 0.1 - 2022-06-20 - first release swhid_core-0.1/LICENSE.md000066400000000000000000000014321425407473400151160ustar00rootroot00000000000000The ISC License (ISC) ===================== Copyright © 2022, OCamlPro Permission to use, copy, modify, and/or 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. swhid_core-0.1/README.md000066400000000000000000000022561425407473400147760ustar00rootroot00000000000000# swhid_core [swhid_core] is an [OCaml] library to work with [persistent identifiers] used by [Software Heritage], also known as swhid. This is the core library, for most use cases you should use the [swhid library] instead. ## Installation `swhid_core` can be installed with [opam]: ```sh opam install swhid_core ``` If you don't have `opam`, you can install it following the [how to install opam] guide. If you can't or don't want to use `opam`, consult the [opam file] for build instructions. ## Quickstart Have a look at the [example] folder or at the [documentation]. ## About - [LICENSE] - [CHANGELOG] [CHANGELOG]: ./CHANGES.md [example]: ./example [LICENSE]: ./LICENSE.md [opam file]: ./swhid_core.opam [test suite]: ./test [documentation]: https://ocamlpro.github.io/swhid_core/api/swhid_core/ [how to install opam]: https://opam.ocaml.org/doc/Install.html [OCaml]: https://ocaml.org [opam]: https://opam.ocaml.org/ [persistent identifiers]: https://docs.softwareheritage.org/devel/swh-model/persistent-identifiers.html [Software Heritage]: https://www.softwareheritage.org [swhid library]: https://github.com/OCamlPro/swhid [swhid_core]: https://github.com/OCamlPro/swhid_core swhid_core-0.1/doc/000077500000000000000000000000001425407473400142575ustar00rootroot00000000000000swhid_core-0.1/doc/dune000066400000000000000000000000711425407473400151330ustar00rootroot00000000000000(documentation (package swhid_core) (mld_files index)) swhid_core-0.1/doc/favicon.png000066400000000000000000001115351425407473400164200ustar00rootroot00000000000000PNG  IHDR$$I  pHYs  sRGBgAMA aIDATxp}.ҔE4iCnܒSOJv#N؉4ѴK*N/yQə[3@Ò4n,8d8TRDucI &eڢ]y~ . >,}G#hoX)zkӓȧ=+:l<)^Sz M{C5W~8ܙb cge6d2ޯx7db:湵;#H"H#NF~G| \DA8#d> DA>RqwXgqVLL p e1 9UO^ N 8 ix'L|op|ErhSNOdoBo0<4!H [Jζ9@¤&WӁaCCU2o H1bDZUhlUדR=L"=0<$#H ^L.{恇[VO1_7Ǻ.M1 "H 7vyfhܒdXo'*f$& HR]{sL,ZӕK$$"cDZYAa_8ѹ2XneMA)qxs'{=M}'#V&Qơ0  Hb?88:yq78rL BH8b$8S\'Rr& #L}f0|i&U!)SoA uxS 2xX-'b[ߠi\0Aǩ`@T:xM,uIN h p=hoX.׎Gb'jԇO86B@BhT*)sA,Y&:1  HŞL}? A118)cXBDIMd7,bI$A1G#4N.0yaxs7Nbj̛Dy^Λ0 `> ̋/ݐeO7Xs] t +@!=z< #F}]:2d^Dϛ2o 8@#Y)#I46%÷gOl(@$#K4Fte%FBXŬDz\xO0< DܡM;ͷb 1D Avxs'{b1  l Dei# B@#2𶏛c@d9OdXҕw"FЌ)W.OEz; O|:@}\]mKu=1 _I,MDT}6|ꍱ3 b@ .4LLVs'6rq*'7 iT*)syXMVu[c$y!H DDD4^0s9&BG@H7{=e~KD #HA#Ftx]؁cl 1  x \y-9Ǒ ҕ g'M?Q0}GNk1і I&Q& v$t=[1RȚ"1kĉ~Eؐ>z<4 ixsL,ѕBAzT]>ypyưCOmiKaKcS2^0|{{RI̋ +#4N.UIMՖw s#H21ґw"F1 A#2v ,Ti*W5ၘ,9OdXҕw"F0 r`Gœmb'ax h1ċJMjkq̛ hbRqOĒm3/0<>@ɣ0qs'68ru+/3o H`7{=e~K :&Qơ0 #H` ]4Ntx5ڇ/wɷG 3 ,1+/+I\O~6^߅rjіb{VrDɸy^b #Ft'-Q+M4D.Mշ^B֑|VG;%adZPGP}6|ꍱ3DA7K7dsWRGzԭJR;'BVn7IlF(02axוS9 䲧̛eQ,YcIi1&IǺ1|8A0 4 yo'ѻk鋑aswWʾ<֢tcoI6Gax`x9~qu%#2RH}}܇e< <&8 /"D BT0q] &AW#vD%Ffk̛0 i v÷gOl( #H.eQي\}$m 4N.UIMw8=rB$R#.12Ƽ H:@}T<&]ʂN_0mm4e[3#t +@!w*7b$Rˏ󖂤EcQ0< :G{ry KVtdMwbf.}X1wm#1 bIAHJ=e>7%+;dUg^DcLF">U #ѿؓɸ#Ikወ27 TqG,IcTj1;cQГ5!Ǒc[yyt"H$1+/+I#7[>yDMc$yӅ h~Ĉbd YɦlhDƉ\Fw;a}#;$zb)Fyb=qYaxDkd*&HqL0oX D䲧̛\Q,;R6#0a.V4F6%Z0<1 _Ew> _r>A"$cg8W#8)r6M9&W&? rq*'77@~qu%zѨ1RH^dBaxDA 1;4F}XaxD tg^$1ri*G`Q0$˼ "z!ϛ8q] &A-bb$& #jb$b<]b>sK>u1r~"ƦW4e܉8lZɷqz P 9xM, ;o)HNZ7EAxMEy(yCb\;j޺KVtdMwbzb$uҔ-kQ/2'\rGCJ=yA,Y&:&#zx5t'+Lc]̛ JA ;40 j l[߫۳'J$"cqS$1RMHOWFGTi\ՓHϛ0 oA ;,2ᓍ I~Ĉ0y\r&zubD_[L_\)WeT!FƼ 0|i0|m=cg$\XH2RH#%{òa߽b!+/m.DG^a!וS9y@miK#ƪr~At0 b>z9YfW,Ik\ 1 ̬d3\z :4NtKՈH09& ĻP؎i#@i #jA p~ĈRvA1 (c>X @yk%t3ݴ G1 ?@`m>j֛%臞6~@I0_iX@CwmbU\ݕOe7~6&axDYc4YżU #wR4_Lp;p'BVn7w7A4_YV?l͒tX'wgsdEGNzR# ;UK1}G:8Ot1okQ.&0xM~Wʐ pƈ㸧̏K }"q04ҁ& 1#Ȃɻy)@>juyO.ECXT4qO_i?J r IK\%Lɻ§яM8AйffIL6iW @ʽx &F^K13KĈ%Q"ezyH o\  /]Mc(-U H&ՕLƈϢ.~/̈&Lޟq r9|r="$տW̫1;'+;룓wuՄ8OO؞~zd:d)hQ of R]{sL,Ev ޵y4Y+Z;]--ψ&L.MЌ0wx$H!FKOկ]9̛qr~bGsƧ$@^&=d0ax4 E:LjGaKcz¼  c.7'·"H1ґeb$f4Ntd0  }]4Y`^A"H8?bDWFr k 5s^W,mc$*8Y瞑qE$M/x"Ēeܒ'Ftk YSAcLR>;E uhsQ2M,;o)k @4{Μ($1KހX Hb cdEG>{'ʥ)MhޤR_9a>YĈQ XDcRqO b6Yՙ@5K,BVn_ƼIMLgKe 7'Ol X!A 2}d~#o>^1 O#2b㆒yω% п8)cXB`ҍax6wliu;aVG%j#F%'AeHIW}X[1bo> $@L#8YM>$˼I o@1K7drS(G.+\;v̛iԘ7ac\*ßp+?bd'ڥY/'@z4_T Ǐ3V/b]sV2"1i\6aDy eƘ3ob_1}T$@LhxsL,ѕK战Ƽ @4oh,[4^O[t뒶\y2岅0(gHgϯ4F~LsY-"H#Fteeo].3'Ɖ4+(惙MDәKY%iЛ2?2)U{Wcd "̥]r@kB~؝|F'@μ`:FtedQCD̡G͛6Dx؁ӝy{2 $)'$FKb3\K3b78rLBDpXTSdegK 846ek3͟wӚl!#F`swUﰎOri|3Md¼giT-Ǚ.Ac\vO{޸]36N/M֬h i#QB!qb۵a[&W&C~Op%7K ~Ț<-#] 9]Vk^Zi,i@#F5sDD4^eƈ⫏U٥IG(`x &F^K1o=ż,+ EDWax?H\ IbhdrY])E0<L+dň"Ho'C1{y9ȴƈ"H~qu%q0<>ӦmM+1a&Lޟ1o̡#秊4IKWYӌ |rxs+{b]ٙ7_͛0 Lϴ\Ƒ^}e$1Ӯ͛rVW>SM+1WA21]$ Y|DxM&8cc#FX##"H ׳u;H9YӝԦc棛=Gu| 7Dl#F|{-q `ƈ9M,aIH/p[# #4FVu%geCJ$@ rU#b KG#B$@4F*A,|0/"Lrll-F ]ȯ;.+yP пcbUb@f0W_i{>,-#2ߓ ȏ.?\ed~#bd1#ƽ;OmHι8:%F+bKZbDq5ċ/ 1R  uKUMbd1L.{!F$^D~1 f#lLjkZiA8@KxE ã|M#F H9#h0<&KG.#kR{b˺M;.FArhSNϕb 1 \4F3-q$g^"Ho~dXBM?Qc$5+-nͻI$Uc%mIq3o>7a>4FFi1&ej~{'vK$#,iHk&n!|\ cEGNt;F*fub/ RLKfeHHb{̛$6YeVLcDWFl#G m>jbdX’641/MY#e!HJG{r}AaKcz¼I\#H3GG u4F* b o0?AX# X&if;FlMfeATiĈG@(6]U4r1R5 6hz ;F 57{e2"##м0n%9#H(ƈ#EMƈnUіstNO`51G 'n)M|DL#Fҋad4i1RȦ;F.NɅъB,WUH_Ac(] ׆16 +!h121eO9c%mه/2 841_iA9@#ƽH@m/W7bd1+# Ahxs;xxk=O[G.SLel}JJ}7[zn?1qhSNϕb 10i̓8=>5 g^d= y}muW>1Rs1M b,Doc(4Fum;ĚaIo0e&O7o#'kb$1hμh$ +;dߕftgI A,}6%myCRY9 1B Abb{r|Mb~YУ$b\{HX7F1z͙[֘xsXҌ'1RD b 1X*f$&үlbʭLjf_w\V_ "cȫDƉ`F9#>W,k "McqSG,!F65 1Ci2\#]w/?N Ad5b#|-OaxJi#>,-#2G|- (Ml_[cMw\b$ :Cx &F^K10͌)W.O5=oBL8:%F+b 1.)#\VWFbK(ϣWadybiNnhoEfcDgEtf A+NUa[g!LVh4ϱ%mt?Gʈ$t=1%m@4_j #FNZ6cdC :YbGc.#ϼ$zxW,!FHR>o}O{-#L=F 4F~s["H8bGw;{I:#ݑduW 4FteC#CDA}6Dcq7?bd}h#H໣b\;*<"ɚnb$#Fzu=*>TS"deg )ѳE6b$>#'ƈTł #zŽ HŞrYWFG,!FHJMJA4Fsӓv[XS!H`1;iZ U!F\ I`ŋ/ 1R r#k+Ce#\c1F}]:2vƈ#CK?)dpՇixss9?R#zI BSmr@,iH!KeH3/ ?,ϚoNo؍_A$+$XϓbIG.SM 4F~W]'ȽO$A#Fte$q1f$XÛ_01K,ʛ"1@Tj1R#X<7uhsQ#=y]O`GU uX,Ws#Fzu=*HsTS&F6%+;dUg^@# u1#`Sn=Fb=W<(HӿS. 9=}}]Fcq07=ftݱ+$G9aYliĈ~GqbХ!FJ_fG.#FHS|UcidrYX1F#F+bΊ|yqbCT^q2:b5+#Eb#FteDwB4olqkb&CG?ּSo$)t=9&hH!KG*rilJl#8?8Yz=q6_c6u?_x\XR7;iZ{1}%[2WKL8?bDWFrƏJͬ\&F`UԽk$m>j3FCbdѕr} zc|!.AP#数M,1@##1(!Hho;،1ϯYУVM]!HDcRqONb6YMSуuqb7p-$!7{4FL6l#:Ϗ7_;7|>A#C WjXSww}_o-&c#dMW^V,mzءnK#F;W=RPG)_ܹ}TY!1?bD׉I.wXϝ[$']Lg[ѭIUVHbvouw/Sra"#η^4>q|\dRZAUo^6!n^L.{+#zaG f$1\o'o/N Bd.y}-}h#FlLj#`;Fq,\hf霌Uh~l."[ѕ+F _# RA\Ƅƈ:f_ ztxf{1b$"^?j輈^2Oo&HYYI_x FcЦƈeH3/#0|Ս|bIK1ҰY3A7w?kKzm¥j{آ1LB*8ׂ=^#LcdX ]V FHFW[ۆu#>[.?맰۲Ҭf趾u3--$G J͓W$SN>:K Û_01K,Eb$ѕVcDgK$bm>jbdXbYA?bd}p1Fl!H"hoX/1G>/zT.?bDZo:*$4F* bI\ܜ|7b$|q5'· 5b#F&zTD+=POaGtp]cDm9N}GfmFi Gd3AŞr٬G  9#FtGՎw~ͭy h81榇db-FXSp{9}pуuG cD7fy^v3A?b$g4F}X[x~آ1+#A|}qtspzt/ 1nXUΏ#Iη^[Œ ͯZx 2#E %/f!F##Ŗƈ35GwDc$˞r,HЃK?cD׿81c#ߔcCOsj6zf4 VFʈ<ixsL, kyHE.M-w|ygi2^[|^:!ѡM;=W%ɢ;iZ{1}%AwO[t#(h6_s(VH|rxs'{b䨹^31LIQēGB1O/p}O{-t)춄q!ϛzz ̏ѕ#E}CEO6}W#I3zw[ˆ?^*'Gn$}6$%`1\̵sOt'n[1"z˺ˏYӕK$HĈopB\G{r+QHr#zCZoL)Q<$J=ebdXBS-]A︬AAtp#9ǑcD_#U_$&3#B҄ŞL}#Ful_i| ¥1+# #QG[;#n7"q"i8{sRZc 1~"bD;5#[ЈcX^,4bD#\|E!1׶_R?bbdsFnGȏ cp XUΏ#I1rCxL*b$%Sra"#}֑bKX1b5*bDqE|#\VWFbAX #E\ΞqV1+NUs}#e;F:ru`.Ą1gl,%\];N攭"[IǴ)Y 4F<9&<nE{1}%AwO[tfxuW>xM,i .Bo}p#k K$H~FM<$@hoX.4FK\B~YУp#zCZoLɟo#$` J=eoXĔ[Mօtɬ#Y~ZGVwۯQOz*H1yB腞u4Fu=ѕUĨ-=lF5FjmKcg$$YIŞZ=m~XBÕR&FHϿ뱏ۯF<bTh8>%=b 1@rz-]wLj~G= y~%ňJ/ 1R zVI.bd^ 䲧bk ;e;F:rY1g̖%]a~q1(bbd FˏIG1R\Dhxs`LXsG*rilJlH~pb˲BNznK.ȵ-'/ ]QCwobd}ǤxO=y\l t##}$$7w?kKzz뿶C6'tgO}5eEGNt#=[*H&|. kp اz?`Rƫr{'v ¥1ǿ\>~F͵sfedDďѕ#ɕȕtLjM/x"֖feHz1R#I3zw[Šۯ8ƈGœmbIXK>b$qzOAݶz˺ˏk@ ?+#'GILfhoXR$" FH_cD"Hp#SnBj.lЃuqYyσpƈېs]kUJn7HE>H7{=e~KÏ7_wˏU;ڂ=ށHAm^uݱwOb5F_#_Ɛx'%FTdWH0 sŖFwKcdbK#F>kʖ!IH 1nXUΏ#Iη^[ŠۯQIh]x &F^K1 @GhEl!F##ŖbkT#*R3$#\Hcp l_1 c#ID3I+d^uDbbK~ňp#L ~H>ٲ}`$ t=8S6c$]?l_qσHD~pʇ#G*cQhxsL, YAݥHw+eCI3/ 7|{آFFAևm#$)0Z[x>y~%bdA^b'FHb$y# dY0N/tp]cD.1ґc$ :ۯQǑ3|fɒবgƮmHIt "FQOl @r`۲d}H <'<.h}B&h_'/gv#ce)ƣZ+:r G> |4F j>! jg.*ClYy>A?\|bސ^lkl,#XtûMiyHKj5vRzzx[{bnA4Fg[xTߏרYyά4 {jR#Fj?^ĈM?uX,]}TGyaNe_^ߑlƩGRެt׸S[hhĈ~%%FiJ)-Ĉ>2/])dbʕ-?edY|gM@zw_ˏV,m 5~rpVHllO. Q- WcS hn qH5?.zu=*1xG iUɸi.#ƪrqr4 ]\}ՄiC$Ί|wb$8ᠯiMk\IL |~ȝ$h_'/ l9{ds?OeLwnWy )}k0<6ѧw=P>eEGNt#~Fcdx T ^&ٗte#F4DnuC{7~˗'d74A>2l}O{-lNϖ͏רLqhS)JRb(G;_kWN1o0<`bzH0H^-+f'm:WVFوU8u &q1 z+#6Uˆ?^Hp~qG h^lm{YCȭw_Q;V&2a+JTǪ2i~u&#Ftb> ɡG͟MZȯ/6a:diqiDž^3G#Ft''F}#s ԭk" 9kZy tXRo{߉0L'%v M"FGDۼAҿq2 ~xޟ܉(w7- 3H dbʕ =}W<(kzJ#8;~B_#U>UJ[G5o5x^f䡛:npt誈>4wi kaJWtՄaxHz#_kp#sq-O1vFiuM~Y@E}'}\km/>,a7?:0<$;{lBOo=o=/*eAeI.~}$F!#y@mn+oMl7 };8A̎4axBOo4x~آ2HۯQc*['0s9:?"M4KWFֿGqd+;̛@ #η^{=4V#H$Ώd;鶍TZ;w$.t֤1oaxƇ]GhEl!F##ŖbkTؚ3HW kGV#e;F:rY1ϸne+1OsVG+("1?x׌jA4Fu7WvɪNW߈Q#ϙ+H릎}v,K۬yatJjMة2ii+'k6~~2{PcQ<^WN>esDdxX26; •%Fc+$/KiVNgOZ61 3Ass݂pS #6n#Fx# 'sJpKŦVN ݄}0L 酞un#ѐ%Fc{Slfĸ#gBqܳ+? 1 @q5z \~ĈfxO> HJaˏ;SQg:g$: 'q M}ޏp#j [Gsp *&NE.t|Y!F§75Fl}M ~%S#O#GDۏlg}mտS=|<#NV0YfVO7zw^sO̦1DAuh77 q-O1vF j}i"J>c^6K4YWbq&Qơ0  Έz+rk17=ﮔ3$Y!kr5sDDwaΛb!W_5 YZhE+ME߿5F;¥12EcDWF>kbbdrH*ז57{%5˃n'wilJ&k/~靶~DYLFɚ+rM~>VIwؒL #i4qGd/fE߭w}NSra"#}֑bKX1b5*H] f w١B'Db$lLj>.i#HVH\_.jMF}!ș}b5NΏTcBOW81~Ĉ>$kqL>ٲ}`$HkAgIl?HެpB{tו|%vdeYax#Fte~pbKai%'FJ>j#FY$ll:hk٧_[ȕ! Imq<#4s$M`^5sn>zLzyI>};o)HlF5F 0ÌGO:'qA+[<#{8M:o҈}w6M,|ӕb$y3LO_SmY&:p3=aEF&F}m?I %>f0{^_n>؃1 ? ==}}Cr{'v •5wϙ+>Z!V5i0_0G: NՋ 1\1b)<9{AMS*ɚL4KG91td/E~^0^0b$#?%FP;e0x{ߑYa0|ۗ1oPBL$1+_Q Qb 5vsڛi?-H'R6aK@qLQVˏѝ #c,FdQ0$˼ ኵ =ljόw-+#Ah5j JY R1 ߘ70ъhބax B1#fƈpo#6_ƐUM"HzMt.꼉*r=N,wi*G$z>y~%17H5\3տthQ8oR*W_䮃̛8:%j-Ht$%FlFE 0}^{3tN]mߟ1ou&Kx  =}љb$|cDϡͻר׭l%F`G#͜EVx 7sݓe{߉0 &X&]].bdn#|fɒ|t0ipDMr7;7Ũß/ؾл#0|_-+x)[XG8q0<^KFڲǤ#F Ahꀖ> A$ꁇ-xɚ`cNۏm q!zd\{|fU`VHzeE>&vYĉkw=_(;Lԟuńt~\m޹_6#Ft'- ?1m9 @f<%f٪}bGC!A<|Q]<ȏ,n=in2oB(¥̈}]ݝ|;uchdu3<"wI[%Cf։($(~n__!ŞC?VtBrxUJf$c>su+ekzy]sAcDO_%lFy)#o>98D!1QaFN3dFI0<,ӻUKWZ};pK?[[7b#mF5FjmKcgɌ ,>l+K)79ђI0%V1/M,V/ߣaxD^59AcDWFx8|o'o/#6_r5F'=GY)/7r ҭ׿&Q=^1onrat7CDGRbkTDЬiI[sUEnIT&axI.1ґc${1ؕWVo26">Q/U0>~]嗶V}+je2&~\_& =\י+"F8rPl yhU3e2v"QLVe]|Š__\O/Mj&D6u^8W>˪ #Fte~p.+$"F̖%#E$}"29dI[~!XG f 2Dn~+:dY{y4M7LэSly7GD}2[;o)Htx]l,9]uYՅ0nEje3Xsɘxs^/9<}egϛDy^uc zz&edޅ}Oc \zz"bD_cS<4+#ϙEv 庂xSMHlﴥ'&wR`ƅ^3 ¦1+#[ˆѫ1W ieF*~:ib$Ͼq0<@/4F5b$i1bQҰbkA\]B&#o"w_h/|0ev}D dxJ;}wNSC\~6=d~՟֕(EV]mՓ7NrK8|1#Ft'ۗڽ~?^'Oz%e4`{_^fl޹x =owٜBdWYs ]9W"? kG~Ï gMt?*_1xG Ss"v:D+&~E~k /jRۚeM:4P'mZ5L G =];zI^?Gl!FhMN~uuAgH}b=\nZ Ƕ:&+~~I+[ 6+ҴZߡߦg疕,].Lz`/}( lv9>:[ab$ʇ/ 9Y>(iD$i#FtCڗDzHÛEK#?;/>m飼#$it~4obVLtIW|#]hW2'kn}U?iD\n[hHJ%Fv7 /ݵnutlJ6KsiaxkMpGM #*ǹt$}]:#Ftxv1ߌQbX#[Ƕ'X YI%:"sn[&/tǟ}|ENcEG[̛qL{QAi'F}#Hm4m+n[sp{VIk771v?'6"|~:+'Q zPsm6;u#ɥ'ŗdLL?nbdAAO-ro4l~:Kr]KĹ$\T_syRnf*%78zB-vZm{n~-u׏}?VkGAk8 5s ԥt6Q?^/sճs_k܄$W=bD_kǶרƈ[myꍱ3R5ؖm(z/b]\pm5jKa5 ~2.Y?v sIp[ n>^Vr+׳ߣ=1I`Ҹ`6!/?yz|{h;@4T?ޕ ?WIllz\ <*q I4{ƬdMvB4J^ՓΛb!'mεc]d u#ϼ$6wqpEcopxXv?|l OB0 _!w+)1 ̝fb9#{@S^ؽ,v<+MsD鿥rkw=?}G AAt^Oiq{'v ¥1ݏ&"Fttݳ#@.VNnW~ztKDWJ$AbD+$˗HGO],[$ :O#+$>/Hkqj='fe֪Z#Fƈ~:Gcĭֶ<`I2Γ⇖Su4o>Dos&VHA NZijPM#R݂p #y,#^'F$2^J#Mg<5ҙ֗F*vn\5w(0$e ?H( 끇_{XB.Hjʈ~IO^RYMȐxM;oMou)iR'#eXk&'v˽O쑠Ȗ]ݴ;R+a;yP=|GkBء":4N*f}9w|Z:o_S1|n~9gl(@`s[,s薺h.7 >ҥaĎCJvWn>yqEw\m>ݣc6qJ ؉AFIEԕV i#렝ȥ1{۞'/gv#@8|a/.M qW߭,ku_=SyIxx⁘+:r;H6߂D='-gR?{ JɻLw0_ƼB 8kH"?wĖm3GjWqhyw Pk?,=c:zIS4R-Ͽs0 )JF~v~;dӒ+s0b ]r@tӁ뉈VjbYyάJKkiy!- Mu2ҙ-KrVOuoV#N^J?:׭$ɬ\0;Dѕҏϊ-axg+"-1Z=]goLt4#ԕ>nMZ閿c$8 o;1DS WOJjRQ>!:jMq ]IA1''FF H ʺo4d{NZCmyy&Kꏏ4qƈ64b$͉)W~n!Fh "݃{t``[G-aȥz N  Yt?p@ k}!~Mwax@lLj#G Eb@At?p%/;sT"t\4+I1ra #g BM?ٞꡉ %E=J,w[e\=Ft1H~H>!F qhXyK5OG(72|b~]V~970<G#F.}GlYՙ AprW#\s9 Hn{01Rڻ!x5F D?~DG_-iz/]ﶄ#,'1`a";W$m ~Ĉ#2R#&%e>/vߚZbVW=oG8"O#@E~&[Ϥ%~%1.@謈Ό؊\ۙ1HxۿqzHX!ۈ['Ϧ$aƈZ6h3c&\1k;m9&/6Zh2[tq aʻ'_#zvHG[~Ĉΐ~  :Xo27h.wl<,x~҈ﮔ9#R+mnsRfk6#|#^>Cr}ځksH]``VF~zo~ }UBX-aUDw_ 5v[VvɪμMc#}fH|4oc\ѡKۤ;iE53dP~Q$%Fj'gKk [rP7%lec\:JL7Rf«Mz^WP2;GZet#?;/C{MV\;+Kw gr_|IJ?>+#AؔYsfedRB2Sb\?. 6 Ik[!ɺ;yPAcDWF-aHfVFLm#{ iz4M@ϽEf1A2S#N}G}@z#kGz1+#1 X sdꧽHi Y|@͜7!HZl0<^~Ĉ+A#F8=|@&Yu镈9 ͫͳ$35{)t"K׿Qk13>=|gW2m njǹ~E"j֭$3{Ȅbɢ1+# #zzGݳnol3~ +J$3H#wX}$wm1r1R ϼ ?|E!hH!KH/IӏtzÞ7!HZt0</I=y'k1b y^eK@ Λ\vބ iMA2@ydudf,#ZFX֘7 j iMTd&hIJ\b@$>ziS#1gl(,!HBdc.53d&Y54^z`m #wRȥ)([$qy#E̛$/HfrMLD9NZG[VtdMw1;iZx78MIx lvB ,$Hf~jVOj6ax`4Fg[Vvɪμzb@$wax5 ax F;/5+#I=}}jo%s&F ִ$ 1+#b ~m]i6d&#Fte6 R6#6sA"Hbnr}d A2@t#$b@$1G ax :tVDgFlH,W'$F}#DAҚd&hʈeƈі # 95aI@|=g$121RZx־$1G& ax_|E!1R#2bOZcgBBAҚL v%%F2T؜CUk[a#Hb iMd&ּydudVW y^eK@#HZ ax`qy#" 95q ၛ#L=Fr`?6/NɅъXD$1Hfb12/b@$$1G&A2S5F.Mݓ=puW>18r&l>P* D AsIk$31 4#F A#F AsIk$3iD}DD &X,=ukbˊ>Ftx]gm1#^no"FDAsIk\X}F;2 ߘ7a 1ǿ\>زMVu%h~H6#Hb iJь|Fo&Lޟ1 y|wR~O%I$ k+ɲBVtz4MGhʈ~%2^gVF3+#{b 95!8RlIV҂axARbbV*^!F 95Lz:)N殭0<Əѝte4H#2bsUWI$YH̤qMՓ4͛0 ( Fg>̟1DAҚLyGMz~ezɠ# X?+#c1EAҚVdb!W_5 NkGP#Sn}7-[/Ϳdjib@$1GV4u4Y+3lk ïY~zA<{9}p.É]Yƈ[myꍱ31GAA2Sc6YږG4 }M7.w-;+#,1QBA?d4_m G;zI^?Gl +FtPO`h*[' 95ALi4V1'ѝ7Q GÛG[G-DAsIkgg;F:rYx\ XI$ ;HgĈ2~#8I$J0P* $AsIk.NAsIkᣋax{9}pb121cy5x־$1G& abw-#2R?FjmSo5I$ #Hfb>o;zI^?Gl +FO+bUk[AsIk.##Ŗ0c܇eh*['p$D)Hfb>ei#ƪr~ $1G&A0|t1 ?bDWFr`?.NɅъXD$1G&A2au?&=_|Xl!Ffn"qVbn 95q 8 7MZxO=y\l׉4?b$l>P* #HZ aJ0<12oPn%F`#HZ ahj̛D}^Do4oz -+;dUg^;iٌE~r6, AsQ%d^JZ4e^V,mKݼI\ቑ#<$$l_c{d) arTM+_w^l #Fj'gKzڢ7_L@S\8^ɩ։xr}oW[.$3i֎,+0 Ta?2E@i:-˙3-O['(M`i| aRZ*d+4 ήu!I+_T/HYB@BsY!#yG$Oƪ6#2bs{F + ;HO-3 swU^d~ GW\4FWY 5̐&F.&Fdu&gzuu~x lvWMtՠcRZ¾q`c4YMŠ m1gKF&F>VHb.s%{&M7\m +$&i;^dtH!|ʈ@q-O1vF$1F0+-7 D5q/rҶ͛9 V wL@$1t؊}sq Abѥq_6oV nѐU N 7I$dx\"Fu<ss=1dϛ{%&ݷ~V#[X,[1ra"3[J@<$17;HrM)W@ /1gl(%jN҄p:[M HLLV.iϾ͡]QڼI%a#46%x|v+1 IMxCLN_iߐseX!: ?S𺓖F-wm=co4Ljo}Z`#5#F7 IMmw1q_ ax=}}j/tt#{  ̓$C+fbps GWM$H$ ?\Ї)׈$H}lsћy $N,euYfEdԄȕrMFwH65qv!HE:o"R5qyMt$ADwUa Ae";ku[$<#Vbҁ$ľїSs{ 0 Zc>[ ҄$ }z8`{!/6aa1>]%X!A|~*['*? čΛok:NmqaxbRݿ!y&B4h̛DdxbR AL{vI&L m5  ٭J%ATjH GB79]H=teΓx A; x&Aط; kθw$9: _?qNx2+"$zL74NWi0_'gsgXv@@ x8.ƇIENDB`swhid_core-0.1/doc/index.html000066400000000000000000000021061425407473400162530ustar00rootroot00000000000000 swhid_core

swhid_core

swhid_core is an OCaml library to work with persistent identifiers found in Software Heritage identifiers, also known as swhid.

swhid_core-0.1/doc/index.mld000066400000000000000000000003301425407473400160600ustar00rootroot00000000000000{0 swhid_core} {{:https://github.com/OCamlPro/swhid_core} swhid_core} is an {{:https://ocaml.org} OCaml} library to work with Software Heritage persistent identifiers (swhids). {1:api API} {!modules: Swhid_core } swhid_core-0.1/doc/style.css000066400000000000000000000003541425407473400161330ustar00rootroot00000000000000body { color: #444; background-color: #EEEEEE; line-height:1.6; font-size:18px; } .content { width: 80%; margin-left: 100px; margin-top: 100px; font-family: sans-serif; } a:link{ color:#5bf } a:visited{ color:#55f } swhid_core-0.1/dune-project000066400000000000000000000014041425407473400160330ustar00rootroot00000000000000(lang dune 1.11) (explicit_js_mode) (implicit_transitive_deps false) (name swhid_core) (license ISC) (authors "Léo Andrès , Dario Pinto ") (maintainers "Léo Andrès ") (source (github ocamlpro/swhid_core)) (generate_opam_files true) (package (name swhid_core) (synopsis "OCaml library to work with swhids.") (description "swhid_core is an OCaml library to with with Software Heritage persistent identifiers (swhids). This is the core library, for most use cases you should use the swhid library instead.") (tags (swhid_core swhid swh persistent identifiers hash software heritage archive)) (depends (ocaml (>= 4.03)) (dune (>= 1.11)) (odoc :with-doc))) swhid_core-0.1/example/000077500000000000000000000000001425407473400151455ustar00rootroot00000000000000swhid_core-0.1/example/dune000066400000000000000000000001021425407473400160140ustar00rootroot00000000000000(executable (name main) (modules main) (libraries swhid_core)) swhid_core-0.1/example/main.ml000066400000000000000000000003661425407473400164300ustar00rootroot00000000000000open Swhid_core let swhid = match Object.of_string "swh:1:cnt:bac494ecb6840e6b66f21aae7feb847b23f0745a" with | Error e -> Format.eprintf "error: %s@." e; exit 1 | Ok id -> Format.printf "%a is a valid swhid !@." Object.pp id swhid_core-0.1/src/000077500000000000000000000000001425407473400143015ustar00rootroot00000000000000swhid_core-0.1/src/compute.ml000066400000000000000000000226031425407473400163120ustar00rootroot00000000000000(* TODO: remove once we get >= 4.08 *) let result_is_error = function Ok _v -> false | Error _v -> true let result_get_ok = function Ok v -> v | Error _v -> invalid_arg "Result.get_ok" type directory_entry_kind = | File | Dir module Make (SHA1 : sig val digest_string_to_hex : string -> string end) (OS : sig val contents : string -> string list option val typ : string -> directory_entry_kind option val read_file : string -> string option val permissions : string -> int option val base : string -> string end) = struct module Git = struct let target_kind_to_git = function | Object.Kind.Content _hash_type -> "blob" | Directory -> "tree" | Release -> "tag" | Revision -> "commit" | Snapshot -> "refs" let id_to_bytes id = String.init (String.length id / 2) (fun i -> let s = String.sub id (2 * i) 2 in Char.chr @@ int_of_string @@ "0x" ^ s ) let object_to_swhid (obj : string) object_type = let scheme = Object.Scheme_version.default in let hash = SHA1.digest_string_to_hex obj in match Object.Hash.of_string hash with | Error _msg as e -> e | Ok hash -> let core_identifier = Object.Core_identifier.mk scheme object_type hash in Ok (Object.mk core_identifier []) let object_header fmt (git_type, len) = match git_type with | "blob" | "commit" | "extid" | "raw_extrinsic_metadata" | "snapshot" | "tag" | "tree" -> Format.fprintf fmt "%s %d\x00" git_type len | git_type -> invalid_arg (Format.sprintf "invalid git object type `%s` (Git.object_header)" git_type ) let object_from_contents_strtarget target_kind contents = let len = String.length contents in Format.asprintf "%a%s" object_header (target_kind, len) contents let object_from_contents target_kind contents = object_from_contents_strtarget (target_kind_to_git target_kind) contents (* TODO: remove once we have > 4.03 *) let string_split_on_char sep s = let r = ref [] in let j = ref (String.length s) in for i = String.length s - 1 downto 0 do if String.unsafe_get s i = sep then begin r := String.sub s (i + 1) (!j - i - 1) :: !r; j := i end done; String.sub s 0 !j :: !r let escape_newlines snippet = String.concat "\n " (string_split_on_char '\n' snippet) (* TODO: replace with Int.abs when we have >= 4.08 *) let abs x = if x >= 0 then x else -x let format_offset fmt (offset, negative_utc) = let sign = if offset < 0 || (offset = 0 && negative_utc) then "-" else "+" in let offset = abs offset in let hours = offset / 60 in let minutes = offset mod 60 in Format.fprintf fmt "%s%02d%02d" sign hours minutes let format_author_data fmt (author, date) = Format.fprintf fmt "%s" author; match date with | None -> () | Some (timestamp, tz_offset, negative_utc) -> Format.fprintf fmt " %Ld %a" timestamp format_offset (tz_offset, negative_utc) end type directory_entry = { typ : directory_entry_kind ; permissions : int ; name : string ; target : Object.Core_identifier.t } type date = { timestamp : Int64.t ; tz_offset : int ; negative_utc : bool } let content_identifier content = let typ = Object.Kind.Content "sha1_git" in let git_object = Git.object_from_contents typ content in Git.object_to_swhid git_object typ let directory_identifier entries = let entries = List.sort (fun entry1 entry2 -> String.compare (if entry1.typ = Dir then entry1.name ^ "/" else entry1.name) (if entry2.typ = Dir then entry2.name ^ "/" else entry2.name) ) entries in let content = Format.asprintf "%a" (Format.pp_print_list ~pp_sep:(fun _fmt () -> ()) (fun fmt entry -> Format.fprintf fmt "%o %s%c%s" entry.permissions entry.name '\x00' (Git.id_to_bytes ( Object.Hash.to_string @@ Object.Core_identifier.get_hash entry.target ) ) ) ) entries in let typ = Object.Kind.Directory in let git_object = Git.object_from_contents typ content in Git.object_to_swhid git_object typ (* TODO: remove once we have >= 4.05 *) let rec list_find_opt p = function | [] -> None | x :: l -> if p x then Some x else list_find_opt p l let rec directory_identifier_deep name = match OS.contents name with | None -> Error (Format.sprintf "can't get contents of `%s`" name) | Some contents -> ( let entries = List.map (fun name -> let typ = OS.typ name in let target = match typ with | Some File -> begin match OS.read_file name with | None -> Error (Format.sprintf "can't read file `%s`" name) | Some content -> content_identifier content end | Some Dir -> directory_identifier_deep name | None -> Error (Format.sprintf "can't get type of file `%s`" name) in let permissions = OS.permissions name in match (typ, permissions, target) with | Some typ, Some permissions, Ok target -> let name = OS.base name in let target = Object.get_core target in Ok { typ; permissions; target; name } | _ -> Error "can't compute directory deep identifier" ) contents in match list_find_opt result_is_error entries with | Some (Error _ as e) -> e | Some _ -> assert false | None -> directory_identifier (List.map result_get_ok entries) ) (* TODO: remove once we have >= 4.08 *) let option_map f = function None -> None | Some v -> Some (f v) let release_identifier target target_kind ~name ~author date ~message = let buff = Buffer.create 512 in let fmt = Format.formatter_of_buffer buff in Format.fprintf fmt "object %a%ctype %s%ctag %s%c" Object.Hash.pp target '\n' (Git.target_kind_to_git target_kind) '\n' (Git.escape_newlines name) '\n'; begin match author with | None -> () | Some author -> Format.fprintf fmt "tagger %a%c" Git.format_author_data ( Git.escape_newlines author , option_map (fun o -> (o.timestamp, o.tz_offset, o.negative_utc)) date ) '\n' end; begin match message with | None -> () | Some message -> Format.fprintf fmt "%c%s" '\n' message end; Format.pp_print_flush fmt (); let content = Buffer.contents buff in let typ = Object.Kind.Release in let git_object = Git.object_from_contents typ content in Git.object_to_swhid git_object typ let revision_identifier directory parents ~author ~author_date ~committer ~committer_date extra_headers ~message = let buff = Buffer.create 512 in let fmt = Format.formatter_of_buffer buff in Format.fprintf fmt "tree %a%c" Object.Hash.pp directory '\n'; List.iter (fun parent -> Format.fprintf fmt "parent %a%c" Object.Hash.pp parent '\n') parents; Format.fprintf fmt "author %a%c" Git.format_author_data ( Git.escape_newlines author , option_map (fun o -> (o.timestamp, o.tz_offset, o.negative_utc)) author_date ) '\n'; Format.fprintf fmt "committer %a%c" Git.format_author_data ( Git.escape_newlines committer , option_map (fun o -> (o.timestamp, o.tz_offset, o.negative_utc)) committer_date ) '\n'; Array.iter (fun (k, v) -> Format.fprintf fmt "%s %s%c" k (Git.escape_newlines v) '\n') extra_headers; begin match message with | None -> () | Some message -> Format.fprintf fmt "%c%s" '\n' message end; Format.pp_print_flush fmt (); let content = Buffer.contents buff in let typ = Object.Kind.Revision in let git_object = Git.object_from_contents typ content in Git.object_to_swhid git_object typ let snapshot_identifier (branches : (string * (string * string) option) list) = let branches = List.sort (fun (name1, _target) (name2, _target) -> String.compare name1 name2) branches in let buff = Buffer.create 512 in let fmt = Format.formatter_of_buffer buff in List.iter (fun (branch_name, target) -> let target, target_kind, target_id_len = match target with | None -> ("", "dangling", 0) | Some (target, target_kind) -> ( match target_kind with | "content" | "directory" | "revision" | "release" | "snapshot" -> (Git.id_to_bytes target, target_kind, 20) | "alias" -> (target, "alias", String.length target) | target_kind -> invalid_arg (Format.sprintf "invalid target type: `%s` (Compute.snapshot_identifier)" target_kind ) ) in Format.fprintf fmt "%s %s%c%d:%s" target_kind branch_name '\x00' target_id_len target ) branches; Format.pp_print_flush fmt (); let content = Buffer.contents buff in let git_object = Git.object_from_contents_strtarget "snapshot" content in Git.object_to_swhid git_object Object.Kind.Snapshot end swhid_core-0.1/src/compute.mli000066400000000000000000000114701425407473400164630ustar00rootroot00000000000000(** Module to compute a swhid for all kinds of objects. *) (** The type of directory entries. *) type directory_entry_kind = | File | Dir module Make (SHA1 : sig (** A module containing the needed hash functions. *) (** [digest_string_to_hex s] computes the SHA1 hash of [s] and returns its hexadecimal representation. *) val digest_string_to_hex : string -> string end) (OS : sig (** A module containing the needed OS-related functions. *) (** [contents dir] returns the list of files in the directory [dir]. *) val contents : string -> string list option (** [type file] returns [Dir] if [file] is a directory and [File] otherwise. *) val typ : string -> directory_entry_kind option (** [read_file f] returns the content of the file [f]. *) val read_file : string -> string option (** [permissions f] returns the 16-bit file mode (as stored by Git) of the file [f]. That is: - [0o120000] if [f] is a symlink - [0o040000] if [f] is a directory - [0o100755] if [f] is an executable file - [0o100644] if [f] is a regular file *) val permissions : string -> int option (** [base f] is the basename of file [f]. *) val base : string -> string end) : sig (** A functor that, given a SHA1 module and a OS module, provides various functions to compute the swhid of a given object. Supported objects are [content], [directory], [release], [revision] and [snapshot]. The origins and visits objects are not supported. *) (** The type for directory entries list, needed to compute directories identifiers. *) type directory_entry = { typ : directory_entry_kind ; permissions : int ; name : string ; target : Object.Core_identifier.t } (** The type for dates, needed to compute releases and revisions identifiers. *) type date = { timestamp : Int64.t ; tz_offset : int ; negative_utc : bool } (** [content_identifier s] computes the swhid for the [s] content. [s] is the raw content of a file as a [string]. E.g. [content_identifier "_build\n"] is the swhid of this library's [.gitignore] file. *) val content_identifier : string -> (Object.t, string) result (** [directory_identifier entries] compute the swhid for the [entries] directory. [entries] is a list of [Kinds.directory_entry] where each element points to another object (usually a file content or a sub-directory). E.g. [directory_identifier \[ { typ = "file" ; permissions = 33188 ; name = "README" ; target = "37ec8ea2110c0b7a32fbb0e872f6e7debbf95e21" }\]] is the swhid of a directory which has a single file [README] with permissions 33188 and whose core identifier from [content_identifier] is [37ec8ea2110c0b7a32fbb0e872f6e7debbf95e21]. *) val directory_identifier : directory_entry list -> (Object.t, string) result (** [directory_identifier_deep] compute the swhid for a given directory name, it uses the various functions provided in the [OS] module parameter to list directory contents, get file permissions and read file contents.*) val directory_identifier_deep : string -> (Object.t, string) result (** [release_identifier target target_kind name ~author date ~message] computes the swhid for a release object pointing to an object of type [target_kind] whose identifier is [target], the release having [~name], [~author] and has been published on [date] with the release [~message]. *) val release_identifier : Object.Hash.t -> Object.Kind.t -> name:string -> author:string option -> date option -> message:string option -> (Object.t, string) result (** [revision dir parents ~author ~author_date ~committer ~committer_date extra_headers message] computes the swhid for a revision object whose directory has id [dir] and whose parents has ids [parents] which was authored by [~author] on [~author_date] and committed by [~committer] on [~committer_date] with extra headers [extra_headers] and message [message]. *) val revision_identifier : Object.Hash.t -> Object.Hash.t list -> author:string -> author_date:date option -> committer:string -> committer_date:date option -> (string * string) array -> message:string option -> (Object.t, string) result (** [snapshot_identifier branches] computes the swhid of the snapshot made of branches [branches] where [branches] is a list of branch elements. Each branch is of the form [name, target] where [name] is the name of the branch and where [target] is a pair made of the identifier of the branch and its type. *) val snapshot_identifier : (string * (string * string) option) list -> (Object.t, string) result end swhid_core-0.1/src/dune000066400000000000000000000000761425407473400151620ustar00rootroot00000000000000(library (public_name swhid_core) (modules compute object)) swhid_core-0.1/src/object.ml000066400000000000000000000155401425407473400161060ustar00rootroot00000000000000(* TODO: remove once we have >= 4.08 *) let result_is_error = function Ok _v -> false | Error _v -> true let result_get_ok = function Ok v -> v | Error _v -> invalid_arg "Result.get_ok" module Scheme_version = struct type t = int let of_string = function | "1" -> Ok 1 | invalid -> Error (Format.sprintf "invalid scheme version `%s`" invalid) let of_int = function | 1 -> Ok 1 | invalid -> Error (Format.sprintf "invalid scheme version `%d`" invalid) let to_int x = x let pp fmt v = Format.fprintf fmt "%d" v let default = 1 end module Kind = struct type t = | Content of string | Directory | Revision | Release | Snapshot let compare t t' = match (t, t') with | Directory, Directory | Release, Release | Revision, Revision | Snapshot, Snapshot -> 0 | Content c, Content c' -> String.compare c c' | Content _, _ -> 1 | _, Content _ -> -1 | Directory, _ -> 1 | _, Directory -> -1 | Revision, _ -> 1 | _, Revision -> -1 | Release, _ -> 1 | _, Release -> -1 let equal t t' = compare t t' = 0 let of_string = function | "cnt" -> Ok (Content "sha1_git") | "dir" -> Ok Directory | "rel" -> Ok Release | "rev" -> Ok Revision | "snp" -> Ok Snapshot | invalid -> Error (Format.sprintf "invalid object kind `%s`" invalid) let to_string = function | Content _f -> "cnt" | Directory -> "dir" | Release -> "rel" | Revision -> "rev" | Snapshot -> "snp" let pp fmt v = Format.fprintf fmt "%s" (to_string v) end module Hash = struct type t = string let compare = String.compare let equal = String.equal let of_string s = let len = ref 0 in try String.iter (function | 'a' .. 'f' | '0' .. '9' -> incr len | _invalid_char -> raise Exit ) s; if !len = 40 then Ok s else raise Exit with Exit -> Error (Format.sprintf "invalid object hash `%s`" s) let to_string v = v let pp fmt v = Format.fprintf fmt "%s" v end (* TODO: remove once we have > 4.03 *) let string_split_on_char sep s = let r = ref [] in let j = ref (String.length s) in for i = String.length s - 1 downto 0 do if String.unsafe_get s i = sep then begin r := String.sub s (i + 1) (!j - i - 1) :: !r; j := i end done; String.sub s 0 !j :: !r module Core_identifier = struct type t = Scheme_version.t * Kind.t * Hash.t let compare (sch_version, object_type, hash) (sch_version', object_type', hash') = let scheme_version = sch_version - sch_version' in if scheme_version <> 0 then scheme_version else let object_type = Kind.compare object_type object_type' in if object_type <> 0 then object_type else Hash.compare hash hash' let equal t t' = compare t t' = 0 let of_string s = match string_split_on_char ':' s with | [ "swh"; "1"; t; hash ] -> begin match Kind.of_string t with | Error _msg as e -> e | Ok t -> begin match Hash.of_string hash with | Error _msg as e -> e | Ok hash -> let scheme = Scheme_version.default in Ok (scheme, t, hash) end end | _whatever -> Error "invalid core identifier" let mk scheme typ hash = (scheme, typ, hash) let pp fmt (scheme, typ, hash) = Format.fprintf fmt "swh:%a:%a:%a" Scheme_version.pp scheme Kind.pp typ Hash.pp hash let to_string v = Format.asprintf "%a" pp v let get_scheme (scheme, _kind, _hash) = scheme let get_kind (_scheme, kind, _hash) = kind let get_hash (_scheme, _kind, hash) = hash end module Qualifier = struct type t = | Anchor of Core_identifier.t | Origin of string | Path of string | Visit of Core_identifier.t | Fragment of (int * int option) let int_of_string_opt s = try Some (int_of_string s) with Failure _ -> None let of_string s = match string_split_on_char '=' s with | "lines" :: lines -> begin match string_split_on_char '-' (String.concat "" lines) with | [ l1 ] -> begin match int_of_string_opt l1 with | None -> Error "invalid qualifier" | Some i -> Ok (Fragment (i, None)) end | [ l1; l2 ] -> begin match (int_of_string_opt l1, int_of_string_opt l2) with | Some i1, Some i2 -> Ok (Fragment (i1, Some i2)) | _, _ -> Error "invalid qualifier" end | _whatever -> Error "invalid qualifier" end | "path" :: path -> (* TODO: check RFC 3987 IRI compliance *) let path = String.concat "" path in Ok (Path path) | "origin" :: url -> (* TODO: check RFC 3987 absolute path compliance *) let url = String.concat "" url in Ok (Origin url) | "visit" :: id -> ( let id = String.concat "" id in match Core_identifier.of_string id with | Error _msg as e -> e | Ok id -> Ok (Visit id) ) | "anchor" :: id -> ( let id = String.concat "" id in match Core_identifier.of_string id with | Error _msg as e -> e | Ok id -> Ok (Anchor id) ) | _whatever -> Error "invalid qualifier" let pp fmt = function | Anchor id -> Format.fprintf fmt "anchor=%a" Core_identifier.pp id | Origin uri -> Format.fprintf fmt "origin=%s" uri | Path path -> Format.fprintf fmt "path=%s" path | Visit id -> Format.fprintf fmt "visit=%a" Core_identifier.pp id | Fragment (l1, l2) -> ( Format.fprintf fmt "lines=%d" l1; match l2 with None -> () | Some l2 -> Format.fprintf fmt "-%d" l2 ) let to_string q = Format.asprintf "%a" pp q end type t = Core_identifier.t * Qualifier.t list (* TODO: remove once we have >= 4.05 *) let rec list_find_opt p = function | [] -> None | x :: l -> if p x then Some x else list_find_opt p l let of_string s = match string_split_on_char ';' s with | id :: qualifiers -> begin match Core_identifier.of_string id with | Error _msg as e -> e | Ok object_core_identifier -> begin let qualifiers = List.map Qualifier.of_string qualifiers in match list_find_opt result_is_error qualifiers with | Some (Error _msg as e) -> e | Some _ -> assert false | None -> let qualifiers = List.map result_get_ok qualifiers in Ok (object_core_identifier, qualifiers) end end | _whatever -> Error "invalid swhid" let mk object_core_identifier qualifiers = (object_core_identifier, qualifiers) let get_core (core, _qualifiers) = core let get_scheme (core, _qualifiers) = Core_identifier.get_scheme core let get_kind (core, _qualifiers) = Core_identifier.get_kind core let get_hash (core, _qualifiers) = Core_identifier.get_hash core let get_qualifiers (_core, qualifiers) = qualifiers let pp_qualifiers fmt q = List.iter (Format.fprintf fmt ";%a" Qualifier.pp) q let pp fmt id = let i = get_core id in let q = get_qualifiers id in Format.fprintf fmt "%a%a" Core_identifier.pp i pp_qualifiers q let to_string id = Format.asprintf "%a" pp id swhid_core-0.1/src/object.mli000066400000000000000000000142661425407473400162630ustar00rootroot00000000000000(** This module contains the various types used to represent a Software Heritage persistent identifier (swhid). These identifiers are documented on {{:https://docs.softwareheritage.org/devel/swh-model/persistent-identifiers.html} Software Heritage}. In short, a swhid is made of a mandatory core identifier (a scheme version, a kind and a hash) and an optional list of qualifiers. *) module Scheme_version : sig (** Module to work with the scheme version of a swhid.*) (** The type of scheme versions. *) type t (** [of_string s] is [Ok v] if [s] is a valid scheme version, otherwise it is [Error e]. *) val of_string : string -> (t, string) result (** [of_int n] is [Ok v] if [n] is a valid scheme version, otherwise it is [Error e]. *) val of_int : int -> (t, string) result (** [to_int v] is the representation of [v] as an integer. *) val to_int : t -> int (** [pp fmt v] prints [v] on formatter [fmt]. *) val pp : Format.formatter -> t -> unit (** [default] is the default scheme version. *) val default : t end module Kind : sig (** Module to work with the different kinds of software artifacts a swhid can points to. They're documented {{:https://docs.softwareheritage.org/devel/swh-model/data-model.html#software-artifacts} here}. *) (** The type of the different kinds of software artifacts. *) type t = | Content of string (** Contents (AKA "blobs"). the string parameter is the name of the hash function used for the computation, defaults to ["sha1_git"] and in most use cases you don't care about it. *) | Directory (** Directories. *) | Revision (** Revisions. *) | Release (** Releases. *) | Snapshot (** Snapshots. *) (** [compare x y] returns [0] if [x] is equal to [y], a negative integer if [x] is less than [y], and a positive integer if [x] is greater than [y]. *) val compare : t -> t -> int (** [equal x y] returns [true] iff [x] is equal to [y]. *) val equal : t -> t -> bool (** [of_string s] is [Ok v] if [s] is a valid kind of object, otherwise it is [Error e]. The valid kinds are ["cnt"], ["dir"], ["rel"], ["rev"] and ["snp"]. *) val of_string : string -> (t, string) result (** [pp fmt v] prints [v] on formatter [fmt]. *) val pp : Format.formatter -> t -> unit (** [to_string v] is the representation of [v] as a string. *) val to_string : t -> string end module Hash : sig (** Module to work with the hash component of a swhid. *) (** The type of hashes. *) type t (** [compare x y] returns [0] if [x] is equal to [y], a negative integer if [x] is less than [y], and a positive integer if [x] is greater than [y]. *) val compare : t -> t -> int (** [equal x y] returns [true] iff [x] is equal to [y]. *) val equal : t -> t -> bool (** [of_string s] is [Ok v] if [s] is a valid hash, otherwise it is [Error e]. A hash is valid if it's made of 40 hexadecimal characters. *) val of_string : string -> (t, string) result (** [pp fmt v] prints [v] on formatter [fmt]. *) val pp : Format.formatter -> t -> unit (** [to_string v] is the representation of [v] as a string. *) val to_string : t -> string end module Core_identifier : sig (** Module to work with the core identifier of a swhid. The core identifier of a swhid is made of its scheme version, its kind and its hash. They're documented {{:https://docs.softwareheritage.org/devel/swh-model/persistent-identifiers.html#core-identifiers} here}. *) (** The type of core identifiers. *) type t (** [compare x y] returns [0] if [x] is equal to [y], a negative integer if [x] is less than [y], and a positive integer if [x] is greater than [y]. *) val compare : t -> t -> int (** [equal x y] returns [true] iff [x] is equal to [y]. *) val equal : t -> t -> bool (** [of_string s] is [Ok v] if [s] is a valid core identifier, otherwise it is [Error e]. *) val of_string : string -> (t, string) result (** [mk scheme kind hash] is the core identifier made of the scheme version [scheme], the kind [kind] and the hash [hash]. *) val mk : Scheme_version.t -> Kind.t -> Hash.t -> t (** [pp fmt v] prints [v] on formatter [fmt]. *) val pp : Format.formatter -> t -> unit (** [to_string v] is the representation of [v] as a string. *) val to_string : t -> string (** [get_scheme v] is the scheme version of [v]. *) val get_scheme : t -> Scheme_version.t (** [get_kind v] is the kind of [v] *) val get_kind : t -> Kind.t (** [get_hash v] is the hash of [v]. *) val get_hash : t -> Hash.t end module Qualifier : sig (** Module to work with qualifiers. They are documented {{:https://docs.softwareheritage.org/devel/swh-model/persistent-identifiers.html#qualifiers} here}. *) (** The type of qualifiers. *) type t = | Anchor of Core_identifier.t | Origin of string | Path of string | Visit of Core_identifier.t | Fragment of (int * int option) (** [of_string s] is [Ok v] if [s] is a valid qualifier, otherwise it is [Error e]. *) val of_string : string -> (t, string) result (** [pp fmt v] prints [v] on formatter [fmt]. *) val pp : Format.formatter -> t -> unit (** [to_string v] is the representation of [v] as a string. *) val to_string : t -> string end (** The type of swhids. *) type t (** [of_string s] is [Ok v] if [s] is a valid swhid, otherwise it is [Error e]. *) val of_string : string -> (t, string) result (** [mk c q] is the swhid made of the core identifier [c] and the list of qualifiers [q]. *) val mk : Core_identifier.t -> Qualifier.t list -> t (** [get_core v] is the core identifier of [v]. *) val get_core : t -> Core_identifier.t (** [get_scheme v] is the scheme of [v]. *) val get_scheme : t -> Scheme_version.t (** [get_kind v] is the kind of [v]. *) val get_kind : t -> Kind.t (** [get_hash v] is the hash of [v]. *) val get_hash : t -> Hash.t (** [get_qualifiers v] is the list of qualifiers of [v]*) val get_qualifiers : t -> Qualifier.t list (** [pp fmt v] prints [v] on formatter [fmt]. *) val pp : Format.formatter -> t -> unit (** [to_string v] is the representation of [v] as a string. *) val to_string : t -> string swhid_core-0.1/swhid_core.opam000066400000000000000000000015751425407473400165260ustar00rootroot00000000000000# This file is generated by dune, edit dune-project instead opam-version: "2.0" synopsis: "OCaml library to work with swhids." description: "swhid_core is an OCaml library to with with Software Heritage persistent identifiers (swhids). This is the core library, for most use cases you should use the swhid library instead." maintainer: ["Léo Andrès "] authors: [ "Léo Andrès , Dario Pinto " ] license: "ISC" homepage: "https://github.com/ocamlpro/swhid_core" bug-reports: "https://github.com/ocamlpro/swhid_core/issues" depends: [ "ocaml" {>= "4.03"} "dune" {>= "1.11"} "odoc" {with-doc} ] build: [ ["dune" "subst"] {pinned} [ "dune" "build" "-p" name "-j" jobs "@install" "@runtest" {with-test} "@doc" {with-doc} ] ] dev-repo: "git+https://github.com/ocamlpro/swhid_core.git" swhid_core-0.1/test/000077500000000000000000000000001425407473400144715ustar00rootroot00000000000000swhid_core-0.1/test/dune000066400000000000000000000000741425407473400153500ustar00rootroot00000000000000(test (name test) (modules test) (libraries swhid_core)) swhid_core-0.1/test/test.ml000066400000000000000000000004171425407473400160040ustar00rootroot00000000000000open Swhid_core let () = match Object.of_string "swh:1:cnt:bac494ecb6840e6b66f21aae7feb847b23f0745a" with | Ok v -> let s = Object.to_string v in assert (String.equal "swh:1:cnt:bac494ecb6840e6b66f21aae7feb847b23f0745a" s) | Error _e -> assert false