forked from
ptr.pet/hydrant
kind of like tap but different and in rust
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}