//// IO for the append-only shelf.entry event log: load it, query one entry's //// group, append events. The pure fold over the log lives in `crate`; HTTP //// concerns stay in the handlers. import atproto/repo import atproto/xrpc.{type Client, type XrpcError} import crate/gen/shelf/entry.{type ShelfEntry, encode_shelf_entry} import crate/storage.{type StoredItem} import crate_server/oauth/sessions.{type OauthSession} import gleam/dynamic/decode import gleam/list import gleam/option import gleam/string pub fn load( client: Client, session: OauthSession, ) -> Result(List(StoredItem(ShelfEntry)), XrpcError) { repo.list_records( client, session.pds, session.access_token, session.did, entry.collection, row_decoder(), ) } /// The genesis (by rkey) plus every event whose subject references it, /// rkey-ordered. Errors when no genesis exists for the id. pub fn entry_events( stored: List(StoredItem(ShelfEntry)), entry_id: String, ) -> Result(List(StoredItem(ShelfEntry)), Nil) { case list.find(stored, fn(s) { s.rkey == entry_id && s.value.subject == option.None }) { Error(Nil) -> Error(Nil) Ok(genesis) -> Ok( stored |> list.filter(fn(s) { case s.value.subject { option.Some(ref) -> ref.uri == genesis.uri option.None -> s.rkey == entry_id } }) |> list.sort(fn(a, b) { string.compare(a.rkey, b.rkey) }), ) } } /// Fetch one event with its envelope (uri/cid), e.g. to validate and /// reference a genesis. pub fn fetch( client: Client, session: OauthSession, rkey: String, ) -> Result(StoredItem(ShelfEntry), XrpcError) { repo.get_record_envelope( client, session.pds, session.access_token, session.did, entry.collection, rkey, row_decoder(), ) } /// Append one immutable event to the log. pub fn append( client: Client, session: OauthSession, event: ShelfEntry, ) -> Result(repo.CreatedRecord, XrpcError) { repo.create_record( client, session.pds, session.access_token, session.did, entry.collection, encode_shelf_entry(event), ) } pub fn delete( client: Client, session: OauthSession, rkey: String, ) -> Result(Nil, XrpcError) { repo.delete_record( client, session.pds, session.access_token, session.did, entry.collection, rkey, ) } fn row_decoder() -> decode.Decoder(StoredItem(ShelfEntry)) { storage.list_records_row_decoder(entry.shelf_entry_decoder()) }