1- use std:: { pin :: Pin , str :: FromStr } ;
1+ use std:: { io , path :: PathBuf , pin :: Pin } ;
22
33use anyhow:: Result ;
44use async_trait:: async_trait;
5- use bao_tree:: io:: BaoContentItem ;
65use bytes:: Bytes ;
6+ use cid:: Cid ;
77use cid_router_core:: {
88 cid_filter:: { CidFilter , CodeFilter } ,
9- crp:: { Crp , CrpCapabilities , ProviderType , RouteResolver } ,
9+ crp:: { BlobWriter , Crp , CrpCapabilities , ProviderType , RouteResolver } ,
1010 routes:: Route ,
1111 Context ,
1212} ;
13- use futures:: { Stream , StreamExt } ;
14- use iroh :: { Endpoint , EndpointAddr , EndpointId } ;
15- use iroh_blobs :: { get :: request :: GetBlobItem , ticket :: BlobTicket , Hash } ;
13+ use futures:: Stream ;
14+ use iroh_blobs :: Hash ;
15+ use log :: info ;
1616use serde:: { Deserialize , Serialize } ;
17- use serde_json:: Value ;
1817
19- #[ derive( Debug ) ]
18+ #[ derive( Debug , Clone ) ]
2019pub struct IrohCrp {
21- addr : EndpointAddr ,
22- endpoint : Endpoint ,
20+ store : iroh_blobs:: store:: fs:: FsStore ,
2321}
2422
2523#[ derive( Debug , Clone , Serialize , Deserialize ) ]
2624pub struct IrohCrpConfig {
27- pub node_addr_ref : IrohNodeAddrRef ,
28- }
29-
30- #[ derive( Debug , Clone , Serialize , Deserialize ) ]
31- #[ serde( rename_all = "snake_case" ) ]
32- pub enum IrohNodeAddrRef {
33- EndpointId ( String ) ,
34- EndpointTicket ( String ) ,
35- Ticket ( String ) ,
25+ /// Path to the directory where blobs are stored
26+ pub path : PathBuf ,
3627}
3728
3829impl IrohCrp {
39- pub async fn new_from_config ( config : Value ) -> Result < Self > {
40- let IrohCrpConfig { node_addr_ref } = serde_json:: from_value ( config) ?;
41-
42- let endpoint_addr = match node_addr_ref {
43- IrohNodeAddrRef :: EndpointId ( node_id) => {
44- let endpoint_id = EndpointId :: from_str ( & node_id) ?;
45- EndpointAddr :: from ( endpoint_id)
46- }
47- IrohNodeAddrRef :: EndpointTicket ( ticket) => {
48- let ticket = iroh_tickets:: endpoint:: EndpointTicket :: from_str ( & ticket) ?;
49- ticket. endpoint_addr ( ) . to_owned ( )
50- }
51- IrohNodeAddrRef :: Ticket ( ticket) => {
52- let ticket = BlobTicket :: from_str ( & ticket) ?;
53- ticket. addr ( ) . clone ( )
54- }
30+ pub async fn new_from_config ( config : IrohCrpConfig ) -> io:: Result < Self > {
31+ let path = if config. path . is_absolute ( ) {
32+ config. path
33+ } else {
34+ std:: env:: current_dir ( ) ?. join ( config. path )
5535 } ;
56-
57- let endpoint = Endpoint :: bind ( ) . await ?;
58-
59- Ok ( Self {
60- addr : endpoint_addr,
61- endpoint,
62- } )
36+ let store = iroh_blobs:: store:: fs:: FsStore :: load ( path)
37+ . await
38+ . map_err ( |e| io:: Error :: other ( e) ) ?;
39+ Ok ( Self { store } )
6340 }
6441}
6542
@@ -75,13 +52,13 @@ impl Crp for IrohCrp {
7552
7653 async fn reindex ( & self , _cx : & Context ) -> anyhow:: Result < ( ) > {
7754 // TODO: Implement reindexing logic
78- todo ! ( ) ;
55+ Ok ( ( ) )
7956 }
8057
8158 fn capabilities < ' a > ( & ' a self ) -> CrpCapabilities < ' a > {
8259 CrpCapabilities {
8360 route_resolver : Some ( self ) ,
84- size_resolver : None , // TODO
61+ blob_writer : Some ( self ) ,
8562 }
8663 }
8764
@@ -90,6 +67,24 @@ impl Crp for IrohCrp {
9067 }
9168}
9269
70+ #[ async_trait]
71+ impl BlobWriter for IrohCrp {
72+ async fn put_blob (
73+ & self ,
74+ _auth : Option < Bytes > ,
75+ cid : & Cid ,
76+ data : & [ u8 ] ,
77+ ) -> Result < ( ) , Box < dyn std:: error:: Error + Send + Sync > > {
78+ let blobs = self . store . blobs ( ) . clone ( ) ;
79+ let data = Bytes :: copy_from_slice ( data) ;
80+ if cid. hash ( ) . code ( ) != 0x1e {
81+ return Err ( "Unsupported CID hash code; only blake3 is supported" . into ( ) ) ;
82+ }
83+ blobs. add_bytes ( data) . with_tag ( ) . await . map_err ( Box :: new) ?;
84+ Ok ( ( ) )
85+ }
86+ }
87+
9388#[ async_trait]
9489impl RouteResolver for IrohCrp {
9590 async fn get_bytes (
@@ -105,39 +100,13 @@ impl RouteResolver for IrohCrp {
105100 > ,
106101 Box < dyn std:: error:: Error + Send + Sync > ,
107102 > {
108- let Self { addr , .. } = self ;
103+ info ! ( "get_bytes for route: {:?}" , route ) ;
109104 let cid = route. cid ;
110-
111105 let hash = cid. hash ( ) . digest ( ) ;
112106 let hash: [ u8 ; 32 ] = hash. try_into ( ) ?;
113107 let hash = Hash :: from_bytes ( hash) ;
114-
115- let conn = self
116- . endpoint
117- . connect ( addr. clone ( ) , iroh_blobs:: ALPN )
118- . await ?;
119-
120- println ! ( "get {:?} from {}" , hash, addr. id. fmt_short( ) ) ;
121-
122- let res = iroh_blobs:: get:: request:: get_blob ( conn, hash) ;
123- let res = res
124- . take_while ( |item| n0_future:: future:: ready ( !matches ! ( item, GetBlobItem :: Done ( _) ) ) )
125- . filter_map ( |item| {
126- n0_future:: future:: ready ( match item {
127- GetBlobItem :: Item ( item) => match item {
128- BaoContentItem :: Leaf ( leaf) => Some ( Ok ( leaf. data ) ) ,
129- // TODO - I don't think this is right. returning None here
130- // will likely end the stream prematurely
131- BaoContentItem :: Parent ( _parent) => None ,
132- } ,
133- // This is filtered out, only for compiler happiness
134- GetBlobItem :: Done ( _stats) => None ,
135- GetBlobItem :: Error ( err) => Some ( Err (
136- Box :: new ( err) as Box < dyn std:: error:: Error + Send + Sync >
137- ) ) ,
138- } )
139- } ) ;
140-
141- Ok ( Box :: pin ( res) )
108+ let data = self . store . blobs ( ) . get_bytes ( hash) . await . map_err ( Box :: new) ?;
109+ let stream = futures:: stream:: once ( async move { Ok ( data) } ) ;
110+ Ok ( Box :: pin ( stream) )
142111 }
143112}
0 commit comments