···4747 pub value: Allow<'a>,
4848}
49495050+impl From<AllowGetRecordOutput<'_>> for Allow<'_> {
5151+ fn from(output: AllowGetRecordOutput<'_>) -> Self {
5252+ use jacquard_common::IntoStatic;
5353+ output.value.into_static()
5454+ }
5555+}
5656+5757+impl jacquard_common::types::collection::Collection for Allow<'_> {
5858+ const NSID: &'static str = "win.tomo-x.pushat.allow";
5959+ type Record = AllowRecord;
6060+}
6161+5062/// Marker type for deserializing records from this collection.
6363+#[derive(Debug, serde::Serialize, serde::Deserialize)]
5164pub struct AllowRecord;
5265impl jacquard_common::xrpc::XrpcResp for AllowRecord {
5366 const NSID: &'static str = "win.tomo-x.pushat.allow";
···5669 type Err<'de> = jacquard_common::types::collection::RecordError<'de>;
5770}
58715959-impl jacquard_common::types::collection::Collection for Allow<'_> {
7272+impl jacquard_common::types::collection::Collection for AllowRecord {
6073 const NSID: &'static str = "win.tomo-x.pushat.allow";
6174 type Record = AllowRecord;
6262-}
6363-6464-impl From<AllowGetRecordOutput<'_>> for Allow<'_> {
6565- fn from(output: AllowGetRecordOutput<'_>) -> Self {
6666- use jacquard_common::IntoStatic;
6767- output.value.into_static()
6868- }
6975}
+16-4
crates/jacquard-lexicon/src/codegen/structs.rs
···48484949 required.iter().all(|field_name| {
5050 let field_name_str: &str = field_name.as_ref();
5151- obj.properties.get(field_name_str)
5151+ obj.properties
5252+ .get(field_name_str)
5253 .map(is_defaultable_string)
5354 .unwrap_or(false)
5455 })
···176177177178 let record_marker = quote! {
178179 /// Marker type for deserializing records from this collection.
180180+ #[derive(Debug, serde::Serialize, serde::Deserialize)]
179181 pub struct #record_marker_ident;
180182181183 impl jacquard_common::xrpc::XrpcResp for #record_marker_ident {
···199201 // Generate Collection trait impl
200202 let collection_impl = quote! {
201203 impl jacquard_common::types::collection::Collection for #ident<'_> {
204204+ const NSID: &'static str = #nsid;
205205+ type Record = #record_marker_ident;
206206+ }
207207+ };
208208+209209+ // Generate collection impl for the marker struct to drive fetch_record()
210210+ let collection_marker_impl = quote! {
211211+ impl jacquard_common::types::collection::Collection for #record_marker_ident {
202212 const NSID: &'static str = #nsid;
203213 type Record = #record_marker_ident;
204214 }
···208218 #struct_def
209219 #(#unions)*
210220 #output_wrapper
221221+ #from_impl
222222+ #collection_impl
211223 #record_marker
212212- #collection_impl
213213- #from_impl
224224+ #collection_marker_impl
214225 })
215226 }
216227 }
···235246 // - 1+ required fields (not all strings): bon::Builder (but not if name conflicts)
236247 let required_count = count_required_fields(obj);
237248 let has_default = required_count == 0 || all_required_are_defaultable_strings(obj);
238238- let has_builder = required_count >= 1 && !has_default && !conflicts_with_builder_macro(&type_name);
249249+ let has_builder =
250250+ required_count >= 1 && !has_default && !conflicts_with_builder_macro(&type_name);
239251240252 let fields = self.generate_object_fields(nsid, &type_name, obj, has_builder)?;
241253 let doc = self.generate_doc_comment(obj.description.as_ref());
+30
crates/jacquard/src/client.rs
···542542 }
543543 }
544544545545+ /// Fetches a record from the PDS. Returns an owned, parsed response.
546546+ ///
547547+ /// `record_type` parameter should be the marker struct for the record (e.g. `PostRecord` for `Post`), rather than the Post itself (though it will intuit things correctly regardless).
548548+ /// This allows the compiler to better intuit the output type without turbofishing.
549549+ fn fetch_record<R>(
550550+ &self,
551551+ record_type: R,
552552+ uri: AtUri<'_>,
553553+ ) -> impl std::future::Future<
554554+ Output = Result<<<R as Collection>::Record as XrpcResp>::Output<'static>, ClientError>,
555555+ >
556556+ where
557557+ R: Collection,
558558+ for<'a> <<R as Collection>::Record as XrpcResp>::Output<'a>:
559559+ IntoStatic<Output = <<R as Collection>::Record as XrpcResp>::Output<'static>>,
560560+ for<'a> <<R as Collection>::Record as XrpcResp>::Err<'a>:
561561+ IntoStatic<Output = <<R as Collection>::Record as XrpcResp>::Err<'static>>,
562562+ {
563563+ let _ = record_type;
564564+ async move {
565565+ let response = self.get_record::<R>(uri.clone()).await?;
566566+ let response: Response<R::Record> = response.transmute();
567567+ let output = response
568568+ .into_output()
569569+ .map_err(|e| ClientError::Transport(TransportError::Other(e.to_string().into())))?;
570570+ // TODO: fix this to use a better error lol
571571+ Ok(output)
572572+ }
573573+ }
574574+545575 /// Update a record in-place with a fetch-modify-put pattern.
546576 ///
547577 /// This fetches the record using an at:// URI, converts it to owned data, applies
+2-2
examples/read_tangled_repo.rs
···22use jacquard::api::sh_tangled::repo::Repo;
33use jacquard::client::{AgentSessionExt, BasicClient};
44use jacquard::types::string::AtUri;
55+use jacquard_api::sh_tangled::repo::RepoRecord;
5667#[derive(Parser, Debug)]
78#[command(author, version, about = "Read a Tangled git repository record")]
···2425 let agent = BasicClient::unauthenticated();
25262627 // Use Agent's get_record helper with the at:// URI
2727- let response = agent.get_record::<Repo>(uri).await?;
2828- let output = response.into_output()?;
2828+ let output = agent.fetch_record(RepoRecord, uri).await?;
29293030 println!("Tangled Repository\n");
3131 println!("URI: {}", output.uri);