kind of like tap but different and in rust
at main 104 lines 3.3 kB view raw
1use std::env; 2use std::sync::Arc; 3use std::time::Duration; 4 5use hydrant::resolver::Resolver; 6use jacquard::IntoStatic; 7use jacquard::api::com_atproto::sync::get_repo::GetRepo; 8use jacquard::prelude::XrpcExt; 9use jacquard::types::did::Did; 10use jacquard_common::types::ident::AtIdentifier; 11use jacquard_repo::MemoryBlockStore; 12use jacquard_repo::mst::Mst; 13use miette::{IntoDiagnostic, Result}; 14use tracing::{Level, info}; 15use tracing_subscriber::FmtSubscriber; 16use url::Url; 17 18#[tokio::main] 19async fn main() -> Result<()> { 20 // Setup logging 21 let subscriber = FmtSubscriber::builder() 22 .with_max_level(Level::INFO) 23 .finish(); 24 tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); 25 26 // Parse args 27 let args: Vec<String> = env::args().collect(); 28 if args.len() != 2 { 29 eprintln!("Usage: {} <handle|did>", args[0]); 30 std::process::exit(1); 31 } 32 let identifier_str = &args[1]; 33 34 // Init resolver 35 let plc_url = Url::parse("https://plc.directory").into_diagnostic()?; 36 let resolver = Resolver::new(vec![plc_url], 100); 37 38 // Resolve identity 39 info!("Resolving {}...", identifier_str); 40 let identifier = if identifier_str.starts_with("did:") { 41 AtIdentifier::Did(Did::new(identifier_str).map_err(|e| miette::miette!("{}", e))?) 42 } else { 43 AtIdentifier::Handle(identifier_str.parse().into_diagnostic()?) 44 }; 45 46 let did = match identifier { 47 AtIdentifier::Did(d) => d.into_static(), 48 AtIdentifier::Handle(h) => { 49 let d = resolver.resolve_did(&AtIdentifier::Handle(h)).await?; 50 d 51 } 52 }; 53 info!("Resolved to DID: {}", did); 54 55 let (pds_url, _) = resolver.resolve_identity_info(&did).await?; 56 info!("PDS URL: {}", pds_url); 57 58 // Fetch repo 59 info!("Fetching repo..."); 60 let http = reqwest::Client::builder() 61 .timeout(Duration::from_secs(30)) 62 .build() 63 .into_diagnostic()?; 64 65 let req = GetRepo::new().did(did.clone()).build(); 66 let resp = http.xrpc(pds_url).send(&req).await?; 67 let car_bytes = resp.into_output().map_err(|e| miette::miette!("{}", e))?; // explicit map_err 68 69 info!("Fetched {} bytes", car_bytes.body.len()); 70 71 // Parse CAR 72 let parsed = jacquard_repo::car::reader::parse_car_bytes(&car_bytes.body) 73 .await 74 .into_diagnostic()?; 75 76 let store = Arc::new(MemoryBlockStore::new()); 77 for (_cid, bytes) in &parsed.blocks { 78 jacquard_repo::BlockStore::put(store.as_ref(), bytes) 79 .await 80 .into_diagnostic()?; 81 } 82 83 // Load MST 84 let root_bytes = parsed 85 .blocks 86 .get(&parsed.root) 87 .ok_or_else(|| miette::miette!("root block missing from CAR"))?; 88 89 let root_commit = jacquard_repo::commit::Commit::from_cbor(root_bytes).into_diagnostic()?; 90 info!("Repo rev: {}", root_commit.rev); 91 92 let mst: Mst<MemoryBlockStore> = Mst::load(store.clone(), root_commit.data, None); 93 let leaves = mst.leaves().await.into_diagnostic()?; 94 let root = mst.root().await.into_diagnostic()?; 95 96 info!("Found {} records", leaves.len()); 97 98 println!("root -> {}", root); 99 for (key, cid) in leaves { 100 println!("{} -> {}", key, cid); 101 } 102 103 Ok(()) 104}