A better Rust ATProto crate
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

minimal auth client, plus some fixes for codegen for bytes upload and encoding

Orual aa87f30d 32f38c8a

+1403 -228
+2 -3
crates/jacquard-api/src/app_bsky/feed/get_suggested_feeds.rs
··· 50 50 51 51 impl jacquard_common::types::xrpc::XrpcRequest for GetSuggestedFeeds<'_> { 52 52 const NSID: &'static str = "app.bsky.feed.getSuggestedFeeds"; 53 - const METHOD: jacquard_common::types::xrpc::XrpcMethod = 54 - jacquard_common::types::xrpc::XrpcMethod::Query; 53 + const METHOD: jacquard_common::types::xrpc::XrpcMethod = jacquard_common::types::xrpc::XrpcMethod::Query; 55 54 const OUTPUT_ENCODING: &'static str = "application/json"; 56 55 type Output<'de> = GetSuggestedFeedsOutput<'de>; 57 56 type Err<'de> = jacquard_common::types::xrpc::GenericError<'de>; 58 - } 57 + }
+2 -3
crates/jacquard-api/src/app_bsky/unspecced/get_onboarding_suggested_starter_packs.rs
··· 40 40 41 41 impl jacquard_common::types::xrpc::XrpcRequest for GetOnboardingSuggestedStarterPacks { 42 42 const NSID: &'static str = "app.bsky.unspecced.getOnboardingSuggestedStarterPacks"; 43 - const METHOD: jacquard_common::types::xrpc::XrpcMethod = 44 - jacquard_common::types::xrpc::XrpcMethod::Query; 43 + const METHOD: jacquard_common::types::xrpc::XrpcMethod = jacquard_common::types::xrpc::XrpcMethod::Query; 45 44 const OUTPUT_ENCODING: &'static str = "application/json"; 46 45 type Output<'de> = GetOnboardingSuggestedStarterPacksOutput<'de>; 47 46 type Err<'de> = jacquard_common::types::xrpc::GenericError<'de>; 48 - } 47 + }
+11 -8
crates/jacquard-api/src/app_bsky/video/upload_video.rs
··· 5 5 // This file was automatically generated from Lexicon schemas. 6 6 // Any manual changes will be overwritten on the next regeneration. 7 7 8 - #[jacquard_derive::lexicon] 9 8 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 10 9 #[serde(rename_all = "camelCase")] 11 - pub struct UploadVideo<'a> {} 12 - impl jacquard_common::IntoStatic for UploadVideo<'_> { 13 - type Output = UploadVideo<'static>; 10 + pub struct UploadVideo { 11 + pub body: bytes::Bytes, 12 + } 13 + 14 + impl jacquard_common::IntoStatic for UploadVideo { 15 + type Output = UploadVideo; 14 16 fn into_static(self) -> Self::Output { 15 - UploadVideo { 16 - extra_data: self.extra_data.into_static(), 17 - } 17 + self 18 18 } 19 19 } 20 20 ··· 36 36 } 37 37 } 38 38 39 - impl jacquard_common::types::xrpc::XrpcRequest for UploadVideo<'_> { 39 + impl jacquard_common::types::xrpc::XrpcRequest for UploadVideo { 40 40 const NSID: &'static str = "app.bsky.video.uploadVideo"; 41 41 const METHOD: jacquard_common::types::xrpc::XrpcMethod = jacquard_common::types::xrpc::XrpcMethod::Procedure( 42 42 "video/mp4", ··· 44 44 const OUTPUT_ENCODING: &'static str = "application/json"; 45 45 type Output<'de> = UploadVideoOutput<'de>; 46 46 type Err<'de> = jacquard_common::types::xrpc::GenericError<'de>; 47 + fn encode_body(&self) -> Result<Vec<u8>, jacquard_common::types::xrpc::EncodeError> { 48 + Ok(self.body.to_vec()) 49 + } 47 50 }
+11 -8
crates/jacquard-api/src/com_atproto/repo/import_repo.rs
··· 5 5 // This file was automatically generated from Lexicon schemas. 6 6 // Any manual changes will be overwritten on the next regeneration. 7 7 8 - #[jacquard_derive::lexicon] 9 8 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 10 9 #[serde(rename_all = "camelCase")] 11 - pub struct ImportRepo<'a> {} 12 - impl jacquard_common::IntoStatic for ImportRepo<'_> { 13 - type Output = ImportRepo<'static>; 10 + pub struct ImportRepo { 11 + pub body: bytes::Bytes, 12 + } 13 + 14 + impl jacquard_common::IntoStatic for ImportRepo { 15 + type Output = ImportRepo; 14 16 fn into_static(self) -> Self::Output { 15 - ImportRepo { 16 - extra_data: self.extra_data.into_static(), 17 - } 17 + self 18 18 } 19 19 } 20 20 21 - impl jacquard_common::types::xrpc::XrpcRequest for ImportRepo<'_> { 21 + impl jacquard_common::types::xrpc::XrpcRequest for ImportRepo { 22 22 const NSID: &'static str = "com.atproto.repo.importRepo"; 23 23 const METHOD: jacquard_common::types::xrpc::XrpcMethod = jacquard_common::types::xrpc::XrpcMethod::Procedure( 24 24 "application/vnd.ipld.car", ··· 26 26 const OUTPUT_ENCODING: &'static str = "application/json"; 27 27 type Output<'de> = (); 28 28 type Err<'de> = jacquard_common::types::xrpc::GenericError<'de>; 29 + fn encode_body(&self) -> Result<Vec<u8>, jacquard_common::types::xrpc::EncodeError> { 30 + Ok(self.body.to_vec()) 31 + } 29 32 }
+11 -8
crates/jacquard-api/src/com_atproto/repo/upload_blob.rs
··· 5 5 // This file was automatically generated from Lexicon schemas. 6 6 // Any manual changes will be overwritten on the next regeneration. 7 7 8 - #[jacquard_derive::lexicon] 9 8 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 10 9 #[serde(rename_all = "camelCase")] 11 - pub struct UploadBlob<'a> {} 12 - impl jacquard_common::IntoStatic for UploadBlob<'_> { 13 - type Output = UploadBlob<'static>; 10 + pub struct UploadBlob { 11 + pub body: bytes::Bytes, 12 + } 13 + 14 + impl jacquard_common::IntoStatic for UploadBlob { 15 + type Output = UploadBlob; 14 16 fn into_static(self) -> Self::Output { 15 - UploadBlob { 16 - extra_data: self.extra_data.into_static(), 17 - } 17 + self 18 18 } 19 19 } 20 20 ··· 36 36 } 37 37 } 38 38 39 - impl jacquard_common::types::xrpc::XrpcRequest for UploadBlob<'_> { 39 + impl jacquard_common::types::xrpc::XrpcRequest for UploadBlob { 40 40 const NSID: &'static str = "com.atproto.repo.uploadBlob"; 41 41 const METHOD: jacquard_common::types::xrpc::XrpcMethod = jacquard_common::types::xrpc::XrpcMethod::Procedure( 42 42 "*/*", ··· 44 44 const OUTPUT_ENCODING: &'static str = "application/json"; 45 45 type Output<'de> = UploadBlobOutput<'de>; 46 46 type Err<'de> = jacquard_common::types::xrpc::GenericError<'de>; 47 + fn encode_body(&self) -> Result<Vec<u8>, jacquard_common::types::xrpc::EncodeError> { 48 + Ok(self.body.to_vec()) 49 + } 47 50 }
+29 -1
crates/jacquard-common/src/types/xrpc.rs
··· 1 - use serde::de::DeserializeOwned; 2 1 use serde::{Deserialize, Serialize}; 3 2 use std::error::Error; 4 3 use std::fmt::{self, Debug}; 5 4 6 5 use crate::IntoStatic; 7 6 use crate::types::value::Data; 7 + 8 + /// Error type for encoding XRPC requests 9 + #[derive(Debug, thiserror::Error, miette::Diagnostic)] 10 + pub enum EncodeError { 11 + /// Failed to serialize query parameters 12 + #[error("Failed to serialize query: {0}")] 13 + Query( 14 + #[from] 15 + #[source] 16 + serde_html_form::ser::Error, 17 + ), 18 + /// Failed to serialize JSON body 19 + #[error("Failed to serialize JSON: {0}")] 20 + Json( 21 + #[from] 22 + #[source] 23 + serde_json::Error, 24 + ), 25 + /// Other encoding error 26 + #[error("Encoding error: {0}")] 27 + Other(String), 28 + } 8 29 9 30 /// XRPC method type 10 31 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] ··· 53 74 54 75 /// Error type for this request 55 76 type Err<'de>: Error + Deserialize<'de> + IntoStatic; 77 + 78 + /// Encode the request body for procedures. 79 + /// 80 + /// Default implementation serializes to JSON. Override for non-JSON encodings. 81 + fn encode_body(&self) -> Result<Vec<u8>, EncodeError> { 82 + Ok(serde_json::to_vec(self)?) 83 + } 56 84 } 57 85 58 86 /// Error type for XRPC endpoints that don't define any errors
+79 -26
crates/jacquard-lexicon/src/codegen.rs
··· 662 662 params_has_lifetime, 663 663 has_output, 664 664 has_errors, 665 + false, // queries never have binary inputs 665 666 )?; 666 667 output.push(xrpc_impl); 667 668 ··· 680 681 let type_base = self.def_to_type_name(nsid, def_name); 681 682 let mut output = Vec::new(); 682 683 683 - // Input bodies always have lifetimes (they get #[lexicon] attribute) 684 - let params_has_lifetime = proc.input.is_some(); 684 + // Check if input is a binary body (no schema) 685 + let is_binary_input = proc 686 + .input 687 + .as_ref() 688 + .map(|i| i.schema.is_none()) 689 + .unwrap_or(false); 690 + 691 + // Input bodies with schemas have lifetimes (they get #[lexicon] attribute) 692 + // Binary inputs don't have lifetimes 693 + let params_has_lifetime = proc.input.is_some() && !is_binary_input; 685 694 let has_input = proc.input.is_some(); 686 695 let has_output = proc.output.is_some(); 687 696 let has_errors = proc.errors.is_some(); ··· 726 735 params_has_lifetime, 727 736 has_output, 728 737 has_errors, 738 + is_binary_input, 729 739 )?; 730 740 output.push(xrpc_impl); 731 741 ··· 1424 1434 ) -> Result<TokenStream> { 1425 1435 let ident = syn::Ident::new(type_base, proc_macro2::Span::call_site()); 1426 1436 1437 + // Check if this is a binary body (no schema, just raw bytes) 1438 + let is_binary_body = body.schema.is_none(); 1439 + 1427 1440 let fields = if let Some(schema) = &body.schema { 1428 1441 self.generate_body_fields("", type_base, schema)? 1429 1442 } else { 1430 - quote! {} 1443 + // Binary body: just a bytes field 1444 + quote! { 1445 + pub body: bytes::Bytes, 1446 + } 1431 1447 }; 1432 1448 1433 1449 let doc = self.generate_doc_comment(body.description.as_ref()); 1434 1450 1435 - // Input structs always get a lifetime since they have the #[lexicon] attribute 1436 - // which adds extra_data: BTreeMap<..., Data<'a>> 1437 - let struct_def = quote! { 1438 - #doc 1439 - #[jacquard_derive::lexicon] 1440 - #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 1441 - #[serde(rename_all = "camelCase")] 1442 - pub struct #ident<'a> { 1443 - #fields 1451 + // Binary bodies don't need #[lexicon] attribute or lifetime 1452 + let struct_def = if is_binary_body { 1453 + quote! { 1454 + #doc 1455 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 1456 + #[serde(rename_all = "camelCase")] 1457 + pub struct #ident { 1458 + #fields 1459 + } 1460 + } 1461 + } else { 1462 + // Input structs with schemas get a lifetime since they have the #[lexicon] attribute 1463 + // which adds extra_data: BTreeMap<..., Data<'a>> 1464 + quote! { 1465 + #doc 1466 + #[jacquard_derive::lexicon] 1467 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 1468 + #[serde(rename_all = "camelCase")] 1469 + pub struct #ident<'a> { 1470 + #fields 1471 + } 1444 1472 } 1445 1473 }; 1446 1474 ··· 1458 1486 } 1459 1487 1460 1488 // Generate IntoStatic impl 1461 - let field_names: Vec<&str> = match &body.schema { 1462 - Some(crate::lexicon::LexXrpcBodySchema::Object(obj)) => { 1463 - obj.properties.keys().map(|k| k.as_str()).collect() 1464 - } 1465 - Some(_) => { 1466 - // For Ref or Union schemas, there's just a single flattened field 1467 - vec!["value"] 1468 - } 1469 - None => { 1470 - // No schema means no fields, just extra_data 1471 - vec![] 1489 + let into_static_impl = if is_binary_body { 1490 + // Binary bodies: simple clone of the Bytes field 1491 + quote! { 1492 + impl jacquard_common::IntoStatic for #ident { 1493 + type Output = #ident; 1494 + fn into_static(self) -> Self::Output { 1495 + self 1496 + } 1497 + } 1472 1498 } 1499 + } else { 1500 + let field_names: Vec<&str> = match &body.schema { 1501 + Some(crate::lexicon::LexXrpcBodySchema::Object(obj)) => { 1502 + obj.properties.keys().map(|k| k.as_str()).collect() 1503 + } 1504 + Some(_) => { 1505 + // For Ref or Union schemas, there's just a single flattened field 1506 + vec!["value"] 1507 + } 1508 + None => { 1509 + // No schema means no fields, just extra_data 1510 + vec![] 1511 + } 1512 + }; 1513 + self.generate_into_static_for_struct(type_base, &field_names, true, true) 1473 1514 }; 1474 - let into_static_impl = 1475 - self.generate_into_static_for_struct(type_base, &field_names, true, true); 1476 1515 1477 1516 Ok(quote! { 1478 1517 #struct_def ··· 1923 1962 params_has_lifetime: bool, 1924 1963 has_output: bool, 1925 1964 has_errors: bool, 1965 + is_binary_input: bool, 1926 1966 ) -> Result<TokenStream> { 1927 1967 let output_type = if has_output { 1928 1968 let output_ident = syn::Ident::new( ··· 1944 1984 quote! { jacquard_common::types::xrpc::GenericError<'de> } 1945 1985 }; 1946 1986 1987 + // Generate encode_body() method for binary inputs 1988 + let encode_body_method = if is_binary_input { 1989 + quote! { 1990 + fn encode_body(&self) -> Result<Vec<u8>, jacquard_common::types::xrpc::EncodeError> { 1991 + Ok(self.body.to_vec()) 1992 + } 1993 + } 1994 + } else { 1995 + quote! {} 1996 + }; 1997 + 1947 1998 if has_params { 1948 1999 // Implement on the params/input struct itself 1949 2000 let request_ident = syn::Ident::new(type_base, proc_macro2::Span::call_site()); ··· 1961 2012 1962 2013 type Output<'de> = #output_type; 1963 2014 type Err<'de> = #error_type; 2015 + 2016 + #encode_body_method 1964 2017 } 1965 2018 }) 1966 2019 } else { ··· 2311 2364 println!("\n{}\n", formatted); 2312 2365 2313 2366 // Check structure 2314 - assert!(formatted.contains("struct GetAuthorFeedParams")); 2367 + assert!(formatted.contains("struct GetAuthorFeed")); 2315 2368 assert!(formatted.contains("struct GetAuthorFeedOutput")); 2316 2369 assert!(formatted.contains("enum GetAuthorFeedError")); 2317 2370 assert!(formatted.contains("pub actor"));
+6 -1
crates/jacquard-lexicon/target/test_codegen_output/app_bsky.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // This file was automatically generated from Lexicon schemas. 4 + // Any manual changes will be overwritten on the next regeneration. 5 + 1 6 pub mod embed; 2 7 pub mod feed; 3 - pub mod richtext; 8 + pub mod richtext;
+6 -1
crates/jacquard-lexicon/target/test_codegen_output/app_bsky/embed.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // This file was automatically generated from Lexicon schemas. 4 + // Any manual changes will be overwritten on the next regeneration. 5 + 1 6 pub mod external; 2 7 pub mod images; 3 8 pub mod record; 4 9 pub mod record_with_media; 5 - pub mod video; 10 + pub mod video;
+77 -7
crates/jacquard-lexicon/target/test_codegen_output/app_bsky/embed/external.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // Lexicon: app.bsky.embed.external 4 + // 5 + // This file was automatically generated from Lexicon schemas. 6 + // Any manual changes will be overwritten on the next regeneration. 7 + 8 + #[jacquard_derive::lexicon] 1 9 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 2 10 #[serde(rename_all = "camelCase")] 3 11 pub struct External<'a> { 12 + #[serde(borrow)] 4 13 pub description: jacquard_common::CowStr<'a>, 5 - #[serde(skip_serializing_if = "Option::is_none")] 6 - pub thumb: Option<jacquard_common::types::blob::Blob<'a>>, 14 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 15 + #[serde(borrow)] 16 + pub thumb: std::option::Option<jacquard_common::types::blob::Blob<'a>>, 17 + #[serde(borrow)] 7 18 pub title: jacquard_common::CowStr<'a>, 19 + #[serde(borrow)] 8 20 pub uri: jacquard_common::types::string::Uri<'a>, 9 21 } 22 + 23 + impl jacquard_common::IntoStatic for External<'_> { 24 + type Output = External<'static>; 25 + fn into_static(self) -> Self::Output { 26 + External { 27 + description: self.description.into_static(), 28 + thumb: self.thumb.into_static(), 29 + title: self.title.into_static(), 30 + uri: self.uri.into_static(), 31 + extra_data: self.extra_data.into_static(), 32 + } 33 + } 34 + } 35 + 10 36 ///A representation of some externally linked content (eg, a URL and 'card'), embedded in a Bluesky record (eg, a post). 37 + #[jacquard_derive::lexicon] 11 38 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 12 39 #[serde(rename_all = "camelCase")] 13 - pub struct External<'a> { 14 - pub external: jacquard_common::types::value::Data<'a>, 40 + pub struct ExternalRecord<'a> { 41 + #[serde(borrow)] 42 + pub external: test_generated::app_bsky::embed::external::External<'a>, 15 43 } 44 + 45 + impl jacquard_common::IntoStatic for ExternalRecord<'_> { 46 + type Output = ExternalRecord<'static>; 47 + fn into_static(self) -> Self::Output { 48 + ExternalRecord { 49 + external: self.external.into_static(), 50 + extra_data: self.extra_data.into_static(), 51 + } 52 + } 53 + } 54 + 55 + #[jacquard_derive::lexicon] 16 56 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 17 57 #[serde(rename_all = "camelCase")] 18 58 pub struct View<'a> { 19 - pub external: jacquard_common::types::value::Data<'a>, 59 + #[serde(borrow)] 60 + pub external: test_generated::app_bsky::embed::external::ViewExternal<'a>, 61 + } 62 + 63 + impl jacquard_common::IntoStatic for View<'_> { 64 + type Output = View<'static>; 65 + fn into_static(self) -> Self::Output { 66 + View { 67 + external: self.external.into_static(), 68 + extra_data: self.extra_data.into_static(), 69 + } 70 + } 20 71 } 72 + 73 + #[jacquard_derive::lexicon] 21 74 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 22 75 #[serde(rename_all = "camelCase")] 23 76 pub struct ViewExternal<'a> { 77 + #[serde(borrow)] 24 78 pub description: jacquard_common::CowStr<'a>, 25 - #[serde(skip_serializing_if = "Option::is_none")] 26 - pub thumb: Option<jacquard_common::types::string::Uri<'a>>, 79 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 80 + #[serde(borrow)] 81 + pub thumb: std::option::Option<jacquard_common::types::string::Uri<'a>>, 82 + #[serde(borrow)] 27 83 pub title: jacquard_common::CowStr<'a>, 84 + #[serde(borrow)] 28 85 pub uri: jacquard_common::types::string::Uri<'a>, 29 86 } 87 + 88 + impl jacquard_common::IntoStatic for ViewExternal<'_> { 89 + type Output = ViewExternal<'static>; 90 + fn into_static(self) -> Self::Output { 91 + ViewExternal { 92 + description: self.description.into_static(), 93 + thumb: self.thumb.into_static(), 94 + title: self.title.into_static(), 95 + uri: self.uri.into_static(), 96 + extra_data: self.extra_data.into_static(), 97 + } 98 + } 99 + }
+78 -6
crates/jacquard-lexicon/target/test_codegen_output/app_bsky/embed/images.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // Lexicon: app.bsky.embed.images 4 + // 5 + // This file was automatically generated from Lexicon schemas. 6 + // Any manual changes will be overwritten on the next regeneration. 7 + 8 + #[jacquard_derive::lexicon] 1 9 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 2 10 #[serde(rename_all = "camelCase")] 3 11 pub struct Image<'a> { 12 + ///Alt text description of the image, for accessibility. 13 + #[serde(borrow)] 4 14 pub alt: jacquard_common::CowStr<'a>, 5 - #[serde(skip_serializing_if = "Option::is_none")] 6 - pub aspect_ratio: Option<jacquard_common::types::value::Data<'a>>, 15 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 16 + #[serde(borrow)] 17 + pub aspect_ratio: std::option::Option<jacquard_common::types::value::Data<'a>>, 18 + #[serde(borrow)] 7 19 pub image: jacquard_common::types::blob::Blob<'a>, 8 20 } 21 + 22 + impl jacquard_common::IntoStatic for Image<'_> { 23 + type Output = Image<'static>; 24 + fn into_static(self) -> Self::Output { 25 + Image { 26 + alt: self.alt.into_static(), 27 + aspect_ratio: self.aspect_ratio.into_static(), 28 + image: self.image.into_static(), 29 + extra_data: self.extra_data.into_static(), 30 + } 31 + } 32 + } 33 + 34 + #[jacquard_derive::lexicon] 9 35 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 10 36 #[serde(rename_all = "camelCase")] 11 37 pub struct Images<'a> { 12 - pub images: Vec<jacquard_common::types::value::Data<'a>>, 38 + #[serde(borrow)] 39 + pub images: Vec<test_generated::app_bsky::embed::images::Image<'a>>, 13 40 } 41 + 42 + impl jacquard_common::IntoStatic for Images<'_> { 43 + type Output = Images<'static>; 44 + fn into_static(self) -> Self::Output { 45 + Images { 46 + images: self.images.into_static(), 47 + extra_data: self.extra_data.into_static(), 48 + } 49 + } 50 + } 51 + 52 + #[jacquard_derive::lexicon] 14 53 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 15 54 #[serde(rename_all = "camelCase")] 16 55 pub struct View<'a> { 17 - pub images: Vec<jacquard_common::types::value::Data<'a>>, 56 + #[serde(borrow)] 57 + pub images: Vec<test_generated::app_bsky::embed::images::ViewImage<'a>>, 58 + } 59 + 60 + impl jacquard_common::IntoStatic for View<'_> { 61 + type Output = View<'static>; 62 + fn into_static(self) -> Self::Output { 63 + View { 64 + images: self.images.into_static(), 65 + extra_data: self.extra_data.into_static(), 66 + } 67 + } 18 68 } 69 + 70 + #[jacquard_derive::lexicon] 19 71 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 20 72 #[serde(rename_all = "camelCase")] 21 73 pub struct ViewImage<'a> { 74 + ///Alt text description of the image, for accessibility. 75 + #[serde(borrow)] 22 76 pub alt: jacquard_common::CowStr<'a>, 23 - #[serde(skip_serializing_if = "Option::is_none")] 24 - pub aspect_ratio: Option<jacquard_common::types::value::Data<'a>>, 77 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 78 + #[serde(borrow)] 79 + pub aspect_ratio: std::option::Option<jacquard_common::types::value::Data<'a>>, 80 + ///Fully-qualified URL where a large version of the image can be fetched. May or may not be the exact original blob. For example, CDN location provided by the App View. 81 + #[serde(borrow)] 25 82 pub fullsize: jacquard_common::types::string::Uri<'a>, 83 + ///Fully-qualified URL where a thumbnail of the image can be fetched. For example, CDN location provided by the App View. 84 + #[serde(borrow)] 26 85 pub thumb: jacquard_common::types::string::Uri<'a>, 27 86 } 87 + 88 + impl jacquard_common::IntoStatic for ViewImage<'_> { 89 + type Output = ViewImage<'static>; 90 + fn into_static(self) -> Self::Output { 91 + ViewImage { 92 + alt: self.alt.into_static(), 93 + aspect_ratio: self.aspect_ratio.into_static(), 94 + fullsize: self.fullsize.into_static(), 95 + thumb: self.thumb.into_static(), 96 + extra_data: self.extra_data.into_static(), 97 + } 98 + } 99 + }
+133 -14
crates/jacquard-lexicon/target/test_codegen_output/app_bsky/embed/record.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // Lexicon: app.bsky.embed.record 4 + // 5 + // This file was automatically generated from Lexicon schemas. 6 + // Any manual changes will be overwritten on the next regeneration. 7 + 8 + #[jacquard_derive::lexicon] 1 9 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 2 10 #[serde(rename_all = "camelCase")] 3 11 pub struct Record<'a> { 4 - pub record: test_generated::com_atproto::repo::StrongRef<'a>, 12 + #[serde(borrow)] 13 + pub record: test_generated::com_atproto::repo::strong_ref::StrongRef<'a>, 14 + } 15 + 16 + impl jacquard_common::IntoStatic for Record<'_> { 17 + type Output = Record<'static>; 18 + fn into_static(self) -> Self::Output { 19 + Record { 20 + record: self.record.into_static(), 21 + extra_data: self.extra_data.into_static(), 22 + } 23 + } 5 24 } 25 + 26 + #[jacquard_derive::lexicon] 6 27 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 7 28 #[serde(rename_all = "camelCase")] 8 29 pub struct View<'a> { 9 - pub record: RecordRecord<'a>, 30 + #[serde(borrow)] 31 + pub record: ViewRecordRecord<'a>, 10 32 } 33 + 34 + #[jacquard_derive::open_union] 35 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 36 + #[serde(tag = "$type")] 37 + #[serde(bound(deserialize = "'de: 'a"))] 38 + pub enum ViewRecordRecord<'a> {} 39 + impl jacquard_common::IntoStatic for ViewRecordRecord<'_> { 40 + type Output = ViewRecordRecord<'static>; 41 + fn into_static(self) -> Self::Output { 42 + match self { 43 + ViewRecordRecord::Unknown(v) => ViewRecordRecord::Unknown(v.into_static()), 44 + } 45 + } 46 + } 47 + 48 + impl jacquard_common::IntoStatic for View<'_> { 49 + type Output = View<'static>; 50 + fn into_static(self) -> Self::Output { 51 + View { 52 + record: self.record.into_static(), 53 + extra_data: self.extra_data.into_static(), 54 + } 55 + } 56 + } 57 + 58 + #[jacquard_derive::lexicon] 11 59 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 12 60 #[serde(rename_all = "camelCase")] 13 61 pub struct ViewBlocked<'a> { 62 + #[serde(borrow)] 14 63 pub author: jacquard_common::types::value::Data<'a>, 15 64 pub blocked: bool, 65 + #[serde(borrow)] 16 66 pub uri: jacquard_common::types::string::AtUri<'a>, 17 67 } 68 + 69 + impl jacquard_common::IntoStatic for ViewBlocked<'_> { 70 + type Output = ViewBlocked<'static>; 71 + fn into_static(self) -> Self::Output { 72 + ViewBlocked { 73 + author: self.author.into_static(), 74 + blocked: self.blocked.into_static(), 75 + uri: self.uri.into_static(), 76 + extra_data: self.extra_data.into_static(), 77 + } 78 + } 79 + } 80 + 81 + #[jacquard_derive::lexicon] 18 82 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 19 83 #[serde(rename_all = "camelCase")] 20 84 pub struct ViewDetached<'a> { 21 85 pub detached: bool, 86 + #[serde(borrow)] 22 87 pub uri: jacquard_common::types::string::AtUri<'a>, 23 88 } 89 + 90 + impl jacquard_common::IntoStatic for ViewDetached<'_> { 91 + type Output = ViewDetached<'static>; 92 + fn into_static(self) -> Self::Output { 93 + ViewDetached { 94 + detached: self.detached.into_static(), 95 + uri: self.uri.into_static(), 96 + extra_data: self.extra_data.into_static(), 97 + } 98 + } 99 + } 100 + 101 + #[jacquard_derive::lexicon] 24 102 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 25 103 #[serde(rename_all = "camelCase")] 26 104 pub struct ViewNotFound<'a> { 27 105 pub not_found: bool, 106 + #[serde(borrow)] 28 107 pub uri: jacquard_common::types::string::AtUri<'a>, 29 108 } 109 + 110 + impl jacquard_common::IntoStatic for ViewNotFound<'_> { 111 + type Output = ViewNotFound<'static>; 112 + fn into_static(self) -> Self::Output { 113 + ViewNotFound { 114 + not_found: self.not_found.into_static(), 115 + uri: self.uri.into_static(), 116 + extra_data: self.extra_data.into_static(), 117 + } 118 + } 119 + } 120 + 121 + #[jacquard_derive::lexicon] 30 122 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 31 123 #[serde(rename_all = "camelCase")] 32 124 pub struct ViewRecord<'a> { 125 + #[serde(borrow)] 33 126 pub author: jacquard_common::types::value::Data<'a>, 127 + #[serde(borrow)] 34 128 pub cid: jacquard_common::types::string::Cid<'a>, 35 - #[serde(skip_serializing_if = "Option::is_none")] 36 - pub embeds: Option<Vec<jacquard_common::types::value::Data<'a>>>, 129 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 130 + #[serde(borrow)] 131 + pub embeds: std::option::Option<Vec<jacquard_common::types::value::Data<'a>>>, 37 132 pub indexed_at: jacquard_common::types::string::Datetime, 38 - #[serde(skip_serializing_if = "Option::is_none")] 39 - pub labels: Option<Vec<test_generated::com_atproto::label::Label<'a>>>, 40 - #[serde(skip_serializing_if = "Option::is_none")] 41 - pub like_count: Option<i64>, 42 - #[serde(skip_serializing_if = "Option::is_none")] 43 - pub quote_count: Option<i64>, 44 - #[serde(skip_serializing_if = "Option::is_none")] 45 - pub reply_count: Option<i64>, 46 - #[serde(skip_serializing_if = "Option::is_none")] 47 - pub repost_count: Option<i64>, 133 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 134 + #[serde(borrow)] 135 + pub labels: std::option::Option<Vec<test_generated::com_atproto::label::Label<'a>>>, 136 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 137 + pub like_count: std::option::Option<i64>, 138 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 139 + pub quote_count: std::option::Option<i64>, 140 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 141 + pub reply_count: std::option::Option<i64>, 142 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 143 + pub repost_count: std::option::Option<i64>, 144 + #[serde(borrow)] 48 145 pub uri: jacquard_common::types::string::AtUri<'a>, 146 + ///The record data itself. 147 + #[serde(borrow)] 49 148 pub value: jacquard_common::types::value::Data<'a>, 50 149 } 150 + 151 + impl jacquard_common::IntoStatic for ViewRecord<'_> { 152 + type Output = ViewRecord<'static>; 153 + fn into_static(self) -> Self::Output { 154 + ViewRecord { 155 + author: self.author.into_static(), 156 + cid: self.cid.into_static(), 157 + embeds: self.embeds.into_static(), 158 + indexed_at: self.indexed_at.into_static(), 159 + labels: self.labels.into_static(), 160 + like_count: self.like_count.into_static(), 161 + quote_count: self.quote_count.into_static(), 162 + reply_count: self.reply_count.into_static(), 163 + repost_count: self.repost_count.into_static(), 164 + uri: self.uri.into_static(), 165 + value: self.value.into_static(), 166 + extra_data: self.extra_data.into_static(), 167 + } 168 + } 169 + }
+102 -4
crates/jacquard-lexicon/target/test_codegen_output/app_bsky/embed/record_with_media.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // Lexicon: app.bsky.embed.recordWithMedia 4 + // 5 + // This file was automatically generated from Lexicon schemas. 6 + // Any manual changes will be overwritten on the next regeneration. 7 + 8 + #[jacquard_derive::lexicon] 1 9 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 2 10 #[serde(rename_all = "camelCase")] 3 11 pub struct RecordWithMedia<'a> { 4 - pub media: RecordMedia<'a>, 5 - pub record: test_generated::app_bsky::embed::Record<'a>, 12 + #[serde(borrow)] 13 + pub media: RecordWithMediaRecordMedia<'a>, 14 + #[serde(borrow)] 15 + pub record: test_generated::app_bsky::embed::record::Record<'a>, 6 16 } 17 + 18 + #[jacquard_derive::open_union] 19 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 20 + #[serde(tag = "$type")] 21 + #[serde(bound(deserialize = "'de: 'a"))] 22 + pub enum RecordWithMediaRecordMedia<'a> { 23 + #[serde(rename = "app.bsky.embed.images")] 24 + Images(Box<test_generated::app_bsky::embed::images::Images<'a>>), 25 + #[serde(rename = "app.bsky.embed.video")] 26 + Video(Box<test_generated::app_bsky::embed::video::Video<'a>>), 27 + #[serde(rename = "app.bsky.embed.external")] 28 + External(Box<test_generated::app_bsky::embed::external::ExternalRecord<'a>>), 29 + } 30 + 31 + impl jacquard_common::IntoStatic for RecordWithMediaRecordMedia<'_> { 32 + type Output = RecordWithMediaRecordMedia<'static>; 33 + fn into_static(self) -> Self::Output { 34 + match self { 35 + RecordWithMediaRecordMedia::Images(v) => { 36 + RecordWithMediaRecordMedia::Images(v.into_static()) 37 + } 38 + RecordWithMediaRecordMedia::Video(v) => { 39 + RecordWithMediaRecordMedia::Video(v.into_static()) 40 + } 41 + RecordWithMediaRecordMedia::External(v) => { 42 + RecordWithMediaRecordMedia::External(v.into_static()) 43 + } 44 + RecordWithMediaRecordMedia::Unknown(v) => { 45 + RecordWithMediaRecordMedia::Unknown(v.into_static()) 46 + } 47 + } 48 + } 49 + } 50 + 51 + impl jacquard_common::IntoStatic for RecordWithMedia<'_> { 52 + type Output = RecordWithMedia<'static>; 53 + fn into_static(self) -> Self::Output { 54 + RecordWithMedia { 55 + media: self.media.into_static(), 56 + record: self.record.into_static(), 57 + extra_data: self.extra_data.into_static(), 58 + } 59 + } 60 + } 61 + 62 + #[jacquard_derive::lexicon] 7 63 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 8 64 #[serde(rename_all = "camelCase")] 9 65 pub struct View<'a> { 10 - pub media: RecordMedia<'a>, 11 - pub record: test_generated::app_bsky::embed::View<'a>, 66 + #[serde(borrow)] 67 + pub media: ViewRecordMedia<'a>, 68 + #[serde(borrow)] 69 + pub record: test_generated::app_bsky::embed::record::View<'a>, 70 + } 71 + 72 + #[jacquard_derive::open_union] 73 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 74 + #[serde(tag = "$type")] 75 + #[serde(bound(deserialize = "'de: 'a"))] 76 + pub enum ViewRecordMedia<'a> { 77 + #[serde(rename = "app.bsky.embed.images#view")] 78 + ImagesView(Box<test_generated::app_bsky::embed::images::View<'a>>), 79 + #[serde(rename = "app.bsky.embed.video#view")] 80 + VideoView(Box<test_generated::app_bsky::embed::video::View<'a>>), 81 + #[serde(rename = "app.bsky.embed.external#view")] 82 + ExternalView(Box<test_generated::app_bsky::embed::external::View<'a>>), 83 + } 84 + 85 + impl jacquard_common::IntoStatic for ViewRecordMedia<'_> { 86 + type Output = ViewRecordMedia<'static>; 87 + fn into_static(self) -> Self::Output { 88 + match self { 89 + ViewRecordMedia::ImagesView(v) => { 90 + ViewRecordMedia::ImagesView(v.into_static()) 91 + } 92 + ViewRecordMedia::VideoView(v) => ViewRecordMedia::VideoView(v.into_static()), 93 + ViewRecordMedia::ExternalView(v) => { 94 + ViewRecordMedia::ExternalView(v.into_static()) 95 + } 96 + ViewRecordMedia::Unknown(v) => ViewRecordMedia::Unknown(v.into_static()), 97 + } 98 + } 12 99 } 100 + 101 + impl jacquard_common::IntoStatic for View<'_> { 102 + type Output = View<'static>; 103 + fn into_static(self) -> Self::Output { 104 + View { 105 + media: self.media.into_static(), 106 + record: self.record.into_static(), 107 + extra_data: self.extra_data.into_static(), 108 + } 109 + } 110 + }
+76 -12
crates/jacquard-lexicon/target/test_codegen_output/app_bsky/embed/video.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // Lexicon: app.bsky.embed.video 4 + // 5 + // This file was automatically generated from Lexicon schemas. 6 + // Any manual changes will be overwritten on the next regeneration. 7 + 8 + #[jacquard_derive::lexicon] 1 9 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 2 10 #[serde(rename_all = "camelCase")] 3 11 pub struct Caption<'a> { 12 + #[serde(borrow)] 4 13 pub file: jacquard_common::types::blob::Blob<'a>, 5 14 pub lang: jacquard_common::types::string::Language, 6 15 } 16 + 17 + impl jacquard_common::IntoStatic for Caption<'_> { 18 + type Output = Caption<'static>; 19 + fn into_static(self) -> Self::Output { 20 + Caption { 21 + file: self.file.into_static(), 22 + lang: self.lang.into_static(), 23 + extra_data: self.extra_data.into_static(), 24 + } 25 + } 26 + } 27 + 28 + #[jacquard_derive::lexicon] 7 29 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 8 30 #[serde(rename_all = "camelCase")] 9 31 pub struct Video<'a> { 10 - #[serde(skip_serializing_if = "Option::is_none")] 11 - pub alt: Option<jacquard_common::CowStr<'a>>, 12 - #[serde(skip_serializing_if = "Option::is_none")] 13 - pub aspect_ratio: Option<jacquard_common::types::value::Data<'a>>, 14 - #[serde(skip_serializing_if = "Option::is_none")] 15 - pub captions: Option<Vec<jacquard_common::types::value::Data<'a>>>, 32 + ///Alt text description of the video, for accessibility. 33 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 34 + #[serde(borrow)] 35 + pub alt: std::option::Option<jacquard_common::CowStr<'a>>, 36 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 37 + #[serde(borrow)] 38 + pub aspect_ratio: std::option::Option<jacquard_common::types::value::Data<'a>>, 39 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 40 + #[serde(borrow)] 41 + pub captions: std::option::Option< 42 + Vec<test_generated::app_bsky::embed::video::Caption<'a>>, 43 + >, 44 + ///The mp4 video file. May be up to 100mb, formerly limited to 50mb. 45 + #[serde(borrow)] 16 46 pub video: jacquard_common::types::blob::Blob<'a>, 17 47 } 48 + 49 + impl jacquard_common::IntoStatic for Video<'_> { 50 + type Output = Video<'static>; 51 + fn into_static(self) -> Self::Output { 52 + Video { 53 + alt: self.alt.into_static(), 54 + aspect_ratio: self.aspect_ratio.into_static(), 55 + captions: self.captions.into_static(), 56 + video: self.video.into_static(), 57 + extra_data: self.extra_data.into_static(), 58 + } 59 + } 60 + } 61 + 62 + #[jacquard_derive::lexicon] 18 63 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 19 64 #[serde(rename_all = "camelCase")] 20 65 pub struct View<'a> { 21 - #[serde(skip_serializing_if = "Option::is_none")] 22 - pub alt: Option<jacquard_common::CowStr<'a>>, 23 - #[serde(skip_serializing_if = "Option::is_none")] 24 - pub aspect_ratio: Option<jacquard_common::types::value::Data<'a>>, 66 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 67 + #[serde(borrow)] 68 + pub alt: std::option::Option<jacquard_common::CowStr<'a>>, 69 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 70 + #[serde(borrow)] 71 + pub aspect_ratio: std::option::Option<jacquard_common::types::value::Data<'a>>, 72 + #[serde(borrow)] 25 73 pub cid: jacquard_common::types::string::Cid<'a>, 74 + #[serde(borrow)] 26 75 pub playlist: jacquard_common::types::string::Uri<'a>, 27 - #[serde(skip_serializing_if = "Option::is_none")] 28 - pub thumbnail: Option<jacquard_common::types::string::Uri<'a>>, 76 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 77 + #[serde(borrow)] 78 + pub thumbnail: std::option::Option<jacquard_common::types::string::Uri<'a>>, 29 79 } 80 + 81 + impl jacquard_common::IntoStatic for View<'_> { 82 + type Output = View<'static>; 83 + fn into_static(self) -> Self::Output { 84 + View { 85 + alt: self.alt.into_static(), 86 + aspect_ratio: self.aspect_ratio.into_static(), 87 + cid: self.cid.into_static(), 88 + playlist: self.playlist.into_static(), 89 + thumbnail: self.thumbnail.into_static(), 90 + extra_data: self.extra_data.into_static(), 91 + } 92 + } 93 + }
+6 -1
crates/jacquard-lexicon/target/test_codegen_output/app_bsky/feed.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // This file was automatically generated from Lexicon schemas. 4 + // Any manual changes will be overwritten on the next regeneration. 5 + 1 6 pub mod get_author_feed; 2 - pub mod post; 7 + pub mod post;
+86 -15
crates/jacquard-lexicon/target/test_codegen_output/app_bsky/feed/get_author_feed.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // Lexicon: app.bsky.feed.getAuthorFeed 4 + // 5 + // This file was automatically generated from Lexicon schemas. 6 + // Any manual changes will be overwritten on the next regeneration. 7 + 1 8 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 2 9 #[serde(rename_all = "camelCase")] 3 - pub struct GetAuthorFeedParams<'a> { 10 + pub struct GetAuthorFeed<'a> { 11 + #[serde(borrow)] 4 12 pub actor: jacquard_common::types::ident::AtIdentifier<'a>, 5 - #[serde(skip_serializing_if = "Option::is_none")] 6 - pub cursor: Option<jacquard_common::CowStr<'a>>, 7 - #[serde(skip_serializing_if = "Option::is_none")] 8 - pub filter: Option<jacquard_common::CowStr<'a>>, 9 - #[serde(skip_serializing_if = "Option::is_none")] 10 - pub include_pins: Option<bool>, 11 - #[serde(skip_serializing_if = "Option::is_none")] 12 - pub limit: Option<i64>, 13 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 14 + #[serde(borrow)] 15 + pub cursor: std::option::Option<jacquard_common::CowStr<'a>>, 16 + ///(default: "posts_with_replies") 17 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 18 + #[serde(borrow)] 19 + pub filter: std::option::Option<jacquard_common::CowStr<'a>>, 20 + ///(default: false) 21 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 22 + pub include_pins: std::option::Option<bool>, 23 + ///(default: 50, min: 1, max: 100) 24 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 25 + pub limit: std::option::Option<i64>, 26 + } 27 + 28 + impl jacquard_common::IntoStatic for GetAuthorFeed<'_> { 29 + type Output = GetAuthorFeed<'static>; 30 + fn into_static(self) -> Self::Output { 31 + GetAuthorFeed { 32 + actor: self.actor.into_static(), 33 + cursor: self.cursor.into_static(), 34 + filter: self.filter.into_static(), 35 + include_pins: self.include_pins.into_static(), 36 + limit: self.limit.into_static(), 37 + } 38 + } 13 39 } 40 + 41 + #[jacquard_derive::lexicon] 14 42 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 15 43 #[serde(rename_all = "camelCase")] 16 44 pub struct GetAuthorFeedOutput<'a> { 17 - #[serde(skip_serializing_if = "Option::is_none")] 18 - pub cursor: Option<jacquard_common::CowStr<'a>>, 45 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 46 + #[serde(borrow)] 47 + pub cursor: std::option::Option<jacquard_common::CowStr<'a>>, 48 + #[serde(borrow)] 19 49 pub feed: Vec<jacquard_common::types::value::Data<'a>>, 20 50 } 51 + 52 + impl jacquard_common::IntoStatic for GetAuthorFeedOutput<'_> { 53 + type Output = GetAuthorFeedOutput<'static>; 54 + fn into_static(self) -> Self::Output { 55 + GetAuthorFeedOutput { 56 + cursor: self.cursor.into_static(), 57 + feed: self.feed.into_static(), 58 + extra_data: self.extra_data.into_static(), 59 + } 60 + } 61 + } 62 + 63 + #[jacquard_derive::open_union] 21 64 #[derive( 22 65 serde::Serialize, 23 66 serde::Deserialize, ··· 29 72 miette::Diagnostic 30 73 )] 31 74 #[serde(tag = "error", content = "message")] 32 - pub enum GetAuthorFeedError { 75 + #[serde(bound(deserialize = "'de: 'a"))] 76 + pub enum GetAuthorFeedError<'a> { 33 77 #[serde(rename = "BlockedActor")] 34 - BlockedActor(Option<jacquard_common::CowStr<'static>>), 78 + BlockedActor(std::option::Option<String>), 35 79 #[serde(rename = "BlockedByActor")] 36 - BlockedByActor(Option<jacquard_common::CowStr<'static>>), 80 + BlockedByActor(std::option::Option<String>), 37 81 } 38 - impl std::fmt::Display for GetAuthorFeedError { 82 + 83 + impl std::fmt::Display for GetAuthorFeedError<'_> { 39 84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 40 85 match self { 41 86 Self::BlockedActor(msg) => { ··· 52 97 } 53 98 Ok(()) 54 99 } 100 + Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 55 101 } 56 102 } 57 103 } 104 + 105 + impl jacquard_common::IntoStatic for GetAuthorFeedError<'_> { 106 + type Output = GetAuthorFeedError<'static>; 107 + fn into_static(self) -> Self::Output { 108 + match self { 109 + GetAuthorFeedError::BlockedActor(v) => { 110 + GetAuthorFeedError::BlockedActor(v.into_static()) 111 + } 112 + GetAuthorFeedError::BlockedByActor(v) => { 113 + GetAuthorFeedError::BlockedByActor(v.into_static()) 114 + } 115 + GetAuthorFeedError::Unknown(v) => { 116 + GetAuthorFeedError::Unknown(v.into_static()) 117 + } 118 + } 119 + } 120 + } 121 + 122 + impl jacquard_common::types::xrpc::XrpcRequest for GetAuthorFeed<'_> { 123 + const NSID: &'static str = "app.bsky.feed.getAuthorFeed"; 124 + const METHOD: jacquard_common::types::xrpc::XrpcMethod = jacquard_common::types::xrpc::XrpcMethod::Query; 125 + const OUTPUT_ENCODING: &'static str = "application/json"; 126 + type Output<'de> = GetAuthorFeedOutput<'de>; 127 + type Err<'de> = GetAuthorFeedError<'de>; 128 + }
+152 -25
crates/jacquard-lexicon/target/test_codegen_output/app_bsky/feed/post.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // Lexicon: app.bsky.feed.post 4 + // 5 + // This file was automatically generated from Lexicon schemas. 6 + // Any manual changes will be overwritten on the next regeneration. 7 + 1 8 ///Deprecated: use facets instead. 9 + #[jacquard_derive::lexicon] 2 10 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 3 11 #[serde(rename_all = "camelCase")] 4 12 pub struct Entity<'a> { 5 - pub index: jacquard_common::types::value::Data<'a>, 13 + #[serde(borrow)] 14 + pub index: test_generated::app_bsky::feed::post::TextSlice<'a>, 15 + ///Expected values are 'mention' and 'link'. 16 + #[serde(borrow)] 6 17 pub r#type: jacquard_common::CowStr<'a>, 18 + #[serde(borrow)] 7 19 pub value: jacquard_common::CowStr<'a>, 8 20 } 21 + 22 + impl jacquard_common::IntoStatic for Entity<'_> { 23 + type Output = Entity<'static>; 24 + fn into_static(self) -> Self::Output { 25 + Entity { 26 + index: self.index.into_static(), 27 + r#type: self.r#type.into_static(), 28 + value: self.value.into_static(), 29 + extra_data: self.extra_data.into_static(), 30 + } 31 + } 32 + } 33 + 9 34 ///Record containing a Bluesky post. 10 35 #[jacquard_derive::lexicon] 11 36 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 12 37 #[serde(rename_all = "camelCase")] 13 38 pub struct Post<'a> { 39 + ///Client-declared timestamp when this post was originally created. 14 40 pub created_at: jacquard_common::types::string::Datetime, 15 - #[serde(skip_serializing_if = "Option::is_none")] 16 - pub embed: Option<RecordEmbed<'a>>, 17 - #[serde(skip_serializing_if = "Option::is_none")] 18 - pub entities: Option<Vec<jacquard_common::types::value::Data<'a>>>, 19 - #[serde(skip_serializing_if = "Option::is_none")] 20 - pub facets: Option<Vec<test_generated::app_bsky::richtext::Facet<'a>>>, 21 - #[serde(skip_serializing_if = "Option::is_none")] 22 - pub labels: Option<RecordLabels<'a>>, 23 - #[serde(skip_serializing_if = "Option::is_none")] 24 - pub langs: Option<Vec<jacquard_common::types::string::Language>>, 25 - #[serde(skip_serializing_if = "Option::is_none")] 26 - pub reply: Option<jacquard_common::types::value::Data<'a>>, 27 - #[serde(skip_serializing_if = "Option::is_none")] 28 - pub tags: Option<Vec<jacquard_common::CowStr<'a>>>, 41 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 42 + #[serde(borrow)] 43 + pub embed: std::option::Option<PostRecordEmbed<'a>>, 44 + ///DEPRECATED: replaced by app.bsky.richtext.facet. 45 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 46 + #[serde(borrow)] 47 + pub entities: std::option::Option< 48 + Vec<test_generated::app_bsky::feed::post::Entity<'a>>, 49 + >, 50 + ///Annotations of text (mentions, URLs, hashtags, etc) 51 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 52 + #[serde(borrow)] 53 + pub facets: std::option::Option< 54 + Vec<test_generated::app_bsky::richtext::facet::Facet<'a>>, 55 + >, 56 + ///Self-label values for this post. Effectively content warnings. 57 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 58 + #[serde(borrow)] 59 + pub labels: std::option::Option<PostRecordLabels<'a>>, 60 + ///Indicates human language of post primary text content. 61 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 62 + pub langs: std::option::Option<Vec<jacquard_common::types::string::Language>>, 63 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 64 + #[serde(borrow)] 65 + pub reply: std::option::Option<test_generated::app_bsky::feed::post::ReplyRef<'a>>, 66 + ///Additional hashtags, in addition to any included in post text and facets. 67 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 68 + #[serde(borrow)] 69 + pub tags: std::option::Option<Vec<jacquard_common::CowStr<'a>>>, 70 + ///The primary post content. May be an empty string, if there are embeds. 71 + #[serde(borrow)] 29 72 pub text: jacquard_common::CowStr<'a>, 30 73 } 74 + 31 75 #[jacquard_derive::open_union] 32 76 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 33 77 #[serde(tag = "$type")] 34 - pub enum RecordEmbed<'a> { 78 + #[serde(bound(deserialize = "'de: 'a"))] 79 + pub enum PostRecordEmbed<'a> { 35 80 #[serde(rename = "app.bsky.embed.images")] 36 - Images(Box<test_generated::app_bsky::embed::Images<'a>>), 81 + Images(Box<test_generated::app_bsky::embed::images::Images<'a>>), 37 82 #[serde(rename = "app.bsky.embed.video")] 38 - Video(Box<test_generated::app_bsky::embed::Video<'a>>), 83 + Video(Box<test_generated::app_bsky::embed::video::Video<'a>>), 39 84 #[serde(rename = "app.bsky.embed.external")] 40 - External(Box<test_generated::app_bsky::embed::External<'a>>), 85 + External(Box<test_generated::app_bsky::embed::external::ExternalRecord<'a>>), 41 86 #[serde(rename = "app.bsky.embed.record")] 42 - Record(Box<test_generated::app_bsky::embed::Record<'a>>), 87 + Record(Box<test_generated::app_bsky::embed::record::Record<'a>>), 43 88 #[serde(rename = "app.bsky.embed.recordWithMedia")] 44 - RecordWithMedia(Box<test_generated::app_bsky::embed::RecordWithMedia<'a>>), 89 + RecordWithMedia( 90 + Box<test_generated::app_bsky::embed::record_with_media::RecordWithMedia<'a>>, 91 + ), 92 + } 93 + 94 + impl jacquard_common::IntoStatic for PostRecordEmbed<'_> { 95 + type Output = PostRecordEmbed<'static>; 96 + fn into_static(self) -> Self::Output { 97 + match self { 98 + PostRecordEmbed::Images(v) => PostRecordEmbed::Images(v.into_static()), 99 + PostRecordEmbed::Video(v) => PostRecordEmbed::Video(v.into_static()), 100 + PostRecordEmbed::External(v) => PostRecordEmbed::External(v.into_static()), 101 + PostRecordEmbed::Record(v) => PostRecordEmbed::Record(v.into_static()), 102 + PostRecordEmbed::RecordWithMedia(v) => { 103 + PostRecordEmbed::RecordWithMedia(v.into_static()) 104 + } 105 + PostRecordEmbed::Unknown(v) => PostRecordEmbed::Unknown(v.into_static()), 106 + } 107 + } 45 108 } 109 + 46 110 #[jacquard_derive::open_union] 47 111 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 48 112 #[serde(tag = "$type")] 49 - pub enum RecordLabels<'a> { 113 + #[serde(bound(deserialize = "'de: 'a"))] 114 + pub enum PostRecordLabels<'a> { 50 115 #[serde(rename = "com.atproto.label.defs#selfLabels")] 51 - SelfLabels(Box<test_generated::com_atproto::label::SelfLabels<'a>>), 116 + DefsSelfLabels(Box<test_generated::com_atproto::label::SelfLabels<'a>>), 52 117 } 118 + 119 + impl jacquard_common::IntoStatic for PostRecordLabels<'_> { 120 + type Output = PostRecordLabels<'static>; 121 + fn into_static(self) -> Self::Output { 122 + match self { 123 + PostRecordLabels::DefsSelfLabels(v) => { 124 + PostRecordLabels::DefsSelfLabels(v.into_static()) 125 + } 126 + PostRecordLabels::Unknown(v) => PostRecordLabels::Unknown(v.into_static()), 127 + } 128 + } 129 + } 130 + 131 + impl jacquard_common::types::collection::Collection for Post<'_> { 132 + const NSID: &'static str = "app.bsky.feed.post"; 133 + } 134 + 135 + impl jacquard_common::IntoStatic for Post<'_> { 136 + type Output = Post<'static>; 137 + fn into_static(self) -> Self::Output { 138 + Post { 139 + created_at: self.created_at.into_static(), 140 + embed: self.embed.into_static(), 141 + entities: self.entities.into_static(), 142 + facets: self.facets.into_static(), 143 + labels: self.labels.into_static(), 144 + langs: self.langs.into_static(), 145 + reply: self.reply.into_static(), 146 + tags: self.tags.into_static(), 147 + text: self.text.into_static(), 148 + extra_data: self.extra_data.into_static(), 149 + } 150 + } 151 + } 152 + 153 + #[jacquard_derive::lexicon] 53 154 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 54 155 #[serde(rename_all = "camelCase")] 55 156 pub struct ReplyRef<'a> { 56 - pub parent: test_generated::com_atproto::repo::StrongRef<'a>, 57 - pub root: test_generated::com_atproto::repo::StrongRef<'a>, 157 + #[serde(borrow)] 158 + pub parent: test_generated::com_atproto::repo::strong_ref::StrongRef<'a>, 159 + #[serde(borrow)] 160 + pub root: test_generated::com_atproto::repo::strong_ref::StrongRef<'a>, 58 161 } 162 + 163 + impl jacquard_common::IntoStatic for ReplyRef<'_> { 164 + type Output = ReplyRef<'static>; 165 + fn into_static(self) -> Self::Output { 166 + ReplyRef { 167 + parent: self.parent.into_static(), 168 + root: self.root.into_static(), 169 + extra_data: self.extra_data.into_static(), 170 + } 171 + } 172 + } 173 + 59 174 ///Deprecated. Use app.bsky.richtext instead -- A text segment. Start is inclusive, end is exclusive. Indices are for utf16-encoded strings. 175 + #[jacquard_derive::lexicon] 60 176 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 61 177 #[serde(rename_all = "camelCase")] 62 178 pub struct TextSlice<'a> { 63 179 pub end: i64, 64 180 pub start: i64, 65 181 } 182 + 183 + impl jacquard_common::IntoStatic for TextSlice<'_> { 184 + type Output = TextSlice<'static>; 185 + fn into_static(self) -> Self::Output { 186 + TextSlice { 187 + end: self.end.into_static(), 188 + start: self.start.into_static(), 189 + extra_data: self.extra_data.into_static(), 190 + } 191 + } 192 + }
+6 -1
crates/jacquard-lexicon/target/test_codegen_output/app_bsky/richtext.rs
··· 1 - pub mod facet; 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // This file was automatically generated from Lexicon schemas. 4 + // Any manual changes will be overwritten on the next regeneration. 5 + 6 + pub mod facet;
+74 -1
crates/jacquard-lexicon/target/test_codegen_output/app_bsky/richtext/facet.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // Lexicon: app.bsky.richtext.facet 4 + // 5 + // This file was automatically generated from Lexicon schemas. 6 + // Any manual changes will be overwritten on the next regeneration. 7 + 1 8 ///Specifies the sub-string range a facet feature applies to. Start index is inclusive, end index is exclusive. Indices are zero-indexed, counting bytes of the UTF-8 encoded text. NOTE: some languages, like Javascript, use UTF-16 or Unicode codepoints for string slice indexing; in these languages, convert to byte arrays before working with facets. 9 + #[jacquard_derive::lexicon] 2 10 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 3 11 #[serde(rename_all = "camelCase")] 4 12 pub struct ByteSlice<'a> { 5 13 pub byte_end: i64, 6 14 pub byte_start: i64, 7 15 } 16 + 17 + impl jacquard_common::IntoStatic for ByteSlice<'_> { 18 + type Output = ByteSlice<'static>; 19 + fn into_static(self) -> Self::Output { 20 + ByteSlice { 21 + byte_end: self.byte_end.into_static(), 22 + byte_start: self.byte_start.into_static(), 23 + extra_data: self.extra_data.into_static(), 24 + } 25 + } 26 + } 27 + 8 28 ///Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL. 29 + #[jacquard_derive::lexicon] 9 30 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 10 31 #[serde(rename_all = "camelCase")] 11 32 pub struct Link<'a> { 33 + #[serde(borrow)] 12 34 pub uri: jacquard_common::types::string::Uri<'a>, 13 35 } 36 + 37 + impl jacquard_common::IntoStatic for Link<'_> { 38 + type Output = Link<'static>; 39 + fn into_static(self) -> Self::Output { 40 + Link { 41 + uri: self.uri.into_static(), 42 + extra_data: self.extra_data.into_static(), 43 + } 44 + } 45 + } 46 + 14 47 ///Annotation of a sub-string within rich text. 48 + #[jacquard_derive::lexicon] 15 49 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 16 50 #[serde(rename_all = "camelCase")] 17 51 pub struct Facet<'a> { 52 + #[serde(borrow)] 18 53 pub features: Vec<jacquard_common::types::value::Data<'a>>, 19 - pub index: jacquard_common::types::value::Data<'a>, 54 + #[serde(borrow)] 55 + pub index: test_generated::app_bsky::richtext::facet::ByteSlice<'a>, 56 + } 57 + 58 + impl jacquard_common::IntoStatic for Facet<'_> { 59 + type Output = Facet<'static>; 60 + fn into_static(self) -> Self::Output { 61 + Facet { 62 + features: self.features.into_static(), 63 + index: self.index.into_static(), 64 + extra_data: self.extra_data.into_static(), 65 + } 66 + } 20 67 } 68 + 21 69 ///Facet feature for mention of another account. The text is usually a handle, including a '@' prefix, but the facet reference is a DID. 70 + #[jacquard_derive::lexicon] 22 71 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 23 72 #[serde(rename_all = "camelCase")] 24 73 pub struct Mention<'a> { 74 + #[serde(borrow)] 25 75 pub did: jacquard_common::types::string::Did<'a>, 26 76 } 77 + 78 + impl jacquard_common::IntoStatic for Mention<'_> { 79 + type Output = Mention<'static>; 80 + fn into_static(self) -> Self::Output { 81 + Mention { 82 + did: self.did.into_static(), 83 + extra_data: self.extra_data.into_static(), 84 + } 85 + } 86 + } 87 + 27 88 ///Facet feature for a hashtag. The text usually includes a '#' prefix, but the facet reference should not (except in the case of 'double hash tags'). 89 + #[jacquard_derive::lexicon] 28 90 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 29 91 #[serde(rename_all = "camelCase")] 30 92 pub struct Tag<'a> { 93 + #[serde(borrow)] 31 94 pub tag: jacquard_common::CowStr<'a>, 32 95 } 96 + 97 + impl jacquard_common::IntoStatic for Tag<'_> { 98 + type Output = Tag<'static>; 99 + fn into_static(self) -> Self::Output { 100 + Tag { 101 + tag: self.tag.into_static(), 102 + extra_data: self.extra_data.into_static(), 103 + } 104 + } 105 + }
+6 -1
crates/jacquard-lexicon/target/test_codegen_output/com_atproto.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // This file was automatically generated from Lexicon schemas. 4 + // Any manual changes will be overwritten on the next regeneration. 5 + 1 6 pub mod label; 2 - pub mod repo; 7 + pub mod repo;
+157 -28
crates/jacquard-lexicon/target/test_codegen_output/com_atproto/label.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // Lexicon: com.atproto.label.defs 4 + // 5 + // This file was automatically generated from Lexicon schemas. 6 + // Any manual changes will be overwritten on the next regeneration. 7 + 1 8 ///Metadata tag on an atproto resource (eg, repo or record). 9 + #[jacquard_derive::lexicon] 2 10 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 3 11 #[serde(rename_all = "camelCase")] 4 12 pub struct Label<'a> { 5 - #[serde(skip_serializing_if = "Option::is_none")] 6 - pub cid: Option<jacquard_common::types::string::Cid<'a>>, 13 + ///Optionally, CID specifying the specific version of 'uri' resource this label applies to. 14 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 15 + #[serde(borrow)] 16 + pub cid: std::option::Option<jacquard_common::types::string::Cid<'a>>, 17 + ///Timestamp when this label was created. 7 18 pub cts: jacquard_common::types::string::Datetime, 8 - #[serde(skip_serializing_if = "Option::is_none")] 9 - pub exp: Option<jacquard_common::types::string::Datetime>, 10 - #[serde(skip_serializing_if = "Option::is_none")] 11 - pub neg: Option<bool>, 12 - #[serde(skip_serializing_if = "Option::is_none")] 13 - pub sig: Option<jacquard_common::types::value::Bytes>, 19 + ///Timestamp at which this label expires (no longer applies). 20 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 21 + pub exp: std::option::Option<jacquard_common::types::string::Datetime>, 22 + ///If true, this is a negation label, overwriting a previous label. 23 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 24 + pub neg: std::option::Option<bool>, 25 + ///Signature of dag-cbor encoded label. 26 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 27 + pub sig: std::option::Option<bytes::Bytes>, 28 + ///DID of the actor who created this label. 29 + #[serde(borrow)] 14 30 pub src: jacquard_common::types::string::Did<'a>, 31 + ///AT URI of the record, repository (account), or other resource that this label applies to. 32 + #[serde(borrow)] 15 33 pub uri: jacquard_common::types::string::Uri<'a>, 34 + ///The short string name of the value or type of this label. 35 + #[serde(borrow)] 16 36 pub val: jacquard_common::CowStr<'a>, 17 - #[serde(skip_serializing_if = "Option::is_none")] 18 - pub ver: Option<i64>, 37 + ///The AT Protocol version of the label object. 38 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 39 + pub ver: std::option::Option<i64>, 40 + } 41 + 42 + impl jacquard_common::IntoStatic for Label<'_> { 43 + type Output = Label<'static>; 44 + fn into_static(self) -> Self::Output { 45 + Label { 46 + cid: self.cid.into_static(), 47 + cts: self.cts.into_static(), 48 + exp: self.exp.into_static(), 49 + neg: self.neg.into_static(), 50 + sig: self.sig.into_static(), 51 + src: self.src.into_static(), 52 + uri: self.uri.into_static(), 53 + val: self.val.into_static(), 54 + ver: self.ver.into_static(), 55 + extra_data: self.extra_data.into_static(), 56 + } 57 + } 19 58 } 59 + 20 60 #[derive(Debug, Clone, PartialEq, Eq, Hash)] 21 61 pub enum LabelValue<'a> { 22 - #[serde(rename = "!hide")] 23 62 Hide, 24 - #[serde(rename = "!no-promote")] 25 63 NoPromote, 26 - #[serde(rename = "!warn")] 27 64 Warn, 28 - #[serde(rename = "!no-unauthenticated")] 29 65 NoUnauthenticated, 30 - #[serde(rename = "dmca-violation")] 31 66 DmcaViolation, 32 - #[serde(rename = "doxxing")] 33 67 Doxxing, 34 - #[serde(rename = "porn")] 35 68 Porn, 36 - #[serde(rename = "sexual")] 37 69 Sexual, 38 - #[serde(rename = "nudity")] 39 70 Nudity, 40 - #[serde(rename = "nsfl")] 41 71 Nsfl, 42 - #[serde(rename = "gore")] 43 72 Gore, 44 - #[serde(untagged)] 45 73 Other(jacquard_common::CowStr<'a>), 46 74 } 75 + 47 76 impl<'a> LabelValue<'a> { 48 77 pub fn as_str(&self) -> &str { 49 78 match self { ··· 62 91 } 63 92 } 64 93 } 94 + 65 95 impl<'a> From<&'a str> for LabelValue<'a> { 66 96 fn from(s: &'a str) -> Self { 67 97 match s { ··· 80 110 } 81 111 } 82 112 } 113 + 83 114 impl<'a> From<String> for LabelValue<'a> { 84 115 fn from(s: String) -> Self { 85 116 match s.as_str() { ··· 98 129 } 99 130 } 100 131 } 132 + 101 133 impl<'a> AsRef<str> for LabelValue<'a> { 102 134 fn as_ref(&self) -> &str { 103 135 self.as_str() 104 136 } 105 137 } 138 + 106 139 impl<'a> serde::Serialize for LabelValue<'a> { 107 140 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 108 141 where ··· 111 144 serializer.serialize_str(self.as_str()) 112 145 } 113 146 } 147 + 114 148 impl<'de, 'a> serde::Deserialize<'de> for LabelValue<'a> 115 149 where 116 150 'de: 'a, ··· 123 157 Ok(Self::from(s)) 124 158 } 125 159 } 160 + 161 + impl jacquard_common::IntoStatic for LabelValue<'_> { 162 + type Output = LabelValue<'static>; 163 + fn into_static(self) -> Self::Output { 164 + match self { 165 + LabelValue::Hide => LabelValue::Hide, 166 + LabelValue::NoPromote => LabelValue::NoPromote, 167 + LabelValue::Warn => LabelValue::Warn, 168 + LabelValue::NoUnauthenticated => LabelValue::NoUnauthenticated, 169 + LabelValue::DmcaViolation => LabelValue::DmcaViolation, 170 + LabelValue::Doxxing => LabelValue::Doxxing, 171 + LabelValue::Porn => LabelValue::Porn, 172 + LabelValue::Sexual => LabelValue::Sexual, 173 + LabelValue::Nudity => LabelValue::Nudity, 174 + LabelValue::Nsfl => LabelValue::Nsfl, 175 + LabelValue::Gore => LabelValue::Gore, 176 + LabelValue::Other(v) => LabelValue::Other(v.into_static()), 177 + } 178 + } 179 + } 180 + 126 181 ///Declares a label value and its expected interpretations and behaviors. 182 + #[jacquard_derive::lexicon] 127 183 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 128 184 #[serde(rename_all = "camelCase")] 129 185 pub struct LabelValueDefinition<'a> { 130 - #[serde(skip_serializing_if = "Option::is_none")] 131 - pub adult_only: Option<bool>, 186 + ///Does the user need to have adult content enabled in order to configure this label? 187 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 188 + pub adult_only: std::option::Option<bool>, 189 + ///What should this label hide in the UI, if applied? 'content' hides all of the target; 'media' hides the images/video/audio; 'none' hides nothing. 190 + #[serde(borrow)] 132 191 pub blurs: jacquard_common::CowStr<'a>, 133 - #[serde(skip_serializing_if = "Option::is_none")] 134 - pub default_setting: Option<jacquard_common::CowStr<'a>>, 192 + ///The default setting for this label. 193 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 194 + #[serde(borrow)] 195 + pub default_setting: std::option::Option<jacquard_common::CowStr<'a>>, 196 + ///The value of the label being defined. Must only include lowercase ascii and the '-' character ([a-z-]+). 197 + #[serde(borrow)] 135 198 pub identifier: jacquard_common::CowStr<'a>, 136 - pub locales: Vec<jacquard_common::types::value::Data<'a>>, 199 + #[serde(borrow)] 200 + pub locales: Vec< 201 + test_generated::com_atproto::label::LabelValueDefinitionStrings<'a>, 202 + >, 203 + ///How should a client visually convey this label? 'inform' means neutral and informational; 'alert' means negative and warning; 'none' means show nothing. 204 + #[serde(borrow)] 137 205 pub severity: jacquard_common::CowStr<'a>, 138 206 } 207 + 208 + impl jacquard_common::IntoStatic for LabelValueDefinition<'_> { 209 + type Output = LabelValueDefinition<'static>; 210 + fn into_static(self) -> Self::Output { 211 + LabelValueDefinition { 212 + adult_only: self.adult_only.into_static(), 213 + blurs: self.blurs.into_static(), 214 + default_setting: self.default_setting.into_static(), 215 + identifier: self.identifier.into_static(), 216 + locales: self.locales.into_static(), 217 + severity: self.severity.into_static(), 218 + extra_data: self.extra_data.into_static(), 219 + } 220 + } 221 + } 222 + 139 223 ///Strings which describe the label in the UI, localized into a specific language. 224 + #[jacquard_derive::lexicon] 140 225 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 141 226 #[serde(rename_all = "camelCase")] 142 227 pub struct LabelValueDefinitionStrings<'a> { 228 + ///A longer description of what the label means and why it might be applied. 229 + #[serde(borrow)] 143 230 pub description: jacquard_common::CowStr<'a>, 231 + ///The code of the language these strings are written in. 144 232 pub lang: jacquard_common::types::string::Language, 233 + ///A short human-readable name for the label. 234 + #[serde(borrow)] 145 235 pub name: jacquard_common::CowStr<'a>, 146 236 } 237 + 238 + impl jacquard_common::IntoStatic for LabelValueDefinitionStrings<'_> { 239 + type Output = LabelValueDefinitionStrings<'static>; 240 + fn into_static(self) -> Self::Output { 241 + LabelValueDefinitionStrings { 242 + description: self.description.into_static(), 243 + lang: self.lang.into_static(), 244 + name: self.name.into_static(), 245 + extra_data: self.extra_data.into_static(), 246 + } 247 + } 248 + } 249 + 147 250 ///Metadata tag on an atproto record, published by the author within the record. Note that schemas should use #selfLabels, not #selfLabel. 251 + #[jacquard_derive::lexicon] 148 252 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 149 253 #[serde(rename_all = "camelCase")] 150 254 pub struct SelfLabel<'a> { 255 + ///The short string name of the value or type of this label. 256 + #[serde(borrow)] 151 257 pub val: jacquard_common::CowStr<'a>, 152 258 } 259 + 260 + impl jacquard_common::IntoStatic for SelfLabel<'_> { 261 + type Output = SelfLabel<'static>; 262 + fn into_static(self) -> Self::Output { 263 + SelfLabel { 264 + val: self.val.into_static(), 265 + extra_data: self.extra_data.into_static(), 266 + } 267 + } 268 + } 269 + 153 270 ///Metadata tags on an atproto record, published by the author within the record. 271 + #[jacquard_derive::lexicon] 154 272 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 155 273 #[serde(rename_all = "camelCase")] 156 274 pub struct SelfLabels<'a> { 157 - pub values: Vec<jacquard_common::types::value::Data<'a>>, 275 + #[serde(borrow)] 276 + pub values: Vec<test_generated::com_atproto::label::SelfLabel<'a>>, 158 277 } 278 + 279 + impl jacquard_common::IntoStatic for SelfLabels<'_> { 280 + type Output = SelfLabels<'static>; 281 + fn into_static(self) -> Self::Output { 282 + SelfLabels { 283 + values: self.values.into_static(), 284 + extra_data: self.extra_data.into_static(), 285 + } 286 + } 287 + }
+6 -1
crates/jacquard-lexicon/target/test_codegen_output/com_atproto/repo.rs
··· 1 - pub mod strong_ref; 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // This file was automatically generated from Lexicon schemas. 4 + // Any manual changes will be overwritten on the next regeneration. 5 + 6 + pub mod strong_ref;
+21
crates/jacquard-lexicon/target/test_codegen_output/com_atproto/repo/strong_ref.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // Lexicon: com.atproto.repo.strongRef 4 + // 5 + // This file was automatically generated from Lexicon schemas. 6 + // Any manual changes will be overwritten on the next regeneration. 7 + 8 + #[jacquard_derive::lexicon] 1 9 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 2 10 #[serde(rename_all = "camelCase")] 3 11 pub struct StrongRef<'a> { 12 + #[serde(borrow)] 4 13 pub cid: jacquard_common::types::string::Cid<'a>, 14 + #[serde(borrow)] 5 15 pub uri: jacquard_common::types::string::AtUri<'a>, 6 16 } 17 + 18 + impl jacquard_common::IntoStatic for StrongRef<'_> { 19 + type Output = StrongRef<'static>; 20 + fn into_static(self) -> Self::Output { 21 + StrongRef { 22 + cid: self.cid.into_static(), 23 + uri: self.uri.into_static(), 24 + extra_data: self.extra_data.into_static(), 25 + } 26 + } 27 + }
+6 -1
crates/jacquard-lexicon/target/test_codegen_output/lib.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // This file was automatically generated from Lexicon schemas. 4 + // Any manual changes will be overwritten on the next regeneration. 5 + 1 6 pub mod app_bsky; 2 - pub mod com_atproto; 7 + pub mod com_atproto;
+149 -25
crates/jacquard/src/client.rs
··· 4 4 use std::fmt::Display; 5 5 use std::future::Future; 6 6 7 - pub use error::{ClientError, Result}; 8 7 use bytes::Bytes; 8 + pub use error::{ClientError, Result}; 9 9 use http::{ 10 10 HeaderName, HeaderValue, Request, 11 11 header::{AUTHORIZATION, CONTENT_TYPE, InvalidHeaderValue}, 12 12 }; 13 13 pub use response::Response; 14 - use serde::Serialize; 14 + 15 + use jacquard_common::{ 16 + CowStr, IntoStatic, 17 + types::{ 18 + string::{Did, Handle}, 19 + xrpc::{XrpcMethod, XrpcRequest}, 20 + }, 21 + }; 22 + 23 + /// Implement HttpClient for reqwest::Client 24 + impl HttpClient for reqwest::Client { 25 + type Error = reqwest::Error; 26 + 27 + async fn send_http( 28 + &self, 29 + request: Request<Vec<u8>>, 30 + ) -> core::result::Result<http::Response<Vec<u8>>, Self::Error> { 31 + // Convert http::Request to reqwest::Request 32 + let (parts, body) = request.into_parts(); 15 33 16 - use jacquard_common::{CowStr, types::xrpc::{XrpcMethod, XrpcRequest}}; 34 + let mut req = self.request(parts.method, parts.uri.to_string()).body(body); 35 + 36 + // Copy headers 37 + for (name, value) in parts.headers.iter() { 38 + req = req.header(name.as_str(), value.as_bytes()); 39 + } 40 + 41 + // Send request 42 + let resp = req.send().await?; 43 + 44 + // Convert reqwest::Response to http::Response 45 + let mut builder = http::Response::builder().status(resp.status()); 46 + 47 + // Copy headers 48 + for (name, value) in resp.headers().iter() { 49 + builder = builder.header(name.as_str(), value.as_bytes()); 50 + } 51 + 52 + // Read body 53 + let body = resp.bytes().await?.to_vec(); 54 + 55 + Ok(builder.body(body).expect("Failed to build response")) 56 + } 57 + } 17 58 18 59 pub trait HttpClient { 19 60 type Error: std::error::Error + Display + Send + Sync + 'static; ··· 98 139 99 140 // Add query parameters for Query methods 100 141 if let XrpcMethod::Query = R::METHOD { 101 - if let Ok(qs) = serde_html_form::to_string(&request) { 102 - if !qs.is_empty() { 103 - uri.push('?'); 104 - uri.push_str(&qs); 105 - } 142 + let qs = serde_html_form::to_string(&request).map_err(error::EncodeError::from)?; 143 + if !qs.is_empty() { 144 + uri.push('?'); 145 + uri.push_str(&qs); 106 146 } 107 147 } 108 148 ··· 139 179 } 140 180 141 181 // Serialize body for procedures 142 - let body = if let XrpcMethod::Procedure(encoding) = R::METHOD { 143 - if encoding == "application/json" { 144 - serde_json::to_vec(&request).map_err(error::EncodeError::Json)? 145 - } else { 146 - // For other encodings, we'd need different serialization 147 - vec![] 148 - } 182 + let body = if let XrpcMethod::Procedure(_) = R::METHOD { 183 + request.encode_body()? 149 184 } else { 150 185 vec![] 151 186 }; 152 187 188 + // TODO: make this not panic 153 189 let http_request = builder.body(body).expect("Failed to build HTTP request"); 154 190 155 191 // Send HTTP request 156 - let http_response = client.send_http(http_request).await.map_err(|e| { 157 - error::TransportError::Other(Box::new(e)) 158 - })?; 192 + let http_response = client 193 + .send_http(http_request) 194 + .await 195 + .map_err(|e| error::TransportError::Other(Box::new(e)))?; 196 + 197 + let status = http_response.status(); 198 + let buffer = Bytes::from(http_response.into_body()); 159 199 160 - // Check status 161 - if !http_response.status().is_success() { 200 + // XRPC errors come as 400/401 with structured error bodies 201 + // Other error status codes (404, 500, etc.) are generic HTTP errors 202 + if !status.is_success() && !matches!(status.as_u16(), 400 | 401) { 162 203 return Err(ClientError::Http(error::HttpError { 163 - status: http_response.status(), 164 - body: Some(Bytes::from(http_response.body().clone())), 204 + status, 205 + body: Some(buffer), 165 206 })); 166 207 } 167 208 168 - // Convert to Response 169 - let buffer = Bytes::from(http_response.into_body()); 170 - Ok(Response::new(buffer)) 209 + // Response will parse XRPC errors for 400/401, or output for 2xx 210 + Ok(Response::new(buffer, status)) 211 + } 212 + 213 + /// Session information from createSession 214 + #[derive(Debug, Clone)] 215 + pub struct Session { 216 + pub access_jwt: CowStr<'static>, 217 + pub refresh_jwt: CowStr<'static>, 218 + pub did: Did<'static>, 219 + pub handle: Handle<'static>, 220 + } 221 + 222 + impl From<jacquard_api::com_atproto::server::create_session::CreateSessionOutput<'_>> for Session { 223 + fn from( 224 + output: jacquard_api::com_atproto::server::create_session::CreateSessionOutput<'_>, 225 + ) -> Self { 226 + Self { 227 + access_jwt: output.access_jwt.into_static(), 228 + refresh_jwt: output.refresh_jwt.into_static(), 229 + did: output.did.into_static(), 230 + handle: output.handle.into_static(), 231 + } 232 + } 233 + } 234 + 235 + /// Authenticated XRPC client that includes session tokens 236 + pub struct AuthenticatedClient<C> { 237 + client: C, 238 + base_uri: CowStr<'static>, 239 + session: Option<Session>, 240 + } 241 + 242 + impl<C> AuthenticatedClient<C> { 243 + /// Create a new authenticated client with a base URI 244 + pub fn new(client: C, base_uri: CowStr<'static>) -> Self { 245 + Self { 246 + client, 247 + base_uri: base_uri, 248 + session: None, 249 + } 250 + } 251 + 252 + /// Set the session 253 + pub fn set_session(&mut self, session: Session) { 254 + self.session = Some(session); 255 + } 256 + 257 + /// Get the current session 258 + pub fn session(&self) -> Option<&Session> { 259 + self.session.as_ref() 260 + } 261 + 262 + /// Clear the session 263 + pub fn clear_session(&mut self) { 264 + self.session = None; 265 + } 266 + } 267 + 268 + impl<C: HttpClient> HttpClient for AuthenticatedClient<C> { 269 + type Error = C::Error; 270 + 271 + fn send_http( 272 + &self, 273 + request: Request<Vec<u8>>, 274 + ) -> impl Future<Output = core::result::Result<http::Response<Vec<u8>>, Self::Error>> { 275 + self.client.send_http(request) 276 + } 277 + } 278 + 279 + impl<C: HttpClient> XrpcClient for AuthenticatedClient<C> { 280 + fn base_uri(&self) -> CowStr<'_> { 281 + self.base_uri.clone() 282 + } 283 + 284 + async fn authorization_token(&self, is_refresh: bool) -> Option<AuthorizationToken<'_>> { 285 + if is_refresh { 286 + self.session 287 + .as_ref() 288 + .map(|s| AuthorizationToken::Bearer(s.refresh_jwt.clone())) 289 + } else { 290 + self.session 291 + .as_ref() 292 + .map(|s| AuthorizationToken::Bearer(s.access_jwt.clone())) 293 + } 294 + } 171 295 }
+2 -15
crates/jacquard/src/client/error.rs
··· 59 59 Other(Box<dyn std::error::Error + Send + Sync>), 60 60 } 61 61 62 - #[derive(Debug, thiserror::Error, miette::Diagnostic)] 63 - pub enum EncodeError { 64 - #[error("Failed to serialize query: {0}")] 65 - Query( 66 - #[from] 67 - #[source] 68 - serde_html_form::ser::Error, 69 - ), 70 - #[error("Failed to serialize JSON: {0}")] 71 - Json( 72 - #[from] 73 - #[source] 74 - serde_json::Error, 75 - ), 76 - } 62 + // Re-export EncodeError from common 63 + pub use jacquard_common::types::xrpc::EncodeError; 77 64 78 65 #[derive(Debug, thiserror::Error, miette::Diagnostic)] 79 66 pub enum DecodeError {
+109 -12
crates/jacquard/src/client/response.rs
··· 1 1 use bytes::Bytes; 2 + use http::StatusCode; 2 3 use jacquard_common::IntoStatic; 3 4 use jacquard_common::types::xrpc::XrpcRequest; 5 + use serde::Deserialize; 4 6 use std::marker::PhantomData; 7 + 8 + use super::error::AuthError; 5 9 6 10 /// XRPC response wrapper that owns the response buffer 7 11 /// 8 12 /// Allows borrowing from the buffer when parsing to avoid unnecessary allocations. 9 13 pub struct Response<R: XrpcRequest> { 10 14 buffer: Bytes, 15 + status: StatusCode, 11 16 _marker: PhantomData<R>, 12 17 } 13 18 14 19 impl<R: XrpcRequest> Response<R> { 15 - /// Create a new response from a buffer 16 - pub fn new(buffer: Bytes) -> Self { 20 + /// Create a new response from a buffer and status code 21 + pub fn new(buffer: Bytes, status: StatusCode) -> Self { 17 22 Self { 18 23 buffer, 24 + status, 19 25 _marker: PhantomData, 20 26 } 27 + } 28 + 29 + /// Get the HTTP status code 30 + pub fn status(&self) -> StatusCode { 31 + self.status 21 32 } 22 33 23 34 /// Parse the response, borrowing from the internal buffer ··· 35 46 serde_json::from_slice(buffer) 36 47 } 37 48 38 - let output = parse_output::<R>(&self.buffer); 39 - if let Ok(output) = output { 40 - Ok(output) 41 - } else { 42 - // Try to parse as error 49 + // 200: parse as output 50 + if self.status.is_success() { 51 + match parse_output::<R>(&self.buffer) { 52 + Ok(output) => Ok(output), 53 + Err(e) => Err(XrpcError::Decode(e)), 54 + } 55 + // 400: try typed XRPC error, fallback to generic error 56 + } else if self.status.as_u16() == 400 { 43 57 match parse_error::<R>(&self.buffer) { 44 58 Ok(error) => Err(XrpcError::Xrpc(error)), 59 + Err(_) => { 60 + // Fallback to generic error (InvalidRequest, ExpiredToken, etc.) 61 + match serde_json::from_slice::<GenericXrpcError>(&self.buffer) { 62 + Ok(generic) => { 63 + // Map auth-related errors to AuthError 64 + match generic.error.as_str() { 65 + "ExpiredToken" => Err(XrpcError::Auth(AuthError::TokenExpired)), 66 + "InvalidToken" => Err(XrpcError::Auth(AuthError::InvalidToken)), 67 + _ => Err(XrpcError::Generic(generic)), 68 + } 69 + } 70 + Err(e) => Err(XrpcError::Decode(e)), 71 + } 72 + } 73 + } 74 + // 401: always auth error 75 + } else { 76 + match serde_json::from_slice::<GenericXrpcError>(&self.buffer) { 77 + Ok(generic) => { 78 + match generic.error.as_str() { 79 + "ExpiredToken" => Err(XrpcError::Auth(AuthError::TokenExpired)), 80 + "InvalidToken" => Err(XrpcError::Auth(AuthError::InvalidToken)), 81 + _ => Err(XrpcError::Auth(AuthError::NotAuthenticated)), 82 + } 83 + } 45 84 Err(e) => Err(XrpcError::Decode(e)), 46 85 } 47 86 } ··· 66 105 serde_json::from_slice(buffer) 67 106 } 68 107 69 - let output = parse_output::<R>(&self.buffer); 70 - if let Ok(output) = output { 71 - Ok(output.into_static()) 72 - } else { 73 - // Try to parse as error 108 + // 200: parse as output 109 + if self.status.is_success() { 110 + match parse_output::<R>(&self.buffer) { 111 + Ok(output) => Ok(output.into_static()), 112 + Err(e) => Err(XrpcError::Decode(e)), 113 + } 114 + // 400: try typed XRPC error, fallback to generic error 115 + } else if self.status.as_u16() == 400 { 74 116 match parse_error::<R>(&self.buffer) { 75 117 Ok(error) => Err(XrpcError::Xrpc(error.into_static())), 118 + Err(_) => { 119 + // Fallback to generic error (InvalidRequest, ExpiredToken, etc.) 120 + match serde_json::from_slice::<GenericXrpcError>(&self.buffer) { 121 + Ok(generic) => { 122 + // Map auth-related errors to AuthError 123 + match generic.error.as_str() { 124 + "ExpiredToken" => Err(XrpcError::Auth(AuthError::TokenExpired)), 125 + "InvalidToken" => Err(XrpcError::Auth(AuthError::InvalidToken)), 126 + _ => Err(XrpcError::Generic(generic)), 127 + } 128 + } 129 + Err(e) => Err(XrpcError::Decode(e)), 130 + } 131 + } 132 + } 133 + // 401: always auth error 134 + } else { 135 + match serde_json::from_slice::<GenericXrpcError>(&self.buffer) { 136 + Ok(generic) => { 137 + match generic.error.as_str() { 138 + "ExpiredToken" => Err(XrpcError::Auth(AuthError::TokenExpired)), 139 + "InvalidToken" => Err(XrpcError::Auth(AuthError::InvalidToken)), 140 + _ => Err(XrpcError::Auth(AuthError::NotAuthenticated)), 141 + } 142 + } 76 143 Err(e) => Err(XrpcError::Decode(e)), 77 144 } 78 145 } ··· 84 151 } 85 152 } 86 153 154 + /// Generic XRPC error format (for InvalidRequest, etc.) 155 + #[derive(Debug, Clone, Deserialize)] 156 + pub struct GenericXrpcError { 157 + pub error: String, 158 + pub message: Option<String>, 159 + } 160 + 161 + impl std::fmt::Display for GenericXrpcError { 162 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 163 + if let Some(msg) = &self.message { 164 + write!(f, "{}: {}", self.error, msg) 165 + } else { 166 + write!(f, "{}", self.error) 167 + } 168 + } 169 + } 170 + 171 + impl std::error::Error for GenericXrpcError {} 172 + 87 173 #[derive(Debug, thiserror::Error, miette::Diagnostic)] 88 174 pub enum XrpcError<E: std::error::Error + IntoStatic> { 175 + /// Typed XRPC error from the endpoint's error enum 89 176 #[error("XRPC error: {0}")] 90 177 Xrpc(E), 178 + 179 + /// Authentication error (ExpiredToken, InvalidToken, etc.) 180 + #[error("Authentication error: {0}")] 181 + Auth(#[from] AuthError), 182 + 183 + /// Generic XRPC error (InvalidRequest, etc.) 184 + #[error("XRPC error: {0}")] 185 + Generic(GenericXrpcError), 186 + 187 + /// Failed to decode response 91 188 #[error("Failed to decode response: {0}")] 92 189 Decode(#[from] serde_json::Error), 93 190 }