Skip to main content

PdsClient

Struct PdsClient 

Source
pub struct PdsClient { /* private fields */ }
Expand description

A lightweight client for one user’s PDS repo.

Holds the user’s DID (the repo to read/write), the PDS base URL (resolved from the DID doc), the shared reqwest::Client, and the Auth token. All the com.atproto.repo.* methods below act on self.did’s repo.

The client may also be anonymous (PdsClient::anonymous), in which case it is read-only: it sends no Authorization header and every write path errors out of Auth::bearer.

Cheap to clone (Arc internals); one is held per logged-in session.

Implementations§

Source§

impl PdsClient

Source

pub fn new( http: Client, pds_base: impl Into<String>, did: impl Into<String>, auth: Auth, ) -> Self

Construct a client against an already-resolved PDS base + DID + auth.

Source

pub fn anonymous( http: Client, pds_base: impl Into<String>, did: impl Into<String>, ) -> Self

Construct a read-only, unauthenticated client for a public repo the caller does not own — com.atproto.repo.listRecords is public on a standard PDS, so a stranger’s records need no credentials.

Every com.atproto.repo.* write returns an error (there is no bearer; see Auth::Anonymous). Callers are expected to have obtained pds_base from resolve_did_to_pds, which already runs crate::net::assert_public_target on the resolved serviceEndpoint — but that is not what makes the fetch safe: every read is re-vetted at fetch time by crate::net::guarded_get_no_privacy, which closes the DNS-rebinding window between resolve and connect. This constructor is deliberately synchronous and does no validation of its own, so the authoritative check is not duplicated (or, worse, mistaken for sufficient).

Source

pub async fn login( http: Client, handle: &str, app_password: &str, resolver_base: Option<&str>, plc_directory: Option<&str>, ) -> Result<Self>

Resolve handle → DID → PDS, obtain an app-password session, and build a ready-to-use client. A convenience constructor for the direct-PDS path that exercises the whole stack end-to-end.

resolver_base / plc_directory default to DEFAULT_RESOLVER_HOST / DEFAULT_PLC_DIRECTORY when passed None.

Source

pub fn did(&self) -> &str

The repo DID this client targets.

Source

pub fn pds_base(&self) -> &str

The PDS base URL this client talks to.

Source

pub async fn list_records( &self, collection: &str, limit: Option<u32>, cursor: Option<&str>, ) -> Result<ListRecordsResponse>

com.atproto.repo.listRecords — one page of a collection’s records.

cursor continues a previous page; limit caps the page (atproto’s max is 100). Use list_all_records to page fully.

The fetch is routed through crate::net::guarded_get_no_privacy — the same per-hop scheme/IP allow-list and connect-pinning the feed poller and the identity-resolution paths use. pds_base was vetted by crate::net::assert_public_target at resolve time, but that is a separate DNS resolution from this fetch; routing the request through the guard closes the rebinding window, which matters as soon as the repo (and therefore the host) is chosen by a stranger. The response body is read via crate::net::read_capped so a hostile PDS cannot stream an unbounded body at a 512 MB box.

Source

pub async fn list_all_records( &self, collection: &str, ) -> Result<Vec<RecordEntry>>

Page through all records in a collection, following the cursor until exhausted. Convenience over list_records for the login-time “load the whole follow-list” read.

Bounded by [MAX_LIST_PAGES] and by cursor-repetition detection, because pds_base may be a host we did not choose (see PdsClient::anonymous).

Source

pub async fn list_recent_matching( &self, collection: &str, max_records: usize, page_size: u32, keep: impl FnMut(&RecordEntry) -> bool, ) -> Result<RecordWalk>

See RecordWalk. The most recent records of a collection that the caller keeps, truncating rather than refusing.

The cap counts kept records, not walked ones. Applying it to the raw collection starves a caller whose filter is selective: a quiet standard.site publication in a repo whose busy sibling fills the window returns nothing at all, permanently, and worse with every post the sibling makes. MAX_LIST_PAGES still bounds the request count, so a filter that matches nothing costs a fixed number of round trips.

Truncating, not refusing, because this is an additive read: see [extend_truncating] for why the [extend_bounded] refusal would be strictly worse here.

page_size is the caller’s, because the right page depends on how big the records are: crate::net::read_capped bounds a response at 8 MB, so 100 long-form articles per page can exceed it and fail the whole walk.

Ordering is the PDS’s: listRecords is descending by rkey, which is newest-first only when rkeys are TIDs. For a publisher using slug rkeys the truncation keeps a lexicographic subset rather than a recent one — acceptable because the cap is now per-publication rather than per-repo, so reaching it at all means an archive larger than this reader stores.

Source

pub async fn delete_record(&self, collection: &str, rkey: &str) -> Result<()>

com.atproto.repo.deleteRecord — delete a record by collection + rkey (e.g. unsubscribe → delete the subscription record).

Source

pub async fn list_subscriptions(&self) -> Result<Vec<(String, Subscription)>>

List every Subscription record in the user’s repo (paged fully). The login-time “what does this user follow?” read.

Source

pub async fn create_subscription( &self, sub: &VettedSubscription, ) -> Result<WriteResult>

Create a Subscription record (subscribe to a feed).

Source

pub async fn list_folders(&self) -> Result<Vec<(String, Folder)>>

List every Folder record in the user’s repo.

Source

pub async fn create_folder(&self, folder: &Folder) -> Result<WriteResult>

Create a Folder record.

Source

pub async fn list_saved(&self) -> Result<Vec<(String, Saved)>>

List every Saved (starred) record in the user’s repo.

Source

pub async fn create_saved(&self, saved: &VettedSaved) -> Result<WriteResult>

Create a Saved record (star an article).

Source

pub async fn list_read_states(&self) -> Result<Vec<(String, ReadState)>>

List every ReadState cursor in the user’s repo (the read side a login-time read-state merge would consume).

Source

pub async fn put_read_state( &self, rkey: &str, state: &ReadState, ) -> Result<WriteResult>

Upsert a single ReadState cursor at its feed-derived rkey. For a batch of dirty cursors prefer flush_read_states.

Source

pub async fn flush_read_states( &self, cursors: &[(String, ReadState, bool)], ) -> Result<()>

Batch-flush many dirty ReadState cursors in one applyWrites call — the debounced read-state flusher’s coalesced write.

Each (rkey, state, pds_created) becomes a create op at the feed-derived rkey when the record does not yet exist, and an update when it does — so a feed’s FIRST flush succeeds (an #update on a missing record errors, and applyWrites is atomic per-repo). Both kinds ride the same batch.

Trait Implementations§

Source§

impl Clone for PdsClient

Source§

fn clone(&self) -> PdsClient

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more