Something went wrong. Try again.
PDS software with bells & whistles you didn’t even know you needed. will move this to its own account when ready.
Something went wrong. Try again.
38 kB · 1752 lines
Rust
at main
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753use serde::{Deserialize, Serialize};use std::borrow::Cow;use std::fmt;use std::ops::Deref;use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, sqlx::Type)]#[serde(transparent)]#[sqlx(transparent)]pub struct Did(String);
impl<'de> Deserialize<'de> for Did { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>, { let s = String::deserialize(deserializer)?; Did::new(&s).map_err(|e| serde::de::Error::custom(e.to_string())) }}
impl From<Did> for String { fn from(did: Did) -> Self { did.0 }}
impl From<String> for Did { fn from(s: String) -> Self { Did(s) }}
impl<'a> From<&'a Did> for Cow<'a, str> { fn from(did: &'a Did) -> Self { Cow::Borrowed(&did.0) }}
impl Did { pub fn new(s: impl Into<String>) -> Result<Self, DidError> { let s = s.into(); jacquard::types::string::Did::new(&s).map_err(|_| DidError::Invalid(s.clone()))?; Ok(Self(s)) }
pub fn new_unchecked(s: impl Into<String>) -> Self { Self(s.into()) }
pub fn as_str(&self) -> &str { &self.0 }
pub fn into_inner(self) -> String { self.0 }
pub fn is_plc(&self) -> bool { self.0.starts_with("did:plc:") }
pub fn is_web(&self) -> bool { self.0.starts_with("did:web:") }}
impl AsRef<str> for Did { fn as_ref(&self) -> &str { &self.0 }}
impl PartialEq<str> for Did { fn eq(&self, other: &str) -> bool { self.0 == other }}
impl PartialEq<&str> for Did { fn eq(&self, other: &&str) -> bool { self.0 == *other }}
impl PartialEq<String> for Did { fn eq(&self, other: &String) -> bool { self.0 == *other }}
impl PartialEq<Did> for String { fn eq(&self, other: &Did) -> bool { *self == other.0 }}
impl PartialEq<Did> for &str { fn eq(&self, other: &Did) -> bool { *self == other.0 }}
impl Deref for Did { type Target = str;
fn deref(&self) -> &Self::Target { &self.0 }}
impl fmt::Display for Did { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) }}
impl FromStr for Did { type Err = DidError;
fn from_str(s: &str) -> Result<Self, Self::Err> { Self::new(s) }}
#[derive(Debug, Clone)]pub enum DidError { Invalid(String),}
impl fmt::Display for DidError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Invalid(s) => write!(f, "invalid DID: {}", s), } }}
impl std::error::Error for DidError {}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]#[serde(transparent)]#[sqlx(transparent)]pub struct Handle(String);
impl From<Handle> for String { fn from(handle: Handle) -> Self { handle.0 }}
impl From<String> for Handle { fn from(s: String) -> Self { Handle(s) }}
impl<'a> From<&'a Handle> for Cow<'a, str> { fn from(handle: &'a Handle) -> Self { Cow::Borrowed(&handle.0) }}
impl Handle { pub fn new(s: impl Into<String>) -> Result<Self, HandleError> { let s = s.into(); jacquard::types::string::Handle::new(&s).map_err(|_| HandleError::Invalid(s.clone()))?; Ok(Self(s)) }
pub fn new_unchecked(s: impl Into<String>) -> Self { Self(s.into()) }
pub fn as_str(&self) -> &str { &self.0 }
pub fn into_inner(self) -> String { self.0 }}
impl AsRef<str> for Handle { fn as_ref(&self) -> &str { &self.0 }}
impl Deref for Handle { type Target = str;
fn deref(&self) -> &Self::Target { &self.0 }}
impl PartialEq<str> for Handle { fn eq(&self, other: &str) -> bool { self.0 == other }}
impl PartialEq<&str> for Handle { fn eq(&self, other: &&str) -> bool { self.0 == *other }}
impl PartialEq<String> for Handle { fn eq(&self, other: &String) -> bool { self.0 == *other }}
impl PartialEq<Handle> for String { fn eq(&self, other: &Handle) -> bool { *self == other.0 }}
impl PartialEq<Handle> for &str { fn eq(&self, other: &Handle) -> bool { *self == other.0 }}
impl fmt::Display for Handle { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) }}
impl FromStr for Handle { type Err = HandleError;
fn from_str(s: &str) -> Result<Self, Self::Err> { Self::new(s) }}
#[derive(Debug, Clone)]pub enum HandleError { Invalid(String),}
impl fmt::Display for HandleError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Invalid(s) => write!(f, "invalid handle: {}", s), } }}
impl std::error::Error for HandleError {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]pub enum AtIdentifier { Did(Did), Handle(Handle),}
impl AtIdentifier { pub fn new(s: impl AsRef<str>) -> Result<Self, AtIdentifierError> { let s = s.as_ref(); if s.starts_with("did:") { Did::new(s) .map(AtIdentifier::Did) .map_err(|_| AtIdentifierError::Invalid(s.to_string())) } else { Handle::new(s) .map(AtIdentifier::Handle) .map_err(|_| AtIdentifierError::Invalid(s.to_string())) } }
pub fn as_str(&self) -> &str { match self { AtIdentifier::Did(d) => d.as_str(), AtIdentifier::Handle(h) => h.as_str(), } }
pub fn into_inner(self) -> String { match self { AtIdentifier::Did(d) => d.into_inner(), AtIdentifier::Handle(h) => h.into_inner(), } }
pub fn is_did(&self) -> bool { matches!(self, AtIdentifier::Did(_)) }
pub fn is_handle(&self) -> bool { matches!(self, AtIdentifier::Handle(_)) }
pub fn as_did(&self) -> Option<&Did> { match self { AtIdentifier::Did(d) => Some(d), AtIdentifier::Handle(_) => None, } }
pub fn as_handle(&self) -> Option<&Handle> { match self { AtIdentifier::Handle(h) => Some(h), AtIdentifier::Did(_) => None, } }}
impl From<Did> for AtIdentifier { fn from(did: Did) -> Self { AtIdentifier::Did(did) }}
impl From<Handle> for AtIdentifier { fn from(handle: Handle) -> Self { AtIdentifier::Handle(handle) }}
impl AsRef<str> for AtIdentifier { fn as_ref(&self) -> &str { self.as_str() }}
impl Deref for AtIdentifier { type Target = str;
fn deref(&self) -> &Self::Target { self.as_str() }}
impl fmt::Display for AtIdentifier { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.as_str()) }}
impl FromStr for AtIdentifier { type Err = AtIdentifierError;
fn from_str(s: &str) -> Result<Self, Self::Err> { Self::new(s) }}
impl Serialize for AtIdentifier { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer, { serializer.serialize_str(self.as_str()) }}
impl<'de> Deserialize<'de> for AtIdentifier { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>, { let s = String::deserialize(deserializer)?; AtIdentifier::new(&s).map_err(serde::de::Error::custom) }}
#[derive(Debug, Clone)]pub enum AtIdentifierError { Invalid(String),}
impl fmt::Display for AtIdentifierError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Invalid(s) => write!(f, "invalid AT identifier: {}", s), } }}
impl std::error::Error for AtIdentifierError {}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]#[serde(transparent)]#[sqlx(type_name = "rkey")]pub struct Rkey(String);
impl From<Rkey> for String { fn from(rkey: Rkey) -> Self { rkey.0 }}
impl From<String> for Rkey { fn from(s: String) -> Self { Rkey(s) }}
impl<'a> From<&'a Rkey> for Cow<'a, str> { fn from(rkey: &'a Rkey) -> Self { Cow::Borrowed(&rkey.0) }}
impl Rkey { pub fn new(s: impl Into<String>) -> Result<Self, RkeyError> { let s = s.into(); jacquard::types::string::Rkey::new(&s).map_err(|_| RkeyError::Invalid(s.clone()))?; Ok(Self(s)) }
pub fn new_unchecked(s: impl Into<String>) -> Self { Self(s.into()) }
pub fn generate() -> Self { use jacquard::types::integer::LimitedU32; Self(jacquard::types::string::Tid::now(LimitedU32::MIN).to_string()) }
pub fn as_str(&self) -> &str { &self.0 }
pub fn into_inner(self) -> String { self.0 }
pub fn is_tid(&self) -> bool { jacquard::types::string::Tid::from_str(&self.0).is_ok() }}
impl AsRef<str> for Rkey { fn as_ref(&self) -> &str { &self.0 }}
impl Deref for Rkey { type Target = str;
fn deref(&self) -> &Self::Target { &self.0 }}
impl PartialEq<str> for Rkey { fn eq(&self, other: &str) -> bool { self.0 == other }}
impl PartialEq<&str> for Rkey { fn eq(&self, other: &&str) -> bool { self.0 == *other }}
impl PartialEq<String> for Rkey { fn eq(&self, other: &String) -> bool { self.0 == *other }}
impl PartialEq<Rkey> for String { fn eq(&self, other: &Rkey) -> bool { *self == other.0 }}
impl PartialEq<Rkey> for &str { fn eq(&self, other: &Rkey) -> bool { *self == other.0 }}
impl fmt::Display for Rkey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) }}
impl FromStr for Rkey { type Err = RkeyError;
fn from_str(s: &str) -> Result<Self, Self::Err> { Self::new(s) }}
#[derive(Debug, Clone)]pub enum RkeyError { Invalid(String),}
impl fmt::Display for RkeyError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Invalid(s) => write!(f, "invalid rkey: {}", s), } }}
impl std::error::Error for RkeyError {}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]#[serde(transparent)]#[sqlx(type_name = "nsid")]pub struct Nsid(String);
impl From<Nsid> for String { fn from(nsid: Nsid) -> Self { nsid.0 }}
impl From<String> for Nsid { fn from(s: String) -> Self { Nsid(s) }}
impl<'a> From<&'a Nsid> for Cow<'a, str> { fn from(nsid: &'a Nsid) -> Self { Cow::Borrowed(&nsid.0) }}
impl Nsid { pub fn new(s: impl Into<String>) -> Result<Self, NsidError> { let s = s.into(); jacquard::types::string::Nsid::new(&s).map_err(|_| NsidError::Invalid(s.clone()))?; Ok(Self(s)) }
pub fn new_unchecked(s: impl Into<String>) -> Self { Self(s.into()) }
pub fn as_str(&self) -> &str { &self.0 }
pub fn into_inner(self) -> String { self.0 }
pub fn authority(&self) -> Option<&str> { let parts: Vec<&str> = self.0.rsplitn(2, '.').collect(); if parts.len() == 2 { Some(parts[1]) } else { None } }
pub fn name(&self) -> Option<&str> { self.0.rsplit('.').next() }}
impl AsRef<str> for Nsid { fn as_ref(&self) -> &str { &self.0 }}
impl Deref for Nsid { type Target = str;
fn deref(&self) -> &Self::Target { &self.0 }}
impl PartialEq<str> for Nsid { fn eq(&self, other: &str) -> bool { self.0 == other }}
impl PartialEq<&str> for Nsid { fn eq(&self, other: &&str) -> bool { self.0 == *other }}
impl PartialEq<String> for Nsid { fn eq(&self, other: &String) -> bool { self.0 == *other }}
impl PartialEq<Nsid> for String { fn eq(&self, other: &Nsid) -> bool { *self == other.0 }}
impl PartialEq<Nsid> for &str { fn eq(&self, other: &Nsid) -> bool { *self == other.0 }}
impl fmt::Display for Nsid { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) }}
impl FromStr for Nsid { type Err = NsidError;
fn from_str(s: &str) -> Result<Self, Self::Err> { Self::new(s) }}
#[derive(Debug, Clone)]pub enum NsidError { Invalid(String),}
impl fmt::Display for NsidError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Invalid(s) => write!(f, "invalid NSID: {}", s), } }}
impl std::error::Error for NsidError {}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]#[serde(transparent)]#[sqlx(type_name = "at_uri")]pub struct AtUri(String);
impl From<AtUri> for String { fn from(uri: AtUri) -> Self { uri.0 }}
impl From<String> for AtUri { fn from(s: String) -> Self { AtUri(s) }}
impl<'a> From<&'a AtUri> for Cow<'a, str> { fn from(uri: &'a AtUri) -> Self { Cow::Borrowed(&uri.0) }}
impl AtUri { pub fn new(s: impl Into<String>) -> Result<Self, AtUriError> { let s = s.into(); jacquard::types::string::AtUri::new(&s).map_err(|_| AtUriError::Invalid(s.clone()))?; Ok(Self(s)) }
pub fn new_unchecked(s: impl Into<String>) -> Self { Self(s.into()) }
pub fn from_parts(did: &str, collection: &str, rkey: &str) -> Self { Self(format!("at://{}/{}/{}", did, collection, rkey)) }
pub fn as_str(&self) -> &str { &self.0 }
pub fn into_inner(self) -> String { self.0 }}
impl AsRef<str> for AtUri { fn as_ref(&self) -> &str { &self.0 }}
impl Deref for AtUri { type Target = str;
fn deref(&self) -> &Self::Target { &self.0 }}
impl PartialEq<str> for AtUri { fn eq(&self, other: &str) -> bool { self.0 == other }}
impl PartialEq<&str> for AtUri { fn eq(&self, other: &&str) -> bool { self.0 == *other }}
impl PartialEq<String> for AtUri { fn eq(&self, other: &String) -> bool { self.0 == *other }}
impl PartialEq<AtUri> for String { fn eq(&self, other: &AtUri) -> bool { *self == other.0 }}
impl PartialEq<AtUri> for &str { fn eq(&self, other: &AtUri) -> bool { *self == other.0 }}
impl fmt::Display for AtUri { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) }}
impl FromStr for AtUri { type Err = AtUriError;
fn from_str(s: &str) -> Result<Self, Self::Err> { Self::new(s) }}
#[derive(Debug, Clone)]pub enum AtUriError { Invalid(String),}
impl fmt::Display for AtUriError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Invalid(s) => write!(f, "invalid AT URI: {}", s), } }}
impl std::error::Error for AtUriError {}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]#[serde(transparent)]#[sqlx(transparent)]pub struct Tid(String);
impl From<Tid> for String { fn from(tid: Tid) -> Self { tid.0 }}
impl From<String> for Tid { fn from(s: String) -> Self { Tid(s) }}
impl<'a> From<&'a Tid> for Cow<'a, str> { fn from(tid: &'a Tid) -> Self { Cow::Borrowed(&tid.0) }}
impl Tid { pub fn new(s: impl Into<String>) -> Result<Self, TidError> { let s = s.into(); jacquard::types::string::Tid::from_str(&s).map_err(|_| TidError::Invalid(s.clone()))?; Ok(Self(s)) }
pub fn new_unchecked(s: impl Into<String>) -> Self { Self(s.into()) }
pub fn now() -> Self { use jacquard::types::integer::LimitedU32; Self(jacquard::types::string::Tid::now(LimitedU32::MIN).to_string()) }
pub fn as_str(&self) -> &str { &self.0 }
pub fn into_inner(self) -> String { self.0 }}
impl AsRef<str> for Tid { fn as_ref(&self) -> &str { &self.0 }}
impl Deref for Tid { type Target = str;
fn deref(&self) -> &Self::Target { &self.0 }}
impl PartialEq<str> for Tid { fn eq(&self, other: &str) -> bool { self.0 == other }}
impl PartialEq<&str> for Tid { fn eq(&self, other: &&str) -> bool { self.0 == *other }}
impl PartialEq<String> for Tid { fn eq(&self, other: &String) -> bool { self.0 == *other }}
impl PartialEq<Tid> for String { fn eq(&self, other: &Tid) -> bool { *self == other.0 }}
impl PartialEq<Tid> for &str { fn eq(&self, other: &Tid) -> bool { *self == other.0 }}
impl fmt::Display for Tid { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) }}
impl FromStr for Tid { type Err = TidError;
fn from_str(s: &str) -> Result<Self, Self::Err> { Self::new(s) }}
#[derive(Debug, Clone)]pub enum TidError { Invalid(String),}
impl fmt::Display for TidError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Invalid(s) => write!(f, "invalid TID: {}", s), } }}
impl std::error::Error for TidError {}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]#[serde(transparent)]#[sqlx(transparent)]pub struct Datetime(String);
impl From<Datetime> for String { fn from(dt: Datetime) -> Self { dt.0 }}
impl From<String> for Datetime { fn from(s: String) -> Self { Datetime(s) }}
impl<'a> From<&'a Datetime> for Cow<'a, str> { fn from(dt: &'a Datetime) -> Self { Cow::Borrowed(&dt.0) }}
impl Datetime { pub fn new(s: impl Into<String>) -> Result<Self, DatetimeError> { let s = s.into(); jacquard::types::string::Datetime::from_str(&s) .map_err(|_| DatetimeError::Invalid(s.clone()))?; Ok(Self(s)) }
pub fn new_unchecked(s: impl Into<String>) -> Self { Self(s.into()) }
pub fn now() -> Self { Self( chrono::Utc::now() .format("%Y-%m-%dT%H:%M:%S%.3fZ") .to_string(), ) }
pub fn as_str(&self) -> &str { &self.0 }
pub fn into_inner(self) -> String { self.0 }}
impl AsRef<str> for Datetime { fn as_ref(&self) -> &str { &self.0 }}
impl Deref for Datetime { type Target = str;
fn deref(&self) -> &Self::Target { &self.0 }}
impl PartialEq<str> for Datetime { fn eq(&self, other: &str) -> bool { self.0 == other }}
impl PartialEq<&str> for Datetime { fn eq(&self, other: &&str) -> bool { self.0 == *other }}
impl PartialEq<String> for Datetime { fn eq(&self, other: &String) -> bool { self.0 == *other }}
impl PartialEq<Datetime> for String { fn eq(&self, other: &Datetime) -> bool { *self == other.0 }}
impl PartialEq<Datetime> for &str { fn eq(&self, other: &Datetime) -> bool { *self == other.0 }}
impl fmt::Display for Datetime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) }}
impl FromStr for Datetime { type Err = DatetimeError;
fn from_str(s: &str) -> Result<Self, Self::Err> { Self::new(s) }}
#[derive(Debug, Clone)]pub enum DatetimeError { Invalid(String),}
impl fmt::Display for DatetimeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Invalid(s) => write!(f, "invalid datetime: {}", s), } }}
impl std::error::Error for DatetimeError {}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]#[serde(transparent)]#[sqlx(transparent)]pub struct Language(String);
impl From<Language> for String { fn from(lang: Language) -> Self { lang.0 }}
impl From<String> for Language { fn from(s: String) -> Self { Language(s) }}
impl<'a> From<&'a Language> for Cow<'a, str> { fn from(lang: &'a Language) -> Self { Cow::Borrowed(&lang.0) }}
impl Language { pub fn new(s: impl Into<String>) -> Result<Self, LanguageError> { let s = s.into(); jacquard::types::string::Language::from_str(&s) .map_err(|_| LanguageError::Invalid(s.clone()))?; Ok(Self(s)) }
pub fn new_unchecked(s: impl Into<String>) -> Self { Self(s.into()) }
pub fn as_str(&self) -> &str { &self.0 }
pub fn into_inner(self) -> String { self.0 }}
impl AsRef<str> for Language { fn as_ref(&self) -> &str { &self.0 }}
impl Deref for Language { type Target = str;
fn deref(&self) -> &Self::Target { &self.0 }}
impl PartialEq<str> for Language { fn eq(&self, other: &str) -> bool { self.0 == other }}
impl PartialEq<&str> for Language { fn eq(&self, other: &&str) -> bool { self.0 == *other }}
impl PartialEq<String> for Language { fn eq(&self, other: &String) -> bool { self.0 == *other }}
impl PartialEq<Language> for String { fn eq(&self, other: &Language) -> bool { *self == other.0 }}
impl PartialEq<Language> for &str { fn eq(&self, other: &Language) -> bool { *self == other.0 }}
impl fmt::Display for Language { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) }}
impl FromStr for Language { type Err = LanguageError;
fn from_str(s: &str) -> Result<Self, Self::Err> { Self::new(s) }}
#[derive(Debug, Clone)]pub enum LanguageError { Invalid(String),}
impl fmt::Display for LanguageError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Invalid(s) => write!(f, "invalid language tag: {}", s), } }}
impl std::error::Error for LanguageError {}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]#[serde(transparent)]#[sqlx(transparent)]pub struct CidLink(String);
impl From<CidLink> for String { fn from(cid: CidLink) -> Self { cid.0 }}
impl From<String> for CidLink { fn from(s: String) -> Self { CidLink(s) }}
impl<'a> From<&'a CidLink> for Cow<'a, str> { fn from(cid: &'a CidLink) -> Self { Cow::Borrowed(&cid.0) }}
impl CidLink { pub fn new(s: impl Into<String>) -> Result<Self, CidLinkError> { let s = s.into(); cid::Cid::from_str(&s).map_err(|_| CidLinkError::Invalid(s.clone()))?; Ok(Self(s)) }
pub fn new_unchecked(s: impl Into<String>) -> Self { Self(s.into()) }
pub fn as_str(&self) -> &str { &self.0 }
pub fn into_inner(self) -> String { self.0 }
pub fn to_cid(&self) -> Result<cid::Cid, cid::Error> { cid::Cid::from_str(&self.0) }}
impl AsRef<str> for CidLink { fn as_ref(&self) -> &str { &self.0 }}
impl Deref for CidLink { type Target = str;
fn deref(&self) -> &Self::Target { &self.0 }}
impl PartialEq<str> for CidLink { fn eq(&self, other: &str) -> bool { self.0 == other }}
impl PartialEq<&str> for CidLink { fn eq(&self, other: &&str) -> bool { self.0 == *other }}
impl PartialEq<String> for CidLink { fn eq(&self, other: &String) -> bool { self.0 == *other }}
impl PartialEq<CidLink> for String { fn eq(&self, other: &CidLink) -> bool { *self == other.0 }}
impl PartialEq<CidLink> for &str { fn eq(&self, other: &CidLink) -> bool { *self == other.0 }}
impl fmt::Display for CidLink { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) }}
impl FromStr for CidLink { type Err = CidLinkError;
fn from_str(s: &str) -> Result<Self, Self::Err> { Self::new(s) }}
#[derive(Debug, Clone)]pub enum CidLinkError { Invalid(String),}
impl fmt::Display for CidLinkError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Invalid(s) => write!(f, "invalid CID: {}", s), } }}
impl std::error::Error for CidLinkError {}
#[derive(Debug, Clone, PartialEq, Eq)]pub enum AccountState { Active, Deactivated { at: chrono::DateTime<chrono::Utc>, }, TakenDown { reference: String, }, Migrated { at: chrono::DateTime<chrono::Utc>, to_pds: String, },}
impl AccountState { pub fn from_db_fields( deactivated_at: Option<chrono::DateTime<chrono::Utc>>, takedown_ref: Option<String>, migrated_to_pds: Option<String>, migrated_at: Option<chrono::DateTime<chrono::Utc>>, ) -> Self { if let Some(reference) = takedown_ref { AccountState::TakenDown { reference } } else if let (Some(at), Some(to_pds)) = (deactivated_at, migrated_to_pds) { let migrated_at = migrated_at.unwrap_or(at); AccountState::Migrated { at: migrated_at, to_pds, } } else if let Some(at) = deactivated_at { AccountState::Deactivated { at } } else { AccountState::Active } }
pub fn is_active(&self) -> bool { matches!(self, AccountState::Active) }
pub fn is_deactivated(&self) -> bool { matches!(self, AccountState::Deactivated { .. }) }
pub fn is_takendown(&self) -> bool { matches!(self, AccountState::TakenDown { .. }) }
pub fn is_migrated(&self) -> bool { matches!(self, AccountState::Migrated { .. }) }
pub fn can_login(&self) -> bool { matches!(self, AccountState::Active) }
pub fn can_access_repo(&self) -> bool { matches!( self, AccountState::Active | AccountState::Deactivated { .. } ) }
pub fn status_string(&self) -> &'static str { match self { AccountState::Active => "active", AccountState::Deactivated { .. } => "deactivated", AccountState::TakenDown { .. } => "takendown", AccountState::Migrated { .. } => "deactivated", } }
pub fn status_for_session(&self) -> Option<&'static str> { match self { AccountState::Active => None, AccountState::Deactivated { .. } => Some("deactivated"), AccountState::TakenDown { .. } => Some("takendown"), AccountState::Migrated { .. } => Some("migrated"), } }}
impl fmt::Display for AccountState { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { AccountState::Active => write!(f, "active"), AccountState::Deactivated { at } => write!(f, "deactivated ({})", at), AccountState::TakenDown { reference } => write!(f, "takendown ({})", reference), AccountState::Migrated { to_pds, .. } => write!(f, "migrated to {}", to_pds), } }}
#[derive(Debug, Clone, Deserialize)]#[serde(transparent)]pub struct PlainPassword(String);
impl PlainPassword { pub fn new(s: impl Into<String>) -> Self { Self(s.into()) }
pub fn as_str(&self) -> &str { &self.0 }
pub fn into_inner(self) -> String { self.0 }
pub fn is_empty(&self) -> bool { self.0.is_empty() }}
impl AsRef<str> for PlainPassword { fn as_ref(&self) -> &str { &self.0 }}
impl AsRef<[u8]> for PlainPassword { fn as_ref(&self) -> &[u8] { self.0.as_bytes() }}
impl Deref for PlainPassword { type Target = str;
fn deref(&self) -> &Self::Target { &self.0 }}
#[derive(Debug, Clone, Serialize, sqlx::Type)]#[serde(transparent)]#[sqlx(transparent)]pub struct PasswordHash(String);
impl PasswordHash { pub fn from_hash(hash: impl Into<String>) -> Self { Self(hash.into()) }
pub fn as_str(&self) -> &str { &self.0 }
pub fn into_inner(self) -> String { self.0 }}
impl AsRef<str> for PasswordHash { fn as_ref(&self) -> &str { &self.0 }}
impl From<String> for PasswordHash { fn from(s: String) -> Self { Self(s) }}
#[derive(Debug, Clone, PartialEq, Eq)]pub enum TokenSource { Session, OAuth { client_id: Option<String>, }, ServiceAuth { lxm: Option<String>, aud: Option<String>, },}
impl TokenSource { pub fn is_session(&self) -> bool { matches!(self, TokenSource::Session) }
pub fn is_oauth(&self) -> bool { matches!(self, TokenSource::OAuth { .. }) }
pub fn is_service_auth(&self) -> bool { matches!(self, TokenSource::ServiceAuth { .. }) }}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]#[serde(transparent)]pub struct JwkThumbprint(String);
impl JwkThumbprint { pub fn new(s: impl Into<String>) -> Self { Self(s.into()) }
pub fn as_str(&self) -> &str { &self.0 }
pub fn into_inner(self) -> String { self.0 }}
impl AsRef<str> for JwkThumbprint { fn as_ref(&self) -> &str { &self.0 }}
impl Deref for JwkThumbprint { type Target = str;
fn deref(&self) -> &Self::Target { &self.0 }}
impl From<String> for JwkThumbprint { fn from(s: String) -> Self { Self(s) }}
impl PartialEq<str> for JwkThumbprint { fn eq(&self, other: &str) -> bool { self.0 == other }}
impl PartialEq<String> for JwkThumbprint { fn eq(&self, other: &String) -> bool { &self.0 == other }}
impl PartialEq<JwkThumbprint> for String { fn eq(&self, other: &JwkThumbprint) -> bool { self == &other.0 }}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]#[serde(transparent)]pub struct DPoPProofId(String);
impl DPoPProofId { pub fn new(s: impl Into<String>) -> Self { Self(s.into()) }
pub fn as_str(&self) -> &str { &self.0 }
pub fn into_inner(self) -> String { self.0 }}
impl AsRef<str> for DPoPProofId { fn as_ref(&self) -> &str { &self.0 }}
impl Deref for DPoPProofId { type Target = str;
fn deref(&self) -> &Self::Target { &self.0 }}
impl From<String> for DPoPProofId { fn from(s: String) -> Self { Self(s) }}
#[cfg(test)]mod tests { use super::*;
#[test] fn test_did_validation() { assert!(Did::new("did:plc:abc123").is_ok()); assert!(Did::new("did:web:example.com").is_ok()); assert!(Did::new("not-a-did").is_err()); assert!(Did::new("").is_err()); }
#[test] fn test_did_methods() { let plc = Did::new("did:plc:abc123").unwrap(); assert!(plc.is_plc()); assert!(!plc.is_web()); assert_eq!(plc.as_str(), "did:plc:abc123");
let web = Did::new("did:web:example.com").unwrap(); assert!(!web.is_plc()); assert!(web.is_web()); }
#[test] fn test_did_conversions() { let did = Did::new("did:plc:test123").unwrap(); let s: String = did.clone().into(); assert_eq!(s, "did:plc:test123"); assert_eq!(format!("{}", did), "did:plc:test123"); }
#[test] fn test_did_serde() { let did = Did::new("did:plc:test123").unwrap(); let json = serde_json::to_string(&did).unwrap(); assert_eq!(json, "\"did:plc:test123\"");
let parsed: Did = serde_json::from_str(&json).unwrap(); assert_eq!(parsed, did); }
#[test] fn test_handle_validation() { assert!(Handle::new("user.bsky.social").is_ok()); assert!(Handle::new("test.example.com").is_ok()); assert!(Handle::new("invalid handle with spaces").is_err()); }
#[test] fn test_rkey_validation() { assert!(Rkey::new("self").is_ok()); assert!(Rkey::new("3jzfcijpj2z2a").is_ok()); assert!(Rkey::new("invalid/rkey").is_err()); }
#[test] fn test_rkey_generate() { let rkey = Rkey::generate(); assert!(rkey.is_tid()); assert!(!rkey.as_str().is_empty()); }
#[test] fn test_nsid_validation() { assert!(Nsid::new("app.bsky.feed.post").is_ok()); assert!(Nsid::new("com.atproto.repo.createRecord").is_ok()); assert!(Nsid::new("invalid").is_err()); }
#[test] fn test_nsid_parts() { let nsid = Nsid::new("app.bsky.feed.post").unwrap(); assert_eq!(nsid.name(), Some("post")); }
#[test] fn test_at_uri_validation() { assert!(AtUri::new("at://did:plc:abc123/app.bsky.feed.post/xyz").is_ok()); assert!(AtUri::new("not-an-at-uri").is_err()); }
#[test] fn test_at_uri_from_parts() { let uri = AtUri::from_parts("did:plc:abc123", "app.bsky.feed.post", "xyz"); assert_eq!(uri.as_str(), "at://did:plc:abc123/app.bsky.feed.post/xyz"); }
#[test] fn test_type_safety() { fn takes_did(_: &Did) {} fn takes_handle(_: &Handle) {}
let did = Did::new("did:plc:test").unwrap(); let handle = Handle::new("test.bsky.social").unwrap();
takes_did(&did); takes_handle(&handle); }
#[test] fn test_tid_validation() { let tid = Tid::now(); assert!(!tid.as_str().is_empty()); assert!(Tid::new(tid.as_str()).is_ok()); assert!(Tid::new("invalid").is_err()); }
#[test] fn test_datetime_validation() { assert!(Datetime::new("2024-01-15T12:30:45.123Z").is_ok()); assert!(Datetime::new("not-a-date").is_err()); let now = Datetime::now(); assert!(!now.as_str().is_empty()); }
#[test] fn test_language_validation() { assert!(Language::new("en").is_ok()); assert!(Language::new("en-US").is_ok()); assert!(Language::new("ja").is_ok()); }
#[test] fn test_cidlink_validation() { assert!( CidLink::new("bafyreib74ckyq525l3y6an5txykwwtb3dgex6ofzakml53di77oxwr5pfe").is_ok() ); assert!(CidLink::new("not-a-cid").is_err()); }
#[test] fn test_at_identifier_validation() { let did_ident = AtIdentifier::new("did:plc:abc123").unwrap(); assert!(did_ident.is_did()); assert!(!did_ident.is_handle()); assert!(did_ident.as_did().is_some()); assert!(did_ident.as_handle().is_none());
let handle_ident = AtIdentifier::new("user.bsky.social").unwrap(); assert!(!handle_ident.is_did()); assert!(handle_ident.is_handle()); assert!(handle_ident.as_did().is_none()); assert!(handle_ident.as_handle().is_some());
assert!(AtIdentifier::new("invalid identifier").is_err()); }
#[test] fn test_at_identifier_serde() { let ident = AtIdentifier::new("did:plc:test123").unwrap(); let json = serde_json::to_string(&ident).unwrap(); assert_eq!(json, "\"did:plc:test123\"");
let parsed: AtIdentifier = serde_json::from_str(&json).unwrap(); assert_eq!(parsed.as_str(), "did:plc:test123"); }
#[test] fn test_account_state_active() { let state = AccountState::from_db_fields(None, None, None, None); assert!(state.is_active()); assert!(!state.is_deactivated()); assert!(!state.is_takendown()); assert!(!state.is_migrated()); assert!(state.can_login()); assert!(state.can_access_repo()); assert_eq!(state.status_string(), "active"); }
#[test] fn test_account_state_deactivated() { let now = chrono::Utc::now(); let state = AccountState::from_db_fields(Some(now), None, None, None); assert!(!state.is_active()); assert!(state.is_deactivated()); assert!(!state.is_takendown()); assert!(!state.is_migrated()); assert!(!state.can_login()); assert!(state.can_access_repo()); assert_eq!(state.status_string(), "deactivated"); }
#[test] fn test_account_state_takendown() { let state = AccountState::from_db_fields(None, Some("mod-action-123".into()), None, None); assert!(!state.is_active()); assert!(!state.is_deactivated()); assert!(state.is_takendown()); assert!(!state.is_migrated()); assert!(!state.can_login()); assert!(!state.can_access_repo()); assert_eq!(state.status_string(), "takendown"); }
#[test] fn test_account_state_migrated() { let now = chrono::Utc::now(); let state = AccountState::from_db_fields(Some(now), None, Some("https://other.pds".into()), None); assert!(!state.is_active()); assert!(!state.is_deactivated()); assert!(!state.is_takendown()); assert!(state.is_migrated()); assert!(!state.can_login()); assert!(!state.can_access_repo()); assert_eq!(state.status_string(), "deactivated"); }
#[test] fn test_account_state_takedown_priority() { let now = chrono::Utc::now(); let state = AccountState::from_db_fields( Some(now), Some("mod-action".into()), Some("https://other.pds".into()), None, ); assert!(state.is_takendown()); }}