Something went wrong. Try again.
very fast at protocol indexer with flexible filtering, xrpc queries, cursor-backed event stream, and more, built on fjall
rust fjall at-protocol atproto indexer
Something went wrong. Try again.
Rust
at fix-counts
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437use data_encoding::BASE32_NOPAD;use fjall::UserKey;use jacquard_common::types::crypto::{PublicKey, encode_uvarint};use jacquard_common::types::string::Did;use jacquard_common::types::tid::Tid;use jacquard_common::{CowStr, IntoStatic};use miette::{Context, IntoDiagnostic};use serde::{Deserialize, Deserializer, Serialize, Serializer};use smol_str::{SmolStr, SmolStrBuilder, format_smolstr};use std::borrow::Cow;use std::fmt::Display;
const S32_CHAR: &str = "234567abcdefghijklmnopqrstuvwxyz";
#[derive(Clone, Debug, PartialEq, Eq)]pub enum TrimmedDid<'s> { Plc([u8; 15]), Web(CowStr<'s>), Other(CowStr<'s>),}
const TAG_PLC: u8 = 1;const TAG_WEB: u8 = 2;
impl<'s> TrimmedDid<'s> { pub fn len(&self) -> usize { match self { TrimmedDid::Plc(_) => 16, TrimmedDid::Web(s) => 1 + s.len(), TrimmedDid::Other(s) => s.len(), } }
pub fn into_static(self) -> TrimmedDid<'static> { match self { TrimmedDid::Plc(bytes) => TrimmedDid::Plc(bytes), TrimmedDid::Web(s) => TrimmedDid::Web(s.into_static()), TrimmedDid::Other(s) => TrimmedDid::Other(s.into_static()), } }
pub fn to_did(&self) -> Did<'static> { match self { TrimmedDid::Plc(_) => { let s = self.to_string(); Did::new_owned(format_smolstr!("did:{}", s)).expect("valid did from plc") } TrimmedDid::Web(s) => { Did::new_owned(format_smolstr!("did:web:{}", s)).expect("valid did from web") } TrimmedDid::Other(s) => { Did::new_owned(format_smolstr!("did:{}", s)).expect("valid did from other") } } }
pub fn write_to_vec(&self, buf: &mut Vec<u8>) { match self { TrimmedDid::Plc(bytes) => { buf.push(TAG_PLC); buf.extend_from_slice(bytes); } TrimmedDid::Web(s) => { buf.push(TAG_WEB); buf.extend_from_slice(s.as_bytes()); } TrimmedDid::Other(s) => buf.extend_from_slice(s.as_bytes()), } }}
impl Display for TrimmedDid<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { TrimmedDid::Plc(bytes) => { f.write_str("plc:")?; f.write_str(&BASE32_NOPAD.encode(bytes).to_ascii_lowercase()) } TrimmedDid::Web(s) => { f.write_str("web:")?; f.write_str(s) } TrimmedDid::Other(s) => f.write_str(s), } }}
impl<'a> From<&'a Did<'a>> for TrimmedDid<'a> { fn from(did: &'a Did<'a>) -> Self { let s = did.as_str(); if let Some(rest) = s.strip_prefix("did:plc:") { if rest.len() == 24 { // decode if let Ok(bytes) = BASE32_NOPAD.decode(rest.to_ascii_uppercase().as_bytes()) && bytes.len() == 15 { return TrimmedDid::Plc(bytes.try_into().unwrap()); } } } else if let Some(rest) = s.strip_prefix("did:web:") { return TrimmedDid::Web(CowStr::Borrowed(rest)); } TrimmedDid::Other(CowStr::Borrowed(s.trim_start_matches("did:"))) }}
impl<'a> TryFrom<&'a [u8]> for TrimmedDid<'a> { type Error = miette::Report;
fn try_from(value: &'a [u8]) -> miette::Result<Self> { if value.is_empty() { miette::bail!("empty did key"); } match value[0] { TAG_PLC => { if value.len() == 16 { let mut arr = [0u8; 15]; arr.copy_from_slice(&value[1..]); return Ok(TrimmedDid::Plc(arr)); } miette::bail!("invalid length for tagged plc did"); } TAG_WEB => { if let Ok(s) = std::str::from_utf8(&value[1..]) { return Ok(TrimmedDid::Web(CowStr::Borrowed(s))); } miette::bail!("invalid utf8 for tagged web did"); } _ => { if let Ok(s) = std::str::from_utf8(value) { return Ok(TrimmedDid::Other(CowStr::Borrowed(s))); } miette::bail!("invalid utf8 for other did"); } } }}
impl<'a> From<TrimmedDid<'a>> for UserKey { fn from(val: TrimmedDid<'a>) -> Self { let mut vec = Vec::with_capacity(32); val.write_to_vec(&mut vec); UserKey::new(&vec) }}
impl<'a> From<&TrimmedDid<'a>> for UserKey { fn from(val: &TrimmedDid<'a>) -> Self { let mut vec = Vec::with_capacity(32); val.write_to_vec(&mut vec); UserKey::new(&vec) }}
impl Serialize for TrimmedDid<'_> { fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { let mut vec = Vec::with_capacity(32); self.write_to_vec(&mut vec); serializer.serialize_bytes(&vec) }}
impl<'de> Deserialize<'de> for TrimmedDid<'de> { fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { struct DidVisitor; impl<'de> serde::de::Visitor<'de> for DidVisitor { type Value = TrimmedDid<'de>; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("bytes (tagged) or string (legacy)") }
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> where E: serde::de::Error, { TrimmedDid::try_from(v) .map(|td| td.into_static()) .map_err(E::custom) }
fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E> where E: serde::de::Error, { TrimmedDid::try_from(v).map_err(E::custom) } } deserializer.deserialize_any(DidVisitor) }}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]pub struct DbTid(#[serde(with = "serde_bytes")] [u8; 8]);
impl DbTid { pub fn new_from_bytes(bytes: [u8; 8]) -> Self { Self(bytes) }
pub fn len(&self) -> usize { self.0.len() }
pub fn as_bytes(&self) -> &[u8] { &self.0 }
pub fn to_tid(self) -> Tid { Tid::raw(self.to_smolstr()) }
fn to_smolstr(self) -> SmolStr { let mut i = u64::from_be_bytes(self.0); let mut s = SmolStrBuilder::new(); for _ in 0..13 { let c = i & 0x1F; s.push(S32_CHAR.chars().nth(c as usize).unwrap()); i >>= 5; }
let mut builder = SmolStrBuilder::new(); for c in s.finish().chars().rev() { builder.push(c); } builder.finish() }}
pub fn s32decode(s: &str) -> u64 { let mut i: usize = 0; for c in s.chars() { i = i * 32 + S32_CHAR.chars().position(|x| x == c).unwrap(); } i as u64}
impl From<&Tid> for DbTid { fn from(tid: &Tid) -> Self { DbTid(s32decode(tid.as_str()).to_be_bytes()) }}
impl From<DbTid> for Tid { fn from(val: DbTid) -> Self { val.to_tid() }}
impl Display for DbTid { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.to_smolstr()) }}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]#[repr(u8)]#[cfg_attr(not(feature = "indexer"), allow(dead_code))]pub enum DbAction { Create = 0, Update = 1, Delete = 2,}
#[cfg_attr(not(feature = "indexer"), allow(dead_code))]impl DbAction { pub fn as_str(&self) -> &'static str { match self { DbAction::Create => "create", DbAction::Update => "update", DbAction::Delete => "delete", } }}
impl<'a> TryFrom<&'a str> for DbAction { type Error = miette::Report;
fn try_from(s: &'a str) -> Result<Self, Self::Error> { match s { "create" => Ok(DbAction::Create), "update" => Ok(DbAction::Update), "delete" => Ok(DbAction::Delete), _ => miette::bail!("invalid action: {}", s), } }}
impl Display for DbAction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.as_str()) }}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]#[serde(untagged)]#[cfg_attr(not(feature = "indexer"), allow(dead_code))]pub enum DbRkey { Tid(DbTid), Str(SmolStr),}
#[cfg_attr(not(feature = "indexer"), allow(dead_code))]impl DbRkey { pub fn new(s: &str) -> Self { if let Ok(tid) = Tid::new(s) { DbRkey::Tid(DbTid::from(&tid)) } else { DbRkey::Str(SmolStr::from(s)) } }
pub fn len(&self) -> usize { match self { DbRkey::Tid(tid) => tid.len(), DbRkey::Str(s) => s.len(), } }
pub fn to_smolstr(&self) -> SmolStr { match self { DbRkey::Tid(tid) => tid.to_smolstr(), DbRkey::Str(s) => s.clone(), } }}
impl From<&str> for DbRkey { fn from(s: &str) -> Self { Self::new(s) }}
impl From<String> for DbRkey { fn from(s: String) -> Self { Self::new(&s) }}
impl From<SmolStr> for DbRkey { fn from(s: SmolStr) -> Self { Self::new(s.as_str()) }}
impl Display for DbRkey { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.to_smolstr()) }}
/// did:key:z... → raw multicodec public key bytes#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, jacquard_derive::IntoStatic)]pub struct DidKey<'b>( #[serde(borrow)] #[serde(with = "serde_bytes")] pub Cow<'b, [u8]>,);
impl DidKey<'_> { #[allow(dead_code)] pub fn from_did_key(s: &str) -> miette::Result<Self> { let multibase_str = s .strip_prefix("did:key:") .ok_or_else(|| miette::miette!("missing did:key: prefix in {s}"))?; let (_base, bytes) = multibase::decode(multibase_str) .into_diagnostic() .wrap_err("invalid multibase in did:key")?; Ok(Self(Cow::Owned(bytes))) }
pub fn encode(&self) -> String { multibase::encode(multibase::Base::Base58Btc, &self.0) }}
impl std::fmt::Display for DidKey<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "did:key:{}", self.encode()) }}
impl From<PublicKey<'_>> for DidKey<'static> { fn from(value: PublicKey<'_>) -> Self { use jacquard_common::types::crypto::KeyCodec; fn code_of(codec: KeyCodec) -> u64 { match codec { KeyCodec::Ed25519 => 0xED, KeyCodec::Secp256k1 => 0xE7, KeyCodec::P256 => 0x1200, KeyCodec::Unknown(c) => c, } } let mut bytes = Vec::with_capacity(8 + value.bytes.len()); bytes.append(&mut encode_uvarint(code_of(value.codec))); bytes.extend_from_slice(&value.bytes); Self(bytes.into()) }}
#[cfg(test)]mod tests { use super::*; use jacquard_common::types::tid::Tid;
#[test] fn test_dbtid_roundtrip() { let tid_str = "3jzfcijpj2z2a"; let tid = Tid::new(tid_str).unwrap(); let db_tid = DbTid::from(&tid); assert_eq!(db_tid.to_tid().as_str(), tid_str);
let tid_str_2 = "2222222222222"; let tid = Tid::new(tid_str_2).unwrap(); let db_tid = DbTid::from(&tid); assert_eq!(db_tid.to_tid().as_str(), tid_str_2); }
#[test] fn test_dbrkey() { let tid_str = "3jzfcijpj2z2a"; let rkey = DbRkey::new(tid_str); if let DbRkey::Tid(t) = rkey { assert_eq!(t.to_tid().as_str(), tid_str); } else { panic!("expected tid"); }
let str_val = "self"; let rkey = DbRkey::new(str_val); if let DbRkey::Str(s) = rkey { assert_eq!(s, str_val); } else { panic!("expected str"); } }}